Skip to content

Commit 4f9e787

Browse files
authored
feat(AutoFill): add IsVirtulize parameter (#5664)
* refactor: 重构代码消除警告信息 * doc: 文档更改为英文注释 * feat: 增加 IsVirtualize 参数 * style: 精简样式 * refactor: 增加阴影效果 * refactor: 移除 IsFixedSearch 参数 * test: 更新单元测试 * refactor: 移除 IsFixedSearch 参数 * test: 更新单元测试 * doc: 更新默认值 * refactor: 移除 IsFixedSearch 参数 * refactor: 标注已弃用 * test: 更新单元测试 * doc: 更新示例 * doc: 更新文档注释 * doc: 增加示例 * feat: 增加虚拟化功能 * refactor: 增加 CloseButtonIcon 图标参数 * refactor: 更新图标 * test: 更新单元测试 * refactor: 移除不使用的命名空间 * refactor: 微调搜索图标位置 * refactor: 增加虚拟化逻辑 * doc: 增加示例文档 * doc: 增加虚拟化参数文档 * doc: 增加参数注释文档 * feat: 增加 IsClearable 支持 * refactor: 增加样式 * doc: 更新示例 * test: 更新单元测试 * chore: bump version 9.5.0-beta06 * test: 更新单元测试 * doc: 移除 IsFixedSearch 相关文档 * test: 更新单元测试 * chore: bump version 9.5.0-beta07 * revert: 移除样式
1 parent c2a7708 commit 4f9e787

File tree

11 files changed

+488
-62
lines changed

11 files changed

+488
-62
lines changed

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
<section ignore>
1010
@((MarkupString)@Localizer["NormalDesc"].Value)
1111
</section>
12-
<AutoFill @bind-Value="Model1" Items="Items1" IsLikeMatch="true" OnGetDisplayText="OnGetDisplayText" class="mb-3" IsSelectAllTextOnFocus="true">
12+
<AutoFill @bind-Value="Model1" Items="Items1" class="mb-3"
13+
IsLikeMatch="true" OnGetDisplayText="OnGetDisplayText" IsSelectAllTextOnFocus="true">
1314
<ItemTemplate>
1415
<div class="d-flex">
1516
<div>
@@ -71,4 +72,72 @@
7172
</section>
7273
</DemoBlock>
7374

75+
<DemoBlock Title="@Localizer["IsVirtualizeTitle"]" Introduction="@Localizer["IsVirtualizeIntro"]" Name="IsVirtualize">
76+
<section ignore>
77+
<p>@((MarkupString)Localizer["IsVirtualizeDescription"].Value)</p>
78+
<div class="row g-3">
79+
<div class="col-12 col-sm-6">
80+
<BootstrapInputGroup>
81+
<BootstrapInputGroupLabel DisplayText="IsClearable" />
82+
<Checkbox @bind-Value="@_isClearable" />
83+
</BootstrapInputGroup>
84+
</div>
85+
</div>
86+
</section>
87+
88+
<p class="code-label">1. 使用 OnQueryAsync 作为数据源</p>
89+
<div class="row mb-3">
90+
<div class="col-12">
91+
<AutoFill @bind-Value="Model4" OnQueryAsync="OnQueryAsync" OnGetDisplayText="OnGetDisplayText" class="mb-3"
92+
IsSelectAllTextOnFocus="true" OnCustomFilter="OnCustomVirtulizeFilter"
93+
IsVirtualize="true" RowHeight="58f" IsClearable="_isClearable">
94+
<ItemTemplate>
95+
<div class="d-flex">
96+
<div>
97+
<img src="@WebsiteOption.CurrentValue.GetAvatarUrl(context.Id)" class="bb-avatar" />
98+
</div>
99+
<div class="ps-2">
100+
<div>@context.Name</div>
101+
<div class="bb-sub">@Foo.GetTitle(context.Id)</div>
102+
</div>
103+
</div>
104+
</ItemTemplate>
105+
</AutoFill>
106+
<section ignore>
107+
@if (Model4 != null)
108+
{
109+
<EditorForm Model="@Model4" RowType="RowType.Inline" ItemsPerRow="2" />
110+
}
111+
</section>
112+
</div>
113+
</div>
114+
115+
<p class="code-label">2. 使用 Items 作为数据源</p>
116+
<div class="row">
117+
<div class="col-12">
118+
<AutoFill @bind-Value="Model4" Items="Items4" OnGetDisplayText="OnGetDisplayText" class="mb-3"
119+
IsSelectAllTextOnFocus="true" OnCustomFilter="OnCustomVirtulizeFilter"
120+
IsVirtualize="true" RowHeight="58f" IsClearable="_isClearable">
121+
<ItemTemplate>
122+
<div class="d-flex">
123+
<div>
124+
<img src="@WebsiteOption.CurrentValue.GetAvatarUrl(context.Id)" class="bb-avatar" />
125+
</div>
126+
<div class="ps-2">
127+
<div>@context.Name</div>
128+
<div class="bb-sub">@Foo.GetTitle(context.Id)</div>
129+
</div>
130+
</div>
131+
</ItemTemplate>
132+
</AutoFill>
133+
<section ignore>
134+
@if (Model4 != null)
135+
{
136+
<EditorForm Model="@Model4" RowType="RowType.Inline" ItemsPerRow="2" />
137+
}
138+
</section>
139+
</div>
140+
</div>
141+
</DemoBlock>
142+
74143
<AttributeTable Items="@GetAttributes()" />

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@ partial class AutoFills
1515

1616
[NotNull]
1717
private Foo Model2 { get; set; } = new();
18+
1819
[NotNull]
1920
private Foo Model3 { get; set; } = new();
2021

22+
[NotNull]
23+
private Foo Model4 { get; set; } = new();
24+
2125
private static string? OnGetDisplayText(Foo? foo) => foo?.Name;
2226

2327
[NotNull]
@@ -29,10 +33,15 @@ partial class AutoFills
2933
[NotNull]
3034
private IEnumerable<Foo>? Items3 { get; set; }
3135

36+
[NotNull]
37+
private IEnumerable<Foo>? Items4 { get; set; }
38+
3239
[Inject]
3340
[NotNull]
3441
private IStringLocalizer<Foo>? LocalizerFoo { get; set; }
3542

43+
private bool _isClearable = true;
44+
3645
/// <inheritdoc/>
3746
protected override void OnInitialized()
3847
{
@@ -46,6 +55,9 @@ protected override void OnInitialized()
4655

4756
Items3 = Foo.GenerateFoo(LocalizerFoo);
4857
Model3 = Items3.First();
58+
59+
Items4 = Foo.GenerateFoo(LocalizerFoo);
60+
Model4 = Items3.First();
4961
}
5062

5163
private Task<IEnumerable<Foo>> OnCustomFilter(string searchText)
@@ -54,6 +66,27 @@ private Task<IEnumerable<Foo>> OnCustomFilter(string searchText)
5466
return Task.FromResult(items);
5567
}
5668

