Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
</ValidateForm>
</DemoBlock>

<DemoBlock Title="@Localizer["GenericTitle"]" Introduction="@Localizer["GenericIntro"]" Name="Generic">
<DemoBlock Title="@Localizer["CheckboxListGenericTitle"]" Introduction="@Localizer["CheckboxListGenericIntro"]" Name="Generic">
<CheckboxListGeneric @bind-Value="@_selectedFoos" Items="@GenericItems" />
<section ignore>
@if (_selectedFoos != null)
Expand Down
7 changes: 7 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/Radios.razor
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@
</ValidateForm>
</DemoBlock>

<DemoBlock Title="@Localizer["RadioListGenericTitle"]" Introduction="@Localizer["RadioListGenericIntro"]" Name="Generic">
<RadioListGeneric @bind-Value="@_selectedFoo" Items="@GenericItems" />
<section ignore>
<div>@_selectedFoo.Name</div>
</section>
</DemoBlock>

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

<EventTable Items="@GetEvents()" />
Expand Down
18 changes: 18 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/Radios.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Wordprocessing;

namespace BootstrapBlazor.Server.Components.Samples;

/// <summary>
Expand Down Expand Up @@ -52,6 +55,12 @@ private Task OnSelectedChanged(IEnumerable<SelectedItem> values, string val)
return Task.CompletedTask;
}

[NotNull]
private IEnumerable<SelectedItem<Foo>>? GenericItems { get; set; }

[NotNull]
private Foo? _selectedFoo = default;

/// <summary>
/// OnInitialized 方法
/// </summary>
Expand Down Expand Up @@ -85,6 +94,15 @@ protected override void OnInitialized()

Model = Foo.Generate(LocalizerFoo);
FooItems = Foo.GetCompleteItems(LocalizerFoo);

GenericItems = new List<SelectedItem<Foo>>()
{
new() { Text = Localizer["item1"], Value = new Foo() { Id = 1, Name = LocalizerFoo["Foo.Name", "001"] } },
new() { Text = Localizer["item2"], Value = new Foo() { Id = 2, Name = LocalizerFoo["Foo.Name", "002"] } },
new() { Text = Localizer["item3"], Value = new Foo() { Id = 3, Name = LocalizerFoo["Foo.Name", "003"] } },
};

_selectedFoo = new Foo() { Id = 1, Name = LocalizerFoo["Foo.Name", "001"] };
}

