Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(Modal): use BootstrapBlazorRootOutlet wrap Modal component #5595

Merged
merged 18 commits into from
Mar 10, 2025
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 @@ -6,4 +6,6 @@

main {
min-height: calc(100vh - var(--bs-header-height));
position: relative;
z-index: 10;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

.main {
padding: var(--bb-main-pading);
position: relative;
z-index: 5;
}

.sidebar-title {
Expand Down
5 changes: 3 additions & 2 deletions src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
}
}

private async Task Show(DialogOption option)
private Task Show(DialogOption option)
{
_onShownAsync = async () =>
{
Expand Down Expand Up @@ -158,7 +158,8 @@ private async Task Show(DialogOption option)

// Add ModalDialog to the container
DialogParameters.Add(parameters, (_isKeyboard, _isBackdrop));
await InvokeAsync(StateHasChanged);
StateHasChanged();
return Task.CompletedTask;
}

private static RenderFragment RenderDialog(int index, Dictionary<string, object> parameter) => builder =>
Expand Down
12 changes: 7 additions & 5 deletions src/BootstrapBlazor/Components/Modal/Modal.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
@inherits BootstrapModuleComponentBase
@attribute [BootstrapModuleAutoLoader(JSObjectReference = true)]

<div @attributes="@AdditionalAttributes" class="@ClassString" tabindex="-1" role="dialog" data-bs-backdrop="@Backdrop" data-bs-keyboard="@KeyboardString" id="@Id">
<CascadingValue Value="this" IsFixed="true">
@ChildContent
</CascadingValue>
</div>
<BootstrapBlazorRootContent>
<div @attributes="@AdditionalAttributes" class="@ClassString" tabindex="-1" role="dialog" data-bs-backdrop="@Backdrop" data-bs-keyboard="@KeyboardString" id="@Id">
<CascadingValue Value="this" IsFixed="true">
@ChildContent
</CascadingValue>
</div>
</BootstrapBlazorRootContent>
57 changes: 29 additions & 28 deletions src/BootstrapBlazor/Components/Modal/Modal.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,68 +8,69 @@
namespace BootstrapBlazor.Components;

/// <summary>
/// Modal 组件
/// Modal component
/// </summary>
public partial class Modal
{
/// <summary>
/// 获得 样式字符串
/// Gets the style string
/// </summary>
private string? ClassString => CssBuilder.Default("modal")
.AddClass("fade", IsFade)
.AddClassFromAttributes(AdditionalAttributes)
.Build();

/// <summary>
/// 获得 ModalDialog 集合
/// Gets the collection of ModalDialog
/// </summary>
protected List<ModalDialog> Dialogs { get; } = new(8);

private readonly ConcurrentDictionary<IComponent, Func<Task>> _shownCallbackCache = [];

/// <summary>
/// 获得/设置 是否后台关闭弹窗 默认 false
/// Gets or sets whether to close the popup in the background, default is false
/// </summary>
[Parameter]
public bool IsBackdrop { get; set; }

/// <summary>
/// 获得/设置 是否开启键盘支持 默认 true 响应键盘 ESC 按键
/// Gets or sets whether to enable keyboard support, default is true to respond to the ESC key
/// </summary>
[Parameter]
public bool IsKeyboard { get; set; } = true;

/// <summary>
/// 获得/设置 是否开启淡入淡出动画 默认为 true 开启动画
/// Gets or sets whether to enable fade in and out animation, default is true to enable animation
/// </summary>
[Parameter]
public bool IsFade { get; set; } = true;

/// <summary>
/// 获得/设置 子组件
/// Gets or sets the child component
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }

/// <summary>
/// 获得/设置 组件已经渲染完毕回调方法
/// Gets or sets the callback method when the component has finished rendering
/// </summary>
[Parameter]
public Func<Modal, Task>? FirstAfterRenderCallbackAsync { get; set; }

/// <summary>
/// 获得/设置 弹窗已显示时回调此方法
/// Gets or sets the callback method when the popup is shown
/// </summary>
[Parameter]
public Func<Task>? OnShownAsync { get; set; }

/// <summary>
/// 获得/设置 关闭弹窗回调委托
/// Gets or sets the callback delegate when the popup is closed
/// </summary>
[Parameter]
public Func<Task>? OnCloseAsync { get; set; }

/// <summary>
/// 获得 后台关闭弹窗设置
/// Gets the background close popup setting
/// </summary>
private string? Backdrop => IsBackdrop ? null : "static";

Expand Down Expand Up @@ -97,7 +98,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, nameof(ShownCallback), nameof(CloseCallback));

