dialkit 1.0.0 → 1.2.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 (97) hide show
  1. package/README.md +293 -2
  2. package/dist/index.cjs +905 -363
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +46 -9
  5. package/dist/index.d.ts +46 -9
  6. package/dist/index.js +910 -370
  7. package/dist/index.js.map +1 -1
  8. package/dist/solid/index.cjs +1042 -532
  9. package/dist/solid/index.cjs.map +1 -1
  10. package/dist/solid/index.d.cts +36 -3
  11. package/dist/solid/index.d.ts +36 -3
  12. package/dist/solid/index.js +911 -402
  13. package/dist/solid/index.js.map +1 -1
  14. package/dist/store/index.cjs +490 -0
  15. package/dist/store/index.cjs.map +1 -0
  16. package/dist/store/index.d.cts +146 -0
  17. package/dist/store/index.d.ts +146 -0
  18. package/dist/store/index.js +465 -0
  19. package/dist/store/index.js.map +1 -0
  20. package/dist/styles.css +287 -25
  21. package/dist/svelte/Portal.svelte +30 -0
  22. package/dist/svelte/Portal.svelte.d.ts +9 -0
  23. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  24. package/dist/svelte/components/ButtonGroup.svelte +16 -0
  25. package/dist/svelte/components/ButtonGroup.svelte.d.ts +11 -0
  26. package/dist/svelte/components/ButtonGroup.svelte.d.ts.map +1 -0
  27. package/dist/svelte/components/ColorControl.svelte +81 -0
  28. package/dist/svelte/components/ColorControl.svelte.d.ts +9 -0
  29. package/dist/svelte/components/ColorControl.svelte.d.ts.map +1 -0
  30. package/dist/svelte/components/ControlRenderer.svelte +96 -0
  31. package/dist/svelte/components/ControlRenderer.svelte.d.ts +11 -0
  32. package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -0
  33. package/dist/svelte/components/DialRoot.svelte +79 -0
  34. package/dist/svelte/components/DialRoot.svelte.d.ts +14 -0
  35. package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -0
  36. package/dist/svelte/components/EasingVisualization.svelte +50 -0
  37. package/dist/svelte/components/EasingVisualization.svelte.d.ts +10 -0
  38. package/dist/svelte/components/EasingVisualization.svelte.d.ts.map +1 -0
  39. package/dist/svelte/components/Folder.svelte +213 -0
  40. package/dist/svelte/components/Folder.svelte.d.ts +14 -0
  41. package/dist/svelte/components/Folder.svelte.d.ts.map +1 -0
  42. package/dist/svelte/components/Panel.svelte +149 -0
  43. package/dist/svelte/components/Panel.svelte.d.ts +10 -0
  44. package/dist/svelte/components/Panel.svelte.d.ts.map +1 -0
  45. package/dist/svelte/components/PresetManager.svelte +157 -0
  46. package/dist/svelte/components/PresetManager.svelte.d.ts +10 -0
  47. package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -0
  48. package/dist/svelte/components/SegmentedControl.svelte +62 -0
  49. package/dist/svelte/components/SegmentedControl.svelte.d.ts +13 -0
  50. package/dist/svelte/components/SegmentedControl.svelte.d.ts.map +1 -0
  51. package/dist/svelte/components/SelectControl.svelte +152 -0
  52. package/dist/svelte/components/SelectControl.svelte.d.ts +14 -0
  53. package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -0
  54. package/dist/svelte/components/ShortcutListener.svelte +265 -0
  55. package/dist/svelte/components/ShortcutListener.svelte.d.ts +13 -0
  56. package/dist/svelte/components/ShortcutListener.svelte.d.ts.map +1 -0
  57. package/dist/svelte/components/ShortcutsMenu.svelte +128 -0
  58. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts +7 -0
  59. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts.map +1 -0
  60. package/dist/svelte/components/Slider.svelte +332 -0
  61. package/dist/svelte/components/Slider.svelte.d.ts +16 -0
  62. package/dist/svelte/components/Slider.svelte.d.ts.map +1 -0
  63. package/dist/svelte/components/SpringControl.svelte +126 -0
  64. package/dist/svelte/components/SpringControl.svelte.d.ts +12 -0
  65. package/dist/svelte/components/SpringControl.svelte.d.ts.map +1 -0
  66. package/dist/svelte/components/SpringVisualization.svelte +115 -0
  67. package/dist/svelte/components/SpringVisualization.svelte.d.ts +9 -0
  68. package/dist/svelte/components/SpringVisualization.svelte.d.ts.map +1 -0
  69. package/dist/svelte/components/TextControl.svelte +19 -0
  70. package/dist/svelte/components/TextControl.svelte.d.ts +10 -0
  71. package/dist/svelte/components/TextControl.svelte.d.ts.map +1 -0
  72. package/dist/svelte/components/Toggle.svelte +32 -0
  73. package/dist/svelte/components/Toggle.svelte.d.ts +12 -0
  74. package/dist/svelte/components/Toggle.svelte.d.ts.map +1 -0
  75. package/dist/svelte/components/TransitionControl.svelte +203 -0
  76. package/dist/svelte/components/TransitionControl.svelte.d.ts +12 -0
  77. package/dist/svelte/components/TransitionControl.svelte.d.ts.map +1 -0
  78. package/dist/svelte/components/transitions.d.ts +6 -0
  79. package/dist/svelte/components/transitions.d.ts.map +1 -0
  80. package/dist/svelte/components/transitions.js +14 -0
  81. package/dist/svelte/createDialKit.svelte.d.ts +8 -0
  82. package/dist/svelte/createDialKit.svelte.d.ts.map +1 -0
  83. package/dist/svelte/createDialKit.svelte.js +82 -0
  84. package/dist/svelte/index.d.ts +23 -0
  85. package/dist/svelte/index.d.ts.map +1 -0
  86. package/dist/svelte/index.js +23 -0
  87. package/dist/svelte/shims.d.ts +9 -0
  88. package/dist/svelte/theme-css.d.ts +2 -0
  89. package/dist/svelte/theme-css.d.ts.map +1 -0
  90. package/dist/svelte/theme-css.js +1328 -0
  91. package/dist/vue/index.cjs +3056 -0
  92. package/dist/vue/index.cjs.map +1 -0
  93. package/dist/vue/index.d.cts +675 -0
  94. package/dist/vue/index.d.ts +675 -0
  95. package/dist/vue/index.js +3014 -0
  96. package/dist/vue/index.js.map +1 -0
  97. package/package.json +50 -6
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ label: string;
3
+ value: string;
4
+ onChange: (value: string) => void;
5
+ };
6
+ declare const ColorControl: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type ColorControl = ReturnType<typeof ColorControl>;
8
+ export default ColorControl;
9
+ //# sourceMappingURL=ColorControl.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorControl.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ColorControl.svelte.ts"],"names":[],"mappings":"AAGC,KAAK,gBAAgB,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AA4DJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,96 @@
1
+ <script lang="ts">
2
+ import { getContext } from 'svelte';
3
+ import { DialStore } from 'dialkit/store';
4
+ import type { ControlMeta, DialValue, SpringConfig, TransitionConfig } from 'dialkit/store';
5
+ import Slider from './Slider.svelte';
6
+ import Toggle from './Toggle.svelte';
7
+ import Folder from './Folder.svelte';
8
+ import SpringControl from './SpringControl.svelte';
9
+ import TransitionControl from './TransitionControl.svelte';
10
+ import TextControl from './TextControl.svelte';
11
+ import SelectControl from './SelectControl.svelte';
12
+ import ColorControl from './ColorControl.svelte';
13
+ import ControlRenderer from './ControlRenderer.svelte';
14
+ import { SHORTCUT_CTX } from './ShortcutListener.svelte';
15
+ import type { ShortcutContextValue } from './ShortcutListener.svelte';
16
+
17
+ let { panelId, control, values } = $props<{
18
+ panelId: string;
19
+ control: ControlMeta;
20
+ values: Record<string, DialValue>;
21
+ }>();
22
+
23
+ const shortcutCtx = getContext<ShortcutContextValue | undefined>(SHORTCUT_CTX);
24
+
25
+ const controlValue = $derived(values[control.path]);
26
+ const isShortcutActive = $derived(
27
+ shortcutCtx ? shortcutCtx.activePanelId === panelId && shortcutCtx.activePath === control.path : false
28
+ );
29
+ </script>
30
+
31
+ {#if control.type === 'slider'}
32
+ <Slider
33
+ label={control.label}
34
+ value={controlValue as number}
35
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
36
+ min={control.min}
37
+ max={control.max}
38
+ step={control.step}
39
+ shortcut={control.shortcut}
40
+ shortcutActive={isShortcutActive}
41
+ />
42
+ {:else if control.type === 'toggle'}
43
+ <Toggle
44
+ label={control.label}
45
+ checked={controlValue as boolean}
46
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
47
+ shortcut={control.shortcut}
48
+ shortcutActive={isShortcutActive}
49
+ />
50
+ {:else if control.type === 'spring'}
51
+ <SpringControl
52
+ {panelId}
53
+ path={control.path}
54
+ label={control.label}
55
+ spring={controlValue as SpringConfig}
56
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
57
+ />
58
+ {:else if control.type === 'transition'}
59
+ <TransitionControl
60
+ {panelId}
61
+ path={control.path}
62
+ label={control.label}
63
+ value={controlValue as TransitionConfig}
64
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
65
+ />
66
+ {:else if control.type === 'folder'}
67
+ <Folder title={control.label} defaultOpen={control.defaultOpen ?? true}>
68
+ {#each control.children ?? [] as child (child.path)}
69
+ <ControlRenderer {panelId} control={child} {values} />
70
+ {/each}
71
+ </Folder>
72
+ {:else if control.type === 'text'}
73
+ <TextControl
74
+ label={control.label}
75
+ value={controlValue as string}
76
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
77
+ placeholder={control.placeholder}
78
+ />
79
+ {:else if control.type === 'select'}
80
+ <SelectControl
81
+ label={control.label}
82
+ value={controlValue as string}
83
+ options={control.options ?? []}
84
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
85
+ />
86
+ {:else if control.type === 'color'}
87
+ <ColorControl
88
+ label={control.label}
89
+ value={controlValue as string}
90
+ onChange={(v) => DialStore.updateValue(panelId, control.path, v)}
91
+ />
92
+ {:else if control.type === 'action'}
93
+ <button class="dialkit-button" onclick={() => DialStore.triggerAction(panelId, control.path)}>
94
+ {control.label}
95
+ </button>
96
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { ControlMeta, DialValue } from 'dialkit/store';
2
+ import ControlRenderer from './ControlRenderer.svelte';
3
+ type $$ComponentProps = {
4
+ panelId: string;
5
+ control: ControlMeta;
6
+ values: Record<string, DialValue>;
7
+ };
8
+ declare const ControlRenderer: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ControlRenderer = ReturnType<typeof ControlRenderer>;
10
+ export default ControlRenderer;
11
+ //# sourceMappingURL=ControlRenderer.svelte.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ import { DialStore } from 'dialkit/store';
3
+ import type { PanelConfig } from 'dialkit/store';
4
+ import { themeCSS } from '../theme-css';
5
+ import Portal from '../Portal.svelte';
6
+ import Panel from './Panel.svelte';
7
+ import ShortcutListener from './ShortcutListener.svelte';
8
+
9
+ export type DialPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
10
+ export type DialMode = 'popover' | 'inline';
11
+ export type DialTheme = 'light' | 'dark' | 'system';
12
+
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'
19
+ : true;
20
+
21
+ let { position = 'top-right', defaultOpen = true, mode = 'popover', theme = 'system' as DialTheme, productionEnabled = isDevDefault } = $props<{
22
+ position?: DialPosition;
23
+ defaultOpen?: boolean;
24
+ mode?: DialMode;
25
+ theme?: DialTheme;
26
+ productionEnabled?: boolean;
27
+ }>();
28
+
29
+ const inline = $derived(mode === 'inline');
30
+
31
+ let panels = $state<PanelConfig[]>([]);
32
+ let mounted = $state(false);
33
+
34
+ $effect(() => {
35
+ if (typeof document === 'undefined') return;
36
+ const id = 'dialkit-theme';
37
+ if (!document.getElementById(id)) {
38
+ const style = document.createElement('style');
39
+ style.id = id;
40
+ style.textContent = themeCSS;
41
+ document.head.appendChild(style);
42
+ }
43
+ });
44
+
45
+ $effect(() => {
46
+ if (typeof window === 'undefined') return;
47
+
48
+ mounted = true;
49
+ panels = DialStore.getPanels();
50
+
51
+ const unsub = DialStore.subscribeGlobal(() => {
52
+ panels = DialStore.getPanels();
53
+ });
54
+
55
+ return unsub;
56
+ });
57
+ </script>
58
+
59
+ {#if productionEnabled && mounted && panels.length > 0}
60
+ {#snippet content()}
61
+ <ShortcutListener>
62
+ <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}
67
+ </div>
68
+ </div>
69
+ </ShortcutListener>
70
+ {/snippet}
71
+
72
+ {#if inline}
73
+ {@render content()}
74
+ {:else}
75
+ <Portal target="body">
76
+ {@render content()}
77
+ </Portal>
78
+ {/if}
79
+ {/if}
@@ -0,0 +1,14 @@
1
+ export type DialPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
2
+ export type DialMode = 'popover' | 'inline';
3
+ export type DialTheme = 'light' | 'dark' | 'system';
4
+ type $$ComponentProps = {
5
+ position?: DialPosition;
6
+ defaultOpen?: boolean;
7
+ mode?: DialMode;
8
+ theme?: DialTheme;
9
+ productionEnabled?: boolean;
10
+ };
11
+ declare const DialRoot: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type DialRoot = ReturnType<typeof DialRoot>;
13
+ export default DialRoot;
14
+ //# sourceMappingURL=DialRoot.svelte.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,50 @@
1
+ <script lang="ts">
2
+ import type { EasingConfig } from 'dialkit/store';
3
+
4
+ let { easing } = $props<{ easing: EasingConfig }>();
5
+
6
+ export const easingPresets: Record<string, [number, number, number, number]> = {
7
+ linear: [0, 0, 1, 1],
8
+ easeIn: [0.42, 0, 1, 1],
9
+ easeOut: [0, 0, 0.58, 1],
10
+ easeInOut: [0.42, 0, 0.58, 1],
11
+ };
12
+
13
+ const s = 200;
14
+ const pad = 10;
15
+ const inner = s - pad * 2;
16
+ const unit = inner / 2;
17
+
18
+ const toSvg = (nx: number, ny: number) => ({
19
+ x: pad + (nx + 0.5) * unit,
20
+ y: pad + (1.5 - ny) * unit,
21
+ });
22
+
23
+ const points = $derived.by(() => {
24
+ const ease = easing.ease;
25
+ const start = toSvg(0, 0);
26
+ const end = toSvg(1, 1);
27
+ const p1 = toSvg(ease[0], ease[1]);
28
+ const p2 = toSvg(ease[2], ease[3]);
29
+
30
+ return {
31
+ start,
32
+ end,
33
+ curvePath: `M ${start.x} ${start.y} C ${p1.x} ${p1.y}, ${p2.x} ${p2.y}, ${end.x} ${end.y}`,
34
+ };
35
+ });
36
+ </script>
37
+
38
+ <svg viewBox={`0 0 ${s} ${s}`} preserveAspectRatio="xMidYMid slice" class="dialkit-spring-viz dialkit-easing-viz">
39
+ <line
40
+ x1={points.start.x}
41
+ y1={points.start.y}
42
+ x2={points.end.x}
43
+ y2={points.end.y}
44
+ stroke="rgba(255, 255, 255, 0.15)"
45
+ stroke-width="1"
46
+ stroke-dasharray="4,4"
47
+ />
48
+
49
+ <path d={points.curvePath} fill="none" stroke="rgba(255, 255, 255, 0.6)" stroke-width="2" stroke-linecap="round" />
50
+ </svg>
@@ -0,0 +1,10 @@
1
+ import type { EasingConfig } from 'dialkit/store';
2
+ type $$ComponentProps = {
3
+ easing: EasingConfig;
4
+ };
5
+ declare const EasingVisualization: import("svelte").Component<$$ComponentProps, {
6
+ easingPresets: Record<string, [number, number, number, number]>;
7
+ }, "">;
8
+ type EasingVisualization = ReturnType<typeof EasingVisualization>;
9
+ export default EasingVisualization;
10
+ //# sourceMappingURL=EasingVisualization.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EasingVisualization.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EasingVisualization.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEjD,KAAK,gBAAgB,GAAG;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AA8ClD,QAAA,MAAM,mBAAmB;mBADyF,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;MACjG,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,213 @@
1
+ <script lang="ts">
2
+ import { Spring } from 'svelte/motion';
3
+ import { slide } from 'svelte/transition';
4
+
5
+ import type { Snippet } from 'svelte';
6
+ import { ICON_PANEL, ICON_CHEVRON } from '../../icons';
7
+
8
+ let {
9
+ title,
10
+ defaultOpen = true,
11
+ isRoot = false,
12
+ inline = false,
13
+ onOpenChange,
14
+ toolbar,
15
+ children,
16
+ } = $props<{
17
+ title: string;
18
+ defaultOpen?: boolean;
19
+ isRoot?: boolean;
20
+ inline?: boolean;
21
+ onOpenChange?: (isOpen: boolean) => void;
22
+ toolbar?: Snippet;
23
+ children?: Snippet;
24
+ }>();
25
+
26
+ let isOpen = $state(defaultOpen);
27
+ let isCollapsed = $state(!defaultOpen);
28
+ let contentHeight = $state<number | undefined>(undefined);
29
+
30
+ let contentRef: HTMLDivElement | undefined;
31
+ let panelRef: HTMLDivElement | undefined;
32
+ let windowHeight = $state(typeof window !== 'undefined' ? window.innerHeight : 800);
33
+
34
+ $effect(() => {
35
+ if (!isRoot) return;
36
+ const onResize = () => { windowHeight = window.innerHeight; };
37
+ window.addEventListener('resize', onResize);
38
+ return () => window.removeEventListener('resize', onResize);
39
+ });
40
+
41
+ const chevronRotation = new Spring(defaultOpen ? 0 : 180, { stiffness: 0.2, damping: 0.6 });
42
+ const panelWidth = new Spring(defaultOpen ? 280 : 42, { stiffness: 0.2, damping: 0.62 });
43
+ const panelHeight = new Spring(defaultOpen ? 220 : 42, { stiffness: 0.2, damping: 0.62 });
44
+ const panelRadius = new Spring(defaultOpen ? 14 : 21, { stiffness: 0.2, damping: 0.62 });
45
+ const panelScale = new Spring(1, { stiffness: 0.25, damping: 0.7 });
46
+
47
+ $effect(() => {
48
+ if (!isRoot || !contentRef || typeof ResizeObserver === 'undefined') return;
49
+
50
+ const ro = new ResizeObserver(() => {
51
+ if (!isOpen) return;
52
+ const next = contentRef?.offsetHeight;
53
+ if (!next) return;
54
+ contentHeight = next;
55
+ });
56
+
57
+ ro.observe(contentRef);
58
+
59
+ if (contentRef.offsetHeight > 0) {
60
+ contentHeight = contentRef.offsetHeight;
61
+ }
62
+
63
+ return () => {
64
+ ro.disconnect();
65
+ };
66
+ });
67
+
68
+ $effect(() => {
69
+ if (isRoot) return;
70
+ chevronRotation.set(isOpen ? 0 : 180);
71
+ });
72
+
73
+ $effect(() => {
74
+ if (!isRoot) return;
75
+
76
+ const measured = contentHeight ?? panelRef?.getBoundingClientRect().height ?? 42;
77
+ const nextHeight = isOpen ? Math.min(measured + 10, windowHeight - 32) : 42;
78
+
79
+ panelWidth.set(isOpen ? 280 : 42);
80
+ panelHeight.set(nextHeight);
81
+ panelRadius.set(isOpen ? 14 : 21);
82
+ });
83
+
84
+ const handleToggle = () => {
85
+ if (inline && isRoot) return;
86
+ const next = !isOpen;
87
+ isOpen = next;
88
+ isCollapsed = !next;
89
+ onOpenChange?.(next);
90
+ };
91
+
92
+ const handleCollapsedTapStart = () => {
93
+ if (isOpen) return;
94
+ (document.activeElement as HTMLElement | null)?.blur?.();
95
+ panelScale.set(0.9);
96
+ };
97
+
98
+ const handleCollapsedTapEnd = () => {
99
+ if (isOpen) return;
100
+ panelScale.set(1);
101
+ };
102
+
103
+ const panelStyle = $derived(
104
+ `width:${panelWidth.current}px;height:${panelHeight.current}px;border-radius:${panelRadius.current}px;` +
105
+ `box-shadow:${isOpen ? 'var(--dial-shadow)' : 'var(--dial-shadow-collapsed)'};` +
106
+ `cursor:${isOpen ? '' : 'pointer'};overflow:${isOpen ? 'hidden auto' : 'hidden'};` +
107
+ `transform:scale(${panelScale.current});`
108
+ );
109
+ </script>
110
+
111
+ {#if isRoot && inline}
112
+ <div class="dialkit-panel-inner dialkit-panel-inline">
113
+ <div bind:this={contentRef} class="dialkit-folder dialkit-folder-root">
114
+ <div class="dialkit-folder-header dialkit-panel-header" onclick={(e) => { e.stopPropagation(); handleToggle(); }}>
115
+ <div class="dialkit-folder-header-top">
116
+ <div class="dialkit-folder-title-row">
117
+ <span class="dialkit-folder-title dialkit-folder-title-root">{title}</span>
118
+ </div>
119
+ </div>
120
+
121
+ <div class="dialkit-panel-toolbar" onclick={(e) => e.stopPropagation()}>
122
+ {#if toolbar}{@render toolbar()}{/if}
123
+ </div>
124
+ </div>
125
+
126
+ <div class="dialkit-folder-content">
127
+ <div class="dialkit-folder-inner">
128
+ {#if children}{@render children()}{/if}
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ {:else if isRoot}
134
+ <div
135
+ bind:this={panelRef}
136
+ class="dialkit-panel-inner"
137
+ data-collapsed={String(isCollapsed)}
138
+ style={panelStyle}
139
+ onpointerdown={handleCollapsedTapStart}
140
+ onpointerup={handleCollapsedTapEnd}
141
+ onpointercancel={handleCollapsedTapEnd}
142
+ onpointerleave={handleCollapsedTapEnd}
143
+ onclick={() => { if (!isOpen) handleToggle(); }}
144
+ >
145
+ <div bind:this={contentRef} class="dialkit-folder dialkit-folder-root">
146
+ <div class="dialkit-folder-header dialkit-panel-header" onclick={(e) => { e.stopPropagation(); handleToggle(); }}>
147
+ <div class="dialkit-folder-header-top">
148
+ {#if isOpen}
149
+ <div class="dialkit-folder-title-row">
150
+ <span class="dialkit-folder-title dialkit-folder-title-root">{title}</span>
151
+ </div>
152
+ {/if}
153
+
154
+ <svg class="dialkit-panel-icon" viewBox="0 0 16 16" fill="none">
155
+ <path
156
+ opacity="0.5"
157
+ d={ICON_PANEL.path}
158
+ fill="currentColor"
159
+ />
160
+ <circle cx={ICON_PANEL.circles[0].cx} cy={ICON_PANEL.circles[0].cy} r={ICON_PANEL.circles[0].r} fill="currentColor" stroke="currentColor" stroke-width="1.25" />
161
+ <circle cx={ICON_PANEL.circles[1].cx} cy={ICON_PANEL.circles[1].cy} r={ICON_PANEL.circles[1].r} fill="currentColor" stroke="currentColor" stroke-width="1.25" />
162
+ <circle cx={ICON_PANEL.circles[2].cx} cy={ICON_PANEL.circles[2].cy} r={ICON_PANEL.circles[2].r} fill="currentColor" stroke="currentColor" stroke-width="1.25" />
163
+ </svg>
164
+ </div>
165
+
166
+ {#if isOpen}
167
+ <div class="dialkit-panel-toolbar" onclick={(e) => e.stopPropagation()}>
168
+ {#if toolbar}{@render toolbar()}{/if}
169
+ </div>
170
+ {/if}
171
+ </div>
172
+
173
+ {#if isOpen}
174
+ <div class="dialkit-folder-content">
175
+ <div class="dialkit-folder-inner">
176
+ {#if children}{@render children()}{/if}
177
+ </div>
178
+ </div>
179
+ {/if}
180
+ </div>
181
+ </div>
182
+ {:else}
183
+ <div class="dialkit-folder">
184
+ <div class="dialkit-folder-header" onclick={handleToggle}>
185
+ <div class="dialkit-folder-header-top">
186
+ <div class="dialkit-folder-title-row">
187
+ <span class="dialkit-folder-title">{title}</span>
188
+ </div>
189
+
190
+ <svg
191
+ class="dialkit-folder-icon"
192
+ viewBox="0 0 24 24"
193
+ fill="none"
194
+ stroke="currentColor"
195
+ stroke-width="2.5"
196
+ stroke-linecap="round"
197
+ stroke-linejoin="round"
198
+ style:transform={`rotate(${chevronRotation.current}deg)`}
199
+ >
200
+ <path d={ICON_CHEVRON} />
201
+ </svg>
202
+ </div>
203
+ </div>
204
+
205
+ {#if isOpen}
206
+ <div class="dialkit-folder-content" style="clip-path: inset(0 -20px);" transition:slide={{ duration: 220 }}>
207
+ <div class="dialkit-folder-inner">
208
+ {#if children}{@render children()}{/if}
209
+ </div>
210
+ </div>
211
+ {/if}
212
+ </div>
213
+ {/if}
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ title: string;
4
+ defaultOpen?: boolean;
5
+ isRoot?: boolean;
6
+ inline?: boolean;
7
+ onOpenChange?: (isOpen: boolean) => void;
8
+ toolbar?: Snippet;
9
+ children?: Snippet;
10
+ };
11
+ declare const Folder: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Folder = ReturnType<typeof Folder>;
13
+ export default Folder;
14
+ //# sourceMappingURL=Folder.svelte.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,149 @@
1
+ <script lang="ts">
2
+ import { Spring } from 'svelte/motion';
3
+ import { DialStore } from 'dialkit/store';
4
+ import type { DialValue, PanelConfig, Preset } from 'dialkit/store';
5
+ import Folder from './Folder.svelte';
6
+ import PresetManager from './PresetManager.svelte';
7
+ import ControlRenderer from './ControlRenderer.svelte';
8
+ import ShortcutsMenu from './ShortcutsMenu.svelte';
9
+ import { ICON_CLIPBOARD, ICON_CHECK, ICON_ADD_PRESET } from '../../icons';
10
+
11
+ let { panel, defaultOpen = true, inline = false } = $props<{ panel: PanelConfig; defaultOpen?: boolean; inline?: boolean }>();
12
+
13
+ const hasShortcuts = $derived(Object.keys(panel.shortcuts).length > 0);
14
+
15
+ let copied = $state(false);
16
+ let isPanelOpen = $state(defaultOpen);
17
+ let values = $state<Record<string, DialValue>>(DialStore.getValues(panel.id));
18
+ let presets = $state<Preset[]>(DialStore.getPresets(panel.id));
19
+ let activePresetId = $state<string | null>(DialStore.getActivePresetId(panel.id));
20
+
21
+ const addScale = new Spring(1, { stiffness: 0.25, damping: 0.7 });
22
+ const copyScale = new Spring(1, { stiffness: 0.25, damping: 0.7 });
23
+ const clipboardOpacity = new Spring(1, { stiffness: 0.25, damping: 0.7 });
24
+ const clipboardScale = new Spring(1, { stiffness: 0.2, damping: 0.6 });
25
+ const checkOpacity = new Spring(0, { stiffness: 0.25, damping: 0.7 });
26
+ const checkScale = new Spring(0.5, { stiffness: 0.2, damping: 0.6 });
27
+
28
+ let copyTimeout: ReturnType<typeof setTimeout> | undefined;
29
+
30
+ $effect(() => {
31
+ const unsub = DialStore.subscribe(panel.id, () => {
32
+ values = DialStore.getValues(panel.id);
33
+ presets = DialStore.getPresets(panel.id);
34
+ activePresetId = DialStore.getActivePresetId(panel.id);
35
+ });
36
+
37
+ return () => {
38
+ unsub();
39
+ if (copyTimeout) clearTimeout(copyTimeout);
40
+ };
41
+ });
42
+
43
+ $effect(() => {
44
+ if (copied) {
45
+ clipboardOpacity.set(0);
46
+ clipboardScale.set(0.5);
47
+ checkOpacity.set(1);
48
+ checkScale.set(1);
49
+ return;
50
+ }
51
+
52
+ clipboardOpacity.set(1);
53
+ clipboardScale.set(1);
54
+ checkOpacity.set(0);
55
+ checkScale.set(0.5);
56
+ });
57
+
58
+ const handleAddPreset = () => {
59
+ const nextNum = presets.length + 2;
60
+ DialStore.savePreset(panel.id, `Version ${nextNum}`);
61
+ };
62
+
63
+ const handleCopy = async () => {
64
+ const jsonStr = JSON.stringify(values, null, 2);
65
+ const instruction = `Update the createDialKit configuration for "${panel.name}" with these values:\n\n\`\`\`json\n${jsonStr}\n\`\`\`\n\nApply these values as the new defaults in the createDialKit call.`;
66
+
67
+ await navigator.clipboard.writeText(instruction);
68
+ copied = true;
69
+
70
+ if (copyTimeout) clearTimeout(copyTimeout);
71
+ copyTimeout = setTimeout(() => {
72
+ copied = false;
73
+ }, 1500);
74
+ };
75
+ </script>
76
+
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})`}
89
+ >
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})`}
110
+ >
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
+ </Folder>
149
+ </div>