Skip to content

Commit 72398af

Browse files
A5196060ArgoZhangj4587698tiansfather
authored
feat(RadioListGeneric): implement RadioListGeneric component (#4908)
* feat: 更新逻辑 * refactor: 重构组件 * doc: 增加示例 * doc: 更新 CheckboxList 示例 * doc: 更新 RadioList 示例 * feat: 增加 Value 逻辑 * test: 增加单元测试 * chore: bump version 9.1.6 --------- Co-authored-by: Argo Zhang <argo@live.ca> Co-authored-by: A5196060 <5196060@qq.com> Co-authored-by: j4587698 <zhuce@jvxiang.com> Co-authored-by: Mr Lu <ghi.ghi@163.com>
1 parent 3c4cc8e commit 72398af

File tree

9 files changed

+414
-62
lines changed

9 files changed

+414
-62
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
</ValidateForm>
121121
</DemoBlock>
122122

123-
<DemoBlock Title="@Localizer["GenericTitle"]" Introduction="@Localizer["GenericIntro"]" Name="Generic">
123+
<DemoBlock Title="@Localizer["CheckboxListGenericTitle"]" Introduction="@Localizer["CheckboxListGenericIntro"]" Name="Generic">
124124
<CheckboxListGeneric @bind-Value="@_selectedFoos" Items="@GenericItems" />
125125
<section ignore>
126126
@if (_selectedFoos != null)

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

+7
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@
125125
</ValidateForm>
126126
</DemoBlock>
127127

128+
<DemoBlock Title="@Localizer["RadioListGenericTitle"]" Introduction="@Localizer["RadioListGenericIntro"]" Name="Generic">
129+
<RadioListGeneric @bind-Value="@_selectedFoo" Items="@GenericItems" />
130+
<section ignore>
131+
<div>@_selectedFoo.Name</div>
132+
</section>
133+
</DemoBlock>
134+
128135
<AttributeTable Items="@GetAttributes()" />
129136

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

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

+18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
55

6+
using DocumentFormat.OpenXml.Office2010.Excel;
7+
using DocumentFormat.OpenXml.Wordprocessing;
8+
69
namespace BootstrapBlazor.Server.Components.Samples;
710

811
/// <summary>
@@ -52,6 +55,12 @@ private Task OnSelectedChanged(IEnumerable<SelectedItem> values, string val)
5255
return Task.CompletedTask;
5356
}
5457

58+
[NotNull]
59+
private IEnumerable<SelectedItem<Foo>>? GenericItems { get; set; }
60+
61+
[NotNull]
62+
private Foo? _selectedFoo = default;
63+
5564
/// <summary>
5665
/// OnInitialized 方法
5766
/// </summary>
@@ -85,6 +94,15 @@ protected override void OnInitialized()
8594

8695
Model = Foo.Generate(LocalizerFoo);
8796
FooItems = Foo.GetCompleteItems(LocalizerFoo);
97+
98+
GenericItems = new List<SelectedItem<Foo>>()
99+
{
100+
new() { Text = Localizer["item1"], Value = new Foo() { Id = 1, Name = LocalizerFoo["Foo.Name", "001"] } },
101+
new() { Text = Localizer["item2"], Value = new Foo() { Id = 2, Name = LocalizerFoo["Foo.Name", "002"] } },
102+
new() { Text = Localizer["item3"], Value = new Foo() { Id = 3, Name = LocalizerFoo["Foo.Name", "003"] } },
103+
};
104+
105+
_selectedFoo = new Foo() { Id = 1, Name = LocalizerFoo["Foo.Name", "001"] };
88106
}
89107

