mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-11-30 02:58:37 +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"]"
|
||||
Introduction="@Localizer["SplitsNormalIntro"]"
|
||||
Name="Normal">
|
||||
<section ignore class="d-flex align-items-center">
|
||||
<Switch Value="_showBarHandle" OnValueChanged="OnShowBarHandle" class="w-auto"></Switch>
|
||||
<div class="ms-3">@_barHandleText</div>
|
||||
<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>
|
||||
<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>
|
||||
<div class="border split-demo split-demo-horizontal">
|
||||
<Split ShowBarHandle="_showBarHandle">
|
||||
<Split ShowBarHandle="_showBarHandle" OnResizedAsync="OnResizedAsync" IsCollapsible="_isCollapsible">
|
||||
<FirstPaneTemplate>
|
||||
<div class="d-flex justify-content-center align-items-center h-100">@Localizer["SplitsPanel1"]</div>
|
||||
</FirstPaneTemplate>
|
||||
@ -20,6 +30,7 @@
|
||||
</SecondPaneTemplate>
|
||||
</Split>
|
||||
</div>
|
||||
<ConsoleLogger @ref="_logger"></ConsoleLogger>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="@Localizer["SplitsPercentTitle"]"
|
||||
|
@ -11,8 +11,14 @@ public sealed partial class Splits
|
||||
{
|
||||
private bool _showBarHandle = true;
|
||||
|
||||
private bool _isCollapsible = false;
|
||||
|
||||
private string? _barHandleText;
|
||||
|
||||
private string? _collapsibleText;
|
||||
|
||||
private ConsoleLogger _logger = default!;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
@ -20,6 +26,7 @@ public sealed partial class Splits
|
||||
{
|
||||
base.OnParametersSet();
|
||||
_barHandleText = _showBarHandle ? Localizer["SplitsBarHandleShow"] : Localizer["SplitsBarHandleHide"];
|
||||
_collapsibleText = _showBarHandle ? Localizer["SplitsCollapsibleTrue"] : Localizer["SplitsCollapsibleFalse"];
|
||||
}
|
||||
|
||||
private Task OnShowBarHandle(bool v)
|
||||
@ -30,6 +37,20 @@ public sealed partial class Splits
|
||||
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>
|
||||
@ -91,6 +112,14 @@ public sealed partial class Splits
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
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",
|
||||
"SplitsShowBarHandle": "Whether to display the drag bar",
|
||||
"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": {
|
||||
"Title": "Dropdown",
|
||||
|
@ -1774,7 +1774,10 @@
|
||||
"SplitsIsKeepOriginalSize": "折叠后恢复时是否保持原始大小",
|
||||
"SplitsShowBarHandle": "是否显示拖动条",
|
||||
"SplitsBarHandleShow": "显示拖动栏",
|
||||
"SplitsBarHandleHide": "隐藏拖动栏"
|
||||
"SplitsBarHandleHide": "隐藏拖动栏",
|
||||
"SplitsOnResizedAsync": "面板尺寸改变时回调方法",
|
||||
"SplitsCollapsibleTrue": "显示调整按钮",
|
||||
"SplitsCollapsibleFalse": "隐藏调整按钮"
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.Dropdowns": {
|
||||
"Title": "Dropdown 下拉菜单",
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>8.8.5-beta05</Version>
|
||||
<Version>8.8.5-beta06</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||
|
@ -9,21 +9,6 @@ namespace BootstrapBlazor.Components;
|
||||
/// </summary>
|
||||
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>
|
||||
/// 获取 是否开启折叠功能 默认 false
|
||||
/// </summary>
|
||||
@ -70,25 +55,69 @@ public sealed partial class Split
|
||||
/// 获得/设置 窗格折叠时回调方法 参数 bool 值为 true 是表示已折叠 值为 false 表示第二个已折叠
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[Obsolete("已过期,请使用 Deprecated. Please use OnResizedAsync")]
|
||||
[ExcludeFromCodeCoverage]
|
||||
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>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// 窗格折叠时回调方法 由 JavaScript 调用
|
||||
/// </summary>
|
||||
/// <param name="collapsed"></param>
|
||||
/// <returns></returns>
|
||||
[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 splitBar = el.children[2];
|
||||
|
||||
const split = { el, option }
|
||||
split.splitLeft = splitLeft;
|
||||
split.splitBar = splitBar;
|
||||
const split = { el, invoke, method, option, splitLeft, splitBar };
|
||||
Data.set(id, split)
|
||||
Drag.drag(splitBar,
|
||||
e => {
|
||||
@ -55,38 +53,53 @@ export function init(id, invoke, method, option) {
|
||||
},
|
||||
() => {
|
||||
el.classList.remove('dragging');
|
||||
console.log('end');
|
||||
delete option.restoreLeftBasis;
|
||||
removeMask(splitLeft, splitRight);
|
||||
invoke.invokeMethodAsync(method, splitLeft.style.flexBasis);
|
||||
}
|
||||
);
|
||||
|
||||
let start = 0;
|
||||
const step = ts => {
|
||||
if (start === 0) {
|
||||
start = ts;
|
||||
}
|
||||
if (ts - start > 300) {
|
||||
splitLeft.classList.remove('is-collapsed');
|
||||
}
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
[...splitBar.querySelectorAll('.split-bar-arrow')].forEach(element => {
|
||||
EventHandler.on(element, 'mousedown', e => {
|
||||
e.stopPropagation();
|
||||
splitLeft.classList.add('is-collapsed');
|
||||
const triggerLeft = element.classList.contains("split-bar-arrow-left");
|
||||
invoke.invokeMethodAsync(method, triggerLeft);
|
||||
setLeftBasis(split, triggerLeft);
|
||||
start = 0;
|
||||
split.initCollapseButton = () => {
|
||||
let start = 0;
|
||||
const step = ts => {
|
||||
if (start === 0) {
|
||||
start = ts;
|
||||
}
|
||||
if (ts - start > 300) {
|
||||
splitLeft.classList.remove('is-collapsed');
|
||||
}
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
[...splitBar.querySelectorAll('.split-bar-arrow')].forEach(element => {
|
||||
EventHandler.on(element, 'mousedown', e => {
|
||||
e.stopPropagation();
|
||||
splitLeft.classList.add('is-collapsed');
|
||||
const triggerLeft = element.classList.contains("split-bar-arrow-left");
|
||||
setLeftBasis(split, triggerLeft);
|
||||
start = 0;
|
||||
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 { option, splitLeft } = split;
|
||||
const { option, splitLeft, invoke, method } = split;
|
||||
let leftBasis = splitLeft.style.flexBasis;
|
||||
if (option.isKeepOriginalSize) {
|
||||
if (option.restoreLeftBasis === void 0) {
|
||||
@ -112,6 +125,7 @@ const setLeftBasis = (split, triggerLeft) => {
|
||||
}
|
||||
}
|
||||
splitLeft.style.setProperty('flex-basis', leftBasis);
|
||||
invoke.invokeMethodAsync(method, leftBasis);
|
||||
}
|
||||
|
||||
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) {
|
||||
const split = Data.get(id)
|
||||
Data.remove(id)
|
||||
|
||||
if (split) {
|
||||
const { el } = split;
|
||||
if (el.splitBar) {
|
||||
EventHandler.off(splitBar, 'click', '.split-bar-arrow');
|
||||
Drag.dispose(el.splitBar);
|
||||
const { splitBar } = split;
|
||||
if (splitBar) {
|
||||
disposeCollapseButton(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]
|
||||
public async Task OnCollapsedAsync_Ok()
|
||||
public async Task OnResizedAsync_Ok()
|
||||
{
|
||||
var state = false;
|
||||
SplitterResizedEventArgs? state = null;
|
||||
var cut = Context.RenderComponent<Split>(pb =>
|
||||
{
|
||||
pb.Add(b => b.FirstPaneTemplate, RenderSplitView("I am Pane1"));
|
||||
pb.Add(b => b.SecondPaneTemplate, RenderSplitView("I am Pane2"));
|
||||
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 cut.InvokeAsync(() => cut.Instance.TriggerOnCollapsed(true));
|
||||
Assert.True(state);
|
||||
await cut.InvokeAsync(() => cut.Instance.TriggerOnCollapsed(false));
|
||||
Assert.False(state);
|
||||
Assert.Null(state);
|
||||
await cut.InvokeAsync(() => cut.Instance.TriggerOnResize("0%"));
|
||||
Assert.NotNull(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 =>
|
||||
|
Loading…
Reference in New Issue
Block a user