Prograss - Input field

features/refactor
stm 2 years ago
parent c5e97f9600
commit 3ea88592ce

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

@ -19,7 +19,7 @@
TextChanged="OnTextChanged" OnBlur="OnInputBlurred"
OnKeyDown="@this.OnInputKeyDown"
OnKeyUp="@this.OnInputKeyUp" autocomplete=@("disabled-"+Guid.NewGuid()) KeyUpPreventDefault="KeyUpPreventDefault"
Placeholder="@Placeholder" Immediate="true"
Placeholder="@Placeholder" ChangeTextImmediately="true"
InputMode="@InputMode" Pattern="@Pattern"
T="string" />

@ -2,20 +2,20 @@
@inherits ButtonBase
@using Connected.Extensions;
@using Connected.Components;
<Element disabled="@Disabled"
title="@GlyphTitle"
type="@ButtonType.ToDescription()"
title="@IconTitle"
type="@ButtonType.ToString()"
ClassList="@CompiledClassList.ToString()"
HtmlTag="@HtmlTag"
PreventOnClickPropagation="PreventOnClickPropagation"
@attributes="CustomAttributes"
@onclick="OnClick">
@if (!String.IsNullOrEmpty(Glyph))
@if (!String.IsNullOrEmpty(Icon))
{
<span name="glyph-container" class="glyph-button-label">
<Icon Glyph="@Glyph" />
<Icon Glyph="@Icon" />
</span>
}
else

@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class GlyphButton : ButtonBase
public partial class IconButton : ButtonBase
{
/// <summary>
/// Contains the default container classlist and the user defined classes.
@ -28,16 +28,16 @@ public partial class GlyphButton : ButtonBase
#region Content placeholders
/// <summary>
/// The Glyph that will be used in the component.
/// The Icon that will be used in the component.
/// </summary>
[Parameter]
public string? Glyph { get; set; }
public string? Icon { get; set; }
/// <summary>
/// GlyphTitle of the icon used for accessibility.
/// </summary>
[Parameter]
public string? GlyphTitle { get; set; }
public string? IconTitle { get; set; }
#endregion
#region Styling properties

@ -2,12 +2,12 @@
@inherits UIComponent
<GlyphButton aria-pressed="@Toggled.ToString()"
<IconButton aria-pressed="@Toggled.ToString()"
ClassList="@ClassList"
Clicked="Toggle"
Disabled="Disabled"
Glyph="@(Toggled ? ToggledGlyph : Glyph)"
GlyphTitle="@(Toggled && ToggledGlyphTitle != null ? ToggledGlyphTitle : GlyphTitle)"
Icon="@(Toggled ? ToggledGlyph : Glyph)"
IconTitle="@(Toggled && ToggledGlyphTitle != null ? ToggledGlyphTitle : GlyphTitle)"
Variant="Variant"
@attributes="CustomAttributes"
/>

@ -2,7 +2,7 @@
namespace Connected.Components;
public partial class ToggleGlyphButton : UIComponent
public partial class ToggleIconButton : UIComponent
{
#region EventCallbacks
/// <summary>

@ -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" OnClick="Previous" Color="ThemeColor.Inherit" />
<IconButton tabindex="1" aria-label="Go to previous" Class="@NavigationButtonsClassName" Style="z-index:3;opacity:0.75" Icon="@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)" OnClick="(() => MoveTo(current))" Color="ThemeColor.Inherit" />
<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" />
}
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" OnClick="Next" Color="ThemeColor.Inherit" />
<IconButton tabindex="2" aria-label="Go to next" Class="@NavigationButtonsClassName" Style="z-index:3;opacity:0.75" Icon="@NextIcon" Clicked="Next" Color="ThemeColor.Inherit" />
}
else
{

@ -27,7 +27,7 @@
}
@if (OnClose.HasDelegate || ChipSet?.AllClosable==true)
{
<IconButton Class="chip-close-button" Icon="@(String.IsNullOrEmpty(CloseIcon) ? $"{Icons.Material.Filled.Cancel}" : $"{CloseIcon}")" OnClick="OnCloseHandler" Size="Size.Small"/>
<IconButton Class="chip-close-button" Icon="@(String.IsNullOrEmpty(CloseIcon) ? $"{Icons.Material.Filled.Cancel}" : $"{CloseIcon}")" Clicked="OnCloseHandler" Size="Size.Small"/>
}
</span>
</div>

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