90108
private Task OnItemChanged(IEnumerable<SelectedItem> values, SelectedItem val)

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -2376,7 +2376,9 @@
23762376
"StatusText2": "Not selected",
23772377
"StatusText3": "Indeterminate",
23782378
"Checkbox2Text": "Two-way binding",
2379-
"Checkbox3Text": "Handwritten labels"
2379+
"Checkbox3Text": "Handwritten labels",
2380+
"RadioListGenericTitle": "Generic List",
2381+
"RadioListGenericIntro": "Enable generic support through <code>SelectedItem&lt;TValue&gt;</code>"
23802382
},
23812383
"BootstrapBlazor.Server.Components.Samples.CheckboxLists": {
23822384
"Title": "CheckboxList",
@@ -3024,7 +3026,9 @@
30243026
"RadiosItem1": "Option one",
30253027
"RadiosItem2": "Option two",
30263028
"RadiosAdd1": "Beijing",
3027-
"RadiosAdd2": "Shanghai"
3029+
"RadiosAdd2": "Shanghai",
3030+
"RadioListGenericTitle": "Generic List",
3031+
"RadioListGenericIntro": "Enable generic support through <code>SelectedItem&lt;TValue&gt;</code>"
30283032
},
30293033
"BootstrapBlazor.Server.Components.Samples.Rates": {
30303034
"RatesTitle": "Rate",

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -2426,7 +2426,9 @@
24262426
"MaxSelectedCountIntro": "通过设置 <code>MaxSelectedCount</code> 属性控制最大可选数量,通过 <code>OnMaxSelectedCountExceed</code> 回调处理逻辑",
24272427
"MaxSelectedCountDesc": "选中节点超过 2 个时,弹出 <code>Toast</code> 提示栏",
24282428
"OnMaxSelectedCountExceedTitle": "可选最大数量提示",
2429-
"OnMaxSelectedCountExceedContent": "最多只能选择 {0} 项"
2429+
"OnMaxSelectedCountExceedContent": "最多只能选择 {0} 项",
2430+
"CheckboxListGenericTitle": "泛型支持",
2431+
"CheckboxListGenericIntro": "通过 <code>SelectedItem&lt;TValue&gt;</code> 开启泛型支持"
24302432
},
24312433
"BootstrapBlazor.Server.Components.Samples.ColorPickers": {
24322434
"Title": "ColorPicker 颜色拾取器",
@@ -3024,7 +3026,9 @@
30243026
"RadiosItem1": "选项一",
30253027
"RadiosItem2": "选项二",
30263028
"RadiosAdd1": "北京",
3027-
"RadiosAdd2": "上海"
3029+
"RadiosAdd2": "上海",
3030+
"RadioListGenericTitle": "泛型支持",
3031+
"RadioListGenericIntro": "通过 <code>SelectedItem&lt;TValue&gt;</code> 开启泛型支持"
30283032
},
30293033
"BootstrapBlazor.Server.Components.Samples.Rates": {
30303034
"RatesTitle": "Rate 评分",

src/BootstrapBlazor/BootstrapBlazor.csproj

+1-1
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.1.6-beta03</Version>
4+
<Version>9.1.6</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
{
77
<BootstrapLabel required="@Required" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText" />
88
}
9-
@* @if (IsButton)
9+
@if (IsButton)
1010
{
1111
<div @attributes="@AdditionalAttributes" class="radio-list-group">
1212
<div class="@ButtonClassString" role="group">
@@ -29,4 +29,3 @@ else
2929
}
3030
</div>
3131
}
32-
*@

src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor.cs

