@delightstack/components 0.1.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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -0
  3. package/SKILL.md +149 -0
  4. package/bin/agents.js +63 -0
  5. package/dist/actions/Alert.svelte +202 -0
  6. package/dist/actions/Alert.svelte.d.ts +36 -0
  7. package/dist/actions/Alert.svelte.d.ts.map +1 -0
  8. package/dist/actions/Button.svelte +1450 -0
  9. package/dist/actions/Button.svelte.d.ts +56 -0
  10. package/dist/actions/Button.svelte.d.ts.map +1 -0
  11. package/dist/actions/ButtonGroup.svelte +111 -0
  12. package/dist/actions/ButtonGroup.svelte.d.ts +41 -0
  13. package/dist/actions/ButtonGroup.svelte.d.ts.map +1 -0
  14. package/dist/actions/CommandPalette.svelte +939 -0
  15. package/dist/actions/CommandPalette.svelte.d.ts +37 -0
  16. package/dist/actions/CommandPalette.svelte.d.ts.map +1 -0
  17. package/dist/actions/ContextMenu.svelte +138 -0
  18. package/dist/actions/ContextMenu.svelte.d.ts +54 -0
  19. package/dist/actions/ContextMenu.svelte.d.ts.map +1 -0
  20. package/dist/actions/Modal.svelte +474 -0
  21. package/dist/actions/Modal.svelte.d.ts +28 -0
  22. package/dist/actions/Modal.svelte.d.ts.map +1 -0
  23. package/dist/actions/Popover.svelte +1214 -0
  24. package/dist/actions/Popover.svelte.d.ts +31 -0
  25. package/dist/actions/Popover.svelte.d.ts.map +1 -0
  26. package/dist/actions/Portal.svelte +80 -0
  27. package/dist/actions/Portal.svelte.d.ts +17 -0
  28. package/dist/actions/Portal.svelte.d.ts.map +1 -0
  29. package/dist/actions/ThemeToggle.svelte +345 -0
  30. package/dist/actions/ThemeToggle.svelte.d.ts +15 -0
  31. package/dist/actions/ThemeToggle.svelte.d.ts.map +1 -0
  32. package/dist/actions/index.d.ts +13 -0
  33. package/dist/actions/index.d.ts.map +1 -0
  34. package/dist/actions/index.js +10 -0
  35. package/dist/actions/scrollbar.d.ts +48 -0
  36. package/dist/actions/scrollbar.d.ts.map +1 -0
  37. package/dist/actions/scrollbar.js +404 -0
  38. package/dist/display/Accordion.svelte +586 -0
  39. package/dist/display/Accordion.svelte.d.ts +41 -0
  40. package/dist/display/Accordion.svelte.d.ts.map +1 -0
  41. package/dist/display/Avatar.svelte +527 -0
  42. package/dist/display/Avatar.svelte.d.ts +22 -0
  43. package/dist/display/Avatar.svelte.d.ts.map +1 -0
  44. package/dist/display/AvatarGroup.svelte +298 -0
  45. package/dist/display/AvatarGroup.svelte.d.ts +31 -0
  46. package/dist/display/AvatarGroup.svelte.d.ts.map +1 -0
  47. package/dist/display/Calendar.svelte +1366 -0
  48. package/dist/display/Calendar.svelte.d.ts +58 -0
  49. package/dist/display/Calendar.svelte.d.ts.map +1 -0
  50. package/dist/display/Chart.svelte +1426 -0
  51. package/dist/display/Chart.svelte.d.ts +35 -0
  52. package/dist/display/Chart.svelte.d.ts.map +1 -0
  53. package/dist/display/Code.svelte +780 -0
  54. package/dist/display/Code.svelte.d.ts +19 -0
  55. package/dist/display/Code.svelte.d.ts.map +1 -0
  56. package/dist/display/Comparison.svelte +686 -0
  57. package/dist/display/Comparison.svelte.d.ts +22 -0
  58. package/dist/display/Comparison.svelte.d.ts.map +1 -0
  59. package/dist/display/Counter.svelte +285 -0
  60. package/dist/display/Counter.svelte.d.ts +21 -0
  61. package/dist/display/Counter.svelte.d.ts.map +1 -0
  62. package/dist/display/Expand.svelte +48 -0
  63. package/dist/display/Expand.svelte.d.ts +9 -0
  64. package/dist/display/Expand.svelte.d.ts.map +1 -0
  65. package/dist/display/List.svelte +294 -0
  66. package/dist/display/List.svelte.d.ts +40 -0
  67. package/dist/display/List.svelte.d.ts.map +1 -0
  68. package/dist/display/ListContextReset.svelte +19 -0
  69. package/dist/display/ListContextReset.svelte.d.ts +7 -0
  70. package/dist/display/ListContextReset.svelte.d.ts.map +1 -0
  71. package/dist/display/ListItem.svelte +834 -0
  72. package/dist/display/ListItem.svelte.d.ts +22 -0
  73. package/dist/display/ListItem.svelte.d.ts.map +1 -0
  74. package/dist/display/QR.svelte +1193 -0
  75. package/dist/display/QR.svelte.d.ts +23 -0
  76. package/dist/display/QR.svelte.d.ts.map +1 -0
  77. package/dist/display/SplitPane.svelte +744 -0
  78. package/dist/display/SplitPane.svelte.d.ts +25 -0
  79. package/dist/display/SplitPane.svelte.d.ts.map +1 -0
  80. package/dist/display/Stat.svelte +439 -0
  81. package/dist/display/Stat.svelte.d.ts +24 -0
  82. package/dist/display/Stat.svelte.d.ts.map +1 -0
  83. package/dist/display/Table.svelte +4654 -0
  84. package/dist/display/Table.svelte.d.ts +249 -0
  85. package/dist/display/Table.svelte.d.ts.map +1 -0
  86. package/dist/display/TableCellEditor.svelte +935 -0
  87. package/dist/display/TableCellEditor.svelte.d.ts +58 -0
  88. package/dist/display/TableCellEditor.svelte.d.ts.map +1 -0
  89. package/dist/display/Timeline.svelte +1258 -0
  90. package/dist/display/Timeline.svelte.d.ts +43 -0
  91. package/dist/display/Timeline.svelte.d.ts.map +1 -0
  92. package/dist/display/Tree.svelte +1740 -0
  93. package/dist/display/Tree.svelte.d.ts +74 -0
  94. package/dist/display/Tree.svelte.d.ts.map +1 -0
  95. package/dist/display/Typewriter.svelte +338 -0
  96. package/dist/display/Typewriter.svelte.d.ts +22 -0
  97. package/dist/display/Typewriter.svelte.d.ts.map +1 -0
  98. package/dist/display/index.d.ts +24 -0
  99. package/dist/display/index.d.ts.map +1 -0
  100. package/dist/display/index.js +18 -0
  101. package/dist/feedback/Callout.svelte +529 -0
  102. package/dist/feedback/Callout.svelte.d.ts +24 -0
  103. package/dist/feedback/Callout.svelte.d.ts.map +1 -0
  104. package/dist/feedback/Confetti.svelte +631 -0
  105. package/dist/feedback/Confetti.svelte.d.ts +90 -0
  106. package/dist/feedback/Confetti.svelte.d.ts.map +1 -0
  107. package/dist/feedback/Progress.svelte +382 -0
  108. package/dist/feedback/Progress.svelte.d.ts +25 -0
  109. package/dist/feedback/Progress.svelte.d.ts.map +1 -0
  110. package/dist/feedback/Toast.svelte +967 -0
  111. package/dist/feedback/Toast.svelte.d.ts +54 -0
  112. package/dist/feedback/Toast.svelte.d.ts.map +1 -0
  113. package/dist/feedback/index.d.ts +7 -0
  114. package/dist/feedback/index.d.ts.map +1 -0
  115. package/dist/feedback/index.js +4 -0
  116. package/dist/form/Checkbox.svelte +449 -0
  117. package/dist/form/Checkbox.svelte.d.ts +27 -0
  118. package/dist/form/Checkbox.svelte.d.ts.map +1 -0
  119. package/dist/form/Fieldset.svelte +410 -0
  120. package/dist/form/Fieldset.svelte.d.ts +22 -0
  121. package/dist/form/Fieldset.svelte.d.ts.map +1 -0
  122. package/dist/form/FileUpload.svelte +934 -0
  123. package/dist/form/FileUpload.svelte.d.ts +41 -0
  124. package/dist/form/FileUpload.svelte.d.ts.map +1 -0
  125. package/dist/form/Form.svelte +530 -0
  126. package/dist/form/Form.svelte.d.ts +120 -0
  127. package/dist/form/Form.svelte.d.ts.map +1 -0
  128. package/dist/form/Input.svelte +2858 -0
  129. package/dist/form/Input.svelte.d.ts +66 -0
  130. package/dist/form/Input.svelte.d.ts.map +1 -0
  131. package/dist/form/Radio.svelte +507 -0
  132. package/dist/form/Radio.svelte.d.ts +39 -0
  133. package/dist/form/Radio.svelte.d.ts.map +1 -0
  134. package/dist/form/Range.svelte +912 -0
  135. package/dist/form/Range.svelte.d.ts +33 -0
  136. package/dist/form/Range.svelte.d.ts.map +1 -0
  137. package/dist/form/Rating.svelte +429 -0
  138. package/dist/form/Rating.svelte.d.ts +28 -0
  139. package/dist/form/Rating.svelte.d.ts.map +1 -0
  140. package/dist/form/Select.svelte +1933 -0
  141. package/dist/form/Select.svelte.d.ts +54 -0
  142. package/dist/form/Select.svelte.d.ts.map +1 -0
  143. package/dist/form/Toggle.svelte +645 -0
  144. package/dist/form/Toggle.svelte.d.ts +50 -0
  145. package/dist/form/Toggle.svelte.d.ts.map +1 -0
  146. package/dist/form/index.d.ts +15 -0
  147. package/dist/form/index.d.ts.map +1 -0
  148. package/dist/form/index.js +10 -0
  149. package/dist/index.d.ts +7 -0
  150. package/dist/index.d.ts.map +1 -0
  151. package/dist/index.js +6 -0
  152. package/dist/layout/README.md +172 -0
  153. package/dist/media/Carousel.svelte +2424 -0
  154. package/dist/media/Carousel.svelte.d.ts +47 -0
  155. package/dist/media/Carousel.svelte.d.ts.map +1 -0
  156. package/dist/media/Gallery.svelte +2881 -0
  157. package/dist/media/Gallery.svelte.d.ts +82 -0
  158. package/dist/media/Gallery.svelte.d.ts.map +1 -0
  159. package/dist/media/Image.svelte +389 -0
  160. package/dist/media/Image.svelte.d.ts +33 -0
  161. package/dist/media/Image.svelte.d.ts.map +1 -0
  162. package/dist/media/PDF.svelte +1793 -0
  163. package/dist/media/PDF.svelte.d.ts +44 -0
  164. package/dist/media/PDF.svelte.d.ts.map +1 -0
  165. package/dist/media/Panorama.svelte +1391 -0
  166. package/dist/media/Panorama.svelte.d.ts +47 -0
  167. package/dist/media/Panorama.svelte.d.ts.map +1 -0
  168. package/dist/media/Video.svelte +2501 -0
  169. package/dist/media/Video.svelte.d.ts +58 -0
  170. package/dist/media/Video.svelte.d.ts.map +1 -0
  171. package/dist/media/carousel.d.ts +211 -0
  172. package/dist/media/carousel.d.ts.map +1 -0
  173. package/dist/media/carousel.js +408 -0
  174. package/dist/media/index.d.ts +11 -0
  175. package/dist/media/index.d.ts.map +1 -0
  176. package/dist/media/index.js +5 -0
  177. package/dist/navigation/BottomSheet.svelte +636 -0
  178. package/dist/navigation/BottomSheet.svelte.d.ts +27 -0
  179. package/dist/navigation/BottomSheet.svelte.d.ts.map +1 -0
  180. package/dist/navigation/Breadcrumbs.svelte +611 -0
  181. package/dist/navigation/Breadcrumbs.svelte.d.ts +28 -0
  182. package/dist/navigation/Breadcrumbs.svelte.d.ts.map +1 -0
  183. package/dist/navigation/Pagination.svelte +641 -0
  184. package/dist/navigation/Pagination.svelte.d.ts +27 -0
  185. package/dist/navigation/Pagination.svelte.d.ts.map +1 -0
  186. package/dist/navigation/Steps.svelte +965 -0
  187. package/dist/navigation/Steps.svelte.d.ts +43 -0
  188. package/dist/navigation/Steps.svelte.d.ts.map +1 -0
  189. package/dist/navigation/Tabs.svelte +698 -0
  190. package/dist/navigation/Tabs.svelte.d.ts +41 -0
  191. package/dist/navigation/Tabs.svelte.d.ts.map +1 -0
  192. package/dist/navigation/index.d.ts +8 -0
  193. package/dist/navigation/index.d.ts.map +1 -0
  194. package/dist/navigation/index.js +5 -0
  195. package/package.json +139 -0
