blume 0.0.0 → 0.1.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/bin/blume.mjs +18 -0
- package/dist/cli/index.js +11989 -0
- package/dist/cli/index.js.map +141 -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 +187 -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 +271 -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 +565 -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 +140 -6
- package/src/ai/ask.ts +93 -0
- package/src/ai/llms.ts +65 -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/generate.ts +878 -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 +28 -0
- package/src/astro/templates.ts +1199 -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 +60 -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/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/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 +40 -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/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 +15 -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/Pagination.astro +56 -0
- package/src/components/layout/ReferenceLayout.astro +102 -0
- package/src/components/layout/RootLayout.astro +533 -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/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/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/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 +336 -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 +216 -0
- package/src/core/sources/mintlify.ts +161 -0
- package/src/core/sources/normalize.ts +227 -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/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 +27 -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 +159 -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 +106 -0
- package/src/migrate/fumadocs/content.ts +365 -0
- package/src/migrate/fumadocs/frontmatter.ts +18 -0
- package/src/migrate/fumadocs/index.ts +252 -0
- package/src/migrate/fumadocs/meta.ts +114 -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 +305 -0
- package/src/migrate/mintlify/transform.ts +81 -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 +623 -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 +263 -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 +165 -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 +608 -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,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
3
|
+
import type { LocaleSwitchOption, Navigation } from "../../core/types.ts";
|
|
4
|
+
import { GITHUB_MARK } from "../github-mark.ts";
|
|
5
|
+
import Icon from "../Icon.astro";
|
|
6
|
+
import LanguageSwitcher from "./LanguageSwitcher.astro";
|
|
7
|
+
import Search from "./Search.astro";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
site: { title: string };
|
|
11
|
+
logo?: {
|
|
12
|
+
svg?: string;
|
|
13
|
+
light?: string;
|
|
14
|
+
dark?: string;
|
|
15
|
+
alt: string;
|
|
16
|
+
href: string;
|
|
17
|
+
} | null;
|
|
18
|
+
navigation: Navigation;
|
|
19
|
+
route: string;
|
|
20
|
+
searchEnabled: boolean;
|
|
21
|
+
askEnabled?: boolean;
|
|
22
|
+
// The mobile menu button toggles the docs sidebar drawer; custom pages
|
|
23
|
+
// without a sidebar (e.g. a landing page) pass `false` to hide it.
|
|
24
|
+
hasSidebar?: boolean;
|
|
25
|
+
searchStrings?: UIStrings["search"];
|
|
26
|
+
switcherStrings?: UIStrings["languageSwitcher"];
|
|
27
|
+
localeSwitch?: LocaleSwitchOption[];
|
|
28
|
+
/** Active locale for per-language search filtering. */
|
|
29
|
+
searchLocale?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
site,
|
|
34
|
+
logo,
|
|
35
|
+
navigation,
|
|
36
|
+
route,
|
|
37
|
+
searchEnabled,
|
|
38
|
+
askEnabled = false,
|
|
39
|
+
hasSidebar = true,
|
|
40
|
+
searchStrings,
|
|
41
|
+
switcherStrings,
|
|
42
|
+
localeSwitch,
|
|
43
|
+
searchLocale,
|
|
44
|
+
} = Astro.props;
|
|
45
|
+
|
|
46
|
+
// The logo is resolved upstream: an inline `svg` (so a currentColor mark
|
|
47
|
+
// follows the theme) or image URL(s) for an <img>.
|
|
48
|
+
const logoSvg = logo?.svg;
|
|
49
|
+
const logoLight = logo?.light;
|
|
50
|
+
const logoDark = logo?.dark ?? logo?.light;
|
|
51
|
+
const logoAlt = logo?.alt ?? "";
|
|
52
|
+
const brandHref = logo?.href ?? "/";
|
|
53
|
+
|
|
54
|
+
const iconButton =
|
|
55
|
+
"inline-flex size-9 cursor-pointer items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
|
|
56
|
+
|
|
57
|
+
// Delegated handlers for the theme toggle, mobile nav drawer, and banner
|
|
58
|
+
// dismiss. Living with the header means every page that renders it — docs pages
|
|
59
|
+
// and custom pages alike — gets identical behavior from one source.
|
|
60
|
+
//
|
|
61
|
+
// Toggling the theme flips colors; without suppressing transitions the animated
|
|
62
|
+
// styles fade while the non-animated ones snap, so we disable all transitions
|
|
63
|
+
// for the swap (next-themes' disableTransitionOnChange trick), force a
|
|
64
|
+
// synchronous style flush, then restore them on the next tick.
|
|
65
|
+
const clickScript = `document.addEventListener("click",(e)=>{const t=e.target.closest("[data-blume-theme-toggle]");if(t){const d=document.documentElement;const next=d.dataset.theme==="dark"?"light":"dark";const s=document.createElement("style");s.appendChild(document.createTextNode("*,*::before,*::after{transition:none!important}"));document.head.appendChild(s);d.dataset.theme=next;localStorage.setItem("blume-theme",next);window.getComputedStyle(d).opacity;setTimeout(()=>document.head.removeChild(s),1);}const n=e.target.closest("[data-blume-nav-toggle]");if(n){document.documentElement.toggleAttribute("data-blume-nav-open");}const b=e.target.closest("[data-blume-banner-dismiss]");if(b){const bar=b.closest("[data-blume-banner]");const k=bar&&bar.getAttribute("data-banner-key");if(k){localStorage.setItem("blume-banner:"+k,"1");}document.documentElement.setAttribute("data-blume-banner-hidden","");}});`;
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
<header
|
|
69
|
+
class="sticky top-0 z-40 flex h-16 items-center gap-3 bg-background/90 px-4 backdrop-blur md:px-6"
|
|
70
|
+
>
|
|
71
|
+
{
|
|
72
|
+
hasSidebar && (
|
|
73
|
+
<button
|
|
74
|
+
aria-label="Toggle navigation"
|
|
75
|
+
class={`${iconButton} lg:hidden`}
|
|
76
|
+
data-blume-nav-toggle
|
|
77
|
+
type="button"
|
|
78
|
+
>
|
|
79
|
+
<Icon name="menu" size={20} />
|
|
80
|
+
</button>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
<a
|
|
84
|
+
class="inline-flex items-center gap-2 font-semibold text-base text-foreground"
|
|
85
|
+
href={brandHref}
|
|
86
|
+
>
|
|
87
|
+
{
|
|
88
|
+
logoSvg && (
|
|
89
|
+
<span
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
class="inline-flex h-5 items-center [&>svg]:h-5 [&>svg]:w-auto"
|
|
92
|
+
set:html={logoSvg}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
{
|
|
97
|
+
!logoSvg &&
|
|
98
|
+
logoLight &&
|
|
99
|
+
(logoLight === logoDark ? (
|
|
100
|
+
<img alt={logoAlt} class="h-5 w-auto" src={logoLight} />
|
|
101
|
+
) : (
|
|
102
|
+
<>
|
|
103
|
+
<img alt={logoAlt} class="h-5 w-auto dark:hidden" src={logoLight} />
|
|
104
|
+
<img
|
|
105
|
+
alt={logoAlt}
|
|
106
|
+
class="hidden h-5 w-auto dark:block"
|
|
107
|
+
src={logoDark}
|
|
108
|
+
/>
|
|
109
|
+
</>
|
|
110
|
+
))
|
|
111
|
+
}
|
|
112
|
+
{site.title}
|
|
113
|
+
</a>
|
|
114
|
+
{
|
|
115
|
+
navigation.tabs.length > 0 && (
|
|
116
|
+
<nav aria-label="Sections" class="hidden gap-1 md:flex">
|
|
117
|
+
{navigation.tabs.map((tab) => (
|
|
118
|
+
<a
|
|
119
|
+
aria-current={
|
|
120
|
+
route === tab.path ||
|
|
121
|
+
(tab.path !== "/" && route.startsWith(tab.path))
|
|
122
|
+
? "page"
|
|
123
|
+
: undefined
|
|
124
|
+
}
|
|
125
|
+
class="rounded-full px-3 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:text-foreground"
|
|
126
|
+
href={tab.path}
|
|
127
|
+
>
|
|
128
|
+
{tab.label}
|
|
129
|
+
</a>
|
|
130
|
+
))}
|
|
131
|
+
</nav>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
<div class="flex-1"></div>
|
|
135
|
+
<slot name="ask" />
|
|
136
|
+
{
|
|
137
|
+
localeSwitch && localeSwitch.length > 1 && (
|
|
138
|
+
<LanguageSwitcher
|
|
139
|
+
label={switcherStrings?.label ?? "Language"}
|
|
140
|
+
options={localeSwitch}
|
|
141
|
+
untranslatedLabel={switcherStrings?.untranslated ?? "Not translated"}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
{
|
|
146
|
+
searchEnabled && (
|
|
147
|
+
<Search
|
|
148
|
+
askEnabled={askEnabled}
|
|
149
|
+
locale={searchLocale}
|
|
150
|
+
navigation={navigation}
|
|
151
|
+
strings={searchStrings}
|
|
152
|
+
/>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
<div class="flex items-center gap-2">
|
|
156
|
+
{
|
|
157
|
+
navigation.repoUrl && (
|
|
158
|
+
<a
|
|
159
|
+
aria-label="GitHub repository"
|
|
160
|
+
class={iconButton}
|
|
161
|
+
href={navigation.repoUrl}
|
|
162
|
+
rel="noreferrer"
|
|
163
|
+
target="_blank"
|
|
164
|
+
>
|
|
165
|
+
<svg
|
|
166
|
+
aria-hidden="true"
|
|
167
|
+
fill="currentColor"
|
|
168
|
+
height="18"
|
|
169
|
+
set:html={GITHUB_MARK}
|
|
170
|
+
viewBox="0 0 16 16"
|
|
171
|
+
width="18"
|
|
172
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
173
|
+
/>
|
|
174
|
+
</a>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
<button
|
|
178
|
+
aria-label="Toggle color theme"
|
|
179
|
+
class={iconButton}
|
|
180
|
+
data-blume-theme-toggle
|
|
181
|
+
type="button"
|
|
182
|
+
>
|
|
183
|
+
<span class="inline-flex dark:hidden"><Icon name="sun" size={18} /></span>
|
|
184
|
+
<span class="hidden dark:inline-flex"><Icon name="moon" size={18} /></span>
|
|
185
|
+
</button>
|
|
186
|
+
</div>
|
|
187
|
+
</header>
|
|
188
|
+
<script is:inline set:html={clickScript} />
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { LocaleSwitchOption } from "../../core/types.ts";
|
|
3
|
+
import Icon from "../Icon.astro";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
options: LocaleSwitchOption[];
|
|
7
|
+
label: string;
|
|
8
|
+
untranslatedLabel: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { options, label, untranslatedLabel } = Astro.props;
|
|
12
|
+
const current = options.find((option) => option.current);
|
|
13
|
+
|
|
14
|
+
const iconButton =
|
|
15
|
+
"inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-full border border-border bg-background px-3 text-muted-foreground text-sm transition-colors hover:border-foreground hover:text-foreground";
|
|
16
|
+
const menuRowClass =
|
|
17
|
+
"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-start text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground";
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
options.length > 1 && (
|
|
22
|
+
<details class="group relative">
|
|
23
|
+
<summary
|
|
24
|
+
aria-label={label}
|
|
25
|
+
class={`${iconButton} list-none [&::-webkit-details-marker]:hidden`}
|
|
26
|
+
>
|
|
27
|
+
<Icon name="globe" size={16} />
|
|
28
|
+
<span class="max-sm:hidden">{current?.label ?? label}</span>
|
|
29
|
+
<Icon
|
|
30
|
+
class="transition-transform group-open:rotate-180"
|
|
31
|
+
name="chevron-down"
|
|
32
|
+
size={14}
|
|
33
|
+
/>
|
|
34
|
+
</summary>
|
|
35
|
+
<div class="absolute end-0 z-50 mt-2 min-w-44 rounded-blume border border-border bg-background p-1 shadow-xl">
|
|
36
|
+
{options.map((option) => (
|
|
37
|
+
<a
|
|
38
|
+
aria-current={option.current ? "true" : undefined}
|
|
39
|
+
class={menuRowClass}
|
|
40
|
+
href={option.href}
|
|
41
|
+
hreflang={option.code}
|
|
42
|
+
lang={option.code}
|
|
43
|
+
>
|
|
44
|
+
<span class="flex-1">{option.label}</span>
|
|
45
|
+
{option.untranslated && (
|
|
46
|
+
<span class="text-muted-foreground text-xs italic">
|
|
47
|
+
{untranslatedLabel}
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
{option.current && <Icon name="check" size={14} />}
|
|
51
|
+
</a>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
</details>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -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>
|