Skip to content

Commit 44c3b45

Browse files
authored
feat(Select): add IsAutoClearSearchTextWhenCollapsed parameter (#6024)
* refactor: 重构代码 * feat: 增加 IsAutoClearSearchTextWhenCollapsed 参数 * feat: 增加 OnCollapsed 回调方法 * test: 增加单元测试 * test: 增加单元测试 * doc: 更新文档 * doc: 更新注释文档 * doc: 增加注释 * chore: bump version 9.6.2-beta06
1 parent 665d46f commit 44c3b45

File tree

7 files changed

+143
-75
lines changed

7 files changed

+143
-75
lines changed

src/BootstrapBlazor.Server/Components/Samples/Selects.razor.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,14 @@ private AttributeItem[] GetAttributes() =>
321321
DefaultValue = "false"
322322
},
323323
new()
324+
{
325+
Name = "IsAutoClearSearchTextWhenCollapsed",
326+
Description = Localizer["SelectsIsAutoClearSearchTextWhenCollapsed"],
327+
Type = "bool",
328+
ValueList = "true|false",
329+
DefaultValue = "false"
330+
},
331+
new()
324332
{
325333
Name = "DisplayText",
326334
Description = Localizer["SelectsDisplayText"],

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3211,7 +3211,7 @@
32113211
"SelectsCustomTemplateTitle": "Custom option templates",
32123212
"SelectsCustomTemplateIntro": "By setting the <code>ItemTemplate</code> you can customize the option rendering style",
32133213
"SelectsShowSearchTitle": "Drop-down box with search box",
3214-
"SelectsShowSearchIntro": "Controls whether the search box is displayed by setting the <code>ShowSearch</code> property, which is not displayed by default <b>false</b>",
3214+
"SelectsShowSearchIntro": "Controls whether the search box is displayed by setting the <code>ShowSearch</code> property, which is not displayed by default <b>false</b>. You can set the <code>IsAutoClearSearchTextWhenCollapsed</code> parameter to control whether the text in the search box is automatically cleared after the drop-down box is collapsed. The default value is <b>false</b>.",
32153215
"SelectsConfirmSelectTitle": "Drop-down box with confirmation",
32163216
"SelectsConfirmSelectIntro": "Block changes to the current value by setting the <code>OnBeforeSelectedItemChange</code> delegate.",
32173217
"SelectsTimeZoneTitle": "Timezone",
@@ -3221,6 +3221,7 @@
32213221
"SwalFooter": "Click Confirm to change the option value and select Cancel to leave the value unchanged",
32223222
"SelectsShowLabel": "Whether to display the front label",
32233223
"SelectsShowSearch": "Whether to display the search box",
3224+
"SelectsIsAutoClearSearchTextWhenCollapsed": "Whether to automatically clear the search bar when the drop-down box is collapsed",
32243225
"SelectsDisplayText": "The front label displays text",
32253226
"SelectsClass": "Style",
32263227
"SelectsColor": "Color",

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3211,7 +3211,7 @@
32113211
"SelectsCustomTemplateTitle": "自定义选项模板",
32123212
"SelectsCustomTemplateIntro": "通过设置 <code>ItemTemplate</code> 可以自定义选项渲染样式",
32133213
"SelectsShowSearchTitle": "带搜索框的下拉框",
3214-
"SelectsShowSearchIntro": "通过设置 <code>ShowSearch</code> 属性控制是否显示搜索框,默认为 <b>false</b> 不显示搜索框",
3214+
"SelectsShowSearchIntro": "通过设置 <code>ShowSearch</code> 属性控制是否显示搜索框,默认为 <b>false</b> 不显示搜索框,可以通过设置 <code>IsAutoClearSearchTextWhenCollapsed</code> 参数控制下拉框收起后是否自动清空搜索框内文字,默认值为 <b>false</b> 不清空",
32153215
"SelectsConfirmSelectTitle": "带确认的下拉框",
32163216
"SelectsConfirmSelectIntro": "通过设置 <code>OnBeforeSelectedItemChange</code> 委托,阻止当前值的改变",
32173217
"SelectsTimeZoneTitle": "时区下拉框",
@@ -3221,6 +3221,7 @@
32213221
"SwalFooter": "点击确认改变选项值,选择取消后值不变",
32223222
"SelectsShowLabel": "是否显示前置标签",
32233223
"SelectsShowSearch": "是否显示搜索框",
3224+
"SelectsIsAutoClearSearchTextWhenCollapsed": "下拉框收起时是否自动清空搜索栏",
32243225
"SelectsDisplayText": "前置标签显示文本",
32253226
"SelectsClass": "样式",
32263227
"SelectsColor": "颜色",

src/BootstrapBlazor/BootstrapBlazor.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.6.2-beta05</Version>
4+
<Version>9.6.2-beta06</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/Select/Select.razor.cs

Lines changed: 95 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,13 @@ public partial class Select<TValue> : ISelect, ILookup
1818
[NotNull]
1919
private SwalService? SwalService { get; set; }
2020

21-
private string? ClassString => CssBuilder.Default("select dropdown")
22-
.AddClass("is-clearable", IsClearable)
23-
.AddClassFromAttributes(AdditionalAttributes)
24-
.Build();
25-
26-
private string? InputClassString => CssBuilder.Default("form-select form-control")
27-
.AddClass($"border-{Color.ToDescriptionString()}", Color != Color.None && !IsDisabled && !IsValid.HasValue)
28-
.AddClass($"border-success", IsValid.HasValue && IsValid.Value)
29-
.AddClass($"border-danger", IsValid.HasValue && !IsValid.Value)
30-
.AddClass(CssClass).AddClass(ValidCss)
31-
.Build();
32-
33-
private string? ActiveItem(SelectedItem item) => CssBuilder.Default("dropdown-item")
34-
.AddClass("active", item.Value == CurrentValueAsString)
35-
.AddClass("disabled", item.IsDisabled)
36-
.Build();
37-
38-
private readonly List<SelectedItem> _children = [];
21+
[Inject]
22+
[NotNull]
23+
private IStringLocalizer<Select<TValue>>? Localizer { get; set; }
3924

40-
private string? ScrollIntoViewBehaviorString => ScrollIntoViewBehavior == ScrollIntoViewBehavior.Smooth ? null : ScrollIntoViewBehavior.ToDescriptionString();
25+
[Inject]
26+
[NotNull]
27+
private ILookupService? InjectLookupService { get; set; }
4128

4229
/// <summary>
4330
/// Gets or sets the display template. Default is null.
@@ -125,31 +112,47 @@ public partial class Select<TValue> : ISelect, ILookup
125112
public string? DefaultVirtualizeItemText { get; set; }
126113

127114
/// <summary>
128-
/// <inheritdoc/>
115+
/// Gets or sets whether auto clear the search text when dropdown closed.
129116
/// </summary>
130-
IEnumerable<SelectedItem>? ILookup.Lookup { get; set; }
117+
[Parameter]
118+
public bool IsAutoClearSearchTextWhenCollapsed { get; set; }
131119

132120
/// <summary>
133-
/// <inheritdoc/>
121+
/// Gets or sets the dropdown collapsed callback method.
134122
/// </summary>
135-
StringComparison ILookup.LookupStringComparison { get; set; }
123+
[Parameter]
124+
public Func<Task>? OnCollapsed { get; set; }
136125

137-
[Inject]
138-
[NotNull]
139-
private IStringLocalizer<Select<TValue>>? Localizer { get; set; }
126+
IEnumerable<SelectedItem>? ILookup.Lookup { get => Items; set => Items = value; }
140127

141-
/// <summary>
142-
/// Gets or sets the injected lookup service instance.
143-
/// </summary>
144-
[Inject]
145-
[NotNull]
146-
private ILookupService? InjectLookupService { get; set; }
128+
StringComparison ILookup.LookupStringComparison { get => StringComparison; set => StringComparison = value; }
147129

148130
/// <summary>
149131
/// <inheritdoc/>
150132
/// </summary>
151133
protected override string? RetrieveId() => InputId;
152134

135+
private string? ClassString => CssBuilder.Default("select dropdown")
136+
.AddClass("is-clearable", IsClearable)
137+
.AddClassFromAttributes(AdditionalAttributes)
138+
.Build();
139+
140+
private string? InputClassString => CssBuilder.Default("form-select form-control")
141+
.AddClass($"border-{Color.ToDescriptionString()}", Color != Color.None && !IsDisabled && !IsValid.HasValue)
142+
.AddClass($"border-success", IsValid.HasValue && IsValid.Value)
143+
.AddClass($"border-danger", IsValid.HasValue && !IsValid.Value)
144+
.AddClass(CssClass).AddClass(ValidCss)
145+
.Build();
146+
147+
private string? ActiveItem(SelectedItem item) => CssBuilder.Default("dropdown-item")
148+
.AddClass("active", item.Value == CurrentValueAsString)
149+
.AddClass("disabled", item.IsDisabled)
150+
.Build();
151+
152+
private readonly List<SelectedItem> _children = [];
153+
154+
private string? ScrollIntoViewBehaviorString => ScrollIntoViewBehavior == ScrollIntoViewBehavior.Smooth ? null : ScrollIntoViewBehavior.ToDescriptionString();
155+
153156
private string? InputId => $"{Id}_input";
154157

155158
private bool _init = true;
@@ -169,44 +172,6 @@ private SelectedItem? SelectedRow
169172
}
170173
}
171174

