dialkit 0.1.2 → 0.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.
package/README.md CHANGED
@@ -2,21 +2,14 @@
2
2
 
3
3
  Real-time parameter tweaking for React + Motion.
4
4
 
5
- ---
6
-
7
- ## Installation
5
+ ## Quick Start
8
6
 
9
7
  ```bash
10
8
  npm install dialkit motion
11
9
  ```
12
10
 
13
- ---
14
-
15
- ## Setup
16
-
17
- Add `<DialRoot />` once in your app layout:
18
-
19
11
  ```tsx
12
+ // layout.tsx
20
13
  import { DialRoot } from 'dialkit';
21
14
  import 'dialkit/styles.css';
22
15
 
@@ -25,51 +18,41 @@ export default function Layout({ children }) {
25
18
  <html>
26
19
  <body>
27
20
  {children}
28
- <DialRoot position="top-right" />
21
+ <DialRoot />
29
22
  </body>
30
23
  </html>
31
24
  );
32
25
  }
33
26
  ```
34
27
 
35
- ---
36
-
37
- ## Usage
38
-
39
28
  ```tsx
29
+ // component.tsx
40
30
  import { useDialKit } from 'dialkit';
41
- import { motion } from 'motion/react';
42
31
 
43
32
  function Card() {
44
- const params = useDialKit('Card', {
33
+ const p = useDialKit('Card', {
45
34
  blur: [24, 0, 100],
46
- opacity: [0.8, 0, 1],
47
- scale: 1.18,
48
- spring: {
49
- type: 'spring',
50
- visualDuration: 0.3,
51
- bounce: 0.2,
52
- },
35
+ scale: 1.2,
36
+ color: '#ff5500',
37
+ visible: true,
53
38
  });
54
39
 
55
40
  return (
56
- <motion.div
57
- style={{
58
- filter: `blur(${params.blur}px)`,
59
- opacity: params.opacity,
60
- }}
61
- animate={{ scale: params.scale }}
62
- transition={params.spring}
63
- />
41
+ <div style={{
42
+ filter: `blur(${p.blur}px)`,
43
+ transform: `scale(${p.scale})`,
44
+ color: p.color,
45
+ opacity: p.visible ? 1 : 0,
46
+ }}>
47
+ ...
48
+ </div>
64
49
  );
65
50
  }
66
51
  ```
67
52
 
68
53
  ---
69
54
 
70
- ## API Reference
71
-
72
- ### useDialKit
55
+ ## useDialKit
73
56
 
74
57
  ```tsx
75
58
  const params = useDialKit(name, config, options?)
@@ -77,70 +60,262 @@ const params = useDialKit(name, config, options?)
77
60
 
78
61
  | Param | Type | Description |
79
62
  |-------|------|-------------|
80
- | `name` | `string` | Panel title |
81
- | `config` | `DialConfig` | Parameter definitions |
82
- | `options.onAction` | `(action: string) => void` | Callback for action buttons |
63
+ | `name` | `string` | Panel title displayed in the UI |
64
+ | `config` | `DialConfig` | Parameter definitions (see Control Types below) |
65
+ | `options.onAction` | `(path: string) => void` | Callback when action buttons are clicked |
66
+
67
+ Returns a fully typed object matching your config shape with live values. Updating a control in the UI immediately updates the returned values.
68
+
69
+ ---
70
+
71
+ ## Control Types
72
+
73
+ ### Slider
74
+
75
+ ```tsx
76
+ blur: [24, 0, 100] // [default, min, max] — explicit range
77
+ scale: 1.2 // auto-infers range from value
78
+ ```
79
+
80
+ Numbers create sliders. With a tuple `[default, min, max]` you set the range explicitly. A bare number auto-infers a reasonable range:
81
+
82
+ | Value range | Inferred min/max | Step |
83
+ |-------------|-----------------|------|
84
+ | 0–1 | 0 to 1 | 0.01 |
85
+ | 0–10 | 0 to value &times; 3 | 0.1 |
86
+ | 0–100 | 0 to value &times; 3 | 1 |
87
+ | 100+ | 0 to value &times; 3 | 10 |
88
+
89
+ **Returns:** `number`
90
+
91
+ Sliders support click-to-snap (with spring animation), drag with rubber-band overflow, and direct text editing (hover the value for 800ms, then click to type).
92
+
93
+ ### Toggle
83
94
 
84
- ### Config Types
95
+ ```tsx
96
+ enabled: true
97
+ darkMode: false
98
+ ```
99
+
100
+ Booleans create an Off/On segmented control.
101
+
102
+ **Returns:** `boolean`
103
+
104
+ ### Text
105
+
106
+ ```tsx
107
+ title: 'Hello' // auto-detected from string
108
+ subtitle: { type: 'text', default: '', placeholder: 'Enter subtitle...' }
109
+ ```
110
+
111
+ Non-hex strings are auto-detected as text inputs. Use the explicit form for a placeholder or to set a default.
112
+
113
+ **Returns:** `string`
114
+
115
+ ### Color
116
+
117
+ ```tsx
118
+ color: '#ff5500' // auto-detected from hex string
119
+ bg: { type: 'color', default: '#000' } // explicit
120
+ ```
85
121
 
86
- | Format | Control | Example |
87
- |--------|---------|---------|
88
- | `[default, min, max]` | Slider | `blur: [24, 0, 100]` |
89
- | `number` | Slider (auto range) | `scale: 1.18` |
90
- | `boolean` | Toggle | `enabled: true` |
91
- | `{ type: 'spring', ... }` | Spring editor | See below |
92
- | `{ type: 'action' }` | Button | `reset: { type: 'action' }` |
93
- | `{ nested: ... }` | Folder | Nest any config |
122
+ Hex strings (`#RGB`, `#RRGGBB`, `#RRGGBBAA`) are auto-detected as color pickers. Each color control has a text display (click to edit the hex value), and a swatch button that opens the native color picker.
94
123
 
