features/rewrite/modal1 #10

Merged
koma merged 18 commits from features/rewrite/modal1 into features/rewrite/main 2 years ago

@ -15,4 +15,11 @@
<ProjectReference Include="..\Connected.Components.Showcase\Connected.Components.Showcase.csproj" />
</ItemGroup>
<ItemGroup>
<Watch Include="..\..\src\**" />
<Watch Remove="..\..\src\**\obj\**" />
<Watch Remove="..\..\src\**\bin\**" />
</ItemGroup>
</Project>

@ -1,5 +1,7 @@
@inherits LayoutComponentBase
<ModalDialog/>
<main>
@Body
</main>

File diff suppressed because it is too large Load Diff

@ -1,6 +1,7 @@
using Connected.Components.Showcase.Runner;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Connected.Services;
internal class Program
{
@ -12,6 +13,8 @@ internal class Program
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddModalDialogService();
await builder.Build().RunAsync();
}
}

@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connected.Components.Showcase.Runner</title>
<base href="/" />
<link href="css/app.css" rel="stylesheet" />
@ -27,4 +28,4 @@
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
</html>

@ -1,44 +1,9 @@
<button
type="button"
href="#"
@onclick="@OnClick"
disabled=@Disabled
style="@Style"
class="@ClassList">
<button type="button"
@onclick="@OnClick"
disabled=@Disabled
style="@StyleList"
class="@ClassList">
<div class="@ContentClassList">
@if (!string.IsNullOrEmpty(Glyph))
{
@if (GlyphPosition == Position.Top || GlyphPosition == Position.Bottom)
{
<div style="align-items:center">
@if (GlyphPosition == Position.Top)
{
<Glyph SVG="@Glyph" Color="@GlyphColor"/>
}
@ChildContent
@if (GlyphPosition == Position.Bottom)
{
<Glyph SVG="@Glyph" Color="@GlyphColor"/>
}
</div>
}
@if (GlyphPosition == Position.Left || GlyphPosition == Position.Right)
{
<div style="display:flex; align-items:center">
@if (GlyphPosition == Position.Left)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" Class="m-1" />
}
@ChildContent
@if (GlyphPosition == Position.Right)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" Class="m-1" />
}
</div>
}
} else
{
@ChildContent
}
</div>
</button>