/// <summary>
/// 添加对话框方法
/// Method to add a dialog
/// </summary>
/// <param name="dialog"></param>
internal void AddDialog(ModalDialog dialog)
Expand All @@ -107,12 +108,12 @@ internal void AddDialog(ModalDialog dialog)
}

/// <summary>
/// 移除对话框方法
/// Method to remove a dialog
/// </summary>
/// <param name="dialog"></param>
internal void RemoveDialog(ModalDialog dialog)
{
// 移除当前弹窗
// Remove the current popup
Dialogs.Remove(dialog);

if (Dialogs.Count > 0)
Expand All @@ -123,15 +124,15 @@ internal void RemoveDialog(ModalDialog dialog)

private void ResetShownDialog(ModalDialog dialog)
{
// 保证新添加的 Dialog 为当前弹窗
// Ensure the newly added Dialog is the current popup
Dialogs.ForEach(d =>
{
d.IsShown = d == dialog;
});
}

/// <summary>
/// 弹窗已经弹出回调方法 JSInvoke 调用
/// Callback method when the popup has been shown, called by JSInvoke
/// </summary>
/// <returns></returns>
[JSInvokable]
Expand All @@ -149,20 +150,20 @@ public async Task ShownCallback()
}

/// <summary>
/// 弹窗已经关闭回调方法 JSInvoke 调用
/// Callback method when the popup has been closed, called by JSInvoke
/// </summary>
/// <returns></returns>
[JSInvokable]
public async Task CloseCallback()
{
// 移除当前弹窗
// Remove the current popup
var dialog = Dialogs.FirstOrDefault(d => d.IsShown);
if (dialog != null)
{
Dialogs.Remove(dialog);
}

// 多级弹窗支持
// Support for multi-level popups
if (Dialogs.Count > 0)
{
ResetShownDialog(Dialogs.Last());
Expand All @@ -175,7 +176,7 @@ public async Task CloseCallback()
}

/// <summary>
/// 弹窗状态切换方法
/// Method to toggle the popup state
/// </summary>
public async Task Toggle()
{
Expand All @@ -184,7 +185,7 @@ public async Task Toggle()
}

/// <summary>
/// 显示弹窗方法
/// Method to show the popup
/// </summary>
/// <returns></returns>
public async Task Show()
Expand All @@ -194,13 +195,13 @@ public async Task Show()
}

/// <summary>
/// 关闭当前弹窗方法
/// Method to close the current popup
/// </summary>
/// <returns></returns>
public Task Close() => InvokeVoidAsync("execute", Id, "hide");

/// <summary>
/// 设置 Header 文字方法
/// Method to set the header text
/// </summary>
/// <param name="text"></param>
public void SetHeaderText(string text)
Expand All @@ -210,19 +211,19 @@ public void SetHeaderText(string text)
}

/// <summary>
/// 注册弹窗显示后回调方法,供代码调用等效 OnShownAsync 参数赋值
/// Registers a callback method to be called after the popup is shown, equivalent to setting the OnShownAsync parameter
/// </summary>
/// <param name="component">组件</param>
/// <param name="value">回调方法</param>
/// <param name="component">Component</param>
/// <param name="value">Callback method</param>
public void RegisterShownCallback(IComponent component, Func<Task> value)
{
_shownCallbackCache.AddOrUpdate(component, _ => value, (_, _) => value);
}

