Merge branch 'features/refactor' of https://git.tompit.com/Connected/Connected.Components into features/refactor

This commit is contained in:
stm 2023-01-30 07:55:55 +01:00
commit 1dabf54e39
75 changed files with 3443 additions and 3293 deletions

View File

@ -21,7 +21,7 @@
@if (CloseGlyphVisible)
{
<div class="alert-close">
<IconButton ClassList="size-small" Icon="@CloseGlyph" Clicked="OnCloseGlyphClick" />
<GlyphButton ClassList="size-small" Glyph="@CloseGlyph" Clicked="OnCloseGlyphClick" />
</div>
}
</div>

View File

@ -11,7 +11,7 @@
{
@if (!String.IsNullOrEmpty(Icon))
{
<Icon Icon="@Icon" Class="icon-badge" />
<Icon Glyph="@Icon" Class="icon-badge" />
}
else
{

View File

@ -7,7 +7,7 @@
<a href="@(Item?.Href ?? "#")">
@if (Item?.Icon != null)
{
<Icon Icon="@Item?.Icon" Size="Size.Small" />
<Icon Glyph="@Item?.Icon" Size="Size.Small" />
}
@Item?.Text
</a>

View File

@ -13,7 +13,7 @@
<BreadcrumbLink Item="Items[0]"></BreadcrumbLink>
<BreadcrumbSeparator></BreadcrumbSeparator>
<li class="breadcrumbs-expander" @onclick="Expand">
<Icon Icon="@ExpanderIcon" Size="Size.Small"></Icon>
<Icon Glyph="@ExpanderIcon" Size="Size.Small"></Icon>
</li>
<BreadcrumbSeparator></BreadcrumbSeparator>
<BreadcrumbLink Item="Items[Items.Count - 1]"></BreadcrumbLink>

View File

@ -14,12 +14,12 @@
<span class="fab-label">
@if (!string.IsNullOrWhiteSpace(StartIcon))
{
<Icon Icon="@StartIcon" Color="@IconColor" Size="@IconSize" />
<Icon Glyph="@StartIcon" Color="@IconColor" Size="@IconSize" />
}
@Label
@if (!string.IsNullOrWhiteSpace(EndIcon))
{
<Icon Icon="@EndIcon" Color="@IconColor" Size="@IconSize" />
<Icon Glyph="@EndIcon" Color="@IconColor" Size="@IconSize" />
}
</span>
</Element>

View File

@ -1,27 +0,0 @@
@namespace Connected.Components
@inherits ButtonBase
@using Connected.Components;
<Element disabled="@Disabled"
title="@IconTitle"
type="@ButtonType.ToString()"
ClassList="@CompiledClassList.ToString()"
HtmlTag="@HtmlTag"
PreventOnClickPropagation="PreventOnClickPropagation"
@attributes="CustomAttributes"
@onclick="OnClick">
@if (!String.IsNullOrEmpty(Icon))
{
<span name="glyph-container" class="glyph-button-label">
<Icon Glyph="@Icon" />
</span>
}
else
{
<TextContent Typo="Typo.body2">
@ChildContent
</TextContent>
}
</Element>

View File

@ -1,54 +0,0 @@
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class IconButton : ButtonBase
{
#region Content
/// <summary>
/// The Icon that will be used in the component.
/// </summary>
[Parameter]
public string? Icon { get; set; }
/// <summary>
/// GlyphTitle of the icon used for accessibility.
/// </summary>
[Parameter]
public string? IconTitle { get; set; }
/// <summary>
/// Child content of component, only shows if Glyph is null or Empty.
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
#endregion
#region Styling
/// <summary>
/// A space separated list of class names, added on top of the default class list.
/// </summary>
[Parameter]
public string? ClassList { get; set; }
/// <summary>
/// The variant to use.
/// </summary>
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
/// <summary>
/// Contains the default container classlist and the user defined classes.
/// </summary>
private CssBuilder CompiledClassList
{
get
{
return new CssBuilder("button-root glyph-button")
.AddClass(ClassList);
}
}
#endregion
}

View File

@ -1,13 +0,0 @@
@namespace Connected.Components
@inherits UIComponent
<IconButton aria-pressed="@Toggled.ToString()"
ClassList="@ClassList"
Clicked="Toggle"
Disabled="Disabled"
Icon="@(Toggled ? ToggledIcon : Icon)"
IconTitle="@(Toggled && ToggledIconTitle != null ? ToggledIconTitle : IconTitle)"
Variant="Variant"
@attributes="CustomAttributes"
/>

View File

@ -1,89 +0,0 @@
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class ToggleIconButton : UIComponent
{
#region Events
/// <summary>
/// Fires whenever toggled is changed.
/// </summary>
[Parameter]
public EventCallback<bool> ToggledChanged { get; set; }
public async Task Toggle()
{
await SetToggledAsync(!Toggled);
}
protected internal async Task SetToggledAsync(bool toggled)
{
if (Disabled)
return;
if (Toggled != toggled)
{
Toggled = toggled;
if (!ToggledChanged.HasDelegate)
return;
await ToggledChanged.InvokeAsync(Toggled);
}
}
#endregion
#region Content
/// <summary>
/// The glyph that will be used in the untoggled state.
/// </summary>
[Parameter]
public string? Icon { get; set; }
/// <summary>
/// GlyphTitle of the icon used for accessibility.
/// </summary>
[Parameter]
public string? IconTitle { get; set; }
/// <summary>
/// The glyph that will be used in the toggled state.
/// </summary>
[Parameter]
public string? ToggledIcon { get; set; }
/// <summary>
/// GlyphTitle used in toggled state, if different.
/// </summary>
[Parameter]
public string? ToggledIconTitle { get; set; }
#endregion
#region Styling
/// <summary>
/// A space separated list of class names, added on top of the default class list.
/// </summary>
[Parameter]
public string? ClassList { get; set; }
/// <summary>
/// The variant to use.
/// </summary>
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
/// <summary>
/// If true, the button will be disabled.
/// </summary>
[Parameter]
public bool Disabled { get; set; }
/// <summary>
/// The button toggled state.
/// </summary>
[Parameter]
public bool Toggled { get; set; }
#endregion
}

View File

@ -31,7 +31,7 @@
{
@if (PreviousButtonTemplate == null)
{
<IconButton tabindex="1" aria-label="Go to previous" Class="@NavigationButtonsClassName" Style="z-index:3;opacity:0.75" Icon="@PreviousIcon" Clicked="Previous" Color="ThemeColor.Inherit" />
<GlyphButton tabindex="1" aria-label="Go to previous" Class="@NavigationButtonsClassName" Style="z-index:3;opacity:0.75" Glyph="@PreviousIcon" Clicked="Previous" Color="ThemeColor.Inherit" />
}
else
{
@ -51,7 +51,7 @@
int current = i;
if (BulletTemplate == null)
{
<IconButton tabindex="@(i+3)" aria-label="@(i+1)" Class="@BulletsButtonsClassName" Style="z-index:3;opacity:0.75" Icon="@(current == SelectedIndex ? CheckedIcon : UncheckedIcon)" Clicked="(() => MoveTo(current))" Color="ThemeColor.Inherit" />
<GlyphButton tabindex="@(i+3)" aria-label="@(i+1)" Class="@BulletsButtonsClassName" Style="z-index:3;opacity:0.75" Glyph="@(current == SelectedIndex ? CheckedIcon : UncheckedIcon)" Clicked="(() => MoveTo(current))" Color="ThemeColor.Inherit" />
}
else
{
@ -69,7 +69,7 @@
{
@if (NextButtonTemplate == null)
{
<IconButton tabindex="2" aria-label="Go to next" Class="@NavigationButtonsClassName" Style="z-index:3;opacity:0.75" Icon="@NextIcon" Clicked="Next" Color="ThemeColor.Inherit" />
<GlyphButton tabindex="2" aria-label="Go to next" Class="@NavigationButtonsClassName" Style="z-index:3;opacity:0.75" Glyph="@NextIcon" Clicked="Next" Color="ThemeColor.Inherit" />
}
else
{

View File

@ -8,7 +8,7 @@
<span tabindex="0" class="@CheckBoxClassname">
@*note: stopping the click propagation is important here. otherwise checking the checkbox results in click events on its parent (i.e. table row), which is generally not what you would want*@
<input tabindex="-1" @attributes="CustomAttributes" type="checkbox" class="checkbox-input" checked="@BoolValue" @onchange="@OnChange" disabled="@Disabled" @onclick:preventDefault="@ReadOnly" />
<Icon Icon="@GetIcon()" Color="HasErrors ? ThemeColor.Error : ThemeColor.Inherit" Size="@Size" />
<Icon Glyph="@GetIcon()" Color="HasErrors ? ThemeColor.Error : ThemeColor.Inherit" Size="@Size" />
</span>
@if (!String.IsNullOrEmpty(Label))
{

View File

@ -10,11 +10,11 @@
}
else if (!String.IsNullOrEmpty(Icon) && !IsChecked)
{
<Icon Icon="@Icon" Class="chip-icon" Size="Size.Small" Color="@IconColor" />
<Icon Glyph="@Icon" Class="chip-icon" Size="Size.Small" Color="@IconColor" />
}
else if (IsChecked)
{
<Icon Icon="@CheckedIcon" Class="chip-icon" Size="Size.Small" />
<Icon Glyph="@CheckedIcon" Class="chip-icon" Size="Size.Small" />
}
<span class="chip-content">
@if (ChildContent == null)
@ -27,7 +27,7 @@
}
@if (OnClose.HasDelegate || ChipSet?.AllClosable==true)
{
<IconButton Class="chip-close-button" Icon="@(String.IsNullOrEmpty(CloseIcon) ? $"{Icons.Material.Filled.Cancel}" : $"{CloseIcon}")" Clicked="OnCloseHandler" Size="Size.Small"/>
<GlyphButton Class="chip-close-button" Glyph="@(String.IsNullOrEmpty(CloseIcon) ? $"{Icons.Material.Filled.Cancel}" : $"{CloseIcon}")" Clicked="OnCloseHandler" Size="Size.Small"/>
}
</span>
</div>

View File

@ -12,12 +12,12 @@
<PickerToolbar DisableToolbar="@DisableToolbar" Class="picker-color-toolbar">
@if (PickerVariant != PickerVariant.Static)
{
<IconButton Class="pa-1 mud-close-picker-button" Size="Size.Small" Color="ThemeColor.Inherit" Icon="@CloseIcon" Clicked="@GetEventCallback()" />
<GlyphButton Class="pa-1 mud-close-picker-button" Size="Size.Small" Color="ThemeColor.Inherit" Glyph="@CloseIcon" Clicked="@GetEventCallback()" />
}
<Spacer />
<IconButton Class="pa-1" Size="Size.Small" Color="GetButtonColor(ColorPickerView.Spectrum)" Icon="@SpectrumIcon" Clicked="(() => ChangeView(ColorPickerView.Spectrum))" />
<IconButton Class="pa-1 mx-1" Size="Size.Small" Color="GetButtonColor(ColorPickerView.Grid)" Icon="@GridIcon" Clicked="(() => ChangeView(ColorPickerView.Grid))" />
<IconButton Class="pa-1" Size="Size.Small" Color="GetButtonColor(ColorPickerView.Palette)" Icon="@PaletteIcon" Clicked="(() => ChangeView(ColorPickerView.Palette))" />
<GlyphButton Class="pa-1" Size="Size.Small" Color="GetButtonColor(ColorPickerView.Spectrum)" Glyph="@SpectrumIcon" Clicked="(() => ChangeView(ColorPickerView.Spectrum))" />
<GlyphButton Class="pa-1 mx-1" Size="Size.Small" Color="GetButtonColor(ColorPickerView.Grid)" Glyph="@GridIcon" Clicked="(() => ChangeView(ColorPickerView.Grid))" />
<GlyphButton Class="pa-1" Size="Size.Small" Color="GetButtonColor(ColorPickerView.Palette)" Glyph="@PaletteIcon" Clicked="(() => ChangeView(ColorPickerView.Palette))" />
</PickerToolbar>
<PickerContent Class="picker-color-content">
@if (!DisableColorField)
@ -126,7 +126,7 @@
@if (!DisableModeSwitch)
{
<div class="picker-control-switch">
<IconButton Clicked="ChangeMode" Icon="@ImportExportIcon" Class="pa-1 me-n1"></IconButton>
<GlyphButton Clicked="ChangeMode" Glyph="@ImportExportIcon" Class="pa-1 me-n1"></GlyphButton>
</div>
}
</div>

View File

@ -126,9 +126,9 @@
@{ var groupStyle = new StyleBuilder().AddStyle(GroupStyle).AddStyle(GroupStyleFunc?.Invoke(g)).Build(); }
<td class="table-cell @groupClass" colspan="1000" style="background-color:var(--mud-palette-background-grey);@groupStyle">
<IconButton
<GlyphButton
Class="table-row-expander"
Icon="@(g.IsExpanded ? Icons.Material.Filled.ExpandMore : Icons.Material.Filled.ChevronRight)"
Glyph="@(g.IsExpanded ? Icons.Material.Filled.ExpandMore : Icons.Material.Filled.ChevronRight)"
Clicked="@(() => ToggleGroupExpansion(g))" />
@if (GroupedColumn.GroupTemplate == null)
@ -377,7 +377,7 @@
@if (column == null)
{
<Item xs="1" Class="d-flex">
<IconButton Class="remove-filter-button" Icon="@Icons.Material.Filled.Close" Clicked="@filter.RemoveFilter" Size="@Size.Small" Style="align-self:flex-end"></IconButton>
<GlyphButton Class="remove-filter-button" Glyph="@Icons.Material.Filled.Close" Clicked="@filter.RemoveFilter" Size="@Size.Small" Style="align-self:flex-end"></GlyphButton>
</Item>
<Item xs="4">
<Select T="string" Value="@f.Field" ValueChanged="@filter.FieldChanged" FullWidth="true" Label="Column" Dense="true" Margin="@Margin.Dense"

View File

@ -20,10 +20,10 @@
@Info
</TextContent>
<div class="table-pagination-actions">
<IconButton Class="flip-x-rtl" Icon="@Icons.Material.Filled.FirstPage" Disabled="@BackButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.First))" />
<IconButton Class="flip-x-rtl" Icon="@Icons.Material.Filled.NavigateBefore" Disabled="@BackButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.Previous))" />
<IconButton Class="flip-x-rtl" Icon="@Icons.Material.Filled.NavigateNext" Disabled="@ForwardButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.Next))" />
<IconButton Class="flip-x-rtl" Icon="@Icons.Material.Filled.LastPage" Disabled="@ForwardButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.Last))" />
<GlyphButton Class="flip-x-rtl" Glyph="@Icons.Material.Filled.FirstPage" Disabled="@BackButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.First))" />
<GlyphButton Class="flip-x-rtl" Glyph="@Icons.Material.Filled.NavigateBefore" Disabled="@BackButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.Previous))" />
<GlyphButton Class="flip-x-rtl" Glyph="@Icons.Material.Filled.NavigateNext" Disabled="@ForwardButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.Next))" />
<GlyphButton Class="flip-x-rtl" Glyph="@Icons.Material.Filled.LastPage" Disabled="@ForwardButtonsDisabled" Clicked="@(() => DataGrid.NavigateTo(Page.Last))" />
</div>
</ToolBar>

View File

@ -58,7 +58,7 @@
}
}
</Menu>
<IconButton Class="align-self-center" Icon="@Icons.Material.Filled.FilterAltOff" Size="@Size.Small" Clicked="@ClearFilter"></IconButton>
<GlyphButton Class="align-self-center" Glyph="@Icons.Material.Filled.FilterAltOff" Size="@Size.Small" Clicked="@ClearFilter"></GlyphButton>
</Stack>
}
}

View File

@ -41,11 +41,11 @@ else if (Column != null && !Column.Hidden)
{
if (_initialDirection == SortDirection.None)
{
<IconButton Icon="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" Clicked="@SortChangedAsync"></IconButton>
<GlyphButton Glyph="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" Clicked="@SortChangedAsync"></GlyphButton>
}
else
{
<IconButton Icon="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" Clicked="@SortChangedAsync"></IconButton>
<GlyphButton Glyph="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" Clicked="@SortChangedAsync"></GlyphButton>
if(DataGrid.SortMode == SortMode.Multiple)
{
<span class="sort-index mud-text-disabled">@(Column.SortIndex + 1)</span>
@ -57,11 +57,11 @@ else if (Column != null && !Column.Hidden)
{
if (hasFilter)
{
<IconButton Class="filter-button filtered" Icon="@Icons.Material.Filled.FilterAlt" Size="@Size.Small" Clicked="@OpenFilters"></IconButton>
<GlyphButton Class="filter-button filtered" Glyph="@Icons.Material.Filled.FilterAlt" Size="@Size.Small" Clicked="@OpenFilters"></GlyphButton>
}
else if (showFilterIcon)
{
<IconButton Class="filter-button" Icon="@Icons.Material.Outlined.FilterAlt" Size="@Size.Small" Clicked="@AddFilter"></IconButton>
<GlyphButton Class="filter-button" Glyph="@Icons.Material.Outlined.FilterAlt" Size="@Size.Small" Clicked="@AddFilter"></GlyphButton>
}
}

View File

@ -6,8 +6,7 @@
Filterable="false">
<CellTemplate>
<label class="ma-n3">
<IconButton
Icon="@(context.openHierarchies.Contains(context.Item) ? OpenIcon : ClosedIcon)"
<GlyphButton Glyph="@(context.openHierarchies.Contains(context.Item) ? OpenIcon : ClosedIcon)"
OnClick="context.Actions.ToggleHierarchyVisibilityForItem"
Size="@IconSize"
Disabled="ButtonDisabledFunc.Invoke(context.Item)"/>

View File

@ -47,11 +47,11 @@
<div class="picker-calendar-header-switch">
@if (!FixYear.HasValue)
{
<IconButton aria-label="@prevLabel" Icon="@PreviousIcon" Clicked="OnPreviousYearClick" Class="flip-x-rtl" />
<GlyphButton aria-label="@prevLabel" Glyph="@PreviousIcon" Clicked="OnPreviousYearClick" Class="flip-x-rtl" />
<button type="button" class="picker-slide-transition mud-picker-calendar-header-transition" @onclick="OnYearClick" @onclick:stopPropagation="true">
<TextContent Typo="Typo.body1" Align="Align.Center">@calendarYear</TextContent>
</button>
<IconButton aria-label="@nextLabel" Icon="@NextIcon" Clicked="OnNextYearClick" Class="flip-x-rtl" />
<GlyphButton aria-label="@nextLabel" Glyph="@NextIcon" Clicked="OnNextYearClick" Class="flip-x-rtl" />
}
else
{
@ -77,11 +77,11 @@
<div class="picker-calendar-header-switch">
@if (!FixMonth.HasValue)
{
<IconButton aria-label="@prevLabel" Class="picker-nav-button-prev mud-flip-x-rtl" Icon="@PreviousIcon" Clicked="OnPreviousMonthClick" />
<GlyphButton aria-label="@prevLabel" Class="picker-nav-button-prev mud-flip-x-rtl" Glyph="@PreviousIcon" Clicked="OnPreviousMonthClick" />
<button type="button" class="picker-slide-transition mud-picker-calendar-header-transition mud-button-month" @onclick="(() => OnMonthClicked(tempMonth))" @onclick:stopPropagation="true">
<TextContent Typo="Typo.body1" Align="Align.Center">@GetMonthName(tempMonth)</TextContent>
</button>
<IconButton aria-label="@nextLabel" Class="picker-nav-button-next mud-flip-x-rtl" Icon="@NextIcon" Clicked="OnNextMonthClick" />
<GlyphButton aria-label="@nextLabel" Class="picker-nav-button-next mud-flip-x-rtl" Glyph="@NextIcon" Clicked="OnNextMonthClick" />
}
else
{

View File

@ -17,7 +17,7 @@
}
@if (CloseButton)
{
<IconButton aria-label="close" Icon="@CloseIcon" Clicked="Cancel" />
<GlyphButton aria-label="close" Glyph="@CloseIcon" Clicked="Cancel" />
}
</div>
}

View File

@ -27,6 +27,12 @@ public class DragAndDropIndexChangedEventArgs : EventArgs
#endregion
}
/// <summary>
/// Used to encapsulate data for a drag and drop transaction
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T"></typeparam>
public class DragAndDropItemTransaction<T>
{
#region Variables
@ -113,23 +119,8 @@ public class DragAndDropItemTransaction<T>
#endregion
#region Styling
#endregion
#region Behavior
#endregion
#region Lifecycle
#endregion
}
/// <summary>
/// Used to encapsulate data for a drag and drop transaction
/// </summary>
/// <typeparam name="T"></typeparam>
/// <summary>
/// Record encaplusalting data regaring a completed transaction
@ -142,6 +133,16 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
public class DragAndDropTransactionFinishedEventArgs<T> : EventArgs
{
#region Variables
public T Item { get; }
public bool Success { get; }
public string OriginatedDropzoneIdentifier { get; }
public string DestinationDropzoneIdentifier { get; }
public int OriginIndex { get; }
public int DestinationIndex { get; }
#endregion
#region Events
public DragAndDropTransactionFinishedEventArgs(DragAndDropItemTransaction<T> transaction) :
this(string.Empty, false, transaction)
{
@ -158,14 +159,9 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
DestinationIndex = transaction.Index;
}
public T Item { get; }
public bool Success { get; }
public string OriginatedDropzoneIdentifier { get; }
public string DestinationDropzoneIdentifier { get; }
public int OriginIndex { get; }
public int DestinationIndex { get; }
}
#endregion
}
/// <summary>
/// The container of a drag and drop zones
@ -173,13 +169,20 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
/// <typeparam name="T">Datetype of items</typeparam>
public partial class DropContainer<T> : UIComponent
{
#region Variables
private DragAndDropItemTransaction<T> _transaction;
#endregion
protected string Classname =>
new CssBuilder("drop-container")
.AddClass(AdditionalClassList)
.Build();
#region Events
public event EventHandler<DragAndDropItemTransaction<T>> TransactionStarted;
public event EventHandler<DragAndDropIndexChangedEventArgs> TransactionIndexChanged;
public event EventHandler<DragAndDropTransactionFinishedEventArgs<T>> TransactionEnded;
public event EventHandler RefreshRequested;
#endregion
#region Content
/// <summary>
/// Child content of component. This should include the drop zones
/// </summary>
@ -193,34 +196,14 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public IEnumerable<T> Items { get; set; }
#endregion
/// <summary>
/// The render fragment (template) that should be used to render the items within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public RenderFragment<T> ItemRenderer { get; set; }
#region Styling
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public Func<T, string, bool> ItemsSelector { get; set; }
/// <summary>
/// Callback that indicates that an item has been dropped on a drop zone. Should be used to update the "status" of the data item
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public EventCallback<ItemDropInfo<T>> ItemDropped { get; set; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public Func<T, string, bool> CanDrop { get; set; }
protected string Classname =>
new CssBuilder("drop-container")
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// The CSS class(es), that is applied to drop zones that are a valid target for drag and drop transaction
@ -236,13 +219,6 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
[Category(CategoryTypes.DropZone.DropRules)]
public string NoDropClass { get; set; }
/// <summary>
/// If true, drop classes CanDropClass <see cref="CanDropClass"/> or NoDropClass <see cref="NoDropClass"/> or applied as soon, as a transaction has started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public bool ApplyDropClassesOnDragStarted { get; set; } = false;
/// <summary>
/// The method is used to determinate if an item should be disabled for dragging. Defaults to allow all items
/// </summary>
@ -271,11 +247,43 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
[Category(CategoryTypes.DropZone.DraggingClass)]
public string ItemDraggingClass { get; set; }
public event EventHandler<DragAndDropItemTransaction<T>> TransactionStarted;
public event EventHandler<DragAndDropIndexChangedEventArgs> TransactionIndexChanged;
#endregion
public event EventHandler<DragAndDropTransactionFinishedEventArgs<T>> TransactionEnded;
public event EventHandler RefreshRequested;
#region Behavior
/// <summary>
/// Callback that indicates that an item has been dropped on a drop zone. Should be used to update the "status" of the data item
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public EventCallback<ItemDropInfo<T>> ItemDropped { get; set; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public Func<T, string, bool> CanDrop { get; set; }
/// <summary>
/// The render fragment (template) that should be used to render the items within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public RenderFragment<T> ItemRenderer { get; set; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public Func<T, string, bool> ItemsSelector { get; set; }
/// <summary>
/// If true, drop classes CanDropClass <see cref="CanDropClass"/> or NoDropClass <see cref="NoDropClass"/> or applied as soon, as a transaction has started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public bool ApplyDropClassesOnDragStarted { get; set; } = false;
public void StartTransaction(T item, string identifier, int index, Func<Task> commitCallback, Func<Task> cancelCallback)
{
@ -372,5 +380,6 @@ public record ItemDropInfo<T>(T Item, string DropzoneIdentifier, int IndexInZone
/// </summary>
public void Refresh() => RefreshRequested?.Invoke(this, EventArgs.Empty);
#endregion
}

View File

@ -11,12 +11,15 @@ namespace Connected.Components;
public partial class DropZone<T> : UIComponent, IDisposable
{
#region Variables
private bool _containerIsInitialized = false;
private bool _canDrop = false;
private bool _dragInProgress = false;
private bool _disposedValue = false;
private Guid _id = Guid.NewGuid();
private int _dragCounter = 0;
private Dictionary<T, int> _indicies = new();
[Inject] private IJSRuntime JsRuntime { get; set; }
@ -24,103 +27,9 @@ public partial class DropZone<T> : UIComponent, IDisposable
[CascadingParameter]
protected DropContainer<T> Container { get; set; }
/// <summary>
/// Child content of component
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// The unique identifier of this drop zone. It is used within transaction to
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public string Identifier { get; set; }
/// <summary>
/// The render fragment (template) that should be used to render the items within a drop zone. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public RenderFragment<T> ItemRenderer { get; set; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public Func<T, bool> ItemsSelector { get; set; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public Func<T, bool> CanDrop { get; set; }
/// <summary>
/// The CSS class(es), that is applied to drop zones that are a valid target for drag and drop transaction. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public string CanDropClass { get; set; }
/// <summary>
/// The CSS class(es), that is applied to drop zones that are NOT valid target for drag and drop transaction. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public string NoDropClass { get; set; }
/// <summary>
/// If true, drop classes CanDropClass <see cref="CanDropClass"/> or NoDropClass <see cref="NoDropClass"/> or applied as soon, as a transaction has started. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public bool? ApplyDropClassesOnDragStarted { get; set; }
/// <summary>
/// The method is used to determinate if an item should be disabled for dragging. Defaults to allow all items. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public Func<T, bool> ItemIsDisabled { get; set; }
/// <summary>
/// If a drop item is disabled (determinate by <see cref="ItemIsDisabled"/>). This class is applied to the element. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public string DisabledClass { get; set; }
/// <summary>
/// An additional class that is applied to the drop zone where a drag operation started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string DraggingClass { get; set; }
/// <summary>
/// An additional class that is applied to an drop item, when it is dragged
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string ItemDraggingClass { get; set; }
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public bool AllowReorder { get; set; }
/// <summary>
/// If true, will only act as a dropable zone and not render any items.
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public bool OnlyZone { get; set; }
#region view helper
#endregion
#region Events
private int GetItemIndex(T item)
{
if (_indicies.ContainsKey(item) == false)
@ -178,52 +87,6 @@ public partial class DropZone<T> : UIComponent, IDisposable
return result;
}
protected string Classname =>
new CssBuilder("drop-zone")
//.AddClass("drop-zone-drag-block", Container?.TransactionInProgress() == true && Container.GetTransactionOrignZoneIdentiifer() != Identifier)
.AddClass(CanDropClass ?? Container?.CanDropClass, Container?.TransactionInProgress() == true && Container.GetTransactionOrignZoneIdentiifer() != Identifier && _canDrop == true && (_dragCounter > 0 || GetApplyDropClassesOnDragStarted() == true))
.AddClass(NoDropClass ?? Container?.NoDropClass, Container?.TransactionInProgress() == true && Container.GetTransactionOrignZoneIdentiifer() != Identifier && _canDrop == false && (_dragCounter > 0 || GetApplyDropClassesOnDragStarted() == true))
.AddClass(GetDragginClass(), _dragInProgress == true)
.AddClass(AdditionalClassList)
.Build();
protected string PlaceholderClassname =>
new CssBuilder("border-2 mud-border-primary border-dashed mud-chip-text mud-chip-color-primary pa-4 mud-dropitem-placeholder")
.AddClass("d-none", AllowReorder == false || (Container?.TransactionInProgress() == false || Container.GetTransactionCurrentZoneIdentiifer() != Identifier))
.Build();
#endregion
#region helper
private (T, bool) ItemCanBeDropped()
{
if (Container == null || Container.TransactionInProgress() == false)
{
return (default(T), false);
}
var item = Container.GetTransactionItem();
var result = true;
if (CanDrop != null)
{
result = CanDrop(item);
}
else if (Container.CanDrop != null)
{
result = Container.CanDrop(item, Identifier);
}
return (item, result);
}
private bool IsOrign(int index) => Container.IsOrign(index, Identifier);
#endregion
#region container event handling
private void Container_TransactionEnded(object sender, DragAndDropTransactionFinishedEventArgs<T> e)
{
_dragCounter = 0;
@ -271,12 +134,6 @@ public partial class DropZone<T> : UIComponent, IDisposable
InvokeAsync(StateHasChanged);
}
#endregion
#region handling event callbacks
private int _dragCounter = 0;
private void HandleDragEnter()
{
_dragCounter++;
@ -370,9 +227,160 @@ public partial class DropZone<T> : UIComponent, IDisposable
private void FinishedDragOperation() => _dragInProgress = false;
private void DragOperationStarted() => _dragInProgress = true;
private void Container_TransactionIndexChanged(object sender, DragAndDropIndexChangedEventArgs e)
{
if (e.ZoneIdentifier != Identifier && e.OldZoneIdentifier != Identifier) { return; }
StateHasChanged();
}
#endregion
#region life cycle
#region Content
/// <summary>
/// Child content of component
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// The unique identifier of this drop zone. It is used within transaction to
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public string Identifier { get; set; }
/// <summary>
/// The render fragment (template) that should be used to render the items within a drop zone. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public RenderFragment<T> ItemRenderer { get; set; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public Func<T, bool> ItemsSelector { get; set; }
#endregion
#region Styling
/// <summary>
/// The CSS class(es), that is applied to drop zones that are a valid target for drag and drop transaction. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public string CanDropClass { get; set; }
/// <summary>
/// The CSS class(es), that is applied to drop zones that are NOT valid target for drag and drop transaction. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public string NoDropClass { get; set; }
/// <summary>
/// The method is used to determinate if an item should be disabled for dragging. Defaults to allow all items. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public Func<T, bool> ItemIsDisabled { get; set; }
/// <summary>
/// If a drop item is disabled (determinate by <see cref="ItemIsDisabled"/>). This class is applied to the element. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public string DisabledClass { get; set; }
/// <summary>
/// An additional class that is applied to the drop zone where a drag operation started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string DraggingClass { get; set; }
/// <summary>
/// An additional class that is applied to an drop item, when it is dragged
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string ItemDraggingClass { get; set; }
protected string Classname =>
new CssBuilder("drop-zone")
//.AddClass("drop-zone-drag-block", Container?.TransactionInProgress() == true && Container.GetTransactionOrignZoneIdentiifer() != Identifier)
.AddClass(CanDropClass ?? Container?.CanDropClass, Container?.TransactionInProgress() == true && Container.GetTransactionOrignZoneIdentiifer() != Identifier && _canDrop == true && (_dragCounter > 0 || GetApplyDropClassesOnDragStarted() == true))
.AddClass(NoDropClass ?? Container?.NoDropClass, Container?.TransactionInProgress() == true && Container.GetTransactionOrignZoneIdentiifer() != Identifier && _canDrop == false && (_dragCounter > 0 || GetApplyDropClassesOnDragStarted() == true))
.AddClass(GetDragginClass(), _dragInProgress == true)
.AddClass(AdditionalClassList)
.Build();
protected string PlaceholderClassname =>
new CssBuilder("border-2 mud-border-primary border-dashed mud-chip-text mud-chip-color-primary pa-4 mud-dropitem-placeholder")
.AddClass("d-none", AllowReorder == false || (Container?.TransactionInProgress() == false || Container.GetTransactionCurrentZoneIdentiifer() != Identifier))
.Build();
#endregion
#region Behavior
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public Func<T, bool> CanDrop { get; set; }
/// <summary>
/// If true, drop classes CanDropClass <see cref="CanDropClass"/> or NoDropClass <see cref="NoDropClass"/> or applied as soon, as a transaction has started. Overrides value provided by drop container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public bool? ApplyDropClassesOnDragStarted { get; set; }
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public bool AllowReorder { get; set; }
/// <summary>
/// If true, will only act as a dropable zone and not render any items.
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public bool OnlyZone { get; set; }
private (T, bool) ItemCanBeDropped()
{
if (Container == null || Container.TransactionInProgress() == false)
{
return (default(T), false);
}
var item = Container.GetTransactionItem();
var result = true;
if (CanDrop != null)
{
result = CanDrop(item);
}
else if (Container.CanDrop != null)
{
result = Container.CanDrop(item, Identifier);
}
return (item, result);
}
private bool IsOrign(int index) => Container.IsOrign(index, Identifier);
#endregion
#region Lifecycle
protected override void OnParametersSet()
{
@ -388,12 +396,6 @@ public partial class DropZone<T> : UIComponent, IDisposable
base.OnParametersSet();
}
private void Container_TransactionIndexChanged(object sender, DragAndDropIndexChangedEventArgs e)
{
if (e.ZoneIdentifier != Identifier && e.OldZoneIdentifier != Identifier) { return; }
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{

View File

@ -11,39 +11,12 @@ namespace Connected.Components;
public partial class DynamicDropItem<T> : UIComponent
{
#region Variables
private bool _dragOperationIsInProgress = false;
#endregion
[CascadingParameter]
protected DropContainer<T> Container { get; set; }
/// <summary>
/// The zone identifier of the corresponding drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public string ZoneIdentifier { get; set; }
/// <summary>
/// the data item that is represneted by this item
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public T Item { get; set; }
/// <summary>
/// Child content of component
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// An additional class that is applied to this element when a drag operation is in progress
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string DraggingClass { get; set; }
#region Events
/// <summary>
/// An event callback set fires, when a drag operation has been started
/// </summary>
@ -58,30 +31,6 @@ public partial class DynamicDropItem<T> : UIComponent
[Category(CategoryTypes.DropZone.Behavior)]
public EventCallback<T> OnDragEnded { get; set; }
/// <summary>
/// When true, the item can't be dragged. defaults to false
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public bool Disabled { get; set; } = false;
/// <summary>
/// The class that is applied when disabled <see cref="Disabled"/> is set to true
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public string DisabledClass { get; set; }
[Parameter]
[Category(CategoryTypes.DropZone.Sorting)]
public int Index { get; set; } = -1;
[Parameter]
[Category(CategoryTypes.DropZone.Sorting)]
public bool HideContent { get; set; }
#region Event handling and callbacks
private async Task DragStarted()
{
if (Container == null) { return; }
@ -133,6 +82,58 @@ public partial class DynamicDropItem<T> : UIComponent
#endregion
#region Content
[CascadingParameter]
protected DropContainer<T> Container { get; set; }
/// <summary>
/// The zone identifier of the corresponding drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public string ZoneIdentifier { get; set; }
/// <summary>
/// the data item that is represneted by this item
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Behavior)]
public T Item { get; set; }
/// <summary>
/// Child content of component
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
/// <summary>
/// An additional class that is applied to this element when a drag operation is in progress
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string DraggingClass { get; set; }
/// <summary>
/// When true, the item can't be dragged. defaults to false
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public bool Disabled { get; set; } = false;
/// <summary>
/// The class that is applied when disabled <see cref="Disabled"/> is set to true
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public string DisabledClass { get; set; }
[Parameter]
[Category(CategoryTypes.DropZone.Sorting)]
public int Index { get; set; } = -1;
protected string Classname =>
new CssBuilder("drop-item")
.AddClass(DraggingClass, _dragOperationIsInProgress == true)
@ -140,4 +141,13 @@ public partial class DynamicDropItem<T> : UIComponent
.AddClass(AdditionalClassList)
.Build();
#endregion
#region Behavior
[Parameter]
[Category(CategoryTypes.DropZone.Sorting)]
public bool HideContent { get; set; }
#endregion
}

View File

@ -15,7 +15,7 @@
</div>
@if (!HideIcon)
{
<Icon Icon="@Icon" class="@(IsExpanded? "expand-panel-icon mud-transform" : "expand-panel-icon")" />
<Icon Glyph="@Icon" class="@(IsExpanded? "expand-panel-icon mud-transform" : "expand-panel-icon")" />
}
</div>
<Collapse Expanded="@_collapseIsExpanded" MaxHeight="@MaxHeight">

View File

@ -6,142 +6,14 @@ namespace Connected.Components;
public partial class ExpansionPanel : UIComponent, IDisposable
{
#region Variables
private bool _nextPanelExpanded;
private bool _isExpanded;
private bool _collapseIsExpanded;
#endregion
[CascadingParameter] private ExpansionPanels Parent { get; set; }
protected string Classname =>
new CssBuilder("expand-panel")
.AddClass("panel-expanded", IsExpanded)
.AddClass("panel-next-expanded", NextPanelExpanded)
.AddClass("disabled", Disabled)
.AddClass($"elevation-{Parent?.Elevation.ToString()}")
.AddClass($"expand-panel-border", Parent?.DisableBorders != true)
.AddClass(AdditionalClassList)
.Build();
protected string PanelContentClassname =>
new CssBuilder("expand-panel-content")
.AddClass("expand-panel-gutters", DisableGutters || Parent?.DisableGutters == true)
.AddClass("expand-panel-dense", Dense || Parent?.Dense == true)
.Build();
/// <summary>
/// Explicitly sets the height for the Collapse element to override the css default.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public int? MaxHeight { get; set; }
/// <summary>
/// RenderFragment to be displayed in the expansion panel which will override header text if defined.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public RenderFragment TitleContent { get; set; }
/// <summary>
/// The text to be displayed in the expansion panel.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public string Text { get; set; }
/// <summary>
/// If true, expand icon will not show
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool HideIcon { get; set; }
/// <summary>
/// Custom hide icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public string Icon { get; set; } = Icons.Material.Filled.ExpandMore;
/// <summary>
/// If true, removes vertical padding from childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed from childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool DisableGutters { get; set; }
/// <summary>
/// Raised when IsExpanded changes.
/// </summary>
[Parameter] public EventCallback<bool> IsExpandedChanged { get; set; }
internal event Action<ExpansionPanel> NotifyIsExpandedChanged;
/// <summary>
/// Expansion state of the panel (two-way bindable)
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool IsExpanded
{
get => _isExpanded;
set
{
if (_isExpanded == value)
return;
_isExpanded = value;
NotifyIsExpandedChanged?.Invoke(this);
IsExpandedChanged.InvokeAsync(_isExpanded).ContinueWith(t =>
{
if (_collapseIsExpanded != _isExpanded)
{
_collapseIsExpanded = _isExpanded;
InvokeAsync(() => StateHasChanged());
}
});
}
}
/// <summary>
/// Sets the initial expansion state. Do not use in combination with IsExpanded.
/// Combine with MultiExpansion to have more than one panel start open.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool IsInitiallyExpanded { get; set; }
/// <summary>
/// If true, the component will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public RenderFragment ChildContent { get; set; }
public bool NextPanelExpanded
{
get => _nextPanelExpanded;
set
{
if (_nextPanelExpanded == value)
return;
_nextPanelExpanded = value;
//InvokeAsync(StateHasChanged);
}
}
#region Events
public void ToggleExpansion()
{
@ -177,6 +49,152 @@ public partial class ExpansionPanel : UIComponent, IDisposable
}
}
/// <summary>
/// Raised when IsExpanded changes.
/// </summary>
[Parameter] public EventCallback<bool> IsExpandedChanged { get; set; }
internal event Action<ExpansionPanel> NotifyIsExpandedChanged;
#endregion
#region Content
[CascadingParameter] private ExpansionPanels Parent { get; set; }
/// <summary>
/// RenderFragment to be displayed in the expansion panel which will override header text if defined.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public RenderFragment TitleContent { get; set; }
/// <summary>
/// The text to be displayed in the expansion panel.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public string Text { get; set; }
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("expand-panel")
.AddClass("panel-expanded", IsExpanded)
.AddClass("panel-next-expanded", NextPanelExpanded)
.AddClass("disabled", Disabled)
.AddClass($"elevation-{Parent?.Elevation.ToString()}")
.AddClass($"expand-panel-border", Parent?.DisableBorders != true)
.AddClass(AdditionalClassList)
.Build();
protected string PanelContentClassname =>
new CssBuilder("expand-panel-content")
.AddClass("expand-panel-gutters", DisableGutters || Parent?.DisableGutters == true)
.AddClass("expand-panel-dense", Dense || Parent?.Dense == true)
.Build();
/// <summary>
/// Explicitly sets the height for the Collapse element to override the css default.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public int? MaxHeight { get; set; }
/// <summary>
/// If true, expand icon will not show
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool HideIcon { get; set; }
/// <summary>
/// Custom hide icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public string Icon { get; set; } = Icons.Material.Filled.ExpandMore;
/// <summary>
/// If true, removes vertical padding from childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed from childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool DisableGutters { get; set; }
/// <summary>
/// Expansion state of the panel (two-way bindable)
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool IsExpanded
{
get => _isExpanded;
set
{
if (_isExpanded == value)
return;
_isExpanded = value;
NotifyIsExpandedChanged?.Invoke(this);
IsExpandedChanged.InvokeAsync(_isExpanded).ContinueWith(t =>
{
if (_collapseIsExpanded != _isExpanded)
{
_collapseIsExpanded = _isExpanded;
InvokeAsync(() => StateHasChanged());
}
});
}
}
/// <summary>
/// If true, the component will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool Disabled { get; set; }
#endregion
#region Behavior
/// <summary>
/// Sets the initial expansion state. Do not use in combination with IsExpanded.
/// Combine with MultiExpansion to have more than one panel start open.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool IsInitiallyExpanded { get; set; }
public bool NextPanelExpanded
{
get => _nextPanelExpanded;
set
{
if (_nextPanelExpanded == value)
return;
_nextPanelExpanded = value;
//InvokeAsync(StateHasChanged);
}
}
#endregion
#region Lifecycle
protected override void OnInitialized()
{
// NOTE: we can't throw here because we need to be able to instanciate the type for the API Docs to infer default values
@ -196,4 +214,6 @@ public partial class ExpansionPanel : UIComponent, IDisposable
{
Parent?.RemovePanel(this);
}
#endregion
}

View File

@ -7,63 +7,8 @@ namespace Connected.Components;
public partial class ExpansionPanels : UIComponent
{
protected string Classname =>
new CssBuilder("expansion-panels")
.AddClass($"expansion-panels-square", Square)
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// If true, border-radius is set to 0.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool Square { get; set; }
/// <summary>
/// If true, multiple panels can be expanded at the same time.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool MultiExpansion { get; set; }
/// <summary>
/// The higher the number, the heavier the drop-shadow. 0 for no shadow.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public int Elevation { set; get; } = 1;
/// <summary>
/// If true, removes vertical padding from all panels' childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed from all panels' childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool DisableGutters { get; set; }
/// <summary>
/// If true, the borders around each panel will be removed.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool DisableBorders { get; set; }
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public RenderFragment ChildContent { get; set; }
private List<ExpansionPanel> _panels = new();
#region Events
internal void AddPanel(ExpansionPanel panel)
{
if (MultiExpansion == false && _panels.Any(p => p.IsExpanded))
@ -109,13 +54,6 @@ public partial class ExpansionPanels : UIComponent
StateHasChanged();
}
[Obsolete("Use CollapseAllExcept instead.")]
[ExcludeFromCodeCoverage]
public void CloseAllExcept(ExpansionPanel panel)
{
CollapseAllExcept(panel);
}
/// <summary>
/// Collapses all panels except the given one.
/// </summary>
@ -154,4 +92,69 @@ public partial class ExpansionPanels : UIComponent
}
this.InvokeAsync(UpdateAll);
}
#endregion
#region Content
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public RenderFragment ChildContent { get; set; }
private List<ExpansionPanel> _panels = new();
#endregion
#region Styling
protected string Classname =>
new CssBuilder("expansion-panels")
.AddClass($"expansion-panels-square", Square)
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// If true, border-radius is set to 0.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool Square { get; set; }
/// <summary>
/// The higher the number, the heavier the drop-shadow. 0 for no shadow.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public int Elevation { set; get; } = 1;
/// <summary>
/// If true, removes vertical padding from all panels' childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed from all panels' childcontent.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool DisableGutters { get; set; }
/// <summary>
/// If true, the borders around each panel will be removed.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Appearance)]
public bool DisableBorders { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, multiple panels can be expanded at the same time.
/// </summary>
[Parameter]
[Category(CategoryTypes.ExpansionPanel.Behavior)]
public bool MultiExpansion { get; set; }
#endregion
}

View File

@ -9,6 +9,53 @@ namespace Connected.Components;
//TODO Maybe can inherit from MudBaseInput?
public partial class Field : UIComponent
{
#region Events
/// <summary>
/// Button click event if set and Adornment used.
/// </summary>
[Parameter] public EventCallback<MouseEventArgs> OnAdornmentClick { get; set; }
#endregion
#region Content
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Data)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// The ErrorText that will be displayed if Error true
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Validation)]
public string ErrorText { get; set; }
/// <summary>
/// The HelperText will be displayed below the text field.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Behavior)]
public string HelperText { get; set; }
/// <summary>
/// If string has value the label text will be displayed in the input, and scaled down at the top if the field has value.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Behavior)]
public string Label { get; set; }
/// <summary>
/// Text that will be used if Adornment is set to Start or End, the Text overrides Glyph.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Behavior)]
public string AdornmentText { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("input")
.AddClass($"input-{Variant.ToDescription()}")
@ -41,12 +88,7 @@ public partial class Field : UIComponent
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Data)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Will adjust vertical spacing.
@ -62,20 +104,6 @@ public partial class Field : UIComponent
[Category(CategoryTypes.Field.Validation)]
public bool Error { get; set; }
/// <summary>
/// The ErrorText that will be displayed if Error true
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Validation)]
public string ErrorText { get; set; }
/// <summary>
/// The HelperText will be displayed below the text field.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Behavior)]
public string HelperText { get; set; }
/// <summary>
/// If true, the field will take up the full width of its container.
/// </summary>
@ -83,13 +111,6 @@ public partial class Field : UIComponent
[Category(CategoryTypes.Field.Appearance)]
public bool FullWidth { get; set; }
/// <summary>
/// If string has value the label text will be displayed in the input, and scaled down at the top if the field has value.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Behavior)]
public string Label { get; set; }
/// <summary>
/// Variant can be Text, Filled or Outlined.
/// </summary>
@ -111,13 +132,6 @@ public partial class Field : UIComponent
[Category(CategoryTypes.Field.Behavior)]
public string AdornmentIcon { get; set; }
/// <summary>
/// Text that will be used if Adornment is set to Start or End, the Text overrides Glyph.
/// </summary>
[Parameter]
[Category(CategoryTypes.Field.Behavior)]
public string AdornmentText { get; set; }
/// <summary>
/// The Adornment if used. By default, it is set to None.
/// </summary>
@ -139,11 +153,6 @@ public partial class Field : UIComponent
[Category(CategoryTypes.Field.Appearance)]
public Size IconSize { get; set; } = Size.Medium;
/// <summary>
/// Button click event if set and Adornment used.
/// </summary>
[Parameter] public EventCallback<MouseEventArgs> OnAdornmentClick { get; set; }
/// <summary>
/// If true, the inner contents padding is removed.
/// </summary>
@ -157,4 +166,6 @@ public partial class Field : UIComponent
[Parameter]
[Category(CategoryTypes.Field.Appearance)]
public bool DisableUnderLine { get; set; }
#endregion
}

View File

@ -12,32 +12,11 @@ namespace Connected.Components;
public partial class FileUpload<T> : FormComponent<T, string>
{
public FileUpload() : base(new DefaultConverter<T>()) { }
#region Variables
private readonly string _id = $"mud_fileupload_{Guid.NewGuid()}";
#endregion
protected string Classname =>
new CssBuilder("file-upload")
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// The value of the MudFileUpload component.
/// If T is <see cref="IBrowserFile">IBrowserFile</see>, it represents a single file.
/// If T is <see cref="IReadOnlyCollection{IBrowserFile}">IReadOnlyList&lt;IBrowserFile&gt;</see>, it represents multiple files
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public T Files
{
get => _value;
set
{
if (_value != null && _value.Equals(value))
return;
_value = value;
}
}
#region Events
/// <summary>
/// Triggered when the internal OnChange event fires
/// </summary>
@ -51,48 +30,6 @@ public partial class FileUpload<T> : FormComponent<T, string>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public EventCallback<InputFileChangeEventArgs> OnFilesChanged { get; set; }
/// <summary>
/// Renders the button that triggers the input. Required for functioning.
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public RenderFragment<string> ButtonTemplate { get; set; }
/// <summary>
/// Renders the selected files, if desired.
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public RenderFragment<T> SelectedTemplate { get; set; }
/// <summary>
/// If true, OnFilesChanged will not trigger if validation fails
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public bool SuppressOnChangeWhenInvalid { get; set; }
/// <summary>
/// Sets the file types this input will accept
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public string Accept { get; set; }
/// <summary>
/// If false, the inner FileInput will be visible
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public bool Hidden { get; set; } = true;
/// <summary>
/// Css classes to apply to the internal InputFile
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public string InputClass { get; set; }
/// <summary>
/// Style to apply to the internal InputFile
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public string InputStyle { get; set; }
private async Task OnChange(InputFileChangeEventArgs args)
{
@ -112,6 +49,86 @@ public partial class FileUpload<T> : FormComponent<T, string>
if (!HasError || !SuppressOnChangeWhenInvalid) //only trigger FilesChanged if validation passes or SuppressOnChangeWhenInvalid is false
await OnFilesChanged.InvokeAsync(args);
}
#endregion
#region Content
/// <summary>
/// The value of the MudFileUpload component.
/// If T is <see cref="IBrowserFile">IBrowserFile</see>, it represents a single file.
/// If T is <see cref="IReadOnlyCollection{IBrowserFile}">IReadOnlyList&lt;IBrowserFile&gt;</see>, it represents multiple files
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public T Files
{
get => _value;
set
{
if (_value != null && _value.Equals(value))
return;
_value = value;
}
}
#endregion
#region Styling
protected string Classname =>
new CssBuilder("file-upload")
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// Renders the button that triggers the input. Required for functioning.
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public RenderFragment<string> ButtonTemplate { get; set; }
/// <summary>
/// Renders the selected files, if desired.
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public RenderFragment<T> SelectedTemplate { get; set; }
/// <summary>
/// If false, the inner FileInput will be visible
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public bool Hidden { get; set; } = true;
/// <summary>
/// Css classes to apply to the internal InputFile
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public string InputClass { get; set; }
/// <summary>
/// Style to apply to the internal InputFile
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Appearance)]
public string InputStyle { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, OnFilesChanged will not trigger if validation fails
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public bool SuppressOnChangeWhenInvalid { get; set; }
/// <summary>
/// Sets the file types this input will accept
/// </summary>
[Parameter]
[Category(CategoryTypes.FileUpload.Behavior)]
public string Accept { get; set; }
#endregion
#region Lifecycle
public FileUpload() : base(new DefaultConverter<T>()) { }
protected override void OnInitialized()
{
@ -120,4 +137,7 @@ public partial class FileUpload<T> : FormComponent<T, string>
base.OnInitialized();
}
#endregion
}

View File

@ -7,11 +7,7 @@ namespace Connected.Components;
public partial class FocusTrap : IDisposable
{
protected string Classname =>
new CssBuilder("outline-none")
.AddClass(AdditionalClassList)
.Build();
#region Variables
protected ElementReference _firstBumper;
protected ElementReference _lastBumper;
protected ElementReference _fallback;
@ -21,50 +17,10 @@ public partial class FocusTrap : IDisposable
private bool _disabled;
private bool _initialized;
/// <summary>
/// Child content of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.FocusTrap.Behavior)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// If true, the focus will no longer loop inside the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.FocusTrap.Behavior)]
public bool Disabled
{
get => _disabled;
set
{
if (_disabled != value)
{
_disabled = value;
_initialized = false;
}
}
}
/// <summary>
/// Defines on which element to set the focus when the component is created or enabled.
/// When DefaultFocus.Element is used, the focus will be set to the FocusTrap itself, so the user will have to press TAB key once to focus the first tabbable element.
/// </summary>
[Parameter]
[Category(CategoryTypes.FocusTrap.Behavior)]
public DefaultFocus DefaultFocus { get; set; } = DefaultFocus.FirstChild;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
await SaveFocusAsync();
if (!_initialized)
await InitializeFocusAsync();
}
bool _shouldRender = true;
#endregion
#region Events
private Task OnBottomFocusAsync(FocusEventArgs args)
{
return FocusLastAsync();
@ -142,9 +98,50 @@ public partial class FocusTrap : IDisposable
{
return _root.SaveFocusAsync().AsTask();
}
#endregion
bool _shouldRender = true;
#region Content
/// <summary>
/// Child content of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.FocusTrap.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("outline-none")
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// Defines on which element to set the focus when the component is created or enabled.
/// When DefaultFocus.Element is used, the focus will be set to the FocusTrap itself, so the user will have to press TAB key once to focus the first tabbable element.
/// </summary>
[Parameter]
[Category(CategoryTypes.FocusTrap.Behavior)]
public DefaultFocus DefaultFocus { get; set; } = DefaultFocus.FirstChild;
#endregion
#region Behavior
/// <summary>
/// If true, the focus will no longer loop inside the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.FocusTrap.Behavior)]
public bool Disabled
{
get => _disabled;
set
{
if (_disabled != value)
{
_disabled = value;
_initialized = false;
}
}
}
protected override bool ShouldRender()
{
if (_shouldRender)
@ -153,9 +150,25 @@ public partial class FocusTrap : IDisposable
return false;
}
#endregion
#region Lifecycle
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
await SaveFocusAsync();
if (!_initialized)
await InitializeFocusAsync();
}
public void Dispose()
{
if (!_disabled)
RestoreFocusAsync().AndForget(TaskOption.Safe);
}
#endregion
}

View File

@ -6,37 +6,23 @@ namespace Connected.Components;
public partial class Form : UIComponent, IDisposable, IForm
{
protected string Classname =>
new CssBuilder("form")
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidatedData)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Validation status. True if the form is valid and without errors. This parameter is two-way bindable.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidationResult)]
public bool IsValid
{
get => _valid && ChildForms.All(x => x.IsValid);
set
{
_valid = value;
}
}
#region Variables
// Note: w/o any children the form is automatically valid.
// It stays valid, as long as non-required fields are added or
// a required field is added or the user touches a field that fails validation.
private bool _valid = true;
private bool _touched = false;
protected HashSet<IFormComponent> _formControls = new();
protected HashSet<string> _errors = new();
private Timer _timer;
private bool _shouldRender = true; // <-- default is true, we need the form children to render
#endregion
#region Events
private void SetIsValid(bool value)
{
if (IsValid == value)
@ -45,47 +31,6 @@ public partial class Form : UIComponent, IDisposable, IForm
IsValidChanged.InvokeAsync(IsValid).AndForget();
}
// Note: w/o any children the form is automatically valid.
// It stays valid, as long as non-required fields are added or
// a required field is added or the user touches a field that fails validation.
/// <summary>
/// True if any field of the field was touched. This parameter is readonly.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public bool IsTouched { get => _touched; set {/* readonly parameter! */ } }
private bool _touched = false;
/// <summary>
/// Validation debounce delay in milliseconds. This can help improve rendering performance of forms with real-time validation of inputs
/// i.e. when textfields have ChangeTextImmediately="true".
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public int ValidationDelay { get; set; } = 300;
/// <summary>
/// When true, the form will not re-render its child contents on validation updates (i.e. when IsValid changes).
/// This is an optimization which can be necessary especially for larger forms on older devices.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public bool SuppressRenderingOnValidation { get; set; } = false;
/// <summary>
/// When true, will not cause a page refresh on Enter if any input has focus.
/// </summary>
/// <remarks>
/// https://www.w3.org/TR/2018/SPSD-html5-20180327/forms.html#implicit-submission
/// Usually this is not wanted, as it can cause a page refresh in the middle of editing a form.
/// When the form is in a dialog this will cause the dialog to close. So by default we suppress it.
/// </remarks>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public bool SuppressImplicitSubmission { get; set; } = true;
/// <summary>
/// Raised when IsValid changes.
/// </summary>
@ -101,60 +46,8 @@ public partial class Form : UIComponent, IDisposable, IForm
/// </summary>
[Parameter] public EventCallback<FormFieldChangedEventArgs> FieldChanged { get; set; }
// keeps track of validation. if the input was validated at least once the value will be true
protected HashSet<IFormComponent> _formControls = new();
protected HashSet<string> _errors = new();
/// <summary>
/// A default validation func or a validation attribute to use for form controls that don't have one.
/// Supported types are:
/// <para>Func&lt;T, bool&gt; ... will output the standard error message "Invalid" if false</para>
/// <para>Func&lt;T, string&gt; ... outputs the result as error message, no error if null </para>
/// <para>Func&lt;T, IEnumerable&lt; string &gt;&gt; ... outputs all the returned error messages, no error if empty</para>
/// <para>Func&lt;object, string, IEnumerable&lt; string &gt;&gt; input Form.Model, Full Path of Member ... outputs all the returned error messages, no error if empty</para>
/// <para>Func&lt;T, Task&lt; bool &gt;&gt; ... will output the standard error message "Invalid" if false</para>
/// <para>Func&lt;T, Task&lt; string &gt;&gt; ... outputs the result as error message, no error if null</para>
/// <para>Func&lt;T, Task&lt;IEnumerable&lt; string &gt;&gt;&gt; ... outputs all the returned error messages, no error if empty</para>
/// <para>Func&lt;object, string, Task&lt;IEnumerable&lt; string &gt;&gt;&gt; input Form.Model, Full Path of Member ... outputs all the returned error messages, no error if empty</para>
/// <para>System.ComponentModel.DataAnnotations.ValidationAttribute instances</para>
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public object Validation { get; set; }
/// <summary>
/// If a field already has a validation, override it with <see cref="Validation"/>.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public bool? OverrideFieldValidation { get; set; }
/// <summary>
/// Validation error messages.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidationResult)]
public string[] Errors
{
get => _errors.ToArray();
set { /* readonly */ }
}
[Parameter] public EventCallback<string[]> ErrorsChanged { get; set; }
/// <summary>
/// Specifies the top-level model object for the form. Used with Fluent Validation
/// </summary>
#nullable enable
[Parameter]
[Category(CategoryTypes.Form.ValidatedData)]
public object? Model { get; set; }
#nullable disable
private HashSet<Form> ChildForms { get; set; } = new HashSet<Form>();
[CascadingParameter] private Form ParentForm { get; set; }
void IForm.FieldChanged(IFormComponent formControl, object newValue)
{
FieldChanged.InvokeAsync(new FormFieldChangedEventArgs { Field = formControl, NewValue = newValue }).AndForget();
@ -173,8 +66,6 @@ public partial class Form : UIComponent, IDisposable, IForm
_formControls.Remove(formControl);
}
private Timer _timer;
/// <summary>
/// Called by any input of the form to signal that its value changed.
/// </summary>
@ -195,8 +86,6 @@ public partial class Form : UIComponent, IDisposable, IForm
private void OnTimerComplete(object stateInfo) => InvokeAsync(OnEvaluateForm);
private bool _shouldRender = true; // <-- default is true, we need the form children to render
protected async Task OnEvaluateForm()
{
_errors.Clear();
@ -282,7 +171,143 @@ public partial class Form : UIComponent, IDisposable, IForm
EvaluateForm(debounce: false);
}
private void SetDefaultControlValidation(IFormComponent formComponent)
{
if (Validation == null) return;
if (!formComponent.IsForNull && (formComponent.Validation == null || (OverrideFieldValidation ?? true)))
{
formComponent.Validation = Validation;
}
}
#endregion
#region Content
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidatedData)]
public RenderFragment ChildContent { get; set; }
private HashSet<Form> ChildForms { get; set; } = new HashSet<Form>();
[CascadingParameter] private Form ParentForm { get; set; }
// keeps track of validation. if the input was validated at least once the value will be true
/// <summary>
/// A default validation func or a validation attribute to use for form controls that don't have one.
/// Supported types are:
/// <para>Func&lt;T, bool&gt; ... will output the standard error message "Invalid" if false</para>
/// <para>Func&lt;T, string&gt; ... outputs the result as error message, no error if null </para>
/// <para>Func&lt;T, IEnumerable&lt; string &gt;&gt; ... outputs all the returned error messages, no error if empty</para>
/// <para>Func&lt;object, string, IEnumerable&lt; string &gt;&gt; input Form.Model, Full Path of Member ... outputs all the returned error messages, no error if empty</para>
/// <para>Func&lt;T, Task&lt; bool &gt;&gt; ... will output the standard error message "Invalid" if false</para>
/// <para>Func&lt;T, Task&lt; string &gt;&gt; ... outputs the result as error message, no error if null</para>
/// <para>Func&lt;T, Task&lt;IEnumerable&lt; string &gt;&gt;&gt; ... outputs all the returned error messages, no error if empty</para>
/// <para>Func&lt;object, string, Task&lt;IEnumerable&lt; string &gt;&gt;&gt; input Form.Model, Full Path of Member ... outputs all the returned error messages, no error if empty</para>
/// <para>System.ComponentModel.DataAnnotations.ValidationAttribute instances</para>
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public object Validation { get; set; }
/// <summary>
/// Validation error messages.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidationResult)]
public string[] Errors
{
get => _errors.ToArray();
set { /* readonly */ }
}
/// <summary>
/// Specifies the top-level model object for the form. Used with Fluent Validation
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidatedData)]
public object? Model { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("form")
.AddClass(AdditionalClassList)
.Build();
#endregion
#region Behavior
// Note: w/o any children the form is automatically valid.
// It stays valid, as long as non-required fields are added or
// a required field is added or the user touches a field that fails validation.
/// <summary>
/// Validation debounce delay in milliseconds. This can help improve rendering performance of forms with real-time validation of inputs
/// i.e. when textfields have ChangeTextImmediately="true".
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public int ValidationDelay { get; set; } = 300;
/// <summary>
/// Validation status. True if the form is valid and without errors. This parameter is two-way bindable.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.ValidationResult)]
public bool IsValid
{
get => _valid && ChildForms.All(x => x.IsValid);
set
{
_valid = value;
}
}
/// <summary>
/// True if any field of the field was touched. This parameter is readonly.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public bool IsTouched { get => _touched; set {/* readonly parameter! */ } }
/// <summary>
/// When true, the form will not re-render its child contents on validation updates (i.e. when IsValid changes).
/// This is an optimization which can be necessary especially for larger forms on older devices.
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public bool SuppressRenderingOnValidation { get; set; } = false;
/// <summary>
/// When true, will not cause a page refresh on Enter if any input has focus.
/// </summary>
/// <remarks>
/// https://www.w3.org/TR/2018/SPSD-html5-20180327/forms.html#implicit-submission
/// Usually this is not wanted, as it can cause a page refresh in the middle of editing a form.
/// When the form is in a dialog this will cause the dialog to close. So by default we suppress it.
/// </remarks>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]
public bool SuppressImplicitSubmission { get; set; } = true;
/// <summary>
/// If a field already has a validation, override it with <see cref="Validation"/>.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public bool? OverrideFieldValidation { get; set; }
#endregion
#region Lifecycle
protected override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
@ -299,16 +324,6 @@ public partial class Form : UIComponent, IDisposable, IForm
return base.OnAfterRenderAsync(firstRender);
}
private void SetDefaultControlValidation(IFormComponent formComponent)
{
if (Validation == null) return;
if (!formComponent.IsForNull && (formComponent.Validation == null || (OverrideFieldValidation ?? true)))
{
formComponent.Validation = Validation;
}
}
protected override void OnInitialized()
{
if (ParentForm != null)
@ -323,4 +338,21 @@ public partial class Form : UIComponent, IDisposable, IForm
{
_timer?.Dispose();
}
#endregion
}