@ -128,8 +128,8 @@
<td class="table-cell @groupClass" colspan="1000" style="background-color:var(--mud-palette-background-grey);@groupStyle">
<IconButton
Class="table-row-expander"
Icon="@(g.IsExpanded ? Icons.Material.Filled.ExpandMore : Icons.Material.Filled.ChevronRight)"
OnClick="@(() => ToggleGroupExpansion(g))" />
Icon="@(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" OnClick="@filter.RemoveFilter" Size="@Size.Small" Style="align-self:flex-end"></IconButton>
<IconButton Class="remove-filter-button" Icon="@Icons.Material.Filled.Close" Clicked="@filter.RemoveFilter" Size="@Size.Small" Style="align-self:flex-end"></IconButton>
</Item>
<Item xs="4">
<Select T="string" Value="@f.Field" ValueChanged="@filter.FieldChanged" FullWidth="true" Label="Column" Dense="true" Margin="@Margin.Dense"
@ -401,12 +401,12 @@
@if (filter.dataType == typeof(string) && !(f.Operator ?? "").EndsWith("empty"))
{
<TextField T="string" Value="@filter._valueString" ValueChanged="@filter.StringValueChanged" FullWidth="true" Label="Value" Placeholder="Filter value" Margin="@Margin.Dense"
Immediate="true" Class="filter-input" />
ChangeTextImmediately="true" Class="filter-input" />
}
else if (filter.isNumber && !(f.Operator ?? "").EndsWith("empty"))
{
<NumericField T="double?" Value="@filter._valueNumber" ValueChanged="@filter.NumberValueChanged" FullWidth="true" Label="Value" Placeholder="Filter value" Margin="@Margin.Dense"
Immediate="true" Class="filter-input" Culture="@filter.filterColumn?.Culture" />
<NumericField T="double?" Value="@filter._valueNumber" ValueChanged="@filter.NumberValueChanged" FullWidth="true" Label="Value" Placeholder="Filter value" Margin="@Margin.Dense"
ChangeTextImmediately="true" Class="filter-input" Culture="@filter.filterColumn?.Culture" />
}
else if (filter.isEnum)
{
@ -442,7 +442,7 @@
else if (filter.dataType == typeof(Guid))
{
<TextField T="string" Value="@filter._valueString" ValueChanged="@filter.StringValueChanged" FullWidth="true" Label="Value" Placeholder="Filter value" Margin="@Margin.Dense"
Immediate="true" Class="filter-input" />
ChangeTextImmediately="true" Class="filter-input" />
}
</Item>
}
@ -461,12 +461,12 @@
@if (filter.dataType == typeof(string) && !(f.Operator ?? "").EndsWith("empty"))
{
<TextField T="string" Value="@filter._valueString" ValueChanged="@filter.StringValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense"
Immediate="true" Class="filter-input" />
ChangeTextImmediately="true" Class="filter-input" />
}
else if (filter.isNumber && !(f.Operator ?? "").EndsWith("empty"))
{
<NumericField T="double?" Value="@filter._valueNumber" ValueChanged="@filter.NumberValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense"
Immediate="true" Class="filter-input" Culture="@filter.filterColumn?.Culture" />
<NumericField T="double?" Value="@filter._valueNumber" ValueChanged="@filter.NumberValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense"
ChangeTextImmediately="true" Class="filter-input" Culture="@filter.filterColumn?.Culture" />
}
else if (filter.isEnum)
{
@ -501,8 +501,8 @@
}
else if (filter.dataType == typeof(Guid))
{
<TextField T="string" Value="@filter._valueString" ValueChanged="@filter.StringValueChanged" FullWidth="true" Label="Value" Placeholder="Filter value" Margin="@Margin.Dense"
Immediate="true" Class="filter-input" />
<TextField T="string" Value="@filter._valueString" ValueChanged="@filter.StringValueChanged" FullWidth="true" Label="Value" Placeholder="Filter value" Margin="@Margin.Dense"
ChangeTextImmediately="true" Class="filter-input" />
}
</Item>
}

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

@ -14,11 +14,11 @@
<Stack Row="true">
@if (dataType == typeof(string) && !(_operator ?? "").EndsWith("empty"))
{
<TextField T="string" Value="@_valueString" ValueChanged="@StringValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense" Immediate="true" />
<TextField T="string" Value="@_valueString" ValueChanged="@StringValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense" ChangeTextImmediately="true" />
}
else if (isNumber && !(_operator ?? "").EndsWith("empty"))
{
<NumericField T="double?" Culture="@Column.Culture" Value="@_valueNumber" ValueChanged="@NumberValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense" Immediate="true"></NumericField>
<NumericField T="double?" Culture="@Column.Culture" Value="@_valueNumber" ValueChanged="@NumberValueChanged" FullWidth="true" Placeholder="Filter value" Margin="@Margin.Dense" ChangeTextImmediately="true"></NumericField>
}
else if (isEnum)
{
@ -58,7 +58,7 @@
}
}
</Menu>
<GlyphButton Class="align-self-center" Icon="@Icons.Material.Filled.FilterAltOff" Size="@Size.Small" Clicked="@ClearFilter"></GlyphButton>
<IconButton Class="align-self-center" Icon="@Icons.Material.Filled.FilterAltOff" Size="@Size.Small" Clicked="@ClearFilter"></IconButton>
</Stack>
}
}

@ -41,11 +41,11 @@ else if (Column != null && !Column.Hidden)
{
if (_initialDirection == SortDirection.None)
{
<IconButton Icon="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" OnClick="@SortChangedAsync"></IconButton>
<IconButton Icon="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" Clicked="@SortChangedAsync"></IconButton>
}
else
{
<IconButton Icon="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" OnClick="@SortChangedAsync"></IconButton>
<IconButton Icon="@Column.SortIcon" Class="@sortIconClass" Size="@Size.Small" Clicked="@SortChangedAsync"></IconButton>
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" OnClick="@OpenFilters"></IconButton>
<IconButton Class="filter-button filtered" Icon="@Icons.Material.Filled.FilterAlt" Size="@Size.Small" Clicked="@OpenFilters"></IconButton>
}
else if (showFilterIcon)
{
<IconButton Class="filter-button" Icon="@Icons.Material.Outlined.FilterAlt" Size="@Size.Small" OnClick="@AddFilter"></IconButton>
<IconButton Class="filter-button" Icon="@Icons.Material.Outlined.FilterAlt" Size="@Size.Small" Clicked="@AddFilter"></IconButton>
}
}

