@sepale/components 0.0.0-stage → 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 (261) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -3
  3. package/dist/components/Accordion.svelte +48 -0
  4. package/dist/components/Accordion.svelte.d.ts +24 -0
  5. package/dist/components/Alert.svelte +107 -0
  6. package/dist/components/Alert.svelte.d.ts +40 -0
  7. package/dist/components/Avatar.svelte +75 -0
  8. package/dist/components/Avatar.svelte.d.ts +37 -0
  9. package/dist/components/Badge.svelte +46 -0
  10. package/dist/components/Badge.svelte.d.ts +22 -0
  11. package/dist/components/Breadcrumbs.svelte +36 -0
  12. package/dist/components/Breadcrumbs.svelte.d.ts +18 -0
  13. package/dist/components/Bubble.svelte +43 -0
  14. package/dist/components/Bubble.svelte.d.ts +23 -0
  15. package/dist/components/Button.svelte +132 -0
  16. package/dist/components/Button.svelte.d.ts +40 -0
  17. package/dist/components/Calendar.svelte +193 -0
  18. package/dist/components/Calendar.svelte.d.ts +60 -0
  19. package/dist/components/Card.svelte +69 -0
  20. package/dist/components/Card.svelte.d.ts +37 -0
  21. package/dist/components/Carousel.svelte +550 -0
  22. package/dist/components/Carousel.svelte.d.ts +124 -0
  23. package/dist/components/Checkbox.svelte +48 -0
  24. package/dist/components/Checkbox.svelte.d.ts +26 -0
  25. package/dist/components/Chip.svelte +60 -0
  26. package/dist/components/Chip.svelte.d.ts +26 -0
  27. package/dist/components/ChipsInput.svelte +158 -0
  28. package/dist/components/ChipsInput.svelte.d.ts +64 -0
  29. package/dist/components/Collapse.svelte +101 -0
  30. package/dist/components/Collapse.svelte.d.ts +46 -0
  31. package/dist/components/ColorPicker.svelte +135 -0
  32. package/dist/components/ColorPicker.svelte.d.ts +44 -0
  33. package/dist/components/Combobox.svelte +496 -0
  34. package/dist/components/Combobox.svelte.d.ts +96 -0
  35. package/dist/components/DataControl.svelte +754 -0
  36. package/dist/components/DataControl.svelte.d.ts +143 -0
  37. package/dist/components/DataHandler.svelte +67 -0
  38. package/dist/components/DataHandler.svelte.d.ts +44 -0
  39. package/dist/components/DatePicker.svelte +185 -0
  40. package/dist/components/DatePicker.svelte.d.ts +56 -0
  41. package/dist/components/Dialog.svelte +194 -0
  42. package/dist/components/Dialog.svelte.d.ts +51 -0
  43. package/dist/components/Divider.svelte +54 -0
  44. package/dist/components/Divider.svelte.d.ts +27 -0
  45. package/dist/components/Dock.svelte +34 -0
  46. package/dist/components/Dock.svelte.d.ts +18 -0
  47. package/dist/components/Drawer.svelte +174 -0
  48. package/dist/components/Drawer.svelte.d.ts +51 -0
  49. package/dist/components/Dropdown.svelte +183 -0
  50. package/dist/components/Dropdown.svelte.d.ts +52 -0
  51. package/dist/components/Editable.svelte +292 -0
  52. package/dist/components/Editable.svelte.d.ts +113 -0
  53. package/dist/components/EmptyState.svelte +64 -0
  54. package/dist/components/EmptyState.svelte.d.ts +29 -0
  55. package/dist/components/Fieldset.svelte +42 -0
  56. package/dist/components/Fieldset.svelte.d.ts +20 -0
  57. package/dist/components/FileDropzone.svelte +154 -0
  58. package/dist/components/FileDropzone.svelte.d.ts +45 -0
  59. package/dist/components/FileExplorer.svelte +643 -0
  60. package/dist/components/FileExplorer.svelte.d.ts +114 -0
  61. package/dist/components/FileInput.svelte +125 -0
  62. package/dist/components/FileInput.svelte.d.ts +51 -0
  63. package/dist/components/FloatingLabel.svelte +38 -0
  64. package/dist/components/FloatingLabel.svelte.d.ts +24 -0
  65. package/dist/components/Footer.svelte +68 -0
  66. package/dist/components/Footer.svelte.d.ts +37 -0
  67. package/dist/components/Grid.svelte +122 -0
  68. package/dist/components/Grid.svelte.d.ts +72 -0
  69. package/dist/components/Hero.svelte +62 -0
  70. package/dist/components/Hero.svelte.d.ts +33 -0
  71. package/dist/components/Input.svelte +184 -0
  72. package/dist/components/Input.svelte.d.ts +65 -0
  73. package/dist/components/Join.svelte +38 -0
  74. package/dist/components/Join.svelte.d.ts +21 -0
  75. package/dist/components/Kanban.svelte +284 -0
  76. package/dist/components/Kanban.svelte.d.ts +93 -0
  77. package/dist/components/Label.svelte +32 -0
  78. package/dist/components/Label.svelte.d.ts +21 -0
  79. package/dist/components/LaptopFrame.svelte +44 -0
  80. package/dist/components/LaptopFrame.svelte.d.ts +21 -0
  81. package/dist/components/List.svelte +95 -0
  82. package/dist/components/List.svelte.d.ts +69 -0
  83. package/dist/components/Loading.svelte +37 -0
  84. package/dist/components/Loading.svelte.d.ts +17 -0
  85. package/dist/components/Menu.svelte +144 -0
  86. package/dist/components/Menu.svelte.d.ts +53 -0
  87. package/dist/components/MobileFrame.svelte +41 -0
  88. package/dist/components/MobileFrame.svelte.d.ts +21 -0
  89. package/dist/components/Navbar.svelte +49 -0
  90. package/dist/components/Navbar.svelte.d.ts +25 -0
  91. package/dist/components/NumberInput.svelte +255 -0
  92. package/dist/components/NumberInput.svelte.d.ts +68 -0
  93. package/dist/components/Pagination.svelte +169 -0
  94. package/dist/components/Pagination.svelte.d.ts +50 -0
  95. package/dist/components/Progress.svelte +65 -0
  96. package/dist/components/Progress.svelte.d.ts +28 -0
  97. package/dist/components/RadialProgress.svelte +48 -0
  98. package/dist/components/RadialProgress.svelte.d.ts +20 -0
  99. package/dist/components/Radio.svelte +50 -0
  100. package/dist/components/Radio.svelte.d.ts +28 -0
  101. package/dist/components/Range.svelte +53 -0
  102. package/dist/components/Range.svelte.d.ts +26 -0
  103. package/dist/components/Rating.svelte +90 -0
  104. package/dist/components/Rating.svelte.d.ts +41 -0
  105. package/dist/components/Select.svelte +338 -0
  106. package/dist/components/Select.svelte.d.ts +74 -0
  107. package/dist/components/Sidebar.svelte +100 -0
  108. package/dist/components/Sidebar.svelte.d.ts +54 -0
  109. package/dist/components/Skeleton.svelte +40 -0
  110. package/dist/components/Skeleton.svelte.d.ts +18 -0
  111. package/dist/components/Stat.svelte +71 -0
  112. package/dist/components/Stat.svelte.d.ts +35 -0
  113. package/dist/components/Stats.svelte +38 -0
  114. package/dist/components/Stats.svelte.d.ts +22 -0
  115. package/dist/components/Steps.svelte +204 -0
  116. package/dist/components/Steps.svelte.d.ts +66 -0
  117. package/dist/components/Subtitle.svelte +44 -0
  118. package/dist/components/Subtitle.svelte.d.ts +26 -0
  119. package/dist/components/Swatch.svelte +61 -0
  120. package/dist/components/Swatch.svelte.d.ts +27 -0
  121. package/dist/components/Switch.svelte +105 -0
  122. package/dist/components/Switch.svelte.d.ts +44 -0
  123. package/dist/components/Table.svelte +243 -0
  124. package/dist/components/Table.svelte.d.ts +121 -0
  125. package/dist/components/Tabs.svelte +142 -0
  126. package/dist/components/Tabs.svelte.d.ts +80 -0
  127. package/dist/components/Text.svelte +43 -0
  128. package/dist/components/Text.svelte.d.ts +25 -0
  129. package/dist/components/Textarea.svelte +76 -0
  130. package/dist/components/Textarea.svelte.d.ts +39 -0
  131. package/dist/components/Title.svelte +46 -0
  132. package/dist/components/Title.svelte.d.ts +29 -0
  133. package/dist/components/Toast.svelte +154 -0
  134. package/dist/components/Toast.svelte.d.ts +49 -0
  135. package/dist/components/Toggle.svelte +48 -0
  136. package/dist/components/Toggle.svelte.d.ts +25 -0
  137. package/dist/components/Tooltip.svelte +70 -0
  138. package/dist/components/Tooltip.svelte.d.ts +34 -0
  139. package/dist/components/WindowFrame.svelte +53 -0
  140. package/dist/components/WindowFrame.svelte.d.ts +24 -0
  141. package/dist/components/accordion-context.d.ts +8 -0
  142. package/dist/components/accordion-context.js +25 -0
  143. package/dist/components/color.d.ts +39 -0
  144. package/dist/components/color.js +92 -0
  145. package/dist/components/data-handler.svelte.d.ts +362 -0
  146. package/dist/components/data-handler.svelte.js +690 -0
  147. package/dist/components/file-icons.d.ts +25 -0
  148. package/dist/components/file-icons.js +46 -0
  149. package/dist/family-types.d.ts +45 -0
  150. package/dist/family-types.js +73 -0
  151. package/dist/generated/classes.d.ts +17 -0
  152. package/dist/generated/classes.js +2511 -0
  153. package/dist/generated/components.d.ts +39 -0
  154. package/dist/generated/components.js +28666 -0
  155. package/dist/generated/registry.d.ts +35 -0
  156. package/dist/generated/registry.js +2658 -0
  157. package/dist/index.d.ts +73 -0
  158. package/dist/index.js +99 -0
  159. package/dist/sepale-types.d.ts +8 -0
  160. package/dist/sepale-types.js +200 -0
  161. package/dist/theme.d.ts +123 -0
  162. package/dist/theme.js +226 -0
  163. package/dist/variants.d.ts +124 -0
  164. package/dist/variants.js +70 -0
  165. package/dist/vendor/air-datepicker/LICENSE.md +21 -0
  166. package/dist/vendor/air-datepicker/README.md +19 -0
  167. package/dist/vendor/air-datepicker/buttonPresets.d.ts +13 -0
  168. package/dist/vendor/air-datepicker/buttonPresets.js +12 -0
  169. package/dist/vendor/air-datepicker/consts.d.ts +18 -0
  170. package/dist/vendor/air-datepicker/consts.js +35 -0
  171. package/dist/vendor/air-datepicker/datepicker.d.ts +289 -0
  172. package/dist/vendor/air-datepicker/datepicker.js +1435 -0
  173. package/dist/vendor/air-datepicker/datepickerBody.d.ts +46 -0
  174. package/dist/vendor/air-datepicker/datepickerBody.js +386 -0
  175. package/dist/vendor/air-datepicker/datepickerButtons.d.ts +25 -0
  176. package/dist/vendor/air-datepicker/datepickerButtons.js +85 -0
  177. package/dist/vendor/air-datepicker/datepickerCell.d.ts +35 -0
  178. package/dist/vendor/air-datepicker/datepickerCell.js +297 -0
  179. package/dist/vendor/air-datepicker/datepickerKeyboard.d.ts +26 -0
  180. package/dist/vendor/air-datepicker/datepickerKeyboard.js +207 -0
  181. package/dist/vendor/air-datepicker/datepickerNav.d.ts +31 -0
  182. package/dist/vendor/air-datepicker/datepickerNav.js +200 -0
  183. package/dist/vendor/air-datepicker/datepickerTime.d.ts +60 -0
  184. package/dist/vendor/air-datepicker/datepickerTime.js +276 -0
  185. package/dist/vendor/air-datepicker/defaults.d.ts +65 -0
  186. package/dist/vendor/air-datepicker/defaults.js +79 -0
  187. package/dist/vendor/air-datepicker/index.d.ts +168 -0
  188. package/dist/vendor/air-datepicker/index.js +3 -0
  189. package/dist/vendor/air-datepicker/locale/ar.d.ts +4 -0
  190. package/dist/vendor/air-datepicker/locale/ar.js +14 -0
  191. package/dist/vendor/air-datepicker/locale/bg.d.ts +4 -0
  192. package/dist/vendor/air-datepicker/locale/bg.js +14 -0
  193. package/dist/vendor/air-datepicker/locale/ca.d.ts +4 -0
  194. package/dist/vendor/air-datepicker/locale/ca.js +14 -0
  195. package/dist/vendor/air-datepicker/locale/cs.d.ts +4 -0
  196. package/dist/vendor/air-datepicker/locale/cs.js +14 -0
  197. package/dist/vendor/air-datepicker/locale/da.d.ts +4 -0
  198. package/dist/vendor/air-datepicker/locale/da.js +14 -0
  199. package/dist/vendor/air-datepicker/locale/de.d.ts +4 -0
  200. package/dist/vendor/air-datepicker/locale/de.js +14 -0
  201. package/dist/vendor/air-datepicker/locale/el.d.ts +4 -0
  202. package/dist/vendor/air-datepicker/locale/el.js +14 -0
  203. package/dist/vendor/air-datepicker/locale/en.d.ts +4 -0
  204. package/dist/vendor/air-datepicker/locale/en.js +14 -0
  205. package/dist/vendor/air-datepicker/locale/es.d.ts +4 -0
  206. package/dist/vendor/air-datepicker/locale/es.js +14 -0
  207. package/dist/vendor/air-datepicker/locale/eu.d.ts +4 -0
  208. package/dist/vendor/air-datepicker/locale/eu.js +14 -0
  209. package/dist/vendor/air-datepicker/locale/fi.d.ts +4 -0
  210. package/dist/vendor/air-datepicker/locale/fi.js +14 -0
  211. package/dist/vendor/air-datepicker/locale/fr.d.ts +4 -0
  212. package/dist/vendor/air-datepicker/locale/fr.js +14 -0
  213. package/dist/vendor/air-datepicker/locale/hr.d.ts +4 -0
  214. package/dist/vendor/air-datepicker/locale/hr.js +14 -0
  215. package/dist/vendor/air-datepicker/locale/hu.d.ts +4 -0
  216. package/dist/vendor/air-datepicker/locale/hu.js +14 -0
  217. package/dist/vendor/air-datepicker/locale/id.d.ts +4 -0
  218. package/dist/vendor/air-datepicker/locale/id.js +14 -0
  219. package/dist/vendor/air-datepicker/locale/it.d.ts +4 -0
  220. package/dist/vendor/air-datepicker/locale/it.js +14 -0
  221. package/dist/vendor/air-datepicker/locale/ja.d.ts +4 -0
  222. package/dist/vendor/air-datepicker/locale/ja.js +14 -0
  223. package/dist/vendor/air-datepicker/locale/ko.d.ts +4 -0
  224. package/dist/vendor/air-datepicker/locale/ko.js +14 -0
  225. package/dist/vendor/air-datepicker/locale/nb.d.ts +4 -0
  226. package/dist/vendor/air-datepicker/locale/nb.js +14 -0
  227. package/dist/vendor/air-datepicker/locale/nl.d.ts +4 -0
  228. package/dist/vendor/air-datepicker/locale/nl.js +14 -0
  229. package/dist/vendor/air-datepicker/locale/pl.d.ts +4 -0
  230. package/dist/vendor/air-datepicker/locale/pl.js +14 -0
  231. package/dist/vendor/air-datepicker/locale/pt-BR.d.ts +4 -0
  232. package/dist/vendor/air-datepicker/locale/pt-BR.js +14 -0
  233. package/dist/vendor/air-datepicker/locale/pt.d.ts +4 -0
  234. package/dist/vendor/air-datepicker/locale/pt.js +14 -0
  235. package/dist/vendor/air-datepicker/locale/ro.d.ts +4 -0
  236. package/dist/vendor/air-datepicker/locale/ro.js +14 -0
  237. package/dist/vendor/air-datepicker/locale/ru.d.ts +4 -0
  238. package/dist/vendor/air-datepicker/locale/ru.js +14 -0
  239. package/dist/vendor/air-datepicker/locale/si.d.ts +4 -0
  240. package/dist/vendor/air-datepicker/locale/si.js +14 -0
  241. package/dist/vendor/air-datepicker/locale/sk.d.ts +4 -0
  242. package/dist/vendor/air-datepicker/locale/sk.js +14 -0
  243. package/dist/vendor/air-datepicker/locale/sl.d.ts +4 -0
  244. package/dist/vendor/air-datepicker/locale/sl.js +14 -0
  245. package/dist/vendor/air-datepicker/locale/sv.d.ts +4 -0
  246. package/dist/vendor/air-datepicker/locale/sv.js +14 -0
  247. package/dist/vendor/air-datepicker/locale/th.d.ts +4 -0
  248. package/dist/vendor/air-datepicker/locale/th.js +14 -0
  249. package/dist/vendor/air-datepicker/locale/tr.d.ts +4 -0
  250. package/dist/vendor/air-datepicker/locale/tr.js +16 -0
  251. package/dist/vendor/air-datepicker/locale/uk.d.ts +4 -0
  252. package/dist/vendor/air-datepicker/locale/uk.js +14 -0
  253. package/dist/vendor/air-datepicker/locale/zh.d.ts +4 -0
  254. package/dist/vendor/air-datepicker/locale/zh.js +14 -0
  255. package/dist/vendor/air-datepicker/typedefs.d.ts +3 -0
  256. package/dist/vendor/air-datepicker/typedefs.js +15 -0
  257. package/dist/vendor/air-datepicker/utils.d.ts +155 -0
  258. package/dist/vendor/air-datepicker/utils.js +395 -0
  259. package/dist/vendor/air-datepicker/withEvents.d.ts +1 -0
  260. package/dist/vendor/air-datepicker/withEvents.js +39 -0
  261. package/package.json +44 -3