/// <summary>
/// 取消注册窗口显示后回调方法
/// Unregisters the callback method to be called after the popup is shown
/// </summary>
/// <param name="component">组件</param>
/// <param name="component">Component</param>
public void UnRegisterShownCallback(IComponent component)
{
_shownCallbackCache.TryRemove(component, out _);
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/Components/Modal/ModalDialog.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@inherits BootstrapModuleComponentBase
@attribute [BootstrapModuleAutoLoader("Modal/ModalDialog.razor.js", JSObjectReference = true)]

<div class="@ClassName" id="@Id">
<div @attributes="AdditionalAttributes" class="@ClassName" id="@Id">
<div class="modal-content">
@if (ShowHeader)
{
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor/Components/Modal/ModalDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public partial class ModalDialog : IHandlerException
.AddClass("is-draggable-center", IsCentered && IsDraggable && _firstRender)
.AddClass("d-none", !IsShown)
.AddClass(Class, !string.IsNullOrEmpty(Class))
.AddClassFromAttributes(AdditionalAttributes)
.Build();

/// <summary>
Expand Down
8 changes: 3 additions & 5 deletions src/BootstrapBlazor/Components/SweetAlert/SweetAlert.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
@inherits BootstrapComponentBase
@inject SwalService Swal

<div class="swal">
<Modal @ref="ModalContainer" IsKeyboard="false" OnCloseAsync="OnCloseAsync">
@RenderDialog()
</Modal>
</div>
<Modal @ref="ModalContainer" IsKeyboard="false" OnCloseAsync="OnCloseAsync" class="swal">
@RenderDialog()
</Modal>
28 changes: 14 additions & 14 deletions src/BootstrapBlazor/Components/SweetAlert/SweetAlert.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
namespace BootstrapBlazor.Components;

/// <summary>
/// SweetAlert 组件
/// SweetAlert component
/// </summary>
public partial class SweetAlert : IAsyncDisposable
{
/// <summary>
/// 获得/设置 Modal 容器组件实例
/// Gets or sets the Modal container component instance
/// </summary>
[NotNull]
private Modal? ModalContainer { get; set; }

/// <summary>
/// DialogServices 服务实例
/// Gets or sets the DialogServices service instance
/// </summary>
[Inject]
[NotNull]
Expand Down Expand Up @@ -46,10 +46,10 @@ protected override void OnInitialized()
{
base.OnInitialized();

// 注册 Swal 弹窗事件
// Register Swal popup event
SwalService.Register(this, Show);

// 设置 OnCloseAsync 回调方法
// Set OnCloseAsync callback method
OnCloseAsync = () =>
{
IsShowDialog = false;
Expand Down Expand Up @@ -78,10 +78,10 @@ protected override async Task OnAfterRenderAsync(bool firstRender)

if (IsShowDialog)
{
// 打开弹窗
// Open popup
await ModalContainer.Show();

// 自动关闭处理逻辑
// Auto close handling logic
if (AutoHideCheck())
{
try
Expand Down Expand Up @@ -112,7 +112,7 @@ private async Task Show(SwalOption option)
{
if (!IsShowDialog)
{
// 保证仅打开一个弹窗
// Ensure only one popup is opened
IsShowDialog = true;

IsAutoHide = option.IsAutoHide;
Expand All @@ -131,7 +131,7 @@ private async Task Show(SwalOption option)

OnCloseCallbackAsync = AutoHideCheck() ? option.OnCloseAsync : null;

// 渲染 UI 准备弹窗 Dialog
// Render UI to prepare popup Dialog
await InvokeAsync(StateHasChanged);
}
}
Expand All @@ -151,7 +151,7 @@ private RenderFragment RenderDialog() => builder =>
private bool disposed;

/// <summary>
/// Dispose 方法
/// Dispose method
/// </summary>
/// <param name="disposing"></param>
protected virtual async ValueTask DisposeAsync(bool disposing)
Expand All @@ -162,22 +162,22 @@ protected virtual async ValueTask DisposeAsync(bool disposing)

if (IsShowDialog)
{
// 关闭弹窗
// Close popup
DelayToken.Cancel();
await ModalContainer.Close();
IsShowDialog = false;
}

// 释放 Token
// Release Token
DelayToken.Dispose();

// 注销服务
// Unregister service
SwalService.UnRegister(this);
}
}

/// <summary>
/// Dispose 方法
/// <inheritdoc/>
/// </summary>
public async ValueTask DisposeAsync()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
.swal {
position: fixed;
--bb-swal-zindex: 1075;
z-index: var(--bb-swal-zindex);
}

.swal2-header {
display: flex;
flex-direction: column;
Expand Down
Loading