@@ -0,0 +1,54 @@
1
+ type Position = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
2
+ export interface ToastOptions {
3
+ /** Optional secondary line shown beneath the title. */
4
+ description?: string;
5
+ /** Auto-dismiss delay in milliseconds (overrides the Toaster default) */
6
+ duration?: number;
7
+ /** Whether the toast shows a close button */
8
+ dismissible?: boolean;
9
+ /** Style the toast as a success message */
10
+ success?: boolean;
11
+ /** Style the toast as a warning message */
12
+ warning?: boolean;
13
+ /** Style the toast as an error message */
14
+ error?: boolean;
15
+ /** Style the toast as an informational message */
16
+ info?: boolean;
17
+ /** An action button shown in the toast */
18
+ action?: {
19
+ label: string;
20
+ onclick: () => void;
21
+ };
22
+ /** Whether the toast stays until manually dismissed (no auto-dismiss) */
23
+ persistent?: boolean;
24
+ /** Custom toast id — reusing an id updates the existing toast in place */
25
+ id?: string;
26
+ }
27
+ /** Show a toast notification. Returns the toast ID for later dismissal. */
28
+ export declare function toast(message: string, options?: ToastOptions): string;
29
+ export declare namespace toast {
30
+ var success: (message: string, options?: ToastOptions) => string;
31
+ var error: (message: string, options?: ToastOptions) => string;
32
+ var warning: (message: string, options?: ToastOptions) => string;
33
+ var info: (message: string, options?: ToastOptions) => string;
34
+ var loading: (message: string, options?: ToastOptions) => string;
35
+ var promise: <T>(p: Promise<T>, messages: {
36
+ loading: string;
37
+ success: string | ((result: T) => string);
38
+ error: string | ((err: Error) => string);
39
+ }, options?: ToastOptions) => Promise<T>;
40
+ var dismiss: (id?: string) => void;
41
+ }
42
+ declare const Toast: import("svelte").Component<{
43
+ position?: Position;
44
+ max_visible?: number;
45
+ gap?: number;
46
+ width?: number;
47
+ duration?: number;
48
+ rich_colors?: boolean;
49
+ id?: string;
50
+ class?: string;
51
+ }, {}, "">;
52
+ type Toast = ReturnType<typeof Toast>;
53
+ export default Toast;
54
+ //# sourceMappingURL=Toast.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toast.svelte.d.ts","sourceRoot":"","sources":["../../src/feedback/Toast.svelte.ts"],"names":[],"mappings":"AAKC,KAAK,QAAQ,GACV,UAAU,GACV,YAAY,GACZ,WAAW,GACX,aAAa,GACb,eAAe,GACf,cAAc,CAAC;AAElB,MAAM,WAAW,YAAY;IAC5B,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAChD,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ;AAiGD,2EAA2E;AAC3E,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAErE;yBAFe,KAAK;2BAIqB,MAAM,YAAY,YAAY,KAAG,MAAM;yBAI3C,MAAM,YAAY,YAAY,KAAG,MAAM;2BAInC,MAAM,YAAY,YAAY,KAAG,MAAM;wBAI7C,MAAM,YAAY,YAAY,KAAG,MAAM;2BAIjC,MAAM,YAAY,YAAY,KAAG,MAAM;kBAI1C,CAAC,KACpC,OAAO,CAAC,CAAC,CAAC,YACH;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC;QAC1C,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC;KACzC,YACS,YAAY,KACpB,OAAO,CAAC,CAAC,CAAC;uBAsByB,MAAM,KAAG,IAAI;;AAsYpD,QAAA,MAAM,KAAK;eAlXyE,QAAQ;kBAAgB,MAAM;UAAQ,MAAM;YAAU,MAAM;eAAa,MAAM;kBAAgB,OAAO;;YAA8B,MAAM;UAkX3K,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { default as Callout } from './Callout.svelte';
2
+ export { default as Confetti, confetti } from './Confetti.svelte';
3
+ export type { ConfettiOptions, ConfettiTarget, CannonOptions, RainOptions, } from './Confetti.svelte';
4
+ export { default as Progress } from './Progress.svelte';
5
+ export { default as Toaster, toast } from './Toast.svelte';
6
+ export type { ToastOptions } from './Toast.svelte';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/feedback/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClE,YAAY,EACX,eAAe,EACf,cAAc,EACd,aAAa,EACb,WAAW,GACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { default as Callout } from './Callout.svelte';
2
+ export { default as Confetti, confetti } from './Confetti.svelte';
3
+ export { default as Progress } from './Progress.svelte';
4
+ export { default as Toaster, toast } from './Toast.svelte';
@@ -0,0 +1,449 @@
1
+ <script lang="ts">
2
+ import { ripple, tooltip } from '@delightstack/utilities';
3
+ import { getContext } from 'svelte';
4
+ import type { FormContext } from './Form.svelte';
5
+
6
+ const propId = $props.id();
7
+ let {
8
+ /** Whether the checkbox is checked. When omitted inside a Form (with a
9
+ * name), the checked state is driven by the form data instead. */
10
+ checked = $bindable() as boolean | undefined,
11
+
12
+ /** Whether the checkbox is in an indeterminate state */
13
+ indeterminate = false,
14
+
15
+ /** The value submitted with form data */
16
+ value = '',
17
+
18
+ /** Whether the checkbox is disabled */
19
+ disabled = false,
20
+
21
+ /** The size of the checkbox. 0=16px, 1=20px, 2=24px, 3=28px */
22
+ size = '1' as '0' | '1' | '2' | '3',
23
+
24
+ /** The label text for the checkbox */
25
+ label = '',
26
+
27
+ /** A description shown below the label */
28
+ description = '',
29
+
30
+ /** An error message shown below the checkbox */
31
+ error = '',
32
+
33
+ /** Parses & validates the value (e.g. a database table form field's
34
+ * `parse`). Inside a Form it is registered with the form, which runs it
35
+ * on the form's validation timing. */
36
+ parse = undefined as ((value: unknown) => unknown) | undefined,
37
+
38
+ /** Tri-state mode (set by optional non-defaulted boolean form fields):
39
+ * a null/undefined value means "unanswered" and displays as
40
+ * indeterminate. Clicking resolves to checked, matching the browser's
41
+ * native indeterminate-checkbox behavior — use Toggle for a control
42
+ * the user can cycle back to the middle state. */
43
+ tristate = false,
44
+
45
+ /** The field's default value (set by defaulted boolean form fields):
46
+ * shown when the form data has no value yet, so the display matches
47
+ * what saving would persist. */
48
+ default_checked = undefined as boolean | undefined,
49
+
50
+ /** Whether the checkbox is required */
51
+ required = false,
52
+
53
+ /** The tooltip message shown on hover */
54
+ tooltip: tooltip_message = '',
55
+
56
+ /** Whether the checkbox should display in a condensed view */
57
+ dense = false,
58
+
59
+ /** Whether the checkbox should display in an expanded view */
60
+ comfortable = false,
61
+
62
+ /** The ID of the checkbox element */
63
+ id = propId,
64
+
65
+ /** The name attribute for the hidden input */
66
+ name = '',
67
+
68
+ /** Specifies a custom class name for the container element */
69
+ class: class_name = '',
70
+
71
+ /** Called when the checked state changes */
72
+ onchange = undefined as
73
+ | ((detail: { checked: boolean; value: string }) => void)
74
+ | undefined,
75
+ } = $props();
76
+
77
+ const sizes: Record<string, number> = { '0': 16, '1': 20, '2': 24, '3': 28 };
78
+ const px = $derived(sizes[size] ?? 20);
79
+
80
+ let animation = $state<'none' | 'check' | 'uncheck'>('none');
81
+ let indicator_element = $state<HTMLElement | undefined>(undefined);
82
+
83
+ /* ------------------------------------------------------------------ */
84
+ /* Form context integration */
85
+ /* ------------------------------------------------------------------ */
86
+
87
+ const form_ctx = getContext<FormContext | undefined>('form');
88
+
89
+ $effect(() => {
90
+ if (!form_ctx || !name) return;
91
+ if (indicator_element) form_ctx.register(name, indicator_element, parse);
92
+ return () => {
93
+ if (name) form_ctx.unregister(name);
94
+ };
95
+ });
96
+
97
+ /**
98
+ * Context-driven mode: inside a Form, with a name, and no checked prop,
99
+ * the checkbox mirrors the form data (e.g. an entity's draft) —
100
+ * `<Checkbox {...field.is_public} />` needs no bind:checked.
101
+ */
102
+ const context_driven = !!(form_ctx && name && checked === undefined);
103
+
104
+ $effect(() => {
105
+ if (!context_driven || !form_ctx || !name) return;
106
+ const ctx_value = form_ctx.getValue(name);
107
+ let next: boolean | undefined;
108
+ if (ctx_value === undefined || ctx_value === null) {
109
+ // Unanswered: tri-state fields stay undefined (indeterminate display),
110
+ // defaulted fields show their default, plain booleans show unchecked
111
+ next = tristate ? undefined : (default_checked ?? false);
112
+ } else {
113
+ next = Boolean(ctx_value);
114
+ }
115
+ if (next !== checked) checked = next;
116
+ });
117
+
118
+ /** The effective checked state (an omitted checked prop means unchecked) */
119
+ const is_checked = $derived(checked ?? false);
120
+
121
+ /** Indeterminate display: the explicit prop, or an unanswered tri-state value */
122
+ const show_indeterminate = $derived(
123
+ indeterminate || (tristate && (checked === undefined || checked === null)),
124
+ );
125
+
126
+ /** Error from the local prop or the form context */
127
+ const resolved_error = $derived.by(() => {
128
+ if (error) return error;
129
+ if (form_ctx && name && form_ctx.errors[name]) return form_ctx.errors[name];
130
+ return '';
131
+ });
132
+
133
+ function toggle() {
134
+ if (disabled) return;
135
+ checked = !is_checked;
136
+ animation = checked ? 'check' : 'uncheck';
137
+ setTimeout(() => (animation = 'none'), checked ? 350 : 50);
138
+ if (form_ctx && name) {
139
+ form_ctx.setValue(name, checked);
140
+ form_ctx.setTouched(name);
141
+ }
142
+ onchange?.({ checked, value });
143
+ }
144
+
145
+ function onKeyDown(e: KeyboardEvent) {
146
+ if (e.key === ' ') {
147
+ e.preventDefault();
148
+ toggle();
149
+ }
150
+ }
151
+ </script>
152
+
153
+ <!-- The whole container is the click target so the hit area matches the
154
+ hover/press feedback (which keys off `.checkbox`). Keyboard activation
155
+ stays on the focusable role="checkbox" indicator below. -->
156
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
157
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
158
+ <div
159
+ class={['checkbox', class_name].filter(Boolean).join(' ')}
160
+ class:dense
161
+ class:comfortable
162
+ class:disabled
163
+ class:has-error={!!resolved_error}
164
+ {@attach tooltip(tooltip_message)}
165
+ style:--size="{px}px"
166
+ style:font-size={`var(--control-font-${size})`}
167
+ onclick={toggle}>
168
+ <!-- Hidden native input for form submission -->
169
+ <input
170
+ type="checkbox"
171
+ {id}
172
+ {name}
173
+ {value}
174
+ {required}
175
+ {disabled}
176
+ checked={is_checked}
177
+ indeterminate={show_indeterminate}
178
+ tabindex={-1}
179
+ aria-hidden="true" />
180
+
181
+ <div
182
+ bind:this={indicator_element}
183
+ class="indicator-wrapper"
184
+ class:checked={is_checked}
185
+ class:indeterminate={show_indeterminate}
186
+ role="checkbox"
187
+ tabindex={disabled ? -1 : 0}
188
+ aria-checked={show_indeterminate ? 'mixed' : is_checked}
189
+ aria-disabled={disabled}
190
+ aria-labelledby={label ? `${id}-label` : undefined}
191
+ {@attach ripple({ enabled: !disabled, centered: true, opacity: 0.15 })}
192
+ onkeydown={onKeyDown}>
193
+ <svg
194
+ class="indicator"
195
+ class:checked={is_checked}
196
+ class:indeterminate={show_indeterminate}
197
+ class:animating-check={animation === 'check'}
198
+ class:animating-uncheck={animation === 'uncheck'}
199
+ viewBox="0 0 24 24"
200
+ width={px}
201
+ height={px}
202
+ fill="none">
203
+ <rect class="box" x="2" y="2" width="20" height="20" rx="3" stroke-width="2" />
204
+ {#if show_indeterminate}
205
+ <line
206
+ class="dash"
207
+ x1="7"
208
+ y1="12"
209
+ x2="17"
210
+ y2="12"
211
+ stroke-width="2.5"
212
+ stroke-linecap="round" />
213
+ {:else}
214
+ <path
215
+ class="check"
216
+ d="M6 12.5 L10 16.5 L18 8"
217
+ stroke-width="2.5"
218
+ stroke-linecap="round"
219
+ stroke-linejoin="round" />
220
+ {/if}
221
+ </svg>
222
+ </div>
223
+
224
+ {#if label || description}
225
+ <div class="content">
226
+ {#if label}
227
+ <span id="{id}-label" class="label">{label}</span>
228
+ {/if}
229
+ {#if description}
230
+ <span class="description">{description}</span>
231
+ {/if}
232
+ </div>
233
+ {/if}
234
+
235
+ {#if resolved_error}
236
+ <span class="error-text">{resolved_error}</span>
237
+ {/if}
238
+ </div>
239
+
240
+ <style>
241
+ .checkbox {
242
+ display: flex;
243
+ flex-wrap: wrap;
244
+ align-items: flex-start;
245
+ gap: 0.5em;
246
+ /* The whole container is clickable, so the pointer cursor and the
247
+ hover/press feedback now line up with the actual hit area. Bound it
248
+ to its content (indicator + label + description) so a block-level
249
+ parent can't stretch the hit area across the whole row. */
250
+ width: fit-content;
251
+ cursor: pointer;
252
+ user-select: none;
253
+ position: relative;
254
+ perspective: 100px;
255
+ transition: translate 200ms ease;
256
+
257
+ &:not(.disabled):active {
258
+ translate: 0px 3px clamp(-10px, calc(0.2em - 12px), -2px);
259
+ }
260
+
261
+ &.dense {
262
+ gap: 0.25em;
263
+ }
264
+ &.comfortable {
265
+ gap: 0.75em;
266
+ }
267
+ &.disabled {
268
+ opacity: 0.5;
269
+ pointer-events: none;
270
+ }
271
+ &.has-error {
272
+ .indicator .box {
273
+ stroke: var(--color-error, #d32f2f);
274
+ }
275
+ .error-text {
276
+ display: block;
277
+ }
278
+ }
279
+
280
+ /* Hover: circular background tint on indicator (triggers from label hover too) */
281
+ &:not(.disabled):hover > .indicator-wrapper {
282
+ background: var(--hover-tint);
283
+ transition: none;
284
+ }
285
+
286
+ /* Active: press scale on indicator (triggers from label click too) */
287
+ &:not(.disabled):active > .indicator-wrapper {
288
+ transform: scale(0.9);
289
+ transition:
290
+ transform 80ms ease,
291
+ background 200ms ease;
292
+ }
293
+ }
294
+
295
+ /* Hidden native checkbox, kept for form submission */
296
+ input {
297
+ position: absolute;
298
+ width: 1px;
299
+ height: 1px;
300
+ overflow: hidden;
301
+ clip: rect(0, 0, 0, 0);
302
+ white-space: nowrap;
303
+ border: 0;
304
+ padding: 0;
305
+ margin: -1px;
306
+ }
307
+
308
+ .indicator-wrapper {
309
+ position: relative;
310
+ display: flex;
311
+ align-items: center;
312
+ justify-content: center;
313
+ width: calc(var(--size) + 20px);
314
+ height: calc(var(--size) + 20px);
315
+ border-radius: 50%;
316
+ cursor: pointer;
317
+ flex-shrink: 0;
318
+ overflow: hidden;
319
+ outline: none;
320
+ -webkit-tap-highlight-color: transparent;
321
+ --hover-tint: color-mix(in srgb, var(--color-text, currentColor) 12%, transparent);
322
+ transition:
323
+ background 200ms ease,
324
+ transform 150ms ease;
325
+
326
+ /* Accent-tinted hover when checked or indeterminate */
327
+ &.checked,
328
+ &.indeterminate {
329
+ --hover-tint: color-mix(in srgb, var(--color-action, #1976d2) 16%, transparent);
330
+ }
331
+
332
+ &:focus-visible {
333
+ box-shadow: 0 0 0 2px var(--color-text, currentColor);
334
+ border-radius: 50%;
335
+ }
336
+ }
337
+
338
+ .indicator {
339
+ flex-shrink: 0;
340
+
341
+ .box {
342
+ stroke: var(--color-text-disabled, #999);
343
+ fill: transparent;
344
+ transition:
345
+ stroke 150ms ease,
346
+ fill 150ms ease;
347
+ }
348
+
349
+ .check {
350
+ stroke: transparent;
351
+ fill: none;
352
+ stroke-dasharray: 28;
353
+ stroke-dashoffset: 28;
354
+ transition:
355
+ stroke-dashoffset 250ms ease,
356
+ stroke 150ms ease;
357
+ }
358
+
359
+ .dash {
360
+ stroke: transparent;
361
+ transition: stroke 150ms ease;
362
+ }
363
+
364
+ &.checked {
365
+ .box {
366
+ stroke: var(--color-action, #1976d2);
367
+ fill: var(--color-action, #1976d2);
368
+ }
369
+ .check {
370
+ stroke: var(--color-action-text, #fff);
371
+ stroke-dashoffset: 0;
372
+ }
373
+ }
374
+
375
+ &.indeterminate {
376
+ .box {
377
+ stroke: var(--color-action, #1976d2);
378
+ fill: var(--color-action, #1976d2);
379
+ }
380
+ .dash {
381
+ stroke: var(--color-action-text, #fff);
382
+ }
383
+ }
384
+
385
+ /* Check-in: elastic checkmark draw with overshoot + scale pulse */
386
+ &.animating-check {
387
+ animation: box-pulse 350ms cubic-bezier(0.34, 1.56, 0.64, 1);
388
+
389
+ .check {
390
+ stroke-dashoffset: 0;
391
+ transition: stroke-dashoffset 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
392
+ }
393
+ }
394
+
395
+ /* Uncheck: visible stroke retraction, box holds fill then fades */
396
+ &.animating-uncheck {
397
+ .check {
398
+ stroke: var(--color-action-text, #fff);
399
+ stroke-dashoffset: 28;
400
+ transition: stroke-dashoffset 50ms cubic-bezier(0.4, 0, 0.2, 1);
401
+ }
402
+ .box {
403
+ stroke: var(--color-action, #1976d2);
404
+ fill: var(--color-action, #1976d2);
405
+ }
406
+ }
407
+ }
408
+
409
+ @keyframes box-pulse {
410
+ 0% {
411
+ transform: scale(1);
412
+ }
413
+ 40% {
414
+ transform: scale(1.1);
415
+ }
416
+ 100% {
417
+ transform: scale(1);
418
+ }
419
+ }
420
+
421
+ .content {
422
+ display: flex;
423
+ flex-direction: column;
424
+ gap: 0.1em;
425
+ padding-top: calc((var(--size) + 20px) / 2 - 0.7em);
426
+ margin-left: -8px;
427
+ }
428
+
429
+ .label {
430
+ cursor: pointer;
431
+ user-select: none;
432
+ line-height: 1.4;
433
+ color: var(--color-text, inherit);
434
+ }
435
+
436
+ .description {
437
+ font-size: 0.85em;
438
+ color: var(--color-text-disabled, #888);
439
+ line-height: 1.3;
440
+ }
441
+
442
+ .error-text {
443
+ display: none;
444
+ width: 100%;
445
+ font-size: 0.8em;
446
+ color: var(--color-error, #d32f2f);
447
+ margin-top: -0.25em;
448
+ }
449
+ </style>
@@ -0,0 +1,27 @@
1
+ declare const Checkbox: import("svelte").Component<{
2
+ checked?: boolean | undefined;
3
+ indeterminate?: boolean;
4
+ value?: string;
5
+ disabled?: boolean;
6
+ size?: "0" | "1" | "2" | "3";
7
+ label?: string;
8
+ description?: string;
9
+ error?: string;
10
+ parse?: ((value: unknown) => unknown) | undefined;
11
+ tristate?: boolean;
12
+ default_checked?: boolean | undefined;
13
+ required?: boolean;
14
+ tooltip?: string;
15
+ dense?: boolean;
16
+ comfortable?: boolean;
17
+ id?: string;
18
+ name?: string;
19
+ class?: string;
20
+ onchange?: ((detail: {
21
+ checked: boolean;
22
+ value: string;
23
+ }) => void) | undefined;
24
+ }, {}, "checked">;
25
+ type Checkbox = ReturnType<typeof Checkbox>;
26
+ export default Checkbox;
27
+ //# sourceMappingURL=Checkbox.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkbox.svelte.d.ts","sourceRoot":"","sources":["../../src/form/Checkbox.svelte.ts"],"names":[],"mappings":"AAsMA,QAAA,MAAM,QAAQ;cA1LqE,OAAO,GAAG,SAAS;oBAAkB,OAAO;YAAU,MAAM;eAAa,OAAO;WAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;YAAU,MAAM;kBAAgB,MAAM;YAAU,MAAM;YAAU,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,SAAS;eAAa,OAAO;sBAAoB,OAAO,GAAG,SAAS;eAAa,OAAO;cAAY,MAAM;YAAU,OAAO;kBAAgB,OAAO;;WAA6B,MAAM;YAAU,MAAM;eAAe,CAAC,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GACviB,SAAS;iBAyLwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}