dialkit 0.1.1 → 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 +902 -262
- 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 +909 -272
- package/dist/index.js.map +1 -1
- package/dist/styles.css +620 -44
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -22,13 +22,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
ButtonGroup: () => ButtonGroup,
|
|
25
|
+
ColorControl: () => ColorControl,
|
|
25
26
|
DialRoot: () => DialRoot,
|
|
26
27
|
DialStore: () => DialStore,
|
|
27
28
|
Folder: () => Folder,
|
|
28
|
-
|
|
29
|
+
PresetManager: () => PresetManager,
|
|
30
|
+
SelectControl: () => SelectControl,
|
|
29
31
|
Slider: () => Slider,
|
|
30
32
|
SpringControl: () => SpringControl,
|
|
31
33
|
SpringVisualization: () => SpringVisualization,
|
|
34
|
+
TextControl: () => TextControl,
|
|
32
35
|
Toggle: () => Toggle,
|
|
33
36
|
useDialKit: () => useDialKit
|
|
34
37
|
});
|
|
@@ -38,6 +41,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
38
41
|
var import_react = require("react");
|
|
39
42
|
|
|
40
43
|
// src/store/DialStore.ts
|
|
44
|
+
var EMPTY_VALUES = Object.freeze({});
|
|
41
45
|
var DialStoreClass = class {
|
|
42
46
|
constructor() {
|
|
43
47
|
this.panels = /* @__PURE__ */ new Map();
|
|
@@ -45,12 +49,16 @@ var DialStoreClass = class {
|
|
|
45
49
|
this.globalListeners = /* @__PURE__ */ new Set();
|
|
46
50
|
this.snapshots = /* @__PURE__ */ new Map();
|
|
47
51
|
this.actionListeners = /* @__PURE__ */ new Map();
|
|
52
|
+
this.presets = /* @__PURE__ */ new Map();
|
|
53
|
+
this.activePreset = /* @__PURE__ */ new Map();
|
|
54
|
+
this.baseValues = /* @__PURE__ */ new Map();
|
|
48
55
|
}
|
|
49
56
|
registerPanel(id, name, config) {
|
|
50
57
|
const controls = this.parseConfig(config, "");
|
|
51
58
|
const values = this.flattenValues(config, "");
|
|
52
59
|
this.panels.set(id, { id, name, controls, values });
|
|
53
60
|
this.snapshots.set(id, { ...values });
|
|
61
|
+
this.baseValues.set(id, { ...values });
|
|
54
62
|
this.notifyGlobal();
|
|
55
63
|
}
|
|
56
64
|
unregisterPanel(id) {
|
|
@@ -63,6 +71,15 @@ var DialStoreClass = class {
|
|
|
63
71
|
const panel = this.panels.get(panelId);
|
|
64
72
|
if (!panel) return;
|
|
65
73
|
panel.values[path] = value;
|
|
74
|
+
const activeId = this.activePreset.get(panelId);
|
|
75
|
+
if (activeId) {
|
|
76
|
+
const presets = this.presets.get(panelId) ?? [];
|
|
77
|
+
const preset = presets.find((p) => p.id === activeId);
|
|
78
|
+
if (preset) preset.values[path] = value;
|
|
79
|
+
} else {
|
|
80
|
+
const base = this.baseValues.get(panelId);
|
|
81
|
+
if (base) base[path] = value;
|
|
82
|
+
}
|
|
66
83
|
this.snapshots.set(panelId, { ...panel.values });
|
|
67
84
|
this.notify(panelId);
|
|
68
85
|
}
|
|
@@ -83,7 +100,7 @@ var DialStoreClass = class {
|
|
|
83
100
|
return panel?.values[path];
|
|
84
101
|
}
|
|
85
102
|
getValues(panelId) {
|
|
86
|
-
return this.snapshots.get(panelId) ??
|
|
103
|
+
return this.snapshots.get(panelId) ?? EMPTY_VALUES;
|
|
87
104
|
}
|
|
88
105
|
getPanels() {
|
|
89
106
|
return Array.from(this.panels.values());
|
|
@@ -116,6 +133,61 @@ var DialStoreClass = class {
|
|
|
116
133
|
triggerAction(panelId, path) {
|
|
117
134
|
this.actionListeners.get(panelId)?.forEach((fn) => fn(path));
|
|
118
135
|
}
|
|
136
|
+
savePreset(panelId, name) {
|
|
137
|
+
const panel = this.panels.get(panelId);
|
|
138
|
+
if (!panel) throw new Error(`Panel ${panelId} not found`);
|
|
139
|
+
const id = `preset-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
140
|
+
const preset = {
|
|
141
|
+
id,
|
|
142
|
+
name,
|
|
143
|
+
values: { ...panel.values }
|
|
144
|
+
};
|
|
145
|
+
const existing = this.presets.get(panelId) ?? [];
|
|
146
|
+
this.presets.set(panelId, [...existing, preset]);
|
|
147
|
+
this.activePreset.set(panelId, id);
|
|
148
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
149
|
+
this.notify(panelId);
|
|
150
|
+
return id;
|
|
151
|
+
}
|
|
152
|
+
loadPreset(panelId, presetId) {
|
|
153
|
+
const panel = this.panels.get(panelId);
|
|
154
|
+
if (!panel) return;
|
|
155
|
+
const presets = this.presets.get(panelId) ?? [];
|
|
156
|
+
const preset = presets.find((p) => p.id === presetId);
|
|
157
|
+
if (!preset) return;
|
|
158
|
+
panel.values = { ...preset.values };
|
|
159
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
160
|
+
this.activePreset.set(panelId, presetId);
|
|
161
|
+
this.notify(panelId);
|
|
162
|
+
}
|
|
163
|
+
deletePreset(panelId, presetId) {
|
|
164
|
+
const presets = this.presets.get(panelId) ?? [];
|
|
165
|
+
this.presets.set(panelId, presets.filter((p) => p.id !== presetId));
|
|
166
|
+
if (this.activePreset.get(panelId) === presetId) {
|
|
167
|
+
this.activePreset.set(panelId, null);
|
|
168
|
+
}
|
|
169
|
+
const panel = this.panels.get(panelId);
|
|
170
|
+
if (panel) {
|
|
171
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
172
|
+
}
|
|
173
|
+
this.notify(panelId);
|
|
174
|
+
}
|
|
175
|
+
getPresets(panelId) {
|
|
176
|
+
return this.presets.get(panelId) ?? [];
|
|
177
|
+
}
|
|
178
|
+
getActivePresetId(panelId) {
|
|
179
|
+
return this.activePreset.get(panelId) ?? null;
|
|
180
|
+
}
|
|
181
|
+
clearActivePreset(panelId) {
|
|
182
|
+
const panel = this.panels.get(panelId);
|
|
183
|
+
const base = this.baseValues.get(panelId);
|
|
184
|
+
if (panel && base) {
|
|
185
|
+
panel.values = { ...base };
|
|
186
|
+
this.snapshots.set(panelId, { ...panel.values });
|
|
187
|
+
}
|
|
188
|
+
this.activePreset.set(panelId, null);
|
|
189
|
+
this.notify(panelId);
|
|
190
|
+
}
|
|
119
191
|
notify(panelId) {
|
|
120
192
|
this.listeners.get(panelId)?.forEach((fn) => fn());
|
|
121
193
|
}
|
|
@@ -145,6 +217,18 @@ var DialStoreClass = class {
|
|
|
145
217
|
controls.push({ type: "spring", path, label });
|
|
146
218
|
} else if (this.isActionConfig(value)) {
|
|
147
219
|
controls.push({ type: "action", path, label: value.label || label });
|
|
220
|
+
} else if (this.isSelectConfig(value)) {
|
|
221
|
+
controls.push({ type: "select", path, label, options: value.options });
|
|
222
|
+
} else if (this.isColorConfig(value)) {
|
|
223
|
+
controls.push({ type: "color", path, label });
|
|
224
|
+
} else if (this.isTextConfig(value)) {
|
|
225
|
+
controls.push({ type: "text", path, label, placeholder: value.placeholder });
|
|
226
|
+
} else if (typeof value === "string") {
|
|
227
|
+
if (this.isHexColor(value)) {
|
|
228
|
+
controls.push({ type: "color", path, label });
|
|
229
|
+
} else {
|
|
230
|
+
controls.push({ type: "text", path, label });
|
|
231
|
+
}
|
|
148
232
|
} else if (typeof value === "object" && value !== null) {
|
|
149
233
|
controls.push({
|
|
150
234
|
type: "folder",
|
|
@@ -168,6 +252,14 @@ var DialStoreClass = class {
|
|
|
168
252
|
values[path] = value;
|
|
169
253
|
} else if (this.isActionConfig(value)) {
|
|
170
254
|
values[path] = value;
|
|
255
|
+
} else if (this.isSelectConfig(value)) {
|
|
256
|
+
const firstOption = value.options[0];
|
|
257
|
+
const firstValue = typeof firstOption === "string" ? firstOption : firstOption.value;
|
|
258
|
+
values[path] = value.default ?? firstValue;
|
|
259
|
+
} else if (this.isColorConfig(value)) {
|
|
260
|
+
values[path] = value.default ?? "#000000";
|
|
261
|
+
} else if (this.isTextConfig(value)) {
|
|
262
|
+
values[path] = value.default ?? "";
|
|
171
263
|
} else if (typeof value === "object" && value !== null) {
|
|
172
264
|
Object.assign(values, this.flattenValues(value, path));
|
|
173
265
|
}
|
|
@@ -180,6 +272,18 @@ var DialStoreClass = class {
|
|
|
180
272
|
isActionConfig(value) {
|
|
181
273
|
return typeof value === "object" && value !== null && "type" in value && value.type === "action";
|
|
182
274
|
}
|
|
275
|
+
isSelectConfig(value) {
|
|
276
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === "select" && "options" in value && Array.isArray(value.options);
|
|
277
|
+
}
|
|
278
|
+
isColorConfig(value) {
|
|
279
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === "color";
|
|
280
|
+
}
|
|
281
|
+
isTextConfig(value) {
|
|
282
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === "text";
|
|
283
|
+
}
|
|
284
|
+
isHexColor(value) {
|
|
285
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value);
|
|
286
|
+
}
|
|
183
287
|
formatLabel(key) {
|
|
184
288
|
return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
185
289
|
}
|
|
@@ -187,13 +291,13 @@ var DialStoreClass = class {
|
|
|
187
291
|
if (value >= 0 && value <= 1) {
|
|
188
292
|
return { min: 0, max: 1, step: 0.01 };
|
|
189
293
|
} else if (value >= 0 && value <= 10) {
|
|
190
|
-
return { min: 0, max: value *
|
|
294
|
+
return { min: 0, max: value * 3 || 10, step: 0.1 };
|
|
191
295
|
} else if (value >= 0 && value <= 100) {
|
|
192
|
-
return { min: 0, max: value *
|
|
296
|
+
return { min: 0, max: value * 3 || 100, step: 1 };
|
|
193
297
|
} else if (value >= 0) {
|
|
194
|
-
return { min: 0, max: value *
|
|
298
|
+
return { min: 0, max: value * 3 || 1e3, step: 10 };
|
|
195
299
|
} else {
|
|
196
|
-
return { min: value *
|
|
300
|
+
return { min: value * 3, max: -value * 3, step: 1 };
|
|
197
301
|
}
|
|
198
302
|
}
|
|
199
303
|
inferStep(min, max) {
|
|
@@ -207,7 +311,6 @@ var DialStoreClass = class {
|
|
|
207
311
|
var DialStore = new DialStoreClass();
|
|
208
312
|
|
|
209
313
|
// src/hooks/useDialKit.ts
|
|
210
|
-
var EMPTY_SERVER_SNAPSHOT = {};
|
|
211
314
|
function useDialKit(name, config, options) {
|
|
212
315
|
const instanceId = (0, import_react.useId)();
|
|
213
316
|
const panelId = `${name}-${instanceId}`;
|
|
@@ -226,7 +329,7 @@ function useDialKit(name, config, options) {
|
|
|
226
329
|
const values = (0, import_react.useSyncExternalStore)(
|
|
227
330
|
(callback) => DialStore.subscribe(panelId, callback),
|
|
228
331
|
() => DialStore.getValues(panelId),
|
|
229
|
-
() =>
|
|
332
|
+
() => DialStore.getValues(panelId)
|
|
230
333
|
);
|
|
231
334
|
return buildResolvedValues(config, values, "");
|
|
232
335
|
}
|
|
@@ -240,188 +343,150 @@ function buildResolvedValues(config, flatValues, prefix) {
|
|
|
240
343
|
result[key] = flatValues[path] ?? configValue;
|
|
241
344
|
} else if (isSpringConfig(configValue)) {
|
|
242
345
|
result[key] = flatValues[path] ?? configValue;
|
|
346
|
+
} else if (isActionConfig(configValue)) {
|
|
347
|
+
result[key] = flatValues[path] ?? configValue;
|
|
348
|
+
} else if (isSelectConfig(configValue)) {
|
|
349
|
+
const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
|
|
350
|
+
result[key] = flatValues[path] ?? defaultValue;
|
|
351
|
+
} else if (isColorConfig(configValue)) {
|
|
352
|
+
result[key] = flatValues[path] ?? configValue.default ?? "#000000";
|
|
353
|
+
} else if (isTextConfig(configValue)) {
|
|
354
|
+
result[key] = flatValues[path] ?? configValue.default ?? "";
|
|
243
355
|
} else if (typeof configValue === "object" && configValue !== null) {
|
|
244
356
|
result[key] = buildResolvedValues(configValue, flatValues, path);
|
|
245
357
|
}
|
|
246
358
|
}
|
|
247
359
|
return result;
|
|
248
360
|
}
|
|
361
|
+
function hasType(value, type) {
|
|
362
|
+
return typeof value === "object" && value !== null && "type" in value && value.type === type;
|
|
363
|
+
}
|
|
249
364
|
function isSpringConfig(value) {
|
|
250
|
-
return
|
|
365
|
+
return hasType(value, "spring");
|
|
366
|
+
}
|
|
367
|
+
function isActionConfig(value) {
|
|
368
|
+
return hasType(value, "action");
|
|
369
|
+
}
|
|
370
|
+
function isSelectConfig(value) {
|
|
371
|
+
return hasType(value, "select") && "options" in value && Array.isArray(value.options);
|
|
372
|
+
}
|
|
373
|
+
function isColorConfig(value) {
|
|
374
|
+
return hasType(value, "color");
|
|
375
|
+
}
|
|
376
|
+
function isTextConfig(value) {
|
|
377
|
+
return hasType(value, "text");
|
|
378
|
+
}
|
|
379
|
+
function getFirstOptionValue(options) {
|
|
380
|
+
const first = options[0];
|
|
381
|
+
return typeof first === "string" ? first : first.value;
|
|
251
382
|
}
|
|
252
383
|
|
|
253
384
|
// src/components/DialRoot.tsx
|
|
254
|
-
var
|
|
255
|
-
var
|
|
385
|
+
var import_react16 = require("react");
|
|
386
|
+
var import_react_dom2 = require("react-dom");
|
|
387
|
+
|
|
388
|
+
// src/components/Panel.tsx
|
|
389
|
+
var import_react14 = require("react");
|
|
390
|
+
var import_react15 = require("motion/react");
|
|
256
391
|
|
|
257
392
|
// src/components/Folder.tsx
|
|
258
393
|
var import_react2 = require("react");
|
|
259
394
|
var import_react3 = require("motion/react");
|
|
260
395
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
261
|
-
function Folder({ title, children, defaultOpen = true, isRoot = false,
|
|
396
|
+
function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenChange, toolbar }) {
|
|
262
397
|
const [isOpen, setIsOpen] = (0, import_react2.useState)(defaultOpen);
|
|
263
|
-
const [
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
if (!panelId) return;
|
|
267
|
-
const values = DialStore.getValues(panelId);
|
|
268
|
-
const jsonStr = JSON.stringify(values, null, 2);
|
|
269
|
-
const instruction = `Update the useDialKit configuration for "${title}" with these values:
|
|
270
|
-
|
|
271
|
-
\`\`\`json
|
|
272
|
-
${jsonStr}
|
|
273
|
-
\`\`\`
|
|
274
|
-
|
|
275
|
-
Apply these values as the new defaults in the useDialKit call.`;
|
|
276
|
-
navigator.clipboard.writeText(instruction);
|
|
277
|
-
setCopied(true);
|
|
278
|
-
setTimeout(() => setCopied(false), 1500);
|
|
279
|
-
};
|
|
398
|
+
const [isCollapsed, setIsCollapsed] = (0, import_react2.useState)(!defaultOpen);
|
|
399
|
+
const contentRef = (0, import_react2.useRef)(null);
|
|
400
|
+
const [contentHeight, setContentHeight] = (0, import_react2.useState)(void 0);
|
|
280
401
|
const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
402
|
+
const panelTransition = { type: "spring", visualDuration: 0.4, bounce: 0.2 };
|
|
403
|
+
(0, import_react2.useEffect)(() => {
|
|
404
|
+
const el = contentRef.current;
|
|
405
|
+
if (!el) return;
|
|
406
|
+
const ro = new ResizeObserver(() => {
|
|
407
|
+
if (isOpen) {
|
|
408
|
+
const h = el.offsetHeight;
|
|
409
|
+
setContentHeight((prev) => prev === h ? prev : h);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
ro.observe(el);
|
|
413
|
+
return () => ro.disconnect();
|
|
414
|
+
}, [isOpen]);
|
|
415
|
+
const folderContent = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: () => {
|
|
417
|
+
const next = !isOpen;
|
|
418
|
+
setIsOpen(next);
|
|
419
|
+
if (next) setIsCollapsed(false);
|
|
420
|
+
onOpenChange?.(next);
|
|
421
|
+
}, children: [
|
|
422
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-header-top", children: [
|
|
423
|
+
isRoot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.AnimatePresence, { initial: false, mode: "popLayout", children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
424
|
+
import_react3.motion.div,
|
|
287
425
|
{
|
|
288
|
-
className: "dialkit-folder-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
scale: isOpen ? 1 : 0.7,
|
|
295
|
-
filter: isOpen ? "blur(0px)" : "blur(6px)"
|
|
296
|
-
},
|
|
297
|
-
transition: iconTransition,
|
|
298
|
-
style: { pointerEvents: isOpen ? "auto" : "none" },
|
|
299
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "relative", width: 14, height: 14 }, children: [
|
|
300
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
301
|
-
import_react3.motion.svg,
|
|
302
|
-
{
|
|
303
|
-
viewBox: "0 0 24 24",
|
|
304
|
-
fill: "none",
|
|
305
|
-
stroke: "currentColor",
|
|
306
|
-
strokeWidth: "2",
|
|
307
|
-
strokeLinecap: "round",
|
|
308
|
-
strokeLinejoin: "round",
|
|
309
|
-
style: { position: "absolute", inset: 0 },
|
|
310
|
-
initial: false,
|
|
311
|
-
animate: {
|
|
312
|
-
opacity: copied ? 0 : 1,
|
|
313
|
-
scale: copied ? 0.7 : 1,
|
|
314
|
-
filter: copied ? "blur(6px)" : "blur(0px)"
|
|
315
|
-
},
|
|
316
|
-
transition: iconTransition,
|
|
317
|
-
children: [
|
|
318
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
319
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
320
|
-
]
|
|
321
|
-
}
|
|
322
|
-
),
|
|
323
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
324
|
-
import_react3.motion.svg,
|
|
325
|
-
{
|
|
326
|
-
viewBox: "0 0 24 24",
|
|
327
|
-
fill: "none",
|
|
328
|
-
stroke: "currentColor",
|
|
329
|
-
strokeWidth: "2",
|
|
330
|
-
strokeLinecap: "round",
|
|
331
|
-
strokeLinejoin: "round",
|
|
332
|
-
style: { position: "absolute", inset: 0 },
|
|
333
|
-
initial: false,
|
|
334
|
-
animate: {
|
|
335
|
-
opacity: copied ? 1 : 0,
|
|
336
|
-
scale: copied ? 1 : 0.7,
|
|
337
|
-
filter: copied ? "blur(0px)" : "blur(6px)"
|
|
338
|
-
},
|
|
339
|
-
transition: iconTransition,
|
|
340
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "20 6 9 17 4 12" })
|
|
341
|
-
}
|
|
342
|
-
)
|
|
343
|
-
] })
|
|
426
|
+
className: "dialkit-folder-title-row",
|
|
427
|
+
initial: { opacity: 1 },
|
|
428
|
+
animate: { opacity: 1 },
|
|
429
|
+
exit: { opacity: 0 },
|
|
430
|
+
transition: { duration: 0.15 },
|
|
431
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title })
|
|
344
432
|
}
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
351
|
-
import_react3.motion.svg,
|
|
433
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dialkit-folder-title", children: title }) }),
|
|
434
|
+
isRoot ? (
|
|
435
|
+
// Root panel icon — fixed position, container morphs around it
|
|
436
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
437
|
+
"svg",
|
|
352
438
|
{
|
|
353
|
-
|
|
439
|
+
className: "dialkit-panel-icon",
|
|
440
|
+
viewBox: "0 0 16 16",
|
|
354
441
|
fill: "none",
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
animate: {
|
|
362
|
-
opacity: isOpen ? 1 : 0,
|
|
363
|
-
scale: isOpen ? 1 : 0.7,
|
|
364
|
-
filter: isOpen ? "blur(0px)" : "blur(6px)"
|
|
365
|
-
},
|
|
366
|
-
transition: iconTransition,
|
|
367
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
442
|
+
children: [
|
|
443
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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" }),
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "8", r: "0.998596", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
|
|
445
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.4999", cy: "3.5", r: "0.998657", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9.75015", cy: "12.5", r: "0.997986", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })
|
|
447
|
+
]
|
|
368
448
|
}
|
|
369
|
-
)
|
|
370
|
-
|
|
449
|
+
)
|
|
450
|
+
) : (
|
|
451
|
+
// Section folders use rotating chevron with gentle spring
|
|
452
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
371
453
|
import_react3.motion.svg,
|
|
372
454
|
{
|
|
455
|
+
className: "dialkit-folder-icon",
|
|
373
456
|
viewBox: "0 0 24 24",
|
|
374
457
|
fill: "none",
|
|
375
458
|
stroke: "currentColor",
|
|
376
|
-
strokeWidth: "2",
|
|
459
|
+
strokeWidth: "2.5",
|
|
377
460
|
strokeLinecap: "round",
|
|
378
461
|
strokeLinejoin: "round",
|
|
379
|
-
style: { position: "absolute", inset: 0, width: "100%", height: "100%" },
|
|
380
462
|
initial: false,
|
|
381
|
-
animate: {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
filter: isOpen ? "blur(6px)" : "blur(0px)"
|
|
385
|
-
},
|
|
386
|
-
transition: iconTransition,
|
|
387
|
-
children: [
|
|
388
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
389
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
390
|
-
]
|
|
463
|
+
animate: { rotate: isOpen ? 0 : 180 },
|
|
464
|
+
transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
|
|
465
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
|
|
391
466
|
}
|
|
392
467
|
)
|
|
393
|
-
] })
|
|
394
|
-
) : (
|
|
395
|
-
// Section folders use rotating chevron with gentle spring
|
|
396
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
397
|
-
import_react3.motion.svg,
|
|
398
|
-
{
|
|
399
|
-
className: "dialkit-folder-icon",
|
|
400
|
-
viewBox: "0 0 24 24",
|
|
401
|
-
fill: "none",
|
|
402
|
-
stroke: "currentColor",
|
|
403
|
-
strokeWidth: "1.5",
|
|
404
|
-
strokeLinecap: "round",
|
|
405
|
-
strokeLinejoin: "round",
|
|
406
|
-
initial: false,
|
|
407
|
-
animate: { rotate: isOpen ? 0 : 180 },
|
|
408
|
-
transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
|
|
409
|
-
children: [
|
|
410
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "5", y1: "9", x2: "12", y2: "16" }),
|
|
411
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "19", y1: "9", x2: "12", y2: "16" })
|
|
412
|
-
]
|
|
413
|
-
}
|
|
414
468
|
)
|
|
415
|
-
)
|
|
469
|
+
] }),
|
|
470
|
+
isRoot && toolbar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
471
|
+
import_react3.motion.div,
|
|
472
|
+
{
|
|
473
|
+
initial: { opacity: 1 },
|
|
474
|
+
animate: { opacity: 1 },
|
|
475
|
+
exit: { opacity: 0 },
|
|
476
|
+
transition: { duration: 0.15 },
|
|
477
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
|
|
478
|
+
}
|
|
479
|
+
) })
|
|
416
480
|
] }),
|
|
417
481
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
418
482
|
import_react3.motion.div,
|
|
419
483
|
{
|
|
420
484
|
className: "dialkit-folder-content",
|
|
421
|
-
initial: { height: 0, opacity: 0 },
|
|
422
|
-
animate: { height: "auto", opacity: 1 },
|
|
423
|
-
exit: { height: 0, opacity: 0 },
|
|
424
|
-
transition: { type: "spring", visualDuration: 0.
|
|
485
|
+
initial: isRoot ? { opacity: 1 } : { height: 0, opacity: 0 },
|
|
486
|
+
animate: isRoot ? { opacity: 1 } : { height: "auto", opacity: 1 },
|
|
487
|
+
exit: isRoot ? { opacity: 0 } : { height: 0, opacity: 0 },
|
|
488
|
+
transition: isRoot ? { duration: 0.15 } : { type: "spring", visualDuration: 0.35, bounce: 0.1 },
|
|
489
|
+
style: isRoot ? void 0 : { overflow: "hidden" },
|
|
425
490
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-inner", children })
|
|
426
491
|
}
|
|
427
492
|
) })
|
|
@@ -433,11 +498,22 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
433
498
|
className: "dialkit-panel-inner",
|
|
434
499
|
initial: false,
|
|
435
500
|
animate: {
|
|
436
|
-
width: isOpen ? 280 :
|
|
501
|
+
width: isOpen ? 280 : 42,
|
|
502
|
+
height: isOpen ? contentHeight !== void 0 ? contentHeight + 24 : "auto" : 42,
|
|
503
|
+
borderRadius: isOpen ? 14 : 21,
|
|
437
504
|
boxShadow: isOpen ? "0 8px 32px rgba(0, 0, 0, 0.5)" : "0 4px 16px rgba(0, 0, 0, 0.25)"
|
|
438
505
|
},
|
|
439
|
-
transition:
|
|
440
|
-
"
|
|
506
|
+
transition: panelTransition,
|
|
507
|
+
style: { overflow: isOpen ? void 0 : "hidden", cursor: isOpen ? void 0 : "pointer" },
|
|
508
|
+
onClick: !isOpen ? () => {
|
|
509
|
+
setIsOpen(true);
|
|
510
|
+
setIsCollapsed(false);
|
|
511
|
+
onOpenChange?.(true);
|
|
512
|
+
} : void 0,
|
|
513
|
+
onAnimationComplete: () => {
|
|
514
|
+
if (!isOpen) setIsCollapsed(true);
|
|
515
|
+
},
|
|
516
|
+
"data-collapsed": isCollapsed,
|
|
441
517
|
children: folderContent
|
|
442
518
|
}
|
|
443
519
|
);
|
|
@@ -449,6 +525,23 @@ Apply these values as the new defaults in the useDialKit call.`;
|
|
|
449
525
|
var import_react4 = require("react");
|
|
450
526
|
var import_react5 = require("motion/react");
|
|
451
527
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
528
|
+
var CLICK_THRESHOLD = 3;
|
|
529
|
+
var DEAD_ZONE = 32;
|
|
530
|
+
var MAX_CURSOR_RANGE = 200;
|
|
531
|
+
var MAX_STRETCH = 8;
|
|
532
|
+
function roundValue(val, step) {
|
|
533
|
+
const decimals = step >= 1 ? 0 : 2;
|
|
534
|
+
const factor = 10 ** decimals;
|
|
535
|
+
return Math.round(val * factor) / factor;
|
|
536
|
+
}
|
|
537
|
+
function snapToDecile(rawValue, min, max) {
|
|
538
|
+
const normalized = (rawValue - min) / (max - min);
|
|
539
|
+
const nearest = Math.round(normalized * 10) / 10;
|
|
540
|
+
if (Math.abs(normalized - nearest) <= 0.03125) {
|
|
541
|
+
return min + nearest * (max - min);
|
|
542
|
+
}
|
|
543
|
+
return rawValue;
|
|
544
|
+
}
|
|
452
545
|
function Slider({
|
|
453
546
|
label,
|
|
454
547
|
value,
|
|
@@ -458,8 +551,11 @@ function Slider({
|
|
|
458
551
|
step = 0.01,
|
|
459
552
|
unit
|
|
460
553
|
}) {
|
|
554
|
+
const wrapperRef = (0, import_react4.useRef)(null);
|
|
461
555
|
const trackRef = (0, import_react4.useRef)(null);
|
|
462
556
|
const inputRef = (0, import_react4.useRef)(null);
|
|
557
|
+
const labelRef = (0, import_react4.useRef)(null);
|
|
558
|
+
const valueSpanRef = (0, import_react4.useRef)(null);
|
|
463
559
|
const [isInteracting, setIsInteracting] = (0, import_react4.useState)(false);
|
|
464
560
|
const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
|
|
465
561
|
const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
|
|
@@ -468,78 +564,160 @@ function Slider({
|
|
|
468
564
|
const [showInput, setShowInput] = (0, import_react4.useState)(false);
|
|
469
565
|
const [inputValue, setInputValue] = (0, import_react4.useState)("");
|
|
470
566
|
const hoverTimeoutRef = (0, import_react4.useRef)(null);
|
|
567
|
+
const pointerDownPos = (0, import_react4.useRef)(null);
|
|
568
|
+
const isClickRef = (0, import_react4.useRef)(true);
|
|
569
|
+
const animRef = (0, import_react4.useRef)(null);
|
|
570
|
+
const wrapperRectRef = (0, import_react4.useRef)(null);
|
|
571
|
+
const scaleRef = (0, import_react4.useRef)(1);
|
|
471
572
|
const percentage = (value - min) / (max - min) * 100;
|
|
472
|
-
const isAtMinimum = value <= min;
|
|
473
573
|
const isActive = isInteracting || isHovered;
|
|
574
|
+
const fillPercent = (0, import_react5.useMotionValue)(percentage);
|
|
575
|
+
const fillWidth = (0, import_react5.useTransform)(fillPercent, (pct) => `${pct}%`);
|
|
576
|
+
const handleLeft = (0, import_react5.useTransform)(
|
|
577
|
+
fillPercent,
|
|
578
|
+
(pct) => `max(5px, calc(${pct}% - 9px))`
|
|
579
|
+
);
|
|
580
|
+
const rubberStretchPx = (0, import_react5.useMotionValue)(0);
|
|
581
|
+
const rubberBandWidth = (0, import_react5.useTransform)(
|
|
582
|
+
rubberStretchPx,
|
|
583
|
+
(stretch) => `calc(100% + ${Math.abs(stretch)}px)`
|
|
584
|
+
);
|
|
585
|
+
const rubberBandX = (0, import_react5.useTransform)(
|
|
586
|
+
rubberStretchPx,
|
|
587
|
+
(stretch) => stretch < 0 ? stretch : 0
|
|
588
|
+
);
|
|
589
|
+
(0, import_react4.useEffect)(() => {
|
|
590
|
+
if (!isInteracting && !animRef.current) {
|
|
591
|
+
fillPercent.jump(percentage);
|
|
592
|
+
}
|
|
593
|
+
}, [percentage, isInteracting, fillPercent]);
|
|
474
594
|
const positionToValue = (0, import_react4.useCallback)(
|
|
475
595
|
(clientX) => {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
const
|
|
596
|
+
const rect = wrapperRectRef.current;
|
|
597
|
+
if (!rect) return value;
|
|
598
|
+
const screenX = clientX - rect.left;
|
|
599
|
+
const sceneX = screenX / scaleRef.current;
|
|
600
|
+
const nativeWidth = wrapperRef.current ? wrapperRef.current.offsetWidth : rect.width;
|
|
601
|
+
const percent = Math.max(0, Math.min(1, sceneX / nativeWidth));
|
|
480
602
|
const rawValue = min + percent * (max - min);
|
|
481
603
|
return Math.max(min, Math.min(max, rawValue));
|
|
482
604
|
},
|
|
483
605
|
[min, max, value]
|
|
484
606
|
);
|
|
485
|
-
const
|
|
486
|
-
(
|
|
487
|
-
|
|
488
|
-
e.preventDefault();
|
|
489
|
-
setIsInteracting(true);
|
|
490
|
-
onChange(positionToValue(e.clientX));
|
|
491
|
-
},
|
|
492
|
-
[positionToValue, onChange, showInput]
|
|
607
|
+
const percentFromValue = (0, import_react4.useCallback)(
|
|
608
|
+
(v) => (v - min) / (max - min) * 100,
|
|
609
|
+
[min, max]
|
|
493
610
|
);
|
|
494
|
-
const
|
|
495
|
-
(
|
|
496
|
-
|
|
497
|
-
if (!
|
|
498
|
-
|
|
611
|
+
const computeRubberStretch = (0, import_react4.useCallback)(
|
|
612
|
+
(clientX, sign) => {
|
|
613
|
+
const rect = wrapperRectRef.current;
|
|
614
|
+
if (!rect) return 0;
|
|
615
|
+
const distancePast = sign < 0 ? rect.left - clientX : clientX - rect.right;
|
|
616
|
+
const overflow = Math.max(0, distancePast - DEAD_ZONE);
|
|
617
|
+
return sign * MAX_STRETCH * Math.sqrt(Math.min(overflow / MAX_CURSOR_RANGE, 1));
|
|
499
618
|
},
|
|
500
|
-
[
|
|
619
|
+
[]
|
|
501
620
|
);
|
|
502
|
-
const
|
|
503
|
-
setIsInteracting(false);
|
|
504
|
-
setIsDragging(false);
|
|
505
|
-
}, []);
|
|
506
|
-
const handleTouchStart = (0, import_react4.useCallback)(
|
|
621
|
+
const handlePointerDown = (0, import_react4.useCallback)(
|
|
507
622
|
(e) => {
|
|
508
623
|
if (showInput) return;
|
|
509
624
|
e.preventDefault();
|
|
625
|
+
e.target.setPointerCapture(e.pointerId);
|
|
626
|
+
pointerDownPos.current = { x: e.clientX, y: e.clientY };
|
|
627
|
+
isClickRef.current = true;
|
|
510
628
|
setIsInteracting(true);
|
|
511
|
-
|
|
512
|
-
|
|
629
|
+
if (wrapperRef.current) {
|
|
630
|
+
wrapperRectRef.current = wrapperRef.current.getBoundingClientRect();
|
|
631
|
+
const nativeWidth = wrapperRef.current.offsetWidth;
|
|
632
|
+
scaleRef.current = wrapperRectRef.current.width / nativeWidth;
|
|
633
|
+
}
|
|
513
634
|
},
|
|
514
|
-
[
|
|
635
|
+
[showInput]
|
|
515
636
|
);
|
|
516
|
-
const
|
|
637
|
+
const handlePointerMove = (0, import_react4.useCallback)(
|
|
638
|
+
(e) => {
|
|
639
|
+
if (!isInteracting || !pointerDownPos.current) return;
|
|
640
|
+
const dx = e.clientX - pointerDownPos.current.x;
|
|
641
|
+
const dy = e.clientY - pointerDownPos.current.y;
|
|
642
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
643
|
+
if (isClickRef.current && distance > CLICK_THRESHOLD) {
|
|
644
|
+
isClickRef.current = false;
|
|
645
|
+
setIsDragging(true);
|
|
646
|
+
}
|
|
647
|
+
if (!isClickRef.current) {
|
|
648
|
+
const rect = wrapperRectRef.current;
|
|
649
|
+
if (rect) {
|
|
650
|
+
if (e.clientX < rect.left) {
|
|
651
|
+
rubberStretchPx.jump(computeRubberStretch(e.clientX, -1));
|
|
652
|
+
} else if (e.clientX > rect.right) {
|
|
653
|
+
rubberStretchPx.jump(computeRubberStretch(e.clientX, 1));
|
|
654
|
+
} else {
|
|
655
|
+
rubberStretchPx.jump(0);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const newValue = positionToValue(e.clientX);
|
|
659
|
+
const newPct = percentFromValue(newValue);
|
|
660
|
+
if (animRef.current) {
|
|
661
|
+
animRef.current.stop();
|
|
662
|
+
animRef.current = null;
|
|
663
|
+
}
|
|
664
|
+
fillPercent.jump(newPct);
|
|
665
|
+
onChange(roundValue(newValue, step));
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
[
|
|
669
|
+
isInteracting,
|
|
670
|
+
positionToValue,
|
|
671
|
+
percentFromValue,
|
|
672
|
+
onChange,
|
|
673
|
+
fillPercent,
|
|
674
|
+
rubberStretchPx,
|
|
675
|
+
computeRubberStretch
|
|
676
|
+
]
|
|
677
|
+
);
|
|
678
|
+
const handlePointerUp = (0, import_react4.useCallback)(
|
|
517
679
|
(e) => {
|
|
518
680
|
if (!isInteracting) return;
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
681
|
+
if (isClickRef.current) {
|
|
682
|
+
const rawValue = positionToValue(e.clientX);
|
|
683
|
+
const snappedValue = snapToDecile(rawValue, min, max);
|
|
684
|
+
const newPct = percentFromValue(snappedValue);
|
|
685
|
+
if (animRef.current) {
|
|
686
|
+
animRef.current.stop();
|
|
687
|
+
}
|
|
688
|
+
animRef.current = (0, import_react5.animate)(fillPercent, newPct, {
|
|
689
|
+
type: "spring",
|
|
690
|
+
stiffness: 300,
|
|
691
|
+
damping: 25,
|
|
692
|
+
mass: 0.8,
|
|
693
|
+
onComplete: () => {
|
|
694
|
+
animRef.current = null;
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
onChange(roundValue(snappedValue, step));
|
|
698
|
+
}
|
|
699
|
+
if (rubberStretchPx.get() !== 0) {
|
|
700
|
+
(0, import_react5.animate)(rubberStretchPx, 0, {
|
|
701
|
+
type: "spring",
|
|
702
|
+
visualDuration: 0.35,
|
|
703
|
+
bounce: 0.15
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
setIsInteracting(false);
|
|
707
|
+
setIsDragging(false);
|
|
708
|
+
pointerDownPos.current = null;
|
|
522
709
|
},
|
|
523
|
-
[
|
|
710
|
+
[
|
|
711
|
+
isInteracting,
|
|
712
|
+
positionToValue,
|
|
713
|
+
percentFromValue,
|
|
714
|
+
onChange,
|
|
715
|
+
min,
|
|
716
|
+
max,
|
|
717
|
+
fillPercent,
|
|
718
|
+
rubberStretchPx
|
|
719
|
+
]
|
|
524
720
|
);
|
|
525
|
-
const handleTouchEnd = (0, import_react4.useCallback)(() => {
|
|
526
|
-
setIsInteracting(false);
|
|
527
|
-
setIsDragging(false);
|
|
528
|
-
}, []);
|
|
529
|
-
(0, import_react4.useEffect)(() => {
|
|
530
|
-
if (isInteracting) {
|
|
531
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
532
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
533
|
-
document.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
534
|
-
document.addEventListener("touchend", handleTouchEnd);
|
|
535
|
-
return () => {
|
|
536
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
537
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
538
|
-
document.removeEventListener("touchmove", handleTouchMove);
|
|
539
|
-
document.removeEventListener("touchend", handleTouchEnd);
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
}, [isInteracting, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
|
|
543
721
|
(0, import_react4.useEffect)(() => {
|
|
544
722
|
if (isValueHovered && !showInput && !isValueEditable) {
|
|
545
723
|
hoverTimeoutRef.current = setTimeout(() => {
|
|
@@ -571,7 +749,7 @@ function Slider({
|
|
|
571
749
|
const parsed = parseFloat(inputValue);
|
|
572
750
|
if (!isNaN(parsed)) {
|
|
573
751
|
const clamped = Math.max(min, Math.min(max, parsed));
|
|
574
|
-
onChange(clamped);
|
|
752
|
+
onChange(roundValue(clamped, step));
|
|
575
753
|
}
|
|
576
754
|
setShowInput(false);
|
|
577
755
|
setIsValueHovered(false);
|
|
@@ -597,29 +775,56 @@ function Slider({
|
|
|
597
775
|
handleInputSubmit();
|
|
598
776
|
};
|
|
599
777
|
const displayValue = step >= 1 ? value.toFixed(0) : value.toFixed(2);
|
|
600
|
-
const
|
|
601
|
-
const
|
|
602
|
-
const
|
|
778
|
+
const HANDLE_BUFFER = 8;
|
|
779
|
+
const LABEL_CSS_LEFT = 10;
|
|
780
|
+
const VALUE_CSS_RIGHT = 10;
|
|
781
|
+
let leftThreshold = 30;
|
|
782
|
+
let rightThreshold = 78;
|
|
783
|
+
const trackWidth = wrapperRef.current?.offsetWidth;
|
|
784
|
+
if (trackWidth && trackWidth > 0) {
|
|
785
|
+
if (labelRef.current) {
|
|
786
|
+
leftThreshold = (LABEL_CSS_LEFT + labelRef.current.offsetWidth + HANDLE_BUFFER) / trackWidth * 100;
|
|
787
|
+
}
|
|
788
|
+
if (valueSpanRef.current) {
|
|
789
|
+
rightThreshold = (trackWidth - VALUE_CSS_RIGHT - valueSpanRef.current.offsetWidth - HANDLE_BUFFER) / trackWidth * 100;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
const valueDodge = percentage < leftThreshold || percentage > rightThreshold;
|
|
793
|
+
const handleOpacity = !isActive ? 0 : valueDodge ? 0.1 : isDragging ? 0.9 : 0.5;
|
|
603
794
|
const fillBackground = isActive ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.11)";
|
|
604
|
-
const hashMarks = Array.from({ length:
|
|
605
|
-
|
|
606
|
-
|
|
795
|
+
const hashMarks = Array.from({ length: 9 }, (_, i) => {
|
|
796
|
+
const pct = (i + 1) * 10;
|
|
797
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
798
|
+
"div",
|
|
799
|
+
{
|
|
800
|
+
className: "dialkit-slider-hashmark",
|
|
801
|
+
style: { left: `${pct}%` }
|
|
802
|
+
},
|
|
803
|
+
i
|
|
804
|
+
);
|
|
805
|
+
});
|
|
806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
807
|
+
import_react5.motion.div,
|
|
607
808
|
{
|
|
608
809
|
ref: trackRef,
|
|
609
810
|
className: `dialkit-slider ${isActive ? "dialkit-slider-active" : ""}`,
|
|
610
|
-
|
|
611
|
-
|
|
811
|
+
onPointerDown: handlePointerDown,
|
|
812
|
+
onPointerMove: handlePointerMove,
|
|
813
|
+
onPointerUp: handlePointerUp,
|
|
612
814
|
onMouseEnter: () => setIsHovered(true),
|
|
613
815
|
onMouseLeave: () => setIsHovered(false),
|
|
816
|
+
style: { width: rubberBandWidth, x: rubberBandX },
|
|
614
817
|
children: [
|
|
615
818
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
|
|
616
819
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
617
820
|
import_react5.motion.div,
|
|
618
821
|
{
|
|
619
822
|
className: "dialkit-slider-fill",
|
|
620
|
-
style: {
|
|
621
|
-
|
|
622
|
-
|
|
823
|
+
style: {
|
|
824
|
+
background: fillBackground,
|
|
825
|
+
width: fillWidth,
|
|
826
|
+
transition: "background 0.15s"
|
|
827
|
+
}
|
|
623
828
|
}
|
|
624
829
|
),
|
|
625
830
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -627,15 +832,23 @@ function Slider({
|
|
|
627
832
|
{
|
|
628
833
|
className: "dialkit-slider-handle",
|
|
629
834
|
style: {
|
|
630
|
-
background: isInteracting ? "rgba(255, 255, 255, 1)" : "rgba(255, 255, 255, 0.5)",
|
|
631
835
|
left: handleLeft,
|
|
632
|
-
|
|
836
|
+
y: "-50%",
|
|
837
|
+
background: "rgba(255, 255, 255, 0.9)"
|
|
838
|
+
},
|
|
839
|
+
animate: {
|
|
840
|
+
opacity: handleOpacity,
|
|
841
|
+
scaleX: isActive ? 1 : 0.25,
|
|
842
|
+
scaleY: isActive && valueDodge ? 0.75 : 1
|
|
633
843
|
},
|
|
634
|
-
|
|
635
|
-
|
|
844
|
+
transition: {
|
|
845
|
+
scaleX: { type: "spring", visualDuration: 0.25, bounce: 0.15 },
|
|
846
|
+
scaleY: { type: "spring", visualDuration: 0.2, bounce: 0.1 },
|
|
847
|
+
opacity: { duration: 0.15 }
|
|
848
|
+
}
|
|
636
849
|
}
|
|
637
850
|
),
|
|
638
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dialkit-slider-label", children: label }),
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { ref: labelRef, className: "dialkit-slider-label", children: label }),
|
|
639
852
|
showInput ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
640
853
|
"input",
|
|
641
854
|
{
|
|
@@ -649,24 +862,22 @@ function Slider({
|
|
|
649
862
|
onClick: (e) => e.stopPropagation(),
|
|
650
863
|
onMouseDown: (e) => e.stopPropagation()
|
|
651
864
|
}
|
|
652
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime2.
|
|
865
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
653
866
|
"span",
|
|
654
867
|
{
|
|
868
|
+
ref: valueSpanRef,
|
|
655
869
|
className: `dialkit-slider-value ${isValueEditable ? "dialkit-slider-value-editable" : ""}`,
|
|
656
870
|
onMouseEnter: () => setIsValueHovered(true),
|
|
657
871
|
onMouseLeave: () => setIsValueHovered(false),
|
|
658
872
|
onClick: handleValueClick,
|
|
659
873
|
onMouseDown: (e) => isValueEditable && e.stopPropagation(),
|
|
660
874
|
style: { cursor: isValueEditable ? "text" : "default" },
|
|
661
|
-
children:
|
|
662
|
-
displayValue,
|
|
663
|
-
unit
|
|
664
|
-
]
|
|
875
|
+
children: displayValue
|
|
665
876
|
}
|
|
666
877
|
)
|
|
667
878
|
]
|
|
668
879
|
}
|
|
669
|
-
);
|
|
880
|
+
) });
|
|
670
881
|
}
|
|
671
882
|
|
|
672
883
|
// src/components/SegmentedControl.tsx
|
|
@@ -951,20 +1162,349 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
|
|
|
951
1162
|
] }) });
|
|
952
1163
|
}
|
|
953
1164
|
|
|
954
|
-
// src/components/
|
|
955
|
-
var import_react9 = require("react");
|
|
1165
|
+
// src/components/TextControl.tsx
|
|
956
1166
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1167
|
+
function TextControl({ label, value, onChange, placeholder }) {
|
|
1168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dialkit-text-control", children: [
|
|
1169
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "dialkit-text-label", children: label }),
|
|
1170
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1171
|
+
"input",
|
|
1172
|
+
{
|
|
1173
|
+
type: "text",
|
|
1174
|
+
className: "dialkit-text-input",
|
|
1175
|
+
value,
|
|
1176
|
+
onChange: (e) => onChange(e.target.value),
|
|
1177
|
+
placeholder
|
|
1178
|
+
}
|
|
1179
|
+
)
|
|
1180
|
+
] });
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// src/components/SelectControl.tsx
|
|
1184
|
+
var import_react9 = require("react");
|
|
1185
|
+
var import_react10 = require("motion/react");
|
|
1186
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1187
|
+
function normalizeOptions(options) {
|
|
1188
|
+
return options.map(
|
|
1189
|
+
(opt) => typeof opt === "string" ? { value: opt, label: opt } : opt
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
function SelectControl({ label, value, options, onChange }) {
|
|
1193
|
+
const [isOpen, setIsOpen] = (0, import_react9.useState)(false);
|
|
1194
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
1195
|
+
const normalized = normalizeOptions(options);
|
|
1196
|
+
const selectedOption = normalized.find((o) => o.value === value);
|
|
1197
|
+
(0, import_react9.useEffect)(() => {
|
|
1198
|
+
if (!isOpen) return;
|
|
1199
|
+
const handleClick = (e) => {
|
|
1200
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
1201
|
+
setIsOpen(false);
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
document.addEventListener("mousedown", handleClick);
|
|
1205
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
1206
|
+
}, [isOpen]);
|
|
1207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: containerRef, className: "dialkit-select-row", children: [
|
|
1208
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
className: "dialkit-select-trigger",
|
|
1212
|
+
onClick: () => setIsOpen(!isOpen),
|
|
1213
|
+
"data-open": String(isOpen),
|
|
1214
|
+
children: [
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-select-label", children: label }),
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dialkit-select-right", children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
|
|
1218
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1219
|
+
import_react10.motion.svg,
|
|
1220
|
+
{
|
|
1221
|
+
className: "dialkit-select-chevron",
|
|
1222
|
+
viewBox: "0 0 24 24",
|
|
1223
|
+
fill: "none",
|
|
1224
|
+
stroke: "currentColor",
|
|
1225
|
+
strokeWidth: "2.5",
|
|
1226
|
+
strokeLinecap: "round",
|
|
1227
|
+
strokeLinejoin: "round",
|
|
1228
|
+
animate: { rotate: isOpen ? 180 : 0 },
|
|
1229
|
+
transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
|
|
1230
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
|
|
1231
|
+
}
|
|
1232
|
+
)
|
|
1233
|
+
] })
|
|
1234
|
+
]
|
|
1235
|
+
}
|
|
1236
|
+
),
|
|
1237
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react10.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1238
|
+
import_react10.motion.div,
|
|
1239
|
+
{
|
|
1240
|
+
className: "dialkit-select-dropdown",
|
|
1241
|
+
initial: { opacity: 0, y: -8, scale: 0.95 },
|
|
1242
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
1243
|
+
exit: { opacity: 0, y: -8, scale: 0.95 },
|
|
1244
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
1245
|
+
children: normalized.map((option) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1246
|
+
"button",
|
|
1247
|
+
{
|
|
1248
|
+
className: "dialkit-select-option",
|
|
1249
|
+
"data-selected": String(option.value === value),
|
|
1250
|
+
onClick: () => {
|
|
1251
|
+
onChange(option.value);
|
|
1252
|
+
setIsOpen(false);
|
|
1253
|
+
},
|
|
1254
|
+
children: option.label
|
|
1255
|
+
},
|
|
1256
|
+
option.value
|
|
1257
|
+
))
|
|
1258
|
+
}
|
|
1259
|
+
) })
|
|
1260
|
+
] });
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/components/ColorControl.tsx
|
|
1264
|
+
var import_react11 = require("react");
|
|
1265
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1266
|
+
var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
1267
|
+
function ColorControl({ label, value, onChange }) {
|
|
1268
|
+
const [isEditing, setIsEditing] = (0, import_react11.useState)(false);
|
|
1269
|
+
const [editValue, setEditValue] = (0, import_react11.useState)(value);
|
|
1270
|
+
const colorInputRef = (0, import_react11.useRef)(null);
|
|
1271
|
+
(0, import_react11.useEffect)(() => {
|
|
1272
|
+
if (!isEditing) {
|
|
1273
|
+
setEditValue(value);
|
|
1274
|
+
}
|
|
1275
|
+
}, [value, isEditing]);
|
|
1276
|
+
function handleTextSubmit() {
|
|
1277
|
+
setIsEditing(false);
|
|
1278
|
+
if (HEX_COLOR_REGEX.test(editValue)) {
|
|
1279
|
+
onChange(editValue);
|
|
1280
|
+
} else {
|
|
1281
|
+
setEditValue(value);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function handleKeyDown(e) {
|
|
1285
|
+
if (e.key === "Enter") {
|
|
1286
|
+
handleTextSubmit();
|
|
1287
|
+
} else if (e.key === "Escape") {
|
|
1288
|
+
setIsEditing(false);
|
|
1289
|
+
setEditValue(value);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-color-control", children: [
|
|
1293
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dialkit-color-label", children: label }),
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-color-inputs", children: [
|
|
1295
|
+
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1296
|
+
"input",
|
|
1297
|
+
{
|
|
1298
|
+
type: "text",
|
|
1299
|
+
className: "dialkit-color-hex-input",
|
|
1300
|
+
value: editValue,
|
|
1301
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
1302
|
+
onBlur: handleTextSubmit,
|
|
1303
|
+
onKeyDown: handleKeyDown,
|
|
1304
|
+
autoFocus: true
|
|
1305
|
+
}
|
|
1306
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1307
|
+
"span",
|
|
1308
|
+
{
|
|
1309
|
+
className: "dialkit-color-hex",
|
|
1310
|
+
onClick: () => setIsEditing(true),
|
|
1311
|
+
children: (value ?? "").toUpperCase()
|
|
1312
|
+
}
|
|
1313
|
+
),
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1315
|
+
"button",
|
|
1316
|
+
{
|
|
1317
|
+
className: "dialkit-color-swatch",
|
|
1318
|
+
style: { backgroundColor: value },
|
|
1319
|
+
onClick: () => colorInputRef.current?.click(),
|
|
1320
|
+
title: "Pick color"
|
|
1321
|
+
}
|
|
1322
|
+
),
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1324
|
+
"input",
|
|
1325
|
+
{
|
|
1326
|
+
ref: colorInputRef,
|
|
1327
|
+
type: "color",
|
|
1328
|
+
className: "dialkit-color-picker-native",
|
|
1329
|
+
value: value.length === 4 ? expandShorthandHex(value) : value.slice(0, 7),
|
|
1330
|
+
onChange: (e) => onChange(e.target.value)
|
|
1331
|
+
}
|
|
1332
|
+
)
|
|
1333
|
+
] })
|
|
1334
|
+
] });
|
|
1335
|
+
}
|
|
1336
|
+
function expandShorthandHex(hex) {
|
|
1337
|
+
if (hex.length !== 4) return hex;
|
|
1338
|
+
return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// src/components/PresetManager.tsx
|
|
1342
|
+
var import_react12 = require("react");
|
|
1343
|
+
var import_react_dom = require("react-dom");
|
|
1344
|
+
var import_react13 = require("motion/react");
|
|
1345
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1346
|
+
function PresetManager({ panelId, presets, activePresetId, onAdd }) {
|
|
1347
|
+
const [isOpen, setIsOpen] = (0, import_react12.useState)(false);
|
|
1348
|
+
const triggerRef = (0, import_react12.useRef)(null);
|
|
1349
|
+
const dropdownRef = (0, import_react12.useRef)(null);
|
|
1350
|
+
const [pos, setPos] = (0, import_react12.useState)({ top: 0, left: 0, width: 0 });
|
|
1351
|
+
const hasPresets = presets.length > 0;
|
|
1352
|
+
const activePreset = presets.find((p) => p.id === activePresetId);
|
|
1353
|
+
const open = (0, import_react12.useCallback)(() => {
|
|
1354
|
+
if (!hasPresets) return;
|
|
1355
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
1356
|
+
if (rect) {
|
|
1357
|
+
setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
|
|
1358
|
+
}
|
|
1359
|
+
setIsOpen(true);
|
|
1360
|
+
}, [hasPresets]);
|
|
1361
|
+
const close = (0, import_react12.useCallback)(() => setIsOpen(false), []);
|
|
1362
|
+
const toggle = (0, import_react12.useCallback)(() => {
|
|
1363
|
+
if (isOpen) close();
|
|
1364
|
+
else open();
|
|
1365
|
+
}, [isOpen, open, close]);
|
|
1366
|
+
(0, import_react12.useEffect)(() => {
|
|
1367
|
+
if (!isOpen) return;
|
|
1368
|
+
const handler = (e) => {
|
|
1369
|
+
const target = e.target;
|
|
1370
|
+
if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
|
|
1371
|
+
close();
|
|
1372
|
+
};
|
|
1373
|
+
document.addEventListener("mousedown", handler);
|
|
1374
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
1375
|
+
}, [isOpen, close]);
|
|
1376
|
+
const handleSelect = (presetId) => {
|
|
1377
|
+
if (presetId) {
|
|
1378
|
+
DialStore.loadPreset(panelId, presetId);
|
|
1379
|
+
} else {
|
|
1380
|
+
DialStore.clearActivePreset(panelId);
|
|
1381
|
+
}
|
|
1382
|
+
close();
|
|
1383
|
+
};
|
|
1384
|
+
const handleDelete = (e, presetId) => {
|
|
1385
|
+
e.stopPropagation();
|
|
1386
|
+
DialStore.deletePreset(panelId, presetId);
|
|
1387
|
+
};
|
|
1388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-preset-manager", children: [
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1390
|
+
"button",
|
|
1391
|
+
{
|
|
1392
|
+
ref: triggerRef,
|
|
1393
|
+
className: "dialkit-preset-trigger",
|
|
1394
|
+
onClick: toggle,
|
|
1395
|
+
"data-open": String(isOpen),
|
|
1396
|
+
"data-has-preset": String(!!activePreset),
|
|
1397
|
+
"data-disabled": String(!hasPresets),
|
|
1398
|
+
children: [
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
|
|
1400
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1401
|
+
import_react13.motion.svg,
|
|
1402
|
+
{
|
|
1403
|
+
className: "dialkit-select-chevron",
|
|
1404
|
+
viewBox: "0 0 24 24",
|
|
1405
|
+
fill: "none",
|
|
1406
|
+
stroke: "currentColor",
|
|
1407
|
+
strokeWidth: "2.5",
|
|
1408
|
+
strokeLinecap: "round",
|
|
1409
|
+
strokeLinejoin: "round",
|
|
1410
|
+
animate: { rotate: isOpen ? 180 : 0 },
|
|
1411
|
+
transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
|
|
1412
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
|
|
1413
|
+
}
|
|
1414
|
+
)
|
|
1415
|
+
]
|
|
1416
|
+
}
|
|
1417
|
+
),
|
|
1418
|
+
(0, import_react_dom.createPortal)(
|
|
1419
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react13.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1420
|
+
import_react13.motion.div,
|
|
1421
|
+
{
|
|
1422
|
+
ref: dropdownRef,
|
|
1423
|
+
className: "dialkit-preset-dropdown",
|
|
1424
|
+
style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.width },
|
|
1425
|
+
initial: { opacity: 0, y: 4, scale: 0.97 },
|
|
1426
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
1427
|
+
exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
|
|
1428
|
+
transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
|
|
1429
|
+
children: [
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1431
|
+
"div",
|
|
1432
|
+
{
|
|
1433
|
+
className: "dialkit-preset-item",
|
|
1434
|
+
"data-active": String(!activePresetId),
|
|
1435
|
+
onClick: () => handleSelect(null),
|
|
1436
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-preset-name", children: "Version 1" })
|
|
1437
|
+
}
|
|
1438
|
+
),
|
|
1439
|
+
presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1440
|
+
"div",
|
|
1441
|
+
{
|
|
1442
|
+
className: "dialkit-preset-item",
|
|
1443
|
+
"data-active": String(preset.id === activePresetId),
|
|
1444
|
+
onClick: () => handleSelect(preset.id),
|
|
1445
|
+
children: [
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-preset-name", children: preset.name }),
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1448
|
+
"button",
|
|
1449
|
+
{
|
|
1450
|
+
className: "dialkit-preset-delete",
|
|
1451
|
+
onClick: (e) => handleDelete(e, preset.id),
|
|
1452
|
+
title: "Delete preset",
|
|
1453
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1454
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("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" }),
|
|
1455
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M10 11V16" }),
|
|
1456
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M14 11V16" }),
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M3.5 6H20.5" }),
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("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" })
|
|
1459
|
+
] })
|
|
1460
|
+
}
|
|
1461
|
+
)
|
|
1462
|
+
]
|
|
1463
|
+
},
|
|
1464
|
+
preset.id
|
|
1465
|
+
))
|
|
1466
|
+
]
|
|
1467
|
+
}
|
|
1468
|
+
) }),
|
|
1469
|
+
document.body
|
|
1470
|
+
)
|
|
1471
|
+
] });
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// src/components/Panel.tsx
|
|
1475
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
957
1476
|
function Panel({ panel }) {
|
|
958
|
-
const
|
|
1477
|
+
const [copied, setCopied] = (0, import_react14.useState)(false);
|
|
1478
|
+
const [isPanelOpen, setIsPanelOpen] = (0, import_react14.useState)(true);
|
|
1479
|
+
const values = (0, import_react14.useSyncExternalStore)(
|
|
959
1480
|
(cb) => DialStore.subscribe(panel.id, cb),
|
|
960
1481
|
() => DialStore.getValues(panel.id),
|
|
961
1482
|
() => DialStore.getValues(panel.id)
|
|
962
1483
|
);
|
|
1484
|
+
const presets = DialStore.getPresets(panel.id);
|
|
1485
|
+
const activePresetId = DialStore.getActivePresetId(panel.id);
|
|
1486
|
+
const handleAddPreset = () => {
|
|
1487
|
+
const nextNum = presets.length + 2;
|
|
1488
|
+
DialStore.savePreset(panel.id, `Version ${nextNum}`);
|
|
1489
|
+
};
|
|
1490
|
+
const handleCopy = () => {
|
|
1491
|
+
const jsonStr = JSON.stringify(values, null, 2);
|
|
1492
|
+
const instruction = `Update the useDialKit configuration for "${panel.name}" with these values:
|
|
1493
|
+
|
|
1494
|
+
\`\`\`json
|
|
1495
|
+
${jsonStr}
|
|
1496
|
+
\`\`\`
|
|
1497
|
+
|
|
1498
|
+
Apply these values as the new defaults in the useDialKit call.`;
|
|
1499
|
+
navigator.clipboard.writeText(instruction);
|
|
1500
|
+
setCopied(true);
|
|
1501
|
+
setTimeout(() => setCopied(false), 1500);
|
|
1502
|
+
};
|
|
963
1503
|
const renderControl = (control) => {
|
|
964
1504
|
const value = values[control.path];
|
|
965
1505
|
switch (control.type) {
|
|
966
1506
|
case "slider":
|
|
967
|
-
return /* @__PURE__ */ (0,
|
|
1507
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
968
1508
|
Slider,
|
|
969
1509
|
{
|
|
970
1510
|
label: control.label,
|
|
@@ -977,7 +1517,7 @@ function Panel({ panel }) {
|
|
|
977
1517
|
control.path
|
|
978
1518
|
);
|
|
979
1519
|
case "toggle":
|
|
980
|
-
return /* @__PURE__ */ (0,
|
|
1520
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
981
1521
|
Toggle,
|
|
982
1522
|
{
|
|
983
1523
|
label: control.label,
|
|
@@ -987,7 +1527,7 @@ function Panel({ panel }) {
|
|
|
987
1527
|
control.path
|
|
988
1528
|
);
|
|
989
1529
|
case "spring":
|
|
990
|
-
return /* @__PURE__ */ (0,
|
|
1530
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
991
1531
|
SpringControl,
|
|
992
1532
|
{
|
|
993
1533
|
panelId: panel.id,
|
|
@@ -999,7 +1539,39 @@ function Panel({ panel }) {
|
|
|
999
1539
|
control.path
|
|
1000
1540
|
);
|
|
1001
1541
|
case "folder":
|
|
1002
|
-
return /* @__PURE__ */ (0,
|
|
1542
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Folder, { title: control.label, children: control.children?.map(renderControl) }, control.path);
|
|
1543
|
+
case "text":
|
|
1544
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1545
|
+
TextControl,
|
|
1546
|
+
{
|
|
1547
|
+
label: control.label,
|
|
1548
|
+
value,
|
|
1549
|
+
onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
|
|
1550
|
+
placeholder: control.placeholder
|
|
1551
|
+
},
|
|
1552
|
+
control.path
|
|
1553
|
+
);
|
|
1554
|
+
case "select":
|
|
1555
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1556
|
+
SelectControl,
|
|
1557
|
+
{
|
|
1558
|
+
label: control.label,
|
|
1559
|
+
value,
|
|
1560
|
+
options: control.options ?? [],
|
|
1561
|
+
onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
|
|
1562
|
+
},
|
|
1563
|
+
control.path
|
|
1564
|
+
);
|
|
1565
|
+
case "color":
|
|
1566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1567
|
+
ColorControl,
|
|
1568
|
+
{
|
|
1569
|
+
label: control.label,
|
|
1570
|
+
value,
|
|
1571
|
+
onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
|
|
1572
|
+
},
|
|
1573
|
+
control.path
|
|
1574
|
+
);
|
|
1003
1575
|
default:
|
|
1004
1576
|
return null;
|
|
1005
1577
|
}
|
|
@@ -1010,24 +1582,16 @@ function Panel({ panel }) {
|
|
|
1010
1582
|
while (i < panel.controls.length) {
|
|
1011
1583
|
const control = panel.controls[i];
|
|
1012
1584
|
if (control.type === "action") {
|
|
1013
|
-
const actions = [control];
|
|
1014
|
-
while (i + 1 < panel.controls.length && panel.controls[i + 1].type === "action") {
|
|
1015
|
-
i++;
|
|
1016
|
-
actions.push(panel.controls[i]);
|
|
1017
|
-
}
|
|
1018
1585
|
result.push(
|
|
1019
|
-
/* @__PURE__ */ (0,
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
"button",
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
action.path
|
|
1029
|
-
)) })
|
|
1030
|
-
] }, `actions-${actions[0].path}`)
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1587
|
+
"button",
|
|
1588
|
+
{
|
|
1589
|
+
className: "dialkit-button",
|
|
1590
|
+
onClick: () => DialStore.triggerAction(panel.id, control.path),
|
|
1591
|
+
children: control.label
|
|
1592
|
+
},
|
|
1593
|
+
control.path
|
|
1594
|
+
)
|
|
1031
1595
|
);
|
|
1032
1596
|
} else {
|
|
1033
1597
|
result.push(renderControl(control));
|
|
@@ -1036,15 +1600,88 @@ function Panel({ panel }) {
|
|
|
1036
1600
|
}
|
|
1037
1601
|
return result;
|
|
1038
1602
|
};
|
|
1039
|
-
|
|
1603
|
+
const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
|
|
1604
|
+
const toolbar = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1605
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1606
|
+
"button",
|
|
1607
|
+
{
|
|
1608
|
+
className: "dialkit-toolbar-add",
|
|
1609
|
+
onClick: handleAddPreset,
|
|
1610
|
+
title: "Add preset",
|
|
1611
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1612
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 6H20" }),
|
|
1613
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 12H10" }),
|
|
1614
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M15 15L21 15" }),
|
|
1615
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M18 12V18" }),
|
|
1616
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 18H10" })
|
|
1617
|
+
] })
|
|
1618
|
+
}
|
|
1619
|
+
),
|
|
1620
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1621
|
+
PresetManager,
|
|
1622
|
+
{
|
|
1623
|
+
panelId: panel.id,
|
|
1624
|
+
presets,
|
|
1625
|
+
activePresetId,
|
|
1626
|
+
onAdd: handleAddPreset
|
|
1627
|
+
}
|
|
1628
|
+
),
|
|
1629
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1630
|
+
"button",
|
|
1631
|
+
{
|
|
1632
|
+
className: "dialkit-toolbar-copy",
|
|
1633
|
+
onClick: handleCopy,
|
|
1634
|
+
title: "Copy parameters",
|
|
1635
|
+
children: [
|
|
1636
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dialkit-toolbar-copy-icon-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react15.AnimatePresence, { mode: "popLayout", children: copied ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1637
|
+
import_react15.motion.svg,
|
|
1638
|
+
{
|
|
1639
|
+
className: "dialkit-toolbar-copy-icon",
|
|
1640
|
+
viewBox: "0 0 24 24",
|
|
1641
|
+
fill: "none",
|
|
1642
|
+
stroke: "currentColor",
|
|
1643
|
+
strokeWidth: "2",
|
|
1644
|
+
strokeLinecap: "round",
|
|
1645
|
+
strokeLinejoin: "round",
|
|
1646
|
+
initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1647
|
+
animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
|
|
1648
|
+
exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1649
|
+
transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
1650
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M5 12.75L10 19L19 5" })
|
|
1651
|
+
},
|
|
1652
|
+
"check"
|
|
1653
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1654
|
+
import_react15.motion.svg,
|
|
1655
|
+
{
|
|
1656
|
+
className: "dialkit-toolbar-copy-icon",
|
|
1657
|
+
viewBox: "0 0 24 24",
|
|
1658
|
+
fill: "none",
|
|
1659
|
+
initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1660
|
+
animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
|
|
1661
|
+
exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
|
|
1662
|
+
transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
1663
|
+
children: [
|
|
1664
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
|
|
1665
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }),
|
|
1666
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" })
|
|
1667
|
+
]
|
|
1668
|
+
},
|
|
1669
|
+
"clipboard"
|
|
1670
|
+
) }) }),
|
|
1671
|
+
"Copy"
|
|
1672
|
+
]
|
|
1673
|
+
}
|
|
1674
|
+
)
|
|
1675
|
+
] });
|
|
1676
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Folder, { title: panel.name, defaultOpen: true, isRoot: true, onOpenChange: setIsPanelOpen, toolbar, children: renderControls() }) });
|
|
1040
1677
|
}
|
|
1041
1678
|
|
|
1042
1679
|
// src/components/DialRoot.tsx
|
|
1043
|
-
var
|
|
1680
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1044
1681
|
function DialRoot({ position = "top-right" }) {
|
|
1045
|
-
const [panels, setPanels] = (0,
|
|
1046
|
-
const [mounted, setMounted] = (0,
|
|
1047
|
-
(0,
|
|
1682
|
+
const [panels, setPanels] = (0, import_react16.useState)([]);
|
|
1683
|
+
const [mounted, setMounted] = (0, import_react16.useState)(false);
|
|
1684
|
+
(0, import_react16.useEffect)(() => {
|
|
1048
1685
|
setMounted(true);
|
|
1049
1686
|
setPanels(DialStore.getPanels());
|
|
1050
1687
|
const unsubscribe = DialStore.subscribeGlobal(() => {
|
|
@@ -1058,14 +1695,14 @@ function DialRoot({ position = "top-right" }) {
|
|
|
1058
1695
|
if (panels.length === 0) {
|
|
1059
1696
|
return null;
|
|
1060
1697
|
}
|
|
1061
|
-
const content = /* @__PURE__ */ (0,
|
|
1062
|
-
return (0,
|
|
1698
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dialkit-root", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dialkit-panel", "data-position": position, children: panels.map((panel) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Panel, { panel }, panel.id)) }) });
|
|
1699
|
+
return (0, import_react_dom2.createPortal)(content, document.body);
|
|
1063
1700
|
}
|
|
1064
1701
|
|
|
1065
1702
|
// src/components/ButtonGroup.tsx
|
|
1066
|
-
var
|
|
1703
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1067
1704
|
function ButtonGroup({ buttons }) {
|
|
1068
|
-
return /* @__PURE__ */ (0,
|
|
1705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1069
1706
|
"button",
|
|
1070
1707
|
{
|
|
1071
1708
|
className: "dialkit-button",
|
|
@@ -1078,13 +1715,16 @@ function ButtonGroup({ buttons }) {
|
|
|
1078
1715
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1079
1716
|
0 && (module.exports = {
|
|
1080
1717
|
ButtonGroup,
|
|
1718
|
+
ColorControl,
|
|
1081
1719
|
DialRoot,
|
|
1082
1720
|
DialStore,
|
|
1083
1721
|
Folder,
|
|
1084
|
-
|
|
1722
|
+
PresetManager,
|
|
1723
|
+
SelectControl,
|
|
1085
1724
|
Slider,
|
|
1086
1725
|
SpringControl,
|
|
1087
1726
|
SpringVisualization,
|
|
1727
|
+
TextControl,
|
|
1088
1728
|
Toggle,
|
|
1089
1729
|
useDialKit
|
|
1090
1730
|
});
|