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
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # dialkit
2
2
 
3
- Real-time parameter tweaking for React + Motion.
3
+ Real-time parameter tweaking for React, Solid, Svelte, and Vue, created by Josh Puckett.
4
+
5
+ To learn more about how I use DialKit, and approach design in general, feel free to check out [Interface Craft](http://interfacecraft.dev/).
6
+
7
+ ## Contributing
8
+
9
+ - **Open an issue first.** All pull requests should reference an existing issue. PRs without a corresponding issue will be closed.
10
+ - **Keep PRs small and focused.** Each pull request should address a single change — one bug fix, one feature, or one refactor. Avoid bundling unrelated changes together.
11
+ - **No unnecessary dependencies.** If your change can be accomplished without adding a new dependency, it should be. Any new dependency needs justification in the PR description.
4
12
 
5
13
  ## Quick Start
6
14
 
@@ -63,6 +71,7 @@ const params = useDialKit(name, config, options?)
63
71
  | `name` | `string` | Panel title displayed in the UI |
64
72
  | `config` | `DialConfig` | Parameter definitions (see Control Types below) |
65
73
  | `options.onAction` | `(path: string) => void` | Callback when action buttons are clicked |
74
+ | `options.shortcuts` | `Record<string, ShortcutConfig>` | Keyboard shortcuts for controls (see [Keyboard Shortcuts](#keyboard-shortcuts)) |
66
75
 
67
76
  Returns a fully typed object matching your config shape with live values. Updating a control in the UI immediately updates the returned values.
68
77
 
@@ -256,8 +265,47 @@ const values = useDialKit('Controls', {
256
265
  |------|------|---------|
257
266
  | `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'top-right'` |
258
267
  | `defaultOpen` | `boolean` | `true` |
268
+ | `mode` | `'popover' \| 'inline'` | `'popover'` |
269
+ | `productionEnabled` | `boolean` | `false` in production, `true` otherwise |
270
+
271
+ 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.
272
+
273
+ DialKit is automatically hidden in production builds. To enable it in production, pass `productionEnabled`:
274
+
275
+ ```tsx
276
+ <DialRoot productionEnabled />
277
+ ```
278
+
279
+ ### Draggable panel
280
+
281
+ In popover mode, the collapsed panel bubble can be dragged to any position on the screen. When you click to open the panel, it snaps to the nearest side — top-left if the bubble is on the left half of the screen, top-right if on the right half. When the panel is closed again, it returns to where you last dragged it.
282
+
283
+ ### Inline mode
284
+
285
+ Use `mode="inline"` to render DialKit directly in your layout instead of as a floating popover. The panel fills its container and scrolls internally, which is useful for embedding in a sidebar or resizable panel. Inline mode works across all frameworks:
286
+
287
+ **React:**
288
+ ```tsx
289
+ <aside style={{ width: 300, height: '100vh', overflow: 'hidden' }}>
290
+ <DialRoot mode="inline" />
291
+ </aside>
292
+ ```
293
+
294
+ **Solid:**
295
+ ```tsx
296
+ <aside style={{ width: '300px', height: '100vh', overflow: 'hidden' }}>
297
+ <DialRoot mode="inline" />
298
+ </aside>
299
+ ```
300
+
301
+ **Svelte:**
302
+ ```svelte
303
+ <aside style:width="300px" style:height="100vh" style:overflow="hidden">
304
+ <DialRoot mode="inline" />
305
+ </aside>
306
+ ```
259
307
 
260
- 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.
308
+ In inline mode, the `position` prop is ignored and the collapse-to-icon behavior is disabled.
261
309
 
262
310
  ---
263
311
 
@@ -270,6 +318,88 @@ When the panel is open, the toolbar provides:
270
318
 
271
319
  ---
272
320
 
321
+ ## Keyboard Shortcuts
322
+
323
+ Assign keyboard shortcuts to controls so you can adjust values without touching the panel. Pass a `shortcuts` map in the options object:
324
+
325
+ ```tsx
326
+ const p = useDialKit('Card', {
327
+ blur: [24, 0, 100],
328
+ scale: 1.2,
329
+ opacity: [1, 0, 1],
330
+ borderRadius: [16, 0, 64],
331
+ darkMode: true,
332
+ shadow: {
333
+ blur: [10, 0, 50],
334
+ },
335
+ }, {
336
+ shortcuts: {
337
+ blur: { key: 'b', mode: 'fine' }, // B+Scroll
338
+ scale: { key: 's', interaction: 'drag', mode: 'coarse' }, // S+Drag
339
+ opacity: { key: 'o', interaction: 'move' }, // O+Move
340
+ borderRadius: { interaction: 'scroll-only' }, // Scroll (no key)
341
+ darkMode: { key: 'm' }, // press M
342
+ 'shadow.blur': { key: 'd', mode: 'fine' }, // D+Scroll
343
+ },
344
+ });
345
+ ```
346
+
347
+ ### ShortcutConfig
348
+
349
+ ```tsx
350
+ type ShortcutConfig = {
351
+ key?: string; // trigger key (e.g. 'b', 's') — optional for scroll-only
352
+ modifier?: 'alt' | 'shift' | 'meta'; // optional modifier key
353
+ mode?: 'fine' | 'normal' | 'coarse'; // precision level (default: 'normal')
354
+ interaction?: 'scroll' | 'drag' | 'move' | 'scroll-only'; // input method (default: 'scroll')
355
+ };
356
+ ```
357
+
358
+ ### Interaction types
359
+
360
+ | Interaction | Description | Example pill |
361
+ |-------------|-------------|-------------|
362
+ | `scroll` | Hold key + scroll wheel to adjust (default) | `B+Scroll` |
363
+ | `drag` | Hold key + click and drag horizontally | `S+Drag` |
364
+ | `move` | Hold key + move mouse (no click needed) | `O+Move` |
365
+ | `scroll-only` | Just scroll anywhere, no key needed | `Scroll` |
366
+
367
+ ### Supported controls
368
+
369
+ | Control | Interactions | Description |
370
+ |---------|-------------|-------------|
371
+ | **Slider** | `scroll`, `drag`, `move`, `scroll-only` | Adjust value with chosen input method |
372
+ | **Toggle** | key press | Press the assigned key to flip on/off |
373
+
374
+ ### Precision modes
375
+
376
+ For sliders, the `mode` controls how much each scroll tick or drag pixel changes the value:
377
+
378
+ | Mode | Step multiplier | Use case |
379
+ |------|----------------|----------|
380
+ | `fine` | step &divide; 10 | Precision tweaking |
381
+ | `normal` | step &times; 1 | Default behavior |
382
+ | `coarse` | step &times; 10 | Big sweeps |
383
+
384
+ ### Nested paths
385
+
386
+ For controls inside folders, use dot notation:
387
+
388
+ ```tsx
389
+ shortcuts: {
390
+ 'shadow.blur': { key: 'd' },
391
+ 'shadow.opacity': { key: 'a', interaction: 'drag', mode: 'fine' },
392
+ }
393
+ ```
394
+
395
+ ### UI indicators
396
+
397
+ Each control with a shortcut displays a pill badge next to its label showing the key and interaction (e.g. `B+Scroll`, `S+Drag`, `O+Move`, `Scroll`). The pill highlights when the shortcut key is actively held.
398
+
399
+ Shortcuts are automatically disabled when a text input is focused.
400
+
401
+ ---
402
+
273
403
  ## Full Example
274
404
 
275
405
  ```tsx
@@ -307,6 +437,11 @@ function PhotoStack() {
307
437
  next: { type: 'action' },
308
438
  previous: { type: 'action' },
309
439
  }, {
440
+ shortcuts: {
441
+ 'backPhoto.offsetX': { key: 'x', interaction: 'drag', mode: 'coarse' },
442
+ 'backPhoto.scale': { key: 's', interaction: 'move', mode: 'fine' },
443
+ darkMode: { key: 'm' },
444
+ },
310
445
  onAction: (action) => {
311
446
  if (action === 'next') goNext();
312
447
  if (action === 'previous') goPrevious();
@@ -328,6 +463,160 @@ function PhotoStack() {
328
463
 
329
464
  ---
330
465
 
466
+ ## Solid
467
+
468
+ 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.
469
+
470
+ ```bash
471
+ npm install dialkit solid-js
472
+ ```
473
+
474
+ ```tsx
475
+ // App.tsx
476
+ import { DialRoot } from 'dialkit/solid';
477
+ import 'dialkit/styles.css';
478
+
479
+ export default function App() {
480
+ return (
481
+ <>
482
+ <MyComponent />
483
+ <DialRoot />
484
+ </>
485
+ );
486
+ }
487
+ ```
488
+
489
+ ```tsx
490
+ // component.tsx
491
+ import { createDialKit } from 'dialkit/solid';
492
+
493
+ function Card() {
494
+ const params = createDialKit('Card', {
495
+ blur: [24, 0, 100],
496
+ scale: 1.2,
497
+ color: '#ff5500',
498
+ visible: true,
499
+ });
500
+
501
+ return (
502
+ <div style={{
503
+ filter: `blur(${params().blur}px)`,
504
+ transform: `scale(${params().scale})`,
505
+ color: params().color,
506
+ opacity: params().visible ? 1 : 0,
507
+ }}>
508
+ ...
509
+ </div>
510
+ );
511
+ }
512
+ ```
513
+
514
+ `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.
515
+
516
+ ---
517
+
518
+ ## Svelte
519
+
520
+ DialKit works with Svelte 5 (≥5.8.0). Import from `dialkit/svelte` — no extra dependencies needed.
521
+
522
+ ```bash
523
+ npm install dialkit
524
+ ```
525
+
526
+ ```svelte
527
+ <!-- +layout.svelte -->
528
+ <script>
529
+ import { DialRoot } from 'dialkit/svelte';
530
+ let { children } = $props();
531
+ </script>
532
+
533
+ {@render children()}
534
+ <DialRoot />
535
+ ```
536
+
537
+ ```svelte
538
+ <!-- Card.svelte -->
539
+ <script>
540
+ import { createDialKit } from 'dialkit/svelte';
541
+
542
+ const params = createDialKit('Card', {
543
+ blur: [24, 0, 100],
544
+ scale: 1.2,
545
+ color: '#ff5500',
546
+ visible: true
547
+ });
548
+ </script>
549
+
550
+ <div style:filter={`blur(${params.blur}px)`} style:color={params.color}>
551
+ ...
552
+ </div>
553
+ ```
554
+
555
+ `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 transitions match the React/Solid entries.
556
+
557
+ ---
558
+
559
+ ## Vue
560
+
561
+ DialKit works with Vue 3 (≥3.3.0). Import from `dialkit/vue`.
562
+
563
+ ```bash
564
+ npm install dialkit motion-v vue
565
+ ```
566
+
567
+ ```ts
568
+ // main.ts
569
+ import { createApp } from 'vue';
570
+ import { DialRoot } from 'dialkit/vue';
571
+ import 'dialkit/styles.css';
572
+ import App from './App.vue';
573
+
574
+ const app = createApp(App);
575
+ app.mount('#app');
576
+ ```
577
+
578
+ ```vue
579
+ <!-- App.vue -->
580
+ <script setup>
581
+ import { DialRoot } from 'dialkit/vue';
582
+ import Card from './Card.vue';
583
+ </script>
584
+
585
+ <template>
586
+ <Card />
587
+ <DialRoot />
588
+ </template>
589
+ ```
590
+
591
+ ```vue
592
+ <!-- Card.vue -->
593
+ <script setup>
594
+ import { useDialKit } from 'dialkit/vue';
595
+
596
+ const params = useDialKit('Card', {
597
+ blur: [24, 0, 100],
598
+ scale: 1.2,
599
+ color: '#ff5500',
600
+ visible: true,
601
+ });
602
+ </script>
603
+
604
+ <template>
605
+ <div :style="{
606
+ filter: `blur(${params.blur}px)`,
607
+ transform: `scale(${params.scale})`,
608
+ color: params.color,
609
+ opacity: params.visible ? 1 : 0,
610
+ }">
611
+ ...
612
+ </div>
613
+ </template>
614
+ ```
615
+
616
+ `useDialKit` returns a reactive object. All control types, presets, folders, keyboard shortcuts, and transitions work identically to the other frameworks.
617
+
618
+ ---
619
+
331
620
  ## Types
332
621
 
333
622
  All config and value types are exported:
@@ -339,6 +628,8 @@ import type {
339
628
  SelectConfig,
340
629
  ColorConfig,
341
630
  TextConfig,
631
+ ShortcutConfig,
632
+ ShortcutMode,
342
633
  DialConfig,
343
634
  DialValue,
344
635
  ResolvedValues,