private Task OnItemChanged(IEnumerable<SelectedItem> values, SelectedItem val)
Expand Down
8 changes: 6 additions & 2 deletions src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2376,7 +2376,9 @@
"StatusText2": "Not selected",
"StatusText3": "Indeterminate",
"Checkbox2Text": "Two-way binding",
"Checkbox3Text": "Handwritten labels"
"Checkbox3Text": "Handwritten labels",
"RadioListGenericTitle": "Generic List",
"RadioListGenericIntro": "Enable generic support through <code>SelectedItem&lt;TValue&gt;</code>"
},
"BootstrapBlazor.Server.Components.Samples.CheckboxLists": {
"Title": "CheckboxList",
Expand Down Expand Up @@ -3024,7 +3026,9 @@
"RadiosItem1": "Option one",
"RadiosItem2": "Option two",
"RadiosAdd1": "Beijing",
"RadiosAdd2": "Shanghai"
"RadiosAdd2": "Shanghai",
"RadioListGenericTitle": "Generic List",
"RadioListGenericIntro": "Enable generic support through <code>SelectedItem&lt;TValue&gt;</code>"
},
"BootstrapBlazor.Server.Components.Samples.Rates": {
"RatesTitle": "Rate",
Expand Down
8 changes: 6 additions & 2 deletions src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2426,7 +2426,9 @@
"MaxSelectedCountIntro": "通过设置 <code>MaxSelectedCount</code> 属性控制最大可选数量,通过 <code>OnMaxSelectedCountExceed</code> 回调处理逻辑",
"MaxSelectedCountDesc": "选中节点超过 2 个时,弹出 <code>Toast</code> 提示栏",
"OnMaxSelectedCountExceedTitle": "可选最大数量提示",
"OnMaxSelectedCountExceedContent": "最多只能选择 {0} 项"
"OnMaxSelectedCountExceedContent": "最多只能选择 {0} 项",
"CheckboxListGenericTitle": "泛型支持",
"CheckboxListGenericIntro": "通过 <code>SelectedItem&lt;TValue&gt;</code> 开启泛型支持"
},
"BootstrapBlazor.Server.Components.Samples.ColorPickers": {
"Title": "ColorPicker 颜色拾取器",
Expand Down Expand Up @@ -3024,7 +3026,9 @@
"RadiosItem1": "选项一",
"RadiosItem2": "选项二",
"RadiosAdd1": "北京",
"RadiosAdd2": "上海"
"RadiosAdd2": "上海",
"RadioListGenericTitle": "泛型支持",
"RadioListGenericIntro": "通过 <code>SelectedItem&lt;TValue&gt;</code> 开启泛型支持"
},
"BootstrapBlazor.Server.Components.Samples.Rates": {
"RatesTitle": "Rate 评分",
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.1.6-beta03</Version>
<Version>9.1.6</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
3 changes: 1 addition & 2 deletions src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
{
<BootstrapLabel required="@Required" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText" />
}
@* @if (IsButton)
@if (IsButton)
{
<div @attributes="@AdditionalAttributes" class="radio-list-group">
<div class="@ButtonClassString" role="group">
@foreach (var item in Items)
{
<DynamicElement TagName="span" TriggerClick="!IsDisabled" OnClick="() => OnClick(item)" class="@GetButtonItemClassString(item)">

Check warning on line 15 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / run test

Argument of type 'SelectedItem<TValue>' cannot be used for parameter 'item' of type 'SelectedItem<TValue?>' in 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' due to differences in the nullability of reference types.

Check warning on line 15 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Argument of type 'SelectedItem<TValue>' cannot be used for parameter 'item' of type 'SelectedItem<TValue?>' in 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' due to differences in the nullability of reference types.

Check warning on line 15 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Argument of type 'SelectedItem<TValue>' cannot be used for parameter 'item' of type 'SelectedItem<TValue?>' in 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' due to differences in the nullability of reference types.

Check warning on line 15 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Argument of type 'SelectedItem<TValue>' cannot be used for parameter 'item' of type 'SelectedItem<TValue?>' in 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' due to differences in the nullability of reference types.

Check warning on line 15 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Argument of type 'SelectedItem<TValue>' cannot be used for parameter 'item' of type 'SelectedItem<TValue?>' in 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' due to differences in the nullability of reference types.

Check warning on line 15 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Argument of type 'SelectedItem<TValue>' cannot be used for parameter 'item' of type 'SelectedItem<TValue?>' in 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' due to differences in the nullability of reference types.
@item.Text
</DynamicElement>
}
Expand All @@ -25,8 +25,7 @@
@foreach (var item in Items)
{
var content = GetChildContent(item);
<Radio Value="@item" Color="@Color" GroupName="@GroupName" IsDisabled="@GetDisabledState(item)" ShowAfterLabel="true" ShowLabel="false" DisplayText="@item.Text" State="@CheckState(item)" OnClick="OnClick" ChildContent="content!"></Radio>

Check warning on line 28 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / run test

Nullability of reference types in type of parameter 'item' of 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' doesn't match the target delegate 'Func<SelectedItem<TValue>, Task>' (possibly because of nullability attributes).

Check warning on line 28 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of parameter 'item' of 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' doesn't match the target delegate 'Func<SelectedItem<TValue>, Task>' (possibly because of nullability attributes).

Check warning on line 28 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of parameter 'item' of 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' doesn't match the target delegate 'Func<SelectedItem<TValue>, Task>' (possibly because of nullability attributes).

Check warning on line 28 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of parameter 'item' of 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' doesn't match the target delegate 'Func<SelectedItem<TValue>, Task>' (possibly because of nullability attributes).

Check warning on line 28 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of parameter 'item' of 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' doesn't match the target delegate 'Func<SelectedItem<TValue>, Task>' (possibly because of nullability attributes).

Check warning on line 28 in src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of parameter 'item' of 'Task RadioListGeneric<TValue>.OnClick(SelectedItem<TValue?> item)' doesn't match the target delegate 'Func<SelectedItem<TValue>, Task>' (possibly because of nullability attributes).
}
</div>
}
*@
188 changes: 134 additions & 54 deletions src/BootstrapBlazor/Components/Radio/RadioListGeneric.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ namespace BootstrapBlazor.Components;
/// <summary>
/// 单选框组合组件
/// </summary>
[ExcludeFromCodeCoverage]
public partial class RadioListGeneric<TValue>
public partial class RadioListGeneric<TValue> : IModelEqualityComparer<TValue>
{
/// <summary>
/// 获得/设置 值为可为空枚举类型时是否自动添加空值 默认 false 自定义空值显示文本请参考 <see cref="NullItemText"/>
Expand All @@ -30,56 +29,137 @@ public partial class RadioListGeneric<TValue>
[Parameter]
public RenderFragment<SelectedItem<TValue>>? ItemTemplate { get; set; }

//private string? GroupName => Id;

//private string? ClassString => CssBuilder.Default("radio-list form-control")
// .AddClass("no-border", !ShowBorder && ValidCss != "is-invalid")
// .AddClass("is-vertical", IsVertical)
// .AddClass(CssClass).AddClass(ValidCss)
// .Build();

//private string? ButtonClassString => CssBuilder.Default("radio-list btn-group")
// .AddClass("disabled", IsDisabled)
// .AddClass("btn-group-vertical", IsVertical)
// .AddClassFromAttributes(AdditionalAttributes)
// .Build();

//private string? GetButtonItemClassString(SelectedItem item) => CssBuilder.Default("btn")
// .AddClass($"active bg-{Color.ToDescriptionString()}", CurrentValueAsString == item.Value)
// .Build();

///// <summary>
///// <inheritdoc/>
///// </summary>
//protected override void OnParametersSet()
//{
// var t = NullableUnderlyingType ?? typeof(TValue);
// if (t.IsEnum && Items == null)
// {
// Items = t.ToSelectList<TValue>((NullableUnderlyingType != null && IsAutoAddNullItem) ? new SelectedItem<TValue>(default, NullItemText) : null);
// }

// base.OnParametersSet();
//}

///// <summary>
///// 点击选择框方法
///// </summary>
//private async Task OnClick(SelectedItem<TValue> item)
//{
// if (!IsDisabled)
// {
// if (OnSelectedChanged != null)
// {
// await OnSelectedChanged(Items, Value);
// }
// StateHasChanged();
// }
//}

//private CheckboxState CheckState(SelectedItem<TValue> item) => this.Equals<TValue>(Value, item.Value) ? CheckboxState.Checked : CheckboxState.UnChecked;

//private RenderFragment? GetChildContent(SelectedItem<TValue> item) => ItemTemplate == null
// ? null
// : ItemTemplate(item);
/// <summary>
/// 获得/设置 是否为按钮样式 默认 false
/// </summary>
[Parameter]
public bool IsButton { get; set; }

/// <summary>
/// 获得/设置 是否显示边框 默认为 true
/// </summary>
[Parameter]
public bool ShowBorder { get; set; } = true;

/// <summary>
/// 获得/设置 是否为竖向排列 默认为 false
/// </summary>
[Parameter]
public bool IsVertical { get; set; }

/// <summary>
/// 获得/设置 按钮颜色 默认为 None 未设置
/// </summary>
[Parameter]
public Color Color { get; set; }

/// <summary>
/// 获得/设置 数据源
/// </summary>
[Parameter]
[NotNull]
public IEnumerable<SelectedItem<TValue>>? Items { get; set; }

/// <summary>
/// 获得/设置 SelectedItemChanged 方法
/// </summary>
[Parameter]
public Func<TValue, Task>? OnSelectedChanged { get; set; }

/// <summary>
/// 获得/设置 数据主键标识标签 默认为 <see cref="KeyAttribute"/><code><br /></code>用于判断数据主键标签,如果模型未设置主键时可使用 <see cref="ModelEqualityComparer"/> 参数自定义判断 <code><br /></code>数据模型支持联合主键
/// </summary>
[Parameter]
[NotNull]
public Type? CustomKeyAttribute { get; set; } = typeof(KeyAttribute);

/// <summary>
/// 获得/设置 比较数据是否相同回调方法 默认为 null
/// <para>提供此回调方法时忽略 <see cref="CustomKeyAttribute"/> 属性</para>
/// </summary>
[Parameter]
public Func<TValue, TValue, bool>? ModelEqualityComparer { get; set; }

/// <summary>
/// 获得 当前选项是否被禁用
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private bool GetDisabledState(SelectedItem<TValue> item) => IsDisabled || item.IsDisabled;

private string? GroupName => Id;

private string? ClassString => CssBuilder.Default("radio-list form-control")
.AddClass("no-border", !ShowBorder && ValidCss != "is-invalid")
.AddClass("is-vertical", IsVertical)
.AddClass(CssClass).AddClass(ValidCss)
.Build();

private string? ButtonClassString => CssBuilder.Default("radio-list btn-group")
.AddClass("disabled", IsDisabled)
.AddClass("btn-group-vertical", IsVertical)
.AddClassFromAttributes(AdditionalAttributes)
.Build();

private string? GetButtonItemClassString(SelectedItem<TValue> item) => CssBuilder.Default("btn")
.AddClass($"active bg-{Color.ToDescriptionString()}", Equals(Value, item.Value))
.Build();

/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
base.OnParametersSet();

if (IsButton && Color == Color.None)
{
Color = Color.Primary;
}

var t = NullableUnderlyingType ?? typeof(TValue);
if (t.IsEnum && Items == null)
{
Items = t.ToSelectList<TValue>((NullableUnderlyingType != null && IsAutoAddNullItem) ? new SelectedItem<TValue>(default, NullItemText) : null);
}

Items ??= [];

// set item active
if (Value != null)
{
var item = Items.FirstOrDefault(i => Equals(Value, i.Value));
if (item == null)
{
Value = default;
}
}
}

/// <summary>
/// 点击选择框方法
/// </summary>
private async Task OnClick(SelectedItem<TValue?> item)
{
if (!IsDisabled)
{
CurrentValue = item.Value;
if (OnSelectedChanged != null)
{
await OnSelectedChanged(Value);
}
StateHasChanged();
}
}

private CheckboxState CheckState(SelectedItem<TValue> item) => this.Equals<TValue>(Value, item.Value) ? CheckboxState.Checked : CheckboxState.UnChecked;

private RenderFragment? GetChildContent(SelectedItem<TValue> item) => ItemTemplate == null
? null
: ItemTemplate(item);

/// <summary>
/// <inheritdoc/>
/// </summary>
public bool Equals(TValue? x, TValue? y) => this.Equals<TValue>(x, y);
}
Loading
Loading