@ -1,8 +1,6 @@
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using static Connected.Colors;
using System;
namespace Connected.Components;
public partial class Button
@ -28,10 +26,10 @@ public partial class Button
/// <summary>
/// Size of the button.
/// Options: Size.[Small,Medium,Large,FullWidth]
/// Default: Size.Large
/// Default: Size.Medium
/// </summary>
[Parameter]
public Size Size { get; set; } = Size.Large;
public Size Size { get; set; } = Size.Medium;
/// <summary>
/// Text shown inside the button
@ -48,29 +46,6 @@ public partial class Button
[Parameter]
public bool Disabled { get; set; } = false;
/// <summary>
/// Glyph (Icon) inside the button.
/// Options: SVG string --> Icons
/// Default: string.Empty
/// </summary>
[Parameter]
public string Glyph { get; set; } = string.Empty;
/// <summary>
/// Position of the glyph relative to button Text parameter. If Glyph parameter == string.Empty this parameter is ignored
/// Options: Position.[left,top,right,bottom]
/// Default: Position.left
/// </summary>
[Parameter]
public Position GlyphPosition { get; set; } = Position.Left;
/// <summary>
/// HEX string of the color for the glyph. If Glyph parameter is empty this parameter is ignored
/// Options: any HEX color string
/// Default: Black (#000000)
/// </summary>
[Parameter]
public string GlyphColor { get; set; } = "#000000";
/// <summary>
/// User defined custom class added on top of default generated classes
@ -90,7 +65,7 @@ public partial class Button
/// <summary>
/// User defined custom style
/// Options: any user defined string with valid CSS style
/// Options: any valid CSS style
/// Default: string.Empty
/// </summary>
[Parameter]
@ -115,10 +90,21 @@ public partial class Button
#region Styling
public string StyleList
{
get
{
return new StyleBuilder()
.AddStyle(Style)
.Build();
}
}
/// <summary>
/// Generated class list for button based on user parameters
/// </summary>
private string ClassList
public string ClassList
{
get
{
@ -134,7 +120,7 @@ public partial class Button
/// <summary>
/// Generated class list for button based on user parameters
/// </summary>
private string ContentClassList
public string ContentClassList
{
get
{

@ -2,12 +2,22 @@
@inherits InputBase;
<div class="checkbox-group">
@if (Checked)
{
<input id="@Id" name="checkbox" type="checkbox" @onchange="OnChange" @attributes=@InputAttributes checked>
} else {
<input id="@Id" name="checkbox" type="checkbox" @onchange="OnChange" @attributes=@InputAttributes>
}
<label for="@Id" class="checkbox-label">@Label</label>
</div>
<label class="checkbox-group"
for="@Id">
<input class="@ClassList"
style="@StyleList"
id="@Id"
type="checkbox"
@attributes=@InputAttributes
checked="@Checked"
readonly="@Readonly"
disabled="@Disabled">
<div class="checkbox-fill"></div>
<label for="@Id"
class="@LabelClassList"
style="@LabelStyleList">
@Label
</label>
</label>

@ -1,21 +1,100 @@
using Connected.Models;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class CheckBox: InputBase
public partial class CheckBox : InputBase
{
/// <summary>
/// State of the CheckBox
/// Options: true, false
/// Default: false
/// </summary>
[Parameter]
public bool Checked { get; set; } = false;
/// <summary>
/// ID for the CheckBox
/// </summary>
[Parameter, EditorRequired]
public string Id { get; set; }
public string? Id { get; set; }
/// <summary>
/// Event when the checked is changed
/// </summary>
[Parameter]
public EventCallback<bool> CheckedChanged { get; set; }
/// <summary>
/// OPTIONAL - Style for the input
/// Options: any valid CSS style
/// Default: string.Empty
/// </summary>
[Parameter]
public string Style { get; set; } = string.Empty;
/// <summary>
/// OPTIONAL - Class for the label of input
/// Options: any valid Class name or multiple separated with space
/// Default: string.Empty
/// </summary>
[Parameter]
public EventCallback<bool> CheckedChange { get; set; }
public async Task OnChange(ChangeEventArgs args)
public string LabelClass { get; set; } = string.Empty;
/// <summary>
/// OPTIONAL - Style for the label of input
/// Options: any valid CSS style
/// Default: string.Empty
/// </summary>
[Parameter]
public string LabelStyle { get; set; } = string.Empty;
/// <summary>
/// OnChange event when checked is changed
/// </summary>
/// <returns></returns>
public async Task OnChange()
{
Checked = (bool)args.Value;
CheckedChange.InvokeAsync(Checked);
Checked = !Checked;
await CheckedChanged.InvokeAsync(Checked);
}
private string ClassList
{
get
{
return new CssBuilder("checkbox-input")
.AddClass(base.Class)
.Build();
}
}
private string StyleList
{
get
{
return new StyleBuilder()
.AddStyle(Style)
.Build();
}
}
private string LabelClassList
{
get
{
return new CssBuilder("checkbox-label")
.AddClass(LabelClass)
.Build();
}
}
private string LabelStyleList
{
get
{
return new StyleBuilder()
.AddStyle(LabelStyle)
.Build();
}
}
}

@ -1,9 +0,0 @@
@using Connected.Models;
@inherits InputBase;
<div>
<div class="container">
@ChildContent
</div>
</div>

@ -1,11 +0,0 @@
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class CheckBoxGroup
{
[Parameter, EditorRequired]
public string Id { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
}

@ -1,6 +1,6 @@

<div style="width:@WidthString; height:@HeightString; overflow:hidden">
<svg viewBox="0 0 24 24" style="fill:@Color;" class="@GlyphClassList" @onclick="@OnClick">
<div style="@GlyphStyleList">
<svg viewBox="0 0 24 24" class="@GlyphClassList" @onclick="@OnClick">
@((MarkupString)SVG)
</svg>
</div>

@ -2,59 +2,87 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Connected.Components
namespace Connected.Components;
public partial class Glyph
{
public partial class Glyph
{
[Parameter]
public string SVG { get; set; } = string.Empty;
[Parameter]
public string Color { get; set; } = "#000000";
[Parameter]
public int Width { get; set; } = 24;
[Parameter]
public int Height { get; set; } = 24;
private string WidthString
{
get
{
return Width.ToString() + "px";
}
}
private string HeightString
/// <summary>
/// SVG markup string for glyph
/// Options: any valid SVG markup string
/// Default: string.Empty
/// </summary>
[Parameter]
public string SVG { get; set; } = string.Empty;
/// <summary>
/// Color of the glyph
/// Options: Color.[Core,Primary,Secondary,Success,Info,Warning,Danger,White,Light,Dark]
/// Default: Color.Dark
/// </summary>
[Parameter]
public Color Color { get; set; } = Color.Dark;
/// <summary>
/// Width of the glyph in px
/// Options: Any positive integer number
/// Default: 24
/// </summary>
[Parameter]
public int Width { get; set; } = 24;
/// <summary>
/// Height of the glyph in px
/// Options: Any positive integer number
/// Default: 24
/// </summary>
[Parameter]
public int Height { get; set; } = 24;
/// <summary>
/// Class name or multiple classes separated by space
/// Options: Any valid class name or names separated by space
/// Default: string.Empty
/// </summary>
[Parameter]
public string Class { get; set; } = string.Empty;
private string GlyphClassList
{
get
{
get
{
return Width.ToString() + "px";
}
return new CssBuilder()
.AddClass("color-"+Helper.GetEnumDescription<Color>(Color))
.AddClass(Class)
.Build();
}
}
[Parameter]
public string Class { get; set; } = string.Empty;
private string GlyphClassList
{
get
{
return new CssBuilder()
.AddClass(Class)
.Build();
}
}
[Parameter]
/// <summary>
/// Glyph click event.
/// </summary>
public EventCallback<MouseEventArgs> Click { get; set; }
protected async Task OnClick(MouseEventArgs e)
{
await Click.InvokeAsync(e);
/// <summary>
/// User defined style for the glyph
/// Options: Any valid CSS style
/// Default: string.Empty
/// </summary>
[Parameter]
public string Style { get; set; } = string.Empty;
private string GlyphStyleList
{
get
{
return new StyleBuilder()
.AddStyle("width: "+Width.ToString()+"px; height:"+Height.ToString()+"px; overflow: hidden")
.AddStyle(Style)
.Build();
}
}
[Parameter]
/// <summary>
/// Glyph click event.
/// </summary>
public EventCallback<MouseEventArgs> Click { get; set; }
protected async Task OnClick(MouseEventArgs e)
{
await Click.InvokeAsync(e);
}
}

@ -0,0 +1,46 @@
@inherits Button
<button type="button"
href="#"
@onclick="@OnClick"
disabled=@Disabled
style="@StyleList"
class="@ClassList">
<div class="@ContentClassList">
@if (!string.IsNullOrEmpty(Glyph))
{
@if (GlyphPosition == Position.Top || GlyphPosition == Position.Bottom)
{
<div style="align-items:center">
@if (GlyphPosition == Position.Top)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" />
}
@ChildContent
@if (GlyphPosition == Position.Bottom)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" />
}
</div>
}
@if (GlyphPosition == Position.Left || GlyphPosition == Position.Right)
{
<div style="display:flex; align-items:center">
@if (GlyphPosition == Position.Left)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" Class="m-1" />
}
@ChildContent
@if (GlyphPosition == Position.Right)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" Class="m-1" />
}
</div>
}
}
else
{
@ChildContent
}
</div>
</button>

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class GlyphButton : Button
{
#region Parameters
/// <summary>
/// Glyph (Icon) inside the button.
/// Options: SVG string --> Icons
/// Default: string.Empty
/// </summary>
[Parameter, EditorRequired]
public string Glyph { get; set; } = string.Empty;
/// <summary>
/// Position of the glyph relative to button Text parameter. If Glyph parameter == string.Empty this parameter is ignored
/// Options: Position.[left,top,right,bottom]
/// Default: Position.left
/// </summary>
[Parameter]
public Position GlyphPosition { get; set; } = Position.Left;
/// <summary>
/// Color for the glyph. If Glyph parameter is empty this parameter is ignored
/// Options: Color.[Core,Primary,Secondary,Success,Info,Warning,Danger,White,Light,Dark]
/// Default: Color.Dark
/// </summary>
[Parameter]
public Color GlyphColor { get; set; } = Color.Dark;
#endregion
}

@ -1,7 +1,6 @@
<a
class="@LinkClassList"
style="@LinkStyleList"
href="@Url"
target="@Target" >
<a class="@LinkClassList"
style="@LinkStyleList"
href="@Url"
target="@_target">
@Text
</a>

@ -1,49 +1,86 @@
using Connected.Utilities;
using Connected.Enums;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class Link
{
[Parameter, EditorRequired]
public string Url { get; set; } = string.Empty;
[Parameter, EditorRequired]
public string Text { get; set; } = string.Empty;
[Parameter, EditorRequired]
public string Target { get; set; } = "_self";
[Parameter]
public string Class { get; set; } = string.Empty;
private string LinkClassList
{
get
{
return new CssBuilder()
.AddClass(Class)
.Build();
}
}
[Parameter]
public string Style { get; set; } = string.Empty;
private string LinkStyleList
{
get
{
return new StyleBuilder()
.AddStyle(Style)
.Build();
}
}
protected override async Task OnParametersSetAsync()
{
if (string.IsNullOrEmpty(Text)) Text = Url;
await base.OnParametersSetAsync();
}
/// <summary>
/// URL of the link
/// Options: Any valid web page adress
/// Default: string.Empty
/// </summary>
[Parameter, EditorRequired]
public string Url { get; set; } = string.Empty;
/// <summary>
/// Text shown for the link. If this is not provided URL is used
/// Options: Any valid string
/// Default: string.Empty
/// </summary>
[Parameter]
public string Text { get; set; } = string.Empty;
/// <summary>
/// Target where the link shall open
/// Options: Target.[Self,Parent,Top,Blank]
/// Default: Target.Self
/// </summary>
[Parameter]
public Target Target { get; set; } = Target.Self;
private string _target
{
get
{
return Helper.GetEnumDescription<Target>(Target);
}
}
/// <summary>
/// Class name or multiple classes separated by space
/// Options: Any valid class name or names separated by space
/// Default: string.Empty
/// </summary>
[Parameter]
public string Class { get; set; } = string.Empty;
private string LinkClassList
{
get
{
return new CssBuilder()
.AddClass(Class)
.Build();
}
}
/// <summary>
/// Style string for the link
/// Options: Any valid CSS style
/// Default: string.Empty
/// </summary>
[Parameter]
public string Style { get; set; } = string.Empty;
private string LinkStyleList
{
get
{
return new StyleBuilder()
.AddStyle(Style)
.Build();
}
}
protected override async Task OnInitializedAsync()
{
//if Text parameter is not provided we set it to match URL
if (string.IsNullOrEmpty(Text))
Text = Url;
await base.OnInitializedAsync();
}
}

@ -0,0 +1,25 @@
@using Connected.Models.Modal;
@if (IsVisible)
{
<div class="modal fade show" @onclick="@CloseIfEnabled" @onkeydown="@(e => CheckEscape(e))" tabindex="-1" @ref="@root">
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
<div class="modal-content" @onclick="PreventClose">
@if (!ModalOptions.NoHeader)
{
<div class="modal-header">
<h3 class="modal-title">@Title</h3>
</div>
}
<div class="modal-body">
@Content
</div>
<div class="modal-footer">
@foreach (ModalButton button in buttons)
{
<button type="button" class="btn @button.GetButtonClass" @onclick="@(()=>CloseModal(button))">@button.ButtonText</button>
}
</div>
</div>
</div>
</div>
}

@ -0,0 +1,108 @@
using Connected.Models.Modal;
using Connected.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Connected.Components;
public partial class ModalDialog : IDisposable
{
[Inject] ModalDialogService? ModalService { get; set; }
protected ElementReference root;
protected bool IsVisible { get; set; }
protected string? Title { get; set; }
protected RenderFragment? Content { get; set; }
protected bool OverlayClickToClose { get; set; } = true;
protected List<ModalButton> buttons { get; set; } = new();
protected ModalOptions? ModalOptions { get; set; }
protected override void OnInitialized()
{
ModalService.OnShow += ShowModal;
ModalService.OnClose += CloseModal;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (IsVisible)
await root.FocusAsync();
}
public void ShowModal(string title, RenderFragment content, List<ModalButton> buttons, ModalOptions options)
{
Title = title;
Content = content;
IsVisible = true;
ModalOptions = options;
this.buttons = buttons;
StateHasChanged();
}
public void CloseModal(ModalButton? button)
{
if (button is not null)
{
if (button.CloseDialogOnClick)
{
CloseModal();
}
button.OnClickEvent.Delegate.DynamicInvoke(button.OnClickEvent.args);
}
StateHasChanged();
}
public void CloseModal()
{
IsVisible = false;
Title = "";
Content = null;
StateHasChanged();
}
public void Dispose()
{
if (ModalService is not null)
{
ModalService.OnShow -= ShowModal;
ModalService.OnClose -= CloseModal;
}
}
public void CheckEscape(KeyboardEventArgs args)
{
if (!ModalOptions.DisableEscKey)
{
var key = args.Key.ToLower();
if (key.Equals("escape"))
{
CloseModal();
}
}
}
public void CloseIfEnabled(MouseEventArgs args)
{
if (!ModalOptions.DisableBackdropClick)
{
if (OverlayClickToClose)
{
CloseModal();
}
}
OverlayClickToClose = true;
}
public void PreventClose(MouseEventArgs args)
{
OverlayClickToClose = false;
}
}

@ -0,0 +1,45 @@
@using Connected.Models;
@inherits InputBase;
<div class="@InputFieldClassList">
<textarea value="@Value"
placeholder="@Placeholder"
disabled="@Disabled"
readonly="@Readonly"
required="@Required"
style="overflow-x: hidden; overflow-y: hidden;"
@oninput=@ChangeValueAsync
@attributes="@InputAttributes" />
<span class="highlight"></span>
<span class="bar"></span>
@if (IsLabel)
{
<label class="label-animated">@Label</label>
}
@if (IsHelperText && !IsError)
{
<div class="input-helper-text">@HelperText</div>
}
@if (IsError)
{
<div class="input-error-text">@ErrorText</div>
}
<span class="input-glyph-wraper">
<span class="input-glyph">
@if (Clearable && !string.IsNullOrEmpty(Value))
{
<span class="input-glyph button" @onclick="Clear">
<Glyph SVG="@Icons.Material.Rounded.Dangerous" />
</span>
}
@if (IsError)
{
<span class="input-glyph error">
<Glyph SVG="@Icons.Material.Outlined.Error" />
</span>
}
</span>
</span>
</div>

@ -0,0 +1,119 @@
using Connected.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
namespace Connected.Components;
public partial class MultilineInput : InputBase
{
#region Parameters
private int MinRows { get; set; } = 1;
/// <summary>
/// Number of rows
/// </summary>
[Parameter]
public int Rows
{
get
{
return _numRows;
}
set
{
if (value >= MinRows) _numRows= value;
else _numRows = MinRows;
}
}
private int _numRows = 1;
/// <summary>
/// Value of the TextInput. Used for @bind-Value
/// </summary>
[Parameter]
public string Value { get; set; } = string.Empty;
#endregion
#region Events, Methods
/// <summary>
/// Event triggered when value changes
/// </summary>
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
/// <summary>
/// Method that triggers oninput -> when value inside the component changes
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task ChangeValueAsync(ChangeEventArgs args)
{
int oldRows = Rows;
await ValueChanged.InvokeAsync(args?.Value?.ToString());
int newRows = GetNumberOfLines(args.Value.ToString());
if (newRows == MinRows)
{
Rows = MinRows;
ChangeAttribute("rows", Rows);
StateHasChanged();
}
else
{
Rows = Math.Max(MinRows, newRows);
if (oldRows < Rows)
{
ChangeAttribute("rows", Rows);
StateHasChanged();
}
}
}
private int GetNumberOfLines(string s)
{
int result = Math.Max(s.Split("\r\n").Length, 1);
result = Math.Max(s.Split("\r").Length, result);
result = Math.Max(s.Split("\n").Length, result);
return result;
}
/// <summary>
/// Clear the value of the TextInput
/// </summary>
/// <returns></returns>
private async Task Clear()
{
await ValueChanged.InvokeAsync(string.Empty);
}
#endregion
#region Lifecycle
private void AddAttribute(string key, object value)
{
if (!InputAttributes.ContainsKey(key))
InputAttributes.Add(key, value);
}
private void ChangeAttribute(string key, object value)
{
if (InputAttributes.ContainsKey(key)) InputAttributes.Remove(key);
InputAttributes.Add(key, value);
}
protected override void OnInitialized()
{
base.OnInitialized();
MinRows = Rows;
AddAttribute("rows", MinRows);
}
#endregion
}

@ -7,13 +7,14 @@
<div class="@InputFieldClassList">
<input type="text"
placeholder="@Placeholder"
step="@_step"
step="@Step"
disabled="@Disabled"
readonly="@Readonly"
required="@Required"
value="@_value"
@onkeydown=@(args => ChangeValue(args))
@onkeydown:preventDefault="@_preventDefaultAction"
@oninput=@GetValueAsync
@oninput=@SetValueAsync
@onmousewheel=@OnMouseWheel
@onchange="@Change"
@onwheel="OnMouseWheel"
@ -39,18 +40,16 @@
<Glyph Width=16 Height=16 SVG="@Icons.Material.Outlined.KeyboardArrowUp" Click="StepUp" />
<Glyph Width=16 Height=16 SVG="@Icons.Material.Outlined.KeyboardArrowDown" Click="StepDown"></Glyph>
</span>
@if (Clearable && Value.ToString().Length > 0)
@if (Clearable && !string.IsNullOrEmpty(Value?.ToString()))
{
<span class="input-glyph button" @onclick="Clear">
<Glyph SVG="@Icons.Material.Rounded.Dangerous" />
<!--<i class='bx bx-x-circle'></i>-->
</span>
}
@if (IsError)
{
<span class="input-glyph error">
<Glyph SVG="@Icons.Material.Outlined.Error" Color="#D10000" />
<!--<i class='bx bx-error-circle'></i>-->
<Glyph SVG="@Icons.Material.Outlined.Error" />
</span>
}
</span>

@ -5,142 +5,189 @@ using Microsoft.AspNetCore.Components.Web;
using System.Numerics;
namespace Connected.Components;
public partial class NumberInput<NumberType>:InputBase where NumberType : INumber<NumberType>
public partial class NumberInput<NumberType> : InputBase where NumberType : INumber<NumberType>
{
private double _step =1;
/// <summary>
/// Step for up and down on numeric field
/// Options: Any double number
/// Default: 1
/// </summary>
[Parameter]
public double Step {
get
{
return _step;
}
set
{
_step=value;
}
}
public double Step { get; set; } = 1;
/// <summary>
/// Mouse wheel disable to prevent StepUp/StepDown on number filed
/// Options: true, false
/// Default: false
/// </summary>
[Parameter]
public bool DisableMouseWheel
{
get;
set;
} = false;
public bool DisableMouseWheel { get; set; } = false;
/// <summary>
/// Increase 'Value' for the 'Step'
/// </summary>
/// <returns>'Value' increased for the 'Step' parameter</returns>
private async Task StepUp()
{
try
{
double num = (double)Convert.ChangeType(Value, typeof(double));
num += _step;
var num = Helper.ConvertToType<double>(Value);
num += Step;
if (DecimalPlaces > 0)
num = Math.Round(num, DecimalPlaces);
Value = (NumberType)Convert.ChangeType(num, typeof(NumberType));
if (IsError) ErrorText = string.Empty;
Value = Helper.ConvertToType<NumberType>(num);
if (IsError)
ErrorText = string.Empty;
}
catch
{
ErrorText = "Error with step up!";
Value = default(NumberType);
Value = default;
}
await ValueChanged.InvokeAsync(Value);
}
/// <summary>
/// Decrease 'Value' for the 'Step'
/// </summary>
/// <returns>'Value' decreased for the 'Step' parameter</returns>
private async Task StepDown()
{
try
{
double num = (double)Convert.ChangeType(Value, typeof(double));
num -= _step;
var num = Helper.ConvertToType<double>(Value);
num -= Step;
if (DecimalPlaces > 0)
num = Math.Round(num, DecimalPlaces);
Value = (NumberType)Convert.ChangeType(num, typeof(NumberType));
if (IsError) ErrorText = string.Empty;
} catch
Value = Helper.ConvertToType<NumberType>(num);
if (IsError)
ErrorText = string.Empty;
}
catch
{
ErrorText = "Error with step down!";
Value = default(NumberType);
Value = default;
}
await ValueChanged.InvokeAsync(Value);
}
/// <summary>
/// Event triggered when mouse wheel is activated inside component
/// </summary>
/// <param name="args">WheelEventArgs argument</param>
/// <returns>Doesnt return values just increasing/decreasing values</returns>
protected async Task OnMouseWheel(WheelEventArgs args)
{
if (DisableMouseWheel == false)
if (DisableMouseWheel)
return;
if (args.ShiftKey || Disabled || Readonly)
return;
if (args.DeltaY >= 0)
{
if (args.ShiftKey || Disabled || Readonly)
return;
if (args.DeltaY >= 0)
{
await StepDown();
}
else
{
await StepUp();
}
} else
await StepDown();
}
else
{
return;
await StepUp();
}
}
private string _value;
private string? _value;
/// <summary>
/// Value of any numeric type
/// Options: any numeric type variable
/// Default: null
/// </summary>
[Parameter]
[EditorRequired]
public NumberType? Value
{
get
{
if (string.IsNullOrEmpty(_value)) return default(NumberType);
return (NumberType)Convert.ChangeType(_value, typeof(NumberType));
if (string.IsNullOrEmpty(_value))
return default;
else
{
try
{
return Helper.ConvertToType<NumberType>(_value);
} catch
{
return default;
}
}
}
set
{
_value = value.ToString();
_value = value?.ToString();
}
}
/// <summary>
/// Number of decimal places for Value. If set, Value is corrected when input looses focus
/// Options: any integer number greater or equal 0
/// Default: 0
/// </summary>
[Parameter]
public int DecimalPlaces { get; set; } =0;
public int DecimalPlaces { get; set; } = 0;
/// <summary>
/// Value change event
/// </summary>
[Parameter]
public EventCallback<NumberType> ValueChanged { get; set; }
public EventCallback<NumberType> ValueChanged { get; set; }
public async Task GetValueAsync(ChangeEventArgs args)
{
public async Task SetValueAsync(ChangeEventArgs args)
{
if (args.Value is not null)
{
string newVal = args.Value.ToString();
var newVal = args.Value.ToString()!;
if (!newVal.Equals("0"))
{
if (newVal.ToString().Contains("-"))
newVal = "-" + newVal.ToString().Replace("-", "");
if (newVal.ToString().ToLower().Contains("e"))
newVal = "e" + newVal.ToString().Replace("e", "");
}
if (string.IsNullOrEmpty(newVal))
{
await ValueChanged.InvokeAsync((NumberType)Convert.ChangeType((NumberType)Convert.ChangeType(null, typeof(NumberType)), typeof(NumberType)));
}
else
{
if (!newVal.Equals(_value))
await ValueChanged.InvokeAsync((NumberType)Convert.ChangeType((NumberType)Convert.ChangeType(newVal, typeof(NumberType)), typeof(NumberType)));
}
await ValueChanged.InvokeAsync(default);
if (!newVal.Equals(_value))
await ValueChanged.InvokeAsync(Helper.ConvertToType<NumberType>(newVal));
}
}
public async Task Change(ChangeEventArgs args)
{
if (args.Value is not null)
{
Value = AdjustDecimalPlaces((NumberType)Convert.ChangeType(args.Value, typeof(NumberType)));
}
ValueChanged.InvokeAsync(Value);
Value = AdjustDecimalPlaces(Helper.ConvertToType<NumberType>(args.Value));
await ValueChanged.InvokeAsync(Value);
}
[Parameter] public EventCallback<KeyboardEventArgs> OnKeyDown { get; set; }
/// <summary>
/// On keyboard key press event
/// </summary>
[Parameter]
public EventCallback<KeyboardEventArgs> OnKeyDown { get; set; }
private bool CheckKey(string key)
{
@ -159,7 +206,7 @@ public partial class NumberInput<NumberType>:InputBase where NumberType : INumbe
break;
}
default:
{
{
result = false;
break;
}
@ -172,49 +219,48 @@ public partial class NumberInput<NumberType>:InputBase where NumberType : INumbe
private bool _preventDefaultAction = true;
public async Task ChangeValue(KeyboardEventArgs args)
{
_preventDefaultAction= true;
_preventDefaultAction = true;
if (args is not null)
{
var key = args.Key.ToString().ToLower();
if (CheckKey(key))
{
_preventDefaultAction = false;
await OnKeyDown.InvokeAsync(args);
}
} else
}
else
{
args.Key = null;
}
}
private NumberType AdjustDecimalPlaces(NumberType value)
/// <summary>
/// Method for adjusting decimal places provided with parameter
/// </summary>
/// <param name="value">Value whose decimal places we want to change</param>
/// <returns>NumberType result with adjusted decimal places</returns>
private NumberType? AdjustDecimalPlaces(NumberType? value)
{
var result = value;
if (DecimalPlaces > 0)
{
double converted = Math.Round((double)Convert.ChangeType(result, typeof(double)), DecimalPlaces);
return (NumberType)Convert.ChangeType(converted, typeof(NumberType));
double converted = Math.Round(Helper.ConvertToType<double>(result), DecimalPlaces);
return Helper.ConvertToType<NumberType>(converted);
}
return result;
}
/// <summary>
/// Clear event for user clear icon click. It clears the Value and set it to
/// </summary>
/// <returns></returns>
private async Task Clear()
{
_value = string.Empty;
await ValueChanged.InvokeAsync((NumberType)Convert.ChangeType(0, typeof(NumberType)));
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (!DecimalPlaces.Equals(Helper.GetDecimalPlaces(Value)))
{
Value = AdjustDecimalPlaces(Value);
StateHasChanged();
}
}
var val = Helper.ConvertToType<NumberType>(null);
await ValueChanged.InvokeAsync(val);
}
#region Lifecycle
@ -222,18 +268,26 @@ public partial class NumberInput<NumberType>:InputBase where NumberType : INumbe
{
if (typeof(NumberType).Name.ToLower().Contains("int"))
{
if (Step - (int)Step > 0) Step = (int)Step;
if (Step < 1) Step = 1;
if (Step - (int)Step > 0)
Step = (int)Step;
if (Step < 1)
Step = 1;
}
await base.OnParametersSetAsync();
}
}
protected override async Task OnInitializedAsync()
protected override void OnAfterRender(bool firstRender)
{
await base.OnInitializedAsync();
if (Required)
if (firstRender)
{
if (!InputAttributes.ContainsKey("required")) InputAttributes.Add("required", true);
if (Value is not null)
{
if (!DecimalPlaces.Equals(Helper.GetDecimalPlaces(Value)))
{
Value = AdjustDecimalPlaces(Value);
StateHasChanged();
}
}
}
}

@ -2,7 +2,20 @@
@inherits InputBase;
<div class="radio-group">
<input id="@Id" name="@ParentRadioGroup.Name" type="radio" @onchange="OnChange" @attributes=@InputAttributes>
<label for="@Id" class="radio-label">@Label</label>
</div>
<label class="radio-group"
for="@Id">
<input class="@ClassNameList"
id="@Id"
name="@ParentRadioGroup?.Name"
type="radio"
@onchange="OnChange"
@attributes=@InputAttributes
disabled="@Disabled"
readonly="@Readonly"
checked="@Checked">
<div class="radio-fill"></div>
<label for="@Id"
class="@LabelClassNameList">@Label</label>
</label>

@ -1,9 +1,11 @@
using Connected.Models;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class Radio: InputBase
public partial class Radio : InputBase
{
#region Parameters
[CascadingParameter]
public RadioGroup? ParentRadioGroup { get; set; }
@ -11,21 +13,51 @@ public partial class Radio: InputBase
public bool Checked { get; set; } = false;
[Parameter, EditorRequired]
public string Id { get; set; }
public string? Id { get; set; }
#endregion
#region Events
[Parameter]
public EventCallback<bool> CheckedChanged { get; set; }
public async Task OnChange()
{
Checked = !Checked;
await CheckedChanged.InvokeAsync(Checked);
}
#endregion
#region Style
[Parameter]
public EventCallback<bool> CheckedChange { get; set; }
public async Task OnChange(ChangeEventArgs args)
public string ClassName { get; set; } = string.Empty;
private string ClassNameList
{
Checked = (bool)args.Value;
CheckedChange.InvokeAsync(Checked);
get
{
return new CssBuilder("radio-input")
.AddClass(ClassName)
.Build();
}
}
protected override async Task OnInitializedAsync()
[Parameter]
public string LabelClassName { get; set; } = string.Empty;
private string LabelClassNameList
{
await base.OnInitializedAsync();
if (ParentRadioGroup.Disabled) Disabled = true;
if (!InputAttributes.ContainsKey("disabled"))
InputAttributes.Add("disabled", Disabled);
get
{
return new CssBuilder("radio-label")
.AddClass(LabelClassName)
.Build();
}
}
#endregion
}

@ -1,15 +1,13 @@
@using Connected.Models;
@inherits InputBase;
<CascadingValue Value="this">
<div>
@if (!string.IsNullOrEmpty(Name))
{
<h5>@Name</h5>
}
<div class="container">
@ChildContent
</div>
</div>
<div>
@if (!string.IsNullOrEmpty(Name))
{
<h5>@Name</h5>
}
<div class="container">
@ChildContent
</div>
</div>
</CascadingValue>

@ -3,10 +3,32 @@
namespace Connected.Components;
public partial class RadioGroup
{
#region Parameters
/// <summary>
/// Radio group name. Mandatory! Used for proper radio button grouping
/// Options: any string will do
/// Default: 'radiogroup'
/// </summary>
[Parameter, EditorRequired]
public string Name { get; set; }
public string? Name { get; set; } = "radiogroup";
/// <summary>
/// Used for globaly disabling radio button group and all the radios within
/// Options: true or false
/// Default: false
/// </summary>
[Parameter]
public bool Disabled { get; set; } = false;
/// <summary>
/// All the radiobuttons and other components inside radio group
/// </summary>
[Parameter]
public RenderFragment ChildContent { get; set; }
public RenderFragment? ChildContent { get; set; }
#endregion
}
}