172-
private SelectedItem? GetSelectedRow()
173-
{
174-
if (Value is null)
175-
{
176-
_lastSelectedValueString = "";
177-
_init = false;
178-
return null;
179-
}
180-
181-
var item = IsVirtualize ? GetItemByVirtualized() : GetItemByRows();
182-
if (item != null)
183-
{
184-
if (_init && DisableItemChangedWhenFirstRender)
185-
{
186-
187-
}
188-
else
189-
{
190-
_ = SelectedItemChanged(item);
191-
_init = false;
192-
}
193-
}
194-
return item;
195-
}
196-
197-
private SelectedItem? GetItemWithEnumValue() => ValueType.IsEnum ? Rows.Find(i => i.Value == Convert.ToInt32(Value).ToString()) : null;
198-
199-
private SelectedItem GetItemByVirtualized() => new(CurrentValueAsString, _defaultVirtualizedItemText);
200-
201-
private SelectedItem? GetItemByRows()
202-
{
203-
var item = GetItemWithEnumValue()
204-
?? Rows.Find(i => i.Value == CurrentValueAsString)
205-
?? Rows.Find(i => i.Active)
206-
?? Rows.FirstOrDefault(i => !i.IsDisabled);
207-
return item;
208-
}
209-
210175
/// <summary>
211176
/// <inheritdoc/>
212177
/// </summary>
@@ -308,9 +273,30 @@ private bool TryParseSelectItem(string value, [MaybeNullWhen(false)] out TValue
308273
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new
309274
{
310275
ConfirmMethodCallback = nameof(ConfirmSelectedItem),
311-
SearchMethodCallback = nameof(TriggerOnSearch)
276+
SearchMethodCallback = nameof(TriggerOnSearch),
277+
TriggerCollapsed = (OnCollapsed != null || IsAutoClearSearchTextWhenCollapsed) ? nameof(TriggerCollapsed) : null
312278
});
313279