69+
private Task<IEnumerable<Foo>> OnCustomVirtulizeFilter(string searchText)
70+
{
71+
var items = string.IsNullOrEmpty(searchText) ? Items4 : Items4.Where(i => i.Name!.Contains(searchText));
72+
return Task.FromResult(items);
73+
}
74+
75+
private async Task<QueryData<Foo>> OnQueryAsync(VirtualizeQueryOption option)
76+
{
77+
await Task.Delay(200);
78+
var items = Foo.GenerateFoo(LocalizerFoo);
79+
if (!string.IsNullOrEmpty(option.SearchText))
80+
{
81+
items = [.. items.Where(i => i.Name!.Contains(option.SearchText, StringComparison.OrdinalIgnoreCase))];
82+
}
83+
return new QueryData<Foo>
84+
{
85+
Items = items.Skip(option.StartIndex).Take(option.Count),
86+
TotalCount = items.Count
87+
};
88+
}
89+
5790
/// <summary>
5891
/// Get property method
5992
/// </summary>
@@ -163,6 +196,14 @@ private AttributeItem[] GetAttributes() =>
163196
Type = "bool",
164197
ValueList = "true/false",
165198
DefaultValue = "false"
199+
},
200+
new()
201+
{
202+
Name = nameof(AutoFill<string>.IsVirtualize),
203+
Description = Localizer["AttrIsVirtualize"],
204+
Type = "bool",
205+
ValueList = "true/false",
206+
DefaultValue = "false"
166207
}
167208
];
168209
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,7 +2151,11 @@
21512151
"Att10": "Whether to expand the dropdown candidate menu when it gains focus",
21522152
"Att11": "Candidate template",
21532153
"Att12": "Whether to skip Enter key handling",
2154-
"Att13": "Whether to skip Esc key processing"
2154+
"Att13": "Whether to skip Esc key processing",
2155+
"IsVirtualizeTitle": "Virtualize",
2156+
"IsVirtualizeIntro": "Set <code>IsVirtualize</code> to <b>true</b> enable virtual scroll for large data",
2157+
"IsVirtualizeDescription": "Component virtual scrolling supports two ways of providing data through <code>Items</code> or <code>OnQueryAsync</code> callback methods",
2158+
"AttrIsVirtualize": "Wether to enable virtualize"
21552159
},
21562160
"BootstrapBlazor.Server.Components.Samples.AutoCompletes": {
21572161
"Title": "AutoComplete",
@@ -3026,7 +3030,8 @@
30263030
"MultiSelectVirtualizeTitle": "Virtualize",
30273031
"MultiSelectVirtualizeIntro": "Set <code>IsVirtualize</code> to <b>true</b> enable virtual scroll for large data",
30283032
"MultiSelectVirtualizeDescription": "Component virtual scrolling supports two ways of providing data through <code>Items</code> or <code>OnQueryAsync</code> callback methods",
3029-
"MultiSelectsAttribute_ShowSearch": "Whether to display the search box"
3033+
"MultiSelectsAttribute_ShowSearch": "Whether to display the search box",
3034+
"MultiSelectsAttribute_IsVirtualize": "Wether to enable virtualize"
30303035
},
30313036
"BootstrapBlazor.Server.Components.Samples.Radios": {
30323037
"RadiosTitle": "Radio",

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,7 +2151,11 @@
21512151
"Att10": "获得焦点时是否展开下拉候选菜单",
21522152
"Att11": "候选项模板",
21532153
"Att12": "是否跳过 Enter 按键处理",
2154-
"Att13": "是否跳过 Esc 按键处理"
2154+
"Att13": "是否跳过 Esc 按键处理",
2155+
"IsVirtualizeTitle": "虚拟滚动",
2156+
"IsVirtualizeIntro": "通过设置 <code>IsVirtualize</code> 参数开启组件虚拟功能特性",
2157+
"IsVirtualizeDescription": "组件虚拟滚动支持两种形式通过 <code>Items</code> 或者 <code>OnQueryAsync</code> 回调方法提供数据",
2158+
"AttrIsVirtualize": "是否开启虚拟滚动"
21552159
},
21562160
"BootstrapBlazor.Server.Components.Samples.AutoCompletes": {
21572161
"Title": "AutoComplete 自动完成",
@@ -3026,7 +3030,8 @@
30263030
"MultiSelectVirtualizeTitle": "虚拟滚动",
30273031
"MultiSelectVirtualizeIntro": "通过设置 <code>IsVirtualize</code> 参数开启组件虚拟功能特性",
30283032
"MultiSelectVirtualizeDescription": "组件虚拟滚动支持两种形式通过 <code>Items</code> 或者 <code>OnQueryAsync</code> 回调方法提供数据",
3029-
"MultiSelectsAttribute_ShowSearch": "是否显示搜索框"
3033+
"MultiSelectsAttribute_ShowSearch": "是否显示搜索框",
3034+
"MultiSelectsAttribute_IsVirtualize": "是否开启虚拟滚动"
30303035
},
30313036
"BootstrapBlazor.Server.Components.Samples.Radios": {
30323037
"RadiosTitle": "Radio 单选框",

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.5.0-beta05</Version>
4+
<Version>9.5.0-beta07</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.scss

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
.auto-complete {
22
--bb-ac-padding-right: #{$bb-ac-padding-right};
3-
--bb-ac-menu-top: #{$bb-ac-menu-top};
4-
--bb-ac-menu-left: #{$bb-ac-menu-left};
5-
--bb-ac-menu-right: #{$bb-ac-menu-right};
6-
--bb-ac-menu-shadow: #{$bb-ac-menu-shadow};
7-
--bb-ac-dropdown-max-height: var(--bb-dropdown-max-height);
83
--bb-select-append-width: #{$bb-select-append-width};
94
--bb-select-append-color: #{$bb-select-append-color};
105
position: relative;
@@ -15,11 +10,7 @@
1510
}
1611

1712
.dropdown-menu {
18-
top: var(--bb-ac-menu-top);
19-
left: var(--bb-ac-menu-left);
20-
right: var(--bb-ac-menu-right);
21-
box-shadow: var(--bb-ac-menu-shadow);
22-
max-height: var(--bb-ac-dropdown-max-height);
13+
width: 100%;
2314
}
2415

2516
.ac-loading {

src/BootstrapBlazor/Components/AutoFill/AutoFill.razor

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@namespace BootstrapBlazor.Components
2+
@using Microsoft.AspNetCore.Components.Web.Virtualization
23
@typeparam TValue
34
@inherits PopoverCompleteBase<TValue>
45

@@ -17,23 +18,58 @@
1718
placeholder="@PlaceHolder" disabled="@Disabled" @ref="FocusElement" />
1819
<span class="form-select-append"><i class="@Icon"></i></span>
1920
<span class="form-select-append ac-loading"><i class="@LoadingIcon"></i></span>
21+
@if (GetClearable())
22+
{
23+
<span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
24+
}
2025
<ul class="dropdown-menu">
21-
@foreach (var item in Rows)
26+
@if (IsVirtualize)
2227
{
23-
<li @key="@item" class="dropdown-item" @onclick="() => OnClickItem(item)">
24-
@if (ItemTemplate != null)
28+
<div class="dropdown-menu-body dropdown-virtual">
29+
@if (OnQueryAsync == null)
2530
{
26-
@ItemTemplate(item)
31+
<Virtualize ItemSize="RowHeight" OverscanCount="OverscanCount" Items="@Rows" ChildContent="RenderRow">
32+
</Virtualize>
2733
}
2834
else
2935
{
30-
<div>@GetDisplayText(item)</div>
36+
<Virtualize ItemSize="RowHeight" OverscanCount="OverscanCount" ItemsProvider="LoadItems"
37+
Placeholder="RenderPlaceHolderRow" ItemContent="RenderRow" @ref="_virtualizeElement">
38+
</Virtualize>
3139
}
32-
</li>
40+
</div>
3341
}
34-
@if (ShowNoDataTip && Rows.Count == 0)
42+
else if (ShowNoDataTip && Rows.Count == 0)
3543
{
3644
<li class="dropdown-item">@NoDataTip</li>
3745
}
46+
else
47+
{
48+
<div class="dropdown-menu-body">
49+
@foreach (var item in Rows)
50+
{
51+
@RenderRow(item)
52+
}
53+
</div>
54+
}
3855
</ul>
3956
</div>
57+
58+
@code {
59+
RenderFragment<TValue> RenderRow => item =>
60+
@<li @key="@item" class="dropdown-item" @onclick="() => OnClickItem(item)">
61+
@if (ItemTemplate != null)
62+
{
63+
@ItemTemplate(item)
64+
}
65+
else
66+
{
67+
<div>@GetDisplayText(item)</div>
68+
}
69+
</li>;
70+
71+
RenderFragment<PlaceholderContext> RenderPlaceHolderRow => context =>
72+
@<div class="dropdown-item">
73+
<div class="is-ph"></div>
74+
</div>;
75+
}

0 commit comments

Comments
 (0)