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.
- package/README.md +220 -3
- package/dist/dropdown-position.d.ts +15 -0
- package/dist/dropdown-position.js +22 -0
- package/dist/dropdown-position.js.map +1 -0
- package/dist/index.cjs +697 -307
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -5
- package/dist/index.d.ts +42 -5
- package/dist/index.js +720 -331
- package/dist/index.js.map +1 -1
- package/dist/panel-drag.d.ts +19 -0
- package/dist/panel-drag.js +72 -0
- package/dist/panel-drag.js.map +1 -0
- package/dist/solid/index.cjs +661 -204
- package/dist/solid/index.cjs.map +1 -1
- package/dist/solid/index.d.cts +40 -3
- package/dist/solid/index.d.ts +40 -3
- package/dist/solid/index.js +646 -190
- package/dist/solid/index.js.map +1 -1
- package/dist/store/index.cjs +272 -41
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +30 -3
- package/dist/store/index.d.ts +30 -3
- package/dist/store/index.js +269 -40
- package/dist/store/index.js.map +1 -1
- package/dist/styles.css +105 -7
- package/dist/svelte/components/DialRoot.svelte +176 -11
- package/dist/svelte/components/DialRoot.svelte.d.ts +1 -0
- package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -1
- package/dist/svelte/components/Folder.svelte +24 -13
- package/dist/svelte/components/Folder.svelte.d.ts +1 -0
- package/dist/svelte/components/Folder.svelte.d.ts.map +1 -1
- package/dist/svelte/components/Panel.svelte +98 -71
- package/dist/svelte/components/Panel.svelte.d.ts +2 -0
- package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
- package/dist/svelte/components/PresetManager.svelte +5 -5
- package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -1
- package/dist/svelte/components/SelectControl.svelte +6 -14
- package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -1
- package/dist/svelte/createDialKit.svelte.d.ts +11 -1
- package/dist/svelte/createDialKit.svelte.d.ts.map +1 -1
- package/dist/svelte/createDialKit.svelte.js +61 -34
- package/dist/svelte/index.d.ts +3 -3
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -1
- package/dist/svelte/theme-css.d.ts +1 -1
- package/dist/svelte/theme-css.d.ts.map +1 -1
- package/dist/svelte/theme-css.js +105 -7
- package/dist/vue/index.cjs +573 -132
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.cts +53 -6
- package/dist/vue/index.d.ts +53 -6
- package/dist/vue/index.js +573 -133
- package/dist/vue/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -72,6 +72,8 @@ const params = useDialKit(name, config, options?)
|
|
|
72
72
|
|-------|------|-------------|
|
|
73
73
|
| `name` | `string` | Panel title displayed in the UI |
|
|
74
74
|
| `config` | `DialConfig` | Parameter definitions (see Control Types below) |
|
|
75
|
+
| `options.id` | `string` | Stable logical id for sharing values across remounts/pages |
|
|
76
|
+
| `options.persist` | `DialKitPersistOptions` | Persist values to browser storage |
|
|
75
77
|
| `options.onAction` | `(path: string) => void` | Callback when action buttons are clicked |
|
|
76
78
|
| `options.shortcuts` | `Record<string, ShortcutConfig>` | Keyboard shortcuts for controls (see [Keyboard Shortcuts](#keyboard-shortcuts)) |
|
|
77
79
|
|
|
@@ -79,6 +81,122 @@ Returns a fully typed object matching your config shape with live values. Updati
|
|
|
79
81
|
|
|
80
82
|
---
|
|
81
83
|
|
|
84
|
+
## Stable IDs and Persistence
|
|
85
|
+
|
|
86
|
+
By default, a DialKit panel is tied to the lifecycle of the component that calls `useDialKit`. Pass `id` when multiple mounts should reconnect to the same logical panel, and pass `persist: true` when values should survive reloads and browser sessions.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// dials/useOnboardingDials.ts
|
|
90
|
+
import { useDialKit } from 'dialkit';
|
|
91
|
+
|
|
92
|
+
export function useOnboardingDials() {
|
|
93
|
+
return useDialKit('Onboarding', {
|
|
94
|
+
name: { type: 'text', default: 'Avery', placeholder: 'Name' },
|
|
95
|
+
avatarScale: [1, 0.6, 1.6, 0.01],
|
|
96
|
+
accent: { type: 'color', default: '#6C5CE7' },
|
|
97
|
+
}, {
|
|
98
|
+
id: 'onboarding',
|
|
99
|
+
persist: true,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Use that helper anywhere the shared values are needed:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
function PageTwo() {
|
|
108
|
+
const onboarding = useOnboardingDials();
|
|
109
|
+
|
|
110
|
+
const page = useDialKit('Page Two', {
|
|
111
|
+
cardRadius: [16, 0, 64],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<Card
|
|
116
|
+
name={onboarding.name}
|
|
117
|
+
radius={page.cardRadius}
|
|
118
|
+
accent={onboarding.accent}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
When `PageTwo` is mounted, the single `<DialRoot />` shows both `Onboarding` and `Page Two` as top-level sections. If another page calls `useOnboardingDials()`, DialKit reconnects to the same `id` and keeps the shared values.
|
|
125
|
+
|
|
126
|
+
`persist: true` stores values, presets, and the active preset in `localStorage` using `dialkit:${id}` as the key. Use the object form to customize storage:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
useDialKit('Onboarding', config, {
|
|
130
|
+
id: 'onboarding',
|
|
131
|
+
persist: {
|
|
132
|
+
key: 'my-app:onboarding-dials',
|
|
133
|
+
storage: 'sessionStorage',
|
|
134
|
+
presets: false,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The `id` string has no special format; it only needs to be reused wherever you want the same logical panel. Without `id` or `persist`, DialKit behaves exactly as before.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## useDialKitController
|
|
144
|
+
|
|
145
|
+
Use the controller API when your app code also needs to update DialKit values, such as reset buttons, URL sync, or app-defined preset buttons.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { useDialKitController } from 'dialkit';
|
|
149
|
+
|
|
150
|
+
function Card() {
|
|
151
|
+
const dial = useDialKitController('Card', {
|
|
152
|
+
blur: [24, 0, 100],
|
|
153
|
+
scale: 1.2,
|
|
154
|
+
color: '#ff5500',
|
|
155
|
+
visible: true,
|
|
156
|
+
shadow: {
|
|
157
|
+
radius: [16, 0, 64],
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<>
|
|
163
|
+
<button onClick={() => dial.setValues({
|
|
164
|
+
blur: 48,
|
|
165
|
+
scale: 1,
|
|
166
|
+
shadow: { radius: 28 },
|
|
167
|
+
})}>
|
|
168
|
+
Apply preset
|
|
169
|
+
</button>
|
|
170
|
+
<button onClick={() => dial.resetValues()}>Reset</button>
|
|
171
|
+
|
|
172
|
+
<div style={{
|
|
173
|
+
filter: `blur(${dial.values.blur}px)`,
|
|
174
|
+
transform: `scale(${dial.values.scale})`,
|
|
175
|
+
color: dial.values.color,
|
|
176
|
+
opacity: dial.values.visible ? 1 : 0,
|
|
177
|
+
borderRadius: dial.values.shadow.radius,
|
|
178
|
+
}}>
|
|
179
|
+
...
|
|
180
|
+
</div>
|
|
181
|
+
</>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Controller methods:
|
|
187
|
+
|
|
188
|
+
| Method | Description |
|
|
189
|
+
|--------|-------------|
|
|
190
|
+
| `values` | The same live resolved values returned by `useDialKit` |
|
|
191
|
+
| `setValue(path, value)` | Updates one control by dot path, like `'shadow.radius'` |
|
|
192
|
+
| `setValues(values)` | Updates multiple controls with a typed nested partial object |
|
|
193
|
+
| `resetValues()` | Restores the current config defaults and clears the active preset |
|
|
194
|
+
| `getValues()` | Reads the latest resolved values outside render callbacks |
|
|
195
|
+
|
|
196
|
+
Programmatic updates use the same state as panel edits. If a saved preset is active, updates are saved into that preset; otherwise they update the base "Version 1" values. Action controls are triggers, so they are not set by `setValues`.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
82
200
|
## Control Types
|
|
83
201
|
|
|
84
202
|
### Slider
|
|
@@ -269,9 +387,47 @@ const values = useDialKit('Controls', {
|
|
|
269
387
|
| `defaultOpen` | `boolean` | `true` |
|
|
270
388
|
| `mode` | `'popover' \| 'inline'` | `'popover'` |
|
|
271
389
|
| `productionEnabled` | `boolean` | `false` in production, `true` otherwise |
|
|
390
|
+
| `onOpenChange` | `(open: boolean) => void` | `undefined` |
|
|
272
391
|
|
|
273
392
|
Mount once at your app root. In the default `popover` mode, the panel renders via a portal on `document.body`. It collapses to a small icon button and expands to 280px wide on click.
|
|
274
393
|
|
|
394
|
+
### Multiple panels
|
|
395
|
+
|
|
396
|
+
If multiple `useDialKit` calls are registered under the same root, DialKit renders one shared shell and shows each panel as a collapsible top-level section. No extra API is needed:
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
function PhotoStack() {
|
|
400
|
+
const photo = useDialKit('Photo Stack', {
|
|
401
|
+
blur: [12, 0, 40],
|
|
402
|
+
scale: [1, 0.5, 2],
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const stage = useDialKit('Stage', {
|
|
406
|
+
pagePadding: [40, 16, 96],
|
|
407
|
+
background: '#ffffff',
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<div style={{ padding: stage.pagePadding, background: stage.background }}>
|
|
412
|
+
<img style={{ filter: `blur(${photo.blur}px)`, transform: `scale(${photo.scale})` }} />
|
|
413
|
+
</div>
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
The mounted `<DialRoot />` stays the same. With a single registered panel, the panel title and layout stay unchanged. This behavior works the same way in React, Solid, Svelte, and Vue.
|
|
419
|
+
|
|
420
|
+
Use `onOpenChange` when you need to persist whether the floating panel is open or collapsed:
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
<DialRoot
|
|
424
|
+
defaultOpen={localStorage.getItem('dialkit-open') !== '0'}
|
|
425
|
+
onOpenChange={(open) => {
|
|
426
|
+
localStorage.setItem('dialkit-open', open ? '1' : '0');
|
|
427
|
+
}}
|
|
428
|
+
/>
|
|
429
|
+
```
|
|
430
|
+
|
|
275
431
|
DialKit is automatically hidden in production builds. To enable it in production, pass `productionEnabled`:
|
|
276
432
|
|
|
277
433
|
```tsx
|
|
@@ -513,7 +669,22 @@ function Card() {
|
|
|
513
669
|
}
|
|
514
670
|
```
|
|
515
671
|
|
|
516
|
-
`createDialKit` returns an accessor — call `params()` to read the current values. All control types, config shapes, and panel features (presets, copy, folders) work identically to the React version.
|
|
672
|
+
`createDialKit` returns an accessor — call `params()` to read the current values. All control types, config shapes, and panel features (presets, copy, folders, and `DialRoot` props like `onOpenChange`) work identically to the React version.
|
|
673
|
+
|
|
674
|
+
Use `createDialKitController` when Solid code needs to update values:
|
|
675
|
+
|
|
676
|
+
```tsx
|
|
677
|
+
import { createDialKitController } from 'dialkit/solid';
|
|
678
|
+
|
|
679
|
+
const dial = createDialKitController('Card', {
|
|
680
|
+
blur: [24, 0, 100],
|
|
681
|
+
shadow: { radius: [16, 0, 64] },
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
dial.setValues({ blur: 48, shadow: { radius: 28 } });
|
|
685
|
+
dial.resetValues();
|
|
686
|
+
dial.values().blur;
|
|
687
|
+
```
|
|
517
688
|
|
|
518
689
|
---
|
|
519
690
|
|
|
@@ -554,7 +725,30 @@ npm install dialkit
|
|
|
554
725
|
</div>
|
|
555
726
|
```
|
|
556
727
|
|
|
557
|
-
`createDialKit` returns a reactive object — access values directly (e.g. `params.blur`). Styles are injected automatically by `DialRoot` (no CSS import needed). Cleanup is automatic when the component unmounts. All control types, presets, folders, and
|
|
728
|
+
`createDialKit` returns a reactive object — access values directly (e.g. `params.blur`). Styles are injected automatically by `DialRoot` (no CSS import needed). Cleanup is automatic when the component unmounts. All control types, presets, folders, transitions, and `DialRoot` props like `onOpenChange` match the React/Solid entries.
|
|
729
|
+
|
|
730
|
+
Use `createDialKitController` when Svelte code needs to update values:
|
|
731
|
+
|
|
732
|
+
```svelte
|
|
733
|
+
<script>
|
|
734
|
+
import { createDialKitController } from 'dialkit/svelte';
|
|
735
|
+
|
|
736
|
+
const dial = createDialKitController('Card', {
|
|
737
|
+
blur: [24, 0, 100],
|
|
738
|
+
shadow: { radius: [16, 0, 64] }
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
const params = dial.values;
|
|
742
|
+
</script>
|
|
743
|
+
|
|
744
|
+
<button onclick={() => dial.setValues({ blur: 48, shadow: { radius: 28 } })}>
|
|
745
|
+
Apply preset
|
|
746
|
+
</button>
|
|
747
|
+
|
|
748
|
+
<div style:filter={`blur(${params.blur}px)`} style:border-radius={`${params.shadow.radius}px`}>
|
|
749
|
+
...
|
|
750
|
+
</div>
|
|
751
|
+
```
|
|
558
752
|
|
|
559
753
|
---
|
|
560
754
|
|
|
@@ -586,7 +780,7 @@ import Card from './Card.vue';
|
|
|
586
780
|
|
|
587
781
|
<template>
|
|
588
782
|
<Card />
|
|
589
|
-
<DialRoot />
|
|
783
|
+
<DialRoot @open-change="(open) => localStorage.setItem('dialkit-open', open ? '1' : '0')" />
|
|
590
784
|
</template>
|
|
591
785
|
```
|
|
592
786
|
|
|
@@ -617,6 +811,25 @@ const params = useDialKit('Card', {
|
|
|
617
811
|
|
|
618
812
|
`useDialKit` returns a reactive object. All control types, presets, folders, keyboard shortcuts, and transitions work identically to the other frameworks.
|
|
619
813
|
|
|
814
|
+
Use `useDialKitController` when Vue code needs to update values:
|
|
815
|
+
|
|
816
|
+
```vue
|
|
817
|
+
<script setup>
|
|
818
|
+
import { useDialKitController } from 'dialkit/vue';
|
|
819
|
+
|
|
820
|
+
const { values, setValues, resetValues } = useDialKitController('Card', {
|
|
821
|
+
blur: [24, 0, 100],
|
|
822
|
+
shadow: { radius: [16, 0, 64] },
|
|
823
|
+
});
|
|
824
|
+
</script>
|
|
825
|
+
|
|
826
|
+
<template>
|
|
827
|
+
<button @click="setValues({ blur: 48, shadow: { radius: 28 } })">Apply preset</button>
|
|
828
|
+
<button @click="resetValues()">Reset</button>
|
|
829
|
+
<div :style="{ filter: `blur(${values.blur}px)`, borderRadius: `${values.shadow.radius}px` }" />
|
|
830
|
+
</template>
|
|
831
|
+
```
|
|
832
|
+
|
|
620
833
|
---
|
|
621
834
|
|
|
622
835
|
## Types
|
|
@@ -626,6 +839,8 @@ All config and value types are exported:
|
|
|
626
839
|
```tsx
|
|
627
840
|
import type {
|
|
628
841
|
SpringConfig,
|
|
842
|
+
EasingConfig,
|
|
843
|
+
TransitionConfig,
|
|
629
844
|
ActionConfig,
|
|
630
845
|
SelectConfig,
|
|
631
846
|
ColorConfig,
|
|
@@ -634,6 +849,8 @@ import type {
|
|
|
634
849
|
ShortcutMode,
|
|
635
850
|
DialConfig,
|
|
636
851
|
DialValue,
|
|
852
|
+
DialKitController,
|
|
853
|
+
DialKitValueUpdates,
|
|
637
854
|
ResolvedValues,
|
|
638
855
|
ControlMeta,
|
|
639
856
|
PanelConfig,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type DropdownPosition = {
|
|
2
|
+
top: number;
|
|
3
|
+
left: number;
|
|
4
|
+
width: number;
|
|
5
|
+
above: boolean;
|
|
6
|
+
};
|
|
7
|
+
type DropdownPositionOptions = {
|
|
8
|
+
dropdownHeight?: number;
|
|
9
|
+
gap?: number;
|
|
10
|
+
allowAbove?: boolean;
|
|
11
|
+
};
|
|
12
|
+
declare function getDropdownPosition(trigger: HTMLElement, portalRoot: HTMLElement, options?: DropdownPositionOptions): DropdownPosition;
|
|
13
|
+
declare function getDialKitPortalRoot(trigger: HTMLElement | null | undefined): HTMLElement | null;
|
|
14
|
+
|
|
15
|
+
export { type DropdownPosition, type DropdownPositionOptions, getDialKitPortalRoot, getDropdownPosition };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/dropdown-position.ts
|
|
2
|
+
function getDropdownPosition(trigger, portalRoot, options = {}) {
|
|
3
|
+
const { dropdownHeight = 0, gap = 4, allowAbove = true } = options;
|
|
4
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
5
|
+
const rootRect = portalRoot.getBoundingClientRect();
|
|
6
|
+
const spaceBelow = window.innerHeight - triggerRect.bottom - gap;
|
|
7
|
+
const above = allowAbove && spaceBelow < dropdownHeight && triggerRect.top > spaceBelow;
|
|
8
|
+
return {
|
|
9
|
+
top: above ? triggerRect.top - rootRect.top - dropdownHeight - gap : triggerRect.bottom - rootRect.top + gap,
|
|
10
|
+
left: triggerRect.left - rootRect.left,
|
|
11
|
+
width: triggerRect.width,
|
|
12
|
+
above
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function getDialKitPortalRoot(trigger) {
|
|
16
|
+
return trigger?.closest(".dialkit-root") ?? null;
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
getDialKitPortalRoot,
|
|
20
|
+
getDropdownPosition
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=dropdown-position.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dropdown-position.ts"],"sourcesContent":["export type DropdownPosition = {\n top: number;\n left: number;\n width: number;\n above: boolean;\n};\n\nexport type DropdownPositionOptions = {\n dropdownHeight?: number;\n gap?: number;\n allowAbove?: boolean;\n};\n\nexport function getDropdownPosition(\n trigger: HTMLElement,\n portalRoot: HTMLElement,\n options: DropdownPositionOptions = {}\n): DropdownPosition {\n const { dropdownHeight = 0, gap = 4, allowAbove = true } = options;\n const triggerRect = trigger.getBoundingClientRect();\n const rootRect = portalRoot.getBoundingClientRect();\n const spaceBelow = window.innerHeight - triggerRect.bottom - gap;\n const above = allowAbove && spaceBelow < dropdownHeight && triggerRect.top > spaceBelow;\n\n return {\n top: above\n ? triggerRect.top - rootRect.top - dropdownHeight - gap\n : triggerRect.bottom - rootRect.top + gap,\n left: triggerRect.left - rootRect.left,\n width: triggerRect.width,\n above,\n };\n}\n\nexport function getDialKitPortalRoot(trigger: HTMLElement | null | undefined): HTMLElement | null {\n return (trigger?.closest('.dialkit-root') as HTMLElement | null) ?? null;\n}\n"],"mappings":";AAaO,SAAS,oBACd,SACA,YACA,UAAmC,CAAC,GAClB;AAClB,QAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,aAAa,KAAK,IAAI;AAC3D,QAAM,cAAc,QAAQ,sBAAsB;AAClD,QAAM,WAAW,WAAW,sBAAsB;AAClD,QAAM,aAAa,OAAO,cAAc,YAAY,SAAS;AAC7D,QAAM,QAAQ,cAAc,aAAa,kBAAkB,YAAY,MAAM;AAE7E,SAAO;AAAA,IACL,KAAK,QACD,YAAY,MAAM,SAAS,MAAM,iBAAiB,MAClD,YAAY,SAAS,SAAS,MAAM;AAAA,IACxC,MAAM,YAAY,OAAO,SAAS;AAAA,IAClC,OAAO,YAAY;AAAA,IACnB;AAAA,EACF;AACF;AAEO,SAAS,qBAAqB,SAA6D;AAChG,SAAQ,SAAS,QAAQ,eAAe,KAA4B;AACtE;","names":[]}
|