[WIP] Refactor components to match converter refactor
This commit is contained in:
		
							parent
							
								
									f3ae953772
								
							
						
					
					
						commit
						6f91dacb0c
					
				@ -5,14 +5,14 @@
 | 
				
			|||||||
<CascadingValue Name="SubscribeToParentForm" Value="false" IsFixed="true">
 | 
					<CascadingValue Name="SubscribeToParentForm" Value="false" IsFixed="true">
 | 
				
			||||||
    <div class="@AutocompleteClassList">
 | 
					    <div class="@AutocompleteClassList">
 | 
				
			||||||
        <InputControl Label="@Label" Variant="@Variant" HelperText="@HelperText" HelperTextOnFocus="@HelperTextOnFocus" FullWidth="@FullWidth" Margin="@Margin" Class="@ClassList()" Style="@Style"
 | 
					        <InputControl Label="@Label" Variant="@Variant" HelperText="@HelperText" HelperTextOnFocus="@HelperTextOnFocus" FullWidth="@FullWidth" Margin="@Margin" Class="@ClassList()" Style="@Style"
 | 
				
			||||||
                         Error="@Error" ErrorText="@ErrorText" Disabled="@Disabled" @onclick="@ToggleMenu" Required="@Required" ForId="@FieldId">
 | 
					                         Error="@HasError" ErrorText="@ErrorText" Disabled="@Disabled" @onclick="@ToggleMenu" Required="@Required" ForId="@FieldId">
 | 
				
			||||||
            <InputContent>
 | 
					            <InputContent>
 | 
				
			||||||
                <Input @ref="_elementReference" @key="_elementKey" InputType="InputType.Text"
 | 
					                <Input @ref="_elementReference" @key="_elementKey" InputType="InputType.Text"
 | 
				
			||||||
                          Class="select-input" Margin="@Margin"
 | 
					                          Class="select-input" Margin="@Margin"
 | 
				
			||||||
                          Variant="@Variant"
 | 
					                          Variant="@Variant"
 | 
				
			||||||
                          TextUpdateSuppression="@TextUpdateSuppression"
 | 
					                          TextUpdateSuppression="@TextUpdateSuppression"
 | 
				
			||||||
                          Value="@Text" DisableUnderLine="@DisableUnderLine"
 | 
					                          Value="@Text" DisableUnderLine="@DisableUnderLine"
 | 
				
			||||||
                          Disabled="@Disabled" ReadOnly="@ReadOnly" Error="@Error"
 | 
					                          Disabled="@Disabled" ReadOnly="@ReadOnly" Error="@HasError"
 | 
				
			||||||
                          OnAdornmentClick="@OnAdornmentClick" AdornmentIcon="@CurrentIcon" Adornment="@Adornment" AdornmentColor="@AdornmentColor" IconSize="@IconSize" AdornmentText="@AdornmentText"
 | 
					                          OnAdornmentClick="@OnAdornmentClick" AdornmentIcon="@CurrentIcon" Adornment="@Adornment" AdornmentColor="@AdornmentColor" IconSize="@IconSize" AdornmentText="@AdornmentText"
 | 
				
			||||||
                          Clearable="@Clearable" OnClearButtonClick="@OnClearButtonClick"
 | 
					                          Clearable="@Clearable" OnClearButtonClick="@OnClearButtonClick"
 | 
				
			||||||
                          @attributes="UserAttributes"
 | 
					                          @attributes="UserAttributes"
 | 
				
			||||||
 | 
				
			|||||||