@ -47,11 +47,11 @@
<div class="picker-calendar-header-switch">
@if (!FixYear.HasValue)
{
<IconButton aria-label="@prevLabel" Icon="@PreviousIcon" OnClick="OnPreviousYearClick" Class="flip-x-rtl" />
<IconButton aria-label="@prevLabel" Icon="@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" OnClick="OnNextYearClick" Class="flip-x-rtl" />
<IconButton aria-label="@nextLabel" Icon="@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" OnClick="OnPreviousMonthClick" />
<IconButton aria-label="@prevLabel" Class="picker-nav-button-prev mud-flip-x-rtl" Icon="@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" OnClick="OnNextMonthClick" />
<IconButton aria-label="@nextLabel" Class="picker-nav-button-next mud-flip-x-rtl" Icon="@NextIcon" Clicked="OnNextMonthClick" />
}
else
{

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

@ -60,7 +60,7 @@ public partial class Form : UIComponent, IDisposable, IForm
/// <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 Immediate="true".
/// i.e. when textfields have ChangeTextImmediately="true".
/// </summary>
[Parameter]
[Category(CategoryTypes.Form.Behavior)]

@ -72,10 +72,10 @@ public abstract class DebouncedInput<T> : InputBase<T>
protected override void OnParametersSet()
{
base.OnParametersSet();
// if input is to be debounced, makes sense to bind the change of the text to oninput
// so we set Immediate to true
if (DebounceInterval > 0)
Immediate = true;
// if input is to be debounced, makes sense to bind the change of the text to oninput
// so we set ChangeTextImmediately to true
if (DebounceInterval > 0)
ChangeTextImmediately = true;
}
private void SetTimer()

@ -2,162 +2,147 @@
@typeparam T
@inherits InputBase<T>
<InputControl Label="@Label"
Variant="@Variant"
HelperText="@HelperText"
HelperTextOnFocus="@HelperTextOnFocus"
CounterText="@GetCounterText()"
FullWidth="@FullWidth"
Class="@CompiledClassList.Build()"
Error="@HasErrors"
ErrorText="@ErrorText"
ErrorId="@ErrorId"
Disabled="@Disabled"
Margin="@Margin"
Required="@Required"
ForId="@FieldId">
<CascadingValue Name="SubscribeToParentForm" Value="@base.SubscribeToParentForm" IsFixed="true">
<div class="@CompiledWrapperClass.Build()">
@if (Adornment == Adornment.Start)
{
<InputAdornment Class="@AdornmentClassname"
Icon="@AdornmentIcon"
Color="@AdornmentColor"
Size="@IconSize"
Text="@AdornmentText"
Edge="@Edge.Start"
AdornmentClick="@OnAdornmentClick"
AriaLabel="@AdornmentAriaLabel"
<InputAdornment Class="@CompiledAdornmentClass.Build()"
Icon="@AdornmentIcon"
Color="@AdornmentColor"
Size="@IconSize"
Text="@AdornmentText"
Edge="@Edge.Start"
AdornmentClick="@OnAdornmentClick"
AriaLabel="@AdornmentAriaLabel"
/>
}
<InputContent>
@if (Lines > 1)
{
<textarea class="@InputClassname"
@ref="ElementReference"
rows="@Lines"
@attributes="CustomAttributes"
type="@InputTypeString"
placeholder="@Placeholder"
disabled=@Disabled
readonly="@ReadOnly"
inputmode="@InputMode.ToString()"
@oninput="OnInput"
@onchange="OnChange"
@onblur="@OnBlurred"
@onkeydown="@InvokeKeyDown"
@onkeypress="@InvokeKeyPress"
@onkeyup="@InvokeKeyUp"
@onpaste="@OnPaste"
value="@_internalText"
maxlength="@MaxLength"
@onkeydown:preventDefault="@KeyDownPreventDefault"
@onkeypress:preventDefault="@KeyPressPreventDefault"
@onkeyup:preventDefault="@KeyUpPreventDefault"
@onmousewheel="@OnMouseWheel"
@onwheel="@OnMouseWheel"
aria-invalid="@HasError.ToString().ToLower()"
aria-describedby="@ErrorId">
@Text
</textarea>
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
@*note: the value="@_internalText" is absolutely essential here. the inner html @Text is needed by tests checking it*@
}
else
{
<input class="@InputClassname"
@ref="ElementReference"
@attributes="CustomAttributes"
type="@InputTypeString"
value="@_internalText"
@oninput="OnInput"
@onchange="OnChange"
placeholder="@Placeholder"
disabled=@Disabled
readonly="@ReadOnly"
@onblur="@OnBlurred"
inputmode="@InputMode.ToString()"
pattern="@Pattern"
@onkeydown="@InvokeKeyDown"
@onkeypress="@InvokeKeyPress"
@onkeyup="@InvokeKeyUp"
maxlength="@MaxLength"
@onkeydown:preventDefault="KeyDownPreventDefault"
@onkeypress:preventDefault="@KeyPressPreventDefault"
@onkeyup:preventDefault="@KeyUpPreventDefault"
@onmousewheel="@OnMouseWheel"
@onwheel="@OnMouseWheel"
aria-invalid="@HasError.ToString().ToLower()"
aria-describedby="@ErrorId"
/>
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
@if (NumberOfLines > 1)
{
<textarea class="@CompiledInputClass.Build()"
@ref="ElementReference"
rows="@NumberOfLines"
style="@Style"
@attributes="@Attributes"
type="@InputTypeString"
placeholder="@Placeholder"
disabled=@Disabled
readonly="@ReadOnly"
inputmode="@InputMode.ToString()"
@oninput="OnInput"
@onchange="OnChange"
@onblur="@OnBlurred"
@onkeydown="@InvokeKeyDown"
@onkeypress="@InvokeKeyPress"
@onkeyup="@InvokeKeyUp"
@onpaste="@OnPaste"
value="@_internalText"
maxlength="@MaxLength"
@onkeydown:preventDefault="@KeyDownPreventDefault"
@onkeypress:preventDefault="@KeyPressPreventDefault"
@onkeyup:preventDefault="@KeyUpPreventDefault"
@onmousewheel="@OnMouseWheel"
@onwheel="@OnMouseWheel"
aria-invalid="@HasError.ToString().ToLower()"
aria-describedby="@ErrorId"
>
@Text
</textarea>
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
@*note: the value="@_internalText" is absolutely essential here. the inner html @Text is needed by tests checking it*@
}
else
{
<input class="@CompiledInputClass.Build()"
style="@Style"
@ref="ElementReference"
@attributes="@Attributes"
step="@Step"
type="@InputTypeString"
value="@_internalText"
@oninput="OnInput"
@onchange="OnChange"
placeholder="@Placeholder"
disabled=@Disabled
readonly="@ReadOnly"
@onblur="@OnBlurred"
inputmode="@InputMode.ToString()"
pattern="@Pattern"
@onkeydown="@InvokeKeyDown"
@onkeypress="@InvokeKeyPress"
@onkeyup="@InvokeKeyUp"
maxlength="@MaxLength"
@onkeydown:preventDefault="KeyDownPreventDefault"
@onkeypress:preventDefault="@KeyPressPreventDefault"
@onkeyup:preventDefault="@KeyUpPreventDefault"
@onmousewheel="@OnMouseWheel"
@onwheel="@OnMouseWheel"
aria-invalid="@HasError.ToString().ToLower()"
aria-describedby="@ErrorId"
/>
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
@if (Disabled) {
@*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="@InputClassname"
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
@onblur="@OnBlurred" @ref="@_elementReference1"
>
@ChildContent
</div>
}
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="@InputClassname"
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
tabindex="@(InputType == InputType.Hidden && ChildContent != null ? 0 : -1)"
@onblur="@OnBlurred" @ref="@_elementReference1"
>
@ChildContent
</div>
}
@if (Disabled) {
@*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()"
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
@onblur="@OnBlurred" @ref="@_elementReference1"
>
@ChildContent
</div>
}
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()"
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
tabindex="@(InputType == InputType.Hidden && ChildContent != null ? 0 : -1)"
@onblur="@OnBlurred" @ref="@_elementReference1"
>
@ChildContent
</div>
}
}
@if (_showClearable && !Disabled)
{
@if (_showClearable && !Disabled)
{
<IconButton ClassList="@CompiledClearButtonClassList.Build()"
Icon="@ClearIcon"
Clicked="@ClearButtonClickHandlerAsync"
tabindex="-1"
/>
}
}
@if (Adornment == Adornment.End)
{
<InputAdornment Class="@AdornmentClassname"
@if (Adornment == Adornment.End)
{
<InputAdornment Class="@CompiledAdornmentClass.Build()"
Icon="@AdornmentIcon"
Color="@AdornmentColor"
Size="@IconSize"
Text="@AdornmentText"
Edge="@Edge.End"
AdornmentClick="@OnAdornmentClick"
AriaLabel="@AdornmentAriaLabel"
/>
}
@if (Variant == Variant.Outlined)
{
<div class="input-outlined-border"></div>
}
Color="@AdornmentColor"
Size="@IconSize"
Text="@AdornmentText"
Edge="@Edge.End"
AdornmentClick="@OnAdornmentClick"
AriaLabel="@AdornmentAriaLabel"
/>
}
@if (!HideSpinButtons)
{
<div class="input-numeric-spin">
<Button Variant="Variant.Text" @onclick="OnIncrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon Icon="@NumericUpIcon" Size="@GetButtonSize()" />
</Button>
<Button Variant="Variant.Text" @onclick="OnDecrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon Icon="@NumericDownIcon" Size="@GetButtonSize()" />
</Button>
</div>
}
@if (Variant == Variant.Outlined)
{
<div class="input-outlined-border"></div>
}
</InputContent>
</CascadingValue>
</InputControl>
@if (!HideSpinButtons)
{
<div class="input-numeric-spin">
<Button Variant="Variant.Text" Clicked="OnIncrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon Icon="@NumericUpIcon" Size="@GetButtonSize()" />
</Button>
<Button Variant="Variant.Text" Clicked="OnDecrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
<Icon Icon="@NumericDownIcon" Size="@GetButtonSize()" />
</Button>
</div>
}
</div>

