Skip to content

Commit fff6f06

Browse files
authored
feat(Search): add IsClearable parameter (#5296)
* chore: 更新 OctIcon 支持 Color 参数 * doc: 更新图标示例 * chore: 更新依赖包 * feat: 增加 SearchContext 精简代码逻辑 减少级联参数 * style: 增加样式 * doc: 更新示例代码 * doc: 更新示例文档 * test: 增加单元测试 * test: 更新单元测试 * chore: bump version 9.3.1-beta07 * test: 更新单元测试 * feat: 增加 ButtonTemplate 参数 * feat: 增加 ShowClearIcon 参数 * refactor: 增加 form-control-group 节点 * style: 更新样式 * feat: 增加 PrefixButtonTemplate 参数 * refactor: 增加 PrefixButtonTemplate 逻辑 * style: 增加 bb-search-icon-input-padding-right 变量 * doc: 增加示例 * refactor: 更改 IsClearable 参数 * refactor: 调整 ButtonTemplate 位置 * doc: 增加示例代码 * doc: 更新示例 * doc: 增加文档注释 * doc: 增加注释文档多语言 * test: 更新单元测试 * refactor: 重构 DOM 结构精简样式
1 parent 15fa69e commit fff6f06

File tree

15 files changed

+399
-51
lines changed

15 files changed

+399
-51
lines changed

src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</ItemGroup>
2121

2222
<ItemGroup>
23-
<PackageReference Include="BootstrapBlazor.AntDesignIcon" Version="9.0.1" />
23+
<PackageReference Include="BootstrapBlazor.AntDesignIcon" Version="9.0.2" />
2424
<PackageReference Include="BootstrapBlazor.AzureOpenAI" Version="9.0.0" />
2525
<PackageReference Include="BootstrapBlazor.AzureTranslator" Version="9.0.0" />
2626
<PackageReference Include="BootstrapBlazor.BaiduSpeech" Version="9.0.0" />
@@ -32,7 +32,7 @@
3232
<PackageReference Include="BootstrapBlazor.CherryMarkdown" Version="9.0.0" />
3333
<PackageReference Include="BootstrapBlazor.Dock" Version="9.0.0" />
3434
<PackageReference Include="BootstrapBlazor.DockView" Version="9.0.3" />
35-
<PackageReference Include="BootstrapBlazor.DriverJs" Version="9.0.1" />
35+
<PackageReference Include="BootstrapBlazor.DriverJs" Version="9.0.3" />
3636
<PackageReference Include="BootstrapBlazor.ElementIcon" Version="9.0.1" />
3737
<PackageReference Include="BootstrapBlazor.FileViewer" Version="9.0.0" />
3838
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" />
@@ -48,7 +48,7 @@
4848
<PackageReference Include="BootstrapBlazor.Mermaid" Version="9.0.3" />
4949
<PackageReference Include="BootstrapBlazor.MindMap" Version="9.1.3" />
5050
<PackageReference Include="BootstrapBlazor.MouseFollower" Version="9.0.1" />
51-
<PackageReference Include="BootstrapBlazor.OctIcon" Version="9.0.1" />
51+
<PackageReference Include="BootstrapBlazor.OctIcon" Version="9.0.2" />
5252
<PackageReference Include="BootstrapBlazor.OnScreenKeyboard" Version="9.0.1" />
5353
<PackageReference Include="BootstrapBlazor.PdfReader" Version="9.0.0" />
5454
<PackageReference Include="BootstrapBlazor.Player" Version="9.0.0" />
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@typeparam TValue
2+
3+
<Button Text="Clear1" OnClick="OnClickClear"></Button>
4+
<Button Text="Search1" OnClick="OnClickSearch"></Button>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Server.Components.Components;
7+
8+
/// <summary>
9+
/// SearchButtonTemplateDemo 示例组件
10+
/// </summary>
11+
public partial class SearchButtonTemplateDemo<TValue>
12+
{
13+
/// <summary>
14+
/// 获得/设置 <see cref="SearchContext{TValue}"/> 实例"/>
15+
/// </summary>
16+
[Parameter, EditorRequired, NotNull]
17+
public SearchContext<TValue>? Context { get; set; }
18+
19+
[Inject, NotNull]
20+
private ToastService? ToastService { get; set; }
21+
22+
private async Task OnClickSearch()
23+
{
24+
await Context.OnSearchAsync();
25+
26+
await ToastService.Information("Search-ButtonTemplate", "Click Search1 Button");
27+
}
28+
29+
private async Task OnClickClear()
30+
{
31+
await Context.OnClearAsync();
32+
33+
await ToastService.Information("Search-ButtonTemplate", "Click Clear1 Button");
34+
}
35+
}

src/BootstrapBlazor.Server/Components/Samples/Icons/AntDesignIcons.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
<Pre>&lt;link href="_content/BootstrapBlazor.AntDesignIcon/BootstrapBlazor.AntDesignIcon.bundle.scp.css" rel="stylesheet"&gt;</Pre>
2424

2525
<div class="mb-2">
26-
<AntDesignIcon Category="AntDesignIconCategory.Outlined" Name="github"></AntDesignIcon>
26+
<AntDesignIcon Category="AntDesignIconCategory.Filled" Name="left-circle"></AntDesignIcon>
27+
<AntDesignIcon Category="AntDesignIconCategory.Outlined" Name="left-circle"></AntDesignIcon>
2728
</div>
2829

2930
<Pre>&lt;AntDesignIcon Category="AntDesignIconCategory.Outlined" Name="github"&gt;&lt;/AntDesignIcon&gt;</Pre>

src/BootstrapBlazor.Server/Components/Samples/Searches.razor

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,106 @@
9292
</Search>
9393
</div>
9494
</div>
95+
</DemoBlock>
96+
97+
<DemoBlock Title="@Localizer["SearchesIconTemplateTitle"]"
98+
Introduction="@Localizer["SearchesIconTemplateIntro"]"
99+
Name="IconTemplate">
100+
<section ignore>
101+
<p>@((MarkupString)Localizer["SearchesIconTemplateDesc"].Value)</p>
102+
</section>
103+
<div class="row g-3">
104+
<div class="col-12">
105+
<Search PlaceHolder="@Localizer["SearchesPlaceHolder"]"
106+
OnSearch="@OnModelSearch"
107+
IsClearable="true" ShowSearchButton="false"
108+
ShowPrefixIcon="true" PrefixIcon="fa fa-flag">
109+
<IconTemplate>
110+
<i class="search-icon fa-solid fa-camera" @onclick="() => OnClickCamera(context)"></i>
111+
</IconTemplate>
112+
</Search>
113+
</div>
114+
</div>
115+
</DemoBlock>
95116

117+
<DemoBlock Title="@Localizer["SearchesButtonTemplateTitle"]"
118+
Introduction="@Localizer["SearchesButtonTemplateIntro"]"
119+
Name="ButtonTemplate">
120+
<section ignore>
121+
<p>@((MarkupString)Localizer["SearchesButtonTemplateDesc"].Value)</p>
122+
<p>@((MarkupString)Localizer["SearchesButtonTemplateDesc2"].Value)</p>
123+
<Pre>[Parameter, EditorRequired, NotNull]
124+
public SearchContext&lt;TValue&gt;? Context { get; set; }
125+
126+
[Inject, NotNull]
127+
private ToastService? ToastService { get; set; }
128+
129+
private async Task OnClickSearch()
130+
{
131+
await Context.OnSearchAsync();
132+
133+
await ToastService.Information("Search-ButtonTemplate", "Click Search1 Button");
134+
}
135+
136+
private async Task OnClickClear()
137+
{
138+
await Context.OnClearAsync();
139+
140+
await ToastService.Information("Search-ButtonTemplate", "Click Clear1 Button");
141+
}</Pre>
142+
</section>
143+
<div class="row g-3">
144+
<div class="col-12">
145+
<Search PlaceHolder="@Localizer["SearchesPlaceHolder"]"
146+
OnSearch="@OnModelSearch"
147+
IsClearable="true"
148+
ShowPrefixIcon="true" PrefixIcon="fa fa-flag">
149+
<PrefixButtonTemplate>
150+
<Button Text="Text1"></Button>
151+
<Button Text="Text2"></Button>
152+
</PrefixButtonTemplate>
153+
<ButtonTemplate>
154+
<SearchButtonTemplateDemo Context="context"></SearchButtonTemplateDemo>
155+
</ButtonTemplate>
156+
</Search>
157+
</div>
158+
</div>
159+
</DemoBlock>
160+
161+
<DemoBlock Title="@Localizer["SearchesIsClearableTitle"]"
162+
Introduction="@Localizer["SearchesIsClearableIntro"]"
163+
Name="ButtonTemplate">
164+
<section ignore>
165+
<div class="row g-3">
166+
<div class="col-12 col-sm-4">
167+
<BootstrapInputGroup>
168+
<BootstrapInputGroupLabel DisplayText="IsClearable"></BootstrapInputGroupLabel>
169+
<Checkbox @bind-Value="_isClearable"></Checkbox>
170+
</BootstrapInputGroup>
171+
</div>
172+
<div class="col-12 col-sm-4">
173+
<BootstrapInputGroup>
174+
<BootstrapInputGroupLabel DisplayText="ShowClearButton"></BootstrapInputGroupLabel>
175+
<Checkbox @bind-Value="_showClearButton"></Checkbox>
176+
</BootstrapInputGroup>
177+
</div>
178+
<div class="col-12 col-sm-4">
179+
<BootstrapInputGroup>
180+
<BootstrapInputGroupLabel DisplayText="ShowSearchButton"></BootstrapInputGroupLabel>
181+
<Checkbox @bind-Value="_showSearchButton"></Checkbox>
182+
</BootstrapInputGroup>
183+
</div>
184+
</div>
185+
</section>
186+
<div class="row g-3">
187+
<div class="col-12 col-sm-6">
188+
<Search PlaceHolder="@Localizer["SearchesPlaceHolder"]"
189+
OnSearch="@OnModelSearch"
190+
IsClearable="_isClearable" ShowClearButton="_showClearButton" ShowSearchButton="_showSearchButton"
191+
ShowPrefixIcon="true" PrefixIcon="fa-brands fa-github">
192+
</Search>
193+
</div>
194+
</div>
96195
</DemoBlock>
97196

98197
<AttributeTable Items="@GetAttributes()" />

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

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace BootstrapBlazor.Server.Components.Samples;
1010
/// </summary>
1111
public sealed partial class Searches
1212
{
13+
[Inject, NotNull]
14+
private ToastService? ToastService { get; set; }
15+
1316
[NotNull]
1417
private ConsoleLogger? Logger { get; set; }
1518

@@ -78,6 +81,17 @@ private async Task<IEnumerable<Foo>> OnSearchFoo(string searchText)
7881
: Enumerable.Range(1, 10).Select(i => LocalizerFoo["Foo.Name", $"{i:d4}"].Value).ToList();
7982
}
8083

84+
private async Task OnClickCamera(SearchContext<string?> context)
85+
{
86+
await Task.Delay(10);
87+
88+
await ToastService.Information("Custom IconTemplate", "Click custom icon");
89+
}
90+
91+
private bool _isClearable = true;
92+
private bool _showClearButton = false;
93+
private bool _showSearchButton = false;
94+
8195
/// <summary>
8296
/// 获得属性方法
8397
/// </summary>
@@ -93,11 +107,35 @@ private AttributeItem[] GetAttributes() =>
93107
},
94108
new()
95109
{
96-
Name="SearchButtonLoadingIcon",
97-
Description = Localizer["SearchesButtonLoadingIcon"],
110+
Name="IsClearable",
111+
Description = Localizer["SearchesIsClearable"],
112+
Type = "bool",
113+
ValueList = "true|false",
114+
DefaultValue = "false"
115+
},
116+
new()
117+
{
118+
Name="ClearIcon",
119+
Description = Localizer["SearchesClearIcon"],
98120
Type = "string",
99121
ValueList = " — ",
100-
DefaultValue = "fa-fw fa-spin fa-solid fa-spinner"
122+
DefaultValue = " — "
123+
},
124+
new()
125+
{
126+
Name="PrefixButtonTemplate",
127+
Description = Localizer["SearchesPrefixButtonTemplate"],
128+
Type = "RenderFragment",
129+
ValueList = " — ",
130+
DefaultValue = " — "
131+
},
132+
new()
133+
{
134+
Name="ButtonTemplate",
135+
Description = Localizer["SearchesButtonTemplate"],
136+
Type = "RenderFragment",
137+
ValueList = " — ",
138+
DefaultValue = " — "
101139
},
102140
new() {
103141
Name = "ClearButtonIcon",

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4226,7 +4226,20 @@
42264226
"SearchesItemTemplateIntro": "By setting <code>ItemTemplate</code> and matching generic data, you can achieve any desired effect. In this example, by searching for any keyword, the backend calls any third-party search results and displays them. After selecting the search item, you can handle it yourself through the <code>OnSelectedItemChanged</code> callback method",
42274227
"SearchesShowPrefixIconTitle": "ShowPrefixIcon",
42284228
"SearchesShowPrefixIconIntro": "Control whether to show the prefix icon by setting the <code>ShowPrefixIcon</code> parameter",
4229-
"SearchesShowPrefixIconDescription": "You can customize the prefix through <code>PrefixIconTemplate</code>. In this example, the <code>Svg</code> icon is used through the prefix template."
4229+
"SearchesShowPrefixIconDescription": "You can customize the prefix through <code>PrefixIconTemplate</code>. In this example, the <code>Svg</code> icon is used through the prefix template.",
4230+
"SearchesButtonTemplateTitle": "Button Template",
4231+
"SearchesButtonTemplateIntro": "Customize the buttons displayed by the component by setting <code>ButtonTemplate</code>",
4232+
"SearchesButtonTemplateDesc": "Customize the buttons displayed in front of the component by setting <code>PrefixButtonTemplate</code>",
4233+
"SearchesButtonTemplateDesc2": "In a custom template, you can call the <code>OnClear</code> <code>OnSearch</code> method inside the <code>Search</code> component with the context.",
4234+
"SearchesIsClearableTitle": "IsClearable",
4235+
"SearchesIsClearableIntro": "Display the clear icon by setting <code>IsClearable=\"true\"</code>",
4236+
"SearchesIsClearable": "Whether to display the clear icon",
4237+
"SearchesClearIcon": "Clear Icon",
4238+
"SearchesPrefixButtonTemplate": "Prefix button template",
4239+
"SearchesButtonTemplate": "Button template",
4240+
"SearchesIconTemplateTitle": "Icon Template",
4241+
"SearchesIconTemplateIntro": "Customize the icon displayed by the component by setting <code>IconTemplate</code>",
4242+
"SearchesIconTemplateDesc": "The search component context <code>SearchContext&lt;string&gt;</code> provides the <code>OnClear</code> <code>OnSearch</code> methods within the component"
42304243
},
42314244
"BootstrapBlazor.Server.Components.Samples.Titles": {
42324245
"Title": "Title",

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4226,7 +4226,20 @@
42264226
"SearchesItemTemplateIntro": "通过设置 <code>ItemTemplate</code> 配合泛型数据可以做出自己想要的任何效果,本例中通过搜索任意关键字,后台调用任意第三方搜索结果并且进行展示,选中搜索项后通过 <code>OnSelectedItemChanged</code> 回调方法可以自行处理",
42274227
"SearchesShowPrefixIconTitle": "显示前缀图标",
42284228
"SearchesShowPrefixIconIntro": "通过设置 <code>ShowPrefixIcon</code> 参数控制是否显示前缀图标",
4229-
"SearchesShowPrefixIconDescription": "可以通过 <code>PrefixIconTemplate</code> 自定义前缀,本例中通过前缀模板使用 <code>Svg</code> 图标"
4229+
"SearchesShowPrefixIconDescription": "可以通过 <code>PrefixIconTemplate</code> 自定义前缀,本例中通过前缀模板使用 <code>Svg</code> 图标",
4230+
"SearchesButtonTemplateTitle": "按钮模板",
4231+
"SearchesButtonTemplateIntro": "通过设置 <code>ButtonTemplate</code> 自定义组件显示的按钮",
4232+
"SearchesButtonTemplateDesc": "通过设置 <code>PrefixButtonTemplate</code> 自定义组件前置显示的按钮",
4233+
"SearchesButtonTemplateDesc2": "在自定义模板中可以通过关联参数调用 <code>Search</code> 组件内部的 <code>OnClear</code> <code>OnSearch</code> 方法",
4234+
"SearchesIsClearableTitle": "IsClearable",
4235+
"SearchesIsClearableIntro": "通过设置 <code>IsClearable=\"true\"</code> 显示清空小图标",
4236+
"SearchesIsClearable": "是否显示清空小按钮",
4237+
"SearchesClearIcon": "清空图标",
4238+
"SearchesPrefixButtonTemplate": "前置按钮模板",
4239+
"SearchesButtonTemplate": "按钮模板",
4240+
"SearchesIconTemplateTitle": "图标模板",
4241+
"SearchesIconTemplateIntro": "通过设置 <code>IconTemplate</code> 自定义组件显示的图标",
4242+
"SearchesIconTemplateDesc": "搜索组件上下文 <code>SearchContext&lt;string&gt;</code> 提供了组件内部的 <code>OnClear</code> <code>OnSearch</code> 方法"
42304243
},
42314244
"BootstrapBlazor.Server.Components.Samples.Titles": {
42324245
"Title": "Title 网站标题",

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.3.1-beta06</Version>
4+
<Version>9.3.1-beta07</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/Search/Search.razor

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,48 @@
44

55
<div @attributes="@AdditionalAttributes" class="@ClassString" id="@Id">
66
<div class="input-group">
7-
<input @attributes="AdditionalAttributes" id="@InputId" class="@ClassName" autocomplete="off" type="text"
8-
data-bs-toggle="@ToggleString" data-bs-placement="@PlacementString"
9-
data-bs-offset="@OffsetString" data-bs-custom-class="@CustomClassString"
10-
data-bb-auto-dropdown-focus="@ShowDropdownListOnFocusString"
11-
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString"
12-
data-bb-scroll-behavior="@ScrollIntoViewBehaviorString"
13-
data-bb-input="@UseInputString"
14-
value="@_displayText"
15-
placeholder="@PlaceHolder" disabled="@Disabled" @ref="FocusElement" />
7+
@if (PrefixButtonTemplate != null)
8+
{
9+
@PrefixButtonTemplate(_context)
10+
}
11+
<div class="form-control-group form-control">
12+
@if (ShowPrefixIcon)
13+
{
14+
<div class="search-prefix-icon">
15+
@if (PrefixIconTemplate != null)
16+
{
17+
@PrefixIconTemplate(_context)
18+
}
19+
else
20+
{
21+
<i class="@PrefixIcon"></i>
22+
}
23+
</div>
24+
}
25+
<input id="@InputId" class="search-input" autocomplete="off" type="text"
26+
data-bs-toggle="@ToggleString" data-bs-placement="@PlacementString"
27+
data-bs-offset="@OffsetString" data-bs-custom-class="@CustomClassString"
28+
data-bb-auto-dropdown-focus="@ShowDropdownListOnFocusString"
29+
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString"
30+
data-bb-scroll-behavior="@ScrollIntoViewBehaviorString"
31+
data-bb-input="@UseInputString"
32+
value="@_displayText"
33+
placeholder="@PlaceHolder" disabled="@Disabled" @ref="FocusElement" />
34+
@if (IsClearable)
35+
{
36+
<div class="search-icon search-clear-icon">
37+
<i class="@ClearIcon" @onclick="OnClearClick" aria-label="Clear"></i>
38+
</div>
39+
}
40+
@if (IconTemplate != null)
41+
{
42+
@IconTemplate(_context)
43+
}
44+
</div>
45+
@if (ButtonTemplate != null)
46+
{
47+
@ButtonTemplate(_context)
48+
}
1649
@if (ShowClearButton)
1750
{
1851
<Button Color="ClearButtonColor" Text="@ClearButtonText" Icon="@ClearButtonIcon" OnClick="OnClearClick" aria-label="Clear"></Button>
@@ -22,19 +55,6 @@
2255
<Button Color="SearchButtonColor" Text="@SearchButtonText" Icon="@ButtonIcon" OnClick="OnSearchClick" aria-label="Search"></Button>
2356
}
2457
</div>
25-
@if (ShowPrefixIcon)
26-
{
27-
<div class="search-prefix-icon">
28-
@if (PrefixIconTemplate != null)
29-
{
30-
@PrefixIconTemplate
31-
}
32-
else
33-
{
34-
<i class="@PrefixIcon"></i>
35-
}
36-
</div>
37-
}
3858
<ul class="dropdown-menu">
3959
@foreach (var item in _filterItems)
4060
{

0 commit comments

Comments
 (0)