@ -4,19 +4,23 @@
@inherits InputBase;
@if (Items is not null)
@if (component_loaded)
{
<div class="@InputFieldClassList">
<select type="textarea" style="height:0px;" @attributes=@InputAttributes>
</select>
@if (Items is not null)
{
<div class="@InputFieldClassList">
<select type="textarea"
style="height:0px;"
@attributes=@InputAttributes></select>
@if (IsLabel)
{
<label class="label-animated">@Label</label>
}
<span class="highlight"></span>
<span class="bar">
</span>
<span class="bar"></span>
<span class="input-glyph-wraper">
@if (Clearable)
{
@ -24,9 +28,9 @@
<i class='bx bx-x-circle'></i>
</span>
}
<span class="input-glyph">
<i class='bx bx-caret-down'></i>
</span>
<span class="input-glyph">
<i class='bx bx-caret-down'></i>
</span>
@if (IsError)
{
<span class="input-glyph error">
@ -35,22 +39,27 @@
}
</span>
<div class="drop-down">
<div class="dropdown-menu p-2" aria-labelledby="dropdownMenuButton">
<div class="dropdown-menu p-2"
aria-labelledby="dropdownMenuButton">
@if (EnableSearch)
{
<input type="text"
placeholder="Enter search string..."
class="dropdown-item"
@bind-value="@SearchText" />
placeholder="Enter search string..."
class="dropdown-item"
@bind-value="@SearchText" />
}
@foreach (ValueType item in Items)
@foreach (ValueType item in FilteredItems)
{
@if (item is not null)
{
<div class="dropdown-item" @onclick=@(()=>SetSelectedItem(@item))>@item.ToString()</div>
<div class="dropdown-item"
@onclick=@(()=>SetSelectedItem(@item))>
@item.ToString()
</div>
}
}
</div>
</div>
</div>
</div>
}
}

@ -1,21 +1,46 @@
using Connected.Models;
using Microsoft.AspNetCore.Components;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Connected.Components;
public partial class SimpleSelect<ValueType> : InputBase
{
private bool component_loaded = false;
#region Parameters
/// <summary>
/// Value that is currently selected in the dropdown. Used for @bind-Value
/// </summary>
[Parameter]
public ValueType Value { get; set; }
public ValueType? Value { get; set; }
[Parameter]
public IEnumerable<ValueType> Items { get; set; }
/// <summary>
/// Collection of Items to work on (Filter). Filtered result is then shown in dropdown. If no filter is aplied, all the items are shown
/// </summary>
[Parameter, EditorRequired]
public ObservableCollection<ValueType>? Items { get; set; }
public IEnumerable<ValueType> OriginalItems { get; set; }
/// <summary>
/// Collection of items from 'Items' filtered with 'SearchText'
/// </summary>
private List<ValueType>? FilteredItems { get; set; }
/// <summary>
/// Enable edit text search box for item filtering
/// Options: true or false
/// Default: true
/// </summary>
[Parameter]
public bool EnableSearch { get; set; } = true;
private string _searchText { get; set; } = string.Empty;
/// <summary>
/// Search string provided by user
/// Options: any string
/// Default: string.Empty
/// </summary>
public string SearchText
{
get
@ -29,48 +54,92 @@ public partial class SimpleSelect<ValueType> : InputBase
}
}
#endregion
#region Events, Methods
/// <summary>
/// Method for setting the item on select
/// </summary>
/// <param name="item">item that will be set as selected</param>
/// <returns>Methot returns nothing</returns>
private async Task SetSelectedItem(ValueType item)
{
//DropDownClassToggle();
await ValueChanged.InvokeAsync(item);
}
/// <summary>
/// Method for filtering items using 'SearchText' as filter
/// </summary>
private void FilterItems()
{
if (string.IsNullOrEmpty(_searchText))
{
Items = OriginalItems;
}
else
if (Items is not null)
{
Items = Items.Where(item => item.ToString().ToLower().Contains(_searchText.ToLower()));
if (string.IsNullOrEmpty(_searchText))
{
SetItems();
}
else
{
FilteredItems = Items.Where(item => item.ToString().ToLower().Contains(_searchText.ToLower())).ToList();
}
StateHasChanged();
}
StateHasChanged();
}
/// <summary>
/// Event triggered when value changes
/// </summary>
[Parameter]
public EventCallback<ValueType> ValueChanged { get; set; }
private async Task ChangeValueAsync(ChangeEventArgs args)
/// <summary>
/// Method for setting the FilteredItems collection keeping the original Item collection
/// </summary>
private void SetItems()
{
await ValueChanged.InvokeAsync((ValueType)Convert.ChangeType(args.Value, typeof(ValueType)));
if (Items is not null)
{
FilteredItems = Items.ToList();
}
}
protected override async Task OnParametersSetAsync()
/// <summary>
/// Event triggered when the provided Items collection changes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OriginalItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
OriginalItems = Items;
if (_searchText.Length>0) FilterItems();
await base.OnParametersSetAsync();
SetItems();
FilterItems();
}
#endregion
#region Lifecycle
/// <summary>
/// Initializing Collections and aplying Filters if provided
/// </summary>
/// <returns>Nothing gets returned</returns>
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (Required)
if (Items is null)
{
if (InputAttributes.ContainsKey("required")) InputAttributes.Add("required", true);
Items= new ObservableCollection<ValueType>();
}
SetItems();
if (_searchText.Length > 0)
FilterItems();
Items.CollectionChanged += OriginalItems_CollectionChanged;
component_loaded = true;
StateHasChanged();
}
#endregion
}

@ -3,27 +3,15 @@
@inherits InputBase;
<div class="@InputFieldClassList">
@if (NumOfRows==1)
{
<input type="@inputType"
value="@Value"
placeholder="@Placeholder"
disabled="@Disabled"
readonly="@Readonly"
@oninput=@ChangeValueAsync
@attributes="@InputAttributes" />
} else
{
<textarea type="textarea"
rows="@NumOfRows"
<input type="@InputType"
value="@Value"
placeholder="@Placeholder"
disabled="@Disabled"
readonly="@Readonly"
required="@Required"
@oninput=@ChangeValueAsync
@attributes="@InputAttributes" />
}
<span class="highlight"></span>
<span class="bar"></span>
@ -45,16 +33,15 @@
{
<span class="input-glyph button" @onclick="Clear">
<Glyph SVG="@Icons.Material.Rounded.Dangerous" />
<!--<i class='bx bx-x-circle'></i>-->
</span>
}
@if (IsError)
{
<span class="input-glyph error">
<Glyph SVG="@Icons.Material.Outlined.Error" Color="#D10000" />
<!--<i class='bx bx-error-circle'></i>-->
<Glyph SVG="@Icons.Material.Outlined.Error" />
</span>
}
</span>
</span>
</div>
</div>

@ -2,67 +2,74 @@
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class TextInput: InputBase
public partial class TextInput : InputBase
{
#region Parameters
/// <summary>
/// Setter for the TextInput if input is password field (for hiding characters)
/// Options: true or false
/// Default: false;
/// </summary>
[Parameter]
public bool IsPassword { get; set; } = false;
[Parameter]
public int NumOfRows
{
get
{
return _numberOfLines;
}
set
{
if (value < 1)
{
_numberOfLines = 1;
}
else
{
_numberOfLines = value;
}
}
}
private int _numberOfLines = 1;
/// <summary>
/// String for the component to use if TextInput is password field
/// </summary>
private string InputType => IsPassword ? "password" : "text";
/// <summary>
/// Number of rows
/// </summary>
[Parameter]
public string Value { get; set; } = string.Empty;
private string inputType
public int NumOfRows
{
get
{
if (IsPassword) return "password";
return "text";
return _numberOfLines;
}
set
{
_numberOfLines = Math.Max(1, value);
}
}
private int _numberOfLines = 1;
/// <summary>
/// Value of the TextInput. Used for @bind-Value
/// </summary>
[Parameter]
public string Value { get; set; } = string.Empty;
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
#endregion
private async Task ChangeValueAsync(ChangeEventArgs args)
{
await ValueChanged.InvokeAsync(args.Value.ToString());
}
#region Events, Methods
/// <summary>
/// Event triggered when value changes
/// </summary>
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
/// <summary>
/// Method that triggers oninput -> when value inside the component changes
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task ChangeValueAsync(ChangeEventArgs args)
{
await ValueChanged.InvokeAsync(args?.Value?.ToString());
}
/// <summary>
/// Clear the value of the TextInput
/// </summary>
/// <returns></returns>
private async Task Clear()
{
await ValueChanged.InvokeAsync(string.Empty);
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (Required)
{
if (!InputAttributes.ContainsKey("required")) InputAttributes.Add("required", true);
}
}
#endregion
}

