Skip to content

Commit a7d575b

Browse files
authored
feat(MultiSelectGeneric): add MultiSelectGeneric component (#6103)
* refactor: 删除冗余样式 * feat: 增加多选泛型组件 * doc: 更新示例 * doc: 增加文档
1 parent 89d9077 commit a7d575b

File tree

8 files changed

+691
-244
lines changed

8 files changed

+691
-244
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,14 @@ private enum MultiSelectEnumFoo
351351
</div>
352352
</DemoBlock>
353353

354+
<DemoBlock Title="@Localizer["MultiSelectGenericTitle"]" Introduction="@Localizer["MultiSelectGenericIntro"]" Name="Generic">
355+
<div class="row">
356+
<div class="col-12">
357+
<MultiSelectGeneric Items="@FooItems" @bind-Value="_genericValue" ShowSearch="true" IsPopover="true"></MultiSelectGeneric>
358+
</div>
359+
</div>
360+
</DemoBlock>
361+
354362
<AttributeTable Items="@GetAttributes()"></AttributeTable>
355363

356364
<EventTable Items="@GetEvents()"></EventTable>

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ private enum MultiSelectEnumFoo
125125
private bool _showToolbar = true;
126126
private bool _showSearch = true;
127127

128+
[NotNull]
129+
private List<SelectedItem<Foo>>? FooItems { get; set; }
130+
131+
private List<Foo>? _genericValue = null;
132+
128133
private async Task<SelectedItem> OnEditCallback(string value)
129134
{
130135
await Task.Delay(100);
@@ -188,6 +193,7 @@ protected override void OnInitialized()
188193
Items8 = GenerateItems();
189194
TemplateItems = GenerateItems();
190195
EditableItems = GenerateItems();
196+
FooItems = [.. Foo.GenerateFoo(LocalizerFoo).Select(i => new SelectedItem<Foo>(i, i.Name!))];
191197

192198
// 初始化数据
193199
DataSource =

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3088,7 +3088,9 @@
30883088
"MultiSelectVirtualizeDescription": "Component virtual scrolling supports two ways of providing data through <code>Items</code> or <code>OnQueryAsync</code> callback methods",
30893089
"MultiSelectsAttribute_ShowSearch": "Whether to display the search box",
30903090
"MultiSelectsAttribute_IsVirtualize": "Wether to enable virtualize",
3091-
"MultiSelectsAttribute_DefaultVirtualizeItemText": "The text string corresponding to the first load value when virtual scrolling is turned on is separated by commas"
3091+
"MultiSelectsAttribute_DefaultVirtualizeItemText": "The text string corresponding to the first load value when virtual scrolling is turned on is separated by commas",
3092+
"MultiSelectGenericTitle": "Generic",
3093+
"MultiSelectGenericIntro": "Data source <code>Items</code> supports generics when using <code>SelectedItem&lt;TValue&gt;</code>"
30923094
},
30933095
"BootstrapBlazor.Server.Components.Samples.Radios": {
30943096
"RadiosTitle": "Radio",

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3088,7 +3088,9 @@
30883088
"MultiSelectVirtualizeDescription": "组件虚拟滚动支持两种形式通过 <code>Items</code> 或者 <code>OnQueryAsync</code> 回调方法提供数据",
30893089
"MultiSelectsAttribute_ShowSearch": "是否显示搜索框",
30903090
"MultiSelectsAttribute_IsVirtualize": "是否开启虚拟滚动",
3091-
"MultiSelectsAttribute_DefaultVirtualizeItemText": "开启虚拟滚动时首次加载 Value 对应的文本字符串用逗号分割"
3091+
"MultiSelectsAttribute_DefaultVirtualizeItemText": "开启虚拟滚动时首次加载 Value 对应的文本字符串用逗号分割",
3092+
"MultiSelectGenericTitle": "泛型支持",
3093+
"MultiSelectGenericIntro": "数据源 <code>Items</code> 使用 <code>SelectedItem&lt;TValue&gt;</code> 时即可支持泛型"
30923094
},
30933095
"BootstrapBlazor.Server.Components.Samples.Radios": {
30943096
"RadiosTitle": "Radio 单选框",
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
@namespace BootstrapBlazor.Components
2+
@using Microsoft.AspNetCore.Components.Web.Virtualization
3+
@typeparam TValue
4+
@inherits SelectBase<List<TValue>>
5+
@attribute [BootstrapModuleAutoLoader("Select/MultiSelect.razor.js", JSObjectReference = true)]
6+
7+
@if (IsShowLabel)
8+
{
9+
<BootstrapLabel required="@Required" for="@Id" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText"></BootstrapLabel>
10+
}
11+
<div @attributes="@AdditionalAttributes" class="@ClassString" id="@Id" data-bb-scroll-behavior="@ScrollIntoViewBehaviorString">
12+
<div class="@ToggleClassString" data-bs-toggle="@ToggleString" data-bs-placement="@PlacementString" data-bs-offset="@OffsetString" data-bs-auto-close="outside" data-bs-custom-class="@CustomClassString" tabindex="0">
13+
<div class="@PlaceHolderClassString">@PlaceHolder</div>
14+
<div class="multi-select-items">
15+
@if (DisplayTemplate != null)
16+
{
17+
@DisplayTemplate(SelectedItems)
18+
}
19+
else
20+
{
21+
foreach (var item in SelectedItems)
22+
{
23+
if (ShowCloseButton)
24+
{
25+
<div class="multi-select-item-group">
26+
<DynamicElement TagName="span" class="multi-select-close"
27+
TriggerClick="@(!IsPopover)" OnClick="() => ToggleRow(item)">
28+
<i class="@CloseButtonIcon"></i>
29+
</DynamicElement>
30+
<span class="multi-select-item">@item.Text</span>
31+
</div>
32+
}
33+
else
34+
{
35+
<span class="multi-select-item">@item.Text</span>
36+
}
37+
}
38+
}
39+
</div>
40+
@if (!IsSingleLine)
41+
{
42+
<span class="@AppendClassString"><i class="@DropdownIcon"></i></span>
43+
}
44+
</div>
45+
@if (GetClearable())
46+
{
47+
<span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
48+
}
49+
<div class="@DropdownMenuClassString">
50+
@if (ShowSearch)
51+
{
52+
<div class="dropdown-menu-search">
53+
<input type="text" class="search-text form-control" autocomplete="off" value="@SearchText" aria-label="search" />
54+
<i class="@SearchIconString"></i>
55+
<i class="@SearchLoadingIconString"></i>
56+
</div>
57+
}
58+
@if (ShowToolbar)
59+
{
60+
<div class="toolbar">
61+
@if (ShowDefaultButtons)
62+
{
63+
<DynamicElement TagName="button" type="button" class="btn" OnClick="SelectAll">@SelectAllText</DynamicElement>
64+
<DynamicElement TagName="button" type="button" class="btn" OnClick="InvertSelect">@ReverseSelectText</DynamicElement>
65+
<DynamicElement TagName="button" type="button" class="btn" OnClick="Clear">@ClearText</DynamicElement>
66+
}
67+
@ButtonTemplate
68+
</div>
69+
}
70+
@if (IsVirtualize)
71+
{
72+
<div class="dropdown-menu-body dropdown-virtual">
73+
@if (OnQueryAsync == null)
74+
{
75+
<Virtualize ItemSize="RowHeight" OverscanCount="OverscanCount" Items="@GetVirtualItems()" ChildContent="RenderRow">
76+
</Virtualize>
77+
}
78+
else
79+
{
80+
<Virtualize ItemSize="RowHeight" OverscanCount="OverscanCount" ItemsProvider="LoadItems"
81+
Placeholder="RenderPlaceHolderRow" ItemContent="RenderRow" @ref="_virtualizeElement">
82+
</Virtualize>
83+
}
84+
</div>
85+
}
86+
else if (Rows.Count == 0)
87+
{
88+
<div class="dropdown-item">@NoSearchDataText</div>
89+
}
90+
else
91+
{
92+
<div class="dropdown-menu-body">
93+
@foreach (var itemGroup in Rows.GroupBy(i => i.GroupName))
94+
{
95+
if (!string.IsNullOrEmpty(itemGroup.Key))
96+
{
97+
if (GroupItemTemplate != null)
98+
{
99+
@GroupItemTemplate(itemGroup.Key)
100+
}
101+
else
102+
{
103+
<Divider Text="@itemGroup.Key" />
104+
}
105+
}
106+
@foreach (var item in itemGroup)
107+
{
108+
@RenderRow(item)
109+
}
110+
111+
if (!string.IsNullOrEmpty(itemGroup.Key))
112+
{
113+
if (GroupItemTemplate != null)
114+
{
115+
@GroupItemTemplate(itemGroup.Key)
116+
}
117+
else
118+
{
119+
<Divider Text="@itemGroup.Key" />
120+
}
121+
}
122+
}
123+
</div>
124+
}
125+
</div>
126+
</div>
127+
128+
@code {
129+
RenderFragment<SelectedItem<TValue>> RenderRow => item =>
130+
@<DynamicElement OnClick="() => ToggleRow(item)" TriggerClick="@CheckCanTrigger(item)" class="@GetItemClassString(item)">
131+
<div class="multi-select-item">
132+
<div class="form-check">
133+
<input class="form-check-input" type="checkbox" disabled="@CheckCanSelect(item)" checked="@GetCheckedString(item)" />
134+
</div>
135+
@if (ItemTemplate != null)
136+
{
137+
@ItemTemplate(item)
138+
}
139+
else if (IsMarkupString)
140+
{
141+
@((MarkupString)item.Text)
142+
}
143+
else
144+
{
145+
<span>@item.Text</span>
146+
}
147+
</div>
148+
</DynamicElement>;
149+
150+
RenderFragment<PlaceholderContext> RenderPlaceHolderRow => context =>
151+
@<div class="dropdown-item">
152+
<div class="is-ph"></div>
153+
</div>;
154+
}

0 commit comments

Comments
 (0)