diff --git a/src/MatBlazor.Demo/Demo/DemoAutocomplete.razor b/src/MatBlazor.Demo/Demo/DemoAutocomplete.razor index aeb474e0..f3c8e707 100644 --- a/src/MatBlazor.Demo/Demo/DemoAutocomplete.razor +++ b/src/MatBlazor.Demo/Demo/DemoAutocomplete.razor @@ -1,19 +1,14 @@ -This component is in progress, use MatAutocompleteList instead! - -
Example
- + @code { - string value; - string[] options = new[] { "One", @@ -26,13 +21,11 @@ + @code { - string value; - string[] options = new[] { ""One"", @@ -46,3 +39,316 @@ +
Icon
+ + + + + + + ")> + + + +
Clear Button
+ + + + + + + ")> + + + +
ItemTemplate
+ + +

+ + +

+
@context.Name
+
@context.Price$
+
+ + +

+

+ Selected value: @(value?.Name) +

+ + @code + { + + public class Car + { + public string Name { get; set; } + public double Price { get; set; } + + public Car(string name, double price) + { + Name = name; + Price = price; + } + } + + Car value = null; + + Car[] options2 = new[] + { + new Car("Volkswagen Golf", 10000), + new Car("Volkswagen Passat", 11000), + new Car("Volkswagen Polo", 12000), + new Car("Ford Focus", 13000), + new Car("Ford Fiesta", 14000), + new Car("Ford Fusion", 15000), + new Car("Ford Mondeo", 16000), + }; + + } + +
+ + + i.Name)"" @bind-SelectedItem=""@value""> + +
+
@context.Name
+
@context.Price$
+
+
+
+

+

+ Selected value: @(value?.Name) +

+ + @code + { + + public class Car + { + public string Name { get; set; } + public double Price { get; set; } + + public Car(string name, double price) + { + Name = name; + Price = price; + } + } + + Car value = null; + + Car[] options2 = new[] + { + new Car(""Volkswagen Golf"", 10000), + new Car(""Volkswagen Passat"", 11000), + new Car(""Volkswagen Polo"", 12000), + new Car(""Ford Focus"", 13000), + new Car(""Ford Fiesta"", 14000), + new Car(""Ford Fusion"", 15000), + new Car(""Ford Mondeo"", 16000), + }; + + } + + ")>
+
+
+ + +
With Full Width
+ + + + +
+
@context.Name
+
@context.Price$
+
+
+
+
+ + i.Name)""> + +
+
@context.Name
+
@context.Price$
+
+
+ + ")>
+
+
+ +
With Disabled
+ + + + +
+
@context.Name
+
@context.Price$
+
+
+
+
+ + i.Name)"" SelectedItem=""@options2[1]""> + +
+
@context.Name
+
@context.Price$
+
+
+ + ")>
+
+
+ +
In an edit form with a Data Annotation Validator
+ + + + + + + Submit + + @code + { + class AutocompleteContextModel + { + public List Options { get; set; } = new List() { "A test option", "Another test option", "One more option" }; + + [System.ComponentModel.DataAnnotations.Required] + public string SelectedOption { get; set; } + } + AutocompleteContextModel model = new AutocompleteContextModel(); + + void HandleValidSubmit() + { + Console.WriteLine("On Valid Submit"); + } + } + + + + + + + Submit + + @code + { + class AutocompleteContextModel + { + public List Options { get; set; } = new List() { ""A test option"", ""Another test option"", ""One more option"" }; + + [System.ComponentModel.DataAnnotations.Required] + public string SelectedOption { get; set; } + } + AutocompleteContextModel model = new AutocompleteContextModel(); + + void HandleValidSubmit() + { + Console.WriteLine(""On Valid Submit""); + } + } + ")> + + + +
With ItemsSource and retrieving data from a backend.
+

This is useful when you have a backend with pagination based on the search text and you want to use the pagination directly in the backend server.

+ + + + +
+
@context.Name
+
@context.Price$
+
+
+
+ @code + { + public class DemoItemsSource : IAutocompleteItemsSource + { + + Car[] carsInServer = new[] + { + new Car("Volkswagen Golf", 10000), + new Car("Volkswagen Passat", 11000), + new Car("Volkswagen Polo", 12000), + new Car("Ford Focus", 13000), + new Car("Ford Fiesta", 14000), + new Car("Ford Fusion", 15000), + new Car("Ford Mondeo", 16000), + }; + + public async Task> GetFilteredItemsAsync(string searchText) + { + // We can retrieve the results from a backend server. + return await Task.Run(() => + { + System.Threading.Thread.Sleep(300); + return carsInServer.Where(car => string.IsNullOrEmpty(searchText) || car.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase)).Take(10); + }); + } + } + + DemoItemsSource DemoSource = new DemoItemsSource(); + } +
+ + i.Name)""> + +
+
@context.Name
+
@context.Price$
+
+
+ + @code + { + public class DemoItemsSource : IAutocompleteItemsSource + { + + Car[] carsInServer = new[] + { + new Car(""Volkswagen Golf"", 10000), + new Car(""Volkswagen Passat"", 11000), + new Car(""Volkswagen Polo"", 12000), + new Car(""Ford Focus"", 13000), + new Car(""Ford Fiesta"", 14000), + new Car(""Ford Fusion"", 15000), + new Car(""Ford Mondeo"", 16000), + }; + + public async Task> GetFilteredItemsAsync(string searchText) + { + // We can retrieve the results from a backend server. + return await Task.Run(() => + { + System.Threading.Thread.Sleep(300); + return carsInServer.Where(car => string.IsNullOrEmpty(searchText) || car.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase)).Take(10); + }); + } + } + + DemoItemsSource DemoSource = new DemoItemsSource(); + } + ")>
+
+
\ No newline at end of file diff --git a/src/MatBlazor.Demo/Doc/DocAutocompleteSearchResult.razor b/src/MatBlazor.Demo/Doc/DocAutocompleteSearchResult.razor new file mode 100644 index 00000000..985feb8b --- /dev/null +++ b/src/MatBlazor.Demo/Doc/DocAutocompleteSearchResult.razor @@ -0,0 +1,30 @@ +@inherits MatBlazor.Demo.Components.BaseDocComponent + +@* THIS FILE IS AUTOGENERATED FROM C# XML Comments! *@ +@* ALL MANUAL CHANGES WILL BE REMOVED! *@ + + +@if (!Secondary) {

AutocompleteSearchResult

} else {
AutocompleteSearchResult
} + +
+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
TItemGeneric argument
ListResultList<MatAutocompleteItem<TItem>>
SearchTextString
diff --git a/src/MatBlazor.Demo/Doc/DocIAutocompleteItemsSource.razor b/src/MatBlazor.Demo/Doc/DocIAutocompleteItemsSource.razor new file mode 100644 index 00000000..cacb9be9 --- /dev/null +++ b/src/MatBlazor.Demo/Doc/DocIAutocompleteItemsSource.razor @@ -0,0 +1,20 @@ +@inherits MatBlazor.Demo.Components.BaseDocComponent + +@* THIS FILE IS AUTOGENERATED FROM C# XML Comments! *@ +@* ALL MANUAL CHANGES WILL BE REMOVED! *@ + + +@if (!Secondary) {

IAutocompleteItemsSource

} else {
IAutocompleteItemsSource
} + +
+ + + + + + + + + + +
NameTypeDescription
TItemGeneric argument
diff --git a/src/MatBlazor.Demo/Doc/DocMatAutocomplete.razor b/src/MatBlazor.Demo/Doc/DocMatAutocomplete.razor index 8966788f..95416edc 100644 --- a/src/MatBlazor.Demo/Doc/DocMatAutocomplete.razor +++ b/src/MatBlazor.Demo/Doc/DocMatAutocomplete.razor @@ -6,7 +6,7 @@ @if (!Secondary) {

@Header

} else {
@Header
} -

Base class for any input control that optionally supports an .

+

The autocomplete is a normal text input enhanced by a panel of suggested options.

@@ -14,15 +14,10 @@ - - - - - - + @@ -44,6 +39,11 @@ + + + + + @@ -122,17 +122,17 @@ - + - - - + + + - - - + + + @@ -142,7 +142,7 @@ - + @@ -174,6 +174,11 @@ + + + + + @@ -199,6 +204,21 @@ + + + + + + + + + + + + + + + @@ -221,17 +241,17 @@ - + - + - + diff --git a/src/MatBlazor.Demo/Doc/DocMatAutocompleteItem.razor b/src/MatBlazor.Demo/Doc/DocMatAutocompleteItem.razor index e92bc1f4..53163149 100644 --- a/src/MatBlazor.Demo/Doc/DocMatAutocompleteItem.razor +++ b/src/MatBlazor.Demo/Doc/DocMatAutocompleteItem.razor @@ -12,11 +12,6 @@ - - - - - @@ -29,7 +24,7 @@ - +
Type Description
TValueGeneric argument
TItem Generic argumentType of items.
AttributesString Specifies one or more classnames for an DOM element.
CustomStringSelectorFunc<TItem,String>Gets or sets the function used to select the string part from the item, used both for filtering and displaying if no is given.
Dense Boolean
Items IEnumerable<TItem>Gets or sets the items source which will be filtered and displayed in the dropdown list.
ItemTemplateRenderFragment<TItem>ItemTemplate is used to render the elements in the popup if no template is given then the string value of the objects is displayed..ItemsSourceIAutocompleteItemsSource<TItem>Gets or sets the items source for autocomplete items. If provided, is ignored and everytime the user is typing something, this source will be used to populate the items in the dropdown list.
ItemValueSelectorFunc<TItem,TValue>This function is used to select the string part from the item, used both for filtering and displaying if no ItemTemplate is defined.ItemTemplateRenderFragment<TItem>Gets or sets the template used to render the elements in the popup, if no template is given then the string value of the objects is displayed.
Label
NumberOfElementsInPopup Nullable<Int32>Maximum number of elements displayed in the popupGets or sets the maximum number of elements displayed in the dropdown list.
OnFocusEventCallback<KeyboardEventArgs>
OnOpenedChangedEventCallback<Boolean>Gets or sets the event callback which is invoked when the dialog is opened or closed. The parameter will be "true" if the dialog was opened, "false" otherwise.
Outlined BooleanBoolean
SelectedItemTItemGets or sets the selected item from the dropdown list.
SelectedItemChangedEventCallback<TItem>ValueChanged is fired when the value is selected(by clicking on an element in the popup)
ShowClearButtonBooleanGets or sets the visibility of the clear button. The default value is "false". If the clear button is displayed and pressed, the text and selected value will be cleared.
Style String
ValueTValueString Gets or sets the value of the input. This should be used with two-way binding.
ValueChangedEventCallback<TValue>EventCallback<String> Gets or sets a callback that updates the bound value.
ValueExpressionExpression<Func<TValue>>Expression<Func<String>> Gets or sets an expression that identifies the bound value.
Type Description
TValueGeneric argument
TItem Generic argument
ValueTValueString
diff --git a/src/MatBlazor.Web/src/matAutocompleteList/matAutocompleteList.scss b/src/MatBlazor.Web/src/matAutocompleteList/matAutocompleteList.scss index f5319b36..e8788982 100644 --- a/src/MatBlazor.Web/src/matAutocompleteList/matAutocompleteList.scss +++ b/src/MatBlazor.Web/src/matAutocompleteList/matAutocompleteList.scss @@ -4,12 +4,12 @@ display: inline-block; } -.mat-autocomplete-list-wrapper-fullwidth +.mat-autocomplete-list-wrapper-fullwidth, .mat-autocomplete-fullwidth { width:100%; } -.mat-autocomplete-list-clearbutton +.mat-autocomplete-list-clearbutton, .mat-autocomplete-clearbutton { position: absolute; right: 5px; @@ -17,7 +17,7 @@ text-align: center; } -.mat-autocomplete-list-popup +.mat-autocomplete-list-popup, .mat-autocomplete-dropdown { width: 100%; border-style: solid; diff --git a/src/MatBlazor/Components/MatAutocomplete/AutocompleteSearchResult.cs b/src/MatBlazor/Components/MatAutocomplete/AutocompleteSearchResult.cs new file mode 100644 index 00000000..3251f60e --- /dev/null +++ b/src/MatBlazor/Components/MatAutocomplete/AutocompleteSearchResult.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace MatBlazor.Components.MatAutocomplete +{ + public class AutocompleteSearchResult + { + public List> ListResult { get; set; } + + public string SearchText { get; set; } + } +} diff --git a/src/MatBlazor/Components/MatAutocomplete/BaseMatAutocomplete.cs b/src/MatBlazor/Components/MatAutocomplete/BaseMatAutocomplete.cs index 69589077..9f83f2d1 100644 --- a/src/MatBlazor/Components/MatAutocomplete/BaseMatAutocomplete.cs +++ b/src/MatBlazor/Components/MatAutocomplete/BaseMatAutocomplete.cs @@ -2,89 +2,275 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using MatBlazor.Components.MatAutocomplete; +using Microsoft.AspNetCore.Components.Web; namespace MatBlazor { - public class BaseMatAutocomplete : MatInputTextComponent + /// + /// The autocomplete is a normal text input enhanced by a panel of suggested options. + /// + /// Type of items. + public class BaseMatAutocomplete : MatInputTextComponent { - protected BaseMatMenu MenuRef; - protected const int DefaultsElementsInPopup = 10; + private bool isOpened; + private TItem _value; + protected AutocompleteSearchResult SearchResult; + protected MatList ListRef; + protected ClassMapper WrapperClassMapper = new ClassMapper(); - protected BaseMatList ListRef; - - [Parameter] - public IEnumerable Items { get; set; } - - public bool IsOpened { get; set; } + protected BaseMatAutocomplete() + { + OnFocusEvent.Event += (sender, e) => OpenPopup(); + OnFocusOutEvent.Event += (sender, e) => ClosePopup(); + OnInputEvent.Event += (sender, e) => OnInputDetected(e); + OnKeyDownEvent.Event += OnKeyDownHandler; + WrapperClassMapper + .Add("mat-autocomplete") + .If("mat-autocomplete-fullwidth", () => FullWidth); + } - public BaseMatAutocomplete() + protected override async Task OnInitializedAsync() { - OnFocusEvent.Event += OnFocusEvent_Event; - OnFocusOutEvent.Event += OnFocusOutEvent_Event; -// ClassMapper.Add("mat-autocomplete"); + await base.OnInitializedAsync(); + await CacheFilteredItemsAsync(Value); } - private void OnFocusEvent_Event(object sender, Microsoft.AspNetCore.Components.Web.FocusEventArgs e) + protected async Task CacheFilteredItemsAsync(string searchText) { - IsOpened = true; - CallAfterRender(async () => + if (SearchResult == null || SearchResult.SearchText != searchText) { - await MenuRef.OpenAsync(InputRef); - }); + SearchResult = new AutocompleteSearchResult() + { + SearchText = searchText, + ListResult = ItemsSource != null + ? (await ItemsSource.GetFilteredItemsAsync(searchText)) + .Select(item => new MatAutocompleteItem() + { + Item = item, + Value = ComputeStringValue(item) + }).ToList() + : Items.Select(x => new MatAutocompleteItem() + { + Value = ComputeStringValue(x), + Item = x + }) + .Where + ( + x => x != null + && (string.IsNullOrEmpty(searchText) + || x.Value.ToLowerInvariant().Contains(searchText.ToLowerInvariant()) + ) + ) + .Take(NumberOfElementsInPopup ?? DefaultsElementsInPopup) + .ToList() + }; + StateHasChanged(); + } } - - private void OnFocusOutEvent_Event(object sender, Microsoft.AspNetCore.Components.Web.FocusEventArgs e) + + protected bool IsShowingClearButton { -// IsOpened = false; + get => ShowClearButton && !string.IsNullOrEmpty(this.CurrentValueAsString); } + public bool IsOpened + { + get { return isOpened; } + set + { + isOpened = value; + OnOpenedChanged.InvokeAsync(value); + this.StateHasChanged(); + } + } /// - /// This function is used to select the string part from the item, used both for filtering and displaying if no ItemTemplate is defined. + /// Gets or sets the maximum number of elements displayed in the dropdown list. /// [Parameter] - public Func ItemValueSelector { get; set; } + public int? NumberOfElementsInPopup { get; set; } - private TValue ComputeItemValue(TItem obj) + /// + /// Gets or sets the selected item from the dropdown list. + /// + [Parameter] + public TItem SelectedItem { - if (ItemValueSelector != null) + get { return _value; } + set { - return ItemValueSelector.Invoke(obj); + if (!EqualValues(value, default)) + { + var newValue = ComputeStringValue(value); + if (newValue != Value) + { + Value = newValue; + } + } + + if (EqualValues(value, _value)) + { + return; + } + + _value = value; + SelectedItemChanged.InvokeAsync(_value); } - return SwitchT.ParseFromString(obj?.ToString(), Format); + } + + private static bool EqualValues(TItem a1, TItem a2) + { + return EqualityComparer.Default.Equals(a1, a2); } /// - /// Maximum number of elements displayed in the popup + /// ValueChanged is fired when the value is selected(by clicking on an element in the popup) /// [Parameter] - public int? NumberOfElementsInPopup { get; set; } + public EventCallback SelectedItemChanged { get; set; } + + /// + /// Gets or sets the template used to render the elements in the popup, if no template is given then the string value of the objects is displayed. + /// + [Parameter] + public RenderFragment ItemTemplate { get; set; } + + /// + /// Gets or sets the function used to select the string part from the item, used both for filtering and displaying if no is given. + /// + [Parameter] + public Func CustomStringSelector { get; set; } + + /// + /// Gets or sets the items source which will be filtered and displayed in the dropdown list. + /// + [Parameter] + public IEnumerable Items { get; set; } + + /// + /// Gets or sets the event callback which is invoked when the dialog is opened or closed. The parameter will be "true" if the dialog was opened, "false" otherwise. + /// + [Parameter] + public EventCallback OnOpenedChanged { get; set; } + + /// + /// Gets or sets the visibility of the clear button. The default value is "false". If the clear button is displayed and pressed, the text and selected value will be cleared. + /// + [Parameter] + public bool ShowClearButton { get; set; } - public void ItemClicked(MatAutocompleteItem selectedObject) + /// + /// Gets or sets the items source for autocomplete items. If provided, is ignored and everytime the user is typing something, this source will be used to populate the items in the dropdown list. + /// + [Parameter] + public IAutocompleteItemsSource ItemsSource { get; set; } + + protected void OpenPopup() + { + if (Disabled) + { + return; + } + IsOpened = true; + } + + protected async void OnInputDetected(ChangeEventArgs ev) { -// todo: Value = selectedObject.Item; + Value = (string)ev.Value; + if (SearchResult != null) + { + SearchResult = null; + } + // In case the filtering is switching on other thread, we want to update the dropdown with a progress bar until the filtering is done + // so we're calling StateHasChanged. StateHasChanged(); + await CacheFilteredItemsAsync(Value); + } + + protected async void ClosePopup() + { + if (!EqualValues(SelectedItem, default) && Value != ComputeStringValue(SelectedItem)) + { + _value = default; + await SelectedItemChanged.InvokeAsync(_value); + } + IsOpened = false; + } + + public override Task SetParametersAsync(ParameterView parameters) + { + ValueExpression = () => Value; + return base.SetParametersAsync(parameters); + } + + protected async void OnKeyDownHandler(object sender, KeyboardEventArgs ev) + { + var currentIndex = await ListRef.GetSelectedIndex(); + var wasCurrentIndexChanged = false; + if (currentIndex < 0) + { + currentIndex = 0; + wasCurrentIndexChanged = true; + } + if (SearchResult != null && SearchResult.ListResult.Count > 0 && currentIndex > SearchResult.ListResult.Count) + { + currentIndex = SearchResult.ListResult.Count - 1; + wasCurrentIndexChanged = true; + } + if (ev.Key == "ArrowDown") + { + currentIndex++; + wasCurrentIndexChanged = true; + } + if (ev.Key == "ArrowUp") + { + currentIndex--; + wasCurrentIndexChanged = true; + } + if (ev.Key == "Backspace") + { + currentIndex = 0; + wasCurrentIndexChanged = true; + } + if (ev.Key == "Tab") + { + return; + } + if (wasCurrentIndexChanged) + { + await ListRef.SetSelectedIndex(currentIndex); + } + + if (ev.Key == "Enter" && SearchResult != null && currentIndex >= 0 && currentIndex < SearchResult.ListResult.Count) + { + ItemSelected(SearchResult.ListResult[currentIndex].Item); + } } + public void ItemSelected(TItem selectedObject) + { + SelectedItem = selectedObject; + StateHasChanged(); + } /// - /// ItemTemplate is used to render the elements in the popup if no template is given then the string value of the objects is displayed.. + /// Clears current value of the autocomplete text /// - [Parameter] - public RenderFragment ItemTemplate { get; set; } + /// + public void ClearText(EventArgs e) + { + Value = default; + SelectedItem = default; + StateHasChanged(); + } - protected IEnumerable> GetFilteredCollection(string searchText) + private string ComputeStringValue(TItem obj) { - return Items.Select(x => new MatAutocompleteItem() - { - Value = ComputeItemValue(x), - Item = x - }) - .Where(x => string.IsNullOrEmpty(searchText) - || (SwitchT.FormatValueAsString(x.Value, Format)?.ToLowerInvariant().Contains(searchText?.ToLowerInvariant()) == true)) - .Take(NumberOfElementsInPopup ?? DefaultsElementsInPopup); + return CustomStringSelector?.Invoke(obj) ?? obj?.ToString(); } } } diff --git a/src/MatBlazor/Components/MatAutocomplete/IAutocompleteItemsSource.cs b/src/MatBlazor/Components/MatAutocomplete/IAutocompleteItemsSource.cs new file mode 100644 index 00000000..cf37190f --- /dev/null +++ b/src/MatBlazor/Components/MatAutocomplete/IAutocompleteItemsSource.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MatBlazor +{ + public interface IAutocompleteItemsSource + { + /// + /// Returns the items to be populated in the dropdown list based on a search after the search text. + /// + /// The search text which is provided by the user in the input component. + /// + Task> GetFilteredItemsAsync(string searchText); + } +} diff --git a/src/MatBlazor/Components/MatAutocomplete/MatAutocomplete.razor b/src/MatBlazor/Components/MatAutocomplete/MatAutocomplete.razor index a3c6f418..b54b41bf 100644 --- a/src/MatBlazor/Components/MatAutocomplete/MatAutocomplete.razor +++ b/src/MatBlazor/Components/MatAutocomplete/MatAutocomplete.razor @@ -1,12 +1,39 @@ @namespace MatBlazor -@typeparam TValue @typeparam TItem -@inherits BaseMatAutocomplete - - -@base.BuildRenderTree - +@inherits BaseMatAutocomplete +
+ @base.BuildRenderTree + @if (IsOpened) + { +
+ + @if (SearchResult?.ListResult != null) + { + @foreach (var elementWrapper in SearchResult.ListResult) + { + + @if (ItemTemplate != null) + { + @ItemTemplate(elementWrapper.Item) + } + else + { + @(elementWrapper.Value) + } + + } + } + else + { + + + + } + +
+ } +
@code { @@ -15,30 +42,17 @@ return builder => { builder.AddContent(0, base.BuildRenderTreeChildContent()); - - builder.AddContent(1, @ - - @foreach (var elementWrapper in GetFilteredCollection(CurrentValueAsString)) - { - - @if (ItemTemplate != null) - { - @ItemTemplate(elementWrapper.Item) - } - else - { - @( elementWrapper.Value?.ToString()) - } - - - - } - - - - ); - + if (IsShowingClearButton) + { + builder.AddContent(1, BuildClearButton()); + } }; } + protected RenderFragment BuildClearButton() + { + return@
+ +
; +} } \ No newline at end of file diff --git a/src/MatBlazor/Components/MatAutocomplete/MatAutocompleteItem.cs b/src/MatBlazor/Components/MatAutocomplete/MatAutocompleteItem.cs index d39fd3c7..414d5353 100644 --- a/src/MatBlazor/Components/MatAutocomplete/MatAutocompleteItem.cs +++ b/src/MatBlazor/Components/MatAutocomplete/MatAutocompleteItem.cs @@ -1,8 +1,8 @@ namespace MatBlazor { - public class MatAutocompleteItem + public class MatAutocompleteItem { public TItem Item { get; set; } - public TValue Value { get; set; } + public string Value { get; set; } } } \ No newline at end of file diff --git a/src/MatBlazor/Components/MatNumericUpDownField/BaseMatNumericUpDownFieldInternal.cs b/src/MatBlazor/Components/MatNumericUpDownField/BaseMatNumericUpDownFieldInternal.cs index 1ef3fa4b..7179d051 100644 --- a/src/MatBlazor/Components/MatNumericUpDownField/BaseMatNumericUpDownFieldInternal.cs +++ b/src/MatBlazor/Components/MatNumericUpDownField/BaseMatNumericUpDownFieldInternal.cs @@ -11,11 +11,6 @@ namespace MatBlazor /// sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, decimal? public class BaseMatNumericUpDownFieldInternal : MatInputTextComponent { - protected override EventCallback OnKeyDownEvent() - { - return OnKeyDownEvent2; - } - protected void Increase() { CurrentValue = SwitchT.Increase(CurrentValue, Step, Maximum); @@ -62,21 +57,10 @@ protected override bool InputTextReadOnly() return base.InputTextReadOnly() || !AllowInput; } - private readonly EventCallback OnKeyDownEvent2; public BaseMatNumericUpDownFieldInternal() { - OnKeyDownEvent2 = EventCallback.Factory.Create(this, async (e) => - { - await OnKeyDown.InvokeAsync(e); - if (e.Key == "ArrowUp") - { - Increase(); - } - else if (e.Key == "ArrowDown") - { - Decrease(); - } - }); + OnKeyDownEvent.Event += this.OnKeyDownEvent_Event; + Maximum = SwitchT.GetMaximum(); Minimum = SwitchT.GetMinimum(); @@ -84,6 +68,18 @@ public BaseMatNumericUpDownFieldInternal() ClassMapper.Add("mat-text-field-with-actions-container"); } + private void OnKeyDownEvent_Event(object sender, KeyboardEventArgs e) + { + if (e.Key == "ArrowUp") + { + Increase(); + } + else if (e.Key == "ArrowDown") + { + Decrease(); + } + } + private readonly TValue ZeroValue = MatTypeConverter.ChangeType(0); protected override void OnParametersSet() diff --git a/src/MatBlazor/Components/MatTextField/BaseMatInputTextComponent.cs b/src/MatBlazor/Components/MatTextField/BaseMatInputTextComponent.cs index a0c9417f..8da75825 100644 --- a/src/MatBlazor/Components/MatTextField/BaseMatInputTextComponent.cs +++ b/src/MatBlazor/Components/MatTextField/BaseMatInputTextComponent.cs @@ -27,12 +27,15 @@ public abstract class BaseMatInputTextComponent : BaseMatInputTextElemen [Parameter] public EventCallback OnKeyDown { get; set; } + protected MatEventCallback OnKeyDownEvent; + [Parameter] public EventCallback OnKeyUp { get; set; } [Parameter] public EventCallback OnInput { get; set; } + protected MatEventCallback OnInputEvent; [Parameter] public string Label { get; set; } @@ -95,12 +98,6 @@ protected virtual bool InputTextReadOnly() public string Type { get; set; } = "text"; - protected virtual EventCallback OnKeyDownEvent() - { - return this.OnKeyDown; - } - - /// /// Css class of input element /// @@ -126,6 +123,8 @@ protected BaseMatInputTextComponent() { OnFocusEvent = new MatEventCallback(this, () => OnFocus); OnFocusOutEvent = new MatEventCallback(this, () => OnFocusOut); + OnInputEvent = new MatEventCallback(this, () => OnInput); + OnKeyDownEvent = new MatEventCallback(this, () => OnKeyDown); ClassMapper .Add("mat-text-field") diff --git a/src/MatBlazor/Components/MatTextField/MatInputTextComponent.razor b/src/MatBlazor/Components/MatTextField/MatInputTextComponent.razor index 3c1b5840..60acc352 100644 --- a/src/MatBlazor/Components/MatTextField/MatInputTextComponent.razor +++ b/src/MatBlazor/Components/MatTextField/MatInputTextComponent.razor @@ -16,11 +16,11 @@ @if (TextArea) { -