diff --git a/CHANGELOG.md b/CHANGELOG.md index e8f31fd..baf239b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to ShellDocs land here. Format follows [Keep a Changelog](ht ## [Unreleased] +## [0.1.7-alpha] — 2026-09-06 + +Fixes the interactivity gap the `0.1.5-alpha` static-prerender pipeline opened up. Four chrome interactions (sidebar section expand/collapse, package selector dropdown, TableOfContents scroll-spy, PreviewFrame source view) were written as ordinary interactive Razor components with `@onclick` handlers that mutate `[Parameter] bool` state and re-render via `StateHasChanged()`. Prerendered HTML captured only the initial state; on a static host with no Blazor runtime, every one of those interactions was dead on the deployed site. + +The `0.1.5-alpha` CHANGELOG glossed this as "SignalR-backed component state doesn't survive the static build — but a docs site doesn't need it." That was wrong. A docs site's primary navigation surface is the sidebar; if you can't expand a section, you can't reach the pages under it. + +### Fixed + +- **Sidebar section expand/collapse works on static-hosted builds.** `DocsSidebarNode.razor` no longer routes clicks through Blazor's `@onclick="Toggle"` + `_isOpen` state. Instead, `shelldocs.js` attaches a delegated `click` listener on `.sidebar-section-toggle` and flips `[data-open]` on the ancestor `.sidebar-section` and its `.sidebar-section-shell` child. The initial `[data-open]` value (from `OnParametersSet`'s active-path check) still comes from server rendering — so the ancestor of the current page pre-expands correctly on first paint. CSS unchanged; it already selected on `[data-open]` for both the chevron rotate and the grid-rows animation. +- **Package selector dropdown opens/closes on static-hosted builds.** `PackageSelector.razor` always renders the `.pkg-menu` now (previously conditional on `_open`); CSS hides it under `display: none` unless `.pkg[data-open="true"]`. Delegated JS handler on `.pkg-trigger` flips `[data-open]`; outside-click closes any open menu. Options are plain `` — native navigation, no Blazor round-trip. +- **TableOfContents scroll-spy attaches on static-hosted builds.** Removed the `OnAfterRenderAsync` → `shelldocsToc.attach` invocation (which only fires with a live Blazor runtime). TOC list now emits `[data-toc-list]` + `[data-toc-ids="id1,id2,..."]`; `shelldocs.js` scans for these on `DOMContentLoaded` and after Blazor `enhancedload` and calls `shelldocsToc.attach` itself. Anchor click uses native `href="#id"` navigation. +- **`PreviewFrame` and `ComponentPreview` source-view expand/collapse work on static-hosted builds.** Removed `@onclick="Expand"` / `Collapse` / `Show` / `Hide` and the `_expanded` / `_showSource` state fields. Buttons carry `data-preview-toggle="expand|collapse"`; `shelldocs.js` toggles the same `.expanded` / `.collapsed` classes the Blazor state used to toggle. The copy button follows the same pattern (`[data-preview-copy]`). + +### Notes + +Every one of these interactions still works under a live Blazor runtime — the DOM emits the same initial state Blazor's rendering produced; JS mutations happen on top. Delegated document-level click listeners survive Blazor's enhanced-nav DOM swap without re-attaching; only the TOC scroll-spy needs re-init on `enhancedload` because heading IDs change per page. + +### Removed + +- `DocsSidebarNode.Toggle()` method — click handling is JS-side now. +- `PackageSelector._open` state, `Toggle()`, `Choose()`, `OnBlur()` — same reason; navigation is plain anchor. +- `TableOfContents._handle` / `_sig` / `Scroll()` and the `IJSObjectReference` / `IAsyncDisposable` machinery — scroll-spy attaches from JS, scroll-to uses native anchor. +- `PreviewFrame._expanded` / `_copied` / `_highlighted` / `_codeEl` / `Expand()` / `Collapse()` / `Copy()` — all JS-side. +- `ComponentPreview._showSource` / `_copied` / `_highlighted` / `_sourceEl` / `Show()` / `Hide()` / `Copy()` — same. + ## [0.1.6-alpha] — 2026-08-22 Three authoring / SEO features that stack together to make writing per-component docs and shipping a public site substantially less manual. diff --git a/Directory.Build.props b/Directory.Build.props index aaee843..1448996 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,7 +18,7 @@ - 0.1.6-alpha + 0.1.7-alpha ShellUI ShellUI Copyright © 2026 ShellUI diff --git a/src/ShellDocs.Components/Chrome/DocsSidebarNode.razor b/src/ShellDocs.Components/Chrome/DocsSidebarNode.razor index afbd4f3..e57492b 100644 --- a/src/ShellDocs.Components/Chrome/DocsSidebarNode.razor +++ b/src/ShellDocs.Components/Chrome/DocsSidebarNode.razor @@ -11,7 +11,10 @@ else if (Node.Kind == NodeKind.Section) { @if (_isToggleable) { - @@ -92,7 +95,8 @@ else _lastPath = CurrentPath; } - private void Toggle() => _isOpen = !_isOpen; + // Toggle handled by shelldocs.js — kept as a no-op just in case the + // Blazor lifecycle re-renders and needs the initial state stable. private static bool ContainsPath(NavigationNode node, string path) { diff --git a/src/ShellDocs.Components/Chrome/PackageSelector.razor b/src/ShellDocs.Components/Chrome/PackageSelector.razor index 3937134..d0c3729 100644 --- a/src/ShellDocs.Components/Chrome/PackageSelector.razor +++ b/src/ShellDocs.Components/Chrome/PackageSelector.razor @@ -1,51 +1,50 @@ @inject NavigationManager Nav @inject ShellDocsOptions Options +@* Dropdown open/close handled by shelldocs.js — delegated click on + .pkg-trigger flips [data-open] on the .pkg root; outside-click closes. + Menu is always rendered; CSS hides it unless [data-open="true"]. *@ @if (Options.Packages.Count > 1) { -
- - @if (_open) - { -
- @foreach (var pkg in Options.Packages) - { - var isSel = pkg.Id == Selected.Id; - - } -
- } +
+ @foreach (var pkg in Options.Packages) + { + var isSel = pkg.Id == Selected.Id; + @* Plain — native navigation, no Blazor round-trip. *@ + + + + + + + + @pkg.Title + @pkg.Description + + @if (isSel) + { + + } + + } +
} @code { - private bool _open; - private const string DefaultIcon = "M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z M3.27 6.96 12 12.01l8.73-5.05 M12 22.08V12"; // Longest RootUrl prefix wins — "/docs/components" beats "/docs" for "/docs/components/callout". @@ -61,14 +60,4 @@ ?? Options.Packages[0]; } } - - private void Toggle() => _open = !_open; - - private void Choose(DocsPackage pkg) - { - _open = false; - Nav.NavigateTo(pkg.RootUrl); - } - - private void OnBlur(FocusEventArgs _) { /* closes via next click cycle */ } } diff --git a/src/ShellDocs.Components/Chrome/PackageSelector.razor.css b/src/ShellDocs.Components/Chrome/PackageSelector.razor.css index e811883..94619e0 100644 --- a/src/ShellDocs.Components/Chrome/PackageSelector.razor.css +++ b/src/ShellDocs.Components/Chrome/PackageSelector.razor.css @@ -69,12 +69,18 @@ box-shadow: 0 12px 32px -8px rgb(0 0 0 / 0.18), 0 4px 12px -4px rgb(0 0 0 / 0.06); padding: 0.35rem; z-index: 30; - display: flex; + /* Always rendered so shelldocs.js can toggle visibility via [data-open] + on the parent .pkg without needing a Blazor re-render. */ + display: none; flex-direction: column; gap: 0.15rem; max-height: 22rem; overflow-y: auto; } +.pkg[data-open="true"] .pkg-menu { display: flex; } + +/* Anchor variant of .pkg-option — same styling as the old - - } - else - { -
- - -
- } +
@_source
+
+ +
+
+ + +
@@ -57,10 +46,6 @@ private Type? _target; private IDictionary? _targetParams; private string? _source; - private bool _showSource; - private bool _copied; - private bool _highlighted; - private ElementReference _sourceEl; protected override void OnParametersSet() { @@ -108,31 +93,4 @@ } return sb.ToString(); } - - private void Show() => _showSource = true; - private void Hide() => _showSource = false; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender && !_highlighted && _source is not null) - { - _highlighted = true; - try { await JS.InvokeVoidAsync("shelldocsHighlightElement", _sourceEl); } catch { } - } - } - - private async Task Copy() - { - if (_source is null) return; - try - { - await JS.InvokeVoidAsync("navigator.clipboard.writeText", _source); - _copied = true; - StateHasChanged(); - await Task.Delay(1400); - _copied = false; - StateHasChanged(); - } - catch { } - } } diff --git a/src/ShellDocs.Components/Content/PreviewFrame.razor b/src/ShellDocs.Components/Content/PreviewFrame.razor index 201fd5d..936ca5c 100644 --- a/src/ShellDocs.Components/Content/PreviewFrame.razor +++ b/src/ShellDocs.Components/Content/PreviewFrame.razor @@ -2,7 +2,10 @@ @attribute [ShellDocsIgnore] @inject MarkdownRenderer Renderer -
+@* Expand/collapse of the source region is handled by shelldocs.js — delegated + click on [data-preview-toggle] toggles the expanded/collapsed class on the + ancestor .preview-frame. Works without a live Blazor runtime. *@ +