diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs index 6b9bf75064..e14d0a851e 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs @@ -6,21 +6,21 @@ internal static class BitAppShellJsRuntimeExtensions { internal static ValueTask BitAppShellInitScroll(this IJSRuntime jsRuntime, ElementReference container, string url) { - return jsRuntime.InvokeVoid("BitBlazorUI.AppShell.initScroll", container, url); + return jsRuntime.FastInvokeVoid("BitBlazorUI.AppShell.initScroll", container, url); } internal static ValueTask BitAppShellLocationChangedScroll(this IJSRuntime jsRuntime, string url) { - return jsRuntime.InvokeVoid("BitBlazorUI.AppShell.locationChangedScroll", url); + return jsRuntime.FastInvokeVoid("BitBlazorUI.AppShell.locationChangedScroll", url); } internal static ValueTask BitAppShellAfterRenderScroll(this IJSRuntime jsRuntime, string url) { - return jsRuntime.InvokeVoid("BitBlazorUI.AppShell.afterRenderScroll", url); + return jsRuntime.FastInvokeVoid("BitBlazorUI.AppShell.afterRenderScroll", url); } internal static ValueTask BitAppShellDisposeScroll(this IJSRuntime jsRuntime) { - return jsRuntime.InvokeVoid("BitBlazorUI.AppShell.disposeScroll"); + return jsRuntime.FastInvokeVoid("BitBlazorUI.AppShell.disposeScroll"); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrolling.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrolling.ts index 417bb8541f..a3e940dc88 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrolling.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrolling.ts @@ -23,7 +23,13 @@ namespace BitBlazorUI { for (const entry of entries) { if (entry.isIntersecting) { observer.unobserve(lastElement); - await dotnetObj.invokeMethodAsync("Load"); + try { + await dotnetObj.invokeMethodAsync("Load"); + } catch (e) { + // Swallow the rejection so it doesn't surface as an unhandled promise rejection. + // The .NET object can already be disposed (component torn down) or the circuit gone. + console.error('BitBlazorUI.InfiniteScrolling.setup:', e); + } } } }, { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs index ecfcd131e9..0557a3c4fe 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs @@ -10,18 +10,18 @@ public static ValueTask BitInfiniteScrollingSetup(this IJSRuntime jsRuntime, decimal? threshold, DotNetObjectReference> dotnetObj) { - return jsRuntime.InvokeVoid("BitBlazorUI.InfiniteScrolling.setup", id, scrollerSelector, rootElement, lastElement, threshold, dotnetObj); + return jsRuntime.FastInvokeVoid("BitBlazorUI.InfiniteScrolling.setup", id, scrollerSelector, rootElement, lastElement, threshold, dotnetObj); } public static ValueTask BitInfiniteScrollingReobserve(this IJSRuntime jsRuntime, string id, ElementReference lastElement) { - return jsRuntime.InvokeVoid("BitBlazorUI.InfiniteScrolling.reobserve", id, lastElement); + return jsRuntime.FastInvokeVoid("BitBlazorUI.InfiniteScrolling.reobserve", id, lastElement); } public static ValueTask BitInfiniteScrollingDispose(this IJSRuntime jsRuntime, string id) { - return jsRuntime.InvokeVoid("BitBlazorUI.InfiniteScrolling.dispose", id); + return jsRuntime.FastInvokeVoid("BitBlazorUI.InfiniteScrolling.dispose", id); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs index de7deccec1..82d88f2a97 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs @@ -369,7 +369,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (_isOpen && _activeIndex >= 0 && _activeIndex != _lastScrolledIndex) { _lastScrolledIndex = _activeIndex; - await _js.BitExtrasScrollOptionIntoView(GetOptionId(_activeIndex)); + await _js.BitExtrasScrollElementIntoView(GetOptionId(_activeIndex)); } await base.OnAfterRenderAsync(firstRender); diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs index 698ff5fb59..575b415d6e 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs @@ -440,11 +440,11 @@ private async Task ToggleScroll(bool isOpen) if (_scrollerElementOnOpen.HasValue) { - _offsetTop = await _js.BitUtilsToggleOverflow(_scrollerElementOnOpen.Value, isOpen); + _offsetTop = await _js.BitUtilsToggleOverflow(_scrollerElementOnOpen.Value, isOpen) ?? 0; } else { - _offsetTop = await _js.BitUtilsToggleOverflow(_scrollerSelectorOnOpen ?? "body", isOpen); + _offsetTop = await _js.BitUtilsToggleOverflow(_scrollerSelectorOnOpen ?? "body", isOpen) ?? 0; } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs index 68a2da41d3..4f78091e41 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs @@ -4,17 +4,17 @@ internal static class ExtrasJsRuntimeExtensions { internal static ValueTask BitExtrasApplyRootClasses(this IJSRuntime jsRuntime, List cssClasses, Dictionary cssVariables) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.applyRootClasses", cssClasses, cssVariables); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Extras.applyRootClasses", cssClasses, cssVariables); } internal static ValueTask BitExtrasGoToTop(this IJSRuntime jsRuntime, ElementReference element, BitScrollBehavior? behavior = null) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.goToTop", element, behavior?.ToString().ToLowerInvariant()); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Extras.goToTop", element, behavior?.ToString().ToLowerInvariant()); } internal static ValueTask BitExtrasScrollBy(this IJSRuntime jsRuntime, ElementReference element, decimal x, decimal y) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.scrollBy", element, x, y); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Extras.scrollBy", element, x, y); } public static ValueTask BitExtrasInitScripts(this IJSRuntime jsRuntime, IEnumerable scripts, bool isModule = false) @@ -29,16 +29,16 @@ public static ValueTask BitExtrasInitStylesheets(this IJSRuntime jsRuntime, IEnu internal static ValueTask BitExtrasSetPreventKeys(this IJSRuntime jsRuntime, ElementReference element, string[] keys) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.setPreventKeys", element, keys); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Extras.setPreventKeys", element, keys); } internal static ValueTask BitExtrasDisposePreventKeys(this IJSRuntime jsRuntime, ElementReference element) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.disposePreventKeys", element); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Extras.disposePreventKeys", element); } - internal static ValueTask BitExtrasScrollOptionIntoView(this IJSRuntime jsRuntime, string optionId) + internal static ValueTask BitExtrasScrollElementIntoView(this IJSRuntime jsRuntime, string elementId) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.scrollOptionIntoView", optionId); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Extras.scrollElementIntoView", elementId); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts index f5f215e628..7d14399878 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts @@ -18,7 +18,7 @@ namespace BitBlazorUI { element.scrollBy(x, y); } - + // Attaches (or updates) a deterministic keydown listener that calls preventDefault // for the provided keys. Unlike Blazor's `@onkeydown:preventDefault` binding -- whose // value is evaluated at render time and therefore only applies to the *next* key event @@ -52,93 +52,299 @@ namespace BitBlazorUI { delete el.bitPreventKeys; } - // Scrolls the option element into the visible area of its scroll container using + // Scrolls the element into the visible area of its scroll container using // 'nearest' so keyboard navigation keeps the active item on screen with minimal movement. - public static scrollOptionIntoView(optionId: string) { - if (!optionId) return; + public static scrollElementIntoView(elementId: string) { + if (!elementId) return; - const element = document.getElementById(optionId); + const element = document.getElementById(elementId); if (!element) return; try { element.scrollIntoView({ block: 'nearest', inline: 'nearest' }); - } catch (e) { console.error('BitBlazorUI.Extras.scrollOptionIntoView:', e); } + } catch (e) { console.error('BitBlazorUI.Extras.scrollElementIntoView:', e); } } - - private static _initScriptsPromises: { [key: string]: Promise } = {}; + public static async initScripts(scripts: string[], isModule: boolean) { - const key = scripts.join('|'); - if (Extras._initScriptsPromises[key] !== undefined) { - return Extras._initScriptsPromises[key]; + // Resolve only when every script has actually executed. Loading is tracked per-url so that + // concurrent callers (e.g. several components, or a re-mount) await the same execution instead + // of a second caller seeing the