@cavebatsofware/riposte-pickers 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/LICENSE +674 -0
- package/README.md +214 -0
- package/dist/chunk-6FAEMGAR.js +93 -0
- package/dist/chunk-6FAEMGAR.js.map +1 -0
- package/dist/chunk-6OZDKKEP.cjs +95 -0
- package/dist/chunk-6OZDKKEP.cjs.map +1 -0
- package/dist/chunk-7JL223UJ.cjs +84 -0
- package/dist/chunk-7JL223UJ.cjs.map +1 -0
- package/dist/chunk-A4OVAXLP.js +261 -0
- package/dist/chunk-A4OVAXLP.js.map +1 -0
- package/dist/chunk-ATS6MI5Q.js +3 -0
- package/dist/chunk-ATS6MI5Q.js.map +1 -0
- package/dist/chunk-GDYHLOSQ.cjs +268 -0
- package/dist/chunk-GDYHLOSQ.cjs.map +1 -0
- package/dist/chunk-IWUGV7HL.js +113 -0
- package/dist/chunk-IWUGV7HL.js.map +1 -0
- package/dist/chunk-LIGL56YJ.cjs +116 -0
- package/dist/chunk-LIGL56YJ.cjs.map +1 -0
- package/dist/chunk-U73YJG4C.cjs +4 -0
- package/dist/chunk-U73YJG4C.cjs.map +1 -0
- package/dist/chunk-XWM3AYYR.js +82 -0
- package/dist/chunk-XWM3AYYR.js.map +1 -0
- package/dist/i18n/index.cjs +192 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.cts +156 -0
- package/dist/i18n/index.d.ts +156 -0
- package/dist/i18n/index.js +189 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.cjs +52 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/language.cjs +18 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +39 -0
- package/dist/language.d.ts +39 -0
- package/dist/language.js +5 -0
- package/dist/language.js.map +1 -0
- package/dist/shared.cjs +18 -0
- package/dist/shared.cjs.map +1 -0
- package/dist/shared.d.cts +29 -0
- package/dist/shared.d.ts +29 -0
- package/dist/shared.js +5 -0
- package/dist/shared.js.map +1 -0
- package/dist/theme.cjs +33 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +54 -0
- package/dist/theme.d.ts +54 -0
- package/dist/theme.js +4 -0
- package/dist/theme.js.map +1 -0
- package/package.json +97 -0
- package/styles/index.css +3 -0
- package/styles/language.css +89 -0
- package/styles/palette.css +614 -0
- package/styles/picker.css +139 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useRovingFocus_default } from './chunk-XWM3AYYR.js';
|
|
2
|
+
import { PopoverPicker } from './chunk-6FAEMGAR.js';
|
|
3
|
+
import { useState, useRef } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/language/languages.ts
|
|
8
|
+
var DEFAULT_LANGUAGES = [
|
|
9
|
+
{ code: "en", nativeName: "English" },
|
|
10
|
+
{ code: "es", nativeName: "Espa\xF1ol" },
|
|
11
|
+
{ code: "fr", nativeName: "Fran\xE7ais" },
|
|
12
|
+
{ code: "zh", nativeName: "\u4E2D\u6587" },
|
|
13
|
+
{ code: "de", nativeName: "Deutsch" }
|
|
14
|
+
];
|
|
15
|
+
function LanguagePicker({
|
|
16
|
+
variant = "popover",
|
|
17
|
+
namespace = "common",
|
|
18
|
+
languages = DEFAULT_LANGUAGES,
|
|
19
|
+
onChange
|
|
20
|
+
}) {
|
|
21
|
+
const { i18n, t } = useTranslation(namespace);
|
|
22
|
+
const [open, setOpen] = useState(false);
|
|
23
|
+
const popoverRef = useRef(null);
|
|
24
|
+
const inlineRef = useRef(null);
|
|
25
|
+
useRovingFocus_default(popoverRef, variant === "popover" && open);
|
|
26
|
+
useRovingFocus_default(inlineRef, variant === "inline", { autoFocus: false });
|
|
27
|
+
const codes = languages.map((l) => l.code);
|
|
28
|
+
const active = (i18n.resolvedLanguage || i18n.language || codes[0] || "en").split(
|
|
29
|
+
"-"
|
|
30
|
+
)[0];
|
|
31
|
+
async function pick(code) {
|
|
32
|
+
if (!codes.includes(code)) return;
|
|
33
|
+
if (code === active) {
|
|
34
|
+
if (variant === "popover") setOpen(false);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await i18n.changeLanguage(code);
|
|
38
|
+
if (variant === "popover") setOpen(false);
|
|
39
|
+
if (onChange) {
|
|
40
|
+
try {
|
|
41
|
+
await onChange(code);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const list = /* @__PURE__ */ jsxs(
|
|
47
|
+
"div",
|
|
48
|
+
{
|
|
49
|
+
className: "language-picker-list",
|
|
50
|
+
role: "menu",
|
|
51
|
+
tabIndex: -1,
|
|
52
|
+
"aria-label": t("language.menuAria"),
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsx("div", { className: "language-picker-title", children: t("language.title") }),
|
|
55
|
+
languages.map((lng) => {
|
|
56
|
+
const isActive = lng.code === active;
|
|
57
|
+
return /* @__PURE__ */ jsxs(
|
|
58
|
+
"button",
|
|
59
|
+
{
|
|
60
|
+
type: "button",
|
|
61
|
+
role: "menuitemradio",
|
|
62
|
+
"aria-checked": isActive,
|
|
63
|
+
className: `language-picker-item ${isActive ? "active" : ""}`,
|
|
64
|
+
onClick: () => pick(lng.code),
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ jsx("span", { className: "language-picker-name", children: lng.nativeName }),
|
|
67
|
+
isActive && /* @__PURE__ */ jsx("span", { className: "language-picker-check", "aria-hidden": "true", children: "\u2713" })
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
lng.code
|
|
71
|
+
);
|
|
72
|
+
})
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
PopoverPicker,
|
|
78
|
+
{
|
|
79
|
+
variant,
|
|
80
|
+
open,
|
|
81
|
+
onOpenChange: setOpen,
|
|
82
|
+
className: "language-picker",
|
|
83
|
+
toggleAriaLabel: t("language.toggleAria"),
|
|
84
|
+
popoverAriaLabel: t("language.title"),
|
|
85
|
+
popoverRef,
|
|
86
|
+
inlineRef,
|
|
87
|
+
toggleIcon: /* @__PURE__ */ jsxs(
|
|
88
|
+
"svg",
|
|
89
|
+
{
|
|
90
|
+
width: "20",
|
|
91
|
+
height: "20",
|
|
92
|
+
viewBox: "0 0 24 24",
|
|
93
|
+
fill: "none",
|
|
94
|
+
stroke: "currentColor",
|
|
95
|
+
strokeWidth: "1.8",
|
|
96
|
+
strokeLinecap: "round",
|
|
97
|
+
strokeLinejoin: "round",
|
|
98
|
+
"aria-hidden": "true",
|
|
99
|
+
children: [
|
|
100
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
101
|
+
/* @__PURE__ */ jsx("path", { d: "M2 12h20" }),
|
|
102
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" })
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
children: list
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { DEFAULT_LANGUAGES, LanguagePicker };
|
|
112
|
+
//# sourceMappingURL=chunk-IWUGV7HL.js.map
|
|
113
|
+
//# sourceMappingURL=chunk-IWUGV7HL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/language/languages.ts","../src/language/LanguagePicker.tsx"],"names":[],"mappings":"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","file":"chunk-IWUGV7HL.js","sourcesContent":["/* This file is part of @cavebatsofware/riposte-pickers\n * Copyright (C) 2026 Grant DeFayette\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, version 3 of the License (GPL-3.0-only).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.\n */\nexport interface Language {\n /** Base language code (no region subtag), e.g. `en`. Passed to i18next. */\n code: string;\n /**\n * Display name written in the language's own script (e.g. `Deutsch`,\n * `中文`). Shown verbatim in the list so a reader recognizes their own\n * language without a translation chain.\n */\n nativeName: string;\n}\n\n/// The bundled default catalog: the five riposte launch locales. Override\n/// via the `languages` prop on `LanguagePicker` to ship a different set.\nexport const DEFAULT_LANGUAGES: Language[] = [\n { code: \"en\", nativeName: \"English\" },\n { code: \"es\", nativeName: \"Español\" },\n { code: \"fr\", nativeName: \"Français\" },\n { code: \"zh\", nativeName: \"中文\" },\n { code: \"de\", nativeName: \"Deutsch\" },\n];\n","/* This file is part of @cavebatsofware/riposte-pickers\n * Copyright (C) 2026 Grant DeFayette\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, version 3 of the License (GPL-3.0-only).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.\n */\nimport { useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport PopoverPicker from \"../shared/PopoverPicker\";\nimport useRovingFocus from \"../shared/useRovingFocus\";\nimport { DEFAULT_LANGUAGES, type Language } from \"./languages\";\n\nexport interface LanguagePickerProps {\n /**\n * `popover` (default): a globe-icon button in a header that opens a\n * dropdown of languages. `inline`: the list rendered directly, for use\n * inside a drawer.\n */\n variant?: \"popover\" | \"inline\";\n /**\n * i18next namespace holding the `language.*` keys. Defaults to `common`.\n * Merge the package's `languageResources` into this namespace.\n */\n namespace?: string;\n /** Catalog of selectable languages. Defaults to the bundled five. */\n languages?: Language[];\n /**\n * Optional side effect fired after a successful switch, e.g. persisting the\n * choice server-side. Receives the chosen base code. Rejections are caught\n * so they never undo the local switch; handle errors inside the callback if\n * you need to surface them.\n */\n onChange?: (code: string) => void | Promise<void>;\n}\n\n/// Language picker built on the shared popover/roving-focus chassis.\n///\n/// Switching always calls `i18next.changeLanguage`, which (with the standard\n/// browser-language-detector `caches`) persists to localStorage. The optional\n/// `onChange` callback runs afterward for app-specific persistence (e.g. a\n/// server PATCH) and is intentionally decoupled: this component knows nothing\n/// about auth or your API.\nexport default function LanguagePicker({\n variant = \"popover\",\n namespace = \"common\",\n languages = DEFAULT_LANGUAGES,\n onChange,\n}: LanguagePickerProps) {\n const { i18n, t } = useTranslation(namespace);\n const [open, setOpen] = useState(false);\n const popoverRef = useRef<HTMLDivElement | null>(null);\n const inlineRef = useRef<HTMLDivElement | null>(null);\n\n useRovingFocus(popoverRef, variant === \"popover\" && open);\n useRovingFocus(inlineRef, variant === \"inline\", { autoFocus: false });\n\n const codes = languages.map((l) => l.code);\n // i18n.language can include a region subtag (e.g. \"en-US\"); compare against\n // the canonical base codes.\n const active = (i18n.resolvedLanguage || i18n.language || codes[0] || \"en\").split(\n \"-\",\n )[0];\n\n async function pick(code: string) {\n if (!codes.includes(code)) return;\n if (code === active) {\n if (variant === \"popover\") setOpen(false);\n return;\n }\n await i18n.changeLanguage(code);\n if (variant === \"popover\") setOpen(false);\n\n if (onChange) {\n try {\n await onChange(code);\n } catch {\n // The local switch already took effect; a failed persist must not\n // undo it. The consumer owns error reporting.\n }\n }\n }\n\n const list = (\n <div\n className=\"language-picker-list\"\n role=\"menu\"\n tabIndex={-1}\n aria-label={t(\"language.menuAria\")}\n >\n <div className=\"language-picker-title\">{t(\"language.title\")}</div>\n {languages.map((lng) => {\n const isActive = lng.code === active;\n return (\n <button\n key={lng.code}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={isActive}\n className={`language-picker-item ${isActive ? \"active\" : \"\"}`}\n onClick={() => pick(lng.code)}\n >\n <span className=\"language-picker-name\">{lng.nativeName}</span>\n {isActive && (\n <span className=\"language-picker-check\" aria-hidden=\"true\">\n ✓\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n\n return (\n <PopoverPicker\n variant={variant}\n open={open}\n onOpenChange={setOpen}\n className=\"language-picker\"\n toggleAriaLabel={t(\"language.toggleAria\")}\n popoverAriaLabel={t(\"language.title\")}\n popoverRef={popoverRef}\n inlineRef={inlineRef}\n toggleIcon={\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M2 12h20\" />\n <path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\" />\n </svg>\n }\n >\n {list}\n </PopoverPicker>\n );\n}\n"]}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk7JL223UJ_cjs = require('./chunk-7JL223UJ.cjs');
|
|
4
|
+
var chunk6OZDKKEP_cjs = require('./chunk-6OZDKKEP.cjs');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var reactI18next = require('react-i18next');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
// src/language/languages.ts
|
|
10
|
+
var DEFAULT_LANGUAGES = [
|
|
11
|
+
{ code: "en", nativeName: "English" },
|
|
12
|
+
{ code: "es", nativeName: "Espa\xF1ol" },
|
|
13
|
+
{ code: "fr", nativeName: "Fran\xE7ais" },
|
|
14
|
+
{ code: "zh", nativeName: "\u4E2D\u6587" },
|
|
15
|
+
{ code: "de", nativeName: "Deutsch" }
|
|
16
|
+
];
|
|
17
|
+
function LanguagePicker({
|
|
18
|
+
variant = "popover",
|
|
19
|
+
namespace = "common",
|
|
20
|
+
languages = DEFAULT_LANGUAGES,
|
|
21
|
+
onChange
|
|
22
|
+
}) {
|
|
23
|
+
const { i18n, t } = reactI18next.useTranslation(namespace);
|
|
24
|
+
const [open, setOpen] = react.useState(false);
|
|
25
|
+
const popoverRef = react.useRef(null);
|
|
26
|
+
const inlineRef = react.useRef(null);
|
|
27
|
+
chunk7JL223UJ_cjs.useRovingFocus_default(popoverRef, variant === "popover" && open);
|
|
28
|
+
chunk7JL223UJ_cjs.useRovingFocus_default(inlineRef, variant === "inline", { autoFocus: false });
|
|
29
|
+
const codes = languages.map((l) => l.code);
|
|
30
|
+
const active = (i18n.resolvedLanguage || i18n.language || codes[0] || "en").split(
|
|
31
|
+
"-"
|
|
32
|
+
)[0];
|
|
33
|
+
async function pick(code) {
|
|
34
|
+
if (!codes.includes(code)) return;
|
|
35
|
+
if (code === active) {
|
|
36
|
+
if (variant === "popover") setOpen(false);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await i18n.changeLanguage(code);
|
|
40
|
+
if (variant === "popover") setOpen(false);
|
|
41
|
+
if (onChange) {
|
|
42
|
+
try {
|
|
43
|
+
await onChange(code);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const list = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49
|
+
"div",
|
|
50
|
+
{
|
|
51
|
+
className: "language-picker-list",
|
|
52
|
+
role: "menu",
|
|
53
|
+
tabIndex: -1,
|
|
54
|
+
"aria-label": t("language.menuAria"),
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "language-picker-title", children: t("language.title") }),
|
|
57
|
+
languages.map((lng) => {
|
|
58
|
+
const isActive = lng.code === active;
|
|
59
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
60
|
+
"button",
|
|
61
|
+
{
|
|
62
|
+
type: "button",
|
|
63
|
+
role: "menuitemradio",
|
|
64
|
+
"aria-checked": isActive,
|
|
65
|
+
className: `language-picker-item ${isActive ? "active" : ""}`,
|
|
66
|
+
onClick: () => pick(lng.code),
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "language-picker-name", children: lng.nativeName }),
|
|
69
|
+
isActive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "language-picker-check", "aria-hidden": "true", children: "\u2713" })
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
lng.code
|
|
73
|
+
);
|
|
74
|
+
})
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
79
|
+
chunk6OZDKKEP_cjs.PopoverPicker,
|
|
80
|
+
{
|
|
81
|
+
variant,
|
|
82
|
+
open,
|
|
83
|
+
onOpenChange: setOpen,
|
|
84
|
+
className: "language-picker",
|
|
85
|
+
toggleAriaLabel: t("language.toggleAria"),
|
|
86
|
+
popoverAriaLabel: t("language.title"),
|
|
87
|
+
popoverRef,
|
|
88
|
+
inlineRef,
|
|
89
|
+
toggleIcon: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
90
|
+
"svg",
|
|
91
|
+
{
|
|
92
|
+
width: "20",
|
|
93
|
+
height: "20",
|
|
94
|
+
viewBox: "0 0 24 24",
|
|
95
|
+
fill: "none",
|
|
96
|
+
stroke: "currentColor",
|
|
97
|
+
strokeWidth: "1.8",
|
|
98
|
+
strokeLinecap: "round",
|
|
99
|
+
strokeLinejoin: "round",
|
|
100
|
+
"aria-hidden": "true",
|
|
101
|
+
children: [
|
|
102
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
103
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 12h20" }),
|
|
104
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" })
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
children: list
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
exports.DEFAULT_LANGUAGES = DEFAULT_LANGUAGES;
|
|
114
|
+
exports.LanguagePicker = LanguagePicker;
|
|
115
|
+
//# sourceMappingURL=chunk-LIGL56YJ.cjs.map
|
|
116
|
+
//# sourceMappingURL=chunk-LIGL56YJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/language/languages.ts","../src/language/LanguagePicker.tsx"],"names":["useTranslation","useState","useRef","useRovingFocus_default","jsxs","jsx","PopoverPicker"],"mappings":";;;;;;;;;AA4BO,IAAM,iBAAA,GAAgC;AAAA,EAC3C,EAAE,IAAA,EAAM,IAAA,EAAM,UAAA,EAAY,SAAA,EAAU;AAAA,EACpC,EAAE,IAAA,EAAM,IAAA,EAAM,UAAA,EAAY,YAAA,EAAU;AAAA,EACpC,EAAE,IAAA,EAAM,IAAA,EAAM,UAAA,EAAY,aAAA,EAAW;AAAA,EACrC,EAAE,IAAA,EAAM,IAAA,EAAM,UAAA,EAAY,cAAA,EAAK;AAAA,EAC/B,EAAE,IAAA,EAAM,IAAA,EAAM,UAAA,EAAY,SAAA;AAC5B;ACiBe,SAAR,cAAA,CAAgC;AAAA,EACrC,OAAA,GAAU,SAAA;AAAA,EACV,SAAA,GAAY,QAAA;AAAA,EACZ,SAAA,GAAY,iBAAA;AAAA,EACZ;AACF,CAAA,EAAwB;AACtB,EAAA,MAAM,EAAE,IAAA,EAAM,CAAA,EAAE,GAAIA,4BAAe,SAAS,CAAA;AAC5C,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIC,eAAS,KAAK,CAAA;AACtC,EAAA,MAAM,UAAA,GAAaC,aAA8B,IAAI,CAAA;AACrD,EAAA,MAAM,SAAA,GAAYA,aAA8B,IAAI,CAAA;AAEpD,EAAAC,wCAAA,CAAe,UAAA,EAAY,OAAA,KAAY,SAAA,IAAa,IAAI,CAAA;AACxD,EAAAA,wCAAA,CAAe,WAAW,OAAA,KAAY,QAAA,EAAU,EAAE,SAAA,EAAW,OAAO,CAAA;AAEpE,EAAA,MAAM,QAAQ,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAGzC,EAAA,MAAM,MAAA,GAAA,CAAU,KAAK,gBAAA,IAAoB,IAAA,CAAK,YAAY,KAAA,CAAM,CAAC,KAAK,IAAA,EAAM,KAAA;AAAA,IAC1E;AAAA,IACA,CAAC,CAAA;AAEH,EAAA,eAAe,KAAK,IAAA,EAAc;AAChC,IAAA,IAAI,CAAC,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,EAAG;AAC3B,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,IAAI,OAAA,KAAY,SAAA,EAAW,OAAA,CAAQ,KAAK,CAAA;AACxC,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,CAAK,eAAe,IAAI,CAAA;AAC9B,IAAA,IAAI,OAAA,KAAY,SAAA,EAAW,OAAA,CAAQ,KAAK,CAAA;AAExC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAI;AACF,QAAA,MAAM,SAAS,IAAI,CAAA;AAAA,MACrB,CAAA,CAAA,MAAQ;AAAA,MAGR;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,mBACJC,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,sBAAA;AAAA,MACV,IAAA,EAAK,MAAA;AAAA,MACL,QAAA,EAAU,EAAA;AAAA,MACV,YAAA,EAAY,EAAE,mBAAmB,CAAA;AAAA,MAEjC,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,CAAA,CAAE,gBAAgB,CAAA,EAAE,CAAA;AAAA,QAC3D,SAAA,CAAU,GAAA,CAAI,CAAC,GAAA,KAAQ;AACtB,UAAA,MAAM,QAAA,GAAW,IAAI,IAAA,KAAS,MAAA;AAC9B,UAAA,uBACED,eAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cAEC,IAAA,EAAK,QAAA;AAAA,cACL,IAAA,EAAK,eAAA;AAAA,cACL,cAAA,EAAc,QAAA;AAAA,cACd,SAAA,EAAW,CAAA,qBAAA,EAAwB,QAAA,GAAW,QAAA,GAAW,EAAE,CAAA,CAAA;AAAA,cAC3D,OAAA,EAAS,MAAM,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA;AAAA,cAE5B,QAAA,EAAA;AAAA,gCAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sBAAA,EAAwB,QAAA,EAAA,GAAA,CAAI,UAAA,EAAW,CAAA;AAAA,gBACtD,4BACCA,cAAA,CAAC,MAAA,EAAA,EAAK,WAAU,uBAAA,EAAwB,aAAA,EAAY,QAAO,QAAA,EAAA,QAAA,EAE3D;AAAA;AAAA,aAAA;AAAA,YAXG,GAAA,CAAI;AAAA,WAaX;AAAA,QAEJ,CAAC;AAAA;AAAA;AAAA,GACH;AAGF,EAAA,uBACEA,cAAA;AAAA,IAACC,+BAAA;AAAA,IAAA;AAAA,MACC,OAAA;AAAA,MACA,IAAA;AAAA,MACA,YAAA,EAAc,OAAA;AAAA,MACd,SAAA,EAAU,iBAAA;AAAA,MACV,eAAA,EAAiB,EAAE,qBAAqB,CAAA;AAAA,MACxC,gBAAA,EAAkB,EAAE,gBAAgB,CAAA;AAAA,MACpC,UAAA;AAAA,MACA,SAAA;AAAA,MACA,UAAA,kBACEF,eAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAM,IAAA;AAAA,UACN,MAAA,EAAO,IAAA;AAAA,UACP,OAAA,EAAQ,WAAA;AAAA,UACR,IAAA,EAAK,MAAA;AAAA,UACL,MAAA,EAAO,cAAA;AAAA,UACP,WAAA,EAAY,KAAA;AAAA,UACZ,aAAA,EAAc,OAAA;AAAA,UACd,cAAA,EAAe,OAAA;AAAA,UACf,aAAA,EAAY,MAAA;AAAA,UAEZ,QAAA,EAAA;AAAA,4BAAAC,cAAA,CAAC,YAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,GAAE,IAAA,EAAK,CAAA;AAAA,4BAC/BA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,UAAA,EAAW,CAAA;AAAA,4BACnBA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,4FAAA,EAA6F;AAAA;AAAA;AAAA,OACvG;AAAA,MAGD,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ","file":"chunk-LIGL56YJ.cjs","sourcesContent":["/* This file is part of @cavebatsofware/riposte-pickers\n * Copyright (C) 2026 Grant DeFayette\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, version 3 of the License (GPL-3.0-only).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.\n */\nexport interface Language {\n /** Base language code (no region subtag), e.g. `en`. Passed to i18next. */\n code: string;\n /**\n * Display name written in the language's own script (e.g. `Deutsch`,\n * `中文`). Shown verbatim in the list so a reader recognizes their own\n * language without a translation chain.\n */\n nativeName: string;\n}\n\n/// The bundled default catalog: the five riposte launch locales. Override\n/// via the `languages` prop on `LanguagePicker` to ship a different set.\nexport const DEFAULT_LANGUAGES: Language[] = [\n { code: \"en\", nativeName: \"English\" },\n { code: \"es\", nativeName: \"Español\" },\n { code: \"fr\", nativeName: \"Français\" },\n { code: \"zh\", nativeName: \"中文\" },\n { code: \"de\", nativeName: \"Deutsch\" },\n];\n","/* This file is part of @cavebatsofware/riposte-pickers\n * Copyright (C) 2026 Grant DeFayette\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, version 3 of the License (GPL-3.0-only).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.\n */\nimport { useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport PopoverPicker from \"../shared/PopoverPicker\";\nimport useRovingFocus from \"../shared/useRovingFocus\";\nimport { DEFAULT_LANGUAGES, type Language } from \"./languages\";\n\nexport interface LanguagePickerProps {\n /**\n * `popover` (default): a globe-icon button in a header that opens a\n * dropdown of languages. `inline`: the list rendered directly, for use\n * inside a drawer.\n */\n variant?: \"popover\" | \"inline\";\n /**\n * i18next namespace holding the `language.*` keys. Defaults to `common`.\n * Merge the package's `languageResources` into this namespace.\n */\n namespace?: string;\n /** Catalog of selectable languages. Defaults to the bundled five. */\n languages?: Language[];\n /**\n * Optional side effect fired after a successful switch, e.g. persisting the\n * choice server-side. Receives the chosen base code. Rejections are caught\n * so they never undo the local switch; handle errors inside the callback if\n * you need to surface them.\n */\n onChange?: (code: string) => void | Promise<void>;\n}\n\n/// Language picker built on the shared popover/roving-focus chassis.\n///\n/// Switching always calls `i18next.changeLanguage`, which (with the standard\n/// browser-language-detector `caches`) persists to localStorage. The optional\n/// `onChange` callback runs afterward for app-specific persistence (e.g. a\n/// server PATCH) and is intentionally decoupled: this component knows nothing\n/// about auth or your API.\nexport default function LanguagePicker({\n variant = \"popover\",\n namespace = \"common\",\n languages = DEFAULT_LANGUAGES,\n onChange,\n}: LanguagePickerProps) {\n const { i18n, t } = useTranslation(namespace);\n const [open, setOpen] = useState(false);\n const popoverRef = useRef<HTMLDivElement | null>(null);\n const inlineRef = useRef<HTMLDivElement | null>(null);\n\n useRovingFocus(popoverRef, variant === \"popover\" && open);\n useRovingFocus(inlineRef, variant === \"inline\", { autoFocus: false });\n\n const codes = languages.map((l) => l.code);\n // i18n.language can include a region subtag (e.g. \"en-US\"); compare against\n // the canonical base codes.\n const active = (i18n.resolvedLanguage || i18n.language || codes[0] || \"en\").split(\n \"-\",\n )[0];\n\n async function pick(code: string) {\n if (!codes.includes(code)) return;\n if (code === active) {\n if (variant === \"popover\") setOpen(false);\n return;\n }\n await i18n.changeLanguage(code);\n if (variant === \"popover\") setOpen(false);\n\n if (onChange) {\n try {\n await onChange(code);\n } catch {\n // The local switch already took effect; a failed persist must not\n // undo it. The consumer owns error reporting.\n }\n }\n }\n\n const list = (\n <div\n className=\"language-picker-list\"\n role=\"menu\"\n tabIndex={-1}\n aria-label={t(\"language.menuAria\")}\n >\n <div className=\"language-picker-title\">{t(\"language.title\")}</div>\n {languages.map((lng) => {\n const isActive = lng.code === active;\n return (\n <button\n key={lng.code}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={isActive}\n className={`language-picker-item ${isActive ? \"active\" : \"\"}`}\n onClick={() => pick(lng.code)}\n >\n <span className=\"language-picker-name\">{lng.nativeName}</span>\n {isActive && (\n <span className=\"language-picker-check\" aria-hidden=\"true\">\n ✓\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n\n return (\n <PopoverPicker\n variant={variant}\n open={open}\n onOpenChange={setOpen}\n className=\"language-picker\"\n toggleAriaLabel={t(\"language.toggleAria\")}\n popoverAriaLabel={t(\"language.title\")}\n popoverRef={popoverRef}\n inlineRef={inlineRef}\n toggleIcon={\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M2 12h20\" />\n <path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\" />\n </svg>\n }\n >\n {list}\n </PopoverPicker>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-U73YJG4C.cjs"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/shared/useRovingFocus.ts
|
|
4
|
+
var DEFAULT_SELECTOR = '[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"]';
|
|
5
|
+
function useRovingFocus(containerRef, active, {
|
|
6
|
+
selector = DEFAULT_SELECTOR,
|
|
7
|
+
orientation = "vertical",
|
|
8
|
+
wrap = true,
|
|
9
|
+
autoFocus = true
|
|
10
|
+
} = {}) {
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!active) return void 0;
|
|
13
|
+
const container = containerRef.current;
|
|
14
|
+
if (!container) return void 0;
|
|
15
|
+
function items() {
|
|
16
|
+
return Array.from(
|
|
17
|
+
container.querySelectorAll(selector)
|
|
18
|
+
).filter((el) => !el.hasAttribute("disabled"));
|
|
19
|
+
}
|
|
20
|
+
function setRovingTabIndex(list, focusedIndex) {
|
|
21
|
+
list.forEach((el, i) => {
|
|
22
|
+
el.tabIndex = i === focusedIndex ? 0 : -1;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const initial = items();
|
|
26
|
+
if (initial.length > 0) {
|
|
27
|
+
const checkedIdx = initial.findIndex(
|
|
28
|
+
(el) => el.getAttribute("aria-checked") === "true"
|
|
29
|
+
);
|
|
30
|
+
const startIdx = checkedIdx >= 0 ? checkedIdx : 0;
|
|
31
|
+
setRovingTabIndex(initial, startIdx);
|
|
32
|
+
if (autoFocus) initial[startIdx].focus();
|
|
33
|
+
}
|
|
34
|
+
const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
|
|
35
|
+
const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
|
|
36
|
+
function onKeyDown(e) {
|
|
37
|
+
if (![nextKey, prevKey, "Home", "End"].includes(e.key)) return;
|
|
38
|
+
const list = items();
|
|
39
|
+
if (list.length === 0) return;
|
|
40
|
+
const current = document.activeElement;
|
|
41
|
+
const idx = current ? list.indexOf(current) : -1;
|
|
42
|
+
let next;
|
|
43
|
+
if (e.key === "Home") {
|
|
44
|
+
next = 0;
|
|
45
|
+
} else if (e.key === "End") {
|
|
46
|
+
next = list.length - 1;
|
|
47
|
+
} else if (e.key === nextKey) {
|
|
48
|
+
next = idx + 1;
|
|
49
|
+
if (next >= list.length) next = wrap ? 0 : list.length - 1;
|
|
50
|
+
} else {
|
|
51
|
+
next = idx - 1;
|
|
52
|
+
if (next < 0) next = wrap ? list.length - 1 : 0;
|
|
53
|
+
}
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
setRovingTabIndex(list, next);
|
|
56
|
+
list[next].focus();
|
|
57
|
+
}
|
|
58
|
+
function onClick(e) {
|
|
59
|
+
const target = e.target?.closest(
|
|
60
|
+
selector
|
|
61
|
+
);
|
|
62
|
+
if (!target) return;
|
|
63
|
+
const list = items();
|
|
64
|
+
const idx = list.indexOf(target);
|
|
65
|
+
if (idx >= 0) setRovingTabIndex(list, idx);
|
|
66
|
+
}
|
|
67
|
+
container.addEventListener("keydown", onKeyDown);
|
|
68
|
+
container.addEventListener("click", onClick);
|
|
69
|
+
return () => {
|
|
70
|
+
container.removeEventListener("keydown", onKeyDown);
|
|
71
|
+
container.removeEventListener("click", onClick);
|
|
72
|
+
items().forEach((el) => {
|
|
73
|
+
el.removeAttribute("tabindex");
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
}, [active, containerRef, selector, orientation, wrap, autoFocus]);
|
|
77
|
+
}
|
|
78
|
+
var useRovingFocus_default = useRovingFocus;
|
|
79
|
+
|
|
80
|
+
export { useRovingFocus_default };
|
|
81
|
+
//# sourceMappingURL=chunk-XWM3AYYR.js.map
|
|
82
|
+
//# sourceMappingURL=chunk-XWM3AYYR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/useRovingFocus.ts"],"names":[],"mappings":"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","file":"chunk-XWM3AYYR.js","sourcesContent":["/* This file is part of @cavebatsofware/riposte-pickers\n * Copyright (C) 2026 Grant DeFayette\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, version 3 of the License (GPL-3.0-only).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.\n */\nimport { useEffect, type RefObject } from \"react\";\n\nexport interface RovingFocusOptions {\n /** CSS selector for the navigable items. Defaults to the ARIA menu roles. */\n selector?: string;\n /** Arrow-key axis. Defaults to vertical. */\n orientation?: \"vertical\" | \"horizontal\";\n /** Wrap from the last item back to the first (and vice versa). Default true. */\n wrap?: boolean;\n /** Move DOM focus into the list on activation. Default true. */\n autoFocus?: boolean;\n}\n\nconst DEFAULT_SELECTOR =\n '[role=\"menuitem\"], [role=\"menuitemradio\"], [role=\"menuitemcheckbox\"]';\n\n/// Wire roving-focus keyboard navigation onto a popover container.\n///\n/// Listens for Arrow / Home / End on the container element while `active`\n/// is true and moves focus across the matching descendants (default: any\n/// element with a `menuitem`, `menuitemradio`, or `menuitemcheckbox` role).\n///\n/// Implements the WAI-ARIA menu pattern's roving tabindex: only one item is\n/// in the tab order at a time (`tabindex=\"0\"`), and arrow keys move both DOM\n/// focus and the tab-order anchor across items. Tab from inside the menu\n/// therefore exits to the next focusable element in the document instead of\n/// cycling through siblings.\n///\n/// On activation the active item (or the first item if none is marked active)\n/// receives focus so screen reader users land inside the menu without an extra\n/// Tab. Consumers that need a focus trap should compose with a trap hook.\nexport function useRovingFocus(\n containerRef: RefObject<HTMLElement | null>,\n active: boolean,\n {\n selector = DEFAULT_SELECTOR,\n orientation = \"vertical\",\n wrap = true,\n autoFocus = true,\n }: RovingFocusOptions = {},\n) {\n useEffect(() => {\n if (!active) return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n\n function items(): HTMLElement[] {\n return Array.from(\n container!.querySelectorAll<HTMLElement>(selector),\n ).filter((el) => !el.hasAttribute(\"disabled\"));\n }\n\n function setRovingTabIndex(list: HTMLElement[], focusedIndex: number) {\n list.forEach((el, i) => {\n el.tabIndex = i === focusedIndex ? 0 : -1;\n });\n }\n\n const initial = items();\n if (initial.length > 0) {\n // Anchor the tab order on the first menuitemradio that's already\n // checked, otherwise the first item.\n const checkedIdx = initial.findIndex(\n (el) => el.getAttribute(\"aria-checked\") === \"true\",\n );\n const startIdx = checkedIdx >= 0 ? checkedIdx : 0;\n setRovingTabIndex(initial, startIdx);\n if (autoFocus) initial[startIdx].focus();\n }\n\n const nextKey = orientation === \"horizontal\" ? \"ArrowRight\" : \"ArrowDown\";\n const prevKey = orientation === \"horizontal\" ? \"ArrowLeft\" : \"ArrowUp\";\n\n function onKeyDown(e: KeyboardEvent) {\n if (![nextKey, prevKey, \"Home\", \"End\"].includes(e.key)) return;\n const list = items();\n if (list.length === 0) return;\n const current = document.activeElement as HTMLElement | null;\n const idx = current ? list.indexOf(current) : -1;\n let next: number;\n if (e.key === \"Home\") {\n next = 0;\n } else if (e.key === \"End\") {\n next = list.length - 1;\n } else if (e.key === nextKey) {\n next = idx + 1;\n if (next >= list.length) next = wrap ? 0 : list.length - 1;\n } else {\n next = idx - 1;\n if (next < 0) next = wrap ? list.length - 1 : 0;\n }\n e.preventDefault();\n setRovingTabIndex(list, next);\n list[next].focus();\n }\n\n // Click on any item also re-anchors the tab order. Without this, a\n // consumer that updates selection state via click leaves the tab-order\n // anchor stuck on the previously-selected item: aria-checked follows\n // React state, but tabindex was set imperatively at activation and never\n // moves.\n function onClick(e: MouseEvent) {\n const target = (e.target as HTMLElement | null)?.closest<HTMLElement>(\n selector,\n );\n if (!target) return;\n const list = items();\n const idx = list.indexOf(target);\n if (idx >= 0) setRovingTabIndex(list, idx);\n }\n\n container.addEventListener(\"keydown\", onKeyDown);\n container.addEventListener(\"click\", onClick);\n return () => {\n container.removeEventListener(\"keydown\", onKeyDown);\n container.removeEventListener(\"click\", onClick);\n // Restore items to default tab order on cleanup so the next mount\n // starts from a clean slate.\n items().forEach((el) => {\n el.removeAttribute(\"tabindex\");\n });\n };\n }, [active, containerRef, selector, orientation, wrap, autoFocus]);\n}\n\nexport default useRovingFocus;\n"]}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/i18n/theme/en.ts
|
|
4
|
+
var en = {
|
|
5
|
+
theme: {
|
|
6
|
+
title: "Theme",
|
|
7
|
+
toggleAria: "Choose theme",
|
|
8
|
+
dialogAria: "Choose theme",
|
|
9
|
+
colorwayAria: "Colorway",
|
|
10
|
+
modeAria: "Light or dark",
|
|
11
|
+
colorways: {
|
|
12
|
+
forest: "Forest & Cream",
|
|
13
|
+
warm: "Warm Editorial",
|
|
14
|
+
plum: "Plum & Apricot",
|
|
15
|
+
avernus: "Avernus & Clouds",
|
|
16
|
+
mineral: "Rocks & Minerals",
|
|
17
|
+
daltonia: "Red-Green Accessible",
|
|
18
|
+
tritan: "Blue-Yellow Accessible",
|
|
19
|
+
achroma: "High Contrast"
|
|
20
|
+
},
|
|
21
|
+
mode: {
|
|
22
|
+
light: "Light",
|
|
23
|
+
dark: "Dark"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/i18n/theme/de.ts
|
|
29
|
+
var de = {
|
|
30
|
+
theme: {
|
|
31
|
+
title: "Design",
|
|
32
|
+
toggleAria: "Design w\xE4hlen",
|
|
33
|
+
dialogAria: "Design w\xE4hlen",
|
|
34
|
+
colorwayAria: "Farbschema",
|
|
35
|
+
modeAria: "Hell oder dunkel",
|
|
36
|
+
colorways: {
|
|
37
|
+
forest: "Wald und Creme",
|
|
38
|
+
warm: "Warmes Editorial",
|
|
39
|
+
plum: "Pflaume und Aprikose",
|
|
40
|
+
avernus: "Avernus und Wolken",
|
|
41
|
+
mineral: "Gestein und Mineralien",
|
|
42
|
+
daltonia: "Rot-Gr\xFCn-Filter",
|
|
43
|
+
tritan: "Blau-Gelb-Filter",
|
|
44
|
+
achroma: "Hoher Kontrast"
|
|
45
|
+
},
|
|
46
|
+
mode: {
|
|
47
|
+
light: "Hell",
|
|
48
|
+
dark: "Dunkel"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/i18n/theme/es.ts
|
|
54
|
+
var es = {
|
|
55
|
+
theme: {
|
|
56
|
+
title: "Tema",
|
|
57
|
+
toggleAria: "Elegir tema",
|
|
58
|
+
dialogAria: "Elegir tema",
|
|
59
|
+
colorwayAria: "Combinaci\xF3n de colores",
|
|
60
|
+
modeAria: "Claro u oscuro",
|
|
61
|
+
colorways: {
|
|
62
|
+
forest: "Bosque y crema",
|
|
63
|
+
warm: "Editorial c\xE1lido",
|
|
64
|
+
plum: "Ciruela y albaricoque",
|
|
65
|
+
avernus: "Averno y nubes",
|
|
66
|
+
mineral: "Rocas y minerales",
|
|
67
|
+
daltonia: "Modo dalt\xF3nico rojo-verde",
|
|
68
|
+
tritan: "Modo dalt\xF3nico azul-amarillo",
|
|
69
|
+
achroma: "Alto contraste"
|
|
70
|
+
},
|
|
71
|
+
mode: {
|
|
72
|
+
light: "Claro",
|
|
73
|
+
dark: "Oscuro"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/i18n/theme/fr.ts
|
|
79
|
+
var fr = {
|
|
80
|
+
theme: {
|
|
81
|
+
title: "Th\xE8me",
|
|
82
|
+
toggleAria: "Choisir un th\xE8me",
|
|
83
|
+
dialogAria: "Choisir un th\xE8me",
|
|
84
|
+
colorwayAria: "Coloris",
|
|
85
|
+
modeAria: "Clair ou sombre",
|
|
86
|
+
colorways: {
|
|
87
|
+
forest: "For\xEAt et cr\xE8me",
|
|
88
|
+
warm: "\xC9ditorial chaleureux",
|
|
89
|
+
plum: "Prune et abricot",
|
|
90
|
+
avernus: "Averne et nuages",
|
|
91
|
+
mineral: "Roches et min\xE9raux",
|
|
92
|
+
daltonia: "Mode daltonien rouge-vert",
|
|
93
|
+
tritan: "Mode daltonien bleu-jaune",
|
|
94
|
+
achroma: "Contraste \xE9lev\xE9"
|
|
95
|
+
},
|
|
96
|
+
mode: {
|
|
97
|
+
light: "Clair",
|
|
98
|
+
dark: "Sombre"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/i18n/theme/zh.ts
|
|
104
|
+
var zh = {
|
|
105
|
+
theme: {
|
|
106
|
+
title: "\u4E3B\u9898",
|
|
107
|
+
toggleAria: "\u9009\u62E9\u4E3B\u9898",
|
|
108
|
+
dialogAria: "\u9009\u62E9\u4E3B\u9898",
|
|
109
|
+
colorwayAria: "\u914D\u8272",
|
|
110
|
+
modeAria: "\u6D45\u8272\u6216\u6DF1\u8272",
|
|
111
|
+
colorways: {
|
|
112
|
+
forest: "\u68EE\u6797\u4E0E\u5976\u6CB9",
|
|
113
|
+
warm: "\u6696\u8272\u8C03",
|
|
114
|
+
plum: "\u6885\u5B50\u4E0E\u674F",
|
|
115
|
+
avernus: "\u963F\u7EF4\u52AA\u65AF\u4E0E\u4E91",
|
|
116
|
+
mineral: "\u5CA9\u77F3\u4E0E\u77FF\u7269",
|
|
117
|
+
daltonia: "\u7EA2\u7EFF\u8272\u5F31\u6A21\u5F0F",
|
|
118
|
+
tritan: "\u84DD\u9EC4\u8272\u5F31\u6A21\u5F0F",
|
|
119
|
+
achroma: "\u9AD8\u5BF9\u6BD4\u5EA6"
|
|
120
|
+
},
|
|
121
|
+
mode: {
|
|
122
|
+
light: "\u6D45\u8272",
|
|
123
|
+
dark: "\u6DF1\u8272"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/i18n/language/en.ts
|
|
129
|
+
var en2 = {
|
|
130
|
+
language: {
|
|
131
|
+
title: "Language",
|
|
132
|
+
toggleAria: "Change language",
|
|
133
|
+
menuAria: "Choose language"
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/i18n/language/de.ts
|
|
138
|
+
var de2 = {
|
|
139
|
+
language: {
|
|
140
|
+
title: "Sprache",
|
|
141
|
+
toggleAria: "Sprache \xE4ndern",
|
|
142
|
+
menuAria: "Sprache w\xE4hlen"
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// src/i18n/language/es.ts
|
|
147
|
+
var es2 = {
|
|
148
|
+
language: {
|
|
149
|
+
title: "Idioma",
|
|
150
|
+
toggleAria: "Cambiar idioma",
|
|
151
|
+
menuAria: "Elegir idioma"
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/i18n/language/fr.ts
|
|
156
|
+
var fr2 = {
|
|
157
|
+
language: {
|
|
158
|
+
title: "Langue",
|
|
159
|
+
toggleAria: "Changer la langue",
|
|
160
|
+
menuAria: "Choisir une langue"
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/i18n/language/zh.ts
|
|
165
|
+
var zh2 = {
|
|
166
|
+
language: {
|
|
167
|
+
title: "\u8BED\u8A00",
|
|
168
|
+
toggleAria: "\u66F4\u6539\u8BED\u8A00",
|
|
169
|
+
menuAria: "\u9009\u62E9\u8BED\u8A00"
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/i18n/index.ts
|
|
174
|
+
var themeResources = {
|
|
175
|
+
en,
|
|
176
|
+
de,
|
|
177
|
+
es,
|
|
178
|
+
fr,
|
|
179
|
+
zh
|
|
180
|
+
};
|
|
181
|
+
var languageResources = {
|
|
182
|
+
en: en2,
|
|
183
|
+
de: de2,
|
|
184
|
+
es: es2,
|
|
185
|
+
fr: fr2,
|
|
186
|
+
zh: zh2
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
exports.languageResources = languageResources;
|
|
190
|
+
exports.themeResources = themeResources;
|
|
191
|
+
//# sourceMappingURL=index.cjs.map
|
|
192
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/i18n/theme/en.ts","../../src/i18n/theme/de.ts","../../src/i18n/theme/es.ts","../../src/i18n/theme/fr.ts","../../src/i18n/theme/zh.ts","../../src/i18n/language/en.ts","../../src/i18n/language/de.ts","../../src/i18n/language/es.ts","../../src/i18n/language/fr.ts","../../src/i18n/language/zh.ts","../../src/i18n/index.ts"],"names":["en","de","es","fr","zh"],"mappings":"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","file":"index.cjs","sourcesContent":["export const en = {\n theme: {\n title: \"Theme\",\n toggleAria: \"Choose theme\",\n dialogAria: \"Choose theme\",\n colorwayAria: \"Colorway\",\n modeAria: \"Light or dark\",\n colorways: {\n forest: \"Forest & Cream\",\n warm: \"Warm Editorial\",\n plum: \"Plum & Apricot\",\n avernus: \"Avernus & Clouds\",\n mineral: \"Rocks & Minerals\",\n daltonia: \"Red-Green Accessible\",\n tritan: \"Blue-Yellow Accessible\",\n achroma: \"High Contrast\",\n },\n mode: {\n light: \"Light\",\n dark: \"Dark\",\n },\n },\n};\n","export const de = {\n theme: {\n title: \"Design\",\n toggleAria: \"Design wählen\",\n dialogAria: \"Design wählen\",\n colorwayAria: \"Farbschema\",\n modeAria: \"Hell oder dunkel\",\n colorways: {\n forest: \"Wald und Creme\",\n warm: \"Warmes Editorial\",\n plum: \"Pflaume und Aprikose\",\n avernus: \"Avernus und Wolken\",\n mineral: \"Gestein und Mineralien\",\n daltonia: \"Rot-Grün-Filter\",\n tritan: \"Blau-Gelb-Filter\",\n achroma: \"Hoher Kontrast\",\n },\n mode: {\n light: \"Hell\",\n dark: \"Dunkel\",\n },\n },\n};\n","export const es = {\n theme: {\n title: \"Tema\",\n toggleAria: \"Elegir tema\",\n dialogAria: \"Elegir tema\",\n colorwayAria: \"Combinación de colores\",\n modeAria: \"Claro u oscuro\",\n colorways: {\n forest: \"Bosque y crema\",\n warm: \"Editorial cálido\",\n plum: \"Ciruela y albaricoque\",\n avernus: \"Averno y nubes\",\n mineral: \"Rocas y minerales\",\n daltonia: \"Modo daltónico rojo-verde\",\n tritan: \"Modo daltónico azul-amarillo\",\n achroma: \"Alto contraste\",\n },\n mode: {\n light: \"Claro\",\n dark: \"Oscuro\",\n },\n },\n};\n","export const fr = {\n theme: {\n title: \"Thème\",\n toggleAria: \"Choisir un thème\",\n dialogAria: \"Choisir un thème\",\n colorwayAria: \"Coloris\",\n modeAria: \"Clair ou sombre\",\n colorways: {\n forest: \"Forêt et crème\",\n warm: \"Éditorial chaleureux\",\n plum: \"Prune et abricot\",\n avernus: \"Averne et nuages\",\n mineral: \"Roches et minéraux\",\n daltonia: \"Mode daltonien rouge-vert\",\n tritan: \"Mode daltonien bleu-jaune\",\n achroma: \"Contraste élevé\",\n },\n mode: {\n light: \"Clair\",\n dark: \"Sombre\",\n },\n },\n};\n","export const zh = {\n theme: {\n title: \"主题\",\n toggleAria: \"选择主题\",\n dialogAria: \"选择主题\",\n colorwayAria: \"配色\",\n modeAria: \"浅色或深色\",\n colorways: {\n forest: \"森林与奶油\",\n warm: \"暖色调\",\n plum: \"梅子与杏\",\n avernus: \"阿维努斯与云\",\n mineral: \"岩石与矿物\",\n daltonia: \"红绿色弱模式\",\n tritan: \"蓝黄色弱模式\",\n achroma: \"高对比度\",\n },\n mode: {\n light: \"浅色\",\n dark: \"深色\",\n },\n },\n};\n","export const en = {\n language: {\n title: \"Language\",\n toggleAria: \"Change language\",\n menuAria: \"Choose language\",\n },\n};\n","export const de = {\n language: {\n title: \"Sprache\",\n toggleAria: \"Sprache ändern\",\n menuAria: \"Sprache wählen\",\n },\n};\n","export const es = {\n language: {\n title: \"Idioma\",\n toggleAria: \"Cambiar idioma\",\n menuAria: \"Elegir idioma\",\n },\n};\n","export const fr = {\n language: {\n title: \"Langue\",\n toggleAria: \"Changer la langue\",\n menuAria: \"Choisir une langue\",\n },\n};\n","export const zh = {\n language: {\n title: \"语言\",\n toggleAria: \"更改语言\",\n menuAria: \"选择语言\",\n },\n};\n","import { en as themeEn } from \"./theme/en\";\nimport { de as themeDe } from \"./theme/de\";\nimport { es as themeEs } from \"./theme/es\";\nimport { fr as themeFr } from \"./theme/fr\";\nimport { zh as themeZh } from \"./theme/zh\";\n\nimport { en as langEn } from \"./language/en\";\nimport { de as langDe } from \"./language/de\";\nimport { es as langEs } from \"./language/es\";\nimport { fr as langFr } from \"./language/fr\";\nimport { zh as langZh } from \"./language/zh\";\n\n/// Theme translation fragments keyed by language. Each value is a\n/// `{ theme: {...} }` tree meant to be merged into your i18next namespace\n/// (default `common`):\n///\n/// import { themeResources } from \"@cavebatsofware/riposte-pickers/i18n\";\n/// for (const [lng, res] of Object.entries(themeResources)) {\n/// i18n.addResourceBundle(lng, \"common\", res, true, true);\n/// }\nexport const themeResources = {\n en: themeEn,\n de: themeDe,\n es: themeEs,\n fr: themeFr,\n zh: themeZh,\n};\n\n/// Language-picker translation fragments keyed by language. Each value is a\n/// `{ language: {...} }` tree, merged the same way as `themeResources`.\nexport const languageResources = {\n en: langEn,\n de: langDe,\n es: langEs,\n fr: langFr,\n zh: langZh,\n};\n"]}
|