@ -0,0 +1,21 @@
@inherits Button
<button type="button"
@onclick="@Clicked"
disabled=@Disabled
style="@StyleList"
class="@ClassList">
<div class="@ContentClassList">
<div style="align-items:center">
@if (GlyphPosition == Position.Top)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" />
}
@ChildContent
@if (GlyphPosition == Position.Bottom)
{
<Glyph SVG="@Glyph" Color="@GlyphColor" />
}
</div>
</div>
</button>

@ -0,0 +1,120 @@
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Connected.Components;
public partial class ToggleGlyphButton: Button
{
#region Parameters
/// <summary>
/// Outline type of the button.
/// Options: true, false
/// Default: false
/// </summary>
[Parameter]
public bool Toggled { get; set; } = false;
/// <summary>
/// Glyph (Icon) inside the button.
/// Options: SVG string --> Icons
/// Default: string.Empty
/// </summary>
[Parameter, EditorRequired]
public string Glyph { get; set; } = string.Empty;
/// <summary>
/// Glyph (Icon) inside the button when tge .
/// Options: SVG string --> Icons
/// Default: string.Empty
/// </summary>
[Parameter, EditorRequired]
public string ToggledGlyph { get; set; } = string.Empty;
/// <summary>
/// Position of the glyph relative to button Text parameter. If Glyph parameter == string.Empty this parameter is ignored
/// Options: Position.[left,top,right,bottom]
/// Default: Position.left
/// </summary>
[Parameter]
public Position GlyphPosition { get; set; } = Position.Left;
/// <summary>
/// Color for the glyph. If Glyph parameter is empty this parameter is ignored
/// Options: Color.[Core,Primary,Secondary,Success,Info,Warning,Danger,White,Light,Dark]
/// Default: Color.Dark
/// </summary>
[Parameter]
public Color GlyphColor { get; set; } = Color.Dark;
/// <summary>
/// Color for the glyph. If Glyph parameter is empty this parameter is ignored
/// Options: Color.[Core,Primary,Secondary,Success,Info,Warning,Danger,White,Light,Dark]
/// Default: Color.Dark
/// </summary>
[Parameter]
public Color ToggledGlyphColor { get; set; } = Color.Dark;
#endregion
#region Events
/// <summary>
/// Button click event.
/// Options: any MouseEventCallback event
/// Default: empty
[Parameter]
public EventCallback<bool> ToggledChanged { get; set; }
protected async Task Clicked(MouseEventArgs e)
{
Toggled = !Toggled;
await ToggledChanged.InvokeAsync(Toggled);
}
#endregion
#region Styling
public string StyleList
{
get
{
return new StyleBuilder()
.AddStyle(base.Style)
.Build();
}
}
/// <summary>
/// Generated class list for button based on user parameters
/// </summary>
public string ClassList
{
get
{
return new CssBuilder("btn")
.AddClass("btn-" + Helper.GetEnumDescription<Size>(base.Size))
.AddClass("btn-" + Helper.GetEnumDescription<Color>(base.Color),!base.Outlined)
.AddClass("btn-outline-" + Helper.GetEnumDescription<Color>(base.Color), base.Outlined)
.AddClass(base.Class)
.Build();
}
}
/// <summary>
/// Generated class list for button based on user parameters
/// </summary>
public string ContentClassList
{
get
{
return new CssBuilder("")
.AddClass(base.ContentClass)
.Build();
}
}
#endregion
}

@ -0,0 +1,17 @@
@using Connected.Models;
@inherits InputBase;
<label class="toggle-group" for="@Id">
<input class="toggle-input"
type="checkbox"
name="toggle"
disabled="@Disabled"
id="@Id"
checked="@Checked"
@onchange="@OnChange"
@attributes=@InputAttributes>
<div class="toggle-fill"></div>
<label for="@Id" class="toggle-label">@Label</label>
</label>

@ -0,0 +1,27 @@
using Connected.Models;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class ToggleInput: InputBase
{
private bool _checked { get; set; }
[Parameter]
public bool? Checked
{
get => _checked;
set => _checked= (bool)value;
}
[Parameter, EditorRequired]
public string Id { get; set; }
[Parameter]
public EventCallback<bool> CheckedChanged { get; set; }
public async Task OnChange()
{
Checked = !Checked;
await CheckedChanged.InvokeAsync((bool)Checked);
}
}

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Connected.Enums;
public enum ModalButtonType
{
OkButton,
CancelButton,
RegularButton
}

@ -0,0 +1,18 @@
using System.ComponentModel;
namespace Connected.Enums;
public enum Target
{
[Description("_top")]
Top,
[Description("_parent")]
Parent,
[Description("_blank")]
Blank,
[Description("_self")]
Self,
}

