diff --git a/src/BootstrapBlazor.Server/Components/Samples/AutoCompletes.razor b/src/BootstrapBlazor.Server/Components/Samples/AutoCompletes.razor index 82152c3eba1..4e42c5a9605 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/AutoCompletes.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/AutoCompletes.razor @@ -6,9 +6,9 @@

@Localizer["Description"]

-
@Localizer["NormalDescription"]
+
@((MarkupString)Localizer["NormalDescription"].Value)
- +
diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 809cfdf7f3d..97d5e6bcc05 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -2121,7 +2121,7 @@ "OnSelectedItemChangedTitle": "OnSelectedItemChanged", "OnSelectedItemChangedIntro": "Click the dropdown item or Enter trigger the callback", "OnValueChanged": "Callback for the Value changed", - "NormalDescription": "In this example, type 123 strings to display the viewing effect, automatically give the component initialization to the auto-prompt dataset, and the dataset does not change", + "NormalDescription": "In this example, type 123 strings to display the viewing effect, automatically give the component initialization to the auto-prompt dataset, and the dataset does not change. Enable the clear button by setting IsClearable", "LikeMatchDescription": "In this example, type the abc string to display the viewing effect and select all matches in the collection that contain abc and have the same case", "NoDataTipDescription": "In this example, type 567 strings because the autocomplete information center does not display custom prompt information - the data you want is not found", "NoDataTip": "There is nothing", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index c65d2c414ea..70797ba585d 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -2121,7 +2121,7 @@ "OnSelectedItemChangedTitle": "下拉菜单选选中回调", "OnSelectedItemChangedIntro": "点击下拉菜单或者 Enter 回车时触发此回调方法", "OnValueChanged": "Value 改变时回调方法", - "NormalDescription": "本例中请键入 123 字符串显示查看效果,自动完成组件初始化时给了自动提示数据集并且数据集无变化", + "NormalDescription": "本例中请键入 123 字符串显示查看效果,自动完成组件初始化时给了自动提示数据集并且数据集无变化,通过设置 IsClearable 开启清空小按钮", "LikeMatchDescription": "本例中请键入 123 字符串显示查看效果,自动完成组件初始化时给了自动提示数据集并且数据集无变化", "NoDataTipDescription": "本例中请键入 567 字符串由于自动完成信息中心无数据显示自定义提示信息 - 没有找到你想要的数据", "NoDataTip": "没有找到你想要的数据", diff --git a/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor b/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor index b0c50b3674f..af0673f6455 100644 --- a/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor +++ b/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor @@ -5,7 +5,7 @@ { } -
+
+ @if (GetClearable()) + { + + } @RenderDropdown diff --git a/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs b/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs index f1fb45b1257..6c079f3c885 100644 --- a/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs +++ b/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs @@ -68,6 +68,19 @@ public partial class AutoComplete [Parameter] public bool ShowNoDataTip { get; set; } = true; + /// + /// Gets or sets whether the select component is clearable. Default is false. + /// + [Parameter] + public bool IsClearable { get; set; } + + /// + /// Gets or sets the right-side clear icon. Default is fa-solid fa-angle-up. + /// + [Parameter] + [NotNull] + public string? ClearIcon { get; set; } + /// /// IStringLocalizer service instance /// @@ -85,6 +98,19 @@ public partial class AutoComplete [NotNull] private RenderTemplate? _dropdown = null; + private string? ClassString => CssBuilder.Default("auto-complete") + .AddClass("is-clearable", IsClearable) + .Build(); + + /// + /// Gets the clear icon class string. + /// + private string? ClearClassString => CssBuilder.Default("clear-icon") + .AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None) + .AddClass($"text-success", IsValid.HasValue && IsValid.Value) + .AddClass($"text-danger", IsValid.HasValue && !IsValid.Value) + .Build(); + /// /// /// @@ -117,6 +143,7 @@ protected override void OnParametersSet() PlaceHolder ??= Localizer[nameof(PlaceHolder)]; Icon ??= IconTheme.GetIconByKey(ComponentIcons.AutoCompleteIcon); LoadingIcon ??= IconTheme.GetIconByKey(ComponentIcons.LoadingIcon); + ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectClearIcon); } /// @@ -125,6 +152,12 @@ protected override void OnParametersSet() /// protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value); + /// + /// Gets whether show the clear button. + /// + /// + private bool GetClearable() => IsClearable && !IsDisabled; + /// /// Callback method when a candidate item is clicked /// diff --git a/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js b/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js index 98ec1d7129a..f4ae52399b4 100644 --- a/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js +++ b/src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js @@ -133,6 +133,11 @@ export function init(id, invoke) { EventHandler.on(document, 'click', ac.closePopover); EventHandler.on(document, 'keyup', ac.keyup); }); + + EventHandler.on(el, 'click', '.clear-icon', e => { + input.value = ''; + invoke.invokeMethodAsync('TriggerFilter', ''); + }); } const handlerKeyup = (ac, e) => { @@ -188,7 +193,7 @@ export function dispose(id) { Data.remove(id) if (ac) { - const { popover, input, menu } = ac; + const { el, popover, input, menu } = ac; if (popover) { Popover.dispose(popover) if (input) { @@ -198,6 +203,8 @@ export function dispose(id) { EventHandler.off(menu, 'click'); EventHandler.off(input, 'keyup'); Input.dispose(input); + + EventHandler.off(el, 'click'); } const { AutoComplete } = window.BootstrapBlazor; diff --git a/test/UnitTest/Components/AutoCompleteTest.cs b/test/UnitTest/Components/AutoCompleteTest.cs index 198262900e3..f363c4196b4 100644 --- a/test/UnitTest/Components/AutoCompleteTest.cs +++ b/test/UnitTest/Components/AutoCompleteTest.cs @@ -54,6 +54,35 @@ public void Value_Ok() Assert.Equal(2, menus.Count); } + [Fact] + public void IsClearable_Ok() + { + var cut = Context.RenderComponent(); + Assert.DoesNotContain("clear-icon", cut.Markup); + + cut.SetParametersAndRender(pb => pb.Add(a => a.IsClearable, true)); + cut.Contains("clear-icon"); + + // Color + cut.SetParametersAndRender(pb => pb.Add(a => a.Color, Color.Danger)); + cut.Contains("clear-icon text-danger"); + + // 反射 Validate + var classStringProperty = cut.Instance.GetType().GetProperty("ClearClassString", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + Assert.NotNull(classStringProperty); + + var validProperty = cut.Instance.GetType().GetProperty("IsValid", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + Assert.NotNull(validProperty); + + validProperty.SetValue(cut.Instance, true); + var validClassString = classStringProperty.GetValue(cut.Instance); + Assert.Equal("clear-icon text-danger text-success", validClassString); + + validProperty.SetValue(cut.Instance, false); + validClassString = classStringProperty.GetValue(cut.Instance); + Assert.Equal("clear-icon text-danger text-danger", validClassString); + } + [Fact] public void Debounce_Ok() {