dialkit 1.2.1 → 1.4.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 (88) hide show
  1. package/README.md +653 -6
  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/icons.d.ts +4 -1
  6. package/dist/icons.js +13 -0
  7. package/dist/icons.js.map +1 -1
  8. package/dist/index.cjs +3796 -768
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +315 -10
  11. package/dist/index.d.ts +315 -10
  12. package/dist/index.js +3733 -711
  13. package/dist/index.js.map +1 -1
  14. package/dist/panel-drag.d.ts +19 -0
  15. package/dist/panel-drag.js +72 -0
  16. package/dist/panel-drag.js.map +1 -0
  17. package/dist/solid/index.d.ts +264 -5
  18. package/dist/solid/index.js +5277 -1632
  19. package/dist/solid/index.js.map +1 -1
  20. package/dist/store/index.cjs +329 -57
  21. package/dist/store/index.cjs.map +1 -1
  22. package/dist/store/index.d.cts +43 -4
  23. package/dist/store/index.d.ts +43 -4
  24. package/dist/store/index.js +321 -56
  25. package/dist/store/index.js.map +1 -1
  26. package/dist/styles.css +809 -7
  27. package/dist/svelte/components/ControlRenderer.svelte +5 -2
  28. package/dist/svelte/components/ControlRenderer.svelte.d.ts +2 -0
  29. package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -1
  30. package/dist/svelte/components/DialRoot.svelte +218 -16
  31. package/dist/svelte/components/DialRoot.svelte.d.ts +1 -0
  32. package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -1
  33. package/dist/svelte/components/Folder.svelte +24 -13
  34. package/dist/svelte/components/Folder.svelte.d.ts +1 -0
  35. package/dist/svelte/components/Folder.svelte.d.ts.map +1 -1
  36. package/dist/svelte/components/Panel.svelte +104 -71
  37. package/dist/svelte/components/Panel.svelte.d.ts +4 -0
  38. package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
  39. package/dist/svelte/components/PresetManager.svelte +5 -5
  40. package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -1
  41. package/dist/svelte/components/SelectControl.svelte +6 -14
  42. package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -1
  43. package/dist/svelte/components/Timeline/ClipPopover.svelte +206 -0
  44. package/dist/svelte/components/Timeline/ClipPopover.svelte.d.ts +26 -0
  45. package/dist/svelte/components/Timeline/ClipPopover.svelte.d.ts.map +1 -0
  46. package/dist/svelte/components/Timeline/DialTimeline.svelte +76 -0
  47. package/dist/svelte/components/Timeline/DialTimeline.svelte.d.ts +13 -0
  48. package/dist/svelte/components/Timeline/DialTimeline.svelte.d.ts.map +1 -0
  49. package/dist/svelte/components/Timeline/TimelineClip.svelte +233 -0
  50. package/dist/svelte/components/Timeline/TimelineClip.svelte.d.ts +24 -0
  51. package/dist/svelte/components/Timeline/TimelineClip.svelte.d.ts.map +1 -0
  52. package/dist/svelte/components/Timeline/TimelineSection.svelte +756 -0
  53. package/dist/svelte/components/Timeline/TimelineSection.svelte.d.ts +12 -0
  54. package/dist/svelte/components/Timeline/TimelineSection.svelte.d.ts.map +1 -0
  55. package/dist/svelte/components/Timeline/TimelineToggleButton.svelte +25 -0
  56. package/dist/svelte/components/Timeline/TimelineToggleButton.svelte.d.ts +4 -0
  57. package/dist/svelte/components/Timeline/TimelineToggleButton.svelte.d.ts.map +1 -0
  58. package/dist/svelte/components/TransitionControl.svelte +26 -11
  59. package/dist/svelte/components/TransitionControl.svelte.d.ts +9 -0
  60. package/dist/svelte/components/TransitionControl.svelte.d.ts.map +1 -1
  61. package/dist/svelte/createDialKit.svelte.d.ts +11 -1
  62. package/dist/svelte/createDialKit.svelte.d.ts.map +1 -1
  63. package/dist/svelte/createDialKit.svelte.js +61 -34
  64. package/dist/svelte/createDialTimeline.svelte.d.ts +4 -0
  65. package/dist/svelte/createDialTimeline.svelte.d.ts.map +1 -0
  66. package/dist/svelte/createDialTimeline.svelte.js +73 -0
  67. package/dist/svelte/index.d.ts +7 -3
  68. package/dist/svelte/index.d.ts.map +1 -1
  69. package/dist/svelte/index.js +4 -1
  70. package/dist/svelte/theme-css.d.ts +1 -1
  71. package/dist/svelte/theme-css.d.ts.map +1 -1
  72. package/dist/svelte/theme-css.js +809 -7
  73. package/dist/timeline/index.cjs +1288 -0
  74. package/dist/timeline/index.cjs.map +1 -0
  75. package/dist/timeline/index.d.cts +443 -0
  76. package/dist/timeline/index.d.ts +443 -0
  77. package/dist/timeline/index.js +1233 -0
  78. package/dist/timeline/index.js.map +1 -0
  79. package/dist/vue/index.d.ts +325 -12
  80. package/dist/vue/index.js +3226 -280
  81. package/dist/vue/index.js.map +1 -1
  82. package/package.json +23 -13
  83. package/dist/solid/index.cjs +0 -3079
  84. package/dist/solid/index.cjs.map +0 -1
  85. package/dist/solid/index.d.cts +0 -258
  86. package/dist/vue/index.cjs +0 -3056
  87. package/dist/vue/index.cjs.map +0 -1
  88. package/dist/vue/index.d.cts +0 -675