View File

@ -6,13 +6,7 @@ namespace Connected.Components;
public partial class Grid
{
#region Event callbacks
#endregion
#region Content placeholders
#endregion
#region Styling properties
#region Styling
private CssBuilder CompiledClassList
{
@ -42,8 +36,5 @@ public partial class Grid
#endregion
#region Lifecycle
#endregion
}

View File

@ -5,10 +5,7 @@ namespace Connected.Components;
public partial class Item
{
#region Event callbacks
#endregion
#region Content placeholders
#region Content
[CascadingParameter]
private Grid Parent { get; set; }
@ -25,7 +22,7 @@ public partial class Item
#endregion
#region Styling properties
#region Styling
private CssBuilder CompiledClassList
{
@ -50,7 +47,7 @@ public partial class Item
#endregion
#region Lifecycle events
#region Lifecycle
protected override void OnInitialized()
{
@ -58,19 +55,8 @@ public partial class Item
//if (Parent == null)
// throw new ArgumentNullException(nameof(Parent), "Item must exist within a Grid");
base.OnInitialized();
}
#endregion
// ToDo false,auto,true on all sizes.
}
}

View File

@ -7,61 +7,24 @@ namespace Connected.Components;
public partial class Hidden : UIComponent, IAsyncDisposable
{
#region Variables
private Breakpoint _currentBreakpoint = Breakpoint.None;
private bool _serviceIsReady = false;
private Guid _breakpointServiceSubscriptionId;
private bool _isHidden = true;
[Inject] public IBreakpointService BreakpointService { get; set; }
[CascadingParameter] public Breakpoint CurrentBreakpointFromProvider { get; set; } = Breakpoint.None;
/// <summary>
/// The screen size(s) depending on which the ChildContent should not be rendered (or should be, if Invert is true)
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public Breakpoint Breakpoint { get; set; }
/// <summary>
/// Inverts the Breakpoint, so that the ChildContent is only rendered when the breakpoint matches the screen size.
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public bool Invert { get; set; }
private bool _isHidden = true;
/// <summary>
/// True if the component is not visible (two-way bindable)
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public bool IsHidden
{
get => _isHidden;
set
{
if (_isHidden != value)
{
_isHidden = value;
IsHiddenChanged.InvokeAsync(_isHidden);
}
}
}
#endregion
#region Events
/// <summary>
/// Fires when the breakpoint changes visibility of the component
/// </summary>
[Parameter] public EventCallback<bool> IsHiddenChanged { get; set; }
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public RenderFragment ChildContent { get; set; }
protected void Update(Breakpoint currentBreakpoint)
{
if (CurrentBreakpointFromProvider != Breakpoint.None)
@ -82,7 +45,54 @@ public partial class Hidden : UIComponent, IAsyncDisposable
IsHidden = hidden;
}
#endregion
#region Content
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
/// <summary>
/// The screen size(s) depending on which the ChildContent should not be rendered (or should be, if Invert is true)
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public Breakpoint Breakpoint { get; set; }
/// <summary>
/// Inverts the Breakpoint, so that the ChildContent is only rendered when the breakpoint matches the screen size.
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public bool Invert { get; set; }
/// <summary>
/// True if the component is not visible (two-way bindable)
/// </summary>
[Parameter]
[Category(CategoryTypes.Hidden.Behavior)]
public bool IsHidden
{
get => _isHidden;
set
{
if (_isHidden != value)
{
_isHidden = value;
IsHiddenChanged.InvokeAsync(_isHidden);
}
}
}
#endregion
#region Lifecycle
protected override void OnParametersSet()
{
base.OnParametersSet();
@ -115,4 +125,6 @@ public partial class Hidden : UIComponent, IAsyncDisposable
}
public async ValueTask DisposeAsync() => await BreakpointService.Unsubscribe(_breakpointServiceSubscriptionId);
#endregion
}

View File

@ -10,9 +10,12 @@ namespace Connected.Components;
public partial class Highlighter : UIComponent
{
#region Variables
private Memory<string> _fragments;
private string _regex;
#endregion
#region Content
/// <summary>
/// The whole text in which a fragment will be highlighted
/// </summary>
@ -33,27 +36,34 @@ public partial class Highlighter : UIComponent
[Parameter]
[Category(CategoryTypes.Highlighter.Behavior)]
public IEnumerable<string> HighlightedTexts { get; set; }
#endregion
#region Styling
/// <summary>
/// Whether or not the highlighted text is case sensitive
/// </summary>
[Parameter]
[Category(CategoryTypes.Highlighter.Behavior)]
public bool CaseSensitive { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, highlights the text until the next regex boundary
/// </summary>
[Parameter]
[Category(CategoryTypes.Highlighter.Behavior)]
public bool UntilNextBoundary { get; set; }
public bool HighlightUntilNextBoundary { get; set; }
#endregion
#region Lifecycle
//TODO
//Accept regex highlightings
// [Parameter] public bool IsRegex { get; set; }
protected override void OnParametersSet()
{
_fragments = GetFragments(Text, HighlightedText, HighlightedTexts, out _regex, CaseSensitive, UntilNextBoundary);
_fragments = GetFragments(Text, HighlightedText, HighlightedTexts, out _regex, CaseSensitive, HighlightUntilNextBoundary);
}
#endregion
}

View File

@ -7,6 +7,24 @@ namespace Connected.Components;
public partial class Icon : UIComponent
{
#region Content
/// <summary>
/// GlyphTitle of the icon used for accessibility.
/// </summary>
[Parameter]
[Category(CategoryTypes.Icon.Behavior)]
public string Title { get; set; }
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Icon.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("icon-root")
.AddClass($"icon-default", Color == ThemeColor.Default)
@ -23,13 +41,6 @@ public partial class Icon : UIComponent
[Category(CategoryTypes.Icon.Behavior)]
public string Glyph { get; set; }
/// <summary>
/// GlyphTitle of the icon used for accessibility.
/// </summary>
[Parameter]
[Category(CategoryTypes.Icon.Behavior)]
public string Title { get; set; }
/// <summary>
/// The Size of the icon.
/// </summary>
@ -50,11 +61,7 @@ public partial class Icon : UIComponent
[Parameter]
[Category(CategoryTypes.Icon.Behavior)]
public string ViewBox { get; set; } = "0 0 24 24";
#endregion
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Icon.Behavior)]
public RenderFragment ChildContent { get; set; }
}

