Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<h4>@Localizer["Description"]</h4>

<DemoBlock Title="@Localizer["Block1Title"]" Introduction="@Localizer["Block1Intro"]" Name="Normal">
<section ignore>@Localizer["NormalDescription"]</section>
<section ignore>@((MarkupString)Localizer["NormalDescription"].Value)</section>
<div style="width: 200px;">
<AutoComplete Value="@_value" Items="@StaticItems" IsSelectAllTextOnFocus="true"></AutoComplete>
<AutoComplete Value="@_value" Items="@StaticItems" IsSelectAllTextOnFocus="true" IsClearable></AutoComplete>
</div>
</DemoBlock>

Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2121,7 +2121,7 @@
"OnSelectedItemChangedTitle": "OnSelectedItemChanged",
"OnSelectedItemChangedIntro": "Click the dropdown item or <kbd>Enter</kbd> 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 <code>IsClearable</code>",
"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 - <code>the data you want is not found</code>",
"NoDataTip": "There is nothing",
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2121,7 +2121,7 @@
"OnSelectedItemChangedTitle": "下拉菜单选选中回调",
"OnSelectedItemChangedIntro": "点击下拉菜单或者 <kbd>Enter</kbd> 回车时触发此回调方法",
"OnValueChanged": "Value 改变时回调方法",
"NormalDescription": "本例中请键入 123 字符串显示查看效果,自动完成组件初始化时给了自动提示数据集并且数据集无变化",
"NormalDescription": "本例中请键入 123 字符串显示查看效果,自动完成组件初始化时给了自动提示数据集并且数据集无变化,通过设置 <code>IsClearable</code> 开启清空小按钮",
"LikeMatchDescription": "本例中请键入 123 字符串显示查看效果,自动完成组件初始化时给了自动提示数据集并且数据集无变化",
"NoDataTipDescription": "本例中请键入 567 字符串由于自动完成信息中心无数据显示自定义提示信息 - <code>没有找到你想要的数据</code>",
"NoDataTip": "没有找到你想要的数据",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
<BootstrapLabel required="@Required" for="@InputId" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText"/>
}
<div class="auto-complete" id="@Id">
<div class="@ClassString" id="@Id">
<input @attributes="AdditionalAttributes" id="@InputId" class="@ClassName" autocomplete="off" type="text"
data-bs-toggle="@ToggleString" data-bs-placement="@PlacementString"
data-bs-offset="@OffsetString" data-bs-custom-class="@CustomClassString"
Expand All @@ -15,6 +15,10 @@
placeholder="@PlaceHolder" disabled="@Disabled" @ref="FocusElement"/>
<span class="form-select-append"><i class="@Icon"></i></span>
<span class="form-select-append ac-loading"><i class="@LoadingIcon"></i></span>
@if (GetClearable())
{
<span class="@ClearClassString"><i class="@ClearIcon"></i></span>
}
<RenderTemplate @ref="_dropdown">
@RenderDropdown
</RenderTemplate>
Expand Down
33 changes: 33 additions & 0 deletions src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ public partial class AutoComplete
[Parameter]
public bool ShowNoDataTip { get; set; } = true;

/// <summary>
/// Gets or sets whether the select component is clearable. Default is false.
/// </summary>
[Parameter]
public bool IsClearable { get; set; }

/// <summary>
/// Gets or sets the right-side clear icon. Default is fa-solid fa-angle-up.
/// </summary>
[Parameter]
[NotNull]
public string? ClearIcon { get; set; }

/// <summary>
/// IStringLocalizer service instance
/// </summary>
Expand All @@ -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();

/// <summary>
/// Gets the clear icon class string.
/// </summary>
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();

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand All @@ -125,6 +152,12 @@ protected override void OnParametersSet()
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value);

/// <summary>
/// Gets whether show the clear button.
/// </summary>
/// <returns></returns>
private bool GetClearable() => IsClearable && !IsDisabled;

/// <summary>
/// Callback method when a candidate item is clicked
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
29 changes: 29 additions & 0 deletions test/UnitTest/Components/AutoCompleteTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,35 @@ public void Value_Ok()
Assert.Equal(2, menus.Count);
}

[Fact]
public void IsClearable_Ok()
{
var cut = Context.RenderComponent<AutoComplete>();
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()
{
Expand Down
Loading