dialkit 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +249 -74
- package/dist/index.cjs +899 -259
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -17
- package/dist/index.d.ts +82 -17
- package/dist/index.js +906 -269
- package/dist/index.js.map +1 -1
- package/dist/styles.css +620 -44
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,12 +12,16 @@ var DialStoreClass = class {
|
|
|
12
12
|
this.globalListeners = /* @__PURE__ */ new Set();
|
|
13
13
|
this.snapshots = /* @__PURE__ */ new Map();
|
|
14
14
|
this.actionListeners = /* @__PURE__ */ new Map();
|
|
15
|
+
this.presets = /* @__PURE__ */ new Map();
|
|
16
|
+
this.activePreset = /* @__PURE__ */ new Map();
|
|
17
|
+
this.baseValues = /* @__PURE__ */ new Map();
|
|
15
18
|
}
|
|
16
19
|
registerPanel(id, name, config) {
|
|
17
20
|
const controls = this.parseConfig(config, "");
|
|
18
21
|
const values = this.flattenValues(config, "");
|
|
19
22
|
this.panels.set(id, { id, name, controls, values });
|
|
20
23
|
this.snapshots.set(id, { ...values });
|
|
24
|
+
this.baseValues.set(id, { ...values });
|
|
21
25
|
this.notifyGlobal();
|
|
22
26
|
}
|
|
23
27
|
unregisterPanel(id) {
|
|
@@ -30,6 +34,15 @@ var DialStoreClass = class {
|
|
|
30
34
|
const panel = this.panels.get(panelId);
|
|
31
35
|
if (!panel) return;
|
|
32
36
|
panel.values[path] = value;
|
|
37
|
+
const activeId = this.activePreset.get(panelId);
|
|
38
|
+
if (activeId) {
|
|
39
|
+
const presets = this.presets.get(panelId) ?? [];
|
|
40
|
+
const preset = presets.find((p) => p.id === activeId);
|
|
41
|
+
if (preset) preset.values[path] = value;
|
|
42
|
+
} else {
|
|
43
|
+
const base = this.baseValues.get(panelId);
|
|
44
|
+
if (base) base[path] = value;
|
|
45
|
+
}
|
|
33
46
|
this.snapshots.set(panelId, { ...panel.values });
|
|
34
47
|
this.notify(panelId);
|
|
35
48
|
}
|
|
@@ -83,6 +96,61 @@ var DialStoreClass = class {
|
|
|
83
96
|
triggerAction(panelId, path) {
|
|
84
97
|
this.actionListeners.get(panelId)?.forEach((fn) => fn(path));
|
|
85
98
|
}
|
|
99
|
+
savePreset(panelId, name) {
|
|
100
|
+
const panel = this.panels.get(panelId);
|
|
101
|
+
if (!panel) throw new Error(`Panel ${panelId} not found`);
|
|
102
|
+
const id = `preset-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
103
|
+
const preset = {
|
|
104
|
+
id,
|
|
105
|
+
name,
|
|
106
|
+
values: { ...panel.values }
|
|
107
|
+
};
|
|
108
|
+
const existing = this.presets.get(panelId) ?? [];
|
|
109
|
+
this.presets.set(panelId, [...existing, preset]);
|
|
110
|
+
this.activePreset.set(panelId, id);
|
|
111
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
112
|
+
this.notify(panelId);
|
|
113
|
+
return id;
|
|
114
|
+
}
|
|
115
|
+
loadPreset(panelId, presetId) {
|
|
116
|
+
const panel = this.panels.get(panelId);
|
|
117
|
+
if (!panel) return;
|
|
118
|
+
const presets = this.presets.get(panelId) ?? [];
|
|
119
|
+
const preset = presets.find((p) => p.id === presetId);
|
|
120
|
+
if (!preset) return;
|
|
121
|
+
panel.values = { ...preset.values };
|
|
122
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
123
|
+
this.activePreset.set(panelId, presetId);
|
|
124
|
+
this.notify(panelId);
|
|
125
|
+
}
|
|
126
|
+
deletePreset(panelId, presetId) {
|
|
127
|
+
const presets = this.presets.get(panelId) ?? [];
|
|
128
|
+
this.presets.set(panelId, presets.filter((p) => p.id !== presetId));
|
|
129
|
+
if (this.activePreset.get(panelId) === presetId) {
|
|
130
|
+
this.activePreset.set(panelId, null);
|
|
131
|
+
}
|
|
132
|
+
const panel = this.panels.get(panelId);
|
|
133
|
+
if (panel) {
|
|
134
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
135
|
+
}
|
|
136
|
+
this.notify(panelId);
|
|
137
|
+
}
|
|
138
|
+
getPresets(panelId) {
|
|
139
|
+
return this.presets.get(panelId) ?? [];
|
|
140
|
+
}
|
|
141
|
+
getActivePresetId(panelId) {
|
|
142
|
+
return this.activePreset.get(panelId) ?? null;
|
|
143
|
+
}
|
|
144
|
+
clearActivePreset(panelId) {
|
|
145
|
+
const panel = this.panels.get(panelId);
|
|
146
|
+
const base = this.baseValues.get(panelId);
|
|
147
|
+
if (panel && base) {
|
|
148
|
+
panel.values = { ...base };
|
|
149
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
150
|
+
}
|
|
151
|
+
this.activePreset.set(panelId, null);
|
|
152
|
+
this.notify(panelId);
|
|
153
|
+
}
|
|
86
154
|
notify(panelId) {
|
|
87
155
|
this.listeners.get(panelId)?.forEach((fn) => fn());
|
|
88
156
|
}
|
|
@@ -112,6 +180,18 @@ var DialStoreClass = class {
|
|
|
112
180
|
controls.push({ type: "spring", path, label });
|
|
113
181
|
} else if (this.isActionConfig(value)) {
|
|
114
182
|
controls.push({ type: "action", path, label: value.label || label });
|
|
183
|
+
} else if (this.isSelectConfig(value)) {
|
|
184
|
+
controls.push({ type: "select", path, label, options: value.options });
|
|
185
|
+
} else if (this.isColorConfig(value)) {
|
|
186
|
+
controls.push({ type: "color", path, label });
|
|
187
|
+
} else if (this.isTextConfig(value)) {
|
|
188
|
+
controls.push({ type: "text", path, label, placeholder: value.placeholder });
|
|
189
|
+
} else if (typeof value === "string") {
|
|
190
|
+
if (this.isHexColor(value)) {
|
|
191
|
+
controls.push({ type: "color", path, label });
|
|
192
|
+
} else {
|
|
193
|
+
controls.push({ type: "text", path, label });
|
|
194
|
+
}
|
|
115
195
|
} else if (typeof value === "object" && value !== null) {
|
|
116
196
|
controls.push({
|
|
117
197
|
type: "folder",
|
|
@@ -135,6 +215,14 @@ var DialStoreClass = class {
|
|
|
135
215
|
values[path] = value;
|
|
136
216
|
} else if (this.isActionConfig(value)) {
|
|
137
217
|
values[path] = value;
|
|
218
|
+
} else if (this.isSelectConfig(value)) {
|
|
219
|
+
const firstOption = value.options[0];
|
|
220
|
+
const firstValue = typeof firstOption === "string" ? firstOption : firstOption.value;
|
|
221
|
+
values[path] = value.default ?? firstValue;
|
|
222
|
+
} else if (this.isColorConfig(value)) {
|
|
223
|
+
values[path] = value.default ?? "#000000";
|
|
224
|
+
} else if (this.isTextConfig(value)) {
|
|
225
|
+
values[path] = value.default ?? "";
|
|
138
226
|
} else if (typeof value === "object" && value !== null) {
|
|
139
227
|
Object.assign(values, this.flattenValues(value, path));
|
|
140
228
|
}
|
|
@@ -147,6 +235,18 @@ var DialStoreClass = class {
|
|
|
147
235
|
isActionConfig(value) {
|
|
148
236
|
return typeof value === "object" && value !== null && "type" in value && value.type === "action";
|
|
149
237
|
}
|
|
238
|
+
isSelectConfig(value) {
|
|
239
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === "select" && "options" in value && Array.isArray(value.options);
|
|
240
|
+
}
|
|
241
|
+
isColorConfig(value) {
|
|
242
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === "color";
|
|
243
|
+
}
|
|
244
|
+
isTextConfig(value) {
|
|
245
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === "text";
|
|
246
|
+
}
|
|
247
|
+
isHexColor(value) {
|
|
248
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value);
|
|
249
|
+
}
|
|
150
250
|
formatLabel(key) {
|
|
151
251
|
return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
152
252
|
}
|
|
@@ -154,13 +254,13 @@ var DialStoreClass = class {
|
|
|
154
254
|
if (value >= 0 && value <= 1) {
|
|
155
255
|
return { min: 0, max: 1, step: 0.01 };
|
|
156
256
|
} else if (value >= 0 && value <= 10) {
|
|
157
|
-
return { min: 0, max: value *
|
|
257
|
+
return { min: 0, max: value * 3 || 10, step: 0.1 };
|
|
158
258
|
} else if (value >= 0 && value <= 100) {
|
|
159
|
-
return { min: 0, max: value *
|
|
259
|
+
return { min: 0, max: value * 3 || 100, step: 1 };
|
|
160
260
|
} else if (value >= 0) {
|
|
161
|
-
return { min: 0, max: value *
|
|
261
|
+
return { min: 0, max: value * 3 || 1e3, step: 10 };
|
|
162
262
|
} else {
|
|
163
|
-
return { min: value *
|
|
263
|
+
return { min: value * 3, max: -value * 3, step: 1 };
|
|
164
264
|
}
|
|
165
265
|
}
|
|
166
266
|
inferStep(min, max) {
|
|
@@ -206,188 +306,150 @@ function buildResolvedValues(config, flatValues, prefix) {
|
|
|
206
306
|
result[key] = flatValues[path] ?? configValue;
|
|
207
307
|
} else if (isSpringConfig(configValue)) {
|
|
208
308
|
result[key] = flatValues[path] ?? configValue;
|
|
309
|
+
} else if (isActionConfig(configValue)) {
|
|
310
|
+
result[key] = flatValues[path] ?? configValue;
|
|
311
|
+
} else if (isSelectConfig(configValue)) {
|
|
312
|
+
const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
|
|
313
|
+
result[key] = flatValues[path] ?? defaultValue;
|
|
314
|
+
} else if (isColorConfig(configValue)) {
|
|
315
|
+
result[key] = flatValues[path] ?? configValue.default ?? "#000000";
|
|
316
|
+
} else if (isTextConfig(configValue)) {
|
|
317
|
+
result[key] = flatValues[path] ?? configValue.default ?? "";
|
|
209
318
|
} else if (typeof configValue === "object" && configValue !== null) {
|
|
210
319
|
result[key] = buildResolvedValues(configValue, flatValues, path);
|
|
211
320
|
}
|
|
212
321
|
}
|
|
213
322
|
return result;
|
|
214
323
|
}
|
|
324
|
+
function hasType(value, type) {
|
|
325
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === type;
|
|
326
|
+
}
|
|
215
327
|
function isSpringConfig(value) {
|
|
216
|
-
return
|
|
328
|
+
return hasType(value, "spring");
|
|
329
|
+
}
|
|
330
|
+
function isActionConfig(value) {
|
|
331
|
+
return hasType(value, "action");
|
|
332
|
+
}
|
|
333
|
+
function isSelectConfig(value) {
|
|
334
|
+
return hasType(value, "select") && "options" in value && Array.isArray(value.options);
|
|
335
|
+
}
|
|
336
|
+
function isColorConfig(value) {
|
|
337
|
+
return hasType(value, "color");
|
|
338
|
+
}
|
|
339
|
+
function isTextConfig(value) {
|
|
340
|
+
return hasType(value, "text");
|
|
341
|
+
}
|
|
342
|
+
function getFirstOptionValue(options) {
|
|
343
|
+
const first = options[0];
|
|
344
|
+
return typeof first === "string" ? first : first.value;
|
|
217
345
|
}
|
|
218
346
|
|
|
219
347
|
// src/components/DialRoot.tsx
|
|
220
|
-
import { useEffect as
|
|
221
|
-
import { createPortal } from "react-dom";
|
|
348
|
+
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
349
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
350
|
+
|
|
351
|
+
// src/components/Panel.tsx
|
|
352
|
+
import { useState as useState7, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
353
|
+
import { motion as motion6, AnimatePresence as AnimatePresence4 } from "motion/react";
|
|
222
354
|
|
|
223
355
|
// src/components/Folder.tsx
|
|
224
|
-
import { useState } from "react";
|
|
356
|
+
import { useState, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
225
357
|
import { motion, AnimatePresence } from "motion/react";
|
|
226
358
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
227
|
-
function Folder({ title, children, defaultOpen = true, isRoot = false,
|
|
359
|
+
function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenChange, toolbar }) {
|
|
228
360
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
229
|
-
const [
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
if (!panelId) return;
|
|
233
|
-
const values = DialStore.getValues(panelId);
|
|
234
|
-
const jsonStr = JSON.stringify(values, null, 2);
|
|
235
|
-
const instruction = `Update the useDialKit configuration for "${title}" with these values:
|
|
236
|
-
|
|
237
|
-
\`\`\`json
|
|
238
|
-
${jsonStr}
|
|
239
|
-
\`\`\`
|
|
240
|
-
|
|
241
|
-
Apply these values as the new defaults in the useDialKit call.`;
|
|
242
|
-
navigator.clipboard.writeText(instruction);
|
|
243
|
-
setCopied(true);
|
|
244
|
-
setTimeout(() => setCopied(false), 1500);
|
|
245
|
-
};
|
|
361
|
+
const [isCollapsed, setIsCollapsed] = useState(!defaultOpen);
|
|
362
|
+
const contentRef = useRef2(null);
|
|
363
|
+
const [contentHeight, setContentHeight] = useState(void 0);
|
|
246
364
|
const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
365
|
+
const panelTransition = { type: "spring", visualDuration: 0.4, bounce: 0.2 };
|
|
366
|
+
useEffect2(() => {
|
|
367
|
+
const el = contentRef.current;
|
|
368
|
+
if (!el) return;
|
|
369
|
+
const ro = new ResizeObserver(() => {
|
|
370
|
+
if (isOpen) {
|
|
371
|
+
const h = el.offsetHeight;
|
|
372
|
+
setContentHeight((prev) => prev === h ? prev : h);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
ro.observe(el);
|
|
376
|
+
return () => ro.disconnect();
|
|
377
|
+
}, [isOpen]);
|
|
378
|
+
const folderContent = /* @__PURE__ */ jsxs("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
|
|
379
|
+
/* @__PURE__ */ jsxs("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: () => {
|
|
380
|
+
const next = !isOpen;
|
|
381
|
+
setIsOpen(next);
|
|
382
|
+
if (next) setIsCollapsed(false);
|
|
383
|
+
onOpenChange?.(next);
|
|
384
|
+
}, children: [
|
|
385
|
+
/* @__PURE__ */ jsxs("div", { className: "dialkit-folder-header-top", children: [
|
|
386
|
+
isRoot ? /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "popLayout", children: isOpen && /* @__PURE__ */ jsx(
|
|
387
|
+
motion.div,
|
|
253
388
|
{
|
|
254
|
-
className: "dialkit-folder-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
scale: isOpen ? 1 : 0.7,
|
|
261
|
-
filter: isOpen ? "blur(0px)" : "blur(6px)"
|
|
262
|
-
},
|
|
263
|
-
transition: iconTransition,
|
|
264
|
-
style: { pointerEvents: isOpen ? "auto" : "none" },
|
|
265
|
-
children: /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: 14, height: 14 }, children: [
|
|
266
|
-
/* @__PURE__ */ jsxs(
|
|
267
|
-
motion.svg,
|
|
268
|
-
{
|
|
269
|
-
viewBox: "0 0 24 24",
|
|
270
|
-
fill: "none",
|
|
271
|
-
stroke: "currentColor",
|
|
272
|
-
strokeWidth: "2",
|
|
273
|
-
strokeLinecap: "round",
|
|
274
|
-
strokeLinejoin: "round",
|
|
275
|
-
style: { position: "absolute", inset: 0 },
|
|
276
|
-
initial: false,
|
|
277
|
-
animate: {
|
|
278
|
-
opacity: copied ? 0 : 1,
|
|
279
|
-
scale: copied ? 0.7 : 1,
|
|
280
|
-
filter: copied ? "blur(6px)" : "blur(0px)"
|
|
281
|
-
},
|
|
282
|
-
transition: iconTransition,
|
|
283
|
-
children: [
|
|
284
|
-
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
285
|
-
/* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
286
|
-
]
|
|
287
|
-
}
|
|
288
|
-
),
|
|
289
|
-
/* @__PURE__ */ jsx(
|
|
290
|
-
motion.svg,
|
|
291
|
-
{
|
|
292
|
-
viewBox: "0 0 24 24",
|
|
293
|
-
fill: "none",
|
|
294
|
-
stroke: "currentColor",
|
|
295
|
-
strokeWidth: "2",
|
|
296
|
-
strokeLinecap: "round",
|
|
297
|
-
strokeLinejoin: "round",
|
|
298
|
-
style: { position: "absolute", inset: 0 },
|
|
299
|
-
initial: false,
|
|
300
|
-
animate: {
|
|
301
|
-
opacity: copied ? 1 : 0,
|
|
302
|
-
scale: copied ? 1 : 0.7,
|
|
303
|
-
filter: copied ? "blur(0px)" : "blur(6px)"
|
|
304
|
-
},
|
|
305
|
-
transition: iconTransition,
|
|
306
|
-
children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
|
|
307
|
-
}
|
|
308
|
-
)
|
|
309
|
-
] })
|
|
389
|
+
className: "dialkit-folder-title-row",
|
|
390
|
+
initial: { opacity: 1 },
|
|
391
|
+
animate: { opacity: 1 },
|
|
392
|
+
exit: { opacity: 0 },
|
|
393
|
+
transition: { duration: 0.15 },
|
|
394
|
+
children: /* @__PURE__ */ jsx("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title })
|
|
310
395
|
}
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
/* @__PURE__ */ jsx(
|
|
317
|
-
motion.svg,
|
|
396
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ jsx("span", { className: "dialkit-folder-title", children: title }) }),
|
|
397
|
+
isRoot ? (
|
|
398
|
+
// Root panel icon — fixed position, container morphs around it
|
|
399
|
+
/* @__PURE__ */ jsxs(
|
|
400
|
+
"svg",
|
|
318
401
|
{
|
|
319
|
-
|
|
402
|
+
className: "dialkit-panel-icon",
|
|
403
|
+
viewBox: "0 0 16 16",
|
|
320
404
|
fill: "none",
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
animate: {
|
|
328
|
-
opacity: isOpen ? 1 : 0,
|
|
329
|
-
scale: isOpen ? 1 : 0.7,
|
|
330
|
-
filter: isOpen ? "blur(0px)" : "blur(6px)"
|
|
331
|
-
},
|
|
332
|
-
transition: iconTransition,
|
|
333
|
-
children: /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
405
|
+
children: [
|
|
406
|
+
/* @__PURE__ */ jsx("path", { opacity: "0.5", d: "M6.84766 11.75C6.78583 11.9899 6.75 12.2408 6.75 12.5C6.75 12.7592 6.78583 13.0101 6.84766 13.25H2C1.58579 13.25 1.25 12.9142 1.25 12.5C1.25 12.0858 1.58579 11.75 2 11.75H6.84766ZM14 11.75C14.4142 11.75 14.75 12.0858 14.75 12.5C14.75 12.9142 14.4142 13.25 14 13.25H12.6523C12.7142 13.0101 12.75 12.7592 12.75 12.5C12.75 12.2408 12.7142 11.9899 12.6523 11.75H14ZM3.09766 7.25C3.03583 7.48994 3 7.74075 3 8C3 8.25925 3.03583 8.51006 3.09766 8.75H2C1.58579 8.75 1.25 8.41421 1.25 8C1.25 7.58579 1.58579 7.25 2 7.25H3.09766ZM14 7.25C14.4142 7.25 14.75 7.58579 14.75 8C14.75 8.41421 14.4142 8.75 14 8.75H8.90234C8.96417 8.51006 9 8.25925 9 8C9 7.74075 8.96417 7.48994 8.90234 7.25H14ZM7.59766 2.75C7.53583 2.98994 7.5 3.24075 7.5 3.5C7.5 3.75925 7.53583 4.01006 7.59766 4.25H2C1.58579 4.25 1.25 3.91421 1.25 3.5C1.25 3.08579 1.58579 2.75 2 2.75H7.59766ZM14 2.75C14.4142 2.75 14.75 3.08579 14.75 3.5C14.75 3.91421 14.4142 4.25 14 4.25H13.4023C13.4642 4.01006 13.5 3.75925 13.5 3.5C13.5 3.24075 13.4642 2.98994 13.4023 2.75H14Z", fill: "currentColor" }),
|
|
407
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "8", r: "0.998596", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
|
|
408
|
+
/* @__PURE__ */ jsx("circle", { cx: "10.4999", cy: "3.5", r: "0.998657", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
|
|
409
|
+
/* @__PURE__ */ jsx("circle", { cx: "9.75015", cy: "12.5", r: "0.997986", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })
|
|
410
|
+
]
|
|
334
411
|
}
|
|
335
|
-
)
|
|
336
|
-
|
|
412
|
+
)
|
|
413
|
+
) : (
|
|
414
|
+
// Section folders use rotating chevron with gentle spring
|
|
415
|
+
/* @__PURE__ */ jsx(
|
|
337
416
|
motion.svg,
|
|
338
417
|
{
|
|
418
|
+
className: "dialkit-folder-icon",
|
|
339
419
|
viewBox: "0 0 24 24",
|
|
340
420
|
fill: "none",
|
|
341
421
|
stroke: "currentColor",
|
|
342
|
-
strokeWidth: "2",
|
|
422
|
+
strokeWidth: "2.5",
|
|
343
423
|
strokeLinecap: "round",
|
|
344
424
|
strokeLinejoin: "round",
|
|
345
|
-
style: { position: "absolute", inset: 0, width: "100%", height: "100%" },
|
|
346
425
|
initial: false,
|
|
347
|
-
animate: {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
filter: isOpen ? "blur(6px)" : "blur(0px)"
|
|
351
|
-
},
|
|
352
|
-
transition: iconTransition,
|
|
353
|
-
children: [
|
|
354
|
-
/* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
355
|
-
/* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
356
|
-
]
|
|
426
|
+
animate: { rotate: isOpen ? 0 : 180 },
|
|
427
|
+
transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
|
|
428
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 9.5L12 15.5L18 9.5" })
|
|
357
429
|
}
|
|
358
430
|
)
|
|
359
|
-
] })
|
|
360
|
-
) : (
|
|
361
|
-
// Section folders use rotating chevron with gentle spring
|
|
362
|
-
/* @__PURE__ */ jsxs(
|
|
363
|
-
motion.svg,
|
|
364
|
-
{
|
|
365
|
-
className: "dialkit-folder-icon",
|
|
366
|
-
viewBox: "0 0 24 24",
|
|
367
|
-
fill: "none",
|
|
368
|
-
stroke: "currentColor",
|
|
369
|
-
strokeWidth: "1.5",
|
|
370
|
-
strokeLinecap: "round",
|
|
371
|
-
strokeLinejoin: "round",
|
|
372
|
-
initial: false,
|
|
373
|
-
animate: { rotate: isOpen ? 0 : 180 },
|
|
374
|
-
transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
|
|
375
|
-
children: [
|
|
376
|
-
/* @__PURE__ */ jsx("line", { x1: "5", y1: "9", x2: "12", y2: "16" }),
|
|
377
|
-
/* @__PURE__ */ jsx("line", { x1: "19", y1: "9", x2: "12", y2: "16" })
|
|
378
|
-
]
|
|
379
|
-
}
|
|
380
431
|
)
|
|
381
|
-
)
|
|
432
|
+
] }),
|
|
433
|
+
isRoot && toolbar && /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
|
|
434
|
+
motion.div,
|
|
435
|
+
{
|
|
436
|
+
initial: { opacity: 1 },
|
|
437
|
+
animate: { opacity: 1 },
|
|
438
|
+
exit: { opacity: 0 },
|
|
439
|
+
transition: { duration: 0.15 },
|
|
440
|
+
children: /* @__PURE__ */ jsx("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
|
|
441
|
+
}
|
|
442
|
+
) })
|
|
382
443
|
] }),
|
|
383
444
|
/* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
|
|
384
445
|
motion.div,
|
|
385
446
|
{
|
|
386
447
|
className: "dialkit-folder-content",
|
|
387
|
-
initial: { height: 0, opacity: 0 },
|
|
388
|
-
animate: { height: "auto", opacity: 1 },
|
|
389
|
-
exit: { height: 0, opacity: 0 },
|
|
390
|
-
transition: { type: "spring", visualDuration: 0.
|
|
448
|
+
initial: isRoot ? { opacity: 1 } : { height: 0, opacity: 0 },
|
|
449
|
+
animate: isRoot ? { opacity: 1 } : { height: "auto", opacity: 1 },
|
|
450
|
+
exit: isRoot ? { opacity: 0 } : { height: 0, opacity: 0 },
|
|
451
|
+
transition: isRoot ? { duration: 0.15 } : { type: "spring", visualDuration: 0.35, bounce: 0.1 },
|
|
452
|
+
style: isRoot ? void 0 : { overflow: "hidden" },
|
|
391
453
|
children: /* @__PURE__ */ jsx("div", { className: "dialkit-folder-inner", children })
|
|
392
454
|
}
|
|
393
455
|
) })
|
|
@@ -399,11 +461,22 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
399
461
|
className: "dialkit-panel-inner",
|
|
400
462
|
initial: false,
|
|
401
463
|
animate: {
|
|
402
|
-
width: isOpen ? 280 :
|
|
464
|
+
width: isOpen ? 280 : 42,
|
|
465
|
+
height: isOpen ? contentHeight !== void 0 ? contentHeight + 24 : "auto" : 42,
|
|
466
|
+
borderRadius: isOpen ? 14 : 21,
|
|
403
467
|
boxShadow: isOpen ? "0 8px 32px rgba(0, 0, 0, 0.5)" : "0 4px 16px rgba(0, 0, 0, 0.25)"
|
|
404
468
|
},
|
|
405
|
-
transition:
|
|
406
|
-
"
|
|
469
|
+
transition: panelTransition,
|
|
470
|
+
style: { overflow: isOpen ? void 0 : "hidden", cursor: isOpen ? void 0 : "pointer" },
|
|
471
|
+
onClick: !isOpen ? () => {
|
|
472
|
+
setIsOpen(true);
|
|
473
|
+
setIsCollapsed(false);
|
|
474
|
+
onOpenChange?.(true);
|
|
475
|
+
} : void 0,
|
|
476
|
+
onAnimationComplete: () => {
|
|
477
|
+
if (!isOpen) setIsCollapsed(true);
|
|
478
|
+
},
|
|
479
|
+
"data-collapsed": isCollapsed,
|
|
407
480
|
children: folderContent
|
|
408
481
|
}
|
|
409
482
|
);
|
|
@@ -412,9 +485,26 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
412
485
|
}
|
|
413
486
|
|
|
414
487
|
// src/components/Slider.tsx
|
|
415
|
-
import { useRef as
|
|
416
|
-
import { motion as motion2 } from "motion/react";
|
|
488
|
+
import { useRef as useRef3, useState as useState2, useCallback, useEffect as useEffect3 } from "react";
|
|
489
|
+
import { motion as motion2, useMotionValue, useTransform, animate } from "motion/react";
|
|
417
490
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
491
|
+
var CLICK_THRESHOLD = 3;
|
|
492
|
+
var DEAD_ZONE = 32;
|
|
493
|
+
var MAX_CURSOR_RANGE = 200;
|
|
494
|
+
var MAX_STRETCH = 8;
|
|
495
|
+
function roundValue(val, step) {
|
|
496
|
+
const decimals = step >= 1 ? 0 : 2;
|
|
497
|
+
const factor = 10 ** decimals;
|
|
498
|
+
return Math.round(val * factor) / factor;
|
|
499
|
+
}
|
|
500
|
+
function snapToDecile(rawValue, min, max) {
|
|
501
|
+
const normalized = (rawValue - min) / (max - min);
|
|
502
|
+
const nearest = Math.round(normalized * 10) / 10;
|
|
503
|
+
if (Math.abs(normalized - nearest) <= 0.03125) {
|
|
504
|
+
return min + nearest * (max - min);
|
|
505
|
+
}
|
|
506
|
+
return rawValue;
|
|
507
|
+
}
|
|
418
508
|
function Slider({
|
|
419
509
|
label,
|
|
420
510
|
value,
|
|
@@ -424,8 +514,11 @@ function Slider({
|
|
|
424
514
|
step = 0.01,
|
|
425
515
|
unit
|
|
426
516
|
}) {
|
|
427
|
-
const
|
|
428
|
-
const
|
|
517
|
+
const wrapperRef = useRef3(null);
|
|
518
|
+
const trackRef = useRef3(null);
|
|
519
|
+
const inputRef = useRef3(null);
|
|
520
|
+
const labelRef = useRef3(null);
|
|
521
|
+
const valueSpanRef = useRef3(null);
|
|
429
522
|
const [isInteracting, setIsInteracting] = useState2(false);
|
|
430
523
|
const [isDragging, setIsDragging] = useState2(false);
|
|
431
524
|
const [isHovered, setIsHovered] = useState2(false);
|
|
@@ -433,80 +526,162 @@ function Slider({
|
|
|
433
526
|
const [isValueEditable, setIsValueEditable] = useState2(false);
|
|
434
527
|
const [showInput, setShowInput] = useState2(false);
|
|
435
528
|
const [inputValue, setInputValue] = useState2("");
|
|
436
|
-
const hoverTimeoutRef =
|
|
529
|
+
const hoverTimeoutRef = useRef3(null);
|
|
530
|
+
const pointerDownPos = useRef3(null);
|
|
531
|
+
const isClickRef = useRef3(true);
|
|
532
|
+
const animRef = useRef3(null);
|
|
533
|
+
const wrapperRectRef = useRef3(null);
|
|
534
|
+
const scaleRef = useRef3(1);
|
|
437
535
|
const percentage = (value - min) / (max - min) * 100;
|
|
438
|
-
const isAtMinimum = value <= min;
|
|
439
536
|
const isActive = isInteracting || isHovered;
|
|
537
|
+
const fillPercent = useMotionValue(percentage);
|
|
538
|
+
const fillWidth = useTransform(fillPercent, (pct) => `${pct}%`);
|
|
539
|
+
const handleLeft = useTransform(
|
|
540
|
+
fillPercent,
|
|
541
|
+
(pct) => `max(5px, calc(${pct}% - 9px))`
|
|
542
|
+
);
|
|
543
|
+
const rubberStretchPx = useMotionValue(0);
|
|
544
|
+
const rubberBandWidth = useTransform(
|
|
545
|
+
rubberStretchPx,
|
|
546
|
+
(stretch) => `calc(100% + ${Math.abs(stretch)}px)`
|
|
547
|
+
);
|
|
548
|
+
const rubberBandX = useTransform(
|
|
549
|
+
rubberStretchPx,
|
|
550
|
+
(stretch) => stretch < 0 ? stretch : 0
|
|
551
|
+
);
|
|
552
|
+
useEffect3(() => {
|
|
553
|
+
if (!isInteracting && !animRef.current) {
|
|
554
|
+
fillPercent.jump(percentage);
|
|
555
|
+
}
|
|
556
|
+
}, [percentage, isInteracting, fillPercent]);
|
|
440
557
|
const positionToValue = useCallback(
|
|
441
558
|
(clientX) => {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const
|
|
445
|
-
const
|
|
559
|
+
const rect = wrapperRectRef.current;
|
|
560
|
+
if (!rect) return value;
|
|
561
|
+
const screenX = clientX - rect.left;
|
|
562
|
+
const sceneX = screenX / scaleRef.current;
|
|
563
|
+
const nativeWidth = wrapperRef.current ? wrapperRef.current.offsetWidth : rect.width;
|
|
564
|
+
const percent = Math.max(0, Math.min(1, sceneX / nativeWidth));
|
|
446
565
|
const rawValue = min + percent * (max - min);
|
|
447
566
|
return Math.max(min, Math.min(max, rawValue));
|
|
448
567
|
},
|
|
449
568
|
[min, max, value]
|
|
450
569
|
);
|
|
451
|
-
const
|
|
452
|
-
(
|
|
453
|
-
|
|
454
|
-
e.preventDefault();
|
|
455
|
-
setIsInteracting(true);
|
|
456
|
-
onChange(positionToValue(e.clientX));
|
|
457
|
-
},
|
|
458
|
-
[positionToValue, onChange, showInput]
|
|
570
|
+
const percentFromValue = useCallback(
|
|
571
|
+
(v) => (v - min) / (max - min) * 100,
|
|
572
|
+
[min, max]
|
|
459
573
|
);
|
|
460
|
-
const
|
|
461
|
-
(
|
|
462
|
-
|
|
463
|
-
if (!
|
|
464
|
-
|
|
574
|
+
const computeRubberStretch = useCallback(
|
|
575
|
+
(clientX, sign) => {
|
|
576
|
+
const rect = wrapperRectRef.current;
|
|
577
|
+
if (!rect) return 0;
|
|
578
|
+
const distancePast = sign < 0 ? rect.left - clientX : clientX - rect.right;
|
|
579
|
+
const overflow = Math.max(0, distancePast - DEAD_ZONE);
|
|
580
|
+
return sign * MAX_STRETCH * Math.sqrt(Math.min(overflow / MAX_CURSOR_RANGE, 1));
|
|
465
581
|
},
|
|
466
|
-
[
|
|
582
|
+
[]
|
|
467
583
|
);
|
|
468
|
-
const
|
|
469
|
-
setIsInteracting(false);
|
|
470
|
-
setIsDragging(false);
|
|
471
|
-
}, []);
|
|
472
|
-
const handleTouchStart = useCallback(
|
|
584
|
+
const handlePointerDown = useCallback(
|
|
473
585
|
(e) => {
|
|
474
586
|
if (showInput) return;
|
|
475
587
|
e.preventDefault();
|
|
588
|
+
e.target.setPointerCapture(e.pointerId);
|
|
589
|
+
pointerDownPos.current = { x: e.clientX, y: e.clientY };
|
|
590
|
+
isClickRef.current = true;
|
|
476
591
|
setIsInteracting(true);
|
|
477
|
-
|
|
478
|
-
|
|
592
|
+
if (wrapperRef.current) {
|
|
593
|
+
wrapperRectRef.current = wrapperRef.current.getBoundingClientRect();
|
|
594
|
+
const nativeWidth = wrapperRef.current.offsetWidth;
|
|
595
|
+
scaleRef.current = wrapperRectRef.current.width / nativeWidth;
|
|
596
|
+
}
|
|
479
597
|
},
|
|
480
|
-
[
|
|
598
|
+
[showInput]
|
|
481
599
|
);
|
|
482
|
-
const
|
|
600
|
+
const handlePointerMove = useCallback(
|
|
601
|
+
(e) => {
|
|
602
|
+
if (!isInteracting || !pointerDownPos.current) return;
|
|
603
|
+
const dx = e.clientX - pointerDownPos.current.x;
|
|
604
|
+
const dy = e.clientY - pointerDownPos.current.y;
|
|
605
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
606
|
+
if (isClickRef.current && distance > CLICK_THRESHOLD) {
|
|
607
|
+
isClickRef.current = false;
|
|
608
|
+
setIsDragging(true);
|
|
609
|
+
}
|
|
610
|
+
if (!isClickRef.current) {
|
|
611
|
+
const rect = wrapperRectRef.current;
|
|
612
|
+
if (rect) {
|
|
613
|
+
if (e.clientX < rect.left) {
|
|
614
|
+
rubberStretchPx.jump(computeRubberStretch(e.clientX, -1));
|
|
615
|
+
} else if (e.clientX > rect.right) {
|
|
616
|
+
rubberStretchPx.jump(computeRubberStretch(e.clientX, 1));
|
|
617
|
+
} else {
|
|
618
|
+
rubberStretchPx.jump(0);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
const newValue = positionToValue(e.clientX);
|
|
622
|
+
const newPct = percentFromValue(newValue);
|
|
623
|
+
if (animRef.current) {
|
|
624
|
+
animRef.current.stop();
|
|
625
|
+
animRef.current = null;
|
|
626
|
+
}
|
|
627
|
+
fillPercent.jump(newPct);
|
|
628
|
+
onChange(roundValue(newValue, step));
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
[
|
|
632
|
+
isInteracting,
|
|
633
|
+
positionToValue,
|
|
634
|
+
percentFromValue,
|
|
635
|
+
onChange,
|
|
636
|
+
fillPercent,
|
|
637
|
+
rubberStretchPx,
|
|
638
|
+
computeRubberStretch
|
|
639
|
+
]
|
|
640
|
+
);
|
|
641
|
+
const handlePointerUp = useCallback(
|
|
483
642
|
(e) => {
|
|
484
643
|
if (!isInteracting) return;
|
|
485
|
-
if (
|
|
486
|
-
|
|
487
|
-
|
|
644
|
+
if (isClickRef.current) {
|
|
645
|
+
const rawValue = positionToValue(e.clientX);
|
|
646
|
+
const snappedValue = snapToDecile(rawValue, min, max);
|
|
647
|
+
const newPct = percentFromValue(snappedValue);
|
|
648
|
+
if (animRef.current) {
|
|
649
|
+
animRef.current.stop();
|
|
650
|
+
}
|
|
651
|
+
animRef.current = animate(fillPercent, newPct, {
|
|
652
|
+
type: "spring",
|
|
653
|
+
stiffness: 300,
|
|
654
|
+
damping: 25,
|
|
655
|
+
mass: 0.8,
|
|
656
|
+
onComplete: () => {
|
|
657
|
+
animRef.current = null;
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
onChange(roundValue(snappedValue, step));
|
|
661
|
+
}
|
|
662
|
+
if (rubberStretchPx.get() !== 0) {
|
|
663
|
+
animate(rubberStretchPx, 0, {
|
|
664
|
+
type: "spring",
|
|
665
|
+
visualDuration: 0.35,
|
|
666
|
+
bounce: 0.15
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
setIsInteracting(false);
|
|
670
|
+
setIsDragging(false);
|
|
671
|
+
pointerDownPos.current = null;
|
|
488
672
|
},
|
|
489
|
-
[
|
|
673
|
+
[
|
|
674
|
+
isInteracting,
|
|
675
|
+
positionToValue,
|
|
676
|
+
percentFromValue,
|
|
677
|
+
onChange,
|
|
678
|
+
min,
|
|
679
|
+
max,
|
|
680
|
+
fillPercent,
|
|
681
|
+
rubberStretchPx
|
|
682
|
+
]
|
|
490
683
|
);
|
|
491
|
-
|
|
492
|
-
setIsInteracting(false);
|
|
493
|
-
setIsDragging(false);
|
|
494
|
-
}, []);
|
|
495
|
-
useEffect2(() => {
|
|
496
|
-
if (isInteracting) {
|
|
497
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
498
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
499
|
-
document.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
500
|
-
document.addEventListener("touchend", handleTouchEnd);
|
|
501
|
-
return () => {
|
|
502
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
503
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
504
|
-
document.removeEventListener("touchmove", handleTouchMove);
|
|
505
|
-
document.removeEventListener("touchend", handleTouchEnd);
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
}, [isInteracting, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
|
|
509
|
-
useEffect2(() => {
|
|
684
|
+
useEffect3(() => {
|
|
510
685
|
if (isValueHovered && !showInput && !isValueEditable) {
|
|
511
686
|
hoverTimeoutRef.current = setTimeout(() => {
|
|
512
687
|
setIsValueEditable(true);
|
|
@@ -524,7 +699,7 @@ function Slider({
|
|
|
524
699
|
}
|
|
525
700
|
};
|
|
526
701
|
}, [isValueHovered, showInput, isValueEditable]);
|
|
527
|
-
|
|
702
|
+
useEffect3(() => {
|
|
528
703
|
if (showInput && inputRef.current) {
|
|
529
704
|
inputRef.current.focus();
|
|
530
705
|
inputRef.current.select();
|
|
@@ -537,7 +712,7 @@ function Slider({
|
|
|
537
712
|
const parsed = parseFloat(inputValue);
|
|
538
713
|
if (!isNaN(parsed)) {
|
|
539
714
|
const clamped = Math.max(min, Math.min(max, parsed));
|
|
540
|
-
onChange(clamped);
|
|
715
|
+
onChange(roundValue(clamped, step));
|
|
541
716
|
}
|
|
542
717
|
setShowInput(false);
|
|
543
718
|
setIsValueHovered(false);
|
|
@@ -563,29 +738,56 @@ function Slider({
|
|
|
563
738
|
handleInputSubmit();
|
|
564
739
|
};
|
|
565
740
|
const displayValue = step >= 1 ? value.toFixed(0) : value.toFixed(2);
|
|
566
|
-
const
|
|
567
|
-
const
|
|
568
|
-
const
|
|
741
|
+
const HANDLE_BUFFER = 8;
|
|
742
|
+
const LABEL_CSS_LEFT = 10;
|
|
743
|
+
const VALUE_CSS_RIGHT = 10;
|
|
744
|
+
let leftThreshold = 30;
|
|
745
|
+
let rightThreshold = 78;
|
|
746
|
+
const trackWidth = wrapperRef.current?.offsetWidth;
|
|
747
|
+
if (trackWidth && trackWidth > 0) {
|
|
748
|
+
if (labelRef.current) {
|
|
749
|
+
leftThreshold = (LABEL_CSS_LEFT + labelRef.current.offsetWidth + HANDLE_BUFFER) / trackWidth * 100;
|
|
750
|
+
}
|
|
751
|
+
if (valueSpanRef.current) {
|
|
752
|
+
rightThreshold = (trackWidth - VALUE_CSS_RIGHT - valueSpanRef.current.offsetWidth - HANDLE_BUFFER) / trackWidth * 100;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const valueDodge = percentage < leftThreshold || percentage > rightThreshold;
|
|
756
|
+
const handleOpacity = !isActive ? 0 : valueDodge ? 0.1 : isDragging ? 0.9 : 0.5;
|
|
569
757
|
const fillBackground = isActive ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.11)";
|
|
570
|
-
const hashMarks = Array.from({ length:
|
|
571
|
-
|
|
572
|
-
|
|
758
|
+
const hashMarks = Array.from({ length: 9 }, (_, i) => {
|
|
759
|
+
const pct = (i + 1) * 10;
|
|
760
|
+
return /* @__PURE__ */ jsx2(
|
|
761
|
+
"div",
|
|
762
|
+
{
|
|
763
|
+
className: "dialkit-slider-hashmark",
|
|
764
|
+
style: { left: `${pct}%` }
|
|
765
|
+
},
|
|
766
|
+
i
|
|
767
|
+
);
|
|
768
|
+
});
|
|
769
|
+
return /* @__PURE__ */ jsx2("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ jsxs2(
|
|
770
|
+
motion2.div,
|
|
573
771
|
{
|
|
574
772
|
ref: trackRef,
|
|
575
773
|
className: `dialkit-slider ${isActive ? "dialkit-slider-active" : ""}`,
|
|
576
|
-
|
|
577
|
-
|
|
774
|
+
onPointerDown: handlePointerDown,
|
|
775
|
+
onPointerMove: handlePointerMove,
|
|
776
|
+
onPointerUp: handlePointerUp,
|
|
578
777
|
onMouseEnter: () => setIsHovered(true),
|
|
579
778
|
onMouseLeave: () => setIsHovered(false),
|
|
779
|
+
style: { width: rubberBandWidth, x: rubberBandX },
|
|
580
780
|
children: [
|
|
581
781
|
/* @__PURE__ */ jsx2("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
|
|
582
782
|
/* @__PURE__ */ jsx2(
|
|
583
783
|
motion2.div,
|
|
584
784
|
{
|
|
585
785
|
className: "dialkit-slider-fill",
|
|
586
|
-
style: {
|
|
587
|
-
|
|
588
|
-
|
|
786
|
+
style: {
|
|
787
|
+
background: fillBackground,
|
|
788
|
+
width: fillWidth,
|
|
789
|
+
transition: "background 0.15s"
|
|
790
|
+
}
|
|
589
791
|
}
|
|
590
792
|
),
|
|
591
793
|
/* @__PURE__ */ jsx2(
|
|
@@ -593,15 +795,23 @@ function Slider({
|
|
|
593
795
|
{
|
|
594
796
|
className: "dialkit-slider-handle",
|
|
595
797
|
style: {
|
|
596
|
-
background: isInteracting ? "rgba(255, 255, 255, 1)" : "rgba(255, 255, 255, 0.5)",
|
|
597
798
|
left: handleLeft,
|
|
598
|
-
|
|
799
|
+
y: "-50%",
|
|
800
|
+
background: "rgba(255, 255, 255, 0.9)"
|
|
599
801
|
},
|
|
600
|
-
animate: {
|
|
601
|
-
|
|
802
|
+
animate: {
|
|
803
|
+
opacity: handleOpacity,
|
|
804
|
+
scaleX: isActive ? 1 : 0.25,
|
|
805
|
+
scaleY: isActive && valueDodge ? 0.75 : 1
|
|
806
|
+
},
|
|
807
|
+
transition: {
|
|
808
|
+
scaleX: { type: "spring", visualDuration: 0.25, bounce: 0.15 },
|
|
809
|
+
scaleY: { type: "spring", visualDuration: 0.2, bounce: 0.1 },
|
|
810
|
+
opacity: { duration: 0.15 }
|
|
811
|
+
}
|
|
602
812
|
}
|
|
603
813
|
),
|
|
604
|
-
/* @__PURE__ */ jsx2("span", { className: "dialkit-slider-label", children: label }),
|
|
814
|
+
/* @__PURE__ */ jsx2("span", { ref: labelRef, className: "dialkit-slider-label", children: label }),
|
|
605
815
|
showInput ? /* @__PURE__ */ jsx2(
|
|
606
816
|
"input",
|
|
607
817
|
{
|
|
@@ -615,28 +825,26 @@ function Slider({
|
|
|
615
825
|
onClick: (e) => e.stopPropagation(),
|
|
616
826
|
onMouseDown: (e) => e.stopPropagation()
|
|
617
827
|
}
|
|
618
|
-
) : /* @__PURE__ */
|
|
828
|
+
) : /* @__PURE__ */ jsx2(
|
|
619
829
|
"span",
|
|
620
830
|
{
|
|
831
|
+
ref: valueSpanRef,
|
|
621
832
|
className: `dialkit-slider-value ${isValueEditable ? "dialkit-slider-value-editable" : ""}`,
|
|
622
833
|
onMouseEnter: () => setIsValueHovered(true),
|
|
623
834
|
onMouseLeave: () => setIsValueHovered(false),
|
|
624
835
|
onClick: handleValueClick,
|
|
625
836
|
onMouseDown: (e) => isValueEditable && e.stopPropagation(),
|
|
626
837
|
style: { cursor: isValueEditable ? "text" : "default" },
|
|
627
|
-
children:
|
|
628
|
-
displayValue,
|
|
629
|
-
unit
|
|
630
|
-
]
|
|
838
|
+
children: displayValue
|
|
631
839
|
}
|
|
632
840
|
)
|
|
633
841
|
]
|
|
634
842
|
}
|
|
635
|
-
);
|
|
843
|
+
) });
|
|
636
844
|
}
|
|
637
845
|
|
|
638
846
|
// src/components/SegmentedControl.tsx
|
|
639
|
-
import { useRef as
|
|
847
|
+
import { useRef as useRef4, useState as useState3, useLayoutEffect } from "react";
|
|
640
848
|
import { motion as motion3 } from "motion/react";
|
|
641
849
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
642
850
|
function SegmentedControl({
|
|
@@ -644,10 +852,10 @@ function SegmentedControl({
|
|
|
644
852
|
value,
|
|
645
853
|
onChange
|
|
646
854
|
}) {
|
|
647
|
-
const containerRef =
|
|
648
|
-
const buttonRefs =
|
|
855
|
+
const containerRef = useRef4(null);
|
|
856
|
+
const buttonRefs = useRef4(/* @__PURE__ */ new Map());
|
|
649
857
|
const [pillStyle, setPillStyle] = useState3(null);
|
|
650
|
-
const hasAnimated =
|
|
858
|
+
const hasAnimated = useRef4(false);
|
|
651
859
|
useLayoutEffect(() => {
|
|
652
860
|
const button = buttonRefs.current.get(value);
|
|
653
861
|
const container = containerRef.current;
|
|
@@ -917,20 +1125,349 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
|
|
|
917
1125
|
] }) });
|
|
918
1126
|
}
|
|
919
1127
|
|
|
920
|
-
// src/components/
|
|
921
|
-
import { useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
1128
|
+
// src/components/TextControl.tsx
|
|
922
1129
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1130
|
+
function TextControl({ label, value, onChange, placeholder }) {
|
|
1131
|
+
return /* @__PURE__ */ jsxs7("div", { className: "dialkit-text-control", children: [
|
|
1132
|
+
/* @__PURE__ */ jsx7("label", { className: "dialkit-text-label", children: label }),
|
|
1133
|
+
/* @__PURE__ */ jsx7(
|
|
1134
|
+
"input",
|
|
1135
|
+
{
|
|
1136
|
+
type: "text",
|
|
1137
|
+
className: "dialkit-text-input",
|
|
1138
|
+
value,
|
|
1139
|
+
onChange: (e) => onChange(e.target.value),
|
|
1140
|
+
placeholder
|
|
1141
|
+
}
|
|
1142
|
+
)
|
|
1143
|
+
] });
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// src/components/SelectControl.tsx
|
|
1147
|
+
import { useState as useState4, useRef as useRef5, useEffect as useEffect4 } from "react";
|
|
1148
|
+
import { motion as motion4, AnimatePresence as AnimatePresence2 } from "motion/react";
|
|
1149
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1150
|
+
function normalizeOptions(options) {
|
|
1151
|
+
return options.map(
|
|
1152
|
+
(opt) => typeof opt === "string" ? { value: opt, label: opt } : opt
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
function SelectControl({ label, value, options, onChange }) {
|
|
1156
|
+
const [isOpen, setIsOpen] = useState4(false);
|
|
1157
|
+
const containerRef = useRef5(null);
|
|
1158
|
+
const normalized = normalizeOptions(options);
|
|
1159
|
+
const selectedOption = normalized.find((o) => o.value === value);
|
|
1160
|
+
useEffect4(() => {
|
|
1161
|
+
if (!isOpen) return;
|
|
1162
|
+
const handleClick = (e) => {
|
|
1163
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
1164
|
+
setIsOpen(false);
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
document.addEventListener("mousedown", handleClick);
|
|
1168
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
1169
|
+
}, [isOpen]);
|
|
1170
|
+
return /* @__PURE__ */ jsxs8("div", { ref: containerRef, className: "dialkit-select-row", children: [
|
|
1171
|
+
/* @__PURE__ */ jsxs8(
|
|
1172
|
+
"button",
|
|
1173
|
+
{
|
|
1174
|
+
className: "dialkit-select-trigger",
|
|
1175
|
+
onClick: () => setIsOpen(!isOpen),
|
|
1176
|
+
"data-open": String(isOpen),
|
|
1177
|
+
children: [
|
|
1178
|
+
/* @__PURE__ */ jsx8("span", { className: "dialkit-select-label", children: label }),
|
|
1179
|
+
/* @__PURE__ */ jsxs8("div", { className: "dialkit-select-right", children: [
|
|
1180
|
+
/* @__PURE__ */ jsx8("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
|
|
1181
|
+
/* @__PURE__ */ jsx8(
|
|
1182
|
+
motion4.svg,
|
|
1183
|
+
{
|
|
1184
|
+
className: "dialkit-select-chevron",
|
|
1185
|
+
viewBox: "0 0 24 24",
|
|
1186
|
+
fill: "none",
|
|
1187
|
+
stroke: "currentColor",
|
|
1188
|
+
strokeWidth: "2.5",
|
|
1189
|
+
strokeLinecap: "round",
|
|
1190
|
+
strokeLinejoin: "round",
|
|
1191
|
+
animate: { rotate: isOpen ? 180 : 0 },
|
|
1192
|
+
transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
|
|
1193
|
+
children: /* @__PURE__ */ jsx8("path", { d: "M6 9.5L12 15.5L18 9.5" })
|
|
1194
|
+
}
|
|
1195
|
+
)
|
|
1196
|
+
] })
|
|
1197
|
+
]
|
|
1198
|
+
}
|
|
1199
|
+
),
|
|
1200
|
+
/* @__PURE__ */ jsx8(AnimatePresence2, { children: isOpen && /* @__PURE__ */ jsx8(
|
|
1201
|
+
motion4.div,
|
|
1202
|
+
{
|
|
1203
|
+
className: "dialkit-select-dropdown",
|
|
1204
|
+
initial: { opacity: 0, y: -8, scale: 0.95 },
|
|
1205
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
1206
|
+
exit: { opacity: 0, y: -8, scale: 0.95 },
|
|
1207
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
1208
|
+
children: normalized.map((option) => /* @__PURE__ */ jsx8(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
className: "dialkit-select-option",
|
|
1212
|
+
"data-selected": String(option.value === value),
|
|
1213
|
+
onClick: () => {
|
|
1214
|
+
onChange(option.value);
|
|
1215
|
+
setIsOpen(false);
|
|
1216
|
+
},
|
|
1217
|
+
children: option.label
|
|
1218
|
+
},
|
|
1219
|
+
option.value
|
|
1220
|
+
))
|
|
1221
|
+
}
|
|
1222
|
+
) })
|
|
1223
|
+
] });
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// src/components/ColorControl.tsx
|
|
1227
|
+
import { useState as useState5, useRef as useRef6, useEffect as useEffect5 } from "react";
|
|
1228
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1229
|
+
var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
1230
|
+
function ColorControl({ label, value, onChange }) {
|
|
1231
|
+
const [isEditing, setIsEditing] = useState5(false);
|
|
1232
|
+
const [editValue, setEditValue] = useState5(value);
|
|
1233
|
+
const colorInputRef = useRef6(null);
|
|
1234
|
+
useEffect5(() => {
|
|
1235
|
+
if (!isEditing) {
|
|
1236
|
+
setEditValue(value);
|
|
1237
|
+
}
|
|
1238
|
+
}, [value, isEditing]);
|
|
1239
|
+
function handleTextSubmit() {
|
|
1240
|
+
setIsEditing(false);
|
|
1241
|
+
if (HEX_COLOR_REGEX.test(editValue)) {
|
|
1242
|
+
onChange(editValue);
|
|
1243
|
+
} else {
|
|
1244
|
+
setEditValue(value);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
function handleKeyDown(e) {
|
|
1248
|
+
if (e.key === "Enter") {
|
|
1249
|
+
handleTextSubmit();
|
|
1250
|
+
} else if (e.key === "Escape") {
|
|
1251
|
+
setIsEditing(false);
|
|
1252
|
+
setEditValue(value);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
return /* @__PURE__ */ jsxs9("div", { className: "dialkit-color-control", children: [
|
|
1256
|
+
/* @__PURE__ */ jsx9("span", { className: "dialkit-color-label", children: label }),
|
|
1257
|
+
/* @__PURE__ */ jsxs9("div", { className: "dialkit-color-inputs", children: [
|
|
1258
|
+
isEditing ? /* @__PURE__ */ jsx9(
|
|
1259
|
+
"input",
|
|
1260
|
+
{
|
|
1261
|
+
type: "text",
|
|
1262
|
+
className: "dialkit-color-hex-input",
|
|
1263
|
+
value: editValue,
|
|
1264
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
1265
|
+
onBlur: handleTextSubmit,
|
|
1266
|
+
onKeyDown: handleKeyDown,
|
|
1267
|
+
autoFocus: true
|
|
1268
|
+
}
|
|
1269
|
+
) : /* @__PURE__ */ jsx9(
|
|
1270
|
+
"span",
|
|
1271
|
+
{
|
|
1272
|
+
className: "dialkit-color-hex",
|
|
1273
|
+
onClick: () => setIsEditing(true),
|
|
1274
|
+
children: (value ?? "").toUpperCase()
|
|
1275
|
+
}
|
|
1276
|
+
),
|
|
1277
|
+
/* @__PURE__ */ jsx9(
|
|
1278
|
+
"button",
|
|
1279
|
+
{
|
|
1280
|
+
className: "dialkit-color-swatch",
|
|
1281
|
+
style: { backgroundColor: value },
|
|
1282
|
+
onClick: () => colorInputRef.current?.click(),
|
|
1283
|
+
title: "Pick color"
|
|
1284
|
+
}
|
|
1285
|
+
),
|
|
1286
|
+
/* @__PURE__ */ jsx9(
|
|
1287
|
+
"input",
|
|
1288
|
+
{
|
|
1289
|
+
ref: colorInputRef,
|
|
1290
|
+
type: "color",
|
|
1291
|
+
className: "dialkit-color-picker-native",
|
|
1292
|
+
value: value.length === 4 ? expandShorthandHex(value) : value.slice(0, 7),
|
|
1293
|
+
onChange: (e) => onChange(e.target.value)
|
|
1294
|
+
}
|
|
1295
|
+
)
|
|
1296
|
+
] })
|
|
1297
|
+
] });
|
|
1298
|
+
}
|
|
1299
|
+
function expandShorthandHex(hex) {
|
|
1300
|
+
if (hex.length !== 4) return hex;
|
|
1301
|
+
return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// src/components/PresetManager.tsx
|
|
1305
|
+
import { useState as useState6, useRef as useRef7, useEffect as useEffect6, useCallback as useCallback2 } from "react";
|
|
1306
|
+
import { createPortal } from "react-dom";
|
|
1307
|
+
import { motion as motion5, AnimatePresence as AnimatePresence3 } from "motion/react";
|
|
1308
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1309
|
+
function PresetManager({ panelId, presets, activePresetId, onAdd }) {
|
|
1310
|
+
const [isOpen, setIsOpen] = useState6(false);
|
|
1311
|
+
const triggerRef = useRef7(null);
|
|
1312
|
+
const dropdownRef = useRef7(null);
|
|
1313
|
+
const [pos, setPos] = useState6({ top: 0, left: 0, width: 0 });
|
|
1314
|
+
const hasPresets = presets.length > 0;
|
|
1315
|
+
const activePreset = presets.find((p) => p.id === activePresetId);
|
|
1316
|
+
const open = useCallback2(() => {
|
|
1317
|
+
if (!hasPresets) return;
|
|
1318
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
1319
|
+
if (rect) {
|
|
1320
|
+
setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
|
|
1321
|
+
}
|
|
1322
|
+
setIsOpen(true);
|
|
1323
|
+
}, [hasPresets]);
|
|
1324
|
+
const close = useCallback2(() => setIsOpen(false), []);
|
|
1325
|
+
const toggle = useCallback2(() => {
|
|
1326
|
+
if (isOpen) close();
|
|
1327
|
+
else open();
|
|
1328
|
+
}, [isOpen, open, close]);
|
|
1329
|
+
useEffect6(() => {
|
|
1330
|
+
if (!isOpen) return;
|
|
1331
|
+
const handler = (e) => {
|
|
1332
|
+
const target = e.target;
|
|
1333
|
+
if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
|
|
1334
|
+
close();
|
|
1335
|
+
};
|
|
1336
|
+
document.addEventListener("mousedown", handler);
|
|
1337
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
1338
|
+
}, [isOpen, close]);
|
|
1339
|
+
const handleSelect = (presetId) => {
|
|
1340
|
+
if (presetId) {
|
|
1341
|
+
DialStore.loadPreset(panelId, presetId);
|
|
1342
|
+
} else {
|
|
1343
|
+
DialStore.clearActivePreset(panelId);
|
|
1344
|
+
}
|
|
1345
|
+
close();
|
|
1346
|
+
};
|
|
1347
|
+
const handleDelete = (e, presetId) => {
|
|
1348
|
+
e.stopPropagation();
|
|
1349
|
+
DialStore.deletePreset(panelId, presetId);
|
|
1350
|
+
};
|
|
1351
|
+
return /* @__PURE__ */ jsxs10("div", { className: "dialkit-preset-manager", children: [
|
|
1352
|
+
/* @__PURE__ */ jsxs10(
|
|
1353
|
+
"button",
|
|
1354
|
+
{
|
|
1355
|
+
ref: triggerRef,
|
|
1356
|
+
className: "dialkit-preset-trigger",
|
|
1357
|
+
onClick: toggle,
|
|
1358
|
+
"data-open": String(isOpen),
|
|
1359
|
+
"data-has-preset": String(!!activePreset),
|
|
1360
|
+
"data-disabled": String(!hasPresets),
|
|
1361
|
+
children: [
|
|
1362
|
+
/* @__PURE__ */ jsx10("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
|
|
1363
|
+
/* @__PURE__ */ jsx10(
|
|
1364
|
+
motion5.svg,
|
|
1365
|
+
{
|
|
1366
|
+
className: "dialkit-select-chevron",
|
|
1367
|
+
viewBox: "0 0 24 24",
|
|
1368
|
+
fill: "none",
|
|
1369
|
+
stroke: "currentColor",
|
|
1370
|
+
strokeWidth: "2.5",
|
|
1371
|
+
strokeLinecap: "round",
|
|
1372
|
+
strokeLinejoin: "round",
|
|
1373
|
+
animate: { rotate: isOpen ? 180 : 0 },
|
|
1374
|
+
transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
|
|
1375
|
+
children: /* @__PURE__ */ jsx10("path", { d: "M6 9.5L12 15.5L18 9.5" })
|
|
1376
|
+
}
|
|
1377
|
+
)
|
|
1378
|
+
]
|
|
1379
|
+
}
|
|
1380
|
+
),
|
|
1381
|
+
createPortal(
|
|
1382
|
+
/* @__PURE__ */ jsx10(AnimatePresence3, { children: isOpen && /* @__PURE__ */ jsxs10(
|
|
1383
|
+
motion5.div,
|
|
1384
|
+
{
|
|
1385
|
+
ref: dropdownRef,
|
|
1386
|
+
className: "dialkit-preset-dropdown",
|
|
1387
|
+
style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.width },
|
|
1388
|
+
initial: { opacity: 0, y: 4, scale: 0.97 },
|
|
1389
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
1390
|
+
exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
|
|
1391
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
1392
|
+
children: [
|
|
1393
|
+
/* @__PURE__ */ jsx10(
|
|
1394
|
+
"div",
|
|
1395
|
+
{
|
|
1396
|
+
className: "dialkit-preset-item",
|
|
1397
|
+
"data-active": String(!activePresetId),
|
|
1398
|
+
onClick: () => handleSelect(null),
|
|
1399
|
+
children: /* @__PURE__ */ jsx10("span", { className: "dialkit-preset-name", children: "Version 1" })
|
|
1400
|
+
}
|
|
1401
|
+
),
|
|
1402
|
+
presets.map((preset) => /* @__PURE__ */ jsxs10(
|
|
1403
|
+
"div",
|
|
1404
|
+
{
|
|
1405
|
+
className: "dialkit-preset-item",
|
|
1406
|
+
"data-active": String(preset.id === activePresetId),
|
|
1407
|
+
onClick: () => handleSelect(preset.id),
|
|
1408
|
+
children: [
|
|
1409
|
+
/* @__PURE__ */ jsx10("span", { className: "dialkit-preset-name", children: preset.name }),
|
|
1410
|
+
/* @__PURE__ */ jsx10(
|
|
1411
|
+
"button",
|
|
1412
|
+
{
|
|
1413
|
+
className: "dialkit-preset-delete",
|
|
1414
|
+
onClick: (e) => handleDelete(e, preset.id),
|
|
1415
|
+
title: "Delete preset",
|
|
1416
|
+
children: /* @__PURE__ */ jsxs10("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1417
|
+
/* @__PURE__ */ jsx10("path", { d: "M5 6.5L5.80734 18.2064C5.91582 19.7794 7.22348 21 8.80023 21H15.1998C16.7765 21 18.0842 19.7794 18.1927 18.2064L19 6.5" }),
|
|
1418
|
+
/* @__PURE__ */ jsx10("path", { d: "M10 11V16" }),
|
|
1419
|
+
/* @__PURE__ */ jsx10("path", { d: "M14 11V16" }),
|
|
1420
|
+
/* @__PURE__ */ jsx10("path", { d: "M3.5 6H20.5" }),
|
|
1421
|
+
/* @__PURE__ */ jsx10("path", { d: "M8.07092 5.74621C8.42348 3.89745 10.0485 2.5 12 2.5C13.9515 2.5 15.5765 3.89745 15.9291 5.74621" })
|
|
1422
|
+
] })
|
|
1423
|
+
}
|
|
1424
|
+
)
|
|
1425
|
+
]
|
|
1426
|
+
},
|
|
1427
|
+
preset.id
|
|
1428
|
+
))
|
|
1429
|
+
]
|
|
1430
|
+
}
|
|
1431
|
+
) }),
|
|
1432
|
+
document.body
|
|
1433
|
+
)
|
|
1434
|
+
] });
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// src/components/Panel.tsx
|
|
1438
|
+
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
923
1439
|
function Panel({ panel }) {
|
|
1440
|
+
const [copied, setCopied] = useState7(false);
|
|
1441
|
+
const [isPanelOpen, setIsPanelOpen] = useState7(true);
|
|
924
1442
|
const values = useSyncExternalStore3(
|
|
925
1443
|
(cb) => DialStore.subscribe(panel.id, cb),
|
|
926
1444
|
() => DialStore.getValues(panel.id),
|
|
927
1445
|
() => DialStore.getValues(panel.id)
|
|
928
1446
|
);
|
|
1447
|
+
const presets = DialStore.getPresets(panel.id);
|
|
1448
|
+
const activePresetId = DialStore.getActivePresetId(panel.id);
|
|
1449
|
+
const handleAddPreset = () => {
|
|
1450
|
+
const nextNum = presets.length + 2;
|
|
1451
|
+
DialStore.savePreset(panel.id, `Version ${nextNum}`);
|
|
1452
|
+
};
|
|
1453
|
+
const handleCopy = () => {
|
|
1454
|
+
const jsonStr = JSON.stringify(values, null, 2);
|
|
1455
|
+
const instruction = `Update the useDialKit configuration for "${panel.name}" with these values:
|
|
1456
|
+
|
|
1457
|
+
\`\`\`json
|
|
1458
|
+
${jsonStr}
|
|
1459
|
+
\`\`\`
|
|
1460
|
+
|
|
1461
|
+
Apply these values as the new defaults in the useDialKit call.`;
|
|
1462
|
+
navigator.clipboard.writeText(instruction);
|
|
1463
|
+
setCopied(true);
|
|
1464
|
+
setTimeout(() => setCopied(false), 1500);
|
|
1465
|
+
};
|
|
929
1466
|
const renderControl = (control) => {
|
|
930
1467
|
const value = values[control.path];
|
|
931
1468
|
switch (control.type) {
|
|
932
1469
|
case "slider":
|
|
933
|
-
return /* @__PURE__ */
|
|
1470
|
+
return /* @__PURE__ */ jsx11(
|
|
934
1471
|
Slider,
|
|
935
1472
|
{
|
|
936
1473
|
label: control.label,
|
|
@@ -943,7 +1480,7 @@ function Panel({ panel }) {
|
|
|
943
1480
|
control.path
|
|
944
1481
|
);
|
|
945
1482
|
case "toggle":
|
|
946
|
-
return /* @__PURE__ */
|
|
1483
|
+
return /* @__PURE__ */ jsx11(
|
|
947
1484
|
Toggle,
|
|
948
1485
|
{
|
|
949
1486
|
label: control.label,
|
|
@@ -953,7 +1490,7 @@ function Panel({ panel }) {
|
|
|
953
1490
|
control.path
|
|
954
1491
|
);
|
|
955
1492
|
case "spring":
|
|
956
|
-
return /* @__PURE__ */
|
|
1493
|
+
return /* @__PURE__ */ jsx11(
|
|
957
1494
|
SpringControl,
|
|
958
1495
|
{
|
|
959
1496
|
panelId: panel.id,
|
|
@@ -965,7 +1502,39 @@ function Panel({ panel }) {
|
|
|
965
1502
|
control.path
|
|
966
1503
|
);
|
|
967
1504
|
case "folder":
|
|
968
|
-
return /* @__PURE__ */
|
|
1505
|
+
return /* @__PURE__ */ jsx11(Folder, { title: control.label, children: control.children?.map(renderControl) }, control.path);
|
|
1506
|
+
case "text":
|
|
1507
|
+
return /* @__PURE__ */ jsx11(
|
|
1508
|
+
TextControl,
|
|
1509
|
+
{
|
|
1510
|
+
label: control.label,
|
|
1511
|
+
value,
|
|
1512
|
+
onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
|
|
1513
|
+
placeholder: control.placeholder
|
|
1514
|
+
},
|
|
1515
|
+
control.path
|
|
1516
|
+
);
|
|
1517
|
+
case "select":
|
|
1518
|
+
return /* @__PURE__ */ jsx11(
|
|
1519
|
+
SelectControl,
|
|
1520
|
+
{
|
|
1521
|
+
label: control.label,
|
|
1522
|
+
value,
|
|
1523
|
+
options: control.options ?? [],
|
|
1524
|
+
onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
|
|
1525
|
+
},
|
|
1526
|
+
control.path
|
|
1527
|
+
);
|
|
1528
|
+
case "color":
|
|
1529
|
+
return /* @__PURE__ */ jsx11(
|
|
1530
|
+
ColorControl,
|
|
1531
|
+
{
|
|
1532
|
+
label: control.label,
|
|
1533
|
+
value,
|
|
1534
|
+
onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
|
|
1535
|
+
},
|
|
1536
|
+
control.path
|
|
1537
|
+
);
|
|
969
1538
|
default:
|
|
970
1539
|
return null;
|
|
971
1540
|
}
|
|
@@ -976,24 +1545,16 @@ function Panel({ panel }) {
|
|
|
976
1545
|
while (i < panel.controls.length) {
|
|
977
1546
|
const control = panel.controls[i];
|
|
978
1547
|
if (control.type === "action") {
|
|
979
|
-
const actions = [control];
|
|
980
|
-
while (i + 1 < panel.controls.length && panel.controls[i + 1].type === "action") {
|
|
981
|
-
i++;
|
|
982
|
-
actions.push(panel.controls[i]);
|
|
983
|
-
}
|
|
984
1548
|
result.push(
|
|
985
|
-
/* @__PURE__ */
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
"button",
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
action.path
|
|
995
|
-
)) })
|
|
996
|
-
] }, `actions-${actions[0].path}`)
|
|
1549
|
+
/* @__PURE__ */ jsx11(
|
|
1550
|
+
"button",
|
|
1551
|
+
{
|
|
1552
|
+
className: "dialkit-button",
|
|
1553
|
+
onClick: () => DialStore.triggerAction(panel.id, control.path),
|
|
1554
|
+
children: control.label
|
|
1555
|
+
},
|
|
1556
|
+
control.path
|
|
1557
|
+
)
|
|
997
1558
|
);
|
|
998
1559
|
} else {
|
|
999
1560
|
result.push(renderControl(control));
|
|
@@ -1002,15 +1563,88 @@ function Panel({ panel }) {
|
|
|
1002
1563
|
}
|
|
1003
1564
|
return result;
|
|
1004
1565
|
};
|
|
1005
|
-
|
|
1566
|
+
const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
|
|
1567
|
+
const toolbar = /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
1568
|
+
/* @__PURE__ */ jsx11(
|
|
1569
|
+
"button",
|
|
1570
|
+
{
|
|
1571
|
+
className: "dialkit-toolbar-add",
|
|
1572
|
+
onClick: handleAddPreset,
|
|
1573
|
+
title: "Add preset",
|
|
1574
|
+
children: /* @__PURE__ */ jsxs11("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1575
|
+
/* @__PURE__ */ jsx11("path", { d: "M4 6H20" }),
|
|
1576
|
+
/* @__PURE__ */ jsx11("path", { d: "M4 12H10" }),
|
|
1577
|
+
/* @__PURE__ */ jsx11("path", { d: "M15 15L21 15" }),
|
|
1578
|
+
/* @__PURE__ */ jsx11("path", { d: "M18 12V18" }),
|
|
1579
|
+
/* @__PURE__ */ jsx11("path", { d: "M4 18H10" })
|
|
1580
|
+
] })
|
|
1581
|
+
}
|
|
1582
|
+
),
|
|
1583
|
+
/* @__PURE__ */ jsx11(
|
|
1584
|
+
PresetManager,
|
|
1585
|
+
{
|
|
1586
|
+
panelId: panel.id,
|
|
1587
|
+
presets,
|
|
1588
|
+
activePresetId,
|
|
1589
|
+
onAdd: handleAddPreset
|
|
1590
|
+
}
|
|
1591
|
+
),
|
|
1592
|
+
/* @__PURE__ */ jsxs11(
|
|
1593
|
+
"button",
|
|
1594
|
+
{
|
|
1595
|
+
className: "dialkit-toolbar-copy",
|
|
1596
|
+
onClick: handleCopy,
|
|
1597
|
+
title: "Copy parameters",
|
|
1598
|
+
children: [
|
|
1599
|
+
/* @__PURE__ */ jsx11("span", { className: "dialkit-toolbar-copy-icon-wrap", children: /* @__PURE__ */ jsx11(AnimatePresence4, { mode: "popLayout", children: copied ? /* @__PURE__ */ jsx11(
|
|
1600
|
+
motion6.svg,
|
|
1601
|
+
{
|
|
1602
|
+
className: "dialkit-toolbar-copy-icon",
|
|
1603
|
+
viewBox: "0 0 24 24",
|
|
1604
|
+
fill: "none",
|
|
1605
|
+
stroke: "currentColor",
|
|
1606
|
+
strokeWidth: "2",
|
|
1607
|
+
strokeLinecap: "round",
|
|
1608
|
+
strokeLinejoin: "round",
|
|
1609
|
+
initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1610
|
+
animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
|
|
1611
|
+
exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1612
|
+
transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
1613
|
+
children: /* @__PURE__ */ jsx11("path", { d: "M5 12.75L10 19L19 5" })
|
|
1614
|
+
},
|
|
1615
|
+
"check"
|
|
1616
|
+
) : /* @__PURE__ */ jsxs11(
|
|
1617
|
+
motion6.svg,
|
|
1618
|
+
{
|
|
1619
|
+
className: "dialkit-toolbar-copy-icon",
|
|
1620
|
+
viewBox: "0 0 24 24",
|
|
1621
|
+
fill: "none",
|
|
1622
|
+
initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1623
|
+
animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
|
|
1624
|
+
exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1625
|
+
transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
1626
|
+
children: [
|
|
1627
|
+
/* @__PURE__ */ jsx11("path", { d: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
|
|
1628
|
+
/* @__PURE__ */ jsx11("path", { d: "M19.2405 16.1852L18.5436 14.3733C18.4571 14.1484 18.241 14 18 14C17.759 14 17.5429 14.1484 17.4564 14.3733L16.7595 16.1852C16.658 16.4493 16.4493 16.658 16.1852 16.7595L14.3733 17.4564C14.1484 17.5429 14 17.759 14 18C14 18.241 14.1484 18.4571 14.3733 18.5436L16.1852 19.2405C16.4493 19.342 16.658 19.5507 16.7595 19.8148L17.4564 21.6267C17.5429 21.8516 17.759 22 18 22C18.241 22 18.4571 21.8516 18.5436 21.6267L19.2405 19.8148C19.342 19.5507 19.5507 19.342 19.8148 19.2405L21.6267 18.5436C21.8516 18.4571 22 18.241 22 18C22 17.759 21.8516 17.5429 21.6267 17.4564L19.8148 16.7595C19.5507 16.658 19.342 16.4493 19.2405 16.1852Z", fill: "currentColor" }),
|
|
1629
|
+
/* @__PURE__ */ jsx11("path", { d: "M16 5H17C18.6569 5 20 6.34315 20 8V11M8 5H7C5.34315 5 4 6.34315 4 8V18C4 19.6569 5.34315 21 7 21H12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1630
|
+
]
|
|
1631
|
+
},
|
|
1632
|
+
"clipboard"
|
|
1633
|
+
) }) }),
|
|
1634
|
+
"Copy"
|
|
1635
|
+
]
|
|
1636
|
+
}
|
|
1637
|
+
)
|
|
1638
|
+
] });
|
|
1639
|
+
return /* @__PURE__ */ jsx11("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ jsx11(Folder, { title: panel.name, defaultOpen: true, isRoot: true, onOpenChange: setIsPanelOpen, toolbar, children: renderControls() }) });
|
|
1006
1640
|
}
|
|
1007
1641
|
|
|
1008
1642
|
// src/components/DialRoot.tsx
|
|
1009
|
-
import { jsx as
|
|
1643
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1010
1644
|
function DialRoot({ position = "top-right" }) {
|
|
1011
|
-
const [panels, setPanels] =
|
|
1012
|
-
const [mounted, setMounted] =
|
|
1013
|
-
|
|
1645
|
+
const [panels, setPanels] = useState8([]);
|
|
1646
|
+
const [mounted, setMounted] = useState8(false);
|
|
1647
|
+
useEffect7(() => {
|
|
1014
1648
|
setMounted(true);
|
|
1015
1649
|
setPanels(DialStore.getPanels());
|
|
1016
1650
|
const unsubscribe = DialStore.subscribeGlobal(() => {
|
|
@@ -1024,14 +1658,14 @@ function DialRoot({ position = "top-right" }) {
|
|
|
1024
1658
|
if (panels.length === 0) {
|
|
1025
1659
|
return null;
|
|
1026
1660
|
}
|
|
1027
|
-
const content = /* @__PURE__ */
|
|
1028
|
-
return
|
|
1661
|
+
const content = /* @__PURE__ */ jsx12("div", { className: "dialkit-root", children: /* @__PURE__ */ jsx12("div", { className: "dialkit-panel", "data-position": position, children: panels.map((panel) => /* @__PURE__ */ jsx12(Panel, { panel }, panel.id)) }) });
|
|
1662
|
+
return createPortal2(content, document.body);
|
|
1029
1663
|
}
|
|
1030
1664
|
|
|
1031
1665
|
// src/components/ButtonGroup.tsx
|
|
1032
|
-
import { jsx as
|
|
1666
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1033
1667
|
function ButtonGroup({ buttons }) {
|
|
1034
|
-
return /* @__PURE__ */
|
|
1668
|
+
return /* @__PURE__ */ jsx13("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ jsx13(
|
|
1035
1669
|
"button",
|
|
1036
1670
|
{
|
|
1037
1671
|
className: "dialkit-button",
|
|
@@ -1043,13 +1677,16 @@ function ButtonGroup({ buttons }) {
|
|
|
1043
1677
|
}
|
|
1044
1678
|
export {
|
|
1045
1679
|
ButtonGroup,
|
|
1680
|
+
ColorControl,
|
|
1046
1681
|
DialRoot,
|
|
1047
1682
|
DialStore,
|
|
1048
1683
|
Folder,
|
|
1049
|
-
|
|
1684
|
+
PresetManager,
|
|
1685
|
+
SelectControl,
|
|
1050
1686
|
Slider,
|
|
1051
1687
|
SpringControl,
|
|
1052
1688
|
SpringVisualization,
|
|
1689
|
+
TextControl,
|
|
1053
1690
|
Toggle,
|
|
1054
1691
|
useDialKit
|
|
1055
1692
|
};
|