View File

@ -2,7 +2,6 @@
// MudBlazor licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Connected.Annotations;
using Connected.Extensions;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
@ -11,7 +10,39 @@ namespace Connected.Components;
public partial class Image : UIComponent
{
#region Content
/// <summary>
/// Specifies the path to the image.
/// </summary>
[Parameter]
public string Src { get; set; }
/// <summary>
/// Specifies an alternate text for the image.
/// </summary>
[Parameter]
public string Alt { get; set; }
#endregion
#region Styling
/// <summary>
/// Specifies the height of the image in px.
/// </summary>
[Parameter]
public int? Height { get; set; }
/// <summary>
/// Specifies the width of the image in px.
/// </summary>
[Parameter]
public int? Width { get; set; }
/// <summary>
/// The higher the number, the heavier the drop-shadow.
/// </summary>
[Parameter]
public int Elevation { set; get; }
private CssBuilder CompiledClassList
{
get
@ -31,36 +62,6 @@ public partial class Image : UIComponent
[Parameter]
public bool Fluid { get; set; }
/// <summary>
/// Specifies the path to the image.
/// </summary>
[Parameter]
public string Src { get; set; }
/// <summary>
/// Specifies an alternate text for the image.
/// </summary>
[Parameter]
public string Alt { get; set; }
/// <summary>
/// Specifies the height of the image in px.
/// </summary>
[Parameter]
public int? Height { get; set; }
/// <summary>
/// Specifies the width of the image in px.
/// </summary>
[Parameter]
public int? Width { get; set; }
/// <summary>
/// The higher the number, the heavier the drop-shadow.
/// </summary>
[Parameter]
public int Elevation { set; get; }
/// <summary>
/// Controls how the image should be resized.
/// </summary>
@ -78,4 +79,6 @@ public partial class Image : UIComponent
/// </summary>
[Parameter]
public string? ClassList { get; set; }
#endregion
}

View File

@ -2,28 +2,11 @@
@typeparam T
@inherits InputBase<T>
<CascadingValue Name="SubscribeToParentForm" Value="@base.SubscribeToParentForm" IsFixed="true">
<div class="@CompiledWrapperClass.Build()">
<InputControl Label="@Label"
Variant="@Variant"
HelperText="@HelperText"
HelperTextOnFocus="@HelperTextOnFocus"
CounterText="@GetCounterText()"
FullWidth="@FullWidth"
class="@CompiledHelperContainerClassList.Build()"
Error="@HasErrors"
ErrorText="@GetErrorText()"
ErrorId="@ErrorId"
Disabled="@Disabled"
Margin="@Margin"
Required="@Required"
ForId="@FieldId">
<InputContent>
<CascadingValue Name="SubscribeToParentForm" Value="false" IsFixed="true">
<div class="@WrapperClassList" style="@Style">
@if (Adornment == Adornment.Start)
{
<InputAdornment Class="@CompiledAdornmentClass.Build()"
<InputAdornment Class="@AdornmentClassList"
Icon="@AdornmentIcon"
Color="@AdornmentColor"
Size="@IconSize"
@ -34,12 +17,11 @@
/>
}
@if (NumberOfLines > 1)
@if (Lines > 1)
{
<textarea class="@CompiledInputClass.Build()"
<textarea class="@InputClassList"
@ref="ElementReference"
rows="@NumberOfLines"
@attributes="@Attributes"
rows="@Lines"
type="@InputTypeString"
placeholder="@Placeholder"
disabled=@Disabled
@ -59,7 +41,7 @@
@onkeyup:preventDefault="@KeyUpPreventDefault"
@onmousewheel="@OnMouseWheel"
@onwheel="@OnMouseWheel"
aria-invalid="@HasError.ToString().ToLower()"
aria-invalid="@GetErrorText()"
aria-describedby="@ErrorId"
>
@Text
@ -69,13 +51,12 @@
}
else
{
<input class="@CompiledInputClass.Build()"
<input class="@InputClassList"
@ref="ElementReference"
@attributes="@Attributes"
step="@Step"
type="@InputTypeString"
value="@_internalText"
@oninput="OnInput"
step="@Step"
@onchange="OnChange"
placeholder="@Placeholder"
disabled=@Disabled
@ -92,7 +73,7 @@
@onkeyup:preventDefault="@KeyUpPreventDefault"
@onmousewheel="@OnMouseWheel"
@onwheel="@OnMouseWheel"
aria-invalid="@HasError.ToString().ToLower()"
aria-invalid="@GetErrorText()"
aria-describedby="@ErrorId"
/>
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
@ -101,7 +82,7 @@
@*Note: this div must always be there to avoid crashes in WASM, but it is hidden most of the time except if ChildContent should be shown.
In Disabled state the tabindex attribute must NOT be set at all or else it will get focus on click
*@
<div class="@CompiledInputClass.Build()"
<div class="@InputClassList"
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
@onblur="@OnBlurred" @ref="@_elementReference1"
>
@ -111,7 +92,7 @@
else
{
@*Note: this div must always be there to avoid crashes in WASM, but it is hidden most of the time except if ChildContent should be shown.*@
<div class="@CompiledInputClass.Build()"
<div class="@InputClassList"
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
tabindex="@(InputType == InputType.Hidden && ChildContent != null ? 0 : -1)"
@onblur="@OnBlurred" @ref="@_elementReference1"
@ -123,16 +104,17 @@
@if (_showClearable && !Disabled)
{
<IconButton ClassList="@CompiledClearButtonClassList.Build()"
Icon="@ClearIcon"
<GlyphButton ClassList="@ClearButtonClassList"
Glyph="@ClearIcon"
Clicked="@ClearButtonClickHandlerAsync"
aria-label="Clear"
tabindex="-1"
/>
}
@if (Adornment == Adornment.End)
{
<InputAdornment Class="@CompiledAdornmentClass.Build()"
<InputAdornment Class="@AdornmentClassList"
Icon="@AdornmentIcon"
Color="@AdornmentColor"
Size="@IconSize"
@ -145,25 +127,18 @@
@if (Variant == Variant.Outlined)
{
<div class="input-outlined-border"></div>
<div class="mud-input-outlined-border"></div>
}
@if (!HideSpinButtons)
{
<div class="input-numeric-spin">
<Button Variant="Variant.Text" Clicked="OnIncrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon Icon="@NumericUpIcon" Size="@GetButtonSize()" />
<div class="mud-input-numeric-spin">
<Button Variant="Variant.Text" @onclick="OnIncrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon aria-label="Increment" Glyph="@NumericUpIcon" Size="@GetButtonSize()" />
</Button>
<Button Variant="Variant.Text" Clicked="OnDecrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon Icon="@NumericDownIcon" Size="@GetButtonSize()" />
<Button Variant="Variant.Text" @onclick="OnDecrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon aria-label="Decrement" Glyph="@NumericDownIcon" Size="@GetButtonSize()" />
</Button>
</div>
}
</CascadingValue>
</InputContent>
</InputControl>
</div>
</CascadingValue>

View File

@ -45,6 +45,7 @@ public partial class Input<T> : InputBase<T>
// This method is called when Value property needs to be refreshed from the current Text property, so typically because Text property has changed.
// We want to debounce only text-input, not a value being set, so the debouncing is only done when updateText==false (because that indicates the
// change came from a Text setter)
if (updateText)
{
// we have a change coming not from the Text setter, no debouncing is needed
@ -72,11 +73,36 @@ public partial class Input<T> : InputBase<T>
}
protected Task OnInput(ChangeEventArgs args)
{
var input = args.Value.ToString();
/*if (InputType==InputType.Number)
{
if (!Helper.IsNumeric(input))
{
input = Regex.Replace(input, "[^0-9.]", "");
Text = input;
UpdateValuePropertyAsync(true);
}
}*/
if (!ChangeTextImmediately)
return Task.CompletedTask;
_isFocused = true;
return SetTextAsync(args?.Value as string);
return SetTextAsync(input);
}
protected virtual void OnKeyDown(KeyboardEventArgs obj)
{
if (InputType == InputType.Number)
{
if (Helper.IsNumeric(obj.Key))
{
_isFocused = true;
base.OnKeyDown.InvokeAsync(obj).AndForget();
}
} else
{
_isFocused = true;
base.OnKeyDown.InvokeAsync(obj).AndForget();
}
}
protected async Task OnChange(ChangeEventArgs args)
@ -95,7 +121,6 @@ public partial class Input<T> : InputBase<T>
await SetTextAsync(args?.Value as string);
}
}
}
/// <summary>
@ -178,8 +203,6 @@ public partial class Input<T> : InputBase<T>
#endregion
#region Style properties
/// <summary>
@ -209,25 +232,52 @@ public partial class Input<T> : InputBase<T>
[Parameter] public bool Clearable { get; set; } = false;
#region Wrapper class
/*[Parameter]
public string WrapperClass { get; set; } = string.Empty;*/
/*protected string WrapperClass => InputCssHelper.GetClassname(this,
() => HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder));*/
[Parameter]
public string WrapperClass { get; set; } = string.Empty;
protected CssBuilder CompiledWrapperClass
public string ClassList { get; set; } = string.Empty;
protected string WrapperClassList
{
get
{
return new CssBuilder("input")
/*return new CssBuilder("input")
.AddClass($"input-{Variant.ToDescription()}")
.AddClass($"input-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass($"input-underline", when: () => DisableUnderLine == false && Variant != Variant.Outlined)
.AddClass($"shrink", when: HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder))
.AddClass($"disabled", Disabled)
.AddClass($"input-error", HasErrors)
.AddClass($"ltr", GetInputType() == InputType.Email || GetInputType() == InputType.Telephone)
.AddClass(WrapperClass)
.Build();*/
var clasList = new CssBuilder("input")
.AddClass($"input-{Variant.ToDescription()}")
.AddClass($"input-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass("input-underline", when: () => DisableUnderLine == false && Variant != Variant.Outlined)
.AddClass("shrink", when: HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder))
//.AddClass("shrink", when: () => HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder))
.AddClass("disabled", Disabled)
.AddClass("input-error", HasErrors)
.AddClass("ltr", GetInputType() == InputType.Email || GetInputType() == InputType.Telephone)
.AddClass(WrapperClass);
.AddClass(ClassList)
.Build();
return clasList;
}
}
/// <summary>
/// User styles, applied on top of the component's own classes and styles.
/// </summary>
[Parameter]
public string Style { get; set; }
#endregion
@ -235,8 +285,8 @@ public partial class Input<T> : InputBase<T>
#region Input field class
[Parameter]
public string Class { get; set; } = string.Empty;
protected CssBuilder CompiledInputClass
public string InputClass { get; set; } = string.Empty;
protected string InputClassList
{
get
{
@ -245,7 +295,10 @@ public partial class Input<T> : InputBase<T>
.AddClass($"input-root-{Variant.ToDescription()}")
.AddClass($"input-root-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-root-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass(Class);
.AddClass(InputClass)
.Build();
//return new CssBuilder().AddClass(InputCssHelper.GetInputClassname(this)).Build();
}
}
#endregion
@ -253,7 +306,7 @@ public partial class Input<T> : InputBase<T>
#region Adornment class
[Parameter]
public string AdornmentClass { get; set; } = string.Empty;
protected CssBuilder CompiledAdornmentClass
protected string AdornmentClassList
{
get
{
@ -261,7 +314,10 @@ public partial class Input<T> : InputBase<T>
.AddClass($"input-adornment-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"text", !string.IsNullOrEmpty(AdornmentText))
.AddClass($"input-root-filled-shrink", Variant == Variant.Filled)
.AddClass(AdornmentClass);
.AddClass(AdornmentClass)
.Build();
//return new CssBuilder().AddClass(InputCssHelper.GetAdornmentClassname(this)).Build();
}
}
#endregion
@ -269,7 +325,7 @@ public partial class Input<T> : InputBase<T>
#region Clear icon class
[Parameter]
public string ClearButtonClass { get; set; } = string.Empty;
protected CssBuilder CompiledClearButtonClassList
protected string ClearButtonClassList
{
get
{
@ -277,8 +333,9 @@ public partial class Input<T> : InputBase<T>
.AddClass("me-n1", Adornment == Adornment.End && HideSpinButtons == false)
.AddClass("icon-button-edge-end", Adornment == Adornment.End && HideSpinButtons == true)
.AddClass("me-6", Adornment != Adornment.End && HideSpinButtons == false)
.AddClass("icon-button-edge-margin-end", Adornment != Adornment.End && HideSpinButtons == true)
.AddClass(ClearButtonClass);
.AddClass("micon-button-edge-margin-end", Adornment != Adornment.End && HideSpinButtons == true)
.AddClass(ClearButtonClass)
.Build();
}
}
#endregion
@ -290,7 +347,7 @@ public partial class Input<T> : InputBase<T>
/// </summary>
[Parameter]
public string? HelperContainerClass { get; set; }
protected CssBuilder CompiledHelperContainerClassList
protected string HelperContainerClassList
{
get
{
@ -298,7 +355,8 @@ public partial class Input<T> : InputBase<T>
.AddClass($"px-1", Variant == Variant.Filled)
.AddClass($"px-2", Variant == Variant.Outlined)
.AddClass($"px-1", Variant == Variant.Text)
.AddClass(HelperContainerClass);
.AddClass(HelperContainerClass)
.Build();
}
}
@ -314,7 +372,6 @@ public partial class Input<T> : InputBase<T>
#endregion
#region Content placeholders
/// <summary>
@ -336,6 +393,9 @@ public partial class Input<T> : InputBase<T>
private double _textChangeInterval;
[Parameter]
public int Lines { get; set; } = 1;
/// <summary>
/// Interval to be awaited in MILLISECONDS before changing the Text value
@ -423,7 +483,6 @@ public partial class Input<T> : InputBase<T>
#endregion
#region Lifecycle events
public override async Task SetParametersAsync(ParameterView parameters)
{
@ -460,5 +519,4 @@ public partial class Input<T> : InputBase<T>
}
#endregion
}

View File

@ -11,7 +11,7 @@
{
@if (AdornmentClick.HasDelegate)
{
<IconButton Icon="@Icon" Clicked="@AdornmentClick" Edge="@Edge" Size="@Size" Color="@Color" aria-label="@(!string.IsNullOrEmpty(AriaLabel) ? AriaLabel : "Icon Button")" tabindex="-1" />
<GlyphButton Glyph="@Icon" Clicked="@AdornmentClick" Edge="@Edge" Size="@Size" Color="@Color" aria-label="@(!string.IsNullOrEmpty(AriaLabel) ? AriaLabel : "Icon Button")" tabindex="-1" />
}
else
{

View File

@ -17,7 +17,7 @@
<input @ref="_elementReferenceStart" @attributes="CustomAttributes" type="@InputTypeString" class="@InputClassname" @bind-value="@TextStart" @bind-value:event="@((ChangeTextImmediately ? "oninput" : "onchange"))"
placeholder="@PlaceholderStart" disabled=@Disabled readonly="@ReadOnly" @onblur="@OnBlurred" @onkeydown="@InvokeKeyDown" @onkeypress="@InvokeKeyPress" @onkeyup="@InvokeKeyUp" inputmode="@InputMode.ToString()" pattern="@Pattern" />
<Icon Class="range-input-separator mud-flip-x-rtl" Icon="@SeparatorIcon" Color="@ThemeColor.Default" />
<Icon Class="range-input-separator mud-flip-x-rtl" Glyph="@SeparatorIcon" Color="@ThemeColor.Default" />
<input @ref="_elementReferenceEnd" @attributes="CustomAttributes" type="@InputTypeString" class="@InputClassname" @bind-value="@TextEnd" @bind-value:event="@((ChangeTextImmediately ? "oninput" : "onchange"))" inputmode="@InputMode.ToString()" pattern="@Pattern"
placeholder="@PlaceholderEnd" disabled=@Disabled readonly="@ReadOnly" @onblur="@OnBlurred" @onkeydown="@InvokeKeyDown" @onkeypress="@InvokeKeyPress" @onkeyup="@InvokeKeyUp" />

View File

@ -6,6 +6,45 @@ namespace Connected.Components;
public partial class InputControl : UIComponent
{
#region Content
/// <summary>
/// Child content of component.
/// </summary>
[Parameter] public RenderFragment ChildContent { get; set; }
/// <summary>
/// Should be the Input
/// </summary>
[Parameter] public RenderFragment InputContent { get; set; }
/// <summary>
/// The ErrorText that will be displayed if Error true
/// </summary>
[Parameter] public string ErrorText { get; set; }
/// <summary>
/// The HelperText will be displayed below the text field.
/// </summary>
[Parameter] public string HelperText { get; set; }
/// <summary>
/// The current character counter, displayed below the text field.
/// </summary>
[Parameter] public string CounterText { get; set; }
/// <summary>
/// If string has value the label text will be displayed in the input, and scaled down at the top if the input has value.
/// </summary>
[Parameter] public string Label { get; set; }
/// <summary>
/// If string has value the label "for" attribute will be added.
/// </summary>
[Parameter] public string ForId { get; set; } = string.Empty;
#endregion
#region Styling
protected string Classname =>
new CssBuilder("input-control")
.AddClass("input-required", when: () => Required)
@ -27,64 +66,16 @@ public partial class InputControl : UIComponent
.AddClass("input-error", Error)
.Build();
/// <summary>
/// Child content of component.
/// </summary>
[Parameter] public RenderFragment ChildContent { get; set; }
/// <summary>
/// Should be the Input
/// </summary>
[Parameter] public RenderFragment InputContent { get; set; }
/// <summary>
/// Will adjust vertical spacing.
/// </summary>
[Parameter] public Margin Margin { get; set; } = Margin.None;
/// <summary>
/// If true, will apply mud-input-required class to the output div
/// </summary>
[Parameter] public bool Required { get; set; }
/// <summary>
/// If true, the label will be displayed in an error state.
/// </summary>
[Parameter] public bool Error { get; set; }
/// <summary>
/// The ErrorText that will be displayed if Error true
/// </summary>
[Parameter] public string ErrorText { get; set; }
/// <summary>
/// The ErrorId that will be used by aria-describedby if Error true
/// </summary>
[Parameter] public string ErrorId { get; set; }
/// <summary>
/// The HelperText will be displayed below the text field.
/// </summary>
[Parameter] public string HelperText { get; set; }
/// <summary>
/// If true, the helper text will only be visible on focus.
/// </summary>
[Parameter] public bool HelperTextOnFocus { get; set; }
/// <summary>
/// The current character counter, displayed below the text field.
/// </summary>
[Parameter] public string CounterText { get; set; }
/// <summary>
/// If true, the input will take up the full width of its container.
/// </summary>
[Parameter] public bool FullWidth { get; set; }
/// <summary>
/// If string has value the label text will be displayed in the input, and scaled down at the top if the input has value.
/// </summary>
[Parameter] public string Label { get; set; }
/// <summary>
/// Variant can be Text, Filled or Outlined.
@ -95,9 +86,29 @@ public partial class InputControl : UIComponent
/// If true, the input element will be disabled.
/// </summary>
[Parameter] public bool Disabled { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, the label will be displayed in an error state.
/// </summary>
[Parameter] public bool Error { get; set; }
/// <summary>
/// If string has value the label "for" attribute will be added.
/// If true, will apply mud-input-required class to the output div
/// </summary>
[Parameter] public string ForId { get; set; } = string.Empty;
[Parameter] public bool Required { get; set; }
/// <summary>
/// If true, the helper text will only be visible on focus.
/// </summary>
[Parameter] public bool HelperTextOnFocus { get; set; }
/// <summary>
/// The ErrorId that will be used by aria-describedby if Error true
/// </summary>
[Parameter] public string ErrorId { get; set; }
#endregion
}

View File

@ -4,6 +4,7 @@ namespace Connected.Components;
public partial class Layout : DrawerContainer
{
#region Styling
protected override CssBuilder CompiledClassList
{
get
@ -22,8 +23,14 @@ public partial class Layout : DrawerContainer
}
}
#endregion
#region Lifecycle
public Layout()
{
Fixed = true;
}
#endregion
}

View File

@ -8,6 +8,45 @@ namespace Connected.Components;
public partial class Link : UIComponent
{
#region Events
/// <summary>
/// Link click event.
/// </summary>
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
protected async Task OnClickHandler(MouseEventArgs ev)
{
if (Disabled) return;
await OnClick.InvokeAsync(ev);
}
#endregion
#region Content
private Dictionary<string, object> Attributes
{
get => Disabled ? CustomAttributes : new Dictionary<string, object>(CustomAttributes)
{
{ "href", Href },
{ "target", Target }
};
}
/// <summary>
/// The URL, which is the actual link.
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public string Href { get; set; }
/// <summary>
/// The target attribute specifies where to open the link, if Link is specified. Possible values: _blank | _self | _parent | _top | <i>framename</i>
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public string Target { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("typography mud-link")
.AddClass($"{Color.ToDescription()}-text")
@ -20,14 +59,12 @@ public partial class Link : UIComponent
.AddClass(AdditionalClassList)
.Build();
private Dictionary<string, object> Attributes
{
get => Disabled ? CustomAttributes : new Dictionary<string, object>(CustomAttributes)
{
{ "href", Href },
{ "target", Target }
};
}
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// The color of the component. It supports the theme colors.
@ -50,42 +87,13 @@ public partial class Link : UIComponent
[Category(CategoryTypes.Link.Appearance)]
public Underline Underline { get; set; } = Underline.Hover;
/// <summary>
/// The URL, which is the actual link.
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public string Href { get; set; }
/// <summary>
/// The target attribute specifies where to open the link, if Link is specified. Possible values: _blank | _self | _parent | _top | <i>framename</i>
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public string Target { get; set; }
/// <summary>
/// Link click event.
/// </summary>
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
protected async Task OnClickHandler(MouseEventArgs ev)
{
if (Disabled) return;
await OnClick.InvokeAsync(ev);
}
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// If true, the navlink will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.Link.Behavior)]
public bool Disabled { get; set; }
#endregion
}

View File

@ -6,131 +6,30 @@ namespace Connected.Components;
public partial class List : UIComponent, IDisposable
{
protected string Classname =>
new CssBuilder("list")
.AddClass("list-padding", !DisablePadding)
.AddClass(AdditionalClassList)
.Build();
#region Variables
[CascadingParameter] protected List ParentList { get; set; }
/// <summary>
/// The color of the selected List Item.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public ThemeColor Color { get; set; } = ThemeColor.Primary;
private HashSet<ListItem> _items = new();
private HashSet<List> _childLists = new();
private ListItem _selectedItem;
private object _selectedValue;
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment ChildContent { get; set; }
internal bool CanSelect { get; private set; }
/// <summary>
/// Set true to make the list items clickable. This is also the precondition for list selection to work.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public bool Clickable { get; set; }
internal event Action ParametersChanged;
#endregion
/// <summary>
/// If true, vertical padding will be removed from the list.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisablePadding { get; set; }
/// <summary>
/// If true, compact vertical padding will be applied to all list items.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed on all list items.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableGutters { get; set; }
/// <summary>
/// If true, will disable the list item if it has onclick.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// The current selected list item.
/// Note: make the list Clickable for item selection to work.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public ListItem SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem == value)
return;
SetSelectedValue(_selectedItem?.Value, force: true);
}
}
#region Events
/// <summary>
/// Called whenever the selection changed
/// </summary>
[Parameter] public EventCallback<ListItem> SelectedItemChanged { get; set; }
/// <summary>
/// The current selected value.
/// Note: make the list Clickable for item selection to work.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public object SelectedValue
{
get => _selectedValue;
set
{
SetSelectedValue(value, force: true);
}
}
/// <summary>
/// Called whenever the selection changed
/// </summary>
[Parameter] public EventCallback<object> SelectedValueChanged { get; set; }
protected override void OnInitialized()
{
if (ParentList != null)
{
ParentList.Register(this);
CanSelect = ParentList.CanSelect;
}
else
{
CanSelect = SelectedItemChanged.HasDelegate || SelectedValueChanged.HasDelegate || SelectedValue != null;
}
}
internal event Action ParametersChanged;
protected override void OnParametersSet()
{
base.OnParametersSet();
ParametersChanged?.Invoke();
}
private HashSet<ListItem> _items = new();
private HashSet<List> _childLists = new();
private ListItem _selectedItem;
private object _selectedValue;
internal void Register(ListItem item)
{
_items.Add(item);
@ -182,13 +81,129 @@ public partial class List : UIComponent, IDisposable
SelectedItemChanged.InvokeAsync(_selectedItem).AndForget();
ParentList?.SetSelectedValue(value);
}
#endregion
internal bool CanSelect { get; private set; }
#region Content
/// <summary>
/// Child content of component.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// The current selected list item.
/// Note: make the list Clickable for item selection to work.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public ListItem SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem == value)
return;
SetSelectedValue(_selectedItem?.Value, force: true);
}
}
/// <summary>
/// The current selected value.
/// Note: make the list Clickable for item selection to work.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public object SelectedValue
{
get => _selectedValue;
set
{
SetSelectedValue(value, force: true);
}
}
#endregion
#region Styling
protected string Classname =>
new CssBuilder("list")
.AddClass("list-padding", !DisablePadding)
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// The color of the selected List Item.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public ThemeColor Color { get; set; } = ThemeColor.Primary;
/// <summary>
/// If true, vertical padding will be removed from the list.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisablePadding { get; set; }
/// <summary>
/// If true, compact vertical padding will be applied to all list items.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed on all list items.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableGutters { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, will disable the list item if it has onclick.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// Set true to make the list items clickable. This is also the precondition for list selection to work.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public bool Clickable { get; set; }
#endregion
#region Lifecycle
protected override void OnInitialized()
{
if (ParentList != null)
{
ParentList.Register(this);
CanSelect = ParentList.CanSelect;
}
else
{
CanSelect = SelectedItemChanged.HasDelegate || SelectedValueChanged.HasDelegate || SelectedValue != null;
}
}
protected override void OnParametersSet()
{
base.OnParametersSet();
ParametersChanged?.Invoke();
}
public void Dispose()
{
ParametersChanged = null;
ParentList?.Unregister(this);
}
#endregion
}

View File

@ -6,14 +6,14 @@
{
<div class="list-item-avatar">
<Avatar Class="@AvatarClass">
<Icon Icon="@Avatar" Color="@IconColor" Size="@IconSize" />
<Icon Glyph="@Avatar" Color="@IconColor" Size="@IconSize" />
</Avatar>
</div>
}
else if (!string.IsNullOrWhiteSpace(Icon))
{
<div class="list-item-icon">
<Icon Icon="@Icon" Color="@IconColor" Size="@IconSize" />
<Icon Glyph="@Icon" Color="@IconColor" Size="@IconSize" />
</div>
}
<div class="list-item-text @(Inset? "list-item-text-inset" : "")">
@ -30,7 +30,7 @@
</div>
@if (NestedList != null)
{
<Icon Icon="@($"{(_expanded ? ExpandLessIcon : ExpandMoreIcon)}")" Size="@IconSize" Color="@AdornmentColor" />
<Icon Glyph="@($"{(_expanded ? ExpandLessIcon : ExpandMoreIcon)}")" Size="@IconSize" Color="@AdornmentColor" />
}
</div>
@if (NestedList != null)

View File

@ -9,196 +9,23 @@ namespace Connected.Components;
public partial class ListItem : UIComponent, IDisposable
{
protected string Classname =>
new CssBuilder("list-item")
.AddClass("list-item-dense", (Dense ?? List?.Dense) ?? false)
.AddClass("list-item-gutters", !DisableGutters && !(List?.DisableGutters == true))
.AddClass("list-item-clickable", List?.Clickable)
.AddClass("ripple", List?.Clickable == true && !DisableRipple && !Disabled)
.AddClass($"selected-item mud-{List?.Color.ToDescription()}-text mud-{List?.Color.ToDescription()}-hover", _selected && !Disabled)
.AddClass("list-item-disabled", Disabled)
.AddClass(AdditionalClassList)
.Build();
#region Variables
[Inject] protected NavigationManager UriHelper { get; set; }
[CascadingParameter] protected List List { get; set; }
private bool _onClickHandlerPreventDefault = false;
/// <summary>
/// The text to display
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Text { get; set; }
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public object Value { get; set; }
/// <summary>
/// Avatar to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Avatar { get; set; }
/// <summary>
/// Link to a URL when clicked.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public string Href { get; set; }
/// <summary>
/// If true, force browser to redirect outside component router-space.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public bool ForceLoad { get; set; }
/// <summary>
/// Avatar CSS Class to apply if Avatar is set.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public string AvatarClass { get; set; }
private bool _disabled;
/// <summary>
/// If true, will disable the list item if it has onclick.
/// The value can be overridden by the parent list.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public bool Disabled
{
get => _disabled || (List?.Disabled ?? false);
set => _disabled = value;
}
/// <summary>
/// If true, disables ripple effect.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableRipple { get; set; }
/// <summary>
/// Glyph to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Icon { get; set; }
/// <summary>
/// The color of the icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
/// <summary>
/// Sets the Glyph Size.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public Size IconSize { get; set; } = Size.Medium;
/// <summary>
/// The color of the adornment if used. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public ThemeColor AdornmentColor { get; set; } = ThemeColor.Default;
/// <summary>
/// Custom expand less icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public string ExpandLessIcon { get; set; } = Icons.Material.Filled.ExpandLess;
/// <summary>
/// Custom expand more icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public string ExpandMoreIcon { get; set; } = Icons.Material.Filled.ExpandMore;
/// <summary>
/// If true, the List Subheader will be indented.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool Inset { get; set; }
/// <summary>
/// If true, compact vertical padding will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool? Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableGutters { get; set; }
/// <summary>
/// Expand or collapse nested list. Two-way bindable. Note: if you directly set this to
/// true or false (instead of using two-way binding) it will force the nested list's expansion state.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public bool Expanded
{
get => _expanded;
set
{
if (_expanded == value)
return;
_expanded = value;
_ = ExpandedChanged.InvokeAsync(value);
}
}
private bool _expanded;
private Typo _textTypo;
private bool _selected;
#endregion
#region Events
[Parameter]
public EventCallback<bool> ExpandedChanged { get; set; }
/// <summary>
/// If true, expands the nested list on first display
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public bool InitiallyExpanded { get; set; }
/// <summary>
/// Command parameter.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public object CommandParameter { get; set; }
/// <summary>
/// Command executed when the user clicks on an element.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public ICommand Command { get; set; }
/// <summary>
/// Display content of this list item. If set, this overrides Text
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment ChildContent { get; set; }
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public bool OnClickHandlerPreventDefault
@ -207,13 +34,6 @@ public partial class ListItem : UIComponent, IDisposable
set => _onClickHandlerPreventDefault = value;
}
/// <summary>
/// Add child list items here to create a nested list.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment NestedList { get; set; }
/// <summary>
/// List click event.
/// </summary>
@ -252,18 +72,6 @@ public partial class ListItem : UIComponent, IDisposable
}
}
protected override void OnInitialized()
{
_expanded = InitiallyExpanded;
if (List != null)
{
List.Register(this);
OnListParametersChanged();
List.ParametersChanged += OnListParametersChanged;
}
}
private Typo _textTypo;
private void OnListParametersChanged()
{
if ((Dense ?? List?.Dense) ?? false)
@ -277,8 +85,6 @@ public partial class ListItem : UIComponent, IDisposable
StateHasChanged();
}
private bool _selected;
internal void SetSelected(bool selected)
{
if (Disabled)
@ -289,6 +95,217 @@ public partial class ListItem : UIComponent, IDisposable
StateHasChanged();
}
#endregion
#region Content
/// <summary>
/// The text to display
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Text { get; set; }
[Parameter]
[Category(CategoryTypes.List.Selecting)]
public object Value { get; set; }
/// <summary>
/// Avatar to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Avatar { get; set; }
/// <summary>
/// Glyph to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Icon { get; set; }
/// <summary>
/// Custom expand less icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public string ExpandLessIcon { get; set; } = Icons.Material.Filled.ExpandLess;
/// <summary>
/// Custom expand more icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public string ExpandMoreIcon { get; set; } = Icons.Material.Filled.ExpandMore;
/// <summary>
/// Display content of this list item. If set, this overrides Text
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Add child list items here to create a nested list.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment NestedList { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("list-item")
.AddClass("list-item-dense", (Dense ?? List?.Dense) ?? false)
.AddClass("list-item-gutters", !DisableGutters && !(List?.DisableGutters == true))
.AddClass("list-item-clickable", List?.Clickable)
.AddClass("ripple", List?.Clickable == true && !DisableRipple && !Disabled)
.AddClass($"selected-item mud-{List?.Color.ToDescription()}-text mud-{List?.Color.ToDescription()}-hover", _selected && !Disabled)
.AddClass("list-item-disabled", Disabled)
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// Link to a URL when clicked.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public string Href { get; set; }
/// <summary>
/// Avatar CSS Class to apply if Avatar is set.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public string AvatarClass { get; set; }
/// <summary>
/// If true, will disable the list item if it has onclick.
/// The value can be overridden by the parent list.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public bool Disabled
{
get => _disabled || (List?.Disabled ?? false);
set => _disabled = value;
}
/// <summary>
/// If true, disables ripple effect.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableRipple { get; set; }
/// <summary>
/// The color of the icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
/// <summary>
/// Sets the Glyph Size.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public Size IconSize { get; set; } = Size.Medium;
/// <summary>
/// The color of the adornment if used. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public ThemeColor AdornmentColor { get; set; } = ThemeColor.Default;
/// <summary>
/// If true, the List Subheader will be indented.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool Inset { get; set; }
/// <summary>
/// If true, compact vertical padding will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool? Dense { get; set; }
/// <summary>
/// If true, the left and right padding is removed.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableGutters { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, force browser to redirect outside component router-space.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public bool ForceLoad { get; set; }
/// <summary>
/// Expand or collapse nested list. Two-way bindable. Note: if you directly set this to
/// true or false (instead of using two-way binding) it will force the nested list's expansion state.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public bool Expanded
{
get => _expanded;
set
{
if (_expanded == value)
return;
_expanded = value;
_ = ExpandedChanged.InvokeAsync(value);
}
}
/// <summary>
/// If true, expands the nested list on first display
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Expanding)]
public bool InitiallyExpanded { get; set; }
/// <summary>
/// Command parameter.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public object CommandParameter { get; set; }
/// <summary>
/// Command executed when the user clicks on an element.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.ClickAction)]
public ICommand Command { get; set; }
#endregion
#region Lifecycle
protected override void OnInitialized()
{
_expanded = InitiallyExpanded;
if (List != null)
{
List.Register(this);
OnListParametersChanged();
List.ParametersChanged += OnListParametersChanged;
}
}
public void Dispose()
{
try
@ -301,4 +318,7 @@ public partial class ListItem : UIComponent, IDisposable
catch (Exception) { /*ignore*/ }
}
#endregion
}