280+
/// <summary>
281+
/// Trigger <see cref="OnCollapsed"/> event callback method. called by JavaScript.
282+
/// </summary>
283+
/// <returns></returns>
284+
[JSInvokable]
285+
public async Task TriggerCollapsed()
286+
{
287+
if (OnCollapsed != null)
288+
{
289+
await OnCollapsed();
290+
}
291+
292+
if (IsAutoClearSearchTextWhenCollapsed)
293+
{
294+
SearchText = string.Empty;
295+
_itemsCache = null;
296+
StateHasChanged();
297+
}
298+
}
299+
314300
/// <summary>
315301
/// <inheritdoc/>
316302
/// </summary>
@@ -440,4 +426,42 @@ private async Task OnChange(ChangeEventArgs args)
440426
}
441427
}
442428
}
429+
430+
private SelectedItem? GetSelectedRow()
431+
{
432+
if (Value is null)
433+
{
434+
_lastSelectedValueString = "";
435+
_init = false;
436+
return null;
437+
}
438+
439+
var item = IsVirtualize ? GetItemByVirtualized() : GetItemByRows();
440+
if (item != null)
441+
{
442+
if (_init && DisableItemChangedWhenFirstRender)
443+
{
444+
445+
}
446+
else
447+
{
448+
_ = SelectedItemChanged(item);
449+
_init = false;
450+
}
451+
}
452+
return item;
453+
}
454+
455+
private SelectedItem? GetItemWithEnumValue() => ValueType.IsEnum ? Rows.Find(i => i.Value == Convert.ToInt32(Value).ToString()) : null;
456+
457+
private SelectedItem GetItemByVirtualized() => new(CurrentValueAsString, _defaultVirtualizedItemText);
458+
459+
private SelectedItem? GetItemByRows()
460+
{
461+
var item = GetItemWithEnumValue()
462+
?? Rows.Find(i => i.Value == CurrentValueAsString)
463+
?? Rows.Find(i => i.Active)
464+
?? Rows.FirstOrDefault(i => !i.IsDisabled);
465+
return item;
466+
}
443467
}