@ -83,10 +83,7 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				_toStringFunc = value;
 | 
									_toStringFunc = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Converter = new Converter<T>
 | 
									Converter = new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null);
 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				SetFunc = _toStringFunc ?? (x => x?.ToString()),
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
		  }
 | 
							  }
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
@ -461,7 +458,7 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
 | 
				
			|||||||
				return string.Empty;
 | 
									return string.Empty;
 | 
				
			||||||
		  try
 | 
							  try
 | 
				
			||||||
		  {
 | 
							  {
 | 
				
			||||||
			return Converter.Set(item);
 | 
									return Converter.Convert(item);
 | 
				
			||||||
		  }
 | 
							  }
 | 
				
			||||||
		  catch (NullReferenceException) { }
 | 
							  catch (NullReferenceException) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -628,7 +625,7 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		  _timer?.Dispose();
 | 
							  _timer?.Dispose();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var value = Converter.Get(Text);
 | 
							  var value = Converter.ConvertBack(Text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		  return SetValueAsync(value, updateText: false);
 | 
							  return SetValueAsync(value, updateText: false);
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
				
			|||||||
@ -44,17 +44,17 @@ public class BooleanInput<T> : FormComponent<T, bool?>
 | 
				
			|||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	[Parameter] public EventCallback<T> CheckedChanged { get; set; }
 | 
						[Parameter] public EventCallback<T> CheckedChanged { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected bool? BoolValue => Converter.Set(Checked);
 | 
						protected bool? BoolValue => Converter.Convert(Checked);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected virtual Task OnChange(ChangeEventArgs args)
 | 
						protected virtual Task OnChange(ChangeEventArgs args)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Touched = true;
 | 
							Modified = true;
 | 
				
			||||||
		return SetBoolValueAsync((bool?)args.Value);
 | 
							return SetBoolValueAsync((bool?)args.Value);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected Task SetBoolValueAsync(bool? value)
 | 
						protected Task SetBoolValueAsync(bool? value)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return SetCheckedAsync(Converter.Get(value));
 | 
							return SetCheckedAsync(Converter.ConvertBack(value));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected async Task SetCheckedAsync(T value)
 | 
						protected async Task SetCheckedAsync(T value)
 | 
				
			||||||
@ -74,7 +74,7 @@ public class BooleanInput<T> : FormComponent<T, bool?>
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		var changed = base.SetConverter(value);
 | 
							var changed = base.SetConverter(value);
 | 
				
			||||||
		if (changed)
 | 
							if (changed)
 | 
				
			||||||
			SetBoolValueAsync(Converter.Set(Checked)).AndForget();
 | 
								SetBoolValueAsync(Converter.Convert(Checked)).AndForget();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return changed;
 | 
							return changed;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -139,7 +139,7 @@ public partial class CheckBox<T> : BooleanInput<T>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	protected override Task OnChange(ChangeEventArgs args)
 | 
						protected override Task OnChange(ChangeEventArgs args)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Touched = true;
 | 
							Modified = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Apply only when TriState parameter is set to true and T is bool?
 | 
							// Apply only when TriState parameter is set to true and T is bool?
 | 
				
			||||||
		if (TriState && typeof(T) == typeof(bool?))
 | 
							if (TriState && typeof(T) == typeof(bool?))
 | 
				
			||||||
 | 
				
			|||||||
@ -116,11 +116,7 @@ public partial class DataGrid<T> : UIComponent
 | 
				
			|||||||
	 internal bool isEditFormOpen;
 | 
						 internal bool isEditFormOpen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 // converters
 | 
						 // converters
 | 
				
			||||||
   private Converter<bool, bool?> _oppositeBoolConverter = new Converter<bool, bool?>
 | 
						 private Converter<bool, bool?> _oppositeBoolConverter = new LambdaConverter<bool, bool?>(value => value ? false : true, value => value.HasValue ? !value.Value : true);
 | 
				
			||||||
   {
 | 
					 | 
				
			||||||
      SetFunc = value => value ? false : true,
 | 
					 | 
				
			||||||
      GetFunc = value => value.HasValue ? !value.Value : true,
 | 
					 | 
				
			||||||
   };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 #region Notify Children Delegates
 | 
						 #region Notify Children Delegates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -30,7 +30,7 @@ public class DatePicker : DatePickerBase
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		if (_value != date)
 | 
							if (_value != date)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Touched = true;
 | 
								Modified = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (date is not null && IsDateDisabledFunc(date.Value.Date))
 | 
								if (date is not null && IsDateDisabledFunc(date.Value.Date))
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
@ -40,10 +40,8 @@ public class DatePicker : DatePickerBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			_value = date;
 | 
								_value = date;
 | 
				
			||||||
			if (updateValue)
 | 
								if (updateValue)
 | 
				
			||||||
			{
 | 
									await SetTextAsync(Converter.Convert(_value), false);
 | 
				
			||||||
				Converter.GetError = false;
 | 
								
 | 
				
			||||||
				await SetTextAsync(Converter.Set(_value), false);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			await DateChanged.InvokeAsync(_value);
 | 
								await DateChanged.InvokeAsync(_value);
 | 
				
			||||||
			BeginValidate();
 | 
								BeginValidate();
 | 
				
			||||||
			FieldChanged(_value);
 | 
								FieldChanged(_value);
 | 
				
			||||||
@ -52,15 +50,15 @@ public class DatePicker : DatePickerBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	protected override Task DateFormatChanged(string newFormat)
 | 
						protected override Task DateFormatChanged(string newFormat)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Touched = true;
 | 
							Modified = true;
 | 
				
			||||||
		return SetTextAsync(Converter.Set(_value), false);
 | 
							return SetTextAsync(Converter.Convert(_value), false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected override Task StringValueChanged(string value)
 | 
						protected override Task StringValueChanged(string value)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Touched = true;
 | 
							Modified = true;
 | 
				
			||||||
		// Update the date property (without updating back the Value property)
 | 
							// Update the date property (without updating back the Value property)
 | 
				
			||||||
		return SetDateAsync(Converter.Get(value), false);
 | 
							return SetDateAsync(Converter.ConvertBack(value), false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected override string GetDayClasses(int month, DateTime day)
 | 
						protected override string GetDayClasses(int month, DateTime day)
 | 
				
			||||||
 | 
				
			|||||||
@ -230,7 +230,7 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
 | 
				
			|||||||
		base.OnPickerOpened();
 | 
							base.OnPickerOpened();
 | 
				
			||||||
		if (Editable == true && Text != null)
 | 
							if (Editable == true && Text != null)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			DateTime? a = Converter.Get(Text);
 | 
								DateTime? a = Converter.ConvertBack(Text);
 | 
				
			||||||
			if (a.HasValue)
 | 
								if (a.HasValue)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				a = new DateTime(a.Value.Year, a.Value.Month, 1);
 | 
									a = new DateTime(a.Value.Year, a.Value.Month, 1);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,6 @@
 | 
				
			|||||||
namespace Connected.Components;
 | 
					using Connected.Extensions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Connected.Components;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class DateRange : Range<DateTime?>, IEquatable<DateRange>
 | 
					public class DateRange : Range<DateTime?>, IEquatable<DateRange>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -15,7 +17,7 @@ public class DateRange : Range<DateTime?>, IEquatable<DateRange>
 | 
				
			|||||||
      if (Start == null || End == null)
 | 
					      if (Start == null || End == null)
 | 
				
			||||||
         return string.Empty;
 | 
					         return string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return RangeConverter<DateTime>.Join(converter.Set(Start.Value), converter.Set(End.Value));
 | 
					      return RangeConverter<DateTime>.Join(converter.Convert(Start.Value), converter.Convert(End.Value));
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   public string ToIsoDateString()
 | 
					   public string ToIsoDateString()
 | 
				
			||||||
@ -40,12 +42,14 @@ public class DateRange : Range<DateTime?>, IEquatable<DateRange>
 | 
				
			|||||||
   {
 | 
					   {
 | 
				
			||||||
      date = null;
 | 
					      date = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var endDate = converter.Get(end);
 | 
					      var endDate = converter.ConvertBack(end);
 | 
				
			||||||
      if (converter.GetError)
 | 
					
 | 
				
			||||||
 | 
					      if (endDate is null)
 | 
				
			||||||
         return false;
 | 
					         return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var startDate = converter.Get(start);
 | 
					      var startDate = converter.ConvertBack(start);
 | 
				
			||||||
      if (converter.GetError)
 | 
					
 | 
				
			||||||
 | 
					      if (startDate is null)
 | 
				
			||||||
         return false;
 | 
					         return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      date = new DateRange(startDate, endDate);
 | 
					      date = new DateRange(startDate, endDate);
 | 
				
			||||||
 | 
				
			|||||||
@ -6,13 +6,13 @@
 | 
				
			|||||||
@code{
 | 
					@code{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected override RenderFragment InputContent=> 
 | 
					    protected override RenderFragment InputContent=> 
 | 
				
			||||||
        @<InputControl Label="@Label" Variant="@Variant" HelperText="@HelperText" Error="@Error" 
 | 
					        @<InputControl Label="@Label" Variant="@Variant" HelperText="@HelperText" Error="@HasError" 
 | 
				
			||||||
                          ErrorText="@ErrorText" Disabled="@Disabled" Margin="@Margin" Required="@Required"
 | 
					                          ErrorText="@ErrorText" Disabled="@Disabled" Margin="@Margin" Required="@Required"
 | 
				
			||||||
                          @onclick="() => { if (!Editable) ToggleState(); }" ForId="@FieldId">
 | 
					                          @onclick="() => { if (!Editable) ToggleState(); }" ForId="@FieldId">
 | 
				
			||||||
               <InputContent>
 | 
					               <InputContent>
 | 
				
			||||||
                   <RangeInput @ref="_rangeInput" @attributes="UserAttributes" InputType="@InputType.Text" Class="@PickerInputClass" Style="@Style" Variant="@Variant" ReadOnly="@(!Editable)"
 | 
					                   <RangeInput @ref="_rangeInput" @attributes="UserAttributes" InputType="@InputType.Text" Class="@PickerInputClass" Style="@Style" Variant="@Variant" ReadOnly="@(!Editable)"
 | 
				
			||||||
                                   @bind-Value="@RangeText" Disabled="@Disabled" Adornment="@Adornment" AdornmentIcon="@AdornmentIcon" AdornmentColor="@AdornmentColor" IconSize="@IconSize" OnAdornmentClick="ToggleState"
 | 
					                                   @bind-Value="@RangeText" Disabled="@Disabled" Adornment="@Adornment" AdornmentIcon="@AdornmentIcon" AdornmentColor="@AdornmentColor" IconSize="@IconSize" OnAdornmentClick="ToggleState"
 | 
				
			||||||
                                   Required="@Required" RequiredError="@RequiredError" Error="@Error" ErrorText="@ErrorText" Margin="@Margin" AdornmentAriaLabel="@AdornmentAriaLabel"/>
 | 
					                                   Required="@Required" RequiredError="@RequiredError" Error="@HasError" ErrorText="@ErrorText" Margin="@Margin" AdornmentAriaLabel="@AdornmentAriaLabel"/>
 | 
				
			||||||
               </InputContent>
 | 
					               </InputContent>
 | 
				
			||||||
           </InputControl>;
 | 
					           </InputControl>;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
				
			|||||||
@ -57,7 +57,6 @@ public partial class DateRangePicker : DatePickerBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
         if (updateValue)
 | 
					         if (updateValue)
 | 
				
			||||||
         {
 | 
					         {
 | 
				
			||||||
            Converter.GetError = false;
 | 
					 | 
				
			||||||
            if (_dateRange == null)
 | 
					            if (_dateRange == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
               _rangeText = null;
 | 
					               _rangeText = null;
 | 
				
			||||||
@ -66,8 +65,8 @@ public partial class DateRangePicker : DatePickerBase
 | 
				
			|||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
               _rangeText = new Range<string>(
 | 
					               _rangeText = new Range<string>(
 | 
				
			||||||
                   Converter.Set(_dateRange.Start),
 | 
					                   Converter.Convert(_dateRange.Start),
 | 
				
			||||||
                   Converter.Set(_dateRange.End));
 | 
					                   Converter.Convert(_dateRange.End));
 | 
				
			||||||
               await SetTextAsync(_dateRange.ToString(Converter), false);
 | 
					               await SetTextAsync(_dateRange.ToString(Converter), false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
         }
 | 
					         }
 | 
				
			||||||
@ -85,7 +84,7 @@ public partial class DateRangePicker : DatePickerBase
 | 
				
			|||||||
         if (_rangeText?.Equals(value) ?? value == null)
 | 
					         if (_rangeText?.Equals(value) ?? value == null)
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
         Touched = true;
 | 
					         Modified = true;
 | 
				
			||||||
         _rangeText = value;
 | 
					         _rangeText = value;
 | 
				
			||||||
         SetDateRangeAsync(ParseDateRangeValue(value?.Start, value?.End), false).AndForget();
 | 
					         SetDateRangeAsync(ParseDateRangeValue(value?.Start, value?.End), false).AndForget();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -135,13 +134,13 @@ public partial class DateRangePicker : DatePickerBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   protected override Task DateFormatChanged(string newFormat)
 | 
					   protected override Task DateFormatChanged(string newFormat)
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      Touched = true;
 | 
					      Modified = true;
 | 
				
			||||||
      return SetTextAsync(_dateRange?.ToString(Converter), false);
 | 
					      return SetTextAsync(_dateRange?.ToString(Converter), false);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   protected override Task StringValueChanged(string value)
 | 
					   protected override Task StringValueChanged(string value)
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      Touched = true;
 | 
					      Modified = true;
 | 
				
			||||||
      // Update the daterange property (without updating back the Value property)
 | 
					      // Update the daterange property (without updating back the Value property)
 | 
				
			||||||
      return SetDateRangeAsync(ParseDateRangeValue(value), false);
 | 
					      return SetDateRangeAsync(ParseDateRangeValue(value), false);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
				
			|||||||
@ -109,14 +109,14 @@ public partial class FileUpload<T> : FormComponent<T, string>
 | 
				
			|||||||
      await FilesChanged.InvokeAsync(_value);
 | 
					      await FilesChanged.InvokeAsync(_value);
 | 
				
			||||||
      BeginValidate();
 | 
					      BeginValidate();
 | 
				
			||||||
      FieldChanged(_value);
 | 
					      FieldChanged(_value);
 | 
				
			||||||
      if (!Error || !SuppressOnChangeWhenInvalid) //only trigger FilesChanged if validation passes or SuppressOnChangeWhenInvalid is false
 | 
					      if (!HasError || !SuppressOnChangeWhenInvalid) //only trigger FilesChanged if validation passes or SuppressOnChangeWhenInvalid is false
 | 
				
			||||||
         await OnFilesChanged.InvokeAsync(args);
 | 
					         await OnFilesChanged.InvokeAsync(args);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   protected override void OnInitialized()
 | 
					   protected override void OnInitialized()
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      if (!(typeof(T) == typeof(IReadOnlyList<IBrowserFile>) || typeof(T) == typeof(IBrowserFile)))
 | 
					      //if (!(typeof(T) == typeof(IReadOnlyList<IBrowserFile>) || typeof(T) == typeof(IBrowserFile)))
 | 
				
			||||||
         Logger.LogWarning("T must be of type {type1} or {type2}", typeof(IReadOnlyList<IBrowserFile>), typeof(IBrowserFile));
 | 
					      //   Logger.LogWarning("T must be of type {type1} or {type2}", typeof(IReadOnlyList<IBrowserFile>), typeof(IBrowserFile));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      base.OnInitialized();
 | 
					      base.OnInitialized();
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
				
			|||||||
@ -206,11 +206,11 @@ public partial class Form : UIComponent, IDisposable, IForm
 | 
				
			|||||||
		// - none have an error
 | 
							// - none have an error
 | 
				
			||||||
		// - all required fields have been touched (and thus validated)
 | 
							// - all required fields have been touched (and thus validated)
 | 
				
			||||||
		var no_errors = _formControls.All(x => x.HasErrors == false);
 | 
							var no_errors = _formControls.All(x => x.HasErrors == false);
 | 
				
			||||||
		var required_all_touched = _formControls.Where(x => x.Required).All(x => x.Touched);
 | 
							var required_all_touched = _formControls.Where(x => x.Required).All(x => x.Modified);
 | 
				
			||||||
		var valid = no_errors && required_all_touched;
 | 
							var valid = no_errors && required_all_touched;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var old_touched = _touched;
 | 
							var old_touched = _touched;
 | 
				
			||||||
		_touched = _formControls.Any(x => x.Touched);
 | 
							_touched = _formControls.Any(x => x.Modified);
 | 
				
			||||||
		try
 | 
							try
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			_shouldRender = false;
 | 
								_shouldRender = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -14,13 +14,19 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	 private Converter<T, U> _converter;
 | 
						 private Converter<T, U> _converter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 /// <summary>
 | 
				
			||||||
 | 
						 /// Invoked whenever the string value cannot be converted
 | 
				
			||||||
 | 
						 /// </summary>
 | 
				
			||||||
 | 
						 public event EventHandler<string> ConversionErrorOccured;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 protected FormComponent(Converter<T, U> converter)
 | 
						 protected FormComponent(Converter<T, U> converter)
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
		  _converter = converter ?? throw new ArgumentNullException(nameof(converter));
 | 
							  _converter = converter ?? throw new ArgumentNullException(nameof(converter));
 | 
				
			||||||
		_converter.OnError = OnConversionError;
 | 
							  _converter.ErrorOccured += (s, e) => OnConversionError(e);
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[CascadingParameter] internal IForm Form { get; set; }
 | 
						 [CascadingParameter]
 | 
				
			||||||
 | 
						 internal IForm? Form { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// If true, this is a top-level form component. If false, this input is a sub-component of another input (i.e. TextField, Select, etc).
 | 
						 /// If true, this is a top-level form component. If false, this input is a sub-component of another input (i.e. TextField, Select, etc).
 | 
				
			||||||
@ -33,77 +39,77 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 /// If true, this form input is required to be filled out.
 | 
						 /// If true, this form input is required to be filled out.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Validation)]
 | 
					 | 
				
			||||||
	 public bool Required { get; set; }
 | 
						 public bool Required { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// The error text that will be displayed if the input is not filled out but required.
 | 
						 /// The error text that will be displayed if the input is not filled out but required.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Validation)]
 | 
					 | 
				
			||||||
	 public string RequiredError { get; set; } = "Required";
 | 
						 public string RequiredError { get; set; } = "Required";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	/// The ErrorText that will be displayed if Error true.
 | 
						 /// The ErrorText that will be displayed if <see cref="HasError"/> is set to true.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Validation)]
 | 
					 | 
				
			||||||
	 public string ErrorText { get; set; }
 | 
						 public string ErrorText { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// If true, the label will be displayed in an error state.
 | 
						 /// If true, the label will be displayed in an error state.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Validation)]
 | 
						 public bool HasError { get; set; }
 | 
				
			||||||
	public bool Error { get; set; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	/// The ErrorId that will be used by aria-describedby if Error true
 | 
						 /// The ErrorId that will be used by aria-describedby if <see cref="HasError"/> is true
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Validation)]
 | 
					 | 
				
			||||||
	 public string ErrorId { get; set; }
 | 
						 public string ErrorId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// The generic converter of the component.
 | 
						 /// The generic converter of the component.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Behavior)]
 | 
					 | 
				
			||||||
	 public Converter<T, U> Converter
 | 
						 public Converter<T, U> Converter
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
		  get => _converter;
 | 
							  get => _converter;
 | 
				
			||||||
		  set => SetConverter(value);
 | 
							  set => SetConverter(value);
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected virtual bool SetConverter(Converter<T, U> value)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		var changed = (_converter != value);
 | 
					 | 
				
			||||||
		if (changed)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			_converter = value ?? throw new ArgumentNullException(nameof(value));   // converter is mandatory at all times
 | 
					 | 
				
			||||||
			_converter.OnError = OnConversionError;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return changed;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	/// The culture of the component.
 | 
						 /// The culture of the component. Also sets the culture of the <see cref="Converter"/> .
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Behavior)]
 | 
					 | 
				
			||||||
	 public CultureInfo Culture
 | 
						 public CultureInfo Culture
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
		  get => _converter.Culture;
 | 
							  get => _converter.Culture;
 | 
				
			||||||
		  set => SetCulture(value);
 | 
							  set => SetCulture(value);
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected virtual bool SetCulture(CultureInfo value)
 | 
						 private string _conversionError { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 protected virtual bool SetConverter(Converter<T, U> value)
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
		var changed = (_converter.Culture != value);
 | 
							  var changed = _converter != value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		  if (changed)
 | 
							  if (changed)
 | 
				
			||||||
		  {
 | 
							  {
 | 
				
			||||||
			_converter.Culture = value;
 | 
									/*
 | 
				
			||||||
 | 
					             * Converter is mandatory at all times
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
									_converter = value ?? throw new ArgumentNullException(nameof(value));
 | 
				
			||||||
 | 
									_converter.ErrorOccured += (s, e) => OnConversionError(e);
 | 
				
			||||||
		  }
 | 
							  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							  return changed;
 | 
				
			||||||
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 protected virtual bool SetCulture(CultureInfo value)
 | 
				
			||||||
 | 
						 {
 | 
				
			||||||
 | 
							  var changed = _converter.Culture != value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							  if (changed)
 | 
				
			||||||
 | 
									_converter.Culture = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		  return changed;
 | 
							  return changed;
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -111,37 +117,38 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 {
 | 
						 {
 | 
				
			||||||
		  // note: we need to update the form here because the conversion error might lead to not updating the value
 | 
							  // note: we need to update the form here because the conversion error might lead to not updating the value
 | 
				
			||||||
		  // ... which leads to not updating the form
 | 
							  // ... which leads to not updating the form
 | 
				
			||||||
		Touched = true;
 | 
					 | 
				
			||||||
		Form?.Update(this);
 | 
					 | 
				
			||||||
		OnConversionErrorOccurred(error);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected virtual void OnConversionErrorOccurred(string error)
 | 
							  //TODO Why does the form need to be updated?
 | 
				
			||||||
	{
 | 
							  Modified = true;
 | 
				
			||||||
		/* Descendants can override this method to catch conversion errors */
 | 
					
 | 
				
			||||||
 | 
							  _conversionError = error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							  Form?.Update(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							  ConversionErrorOccured?.Invoke(this, error);
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// True if the conversion from string to T failed
 | 
						 /// True if the conversion from string to T failed
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	public bool ConversionError => _converter.GetError;
 | 
						 public bool ConversionError => !string.IsNullOrWhiteSpace(_conversionError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// The error message of the conversion error from string to T. Null otherwise
 | 
						 /// The error message of the conversion error from string to T. Null otherwise
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	public string ConversionErrorMessage => _converter.GetErrorMessage;
 | 
						 public string ConversionErrorMessage => _conversionError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// True if the input has any of the following errors: An error set from outside, a conversion error or
 | 
						 /// True if the input has any of the following errors: An error set from outside, a conversion error or
 | 
				
			||||||
	 /// one or more validation errors
 | 
						 /// one or more validation errors
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	public bool HasErrors => Error || 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()
 | 
						 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))
 | 
				
			||||||
@ -154,13 +161,13 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	/// This manages the state of having been "touched" by the user. A form control always starts out untouched
 | 
						 /// This manages the state of having been modified by the user. A form control always starts out unmodified
 | 
				
			||||||
	/// but becomes touched when the user performed input or the blur event was raised.
 | 
						 /// but becomes modified when the user performed input or the blur event was raised.
 | 
				
			||||||
	 ///
 | 
						 ///
 | 
				
			||||||
	/// The touched state is only relevant for inputs that have no value (i.e. empty text fields). Being untouched will
 | 
						 /// The modified state is only relevant for inputs that have no value (i.e. empty text fields). Being unmodified will
 | 
				
			||||||
	/// suppress RequiredError
 | 
						 /// suppress the display of the  <see cref="RequiredError"/>
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	public bool Touched { get; protected set; }
 | 
						 public bool Modified { get; protected set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 #region MudForm Validation
 | 
						 #region MudForm Validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,10 +189,20 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 [Category(CategoryTypes.FormComponent.Validation)]
 | 
						 [Category(CategoryTypes.FormComponent.Validation)]
 | 
				
			||||||
	 public object Validation { get; set; }
 | 
						 public object Validation { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 private T __value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// This is the form component's value.
 | 
						 /// This is the form component's value.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	protected T _value;
 | 
						 protected T _value
 | 
				
			||||||
 | 
						 {
 | 
				
			||||||
 | 
							  get => __value;
 | 
				
			||||||
 | 
							  set
 | 
				
			||||||
 | 
							  {
 | 
				
			||||||
 | 
									__value = value;
 | 
				
			||||||
 | 
									_conversionError = null;
 | 
				
			||||||
 | 
							  }
 | 
				
			||||||
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 // These are the fire-and-forget methods to launch an async validation process.
 | 
						 // These are the fire-and-forget methods to launch an async validation process.
 | 
				
			||||||
	 // After each async step, we make sure the current Value of the component has not changed while
 | 
						 // After each async step, we make sure the current Value of the component has not changed while
 | 
				
			||||||
@ -232,7 +249,7 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
		  // when a validation is forced, we must set Touched to true, because for untouched fields with
 | 
							  // when a validation is forced, we must set Touched to true, because for untouched fields with
 | 
				
			||||||
		  // no value, validation does nothing due to the way forms are expected to work (display errors
 | 
							  // no value, validation does nothing due to the way forms are expected to work (display errors
 | 
				
			||||||
		  // only after fields have been touched).
 | 
							  // only after fields have been touched).
 | 
				
			||||||
		Touched = true;
 | 
							  Modified = true;
 | 
				
			||||||
		  return ValidateValue();
 | 
							  return ValidateValue();
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -284,7 +301,7 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
				// required error (must be last, because it is least important!)
 | 
									// required error (must be last, because it is least important!)
 | 
				
			||||||
				if (Required)
 | 
									if (Required)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
				if (Touched && !HasValue(_value))
 | 
										 if (Modified && !HasValue(_value))
 | 
				
			||||||
						  errors.Add(RequiredError);
 | 
											  errors.Add(RequiredError);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
		  }
 | 
							  }
 | 
				
			||||||
@ -297,7 +314,7 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
					 // if Error and ErrorText are set by the user, setting them here will have no effect.
 | 
										 // if Error and ErrorText are set by the user, setting them here will have no effect.
 | 
				
			||||||
					 // if Error, create an error id that can be used by aria-describedby on input control
 | 
										 // if Error, create an error id that can be used by aria-describedby on input control
 | 
				
			||||||
					 ValidationErrors = errors;
 | 
										 ValidationErrors = errors;
 | 
				
			||||||
				Error = errors.Count > 0;
 | 
										 HasError = errors.Count > 0;
 | 
				
			||||||
					 ErrorText = errors.FirstOrDefault();
 | 
										 ErrorText = errors.FirstOrDefault();
 | 
				
			||||||
					 ErrorId = HasErrors ? Guid.NewGuid().ToString() : null;
 | 
										 ErrorId = HasErrors ? Guid.NewGuid().ToString() : null;
 | 
				
			||||||
					 Form?.Update(this);
 | 
										 Form?.Update(this);
 | 
				
			||||||
@ -487,7 +504,8 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 {
 | 
						 {
 | 
				
			||||||
		  /* to be overridden */
 | 
							  /* to be overridden */
 | 
				
			||||||
		  _value = default;
 | 
							  _value = default;
 | 
				
			||||||
		Touched = false;
 | 
							  _conversionError = null;
 | 
				
			||||||
 | 
							  Modified = false;
 | 
				
			||||||
		  StateHasChanged();
 | 
							  StateHasChanged();
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -496,7 +514,7 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
	 public void ResetValidation()
 | 
						 public void ResetValidation()
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
		Error = false;
 | 
							  HasError = false;
 | 
				
			||||||
		  ValidationErrors.Clear();
 | 
							  ValidationErrors.Clear();
 | 
				
			||||||
		  ErrorText = null;
 | 
							  ErrorText = null;
 | 
				
			||||||
		  StateHasChanged();
 | 
							  StateHasChanged();
 | 
				
			||||||
@ -527,28 +545,26 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// Specify an expression which returns the model's field for which validation messages should be displayed.
 | 
						 /// Specify an expression which returns the model's field for which validation messages should be displayed.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
#nullable enable
 | 
					 | 
				
			||||||
	 [Parameter]
 | 
						 [Parameter]
 | 
				
			||||||
	[Category(CategoryTypes.FormComponent.Validation)]
 | 
					 | 
				
			||||||
	 public Expression<Func<T>>? For { get; set; }
 | 
						 public Expression<Func<T>>? For { get; set; }
 | 
				
			||||||
#nullable disable
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 public bool IsForNull => For == null;
 | 
						 public bool IsForNull => For == null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// Stores the list of validation attributes attached to the property targeted by <seealso cref="For"/>. If <seealso cref="For"/> is null, this property is null too.
 | 
						 /// Stores the list of validation attributes attached to the property targeted by <seealso cref="For"/>. If <seealso cref="For"/> is null, this property is null too.
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
#nullable enable
 | 
					 | 
				
			||||||
	 private IEnumerable<ValidationAttribute>? _validationAttrsFor;
 | 
						 private IEnumerable<ValidationAttribute>? _validationAttrsFor;
 | 
				
			||||||
#nullable disable
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 private void OnValidationStateChanged(object sender, ValidationStateChangedEventArgs e)
 | 
						 private void OnValidationStateChanged(object sender, ValidationStateChangedEventArgs e)
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
		  if (EditContext != null && !_fieldIdentifier.Equals(default(FieldIdentifier)))
 | 
							  if (EditContext != null && !_fieldIdentifier.Equals(default(FieldIdentifier)))
 | 
				
			||||||
		  {
 | 
							  {
 | 
				
			||||||
				var error_msgs = EditContext.GetValidationMessages(_fieldIdentifier).ToArray();
 | 
									var error_msgs = EditContext.GetValidationMessages(_fieldIdentifier).ToArray();
 | 
				
			||||||
			Error = error_msgs.Length > 0;
 | 
									HasError = error_msgs.Length > 0;
 | 
				
			||||||
			ErrorText = (Error ? error_msgs[0] : null);
 | 
									ErrorText = (HasError ? error_msgs[0] : null);
 | 
				
			||||||
				StateHasChanged();
 | 
									StateHasChanged();
 | 
				
			||||||
		  }
 | 
							  }
 | 
				
			||||||
	 }
 | 
						 }
 | 
				
			||||||
@ -561,16 +577,13 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
 | 
				
			|||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// To find out whether or not For parameter has changed we keep a separate reference
 | 
						 /// To find out whether or not For parameter has changed we keep a separate reference
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
#nullable enable
 | 
					 | 
				
			||||||
	 private Expression<Func<T>>? _currentFor;
 | 
						 private Expression<Func<T>>? _currentFor;
 | 
				
			||||||
#nullable disable
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 /// <summary>
 | 
						 /// <summary>
 | 
				
			||||||
	 /// To find out whether or not EditContext parameter has changed we keep a separate reference
 | 
						 /// To find out whether or not EditContext parameter has changed we keep a separate reference
 | 
				
			||||||
	 /// </summary>
 | 
						 /// </summary>
 | 
				
			||||||
#nullable enable
 | 
					 | 
				
			||||||
	 private EditContext? _currentEditContext;
 | 
						 private EditContext? _currentEditContext;
 | 
				
			||||||
#nullable disable
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 protected override void OnParametersSet()
 | 
						 protected override void OnParametersSet()
 | 
				
			||||||
	 {
 | 
						 {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,9 +3,9 @@
 | 
				
			|||||||
public interface IFormComponent
 | 
					public interface IFormComponent
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	public bool Required { get; set; }
 | 
						public bool Required { get; set; }
 | 
				
			||||||
	public bool Error { get; set; }
 | 
						public bool HasError { get; set; }
 | 
				
			||||||
	public bool HasErrors { get; }
 | 
						public bool HasErrors { get; }
 | 
				
			||||||
	public bool Touched { get; }
 | 
						public bool Modified { get; }
 | 
				
			||||||
	public object Validation { get; set; }
 | 
						public object Validation { get; set; }
 | 
				
			||||||
	public bool IsForNull { get; }
 | 
						public bool IsForNull { get; }
 | 
				
			||||||
	public List<string> ValidationErrors { get; set; }
 | 
						public List<string> ValidationErrors { get; set; }
 | 
				
			||||||
 | 
				
			|||||||
@ -41,7 +41,7 @@
 | 
				
			|||||||
                @onkeyup:preventDefault="@KeyUpPreventDefault"
 | 
					                @onkeyup:preventDefault="@KeyUpPreventDefault"
 | 
				
			||||||
                @onmousewheel="@OnMouseWheel" 
 | 
					                @onmousewheel="@OnMouseWheel" 
 | 
				
			||||||
                @onwheel="@OnMouseWheel"
 | 
					                @onwheel="@OnMouseWheel"
 | 
				
			||||||
                aria-invalid="@Error.ToString().ToLower()"
 | 
					                aria-invalid="@HasError.ToString().ToLower()"
 | 
				
			||||||
                aria-describedby="@ErrorId"
 | 
					                aria-describedby="@ErrorId"
 | 
				
			||||||
        > 
 | 
					        > 
 | 
				
			||||||
            @Text
 | 
					            @Text
 | 
				
			||||||
@ -73,7 +73,7 @@
 | 
				
			|||||||
                @onkeyup:preventDefault="@KeyUpPreventDefault"
 | 
					                @onkeyup:preventDefault="@KeyUpPreventDefault"
 | 
				
			||||||
                @onmousewheel="@OnMouseWheel" 
 | 
					                @onmousewheel="@OnMouseWheel" 
 | 
				
			||||||
                @onwheel="@OnMouseWheel"
 | 
					                @onwheel="@OnMouseWheel"
 | 
				
			||||||
                aria-invalid="@Error.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*@
 | 
				
			||||||
 | 
				
			|||||||
@ -215,7 +215,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
 | 
				
			|||||||
      {
 | 
					      {
 | 
				
			||||||
         Text = text;
 | 
					         Text = text;
 | 
				
			||||||
         if (!string.IsNullOrWhiteSpace(Text))
 | 
					         if (!string.IsNullOrWhiteSpace(Text))
 | 
				
			||||||
            Touched = true;
 | 
					            Modified = true;
 | 
				
			||||||
         if (updateValue)
 | 
					         if (updateValue)
 | 
				
			||||||
            await UpdateValuePropertyAsync(false);
 | 
					            await UpdateValuePropertyAsync(false);
 | 
				
			||||||
         await TextChanged.InvokeAsync(Text);
 | 
					         await TextChanged.InvokeAsync(Text);
 | 
				
			||||||
@ -227,7 +227,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
 | 
				
			|||||||
   /// </summary>
 | 
					   /// </summary>
 | 
				
			||||||
   protected virtual Task UpdateTextPropertyAsync(bool updateValue)
 | 
					   protected virtual Task UpdateTextPropertyAsync(bool updateValue)
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      return SetTextAsync(Converter.Set(Value), updateValue);
 | 
					      return SetTextAsync(Converter.Convert(Value), updateValue);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /// <summary>
 | 
					   /// <summary>
 | 
				
			||||||
@ -266,7 +266,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      if (!OnlyValidateIfDirty || _isDirty)
 | 
					      if (!OnlyValidateIfDirty || _isDirty)
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
         Touched = true;
 | 
					         Modified = true;
 | 
				
			||||||
         BeginValidateAfter(OnBlur.InvokeAsync(obj));
 | 
					         BeginValidateAfter(OnBlur.InvokeAsync(obj));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
@ -361,7 +361,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
 | 
				
			|||||||
   /// </summary>
 | 
					   /// </summary>
 | 
				
			||||||
   protected virtual Task UpdateValuePropertyAsync(bool updateText)
 | 
					   protected virtual Task UpdateValuePropertyAsync(bool updateText)
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      return SetValueAsync(Converter.Get(Text), updateText);
 | 
					      return SetValueAsync(Converter.ConvertBack(Text), updateText);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   protected override bool SetConverter(Converter<T, string> value)
 | 
					   protected override bool SetConverter(Converter<T, string> value)
 | 
				
			||||||
@ -389,7 +389,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
 | 
				
			|||||||
   [Category(CategoryTypes.FormComponent.Behavior)]
 | 
					   [Category(CategoryTypes.FormComponent.Behavior)]
 | 
				
			||||||
   public string Format
 | 
					   public string Format
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      get => ((Converter<T>)Converter).Format;
 | 
					      get => ((ToStringConverter<T>)Converter).Format;
 | 
				
			||||||
      set => SetFormat(value);
 | 
					      set => SetFormat(value);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -398,7 +398,7 @@ public abstract class InputBase<T> : FormComponent<T, string>
 | 
				
			|||||||
      var changed = Format != value;
 | 
					      var changed = Format != value;
 | 
				
			||||||
      if (changed)
 | 
					      if (changed)
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
         ((Converter<T>)Converter).Format = value;
 | 
					         ((ToStringConverter<T>)Converter).Format = value;
 | 
				
			||||||
         UpdateTextPropertyAsync(false).AndForget();      // refresh only Text property from current Value
 | 
					         UpdateTextPropertyAsync(false).AndForget();      // refresh only Text property from current Value
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return changed;
 | 
					      return changed;
 | 
				
			||||||
 | 
				
			|||||||
@ -238,7 +238,7 @@ public partial class Mask : InputBase<string>, IDisposable
 | 
				
			|||||||
         await base.SetTextAsync(text, updateValue: false);
 | 
					         await base.SetTextAsync(text, updateValue: false);
 | 
				
			||||||
         if (Clearable)
 | 
					         if (Clearable)
 | 
				
			||||||
            UpdateClearable(Text);
 | 
					            UpdateClearable(Text);
 | 
				
			||||||
         var v = Converter.Get(cleanText);
 | 
					         var v = Converter.ConvertBack(cleanText);
 | 
				
			||||||
         Value = v;
 | 
					         Value = v;
 | 
				
			||||||
         await ValueChanged.InvokeAsync(v);
 | 
					         await ValueChanged.InvokeAsync(v);
 | 
				
			||||||
         SetCaretPosition(caret, selection);
 | 
					         SetCaretPosition(caret, selection);
 | 
				
			||||||
@ -262,7 +262,7 @@ public partial class Mask : InputBase<string>, IDisposable
 | 
				
			|||||||
      // allow this only via changes from the outside
 | 
					      // allow this only via changes from the outside
 | 
				
			||||||
      if (_updating)
 | 
					      if (_updating)
 | 
				
			||||||
         return;
 | 
					         return;
 | 
				
			||||||
      var text = Converter.Set(Value);
 | 
					      var text = Converter.Convert(Value);
 | 
				
			||||||
      var cleanText = MaskKind.GetCleanText();
 | 
					      var cleanText = MaskKind.GetCleanText();
 | 
				
			||||||
      if (cleanText == text || string.IsNullOrEmpty(cleanText) && string.IsNullOrEmpty(text))
 | 
					      if (cleanText == text || string.IsNullOrEmpty(cleanText) && string.IsNullOrEmpty(text))
 | 
				
			||||||
         return;
 | 
					         return;
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,7 @@
 | 
				
			|||||||
                          AdornmentIcon="@AdornmentIcon"
 | 
					                          AdornmentIcon="@AdornmentIcon"
 | 
				
			||||||
                          AdornmentColor="@AdornmentColor"
 | 
					                          AdornmentColor="@AdornmentColor"
 | 
				
			||||||
                          IconSize="@IconSize"
 | 
					                          IconSize="@IconSize"
 | 
				
			||||||
                          Error="@Error"
 | 
					                          Error="@HasError"
 | 
				
			||||||
                          Immediate="@(Immediate)"
 | 
					                          Immediate="@(Immediate)"
 | 
				
			||||||
                          Margin="@Margin"
 | 
					                          Margin="@Margin"
 | 
				
			||||||
                          MaxLength="@MaxLength"
 | 
					                          MaxLength="@MaxLength"
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@
 | 
				
			|||||||
             Margin="@Margin" 
 | 
					             Margin="@Margin" 
 | 
				
			||||||
             Required="@Required" 
 | 
					             Required="@Required" 
 | 
				
			||||||
             RequiredError="@RequiredError" 
 | 
					             RequiredError="@RequiredError" 
 | 
				
			||||||
             Error="@Error" 
 | 
					             Error="@HasError" 
 | 
				
			||||||
             ErrorText="@ErrorText"
 | 
					             ErrorText="@ErrorText"
 | 
				
			||||||
             Clearable="@(ReadOnly ? false : Clearable)"
 | 
					             Clearable="@(ReadOnly ? false : Clearable)"
 | 
				
			||||||
             OnClearButtonClick="@(() => Clear())"
 | 
					             OnClearButtonClick="@(() => Clear())"
 | 
				
			||||||
 | 
				
			|||||||
@ -81,7 +81,7 @@ public partial class RadioGroup<T> : FormComponent<T, T>, IRadioGroup
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   internal Task SetSelectedRadioAsync(Radio<T> radio)
 | 
					   internal Task SetSelectedRadioAsync(Radio<T> radio)
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      Touched = true;
 | 
					      Modified = true;
 | 
				
			||||||
      return SetSelectedRadioAsync(radio, true);
 | 
					      return SetSelectedRadioAsync(radio, true);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,14 +5,14 @@
 | 
				
			|||||||
<CascadingValue Name="SubscribeToParentForm" Value="false" IsFixed="true">
 | 
					<CascadingValue Name="SubscribeToParentForm" Value="false" IsFixed="true">
 | 
				
			||||||
	<div class="mud-select" id="@_elementId">
 | 
						<div class="mud-select" id="@_elementId">
 | 
				
			||||||
		<InputControl Label="@Label" Variant="@Variant" HelperText="@HelperText" HelperTextOnFocus="@HelperTextOnFocus" FullWidth="@FullWidth" Margin="@Margin" Class="@Classname" Style="@Style"
 | 
							<InputControl Label="@Label" Variant="@Variant" HelperText="@HelperText" HelperTextOnFocus="@HelperTextOnFocus" FullWidth="@FullWidth" Margin="@Margin" Class="@Classname" Style="@Style"
 | 
				
			||||||
						 Error="@Error" ErrorText="@ErrorText" ErrorId="@ErrorId" Disabled="@Disabled" @onclick="@ToggleMenu" Required="@Required" ForId="@FieldId">
 | 
											 Error="@HasError" ErrorText="@ErrorText" ErrorId="@ErrorId" Disabled="@Disabled" @onclick="@ToggleMenu" Required="@Required" ForId="@FieldId">
 | 
				
			||||||
			<InputContent>
 | 
								<InputContent>
 | 
				
			||||||
                <Input @ref="_elementReference" InputType="@(CanRenderValue || (Strict && !IsValueInList) ? InputType.Hidden : InputType.Text)"
 | 
					                <Input @ref="_elementReference" InputType="@(CanRenderValue || (Strict && !IsValueInList) ? InputType.Hidden : InputType.Text)"
 | 
				
			||||||
                          Class="mud-select-input" Margin="@Margin" Placeholder="@Placeholder"
 | 
					                          Class="mud-select-input" Margin="@Margin" Placeholder="@Placeholder"
 | 
				
			||||||
                          Variant="@Variant"
 | 
					                          Variant="@Variant"
 | 
				
			||||||
                          TextUpdateSuppression="false"
 | 
					                          TextUpdateSuppression="false"
 | 
				
			||||||
                          Value="@(Strict && !IsValueInList ? null : Text)" DisableUnderLine="@DisableUnderLine"
 | 
					                          Value="@(Strict && !IsValueInList ? null : Text)" DisableUnderLine="@DisableUnderLine"
 | 
				
			||||||
                          Disabled="@Disabled" ReadOnly="true" Error="@Error" ErrorId="@ErrorId"
 | 
					                          Disabled="@Disabled" ReadOnly="true" Error="@HasError" ErrorId="@ErrorId"
 | 
				
			||||||
                          OnAdornmentClick="@OnAdornmentClick" AdornmentIcon="@_currentIcon" Adornment="@Adornment"
 | 
					                          OnAdornmentClick="@OnAdornmentClick" AdornmentIcon="@_currentIcon" Adornment="@Adornment"
 | 
				
			||||||
                          AdornmentColor="@AdornmentColor" IconSize="@IconSize" AdornmentText="@AdornmentText"
 | 
					                          AdornmentColor="@AdornmentColor" IconSize="@IconSize" AdornmentText="@AdornmentText"
 | 
				
			||||||
                          Clearable="@Clearable" OnClearButtonClick="(async (e) => await SelectClearButtonClickHandlerAsync(e))"
 | 
					                          Clearable="@Clearable" OnClearButtonClick="(async (e) => await SelectClearButtonClickHandlerAsync(e))"
 | 
				
			||||||
 | 
				
			|||||||
@ -86,12 +86,12 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
			// find first item that starts with the letter
 | 
								// find first item that starts with the letter
 | 
				
			||||||
			var currentItem = items.FirstOrDefault(x => x.ItemId == (string)_activeItemId);
 | 
								var currentItem = items.FirstOrDefault(x => x.ItemId == (string)_activeItemId);
 | 
				
			||||||
			if (currentItem != null &&
 | 
								if (currentItem != null &&
 | 
				
			||||||
            Converter.Set(currentItem.Value)?.ToLowerInvariant().StartsWith(startChar) == true)
 | 
								   Converter.Convert(currentItem.Value)?.ToLowerInvariant().StartsWith(startChar) == true)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				// this will step through all items that start with the same letter if pressed multiple times
 | 
									// this will step through all items that start with the same letter if pressed multiple times
 | 
				
			||||||
				items = items.SkipWhile(x => x != currentItem).Skip(1);
 | 
									items = items.SkipWhile(x => x != currentItem).Skip(1);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
         items = items.Where(x => Converter.Set(x.Value)?.ToLowerInvariant().StartsWith(startChar) == true);
 | 
								items = items.Where(x => Converter.Convert(x.Value)?.ToLowerInvariant().StartsWith(startChar) == true);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		var item = items.FirstOrDefault();
 | 
							var item = items.FirstOrDefault();
 | 
				
			||||||
		if (item == null)
 | 
							if (item == null)
 | 
				
			||||||
@ -244,13 +244,13 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
				//Warning. Here the Converter was not set yet
 | 
									//Warning. Here the Converter was not set yet
 | 
				
			||||||
				if (MultiSelectionTextFunc != null)
 | 
									if (MultiSelectionTextFunc != null)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
               SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))),
 | 
										SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))),
 | 
				
			||||||
                  selectedConvertedValues: SelectedValues.Select(x => Converter.Set(x)).ToList(),
 | 
										   selectedConvertedValues: SelectedValues.Select(x => Converter.Convert(x)).ToList(),
 | 
				
			||||||
					   multiSelectionTextFunc: MultiSelectionTextFunc).AndForget();
 | 
										   multiSelectionTextFunc: MultiSelectionTextFunc).AndForget();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
               SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))), updateValue: false).AndForget();
 | 
										SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))), updateValue: false).AndForget();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			SelectedValuesChanged.InvokeAsync(new HashSet<T>(SelectedValues, _comparer));
 | 
								SelectedValuesChanged.InvokeAsync(new HashSet<T>(SelectedValues, _comparer));
 | 
				
			||||||
@ -293,11 +293,7 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
			if (_toStringFunc == value)
 | 
								if (_toStringFunc == value)
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			_toStringFunc = value;
 | 
								_toStringFunc = value;
 | 
				
			||||||
         Converter = new Converter<T>
 | 
								Converter = new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null);
 | 
				
			||||||
         {
 | 
					 | 
				
			||||||
            SetFunc = _toStringFunc ?? (x => x?.ToString()),
 | 
					 | 
				
			||||||
            //GetFunc = LookupValue,
 | 
					 | 
				
			||||||
         };
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -393,15 +389,15 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
		if (MultiSelectionTextFunc != null)
 | 
							if (MultiSelectionTextFunc != null)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return MultiSelection
 | 
								return MultiSelection
 | 
				
			||||||
            ? SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))),
 | 
								   ? SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))),
 | 
				
			||||||
               selectedConvertedValues: SelectedValues.Select(x => Converter.Set(x)).ToList(),
 | 
									  selectedConvertedValues: SelectedValues.Select(x => Converter.Convert(x)).ToList(),
 | 
				
			||||||
				  multiSelectionTextFunc: MultiSelectionTextFunc)
 | 
									  multiSelectionTextFunc: MultiSelectionTextFunc)
 | 
				
			||||||
			   : base.UpdateTextPropertyAsync(updateValue);
 | 
								   : base.UpdateTextPropertyAsync(updateValue);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return MultiSelection
 | 
								return MultiSelection
 | 
				
			||||||
            ? SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))))
 | 
								   ? SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))))
 | 
				
			||||||
			   : base.UpdateTextPropertyAsync(updateValue);
 | 
								   : base.UpdateTextPropertyAsync(updateValue);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -568,13 +564,13 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			if (MultiSelectionTextFunc != null)
 | 
								if (MultiSelectionTextFunc != null)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
            await SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))),
 | 
									await SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))),
 | 
				
			||||||
               selectedConvertedValues: SelectedValues.Select(x => Converter.Set(x)).ToList(),
 | 
									   selectedConvertedValues: SelectedValues.Select(x => Converter.Convert(x)).ToList(),
 | 
				
			||||||
				   multiSelectionTextFunc: MultiSelectionTextFunc);
 | 
									   multiSelectionTextFunc: MultiSelectionTextFunc);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
            await SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))), updateValue: false);
 | 
									await SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))), updateValue: false);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			UpdateSelectAllChecked();
 | 
								UpdateSelectAllChecked();
 | 
				
			||||||