View File

@ -6,6 +6,7 @@ namespace Connected.Components;
public partial class ListSubheader : UIComponent
{
#region Styling
protected string Classname =>
new CssBuilder("list-subheader")
.AddClass("list-subheader-gutters", !DisableGutters)
@ -13,10 +14,6 @@ public partial class ListSubheader : UIComponent
.AddClass(AdditionalClassList)
.Build();
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment ChildContent { get; set; }
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool DisableGutters { get; set; }
@ -24,4 +21,13 @@ public partial class ListSubheader : UIComponent
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public bool Inset { get; set; }
#endregion
#region Content
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
}

View File

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class MainContent
{
#region Styling
private CssBuilder CompiledClassList
{
get
@ -13,12 +14,16 @@ public partial class MainContent
}
}
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// A space separated list of class names, added on top of the default class list.
/// </summary>
[Parameter]
public string? ClassList { get; set; }
#endregion
#region Content
[Parameter]
public RenderFragment ChildContent { get; set; }
#endregion
}

View File

@ -54,9 +54,9 @@
@if (_showClearable && !Disabled)
{
<IconButton Class="@ClearButtonClassname"
<GlyphButton Class="@ClearButtonClassname"
Color="@ThemeColor.Default"
Icon="@ClearIcon"
Glyph="@ClearIcon"
Size="@Size.Small"
Clicked="@HandleClearButton" />
}