@@ -0,0 +1,62 @@
1
+ <script module lang="ts">
2
+ /** What .hero is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'hero';
5
+
6
+ export const AXES = {
7
+ color: ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'],
8
+ size: ['xs', 'sm', 'md', 'lg', 'xl']
9
+ } as const;
10
+
11
+ export const FLAGS = {
12
+ center: 'Centre the text; best without media.',
13
+ reverse: 'Put the media first.'
14
+ } as const;
15
+ export const PARTS = ['content', 'title', 'body', 'actions', 'media'] as const;
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ /**
20
+ * The large opening block of a page: a title, a sentence, a call to action,
21
+ * and optionally an image beside them — stacked when there is no room.
22
+ */
23
+ import type { Snippet } from 'svelte';
24
+ import type { HTMLAttributes } from 'svelte/elements';
25
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
26
+
27
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'color' | 'title'> {
28
+ title?: string;
29
+ /** The heading level of the title. */
30
+ level?: 1 | 2 | 3;
31
+ actions?: Snippet;
32
+ /** An image, a video, an illustration. */
33
+ media?: Snippet;
34
+ class?: string;
35
+ /** The sentence under the title. */
36
+ children?: Snippet;
37
+ }
38
+
39
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
40
+ // resolved by name, so an axis a family drops changes nothing down here.
41
+ let {
42
+ title,
43
+ level = 1,
44
+ actions,
45
+ media,
46
+ class: klass = '',
47
+ children,
48
+ ...axes
49
+ }: Props = $props();
50
+
51
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
52
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
53
+ </script>
54
+
55
+ <section {...applied.rest} class={classes}>
56
+ <div class="hero-content">
57
+ {#if title}<svelte:element this={`h${level}`} class="hero-title">{title}</svelte:element>{/if}
58
+ {#if children}<p class="hero-body">{@render children()}</p>{/if}
59
+ {#if actions}<div class="hero-actions">{@render actions()}</div>{/if}
60
+ </div>
61
+ {#if media}<div class="hero-media">{@render media()}</div>{/if}
62
+ </section>
@@ -0,0 +1,33 @@
1
+ /** What .hero is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "hero";
4
+ export declare const AXES: {
5
+ readonly color: readonly ["primary", "secondary", "accent", "neutral", "info", "success", "warning", "error"];
6
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
7
+ };
8
+ export declare const FLAGS: {
9
+ readonly center: "Centre the text; best without media.";
10
+ readonly reverse: "Put the media first.";
11
+ };
12
+ export declare const PARTS: readonly ["content", "title", "body", "actions", "media"];
13
+ /**
14
+ * The large opening block of a page: a title, a sentence, a call to action,
15
+ * and optionally an image beside them — stacked when there is no room.
16
+ */
17
+ import type { Snippet } from 'svelte';
18
+ import type { HTMLAttributes } from 'svelte/elements';
19
+ import { type AxisProps } from '../variants.js';
20
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'color' | 'title'> {
21
+ title?: string;
22
+ /** The heading level of the title. */
23
+ level?: 1 | 2 | 3;
24
+ actions?: Snippet;
25
+ /** An image, a video, an illustration. */
26
+ media?: Snippet;
27
+ class?: string;
28
+ /** The sentence under the title. */
29
+ children?: Snippet;
30
+ }
31
+ declare const Hero: import("svelte").Component<Props, {}, "">;
32
+ type Hero = ReturnType<typeof Hero>;
33
+ export default Hero;
@@ -0,0 +1,184 @@
1
+ <script module lang="ts">
2
+ /** What .input is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'input';
5
+
6
+ export const AXES = {
7
+ color: ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'],
8
+ size: ['xs', 'sm', 'md', 'lg', 'xl'],
9
+ variant: ['ghost']
10
+ } as const;
11
+
12
+ export const FLAGS = {
13
+ nospinner: 'Hide the step arrows of a number field.'
14
+ } as const;
15
+ export const PARTS = [] as const;
16
+
17
+ </script>
18
+
19
+ <script lang="ts">
20
+ /**
21
+ * A text field. Plain by default — one `<input class="input">` — and a
22
+ * frame around the field (`<label class="input">`) as soon as it needs one:
23
+ * something drawn before or after the text (`start`, `end`). Suggestions
24
+ * are a Combobox's job.
25
+ *
26
+ * Validation stays the browser's: `required`, `pattern`, `min`… are plain
27
+ * attributes, and the message shown is the browser's own `validationMessage`,
28
+ * in the visitor's language. `error` forces one from outside.
29
+ */
30
+ import type { Snippet } from 'svelte';
31
+ import type { ClassValue, HTMLInputAttributes } from 'svelte/elements';
32
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
33
+
34
+ // `size` is ours — the HTML attribute of the same name is a character count
35
+ // our CSS overrides anyway.
36
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'color'> {
37
+ /**
38
+ * Colour the field by its own validity and reveal `hint` once the
39
+ * browser marks it invalid. The CSS drives this off `:user-invalid`,
40
+ * so it only fires after the visitor has actually had a go.
41
+ */
42
+ validator?: boolean;
43
+ /**
44
+ * Shown under the field when it is invalid. It has to be the field's
45
+ * next sibling for the CSS to reach it, which is the whole reason this
46
+ * is a prop and not something you write yourself. Without it, the
47
+ * message is the browser's own.
48
+ */
49
+ hint?: Snippet;
50
+ /**
51
+ * An error from outside (the server said no): marks the field invalid and
52
+ * shows this, for as long as it is given. The parent owns it — it knows
53
+ * the value and is the one able to test it — so the field never clears it.
54
+ */
55
+ error?: string | null;
56
+ /** The message of the last check, `false` when the field passed. Bindable. */
57
+ invalid?: string | false;
58
+ /** Drawn inside the frame, before the text: an icon, a label, a unit. */
59
+ start?: Snippet;
60
+ /** Drawn inside the frame, after the text. */
61
+ end?: Snippet;
62
+ /** The field grows with its text. A number adds that many characters of room. */
63
+ autowidth?: boolean | number;
64
+ /** The `<input>` itself. Bindable. */
65
+ element?: HTMLInputElement;
66
+ class?: string;
67
+ value?: any;
68
+ }
69
+
70
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
71
+ // resolved by name, so an axis a family drops changes nothing down here.
72
+ let {
73
+ validator = false,
74
+ hint,
75
+ error = null,
76
+ invalid = $bindable(false),
77
+ start,
78
+ end,
79
+ autowidth = false,
80
+ element = $bindable(),
81
+ value = $bindable(''),
82
+ class: klass = '',
83
+ disabled = false,
84
+ autocomplete,
85
+ oninput,
86
+ onfocus,
87
+ onblur,
88
+ onkeydown,
89
+ ...axes
90
+ }: Props = $props();
91
+
92
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
93
+
94
+ /* ── Validity ─────────────────────────────────────────────────────── */
95
+
96
+ /** What is shown under the field. A disabled field shows nothing. */
97
+ let message = $derived(disabled ? '' : error || (typeof invalid === 'string' ? invalid : ''));
98
+
99
+ /* An error from outside is the field's validity too: a form holding it
100
+ does not submit. */
101
+ $effect(() => {
102
+ element?.setCustomValidity(disabled || !error ? '' : error);
103
+ });
104
+
105
+ let classes = $derived(
106
+ cx(PREFIX, applied.classes, (validator || !!message) && 'validator', klass)
107
+ );
108
+
109
+ /** Check the field now and show the browser's message if it fails. */
110
+ export function isValid(): boolean {
111
+ if (!element || disabled) return !error;
112
+ invalid = element.validity.valid ? false : element.validationMessage;
113
+ return !error && !invalid;
114
+ }
115
+
116
+ export function focus() {
117
+ element?.focus();
118
+ }
119
+
120
+ /** Empty the field. */
121
+ export function clear() {
122
+ value = '';
123
+ }
124
+
125
+ /* ── Width ────────────────────────────────────────────────────────── */
126
+
127
+ let width = $derived(
128
+ autowidth
129
+ ? `${Math.max(`${value ?? ''}`.length, 5) + (typeof autowidth === 'number' ? autowidth : 0) + 2}ch`
130
+ : undefined
131
+ );
132
+
133
+ /* ── Events ───────────────────────────────────────────────────────── */
134
+
135
+ function handleInput(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
136
+ // A field shown invalid is re-checked as it is corrected.
137
+ if (invalid) invalid = e.currentTarget.validity.valid ? false : e.currentTarget.validationMessage;
138
+ oninput?.(e);
139
+ }
140
+
141
+ function handleBlur(e: FocusEvent & { currentTarget: EventTarget & HTMLInputElement }) {
142
+ if (validator) isValid();
143
+ onblur?.(e);
144
+ }
145
+
146
+ let framed = $derived(!!(start || end));
147
+ </script>
148
+
149
+ {#snippet field(cls: ClassValue | undefined, style: string | undefined)}
150
+ <!-- Ours first: what the caller writes in the spread still wins. -->
151
+ <input
152
+ aria-invalid={message ? 'true' : undefined}
153
+ {...applied.rest}
154
+ bind:this={element}
155
+ bind:value
156
+ class={cls}
157
+ style:width={style}
158
+ {disabled}
159
+ {autocomplete}
160
+ oninput={handleInput}
161
+ {onfocus}
162
+ onblur={handleBlur}
163
+ {onkeydown}
164
+ />
165
+ {/snippet}
166
+
167
+ {#if framed}
168
+ <label class={classes} style:width>
169
+ {@render start?.()}
170
+ {@render field(undefined, undefined)}
171
+ {@render end?.()}
172
+ </label>
173
+ {:else}
174
+ {@render field(classes, width)}
175
+ {/if}
176
+ <!-- An error from outside first, then the caller's hint, then the browser's
177
+ own message. -->
178
+ {#if error && !disabled}
179
+ <p class="validator-hint">{error}</p>
180
+ {:else if hint}
181
+ <p class="validator-hint">{@render hint()}</p>
182
+ {:else if message}
183
+ <p class="validator-hint">{message}</p>
184
+ {/if}
@@ -0,0 +1,65 @@
1
+ /** What .input is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "input";
4
+ export declare const AXES: {
5
+ readonly color: readonly ["primary", "secondary", "accent", "neutral", "info", "success", "warning", "error"];
6
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
7
+ readonly variant: readonly ["ghost"];
8
+ };
9
+ export declare const FLAGS: {
10
+ readonly nospinner: "Hide the step arrows of a number field.";
11
+ };
12
+ export declare const PARTS: readonly [];
13
+ /**
14
+ * A text field. Plain by default — one `<input class="input">` — and a
15
+ * frame around the field (`<label class="input">`) as soon as it needs one:
16
+ * something drawn before or after the text (`start`, `end`). Suggestions
17
+ * are a Combobox's job.
18
+ *
19
+ * Validation stays the browser's: `required`, `pattern`, `min`… are plain
20
+ * attributes, and the message shown is the browser's own `validationMessage`,
21
+ * in the visitor's language. `error` forces one from outside.
22
+ */
23
+ import type { Snippet } from 'svelte';
24
+ import type { HTMLInputAttributes } from 'svelte/elements';
25
+ import { type AxisProps } from '../variants.js';
26
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'color'> {
27
+ /**
28
+ * Colour the field by its own validity and reveal `hint` once the
29
+ * browser marks it invalid. The CSS drives this off `:user-invalid`,
30
+ * so it only fires after the visitor has actually had a go.
31
+ */
32
+ validator?: boolean;
33
+ /**
34
+ * Shown under the field when it is invalid. It has to be the field's
35
+ * next sibling for the CSS to reach it, which is the whole reason this
36
+ * is a prop and not something you write yourself. Without it, the
37
+ * message is the browser's own.
38
+ */
39
+ hint?: Snippet;
40
+ /**
41
+ * An error from outside (the server said no): marks the field invalid and
42
+ * shows this, for as long as it is given. The parent owns it — it knows
43
+ * the value and is the one able to test it — so the field never clears it.
44
+ */
45
+ error?: string | null;
46
+ /** The message of the last check, `false` when the field passed. Bindable. */
47
+ invalid?: string | false;
48
+ /** Drawn inside the frame, before the text: an icon, a label, a unit. */
49
+ start?: Snippet;
50
+ /** Drawn inside the frame, after the text. */
51
+ end?: Snippet;
52
+ /** The field grows with its text. A number adds that many characters of room. */
53
+ autowidth?: boolean | number;
54
+ /** The `<input>` itself. Bindable. */
55
+ element?: HTMLInputElement;
56
+ class?: string;
57
+ value?: any;
58
+ }
59
+ declare const Input: import("svelte").Component<Props, {
60
+ isValid: () => boolean;
61
+ focus: () => void;
62
+ clear: () => void;
63
+ }, "value" | "invalid" | "element">;
64
+ type Input = ReturnType<typeof Input>;
65
+ export default Input;
@@ -0,0 +1,38 @@
1
+ <script module lang="ts">
2
+ /** What .join is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015).
4
+
5
+ Each child must carry `join-item` for the seams to land; that class is
6
+ a part of this component, not a component of its own. */
7
+ export const PREFIX = 'join';
8
+
9
+ export const AXES = {
10
+ /* Which way the seams run. */
11
+ direction: ['horizontal', 'vertical']
12
+ } as const;
13
+
14
+ export const FLAGS = {} as const;
15
+ export const PARTS = ['item'] as const;
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ import type { Snippet } from 'svelte';
20
+ import type { HTMLAttributes } from 'svelte/elements';
21
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
22
+
23
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
24
+ class?: string;
25
+ children?: Snippet;
26
+ }
27
+
28
+ let {
29
+ class: klass = '',
30
+ children,
31
+ ...axes
32
+ }: Props = $props();
33
+
34
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
35
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
36
+ </script>
37
+
38
+ <div {...applied.rest} class={classes}>{@render children?.()}</div>
@@ -0,0 +1,21 @@
1
+ /** What .join is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015).
3
+
4
+ Each child must carry `join-item` for the seams to land; that class is
5
+ a part of this component, not a component of its own. */
6
+ export declare const PREFIX = "join";
7
+ export declare const AXES: {
8
+ readonly direction: readonly ["horizontal", "vertical"];
9
+ };
10
+ export declare const FLAGS: {};
11
+ export declare const PARTS: readonly ["item"];
12
+ import type { Snippet } from 'svelte';
13
+ import type { HTMLAttributes } from 'svelte/elements';
14
+ import { type AxisProps } from '../variants.js';
15
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
16
+ class?: string;
17
+ children?: Snippet;
18
+ }
19
+ declare const Join: import("svelte").Component<Props, {}, "">;
20
+ type Join = ReturnType<typeof Join>;
21
+ export default Join;
@@ -0,0 +1,284 @@
1
+ <script module lang="ts">
2
+ /** What .kanban is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'kanban';
5
+
6
+ export const AXES = {
7
+ size: ['xs', 'sm', 'md', 'lg', 'xl'],
8
+ /* Space between the columns. */
9
+ gap: ['none', 'tight', 'loose']
10
+ } as const;
11
+
12
+ export const FLAGS = {
13
+ loading: 'Fade the columns while new items are on their way.'
14
+ } as const;
15
+ export const PARTS = [
16
+ 'column',
17
+ 'column-over',
18
+ 'column-head',
19
+ 'column-title',
20
+ 'column-count',
21
+ 'column-body',
22
+ 'column-foot',
23
+ 'column-empty',
24
+ 'item',
25
+ 'item-selected',
26
+ 'item-dirty',
27
+ 'item-dragged',
28
+ 'marker'
29
+ ] as const;
30
+
31
+ export type KanbanColumn = {
32
+ id: string;
33
+ /** Defaults to the id. */
34
+ title?: string;
35
+ };
36
+
37
+ export type KanbanMove<Item> = {
38
+ item: Item;
39
+ /** The column it leaves. */
40
+ from: string;
41
+ /** The column it lands in: `from` when it only changed places. */
42
+ to: string;
43
+ /** Its place in `to`, counted without the item itself. */
44
+ index: number;
45
+ };
46
+ </script>
47
+
48
+ <script lang="ts" generics="Item">
49
+ /**
50
+ * Items in columns, one column per state they can be in. Like a table or a
51
+ * grid it knows nothing of where its items come from, and it moves nothing:
52
+ * dragging a card, or Shift + an arrow key on it, calls `onMove`, and the
53
+ * items only change when the caller changes them. Without `onMove` the board
54
+ * is read-only.
55
+ *
56
+ * The drag is the browser's own, which brings the drag image, Escape to
57
+ * cancel and the scrolling at the edges. An item naming a column that is not
58
+ * in `columns` is not drawn.
59
+ */
60
+ import { tick, type Snippet } from 'svelte';
61
+ import type { HTMLAttributes } from 'svelte/elements';
62
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
63
+
64
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
65
+ /** The items: your data, as is, each column keeping their order. */
66
+ items?: Item[];
67
+ /** The columns, in order. */
68
+ columns: KanbanColumn[];
69
+ /** The id of the column an item belongs to. */
70
+ columnOf: (item: Item) => string;
71
+ /** How one item is drawn, given its place in its column. */
72
+ item: Snippet<[Item, number]>;
73
+ /** An item's identity, for `selected`, `dirty` and keyed rendering. Defaults to its index. */
74
+ key?: (item: Item, index: number) => string;
75
+ /** Replaces a column's title and count. */
76
+ head?: Snippet<[KanbanColumn, Item[]]>;
77
+ /** Drawn under a column's items: an add button, usually. */
78
+ foot?: Snippet<[KanbanColumn]>;
79
+ /** Drawn in a column with no items. */
80
+ empty?: Snippet<[KanbanColumn]>;
81
+ /** Keys of the items to draw as selected. */
82
+ selected?: Iterable<string>;
83
+ /** Keys of the items to draw as changed and not yet saved. */
84
+ dirty?: Iterable<string>;
85
+ /** An item was dropped somewhere new. Given, it makes the items movable. */
86
+ onMove?: (move: KanbanMove<Item>) => void;
87
+ class?: string;
88
+ }
89
+
90
+ let {
91
+ items = [],
92
+ columns,
93
+ columnOf,
94
+ item,
95
+ key = (_item: Item, i: number) => String(i),
96
+ head,
97
+ foot,
98
+ empty,
99
+ selected,
100
+ dirty,
101
+ onMove,
102
+ class: klass = '',
103
+ ...axes
104
+ }: Props = $props();
105
+
106
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
107
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
108
+
109
+ let selectedSet = $derived(new Set(selected ?? []));
110
+ let dirtySet = $derived(new Set(dirty ?? []));
111
+
112
+ type Entry = { item: Item; key: string };
113
+
114
+ let lanes = $derived.by(() => {
115
+ const byColumn = new Map<string, Entry[]>(columns.map((c) => [c.id, []]));
116
+ items.forEach((it, i) => byColumn.get(columnOf(it))?.push({ item: it, key: key(it, i) }));
117
+ return columns.map((column) => ({ column, entries: byColumn.get(column.id)! }));
118
+ });
119
+
120
+ let root: HTMLDivElement | undefined = $state();
121
+
122
+ /* What is in flight. Read by the drag handlers, so it is set at once; what
123
+ the page shows follows a frame later, because restyling the source during
124
+ `dragstart` would end up in the drag image. */
125
+ let flight: { entry: Entry; from: string } | null = null;
126
+ let draggedKey = $state<string | null>(null);
127
+ let over = $state<{ column: string; index: number } | null>(null);
128
+
129
+ /* Where the drop would land, or null when it would change nothing. */
130
+ let marker = $derived.by(() => {
131
+ if (!over || draggedKey === null) return null;
132
+ const entries = lanes.find((lane) => lane.column.id === over!.column)?.entries ?? [];
133
+ if (entries.findIndex((e) => e.key === draggedKey) === over.index) return null;
134
+ const before = entries.filter((e) => e.key !== draggedKey)[over.index];
135
+ return { column: over.column, before: before?.key ?? null };
136
+ });
137
+
138
+ function dragstart(e: DragEvent, entry: Entry, from: string) {
139
+ flight = { entry, from };
140
+ if (e.dataTransfer) {
141
+ e.dataTransfer.effectAllowed = 'move';
142
+ /* Firefox starts no drag without data. */
143
+ e.dataTransfer.setData('text/plain', entry.key);
144
+ }
145
+ requestAnimationFrame(() => {
146
+ if (flight) draggedKey = entry.key;
147
+ });
148
+ }
149
+
150
+ /* The place under the pointer, counted without the item in flight. */
151
+ function placeAt(e: DragEvent & { currentTarget: HTMLElement }) {
152
+ const others = [...e.currentTarget.querySelectorAll<HTMLElement>('[data-kanban-key]')].filter(
153
+ (el) => el.dataset.kanbanKey !== flight!.entry.key
154
+ );
155
+ const next = others.findIndex((el) => {
156
+ const box = el.getBoundingClientRect();
157
+ return e.clientY < box.top + box.height / 2;
158
+ });
159
+ return next < 0 ? others.length : next;
160
+ }
161
+
162
+ function dragover(e: DragEvent & { currentTarget: HTMLElement }, column: string) {
163
+ /* Anything else dragged over the board is not ours to accept. */
164
+ if (!flight) return;
165
+ e.preventDefault();
166
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
167
+ const index = placeAt(e);
168
+ if (over?.column !== column || over.index !== index) over = { column, index };
169
+ }
170
+
171
+ /* By the pointer, not `relatedTarget`, which not every browser fills in. */
172
+ function dragleave(e: DragEvent & { currentTarget: HTMLElement }) {
173
+ const box = e.currentTarget.getBoundingClientRect();
174
+ const inside =
175
+ e.clientX > box.left && e.clientX < box.right && e.clientY > box.top && e.clientY < box.bottom;
176
+ if (!inside) over = null;
177
+ }
178
+
179
+ /* The place is read again rather than taken from `over`: a drop must not
180
+ depend on the last dragover having been drawn. */
181
+ function drop(e: DragEvent & { currentTarget: HTMLElement }, column: string) {
182
+ if (!flight) return;
183
+ e.preventDefault();
184
+ const index = placeAt(e);
185
+ const { entry, from } = flight;
186
+ const stays =
187
+ from === column &&
188
+ lanes.find((lane) => lane.column.id === column)?.entries.findIndex((other) => other.key === entry.key) === index;
189
+ if (!stays) onMove?.({ item: entry.item, from, to: column, index });
190
+ dragend();
191
+ }
192
+
193
+ function dragend() {
194
+ flight = null;
195
+ draggedKey = null;
196
+ over = null;
197
+ }
198
+
199
+ /* Shift + an arrow: the same move as a drop, then the focus follows the
200
+ item, whose element the move may have replaced. */
201
+ function keydown(
202
+ e: KeyboardEvent & { currentTarget: HTMLElement },
203
+ entry: Entry,
204
+ laneIndex: number,
205
+ index: number
206
+ ) {
207
+ if (!onMove || !e.shiftKey || e.target !== e.currentTarget) return;
208
+ const lane = lanes[laneIndex]!;
209
+ const rtl = getComputedStyle(e.currentTarget).direction === 'rtl';
210
+ let to = laneIndex;
211
+ let place = index;
212
+ if (e.key === 'ArrowUp') place = index - 1;
213
+ else if (e.key === 'ArrowDown') place = index + 1;
214
+ else if (e.key === 'ArrowLeft') to = laneIndex + (rtl ? 1 : -1);
215
+ else if (e.key === 'ArrowRight') to = laneIndex + (rtl ? -1 : 1);
216
+ else return;
217
+ e.preventDefault();
218
+ const target = lanes[to];
219
+ if (!target || place < 0) return;
220
+ if (to === laneIndex && place >= lane.entries.length) return;
221
+ onMove({
222
+ item: entry.item,
223
+ from: lane.column.id,
224
+ to: target.column.id,
225
+ index: Math.min(place, target.entries.length)
226
+ });
227
+ tick().then(() =>
228
+ root?.querySelector<HTMLElement>(`[data-kanban-key="${CSS.escape(entry.key)}"]`)?.focus()
229
+ );
230
+ }
231
+ </script>
232
+
233
+ <div {...applied.rest} bind:this={root} class={classes}>
234
+ {#each lanes as lane, laneIndex (lane.column.id)}
235
+ {@const title = lane.column.title ?? lane.column.id}
236
+ <section
237
+ class={cx(`${PREFIX}-column`, over?.column === lane.column.id && `${PREFIX}-column-over`)}
238
+ aria-label={title}
239
+ ondragover={(e) => dragover(e, lane.column.id)}
240
+ ondragleave={dragleave}
241
+ ondrop={(e) => drop(e, lane.column.id)}
242
+ >
243
+ <header class={`${PREFIX}-column-head`}>
244
+ {#if head}
245
+ {@render head(lane.column, lane.entries.map((e) => e.item))}
246
+ {:else}
247
+ <span class={`${PREFIX}-column-title`}>{title}</span>
248
+ <span class={`${PREFIX}-column-count`}>{lane.entries.length}</span>
249
+ {/if}
250
+ </header>
251
+ <ul class={`${PREFIX}-column-body`}>
252
+ {#each lane.entries as entry, i (entry.key)}
253
+ {#if marker?.column === lane.column.id && marker.before === entry.key}
254
+ <li class={`${PREFIX}-marker`} aria-hidden="true"></li>
255
+ {/if}
256
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -->
257
+ <li
258
+ class={cx(
259
+ `${PREFIX}-item`,
260
+ selectedSet.has(entry.key) && `${PREFIX}-item-selected`,
261
+ dirtySet.has(entry.key) && `${PREFIX}-item-dirty`,
262
+ draggedKey === entry.key && `${PREFIX}-item-dragged`
263
+ )}
264
+ data-kanban-key={entry.key}
265
+ draggable={onMove ? 'true' : undefined}
266
+ tabindex={onMove ? 0 : undefined}
267
+ aria-keyshortcuts={onMove ? 'Shift+ArrowUp Shift+ArrowDown Shift+ArrowLeft Shift+ArrowRight' : undefined}
268
+ ondragstart={(e) => dragstart(e, entry, lane.column.id)}
269
+ ondragend={dragend}
270
+ onkeydown={(e) => keydown(e, entry, laneIndex, i)}
271
+ >
272
+ {@render item(entry.item, i)}
273
+ </li>
274
+ {:else}
275
+ {#if empty && !marker}<li class={`${PREFIX}-column-empty`}>{@render empty(lane.column)}</li>{/if}
276
+ {/each}
277
+ {#if marker?.column === lane.column.id && marker.before === null}
278
+ <li class={`${PREFIX}-marker`} aria-hidden="true"></li>
279
+ {/if}
280
+ </ul>
281
+ {#if foot}<footer class={`${PREFIX}-column-foot`}>{@render foot(lane.column)}</footer>{/if}
282
+ </section>
283
+ {/each}
284
+ </div>