|
1 | 1 | @inject NavigationManager Nav |
| 2 | +@inject ShellDocsOptions Options |
2 | 3 |
|
3 | | -<div class="pkg" @onfocusout="OnBlur"> |
4 | | - <button type="button" class="pkg-trigger" @onclick="Toggle" aria-haspopup="listbox" aria-expanded="@_open"> |
5 | | - <span class="pkg-mark"> |
6 | | - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> |
7 | | - <path d="@Selected.IconPath"/> |
| 4 | +@if (Options.Packages.Count > 1) |
| 5 | +{ |
| 6 | + <div class="pkg" @onfocusout="OnBlur"> |
| 7 | + <button type="button" class="pkg-trigger" @onclick="Toggle" aria-haspopup="listbox" aria-expanded="@_open"> |
| 8 | + <span class="pkg-mark"> |
| 9 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> |
| 10 | + <path d="@(Selected.IconPath ?? DefaultIcon)"/> |
| 11 | + </svg> |
| 12 | + </span> |
| 13 | + <span class="pkg-value">@Selected.Title</span> |
| 14 | + <svg class="pkg-chevron @(_open ? "open" : "")" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| 15 | + <polyline points="6 9 12 15 18 9"/> |
8 | 16 | </svg> |
9 | | - </span> |
10 | | - <span class="pkg-value">@Selected.Title</span> |
11 | | - <svg class="pkg-chevron @(_open ? "open" : "")" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
12 | | - <polyline points="6 9 12 15 18 9"/> |
13 | | - </svg> |
14 | | - </button> |
| 17 | + </button> |
15 | 18 |
|
16 | | - @if (_open) |
17 | | - { |
18 | | - <div class="pkg-menu" role="listbox"> |
19 | | - @foreach (var pkg in Packages) |
20 | | - { |
21 | | - var isSel = pkg.Id == Selected.Id; |
22 | | - <button type="button" class="pkg-option @(isSel ? "selected" : "")" role="option" aria-selected="@isSel" @onclick="() => Choose(pkg)"> |
23 | | - <span class="pkg-option-icon"> |
24 | | - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> |
25 | | - <path d="@pkg.IconPath"/> |
26 | | - </svg> |
27 | | - </span> |
28 | | - <span class="pkg-option-body"> |
29 | | - <span class="pkg-option-title">@pkg.Title</span> |
30 | | - <span class="pkg-option-desc">@pkg.Description</span> |
31 | | - </span> |
32 | | - @if (isSel) |
33 | | - { |
34 | | - <svg class="pkg-option-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> |
35 | | - } |
36 | | - </button> |
37 | | - } |
38 | | - </div> |
39 | | - } |
40 | | -</div> |
| 19 | + @if (_open) |
| 20 | + { |
| 21 | + <div class="pkg-menu" role="listbox"> |
| 22 | + @foreach (var pkg in Options.Packages) |
| 23 | + { |
| 24 | + var isSel = pkg.Id == Selected.Id; |
| 25 | + <button type="button" class="pkg-option @(isSel ? "selected" : "")" role="option" aria-selected="@isSel" @onclick="() => Choose(pkg)"> |
| 26 | + <span class="pkg-option-icon"> |
| 27 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"> |
| 28 | + <path d="@(pkg.IconPath ?? DefaultIcon)"/> |
| 29 | + </svg> |
| 30 | + </span> |
| 31 | + <span class="pkg-option-body"> |
| 32 | + <span class="pkg-option-title">@pkg.Title</span> |
| 33 | + <span class="pkg-option-desc">@pkg.Description</span> |
| 34 | + </span> |
| 35 | + @if (isSel) |
| 36 | + { |
| 37 | + <svg class="pkg-option-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> |
| 38 | + } |
| 39 | + </button> |
| 40 | + } |
| 41 | + </div> |
| 42 | + } |
| 43 | + </div> |
| 44 | +} |
41 | 45 |
|
42 | 46 | @code { |
43 | 47 | private bool _open; |
44 | 48 |
|
45 | | - private record Package(string Id, string Title, string Description, string IconPath, string RootUrl); |
| 49 | + 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"; |
46 | 50 |
|
47 | | - private static readonly List<Package> Packages = new() |
| 51 | + // Longest RootUrl prefix wins — "/docs/components" beats "/docs" for "/docs/components/callout". |
| 52 | + private DocsPackage Selected |
48 | 53 | { |
49 | | - new("shelldocs", "ShellDocs", "The docs framework itself.", "M12 2 4 6v6c0 5 3.5 9.5 8 10 4.5-.5 8-5 8-10V6z", "/docs/introduction"), |
50 | | - new("shelldocs.markdown", "ShellDocs.Markdown", "Markdig extensions, slots, MDX.", "M4 4h16v16H4z M4 9h16 M9 4v16", "/docs/markdown"), |
51 | | - new("shelldocs.core", "ShellDocs.Core", "Navigation graph + content model.","M12 2 2 7l10 5 10-5-10-5z M2 17l10 5 10-5 M2 12l10 5 10-5", "/docs/core"), |
52 | | - new("shelldocs.cli", "ShellDocs.CLI", "Scaffold, build, publish.", "m8 6-6 6 6 6 M16 6l6 6-6 6", "/docs/cli"), |
53 | | - new("shelldocs.components","ShellDocs.Components","Layouts, header, sidebar, TOC.","M3 3h7v7H3z M14 3h7v7h-7z M3 14h7v7H3z M14 14h7v7h-7z", "/docs/components") |
54 | | - }; |
55 | | - |
56 | | - private Package Selected => Packages.FirstOrDefault(p => Nav.Uri.Contains(p.RootUrl.Split('/', StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "")) ?? Packages[0]; |
| 54 | + get |
| 55 | + { |
| 56 | + var path = new Uri(Nav.Uri).AbsolutePath; |
| 57 | + return Options.Packages |
| 58 | + .Where(p => path.StartsWith(p.RootUrl, StringComparison.OrdinalIgnoreCase)) |
| 59 | + .OrderByDescending(p => p.RootUrl.Length) |
| 60 | + .FirstOrDefault() |
| 61 | + ?? Options.Packages[0]; |
| 62 | + } |
| 63 | + } |
57 | 64 |
|
58 | 65 | private void Toggle() => _open = !_open; |
59 | 66 |
|
60 | | - private void Choose(Package pkg) |
| 67 | + private void Choose(DocsPackage pkg) |
61 | 68 | { |
62 | 69 | _open = false; |
63 | 70 | Nav.NavigateTo(pkg.RootUrl); |
|
0 commit comments