@ -9,39 +9,18 @@ namespace Connected.Components;
public partial class Input<T> : InputBase<T>
{
/*
* Debounce
*
*/
#region Event Callbacks
/// <summary>
/// The current character counter, displayed below the text field.
/// Paste hook for descendants.
/// </summary>
[Parameter] public string CounterText { get; set; }
private System.Timers.Timer _timer;
private double _debounceInterval;
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
/// <summary>
/// Interval to be awaited in MILLISECONDS before changing the Text value
/// </summary>
[Parameter]
public double TextChangeDelay
protected virtual async Task OnPaste(ClipboardEventArgs args)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
get => _debounceInterval;
set
{
if (NumericConverter<double>.AreEqual(_debounceInterval, value))
return;
_debounceInterval = value;
if (_debounceInterval == 0)
{
// not debounced, dispose timer if any
ClearTimer(suppressTick: false);
return;
}
SetTimer();
}
// do nothing
return;
}
/// <summary>
@ -81,126 +60,232 @@ public partial class Input<T> : InputBase<T>
return Task.CompletedTask;
}
protected override void OnParametersSet()
private void OnTimerTick(object sender, ElapsedEventArgs e)
{
base.OnParametersSet();
// if input is to be debounced, makes sense to bind the change of the text to oninput
// so we set Immediate to true
if (TextChangeDelay > 0)
Immediate = true;
InvokeAsync(OnTimerTickGuiThread).AndForget();
}
private void SetTimer()
private async Task OnTimerTickGuiThread()
{
if (_timer == null)
await base.UpdateValuePropertyAsync(false);
await OnDebounceIntervalElapsed.InvokeAsync(Text);
}
protected Task OnInput(ChangeEventArgs args)
{
if (!ChangeTextImmediately)
return Task.CompletedTask;
_isFocused = true;
return SetTextAsync(args?.Value as string);
}
protected async Task OnChange(ChangeEventArgs args)
{
if (TextChangeDelay > 0 && _timer != null)
{
_timer = new System.Timers.Timer();
_timer.Elapsed += OnTimerTick;
_timer.AutoReset = false;
_timer.Stop();
await UpdateValuePropertyAsync(false);
}
_timer.Interval = TextChangeDelay;
else
{
_internalText = args?.Value as string;
await OnInternalInputChanged.InvokeAsync(args);
if (!ChangeTextImmediately)
{
await SetTextAsync(args?.Value as string);
}
}
}
private void OnTimerTick(object sender, ElapsedEventArgs e)
/// <summary>
/// Invokes the callback when the Up arrow button is clicked when the input is set to <see cref="InputType.Number"/>.
/// Note: use the optimized control <see cref="NumericField{T}"/> if you need to deal with numbers.
/// </summary>
[Parameter] public EventCallback OnIncrement { get; set; }
/// <summary>
/// Invokes the callback when the Down arrow button is clicked when the input is set to <see cref="InputType.Number"/>.
/// Note: use the optimized control <see cref="NumericField{T}"/> if you need to deal with numbers.
/// </summary>
[Parameter] public EventCallback OnDecrement { get; set; }
/// <summary>
/// Button click event for clear button. Called after text and value has been cleared.
/// </summary>
[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
/// <summary>
/// Mouse wheel event for input.
/// </summary>
[Parameter] public EventCallback<WheelEventArgs> OnMouseWheel { get; set; }
public override async ValueTask FocusAsync()
{
InvokeAsync(OnTimerTickGuiThread).AndForget();
try
{
if (InputType == InputType.Hidden && ChildContent != null)
await _elementReference1.FocusAsync();
else
await ElementReference.FocusAsync();
}
catch (Exception e)
{
Console.WriteLine("Input.FocusAsync: " + e.Message);
}
}
private async Task OnTimerTickGuiThread()
public override ValueTask BlurAsync()
{
await base.UpdateValuePropertyAsync(false);
await OnDebounceIntervalElapsed.InvokeAsync(Text);
return ElementReference.BlurAsync();
}
private void ClearTimer(bool suppressTick = false)
public override ValueTask SelectAsync()
{
if (_timer == null)
return;
var wasEnabled = _timer.Enabled;
_timer.Stop();
_timer.Elapsed -= OnTimerTick;
_timer.Dispose();
_timer = null;
if (wasEnabled && !suppressTick)
OnTimerTickGuiThread().AndForget();
return ElementReference.SelectAsync();
}
/*
* Debounce end
*/
public override ValueTask SelectRangeAsync(int pos1, int pos2)
{
return ElementReference.SelectRangeAsync(pos1, pos2);
}
protected CssBuilder CompiledHelperContainerClassList
private void UpdateClearable(object value)
{
get
{
return new CssBuilder("input-control-helper-container")
.AddClass($"px-1", Variant == Variant.Filled)
.AddClass($"px-2", Variant == Variant.Outlined)
.AddClass($"px-1", Variant == Variant.Text)
.AddClass(CompiledClearButtonClassList.Build())
.AddClass(CompiledHelperClassList.Build());
}
var showClearable = Clearable && ((value is string stringValue && !string.IsNullOrWhiteSpace(stringValue)) || (value is not string && value is not null));
if (_showClearable != showClearable)
_showClearable = showClearable;
}
protected CssBuilder CompiledClearButtonClassList
protected override async Task UpdateTextPropertyAsync(bool updateValue)
{
get
{
return new CssBuilder()
.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);
}
await base.UpdateTextPropertyAsync(updateValue);
if (Clearable)
UpdateClearable(Text);
}
protected virtual async Task ClearButtonClickHandlerAsync(MouseEventArgs e)
{
await SetTextAsync(string.Empty, updateValue: true);
await ElementReference.FocusAsync();
await OnClearButtonClick.InvokeAsync(e);
}
/// <summary>
/// A space separated list of class names, added on top of the default helper container class list.
/// Custom input Attributes
/// </summary>
[Parameter]
public string? HelperContainerClassList { get; set; }
[Parameter] public Dictionary<string, object>? Attributes { get; set; } = null;
#endregion
#region Style properties
protected CssBuilder CompiledHelperClassList
/// <summary>
/// Type of the input element. It should be a valid HTML5 input type.
/// </summary>
[Parameter] public InputType InputType { get; set; } = InputType.Text;
private string ClearIcon = Icons.Material.Filled.Clear;
private string NumericUpIcon = Icons.Material.Filled.KeyboardArrowUp;
private string NumericDownIcon = Icons.Material.Filled.KeyboardArrowDown;
/// <summary>
/// Hides the spin buttons"/>
/// </summary>
[Parameter] public bool HideSpinButtons { get; set; } = true;
/// <summary>
/// Show clear button.
/// </summary>
[Parameter] public bool Clearable { get; set; } = false;
#region Wrapper class
[Parameter]
public string WrapperClass { get; set; } = string.Empty;
protected CssBuilder CompiledWrapperClass
{
get
{
return new CssBuilder("input-helper-text")
.AddClass("input-helper-onfocus", HelperTextOnFocus)
.AddClass(HelperClassList);
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);
}
}
/// <summary>
/// A space separated list of class names, added on top of the default helper class list.
/// </summary>
[Parameter]
public string? HelperClassList { get; set; }
#endregion
/*protected string HelperContainer =>
new CssBuilder("input-control-helper-container")
.AddClass($"px-1", Variant == Variant.Filled)
.AddClass($"px-2", Variant == Variant.Outlined)
.Build();
#region Input field class
[Parameter]
public string InputClass { get; set; } = string.Empty;
protected CssBuilder CompiledInputClass
{
get
{
return 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(InputClass);
}
}
#endregion
protected string HelperClass =>
new CssBuilder("input-helper-text")
.AddClass("input-helper-onfocus", HelperTextOnFocus)
.Build();*/
#region Adornment class
[Parameter]
public string AdornmentClass { get; set; } = string.Empty;
protected CssBuilder CompiledAdornmentClass
{
get
{
return 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(AdornmentClass);
}
}
#endregion
private string GetCounterText()
#region Clear icon class
[Parameter]
public string ClearButtonClass { get; set; } = string.Empty;
protected CssBuilder CompiledClearButtonClassList
{
string result = Text.Length.ToString();
if (string.IsNullOrEmpty(Text)) result = "0";
return result;
get
{
return new CssBuilder()
.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);
}
}
#endregion
protected string Classname => InputCssHelper.GetClassname(this,
() => HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder));
#region Helper container class
protected CssBuilder CompiledClassList
/// <summary>
/// A space separated list of class names, added on top of the default helper container class list.
/// </summary>
[Parameter]
public string? HelperContainerClass { get; set; }
protected CssBuilder CompiledHelperContainerClassList
{
get
{
@ -208,304 +293,149 @@ 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(CompiledClearButtonClassList.Build())
.AddClass(CompiledHelperClassList.Build());
.AddClass(HelperContainerClass);
}
}
protected string InputClassname => InputCssHelper.GetInputClassname(this);
#endregion
protected string AdornmentClassname => InputCssHelper.GetAdornmentClassname(this);
#region Error container class -- still needs implementation
#endregion
/// <summary>
/// Type of the input element. It should be a valid HTML5 input type.
/// </summary>
[Parameter] public InputType InputType { get; set; } = InputType.Text;
#region Counter container class -- still needs implementation
internal override InputType GetInputType() => InputType;
#endregion
protected string InputTypeString => InputType.ToDescription();
#endregion
/*
private bool IsDecimalNumber(string type)
#region Content placeholders
/// <summary>
/// The current character counter, displayed below the text field.
/// </summary>
[Parameter] public string CounterText { get; set; }
private System.Timers.Timer _timer;
private double _textChangeInterval;
/// <summary>
/// Interval to be awaited in MILLISECONDS before changing the Text value
/// </summary>
[Parameter]
public double TextChangeDelay
{
switch (type.ToLower())
get => _textChangeInterval;
set
{
case "double":
case "float":
return true;
default:
return false;
if (NumericConverter<double>.AreEqual(_textChangeInterval, value))
return;
_textChangeInterval = value;
if (_textChangeInterval == 0)
{
// not debounced, dispose timer if any
ClearTimer(suppressTick: false);
return;
}
SetTimer();
}
}
private bool IsNumber(string s)
private void SetTimer()
{
bool result = false;
try
{
double d;
result= double.TryParse(s, out d);
} catch
if (_timer == null)
{
result = false;
_timer = new System.Timers.Timer();
_timer.Elapsed += OnTimerTick;
_timer.AutoReset = false;
}
return result;
_timer.Interval = TextChangeDelay;
}
private string ValidateInput(string value)
private void ClearTimer(bool suppressTick = false)
{
string result = value;
if (value is not null)
{
var expectedType = typeof(T).Name;
if (IsNumericType(expectedType))
{
if (IsNumber(value.ToString()))
{
result = value.ToString();
}
else
{
if (IsDecimalNumber(value.ToString()))
result = Regex.Replace(value.ToString(), "[^0-9.]", "");
else result = Regex.Replace(value.ToString(), "[^0-9]", "");
}
}
else
{
result = value;
}
}
return result;
}*/
if (_timer == null)
return;
var wasEnabled = _timer.Enabled;
_timer.Stop();
_timer.Elapsed -= OnTimerTick;
_timer.Dispose();
_timer = null;
if (wasEnabled && !suppressTick)
OnTimerTickGuiThread().AndForget();
}
protected Task OnInput(ChangeEventArgs args)
{
if (!Immediate)
return Task.CompletedTask;
_isFocused = true;
return SetTextAsync(args?.Value as string);
/// <summary>
/// ChildContent of the MudInput will only be displayed if InputType.Hidden and if its not null.
/// </summary>
[Parameter] public RenderFragment ChildContent { get; set; }
private string TextCounter()
{
if (string.IsNullOrEmpty(Text)) return "0";
return Text.Length.ToString();
}
protected async Task OnChange(ChangeEventArgs args)
{
if (TextChangeDelay > 0 && _timer != null)
[Parameter]
public double Step { get; set; } = 1;
internal override InputType GetInputType() => InputType;
protected string InputTypeString => InputType.ToDescription();
public ElementReference ElementReference { get; private set; }
private ElementReference _elementReference1;
private Size GetButtonSize() => Margin == Margin.Dense ? Size.Small : Size.Medium;
private bool _showClearable;
private string _internalText;
#endregion
#region Lifecycle events
public override async Task SetParametersAsync(ParameterView parameters)
{
await base.SetParametersAsync(parameters);
//if (!_isFocused || _forceTextUpdate)
// _internalText = Text;
if (RuntimeLocation.IsServerSide && TextUpdateSuppression)
{
_timer.Stop();
await UpdateValuePropertyAsync(false);
// Text update suppression, only in BSS (not in WASM).
// This is a fix for #1012
if (!_isFocused || _forceTextUpdate)
_internalText = Text;
}
else
{
_internalText = args?.Value as string;
await OnInternalInputChanged.InvokeAsync(args);
if (!Immediate)
{
await SetTextAsync(args?.Value as string);
}
// in WASM (or in BSS with TextUpdateSuppression==false) we always update
_internalText = Text;
}
}
/// <summary>
/// Paste hook for descendants.
/// </summary>
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
protected virtual async Task OnPaste(ClipboardEventArgs args)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
// do nothing
return;
}
/// <summary>
/// ChildContent of the MudInput will only be displayed if InputType.Hidden and if its not null.
/// </summary>
[Parameter] public RenderFragment ChildContent { get; set; }
public ElementReference ElementReference { get; private set; }
private ElementReference _elementReference1;
public override async ValueTask FocusAsync()
{
try
{
if (InputType == InputType.Hidden && ChildContent != null)
await _elementReference1.FocusAsync();
else
await ElementReference.FocusAsync();
}
catch (Exception e)
{
Console.WriteLine("Input.FocusAsync: " + e.Message);
}
}
public override ValueTask BlurAsync()
{
return ElementReference.BlurAsync();
}
public override ValueTask SelectAsync()
{
return ElementReference.SelectAsync();
}
public override ValueTask SelectRangeAsync(int pos1, int pos2)
{
return ElementReference.SelectRangeAsync(pos1, pos2);
}
/// <summary>
/// Invokes the callback when the Up arrow button is clicked when the input is set to <see cref="InputType.Number"/>.
/// Note: use the optimized control <see cref="NumericField{T}"/> if you need to deal with numbers.
/// </summary>
[Parameter] public EventCallback OnIncrement { get; set; }
/// <summary>
/// Invokes the callback when the Down arrow button is clicked when the input is set to <see cref="InputType.Number"/>.
/// Note: use the optimized control <see cref="MudNumericField{T}"/> if you need to deal with numbers.
/// </summary>
[Parameter] public EventCallback OnDecrement { get; set; }
/// <summary>
/// Hides the spin buttons for <see cref="MudNumericField{T}"/>
/// </summary>
[Parameter] public bool HideSpinButtons { get; set; } = true;
/// <summary>
/// Show clear button.
/// </summary>
[Parameter] public bool Clearable { get; set; } = false;
/// <summary>
/// Button click event for clear button. Called after text and value has been cleared.
/// </summary>
[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
/// <summary>
/// Mouse wheel event for input.
/// </summary>
[Parameter] public EventCallback<WheelEventArgs> OnMouseWheel { get; set; }
/// <summary>
/// Custom clear icon.
/// </summary>
[Parameter] public string ClearIcon { get; set; } = Icons.Material.Filled.Clear;
/// <summary>
/// Custom numeric up icon.
/// </summary>
[Parameter] public string NumericUpIcon { get; set; } = Icons.Material.Filled.KeyboardArrowUp;
/// <summary>
/// Custom numeric down icon.
/// </summary>
[Parameter] public string NumericDownIcon { get; set; } = Icons.Material.Filled.KeyboardArrowDown;
private Size GetButtonSize() => Margin == Margin.Dense ? Size.Small : Size.Medium;
private bool _showClearable;
private void UpdateClearable(object value)
{
var showClearable = Clearable && ((value is string stringValue && !string.IsNullOrWhiteSpace(stringValue)) || (value is not string && value is not null));
if (_showClearable != showClearable)
_showClearable = showClearable;
}
protected override async Task UpdateTextPropertyAsync(bool updateValue)
{
await base.UpdateTextPropertyAsync(updateValue);
if (Clearable)
UpdateClearable(Text);
}
/*protected override async Task UpdateValuePropertyAsync(bool updateText)
{
await base.UpdateValuePropertyAsync(updateText);
if (Clearable)
UpdateClearable(Value);
}*/
protected virtual async Task ClearButtonClickHandlerAsync(MouseEventArgs e)
{
await SetTextAsync(string.Empty, updateValue: true);
await ElementReference.FocusAsync();
await OnClearButtonClick.InvokeAsync(e);
}
private string _internalText;
public override async Task SetParametersAsync(ParameterView parameters)
{
await base.SetParametersAsync(parameters);
//if (!_isFocused || _forceTextUpdate)
// _internalText = Text;
if (RuntimeLocation.IsServerSide && TextUpdateSuppression)
{
// Text update suppression, only in BSS (not in WASM).
// This is a fix for #1012
if (!_isFocused || _forceTextUpdate)
_internalText = Text;
}
else
{
// in WASM (or in BSS with TextUpdateSuppression==false) we always update
_internalText = Text;
}
string baseTypeName = typeof(T).Name;
if (IsNumericType(baseTypeName) && InputType !=InputType.Number)
if (Helper.IsNumericType(typeof(T).Name) && InputType != InputType.Number)
{
InputType = InputType.Number;
}
}
}
private bool IsNumericType(string type)
protected override void OnParametersSet()
{
switch (type.ToLower())
{
case "uint16":
case "uint32":
case "uint64":
case "int16":
case "int32":
case "int64":
case "int":
case "double":
case "decimal":
case "float":
return true;
default:
return false;
}
base.OnParametersSet();
// if input is to be debounced, makes sense to bind the change of the text to oninput
// so we set ChangeTextImmediately to true
if (TextChangeDelay > 0)
ChangeTextImmediately = true;
}
#endregion
/// <summary>
/// Sets the input text from outside programmatically
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public Task SetText(string text)
{
_internalText = text;
return SetTextAsync(text);
}
// Certain HTML5 inputs (dates and color) have a native placeholder
private bool HasNativeHtmlPlaceholder()
{
return GetInputType() is InputType.Color or InputType.Date or InputType.DateTimeLocal or InputType.Month
or InputType.Time or InputType.Week;
}
}
public class InputString : Input<string> { }
}

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

