@diwauhris/ui 1.0.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/README.md +75 -0
- package/lib/assets/ui.css +154 -0
- package/lib/index.cjs +183 -0
- package/lib/index.mjs +65 -0
- package/lib/index10.cjs +33 -0
- package/lib/index10.cjs.map +1 -0
- package/lib/index10.mjs +33 -0
- package/lib/index10.mjs.map +1 -0
- package/lib/index11.cjs +16 -0
- package/lib/index11.cjs.map +1 -0
- package/lib/index11.mjs +16 -0
- package/lib/index11.mjs.map +1 -0
- package/lib/index12.cjs +16 -0
- package/lib/index12.cjs.map +1 -0
- package/lib/index12.mjs +15 -0
- package/lib/index12.mjs.map +1 -0
- package/lib/index13.cjs +57 -0
- package/lib/index13.cjs.map +1 -0
- package/lib/index13.mjs +57 -0
- package/lib/index13.mjs.map +1 -0
- package/lib/index14.cjs +10 -0
- package/lib/index14.cjs.map +1 -0
- package/lib/index14.mjs +10 -0
- package/lib/index14.mjs.map +1 -0
- package/lib/index15.cjs +30 -0
- package/lib/index15.cjs.map +1 -0
- package/lib/index15.mjs +30 -0
- package/lib/index15.mjs.map +1 -0
- package/lib/index16.cjs +28 -0
- package/lib/index16.cjs.map +1 -0
- package/lib/index16.mjs +26 -0
- package/lib/index16.mjs.map +1 -0
- package/lib/index17.cjs +43 -0
- package/lib/index17.cjs.map +1 -0
- package/lib/index17.mjs +43 -0
- package/lib/index17.mjs.map +1 -0
- package/lib/index18.cjs +59 -0
- package/lib/index18.cjs.map +1 -0
- package/lib/index18.mjs +59 -0
- package/lib/index18.mjs.map +1 -0
- package/lib/index19.cjs +67 -0
- package/lib/index19.cjs.map +1 -0
- package/lib/index19.mjs +67 -0
- package/lib/index19.mjs.map +1 -0
- package/lib/index2.cjs +33 -0
- package/lib/index2.mjs +13 -0
- package/lib/index20.cjs +79 -0
- package/lib/index20.cjs.map +1 -0
- package/lib/index20.mjs +79 -0
- package/lib/index20.mjs.map +1 -0
- package/lib/index21.cjs +105 -0
- package/lib/index21.cjs.map +1 -0
- package/lib/index21.mjs +104 -0
- package/lib/index21.mjs.map +1 -0
- package/lib/index22.cjs +50 -0
- package/lib/index22.cjs.map +1 -0
- package/lib/index22.mjs +50 -0
- package/lib/index22.mjs.map +1 -0
- package/lib/index23.cjs +143 -0
- package/lib/index23.cjs.map +1 -0
- package/lib/index23.mjs +143 -0
- package/lib/index23.mjs.map +1 -0
- package/lib/index25.cjs +177 -0
- package/lib/index25.cjs.map +1 -0
- package/lib/index25.mjs +175 -0
- package/lib/index25.mjs.map +1 -0
- package/lib/index26.cjs +108 -0
- package/lib/index26.cjs.map +1 -0
- package/lib/index26.mjs +108 -0
- package/lib/index26.mjs.map +1 -0
- package/lib/index27.cjs +120 -0
- package/lib/index27.cjs.map +1 -0
- package/lib/index27.mjs +120 -0
- package/lib/index27.mjs.map +1 -0
- package/lib/index28.cjs +54 -0
- package/lib/index28.cjs.map +1 -0
- package/lib/index28.mjs +54 -0
- package/lib/index28.mjs.map +1 -0
- package/lib/index29.cjs +100 -0
- package/lib/index29.cjs.map +1 -0
- package/lib/index29.mjs +100 -0
- package/lib/index29.mjs.map +1 -0
- package/lib/index3.cjs +38 -0
- package/lib/index3.cjs.map +1 -0
- package/lib/index3.mjs +38 -0
- package/lib/index3.mjs.map +1 -0
- package/lib/index30.cjs +27 -0
- package/lib/index30.cjs.map +1 -0
- package/lib/index30.mjs +27 -0
- package/lib/index30.mjs.map +1 -0
- package/lib/index31.cjs +72 -0
- package/lib/index31.cjs.map +1 -0
- package/lib/index31.mjs +72 -0
- package/lib/index31.mjs.map +1 -0
- package/lib/index32.cjs +19 -0
- package/lib/index32.cjs.map +1 -0
- package/lib/index32.mjs +19 -0
- package/lib/index32.mjs.map +1 -0
- package/lib/index33.cjs +25 -0
- package/lib/index33.cjs.map +1 -0
- package/lib/index33.mjs +25 -0
- package/lib/index33.mjs.map +1 -0
- package/lib/index34.cjs +55 -0
- package/lib/index34.cjs.map +1 -0
- package/lib/index34.mjs +51 -0
- package/lib/index34.mjs.map +1 -0
- package/lib/index35.cjs +25 -0
- package/lib/index35.cjs.map +1 -0
- package/lib/index35.mjs +25 -0
- package/lib/index35.mjs.map +1 -0
- package/lib/index36.cjs +54 -0
- package/lib/index36.cjs.map +1 -0
- package/lib/index36.mjs +49 -0
- package/lib/index36.mjs.map +1 -0
- package/lib/index37.cjs +130 -0
- package/lib/index37.cjs.map +1 -0
- package/lib/index37.mjs +128 -0
- package/lib/index37.mjs.map +1 -0
- package/lib/index38.cjs +106 -0
- package/lib/index38.cjs.map +1 -0
- package/lib/index38.mjs +102 -0
- package/lib/index38.mjs.map +1 -0
- package/lib/index39.cjs +137 -0
- package/lib/index39.cjs.map +1 -0
- package/lib/index39.mjs +137 -0
- package/lib/index39.mjs.map +1 -0
- package/lib/index4.cjs +24 -0
- package/lib/index4.cjs.map +1 -0
- package/lib/index4.mjs +24 -0
- package/lib/index4.mjs.map +1 -0
- package/lib/index40.cjs +130 -0
- package/lib/index40.cjs.map +1 -0
- package/lib/index40.mjs +126 -0
- package/lib/index40.mjs.map +1 -0
- package/lib/index41.cjs +60 -0
- package/lib/index41.cjs.map +1 -0
- package/lib/index41.mjs +58 -0
- package/lib/index41.mjs.map +1 -0
- package/lib/index42.cjs +105 -0
- package/lib/index42.cjs.map +1 -0
- package/lib/index42.mjs +105 -0
- package/lib/index42.mjs.map +1 -0
- package/lib/index43.cjs +47 -0
- package/lib/index43.cjs.map +1 -0
- package/lib/index43.mjs +42 -0
- package/lib/index43.mjs.map +1 -0
- package/lib/index44.cjs +40 -0
- package/lib/index44.cjs.map +1 -0
- package/lib/index44.mjs +35 -0
- package/lib/index44.mjs.map +1 -0
- package/lib/index45.cjs +91 -0
- package/lib/index45.cjs.map +1 -0
- package/lib/index45.mjs +91 -0
- package/lib/index45.mjs.map +1 -0
- package/lib/index46.cjs +180 -0
- package/lib/index46.cjs.map +1 -0
- package/lib/index46.mjs +180 -0
- package/lib/index46.mjs.map +1 -0
- package/lib/index47.cjs +61 -0
- package/lib/index47.cjs.map +1 -0
- package/lib/index47.mjs +61 -0
- package/lib/index47.mjs.map +1 -0
- package/lib/index48.cjs +165 -0
- package/lib/index48.cjs.map +1 -0
- package/lib/index48.mjs +161 -0
- package/lib/index48.mjs.map +1 -0
- package/lib/index49.cjs +83 -0
- package/lib/index49.cjs.map +1 -0
- package/lib/index49.mjs +83 -0
- package/lib/index49.mjs.map +1 -0
- package/lib/index5.cjs +39 -0
- package/lib/index5.cjs.map +1 -0
- package/lib/index5.mjs +39 -0
- package/lib/index5.mjs.map +1 -0
- package/lib/index50.cjs +100 -0
- package/lib/index50.cjs.map +1 -0
- package/lib/index50.mjs +100 -0
- package/lib/index50.mjs.map +1 -0
- package/lib/index51.cjs +116 -0
- package/lib/index51.cjs.map +1 -0
- package/lib/index51.mjs +113 -0
- package/lib/index51.mjs.map +1 -0
- package/lib/index52.cjs +93 -0
- package/lib/index52.cjs.map +1 -0
- package/lib/index52.mjs +93 -0
- package/lib/index52.mjs.map +1 -0
- package/lib/index53.cjs +120 -0
- package/lib/index53.cjs.map +1 -0
- package/lib/index53.mjs +120 -0
- package/lib/index53.mjs.map +1 -0
- package/lib/index54.cjs +53 -0
- package/lib/index54.cjs.map +1 -0
- package/lib/index54.mjs +50 -0
- package/lib/index54.mjs.map +1 -0
- package/lib/index55.cjs +130 -0
- package/lib/index55.cjs.map +1 -0
- package/lib/index55.mjs +127 -0
- package/lib/index55.mjs.map +1 -0
- package/lib/index56.cjs +36 -0
- package/lib/index56.cjs.map +1 -0
- package/lib/index56.mjs +35 -0
- package/lib/index56.mjs.map +1 -0
- package/lib/index57.cjs +47 -0
- package/lib/index57.cjs.map +1 -0
- package/lib/index57.mjs +47 -0
- package/lib/index57.mjs.map +1 -0
- package/lib/index58.cjs +97 -0
- package/lib/index58.cjs.map +1 -0
- package/lib/index58.mjs +97 -0
- package/lib/index58.mjs.map +1 -0
- package/lib/index59.cjs +120 -0
- package/lib/index59.cjs.map +1 -0
- package/lib/index59.mjs +116 -0
- package/lib/index59.mjs.map +1 -0
- package/lib/index6.cjs +29 -0
- package/lib/index6.cjs.map +1 -0
- package/lib/index6.mjs +29 -0
- package/lib/index6.mjs.map +1 -0
- package/lib/index60.cjs +34 -0
- package/lib/index60.cjs.map +1 -0
- package/lib/index60.mjs +34 -0
- package/lib/index60.mjs.map +1 -0
- package/lib/index61.cjs +62 -0
- package/lib/index61.cjs.map +1 -0
- package/lib/index61.mjs +62 -0
- package/lib/index61.mjs.map +1 -0
- package/lib/index62.cjs +213 -0
- package/lib/index62.cjs.map +1 -0
- package/lib/index62.mjs +213 -0
- package/lib/index62.mjs.map +1 -0
- package/lib/index63.cjs +42 -0
- package/lib/index63.cjs.map +1 -0
- package/lib/index63.mjs +42 -0
- package/lib/index63.mjs.map +1 -0
- package/lib/index64.cjs +86 -0
- package/lib/index64.cjs.map +1 -0
- package/lib/index64.mjs +86 -0
- package/lib/index64.mjs.map +1 -0
- package/lib/index65.cjs +156 -0
- package/lib/index65.cjs.map +1 -0
- package/lib/index65.mjs +156 -0
- package/lib/index65.mjs.map +1 -0
- package/lib/index66.cjs +107 -0
- package/lib/index66.cjs.map +1 -0
- package/lib/index66.mjs +106 -0
- package/lib/index66.mjs.map +1 -0
- package/lib/index67.cjs +52 -0
- package/lib/index67.cjs.map +1 -0
- package/lib/index67.mjs +52 -0
- package/lib/index67.mjs.map +1 -0
- package/lib/index68.cjs +125 -0
- package/lib/index68.cjs.map +1 -0
- package/lib/index68.mjs +115 -0
- package/lib/index68.mjs.map +1 -0
- package/lib/index69.cjs +34 -0
- package/lib/index69.cjs.map +1 -0
- package/lib/index69.mjs +29 -0
- package/lib/index69.mjs.map +1 -0
- package/lib/index7.cjs +17 -0
- package/lib/index7.cjs.map +1 -0
- package/lib/index7.mjs +17 -0
- package/lib/index7.mjs.map +1 -0
- package/lib/index8.cjs +86 -0
- package/lib/index8.cjs.map +1 -0
- package/lib/index8.mjs +86 -0
- package/lib/index8.mjs.map +1 -0
- package/lib/index9.cjs +29 -0
- package/lib/index9.cjs.map +1 -0
- package/lib/index9.mjs +29 -0
- package/lib/index9.mjs.map +1 -0
- package/lib/types/index.d.ts +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index19.cjs","names":[],"sources":["../src/ui-library/gallery/select/Select.tsx"],"sourcesContent":["/**\n * Select — Design System Component\n *\n * A single-value selection control. Wraps the native <select> element\n * with design-system styling. Accessible by default — keyboard and\n * screen-reader behavior is inherited from the browser.\n *\n * A custom dropdown (non-native) is NOT used here because:\n * - Native <select> provides full keyboard/AT support at zero cost.\n * - Custom dropdowns require significant ARIA authoring to match\n * native behavior, and frequently regress in edge cases.\n * - For searchable/multi-select use cases, use Combobox (Phase 3).\n *\n * The label is NOT included. The consuming feature pairs a <label>.\n */\n\nimport { ChevronDown } from 'lucide-react';\nimport {\n type FieldStatus,\n FIELD_STATUS_RING_DIRECT,\n FIELD_HELPER_COLOR,\n} from '../shared/fieldStatus';\n\nexport type SelectStatus = FieldStatus;\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectGroup {\n group: string;\n options: SelectOption[];\n}\n\nexport type SelectItem = SelectOption | SelectGroup;\n\nexport interface SelectProps extends Omit<React.ComponentProps<'select'>, 'children'> {\n /** Flat options or grouped options */\n items: SelectItem[];\n /** Placeholder option shown when no value is selected */\n placeholder?: string;\n status?: SelectStatus;\n helper?: string;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction isGroup(item: SelectItem): item is SelectGroup {\n return 'group' in item;\n}\n\n// ── Style maps ────────────────────────────────────────────────────────────────\n// STATUS_CLASS and HELPER_CLASS use FIELD_STATUS_RING_DIRECT / FIELD_HELPER_COLOR\n// from shared/fieldStatus — same semantic mapping as Input and Textarea.\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function Select({\n items,\n placeholder,\n status = 'default',\n helper,\n disabled,\n className = '',\n ...props\n}: SelectProps) {\n return (\n <div className=\"w-full\">\n <div className=\"relative\">\n <select\n disabled={disabled}\n className={[\n 'w-full appearance-none rounded-md border bg-white py-2.5 pl-4 pr-10 text-sm font-medium text-slate-900 outline-none transition',\n FIELD_STATUS_RING_DIRECT[status],\n disabled ? 'cursor-not-allowed bg-slate-50 opacity-60' : 'cursor-pointer',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n >\n {placeholder && (\n <option value=\"\" disabled>\n {placeholder}\n </option>\n )}\n\n {items.map((item) =>\n isGroup(item) ? (\n <optgroup key={item.group} label={item.group}>\n {item.options.map((opt) => (\n <option key={opt.value} value={opt.value} disabled={opt.disabled}>\n {opt.label}\n </option>\n ))}\n </optgroup>\n ) : (\n <option key={item.value} value={item.value} disabled={item.disabled}>\n {item.label}\n </option>\n ),\n )}\n </select>\n\n {/* Custom caret — purely decorative */}\n <span\n className=\"pointer-events-none absolute right-3.5 top-1/2 -translate-y-1/2 text-slate-400\"\n aria-hidden=\"true\"\n >\n <ChevronDown size={14} />\n </span>\n </div>\n\n {helper && (\n <p className={`mt-1.5 text-xs font-medium ${FIELD_HELPER_COLOR[status]}`}>{helper}</p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiDA,SAAS,QAAQ,MAAuC;CACtD,OAAO,WAAW;AACpB;AAQA,SAAgB,OAAO,EACrB,OACA,aACA,SAAS,WACT,QACA,UACA,YAAY,IACZ,GAAG,SACW;CACd,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;IACY;IACV,WAAW;KACT;KACA,oBAAA,yBAAyB;KACzB,WAAW,8CAA8C;KACzD;IACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;IACX,GAAI;IAVN,UAAA,CAYG,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KAAQ,OAAM;KAAG,UAAA;KACd,UAAA;IACK,CAAA,GAGT,MAAM,KAAK,SACV,QAAQ,IAAI,IACV,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;KAA2B,OAAO,KAAK;KACpC,UAAA,KAAK,QAAQ,KAAK,QACjB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MAAwB,OAAO,IAAI;MAAO,UAAU,IAAI;MACrD,UAAA,IAAI;KACC,GAFK,IAAI,KAET,CACT;IACO,GANK,KAAK,KAMV,IAEV,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KAAyB,OAAO,KAAK;KAAO,UAAU,KAAK;KACxD,UAAA,KAAK;IACA,GAFK,KAAK,KAEV,CAEZ,CACM;GAGR,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IACE,WAAU;IACV,eAAY;IAEZ,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,aAAD,EAAa,MAAM,GAAK,CAAA;GACpB,CAAA,CACH;EAEJ,CAAA,GAAA,UACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;GAAG,WAAW,8BAA8B,oBAAA,mBAAmB;GAAY,UAAA;EAAU,CAAA,CAEpF;;AAET"}
|
package/lib/index19.mjs
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { FIELD_HELPER_COLOR, FIELD_STATUS_RING_DIRECT } from "./index16.mjs";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown } from "lucide-react";
|
|
4
|
+
//#region src/ui-library/gallery/select/Select.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Select — Design System Component
|
|
7
|
+
*
|
|
8
|
+
* A single-value selection control. Wraps the native <select> element
|
|
9
|
+
* with design-system styling. Accessible by default — keyboard and
|
|
10
|
+
* screen-reader behavior is inherited from the browser.
|
|
11
|
+
*
|
|
12
|
+
* A custom dropdown (non-native) is NOT used here because:
|
|
13
|
+
* - Native <select> provides full keyboard/AT support at zero cost.
|
|
14
|
+
* - Custom dropdowns require significant ARIA authoring to match
|
|
15
|
+
* native behavior, and frequently regress in edge cases.
|
|
16
|
+
* - For searchable/multi-select use cases, use Combobox (Phase 3).
|
|
17
|
+
*
|
|
18
|
+
* The label is NOT included. The consuming feature pairs a <label>.
|
|
19
|
+
*/
|
|
20
|
+
function isGroup(item) {
|
|
21
|
+
return "group" in item;
|
|
22
|
+
}
|
|
23
|
+
function Select({ items, placeholder, status = "default", helper, disabled, className = "", ...props }) {
|
|
24
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
25
|
+
className: "w-full",
|
|
26
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
27
|
+
className: "relative",
|
|
28
|
+
children: [/* @__PURE__ */ jsxs("select", {
|
|
29
|
+
disabled,
|
|
30
|
+
className: [
|
|
31
|
+
"w-full appearance-none rounded-md border bg-white py-2.5 pl-4 pr-10 text-sm font-medium text-slate-900 outline-none transition",
|
|
32
|
+
FIELD_STATUS_RING_DIRECT[status],
|
|
33
|
+
disabled ? "cursor-not-allowed bg-slate-50 opacity-60" : "cursor-pointer",
|
|
34
|
+
className
|
|
35
|
+
].filter(Boolean).join(" "),
|
|
36
|
+
...props,
|
|
37
|
+
children: [placeholder && /* @__PURE__ */ jsx("option", {
|
|
38
|
+
value: "",
|
|
39
|
+
disabled: true,
|
|
40
|
+
children: placeholder
|
|
41
|
+
}), items.map((item) => isGroup(item) ? /* @__PURE__ */ jsx("optgroup", {
|
|
42
|
+
label: item.group,
|
|
43
|
+
children: item.options.map((opt) => /* @__PURE__ */ jsx("option", {
|
|
44
|
+
value: opt.value,
|
|
45
|
+
disabled: opt.disabled,
|
|
46
|
+
children: opt.label
|
|
47
|
+
}, opt.value))
|
|
48
|
+
}, item.group) : /* @__PURE__ */ jsx("option", {
|
|
49
|
+
value: item.value,
|
|
50
|
+
disabled: item.disabled,
|
|
51
|
+
children: item.label
|
|
52
|
+
}, item.value))]
|
|
53
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
54
|
+
className: "pointer-events-none absolute right-3.5 top-1/2 -translate-y-1/2 text-slate-400",
|
|
55
|
+
"aria-hidden": "true",
|
|
56
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { size: 14 })
|
|
57
|
+
})]
|
|
58
|
+
}), helper && /* @__PURE__ */ jsx("p", {
|
|
59
|
+
className: `mt-1.5 text-xs font-medium ${FIELD_HELPER_COLOR[status]}`,
|
|
60
|
+
children: helper
|
|
61
|
+
})]
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { Select };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=index19.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index19.mjs","names":[],"sources":["../src/ui-library/gallery/select/Select.tsx"],"sourcesContent":["/**\n * Select — Design System Component\n *\n * A single-value selection control. Wraps the native <select> element\n * with design-system styling. Accessible by default — keyboard and\n * screen-reader behavior is inherited from the browser.\n *\n * A custom dropdown (non-native) is NOT used here because:\n * - Native <select> provides full keyboard/AT support at zero cost.\n * - Custom dropdowns require significant ARIA authoring to match\n * native behavior, and frequently regress in edge cases.\n * - For searchable/multi-select use cases, use Combobox (Phase 3).\n *\n * The label is NOT included. The consuming feature pairs a <label>.\n */\n\nimport { ChevronDown } from 'lucide-react';\nimport {\n type FieldStatus,\n FIELD_STATUS_RING_DIRECT,\n FIELD_HELPER_COLOR,\n} from '../shared/fieldStatus';\n\nexport type SelectStatus = FieldStatus;\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectGroup {\n group: string;\n options: SelectOption[];\n}\n\nexport type SelectItem = SelectOption | SelectGroup;\n\nexport interface SelectProps extends Omit<React.ComponentProps<'select'>, 'children'> {\n /** Flat options or grouped options */\n items: SelectItem[];\n /** Placeholder option shown when no value is selected */\n placeholder?: string;\n status?: SelectStatus;\n helper?: string;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction isGroup(item: SelectItem): item is SelectGroup {\n return 'group' in item;\n}\n\n// ── Style maps ────────────────────────────────────────────────────────────────\n// STATUS_CLASS and HELPER_CLASS use FIELD_STATUS_RING_DIRECT / FIELD_HELPER_COLOR\n// from shared/fieldStatus — same semantic mapping as Input and Textarea.\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function Select({\n items,\n placeholder,\n status = 'default',\n helper,\n disabled,\n className = '',\n ...props\n}: SelectProps) {\n return (\n <div className=\"w-full\">\n <div className=\"relative\">\n <select\n disabled={disabled}\n className={[\n 'w-full appearance-none rounded-md border bg-white py-2.5 pl-4 pr-10 text-sm font-medium text-slate-900 outline-none transition',\n FIELD_STATUS_RING_DIRECT[status],\n disabled ? 'cursor-not-allowed bg-slate-50 opacity-60' : 'cursor-pointer',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n >\n {placeholder && (\n <option value=\"\" disabled>\n {placeholder}\n </option>\n )}\n\n {items.map((item) =>\n isGroup(item) ? (\n <optgroup key={item.group} label={item.group}>\n {item.options.map((opt) => (\n <option key={opt.value} value={opt.value} disabled={opt.disabled}>\n {opt.label}\n </option>\n ))}\n </optgroup>\n ) : (\n <option key={item.value} value={item.value} disabled={item.disabled}>\n {item.label}\n </option>\n ),\n )}\n </select>\n\n {/* Custom caret — purely decorative */}\n <span\n className=\"pointer-events-none absolute right-3.5 top-1/2 -translate-y-1/2 text-slate-400\"\n aria-hidden=\"true\"\n >\n <ChevronDown size={14} />\n </span>\n </div>\n\n {helper && (\n <p className={`mt-1.5 text-xs font-medium ${FIELD_HELPER_COLOR[status]}`}>{helper}</p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiDA,SAAS,QAAQ,MAAuC;CACtD,OAAO,WAAW;AACpB;AAQA,SAAgB,OAAO,EACrB,OACA,aACA,SAAS,WACT,QACA,UACA,YAAY,IACZ,GAAG,SACW;CACd,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,qBAAC,UAAD;IACY;IACV,WAAW;KACT;KACA,yBAAyB;KACzB,WAAW,8CAA8C;KACzD;IACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;IACX,GAAI;IAVN,UAAA,CAYG,eACC,oBAAC,UAAD;KAAQ,OAAM;KAAG,UAAA;KACd,UAAA;IACK,CAAA,GAGT,MAAM,KAAK,SACV,QAAQ,IAAI,IACV,oBAAC,YAAD;KAA2B,OAAO,KAAK;KACpC,UAAA,KAAK,QAAQ,KAAK,QACjB,oBAAC,UAAD;MAAwB,OAAO,IAAI;MAAO,UAAU,IAAI;MACrD,UAAA,IAAI;KACC,GAFK,IAAI,KAET,CACT;IACO,GANK,KAAK,KAMV,IAEV,oBAAC,UAAD;KAAyB,OAAO,KAAK;KAAO,UAAU,KAAK;KACxD,UAAA,KAAK;IACA,GAFK,KAAK,KAEV,CAEZ,CACM;GAGR,CAAA,GAAA,oBAAC,QAAD;IACE,WAAU;IACV,eAAY;IAEZ,UAAA,oBAAC,aAAD,EAAa,MAAM,GAAK,CAAA;GACpB,CAAA,CACH;EAEJ,CAAA,GAAA,UACC,oBAAC,KAAD;GAAG,WAAW,8BAA8B,mBAAmB;GAAY,UAAA;EAAU,CAAA,CAEpF;;AAET"}
|
package/lib/index2.cjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __exportAll = (all, no_symbols) => {
|
|
9
|
+
let target = {};
|
|
10
|
+
for (var name in all) __defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true
|
|
13
|
+
});
|
|
14
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
15
|
+
return target;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
19
|
+
key = keys[i];
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
21
|
+
get: ((k) => from[k]).bind(null, key),
|
|
22
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
28
|
+
value: mod,
|
|
29
|
+
enumerable: true
|
|
30
|
+
}) : target, mod));
|
|
31
|
+
//#endregion
|
|
32
|
+
exports.__exportAll = __exportAll;
|
|
33
|
+
exports.__toESM = __toESM;
|
package/lib/index2.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll };
|
package/lib/index20.cjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let lucide_react = require("lucide-react");
|
|
4
|
+
//#region src/ui-library/gallery/checkbox/Checkbox.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Checkbox — Design System Component
|
|
7
|
+
*
|
|
8
|
+
* A binary or indeterminate selection control with an optional label.
|
|
9
|
+
* The label is composed inline here because the checkbox + label unit
|
|
10
|
+
* is so consistently used together that separating them adds no value.
|
|
11
|
+
*
|
|
12
|
+
* States: unchecked | checked | indeterminate | disabled | error
|
|
13
|
+
*/
|
|
14
|
+
function Checkbox({ label, description, indeterminate = false, error = false, helper, disabled, checked, id: externalId, className = "", ...props }) {
|
|
15
|
+
const generatedId = (0, react.useId)();
|
|
16
|
+
const id = externalId ?? generatedId;
|
|
17
|
+
const attachIndeterminate = (el) => {
|
|
18
|
+
if (el) el.indeterminate = indeterminate;
|
|
19
|
+
};
|
|
20
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21
|
+
className: `flex flex-col gap-1 ${className}`,
|
|
22
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
23
|
+
htmlFor: id,
|
|
24
|
+
className: `group inline-flex cursor-pointer items-start gap-2.5 ${disabled ? "cursor-not-allowed opacity-60" : ""}`,
|
|
25
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
26
|
+
className: "relative mt-0.5 shrink-0",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
29
|
+
ref: attachIndeterminate,
|
|
30
|
+
id,
|
|
31
|
+
type: "checkbox",
|
|
32
|
+
checked,
|
|
33
|
+
disabled,
|
|
34
|
+
"aria-invalid": error || void 0,
|
|
35
|
+
className: "peer sr-only",
|
|
36
|
+
...props
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
39
|
+
"aria-hidden": "true",
|
|
40
|
+
className: [
|
|
41
|
+
"h-[18px] w-[18px] rounded border-2 transition-all duration-100",
|
|
42
|
+
"peer-focus-visible:ring-2 peer-focus-visible:ring-offset-1",
|
|
43
|
+
error ? "border-rose-400 peer-checked:bg-rose-500 peer-checked:border-rose-500 peer-focus-visible:ring-rose-200" : "border-slate-300 peer-checked:bg-brand-blue peer-checked:border-brand-blue peer-focus-visible:ring-brand-blue/30",
|
|
44
|
+
indeterminate && !error && "bg-brand-blue border-brand-blue",
|
|
45
|
+
indeterminate && error && "bg-rose-500 border-rose-500"
|
|
46
|
+
].filter(Boolean).join(" ")
|
|
47
|
+
}),
|
|
48
|
+
indeterminate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Minus, {
|
|
49
|
+
size: 11,
|
|
50
|
+
strokeWidth: 3,
|
|
51
|
+
"aria-hidden": "true",
|
|
52
|
+
className: "pointer-events-none absolute inset-0 m-auto text-white"
|
|
53
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {
|
|
54
|
+
size: 11,
|
|
55
|
+
strokeWidth: 3,
|
|
56
|
+
"aria-hidden": "true",
|
|
57
|
+
className: "pointer-events-none absolute inset-0 m-auto text-white opacity-0 peer-checked:opacity-100 transition-opacity duration-100"
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
}), (label || description) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
61
|
+
className: "min-w-0",
|
|
62
|
+
children: [label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
63
|
+
className: "block text-sm font-semibold text-slate-800",
|
|
64
|
+
children: label
|
|
65
|
+
}), description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
66
|
+
className: "block text-xs font-medium text-slate-400 leading-relaxed",
|
|
67
|
+
children: description
|
|
68
|
+
})]
|
|
69
|
+
})]
|
|
70
|
+
}), helper && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
71
|
+
className: `pl-7 text-xs font-medium ${error ? "text-rose-500" : "text-slate-400"}`,
|
|
72
|
+
children: helper
|
|
73
|
+
})]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
exports.Checkbox = Checkbox;
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=index20.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index20.cjs","names":[],"sources":["../src/ui-library/gallery/checkbox/Checkbox.tsx"],"sourcesContent":["/**\n * Checkbox — Design System Component\n *\n * A binary or indeterminate selection control with an optional label.\n * The label is composed inline here because the checkbox + label unit\n * is so consistently used together that separating them adds no value.\n *\n * States: unchecked | checked | indeterminate | disabled | error\n */\n\nimport { Check, Minus } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { useId } from 'react';\n\nexport interface CheckboxProps\n extends Omit<React.ComponentProps<'input'>, 'type' | 'size'> {\n /** Visible label next to the checkbox */\n label?: ReactNode;\n /** Description line below the label */\n description?: string;\n /** Indeterminate state (partial selection) */\n indeterminate?: boolean;\n /** Visual error state */\n error?: boolean;\n /** Error message shown below */\n helper?: string;\n}\n\nexport function Checkbox({\n label,\n description,\n indeterminate = false,\n error = false,\n helper,\n disabled,\n checked,\n id: externalId,\n className = '',\n ...props\n}: CheckboxProps) {\n const generatedId = useId();\n const id = externalId ?? generatedId;\n\n // Attach indeterminate via callback ref — it's not a valid HTML attribute\n const attachIndeterminate = (el: HTMLInputElement | null) => {\n if (el) el.indeterminate = indeterminate;\n };\n\n return (\n <div className={`flex flex-col gap-1 ${className}`}>\n <label\n htmlFor={id}\n className={`group inline-flex cursor-pointer items-start gap-2.5 ${disabled ? 'cursor-not-allowed opacity-60' : ''}`}\n >\n {/* Custom checkbox visual */}\n <div className=\"relative mt-0.5 shrink-0\">\n <input\n ref={attachIndeterminate}\n id={id}\n type=\"checkbox\"\n checked={checked}\n disabled={disabled}\n aria-invalid={error || undefined}\n className=\"peer sr-only\"\n {...props}\n />\n {/* Box background — direct sibling of peer input */}\n <div\n aria-hidden=\"true\"\n className={[\n 'h-[18px] w-[18px] rounded border-2 transition-all duration-100',\n 'peer-focus-visible:ring-2 peer-focus-visible:ring-offset-1',\n error\n ? 'border-rose-400 peer-checked:bg-rose-500 peer-checked:border-rose-500 peer-focus-visible:ring-rose-200'\n : 'border-slate-300 peer-checked:bg-brand-blue peer-checked:border-brand-blue peer-focus-visible:ring-brand-blue/30',\n indeterminate && !error && 'bg-brand-blue border-brand-blue',\n indeterminate && error && 'bg-rose-500 border-rose-500',\n ]\n .filter(Boolean)\n .join(' ')}\n />\n {/* Icon — direct sibling of peer input, positioned over the box */}\n {indeterminate ? (\n <Minus\n size={11}\n strokeWidth={3}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 m-auto text-white\"\n />\n ) : (\n <Check\n size={11}\n strokeWidth={3}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 m-auto text-white opacity-0 peer-checked:opacity-100 transition-opacity duration-100\"\n />\n )}\n </div>\n\n {/* Label + description */}\n {(label || description) && (\n <div className=\"min-w-0\">\n {label && (\n <span className=\"block text-sm font-semibold text-slate-800\">\n {label}\n </span>\n )}\n {description && (\n <span className=\"block text-xs font-medium text-slate-400 leading-relaxed\">\n {description}\n </span>\n )}\n </div>\n )}\n </label>\n\n {helper && (\n <p className={`pl-7 text-xs font-medium ${error ? 'text-rose-500' : 'text-slate-400'}`}>\n {helper}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA4BA,SAAgB,SAAS,EACvB,OACA,aACA,gBAAgB,OAChB,QAAQ,OACR,QACA,UACA,SACA,IAAI,YACJ,YAAY,IACZ,GAAG,SACa;CAChB,MAAM,eAAA,GAAc,MAAA,MAAA,CAAM;CAC1B,MAAM,KAAK,cAAc;CAGzB,MAAM,uBAAuB,OAAgC;EAC3D,IAAI,IAAI,GAAG,gBAAgB;CAC7B;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAW,uBAAuB;EAAvC,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;GACE,SAAS;GACT,WAAW,wDAAwD,WAAW,kCAAkC;GAFlH,UAAA,CAKE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MACE,KAAK;MACD;MACJ,MAAK;MACI;MACC;MACV,gBAAc,SAAS,KAAA;MACvB,WAAU;MACV,GAAI;KACL,CAAA;KAED,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,eAAY;MACZ,WAAW;OACT;OACA;OACA,QACI,2GACA;OACJ,iBAAiB,CAAC,SAAS;OAC3B,iBAAiB,SAAU;MAC7B,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;KACZ,CAAA;KAEA,gBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,OAAD;MACE,MAAM;MACN,aAAa;MACb,eAAY;MACZ,WAAU;KACX,CAAA,IAED,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,OAAD;MACE,MAAM;MACN,aAAa;MACb,eAAY;MACZ,WAAU;KACX,CAAA;IAEA;GAGH,CAAA,IAAA,SAAS,gBACT,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACG,SACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KACb,UAAA;IACG,CAAA,GAEP,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KACb,UAAA;IACG,CAAA,CAEL;GAEF,CAAA,CAAA;EAEN,CAAA,GAAA,UACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;GAAG,WAAW,4BAA4B,QAAQ,kBAAkB;GACjE,UAAA;EACA,CAAA,CAEF;;AAET"}
|
package/lib/index20.mjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { Check, Minus } from "lucide-react";
|
|
4
|
+
//#region src/ui-library/gallery/checkbox/Checkbox.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Checkbox — Design System Component
|
|
7
|
+
*
|
|
8
|
+
* A binary or indeterminate selection control with an optional label.
|
|
9
|
+
* The label is composed inline here because the checkbox + label unit
|
|
10
|
+
* is so consistently used together that separating them adds no value.
|
|
11
|
+
*
|
|
12
|
+
* States: unchecked | checked | indeterminate | disabled | error
|
|
13
|
+
*/
|
|
14
|
+
function Checkbox({ label, description, indeterminate = false, error = false, helper, disabled, checked, id: externalId, className = "", ...props }) {
|
|
15
|
+
const generatedId = useId();
|
|
16
|
+
const id = externalId ?? generatedId;
|
|
17
|
+
const attachIndeterminate = (el) => {
|
|
18
|
+
if (el) el.indeterminate = indeterminate;
|
|
19
|
+
};
|
|
20
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
21
|
+
className: `flex flex-col gap-1 ${className}`,
|
|
22
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
23
|
+
htmlFor: id,
|
|
24
|
+
className: `group inline-flex cursor-pointer items-start gap-2.5 ${disabled ? "cursor-not-allowed opacity-60" : ""}`,
|
|
25
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
26
|
+
className: "relative mt-0.5 shrink-0",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ jsx("input", {
|
|
29
|
+
ref: attachIndeterminate,
|
|
30
|
+
id,
|
|
31
|
+
type: "checkbox",
|
|
32
|
+
checked,
|
|
33
|
+
disabled,
|
|
34
|
+
"aria-invalid": error || void 0,
|
|
35
|
+
className: "peer sr-only",
|
|
36
|
+
...props
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsx("div", {
|
|
39
|
+
"aria-hidden": "true",
|
|
40
|
+
className: [
|
|
41
|
+
"h-[18px] w-[18px] rounded border-2 transition-all duration-100",
|
|
42
|
+
"peer-focus-visible:ring-2 peer-focus-visible:ring-offset-1",
|
|
43
|
+
error ? "border-rose-400 peer-checked:bg-rose-500 peer-checked:border-rose-500 peer-focus-visible:ring-rose-200" : "border-slate-300 peer-checked:bg-brand-blue peer-checked:border-brand-blue peer-focus-visible:ring-brand-blue/30",
|
|
44
|
+
indeterminate && !error && "bg-brand-blue border-brand-blue",
|
|
45
|
+
indeterminate && error && "bg-rose-500 border-rose-500"
|
|
46
|
+
].filter(Boolean).join(" ")
|
|
47
|
+
}),
|
|
48
|
+
indeterminate ? /* @__PURE__ */ jsx(Minus, {
|
|
49
|
+
size: 11,
|
|
50
|
+
strokeWidth: 3,
|
|
51
|
+
"aria-hidden": "true",
|
|
52
|
+
className: "pointer-events-none absolute inset-0 m-auto text-white"
|
|
53
|
+
}) : /* @__PURE__ */ jsx(Check, {
|
|
54
|
+
size: 11,
|
|
55
|
+
strokeWidth: 3,
|
|
56
|
+
"aria-hidden": "true",
|
|
57
|
+
className: "pointer-events-none absolute inset-0 m-auto text-white opacity-0 peer-checked:opacity-100 transition-opacity duration-100"
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
}), (label || description) && /* @__PURE__ */ jsxs("div", {
|
|
61
|
+
className: "min-w-0",
|
|
62
|
+
children: [label && /* @__PURE__ */ jsx("span", {
|
|
63
|
+
className: "block text-sm font-semibold text-slate-800",
|
|
64
|
+
children: label
|
|
65
|
+
}), description && /* @__PURE__ */ jsx("span", {
|
|
66
|
+
className: "block text-xs font-medium text-slate-400 leading-relaxed",
|
|
67
|
+
children: description
|
|
68
|
+
})]
|
|
69
|
+
})]
|
|
70
|
+
}), helper && /* @__PURE__ */ jsx("p", {
|
|
71
|
+
className: `pl-7 text-xs font-medium ${error ? "text-rose-500" : "text-slate-400"}`,
|
|
72
|
+
children: helper
|
|
73
|
+
})]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { Checkbox };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=index20.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index20.mjs","names":[],"sources":["../src/ui-library/gallery/checkbox/Checkbox.tsx"],"sourcesContent":["/**\n * Checkbox — Design System Component\n *\n * A binary or indeterminate selection control with an optional label.\n * The label is composed inline here because the checkbox + label unit\n * is so consistently used together that separating them adds no value.\n *\n * States: unchecked | checked | indeterminate | disabled | error\n */\n\nimport { Check, Minus } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { useId } from 'react';\n\nexport interface CheckboxProps\n extends Omit<React.ComponentProps<'input'>, 'type' | 'size'> {\n /** Visible label next to the checkbox */\n label?: ReactNode;\n /** Description line below the label */\n description?: string;\n /** Indeterminate state (partial selection) */\n indeterminate?: boolean;\n /** Visual error state */\n error?: boolean;\n /** Error message shown below */\n helper?: string;\n}\n\nexport function Checkbox({\n label,\n description,\n indeterminate = false,\n error = false,\n helper,\n disabled,\n checked,\n id: externalId,\n className = '',\n ...props\n}: CheckboxProps) {\n const generatedId = useId();\n const id = externalId ?? generatedId;\n\n // Attach indeterminate via callback ref — it's not a valid HTML attribute\n const attachIndeterminate = (el: HTMLInputElement | null) => {\n if (el) el.indeterminate = indeterminate;\n };\n\n return (\n <div className={`flex flex-col gap-1 ${className}`}>\n <label\n htmlFor={id}\n className={`group inline-flex cursor-pointer items-start gap-2.5 ${disabled ? 'cursor-not-allowed opacity-60' : ''}`}\n >\n {/* Custom checkbox visual */}\n <div className=\"relative mt-0.5 shrink-0\">\n <input\n ref={attachIndeterminate}\n id={id}\n type=\"checkbox\"\n checked={checked}\n disabled={disabled}\n aria-invalid={error || undefined}\n className=\"peer sr-only\"\n {...props}\n />\n {/* Box background — direct sibling of peer input */}\n <div\n aria-hidden=\"true\"\n className={[\n 'h-[18px] w-[18px] rounded border-2 transition-all duration-100',\n 'peer-focus-visible:ring-2 peer-focus-visible:ring-offset-1',\n error\n ? 'border-rose-400 peer-checked:bg-rose-500 peer-checked:border-rose-500 peer-focus-visible:ring-rose-200'\n : 'border-slate-300 peer-checked:bg-brand-blue peer-checked:border-brand-blue peer-focus-visible:ring-brand-blue/30',\n indeterminate && !error && 'bg-brand-blue border-brand-blue',\n indeterminate && error && 'bg-rose-500 border-rose-500',\n ]\n .filter(Boolean)\n .join(' ')}\n />\n {/* Icon — direct sibling of peer input, positioned over the box */}\n {indeterminate ? (\n <Minus\n size={11}\n strokeWidth={3}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 m-auto text-white\"\n />\n ) : (\n <Check\n size={11}\n strokeWidth={3}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 m-auto text-white opacity-0 peer-checked:opacity-100 transition-opacity duration-100\"\n />\n )}\n </div>\n\n {/* Label + description */}\n {(label || description) && (\n <div className=\"min-w-0\">\n {label && (\n <span className=\"block text-sm font-semibold text-slate-800\">\n {label}\n </span>\n )}\n {description && (\n <span className=\"block text-xs font-medium text-slate-400 leading-relaxed\">\n {description}\n </span>\n )}\n </div>\n )}\n </label>\n\n {helper && (\n <p className={`pl-7 text-xs font-medium ${error ? 'text-rose-500' : 'text-slate-400'}`}>\n {helper}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA4BA,SAAgB,SAAS,EACvB,OACA,aACA,gBAAgB,OAChB,QAAQ,OACR,QACA,UACA,SACA,IAAI,YACJ,YAAY,IACZ,GAAG,SACa;CAChB,MAAM,cAAc,MAAM;CAC1B,MAAM,KAAK,cAAc;CAGzB,MAAM,uBAAuB,OAAgC;EAC3D,IAAI,IAAI,GAAG,gBAAgB;CAC7B;CAEA,OACE,qBAAC,OAAD;EAAK,WAAW,uBAAuB;EAAvC,UAAA,CACE,qBAAC,SAAD;GACE,SAAS;GACT,WAAW,wDAAwD,WAAW,kCAAkC;GAFlH,UAAA,CAKE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,SAAD;MACE,KAAK;MACD;MACJ,MAAK;MACI;MACC;MACV,gBAAc,SAAS,KAAA;MACvB,WAAU;MACV,GAAI;KACL,CAAA;KAED,oBAAC,OAAD;MACE,eAAY;MACZ,WAAW;OACT;OACA;OACA,QACI,2GACA;OACJ,iBAAiB,CAAC,SAAS;OAC3B,iBAAiB,SAAU;MAC7B,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;KACZ,CAAA;KAEA,gBACC,oBAAC,OAAD;MACE,MAAM;MACN,aAAa;MACb,eAAY;MACZ,WAAU;KACX,CAAA,IAED,oBAAC,OAAD;MACE,MAAM;MACN,aAAa;MACb,eAAY;MACZ,WAAU;KACX,CAAA;IAEA;GAGH,CAAA,IAAA,SAAS,gBACT,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACG,SACC,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA;IACG,CAAA,GAEP,eACC,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA;IACG,CAAA,CAEL;GAEF,CAAA,CAAA;EAEN,CAAA,GAAA,UACC,oBAAC,KAAD;GAAG,WAAW,4BAA4B,QAAQ,kBAAkB;GACjE,UAAA;EACA,CAAA,CAEF;;AAET"}
|
package/lib/index21.cjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
//#region src/ui-library/gallery/radio-group/RadioGroup.tsx
|
|
4
|
+
/**
|
|
5
|
+
* RadioGroup — Design System Component
|
|
6
|
+
*
|
|
7
|
+
* Accessible radio button group using native <input type="radio"> for
|
|
8
|
+
* correct browser behavior: keyboard navigation, form participation,
|
|
9
|
+
* focus management, and screen-reader announcements all come for free.
|
|
10
|
+
*
|
|
11
|
+
* Composition:
|
|
12
|
+
* <RadioGroup name="plan" defaultValue="monthly" aria-label="Billing period">
|
|
13
|
+
* <RadioItem value="monthly" label="Monthly" />
|
|
14
|
+
* <RadioItem value="annual" label="Annual" description="Save 20%" />
|
|
15
|
+
* <RadioItem value="custom" label="Custom" disabled />
|
|
16
|
+
* </RadioGroup>
|
|
17
|
+
*
|
|
18
|
+
* Layout: pass orientation="horizontal" for an inline row.
|
|
19
|
+
*/
|
|
20
|
+
var RadioGroupContext = (0, react.createContext)(null);
|
|
21
|
+
function useRadioGroupContext() {
|
|
22
|
+
const ctx = (0, react.useContext)(RadioGroupContext);
|
|
23
|
+
if (!ctx) throw new Error("RadioItem must be used inside RadioGroup");
|
|
24
|
+
return ctx;
|
|
25
|
+
}
|
|
26
|
+
function RadioGroup({ name: externalName, defaultValue = "", value: controlledValue, onValueChange, disabled = false, required = false, orientation = "vertical", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className = "" }) {
|
|
27
|
+
const generatedName = (0, react.useId)();
|
|
28
|
+
const name = externalName ?? generatedName;
|
|
29
|
+
const isControlled = controlledValue !== void 0;
|
|
30
|
+
const [uncontrolled, setUncontrolled] = (0, react.useState)(defaultValue);
|
|
31
|
+
const activeValue = isControlled ? controlledValue : uncontrolled;
|
|
32
|
+
const select = (val) => {
|
|
33
|
+
if (!isControlled) setUncontrolled(val);
|
|
34
|
+
onValueChange?.(val);
|
|
35
|
+
};
|
|
36
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RadioGroupContext.Provider, {
|
|
37
|
+
value: {
|
|
38
|
+
name,
|
|
39
|
+
activeValue,
|
|
40
|
+
select,
|
|
41
|
+
disabled,
|
|
42
|
+
required
|
|
43
|
+
},
|
|
44
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
45
|
+
role: "radiogroup",
|
|
46
|
+
"aria-label": ariaLabel,
|
|
47
|
+
"aria-labelledby": ariaLabelledby,
|
|
48
|
+
"aria-required": required || void 0,
|
|
49
|
+
"aria-disabled": disabled || void 0,
|
|
50
|
+
className: [orientation === "horizontal" ? "flex flex-wrap gap-4" : "flex flex-col gap-3", className].filter(Boolean).join(" "),
|
|
51
|
+
children
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function RadioItem({ value, label, description, disabled: itemDisabled = false, className = "" }) {
|
|
56
|
+
const { name, activeValue, select, disabled: groupDisabled, required } = useRadioGroupContext();
|
|
57
|
+
const id = (0, react.useId)();
|
|
58
|
+
const isDisabled = groupDisabled || itemDisabled;
|
|
59
|
+
const isChecked = activeValue === value;
|
|
60
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
61
|
+
className: [
|
|
62
|
+
"group flex cursor-pointer items-start gap-3",
|
|
63
|
+
isDisabled ? "cursor-not-allowed opacity-50" : "",
|
|
64
|
+
className
|
|
65
|
+
].filter(Boolean).join(" "),
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
68
|
+
type: "radio",
|
|
69
|
+
id,
|
|
70
|
+
name,
|
|
71
|
+
value,
|
|
72
|
+
checked: isChecked,
|
|
73
|
+
disabled: isDisabled,
|
|
74
|
+
required,
|
|
75
|
+
onChange: () => !isDisabled && select(value),
|
|
76
|
+
className: "peer sr-only"
|
|
77
|
+
}),
|
|
78
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
79
|
+
className: [
|
|
80
|
+
"relative mt-0.5 flex h-[18px] w-[18px] shrink-0 items-center justify-center",
|
|
81
|
+
"rounded-full border-2 transition-all duration-100",
|
|
82
|
+
"peer-focus-visible:ring-2 peer-focus-visible:ring-brand-blue/30 peer-focus-visible:ring-offset-1",
|
|
83
|
+
isChecked ? "border-brand-blue bg-brand-blue" : "border-slate-300 bg-white group-hover:border-brand-blue"
|
|
84
|
+
].filter(Boolean).join(" "),
|
|
85
|
+
"aria-hidden": "true",
|
|
86
|
+
children: isChecked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-2 w-2 rounded-full bg-white" })
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
89
|
+
className: "min-w-0",
|
|
90
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
91
|
+
className: "block text-sm font-semibold text-slate-800",
|
|
92
|
+
children: label
|
|
93
|
+
}), description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
94
|
+
className: "block text-xs font-medium text-slate-400 leading-relaxed mt-0.5",
|
|
95
|
+
children: description
|
|
96
|
+
})]
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
exports.RadioGroup = RadioGroup;
|
|
103
|
+
exports.RadioItem = RadioItem;
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=index21.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index21.cjs","names":[],"sources":["../src/ui-library/gallery/radio-group/RadioGroup.tsx"],"sourcesContent":["/**\n * RadioGroup — Design System Component\n *\n * Accessible radio button group using native <input type=\"radio\"> for\n * correct browser behavior: keyboard navigation, form participation,\n * focus management, and screen-reader announcements all come for free.\n *\n * Composition:\n * <RadioGroup name=\"plan\" defaultValue=\"monthly\" aria-label=\"Billing period\">\n * <RadioItem value=\"monthly\" label=\"Monthly\" />\n * <RadioItem value=\"annual\" label=\"Annual\" description=\"Save 20%\" />\n * <RadioItem value=\"custom\" label=\"Custom\" disabled />\n * </RadioGroup>\n *\n * Layout: pass orientation=\"horizontal\" for an inline row.\n */\n\nimport { createContext, useContext, useId, useState, type ReactNode } from 'react';\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\ninterface RadioGroupContextValue {\n name: string;\n activeValue: string;\n select: (value: string) => void;\n disabled: boolean;\n required: boolean;\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null);\n\nfunction useRadioGroupContext() {\n const ctx = useContext(RadioGroupContext);\n if (!ctx) throw new Error('RadioItem must be used inside RadioGroup');\n return ctx;\n}\n\n// ── RadioGroup ────────────────────────────────────────────────────────────────\n\nexport interface RadioGroupProps {\n /** HTML name attribute shared by all radio inputs in the group */\n name?: string;\n /** Uncontrolled: initial selected value */\n defaultValue?: string;\n /** Controlled: current selected value */\n value?: string;\n /** Controlled: called on selection change */\n onValueChange?: (value: string) => void;\n /** Disables all radio items */\n disabled?: boolean;\n /** Marks the group as required */\n required?: boolean;\n /** 'vertical' (default) or 'horizontal' */\n orientation?: 'vertical' | 'horizontal';\n /** Accessible label for the group */\n 'aria-label'?: string;\n /** ID of an external <label> or heading */\n 'aria-labelledby'?: string;\n children: ReactNode;\n className?: string;\n}\n\nexport function RadioGroup({\n name: externalName,\n defaultValue = '',\n value: controlledValue,\n onValueChange,\n disabled = false,\n required = false,\n orientation = 'vertical',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n children,\n className = '',\n}: RadioGroupProps) {\n const generatedName = useId();\n const name = externalName ?? generatedName;\n\n const isControlled = controlledValue !== undefined;\n const [uncontrolled, setUncontrolled] = useState(defaultValue);\n const activeValue = isControlled ? controlledValue : uncontrolled;\n\n const select = (val: string) => {\n if (!isControlled) setUncontrolled(val);\n onValueChange?.(val);\n };\n\n return (\n <RadioGroupContext.Provider value={{ name, activeValue, select, disabled, required }}>\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-required={required || undefined}\n aria-disabled={disabled || undefined}\n className={[\n orientation === 'horizontal' ? 'flex flex-wrap gap-4' : 'flex flex-col gap-3',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {children}\n </div>\n </RadioGroupContext.Provider>\n );\n}\n\n// ── RadioItem ─────────────────────────────────────────────────────────────────\n\nexport interface RadioItemProps {\n value: string;\n label: ReactNode;\n /** Optional description line below the label */\n description?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function RadioItem({\n value,\n label,\n description,\n disabled: itemDisabled = false,\n className = '',\n}: RadioItemProps) {\n const { name, activeValue, select, disabled: groupDisabled, required } = useRadioGroupContext();\n const id = useId();\n const isDisabled = groupDisabled || itemDisabled;\n const isChecked = activeValue === value;\n\n return (\n <label\n className={[\n 'group flex cursor-pointer items-start gap-3',\n isDisabled ? 'cursor-not-allowed opacity-50' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {/* Native radio input — visually hidden, but fully keyboard and AT accessible */}\n <input\n type=\"radio\"\n id={id}\n name={name}\n value={value}\n checked={isChecked}\n disabled={isDisabled}\n required={required}\n onChange={() => !isDisabled && select(value)}\n className=\"peer sr-only\"\n />\n\n {/* Custom radio visual */}\n <div\n className={[\n 'relative mt-0.5 flex h-[18px] w-[18px] shrink-0 items-center justify-center',\n 'rounded-full border-2 transition-all duration-100',\n 'peer-focus-visible:ring-2 peer-focus-visible:ring-brand-blue/30 peer-focus-visible:ring-offset-1',\n isChecked\n ? 'border-brand-blue bg-brand-blue'\n : 'border-slate-300 bg-white group-hover:border-brand-blue',\n ]\n .filter(Boolean)\n .join(' ')}\n aria-hidden=\"true\"\n >\n {isChecked && (\n <span className=\"h-2 w-2 rounded-full bg-white\" />\n )}\n </div>\n\n {/* Label + description */}\n <div className=\"min-w-0\">\n <span className=\"block text-sm font-semibold text-slate-800\">{label}</span>\n {description && (\n <span className=\"block text-xs font-medium text-slate-400 leading-relaxed mt-0.5\">\n {description}\n </span>\n )}\n </div>\n </label>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,IAAM,qBAAA,GAAoB,MAAA,cAAA,CAA6C,IAAI;AAE3E,SAAS,uBAAuB;CAC9B,MAAM,OAAA,GAAM,MAAA,WAAA,CAAW,iBAAiB;CACxC,IAAI,CAAC,KAAK,MAAM,IAAI,MAAM,0CAA0C;CACpE,OAAO;AACT;AA2BA,SAAgB,WAAW,EACzB,MAAM,cACN,eAAe,IACf,OAAO,iBACP,eACA,WAAW,OACX,WAAW,OACX,cAAc,YACd,cAAc,WACd,mBAAmB,gBACnB,UACA,YAAY,MACM;CAClB,MAAM,iBAAA,GAAgB,MAAA,MAAA,CAAM;CAC5B,MAAM,OAAO,gBAAgB;CAE7B,MAAM,eAAe,oBAAoB,KAAA;CACzC,MAAM,CAAC,cAAc,oBAAA,GAAmB,MAAA,SAAA,CAAS,YAAY;CAC7D,MAAM,cAAc,eAAe,kBAAkB;CAErD,MAAM,UAAU,QAAgB;EAC9B,IAAI,CAAC,cAAc,gBAAgB,GAAG;EACtC,gBAAgB,GAAG;CACrB;CAEA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,kBAAkB,UAAnB;EAA4B,OAAO;GAAE;GAAM;GAAa;GAAQ;GAAU;EAAS;EACjF,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;GACE,MAAK;GACL,cAAY;GACZ,mBAAiB;GACjB,iBAAe,YAAY,KAAA;GAC3B,iBAAe,YAAY,KAAA;GAC3B,WAAW,CACT,gBAAgB,eAAe,yBAAyB,uBACxD,SACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;GAEV;EACE,CAAA;CACqB,CAAA;AAEhC;AAaA,SAAgB,UAAU,EACxB,OACA,OACA,aACA,UAAU,eAAe,OACzB,YAAY,MACK;CACjB,MAAM,EAAE,MAAM,aAAa,QAAQ,UAAU,eAAe,aAAa,qBAAqB;CAC9F,MAAM,MAAA,GAAK,MAAA,MAAA,CAAM;CACjB,MAAM,aAAa,iBAAiB;CACpC,MAAM,YAAa,gBAAgB;CAEnC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;EACE,WAAW;GACT;GACA,aAAa,kCAAkC;GAC/C;EACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAPb,UAAA;GAUE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;IACE,MAAK;IACD;IACE;IACC;IACP,SAAS;IACT,UAAU;IACA;IACV,gBAAgB,CAAC,cAAc,OAAO,KAAK;IAC3C,WAAU;GACX,CAAA;GAGD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IACE,WAAW;KACT;KACA;KACA;KACA,YACI,oCACA;IACN,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;IACX,eAAY;IAEX,UAAA,aACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAU,gCAAiC,CAAA;GAEhD,CAAA;GAGL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KAA8C,UAAA;IAAY,CAAA,GACzE,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KACb,UAAA;IACG,CAAA,CAEL;;EACA;;AAEX"}
|
package/lib/index21.mjs
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useId, useState } from "react";
|
|
3
|
+
//#region src/ui-library/gallery/radio-group/RadioGroup.tsx
|
|
4
|
+
/**
|
|
5
|
+
* RadioGroup — Design System Component
|
|
6
|
+
*
|
|
7
|
+
* Accessible radio button group using native <input type="radio"> for
|
|
8
|
+
* correct browser behavior: keyboard navigation, form participation,
|
|
9
|
+
* focus management, and screen-reader announcements all come for free.
|
|
10
|
+
*
|
|
11
|
+
* Composition:
|
|
12
|
+
* <RadioGroup name="plan" defaultValue="monthly" aria-label="Billing period">
|
|
13
|
+
* <RadioItem value="monthly" label="Monthly" />
|
|
14
|
+
* <RadioItem value="annual" label="Annual" description="Save 20%" />
|
|
15
|
+
* <RadioItem value="custom" label="Custom" disabled />
|
|
16
|
+
* </RadioGroup>
|
|
17
|
+
*
|
|
18
|
+
* Layout: pass orientation="horizontal" for an inline row.
|
|
19
|
+
*/
|
|
20
|
+
var RadioGroupContext = createContext(null);
|
|
21
|
+
function useRadioGroupContext() {
|
|
22
|
+
const ctx = useContext(RadioGroupContext);
|
|
23
|
+
if (!ctx) throw new Error("RadioItem must be used inside RadioGroup");
|
|
24
|
+
return ctx;
|
|
25
|
+
}
|
|
26
|
+
function RadioGroup({ name: externalName, defaultValue = "", value: controlledValue, onValueChange, disabled = false, required = false, orientation = "vertical", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className = "" }) {
|
|
27
|
+
const generatedName = useId();
|
|
28
|
+
const name = externalName ?? generatedName;
|
|
29
|
+
const isControlled = controlledValue !== void 0;
|
|
30
|
+
const [uncontrolled, setUncontrolled] = useState(defaultValue);
|
|
31
|
+
const activeValue = isControlled ? controlledValue : uncontrolled;
|
|
32
|
+
const select = (val) => {
|
|
33
|
+
if (!isControlled) setUncontrolled(val);
|
|
34
|
+
onValueChange?.(val);
|
|
35
|
+
};
|
|
36
|
+
return /* @__PURE__ */ jsx(RadioGroupContext.Provider, {
|
|
37
|
+
value: {
|
|
38
|
+
name,
|
|
39
|
+
activeValue,
|
|
40
|
+
select,
|
|
41
|
+
disabled,
|
|
42
|
+
required
|
|
43
|
+
},
|
|
44
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
45
|
+
role: "radiogroup",
|
|
46
|
+
"aria-label": ariaLabel,
|
|
47
|
+
"aria-labelledby": ariaLabelledby,
|
|
48
|
+
"aria-required": required || void 0,
|
|
49
|
+
"aria-disabled": disabled || void 0,
|
|
50
|
+
className: [orientation === "horizontal" ? "flex flex-wrap gap-4" : "flex flex-col gap-3", className].filter(Boolean).join(" "),
|
|
51
|
+
children
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function RadioItem({ value, label, description, disabled: itemDisabled = false, className = "" }) {
|
|
56
|
+
const { name, activeValue, select, disabled: groupDisabled, required } = useRadioGroupContext();
|
|
57
|
+
const id = useId();
|
|
58
|
+
const isDisabled = groupDisabled || itemDisabled;
|
|
59
|
+
const isChecked = activeValue === value;
|
|
60
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
61
|
+
className: [
|
|
62
|
+
"group flex cursor-pointer items-start gap-3",
|
|
63
|
+
isDisabled ? "cursor-not-allowed opacity-50" : "",
|
|
64
|
+
className
|
|
65
|
+
].filter(Boolean).join(" "),
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx("input", {
|
|
68
|
+
type: "radio",
|
|
69
|
+
id,
|
|
70
|
+
name,
|
|
71
|
+
value,
|
|
72
|
+
checked: isChecked,
|
|
73
|
+
disabled: isDisabled,
|
|
74
|
+
required,
|
|
75
|
+
onChange: () => !isDisabled && select(value),
|
|
76
|
+
className: "peer sr-only"
|
|
77
|
+
}),
|
|
78
|
+
/* @__PURE__ */ jsx("div", {
|
|
79
|
+
className: [
|
|
80
|
+
"relative mt-0.5 flex h-[18px] w-[18px] shrink-0 items-center justify-center",
|
|
81
|
+
"rounded-full border-2 transition-all duration-100",
|
|
82
|
+
"peer-focus-visible:ring-2 peer-focus-visible:ring-brand-blue/30 peer-focus-visible:ring-offset-1",
|
|
83
|
+
isChecked ? "border-brand-blue bg-brand-blue" : "border-slate-300 bg-white group-hover:border-brand-blue"
|
|
84
|
+
].filter(Boolean).join(" "),
|
|
85
|
+
"aria-hidden": "true",
|
|
86
|
+
children: isChecked && /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-white" })
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ jsxs("div", {
|
|
89
|
+
className: "min-w-0",
|
|
90
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
91
|
+
className: "block text-sm font-semibold text-slate-800",
|
|
92
|
+
children: label
|
|
93
|
+
}), description && /* @__PURE__ */ jsx("span", {
|
|
94
|
+
className: "block text-xs font-medium text-slate-400 leading-relaxed mt-0.5",
|
|
95
|
+
children: description
|
|
96
|
+
})]
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { RadioGroup, RadioItem };
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=index21.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index21.mjs","names":[],"sources":["../src/ui-library/gallery/radio-group/RadioGroup.tsx"],"sourcesContent":["/**\n * RadioGroup — Design System Component\n *\n * Accessible radio button group using native <input type=\"radio\"> for\n * correct browser behavior: keyboard navigation, form participation,\n * focus management, and screen-reader announcements all come for free.\n *\n * Composition:\n * <RadioGroup name=\"plan\" defaultValue=\"monthly\" aria-label=\"Billing period\">\n * <RadioItem value=\"monthly\" label=\"Monthly\" />\n * <RadioItem value=\"annual\" label=\"Annual\" description=\"Save 20%\" />\n * <RadioItem value=\"custom\" label=\"Custom\" disabled />\n * </RadioGroup>\n *\n * Layout: pass orientation=\"horizontal\" for an inline row.\n */\n\nimport { createContext, useContext, useId, useState, type ReactNode } from 'react';\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\ninterface RadioGroupContextValue {\n name: string;\n activeValue: string;\n select: (value: string) => void;\n disabled: boolean;\n required: boolean;\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null);\n\nfunction useRadioGroupContext() {\n const ctx = useContext(RadioGroupContext);\n if (!ctx) throw new Error('RadioItem must be used inside RadioGroup');\n return ctx;\n}\n\n// ── RadioGroup ────────────────────────────────────────────────────────────────\n\nexport interface RadioGroupProps {\n /** HTML name attribute shared by all radio inputs in the group */\n name?: string;\n /** Uncontrolled: initial selected value */\n defaultValue?: string;\n /** Controlled: current selected value */\n value?: string;\n /** Controlled: called on selection change */\n onValueChange?: (value: string) => void;\n /** Disables all radio items */\n disabled?: boolean;\n /** Marks the group as required */\n required?: boolean;\n /** 'vertical' (default) or 'horizontal' */\n orientation?: 'vertical' | 'horizontal';\n /** Accessible label for the group */\n 'aria-label'?: string;\n /** ID of an external <label> or heading */\n 'aria-labelledby'?: string;\n children: ReactNode;\n className?: string;\n}\n\nexport function RadioGroup({\n name: externalName,\n defaultValue = '',\n value: controlledValue,\n onValueChange,\n disabled = false,\n required = false,\n orientation = 'vertical',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n children,\n className = '',\n}: RadioGroupProps) {\n const generatedName = useId();\n const name = externalName ?? generatedName;\n\n const isControlled = controlledValue !== undefined;\n const [uncontrolled, setUncontrolled] = useState(defaultValue);\n const activeValue = isControlled ? controlledValue : uncontrolled;\n\n const select = (val: string) => {\n if (!isControlled) setUncontrolled(val);\n onValueChange?.(val);\n };\n\n return (\n <RadioGroupContext.Provider value={{ name, activeValue, select, disabled, required }}>\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-required={required || undefined}\n aria-disabled={disabled || undefined}\n className={[\n orientation === 'horizontal' ? 'flex flex-wrap gap-4' : 'flex flex-col gap-3',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {children}\n </div>\n </RadioGroupContext.Provider>\n );\n}\n\n// ── RadioItem ─────────────────────────────────────────────────────────────────\n\nexport interface RadioItemProps {\n value: string;\n label: ReactNode;\n /** Optional description line below the label */\n description?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function RadioItem({\n value,\n label,\n description,\n disabled: itemDisabled = false,\n className = '',\n}: RadioItemProps) {\n const { name, activeValue, select, disabled: groupDisabled, required } = useRadioGroupContext();\n const id = useId();\n const isDisabled = groupDisabled || itemDisabled;\n const isChecked = activeValue === value;\n\n return (\n <label\n className={[\n 'group flex cursor-pointer items-start gap-3',\n isDisabled ? 'cursor-not-allowed opacity-50' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {/* Native radio input — visually hidden, but fully keyboard and AT accessible */}\n <input\n type=\"radio\"\n id={id}\n name={name}\n value={value}\n checked={isChecked}\n disabled={isDisabled}\n required={required}\n onChange={() => !isDisabled && select(value)}\n className=\"peer sr-only\"\n />\n\n {/* Custom radio visual */}\n <div\n className={[\n 'relative mt-0.5 flex h-[18px] w-[18px] shrink-0 items-center justify-center',\n 'rounded-full border-2 transition-all duration-100',\n 'peer-focus-visible:ring-2 peer-focus-visible:ring-brand-blue/30 peer-focus-visible:ring-offset-1',\n isChecked\n ? 'border-brand-blue bg-brand-blue'\n : 'border-slate-300 bg-white group-hover:border-brand-blue',\n ]\n .filter(Boolean)\n .join(' ')}\n aria-hidden=\"true\"\n >\n {isChecked && (\n <span className=\"h-2 w-2 rounded-full bg-white\" />\n )}\n </div>\n\n {/* Label + description */}\n <div className=\"min-w-0\">\n <span className=\"block text-sm font-semibold text-slate-800\">{label}</span>\n {description && (\n <span className=\"block text-xs font-medium text-slate-400 leading-relaxed mt-0.5\">\n {description}\n </span>\n )}\n </div>\n </label>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,IAAM,oBAAoB,cAA6C,IAAI;AAE3E,SAAS,uBAAuB;CAC9B,MAAM,MAAM,WAAW,iBAAiB;CACxC,IAAI,CAAC,KAAK,MAAM,IAAI,MAAM,0CAA0C;CACpE,OAAO;AACT;AA2BA,SAAgB,WAAW,EACzB,MAAM,cACN,eAAe,IACf,OAAO,iBACP,eACA,WAAW,OACX,WAAW,OACX,cAAc,YACd,cAAc,WACd,mBAAmB,gBACnB,UACA,YAAY,MACM;CAClB,MAAM,gBAAgB,MAAM;CAC5B,MAAM,OAAO,gBAAgB;CAE7B,MAAM,eAAe,oBAAoB,KAAA;CACzC,MAAM,CAAC,cAAc,mBAAmB,SAAS,YAAY;CAC7D,MAAM,cAAc,eAAe,kBAAkB;CAErD,MAAM,UAAU,QAAgB;EAC9B,IAAI,CAAC,cAAc,gBAAgB,GAAG;EACtC,gBAAgB,GAAG;CACrB;CAEA,OACE,oBAAC,kBAAkB,UAAnB;EAA4B,OAAO;GAAE;GAAM;GAAa;GAAQ;GAAU;EAAS;EACjF,UAAA,oBAAC,OAAD;GACE,MAAK;GACL,cAAY;GACZ,mBAAiB;GACjB,iBAAe,YAAY,KAAA;GAC3B,iBAAe,YAAY,KAAA;GAC3B,WAAW,CACT,gBAAgB,eAAe,yBAAyB,uBACxD,SACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;GAEV;EACE,CAAA;CACqB,CAAA;AAEhC;AAaA,SAAgB,UAAU,EACxB,OACA,OACA,aACA,UAAU,eAAe,OACzB,YAAY,MACK;CACjB,MAAM,EAAE,MAAM,aAAa,QAAQ,UAAU,eAAe,aAAa,qBAAqB;CAC9F,MAAM,KAAK,MAAM;CACjB,MAAM,aAAa,iBAAiB;CACpC,MAAM,YAAa,gBAAgB;CAEnC,OACE,qBAAC,SAAD;EACE,WAAW;GACT;GACA,aAAa,kCAAkC;GAC/C;EACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;EAPb,UAAA;GAUE,oBAAC,SAAD;IACE,MAAK;IACD;IACE;IACC;IACP,SAAS;IACT,UAAU;IACA;IACV,gBAAgB,CAAC,cAAc,OAAO,KAAK;IAC3C,WAAU;GACX,CAAA;GAGD,oBAAC,OAAD;IACE,WAAW;KACT;KACA;KACA;KACA,YACI,oCACA;IACN,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;IACX,eAAY;IAEX,UAAA,aACC,oBAAC,QAAD,EAAM,WAAU,gCAAiC,CAAA;GAEhD,CAAA;GAGL,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAA8C,UAAA;IAAY,CAAA,GACzE,eACC,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA;IACG,CAAA,CAEL;;EACA;;AAEX"}
|
package/lib/index22.cjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
//#region src/ui-library/gallery/switch/Switch.tsx
|
|
4
|
+
function Switch({ checked, onChange, label, description, disabled = false, loading = false, id: externalId }) {
|
|
5
|
+
const generatedId = (0, react.useId)();
|
|
6
|
+
const id = externalId ?? generatedId;
|
|
7
|
+
const labelId = label ? `${id}-label` : void 0;
|
|
8
|
+
const isInteractive = !disabled && !loading;
|
|
9
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10
|
+
className: "flex items-start gap-3",
|
|
11
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
12
|
+
id,
|
|
13
|
+
type: "button",
|
|
14
|
+
role: "switch",
|
|
15
|
+
"aria-checked": checked,
|
|
16
|
+
"aria-labelledby": labelId,
|
|
17
|
+
"aria-disabled": !isInteractive || void 0,
|
|
18
|
+
disabled: !isInteractive,
|
|
19
|
+
onClick: () => isInteractive && onChange(!checked),
|
|
20
|
+
className: [
|
|
21
|
+
"relative inline-flex h-6 w-11 shrink-0 items-center rounded-full border-2 border-transparent transition-colors duration-200",
|
|
22
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 focus-visible:ring-offset-1",
|
|
23
|
+
checked ? "bg-brand-blue" : "bg-slate-200",
|
|
24
|
+
!isInteractive ? "cursor-not-allowed opacity-60" : "cursor-pointer"
|
|
25
|
+
].filter(Boolean).join(" "),
|
|
26
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27
|
+
"aria-hidden": "true",
|
|
28
|
+
className: ["inline-block h-5 w-5 rounded-full bg-white shadow-sm ring-0 transition-transform duration-200", checked ? "translate-x-5" : "translate-x-0"].filter(Boolean).join(" "),
|
|
29
|
+
children: loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
30
|
+
className: "flex h-full w-full items-center justify-center",
|
|
31
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-2.5 w-2.5 animate-spin rounded-full border-2 border-slate-300 border-t-brand-blue" })
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
}), (label || description) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
35
|
+
className: "min-w-0 pt-0.5",
|
|
36
|
+
children: [label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
37
|
+
id: labelId,
|
|
38
|
+
className: `block text-sm font-semibold ${disabled ? "text-slate-400" : "text-slate-800"}`,
|
|
39
|
+
children: label
|
|
40
|
+
}), description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
41
|
+
className: "block text-xs font-medium leading-relaxed text-slate-400",
|
|
42
|
+
children: description
|
|
43
|
+
})]
|
|
44
|
+
})]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
exports.Switch = Switch;
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=index22.cjs.map
|