@tenphi/renderer 0.20.4 → 0.20.5
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/README.md +2 -2
- package/dist/client/navigation.js +2 -6
- package/dist/client/navigation.js.map +1 -1
- package/dist/client/popover-motion.d.ts +6 -0
- package/dist/client/popover-motion.d.ts.map +1 -0
- package/dist/client/popover-motion.js +14 -0
- package/dist/client/popover-motion.js.map +1 -0
- package/dist/components/GlobalStyles.js +78 -38
- package/dist/components/HeaderLinks.astro +14 -3
- package/dist/components/Icon.astro +1 -1
- package/dist/components/LayoutComponents.js +31 -40
- package/dist/components/Preview.astro +1 -1
- package/dist/components/SelectPopover.astro +64 -0
- package/dist/components/TastyComponents.js +16 -2
- package/dist/components/VersionSwitcher.astro +11 -18
- package/dist/components/select-popover-styles.js +107 -0
- package/dist/localization.d.ts.map +1 -1
- package/dist/localization.js +19 -5
- package/dist/localization.js.map +1 -1
- package/dist/overrides/Header.astro +8 -0
- package/dist/overrides/LanguageSelect.astro +29 -35
- package/dist/overrides/MobileMenuFooter.astro +1 -1
- package/dist/overrides/ThemeSelect.astro +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ collections remain independent.
|
|
|
10
10
|
|
|
11
11
|
`@tenphi/renderer/styling` exports `tasty`, `useGlobalStyles`, `mergeStyles`,
|
|
12
12
|
the `Styles` type, and the `defineComponent` and `resolveComponentStyles`
|
|
13
|
-
helpers for merging `theme.
|
|
13
|
+
helpers for merging `theme.customStyles` into custom components. The facade exposes
|
|
14
14
|
the same API through `@tenphi/cookbook/styling`.
|
|
15
15
|
|
|
16
16
|
`@tenphi/renderer/eslint-plugin` exports the default Tasty ESLint plugin,
|
|
@@ -19,5 +19,5 @@ the same API through `@tenphi/cookbook/styling`.
|
|
|
19
19
|
Use `extends: "@tenphi/renderer"` in `tasty.config.ts` to inherit this preset
|
|
20
20
|
and list only your additional tokens, states, or presets.
|
|
21
21
|
It matches `@tenphi/cookbook/eslint-plugin`; see
|
|
22
|
-
[Linting custom styles](https://cookbook.tenphi.me/
|
|
22
|
+
[Linting custom styles](https://cookbook.tenphi.me/custom-components/#linting-custom-styles)
|
|
23
23
|
for ESLint and oxlint setup.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { initializePopoverMotion } from "./popover-motion.js";
|
|
1
2
|
//#region src/client/navigation.ts
|
|
2
3
|
var CookbookSidebarPane = class extends HTMLElement {
|
|
3
4
|
#listeners;
|
|
@@ -93,12 +94,7 @@ var CookbookHeaderLinks = class extends HTMLElement {
|
|
|
93
94
|
const close = () => {
|
|
94
95
|
if (panel?.matches(":popover-open")) panel.hidePopover();
|
|
95
96
|
};
|
|
96
|
-
panel
|
|
97
|
-
if (panel.matches(":popover-open")) {
|
|
98
|
-
panel.getBoundingClientRect();
|
|
99
|
-
panel.setAttribute("data-open", "");
|
|
100
|
-
} else panel.removeAttribute("data-open");
|
|
101
|
-
}, { signal });
|
|
97
|
+
if (panel) initializePopoverMotion(panel, signal);
|
|
102
98
|
desktop.addEventListener("change", () => {
|
|
103
99
|
if (desktop.matches) close();
|
|
104
100
|
}, { signal });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","names":[],"sources":["../../src/client/navigation.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"navigation.js","names":[],"sources":["../../src/client/navigation.ts"],"sourcesContent":["import { initializePopoverMotion } from \"./popover-motion.js\";\n\nclass CookbookSidebarPane extends HTMLElement {\n #listeners?: AbortController;\n #inert = new Map<HTMLElement, boolean>();\n #trigger: HTMLButtonElement | undefined;\n\n connectedCallback() {\n this.#listeners?.abort();\n this.#listeners = new AbortController();\n const { signal } = this.#listeners;\n this.#trigger =\n document.querySelector<HTMLButtonElement>(\n '.td-menu-button[popovertarget=\"cookbook__sidebar\"]',\n ) ?? undefined;\n const desktop = matchMedia(\"(min-width: 50rem)\");\n const syncViewport = () => {\n if (desktop.matches) {\n if (this.matches(\":popover-open\")) this.hidePopover();\n this.#release();\n this.popover = null;\n this.removeAttribute(\"role\");\n this.removeAttribute(\"aria-modal\");\n } else {\n this.popover = \"auto\";\n this.setAttribute(\"role\", \"dialog\");\n this.setAttribute(\"aria-modal\", \"true\");\n }\n };\n syncViewport();\n desktop.addEventListener(\"change\", syncViewport, { signal });\n this.addEventListener(\n \"toggle\",\n () => {\n const open = !desktop.matches && this.matches(\":popover-open\");\n this.#trigger?.setAttribute(\"aria-expanded\", String(open));\n if (open) {\n // Establish the closed position after the popover enters the top\n // layer so its attribute change transitions from an actual layout.\n // Tasty's global SSR renderer does not preserve @starting-style.\n this.getBoundingClientRect();\n this.setAttribute(\"data-open\", \"\");\n for (const element of document.querySelectorAll<HTMLElement>(\n \".header, .main-frame, .cookbook-skip-link\",\n )) {\n this.#inert.set(element, element.inert);\n element.inert = true;\n }\n this.querySelector<HTMLElement>(\"[autofocus]\")?.focus({\n preventScroll: true,\n });\n } else {\n const restoreFocus =\n this.contains(document.activeElement) ||\n document.activeElement === document.body;\n this.#release();\n if (restoreFocus && !desktop.matches) this.#trigger?.focus();\n }\n },\n { signal },\n );\n this.addEventListener(\n \"keydown\",\n (event) => {\n if (desktop.matches || !this.matches(\":popover-open\")) return;\n const sections = this.querySelector<HTMLDetailsElement>(\n \".td-mobile-tabs details[open]\",\n );\n if (event.key === \"Escape\" && sections) {\n event.preventDefault();\n sections.open = false;\n sections.querySelector(\"summary\")?.focus();\n return;\n }\n if (event.key !== \"Tab\") return;\n const focusable = Array.from(\n this.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), select:not([disabled]), input:not([disabled]), summary, [tabindex=\"0\"]',\n ),\n ).filter(\n (element) =>\n element.tabIndex >= 0 && element.getClientRects().length > 0,\n );\n const first = focusable[0];\n const last = focusable.at(-1);\n if (!first || !last) {\n event.preventDefault();\n this.focus();\n return;\n }\n if (\n event.shiftKey &&\n (document.activeElement === first || document.activeElement === this)\n ) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n },\n { signal },\n );\n this.addEventListener(\n \"click\",\n (event) => {\n if (\n !event.defaultPrevented &&\n event.target instanceof Element &&\n event.target.closest(\"a[href]\") &&\n this.matches(\":popover-open\")\n )\n this.hidePopover();\n },\n { signal },\n );\n }\n\n #release() {\n this.removeAttribute(\"data-open\");\n for (const [element, inert] of this.#inert) element.inert = inert;\n this.#inert.clear();\n this.#trigger?.setAttribute(\"aria-expanded\", \"false\");\n }\n\n disconnectedCallback() {\n this.#listeners?.abort();\n this.#release();\n }\n}\n\nclass CookbookHeaderLinks extends HTMLElement {\n #listeners?: AbortController;\n connectedCallback() {\n this.#listeners?.abort();\n this.#listeners = new AbortController();\n const { signal } = this.#listeners;\n const panel = this.querySelector<HTMLElement>(\"[popover]\");\n const desktop = matchMedia(\"(min-width: 50rem)\");\n const close = () => {\n if (panel?.matches(\":popover-open\")) panel.hidePopover();\n };\n if (panel) initializePopoverMotion(panel, signal);\n desktop.addEventListener(\n \"change\",\n () => {\n if (desktop.matches) close();\n },\n { signal },\n );\n panel?.addEventListener(\n \"click\",\n (event) => {\n if (event.target instanceof Element && event.target.closest(\"a[href]\"))\n close();\n },\n { signal },\n );\n }\n disconnectedCallback() {\n this.#listeners?.abort();\n }\n}\n\nif (!customElements.get(\"cookbook-sidebar-pane\"))\n customElements.define(\"cookbook-sidebar-pane\", CookbookSidebarPane);\nif (!customElements.get(\"cookbook-header-links\"))\n customElements.define(\"cookbook-header-links\", CookbookHeaderLinks);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/client/popover-motion.d.ts
|
|
2
|
+
/** Animate popovers after they have entered the top layer. */
|
|
3
|
+
declare function initializePopoverMotion(panel: HTMLElement, signal?: AbortSignal): void;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { initializePopoverMotion };
|
|
6
|
+
//# sourceMappingURL=popover-motion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover-motion.d.ts","names":[],"sources":["../../src/client/popover-motion.ts"],"mappings":";;iBACgB,wBACd,OAAO,aACP,SAAS"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/client/popover-motion.ts
|
|
2
|
+
/** Animate popovers after they have entered the top layer. */
|
|
3
|
+
function initializePopoverMotion(panel, signal) {
|
|
4
|
+
panel.addEventListener("toggle", () => {
|
|
5
|
+
if (panel.matches(":popover-open")) {
|
|
6
|
+
panel.getBoundingClientRect();
|
|
7
|
+
panel.setAttribute("data-open", "");
|
|
8
|
+
} else panel.removeAttribute("data-open");
|
|
9
|
+
}, signal ? { signal } : void 0);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { initializePopoverMotion };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=popover-motion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover-motion.js","names":[],"sources":["../../src/client/popover-motion.ts"],"sourcesContent":["/** Animate popovers after they have entered the top layer. */\nexport function initializePopoverMotion(\n panel: HTMLElement,\n signal?: AbortSignal,\n): void {\n panel.addEventListener(\n \"toggle\",\n () => {\n if (panel.matches(\":popover-open\")) {\n panel.getBoundingClientRect();\n panel.setAttribute(\"data-open\", \"\");\n } else {\n panel.removeAttribute(\"data-open\");\n }\n },\n signal ? { signal } : undefined,\n );\n}\n"],"mappings":";;AACA,SAAgB,wBACd,OACA,QACM;CACN,MAAM,iBACJ,gBACM;EACJ,IAAI,MAAM,QAAQ,eAAe,GAAG;GAClC,MAAM,sBAAsB;GAC5B,MAAM,aAAa,aAAa,EAAE;EACpC,OACE,MAAM,gBAAgB,WAAW;CAErC,GACA,SAAS,EAAE,OAAO,IAAI,KAAA,CACxB;AACF"}
|
|
@@ -9,6 +9,7 @@ import closeIcon from "../icons/close.svg?raw";
|
|
|
9
9
|
import copyIcon from "../icons/copy.svg?raw";
|
|
10
10
|
import searchIcon from "../icons/search.svg?raw";
|
|
11
11
|
import { resolveComponentStyles } from "./component-styles.js";
|
|
12
|
+
import { selectPopoverStyles } from "./select-popover-styles.js";
|
|
12
13
|
import { svgIconUrl } from "./svg-icon.js";
|
|
13
14
|
import { configureCookbookStates } from "./tasty-states.js";
|
|
14
15
|
import { getDefaultFontUsage, getFontFaces } from "../theme/fonts.js";
|
|
@@ -935,8 +936,9 @@ export default function GlobalStyles() {
|
|
|
935
936
|
marginBlockStart: "0",
|
|
936
937
|
marginBlockEnd: "0",
|
|
937
938
|
},
|
|
939
|
+
// Linked Cards are block content even though their root is an anchor.
|
|
938
940
|
BlockSpacing: {
|
|
939
|
-
$: ":not(a):not(strong):not(em):not(del):not(span):not(input):not(code):not(br) + :not(a):not(strong):not(em):not(del):not(span):not(input):not(code):not(br):not(li):not(dt):not(dd):not(.not-content *)",
|
|
941
|
+
$: ":not(a):not(strong):not(em):not(del):not(span):not(input):not(code):not(br) + :not(a):not(strong):not(em):not(del):not(span):not(input):not(code):not(br):not(li):not(dt):not(dd):not(.not-content *), :not(a):not(strong):not(em):not(del):not(span):not(input):not(code):not(br) + a.td-card, a.td-card + :not(a):not(strong):not(em):not(del):not(span):not(input):not(code):not(br):not(li):not(dt):not(dd):not(.not-content *), a.td-card + a.td-card",
|
|
940
942
|
marginBlockStart: "($gap * 3)",
|
|
941
943
|
},
|
|
942
944
|
HeadingSpacing: {
|
|
@@ -1173,21 +1175,21 @@ export default function GlobalStyles() {
|
|
|
1173
1175
|
lineHeight: "0",
|
|
1174
1176
|
},
|
|
1175
1177
|
DiffLine: {
|
|
1176
|
-
$: "pre.td-diff .line",
|
|
1178
|
+
$: "pre.td-diff > code > .line",
|
|
1177
1179
|
display: "block",
|
|
1178
1180
|
inlinePadding: "1rem",
|
|
1179
1181
|
preset: "code",
|
|
1180
1182
|
},
|
|
1181
1183
|
EmptyDiffLine: {
|
|
1182
|
-
$: "pre.td-diff .line:empty::before",
|
|
1184
|
+
$: "pre.td-diff > code > .line:empty::before",
|
|
1183
1185
|
content: '"\\200b"',
|
|
1184
1186
|
},
|
|
1185
1187
|
InsertedLine: {
|
|
1186
|
-
$: "pre.td-diff .td-diff-line--inserted",
|
|
1188
|
+
$: "pre.td-diff > code > .td-diff-line--inserted",
|
|
1187
1189
|
fill: "#green-surface",
|
|
1188
1190
|
},
|
|
1189
1191
|
DeletedLine: {
|
|
1190
|
-
$: "pre.td-diff .td-diff-line--deleted",
|
|
1192
|
+
$: "pre.td-diff > code > .td-diff-line--deleted",
|
|
1191
1193
|
fill: "#red-surface",
|
|
1192
1194
|
},
|
|
1193
1195
|
}),
|
|
@@ -1431,47 +1433,85 @@ export default function GlobalStyles() {
|
|
|
1431
1433
|
}),
|
|
1432
1434
|
);
|
|
1433
1435
|
|
|
1436
|
+
const languageSelectStyles = selectPopoverStyles();
|
|
1434
1437
|
useGlobalStyles(
|
|
1435
1438
|
"cookbook-language-select",
|
|
1436
1439
|
resolveComponentStyles("LanguageSelect", {
|
|
1437
|
-
display: "
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
alignItems: "center",
|
|
1443
|
-
color: "#text-soft",
|
|
1444
|
-
},
|
|
1445
|
-
HoverLabel: { $: "label:hover", color: "#text" },
|
|
1446
|
-
LabelIcon: {
|
|
1447
|
-
$: ".label-icon",
|
|
1448
|
-
position: "absolute",
|
|
1449
|
-
zIndex: "1",
|
|
1450
|
-
insetInlineStart: "$gap",
|
|
1451
|
-
pointerEvents: "none",
|
|
1440
|
+
display: "flex",
|
|
1441
|
+
flexShrink: "0",
|
|
1442
|
+
"$popover-transition": languageSelectStyles["$popover-transition"],
|
|
1443
|
+
Compact: {
|
|
1444
|
+
$: "&[data-compact]",
|
|
1452
1445
|
},
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
appearance: "none",
|
|
1446
|
+
Trigger: {
|
|
1447
|
+
...languageSelectStyles.Trigger,
|
|
1456
1448
|
inlineSize: "8rem",
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
paddingInlineEnd: "($gap * 3.5)",
|
|
1460
|
-
color: "#text-soft",
|
|
1461
|
-
border: "0",
|
|
1462
|
-
radius: "$header-control-radius",
|
|
1449
|
+
blockSize: "$control-height",
|
|
1450
|
+
padding: "0 $gap",
|
|
1463
1451
|
fill: "#clear",
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
textOverflow: "ellipsis",
|
|
1452
|
+
border: "0",
|
|
1453
|
+
transition: "color $transition, fill $transition",
|
|
1467
1454
|
},
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1455
|
+
HoverTrigger: languageSelectStyles.HoverTrigger,
|
|
1456
|
+
ActiveTrigger: {
|
|
1457
|
+
$: "> button:active",
|
|
1458
|
+
color: "#text",
|
|
1459
|
+
fill: "#surface-2-pressed",
|
|
1460
|
+
},
|
|
1461
|
+
LabelIcon: {
|
|
1462
|
+
$: "> button > .label-icon",
|
|
1463
|
+
display: "block",
|
|
1464
|
+
flexShrink: "0",
|
|
1465
|
+
inlineSize: { "": "1.25rem", "@mobile": "1.125rem" },
|
|
1466
|
+
blockSize: { "": "1.25rem", "@mobile": "1.125rem" },
|
|
1467
|
+
},
|
|
1468
|
+
TriggerLabel: languageSelectStyles.TriggerLabel,
|
|
1469
|
+
Caret: languageSelectStyles.Caret,
|
|
1470
|
+
CompactTrigger: {
|
|
1471
|
+
$: "&[data-compact] > button",
|
|
1472
|
+
inlineSize: "$docs-menu-button-size",
|
|
1473
|
+
blockSize: "$docs-menu-button-size",
|
|
1474
|
+
padding: "0",
|
|
1475
|
+
},
|
|
1476
|
+
CompactLabel: {
|
|
1477
|
+
$: "&[data-compact] > button > .trigger-label",
|
|
1478
|
+
hide: true,
|
|
1473
1479
|
},
|
|
1474
|
-
|
|
1480
|
+
CompactCaret: { $: "&[data-compact] > button > .caret", hide: true },
|
|
1481
|
+
Label: { $: "> button" },
|
|
1482
|
+
HoverLabel: { $: "> button:hover" },
|
|
1483
|
+
Select: { $: "> button" },
|
|
1484
|
+
CompactSelect: { $: "&[data-compact] > button" },
|
|
1485
|
+
CompactLabelIcon: { $: "&[data-compact] > button > .label-icon" },
|
|
1486
|
+
SidebarTrigger: {
|
|
1487
|
+
$: "&[data-sidebar] > button",
|
|
1488
|
+
anchorName: "--language-trigger",
|
|
1489
|
+
},
|
|
1490
|
+
Panel: {
|
|
1491
|
+
...languageSelectStyles.Panel,
|
|
1492
|
+
blockInset: { "": "4rem start", "@mobile": "3.5rem start" },
|
|
1493
|
+
inlineInset:
|
|
1494
|
+
"max($docs-nav-pad-x, ((100vw - $layout-width) / 2 + $docs-sidebar-pad-x)) end",
|
|
1495
|
+
},
|
|
1496
|
+
SidebarPanel: {
|
|
1497
|
+
$: "&[data-sidebar] > [popover]",
|
|
1498
|
+
positionAnchor: "--language-trigger",
|
|
1499
|
+
blockInset: "auto start, anchor(top) end",
|
|
1500
|
+
blockMargin: "$gap end",
|
|
1501
|
+
inlineInset: "$gap start, auto end",
|
|
1502
|
+
inlineSize: "min(20rem, calc(100vw - 4rem))",
|
|
1503
|
+
blockSize: "max (100dvh - 8rem)",
|
|
1504
|
+
transformOrigin: "bottom",
|
|
1505
|
+
},
|
|
1506
|
+
OpenPanel: languageSelectStyles.OpenPanel,
|
|
1507
|
+
PanelTitle: languageSelectStyles.PanelTitle,
|
|
1508
|
+
Options: languageSelectStyles.Options,
|
|
1509
|
+
Option: languageSelectStyles.Option,
|
|
1510
|
+
HoverOption: languageSelectStyles.HoverOption,
|
|
1511
|
+
CurrentOption: languageSelectStyles.CurrentOption,
|
|
1512
|
+
Fallback: { $: ".fallback-label", color: "#text-muted" },
|
|
1513
|
+
Checkmark: languageSelectStyles.Checkmark,
|
|
1514
|
+
SelectedCheckmark: languageSelectStyles.SelectedCheckmark,
|
|
1475
1515
|
}),
|
|
1476
1516
|
);
|
|
1477
1517
|
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { messages } from "../localization.js";
|
|
3
|
+
import { localeHref, routeLocale } from "../localization.js";
|
|
4
|
+
import { navigationPath } from "../navigation.js";
|
|
3
5
|
import { content } from "virtual:cookbook/config";
|
|
4
6
|
import { HeaderLinksRoot } from "./LayoutComponents.js";
|
|
5
7
|
|
|
6
8
|
const links = content.site.headerLinks ?? [];
|
|
7
9
|
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
|
|
8
|
-
const
|
|
10
|
+
const currentPath = navigationPath(Astro.url.pathname, base || "/");
|
|
11
|
+
const href = (link: string) =>
|
|
12
|
+
localeHref(link, currentPath, content.routes, content, base || "/");
|
|
13
|
+
const locale = routeLocale(currentPath, content);
|
|
14
|
+
const language =
|
|
15
|
+
content.locales?.[locale]?.lang ?? (locale === "root" ? "en" : locale);
|
|
16
|
+
const label = (value: string) =>
|
|
17
|
+
content.translations?.[language]?.[`navigation.${value}`] ??
|
|
18
|
+
content.translations?.[language.split("-")[0]!]?.[`navigation.${value}`] ??
|
|
19
|
+
value;
|
|
9
20
|
const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
|
|
10
21
|
---
|
|
11
22
|
|
|
@@ -20,7 +31,7 @@ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
|
|
|
20
31
|
target={link.newTab ? "_blank" : undefined}
|
|
21
32
|
rel={link.newTab ? "noopener noreferrer" : undefined}
|
|
22
33
|
>
|
|
23
|
-
{link.label}
|
|
34
|
+
{label(link.label)}
|
|
24
35
|
</a>
|
|
25
36
|
))}
|
|
26
37
|
</nav>
|
|
@@ -74,7 +85,7 @@ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
|
|
|
74
85
|
target={link.newTab ? "_blank" : undefined}
|
|
75
86
|
rel={link.newTab ? "noopener noreferrer" : undefined}
|
|
76
87
|
>
|
|
77
|
-
{link.label}
|
|
88
|
+
{label(link.label)}
|
|
78
89
|
</a>
|
|
79
90
|
))}
|
|
80
91
|
</nav>
|
|
@@ -23,7 +23,7 @@ const originals: Record<string, string> = {
|
|
|
23
23
|
laptop: desktop,
|
|
24
24
|
"approve-check": check,
|
|
25
25
|
translate:
|
|
26
|
-
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
26
|
+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><ellipse cx="12" cy="12" rx="4" ry="9"/><path d="M3 12h18"/></svg>',
|
|
27
27
|
github:
|
|
28
28
|
'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2a10 10 0 0 0-3.16 19.49c.5.09.68-.22.68-.48v-1.7c-2.77.6-3.36-1.18-3.36-1.18-.45-1.14-1.1-1.44-1.1-1.44-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.89 1.52 2.33 1.08 2.9.83.09-.65.35-1.08.63-1.33-2.21-.25-4.53-1.1-4.53-4.94 0-1.09.39-1.98 1.03-2.68-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02A9.6 9.6 0 0 1 12 6.66c.85 0 1.7.11 2.5.34 1.9-1.29 2.74-1.02 2.74-1.02.55 1.38.2 2.4.1 2.65.64.7 1.03 1.59 1.03 2.68 0 3.85-2.33 4.68-4.55 4.93.36.31.68.92.68 1.86v2.91c0 .27.18.58.69.48A10 10 0 0 0 12 2Z"/></svg>',
|
|
29
29
|
gitlab:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tasty } from "@tenphi/tasty";
|
|
2
2
|
import { customizeComponent } from "./customize-component.js";
|
|
3
|
+
import { selectPopoverStyles } from "./select-popover-styles.js";
|
|
3
4
|
import { configureCookbookStates } from "./tasty-states.js";
|
|
4
5
|
|
|
5
6
|
configureCookbookStates();
|
|
@@ -117,56 +118,37 @@ export const PackageVersionRoot = customizeComponent(
|
|
|
117
118
|
}),
|
|
118
119
|
);
|
|
119
120
|
|
|
121
|
+
const versionSelectStyles = selectPopoverStyles({
|
|
122
|
+
option: "Link",
|
|
123
|
+
hoverOption: "HoverLink",
|
|
124
|
+
currentOption: "CurrentLink",
|
|
125
|
+
});
|
|
126
|
+
|
|
120
127
|
export const VersionSwitcherRoot = customizeComponent(
|
|
121
128
|
"VersionSwitcher",
|
|
122
129
|
tasty({
|
|
123
|
-
as: "
|
|
130
|
+
as: "div",
|
|
124
131
|
styles: {
|
|
132
|
+
...versionSelectStyles,
|
|
125
133
|
display: "inline-flex",
|
|
126
|
-
position: "relative",
|
|
127
134
|
flexShrink: "0",
|
|
128
|
-
color: "#text-soft",
|
|
129
135
|
Trigger: {
|
|
130
|
-
|
|
131
|
-
display: "flex",
|
|
132
|
-
alignItems: "center",
|
|
136
|
+
...versionSelectStyles.Trigger,
|
|
133
137
|
blockSize: "min $control-height",
|
|
134
138
|
inlinePadding: "$gap",
|
|
135
139
|
fill: "#surface-2",
|
|
136
140
|
border: true,
|
|
137
|
-
radius: "$header-control-radius",
|
|
138
141
|
preset: "small / strong",
|
|
139
|
-
|
|
140
|
-
whiteSpace: "nowrap",
|
|
142
|
+
anchorName: "--version-trigger",
|
|
141
143
|
},
|
|
142
144
|
Panel: {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
border: true,
|
|
151
|
-
radius: "$radius",
|
|
152
|
-
shadow: "0 0.75rem 2rem #shadow",
|
|
153
|
-
},
|
|
154
|
-
OpenPanel: {
|
|
155
|
-
$: "&[open] > nav",
|
|
156
|
-
display: "grid",
|
|
157
|
-
},
|
|
158
|
-
Link: {
|
|
159
|
-
$: "> nav > a",
|
|
160
|
-
display: "block",
|
|
161
|
-
padding: "($gap * 0.5) $gap",
|
|
162
|
-
color: "#text-soft",
|
|
163
|
-
preset: "small",
|
|
164
|
-
textDecoration: "none",
|
|
165
|
-
},
|
|
166
|
-
CurrentLink: {
|
|
167
|
-
$: '> nav > a[aria-current="page"]',
|
|
168
|
-
color: "#accent-text",
|
|
169
|
-
preset: "small / strong",
|
|
145
|
+
...versionSelectStyles.Panel,
|
|
146
|
+
positionAnchor: "--version-trigger",
|
|
147
|
+
blockInset: "(anchor(bottom) + $gap) start, auto end",
|
|
148
|
+
inlineInset: {
|
|
149
|
+
"": "anchor(left) start, auto end",
|
|
150
|
+
"@mobile": "auto start, anchor(right) end",
|
|
151
|
+
},
|
|
170
152
|
},
|
|
171
153
|
},
|
|
172
154
|
}),
|
|
@@ -320,6 +302,15 @@ export const HeaderRoot = customizeComponent(
|
|
|
320
302
|
inlineSize: "$docs-menu-button-size",
|
|
321
303
|
blockSize: "$docs-menu-button-size",
|
|
322
304
|
},
|
|
305
|
+
MobileLanguage: {
|
|
306
|
+
$: ".td-header__mobile-language",
|
|
307
|
+
display: "grid",
|
|
308
|
+
hide: { "": true, "@mobile": false },
|
|
309
|
+
flexShrink: "0",
|
|
310
|
+
order: "1",
|
|
311
|
+
inlineSize: "$docs-menu-button-size",
|
|
312
|
+
blockSize: "$docs-menu-button-size",
|
|
313
|
+
},
|
|
323
314
|
},
|
|
324
315
|
}),
|
|
325
316
|
);
|
|
@@ -394,9 +385,9 @@ export const ThemeSelectRoot = customizeComponent(
|
|
|
394
385
|
},
|
|
395
386
|
},
|
|
396
387
|
OpenPanel: {
|
|
397
|
-
$: "[popover]:popover-open",
|
|
398
|
-
opacity:
|
|
399
|
-
scale:
|
|
388
|
+
$: "[popover]:popover-open[data-open]",
|
|
389
|
+
opacity: "1",
|
|
390
|
+
scale: "1",
|
|
400
391
|
},
|
|
401
392
|
Section: {
|
|
402
393
|
$: "fieldset",
|
|
@@ -745,7 +736,7 @@ export const HeaderLinksRoot = customizeComponent(
|
|
|
745
736
|
shadow: "0 0.75rem 2rem #shadow",
|
|
746
737
|
opacity: "0",
|
|
747
738
|
scale: "1 0.96",
|
|
748
|
-
transformOrigin: "top
|
|
739
|
+
transformOrigin: "top",
|
|
749
740
|
transition: {
|
|
750
741
|
"": "none",
|
|
751
742
|
"!@reduced-motion":
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Icon from "./Icon.astro";
|
|
3
|
+
|
|
4
|
+
interface Option {
|
|
5
|
+
href: string;
|
|
6
|
+
label: string;
|
|
7
|
+
lang?: string;
|
|
8
|
+
hint?: string;
|
|
9
|
+
current?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
triggerLabel: string;
|
|
14
|
+
selectedLabel: string;
|
|
15
|
+
title: string;
|
|
16
|
+
options: Option[];
|
|
17
|
+
icon?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { triggerLabel, selectedLabel, title, options, icon } = Astro.props;
|
|
21
|
+
const id = `select-${crypto.randomUUID()}`;
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
popovertarget={id}
|
|
27
|
+
aria-label={triggerLabel}
|
|
28
|
+
title={title}
|
|
29
|
+
>
|
|
30
|
+
{icon && <Icon name={icon} class="label-icon" />}
|
|
31
|
+
<span class="trigger-label">{selectedLabel}</span>
|
|
32
|
+
<Icon name="down-caret" class="caret" />
|
|
33
|
+
</button>
|
|
34
|
+
<div
|
|
35
|
+
id={id}
|
|
36
|
+
popover="auto"
|
|
37
|
+
role="group"
|
|
38
|
+
aria-label={title}
|
|
39
|
+
data-select-popover-panel
|
|
40
|
+
>
|
|
41
|
+
<div class="panel-title">{title}</div>
|
|
42
|
+
<nav aria-label={title}>
|
|
43
|
+
{
|
|
44
|
+
options.map((option) => (
|
|
45
|
+
<a
|
|
46
|
+
href={option.href}
|
|
47
|
+
aria-current={option.current ? "page" : undefined}
|
|
48
|
+
>
|
|
49
|
+
<span lang={option.lang}>{option.label}</span>
|
|
50
|
+
{option.hint && <span class="fallback-label">({option.hint})</span>}
|
|
51
|
+
<Icon name="approve-check" class="checkmark" />
|
|
52
|
+
</a>
|
|
53
|
+
))
|
|
54
|
+
}
|
|
55
|
+
</nav>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<script>
|
|
59
|
+
import { initializePopoverMotion } from "../client/popover-motion.js";
|
|
60
|
+
|
|
61
|
+
document
|
|
62
|
+
.querySelectorAll<HTMLElement>("[data-select-popover-panel]")
|
|
63
|
+
.forEach((panel) => initializePopoverMotion(panel));
|
|
64
|
+
</script>
|
|
@@ -84,13 +84,13 @@ export const PreviewRoot = customizeComponent(
|
|
|
84
84
|
fill: "#surface-2",
|
|
85
85
|
},
|
|
86
86
|
Stage: {
|
|
87
|
-
padding: "
|
|
87
|
+
padding: "2x",
|
|
88
88
|
fill: "#surface",
|
|
89
89
|
},
|
|
90
90
|
Frame: {
|
|
91
91
|
display: "block",
|
|
92
92
|
width: "100%",
|
|
93
|
-
height: "min
|
|
93
|
+
height: "min 10rem",
|
|
94
94
|
border: "0",
|
|
95
95
|
fill: "#surface",
|
|
96
96
|
},
|
|
@@ -102,9 +102,23 @@ export const PreviewRoot = customizeComponent(
|
|
|
102
102
|
preset: "small",
|
|
103
103
|
fill: "#surface-2",
|
|
104
104
|
cursor: "pointer",
|
|
105
|
+
transition: "fill $transition",
|
|
106
|
+
},
|
|
107
|
+
HoverSummary: {
|
|
108
|
+
$: "> details > summary:hover",
|
|
109
|
+
fill: "#surface-2-hover",
|
|
110
|
+
},
|
|
111
|
+
ActiveSummary: {
|
|
112
|
+
$: "> details > summary:active",
|
|
113
|
+
fill: "#surface-2-pressed",
|
|
105
114
|
},
|
|
106
115
|
Pre: {
|
|
107
116
|
margin: "0",
|
|
117
|
+
padding: "2x",
|
|
118
|
+
overflowX: "auto",
|
|
119
|
+
color: "#syntax-text",
|
|
120
|
+
fill: "#syntax-bg",
|
|
121
|
+
preset: "code",
|
|
108
122
|
radius: "0",
|
|
109
123
|
},
|
|
110
124
|
},
|
|
@@ -4,6 +4,7 @@ import { content } from "virtual:cookbook/config";
|
|
|
4
4
|
import type { DocsRoute, SiteVersion } from "@tenphi/docs";
|
|
5
5
|
import { versionLinks } from "../versioning.js";
|
|
6
6
|
import { VersionSwitcherRoot } from "./LayoutComponents.js";
|
|
7
|
+
import SelectPopover from "./SelectPopover.astro";
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
versions: SiteVersion[];
|
|
@@ -23,22 +24,14 @@ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
|
|
|
23
24
|
---
|
|
24
25
|
|
|
25
26
|
<VersionSwitcherRoot data-tasty-anatomy="VersionSwitcher">
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
version.routeBase === current?.routeBase ? "page" : undefined
|
|
37
|
-
}
|
|
38
|
-
>
|
|
39
|
-
{version.label}
|
|
40
|
-
</a>
|
|
41
|
-
))
|
|
42
|
-
}
|
|
43
|
-
</nav>
|
|
27
|
+
<SelectPopover
|
|
28
|
+
triggerLabel={`${ui.documentationVersion}: ${current?.label ?? ui.versions}`}
|
|
29
|
+
selectedLabel={current?.label ?? ui.versions}
|
|
30
|
+
title={ui.documentationVersions}
|
|
31
|
+
options={links.map((version) => ({
|
|
32
|
+
href: version.href,
|
|
33
|
+
label: version.label,
|
|
34
|
+
current: version.routeBase === current?.routeBase,
|
|
35
|
+
}))}
|
|
36
|
+
/>
|
|
44
37
|
</VersionSwitcherRoot>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** Shared server-side styles for the language and version selectors. */
|
|
2
|
+
export function selectPopoverStyles({
|
|
3
|
+
option = "Option",
|
|
4
|
+
hoverOption = "HoverOption",
|
|
5
|
+
currentOption = "CurrentOption",
|
|
6
|
+
} = {}) {
|
|
7
|
+
return {
|
|
8
|
+
"$popover-transition": "120ms",
|
|
9
|
+
Trigger: {
|
|
10
|
+
$: "> button",
|
|
11
|
+
display: "flex",
|
|
12
|
+
alignItems: "center",
|
|
13
|
+
justifyContent: "center",
|
|
14
|
+
gap: "($gap * 0.5)",
|
|
15
|
+
color: "#text-soft",
|
|
16
|
+
radius: "$header-control-radius",
|
|
17
|
+
preset: "small",
|
|
18
|
+
cursor: "pointer",
|
|
19
|
+
},
|
|
20
|
+
HoverTrigger: {
|
|
21
|
+
$: "> button:hover, &:has([popover]:popover-open) > button",
|
|
22
|
+
color: "#text",
|
|
23
|
+
fill: "#surface-2-hover",
|
|
24
|
+
},
|
|
25
|
+
TriggerLabel: {
|
|
26
|
+
$: ".trigger-label",
|
|
27
|
+
overflow: "hidden",
|
|
28
|
+
textOverflow: "ellipsis",
|
|
29
|
+
whiteSpace: "nowrap",
|
|
30
|
+
},
|
|
31
|
+
Caret: {
|
|
32
|
+
$: "> button > .caret",
|
|
33
|
+
flexShrink: "0",
|
|
34
|
+
inlineSize: "0.875rem",
|
|
35
|
+
blockSize: "0.875rem",
|
|
36
|
+
},
|
|
37
|
+
Panel: {
|
|
38
|
+
$: "> [popover]",
|
|
39
|
+
position: "fixed",
|
|
40
|
+
inset: "auto",
|
|
41
|
+
inlineSize: "min(15rem, calc(100vw - 2 * $docs-nav-pad-x))",
|
|
42
|
+
blockSize: "max (100dvh - $docs-nav-height - $gap)",
|
|
43
|
+
overflowY: "auto",
|
|
44
|
+
margin: "0",
|
|
45
|
+
padding: "$gap",
|
|
46
|
+
color: "#text",
|
|
47
|
+
fill: "#surface-2",
|
|
48
|
+
border: true,
|
|
49
|
+
radius: "$card-radius",
|
|
50
|
+
shadow: "0 0.75rem 2rem #shadow",
|
|
51
|
+
opacity: "0",
|
|
52
|
+
scale: "1 0.96",
|
|
53
|
+
transformOrigin: "top",
|
|
54
|
+
transition: {
|
|
55
|
+
"": "none",
|
|
56
|
+
"!@reduced-motion":
|
|
57
|
+
"opacity $popover-transition ease-out, scale $popover-transition ease-out, display $popover-transition allow-discrete, overlay $popover-transition allow-discrete",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
OpenPanel: {
|
|
61
|
+
$: "> [popover]:popover-open[data-open]",
|
|
62
|
+
opacity: "1",
|
|
63
|
+
scale: "1",
|
|
64
|
+
},
|
|
65
|
+
PanelTitle: {
|
|
66
|
+
$: ".panel-title",
|
|
67
|
+
padding: "($gap * 0.75) $gap",
|
|
68
|
+
color: "#text-muted",
|
|
69
|
+
preset: "small / strong",
|
|
70
|
+
},
|
|
71
|
+
Options: { $: "nav", display: "grid", gap: "($gap * 0.25)" },
|
|
72
|
+
[option]: {
|
|
73
|
+
$: "nav a",
|
|
74
|
+
display: "flex",
|
|
75
|
+
alignItems: "center",
|
|
76
|
+
gap: "$gap",
|
|
77
|
+
minBlockSize: "2.25rem",
|
|
78
|
+
padding: "($gap * 0.75) $gap",
|
|
79
|
+
color: "#text-soft",
|
|
80
|
+
preset: "small",
|
|
81
|
+
textDecoration: "none",
|
|
82
|
+
radius: "$radius",
|
|
83
|
+
},
|
|
84
|
+
[hoverOption]: {
|
|
85
|
+
$: "nav a:hover",
|
|
86
|
+
color: "#text",
|
|
87
|
+
fill: "#surface-2-hover",
|
|
88
|
+
},
|
|
89
|
+
[currentOption]: {
|
|
90
|
+
$: 'nav a[aria-current="page"]',
|
|
91
|
+
color: "#accent-text",
|
|
92
|
+
fill: "#accent-surface-2-subtle",
|
|
93
|
+
},
|
|
94
|
+
Checkmark: {
|
|
95
|
+
$: "nav a > .checkmark",
|
|
96
|
+
flexShrink: "0",
|
|
97
|
+
inlineMargin: "auto start",
|
|
98
|
+
inlineSize: "1rem",
|
|
99
|
+
blockSize: "1rem",
|
|
100
|
+
visibility: "hidden",
|
|
101
|
+
},
|
|
102
|
+
SelectedCheckmark: {
|
|
103
|
+
$: 'nav a[aria-current="page"] > .checkmark',
|
|
104
|
+
visibility: "visible",
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization.d.ts","names":[],"sources":["../src/localization.ts"],"mappings":";;;KAMK;EACH,UAAU;EACV,gBAAgB;EAChB,eAAe;;iBAGD,YAAY,cAAc,SAAS;;iBAMnC,gBACd,kBACA,cACA,SAAS;iBAWK,WAAW,cAAc,SAAS;iBAQlC,eACd,cACA,gBACA,SAAS;;iBASK,aACd,cACA,gBACA,QAAQ,aACR,SAAS;iBAoBK,WACd,cACA,qBACA,QAAQ,aACR,SAAS,eACT;;iBAec,oBACd,QAAQ,0BACR,QAAQ,aACR,qBACA,SAAS;EACN,QAAQ;EAA0B,QAAQ;;
|
|
1
|
+
{"version":3,"file":"localization.d.ts","names":[],"sources":["../src/localization.ts"],"mappings":";;;KAMK;EACH,UAAU;EACV,gBAAgB;EAChB,eAAe;;iBAGD,YAAY,cAAc,SAAS;;iBAMnC,gBACd,kBACA,cACA,SAAS;iBAWK,WAAW,cAAc,SAAS;iBAQlC,eACd,cACA,gBACA,SAAS;;iBASK,aACd,cACA,gBACA,QAAQ,aACR,SAAS;iBAoBK,WACd,cACA,qBACA,QAAQ,aACR,SAAS,eACT;;iBAec,oBACd,QAAQ,0BACR,QAAQ,aACR,qBACA,SAAS;EACN,QAAQ;EAA0B,QAAQ;;cA8FlC;WACX;WACA;WACA;WACA;WACA;WACA;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;KAEU,0BAA0B;iBA0CtB,SACd,cACA,UAAS,gBACR,OAAO;iBAUM,iBACd,cACA,QAAQ,aACR,SAAS,gBACR;EAAQ;EAAc"}
|
package/dist/localization.js
CHANGED
|
@@ -51,24 +51,38 @@ function localizedNavigation(layout, routes, currentPath, options) {
|
|
|
51
51
|
route: localePath(route.route, options)
|
|
52
52
|
}));
|
|
53
53
|
const available = new Set(localRoutes.filter((route) => route.discoverable !== false).map((route) => route.route));
|
|
54
|
+
const visibleRoutes = new Map(routes.filter((route) => route.discoverable !== false).map((route) => [route.route, route]));
|
|
55
|
+
const fallbackLocale = options.defaultLocale ?? (options.locales.root ? "root" : Object.keys(options.locales)[0]);
|
|
54
56
|
const convert = (items) => items.flatMap((item) => {
|
|
55
57
|
const link = typeof item === "string" ? item : item.link;
|
|
56
58
|
const internal = link?.startsWith("/") && !link.startsWith("//");
|
|
57
59
|
const translated = internal ? localePath(link, options) : link;
|
|
58
|
-
|
|
60
|
+
const fallback = internal ? localizedRoute(link, fallbackLocale, options) : void 0;
|
|
61
|
+
const target = !internal || available.has(translated) ? translated : fallback && visibleRoutes.has(fallback) ? fallback : void 0;
|
|
62
|
+
if (typeof item === "string") {
|
|
63
|
+
if (!target) return [];
|
|
64
|
+
if (!internal || available.has(translated)) return [target];
|
|
65
|
+
const route = visibleRoutes.get(target);
|
|
66
|
+
return [{
|
|
67
|
+
label: typeof route.sidebar === "object" && route.sidebar.label ? route.sidebar.label : route.title,
|
|
68
|
+
link: target
|
|
69
|
+
}];
|
|
70
|
+
}
|
|
59
71
|
if ("items" in item || "autogenerate" in item) {
|
|
60
72
|
const { link: _link, ...group } = item;
|
|
73
|
+
const children = "items" in item ? convert(item.items) : void 0;
|
|
74
|
+
if (children?.length === 0 && !target) return [];
|
|
61
75
|
return [{
|
|
62
76
|
...group,
|
|
63
77
|
label: label(group.label),
|
|
64
|
-
...
|
|
65
|
-
..."items" in item ? { items:
|
|
78
|
+
...target ? { link: target } : {},
|
|
79
|
+
..."items" in item ? { items: children } : { autogenerate: { directory: localePath(item.autogenerate.directory, options) } }
|
|
66
80
|
}];
|
|
67
81
|
}
|
|
68
|
-
return
|
|
82
|
+
return target ? [{
|
|
69
83
|
...item,
|
|
70
84
|
label: label(item.label),
|
|
71
|
-
link:
|
|
85
|
+
link: target
|
|
72
86
|
}] : [];
|
|
73
87
|
});
|
|
74
88
|
return {
|
package/dist/localization.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization.js","names":[],"sources":["../src/localization.ts"],"sourcesContent":["import type { DocsConfig, DocsRoute, NavigationItem } from \"@tenphi/docs\";\nimport {\n normalizeNavigationPath,\n type ResolvedNavigationLayout,\n} from \"./navigation.js\";\n\ntype LocaleOptions = {\n locales?: DocsConfig[\"locales\"] | undefined;\n defaultLocale?: DocsConfig[\"defaultLocale\"] | undefined;\n translations?: DocsConfig[\"translations\"] | undefined;\n};\n\nexport function routeLocale(path: string, options: LocaleOptions): string {\n const first = normalizeNavigationPath(path).split(\"/\")[1];\n return first && first !== \"root\" && options.locales?.[first] ? first : \"root\";\n}\n\n/** Author components can render before the page initializes route locals. */\nexport function requestLanguage(\n pathname: string,\n base: string,\n options: LocaleOptions,\n): string {\n const prefix = base.replace(/\\/$/, \"\");\n const path =\n prefix && (pathname === prefix || pathname.startsWith(`${prefix}/`))\n ? pathname.slice(prefix.length)\n : pathname;\n const locale = routeLocale(path, options);\n return options.locales?.[locale]?.lang ?? (locale === \"root\" ? \"en\" : locale);\n}\n\nexport function localePath(path: string, options: LocaleOptions): string {\n const route = normalizeNavigationPath(path);\n const locale = routeLocale(route, options);\n return locale === \"root\"\n ? route\n : normalizeNavigationPath(route.slice(locale.length + 1));\n}\n\nexport function localizedRoute(\n path: string,\n locale: string,\n options: LocaleOptions,\n): string {\n const suffix = localePath(path, options);\n return normalizeNavigationPath(\n `${locale === \"root\" ? \"\" : `/${locale}`}${suffix}`,\n );\n}\n\n/** Missing translations use a real default-language page, then a real locale home. */\nexport function localeTarget(\n path: string,\n locale: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n): string | undefined {\n const visible = new Set(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n const fallback =\n options.defaultLocale ??\n (options.locales?.root\n ? \"root\"\n : (Object.keys(options.locales ?? {})[0] ?? \"root\"));\n return [\n localizedRoute(path, locale, options),\n localizedRoute(path, fallback, options),\n localizedRoute(\"/\", locale, options),\n localizedRoute(\"/\", fallback, options),\n ].find((route) => visible.has(route));\n}\n\nexport function localeHref(\n link: string,\n currentPath: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n base = \"/\",\n): string {\n if (!link.startsWith(\"/\") || link.startsWith(\"//\")) return link;\n const suffix = /[?#].*$/.exec(link)?.[0] ?? \"\";\n const path = suffix ? link.slice(0, -suffix.length) : link;\n const target = options.locales\n ? localeTarget(path, routeLocale(currentPath, options), routes, options)\n : normalizeNavigationPath(path);\n return (\n `${base.replace(/\\/$/, \"\")}${target ?? normalizeNavigationPath(path)}${suffix}` ||\n \"/\"\n );\n}\n\n/** Resolve navigation against the current locale's available routes. */\nexport function localizedNavigation(\n layout: ResolvedNavigationLayout,\n routes: DocsRoute[],\n currentPath: string,\n options: LocaleOptions,\n): { layout: ResolvedNavigationLayout; routes: DocsRoute[] } {\n if (!options.locales) return { layout, routes };\n const locale = routeLocale(currentPath, options);\n const language =\n options.locales[locale]?.lang ?? (locale === \"root\" ? \"en\" : locale);\n const label = (value: string) =>\n options.translations?.[language]?.[`navigation.${value}`] ??\n options.translations?.[language.split(\"-\")[0]!]?.[`navigation.${value}`] ??\n value;\n const localRoutes = routes\n .filter((route) => routeLocale(route.route, options) === locale)\n .map((route) => ({ ...route, route: localePath(route.route, options) }));\n const available = new Set(\n localRoutes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n const convert = (items: NavigationItem[]): NavigationItem[] =>\n items.flatMap((item): NavigationItem[] => {\n const link = typeof item === \"string\" ? item : item.link;\n const internal = link?.startsWith(\"/\") && !link.startsWith(\"//\");\n const translated = internal ? localePath(link!, options) : link;\n if (typeof item === \"string\")\n return !internal || available.has(translated!) ? [translated!] : [];\n if (\"items\" in item || \"autogenerate\" in item) {\n const { link: _link, ...group } = item;\n return [\n {\n ...group,\n label: label(group.label),\n ...(translated && (!internal || available.has(translated))\n ? { link: translated }\n : {}),\n ...(\"items\" in item\n ? { items: convert(item.items) }\n : {\n autogenerate: {\n directory: localePath(item.autogenerate.directory, options),\n },\n }),\n },\n ];\n }\n return !internal || available.has(translated!)\n ? [{ ...item, label: label(item.label), link: translated! }]\n : [];\n });\n return {\n layout: {\n ...layout,\n ...(layout.items ? { items: convert(layout.items) } : {}),\n tabs: layout.tabs.map((tab) => ({\n ...tab,\n label: label(tab.label),\n link:\n tab.link.startsWith(\"/\") && !tab.link.startsWith(\"//\")\n ? localePath(tab.link, options)\n : tab.link,\n ...(tab.items ? { items: convert(tab.items) } : {}),\n })),\n },\n routes: localRoutes,\n };\n}\n\nexport const ENGLISH_MESSAGES = {\n copyPage: \"Copy page\",\n viewMarkdown: \"Download Markdown\",\n pageCopied: \"Page copied\",\n pageCopyError: \"Could not copy. Download Markdown to read or copy the text.\",\n draftTitle: \"Draft\",\n draftNotice:\n \"This page is available by direct link and excluded from navigation and indexes.\",\n searchError: \"Search could not load. Close and try again.\",\n appearance: \"Appearance\",\n colorScheme: \"Color scheme\",\n contrast: \"Contrast\",\n normalContrast: \"Normal\",\n highContrast: \"High\",\n auto: \"Auto\",\n closeNavigation: \"Close navigation\",\n primary: \"Primary\",\n sections: \"Sections\",\n chooseSection: \"Choose section\",\n headerLinks: \"Header links\",\n more: \"More\",\n closeMore: \"Close more menu\",\n moreLinks: \"More links\",\n versions: \"Versions\",\n documentationVersion: \"Documentation version\",\n documentationVersions: \"Documentation versions\",\n version: \"Version\",\n home: \"Home\",\n generatedWith: \"Generated with\",\n viewSource: \"View source\",\n copyCode: \"Copy code\",\n codeCopied: \"Code copied\",\n codeCopyError: \"Could not copy code\",\n copyHeading: \"Copy link to “{heading}”\",\n linkCopied: \"Link copied\",\n linkCopyError: \"Could not copy link\",\n notFound: \"Page not found\",\n notFoundDescription: \"The page you requested could not be found.\",\n fallback: \"fallback\",\n} as const;\nexport type MessageKey = keyof typeof ENGLISH_MESSAGES;\nconst FRENCH_MESSAGES: Record<MessageKey, string> = {\n copyPage: \"Copier la page\",\n viewMarkdown: \"Télécharger le Markdown\",\n pageCopied: \"Page copiée\",\n pageCopyError:\n \"Copie impossible. Téléchargez le Markdown pour lire ou copier le texte.\",\n draftTitle: \"Brouillon\",\n draftNotice:\n \"Cette page est accessible par son lien direct et exclue de la navigation et des index.\",\n searchError: \"La recherche n’a pas pu se charger. Fermez et réessayez.\",\n appearance: \"Apparence\",\n colorScheme: \"Thème de couleur\",\n contrast: \"Contraste\",\n normalContrast: \"Normal\",\n highContrast: \"Élevé\",\n auto: \"Auto\",\n closeNavigation: \"Fermer la navigation\",\n primary: \"Navigation principale\",\n sections: \"Sections\",\n chooseSection: \"Choisir une section\",\n headerLinks: \"Liens d’en-tête\",\n more: \"Plus\",\n closeMore: \"Fermer le menu\",\n moreLinks: \"Autres liens\",\n versions: \"Versions\",\n documentationVersion: \"Version de la documentation\",\n documentationVersions: \"Versions de la documentation\",\n version: \"Version\",\n home: \"Accueil\",\n generatedWith: \"Créé avec\",\n viewSource: \"Voir le code source\",\n copyCode: \"Copier le code\",\n codeCopied: \"Code copié\",\n codeCopyError: \"Impossible de copier le code\",\n copyHeading: \"Copier le lien vers « {heading} »\",\n linkCopied: \"Lien copié\",\n linkCopyError: \"Impossible de copier le lien\",\n notFound: \"Page introuvable\",\n notFoundDescription: \"La page demandée est introuvable.\",\n fallback: \"langue de secours\",\n};\nexport function messages(\n lang: string,\n options: LocaleOptions = {},\n): Record<MessageKey, string> {\n const language = lang.split(\"-\")[0] ?? \"en\";\n return {\n ...ENGLISH_MESSAGES,\n ...(language === \"fr\" ? FRENCH_MESSAGES : {}),\n ...options.translations?.[language],\n ...options.translations?.[lang],\n };\n}\n\nexport function localeAlternates(\n path: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n): Array<{ lang: string; route: string }> {\n const available = new Set(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n if (!options.locales || !available.has(path)) return [];\n const alternates = Object.entries(options.locales).flatMap(\n ([locale, config]) => {\n const route = localizedRoute(path, locale, options);\n return available.has(route)\n ? [{ lang: config.lang ?? locale, route }]\n : [];\n },\n );\n const fallback = localizedRoute(\n path,\n options.defaultLocale ??\n (options.locales.root ? \"root\" : Object.keys(options.locales)[0]!),\n options,\n );\n if (available.has(fallback))\n alternates.push({ lang: \"x-default\", route: fallback });\n return alternates;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"localization.js","names":[],"sources":["../src/localization.ts"],"sourcesContent":["import type { DocsConfig, DocsRoute, NavigationItem } from \"@tenphi/docs\";\nimport {\n normalizeNavigationPath,\n type ResolvedNavigationLayout,\n} from \"./navigation.js\";\n\ntype LocaleOptions = {\n locales?: DocsConfig[\"locales\"] | undefined;\n defaultLocale?: DocsConfig[\"defaultLocale\"] | undefined;\n translations?: DocsConfig[\"translations\"] | undefined;\n};\n\nexport function routeLocale(path: string, options: LocaleOptions): string {\n const first = normalizeNavigationPath(path).split(\"/\")[1];\n return first && first !== \"root\" && options.locales?.[first] ? first : \"root\";\n}\n\n/** Author components can render before the page initializes route locals. */\nexport function requestLanguage(\n pathname: string,\n base: string,\n options: LocaleOptions,\n): string {\n const prefix = base.replace(/\\/$/, \"\");\n const path =\n prefix && (pathname === prefix || pathname.startsWith(`${prefix}/`))\n ? pathname.slice(prefix.length)\n : pathname;\n const locale = routeLocale(path, options);\n return options.locales?.[locale]?.lang ?? (locale === \"root\" ? \"en\" : locale);\n}\n\nexport function localePath(path: string, options: LocaleOptions): string {\n const route = normalizeNavigationPath(path);\n const locale = routeLocale(route, options);\n return locale === \"root\"\n ? route\n : normalizeNavigationPath(route.slice(locale.length + 1));\n}\n\nexport function localizedRoute(\n path: string,\n locale: string,\n options: LocaleOptions,\n): string {\n const suffix = localePath(path, options);\n return normalizeNavigationPath(\n `${locale === \"root\" ? \"\" : `/${locale}`}${suffix}`,\n );\n}\n\n/** Missing translations use a real default-language page, then a real locale home. */\nexport function localeTarget(\n path: string,\n locale: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n): string | undefined {\n const visible = new Set(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n const fallback =\n options.defaultLocale ??\n (options.locales?.root\n ? \"root\"\n : (Object.keys(options.locales ?? {})[0] ?? \"root\"));\n return [\n localizedRoute(path, locale, options),\n localizedRoute(path, fallback, options),\n localizedRoute(\"/\", locale, options),\n localizedRoute(\"/\", fallback, options),\n ].find((route) => visible.has(route));\n}\n\nexport function localeHref(\n link: string,\n currentPath: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n base = \"/\",\n): string {\n if (!link.startsWith(\"/\") || link.startsWith(\"//\")) return link;\n const suffix = /[?#].*$/.exec(link)?.[0] ?? \"\";\n const path = suffix ? link.slice(0, -suffix.length) : link;\n const target = options.locales\n ? localeTarget(path, routeLocale(currentPath, options), routes, options)\n : normalizeNavigationPath(path);\n return (\n `${base.replace(/\\/$/, \"\")}${target ?? normalizeNavigationPath(path)}${suffix}` ||\n \"/\"\n );\n}\n\n/** Resolve navigation against the current locale's available routes. */\nexport function localizedNavigation(\n layout: ResolvedNavigationLayout,\n routes: DocsRoute[],\n currentPath: string,\n options: LocaleOptions,\n): { layout: ResolvedNavigationLayout; routes: DocsRoute[] } {\n if (!options.locales) return { layout, routes };\n const locale = routeLocale(currentPath, options);\n const language =\n options.locales[locale]?.lang ?? (locale === \"root\" ? \"en\" : locale);\n const label = (value: string) =>\n options.translations?.[language]?.[`navigation.${value}`] ??\n options.translations?.[language.split(\"-\")[0]!]?.[`navigation.${value}`] ??\n value;\n const localRoutes = routes\n .filter((route) => routeLocale(route.route, options) === locale)\n .map((route) => ({ ...route, route: localePath(route.route, options) }));\n const available = new Set(\n localRoutes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n const visibleRoutes = new Map(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => [route.route, route]),\n );\n const fallbackLocale =\n options.defaultLocale ??\n (options.locales.root ? \"root\" : Object.keys(options.locales)[0]!);\n const convert = (items: NavigationItem[]): NavigationItem[] =>\n items.flatMap((item): NavigationItem[] => {\n const link = typeof item === \"string\" ? item : item.link;\n const internal = link?.startsWith(\"/\") && !link.startsWith(\"//\");\n const translated = internal ? localePath(link!, options) : link;\n const fallback = internal\n ? localizedRoute(link!, fallbackLocale, options)\n : undefined;\n const target =\n !internal || available.has(translated!)\n ? translated\n : fallback && visibleRoutes.has(fallback)\n ? fallback\n : undefined;\n if (typeof item === \"string\") {\n if (!target) return [];\n if (!internal || available.has(translated!)) return [target];\n const route = visibleRoutes.get(target)!;\n return [\n {\n label:\n typeof route.sidebar === \"object\" && route.sidebar.label\n ? route.sidebar.label\n : route.title,\n link: target,\n },\n ];\n }\n if (\"items\" in item || \"autogenerate\" in item) {\n const { link: _link, ...group } = item;\n const children = \"items\" in item ? convert(item.items) : undefined;\n if (children?.length === 0 && !target) return [];\n return [\n {\n ...group,\n label: label(group.label),\n ...(target ? { link: target } : {}),\n ...(\"items\" in item\n ? { items: children! }\n : {\n autogenerate: {\n directory: localePath(item.autogenerate.directory, options),\n },\n }),\n },\n ];\n }\n return target\n ? [{ ...item, label: label(item.label), link: target }]\n : [];\n });\n return {\n layout: {\n ...layout,\n ...(layout.items ? { items: convert(layout.items) } : {}),\n tabs: layout.tabs.map((tab) => ({\n ...tab,\n label: label(tab.label),\n link:\n tab.link.startsWith(\"/\") && !tab.link.startsWith(\"//\")\n ? localePath(tab.link, options)\n : tab.link,\n ...(tab.items ? { items: convert(tab.items) } : {}),\n })),\n },\n routes: localRoutes,\n };\n}\n\nexport const ENGLISH_MESSAGES = {\n copyPage: \"Copy page\",\n viewMarkdown: \"Download Markdown\",\n pageCopied: \"Page copied\",\n pageCopyError: \"Could not copy. Download Markdown to read or copy the text.\",\n draftTitle: \"Draft\",\n draftNotice:\n \"This page is available by direct link and excluded from navigation and indexes.\",\n searchError: \"Search could not load. Close and try again.\",\n appearance: \"Appearance\",\n colorScheme: \"Color scheme\",\n contrast: \"Contrast\",\n normalContrast: \"Normal\",\n highContrast: \"High\",\n auto: \"Auto\",\n closeNavigation: \"Close navigation\",\n primary: \"Primary\",\n sections: \"Sections\",\n chooseSection: \"Choose section\",\n headerLinks: \"Header links\",\n more: \"More\",\n closeMore: \"Close more menu\",\n moreLinks: \"More links\",\n versions: \"Versions\",\n documentationVersion: \"Documentation version\",\n documentationVersions: \"Documentation versions\",\n version: \"Version\",\n home: \"Home\",\n generatedWith: \"Generated with\",\n viewSource: \"View source\",\n copyCode: \"Copy code\",\n codeCopied: \"Code copied\",\n codeCopyError: \"Could not copy code\",\n copyHeading: \"Copy link to “{heading}”\",\n linkCopied: \"Link copied\",\n linkCopyError: \"Could not copy link\",\n notFound: \"Page not found\",\n notFoundDescription: \"The page you requested could not be found.\",\n fallback: \"fallback\",\n} as const;\nexport type MessageKey = keyof typeof ENGLISH_MESSAGES;\nconst FRENCH_MESSAGES: Record<MessageKey, string> = {\n copyPage: \"Copier la page\",\n viewMarkdown: \"Télécharger le Markdown\",\n pageCopied: \"Page copiée\",\n pageCopyError:\n \"Copie impossible. Téléchargez le Markdown pour lire ou copier le texte.\",\n draftTitle: \"Brouillon\",\n draftNotice:\n \"Cette page est accessible par son lien direct et exclue de la navigation et des index.\",\n searchError: \"La recherche n’a pas pu se charger. Fermez et réessayez.\",\n appearance: \"Apparence\",\n colorScheme: \"Thème de couleur\",\n contrast: \"Contraste\",\n normalContrast: \"Normal\",\n highContrast: \"Élevé\",\n auto: \"Auto\",\n closeNavigation: \"Fermer la navigation\",\n primary: \"Navigation principale\",\n sections: \"Sections\",\n chooseSection: \"Choisir une section\",\n headerLinks: \"Liens d’en-tête\",\n more: \"Plus\",\n closeMore: \"Fermer le menu\",\n moreLinks: \"Autres liens\",\n versions: \"Versions\",\n documentationVersion: \"Version de la documentation\",\n documentationVersions: \"Versions de la documentation\",\n version: \"Version\",\n home: \"Accueil\",\n generatedWith: \"Créé avec\",\n viewSource: \"Voir le code source\",\n copyCode: \"Copier le code\",\n codeCopied: \"Code copié\",\n codeCopyError: \"Impossible de copier le code\",\n copyHeading: \"Copier le lien vers « {heading} »\",\n linkCopied: \"Lien copié\",\n linkCopyError: \"Impossible de copier le lien\",\n notFound: \"Page introuvable\",\n notFoundDescription: \"La page demandée est introuvable.\",\n fallback: \"langue de secours\",\n};\nexport function messages(\n lang: string,\n options: LocaleOptions = {},\n): Record<MessageKey, string> {\n const language = lang.split(\"-\")[0] ?? \"en\";\n return {\n ...ENGLISH_MESSAGES,\n ...(language === \"fr\" ? FRENCH_MESSAGES : {}),\n ...options.translations?.[language],\n ...options.translations?.[lang],\n };\n}\n\nexport function localeAlternates(\n path: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n): Array<{ lang: string; route: string }> {\n const available = new Set(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n if (!options.locales || !available.has(path)) return [];\n const alternates = Object.entries(options.locales).flatMap(\n ([locale, config]) => {\n const route = localizedRoute(path, locale, options);\n return available.has(route)\n ? [{ lang: config.lang ?? locale, route }]\n : [];\n },\n );\n const fallback = localizedRoute(\n path,\n options.defaultLocale ??\n (options.locales.root ? \"root\" : Object.keys(options.locales)[0]!),\n options,\n );\n if (available.has(fallback))\n alternates.push({ lang: \"x-default\", route: fallback });\n return alternates;\n}\n"],"mappings":"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"}
|
|
@@ -107,6 +107,14 @@ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
|
|
|
107
107
|
>
|
|
108
108
|
<ThemeSelect />
|
|
109
109
|
</div>
|
|
110
|
+
{
|
|
111
|
+
!Astro.locals.cookbookRoute.hasSidebar &&
|
|
112
|
+
Object.keys(content.locales ?? {}).length > 1 && (
|
|
113
|
+
<div class="td-header__mobile-language cookbook-print-hidden">
|
|
114
|
+
<LanguageSelect compact />
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
110
118
|
<div
|
|
111
119
|
class="td-header__tools cookbook-hidden md:cookbook-flex cookbook-print-hidden"
|
|
112
120
|
>
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
import { content } from "virtual:cookbook/config";
|
|
3
3
|
import { localeTarget, routeLocale, messages } from "../localization.js";
|
|
4
4
|
import { navigationPath } from "../navigation.js";
|
|
5
|
-
import
|
|
5
|
+
import SelectPopover from "../components/SelectPopover.astro";
|
|
6
|
+
interface Props {
|
|
7
|
+
compact?: boolean;
|
|
8
|
+
sidebar?: boolean;
|
|
9
|
+
}
|
|
10
|
+
const { compact = false, sidebar = false } = Astro.props;
|
|
6
11
|
const current = navigationPath(Astro.url.pathname, content.base);
|
|
7
12
|
const selected = routeLocale(current, content);
|
|
8
13
|
const ui = messages(Astro.locals.cookbookRoute.lang, content);
|
|
14
|
+
const accessibleLabel = Astro.locals.t("languageSelect.accessibleLabel");
|
|
9
15
|
const options = Object.entries(content.locales ?? {}).flatMap(
|
|
10
16
|
([locale, config]) => {
|
|
11
17
|
const target = localeTarget(current, locale, content.routes, content);
|
|
@@ -14,50 +20,38 @@ const options = Object.entries(content.locales ?? {}).flatMap(
|
|
|
14
20
|
? [
|
|
15
21
|
{
|
|
16
22
|
locale,
|
|
17
|
-
label:
|
|
23
|
+
label: config.label,
|
|
24
|
+
lang: config.lang ?? (locale === "root" ? "en" : locale),
|
|
25
|
+
fallback,
|
|
18
26
|
href: `${content.base.replace(/\/$/, "")}${target === "/" ? "/" : `${target}/`}`,
|
|
19
27
|
},
|
|
20
28
|
]
|
|
21
29
|
: [];
|
|
22
30
|
},
|
|
23
31
|
);
|
|
32
|
+
const selectedOption = options.find((option) => option.locale === selected);
|
|
24
33
|
---
|
|
25
34
|
|
|
26
35
|
{
|
|
27
36
|
options.length > 1 && (
|
|
28
|
-
<cookbook-language-select
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
<cookbook-language-select
|
|
38
|
+
data-compact={compact ? "" : undefined}
|
|
39
|
+
data-sidebar={sidebar ? "" : undefined}
|
|
40
|
+
data-tasty-anatomy="LanguageSelect"
|
|
41
|
+
>
|
|
42
|
+
<SelectPopover
|
|
43
|
+
triggerLabel={`${accessibleLabel}: ${selectedOption?.label ?? ""}`}
|
|
44
|
+
selectedLabel={selectedOption?.label ?? ""}
|
|
45
|
+
title={accessibleLabel}
|
|
46
|
+
icon="translate"
|
|
47
|
+
options={options.map((option) => ({
|
|
48
|
+
href: option.href,
|
|
49
|
+
label: option.label,
|
|
50
|
+
lang: option.lang,
|
|
51
|
+
hint: option.fallback ? ui.fallback : undefined,
|
|
52
|
+
current: option.locale === selected,
|
|
53
|
+
}))}
|
|
54
|
+
/>
|
|
43
55
|
</cookbook-language-select>
|
|
44
56
|
)
|
|
45
57
|
}
|
|
46
|
-
|
|
47
|
-
<script>
|
|
48
|
-
class CookbookLanguageSelect extends HTMLElement {
|
|
49
|
-
connectedCallback() {
|
|
50
|
-
const select = this.querySelector("select");
|
|
51
|
-
select?.addEventListener("change", () => {
|
|
52
|
-
window.location.href = select.value;
|
|
53
|
-
});
|
|
54
|
-
window.addEventListener("pageshow", () => {
|
|
55
|
-
if (select)
|
|
56
|
-
select.selectedIndex =
|
|
57
|
-
select.querySelector("option[selected]")?.index ?? 0;
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (!customElements.get("cookbook-language-select"))
|
|
62
|
-
customElements.define("cookbook-language-select", CookbookLanguageSelect);
|
|
63
|
-
</script>
|
|
@@ -82,6 +82,7 @@ const contrasts = [
|
|
|
82
82
|
parseScheme,
|
|
83
83
|
parseContrast,
|
|
84
84
|
} from "../components/appearance-preferences.js";
|
|
85
|
+
import { initializePopoverMotion } from "../client/popover-motion.js";
|
|
85
86
|
|
|
86
87
|
let current = loadPreferences();
|
|
87
88
|
const systemScheme = matchMedia("(prefers-color-scheme: light)");
|
|
@@ -127,6 +128,7 @@ const contrasts = [
|
|
|
127
128
|
connectedCallback(): void {
|
|
128
129
|
if (this.dataset.ready) return;
|
|
129
130
|
this.dataset.ready = "true";
|
|
131
|
+
initializePopoverMotion(this.querySelector<HTMLElement>("[popover]")!);
|
|
130
132
|
this.addEventListener("change", (event) => {
|
|
131
133
|
const input = event.target;
|
|
132
134
|
if (!(input instanceof HTMLInputElement)) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenphi/renderer",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.5",
|
|
4
4
|
"description": "Astro renderer and Tasty theme for Cookbook",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
62
62
|
"@fontsource-variable/onest": "^5.3.0",
|
|
63
63
|
"@pagefind/default-ui": "1.5.2",
|
|
64
|
-
"@tenphi/docs": "0.20.
|
|
64
|
+
"@tenphi/docs": "0.20.5",
|
|
65
65
|
"@tenphi/eslint-plugin-tasty": "1.1.0",
|
|
66
66
|
"@tenphi/glaze": "2.0.0",
|
|
67
67
|
"@tenphi/tasty": "3.8.0",
|