@ -39,7 +39,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Behavior)]
public bool Immediate { get; set; }
public bool ChangeTextImmediately { get; set; } = true;
/// <summary>
/// If true, the input will not have an underline.
@ -170,7 +170,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Behavior)]
public int Lines { get; set; } = 1;
public int NumberOfLines { get; set; } = 1;
/// <summary>
/// The text to be displayed.

@ -15,10 +15,10 @@
</div>
}
<input @ref="_elementReferenceStart" @attributes="CustomAttributes" type="@InputTypeString" class="@InputClassname" @bind-value="@TextStart" @bind-value:event="@((Immediate ? "oninput" : "onchange"))"
<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" />
<input @ref="_elementReferenceEnd" @attributes="CustomAttributes" type="@InputTypeString" class="@InputClassname" @bind-value="@TextEnd" @bind-value:event="@((Immediate ? "oninput" : "onchange"))" inputmode="@InputMode.ToString()" pattern="@Pattern"
<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" />
@if (Adornment == Adornment.End)

@ -58,7 +58,7 @@
Color="@ThemeColor.Default"
Icon="@ClearIcon"
Size="@Size.Small"
OnClick="@HandleClearButton"/>
Clicked="@HandleClearButton" />
}
@if (Adornment == Adornment.End)

@ -33,7 +33,7 @@
}
else
{
<IconButton Variant="@Variant" Icon="@Icon" Color="@Color" Size="@Size" Disabled="@Disabled" DisableRipple="@DisableRipple" DisableElevation="@DisableElevation" @onclick="@ToggleMenu" @ontouchend="@(ActivationEvent == MouseEvent.RightClick ? ToggleMenuTouch : null)" @oncontextmenu="@(ActivationEvent==MouseEvent.RightClick ? ToggleMenu : null)" />
<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)" />
}
@* 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">