+134-54
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ namespace BootstrapBlazor.Components;
88
/// <summary>
99
/// 单选框组合组件
1010
/// </summary>
11-
[ExcludeFromCodeCoverage]
12-
public partial class RadioListGeneric<TValue>
11+
public partial class RadioListGeneric<TValue> : IModelEqualityComparer<TValue>
1312
{
1413
/// <summary>
1514
/// 获得/设置 值为可为空枚举类型时是否自动添加空值 默认 false 自定义空值显示文本请参考 <see cref="NullItemText"/>
@@ -30,56 +29,137 @@ public partial class RadioListGeneric<TValue>
3029
[Parameter]
3130
public RenderFragment<SelectedItem<TValue>>? ItemTemplate { get; set; }
3231

33-
//private string? GroupName => Id;
34-
35-
//private string? ClassString => CssBuilder.Default("radio-list form-control")
36-
// .AddClass("no-border", !ShowBorder && ValidCss != "is-invalid")
37-
// .AddClass("is-vertical", IsVertical)
38-
// .AddClass(CssClass).AddClass(ValidCss)
39-
// .Build();
40-
41-
//private string? ButtonClassString => CssBuilder.Default("radio-list btn-group")
42-
// .AddClass("disabled", IsDisabled)
43-
// .AddClass("btn-group-vertical", IsVertical)
44-
// .AddClassFromAttributes(AdditionalAttributes)
45-
// .Build();
46-
47-
//private string? GetButtonItemClassString(SelectedItem item) => CssBuilder.Default("btn")
48-
// .AddClass($"active bg-{Color.ToDescriptionString()}", CurrentValueAsString == item.Value)
49-
// .Build();
50-
51-
///// <summary>
52-
///// <inheritdoc/>
53-
///// </summary>
54-
//protected override void OnParametersSet()
55-
//{
56-
// var t = NullableUnderlyingType ?? typeof(TValue);
57-
// if (t.IsEnum && Items == null)
58-
// {
59-
// Items = t.ToSelectList<TValue>((NullableUnderlyingType != null && IsAutoAddNullItem) ? new SelectedItem<TValue>(default, NullItemText) : null);
60-
// }
61-
62-
// base.OnParametersSet();
63-
//}
64-
65-
///// <summary>
66-
///// 点击选择框方法
67-
///// </summary>
68-
//private async Task OnClick(SelectedItem<TValue> item)
69-
//{
70-
// if (!IsDisabled)
71-
// {
72-
// if (OnSelectedChanged != null)
73-
// {
74-
// await OnSelectedChanged(Items, Value);
75-
// }
76-
// StateHasChanged();
77-
// }
78-
//}
79-
80-
//private CheckboxState CheckState(SelectedItem<TValue> item) => this.Equals<TValue>(Value, item.Value) ? CheckboxState.Checked : CheckboxState.UnChecked;
81-
82-
//private RenderFragment? GetChildContent(SelectedItem<TValue> item) => ItemTemplate == null
83-
// ? null
84-
// : ItemTemplate(item);
32+
/// <summary>
33+
/// 获得/设置 是否为按钮样式 默认 false
34+
/// </summary>
35+
[Parameter]
36+
public bool IsButton { get; set; }
37+
38+
/// <summary>
39+
/// 获得/设置 是否显示边框 默认为 true
40+
/// </summary>
41+
[Parameter]
42+
public bool ShowBorder { get; set; } = true;
43+
44+
/// <summary>
45+
/// 获得/设置 是否为竖向排列 默认为 false
46+
/// </summary>
47+
[Parameter]
48+
public bool IsVertical { get; set; }
49+
50+
/// <summary>
51+
/// 获得/设置 按钮颜色 默认为 None 未设置
52+
/// </summary>
53+
[Parameter]
54+
public Color Color { get; set; }
55+
56+
/// <summary>
57+
/// 获得/设置 数据源
58+
/// </summary>
59+
[Parameter]
60+
[NotNull]
61+
public IEnumerable<SelectedItem<TValue>>? Items { get; set; }
62+
63+
/// <summary>
64+
/// 获得/设置 SelectedItemChanged 方法
65+
/// </summary>
66+
[Parameter]
67+
public Func<TValue, Task>? OnSelectedChanged { get; set; }
68+
69+
/// <summary>
70+
/// 获得/设置 数据主键标识标签 默认为 <see cref="KeyAttribute"/><code><br /></code>用于判断数据主键标签,如果模型未设置主键时可使用 <see cref="ModelEqualityComparer"/> 参数自定义判断 <code><br /></code>数据模型支持联合主键
71+
/// </summary>
72+
[Parameter]
73+
[NotNull]
74+
public Type? CustomKeyAttribute { get; set; } = typeof(KeyAttribute);
75+
76+
/// <summary>
77+
/// 获得/设置 比较数据是否相同回调方法 默认为 null
78+
/// <para>提供此回调方法时忽略 <see cref="CustomKeyAttribute"/> 属性</para>
79+
/// </summary>
80+
[Parameter]
81+
public Func<TValue, TValue, bool>? ModelEqualityComparer { get; set; }
82+
83+
/// <summary>
84+
/// 获得 当前选项是否被禁用
85+
/// </summary>
86+
/// <param name="item"></param>
87+
/// <returns></returns>
88+
private bool GetDisabledState(SelectedItem<TValue> item) => IsDisabled || item.IsDisabled;
89+
90+
private string? GroupName => Id;
91+
92+
private string? ClassString => CssBuilder.Default("radio-list form-control")
93+
.AddClass("no-border", !ShowBorder && ValidCss != "is-invalid")
94+
.AddClass("is-vertical", IsVertical)
95+
.AddClass(CssClass).AddClass(ValidCss)
96+
.Build();
97+
98+
private string? ButtonClassString => CssBuilder.Default("radio-list btn-group")
99+
.AddClass("disabled", IsDisabled)
100+
.AddClass("btn-group-vertical", IsVertical)
101+
.AddClassFromAttributes(AdditionalAttributes)
102+
.Build();
103+
104+
private string? GetButtonItemClassString(SelectedItem<TValue> item) => CssBuilder.Default("btn")
105+
.AddClass($"active bg-{Color.ToDescriptionString()}", Equals(Value, item.Value))
106+
.Build();
107+
108+
/// <summary>
109+
/// <inheritdoc/>
110+
/// </summary>
111+
protected override void OnParametersSet()
112+
{
113+
base.OnParametersSet();
114+
115+
if (IsButton && Color == Color.None)
116+
{
117+
Color = Color.Primary;
118+
}
119+
120+
var t = NullableUnderlyingType ?? typeof(TValue);
121+
if (t.IsEnum && Items == null)
122+
{
123+
Items = t.ToSelectList<TValue>((NullableUnderlyingType != null && IsAutoAddNullItem) ? new SelectedItem<TValue>(default, NullItemText) : null);
124+
}
125+
126+
Items ??= [];
127+
128+
// set item active
129+
if (Value != null)
130+
{
131+
var item = Items.FirstOrDefault(i => Equals(Value, i.Value));
132+
if (item == null)
133+
{
134+
Value = default;
135+
}
136+
}
137+
}
138+
139+
/// <summary>
140+
/// 点击选择框方法
141+
/// </summary>
142+
private async Task OnClick(SelectedItem<TValue?> item)
143+
{
144+
if (!IsDisabled)
145+
{
146+
CurrentValue = item.Value;
147+
if (OnSelectedChanged != null)
148+
{
149+
await OnSelectedChanged(Value);
150+
}
151+
StateHasChanged();
152+
}
153+
}
154+
155+
private CheckboxState CheckState(SelectedItem<TValue> item) => this.Equals<TValue>(Value, item.Value) ? CheckboxState.Checked : CheckboxState.UnChecked;
156+
157+
private RenderFragment? GetChildContent(SelectedItem<TValue> item) => ItemTemplate == null
158+
? null
159+
: ItemTemplate(item);
160+
161+
/// <summary>
162+
/// <inheritdoc/>
163+
/// </summary>
164+
public bool Equals(TValue? x, TValue? y) => this.Equals<TValue>(x, y);
85165
}

0 commit comments

Comments
 (0)