Compare commits
No commits in common. "edcc8661e391f12743c2faecf25dba895f89d2b7" and "25606b926b9d53576a8d2f5801547be1a49fb6e3" have entirely different histories.
edcc8661e3
...
25606b926b
@ -139,11 +139,11 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasErrors => HasError || ConversionError || ValidationErrors.Count > 0;
|
public bool HasErrors => HasError || ConversionError || ValidationErrors.Count > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return the validation error text or the conversion error message.
|
/// Return the validation error text or the conversion error message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Error text/message</returns>
|
/// <returns>Error text/message</returns>
|
||||||
public string? GetErrorText(bool test = false)
|
public string? GetErrorText()
|
||||||
{
|
{
|
||||||
// ErrorText is either set from outside or the first validation error
|
// ErrorText is either set from outside or the first validation error
|
||||||
if (!IsNullOrWhiteSpace(ErrorText))
|
if (!IsNullOrWhiteSpace(ErrorText))
|
||||||
@ -152,8 +152,6 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
|
|||||||
if (!IsNullOrWhiteSpace(ConversionErrorMessage))
|
if (!IsNullOrWhiteSpace(ConversionErrorMessage))
|
||||||
return ConversionErrorMessage;
|
return ConversionErrorMessage;
|
||||||
|
|
||||||
if (test) return "Error: test";
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Timers;
|
using System.Timers;
|
||||||
using Connected.Annotations;
|
using Connected.Annotations;
|
||||||
|
using Connected.Utilities.BindingConverters;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace Connected.Components;
|
namespace Connected.Components;
|
||||||
|
@ -3,23 +3,6 @@
|
|||||||
@inherits InputBase<T>
|
@inherits InputBase<T>
|
||||||
|
|
||||||
<div class="@Classname">
|
<div class="@Classname">
|
||||||
<InputControl Label="@Label"
|
|
||||||
Variant="@Variant"
|
|
||||||
HelperText="@HelperText"
|
|
||||||
HelperTextOnFocus="@HelperTextOnFocus"
|
|
||||||
CounterText="@GetCounterText()"
|
|
||||||
FullWidth="@FullWidth"
|
|
||||||
Class="@Classname"
|
|
||||||
Error="@HasErrors"
|
|
||||||
ErrorText="@ErrorText"
|
|
||||||
ErrorId="@ErrorId"
|
|
||||||
Disabled="@Disabled"
|
|
||||||
Margin="@Margin"
|
|
||||||
Required="@Required"
|
|
||||||
ForId="@FieldId">
|
|
||||||
<CascadingValue Name="SubscribeToParentForm" Value="@base.SubscribeToParentForm" IsFixed="true">
|
|
||||||
|
|
||||||
|
|
||||||
@if (Adornment == Adornment.Start)
|
@if (Adornment == Adornment.Start)
|
||||||
{
|
{
|
||||||
<InputAdornment Class="@AdornmentClassname"
|
<InputAdornment Class="@AdornmentClassname"
|
||||||
@ -32,135 +15,131 @@
|
|||||||
AriaLabel="@AdornmentAriaLabel"
|
AriaLabel="@AdornmentAriaLabel"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<InputContent>
|
|
||||||
@if (Lines > 1)
|
@if (Lines > 1)
|
||||||
{
|
{
|
||||||
<textarea class="@InputClassname"
|
<textarea class="@InputClassname"
|
||||||
@ref="ElementReference"
|
@ref="ElementReference"
|
||||||
rows="@Lines"
|
rows="@Lines"
|
||||||
@attributes="CustomAttributes"
|
@attributes="CustomAttributes"
|
||||||
type="@InputTypeString"
|
type="@InputTypeString"
|
||||||
placeholder="@Placeholder"
|
placeholder="@Placeholder"
|
||||||
disabled=@Disabled
|
disabled=@Disabled
|
||||||
readonly="@ReadOnly"
|
readonly="@ReadOnly"
|
||||||
inputmode="@InputMode.ToString()"
|
inputmode="@InputMode.ToString()"
|
||||||
@oninput="OnInput"
|
@oninput="OnInput"
|
||||||
@onchange="OnChange"
|
@onchange="OnChange"
|
||||||
@onblur="@OnBlurred"
|
@onblur="@OnBlurred"
|
||||||
@onkeydown="@InvokeKeyDown"
|
@onkeydown="@InvokeKeyDown"
|
||||||
@onkeypress="@InvokeKeyPress"
|
@onkeypress="@InvokeKeyPress"
|
||||||
@onkeyup="@InvokeKeyUp"
|
@onkeyup="@InvokeKeyUp"
|
||||||
@onpaste="@OnPaste"
|
@onpaste="@OnPaste"
|
||||||
value="@_internalText"
|
value="@_internalText"
|
||||||
maxlength="@MaxLength"
|
maxlength="@MaxLength"
|
||||||
@onkeydown:preventDefault="@KeyDownPreventDefault"
|
@onkeydown:preventDefault="@KeyDownPreventDefault"
|
||||||
@onkeypress:preventDefault="@KeyPressPreventDefault"
|
@onkeypress:preventDefault="@KeyPressPreventDefault"
|
||||||
@onkeyup:preventDefault="@KeyUpPreventDefault"
|
@onkeyup:preventDefault="@KeyUpPreventDefault"
|
||||||
@onmousewheel="@OnMouseWheel"
|
@onmousewheel="@OnMouseWheel"
|
||||||
@onwheel="@OnMouseWheel"
|
@onwheel="@OnMouseWheel"
|
||||||
aria-invalid="@HasError.ToString().ToLower()"
|
aria-invalid="@HasError.ToString().ToLower()"
|
||||||
aria-describedby="@ErrorId"
|
aria-describedby="@ErrorId"
|
||||||
>
|
>
|
||||||
@Text
|
@Text
|
||||||
</textarea>
|
</textarea>
|
||||||
|
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
|
||||||
@*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*@
|
||||||
@*note: the value="@_internalText" is absolutely essential here. the inner html @Text is needed by tests checking it*@
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
<input class="@InputClassname"
|
||||||
<input class="@InputClassname"
|
@ref="ElementReference"
|
||||||
@ref="ElementReference"
|
@attributes="CustomAttributes"
|
||||||
@attributes="CustomAttributes"
|
type="@InputTypeString"
|
||||||
type="@InputTypeString"
|
value="@_internalText"
|
||||||
value="@_internalText"
|
@oninput="OnInput"
|
||||||
@oninput="OnInput"
|
@onchange="OnChange"
|
||||||
@onchange="OnChange"
|
placeholder="@Placeholder"
|
||||||
placeholder="@Placeholder"
|
disabled=@Disabled
|
||||||
disabled=@Disabled
|
readonly="@ReadOnly"
|
||||||
readonly="@ReadOnly"
|
@onblur="@OnBlurred"
|
||||||
@onblur="@OnBlurred"
|
inputmode="@InputMode.ToString()"
|
||||||
inputmode="@InputMode.ToString()"
|
pattern="@Pattern"
|
||||||
pattern="@Pattern"
|
@onkeydown="@InvokeKeyDown"
|
||||||
@onkeydown="@InvokeKeyDown"
|
@onkeypress="@InvokeKeyPress"
|
||||||
@onkeypress="@InvokeKeyPress"
|
@onkeyup="@InvokeKeyUp"
|
||||||
@onkeyup="@InvokeKeyUp"
|
maxlength="@MaxLength"
|
||||||
maxlength="@MaxLength"
|
@onkeydown:preventDefault="KeyDownPreventDefault"
|
||||||
@onkeydown:preventDefault="KeyDownPreventDefault"
|
@onkeypress:preventDefault="@KeyPressPreventDefault"
|
||||||
@onkeypress:preventDefault="@KeyPressPreventDefault"
|
@onkeyup:preventDefault="@KeyUpPreventDefault"
|
||||||
@onkeyup:preventDefault="@KeyUpPreventDefault"
|
@onmousewheel="@OnMouseWheel"
|
||||||
@onmousewheel="@OnMouseWheel"
|
@onwheel="@OnMouseWheel"
|
||||||
@onwheel="@OnMouseWheel"
|
aria-invalid="@HasError.ToString().ToLower()"
|
||||||
aria-invalid="@HasError.ToString().ToLower()"
|
aria-describedby="@ErrorId"
|
||||||
aria-describedby="@ErrorId"
|
/>
|
||||||
/>
|
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
|
||||||
@*Note: double mouse wheel handlers needed for Firefox because it doesn't know onmousewheel*@
|
|
||||||
|
|
||||||
@if (Disabled) {
|
@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.
|
@*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
|
In Disabled state the tabindex attribute must NOT be set at all or else it will get focus on click
|
||||||
*@
|
*@
|
||||||
<div class="@InputClassname"
|
<div class="@InputClassname"
|
||||||
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
|
style="@("display:"+(InputType == InputType.Hidden && ChildContent != null ? "inline" : "none"))"
|
||||||
@onblur="@OnBlurred" @ref="@_elementReference1"
|
@onblur="@OnBlurred" @ref="@_elementReference1"
|
||||||
>
|
>
|
||||||
@ChildContent
|
@ChildContent
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
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 (_showClearable && !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.*@
|
||||||
|
<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 (Adornment == Adornment.End)
|
|
||||||
{
|
|
||||||
<InputAdornment Class="@AdornmentClassname"
|
|
||||||
Icon="@AdornmentIcon"
|
|
||||||
Color="@AdornmentColor"
|
|
||||||
Size="@IconSize"
|
|
||||||
Text="@AdornmentText"
|
|
||||||
Edge="@Edge.End"
|
|
||||||
AdornmentClick="@OnAdornmentClick"
|
|
||||||
AriaLabel="@AdornmentAriaLabel"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (Variant == Variant.Outlined)
|
@if (_showClearable && !Disabled)
|
||||||
{
|
{
|
||||||
<div class="input-outlined-border"></div>
|
<IconButton Class="@ClearButtonClassname"
|
||||||
}
|
Color="@ThemeColor.Default"
|
||||||
|
Icon="@ClearIcon"
|
||||||
|
Size="@Size.Small"
|
||||||
|
OnClick="@ClearButtonClickHandlerAsync"
|
||||||
|
tabindex="-1"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
@if (!HideSpinButtons)
|
@if (Adornment == Adornment.End)
|
||||||
{
|
{
|
||||||
<div class="input-numeric-spin">
|
<InputAdornment Class="@AdornmentClassname"
|
||||||
<Button Variant="Variant.Text" @onclick="OnIncrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
|
Icon="@AdornmentIcon"
|
||||||
<Icon Icon="@NumericUpIcon" Size="@GetButtonSize()" />
|
Color="@AdornmentColor"
|
||||||
</Button>
|
Size="@IconSize"
|
||||||
<Button Variant="Variant.Text" @onclick="OnDecrement" Disabled="@(Disabled || ReadOnly)" tabindex="-1">
|
Text="@AdornmentText"
|
||||||
<Icon Icon="@NumericDownIcon" Size="@GetButtonSize()" />
|
Edge="@Edge.End"
|
||||||
</Button>
|
AdornmentClick="@OnAdornmentClick"
|
||||||
</div>
|
AriaLabel="@AdornmentAriaLabel"
|
||||||
}
|
/>
|
||||||
|
}
|
||||||
</InputContent>
|
|
||||||
|
|
||||||
</CascadingValue>
|
|
||||||
|
|
||||||
</InputControl>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
@if (Variant == Variant.Outlined)
|
||||||
|
{
|
||||||
|
<div class="input-outlined-border"></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@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>
|
||||||
|
}
|
||||||
|
</div>
|
@ -2,192 +2,12 @@
|
|||||||
using Connected.Utilities;
|
using Connected.Utilities;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using System.Numerics;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Timers;
|
|
||||||
|
|
||||||
namespace Connected.Components;
|
namespace Connected.Components;
|
||||||
|
|
||||||
public partial class Input<T> : InputBase<T>
|
public partial class Input<T> : InputBase<T>
|
||||||
{
|
{
|
||||||
|
protected string Classname => InputCssHelper.GetClassname(this,
|
||||||
/*
|
|
||||||
* Debounce
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current character counter, displayed below the text field.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public string CounterText { get; set; }
|
|
||||||
|
|
||||||
private System.Timers.Timer _timer;
|
|
||||||
private double _debounceInterval;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interval to be awaited in MILLISECONDS before changing the Text value
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public double TextChangeDelay
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// callback to be called when the debounce interval has elapsed
|
|
||||||
/// receives the Text as a parameter
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public EventCallback<string> OnDebounceIntervalElapsed { get; set; }
|
|
||||||
|
|
||||||
protected Task OnDebounceChange()
|
|
||||||
{
|
|
||||||
|
|
||||||
if (TextChangeDelay > 0 && _timer != null)
|
|
||||||
{
|
|
||||||
_timer.Stop();
|
|
||||||
return base.UpdateValuePropertyAsync(false);
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task UpdateValuePropertyAsync(bool updateText)
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
return base.UpdateValuePropertyAsync(updateText);
|
|
||||||
}
|
|
||||||
// if debounce interval is 0 we update immediately
|
|
||||||
if (TextChangeDelay <= 0 || _timer == null)
|
|
||||||
return base.UpdateValuePropertyAsync(updateText);
|
|
||||||
// If a debounce interval is defined, we want to delay the update of Value property.
|
|
||||||
_timer.Stop();
|
|
||||||
// restart the timer while user is typing
|
|
||||||
_timer.Start();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (TextChangeDelay > 0)
|
|
||||||
Immediate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetTimer()
|
|
||||||
{
|
|
||||||
if (_timer == null)
|
|
||||||
{
|
|
||||||
_timer = new System.Timers.Timer();
|
|
||||||
_timer.Elapsed += OnTimerTick;
|
|
||||||
_timer.AutoReset = false;
|
|
||||||
}
|
|
||||||
_timer.Interval = TextChangeDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTimerTick(object sender, ElapsedEventArgs e)
|
|
||||||
{
|
|
||||||
InvokeAsync(OnTimerTickGuiThread).AndForget();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnTimerTickGuiThread()
|
|
||||||
{
|
|
||||||
await base.UpdateValuePropertyAsync(false);
|
|
||||||
await OnDebounceIntervalElapsed.InvokeAsync(Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearTimer(bool suppressTick = false)
|
|
||||||
{
|
|
||||||
if (_timer == null)
|
|
||||||
return;
|
|
||||||
var wasEnabled = _timer.Enabled;
|
|
||||||
_timer.Stop();
|
|
||||||
_timer.Elapsed -= OnTimerTick;
|
|
||||||
_timer.Dispose();
|
|
||||||
_timer = null;
|
|
||||||
if (wasEnabled && !suppressTick)
|
|
||||||
OnTimerTickGuiThread().AndForget();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Debounce end
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected CssBuilder CompiledHelperContainerClassList
|
|
||||||
{
|
|
||||||
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(HelperContainerClassList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A space separated list of class names, added on top of the default helper container class list.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? HelperContainerClassList { get; set; }
|
|
||||||
|
|
||||||
protected CssBuilder CompiledHelperClassList
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new CssBuilder("input-helper-text")
|
|
||||||
.AddClass("input-helper-onfocus", HelperTextOnFocus)
|
|
||||||
.AddClass(HelperClassList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A space separated list of class names, added on top of the default helper class list.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? HelperClassList { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*protected string HelperContainer =>
|
|
||||||
new CssBuilder("input-control-helper-container")
|
|
||||||
.AddClass($"px-1", Variant == Variant.Filled)
|
|
||||||
.AddClass($"px-2", Variant == Variant.Outlined)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
protected string HelperClass =>
|
|
||||||
new CssBuilder("input-helper-text")
|
|
||||||
.AddClass("input-helper-onfocus", HelperTextOnFocus)
|
|
||||||
.Build();*/
|
|
||||||
|
|
||||||
private string GetCounterText()
|
|
||||||
{
|
|
||||||
string result = Text.Length.ToString();
|
|
||||||
if (string.IsNullOrEmpty(Text)) result = "0";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string Classname => InputCssHelper.GetClassname(this,
|
|
||||||
() => HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder));
|
() => HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder));
|
||||||
|
|
||||||
protected string InputClassname => InputCssHelper.GetInputClassname(this);
|
protected string InputClassname => InputCssHelper.GetInputClassname(this);
|
||||||
@ -211,89 +31,23 @@ public partial class Input<T> : InputBase<T>
|
|||||||
|
|
||||||
protected string InputTypeString => InputType.ToDescription();
|
protected string InputTypeString => InputType.ToDescription();
|
||||||
|
|
||||||
/*
|
protected Task OnInput(ChangeEventArgs args)
|
||||||
|
|
||||||
private bool IsDecimalNumber(string type)
|
|
||||||
{
|
|
||||||
switch (type.ToLower())
|
|
||||||
{
|
|
||||||
case "double":
|
|
||||||
case "float":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsNumber(string s)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
double d;
|
|
||||||
result= double.TryParse(s, out d);
|
|
||||||
} catch
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ValidateInput(string value)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
protected Task OnInput(ChangeEventArgs args)
|
|
||||||
{
|
{
|
||||||
if (!Immediate)
|
if (!Immediate)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
_isFocused = true;
|
_isFocused = true;
|
||||||
return SetTextAsync(args?.Value as string);
|
return SetTextAsync(args?.Value as string);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task OnChange(ChangeEventArgs args)
|
protected async Task OnChange(ChangeEventArgs args)
|
||||||
{
|
{
|
||||||
if (TextChangeDelay > 0 && _timer != null)
|
_internalText = args?.Value as string;
|
||||||
{
|
await OnInternalInputChanged.InvokeAsync(args);
|
||||||
_timer.Stop();
|
if (!Immediate)
|
||||||
await UpdateValuePropertyAsync(false);
|
{
|
||||||
}
|
await SetTextAsync(args?.Value as string);
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
_internalText = args?.Value as string;
|
|
||||||
await OnInternalInputChanged.InvokeAsync(args);
|
|
||||||
if (!Immediate)
|
|
||||||
{
|
|
||||||
await SetTextAsync(args?.Value as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Paste hook for descendants.
|
/// Paste hook for descendants.
|
||||||
@ -410,12 +164,12 @@ public partial class Input<T> : InputBase<T>
|
|||||||
UpdateClearable(Text);
|
UpdateClearable(Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*protected override async Task UpdateValuePropertyAsync(bool updateText)
|
protected override async Task UpdateValuePropertyAsync(bool updateText)
|
||||||
{
|
{
|
||||||
await base.UpdateValuePropertyAsync(updateText);
|
await base.UpdateValuePropertyAsync(updateText);
|
||||||
if (Clearable)
|
if (Clearable)
|
||||||
UpdateClearable(Value);
|
UpdateClearable(Value);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
protected virtual async Task ClearButtonClickHandlerAsync(MouseEventArgs e)
|
protected virtual async Task ClearButtonClickHandlerAsync(MouseEventArgs e)
|
||||||
{
|
{
|
||||||
@ -443,41 +197,14 @@ public partial class Input<T> : InputBase<T>
|
|||||||
// in WASM (or in BSS with TextUpdateSuppression==false) we always update
|
// in WASM (or in BSS with TextUpdateSuppression==false) we always update
|
||||||
_internalText = Text;
|
_internalText = Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
string baseTypeName = typeof(T).Name;
|
|
||||||
if (IsNumericType(baseTypeName) && InputType !=InputType.Number)
|
|
||||||
{
|
|
||||||
InputType = InputType.Number;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsNumericType(string type)
|
/// <summary>
|
||||||
{
|
/// Sets the input text from outside programmatically
|
||||||
switch (type.ToLower())
|
/// </summary>
|
||||||
{
|
/// <param name="text"></param>
|
||||||
case "uint16":
|
/// <returns></returns>
|
||||||
case "uint32":
|
public Task SetText(string text)
|
||||||
case "uint64":
|
|
||||||
case "int16":
|
|
||||||
case "int32":
|
|
||||||
case "int64":
|
|
||||||
case "int":
|
|
||||||
case "double":
|
|
||||||
case "decimal":
|
|
||||||
case "float":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the input text from outside programmatically
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task SetText(string text)
|
|
||||||
{
|
{
|
||||||
_internalText = text;
|
_internalText = text;
|
||||||
return SetTextAsync(text);
|
return SetTextAsync(text);
|
||||||
|
@ -211,7 +211,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
|
|||||||
|
|
||||||
protected virtual async Task SetTextAsync(string text, bool updateValue = true)
|
protected virtual async Task SetTextAsync(string text, bool updateValue = true)
|
||||||
{
|
{
|
||||||
if (Text != text)
|
if (Text != text)
|
||||||
{
|
{
|
||||||
Text = text;
|
Text = text;
|
||||||
if (!string.IsNullOrWhiteSpace(Text))
|
if (!string.IsNullOrWhiteSpace(Text))
|
||||||
@ -220,7 +220,6 @@ public abstract class InputBase<T> : FormComponent<T, string>
|
|||||||
await UpdateValuePropertyAsync(false);
|
await UpdateValuePropertyAsync(false);
|
||||||
await TextChanged.InvokeAsync(Text);
|
await TextChanged.InvokeAsync(Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -330,11 +329,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
|
|||||||
/// Fired when the Value property changes.
|
/// Fired when the Value property changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<T> ValueChanged
|
public EventCallback<T> ValueChanged { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of this input element.
|
/// The value of this input element.
|
||||||
|
@ -387,7 +387,7 @@ public partial class Picker<T> : FormComponent<T, string>
|
|||||||
|
|
||||||
protected override void ResetValue()
|
protected override void ResetValue()
|
||||||
{
|
{
|
||||||
_inputReference?.InputReference.Reset();
|
_inputReference?.Reset();
|
||||||
base.ResetValue();
|
base.ResetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
@namespace Connected.Components
|
@namespace Connected.Components
|
||||||
@typeparam T
|
@typeparam T
|
||||||
@inherits InputBase<T>
|
@inherits DebouncedInput<T>
|
||||||
|
|
||||||
<CascadingValue Name="SubscribeToParentForm" Value="@base.SubscribeToParentForm" IsFixed="true">
|
<CascadingValue Name="SubscribeToParentForm" Value="@SubscribeToParentForm" IsFixed="true">
|
||||||
<InputControl Label="@Label"
|
<InputControl Label="@Label"
|
||||||
Variant="@Variant"
|
Variant="@Variant"
|
||||||
HelperText="@HelperText"
|
HelperText="@HelperText"
|
||||||
HelperTextOnFocus="@HelperTextOnFocus"
|
HelperTextOnFocus="@HelperTextOnFocus"
|
||||||
CounterText="@GetCounterText()"
|
CounterText="@GetCounterText()"
|
||||||
FullWidth="@FullWidth"
|
FullWidth="@FullWidth"
|
||||||
class="@ClassList"
|
Class="@ClassList"
|
||||||
Error="@HasErrors"
|
Error="@HasErrors"
|
||||||
ErrorText="@GetErrorText()"
|
ErrorText="@GetErrorText()"
|
||||||
ErrorId="@ErrorId"
|
ErrorId="@ErrorId"
|
||||||
@ -48,6 +48,7 @@
|
|||||||
Margin="@Margin"
|
Margin="@Margin"
|
||||||
OnBlur="@OnBlurred"
|
OnBlur="@OnBlurred"
|
||||||
OnKeyDown="@InvokeKeyDown"
|
OnKeyDown="@InvokeKeyDown"
|
||||||
|
OnInternalInputChanged="OnChange"
|
||||||
OnKeyPress="@InvokeKeyPress"
|
OnKeyPress="@InvokeKeyPress"
|
||||||
OnKeyUp="@InvokeKeyUp"
|
OnKeyUp="@InvokeKeyUp"
|
||||||
KeyDownPreventDefault="KeyDownPreventDefault"
|
KeyDownPreventDefault="KeyDownPreventDefault"
|
||||||
@ -56,7 +57,6 @@
|
|||||||
HideSpinButtons="true"
|
HideSpinButtons="true"
|
||||||
Clearable="@Clearable"
|
Clearable="@Clearable"
|
||||||
OnClearButtonClick="@OnClearButtonClick"
|
OnClearButtonClick="@OnClearButtonClick"
|
||||||
Class="@CompiledClassList.Build()"
|
|
||||||
Pattern="@Pattern"/>
|
Pattern="@Pattern"/>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -83,10 +83,8 @@
|
|||||||
OnAdornmentClick="@OnAdornmentClick"
|
OnAdornmentClick="@OnAdornmentClick"
|
||||||
Error="@HasError"
|
Error="@HasError"
|
||||||
Immediate="@Immediate"
|
Immediate="@Immediate"
|
||||||
Margin="@Margin"
|
Margin="@Margin" OnBlur="@OnBlurred"
|
||||||
OnBlur="@OnBlurred"
|
|
||||||
Clearable="@Clearable"
|
Clearable="@Clearable"
|
||||||
Class="@CompiledClassList.Build()"
|
|
||||||
OnClearButtonClick="@OnClearButtonClick"/>
|
OnClearButtonClick="@OnClearButtonClick"/>
|
||||||
}
|
}
|
||||||
</CascadingValue>
|
</CascadingValue>
|
||||||
|
@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Components.Web;
|
|||||||
|
|
||||||
namespace Connected.Components;
|
namespace Connected.Components;
|
||||||
|
|
||||||
public partial class TextField<T> : InputBase<T>
|
public partial class TextField<T> : DebouncedInput<T>
|
||||||
{
|
{
|
||||||
private Mask? _maskReference;
|
private Mask? _maskReference;
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ public partial class TextField<T> : InputBase<T>
|
|||||||
|
|
||||||
internal override InputType GetInputType() => InputType;
|
internal override InputType GetInputType() => InputType;
|
||||||
|
|
||||||
private string GetCounterText() => base.Counter == null ? string.Empty : (base.Counter == 0 ? (string.IsNullOrEmpty(base.Text) ? "0" : $"{base.Text.Length}") : ((string.IsNullOrEmpty(base.Text) ? "0" : $"{base.Text.Length}") + $" / {base.Counter}"));
|
private string GetCounterText() => Counter == null ? string.Empty : (Counter == 0 ? (string.IsNullOrEmpty(Text) ? "0" : $"{Text.Length}") : ((string.IsNullOrEmpty(Text) ? "0" : $"{Text.Length}") + $" / {Counter}"));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show clear button.
|
/// Show clear button.
|
||||||
@ -32,25 +32,10 @@ public partial class TextField<T> : InputBase<T>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
|
[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
|
||||||
|
|
||||||
/*protected string ClassList =>
|
protected string ClassList =>
|
||||||
new CssBuilder("input-input-control")
|
new CssBuilder("input-input-control")
|
||||||
.AddClass(base.AdditionalClassList)
|
.AddClass(base.AdditionalClassList)
|
||||||
.Build();*/
|
.Build();
|
||||||
|
|
||||||
protected virtual CssBuilder CompiledClassList
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new CssBuilder("input-input-control")
|
|
||||||
.AddClass(ClassList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A space separated list of class names, added on top of the default class list.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ClassList { get; set; }
|
|
||||||
|
|
||||||
public override ValueTask FocusAsync()
|
public override ValueTask FocusAsync()
|
||||||
{
|
{
|
||||||
@ -143,10 +128,10 @@ public partial class TextField<T> : InputBase<T>
|
|||||||
{
|
{
|
||||||
if (_mask != null)
|
if (_mask != null)
|
||||||
{
|
{
|
||||||
var textValue = base.Converter.Convert(value);
|
var textValue = Converter.Convert(value);
|
||||||
_mask.SetText(textValue);
|
_mask.SetText(textValue);
|
||||||
textValue = Mask.GetCleanText();
|
textValue = Mask.GetCleanText();
|
||||||
value = base.Converter.ConvertBack(textValue);
|
value = Converter.ConvertBack(textValue);
|
||||||
}
|
}
|
||||||
return base.SetValueAsync(value, updateText);
|
return base.SetValueAsync(value, updateText);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user