dialkit 1.2.1 → 1.3.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.
Files changed (55) hide show
  1. package/README.md +220 -3
  2. package/dist/dropdown-position.d.ts +15 -0
  3. package/dist/dropdown-position.js +22 -0
  4. package/dist/dropdown-position.js.map +1 -0
  5. package/dist/index.cjs +697 -307
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +42 -5
  8. package/dist/index.d.ts +42 -5
  9. package/dist/index.js +720 -331
  10. package/dist/index.js.map +1 -1
  11. package/dist/panel-drag.d.ts +19 -0
  12. package/dist/panel-drag.js +72 -0
  13. package/dist/panel-drag.js.map +1 -0
  14. package/dist/solid/index.cjs +661 -204
  15. package/dist/solid/index.cjs.map +1 -1
  16. package/dist/solid/index.d.cts +40 -3
  17. package/dist/solid/index.d.ts +40 -3
  18. package/dist/solid/index.js +646 -190
  19. package/dist/solid/index.js.map +1 -1
  20. package/dist/store/index.cjs +272 -41
  21. package/dist/store/index.cjs.map +1 -1
  22. package/dist/store/index.d.cts +30 -3
  23. package/dist/store/index.d.ts +30 -3
  24. package/dist/store/index.js +269 -40
  25. package/dist/store/index.js.map +1 -1
  26. package/dist/styles.css +105 -7
  27. package/dist/svelte/components/DialRoot.svelte +176 -11
  28. package/dist/svelte/components/DialRoot.svelte.d.ts +1 -0
  29. package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -1
  30. package/dist/svelte/components/Folder.svelte +24 -13
  31. package/dist/svelte/components/Folder.svelte.d.ts +1 -0
  32. package/dist/svelte/components/Folder.svelte.d.ts.map +1 -1
  33. package/dist/svelte/components/Panel.svelte +98 -71
  34. package/dist/svelte/components/Panel.svelte.d.ts +2 -0
  35. package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
  36. package/dist/svelte/components/PresetManager.svelte +5 -5
  37. package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -1
  38. package/dist/svelte/components/SelectControl.svelte +6 -14
  39. package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -1
  40. package/dist/svelte/createDialKit.svelte.d.ts +11 -1
  41. package/dist/svelte/createDialKit.svelte.d.ts.map +1 -1
  42. package/dist/svelte/createDialKit.svelte.js +61 -34
  43. package/dist/svelte/index.d.ts +3 -3
  44. package/dist/svelte/index.d.ts.map +1 -1
  45. package/dist/svelte/index.js +1 -1
  46. package/dist/svelte/theme-css.d.ts +1 -1
  47. package/dist/svelte/theme-css.d.ts.map +1 -1
  48. package/dist/svelte/theme-css.js +105 -7
  49. package/dist/vue/index.cjs +573 -132
  50. package/dist/vue/index.cjs.map +1 -1
  51. package/dist/vue/index.d.cts +53 -6
  52. package/dist/vue/index.d.ts +53 -6
  53. package/dist/vue/index.js +573 -133
  54. package/dist/vue/index.js.map +1 -1
  55. package/package.json +2 -2
@@ -3,6 +3,89 @@ import { createSignal, createMemo, onMount, onCleanup, createUniqueId } from "so
3
3
 
4
4
  // src/store/DialStore.ts
5
5
  var EMPTY_VALUES = Object.freeze({});
