diff --git a/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts b/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts index 4798f4b6a4..dc5aca34ac 100644 --- a/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts +++ b/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts @@ -10,11 +10,41 @@ export namespace Microsoft.FluentUI.Blazor.Components.Dialog { } /** - * Hide the fluent-dialog with the given id - * @param id The id of the fluent-dialog to hide - */ + * Hide the fluent-dialog with the given id + * @param id The id of the fluent-dialog to hide + */ export function Hide(id: string): void { const dialog = document.getElementById(id) as any; dialog?.hide(); + FocusOnPreviousActiveElement(id); + } + + /** + * Save the element that was active before the dialog was opened + * @param id + */ + export function DialogToggle_PreviousActiveElement(id: string, newState: string): void { + const dialog = document.getElementById(id) as any; + if (dialog) { + if (newState === 'open') { + dialog.previousActiveElement = document.activeElement; + } + else if (newState === 'closed') { + FocusOnPreviousActiveElement(id); + } + } + } + + /** + * Focus on the element that was active before the dialog was opened + * @param id + */ + export function FocusOnPreviousActiveElement(id: string): void { + const dialog = document.getElementById(id) as any; + if (dialog) { + setTimeout(() => { + dialog.previousActiveElement?.focus(); + }, 25); + } } } diff --git a/src/Core.Scripts/src/FluentUICustomEvents.ts b/src/Core.Scripts/src/FluentUICustomEvents.ts index e9700c9e71..c3eb615c93 100644 --- a/src/Core.Scripts/src/FluentUICustomEvents.ts +++ b/src/Core.Scripts/src/FluentUICustomEvents.ts @@ -1,3 +1,5 @@ +import { Microsoft as FluentDialogFile } from "./Components/Dialog/FluentDialog"; + export namespace Microsoft.FluentUI.Blazor.FluentUICustomEvents { /** @@ -28,6 +30,7 @@ export namespace Microsoft.FluentUI.Blazor.FluentUICustomEvents { blazor.registerCustomEventType('dialogbeforetoggle', { browserEventName: 'beforetoggle', createEventArgs: (event: any) => { + FluentDialogFile.FluentUI.Blazor.Components.Dialog.DialogToggle_PreviousActiveElement(event.target.id, event.detail?.newState ?? event.newState); return { id: event.target.id, type: event.type, @@ -130,7 +133,7 @@ export namespace Microsoft.FluentUI.Blazor.FluentUICustomEvents { newState: event.detail?.newState ?? event.newState, }; } - }); + }); } // [^^^ Add your other custom events before this line ^^^] diff --git a/src/Core/Components/Dialog/FluentDialog.razor.cs b/src/Core/Components/Dialog/FluentDialog.razor.cs index 18e5841ac0..1ed1e3c958 100644 --- a/src/Core/Components/Dialog/FluentDialog.razor.cs +++ b/src/Core/Components/Dialog/FluentDialog.razor.cs @@ -12,7 +12,7 @@ namespace Microsoft.FluentUI.AspNetCore.Components; /// /// The dialog component is a window overlaid on either the primary window or another dialog window. -/// Windows under a modal dialog are inert. +/// Windows under a modal dialog are inert. /// public partial class FluentDialog : FluentComponentBase { @@ -75,11 +75,7 @@ public FluentDialog(LibraryConfiguration configuration) : base(configuration) [Parameter] public EventCallback OnStateChange { get; set; } - /// - /// - /// - /// - /// + /// protected override Task OnAfterRenderAsync(bool firstRender) { if (firstRender && LaunchedFromService) diff --git a/src/Core/Components/Dialog/Services/DialogService.cs b/src/Core/Components/Dialog/Services/DialogService.cs index 8ed4116302..03c1cb55ae 100644 --- a/src/Core/Components/Dialog/Services/DialogService.cs +++ b/src/Core/Components/Dialog/Services/DialogService.cs @@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -13,6 +15,7 @@ namespace Microsoft.FluentUI.AspNetCore.Components; public partial class DialogService : FluentServiceBase, IDialogService { private readonly IServiceProvider _serviceProvider; + private readonly IJSRuntime _jsRuntime; /// /// Initializes a new instance of the class. @@ -25,6 +28,7 @@ public partial class DialogService : FluentServiceBase, IDialog public DialogService(IServiceProvider serviceProvider, IFluentLocalizer? localizer) { _serviceProvider = serviceProvider; + _jsRuntime = serviceProvider.GetRequiredService(); Localizer = localizer ?? FluentLocalizerInternal.Default; } @@ -104,11 +108,11 @@ public virtual async Task ShowDialogAsync([DynamicallyAccessedMemb /// /// /// - internal Task RemoveDialogFromProviderAsync(IDialogInstance? dialog) + internal async Task RemoveDialogFromProviderAsync(IDialogInstance? dialog) { if (dialog is null) { - return Task.CompletedTask; + return; } // Remove the HTML code from the DialogProvider @@ -117,6 +121,7 @@ internal Task RemoveDialogFromProviderAsync(IDialogInstance? dialog) throw new InvalidOperationException($"Failed to remove dialog from DialogProvider: the ID '{dialog.Id}' doesn't exist in the DialogServiceProvider."); } - return ServiceProvider.OnUpdatedAsync.Invoke(dialog); + await ServiceProvider.OnUpdatedAsync.Invoke(dialog); + await _jsRuntime.InvokeVoidAsync("Microsoft.FluentUI.Blazor.Components.Dialog.FocusOnPreviousActiveElement", dialog.Id); } }