View File

@ -14,52 +14,7 @@ namespace Connected.Components;
public partial class Mask : InputBase<string>, IDisposable
{
public Mask()
{
TextUpdateSuppression = false;
}
protected string Classname =>
new CssBuilder("input")
.AddClass($"input-{Variant.ToDescription()}")
.AddClass($"input-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass("input-underline", when: () => DisableUnderLine == false && Variant != Variant.Outlined)
.AddClass("shrink",
when: () => !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start ||
!string.IsNullOrWhiteSpace(Placeholder))
.AddClass("disabled", Disabled)
.AddClass("input-error", HasErrors)
.AddClass("ltr", GetInputType() == InputType.Email || GetInputType() == InputType.Telephone)
.AddClass(AdditionalClassList)
.Build();
protected string InputClassname =>
new CssBuilder("input-slot")
.AddClass("input-root")
.AddClass($"input-root-{Variant.ToDescription()}")
.AddClass($"input-root-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-root-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass(AdditionalClassList)
.Build();
protected string AdornmentClassname =>
new CssBuilder("input-adornment")
.AddClass($"input-adornment-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"text", !string.IsNullOrEmpty(AdornmentText))
.AddClass($"input-root-filled-shrink", Variant == Variant.Filled)
.AddClass(AdditionalClassList)
.Build();
protected string ClearButtonClassname =>
new CssBuilder()
// .AddClass("me-n1", Adornment == Adornment.End && HideSpinButtons == false)
.AddClass("icon-button-edge-end", Adornment == Adornment.End)
// .AddClass("me-6", Adornment != Adornment.End && HideSpinButtons == false)
.AddClass("icon-button-edge-margin-end", Adornment != Adornment.End)
.Build();
#region Variables
private ElementReference _elementReference;
private ElementReference _elementReference1;
private IJsEvent _jsEvent;
@ -74,40 +29,12 @@ public partial class Mask : InputBase<string>, IDisposable
private IMask _mask = new PatternMask("** **-** **");
/// <summary>
/// ChildContent will only be displayed if InputType.Hidden and if its not null. Required for Select
/// </summary>
[Parameter]
[Category(CategoryTypes.General.Appearance)]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Provide a masking object. Built-in masks are PatternMask, MultiMask, RegexMask and BlockMask
/// </summary>
[Parameter]
[Category(CategoryTypes.General.Data)]
public IMask MaskKind
{
get => _mask;
set => SetMask(value);
}
/// <summary>
/// Type of the input element. It should be a valid HTML5 input type.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.ListAppearance)]
public InputType InputType { get; set; } = InputType.Text;
/// <summary>
/// Show clear button.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.ListBehavior)]
public bool Clearable { get; set; } = false;
private bool _showClearable;
private bool _updating;
#endregion
#region Events
private void UpdateClearable(object value)
{
var showClearable = Clearable && !string.IsNullOrWhiteSpace(Text);
@ -121,67 +48,6 @@ public partial class Mask : InputBase<string>, IDisposable
[Parameter]
[Category(CategoryTypes.FormComponent.ListAppearance)]
public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
/// <summary>
/// Custom clear icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.General.Appearance)]
public string ClearIcon { get; set; } = Icons.Material.Filled.Clear;
protected override async Task OnInitializedAsync()
{
if (Text != MaskKind.Text)
await SetTextAsync(MaskKind.Text, updateValue: false);
await base.OnInitializedAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsEvent = _jsEventFactory.Create();
await _jsEvent.Connect(_elementId,
new JsEventOptions
{
//EnableLogging = true,
TargetClass = "input-slot",
TagName = "INPUT"
});
_jsEvent.CaretPositionChanged += OnCaretPositionChanged;
_jsEvent.Paste += OnPaste;
_jsEvent.Select += OnSelect;
_keyInterceptor = _keyInterceptorFactory.Create();
await _keyInterceptor.Connect(_elementId, new KeyInterceptorOptions()
{
//EnableLogging = true,
TargetClass = "input-slot",
Keys =
{
new KeyOptions
{
Key = " ", PreventDown = "key+none"
}, //prevent scrolling page, toggle open/close
new KeyOptions { Key = "ArrowUp", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = "ArrowDown", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = "PageUp", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = "PageDown", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = @"/^.$/", PreventDown = "key+none|key+shift" },
new KeyOptions { Key = "/./", SubscribeDown = true },
new KeyOptions { Key = "Backspace", PreventDown = "key+none" },
new KeyOptions { Key = "Delete", PreventDown = "key+none" },
},
});
_keyInterceptor.KeyDown += HandleKeyDownInternally;
}
if (_isFocused && MaskKind.Selection == null)
SetCaretPosition(MaskKind.CaretPos, _selection, render: false);
await base.OnAfterRenderAsync(firstRender);
}
private async void HandleKeyDownInternally(KeyboardEventArgs args)
{
await HandleKeyDown(args);
@ -224,8 +90,6 @@ public partial class Mask : InputBase<string>, IDisposable
}
}
private bool _updating;
private async Task Update()
{
var caret = MaskKind.CaretPos;
@ -417,6 +281,95 @@ public partial class Mask : InputBase<string>, IDisposable
MaskKind.Delete();
await Update();
}
#endregion
#region Content
/// <summary>
/// Custom clear icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.General.Appearance)]
public string ClearIcon { get; set; } = Icons.Material.Filled.Clear;
/// <summary>
/// ChildContent will only be displayed if InputType.Hidden and if its not null. Required for Select
/// </summary>
[Parameter]
[Category(CategoryTypes.General.Appearance)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
/// <summary>
/// Provide a masking object. Built-in masks are PatternMask, MultiMask, RegexMask and BlockMask
/// </summary>
[Parameter]
[Category(CategoryTypes.General.Data)]
public IMask MaskKind
{
get => _mask;
set => SetMask(value);
}
protected string Classname =>
new CssBuilder("input")
.AddClass($"input-{Variant.ToDescription()}")
.AddClass($"input-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass("input-underline", when: () => DisableUnderLine == false && Variant != Variant.Outlined)
.AddClass("shrink",
when: () => !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start ||
!string.IsNullOrWhiteSpace(Placeholder))
.AddClass("disabled", Disabled)
.AddClass("input-error", HasErrors)
.AddClass("ltr", GetInputType() == InputType.Email || GetInputType() == InputType.Telephone)
.AddClass(AdditionalClassList)
.Build();
protected string InputClassname =>
new CssBuilder("input-slot")
.AddClass("input-root")
.AddClass($"input-root-{Variant.ToDescription()}")
.AddClass($"input-root-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-root-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
.AddClass(AdditionalClassList)
.Build();
protected string AdornmentClassname =>
new CssBuilder("input-adornment")
.AddClass($"input-adornment-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"text", !string.IsNullOrEmpty(AdornmentText))
.AddClass($"input-root-filled-shrink", Variant == Variant.Filled)
.AddClass(AdditionalClassList)
.Build();
protected string ClearButtonClassname =>
new CssBuilder()
// .AddClass("me-n1", Adornment == Adornment.End && HideSpinButtons == false)
.AddClass("icon-button-edge-end", Adornment == Adornment.End)
// .AddClass("me-6", Adornment != Adornment.End && HideSpinButtons == false)
.AddClass("icon-button-edge-margin-end", Adornment != Adornment.End)
.Build();
#endregion
#region Behavior
/// <summary>
/// Show clear button.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.ListBehavior)]
public bool Clearable { get; set; } = false;
/// <summary>
/// Type of the input element. It should be a valid HTML5 input type.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.ListAppearance)]
public InputType InputType { get; set; } = InputType.Text;
#endregion
#region Lifecycle
protected override void Dispose(bool disposing)
{
@ -435,4 +388,63 @@ public partial class Mask : InputBase<string>, IDisposable
_keyInterceptor?.Dispose();
}
}
public Mask()
{
TextUpdateSuppression = false;
}
protected override async Task OnInitializedAsync()
{
if (Text != MaskKind.Text)
await SetTextAsync(MaskKind.Text, updateValue: false);
await base.OnInitializedAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsEvent = _jsEventFactory.Create();
await _jsEvent.Connect(_elementId,
new JsEventOptions
{
//EnableLogging = true,
TargetClass = "input-slot",
TagName = "INPUT"
});
_jsEvent.CaretPositionChanged += OnCaretPositionChanged;
_jsEvent.Paste += OnPaste;
_jsEvent.Select += OnSelect;
_keyInterceptor = _keyInterceptorFactory.Create();
await _keyInterceptor.Connect(_elementId, new KeyInterceptorOptions()
{
//EnableLogging = true,
TargetClass = "input-slot",
Keys =
{
new KeyOptions
{
Key = " ", PreventDown = "key+none"
}, //prevent scrolling page, toggle open/close
new KeyOptions { Key = "ArrowUp", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = "ArrowDown", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = "PageUp", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = "PageDown", PreventDown = "key+none" }, // prevent scrolling page
new KeyOptions { Key = @"/^.$/", PreventDown = "key+none|key+shift" },
new KeyOptions { Key = "/./", SubscribeDown = true },
new KeyOptions { Key = "Backspace", PreventDown = "key+none" },
new KeyOptions { Key = "Delete", PreventDown = "key+none" },
},
});
_keyInterceptor.KeyDown += HandleKeyDownInternally;
}
if (_isFocused && MaskKind.Selection == null)
SetCaretPosition(MaskKind.CaretPos, _selection, render: false);
await base.OnAfterRenderAsync(firstRender);
}
#endregion
}

View File

@ -33,7 +33,7 @@
}
else
{
<IconButton Variant="@Variant" Icon="@Icon" Color="@Color" Size="@Size" Disabled="@Disabled" DisableRipple="@DisableRipple" DisableElevation="@DisableElevation" Clicked="@ToggleMenu" @ontouchend="@(ActivationEvent == MouseEvent.RightClick ? ToggleMenuTouch : null)" @oncontextmenu="@(ActivationEvent==MouseEvent.RightClick ? ToggleMenu : null)" />
<GlyphButton Variant="@Variant" Glyph="@Icon" Color="@Color" Size="@Size" Disabled="@Disabled" DisableRipple="@DisableRipple" DisableElevation="@DisableElevation" Clicked="@ToggleMenu" @ontouchend="@(ActivationEvent == MouseEvent.RightClick ? ToggleMenuTouch : null)" @oncontextmenu="@(ActivationEvent==MouseEvent.RightClick ? ToggleMenu : null)" />
}
@* The portal has to include the cascading values inside, because it's not able to teletransport the cascade *@
<Popover Open="@_isOpen" Class="@PopoverClass" MaxHeight="@MaxHeight" AnchorOrigin="@AnchorOrigin" TransformOrigin="@TransformOrigin" RelativeWidth="@FullWidth" Style="@PopoverStyle">

View File

@ -9,136 +9,14 @@ namespace Connected.Components;
public partial class Menu : UIComponent, IActivatable
{
protected string Classname =>
new CssBuilder("menu")
.AddClass(AdditionalClassList)
.Build();
protected string ActivatorClassname =>
new CssBuilder("menu-activator")
.AddClass("disabled", Disabled)
.Build();
#region Variables
private bool _isOpen;
private bool _isMouseOver = false;
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string Label { get; set; }
/// <summary>
/// User class names for the list, separated by space
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public string ListClass { get; set; }
/// <summary>
/// User class names for the popover, separated by space
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public string PopoverClass { get; set; }
/// <summary>
/// Glyph to use if set will turn the button into a MudIconButton.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string Icon { get; set; }
/// <summary>
/// The color of the icon. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
/// <summary>
/// Glyph placed before the text if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string StartIcon { get; set; }
/// <summary>
/// Glyph placed after the text if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string EndIcon { get; set; }
/// <summary>
/// The color of the button. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public ThemeColor Color { get; set; } = ThemeColor.Default;
/// <summary>
/// The button Size of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public Size Size { get; set; } = Size.Medium;
/// <summary>
/// The button variant to use.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public Variant Variant { get; set; } = Variant.Text;
/// <summary>
/// If true, compact vertical padding will be applied to all menu items.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the list menu will be same width as the parent.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool FullWidth { get; set; }
/// <summary>
/// Sets the maxheight the menu can have when open.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public int? MaxHeight { get; set; }
/// <summary>
/// If true, instead of positioning the menu at the left upper corner, position at the exact cursor location.
/// This makes sense for larger activators
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupBehavior)]
public bool PositionAtCursor { get; set; }
/// <summary>
/// If true, instead of positioning the menu at the left upper corner, position at the exact cursor location.
/// This makes sense for larger activators
/// </summary>
[Obsolete("Use PositionAtCursor instead.", true)]
[Parameter]
public bool PositionAtCurser
{
get => PositionAtCursor;
set => PositionAtCursor = value;
}
/// <summary>
/// Place a MudButton, a MudIconButton or any other component capable of acting as an activator. This will
/// override the standard button and all parameters which concern it.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public RenderFragment ActivatorContent { get; set; }
public string PopoverStyle { get; set; }
#endregion
#region Events
/// <summary>
/// Specify the activation event when ActivatorContent is set
/// </summary>
@ -146,106 +24,6 @@ public partial class Menu : UIComponent, IActivatable
[Category(CategoryTypes.Menu.Behavior)]
public MouseEvent ActivationEvent { get; set; } = MouseEvent.LeftClick;
/// <summary>
/// Set the anchor origin point to determen where the popover will open from.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public Origin AnchorOrigin { get; set; } = Origin.TopLeft;
/// <summary>
/// Sets the transform origin point for the popover.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public Origin TransformOrigin { get; set; } = Origin.TopLeft;
/// <summary>
/// Sets the direction the select menu will start from relative to its parent.
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)]
[Parameter] public Direction Direction { get; set; } = Direction.Bottom;
/// <summary>
/// If true, the select menu will open either before or after the input depending on the direction.
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)]
[Parameter] public bool OffsetY { get; set; }
/// <summary>
/// If true, the select menu will open either above or bellow the input depending on the direction.
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)]
[Parameter] public bool OffsetX { get; set; }
/// <summary>
/// Set to true if you want to prevent page from scrolling when the menu is open
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool LockScroll { get; set; }
/// <summary>
/// If true, menu will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// If true, disables ripple effect.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public bool DisableRipple { get; set; }
/// <summary>
/// If true, no drop-shadow will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public bool DisableElevation { get; set; }
#region Obsolete members from previous MudButtonBase inherited structure
[ExcludeFromCodeCoverage]
[Obsolete("Linking is not supported. MudMenu is not a MudBaseButton anymore.", true)]
[Parameter] public string Link { get; set; }
[ExcludeFromCodeCoverage]
[Obsolete("Linking is not supported. MudMenu is not a MudBaseButton anymore.", true)]
[Parameter] public string Target { get; set; }
[ExcludeFromCodeCoverage]
[Obsolete("MudMenu is not a MudBaseButton anymore.", true)]
[Parameter] public string HtmlTag { get; set; } = "button";
[ExcludeFromCodeCoverage]
[Obsolete("MudMenu is not a MudBaseButton anymore.", true)]
[Parameter] public ButtonType ButtonType { get; set; }
[ExcludeFromCodeCoverage]
[Obsolete("MudMenu is not a MudBaseButton anymore.", true)]
[Parameter] public ICommand Command { get; set; }
[ExcludeFromCodeCoverage]
[Obsolete("MudMenu is not a MudBaseButton anymore.", true)]
[Parameter] public object CommandParameter { get; set; }
#endregion
/// <summary>
/// Add menu items here
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupBehavior)]
public RenderFragment ChildContent { get; set; }
public string PopoverStyle { get; set; }
public void CloseMenu()
{
_isOpen = false;
@ -332,4 +110,203 @@ public partial class Menu : UIComponent, IActivatable
CloseMenu();
}
}
#endregion
#region Content
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string Label { get; set; }
/// <summary>
/// Glyph to use if set will turn the button into a MudIconButton.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string Icon { get; set; }
/// <summary>
/// Glyph placed before the text if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string StartIcon { get; set; }
/// <summary>
/// Glyph placed after the text if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public string EndIcon { get; set; }
/// <summary>
/// Place a MudButton, a MudIconButton or any other component capable of acting as an activator. This will
/// override the standard button and all parameters which concern it.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public RenderFragment ActivatorContent { get; set; }
/// <summary>
/// Add menu items here
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupBehavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
/// <summary>
/// If true, the select menu will open either before or after the input depending on the direction.
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)]
[Parameter] public bool OffsetY { get; set; }
/// <summary>
/// If true, the select menu will open either above or bellow the input depending on the direction.
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)]
[Parameter] public bool OffsetX { get; set; }
protected string Classname =>
new CssBuilder("menu")
.AddClass(AdditionalClassList)
.Build();
protected string ActivatorClassname =>
new CssBuilder("menu-activator")
.AddClass("disabled", Disabled)
.Build();
/// <summary>
/// User class names for the list, separated by space
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public string ListClass { get; set; }
/// <summary>
/// User class names for the popover, separated by space
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public string PopoverClass { get; set; }
/// <summary>
/// The color of the icon. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
/// <summary>
/// The color of the button. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public ThemeColor Color { get; set; } = ThemeColor.Default;
/// <summary>
/// The button Size of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public Size Size { get; set; } = Size.Medium;
/// <summary>
/// The button variant to use.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public Variant Variant { get; set; } = Variant.Text;
/// <summary>
/// If true, compact vertical padding will be applied to all menu items.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool Dense { get; set; }
/// <summary>
/// If true, the list menu will be same width as the parent.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool FullWidth { get; set; }
/// <summary>
/// Sets the maxheight the menu can have when open.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public int? MaxHeight { get; set; }
#endregion
#region Behavior
/// <summary>
/// Set to true if you want to prevent page from scrolling when the menu is open
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool LockScroll { get; set; }
/// <summary>
/// If true, menu will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// If true, disables ripple effect.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public bool DisableRipple { get; set; }
/// <summary>
/// If true, no drop-shadow will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public bool DisableElevation { get; set; }
/// <summary>
/// If true, instead of positioning the menu at the left upper corner, position at the exact cursor location.
/// This makes sense for larger activators
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupBehavior)]
public bool PositionAtCursor { get; set; }
/// <summary>
/// Set the anchor origin point to determen where the popover will open from.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public Origin AnchorOrigin { get; set; } = Origin.TopLeft;
/// <summary>
/// Sets the transform origin point for the popover.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public Origin TransformOrigin { get; set; } = Origin.TopLeft;
/// <summary>
/// Sets the direction the select menu will start from relative to its parent.
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)]
[Parameter] public Direction Direction { get; set; } = Direction.Bottom;
#endregion
}