@ -811,7 +807,7 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			multiSelectionText = text;
 | 
								multiSelectionText = text;
 | 
				
			||||||
			if (!string.IsNullOrWhiteSpace(multiSelectionText))
 | 
								if (!string.IsNullOrWhiteSpace(multiSelectionText))
 | 
				
			||||||
            Touched = true;
 | 
									Modified = true;
 | 
				
			||||||
			if (updateValue)
 | 
								if (updateValue)
 | 
				
			||||||
				await UpdateValuePropertyAsync(false);
 | 
									await UpdateValuePropertyAsync(false);
 | 
				
			||||||
			await TextChanged.InvokeAsync(multiSelectionText);
 | 
								await TextChanged.InvokeAsync(multiSelectionText);
 | 
				
			||||||
@ -1004,13 +1000,13 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
 | 
				
			|||||||
		_selectedValues = new HashSet<T>(selectedValues, _comparer);
 | 
							_selectedValues = new HashSet<T>(selectedValues, _comparer);
 | 
				
			||||||
		if (MultiSelectionTextFunc != null)
 | 
							if (MultiSelectionTextFunc != null)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
         await SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))),
 | 
								await SetCustomizedTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))),
 | 
				
			||||||
            selectedConvertedValues: SelectedValues.Select(x => Converter.Set(x)).ToList(),
 | 
								   selectedConvertedValues: SelectedValues.Select(x => Converter.Convert(x)).ToList(),
 | 
				
			||||||
			   multiSelectionTextFunc: MultiSelectionTextFunc);
 | 
								   multiSelectionTextFunc: MultiSelectionTextFunc);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
         await SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Set(x))), updateValue: false);
 | 
								await SetTextAsync(string.Join(Delimiter, SelectedValues.Select(x => Converter.Convert(x))), updateValue: false);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		UpdateSelectAllChecked();
 | 
							UpdateSelectAllChecked();
 | 
				
			||||||
		_selectedValues = selectedValues; // need to force selected values because Blazor overwrites it under certain circumstances due to changes of Text or Value
 | 
							_selectedValues = selectedValues; // need to force selected values because Blazor overwrites it under certain circumstances due to changes of Text or Value
 | 
				
			||||||
 | 
				
			|||||||
