Progress
This commit is contained in:
parent
9f1214fd9f
commit
a04a0f07e2
@ -139,11 +139,11 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
|
||||
/// </summary>
|
||||
public bool HasErrors => HasError || ConversionError || ValidationErrors.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Return the validation error text or the conversion error message.
|
||||
/// </summary>
|
||||
/// <returns>Error text/message</returns>
|
||||
public string? GetErrorText()
|
||||
/// <summary>
|
||||
/// Return the validation error text or the conversion error message.
|
||||
/// </summary>
|
||||
/// <returns>Error text/message</returns>
|
||||
public string? GetErrorText(bool test = false)
|
||||
{
|
||||
// ErrorText is either set from outside or the first validation error
|
||||
if (!IsNullOrWhiteSpace(ErrorText))
|
||||
@ -152,6 +152,8 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
|
||||
if (!IsNullOrWhiteSpace(ConversionErrorMessage))
|
||||
return ConversionErrorMessage;
|
||||
|
||||
if (test) return "Error: test";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Timers;
|
||||
using Connected.Annotations;
|
||||
using Connected.Utilities.BindingConverters;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Connected.Components;
|
||||
|
@ -3,6 +3,23 @@
|
||||
@inherits InputBase<T>
|
||||
|
||||
<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)
|
||||
{
|
||||
<InputAdornment Class="@AdornmentClassname"
|
||||
@ -15,131 +32,135 @@
|
||||
AriaLabel="@AdornmentAriaLabel"
|
||||
/>
|
||||
}
|
||||
|
||||
@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 (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"
|
||||
<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"
|
||||
>
|
||||
@ChildContent
|
||||
</div>
|
||||
@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 (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>
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@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 (_showClearable && !Disabled)
|
||||
{
|
||||
<IconButton Class="@ClearButtonClassname"
|
||||
Color="@ThemeColor.Default"
|
||||
Icon="@ClearIcon"
|
||||
Size="@Size.Small"
|
||||
OnClick="@ClearButtonClickHandlerAsync"
|
||||
tabindex="-1"
|
||||
/>
|
||||
}
|
||||
|
||||
@if (Adornment == Adornment.End)
|
||||
{
|
||||
<InputAdornment Class="@AdornmentClassname"
|
||||
Icon="@AdornmentIcon"
|
||||
Color="@AdornmentColor"
|
||||
Size="@IconSize"
|
||||
Text="@AdornmentText"
|
||||
Edge="@Edge.End"
|
||||
AdornmentClick="@OnAdornmentClick"
|
||||
AriaLabel="@AdornmentAriaLabel"
|
||||
/>
|
||||
}
|
||||
@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)
|
||||
{
|
||||
<div class="input-outlined-border"></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>
|
||||
}
|
||||
@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>
|
||||
}
|
||||
|
||||
</InputContent>
|
||||
|
||||
</CascadingValue>
|
||||
|
||||
</InputControl>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -2,12 +2,192 @@
|
||||
using Connected.Utilities;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Timers;
|
||||
|
||||
namespace Connected.Components;
|
||||
|
||||
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));
|
||||
|
||||
protected string InputClassname => InputCssHelper.GetInputClassname(this);
|
||||
@ -31,23 +211,89 @@ public partial class Input<T> : InputBase<T>
|
||||
|
||||
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)
|
||||
return Task.CompletedTask;
|
||||
_isFocused = true;
|
||||
return SetTextAsync(args?.Value as string);
|
||||
}
|
||||
return SetTextAsync(args?.Value as string);
|
||||
|
||||
}
|
||||
|
||||
protected async Task OnChange(ChangeEventArgs args)
|
||||
{
|
||||
_internalText = args?.Value as string;
|
||||
await OnInternalInputChanged.InvokeAsync(args);
|
||||
if (!Immediate)
|
||||
{
|
||||
await SetTextAsync(args?.Value as string);
|
||||
}
|
||||
}
|
||||
if (TextChangeDelay > 0 && _timer != null)
|
||||
{
|
||||
_timer.Stop();
|
||||
await UpdateValuePropertyAsync(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_internalText = args?.Value as string;
|
||||
await OnInternalInputChanged.InvokeAsync(args);
|
||||
if (!Immediate)
|
||||
{
|
||||
await SetTextAsync(args?.Value as string);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Paste hook for descendants.
|
||||
@ -164,12 +410,12 @@ public partial class Input<T> : InputBase<T>
|
||||
UpdateClearable(Text);
|
||||
}
|
||||
|
||||
protected override async Task UpdateValuePropertyAsync(bool updateText)
|
||||
/*protected override async Task UpdateValuePropertyAsync(bool updateText)
|
||||
{
|
||||
await base.UpdateValuePropertyAsync(updateText);
|
||||
if (Clearable)
|
||||
UpdateClearable(Value);
|
||||
}
|
||||
}*/
|
||||
|
||||
protected virtual async Task ClearButtonClickHandlerAsync(MouseEventArgs e)
|
||||
{
|
||||
@ -197,14 +443,41 @@ public partial class Input<T> : InputBase<T>
|
||||
// in WASM (or in BSS with TextUpdateSuppression==false) we always update
|
||||
_internalText = Text;
|
||||
}
|
||||
|
||||
string baseTypeName = typeof(T).Name;
|
||||
if (IsNumericType(baseTypeName))
|
||||
{
|
||||
InputType = InputType.Number;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the input text from outside programmatically
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public Task SetText(string text)
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
@ -211,7 +211,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
|
||||
|
||||
protected virtual async Task SetTextAsync(string text, bool updateValue = true)
|
||||
{
|
||||
if (Text != text)
|
||||
if (Text != text)
|
||||
{
|
||||
Text = text;
|
||||
if (!string.IsNullOrWhiteSpace(Text))
|
||||
@ -220,6 +220,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
|
||||
await UpdateValuePropertyAsync(false);
|
||||
await TextChanged.InvokeAsync(Text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -329,7 +330,11 @@ public abstract class InputBase<T> : FormComponent<T, string>
|
||||
/// Fired when the Value property changes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<T> ValueChanged { get; set; }
|
||||
public EventCallback<T> ValueChanged
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value of this input element.
|
||||
|
@ -387,7 +387,7 @@ public partial class Picker<T> : FormComponent<T, string>
|
||||
|
||||
protected override void ResetValue()
|
||||
{
|
||||
_inputReference?.Reset();
|
||||
_inputReference?.InputReference.Reset();
|
||||
base.ResetValue();
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
@namespace Connected.Components
|
||||
@typeparam T
|
||||
@inherits DebouncedInput<T>
|
||||
@inherits InputBase<T>
|
||||
|
||||
<CascadingValue Name="SubscribeToParentForm" Value="@SubscribeToParentForm" IsFixed="true">
|
||||
<CascadingValue Name="SubscribeToParentForm" Value="@base.SubscribeToParentForm" IsFixed="true">
|
||||
<InputControl Label="@Label"
|
||||
Variant="@Variant"
|
||||
HelperText="@HelperText"
|
||||
HelperTextOnFocus="@HelperTextOnFocus"
|
||||
CounterText="@GetCounterText()"
|
||||
FullWidth="@FullWidth"
|
||||
Class="@ClassList"
|
||||
class="@ClassList"
|
||||
Error="@HasErrors"
|
||||
ErrorText="@GetErrorText()"
|
||||
ErrorId="@ErrorId"
|
||||
@ -48,7 +48,6 @@
|
||||
Margin="@Margin"
|
||||
OnBlur="@OnBlurred"
|
||||
OnKeyDown="@InvokeKeyDown"
|
||||
OnInternalInputChanged="OnChange"
|
||||
OnKeyPress="@InvokeKeyPress"
|
||||
OnKeyUp="@InvokeKeyUp"
|
||||
KeyDownPreventDefault="KeyDownPreventDefault"
|
||||
@ -57,6 +56,7 @@
|
||||
HideSpinButtons="true"
|
||||
Clearable="@Clearable"
|
||||
OnClearButtonClick="@OnClearButtonClick"
|
||||
Class="@CompiledClassList.Build()"
|
||||
Pattern="@Pattern"/>
|
||||
}
|
||||
else
|
||||
@ -83,8 +83,10 @@
|
||||
OnAdornmentClick="@OnAdornmentClick"
|
||||
Error="@HasError"
|
||||
Immediate="@Immediate"
|
||||
Margin="@Margin" OnBlur="@OnBlurred"
|
||||
Margin="@Margin"
|
||||
OnBlur="@OnBlurred"
|
||||
Clearable="@Clearable"
|
||||
Class="@CompiledClassList.Build()"
|
||||
OnClearButtonClick="@OnClearButtonClick"/>
|
||||
}
|
||||
</CascadingValue>
|
||||
|
@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Components.Web;
|
||||
|
||||
namespace Connected.Components;
|
||||
|
||||
public partial class TextField<T> : DebouncedInput<T>
|
||||
public partial class TextField<T> : InputBase<T>
|
||||
{
|
||||
private Mask? _maskReference;
|
||||
|
||||
@ -19,7 +19,7 @@ public partial class TextField<T> : DebouncedInput<T>
|
||||
|
||||
internal override InputType GetInputType() => InputType;
|
||||
|
||||
private string GetCounterText() => Counter == null ? string.Empty : (Counter == 0 ? (string.IsNullOrEmpty(Text) ? "0" : $"{Text.Length}") : ((string.IsNullOrEmpty(Text) ? "0" : $"{Text.Length}") + $" / {Counter}"));
|
||||
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}"));
|
||||
|
||||
/// <summary>
|
||||
/// Show clear button.
|
||||
@ -32,10 +32,25 @@ public partial class TextField<T> : DebouncedInput<T>
|
||||
/// </summary>
|
||||
[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
|
||||
|
||||
protected string ClassList =>
|
||||
/*protected string ClassList =>
|
||||
new CssBuilder("input-input-control")
|
||||
.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()
|
||||
{
|
||||
@ -128,10 +143,10 @@ public partial class TextField<T> : DebouncedInput<T>
|
||||
{
|
||||
if (_mask != null)
|
||||
{
|
||||
var textValue = Converter.Convert(value);
|
||||
var textValue = base.Converter.Convert(value);
|
||||
_mask.SetText(textValue);
|
||||
textValue = Mask.GetCleanText();
|
||||
value = Converter.ConvertBack(textValue);
|
||||
value = base.Converter.ConvertBack(textValue);
|
||||
}
|
||||
return base.SetValueAsync(value, updateText);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user