@ -39,7 +39,7 @@
AdornmentColor="@AdornmentColor"
IconSize="@IconSize"
Error="@HasError"
Immediate="@(Immediate)"
ChangeTextImmediately="@(ChangeTextImmediately)"
Margin="@Margin"
MaxLength="@MaxLength"
HideSpinButtons="@HideSpinButtons"

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

@ -39,7 +39,7 @@
</div>
}
<input class="slider-input" aria-valuenow="@Value" aria-valuemin="@_min" aria-valuemax="@_max" role="slider" @attributes="CustomAttributes" type="range" min="@_min" max="@_max" step="@_step" disabled="@Disabled"
@bind-value="@Text" @bind-value:event="@((Immediate == true ? "oninput" : "onchange"))" />
@bind-value="@Text" @bind-value:event="@((ChangeImmediately == true ? "oninput" : "onchange"))" />
@if (ValueLabel)
{
<div class="slider-value-label" style="@($"left:{width}%;")">

@ -117,7 +117,7 @@ public partial class Slider<T> : UIComponent
/// </summary>
[Parameter]
[Category(CategoryTypes.Slider.Behavior)]
public bool Immediate { get; set; } = true;
public bool ChangeImmediately { get; set; } = true;
/// <summary>
/// If true, displays the slider vertical.

@ -23,12 +23,12 @@
@if (ShowActionButton)
{
<Button Variant="@ActionVariant" Color="@ActionColor" OnClick="ActionClicked" DisableElevation>@Action</Button>
<Button Variant="@ActionVariant" Color="@ActionColor" Clicked="ActionClicked" DisableElevation>@Action</Button>
}
@if (ShowCloseIcon)
{
<IconButton Icon="@CloseIcon" Size="Size.Small" Class="ms-2" OnClick="CloseIconClicked" />
<IconButton Icon="@CloseIcon" Size="Size.Small" Class="ms-2" Clicked="CloseIconClicked" />
}
</div>
}

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

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

