From 2591710f095fe3a739408bc17b9fd3fabcf0c49e Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sat, 20 Jun 2026 00:26:52 -0400 Subject: [PATCH] refactor: replace eval dark-mode body-class with TS theme interop helper Extracts setBodyThemeClass into DeepDrftShared.Client/Interop/theme/theme.ts; MainLayout lazy-imports the compiled module and calls it, matching the established knob/parallax IJSObjectReference pattern. DisposeAsync added. --- DeepDrftPublic.Client/Layout/MainLayout.razor | 16 ++++++++++++++-- DeepDrftShared.Client/Interop/theme/theme.ts | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 DeepDrftShared.Client/Interop/theme/theme.ts diff --git a/DeepDrftPublic.Client/Layout/MainLayout.razor b/DeepDrftPublic.Client/Layout/MainLayout.razor index 38dca39..bdfc432 100644 --- a/DeepDrftPublic.Client/Layout/MainLayout.razor +++ b/DeepDrftPublic.Client/Layout/MainLayout.razor @@ -6,6 +6,7 @@ @using Microsoft.AspNetCore.Components @inherits LayoutComponentBase @implements IDisposable +@implements IAsyncDisposable @@ -44,6 +45,7 @@ private bool _isDarkMode = false; private bool? _lastAppliedDarkMode = null; private PersistingComponentStateSubscription _persistingSubscription; + private IJSObjectReference? _themeModule; [Inject] public required PersistentComponentState PersistentState { get; set; } [Inject] public required DarkModeSettings DarkModeSettings { get; set; } @@ -80,8 +82,9 @@ if (firstRender || _isDarkMode != _lastAppliedDarkMode) { _lastAppliedDarkMode = _isDarkMode; - await JS.InvokeVoidAsync("eval", - $"document.body.classList.toggle('deepdrft-theme-dark', {_isDarkMode.ToString().ToLower()})"); + _themeModule ??= await JS.InvokeAsync( + "import", "./_content/DeepDrftShared.Client/js/theme/theme.js"); + await _themeModule.InvokeVoidAsync("setBodyThemeClass", _isDarkMode); } } @@ -99,6 +102,15 @@ _persistingSubscription.Dispose(); } + public async ValueTask DisposeAsync() + { + if (_themeModule != null) + { + try { await _themeModule.DisposeAsync(); } + catch (JSDisconnectedException) { /* circuit torn down */ } + } + } + private void ToggleAudioPlayerMinimized(bool isMinimized) { _audioPlayerClass = isMinimized ? "minimized" : "expanded"; diff --git a/DeepDrftShared.Client/Interop/theme/theme.ts b/DeepDrftShared.Client/Interop/theme/theme.ts new file mode 100644 index 0000000..1566099 --- /dev/null +++ b/DeepDrftShared.Client/Interop/theme/theme.ts @@ -0,0 +1,15 @@ +/** + * theme - body-class helpers for dark-mode theme toggling. + * + * Single Responsibility: apply or remove the deepdrft-theme-dark class on + * document.body so that portaled MudBlazor elements (popovers, menus, selects) + * inherit --deepdrft-popover-surface from body.deepdrft-theme-dark rather than + * from :root only. Popovers portal outside the ThemeWrapperClass div, so only + * a body-level class can reach them. + */ + +/** Toggle the deepdrft-theme-dark class on document.body. + * @param isDark true to add the class, false to remove it. */ +export function setBodyThemeClass(isDark: boolean): void { + document.body.classList.toggle('deepdrft-theme-dark', isDark); +}