@ -129,7 +129,7 @@ public partial class SelectItem<T> : SelectItemBase, IDisposable
 | 
				
			|||||||
         var converter = Select?.Converter;
 | 
					         var converter = Select?.Converter;
 | 
				
			||||||
         if (converter == null)
 | 
					         if (converter == null)
 | 
				
			||||||
            return $"{Value}";
 | 
					            return $"{Value}";
 | 
				
			||||||
         return converter.Set(Value);
 | 
					         return converter.Convert(Value);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -28,8 +28,8 @@ public partial class Slider<T> : UIComponent
 | 
				
			|||||||
   [Category(CategoryTypes.Slider.Validation)]
 | 
					   [Category(CategoryTypes.Slider.Validation)]
 | 
				
			||||||
   public T Min
 | 
					   public T Min
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      get => Converter.Get(_min);
 | 
					      get => Converter.ConvertBack(_min);
 | 
				
			||||||
      set => _min = Converter.Set(value);
 | 
					      set => _min = Converter.Convert(value);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /// <summary>
 | 
					   /// <summary>
 | 
				
			||||||
@ -40,8 +40,8 @@ public partial class Slider<T> : UIComponent
 | 
				
			|||||||
   [Category(CategoryTypes.Slider.Validation)]
 | 
					   [Category(CategoryTypes.Slider.Validation)]
 | 
				
			||||||
   public T Max
 | 
					   public T Max
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      get => Converter.Get(_max);
 | 
					      get => Converter.ConvertBack(_max);
 | 
				
			||||||
      set => _max = Converter.Set(value);
 | 
					      set => _max = Converter.Convert(value);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /// <summary>
 | 
					   /// <summary>
 | 
				
			||||||
