mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-11-30 11:08:53 +08:00
feat(Split): add OnResizeAsync parameter (#4177)
* feat: 增加 OnResizedAsync 回调方法 * feat: 增加 SplitterResizedEventArgs 类 * doc: 增加 OnResizeAsync 示例 * refactor: 增加 SplitsOnResizedAsync 文档 * doc: 增加折叠按钮文档 * refactor: 增加 IsCollapsible 参数动态支持 * test: 更新单元测试 * refactor: 重构方法名 * chore: bump version 8.8.5-beta06
This commit is contained in:
parent
ca28669677
commit
09d53ff881
@ -6,12 +6,22 @@
|
|||||||
<DemoBlock Title="@Localizer["SplitsNormalTitle"]"
|
<DemoBlock Title="@Localizer["SplitsNormalTitle"]"
|
||||||
Introduction="@Localizer["SplitsNormalIntro"]"
|
Introduction="@Localizer["SplitsNormalIntro"]"
|
||||||
Name="Normal">
|
Name="Normal">
|
||||||
<section ignore class="d-flex align-items-center">
|
<section ignore class="row">
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
<Switch Value="_showBarHandle" OnValueChanged="OnShowBarHandle" class="w-auto"></Switch>
|
<Switch Value="_showBarHandle" OnValueChanged="OnShowBarHandle" class="w-auto"></Switch>
|
||||||
<div class="ms-3">@_barHandleText</div>
|
<div class="ms-3">@_barHandleText</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<Switch Value="_isCollapsible" OnValueChanged="OnCollapsible" class="w-auto"></Switch>
|
||||||
|
<div class="ms-3">@_collapsibleText</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div class="border split-demo split-demo-horizontal">
|
<div class="border split-demo split-demo-horizontal">
|
||||||
<Split ShowBarHandle="_showBarHandle">
|
<Split ShowBarHandle="_showBarHandle" OnResizedAsync="OnResizedAsync" IsCollapsible="_isCollapsible">
|
||||||
<FirstPaneTemplate>
|
<FirstPaneTemplate>
|
||||||
<div class="d-flex justify-content-center align-items-center h-100">@Localizer["SplitsPanel1"]</div>
|
<div class="d-flex justify-content-center align-items-center h-100">@Localizer["SplitsPanel1"]</div>
|
||||||
</FirstPaneTemplate>
|
</FirstPaneTemplate>
|
||||||
@ -20,6 +30,7 @@
|
|||||||
</SecondPaneTemplate>
|
</SecondPaneTemplate>
|
||||||
</Split>
|
</Split>
|
||||||
</div>
|
</div>
|
||||||
|
<ConsoleLogger @ref="_logger"></ConsoleLogger>
|
||||||
</DemoBlock>
|
</DemoBlock>
|
||||||
|
|
||||||
<DemoBlock Title="@Localizer["SplitsPercentTitle"]"
|
<DemoBlock Title="@Localizer["SplitsPercentTitle"]"
|
||||||
|
@ -11,8 +11,14 @@ public sealed partial class Splits
|
|||||||
{
|
{
|
||||||
private bool _showBarHandle = true;
|
private bool _showBarHandle = true;
|
||||||
|
|
||||||
|
private bool _isCollapsible = false;
|
||||||
|
|
||||||
private string? _barHandleText;
|
private string? _barHandleText;
|
||||||
|
|
||||||
|
private string? _collapsibleText;
|
||||||
|
|
||||||
|
private ConsoleLogger _logger = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,6 +26,7 @@ public sealed partial class Splits
|
|||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
_barHandleText = _showBarHandle ? Localizer["SplitsBarHandleShow"] : Localizer["SplitsBarHandleHide"];
|
_barHandleText = _showBarHandle ? Localizer["SplitsBarHandleShow"] : Localizer["SplitsBarHandleHide"];
|
||||||
|
_collapsibleText = _showBarHandle ? Localizer["SplitsCollapsibleTrue"] : Localizer["SplitsCollapsibleFalse"];
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task OnShowBarHandle(bool v)
|
private Task OnShowBarHandle(bool v)
|
||||||
@ -30,6 +37,20 @@ public sealed partial class Splits
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task OnCollapsible(bool v)
|
||||||
|
{
|
||||||
|
_isCollapsible = v;
|
||||||
|
_collapsibleText = _showBarHandle ? Localizer["SplitsCollapsibleTrue"] : Localizer["SplitsCollapsibleFalse"];
|
||||||
|
StateHasChanged();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task OnResizedAsync(SplitterResizedEventArgs args)
|
||||||
|
{
|
||||||
|
_logger.Log($"FirstPanelSize: {args.FirstPanelSize} IsCollapsed: {args.IsCollapsed} IsExpanded: {args.IsExpanded}");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得属性方法
|
/// 获得属性方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -91,6 +112,14 @@ public sealed partial class Splits
|
|||||||
Type = "bool",
|
Type = "bool",
|
||||||
ValueList = "true|false",
|
ValueList = "true|false",
|
||||||
DefaultValue = "true"
|
DefaultValue = "true"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "OnResizedAsync",
|
||||||
|
Description = Localizer["SplitsOnResizedAsync"],
|
||||||
|
Type = "Func<SplitterResizedEventArgs, Task>",
|
||||||
|
ValueList = " — ",
|
||||||
|
DefaultValue = " — "
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1774,7 +1774,10 @@
|
|||||||
"SplitsIsKeepOriginalSize": "Whether to keep the original size when restoring after collapsed",
|
"SplitsIsKeepOriginalSize": "Whether to keep the original size when restoring after collapsed",
|
||||||
"SplitsShowBarHandle": "Whether to display the drag bar",
|
"SplitsShowBarHandle": "Whether to display the drag bar",
|
||||||
"SplitsBarHandleShow": "Bar handle shown",
|
"SplitsBarHandleShow": "Bar handle shown",
|
||||||
"SplitsBarHandleHide": "Bar handle hidden"
|
"SplitsBarHandleHide": "Bar handle hidden",
|
||||||
|
"SplitsOnResizedAsync": "Callback method when the panel size changes",
|
||||||
|
"SplitsCollapsibleTrue": "Collapse button shown",
|
||||||
|
"SplitsCollapsibleFalse": "Collapse button hidden"
|
||||||
},
|
},
|
||||||
"BootstrapBlazor.Server.Components.Samples.Dropdowns": {
|
"BootstrapBlazor.Server.Components.Samples.Dropdowns": {
|
||||||
"Title": "Dropdown",
|
"Title": "Dropdown",
|
||||||
|
@ -1774,7 +1774,10 @@
|
|||||||
"SplitsIsKeepOriginalSize": "折叠后恢复时是否保持原始大小",
|
"SplitsIsKeepOriginalSize": "折叠后恢复时是否保持原始大小",
|
||||||
"SplitsShowBarHandle": "是否显示拖动条",
|
"SplitsShowBarHandle": "是否显示拖动条",
|
||||||
"SplitsBarHandleShow": "显示拖动栏",
|
"SplitsBarHandleShow": "显示拖动栏",
|
||||||
"SplitsBarHandleHide": "隐藏拖动栏"
|
"SplitsBarHandleHide": "隐藏拖动栏",
|
||||||
|
"SplitsOnResizedAsync": "面板尺寸改变时回调方法",
|
||||||
|
"SplitsCollapsibleTrue": "显示调整按钮",
|
||||||
|
"SplitsCollapsibleFalse": "隐藏调整按钮"
|
||||||
},
|
},
|
||||||
"BootstrapBlazor.Server.Components.Samples.Dropdowns": {
|
"BootstrapBlazor.Server.Components.Samples.Dropdowns": {
|
||||||
"Title": "Dropdown 下拉菜单",
|
"Title": "Dropdown 下拉菜单",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>8.8.5-beta05</Version>
|
<Version>8.8.5-beta06</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||||
|
@ -9,21 +9,6 @@ namespace BootstrapBlazor.Components;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class Split
|
public sealed partial class Split
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 获得 组件样式
|
|
||||||
/// </summary>
|
|
||||||
private string? ClassString => CssBuilder.Default("split")
|
|
||||||
.AddClass("is-vertical", IsVertical)
|
|
||||||
.AddClassFromAttributes(AdditionalAttributes)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获得 第一个窗格 Style
|
|
||||||
/// </summary>
|
|
||||||
private string? StyleString => CssBuilder.Default()
|
|
||||||
.AddClass($"flex-basis: {Basis.ConvertToPercentString()};")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 是否开启折叠功能 默认 false
|
/// 获取 是否开启折叠功能 默认 false
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -70,25 +55,69 @@ public sealed partial class Split
|
|||||||
/// 获得/设置 窗格折叠时回调方法 参数 bool 值为 true 是表示已折叠 值为 false 表示第二个已折叠
|
/// 获得/设置 窗格折叠时回调方法 参数 bool 值为 true 是表示已折叠 值为 false 表示第二个已折叠
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
|
[Obsolete("已过期,请使用 Deprecated. Please use OnResizedAsync")]
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
public Func<bool, Task>? OnCollapsedAsync { get; set; }
|
public Func<bool, Task>? OnCollapsedAsync { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 窗格尺寸改变时回调方法 可参阅 <see cref="SplitterResizedEventArgs"/>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<SplitterResizedEventArgs, Task>? OnResizedAsync { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得 组件样式
|
||||||
|
/// </summary>
|
||||||
|
private string? ClassString => CssBuilder.Default("split")
|
||||||
|
.AddClass("is-vertical", IsVertical)
|
||||||
|
.AddClassFromAttributes(AdditionalAttributes)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得 第一个窗格 Style
|
||||||
|
/// </summary>
|
||||||
|
private string? StyleString => CssBuilder.Default()
|
||||||
|
.AddClass($"flex-basis: {Basis.ConvertToPercentString()};")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
private bool _lastCollapsible;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, nameof(TriggerOnCollapsed), new { IsKeepOriginalSize });
|
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, nameof(TriggerOnResize), new { IsKeepOriginalSize });
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
_lastCollapsible = IsCollapsible;
|
||||||
|
}
|
||||||
|
else if (_lastCollapsible != IsCollapsible)
|
||||||
|
{
|
||||||
|
_lastCollapsible = IsCollapsible;
|
||||||
|
await InvokeVoidAsync("update", Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 窗格折叠时回调方法 由 JavaScript 调用
|
/// 窗格折叠时回调方法 由 JavaScript 调用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="collapsed"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[JSInvokable]
|
[JSInvokable]
|
||||||
public async Task TriggerOnCollapsed(bool collapsed)
|
public async Task TriggerOnResize(string left)
|
||||||
{
|
{
|
||||||
if (OnCollapsedAsync != null)
|
if (OnResizedAsync != null)
|
||||||
{
|
{
|
||||||
await OnCollapsedAsync(collapsed);
|
await OnResizedAsync(new SplitterResizedEventArgs(left));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,7 @@ export function init(id, invoke, method, option) {
|
|||||||
const splitRight = el.children[1];
|
const splitRight = el.children[1];
|
||||||
const splitBar = el.children[2];
|
const splitBar = el.children[2];
|
||||||
|
|
||||||
const split = { el, option }
|
const split = { el, invoke, method, option, splitLeft, splitBar };
|
||||||
split.splitLeft = splitLeft;
|
|
||||||
split.splitBar = splitBar;
|
|
||||||
Data.set(id, split)
|
Data.set(id, split)
|
||||||
Drag.drag(splitBar,
|
Drag.drag(splitBar,
|
||||||
e => {
|
e => {
|
||||||
@ -55,12 +53,13 @@ export function init(id, invoke, method, option) {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
el.classList.remove('dragging');
|
el.classList.remove('dragging');
|
||||||
console.log('end');
|
|
||||||
delete option.restoreLeftBasis;
|
delete option.restoreLeftBasis;
|
||||||
removeMask(splitLeft, splitRight);
|
removeMask(splitLeft, splitRight);
|
||||||
|
invoke.invokeMethodAsync(method, splitLeft.style.flexBasis);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
split.initCollapseButton = () => {
|
||||||
let start = 0;
|
let start = 0;
|
||||||
const step = ts => {
|
const step = ts => {
|
||||||
if (start === 0) {
|
if (start === 0) {
|
||||||
@ -77,16 +76,30 @@ export function init(id, invoke, method, option) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
splitLeft.classList.add('is-collapsed');
|
splitLeft.classList.add('is-collapsed');
|
||||||
const triggerLeft = element.classList.contains("split-bar-arrow-left");
|
const triggerLeft = element.classList.contains("split-bar-arrow-left");
|
||||||
invoke.invokeMethodAsync(method, triggerLeft);
|
|
||||||
setLeftBasis(split, triggerLeft);
|
setLeftBasis(split, triggerLeft);
|
||||||
start = 0;
|
start = 0;
|
||||||
requestAnimationFrame(step);
|
requestAnimationFrame(step);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
split.initCollapseButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update(id) {
|
||||||
|
const split = Data.get(id)
|
||||||
|
|
||||||
|
if (split) {
|
||||||
|
const { splitBar, initCollapseButton } = split;
|
||||||
|
if (splitBar) {
|
||||||
|
disposeCollapseButton(splitBar);
|
||||||
|
initCollapseButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setLeftBasis = (split, triggerLeft) => {
|
const setLeftBasis = (split, triggerLeft) => {
|
||||||
const { option, splitLeft } = split;
|
const { option, splitLeft, invoke, method } = split;
|
||||||
let leftBasis = splitLeft.style.flexBasis;
|
let leftBasis = splitLeft.style.flexBasis;
|
||||||
if (option.isKeepOriginalSize) {
|
if (option.isKeepOriginalSize) {
|
||||||
if (option.restoreLeftBasis === void 0) {
|
if (option.restoreLeftBasis === void 0) {
|
||||||
@ -112,6 +125,7 @@ const setLeftBasis = (split, triggerLeft) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
splitLeft.style.setProperty('flex-basis', leftBasis);
|
splitLeft.style.setProperty('flex-basis', leftBasis);
|
||||||
|
invoke.invokeMethodAsync(method, leftBasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
const showMask = (left, right) => {
|
const showMask = (left, right) => {
|
||||||
@ -136,15 +150,21 @@ const deleteMask = el => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const disposeCollapseButton = splitBar => {
|
||||||
|
[...splitBar.querySelectorAll('.split-bar-arrow')].forEach(element => {
|
||||||
|
EventHandler.off(element, 'mousedown');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function dispose(id) {
|
export function dispose(id) {
|
||||||
const split = Data.get(id)
|
const split = Data.get(id)
|
||||||
Data.remove(id)
|
Data.remove(id)
|
||||||
|
|
||||||
if (split) {
|
if (split) {
|
||||||
const { el } = split;
|
const { splitBar } = split;
|
||||||
if (el.splitBar) {
|
if (splitBar) {
|
||||||
EventHandler.off(splitBar, 'click', '.split-bar-arrow');
|
disposeCollapseButton(splitBar);
|
||||||
Drag.dispose(el.splitBar);
|
Drag.dispose(splitBar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
// 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/
|
||||||
|
|
||||||
|
namespace BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SplitterResizedEventArgs 类
|
||||||
|
/// </summary>
|
||||||
|
public class SplitterResizedEventArgs(string left)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of panel 1 (top/left) after a resize operation.
|
||||||
|
/// </summary>
|
||||||
|
public string FirstPanelSize => left;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得 组件第一个面板是否折叠
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCollapsed => left == "0%";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得 组件第一个面板是否展开
|
||||||
|
/// </summary>
|
||||||
|
public bool IsExpanded => left == "100%";
|
||||||
|
}
|
@ -67,24 +67,31 @@ public class SplitTest : BootstrapBlazorTestBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task OnCollapsedAsync_Ok()
|
public async Task OnResizedAsync_Ok()
|
||||||
{
|
{
|
||||||
var state = false;
|
SplitterResizedEventArgs? state = null;
|
||||||
var cut = Context.RenderComponent<Split>(pb =>
|
var cut = Context.RenderComponent<Split>(pb =>
|
||||||
{
|
{
|
||||||
pb.Add(b => b.FirstPaneTemplate, RenderSplitView("I am Pane1"));
|
pb.Add(b => b.FirstPaneTemplate, RenderSplitView("I am Pane1"));
|
||||||
pb.Add(b => b.SecondPaneTemplate, RenderSplitView("I am Pane2"));
|
pb.Add(b => b.SecondPaneTemplate, RenderSplitView("I am Pane2"));
|
||||||
pb.Add(b => b.IsCollapsible, true);
|
pb.Add(b => b.IsCollapsible, true);
|
||||||
pb.Add(b => b.OnCollapsedAsync, async (collapsed) =>
|
pb.Add(b => b.OnResizedAsync, async args =>
|
||||||
{
|
{
|
||||||
state = collapsed;
|
state = args;
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await cut.InvokeAsync(() => cut.Instance.TriggerOnCollapsed(true));
|
Assert.Null(state);
|
||||||
Assert.True(state);
|
await cut.InvokeAsync(() => cut.Instance.TriggerOnResize("0%"));
|
||||||
await cut.InvokeAsync(() => cut.Instance.TriggerOnCollapsed(false));
|
Assert.NotNull(state);
|
||||||
Assert.False(state);
|
Assert.Equal("0%", state.FirstPanelSize);
|
||||||
|
Assert.True(state.IsCollapsed);
|
||||||
|
Assert.False(state.IsExpanded);
|
||||||
|
|
||||||
|
await cut.InvokeAsync(() => cut.Instance.TriggerOnResize("100%"));
|
||||||
|
Assert.Equal("100%", state.FirstPanelSize);
|
||||||
|
Assert.True(state.IsExpanded);
|
||||||
|
Assert.False(state.IsCollapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RenderFragment RenderSplitView(string name = "I am Pane1") => builder =>
|
static RenderFragment RenderSplitView(string name = "I am Pane1") => builder =>
|
||||||
|
Loading…
Reference in New Issue
Block a user