Merge pull request 'features/rewrite/modal1' (#10) from features/rewrite/modal1 into features/rewrite/main

Reviewed-on: #10
Reviewed-by: Matija Koželj <matija.kozelj@tompit.com>
pull/11/head
Matija Koželj 2 years ago
commit 10a7454c48

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

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

File diff suppressed because it is too large Load Diff

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

@ -3,6 +3,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connected.Components.Showcase.Runner</title> <title>Connected.Components.Showcase.Runner</title>
<base href="/" /> <base href="/" />
<link href="css/app.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" />

@ -1,44 +1,9 @@
<button <button type="button"
type="button" @onclick="@OnClick"
href="#" disabled=@Disabled
@onclick="@OnClick" style="@StyleList"
disabled=@Disabled class="@ClassList">
style="@Style"
class="@ClassList">
<div class="@ContentClassList"> <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 @ChildContent
}
</div> </div>
</button> </button>

@ -1,8 +1,6 @@
using Connected.Utilities; using Connected.Utilities;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using static Connected.Colors;
using System;
namespace Connected.Components; namespace Connected.Components;
public partial class Button public partial class Button
@ -28,10 +26,10 @@ public partial class Button
/// <summary> /// <summary>
/// Size of the button. /// Size of the button.
/// Options: Size.[Small,Medium,Large,FullWidth] /// Options: Size.[Small,Medium,Large,FullWidth]
/// Default: Size.Large /// Default: Size.Medium
/// </summary> /// </summary>
[Parameter] [Parameter]
public Size Size { get; set; } = Size.Large; public Size Size { get; set; } = Size.Medium;
/// <summary> /// <summary>
/// Text shown inside the button /// Text shown inside the button
@ -48,29 +46,6 @@ public partial class Button
[Parameter] [Parameter]
public bool Disabled { get; set; } = false; 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> /// <summary>
/// User defined custom class added on top of default generated classes /// User defined custom class added on top of default generated classes
@ -90,7 +65,7 @@ public partial class Button
/// <summary> /// <summary>
/// User defined custom style /// User defined custom style
/// Options: any user defined string with valid CSS style /// Options: any valid CSS style
/// Default: string.Empty /// Default: string.Empty
/// </summary> /// </summary>
[Parameter] [Parameter]
@ -115,10 +90,21 @@ public partial class Button
#region Styling #region Styling
public string StyleList
{
get
{
return new StyleBuilder()
.AddStyle(Style)
.Build();
}
}
/// <summary> /// <summary>
/// Generated class list for button based on user parameters /// Generated class list for button based on user parameters
/// </summary> /// </summary>
private string ClassList public string ClassList
{ {
get get
{ {
@ -134,7 +120,7 @@ public partial class Button
/// <summary> /// <summary>
/// Generated class list for button based on user parameters /// Generated class list for button based on user parameters
/// </summary> /// </summary>
private string ContentClassList public string ContentClassList
{ {
get get
{ {

@ -2,12 +2,22 @@
@inherits InputBase; @inherits InputBase;
<div class="checkbox-group"> <label class="checkbox-group"
@if (Checked) for="@Id">
{ <input class="@ClassList"
<input id="@Id" name="checkbox" type="checkbox" @onchange="OnChange" @attributes=@InputAttributes checked> style="@StyleList"
} else { id="@Id"
<input id="@Id" name="checkbox" type="checkbox" @onchange="OnChange" @attributes=@InputAttributes> type="checkbox"
} @attributes=@InputAttributes
<label for="@Id" class="checkbox-label">@Label</label> checked="@Checked"
</div> 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.Models;
using Connected.Utilities;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace Connected.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] [Parameter]
public bool Checked { get; set; } = false; public bool Checked { get; set; } = false;
/// <summary>
/// ID for the CheckBox
/// </summary>
[Parameter, EditorRequired] [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] [Parameter]
public EventCallback<bool> CheckedChange { get; set; } public string LabelClass { get; set; } = string.Empty;
public async Task OnChange(ChangeEventArgs args)
/// <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; Checked = !Checked;
CheckedChange.InvokeAsync(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"> <div style="@GlyphStyleList">
<svg viewBox="0 0 24 24" style="fill:@Color;" class="@GlyphClassList" @onclick="@OnClick"> <svg viewBox="0 0 24 24" class="@GlyphClassList" @onclick="@OnClick">
@((MarkupString)SVG) @((MarkupString)SVG)
</svg> </svg>
</div> </div>

@ -2,59 +2,87 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
namespace Connected.Components namespace Connected.Components;
public partial class Glyph
{ {
public partial class Glyph /// <summary>
{ /// SVG markup string for glyph
[Parameter] /// Options: any valid SVG markup string
public string SVG { get; set; } = string.Empty; /// Default: string.Empty
/// </summary>
[Parameter] [Parameter]
public string Color { get; set; } = "#000000"; public string SVG { get; set; } = string.Empty;
[Parameter] /// <summary>
public int Width { get; set; } = 24; /// Color of the glyph
[Parameter] /// Options: Color.[Core,Primary,Secondary,Success,Info,Warning,Danger,White,Light,Dark]
public int Height { get; set; } = 24; /// Default: Color.Dark
/// </summary>
private string WidthString [Parameter]
{ public Color Color { get; set; } = Color.Dark;
get
{ /// <summary>
return Width.ToString() + "px"; /// Width of the glyph in px
} /// Options: Any positive integer number
} /// Default: 24
/// </summary>
private string HeightString [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 new CssBuilder()
{ .AddClass("color-"+Helper.GetEnumDescription<Color>(Color))
return Width.ToString() + "px"; .AddClass(Class)
} .Build();
} }
}
[Parameter] /// <summary>
public string Class { get; set; } = string.Empty; /// User defined style for the glyph
/// Options: Any valid CSS style
private string GlyphClassList /// Default: string.Empty
{ /// </summary>
get [Parameter]
{ public string Style { get; set; } = string.Empty;
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);
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 <a class="@LinkClassList"
class="@LinkClassList" style="@LinkStyleList"
style="@LinkStyleList" href="@Url"
href="@Url" target="@_target">
target="@Target" >
@Text @Text
</a> </a>

@ -1,49 +1,86 @@
using Connected.Utilities; using Connected.Enums;
using Connected.Utilities;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace Connected.Components; namespace Connected.Components;
public partial class Link public partial class Link
{ {
[Parameter, EditorRequired] /// <summary>
public string Url { get; set; } = string.Empty; /// URL of the link
/// Options: Any valid web page adress
[Parameter, EditorRequired] /// Default: string.Empty
public string Text { get; set; } = string.Empty; /// </summary>
[Parameter, EditorRequired]
[Parameter, EditorRequired] public string Url { get; set; } = string.Empty;
public string Target { get; set; } = "_self";
/// <summary>
[Parameter] /// Text shown for the link. If this is not provided URL is used
public string Class { get; set; } = string.Empty; /// Options: Any valid string
/// Default: string.Empty
private string LinkClassList /// </summary>
{ [Parameter]
get public string Text { get; set; } = string.Empty;
{
return new CssBuilder() /// <summary>
.AddClass(Class) /// Target where the link shall open
.Build(); /// Options: Target.[Self,Parent,Top,Blank]
} /// Default: Target.Self
} /// </summary>
[Parameter]
[Parameter] public Target Target { get; set; } = Target.Self;
public string Style { get; set; } = string.Empty;
private string _target
private string LinkStyleList {
{ get
get {
{ return Helper.GetEnumDescription<Target>(Target);
return new StyleBuilder() }
.AddStyle(Style) }
.Build();
} /// <summary>
} /// Class name or multiple classes separated by space
/// Options: Any valid class name or names separated by space
protected override async Task OnParametersSetAsync() /// Default: string.Empty
{ /// </summary>
if (string.IsNullOrEmpty(Text)) Text = Url; [Parameter]
await base.OnParametersSetAsync(); 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"> <div class="@InputFieldClassList">
<input type="text" <input type="text"
placeholder="@Placeholder" placeholder="@Placeholder"
step="@_step" step="@Step"
disabled="@Disabled" disabled="@Disabled"
readonly="@Readonly" readonly="@Readonly"
required="@Required"
value="@_value" value="@_value"
@onkeydown=@(args => ChangeValue(args)) @onkeydown=@(args => ChangeValue(args))
@onkeydown:preventDefault="@_preventDefaultAction" @onkeydown:preventDefault="@_preventDefaultAction"
@oninput=@GetValueAsync @oninput=@SetValueAsync
@onmousewheel=@OnMouseWheel @onmousewheel=@OnMouseWheel
@onchange="@Change" @onchange="@Change"
@onwheel="OnMouseWheel" @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.KeyboardArrowUp" Click="StepUp" />
<Glyph Width=16 Height=16 SVG="@Icons.Material.Outlined.KeyboardArrowDown" Click="StepDown"></Glyph> <Glyph Width=16 Height=16 SVG="@Icons.Material.Outlined.KeyboardArrowDown" Click="StepDown"></Glyph>
</span> </span>
@if (Clearable && Value.ToString().Length > 0) @if (Clearable && !string.IsNullOrEmpty(Value?.ToString()))
{ {
<span class="input-glyph button" @onclick="Clear"> <span class="input-glyph button" @onclick="Clear">
<Glyph SVG="@Icons.Material.Rounded.Dangerous" /> <Glyph SVG="@Icons.Material.Rounded.Dangerous" />
<!--<i class='bx bx-x-circle'></i>-->
</span> </span>
} }
@if (IsError) @if (IsError)
{ {
<span class="input-glyph error"> <span class="input-glyph error">
<Glyph SVG="@Icons.Material.Outlined.Error" Color="#D10000" /> <Glyph SVG="@Icons.Material.Outlined.Error" />
<!--<i class='bx bx-error-circle'></i>-->
</span> </span>
} }
</span> </span>

@ -5,142 +5,189 @@ using Microsoft.AspNetCore.Components.Web;
using System.Numerics; using System.Numerics;
namespace Connected.Components; namespace Connected.Components;
public partial class NumberInput<NumberType>:InputBase where NumberType : INumber<NumberType> public partial class NumberInput<NumberType> : InputBase where NumberType : INumber<NumberType>
{ {
/// <summary>
private double _step =1; /// Step for up and down on numeric field
/// Options: Any double number
/// Default: 1
/// </summary>
[Parameter] [Parameter]
public double Step { public double Step { get; set; } = 1;
get
{
return _step;
}
set
{
_step=value;
}
}
/// <summary>
/// Mouse wheel disable to prevent StepUp/StepDown on number filed
/// Options: true, false
/// Default: false
/// </summary>
[Parameter] [Parameter]
public bool DisableMouseWheel public bool DisableMouseWheel { get; set; } = false;
{
get;
set;
} = false;
/// <summary>
/// Increase 'Value' for the 'Step'
/// </summary>
/// <returns>'Value' increased for the 'Step' parameter</returns>
private async Task StepUp() private async Task StepUp()
{ {
try try
{ {
double num = (double)Convert.ChangeType(Value, typeof(double)); var num = Helper.ConvertToType<double>(Value);
num += _step;
num += Step;
if (DecimalPlaces > 0) if (DecimalPlaces > 0)
num = Math.Round(num, DecimalPlaces); num = Math.Round(num, DecimalPlaces);
Value = (NumberType)Convert.ChangeType(num, typeof(NumberType)); Value = Helper.ConvertToType<NumberType>(num);
if (IsError) ErrorText = string.Empty;
if (IsError)
ErrorText = string.Empty;
} }
catch catch
{ {
ErrorText = "Error with step up!"; ErrorText = "Error with step up!";
Value = default(NumberType); Value = default;
} }
await ValueChanged.InvokeAsync(Value); await ValueChanged.InvokeAsync(Value);
} }
/// <summary>
/// Decrease 'Value' for the 'Step'
/// </summary>
/// <returns>'Value' decreased for the 'Step' parameter</returns>
private async Task StepDown() private async Task StepDown()
{ {
try try
{ {
double num = (double)Convert.ChangeType(Value, typeof(double)); var num = Helper.ConvertToType<double>(Value);
num -= Step;
num -= _step;
if (DecimalPlaces > 0) if (DecimalPlaces > 0)
num = Math.Round(num, DecimalPlaces); num = Math.Round(num, DecimalPlaces);
Value = (NumberType)Convert.ChangeType(num, typeof(NumberType));
if (IsError) ErrorText = string.Empty; Value = Helper.ConvertToType<NumberType>(num);
} catch
if (IsError)
ErrorText = string.Empty;
}
catch
{ {
ErrorText = "Error with step down!"; ErrorText = "Error with step down!";
Value = default(NumberType); Value = default;
} }
await ValueChanged.InvokeAsync(Value); 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) 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) await StepDown();
return; }
if (args.DeltaY >= 0) else
{
await StepDown();
}
else
{
await StepUp();
}
} 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] [Parameter]
[EditorRequired]
public NumberType? Value public NumberType? Value
{ {
get get
{ {
if (string.IsNullOrEmpty(_value)) return default(NumberType); if (string.IsNullOrEmpty(_value))
return (NumberType)Convert.ChangeType(_value, typeof(NumberType)); return default;
else
{
try
{
return Helper.ConvertToType<NumberType>(_value);
} catch
{
return default;
}
}
} }
set 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] [Parameter]
public int DecimalPlaces { get; set; } =0; public int DecimalPlaces { get; set; } = 0;
/// <summary>
/// Value change event
/// </summary>
[Parameter] [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) if (args.Value is not null)
{ {
string newVal = args.Value.ToString(); var newVal = args.Value.ToString()!;
if (!newVal.Equals("0")) if (!newVal.Equals("0"))
{ {
if (newVal.ToString().Contains("-")) if (newVal.ToString().Contains("-"))
newVal = "-" + newVal.ToString().Replace("-", ""); newVal = "-" + newVal.ToString().Replace("-", "");
if (newVal.ToString().ToLower().Contains("e")) if (newVal.ToString().ToLower().Contains("e"))
newVal = "e" + newVal.ToString().Replace("e", ""); newVal = "e" + newVal.ToString().Replace("e", "");
} }
if (string.IsNullOrEmpty(newVal)) if (string.IsNullOrEmpty(newVal))
{ await ValueChanged.InvokeAsync(default);
await ValueChanged.InvokeAsync((NumberType)Convert.ChangeType((NumberType)Convert.ChangeType(null, typeof(NumberType)), typeof(NumberType)));
} if (!newVal.Equals(_value))
else await ValueChanged.InvokeAsync(Helper.ConvertToType<NumberType>(newVal));
{
if (!newVal.Equals(_value))
await ValueChanged.InvokeAsync((NumberType)Convert.ChangeType((NumberType)Convert.ChangeType(newVal, typeof(NumberType)), typeof(NumberType)));
}
} }
} }
public async Task Change(ChangeEventArgs args) public async Task Change(ChangeEventArgs args)
{ {
if (args.Value is not null) if (args.Value is not null)
{ Value = AdjustDecimalPlaces(Helper.ConvertToType<NumberType>(args.Value));
Value = AdjustDecimalPlaces((NumberType)Convert.ChangeType(args.Value, typeof(NumberType)));
} await ValueChanged.InvokeAsync(Value);
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) private bool CheckKey(string key)
{ {
@ -172,49 +219,48 @@ public partial class NumberInput<NumberType>:InputBase where NumberType : INumbe
private bool _preventDefaultAction = true; private bool _preventDefaultAction = true;
public async Task ChangeValue(KeyboardEventArgs args) public async Task ChangeValue(KeyboardEventArgs args)
{ {
_preventDefaultAction= true; _preventDefaultAction = true;
if (args is not null) if (args is not null)
{ {
var key = args.Key.ToString().ToLower(); var key = args.Key.ToString().ToLower();
if (CheckKey(key)) if (CheckKey(key))
{ {
_preventDefaultAction = false; _preventDefaultAction = false;
await OnKeyDown.InvokeAsync(args); await OnKeyDown.InvokeAsync(args);
} }
} else }
else
{ {
args.Key = null; 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; var result = value;
if (DecimalPlaces > 0) if (DecimalPlaces > 0)
{ {
double converted = Math.Round((double)Convert.ChangeType(result, typeof(double)), DecimalPlaces); double converted = Math.Round(Helper.ConvertToType<double>(result), DecimalPlaces);
return (NumberType)Convert.ChangeType(converted, typeof(NumberType)); return Helper.ConvertToType<NumberType>(converted);
} }
return result; 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() private async Task Clear()
{ {
_value = string.Empty; var val = Helper.ConvertToType<NumberType>(null);
await ValueChanged.InvokeAsync((NumberType)Convert.ChangeType(0, typeof(NumberType))); await ValueChanged.InvokeAsync(val);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (!DecimalPlaces.Equals(Helper.GetDecimalPlaces(Value)))
{
Value = AdjustDecimalPlaces(Value);
StateHasChanged();
}
}
} }
#region Lifecycle #region Lifecycle
@ -222,18 +268,26 @@ public partial class NumberInput<NumberType>:InputBase where NumberType : INumbe
{ {
if (typeof(NumberType).Name.ToLower().Contains("int")) if (typeof(NumberType).Name.ToLower().Contains("int"))
{ {
if (Step - (int)Step > 0) Step = (int)Step; if (Step - (int)Step > 0)
if (Step < 1) Step = 1; Step = (int)Step;
if (Step < 1)
Step = 1;
} }
await base.OnParametersSetAsync(); await base.OnParametersSetAsync();
} }
protected override async Task OnInitializedAsync() protected override void OnAfterRender(bool firstRender)
{ {
await base.OnInitializedAsync(); if (firstRender)
if (Required)
{ {
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; @inherits InputBase;
<div class="radio-group"> <label class="radio-group"
<input id="@Id" name="@ParentRadioGroup.Name" type="radio" @onchange="OnChange" @attributes=@InputAttributes> for="@Id">
<label for="@Id" class="radio-label">@Label</label> <input class="@ClassNameList"
</div> 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.Models;
using Connected.Utilities;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace Connected.Components; namespace Connected.Components;
public partial class Radio: InputBase public partial class Radio : InputBase
{ {
#region Parameters
[CascadingParameter] [CascadingParameter]
public RadioGroup? ParentRadioGroup { get; set; } public RadioGroup? ParentRadioGroup { get; set; }
@ -11,21 +13,51 @@ public partial class Radio: InputBase
public bool Checked { get; set; } = false; public bool Checked { get; set; } = false;
[Parameter, EditorRequired] [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] [Parameter]
public EventCallback<bool> CheckedChange { get; set; } public string ClassName { get; set; } = string.Empty;
public async Task OnChange(ChangeEventArgs args)
private string ClassNameList
{ {
Checked = (bool)args.Value; get
CheckedChange.InvokeAsync(Checked); {
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(); get
if (ParentRadioGroup.Disabled) Disabled = true; {
if (!InputAttributes.ContainsKey("disabled")) return new CssBuilder("radio-label")
InputAttributes.Add("disabled", Disabled); .AddClass(LabelClassName)
.Build();
}
} }
#endregion
} }

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

@ -3,10 +3,32 @@
namespace Connected.Components; namespace Connected.Components;
public partial class RadioGroup 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] [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] [Parameter]
public RenderFragment ChildContent { get; set; } public RenderFragment? ChildContent { get; set; }
#endregion
} }

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

@ -1,21 +1,46 @@
using Connected.Models; using Connected.Models;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Connected.Components; namespace Connected.Components;
public partial class SimpleSelect<ValueType> : InputBase public partial class SimpleSelect<ValueType> : InputBase
{ {
[Parameter] private bool component_loaded = false;
public ValueType Value { get; set; } #region Parameters
/// <summary>
/// Value that is currently selected in the dropdown. Used for @bind-Value
/// </summary>
[Parameter] [Parameter]
public IEnumerable<ValueType> Items { get; set; } public ValueType? Value { 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] [Parameter]
public bool EnableSearch { get; set; } = true; public bool EnableSearch { get; set; } = true;
private string _searchText { get; set; } = string.Empty; private string _searchText { get; set; } = string.Empty;
/// <summary>
/// Search string provided by user
/// Options: any string
/// Default: string.Empty
/// </summary>
public string SearchText public string SearchText
{ {
get 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) private async Task SetSelectedItem(ValueType item)
{ {
//DropDownClassToggle();
await ValueChanged.InvokeAsync(item); await ValueChanged.InvokeAsync(item);
} }
/// <summary>
/// Method for filtering items using 'SearchText' as filter
/// </summary>
private void FilterItems() private void FilterItems()
{ {
if (string.IsNullOrEmpty(_searchText)) if (Items is not null)
{ {
Items = OriginalItems; if (string.IsNullOrEmpty(_searchText))
{
SetItems();
}
else
{
FilteredItems = Items.Where(item => item.ToString().ToLower().Contains(_searchText.ToLower())).ToList();
}
StateHasChanged();
} }
else
{
Items = Items.Where(item => item.ToString().ToLower().Contains(_searchText.ToLower()));
}
StateHasChanged();
} }
/// <summary>
/// Event triggered when value changes
/// </summary>
[Parameter] [Parameter]
public EventCallback<ValueType> ValueChanged { get; set; } 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)
{ {
SetItems();
OriginalItems = Items; FilterItems();
if (_searchText.Length>0) FilterItems();
await base.OnParametersSetAsync();
} }
#endregion
#region Lifecycle
/// <summary>
/// Initializing Collections and aplying Filters if provided
/// </summary>
/// <returns>Nothing gets returned</returns>
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await base.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; @inherits InputBase;
<div class="@InputFieldClassList"> <div class="@InputFieldClassList">
@if (NumOfRows==1) <input type="@InputType"
{
<input type="@inputType"
value="@Value"
placeholder="@Placeholder"
disabled="@Disabled"
readonly="@Readonly"
@oninput=@ChangeValueAsync
@attributes="@InputAttributes" />
} else
{
<textarea type="textarea"
rows="@NumOfRows"
value="@Value" value="@Value"
placeholder="@Placeholder" placeholder="@Placeholder"
disabled="@Disabled" disabled="@Disabled"
readonly="@Readonly" readonly="@Readonly"
required="@Required"
@oninput=@ChangeValueAsync @oninput=@ChangeValueAsync
@attributes="@InputAttributes" /> @attributes="@InputAttributes" />
}
<span class="highlight"></span> <span class="highlight"></span>
<span class="bar"></span> <span class="bar"></span>
@ -45,16 +33,15 @@
{ {
<span class="input-glyph button" @onclick="Clear"> <span class="input-glyph button" @onclick="Clear">
<Glyph SVG="@Icons.Material.Rounded.Dangerous" /> <Glyph SVG="@Icons.Material.Rounded.Dangerous" />
<!--<i class='bx bx-x-circle'></i>-->
</span> </span>
} }
@if (IsError) @if (IsError)
{ {
<span class="input-glyph error"> <span class="input-glyph error">
<Glyph SVG="@Icons.Material.Outlined.Error" Color="#D10000" /> <Glyph SVG="@Icons.Material.Outlined.Error" />
<!--<i class='bx bx-error-circle'></i>-->
</span> </span>
} }
</span> </span>
</span> </span>
</div> </div>

@ -2,67 +2,74 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace Connected.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] [Parameter]
public bool IsPassword { get; set; } = false; public bool IsPassword { get; set; } = false;
[Parameter] /// <summary>
public int NumOfRows /// String for the component to use if TextInput is password field
{ /// </summary>
get private string InputType => IsPassword ? "password" : "text";
{
return _numberOfLines;
}
set
{
if (value < 1)
{
_numberOfLines = 1;
}
else
{
_numberOfLines = value;
}
}
}
private int _numberOfLines = 1;
/// <summary>
/// Number of rows
/// </summary>
[Parameter] [Parameter]
public string Value { get; set; } = string.Empty; public int NumOfRows
private string inputType
{ {
get get
{ {
if (IsPassword) return "password"; return _numberOfLines;
return "text"; }
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] #endregion
public EventCallback<string> ValueChanged { get; set; }
private async Task ChangeValueAsync(ChangeEventArgs args) #region Events, Methods
{
await ValueChanged.InvokeAsync(args.Value.ToString());
}
/// <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() private async Task Clear()
{ {
await ValueChanged.InvokeAsync(string.Empty); await ValueChanged.InvokeAsync(string.Empty);
} }
protected override async Task OnInitializedAsync() #endregion
{
await base.OnInitializedAsync();
if (Required)
{
if (!InputAttributes.ContainsKey("required")) InputAttributes.Add("required", true);
}
}
} }

@ -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] [Parameter]
public bool Required { get; set; } = false; public bool Required { get; set; } = false;
public Dictionary<string, object> InputAttributes { get; set; } public Dictionary<string, object> InputAttributes { get; set; } = new();
/// <summary> /// <summary>
/// Show clear button. /// Disable input component
/// Options: true or false
/// Default: false
/// </summary> /// </summary>
[Parameter] [Parameter]
public bool Disabled { get; set; } = false; public bool Disabled { get; set; } = false;
/// <summary> /// <summary>
/// Show clear button. /// Make input component readonly
/// Options: true or false
/// Default: false
/// </summary> /// </summary>
[Parameter] [Parameter]
public bool Readonly { get; set; } = false; public bool Readonly { get; set; } = false;
@ -50,18 +54,11 @@ public class InputBase : ComponentBase
[Parameter] [Parameter]
public bool ShowCharacterCounter { get; set; } public bool ShowCharacterCounter { get; set; }
private string _errorText = string.Empty; /// <summary>
///
/// </summary>
[Parameter] [Parameter]
public string ErrorText { public string ErrorText { get; set; } = string.Empty;
get
{
return _errorText;
}
set
{
_errorText = value;
}
}
public bool IsError public bool IsError
{ {
@ -79,15 +76,16 @@ public class InputBase : ComponentBase
protected virtual async Task SetTextAsync(string text) protected virtual async Task SetTextAsync(string text)
{ {
if (Text != text) if (Text == text)
{ return;
Text = text;
await TextChanged.InvokeAsync(text); Text = text;
} await TextChanged.InvokeAsync(text);
} }
private string _helperText = string.Empty; private string _helperText = string.Empty;
[Parameter] [Parameter]
public string HelperText public string HelperText
{ {
@ -123,7 +121,7 @@ public class InputBase : ComponentBase
[Parameter] [Parameter]
public string Placeholder { get; set; } = string.Empty; public string Placeholder { get; set; } = string.Empty;
protected override async Task OnInitializedAsync() protected override void OnInitialized()
{ {
if (InputAttributes is null) InputAttributes = new(); 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; using Microsoft.JSInterop;
namespace Connected; namespace Connected;
@ -24,8 +26,9 @@ internal class Navigation
/// Navigates to the specified url. /// Navigates to the specified url.
/// </summary> /// </summary>
/// <param name="url">The destination url (relative or absolute).</param> /// <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 (!target.Equals("_self"))
{ {
if (!url.Equals(_navigationManager.Uri)) 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'; @forward 'globals';
@import 'layout/_index'; @forward 'layout';
@import 'components/_index'; @forward 'components';
@import 'util/_index'; @forward 'util';

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

@ -1,102 +1,188 @@
$color3: #f4f4f4; $checkbox-primary-color: var(--bg-core-primary-darken);
$color4: var(--bg-core-primary-darken); $checkbox-secondary-color: #f4f4f4;
$checkbox-disabled-color: darken($checkbox-secondary-color, 25%);
.checkbox-group { .checkbox-group {
margin: 0.5rem; --width: 1.25rem;
position:relative; --height: var(--width);
input[type="checkbox"] { --inset: calc(var(--width) / 10);
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;
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 { input[type="checkbox"] {
// + .checkbox-label { position: absolute;
// &:after { opacity: 0;
// 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;
} }
}
}
}
} }
.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-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-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-label:hover:after { .checkbox-input:disabled ~ .checkbox-label {
background: var(--bg-core-primary-lighten); color: $checkbox-disabled-color;
scale: 1; }
opacity: .5;
}
.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;
// }

@ -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 "../util" as *;
@use "../globals" 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*/

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

@ -10,7 +10,7 @@ $width: 100%;
form{ form{
display: block; display: block;
margin-top: 1rem; //padding-top: 1rem;
} }
.form-group{ .form-group{
@ -36,12 +36,13 @@ form{
width: 100%; width: 100%;
border: none; border: none;
border-radius: 0; border-radius: 0;
border-bottom: 1px solid $border-core; border-bottom: 1px solid var(--border-core);
&:focus { &:focus {
outline: none; outline: none;
} }
&:focus ~ label.label-animated, &:focus ~ label.label-animated,
&:valid ~ label.label-animated { &:valid ~ label.label-animated,
&:disabled ~ label.label-animated {
top: -12px; top: -12px;
font-size: $font-size-sm; font-size: $font-size-sm;
color: var(--bg-core-primary-darken); color: var(--bg-core-primary-darken);
@ -204,31 +205,71 @@ select {
height: auto; height: auto;
max-height: max-content; max-height: max-content;
opacity: 1; opacity: 1;
transition: all $trans-time ease-in-out; pointer-events: initial;
}
&:focus-within ~ .backdrop {
opacity: 1;
display: block;
} }
} }
.drop-down{ .drop-down{
position: absolute; position: fixed;
opacity: 0; top: 50%;
display: flex; left: 50%;
flex-direction: column; transform: translate(-50%, -50%);
top: 46px; min-width: 80%;
left: 0; width: 80%;
min-width: 100%;
height: 0; height: 0;
max-height: 0; max-height: 0;
//padding: $base-padding; display: flex;
flex-direction: column;
opacity: 0;
pointer-events: none;
z-index: 1500; z-index: 1500;
list-style: none; list-style: none;
background-color: #fff; background-color: #fff;
box-shadow: $base-box-shadow; 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; border-radius: 0 0 $border-radius-lg $border-radius-lg;
}
} }
.dropdown-header { .dropdown-header {
display: block; display: block;
padding: 0.5rem 1.5rem; padding: 0.5rem 1.5rem;
@ -253,12 +294,12 @@ select {
.dropdown-item:focus, .dropdown-item:hover { .dropdown-item:focus, .dropdown-item:hover {
color: var(--text-core-hc); color: var(--text-core-hc);
text-decoration: none; text-decoration: none;
background-color: var(--bg-core-primary-lighten); background-color: var(--bg-core-primary-light);
} }
.dropdown-divider { .dropdown-divider {
height: 0; height: 0;
margin: 0.5rem 0; margin: 0.5rem 0;
overflow: hidden; 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; $radio-primary-color: var(--bg-core-primary-darken);
$color2: var(--bg-core-primary-darken); $radio-secondary-color: #f4f4f4;
$radio-disabled-color: darken($radio-secondary-color, 25%);
.radio-group { .radio-group {
margin: 0.5rem; --width: 1.25rem;
position:relative; --height: var(--width);
input[type="radio"] { --inset: calc(var(--width) / 10);
position: absolute;
opacity: 0; margin: 0.75rem 0;
+ .radio-label { width: max-content;
&:before { cursor: pointer;
content: ""; position:relative;
background: $color1; display: flex;
border-radius: 100%; align-items: center;
border: 2px solid $color2; gap:.75rem;
display: inline-block; z-index: 1;
width: 1.25em;
height: 1.25em; &.reversed{
position: relative; justify-content: space-between;
top: .15rem; width: 100%;
margin-right: .75em;
vertical-align: top; & :nth-child(1) {order:2;}
cursor: pointer; & :nth-child(2) {order:1;}
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%);
} }
&.column{
flex-direction: column;
gap:.25rem;
width: 100%;
} }
}
+ .radio-label { input[type="radio"] {
&:empty { position: absolute;
&:before { opacity: 0;
margin-right: 0;
} }
}
}
}
} }
.radio-fill {
position: relative;
.radio-label:after {
content: "";
display: inline-block; display: inline-block;
width: 35px; width: var(--width);
height: 35px; height: var(--height);
border-radius: 50%; border-radius: 50%;
background-color: transparent; background: transperent;
position: absolute; border: 2px solid $radio-primary-color;
left: -8px; transition: background 0.2s ease-in-out;
top: -6px; }
scale: .75;
transition: scale 0.35s cubic-bezier(0.6,-1.25,0.6,2.25); .radio-input:checked ~ .radio-fill {
background: var(--bg-core-primary-darken);
box-shadow: inset 0 0 0 var(--inset) $radio-secondary-color;
} }
.radio-label:hover:after { .radio-input:focus-within ~ .radio-fill {
background: var(--bg-core-primary-lighten); outline: 1px solid $radio-primary-color;
scale: 1;
opacity: .5;
} }
.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-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;
// }

@ -2,28 +2,35 @@
@use '../util/' as *; @use '../util/' as *;
$sidebar-width-open:300px;
$sidebar-width-closed:75px;
$sidebar-right-width-open:300px;
$sidebar-right-width-closed:0px;
.sidebar{ .sidebar{
position: fixed; position: fixed;
padding-inline: 0.75rem;
height: 100vh; height: 100vh;
width: 100%; width: 100%;
color: $text-core; color: $text-core;
background: var(--bg-core-primary); background: var(--bg-core-primary-light);
//margin: -2rem 2rem -2rem -2rem; border-right: 1px solid rgba(0,0,0,.025);
z-index: 100; border-radius: $border-radius-lg 0 0 $border-radius-lg;
z-index: $sidebar-zindex;
transition: all 0.3s ease; transition: all 0.3s ease;
@include breakpoint(sm) { @include breakpoint(sm) {
height: calc(100% + 4rem); height: 100%;
position: relative; position: relative;
width: 300px; width: $sidebar-width-open;
} }
} }
.sidebar.close{ .sidebar.close{
width: 0; width: 0;
transform: translateX(-75px); transform: translateX($sidebar-width-closed);
@include breakpoint(sm) { @include breakpoint(sm) {
width: 75px; width: $sidebar-width-closed;
transform: translateX(0); transform: translateX(0);
} }
} }
@ -31,59 +38,80 @@
.main { .main {
width: calc(100% - 1.5rem) ; width: calc(100% - 1.5rem) ;
margin-inline:auto; margin-inline:auto;
transition: all 0.3s ease;
@include breakpoint (sm) { @include breakpoint (sm) {
width: calc(100% - 300px);; width: calc(100% - $sidebar-width-open - $sidebar-right-width-open);
padding: 2rem; padding: 2rem;
} }
} }
.main.close{ .main.close{
@include breakpoint (sm) { @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 //profile
.sidebar .profile-details{ .sidebar .profile-details{
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
color: #fff; padding-top: 0;
background-color: rgba(0,0,0,.6); //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{ .sidebar .profile-details .avatar{
min-width: calc(70px - 1rem); min-width: 50px;
text-align: center; text-align: center;
margin: 1rem 0 1rem 1rem; margin-right: 12px;
}
}
.sidebar .profile-details img{ .sidebar .profile-details img{
height: 40px; height: 50px;
width: 40px; width: 50px;
object-fit: cover; object-fit: cover;
border-radius: 50%; 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{ .sidebar .profile-details .profile-name{
font-weight: 600; font-weight: 600;
white-space: nowrap; white-space: nowrap;
color:var(--text-core-hc)
} }
.sidebar .profile-details .profile-job{ .sidebar .profile-details .profile-job{
font-size: 12px; font-size: 12px;
font-weight: 500; font-weight: 500;
color:var(--text-core-lc)
} }
.sidebar .profile-details i{ .sidebar .profile-details i{
height: 50px; height: 50px;
min-width: 70px; min-width: 50px;
text-align: center; text-align: center;
line-height: 50px; line-height: 50px;
font-size: 20px; font-size: 20px;
@ -94,137 +122,359 @@
.sidebar.close .profile-details .profile-name, .sidebar.close .profile-details .profile-name,
.sidebar.close .profile-details .profile-job{ .sidebar.close .profile-details .profile-job{
display: none; display: none;
} }
//links /*NAV*/
.sidebar .nav-links{ .sidebar nav{
height: 100%; position: sticky;
top: 0;
}
.sidebar .navbar{
height: auto;
//padding: 12px;
padding: 0; padding: 0;
overflow: auto; overflow: auto;
display: flex;
flex-direction: column;
} }
.sidebar.close .nav-links{
.sidebar.close .navbar{
overflow: visible; overflow: visible;
} }
.sidebar .nav-links::-webkit-scrollbar{
.sidebar .navbar::-webkit-scrollbar{
display: none; 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; position: relative;
list-style: none; list-style: none;
transition: all 0.4s ease; 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); background: var(--bg-core-primary-lighten);
} }
.sidebar .nav-links li .iocn-link{
display: flex; .sidebar.close .navbar .navbar-item{
align-items: center; border-radius: 50%;
justify-content: space-between;
} }
.sidebar.close .nav-links li .iocn-link{
display: block .sidebar .navbar .navbar-item.fab{
background-color:#ffdad9;
border-radius: $border-radius-lg;
margin-bottom: 36px;
transition: all 0.3s ease;
}
.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; height: 50px;
min-width: 70px; min-width: 50px;
text-align: center; text-align: center;
line-height: 50px; line-height: 50px;
color: var(--text-core-vc); color: var(--text-core-lc);
font-size: 20px; font-size: 22px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.sidebar .nav-links li.showMenu i.arrow{
transform: rotate(-180deg); .sidebar .navbar .navbar-item a{
}
.sidebar.close .nav-links i.arrow{
display: none;
}
.sidebar .nav-links li a{
display: flex; display: flex;
align-items: center; align-items: center;
text-decoration: none; text-decoration: none;
} }
.sidebar .nav-links li a .link_name{
.sidebar .navbar .navbar-item a .navbar-link{
flex: 1 0 auto; flex: 1 0 auto;
font-weight: 400; font-weight: $font-weight-400;
color: var(--text-core); color: var(--text-core);
//transition: all 0.4s ease;
} }
.sidebar.close .nav-links li a .link_name{
opacity: 0;
pointer-events: none;
}
.sidebar .navbar .navbar-item a .navbar-link:first-child {
padding-left: 1.5rem;
}
.sidebar .nav-links li .sub-menu{ .sidebar.close .navbar .navbar-item a .navbar-link{
padding: 6px 6px 14px 70px; opacity: 0;
margin-top: -10px; pointer-events: none;
background:var(--bg-core-primary-lighten);
display: none; 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 .navbar .navbar-item a .navbar-link.navbar-link-detail{
} flex: 0 0 auto;
.sidebar .nav-links li .sub-menu a:hover{ font-weight: $font-weight-600;
color: var(--bg-core-primary-darken); color: var(--text-core-hc);
text-align: right;
padding-right: 1rem;
} }
.sidebar.close .nav-links li .sub-menu{
/*TOOLTIP*/
.sidebar.close .navbar .navbar-item .navbar-tooltip{
position: absolute; position: absolute;
left: 100%; background: #fff;
top: -10px; box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
width: max-content;
left: calc(100% + 12px);
top: 0;
margin-top: 0; margin-top: 0;
padding: 10px 20px; padding: 10px 20px;
border-radius: 0 6px 6px 0; border-radius: $border-radius-lg;
opacity: 0; opacity: 0;
display: block; display: block;
pointer-events: none; pointer-events: none;
transition: 0s; 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; opacity: 1;
pointer-events: auto; pointer-events: auto;
transition: all 0.4s ease; transition: all 0.4s ease;
} }
.sidebar .nav-links li .sub-menu .link_name{
display: none; .sidebar.close .navbar .navbar-tooltip {
}
.sidebar.close .nav-links li .sub-menu .link_name{
font-weight: 600;
opacity: 1; opacity: 1;
display: block; display: block;
} }
.sidebar .nav-links li .sub-menu.blank{
opacity: 1; .sidebar .navbar .navbar-tooltip {
pointer-events: auto; display: none;
padding: 3px 20px 6px 16px;
opacity: 0;
pointer-events: 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,7 +8,7 @@ body{
background: linear-gradient(to left, var(--bg-core-primary-lighten), var(--bg-core-end)); background: linear-gradient(to left, var(--bg-core-primary-lighten), var(--bg-core-end));
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
font-size: $base-font-size; 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); color: var(--text-core);
@include breakpoint(sm){ @include breakpoint(sm){
@ -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 */ /* Box sizing rules */
*, *, *::before, *::after {
*::before,
*::after {
box-sizing: border-box; box-sizing: border-box;
} }
@ -48,7 +46,13 @@ a:not([class]) {
text-decoration-skip-ink: auto; text-decoration-skip-ink: auto;
} }
a, a:hover { a {
text-decoration: none;
transition: color 0.15s ease-in-out;
}
a:hover {
text-decoration: none; text-decoration: none;
transition: color 0.15s ease-in-out; transition: color 0.15s ease-in-out;
} }

@ -157,6 +157,27 @@ p,
text-overflow: ellipsis; 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: ( // $fs: (
// "font-size": ( // "font-size": (
// "prefix": "fs", // "prefix": "fs",

@ -1,8 +1,8 @@
@use "sass:math"; @use "sass:math";
//theme colors //theme colors
$bg-core-primary: hsl(225, 68%, 85%); // body-background; //$bg-core-primary: hsl(225, 68%, 85%); // body-background;
$bg-core-start: $bg-core-primary; //$bg-core-start: $bg-core-primary;
$bg-core-end: #fafafa; $bg-core-end: #fafafa;
$text-core-hc: #212121; //title $text-core-hc: #212121; //title
@ -10,67 +10,112 @@ $text-core: #525252; //text
$text-core-lc: #7d7d80; //metadata $text-core-lc: #7d7d80; //metadata
$text-core-vc: #969699; //ikona-text $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-bg-core: rgba(255, 255, 255, 0.5); // elements-background
$element-fg-core: rgba(255, 255, 255, 0.25); // elements-foreground $element-fg-core: rgba(255, 255, 255, 0.25); // elements-foreground
:root { :root {
--bg-core-primary: #{$bg-core-primary}; // body-background; $blue-50: #f2f4fd;
--bg-core-primary-lighten: hsl(225, 68%, 90%); // lighten $blue-100: #e5eafa;
--bg-core-primary-darken: hsl(225, 68%, 75%); // darken; $blue-200: #cbd6f5;
--bg-core-start: #{$bg-core-primary}; $blue-300: #b2c1f1;
--bg-core-end: #{$bg-core-end}; $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-hc: #{$text-core-hc}; //title
--text-core: #{$text-core}; //text --text-core: #{$blue-800}; //text
--text-core-lc: #{$text-core-lc}; //metadata --text-core-lc: #{$text-core-lc}; //metadata
--text-core-vc: #{$text-core-vc}; //ikona-text --text-core-vc: #{$text-core-vc}; //ikona-text
--bg-core-primary-lighter: hsl(225, 68%, 90%); //hover --border-core-lighten:#{$blue-100}; //border-lighten
--border-core: #{$border-core}; //border --border-core: #{$blue-200}; //border
--element-bg-core: #{$element-bg-core}; // elements-background --element-bg-core: #{$element-bg-core}; // elements-background
--element-fg-core: #{$element-fg-core}; // elements-foreground --element-fg-core: #{$element-fg-core}; // elements-foreground
} }
.pink,
:root:has(pink:checked) { .pink {
--bg-core-primary: hsl(310 50% 90%); $violet-50: #f0eef6;
--bg-core-primary-lighten: hsl(310 50% 95%); //lighten $violet-100: #e1dced;
--bg-core-primary-darken: hsl(310 50% 85%); //darken $violet-200: #c2b9db;
--element-bg-core: hsl(310 50% 100% / 50%); $violet-300: #a496c8;
--text-core: hsl(310 50% 15%); $violet-400: #8573b6;
--border-core: hsl(310 50% 90%); $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: hsl(0 0% 10% / 100%); // body-background;
--bg-core-primary-lighten: hsl(0 0% 15% / 100%); // lighten --bg-core-primary-light: hsl(0 0% 15% / 100%); // light
--bg-core-primary-darken: hsl(0 0% 5% / 100%); // darken; --bg-core-primary-lighten: hsl(0 0% 17% / 100%); // lighten
--bg-core-start: var(--bg-core-primary); --bg-core-primary-darken: hsl(0 0% 7% / 100%); // darken;
--bg-core-end: hsl(0 0% 15% / 100%); --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-hc: hsl(0 0% 97% / 100%); //title
--text-core: hsl(0 0% 73% / 100%); //text --text-core: hsl(0 0% 73% / 100%); //text
--text-core-lc: hsl(0 0% 53% / 100%); //metadata --text-core-lc: hsl(0 0% 53% / 100%); //metadata
--text-core-vc: hsl(0 0% 60% / 100%); //ikona-text --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-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 // colors
$blue: #0d6efd; // $blue: #0d6efd;
$indigo: #6610f2; // $indigo: #6610f2;
$purple: #6f42c1; // $purple: #6f42c1;
$pink: #d63384; // $pink: #d63384;
$red: #dc3545; // $red: #dc3545;
$orange: #fd7e14; // $orange: #fd7e14;
$yellow: #ffc107; // $yellow: #ffc107;
$green: #00c851; //#198754; // $green: #00c851; //#198754;
$teal: #20c997; // $teal: #20c997;
$cyan: #0dcaf0; // $cyan: #0dcaf0;
$white: #fff; $white: #fff;
$gray-100: #f8f9fa; $gray-100: #f8f9fa;
@ -84,13 +129,20 @@ $gray-800: #343a40;
$gray-900: #212529; $gray-900: #212529;
$black: #000; $black: #000;
$blue: #0dbcf0c4;
$red: #ff7f78;
$yellow: #ffb480;
$green: #8fcaa3;
$cyan: #729fe9;
$primary: $blue; $primary: $blue;
$secondary: $gray-600; $secondary: $gray-400;
$success: $green; $success: $green;
$info: $cyan; $info: $cyan;
$warning: $yellow; $warning: $yellow;
$danger: $red; $danger: $red;
$light: $gray-400; $light: $gray-300;
$dark: $gray-900; $dark: $gray-900;
//color palette //color palette
@ -138,9 +190,16 @@ $font-weight-800: 800; // extra bold
$base-border-radius: 0.3rem; $base-border-radius: 0.3rem;
$border-radius-sm: math.div($base-border-radius, 2); $border-radius-sm: math.div($base-border-radius, 2);
$border-radius-lg: $base-border-radius * 2; $border-radius-lg: $base-border-radius * 2;
$border-radius-pill: 100vw;
// box-shadow // box-shadow
$base-box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 10%) !default; $base-box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 10%) !default;
// transition // transition
$transition: all 0.15s ease-in-out; $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 *; @use "../globals" as *;
.color-picker-container {
margin-bottom: 1rem;
}
.color-picker > fieldset { .color-picker > fieldset {
border: 0; border: 0;
display: flex; display: flex;
flex-direction: column; //flex-direction: column;
gap: 2rem; gap: 1.5rem;
width: fit-content; width: fit-content;
//background: #fff; //background: #fff;
padding: 1rem 3rem; padding: 0.5rem 1.5rem;
position: fixed; //position: fixed;
top: 1rem; //top: 1rem;
right: 0; //right: 0;
border-radius: 0 0 1rem 1rem; border-radius: 0 0 1rem 1rem;
} }
@ -31,14 +36,15 @@
background-color: var(--radio-color); background-color: var(--radio-color);
} }
.color-picker input[type="radio"]light { .color-picker input[type="radio"] {
--radio-color: hsl(225, 68%, 85%); --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; --radio-color: #232323;
} }
@ -58,8 +64,9 @@
background: var(--element-fg-core); background: var(--element-fg-core);
border: 1px solid; border: 1px solid;
border-color: var(--border-core); border-color: var(--border-core);
color: inherit; //color: inherit;
border-radius: $border-radius-lg; border-radius: $border-radius-lg;
} }

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

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

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

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

@ -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 //Display classes
$displayProps: ( $displayProps: (
"block", "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) public static bool IsNumeric(string input)
{ {
try try
{ {
var number = Double.Parse(input); return Double.TryParse(input, out var number);
return true;
} }
catch catch
{ {

Loading…
Cancel
Save