View File

@ -7,53 +7,15 @@ namespace Connected.Components;
public partial class MenuItem : UIComponent
{
#region Variables
[CascadingParameter] public Menu Menu { get; set; }
[Parameter][Category(CategoryTypes.Menu.Behavior)] public RenderFragment ChildContent { get; set; }
[Parameter][Category(CategoryTypes.Menu.Behavior)] public bool Disabled { get; set; }
[Inject] public NavigationManager UriHelper { get; set; }
[Inject] public IJsApiService JsApiService { get; set; }
#endregion
/// <summary>
/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where (Obsolete replaced by Href)
/// </summary>
[Obsolete("Use Href Instead.", false)]
[Parameter]
[Category(CategoryTypes.Menu.ClickAction)]
public string Link { get => Href; set => Href = value; }
/// <summary>
/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.ClickAction)]
public string Href { get; set; }
/// <summary>
/// Glyph to be used for this menu entry
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Icon { get; set; }
/// <summary>
/// The color of the icon. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
/// <summary>
/// The Glyph Size.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public Size IconSize { get; set; } = Size.Medium;
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public string Target { get; set; }
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public bool ForceLoad { get; set; }
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public ICommand Command { get; set; }
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public object CommandParameter { get; set; }
#region Events
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
[Parameter] public EventCallback<TouchEventArgs> OnTouch { get; set; }
@ -102,4 +64,53 @@ public partial class MenuItem : UIComponent
}
}
}
#endregion
#region Content
/// <summary>
/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where (Obsolete replaced by Href)
/// </summary>
[Obsolete("Use Href Instead.", false)]
[Parameter]
[Category(CategoryTypes.Menu.ClickAction)]
public string Link { get => Href; set => Href = value; }
/// <summary>
/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.ClickAction)]
public string Href { get; set; }
/// <summary>
/// Glyph to be used for this menu entry
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Behavior)]
public string Icon { get; set; }
#endregion
#region Styling
/// <summary>
/// The color of the icon. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
/// <summary>
/// The Glyph Size.
/// </summary>
[Parameter]
[Category(CategoryTypes.List.Appearance)]
public Size IconSize { get; set; } = Size.Medium;
#endregion
#region Behavior
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public string Target { get; set; }
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public bool ForceLoad { get; set; }
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public ICommand Command { get; set; }
[Parameter][Category(CategoryTypes.Menu.ClickAction)] public object CommandParameter { get; set; }
#endregion
}

