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/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
- SegmentedControl: () => SegmentedControl,
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
  });
@@ -46,12 +49,16 @@ var DialStoreClass = class {
46
49
  this.globalListeners = /* @__PURE__ */ new Set();
47
50
  this.snapshots = /* @__PURE__ */ new Map();
48
51
  this.actionListeners = /* @__PURE__ */ new Map();
52
+ this.presets = /* @__PURE__ */ new Map();
53
+ this.activePreset = /* @__PURE__ */ new Map();
54
+ this.baseValues = /* @__PURE__ */ new Map();
49
55
  }
50
56
  registerPanel(id, name, config) {
51
57
  const controls = this.parseConfig(config, "");
52
58
  const values = this.flattenValues(config, "");
53
59
  this.panels.set(id, { id, name, controls, values });
54
60
  this.snapshots.set(id, { ...values });
61
+ this.baseValues.set(id, { ...values });
55
62
  this.notifyGlobal();
56
63
  }
57
64
  unregisterPanel(id) {
@@ -64,6 +71,15 @@ var DialStoreClass = class {
64
71
  const panel = this.panels.get(panelId);
65
72
  if (!panel) return;
66
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
+ }
67
83
  this.snapshots.set(panelId, { ...panel.values });
68
84
  this.notify(panelId);
69
85
  }
