@hadoku/task-ui-components 0.0.0-stage → 4.20.3

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.
Files changed (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +917 -2
  3. package/dist/app-header.css +288 -0
  4. package/dist/bento.css +143 -0
  5. package/dist/button.css +227 -0
  6. package/dist/components/AppHeader.d.ts +61 -0
  7. package/dist/components/AppHeader.d.ts.map +1 -0
  8. package/dist/components/AppHeader.js +23 -0
  9. package/dist/components/AppHeader.js.map +1 -0
  10. package/dist/components/BentoCard.d.ts +23 -0
  11. package/dist/components/BentoCard.d.ts.map +1 -0
  12. package/dist/components/BentoCard.js +49 -0
  13. package/dist/components/BentoCard.js.map +1 -0
  14. package/dist/components/BentoGrid.d.ts +24 -0
  15. package/dist/components/BentoGrid.d.ts.map +1 -0
  16. package/dist/components/BentoGrid.js +22 -0
  17. package/dist/components/BentoGrid.js.map +1 -0
  18. package/dist/components/Button.d.ts +28 -0
  19. package/dist/components/Button.d.ts.map +1 -0
  20. package/dist/components/Button.js +51 -0
  21. package/dist/components/Button.js.map +1 -0
  22. package/dist/components/ConnectedSettings.d.ts +53 -0
  23. package/dist/components/ConnectedSettings.d.ts.map +1 -0
  24. package/dist/components/ConnectedSettings.js +302 -0
  25. package/dist/components/ConnectedSettings.js.map +1 -0
  26. package/dist/components/ConnectedThemePicker.d.ts +52 -0
  27. package/dist/components/ConnectedThemePicker.d.ts.map +1 -0
  28. package/dist/components/ConnectedThemePicker.js +32 -0
  29. package/dist/components/ConnectedThemePicker.js.map +1 -0
  30. package/dist/components/ContextMenu.d.ts +22 -0
  31. package/dist/components/ContextMenu.d.ts.map +1 -0
  32. package/dist/components/ContextMenu.js +23 -0
  33. package/dist/components/ContextMenu.js.map +1 -0
  34. package/dist/components/HadokuThemePicker.d.ts +27 -0
  35. package/dist/components/HadokuThemePicker.d.ts.map +1 -0
  36. package/dist/components/HadokuThemePicker.js +19 -0
  37. package/dist/components/HadokuThemePicker.js.map +1 -0
  38. package/dist/components/LoadingSkeleton.d.ts +14 -0
  39. package/dist/components/LoadingSkeleton.d.ts.map +1 -0
  40. package/dist/components/LoadingSkeleton.js +62 -0
  41. package/dist/components/LoadingSkeleton.js.map +1 -0
  42. package/dist/components/Modal.d.ts +39 -0
  43. package/dist/components/Modal.d.ts.map +1 -0
  44. package/dist/components/Modal.js +106 -0
  45. package/dist/components/Modal.js.map +1 -0
  46. package/dist/components/NumberInput.d.ts +26 -0
  47. package/dist/components/NumberInput.d.ts.map +1 -0
  48. package/dist/components/NumberInput.js +131 -0
  49. package/dist/components/NumberInput.js.map +1 -0
  50. package/dist/components/Select.d.ts +52 -0
  51. package/dist/components/Select.d.ts.map +1 -0
  52. package/dist/components/Select.js +281 -0
  53. package/dist/components/Select.js.map +1 -0
  54. package/dist/components/SettingsModal.d.ts +69 -0
  55. package/dist/components/SettingsModal.d.ts.map +1 -0
  56. package/dist/components/SettingsModal.js +141 -0
  57. package/dist/components/SettingsModal.js.map +1 -0
  58. package/dist/components/ThemeIcons.d.ts +26 -0
  59. package/dist/components/ThemeIcons.d.ts.map +1 -0
  60. package/dist/components/ThemeIcons.js +74 -0
  61. package/dist/components/ThemeIcons.js.map +1 -0
  62. package/dist/components/ThemePicker.d.ts +28 -0
  63. package/dist/components/ThemePicker.d.ts.map +1 -0
  64. package/dist/components/ThemePicker.js +101 -0
  65. package/dist/components/ThemePicker.js.map +1 -0
  66. package/dist/components/Toast.d.ts +13 -0
  67. package/dist/components/Toast.d.ts.map +1 -0
  68. package/dist/components/Toast.js +70 -0
  69. package/dist/components/Toast.js.map +1 -0
  70. package/dist/components/Toaster.d.ts +19 -0
  71. package/dist/components/Toaster.d.ts.map +1 -0
  72. package/dist/components/Toaster.js +19 -0
  73. package/dist/components/Toaster.js.map +1 -0
  74. package/dist/components/Tooltip.d.ts +49 -0
  75. package/dist/components/Tooltip.d.ts.map +1 -0
  76. package/dist/components/Tooltip.js +143 -0
  77. package/dist/components/Tooltip.js.map +1 -0
  78. package/dist/context-menu.css +11 -0
  79. package/dist/controls.css +137 -0
  80. package/dist/hooks/useReducedMotion.d.ts +7 -0
  81. package/dist/hooks/useReducedMotion.d.ts.map +1 -0
  82. package/dist/hooks/useReducedMotion.js +25 -0
  83. package/dist/hooks/useReducedMotion.js.map +1 -0
  84. package/dist/hooks/useToast.d.ts +18 -0
  85. package/dist/hooks/useToast.d.ts.map +1 -0
  86. package/dist/hooks/useToast.js +28 -0
  87. package/dist/hooks/useToast.js.map +1 -0
  88. package/dist/icon-btn.css +64 -0
  89. package/dist/index.d.ts +49 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +72 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/lib/menuPlacement.d.ts +16 -0
  94. package/dist/lib/menuPlacement.d.ts.map +1 -0
  95. package/dist/lib/menuPlacement.js +19 -0
  96. package/dist/lib/menuPlacement.js.map +1 -0
  97. package/dist/lib/selectMenus.d.ts +30 -0
  98. package/dist/lib/selectMenus.d.ts.map +1 -0
  99. package/dist/lib/selectMenus.js +236 -0
  100. package/dist/lib/selectMenus.js.map +1 -0
  101. package/dist/lib/settingsClient.d.ts +120 -0
  102. package/dist/lib/settingsClient.d.ts.map +1 -0
  103. package/dist/lib/settingsClient.js +140 -0
  104. package/dist/lib/settingsClient.js.map +1 -0
  105. package/dist/lib/themeScope.d.ts +19 -0
  106. package/dist/lib/themeScope.d.ts.map +1 -0
  107. package/dist/lib/themeScope.js +10 -0
  108. package/dist/lib/themeScope.js.map +1 -0
  109. package/dist/lib/titleTooltips.d.ts +38 -0
  110. package/dist/lib/titleTooltips.d.ts.map +1 -0
  111. package/dist/lib/titleTooltips.js +166 -0
  112. package/dist/lib/titleTooltips.js.map +1 -0
  113. package/dist/lib/tooltipPlacement.d.ts +14 -0
  114. package/dist/lib/tooltipPlacement.d.ts.map +1 -0
  115. package/dist/lib/tooltipPlacement.js +34 -0
  116. package/dist/lib/tooltipPlacement.js.map +1 -0
  117. package/dist/modal.css +129 -0
  118. package/dist/number-input.css +131 -0
  119. package/dist/scrollbar.css +165 -0
  120. package/dist/select.css +251 -0
  121. package/dist/styles.css +1866 -0
  122. package/dist/task-ui-components.css +1866 -0
  123. package/dist/theme-picker.css +219 -0
  124. package/dist/toaster.css +159 -0
  125. package/dist/tooltip.css +103 -0
  126. package/dist/types/index.d.ts +37 -0
  127. package/dist/types/index.d.ts.map +1 -0
  128. package/package.json +60 -4
  129. package/src/app-header.css +288 -0
  130. package/src/bento.css +143 -0
  131. package/src/button.css +227 -0
  132. package/src/components/AppHeader.tsx +87 -0
  133. package/src/components/BentoCard.tsx +72 -0
  134. package/src/components/BentoGrid.tsx +48 -0
  135. package/src/components/Button.tsx +81 -0
  136. package/src/components/ConnectedSettings.tsx +465 -0
  137. package/src/components/ConnectedThemePicker.tsx +79 -0
  138. package/src/components/ContextMenu.tsx +48 -0
  139. package/src/components/HadokuThemePicker.tsx +39 -0
  140. package/src/components/LoadingSkeleton.tsx +84 -0
  141. package/src/components/Modal.tsx +171 -0
  142. package/src/components/NumberInput.tsx +161 -0
  143. package/src/components/Select.tsx +359 -0
  144. package/src/components/SettingsModal.tsx +235 -0
  145. package/src/components/ThemeIcons.tsx +127 -0
  146. package/src/components/ThemePicker.tsx +149 -0
  147. package/src/components/Toast.tsx +92 -0
  148. package/src/components/Toaster.tsx +43 -0
  149. package/src/components/Tooltip.tsx +212 -0
  150. package/src/context-menu.css +11 -0
  151. package/src/controls.css +137 -0
  152. package/src/hooks/useReducedMotion.ts +36 -0
  153. package/src/hooks/useToast.ts +57 -0
  154. package/src/icon-btn.css +64 -0
  155. package/src/index.ts +140 -0
  156. package/src/lib/menuPlacement.ts +31 -0
  157. package/src/lib/selectMenus.ts +303 -0
  158. package/src/lib/settingsClient.ts +377 -0
  159. package/src/lib/themeScope.ts +23 -0
  160. package/src/lib/titleTooltips.ts +236 -0
  161. package/src/lib/tooltipPlacement.ts +59 -0
  162. package/src/modal.css +129 -0
  163. package/src/number-input.css +131 -0
  164. package/src/scrollbar.css +165 -0
  165. package/src/select.css +251 -0
  166. package/src/theme-picker.css +219 -0
  167. package/src/toaster.css +159 -0
  168. package/src/tooltip.css +103 -0
  169. package/src/types/index.ts +43 -0
@@ -0,0 +1,131 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React, { useRef, useState, useEffect, useCallback } from "react";
3
+ import { Icon } from "@wolffm/themes";
4
+ /* empty css */
5
+ const REPEAT_DELAY_MS = 400;
6
+ const REPEAT_EVERY_MS = 60;
7
+ const ICON_SIZE = { sm: 12, md: 14, lg: 16 };
8
+ const NumberInput = React.forwardRef(
9
+ function NumberInput2({
10
+ size = "md",
11
+ increaseLabel = "Increase",
12
+ decreaseLabel = "Decrease",
13
+ className,
14
+ disabled,
15
+ readOnly,
16
+ onInput,
17
+ ...rest
18
+ }, ref) {
19
+ const inputRef = useRef(null);
20
+ const repeat = useRef({});
21
+ const [current, setCurrent] = useState(String(rest.value ?? rest.defaultValue ?? ""));
22
+ useEffect(() => {
23
+ if (rest.value !== void 0) setCurrent(String(rest.value));
24
+ }, [rest.value]);
25
+ const setRef = useCallback(
26
+ (node) => {
27
+ inputRef.current = node;
28
+ if (typeof ref === "function") ref(node);
29
+ else if (ref) ref.current = node;
30
+ },
31
+ [ref]
32
+ );
33
+ const stop = useCallback(() => {
34
+ clearTimeout(repeat.current.delay);
35
+ clearInterval(repeat.current.every);
36
+ repeat.current = {};
37
+ }, []);
38
+ useEffect(() => stop, [stop]);
39
+ const stepOnce = useCallback((dir) => {
40
+ const input = inputRef.current;
41
+ if (!input || input.disabled || input.readOnly) return false;
42
+ const before = input.value;
43
+ if (dir === 1) input.stepUp();
44
+ else input.stepDown();
45
+ if (input.value === before) return false;
46
+ input.dispatchEvent(new Event("input", { bubbles: true }));
47
+ input.dispatchEvent(new Event("change", { bubbles: true }));
48
+ return true;
49
+ }, []);
50
+ const start = (dir) => (e) => {
51
+ if (e.button !== 0) return;
52
+ e.preventDefault();
53
+ inputRef.current?.focus({ preventScroll: true });
54
+ stop();
55
+ if (!stepOnce(dir)) return;
56
+ repeat.current.delay = setTimeout(() => {
57
+ repeat.current.every = setInterval(() => {
58
+ if (!stepOnce(dir)) stop();
59
+ }, REPEAT_EVERY_MS);
60
+ }, REPEAT_DELAY_MS);
61
+ };
62
+ const n = current === "" ? NaN : Number(current);
63
+ const min = rest.min === void 0 || rest.min === "" ? -Infinity : Number(rest.min);
64
+ const max = rest.max === void 0 || rest.max === "" ? Infinity : Number(rest.max);
65
+ const locked = disabled || readOnly;
66
+ return /* @__PURE__ */ jsxs(
67
+ "span",
68
+ {
69
+ className: [
70
+ "hdk-number",
71
+ `hdk-number--${size}`,
72
+ disabled && "hdk-number--disabled",
73
+ className
74
+ ].filter(Boolean).join(" "),
75
+ children: [
76
+ /* @__PURE__ */ jsx(
77
+ "input",
78
+ {
79
+ ref: setRef,
80
+ type: "number",
81
+ className: "hdk-number__input",
82
+ disabled,
83
+ readOnly,
84
+ onInput: (e) => {
85
+ setCurrent(e.currentTarget.value);
86
+ onInput?.(e);
87
+ },
88
+ ...rest
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsxs("span", { className: "hdk-number__steps", children: [
92
+ /* @__PURE__ */ jsx(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ tabIndex: -1,
97
+ className: "hdk-number__step",
98
+ "aria-label": increaseLabel,
99
+ disabled: locked || n >= max,
100
+ onPointerDown: start(1),
101
+ onPointerUp: stop,
102
+ onPointerLeave: stop,
103
+ onPointerCancel: stop,
104
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-up", size: ICON_SIZE[size] })
105
+ }
106
+ ),
107
+ /* @__PURE__ */ jsx(
108
+ "button",
109
+ {
110
+ type: "button",
111
+ tabIndex: -1,
112
+ className: "hdk-number__step",
113
+ "aria-label": decreaseLabel,
114
+ disabled: locked || n <= min,
115
+ onPointerDown: start(-1),
116
+ onPointerUp: stop,
117
+ onPointerLeave: stop,
118
+ onPointerCancel: stop,
119
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: ICON_SIZE[size] })
120
+ }
121
+ )
122
+ ] })
123
+ ]
124
+ }
125
+ );
126
+ }
127
+ );
128
+ export {
129
+ NumberInput
130
+ };
131
+ //# sourceMappingURL=NumberInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberInput.js","sources":["../../src/components/NumberInput.tsx"],"sourcesContent":["/**\n * NumberInput Component\n * A themed number field with its own stepper, for the OS spinner arrows that\n * no CSS can style (controls.css removes them from a bare <input type=number>).\n *\n * It is a real <input type=\"number\"> with every input prop passed through, so\n * it works controlled (`value` + `onChange`) or uncontrolled (`defaultValue`),\n * submits in a form, and keeps native min/max/step validation and the arrow\n * keys. The buttons call the input's own stepUp()/stepDown() and then fire\n * `input` and `change`, so `onChange` sees an ordinary edit: React's value\n * tracker compares against what React last wrote, and stepUp() goes around it.\n *\n * Holding a button repeats, like the native spinner. The buttons are not tab\n * stops — the arrow keys on the field already step — but they are labelled.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { Icon } from '@wolffm/themes'\nimport '../number-input.css'\n\nexport type NumberInputSize = 'sm' | 'md' | 'lg'\n\nexport interface NumberInputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {\n size?: NumberInputSize\n /** Labels for the two buttons; default \"Increase\" / \"Decrease\". */\n increaseLabel?: string\n decreaseLabel?: string\n}\n\nconst REPEAT_DELAY_MS = 400\nconst REPEAT_EVERY_MS = 60\nconst ICON_SIZE: Record<NumberInputSize, number> = { sm: 12, md: 14, lg: 16 }\n\nexport const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(\n function NumberInput(\n {\n size = 'md',\n increaseLabel = 'Increase',\n decreaseLabel = 'Decrease',\n className,\n disabled,\n readOnly,\n onInput,\n ...rest\n },\n ref\n ) {\n const inputRef = useRef<HTMLInputElement | null>(null)\n const repeat = useRef<{\n delay?: ReturnType<typeof setTimeout>\n every?: ReturnType<typeof setInterval>\n }>({})\n // Mirrors the field so the buttons can disable at min/max, controlled or not.\n const [current, setCurrent] = useState<string>(String(rest.value ?? rest.defaultValue ?? ''))\n useEffect(() => {\n if (rest.value !== undefined) setCurrent(String(rest.value))\n }, [rest.value])\n\n const setRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n },\n [ref]\n )\n\n const stop = useCallback(() => {\n clearTimeout(repeat.current.delay)\n clearInterval(repeat.current.every)\n repeat.current = {}\n }, [])\n useEffect(() => stop, [stop])\n\n const stepOnce = useCallback((dir: 1 | -1) => {\n const input = inputRef.current\n if (!input || input.disabled || input.readOnly) return false\n const before = input.value\n if (dir === 1) input.stepUp()\n else input.stepDown()\n if (input.value === before) return false\n input.dispatchEvent(new Event('input', { bubbles: true }))\n input.dispatchEvent(new Event('change', { bubbles: true }))\n return true\n }, [])\n\n const start = (dir: 1 | -1) => (e: React.PointerEvent) => {\n if (e.button !== 0) return\n // Keep focus (and the caret) in the field, as the native spinner does.\n e.preventDefault()\n inputRef.current?.focus({ preventScroll: true })\n stop()\n if (!stepOnce(dir)) return\n repeat.current.delay = setTimeout(() => {\n repeat.current.every = setInterval(() => {\n if (!stepOnce(dir)) stop()\n }, REPEAT_EVERY_MS)\n }, REPEAT_DELAY_MS)\n }\n\n const n = current === '' ? NaN : Number(current)\n const min = rest.min === undefined || rest.min === '' ? -Infinity : Number(rest.min)\n const max = rest.max === undefined || rest.max === '' ? Infinity : Number(rest.max)\n const locked = disabled || readOnly\n\n return (\n <span\n className={[\n 'hdk-number',\n `hdk-number--${size}`,\n disabled && 'hdk-number--disabled',\n className\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <input\n ref={setRef}\n type=\"number\"\n className=\"hdk-number__input\"\n disabled={disabled}\n readOnly={readOnly}\n onInput={e => {\n setCurrent(e.currentTarget.value)\n onInput?.(e)\n }}\n {...rest}\n />\n <span className=\"hdk-number__steps\">\n <button\n type=\"button\"\n tabIndex={-1}\n className=\"hdk-number__step\"\n aria-label={increaseLabel}\n disabled={locked || n >= max}\n onPointerDown={start(1)}\n onPointerUp={stop}\n onPointerLeave={stop}\n onPointerCancel={stop}\n >\n <Icon name=\"chevron-up\" size={ICON_SIZE[size]} />\n </button>\n <button\n type=\"button\"\n tabIndex={-1}\n className=\"hdk-number__step\"\n aria-label={decreaseLabel}\n disabled={locked || n <= min}\n onPointerDown={start(-1)}\n onPointerUp={stop}\n onPointerLeave={stop}\n onPointerCancel={stop}\n >\n <Icon name=\"chevron-down\" size={ICON_SIZE[size]} />\n </button>\n </span>\n </span>\n )\n }\n)\n"],"names":["NumberInput"],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Select Component
3
+ * Themed single-select dropdown. The open list is a real ARIA listbox with
4
+ * full keyboard support, so it can replace a native <select> wherever the
5
+ * browser's own popup (unthemeable, OS-drawn) looks out of place.
6
+ *
7
+ * The list is portalled into the anchor's theme scope (see themeScopeOf) and
8
+ * positioned fixed from the trigger's rect, so it keeps the theme's tokens and
9
+ * escapes both `overflow: hidden` and transformed ancestors.
10
+ */
11
+ import React from 'react';
12
+ import { type IconName } from '@wolffm/themes';
13
+ import '../select.css';
14
+ import '../scrollbar.css';
15
+ export type SelectVariant = 'outline' | 'filled' | 'ghost' | 'underline';
16
+ export type SelectSize = 'sm' | 'md' | 'lg';
17
+ export interface SelectOption<V extends string = string> {
18
+ value: V;
19
+ /** Plain text so typeahead can match it. */
20
+ label: string;
21
+ icon?: IconName;
22
+ /** Secondary line under the label in the open list. */
23
+ description?: string;
24
+ disabled?: boolean;
25
+ }
26
+ export interface SelectProps<V extends string = string> {
27
+ value: V | null;
28
+ onChange: (value: V) => void;
29
+ options: SelectOption<V>[];
30
+ /** Shown while `value` is null. */
31
+ placeholder?: string;
32
+ variant?: SelectVariant;
33
+ size?: SelectSize;
34
+ disabled?: boolean;
35
+ /** Required when no visible <label htmlFor={id}> names the control. */
36
+ 'aria-label'?: string;
37
+ id?: string;
38
+ className?: string;
39
+ }
40
+ export declare function Select<V extends string = string>({ value, onChange, options, placeholder, variant, size, disabled, 'aria-label': ariaLabel, id, className }: SelectProps<V>): React.JSX.Element;
41
+ export interface NativeSelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
42
+ variant?: SelectVariant;
43
+ size?: SelectSize;
44
+ }
45
+ /**
46
+ * A native <select> wearing the same trigger as <Select>. Use it where the
47
+ * OS picker is the better UX — long lists on mobile, forms that must submit
48
+ * natively. The popup itself stays OS-drawn; `color-scheme` from the theme
49
+ * gets it to the right light/dark mode.
50
+ */
51
+ export declare function NativeSelect({ variant, size, className, children, ...rest }: NativeSelectProps): React.JSX.Element;
52
+ //# sourceMappingURL=Select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAA2E,MAAM,OAAO,CAAA;AAE/F,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,eAAe,CAAA;AACtB,OAAO,kBAAkB,CAAA;AAIzB,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAA;AACxE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE3C,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACrD,KAAK,EAAE,CAAC,CAAA;IACR,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACpD,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAA;IAC5B,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1B,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAID,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EAChD,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAuB,EACvB,OAAmB,EACnB,IAAW,EACX,QAAgB,EAChB,YAAY,EAAE,SAAS,EACvB,EAAE,EACF,SAAS,EACV,EAAE,WAAW,CAAC,CAAC,CAAC,qBAqQhB;AAED,MAAM,WAAW,iBACf,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACnE,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAmB,EACnB,IAAW,EACX,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,iBAAiB,qBAiBnB"}
@@ -0,0 +1,281 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useId, useState, useRef, useCallback, useLayoutEffect, useEffect } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { Icon } from "@wolffm/themes";
5
+ /* empty css */
6
+ /* empty css */
7
+ import { themeScopeOf } from "../lib/themeScope.js";
8
+ import { placeMenu } from "../lib/menuPlacement.js";
9
+ const TYPEAHEAD_RESET_MS = 600;
10
+ function Select({
11
+ value,
12
+ onChange,
13
+ options,
14
+ placeholder = "Select…",
15
+ variant = "outline",
16
+ size = "md",
17
+ disabled = false,
18
+ "aria-label": ariaLabel,
19
+ id,
20
+ className
21
+ }) {
22
+ const autoId = useId();
23
+ const triggerId = id ?? `${autoId}-trigger`;
24
+ const listId = `${autoId}-list`;
25
+ const optionId = (i) => `${autoId}-opt-${i}`;
26
+ const [open, setOpen] = useState(false);
27
+ const [active, setActive] = useState(-1);
28
+ const [position, setPosition] = useState(null);
29
+ const [scope, setScope] = useState(null);
30
+ const triggerRef = useRef(null);
31
+ const listRef = useRef(null);
32
+ const typeahead = useRef({ text: "", at: 0 });
33
+ const selectedIndex = options.findIndex((o) => o.value === value);
34
+ const selected = selectedIndex >= 0 ? options[selectedIndex] : null;
35
+ const step = useCallback(
36
+ (from, dir) => {
37
+ for (let i = 1; i <= options.length; i++) {
38
+ const next = (from + dir * i + options.length) % options.length;
39
+ if (!options[next].disabled) return next;
40
+ }
41
+ return from;
42
+ },
43
+ [options]
44
+ );
45
+ const edge = useCallback(
46
+ (dir) => step(dir === 1 ? -1 : options.length, dir),
47
+ [options.length, step]
48
+ );
49
+ const openMenu = (start) => {
50
+ if (disabled || options.length === 0) return;
51
+ let initial = selectedIndex;
52
+ if (start === "first" || initial < 0) initial = edge(1);
53
+ if (start === "last") initial = edge(-1);
54
+ setActive(initial);
55
+ setScope(themeScopeOf(triggerRef.current));
56
+ setOpen(true);
57
+ };
58
+ const closeMenu = (refocus) => {
59
+ setOpen(false);
60
+ setPosition(null);
61
+ if (refocus) triggerRef.current?.focus();
62
+ };
63
+ const choose = (index) => {
64
+ const option = options[index];
65
+ if (!option || option.disabled) return;
66
+ onChange(option.value);
67
+ closeMenu(true);
68
+ };
69
+ const place = useCallback(() => {
70
+ const trigger = triggerRef.current;
71
+ if (!trigger) return;
72
+ setPosition(placeMenu(trigger.getBoundingClientRect(), listRef.current?.scrollHeight ?? 0));
73
+ }, []);
74
+ useLayoutEffect(() => {
75
+ if (open && scope) place();
76
+ }, [open, scope, place]);
77
+ const placed = position !== null;
78
+ useLayoutEffect(() => {
79
+ if (open && placed) listRef.current?.focus({ preventScroll: true });
80
+ }, [open, placed]);
81
+ useEffect(() => {
82
+ if (!open) return;
83
+ const onPointerDown = (e) => {
84
+ const target = e.target;
85
+ if (triggerRef.current?.contains(target) || listRef.current?.contains(target)) return;
86
+ closeMenu(false);
87
+ };
88
+ window.addEventListener("scroll", place, true);
89
+ window.addEventListener("resize", place);
90
+ document.addEventListener("pointerdown", onPointerDown);
91
+ return () => {
92
+ window.removeEventListener("scroll", place, true);
93
+ window.removeEventListener("resize", place);
94
+ document.removeEventListener("pointerdown", onPointerDown);
95
+ };
96
+ }, [open, place]);
97
+ useEffect(() => {
98
+ if (!open || active < 0) return;
99
+ document.getElementById(`${autoId}-opt-${active}`)?.scrollIntoView({ block: "nearest" });
100
+ }, [open, active, autoId]);
101
+ const matchTypeahead = (key) => {
102
+ const now = Date.now();
103
+ const t = typeahead.current;
104
+ t.text = now - t.at > TYPEAHEAD_RESET_MS ? key : t.text + key;
105
+ t.at = now;
106
+ const needle = t.text.toLowerCase();
107
+ const start = Math.max(active, 0);
108
+ for (let i = 0; i < options.length; i++) {
109
+ const offset = t.text.length === 1 ? i + 1 : i;
110
+ const idx = (start + offset) % options.length;
111
+ const o = options[idx];
112
+ if (!o.disabled && o.label.toLowerCase().startsWith(needle)) return idx;
113
+ }
114
+ return -1;
115
+ };
116
+ const onTriggerKeyDown = (e) => {
117
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
118
+ e.preventDefault();
119
+ openMenu();
120
+ } else if (e.key === "ArrowUp") {
121
+ e.preventDefault();
122
+ openMenu("last");
123
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
124
+ const idx = matchTypeahead(e.key);
125
+ if (idx >= 0) onChange(options[idx].value);
126
+ }
127
+ };
128
+ const onListKeyDown = (e) => {
129
+ switch (e.key) {
130
+ case "ArrowDown":
131
+ e.preventDefault();
132
+ setActive((a) => step(a, 1));
133
+ break;
134
+ case "ArrowUp":
135
+ e.preventDefault();
136
+ setActive((a) => step(a, -1));
137
+ break;
138
+ case "Home":
139
+ e.preventDefault();
140
+ setActive(edge(1));
141
+ break;
142
+ case "End":
143
+ e.preventDefault();
144
+ setActive(edge(-1));
145
+ break;
146
+ case "Enter":
147
+ case " ":
148
+ e.preventDefault();
149
+ choose(active);
150
+ break;
151
+ case "Escape":
152
+ e.preventDefault();
153
+ e.stopPropagation();
154
+ closeMenu(true);
155
+ break;
156
+ case "Tab":
157
+ closeMenu(false);
158
+ break;
159
+ default:
160
+ if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
161
+ const idx = matchTypeahead(e.key);
162
+ if (idx >= 0) setActive(idx);
163
+ }
164
+ }
165
+ };
166
+ const style = position ? {
167
+ left: position.left,
168
+ minWidth: position.width,
169
+ top: position.top,
170
+ bottom: position.bottom,
171
+ maxHeight: position.maxHeight
172
+ } : { visibility: "hidden" };
173
+ const iconSize = size === "lg" ? 18 : size === "sm" ? 14 : 16;
174
+ return /* @__PURE__ */ jsxs(
175
+ "div",
176
+ {
177
+ className: [
178
+ "hdk-select",
179
+ `hdk-select--${variant}`,
180
+ `hdk-select--${size}`,
181
+ open && "hdk-select--open",
182
+ className
183
+ ].filter(Boolean).join(" "),
184
+ children: [
185
+ /* @__PURE__ */ jsxs(
186
+ "button",
187
+ {
188
+ ref: triggerRef,
189
+ id: triggerId,
190
+ type: "button",
191
+ className: "hdk-select__trigger",
192
+ "aria-haspopup": "listbox",
193
+ "aria-expanded": open,
194
+ "aria-controls": open ? listId : void 0,
195
+ "aria-label": ariaLabel,
196
+ disabled,
197
+ onClick: () => open ? closeMenu(true) : openMenu(),
198
+ onKeyDown: onTriggerKeyDown,
199
+ children: [
200
+ selected?.icon && /* @__PURE__ */ jsx(Icon, { name: selected.icon, size: iconSize }),
201
+ /* @__PURE__ */ jsx("span", { className: `hdk-select__value ${selected ? "" : "hdk-select__value--placeholder"}`, children: selected ? selected.label : placeholder }),
202
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: iconSize, className: "hdk-select__chevron" })
203
+ ]
204
+ }
205
+ ),
206
+ open && scope && createPortal(
207
+ /* @__PURE__ */ jsx(
208
+ "ul",
209
+ {
210
+ ref: listRef,
211
+ id: listId,
212
+ role: "listbox",
213
+ tabIndex: -1,
214
+ "aria-labelledby": triggerId,
215
+ "aria-activedescendant": active >= 0 ? optionId(active) : void 0,
216
+ className: "hdk-select__menu hdk-scroll hdk-scroll--thin",
217
+ style,
218
+ onKeyDown: onListKeyDown,
219
+ children: options.map((o, i) => /* @__PURE__ */ jsxs(
220
+ "li",
221
+ {
222
+ id: optionId(i),
223
+ role: "option",
224
+ "aria-selected": i === selectedIndex,
225
+ "aria-disabled": o.disabled || void 0,
226
+ className: [
227
+ "hdk-select__option",
228
+ i === active && "hdk-select__option--active",
229
+ i === selectedIndex && "hdk-select__option--selected"
230
+ ].filter(Boolean).join(" "),
231
+ onPointerMove: () => !o.disabled && i !== active && setActive(i),
232
+ onClick: () => choose(i),
233
+ children: [
234
+ o.icon && /* @__PURE__ */ jsx(Icon, { name: o.icon, size: iconSize }),
235
+ /* @__PURE__ */ jsxs("span", { className: "hdk-select__option-text", children: [
236
+ /* @__PURE__ */ jsx("span", { className: "hdk-select__option-label", children: o.label }),
237
+ o.description && /* @__PURE__ */ jsx("span", { className: "hdk-select__option-desc", children: o.description })
238
+ ] }),
239
+ i === selectedIndex && /* @__PURE__ */ jsx(Icon, { name: "check", size: iconSize, className: "hdk-select__check" })
240
+ ]
241
+ },
242
+ o.value
243
+ ))
244
+ }
245
+ ),
246
+ scope
247
+ )
248
+ ]
249
+ }
250
+ );
251
+ }
252
+ function NativeSelect({
253
+ variant = "outline",
254
+ size = "md",
255
+ className,
256
+ children,
257
+ ...rest
258
+ }) {
259
+ return /* @__PURE__ */ jsxs(
260
+ "span",
261
+ {
262
+ className: ["hdk-select", `hdk-select--${variant}`, `hdk-select--${size}`, className].filter(Boolean).join(" "),
263
+ children: [
264
+ /* @__PURE__ */ jsx("select", { className: "hdk-select__trigger hdk-select__native", ...rest, children }),
265
+ /* @__PURE__ */ jsx(
266
+ Icon,
267
+ {
268
+ name: "chevron-down",
269
+ size: size === "lg" ? 18 : size === "sm" ? 14 : 16,
270
+ className: "hdk-select__chevron hdk-select__chevron--native"
271
+ }
272
+ )
273
+ ]
274
+ }
275
+ );
276
+ }
277
+ export {
278
+ NativeSelect,
279
+ Select
280
+ };
281
+ //# sourceMappingURL=Select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.js","sources":["../../src/components/Select.tsx"],"sourcesContent":["/**\n * Select Component\n * Themed single-select dropdown. The open list is a real ARIA listbox with\n * full keyboard support, so it can replace a native <select> wherever the\n * browser's own popup (unthemeable, OS-drawn) looks out of place.\n *\n * The list is portalled into the anchor's theme scope (see themeScopeOf) and\n * positioned fixed from the trigger's rect, so it keeps the theme's tokens and\n * escapes both `overflow: hidden` and transformed ancestors.\n */\n\nimport React, { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { Icon, type IconName } from '@wolffm/themes'\nimport '../select.css'\nimport '../scrollbar.css'\nimport { themeScopeOf } from '../lib/themeScope'\nimport { placeMenu, type MenuPosition } from '../lib/menuPlacement'\n\nexport type SelectVariant = 'outline' | 'filled' | 'ghost' | 'underline'\nexport type SelectSize = 'sm' | 'md' | 'lg'\n\nexport interface SelectOption<V extends string = string> {\n value: V\n /** Plain text so typeahead can match it. */\n label: string\n icon?: IconName\n /** Secondary line under the label in the open list. */\n description?: string\n disabled?: boolean\n}\n\nexport interface SelectProps<V extends string = string> {\n value: V | null\n onChange: (value: V) => void\n options: SelectOption<V>[]\n /** Shown while `value` is null. */\n placeholder?: string\n variant?: SelectVariant\n size?: SelectSize\n disabled?: boolean\n /** Required when no visible <label htmlFor={id}> names the control. */\n 'aria-label'?: string\n id?: string\n className?: string\n}\n\nconst TYPEAHEAD_RESET_MS = 600\n\nexport function Select<V extends string = string>({\n value,\n onChange,\n options,\n placeholder = 'Select…',\n variant = 'outline',\n size = 'md',\n disabled = false,\n 'aria-label': ariaLabel,\n id,\n className\n}: SelectProps<V>) {\n const autoId = useId()\n const triggerId = id ?? `${autoId}-trigger`\n const listId = `${autoId}-list`\n const optionId = (i: number) => `${autoId}-opt-${i}`\n\n const [open, setOpen] = useState(false)\n const [active, setActive] = useState(-1)\n const [position, setPosition] = useState<MenuPosition | null>(null)\n const [scope, setScope] = useState<HTMLElement | null>(null)\n const triggerRef = useRef<HTMLButtonElement>(null)\n const listRef = useRef<HTMLUListElement>(null)\n const typeahead = useRef({ text: '', at: 0 })\n\n const selectedIndex = options.findIndex(o => o.value === value)\n const selected = selectedIndex >= 0 ? options[selectedIndex] : null\n\n const step = useCallback(\n (from: number, dir: 1 | -1) => {\n for (let i = 1; i <= options.length; i++) {\n const next = (from + dir * i + options.length) % options.length\n if (!options[next].disabled) return next\n }\n return from\n },\n [options]\n )\n\n const edge = useCallback(\n (dir: 1 | -1) => step(dir === 1 ? -1 : options.length, dir),\n [options.length, step]\n )\n\n const openMenu = (start?: 'first' | 'last') => {\n if (disabled || options.length === 0) return\n let initial = selectedIndex\n if (start === 'first' || initial < 0) initial = edge(1)\n if (start === 'last') initial = edge(-1)\n setActive(initial)\n setScope(themeScopeOf(triggerRef.current))\n setOpen(true)\n }\n\n const closeMenu = (refocus: boolean) => {\n setOpen(false)\n setPosition(null)\n if (refocus) triggerRef.current?.focus()\n }\n\n const choose = (index: number) => {\n const option = options[index]\n if (!option || option.disabled) return\n onChange(option.value)\n closeMenu(true)\n }\n\n const place = useCallback(() => {\n const trigger = triggerRef.current\n if (!trigger) return\n setPosition(placeMenu(trigger.getBoundingClientRect(), listRef.current?.scrollHeight ?? 0))\n }, [])\n\n // Measure once the list exists, then follow the trigger while open.\n useLayoutEffect(() => {\n if (open && scope) place()\n }, [open, scope, place])\n\n // Focus only once placed: until then the list is visibility: hidden, and a\n // hidden element silently refuses focus — keys would stay on the trigger.\n const placed = position !== null\n useLayoutEffect(() => {\n if (open && placed) listRef.current?.focus({ preventScroll: true })\n }, [open, placed])\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (e: PointerEvent) => {\n const target = e.target as Node\n if (triggerRef.current?.contains(target) || listRef.current?.contains(target)) return\n closeMenu(false)\n }\n // Capture, so scrolling ANY ancestor re-places the list.\n window.addEventListener('scroll', place, true)\n window.addEventListener('resize', place)\n document.addEventListener('pointerdown', onPointerDown)\n return () => {\n window.removeEventListener('scroll', place, true)\n window.removeEventListener('resize', place)\n document.removeEventListener('pointerdown', onPointerDown)\n }\n }, [open, place])\n\n // Keep the active option in view as the keyboard moves it.\n useEffect(() => {\n if (!open || active < 0) return\n document.getElementById(`${autoId}-opt-${active}`)?.scrollIntoView({ block: 'nearest' })\n }, [open, active, autoId])\n\n const matchTypeahead = (key: string) => {\n const now = Date.now()\n const t = typeahead.current\n t.text = now - t.at > TYPEAHEAD_RESET_MS ? key : t.text + key\n t.at = now\n const needle = t.text.toLowerCase()\n const start = Math.max(active, 0)\n for (let i = 0; i < options.length; i++) {\n // A single repeated key cycles; a longer prefix stays on the current match.\n const offset = t.text.length === 1 ? i + 1 : i\n const idx = (start + offset) % options.length\n const o = options[idx]\n if (!o.disabled && o.label.toLowerCase().startsWith(needle)) return idx\n }\n return -1\n }\n\n const onTriggerKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openMenu()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n openMenu('last')\n } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n // Typeahead on a closed select changes the value, like a native one.\n const idx = matchTypeahead(e.key)\n if (idx >= 0) onChange(options[idx].value)\n }\n }\n\n const onListKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n setActive(a => step(a, 1))\n break\n case 'ArrowUp':\n e.preventDefault()\n setActive(a => step(a, -1))\n break\n case 'Home':\n e.preventDefault()\n setActive(edge(1))\n break\n case 'End':\n e.preventDefault()\n setActive(edge(-1))\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n choose(active)\n break\n case 'Escape':\n // Stop here so an enclosing Modal does not close along with the list.\n e.preventDefault()\n e.stopPropagation()\n closeMenu(true)\n break\n case 'Tab':\n closeMenu(false)\n break\n default:\n if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n const idx = matchTypeahead(e.key)\n if (idx >= 0) setActive(idx)\n }\n }\n }\n\n const style: React.CSSProperties | undefined = position\n ? {\n left: position.left,\n minWidth: position.width,\n top: position.top,\n bottom: position.bottom,\n maxHeight: position.maxHeight\n }\n : { visibility: 'hidden' }\n\n const iconSize = size === 'lg' ? 18 : size === 'sm' ? 14 : 16\n\n return (\n <div\n className={[\n 'hdk-select',\n `hdk-select--${variant}`,\n `hdk-select--${size}`,\n open && 'hdk-select--open',\n className\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <button\n ref={triggerRef}\n id={triggerId}\n type=\"button\"\n className=\"hdk-select__trigger\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={open ? listId : undefined}\n aria-label={ariaLabel}\n disabled={disabled}\n onClick={() => (open ? closeMenu(true) : openMenu())}\n onKeyDown={onTriggerKeyDown}\n >\n {selected?.icon && <Icon name={selected.icon} size={iconSize} />}\n <span className={`hdk-select__value ${selected ? '' : 'hdk-select__value--placeholder'}`}>\n {selected ? selected.label : placeholder}\n </span>\n <Icon name=\"chevron-down\" size={iconSize} className=\"hdk-select__chevron\" />\n </button>\n\n {open &&\n scope &&\n createPortal(\n <ul\n ref={listRef}\n id={listId}\n role=\"listbox\"\n tabIndex={-1}\n aria-labelledby={triggerId}\n aria-activedescendant={active >= 0 ? optionId(active) : undefined}\n className=\"hdk-select__menu hdk-scroll hdk-scroll--thin\"\n style={style}\n onKeyDown={onListKeyDown}\n >\n {options.map((o, i) => (\n <li\n key={o.value}\n id={optionId(i)}\n role=\"option\"\n aria-selected={i === selectedIndex}\n aria-disabled={o.disabled || undefined}\n className={[\n 'hdk-select__option',\n i === active && 'hdk-select__option--active',\n i === selectedIndex && 'hdk-select__option--selected'\n ]\n .filter(Boolean)\n .join(' ')}\n onPointerMove={() => !o.disabled && i !== active && setActive(i)}\n onClick={() => choose(i)}\n >\n {o.icon && <Icon name={o.icon} size={iconSize} />}\n <span className=\"hdk-select__option-text\">\n <span className=\"hdk-select__option-label\">{o.label}</span>\n {o.description && (\n <span className=\"hdk-select__option-desc\">{o.description}</span>\n )}\n </span>\n {i === selectedIndex && (\n <Icon name=\"check\" size={iconSize} className=\"hdk-select__check\" />\n )}\n </li>\n ))}\n </ul>,\n scope\n )}\n </div>\n )\n}\n\nexport interface NativeSelectProps\n extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {\n variant?: SelectVariant\n size?: SelectSize\n}\n\n/**\n * A native <select> wearing the same trigger as <Select>. Use it where the\n * OS picker is the better UX — long lists on mobile, forms that must submit\n * natively. The popup itself stays OS-drawn; `color-scheme` from the theme\n * gets it to the right light/dark mode.\n */\nexport function NativeSelect({\n variant = 'outline',\n size = 'md',\n className,\n children,\n ...rest\n}: NativeSelectProps) {\n return (\n <span\n className={['hdk-select', `hdk-select--${variant}`, `hdk-select--${size}`, className]\n .filter(Boolean)\n .join(' ')}\n >\n <select className=\"hdk-select__trigger hdk-select__native\" {...rest}>\n {children}\n </select>\n <Icon\n name=\"chevron-down\"\n size={size === 'lg' ? 18 : size === 'sm' ? 14 : 16}\n className=\"hdk-select__chevron hdk-select__chevron--native\"\n />\n </span>\n )\n}\n"],"names":[],"mappings":";;;;;;;;AA+CA,MAAM,qBAAqB;AAEpB,SAAS,OAAkC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,SAAS,MAAA;AACf,QAAM,YAAY,MAAM,GAAG,MAAM;AACjC,QAAM,SAAS,GAAG,MAAM;AACxB,QAAM,WAAW,CAAC,MAAc,GAAG,MAAM,QAAQ,CAAC;AAElD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE;AACvC,QAAM,CAAC,UAAU,WAAW,IAAI,SAA8B,IAAI;AAClE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6B,IAAI;AAC3D,QAAM,aAAa,OAA0B,IAAI;AACjD,QAAM,UAAU,OAAyB,IAAI;AAC7C,QAAM,YAAY,OAAO,EAAE,MAAM,IAAI,IAAI,GAAG;AAE5C,QAAM,gBAAgB,QAAQ,UAAU,CAAA,MAAK,EAAE,UAAU,KAAK;AAC9D,QAAM,WAAW,iBAAiB,IAAI,QAAQ,aAAa,IAAI;AAE/D,QAAM,OAAO;AAAA,IACX,CAAC,MAAc,QAAgB;AAC7B,eAAS,IAAI,GAAG,KAAK,QAAQ,QAAQ,KAAK;AACxC,cAAM,QAAQ,OAAO,MAAM,IAAI,QAAQ,UAAU,QAAQ;AACzD,YAAI,CAAC,QAAQ,IAAI,EAAE,SAAU,QAAO;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,OAAO;AAAA,EAAA;AAGV,QAAM,OAAO;AAAA,IACX,CAAC,QAAgB,KAAK,QAAQ,IAAI,KAAK,QAAQ,QAAQ,GAAG;AAAA,IAC1D,CAAC,QAAQ,QAAQ,IAAI;AAAA,EAAA;AAGvB,QAAM,WAAW,CAAC,UAA6B;AAC7C,QAAI,YAAY,QAAQ,WAAW,EAAG;AACtC,QAAI,UAAU;AACd,QAAI,UAAU,WAAW,UAAU,EAAG,WAAU,KAAK,CAAC;AACtD,QAAI,UAAU,OAAQ,WAAU,KAAK,EAAE;AACvC,cAAU,OAAO;AACjB,aAAS,aAAa,WAAW,OAAO,CAAC;AACzC,YAAQ,IAAI;AAAA,EACd;AAEA,QAAM,YAAY,CAAC,YAAqB;AACtC,YAAQ,KAAK;AACb,gBAAY,IAAI;AAChB,QAAI,QAAS,YAAW,SAAS,MAAA;AAAA,EACnC;AAEA,QAAM,SAAS,CAAC,UAAkB;AAChC,UAAM,SAAS,QAAQ,KAAK;AAC5B,QAAI,CAAC,UAAU,OAAO,SAAU;AAChC,aAAS,OAAO,KAAK;AACrB,cAAU,IAAI;AAAA,EAChB;AAEA,QAAM,QAAQ,YAAY,MAAM;AAC9B,UAAM,UAAU,WAAW;AAC3B,QAAI,CAAC,QAAS;AACd,gBAAY,UAAU,QAAQ,sBAAA,GAAyB,QAAQ,SAAS,gBAAgB,CAAC,CAAC;AAAA,EAC5F,GAAG,CAAA,CAAE;AAGL,kBAAgB,MAAM;AACpB,QAAI,QAAQ,MAAO,OAAA;AAAA,EACrB,GAAG,CAAC,MAAM,OAAO,KAAK,CAAC;AAIvB,QAAM,SAAS,aAAa;AAC5B,kBAAgB,MAAM;AACpB,QAAI,QAAQ,OAAQ,SAAQ,SAAS,MAAM,EAAE,eAAe,MAAM;AAAA,EACpE,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,gBAAgB,CAAC,MAAoB;AACzC,YAAM,SAAS,EAAE;AACjB,UAAI,WAAW,SAAS,SAAS,MAAM,KAAK,QAAQ,SAAS,SAAS,MAAM,EAAG;AAC/E,gBAAU,KAAK;AAAA,IACjB;AAEA,WAAO,iBAAiB,UAAU,OAAO,IAAI;AAC7C,WAAO,iBAAiB,UAAU,KAAK;AACvC,aAAS,iBAAiB,eAAe,aAAa;AACtD,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,OAAO,IAAI;AAChD,aAAO,oBAAoB,UAAU,KAAK;AAC1C,eAAS,oBAAoB,eAAe,aAAa;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,MAAM,KAAK,CAAC;AAGhB,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,SAAS,EAAG;AACzB,aAAS,eAAe,GAAG,MAAM,QAAQ,MAAM,EAAE,GAAG,eAAe,EAAE,OAAO,UAAA,CAAW;AAAA,EACzF,GAAG,CAAC,MAAM,QAAQ,MAAM,CAAC;AAEzB,QAAM,iBAAiB,CAAC,QAAgB;AACtC,UAAM,MAAM,KAAK,IAAA;AACjB,UAAM,IAAI,UAAU;AACpB,MAAE,OAAO,MAAM,EAAE,KAAK,qBAAqB,MAAM,EAAE,OAAO;AAC1D,MAAE,KAAK;AACP,UAAM,SAAS,EAAE,KAAK,YAAA;AACtB,UAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AAChC,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AAEvC,YAAM,SAAS,EAAE,KAAK,WAAW,IAAI,IAAI,IAAI;AAC7C,YAAM,OAAO,QAAQ,UAAU,QAAQ;AACvC,YAAM,IAAI,QAAQ,GAAG;AACrB,UAAI,CAAC,EAAE,YAAY,EAAE,MAAM,cAAc,WAAW,MAAM,EAAG,QAAO;AAAA,IACtE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,CAAC,MAA2B;AACnD,QAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AAC/D,QAAE,eAAA;AACF,eAAA;AAAA,IACF,WAAW,EAAE,QAAQ,WAAW;AAC9B,QAAE,eAAA;AACF,eAAS,MAAM;AAAA,IACjB,WAAW,EAAE,IAAI,WAAW,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ;AAEtE,YAAM,MAAM,eAAe,EAAE,GAAG;AAChC,UAAI,OAAO,EAAG,UAAS,QAAQ,GAAG,EAAE,KAAK;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,MAA2B;AAChD,YAAQ,EAAE,KAAA;AAAA,MACR,KAAK;AACH,UAAE,eAAA;AACF,kBAAU,CAAA,MAAK,KAAK,GAAG,CAAC,CAAC;AACzB;AAAA,MACF,KAAK;AACH,UAAE,eAAA;AACF,kBAAU,CAAA,MAAK,KAAK,GAAG,EAAE,CAAC;AAC1B;AAAA,MACF,KAAK;AACH,UAAE,eAAA;AACF,kBAAU,KAAK,CAAC,CAAC;AACjB;AAAA,MACF,KAAK;AACH,UAAE,eAAA;AACF,kBAAU,KAAK,EAAE,CAAC;AAClB;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,UAAE,eAAA;AACF,eAAO,MAAM;AACb;AAAA,MACF,KAAK;AAEH,UAAE,eAAA;AACF,UAAE,gBAAA;AACF,kBAAU,IAAI;AACd;AAAA,MACF,KAAK;AACH,kBAAU,KAAK;AACf;AAAA,MACF;AACE,YAAI,EAAE,IAAI,WAAW,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ;AAC/D,gBAAM,MAAM,eAAe,EAAE,GAAG;AAChC,cAAI,OAAO,EAAG,WAAU,GAAG;AAAA,QAC7B;AAAA,IAAA;AAAA,EAEN;AAEA,QAAM,QAAyC,WAC3C;AAAA,IACE,MAAM,SAAS;AAAA,IACf,UAAU,SAAS;AAAA,IACnB,KAAK,SAAS;AAAA,IACd,QAAQ,SAAS;AAAA,IACjB,WAAW,SAAS;AAAA,EAAA,IAEtB,EAAE,YAAY,SAAA;AAElB,QAAM,WAAW,SAAS,OAAO,KAAK,SAAS,OAAO,KAAK;AAE3D,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,eAAe,OAAO;AAAA,QACtB,eAAe,IAAI;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,MAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,IAAI;AAAA,YACJ,MAAK;AAAA,YACL,WAAU;AAAA,YACV,iBAAc;AAAA,YACd,iBAAe;AAAA,YACf,iBAAe,OAAO,SAAS;AAAA,YAC/B,cAAY;AAAA,YACZ;AAAA,YACA,SAAS,MAAO,OAAO,UAAU,IAAI,IAAI,SAAA;AAAA,YACzC,WAAW;AAAA,YAEV,UAAA;AAAA,cAAA,UAAU,QAAQ,oBAAC,MAAA,EAAK,MAAM,SAAS,MAAM,MAAM,UAAU;AAAA,cAC9D,oBAAC,QAAA,EAAK,WAAW,qBAAqB,WAAW,KAAK,gCAAgC,IACnF,UAAA,WAAW,SAAS,QAAQ,YAAA,CAC/B;AAAA,kCACC,MAAA,EAAK,MAAK,gBAAe,MAAM,UAAU,WAAU,sBAAA,CAAsB;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAG3E,QACC,SACA;AAAA,UACE;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,KAAK;AAAA,cACL,IAAI;AAAA,cACJ,MAAK;AAAA,cACL,UAAU;AAAA,cACV,mBAAiB;AAAA,cACjB,yBAAuB,UAAU,IAAI,SAAS,MAAM,IAAI;AAAA,cACxD,WAAU;AAAA,cACV;AAAA,cACA,WAAW;AAAA,cAEV,UAAA,QAAQ,IAAI,CAAC,GAAG,MACf;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAEC,IAAI,SAAS,CAAC;AAAA,kBACd,MAAK;AAAA,kBACL,iBAAe,MAAM;AAAA,kBACrB,iBAAe,EAAE,YAAY;AAAA,kBAC7B,WAAW;AAAA,oBACT;AAAA,oBACA,MAAM,UAAU;AAAA,oBAChB,MAAM,iBAAiB;AAAA,kBAAA,EAEtB,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,kBACX,eAAe,MAAM,CAAC,EAAE,YAAY,MAAM,UAAU,UAAU,CAAC;AAAA,kBAC/D,SAAS,MAAM,OAAO,CAAC;AAAA,kBAEtB,UAAA;AAAA,oBAAA,EAAE,QAAQ,oBAAC,MAAA,EAAK,MAAM,EAAE,MAAM,MAAM,UAAU;AAAA,oBAC/C,qBAAC,QAAA,EAAK,WAAU,2BACd,UAAA;AAAA,sBAAA,oBAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,EAAE,OAAM;AAAA,sBACnD,EAAE,eACD,oBAAC,UAAK,WAAU,2BAA2B,YAAE,YAAA,CAAY;AAAA,oBAAA,GAE7D;AAAA,oBACC,MAAM,iBACL,oBAAC,MAAA,EAAK,MAAK,SAAQ,MAAM,UAAU,WAAU,oBAAA,CAAoB;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAvB9D,EAAE;AAAA,cAAA,CA0BV;AAAA,YAAA;AAAA,UAAA;AAAA,UAEH;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGR;AAcO,SAAS,aAAa;AAAA,EAC3B,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,CAAC,cAAc,eAAe,OAAO,IAAI,eAAe,IAAI,IAAI,SAAS,EACjF,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX,UAAA;AAAA,QAAA,oBAAC,UAAA,EAAO,WAAU,0CAA0C,GAAG,MAC5D,UACH;AAAA,QACA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,MAAM,SAAS,OAAO,KAAK,SAAS,OAAO,KAAK;AAAA,YAChD,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,MACZ;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * SettingsModal Component
3
+ * Generic settings dialog that can be customized with sections
4
+ */
5
+ import React from 'react';
6
+ export interface SettingsSection {
7
+ id: string;
8
+ title: string;
9
+ fields: SettingsField[];
10
+ }
11
+ export type SettingsField = SettingsToggleField | SettingsTextInputField | SettingsPasswordField | SettingsSelectField | SettingsButtonField | SettingsCustomField;
12
+ export interface BaseSettingsField {
13
+ id: string;
14
+ label: string;
15
+ description?: string;
16
+ disabled?: boolean;
17
+ }
18
+ export interface SettingsToggleField extends BaseSettingsField {
19
+ type: 'toggle';
20
+ value: boolean;
21
+ onChange: (value: boolean) => void;
22
+ }
23
+ export interface SettingsTextInputField extends BaseSettingsField {
24
+ type: 'text';
25
+ value: string;
26
+ onChange: (value: string) => void;
27
+ placeholder?: string;
28
+ }
29
+ export interface SettingsPasswordField extends BaseSettingsField {
30
+ type: 'password';
31
+ value: string;
32
+ onChange: (value: string) => void;
33
+ placeholder?: string;
34
+ showButton?: boolean;
35
+ buttonLabel?: string;
36
+ onButtonClick?: () => void | Promise<void>;
37
+ buttonDisabled?: boolean;
38
+ error?: string | null;
39
+ autoComplete?: string;
40
+ }
41
+ export interface SettingsSelectField extends BaseSettingsField {
42
+ type: 'select';
43
+ value: string;
44
+ onChange: (value: string) => void;
45
+ options: Array<{
46
+ value: string;
47
+ label: string;
48
+ }>;
49
+ }
50
+ export interface SettingsButtonField extends BaseSettingsField {
51
+ type: 'button';
52
+ buttonLabel: string;
53
+ onClick: () => void | Promise<void>;
54
+ variant?: 'primary' | 'danger' | 'default';
55
+ }
56
+ export interface SettingsCustomField extends BaseSettingsField {
57
+ type: 'custom';
58
+ render: () => React.ReactNode;
59
+ }
60
+ export interface SettingsModalProps {
61
+ isOpen: boolean;
62
+ title?: string;
63
+ sections: SettingsSection[];
64
+ onClose: () => void;
65
+ className?: string;
66
+ showCloseButton?: boolean;
67
+ }
68
+ export declare function SettingsModal({ isOpen, title, sections, onClose, className, showCloseButton }: SettingsModalProps): React.JSX.Element;
69
+ //# sourceMappingURL=SettingsModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsModal.d.ts","sourceRoot":"","sources":["../../src/components/SettingsModal.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,aAAa,EAAE,CAAA;CACxB;AAED,MAAM,MAAM,aAAa,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,CAAA;AAEvB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACnC;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,IAAI,EAAE,QAAQ,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAA;CAC3C;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,KAAkB,EAClB,QAAQ,EACR,OAAO,EACP,SAAc,EACd,eAAsB,EACvB,EAAE,kBAAkB,qBA+IpB"}