95
- ### Spring Config
124
+ **Returns:** `string` (hex color)
96
125
 
97
- Two modes available:
126
+ ### Select
98
127
 
99
128
  ```tsx
100
- // Time mode
101
- spring: {
102
- type: 'spring',
103
- visualDuration: 0.3,
104
- bounce: 0.2,
129
+ layout: {
130
+ type: 'select',
131
+ options: ['stack', 'fan', 'grid'],
132
+ default: 'stack',
105
133
  }
134
+ ```
106
135
 
107
- // Physics mode
108
- spring: {
109
- type: 'spring',
110
- stiffness: 200,
111
- damping: 25,
112
- mass: 1,
136
+ Options can be plain strings or `{ value, label }` objects for custom display text:
137
+
138
+ ```tsx
139
+ shape: {
140
+ type: 'select',
141
+ options: [
142
+ { value: 'portrait', label: 'Portrait' },
143
+ { value: 'square', label: 'Square' },
144
+ { value: 'landscape', label: 'Landscape' },
145
+ ],
146
+ default: 'portrait',
113
147
  }
114
148
  ```
115
149
 
116
- ### Actions
150
+ If `default` is omitted, the first option is selected.
151
+
152
+ **Returns:** `string` (the selected option's value)
153
+
154
+ ### Spring
155
+
156
+ ```tsx
157
+ // Time-based (simple mode)
158
+ spring: { type: 'spring', visualDuration: 0.3, bounce: 0.2 }
159
+
160
+ // Physics-based (advanced mode)
161
+ spring: { type: 'spring', stiffness: 200, damping: 25, mass: 1 }
162
+ ```
163
+
164
+ Creates a visual spring editor with a live animation curve preview. The editor supports two modes, toggled in the UI:
165
+
166
+ - **Time** (simple) — `visualDuration` (0.1–1s) and `bounce` (0–1). Ideal for most animations.
167
+ - **Physics** (advanced) — `stiffness` (1–1000), `damping` (1–100), and `mass` (0.1–10). Full control over spring dynamics.
168
+
169
+ The returned config object is passed directly to Motion's `transition` prop:
170
+
171
+ ```tsx
172
+ const p = useDialKit('Card', {
173
+ spring: { type: 'spring', visualDuration: 0.5, bounce: 0.04 },
174
+ x: [0, -200, 200],
175
+ });
176
+
177
+ <motion.div animate={{ x: p.x }} transition={p.spring} />
178
+ ```
179
+
180
+ **Returns:** `SpringConfig` (pass directly to Motion)
117
181
 
118
- Trigger callbacks from the panel:
182
+ ### Action
119
183
 
120
184
  ```tsx
121
- const params = useDialKit('Controls', {
122
- next: { type: 'action' },
123
- previous: { type: 'action' },
185
+ const p = useDialKit('Controls', {
186
+ shuffle: { type: 'action' },
187
+ reset: { type: 'action', label: 'Reset All' },
124
188
  }, {
125
- onAction: (action) => {
126
- if (action === 'next') goNext();
127
- if (action === 'previous') goPrevious();
189
+ onAction: (path) => {
190
+ if (path === 'shuffle') shuffleItems();
191
+ if (path === 'reset') resetToDefaults();
128
192
  },
129
193
  });
130
194
  ```
131
195
 
132
- ### DialRoot
196
+ Action buttons trigger callbacks without storing any value. The `label` defaults to the formatted key name (camelCase becomes Title Case). Multiple adjacent actions are grouped vertically.
197
+
198
+ ### Folder
199
+
200
+ ```tsx
201
+ shadow: {
202
+ blur: [10, 0, 50],
203
+ opacity: [0.25, 0, 1],
204
+ color: '#000000',
205
+ }
206
+ ```
207
+
208
+ Any nested plain object becomes a collapsible folder. Folders can nest arbitrarily deep. Access nested values with dot notation on the returned object:
209
+
210
+ ```tsx
211
+ params.shadow.blur // number
212
+ params.shadow.color // string
213
+ ```
214
+
215
+ ---
216
+
217
+ ## DialRoot
133
218
 
134
219
  ```tsx
135
220
  <DialRoot position="top-right" />
136
221
  ```
137
222
 
138
- | Position | |
139
- |----------|--|
140
- | `top-right` | Default |
141
- | `top-left` | |
142
- | `bottom-right` | |
143
- | `bottom-left` | |
223
+ | Prop | Type | Default |
224
+ |------|------|---------|
225
+ | `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'top-right'` |
226
+
227
+ Mount once at your app root. The panel renders via a portal on `document.body`. It collapses to a small icon button and expands to 280px wide on click.
228
+
229
+ ---
230
+
231
+ ## Panel Toolbar
232
+
233
+ When the panel is open, the toolbar provides:
234
+
235
+ - **Presets** — A version dropdown for saving and loading parameter snapshots. Click "+" to save the current state as a new version. Select a version to load it. Changes auto-save to the active version. "Version 1" always represents the original defaults.
236
+ - **Copy** — Exports the current values as JSON to your clipboard.
237
+
238
+ ---
239
+
240
+ ## Full Example
241
+
242
+ ```tsx
243
+ import { useDialKit } from 'dialkit';
244
+ import { motion } from 'motion/react';
245
+
246
+ function PhotoStack() {
247
+ const p = useDialKit('Photo Stack', {
248
+ // Text inputs
249
+ title: 'Japan',
250
+ subtitle: { type: 'text', default: 'December 2025', placeholder: 'Enter subtitle...' },
251
+
252
+ // Color pickers
253
+ accentColor: '#c41e3a',
254
+ shadowTint: { type: 'color', default: '#000000' },
255
+
256
+ // Select dropdown
257
+ layout: { type: 'select', options: ['stack', 'fan', 'grid'], default: 'stack' },
258
+
259
+ // Grouped sliders in a folder
260
+ backPhoto: {
261
+ offsetX: [239, 0, 400],
262
+ offsetY: [0, 0, 150],
263
+ scale: [0.7, 0.5, 0.95],
264
+ overlayOpacity: [0.6, 0, 1],
265
+ },
266
+
267
+ // Spring config for Motion
268
+ transitionSpring: { type: 'spring', visualDuration: 0.5, bounce: 0.04 },
269
+
270
+ // Toggle
271
+ darkMode: false,
272
+
273
+ // Action buttons
274
+ next: { type: 'action' },
275
+ previous: { type: 'action' },
276
+ }, {
277
+ onAction: (action) => {
278
+ if (action === 'next') goNext();
279
+ if (action === 'previous') goPrevious();
280
+ },
281
+ });
282
+
283
+ return (
284
+ <motion.div
285
+ animate={{ x: p.backPhoto.offsetX }}
286
+ transition={p.transitionSpring}
287
+ style={{ color: p.accentColor }}
288
+ >
289
+ <h1>{p.title}</h1>
290
+ <p>{p.subtitle}</p>
291
+ </motion.div>
292
+ );
293
+ }
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Types
299
+
300
+ All config and value types are exported:
301
+
302
+ ```tsx
303
+ import type {
304
+ SpringConfig,
305
+ ActionConfig,
306
+ SelectConfig,
307
+ ColorConfig,
308
+ TextConfig,
309
+ DialConfig,
310
+ DialValue,
311
+ ResolvedValues,
312
+ ControlMeta,
313
+ PanelConfig,
314
+ Preset,
315
+ } from 'dialkit';
316
+ ```
317
+
318
+ 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.
144
319
 
145
320
  ---
146
321