@ -15,19 +15,19 @@
}
else
{
<IconButton Size="@Size.Small" Icon="@Icons.Outlined.Edit" Class="pa-0" OnClick="StartEditingItem" Disabled="Context.EditButtonDisabled(Item)" />
<IconButton Size="@Size.Small" Icon="@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" OnClick="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
<IconButton Class="pa-0" Icon="@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" OnClick="CancelEdit" Size="@Size.Small" />
<IconButton Class="pa-0 ml-4" Icon="@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" OnClick="StartEditingItem" Disabled="Context.EditButtonDisabled(Item)" />
<IconButton Size="@Size.Small" Icon="@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" OnClick="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
<IconButton Class="pa-0" Icon="@Context.Table.CommitEditIcon" Clicked="FinishEdit" Size="@Size.Small" Disabled="@(!(Context?.Table.Validator?.IsValid ?? false))" />
</Tooltip>
@if (Context.Table.CanCancelEdit)
{
<Tooltip Text="@Context.Table.CancelEditTooltip">
<MudIconButton Class="pa-0 ml-4" Icon="@Context.Table.CancelEditIcon" OnClick="CancelEdit" Size="@Size.Small" />
<IconButton Class="pa-0 ml-4" Icon="@Context.Table.CancelEditIcon" Clicked="CancelEdit" Size="@Size.Small" />
</Tooltip>
}
</div>

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

@ -14,7 +14,7 @@
@if (_showScrollButtons)
{
<div class="tabs-scroll-button">
<GlyphButton Icon="@_prevIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollPrev())" Disabled="@_prevButtonDisabled" />
<IconButton Icon="@_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">
<GlyphButton Icon="@_nextIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollNext())" Disabled="@_nextButtonDisabled" />
<IconButton Icon="@_nextIcon" Color="@ScrollIconColor" Clicked="@((e) => ScrollNext())" Disabled="@_nextButtonDisabled" />
</div>
}
@if (HeaderPosition == TabHeaderPosition.After && Header != null)

@ -25,7 +25,7 @@
@ref="InputReference"
@attributes="CustomAttributes"
InputType="@InputType"
Lines="@Lines"
NumberOfLines="@NumberOfLines"
Variant="@Variant"
TextUpdateSuppression="@TextUpdateSuppression"
Value="@Text"
@ -44,7 +44,7 @@
OnAdornmentClick="@OnAdornmentClick"
Error="@HasError"
ErrorId="@ErrorId"
Immediate="@Immediate"
ChangeTextImmediately="@ChangeTextImmediately"
Margin="@Margin"
OnBlur="@OnBlurred"
OnKeyDown="@InvokeKeyDown"
@ -65,7 +65,7 @@
@attributes="CustomAttributes"
Mask="@_mask"
InputType="@InputType"
Lines="@Lines"
NumberOfLines="@NumberOfLines"
Variant="@Variant"
TextUpdateSuppression="@TextUpdateSuppression"
Value="@Text"
@ -82,7 +82,7 @@
IconSize="@IconSize"
OnAdornmentClick="@OnAdornmentClick"
Error="@HasError"
Immediate="@Immediate"
ChangeTextImmediately="@ChangeTextImmediately"
Margin="@Margin"
OnBlur="@OnBlurred"
Clearable="@Clearable"

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

@ -0,0 +1,23 @@
namespace Connected.Utilities;
public static class Helper
{
public static bool IsNumericType(string type)
{
switch (type.ToLower())
{
case "uint16":
case "uint32":
case "uint64":
case "int16":
case "int32":
case "int64":
case "int":
case "double":
case "decimal":
case "float":
return true;
default:
return false;
}
}
}
Loading…
Cancel
Save