@ -30,16 +30,20 @@ public class InputBase : ComponentBase
[Parameter]
public bool Required { get; set; } = false;
public Dictionary<string, object> InputAttributes { get; set; }
public Dictionary<string, object> InputAttributes { get; set; } = new();
/// <summary>
/// Show clear button.
/// Disable input component
/// Options: true or false
/// Default: false
/// </summary>
[Parameter]
public bool Disabled { get; set; } = false;
/// <summary>
/// Show clear button.
/// Make input component readonly
/// Options: true or false
/// Default: false
/// </summary>
[Parameter]
public bool Readonly { get; set; } = false;
@ -50,18 +54,11 @@ public class InputBase : ComponentBase
[Parameter]
public bool ShowCharacterCounter { get; set; }
private string _errorText = string.Empty;
/// <summary>
///
/// </summary>
[Parameter]
public string ErrorText {
get
{
return _errorText;
}
set
{
_errorText = value;
}
}
public string ErrorText { get; set; } = string.Empty;
public bool IsError
{
@ -79,15 +76,16 @@ public class InputBase : ComponentBase
protected virtual async Task SetTextAsync(string text)
{
if (Text != text)
{
Text = text;
await TextChanged.InvokeAsync(text);
}
if (Text == text)
return;
Text = text;
await TextChanged.InvokeAsync(text);
}
private string _helperText = string.Empty;
[Parameter]
public string HelperText
{
@ -123,7 +121,7 @@ public class InputBase : ComponentBase
[Parameter]
public string Placeholder { get; set; } = string.Empty;
protected override async Task OnInitializedAsync()
protected override void OnInitialized()
{
if (InputAttributes is null) InputAttributes = new();
}

@ -0,0 +1,36 @@
using Connected.Enums;
using Connected.Services;
namespace Connected.Models.Modal;
public class ModalButton
{
public Event OnClickEvent { get; set; }
public ModalButtonType ModalButtonType { get; set; } = ModalButtonType.RegularButton;
public string ButtonText { get; set; }
public bool CloseDialogOnClick { get; set; } = true;
public ModalButton(Event OnClickEvent, string ButtonText, ModalButtonType ModalButtonType = ModalButtonType.RegularButton, bool CloseDialogOnClick = true)
{
this.OnClickEvent = OnClickEvent;
this.ButtonText = ButtonText;
this.ModalButtonType= ModalButtonType;
this.CloseDialogOnClick = CloseDialogOnClick;
}
public string GetButtonClass
{
get
{
switch (this.ModalButtonType)
{
case ModalButtonType.CancelButton:
return "btn-sm btn-core";
case ModalButtonType.OkButton:
return "btn-sm btn-info";
default:
return "btn-sm btn-secondary";
}
}
}
}

@ -0,0 +1,12 @@
namespace Connected.Models.Modal;
public class Event
{
public Delegate Delegate;
public object[] args;
public Event(Delegate Delegate, object[] Args)
{
this.Delegate = Delegate;
args = Args;
}
}

@ -0,0 +1,10 @@
namespace Connected.Models.Modal;
public class ModalOptions
{
public bool DisableEscKey { get; set; } = false;
public bool DisableBackdropClick { get; set; } = false;
public bool NoHeader { get; set; } = false;
}

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Components;
using Connected.Enums;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Connected;
@ -24,8 +26,9 @@ internal class Navigation
/// Navigates to the specified url.
/// </summary>
/// <param name="url">The destination url (relative or absolute).</param>
public async Task NavigateTo(string url, Target target=Target._self)
public async Task NavigateTo(string url, Target t=Target.Self)
{
string target = Helper.GetEnumDescription<Target>(t);
if (!target.Equals("_self"))
{
if (!url.Equals(_navigationManager.Uri))
@ -110,10 +113,4 @@ internal class Navigation
}
}
enum Target
{
_self,
_blank,
_parent,
_top
}

@ -0,0 +1,28 @@
using Connected.Components;
using Connected.Models.Modal;
using Microsoft.AspNetCore.Components;
namespace Connected.Services;
public class ModalDialogService
{
public event Action<string, RenderFragment, List<ModalButton>, ModalOptions> OnShow;
public event Action OnClose;
public void ShowDialog(string title, RenderFragment content, List<ModalButton> buttons, ModalOptions options)
{
OnShow?.Invoke(title, content, buttons, options);
}
public void ShowDialog(string title, MarkupString contentMarkup, List<ModalButton> buttons, ModalOptions options)
{
var content = new RenderFragment(x => x.AddContent(1, contentMarkup));
OnShow?.Invoke(title, content, buttons, options);
}
public void Close()
{
OnClose?.Invoke();
}
}

@ -0,0 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
namespace Connected.Services;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddModalDialogService(this IServiceCollection services)
=> services.AddScoped<ModalDialogService>();
}

@ -1,4 +1,4 @@
@import 'globals/_index';
@import 'layout/_index';
@import 'components/_index';
@import 'util/_index';
@forward 'globals';
@forward 'layout';
@forward 'components';
@forward 'util';

@ -12,12 +12,12 @@
user-select: none;
background-color: $bg-color;
border: 1px solid transparent;
padding: 0.375rem 1rem;
padding: 0.3rem 1.35rem;
font-size: $base-font-size;
text-transform: uppercase;
line-height: $base-font-size * 1.5;
text-decoration: none;
border-radius: $base-border-radius * 20;
border-radius: $border-radius-pill;
transition: $transition;
}
@ -46,12 +46,12 @@
}
.btn-outline-#{$key} {
// @include btn();
color: $val;
color: darken($val, 10);
background-color: transparent;
border: 1px solid $val;
border: 1px solid darken($val, 10);
&:hover {
color: darken($val, 10);
border: 1px solid darken($val, 10);
color: darken($val, 30);
border: 1px solid darken($val, 30);
}
&:focus-visible,
&.focus {
@ -67,24 +67,24 @@
}
.btn.btn-sm {
font-size: $font-size-sm;
line-height: $font-size-sm * 1.5;
padding: 0.25rem 0.75rem;
border-radius: $base-border-radius;
font-size: .75rem;
line-height: $font-size-sm * 1.25;
padding: 0.4rem 1rem;
//border-radius: $base-border-radius;
}
.btn.btn-lg {
font-size: $font-size-md;
line-height: $font-size-md * 1.5;
padding: 0.5rem 1.25rem;
border-radius: $base-border-radius * 2.5;
padding: 0.5rem 1.65rem;
//border-radius: $base-border-radius * 2.5;
}
.btn.btn-block {
font-size: $font-size-md;
line-height: $font-size-md * 1.5;
padding: 0.5rem 1.25rem;
border-radius: $base-border-radius * 2.5;
padding: 0.5rem 1.5rem;
//border-radius: $base-border-radius * 2.5;
display: block;
width: 100%;
}
@ -114,12 +114,12 @@
}
.btn-outline-core {
color: var(--bg-core-primary) !important;
color: var(--bg-core-primary-darken) !important;
background-color: transparent !important;
border: 1px solid var(--bg-core-primary) !important;
border: 1px solid var(--bg-core-primary-darken) !important;
&:hover {
color: var(--bg-core-primary-darken) !important;
border: 1px solid var(--bg-core-primary-darken) !important;
color: var(--bg-core-primary-dark) !important;
border: 1px solid var(--bg-core-primary-dark) !important;
}
&:focus-visible,
&.focus {

@ -1,102 +1,188 @@
$color3: #f4f4f4;
$color4: var(--bg-core-primary-darken);
$checkbox-primary-color: var(--bg-core-primary-darken);
$checkbox-secondary-color: #f4f4f4;
$checkbox-disabled-color: darken($checkbox-secondary-color, 25%);
.checkbox-group {
margin: 0.5rem;
position:relative;
input[type="checkbox"] {
position: absolute;
opacity: 0;
+ .checkbox-label {
&:before {
content: "";
background: transparent;
border-radius: 0%;
border: 2px solid $color4;
display: inline-block;
width: 1.25em;
height: 1.25em;
position: relative;
top: .15rem;
margin-right: .75em;
vertical-align: top;
cursor: pointer;
text-align: center;
z-index: 1;
transition: all 250ms ease;
}
}
&:checked {
+ .checkbox-label {
&:before {
background-color: $color4;
box-shadow: inset 0 0 0 2px $color3;
--width: 1.25rem;
--height: var(--width);
--inset: calc(var(--width) / 10);
margin: 0.75rem 0;
width: max-content;
cursor: pointer;
position:relative;
display: flex;
align-items: center;
gap:.75rem;
z-index: 1;
&.reversed{
justify-content: space-between;
width: 100%;
& :nth-child(1) {order:2;}
& :nth-child(2) {order:1;}
}
&.column{
flex-direction: column;
gap:.25rem;
width: 100%;
}
}
// &:checked {
// + .checkbox-label {
// &:after {
// background: $color3;
// z-index: 1;
// opacity: 1;
// scale: 0.25;
// clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
// }
// }
// }
&:focus {
+ .checkbox-label {
&:before {
outline: none;
border-color: $color4;
}
}
}
&:disabled {
+ .checkbox-label {
color: darken($color3, 25%);
pointer-events: none;
&:before {
box-shadow: inset 0 0 0 2px $color3;
border-color: darken($color3, 25%);
background: darken($color3, 25%);
}
}
}
+ .checkbox-label {
&:empty {
&:before {
margin-right: 0;
input[type="checkbox"] {
position: absolute;
opacity: 0;
}
}
}
}
.checkbox-fill {
position: relative;
display: inline-block;
width: var(--width);
height: var(--height);
background: transperent;
border: 2px solid $checkbox-primary-color;
transition: background 0.2s ease-in-out;
}
.checkbox-input:checked ~ .checkbox-fill {
background: var(--bg-core-primary-darken);
box-shadow: inset 0 0 0 var(--inset) $checkbox-secondary-color;
}
.checkbox-input:focus-within ~ .checkbox-fill {
outline: 1px solid $checkbox-primary-color;
}
.checkbox-input:disabled ~ .checkbox-fill {
box-shadow: inset 0 0 0 var(--inset) $checkbox-secondary-color;
border-color: $checkbox-disabled-color;
background: $checkbox-disabled-color;
pointer-events: none;
}
.checkbox-input:disabled ~ .checkbox-label {
color: $checkbox-disabled-color;
}
.checkbox-label:after {
content: "";
display: inline-block;
width: 35px;
height: 35px;
border-radius: 50%;
background-color: transparent;
position: absolute;
left: -8px;
top: -6px;
scale: .75;
transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
}
.checkbox-fill::before {
content: "";
display: inline-block;
width: calc(var(--width) * 1.8);
height: calc(var(--width) * 1.8);
border-radius: 50%;
background-color: transparent;
position: absolute;
z-index: -1;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
scale: .75;
transform-origin: left top;
transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25), transform 0.2s;
}
.checkbox-fill:hover::before {
background: var(--bg-core-primary-lighten);
scale: 1;
opacity: .5;
}
// $checkbox-primary-color: var(--bg-core-primary-darken);
// $checkbox-secondary-color: #f4f4f4;
// $checkbox-disabled-color: darken($checkbox-secondary-color, 25%);
// .checkbox-group {
// margin: 0.5rem;
// position:relative;
// input[type="checkbox"] {
// position: absolute;
// opacity: 0;
// + .checkbox-label {
// &:before {
// content: "";
// background: transparent;
// border: 2px solid $checkbox-primary-color;
// display: inline-block;
// width: 1.25em;
// height: 1.25em;
// position: relative;
// top: .15rem;
// margin-right: .75em;
// vertical-align: top;
// cursor: pointer;
// text-align: center;
// z-index: 1;
// transition: all 250ms ease;
// }
// }
// &:checked {
// + .checkbox-label {
// &:before {
// background-color: $checkbox-primary-color;
// box-shadow: inset 0 0 0 2px $checkbox-secondary-color;
// }
// }
// }
// &:focus {
// + .checkbox-label {
// &:before {
// outline: 1px solid $checkbox-primary-color;
// border-color: $checkbox-primary-color;
// }
// }
// }
// &:disabled {
// + .checkbox-label {
// color: darken($checkbox-secondary-color, 25%);
// pointer-events: none;
// &:before {
// box-shadow: inset 0 0 0 2px $checkbox-secondary-color;
// border-color: $checkbox-disabled-color;
// background: $checkbox-disabled-color;
// }
// }
// }
// + .checkbox-label {
// &:empty {
// &:before {
// margin-right: 0;
// }
// }
// }
// }
// }
// .checkbox-label:after {
// content: "";
// display: inline-block;
// width: 35px;
// height: 35px;
// border-radius: 50%;
// background-color: transparent;
// position: absolute;
// left: -8px;
// top: -6px;
// scale: .75;
// transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
// }
.checkbox-label:hover:after {
background: var(--bg-core-primary-lighten);
scale: 1;
opacity: .5;
}
// .checkbox-label:hover:after {
// background: var(--bg-core-primary-lighten);
// scale: 1;
// opacity: .5;
// }

@ -0,0 +1,218 @@
@use "../util" as *;
@use "../globals" as *;
/*CHIP*/
/* scroll container */
.horizontal-scroll-container {
display: flex;
position: relative;
flex-wrap: nowrap;
gap: .5rem;
overflow: auto;
scroll-snap-type: both mandatory;
}
/* Hide scrollbar for Chrome, Safari and Opera */
.horizontal-scroll-container::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.horizontal-scroll-container {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.chip-icon {
--height: 2.5rem;
height: var(--height);
aspect-ratio: 1 / 1;
display: flex;
flex: 1 0 auto;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: var(--bg-core-primary-darken);
overflow: hidden;
cursor: pointer;
transition: all 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
}
.chip-icon:hover,
.chip-icon:active,
.chip-icon:active {
background-color: var(--bg-core-primary-dark);
}
.chip-icon.float-left {
float: left;
margin: .05rem .5rem .15rem 0;
margin-top: -.25rem;
clear: both;
}
.chip-group {
--height: 1.5rem;
border: 1px solid var(--bg-core-primary);
padding: .15rem;
min-width: max-content;
color: var(--text-core);
border-radius: $border-radius-pill;
background-color: var(--bg-core-primary-light);
display: inline-block;
position:relative;
scroll-snap-align: start;
}
.chip-group-content{
height: var(--height);
display: flex;
align-items: center;
justify-content: center;
}
.chip-leading-icon {
display: flex;
align-items: center;
justify-content: center;
border-radius: $border-radius-pill;
background-color: var(--bg-core-primary-light);
height: var(--height);
aspect-ratio: 1 / 1;
overflow: hidden;
}
.chip-leading-icon img{
object-fit: cover;
height: var(--height);
aspect-ratio: 1 / 1;
}
.chip-cta-icon{
display: flex;
align-items: center;
justify-content: center;
border-radius: $border-radius-pill;
background-color: transparent;
cursor: pointer;
height: var(--height);
aspect-ratio: 1 / 1;
transition: all 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
}
.chip-label{
font-size: $font-size-sm;
margin:0 .85rem;
}
.chip-cta-icon:hover{
background-color: var(--bg-core-primary-lighten);
}
/*filter*/
.chip-group.filter {
border: 1px solid var(--bg-core-primary);
background-color: var(--bg-core-primary-light);
cursor: pointer;
overflow: hidden;
&:hover{
background-color: var(--bg-core-primary-lighten);
}
&:active,
&.active{
border: 1px solid var(--bg-core-primary);
background-color: var(--bg-core-primary);
}
}
.chip-group.filter .chip-leading-icon{
//display: none;
max-width: 0;
transform: translateX(calc(var(--height) * -1.25));
transition: all 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
}
.chip-group.filter.active .chip-leading-icon{
display: flex;
align-items: center;
justify-content: center;
border-radius: $border-radius-pill;
background-color: var(--bg-core-primary-light);
height: var(--height);
aspect-ratio: 1 / 1;
transform: translateX(0);
max-width: var(--height);
}
.chip-group.select .chip-cta-icon{
display: none;
}
/*end select*/
/*drop-down*/
/* The container must be positioned relative: */
.custom-select {
position: relative;
font-family: Arial;
}
.custom-select select {
display: none; /*hide original SELECT element: */
}
.select-selected {
background-color: DodgerBlue;
}
/* Style the arrow inside the select element: */
.select-selected:after {
position: absolute;
content: "";
top: 14px;
right: 10px;
width: 0;
height: 0;
border: 6px solid transparent;
border-color: #fff transparent transparent transparent;
}
/* Point the arrow upwards when the select box is open (active): */
.select-selected.select-arrow-active:after {
border-color: transparent transparent #fff transparent;
top: 7px;
}
/* style the items (options), including the selected item: */
.select-items div,.select-selected {
color: #ffffff;
padding: 8px 16px;
border: 1px solid transparent;
border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent;
cursor: pointer;
}
/* Style items (options): */
.select-items {
position: absolute;
background-color: DodgerBlue;
top: 100%;
left: 0;
right: 0;
z-index: 99;
}
/* Hide the items when the select box is closed: */
.select-hide {
display: none;
}
.select-items div:hover, .same-as-selected {
background-color: rgba(0, 0, 0, 0.1);
}
/*end-down*/
/*END CHIP*/

@ -1,3 +1,5 @@
@use "sass:math";
@use "../util" as *;
@use "../globals" as *;
@ -102,3 +104,250 @@ $colspan: ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12");
}
}
}
/*DATA GRID*/
$columns: 12;
@mixin create-selectors($breakpoint: null) {
$infix: if($breakpoint == null, '', '-#{$breakpoint}');
@for $i from 1 through $columns {
.col#{$infix}-#{$i} {
grid-column-end: span $i;
padding: $base-padding;
}
.col-offset#{$infix}-#{$i} {
grid-column-start: $i + 1;
}
.row#{$infix}-#{$i} {
grid-row-end: span $i;
}
.row-offset#{$infix}-#{$i} {
grid-row-start: $i + 1;
}
}
}
.row{
display: grid;
grid-template-columns: repeat($columns, 1fr);
position: relative;
}
@include create-selectors;
@each $breakpoint, $width in $breakpoints-up {
@media (min-width: $width) {
@include create-selectors($breakpoint);
}
}
.data-grid-row-content{
display: grid;
grid-template-columns: auto 1fr auto;
}
.data-grid:not(.data-grid.dense) .data-grid-row-content {
background-color: var(--element-bg-core);
border: 1px solid var(--border-core);
border-radius: $border-radius-lg;
padding: 1rem 1.25rem;
margin-bottom: 1rem;
position: relative;
overflow: hidden;
&:last-child{
margin-bottom: 0;
}
}
/*ACTIVE*/
.data-grid:not(.data-grid.dense) .data-grid-row-content:before {
content: "";
position: absolute;
width: 0;
inset:0;
background: var(--bg-core-primary-lighten);
transition: width 300ms ease-in-out;
}
.data-grid:not(.data-grid.dense) .data-grid-row-content.active:before {
width: calc(100% + 1.25rem * 2);
}
/*END ACTIVE*/
.data-grid.dense {
background-color: var(--element-bg-core);
border: 1px solid var(--border-core);
border-radius: $border-radius-lg;
padding: 1rem 1.25rem;
margin-bottom: 1rem;
overflow: hidden;
&:last-child{
margin-bottom: 0;
}
}
/*DENSE*/
.data-grid.dense .data-grid-row-content {
border-bottom: 1px solid var(--border-core);
padding-bottom: .5rem;
margin-bottom: .5rem;
position: relative;
&:last-child{
border-bottom: 0;
padding-bottom: 0;
margin-bottom: 0
}
}
/*END DENSE*/
/*DENSE ACTIVE*/
.data-grid.dense .data-grid-row-content:before {
content: "";
position: absolute;
width: 0;
inset: -.55rem -1.25rem -1px;
background: var(--bg-core-primary-lighten);
transition: width 300ms ease-in-out;
}
.data-grid.dense .data-grid-row-content.active:before {
width: calc(100% + 1.25rem * 2);
}
.data-grid.dense .data-grid-row-content:first-child:before {
top: -1rem;
bottom: -1px;
}
.data-grid.dense .data-grid-row-content:last-child:before {
top: -10px;
bottom: -1rem;
}
.data-grid.dense .data-grid-row-content:only-child:before {
inset: -1rem -1.25rem ;
}
/*END DENSE ACTIVE*/
.data-grid.select .data-grid-select {
display: block;
margin-top: .5rem;
margin-right: .5rem;
}
.data-grid .data-grid-select,
.data-grid .data-grid-collapse-cta{
display: none;
}
.data-grid label {
font-size: $font-size-sm;
color: $text-core-lc;
}
/*COLLAPSE*/
.data-grid.collapse .data-grid-collapse-cta {
display: block;
margin-top: .5rem;
margin-left: .5rem;
cursor: pointer;
}
.collapsed{
max-height: 0;
overflow: hidden;
padding-top: 0!important;
padding-bottom: 0!important;
border-top: 0px solid transparent;
//opacity: 0;
transition: all 0.5s ease-out;
}
.collapsed.show {
max-height: 25vh;
overflow: auto;
padding-top: initial;
padding-bottom: initial;
border-top: 1px solid var(--bg-core-primary-light);
//opacity: 1;
transition: all 0.5s ease-in;
}
.data-grid .data-grid-collapse-cta i {
transform: rotate(0);
transition: all 0.5s ease-in;
}
.data-grid.show .data-grid-collapse-cta i {
transform: rotate(180deg);
}
/*form-wizard*/
.form-wizard {
background-color: var(--element-bg-core);
border: 1px solid var(--border-core);
border-radius: 0.6rem;
padding: 1rem 1.25rem;
position: relative;
}
.form-outer {
display: flex;
flex-wrap: nowrap;
overflow: hidden;
}
.form-step {
width: 100%;
flex: 1 0 auto;
transition: all 0.5s ease-out;
transform: translateX(0);
}
.form-step.next{
transform: translateX(-100%);
}
.form-step.previous{
transform: translateX(100%);
}
.dots {
--width: .75rem;
--height: var(--width);
position: absolute;
bottom: calc(var(--width) / -2);
left: 50%;
transform: translateX(-50%);
.dot {
width: var(--width);
height: var(--height);
border-radius: 50%;
background-color: var(--bg-core-primary-lighten);
display: inline-block;
outline: 1px solid #fff;
transition: all 300ms ease-in-out;
}
.dot.active,
.dot.next {
background-color: var(--bg-core-primary);
outline: 2px solid var(--bg-core-primary);
outline-offset: 2px;
}
.dot.completed {
background-color: $success;
}
}
/*end form-wizard*/

@ -3,4 +3,7 @@
@forward "sidebar";
@forward "radio";
@forward "inputs";
@forward "checkbox";
@forward "checkbox";
@forward "toggle";
@forward "chip";
@forward "modal";

