dialkit 0.1.2 → 0.2.1

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,204 +343,149 @@ 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);
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);
401
+ (0, import_react2.useEffect)(() => {
402
+ const el = contentRef.current;
403
+ if (!el) return;
404
+ const ro = new ResizeObserver(() => {
405
+ if (isOpen) {
406
+ const h = el.offsetHeight;
407
+ setContentHeight((prev) => prev === h ? prev : h);
408
+ }
409
+ });
410
+ ro.observe(el);
411
+ return () => ro.disconnect();
412
+ }, [isOpen]);
413
+ const handleToggle = () => {
414
+ const next = !isOpen;
415
+ setIsOpen(next);
416
+ if (next) {
417
+ setIsCollapsed(false);
418
+ } else {
419
+ setIsCollapsed(true);
420
+ }
421
+ onOpenChange?.(next);
279
422
  };
280
- 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,
287
- {
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
- ] })
344
- }
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,
423
+ const folderContent = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
424
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: handleToggle, children: [
425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-header-top", children: [
426
+ isRoot ? isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title }) }) : /* @__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 }) }),
427
+ isRoot ? (
428
+ // Root panel icon fixed position, container morphs around it
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
430
+ "svg",
352
431
  {
353
- viewBox: "0 0 24 24",
432
+ className: "dialkit-panel-icon",
433
+ viewBox: "0 0 16 16",
354
434
  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" })
435
+ children: [
436
+ /* @__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" }),
437
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "8", r: "0.998596", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
438
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.4999", cy: "3.5", r: "0.998657", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9.75015", cy: "12.5", r: "0.997986", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })
440
+ ]
368
441
  }
369
- ),
370
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
442
+ )
443
+ ) : (
444
+ // Section folders use rotating chevron with gentle spring
445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
371
446
  import_react3.motion.svg,
372
447
  {
448
+ className: "dialkit-folder-icon",
373
449
  viewBox: "0 0 24 24",
374
450
  fill: "none",
375
451
  stroke: "currentColor",
376
- strokeWidth: "2",
452
+ strokeWidth: "2.5",
377
453
  strokeLinecap: "round",
378
454
  strokeLinejoin: "round",
379
- style: { position: "absolute", inset: 0, width: "100%", height: "100%" },
380
455
  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
- ]
456
+ animate: { rotate: isOpen ? 0 : 180 },
457
+ transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
458
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
391
459
  }
392
460
  )
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
461
  )
415
- )
462
+ ] }),
463
+ isRoot && toolbar && isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
416
464
  ] }),
417
465
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
418
466
  import_react3.motion.div,
419
467
  {
420
468
  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 },
469
+ initial: isRoot ? void 0 : { height: 0, opacity: 0 },
470
+ animate: isRoot ? void 0 : { height: "auto", opacity: 1 },
471
+ exit: isRoot ? void 0 : { height: 0, opacity: 0 },
472
+ transition: isRoot ? void 0 : { type: "spring", visualDuration: 0.35, bounce: 0.1 },
473
+ style: isRoot ? void 0 : { clipPath: "inset(0 -20px)" },
425
474
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-inner", children })
426
475
  }
427
476
  ) })
428
477
  ] });
429
478
  if (isRoot) {
479
+ const panelStyle = isOpen ? { width: 280, height: contentHeight !== void 0 ? contentHeight + 24 : "auto", borderRadius: 14, boxShadow: "0 8px 32px rgba(0, 0, 0, 0.5)", cursor: void 0 } : { width: 42, height: 42, borderRadius: 21, boxShadow: "0 4px 16px rgba(0, 0, 0, 0.25)", overflow: "hidden", cursor: "pointer" };
430
480
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
431
481
  import_react3.motion.div,
432
482
  {
433
483
  className: "dialkit-panel-inner",
434
- initial: false,
435
- animate: {
436
- width: isOpen ? 280 : 160,
437
- boxShadow: isOpen ? "0 8px 32px rgba(0, 0, 0, 0.5)" : "0 4px 16px rgba(0, 0, 0, 0.25)"
438
- },
439
- transition: { type: "spring", visualDuration: 0.4, bounce: 0.05 },
440
- "data-collapsed": !isOpen,
484
+ style: panelStyle,
485
+ onClick: !isOpen ? handleToggle : void 0,
486
+ "data-collapsed": isCollapsed,
487
+ whileTap: !isOpen ? { scale: 0.9 } : void 0,
488
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
441
489
  children: folderContent
442
490
  }
443
491
  );
@@ -449,6 +497,23 @@ Apply these values as the new defaults in the useDialKit call.`;
449
497
  var import_react4 = require("react");
450
498
  var import_react5 = require("motion/react");
451
499
  var import_jsx_runtime2 = require("react/jsx-runtime");
500
+ var CLICK_THRESHOLD = 3;
501
+ var DEAD_ZONE = 32;
502
+ var MAX_CURSOR_RANGE = 200;
503
+ var MAX_STRETCH = 8;
504
+ function roundValue(val, step) {
505
+ const decimals = step >= 1 ? 0 : 2;
506
+ const factor = 10 ** decimals;
507
+ return Math.round(val * factor) / factor;
508
+ }
509
+ function snapToDecile(rawValue, min, max) {
510
+ const normalized = (rawValue - min) / (max - min);
511
+ const nearest = Math.round(normalized * 10) / 10;
512
+ if (Math.abs(normalized - nearest) <= 0.03125) {
513
+ return min + nearest * (max - min);
514
+ }
515
+ return rawValue;
516
+ }
452
517
  function Slider({
453
518
  label,
454
519
  value,
@@ -458,8 +523,11 @@ function Slider({
458
523
  step = 0.01,
459
524
  unit
460
525
  }) {
526
+ const wrapperRef = (0, import_react4.useRef)(null);
461
527
  const trackRef = (0, import_react4.useRef)(null);
462
528
  const inputRef = (0, import_react4.useRef)(null);
529
+ const labelRef = (0, import_react4.useRef)(null);
530
+ const valueSpanRef = (0, import_react4.useRef)(null);
463
531
  const [isInteracting, setIsInteracting] = (0, import_react4.useState)(false);
464
532
  const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
465
533
  const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
@@ -468,78 +536,160 @@ function Slider({
468
536
  const [showInput, setShowInput] = (0, import_react4.useState)(false);
469
537
  const [inputValue, setInputValue] = (0, import_react4.useState)("");
470
538
  const hoverTimeoutRef = (0, import_react4.useRef)(null);
539
+ const pointerDownPos = (0, import_react4.useRef)(null);
540
+ const isClickRef = (0, import_react4.useRef)(true);
541
+ const animRef = (0, import_react4.useRef)(null);
542
+ const wrapperRectRef = (0, import_react4.useRef)(null);
543
+ const scaleRef = (0, import_react4.useRef)(1);
471
544
  const percentage = (value - min) / (max - min) * 100;
472
- const isAtMinimum = value <= min;
473
545
  const isActive = isInteracting || isHovered;
546
+ const fillPercent = (0, import_react5.useMotionValue)(percentage);
547
+ const fillWidth = (0, import_react5.useTransform)(fillPercent, (pct) => `${pct}%`);
548
+ const handleLeft = (0, import_react5.useTransform)(
549
+ fillPercent,
550
+ (pct) => `max(5px, calc(${pct}% - 9px))`
551
+ );
552
+ const rubberStretchPx = (0, import_react5.useMotionValue)(0);
553
+ const rubberBandWidth = (0, import_react5.useTransform)(
554
+ rubberStretchPx,
555
+ (stretch) => `calc(100% + ${Math.abs(stretch)}px)`
556
+ );
557
+ const rubberBandX = (0, import_react5.useTransform)(
558
+ rubberStretchPx,
559
+ (stretch) => stretch < 0 ? stretch : 0
560
+ );
561
+ (0, import_react4.useEffect)(() => {
562
+ if (!isInteracting && !animRef.current) {
563
+ fillPercent.jump(percentage);
564
+ }
565
+ }, [percentage, isInteracting, fillPercent]);
474
566
  const positionToValue = (0, import_react4.useCallback)(
475
567
  (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));
568
+ const rect = wrapperRectRef.current;
569
+ if (!rect) return value;
570
+ const screenX = clientX - rect.left;
571
+ const sceneX = screenX / scaleRef.current;
572
+ const nativeWidth = wrapperRef.current ? wrapperRef.current.offsetWidth : rect.width;
573
+ const percent = Math.max(0, Math.min(1, sceneX / nativeWidth));
480
574
  const rawValue = min + percent * (max - min);
481
575
  return Math.max(min, Math.min(max, rawValue));
482
576
  },
483
577
  [min, max, value]
484
578
  );
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]
579
+ const percentFromValue = (0, import_react4.useCallback)(
580
+ (v) => (v - min) / (max - min) * 100,
581
+ [min, max]
493
582
  );
494
- const handleMouseMove = (0, import_react4.useCallback)(
495
- (e) => {
496
- if (!isInteracting) return;
497
- if (!isDragging) setIsDragging(true);
498
- onChange(positionToValue(e.clientX));
583
+ const computeRubberStretch = (0, import_react4.useCallback)(
584
+ (clientX, sign) => {
585
+ const rect = wrapperRectRef.current;
586
+ if (!rect) return 0;
587
+ const distancePast = sign < 0 ? rect.left - clientX : clientX - rect.right;
588
+ const overflow = Math.max(0, distancePast - DEAD_ZONE);
589
+ return sign * MAX_STRETCH * Math.sqrt(Math.min(overflow / MAX_CURSOR_RANGE, 1));
499
590
  },
500
- [isInteracting, isDragging, positionToValue, onChange]
591
+ []
501
592
  );
502
- const handleMouseUp = (0, import_react4.useCallback)(() => {
503
- setIsInteracting(false);
504
- setIsDragging(false);
505
- }, []);
506
- const handleTouchStart = (0, import_react4.useCallback)(
593
+ const handlePointerDown = (0, import_react4.useCallback)(
507
594
  (e) => {
508
595
  if (showInput) return;
509
596
  e.preventDefault();
597
+ e.target.setPointerCapture(e.pointerId);
598
+ pointerDownPos.current = { x: e.clientX, y: e.clientY };
599
+ isClickRef.current = true;
510
600
  setIsInteracting(true);
511
- const touch = e.touches[0];
512
- onChange(positionToValue(touch.clientX));
601
+ if (wrapperRef.current) {
602
+ wrapperRectRef.current = wrapperRef.current.getBoundingClientRect();
603
+ const nativeWidth = wrapperRef.current.offsetWidth;
604
+ scaleRef.current = wrapperRectRef.current.width / nativeWidth;
605
+ }
606
+ },
607
+ [showInput]
608
+ );
609
+ const handlePointerMove = (0, import_react4.useCallback)(
610
+ (e) => {
611
+ if (!isInteracting || !pointerDownPos.current) return;
612
+ const dx = e.clientX - pointerDownPos.current.x;
613
+ const dy = e.clientY - pointerDownPos.current.y;
614
+ const distance = Math.sqrt(dx * dx + dy * dy);
615
+ if (isClickRef.current && distance > CLICK_THRESHOLD) {
616
+ isClickRef.current = false;
617
+ setIsDragging(true);
618
+ }
619
+ if (!isClickRef.current) {
620
+ const rect = wrapperRectRef.current;
621
+ if (rect) {
622
+ if (e.clientX < rect.left) {
623
+ rubberStretchPx.jump(computeRubberStretch(e.clientX, -1));
624
+ } else if (e.clientX > rect.right) {
625
+ rubberStretchPx.jump(computeRubberStretch(e.clientX, 1));
626
+ } else {
627
+ rubberStretchPx.jump(0);
628
+ }
629
+ }
630
+ const newValue = positionToValue(e.clientX);
631
+ const newPct = percentFromValue(newValue);
632
+ if (animRef.current) {
633
+ animRef.current.stop();
634
+ animRef.current = null;
635
+ }
636
+ fillPercent.jump(newPct);
637
+ onChange(roundValue(newValue, step));
638
+ }
513
639
  },
514
- [positionToValue, onChange, showInput]
640
+ [
641
+ isInteracting,
642
+ positionToValue,
643
+ percentFromValue,
644
+ onChange,
645
+ fillPercent,
646
+ rubberStretchPx,
647
+ computeRubberStretch
648
+ ]
515
649
  );
516
- const handleTouchMove = (0, import_react4.useCallback)(
650
+ const handlePointerUp = (0, import_react4.useCallback)(
517
651
  (e) => {
518
652
  if (!isInteracting) return;
519
- if (!isDragging) setIsDragging(true);
520
- const touch = e.touches[0];
521
- onChange(positionToValue(touch.clientX));
653
+ if (isClickRef.current) {
654
+ const rawValue = positionToValue(e.clientX);
655
+ const snappedValue = snapToDecile(rawValue, min, max);
656
+ const newPct = percentFromValue(snappedValue);
657
+ if (animRef.current) {
658
+ animRef.current.stop();
659
+ }
660
+ animRef.current = (0, import_react5.animate)(fillPercent, newPct, {
661
+ type: "spring",
662
+ stiffness: 300,
663
+ damping: 25,
664
+ mass: 0.8,
665
+ onComplete: () => {
666
+ animRef.current = null;
667
+ }
668
+ });
669
+ onChange(roundValue(snappedValue, step));
670
+ }
671
+ if (rubberStretchPx.get() !== 0) {
672
+ (0, import_react5.animate)(rubberStretchPx, 0, {
673
+ type: "spring",
674
+ visualDuration: 0.35,
675
+ bounce: 0.15
676
+ });
677
+ }
678
+ setIsInteracting(false);
679
+ setIsDragging(false);
680
+ pointerDownPos.current = null;
522
681
  },
523
- [isInteracting, isDragging, positionToValue, onChange]
682
+ [
683
+ isInteracting,
684
+ positionToValue,
685
+ percentFromValue,
686
+ onChange,
687
+ min,
688
+ max,
689
+ fillPercent,
690
+ rubberStretchPx
691
+ ]
524
692
  );
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
693
  (0, import_react4.useEffect)(() => {
544
694
  if (isValueHovered && !showInput && !isValueEditable) {
545
695
  hoverTimeoutRef.current = setTimeout(() => {
@@ -571,7 +721,7 @@ function Slider({
571
721
  const parsed = parseFloat(inputValue);
572
722
  if (!isNaN(parsed)) {
573
723
  const clamped = Math.max(min, Math.min(max, parsed));
574
- onChange(clamped);
724
+ onChange(roundValue(clamped, step));
575
725
  }
576
726
  setShowInput(false);
577
727
  setIsValueHovered(false);
@@ -597,29 +747,56 @@ function Slider({
597
747
  handleInputSubmit();
598
748
  };
599
749
  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)`;
750
+ const HANDLE_BUFFER = 8;
751
+ const LABEL_CSS_LEFT = 10;
752
+ const VALUE_CSS_RIGHT = 10;
753
+ let leftThreshold = 30;
754
+ let rightThreshold = 78;
755
+ const trackWidth = wrapperRef.current?.offsetWidth;
756
+ if (trackWidth && trackWidth > 0) {
757
+ if (labelRef.current) {
758
+ leftThreshold = (LABEL_CSS_LEFT + labelRef.current.offsetWidth + HANDLE_BUFFER) / trackWidth * 100;
759
+ }
760
+ if (valueSpanRef.current) {
761
+ rightThreshold = (trackWidth - VALUE_CSS_RIGHT - valueSpanRef.current.offsetWidth - HANDLE_BUFFER) / trackWidth * 100;
762
+ }
763
+ }
764
+ const valueDodge = percentage < leftThreshold || percentage > rightThreshold;
765
+ const handleOpacity = !isActive ? 0 : valueDodge ? 0.1 : isDragging ? 0.9 : 0.5;
603
766
  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",
767
+ const hashMarks = Array.from({ length: 9 }, (_, i) => {
768
+ const pct = (i + 1) * 10;
769
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
770
+ "div",
771
+ {
772
+ className: "dialkit-slider-hashmark",
773
+ style: { left: `${pct}%` }
774
+ },
775
+ i
776
+ );
777
+ });
778
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
779
+ import_react5.motion.div,
607
780
  {
608
781
  ref: trackRef,
609
782
  className: `dialkit-slider ${isActive ? "dialkit-slider-active" : ""}`,
610
- onMouseDown: handleMouseDown,
611
- onTouchStart: handleTouchStart,
783
+ onPointerDown: handlePointerDown,
784
+ onPointerMove: handlePointerMove,
785
+ onPointerUp: handlePointerUp,
612
786
  onMouseEnter: () => setIsHovered(true),
613
787
  onMouseLeave: () => setIsHovered(false),
788
+ style: { width: rubberBandWidth, x: rubberBandX },
614
789
  children: [
615
790
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
616
791
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
617
792
  import_react5.motion.div,
618
793
  {
619
794
  className: "dialkit-slider-fill",
620
- style: { background: fillBackground, width: `${percentage}%`, transition: "background 0.15s" },
621
- animate: { width: `${percentage}%` },
622
- transition
795
+ style: {
796
+ background: fillBackground,
797
+ width: fillWidth,
798
+ transition: "background 0.15s"
799
+ }
623
800
  }
624
801
  ),
625
802
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -627,15 +804,23 @@ function Slider({
627
804
  {
628
805
  className: "dialkit-slider-handle",
629
806
  style: {
630
- background: isInteracting ? "rgba(255, 255, 255, 1)" : "rgba(255, 255, 255, 0.5)",
631
807
  left: handleLeft,
632
- opacity: isActive ? 1 : 0
808
+ y: "-50%",
809
+ background: "rgba(255, 255, 255, 0.9)"
810
+ },
811
+ animate: {
812
+ opacity: handleOpacity,
813
+ scaleX: isActive ? 1 : 0.25,
814
+ scaleY: isActive && valueDodge ? 0.75 : 1
633
815
  },
634
- animate: { left: handleLeft, opacity: isActive ? 1 : 0 },
635
- transition
816
+ transition: {
817
+ scaleX: { type: "spring", visualDuration: 0.25, bounce: 0.15 },
818
+ scaleY: { type: "spring", visualDuration: 0.2, bounce: 0.1 },
819
+ opacity: { duration: 0.15 }
820
+ }
636
821
  }
637
822
  ),
638
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dialkit-slider-label", children: label }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { ref: labelRef, className: "dialkit-slider-label", children: label }),
639
824
  showInput ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
640
825
  "input",
641
826
  {
@@ -649,24 +834,22 @@ function Slider({
649
834
  onClick: (e) => e.stopPropagation(),
650
835
  onMouseDown: (e) => e.stopPropagation()
651
836
  }
652
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
837
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
653
838
  "span",
654
839
  {
840
+ ref: valueSpanRef,
655
841
  className: `dialkit-slider-value ${isValueEditable ? "dialkit-slider-value-editable" : ""}`,
656
842
  onMouseEnter: () => setIsValueHovered(true),
657
843
  onMouseLeave: () => setIsValueHovered(false),
658
844
  onClick: handleValueClick,
659
845
  onMouseDown: (e) => isValueEditable && e.stopPropagation(),
660
846
  style: { cursor: isValueEditable ? "text" : "default" },
661
- children: [
662
- displayValue,
663
- unit
664
- ]
847
+ children: displayValue
665
848
  }
666
849
  )
667
850
  ]
668
851
  }
669
- );
852
+ ) });
670
853
  }
671
854
 
672
855
  // src/components/SegmentedControl.tsx
@@ -951,20 +1134,352 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
951
1134
  ] }) });
952
1135
  }
953
1136
 
954
- // src/components/Panel.tsx
955
- var import_react9 = require("react");
1137
+ // src/components/TextControl.tsx
956
1138
  var import_jsx_runtime7 = require("react/jsx-runtime");
1139
+ function TextControl({ label, value, onChange, placeholder }) {
1140
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dialkit-text-control", children: [
1141
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "dialkit-text-label", children: label }),
1142
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1143
+ "input",
1144
+ {
1145
+ type: "text",
1146
+ className: "dialkit-text-input",
1147
+ value,
1148
+ onChange: (e) => onChange(e.target.value),
1149
+ placeholder
1150
+ }
1151
+ )
1152
+ ] });
1153
+ }
1154
+
1155
+ // src/components/SelectControl.tsx
1156
+ var import_react9 = require("react");
1157
+ var import_react10 = require("motion/react");
1158
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1159
+ function toTitleCase(s) {
1160
+ return s.replace(/\b\w/g, (c) => c.toUpperCase());
1161
+ }
1162
+ function normalizeOptions(options) {
1163
+ return options.map(
1164
+ (opt) => typeof opt === "string" ? { value: opt, label: toTitleCase(opt) } : opt
1165
+ );
1166
+ }
1167
+ function SelectControl({ label, value, options, onChange }) {
1168
+ const [isOpen, setIsOpen] = (0, import_react9.useState)(false);
1169
+ const containerRef = (0, import_react9.useRef)(null);
1170
+ const normalized = normalizeOptions(options);
1171
+ const selectedOption = normalized.find((o) => o.value === value);
1172
+ (0, import_react9.useEffect)(() => {
1173
+ if (!isOpen) return;
1174
+ const handleClick = (e) => {
1175
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
1176
+ setIsOpen(false);
1177
+ }
1178
+ };
1179
+ document.addEventListener("mousedown", handleClick);
1180
+ return () => document.removeEventListener("mousedown", handleClick);
1181
+ }, [isOpen]);
1182
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: containerRef, className: "dialkit-select-row", children: [
1183
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1184
+ "button",
1185
+ {
1186
+ className: "dialkit-select-trigger",
1187
+ onClick: () => setIsOpen(!isOpen),
1188
+ "data-open": String(isOpen),
1189
+ children: [
1190
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-select-label", children: label }),
1191
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dialkit-select-right", children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1194
+ import_react10.motion.svg,
1195
+ {
1196
+ className: "dialkit-select-chevron",
1197
+ viewBox: "0 0 24 24",
1198
+ fill: "none",
1199
+ stroke: "currentColor",
1200
+ strokeWidth: "2.5",
1201
+ strokeLinecap: "round",
1202
+ strokeLinejoin: "round",
1203
+ animate: { rotate: isOpen ? 180 : 0 },
1204
+ transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1205
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
1206
+ }
1207
+ )
1208
+ ] })
1209
+ ]
1210
+ }
1211
+ ),
1212
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react10.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1213
+ import_react10.motion.div,
1214
+ {
1215
+ className: "dialkit-select-dropdown",
1216
+ initial: { opacity: 0, y: -8, scale: 0.95 },
1217
+ animate: { opacity: 1, y: 0, scale: 1 },
1218
+ exit: { opacity: 0, y: -8, scale: 0.95 },
1219
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
1220
+ children: normalized.map((option) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1221
+ "button",
1222
+ {
1223
+ className: "dialkit-select-option",
1224
+ "data-selected": String(option.value === value),
1225
+ onClick: () => {
1226
+ onChange(option.value);
1227
+ setIsOpen(false);
1228
+ },
1229
+ children: option.label
1230
+ },
1231
+ option.value
1232
+ ))
1233
+ }
1234
+ ) })
1235
+ ] });
1236
+ }
1237
+
1238
+ // src/components/ColorControl.tsx
1239
+ var import_react11 = require("react");
1240
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1241
+ var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
1242
+ function ColorControl({ label, value, onChange }) {
1243
+ const [isEditing, setIsEditing] = (0, import_react11.useState)(false);
1244
+ const [editValue, setEditValue] = (0, import_react11.useState)(value);
1245
+ const colorInputRef = (0, import_react11.useRef)(null);
1246
+ (0, import_react11.useEffect)(() => {
1247
+ if (!isEditing) {
1248
+ setEditValue(value);
1249
+ }
1250
+ }, [value, isEditing]);
1251
+ function handleTextSubmit() {
1252
+ setIsEditing(false);
1253
+ if (HEX_COLOR_REGEX.test(editValue)) {
1254
+ onChange(editValue);
1255
+ } else {
1256
+ setEditValue(value);
1257
+ }
1258
+ }
1259
+ function handleKeyDown(e) {
1260
+ if (e.key === "Enter") {
1261
+ handleTextSubmit();
1262
+ } else if (e.key === "Escape") {
1263
+ setIsEditing(false);
1264
+ setEditValue(value);
1265
+ }
1266
+ }
1267
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-color-control", children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dialkit-color-label", children: label }),
1269
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-color-inputs", children: [
1270
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1271
+ "input",
1272
+ {
1273
+ type: "text",
1274
+ className: "dialkit-color-hex-input",
1275
+ value: editValue,
1276
+ onChange: (e) => setEditValue(e.target.value),
1277
+ onBlur: handleTextSubmit,
1278
+ onKeyDown: handleKeyDown,
1279
+ autoFocus: true
1280
+ }
1281
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1282
+ "span",
1283
+ {
1284
+ className: "dialkit-color-hex",
1285
+ onClick: () => setIsEditing(true),
1286
+ children: (value ?? "").toUpperCase()
1287
+ }
1288
+ ),
1289
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1290
+ "button",
1291
+ {
1292
+ className: "dialkit-color-swatch",
1293
+ style: { backgroundColor: value },
1294
+ onClick: () => colorInputRef.current?.click(),
1295
+ title: "Pick color"
1296
+ }
1297
+ ),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1299
+ "input",
1300
+ {
1301
+ ref: colorInputRef,
1302
+ type: "color",
1303
+ className: "dialkit-color-picker-native",
1304
+ value: value.length === 4 ? expandShorthandHex(value) : value.slice(0, 7),
1305
+ onChange: (e) => onChange(e.target.value)
1306
+ }
1307
+ )
1308
+ ] })
1309
+ ] });
1310
+ }
1311
+ function expandShorthandHex(hex) {
1312
+ if (hex.length !== 4) return hex;
1313
+ return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
1314
+ }
1315
+
1316
+ // src/components/PresetManager.tsx
1317
+ var import_react12 = require("react");
1318
+ var import_react_dom = require("react-dom");
1319
+ var import_react13 = require("motion/react");
1320
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1321
+ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1322
+ const [isOpen, setIsOpen] = (0, import_react12.useState)(false);
1323
+ const triggerRef = (0, import_react12.useRef)(null);
1324
+ const dropdownRef = (0, import_react12.useRef)(null);
1325
+ const [pos, setPos] = (0, import_react12.useState)({ top: 0, left: 0, width: 0 });
1326
+ const hasPresets = presets.length > 0;
1327
+ const activePreset = presets.find((p) => p.id === activePresetId);
1328
+ const open = (0, import_react12.useCallback)(() => {
1329
+ if (!hasPresets) return;
1330
+ const rect = triggerRef.current?.getBoundingClientRect();
1331
+ if (rect) {
1332
+ setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
1333
+ }
1334
+ setIsOpen(true);
1335
+ }, [hasPresets]);
1336
+ const close = (0, import_react12.useCallback)(() => setIsOpen(false), []);
1337
+ const toggle = (0, import_react12.useCallback)(() => {
1338
+ if (isOpen) close();
1339
+ else open();
1340
+ }, [isOpen, open, close]);
1341
+ (0, import_react12.useEffect)(() => {
1342
+ if (!isOpen) return;
1343
+ const handler = (e) => {
1344
+ const target = e.target;
1345
+ if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
1346
+ close();
1347
+ };
1348
+ document.addEventListener("mousedown", handler);
1349
+ return () => document.removeEventListener("mousedown", handler);
1350
+ }, [isOpen, close]);
1351
+ const handleSelect = (presetId) => {
1352
+ if (presetId) {
1353
+ DialStore.loadPreset(panelId, presetId);
1354
+ } else {
1355
+ DialStore.clearActivePreset(panelId);
1356
+ }
1357
+ close();
1358
+ };
1359
+ const handleDelete = (e, presetId) => {
1360
+ e.stopPropagation();
1361
+ DialStore.deletePreset(panelId, presetId);
1362
+ };
1363
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-preset-manager", children: [
1364
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1365
+ "button",
1366
+ {
1367
+ ref: triggerRef,
1368
+ className: "dialkit-preset-trigger",
1369
+ onClick: toggle,
1370
+ "data-open": String(isOpen),
1371
+ "data-has-preset": String(!!activePreset),
1372
+ "data-disabled": String(!hasPresets),
1373
+ children: [
1374
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
1375
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1376
+ import_react13.motion.svg,
1377
+ {
1378
+ className: "dialkit-select-chevron",
1379
+ viewBox: "0 0 24 24",
1380
+ fill: "none",
1381
+ stroke: "currentColor",
1382
+ strokeWidth: "2.5",
1383
+ strokeLinecap: "round",
1384
+ strokeLinejoin: "round",
1385
+ animate: { rotate: isOpen ? 180 : 0, opacity: hasPresets ? 0.6 : 0.25 },
1386
+ transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1387
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
1388
+ }
1389
+ )
1390
+ ]
1391
+ }
1392
+ ),
1393
+ (0, import_react_dom.createPortal)(
1394
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react13.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1395
+ import_react13.motion.div,
1396
+ {
1397
+ ref: dropdownRef,
1398
+ className: "dialkit-preset-dropdown",
1399
+ style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.width },
1400
+ initial: { opacity: 0, y: 4, scale: 0.97 },
1401
+ animate: { opacity: 1, y: 0, scale: 1 },
1402
+ exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
1403
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
1404
+ children: [
1405
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1406
+ "div",
1407
+ {
1408
+ className: "dialkit-preset-item",
1409
+ "data-active": String(!activePresetId),
1410
+ onClick: () => handleSelect(null),
1411
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-preset-name", children: "Version 1" })
1412
+ }
1413
+ ),
1414
+ presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1415
+ "div",
1416
+ {
1417
+ className: "dialkit-preset-item",
1418
+ "data-active": String(preset.id === activePresetId),
1419
+ onClick: () => handleSelect(preset.id),
1420
+ children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-preset-name", children: preset.name }),
1422
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1423
+ "button",
1424
+ {
1425
+ className: "dialkit-preset-delete",
1426
+ onClick: (e) => handleDelete(e, preset.id),
1427
+ title: "Delete preset",
1428
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1429
+ /* @__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" }),
1430
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M10 11V16" }),
1431
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M14 11V16" }),
1432
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M3.5 6H20.5" }),
1433
+ /* @__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" })
1434
+ ] })
1435
+ }
1436
+ )
1437
+ ]
1438
+ },
1439
+ preset.id
1440
+ ))
1441
+ ]
1442
+ }
1443
+ ) }),
1444
+ document.body
1445
+ )
1446
+ ] });
1447
+ }
1448
+
1449
+ // src/components/Panel.tsx
1450
+ var import_jsx_runtime11 = require("react/jsx-runtime");
957
1451
  function Panel({ panel }) {
958
- const values = (0, import_react9.useSyncExternalStore)(
1452
+ const [copied, setCopied] = (0, import_react14.useState)(false);
1453
+ const [isPanelOpen, setIsPanelOpen] = (0, import_react14.useState)(true);
1454
+ const values = (0, import_react14.useSyncExternalStore)(
959
1455
  (cb) => DialStore.subscribe(panel.id, cb),
960
1456
  () => DialStore.getValues(panel.id),
961
1457
  () => DialStore.getValues(panel.id)
962
1458
  );
1459
+ const presets = DialStore.getPresets(panel.id);
1460
+ const activePresetId = DialStore.getActivePresetId(panel.id);
1461
+ const handleAddPreset = () => {
1462
+ const nextNum = presets.length + 2;
1463
+ DialStore.savePreset(panel.id, `Version ${nextNum}`);
1464
+ };
1465
+ const handleCopy = () => {
1466
+ const jsonStr = JSON.stringify(values, null, 2);
1467
+ const instruction = `Update the useDialKit configuration for "${panel.name}" with these values:
1468
+
1469
+ \`\`\`json
1470
+ ${jsonStr}
1471
+ \`\`\`
1472
+
1473
+ Apply these values as the new defaults in the useDialKit call.`;
1474
+ navigator.clipboard.writeText(instruction);
1475
+ setCopied(true);
1476
+ setTimeout(() => setCopied(false), 1500);
1477
+ };
963
1478
  const renderControl = (control) => {
964
1479
  const value = values[control.path];
965
1480
  switch (control.type) {
966
1481
  case "slider":
967
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1482
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
968
1483
  Slider,
969
1484
  {
970
1485
  label: control.label,
@@ -977,7 +1492,7 @@ function Panel({ panel }) {
977
1492
  control.path
978
1493
  );
979
1494
  case "toggle":
980
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1495
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
981
1496
  Toggle,
982
1497
  {
983
1498
  label: control.label,
@@ -987,7 +1502,7 @@ function Panel({ panel }) {
987
1502
  control.path
988
1503
  );
989
1504
  case "spring":
990
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1505
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
991
1506
  SpringControl,
992
1507
  {
993
1508
  panelId: panel.id,
@@ -999,7 +1514,39 @@ function Panel({ panel }) {
999
1514
  control.path
1000
1515
  );
1001
1516
  case "folder":
1002
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Folder, { title: control.label, children: control.children?.map(renderControl) }, control.path);
1517
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Folder, { title: control.label, children: control.children?.map(renderControl) }, control.path);
1518
+ case "text":
1519
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1520
+ TextControl,
1521
+ {
1522
+ label: control.label,
1523
+ value,
1524
+ onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
1525
+ placeholder: control.placeholder
1526
+ },
1527
+ control.path
1528
+ );
1529
+ case "select":
1530
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1531
+ SelectControl,
1532
+ {
1533
+ label: control.label,
1534
+ value,
1535
+ options: control.options ?? [],
1536
+ onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
1537
+ },
1538
+ control.path
1539
+ );
1540
+ case "color":
1541
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1542
+ ColorControl,
1543
+ {
1544
+ label: control.label,
1545
+ value,
1546
+ onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
1547
+ },
1548
+ control.path
1549
+ );
1003
1550
  default:
1004
1551
  return null;
1005
1552
  }
@@ -1010,24 +1557,16 @@ function Panel({ panel }) {
1010
1557
  while (i < panel.controls.length) {
1011
1558
  const control = panel.controls[i];
1012
1559
  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
1560
  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}`)
1561
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1562
+ "button",
1563
+ {
1564
+ className: "dialkit-button",
1565
+ onClick: () => DialStore.triggerAction(panel.id, control.path),
1566
+ children: control.label
1567
+ },
1568
+ control.path
1569
+ )
1031
1570
  );
1032
1571
  } else {
1033
1572
  result.push(renderControl(control));
@@ -1036,15 +1575,92 @@ function Panel({ panel }) {
1036
1575
  }
1037
1576
  return result;
1038
1577
  };
1039
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Folder, { title: panel.name, defaultOpen: true, isRoot: true, panelId: panel.id, children: renderControls() });
1578
+ const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
1579
+ const toolbar = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1581
+ import_react15.motion.button,
1582
+ {
1583
+ className: "dialkit-toolbar-add",
1584
+ onClick: handleAddPreset,
1585
+ title: "Add preset",
1586
+ whileTap: { scale: 0.9 },
1587
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
1588
+ 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: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 6H20" }),
1590
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 12H10" }),
1591
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M15 15L21 15" }),
1592
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M18 12V18" }),
1593
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 18H10" })
1594
+ ] })
1595
+ }
1596
+ ),
1597
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1598
+ PresetManager,
1599
+ {
1600
+ panelId: panel.id,
1601
+ presets,
1602
+ activePresetId,
1603
+ onAdd: handleAddPreset
1604
+ }
1605
+ ),
1606
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1607
+ import_react15.motion.button,
1608
+ {
1609
+ className: "dialkit-toolbar-copy",
1610
+ onClick: handleCopy,
1611
+ title: "Copy parameters",
1612
+ whileTap: { scale: 0.95 },
1613
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
1614
+ children: [
1615
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dialkit-toolbar-copy-icon-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react15.AnimatePresence, { initial: false, mode: "popLayout", children: copied ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1616
+ import_react15.motion.svg,
1617
+ {
1618
+ className: "dialkit-toolbar-copy-icon",
1619
+ viewBox: "0 0 24 24",
1620
+ fill: "none",
1621
+ stroke: "currentColor",
1622
+ strokeWidth: "2",
1623
+ strokeLinecap: "round",
1624
+ strokeLinejoin: "round",
1625
+ initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1626
+ animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
1627
+ exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1628
+ transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1629
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M5 12.75L10 19L19 5" })
1630
+ },
1631
+ "check"
1632
+ ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1633
+ import_react15.motion.svg,
1634
+ {
1635
+ className: "dialkit-toolbar-copy-icon",
1636
+ viewBox: "0 0 24 24",
1637
+ fill: "none",
1638
+ initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1639
+ animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
1640
+ exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1641
+ transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1642
+ children: [
1643
+ /* @__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" }),
1644
+ /* @__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" }),
1645
+ /* @__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" })
1646
+ ]
1647
+ },
1648
+ "clipboard"
1649
+ ) }) }),
1650
+ "Copy"
1651
+ ]
1652
+ }
1653
+ )
1654
+ ] });
1655
+ 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
1656
  }
1041
1657
 
1042
1658
  // src/components/DialRoot.tsx
1043
- var import_jsx_runtime8 = require("react/jsx-runtime");
1659
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1044
1660
  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)(() => {
1661
+ const [panels, setPanels] = (0, import_react16.useState)([]);
1662
+ const [mounted, setMounted] = (0, import_react16.useState)(false);
1663
+ (0, import_react16.useEffect)(() => {
1048
1664
  setMounted(true);
1049
1665
  setPanels(DialStore.getPanels());
1050
1666
  const unsubscribe = DialStore.subscribeGlobal(() => {
@@ -1058,14 +1674,14 @@ function DialRoot({ position = "top-right" }) {
1058
1674
  if (panels.length === 0) {
1059
1675
  return null;
1060
1676
  }
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);
1677
+ 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)) }) });
1678
+ return (0, import_react_dom2.createPortal)(content, document.body);
1063
1679
  }
1064
1680
 
1065
1681
  // src/components/ButtonGroup.tsx
1066
- var import_jsx_runtime9 = require("react/jsx-runtime");
1682
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1067
1683
  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)(
1684
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1069
1685
  "button",
1070
1686
  {
1071
1687
  className: "dialkit-button",
@@ -1078,13 +1694,16 @@ function ButtonGroup({ buttons }) {
1078
1694
  // Annotate the CommonJS export names for ESM import in node:
1079
1695
  0 && (module.exports = {
1080
1696
  ButtonGroup,
1697
+ ColorControl,
1081
1698
  DialRoot,
1082
1699
  DialStore,
1083
1700
  Folder,
1084
- SegmentedControl,
1701
+ PresetManager,
1702
+ SelectControl,
1085
1703
  Slider,
1086
1704
  SpringControl,
1087
1705
  SpringVisualization,
1706
+ TextControl,
1088
1707
  Toggle,
1089
1708
  useDialKit
1090
1709
  });