@ -52,8 +52,8 @@ public partial class Slider<T> : UIComponent
 | 
				
			|||||||
   [Category(CategoryTypes.Slider.Validation)]
 | 
					   [Category(CategoryTypes.Slider.Validation)]
 | 
				
			||||||
   public T Step
 | 
					   public T Step
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      get => Converter.Get(_step);
 | 
					      get => Converter.ConvertBack(_step);
 | 
				
			||||||
      set => _step = Converter.Set(value);
 | 
					      set => _step = Converter.Convert(value);
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /// <summary>
 | 
					   /// <summary>
 | 
				
			||||||
@ -73,7 +73,7 @@ public partial class Slider<T> : UIComponent
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   [Parameter]
 | 
					   [Parameter]
 | 
				
			||||||
   [Category(CategoryTypes.Slider.Behavior)]
 | 
					   [Category(CategoryTypes.Slider.Behavior)]
 | 
				
			||||||
   public Converter<T> Converter { get; set; } = new DefaultConverter<T>() { Culture = CultureInfo.InvariantCulture };
 | 
					   public ToStringConverter<T> Converter { get; set; } = new DefaultConverter<T>() { Culture = CultureInfo.InvariantCulture };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   [Parameter] public EventCallback<T> ValueChanged { get; set; }
 | 
					   [Parameter] public EventCallback<T> ValueChanged { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,10 +81,10 @@ public partial class Slider<T> : UIComponent
 | 
				
			|||||||
   [Category(CategoryTypes.Slider.Data)]
 | 
					   [Category(CategoryTypes.Slider.Data)]
 | 
				
			||||||
   public T Value
 | 
					   public T Value
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      get => Converter.Get(_value);
 | 
					      get => Converter.ConvertBack(_value);
 | 
				
			||||||
      set
 | 
					      set
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
         var d = Converter.Set(value);
 | 
					         var d = Converter.Convert(value);
 | 
				
			||||||
         if (_value == d)
 | 
					         if (_value == d)
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
         _value = d;
 | 
					         _value = d;
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@
 | 
				
			|||||||
                     HelperTextOnFocus="@HelperTextOnFocus"
 | 
					                     HelperTextOnFocus="@HelperTextOnFocus"
 | 
				
			||||||
                     CounterText="@GetCounterText()"
 | 
					                     CounterText="@GetCounterText()"
 | 
				
			||||||
                     FullWidth="@FullWidth"
 | 
					                     FullWidth="@FullWidth"
 | 
				
			||||||
                     Class="@Classname"
 | 
					                     Class="@ClassList"
 | 
				
			||||||
                     Error="@HasErrors"
 | 
					                     Error="@HasErrors"
 | 
				
			||||||
                     ErrorText="@GetErrorText()"
 | 
					                     ErrorText="@GetErrorText()"
 | 
				
			||||||
                     ErrorId="@ErrorId"
 | 
					                     ErrorId="@ErrorId"
 | 
				
			||||||
@ -43,7 +43,7 @@
 | 
				
			|||||||
                              AdornmentAriaLabel="@AdornmentAriaLabel"
 | 
					                              AdornmentAriaLabel="@AdornmentAriaLabel"
 | 
				
			||||||
                              IconSize="@IconSize"
 | 
					                              IconSize="@IconSize"
 | 
				
			||||||
                              OnAdornmentClick="@OnAdornmentClick"
 | 
					                              OnAdornmentClick="@OnAdornmentClick"
 | 
				
			||||||
                              Error="@Error"
 | 
					                              Error="@HasError"
 | 
				
			||||||
                              ErrorId="@ErrorId"
 | 
					                              ErrorId="@ErrorId"
 | 
				
			||||||
                              Immediate="@Immediate"
 | 
					                              Immediate="@Immediate"
 | 
				
			||||||
                              Margin="@Margin"
 | 
					                              Margin="@Margin"
 | 
				
			||||||
@ -83,7 +83,7 @@
 | 
				
			|||||||
                             AdornmentColor="@AdornmentColor"
 | 
					                             AdornmentColor="@AdornmentColor"
 | 
				
			||||||
                             IconSize="@IconSize"
 | 
					                             IconSize="@IconSize"
 | 
				
			||||||
                             OnAdornmentClick="@OnAdornmentClick"
 | 
					                             OnAdornmentClick="@OnAdornmentClick"
 | 
				
			||||||
                             Error="@Error"
 | 
					                             Error="@HasError"
 | 
				
			||||||
                             Immediate="@Immediate"
 | 
					                             Immediate="@Immediate"
 | 
				
			||||||
                             Margin="@Margin" OnBlur="@OnBlurred"
 | 
					                             Margin="@Margin" OnBlur="@OnBlurred"
 | 
				
			||||||
                             Clearable="@Clearable"
 | 
					                             Clearable="@Clearable"
 | 
				
			||||||
 | 
				
			|||||||
@ -7,13 +7,9 @@ namespace Connected.Components;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public partial class TextField<T> : DebouncedInput<T>
 | 
					public partial class TextField<T> : DebouncedInput<T>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	protected string Classname =>
 | 
					    private Mask? _maskReference;
 | 
				
			||||||
		new CssBuilder("mud-input-input-control")
 | 
					 | 
				
			||||||
		.AddClass(Class)
 | 
					 | 
				
			||||||
		.Build();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Input<string> InputReference { get; private set; }
 | 
						public Input<string> InputReference { get; private set; }
 | 
				
			||||||
	private Mask _maskReference;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// Type of the input element. It should be a valid HTML5 input type.
 | 
						/// Type of the input element. It should be a valid HTML5 input type.
 | 
				
			||||||
@ -38,6 +34,11 @@ public partial class TextField<T> : DebouncedInput<T>
 | 
				
			|||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
 | 
						[Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected string ClassList =>
 | 
				
			||||||
 | 
							new CssBuilder("mud-input-input-control")
 | 
				
			||||||
 | 
							.AddClass(Class)
 | 
				
			||||||
 | 
							.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public override ValueTask FocusAsync()
 | 
						public override ValueTask FocusAsync()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (_mask == null)
 | 
							if (_mask == null)
 | 
				
			||||||
@ -130,10 +131,10 @@ public partial class TextField<T> : DebouncedInput<T>
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		if (_mask != null)
 | 
							if (_mask != null)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			var textValue = Converter.Set(value);
 | 
								var textValue = Converter.Convert(value);
 | 
				
			||||||
			_mask.SetText(textValue);
 | 
								_mask.SetText(textValue);
 | 
				
			||||||
			textValue = Mask.GetCleanText();
 | 
								textValue = Mask.GetCleanText();
 | 
				
			||||||
			value = Converter.Get(textValue);
 | 
								value = Converter.ConvertBack(textValue);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return base.SetValueAsync(value, updateText);
 | 
							return base.SetValueAsync(value, updateText);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.Diagnostics.CodeAnalysis;
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
 | 
					using System.Dynamic;
 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Text.RegularExpressions;
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
using Connected.Annotations;
 | 
					using Connected.Annotations;
 | 
				
			||||||
@ -14,11 +15,13 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
	private const string format24Hours = "HH:mm";
 | 
						private const string format24Hours = "HH:mm";
 | 
				
			||||||
	private const string format12Hours = "hh:mm tt";
 | 
						private const string format12Hours = "hh:mm tt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public TimePicker() : base(new DefaultConverter<TimeSpan?>())
 | 
					
 | 
				
			||||||
 | 
						public TimePicker() : base()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Converter.GetFunc = OnGet;
 | 
							_timeFormat = format24Hours;
 | 
				
			||||||
		Converter.SetFunc = OnSet;
 | 
					
 | 
				
			||||||
		((DefaultConverter<TimeSpan?>)Converter).Format = format24Hours;
 | 
							Converter = new LambdaConverter<TimeSpan?, string>((e) => OnSet(e), (e) => OnGet(e));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		AdornmentIcon = Icons.Material.Filled.AccessTime;
 | 
							AdornmentIcon = Icons.Material.Filled.AccessTime;
 | 
				
			||||||
		AdornmentAriaLabel = "Open Time Picker";
 | 
							AdornmentAriaLabel = "Open Time Picker";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -30,7 +33,7 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		var time = DateTime.Today.Add(timespan.Value);
 | 
							var time = DateTime.Today.Add(timespan.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return time.ToString(((DefaultConverter<TimeSpan?>)Converter).Format, Culture);
 | 
							return time.ToString(_timeFormat, Culture);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private TimeSpan? OnGet(string value)
 | 
						private TimeSpan? OnGet(string value)
 | 
				
			||||||
@ -38,7 +41,7 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
		if (string.IsNullOrEmpty(value))
 | 
							if (string.IsNullOrEmpty(value))
 | 
				
			||||||
			return null;
 | 
								return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (DateTime.TryParseExact(value, ((DefaultConverter<TimeSpan?>)Converter).Format, Culture, DateTimeStyles.None, out var time))
 | 
							if (DateTime.TryParseExact(value, _timeFormat, Culture, DateTimeStyles.None, out var time))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return time.TimeOfDay;
 | 
								return time.TimeOfDay;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -105,13 +108,10 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			_amPm = value;
 | 
								_amPm = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (Converter is DefaultConverter<TimeSpan?> defaultConverter && string.IsNullOrWhiteSpace(_timeFormat))
 | 
								_timeFormat = AmPm ? format12Hours : format24Hours;
 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				defaultConverter.Format = AmPm ? format12Hours : format24Hours;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Touched = true;
 | 
								Modified = true;
 | 
				
			||||||
			SetTextAsync(Converter.Set(_value), false).AndForget();
 | 
								SetTextAsync(Converter.Convert(_value), false).AndForget();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -129,11 +129,9 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
				return;
 | 
									return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			_timeFormat = value;
 | 
								_timeFormat = value;
 | 
				
			||||||
			if (Converter is DefaultConverter<TimeSpan?> defaultConverter)
 | 
					 | 
				
			||||||
				defaultConverter.Format = _timeFormat;
 | 
					 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
			Touched = true;
 | 
								Modified = true;
 | 
				
			||||||
			SetTextAsync(Converter.Set(_value), false).AndForget();
 | 
								SetTextAsync(Converter.Convert(_value), false).AndForget();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -155,7 +153,7 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
			TimeIntermediate = time;
 | 
								TimeIntermediate = time;
 | 
				
			||||||
			_value = time;
 | 
								_value = time;
 | 
				
			||||||
			if (updateValue)
 | 
								if (updateValue)
 | 
				
			||||||
				await SetTextAsync(Converter.Set(_value), false);
 | 
									await SetTextAsync(Converter.Convert(_value), false);
 | 
				
			||||||
			UpdateTimeSetFromTime();
 | 
								UpdateTimeSetFromTime();
 | 
				
			||||||
			await TimeChanged.InvokeAsync(_value);
 | 
								await TimeChanged.InvokeAsync(_value);
 | 
				
			||||||
			BeginValidate();
 | 
								BeginValidate();
 | 
				
			||||||
@ -170,9 +168,9 @@ public partial class TimePicker : Picker<TimeSpan?>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	protected override Task StringValueChanged(string value)
 | 
						protected override Task StringValueChanged(string value)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Touched = true;
 | 
							Modified = true;
 | 
				
			||||||
		// Update the time property (without updating back the Value property)
 | 
							// Update the time property (without updating back the Value property)
 | 
				
			||||||
		return SetTimeAsync(Converter.Get(value), false);
 | 
							return SetTimeAsync(Converter.ConvertBack(value), false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//The last line cannot be tested
 | 
						//The last line cannot be tested
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ public partial class TreeViewItem<T> : UIComponent
 | 
				
			|||||||
   private string _text;
 | 
					   private string _text;
 | 
				
			||||||
   private bool _disabled;
 | 
					   private bool _disabled;
 | 
				
			||||||
   private bool _isChecked, _isSelected, _isServerLoaded;
 | 
					   private bool _isChecked, _isSelected, _isServerLoaded;
 | 
				
			||||||
   private Converter<T> _converter = new DefaultConverter<T>();
 | 
					   private ToStringConverter<T> _converter = new DefaultConverter<T>();
 | 
				
			||||||
   private readonly List<TreeViewItem<T>> _childItems = new();
 | 
					   private readonly List<TreeViewItem<T>> _childItems = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   protected string Classname =>
 | 
					   protected string Classname =>
 | 
				
			||||||
@ -68,7 +68,7 @@ public partial class TreeViewItem<T> : UIComponent
 | 
				
			|||||||
   [Category(CategoryTypes.TreeView.Behavior)]
 | 
					   [Category(CategoryTypes.TreeView.Behavior)]
 | 
				
			||||||
   public string Text
 | 
					   public string Text
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      get => string.IsNullOrEmpty(_text) ? _converter.Set(Value) : _text;
 | 
					      get => string.IsNullOrEmpty(_text) ? _converter.Convert(Value) : _text;
 | 
				
			||||||
      set => _text = value;
 | 
					      set => _text = value;
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user