From f252066d2188a594a3fefd34a08c3ed27e932929 Mon Sep 17 00:00:00 2001 From: msynk Date: Sun, 7 Jun 2026 08:15:40 +0330 Subject: [PATCH 01/18] use fast invoke js extension methods in sync calls #9917 --- .../BitAppShellJsRuntimeExtensions.cs | 8 +- .../Components/Chart/BitChart.ts | 2 +- .../Chart/JsInterop/BitChartJsInterop.cs | 6 +- .../BitDataGridJsRuntimeExtensions.cs | 4 +- ...BitInfiniteScrollingJsRuntimeExtensions.cs | 6 +- .../BitPdfReaderJsRuntimeExtensions.cs | 2 +- .../JsInterop/ExtrasJsRuntimeExtensions.cs | 6 +- ...itCircularTimePickerJsRuntimeExtensions.cs | 4 +- .../BitColorPickerJsRuntimeExtensions.cs | 4 +- .../BitFileUploadJsRuntimeExtensions.cs | 16 +- .../BitOtpInputJsRuntimeExtensions.cs | 4 +- .../BitSearchBoxJsRuntimeExtensions.cs | 2 +- .../Swiper/BitSwiperJsRuntimeExtensions.cs | 6 +- .../Modal/BitModalJsRuntimeExtensions.cs | 4 +- .../Surfaces/Splitter/BitSplitter.ts | 4 +- .../BitSplitterJsRuntimeExtensions.cs | 14 +- .../PullToRefresh/BitPullToRefresh.ts | 3 +- .../BitPullToRefreshJsRuntimeExtensions.cs | 8 +- .../JsInterop/UtilsJsRuntimeExtensions.cs | 4 +- .../IJSRuntimeFastExtensionsTests.cs | 145 ++++++++++++++++++ 20 files changed, 199 insertions(+), 53 deletions(-) create mode 100644 src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs 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/Chart/BitChart.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts index f4eb9ac8e9..31096398ac 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts @@ -54,7 +54,7 @@ namespace BitBlazorUI { public static updateChart(config: BitChartConfiguration): boolean { if (!BitChart._bitCharts.has(config.canvasId)) - throw `Could not find a chart with the given id. ${config.canvasId}`; + throw `Could not find a chart with the given id: ${config.canvasId}`; let myChart = BitChart._bitCharts.get(config.canvasId); diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs index 391b21674f..84366606b5 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs @@ -22,7 +22,7 @@ internal static class BitChartJsInterop public static ValueTask BitChartJsRemoveChart(this IJSRuntime jsRuntime, string? canvasId) { - return jsRuntime.InvokeVoid("BitBlazorUI.BitChart.removeChart", canvasId); + return jsRuntime.FastInvokeVoid("BitBlazorUI.BitChart.removeChart", canvasId); } /// @@ -35,7 +35,7 @@ public static ValueTask BitChartJsSetupChart(this IJSRuntime jsRuntime, Bi { var dynParam = StripNulls(chartConfig); Dictionary param = ConvertExpandoObjectToDictionary(dynParam!); - return jsRuntime.Invoke("BitBlazorUI.BitChart.setupChart", param); + return jsRuntime.FastInvoke("BitBlazorUI.BitChart.setupChart", param); } /// @@ -48,7 +48,7 @@ public static ValueTask BitChartJsUpdateChart(this IJSRuntime jsRuntime, B { var dynParam = StripNulls(chartConfig); var param = ConvertExpandoObjectToDictionary(dynParam!); - return jsRuntime.Invoke("BitBlazorUI.BitChart.updateChart", param); + return jsRuntime.FastInvoke("BitBlazorUI.BitChart.updateChart", param); } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs index 684418b1a8..a033900d04 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs @@ -4,11 +4,11 @@ internal static class BitDataGridJsRuntimeExtensions { public static async ValueTask BitDataGridInit(this IJSRuntime jsRuntime, ElementReference tableElement) { - return await jsRuntime.Invoke("BitBlazorUI.DataGrid.init", tableElement); + return await jsRuntime.FastInvoke("BitBlazorUI.DataGrid.init", tableElement); } public static async ValueTask BitDataGridCheckColumnOptionsPosition(this IJSRuntime jsRuntime, ElementReference tableElement) { - await jsRuntime.InvokeVoid("BitBlazorUI.DataGrid.checkColumnOptionsPosition", tableElement); + await jsRuntime.FastInvokeVoid("BitBlazorUI.DataGrid.checkColumnOptionsPosition", tableElement); } } 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/PdfReader/BitPdfReaderJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs index f1e7c1eff2..62606fda8f 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs @@ -19,6 +19,6 @@ public static ValueTask BitPdfReaderRefreshPage(this IJSRuntime jsRuntime, BitPd public static ValueTask BitPdfReaderDispose(this IJSRuntime jsRuntime, string id) { - return jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.dispose", id); + return jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.dispose", id); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs index f359e90322..1e3ae12d2e 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) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs index f6da110208..3d4661d614 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs @@ -4,11 +4,11 @@ internal static class BitCircularTimePickerJsRuntimeExtensions { internal static ValueTask BitCircularTimePickerSetup(this IJSRuntime js, DotNetObjectReference obj, string pointerUpHandler, string pointerMoveHandler) { - return js.Invoke("BitBlazorUI.CircularTimePicker.setup", obj, pointerUpHandler, pointerMoveHandler); + return js.FastInvoke("BitBlazorUI.CircularTimePicker.setup", obj, pointerUpHandler, pointerMoveHandler); } internal static ValueTask BitCircularTimePickerDispose(this IJSRuntime jSRuntime, string? abortControllerId) { - return jSRuntime.InvokeVoid("BitBlazorUI.CircularTimePicker.dispose", abortControllerId); + return jSRuntime.FastInvokeVoid("BitBlazorUI.CircularTimePicker.dispose", abortControllerId); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs index 232e258bfe..df2641fc34 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs @@ -4,11 +4,11 @@ internal static class BitColorPickerJsRuntimeExtensions { internal static ValueTask BitColorPickerSetup(this IJSRuntime js, DotNetObjectReference obj, string pointerUpHandler, string pointerMoveHandler) { - return js.Invoke("BitBlazorUI.ColorPicker.setup", obj, pointerUpHandler, pointerMoveHandler); + return js.FastInvoke("BitBlazorUI.ColorPicker.setup", obj, pointerUpHandler, pointerMoveHandler); } internal static ValueTask BitColorPickerDispose(this IJSRuntime jSRuntime, string? abortControllerId) { - return jSRuntime.InvokeVoid("BitBlazorUI.ColorPicker.dispose", abortControllerId); + return jSRuntime.FastInvokeVoid("BitBlazorUI.ColorPicker.dispose", abortControllerId); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs index eb7de51c30..2ebc09057a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs @@ -13,7 +13,7 @@ internal static ValueTask BitFileUploadSetup(this IJSRuntime jsRu string? uploadAddress, Dictionary? uploadRequestHttpHeaders) { - return jsRuntime.Invoke("BitBlazorUI.FileUpload.setup", id, dotnetObjectReference, element, append, uploadAddress, uploadRequestHttpHeaders); + return jsRuntime.FastInvoke("BitBlazorUI.FileUpload.setup", id, dotnetObjectReference, element, append, uploadAddress, uploadRequestHttpHeaders); } internal static ValueTask BitFileUploadUpload(this IJSRuntime jsRuntime, @@ -24,34 +24,34 @@ internal static ValueTask BitFileUploadUpload(this IJSRuntime jsRuntime, string? uploadUrl, Dictionary? httpHeaders) { - return (httpHeaders is null ? jsRuntime.InvokeVoid("BitBlazorUI.FileUpload.upload", id, from, to, index, uploadUrl) - : jsRuntime.InvokeVoid("BitBlazorUI.FileUpload.upload", id, from, to, index, uploadUrl, httpHeaders)); + return (httpHeaders is null ? jsRuntime.FastInvokeVoid("BitBlazorUI.FileUpload.upload", id, from, to, index, uploadUrl) + : jsRuntime.FastInvokeVoid("BitBlazorUI.FileUpload.upload", id, from, to, index, uploadUrl, httpHeaders)); } internal static ValueTask BitFileUploadPause(this IJSRuntime jsRuntime, string id, int index = -1) { - return jsRuntime.InvokeVoid("BitBlazorUI.FileUpload.pause", id, index); + return jsRuntime.FastInvokeVoid("BitBlazorUI.FileUpload.pause", id, index); } internal static ValueTask BitFileUploadSetupDragDrop(this IJSRuntime jsRuntime, ElementReference dragDropZoneElement, ElementReference inputFileElement) { - return jsRuntime.Invoke("BitBlazorUI.FileUpload.setupDragDrop", dragDropZoneElement, inputFileElement); + return jsRuntime.FastInvoke("BitBlazorUI.FileUpload.setupDragDrop", dragDropZoneElement, inputFileElement); } internal static ValueTask BitFileUploadBrowse(this IJSRuntime jsRuntime, ElementReference inputFileElement) { - return jsRuntime.InvokeVoid("BitBlazorUI.FileUpload.browse", inputFileElement); + return jsRuntime.FastInvokeVoid("BitBlazorUI.FileUpload.browse", inputFileElement); } internal static ValueTask BitFileUploadClear(this IJSRuntime jsRuntime, string id) { - return jsRuntime.InvokeVoid("BitBlazorUI.FileUpload.clear", id); + return jsRuntime.FastInvokeVoid("BitBlazorUI.FileUpload.clear", id); } internal static ValueTask BitFileUploadReset(this IJSRuntime jsRuntime, string id, ElementReference inputFileElement) { - return jsRuntime.InvokeVoid("BitBlazorUI.FileUpload.reset", id, inputFileElement); + return jsRuntime.FastInvokeVoid("BitBlazorUI.FileUpload.reset", id, inputFileElement); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs index f90b5b094b..2c97422ff3 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs @@ -4,11 +4,11 @@ internal static class BitOtpInputJsRuntimeExtensions { internal static ValueTask BitOtpInputSetup(this IJSRuntime jsRuntime, string id, DotNetObjectReference obj, ElementReference input) { - return jsRuntime.InvokeVoid("BitBlazorUI.OtpInput.setup", id, obj, input); + return jsRuntime.FastInvokeVoid("BitBlazorUI.OtpInput.setup", id, obj, input); } internal static ValueTask BitOtpInputDispose(this IJSRuntime jsRuntime, string id) { - return jsRuntime.InvokeVoid("BitBlazorUI.OtpInput.dispose", id); + return jsRuntime.FastInvokeVoid("BitBlazorUI.OtpInput.dispose", id); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs index 2e35b6e09c..0c8565683b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs @@ -4,6 +4,6 @@ internal static class BitSearchBoxJsRuntimeExtensions { internal static ValueTask BitSearchBoxMoveCursorToEnd(this IJSRuntime jsRuntime, ElementReference input) { - return jsRuntime.InvokeVoid("BitBlazorUI.SearchBox.moveCursorToEnd", input); + return jsRuntime.FastInvokeVoid("BitBlazorUI.SearchBox.moveCursorToEnd", input); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs index c47f332b0e..5694ad2944 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs @@ -4,16 +4,16 @@ internal static class BitSwiperJsRuntimeExtensions { internal static ValueTask BitSwiperSetup(this IJSRuntime jsRuntime, string id, ElementReference element, DotNetObjectReference dotnetObj) { - return jsRuntime.InvokeVoid("BitBlazorUI.Swiper.setup", id, element, dotnetObj); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Swiper.setup", id, element, dotnetObj); } internal static ValueTask BitSwiperGetDimensions(this IJSRuntime jsRuntime, ElementReference root, ElementReference container) { - return jsRuntime.Invoke("BitBlazorUI.Swiper.getDimensions", root, container); + return jsRuntime.FastInvoke("BitBlazorUI.Swiper.getDimensions", root, container); } internal static ValueTask BitSwiperDispose(this IJSRuntime jsRuntime, string id) { - return jsRuntime.InvokeVoid("BitBlazorUI.Swiper.dispose", id); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Swiper.dispose", id); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs index 4bbdb16006..7149e1c251 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs @@ -4,11 +4,11 @@ internal static class BitModalJsRuntimeExtensions { internal static ValueTask BitModalSetupDragDrop(this IJSRuntime js, string id, string dragElementSelector) { - return js.InvokeVoid("BitBlazorUI.Modal.setupDragDrop", id, dragElementSelector); + return js.FastInvokeVoid("BitBlazorUI.Modal.setupDragDrop", id, dragElementSelector); } internal static ValueTask BitModalRemoveDragDrop(this IJSRuntime js, string id, string dragElementSelector) { - return js.InvokeVoid("BitBlazorUI.Modal.removeDragDrop", id, dragElementSelector); + return js.FastInvokeVoid("BitBlazorUI.Modal.removeDragDrop", id, dragElementSelector); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts index 7ef14df21e..4b8e2c56e0 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts @@ -24,7 +24,9 @@ element.style.height = height + 'px'; }; - public static resetPaneDimensions(element: HTMLElement) { + public static resetPaneDimensions(element: HTMLElement | undefined) { + if (!element || !element.style) return; + element.style.width = ''; element.style.height = ''; }; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs index 6dab2ebcd4..103ba5c3d4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs @@ -4,36 +4,36 @@ internal static class BitSplitterJsRuntimeExtensions { internal static ValueTask BitSplitterResetPaneDimensions(this IJSRuntime js, ElementReference element) { - return js.InvokeVoid("BitBlazorUI.Splitter.resetPaneDimensions", element); + return js.FastInvokeVoid("BitBlazorUI.Splitter.resetPaneDimensions", element); } internal static ValueTask BitSplitterGetSplitterWidth(this IJSRuntime js, ElementReference element) { - return js.Invoke("BitBlazorUI.Splitter.getSplitterWidth", element); + return js.FastInvoke("BitBlazorUI.Splitter.getSplitterWidth", element); } internal static ValueTask BitSplitterSetSplitterWidth(this IJSRuntime js, ElementReference element, double value) { - return js.InvokeVoid("BitBlazorUI.Splitter.setSplitterWidth", element, value); + return js.FastInvokeVoid("BitBlazorUI.Splitter.setSplitterWidth", element, value); } internal static ValueTask BitSplitterGetSplitterHeight(this IJSRuntime js, ElementReference element) { - return js.Invoke("BitBlazorUI.Splitter.getSplitterHeight", element); + return js.FastInvoke("BitBlazorUI.Splitter.getSplitterHeight", element); } internal static ValueTask BitSplitterSetSplitterHeight(this IJSRuntime js, ElementReference element, double value) { - return js.InvokeVoid("BitBlazorUI.Splitter.setSplitterHeight", element, value); + return js.FastInvokeVoid("BitBlazorUI.Splitter.setSplitterHeight", element, value); } internal static ValueTask BitSplitterHandleSplitterDragging(this IJSRuntime js, TouchEventArgs e) { - return js.InvokeVoid("BitBlazorUI.Splitter.handleSplitterDragging", e); + return js.FastInvokeVoid("BitBlazorUI.Splitter.handleSplitterDragging", e); } internal static ValueTask BitSplitterHandleSplitterDraggingEnd(this IJSRuntime js) { - return js.InvokeVoid("BitBlazorUI.Splitter.handleSplitterDraggingEnd"); + return js.FastInvokeVoid("BitBlazorUI.Splitter.handleSplitterDraggingEnd"); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts index 5391495c60..58219b1fa4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts @@ -138,7 +138,7 @@ factor: number; margin: number; threshold: number; - dotnetObj: DotNetObject; + dotnetObj: DotNetObject | undefined; disposer: () => void = () => { }; constructor(id: string, @@ -169,6 +169,7 @@ public dispose() { this.disposer(); this.dotnetObj?.dispose(); + this.dotnetObj = undefined; } } } \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs index bdc8410354..6af591fa7d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Bit.BlazorUI; +namespace Bit.BlazorUI; internal static class BitPullToRefreshJsRuntimeExtensions { @@ -16,11 +14,11 @@ internal static ValueTask BitPullToRefreshSetup(this IJSRuntime jsRuntime, int threshold, DotNetObjectReference? dotnetObjectReference) { - return jsRuntime.InvokeVoid("BitBlazorUI.PullToRefresh.setup", id, anchor, loading, scrollerElement, scrollerSelector, trigger, factor, margin , threshold, dotnetObjectReference); + return jsRuntime.FastInvokeVoid("BitBlazorUI.PullToRefresh.setup", id, anchor, loading, scrollerElement, scrollerSelector, trigger, factor, margin, threshold, dotnetObjectReference); } internal static ValueTask BitPullToRefreshDispose(this IJSRuntime jsRuntime, string id) { - return jsRuntime.InvokeVoid("BitBlazorUI.PullToRefresh.dispose", id); + return jsRuntime.FastInvokeVoid("BitBlazorUI.PullToRefresh.dispose", id); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs index b57f48cdf7..5342b26836 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs @@ -46,11 +46,11 @@ internal static ValueTask BitUtilsSetStyle(this IJSRuntime jsRuntime, ElementRef internal static ValueTask BitUtilsToggleOverflow(this IJSRuntime jsRuntime, string scrollerSelector, bool isHidden) { - return jsRuntime.Invoke("BitBlazorUI.Utils.toggleOverflow", scrollerSelector, isHidden); + return jsRuntime.FastInvoke("BitBlazorUI.Utils.toggleOverflow", scrollerSelector, isHidden); } internal static ValueTask BitUtilsToggleOverflow(this IJSRuntime jsRuntime, ElementReference scrollerElement, bool isHidden) { - return jsRuntime.Invoke("BitBlazorUI.Utils.toggleOverflow", scrollerElement, isHidden); + return jsRuntime.FastInvoke("BitBlazorUI.Utils.toggleOverflow", scrollerElement, isHidden); } } diff --git a/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs b/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs new file mode 100644 index 0000000000..8697281fa8 --- /dev/null +++ b/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.JSInterop; +using Microsoft.JSInterop.Infrastructure; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Extensions.JsInterop; + +[TestClass] +public class IJSRuntimeFastExtensionsTests +{ + [TestMethod] + public void FastInvokeVoid_WhenInProcessRuntime_ShouldInvokeSynchronously() + { + var jsRuntime = new FakeInProcessJsRuntime(); + + var task = jsRuntime.FastInvokeVoid("BitBlazorUI.Test.doStuff", "arg1", 2); + + // The in-process path runs synchronously, so the returned task must already be completed. + Assert.IsTrue(task.IsCompleted); + Assert.AreEqual(1, jsRuntime.SyncInvocations.Count); + Assert.AreEqual("BitBlazorUI.Test.doStuff", jsRuntime.SyncInvocations[0].Identifier); + CollectionAssert.AreEqual(new object?[] { "arg1", 2 }, jsRuntime.SyncInvocations[0].Args); + Assert.AreEqual(0, jsRuntime.AsyncInvocations.Count); + } + + [TestMethod] + public async Task FastInvoke_WhenInProcessRuntime_ShouldReturnSynchronousResult() + { + var jsRuntime = new FakeInProcessJsRuntime { ResultFactory = _ => true }; + + var task = jsRuntime.FastInvoke("BitBlazorUI.Test.getStuff", "x"); + + Assert.IsTrue(task.IsCompleted); + Assert.IsTrue(await task); + Assert.AreEqual(1, jsRuntime.SyncInvocations.Count); + Assert.AreEqual("BitBlazorUI.Test.getStuff", jsRuntime.SyncInvocations[0].Identifier); + Assert.AreEqual(0, jsRuntime.AsyncInvocations.Count); + } + + [TestMethod] + public void FastInvokeVoid_WhenInProcessRuntimeThrowsJsonException_ShouldSwallowAndComplete() + { + var jsRuntime = new FakeInProcessJsRuntime { ExceptionFactory = _ => new JsonException("bad json") }; + + var task = jsRuntime.FastInvokeVoid("BitBlazorUI.Test.doStuff"); + + // A JsonException from the in-process runtime is swallowed; the call still completes successfully. + Assert.IsTrue(task.IsCompleted); + Assert.IsFalse(task.IsFaulted); + } + + [TestMethod] + public async Task FastInvoke_WhenInProcessRuntimeThrowsJsonException_ShouldReturnDefault() + { + var jsRuntime = new FakeInProcessJsRuntime { ExceptionFactory = _ => new JsonException("bad json") }; + + var task = jsRuntime.FastInvoke("BitBlazorUI.Test.getStuff"); + + Assert.IsTrue(task.IsCompleted); + Assert.IsFalse(task.IsFaulted); + Assert.AreEqual(default, await task); + } + + [TestMethod] + public void FastInvokeVoid_WhenNotInProcessRuntime_ShouldFallBackToAsync() + { + var jsRuntime = new FakeJsRuntime(); + + _ = jsRuntime.FastInvokeVoid("BitBlazorUI.Test.doStuff", "arg1"); + + Assert.AreEqual(1, jsRuntime.AsyncInvocations.Count); + Assert.AreEqual("BitBlazorUI.Test.doStuff", jsRuntime.AsyncInvocations[0].Identifier); + } + + [TestMethod] + public void FastInvoke_WhenNotInProcessRuntime_ShouldFallBackToAsync() + { + var jsRuntime = new FakeJsRuntime(); + + _ = jsRuntime.FastInvoke("BitBlazorUI.Test.getStuff", "arg1"); + + Assert.AreEqual(1, jsRuntime.AsyncInvocations.Count); + Assert.AreEqual("BitBlazorUI.Test.getStuff", jsRuntime.AsyncInvocations[0].Identifier); + } + + + private record Invocation(string Identifier, object?[]? Args); + + /// + /// A fake that records synchronous invocations and can be + /// configured to return a value or throw, mirroring how the real WebAssembly runtime behaves. + /// + private sealed class FakeInProcessJsRuntime : IJSInProcessRuntime + { + public List SyncInvocations { get; } = []; + public List AsyncInvocations { get; } = []; + public Func? ResultFactory { get; set; } + public Func? ExceptionFactory { get; set; } + + public TResult Invoke(string identifier, params object?[]? args) + { + SyncInvocations.Add(new Invocation(identifier, args)); + + if (ExceptionFactory is not null) throw ExceptionFactory(identifier); + + return ResultFactory is null ? default! : (TResult)ResultFactory(identifier)!; + } + + public ValueTask InvokeAsync(string identifier, object?[]? args) + { + AsyncInvocations.Add(new Invocation(identifier, args)); + return new ValueTask(default(TValue)!); + } + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) + { + AsyncInvocations.Add(new Invocation(identifier, args)); + return new ValueTask(default(TValue)!); + } + } + + /// + /// A fake that is NOT in-process, used to verify the async fallback path. + /// + private sealed class FakeJsRuntime : IJSRuntime + { + public List AsyncInvocations { get; } = []; + + public ValueTask InvokeAsync(string identifier, object?[]? args) + { + AsyncInvocations.Add(new Invocation(identifier, args)); + return new ValueTask(default(TValue)!); + } + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) + { + AsyncInvocations.Add(new Invocation(identifier, args)); + return new ValueTask(default(TValue)!); + } + } +} From a37c91ea5005b88a5acb8bec0d80fcaa1d449a81 Mon Sep 17 00:00:00 2001 From: msynk Date: Sun, 7 Jun 2026 09:50:57 +0330 Subject: [PATCH 02/18] resolve review comments --- .../PdfReader/BitPdfReaderJsRuntimeExtensions.cs | 4 ++-- .../Components/Surfaces/Splitter/BitSplitter.ts | 2 +- .../BitPullToRefreshJsRuntimeExtensions.cs | 2 +- .../JsInterop/UtilsJsRuntimeExtensions.cs | 14 +++++++------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs index 62606fda8f..14662a7694 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs @@ -9,12 +9,12 @@ public static ValueTask BitPdfReaderSetup(this IJSRuntime jsRuntime, BitPdf public static ValueTask BitPdfReaderRenderPage(this IJSRuntime jsRuntime, string id, int pageNumber) { - return jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.renderPage", id, pageNumber); + return jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.renderPage", id, pageNumber); } public static ValueTask BitPdfReaderRefreshPage(this IJSRuntime jsRuntime, BitPdfReaderConfig config, int pageNumber) { - return jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.refreshPage", config, pageNumber); + return jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.refreshPage", config, pageNumber); } public static ValueTask BitPdfReaderDispose(this IJSRuntime jsRuntime, string id) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts index 4b8e2c56e0..3352ab52cc 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts @@ -25,7 +25,7 @@ }; public static resetPaneDimensions(element: HTMLElement | undefined) { - if (!element || !element.style) return; + if (!element) return; element.style.width = ''; element.style.height = ''; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs index 6af591fa7d..00f847c36a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs @@ -12,7 +12,7 @@ internal static ValueTask BitPullToRefreshSetup(this IJSRuntime jsRuntime, decimal factor, int margin, int threshold, - DotNetObjectReference? dotnetObjectReference) + DotNetObjectReference dotnetObjectReference) { return jsRuntime.FastInvokeVoid("BitBlazorUI.PullToRefresh.setup", id, anchor, loading, scrollerElement, scrollerSelector, trigger, factor, margin, threshold, dotnetObjectReference); } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs index 5342b26836..02c41818d5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs @@ -4,43 +4,43 @@ internal static class UtilsJsRuntimeExtensions { internal static ValueTask BitUtilsGetBodyWidth(this IJSRuntime jsRuntime) { - return jsRuntime.Invoke("BitBlazorUI.Utils.getBodyWidth"); + return jsRuntime.FastInvoke("BitBlazorUI.Utils.getBodyWidth"); } internal static ValueTask BitUtilsSetProperty(this IJSRuntime jsRuntime, ElementReference element, string property, object? value) { - return jsRuntime.InvokeVoid("BitBlazorUI.Utils.setProperty", element, property, value); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Utils.setProperty", element, property, value); } internal static ValueTask BitUtilsGetProperty(this IJSRuntime jsRuntime, ElementReference element, string property) { - return jsRuntime.Invoke("BitBlazorUI.Utils.getProperty", element, property); + return jsRuntime.FastInvoke("BitBlazorUI.Utils.getProperty", element, property); } internal static ValueTask BitUtilsGetBoundingClientRect(this IJSRuntime jsRuntime, ElementReference element) { - return jsRuntime.Invoke("BitBlazorUI.Utils.getBoundingClientRect", element); + return jsRuntime.FastInvoke("BitBlazorUI.Utils.getBoundingClientRect", element); } internal static ValueTask BitUtilsScrollElementIntoView(this IJSRuntime jsRuntime, string targetElementId) { - return jsRuntime.InvokeVoid("BitBlazorUI.Utils.scrollElementIntoView", targetElementId); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Utils.scrollElementIntoView", targetElementId); } internal static ValueTask BitUtilsSelectText(this IJSRuntime jsRuntime, ElementReference element) { - return jsRuntime.InvokeVoid("BitBlazorUI.Utils.selectText", element); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Utils.selectText", element); } internal static ValueTask BitUtilsSetStyle(this IJSRuntime jsRuntime, ElementReference element, string key, string value) { - return jsRuntime.InvokeVoid("BitBlazorUI.Utils.setStyle", element, key, value); + return jsRuntime.FastInvokeVoid("BitBlazorUI.Utils.setStyle", element, key, value); } From 66ddcee1f4322216737c5e6b69c7014ef81b586f Mon Sep 17 00:00:00 2001 From: msynk Date: Sun, 7 Jun 2026 10:26:42 +0330 Subject: [PATCH 03/18] resolve review comments II --- .../Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs | 2 ++ .../Components/Inputs/ColorPicker/BitColorPicker.razor.cs | 1 + .../Components/Lists/Carousel/BitCarousel.razor.cs | 1 + .../Extensions/JsInterop/UtilsJsRuntimeExtensions.cs | 4 ++-- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs index da69893484..157c37bbc8 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs @@ -611,6 +611,8 @@ private async Task UpdateTime(MouseEventArgs e) if (IsEnabled is false || ReadOnly || InvalidValueBinding()) return; var rect = await _js.BitUtilsGetBoundingClientRect(_clockRef); + if (rect is null) return; + var radius = rect.Width / 2; var centerX = radius; var centerY = radius; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs index 638e91e4c4..4f9a06153b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs @@ -165,6 +165,7 @@ private async Task UpdateColor(MouseEventArgs e) if (ColorHasBeenSet && ColorChanged.HasDelegate is false) return; var pickerRect = await _js.BitUtilsGetBoundingClientRect(_saturationPickerRef); + if (pickerRect is null) return; var left = e.ClientX < pickerRect.Left ? 0 diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs index f68593940a..e08327ddd6 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs @@ -293,6 +293,7 @@ private async Task ResetDimensionsAsync() var itemsCount = _allItems.Count; var rect = await _js.BitUtilsGetBoundingClientRect(_carouselContainer); if (rect is null) return; + var sign = Dir == BitDir.Rtl ? -1 : 1; for (int i = 0; i < itemsCount; i++) { diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs index 02c41818d5..dbbc5df86e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs @@ -20,9 +20,9 @@ internal static ValueTask BitUtilsGetProperty(this IJSRuntime jsRuntime, } - internal static ValueTask BitUtilsGetBoundingClientRect(this IJSRuntime jsRuntime, ElementReference element) + internal static ValueTask BitUtilsGetBoundingClientRect(this IJSRuntime jsRuntime, ElementReference element) { - return jsRuntime.FastInvoke("BitBlazorUI.Utils.getBoundingClientRect", element); + return jsRuntime.FastInvoke("BitBlazorUI.Utils.getBoundingClientRect", element); } From 7dc0a74a9ea2412fdfaf000ce305f4bc334b9dba Mon Sep 17 00:00:00 2001 From: msynk Date: Sun, 7 Jun 2026 15:49:44 +0330 Subject: [PATCH 04/18] fix tests --- .../Components/Extras/AppShell/BitAppShellTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs b/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs index 56fc3ef9a1..4a1030a13a 100644 --- a/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs +++ b/src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs @@ -735,8 +735,8 @@ public void BitAppShellShouldCallInitScrollOnFirstRenderWithPersistScroll() [TestMethod] public void BitAppShellShouldDisposeCorrectly() { - Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.initScroll"); - Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.disposeScroll"); + Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.initScroll").SetVoidResult(); + Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.disposeScroll").SetVoidResult(); var component = RenderComponent(parameters => { From e4413eff48f5922b8f67c4d870d5b1475a353395 Mon Sep 17 00:00:00 2001 From: msynk Date: Sun, 7 Jun 2026 16:05:24 +0330 Subject: [PATCH 05/18] fix fast invoke call --- .../PdfReader/BitPdfReaderJsRuntimeExtensions.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs index 14662a7694..d489b10eaa 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs @@ -9,12 +9,17 @@ public static ValueTask BitPdfReaderSetup(this IJSRuntime jsRuntime, BitPdf public static ValueTask BitPdfReaderRenderPage(this IJSRuntime jsRuntime, string id, int pageNumber) { - return jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.renderPage", id, pageNumber); + // The JS renderPage is async (awaits pdf.js page rendering). FastInvoke would use the + // synchronous in-process path in WASM, discarding the returned Promise (fire-and-forget), + // so callers would proceed/raise events before rendering completes and errors would be lost. + return jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.renderPage", id, pageNumber); } public static ValueTask BitPdfReaderRefreshPage(this IJSRuntime jsRuntime, BitPdfReaderConfig config, int pageNumber) { - return jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.refreshPage", config, pageNumber); + // The JS refreshPage is async (awaits renderPage). See BitPdfReaderRenderPage for why + // the asynchronous invocation must be used instead of the synchronous fast-invoke. + return jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.refreshPage", config, pageNumber); } public static ValueTask BitPdfReaderDispose(this IJSRuntime jsRuntime, string id) From 3323b280a6b5fdce7f993d1103c091e8b86ab4fc Mon Sep 17 00:00:00 2001 From: msynk Date: Tue, 9 Jun 2026 06:41:09 +0330 Subject: [PATCH 06/18] resolve local review comments --- .../BitDataGridJsRuntimeExtensions.cs | 4 +- .../Bit.BlazorUI.Extras/Scripts/Extras.ts | 63 ++++--- .../JsInterop/IJSRuntimeExtensions.cs | 38 +++- .../JsInterop/IJSRuntimeFastExtensions.cs | 94 +++++++++- .../JsInterop/FastInvokeSyncContractTests.cs | 165 ++++++++++++++++++ .../IJSRuntimeFastExtensionsTests.cs | 114 ++++++++++++ 6 files changed, 436 insertions(+), 42 deletions(-) create mode 100644 src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs index a033900d04..66fde8a977 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs @@ -2,7 +2,9 @@ internal static class BitDataGridJsRuntimeExtensions { - public static async ValueTask BitDataGridInit(this IJSRuntime jsRuntime, ElementReference tableElement) + // FastInvoke can return null when the in-process (WASM) path swallows a JSON error (it returns + // default), so the contract is nullable. Callers must null-check before using the reference. + public static async ValueTask BitDataGridInit(this IJSRuntime jsRuntime, ElementReference tableElement) { return await jsRuntime.FastInvoke("BitBlazorUI.DataGrid.init", tableElement); } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts index 55601f6233..b9390e7e09 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts @@ -19,42 +19,47 @@ namespace BitBlazorUI { element.scrollBy(x, y); } - 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]; - } - - const allScripts = Array.from(document.scripts).map(s => s.src); - const notAddedScripts = scripts.filter(s => !allScripts.find(as => as.includes(s))); + // 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