@5voltfx/design-system 0.1.1 → 0.3.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/dist/chunk-2KHDM6UP.js +58 -0
- package/dist/chunk-2KHDM6UP.js.map +1 -0
- package/dist/chunk-45346SXP.js +65 -0
- package/dist/chunk-45346SXP.js.map +1 -0
- package/dist/chunk-BAISLPWP.js +14 -0
- package/dist/chunk-BAISLPWP.js.map +1 -0
- package/dist/chunk-CT2TMR44.js +1 -0
- package/dist/chunk-CT2TMR44.js.map +1 -0
- package/dist/chunk-DD5VKG3L.js +22 -0
- package/dist/chunk-DD5VKG3L.js.map +1 -0
- package/dist/chunk-HLP2MPQ6.js +110 -0
- package/dist/chunk-HLP2MPQ6.js.map +1 -0
- package/dist/chunk-HPOL5NKF.js +22 -0
- package/dist/chunk-HPOL5NKF.js.map +1 -0
- package/dist/chunk-I3NS5MAC.js +23 -0
- package/dist/chunk-I3NS5MAC.js.map +1 -0
- package/dist/chunk-LPEUQ2DE.js +48 -0
- package/dist/chunk-LPEUQ2DE.js.map +1 -0
- package/dist/chunk-LY5MPGW2.js +129 -0
- package/dist/chunk-LY5MPGW2.js.map +1 -0
- package/dist/chunk-NJ2SBQYA.js +1208 -0
- package/dist/chunk-NJ2SBQYA.js.map +1 -0
- package/dist/chunk-R42A3ZCY.js +16 -0
- package/dist/chunk-R42A3ZCY.js.map +1 -0
- package/dist/chunk-UZW4E2UH.js +206 -0
- package/dist/chunk-UZW4E2UH.js.map +1 -0
- package/dist/chunk-VRGTUKSJ.js +20 -0
- package/dist/chunk-VRGTUKSJ.js.map +1 -0
- package/dist/chunk-YLUQANAI.js +1023 -0
- package/dist/chunk-YLUQANAI.js.map +1 -0
- package/dist/chunk-ZVUDPTFZ.js +76 -0
- package/dist/chunk-ZVUDPTFZ.js.map +1 -0
- package/dist/components/Accordion/Accordion.d.ts +29 -0
- package/dist/components/Accordion/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion/index.css +58 -0
- package/dist/components/Accordion/index.css.map +1 -0
- package/dist/components/Accordion/index.d.ts +2 -0
- package/dist/components/Accordion/index.d.ts.map +1 -0
- package/dist/components/Accordion/index.js +7 -0
- package/dist/components/Accordion/index.js.map +1 -0
- package/dist/components/Button/Button.d.ts +8 -0
- package/dist/components/Button/Button.d.ts.map +1 -0
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Button/index.d.ts.map +1 -0
- package/dist/components/Card/Card.d.ts +6 -0
- package/dist/components/Card/Card.d.ts.map +1 -0
- package/dist/components/Card/index.d.ts +2 -0
- package/dist/components/Card/index.d.ts.map +1 -0
- package/dist/components/Checkbox/Checkbox.d.ts +15 -0
- package/dist/components/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox/index.css +57 -0
- package/dist/components/Checkbox/index.css.map +1 -0
- package/dist/components/Checkbox/index.d.ts +2 -0
- package/dist/components/Checkbox/index.d.ts.map +1 -0
- package/dist/components/Checkbox/index.js +7 -0
- package/dist/components/Checkbox/index.js.map +1 -0
- package/dist/components/DialNav/DialNav.d.ts +48 -0
- package/dist/components/DialNav/DialNav.d.ts.map +1 -0
- package/dist/components/DialNav/geometry.d.ts +159 -0
- package/dist/components/DialNav/geometry.d.ts.map +1 -0
- package/dist/components/DialNav/index.css +321 -0
- package/dist/components/DialNav/index.css.map +1 -0
- package/dist/components/DialNav/index.d.ts +2 -0
- package/dist/components/DialNav/index.d.ts.map +1 -0
- package/dist/components/DialNav/index.js +8 -0
- package/dist/components/DialNav/index.js.map +1 -0
- package/dist/components/DialNav/variants/Concentric.d.ts +10 -0
- package/dist/components/DialNav/variants/Concentric.d.ts.map +1 -0
- package/dist/components/DialNav/variants/RatchetFan.d.ts +12 -0
- package/dist/components/DialNav/variants/RatchetFan.d.ts.map +1 -0
- package/dist/components/DialNav/variants/Volvelle.d.ts +12 -0
- package/dist/components/DialNav/variants/Volvelle.d.ts.map +1 -0
- package/dist/components/Footer/Footer.d.ts +15 -0
- package/dist/components/Footer/Footer.d.ts.map +1 -0
- package/dist/components/Footer/index.d.ts +2 -0
- package/dist/components/Footer/index.d.ts.map +1 -0
- package/dist/components/FormField/FormField.d.ts +27 -0
- package/dist/components/FormField/FormField.d.ts.map +1 -0
- package/dist/components/FormField/index.css +31 -0
- package/dist/components/FormField/index.css.map +1 -0
- package/dist/components/FormField/index.d.ts +2 -0
- package/dist/components/FormField/index.d.ts.map +1 -0
- package/dist/components/FormField/index.js +8 -0
- package/dist/components/FormField/index.js.map +1 -0
- package/dist/components/Header/Header.d.ts +15 -0
- package/dist/components/Header/Header.d.ts.map +1 -0
- package/dist/components/Header/index.d.ts +2 -0
- package/dist/components/Header/index.d.ts.map +1 -0
- package/dist/components/Input/Input.d.ts +16 -0
- package/dist/components/Input/Input.d.ts.map +1 -0
- package/dist/components/Input/index.css +45 -0
- package/dist/components/Input/index.css.map +1 -0
- package/dist/components/Input/index.d.ts +2 -0
- package/dist/components/Input/index.d.ts.map +1 -0
- package/dist/components/Input/index.js +7 -0
- package/dist/components/Input/index.js.map +1 -0
- package/dist/components/Label/Label.d.ts +15 -0
- package/dist/components/Label/Label.d.ts.map +1 -0
- package/dist/components/Label/index.css +14 -0
- package/dist/components/Label/index.css.map +1 -0
- package/dist/components/Label/index.d.ts +2 -0
- package/dist/components/Label/index.d.ts.map +1 -0
- package/dist/components/Label/index.js +8 -0
- package/dist/components/Label/index.js.map +1 -0
- package/dist/components/Layout/Container.d.ts +6 -0
- package/dist/components/Layout/Container.d.ts.map +1 -0
- package/dist/components/Layout/Grid.d.ts +9 -0
- package/dist/components/Layout/Grid.d.ts.map +1 -0
- package/dist/components/Layout/Stack.d.ts +10 -0
- package/dist/components/Layout/Stack.d.ts.map +1 -0
- package/dist/components/Layout/index.d.ts +4 -0
- package/dist/components/Layout/index.d.ts.map +1 -0
- package/dist/components/Menu/Menu.d.ts +30 -0
- package/dist/components/Menu/Menu.d.ts.map +1 -0
- package/dist/components/Menu/index.css +77 -0
- package/dist/components/Menu/index.css.map +1 -0
- package/dist/components/Menu/index.d.ts +2 -0
- package/dist/components/Menu/index.d.ts.map +1 -0
- package/dist/components/Menu/index.js +7 -0
- package/dist/components/Menu/index.js.map +1 -0
- package/dist/components/Modal/Modal.d.ts +24 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -0
- package/dist/components/Modal/index.css +101 -0
- package/dist/components/Modal/index.css.map +1 -0
- package/dist/components/Modal/index.d.ts +2 -0
- package/dist/components/Modal/index.d.ts.map +1 -0
- package/dist/components/Modal/index.js +7 -0
- package/dist/components/Modal/index.js.map +1 -0
- package/dist/components/Nav/Nav.d.ts +10 -0
- package/dist/components/Nav/Nav.d.ts.map +1 -0
- package/dist/components/Nav/index.d.ts +2 -0
- package/dist/components/Nav/index.d.ts.map +1 -0
- package/dist/components/Radio/Radio.d.ts +17 -0
- package/dist/components/Radio/Radio.d.ts.map +1 -0
- package/dist/components/Radio/RadioGroup.d.ts +25 -0
- package/dist/components/Radio/RadioGroup.d.ts.map +1 -0
- package/dist/components/Radio/RadioGroupContext.d.ts +7 -0
- package/dist/components/Radio/RadioGroupContext.d.ts.map +1 -0
- package/dist/components/Radio/index.css +57 -0
- package/dist/components/Radio/index.css.map +1 -0
- package/dist/components/Radio/index.d.ts +3 -0
- package/dist/components/Radio/index.d.ts.map +1 -0
- package/dist/components/Radio/index.js +9 -0
- package/dist/components/Radio/index.js.map +1 -0
- package/dist/components/Select/Select.d.ts +18 -0
- package/dist/components/Select/Select.d.ts.map +1 -0
- package/dist/components/Select/index.css +52 -0
- package/dist/components/Select/index.css.map +1 -0
- package/dist/components/Select/index.d.ts +2 -0
- package/dist/components/Select/index.d.ts.map +1 -0
- package/dist/components/Select/index.js +7 -0
- package/dist/components/Select/index.js.map +1 -0
- package/dist/components/SlideRuleNav/SlideRuleNav.d.ts +54 -0
- package/dist/components/SlideRuleNav/SlideRuleNav.d.ts.map +1 -0
- package/dist/components/SlideRuleNav/geometry.d.ts +168 -0
- package/dist/components/SlideRuleNav/geometry.d.ts.map +1 -0
- package/dist/components/SlideRuleNav/index.css +196 -0
- package/dist/components/SlideRuleNav/index.css.map +1 -0
- package/dist/components/SlideRuleNav/index.d.ts +2 -0
- package/dist/components/SlideRuleNav/index.d.ts.map +1 -0
- package/dist/components/SlideRuleNav/index.js +8 -0
- package/dist/components/SlideRuleNav/index.js.map +1 -0
- package/dist/components/SlideRuleNav/variants/CursorRule.d.ts +11 -0
- package/dist/components/SlideRuleNav/variants/CursorRule.d.ts.map +1 -0
- package/dist/components/SlideRuleNav/variants/SlideStator.d.ts +13 -0
- package/dist/components/SlideRuleNav/variants/SlideStator.d.ts.map +1 -0
- package/dist/components/SlideRuleNav/variants/VernierCarriage.d.ts +13 -0
- package/dist/components/SlideRuleNav/variants/VernierCarriage.d.ts.map +1 -0
- package/dist/components/Switch/Switch.d.ts +15 -0
- package/dist/components/Switch/Switch.d.ts.map +1 -0
- package/dist/components/Switch/index.css +49 -0
- package/dist/components/Switch/index.css.map +1 -0
- package/dist/components/Switch/index.d.ts +2 -0
- package/dist/components/Switch/index.d.ts.map +1 -0
- package/dist/components/Switch/index.js +7 -0
- package/dist/components/Switch/index.js.map +1 -0
- package/dist/components/Tabs/Tabs.d.ts +34 -0
- package/dist/components/Tabs/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs/index.css +54 -0
- package/dist/components/Tabs/index.css.map +1 -0
- package/dist/components/Tabs/index.d.ts +2 -0
- package/dist/components/Tabs/index.d.ts.map +1 -0
- package/dist/components/Tabs/index.js +7 -0
- package/dist/components/Tabs/index.js.map +1 -0
- package/dist/components/Textarea/Textarea.d.ts +16 -0
- package/dist/components/Textarea/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea/index.css +36 -0
- package/dist/components/Textarea/index.css.map +1 -0
- package/dist/components/Textarea/index.d.ts +2 -0
- package/dist/components/Textarea/index.d.ts.map +1 -0
- package/dist/components/Textarea/index.js +7 -0
- package/dist/components/Textarea/index.js.map +1 -0
- package/dist/components/ThemeSwitcher/ThemeSwitcher.d.ts +4 -0
- package/dist/components/ThemeSwitcher/ThemeSwitcher.d.ts.map +1 -0
- package/dist/components/ThemeSwitcher/index.d.ts +2 -0
- package/dist/components/ThemeSwitcher/index.d.ts.map +1 -0
- package/dist/components/Typography/Heading.d.ts +9 -0
- package/dist/components/Typography/Heading.d.ts.map +1 -0
- package/dist/components/Typography/Text.d.ts +9 -0
- package/dist/components/Typography/Text.d.ts.map +1 -0
- package/dist/components/Typography/index.d.ts +3 -0
- package/dist/components/Typography/index.d.ts.map +1 -0
- package/dist/components/_shared/useDetentTravel.d.ts +87 -0
- package/dist/components/_shared/useDetentTravel.d.ts.map +1 -0
- package/dist/components/_shared/usePointerDrag.d.ts +24 -0
- package/dist/components/_shared/usePointerDrag.d.ts.map +1 -0
- package/dist/components/_shared/useReducedMotion.d.ts +7 -0
- package/dist/components/_shared/useReducedMotion.d.ts.map +1 -0
- package/dist/components/_shared/validateStationCount.d.ts +13 -0
- package/dist/components/_shared/validateStationCount.d.ts.map +1 -0
- package/dist/index.css +1175 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -10
- package/dist/index.js.map +1 -1
- package/dist/theme/ThemeContext.d.ts +2 -0
- package/dist/theme/ThemeContext.d.ts.map +1 -0
- package/dist/theme/ThemeProvider.d.ts +12 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/themeConstants.d.ts +8 -0
- package/dist/theme/themeConstants.d.ts.map +1 -0
- package/dist/theme/useTheme.d.ts +2 -0
- package/dist/theme/useTheme.d.ts.map +1 -0
- package/package.json +21 -5
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/components/Accordion/Accordion.jsx
|
|
2
|
+
import { useId, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function Accordion({
|
|
5
|
+
items = [],
|
|
6
|
+
multiple = false,
|
|
7
|
+
defaultOpen = [],
|
|
8
|
+
className = ""
|
|
9
|
+
}) {
|
|
10
|
+
const baseId = useId();
|
|
11
|
+
const [openIds, setOpenIds] = useState(() => new Set(defaultOpen));
|
|
12
|
+
function toggle(id) {
|
|
13
|
+
setOpenIds((prev) => {
|
|
14
|
+
const next = new Set(multiple ? prev : []);
|
|
15
|
+
if (prev.has(id)) next.delete(id);
|
|
16
|
+
else next.add(id);
|
|
17
|
+
return next;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return /* @__PURE__ */ jsx("div", { className: ["ds-accordion", className].filter(Boolean).join(" "), children: items.map((item) => {
|
|
21
|
+
const isOpen = openIds.has(item.id);
|
|
22
|
+
const headerId = `${baseId}-${item.id}-header`;
|
|
23
|
+
const panelId = `${baseId}-${item.id}-panel`;
|
|
24
|
+
return /* @__PURE__ */ jsxs("div", { className: "ds-accordion__item", children: [
|
|
25
|
+
/* @__PURE__ */ jsx("h3", { className: "ds-accordion__heading", children: /* @__PURE__ */ jsxs(
|
|
26
|
+
"button",
|
|
27
|
+
{
|
|
28
|
+
type: "button",
|
|
29
|
+
id: headerId,
|
|
30
|
+
className: "ds-accordion__trigger",
|
|
31
|
+
"aria-expanded": isOpen,
|
|
32
|
+
"aria-controls": panelId,
|
|
33
|
+
onClick: () => toggle(item.id),
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx("span", { children: item.title }),
|
|
36
|
+
/* @__PURE__ */ jsx("span", { className: "ds-accordion__icon", "aria-hidden": "true" })
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
) }),
|
|
40
|
+
/* @__PURE__ */ jsx(
|
|
41
|
+
"div",
|
|
42
|
+
{
|
|
43
|
+
id: panelId,
|
|
44
|
+
className: "ds-accordion__panel",
|
|
45
|
+
role: "region",
|
|
46
|
+
"aria-labelledby": headerId,
|
|
47
|
+
hidden: !isOpen,
|
|
48
|
+
children: /* @__PURE__ */ jsx("div", { className: "ds-accordion__content", children: item.content })
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
] }, item.id);
|
|
52
|
+
}) });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
Accordion
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=chunk-2KHDM6UP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Accordion/Accordion.jsx"],"sourcesContent":["import { useId, useState } from \"react\";\nimport \"./Accordion.css\";\n\n/**\n * @typedef {object} AccordionItem\n * @property {string} id\n * @property {import(\"react\").ReactNode} title\n * @property {import(\"react\").ReactNode} content\n */\n\n/**\n * Disclosure list built on native <button aria-expanded> + a region panel.\n * Single-open by default; set `multiple` to allow several panels open at once.\n *\n * @param {object} props\n * @param {AccordionItem[]} props.items\n * @param {boolean} [props.multiple] - Allow multiple panels open. Default false.\n * @param {string[]} [props.defaultOpen] - Item ids open initially.\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Accordion({\n items = [],\n multiple = false,\n defaultOpen = [],\n className = \"\",\n}) {\n const baseId = useId();\n const [openIds, setOpenIds] = useState(() => new Set(defaultOpen));\n\n function toggle(id) {\n setOpenIds((prev) => {\n const next = new Set(multiple ? prev : []);\n if (prev.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }\n\n return (\n <div className={[\"ds-accordion\", className].filter(Boolean).join(\" \")}>\n {items.map((item) => {\n const isOpen = openIds.has(item.id);\n const headerId = `${baseId}-${item.id}-header`;\n const panelId = `${baseId}-${item.id}-panel`;\n return (\n <div className=\"ds-accordion__item\" key={item.id}>\n <h3 className=\"ds-accordion__heading\">\n <button\n type=\"button\"\n id={headerId}\n className=\"ds-accordion__trigger\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n onClick={() => toggle(item.id)}\n >\n <span>{item.title}</span>\n <span className=\"ds-accordion__icon\" aria-hidden=\"true\" />\n </button>\n </h3>\n <div\n id={panelId}\n className=\"ds-accordion__panel\"\n role=\"region\"\n aria-labelledby={headerId}\n hidden={!isOpen}\n >\n <div className=\"ds-accordion__content\">{item.content}</div>\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";AAAA,SAAS,OAAO,gBAAgB;AAgDlB,SAQE,KARF;AA3BP,SAAS,UAAU;AAAA,EACxB,QAAQ,CAAC;AAAA,EACT,WAAW;AAAA,EACX,cAAc,CAAC;AAAA,EACf,YAAY;AACd,GAAG;AACD,QAAM,SAAS,MAAM;AACrB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,MAAM,IAAI,IAAI,WAAW,CAAC;AAEjE,WAAS,OAAO,IAAI;AAClB,eAAW,CAAC,SAAS;AACnB,YAAM,OAAO,IAAI,IAAI,WAAW,OAAO,CAAC,CAAC;AACzC,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAC3B,MAAK,IAAI,EAAE;AAChB,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SACE,oBAAC,SAAI,WAAW,CAAC,gBAAgB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GACjE,gBAAM,IAAI,CAAC,SAAS;AACnB,UAAM,SAAS,QAAQ,IAAI,KAAK,EAAE;AAClC,UAAM,WAAW,GAAG,MAAM,IAAI,KAAK,EAAE;AACrC,UAAM,UAAU,GAAG,MAAM,IAAI,KAAK,EAAE;AACpC,WACE,qBAAC,SAAI,WAAU,sBACb;AAAA,0BAAC,QAAG,WAAU,yBACZ;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,IAAI;AAAA,UACJ,WAAU;AAAA,UACV,iBAAe;AAAA,UACf,iBAAe;AAAA,UACf,SAAS,MAAM,OAAO,KAAK,EAAE;AAAA,UAE7B;AAAA,gCAAC,UAAM,eAAK,OAAM;AAAA,YAClB,oBAAC,UAAK,WAAU,sBAAqB,eAAY,QAAO;AAAA;AAAA;AAAA,MAC1D,GACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAU;AAAA,UACV,MAAK;AAAA,UACL,mBAAiB;AAAA,UACjB,QAAQ,CAAC;AAAA,UAET,8BAAC,SAAI,WAAU,yBAAyB,eAAK,SAAQ;AAAA;AAAA,MACvD;AAAA,SAtBuC,KAAK,EAuB9C;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/components/Radio/Radio.jsx
|
|
2
|
+
import { useContext } from "react";
|
|
3
|
+
|
|
4
|
+
// src/components/Radio/RadioGroupContext.js
|
|
5
|
+
import { createContext } from "react";
|
|
6
|
+
var RadioGroupContext = createContext(null);
|
|
7
|
+
|
|
8
|
+
// src/components/Radio/Radio.jsx
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
function Radio({ label, value, className = "", ...rest }) {
|
|
11
|
+
const group = useContext(RadioGroupContext);
|
|
12
|
+
const groupProps = group ? {
|
|
13
|
+
name: group.name,
|
|
14
|
+
checked: group.value === value,
|
|
15
|
+
onChange: () => group.onChange?.(value)
|
|
16
|
+
} : {};
|
|
17
|
+
return /* @__PURE__ */ jsxs("label", { className: ["ds-radio", className].filter(Boolean).join(" "), children: [
|
|
18
|
+
/* @__PURE__ */ jsx(
|
|
19
|
+
"input",
|
|
20
|
+
{
|
|
21
|
+
type: "radio",
|
|
22
|
+
className: "ds-radio__input",
|
|
23
|
+
value,
|
|
24
|
+
...groupProps,
|
|
25
|
+
...rest
|
|
26
|
+
}
|
|
27
|
+
),
|
|
28
|
+
/* @__PURE__ */ jsx("span", { className: "ds-radio__dot", "aria-hidden": "true" }),
|
|
29
|
+
label != null && /* @__PURE__ */ jsx("span", { className: "ds-radio__label", children: label })
|
|
30
|
+
] });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/components/Radio/RadioGroup.jsx
|
|
34
|
+
import { useId, useMemo } from "react";
|
|
35
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
36
|
+
function RadioGroup({
|
|
37
|
+
name,
|
|
38
|
+
value,
|
|
39
|
+
onChange,
|
|
40
|
+
options,
|
|
41
|
+
children,
|
|
42
|
+
className = "",
|
|
43
|
+
...rest
|
|
44
|
+
}) {
|
|
45
|
+
const autoName = useId();
|
|
46
|
+
const ctx = useMemo(
|
|
47
|
+
() => ({ name: name ?? autoName, value, onChange }),
|
|
48
|
+
[name, autoName, value, onChange]
|
|
49
|
+
);
|
|
50
|
+
return /* @__PURE__ */ jsx2(RadioGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx2(
|
|
51
|
+
"div",
|
|
52
|
+
{
|
|
53
|
+
role: "radiogroup",
|
|
54
|
+
className: ["ds-radio-group", className].filter(Boolean).join(" "),
|
|
55
|
+
...rest,
|
|
56
|
+
children: options ? options.map((opt) => /* @__PURE__ */ jsx2(Radio, { value: opt.value, label: opt.label }, opt.value)) : children
|
|
57
|
+
}
|
|
58
|
+
) });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
Radio,
|
|
63
|
+
RadioGroup
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=chunk-45346SXP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Radio/Radio.jsx","../src/components/Radio/RadioGroupContext.js","../src/components/Radio/RadioGroup.jsx"],"sourcesContent":["import { useContext } from \"react\";\nimport { RadioGroupContext } from \"./RadioGroupContext.js\";\nimport \"./Radio.css\";\n\n/**\n * Radio button with a styled indicator over a native <input type=\"radio\">.\n * Inside a RadioGroup it inherits `name` and selection state automatically;\n * standalone, pass `name`/`checked`/`onChange` yourself via `...rest`.\n *\n * @param {object} props\n * @param {import(\"react\").ReactNode} [props.label] - Visible label text.\n * @param {string} [props.value] - This option's value (used by RadioGroup).\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Radio({ label, value, className = \"\", ...rest }) {\n const group = useContext(RadioGroupContext);\n\n const groupProps = group\n ? {\n name: group.name,\n checked: group.value === value,\n onChange: () => group.onChange?.(value),\n }\n : {};\n\n return (\n <label className={[\"ds-radio\", className].filter(Boolean).join(\" \")}>\n <input\n type=\"radio\"\n className=\"ds-radio__input\"\n value={value}\n {...groupProps}\n {...rest}\n />\n <span className=\"ds-radio__dot\" aria-hidden=\"true\" />\n {label != null && <span className=\"ds-radio__label\">{label}</span>}\n </label>\n );\n}\n","import { createContext } from \"react\";\n\n/**\n * Lets a RadioGroup share its `name` and controlled value/onChange with any\n * Radio rendered inside it, without prop-drilling. `null` when a Radio is\n * used standalone.\n */\nexport const RadioGroupContext = createContext(null);\n","import { useId, useMemo } from \"react\";\nimport { RadioGroupContext } from \"./RadioGroupContext.js\";\nimport { Radio } from \"./Radio.jsx\";\nimport \"./Radio.css\";\n\n/**\n * Groups Radios under one `name` with a single controlled selection. Provide\n * either an `options` array or Radio children.\n *\n * @param {object} props\n * @param {string} [props.name] - Shared input name. Auto-generated if omitted.\n * @param {string} [props.value] - Currently selected value (controlled).\n * @param {(value: string) => void} [props.onChange]\n * @param {{ value: string, label: import(\"react\").ReactNode }[]} [props.options]\n * @param {import(\"react\").ReactNode} [props.children]\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function RadioGroup({\n name,\n value,\n onChange,\n options,\n children,\n className = \"\",\n ...rest\n}) {\n const autoName = useId();\n const ctx = useMemo(\n () => ({ name: name ?? autoName, value, onChange }),\n [name, autoName, value, onChange]\n );\n\n return (\n <RadioGroupContext.Provider value={ctx}>\n <div\n role=\"radiogroup\"\n className={[\"ds-radio-group\", className].filter(Boolean).join(\" \")}\n {...rest}\n >\n {options\n ? options.map((opt) => (\n <Radio key={opt.value} value={opt.value} label={opt.label} />\n ))\n : children}\n </div>\n </RadioGroupContext.Provider>\n );\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;;;ACA3B,SAAS,qBAAqB;AAOvB,IAAM,oBAAoB,cAAc,IAAI;;;ADoB/C,SACE,KADF;AAZG,SAAS,MAAM,EAAE,OAAO,OAAO,YAAY,IAAI,GAAG,KAAK,GAAG;AAC/D,QAAM,QAAQ,WAAW,iBAAiB;AAE1C,QAAM,aAAa,QACf;AAAA,IACE,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM,UAAU;AAAA,IACzB,UAAU,MAAM,MAAM,WAAW,KAAK;AAAA,EACxC,IACA,CAAC;AAEL,SACE,qBAAC,WAAM,WAAW,CAAC,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAChE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV;AAAA,QACC,GAAG;AAAA,QACH,GAAG;AAAA;AAAA,IACN;AAAA,IACA,oBAAC,UAAK,WAAU,iBAAgB,eAAY,QAAO;AAAA,IAClD,SAAS,QAAQ,oBAAC,UAAK,WAAU,mBAAmB,iBAAM;AAAA,KAC7D;AAEJ;;;AEvCA,SAAS,OAAO,eAAe;AA0CjB,gBAAAA,YAAA;AAxBP,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,QAAM,WAAW,MAAM;AACvB,QAAM,MAAM;AAAA,IACV,OAAO,EAAE,MAAM,QAAQ,UAAU,OAAO,SAAS;AAAA,IACjD,CAAC,MAAM,UAAU,OAAO,QAAQ;AAAA,EAClC;AAEA,SACE,gBAAAA,KAAC,kBAAkB,UAAlB,EAA2B,OAAO,KACjC,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW,CAAC,kBAAkB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MAChE,GAAG;AAAA,MAEH,oBACG,QAAQ,IAAI,CAAC,QACX,gBAAAA,KAAC,SAAsB,OAAO,IAAI,OAAO,OAAO,IAAI,SAAxC,IAAI,KAA2C,CAC5D,IACD;AAAA;AAAA,EACN,GACF;AAEJ;","names":["jsx"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/components/Checkbox/Checkbox.jsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function Checkbox({ label, className = "", ...rest }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs("label", { className: ["ds-checkbox", className].filter(Boolean).join(" "), children: [
|
|
5
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", className: "ds-checkbox__input", ...rest }),
|
|
6
|
+
/* @__PURE__ */ jsx("span", { className: "ds-checkbox__box", "aria-hidden": "true" }),
|
|
7
|
+
label != null && /* @__PURE__ */ jsx("span", { className: "ds-checkbox__label", children: label })
|
|
8
|
+
] });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
Checkbox
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=chunk-BAISLPWP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Checkbox/Checkbox.jsx"],"sourcesContent":["import \"./Checkbox.css\";\n\n/**\n * Checkbox with a styled indicator over a native <input type=\"checkbox\">, so\n * keyboard/focus/toggle behavior stays native. Controlled or uncontrolled via\n * `...rest` (checked/defaultChecked, onChange).\n *\n * @param {object} props\n * @param {import(\"react\").ReactNode} [props.label] - Visible label text.\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Checkbox({ label, className = \"\", ...rest }) {\n return (\n <label className={[\"ds-checkbox\", className].filter(Boolean).join(\" \")}>\n <input type=\"checkbox\" className=\"ds-checkbox__input\" {...rest} />\n <span className=\"ds-checkbox__box\" aria-hidden=\"true\" />\n {label != null && <span className=\"ds-checkbox__label\">{label}</span>}\n </label>\n );\n}\n"],"mappings":";AAcI,SACE,KADF;AAFG,SAAS,SAAS,EAAE,OAAO,YAAY,IAAI,GAAG,KAAK,GAAG;AAC3D,SACE,qBAAC,WAAM,WAAW,CAAC,eAAe,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GACnE;AAAA,wBAAC,WAAM,MAAK,YAAW,WAAU,sBAAsB,GAAG,MAAM;AAAA,IAChE,oBAAC,UAAK,WAAU,oBAAmB,eAAY,QAAO;AAAA,IACrD,SAAS,QAAQ,oBAAC,UAAK,WAAU,sBAAsB,iBAAM;AAAA,KAChE;AAEJ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-CT2TMR44.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/components/Switch/Switch.jsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function Switch({ label, className = "", ...rest }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs("label", { className: ["ds-switch", className].filter(Boolean).join(" "), children: [
|
|
5
|
+
/* @__PURE__ */ jsx(
|
|
6
|
+
"input",
|
|
7
|
+
{
|
|
8
|
+
type: "checkbox",
|
|
9
|
+
role: "switch",
|
|
10
|
+
className: "ds-switch__input",
|
|
11
|
+
...rest
|
|
12
|
+
}
|
|
13
|
+
),
|
|
14
|
+
/* @__PURE__ */ jsx("span", { className: "ds-switch__track", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "ds-switch__thumb" }) }),
|
|
15
|
+
label != null && /* @__PURE__ */ jsx("span", { className: "ds-switch__label", children: label })
|
|
16
|
+
] });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
Switch
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=chunk-DD5VKG3L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Switch/Switch.jsx"],"sourcesContent":["import \"./Switch.css\";\n\n/**\n * Toggle switch built on a native <input type=\"checkbox\"> — same semantics as\n * a checkbox, styled as a sliding track. Controlled or uncontrolled via\n * `...rest` (checked/defaultChecked, onChange).\n *\n * @param {object} props\n * @param {import(\"react\").ReactNode} [props.label] - Visible label text.\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Switch({ label, className = \"\", ...rest }) {\n return (\n <label className={[\"ds-switch\", className].filter(Boolean).join(\" \")}>\n <input\n type=\"checkbox\"\n role=\"switch\"\n className=\"ds-switch__input\"\n {...rest}\n />\n <span className=\"ds-switch__track\" aria-hidden=\"true\">\n <span className=\"ds-switch__thumb\" />\n </span>\n {label != null && <span className=\"ds-switch__label\">{label}</span>}\n </label>\n );\n}\n"],"mappings":";AAcI,SACE,KADF;AAFG,SAAS,OAAO,EAAE,OAAO,YAAY,IAAI,GAAG,KAAK,GAAG;AACzD,SACE,qBAAC,WAAM,WAAW,CAAC,aAAa,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GACjE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,WAAU;AAAA,QACT,GAAG;AAAA;AAAA,IACN;AAAA,IACA,oBAAC,UAAK,WAAU,oBAAmB,eAAY,QAC7C,8BAAC,UAAK,WAAU,oBAAmB,GACrC;AAAA,IACC,SAAS,QAAQ,oBAAC,UAAK,WAAU,oBAAoB,iBAAM;AAAA,KAC9D;AAEJ;","names":[]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/components/Modal/Modal.jsx
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
6
|
+
function Modal({
|
|
7
|
+
open,
|
|
8
|
+
onClose,
|
|
9
|
+
title,
|
|
10
|
+
size = "md",
|
|
11
|
+
children,
|
|
12
|
+
className = ""
|
|
13
|
+
}) {
|
|
14
|
+
const panelRef = useRef(null);
|
|
15
|
+
const titleId = useRef(
|
|
16
|
+
`ds-modal-${Math.random().toString(36).slice(2, 9)}`
|
|
17
|
+
).current;
|
|
18
|
+
const handleKeyDown = useCallback(
|
|
19
|
+
(event) => {
|
|
20
|
+
if (event.key === "Escape") {
|
|
21
|
+
event.stopPropagation();
|
|
22
|
+
onClose?.();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (event.key !== "Tab") return;
|
|
26
|
+
const nodes = panelRef.current?.querySelectorAll(FOCUSABLE);
|
|
27
|
+
if (!nodes || nodes.length === 0) {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const first = nodes[0];
|
|
32
|
+
const last = nodes[nodes.length - 1];
|
|
33
|
+
const active = document.activeElement;
|
|
34
|
+
if (event.shiftKey && active === first) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
last.focus();
|
|
37
|
+
} else if (!event.shiftKey && active === last) {
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
first.focus();
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[onClose]
|
|
43
|
+
);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!open) return void 0;
|
|
46
|
+
const previouslyFocused = document.activeElement;
|
|
47
|
+
const { overflow } = document.body.style;
|
|
48
|
+
document.body.style.overflow = "hidden";
|
|
49
|
+
const nodes = panelRef.current?.querySelectorAll(FOCUSABLE);
|
|
50
|
+
if (nodes && nodes.length > 0) nodes[0].focus();
|
|
51
|
+
else panelRef.current?.focus();
|
|
52
|
+
return () => {
|
|
53
|
+
document.body.style.overflow = overflow;
|
|
54
|
+
if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus();
|
|
55
|
+
};
|
|
56
|
+
}, [open]);
|
|
57
|
+
if (!open) return null;
|
|
58
|
+
return createPortal(
|
|
59
|
+
/* @__PURE__ */ jsxs(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
className: ["ds-modal", className].filter(Boolean).join(" "),
|
|
63
|
+
onKeyDown: handleKeyDown,
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
"div",
|
|
67
|
+
{
|
|
68
|
+
className: "ds-modal__backdrop",
|
|
69
|
+
onClick: onClose,
|
|
70
|
+
"data-testid": "ds-modal-backdrop"
|
|
71
|
+
}
|
|
72
|
+
),
|
|
73
|
+
/* @__PURE__ */ jsxs(
|
|
74
|
+
"div",
|
|
75
|
+
{
|
|
76
|
+
ref: panelRef,
|
|
77
|
+
className: `ds-modal__panel ds-modal__panel--${size}`,
|
|
78
|
+
role: "dialog",
|
|
79
|
+
"aria-modal": "true",
|
|
80
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
81
|
+
tabIndex: -1,
|
|
82
|
+
children: [
|
|
83
|
+
title != null && /* @__PURE__ */ jsxs("div", { className: "ds-modal__header", children: [
|
|
84
|
+
/* @__PURE__ */ jsx("h2", { id: titleId, className: "ds-modal__title", children: title }),
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
"button",
|
|
87
|
+
{
|
|
88
|
+
type: "button",
|
|
89
|
+
className: "ds-modal__close",
|
|
90
|
+
"aria-label": "Close",
|
|
91
|
+
onClick: onClose,
|
|
92
|
+
children: "\xD7"
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
] }),
|
|
96
|
+
/* @__PURE__ */ jsx("div", { className: "ds-modal__body", children })
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
document.body
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
Modal
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=chunk-HLP2MPQ6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Modal/Modal.jsx"],"sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport \"./Modal.css\";\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Accessible dialog rendered in a portal on document.body. Handles focus trap,\n * Escape-to-close, backdrop click, body scroll lock, and returns focus to the\n * element that was focused before it opened. Nothing renders when `open` is\n * false.\n *\n * @param {object} props\n * @param {boolean} props.open\n * @param {() => void} props.onClose\n * @param {import(\"react\").ReactNode} [props.title]\n * @param {\"sm\"|\"md\"|\"lg\"} [props.size] - Panel max-width. Default \"md\".\n * @param {import(\"react\").ReactNode} [props.children]\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactPortal | null}\n */\nexport function Modal({\n open,\n onClose,\n title,\n size = \"md\",\n children,\n className = \"\",\n}) {\n const panelRef = useRef(null);\n const titleId = useRef(\n `ds-modal-${Math.random().toString(36).slice(2, 9)}`\n ).current;\n\n const handleKeyDown = useCallback(\n (event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onClose?.();\n return;\n }\n if (event.key !== \"Tab\") return;\n\n const nodes = panelRef.current?.querySelectorAll(FOCUSABLE);\n if (!nodes || nodes.length === 0) {\n event.preventDefault();\n return;\n }\n const first = nodes[0];\n const last = nodes[nodes.length - 1];\n const active = document.activeElement;\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n },\n [onClose]\n );\n\n useEffect(() => {\n if (!open) return undefined;\n\n const previouslyFocused = document.activeElement;\n const { overflow } = document.body.style;\n document.body.style.overflow = \"hidden\";\n\n // Focus the first focusable element (or the panel itself) on open.\n const nodes = panelRef.current?.querySelectorAll(FOCUSABLE);\n if (nodes && nodes.length > 0) nodes[0].focus();\n else panelRef.current?.focus();\n\n return () => {\n document.body.style.overflow = overflow;\n if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus();\n };\n }, [open]);\n\n if (!open) return null;\n\n return createPortal(\n <div\n className={[\"ds-modal\", className].filter(Boolean).join(\" \")}\n onKeyDown={handleKeyDown}\n >\n <div\n className=\"ds-modal__backdrop\"\n onClick={onClose}\n data-testid=\"ds-modal-backdrop\"\n />\n <div\n ref={panelRef}\n className={`ds-modal__panel ds-modal__panel--${size}`}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n tabIndex={-1}\n >\n {title != null && (\n <div className=\"ds-modal__header\">\n <h2 id={titleId} className=\"ds-modal__title\">\n {title}\n </h2>\n <button\n type=\"button\"\n className=\"ds-modal__close\"\n aria-label=\"Close\"\n onClick={onClose}\n >\n ×\n </button>\n </div>\n )}\n <div className=\"ds-modal__body\">{children}</div>\n </div>\n </div>,\n document.body\n );\n}\n"],"mappings":";AAAA,SAAS,aAAa,WAAW,cAAc;AAC/C,SAAS,oBAAoB;AAwFvB,cAcI,YAdJ;AArFN,IAAM,YACJ;AAiBK,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,YAAY;AACd,GAAG;AACD,QAAM,WAAW,OAAO,IAAI;AAC5B,QAAM,UAAU;AAAA,IACd,YAAY,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,EACpD,EAAE;AAEF,QAAM,gBAAgB;AAAA,IACpB,CAAC,UAAU;AACT,UAAI,MAAM,QAAQ,UAAU;AAC1B,cAAM,gBAAgB;AACtB,kBAAU;AACV;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,MAAO;AAEzB,YAAM,QAAQ,SAAS,SAAS,iBAAiB,SAAS;AAC1D,UAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,cAAM,eAAe;AACrB;AAAA,MACF;AACA,YAAM,QAAQ,MAAM,CAAC;AACrB,YAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,YAAM,SAAS,SAAS;AAExB,UAAI,MAAM,YAAY,WAAW,OAAO;AACtC,cAAM,eAAe;AACrB,aAAK,MAAM;AAAA,MACb,WAAW,CAAC,MAAM,YAAY,WAAW,MAAM;AAC7C,cAAM,eAAe;AACrB,cAAM,MAAM;AAAA,MACd;AAAA,IACF;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,oBAAoB,SAAS;AACnC,UAAM,EAAE,SAAS,IAAI,SAAS,KAAK;AACnC,aAAS,KAAK,MAAM,WAAW;AAG/B,UAAM,QAAQ,SAAS,SAAS,iBAAiB,SAAS;AAC1D,QAAI,SAAS,MAAM,SAAS,EAAG,OAAM,CAAC,EAAE,MAAM;AAAA,QACzC,UAAS,SAAS,MAAM;AAE7B,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAC/B,UAAI,6BAA6B,YAAa,mBAAkB,MAAM;AAAA,IACxE;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,MAAI,CAAC,KAAM,QAAO;AAElB,SAAO;AAAA,IACL;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,CAAC,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,QAC3D,WAAW;AAAA,QAEX;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,SAAS;AAAA,cACT,eAAY;AAAA;AAAA,UACd;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,WAAW,oCAAoC,IAAI;AAAA,cACnD,MAAK;AAAA,cACL,cAAW;AAAA,cACX,mBAAiB,QAAQ,UAAU;AAAA,cACnC,UAAU;AAAA,cAET;AAAA,yBAAS,QACR,qBAAC,SAAI,WAAU,oBACb;AAAA,sCAAC,QAAG,IAAI,SAAS,WAAU,mBACxB,iBACH;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,cAAW;AAAA,sBACX,SAAS;AAAA,sBACV;AAAA;AAAA,kBAED;AAAA,mBACF;AAAA,gBAEF,oBAAC,SAAI,WAAU,kBAAkB,UAAS;AAAA;AAAA;AAAA,UAC5C;AAAA;AAAA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/components/Select/Select.jsx
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
function Select({
|
|
4
|
+
size = "md",
|
|
5
|
+
invalid = false,
|
|
6
|
+
className = "",
|
|
7
|
+
children,
|
|
8
|
+
...rest
|
|
9
|
+
}) {
|
|
10
|
+
const classes = [
|
|
11
|
+
"ds-select",
|
|
12
|
+
`ds-select--${size}`,
|
|
13
|
+
invalid ? "ds-select--invalid" : "",
|
|
14
|
+
className
|
|
15
|
+
].filter(Boolean).join(" ");
|
|
16
|
+
return /* @__PURE__ */ jsx("select", { className: classes, "aria-invalid": invalid || void 0, ...rest, children });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
Select
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=chunk-HPOL5NKF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Select/Select.jsx"],"sourcesContent":["import \"./Select.css\";\n\n/**\n * Native <select> styled to match Input. Keeping it native avoids a\n * dependency and stays accessible for free. Pass <option> children.\n *\n * @param {object} props\n * @param {\"sm\"|\"md\"|\"lg\"} [props.size] - Control height/padding. Default \"md\".\n * @param {boolean} [props.invalid] - Applies error styling + aria-invalid.\n * @param {import(\"react\").ReactNode} [props.children]\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Select({\n size = \"md\",\n invalid = false,\n className = \"\",\n children,\n ...rest\n}) {\n const classes = [\n \"ds-select\",\n `ds-select--${size}`,\n invalid ? \"ds-select--invalid\" : \"\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <select className={classes} aria-invalid={invalid || undefined} {...rest}>\n {children}\n </select>\n );\n}\n"],"mappings":";AA8BI;AAjBG,SAAS,OAAO;AAAA,EACrB,OAAO;AAAA,EACP,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GAAG;AACD,QAAM,UAAU;AAAA,IACd;AAAA,IACA,cAAc,IAAI;AAAA,IAClB,UAAU,uBAAuB;AAAA,IACjC;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,oBAAC,YAAO,WAAW,SAAS,gBAAc,WAAW,QAAY,GAAG,MACjE,UACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/components/Textarea/Textarea.jsx
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
function Textarea({ invalid = false, rows = 4, className = "", ...rest }) {
|
|
4
|
+
const classes = [
|
|
5
|
+
"ds-textarea",
|
|
6
|
+
invalid ? "ds-textarea--invalid" : "",
|
|
7
|
+
className
|
|
8
|
+
].filter(Boolean).join(" ");
|
|
9
|
+
return /* @__PURE__ */ jsx(
|
|
10
|
+
"textarea",
|
|
11
|
+
{
|
|
12
|
+
className: classes,
|
|
13
|
+
rows,
|
|
14
|
+
"aria-invalid": invalid || void 0,
|
|
15
|
+
...rest
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
Textarea
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=chunk-I3NS5MAC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Textarea/Textarea.jsx"],"sourcesContent":["import \"./Textarea.css\";\n\n/**\n * Multi-line text input. Shares Input's field styling. All native <textarea>\n * props pass through via `...rest`.\n *\n * @param {object} props\n * @param {boolean} [props.invalid] - Applies error styling + aria-invalid.\n * @param {number} [props.rows] - Default 4.\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Textarea({ invalid = false, rows = 4, className = \"\", ...rest }) {\n const classes = [\n \"ds-textarea\",\n invalid ? \"ds-textarea--invalid\" : \"\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <textarea\n className={classes}\n rows={rows}\n aria-invalid={invalid || undefined}\n {...rest}\n />\n );\n}\n"],"mappings":";AAsBI;AAVG,SAAS,SAAS,EAAE,UAAU,OAAO,OAAO,GAAG,YAAY,IAAI,GAAG,KAAK,GAAG;AAC/E,QAAM,UAAU;AAAA,IACd;AAAA,IACA,UAAU,yBAAyB;AAAA,IACnC;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,MACX;AAAA,MACA,gBAAc,WAAW;AAAA,MACxB,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Label
|
|
3
|
+
} from "./chunk-VRGTUKSJ.js";
|
|
4
|
+
|
|
5
|
+
// src/components/FormField/FormField.jsx
|
|
6
|
+
import { cloneElement, isValidElement, useId } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function FormField({
|
|
9
|
+
label,
|
|
10
|
+
help,
|
|
11
|
+
error,
|
|
12
|
+
required = false,
|
|
13
|
+
htmlFor,
|
|
14
|
+
children,
|
|
15
|
+
className = "",
|
|
16
|
+
...rest
|
|
17
|
+
}) {
|
|
18
|
+
const autoId = useId();
|
|
19
|
+
const id = htmlFor ?? autoId;
|
|
20
|
+
const helpId = help ? `${id}-help` : void 0;
|
|
21
|
+
const errorId = error ? `${id}-error` : void 0;
|
|
22
|
+
const describedBy = [helpId, errorId].filter(Boolean).join(" ") || void 0;
|
|
23
|
+
const control = isValidElement(children) ? cloneElement(children, {
|
|
24
|
+
id,
|
|
25
|
+
// Preserve any describedby the control already declared.
|
|
26
|
+
"aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
|
|
27
|
+
invalid: error ? true : children.props.invalid,
|
|
28
|
+
required: required || children.props.required
|
|
29
|
+
}) : children;
|
|
30
|
+
return /* @__PURE__ */ jsxs(
|
|
31
|
+
"div",
|
|
32
|
+
{
|
|
33
|
+
className: ["ds-form-field", className].filter(Boolean).join(" "),
|
|
34
|
+
...rest,
|
|
35
|
+
children: [
|
|
36
|
+
label != null && /* @__PURE__ */ jsx(Label, { htmlFor: id, required, children: label }),
|
|
37
|
+
control,
|
|
38
|
+
help != null && /* @__PURE__ */ jsx("span", { id: helpId, className: "ds-form-field__help", children: help }),
|
|
39
|
+
error != null && /* @__PURE__ */ jsx("span", { id: errorId, className: "ds-form-field__error", role: "alert", children: error })
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
FormField
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=chunk-LPEUQ2DE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/FormField/FormField.jsx"],"sourcesContent":["import { cloneElement, isValidElement, useId } from \"react\";\nimport { Label } from \"../Label/Label.jsx\";\nimport \"./FormField.css\";\n\n/**\n * Wraps a single form control with a label plus optional help/error text, and\n * wires up accessibility: it generates an id, links the label via `htmlFor`,\n * connects help/error text through `aria-describedby`, and sets `aria-invalid`\n * + the control's `invalid` styling when `error` is present. The control is\n * passed as the only child (Input, Textarea, Select, etc.).\n *\n * @param {object} props\n * @param {import(\"react\").ReactNode} [props.label]\n * @param {import(\"react\").ReactNode} [props.help] - Hint text below the control.\n * @param {import(\"react\").ReactNode} [props.error] - Error text; also marks the control invalid.\n * @param {boolean} [props.required]\n * @param {string} [props.htmlFor] - Override the auto-generated control id.\n * @param {import(\"react\").ReactElement} props.children - The control element.\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function FormField({\n label,\n help,\n error,\n required = false,\n htmlFor,\n children,\n className = \"\",\n ...rest\n}) {\n const autoId = useId();\n const id = htmlFor ?? autoId;\n const helpId = help ? `${id}-help` : undefined;\n const errorId = error ? `${id}-error` : undefined;\n const describedBy = [helpId, errorId].filter(Boolean).join(\" \") || undefined;\n\n const control = isValidElement(children)\n ? cloneElement(children, {\n id,\n // Preserve any describedby the control already declared.\n \"aria-describedby\":\n [children.props[\"aria-describedby\"], describedBy]\n .filter(Boolean)\n .join(\" \") || undefined,\n invalid: error ? true : children.props.invalid,\n required: required || children.props.required,\n })\n : children;\n\n return (\n <div\n className={[\"ds-form-field\", className].filter(Boolean).join(\" \")}\n {...rest}\n >\n {label != null && (\n <Label htmlFor={id} required={required}>\n {label}\n </Label>\n )}\n {control}\n {help != null && (\n <span id={helpId} className=\"ds-form-field__help\">\n {help}\n </span>\n )}\n {error != null && (\n <span id={errorId} className=\"ds-form-field__error\" role=\"alert\">\n {error}\n </span>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAAA,SAAS,cAAc,gBAAgB,aAAa;AAmDhD,SAKI,KALJ;AA9BG,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,QAAM,SAAS,MAAM;AACrB,QAAM,KAAK,WAAW;AACtB,QAAM,SAAS,OAAO,GAAG,EAAE,UAAU;AACrC,QAAM,UAAU,QAAQ,GAAG,EAAE,WAAW;AACxC,QAAM,cAAc,CAAC,QAAQ,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAEnE,QAAM,UAAU,eAAe,QAAQ,IACnC,aAAa,UAAU;AAAA,IACrB;AAAA;AAAA,IAEA,oBACE,CAAC,SAAS,MAAM,kBAAkB,GAAG,WAAW,EAC7C,OAAO,OAAO,EACd,KAAK,GAAG,KAAK;AAAA,IAClB,SAAS,QAAQ,OAAO,SAAS,MAAM;AAAA,IACvC,UAAU,YAAY,SAAS,MAAM;AAAA,EACvC,CAAC,IACD;AAEJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,iBAAiB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MAC/D,GAAG;AAAA,MAEH;AAAA,iBAAS,QACR,oBAAC,SAAM,SAAS,IAAI,UACjB,iBACH;AAAA,QAED;AAAA,QACA,QAAQ,QACP,oBAAC,UAAK,IAAI,QAAQ,WAAU,uBACzB,gBACH;AAAA,QAED,SAAS,QACR,oBAAC,UAAK,IAAI,SAAS,WAAU,wBAAuB,MAAK,SACtD,iBACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/components/Menu/Menu.jsx
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function Menu({ trigger, items = [], align = "start", className = "" }) {
|
|
5
|
+
const [open, setOpen] = useState(false);
|
|
6
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
7
|
+
const rootRef = useRef(null);
|
|
8
|
+
const itemRefs = useRef([]);
|
|
9
|
+
const close = useCallback(() => {
|
|
10
|
+
setOpen(false);
|
|
11
|
+
setActiveIndex(-1);
|
|
12
|
+
}, []);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!open) return void 0;
|
|
15
|
+
function onPointerDown(event) {
|
|
16
|
+
if (!rootRef.current?.contains(event.target)) close();
|
|
17
|
+
}
|
|
18
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
19
|
+
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
20
|
+
}, [open, close]);
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
if (open && activeIndex >= 0) itemRefs.current[activeIndex]?.focus();
|
|
23
|
+
}, [open, activeIndex]);
|
|
24
|
+
const firstEnabled = () => items.findIndex((it) => !it.disabled);
|
|
25
|
+
const lastEnabled = () => {
|
|
26
|
+
for (let i = items.length - 1; i >= 0; i -= 1)
|
|
27
|
+
if (!items[i].disabled) return i;
|
|
28
|
+
return -1;
|
|
29
|
+
};
|
|
30
|
+
const nextEnabled = (from, dir) => {
|
|
31
|
+
let i = from;
|
|
32
|
+
for (let step = 0; step < items.length; step += 1) {
|
|
33
|
+
i = (i + dir + items.length) % items.length;
|
|
34
|
+
if (!items[i].disabled) return i;
|
|
35
|
+
}
|
|
36
|
+
return from;
|
|
37
|
+
};
|
|
38
|
+
function onTriggerKeyDown(event) {
|
|
39
|
+
if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
setOpen(true);
|
|
42
|
+
setActiveIndex(firstEnabled());
|
|
43
|
+
} else if (event.key === "ArrowUp") {
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
setOpen(true);
|
|
46
|
+
setActiveIndex(lastEnabled());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function onMenuKeyDown(event) {
|
|
50
|
+
switch (event.key) {
|
|
51
|
+
case "ArrowDown":
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
setActiveIndex((i) => nextEnabled(i < 0 ? -1 : i, 1));
|
|
54
|
+
break;
|
|
55
|
+
case "ArrowUp":
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
setActiveIndex((i) => nextEnabled(i < 0 ? 0 : i, -1));
|
|
58
|
+
break;
|
|
59
|
+
case "Home":
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
setActiveIndex(firstEnabled());
|
|
62
|
+
break;
|
|
63
|
+
case "End":
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
setActiveIndex(lastEnabled());
|
|
66
|
+
break;
|
|
67
|
+
case "Escape":
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
close();
|
|
70
|
+
rootRef.current?.querySelector(".ds-menu__trigger")?.focus();
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function select(item) {
|
|
77
|
+
if (item.disabled) return;
|
|
78
|
+
item.onSelect?.();
|
|
79
|
+
close();
|
|
80
|
+
rootRef.current?.querySelector(".ds-menu__trigger")?.focus();
|
|
81
|
+
}
|
|
82
|
+
return /* @__PURE__ */ jsxs(
|
|
83
|
+
"div",
|
|
84
|
+
{
|
|
85
|
+
ref: rootRef,
|
|
86
|
+
className: ["ds-menu", className].filter(Boolean).join(" "),
|
|
87
|
+
children: [
|
|
88
|
+
/* @__PURE__ */ jsx(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
className: "ds-menu__trigger",
|
|
93
|
+
"aria-haspopup": "menu",
|
|
94
|
+
"aria-expanded": open,
|
|
95
|
+
onClick: () => open ? close() : setOpen(true),
|
|
96
|
+
onKeyDown: onTriggerKeyDown,
|
|
97
|
+
children: trigger
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
open && /* @__PURE__ */ jsx(
|
|
101
|
+
"ul",
|
|
102
|
+
{
|
|
103
|
+
className: `ds-menu__list ds-menu__list--${align}`,
|
|
104
|
+
role: "menu",
|
|
105
|
+
onKeyDown: onMenuKeyDown,
|
|
106
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx("li", { role: "none", children: /* @__PURE__ */ jsx(
|
|
107
|
+
"button",
|
|
108
|
+
{
|
|
109
|
+
type: "button",
|
|
110
|
+
role: "menuitem",
|
|
111
|
+
ref: (el) => itemRefs.current[index] = el,
|
|
112
|
+
className: "ds-menu__item",
|
|
113
|
+
tabIndex: index === activeIndex ? 0 : -1,
|
|
114
|
+
disabled: item.disabled,
|
|
115
|
+
onClick: () => select(item),
|
|
116
|
+
children: item.label
|
|
117
|
+
}
|
|
118
|
+
) }, index))
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export {
|
|
127
|
+
Menu
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=chunk-LY5MPGW2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Menu/Menu.jsx"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport \"./Menu.css\";\n\n/**\n * @typedef {object} MenuItem\n * @property {import(\"react\").ReactNode} label\n * @property {() => void} [onSelect]\n * @property {boolean} [disabled]\n */\n\n/**\n * Dropdown menu: a trigger button that toggles a popover list. Closes on\n * outside click and Escape; Up/Down arrows move a roving focus between items,\n * Enter/Space activates. Uses role=\"menu\"/\"menuitem\".\n *\n * @param {object} props\n * @param {import(\"react\").ReactNode} props.trigger - Trigger button content.\n * @param {MenuItem[]} props.items\n * @param {\"start\"|\"end\"} [props.align] - Which edge the menu aligns to. Default \"start\".\n * @param {string} [props.className]\n * @returns {import(\"react\").ReactElement}\n */\nexport function Menu({ trigger, items = [], align = \"start\", className = \"\" }) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const rootRef = useRef(null);\n const itemRefs = useRef([]);\n\n const close = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n }, []);\n\n useEffect(() => {\n if (!open) return undefined;\n function onPointerDown(event) {\n if (!rootRef.current?.contains(event.target)) close();\n }\n document.addEventListener(\"pointerdown\", onPointerDown);\n return () => document.removeEventListener(\"pointerdown\", onPointerDown);\n }, [open, close]);\n\n // Keep DOM focus on the active item so screen readers track it.\n useLayoutEffect(() => {\n if (open && activeIndex >= 0) itemRefs.current[activeIndex]?.focus();\n }, [open, activeIndex]);\n\n const firstEnabled = () => items.findIndex((it) => !it.disabled);\n const lastEnabled = () => {\n for (let i = items.length - 1; i >= 0; i -= 1)\n if (!items[i].disabled) return i;\n return -1;\n };\n const nextEnabled = (from, dir) => {\n let i = from;\n for (let step = 0; step < items.length; step += 1) {\n i = (i + dir + items.length) % items.length;\n if (!items[i].disabled) return i;\n }\n return from;\n };\n\n function onTriggerKeyDown(event) {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex(firstEnabled());\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex(lastEnabled());\n }\n }\n\n function onMenuKeyDown(event) {\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n setActiveIndex((i) => nextEnabled(i < 0 ? -1 : i, 1));\n break;\n case \"ArrowUp\":\n event.preventDefault();\n setActiveIndex((i) => nextEnabled(i < 0 ? 0 : i, -1));\n break;\n case \"Home\":\n event.preventDefault();\n setActiveIndex(firstEnabled());\n break;\n case \"End\":\n event.preventDefault();\n setActiveIndex(lastEnabled());\n break;\n case \"Escape\":\n event.preventDefault();\n close();\n rootRef.current?.querySelector(\".ds-menu__trigger\")?.focus();\n break;\n default:\n break;\n }\n }\n\n function select(item) {\n if (item.disabled) return;\n item.onSelect?.();\n close();\n rootRef.current?.querySelector(\".ds-menu__trigger\")?.focus();\n }\n\n return (\n <div\n ref={rootRef}\n className={[\"ds-menu\", className].filter(Boolean).join(\" \")}\n >\n <button\n type=\"button\"\n className=\"ds-menu__trigger\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n onClick={() => (open ? close() : setOpen(true))}\n onKeyDown={onTriggerKeyDown}\n >\n {trigger}\n </button>\n {open && (\n <ul\n className={`ds-menu__list ds-menu__list--${align}`}\n role=\"menu\"\n onKeyDown={onMenuKeyDown}\n >\n {items.map((item, index) => (\n <li key={index} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n ref={(el) => (itemRefs.current[index] = el)}\n className=\"ds-menu__item\"\n tabIndex={index === activeIndex ? 0 : -1}\n disabled={item.disabled}\n onClick={() => select(item)}\n >\n {item.label}\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|