View File

@ -6,9 +6,106 @@ namespace Connected.Components;
public partial class MessageBox : UIComponent
{
#region Variables
[Inject] private IDialogService DialogService { get; set; }
[CascadingParameter] private DialogInstance DialogInstance { get; set; }
private bool _isVisible;
private bool IsInline => DialogInstance == null;
private IDialogReference _reference;
private ActivatableCallback _yesCallback, _cancelCallback, _noCallback;
#endregion
#region Events
/// <summary>
/// Fired when the yes button is clicked
/// </summary>
[Parameter]
public EventCallback<bool> OnYes { get; set; }
/// <summary>
/// Fired when the no button is clicked
/// </summary>
[Parameter]
public EventCallback<bool> OnNo { get; set; }
/// <summary>
/// Fired when the cancel button is clicked or the msg box was closed via the X
/// </summary>
[Parameter]
public EventCallback<bool> OnCancel { get; set; }
/// <summary>
/// Raised when the inline dialog's display status changes.
/// </summary>
[Parameter]
public EventCallback<bool> IsVisibleChanged { get; set; }
public async Task<bool?> Show(DialogOptions options = null)
{
if (DialogService == null)
return null;
var parameters = new DialogParameters()
{
[nameof(Title)] = Title,
[nameof(TitleContent)] = TitleContent,
[nameof(Message)] = Message,
[nameof(MarkupMessage)] = MarkupMessage,
[nameof(MessageContent)] = MessageContent,
[nameof(CancelText)] = CancelText,
[nameof(CancelButton)] = CancelButton,
[nameof(NoText)] = NoText,
[nameof(NoButton)] = NoButton,
[nameof(YesText)] = YesText,
[nameof(YesButton)] = YesButton,
};
_reference = await DialogService.ShowAsync<MessageBox>(parameters: parameters, options: options, title: Title);
var result = await _reference.Result;
if (result.Cancelled || result.Data is not bool data)
return null;
return data;
}
public void Close()
{
_reference?.Close();
}
private void OnYesActivated(object arg1, MouseEventArgs arg2) => OnYesClicked();
private void OnNoActivated(object arg1, MouseEventArgs arg2) => OnNoClicked();
private void OnCancelActivated(object arg1, MouseEventArgs arg2) => OnCancelClicked();
private void OnYesClicked() => DialogInstance.Close(DialogResult.Ok(true));
private void OnNoClicked() => DialogInstance.Close(DialogResult.Ok(false));
private void OnCancelClicked() => DialogInstance.Close(DialogResult.Cancel());
private void HandleKeyDown(KeyboardEventArgs args)
{
if (args.Key == "Escape")
{
OnCancelClicked();
}
}
#endregion
#region Content
/// <summary>
/// Define the message box title as a renderfragment (overrides GlyphTitle)
/// </summary>
[Parameter]
[Category(CategoryTypes.MessageBox.Behavior)]
public RenderFragment TitleContent { get; set; }
/// <summary>
/// Define the message box body as a renderfragment (overrides Message)
/// </summary>
[Parameter]
[Category(CategoryTypes.MessageBox.Behavior)]
public RenderFragment MessageContent { get; set; }
/// <summary>
/// The message box title. If null or empty, title will be hidden
@ -17,13 +114,6 @@ public partial class MessageBox : UIComponent
[Category(CategoryTypes.MessageBox.Behavior)]
public string Title { get; set; }
/// <summary>
/// Define the message box title as a renderfragment (overrides GlyphTitle)
/// </summary>
[Parameter]
[Category(CategoryTypes.MessageBox.Behavior)]
public RenderFragment TitleContent { get; set; }
/// <summary>
/// The message box message as string.
/// </summary>
@ -38,14 +128,6 @@ public partial class MessageBox : UIComponent
[Category(CategoryTypes.MessageBox.Behavior)]
public MarkupString MarkupMessage { get; set; }
/// <summary>
/// Define the message box body as a renderfragment (overrides Message)
/// </summary>
[Parameter]
[Category(CategoryTypes.MessageBox.Behavior)]
public RenderFragment MessageContent { get; set; }
/// <summary>
/// Text of the cancel button. Leave null to hide the button.
/// </summary>
@ -90,25 +172,9 @@ public partial class MessageBox : UIComponent
[Parameter]
[Category(CategoryTypes.MessageBox.Behavior)]
public RenderFragment YesButton { get; set; }
#endregion
/// <summary>
/// Fired when the yes button is clicked
/// </summary>
[Parameter]
public EventCallback<bool> OnYes { get; set; }
/// <summary>
/// Fired when the no button is clicked
/// </summary>
[Parameter]
public EventCallback<bool> OnNo { get; set; }
/// <summary>
/// Fired when the cancel button is clicked or the msg box was closed via the X
/// </summary>
[Parameter]
public EventCallback<bool> OnCancel { get; set; }
#region Behavior
/// <summary>
/// Bind this two-way to show and close an inlined message box. Has no effect on opened msg boxes
/// </summary>
@ -133,50 +199,9 @@ public partial class MessageBox : UIComponent
IsVisibleChanged.InvokeAsync(value);
}
}
#endregion
private bool _isVisible;
private bool IsInline => DialogInstance == null;
private IDialogReference _reference;
/// <summary>
/// Raised when the inline dialog's display status changes.
/// </summary>
[Parameter]
public EventCallback<bool> IsVisibleChanged { get; set; }
public async Task<bool?> Show(DialogOptions options = null)
{
if (DialogService == null)
return null;
var parameters = new DialogParameters()
{
[nameof(Title)] = Title,
[nameof(TitleContent)] = TitleContent,
[nameof(Message)] = Message,
[nameof(MarkupMessage)] = MarkupMessage,
[nameof(MessageContent)] = MessageContent,
[nameof(CancelText)] = CancelText,
[nameof(CancelButton)] = CancelButton,
[nameof(NoText)] = NoText,
[nameof(NoButton)] = NoButton,
[nameof(YesText)] = YesText,
[nameof(YesButton)] = YesButton,
};
_reference = await DialogService.ShowAsync<MessageBox>(parameters: parameters, options: options, title: Title);
var result = await _reference.Result;
if (result.Cancelled || result.Data is not bool data)
return null;
return data;
}
public void Close()
{
_reference?.Close();
}
private ActivatableCallback _yesCallback, _cancelCallback, _noCallback;
#region Lifecycle
protected override void OnInitialized()
{
base.OnInitialized();
@ -187,25 +212,6 @@ public partial class MessageBox : UIComponent
if (CancelButton != null)
_cancelCallback = new ActivatableCallback() { ActivateCallback = OnCancelActivated };
}
private void OnYesActivated(object arg1, MouseEventArgs arg2) => OnYesClicked();
private void OnNoActivated(object arg1, MouseEventArgs arg2) => OnNoClicked();
private void OnCancelActivated(object arg1, MouseEventArgs arg2) => OnCancelClicked();
private void OnYesClicked() => DialogInstance.Close(DialogResult.Ok(true));
private void OnNoClicked() => DialogInstance.Close(DialogResult.Ok(false));
private void OnCancelClicked() => DialogInstance.Close(DialogResult.Cancel());
private void HandleKeyDown(KeyboardEventArgs args)
{
if (args.Key == "Escape")
{
OnCancelClicked();
}
}
#endregion
}

View File

@ -5,14 +5,14 @@
<button @onclick="ExpandedToggle" tabindex="0" class="@ButtonClassname">
@if (!String.IsNullOrEmpty(Icon))
{
<Icon Icon="@Icon" Color="@IconColor" Class="@IconClassname" />
<Icon Glyph="@Icon" Color="@IconColor" Class="@IconClassname" />
}
<div Class="nav-link-text">
@Title
</div>
@if (!HideExpandIcon)
{
<Icon Icon="@ExpandIcon" class="@ExpandIconClassname" />
<Icon Glyph="@ExpandIcon" class="@ExpandIconClassname" />
}
</button>
<Collapse Expanded="@Expanded" MaxHeight="@MaxHeight" Class="navgroup-collapse">

View File

@ -6,6 +6,47 @@ namespace Connected.Components;
public partial class NavGroup : UIComponent
{
#region Variables
private bool _expanded;
#endregion
#region Events
protected void ExpandedToggle()
{
_expanded = !Expanded;
ExpandedChanged.InvokeAsync(_expanded);
}
[Parameter] public EventCallback<bool> ExpandedChanged { get; set; }
#endregion
#region Content
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public string Title { get; set; }
/// <summary>
/// Glyph to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public string Icon { get; set; }
/// <summary>
/// The color of the icon. It supports the theme colors, default value uses the themes drawer icon color.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Default;
/// <summary>
/// If set, overrides the default expand icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public string ExpandIcon { get; set; } = @Icons.Filled.ArrowDropDown;
#endregion
#region Styling
protected string Classname =>
new CssBuilder("nav-group")
.AddClass(AdditionalClassList)
@ -29,24 +70,26 @@ public partial class NavGroup : UIComponent
.AddClass($"transform-disabled", Expanded && Disabled)
.Build();
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public string Title { get; set; }
/// <summary>
/// Glyph to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public string Icon { get; set; }
/// <summary>
/// The color of the icon. It supports the theme colors, default value uses the themes drawer icon color.
/// If true, hides expand-icon at the end of the NavGroup.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Default;
public bool HideExpandIcon { get; set; }
/// <summary>
/// Explicitly sets the height for the Collapse element to override the css default.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public int? MaxHeight { get; set; }
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Behavior
/// <summary>
/// If true, the button will be disabled.
/// </summary>
@ -61,7 +104,7 @@ public partial class NavGroup : UIComponent
[Category(CategoryTypes.NavMenu.Appearance)]
public bool DisableRipple { get; set; }
private bool _expanded;
/// <summary>
/// If true, expands the nav group, otherwise collapse it.
/// Two-way bindable
@ -80,37 +123,6 @@ public partial class NavGroup : UIComponent
ExpandedChanged.InvokeAsync(_expanded);
}
}
#endregion
[Parameter] public EventCallback<bool> ExpandedChanged { get; set; }
/// <summary>
/// If true, hides expand-icon at the end of the NavGroup.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public bool HideExpandIcon { get; set; }
/// <summary>
/// Explicitly sets the height for the Collapse element to override the css default.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public int? MaxHeight { get; set; }
/// <summary>
/// If set, overrides the default expand icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public string ExpandIcon { get; set; } = @Icons.Filled.ArrowDropDown;
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public RenderFragment ChildContent { get; set; }
protected void ExpandedToggle()
{
_expanded = !Expanded;
ExpandedChanged.InvokeAsync(_expanded);
}
}

View File

@ -12,7 +12,7 @@
ActiveClass="@ActiveClass">
@if (!string.IsNullOrEmpty(Icon))
{
<Icon Icon="@Icon" Color="@IconColor" Class="@IconClassname"/>
<Icon Glyph="@Icon" Color="@IconColor" Class="@IconClassname" />
}
<div class="nav-link-text">
@ChildContent
@ -26,7 +26,7 @@
ActiveClass="@ActiveClass">
@if (!string.IsNullOrEmpty(Icon))
{
<Icon Icon="@Icon" Color="@IconColor" Class="@IconClassname" />
<Icon Glyph="@Icon" Color="@IconColor" Class="@IconClassname" />
}
<div Class="nav-link-text">
@ChildContent

View File

@ -7,6 +7,34 @@ namespace Connected.Components;
public partial class NavLink : SelectItemBase
{
#region Events
[CascadingParameter] INavigationEventReceiver NavigationEventReceiver { get; set; }
protected Task HandleNavigation()
{
if (!Disabled && NavigationEventReceiver != null)
{
return NavigationEventReceiver.OnNavigation();
}
return Task.CompletedTask;
}
#endregion
#region Content
/// <summary>
/// Glyph to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public string Icon { get; set; }
[Parameter]
[Category(CategoryTypes.NavMenu.ClickAction)]
public string Target { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("nav-item")
.AddClass($"ripple", !DisableRipple && !Disabled)
@ -23,6 +51,26 @@ public partial class NavLink : SelectItemBase
.AddClass($"nav-link-icon-default", IconColor == ThemeColor.Default)
.Build();
/// <summary>
/// The color of the icon. It supports the theme colors, default value uses the themes drawer icon color.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Default;
/// <summary>
/// User class names when active, separated by space.
/// </summary>
[Parameter]
[Category(CategoryTypes.ComponentBase.Common)]
public string ActiveClass { get; set; } = "active";
#endregion
#region Behavior
protected Dictionary<string, object> Attributes
{
get => Disabled ? null : new Dictionary<string, object>()
@ -32,44 +80,9 @@ public partial class NavLink : SelectItemBase
{ "rel", !string.IsNullOrWhiteSpace(Target) ? "noopener noreferrer" : string.Empty }
};
}
/// <summary>
/// Glyph to use if set.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public string Icon { get; set; }
/// <summary>
/// The color of the icon. It supports the theme colors, default value uses the themes drawer icon color.
/// </summary>
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public ThemeColor IconColor { get; set; } = ThemeColor.Default;
[Parameter]
[Category(CategoryTypes.NavMenu.Behavior)]
public NavLinkMatch Match { get; set; } = NavLinkMatch.Prefix;
#endregion
[Parameter]
[Category(CategoryTypes.NavMenu.ClickAction)]
public string Target { get; set; }
/// <summary>
/// User class names when active, separated by space.
/// </summary>
[Parameter]
[Category(CategoryTypes.ComponentBase.Common)]
public string ActiveClass { get; set; } = "active";
[CascadingParameter] INavigationEventReceiver NavigationEventReceiver { get; set; }
protected Task HandleNavigation()
{
if (!Disabled && NavigationEventReceiver != null)
{
return NavigationEventReceiver.OnNavigation();
}
return Task.CompletedTask;
}
}

View File

@ -7,6 +7,13 @@ namespace Connected.Components;
public partial class NavMenu : UIComponent
{
#region Content
[Category(CategoryTypes.NavMenu.Behavior)]
[Parameter] public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("navmenu")
.AddClass($"navmenu-{Color.ToDescription()}")
@ -17,9 +24,6 @@ public partial class NavMenu : UIComponent
.AddClass(AdditionalClassList)
.Build();
[Category(CategoryTypes.NavMenu.Behavior)]
[Parameter] public RenderFragment ChildContent { get; set; }
/// <summary>
/// The color of the active NavLink.
/// </summary>
@ -54,5 +58,6 @@ public partial class NavMenu : UIComponent
[Parameter]
[Category(CategoryTypes.NavMenu.Appearance)]
public bool Dense { get; set; }
#endregion
}

View File

@ -8,40 +8,57 @@ namespace Connected.Components;
public partial class Overlay : UIComponent, IDisposable
{
#region Variables
private bool _visible;
protected string Classname =>
new CssBuilder("overlay")
.AddClass("overlay-absolute", Absolute)
.AddClass(AdditionalClassList)
.Build();
protected string ScrimClassname =>
new CssBuilder("overlay-scrim")
.AddClass("overlay-dark", DarkBackground)
.AddClass("overlay-light", LightBackground)
.Build();
protected string Styles =>
new StyleBuilder()
.AddStyle("z-index", $"{ZIndex}", ZIndex != 5)
.Build();
[Inject] public IScrollManager ScrollManager { get; set; }
#endregion
/// <summary>
/// Child content of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Overlay.Behavior)]
public RenderFragment ChildContent { get; set; }
#region Events
/// <summary>
/// Fires when Visible changes
/// </summary>
[Parameter]
public EventCallback<bool> VisibleChanged { get; set; }
/// <summary>
/// Fired when the overlay is clicked
/// </summary>
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
protected internal void OnClickHandler(MouseEventArgs ev)
{
if (AutoClose)
Visible = false;
OnClick.InvokeAsync(ev);
if (Command?.CanExecute(CommandParameter) ?? false)
{
Command.Execute(CommandParameter);
}
}
//locks the scroll attaching a CSS class to the specified element, in this case the body
void BlockScroll()
{
ScrollManager.LockScrollAsync("body", LockScrollClass);
}
//removes the CSS class that prevented scrolling
void UnblockScroll()
{
ScrollManager.UnlockScrollAsync("body", LockScrollClass);
}
#endregion
#region Content
/// <summary>
/// Child content of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Overlay.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
/// <summary>
/// If true overlay will be visible. Two-way bindable.
/// </summary>
@ -58,20 +75,22 @@ public partial class Overlay : UIComponent, IDisposable
VisibleChanged.InvokeAsync(_visible);
}
}
protected string Classname =>
new CssBuilder("overlay")
.AddClass("overlay-absolute", Absolute)
.AddClass(AdditionalClassList)
.Build();
/// <summary>
/// If true overlay will set Visible false on click.
/// </summary>
[Parameter]
[Category(CategoryTypes.Overlay.ClickAction)]
public bool AutoClose { get; set; }
protected string ScrimClassname =>
new CssBuilder("overlay-scrim")
.AddClass("overlay-dark", DarkBackground)
.AddClass("overlay-light", LightBackground)
.Build();
/// <summary>
/// If true (default), the Document.body element will not be able to scroll
/// </summary>
[Parameter]
[Category(CategoryTypes.Overlay.Behavior)]
public bool LockScroll { get; set; } = true;
protected string Styles =>
new StyleBuilder()
.AddStyle("z-index", $"{ZIndex}", ZIndex != 5)
.Build();
/// <summary>
/// The css class that will be added to body if lockscroll is used.
@ -107,6 +126,22 @@ public partial class Overlay : UIComponent, IDisposable
[Parameter]
[Category(CategoryTypes.Overlay.Behavior)]
public int ZIndex { get; set; } = 5;
#endregion
#region Behavior
/// <summary>
/// If true overlay will set Visible false on click.
/// </summary>
[Parameter]
[Category(CategoryTypes.Overlay.ClickAction)]
public bool AutoClose { get; set; }
/// <summary>
/// If true (default), the Document.body element will not be able to scroll
/// </summary>
[Parameter]
[Category(CategoryTypes.Overlay.Behavior)]
public bool LockScroll { get; set; } = true;
/// <summary>
/// Command parameter.
@ -121,23 +156,9 @@ public partial class Overlay : UIComponent, IDisposable
[Parameter]
[Category(CategoryTypes.Overlay.ClickAction)]
public ICommand Command { get; set; }
#endregion
/// <summary>
/// Fired when the overlay is clicked
/// </summary>
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
protected internal void OnClickHandler(MouseEventArgs ev)
{
if (AutoClose)
Visible = false;
OnClick.InvokeAsync(ev);
if (Command?.CanExecute(CommandParameter) ?? false)
{
Command.Execute(CommandParameter);
}
}
#region Lifecycle
//if not visible or CSS `position:absolute`, don't lock scroll
protected override void OnAfterRender(bool firstTime)
{
@ -151,22 +172,35 @@ public partial class Overlay : UIComponent, IDisposable
}
//locks the scroll attaching a CSS class to the specified element, in this case the body
void BlockScroll()
{
ScrollManager.LockScrollAsync("body", LockScrollClass);
}
//removes the CSS class that prevented scrolling
void UnblockScroll()
{
ScrollManager.UnlockScrollAsync("body", LockScrollClass);
}
//When disposing the overlay, remove the class that prevented scrolling
public void Dispose()
{
UnblockScroll();
}
#endregion
}