6
+ function resolveDialValues(config, flatValues) {
7
+ return resolveConfigValues(config, flatValues, "");
8
+ }
9
+ function flattenDialValueUpdates(config, updates) {
10
+ const values = {};
11
+ if (typeof updates === "object" && updates !== null) {
12
+ flattenConfigUpdates(config, updates, "", values);
13
+ }
14
+ return values;
15
+ }
16
+ function resolveConfigValues(config, flatValues, prefix) {
17
+ const result = {};
18
+ for (const [key, configValue] of Object.entries(config)) {
19
+ if (key === "_collapsed") continue;
20
+ const path = prefix ? `${prefix}.${key}` : key;
21
+ if (Array.isArray(configValue) && configValue.length <= 4 && typeof configValue[0] === "number") {
22
+ result[key] = flatValues[path] ?? configValue[0];
23
+ } else if (typeof configValue === "number" || typeof configValue === "boolean" || typeof configValue === "string") {
24
+ result[key] = flatValues[path] ?? configValue;
25
+ } else if (isSpringConfigValue(configValue) || isEasingConfigValue(configValue)) {
26
+ result[key] = flatValues[path] ?? configValue;
27
+ } else if (isActionConfigValue(configValue)) {
28
+ result[key] = flatValues[path] ?? configValue;
29
+ } else if (isSelectConfigValue(configValue)) {
30
+ const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
31
+ result[key] = flatValues[path] ?? defaultValue;
32
+ } else if (isColorConfigValue(configValue)) {
33
+ result[key] = flatValues[path] ?? configValue.default ?? "#000000";
34
+ } else if (isTextConfigValue(configValue)) {
35
+ result[key] = flatValues[path] ?? configValue.default ?? "";
36
+ } else if (typeof configValue === "object" && configValue !== null) {
37
+ result[key] = resolveConfigValues(configValue, flatValues, path);
38
+ }
39
+ }
40
+ return result;
41
+ }
42
+ function flattenConfigUpdates(config, updates, prefix, values) {
43
+ for (const [key, configValue] of Object.entries(config)) {
44
+ if (key === "_collapsed" || !(key in updates)) continue;
45
+ const nextValue = updates[key];
46
+ if (nextValue === void 0) continue;
47
+ const path = prefix ? `${prefix}.${key}` : key;
48
+ if (isActionConfigValue(configValue)) {
49
+ continue;
50
+ }
51
+ if (isLeafConfigValue(configValue)) {
52
+ values[path] = nextValue;
53
+ continue;
54
+ }
55
+ if (typeof configValue === "object" && configValue !== null && typeof nextValue === "object" && nextValue !== null && !Array.isArray(nextValue)) {
56
+ flattenConfigUpdates(configValue, nextValue, path, values);
57
+ }
58
+ }
59
+ }
60
+ function isLeafConfigValue(value) {
61
+ return Array.isArray(value) && value.length <= 4 && typeof value[0] === "number" || typeof value === "number" || typeof value === "boolean" || typeof value === "string" || isSpringConfigValue(value) || isEasingConfigValue(value) || isActionConfigValue(value) || isSelectConfigValue(value) || isColorConfigValue(value) || isTextConfigValue(value);
62
+ }
63
+ function hasType(value, type) {
64
+ return typeof value === "object" && value !== null && "type" in value && value.type === type;
65
+ }
66
+ function isSpringConfigValue(value) {
67
+ return hasType(value, "spring");
68
+ }
69
+ function isEasingConfigValue(value) {
70
+ return hasType(value, "easing");
71
+ }
72
+ function isActionConfigValue(value) {
73
+ return hasType(value, "action");
74
+ }
75
+ function isSelectConfigValue(value) {
76
+ return hasType(value, "select") && "options" in value && Array.isArray(value.options);
77
+ }
78
+ function isColorConfigValue(value) {
79
+ return hasType(value, "color");
80
+ }
81
+ function isTextConfigValue(value) {
82
+ return hasType(value, "text");
83
+ }
84
+ function getFirstOptionValue(options) {
85
+ const first = options[0];
86
+ if (first === void 0) return "";
87
+ return typeof first === "string" ? first : first.value;
88
+ }
6
89
  var DialStoreClass = class {
7
90
  constructor() {
8
91
  this.panels = /* @__PURE__ */ new Map();
@@ -13,87 +96,138 @@ var DialStoreClass = class {
13
96
  this.presets = /* @__PURE__ */ new Map();
14
97
  this.activePreset = /* @__PURE__ */ new Map();
15
98
  this.baseValues = /* @__PURE__ */ new Map();
99
+ this.defaultValues = /* @__PURE__ */ new Map();
100
+ this.registrationCounts = /* @__PURE__ */ new Map();
101
+ this.retainedPanels = /* @__PURE__ */ new Set();
102
+ this.persistConfigs = /* @__PURE__ */ new Map();
16
103
  }
17
- registerPanel(id, name, config, shortcuts) {
104
+ registerPanel(id, name, config, shortcuts, options = {}) {
105
+ this.configurePanelRetention(id, options);
106
+ this.registrationCounts.set(id, (this.registrationCounts.get(id) ?? 0) + 1);
18
107
  const controls = this.parseConfig(config, "", shortcuts);
19
- const values = this.flattenValues(config, "");
20
- this.initTransitionModes(config, "", values);
108
+ const controlsByPath = this.mapControlsByPath(controls);
109
+ const defaultValues = this.flattenValues(config, "");
110
+ this.initTransitionModes(config, "", defaultValues);
111
+ const persisted = this.loadPersistedPanel(id);
112
+ const previousValues = this.panels.get(id)?.values ?? this.snapshots.get(id) ?? persisted?.values ?? {};
113
+ const values = this.reconcileValues(defaultValues, previousValues, controlsByPath);
114
+ const previousBaseValues = this.baseValues.get(id) ?? persisted?.baseValues ?? persisted?.values ?? {};
115
+ const baseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
21
116
  this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {} });
22
117
  this.snapshots.set(id, { ...values });
23
- this.baseValues.set(id, { ...values });
118
+ this.baseValues.set(id, baseValues);
119
+ this.defaultValues.set(id, { ...defaultValues });
120
+ const existingPresets = this.presets.get(id) ?? persisted?.presets;
121
+ if (existingPresets) {
122
+ this.presets.set(id, this.reconcilePresets(existingPresets, defaultValues, controlsByPath));
123
+ }
124
+ if (!this.activePreset.has(id) && persisted?.activePresetId !== void 0) {
125
+ this.activePreset.set(id, persisted.activePresetId);
126
+ }
127
+ this.persistPanel(id);
128
+ this.notify(id);
24
129
  this.notifyGlobal();
25
130
  }
26
- updatePanel(id, name, config, shortcuts) {
131
+ updatePanel(id, name, config, shortcuts, options = {}) {
132
+ this.configurePanelRetention(id, options);
27
133
  const existing = this.panels.get(id);
28
134
  if (!existing) {
29
- this.registerPanel(id, name, config, shortcuts);
135
+ this.registerPanel(id, name, config, shortcuts, options);
30
136
  return;
31
137
  }
32
138
  const controls = this.parseConfig(config, "", shortcuts);
33
139
  const controlsByPath = this.mapControlsByPath(controls);
34
140
  const defaultValues = this.flattenValues(config, "");
35
- const nextValues = {};
36
- for (const [path, defaultValue] of Object.entries(defaultValues)) {
37
- nextValues[path] = this.normalizePreservedValue(
38
- existing.values[path],
39
- defaultValue,
40
- controlsByPath.get(path)
41
- );
42
- }
43
- this.initTransitionModes(config, "", nextValues);
44
- for (const [path, mode] of Object.entries(existing.values)) {
45
- if (!path.endsWith(".__mode")) {
46
- continue;
47
- }
48
- const transitionPath = path.slice(0, -"__mode".length - 1);
49
- const transitionControl = controlsByPath.get(transitionPath);
50
- if (transitionControl?.type === "transition") {
51
- nextValues[path] = mode;
52
- }
53
- }
141
+ this.initTransitionModes(config, "", defaultValues);
142
+ const nextValues = this.reconcileValues(defaultValues, existing.values, controlsByPath);
54
143
  const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts };
55
144
  this.panels.set(id, nextPanel);
56
145
  this.snapshots.set(id, { ...nextValues });
57
146
  const previousBaseValues = this.baseValues.get(id) ?? {};
58
- const nextBaseValues = {};
59
- for (const [path, defaultValue] of Object.entries(defaultValues)) {
60
- nextBaseValues[path] = this.normalizePreservedValue(
61
- previousBaseValues[path],
62
- defaultValue,
63
- controlsByPath.get(path)
64
- );
65
- }
147
+ const nextBaseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
66
148
  for (const [path, value] of Object.entries(nextValues)) {
67
149
  if (path.endsWith(".__mode")) {
68
150
  nextBaseValues[path] = value;
69
151
  }
70
152
  }
71
153
  this.baseValues.set(id, nextBaseValues);
154
+ this.defaultValues.set(id, { ...defaultValues });
155
+ this.presets.set(id, this.reconcilePresets(this.presets.get(id) ?? [], defaultValues, controlsByPath));
156
+ this.persistPanel(id);
72
157
  this.notify(id);
73
158
  this.notifyGlobal();
74
159
  }
75
160
  unregisterPanel(id) {
161
+ const nextCount = (this.registrationCounts.get(id) ?? 1) - 1;
162
+ if (nextCount > 0) {
163
+ this.registrationCounts.set(id, nextCount);
164
+ return;
165
+ }
166
+ this.registrationCounts.delete(id);
76
167
  this.panels.delete(id);
77
168
  this.listeners.delete(id);
78
- this.snapshots.delete(id);
79
169
  this.actionListeners.delete(id);
80
- this.baseValues.delete(id);
170
+ if (!this.retainedPanels.has(id)) {
171
+ this.snapshots.delete(id);
172
+ this.baseValues.delete(id);
173
+ this.defaultValues.delete(id);
174
+ this.presets.delete(id);
175
+ this.activePreset.delete(id);
176
+ this.persistConfigs.delete(id);
177
+ }
81
178
  this.notifyGlobal();
82
179
  }
83
180
  updateValue(panelId, path, value) {
181
+ this.updateValues(panelId, { [path]: value });
182
+ }
183
+ updateValues(panelId, updates) {
84
184
  const panel = this.panels.get(panelId);
85
185
  if (!panel) return;
86
- panel.values[path] = value;
186
+ const validUpdates = {};
187
+ for (const [path, value] of Object.entries(updates)) {
188
+ if (!Object.prototype.hasOwnProperty.call(panel.values, path)) {
189
+ continue;
190
+ }
191
+ const control = this.findControlByPath(panel.controls, path);
192
+ if (control?.type === "action") {
193
+ continue;
194
+ }
195
+ panel.values[path] = value;
196
+ validUpdates[path] = value;
197
+ }
198
+ if (Object.keys(validUpdates).length === 0) {
199
+ return;
200
+ }
87
201
  const activeId = this.activePreset.get(panelId);
88
202
  if (activeId) {
89
203
  const presets = this.presets.get(panelId) ?? [];
90
204
  const preset = presets.find((p) => p.id === activeId);
91
- if (preset) preset.values[path] = value;
205
+ if (preset) {
206
+ for (const [path, value] of Object.entries(validUpdates)) {
207
+ preset.values[path] = value;
208
+ }
209
+ }
92
210
  } else {
93
211
  const base = this.baseValues.get(panelId);
94
- if (base) base[path] = value;
212
+ if (base) {
213
+ for (const [path, value] of Object.entries(validUpdates)) {
214
+ base[path] = value;
215
+ }
216
+ }
95
217
  }
96
218
  this.snapshots.set(panelId, { ...panel.values });
219
+ this.persistPanel(panelId);
220
+ this.notify(panelId);
221
+ }
222
+ resetValues(panelId) {
223
+ const panel = this.panels.get(panelId);
224
+ const defaults = this.defaultValues.get(panelId);
225
+ if (!panel || !defaults) return;
226
+ panel.values = { ...defaults };
227
+ this.snapshots.set(panelId, { ...panel.values });
228
+ this.baseValues.set(panelId, { ...defaults });
229
+ this.activePreset.set(panelId, null);
230
+ this.persistPanel(panelId);
97
231
  this.notify(panelId);
98
232
  }
99
233
  updateSpringMode(panelId, path, mode) {
@@ -109,6 +243,7 @@ var DialStoreClass = class {
109
243
  if (!panel) return;
110
244
  panel.values[`${path}.__mode`] = mode;
111
245
  this.snapshots.set(panelId, { ...panel.values });
246
+ this.persistPanel(panelId);
112
247
  this.notify(panelId);
113
248
  }
114
249
  getTransitionMode(panelId, path) {
@@ -167,6 +302,7 @@ var DialStoreClass = class {
167
302
  this.presets.set(panelId, [...existing, preset]);
168
303
  this.activePreset.set(panelId, id);
169
304
  this.snapshots.set(panelId, { ...panel.values });
305
+ this.persistPanel(panelId);
170
306
  this.notify(panelId);
171
307
  return id;
172
308
  }
@@ -179,6 +315,7 @@ var DialStoreClass = class {
179
315
  panel.values = { ...preset.values };
180
316
  this.snapshots.set(panelId, { ...panel.values });
181
317
  this.activePreset.set(panelId, presetId);
318
+ this.persistPanel(panelId);
182
319
  this.notify(panelId);
183
320
  }
184
321
  deletePreset(panelId, presetId) {
@@ -191,6 +328,7 @@ var DialStoreClass = class {
191
328
  if (panel) {
192
329
  this.snapshots.set(panelId, { ...panel.values });
193
330
  }
331
+ this.persistPanel(panelId);
194
332
  this.notify(panelId);
195
333
  }
196
334
  getPresets(panelId) {
@@ -207,6 +345,7 @@ var DialStoreClass = class {
207
345
  this.snapshots.set(panelId, { ...panel.values });
208
346
  }
209
347
  this.activePreset.set(panelId, null);
348
+ this.persistPanel(panelId);
210
349
  this.notify(panelId);
211
350
  }
212
351
  resolveShortcutTarget(key, modifier) {
@@ -237,6 +376,94 @@ var DialStoreClass = class {
237
376
  }
238
377
  return results;
239
378
  }
379
+ configurePanelRetention(id, options) {
380
+ if (options.retainOnUnmount) {
381
+ this.retainedPanels.add(id);
382
+ }
383
+ const persistConfig = this.normalizePersistConfig(id, options.persist);
384
+ if (persistConfig) {
385
+ this.persistConfigs.set(id, persistConfig);
386
+ this.retainedPanels.add(id);
387
+ }
388
+ }
389
+ reconcileValues(defaultValues, previousValues, controlsByPath) {
390
+ const nextValues = {};
391
+ for (const [path, defaultValue] of Object.entries(defaultValues)) {
392
+ if (path.endsWith(".__mode")) {
393
+ const transitionPath = path.slice(0, -".__mode".length);
394
+ const transitionControl = controlsByPath.get(transitionPath);
395
+ nextValues[path] = transitionControl?.type === "transition" && previousValues[path] !== void 0 ? previousValues[path] : defaultValue;
396
+ continue;
397
+ }
398
+ nextValues[path] = this.normalizePreservedValue(
399
+ previousValues[path],
400
+ defaultValue,
401
+ controlsByPath.get(path)
402
+ );
403
+ }
404
+ return nextValues;
405
+ }
406
+ reconcilePresets(presets, defaultValues, controlsByPath) {
407
+ return presets.map((preset) => ({
408
+ ...preset,
409
+ values: this.reconcileValues(defaultValues, preset.values, controlsByPath)
410
+ }));
411
+ }
412
+ normalizePersistConfig(id, persist) {
413
+ if (!persist) return null;
414
+ const options = typeof persist === "object" ? persist : {};
415
+ return {
416
+ key: options.key ?? `dialkit:${id}`,
417
+ storage: options.storage ?? "localStorage",
418
+ presets: options.presets ?? true
419
+ };
420
+ }
421
+ loadPersistedPanel(id) {
422
+ const config = this.persistConfigs.get(id);
423
+ if (!config) return null;
424
+ const storage = this.getStorage(config.storage);
425
+ if (!storage) return null;
426
+ try {
427
+ const raw = storage.getItem(config.key);
428
+ if (!raw) return null;
429
+ const parsed = JSON.parse(raw);
430
+ if (parsed?.version !== 1 || typeof parsed !== "object") return null;
431
+ return parsed;
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ persistPanel(id) {
437
+ const config = this.persistConfigs.get(id);
438
+ if (!config) return;
439
+ const storage = this.getStorage(config.storage);
440
+ if (!storage) return;
441
+ const values = this.snapshots.get(id) ?? this.panels.get(id)?.values;
442
+ if (!values) return;
443
+ const state = {
444
+ version: 1,
445
+ values,
446
+ baseValues: this.baseValues.get(id) ?? values,
447
+ activePresetId: this.activePreset.get(id) ?? null
448
+ };
449
+ if (config.presets) {
450
+ state.presets = this.presets.get(id) ?? [];
451
+ }
452
+ try {
453
+ storage.setItem(config.key, JSON.stringify(state));
454
+ } catch {
455
+ }
456
+ }
457
+ getStorage(kind) {
458
+ if (typeof globalThis === "undefined" || !("window" in globalThis)) {
459
+ return null;
460
+ }
461
+ try {
462
+ return kind === "sessionStorage" ? globalThis.window?.sessionStorage ?? null : globalThis.window?.localStorage ?? null;
463
+ } catch {
464
+ return null;
465
+ }
466
+ }
240
467
  findControlByPath(controls, path) {
241
468
  for (const control of controls) {
242
469
  if (control.path === path) return control;
@@ -465,16 +692,23 @@ var DialStore = new DialStoreClass();
465
692
 
466
693
  // src/solid/createDialKit.ts
467
694
  function createDialKit(name, config, options) {
695
+ return createDialKitController(name, config, options).values;
696
+ }
697
+ function createDialKitController(name, config, options) {
468
698
  const id = createUniqueId();
469
- const panelId = `${name}-${id}`;
470
- const [values, setValues] = createSignal(
699
+ const hasStableId = options?.id !== void 0;
700
+ const panelId = options?.id ?? `${name}-${id}`;
701
+ const [flatValues, setFlatValues] = createSignal(
471
702
  DialStore.getValues(panelId)
472
703
  );
473
704
  onMount(() => {
474
- DialStore.registerPanel(panelId, name, config, options?.shortcuts);
475
- setValues(DialStore.getValues(panelId));
705
+ DialStore.registerPanel(panelId, name, config, options?.shortcuts, {
706
+ retainOnUnmount: hasStableId,
707
+ persist: options?.persist
708
+ });
709
+ setFlatValues(DialStore.getValues(panelId));
476
710
  const unsubValues = DialStore.subscribe(panelId, () => {
477
- setValues(DialStore.getValues(panelId));
711
+ setFlatValues(DialStore.getValues(panelId));
478
712
  });
479
713
  const unsubActions = options?.onAction ? DialStore.subscribeActions(panelId, options.onAction) : void 0;
480
714
  onCleanup(() => {
@@ -483,65 +717,36 @@ function createDialKit(name, config, options) {
483
717
  DialStore.unregisterPanel(panelId);
484
718
  });
485
719
  });
486
- return createMemo(() => buildResolvedValues(config, values(), ""));
487
- }
488
- function buildResolvedValues(config, flatValues, prefix) {
489
- const result = {};
490
- for (const [key, configValue] of Object.entries(config)) {
491
- if (key === "_collapsed") continue;
492
- const path = prefix ? `${prefix}.${key}` : key;
493
- if (Array.isArray(configValue) && configValue.length <= 4 && typeof configValue[0] === "number") {
494
- result[key] = flatValues[path] ?? configValue[0];
495
- } else if (typeof configValue === "number" || typeof configValue === "boolean" || typeof configValue === "string") {
496
- result[key] = flatValues[path] ?? configValue;
497
- } else if (isSpringConfig(configValue)) {
498
- result[key] = flatValues[path] ?? configValue;
499
- } else if (isActionConfig(configValue)) {
500
- result[key] = flatValues[path] ?? configValue;
501
- } else if (isSelectConfig(configValue)) {
502
- const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
503
- result[key] = flatValues[path] ?? defaultValue;
504
- } else if (isColorConfig(configValue)) {
505
- result[key] = flatValues[path] ?? configValue.default ?? "#000000";
506
- } else if (isTextConfig(configValue)) {
507
- result[key] = flatValues[path] ?? configValue.default ?? "";
508
- } else if (typeof configValue === "object" && configValue !== null) {
509
- result[key] = buildResolvedValues(configValue, flatValues, path);
720
+ const values = createMemo(() => resolveDialValues(config, flatValues()));
721
+ return {
722
+ values,
723
+ setValue(path, value) {
724
+ DialStore.updateValue(panelId, path, value);
725
+ },
726
+ setValues(nextValues) {
727
+ DialStore.updateValues(panelId, flattenDialValueUpdates(config, nextValues));
728
+ },
729
+ resetValues() {
730
+ DialStore.resetValues(panelId);
731
+ },
732
+ getValues() {
733
+ return resolveDialValues(config, DialStore.getValues(panelId));
510
734
  }
511
- }
512
- return result;
513
- }
514
- function hasType(value, type) {
515
- return typeof value === "object" && value !== null && "type" in value && value.type === type;
516
- }
517
- function isSpringConfig(value) {
518
- return hasType(value, "spring");
519
- }
520
- function isActionConfig(value) {
521
- return hasType(value, "action");
522
- }
523
- function isSelectConfig(value) {
524
- return hasType(value, "select") && "options" in value && Array.isArray(value.options);
525
- }
526
- function isColorConfig(value) {
527
- return hasType(value, "color");
528
- }
529
- function isTextConfig(value) {
530
- return hasType(value, "text");
531
- }
532
- function getFirstOptionValue(options) {
533
- const first = options[0];
534
- return typeof first === "string" ? first : first.value;
735
+ };
535
736
  }
536
737
 
537
738
  // src/solid/components/DialRoot.tsx
538
739
  import { template as _$template12 } from "solid-js/web";
539
- import { memo as _$memo8 } from "solid-js/web";
740
+ import { delegateEvents as _$delegateEvents9 } from "solid-js/web";
741
+ import { style as _$style3 } from "solid-js/web";
540
742
  import { setAttribute as _$setAttribute9 } from "solid-js/web";
541
743
  import { effect as _$effect11 } from "solid-js/web";
542
744
  import { insert as _$insert12 } from "solid-js/web";
543
745
  import { createComponent as _$createComponent11 } from "solid-js/web";
544
- import { createSignal as createSignal11, onMount as onMount8, onCleanup as onCleanup9, Show as Show8, For as For5 } from "solid-js";
746
+ import { memo as _$memo8 } from "solid-js/web";
747
+ import { addEventListener as _$addEventListener } from "solid-js/web";
748
+ import { use as _$use8 } from "solid-js/web";
749
+ import { createEffect as createEffect8, createSignal as createSignal11, onCleanup as onCleanup9, onMount as onMount8, Show as Show8, For as For5 } from "solid-js";
545
750
  import { Portal as Portal3 } from "solid-js/web";
546
751
 
547
752
  // src/solid/components/ShortcutListener.tsx
@@ -862,17 +1067,19 @@ function ShortcutListener(props) {
862
1067
  });
863
1068
  }
864
1069
 
865
- // src/solid/components/Panel.tsx
866
- import { template as _$template11 } from "solid-js/web";
867
- import { delegateEvents as _$delegateEvents8 } from "solid-js/web";
868
- import { setAttribute as _$setAttribute8 } from "solid-js/web";
869
- import { effect as _$effect10 } from "solid-js/web";
870
- import { use as _$use7 } from "solid-js/web";
871
- import { insert as _$insert11 } from "solid-js/web";
872
- import { createComponent as _$createComponent10 } from "solid-js/web";
873
- import { memo as _$memo7 } from "solid-js/web";
874
- import { createSignal as createSignal10, createEffect as createEffect7, onMount as onMount7, onCleanup as onCleanup8, For as For4 } from "solid-js";
875
- import { animate as animate5 } from "motion";
1070
+ // src/solid/components/Folder.tsx
1071
+ import { template as _$template } from "solid-js/web";
1072
+ import { delegateEvents as _$delegateEvents } from "solid-js/web";
1073
+ import { setAttribute as _$setAttribute } from "solid-js/web";
1074
+ import { className as _$className } from "solid-js/web";
1075
+ import { style as _$style } from "solid-js/web";
1076
+ import { effect as _$effect } from "solid-js/web";
1077
+ import { createComponent as _$createComponent2 } from "solid-js/web";
1078
+ import { insert as _$insert } from "solid-js/web";
1079
+ import { memo as _$memo } from "solid-js/web";
1080
+ import { use as _$use } from "solid-js/web";
1081
+ import { createSignal as createSignal3, createEffect, onCleanup as onCleanup3, Show } from "solid-js";
1082
+ import { animate } from "motion";
876
1083
 
877
1084
  // src/icons.ts
878
1085
  var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
@@ -906,18 +1113,6 @@ var ICON_PANEL = {
906
1113
  };
907
1114
 
908
1115
  // src/solid/components/Folder.tsx
909
- import { template as _$template } from "solid-js/web";
910
- import { delegateEvents as _$delegateEvents } from "solid-js/web";
911
- import { setAttribute as _$setAttribute } from "solid-js/web";
912
- import { className as _$className } from "solid-js/web";
913
- import { style as _$style } from "solid-js/web";
914
- import { effect as _$effect } from "solid-js/web";
915
- import { createComponent as _$createComponent2 } from "solid-js/web";
916
- import { insert as _$insert } from "solid-js/web";
917
- import { memo as _$memo } from "solid-js/web";
918
- import { use as _$use } from "solid-js/web";
919
- import { createSignal as createSignal3, createEffect, onCleanup as onCleanup3, Show } from "solid-js";
920
- import { animate } from "motion";
921
1116
  var _tmpl$ = /* @__PURE__ */ _$template(`<div class=dialkit-panel-toolbar>`);
922
1117
  var _tmpl$2 = /* @__PURE__ */ _$template(`<div class=dialkit-folder-content><div class=dialkit-folder-inner>`);
923
1118
  var _tmpl$3 = /* @__PURE__ */ _$template(`<div><div><div class=dialkit-folder-header-top>`);
@@ -1055,17 +1250,17 @@ function Folder(props) {
1055
1250
  return () => _c$2() && (() => {
1056
1251
  var _el$1 = _tmpl$6(), _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling, _el$12 = _el$11.nextSibling, _el$13 = _el$12.nextSibling;
1057
1252
  _$effect((_p$) => {
1058
- var _v$3 = ICON_PANEL.path, _v$4 = ICON_PANEL.circles[0].cx, _v$5 = ICON_PANEL.circles[0].cy, _v$6 = ICON_PANEL.circles[0].r, _v$7 = ICON_PANEL.circles[1].cx, _v$8 = ICON_PANEL.circles[1].cy, _v$9 = ICON_PANEL.circles[1].r, _v$0 = ICON_PANEL.circles[2].cx, _v$1 = ICON_PANEL.circles[2].cy, _v$10 = ICON_PANEL.circles[2].r;
1059
- _v$3 !== _p$.e && _$setAttribute(_el$10, "d", _p$.e = _v$3);
1060
- _v$4 !== _p$.t && _$setAttribute(_el$11, "cx", _p$.t = _v$4);
1061
- _v$5 !== _p$.a && _$setAttribute(_el$11, "cy", _p$.a = _v$5);
1062
- _v$6 !== _p$.o && _$setAttribute(_el$11, "r", _p$.o = _v$6);
1063
- _v$7 !== _p$.i && _$setAttribute(_el$12, "cx", _p$.i = _v$7);
1064
- _v$8 !== _p$.n && _$setAttribute(_el$12, "cy", _p$.n = _v$8);
1065
- _v$9 !== _p$.s && _$setAttribute(_el$12, "r", _p$.s = _v$9);
1066
- _v$0 !== _p$.h && _$setAttribute(_el$13, "cx", _p$.h = _v$0);
1067
- _v$1 !== _p$.r && _$setAttribute(_el$13, "cy", _p$.r = _v$1);
1068
- _v$10 !== _p$.d && _$setAttribute(_el$13, "r", _p$.d = _v$10);
1253
+ var _v$4 = ICON_PANEL.path, _v$5 = ICON_PANEL.circles[0].cx, _v$6 = ICON_PANEL.circles[0].cy, _v$7 = ICON_PANEL.circles[0].r, _v$8 = ICON_PANEL.circles[1].cx, _v$9 = ICON_PANEL.circles[1].cy, _v$0 = ICON_PANEL.circles[1].r, _v$1 = ICON_PANEL.circles[2].cx, _v$10 = ICON_PANEL.circles[2].cy, _v$11 = ICON_PANEL.circles[2].r;
1254
+ _v$4 !== _p$.e && _$setAttribute(_el$10, "d", _p$.e = _v$4);
1255
+ _v$5 !== _p$.t && _$setAttribute(_el$11, "cx", _p$.t = _v$5);
1256
+ _v$6 !== _p$.a && _$setAttribute(_el$11, "cy", _p$.a = _v$6);
1257
+ _v$7 !== _p$.o && _$setAttribute(_el$11, "r", _p$.o = _v$7);
1258
+ _v$8 !== _p$.i && _$setAttribute(_el$12, "cx", _p$.i = _v$8);
1259
+ _v$9 !== _p$.n && _$setAttribute(_el$12, "cy", _p$.n = _v$9);
1260
+ _v$0 !== _p$.s && _$setAttribute(_el$12, "r", _p$.s = _v$0);
1261
+ _v$1 !== _p$.h && _$setAttribute(_el$13, "cx", _p$.h = _v$1);
1262
+ _v$10 !== _p$.r && _$setAttribute(_el$13, "cy", _p$.r = _v$10);
1263
+ _v$11 !== _p$.d && _$setAttribute(_el$13, "r", _p$.d = _v$11);
1069
1264
  return _p$;
1070
1265
  }, {
1071
1266
  e: void 0,
@@ -1139,13 +1334,15 @@ function Folder(props) {
1139
1334
  }
1140
1335
  }), null);
1141
1336
  _$effect((_p$) => {
1142
- var _v$ = `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`, _v$2 = `dialkit-folder-header ${props.isRoot ? "dialkit-panel-header" : ""}`;
1337
+ var _v$ = `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`, _v$2 = String(isOpen()), _v$3 = `dialkit-folder-header ${props.isRoot ? "dialkit-panel-header" : ""}`;
1143
1338
  _v$ !== _p$.e && _$className(_el$, _p$.e = _v$);
1144
- _v$2 !== _p$.t && _$className(_el$2, _p$.t = _v$2);
1339
+ _v$2 !== _p$.t && _$setAttribute(_el$, "data-open", _p$.t = _v$2);
1340
+ _v$3 !== _p$.a && _$className(_el$2, _p$.a = _v$3);
1145
1341
  return _p$;
1146
1342
  }, {
1147
1343
  e: void 0,
1148
- t: void 0
1344
+ t: void 0,
1345
+ a: void 0
1149
1346
  });
1150
1347
  return _el$;
1151
1348
  })();
@@ -1173,7 +1370,7 @@ function Folder(props) {
1173
1370
  createEffect(() => {
1174
1371
  if (!panelRef) return;
1175
1372
  const open = isOpen();
1176
- const measuredOpenHeight = contentHeight() !== void 0 ? Math.min(contentHeight() + 10, windowHeight() - 32) : panelRef.getBoundingClientRect().height;
1373
+ const measuredOpenHeight = contentHeight() !== void 0 ? Math.min(contentHeight() + (props.panelHeightOffset ?? 10), windowHeight() - 32) : panelRef.getBoundingClientRect().height;
1177
1374
  const target = {
1178
1375
  width: open ? 280 : 42,
1179
1376
  height: open ? measuredOpenHeight : 42,
@@ -1270,6 +1467,18 @@ function Folder(props) {
1270
1467
  }
1271
1468
  _$delegateEvents(["click", "pointerdown", "pointerup"]);
1272
1469
 
1470
+ // src/solid/components/Panel.tsx
1471
+ import { template as _$template11 } from "solid-js/web";
1472
+ import { delegateEvents as _$delegateEvents8 } from "solid-js/web";
1473
+ import { setAttribute as _$setAttribute8 } from "solid-js/web";
1474
+ import { effect as _$effect10 } from "solid-js/web";
1475
+ import { use as _$use7 } from "solid-js/web";
1476
+ import { insert as _$insert11 } from "solid-js/web";
1477
+ import { createComponent as _$createComponent10 } from "solid-js/web";
1478
+ import { memo as _$memo7 } from "solid-js/web";
1479
+ import { createSignal as createSignal10, createEffect as createEffect7, onMount as onMount7, onCleanup as onCleanup8, For as For4 } from "solid-js";
1480
+ import { animate as animate5 } from "motion";
1481
+
1273
1482
  // src/solid/components/Slider.tsx
1274
1483
  import { template as _$template2 } from "solid-js/web";
1275
1484
  import { delegateEvents as _$delegateEvents2 } from "solid-js/web";
@@ -2067,6 +2276,26 @@ import { use as _$use4 } from "solid-js/web";
2067
2276
  import { createSignal as createSignal7, createEffect as createEffect4, onMount as onMount5, onCleanup as onCleanup6, Show as Show5, For as For2 } from "solid-js";
2068
2277
  import { Portal } from "solid-js/web";
2069
2278
  import { animate as animate3 } from "motion";
2279
+
2280
+ // src/dropdown-position.ts
2281
+ function getDropdownPosition(trigger, portalRoot, options = {}) {
2282
+ const { dropdownHeight = 0, gap = 4, allowAbove = true } = options;
2283
+ const triggerRect = trigger.getBoundingClientRect();
2284
+ const rootRect = portalRoot.getBoundingClientRect();
2285
+ const spaceBelow = window.innerHeight - triggerRect.bottom - gap;
2286
+ const above = allowAbove && spaceBelow < dropdownHeight && triggerRect.top > spaceBelow;
2287
+ return {
2288
+ top: above ? triggerRect.top - rootRect.top - dropdownHeight - gap : triggerRect.bottom - rootRect.top + gap,
2289
+ left: triggerRect.left - rootRect.left,
2290
+ width: triggerRect.width,
2291
+ above
2292
+ };
2293
+ }
2294
+ function getDialKitPortalRoot(trigger) {
2295
+ return trigger?.closest(".dialkit-root") ?? null;
2296
+ }
2297
+
2298
+ // src/solid/components/SelectControl.tsx
2070
2299
  var _tmpl$16 = /* @__PURE__ */ _$template8(`<div class=dialkit-select-dropdown>`);
2071
2300
  var _tmpl$26 = /* @__PURE__ */ _$template8(`<div class=dialkit-select-row><button class=dialkit-select-trigger><span class=dialkit-select-label></span><div class=dialkit-select-right><span class=dialkit-select-value></span><svg class=dialkit-select-chevron viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2.5 stroke-linecap=round stroke-linejoin=round><path>`);
2072
2301
  var _tmpl$35 = /* @__PURE__ */ _$template8(`<button class=dialkit-select-option>`);
@@ -2092,8 +2321,7 @@ function SelectControl(props) {
2092
2321
  const normalized = () => normalizeOptions(props.options);
2093
2322
  const selectedOption = () => normalized().find((o) => o.value === props.value);
2094
2323
  onMount5(() => {
2095
- const root = triggerRef?.closest(".dialkit-root");
2096
- setPortalTarget(root ?? document.body);
2324
+ setPortalTarget(getDialKitPortalRoot(triggerRef) ?? document.body);
2097
2325
  if (chevronRef) {
2098
2326
  chevronRef.style.transform = `rotate(${isOpen() ? 180 : 0}deg)`;
2099
2327
  }
@@ -2115,17 +2343,12 @@ function SelectControl(props) {
2115
2343
  });
2116
2344
  });
2117
2345
  const updatePos = () => {
2118
- if (!triggerRef) return;
2119
- const rect = triggerRef.getBoundingClientRect();
2346
+ const root = portalTarget();
2347
+ if (!triggerRef || !root) return;
2120
2348
  const dropdownHeight = 8 + normalized().length * 36;
2121
- const spaceBelow = window.innerHeight - rect.bottom - 4;
2122
- const above = spaceBelow < dropdownHeight && rect.top > spaceBelow;
2123
- setPos({
2124
- top: above ? rect.top - 4 : rect.bottom + 4,
2125
- left: rect.left,
2126
- width: rect.width,
2127
- above
2128
- });
2349
+ setPos(getDropdownPosition(triggerRef, root, {
2350
+ dropdownHeight
2351
+ }));
2129
2352
  };
2130
2353
  const openDropdown = () => {
2131
2354
  closeAnim?.stop();
@@ -2179,16 +2402,11 @@ function SelectControl(props) {
2179
2402
  const p = pos();
2180
2403
  if (!p) return {};
2181
2404
  return {
2182
- position: "fixed",
2405
+ position: "absolute",
2183
2406
  left: `${p.left}px`,
2407
+ top: `${p.top}px`,
2184
2408
  width: `${p.width}px`,
2185
- ...p.above ? {
2186
- bottom: `${window.innerHeight - p.top}px`,
2187
- "transform-origin": "bottom"
2188
- } : {
2189
- top: `${p.top}px`,
2190
- "transform-origin": "top"
2191
- }
2409
+ "transform-origin": p.above ? "bottom" : "top"
2192
2410
  };
2193
2411
  };
2194
2412
  return (() => {
@@ -2362,7 +2580,7 @@ import { use as _$use6 } from "solid-js/web";
2362
2580
  import { createSignal as createSignal9, createEffect as createEffect6, onMount as onMount6, onCleanup as onCleanup7, Show as Show7, For as For3 } from "solid-js";
2363
2581
  import { Portal as Portal2 } from "solid-js/web";
2364
2582
  import { animate as animate4 } from "motion";
2365
- var _tmpl$18 = /* @__PURE__ */ _$template10(`<div class="dialkit-root dialkit-preset-dropdown"style=position:fixed><div class=dialkit-preset-item><span class=dialkit-preset-name>Version 1`);
2583
+ var _tmpl$18 = /* @__PURE__ */ _$template10(`<div class="dialkit-root dialkit-preset-dropdown"style=position:absolute><div class=dialkit-preset-item><span class=dialkit-preset-name>Version 1`);
2366
2584
  var _tmpl$28 = /* @__PURE__ */ _$template10(`<div class=dialkit-preset-manager><button class=dialkit-preset-trigger><span class=dialkit-preset-label></span><svg class=dialkit-select-chevron viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2.5 stroke-linecap=round stroke-linejoin=round><path>`);
2367
2585
  var _tmpl$37 = /* @__PURE__ */ _$template10(`<div class=dialkit-preset-item><span class=dialkit-preset-name></span><button class=dialkit-preset-delete title="Delete preset"><svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path></path><path></path><path></path><path></path><path>`);
2368
2586
  function PresetManager(props) {
@@ -2382,8 +2600,7 @@ function PresetManager(props) {
2382
2600
  const hasPresets = () => props.presets.length > 0;
2383
2601
  const activePreset = () => props.presets.find((p) => p.id === props.activePresetId);
2384
2602
  onMount6(() => {
2385
- const root = triggerRef?.closest(".dialkit-root");
2386
- setPortalTarget(root ?? document.body);
2603
+ setPortalTarget(getDialKitPortalRoot(triggerRef) ?? document.body);
2387
2604
  if (chevronRef) {
2388
2605
  chevronRef.style.transform = `rotate(${isOpen() ? 180 : 0}deg)`;
2389
2606
  chevronRef.style.opacity = String(hasPresets() ? 0.6 : 0.25);
@@ -2408,13 +2625,11 @@ function PresetManager(props) {
2408
2625
  });
2409
2626
  });
2410
2627
  const updatePos = () => {
2411
- const rect = triggerRef?.getBoundingClientRect();
2412
- if (!rect) return;
2413
- setPos({
2414
- top: rect.bottom + 4,
2415
- left: rect.left,
2416
- width: rect.width
2417
- });
2628
+ const root = portalTarget();
2629
+ if (!triggerRef || !root) return;
2630
+ setPos(getDropdownPosition(triggerRef, root, {
2631
+ allowAbove: false
2632
+ }));
2418
2633
  };
2419
2634
  const openDropdown = () => {
2420
2635
  if (!hasPresets()) return;
@@ -2586,7 +2801,8 @@ _$delegateEvents7(["click"]);
2586
2801
  var _tmpl$19 = /* @__PURE__ */ _$template11(`<button class=dialkit-button>`);
2587
2802
  var _tmpl$29 = /* @__PURE__ */ _$template11(`<button class=dialkit-toolbar-add title="Add preset"><svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2.5 stroke-linecap=round stroke-linejoin=round><path></path><path></path><path></path><path></path><path>`);
2588
2803
  var _tmpl$38 = /* @__PURE__ */ _$template11(`<button class=dialkit-toolbar-copy title="Copy parameters"><span class=dialkit-toolbar-copy-icon-wrap><span class=dialkit-toolbar-copy-icon style=opacity:1;transform:scale(1);filter:blur(0px)><svg viewBox="0 0 24 24"fill=none width=16 height=16><path stroke=currentColor stroke-width=2 stroke-linejoin=round></path><path fill=currentColor></path><path stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round></path></svg></span><span class=dialkit-toolbar-copy-icon style=opacity:0;transform:scale(0.5);filter:blur(4px)><svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round width=16 height=16><path></path></svg></span></span>Copy`);
2589
- var _tmpl$43 = /* @__PURE__ */ _$template11(`<div class=dialkit-panel-wrapper>`);
2804
+ var _tmpl$43 = /* @__PURE__ */ _$template11(`<div class=dialkit-panel-section-toolbar>`);
2805
+ var _tmpl$52 = /* @__PURE__ */ _$template11(`<div class=dialkit-panel-wrapper>`);
2590
2806
  function Panel(props) {
2591
2807
  const [copied, setCopied] = createSignal10(false);
2592
2808
  const [isPanelOpen, setIsPanelOpen] = createSignal10(props.defaultOpen ?? true);
@@ -2701,6 +2917,10 @@ Apply these values as the new defaults in the createDialKit call.`;
2701
2917
  scale: 1
2702
2918
  }, tapTransition);
2703
2919
  };
2920
+ const handleOpenChange = (open) => {
2921
+ setIsPanelOpen(open);
2922
+ props.onOpenChange?.(open);
2923
+ };
2704
2924
  const renderControl = (control) => {
2705
2925
  const value = () => values()[control.path];
2706
2926
  switch (control.type) {
@@ -2894,9 +3114,28 @@ Apply these values as the new defaults in the createDialKit call.`;
2894
3114
  });
2895
3115
  return _el$9;
2896
3116
  })()];
3117
+ if (props.variant === "section") {
3118
+ return _$createComponent10(Folder, {
3119
+ get title() {
3120
+ return props.panel.name;
3121
+ },
3122
+ get defaultOpen() {
3123
+ return props.defaultOpen ?? true;
3124
+ },
3125
+ onOpenChange: handleOpenChange,
3126
+ get children() {
3127
+ return [(() => {
3128
+ var _el$17 = _tmpl$43();
3129
+ _el$17.$$click = (e) => e.stopPropagation();
3130
+ _$insert11(_el$17, toolbar);
3131
+ return _el$17;
3132
+ })(), _$memo7(() => renderControls())];
3133
+ }
3134
+ });
3135
+ }
2897
3136
  return (() => {
2898
- var _el$17 = _tmpl$43();
2899
- _$insert11(_el$17, _$createComponent10(Folder, {
3137
+ var _el$18 = _tmpl$52();
3138
+ _$insert11(_el$18, _$createComponent10(Folder, {
2900
3139
  get title() {
2901
3140
  return props.panel.name;
2902
3141
  },
@@ -2907,25 +3146,100 @@ Apply these values as the new defaults in the createDialKit call.`;
2907
3146
  get inline() {
2908
3147
  return props.inline ?? false;
2909
3148
  },
2910
- onOpenChange: setIsPanelOpen,
3149
+ onOpenChange: handleOpenChange,
2911
3150
  toolbar,
2912
3151
  get children() {
2913
3152
  return renderControls();
2914
3153
  }
2915
3154
  }));
2916
- return _el$17;
3155
+ return _el$18;
2917
3156
  })();
2918
3157
  }
2919
3158
  _$delegateEvents8(["click", "pointerdown", "pointerup"]);
2920
3159
 
3160
+ // src/panel-drag.ts
3161
+ var PANEL_DRAG_THRESHOLD = 8;
3162
+ var COLLAPSED_PANEL_SIZE = 42;
3163
+ var DRAG_EXCLUSION_SELECTOR = [
3164
+ ".dialkit-panel-icon",
3165
+ ".dialkit-panel-toolbar",
3166
+ "button",
3167
+ "input",
3168
+ "select",
3169
+ "textarea",
3170
+ "a",
3171
+ '[role="button"]',
3172
+ '[contenteditable="true"]'
3173
+ ].join(",");
3174
+ function getPanelDragHandle(target, panel) {
3175
+ if (!(target instanceof Element) || !panel) return null;
3176
+ const inner = target.closest(".dialkit-panel-inner");
3177
+ if (!inner || !panel.contains(inner)) return null;
3178
+ if (inner.getAttribute("data-collapsed") === "true") {
3179
+ return inner;
3180
+ }
3181
+ const header = target.closest(".dialkit-panel-header");
3182
+ if (!header || !inner.contains(header)) return null;
3183
+ if (target.closest(DRAG_EXCLUSION_SELECTOR)) return null;
3184
+ return header;
3185
+ }
3186
+ function getPanelDragStart(pointerX, pointerY, panel) {
3187
+ const rect = panel.getBoundingClientRect();
3188
+ return {
3189
+ pointerX,
3190
+ pointerY,
3191
+ elX: rect.left,
3192
+ elY: rect.top
3193
+ };
3194
+ }
3195
+ function getPanelDragOffset(start, pointerX, pointerY) {
3196
+ return {
3197
+ x: start.elX + pointerX - start.pointerX,
3198
+ y: start.elY + pointerY - start.pointerY
3199
+ };
3200
+ }
3201
+ function hasPanelDragMoved(start, pointerX, pointerY) {
3202
+ const dx = pointerX - start.pointerX;
3203
+ const dy = pointerY - start.pointerY;
3204
+ return Math.hypot(dx, dy) >= PANEL_DRAG_THRESHOLD;
3205
+ }
3206
+ function getPanelOriginX(position, offset, viewportWidth = typeof window !== "undefined" ? window.innerWidth : void 0) {
3207
+ if (offset && viewportWidth) {
3208
+ return offset.x + COLLAPSED_PANEL_SIZE / 2 < viewportWidth / 2 ? "left" : "right";
3209
+ }
3210
+ return position.endsWith("left") ? "left" : "right";
3211
+ }
3212
+ function blockPanelDragClick(handle) {
3213
+ const blocker = (event) => {
3214
+ event.preventDefault();
3215
+ event.stopImmediatePropagation();
3216
+ event.stopPropagation();
3217
+ };
3218
+ handle.addEventListener("click", blocker, { capture: true, once: true });
3219
+ window.setTimeout(() => {
3220
+ handle.removeEventListener("click", blocker, true);
3221
+ }, 0);
3222
+ }
3223
+
2921
3224
  // src/solid/components/DialRoot.tsx
2922
- var _tmpl$20 = /* @__PURE__ */ _$template12(`<div class=dialkit-root><div class=dialkit-panel>`);
3225
+ var _tmpl$20 = /* @__PURE__ */ _$template12(`<div class=dialkit-panel-wrapper>`);
3226
+ var _tmpl$210 = /* @__PURE__ */ _$template12(`<div class=dialkit-root><div class=dialkit-panel>`);
2923
3227
  var isDevDefault = typeof process !== "undefined" && process?.env?.NODE_ENV ? process.env.NODE_ENV !== "production" : typeof import.meta !== "undefined" && import.meta.env?.MODE ? import.meta.env.MODE !== "production" : true;
2924
3228
  function DialRoot(props) {
2925
3229
  if ((props.productionEnabled ?? isDevDefault) === false) return null;
2926
3230
  const [panels, setPanels] = createSignal11([]);
2927
3231
  const [mounted, setMounted] = createSignal11(false);
3232
+ const [dragOffset, setDragOffset] = createSignal11(null);
3233
+ const [activePosition, setActivePosition] = createSignal11(props.position ?? "top-right");
2928
3234
  const inline = () => (props.mode ?? "popover") === "inline";
3235
+ let panelRef;
3236
+ let lastDragOffset = null;
3237
+ let dragging = false;
3238
+ let dragStart = null;
3239
+ let didDrag = false;
3240
+ let dragTarget = null;
3241
+ let panelOpenStates = /* @__PURE__ */ new Map();
3242
+ let rootOpen;
2929
3243
  onMount8(() => {
2930
3244
  setMounted(true);
2931
3245
  setPanels(DialStore.getPanels());
@@ -2934,35 +3248,175 @@ function DialRoot(props) {
2934
3248
  });
2935
3249
  onCleanup9(unsub);
2936
3250
  });
3251
+ createEffect8(() => {
3252
+ const fallbackOpen = inline() || (props.defaultOpen ?? true);
3253
+ const nextStates = /* @__PURE__ */ new Map();
3254
+ for (const panel of panels()) {
3255
+ nextStates.set(panel.id, panelOpenStates.get(panel.id) ?? fallbackOpen);
3256
+ }
3257
+ panelOpenStates = nextStates;
3258
+ rootOpen = Array.from(nextStates.values()).some(Boolean);
3259
+ });
3260
+ createEffect8(() => {
3261
+ if (!mounted() || inline() || !panelRef) return;
3262
+ const observer = new MutationObserver(() => {
3263
+ const inners = panelRef?.querySelectorAll(".dialkit-panel-inner");
3264
+ if (!inners || inners.length === 0) return;
3265
+ const collapsed = Array.from(inners).every((el) => el.getAttribute("data-collapsed") === "true");
3266
+ const currentDragOffset = dragOffset();
3267
+ if (!collapsed) {
3268
+ if (currentDragOffset) {
3269
+ lastDragOffset = currentDragOffset;
3270
+ const bubbleCenterX = currentDragOffset.x + 21;
3271
+ setActivePosition(bubbleCenterX < window.innerWidth / 2 ? "top-left" : "top-right");
3272
+ } else {
3273
+ setActivePosition(props.position ?? "top-right");
3274
+ }
3275
+ setDragOffset(null);
3276
+ } else if (currentDragOffset) {
3277
+ lastDragOffset = currentDragOffset;
3278
+ } else if (lastDragOffset) {
3279
+ setDragOffset(lastDragOffset);
3280
+ }
3281
+ });
3282
+ observer.observe(panelRef, {
3283
+ subtree: true,
3284
+ attributes: true,
3285
+ attributeFilter: ["data-collapsed"]
3286
+ });
3287
+ onCleanup9(() => observer.disconnect());
3288
+ });
3289
+ const handlePointerDown = (event) => {
3290
+ const panel = panelRef ?? null;
3291
+ const handle = getPanelDragHandle(event.target, panel);
3292
+ if (!panel || !handle) return;
3293
+ dragTarget = handle;
3294
+ dragStart = getPanelDragStart(event.clientX, event.clientY, panel);
3295
+ didDrag = false;
3296
+ dragging = true;
3297
+ handle.setPointerCapture(event.pointerId);
3298
+ };
3299
+ const handlePointerMove = (event) => {
3300
+ if (!dragging || !dragStart) return;
3301
+ if (!didDrag && !hasPanelDragMoved(dragStart, event.clientX, event.clientY)) return;
3302
+ didDrag = true;
3303
+ setDragOffset(getPanelDragOffset(dragStart, event.clientX, event.clientY));
3304
+ };
3305
+ const handlePointerUp = (event) => {
3306
+ if (!dragging) return;
3307
+ dragging = false;
3308
+ dragStart = null;
3309
+ const handle = dragTarget;
3310
+ if (handle?.hasPointerCapture(event.pointerId)) {
3311
+ handle.releasePointerCapture(event.pointerId);
3312
+ }
3313
+ if (didDrag) {
3314
+ event.stopPropagation();
3315
+ if (handle) {
3316
+ blockPanelDragClick(handle);
3317
+ }
3318
+ }
3319
+ dragTarget = null;
3320
+ };
3321
+ const handlePanelOpenChange = (panelId, open) => {
3322
+ panelOpenStates.set(panelId, open);
3323
+ const fallbackOpen = inline() || (props.defaultOpen ?? true);
3324
+ const nextRootOpen = panels().some((panel) => panelOpenStates.get(panel.id) ?? fallbackOpen);
3325
+ if (rootOpen === nextRootOpen) return;
3326
+ rootOpen = nextRootOpen;
3327
+ props.onOpenChange?.(nextRootOpen);
3328
+ };
3329
+ const handleRootOpenChange = (open) => {
3330
+ if (rootOpen === open) return;
3331
+ rootOpen = open;
3332
+ props.onOpenChange?.(open);
3333
+ };
3334
+ const dragStyle = () => {
3335
+ const offset = dragOffset();
3336
+ return offset ? {
3337
+ top: `${offset.y}px`,
3338
+ left: `${offset.x}px`,
3339
+ right: "auto",
3340
+ bottom: "auto"
3341
+ } : void 0;
3342
+ };
2937
3343
  const content = () => _$createComponent11(ShortcutListener, {
2938
3344
  get children() {
2939
- var _el$ = _tmpl$20(), _el$2 = _el$.firstChild;
2940
- _$insert12(_el$2, _$createComponent11(For5, {
2941
- get each() {
2942
- return panels();
3345
+ var _el$ = _tmpl$210(), _el$2 = _el$.firstChild;
3346
+ _$addEventListener(_el$2, "pointercancel", !inline() ? handlePointerUp : void 0);
3347
+ _$addEventListener(_el$2, "pointerup", !inline() ? handlePointerUp : void 0, true);
3348
+ _$addEventListener(_el$2, "pointermove", !inline() ? handlePointerMove : void 0, true);
3349
+ _$addEventListener(_el$2, "pointerdown", !inline() ? handlePointerDown : void 0, true);
3350
+ var _ref$ = panelRef;
3351
+ typeof _ref$ === "function" ? _$use8(_ref$, _el$2) : panelRef = _el$2;
3352
+ _$insert12(_el$2, _$createComponent11(Show8, {
3353
+ get when() {
3354
+ return panels().length > 1;
2943
3355
  },
2944
- children: (panel) => _$createComponent11(Panel, {
2945
- panel,
2946
- get defaultOpen() {
2947
- return inline() || (props.defaultOpen ?? true);
2948
- },
2949
- get inline() {
2950
- return inline();
2951
- }
2952
- })
3356
+ get fallback() {
3357
+ return _$createComponent11(For5, {
3358
+ get each() {
3359
+ return panels();
3360
+ },
3361
+ children: (panel) => _$createComponent11(Panel, {
3362
+ panel,
3363
+ get defaultOpen() {
3364
+ return inline() || (props.defaultOpen ?? true);
3365
+ },
3366
+ get inline() {
3367
+ return inline();
3368
+ },
3369
+ onOpenChange: (open) => handlePanelOpenChange(panel.id, open)
3370
+ })
3371
+ });
3372
+ },
3373
+ get children() {
3374
+ var _el$3 = _tmpl$20();
3375
+ _$insert12(_el$3, _$createComponent11(Folder, {
3376
+ title: "DialKit",
3377
+ get defaultOpen() {
3378
+ return inline() || (props.defaultOpen ?? true);
3379
+ },
3380
+ isRoot: true,
3381
+ get inline() {
3382
+ return inline();
3383
+ },
3384
+ onOpenChange: handleRootOpenChange,
3385
+ panelHeightOffset: 2,
3386
+ get children() {
3387
+ return _$createComponent11(For5, {
3388
+ get each() {
3389
+ return panels();
3390
+ },
3391
+ children: (panel) => _$createComponent11(Panel, {
3392
+ panel,
3393
+ defaultOpen: true,
3394
+ variant: "section"
3395
+ })
3396
+ });
3397
+ }
3398
+ }));
3399
+ return _el$3;
3400
+ }
2953
3401
  }));
2954
3402
  _$effect11((_p$) => {
2955
- var _v$ = props.mode ?? "popover", _v$2 = props.theme ?? "system", _v$3 = inline() ? void 0 : props.position ?? "top-right", _v$4 = props.mode ?? "popover";
3403
+ var _v$ = props.mode ?? "popover", _v$2 = props.theme ?? "system", _v$3 = inline() ? void 0 : dragOffset() ? void 0 : activePosition(), _v$4 = inline() ? void 0 : getPanelOriginX(activePosition(), dragOffset()), _v$5 = props.mode ?? "popover", _v$6 = panels().length > 1 ? "true" : void 0, _v$7 = dragStyle();
2956
3404
  _v$ !== _p$.e && _$setAttribute9(_el$, "data-mode", _p$.e = _v$);
2957
3405
  _v$2 !== _p$.t && _$setAttribute9(_el$, "data-theme", _p$.t = _v$2);
2958
3406
  _v$3 !== _p$.a && _$setAttribute9(_el$2, "data-position", _p$.a = _v$3);
2959
- _v$4 !== _p$.o && _$setAttribute9(_el$2, "data-mode", _p$.o = _v$4);
3407
+ _v$4 !== _p$.o && _$setAttribute9(_el$2, "data-origin-x", _p$.o = _v$4);
3408
+ _v$5 !== _p$.i && _$setAttribute9(_el$2, "data-mode", _p$.i = _v$5);
3409
+ _v$6 !== _p$.n && _$setAttribute9(_el$2, "data-multiple", _p$.n = _v$6);
3410
+ _p$.s = _$style3(_el$2, _v$7, _p$.s);
2960
3411
  return _p$;
2961
3412
  }, {
2962
3413
  e: void 0,
2963
3414
  t: void 0,
2964
3415
  a: void 0,
2965
- o: void 0
3416
+ o: void 0,
3417
+ i: void 0,
3418
+ n: void 0,
3419
+ s: void 0
2966
3420
  });
2967
3421
  return _el$;
2968
3422
  }
@@ -2993,16 +3447,17 @@ function DialRoot(props) {
2993
3447
  }
2994
3448
  });
2995
3449
  }
3450
+ _$delegateEvents9(["pointerdown", "pointermove", "pointerup"]);
2996
3451
 
2997
3452
  // src/solid/components/ButtonGroup.tsx
2998
3453
  import { template as _$template13 } from "solid-js/web";
2999
- import { delegateEvents as _$delegateEvents9 } from "solid-js/web";
3000
- import { addEventListener as _$addEventListener } from "solid-js/web";
3454
+ import { delegateEvents as _$delegateEvents10 } from "solid-js/web";
3455
+ import { addEventListener as _$addEventListener2 } from "solid-js/web";
3001
3456
  import { insert as _$insert13 } from "solid-js/web";
3002
3457
  import { createComponent as _$createComponent12 } from "solid-js/web";
3003
3458
  import { For as For6 } from "solid-js";
3004
3459
  var _tmpl$21 = /* @__PURE__ */ _$template13(`<div class=dialkit-button-group>`);
3005
- var _tmpl$210 = /* @__PURE__ */ _$template13(`<button class=dialkit-button>`);
3460
+ var _tmpl$211 = /* @__PURE__ */ _$template13(`<button class=dialkit-button>`);
3006
3461
  function ButtonGroup(props) {
3007
3462
  return (() => {
3008
3463
  var _el$ = _tmpl$21();
@@ -3011,8 +3466,8 @@ function ButtonGroup(props) {
3011
3466
  return props.buttons;
3012
3467
  },
3013
3468
  children: (button) => (() => {
3014
- var _el$2 = _tmpl$210();
3015
- _$addEventListener(_el$2, "click", button.onClick, true);
3469
+ var _el$2 = _tmpl$211();
3470
+ _$addEventListener2(_el$2, "click", button.onClick, true);
3016
3471
  _$insert13(_el$2, () => button.label);
3017
3472
  return _el$2;
3018
3473
  })()
@@ -3020,7 +3475,7 @@ function ButtonGroup(props) {
3020
3475
  return _el$;
3021
3476
  })();
3022
3477
  }
3023
- _$delegateEvents9(["click"]);
3478
+ _$delegateEvents10(["click"]);
3024
3479
  export {
3025
3480
  ButtonGroup,
3026
3481
  ColorControl,
@@ -3034,6 +3489,7 @@ export {
3034
3489
  SpringVisualization,
3035
3490
  TextControl,
3036
3491
  Toggle,
3037
- createDialKit
3492
+ createDialKit,
3493
+ createDialKitController
3038
3494
  };
3039
3495
  //# sourceMappingURL=index.js.map