@@ -117,6 +133,61 @@ var DialStoreClass = class {
117
133
  triggerAction(panelId, path) {
118
134
  this.actionListeners.get(panelId)?.forEach((fn) => fn(path));
119
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
+ }
120
191
  notify(panelId) {
121
192
  this.listeners.get(panelId)?.forEach((fn) => fn());
122
193
  }
@@ -146,6 +217,18 @@ var DialStoreClass = class {
146
217
  controls.push({ type: "spring", path, label });
147
218
  } else if (this.isActionConfig(value)) {
148
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
+ }
149
232
  } else if (typeof value === "object" && value !== null) {
150
233
  controls.push({
151
234
  type: "folder",
@@ -169,6 +252,14 @@ var DialStoreClass = class {
169
252
  values[path] = value;
170
253
  } else if (this.isActionConfig(value)) {
171
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 ?? "";
172
263
  } else if (typeof value === "object" && value !== null) {
173
264
  Object.assign(values, this.flattenValues(value, path));
174
265
  }
@@ -181,6 +272,18 @@ var DialStoreClass = class {
181
272
  isActionConfig(value) {
182
273
  return typeof value === "object" && value !== null && "type" in value && value.type === "action";
183
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
+ }
184
287
  formatLabel(key) {
185
288
  return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
186
289
  }
@@ -188,13 +291,13 @@ var DialStoreClass = class {
188
291
  if (value >= 0 && value <= 1) {
189
292
  return { min: 0, max: 1, step: 0.01 };
190
293
  } else if (value >= 0 && value <= 10) {
191
- return { min: 0, max: value * 2 || 10, step: 0.1 };
294
+ return { min: 0, max: value * 3 || 10, step: 0.1 };
192
295
  } else if (value >= 0 && value <= 100) {
193
- return { min: 0, max: value * 2 || 100, step: 1 };
296
+ return { min: 0, max: value * 3 || 100, step: 1 };
194
297
  } else if (value >= 0) {
195
- return { min: 0, max: value * 2 || 1e3, step: 10 };
298
+ return { min: 0, max: value * 3 || 1e3, step: 10 };
196
299
  } else {
197
- return { min: value * 2, max: -value * 2, step: 1 };
300
+ return { min: value * 3, max: -value * 3, step: 1 };
198
301
  }
199
302
  }
200
303
  inferStep(min, max) {
@@ -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 typeof value === "object" && value !== null && "type" in value && value.type === "spring";
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 import_react10 = require("react");
255
- var import_react_dom = require("react-dom");
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, panelId }) {
396
+ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenChange, toolbar }) {
262
397
  const [isOpen, setIsOpen] = (0, import_react2.useState)(defaultOpen);
263
- const [copied, setCopied] = (0, import_react2.useState)(false);
264
- const handleCopy = (e) => {
265
- e.stopPropagation();
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 folderContent = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
282
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-header", onClick: () => setIsOpen(!isOpen), children: [
283
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-title-row", children: [
284
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `dialkit-folder-title ${isRoot ? "dialkit-folder-title-root" : ""}`, children: title }),
285
- isRoot && panelId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
286
- import_react3.motion.button,
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-copy",
289
- onClick: handleCopy,
290
- title: "Copy parameters",
291
- initial: false,
292
- animate: {
293
- opacity: isOpen ? 1 : 0,
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
- isRoot ? (
348
- // Root panel uses minus/plus icons with crossfade
349
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-icon", style: { position: "relative" }, children: [
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
- viewBox: "0 0 24 24",
439
+ className: "dialkit-panel-icon",
440
+ viewBox: "0 0 16 16",
354
441
  fill: "none",
355
- stroke: "currentColor",
356
- strokeWidth: "2",
357
- strokeLinecap: "round",
358
- strokeLinejoin: "round",
359
- style: { position: "absolute", inset: 0, width: "100%", height: "100%" },
360
- initial: false,
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
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
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
- opacity: isOpen ? 0 : 1,
383
- scale: isOpen ? 0.7 : 1,
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.25, bounce: 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 : 160,
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: { type: "spring", visualDuration: 0.4, bounce: 0.05 },
440
- "data-collapsed": !isOpen,
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
- if (!trackRef.current) return value;
477
- const rect = trackRef.current.getBoundingClientRect();
478
- const x = clientX - rect.left;
479
- const percent = Math.max(0, Math.min(1, x / rect.width));
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 handleMouseDown = (0, import_react4.useCallback)(
486
- (e) => {
487
- if (showInput) return;
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 handleMouseMove = (0, import_react4.useCallback)(
495
- (e) => {
496
- if (!isInteracting) return;
497
- if (!isDragging) setIsDragging(true);
498
- onChange(positionToValue(e.clientX));
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
- [isInteracting, isDragging, positionToValue, onChange]
619
+ []
501
620
  );
502
- const handleMouseUp = (0, import_react4.useCallback)(() => {
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
- const touch = e.touches[0];
512
- onChange(positionToValue(touch.clientX));
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
- [positionToValue, onChange, showInput]
635
+ [showInput]
515
636
  );
516
- const handleTouchMove = (0, import_react4.useCallback)(
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 (!isDragging) setIsDragging(true);
520
- const touch = e.touches[0];
521
- onChange(positionToValue(touch.clientX));
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
- [isInteracting, isDragging, positionToValue, onChange]
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 shouldAnimate = !isDragging;
601
- const transition = shouldAnimate ? { type: "spring", visualDuration: 0.3, bounce: 0.15 } : { duration: 0 };
602
- const handleLeft = isAtMinimum ? 7 : `calc(${percentage}% - 9px)`;
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: 7 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dialkit-slider-hashmark", children: "\u2022" }, i));
605
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
606
- "div",
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
- onMouseDown: handleMouseDown,
611
- onTouchStart: handleTouchStart,
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: { background: fillBackground, width: `${percentage}%`, transition: "background 0.15s" },
621
- animate: { width: `${percentage}%` },
622
- transition
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
- opacity: isActive ? 1 : 0
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
- animate: { left: handleLeft, opacity: isActive ? 1 : 0 },
635
- transition
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.jsxs)(
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/Panel.tsx
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 values = (0, import_react9.useSyncExternalStore)(
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, import_jsx_runtime7.jsx)(
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, import_jsx_runtime7.jsx)(
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, import_jsx_runtime7.jsx)(
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, import_jsx_runtime7.jsx)(Folder, { title: control.label, children: control.children?.map(renderControl) }, control.path);
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, import_jsx_runtime7.jsxs)("div", { className: "dialkit-labeled-control dialkit-actions-group", children: [
1020
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dialkit-labeled-control-label", children: "Actions" }),
1021
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dialkit-actions-stack", children: actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1022
- "button",
1023
- {
1024
- className: "dialkit-action-button",
1025
- onClick: () => DialStore.triggerAction(panel.id, action.path),
1026
- children: action.label
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
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Folder, { title: panel.name, defaultOpen: true, isRoot: true, panelId: panel.id, children: renderControls() });
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 import_jsx_runtime8 = require("react/jsx-runtime");
1680
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1044
1681
  function DialRoot({ position = "top-right" }) {
1045
- const [panels, setPanels] = (0, import_react10.useState)([]);
1046
- const [mounted, setMounted] = (0, import_react10.useState)(false);
1047
- (0, import_react10.useEffect)(() => {
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, import_jsx_runtime8.jsx)("div", { className: "dialkit-root", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dialkit-panel", "data-position": position, children: panels.map((panel) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Panel, { panel }, panel.id)) }) });
1062
- return (0, import_react_dom.createPortal)(content, document.body);
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 import_jsx_runtime9 = require("react/jsx-runtime");
1703
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1067
1704
  function ButtonGroup({ buttons }) {
1068
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
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
- SegmentedControl,
1722
+ PresetManager,
1723
+ SelectControl,
1085
1724
  Slider,
1086
1725
  SpringControl,
1087
1726
  SpringVisualization,
1727
+ TextControl,
1088
1728
  Toggle,
1089
1729
  useDialKit
1090
1730
  });