blume 0.0.0 → 0.1.1
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/bin/blume.mjs +18 -0
- package/dist/cli/index.js +12696 -0
- package/dist/cli/index.js.map +145 -0
- package/dist/types/core/bridge.d.ts +24 -0
- package/dist/types/core/config.d.ts +35 -0
- package/dist/types/core/data.d.ts +129 -0
- package/dist/types/core/define-components.d.ts +27 -0
- package/dist/types/core/define-meta.d.ts +16 -0
- package/dist/types/core/deployment-env.d.ts +9 -0
- package/dist/types/core/diagnostics.d.ts +17 -0
- package/dist/types/core/i18n-ui.d.ts +500 -0
- package/dist/types/core/load-module.d.ts +7 -0
- package/dist/types/core/package-root.d.ts +17 -0
- package/dist/types/core/project.d.ts +9 -0
- package/dist/types/core/schema.d.ts +3451 -0
- package/dist/types/core/sources/types.d.ts +107 -0
- package/dist/types/core/types.d.ts +245 -0
- package/dist/types/core/ui-packs/ar.d.ts +3 -0
- package/dist/types/core/ui-packs/bg.d.ts +3 -0
- package/dist/types/core/ui-packs/bn.d.ts +3 -0
- package/dist/types/core/ui-packs/ca.d.ts +3 -0
- package/dist/types/core/ui-packs/cs.d.ts +3 -0
- package/dist/types/core/ui-packs/da.d.ts +3 -0
- package/dist/types/core/ui-packs/de.d.ts +3 -0
- package/dist/types/core/ui-packs/el.d.ts +3 -0
- package/dist/types/core/ui-packs/es.d.ts +3 -0
- package/dist/types/core/ui-packs/fa.d.ts +3 -0
- package/dist/types/core/ui-packs/fi.d.ts +3 -0
- package/dist/types/core/ui-packs/fr.d.ts +3 -0
- package/dist/types/core/ui-packs/he.d.ts +3 -0
- package/dist/types/core/ui-packs/hi.d.ts +3 -0
- package/dist/types/core/ui-packs/hr.d.ts +3 -0
- package/dist/types/core/ui-packs/hu.d.ts +3 -0
- package/dist/types/core/ui-packs/id.d.ts +3 -0
- package/dist/types/core/ui-packs/index.d.ts +13 -0
- package/dist/types/core/ui-packs/it.d.ts +3 -0
- package/dist/types/core/ui-packs/ja.d.ts +3 -0
- package/dist/types/core/ui-packs/ko.d.ts +3 -0
- package/dist/types/core/ui-packs/nl.d.ts +3 -0
- package/dist/types/core/ui-packs/no.d.ts +3 -0
- package/dist/types/core/ui-packs/pl.d.ts +3 -0
- package/dist/types/core/ui-packs/pt-br.d.ts +3 -0
- package/dist/types/core/ui-packs/pt.d.ts +3 -0
- package/dist/types/core/ui-packs/ro.d.ts +3 -0
- package/dist/types/core/ui-packs/ru.d.ts +3 -0
- package/dist/types/core/ui-packs/sk.d.ts +3 -0
- package/dist/types/core/ui-packs/sr.d.ts +3 -0
- package/dist/types/core/ui-packs/sv.d.ts +3 -0
- package/dist/types/core/ui-packs/th.d.ts +3 -0
- package/dist/types/core/ui-packs/tr.d.ts +3 -0
- package/dist/types/core/ui-packs/uk.d.ts +3 -0
- package/dist/types/core/ui-packs/vi.d.ts +3 -0
- package/dist/types/core/ui-packs/zh-tw.d.ts +3 -0
- package/dist/types/core/ui-packs/zh.d.ts +3 -0
- package/dist/types/core/version.d.ts +8 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/migrate/mintlify/config.d.ts +2 -0
- package/dist/types/migrate/mintlify/i18n.d.ts +7 -0
- package/dist/types/theme/fonts.d.ts +163 -0
- package/docs/01-quickstart.mdx +99 -0
- package/docs/02-deployment.mdx +129 -0
- package/docs/advanced/api-reference.mdx +114 -0
- package/docs/advanced/blog.mdx +121 -0
- package/docs/advanced/changelog.mdx +113 -0
- package/docs/advanced/custom-pages.mdx +268 -0
- package/docs/advanced/meta.ts +7 -0
- package/docs/changelog/v0-1-0.mdx +12 -0
- package/docs/changelog/v0-2-0.mdx +16 -0
- package/docs/configuration/ai.mdx +228 -0
- package/docs/configuration/analytics.mdx +98 -0
- package/docs/configuration/customization.mdx +91 -0
- package/docs/configuration/export.mdx +70 -0
- package/docs/configuration/index.mdx +290 -0
- package/docs/configuration/meta.ts +15 -0
- package/docs/configuration/search.mdx +172 -0
- package/docs/configuration/seo.mdx +196 -0
- package/docs/configuration/theming.mdx +178 -0
- package/docs/content/components.mdx +651 -0
- package/docs/content/i18n.mdx +205 -0
- package/docs/content/index.mdx +161 -0
- package/docs/content/islands.mdx +94 -0
- package/docs/content/meta.mdx +119 -0
- package/docs/content/meta.ts +15 -0
- package/docs/content/navigation.mdx +168 -0
- package/docs/content/sources.mdx +216 -0
- package/docs/content/syntax.mdx +445 -0
- package/docs/index.mdx +112 -0
- package/docs/reference/cli.mdx +43 -0
- package/docs/reference/frontmatter.mdx +74 -0
- package/docs/reference/meta.ts +7 -0
- package/package.json +150 -6
- package/src/ai/ask.ts +93 -0
- package/src/ai/llms.ts +64 -0
- package/src/ai/markdown.ts +31 -0
- package/src/ai/mcp/data.ts +74 -0
- package/src/ai/mcp/discovery.ts +49 -0
- package/src/ai/mcp/server.ts +225 -0
- package/src/ai/mcp/tools.ts +47 -0
- package/src/assets/icon.png +0 -0
- package/src/astro/examples.ts +104 -0
- package/src/astro/generate.ts +1006 -0
- package/src/astro/index.ts +4 -0
- package/src/astro/integration.ts +74 -0
- package/src/astro/islands.ts +131 -0
- package/src/astro/markdown-negotiation.ts +68 -0
- package/src/astro/pages.ts +87 -0
- package/src/astro/templates.ts +1326 -0
- package/src/cli/commands/add.ts +81 -0
- package/src/cli/commands/build.ts +103 -0
- package/src/cli/commands/dev.ts +108 -0
- package/src/cli/commands/doctor.ts +74 -0
- package/src/cli/commands/eject.ts +57 -0
- package/src/cli/commands/init.ts +98 -0
- package/src/cli/commands/migrate.ts +39 -0
- package/src/cli/commands/preview.ts +39 -0
- package/src/cli/commands/sync.ts +52 -0
- package/src/cli/commands/validate.ts +61 -0
- package/src/cli/index.ts +35 -0
- package/src/cli/log.ts +37 -0
- package/src/cli/prepare.ts +80 -0
- package/src/components/Icon.astro +99 -0
- package/src/components/content/Accordion.astro +8 -0
- package/src/components/content/AccordionItem.astro +121 -0
- package/src/components/content/AutoTypeTable.astro +51 -0
- package/src/components/content/Badge.astro +124 -0
- package/src/components/content/Callout.astro +73 -0
- package/src/components/content/Card.astro +104 -0
- package/src/components/content/CardGroup.astro +14 -0
- package/src/components/content/CodeBlock.astro +28 -0
- package/src/components/content/CodeGroup.astro +13 -0
- package/src/components/content/Color.astro +15 -0
- package/src/components/content/ColorItem.astro +87 -0
- package/src/components/content/ColorRow.astro +10 -0
- package/src/components/content/Column.astro +6 -0
- package/src/components/content/Columns.astro +9 -0
- package/src/components/content/Component.astro +65 -0
- package/src/components/content/Diff.astro +44 -0
- package/src/components/content/Expandable.astro +11 -0
- package/src/components/content/FileTree.astro +8 -0
- package/src/components/content/Frame.astro +70 -0
- package/src/components/content/GithubInfo.astro +110 -0
- package/src/components/content/Math.astro +24 -0
- package/src/components/content/Panel.astro +20 -0
- package/src/components/content/Prompt.astro +129 -0
- package/src/components/content/Step.astro +34 -0
- package/src/components/content/Steps.astro +20 -0
- package/src/components/content/Tab.astro +46 -0
- package/src/components/content/Tabs.astro +273 -0
- package/src/components/content/Tile.astro +42 -0
- package/src/components/content/Tooltip.astro +68 -0
- package/src/components/content/Tree.astro +300 -0
- package/src/components/content/TreeFile.astro +15 -0
- package/src/components/content/TreeFolder.astro +62 -0
- package/src/components/content/TypeTable.astro +106 -0
- package/src/components/content/Update.astro +66 -0
- package/src/components/content/Visibility.astro +12 -0
- package/src/components/content/Warning.astro +9 -0
- package/src/components/content/auto-type-table.ts +141 -0
- package/src/components/content/diff.ts +95 -0
- package/src/components/content/github-info.ts +79 -0
- package/src/components/content/mermaid-element.ts +68 -0
- package/src/components/github-mark.ts +9 -0
- package/src/components/index.ts +14 -0
- package/src/components/islands/AskAI.astro +12 -0
- package/src/components/islands/ask-ai.tsx +156 -0
- package/src/components/layout/Analytics.astro +63 -0
- package/src/components/layout/Banner.astro +50 -0
- package/src/components/layout/Breadcrumbs.astro +31 -0
- package/src/components/layout/Favicon.astro +22 -0
- package/src/components/layout/Fonts.astro +14 -0
- package/src/components/layout/Header.astro +188 -0
- package/src/components/layout/LanguageSwitcher.astro +56 -0
- package/src/components/layout/NavTree.astro +462 -0
- package/src/components/layout/PageActions.astro +438 -0
- package/src/components/layout/PageFeedback.astro +58 -0
- package/src/components/layout/PageLayout.astro +173 -0
- package/src/components/layout/Pagination.astro +56 -0
- package/src/components/layout/ReferenceLayout.astro +107 -0
- package/src/components/layout/RootLayout.astro +537 -0
- package/src/components/layout/Search.astro +608 -0
- package/src/components/layout/TableOfContents.astro +68 -0
- package/src/components/layout/analytics-client.ts +38 -0
- package/src/components/layout/head-scripts.ts +19 -0
- package/src/components/layout/nav-utils.ts +87 -0
- package/src/components/layout/overrides.ts +32 -0
- package/src/components/layout/search/algolia.ts +43 -0
- package/src/components/layout/search/endpoint.ts +22 -0
- package/src/components/layout/search/flexsearch.ts +52 -0
- package/src/components/layout/search/orama-cloud.ts +41 -0
- package/src/components/layout/search/orama.ts +26 -0
- package/src/components/layout/search/pagefind.ts +43 -0
- package/src/components/layout/search/types.ts +163 -0
- package/src/components/layout/search/typesense.ts +60 -0
- package/src/components/layout/toc-element.ts +108 -0
- package/src/core/bridge.ts +92 -0
- package/src/core/config.ts +112 -0
- package/src/core/content.ts +50 -0
- package/src/core/data.ts +128 -0
- package/src/core/define-components.ts +34 -0
- package/src/core/define-meta.ts +20 -0
- package/src/core/deployment-env.ts +73 -0
- package/src/core/diagnostics.ts +104 -0
- package/src/core/frontmatter.ts +43 -0
- package/src/core/graph.ts +128 -0
- package/src/core/i18n-ui.ts +171 -0
- package/src/core/i18n.ts +169 -0
- package/src/core/last-modified.ts +88 -0
- package/src/core/links.ts +352 -0
- package/src/core/load-module.ts +15 -0
- package/src/core/manifest.ts +126 -0
- package/src/core/meta.ts +97 -0
- package/src/core/navigation.ts +392 -0
- package/src/core/package-root.ts +37 -0
- package/src/core/project-graph.ts +153 -0
- package/src/core/project.ts +56 -0
- package/src/core/schema.ts +1057 -0
- package/src/core/server-features.ts +23 -0
- package/src/core/sources/assets.ts +77 -0
- package/src/core/sources/cache.ts +122 -0
- package/src/core/sources/filesystem.ts +99 -0
- package/src/core/sources/mdx-remote.ts +215 -0
- package/src/core/sources/mintlify.ts +161 -0
- package/src/core/sources/normalize.ts +243 -0
- package/src/core/sources/notion.ts +440 -0
- package/src/core/sources/portable-text.ts +143 -0
- package/src/core/sources/read.ts +36 -0
- package/src/core/sources/resolve.ts +158 -0
- package/src/core/sources/sanity.ts +218 -0
- package/src/core/sources/types.ts +105 -0
- package/src/core/tsconfig-aliases.ts +201 -0
- package/src/core/types.ts +261 -0
- package/src/core/ui-packs/ar.ts +47 -0
- package/src/core/ui-packs/bg.ts +47 -0
- package/src/core/ui-packs/bn.ts +47 -0
- package/src/core/ui-packs/ca.ts +47 -0
- package/src/core/ui-packs/cs.ts +47 -0
- package/src/core/ui-packs/da.ts +47 -0
- package/src/core/ui-packs/de.ts +47 -0
- package/src/core/ui-packs/el.ts +47 -0
- package/src/core/ui-packs/es.ts +47 -0
- package/src/core/ui-packs/fa.ts +47 -0
- package/src/core/ui-packs/fi.ts +47 -0
- package/src/core/ui-packs/fr.ts +47 -0
- package/src/core/ui-packs/he.ts +47 -0
- package/src/core/ui-packs/hi.ts +47 -0
- package/src/core/ui-packs/hr.ts +47 -0
- package/src/core/ui-packs/hu.ts +47 -0
- package/src/core/ui-packs/id.ts +47 -0
- package/src/core/ui-packs/index.ts +87 -0
- package/src/core/ui-packs/it.ts +47 -0
- package/src/core/ui-packs/ja.ts +47 -0
- package/src/core/ui-packs/ko.ts +47 -0
- package/src/core/ui-packs/nl.ts +47 -0
- package/src/core/ui-packs/no.ts +47 -0
- package/src/core/ui-packs/pl.ts +47 -0
- package/src/core/ui-packs/pt-br.ts +47 -0
- package/src/core/ui-packs/pt.ts +47 -0
- package/src/core/ui-packs/ro.ts +47 -0
- package/src/core/ui-packs/ru.ts +47 -0
- package/src/core/ui-packs/sk.ts +47 -0
- package/src/core/ui-packs/sr.ts +47 -0
- package/src/core/ui-packs/sv.ts +47 -0
- package/src/core/ui-packs/th.ts +47 -0
- package/src/core/ui-packs/tr.ts +47 -0
- package/src/core/ui-packs/uk.ts +47 -0
- package/src/core/ui-packs/vi.ts +47 -0
- package/src/core/ui-packs/zh-tw.ts +47 -0
- package/src/core/ui-packs/zh.ts +47 -0
- package/src/core/version.ts +23 -0
- package/src/deploy/robots.ts +20 -0
- package/src/deploy/rss.ts +128 -0
- package/src/deploy/sitemap.ts +28 -0
- package/src/index.ts +39 -0
- package/src/markdown/code-title.ts +71 -0
- package/src/markdown/directives.ts +83 -0
- package/src/markdown/heading-anchors.ts +137 -0
- package/src/markdown/index.ts +228 -0
- package/src/markdown/inline-code.ts +108 -0
- package/src/markdown/language-icon.ts +172 -0
- package/src/markdown/math.ts +32 -0
- package/src/markdown/mdast.ts +48 -0
- package/src/markdown/mermaid.ts +37 -0
- package/src/markdown/package-commands.ts +159 -0
- package/src/markdown/package-install.ts +40 -0
- package/src/migrate/fumadocs/config.ts +155 -0
- package/src/migrate/fumadocs/content.ts +365 -0
- package/src/migrate/fumadocs/frontmatter.ts +18 -0
- package/src/migrate/fumadocs/groups.ts +230 -0
- package/src/migrate/fumadocs/index.ts +337 -0
- package/src/migrate/fumadocs/meta.ts +244 -0
- package/src/migrate/migrate.ts +53 -0
- package/src/migrate/mintlify/config.ts +1040 -0
- package/src/migrate/mintlify/content.ts +98 -0
- package/src/migrate/mintlify/frontmatter.ts +126 -0
- package/src/migrate/mintlify/i18n.ts +51 -0
- package/src/migrate/mintlify/icons.ts +128 -0
- package/src/migrate/mintlify/index.ts +266 -0
- package/src/migrate/mintlify/snippets.ts +306 -0
- package/src/migrate/mintlify/transform.ts +80 -0
- package/src/migrate/nextra/content.ts +46 -0
- package/src/migrate/nextra/frontmatter.ts +40 -0
- package/src/migrate/nextra/index.ts +374 -0
- package/src/migrate/nextra/meta.ts +266 -0
- package/src/migrate/shared.ts +720 -0
- package/src/migrate/starlight/config.ts +459 -0
- package/src/migrate/starlight/content.ts +78 -0
- package/src/migrate/starlight/frontmatter.ts +111 -0
- package/src/migrate/starlight/i18n.ts +54 -0
- package/src/migrate/starlight/index.ts +131 -0
- package/src/og/card.ts +92 -0
- package/src/og/index.ts +2 -0
- package/src/openapi/scalar.ts +246 -0
- package/src/registry/eject.ts +310 -0
- package/src/registry/registry.ts +100 -0
- package/src/registry/rewrite-imports.ts +39 -0
- package/src/runtime/index.ts +14 -0
- package/src/search/build.ts +23 -0
- package/src/search/documents.ts +164 -0
- package/src/search/orama-index.ts +66 -0
- package/src/search/providers.ts +91 -0
- package/src/search/sync/algolia.ts +30 -0
- package/src/search/sync/index.ts +50 -0
- package/src/search/sync/orama-cloud.ts +40 -0
- package/src/search/sync/typesense.ts +65 -0
- package/src/seo/jsonld.ts +113 -0
- package/src/theme/entry.ts +637 -0
- package/src/theme/fonts.ts +198 -0
- package/src/theme/icons.ts +184 -0
- package/src/theme/palette.ts +143 -0
- package/src/theme/twoslash.ts +81 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { NavNode } from "../../core/types.ts";
|
|
3
|
+
import Icon from "../Icon.astro";
|
|
4
|
+
import Self from "./NavTree.astro";
|
|
5
|
+
|
|
6
|
+
interface PagePanel {
|
|
7
|
+
id: string;
|
|
8
|
+
parentId: string;
|
|
9
|
+
label: string;
|
|
10
|
+
route?: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
children: NavNode[];
|
|
13
|
+
active: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
items: NavNode[];
|
|
18
|
+
currentRoute: string;
|
|
19
|
+
depth?: number;
|
|
20
|
+
/** Id prefix used to address `page`-mode panels (`${prefix}.${index}`). */
|
|
21
|
+
idPrefix?: string;
|
|
22
|
+
/** True only for the top-level call; renders the `<blume-nav>` panel stack. */
|
|
23
|
+
root?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
items,
|
|
28
|
+
currentRoute,
|
|
29
|
+
depth = 0,
|
|
30
|
+
idPrefix = "n",
|
|
31
|
+
root = depth === 0,
|
|
32
|
+
} = Astro.props;
|
|
33
|
+
|
|
34
|
+
const badgeClass =
|
|
35
|
+
"shrink-0 rounded-full bg-muted px-1.5 py-0.5 font-medium text-[0.65rem] text-muted-foreground leading-none";
|
|
36
|
+
const deprecatedClass =
|
|
37
|
+
"shrink-0 rounded-full bg-amber-500/10 px-1.5 py-0.5 font-medium text-[0.65rem] text-amber-700 leading-none dark:text-amber-300";
|
|
38
|
+
|
|
39
|
+
const hasActivePage = (item: NavNode): boolean =>
|
|
40
|
+
item.kind === "page"
|
|
41
|
+
? item.route === currentRoute
|
|
42
|
+
: item.route === currentRoute || item.children.some(hasActivePage);
|
|
43
|
+
|
|
44
|
+
// Collect every `page`-mode group in the tree as a panel that is hoisted to the
|
|
45
|
+
// top of the `<blume-nav>` stack. Panel ids match the `${prefix}.${index}`
|
|
46
|
+
// scheme the row renderer uses for `data-nav-to`, so a drill-in trigger and its
|
|
47
|
+
// panel always agree. `flat`/`group` ancestors keep their children in the same
|
|
48
|
+
// visual panel (parentId unchanged); a `page` group opens a new panel.
|
|
49
|
+
const collectPanels = (
|
|
50
|
+
nodes: NavNode[],
|
|
51
|
+
prefix: string,
|
|
52
|
+
parentId: string
|
|
53
|
+
): PagePanel[] => {
|
|
54
|
+
const out: PagePanel[] = [];
|
|
55
|
+
nodes.forEach((node, index) => {
|
|
56
|
+
if (node.kind !== "group") {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const id = `${prefix}.${index}`;
|
|
60
|
+
if ((node.display ?? "flat") === "page") {
|
|
61
|
+
out.push({
|
|
62
|
+
active: hasActivePage(node),
|
|
63
|
+
children: node.children,
|
|
64
|
+
icon: node.icon,
|
|
65
|
+
id,
|
|
66
|
+
label: node.label,
|
|
67
|
+
parentId,
|
|
68
|
+
route: node.route,
|
|
69
|
+
});
|
|
70
|
+
out.push(...collectPanels(node.children, id, id));
|
|
71
|
+
} else {
|
|
72
|
+
out.push(...collectPanels(node.children, id, parentId));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return out;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const panels = root ? collectPanels(items, idPrefix, "root") : [];
|
|
79
|
+
// Open the deepest panel that contains the current page, so a real page load
|
|
80
|
+
// lands on the right sub-panel (and degrades gracefully without JS).
|
|
81
|
+
const activePanels = panels.filter((panel) => panel.active);
|
|
82
|
+
const initialId =
|
|
83
|
+
activePanels.length > 0
|
|
84
|
+
? activePanels.reduce((a, b) =>
|
|
85
|
+
b.id.split(".").length > a.id.split(".").length ? b : a
|
|
86
|
+
).id
|
|
87
|
+
: "root";
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
root && panels.length > 0 ? (
|
|
92
|
+
<blume-nav class="block" data-initial={initialId}>
|
|
93
|
+
<div data-nav-panel="root" hidden={initialId !== "root"}>
|
|
94
|
+
<Self
|
|
95
|
+
currentRoute={currentRoute}
|
|
96
|
+
depth={0}
|
|
97
|
+
idPrefix={idPrefix}
|
|
98
|
+
items={items}
|
|
99
|
+
root={false}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
{panels.map((panel) => (
|
|
103
|
+
<div data-nav-panel={panel.id} hidden={panel.id !== initialId}>
|
|
104
|
+
<div class="mb-3 flex items-center gap-1.5">
|
|
105
|
+
<button
|
|
106
|
+
aria-label="Back"
|
|
107
|
+
class="-ml-1 flex shrink-0 items-center justify-center rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
108
|
+
data-nav-back={panel.parentId}
|
|
109
|
+
type="button"
|
|
110
|
+
>
|
|
111
|
+
<Icon name="arrow-left" size={16} />
|
|
112
|
+
</button>
|
|
113
|
+
{panel.route ? (
|
|
114
|
+
<a
|
|
115
|
+
aria-current={panel.route === currentRoute ? "page" : undefined}
|
|
116
|
+
class="min-w-0 flex-1 truncate font-semibold text-foreground text-sm hover:underline"
|
|
117
|
+
href={panel.route}
|
|
118
|
+
>
|
|
119
|
+
{panel.label}
|
|
120
|
+
</a>
|
|
121
|
+
) : (
|
|
122
|
+
<span class="min-w-0 flex-1 truncate font-semibold text-foreground text-sm">
|
|
123
|
+
{panel.label}
|
|
124
|
+
</span>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
<Self
|
|
128
|
+
currentRoute={currentRoute}
|
|
129
|
+
depth={1}
|
|
130
|
+
idPrefix={panel.id}
|
|
131
|
+
items={panel.children}
|
|
132
|
+
root={false}
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
))}
|
|
136
|
+
</blume-nav>
|
|
137
|
+
) : (
|
|
138
|
+
<ul class="m-0 list-none p-0">
|
|
139
|
+
{items.map((item, index) => {
|
|
140
|
+
if (item.kind === "page") {
|
|
141
|
+
return (
|
|
142
|
+
<li>
|
|
143
|
+
<a
|
|
144
|
+
aria-current={item.route === currentRoute ? "page" : undefined}
|
|
145
|
+
class="block rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:font-medium aria-[current=page]:text-foreground"
|
|
146
|
+
href={item.route}
|
|
147
|
+
>
|
|
148
|
+
<span class="flex min-w-0 items-center gap-2">
|
|
149
|
+
{item.icon && (
|
|
150
|
+
<Icon
|
|
151
|
+
class="shrink-0 text-muted-foreground"
|
|
152
|
+
name={item.icon}
|
|
153
|
+
size={14}
|
|
154
|
+
/>
|
|
155
|
+
)}
|
|
156
|
+
<span class="min-w-0 flex-1 truncate">{item.label}</span>
|
|
157
|
+
{item.badge && <span class={badgeClass}>{item.badge}</span>}
|
|
158
|
+
{item.deprecated && (
|
|
159
|
+
<span class={deprecatedClass}>deprecated</span>
|
|
160
|
+
)}
|
|
161
|
+
</span>
|
|
162
|
+
</a>
|
|
163
|
+
</li>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const display = item.display ?? "flat";
|
|
168
|
+
const id = `${idPrefix}.${index}`;
|
|
169
|
+
// Flat groups are section headers and want breathing room; collapsible
|
|
170
|
+
// `group`/`page` rows are single rows and should stack tightly.
|
|
171
|
+
const spacing =
|
|
172
|
+
index === 0 ? "mt-0" : display === "flat" ? "mt-6" : "mt-0.5";
|
|
173
|
+
|
|
174
|
+
// `page`: a single row that drills into this group's panel.
|
|
175
|
+
if (display === "page") {
|
|
176
|
+
return (
|
|
177
|
+
<li class={spacing}>
|
|
178
|
+
<button
|
|
179
|
+
class="flex w-full min-w-0 items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-left font-medium text-foreground text-sm transition-colors hover:bg-muted"
|
|
180
|
+
data-nav-to={id}
|
|
181
|
+
type="button"
|
|
182
|
+
>
|
|
183
|
+
{item.icon && (
|
|
184
|
+
<Icon
|
|
185
|
+
class="shrink-0 text-muted-foreground"
|
|
186
|
+
name={item.icon}
|
|
187
|
+
size={14}
|
|
188
|
+
/>
|
|
189
|
+
)}
|
|
190
|
+
<span class="min-w-0 flex-1 truncate">{item.label}</span>
|
|
191
|
+
{item.badge && <span class={badgeClass}>{item.badge}</span>}
|
|
192
|
+
<Icon
|
|
193
|
+
class="shrink-0 text-muted-foreground"
|
|
194
|
+
name="chevron-right"
|
|
195
|
+
size={14}
|
|
196
|
+
/>
|
|
197
|
+
</button>
|
|
198
|
+
</li>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// `group`: a collapsible `<details>` disclosure.
|
|
203
|
+
if (display === "group") {
|
|
204
|
+
const active = hasActivePage(item);
|
|
205
|
+
const open = depth === 0 || active || item.collapsed === false;
|
|
206
|
+
return (
|
|
207
|
+
<li class={spacing}>
|
|
208
|
+
<details class="group" open={open}>
|
|
209
|
+
<summary class="mb-1 flex cursor-pointer list-none items-center gap-1.5 rounded-[0.65rem] px-2.5 py-1.5 font-medium text-foreground text-sm transition-colors hover:bg-muted [&::-webkit-details-marker]:hidden">
|
|
210
|
+
{item.route ? (
|
|
211
|
+
<a
|
|
212
|
+
aria-current={
|
|
213
|
+
item.route === currentRoute ? "page" : undefined
|
|
214
|
+
}
|
|
215
|
+
class="-my-1 flex min-w-0 flex-1 items-center gap-1.5 rounded py-1 text-foreground transition-colors aria-[current=page]:font-semibold"
|
|
216
|
+
href={item.route}
|
|
217
|
+
>
|
|
218
|
+
{item.icon && (
|
|
219
|
+
<Icon
|
|
220
|
+
class="shrink-0 text-muted-foreground"
|
|
221
|
+
name={item.icon}
|
|
222
|
+
size={14}
|
|
223
|
+
/>
|
|
224
|
+
)}
|
|
225
|
+
<span class="min-w-0 flex-1 truncate">{item.label}</span>
|
|
226
|
+
{item.badge && (
|
|
227
|
+
<span class={badgeClass}>{item.badge}</span>
|
|
228
|
+
)}
|
|
229
|
+
</a>
|
|
230
|
+
) : (
|
|
231
|
+
<>
|
|
232
|
+
{item.icon && (
|
|
233
|
+
<Icon
|
|
234
|
+
class="shrink-0 text-muted-foreground"
|
|
235
|
+
name={item.icon}
|
|
236
|
+
size={14}
|
|
237
|
+
/>
|
|
238
|
+
)}
|
|
239
|
+
<span class="min-w-0 flex-1 truncate">{item.label}</span>
|
|
240
|
+
{item.badge && (
|
|
241
|
+
<span class={badgeClass}>{item.badge}</span>
|
|
242
|
+
)}
|
|
243
|
+
</>
|
|
244
|
+
)}
|
|
245
|
+
<span class="shrink-0 text-muted-foreground transition-transform group-open:rotate-90">
|
|
246
|
+
<Icon name="chevron-right" size={13} />
|
|
247
|
+
</span>
|
|
248
|
+
</summary>
|
|
249
|
+
<div class="space-y-0.5 border-border border-l pl-3">
|
|
250
|
+
<Self
|
|
251
|
+
currentRoute={currentRoute}
|
|
252
|
+
depth={depth + 1}
|
|
253
|
+
idPrefix={id}
|
|
254
|
+
items={item.children}
|
|
255
|
+
root={false}
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
258
|
+
</details>
|
|
259
|
+
</li>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// `flat` (default): a non-collapsible header with its items beneath.
|
|
264
|
+
return (
|
|
265
|
+
<li class={spacing}>
|
|
266
|
+
<p class="mb-2 flex min-w-0 items-center gap-1.5 px-2.5 font-medium text-foreground text-sm">
|
|
267
|
+
{item.route ? (
|
|
268
|
+
<a
|
|
269
|
+
aria-current={item.route === currentRoute ? "page" : undefined}
|
|
270
|
+
class="-ml-1 flex min-w-0 flex-1 items-center gap-1.5 rounded px-1 py-0.5 text-foreground transition-colors hover:bg-muted aria-[current=page]:bg-muted"
|
|
271
|
+
href={item.route}
|
|
272
|
+
>
|
|
273
|
+
{item.icon && (
|
|
274
|
+
<Icon
|
|
275
|
+
class="shrink-0 text-muted-foreground"
|
|
276
|
+
name={item.icon}
|
|
277
|
+
size={14}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
<span class="min-w-0 truncate">{item.label}</span>
|
|
281
|
+
{item.badge && (
|
|
282
|
+
<span class={`ml-auto ${badgeClass}`}>{item.badge}</span>
|
|
283
|
+
)}
|
|
284
|
+
</a>
|
|
285
|
+
) : (
|
|
286
|
+
<>
|
|
287
|
+
{item.icon && (
|
|
288
|
+
<Icon
|
|
289
|
+
class="shrink-0 text-muted-foreground"
|
|
290
|
+
name={item.icon}
|
|
291
|
+
size={14}
|
|
292
|
+
/>
|
|
293
|
+
)}
|
|
294
|
+
<span class="min-w-0 truncate">{item.label}</span>
|
|
295
|
+
{item.badge && (
|
|
296
|
+
<span class={`ml-auto ${badgeClass}`}>{item.badge}</span>
|
|
297
|
+
)}
|
|
298
|
+
</>
|
|
299
|
+
)}
|
|
300
|
+
</p>
|
|
301
|
+
<div class="space-y-0.5">
|
|
302
|
+
<Self
|
|
303
|
+
currentRoute={currentRoute}
|
|
304
|
+
depth={depth + 1}
|
|
305
|
+
idPrefix={id}
|
|
306
|
+
items={item.children}
|
|
307
|
+
root={false}
|
|
308
|
+
/>
|
|
309
|
+
</div>
|
|
310
|
+
</li>
|
|
311
|
+
);
|
|
312
|
+
})}
|
|
313
|
+
</ul>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
<script>
|
|
318
|
+
const DURATION = 260;
|
|
319
|
+
const EASING = "cubic-bezier(0.33, 1, 0.68, 1)";
|
|
320
|
+
|
|
321
|
+
class BlumeNav extends HTMLElement {
|
|
322
|
+
connectedCallback() {
|
|
323
|
+
if (this.dataset.ready) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
this.dataset.ready = "1";
|
|
327
|
+
this.addEventListener("click", (event) => {
|
|
328
|
+
const target = event.target;
|
|
329
|
+
if (!(target instanceof Element)) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const drill = target.closest("[data-nav-to]");
|
|
333
|
+
if (drill && this.contains(drill)) {
|
|
334
|
+
const id = drill.getAttribute("data-nav-to");
|
|
335
|
+
if (id && this.panel(id)) {
|
|
336
|
+
event.preventDefault();
|
|
337
|
+
this.show(id, true);
|
|
338
|
+
}
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const back = target.closest("[data-nav-back]");
|
|
342
|
+
if (back && this.contains(back)) {
|
|
343
|
+
event.preventDefault();
|
|
344
|
+
this.show(back.getAttribute("data-nav-back") || "root", true);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
// Initial panel is route-driven, so it snaps into place without a slide.
|
|
348
|
+
this.show(this.dataset.initial || "root", false);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
panel(id) {
|
|
352
|
+
return this.querySelector(`[data-nav-panel="${id}"]`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
depth(id) {
|
|
356
|
+
return id === "root" ? 0 : id.split(".").length;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
reduced() {
|
|
360
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
show(id, animate) {
|
|
364
|
+
const next = this.panel(id) || this.panel("root");
|
|
365
|
+
if (!next) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const resolved = next.getAttribute("data-nav-panel");
|
|
369
|
+
const prev = this.active;
|
|
370
|
+
if (prev === next) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// Settle any in-flight slide before starting the next one.
|
|
374
|
+
if (this.finishSlide) {
|
|
375
|
+
this.finishSlide();
|
|
376
|
+
}
|
|
377
|
+
this.active = next;
|
|
378
|
+
this.dataset.active = resolved;
|
|
379
|
+
|
|
380
|
+
if (!(animate && prev) || this.reduced()) {
|
|
381
|
+
for (const panel of this.querySelectorAll("[data-nav-panel]")) {
|
|
382
|
+
panel.hidden = panel !== next;
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const forward =
|
|
388
|
+
this.depth(resolved) > this.depth(prev.getAttribute("data-nav-panel"));
|
|
389
|
+
this.slide(prev, next, forward);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
slide(out, into, forward) {
|
|
393
|
+
const rtl = getComputedStyle(this).direction === "rtl" ? -1 : 1;
|
|
394
|
+
const sign = (forward ? -1 : 1) * rtl;
|
|
395
|
+
|
|
396
|
+
const fromHeight = out.offsetHeight;
|
|
397
|
+
this.style.position = "relative";
|
|
398
|
+
this.style.overflow = "hidden";
|
|
399
|
+
this.style.height = `${fromHeight}px`;
|
|
400
|
+
into.hidden = false;
|
|
401
|
+
for (const panel of [out, into]) {
|
|
402
|
+
panel.style.position = "absolute";
|
|
403
|
+
panel.style.top = "0";
|
|
404
|
+
panel.style.insetInlineStart = "0";
|
|
405
|
+
panel.style.width = "100%";
|
|
406
|
+
}
|
|
407
|
+
const toHeight = into.offsetHeight;
|
|
408
|
+
this.style.height = `${toHeight}px`;
|
|
409
|
+
|
|
410
|
+
const options = { duration: DURATION, easing: EASING };
|
|
411
|
+
const animations = [
|
|
412
|
+
out.animate(
|
|
413
|
+
[
|
|
414
|
+
{ transform: "translateX(0)" },
|
|
415
|
+
{ transform: `translateX(${sign * 100}%)` },
|
|
416
|
+
],
|
|
417
|
+
options
|
|
418
|
+
),
|
|
419
|
+
into.animate(
|
|
420
|
+
[
|
|
421
|
+
{ transform: `translateX(${-sign * 100}%)` },
|
|
422
|
+
{ transform: "translateX(0)" },
|
|
423
|
+
],
|
|
424
|
+
options
|
|
425
|
+
),
|
|
426
|
+
this.animate(
|
|
427
|
+
[{ height: `${fromHeight}px` }, { height: `${toHeight}px` }],
|
|
428
|
+
options
|
|
429
|
+
),
|
|
430
|
+
];
|
|
431
|
+
|
|
432
|
+
let done = false;
|
|
433
|
+
const finish = () => {
|
|
434
|
+
if (done) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
done = true;
|
|
438
|
+
for (const animation of animations) {
|
|
439
|
+
animation.cancel();
|
|
440
|
+
}
|
|
441
|
+
out.hidden = true;
|
|
442
|
+
for (const panel of [out, into]) {
|
|
443
|
+
panel.style.position = "";
|
|
444
|
+
panel.style.top = "";
|
|
445
|
+
panel.style.insetInlineStart = "";
|
|
446
|
+
panel.style.width = "";
|
|
447
|
+
panel.style.transform = "";
|
|
448
|
+
}
|
|
449
|
+
this.style.position = "";
|
|
450
|
+
this.style.overflow = "";
|
|
451
|
+
this.style.height = "";
|
|
452
|
+
this.finishSlide = null;
|
|
453
|
+
};
|
|
454
|
+
this.finishSlide = finish;
|
|
455
|
+
animations[0].finished.then(finish, finish);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (!customElements.get("blume-nav")) {
|
|
460
|
+
customElements.define("blume-nav", BlumeNav);
|
|
461
|
+
}
|
|
462
|
+
</script>
|