src/BootstrapBlazor/Components/Select/Select.razor.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ export function init(id, invoke, options) {
1010
}
1111

1212
const search = el.querySelector(".search-text")
13-
const popover = Popover.init(el)
13+
const popover = Popover.init(el, {
14+
hideCallback: () => {
15+
if (options.triggerCollapsed) {
16+
invoke.invokeMethodAsync(options.triggerCollapsed);
17+
}
18+
}
19+
});
1420
const input = el.querySelector(`#${id}_input`);
1521
const select = {
1622
el, invoke, options,

test/UnitTest/Components/SelectTest.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,34 @@ public void Color_Ok()
428428
Assert.Contains("border-danger", cut.Markup);
429429
}
430430

431+
[Fact]
432+
public async Task OnCollapsed_Ok()
433+
{
434+
var collapsed = false;
435+
var cut = Context.RenderComponent<Select<string>>(pb =>
436+
{
437+
pb.Add(a => a.OnCollapsed, () => { collapsed = true; return Task.CompletedTask; });
438+
});
439+
await cut.InvokeAsync(() => cut.Instance.TriggerCollapsed());
440+
Assert.True(collapsed);
441+
}
442+
443+
[Fact]
444+
public async Task IsAutoClearSearchTextWhenCollapsed_Ok()
445+
{
446+
var cut = Context.RenderComponent<Select<string>>(pb =>
447+
{
448+
pb.Add(a => a.ShowSearch, true);
449+
pb.Add(a => a.IsAutoClearSearchTextWhenCollapsed, true);
450+
});
451+
452+
await cut.InvokeAsync(() => cut.Instance.TriggerOnSearch("123"));
453+
cut.Contains("value=\"123\"");
454+
455+
await cut.InvokeAsync(() => cut.Instance.TriggerCollapsed());
456+
cut.Contains("value=\"\"");
457+
}
458+
431459
[Fact]
432460
public void Validate_Ok()
433461
{

0 commit comments

Comments
 (0)