@@ -13,11 +13,13 @@
13
13
  import ControlRenderer from './ControlRenderer.svelte';
14
14
  import { SHORTCUT_CTX } from './ShortcutListener.svelte';
15
15
  import type { ShortcutContextValue } from './ShortcutListener.svelte';
16
+ import type { TransitionDurationControl } from './TransitionControl.svelte';
16
17
 
17
- let { panelId, control, values } = $props<{
18
+ let { panelId, control, values, transitionDuration } = $props<{
18
19
  panelId: string;
19
20
  control: ControlMeta;
20
21
  values: Record<string, DialValue>;
22
+ transitionDuration?: TransitionDurationControl;
21
23
  }>();
22
24
 
23
25
  const shortcutCtx = getContext<ShortcutContextValue | undefined>(SHORTCUT_CTX);
@@ -62,11 +64,12 @@
62
64
  label={control.label}
63
65
  value={controlValue as TransitionConfig}
64
66
  onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
67
+ durationControl={transitionDuration}
65
68
  />
66
69
  {:else if control.type === 'folder'}
67
70
  <Folder title={control.label} defaultOpen={control.defaultOpen ?? true}>
68
71
  {#each control.children ?? [] as child (child.path)}
69
- <ControlRenderer {panelId} control={child} {values} />
72
+ <ControlRenderer {panelId} control={child} {values} {transitionDuration} />
70
73
  {/each}
71
74
  </Folder>
72
75
  {:else if control.type === 'text'}
@@ -1,9 +1,11 @@
1
1
  import type { ControlMeta, DialValue } from 'dialkit/store';
2
2
  import ControlRenderer from './ControlRenderer.svelte';
3
+ import type { TransitionDurationControl } from './TransitionControl.svelte';
3
4
  type $$ComponentProps = {
4
5
  panelId: string;
5
6
  control: ControlMeta;
6
7
  values: Record<string, DialValue>;
8
+ transitionDuration?: TransitionDurationControl;
7
9
  };
8
10
  declare const ControlRenderer: import("svelte").Component<$$ComponentProps, {}, "">;
9
11
  type ControlRenderer = ReturnType<typeof ControlRenderer>;
@@ -1 +1 @@
1
- {"version":3,"file":"ControlRenderer.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ControlRenderer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAkC,MAAM,eAAe,CAAC;AAS5F,OAAO,eAAe,MAAM,0BAA0B,CAAC;AAItD,KAAK,gBAAgB,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACnC,CAAC;AAuDJ,QAAA,MAAM,eAAe,sDAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ControlRenderer.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ControlRenderer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAkC,MAAM,eAAe,CAAC;AAS5F,OAAO,eAAe,MAAM,0BAA0B,CAAC;AAGvD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAE3E,KAAK,gBAAgB,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;CAChD,CAAC;AAwDJ,QAAA,MAAM,eAAe,sDAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -1,35 +1,68 @@
1
1
  <script lang="ts">
2
2
  import { DialStore } from 'dialkit/store';
3
3
  import type { PanelConfig } from 'dialkit/store';
4
+ import { TimelineStore } from 'dialkit/timeline';
5
+ import type { TimelineMeta } from 'dialkit/timeline';
4
6
  import { themeCSS } from '../theme-css';
5
7
  import Portal from '../Portal.svelte';
8
+ import Folder from './Folder.svelte';
6
9
  import Panel from './Panel.svelte';
10
+ import TimelineToggleButton from './Timeline/TimelineToggleButton.svelte';
7
11
  import ShortcutListener from './ShortcutListener.svelte';
12
+ import {
13
+ blockPanelDragClick,
14
+ getPanelDragHandle,
15
+ getPanelDragOffset,
16
+ getPanelDragStart,
17
+ getPanelOriginX,
18
+ hasPanelDragMoved,
19
+ type PanelDragOffset,
20
+ type PanelDragStart,
21
+ } from '../../panel-drag';
8
22
 
9
23
  export type DialPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
10
24
  export type DialMode = 'popover' | 'inline';
11
25
  export type DialTheme = 'light' | 'dark' | 'system';
12
26
 
13
- declare const process: { env?: { NODE_ENV?: string } } | undefined;
14
-
15
- const isDevDefault = typeof process !== 'undefined' && process?.env?.NODE_ENV
16
- ? process.env.NODE_ENV !== 'production'
17
- : typeof import.meta !== 'undefined' && (import.meta as any).env?.MODE
18
- ? (import.meta as any).env.MODE !== 'production'
27
+ const nodeEnv = (globalThis as typeof globalThis & { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV;
28
+ const viteMode = (import.meta as { env?: { MODE?: string } }).env?.MODE;
29
+ const isDevDefault = nodeEnv
30
+ ? nodeEnv !== 'production'
31
+ : viteMode
32
+ ? viteMode !== 'production'
19
33
  : true;
20
34
 
21
- let { position = 'top-right', defaultOpen = true, mode = 'popover', theme = 'system' as DialTheme, productionEnabled = isDevDefault } = $props<{
35
+ let { position = 'top-right', defaultOpen = true, mode = 'popover', theme = 'system' as DialTheme, productionEnabled = isDevDefault, onOpenChange } = $props<{
22
36
  position?: DialPosition;
23
37
  defaultOpen?: boolean;
24
38
  mode?: DialMode;
25
39
  theme?: DialTheme;
26
40
  productionEnabled?: boolean;
41
+ onOpenChange?: (open: boolean) => void;
27
42
  }>();
28
43
 
29
44
  const inline = $derived(mode === 'inline');
30
45
 
31
46
  let panels = $state<PanelConfig[]>([]);
47
+ let timelines = $state<TimelineMeta[]>([]);
32
48
  let mounted = $state(false);
49
+ let panelRef = $state<HTMLDivElement>();
50
+ let dragOffset = $state<PanelDragOffset | null>(null);
51
+ let activePosition = $state<DialPosition>(position);
52
+ let lastDragOffset: PanelDragOffset | null = null;
53
+ let dragging = false;
54
+ let dragStart: PanelDragStart | null = null;
55
+ let didDrag = false;
56
+ let dragTarget: HTMLElement | null = null;
57
+ let panelOpenStates = new Map<string, boolean>();
58
+ let rootOpen: boolean | undefined;
59
+
60
+ const dragStyle = $derived(
61
+ dragOffset
62
+ ? `top:${dragOffset.y}px;left:${dragOffset.x}px;right:auto;bottom:auto;`
63
+ : undefined
64
+ );
65
+ const originX = $derived(inline ? undefined : getPanelOriginX(activePosition, dragOffset));
33
66
 
34
67
  $effect(() => {
35
68
  if (typeof document === 'undefined') return;
@@ -46,24 +79,193 @@
46
79
  if (typeof window === 'undefined') return;
47
80
 
48
81
  mounted = true;
49
- panels = DialStore.getPanels();
82
+ panels = DialStore.getPanels('panel');
83
+ timelines = TimelineStore.getTimelines();
84
+
85
+ const unsubscribePanels = DialStore.subscribeGlobal(() => {
86
+ panels = DialStore.getPanels('panel');
87
+ });
88
+ const unsubscribeTimelines = TimelineStore.subscribeGlobal(() => {
89
+ timelines = TimelineStore.getTimelines();
90
+ });
91
+
92
+ return () => {
93
+ unsubscribePanels();
94
+ unsubscribeTimelines();
95
+ };
96
+ });
97
+
98
+ $effect(() => {
99
+ const fallbackOpen = inline || defaultOpen;
100
+ const nextStates = new Map<string, boolean>();
101
+ for (const panel of panels) {
102
+ nextStates.set(panel.id, panelOpenStates.get(panel.id) ?? fallbackOpen);
103
+ }
104
+ panelOpenStates = nextStates;
105
+ rootOpen = Array.from(nextStates.values()).some(Boolean);
106
+ });
107
+
108
+ $effect(() => {
109
+ if (inline || !panelRef) return;
110
+
111
+ const observer = new MutationObserver(() => {
112
+ const inners = panelRef?.querySelectorAll('.dialkit-panel-inner');
113
+ if (!inners || inners.length === 0) return;
114
+ const collapsed = Array.from(inners).every((el) => el.getAttribute('data-collapsed') === 'true');
50
115
 
51
- const unsub = DialStore.subscribeGlobal(() => {
52
- panels = DialStore.getPanels();
116
+ if (!collapsed) {
117
+ if (dragOffset) {
118
+ lastDragOffset = dragOffset;
119
+ const bubbleCenterX = dragOffset.x + 21;
120
+ activePosition = bubbleCenterX < window.innerWidth / 2 ? 'top-left' : 'top-right';
121
+ } else {
122
+ activePosition = position;
123
+ }
124
+ dragOffset = null;
125
+ } else if (dragOffset) {
126
+ lastDragOffset = dragOffset;
127
+ } else if (lastDragOffset) {
128
+ dragOffset = lastDragOffset;
129
+ }
53
130
  });
54
131
 
55
- return unsub;
132
+ observer.observe(panelRef, { subtree: true, attributes: true, attributeFilter: ['data-collapsed'] });
133
+ return () => observer.disconnect();
56
134
  });
135
+
136
+ function handlePointerDown(event: PointerEvent) {
137
+ const panel = panelRef ?? null;
138
+ const handle = getPanelDragHandle(event.target, panel);
139
+ if (!panel || !handle) return;
140
+
141
+ dragTarget = handle;
142
+ dragStart = getPanelDragStart(event.clientX, event.clientY, panel);
143
+ didDrag = false;
144
+ dragging = true;
145
+ handle.setPointerCapture(event.pointerId);
146
+ }
147
+
148
+ function handlePointerMove(event: PointerEvent) {
149
+ if (!dragging || !dragStart) return;
150
+
151
+ if (!didDrag && !hasPanelDragMoved(dragStart, event.clientX, event.clientY)) return;
152
+ didDrag = true;
153
+
154
+ dragOffset = getPanelDragOffset(dragStart, event.clientX, event.clientY);
155
+ }
156
+
157
+ function handlePointerUp(event: PointerEvent) {
158
+ if (!dragging) return;
159
+ dragging = false;
160
+ dragStart = null;
161
+ const handle = dragTarget;
162
+
163
+ if (handle?.hasPointerCapture(event.pointerId)) {
164
+ handle.releasePointerCapture(event.pointerId);
165
+ }
166
+
167
+ if (didDrag) {
168
+ event.stopPropagation();
169
+ if (handle) {
170
+ blockPanelDragClick(handle);
171
+ }
172
+ }
173
+
174
+ dragTarget = null;
175
+ }
176
+
177
+ function handlePanelOpenChange(panelId: string, open: boolean) {
178
+ panelOpenStates.set(panelId, open);
179
+ const fallbackOpen = inline || defaultOpen;
180
+ const nextRootOpen = panels.some((panel) => (
181
+ panelOpenStates.get(panel.id) ?? fallbackOpen
182
+ ));
183
+
184
+ if (rootOpen === nextRootOpen) return;
185
+ rootOpen = nextRootOpen;
186
+ onOpenChange?.(nextRootOpen);
187
+ }
188
+
189
+ function handleRootOpenChange(open: boolean) {
190
+ if (rootOpen === open) return;
191
+ rootOpen = open;
192
+ onOpenChange?.(open);
193
+ }
194
+
57
195
  </script>
58
196
 
59
- {#if productionEnabled && mounted && panels.length > 0}
197
+ {#snippet timelineToolbar()}
198
+ {#if timelines.length > 0}
199
+ <TimelineToggleButton />
200
+ {/if}
201
+ {/snippet}
202
+
203
+ {#if productionEnabled && mounted && (panels.length > 0 || timelines.length > 0)}
60
204
  {#snippet content()}
61
205
  <ShortcutListener>
62
206
  <div class="dialkit-root" data-mode={mode} data-theme={theme}>
63
- <div class="dialkit-panel" data-mode={mode} data-position={inline ? undefined : position}>
64
- {#each panels as panel (panel.id)}
65
- <Panel {panel} defaultOpen={inline || defaultOpen} {inline} />
66
- {/each}
207
+ <div
208
+ bind:this={panelRef}
209
+ class="dialkit-panel"
210
+ data-mode={mode}
211
+ data-position={inline ? undefined : (dragOffset ? undefined : activePosition)}
212
+ data-origin-x={originX}
213
+ data-multiple={panels.length > 1 ? 'true' : undefined}
214
+ style={dragStyle}
215
+ onpointerdown={!inline ? handlePointerDown : undefined}
216
+ onpointermove={!inline ? handlePointerMove : undefined}
217
+ onpointerup={!inline ? handlePointerUp : undefined}
218
+ onpointercancel={!inline ? handlePointerUp : undefined}
219
+ >
220
+ {#if panels.length === 0}
221
+ <div class="dialkit-panel-wrapper">
222
+ <Folder
223
+ title="DialKit"
224
+ defaultOpen={inline || defaultOpen}
225
+ isRoot={true}
226
+ {inline}
227
+ onOpenChange={handleRootOpenChange}
228
+ panelHeightOffset={2}
229
+ >
230
+ {#snippet toolbar()}
231
+ {@render timelineToolbar()}
232
+ {/snippet}
233
+ <div class="dialkit-timeline-toolkit-only">Timeline</div>
234
+ </Folder>
235
+ </div>
236
+ {:else if panels.length > 1}
237
+ <div class="dialkit-panel-wrapper">
238
+ <Folder
239
+ title="DialKit"
240
+ defaultOpen={inline || defaultOpen}
241
+ isRoot={true}
242
+ {inline}
243
+ onOpenChange={handleRootOpenChange}
244
+ panelHeightOffset={2}
245
+ >
246
+ {#snippet toolbar()}
247
+ {@render timelineToolbar()}
248
+ {/snippet}
249
+ {#each panels as panel (panel.id)}
250
+ <Panel
251
+ {panel}
252
+ defaultOpen={true}
253
+ variant="section"
254
+ />
255
+ {/each}
256
+ </Folder>
257
+ </div>
258
+ {:else}
259
+ {#each panels as panel (panel.id)}
260
+ <Panel
261
+ {panel}
262
+ defaultOpen={inline || defaultOpen}
263
+ {inline}
264
+ toolbarExtra={timelineToolbar}
265
+ onOpenChange={(open) => handlePanelOpenChange(panel.id, open)}
266
+ />
267
+ {/each}
268
+ {/if}
67
269
  </div>
68
270
  </div>
69
271
  </ShortcutListener>
@@ -7,6 +7,7 @@ type $$ComponentProps = {
7
7
  mode?: DialMode;
8
8
  theme?: DialTheme;
9
9
  productionEnabled?: boolean;
10
+ onOpenChange?: (open: boolean) => void;
10
11
  };
11
12
  declare const DialRoot: import("svelte").Component<$$ComponentProps, {}, "">;
12
13
  type DialRoot = ReturnType<typeof DialRoot>;
@@ -1 +1 @@
1
- {"version":3,"file":"DialRoot.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/DialRoot.svelte.ts"],"names":[],"mappings":"AAWE,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,aAAa,CAAC;AACrF,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AACrD,KAAK,gBAAgB,GAAG;IACrB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AA4EJ,QAAA,MAAM,QAAQ,sDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"DialRoot.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/DialRoot.svelte.ts"],"names":[],"mappings":"AAyBE,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,aAAa,CAAC;AACrF,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AACrD,KAAK,gBAAgB,GAAG;IACrB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC;AAwOJ,QAAA,MAAM,QAAQ,sDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -12,6 +12,7 @@
12
12
  inline = false,
13
13
  onOpenChange,
14
14
  toolbar,
15
+ panelHeightOffset = 10,
15
16
  children,
16
17
  } = $props<{
17
18
  title: string;
@@ -20,12 +21,14 @@
20
21
  inline?: boolean;
21
22
  onOpenChange?: (isOpen: boolean) => void;
22
23
  toolbar?: Snippet;
24
+ panelHeightOffset?: number;
23
25
  children?: Snippet;
24
26
  }>();
25
27
 
26
28
  let isOpen = $state(defaultOpen);
27
29
  let isCollapsed = $state(!defaultOpen);
28
30
  let contentHeight = $state<number | undefined>(undefined);
31
+ let hasInitializedRootSize = $state(!isRoot || !defaultOpen);
29
32
 
30
33
  let contentRef: HTMLDivElement | undefined;
31
34
  let panelRef: HTMLDivElement | undefined;
@@ -74,11 +77,16 @@
74
77
  if (!isRoot) return;
75
78
 
76
79
  const measured = contentHeight ?? panelRef?.getBoundingClientRect().height ?? 42;
77
- const nextHeight = isOpen ? Math.min(measured + 10, windowHeight - 32) : 42;
80
+ const nextHeight = isOpen ? Math.min(measured + panelHeightOffset, windowHeight - 32) : 42;
81
+ const springOptions = !hasInitializedRootSize && isOpen ? { instant: true } : undefined;
78
82
 
79
- panelWidth.set(isOpen ? 280 : 42);
80
- panelHeight.set(nextHeight);
81
- panelRadius.set(isOpen ? 14 : 21);
83
+ panelWidth.set(isOpen ? 280 : 42, springOptions);
84
+ panelHeight.set(nextHeight, springOptions);
85
+ panelRadius.set(isOpen ? 14 : 21, springOptions);
86
+
87
+ if (isOpen || !defaultOpen) {
88
+ hasInitializedRootSize = true;
89
+ }
82
90
  });
83
91
 
84
92
  const handleToggle = () => {
@@ -100,8 +108,9 @@
100
108
  panelScale.set(1);
101
109
  };
102
110
 
111
+ const panelHeightStyle = $derived(!hasInitializedRootSize && isOpen ? 'auto' : `${panelHeight.current}px`);
103
112
  const panelStyle = $derived(
104
- `width:${panelWidth.current}px;height:${panelHeight.current}px;border-radius:${panelRadius.current}px;` +
113
+ `width:${panelWidth.current}px;height:${panelHeightStyle};max-height:${Math.max(windowHeight - 32, 42)}px;border-radius:${panelRadius.current}px;` +
105
114
  `box-shadow:${isOpen ? 'var(--dial-shadow)' : 'var(--dial-shadow-collapsed)'};` +
106
115
  `cursor:${isOpen ? '' : 'pointer'};overflow:${isOpen ? 'hidden auto' : 'hidden'};` +
107
116
  `transform:scale(${panelScale.current});`
@@ -110,7 +119,7 @@
110
119
 
111
120
  {#if isRoot && inline}
112
121
  <div class="dialkit-panel-inner dialkit-panel-inline">
113
- <div bind:this={contentRef} class="dialkit-folder dialkit-folder-root">
122
+ <div bind:this={contentRef} class="dialkit-folder dialkit-folder-root" data-open={String(isOpen)}>
114
123
  <div class="dialkit-folder-header dialkit-panel-header" onclick={(e) => { e.stopPropagation(); handleToggle(); }}>
115
124
  <div class="dialkit-folder-header-top">
116
125
  <div class="dialkit-folder-title-row">
@@ -118,9 +127,11 @@
118
127
  </div>
119
128
  </div>
120
129
 
121
- <div class="dialkit-panel-toolbar" onclick={(e) => e.stopPropagation()}>
122
- {#if toolbar}{@render toolbar()}{/if}
123
- </div>
130
+ {#if toolbar}
131
+ <div class="dialkit-panel-toolbar" onclick={(e) => e.stopPropagation()}>
132
+ {@render toolbar()}
133
+ </div>
134
+ {/if}
124
135
  </div>
125
136
 
126
137
  <div class="dialkit-folder-content">
@@ -142,7 +153,7 @@
142
153
  onpointerleave={handleCollapsedTapEnd}
143
154
  onclick={() => { if (!isOpen) handleToggle(); }}
144
155
  >
145
- <div bind:this={contentRef} class="dialkit-folder dialkit-folder-root">
156
+ <div bind:this={contentRef} class="dialkit-folder dialkit-folder-root" data-open={String(isOpen)}>
146
157
  <div class="dialkit-folder-header dialkit-panel-header" onclick={(e) => { e.stopPropagation(); handleToggle(); }}>
147
158
  <div class="dialkit-folder-header-top">
148
159
  {#if isOpen}
@@ -163,9 +174,9 @@
163
174
  </svg>
164
175
  </div>
165
176
 
166
- {#if isOpen}
177
+ {#if isOpen && toolbar}
167
178
  <div class="dialkit-panel-toolbar" onclick={(e) => e.stopPropagation()}>
168
- {#if toolbar}{@render toolbar()}{/if}
179
+ {@render toolbar()}
169
180
  </div>
170
181
  {/if}
171
182
  </div>
@@ -180,7 +191,7 @@
180
191
  </div>
181
192
  </div>
182
193
  {:else}
183
- <div class="dialkit-folder">
194
+ <div class="dialkit-folder" data-open={String(isOpen)}>
184
195
  <div class="dialkit-folder-header" onclick={handleToggle}>
185
196
  <div class="dialkit-folder-header-top">
186
197
  <div class="dialkit-folder-title-row">
@@ -6,6 +6,7 @@ type $$ComponentProps = {
6
6
  inline?: boolean;
7
7
  onOpenChange?: (isOpen: boolean) => void;
8
8
  toolbar?: Snippet;
9
+ panelHeightOffset?: number;
9
10
  children?: Snippet;
10
11
  };
11
12
  declare const Folder: import("svelte").Component<$$ComponentProps, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"Folder.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/Folder.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,KAAK,gBAAgB,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA0LJ,QAAA,MAAM,MAAM,sDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Folder.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/Folder.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,KAAK,gBAAgB,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAoMJ,QAAA,MAAM,MAAM,sDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Spring } from 'svelte/motion';
3
+ import type { Snippet } from 'svelte';
3
4
  import { DialStore } from 'dialkit/store';
4
5
  import type { DialValue, PanelConfig, Preset } from 'dialkit/store';
5
6
  import Folder from './Folder.svelte';
@@ -8,7 +9,14 @@
8
9
  import ShortcutsMenu from './ShortcutsMenu.svelte';
9
10
  import { ICON_CLIPBOARD, ICON_CHECK, ICON_ADD_PRESET } from '../../icons';
10
11
 
11
- let { panel, defaultOpen = true, inline = false } = $props<{ panel: PanelConfig; defaultOpen?: boolean; inline?: boolean }>();
12
+ let { panel, defaultOpen = true, inline = false, onOpenChange, variant = 'root', toolbarExtra } = $props<{
13
+ panel: PanelConfig;
14
+ defaultOpen?: boolean;
15
+ inline?: boolean;
16
+ onOpenChange?: (open: boolean) => void;
17
+ variant?: 'root' | 'section';
18
+ toolbarExtra?: Snippet;
19
+ }>();
12
20
 
13
21
  const hasShortcuts = $derived(Object.keys(panel.shortcuts).length > 0);
14
22
 
@@ -72,78 +80,103 @@
72
80
  copied = false;
73
81
  }, 1500);
74
82
  };
83
+
84
+ const handleOpenChange = (open: boolean) => {
85
+ isPanelOpen = open;
86
+ onOpenChange?.(open);
87
+ };
75
88
  </script>
76
89
 
77
- <div class="dialkit-panel-wrapper">
78
- <Folder title={panel.name} {defaultOpen} isRoot={true} {inline} onOpenChange={(open) => (isPanelOpen = open)}>
79
- {#snippet toolbar()}
80
- <button
81
- class="dialkit-toolbar-add"
82
- onclick={handleAddPreset}
83
- onpointerdown={() => addScale.set(0.9)}
84
- onpointerup={() => addScale.set(1)}
85
- onpointercancel={() => addScale.set(1)}
86
- onpointerleave={() => addScale.set(1)}
87
- title="Add preset"
88
- style:transform={`scale(${addScale.current})`}
90
+ {#snippet panelToolbar()}
91
+ <button
92
+ class="dialkit-toolbar-add"
93
+ onclick={handleAddPreset}
94
+ onpointerdown={() => addScale.set(0.9)}
95
+ onpointerup={() => addScale.set(1)}
96
+ onpointercancel={() => addScale.set(1)}
97
+ onpointerleave={() => addScale.set(1)}
98
+ title="Add preset"
99
+ style:transform={`scale(${addScale.current})`}
100
+ >
101
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
102
+ <path d={ICON_ADD_PRESET[0]} />
103
+ <path d={ICON_ADD_PRESET[1]} />
104
+ <path d={ICON_ADD_PRESET[2]} />
105
+ <path d={ICON_ADD_PRESET[3]} />
106
+ <path d={ICON_ADD_PRESET[4]} />
107
+ </svg>
108
+ </button>
109
+
110
+ <PresetManager panelId={panel.id} {presets} {activePresetId} />
111
+
112
+ <button
113
+ class="dialkit-toolbar-copy"
114
+ onclick={handleCopy}
115
+ onpointerdown={() => copyScale.set(0.95)}
116
+ onpointerup={() => copyScale.set(1)}
117
+ onpointercancel={() => copyScale.set(1)}
118
+ onpointerleave={() => copyScale.set(1)}
119
+ title="Copy parameters"
120
+ style:transform={`scale(${copyScale.current})`}
121
+ >
122
+ <span class="dialkit-toolbar-copy-icon-wrap">
123
+ <svg
124
+ class="dialkit-toolbar-copy-icon"
125
+ viewBox="0 0 24 24"
126
+ fill="none"
127
+ style:opacity={clipboardOpacity.current}
128
+ style:transform={`scale(${clipboardScale.current})`}
129
+ style:filter={`blur(${(1 - clipboardOpacity.current) * 4}px)`}
89
130
  >
90
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
91
- <path d={ICON_ADD_PRESET[0]} />
92
- <path d={ICON_ADD_PRESET[1]} />
93
- <path d={ICON_ADD_PRESET[2]} />
94
- <path d={ICON_ADD_PRESET[3]} />
95
- <path d={ICON_ADD_PRESET[4]} />
96
- </svg>
97
- </button>
98
-
99
- <PresetManager panelId={panel.id} {presets} {activePresetId} />
100
-
101
- <button
102
- class="dialkit-toolbar-copy"
103
- onclick={handleCopy}
104
- onpointerdown={() => copyScale.set(0.95)}
105
- onpointerup={() => copyScale.set(1)}
106
- onpointercancel={() => copyScale.set(1)}
107
- onpointerleave={() => copyScale.set(1)}
108
- title="Copy parameters"
109
- style:transform={`scale(${copyScale.current})`}
131
+ <path d={ICON_CLIPBOARD.board} stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
132
+ <path d={ICON_CLIPBOARD.sparkle} fill="currentColor"/>
133
+ <path d={ICON_CLIPBOARD.body} stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
134
+ </svg>
135
+
136
+ <svg
137
+ class="dialkit-toolbar-copy-icon"
138
+ viewBox="0 0 24 24"
139
+ fill="none"
140
+ stroke="currentColor"
141
+ stroke-width="2"
142
+ stroke-linecap="round"
143
+ stroke-linejoin="round"
144
+ style:opacity={checkOpacity.current}
145
+ style:transform={`scale(${checkScale.current})`}
146
+ style:filter={`blur(${(1 - checkOpacity.current) * 4}px)`}
110
147
  >
111
- <span class="dialkit-toolbar-copy-icon-wrap">
112
- <svg
113
- class="dialkit-toolbar-copy-icon"
114
- viewBox="0 0 24 24"
115
- fill="none"
116
- style:opacity={clipboardOpacity.current}
117
- style:transform={`scale(${clipboardScale.current})`}
118
- style:filter={`blur(${(1 - clipboardOpacity.current) * 4}px)`}
119
- >
120
- <path d={ICON_CLIPBOARD.board} stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
121
- <path d={ICON_CLIPBOARD.sparkle} fill="currentColor"/>
122
- <path d={ICON_CLIPBOARD.body} stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
123
- </svg>
124
-
125
- <svg
126
- class="dialkit-toolbar-copy-icon"
127
- viewBox="0 0 24 24"
128
- fill="none"
129
- stroke="currentColor"
130
- stroke-width="2"
131
- stroke-linecap="round"
132
- stroke-linejoin="round"
133
- style:opacity={checkOpacity.current}
134
- style:transform={`scale(${checkScale.current})`}
135
- style:filter={`blur(${(1 - checkOpacity.current) * 4}px)`}
136
- >
137
- <path d={ICON_CHECK} />
138
- </svg>
139
- </span>
140
- Copy
141
- </button>
142
-
143
- {/snippet}
144
-
145
- {#each panel.controls as control (control.path)}
146
- <ControlRenderer panelId={panel.id} {control} {values} />
147
- {/each}
148
+ <path d={ICON_CHECK} />
149
+ </svg>
150
+ </span>
151
+ Copy
152
+ </button>
153
+
154
+ {#if toolbarExtra}
155
+ {@render toolbarExtra()}
156
+ {/if}
157
+ {/snippet}
158
+
159
+ {#snippet panelControls()}
160
+ {#each panel.controls as control (control.path)}
161
+ <ControlRenderer panelId={panel.id} {control} {values} />
162
+ {/each}
163
+ {/snippet}
164
+
165
+ {#if variant === 'section'}
166
+ <Folder title={panel.name} {defaultOpen} onOpenChange={handleOpenChange}>
167
+ <div class="dialkit-panel-section-toolbar" onclick={(e) => e.stopPropagation()}>
168
+ {@render panelToolbar()}
169
+ </div>
170
+ {@render panelControls()}
148
171
  </Folder>
149
- </div>
172
+ {:else}
173
+ <div class="dialkit-panel-wrapper">
174
+ <Folder title={panel.name} {defaultOpen} isRoot={true} {inline} onOpenChange={handleOpenChange}>
175
+ {#snippet toolbar()}
176
+ {@render panelToolbar()}
177
+ {/snippet}
178
+
179
+ {@render panelControls()}
180
+ </Folder>
181
+ </div>
182
+ {/if}