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

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 +368 -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 +75 -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 +144 -0
  102. package/dist/lib/settingsClient.d.ts.map +1 -0
  103. package/dist/lib/settingsClient.js +157 -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 +545 -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 +153 -0
  156. package/src/lib/menuPlacement.ts +31 -0
  157. package/src/lib/selectMenus.ts +303 -0
  158. package/src/lib/settingsClient.ts +425 -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,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":"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"}
@@ -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"}
@@ -0,0 +1,141 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Modal } from "./Modal.js";
3
+ import { Select } from "./Select.js";
4
+ function SettingsModal({
5
+ isOpen,
6
+ title = "Settings",
7
+ sections,
8
+ onClose,
9
+ className = "",
10
+ showCloseButton = true
11
+ }) {
12
+ const renderField = (field) => {
13
+ switch (field.type) {
14
+ case "toggle":
15
+ return /* @__PURE__ */ jsxs("div", { className: "settings-field", children: [
16
+ /* @__PURE__ */ jsxs("div", { className: "settings-field-header", children: [
17
+ /* @__PURE__ */ jsx("label", { className: "settings-field-label", htmlFor: field.id, children: field.label }),
18
+ /* @__PURE__ */ jsx(
19
+ "input",
20
+ {
21
+ type: "checkbox",
22
+ id: field.id,
23
+ className: "settings-toggle",
24
+ checked: field.value,
25
+ onChange: (e) => field.onChange(e.target.checked),
26
+ disabled: field.disabled
27
+ }
28
+ )
29
+ ] }),
30
+ field.description && /* @__PURE__ */ jsx("p", { className: "settings-field-description", children: field.description })
31
+ ] }, field.id);
32
+ case "text":
33
+ return /* @__PURE__ */ jsxs("div", { className: "settings-field", children: [
34
+ /* @__PURE__ */ jsx("label", { className: "settings-field-label", htmlFor: field.id, children: field.label }),
35
+ field.description && /* @__PURE__ */ jsx("p", { className: "settings-field-description", children: field.description }),
36
+ /* @__PURE__ */ jsx(
37
+ "input",
38
+ {
39
+ type: "text",
40
+ id: field.id,
41
+ className: "settings-text-input",
42
+ value: field.value,
43
+ onChange: (e) => field.onChange(e.target.value),
44
+ placeholder: field.placeholder,
45
+ disabled: field.disabled
46
+ }
47
+ )
48
+ ] }, field.id);
49
+ case "password":
50
+ return /* @__PURE__ */ jsxs("div", { className: "settings-field", children: [
51
+ /* @__PURE__ */ jsx("label", { className: "settings-field-label", htmlFor: field.id, children: field.label }),
52
+ field.description && /* @__PURE__ */ jsx("p", { className: "settings-field-description", children: field.description }),
53
+ /* @__PURE__ */ jsxs("div", { className: "settings-field-input-group", children: [
54
+ /* @__PURE__ */ jsx(
55
+ "input",
56
+ {
57
+ type: "password",
58
+ id: field.id,
59
+ name: field.autoComplete || field.id,
60
+ autoComplete: field.autoComplete || "off",
61
+ className: "settings-text-input",
62
+ value: field.value,
63
+ onChange: (e) => field.onChange(e.target.value),
64
+ placeholder: field.placeholder,
65
+ disabled: field.disabled
66
+ }
67
+ ),
68
+ field.showButton && field.onButtonClick && /* @__PURE__ */ jsx(
69
+ "button",
70
+ {
71
+ className: "settings-button",
72
+ onClick: field.onButtonClick,
73
+ disabled: field.buttonDisabled || field.disabled,
74
+ children: field.buttonLabel || "Submit"
75
+ }
76
+ )
77
+ ] }),
78
+ field.error && /* @__PURE__ */ jsx("p", { className: "settings-field-error", children: field.error })
79
+ ] }, field.id);
80
+ case "select":
81
+ return /* @__PURE__ */ jsxs("div", { className: "settings-field", children: [
82
+ /* @__PURE__ */ jsx("label", { className: "settings-field-label", htmlFor: field.id, children: field.label }),
83
+ field.description && /* @__PURE__ */ jsx("p", { className: "settings-field-description", children: field.description }),
84
+ /* @__PURE__ */ jsx(
85
+ Select,
86
+ {
87
+ id: field.id,
88
+ className: "settings-select",
89
+ value: field.value,
90
+ onChange: field.onChange,
91
+ options: field.options,
92
+ disabled: field.disabled
93
+ }
94
+ )
95
+ ] }, field.id);
96
+ case "button":
97
+ return /* @__PURE__ */ jsxs("div", { className: "settings-field", children: [
98
+ field.label && /* @__PURE__ */ jsx("label", { className: "settings-field-label", children: field.label }),
99
+ field.description && /* @__PURE__ */ jsx("p", { className: "settings-field-description", children: field.description }),
100
+ /* @__PURE__ */ jsx(
101
+ "button",
102
+ {
103
+ className: `settings-button settings-button--${field.variant || "default"}`,
104
+ onClick: field.onClick,
105
+ disabled: field.disabled,
106
+ children: field.buttonLabel
107
+ }
108
+ )
109
+ ] }, field.id);
110
+ case "custom":
111
+ return /* @__PURE__ */ jsxs("div", { className: "settings-field", children: [
112
+ field.label && /* @__PURE__ */ jsx("label", { className: "settings-field-label", children: field.label }),
113
+ field.description && /* @__PURE__ */ jsx("p", { className: "settings-field-description", children: field.description }),
114
+ field.render()
115
+ ] }, field.id);
116
+ default:
117
+ return null;
118
+ }
119
+ };
120
+ return /* @__PURE__ */ jsx(
121
+ Modal,
122
+ {
123
+ isOpen,
124
+ title,
125
+ onClose,
126
+ onConfirm: showCloseButton ? onClose : void 0,
127
+ confirmLabel: "Close",
128
+ showCancel: false,
129
+ showConfirm: showCloseButton,
130
+ className: `settings-modal ${className}`,
131
+ children: /* @__PURE__ */ jsx("div", { className: "settings-content", children: sections.map((section) => /* @__PURE__ */ jsxs("div", { className: "settings-section", children: [
132
+ /* @__PURE__ */ jsx("h4", { className: "settings-section-title", children: section.title }),
133
+ section.fields.map((field) => renderField(field))
134
+ ] }, section.id)) })
135
+ }
136
+ );
137
+ }
138
+ export {
139
+ SettingsModal
140
+ };
141
+ //# sourceMappingURL=SettingsModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsModal.js","sources":["../../src/components/SettingsModal.tsx"],"sourcesContent":["/**\n * SettingsModal Component\n * Generic settings dialog that can be customized with sections\n */\n\nimport React from 'react'\nimport { Modal } from './Modal'\nimport { Select } from './Select'\n\nexport interface SettingsSection {\n id: string\n title: string\n fields: SettingsField[]\n}\n\nexport type SettingsField =\n | SettingsToggleField\n | SettingsTextInputField\n | SettingsPasswordField\n | SettingsSelectField\n | SettingsButtonField\n | SettingsCustomField\n\nexport interface BaseSettingsField {\n id: string\n label: string\n description?: string\n disabled?: boolean\n}\n\nexport interface SettingsToggleField extends BaseSettingsField {\n type: 'toggle'\n value: boolean\n onChange: (value: boolean) => void\n}\n\nexport interface SettingsTextInputField extends BaseSettingsField {\n type: 'text'\n value: string\n onChange: (value: string) => void\n placeholder?: string\n}\n\nexport interface SettingsPasswordField extends BaseSettingsField {\n type: 'password'\n value: string\n onChange: (value: string) => void\n placeholder?: string\n showButton?: boolean\n buttonLabel?: string\n onButtonClick?: () => void | Promise<void>\n buttonDisabled?: boolean\n error?: string | null\n autoComplete?: string\n}\n\nexport interface SettingsSelectField extends BaseSettingsField {\n type: 'select'\n value: string\n onChange: (value: string) => void\n options: Array<{ value: string; label: string }>\n}\n\nexport interface SettingsButtonField extends BaseSettingsField {\n type: 'button'\n buttonLabel: string\n onClick: () => void | Promise<void>\n variant?: 'primary' | 'danger' | 'default'\n}\n\nexport interface SettingsCustomField extends BaseSettingsField {\n type: 'custom'\n render: () => React.ReactNode\n}\n\nexport interface SettingsModalProps {\n isOpen: boolean\n title?: string\n sections: SettingsSection[]\n onClose: () => void\n className?: string\n showCloseButton?: boolean\n}\n\nexport function SettingsModal({\n isOpen,\n title = 'Settings',\n sections,\n onClose,\n className = '',\n showCloseButton = true\n}: SettingsModalProps) {\n const renderField = (field: SettingsField) => {\n switch (field.type) {\n case 'toggle':\n return (\n <div key={field.id} className=\"settings-field\">\n <div className=\"settings-field-header\">\n <label className=\"settings-field-label\" htmlFor={field.id}>\n {field.label}\n </label>\n <input\n type=\"checkbox\"\n id={field.id}\n className=\"settings-toggle\"\n checked={field.value}\n onChange={e => field.onChange(e.target.checked)}\n disabled={field.disabled}\n />\n </div>\n {field.description && <p className=\"settings-field-description\">{field.description}</p>}\n </div>\n )\n\n case 'text':\n return (\n <div key={field.id} className=\"settings-field\">\n <label className=\"settings-field-label\" htmlFor={field.id}>\n {field.label}\n </label>\n {field.description && <p className=\"settings-field-description\">{field.description}</p>}\n <input\n type=\"text\"\n id={field.id}\n className=\"settings-text-input\"\n value={field.value}\n onChange={e => field.onChange(e.target.value)}\n placeholder={field.placeholder}\n disabled={field.disabled}\n />\n </div>\n )\n\n case 'password':\n return (\n <div key={field.id} className=\"settings-field\">\n <label className=\"settings-field-label\" htmlFor={field.id}>\n {field.label}\n </label>\n {field.description && <p className=\"settings-field-description\">{field.description}</p>}\n <div className=\"settings-field-input-group\">\n <input\n type=\"password\"\n id={field.id}\n name={field.autoComplete || field.id}\n autoComplete={field.autoComplete || 'off'}\n className=\"settings-text-input\"\n value={field.value}\n onChange={e => field.onChange(e.target.value)}\n placeholder={field.placeholder}\n disabled={field.disabled}\n />\n {field.showButton && field.onButtonClick && (\n <button\n className=\"settings-button\"\n onClick={field.onButtonClick}\n disabled={field.buttonDisabled || field.disabled}\n >\n {field.buttonLabel || 'Submit'}\n </button>\n )}\n </div>\n {field.error && <p className=\"settings-field-error\">{field.error}</p>}\n </div>\n )\n\n case 'select':\n return (\n <div key={field.id} className=\"settings-field\">\n <label className=\"settings-field-label\" htmlFor={field.id}>\n {field.label}\n </label>\n {field.description && <p className=\"settings-field-description\">{field.description}</p>}\n <Select\n id={field.id}\n className=\"settings-select\"\n value={field.value}\n onChange={field.onChange}\n options={field.options}\n disabled={field.disabled}\n />\n </div>\n )\n\n case 'button':\n return (\n <div key={field.id} className=\"settings-field\">\n {field.label && <label className=\"settings-field-label\">{field.label}</label>}\n {field.description && <p className=\"settings-field-description\">{field.description}</p>}\n <button\n className={`settings-button settings-button--${field.variant || 'default'}`}\n onClick={field.onClick}\n disabled={field.disabled}\n >\n {field.buttonLabel}\n </button>\n </div>\n )\n\n case 'custom':\n return (\n <div key={field.id} className=\"settings-field\">\n {field.label && <label className=\"settings-field-label\">{field.label}</label>}\n {field.description && <p className=\"settings-field-description\">{field.description}</p>}\n {field.render()}\n </div>\n )\n\n default:\n return null\n }\n }\n\n return (\n <Modal\n isOpen={isOpen}\n title={title}\n onClose={onClose}\n onConfirm={showCloseButton ? onClose : undefined}\n confirmLabel=\"Close\"\n showCancel={false}\n showConfirm={showCloseButton}\n className={`settings-modal ${className}`}\n >\n <div className=\"settings-content\">\n {sections.map(section => (\n <div key={section.id} className=\"settings-section\">\n <h4 className=\"settings-section-title\">{section.title}</h4>\n {section.fields.map(field => renderField(field))}\n </div>\n ))}\n </div>\n </Modal>\n )\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Component icons - Simple monochrome SVG icons for theme picker
3
+ * All icons use currentColor so they can be styled with CSS
4
+ */
5
+ import React from 'react';
6
+ export declare const SettingsIcon: () => React.JSX.Element;
7
+ export declare const TagIcon: () => React.JSX.Element;
8
+ export declare const CircleIcon: () => React.JSX.Element;
9
+ export declare const SquareIcon: () => React.JSX.Element;
10
+ export declare const TriangleIcon: () => React.JSX.Element;
11
+ export declare const DiamondIcon: () => React.JSX.Element;
12
+ export declare const StarIcon: () => React.JSX.Element;
13
+ export declare const HexagonIcon: () => React.JSX.Element;
14
+ export declare const PentagonIcon: () => React.JSX.Element;
15
+ export declare const OctagonIcon: () => React.JSX.Element;
16
+ /**
17
+ * Fallback icon sets for themes - provides 8 distinct shapes
18
+ * Use these when you don't have specific icons for your themes
19
+ */
20
+ export declare const FALLBACK_ICONS: readonly [() => React.JSX.Element, () => React.JSX.Element, () => React.JSX.Element, () => React.JSX.Element, () => React.JSX.Element, () => React.JSX.Element, () => React.JSX.Element, () => React.JSX.Element];
21
+ /**
22
+ * Get a fallback icon for a theme by index
23
+ * Icons repeat after 8 themes to provide consistent differentiation
24
+ */
25
+ export declare function getFallbackIcon(index: number): React.JSX.Element;
26
+ //# sourceMappingURL=ThemeIcons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeIcons.d.ts","sourceRoot":"","sources":["../../src/components/ThemeIcons.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAazB,eAAO,MAAM,YAAY,yBAyBxB,CAAA;AAED,eAAO,MAAM,OAAO,yBAOnB,CAAA;AAED,eAAO,MAAM,UAAU,yBAItB,CAAA;AAED,eAAO,MAAM,UAAU,yBAItB,CAAA;AAED,eAAO,MAAM,YAAY,yBAIxB,CAAA;AAED,eAAO,MAAM,WAAW,yBAIvB,CAAA;AAED,eAAO,MAAM,QAAQ,yBAIpB,CAAA;AAED,eAAO,MAAM,WAAW,yBAIvB,CAAA;AAED,eAAO,MAAM,YAAY,yBAIxB,CAAA;AAED,eAAO,MAAM,WAAW,yBAIvB,CAAA;AAED;;;GAGG;AAEH,eAAO,MAAM,cAAc,mNASjB,CAAA;AAEV;;;GAGG;AAEH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,qBAG5C"}