View File

@ -9,46 +9,15 @@ namespace Connected.Components;
public partial class PageContentNavigation : IAsyncDisposable
{
#region Variables
private List<PageContentSection> _sections = new();
private IScrollSpy _scrollSpy;
[Inject] IScrollSpyFactory ScrollSpyFactory { get; set; }
/// <summary>
/// The displayed section within the MudPageContentNavigation
/// </summary>
public IEnumerable<PageContentSection> Sections => _sections.AsEnumerable();
/// <summary>
/// The currently active session. null if there is no section selected
/// </summary>
public PageContentSection ActiveSection => _sections.FirstOrDefault(x => x.IsActive == true);
/// <summary>
/// The text displayed about the section links. Defaults to "Conents"
/// </summary>
[Parameter] public string Headline { get; set; } = "Contents";
/// <summary>
/// The css selector used to identifify the HTML elements that should be observed for viewport changes
/// </summary>
[Parameter] public string SectionClassSelector { get; set; } = string.Empty;
/// <summary>
/// If there are mutliple levels, this can specified to make a mapping between a level class likw "second-level" and the level in the hierarchy
/// </summary>
[Parameter] public IDictionary<string, int> HierarchyMapper { get; set; } = new Dictionary<string, int>();
/// <summary>
/// If there are multiple levels, this property controls they visibility of them.
/// </summary>
[Parameter] public ContentNavigationExpandBehaviour ExpandBehaviour { get; set; } = ContentNavigationExpandBehaviour.Always;
/// <summary>
/// If this option is true the first added section will become active when there is no other indication of an active session. Default value is false
/// </summary>
[Parameter] public bool ActivateFirstSectionAsDefault { get; set; } = false;
private Dictionary<PageContentSection, PageContentSection> _parentMapper = new();
#endregion
#region Events
private Task OnNavLinkClick(string id)
{
SelectActiveSection(id);
@ -99,7 +68,7 @@ public partial class PageContentNavigation : IAsyncDisposable
/// <param name="forceUpdate">If true, StateHasChanged is called, forcing a rerender of the component</param>
public void AddSection(string sectionName, string sectionId, bool forceUpdate) => AddSection(new(sectionName, sectionId), forceUpdate);
private Dictionary<PageContentSection, PageContentSection> _parentMapper = new();
/// <summary>
/// Add a section to the content navigation
@ -139,13 +108,54 @@ public partial class PageContentNavigation : IAsyncDisposable
/// Rerender the component
/// </summary>
public void Update() => StateHasChanged();
#endregion
#region Content
/// <summary>
/// The displayed section within the MudPageContentNavigation
/// </summary>
public IEnumerable<PageContentSection> Sections => _sections.AsEnumerable();
/// <summary>
/// The currently active session. null if there is no section selected
/// </summary>
public PageContentSection ActiveSection => _sections.FirstOrDefault(x => x.IsActive == true);
/// <summary>
/// The text displayed about the section links. Defaults to "Conents"
/// </summary>
[Parameter] public string Headline { get; set; } = "Contents";
#endregion
#region Styling
/// <summary>
/// The css selector used to identifify the HTML elements that should be observed for viewport changes
/// </summary>
[Parameter] public string SectionClassSelector { get; set; } = string.Empty;
#endregion
#region Behavior
/// <summary>
/// If there are mutliple levels, this can specified to make a mapping between a level class likw "second-level" and the level in the hierarchy
/// </summary>
[Parameter] public IDictionary<string, int> HierarchyMapper { get; set; } = new Dictionary<string, int>();
/// <summary>
/// If there are multiple levels, this property controls they visibility of them.
/// </summary>
[Parameter] public ContentNavigationExpandBehaviour ExpandBehaviour { get; set; } = ContentNavigationExpandBehaviour.Always;
/// <summary>
/// If this option is true the first added section will become active when there is no other indication of an active session. Default value is false
/// </summary>
[Parameter] public bool ActivateFirstSectionAsDefault { get; set; } = false;
#endregion
#region Lifecycle
protected override void OnInitialized()
{
_scrollSpy = ScrollSpyFactory.Create();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
@ -169,4 +179,6 @@ public partial class PageContentNavigation : IAsyncDisposable
_scrollSpy.ScrollSectionSectionCentered -= ScrollSpy_ScrollSectionSectionCentered;
return _scrollSpy.DisposeAsync();
}
#endregion
}

View File

@ -6,13 +6,13 @@
@if (ShowFirstButton)
{
<li class="@ItemClassname">
<IconButton Icon="@FirstIcon" Size="@Size" Variant="@Variant" Disabled="@(Selected == 1 || Disabled)" Clicked="@(() => OnClickControlButton(Page.First))" aria-label="First page"></IconButton>
<GlyphButton Glyph="@FirstIcon" Size="@Size" Variant="@Variant" Disabled="@(Selected == 1 || Disabled)" Clicked="@(() => OnClickControlButton(Page.First))" aria-label="First page"></GlyphButton>
</li>
}
@if (ShowPreviousButton)
{
<li class="@ItemClassname">
<IconButton Icon="@BeforeIcon" Size="@Size" Variant="@Variant" Disabled="@(Selected == 1 || Disabled)" Clicked="@(() => OnClickControlButton(Page.Previous))" aria-label="Previous page"></IconButton>
<GlyphButton Glyph="@BeforeIcon" Size="@Size" Variant="@Variant" Disabled="@(Selected == 1 || Disabled)" Clicked="@(() => OnClickControlButton(Page.Previous))" aria-label="Previous page"></GlyphButton>
</li>
}
@foreach (var state in GeneratePagination())
@ -39,13 +39,13 @@
@if (ShowNextButton)
{
<li class="@ItemClassname">
<IconButton Icon="@NextIcon" Variant="@Variant" Size="@Size" Disabled="@(Selected == Count || Disabled)" Clicked="@(() => OnClickControlButton(Page.Next))" aria-label="Next page"></IconButton>
<GlyphButton Glyph="@NextIcon" Variant="@Variant" Size="@Size" Disabled="@(Selected == Count || Disabled)" Clicked="@(() => OnClickControlButton(Page.Next))" aria-label="Next page"></GlyphButton>
</li>
}
@if (ShowLastButton)
{
<li class="@ItemClassname">
<IconButton Icon="@LastIcon" Variant="@Variant" Size="@Size" Disabled="@(Selected == Count || Disabled)" Clicked="@(() => OnClickControlButton(Page.Last))" aria-label="Last page"></IconButton>
<GlyphButton Glyph="@LastIcon" Variant="@Variant" Size="@Size" Disabled="@(Selected == Count || Disabled)" Clicked="@(() => OnClickControlButton(Page.Last))" aria-label="Last page"></GlyphButton>
</li>
}
</Element>

View File

@ -10,6 +10,16 @@ namespace Connected.Components;
public partial class Paper : UIComponent
{
#region Content
/// <summary>
/// Child content of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Paper.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
#region Styling
protected string Classname =>
new CssBuilder("paper")
.AddClass($"paper-outlined", Outlined)
@ -91,10 +101,6 @@ public partial class Paper : UIComponent
[Category(CategoryTypes.Paper.Appearance)]
public string MinWidth { get; set; }
/// <summary>
/// Child content of the component.
/// </summary>
[Parameter]
[Category(CategoryTypes.Paper.Behavior)]
public RenderFragment ChildContent { get; set; }
#endregion
}

View File

@ -4,13 +4,13 @@
@if (ReadOnly)
{
<span class="@ClassName">
<Icon Icon="@ItemIcon" Size="@Size"></Icon>
<Icon Glyph="@ItemIcon" Size="@Size"></Icon>
</span>
}
else
{
<span class="@ClassName" @onmouseover="HandleMouseOver" @onclick="HandleClick" @onmouseout="HandleMouseOut ">
<input class="rating-input" type="radio" tabindex="-1" value="@ItemValue" name="@Rating?.Name" disabled="@Disabled" checked="@(IsChecked)" @attributes="CustomAttributes" />
<Icon Icon="@ItemIcon" Size="@Size"></Icon>
<Icon Glyph="@ItemIcon" Size="@Size"></Icon>
</span>
}

View File

@ -6,7 +6,7 @@
@if (!HideIcon)
{
<div class="snackbar-icon">
<Icon Icon="@Icon" Color="@IconColor" Size="@IconSize" />
<Icon Glyph="@Icon" Color="@IconColor" Size="@IconSize" />
</div>
}
@ -28,7 +28,7 @@
@if (ShowCloseIcon)
{
<IconButton Icon="@CloseIcon" Size="Size.Small" Class="ms-2" Clicked="CloseIconClicked" />
<GlyphButton Glyph="@CloseIcon" Size="Size.Small" Class="ms-2" Clicked="CloseIconClicked" />
}
</div>
}

View File

@ -11,7 +11,7 @@
<span class="switch-thumb d-flex align-center justify-center">
@if (!string.IsNullOrEmpty(ThumbIcon))
{
<Icon Color="@ThumbIconColor" Icon="@ThumbIcon" Style=" height:16px; width:16px;" />
<Icon Color="@ThumbIconColor" Glyph="@ThumbIcon" Style=" height:16px; width:16px;" />
}
</span>
</span>

View File

@ -13,7 +13,7 @@
<div class="d-flex">
@if (GroupDefinition.Expandable)
{
<IconButton Class="table-row-expander" Icon="@(IsExpanded ? ExpandIcon : CollapseIcon)" Clicked="@(() => IsExpanded = !IsExpanded)" />
<GlyphButton Class="table-row-expander" Glyph="@(IsExpanded ? ExpandIcon : CollapseIcon)" Clicked="@(() => IsExpanded = !IsExpanded)" />
}
else
{

View File

@ -34,10 +34,10 @@
@if (!HidePagination)
{
<div class="table-pagination-actions">
<IconButton Class="flip-x-rtl" Icon="@FirstIcon" Disabled="@BackButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.First))" aria-label="First page" />
<IconButton Class="flip-x-rtl" Icon="@BeforeIcon" Disabled="@BackButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.Previous))" aria-label="Previous page" />
<IconButton Class="flip-x-rtl" Icon="@NextIcon" Disabled="@ForwardButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.Next))" aria-label="Next page" />
<IconButton Class="flip-x-rtl" Icon="@LastIcon" Disabled="@ForwardButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.Last))" aria-label="Last page" />
<GlyphButton Class="flip-x-rtl" Glyph="@FirstIcon" Disabled="@BackButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.First))" aria-label="First page" />
<GlyphButton Class="flip-x-rtl" Glyph="@BeforeIcon" Disabled="@BackButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.Previous))" aria-label="Previous page" />
<GlyphButton Class="flip-x-rtl" Glyph="@NextIcon" Disabled="@ForwardButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.Next))" aria-label="Next page" />
<GlyphButton Class="flip-x-rtl" Glyph="@LastIcon" Disabled="@ForwardButtonsDisabled" Clicked="@(() => Table.NavigateTo(Page.Last))" aria-label="Last page" />
</div>
}
@if (HorizontalAlignment == HorizontalAlignment.Start ||

View File

@ -12,11 +12,11 @@
{
@if (_direction != SortDirection.None)
{
<Icon Icon="@SortIcon" Class="@GetSortIconClass()" />
<Icon Glyph="@SortIcon" Class="@GetSortIconClass()" />
}
else
{
<Icon Icon="@SortIcon" Class="table-sort-label-icon"/>
<Icon Glyph="@SortIcon" Class="table-sort-label-icon" />
}
}
@if (AppendIcon)

View File

@ -15,19 +15,19 @@
}
else
{
<IconButton Size="@Size.Small" Icon="@Icons.Outlined.Edit" Class="pa-0" Clicked="StartEditingItem" Disabled="Context.EditButtonDisabled(Item)" />
<GlyphButton Size="@Size.Small" Glyph="@Icons.Outlined.Edit" Class="pa-0" Clicked="StartEditingItem" Disabled="Context.EditButtonDisabled(Item)" />
}
}
else if (object.ReferenceEquals(Context?.Table._editingItem, Item) && (!Context?.Table.ReadOnly ?? false) && Context?.Table.ApplyButtonPosition.DisplayApplyButtonAtStart() == true)
{
<div style="display: flex;">
<Tooltip Text="@Context.Table.CommitEditTooltip">
<IconButton Class="pa-0" Icon="@Context.Table.CommitEditIcon" Clicked="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
<GlyphButton Class="pa-0" Glyph="@Context.Table.CommitEditIcon" Clicked="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
</Tooltip>
@if (Context.Table.CanCancelEdit)
{
<Tooltip Text="@Context.Table.CancelEditTooltip">
<IconButton Class="pa-0 ml-4" Icon="@Context.Table.CancelEditIcon" Clicked="CancelEdit" Size="@Size.Small" />
<GlyphButton Class="pa-0 ml-4" Glyph="@Context.Table.CancelEditIcon" Clicked="CancelEdit" Size="@Size.Small" />
</Tooltip>
}
</div>
@ -58,19 +58,19 @@
}
else
{
<IconButton Size="@Size.Small" Icon="@Icons.Outlined.Edit" Class="pa-0" Clicked="StartEditingItem" Disabled="Context.EditButtonDisabled(Item)" />
<GlyphButton Size="@Size.Small" Glyph="@Icons.Outlined.Edit" Class="pa-0" Clicked="StartEditingItem" Disabled="Context.EditButtonDisabled(Item)" />
}
}
else if (object.ReferenceEquals(Context?.Table._editingItem, Item) && (!Context?.Table.ReadOnly ?? false) && Context?.Table.ApplyButtonPosition.DisplayApplyButtonAtEnd() == true)
{
<div style="display: flex;">
<Tooltip Text="@Context.Table.CommitEditTooltip">
<IconButton Class="pa-0" Icon="@Context.Table.CommitEditIcon" Clicked="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
<GlyphButton Class="pa-0" Glyph="@Context.Table.CommitEditIcon" Clicked="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
</Tooltip>
@if (Context.Table.CanCancelEdit)
{
<Tooltip Text="@Context.Table.CancelEditTooltip">
<IconButton Class="pa-0 ml-4" Icon="@Context.Table.CancelEditIcon" Clicked="CancelEdit" Size="@Size.Small" />
<GlyphButton Class="pa-0 ml-4" Glyph="@Context.Table.CancelEditIcon" Clicked="CancelEdit" Size="@Size.Small" />
</Tooltip>
}
</div>

View File

@ -15,12 +15,12 @@
if (string.IsNullOrEmpty(AddIconToolTip) == false)
{
<Tooltip Text="@AddIconToolTip">
<IconButton Icon="@AddTabIcon" Class="@AddIconClass" Style="@AddIconStyle" Clicked="@AddTab" />
<GlyphButton Glyph="@AddTabIcon" Class="@AddIconClass" Style="@AddIconStyle" Clicked="@AddTab" />
</Tooltip>
}
else
{
<IconButton Icon="@AddTabIcon" Class="@AddIconClass" Style="@AddIconStyle" Clicked="@AddTab" />
<GlyphButton Glyph="@AddTabIcon" Class="@AddIconClass" Style="@AddIconStyle" Clicked="@AddTab" />
}
}
;
@ -30,12 +30,12 @@
if (string.IsNullOrEmpty(CloseIconToolTip) == false)
{
<Tooltip Text="@CloseIconToolTip">
<IconButton Icon="@CloseTabIcon" Class="@CloseIconClass" Style="@CloseIconStyle" Clicked="EventCallback.Factory.Create<MouseEventArgs>(this, () => CloseTab.InvokeAsync(context))" />
<GlyphButton Glyph="@CloseTabIcon" Class="@CloseIconClass" Style="@CloseIconStyle" Clicked="EventCallback.Factory.Create<MouseEventArgs>(this, () => CloseTab.InvokeAsync(context))" />
</Tooltip>
}
else
{
<IconButton Icon="@CloseTabIcon" Class="@CloseIconClass" Style="@CloseIconStyle" Clicked="EventCallback.Factory.Create<MouseEventArgs>(this, () => CloseTab.InvokeAsync(context))" />
<GlyphButton Glyph="@CloseTabIcon" Class="@CloseIconClass" Style="@CloseIconStyle" Clicked="EventCallback.Factory.Create<MouseEventArgs>(this, () => CloseTab.InvokeAsync(context))" />
}
}
;

View File

@ -14,7 +14,7 @@
@if (_showScrollButtons)
{
<div class="tabs-scroll-button">
<IconButton Icon="@_prevIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollPrev())" Disabled="@_prevButtonDisabled" />
<GlyphButton Glyph="@_prevIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollPrev())" Disabled="@_prevButtonDisabled" />
</div>
}
<div @ref="@_tabsContentSize" class="tabs-toolbar-content">
@ -52,7 +52,7 @@
@if (_showScrollButtons)
{
<div class="tabs-scroll-button">
<IconButton Icon="@_nextIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollNext())" Disabled="@_nextButtonDisabled" />
<GlyphButton Glyph="@_nextIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollNext())" Disabled="@_nextButtonDisabled" />
</div>
}
@if (HeaderPosition == TabHeaderPosition.After && Header != null)
@ -91,11 +91,11 @@
}
else if (String.IsNullOrEmpty(panel.Text) && !String.IsNullOrEmpty(panel.Icon))
{
<Icon Icon="@panel.Icon" Color="@IconColor" />
<Icon Glyph="@panel.Icon" Color="@IconColor" />
}
else if (!String.IsNullOrEmpty(panel.Text) && !String.IsNullOrEmpty(panel.Icon))
{
<Icon Icon="@panel.Icon" Color="@IconColor" Class="tab-icon-text" />
<Icon Glyph="@panel.Icon" Color="@IconColor" Class="tab-icon-text" />
@((MarkupString)panel.Text)
}
@if (TabPanelHeaderPosition == TabHeaderPosition.After && TabPanelHeader != null)

View File

@ -20,7 +20,7 @@
@if (!string.IsNullOrEmpty(Icon))
{
<div class="treeview-item-icon">
<Icon Icon="@Icon" Color="@IconColor" />
<Icon Glyph="@Icon" Color="@IconColor" />
</div>
}
@ -38,7 +38,7 @@
@if (!string.IsNullOrEmpty(EndIcon))
{
<div class="treeview-item-icon">
<Icon Icon="@EndIcon" Color="@EndIconColor" />
<Icon Glyph="@EndIcon" Color="@EndIconColor" />
</div>
}
}

View File

@ -5,7 +5,7 @@
<div class="treeview-item-arrow">
@if (Visible)
{
<IconButton Clicked="@ToggleAsync" Icon="@(Loading ? LoadingIcon : ExpandedIcon)" Color="@(Loading ? LoadingIconColor : ExpandedIconColor)" Class="@Classname"></IconButton>
<GlyphButton Clicked="@ToggleAsync" Glyph="@(Loading ? LoadingIcon : ExpandedIcon)" Color="@(Loading ? LoadingIconColor : ExpandedIconColor)" Class="@Classname"></GlyphButton>
}
</div>

View File

@ -20,4 +20,16 @@ public static class Helper
return false;
}
}
public static bool IsNumeric(string input)
{
try
{
var number = Double.Parse(input);
return true;
} catch
{
return false;
}
}
}