@takazudo/zudo-doc 5.28.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +49 -16
- package/bin/gen-z-index.mjs +18 -18
- package/dist/agent-docs/identity.d.ts +4 -0
- package/dist/agent-docs/identity.js +24 -0
- package/dist/agent-docs/index.d.ts +5 -0
- package/dist/agent-docs/index.js +5 -0
- package/dist/agent-docs/limits.d.ts +11 -0
- package/dist/agent-docs/limits.js +24 -0
- package/dist/agent-docs/search.d.ts +14 -0
- package/dist/agent-docs/search.js +82 -0
- package/dist/agent-docs/tokenize.d.ts +2 -0
- package/dist/agent-docs/tokenize.js +22 -0
- package/dist/agent-docs/types.d.ts +68 -0
- package/dist/agent-docs/types.js +0 -0
- package/dist/ai-chat-modal/index.d.ts +1 -4
- package/dist/ai-chat-modal/index.js +183 -123
- package/dist/asset-components/asset-card.d.ts +1 -2
- package/dist/asset-components/asset-card.js +12 -12
- package/dist/asset-components/asset-code.d.ts +1 -2
- package/dist/asset-components/asset-code.js +10 -10
- package/dist/asset-components/shared.d.ts +3 -4
- package/dist/asset-components/shared.js +4 -4
- package/dist/asset-index-page/body.d.ts +5 -6
- package/dist/asset-index-page/body.js +6 -6
- package/dist/asset-index-page/index.d.ts +1 -2
- package/dist/asset-index-page/index.js +1 -1
- package/dist/asset-page/body.d.ts +5 -9
- package/dist/asset-page/body.js +3 -3
- package/dist/asset-page/components.d.ts +16 -17
- package/dist/asset-page/components.js +10 -9
- package/dist/asset-page/index.d.ts +1 -2
- package/dist/asset-page/index.js +1 -1
- package/dist/auto-logo/index.d.ts +1 -2
- package/dist/auto-logo/index.js +2 -2
- package/dist/body-foot-util/body-foot-util-area.d.ts +3 -4
- package/dist/body-foot-util/body-foot-util-area.js +1 -2
- package/dist/body-foot-util/edit-link.d.ts +2 -3
- package/dist/body-foot-util/edit-link.js +1 -2
- package/dist/breadcrumb/breadcrumb.d.ts +6 -6
- package/dist/breadcrumb/breadcrumb.js +3 -4
- package/dist/category-nav/index.d.ts +1 -2
- package/dist/category-nav/index.js +11 -5
- package/dist/category-tree-nav/index.d.ts +1 -2
- package/dist/category-tree-nav/index.js +3 -3
- package/dist/chrome/derive.d.ts +18 -27
- package/dist/chrome/derive.js +47 -29
- package/dist/chrome/index.d.ts +5 -5
- package/dist/chrome/index.js +1 -1
- package/dist/chrome/island-data.d.ts +4 -0
- package/dist/chrome/island-data.js +52 -0
- package/dist/chrome/primary-slots.d.ts +2 -3
- package/dist/chrome-bindings.d.ts +28 -22
- package/dist/code-group/index.d.ts +3 -4
- package/dist/code-group/index.js +3 -7
- package/dist/code-syntax/code-block-enhancer.d.ts +1 -2
- package/dist/code-syntax/code-block-enhancer.js +2 -2
- package/dist/code-syntax/mermaid-init.d.ts +1 -2
- package/dist/code-syntax/mermaid-init.js +5 -2
- package/dist/code-syntax/tabs-init-script.js +1 -1
- package/dist/code-syntax/tabs-init.d.ts +1 -2
- package/dist/code-syntax/tabs-init.js +2 -2
- package/dist/code-syntax/tabs.d.ts +6 -6
- package/dist/code-syntax/tabs.js +8 -8
- package/dist/compiled.css +2933 -2943
- package/dist/config-assertions/index.d.ts +13 -0
- package/dist/config-assertions/index.js +14 -0
- package/dist/config.d.ts +36 -17
- package/dist/config.js +23 -95
- package/dist/content/content-blockquote.d.ts +2 -3
- package/dist/content/content-blockquote.js +3 -3
- package/dist/content/content-code.d.ts +3 -4
- package/dist/content/content-code.js +5 -5
- package/dist/content/content-link.d.ts +3 -4
- package/dist/content/content-link.js +10 -10
- package/dist/content/content-ol.d.ts +2 -3
- package/dist/content/content-ol.js +5 -4
- package/dist/content/content-paragraph.d.ts +2 -3
- package/dist/content/content-paragraph.js +1 -1
- package/dist/content/content-strong.d.ts +2 -3
- package/dist/content/content-strong.js +3 -3
- package/dist/content/content-table.d.ts +2 -3
- package/dist/content/content-table.js +4 -4
- package/dist/content/content-ul.d.ts +2 -3
- package/dist/content/content-ul.js +4 -4
- package/dist/content/heading-h2.d.ts +2 -3
- package/dist/content/heading-h2.js +4 -4
- package/dist/content/heading-h3.d.ts +2 -3
- package/dist/content/heading-h3.js +4 -4
- package/dist/content/heading-h4.d.ts +2 -3
- package/dist/content/heading-h4.js +4 -4
- package/dist/content-admonition/index.d.ts +3 -4
- package/dist/content-admonition/index.js +2 -2
- package/dist/content.css +35 -33
- package/dist/design-token-panel-bootstrap-controller.d.ts +178 -0
- package/dist/design-token-panel-bootstrap-controller.js +359 -0
- package/dist/design-token-panel-bootstrap-island.d.ts +5 -0
- package/dist/design-token-panel-bootstrap-island.js +19 -0
- package/dist/design-token-panel-bootstrap.d.ts +5 -195
- package/dist/design-token-panel-bootstrap.js +3 -288
- package/dist/desktop-sidebar-toggle-island/index.d.ts +2 -7
- package/dist/desktop-sidebar-toggle-island/index.js +2 -58
- package/dist/desktop-sidebar-toggle-island/island.d.ts +4 -0
- package/dist/desktop-sidebar-toggle-island/island.js +43 -0
- package/dist/desktop-sidebar-toggle-island/storage.d.ts +4 -0
- package/dist/desktop-sidebar-toggle-island/storage.js +29 -0
- package/dist/desktop-toc-toggle-island/index.d.ts +2 -7
- package/dist/desktop-toc-toggle-island/index.js +2 -58
- package/dist/desktop-toc-toggle-island/island.d.ts +4 -0
- package/dist/desktop-toc-toggle-island/island.js +43 -0
- package/dist/desktop-toc-toggle-island/storage.d.ts +4 -0
- package/dist/desktop-toc-toggle-island/storage.js +29 -0
- package/dist/details/details.d.ts +3 -4
- package/dist/details/details.js +1 -1
- package/dist/doc-body-end/index.d.ts +1 -2
- package/dist/doc-body-end/index.js +1 -1
- package/dist/doc-body-end-islands/design-token-panel-island.d.ts +2 -2
- package/dist/doc-body-end-islands/design-token-panel-island.js +3 -7
- package/dist/doc-body-end-islands/index.d.ts +4 -21
- package/dist/doc-body-end-islands/index.js +5 -3
- package/dist/doc-body-end-islands/theme-pack-switcher-island.d.ts +3 -13
- package/dist/doc-body-end-islands/theme-pack-switcher-island.js +11 -13
- package/dist/doc-content-header/index.d.ts +1 -2
- package/dist/doc-content-header/index.js +1 -1
- package/dist/doc-history/index.d.ts +1 -1
- package/dist/doc-history/index.js +166 -141
- package/dist/doc-history-area/index.d.ts +6 -4
- package/dist/doc-history-area/index.js +17 -16
- package/dist/doc-metainfo-area/index.d.ts +2 -3
- package/dist/doc-metainfo-area/index.js +1 -1
- package/dist/doc-page-renderer/index.d.ts +13 -7
- package/dist/doc-page-renderer/index.js +10 -1
- package/dist/doc-page-shell/index.d.ts +20 -7
- package/dist/doc-page-shell/index.js +17 -6
- package/dist/doc-pager/index.d.ts +1 -2
- package/dist/doc-pager/index.js +3 -3
- package/dist/doc-tags-area/index.d.ts +2 -3
- package/dist/doc-tags-area/index.js +1 -1
- package/dist/doclayout/doc-layout-with-defaults.d.ts +9 -9
- package/dist/doclayout/doc-layout-with-defaults.js +5 -15
- package/dist/doclayout/doc-layout.d.ts +26 -27
- package/dist/doclayout/doc-layout.js +23 -14
- package/dist/docs-schema/index.d.ts +4 -0
- package/dist/docs-schema/index.js +1 -0
- package/dist/extract-headings/index.js +43 -12
- package/dist/factory-context/index.d.ts +22 -19
- package/dist/features.css +70 -8
- package/dist/find-in-page/find-bar.d.ts +4 -2
- package/dist/find-in-page/find-bar.js +58 -69
- package/dist/find-in-page/index.d.ts +2 -8
- package/dist/find-in-page/index.js +33 -30
- package/dist/footer/footer.d.ts +3 -4
- package/dist/footer/footer.js +3 -3
- package/dist/footer-with-defaults/index.d.ts +2 -3
- package/dist/footer-with-defaults/index.js +1 -1
- package/dist/frontmatter-preview-defaults/index.js +1 -0
- package/dist/head/doc-head.d.ts +1 -1
- package/dist/head/doc-head.js +24 -32
- package/dist/head/og-tags.d.ts +1 -1
- package/dist/head/og-tags.js +8 -7
- package/dist/head/twitter-card.d.ts +1 -1
- package/dist/head/twitter-card.js +1 -1
- package/dist/head-with-defaults/index.d.ts +7 -2
- package/dist/head-with-defaults/index.js +46 -32
- package/dist/header/header.d.ts +8 -8
- package/dist/header/header.js +9 -11
- package/dist/header/types.d.ts +7 -7
- package/dist/header-with-defaults/index.d.ts +1 -2
- package/dist/header-with-defaults/index.js +18 -8
- package/dist/home-intro/index.d.ts +2 -5
- package/dist/home-intro/index.js +7 -5
- package/dist/home-intro/prepare.js +4 -3
- package/dist/home-page/index.d.ts +3 -3
- package/dist/home-page/index.js +19 -17
- package/dist/html-preview-wrapper/highlighted-code.d.ts +3 -4
- package/dist/html-preview-wrapper/highlighted-code.js +19 -17
- package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +4 -25
- package/dist/html-preview-wrapper/html-preview-wrapper.js +2 -87
- package/dist/html-preview-wrapper/html-preview.d.ts +3 -10
- package/dist/html-preview-wrapper/html-preview.js +13 -37
- package/dist/html-preview-wrapper/index.d.ts +2 -1
- package/dist/html-preview-wrapper/index.js +1 -1
- package/dist/html-preview-wrapper/preview-base.d.ts +3 -5
- package/dist/html-preview-wrapper/preview-base.js +56 -59
- package/dist/html-preview-wrapper/wrapper.d.ts +20 -0
- package/dist/html-preview-wrapper/wrapper.js +37 -0
- package/dist/hydration-pending.d.ts +3 -5
- package/dist/hydration-pending.js +7 -5
- package/dist/i18n-version/language-switcher.d.ts +5 -32
- package/dist/i18n-version/language-switcher.js +5 -106
- package/dist/i18n-version/switcher-generated-scripts.d.ts +2 -0
- package/dist/i18n-version/switcher-generated-scripts.js +107 -0
- package/dist/i18n-version/switcher-url-state.d.ts +76 -0
- package/dist/i18n-version/switcher-url-state.js +103 -0
- package/dist/i18n-version/version-banner.d.ts +2 -3
- package/dist/i18n-version/version-banner.js +1 -1
- package/dist/i18n-version/version-switcher.d.ts +5 -50
- package/dist/i18n-version/version-switcher.js +5 -157
- package/dist/icons/index.d.ts +18 -19
- package/dist/icons/index.js +17 -33
- package/dist/image-enlarge/index.d.ts +1 -10
- package/dist/image-enlarge/index.js +80 -55
- package/dist/image-enlarge/public.d.ts +2 -0
- package/dist/image-enlarge/public.js +6 -0
- package/dist/image-enlarge/ssr-fallback.d.ts +2 -0
- package/dist/image-enlarge/ssr-fallback.js +8 -0
- package/dist/index.d.ts +11 -4
- package/dist/inline-version-switcher/index.d.ts +1 -2
- package/dist/inline-version-switcher/index.js +1 -1
- package/dist/island-types/index.d.ts +2 -2
- package/dist/island-types/index.js +1 -5
- package/dist/math-block/index.d.ts +4 -7
- package/dist/math-block/index.js +5 -5
- package/dist/mcp/handler.d.ts +8 -0
- package/dist/mcp/handler.js +129 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/loader.d.ts +19 -0
- package/dist/mcp/loader.js +202 -0
- package/dist/mdx-components/index.js +40 -108
- package/dist/mermaid-enlarge/index.d.ts +1 -8
- package/dist/mermaid-enlarge/index.js +174 -158
- package/dist/mermaid-enlarge/public.d.ts +2 -0
- package/dist/mermaid-enlarge/public.js +6 -0
- package/dist/mermaid-enlarge/ssr-fallback.d.ts +2 -0
- package/dist/mermaid-enlarge/ssr-fallback.js +8 -0
- package/dist/metainfo/doc-metainfo.d.ts +2 -3
- package/dist/metainfo/doc-metainfo.js +1 -4
- package/dist/metainfo/doc-tags.d.ts +2 -3
- package/dist/metainfo/doc-tags.js +1 -1
- package/dist/metainfo/frontmatter-preview.d.ts +3 -4
- package/dist/metainfo/frontmatter-preview.js +1 -1
- package/dist/nav-data-prep/index.js +6 -4
- package/dist/nav-indexing/category-nav.d.ts +2 -3
- package/dist/nav-indexing/category-nav.js +1 -2
- package/dist/nav-indexing/category-tree-nav.d.ts +2 -3
- package/dist/nav-indexing/category-tree-nav.js +1 -1
- package/dist/nav-indexing/doc-card-grid.d.ts +2 -3
- package/dist/nav-indexing/doc-card-grid.js +1 -2
- package/dist/nav-indexing/docs-sitemap.d.ts +2 -3
- package/dist/nav-indexing/docs-sitemap.js +2 -2
- package/dist/nav-indexing/index.d.ts +1 -1
- package/dist/nav-indexing/nav-card-grid.d.ts +2 -3
- package/dist/nav-indexing/nav-card-grid.js +1 -2
- package/dist/nav-indexing/note-tray-index-parts/card-list.d.ts +1 -2
- package/dist/nav-indexing/note-tray-index-parts/card-list.js +4 -4
- package/dist/nav-indexing/note-tray-index-parts/date-line.d.ts +2 -3
- package/dist/nav-indexing/note-tray-index-parts/date-line.js +1 -1
- package/dist/nav-indexing/note-tray-index-parts/index-list.d.ts +1 -2
- package/dist/nav-indexing/note-tray-index-parts/index-list.js +2 -2
- package/dist/nav-indexing/note-tray-index-parts/timeline.d.ts +1 -2
- package/dist/nav-indexing/note-tray-index-parts/timeline.js +2 -2
- package/dist/nav-indexing/note-tray-index.d.ts +1 -2
- package/dist/nav-indexing/note-tray-index.js +1 -1
- package/dist/nav-indexing/site-tree-nav-demo.d.ts +2 -3
- package/dist/nav-indexing/site-tree-nav-demo.js +2 -2
- package/dist/nav-indexing/tag-nav.d.ts +1 -2
- package/dist/nav-indexing/tag-nav.js +1 -1
- package/dist/nav-indexing/types.d.ts +2 -2
- package/dist/nav-indexing/versions-page-content.d.ts +2 -3
- package/dist/nav-indexing/versions-page-content.js +8 -8
- package/dist/note-tray-index/index.d.ts +1 -2
- package/dist/note-tray-index/index.js +1 -1
- package/dist/page-loading/page-loading-overlay.d.ts +1 -2
- package/dist/page-loading/page-loading-overlay.js +3 -5
- package/dist/plugins/agent-export.d.ts +3 -0
- package/dist/plugins/agent-export.js +34 -0
- package/dist/plugins/internal/agent-export/chunk.d.ts +5 -0
- package/dist/plugins/internal/agent-export/chunk.js +90 -0
- package/dist/plugins/internal/agent-export/index.d.ts +24 -0
- package/dist/plugins/internal/agent-export/index.js +123 -0
- package/dist/plugins/internal/agent-export/normalize.d.ts +5 -0
- package/dist/plugins/internal/agent-export/normalize.js +73 -0
- package/dist/plugins/internal/llms-txt/dev-middleware.d.ts +6 -0
- package/dist/plugins/internal/llms-txt/dev-middleware.js +8 -3
- package/dist/plugins/internal/llms-txt/emit.js +23 -6
- package/dist/plugins/internal/llms-txt/generate.d.ts +7 -1
- package/dist/plugins/internal/llms-txt/generate.js +9 -2
- package/dist/plugins/internal/llms-txt/load.d.ts +2 -0
- package/dist/plugins/internal/llms-txt/load.js +20 -4
- package/dist/plugins/internal/llms-txt/types.d.ts +10 -0
- package/dist/plugins/routes.js +4 -0
- package/dist/preset.d.ts +19 -9
- package/dist/preset.js +20 -1
- package/dist/route-context-payload/index.js +1 -1
- package/dist/routes/404.d.ts +2 -3
- package/dist/routes/404.js +1 -1
- package/dist/routes/_chrome.d.ts +5 -5
- package/dist/routes/_chrome.js +7 -2
- package/dist/routes/_context.d.ts +4 -4
- package/dist/routes/_design-token-panel-bootstrap.d.ts +1 -4
- package/dist/routes/_design-token-panel-bootstrap.js +9 -1
- package/dist/routes/docs-slug.d.ts +2 -3
- package/dist/routes/docs-tags-index.d.ts +2 -3
- package/dist/routes/docs-tags-index.js +1 -1
- package/dist/routes/docs-tags-tag.d.ts +2 -3
- package/dist/routes/docs-tags-tag.js +1 -1
- package/dist/routes/docs-versions.d.ts +2 -3
- package/dist/routes/docs-versions.js +1 -1
- package/dist/routes/files-path.d.ts +2 -3
- package/dist/routes/files-path.js +1 -1
- package/dist/routes/index.d.ts +2 -3
- package/dist/routes/index.js +1 -1
- package/dist/routes/locale-docs-slug.d.ts +2 -3
- package/dist/routes/locale-docs-tags-index.d.ts +2 -3
- package/dist/routes/locale-docs-tags-index.js +1 -1
- package/dist/routes/locale-docs-tags-tag.d.ts +2 -3
- package/dist/routes/locale-docs-tags-tag.js +1 -1
- package/dist/routes/locale-docs-versions.d.ts +2 -3
- package/dist/routes/locale-docs-versions.js +1 -1
- package/dist/routes/locale-files-path.d.ts +2 -3
- package/dist/routes/locale-files-path.js +1 -1
- package/dist/routes/locale-index.d.ts +2 -3
- package/dist/routes/locale-index.js +1 -1
- package/dist/routes/mcp.d.ts +5 -0
- package/dist/routes/mcp.js +25 -0
- package/dist/routes/v-docs-slug.d.ts +2 -3
- package/dist/routes/v-locale-docs-slug.d.ts +2 -3
- package/dist/search-widget/index.d.ts +1 -2
- package/dist/search-widget/index.js +4 -7
- package/dist/search-widget-script/generated-script.js +12 -5
- package/dist/settings-defaults.d.ts +2 -0
- package/dist/settings-defaults.js +93 -0
- package/dist/settings.d.ts +8 -2
- package/dist/sidebar/index.d.ts +1 -1
- package/dist/sidebar/sidebar.d.ts +12 -44
- package/dist/sidebar/sidebar.js +4 -3
- package/dist/sidebar/types.d.ts +14 -0
- package/dist/sidebar-prepaint/index.d.ts +1 -2
- package/dist/sidebar-prepaint/index.js +4 -6
- package/dist/sidebar-resizer/sidebar-resizer-init.d.ts +2 -3
- package/dist/sidebar-resizer/sidebar-resizer-init.js +3 -3
- package/dist/sidebar-toggle-island/index.d.ts +8 -11
- package/dist/sidebar-toggle-island/index.js +75 -62
- package/dist/sidebar-tree/build-tree.js +5 -6
- package/dist/sidebar-tree/types.d.ts +2 -0
- package/dist/sidebar-tree-island/index.d.ts +4 -19
- package/dist/sidebar-tree-island/index.js +782 -436
- package/dist/sidebar-tree-island/scope-state.d.ts +38 -0
- package/dist/sidebar-tree-island/scope-state.js +151 -0
- package/dist/sidebar-tree-island/sidebar-scroll-preserve.js +6 -2
- package/dist/sidebar-utils/index.d.ts +17 -19
- package/dist/sidebar-utils/index.js +100 -3
- package/dist/sidebar-utils/scope.d.ts +11 -0
- package/dist/sidebar-utils/scope.js +67 -0
- package/dist/sidebar-with-defaults/index.d.ts +1 -2
- package/dist/sidebar-with-defaults/index.js +19 -18
- package/dist/site-schema/nav-tree.js +15 -15
- package/dist/site-schema/types.d.ts +1 -0
- package/dist/site-tree-nav/index.d.ts +2 -3
- package/dist/site-tree-nav/index.js +11 -12
- package/dist/site-tree-nav/normalize-island-props.d.ts +16 -0
- package/dist/site-tree-nav/normalize-island-props.js +122 -0
- package/dist/site-tree-nav-island/index.d.ts +3 -3
- package/dist/site-tree-nav-island/index.js +243 -144
- package/dist/smart-break/index.d.ts +3 -4
- package/dist/smart-break/index.js +1 -1
- package/dist/tab-item/tab-item.d.ts +3 -4
- package/dist/tab-item/tab-item.js +1 -1
- package/dist/tag-pages/index.d.ts +3 -3
- package/dist/tag-pages/index.js +10 -10
- package/dist/theme/color-scheme-provider.d.ts +3 -3
- package/dist/theme/color-scheme-provider.js +3 -3
- package/dist/theme/theme-pack-provider.d.ts +1 -1
- package/dist/theme/theme-pack-provider.js +3 -3
- package/dist/theme/theme-toggle.d.ts +2 -3
- package/dist/theme/theme-toggle.js +1 -1
- package/dist/theme-pack-dialog/index.d.ts +1 -1
- package/dist/theme-pack-dialog/index.js +69 -75
- package/dist/theme-pack-dialog/theme-pack-card.d.ts +3 -4
- package/dist/theme-pack-dialog/theme-pack-card.js +19 -17
- package/dist/theme-pack-switcher/index.d.ts +4 -5
- package/dist/theme-pack-switcher/index.js +52 -49
- package/dist/theme-toggle/index.d.ts +1 -1
- package/dist/theme-toggle/index.js +154 -167
- package/dist/theme.css +47 -121
- package/dist/toc/mobile-toc.d.ts +3 -5
- package/dist/toc/mobile-toc.js +29 -27
- package/dist/toc/toc.d.ts +3 -4
- package/dist/toc/toc.js +18 -18
- package/dist/toc/use-active-heading.d.ts +5 -4
- package/dist/toc/use-active-heading.js +36 -30
- package/dist/toc-prepaint/index.d.ts +1 -2
- package/dist/toc-prepaint/index.js +4 -6
- package/dist/transitions/nested-island-props-refresh.d.ts +3 -16
- package/dist/transitions/nested-island-props-refresh.js +133 -151
- package/dist/transitions/page-events.d.ts +8 -13
- package/dist/tree-nav-shared/index.d.ts +4 -5
- package/dist/tree-nav-shared/index.js +6 -7
- package/dist/use-modal-dialog/index.d.ts +9 -63
- package/dist/use-modal-dialog/index.js +41 -59
- package/dist/versions-page/index.d.ts +3 -3
- package/dist/versions-page/index.js +5 -5
- package/dist/wind/index.d.ts +5 -0
- package/dist/wind/index.js +169 -0
- package/dist/wind.json +498 -0
- package/dist/z-index-defaults/index.d.ts +4 -4
- package/dist/zdtp-loader.d.ts +7 -0
- package/dist/zdtp-loader.js +8 -0
- package/eject/asset-components/asset-card.tsx +14 -16
- package/eject/asset-components/asset-code.tsx +10 -12
- package/eject/asset-components/shared.tsx +5 -7
- package/eject/breadcrumb/breadcrumb.tsx +15 -16
- package/eject/code-group/index.tsx +4 -10
- package/eject/content-admonition/index.tsx +5 -6
- package/eject/desktop-sidebar-toggle-island/index.ts +4 -0
- package/eject/desktop-sidebar-toggle-island/island.tsx +50 -0
- package/eject/desktop-sidebar-toggle-island/storage.ts +27 -0
- package/eject/details/details.tsx +3 -5
- package/eject/doc-history/index.tsx +193 -179
- package/eject/doc-pager/index.tsx +3 -4
- package/eject/footer/footer.tsx +7 -9
- package/eject/header/header.tsx +42 -54
- package/eject/header/nav-overflow-script.ts +1 -1
- package/eject/header/types.ts +12 -13
- package/eject/image-enlarge/index.tsx +84 -69
- package/eject/image-enlarge/public.ts +2 -0
- package/eject/image-enlarge/ssr-fallback.tsx +7 -0
- package/eject/page-loading/page-loading-overlay.tsx +8 -15
- package/eject/sidebar/index.ts +1 -0
- package/eject/sidebar/sidebar.tsx +16 -97
- package/eject/sidebar/types.ts +15 -0
- package/eject/sidebar-toggle-island/index.tsx +103 -102
- package/eject/sidebar-tree-island/index.tsx +827 -615
- package/eject/sidebar-tree-island/scope-state.ts +154 -0
- package/eject/sidebar-tree-island/sidebar-scroll-preserve.ts +8 -2
- package/eject/site-tree-nav-island/index.tsx +349 -222
- package/eject/tab-item/tab-item.tsx +2 -4
- package/eject/theme-toggle/hydration-pending.ts +6 -10
- package/eject/theme-toggle/index.tsx +133 -173
- package/eject/toc/mobile-toc.tsx +27 -31
- package/eject/toc/toc.tsx +21 -24
- package/eject/toc/use-active-heading.ts +43 -38
- package/package.json +40 -22
- package/routes-src/404.tsx +2 -3
- package/routes-src/_chrome.tsx +7 -2
- package/routes-src/_design-token-panel-bootstrap.tsx +1 -63
- package/routes-src/docs-slug.tsx +2 -3
- package/routes-src/docs-tags-index.tsx +2 -3
- package/routes-src/docs-tags-tag.tsx +2 -3
- package/routes-src/docs-versions.tsx +2 -3
- package/routes-src/files-path.tsx +2 -4
- package/routes-src/index.tsx +2 -3
- package/routes-src/locale-docs-slug.tsx +2 -3
- package/routes-src/locale-docs-tags-index.tsx +2 -3
- package/routes-src/locale-docs-tags-tag.tsx +2 -3
- package/routes-src/locale-docs-versions.tsx +2 -3
- package/routes-src/locale-files-path.tsx +2 -4
- package/routes-src/locale-index.tsx +2 -3
- package/routes-src/mcp.tsx +25 -0
- package/routes-src/v-docs-slug.tsx +2 -3
- package/routes-src/v-locale-docs-slug.tsx +2 -3
- package/tsconfig.base.json +1 -1
- package/dist/safelist.css +0 -2
- package/dist/theme-no-reset.css +0 -379
- package/eject/desktop-sidebar-toggle-island/index.tsx +0 -110
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import {
|
|
4
|
+
computed,
|
|
5
|
+
For,
|
|
6
|
+
getScope,
|
|
7
|
+
Show,
|
|
8
|
+
signal,
|
|
9
|
+
type ReadonlySignal,
|
|
10
|
+
type Ref,
|
|
11
|
+
} from "@takazudo/zfb/zudo-react";
|
|
12
|
+
import type {
|
|
13
|
+
SidebarNavNode,
|
|
14
|
+
SidebarNavigationContext,
|
|
15
|
+
SidebarRootMenuItem,
|
|
16
|
+
SidebarLocaleLink,
|
|
17
|
+
} from "../sidebar/types.js";
|
|
10
18
|
import type { ResolvedDateFormats } from "../settings.js";
|
|
11
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
INDENT,
|
|
21
|
+
BASE_PAD,
|
|
22
|
+
connectorLeft,
|
|
23
|
+
ConnectorLines,
|
|
24
|
+
CategoryLinkIcon,
|
|
25
|
+
} from "../tree-nav-shared/index.js";
|
|
12
26
|
import { ChevronRight, ChevronLeft, Search } from "../icons/index.js";
|
|
13
|
-
// BARE ThemeToggle — renders inside the SidebarToggle island, so it must
|
|
14
|
-
// NOT bring its own island wrapper.
|
|
15
27
|
import { ThemeToggle, type ThemeToggleLabels } from "../theme-toggle/index.js";
|
|
16
28
|
import { smartBreakToHtml } from "../smart-break/index.js";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
import { AFTER_NAVIGATE_EVENT, BEFORE_NAVIGATE_EVENT } from "../transitions/index.js";
|
|
29
|
+
import { AFTER_NAVIGATE_EVENT } from "../transitions/index.js";
|
|
30
|
+
import { useSidebarScope, type ScopeControls } from "./scope-state.js";
|
|
20
31
|
import { filterTree } from "../sidebar-filter/index.js";
|
|
21
32
|
import { findActiveSlug, normalizePath } from "../sidebar-active-slug/index.js";
|
|
22
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
CURRENT_PATH_DATASET_KEY,
|
|
35
|
+
readCurrentPath,
|
|
36
|
+
} from "../current-path/index.js";
|
|
23
37
|
import { ensureSidebarScrollPreserve } from "./sidebar-scroll-preserve.js";
|
|
24
38
|
import {
|
|
25
39
|
formatYearLabel,
|
|
@@ -31,25 +45,31 @@ import {
|
|
|
31
45
|
} from "../note-tray-model/index.js";
|
|
32
46
|
import { formatMonthDay } from "../format-date/index.js";
|
|
33
47
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// module/document lifetime so that island lifecycle cannot discard it between
|
|
37
|
-
// before-preparation and after-swap. SSR evaluation is a safe no-op.
|
|
38
|
-
ensureSidebarScrollPreserve();
|
|
48
|
+
const STORAGE_KEY = "zd-sidebar-open";
|
|
49
|
+
const GROUPED_TRAY_ITEM_DEPTH = 3;
|
|
39
50
|
|
|
40
|
-
function ToggleChevron({
|
|
51
|
+
function ToggleChevron({
|
|
52
|
+
isExpanded,
|
|
53
|
+
className,
|
|
54
|
+
}: {
|
|
55
|
+
isExpanded: ReadonlySignal<boolean>;
|
|
56
|
+
className?: ReadonlySignal<string> | string;
|
|
57
|
+
}) {
|
|
41
58
|
return (
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
<For
|
|
60
|
+
each={computed(() => [
|
|
61
|
+
`h-[0.625rem] w-[0.625rem] shrink-0 transition-transform duration-150 ${isExpanded.value ? "rotate-90" : ""} ${typeof className === "string" ? className : (className?.value ?? "")}`,
|
|
62
|
+
])}
|
|
63
|
+
by={(value) => value}
|
|
64
|
+
>
|
|
65
|
+
{(value) => <ChevronRight class={value.value} />}
|
|
66
|
+
</For>
|
|
45
67
|
);
|
|
46
68
|
}
|
|
47
69
|
|
|
48
|
-
const STORAGE_KEY = "zd-sidebar-open";
|
|
49
|
-
const GROUPED_TRAY_ITEM_DEPTH = 3;
|
|
50
|
-
|
|
51
70
|
function padLeft(depth: number, forCategory: boolean): string {
|
|
52
|
-
if (depth === 0)
|
|
71
|
+
if (depth === 0)
|
|
72
|
+
return `calc(${BASE_PAD} + ${forCategory ? "0.15rem" : "0rem"})`;
|
|
53
73
|
return `calc(${depth} * ${INDENT} + 1.25rem + 5px)`;
|
|
54
74
|
}
|
|
55
75
|
|
|
@@ -58,349 +78,450 @@ function getOpenSet(): Set<string> {
|
|
|
58
78
|
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
59
79
|
if (!raw) return new Set();
|
|
60
80
|
const parsed: unknown = JSON.parse(raw);
|
|
61
|
-
return Array.isArray(parsed)
|
|
81
|
+
return Array.isArray(parsed)
|
|
82
|
+
? new Set(
|
|
83
|
+
parsed.filter((value): value is string => typeof value === "string"),
|
|
84
|
+
)
|
|
85
|
+
: new Set();
|
|
62
86
|
} catch {
|
|
63
87
|
return new Set();
|
|
64
88
|
}
|
|
65
89
|
}
|
|
66
90
|
|
|
67
|
-
function saveOpenSet(set: Set<string>) {
|
|
91
|
+
function saveOpenSet(set: Set<string>): void {
|
|
68
92
|
try {
|
|
69
93
|
sessionStorage.setItem(STORAGE_KEY, JSON.stringify([...set]));
|
|
70
94
|
} catch {
|
|
71
|
-
|
|
95
|
+
/* Storage can be unavailable. */
|
|
72
96
|
}
|
|
73
97
|
}
|
|
74
98
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* hydration-time fallback when the parent island does not forward
|
|
80
|
-
* `currentSlug` through its prop boundary, and at every View Transition to
|
|
81
|
-
* keep the highlight in sync.
|
|
82
|
-
*
|
|
83
|
-
* Returns `undefined` both when no pathname is resolvable AND when the
|
|
84
|
-
* resolved pathname matches no route (e.g. the literal "srcdoc") — callers
|
|
85
|
-
* must treat `undefined` as "no update," preserving whatever active slug is
|
|
86
|
-
* already set rather than clearing it.
|
|
87
|
-
*/
|
|
88
|
-
function deriveActiveSlug(nodes: SidebarNavNode[], pathname?: string): string | undefined {
|
|
89
|
-
// An empty-string `pathname` must fall through to the live sources instead
|
|
90
|
-
// of silently disabling derivation — SidebarWithDefaults defaults its own
|
|
91
|
-
// currentPath to "", so "" is the natural absent shape.
|
|
99
|
+
function deriveActiveSlug(
|
|
100
|
+
nodes: SidebarNavNode[],
|
|
101
|
+
pathname?: string,
|
|
102
|
+
): string | undefined {
|
|
92
103
|
const resolved = readCurrentPath(CURRENT_PATH_DATASET_KEY, pathname);
|
|
93
|
-
|
|
94
|
-
return findActiveSlug(nodes, normalizePath(resolved));
|
|
104
|
+
return resolved ? findActiveSlug(nodes, normalizePath(resolved)) : undefined;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
function useActiveSlug(nodes: SidebarNavNode[], initial?: string, currentPath?: string): string | undefined {
|
|
105
|
-
const [slug, setSlug] = useState<string | undefined>(() =>
|
|
106
|
-
initial !== undefined ? initial : deriveActiveSlug(nodes, currentPath),
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
useEffect(() => {
|
|
107
|
+
function useActiveSlug(
|
|
108
|
+
nodes: SidebarNavNode[],
|
|
109
|
+
initial?: string,
|
|
110
|
+
currentPath?: string,
|
|
111
|
+
) {
|
|
112
|
+
const slug = signal<string | undefined>(initial);
|
|
113
|
+
getScope().onActivate(() => {
|
|
110
114
|
const update = (pathname?: string) => {
|
|
111
115
|
const found = deriveActiveSlug(nodes, pathname);
|
|
112
|
-
if (found !== undefined)
|
|
116
|
+
if (found !== undefined) slug.value = found;
|
|
113
117
|
};
|
|
114
|
-
// The static `currentPath` prop is an SSR-serialized value for the page the
|
|
115
|
-
// island first hydrated on — valid at hydration time only. Post-navigation
|
|
116
|
-
// updates must re-read the LIVE sources (dataset override, then
|
|
117
|
-
// location.pathname); recomputing from the frozen prop would pin the
|
|
118
|
-
// highlight to the first-loaded page across client-router navigations.
|
|
119
|
-
const onNavigate = () => update();
|
|
120
118
|
update(currentPath);
|
|
119
|
+
const onNavigate = () => update();
|
|
121
120
|
document.addEventListener(AFTER_NAVIGATE_EVENT, onNavigate);
|
|
122
121
|
return () => document.removeEventListener(AFTER_NAVIGATE_EVENT, onNavigate);
|
|
123
|
-
}
|
|
124
|
-
|
|
122
|
+
});
|
|
125
123
|
return slug;
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
function RootMenuItemEntry({
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
function RootMenuItemEntry({
|
|
127
|
+
item,
|
|
128
|
+
}: {
|
|
129
|
+
item: ReadonlySignal<SidebarRootMenuItem>;
|
|
130
|
+
}) {
|
|
131
|
+
const expanded = signal(false);
|
|
132
|
+
const hasChildren = computed(() => !!item.value.children?.length);
|
|
132
133
|
return (
|
|
133
|
-
<div
|
|
134
|
-
<div
|
|
134
|
+
<div class="border-t border-muted">
|
|
135
|
+
<div class="flex items-center">
|
|
135
136
|
<a
|
|
136
|
-
href={item.href}
|
|
137
|
-
|
|
137
|
+
href={computed(() => item.value.href ?? "")}
|
|
138
|
+
class="flex flex-1 items-center gap-hsp-xs px-hsp-sm py-vsp-xs text-small font-semibold text-fg hover:text-accent hover:underline break-words"
|
|
138
139
|
>
|
|
139
|
-
<CategoryLinkIcon
|
|
140
|
-
<span
|
|
140
|
+
<CategoryLinkIcon class="w-[14px]" />
|
|
141
|
+
<span rawHtml={computed(() => smartBreakToHtml(item.value.label))} />
|
|
141
142
|
</a>
|
|
142
|
-
{hasChildren
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
<Show when={hasChildren}>
|
|
144
|
+
{() => (
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
on:click={() => {
|
|
148
|
+
expanded.value = !expanded.value;
|
|
149
|
+
}}
|
|
150
|
+
class="flex items-center justify-center px-hsp-sm py-vsp-xs text-muted hover:text-fg"
|
|
151
|
+
aria-expanded={computed(() =>
|
|
152
|
+
expanded.value ? "true" : "false",
|
|
153
|
+
)}
|
|
154
|
+
aria-label={computed(
|
|
155
|
+
() =>
|
|
156
|
+
`${expanded.value ? "Collapse" : "Expand"} ${item.value.label}`,
|
|
157
|
+
)}
|
|
158
|
+
>
|
|
159
|
+
<ToggleChevron isExpanded={expanded} className="text-muted" />
|
|
160
|
+
</button>
|
|
161
|
+
)}
|
|
162
|
+
</Show>
|
|
153
163
|
</div>
|
|
154
|
-
{hasChildren && expanded
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
className="block pl-hsp-xl pr-hsp-sm py-vsp-2xs text-small text-muted hover:text-accent hover:underline break-words"
|
|
164
|
+
<Show when={computed(() => hasChildren.value && expanded.value)}>
|
|
165
|
+
{() => (
|
|
166
|
+
<div class="pb-vsp-xs">
|
|
167
|
+
<For
|
|
168
|
+
each={computed(() => item.value.children ?? [])}
|
|
169
|
+
by={(child) => child.href}
|
|
161
170
|
>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
{(child) => (
|
|
172
|
+
<a
|
|
173
|
+
href={computed(() => child.value.href)}
|
|
174
|
+
class="block pl-hsp-xl pr-hsp-sm py-vsp-2xs text-small text-muted hover:text-accent hover:underline break-words"
|
|
175
|
+
>
|
|
176
|
+
<span
|
|
177
|
+
rawHtml={computed(() =>
|
|
178
|
+
smartBreakToHtml(child.value.label),
|
|
179
|
+
)}
|
|
180
|
+
/>
|
|
181
|
+
</a>
|
|
182
|
+
)}
|
|
183
|
+
</For>
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
</Show>
|
|
167
187
|
</div>
|
|
168
188
|
);
|
|
169
189
|
}
|
|
170
190
|
|
|
171
191
|
export interface SidebarTreeProps {
|
|
172
192
|
nodes: SidebarNavNode[];
|
|
193
|
+
navigation?: SidebarNavigationContext;
|
|
173
194
|
currentSlug?: string;
|
|
174
|
-
/**
|
|
175
|
-
* Explicit current-route override, checked before the
|
|
176
|
-
* `data-zd-current-path` dataset override and `window.location.pathname`
|
|
177
|
-
* when deriving the active slug on hydration and at every View Transition.
|
|
178
|
-
* See `deriveActiveSlug` (zudolab/zudo-doc#3398).
|
|
179
|
-
*/
|
|
195
|
+
/** Route override used on activation before dataset and location. */
|
|
180
196
|
currentPath?: string;
|
|
181
197
|
rootMenuItems?: SidebarRootMenuItem[];
|
|
182
198
|
backToMenuLabel?: string;
|
|
183
|
-
/** Display locale; falls back to the active locale link, then English. */
|
|
184
199
|
locale?: string;
|
|
185
200
|
localeLinks?: SidebarLocaleLink[];
|
|
186
201
|
themeDefaultMode?: "light" | "dark";
|
|
187
202
|
themeLabels?: ThemeToggleLabels;
|
|
188
203
|
themeRespectSystem?: boolean;
|
|
189
|
-
/**
|
|
190
|
-
* Per-role date patterns already resolved for this page's locale, serialized
|
|
191
|
-
* into the island's `data-props` by the SSR wrappers (`sidebar-with-defaults`
|
|
192
|
-
* for the desktop sidebar, `header-with-defaults` -> `SidebarToggle` for the
|
|
193
|
-
* mobile drawer). Optional and absent-safe: omitted means every role behaves
|
|
194
|
-
* as `"locale"` — today's `Intl` output (#4075).
|
|
195
|
-
*
|
|
196
|
-
* The patterns are resolved against the real page locale at SSR,
|
|
197
|
-
* so they stay correct on a single-locale site where `localeLinks` is empty.
|
|
198
|
-
*/
|
|
199
204
|
dateFormats?: ResolvedDateFormats;
|
|
200
205
|
}
|
|
201
206
|
|
|
202
|
-
function SidebarFooter({
|
|
207
|
+
function SidebarFooter({
|
|
208
|
+
links,
|
|
209
|
+
themeDefaultMode,
|
|
210
|
+
themeLabels,
|
|
211
|
+
themeRespectSystem,
|
|
212
|
+
}: {
|
|
213
|
+
links?: SidebarLocaleLink[];
|
|
214
|
+
themeDefaultMode?: "light" | "dark";
|
|
215
|
+
themeLabels?: ThemeToggleLabels;
|
|
216
|
+
themeRespectSystem?: boolean;
|
|
217
|
+
}) {
|
|
203
218
|
if (!links && !themeDefaultMode) return null;
|
|
204
219
|
return (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
<div class="lg:hidden flex items-center gap-hsp-md border-t border-muted px-hsp-sm py-vsp-xs pb-[50vh] text-small">
|
|
221
|
+
{themeDefaultMode ? (
|
|
222
|
+
<ThemeToggle
|
|
223
|
+
defaultMode={themeDefaultMode}
|
|
224
|
+
labels={themeLabels}
|
|
225
|
+
respectPrefersColorScheme={themeRespectSystem}
|
|
226
|
+
pendingUntilHydrated={true}
|
|
227
|
+
/>
|
|
228
|
+
) : null}
|
|
229
|
+
<For each={computed(() => links ?? [])} by={(link) => link.href}>
|
|
230
|
+
{(link, index) => (
|
|
231
|
+
<span class="flex items-center gap-hsp-xs">
|
|
232
|
+
<Show when={computed(() => index.value > 0)}>
|
|
233
|
+
{() => <span class="text-muted">/</span>}
|
|
234
|
+
</Show>
|
|
235
|
+
<Show
|
|
236
|
+
when={computed(() => link.value.active)}
|
|
237
|
+
fallback={() => (
|
|
238
|
+
<a
|
|
239
|
+
href={computed(() => link.value.href)}
|
|
240
|
+
lang={computed(() => link.value.code)}
|
|
241
|
+
class="text-muted hover:text-fg"
|
|
242
|
+
>
|
|
243
|
+
{computed(() => link.value.label)}
|
|
244
|
+
</a>
|
|
245
|
+
)}
|
|
246
|
+
>
|
|
247
|
+
{() => (
|
|
248
|
+
<span aria-current="true" class="font-medium text-fg">
|
|
249
|
+
{computed(() => link.value.label)}
|
|
250
|
+
</span>
|
|
251
|
+
)}
|
|
252
|
+
</Show>
|
|
253
|
+
</span>
|
|
254
|
+
)}
|
|
255
|
+
</For>
|
|
222
256
|
</div>
|
|
223
257
|
);
|
|
224
258
|
}
|
|
225
259
|
|
|
226
|
-
export function SidebarTree({
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
260
|
+
export function SidebarTree({
|
|
261
|
+
nodes,
|
|
262
|
+
navigation,
|
|
263
|
+
currentSlug,
|
|
264
|
+
currentPath,
|
|
265
|
+
rootMenuItems,
|
|
266
|
+
backToMenuLabel,
|
|
267
|
+
locale: localeProp,
|
|
268
|
+
localeLinks,
|
|
269
|
+
themeDefaultMode,
|
|
270
|
+
themeLabels,
|
|
271
|
+
themeRespectSystem,
|
|
272
|
+
dateFormats,
|
|
273
|
+
}: SidebarTreeProps) {
|
|
274
|
+
const scope = getScope();
|
|
275
|
+
const activeSlug = useActiveSlug(navigation?.roots ?? nodes, currentSlug, currentPath);
|
|
276
|
+
const query = signal("");
|
|
277
|
+
const { selected, selectedNodes, broader, labels, canRestore, changeScope, save, navRef, scopeControls } = useSidebarScope({
|
|
278
|
+
nodes, navigation, activeSlug, query,
|
|
279
|
+
locale: localeProp ?? localeLinks?.find((link) => link.active)?.code ?? "en",
|
|
280
|
+
});
|
|
281
|
+
const showingRootMenu = signal(false);
|
|
282
|
+
const filterRef: Ref<HTMLInputElement> = { current: null };
|
|
283
|
+
const filterPlaceholder = signal("Filter...");
|
|
284
|
+
scope.onActivate(() => {
|
|
285
|
+
ensureSidebarScrollPreserve();
|
|
286
|
+
const platform =
|
|
287
|
+
(navigator as Navigator & { userAgentData?: { platform: string } })
|
|
288
|
+
.userAgentData?.platform ?? navigator.platform;
|
|
289
|
+
filterPlaceholder.value = /mac/i.test(platform)
|
|
290
|
+
? "Filter... (⌘ + /)"
|
|
291
|
+
: "Filter... (Ctrl + /)";
|
|
292
|
+
const handleKeyDown = (event: Event) => {
|
|
293
|
+
const keyEvent = event as KeyboardEvent;
|
|
294
|
+
if (
|
|
295
|
+
keyEvent.isComposing ||
|
|
296
|
+
keyEvent.key !== "/" ||
|
|
297
|
+
!(keyEvent.metaKey || keyEvent.ctrlKey)
|
|
298
|
+
)
|
|
299
|
+
return;
|
|
300
|
+
const input = filterRef.current;
|
|
301
|
+
if (!input || input.offsetParent === null) return;
|
|
302
|
+
event.preventDefault();
|
|
303
|
+
input.focus();
|
|
304
|
+
input.select();
|
|
305
|
+
};
|
|
252
306
|
document.addEventListener("keydown", handleKeyDown);
|
|
253
307
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
() => (query ? filterTree(nodes, query) : nodes),
|
|
258
|
-
[nodes, query],
|
|
308
|
+
});
|
|
309
|
+
const filteredNodes = computed(() =>
|
|
310
|
+
query.value ? filterTree(selectedNodes.value, query.value) : selectedNodes.value,
|
|
259
311
|
);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
312
|
+
const locale =
|
|
313
|
+
localeProp ?? localeLinks?.find((link) => link.active)?.code ?? "en";
|
|
314
|
+
const footer = (
|
|
315
|
+
<SidebarFooter
|
|
316
|
+
links={localeLinks}
|
|
317
|
+
themeDefaultMode={themeDefaultMode}
|
|
318
|
+
themeLabels={themeLabels}
|
|
319
|
+
themeRespectSystem={themeRespectSystem}
|
|
320
|
+
/>
|
|
321
|
+
);
|
|
322
|
+
const menu = (
|
|
323
|
+
<For each={computed(() => rootMenuItems ?? [])} by={(item) => item.href}>
|
|
324
|
+
{(item) => <RootMenuItemEntry item={item} />}
|
|
325
|
+
</For>
|
|
264
326
|
);
|
|
265
|
-
|
|
266
|
-
const noteTrayRoot = nodes.length === 1 && nodes[0]?.shape === "note-tray" ? nodes[0] : undefined;
|
|
267
|
-
const filteredNoteTrayRoot = noteTrayRoot
|
|
268
|
-
? filteredNodes.find((node) => node.slug === noteTrayRoot.slug)
|
|
269
|
-
: undefined;
|
|
270
|
-
const locale = localeProp ?? localeLinks?.find((link) => link.active)?.code ?? "en";
|
|
271
|
-
|
|
272
|
-
// Root menu view: show headerNav items as a simple list (Docusaurus-style)
|
|
273
|
-
if (showingRootMenu && rootMenuItems) {
|
|
274
|
-
return (
|
|
275
|
-
<nav>
|
|
276
|
-
<button
|
|
277
|
-
type="button"
|
|
278
|
-
onClick={() => setShowingRootMenu(false)}
|
|
279
|
-
className="flex w-full items-center gap-hsp-xs px-hsp-sm py-vsp-xs text-left text-small text-muted hover:text-fg border-b border-muted"
|
|
280
|
-
>
|
|
281
|
-
<ChevronRight className="h-icon-sm w-icon-sm shrink-0" />
|
|
282
|
-
{backToMenuLabel ?? "Back to main menu"}
|
|
283
|
-
</button>
|
|
284
|
-
{rootMenuItems.map((item) => (
|
|
285
|
-
<RootMenuItemEntry key={item.href} item={item} />
|
|
286
|
-
))}
|
|
287
|
-
{footer}
|
|
288
|
-
</nav>
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Top page: show only header nav links, no doc tree or filter.
|
|
293
|
-
if (activeSlug === undefined && rootMenuItems) {
|
|
294
|
-
return (
|
|
295
|
-
<nav>
|
|
296
|
-
{rootMenuItems.map((item) => (
|
|
297
|
-
<RootMenuItemEntry key={item.href} item={item} />
|
|
298
|
-
))}
|
|
299
|
-
{footer}
|
|
300
|
-
</nav>
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
327
|
return (
|
|
305
|
-
<
|
|
306
|
-
{
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
328
|
+
<Show
|
|
329
|
+
when={computed(() => showingRootMenu.value && !!rootMenuItems)}
|
|
330
|
+
fallback={() => (
|
|
331
|
+
<Show
|
|
332
|
+
when={computed(
|
|
333
|
+
() => activeSlug.value === undefined && !!rootMenuItems,
|
|
334
|
+
)}
|
|
335
|
+
fallback={() => (
|
|
336
|
+
<nav ref={navRef} tabindex={-1}>
|
|
337
|
+
{rootMenuItems ? (
|
|
338
|
+
<button
|
|
339
|
+
type="button"
|
|
340
|
+
on:click={() => {
|
|
341
|
+
showingRootMenu.value = true;
|
|
342
|
+
}}
|
|
343
|
+
class="lg:hidden flex w-full items-center gap-hsp-xs px-hsp-sm py-vsp-xs text-left text-small text-muted hover:text-fg border-b border-muted"
|
|
344
|
+
>
|
|
345
|
+
<ChevronLeft class="h-icon-sm w-icon-sm shrink-0" />
|
|
346
|
+
{backToMenuLabel ?? "Back to main menu"}
|
|
347
|
+
</button>
|
|
348
|
+
) : null}
|
|
349
|
+
<div class="px-hsp-sm py-vsp-xs border-b border-muted" data-sidebar-filter-region>
|
|
350
|
+
<div class="flex items-center gap-hsp-xs bg-surface rounded px-hsp-sm py-vsp-2xs">
|
|
351
|
+
<Search class="h-[14px] w-[14px] text-muted shrink-0" />
|
|
352
|
+
<input
|
|
353
|
+
ref={filterRef}
|
|
354
|
+
type="text"
|
|
355
|
+
aria-label="Filter navigation"
|
|
356
|
+
placeholder={filterPlaceholder}
|
|
357
|
+
modelValue={query}
|
|
358
|
+
on:input={(event: Event) => { query.value = (event.target as HTMLInputElement).value; save(); }}
|
|
359
|
+
class="bg-transparent text-small outline-none w-full text-fg placeholder:text-muted focus-visible:outline-2 focus-visible:outline-accent" style="outline-offset:-4px"
|
|
360
|
+
/>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
{navigation ? (
|
|
364
|
+
<Show when={computed(() => broader.value !== null)}>{() => (
|
|
365
|
+
<div class="border-b border-muted px-hsp-md py-vsp-2xs text-small" data-sidebar-scope-toolbar>
|
|
366
|
+
<div class="flex items-center justify-between gap-hsp-xs" data-sidebar-scope-actions>
|
|
367
|
+
<button type="button" data-sidebar-broaden disabled={computed(() => broader.value === null)}
|
|
368
|
+
on:click={() => { if (broader.value !== null) changeScope(broader.value); }}
|
|
369
|
+
style="min-height:40px"
|
|
370
|
+
class="flex items-center gap-hsp-sm px-hsp-xs font-medium rounded text-fg hover:bg-surface focus-visible:bg-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent disabled:text-muted disabled:opacity-50">
|
|
371
|
+
<span aria-hidden="true">↑</span>{labels.broaden}
|
|
372
|
+
</button>
|
|
373
|
+
<Show when={canRestore}>{() => (
|
|
374
|
+
<button type="button" data-sidebar-restore on:click={() => changeScope(null)}
|
|
375
|
+
title={labels.restore} aria-label={labels.restore}
|
|
376
|
+
style="width:40px;height:40px"
|
|
377
|
+
class="grid place-items-center shrink-0 rounded text-muted hover:text-fg focus-visible:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent">
|
|
378
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
379
|
+
<circle cx="12" cy="12" r="10" />
|
|
380
|
+
<circle cx="12" cy="12" r="6" />
|
|
381
|
+
<circle cx="12" cy="12" r="2" />
|
|
382
|
+
</svg>
|
|
383
|
+
</button>
|
|
384
|
+
)}</Show>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
)}</Show>
|
|
388
|
+
) : null}
|
|
389
|
+
<div data-sidebar-tree-root>
|
|
390
|
+
<Show when={computed(() => selectedNodes.value.length === 1 && selectedNodes.value[0]?.shape === "note-tray")}
|
|
391
|
+
fallback={() => <NodeList nodes={filteredNodes} currentSlug={activeSlug} depth={0} forceOpen={computed(() => !!query.value)} scopeControls={scopeControls} />}>
|
|
392
|
+
{() => <Show when={computed(() => !!filteredNodes.value[0])}>{() => (
|
|
393
|
+
<TrayList tray={computed(() => filteredNodes.value[0]!)} itemCount={getNoteTrayItems(selectedNodes.value[0]!).length}
|
|
394
|
+
currentSlug={activeSlug} forceOpen={computed(() => !!query.value)} locale={locale} dateFormats={dateFormats} scopeControls={scopeControls} />
|
|
395
|
+
)}</Show>}
|
|
396
|
+
</Show>
|
|
397
|
+
</div>
|
|
398
|
+
{footer}
|
|
399
|
+
</nav>
|
|
400
|
+
)}
|
|
311
401
|
>
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
402
|
+
{() => (
|
|
403
|
+
<nav>
|
|
404
|
+
{menu}
|
|
405
|
+
{footer}
|
|
406
|
+
</nav>
|
|
407
|
+
)}
|
|
408
|
+
</Show>
|
|
315
409
|
)}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
<
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
<TrayList
|
|
333
|
-
tray={filteredNoteTrayRoot}
|
|
334
|
-
itemCount={getNoteTrayItems(noteTrayRoot).length}
|
|
335
|
-
currentSlug={activeSlug}
|
|
336
|
-
forceOpen={!!query}
|
|
337
|
-
locale={locale}
|
|
338
|
-
dateFormats={dateFormats}
|
|
339
|
-
/>
|
|
340
|
-
)
|
|
341
|
-
) : (
|
|
342
|
-
<NodeList
|
|
343
|
-
nodes={filteredNodes}
|
|
344
|
-
currentSlug={activeSlug}
|
|
345
|
-
depth={0}
|
|
346
|
-
forceOpen={!!query}
|
|
347
|
-
/>
|
|
410
|
+
>
|
|
411
|
+
{() => (
|
|
412
|
+
<nav>
|
|
413
|
+
<button
|
|
414
|
+
type="button"
|
|
415
|
+
on:click={() => {
|
|
416
|
+
showingRootMenu.value = false;
|
|
417
|
+
}}
|
|
418
|
+
class="flex w-full items-center gap-hsp-xs px-hsp-sm py-vsp-xs text-left text-small text-muted hover:text-fg border-b border-muted"
|
|
419
|
+
>
|
|
420
|
+
<ChevronRight class="h-icon-sm w-icon-sm shrink-0" />
|
|
421
|
+
{backToMenuLabel ?? "Back to main menu"}
|
|
422
|
+
</button>
|
|
423
|
+
{menu}
|
|
424
|
+
{footer}
|
|
425
|
+
</nav>
|
|
348
426
|
)}
|
|
349
|
-
|
|
350
|
-
</nav>
|
|
427
|
+
</Show>
|
|
351
428
|
);
|
|
352
429
|
}
|
|
353
430
|
SidebarTree.displayName = "SidebarTree";
|
|
354
431
|
|
|
432
|
+
interface ActiveProps {
|
|
433
|
+
scopeControls?: ScopeControls;
|
|
434
|
+
currentSlug: ReadonlySignal<string | undefined>;
|
|
435
|
+
forceOpen: ReadonlySignal<boolean>;
|
|
436
|
+
}
|
|
437
|
+
interface RowProps extends ActiveProps {
|
|
438
|
+
node: ReadonlySignal<SidebarNavNode>;
|
|
439
|
+
depth: number;
|
|
440
|
+
isLast: ReadonlySignal<boolean>;
|
|
441
|
+
}
|
|
442
|
+
|
|
355
443
|
function TrayList({
|
|
356
444
|
tray,
|
|
357
445
|
itemCount,
|
|
358
446
|
currentSlug,
|
|
359
447
|
forceOpen,
|
|
448
|
+
scopeControls,
|
|
360
449
|
locale,
|
|
361
450
|
dateFormats,
|
|
362
451
|
}: {
|
|
363
|
-
tray: SidebarNavNode
|
|
452
|
+
tray: ReadonlySignal<SidebarNavNode>;
|
|
364
453
|
itemCount: number;
|
|
365
|
-
currentSlug?: string;
|
|
366
|
-
forceOpen: boolean;
|
|
367
454
|
locale: string;
|
|
368
455
|
dateFormats?: ResolvedDateFormats;
|
|
369
|
-
}) {
|
|
370
|
-
const items = getNoteTrayItems(tray);
|
|
371
|
-
const sidebarStyle = tray.noteTraySidebar ?? "index";
|
|
456
|
+
} & ActiveProps) {
|
|
457
|
+
const items = computed(() => getNoteTrayItems(tray.value));
|
|
458
|
+
const sidebarStyle = computed(() => tray.value.noteTraySidebar ?? "index");
|
|
372
459
|
const width = rankWidth(itemCount);
|
|
373
|
-
|
|
374
460
|
return (
|
|
375
461
|
<>
|
|
376
|
-
<LeafNode
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
462
|
+
<LeafNode
|
|
463
|
+
node={tray}
|
|
464
|
+
currentSlug={currentSlug}
|
|
465
|
+
depth={0}
|
|
466
|
+
isLast={computed(() => items.value.length === 0)}
|
|
467
|
+
forceOpen={forceOpen}
|
|
468
|
+
/>
|
|
469
|
+
<Show
|
|
470
|
+
when={computed(() => sidebarStyle.value === "index")}
|
|
471
|
+
fallback={() => (
|
|
472
|
+
<For
|
|
473
|
+
each={computed(() =>
|
|
474
|
+
groupItems(
|
|
475
|
+
items.value,
|
|
476
|
+
sidebarStyle.value as "year" | "month",
|
|
477
|
+
tray.value.sortOrder ?? "asc",
|
|
478
|
+
),
|
|
479
|
+
)}
|
|
480
|
+
by={(group) => group.key}
|
|
481
|
+
>
|
|
482
|
+
{(group, index) => (
|
|
483
|
+
<TrayGroupNode
|
|
484
|
+
traySlug={computed(() => tray.value.slug)}
|
|
485
|
+
trayOccurrence={computed(() => tray.value.occurrenceId ?? tray.value.slug)}
|
|
486
|
+
scopeControls={scopeControls}
|
|
487
|
+
group={group}
|
|
488
|
+
grouping={computed(
|
|
489
|
+
() => sidebarStyle.value as "year" | "month",
|
|
490
|
+
)}
|
|
491
|
+
locale={locale}
|
|
492
|
+
dateFormats={dateFormats}
|
|
493
|
+
currentSlug={currentSlug}
|
|
494
|
+
forceOpen={forceOpen}
|
|
495
|
+
isLast={computed(
|
|
496
|
+
() =>
|
|
497
|
+
index.value ===
|
|
498
|
+
groupItems(
|
|
499
|
+
items.value,
|
|
500
|
+
sidebarStyle.value as "year" | "month",
|
|
501
|
+
tray.value.sortOrder ?? "asc",
|
|
502
|
+
).length -
|
|
503
|
+
1,
|
|
504
|
+
)}
|
|
505
|
+
/>
|
|
506
|
+
)}
|
|
507
|
+
</For>
|
|
508
|
+
)}
|
|
509
|
+
>
|
|
510
|
+
{() => (
|
|
511
|
+
<For each={items} by={(item) => item.slug}>
|
|
512
|
+
{(item, index) => (
|
|
513
|
+
<TrayItem
|
|
514
|
+
item={item}
|
|
515
|
+
currentSlug={currentSlug}
|
|
516
|
+
rankDigits={width}
|
|
517
|
+
isLast={computed(() => index.value === items.value.length - 1)}
|
|
518
|
+
locale={locale}
|
|
519
|
+
dateFormats={dateFormats}
|
|
520
|
+
/>
|
|
521
|
+
)}
|
|
522
|
+
</For>
|
|
523
|
+
)}
|
|
524
|
+
</Show>
|
|
404
525
|
</>
|
|
405
526
|
);
|
|
406
527
|
}
|
|
@@ -415,53 +536,97 @@ function TrayItem({
|
|
|
415
536
|
locale,
|
|
416
537
|
dateFormats,
|
|
417
538
|
}: {
|
|
418
|
-
item: SidebarNavNode
|
|
419
|
-
currentSlug
|
|
539
|
+
item: ReadonlySignal<SidebarNavNode>;
|
|
540
|
+
currentSlug: ReadonlySignal<string | undefined>;
|
|
420
541
|
rankDigits?: number;
|
|
421
|
-
isLast: boolean
|
|
542
|
+
isLast: ReadonlySignal<boolean>;
|
|
422
543
|
showDate?: boolean;
|
|
423
544
|
depth?: number;
|
|
424
545
|
locale?: string;
|
|
425
546
|
dateFormats?: ResolvedDateFormats;
|
|
426
547
|
}) {
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
548
|
+
const active = computed(() => item.value.slug === currentSlug.value);
|
|
549
|
+
const shortDate = computed(() =>
|
|
550
|
+
item.value.date
|
|
551
|
+
? formatMonthDay(item.value.date, dateFormats?.numericMonthDay, locale)
|
|
552
|
+
: undefined,
|
|
553
|
+
);
|
|
554
|
+
return (
|
|
555
|
+
<Show when={computed(() => !!item.value.href)}>
|
|
556
|
+
{() => (
|
|
557
|
+
<div class={computed(() => (isLast.value ? "pb-vsp-md" : ""))}>
|
|
558
|
+
<div class="relative">
|
|
559
|
+
<ConnectorLinesLive
|
|
560
|
+
depth={depth}
|
|
561
|
+
isLast={isLast}
|
|
562
|
+
topPad="var(--spacing-vsp-2xs)"
|
|
563
|
+
/>
|
|
564
|
+
<a
|
|
565
|
+
href={computed(() => item.value.href ?? "")}
|
|
566
|
+
aria-current={computed(() => (active.value ? "page" : "false"))}
|
|
567
|
+
data-nav-active={computed(() => (active.value ? "" : null))}
|
|
568
|
+
class={computed(
|
|
569
|
+
() =>
|
|
570
|
+
`flex items-start gap-hsp-xs py-vsp-2xs pr-hsp-xs lg:pr-hsp-sm text-small break-words ${active.value ? "bg-fg font-medium text-bg" : "text-muted hover:text-accent hover:underline focus:underline focus:text-accent"}`,
|
|
571
|
+
)}
|
|
572
|
+
style={{ "padding-left": padLeft(depth, false) }}
|
|
573
|
+
>
|
|
574
|
+
{rankDigits !== undefined ? (
|
|
575
|
+
<span
|
|
576
|
+
class={computed(
|
|
577
|
+
() =>
|
|
578
|
+
`shrink-0 tabular-nums${active.value ? "" : " text-muted"}`,
|
|
579
|
+
)}
|
|
580
|
+
>
|
|
581
|
+
{computed(() =>
|
|
582
|
+
item.value.rank === undefined
|
|
583
|
+
? ""
|
|
584
|
+
: String(item.value.rank).padStart(rankDigits, "0"),
|
|
585
|
+
)}
|
|
586
|
+
</span>
|
|
587
|
+
) : null}
|
|
588
|
+
<span
|
|
589
|
+
class="min-w-0 flex-1"
|
|
590
|
+
rawHtml={computed(() => smartBreakToHtml(item.value.label))}
|
|
591
|
+
/>
|
|
592
|
+
<Show when={computed(() => showDate && !!shortDate.value)}>
|
|
593
|
+
{() => (
|
|
594
|
+
<span
|
|
595
|
+
class={computed(
|
|
596
|
+
() =>
|
|
597
|
+
`shrink-0 tabular-nums${active.value ? "" : " text-muted"}`,
|
|
598
|
+
)}
|
|
599
|
+
>
|
|
600
|
+
{shortDate}
|
|
601
|
+
</span>
|
|
602
|
+
)}
|
|
603
|
+
</Show>
|
|
604
|
+
</a>
|
|
605
|
+
</div>
|
|
606
|
+
</div>
|
|
607
|
+
)}
|
|
608
|
+
</Show>
|
|
609
|
+
);
|
|
610
|
+
}
|
|
435
611
|
|
|
612
|
+
function ConnectorLinesLive({
|
|
613
|
+
depth,
|
|
614
|
+
isLast,
|
|
615
|
+
topPad,
|
|
616
|
+
}: {
|
|
617
|
+
depth: number;
|
|
618
|
+
isLast: ReadonlySignal<boolean>;
|
|
619
|
+
topPad: string;
|
|
620
|
+
}) {
|
|
436
621
|
return (
|
|
437
|
-
<
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
<
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
isActive
|
|
446
|
-
? "bg-fg font-medium text-bg"
|
|
447
|
-
: "text-muted hover:text-accent hover:underline focus:underline focus:text-accent"
|
|
448
|
-
}`}
|
|
449
|
-
style={{ paddingLeft: padLeft(depth, false) }}
|
|
450
|
-
>
|
|
451
|
-
{rankDigits !== undefined && (
|
|
452
|
-
<span className={`shrink-0 tabular-nums${isActive ? "" : " text-muted"}`}>
|
|
453
|
-
{item.rank === undefined ? "" : String(item.rank).padStart(rankDigits, "0")}
|
|
454
|
-
</span>
|
|
455
|
-
)}
|
|
456
|
-
<span className="min-w-0 flex-1" dangerouslySetInnerHTML={{ __html: labelHtml }} />
|
|
457
|
-
{showDate && shortDate && (
|
|
458
|
-
<span className={`shrink-0 tabular-nums${isActive ? "" : " text-muted"}`}>
|
|
459
|
-
{shortDate}
|
|
460
|
-
</span>
|
|
461
|
-
)}
|
|
462
|
-
</a>
|
|
463
|
-
</div>
|
|
464
|
-
</div>
|
|
622
|
+
<Show
|
|
623
|
+
when={isLast}
|
|
624
|
+
fallback={() => (
|
|
625
|
+
<ConnectorLines depth={depth} isLast={false} topPad={topPad} />
|
|
626
|
+
)}
|
|
627
|
+
>
|
|
628
|
+
{() => <ConnectorLines depth={depth} isLast={true} topPad={topPad} />}
|
|
629
|
+
</Show>
|
|
465
630
|
);
|
|
466
631
|
}
|
|
467
632
|
|
|
@@ -471,6 +636,8 @@ function noteTrayGroupStorageKey(traySlug: string, groupKey: string): string {
|
|
|
471
636
|
|
|
472
637
|
function TrayGroupNode({
|
|
473
638
|
traySlug,
|
|
639
|
+
trayOccurrence,
|
|
640
|
+
scopeControls,
|
|
474
641
|
group,
|
|
475
642
|
grouping,
|
|
476
643
|
locale,
|
|
@@ -479,350 +646,395 @@ function TrayGroupNode({
|
|
|
479
646
|
forceOpen,
|
|
480
647
|
isLast,
|
|
481
648
|
}: {
|
|
482
|
-
traySlug: string
|
|
483
|
-
|
|
484
|
-
|
|
649
|
+
traySlug: ReadonlySignal<string>;
|
|
650
|
+
trayOccurrence: ReadonlySignal<string>;
|
|
651
|
+
group: ReadonlySignal<NoteTrayGroup<SidebarNavNode>>;
|
|
652
|
+
grouping: ReadonlySignal<"year" | "month">;
|
|
485
653
|
locale: string;
|
|
486
654
|
dateFormats?: ResolvedDateFormats;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}, [containsCurrent]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
512
|
-
|
|
513
|
-
useEffect(() => {
|
|
514
|
-
if (open) {
|
|
655
|
+
isLast: ReadonlySignal<boolean>;
|
|
656
|
+
} & ActiveProps) {
|
|
657
|
+
const scope = getScope();
|
|
658
|
+
const containsCurrent = computed(() =>
|
|
659
|
+
group.value.items.some((item) => item.slug === currentSlug.value),
|
|
660
|
+
);
|
|
661
|
+
const occurrenceKey = computed(() => `${trayOccurrence.value}#${group.value.key}`);
|
|
662
|
+
const open = signal(scopeControls?.expansion.value[occurrenceKey.value] ?? (scopeControls?.defaultGroupOpen(group.value.items) ?? containsCurrent.value));
|
|
663
|
+
const storageKey = computed(() =>
|
|
664
|
+
noteTrayGroupStorageKey(traySlug.value, group.value.key),
|
|
665
|
+
);
|
|
666
|
+
const label = computed(() =>
|
|
667
|
+
grouping.value === "year"
|
|
668
|
+
? formatYearLabel(group.value.key, locale, dateFormats?.year)
|
|
669
|
+
: formatYearMonthLabel(group.value.key, locale, dateFormats?.yearMonth),
|
|
670
|
+
);
|
|
671
|
+
scope.onActivate(() => {
|
|
672
|
+
if (!scopeControls && getOpenSet().has(storageKey.value)) open.value = true;
|
|
673
|
+
});
|
|
674
|
+
scope.effect(() => {
|
|
675
|
+
if (containsCurrent.value) open.value = true;
|
|
676
|
+
});
|
|
677
|
+
scope.effect(() => {
|
|
678
|
+
if (!scopeControls && open.value) {
|
|
515
679
|
const stored = getOpenSet();
|
|
516
|
-
if (!stored.has(storageKey)) {
|
|
517
|
-
stored.add(storageKey);
|
|
680
|
+
if (!stored.has(storageKey.value)) {
|
|
681
|
+
stored.add(storageKey.value);
|
|
518
682
|
saveOpenSet(stored);
|
|
519
683
|
}
|
|
520
684
|
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
685
|
+
});
|
|
686
|
+
const toggle = () => {
|
|
687
|
+
open.value = !open.value;
|
|
688
|
+
if (scopeControls) {
|
|
689
|
+
scopeControls.expansion.value = { ...scopeControls.expansion.value, [occurrenceKey.value]: open.value };
|
|
690
|
+
scopeControls.save();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const stored = getOpenSet();
|
|
694
|
+
if (open.value) stored.add(storageKey.value);
|
|
695
|
+
else stored.delete(storageKey.value);
|
|
696
|
+
saveOpenSet(stored);
|
|
697
|
+
};
|
|
698
|
+
scope.effect(() => {
|
|
699
|
+
const stored = scopeControls?.expansion.value[occurrenceKey.value];
|
|
700
|
+
if (scopeControls) open.value = stored ?? scopeControls.defaultGroupOpen(group.value.items);
|
|
701
|
+
});
|
|
702
|
+
const expanded = computed(() => forceOpen.value || open.value);
|
|
703
|
+
const items = computed(() => group.value.items);
|
|
536
704
|
return (
|
|
537
|
-
<div
|
|
538
|
-
{
|
|
539
|
-
|
|
540
|
-
className="absolute border-l border-solid border-muted z-local-1"
|
|
541
|
-
style={{ left: connectorLeft(1), top: 0, bottom: 0 }}
|
|
542
|
-
/>
|
|
705
|
+
<div
|
|
706
|
+
class={computed(() =>
|
|
707
|
+
!isLast.value && expanded.value ? "relative" : "",
|
|
543
708
|
)}
|
|
544
|
-
|
|
545
|
-
|
|
709
|
+
>
|
|
710
|
+
<Show when={computed(() => !isLast.value && expanded.value)}>
|
|
711
|
+
{() => (
|
|
712
|
+
<div
|
|
713
|
+
class="absolute border-l border-solid border-muted z-local-1"
|
|
714
|
+
style={{ left: connectorLeft(1), top: "0px", bottom: "0px" }}
|
|
715
|
+
/>
|
|
716
|
+
)}
|
|
717
|
+
</Show>
|
|
718
|
+
<div class="relative">
|
|
719
|
+
<ConnectorLinesLive
|
|
720
|
+
depth={1}
|
|
721
|
+
isLast={isLast}
|
|
722
|
+
topPad="var(--spacing-vsp-xs)"
|
|
723
|
+
/>
|
|
546
724
|
<button
|
|
547
725
|
type="button"
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
style={{
|
|
551
|
-
aria-expanded={
|
|
552
|
-
aria-label={
|
|
726
|
+
on:click={toggle}
|
|
727
|
+
class="flex w-full items-center gap-hsp-md py-vsp-xs text-left text-small font-semibold text-fg hover:text-accent hover:underline focus:underline focus:text-accent break-words"
|
|
728
|
+
style={{ "padding-left": padLeft(1, true) }}
|
|
729
|
+
aria-expanded={computed(() => (expanded.value ? "true" : "false"))}
|
|
730
|
+
aria-label={computed(
|
|
731
|
+
() => `${expanded.value ? "Collapse" : "Expand"} ${label.value}`,
|
|
732
|
+
)}
|
|
553
733
|
data-zd-sidebar-open-key={storageKey}
|
|
554
734
|
>
|
|
555
|
-
<span
|
|
556
|
-
<ToggleChevron isExpanded={
|
|
735
|
+
<span class="aspect-square flex items-center justify-center w-[1.5rem] shrink-0 border border-muted">
|
|
736
|
+
<ToggleChevron isExpanded={expanded} className="text-muted" />
|
|
557
737
|
</span>
|
|
558
738
|
<span>{label}</span>
|
|
559
739
|
</button>
|
|
560
740
|
</div>
|
|
561
|
-
{
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
<
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
741
|
+
<Show when={expanded}>
|
|
742
|
+
{() => (
|
|
743
|
+
<div>
|
|
744
|
+
<For each={items} by={(item) => item.slug}>
|
|
745
|
+
{(item, index) => (
|
|
746
|
+
<TrayItem
|
|
747
|
+
item={item}
|
|
748
|
+
currentSlug={currentSlug}
|
|
749
|
+
isLast={computed(
|
|
750
|
+
() => index.value === items.value.length - 1,
|
|
751
|
+
)}
|
|
752
|
+
showDate
|
|
753
|
+
locale={locale}
|
|
754
|
+
dateFormats={dateFormats}
|
|
755
|
+
depth={GROUPED_TRAY_ITEM_DEPTH}
|
|
756
|
+
/>
|
|
757
|
+
)}
|
|
758
|
+
</For>
|
|
759
|
+
</div>
|
|
760
|
+
)}
|
|
761
|
+
</Show>
|
|
579
762
|
</div>
|
|
580
763
|
);
|
|
581
764
|
}
|
|
582
765
|
|
|
583
|
-
|
|
584
|
-
// a subtree's nodes/currentSlug/depth/forceOpen are unchanged, Preact can
|
|
585
|
-
// skip re-rendering the whole subtree.
|
|
586
|
-
const NodeList = memo(function NodeList({
|
|
766
|
+
function NodeList({
|
|
587
767
|
nodes,
|
|
588
768
|
currentSlug,
|
|
589
769
|
depth,
|
|
590
770
|
forceOpen,
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
currentSlug?: string;
|
|
594
|
-
depth: number;
|
|
595
|
-
forceOpen: boolean;
|
|
596
|
-
}) {
|
|
771
|
+
scopeControls,
|
|
772
|
+
}: { nodes: ReadonlySignal<SidebarNavNode[]>; depth: number } & ActiveProps) {
|
|
597
773
|
return (
|
|
598
|
-
|
|
599
|
-
{
|
|
600
|
-
const isLast = index === nodes.length - 1;
|
|
601
|
-
return
|
|
602
|
-
<
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
774
|
+
<For each={nodes} by={(node) => node.occurrenceId ?? node.slug}>
|
|
775
|
+
{(node, index) => {
|
|
776
|
+
const isLast = computed(() => index.value === nodes.value.length - 1);
|
|
777
|
+
return (
|
|
778
|
+
<Show
|
|
779
|
+
when={computed(() => node.value.children.length > 0)}
|
|
780
|
+
fallback={() => (
|
|
781
|
+
<LeafNode
|
|
782
|
+
node={node}
|
|
783
|
+
currentSlug={currentSlug}
|
|
784
|
+
depth={depth}
|
|
785
|
+
isLast={isLast}
|
|
786
|
+
forceOpen={forceOpen}
|
|
787
|
+
scopeControls={scopeControls}
|
|
788
|
+
/>
|
|
789
|
+
)}
|
|
790
|
+
>
|
|
791
|
+
{() => (
|
|
792
|
+
<CategoryNode
|
|
793
|
+
node={node}
|
|
794
|
+
currentSlug={currentSlug}
|
|
795
|
+
depth={depth}
|
|
796
|
+
isLast={isLast}
|
|
797
|
+
forceOpen={forceOpen}
|
|
798
|
+
scopeControls={scopeControls}
|
|
799
|
+
/>
|
|
800
|
+
)}
|
|
801
|
+
</Show>
|
|
618
802
|
);
|
|
619
|
-
}
|
|
620
|
-
|
|
803
|
+
}}
|
|
804
|
+
</For>
|
|
621
805
|
);
|
|
622
|
-
}
|
|
806
|
+
}
|
|
623
807
|
|
|
624
|
-
/** Check if currentSlug is anywhere in this node's subtree */
|
|
625
808
|
function subtreeContainsSlug(node: SidebarNavNode, slug?: string): boolean {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
809
|
+
return (
|
|
810
|
+
slug !== undefined &&
|
|
811
|
+
(node.slug === slug ||
|
|
812
|
+
node.children.some((child) => subtreeContainsSlug(child, slug)))
|
|
813
|
+
);
|
|
629
814
|
}
|
|
630
815
|
|
|
631
|
-
|
|
632
|
-
// filter-query re-renders.
|
|
633
|
-
const CategoryNode = memo(function CategoryNode({
|
|
816
|
+
function CategoryNode({
|
|
634
817
|
node,
|
|
635
818
|
currentSlug,
|
|
636
819
|
depth,
|
|
637
820
|
isLast,
|
|
638
821
|
forceOpen,
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
forceOpen: boolean;
|
|
645
|
-
}) {
|
|
646
|
-
const containsCurrent = useMemo(
|
|
647
|
-
() => subtreeContainsSlug(node, currentSlug),
|
|
648
|
-
[node, currentSlug],
|
|
822
|
+
scopeControls,
|
|
823
|
+
}: RowProps) {
|
|
824
|
+
const scope = getScope();
|
|
825
|
+
const containsCurrent = computed(() =>
|
|
826
|
+
subtreeContainsSlug(node.value, currentSlug.value),
|
|
649
827
|
);
|
|
650
|
-
const
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
if (
|
|
664
|
-
setOpen(true);
|
|
828
|
+
const active = computed(() => node.value.slug === currentSlug.value);
|
|
829
|
+
const occurrenceKey = () => node.value.occurrenceId ?? node.value.slug;
|
|
830
|
+
const open = signal(scopeControls?.expansion.value[occurrenceKey()] ?? (scopeControls?.defaultOpen(node.value) ?? (containsCurrent.value || !node.value.collapsed)));
|
|
831
|
+
scope.onActivate(() => {
|
|
832
|
+
if (!scopeControls && getOpenSet().has(node.value.slug)) open.value = true;
|
|
833
|
+
});
|
|
834
|
+
scope.effect(() => {
|
|
835
|
+
const slug = currentSlug.value;
|
|
836
|
+
const revision = scopeControls?.reveal.value;
|
|
837
|
+
if (slug !== undefined && subtreeContainsSlug(node.value, slug)) open.value = true;
|
|
838
|
+
void revision;
|
|
839
|
+
});
|
|
840
|
+
scope.effect(() => {
|
|
841
|
+
if (!scopeControls && open.value) {
|
|
665
842
|
const stored = getOpenSet();
|
|
666
|
-
stored.
|
|
667
|
-
|
|
668
|
-
}
|
|
669
|
-
}, [currentSlug]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
670
|
-
|
|
671
|
-
useEffect(() => {
|
|
672
|
-
if (open) {
|
|
673
|
-
const stored = getOpenSet();
|
|
674
|
-
if (!stored.has(node.slug)) {
|
|
675
|
-
stored.add(node.slug);
|
|
843
|
+
if (!stored.has(node.value.slug)) {
|
|
844
|
+
stored.add(node.value.slug);
|
|
676
845
|
saveOpenSet(stored);
|
|
677
846
|
}
|
|
678
847
|
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
848
|
+
});
|
|
849
|
+
const toggle = () => {
|
|
850
|
+
open.value = !open.value;
|
|
851
|
+
if (scopeControls) {
|
|
852
|
+
scopeControls.expansion.value = { ...scopeControls.expansion.value, [occurrenceKey()]: open.value };
|
|
853
|
+
scopeControls.save();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const stored = getOpenSet();
|
|
857
|
+
if (open.value) stored.add(node.value.slug);
|
|
858
|
+
else stored.delete(node.value.slug);
|
|
859
|
+
saveOpenSet(stored);
|
|
860
|
+
};
|
|
861
|
+
scope.effect(() => {
|
|
862
|
+
const stored = scopeControls?.expansion.value[occurrenceKey()];
|
|
863
|
+
if (scopeControls) open.value = stored ?? scopeControls.defaultOpen(node.value);
|
|
864
|
+
});
|
|
865
|
+
const expanded = computed(() => forceOpen.value || open.value);
|
|
866
|
+
const focusButton = scopeControls ? (
|
|
867
|
+
<button type="button" data-sidebar-focus data-sidebar-focus-scope={computed(() => node.value.occurrenceId ?? "")}
|
|
868
|
+
on:click={() => scopeControls.focus(node.value)}
|
|
869
|
+
aria-label={computed(() => `${scopeControls.focusLabel}: ${node.value.label}`)}
|
|
870
|
+
title={computed(() => `${scopeControls.focusLabel}: ${node.value.label}`)}
|
|
871
|
+
class={computed(() => `ml-auto shrink-0 rounded px-hsp-xs py-vsp-2xs ${active.value ? "text-bg hover:bg-bg/10 focus-visible:bg-bg/10" : "text-muted hover:bg-surface hover:text-fg focus-visible:bg-surface focus-visible:text-fg"}`)}>
|
|
872
|
+
<span aria-hidden="true">◎</span>
|
|
873
|
+
</button>
|
|
874
|
+
) : null;
|
|
696
875
|
const paddingLeft = padLeft(depth, true);
|
|
697
|
-
|
|
698
876
|
return (
|
|
699
|
-
<div
|
|
700
|
-
{
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
style={{
|
|
704
|
-
left: connectorLeft(depth),
|
|
705
|
-
top: 0,
|
|
706
|
-
bottom: 0,
|
|
707
|
-
}}
|
|
708
|
-
/>
|
|
877
|
+
<div
|
|
878
|
+
class={computed(
|
|
879
|
+
() =>
|
|
880
|
+
`${depth === 0 ? "border-t border-muted first:border-t-0" : ""} ${depth >= 1 && !isLast.value ? "relative" : ""}`,
|
|
709
881
|
)}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
{
|
|
882
|
+
>
|
|
883
|
+
<Show
|
|
884
|
+
when={computed(() => depth >= 1 && !isLast.value && expanded.value)}
|
|
885
|
+
>
|
|
886
|
+
{() => (
|
|
713
887
|
<div
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
888
|
+
class="absolute border-l border-solid border-muted z-local-1"
|
|
889
|
+
style={{ left: connectorLeft(depth), top: "0px", bottom: "0px" }}
|
|
890
|
+
/>
|
|
891
|
+
)}
|
|
892
|
+
</Show>
|
|
893
|
+
<div class="relative">
|
|
894
|
+
<ConnectorLinesLive
|
|
895
|
+
depth={depth}
|
|
896
|
+
isLast={isLast}
|
|
897
|
+
topPad="calc(0.15rem + var(--spacing-vsp-xs))"
|
|
898
|
+
/>
|
|
899
|
+
<Show
|
|
900
|
+
when={computed(() => !!node.value.href)}
|
|
901
|
+
fallback={() => (
|
|
902
|
+
<div class="flex flex-wrap items-center">
|
|
729
903
|
<button
|
|
730
904
|
type="button"
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
aria-
|
|
905
|
+
on:click={toggle}
|
|
906
|
+
class="flex flex-1 max-w-full items-center gap-hsp-md text-left text-small font-semibold py-vsp-xs text-fg hover:text-accent hover:underline focus:underline focus:text-accent break-words"
|
|
907
|
+
style={{ "padding-left": paddingLeft }}
|
|
908
|
+
aria-expanded={computed(() =>
|
|
909
|
+
expanded.value ? "true" : "false",
|
|
910
|
+
)}
|
|
911
|
+
aria-label={computed(
|
|
912
|
+
() =>
|
|
913
|
+
`${expanded.value ? "Collapse" : "Expand"} ${node.value.label}`,
|
|
914
|
+
)}
|
|
735
915
|
>
|
|
736
|
-
<
|
|
916
|
+
<span class="aspect-square flex items-center justify-center w-[1.5rem] shrink-0 border border-muted">
|
|
917
|
+
<ToggleChevron isExpanded={expanded} className="text-muted" />
|
|
918
|
+
</span>
|
|
919
|
+
<span
|
|
920
|
+
rawHtml={computed(() => smartBreakToHtml(node.value.label))}
|
|
921
|
+
/>
|
|
737
922
|
</button>
|
|
923
|
+
{focusButton}
|
|
924
|
+
</div>
|
|
925
|
+
)}
|
|
926
|
+
>
|
|
927
|
+
{() => (
|
|
928
|
+
<div
|
|
929
|
+
class={computed(
|
|
930
|
+
() =>
|
|
931
|
+
`flex w-full items-center text-small font-semibold pt-[0.15rem] ${active.value ? "bg-fg text-bg" : "text-fg"}`,
|
|
932
|
+
)}
|
|
933
|
+
>
|
|
934
|
+
<a
|
|
935
|
+
href={computed(() => node.value.href ?? "")}
|
|
936
|
+
aria-current={computed(() => (active.value ? "page" : "false"))}
|
|
937
|
+
class={computed(
|
|
938
|
+
() =>
|
|
939
|
+
`flex-1 flex items-start gap-hsp-xs py-vsp-xs hover:underline focus:underline break-words ${active.value ? "text-bg" : "text-fg hover:text-accent focus:text-accent"}`,
|
|
940
|
+
)}
|
|
941
|
+
style={{ "padding-left": paddingLeft }}
|
|
942
|
+
>
|
|
943
|
+
{depth === 0 ? (
|
|
944
|
+
<span class="flex h-[1lh] items-center">
|
|
945
|
+
<CategoryLinkIcon class="w-[14px]" />
|
|
946
|
+
</span>
|
|
947
|
+
) : null}
|
|
948
|
+
<span
|
|
949
|
+
rawHtml={computed(() => smartBreakToHtml(node.value.label))}
|
|
950
|
+
/>
|
|
951
|
+
</a>
|
|
952
|
+
{focusButton}
|
|
953
|
+
<button
|
|
954
|
+
type="button"
|
|
955
|
+
on:click={toggle}
|
|
956
|
+
class={computed(
|
|
957
|
+
() =>
|
|
958
|
+
`aspect-square flex items-center justify-center w-[1.5rem] border-y border-l hover:underline focus:underline ${active.value ? "border-bg/30" : "border-muted"}`,
|
|
959
|
+
)}
|
|
960
|
+
aria-expanded={computed(() =>
|
|
961
|
+
expanded.value ? "true" : "false",
|
|
962
|
+
)}
|
|
963
|
+
aria-label={computed(
|
|
964
|
+
() =>
|
|
965
|
+
`${expanded.value ? "Collapse" : "Expand"} ${node.value.label}`,
|
|
966
|
+
)}
|
|
967
|
+
>
|
|
968
|
+
<ToggleChevron
|
|
969
|
+
isExpanded={expanded}
|
|
970
|
+
className={computed(() =>
|
|
971
|
+
active.value ? "text-bg" : "text-muted",
|
|
972
|
+
)}
|
|
973
|
+
/>
|
|
974
|
+
</button>
|
|
975
|
+
</div>
|
|
976
|
+
)}
|
|
977
|
+
</Show>
|
|
978
|
+
</div>
|
|
979
|
+
<Show when={expanded}>
|
|
980
|
+
{() => (
|
|
981
|
+
<div>
|
|
982
|
+
<NodeList
|
|
983
|
+
nodes={computed(() => node.value.children)}
|
|
984
|
+
currentSlug={currentSlug}
|
|
985
|
+
depth={depth + 1}
|
|
986
|
+
forceOpen={forceOpen}
|
|
987
|
+
scopeControls={scopeControls}
|
|
988
|
+
/>
|
|
738
989
|
</div>
|
|
739
|
-
) : (
|
|
740
|
-
<button
|
|
741
|
-
type="button"
|
|
742
|
-
onClick={toggle}
|
|
743
|
-
className={`flex w-full items-center gap-hsp-md text-left text-small font-semibold py-vsp-xs text-fg hover:text-accent hover:underline focus:underline focus:text-accent break-words`}
|
|
744
|
-
style={{ paddingLeft }}
|
|
745
|
-
aria-expanded={isExpanded}
|
|
746
|
-
aria-label={isExpanded ? `Collapse ${node.label}` : `Expand ${node.label}`}
|
|
747
|
-
>
|
|
748
|
-
<span className="aspect-square flex items-center justify-center w-[1.5rem] shrink-0 border border-muted">
|
|
749
|
-
<ToggleChevron isExpanded={isExpanded} className="text-muted" />
|
|
750
|
-
</span>
|
|
751
|
-
<span dangerouslySetInnerHTML={{ __html: labelHtml }} />
|
|
752
|
-
</button>
|
|
753
990
|
)}
|
|
754
|
-
</
|
|
755
|
-
{isExpanded && (
|
|
756
|
-
<div>
|
|
757
|
-
<NodeList
|
|
758
|
-
nodes={node.children}
|
|
759
|
-
currentSlug={currentSlug}
|
|
760
|
-
depth={depth + 1}
|
|
761
|
-
forceOpen={forceOpen}
|
|
762
|
-
/>
|
|
763
|
-
</div>
|
|
764
|
-
)}
|
|
991
|
+
</Show>
|
|
765
992
|
</div>
|
|
766
993
|
);
|
|
767
|
-
}
|
|
994
|
+
}
|
|
768
995
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const LeafNode = memo(function LeafNode({
|
|
772
|
-
node,
|
|
773
|
-
currentSlug,
|
|
774
|
-
depth,
|
|
775
|
-
isLast,
|
|
776
|
-
}: {
|
|
777
|
-
node: SidebarNavNode;
|
|
778
|
-
currentSlug?: string;
|
|
779
|
-
depth: number;
|
|
780
|
-
isLast: boolean;
|
|
781
|
-
}) {
|
|
782
|
-
const labelHtml = useMemo(() => smartBreakToHtml(node.label), [node.label]);
|
|
783
|
-
if (!node.href) return null;
|
|
784
|
-
const isActive = node.slug === currentSlug;
|
|
996
|
+
function LeafNode({ node, currentSlug, depth, isLast }: RowProps) {
|
|
997
|
+
const active = computed(() => node.value.slug === currentSlug.value);
|
|
785
998
|
const isRoot = depth === 0;
|
|
786
999
|
const paddingLeft = padLeft(depth, isRoot);
|
|
787
|
-
|
|
788
|
-
const outerClass = isRoot
|
|
789
|
-
? "border-t border-muted"
|
|
790
|
-
: !isRoot && isLast
|
|
791
|
-
? "pb-vsp-md"
|
|
792
|
-
: "";
|
|
793
|
-
|
|
794
1000
|
const topPad = isRoot
|
|
795
1001
|
? "calc(var(--spacing-vsp-xs) + 0.15rem)"
|
|
796
1002
|
: "var(--spacing-vsp-2xs)";
|
|
797
|
-
|
|
798
1003
|
return (
|
|
799
|
-
<
|
|
800
|
-
|
|
801
|
-
<
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
aria-current={isActive ? "page" : undefined}
|
|
805
|
-
data-nav-active={!isRoot && isActive ? "" : undefined}
|
|
806
|
-
className={isRoot
|
|
807
|
-
? `flex items-start gap-hsp-xs py-[calc(var(--spacing-vsp-xs)+0.15rem)] pr-hsp-xs lg:pr-hsp-sm text-small font-semibold break-words ${
|
|
808
|
-
isActive ? "bg-fg text-bg" : "text-fg hover:text-accent hover:underline focus:underline focus:text-accent"
|
|
809
|
-
}`
|
|
810
|
-
: `block py-vsp-2xs pr-hsp-xs lg:pr-hsp-sm text-small break-words ${
|
|
811
|
-
isActive
|
|
812
|
-
? "bg-fg font-medium text-bg"
|
|
813
|
-
: "text-muted hover:text-accent hover:underline focus:underline focus:text-accent"
|
|
814
|
-
}`
|
|
815
|
-
}
|
|
816
|
-
style={{ paddingLeft }}
|
|
817
|
-
>
|
|
818
|
-
{isRoot && (
|
|
819
|
-
<span className="flex h-[1lh] items-center">
|
|
820
|
-
<CategoryLinkIcon className={`w-[14px] ${isActive ? "text-bg" : ""}`} />
|
|
821
|
-
</span>
|
|
1004
|
+
<Show when={computed(() => !!node.value.href)}>
|
|
1005
|
+
{() => (
|
|
1006
|
+
<div
|
|
1007
|
+
class={computed(() =>
|
|
1008
|
+
isRoot ? "border-t border-muted first:border-t-0" : isLast.value ? "pb-vsp-md" : "",
|
|
822
1009
|
)}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1010
|
+
>
|
|
1011
|
+
<div class="relative">
|
|
1012
|
+
<ConnectorLinesLive depth={depth} isLast={isLast} topPad={topPad} />
|
|
1013
|
+
<a
|
|
1014
|
+
href={computed(() => node.value.href ?? "")}
|
|
1015
|
+
aria-current={computed(() => (active.value ? "page" : "false"))}
|
|
1016
|
+
data-nav-active={computed(() =>
|
|
1017
|
+
!isRoot && active.value ? "" : null,
|
|
1018
|
+
)}
|
|
1019
|
+
class={computed(() =>
|
|
1020
|
+
isRoot
|
|
1021
|
+
? `flex items-start gap-hsp-xs py-[calc(var(--spacing-vsp-xs)_+_0.15rem)] pr-hsp-xs lg:pr-hsp-sm text-small font-semibold break-words ${active.value ? "bg-fg text-bg" : "text-fg hover:text-accent hover:underline focus:underline focus:text-accent"}`
|
|
1022
|
+
: `block py-vsp-2xs pr-hsp-xs lg:pr-hsp-sm text-small break-words ${active.value ? "bg-fg font-medium text-bg" : "text-muted hover:text-accent hover:underline focus:underline focus:text-accent"}`,
|
|
1023
|
+
)}
|
|
1024
|
+
style={{ "padding-left": paddingLeft }}
|
|
1025
|
+
>
|
|
1026
|
+
{isRoot ? (
|
|
1027
|
+
<span class="flex h-[1lh] items-center">
|
|
1028
|
+
<CategoryLinkIcon class="w-[14px]" />
|
|
1029
|
+
</span>
|
|
1030
|
+
) : null}
|
|
1031
|
+
<span
|
|
1032
|
+
rawHtml={computed(() => smartBreakToHtml(node.value.label))}
|
|
1033
|
+
/>
|
|
1034
|
+
</a>
|
|
1035
|
+
</div>
|
|
1036
|
+
</div>
|
|
1037
|
+
)}
|
|
1038
|
+
</Show>
|
|
827
1039
|
);
|
|
828
|
-
}
|
|
1040
|
+
}
|