mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-04 21:20:16 +08:00
!1701 feat(#I44TYN): add NullSwitch component support Nullable<bool>
* chore: bump version to 5.5.4 * doc: 增加 NullSwitch 组件示例 * doc: 更新示例 * feat: 新增 NullSwitch 组件支持可为空 Switch 组件 * refactor: 移动部分属性到 Toggle 类中
This commit is contained in:
parent
72107cf2f7
commit
1346b55e1b
@ -85,6 +85,14 @@
|
||||
</div>
|
||||
</Block>
|
||||
|
||||
<Block Title="可为空类型的开关" Introduction="通过设置 <code>DefaultValueWhenNull</code> 属性控制 <code>Null</code> 值的默认值,未设置时为 <code>false</code>">
|
||||
<div class="row g-3">
|
||||
<div class="col-6 col-sm-4 col-md-3 col-lg-auto">
|
||||
<NullSwitch @bind-Value="@NullValue" OnColor="Color.Dark" OnText="开启" OffText="关闭"></NullSwitch>
|
||||
</div>
|
||||
</div>
|
||||
</Block>
|
||||
|
||||
<AttributeTable Items="@GetAttributes()" />
|
||||
|
||||
<EventTable Items="@GetEvents()" />
|
||||
|
@ -6,6 +6,7 @@ using BootstrapBlazor.Shared.Common;
|
||||
using BootstrapBlazor.Shared.Pages.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace BootstrapBlazor.Shared.Pages
|
||||
{
|
||||
@ -30,9 +31,7 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
/// </summary>
|
||||
private bool BindValue { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
private Logger? Trace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@ -42,14 +41,16 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
private void OnValueChanged(bool val)
|
||||
{
|
||||
BindValue = val;
|
||||
Trace?.Log($"Switch CurrentValue: {val}");
|
||||
Trace.Log($"Switch CurrentValue: {val}");
|
||||
}
|
||||
|
||||
private bool? NullValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得属性方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<AttributeItem> GetAttributes() => new AttributeItem[]
|
||||
private static IEnumerable<AttributeItem> GetAttributes() => new AttributeItem[]
|
||||
{
|
||||
// TODO: 移动到数据库中
|
||||
new AttributeItem() {
|
||||
|
@ -27,23 +27,6 @@
|
||||
<div class="col-12">
|
||||
<BootstrapInput @bind-Value="@Model.Name" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Table TItem="Foo"
|
||||
IsPagination="true" PageItemsSource="@PageItemsSource"
|
||||
IsStriped="true" IsBordered="true" IsMultipleSelect="true"
|
||||
ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
|
||||
OnQueryAsync="@OnQueryAsync"
|
||||
OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
|
||||
<TableColumns>
|
||||
<TableColumn @bind-Field="@context.DateTime" Width="180" />
|
||||
<TableColumn @bind-Field="@context.Name" Readonly="true" />
|
||||
<TableColumn @bind-Field="@context.Address" Rows="3" />
|
||||
<TableColumn @bind-Field="@context.Education" />
|
||||
<TableColumn @bind-Field="@context.Count" Editable="false" />
|
||||
<TableColumn @bind-Field="@context.Complete" ComponentType="@typeof(Switch)" />
|
||||
</TableColumns>
|
||||
</Table>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Button ButtonType="ButtonType.Submit" Icon="fa fa-fw fa-save" Text="提交表单" IsAsync="true" />
|
||||
</div>
|
||||
|
@ -52,8 +52,6 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
[NotNull]
|
||||
private ComplexFoo? ComplexModel { get; set; }
|
||||
|
||||
private static IEnumerable<int> PageItemsSource => new int[] { 4, 10, 20 };
|
||||
|
||||
/// <summary>
|
||||
/// OnInitializedAsync 方法
|
||||
/// </summary>
|
||||
@ -62,8 +60,6 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
Hobbys = Foo.GenerateHobbys(LocalizerFoo);
|
||||
|
||||
// 切换线程 模拟异步通过 webapi 加载数据
|
||||
await Task.Yield();
|
||||
|
||||
@ -76,72 +72,6 @@ namespace BootstrapBlazor.Shared.Pages
|
||||
Dummy = new Dummy1() { Dummy2 = new Dummy2() },
|
||||
};
|
||||
}
|
||||
private Task<bool> OnSaveAsync(Foo item)
|
||||
{
|
||||
// 增加数据演示代码
|
||||
if (item.Id == 0)
|
||||
{
|
||||
item.Id = Items.Max(i => i.Id) + 1;
|
||||
Items.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldItem = Items.FirstOrDefault(i => i.Id == item.Id);
|
||||
if (oldItem != null)
|
||||
{
|
||||
oldItem.Name = item.Name;
|
||||
oldItem.Address = item.Address;
|
||||
oldItem.DateTime = item.DateTime;
|
||||
oldItem.Count = item.Count;
|
||||
oldItem.Complete = item.Complete;
|
||||
oldItem.Education = item.Education;
|
||||
}
|
||||
}
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
private Task<bool> OnDeleteAsync(IEnumerable<Foo> items)
|
||||
{
|
||||
items.ToList().ForEach(i => Items.Remove(i));
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
private Task<QueryData<Foo>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
IEnumerable<Foo> items = Items;
|
||||
|
||||
// 过滤
|
||||
var isFiltered = false;
|
||||
if (options.Filters.Any())
|
||||
{
|
||||
items = items.Where(options.Filters.GetFilterFunc<Foo>());
|
||||
isFiltered = true;
|
||||
}
|
||||
|
||||
// 排序
|
||||
var isSorted = false;
|
||||
if (!string.IsNullOrEmpty(options.SortName))
|
||||
{
|
||||
var invoker = SortLambdaCache.GetOrAdd(typeof(Foo), key => LambdaExtensions.GetSortLambda<Foo>().Compile());
|
||||
items = invoker(items, options.SortName, options.SortOrder);
|
||||
isSorted = true;
|
||||
}
|
||||
|
||||
// 设置记录总数
|
||||
var total = items.Count();
|
||||
|
||||
// 内存分页
|
||||
items = items.Skip((options.PageIndex - 1) * options.PageItems).Take(options.PageItems).ToList();
|
||||
|
||||
return Task.FromResult(new QueryData<Foo>()
|
||||
{
|
||||
Items = items,
|
||||
TotalCount = total,
|
||||
IsSorted = isSorted,
|
||||
IsFiltered = isFiltered,
|
||||
IsSearch = true
|
||||
});
|
||||
}
|
||||
|
||||
private Task OnInvalidSubmit1(EditContext context)
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>5.5.3</Version>
|
||||
<Version>5.5.4</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||
|
16
src/BootstrapBlazor/Components/Switch/NullSwitch.razor
Normal file
16
src/BootstrapBlazor/Components/Switch/NullSwitch.razor
Normal file
@ -0,0 +1,16 @@
|
||||
@namespace BootstrapBlazor.Components
|
||||
@inherits ToggleBase<bool?>
|
||||
|
||||
@if (IsShowLabel)
|
||||
{
|
||||
<label class="form-label" required="@Required">@DisplayText</label>
|
||||
}
|
||||
<div @attributes="@AdditionalAttributes" class="@ClassName" style="@SwitchStyleName">
|
||||
<span class="@CoreClassName" data-inner-text="@GetInnerText()" style="@StyleName" @onclick="@OnClick" />
|
||||
@if (!string.IsNullOrEmpty(Text))
|
||||
{
|
||||
<span class="switch-label">
|
||||
@Text
|
||||
</span>
|
||||
}
|
||||
</div>
|
150
src/BootstrapBlazor/Components/Switch/NullSwitch.razor.cs
Normal file
150
src/BootstrapBlazor/Components/Switch/NullSwitch.razor.cs
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BootstrapBlazor.Components
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public partial class NullSwitch
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得 样式集合
|
||||
/// </summary>
|
||||
private string? ClassName => CssBuilder.Default("switch")
|
||||
.AddClass("is-checked", ComponentValue)
|
||||
.AddClass("is-disabled", IsDisabled)
|
||||
.AddClassFromAttributes(AdditionalAttributes)
|
||||
.Build();
|
||||
|
||||
private string? CoreClassName => CssBuilder.Default("switch-core")
|
||||
.AddClass($"border-{OnColor.ToDescriptionString()}", OnColor != Color.None && ComponentValue)
|
||||
.AddClass($"bg-{OnColor.ToDescriptionString()}", OnColor != Color.None && ComponentValue)
|
||||
.AddClass($"border-{OffColor.ToDescriptionString()}", OffColor != Color.None && !ComponentValue)
|
||||
.AddClass($"bg-{OffColor.ToDescriptionString()}", OffColor != Color.None && !ComponentValue)
|
||||
.Build();
|
||||
|
||||
private string? GetInnerText()
|
||||
{
|
||||
string? ret = null;
|
||||
if (ShowInnerText)
|
||||
{
|
||||
ret = ComponentValue ? OnInnerText : OffInnerText;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得 显示文字
|
||||
/// </summary>
|
||||
private string? Text => ComponentValue ? OnText : OffText;
|
||||
|
||||
/// <summary>
|
||||
/// 获得 组件最小宽度
|
||||
/// </summary>
|
||||
private string? SwitchStyleName => CssBuilder.Default()
|
||||
.AddClass($"min-width: {Width}px;", Width > 0)
|
||||
.AddStyleFromAttributes(AdditionalAttributes)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// 获得 Style 集合
|
||||
/// </summary>
|
||||
protected override string? StyleName => CssBuilder.Default()
|
||||
.AddClass($"width: {Width}px;", Width > 0)
|
||||
.AddClass($"height: {Height}px;", Height >= 20)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 开颜色
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Color OnColor { get; set; } = Color.Success;
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 关颜色
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Color OffColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件宽度 默认 40
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public override int Width { get; set; } = 40;
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 控件高度默认 20px
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int Height { get; set; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件 On 时内置显示文本
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public string? OnInnerText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件 Off 时内置显示文本
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public string? OffInnerText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 是否显示内置文字 默认 false 显示
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool ShowInnerText { get; set; }
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IStringLocalizer<Switch>? Localizer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 绑定值为空时的默认值 默认为 false
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool DefaultValueWhenNull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件 Value 值
|
||||
/// </summary>
|
||||
protected bool ComponentValue => Value ?? DefaultValueWhenNull;
|
||||
|
||||
/// <summary>
|
||||
/// OnInitialized 方法
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
OnInnerText ??= Localizer[nameof(OnInnerText)];
|
||||
OffInnerText ??= Localizer[nameof(OffInnerText)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击控件时触发此方法
|
||||
/// </summary>
|
||||
private async Task OnClick()
|
||||
{
|
||||
if (!IsDisabled)
|
||||
{
|
||||
Value = !ComponentValue;
|
||||
if (ValueChanged.HasDelegate)
|
||||
{
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
OnValueChanged?.Invoke(Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
@namespace BootstrapBlazor.Components
|
||||
@inherits ToggleBase
|
||||
@inherits ToggleBase<bool>
|
||||
|
||||
@if (IsShowLabel)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BootstrapBlazor.Components
|
||||
{
|
||||
@ -13,10 +14,7 @@ namespace BootstrapBlazor.Components
|
||||
/// </summary>
|
||||
public partial class Switch
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得 样式集合
|
||||
/// </summary>
|
||||
protected override string? ClassName => CssBuilder.Default("switch")
|
||||
private string? ClassName => CssBuilder.Default("switch")
|
||||
.AddClass("is-checked", Value)
|
||||
.AddClass("is-disabled", IsDisabled)
|
||||
.AddClassFromAttributes(AdditionalAttributes)
|
||||
@ -32,7 +30,11 @@ namespace BootstrapBlazor.Components
|
||||
private string? GetInnerText()
|
||||
{
|
||||
string? ret = null;
|
||||
if (ShowInnerText) ret = Value ? OnInnerText : OffInnerText;
|
||||
if (ShowInnerText)
|
||||
{
|
||||
ret = Value ? OnInnerText : OffInnerText;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -115,5 +117,22 @@ namespace BootstrapBlazor.Components
|
||||
OnInnerText ??= Localizer[nameof(OnInnerText)];
|
||||
OffInnerText ??= Localizer[nameof(OffInnerText)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击控件时触发此方法
|
||||
/// </summary>
|
||||
private async Task OnClick()
|
||||
{
|
||||
if (!IsDisabled)
|
||||
{
|
||||
Value = !Value;
|
||||
if (ValueChanged.HasDelegate)
|
||||
{
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
OnValueChanged?.Invoke(Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
@namespace BootstrapBlazor.Components
|
||||
@inherits ToggleBase
|
||||
@inherits ToggleBase<bool>
|
||||
|
||||
@if (IsShowLabel)
|
||||
{
|
||||
|
@ -5,18 +5,34 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BootstrapBlazor.Components
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed partial class Toggle
|
||||
public partial class Toggle
|
||||
{
|
||||
private string? ClassName => CssBuilder.Default("btn btn-toggle")
|
||||
.AddClass("btn-default off", !Value)
|
||||
.AddClass("disabled", IsDisabled)
|
||||
.Build();
|
||||
|
||||
private string? ToggleOnClassString => CssBuilder.Default("toggle-on")
|
||||
.AddClass($"bg-{Color.ToDescriptionString()}", Color != Color.None)
|
||||
.Build();
|
||||
|
||||
private string? WrapperClassString => CssBuilder.Default("toggle")
|
||||
.AddClassFromAttributes(AdditionalAttributes)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件颜色 默认为 Success 颜色
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Color Color { get; set; } = Color.Success;
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IStringLocalizer<Toggle>? Localizer { get; set; }
|
||||
@ -31,5 +47,18 @@ namespace BootstrapBlazor.Components
|
||||
OnText ??= Localizer[nameof(OnText)];
|
||||
OffText ??= Localizer[nameof(OffText)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击控件时触发此方法
|
||||
/// </summary>
|
||||
private async Task OnClick()
|
||||
{
|
||||
if (!IsDisabled)
|
||||
{
|
||||
Value = !Value;
|
||||
if (ValueChanged.HasDelegate) await ValueChanged.InvokeAsync(Value);
|
||||
OnValueChanged?.Invoke(Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,32 +3,15 @@
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BootstrapBlazor.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Toggle 开关组件
|
||||
/// </summary>
|
||||
public class ToggleBase : ValidateBase<bool>
|
||||
public class ToggleBase<TValue> : ValidateBase<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得 样式集合
|
||||
/// </summary>
|
||||
protected virtual string? ClassName => CssBuilder.Default("btn btn-toggle")
|
||||
.AddClass("btn-default off", !Value)
|
||||
.AddClass("disabled", IsDisabled)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// 获得 ToggleOn 样式
|
||||
/// </summary>
|
||||
protected string? ToggleOnClassString => CssBuilder.Default("toggle-on")
|
||||
.AddClass($"bg-{Color.ToDescriptionString()}", Color != Color.None)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// 获得 Style 集合
|
||||
/// </summary>
|
||||
@ -55,24 +38,5 @@ namespace BootstrapBlazor.Components
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public virtual string? OffText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件颜色 默认为 Success 颜色
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Color Color { get; set; } = Color.Success;
|
||||
|
||||
/// <summary>
|
||||
/// 点击控件时触发此方法
|
||||
/// </summary>
|
||||
protected virtual async Task OnClick()
|
||||
{
|
||||
if (!IsDisabled)
|
||||
{
|
||||
Value = !Value;
|
||||
if (ValueChanged.HasDelegate) await ValueChanged.InvokeAsync(Value);
|
||||
OnValueChanged?.Invoke(Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user