@ -10,7 +10,7 @@ $width: 100%;
form{
display: block;
margin-top: 1rem;
//padding-top: 1rem;
}
.form-group{
@ -36,12 +36,13 @@ form{
width: 100%;
border: none;
border-radius: 0;
border-bottom: 1px solid $border-core;
border-bottom: 1px solid var(--border-core);
&:focus {
outline: none;
}
&:focus ~ label.label-animated,
&:valid ~ label.label-animated {
&:valid ~ label.label-animated,
&:disabled ~ label.label-animated {
top: -12px;
font-size: $font-size-sm;
color: var(--bg-core-primary-darken);
@ -204,31 +205,71 @@ select {
height: auto;
max-height: max-content;
opacity: 1;
transition: all $trans-time ease-in-out;
pointer-events: initial;
}
&:focus-within ~ .backdrop {
opacity: 1;
display: block;
}
}
.drop-down{
position: absolute;
opacity: 0;
display: flex;
flex-direction: column;
top: 46px;
left: 0;
min-width: 100%;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 80%;
width: 80%;
height: 0;
max-height: 0;
//padding: $base-padding;
display: flex;
flex-direction: column;
opacity: 0;
pointer-events: none;
z-index: 1500;
list-style: none;
background-color: #fff;
box-shadow: $base-box-shadow;
border-radius: $border-radius-lg;
transition: all $trans-time ease-in-out;
}
.backdrop{
display: none;
opacity: 0;
z-index: 1499;
position: fixed;
top: 0;
right:0;
bottom:0;
left:0;
overflow: hidden;
background-color: rgba(0,0,0,.5);
transition: all $trans-time ease-in-out;
}
@include breakpoint(sm) {
.drop-down{
position: absolute;
top: 46px;
left: 0;
opacity: 0;
min-width: 100%;
width: 100%;
transform: none;
border-radius: 0 0 $border-radius-lg $border-radius-lg;
}
}
.dropdown-header {
display: block;
padding: 0.5rem 1.5rem;
@ -253,12 +294,12 @@ select {
.dropdown-item:focus, .dropdown-item:hover {
color: var(--text-core-hc);
text-decoration: none;
background-color: var(--bg-core-primary-lighten);
background-color: var(--bg-core-primary-light);
}
.dropdown-divider {
height: 0;
margin: 0.5rem 0;
overflow: hidden;
border-top: 1px solid $border-core;
border-top: 1px solid var(--bg-core-primary-light);
}

@ -0,0 +1,229 @@
@use "../util" as *;
@use "../globals" as *;
// stylelint-disable function-disallowed-list
// .modal-open - body class for killing the scroll
.scroll-disabled{
overflow: hidden;
padding-right: 0;
}
// .modal - container to scroll within
// .modal-dialog - positioning shell for the actual modal
// .modal-content - actual modal w/ bg and corners and stuff
// Container that the modal scrolls within
.modal {
--modal-zindex: #{$modal-zindex};
--modal-width: 60vw;
--modal-height: 50vh;
--modal-padding: 1.5rem;
--modal-margin: 1.5rem;
--modal-color: var(--text-core);
--modal-bg: var(--bg-core-primary-light);
--modal-border-color: var(--bg-core-primary-light);
--modal-border-width: 1px;
--modal-border-radius: 1rem;
--modal-box-shadow: 0 0.5rem 1rem rgba(var(--bg-core-primary), 0.15);
--modal-header-padding-x: 1.5rem; //close button
--modal-header-padding-y: 1.5rem; //close button
--modal-header-bg: var(--modal-bg);
--modal-header-padding: 1.5rem;
--modal-header-border-color: var(--bg-core-primary-light);
--modal-header-border-width: 1px;
--modal-footer-bg: var(--modal-bg);
--modal-footer-padding: 1.5rem;
--modal-footer-border-color: var(--bg-core-primary-light);
--modal-footer-border-width: 1px;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0,0,0,.65);
z-index: var(--modal-zindex);
display: none;
opacity: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
transition: opacity .15s linear;
// Prevent Chrome on Windows from adding a focus outline. For details, see
// https://github.com/twbs/bootstrap/pull/10951.
outline: 0;
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
// See also https://github.com/twbs/bootstrap/issues/17695
}
// When fading in the modal, animate it to slide down
.modal.fade {
opacity: 1;
transition: opacity .15s linear;
}
.modal.show{
display: block;
}
// When trying to close, animate focus to scale
.modal.modal-static {
transform:scale(1.1);
}
// Shell div to position the modal with bottom padding
.modal-dialog {
position: relative;
width: auto;
margin: var(--modal-margin);
// allow clicks to pass through for custom click handling to close modal
pointer-events: none;
}
.modal-dialog-scrollable {
height: calc(100% - var(--modal-margin) * 2);
.modal-content {
max-height: 100%;
overflow: hidden;
}
.modal-body {
overflow-y: auto;
}
}
.modal-dialog-centered {
display: flex;
align-items: center;
min-height: calc(100% - var(--modal-margin) * 2);
}
// Actual modal
.modal-content {
position: relative;
display: flex;
flex-direction: column;
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
// counteract the pointer-events: none; in the .modal-dialog
color: var(--modal-color);
pointer-events: auto;
background-color: var(--modal-bg);
background-clip: padding-box;
border: var(--modal-border-width) solid var(--modal-border-color);
border-radius: var(--modal-border-radius);
box-shadow: var(--modal-box-shadow);
// Remove focus outline from opened modal
outline: 0;
}
// Modal header
// Top section of the modal w/ title and dismiss
.modal-header {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
padding: var(--modal-header-padding);
background-color: var(--modal-header-bg);
border-bottom: var(--modal-header-border-width) solid var(--modal-header-border-color);
.btn-close {
padding: calc(var(--modal-header-padding-y) * .5) calc(var(--modal-header-padding-x) * .5);
margin: calc(-.5 * var(--modal-header-padding-y)) calc(-.5 * var(--modal-header-padding-x)) calc(-.5 * var(--modal-header-padding-y)) auto;
}
}
// Title text within header
.modal-title {
margin-bottom: 0;
}
// Modal body
// Where all modal content resides (sibling of .modal-header and .modal-footer)
.modal-body {
position: relative;
// Enable `flex-grow: 1` so that the body take up as much space as possible
// when there should be a fixed height on `.modal-dialog`.
flex: 1 1 auto;
padding: var(--modal-padding);
}
// Footer (for actions)
.modal-footer {
display: flex;
flex-shrink: 0;
flex-wrap: wrap;
align-items: center; // vertically center
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
padding: var(--modal-footer-padding);
background-color: var(--modal-footer-bg);
border-radius: var(--modal-border-radius);
gap: .5rem;
}
// Automatically set modal's width for larger viewports
.modal-dialog {
max-width: 100%;
max-height: 100%;
padding: 0;
@include breakpoint(sm) {
max-width: 75vw;
margin-right: auto;
margin-left: auto;
padding: var(--modal-padding);
}
@include breakpoint(lg) {
max-width: var(--modal-width);
margin-right: auto;
margin-left: auto;
}
}
// // Modal background
// .modal-backdrop {
// opacity: 0;
// display: none;
// position: fixed;
// top: 0;
// left: 0;
// z-index: #{$backdrop-zindex};
// width: 100vw;
// height: 100vh;
// background-color: #000;
// transition: opacity .15s linear;
// }
// .modal-backdrop.fade {
// opacity: .5;
// transition: opacity .15s linear;
// }
// .modal-backdrop.show {
// display: block;
// }

@ -1,86 +1,276 @@
$color1: #f4f4f4;
$color2: var(--bg-core-primary-darken);
$radio-primary-color: var(--bg-core-primary-darken);
$radio-secondary-color: #f4f4f4;
$radio-disabled-color: darken($radio-secondary-color, 25%);
.radio-group {
margin: 0.5rem;
position:relative;
input[type="radio"] {
position: absolute;
opacity: 0;
+ .radio-label {
&:before {
content: "";
background: $color1;
border-radius: 100%;
border: 2px solid $color2;
display: inline-block;
width: 1.25em;
height: 1.25em;
position: relative;
top: .15rem;
margin-right: .75em;
vertical-align: top;
cursor: pointer;
text-align: center;
z-index: 1;
transition: all 250ms ease;
}
}
&:checked {
+ .radio-label {
&:before {
background-color: $color2;
box-shadow: inset 0 0 0 2px $color1;
}
}
}
&:focus {
+ .radio-label {
&:before {
outline: none;
border-color: $color2;
}
}
}
&:disabled {
+ .radio-label {
color: darken($color1, 25%);
pointer-events: none;
&:before {
box-shadow: inset 0 0 0 2px $color1;
border-color: darken($color1, 25%);
background: darken($color1, 25%);
--width: 1.25rem;
--height: var(--width);
--inset: calc(var(--width) / 10);
margin: 0.75rem 0;
width: max-content;
cursor: pointer;
position:relative;
display: flex;
align-items: center;
gap:.75rem;
z-index: 1;
&.reversed{
justify-content: space-between;
width: 100%;
& :nth-child(1) {order:2;}
& :nth-child(2) {order:1;}
}
&.column{
flex-direction: column;
gap:.25rem;
width: 100%;
}
}
+ .radio-label {
&:empty {
&:before {
margin-right: 0;
input[type="radio"] {
position: absolute;
opacity: 0;
}
}
}
}
.radio-fill {
position: relative;
display: inline-block;
width: var(--width);
height: var(--height);
border-radius: 50%;
background: transperent;
border: 2px solid $radio-primary-color;
transition: background 0.2s ease-in-out;
}
.radio-input:checked ~ .radio-fill {
background: var(--bg-core-primary-darken);
box-shadow: inset 0 0 0 var(--inset) $radio-secondary-color;
}
.radio-input:focus-within ~ .radio-fill {
outline: 1px solid $radio-primary-color;
}
.radio-input:disabled ~ .radio-fill {
box-shadow: inset 0 0 0 var(--inset) $radio-secondary-color;
border-color: $radio-disabled-color;
background: $radio-disabled-color;
pointer-events: none;
}
.radio-label:after {
content: "";
display: inline-block;
width: 35px;
height: 35px;
border-radius: 50%;
background-color: transparent;
position: absolute;
left: -8px;
top: -6px;
scale: .75;
transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
}
.radio-input:disabled ~ .radio-label {
color: $radio-disabled-color;
}
.radio-fill::before {
content: "";
display: inline-block;
width: calc(var(--width) * 1.8);
height: calc(var(--width) * 1.8);
border-radius: 50%;
background-color: transparent;
position: absolute;
z-index: -1;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
scale: .75;
transform-origin: left top;
transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25), transform 0.2s;
}
.radio-fill:hover::before {
background: var(--bg-core-primary-lighten);
scale: 1;
opacity: .5;
}
// $checkbox-primary-color: var(--bg-core-primary-darken);
// $checkbox-secondary-color: #f4f4f4;
// $checkbox-disabled-color: darken($checkbox-secondary-color, 25%);
// .checkbox-group {
// margin: 0.5rem;
// position:relative;
// input[type="checkbox"] {
// position: absolute;
// opacity: 0;
// + .checkbox-label {
// &:before {
// content: "";
// background: transparent;
// border: 2px solid $checkbox-primary-color;
// display: inline-block;
// width: 1.25em;
// height: 1.25em;
// position: relative;
// top: .15rem;
// margin-right: .75em;
// vertical-align: top;
// cursor: pointer;
// text-align: center;
// z-index: 1;
// transition: all 250ms ease;
// }
// }
// &:checked {
// + .checkbox-label {
// &:before {
// background-color: $checkbox-primary-color;
// box-shadow: inset 0 0 0 2px $checkbox-secondary-color;
// }
// }
// }
// &:focus {
// + .checkbox-label {
// &:before {
// outline: 1px solid $checkbox-primary-color;
// border-color: $checkbox-primary-color;
// }
// }
// }
// &:disabled {
// + .checkbox-label {
// color: darken($checkbox-secondary-color, 25%);
// pointer-events: none;
// &:before {
// box-shadow: inset 0 0 0 2px $checkbox-secondary-color;
// border-color: $checkbox-disabled-color;
// background: $checkbox-disabled-color;
// }
// }
// }
// + .checkbox-label {
// &:empty {
// &:before {
// margin-right: 0;
// }
// }
// }
// }
// }
// .checkbox-label:after {
// content: "";
// display: inline-block;
// width: 35px;
// height: 35px;
// border-radius: 50%;
// background-color: transparent;
// position: absolute;
// left: -8px;
// top: -6px;
// scale: .75;
// transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
// }
// .checkbox-label:hover:after {
// background: var(--bg-core-primary-lighten);
// scale: 1;
// opacity: .5;
// }
// $radio-primary-color: var(--bg-core-primary-darken);
// $radio-secondary-color: #f4f4f4;
// $radio-disabled-color: darken($radio-secondary-color, 25%);
// .radio-group {
// margin: 0.5rem;
// position:relative;
// input[type="radio"] {
// position: absolute;
// opacity: 0;
// + .radio-label {
// &:before {
// content: "";
// background: transparent;
// border-radius: 50%;
// border: 2px solid $radio-primary-color;
// display: inline-block;
// width: 1.25em;
// height: 1.25em;
// position: relative;
// top: .15rem;
// margin-right: .75em;
// vertical-align: top;
// cursor: pointer;
// text-align: center;
// z-index: 1;
// transition: all 250ms ease;
// }
// }
// &:checked {
// + .radio-label {
// &:before {
// background-color: $radio-primary-color;
// box-shadow: inset 0 0 0 2px $radio-secondary-color;
// }
// }
// }
// &:focus {
// + .radio-label {
// &:before {
// outline: 1px solid $radio-primary-color;
// border-color: $radio-primary-color;
// }
// }
// }
// &:disabled {
// + .radio-label {
// color: darken($radio-secondary-color, 25%);
// pointer-events: none;
// &:before {
// box-shadow: inset 0 0 0 2px $radio-secondary-color;
// border-color: $radio-disabled-color;
// background: $radio-disabled-color;
// }
// }
// }
// + .radio-label {
// &:empty {
// &:before {
// margin-right: 0;
// }
// }
// }
// }
// }
// .radio-label:after {
// content: "";
// display: inline-block;
// width: 35px;
// height: 35px;
// border-radius: 50%;
// background-color: transparent;
// position: absolute;
// left: -8px;
// top: -6px;
// scale: .75;
// transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25);
// }
// .radio-label:hover:after {
// background: var(--bg-core-primary-lighten);
// scale: 1;
// opacity: .5;
// }
.radio-label:hover:after {
background: var(--bg-core-primary-lighten);
scale: 1;
opacity: .5;
}

@ -2,28 +2,35 @@
@use '../util/' as *;
$sidebar-width-open:300px;
$sidebar-width-closed:75px;
$sidebar-right-width-open:300px;
$sidebar-right-width-closed:0px;
.sidebar{
position: fixed;
padding-inline: 0.75rem;
height: 100vh;
width: 100%;
color: $text-core;
background: var(--bg-core-primary);
//margin: -2rem 2rem -2rem -2rem;
z-index: 100;
background: var(--bg-core-primary-light);
border-right: 1px solid rgba(0,0,0,.025);
border-radius: $border-radius-lg 0 0 $border-radius-lg;
z-index: $sidebar-zindex;
transition: all 0.3s ease;
@include breakpoint(sm) {
height: calc(100% + 4rem);
height: 100%;
position: relative;
width: 300px;
width: $sidebar-width-open;
}
}
.sidebar.close{
width: 0;
transform: translateX(-75px);
transform: translateX($sidebar-width-closed);
@include breakpoint(sm) {
width: 75px;
width: $sidebar-width-closed;
transform: translateX(0);
}
}
@ -31,59 +38,80 @@
.main {
width: calc(100% - 1.5rem) ;
margin-inline:auto;
transition: all 0.3s ease;
@include breakpoint (sm) {
width: calc(100% - 300px);;
width: calc(100% - $sidebar-width-open - $sidebar-right-width-open);
padding: 2rem;
}
}
.main.close{
@include breakpoint (sm) {
width: calc(100% - 75px);
width: calc(100% - $sidebar-width-closed - $sidebar-width-open);
}
}
.main.close-r{
@include breakpoint (sm) {
width: calc(100% - $sidebar-right-width-open);
}
}
.main.close.close-r{
@include breakpoint (sm) {
width: calc(100% - $sidebar-right-width-closed - $sidebar-width-closed);
}
}
//profile
.sidebar .profile-details{
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
background-color: rgba(0,0,0,.6);
}
padding-top: 0;
//color: #fff;
//background-color: var(--bg-core-primary);
border-radius: $border-radius-lg 0 0 0;
transition: all 0.3s ease;
@include breakpoint (sm) {
padding-top: 12px;
}
}
.sidebar .profile-details .avatar{
min-width: calc(70px - 1rem);
min-width: 50px;
text-align: center;
margin: 1rem 0 1rem 1rem;
}
margin-right: 12px;
}
.sidebar .profile-details img{
height: 40px;
width: 40px;
height: 50px;
width: 50px;
object-fit: cover;
border-radius: 50%;
//transition: all 0.5s ease;
}
padding: 5px;
}
.sidebar .profile-details .name-job{
margin-right: auto;
.sidebar .profile-details .profile-description {
margin-right: auto;
}
.sidebar .profile-details .profile-name{
font-weight: 600;
white-space: nowrap;
color:var(--text-core-hc)
}
.sidebar .profile-details .profile-job{
font-size: 12px;
font-weight: 500;
color:var(--text-core-lc)
}
.sidebar .profile-details i{
height: 50px;
min-width: 70px;
min-width: 50px;
text-align: center;
line-height: 50px;
font-size: 20px;
@ -94,137 +122,359 @@
.sidebar.close .profile-details .profile-name,
.sidebar.close .profile-details .profile-job{
display: none;
}
}
//links
.sidebar .nav-links{
height: 100%;
/*NAV*/
.sidebar nav{
position: sticky;
top: 0;
}
.sidebar .navbar{
height: auto;
//padding: 12px;
padding: 0;
overflow: auto;
display: flex;
flex-direction: column;
}
.sidebar.close .nav-links{
.sidebar.close .navbar{
overflow: visible;
}
.sidebar .nav-links::-webkit-scrollbar{
.sidebar .navbar::-webkit-scrollbar{
display: none;
}
.sidebar .nav-links li{
.sidebar .navbar .navbar-header{
display: block;
padding: 0.5rem 0;
margin-bottom: 0;
font-size: $font-size-sm;
color: var(--text-core-lc);
white-space: nowrap;
}
.sidebar .navbar .navbar-item{
position: relative;
list-style: none;
transition: all 0.4s ease;
margin-bottom: 12px;
line-height: 50px;
border-radius: $border-radius-pill;
}
.sidebar .nav-links li:hover{
.sidebar .navbar .navbar-item:hover,
.sidebar .navbar .navbar-item:active,
.sidebar .navbar .navbar-item.active{
background: var(--bg-core-primary-lighten);
}
.sidebar .nav-links li .iocn-link{
display: flex;
align-items: center;
justify-content: space-between;
.sidebar.close .navbar .navbar-item{
border-radius: 50%;
}
.sidebar .navbar .navbar-item.fab{
background-color:#ffdad9;
border-radius: $border-radius-lg;
margin-bottom: 36px;
transition: all 0.3s ease;
}
.sidebar.close .nav-links li .iocn-link{
display: block
.sidebar .navbar .navbar-item.fab:hover,
.sidebar .navbar .navbar-item.fab:active,
.sidebar .navbar .navbar-item.fab.active{
background-color: #fbc5c3;
}
.sidebar .nav-links li i{
.sidebar .navbar .navbar-item i{
height: 50px;
min-width: 70px;
min-width: 50px;
text-align: center;
line-height: 50px;
color: var(--text-core-vc);
font-size: 20px;
color: var(--text-core-lc);
font-size: 22px;
cursor: pointer;
transition: all 0.3s ease;
}
.sidebar .nav-links li.showMenu i.arrow{
transform: rotate(-180deg);
}
.sidebar.close .nav-links i.arrow{
display: none;
}
.sidebar .nav-links li a{
.sidebar .navbar .navbar-item a{
display: flex;
align-items: center;
text-decoration: none;
}
.sidebar .nav-links li a .link_name{
.sidebar .navbar .navbar-item a .navbar-link{
flex: 1 0 auto;
font-weight: 400;
font-weight: $font-weight-400;
color: var(--text-core);
//transition: all 0.4s ease;
}
.sidebar.close .nav-links li a .link_name{
.sidebar .navbar .navbar-item a .navbar-link:first-child {
padding-left: 1.5rem;
}
.sidebar.close .navbar .navbar-item a .navbar-link{
opacity: 0;
pointer-events: none;
display: none;
}
.sidebar .navbar .navbar-item a .navbar-link.navbar-link-detail{
flex: 0 0 auto;
font-weight: $font-weight-600;
color: var(--text-core-hc);
text-align: right;
padding-right: 1rem;
}
.sidebar .nav-links li .sub-menu{
padding: 6px 6px 14px 70px;
margin-top: -10px;
background:var(--bg-core-primary-lighten);
display: none;
}
.sidebar .nav-links li.showMenu .sub-menu{
display: block;
}
.sidebar .nav-links li .sub-menu a{
color: var(--text-core);
font-size: 15px;
padding: 5px 0;
white-space: nowrap;
transition: all 0.3s ease;
}
.sidebar .nav-links li .sub-menu a:hover{
color: var(--bg-core-primary-darken);
}
.sidebar.close .nav-links li .sub-menu{
/*TOOLTIP*/
.sidebar.close .navbar .navbar-item .navbar-tooltip{
position: absolute;
left: 100%;
top: -10px;
background: #fff;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
width: max-content;
left: calc(100% + 12px);
top: 0;
margin-top: 0;
padding: 10px 20px;
border-radius: 0 6px 6px 0;
border-radius: $border-radius-lg;
opacity: 0;
display: block;
pointer-events: none;
transition: 0s;
}
.sidebar.close .nav-links li:hover .sub-menu{
top: 0;
.sidebar.close .navbar .navbar-item:hover .navbar-tooltip{
left: calc(100% + 24px);
opacity: 1;
pointer-events: auto;
transition: all 0.4s ease;
}
.sidebar .nav-links li .sub-menu .link_name{
display: none;
}
.sidebar.close .nav-links li .sub-menu .link_name{
font-weight: 600;
.sidebar.close .navbar .navbar-tooltip {
opacity: 1;
display: block;
}
.sidebar .nav-links li .sub-menu.blank{
opacity: 1;
pointer-events: auto;
padding: 3px 20px 6px 16px;
opacity: 0;
pointer-events: none;
.sidebar .navbar .navbar-tooltip {
display: none;
}
.sidebar .nav-links li:hover .sub-menu.blank{
top: 50%;
transform: translateY(-50%);
.sidebar-divider {
height: 0;
margin: 0.5rem 0;
overflow: hidden;
border-top: 1px solid var(--border-core-lighten);
}
.badge-label {
border-radius: $base-border-radius;
font-size: $font-size-sm;
color: var(--text-core-hc);
display: flex;
justify-content: center;
align-items: center;
min-width: 20px;
height: 20px;
letter-spacing: 0;
padding: 12px;
pointer-events: auto;
text-indent: 0;
white-space: nowrap;
background-color: transparent;
transition: .3s cubic-bezier(.25,.8,.5,1);
}
/*BOOTOMBAR*/
@include breakpoint-down(sm) {
.main {
margin-bottom: rem(75);
}
.sidebar.bottombar,
.sidebar.close.bottombar {
position: fixed;
bottom: 0;
transform: translateX(0);
display: flex;
align-items: center;
height: rem(75);
width: 100%;
color: $text-core;
background: var(--bg-core-primary-light);
border-radius: 0;
z-index: $sidebar-zindex;
}
.sidebar.bottombar .profile-description,
.sidebar.close.bottombar .profile-description,
.sidebar.bottombar .profile-details i,
.sidebar.close.bottombar .profile-deteils i {
display: none;
}
.sidebar.bottombar nav,
.sidebar.close.bottombar nav {
position: relative;
width: 100%;
}
.sidebar.bottombar .navbar,
.sidebar.close.bottombar .navbar {
padding: 0;
flex-direction: row;
justify-content: space-around;
}
.sidebar.bottombar .navbar .navbar-item,
.sidebar.close.bottombar .navbar .navbar-item {
margin-bottom: 0;
}
.sidebar.bottombar .navbar-link,
.sidebar.close.bottombar .navbar-link {
display: none;
}
.sidebar.bottombar .navbar .navbar-item .navbar-tooltip,
.sidebar.close.bottombar .navbar .navbar-item .navbar-tooltip {
display: none;
}
.sidebar.bottombar .navbar-item.fab,
.sidebar.close.bottombar .navbar-item.fab {
position: absolute;
right: 1rem;
top: -4rem;
}
.sidebar.bottombar .sidebar-divider,
.sidebar.close.bottombar .sidebar-divider {
display: none;
}
}
/*RIGHT SIDEBAR*/
.sidebar.right{
position: fixed;
top: 0;
right: 0;
height: 100vh;
width: 100%;
color: $text-core;
background: var(--bg-core-primary-lighten);
border-right: 1px solid rgba(0,0,0,.025);
border-radius: 0 $border-radius-lg $border-radius-lg 0;
z-index: $sidebar-right-zindex;
transition: all 0.3s ease;
@include breakpoint(sm) {
height: 100%;
position: relative;
width: $sidebar-right-width-open;
}
}
.sidebar.right.close-r{
width: 0;
transform: translateX($sidebar-right-width-closed);
padding: 0;
@include breakpoint(sm) {
width: $sidebar-right-width-closed;
transform: translateX(0);
z-index: -1;
opacity: 0;
pointer-events: none;
}
}
.sidebar.right .navbar .navbar-item:hover,
.sidebar.right .navbar .navbar-item:active,
.sidebar.right .navbar .navbar-item.active{
background: var(--bg-core-primary);
}
// .scrollable {
// display: block;
// height: 100%;
// max-height: 100vh;
// min-height: 0;
// }
// .scroll-wrapper {
// position: relative;
// width: 100%;
// height: 100%;
// background-color: turquoise;
// }
// .scroll-container {
// position: relative;
// width: 100%;
// height: 100%;
// touch-action: pan-y;
// overflow-x: hidden;
// overflow-y: auto;
// }
// .scroll-content {
// height: auto;
// transform: none;
// position: relative;
// min-height: 100%;
// }
.scrollable {
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
max-height: calc(100vh - 50px);
}
.scroll-wrapper {
position: relative;
width: auto;
height: 100%;
pointer-events: none;
}
.scroll-container {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
max-height: 100%;
overflow: hidden;
pointer-events: auto;
background-clip: padding-box;
}
.scroll-content {
position: relative;
overflow-y: auto;
flex: 1 1 auto;
}

@ -0,0 +1,107 @@
@use "../globals/" as *;
@use "../util/" as *;
$toggle-primary-color: var(--bg-core-primary-darken);
$toggle-secondary-color: #ddd;
$toggle-disabled-color: darken($toggle-secondary-color, 25%);
.toggle-group {
--width: 42px;
--height: calc(var(--width) / 2);
--border-radius: calc(var(--height) / 2);
margin: 0.75rem 0;
width: max-content;
cursor: pointer;
position:relative;
display: flex;
align-items: center;
gap:.75rem;
&.reversed{
justify-content: space-between;
width: 100%;
& :nth-child(1) {order:2;}
& :nth-child(2) {order:1;}
}
&.column{
flex-direction: column;
gap:.25rem;
width: 100%;
}
input[type="checkbox"] {
position: absolute;
opacity: 0;
}
}
.toggle-fill {
position: relative;
width: var(--width);
height: var(--height);
border-radius: var(--border-radius);
background: $toggle-secondary-color;
//margin-right: .75rem;
transition: background 0.2s ease-in-out;
}
.toggle-input:checked ~ .toggle-fill {
background: var(--bg-core-primary-darken);
}
.toggle-input:focus-within ~ .toggle-fill {
outline: 1px solid $toggle-primary-color;
}
.toggle-input:disabled ~ .toggle-fill {
background: $toggle-disabled-color;
pointer-events: none;
}
.toggle-input:disabled ~ .toggle-label {
color: $toggle-disabled-color;
}
.toggle-fill::after {
content: "";
position: absolute;
top: 2px;
left: 2px;
height: calc(var(--height) - 4px);
width: calc(var(--height) - 4px);
background: #ffffff;
border-radius: var(--border-radius);
transition: transform 0.2s;
}
.toggle-input:checked ~ .toggle-fill::after {
transform: translateX(var(--height));
}
.toggle-fill::before {
content: "";
display: inline-block;
width: calc(var(--height) * 1.8);
height: calc(var(--height) * 1.8);
border-radius: 50%;
background-color: transparent;
position: absolute;
left: calc(30% - var(--height));
top: calc(60% - var(--height));
scale: .75;
transform-origin: center;
transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25), transform 0.2s;
}
.toggle-fill:hover::before {
background: var(--bg-core-primary-lighten);
scale: 1;
opacity: .5;
}
.toggle-input:checked ~ .toggle-fill::before {
transform: translateX(var(--height));
}

@ -8,15 +8,15 @@ body{
background: linear-gradient(to left, var(--bg-core-primary-lighten), var(--bg-core-end));
font-family: 'Open Sans', sans-serif;
font-size: $base-font-size;
//font-size: clamp($base-font-size * 0.9, $base-font-size * 0.9+0.1vw, $base-font-size);
//font-size: clamp($base-font-size * 0.9, $base-font-size * 0.9 + 0.1vw, $base-font-size);
color: var(--text-core);
@include breakpoint(sm){
background: linear-gradient(to left, var(--bg-core-primary), var(--bg-core-end));
}
}
//hr classes
hr {
border-top: 1px solid var(--border-core);
@ -25,11 +25,11 @@ hr {
background-color:var(--border-core);
color: var(--border-core);
}
//section
section,
.section{
margin-bottom: 1rem;
.section{
margin-bottom: 1rem;
}
@ -39,4 +39,43 @@ margin-bottom: 1rem;
}
/*ICONS*/
.icon-default {
color: var(--bg-core-primary);
}
.disabled {
.icon-root, .svg-icon, .icon-default {
color: var(--bg-core-primary-light);
}
}
.icon-root {
width: 1em;
height: 1em;
display: inline-block;
transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
flex-shrink: 0;
user-select: none;
&:focus {
outline: none;
}
&.svg-icon {
fill: currentColor;
}
}
.icon-size-small {
font-size: 1.25rem;
}
.icon-size-medium {
font-size: 1.5rem;
}
.icon-size-large {
font-size: 2.25rem;
}

@ -1,7 +1,5 @@
/* Box sizing rules */
*,
*::before,
*::after {
*, *::before, *::after {
box-sizing: border-box;
}
@ -48,7 +46,13 @@ a:not([class]) {
text-decoration-skip-ink: auto;
}
a, a:hover {
a {
text-decoration: none;
transition: color 0.15s ease-in-out;
}
a:hover {
text-decoration: none;
transition: color 0.15s ease-in-out;
}

@ -113,7 +113,7 @@ p,
@each $property, $map in $fw {
$prefix: map-get($map, "prefix");
$values: map-get($map, "values");
$values: map-get($map, "values");
@each $k, $v in $values {
@if $breakName != "" {
@ -157,6 +157,27 @@ p,
text-overflow: ellipsis;
}
.text-hc {
color: var(--text-core-hc);
}
.text-lc {
color: var(--text-core-lc);
}
a.text-link{
color: var(--bg-core-primary-dark);
border-bottom:1px dotted;
transition: all 0.15s ease-in-out;
}
a.text-link:hover{
color: var(--bg-core-primary-darken);
border-bottom:none;
}
// $fs: (
// "font-size": (
// "prefix": "fs",

@ -1,8 +1,8 @@
@use "sass:math";
//theme colors
$bg-core-primary: hsl(225, 68%, 85%); // body-background;
$bg-core-start: $bg-core-primary;
//$bg-core-primary: hsl(225, 68%, 85%); // body-background;
//$bg-core-start: $bg-core-primary;
$bg-core-end: #fafafa;
$text-core-hc: #212121; //title
@ -10,67 +10,112 @@ $text-core: #525252; //text
$text-core-lc: #7d7d80; //metadata
$text-core-vc: #969699; //ikona-text
$border-core: lighten($bg-core-primary, 7%); //border
//$border-core: lighten($bg-core-primary, 7%); //border
$element-bg-core: rgba(255, 255, 255, 0.5); // elements-background
$element-fg-core: rgba(255, 255, 255, 0.25); // elements-foreground
:root {
--bg-core-primary: #{$bg-core-primary}; // body-background;
--bg-core-primary-lighten: hsl(225, 68%, 90%); // lighten
--bg-core-primary-darken: hsl(225, 68%, 75%); // darken;
--bg-core-start: #{$bg-core-primary};
--bg-core-end: #{$bg-core-end};
$blue-50: #f2f4fd;
$blue-100: #e5eafa;
$blue-200: #cbd6f5;
$blue-300: #b2c1f1;
$blue-400: #98adec;
$blue-500: #7e98e7;
$blue-600: #657ab9;
$blue-700: #4c5b8b;
$blue-800: #323d5c;
$blue-900: #191e2e;
--bg-core-primary: #{$blue-300}; // body-background;
--bg-core-primary-light: #{$blue-50}; // light
--bg-core-primary-lighten: #{$blue-200}; // lighten
--bg-core-primary-darken: #{$blue-400}; // darken;
--bg-core-primary-dark: #{$blue-600}; // dark;
--bg-core-start: #{$blue-300}; // body-background
--bg-core-end: #{$bg-core-end}; // body-background
--text-core-hc: #{$text-core-hc}; //title
--text-core: #{$text-core}; //text
--text-core: #{$blue-800}; //text
--text-core-lc: #{$text-core-lc}; //metadata
--text-core-vc: #{$text-core-vc}; //ikona-text
--bg-core-primary-lighter: hsl(225, 68%, 90%); //hover
--border-core: #{$border-core}; //border
--border-core-lighten:#{$blue-100}; //border-lighten
--border-core: #{$blue-200}; //border
--element-bg-core: #{$element-bg-core}; // elements-background
--element-fg-core: #{$element-fg-core}; // elements-foreground
}
.pink,
:root:has(pink:checked) {
--bg-core-primary: hsl(310 50% 90%);
--bg-core-primary-lighten: hsl(310 50% 95%); //lighten
--bg-core-primary-darken: hsl(310 50% 85%); //darken
--element-bg-core: hsl(310 50% 100% / 50%);
--text-core: hsl(310 50% 15%);
--border-core: hsl(310 50% 90%);
.pink {
$violet-50: #f0eef6;
$violet-100: #e1dced;
$violet-200: #c2b9db;
$violet-300: #a496c8;
$violet-400: #8573b6;
$violet-500: #6750a4;
$violet-600: #524083;
$violet-700: #3e3062;
$violet-800: #151021;
$violet-900: #212529;
--bg-core-primary: #{$violet-300}; // body-background;
--bg-core-primary-light: #{$violet-50}; // light
--bg-core-primary-lighten: #{$violet-200}; // lighten
--bg-core-primary-darken: #{$violet-400}; // darken;
--bg-core-primary-dark: #{$violet-600}; // dark;
--bg-core-start: #{$violet-200}; // body-background
--bg-core-end: #{$bg-core-end}; // body-background
--text-core-hc: #{$text-core-hc}; //title
--text-core: #{$violet-800}; //text
--text-core-lc: #{$text-core-lc}; //metadata
--text-core-vc: #{$text-core-vc}; //ikona-text
--border-core-lighten:#{$violet-100}; //border-lighten
--border-core: #{$violet-200}; //border
--element-bg-core: #{$element-bg-core}; // elements-background
--element-fg-core: #{$element-fg-core}; // elements-foreground
}
.dark,
:root:has(dark:checked) {
.dark {
--bg-core-primary: hsl(0 0% 10% / 100%); // body-background;
--bg-core-primary-lighten: hsl(0 0% 15% / 100%); // lighten
--bg-core-primary-darken: hsl(0 0% 5% / 100%); // darken;
--bg-core-start: var(--bg-core-primary);
--bg-core-end: hsl(0 0% 15% / 100%);
--bg-core-primary-light: hsl(0 0% 15% / 100%); // light
--bg-core-primary-lighten: hsl(0 0% 17% / 100%); // lighten
--bg-core-primary-darken: hsl(0 0% 7% / 100%); // darken;
--bg-core-primary-dark: hsl(0 0% 5% / 100%); // dark;
--bg-core-start: hsl(0 0% 10% / 100%); // body-background
--bg-core-end: hsl(0 0% 15% / 100%); // body-background
--text-core-hc: hsl(0 0% 97% / 100%); //title
--text-core: hsl(0 0% 73% / 100%); //text
--text-core-lc: hsl(0 0% 53% / 100%); //metadata
--text-core-vc: hsl(0 0% 60% / 100%); //ikona-text
--border-core: hsl(0 0% 27% / 100%); //border
--border-core-lighten:hsl(0 0% 17% / 100%); //border-lighten
--border-core: hsl(0 0% 20% / 100%); //border
--element-bg-core: hsl(0 0% 13% / 100%); // elements-background
--element-fg-core: hsl(0 0% 20% / 100%); // elements-foreground
--element-fg-core: hsl(0 0% 15% / 100%); // elements-foreground
}
// colors
$blue: #0d6efd;
$indigo: #6610f2;
$purple: #6f42c1;
$pink: #d63384;
$red: #dc3545;
$orange: #fd7e14;
$yellow: #ffc107;
$green: #00c851; //#198754;
$teal: #20c997;
$cyan: #0dcaf0;
// $blue: #0d6efd;
// $indigo: #6610f2;
// $purple: #6f42c1;
// $pink: #d63384;
// $red: #dc3545;
// $orange: #fd7e14;
// $yellow: #ffc107;
// $green: #00c851; //#198754;
// $teal: #20c997;
// $cyan: #0dcaf0;
$white: #fff;
$gray-100: #f8f9fa;
@ -84,13 +129,20 @@ $gray-800: #343a40;
$gray-900: #212529;
$black: #000;
$blue: #0dbcf0c4;
$red: #ff7f78;
$yellow: #ffb480;
$green: #8fcaa3;
$cyan: #729fe9;
$primary: $blue;
$secondary: $gray-600;
$secondary: $gray-400;
$success: $green;
$info: $cyan;
$warning: $yellow;
$danger: $red;
$light: $gray-400;
$light: $gray-300;
$dark: $gray-900;
//color palette
@ -138,9 +190,16 @@ $font-weight-800: 800; // extra bold
$base-border-radius: 0.3rem;
$border-radius-sm: math.div($base-border-radius, 2);
$border-radius-lg: $base-border-radius * 2;
$border-radius-pill: 100vw;
// box-shadow
$base-box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 10%) !default;
// transition
$transition: all 0.15s ease-in-out;
//z-index
$sidebar-zindex: 899;
$sidebar-right-zindex: 898;
$backdrop-zindex: 999;
$modal-zindex: 10000;

@ -1,16 +1,21 @@
@use "../globals" as *;
.color-picker-container {
margin-bottom: 1rem;
}
.color-picker > fieldset {
border: 0;
display: flex;
flex-direction: column;
gap: 2rem;
//flex-direction: column;
gap: 1.5rem;
width: fit-content;
//background: #fff;
padding: 1rem 3rem;
position: fixed;
top: 1rem;
right: 0;
padding: 0.5rem 1.5rem;
//position: fixed;
//top: 1rem;
//right: 0;
border-radius: 0 0 1rem 1rem;
}
@ -31,14 +36,15 @@
background-color: var(--radio-color);
}
.color-picker input[type="radio"]light {
.color-picker input[type="radio"] {
--radio-color: hsl(225, 68%, 85%);
}
.color-picker input[type="radio"]pink {
--radio-color: pink;
.color-picker input[type="radio"] {
--radio-color: rebeccapurple;
}
.color-picker input[type="radio"]dark {
.color-picker input[type="radio"] {
--radio-color: #232323;
}
@ -58,8 +64,9 @@
background: var(--element-fg-core);
border: 1px solid;
border-color: var(--border-core);
color: inherit;
//color: inherit;
border-radius: $border-radius-lg;
}

@ -20,6 +20,10 @@
//test-sidebar
.document-container {
background-color: var(--element-bg-core);
}
.document-container {
//padding: rem(32);
@ -33,7 +37,8 @@
border-radius: $border-radius-lg;
// padding: rem(32);
box-shadow: $base-box-shadow;
overflow: hidden;
//overflow: hidden;
overflow-x: clip;
}
}

@ -2,7 +2,8 @@
$breakpoints-up: (
"": "",
"": "1",
//'xs': rem(1),
'sm': rem(576),
'md': rem(768),
'lg': rem(992),
@ -11,7 +12,8 @@ $breakpoints-up: (
// 639px, 1149px, 1399px
$breakpoints-down: (
"": "",
"": "0",
//'xs': rem(0),
'sm': rem(575),
'md': rem(767),
'lg': rem(991),

@ -58,8 +58,7 @@
}
//change --bg-core-primary for dark
.dark,
:root:has(dark:checked) {
.dark {
$val: hsl(216 100% 70%);
.text-core {

@ -8,10 +8,10 @@
// change text color based on background color - set-color(color-value)
@function set-color($color) {
@if (lightness($color) > 55) {
@if (lightness($color) > 74) {
@return #333;
}
@else {
@return #FFF;
@return #FFF;
}
}
}

@ -160,6 +160,35 @@
}
}
//Position classes
$positionProps: (
"absolute",
"fixed",
"inherit",
"initial",
"relative",
"revert",
"static",
"sticky",
"unset",
);
@each $pos in $positionProps {
@if $breakName != "" {
@media (min-width: $breakValue) {
.position-#{$breakName}-#{$pos} {
position: #{$pos} !important;
}
}
} @else {
.position-#{$pos} {
position: #{$pos} !important;
}
}
}
//Display classes
$displayProps: (
"block",

@ -135,12 +135,24 @@ public static class Helper
}
}
public static T? ConvertToType<T>(object variable)
{
try
{
if (variable is not null)
return (T)Convert.ChangeType(variable, typeof(T));
return default;
} catch
{
return default;
}
}
public static bool IsNumeric(string input)
{
try
{
var number = Double.Parse(input);
return true;
return Double.TryParse(input, out var number);
}
catch
{

Loading…
Cancel
Save