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
@@ -32,7 +32,8 @@ __export(solid_exports, {
32
32
  SpringVisualization: () => SpringVisualization,
33
33
  TextControl: () => TextControl,
34
34
  Toggle: () => Toggle,
35
- createDialKit: () => createDialKit
35
+ createDialKit: () => createDialKit,
36
+ createDialKitController: () => createDialKitController
36
37
  });
37
38
  module.exports = __toCommonJS(solid_exports);
38
39
 
@@ -41,6 +42,89 @@ var import_solid_js = require("solid-js");
41
42
 
42
43
  // src/store/DialStore.ts
43
44
  var EMPTY_VALUES = Object.freeze({});
45
+ function resolveDialValues(config, flatValues) {
46
+ return resolveConfigValues(config, flatValues, "");
47
+ }
48
+ function flattenDialValueUpdates(config, updates) {
49
+ const values = {};
50
+ if (typeof updates === "object" && updates !== null) {
51
+ flattenConfigUpdates(config, updates, "", values);
52
+ }
53
+ return values;
54
+ }
55
+ function resolveConfigValues(config, flatValues, prefix) {
56
+ const result = {};
57
+ for (const [key, configValue] of Object.entries(config)) {
58
+ if (key === "_collapsed") continue;
59
+ const path = prefix ? `${prefix}.${key}` : key;
60
+ if (Array.isArray(configValue) && configValue.length <= 4 && typeof configValue[0] === "number") {
61
+ result[key] = flatValues[path] ?? configValue[0];
62
+ } else if (typeof configValue === "number" || typeof configValue === "boolean" || typeof configValue === "string") {
63
+ result[key] = flatValues[path] ?? configValue;
64
+ } else if (isSpringConfigValue(configValue) || isEasingConfigValue(configValue)) {
65
+ result[key] = flatValues[path] ?? configValue;
66
+ } else if (isActionConfigValue(configValue)) {
67
+ result[key] = flatValues[path] ?? configValue;
68
+ } else if (isSelectConfigValue(configValue)) {
69
+ const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
70
+ result[key] = flatValues[path] ?? defaultValue;
71
+ } else if (isColorConfigValue(configValue)) {
72
+ result[key] = flatValues[path] ?? configValue.default ?? "#000000";
73
+ } else if (isTextConfigValue(configValue)) {
74
+ result[key] = flatValues[path] ?? configValue.default ?? "";
75
+ } else if (typeof configValue === "object" && configValue !== null) {
76
+ result[key] = resolveConfigValues(configValue, flatValues, path);
77
+ }
78
+ }
79
+ return result;
80
+ }
81
+ function flattenConfigUpdates(config, updates, prefix, values) {
82
+ for (const [key, configValue] of Object.entries(config)) {
83
+ if (key === "_collapsed" || !(key in updates)) continue;
84
+ const nextValue = updates[key];
85
+ if (nextValue === void 0) continue;
86
+ const path = prefix ? `${prefix}.${key}` : key;
87
+ if (isActionConfigValue(configValue)) {
88
+ continue;
89
+ }
90
+ if (isLeafConfigValue(configValue)) {
91
+ values[path] = nextValue;
92
+ continue;
93
+ }
94
+ if (typeof configValue === "object" && configValue !== null && typeof nextValue === "object" && nextValue !== null && !Array.isArray(nextValue)) {
95
+ flattenConfigUpdates(configValue, nextValue, path, values);
96
+ }
97
+ }
98
+ }
99
+ function isLeafConfigValue(value) {
100
+ 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);
101
+ }
102
+ function hasType(value, type) {
103
+ return typeof value === "object" && value !== null && "type" in value && value.type === type;
104
+ }
105
+ function isSpringConfigValue(value) {
106
+ return hasType(value, "spring");
107
+ }
108
+ function isEasingConfigValue(value) {
109
+ return hasType(value, "easing");
110
+ }
111
+ function isActionConfigValue(value) {
112
+ return hasType(value, "action");
113
+ }
114
+ function isSelectConfigValue(value) {
115
+ return hasType(value, "select") && "options" in value && Array.isArray(value.options);
116
+ }
117
+ function isColorConfigValue(value) {
118
+ return hasType(value, "color");
119
+ }
120
+ function isTextConfigValue(value) {
121
+ return hasType(value, "text");
122
+ }
123
+ function getFirstOptionValue(options) {
124
+ const first = options[0];
125
+ if (first === void 0) return "";
126
+ return typeof first === "string" ? first : first.value;
127
+ }
44
128
  var DialStoreClass = class {
45
129
  constructor() {
46
130
  this.panels = /* @__PURE__ */ new Map();
@@ -51,87 +135,138 @@ var DialStoreClass = class {
51
135
  this.presets = /* @__PURE__ */ new Map();
52
136
  this.activePreset = /* @__PURE__ */ new Map();
53
137
  this.baseValues = /* @__PURE__ */ new Map();
138
+ this.defaultValues = /* @__PURE__ */ new Map();
139
+ this.registrationCounts = /* @__PURE__ */ new Map();
140
+ this.retainedPanels = /* @__PURE__ */ new Set();
141
+ this.persistConfigs = /* @__PURE__ */ new Map();
54
142
  }
55
- registerPanel(id, name, config, shortcuts) {
143
+ registerPanel(id, name, config, shortcuts, options = {}) {
144
+ this.configurePanelRetention(id, options);
145
+ this.registrationCounts.set(id, (this.registrationCounts.get(id) ?? 0) + 1);
56
146
  const controls = this.parseConfig(config, "", shortcuts);
57
- const values = this.flattenValues(config, "");
58
- this.initTransitionModes(config, "", values);
147
+ const controlsByPath = this.mapControlsByPath(controls);
148
+ const defaultValues = this.flattenValues(config, "");
149
+ this.initTransitionModes(config, "", defaultValues);
150
+ const persisted = this.loadPersistedPanel(id);
151
+ const previousValues = this.panels.get(id)?.values ?? this.snapshots.get(id) ?? persisted?.values ?? {};
152
+ const values = this.reconcileValues(defaultValues, previousValues, controlsByPath);
153
+ const previousBaseValues = this.baseValues.get(id) ?? persisted?.baseValues ?? persisted?.values ?? {};
154
+ const baseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
59
155
  this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {} });
60
156
  this.snapshots.set(id, { ...values });
61
- this.baseValues.set(id, { ...values });
157
+ this.baseValues.set(id, baseValues);
158
+ this.defaultValues.set(id, { ...defaultValues });
159
+ const existingPresets = this.presets.get(id) ?? persisted?.presets;
160
+ if (existingPresets) {
161
+ this.presets.set(id, this.reconcilePresets(existingPresets, defaultValues, controlsByPath));
162
+ }
163
+ if (!this.activePreset.has(id) && persisted?.activePresetId !== void 0) {
164
+ this.activePreset.set(id, persisted.activePresetId);
165
+ }
166
+ this.persistPanel(id);
167
+ this.notify(id);
62
168
  this.notifyGlobal();
63
169
  }
64
- updatePanel(id, name, config, shortcuts) {
170
+ updatePanel(id, name, config, shortcuts, options = {}) {
171
+ this.configurePanelRetention(id, options);
65
172
  const existing = this.panels.get(id);
66
173
  if (!existing) {
67
- this.registerPanel(id, name, config, shortcuts);
174
+ this.registerPanel(id, name, config, shortcuts, options);
68
175
  return;
69
176
  }
70
177
  const controls = this.parseConfig(config, "", shortcuts);
71
178
  const controlsByPath = this.mapControlsByPath(controls);
72
179
  const defaultValues = this.flattenValues(config, "");
73
- const nextValues = {};
74
- for (const [path, defaultValue] of Object.entries(defaultValues)) {
75
- nextValues[path] = this.normalizePreservedValue(
76
- existing.values[path],
77
- defaultValue,
78
- controlsByPath.get(path)
79
- );
80
- }
81
- this.initTransitionModes(config, "", nextValues);
82
- for (const [path, mode] of Object.entries(existing.values)) {
83
- if (!path.endsWith(".__mode")) {
84
- continue;
85
- }
86
- const transitionPath = path.slice(0, -"__mode".length - 1);
87
- const transitionControl = controlsByPath.get(transitionPath);
88
- if (transitionControl?.type === "transition") {
89
- nextValues[path] = mode;
90
- }
91
- }
180
+ this.initTransitionModes(config, "", defaultValues);
181
+ const nextValues = this.reconcileValues(defaultValues, existing.values, controlsByPath);
92
182
  const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts };
93
183
  this.panels.set(id, nextPanel);
94
184
  this.snapshots.set(id, { ...nextValues });
95
185
  const previousBaseValues = this.baseValues.get(id) ?? {};
96
- const nextBaseValues = {};
97
- for (const [path, defaultValue] of Object.entries(defaultValues)) {
98
- nextBaseValues[path] = this.normalizePreservedValue(
99
- previousBaseValues[path],
100
- defaultValue,
101
- controlsByPath.get(path)
102
- );
103
- }
186
+ const nextBaseValues = this.reconcileValues(defaultValues, previousBaseValues, controlsByPath);
104
187
  for (const [path, value] of Object.entries(nextValues)) {
105
188
  if (path.endsWith(".__mode")) {
106
189
  nextBaseValues[path] = value;
107
190
  }
108
191
  }
109
192
  this.baseValues.set(id, nextBaseValues);
193
+ this.defaultValues.set(id, { ...defaultValues });
194
+ this.presets.set(id, this.reconcilePresets(this.presets.get(id) ?? [], defaultValues, controlsByPath));
195
+ this.persistPanel(id);
110
196
  this.notify(id);
111
197
  this.notifyGlobal();
112
198
  }
113
199
  unregisterPanel(id) {
200
+ const nextCount = (this.registrationCounts.get(id) ?? 1) - 1;
201
+ if (nextCount > 0) {
202
+ this.registrationCounts.set(id, nextCount);
203
+ return;
204
+ }
205
+ this.registrationCounts.delete(id);
114
206
  this.panels.delete(id);
115
207
  this.listeners.delete(id);
116
- this.snapshots.delete(id);
117
208
  this.actionListeners.delete(id);
118
- this.baseValues.delete(id);
209
+ if (!this.retainedPanels.has(id)) {
210
+ this.snapshots.delete(id);
211
+ this.baseValues.delete(id);
212
+ this.defaultValues.delete(id);
213
+ this.presets.delete(id);
214
+ this.activePreset.delete(id);
215
+ this.persistConfigs.delete(id);
216
+ }
119
217
  this.notifyGlobal();
120
218
  }
121
219
  updateValue(panelId, path, value) {
220
+ this.updateValues(panelId, { [path]: value });
221
+ }
222
+ updateValues(panelId, updates) {
122
223
  const panel = this.panels.get(panelId);
123
224
  if (!panel) return;
124
- panel.values[path] = value;
225
+ const validUpdates = {};
226
+ for (const [path, value] of Object.entries(updates)) {
227
+ if (!Object.prototype.hasOwnProperty.call(panel.values, path)) {
228
+ continue;
229
+ }
230
+ const control = this.findControlByPath(panel.controls, path);
231
+ if (control?.type === "action") {
232
+ continue;
233
+ }
234
+ panel.values[path] = value;
235
+ validUpdates[path] = value;
236
+ }
237
+ if (Object.keys(validUpdates).length === 0) {
238
+ return;
239
+ }
125
240
  const activeId = this.activePreset.get(panelId);
126
241
  if (activeId) {
127
242
  const presets = this.presets.get(panelId) ?? [];
128
243
  const preset = presets.find((p) => p.id === activeId);
129
- if (preset) preset.values[path] = value;
244
+ if (preset) {
245
+ for (const [path, value] of Object.entries(validUpdates)) {
246
+ preset.values[path] = value;
247
+ }
248
+ }
130
249
  } else {
131
250
  const base = this.baseValues.get(panelId);
132
- if (base) base[path] = value;
251
+ if (base) {
252
+ for (const [path, value] of Object.entries(validUpdates)) {
253
+ base[path] = value;
254
+ }
255
+ }
133
256
  }
134
257
  this.snapshots.set(panelId, { ...panel.values });
258
+ this.persistPanel(panelId);
259
+ this.notify(panelId);
260
+ }
261
+ resetValues(panelId) {
262
+ const panel = this.panels.get(panelId);
263
+ const defaults = this.defaultValues.get(panelId);
264
+ if (!panel || !defaults) return;
265
+ panel.values = { ...defaults };
266
+ this.snapshots.set(panelId, { ...panel.values });
267
+ this.baseValues.set(panelId, { ...defaults });
268
+ this.activePreset.set(panelId, null);
269
+ this.persistPanel(panelId);
135
270
  this.notify(panelId);
136
271
  }
137
272
  updateSpringMode(panelId, path, mode) {
@@ -147,6 +282,7 @@ var DialStoreClass = class {
147
282
  if (!panel) return;
148
283
  panel.values[`${path}.__mode`] = mode;
149
284
  this.snapshots.set(panelId, { ...panel.values });
285
+ this.persistPanel(panelId);
150
286
  this.notify(panelId);
151
287
  }
152
288
  getTransitionMode(panelId, path) {
@@ -205,6 +341,7 @@ var DialStoreClass = class {
205
341
  this.presets.set(panelId, [...existing, preset]);
206
342
  this.activePreset.set(panelId, id);
207
343
  this.snapshots.set(panelId, { ...panel.values });
344
+ this.persistPanel(panelId);
208
345
  this.notify(panelId);
209
346
  return id;
210
347
  }
@@ -217,6 +354,7 @@ var DialStoreClass = class {
217
354
  panel.values = { ...preset.values };
218
355
  this.snapshots.set(panelId, { ...panel.values });
219
356
  this.activePreset.set(panelId, presetId);
357
+ this.persistPanel(panelId);
220
358
  this.notify(panelId);
221
359
  }
222
360
  deletePreset(panelId, presetId) {
@@ -229,6 +367,7 @@ var DialStoreClass = class {
229
367
  if (panel) {
230
368
  this.snapshots.set(panelId, { ...panel.values });
231
369
  }
370
+ this.persistPanel(panelId);
232
371
  this.notify(panelId);
233
372
  }
234
373
  getPresets(panelId) {
@@ -245,6 +384,7 @@ var DialStoreClass = class {
245
384
  this.snapshots.set(panelId, { ...panel.values });
246
385
  }
247
386
  this.activePreset.set(panelId, null);
387
+ this.persistPanel(panelId);
248
388
  this.notify(panelId);
249
389
  }
250
390
  resolveShortcutTarget(key, modifier) {
@@ -275,6 +415,94 @@ var DialStoreClass = class {
275
415
  }
276
416
  return results;
277
417
  }
418
+ configurePanelRetention(id, options) {
419
+ if (options.retainOnUnmount) {
420
+ this.retainedPanels.add(id);
421
+ }
422
+ const persistConfig = this.normalizePersistConfig(id, options.persist);
423
+ if (persistConfig) {
424
+ this.persistConfigs.set(id, persistConfig);
425
+ this.retainedPanels.add(id);
426
+ }
427
+ }
428
+ reconcileValues(defaultValues, previousValues, controlsByPath) {
429
+ const nextValues = {};
430
+ for (const [path, defaultValue] of Object.entries(defaultValues)) {
431
+ if (path.endsWith(".__mode")) {
432
+ const transitionPath = path.slice(0, -".__mode".length);
433
+ const transitionControl = controlsByPath.get(transitionPath);
434
+ nextValues[path] = transitionControl?.type === "transition" && previousValues[path] !== void 0 ? previousValues[path] : defaultValue;
435
+ continue;
436
+ }
437
+ nextValues[path] = this.normalizePreservedValue(
438
+ previousValues[path],
439
+ defaultValue,
440
+ controlsByPath.get(path)
441
+ );
442
+ }
443
+ return nextValues;
444
+ }
445
+ reconcilePresets(presets, defaultValues, controlsByPath) {
446
+ return presets.map((preset) => ({
447
+ ...preset,
448
+ values: this.reconcileValues(defaultValues, preset.values, controlsByPath)
449
+ }));
450
+ }
451
+ normalizePersistConfig(id, persist) {
452
+ if (!persist) return null;
453
+ const options = typeof persist === "object" ? persist : {};
454
+ return {
455
+ key: options.key ?? `dialkit:${id}`,
456
+ storage: options.storage ?? "localStorage",
457
+ presets: options.presets ?? true
458
+ };
459
+ }
460
+ loadPersistedPanel(id) {
461
+ const config = this.persistConfigs.get(id);
462
+ if (!config) return null;
463
+ const storage = this.getStorage(config.storage);
464
+ if (!storage) return null;
465
+ try {
466
+ const raw = storage.getItem(config.key);
467
+ if (!raw) return null;
468
+ const parsed = JSON.parse(raw);
469
+ if (parsed?.version !== 1 || typeof parsed !== "object") return null;
470
+ return parsed;
471
+ } catch {
472
+ return null;
473
+ }
474
+ }
475
+ persistPanel(id) {
476
+ const config = this.persistConfigs.get(id);
477
+ if (!config) return;
478
+ const storage = this.getStorage(config.storage);
479
+ if (!storage) return;
480
+ const values = this.snapshots.get(id) ?? this.panels.get(id)?.values;
481
+ if (!values) return;
482
+ const state = {
483
+ version: 1,
484
+ values,
485
+ baseValues: this.baseValues.get(id) ?? values,
486
+ activePresetId: this.activePreset.get(id) ?? null
487
+ };
488
+ if (config.presets) {
489
+ state.presets = this.presets.get(id) ?? [];
490
+ }
491
+ try {
492
+ storage.setItem(config.key, JSON.stringify(state));
493
+ } catch {
494
+ }
495
+ }
496
+ getStorage(kind) {
497
+ if (typeof globalThis === "undefined" || !("window" in globalThis)) {
498
+ return null;
499
+ }
500
+ try {
501
+ return kind === "sessionStorage" ? globalThis.window?.sessionStorage ?? null : globalThis.window?.localStorage ?? null;
502
+ } catch {
503
+ return null;
504
+ }
505
+ }
278
506
  findControlByPath(controls, path) {
279
507
  for (const control of controls) {
280
508
  if (control.path === path) return control;
@@ -503,16 +731,23 @@ var DialStore = new DialStoreClass();
503
731
 
504
732
  // src/solid/createDialKit.ts
505
733
  function createDialKit(name, config, options) {
734
+ return createDialKitController(name, config, options).values;
735
+ }
736
+ function createDialKitController(name, config, options) {
506
737
  const id = (0, import_solid_js.createUniqueId)();
507
- const panelId = `${name}-${id}`;
508
- const [values, setValues] = (0, import_solid_js.createSignal)(
738
+ const hasStableId = options?.id !== void 0;
739
+ const panelId = options?.id ?? `${name}-${id}`;
740
+ const [flatValues, setFlatValues] = (0, import_solid_js.createSignal)(
509
741
  DialStore.getValues(panelId)
510
742
  );
511
743
  (0, import_solid_js.onMount)(() => {
512
- DialStore.registerPanel(panelId, name, config, options?.shortcuts);
513
- setValues(DialStore.getValues(panelId));
744
+ DialStore.registerPanel(panelId, name, config, options?.shortcuts, {
745
+ retainOnUnmount: hasStableId,
746
+ persist: options?.persist
747
+ });
748
+ setFlatValues(DialStore.getValues(panelId));
514
749
  const unsubValues = DialStore.subscribe(panelId, () => {
515
- setValues(DialStore.getValues(panelId));
750
+ setFlatValues(DialStore.getValues(panelId));
516
751
  });
517
752
  const unsubActions = options?.onAction ? DialStore.subscribeActions(panelId, options.onAction) : void 0;
518
753
  (0, import_solid_js.onCleanup)(() => {
@@ -521,55 +756,22 @@ function createDialKit(name, config, options) {
521
756
  DialStore.unregisterPanel(panelId);
522
757
  });
523
758
  });
524
- return (0, import_solid_js.createMemo)(() => buildResolvedValues(config, values(), ""));
525
- }
526
- function buildResolvedValues(config, flatValues, prefix) {
527
- const result = {};
528
- for (const [key, configValue] of Object.entries(config)) {
529
- if (key === "_collapsed") continue;
530
- const path = prefix ? `${prefix}.${key}` : key;
531
- if (Array.isArray(configValue) && configValue.length <= 4 && typeof configValue[0] === "number") {
532
- result[key] = flatValues[path] ?? configValue[0];
533
- } else if (typeof configValue === "number" || typeof configValue === "boolean" || typeof configValue === "string") {
534
- result[key] = flatValues[path] ?? configValue;
535
- } else if (isSpringConfig(configValue)) {
536
- result[key] = flatValues[path] ?? configValue;
537
- } else if (isActionConfig(configValue)) {
538
- result[key] = flatValues[path] ?? configValue;
539
- } else if (isSelectConfig(configValue)) {
540
- const defaultValue = configValue.default ?? getFirstOptionValue(configValue.options);
541
- result[key] = flatValues[path] ?? defaultValue;
542
- } else if (isColorConfig(configValue)) {
543
- result[key] = flatValues[path] ?? configValue.default ?? "#000000";
544
- } else if (isTextConfig(configValue)) {
545
- result[key] = flatValues[path] ?? configValue.default ?? "";
546
- } else if (typeof configValue === "object" && configValue !== null) {
547
- result[key] = buildResolvedValues(configValue, flatValues, path);
759
+ const values = (0, import_solid_js.createMemo)(() => resolveDialValues(config, flatValues()));
760
+ return {
761
+ values,
762
+ setValue(path, value) {
763
+ DialStore.updateValue(panelId, path, value);
764
+ },
765
+ setValues(nextValues) {
766
+ DialStore.updateValues(panelId, flattenDialValueUpdates(config, nextValues));
767
+ },
768
+ resetValues() {
769
+ DialStore.resetValues(panelId);
770
+ },
771
+ getValues() {
772
+ return resolveDialValues(config, DialStore.getValues(panelId));
548
773
  }
549
- }
550
- return result;
551
- }
552
- function hasType(value, type) {
553
- return typeof value === "object" && value !== null && "type" in value && value.type === type;
554
- }
555
- function isSpringConfig(value) {
556
- return hasType(value, "spring");
557
- }
558
- function isActionConfig(value) {
559
- return hasType(value, "action");
560
- }
561
- function isSelectConfig(value) {
562
- return hasType(value, "select") && "options" in value && Array.isArray(value.options);
563
- }
564
- function isColorConfig(value) {
565
- return hasType(value, "color");
566
- }
567
- function isTextConfig(value) {
568
- return hasType(value, "text");
569
- }
570
- function getFirstOptionValue(options) {
571
- const first = options[0];
572
- return typeof first === "string" ? first : first.value;
774
+ };
573
775
  }
574
776
 
575
777
  // src/solid/components/DialRoot.tsx
@@ -579,8 +781,12 @@ var import_web86 = require("solid-js/web");
579
781
  var import_web87 = require("solid-js/web");
580
782
  var import_web88 = require("solid-js/web");
581
783
  var import_web89 = require("solid-js/web");
582
- var import_solid_js13 = require("solid-js");
583
784
  var import_web90 = require("solid-js/web");
785
+ var import_web91 = require("solid-js/web");
786
+ var import_web92 = require("solid-js/web");
787
+ var import_web93 = require("solid-js/web");
788
+ var import_solid_js13 = require("solid-js");
789
+ var import_web94 = require("solid-js/web");
584
790
 
585
791
  // src/solid/components/ShortcutListener.tsx
586
792
  var import_web = require("solid-js/web");
@@ -900,17 +1106,19 @@ function ShortcutListener(props) {
900
1106
  });
901
1107
  }
902
1108
 
903
- // src/solid/components/Panel.tsx
904
- var import_web76 = require("solid-js/web");
905
- var import_web77 = require("solid-js/web");
906
- var import_web78 = require("solid-js/web");
907
- var import_web79 = require("solid-js/web");
908
- var import_web80 = require("solid-js/web");
909
- var import_web81 = require("solid-js/web");
910
- var import_web82 = require("solid-js/web");
911
- var import_web83 = require("solid-js/web");
912
- var import_solid_js12 = require("solid-js");
913
- var import_motion5 = require("motion");
1109
+ // src/solid/components/Folder.tsx
1110
+ var import_web2 = require("solid-js/web");
1111
+ var import_web3 = require("solid-js/web");
1112
+ var import_web4 = require("solid-js/web");
1113
+ var import_web5 = require("solid-js/web");
1114
+ var import_web6 = require("solid-js/web");
1115
+ var import_web7 = require("solid-js/web");
1116
+ var import_web8 = require("solid-js/web");
1117
+ var import_web9 = require("solid-js/web");
1118
+ var import_web10 = require("solid-js/web");
1119
+ var import_web11 = require("solid-js/web");
1120
+ var import_solid_js3 = require("solid-js");
1121
+ var import_motion = require("motion");
914
1122
 
915
1123
  // src/icons.ts
916
1124
  var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
@@ -944,18 +1152,6 @@ var ICON_PANEL = {
944
1152
  };
945
1153
 
946
1154
  // src/solid/components/Folder.tsx
947
- var import_web2 = require("solid-js/web");
948
- var import_web3 = require("solid-js/web");
949
- var import_web4 = require("solid-js/web");
950
- var import_web5 = require("solid-js/web");
951
- var import_web6 = require("solid-js/web");
952
- var import_web7 = require("solid-js/web");
953
- var import_web8 = require("solid-js/web");
954
- var import_web9 = require("solid-js/web");
955
- var import_web10 = require("solid-js/web");
956
- var import_web11 = require("solid-js/web");
957
- var import_solid_js3 = require("solid-js");
958
- var import_motion = require("motion");
959
1155
  var _tmpl$ = /* @__PURE__ */ (0, import_web2.template)(`<div class=dialkit-panel-toolbar>`);
960
1156
  var _tmpl$2 = /* @__PURE__ */ (0, import_web2.template)(`<div class=dialkit-folder-content><div class=dialkit-folder-inner>`);
961
1157
  var _tmpl$3 = /* @__PURE__ */ (0, import_web2.template)(`<div><div><div class=dialkit-folder-header-top>`);
@@ -1093,17 +1289,17 @@ function Folder(props) {
1093
1289
  return () => _c$2() && (() => {
1094
1290
  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;
1095
1291
  (0, import_web7.effect)((_p$) => {
1096
- 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;
1097
- _v$3 !== _p$.e && (0, import_web4.setAttribute)(_el$10, "d", _p$.e = _v$3);
1098
- _v$4 !== _p$.t && (0, import_web4.setAttribute)(_el$11, "cx", _p$.t = _v$4);
1099
- _v$5 !== _p$.a && (0, import_web4.setAttribute)(_el$11, "cy", _p$.a = _v$5);
1100
- _v$6 !== _p$.o && (0, import_web4.setAttribute)(_el$11, "r", _p$.o = _v$6);
1101
- _v$7 !== _p$.i && (0, import_web4.setAttribute)(_el$12, "cx", _p$.i = _v$7);
1102
- _v$8 !== _p$.n && (0, import_web4.setAttribute)(_el$12, "cy", _p$.n = _v$8);
1103
- _v$9 !== _p$.s && (0, import_web4.setAttribute)(_el$12, "r", _p$.s = _v$9);
1104
- _v$0 !== _p$.h && (0, import_web4.setAttribute)(_el$13, "cx", _p$.h = _v$0);
1105
- _v$1 !== _p$.r && (0, import_web4.setAttribute)(_el$13, "cy", _p$.r = _v$1);
1106
- _v$10 !== _p$.d && (0, import_web4.setAttribute)(_el$13, "r", _p$.d = _v$10);
1292
+ 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;
1293
+ _v$4 !== _p$.e && (0, import_web4.setAttribute)(_el$10, "d", _p$.e = _v$4);
1294
+ _v$5 !== _p$.t && (0, import_web4.setAttribute)(_el$11, "cx", _p$.t = _v$5);
1295
+ _v$6 !== _p$.a && (0, import_web4.setAttribute)(_el$11, "cy", _p$.a = _v$6);
1296
+ _v$7 !== _p$.o && (0, import_web4.setAttribute)(_el$11, "r", _p$.o = _v$7);
1297
+ _v$8 !== _p$.i && (0, import_web4.setAttribute)(_el$12, "cx", _p$.i = _v$8);
1298
+ _v$9 !== _p$.n && (0, import_web4.setAttribute)(_el$12, "cy", _p$.n = _v$9);
1299
+ _v$0 !== _p$.s && (0, import_web4.setAttribute)(_el$12, "r", _p$.s = _v$0);
1300
+ _v$1 !== _p$.h && (0, import_web4.setAttribute)(_el$13, "cx", _p$.h = _v$1);
1301
+ _v$10 !== _p$.r && (0, import_web4.setAttribute)(_el$13, "cy", _p$.r = _v$10);
1302
+ _v$11 !== _p$.d && (0, import_web4.setAttribute)(_el$13, "r", _p$.d = _v$11);
1107
1303
  return _p$;
1108
1304
  }, {
1109
1305
  e: void 0,
@@ -1177,13 +1373,15 @@ function Folder(props) {
1177
1373
  }
1178
1374
  }), null);
1179
1375
  (0, import_web7.effect)((_p$) => {
1180
- var _v$ = `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`, _v$2 = `dialkit-folder-header ${props.isRoot ? "dialkit-panel-header" : ""}`;
1376
+ var _v$ = `dialkit-folder ${props.isRoot ? "dialkit-folder-root" : ""}`, _v$2 = String(isOpen()), _v$3 = `dialkit-folder-header ${props.isRoot ? "dialkit-panel-header" : ""}`;
1181
1377
  _v$ !== _p$.e && (0, import_web5.className)(_el$, _p$.e = _v$);
1182
- _v$2 !== _p$.t && (0, import_web5.className)(_el$2, _p$.t = _v$2);
1378
+ _v$2 !== _p$.t && (0, import_web4.setAttribute)(_el$, "data-open", _p$.t = _v$2);
1379
+ _v$3 !== _p$.a && (0, import_web5.className)(_el$2, _p$.a = _v$3);
1183
1380
  return _p$;
1184
1381
  }, {
1185
1382
  e: void 0,
1186
- t: void 0
1383
+ t: void 0,
1384
+ a: void 0
1187
1385
  });
1188
1386
  return _el$;
1189
1387
  })();
@@ -1211,7 +1409,7 @@ function Folder(props) {
1211
1409
  (0, import_solid_js3.createEffect)(() => {
1212
1410
  if (!panelRef) return;
1213
1411
  const open = isOpen();
1214
- const measuredOpenHeight = contentHeight() !== void 0 ? Math.min(contentHeight() + 10, windowHeight() - 32) : panelRef.getBoundingClientRect().height;
1412
+ const measuredOpenHeight = contentHeight() !== void 0 ? Math.min(contentHeight() + (props.panelHeightOffset ?? 10), windowHeight() - 32) : panelRef.getBoundingClientRect().height;
1215
1413
  const target = {
1216
1414
  width: open ? 280 : 42,
1217
1415
  height: open ? measuredOpenHeight : 42,
@@ -1308,6 +1506,18 @@ function Folder(props) {
1308
1506
  }
1309
1507
  (0, import_web3.delegateEvents)(["click", "pointerdown", "pointerup"]);
1310
1508
 
1509
+ // src/solid/components/Panel.tsx
1510
+ var import_web76 = require("solid-js/web");
1511
+ var import_web77 = require("solid-js/web");
1512
+ var import_web78 = require("solid-js/web");
1513
+ var import_web79 = require("solid-js/web");
1514
+ var import_web80 = require("solid-js/web");
1515
+ var import_web81 = require("solid-js/web");
1516
+ var import_web82 = require("solid-js/web");
1517
+ var import_web83 = require("solid-js/web");
1518
+ var import_solid_js12 = require("solid-js");
1519
+ var import_motion5 = require("motion");
1520
+
1311
1521
  // src/solid/components/Slider.tsx
1312
1522
  var import_web12 = require("solid-js/web");
1313
1523
  var import_web13 = require("solid-js/web");
@@ -2105,6 +2315,26 @@ var import_web56 = require("solid-js/web");
2105
2315
  var import_solid_js9 = require("solid-js");
2106
2316
  var import_web57 = require("solid-js/web");
2107
2317
  var import_motion3 = require("motion");
2318
+
2319
+ // src/dropdown-position.ts
2320
+ function getDropdownPosition(trigger, portalRoot, options = {}) {
2321
+ const { dropdownHeight = 0, gap = 4, allowAbove = true } = options;
2322
+ const triggerRect = trigger.getBoundingClientRect();
2323
+ const rootRect = portalRoot.getBoundingClientRect();
2324
+ const spaceBelow = window.innerHeight - triggerRect.bottom - gap;
2325
+ const above = allowAbove && spaceBelow < dropdownHeight && triggerRect.top > spaceBelow;
2326
+ return {
2327
+ top: above ? triggerRect.top - rootRect.top - dropdownHeight - gap : triggerRect.bottom - rootRect.top + gap,
2328
+ left: triggerRect.left - rootRect.left,
2329
+ width: triggerRect.width,
2330
+ above
2331
+ };
2332
+ }
2333
+ function getDialKitPortalRoot(trigger) {
2334
+ return trigger?.closest(".dialkit-root") ?? null;
2335
+ }
2336
+
2337
+ // src/solid/components/SelectControl.tsx
2108
2338
  var _tmpl$16 = /* @__PURE__ */ (0, import_web48.template)(`<div class=dialkit-select-dropdown>`);
2109
2339
  var _tmpl$26 = /* @__PURE__ */ (0, import_web48.template)(`<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>`);
2110
2340
  var _tmpl$35 = /* @__PURE__ */ (0, import_web48.template)(`<button class=dialkit-select-option>`);
@@ -2130,8 +2360,7 @@ function SelectControl(props) {
2130
2360
  const normalized = () => normalizeOptions(props.options);
2131
2361
  const selectedOption = () => normalized().find((o) => o.value === props.value);
2132
2362
  (0, import_solid_js9.onMount)(() => {
2133
- const root = triggerRef?.closest(".dialkit-root");
2134
- setPortalTarget(root ?? document.body);
2363
+ setPortalTarget(getDialKitPortalRoot(triggerRef) ?? document.body);
2135
2364
  if (chevronRef) {
2136
2365
  chevronRef.style.transform = `rotate(${isOpen() ? 180 : 0}deg)`;
2137
2366
  }
@@ -2153,17 +2382,12 @@ function SelectControl(props) {
2153
2382
  });
2154
2383
  });
2155
2384
  const updatePos = () => {
2156
- if (!triggerRef) return;
2157
- const rect = triggerRef.getBoundingClientRect();
2385
+ const root = portalTarget();
2386
+ if (!triggerRef || !root) return;
2158
2387
  const dropdownHeight = 8 + normalized().length * 36;
2159
- const spaceBelow = window.innerHeight - rect.bottom - 4;
2160
- const above = spaceBelow < dropdownHeight && rect.top > spaceBelow;
2161
- setPos({
2162
- top: above ? rect.top - 4 : rect.bottom + 4,
2163
- left: rect.left,
2164
- width: rect.width,
2165
- above
2166
- });
2388
+ setPos(getDropdownPosition(triggerRef, root, {
2389
+ dropdownHeight
2390
+ }));
2167
2391
  };
2168
2392
  const openDropdown = () => {
2169
2393
  closeAnim?.stop();
@@ -2217,16 +2441,11 @@ function SelectControl(props) {
2217
2441
  const p = pos();
2218
2442
  if (!p) return {};
2219
2443
  return {
2220
- position: "fixed",
2444
+ position: "absolute",
2221
2445
  left: `${p.left}px`,
2446
+ top: `${p.top}px`,
2222
2447
  width: `${p.width}px`,
2223
- ...p.above ? {
2224
- bottom: `${window.innerHeight - p.top}px`,
2225
- "transform-origin": "bottom"
2226
- } : {
2227
- top: `${p.top}px`,
2228
- "transform-origin": "top"
2229
- }
2448
+ "transform-origin": p.above ? "bottom" : "top"
2230
2449
  };
2231
2450
  };
2232
2451
  return (() => {
@@ -2400,7 +2619,7 @@ var import_web74 = require("solid-js/web");
2400
2619
  var import_solid_js11 = require("solid-js");
2401
2620
  var import_web75 = require("solid-js/web");
2402
2621
  var import_motion4 = require("motion");
2403
- var _tmpl$18 = /* @__PURE__ */ (0, import_web66.template)(`<div class="dialkit-root dialkit-preset-dropdown"style=position:fixed><div class=dialkit-preset-item><span class=dialkit-preset-name>Version 1`);
2622
+ var _tmpl$18 = /* @__PURE__ */ (0, import_web66.template)(`<div class="dialkit-root dialkit-preset-dropdown"style=position:absolute><div class=dialkit-preset-item><span class=dialkit-preset-name>Version 1`);
2404
2623
  var _tmpl$28 = /* @__PURE__ */ (0, import_web66.template)(`<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>`);
2405
2624
  var _tmpl$37 = /* @__PURE__ */ (0, import_web66.template)(`<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>`);
2406
2625
  function PresetManager(props) {
@@ -2420,8 +2639,7 @@ function PresetManager(props) {
2420
2639
  const hasPresets = () => props.presets.length > 0;
2421
2640
  const activePreset = () => props.presets.find((p) => p.id === props.activePresetId);
2422
2641
  (0, import_solid_js11.onMount)(() => {
2423
- const root = triggerRef?.closest(".dialkit-root");
2424
- setPortalTarget(root ?? document.body);
2642
+ setPortalTarget(getDialKitPortalRoot(triggerRef) ?? document.body);
2425
2643
  if (chevronRef) {
2426
2644
  chevronRef.style.transform = `rotate(${isOpen() ? 180 : 0}deg)`;
2427
2645
  chevronRef.style.opacity = String(hasPresets() ? 0.6 : 0.25);
@@ -2446,13 +2664,11 @@ function PresetManager(props) {
2446
2664
  });
2447
2665
  });
2448
2666
  const updatePos = () => {
2449
- const rect = triggerRef?.getBoundingClientRect();
2450
- if (!rect) return;
2451
- setPos({
2452
- top: rect.bottom + 4,
2453
- left: rect.left,
2454
- width: rect.width
2455
- });
2667
+ const root = portalTarget();
2668
+ if (!triggerRef || !root) return;
2669
+ setPos(getDropdownPosition(triggerRef, root, {
2670
+ allowAbove: false
2671
+ }));
2456
2672
  };
2457
2673
  const openDropdown = () => {
2458
2674
  if (!hasPresets()) return;
@@ -2624,7 +2840,8 @@ function PresetManager(props) {
2624
2840
  var _tmpl$19 = /* @__PURE__ */ (0, import_web76.template)(`<button class=dialkit-button>`);
2625
2841
  var _tmpl$29 = /* @__PURE__ */ (0, import_web76.template)(`<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>`);
2626
2842
  var _tmpl$38 = /* @__PURE__ */ (0, import_web76.template)(`<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`);
2627
- var _tmpl$43 = /* @__PURE__ */ (0, import_web76.template)(`<div class=dialkit-panel-wrapper>`);
2843
+ var _tmpl$43 = /* @__PURE__ */ (0, import_web76.template)(`<div class=dialkit-panel-section-toolbar>`);
2844
+ var _tmpl$52 = /* @__PURE__ */ (0, import_web76.template)(`<div class=dialkit-panel-wrapper>`);
2628
2845
  function Panel(props) {
2629
2846
  const [copied, setCopied] = (0, import_solid_js12.createSignal)(false);
2630
2847
  const [isPanelOpen, setIsPanelOpen] = (0, import_solid_js12.createSignal)(props.defaultOpen ?? true);
@@ -2739,6 +2956,10 @@ Apply these values as the new defaults in the createDialKit call.`;
2739
2956
  scale: 1
2740
2957
  }, tapTransition);
2741
2958
  };
2959
+ const handleOpenChange = (open) => {
2960
+ setIsPanelOpen(open);
2961
+ props.onOpenChange?.(open);
2962
+ };
2742
2963
  const renderControl = (control) => {
2743
2964
  const value = () => values()[control.path];
2744
2965
  switch (control.type) {
@@ -2932,9 +3153,28 @@ Apply these values as the new defaults in the createDialKit call.`;
2932
3153
  });
2933
3154
  return _el$9;
2934
3155
  })()];
3156
+ if (props.variant === "section") {
3157
+ return (0, import_web82.createComponent)(Folder, {
3158
+ get title() {
3159
+ return props.panel.name;
3160
+ },
3161
+ get defaultOpen() {
3162
+ return props.defaultOpen ?? true;
3163
+ },
3164
+ onOpenChange: handleOpenChange,
3165
+ get children() {
3166
+ return [(() => {
3167
+ var _el$17 = _tmpl$43();
3168
+ _el$17.$$click = (e) => e.stopPropagation();
3169
+ (0, import_web81.insert)(_el$17, toolbar);
3170
+ return _el$17;
3171
+ })(), (0, import_web83.memo)(() => renderControls())];
3172
+ }
3173
+ });
3174
+ }
2935
3175
  return (() => {
2936
- var _el$17 = _tmpl$43();
2937
- (0, import_web81.insert)(_el$17, (0, import_web82.createComponent)(Folder, {
3176
+ var _el$18 = _tmpl$52();
3177
+ (0, import_web81.insert)(_el$18, (0, import_web82.createComponent)(Folder, {
2938
3178
  get title() {
2939
3179
  return props.panel.name;
2940
3180
  },
@@ -2945,26 +3185,101 @@ Apply these values as the new defaults in the createDialKit call.`;
2945
3185
  get inline() {
2946
3186
  return props.inline ?? false;
2947
3187
  },
2948
- onOpenChange: setIsPanelOpen,
3188
+ onOpenChange: handleOpenChange,
2949
3189
  toolbar,
2950
3190
  get children() {
2951
3191
  return renderControls();
2952
3192
  }
2953
3193
  }));
2954
- return _el$17;
3194
+ return _el$18;
2955
3195
  })();
2956
3196
  }
2957
3197
  (0, import_web77.delegateEvents)(["click", "pointerdown", "pointerup"]);
2958
3198
 
3199
+ // src/panel-drag.ts
3200
+ var PANEL_DRAG_THRESHOLD = 8;
3201
+ var COLLAPSED_PANEL_SIZE = 42;
3202
+ var DRAG_EXCLUSION_SELECTOR = [
3203
+ ".dialkit-panel-icon",
3204
+ ".dialkit-panel-toolbar",
3205
+ "button",
3206
+ "input",
3207
+ "select",
3208
+ "textarea",
3209
+ "a",
3210
+ '[role="button"]',
3211
+ '[contenteditable="true"]'
3212
+ ].join(",");
3213
+ function getPanelDragHandle(target, panel) {
3214
+ if (!(target instanceof Element) || !panel) return null;
3215
+ const inner = target.closest(".dialkit-panel-inner");
3216
+ if (!inner || !panel.contains(inner)) return null;
3217
+ if (inner.getAttribute("data-collapsed") === "true") {
3218
+ return inner;
3219
+ }
3220
+ const header = target.closest(".dialkit-panel-header");
3221
+ if (!header || !inner.contains(header)) return null;
3222
+ if (target.closest(DRAG_EXCLUSION_SELECTOR)) return null;
3223
+ return header;
3224
+ }
3225
+ function getPanelDragStart(pointerX, pointerY, panel) {
3226
+ const rect = panel.getBoundingClientRect();
3227
+ return {
3228
+ pointerX,
3229
+ pointerY,
3230
+ elX: rect.left,
3231
+ elY: rect.top
3232
+ };
3233
+ }
3234
+ function getPanelDragOffset(start, pointerX, pointerY) {
3235
+ return {
3236
+ x: start.elX + pointerX - start.pointerX,
3237
+ y: start.elY + pointerY - start.pointerY
3238
+ };
3239
+ }
3240
+ function hasPanelDragMoved(start, pointerX, pointerY) {
3241
+ const dx = pointerX - start.pointerX;
3242
+ const dy = pointerY - start.pointerY;
3243
+ return Math.hypot(dx, dy) >= PANEL_DRAG_THRESHOLD;
3244
+ }
3245
+ function getPanelOriginX(position, offset, viewportWidth = typeof window !== "undefined" ? window.innerWidth : void 0) {
3246
+ if (offset && viewportWidth) {
3247
+ return offset.x + COLLAPSED_PANEL_SIZE / 2 < viewportWidth / 2 ? "left" : "right";
3248
+ }
3249
+ return position.endsWith("left") ? "left" : "right";
3250
+ }
3251
+ function blockPanelDragClick(handle) {
3252
+ const blocker = (event) => {
3253
+ event.preventDefault();
3254
+ event.stopImmediatePropagation();
3255
+ event.stopPropagation();
3256
+ };
3257
+ handle.addEventListener("click", blocker, { capture: true, once: true });
3258
+ window.setTimeout(() => {
3259
+ handle.removeEventListener("click", blocker, true);
3260
+ }, 0);
3261
+ }
3262
+
2959
3263
  // src/solid/components/DialRoot.tsx
2960
3264
  var import_meta = {};
2961
- var _tmpl$20 = /* @__PURE__ */ (0, import_web84.template)(`<div class=dialkit-root><div class=dialkit-panel>`);
3265
+ var _tmpl$20 = /* @__PURE__ */ (0, import_web84.template)(`<div class=dialkit-panel-wrapper>`);
3266
+ var _tmpl$210 = /* @__PURE__ */ (0, import_web84.template)(`<div class=dialkit-root><div class=dialkit-panel>`);
2962
3267
  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;
2963
3268
  function DialRoot(props) {
2964
3269
  if ((props.productionEnabled ?? isDevDefault) === false) return null;
2965
3270
  const [panels, setPanels] = (0, import_solid_js13.createSignal)([]);
2966
3271
  const [mounted, setMounted] = (0, import_solid_js13.createSignal)(false);
3272
+ const [dragOffset, setDragOffset] = (0, import_solid_js13.createSignal)(null);
3273
+ const [activePosition, setActivePosition] = (0, import_solid_js13.createSignal)(props.position ?? "top-right");
2967
3274
  const inline = () => (props.mode ?? "popover") === "inline";
3275
+ let panelRef;
3276
+ let lastDragOffset = null;
3277
+ let dragging = false;
3278
+ let dragStart = null;
3279
+ let didDrag = false;
3280
+ let dragTarget = null;
3281
+ let panelOpenStates = /* @__PURE__ */ new Map();
3282
+ let rootOpen;
2968
3283
  (0, import_solid_js13.onMount)(() => {
2969
3284
  setMounted(true);
2970
3285
  setPanels(DialStore.getPanels());
@@ -2973,45 +3288,185 @@ function DialRoot(props) {
2973
3288
  });
2974
3289
  (0, import_solid_js13.onCleanup)(unsub);
2975
3290
  });
2976
- const content = () => (0, import_web89.createComponent)(ShortcutListener, {
3291
+ (0, import_solid_js13.createEffect)(() => {
3292
+ const fallbackOpen = inline() || (props.defaultOpen ?? true);
3293
+ const nextStates = /* @__PURE__ */ new Map();
3294
+ for (const panel of panels()) {
3295
+ nextStates.set(panel.id, panelOpenStates.get(panel.id) ?? fallbackOpen);
3296
+ }
3297
+ panelOpenStates = nextStates;
3298
+ rootOpen = Array.from(nextStates.values()).some(Boolean);
3299
+ });
3300
+ (0, import_solid_js13.createEffect)(() => {
3301
+ if (!mounted() || inline() || !panelRef) return;
3302
+ const observer = new MutationObserver(() => {
3303
+ const inners = panelRef?.querySelectorAll(".dialkit-panel-inner");
3304
+ if (!inners || inners.length === 0) return;
3305
+ const collapsed = Array.from(inners).every((el) => el.getAttribute("data-collapsed") === "true");
3306
+ const currentDragOffset = dragOffset();
3307
+ if (!collapsed) {
3308
+ if (currentDragOffset) {
3309
+ lastDragOffset = currentDragOffset;
3310
+ const bubbleCenterX = currentDragOffset.x + 21;
3311
+ setActivePosition(bubbleCenterX < window.innerWidth / 2 ? "top-left" : "top-right");
3312
+ } else {
3313
+ setActivePosition(props.position ?? "top-right");
3314
+ }
3315
+ setDragOffset(null);
3316
+ } else if (currentDragOffset) {
3317
+ lastDragOffset = currentDragOffset;
3318
+ } else if (lastDragOffset) {
3319
+ setDragOffset(lastDragOffset);
3320
+ }
3321
+ });
3322
+ observer.observe(panelRef, {
3323
+ subtree: true,
3324
+ attributes: true,
3325
+ attributeFilter: ["data-collapsed"]
3326
+ });
3327
+ (0, import_solid_js13.onCleanup)(() => observer.disconnect());
3328
+ });
3329
+ const handlePointerDown = (event) => {
3330
+ const panel = panelRef ?? null;
3331
+ const handle = getPanelDragHandle(event.target, panel);
3332
+ if (!panel || !handle) return;
3333
+ dragTarget = handle;
3334
+ dragStart = getPanelDragStart(event.clientX, event.clientY, panel);
3335
+ didDrag = false;
3336
+ dragging = true;
3337
+ handle.setPointerCapture(event.pointerId);
3338
+ };
3339
+ const handlePointerMove = (event) => {
3340
+ if (!dragging || !dragStart) return;
3341
+ if (!didDrag && !hasPanelDragMoved(dragStart, event.clientX, event.clientY)) return;
3342
+ didDrag = true;
3343
+ setDragOffset(getPanelDragOffset(dragStart, event.clientX, event.clientY));
3344
+ };
3345
+ const handlePointerUp = (event) => {
3346
+ if (!dragging) return;
3347
+ dragging = false;
3348
+ dragStart = null;
3349
+ const handle = dragTarget;
3350
+ if (handle?.hasPointerCapture(event.pointerId)) {
3351
+ handle.releasePointerCapture(event.pointerId);
3352
+ }
3353
+ if (didDrag) {
3354
+ event.stopPropagation();
3355
+ if (handle) {
3356
+ blockPanelDragClick(handle);
3357
+ }
3358
+ }
3359
+ dragTarget = null;
3360
+ };
3361
+ const handlePanelOpenChange = (panelId, open) => {
3362
+ panelOpenStates.set(panelId, open);
3363
+ const fallbackOpen = inline() || (props.defaultOpen ?? true);
3364
+ const nextRootOpen = panels().some((panel) => panelOpenStates.get(panel.id) ?? fallbackOpen);
3365
+ if (rootOpen === nextRootOpen) return;
3366
+ rootOpen = nextRootOpen;
3367
+ props.onOpenChange?.(nextRootOpen);
3368
+ };
3369
+ const handleRootOpenChange = (open) => {
3370
+ if (rootOpen === open) return;
3371
+ rootOpen = open;
3372
+ props.onOpenChange?.(open);
3373
+ };
3374
+ const dragStyle = () => {
3375
+ const offset = dragOffset();
3376
+ return offset ? {
3377
+ top: `${offset.y}px`,
3378
+ left: `${offset.x}px`,
3379
+ right: "auto",
3380
+ bottom: "auto"
3381
+ } : void 0;
3382
+ };
3383
+ const content = () => (0, import_web90.createComponent)(ShortcutListener, {
2977
3384
  get children() {
2978
- var _el$ = _tmpl$20(), _el$2 = _el$.firstChild;
2979
- (0, import_web88.insert)(_el$2, (0, import_web89.createComponent)(import_solid_js13.For, {
2980
- get each() {
2981
- return panels();
3385
+ var _el$ = _tmpl$210(), _el$2 = _el$.firstChild;
3386
+ (0, import_web92.addEventListener)(_el$2, "pointercancel", !inline() ? handlePointerUp : void 0);
3387
+ (0, import_web92.addEventListener)(_el$2, "pointerup", !inline() ? handlePointerUp : void 0, true);
3388
+ (0, import_web92.addEventListener)(_el$2, "pointermove", !inline() ? handlePointerMove : void 0, true);
3389
+ (0, import_web92.addEventListener)(_el$2, "pointerdown", !inline() ? handlePointerDown : void 0, true);
3390
+ var _ref$ = panelRef;
3391
+ typeof _ref$ === "function" ? (0, import_web93.use)(_ref$, _el$2) : panelRef = _el$2;
3392
+ (0, import_web89.insert)(_el$2, (0, import_web90.createComponent)(import_solid_js13.Show, {
3393
+ get when() {
3394
+ return panels().length > 1;
2982
3395
  },
2983
- children: (panel) => (0, import_web89.createComponent)(Panel, {
2984
- panel,
2985
- get defaultOpen() {
2986
- return inline() || (props.defaultOpen ?? true);
2987
- },
2988
- get inline() {
2989
- return inline();
2990
- }
2991
- })
3396
+ get fallback() {
3397
+ return (0, import_web90.createComponent)(import_solid_js13.For, {
3398
+ get each() {
3399
+ return panels();
3400
+ },
3401
+ children: (panel) => (0, import_web90.createComponent)(Panel, {
3402
+ panel,
3403
+ get defaultOpen() {
3404
+ return inline() || (props.defaultOpen ?? true);
3405
+ },
3406
+ get inline() {
3407
+ return inline();
3408
+ },
3409
+ onOpenChange: (open) => handlePanelOpenChange(panel.id, open)
3410
+ })
3411
+ });
3412
+ },
3413
+ get children() {
3414
+ var _el$3 = _tmpl$20();
3415
+ (0, import_web89.insert)(_el$3, (0, import_web90.createComponent)(Folder, {
3416
+ title: "DialKit",
3417
+ get defaultOpen() {
3418
+ return inline() || (props.defaultOpen ?? true);
3419
+ },
3420
+ isRoot: true,
3421
+ get inline() {
3422
+ return inline();
3423
+ },
3424
+ onOpenChange: handleRootOpenChange,
3425
+ panelHeightOffset: 2,
3426
+ get children() {
3427
+ return (0, import_web90.createComponent)(import_solid_js13.For, {
3428
+ get each() {
3429
+ return panels();
3430
+ },
3431
+ children: (panel) => (0, import_web90.createComponent)(Panel, {
3432
+ panel,
3433
+ defaultOpen: true,
3434
+ variant: "section"
3435
+ })
3436
+ });
3437
+ }
3438
+ }));
3439
+ return _el$3;
3440
+ }
2992
3441
  }));
2993
- (0, import_web87.effect)((_p$) => {
2994
- var _v$ = props.mode ?? "popover", _v$2 = props.theme ?? "system", _v$3 = inline() ? void 0 : props.position ?? "top-right", _v$4 = props.mode ?? "popover";
2995
- _v$ !== _p$.e && (0, import_web86.setAttribute)(_el$, "data-mode", _p$.e = _v$);
2996
- _v$2 !== _p$.t && (0, import_web86.setAttribute)(_el$, "data-theme", _p$.t = _v$2);
2997
- _v$3 !== _p$.a && (0, import_web86.setAttribute)(_el$2, "data-position", _p$.a = _v$3);
2998
- _v$4 !== _p$.o && (0, import_web86.setAttribute)(_el$2, "data-mode", _p$.o = _v$4);
3442
+ (0, import_web88.effect)((_p$) => {
3443
+ 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();
3444
+ _v$ !== _p$.e && (0, import_web87.setAttribute)(_el$, "data-mode", _p$.e = _v$);
3445
+ _v$2 !== _p$.t && (0, import_web87.setAttribute)(_el$, "data-theme", _p$.t = _v$2);
3446
+ _v$3 !== _p$.a && (0, import_web87.setAttribute)(_el$2, "data-position", _p$.a = _v$3);
3447
+ _v$4 !== _p$.o && (0, import_web87.setAttribute)(_el$2, "data-origin-x", _p$.o = _v$4);
3448
+ _v$5 !== _p$.i && (0, import_web87.setAttribute)(_el$2, "data-mode", _p$.i = _v$5);
3449
+ _v$6 !== _p$.n && (0, import_web87.setAttribute)(_el$2, "data-multiple", _p$.n = _v$6);
3450
+ _p$.s = (0, import_web86.style)(_el$2, _v$7, _p$.s);
2999
3451
  return _p$;
3000
3452
  }, {
3001
3453
  e: void 0,
3002
3454
  t: void 0,
3003
3455
  a: void 0,
3004
- o: void 0
3456
+ o: void 0,
3457
+ i: void 0,
3458
+ n: void 0,
3459
+ s: void 0
3005
3460
  });
3006
3461
  return _el$;
3007
3462
  }
3008
3463
  });
3009
- return (0, import_web89.createComponent)(import_solid_js13.Show, {
3464
+ return (0, import_web90.createComponent)(import_solid_js13.Show, {
3010
3465
  get when() {
3011
- return (0, import_web85.memo)(() => !!(mounted() && typeof window !== "undefined"))() && panels().length > 0;
3466
+ return (0, import_web91.memo)(() => !!(mounted() && typeof window !== "undefined"))() && panels().length > 0;
3012
3467
  },
3013
3468
  get children() {
3014
- return (0, import_web89.createComponent)(import_solid_js13.Show, {
3469
+ return (0, import_web90.createComponent)(import_solid_js13.Show, {
3015
3470
  get when() {
3016
3471
  return !inline();
3017
3472
  },
@@ -3019,7 +3474,7 @@ function DialRoot(props) {
3019
3474
  return content();
3020
3475
  },
3021
3476
  get children() {
3022
- return (0, import_web89.createComponent)(import_web90.Portal, {
3477
+ return (0, import_web90.createComponent)(import_web94.Portal, {
3023
3478
  get mount() {
3024
3479
  return document.body;
3025
3480
  },
@@ -3032,34 +3487,35 @@ function DialRoot(props) {
3032
3487
  }
3033
3488
  });
3034
3489
  }
3490
+ (0, import_web85.delegateEvents)(["pointerdown", "pointermove", "pointerup"]);
3035
3491
 
3036
3492
  // src/solid/components/ButtonGroup.tsx
3037
- var import_web91 = require("solid-js/web");
3038
- var import_web92 = require("solid-js/web");
3039
- var import_web93 = require("solid-js/web");
3040
- var import_web94 = require("solid-js/web");
3041
3493
  var import_web95 = require("solid-js/web");
3494
+ var import_web96 = require("solid-js/web");
3495
+ var import_web97 = require("solid-js/web");
3496
+ var import_web98 = require("solid-js/web");
3497
+ var import_web99 = require("solid-js/web");
3042
3498
  var import_solid_js14 = require("solid-js");
3043
- var _tmpl$21 = /* @__PURE__ */ (0, import_web91.template)(`<div class=dialkit-button-group>`);
3044
- var _tmpl$210 = /* @__PURE__ */ (0, import_web91.template)(`<button class=dialkit-button>`);
3499
+ var _tmpl$21 = /* @__PURE__ */ (0, import_web95.template)(`<div class=dialkit-button-group>`);
3500
+ var _tmpl$211 = /* @__PURE__ */ (0, import_web95.template)(`<button class=dialkit-button>`);
3045
3501
  function ButtonGroup(props) {
3046
3502
  return (() => {
3047
3503
  var _el$ = _tmpl$21();
3048
- (0, import_web94.insert)(_el$, (0, import_web95.createComponent)(import_solid_js14.For, {
3504
+ (0, import_web98.insert)(_el$, (0, import_web99.createComponent)(import_solid_js14.For, {
3049
3505
  get each() {
3050
3506
  return props.buttons;
3051
3507
  },
3052
3508
  children: (button) => (() => {
3053
- var _el$2 = _tmpl$210();
3054
- (0, import_web93.addEventListener)(_el$2, "click", button.onClick, true);
3055
- (0, import_web94.insert)(_el$2, () => button.label);
3509
+ var _el$2 = _tmpl$211();
3510
+ (0, import_web97.addEventListener)(_el$2, "click", button.onClick, true);
3511
+ (0, import_web98.insert)(_el$2, () => button.label);
3056
3512
  return _el$2;
3057
3513
  })()
3058
3514
  }));
3059
3515
  return _el$;
3060
3516
  })();
3061
3517
  }
3062
- (0, import_web92.delegateEvents)(["click"]);
3518
+ (0, import_web96.delegateEvents)(["click"]);
3063
3519
  // Annotate the CommonJS export names for ESM import in node:
3064
3520
  0 && (module.exports = {
3065
3521
  ButtonGroup,
@@ -3074,6 +3530,7 @@ function ButtonGroup(props) {
3074
3530
  SpringVisualization,
3075
3531
  TextControl,
3076
3532
  Toggle,
3077
- createDialKit
3533
+ createDialKit,
3534
+ createDialKitController
3078
3535
  });
3079
3536
  //# sourceMappingURL=index.cjs.map