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.
- package/README.md +653 -6
- 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/icons.d.ts +4 -1
- package/dist/icons.js +13 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +3796 -768
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +315 -10
- package/dist/index.d.ts +315 -10
- package/dist/index.js +3733 -711
- 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.d.ts +264 -5
- package/dist/solid/index.js +5277 -1632
- package/dist/solid/index.js.map +1 -1
- package/dist/store/index.cjs +329 -57
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +43 -4
- package/dist/store/index.d.ts +43 -4
- package/dist/store/index.js +321 -56
- package/dist/store/index.js.map +1 -1
- package/dist/styles.css +809 -7
- package/dist/svelte/components/ControlRenderer.svelte +5 -2
- package/dist/svelte/components/ControlRenderer.svelte.d.ts +2 -0
- package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/DialRoot.svelte +218 -16
- 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 +104 -71
- package/dist/svelte/components/Panel.svelte.d.ts +4 -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/components/Timeline/ClipPopover.svelte +206 -0
- package/dist/svelte/components/Timeline/ClipPopover.svelte.d.ts +26 -0
- package/dist/svelte/components/Timeline/ClipPopover.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/DialTimeline.svelte +76 -0
- package/dist/svelte/components/Timeline/DialTimeline.svelte.d.ts +13 -0
- package/dist/svelte/components/Timeline/DialTimeline.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/TimelineClip.svelte +233 -0
- package/dist/svelte/components/Timeline/TimelineClip.svelte.d.ts +24 -0
- package/dist/svelte/components/Timeline/TimelineClip.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/TimelineSection.svelte +756 -0
- package/dist/svelte/components/Timeline/TimelineSection.svelte.d.ts +12 -0
- package/dist/svelte/components/Timeline/TimelineSection.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Timeline/TimelineToggleButton.svelte +25 -0
- package/dist/svelte/components/Timeline/TimelineToggleButton.svelte.d.ts +4 -0
- package/dist/svelte/components/Timeline/TimelineToggleButton.svelte.d.ts.map +1 -0
- package/dist/svelte/components/TransitionControl.svelte +26 -11
- package/dist/svelte/components/TransitionControl.svelte.d.ts +9 -0
- package/dist/svelte/components/TransitionControl.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/createDialTimeline.svelte.d.ts +4 -0
- package/dist/svelte/createDialTimeline.svelte.d.ts.map +1 -0
- package/dist/svelte/createDialTimeline.svelte.js +73 -0
- package/dist/svelte/index.d.ts +7 -3
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +4 -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 +809 -7
- package/dist/timeline/index.cjs +1288 -0
- package/dist/timeline/index.cjs.map +1 -0
- package/dist/timeline/index.d.cts +443 -0
- package/dist/timeline/index.d.ts +443 -0
- package/dist/timeline/index.js +1233 -0
- package/dist/timeline/index.js.map +1 -0
- package/dist/vue/index.d.ts +325 -12
- package/dist/vue/index.js +3226 -280
- package/dist/vue/index.js.map +1 -1
- package/package.json +23 -13
- package/dist/solid/index.cjs +0 -3079
- package/dist/solid/index.cjs.map +0 -1
- package/dist/solid/index.d.cts +0 -258
- package/dist/vue/index.cjs +0 -3056
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.cts +0 -675
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# dialkit v1.
|
|
1
|
+
# dialkit v1.4.0
|
|
2
2
|
|
|
3
3
|
<img src="https://joshpuckett.me/images/dialkit.png" width="100%" />
|
|
4
4
|
|
|
5
|
-
Real-time parameter tweaking for React, Solid, Svelte, and Vue, created by Josh Puckett.
|
|
5
|
+
Real-time parameter tweaking for React, Solid, Svelte, and Vue, created by Josh Puckett. Now with a scrubbable animation [Timeline](#timeline).
|
|
6
6
|
|
|
7
7
|
To learn more about how I use DialKit, and approach design in general, feel free to check out [Interface Craft](http://interfacecraft.dev/).
|
|
8
8
|
|
|
@@ -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
|
|
@@ -268,10 +386,49 @@ const values = useDialKit('Controls', {
|
|
|
268
386
|
| `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'top-right'` |
|
|
269
387
|
| `defaultOpen` | `boolean` | `true` |
|
|
270
388
|
| `mode` | `'popover' \| 'inline'` | `'popover'` |
|
|
389
|
+
| `theme` | `'system' \| 'light' \| 'dark'` | `'system'` |
|
|
271
390
|
| `productionEnabled` | `boolean` | `false` in production, `true` otherwise |
|
|
391
|
+
| `onOpenChange` | `(open: boolean) => void` | `undefined` |
|
|
272
392
|
|
|
273
393
|
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
394
|
|
|
395
|
+
### Multiple panels
|
|
396
|
+
|
|
397
|
+
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:
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
function PhotoStack() {
|
|
401
|
+
const photo = useDialKit('Photo Stack', {
|
|
402
|
+
blur: [12, 0, 40],
|
|
403
|
+
scale: [1, 0.5, 2],
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const stage = useDialKit('Stage', {
|
|
407
|
+
pagePadding: [40, 16, 96],
|
|
408
|
+
background: '#ffffff',
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return (
|
|
412
|
+
<div style={{ padding: stage.pagePadding, background: stage.background }}>
|
|
413
|
+
<img style={{ filter: `blur(${photo.blur}px)`, transform: `scale(${photo.scale})` }} />
|
|
414
|
+
</div>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
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.
|
|
420
|
+
|
|
421
|
+
Use `onOpenChange` when you need to persist whether the floating panel is open or collapsed:
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
<DialRoot
|
|
425
|
+
defaultOpen={localStorage.getItem('dialkit-open') !== '0'}
|
|
426
|
+
onOpenChange={(open) => {
|
|
427
|
+
localStorage.setItem('dialkit-open', open ? '1' : '0');
|
|
428
|
+
}}
|
|
429
|
+
/>
|
|
430
|
+
```
|
|
431
|
+
|
|
275
432
|
DialKit is automatically hidden in production builds. To enable it in production, pass `productionEnabled`:
|
|
276
433
|
|
|
277
434
|
```tsx
|
|
@@ -465,6 +622,350 @@ function PhotoStack() {
|
|
|
465
622
|
|
|
466
623
|
---
|
|
467
624
|
|
|
625
|
+
## Timeline
|
|
626
|
+
|
|
627
|
+
DialKit Timeline lets you define an animation in code, preview it, and tune its timing, values, and curves in a visual timeline.
|
|
628
|
+
|
|
629
|
+
The animation's structure stays in code. The timeline editor adjusts its properties without changing which clips loop, which clips form sequences, or how your application combines them.
|
|
630
|
+
|
|
631
|
+
Timeline is available in React, Solid, Svelte, and Vue. Every adapter uses the same framework-neutral timeline core and store, while lifecycle and rendering stay native to its framework.
|
|
632
|
+
|
|
633
|
+
```tsx
|
|
634
|
+
import { useDialTimeline, DialTimeline } from 'dialkit';
|
|
635
|
+
import 'dialkit/styles.css';
|
|
636
|
+
|
|
637
|
+
function Hero() {
|
|
638
|
+
const hero = useDialTimeline(
|
|
639
|
+
'Hero',
|
|
640
|
+
{
|
|
641
|
+
entrance: {
|
|
642
|
+
at: 0,
|
|
643
|
+
duration: 0.6,
|
|
644
|
+
from: { y: 32, opacity: 0 },
|
|
645
|
+
to: { y: 0, opacity: 1 },
|
|
646
|
+
transition: { type: 'spring', bounce: 0.2 },
|
|
647
|
+
},
|
|
648
|
+
idle: {
|
|
649
|
+
at: 0.8,
|
|
650
|
+
loop: true,
|
|
651
|
+
from: { y: 0 },
|
|
652
|
+
steps: [
|
|
653
|
+
{ duration: 1, to: { y: -6 } },
|
|
654
|
+
{ duration: 1, to: { y: 0 } },
|
|
655
|
+
],
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
{ loop: { from: 0.8 } }
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
const entrance = hero.entrance.current;
|
|
662
|
+
const idle = hero.idle.current;
|
|
663
|
+
|
|
664
|
+
return (
|
|
665
|
+
<>
|
|
666
|
+
<h1 style={{
|
|
667
|
+
opacity: entrance.opacity,
|
|
668
|
+
transform: `translateY(${entrance.y + idle.y}px)`,
|
|
669
|
+
}}>
|
|
670
|
+
Ship the moment.
|
|
671
|
+
</h1>
|
|
672
|
+
<DialTimeline />
|
|
673
|
+
</>
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Each named entry is a clip and appears as one row in the timeline. During authoring, bind its `current` values to your UI so the element always matches the playhead, whether the timeline is playing, paused, or being scrubbed. `current` is DialKit's deterministic preview of the configured curve; after tuning, Copy the settings into your app's real animation and remove the timeline hook.
|
|
679
|
+
|
|
680
|
+
The framework entry points expose the same config, values, transport, and `<DialTimeline />` dock:
|
|
681
|
+
|
|
682
|
+
| Framework | Import | Timeline function | Read returned values |
|
|
683
|
+
|-----------|--------|-------------------|----------------------|
|
|
684
|
+
| React | `dialkit` | `useDialTimeline` | `timeline.card.current` |
|
|
685
|
+
| Solid | `dialkit/solid` | `createDialTimeline` | `timeline().card.current` |
|
|
686
|
+
| Svelte 5 | `dialkit/svelte` | `createDialTimeline` | `timeline.card.current` |
|
|
687
|
+
| Vue 3 | `dialkit/vue` | `useDialTimeline` | `timeline.value.card.current` in script; auto-unwrapped in templates |
|
|
688
|
+
|
|
689
|
+
### Timeline function
|
|
690
|
+
|
|
691
|
+
```tsx
|
|
692
|
+
const tl = useDialTimeline(name, config, options?)
|
|
693
|
+
// Solid/Svelte: createDialTimeline(name, config, options?)
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
| Param | Type | Description |
|
|
697
|
+
|-------|------|-------------|
|
|
698
|
+
| `name` | `string` | Timeline title displayed in the dock |
|
|
699
|
+
| `config` | `TimelineConfig` | Clip definitions plus an optional top-level `duration` |
|
|
700
|
+
| `options.id` | `string` | Stable logical id, same semantics as `useDialKit` |
|
|
701
|
+
| `options.persist` | `DialKitPersistOptions` | Persist timing edits to browser storage |
|
|
702
|
+
| `options.autoplay` | `boolean` | Start playing on mount. Default `true` |
|
|
703
|
+
| `options.loop` | `boolean \| { from: number }` | Wrap the playhead when it reaches the end (see [Looping](#looping)) |
|
|
704
|
+
|
|
705
|
+
Clip timing lives in the same store as panel values, so presets, persistence, reset, and Copy all work on timing data with no extra wiring.
|
|
706
|
+
|
|
707
|
+
The returned object combines the transport with one entry per clip:
|
|
708
|
+
|
|
709
|
+
```tsx
|
|
710
|
+
tl.time // playhead in seconds
|
|
711
|
+
tl.playing // boolean
|
|
712
|
+
tl.duration // timeline length in seconds
|
|
713
|
+
tl.play() // resume (restarts if parked at the end)
|
|
714
|
+
tl.pause()
|
|
715
|
+
tl.replay() // seek to 0 and play
|
|
716
|
+
tl.seek(1.2) // move the playhead (pins the deterministic first-pass state)
|
|
717
|
+
|
|
718
|
+
tl.headline // TimelineClipValues for the "headline" clip
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
`time`, `playing`, `duration`, `play`, `pause`, `replay`, and `seek` are reserved — a clip with one of those names is skipped with a console warning.
|
|
722
|
+
|
|
723
|
+
### Clips
|
|
724
|
+
|
|
725
|
+
- Use `from` and `to` for one animation from one state to another.
|
|
726
|
+
- Use `steps` for several states played sequentially.
|
|
727
|
+
- Use `props` when properties need independent timing.
|
|
728
|
+
- Use separate named clips for separate behaviors, even when they affect the same element.
|
|
729
|
+
|
|
730
|
+
DialKit does not inspect your rendered elements or automatically combine animations. Your application decides how the values from different clips are used.
|
|
731
|
+
|
|
732
|
+
Every clip requires `at`, which sets its start time:
|
|
733
|
+
|
|
734
|
+
```tsx
|
|
735
|
+
card: {
|
|
736
|
+
at: 0.45, // start time in seconds
|
|
737
|
+
duration: 0.7, // bar length in seconds
|
|
738
|
+
from: { y: 44, scale: 0.95, opacity: 0 }, // any DialKit leaf values
|
|
739
|
+
to: { y: 0, scale: 1, opacity: 1 },
|
|
740
|
+
transition: { type: 'spring', bounce: 0.25 },
|
|
741
|
+
}
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
- **`from` / `to`** accept any normal DialKit leaf values — numbers, hex colors — and become editable controls in the clip's popover. Bare numbers get property-aware slider ranges (`x`/`y` ±100, `rotate` ±180, `scale` 0–2, `opacity` 0–1, and so on), expanded to include your actual endpoints.
|
|
745
|
+
- **`transition`** is a spring or easing config, exactly as in the panel's spring editor. Clips with `from`/`to` and no `transition` animate with a default spring (`{ type: 'spring', bounce: 0.2 }`).
|
|
746
|
+
- **The bar owns the duration.** Time-based springs and easings stretch to the bar: resize the clip and the curve retimes. Physics springs (`stiffness`/`damping`/`mass`) work the other way — the duration is *derived* from their settle time, the bar shows `~0.62s`, and it can't be resized (change the physics instead).
|
|
747
|
+
- **`duration` may be omitted** — it defaults to the easing's duration or the spring's settle time; a `from`/`to` clip with no `transition` gets the default spring's settle time.
|
|
748
|
+
|
|
749
|
+
Config mistakes warn in the console instead of failing silently: an entry missing `at`, conflicting clip shapes (`steps` + `to`, `props` + `from`), or a sequence property with no starting value.
|
|
750
|
+
|
|
751
|
+
A clip with only `at` (and optionally `duration`) is a **marker**: it carries timing state (`started`, `active`, `progress`) but no values. Useful for driving custom effects off `progress`:
|
|
752
|
+
|
|
753
|
+
```tsx
|
|
754
|
+
shine: { at: 2.7, duration: 0.7 },
|
|
755
|
+
|
|
756
|
+
// later
|
|
757
|
+
<div style={{ transform: `translateX(${-150 + tl.shine.progress * 400}%)` }} />
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
### Reading clip state
|
|
761
|
+
|
|
762
|
+
Each clip on the returned object is a `TimelineClipValues`:
|
|
763
|
+
|
|
764
|
+
| Field | Type | Description |
|
|
765
|
+
|-------|------|-------------|
|
|
766
|
+
| `at` | `number` | Clip start in seconds — live, reflects dock edits |
|
|
767
|
+
| `duration` | `number` | Effective duration — the bar length |
|
|
768
|
+
| `loop` | `'off' \| 'repeat'` | Effective loop mode |
|
|
769
|
+
| `started` | `boolean` | Playhead is at or past the clip start |
|
|
770
|
+
| `active` | `boolean` | Playhead is inside the clip (any cycle, for looping clips) |
|
|
771
|
+
| `done` | `boolean` | Playhead is past the clip end (past the timeline end, for looping clips) |
|
|
772
|
+
| `progress` | `number` | 0–1 position within the clip — a sawtooth per cycle for looping clips |
|
|
773
|
+
| `step` | `number` | Index of the leg under the playhead (sequence clips only) |
|
|
774
|
+
| `from` / `to` | `object` | Resolved endpoint values (`to` is the final merged state for sequences) |
|
|
775
|
+
| `animate` | `object` | `to` once the clip has started, `from` before |
|
|
776
|
+
| `transition` | `TransitionConfig` | Motion-ready curve, duration driven by the bar (single-curve clips only) |
|
|
777
|
+
| `css` | `TimelineClipCss` | `transitionDuration` + `transitionTimingFunction` (single-curve clips only) |
|
|
778
|
+
| `current` | `object` | Values interpolated through the clip's curves at the playhead |
|
|
779
|
+
|
|
780
|
+
### Recommended workflow: preview, copy, replace
|
|
781
|
+
|
|
782
|
+
The intended workflow is:
|
|
783
|
+
|
|
784
|
+
1. **Author with `current`.** Bind `clip.current` directly to the element while tuning. DialKit deterministically samples the configured spring or easing curve, which makes every intermediate state scrubbable.
|
|
785
|
+
2. **Copy the tuned settings.** Copy exports the timings, values, and transition parameters you arrived at in the dock.
|
|
786
|
+
3. **Move those settings into the real app animation.** Apply the copied transition to your normal Motion animation (or your production animation system), then remove `useDialTimeline`/`createDialTimeline` and `<DialTimeline />`. Motion now runs its real spring at runtime.
|
|
787
|
+
|
|
788
|
+
Hiding or removing only `<DialTimeline />` hides the editor UI; it does **not** change how the animation is rendered. As long as the component is reading `clip.current`, DialKit's sampled values are still driving it.
|
|
789
|
+
|
|
790
|
+
The sampler uses the damped-spring equation with the configured stiffness, damping, and mass, plus a Motion-compatible mapping for `visualDuration` and `bounce`. It is designed to closely preview the final motion, but it is not a guarantee of frame-for-frame identity with Motion's runtime implementation.
|
|
791
|
+
|
|
792
|
+
There are three ways to bind a clip while authoring:
|
|
793
|
+
|
|
794
|
+
**1. `current` — recommended for tuning.** Bind styles directly; the element sits at DialKit's sampled state at all times, giving you true scrubbing. DialKit remains the renderer until you replace this binding with your production animation.
|
|
795
|
+
|
|
796
|
+
```tsx
|
|
797
|
+
<div style={{
|
|
798
|
+
opacity: tl.card.current.opacity,
|
|
799
|
+
transform: `translateY(${tl.card.current.y}px) scale(${tl.card.current.scale})`,
|
|
800
|
+
}} />
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**2. `animate` + `transition` — real Motion during authoring.** The clip start flips `animate` from `from` to `to`, so Motion runs the actual curve. The tradeoff is that timeline scrubbing snaps between endpoints instead of showing intermediate states.
|
|
804
|
+
|
|
805
|
+
```tsx
|
|
806
|
+
<motion.div animate={tl.card.animate} transition={tl.card.transition} />
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
**3. `animate` + `css` — native CSS during authoring.** Same endpoint flip, using a CSS transition. Easing curves map exactly; springs are approximated with an overshoot bezier, and scrubbing still snaps between endpoints.
|
|
810
|
+
|
|
811
|
+
```tsx
|
|
812
|
+
<div style={{
|
|
813
|
+
opacity: tl.card.animate.opacity,
|
|
814
|
+
transform: `translateY(${tl.card.animate.y}px)`,
|
|
815
|
+
transitionProperty: 'transform, opacity',
|
|
816
|
+
...tl.card.css,
|
|
817
|
+
}} />
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Sequences — `steps`
|
|
821
|
+
|
|
822
|
+
A clip can explicitly chain several sequential legs with a `steps` array. Each array item becomes one segment on the same row; drag a boundary to retime a leg, or click a segment to edit its target values and curve (the first leg's popover also shows the clip's `from`). DialKit does not infer steps from the DOM element consuming the values, from the number of animated properties, or from multiple clips affecting the same element.
|
|
823
|
+
|
|
824
|
+
```tsx
|
|
825
|
+
path: {
|
|
826
|
+
at: 0,
|
|
827
|
+
from: { x: -70, y: 0 },
|
|
828
|
+
transition: { type: 'easing', duration: 0.8, ease: [0.65, 0, 0.35, 1] },
|
|
829
|
+
steps: [
|
|
830
|
+
{ duration: 0.8, to: { x: 0, y: 36 } },
|
|
831
|
+
{ duration: 0.8, to: { x: 70, y: 0 } },
|
|
832
|
+
{ duration: 0.8, to: { x: -70 } }, // y untouched — holds at 0
|
|
833
|
+
],
|
|
834
|
+
},
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
- **The hold rule:** each leg animates only the properties named in its `to`; everything else holds its value from the previous leg. In the example, leg 3 moves `x` while `y` stays where leg 2 left it.
|
|
838
|
+
- Use separate named clips when an element has separate behaviors, such as a one-shot entrance plus an idle animation. They remain separate rows even when the element combines both outputs.
|
|
839
|
+
- **Declare every animated property in `from`.** The sequence's `current` merges all legs from that starting state — a property with no initial value has nothing to hold or interpolate from before its first leg.
|
|
840
|
+
- Each leg takes its own `transition`; legs without one inherit the clip's `transition` (or the default spring).
|
|
841
|
+
- The clip's duration is the sum of its legs — there's no separate duration to edit.
|
|
842
|
+
- `tl.path.step` reports the index of the leg under the playhead.
|
|
843
|
+
|
|
844
|
+
### Property tracks — `props`
|
|
845
|
+
|
|
846
|
+
When one element's properties need *independent* timing — different durations, different curves, offset phases — give each property a full track:
|
|
847
|
+
|
|
848
|
+
```tsx
|
|
849
|
+
float: {
|
|
850
|
+
at: 0,
|
|
851
|
+
loop: true,
|
|
852
|
+
props: {
|
|
853
|
+
y: {
|
|
854
|
+
from: -9,
|
|
855
|
+
transition: { type: 'easing', duration: 0.6, ease: [0.45, 0, 0.55, 1] },
|
|
856
|
+
steps: [
|
|
857
|
+
{ duration: 0.6, to: 9 },
|
|
858
|
+
{ duration: 0.6, to: -9 },
|
|
859
|
+
],
|
|
860
|
+
},
|
|
861
|
+
scale: {
|
|
862
|
+
from: 0.94,
|
|
863
|
+
delay: 0.12, // starts 120ms behind y — a phase offset
|
|
864
|
+
transition: { type: 'easing', duration: 0.6, ease: [0.8, 0, 0.2, 1] },
|
|
865
|
+
steps: [
|
|
866
|
+
{ duration: 0.6, to: 1.06 },
|
|
867
|
+
{ duration: 0.6, to: 0.94 },
|
|
868
|
+
],
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
Each track is a mini-clip: its own `from`/`to` or `steps`, `duration`, `transition`, and a `delay` offset from the clip's `at`. In the dock, the clip's row is a read-only composite bar — click it to expand one full row per track, where everything is editable and dragging a track's bar adjusts its phase. When the clip loops, each track folds against its own cycle length, so tracks with different periods drift in and out of phase like real oscillators.
|
|
875
|
+
|
|
876
|
+
`props` is mutually exclusive with `from`/`to`/`steps` on the same clip. Track names that collide with clip fields (`at`, `duration`, `loop`, `from`, `to`, `transition`, `delay`, `stepN`) are skipped with a warning. Properties that share timing belong in a plain `from`/`to` or `steps` clip instead.
|
|
877
|
+
|
|
878
|
+
### Layers — nested groups
|
|
879
|
+
|
|
880
|
+
Nesting clips one level under a key groups them into a collapsible layer in the dock. Purely presentational — values nest the same way:
|
|
881
|
+
|
|
882
|
+
```tsx
|
|
883
|
+
const tl = useDialTimeline('Compound', {
|
|
884
|
+
circle: {
|
|
885
|
+
path: { at: 0, /* ... */ },
|
|
886
|
+
float: { at: 0, /* ... */ },
|
|
887
|
+
},
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
tl.circle.path.current
|
|
891
|
+
tl.circle.float.current
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### Looping
|
|
895
|
+
|
|
896
|
+
There are two loop controls, and they compose:
|
|
897
|
+
|
|
898
|
+
**Clip loop** — `loop: true` on a clip repeats its cycle from `at` until the timeline ends. The bar is one cycle, so dragging it longer slows the loop. Looping is code-defined rather than editable in the dock. There is no mirror mode — a loop that should return home (a bob, a pulse) is a sequence whose last leg lands back on the starting values, which also puts the loop seam at a natural zero-velocity point.
|
|
899
|
+
|
|
900
|
+
**Timeline loop** — the `loop` option wraps the *playhead*:
|
|
901
|
+
|
|
902
|
+
```tsx
|
|
903
|
+
useDialTimeline('Hero', config, { loop: true }); // wrap to 0
|
|
904
|
+
useDialTimeline('Hero', config, { loop: { from: 1.4 } }); // wrap to 1.4s
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
`{ from }` is the intro-then-idle pattern: clips before that time play exactly once, and looping clips inside the region keep cycling with continuous phase — no snap at the wrap. Scrubbing always pins the deterministic first-pass state.
|
|
908
|
+
|
|
909
|
+
**Event-driven timelines** — pass `autoplay: false` and drive the transport from your app. The dock and your code share the same clock: click your real button, watch the playhead run, scrub back, tune, click again.
|
|
910
|
+
|
|
911
|
+
```tsx
|
|
912
|
+
const toast = useDialTimeline('Toast', config, { autoplay: false });
|
|
913
|
+
|
|
914
|
+
<button onClick={() => toast.replay()}>Save changes</button>
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### Timeline duration
|
|
918
|
+
|
|
919
|
+
The top-level `duration` is the minimum editing window. Omit it and DialKit initially infers an exact fit to the last clip's end. If a live edit—such as switching to a longer physics spring—would move content past that boundary, DialKit extends the timeline automatically. Set `duration` explicitly when you want deliberate slack; authored content is never clipped to fit it.
|
|
920
|
+
|
|
921
|
+
```tsx
|
|
922
|
+
const tl = useDialTimeline('Hero', {
|
|
923
|
+
duration: 4, // seconds; minimum window, inferred when omitted
|
|
924
|
+
headline: { at: 0.45, /* ... */ },
|
|
925
|
+
});
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
### The dock — `<DialTimeline />`
|
|
929
|
+
|
|
930
|
+
Mount once, anywhere. The dock renders fixed to the bottom of the screen via a portal and shows every registered timeline as a section, mirroring how `<DialRoot />` collects panels. It renders nothing until a timeline registers, and like `DialRoot` it's hidden in production builds.
|
|
931
|
+
|
|
932
|
+
```tsx
|
|
933
|
+
<DialTimeline theme="dark" />
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
| Prop | Type | Default |
|
|
937
|
+
|------|------|---------|
|
|
938
|
+
| `theme` | `'system' \| 'light' \| 'dark'` | `'system'` |
|
|
939
|
+
| `defaultVisible` | `boolean` | `true` |
|
|
940
|
+
| `visible` | `boolean` | uncontrolled |
|
|
941
|
+
| `onVisibilityChange` | `(visible: boolean) => void` | `undefined` |
|
|
942
|
+
| `defaultOpen` | `boolean` | `true` |
|
|
943
|
+
| `productionEnabled` | `boolean` | `false` in production, `true` otherwise |
|
|
944
|
+
|
|
945
|
+
Each section's toolbar has **Play/Pause**, **Add Version**, a version selector, **Copy**, and a collapse chevron. The collapsed toolbar stays playable and keeps its full-range overview scrubber, so you can inspect the animation without opening the detailed tracks.
|
|
946
|
+
|
|
947
|
+
When `DialRoot` and `DialTimeline` are mounted together, the panel header gets a timeline icon that hides or shows the entire dock. Visibility never changes playback, and showing the dock restores each section's previous collapsed or expanded state. Use `visible` and `onVisibilityChange` when the host app needs to control or persist this state.
|
|
948
|
+
|
|
949
|
+
In the grid:
|
|
950
|
+
|
|
951
|
+
- **Scrub the full timeline** by dragging the compact toolbar overview — playback pauses while you drag and resumes on release.
|
|
952
|
+
- **Zoom the timescale** by holding Option/Alt while dragging left or right on the expanded seconds ruler. The gesture zooms around the point where it began. Hold Shift and drag to reset to the full view; dragging without a modifier seeks directly.
|
|
953
|
+
- **Drag a clip** to move it in time; **drag its edges** to resize (the transition retimes with it).
|
|
954
|
+
- **Drag a segment boundary** on a sequence to resize the leg on its left; the overall clip grows or shrinks with it.
|
|
955
|
+
- **Click a clip** to open its popover — the same DialKit controls (including values, color pickers, and the spring/easing curve editor) scoped to that clip's values. Time-based transitions expose their duration there, and track popovers expose track delay. Click a segment to edit one leg.
|
|
956
|
+
- **Click a props clip's composite bar** to expand its tracks into full editable rows; drag a track's bar to phase-shift it.
|
|
957
|
+
- Physics-spring clips show `~` before their derived duration and resize via their popover physics instead of their edges.
|
|
958
|
+
|
|
959
|
+
Clip names are labels only. Editors open from clips and segments in the timeline.
|
|
960
|
+
|
|
961
|
+
**Copy** exports the current timing values as an agent-ready instruction — tuned `at`s, durations, curves, and values, normalized so defaults and editor-only fields don't add noise. Physics springs include their effective settled duration. The instruction tells the agent to keep `clip.current` during authoring and add a `TODO(production)` comment beside the hook. That breadcrumb explains that the sampled bindings must eventually be replaced with real Motion animations before `useDialTimeline` and `<DialTimeline />` are removed; hiding the dock alone leaves `clip.current` rendering in place.
|
|
962
|
+
|
|
963
|
+
`DialRoot` and `DialTimeline` are independent: use both together (panels for styling, timeline for timing), or the dock alone. Timeline-backed panels don't appear in `DialRoot`.
|
|
964
|
+
|
|
965
|
+
`formatClock(seconds, tenths?)` is exported for rendering `mm:ss` / `mm:ss.t` readouts in your own UI.
|
|
966
|
+
|
|
967
|
+
---
|
|
968
|
+
|
|
468
969
|
## Solid
|
|
469
970
|
|
|
470
971
|
DialKit also works with Solid. Import from `dialkit/solid` instead of `dialkit` — the API mirrors the React version, with `createDialKit` replacing `useDialKit` and `DialRoot` as a Solid component.
|
|
@@ -513,7 +1014,44 @@ function Card() {
|
|
|
513
1014
|
}
|
|
514
1015
|
```
|
|
515
1016
|
|
|
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.
|
|
1017
|
+
`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.
|
|
1018
|
+
|
|
1019
|
+
Solid timelines use the same accessor shape:
|
|
1020
|
+
|
|
1021
|
+
```tsx
|
|
1022
|
+
import { createDialTimeline, DialTimeline } from 'dialkit/solid';
|
|
1023
|
+
|
|
1024
|
+
function Hero() {
|
|
1025
|
+
const timeline = createDialTimeline('Hero', {
|
|
1026
|
+
entrance: {
|
|
1027
|
+
at: 0,
|
|
1028
|
+
duration: 0.6,
|
|
1029
|
+
from: { y: 32, opacity: 0 },
|
|
1030
|
+
to: { y: 0, opacity: 1 },
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
return <>
|
|
1035
|
+
<h1 style={{ opacity: timeline().entrance.current.opacity }}>Ship it.</h1>
|
|
1036
|
+
<DialTimeline />
|
|
1037
|
+
</>;
|
|
1038
|
+
}
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
Use `createDialKitController` when Solid code needs to update values:
|
|
1042
|
+
|
|
1043
|
+
```tsx
|
|
1044
|
+
import { createDialKitController } from 'dialkit/solid';
|
|
1045
|
+
|
|
1046
|
+
const dial = createDialKitController('Card', {
|
|
1047
|
+
blur: [24, 0, 100],
|
|
1048
|
+
shadow: { radius: [16, 0, 64] },
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
dial.setValues({ blur: 48, shadow: { radius: 28 } });
|
|
1052
|
+
dial.resetValues();
|
|
1053
|
+
dial.values().blur;
|
|
1054
|
+
```
|
|
517
1055
|
|
|
518
1056
|
---
|
|
519
1057
|
|
|
@@ -554,7 +1092,50 @@ npm install dialkit
|
|
|
554
1092
|
</div>
|
|
555
1093
|
```
|
|
556
1094
|
|
|
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
|
|
1095
|
+
`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.
|
|
1096
|
+
|
|
1097
|
+
Svelte timelines are reactive objects too:
|
|
1098
|
+
|
|
1099
|
+
```svelte
|
|
1100
|
+
<script>
|
|
1101
|
+
import { createDialTimeline, DialTimeline } from 'dialkit/svelte';
|
|
1102
|
+
|
|
1103
|
+
const timeline = createDialTimeline('Hero', {
|
|
1104
|
+
entrance: {
|
|
1105
|
+
at: 0,
|
|
1106
|
+
duration: 0.6,
|
|
1107
|
+
from: { y: 32, opacity: 0 },
|
|
1108
|
+
to: { y: 0, opacity: 1 }
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
</script>
|
|
1112
|
+
|
|
1113
|
+
<h1 style:opacity={timeline.entrance.current.opacity}>Ship it.</h1>
|
|
1114
|
+
<DialTimeline />
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
Use `createDialKitController` when Svelte code needs to update values:
|
|
1118
|
+
|
|
1119
|
+
```svelte
|
|
1120
|
+
<script>
|
|
1121
|
+
import { createDialKitController } from 'dialkit/svelte';
|
|
1122
|
+
|
|
1123
|
+
const dial = createDialKitController('Card', {
|
|
1124
|
+
blur: [24, 0, 100],
|
|
1125
|
+
shadow: { radius: [16, 0, 64] }
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
const params = dial.values;
|
|
1129
|
+
</script>
|
|
1130
|
+
|
|
1131
|
+
<button onclick={() => dial.setValues({ blur: 48, shadow: { radius: 28 } })}>
|
|
1132
|
+
Apply preset
|
|
1133
|
+
</button>
|
|
1134
|
+
|
|
1135
|
+
<div style:filter={`blur(${params.blur}px)`} style:border-radius={`${params.shadow.radius}px`}>
|
|
1136
|
+
...
|
|
1137
|
+
</div>
|
|
1138
|
+
```
|
|
558
1139
|
|
|
559
1140
|
---
|
|
560
1141
|
|
|
@@ -586,7 +1167,7 @@ import Card from './Card.vue';
|
|
|
586
1167
|
|
|
587
1168
|
<template>
|
|
588
1169
|
<Card />
|
|
589
|
-
<DialRoot />
|
|
1170
|
+
<DialRoot @open-change="(open) => localStorage.setItem('dialkit-open', open ? '1' : '0')" />
|
|
590
1171
|
</template>
|
|
591
1172
|
```
|
|
592
1173
|
|
|
@@ -617,6 +1198,47 @@ const params = useDialKit('Card', {
|
|
|
617
1198
|
|
|
618
1199
|
`useDialKit` returns a reactive object. All control types, presets, folders, keyboard shortcuts, and transitions work identically to the other frameworks.
|
|
619
1200
|
|
|
1201
|
+
Vue timelines return a computed ref, which templates unwrap automatically:
|
|
1202
|
+
|
|
1203
|
+
```vue
|
|
1204
|
+
<script setup>
|
|
1205
|
+
import { useDialTimeline, DialTimeline } from 'dialkit/vue';
|
|
1206
|
+
|
|
1207
|
+
const timeline = useDialTimeline('Hero', {
|
|
1208
|
+
entrance: {
|
|
1209
|
+
at: 0,
|
|
1210
|
+
duration: 0.6,
|
|
1211
|
+
from: { y: 32, opacity: 0 },
|
|
1212
|
+
to: { y: 0, opacity: 1 },
|
|
1213
|
+
},
|
|
1214
|
+
});
|
|
1215
|
+
</script>
|
|
1216
|
+
|
|
1217
|
+
<template>
|
|
1218
|
+
<h1 :style="{ opacity: timeline.entrance.current.opacity }">Ship it.</h1>
|
|
1219
|
+
<DialTimeline />
|
|
1220
|
+
</template>
|
|
1221
|
+
```
|
|
1222
|
+
|
|
1223
|
+
Use `useDialKitController` when Vue code needs to update values:
|
|
1224
|
+
|
|
1225
|
+
```vue
|
|
1226
|
+
<script setup>
|
|
1227
|
+
import { useDialKitController } from 'dialkit/vue';
|
|
1228
|
+
|
|
1229
|
+
const { values, setValues, resetValues } = useDialKitController('Card', {
|
|
1230
|
+
blur: [24, 0, 100],
|
|
1231
|
+
shadow: { radius: [16, 0, 64] },
|
|
1232
|
+
});
|
|
1233
|
+
</script>
|
|
1234
|
+
|
|
1235
|
+
<template>
|
|
1236
|
+
<button @click="setValues({ blur: 48, shadow: { radius: 28 } })">Apply preset</button>
|
|
1237
|
+
<button @click="resetValues()">Reset</button>
|
|
1238
|
+
<div :style="{ filter: `blur(${values.blur}px)`, borderRadius: `${values.shadow.radius}px` }" />
|
|
1239
|
+
</template>
|
|
1240
|
+
```
|
|
1241
|
+
|
|
620
1242
|
---
|
|
621
1243
|
|
|
622
1244
|
## Types
|
|
@@ -626,6 +1248,8 @@ All config and value types are exported:
|
|
|
626
1248
|
```tsx
|
|
627
1249
|
import type {
|
|
628
1250
|
SpringConfig,
|
|
1251
|
+
EasingConfig,
|
|
1252
|
+
TransitionConfig,
|
|
629
1253
|
ActionConfig,
|
|
630
1254
|
SelectConfig,
|
|
631
1255
|
ColorConfig,
|
|
@@ -634,6 +1258,8 @@ import type {
|
|
|
634
1258
|
ShortcutMode,
|
|
635
1259
|
DialConfig,
|
|
636
1260
|
DialValue,
|
|
1261
|
+
DialKitController,
|
|
1262
|
+
DialKitValueUpdates,
|
|
637
1263
|
ResolvedValues,
|
|
638
1264
|
ControlMeta,
|
|
639
1265
|
PanelConfig,
|
|
@@ -641,7 +1267,28 @@ import type {
|
|
|
641
1267
|
} from 'dialkit';
|
|
642
1268
|
```
|
|
643
1269
|
|
|
644
|
-
|
|
1270
|
+
Timeline types are exported as well:
|
|
1271
|
+
|
|
1272
|
+
```tsx
|
|
1273
|
+
import type {
|
|
1274
|
+
TimelineConfig,
|
|
1275
|
+
TimelineClipConfig,
|
|
1276
|
+
TimelineClipValues,
|
|
1277
|
+
TimelineClipCss,
|
|
1278
|
+
TimelineClipLoop,
|
|
1279
|
+
TimelineGroupConfig,
|
|
1280
|
+
TimelineGroupValues,
|
|
1281
|
+
TimelinePropConfig,
|
|
1282
|
+
TimelinePropStepConfig,
|
|
1283
|
+
TimelineStepConfig,
|
|
1284
|
+
TimelineStepValues,
|
|
1285
|
+
DialTimelineValues,
|
|
1286
|
+
UseDialTimelineOptions,
|
|
1287
|
+
DialTimelineProps,
|
|
1288
|
+
} from 'dialkit';
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
Return values are fully typed: `params.blur` infers as `number`, `params.color` as `string`, `params.spring` as `SpringConfig`, `params.shadow` as a nested object, etc. Timeline values are typed from the config shape too — `tl.card.current.y` infers as `number`, and `step` only exists on sequence clips.
|
|
645
1292
|
|
|
646
1293
|
---
|
|
647
1294
|
|