This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
BlazorDialog is a Razor Class Library published as the BlazorDialog NuGet package. It provides a dialog/modal component that can be used both declaratively (<Dialog> in markup) and procedurally as an injected service (IBlazorDialogService.ShowDialog(...) / ShowComponentAsDialog(...) that you can await for a result).
- .NET SDK pinned in
global.json— currently8.0.302. All projects targetnet8.0viaDirectory.Build.props(which also enablesNullable,ImplicitUsings, andWarningsAsErrors=Nullable). - The library ships static assets (
wwwroot/blazorDialog.js,wwwroot/styles.css) that must be minified viagulpbefore packing — consumers reference the.min.*files. - Package version is set in
BlazorDialog/BlazorDialog.csproj(<Version>);VersionSuffixis appended when passed via build args.
Build library + minify assets + produce NuGet package to Artifacts/nuget:
./build.ps1 # release build
./build.ps1 -VersionSuffix rc1 # prerelease suffix appended to package versionThe script runs npm install and npm run minify inside BlazorDialog/, then dotnet build BlazorDialog.sln -c Release and dotnet pack. The -RunTests switch exists but is a stub — there is no test suite in this repo.
Just minify the JS/CSS (from inside BlazorDialog/):
npm install
npm run minify # gulp: clean + uglify JS + cssmin, outputs *.min.js / *.min.css
Build & publish the demo app to artifacts/demoapp:
./build-demo-app.ps1Run a test app during development (from repo root):
dotnet run --project TestApps/Server/BlazorDialog.BlazorServerTestApp
dotnet run --project TestApps/Wasm/BlazorDialog.BlazorTestApp.Server
dotnet run --project DemoApp/BlazorDialog.DemoApp/Server
Both paths converge on the same Dialog component; the difference is who owns its lifecycle.
- Declarative — user places
<Dialog Id="myId">in their markup. OnOnInitialized, the component registers itself withIBlazorDialogStoreunder thatId. The user then callsIBlazorDialogService.ShowDialog("myId")(or uses two-way@bind-IsShowing). - Component-as-dialog — user calls
IBlazorDialogService.ShowComponentAsDialog<TResult>(new ComponentAsDialogOptions(typeof(MyComp)) { Parameters = ... }). The service registers aComponentDialogin the store, which firesOnComponentAsDialogsChanged.DialogOutput(which the user must place once in their layout) re-renders and creates aComponentAsDialogContainerthat wraps aDialogaround a<DynamicComponent>for the requested type. The service awaitsRenderTaskCompletionSourcebefore callingShowDialogon the freshly-rendered dialog.
Dialog.ShowInternal() creates a TaskCompletionSource<object?> and returns its Task. Hide(result) calls NotifyDialogHidden which invokes OnDialogHide — an Action<object?> that sets the TCS result. This is why the caller can await a value returned from arbitrary user code inside the dialog. The child component reads/sets that result via the DialogContext<TInput> supplied through the <DialogInputProvider> render fragment (input flows in via a cascading DialogInput value, output flows back through ctx.Dialog.Hide(result)).
AddBlazorDialog() in ServiceCollectionExtensions.cs registers three scoped services: IBlazorDialogStore, IBlazorDialogService, ILocationChangingHandler. Everything is scoped so state does not leak between circuits/WASM app instances.
wwwroot/blazorDialog.js implements two things: keyboard-close (default Escape) and dragging.
For keyboard-close, on Show the Dialog passes a DotNetObjectReference<Dialog> plus the enabled/key config to blazorDialog.registerShownDialog. The JS keeps a stack of open dialogs and only routes the key to the top one via HideFromKeyPress (a [JSInvokable] on Dialog).
For dragging (AllowDragging), OnAfterRenderAsync hands the content-wrapper ElementReference to blazorDialog.enableDragging once the dialog is actually in the dom. The JS resolves a drag handle inside that wrapper (an element with blazor-dialog-drag-handle, else the blazor-dialog-header, skipping elements that belong to a nested dialog), attaches pointer-capture based handlers to it, and moves the wrapper with an inline transform: translate(...) clamped to the viewport. There is no interop back to .NET and no document-level listener — the handlers die with the element when the dialog is hidden, which is also what resets the position on the next show.
No other interop is used — z-index, animation, sizing are all pure CSS driven by the Size/Animation/BaseZIndex parameters mapping to classes in styles.css.
LocationChangingHandler wraps NavigationManager.RegisterLocationChangingHandler and maintains a stack of open dialogs. It registers a single handler on the first open and disposes it when the stack empties. PreventNavigation (default true) can be overridden at runtime via Dialog.ForceAllowNavigation() / ForcePreventNavigation().
Dialog.dialogDepth is set from store.GetVisibleDialogsCount() at show time and added to BaseZIndex. This makes dialog-in-dialog stacking work without user intervention.
BlazorDialog/— the actual library. Public API surface is inIBlazorDialogService,IBlazorDialogStore,ComponentAsDialogOptions,DialogContext<T>, and the razor components inComponents/.BlazorDialog.sln— top-level solution that includes the library + all TestApps (used for manual/interactive verification).TestApps/BlazorDialog.TestAppsCommon/— shared razor pages (IndexCommon.razor,ComponentsAsDialogsCommon.razor,NoServiceCommon.razor,ComponentDialog.razor) that are consumed by both the Server and WASM host apps so behavior can be verified in each render mode without duplicating pages.TestApps/Server/andTestApps/Wasm/— Blazor Server and Blazor WASM (hosted) hosts that referenceTestAppsCommon.DemoApp/— has its ownBlazorDialog.DemoApp.slnand is not part of the main solution. It's the public samples site (deployed to azurewebsites.net) and is built viabuild-demo-app.ps1.- No automated test project exists. Changes are verified by running the TestApps.
When you're modifying the library, remember consumers wire it up as follows — breaking these contracts is a breaking change:
builder.Services.AddBlazorDialog();@using BlazorDialogin_Imports.razor- Place
<DialogOutput/>once inMainLayout.razor(or any central interactive spot) — required forShowComponentAsDialogto have somewhere to render. - Reference
_content/BlazorDialog/styles.min.cssand_content/BlazorDialog/blazorDialog.min.jsfromApp.razor.