!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:
Argo 2021-08-10 13:00:41 +00:00
parent 72107cf2f7
commit 1346b55e1b
12 changed files with 238 additions and 138 deletions

View File

@ -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()" />

View File

@ -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() {

View File

@ -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>

View File

@ -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)
{

View File

@ -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'">

View 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>

View 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);
}
}
}
}

View File

@ -1,5 +1,5 @@
@namespace BootstrapBlazor.Components
@inherits ToggleBase
@inherits ToggleBase<bool>
@if (IsShowLabel)
{

View File

@ -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);
}
}
}
}

View File

@ -1,5 +1,5 @@
@namespace BootstrapBlazor.Components
@inherits ToggleBase
@inherits ToggleBase<bool>
@if (IsShowLabel)
{

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}