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
package/dist/vue/index.js CHANGED
@@ -3,6 +3,89 @@ import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
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();
16
- }
17
- registerPanel(id, name, config, shortcuts) {
99
+ this.defaultValues = /* @__PURE__ */ new Map();
100
+ this.registrationCounts = /* @__PURE__ */ new Map();
101
+ this.retainedPanels = /* @__PURE__ */ new Set();
102
+ this.persistConfigs = /* @__PURE__ */ new Map();
103
+ }
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;
@@ -466,21 +693,30 @@ var DialStore = new DialStoreClass();
466
693
  // src/vue/useDialKit.ts
467
694
  var dialKitInstance = 0;
468
695
  function useDialKit(name, config, options) {
469
- const panelId = `${name}-${++dialKitInstance}`;
696
+ return useDialKitController(name, config, options).values;
697
+ }
698
+ function useDialKitController(name, config, options) {
699
+ const hasStableId = options?.id !== void 0;
700
+ const panelId = options?.id ?? `${name}-${++dialKitInstance}`;
470
701
  const configRef = shallowRef(config);
471
702
  const onActionRef = ref(options?.onAction);
472
703
  const shortcutsRef = shallowRef(options?.shortcuts);
473
- const values = ref(DialStore.getValues(panelId));
704
+ const persistRef = shallowRef(options?.persist);
705
+ const flatValues = ref(DialStore.getValues(panelId));
474
706
  const mounted = ref(false);
475
707
  const serializedConfig = computed(() => JSON.stringify(config));
476
708
  const serializedShortcuts = computed(() => JSON.stringify(options?.shortcuts));
709
+ const serializedPersist = computed(() => JSON.stringify(options?.persist));
477
710
  let unsubscribeValues;
478
711
  let unsubscribeActions;
479
712
  const register = () => {
480
- DialStore.registerPanel(panelId, name, configRef.value, shortcutsRef.value);
481
- values.value = DialStore.getValues(panelId);
713
+ DialStore.registerPanel(panelId, name, configRef.value, shortcutsRef.value, {
714
+ retainOnUnmount: hasStableId,
715
+ persist: persistRef.value
716
+ });
717
+ flatValues.value = DialStore.getValues(panelId);
482
718
  unsubscribeValues = DialStore.subscribe(panelId, () => {
483
- values.value = DialStore.getValues(panelId);
719
+ flatValues.value = DialStore.getValues(panelId);
484
720
  });
485
721
  unsubscribeActions = DialStore.subscribeActions(panelId, (action) => {
486
722
  onActionRef.value?.(action);
@@ -492,12 +728,19 @@ function useDialKit(name, config, options) {
492
728
  watch(() => options?.shortcuts, (next) => {
493
729
  shortcutsRef.value = next;
494
730
  });
495
- watch([serializedConfig, serializedShortcuts], () => {
731
+ watch(() => options?.persist, (next) => {
732
+ persistRef.value = next;
733
+ });
734
+ watch([serializedConfig, serializedShortcuts, serializedPersist], () => {
496
735
  configRef.value = config;
497
736
  shortcutsRef.value = options?.shortcuts;
737
+ persistRef.value = options?.persist;
498
738
  if (mounted.value) {
499
- DialStore.updatePanel(panelId, name, configRef.value, shortcutsRef.value);
500
- values.value = DialStore.getValues(panelId);
739
+ DialStore.updatePanel(panelId, name, configRef.value, shortcutsRef.value, {
740
+ retainOnUnmount: hasStableId,
741
+ persist: persistRef.value
742
+ });
743
+ flatValues.value = DialStore.getValues(panelId);
501
744
  }
502
745
  });
503
746
  onMounted(register);
@@ -509,58 +752,22 @@ function useDialKit(name, config, options) {
509
752
  unsubscribeActions?.();
510
753
  DialStore.unregisterPanel(panelId);
511
754
  });
512
- return computed(() => buildResolvedValues(configRef.value, values.value, ""));
513
- }
514
- function buildResolvedValues(config, flatValues, prefix) {
515
- const result = {};
516
- for (const [key, configValue] of Object.entries(config)) {
517
- if (key === "_collapsed") continue;
518
- const path = prefix ? `${prefix}.${key}` : key;
519
- if (Array.isArray(configValue) && configValue.length <= 4 && typeof configValue[0] === "number") {
520
- result[key] = flatValues[path] ?? configValue[0];
521
- } else if (typeof configValue === "number" || typeof configValue === "boolean" || typeof configValue === "string") {
522
- result[key] = flatValues[path] ?? configValue;
523
- } else if (isSpringConfig(configValue) || isEasingConfig(configValue)) {
524
- result[key] = flatValues[path] ?? configValue;
525
- } else if (isActionConfig(configValue)) {
526
- result[key] = flatValues[path] ?? configValue;
527
- } else if (isSelectConfig(configValue)) {
528
- const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
529
- result[key] = flatValues[path] ?? defaultValue;
530
- } else if (isColorConfig(configValue)) {
531
- result[key] = flatValues[path] ?? configValue.default ?? "#000000";
532
- } else if (isTextConfig(configValue)) {
533
- result[key] = flatValues[path] ?? configValue.default ?? "";
534
- } else if (typeof configValue === "object" && configValue !== null) {
535
- result[key] = buildResolvedValues(configValue, flatValues, path);
755
+ const values = computed(() => resolveDialValues(configRef.value, flatValues.value));
756
+ return {
757
+ values,
758
+ setValue(path, value) {
759
+ DialStore.updateValue(panelId, path, value);
760
+ },
761
+ setValues(nextValues) {
762
+ DialStore.updateValues(panelId, flattenDialValueUpdates(configRef.value, nextValues));
763
+ },
764
+ resetValues() {
765
+ DialStore.resetValues(panelId);
766
+ },
767
+ getValues() {
768
+ return resolveDialValues(configRef.value, DialStore.getValues(panelId));
536
769
  }
537
- }
538
- return result;
539
- }
540
- function hasType(value, type) {
541
- return typeof value === "object" && value !== null && "type" in value && value.type === type;
542
- }
543
- function isSpringConfig(value) {
544
- return hasType(value, "spring");
545
- }
546
- function isEasingConfig(value) {
547
- return hasType(value, "easing");
548
- }
549
- function isActionConfig(value) {
550
- return hasType(value, "action");
551
- }
552
- function isSelectConfig(value) {
553
- return hasType(value, "select") && "options" in value && Array.isArray(value.options);
554
- }
555
- function isColorConfig(value) {
556
- return hasType(value, "color");
557
- }
558
- function isTextConfig(value) {
559
- return hasType(value, "text");
560
- }
561
- function getFirstOptionValue(options) {
562
- const first = options[0];
563
- return typeof first === "string" ? first : first.value;
770
+ };
564
771
  }
565
772
 
566
773
  // src/vue/directives/dialkit.ts
@@ -572,11 +779,11 @@ import {
572
779
  } from "vue";
573
780
 
574
781
  // src/vue/components/DialRoot.ts
575
- import { defineComponent as defineComponent15, h as h15, onMounted as onMounted10, onUnmounted as onUnmounted9, ref as ref13, Teleport as Teleport3 } from "vue";
782
+ import { computed as computed5, defineComponent as defineComponent15, h as h15, nextTick as nextTick3, onMounted as onMounted10, onUnmounted as onUnmounted9, ref as ref13, Teleport as Teleport3 } from "vue";
576
783
 
577
- // src/vue/components/Panel.ts
578
- import { Fragment, defineComponent as defineComponent14, h as h14, onMounted as onMounted9, onUnmounted as onUnmounted8, ref as ref12 } from "vue";
579
- import { AnimatePresence as AnimatePresence4, motion as motion4 } from "motion-v";
784
+ // src/vue/components/Folder.ts
785
+ import { defineComponent, h, onMounted as onMounted2, onUnmounted as onUnmounted2, ref as ref2 } from "vue";
786
+ import { AnimatePresence, motion } from "motion-v";
580
787
 
581
788
  // src/icons.ts
582
789
  var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
@@ -610,8 +817,6 @@ var ICON_PANEL = {
610
817
  };
611
818
 
612
819
  // src/vue/components/Folder.ts
613
- import { defineComponent, h, onMounted as onMounted2, onUnmounted as onUnmounted2, ref as ref2 } from "vue";
614
- import { AnimatePresence, motion } from "motion-v";
615
820
  var Folder = defineComponent({
616
821
  name: "DialKitFolder",
617
822
  props: {
@@ -623,6 +828,10 @@ var Folder = defineComponent({
623
828
  type: null,
624
829
  required: false,
625
830
  default: null
831
+ },
832
+ panelHeightOffset: {
833
+ type: Number,
834
+ default: 10
626
835
  }
627
836
  },
628
837
  emits: ["openChange"],
@@ -722,7 +931,8 @@ var Folder = defineComponent({
722
931
  };
723
932
  const folderContent = () => h("div", {
724
933
  ref: props.isRoot ? contentRef : void 0,
725
- class: `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`
934
+ class: `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`,
935
+ "data-open": String(isOpen.value)
726
936
  }, [
727
937
  renderHeader(),
728
938
  renderContent()
@@ -734,7 +944,7 @@ var Folder = defineComponent({
734
944
  }
735
945
  const panelStyle = isOpen.value ? {
736
946
  width: 280,
737
- height: contentHeight.value !== void 0 ? Math.min(contentHeight.value + 10, windowHeight.value - 32) : "auto",
947
+ height: contentHeight.value !== void 0 ? Math.min(contentHeight.value + props.panelHeightOffset, windowHeight.value - 32) : "auto",
738
948
  borderRadius: 14,
739
949
  boxShadow: "var(--dial-shadow)",
740
950
  cursor: void 0,
@@ -761,6 +971,10 @@ var Folder = defineComponent({
761
971
  }
762
972
  });
763
973
 
974
+ // src/vue/components/Panel.ts
975
+ import { Fragment, defineComponent as defineComponent14, h as h14, onMounted as onMounted9, onUnmounted as onUnmounted8, ref as ref12 } from "vue";
976
+ import { AnimatePresence as AnimatePresence4, motion as motion4 } from "motion-v";
977
+
764
978
  // src/vue/components/Slider.ts
765
979
  import { defineComponent as defineComponent2, h as h2, computed as computed2, nextTick, onMounted as onMounted3, onUnmounted as onUnmounted3, ref as ref3, watch as watch2 } from "vue";
766
980
  import { animate, motionValue } from "motion-v";
@@ -1893,6 +2107,26 @@ var TextControl = defineComponent9({
1893
2107
  // src/vue/components/SelectControl.ts
1894
2108
  import { Teleport, defineComponent as defineComponent10, h as h10, onMounted as onMounted7, ref as ref8, watch as watch4 } from "vue";
1895
2109
  import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion-v";
2110
+
2111
+ // src/dropdown-position.ts
2112
+ function getDropdownPosition(trigger, portalRoot, options = {}) {
2113
+ const { dropdownHeight = 0, gap = 4, allowAbove = true } = options;
2114
+ const triggerRect = trigger.getBoundingClientRect();
2115
+ const rootRect = portalRoot.getBoundingClientRect();
2116
+ const spaceBelow = window.innerHeight - triggerRect.bottom - gap;
2117
+ const above = allowAbove && spaceBelow < dropdownHeight && triggerRect.top > spaceBelow;
2118
+ return {
2119
+ top: above ? triggerRect.top - rootRect.top - dropdownHeight - gap : triggerRect.bottom - rootRect.top + gap,
2120
+ left: triggerRect.left - rootRect.left,
2121
+ width: triggerRect.width,
2122
+ above
2123
+ };
2124
+ }
2125
+ function getDialKitPortalRoot(trigger) {
2126
+ return trigger?.closest(".dialkit-root") ?? null;
2127
+ }
2128
+
2129
+ // src/vue/components/SelectControl.ts
1896
2130
  function toTitleCase(value) {
1897
2131
  return value.replace(/\b\w/g, (char) => char.toUpperCase());
1898
2132
  }
@@ -1921,17 +2155,9 @@ var SelectControl = defineComponent10({
1921
2155
  const normalizedOptions = () => normalizeOptions(props.options);
1922
2156
  const selectedLabel = () => normalizedOptions().find((option) => option.value === props.value)?.label ?? props.value;
1923
2157
  const updatePos = () => {
1924
- if (!triggerRef.value) return;
1925
- const rect = triggerRef.value.getBoundingClientRect();
2158
+ if (!triggerRef.value || !portalTarget.value) return;
1926
2159
  const dropdownHeight = 8 + normalizedOptions().length * 36;
1927
- const spaceBelow = window.innerHeight - rect.bottom - 4;
1928
- const above = spaceBelow < dropdownHeight && rect.top > spaceBelow;
1929
- pos.value = {
1930
- top: above ? rect.top - 4 : rect.bottom + 4,
1931
- left: rect.left,
1932
- width: rect.width,
1933
- above
1934
- };
2160
+ pos.value = getDropdownPosition(triggerRef.value, portalTarget.value, { dropdownHeight });
1935
2161
  };
1936
2162
  const openDropdown = () => {
1937
2163
  updatePos();
@@ -1975,8 +2201,7 @@ var SelectControl = defineComponent10({
1975
2201
  });
1976
2202
  });
1977
2203
  onMounted7(() => {
1978
- const root = triggerRef.value?.closest(".dialkit-root");
1979
- portalTarget.value = root ?? document.body;
2204
+ portalTarget.value = getDialKitPortalRoot(triggerRef.value) ?? document.body;
1980
2205
  });
1981
2206
  return () => h10("div", { class: "dialkit-select-row" }, [
1982
2207
  h10("button", {
@@ -2012,16 +2237,11 @@ var SelectControl = defineComponent10({
2012
2237
  exit: { opacity: 0, y: pos.value.above ? 8 : -8, scale: 0.95 },
2013
2238
  transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
2014
2239
  style: {
2015
- position: "fixed",
2240
+ position: "absolute",
2016
2241
  left: `${pos.value.left}px`,
2242
+ top: `${pos.value.top}px`,
2017
2243
  width: `${pos.value.width}px`,
2018
- ...pos.value.above ? {
2019
- bottom: `${window.innerHeight - pos.value.top}px`,
2020
- transformOrigin: "bottom"
2021
- } : {
2022
- top: `${pos.value.top}px`,
2023
- transformOrigin: "top"
2024
- }
2244
+ transformOrigin: pos.value.above ? "bottom" : "top"
2025
2245
  }
2026
2246
  }, normalizedOptions().map((option) => h10("button", {
2027
2247
  key: option.value,
@@ -2478,9 +2698,14 @@ var Panel = defineComponent14({
2478
2698
  inline: {
2479
2699
  type: Boolean,
2480
2700
  default: false
2701
+ },
2702
+ variant: {
2703
+ type: String,
2704
+ default: "root"
2481
2705
  }
2482
2706
  },
2483
- setup(props) {
2707
+ emits: ["openChange"],
2708
+ setup(props, { emit }) {
2484
2709
  const shortcutCtx = useShortcutContext();
2485
2710
  const values = ref12(DialStore.getValues(props.panel.id));
2486
2711
  const presets = ref12(DialStore.getPresets(props.panel.id));
@@ -2529,6 +2754,9 @@ Apply these values as the new defaults in the useDialKit call.`;
2529
2754
  copied.value = false;
2530
2755
  }, 1500);
2531
2756
  };
2757
+ const handleOpenChange = (open) => {
2758
+ emit("openChange", open);
2759
+ };
2532
2760
  const renderControl = (control) => {
2533
2761
  const value = values.value[control.path];
2534
2762
  switch (control.type) {
@@ -2697,13 +2925,29 @@ Apply these values as the new defaults in the useDialKit call.`;
2697
2925
  "Copy"
2698
2926
  ])
2699
2927
  ]);
2928
+ if (props.variant === "section") {
2929
+ return h14(Folder, {
2930
+ title: props.panel.name,
2931
+ defaultOpen: props.defaultOpen,
2932
+ onOpenChange: handleOpenChange
2933
+ }, {
2934
+ default: () => [
2935
+ h14("div", {
2936
+ class: "dialkit-panel-section-toolbar",
2937
+ onClick: (event) => event.stopPropagation()
2938
+ }, [toolbarNode]),
2939
+ ...props.panel.controls.map(renderControl)
2940
+ ]
2941
+ });
2942
+ }
2700
2943
  return h14("div", { class: "dialkit-panel-wrapper" }, [
2701
2944
  h14(Folder, {
2702
2945
  title: props.panel.name,
2703
2946
  defaultOpen: props.defaultOpen,
2704
2947
  isRoot: true,
2705
2948
  inline: props.inline,
2706
- toolbar: () => toolbarNode
2949
+ toolbar: () => toolbarNode,
2950
+ onOpenChange: handleOpenChange
2707
2951
  }, {
2708
2952
  default: () => props.panel.controls.map(renderControl)
2709
2953
  })
@@ -2712,6 +2956,70 @@ Apply these values as the new defaults in the useDialKit call.`;
2712
2956
  }
2713
2957
  });
2714
2958
 
2959
+ // src/panel-drag.ts
2960
+ var PANEL_DRAG_THRESHOLD = 8;
2961
+ var COLLAPSED_PANEL_SIZE = 42;
2962
+ var DRAG_EXCLUSION_SELECTOR = [
2963
+ ".dialkit-panel-icon",
2964
+ ".dialkit-panel-toolbar",
2965
+ "button",
2966
+ "input",
2967
+ "select",
2968
+ "textarea",
2969
+ "a",
2970
+ '[role="button"]',
2971
+ '[contenteditable="true"]'
2972
+ ].join(",");
2973
+ function getPanelDragHandle(target, panel) {
2974
+ if (!(target instanceof Element) || !panel) return null;
2975
+ const inner = target.closest(".dialkit-panel-inner");
2976
+ if (!inner || !panel.contains(inner)) return null;
2977
+ if (inner.getAttribute("data-collapsed") === "true") {
2978
+ return inner;
2979
+ }
2980
+ const header = target.closest(".dialkit-panel-header");
2981
+ if (!header || !inner.contains(header)) return null;
2982
+ if (target.closest(DRAG_EXCLUSION_SELECTOR)) return null;
2983
+ return header;
2984
+ }
2985
+ function getPanelDragStart(pointerX, pointerY, panel) {
2986
+ const rect = panel.getBoundingClientRect();
2987
+ return {
2988
+ pointerX,
2989
+ pointerY,
2990
+ elX: rect.left,
2991
+ elY: rect.top
2992
+ };
2993
+ }
2994
+ function getPanelDragOffset(start, pointerX, pointerY) {
2995
+ return {
2996
+ x: start.elX + pointerX - start.pointerX,
2997
+ y: start.elY + pointerY - start.pointerY
2998
+ };
2999
+ }
3000
+ function hasPanelDragMoved(start, pointerX, pointerY) {
3001
+ const dx = pointerX - start.pointerX;
3002
+ const dy = pointerY - start.pointerY;
3003
+ return Math.hypot(dx, dy) >= PANEL_DRAG_THRESHOLD;
3004
+ }
3005
+ function getPanelOriginX(position, offset, viewportWidth = typeof window !== "undefined" ? window.innerWidth : void 0) {
3006
+ if (offset && viewportWidth) {
3007
+ return offset.x + COLLAPSED_PANEL_SIZE / 2 < viewportWidth / 2 ? "left" : "right";
3008
+ }
3009
+ return position.endsWith("left") ? "left" : "right";
3010
+ }
3011
+ function blockPanelDragClick(handle) {
3012
+ const blocker = (event) => {
3013
+ event.preventDefault();
3014
+ event.stopImmediatePropagation();
3015
+ event.stopPropagation();
3016
+ };
3017
+ handle.addEventListener("click", blocker, { capture: true, once: true });
3018
+ window.setTimeout(() => {
3019
+ handle.removeEventListener("click", blocker, true);
3020
+ }, 0);
3021
+ }
3022
+
2715
3023
  // src/vue/components/DialRoot.ts
2716
3024
  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;
2717
3025
  var DialRoot = defineComponent15({
@@ -2738,31 +3046,162 @@ var DialRoot = defineComponent15({
2738
3046
  default: isDevDefault
2739
3047
  }
2740
3048
  },
2741
- setup(props) {
3049
+ emits: ["openChange"],
3050
+ setup(props, { emit }) {
2742
3051
  const panels = ref13([]);
2743
3052
  const mounted = ref13(false);
3053
+ const panelRef = ref13(null);
3054
+ const dragOffset = ref13(null);
3055
+ const activePosition = ref13(props.position);
2744
3056
  let unsubscribe;
3057
+ let observer;
3058
+ let lastDragOffset = null;
3059
+ let dragging = false;
3060
+ let dragStart = null;
3061
+ let didDrag = false;
3062
+ let dragTarget = null;
3063
+ let panelOpenStates = /* @__PURE__ */ new Map();
3064
+ let rootOpen;
3065
+ const syncPanelOpenStates = () => {
3066
+ const fallbackOpen = props.mode === "inline" || props.defaultOpen;
3067
+ const nextStates = /* @__PURE__ */ new Map();
3068
+ for (const panel of panels.value) {
3069
+ nextStates.set(panel.id, panelOpenStates.get(panel.id) ?? fallbackOpen);
3070
+ }
3071
+ panelOpenStates = nextStates;
3072
+ rootOpen = Array.from(nextStates.values()).some(Boolean);
3073
+ };
3074
+ const connectObserver = () => {
3075
+ if (observer || props.mode === "inline" || !panelRef.value) return;
3076
+ observer = new MutationObserver(() => {
3077
+ const inners = panelRef.value?.querySelectorAll(".dialkit-panel-inner");
3078
+ if (!inners || inners.length === 0) return;
3079
+ const collapsed = Array.from(inners).every((el) => el.getAttribute("data-collapsed") === "true");
3080
+ const currentDragOffset = dragOffset.value;
3081
+ if (!collapsed) {
3082
+ if (currentDragOffset) {
3083
+ lastDragOffset = currentDragOffset;
3084
+ const bubbleCenterX = currentDragOffset.x + 21;
3085
+ activePosition.value = bubbleCenterX < window.innerWidth / 2 ? "top-left" : "top-right";
3086
+ } else {
3087
+ activePosition.value = props.position;
3088
+ }
3089
+ dragOffset.value = null;
3090
+ } else if (currentDragOffset) {
3091
+ lastDragOffset = currentDragOffset;
3092
+ } else if (lastDragOffset) {
3093
+ dragOffset.value = lastDragOffset;
3094
+ }
3095
+ });
3096
+ observer.observe(panelRef.value, { subtree: true, attributes: true, attributeFilter: ["data-collapsed"] });
3097
+ };
3098
+ const handlePointerDown = (event) => {
3099
+ const panel = panelRef.value;
3100
+ const handle = getPanelDragHandle(event.target, panel);
3101
+ if (!panel || !handle) return;
3102
+ dragTarget = handle;
3103
+ dragStart = getPanelDragStart(event.clientX, event.clientY, panel);
3104
+ didDrag = false;
3105
+ dragging = true;
3106
+ handle.setPointerCapture(event.pointerId);
3107
+ };
3108
+ const handlePointerMove = (event) => {
3109
+ if (!dragging || !dragStart) return;
3110
+ if (!didDrag && !hasPanelDragMoved(dragStart, event.clientX, event.clientY)) return;
3111
+ didDrag = true;
3112
+ dragOffset.value = getPanelDragOffset(dragStart, event.clientX, event.clientY);
3113
+ };
3114
+ const handlePointerUp = (event) => {
3115
+ if (!dragging) return;
3116
+ dragging = false;
3117
+ dragStart = null;
3118
+ const handle = dragTarget;
3119
+ if (handle?.hasPointerCapture(event.pointerId)) {
3120
+ handle.releasePointerCapture(event.pointerId);
3121
+ }
3122
+ if (didDrag) {
3123
+ event.stopPropagation();
3124
+ if (handle) {
3125
+ blockPanelDragClick(handle);
3126
+ }
3127
+ }
3128
+ dragTarget = null;
3129
+ };
3130
+ const handlePanelOpenChange = (panelId, open) => {
3131
+ panelOpenStates.set(panelId, open);
3132
+ const fallbackOpen = props.mode === "inline" || props.defaultOpen;
3133
+ const nextRootOpen = panels.value.some((panel) => panelOpenStates.get(panel.id) ?? fallbackOpen);
3134
+ if (rootOpen === nextRootOpen) return;
3135
+ rootOpen = nextRootOpen;
3136
+ emit("openChange", nextRootOpen);
3137
+ };
3138
+ const handleRootOpenChange = (open) => {
3139
+ if (rootOpen === open) return;
3140
+ rootOpen = open;
3141
+ emit("openChange", open);
3142
+ };
3143
+ const getDragStyle = () => dragOffset.value ? {
3144
+ top: `${dragOffset.value.y}px`,
3145
+ left: `${dragOffset.value.x}px`,
3146
+ right: "auto",
3147
+ bottom: "auto"
3148
+ } : void 0;
3149
+ const originX = computed5(() => props.mode === "inline" ? void 0 : getPanelOriginX(activePosition.value, dragOffset.value));
2745
3150
  onMounted10(() => {
2746
3151
  mounted.value = true;
2747
3152
  panels.value = DialStore.getPanels();
3153
+ syncPanelOpenStates();
2748
3154
  unsubscribe = DialStore.subscribeGlobal(() => {
2749
3155
  panels.value = DialStore.getPanels();
3156
+ syncPanelOpenStates();
2750
3157
  });
3158
+ nextTick3(connectObserver);
2751
3159
  });
2752
3160
  onUnmounted9(() => {
2753
3161
  unsubscribe?.();
3162
+ observer?.disconnect();
2754
3163
  });
2755
3164
  const renderContent = () => h15(ShortcutListener, null, {
2756
3165
  default: () => h15("div", { class: "dialkit-root", "data-mode": props.mode, "data-theme": props.theme }, [
2757
3166
  h15("div", {
3167
+ ref: (el) => {
3168
+ panelRef.value = el;
3169
+ connectObserver();
3170
+ },
2758
3171
  class: "dialkit-panel",
2759
- "data-position": props.mode === "inline" ? void 0 : props.position,
2760
- "data-mode": props.mode
2761
- }, panels.value.map((panel) => h15(Panel, {
3172
+ "data-position": props.mode === "inline" ? void 0 : dragOffset.value ? void 0 : activePosition.value,
3173
+ "data-origin-x": originX.value,
3174
+ "data-mode": props.mode,
3175
+ "data-multiple": panels.value.length > 1 ? "true" : void 0,
3176
+ style: getDragStyle(),
3177
+ onPointerdown: props.mode === "inline" ? void 0 : handlePointerDown,
3178
+ onPointermove: props.mode === "inline" ? void 0 : handlePointerMove,
3179
+ onPointerup: props.mode === "inline" ? void 0 : handlePointerUp,
3180
+ onPointercancel: props.mode === "inline" ? void 0 : handlePointerUp
3181
+ }, panels.value.length > 1 ? [
3182
+ h15("div", { class: "dialkit-panel-wrapper" }, [
3183
+ h15(Folder, {
3184
+ title: "DialKit",
3185
+ defaultOpen: props.mode === "inline" || props.defaultOpen,
3186
+ isRoot: true,
3187
+ inline: props.mode === "inline",
3188
+ onOpenChange: handleRootOpenChange,
3189
+ panelHeightOffset: 2
3190
+ }, {
3191
+ default: () => panels.value.map((panel) => h15(Panel, {
3192
+ key: panel.id,
3193
+ panel,
3194
+ defaultOpen: true,
3195
+ variant: "section"
3196
+ }))
3197
+ })
3198
+ ])
3199
+ ] : panels.value.map((panel) => h15(Panel, {
2762
3200
  key: panel.id,
2763
3201
  panel,
2764
3202
  defaultOpen: props.mode === "inline" || props.defaultOpen,
2765
- inline: props.mode === "inline"
3203
+ inline: props.mode === "inline",
3204
+ onOpenChange: (open) => handlePanelOpenChange(panel.id, open)
2766
3205
  })))
2767
3206
  ])
2768
3207
  });
@@ -3008,6 +3447,7 @@ export {
3008
3447
  Toggle,
3009
3448
  TransitionControl,
3010
3449
  useDialKit,
3450
+ useDialKitController,
3011
3451
  useShortcutContext,
3012
3452
  vDialKit
3013
3453
  };