@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.
- package/LICENSE +21 -0
- package/README.md +917 -2
- package/dist/app-header.css +288 -0
- package/dist/bento.css +143 -0
- package/dist/button.css +227 -0
- package/dist/components/AppHeader.d.ts +61 -0
- package/dist/components/AppHeader.d.ts.map +1 -0
- package/dist/components/AppHeader.js +23 -0
- package/dist/components/AppHeader.js.map +1 -0
- package/dist/components/BentoCard.d.ts +23 -0
- package/dist/components/BentoCard.d.ts.map +1 -0
- package/dist/components/BentoCard.js +49 -0
- package/dist/components/BentoCard.js.map +1 -0
- package/dist/components/BentoGrid.d.ts +24 -0
- package/dist/components/BentoGrid.d.ts.map +1 -0
- package/dist/components/BentoGrid.js +22 -0
- package/dist/components/BentoGrid.js.map +1 -0
- package/dist/components/Button.d.ts +28 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +51 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/ConnectedSettings.d.ts +53 -0
- package/dist/components/ConnectedSettings.d.ts.map +1 -0
- package/dist/components/ConnectedSettings.js +368 -0
- package/dist/components/ConnectedSettings.js.map +1 -0
- package/dist/components/ConnectedThemePicker.d.ts +52 -0
- package/dist/components/ConnectedThemePicker.d.ts.map +1 -0
- package/dist/components/ConnectedThemePicker.js +32 -0
- package/dist/components/ConnectedThemePicker.js.map +1 -0
- package/dist/components/ContextMenu.d.ts +22 -0
- package/dist/components/ContextMenu.d.ts.map +1 -0
- package/dist/components/ContextMenu.js +23 -0
- package/dist/components/ContextMenu.js.map +1 -0
- package/dist/components/HadokuThemePicker.d.ts +27 -0
- package/dist/components/HadokuThemePicker.d.ts.map +1 -0
- package/dist/components/HadokuThemePicker.js +19 -0
- package/dist/components/HadokuThemePicker.js.map +1 -0
- package/dist/components/LoadingSkeleton.d.ts +14 -0
- package/dist/components/LoadingSkeleton.d.ts.map +1 -0
- package/dist/components/LoadingSkeleton.js +62 -0
- package/dist/components/LoadingSkeleton.js.map +1 -0
- package/dist/components/Modal.d.ts +39 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +106 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/NumberInput.d.ts +26 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +131 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/Select.d.ts +52 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Select.js +281 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/SettingsModal.d.ts +69 -0
- package/dist/components/SettingsModal.d.ts.map +1 -0
- package/dist/components/SettingsModal.js +141 -0
- package/dist/components/SettingsModal.js.map +1 -0
- package/dist/components/ThemeIcons.d.ts +26 -0
- package/dist/components/ThemeIcons.d.ts.map +1 -0
- package/dist/components/ThemeIcons.js +74 -0
- package/dist/components/ThemeIcons.js.map +1 -0
- package/dist/components/ThemePicker.d.ts +28 -0
- package/dist/components/ThemePicker.d.ts.map +1 -0
- package/dist/components/ThemePicker.js +101 -0
- package/dist/components/ThemePicker.js.map +1 -0
- package/dist/components/Toast.d.ts +13 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +70 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/Toaster.d.ts +19 -0
- package/dist/components/Toaster.d.ts.map +1 -0
- package/dist/components/Toaster.js +19 -0
- package/dist/components/Toaster.js.map +1 -0
- package/dist/components/Tooltip.d.ts +49 -0
- package/dist/components/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip.js +143 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/context-menu.css +11 -0
- package/dist/controls.css +137 -0
- package/dist/hooks/useReducedMotion.d.ts +7 -0
- package/dist/hooks/useReducedMotion.d.ts.map +1 -0
- package/dist/hooks/useReducedMotion.js +25 -0
- package/dist/hooks/useReducedMotion.js.map +1 -0
- package/dist/hooks/useToast.d.ts +18 -0
- package/dist/hooks/useToast.d.ts.map +1 -0
- package/dist/hooks/useToast.js +28 -0
- package/dist/hooks/useToast.js.map +1 -0
- package/dist/icon-btn.css +64 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/menuPlacement.d.ts +16 -0
- package/dist/lib/menuPlacement.d.ts.map +1 -0
- package/dist/lib/menuPlacement.js +19 -0
- package/dist/lib/menuPlacement.js.map +1 -0
- package/dist/lib/selectMenus.d.ts +30 -0
- package/dist/lib/selectMenus.d.ts.map +1 -0
- package/dist/lib/selectMenus.js +236 -0
- package/dist/lib/selectMenus.js.map +1 -0
- package/dist/lib/settingsClient.d.ts +144 -0
- package/dist/lib/settingsClient.d.ts.map +1 -0
- package/dist/lib/settingsClient.js +157 -0
- package/dist/lib/settingsClient.js.map +1 -0
- package/dist/lib/themeScope.d.ts +19 -0
- package/dist/lib/themeScope.d.ts.map +1 -0
- package/dist/lib/themeScope.js +10 -0
- package/dist/lib/themeScope.js.map +1 -0
- package/dist/lib/titleTooltips.d.ts +38 -0
- package/dist/lib/titleTooltips.d.ts.map +1 -0
- package/dist/lib/titleTooltips.js +166 -0
- package/dist/lib/titleTooltips.js.map +1 -0
- package/dist/lib/tooltipPlacement.d.ts +14 -0
- package/dist/lib/tooltipPlacement.d.ts.map +1 -0
- package/dist/lib/tooltipPlacement.js +34 -0
- package/dist/lib/tooltipPlacement.js.map +1 -0
- package/dist/modal.css +129 -0
- package/dist/number-input.css +131 -0
- package/dist/scrollbar.css +165 -0
- package/dist/select.css +251 -0
- package/dist/styles.css +1866 -0
- package/dist/task-ui-components.css +1866 -0
- package/dist/theme-picker.css +219 -0
- package/dist/toaster.css +159 -0
- package/dist/tooltip.css +103 -0
- package/dist/types/index.d.ts +37 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +60 -4
- package/src/app-header.css +288 -0
- package/src/bento.css +143 -0
- package/src/button.css +227 -0
- package/src/components/AppHeader.tsx +87 -0
- package/src/components/BentoCard.tsx +72 -0
- package/src/components/BentoGrid.tsx +48 -0
- package/src/components/Button.tsx +81 -0
- package/src/components/ConnectedSettings.tsx +545 -0
- package/src/components/ConnectedThemePicker.tsx +79 -0
- package/src/components/ContextMenu.tsx +48 -0
- package/src/components/HadokuThemePicker.tsx +39 -0
- package/src/components/LoadingSkeleton.tsx +84 -0
- package/src/components/Modal.tsx +171 -0
- package/src/components/NumberInput.tsx +161 -0
- package/src/components/Select.tsx +359 -0
- package/src/components/SettingsModal.tsx +235 -0
- package/src/components/ThemeIcons.tsx +127 -0
- package/src/components/ThemePicker.tsx +149 -0
- package/src/components/Toast.tsx +92 -0
- package/src/components/Toaster.tsx +43 -0
- package/src/components/Tooltip.tsx +212 -0
- package/src/context-menu.css +11 -0
- package/src/controls.css +137 -0
- package/src/hooks/useReducedMotion.ts +36 -0
- package/src/hooks/useToast.ts +57 -0
- package/src/icon-btn.css +64 -0
- package/src/index.ts +153 -0
- package/src/lib/menuPlacement.ts +31 -0
- package/src/lib/selectMenus.ts +303 -0
- package/src/lib/settingsClient.ts +425 -0
- package/src/lib/themeScope.ts +23 -0
- package/src/lib/titleTooltips.ts +236 -0
- package/src/lib/tooltipPlacement.ts +59 -0
- package/src/modal.css +129 -0
- package/src/number-input.css +131 -0
- package/src/scrollbar.css +165 -0
- package/src/select.css +251 -0
- package/src/theme-picker.css +219 -0
- package/src/toaster.css +159 -0
- package/src/tooltip.css +103 -0
- 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"}
|