@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,21 @@
1
+ /** What .mobile-frame is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "mobile-frame";
4
+ export declare const AXES: {};
5
+ export declare const FLAGS: {};
6
+ export declare const PARTS: readonly ["screen"];
7
+ /**
8
+ * A phone drawn around content — for showcases and screenshots, not an
9
+ * emulator. Its width is `--mobile-frame-width`; the height follows.
10
+ */
11
+ import type { Snippet } from 'svelte';
12
+ import type { HTMLAttributes } from 'svelte/elements';
13
+ import { type AxisProps } from '../variants.js';
14
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
15
+ class?: string;
16
+ /** What the screen shows. It scrolls. */
17
+ children?: Snippet;
18
+ }
19
+ declare const MobileFrame: import("svelte").Component<Props, {}, "">;
20
+ type MobileFrame = ReturnType<typeof MobileFrame>;
21
+ export default MobileFrame;
@@ -0,0 +1,49 @@
1
+ <script module lang="ts">
2
+ /** What .navbar is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'navbar';
5
+
6
+ export const AXES = {
7
+ size: ['xs', 'sm', 'md', 'lg', 'xl']
8
+ } as const;
9
+
10
+ export const FLAGS = {
11
+ sticky: 'Let the bar follow the page instead of scrolling away.'
12
+ } as const;
13
+ export const PARTS = ['start', 'center', 'end'] as const;
14
+ </script>
15
+
16
+ <script lang="ts">
17
+ import type { Snippet } from 'svelte';
18
+ import type { HTMLAttributes } from 'svelte/elements';
19
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
20
+
21
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLElement> {
22
+ /** The three zones. start and end absorb the slack, so whatever is in
23
+ center stays centred while either side grows. */
24
+ start?: Snippet;
25
+ center?: Snippet;
26
+ end?: Snippet;
27
+ class?: string;
28
+ children?: Snippet;
29
+ }
30
+
31
+ let {
32
+ start,
33
+ center,
34
+ end,
35
+ class: klass = '',
36
+ children,
37
+ ...axes
38
+ }: Props = $props();
39
+
40
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
41
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
42
+ </script>
43
+
44
+ <nav {...applied.rest} class={classes}>
45
+ {#if start}<div class="navbar-start">{@render start()}</div>{/if}
46
+ {#if center}<div class="navbar-center">{@render center()}</div>{/if}
47
+ {#if end}<div class="navbar-end">{@render end()}</div>{/if}
48
+ {@render children?.()}
49
+ </nav>
@@ -0,0 +1,25 @@
1
+ /** What .navbar is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "navbar";
4
+ export declare const AXES: {
5
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
6
+ };
7
+ export declare const FLAGS: {
8
+ readonly sticky: "Let the bar follow the page instead of scrolling away.";
9
+ };
10
+ export declare const PARTS: readonly ["start", "center", "end"];
11
+ import type { Snippet } from 'svelte';
12
+ import type { HTMLAttributes } from 'svelte/elements';
13
+ import { type AxisProps } from '../variants.js';
14
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLElement> {
15
+ /** The three zones. start and end absorb the slack, so whatever is in
16
+ center stays centred while either side grows. */
17
+ start?: Snippet;
18
+ center?: Snippet;
19
+ end?: Snippet;
20
+ class?: string;
21
+ children?: Snippet;
22
+ }
23
+ declare const Navbar: import("svelte").Component<Props, {}, "">;
24
+ type Navbar = ReturnType<typeof Navbar>;
25
+ export default Navbar;
@@ -0,0 +1,255 @@
1
+ <script module lang="ts">
2
+ /** What .number-input is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'number-input';
5
+
6
+ export const AXES = {
7
+ size: ['xs', 'sm', 'md', 'lg', 'xl']
8
+ } as const;
9
+
10
+ export const FLAGS = {} as const;
11
+ export const PARTS = ['dec', 'field', 'inc'] as const;
12
+ </script>
13
+
14
+ <script lang="ts">
15
+ /**
16
+ * A number field, aligned to the end like a figure in a column.
17
+ *
18
+ * NOT an `<input type="number">`: that one follows the browser's locale, and
19
+ * in French Firefox refuses `.` while Chrome takes it, so the same keystroke
20
+ * gives a number in one and an empty, invalid field in the other. This is a
21
+ * text field with a numeric keyboard (`inputmode`), and it reads `.` and `,`
22
+ * alike as the decimal separator. What the native field did, it does:
23
+ *
24
+ * - typing is filtered to what a number can be on its way (`-`, digits,
25
+ * one separator); a paste is cleaned of its spaces first;
26
+ * - ↑ / ↓ step by `step`, within `min` / `max`;
27
+ * - `min`, `max` and `step` set the field's validity, so `:invalid` and
28
+ * the validator behave as before; `required` is the native one;
29
+ * - with a `name`, a hidden input submits the value written with a `.`,
30
+ * whatever separator was typed.
31
+ *
32
+ * With `controls`, a − and a + frame it. The buttons are structure, not
33
+ * paint, so they are a prop and not a variant: a family draws them when
34
+ * they are there.
35
+ *
36
+ * `value` is bindable and always a number: what the field holds, parsed,
37
+ * or `null` when it is empty or holds no finite number. Never a string.
38
+ */
39
+ import { untrack, type Snippet } from 'svelte';
40
+ import type { HTMLInputAttributes } from 'svelte/elements';
41
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
42
+
43
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'value' | 'type'> {
44
+ /** Colour the field by its own validity (`required`, `min`, `max`, `step`)
45
+ and reveal `hint` once the browser marks it invalid. */
46
+ validator?: boolean;
47
+ /** Shown under the field when it is invalid. */
48
+ hint?: Snippet;
49
+ /** Bindable. Always a number, or `null` when the field is empty. */
50
+ value?: number | null;
51
+ /** Draw a − and a + around the field. Off: the field alone. */
52
+ controls?: boolean;
53
+ min?: number;
54
+ max?: number;
55
+ /** `any`: every value is valid, and the buttons move by 1. */
56
+ step?: number | 'any';
57
+ disabled?: boolean;
58
+ /** After every change, typed or stepped. */
59
+ onValue?: (value: number | null) => void;
60
+ decreaseLabel?: string;
61
+ increaseLabel?: string;
62
+ /** Submitted with the form, as a number written with a `.`. */
63
+ name?: string;
64
+ /** What the browser says when the field is invalid. */
65
+ messages?: Partial<{ nan: string; min: string; max: string; step: string }>;
66
+ class?: string;
67
+ }
68
+
69
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
70
+ // resolved by name, so an axis a family drops changes nothing down here.
71
+ let {
72
+ validator = false,
73
+ hint,
74
+ value = $bindable(null),
75
+ controls = false,
76
+ min,
77
+ max,
78
+ step = 1,
79
+ disabled = false,
80
+ onValue,
81
+ decreaseLabel = 'Decrease',
82
+ increaseLabel = 'Increase',
83
+ name,
84
+ messages = {},
85
+ /* The field needs these four itself: taken out of the rest, and the
86
+ caller's own called after the field's (see the <input>). */
87
+ onbeforeinput: theirBeforeInput,
88
+ oninput: theirInput,
89
+ onblur: theirBlur,
90
+ onkeydown: theirKeydown,
91
+ class: klass = '',
92
+ ...axes
93
+ }: Props = $props();
94
+
95
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
96
+ let classes = $derived(cx(PREFIX, applied.classes, validator && 'validator', klass));
97
+
98
+ let field: HTMLInputElement | undefined = $state();
99
+
100
+ /* What a number can be while it is being typed: `-`, `12,`, `.5`. */
101
+ const PARTIAL = /^-?\d*(?:[.,]\d*)?$/;
102
+ /* Spaces a paste may carry: thousands, French narrow no-break included. */
103
+ const SPACES = /[\s  ]/g;
104
+
105
+ /** A number from text: `.` or `,` as the separator; null when there is none. */
106
+ function parse(text: string): number | null {
107
+ const s = text.trim().replace(',', '.');
108
+ if (!/\d/.test(s)) return null;
109
+ const n = Number(s);
110
+ return Number.isFinite(n) ? n : null;
111
+ }
112
+
113
+ const format = (n: number | null | undefined) => (n == null || !Number.isFinite(n) ? '' : String(n));
114
+
115
+ /* The text in the field, kept as typed (`12,5` stays `12,5`). It follows
116
+ `value` only when `value` changes from outside. */
117
+ let text = $state(untrack(() => format(value)));
118
+ $effect(() => {
119
+ const outside = value ?? null;
120
+ if (untrack(() => parse(text)) !== outside) text = format(outside);
121
+ });
122
+
123
+ function commit(next: string) {
124
+ text = next;
125
+ value = parse(next);
126
+ onValue?.(value);
127
+ }
128
+
129
+ /* Keep out what no number can become; clean a paste of its spaces. */
130
+ function guard(e: InputEvent) {
131
+ if (!field || e.data == null) return;
132
+ const from = field.selectionStart ?? field.value.length;
133
+ const to = field.selectionEnd ?? from;
134
+ const data = e.data.replace(SPACES, '');
135
+ const next = field.value.slice(0, from) + data + field.value.slice(to);
136
+ if (!PARTIAL.test(next)) return e.preventDefault();
137
+ if (data !== e.data) {
138
+ e.preventDefault();
139
+ field.setRangeText(data, from, to, 'end');
140
+ commit(field.value);
141
+ }
142
+ }
143
+
144
+ /* The last word, for input that skipped beforeinput (some mobile IMEs). */
145
+ function sanitise() {
146
+ if (!field) return;
147
+ if (!PARTIAL.test(field.value)) field.value = text;
148
+ else commit(field.value);
149
+ }
150
+
151
+ /* `12,` or `-` left behind: tidied once the field is left. */
152
+ function tidy() {
153
+ if (text === '-' || /^-?[.,]$/.test(text)) commit('');
154
+ else if (/[.,]$/.test(text)) commit(text.slice(0, -1));
155
+ }
156
+
157
+ function arrows(e: KeyboardEvent) {
158
+ if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
159
+ e.preventDefault();
160
+ stepBy(e.key === 'ArrowUp');
161
+ }
162
+
163
+ /** Digits after the point, to keep 0.1 + 0.2 from showing 0.30000000000000004. */
164
+ const decimals = (n: number | null | undefined) =>
165
+ n == null ? 0 : (String(n).split('.')[1]?.length ?? 0);
166
+
167
+ function stepBy(up: boolean) {
168
+ if (disabled) return;
169
+ const by = step === 'any' ? 1 : step;
170
+ const current = parse(text);
171
+ let next = current === null ? (min ?? 0) : current + (up ? by : -by);
172
+ /* On the grid `min + k × step`, as stepUp() / stepDown() do. */
173
+ if (current !== null && step !== 'any') {
174
+ const base = min ?? 0;
175
+ next = base + Math.round((next - base) / by) * by;
176
+ }
177
+ next = Math.min(max ?? Infinity, Math.max(min ?? -Infinity, next));
178
+ next = Number(next.toFixed(Math.max(decimals(by), decimals(current), decimals(min))));
179
+ commit(format(next));
180
+ field?.dispatchEvent(new Event('change', { bubbles: true }));
181
+ }
182
+
183
+ /* The validity the native field had: min, max, step, and "not a number". */
184
+ let problem = $derived.by(() => {
185
+ const n = parse(text);
186
+ if (n === null) return text.trim() && !PARTIAL.test(text.trim()) ? (messages.nan ?? 'Enter a number.') : '';
187
+ if (min !== undefined && n < min) return messages.min ?? `Enter ${min} or more.`;
188
+ if (max !== undefined && n > max) return messages.max ?? `Enter ${max} or less.`;
189
+ if (step !== 'any' && step > 0) {
190
+ const k = (n - (min ?? 0)) / step;
191
+ if (Math.abs(k - Math.round(k)) > 1e-9) return messages.step ?? `Enter a multiple of ${step}.`;
192
+ }
193
+ return '';
194
+ });
195
+ $effect(() => field?.setCustomValidity(problem));
196
+
197
+ /* A minus on the phone's keyboard only where a negative is allowed: iOS's
198
+ decimal pad has none. */
199
+ let mode = $derived<'decimal' | 'text'>(min !== undefined && min >= 0 ? 'decimal' : 'text');
200
+ </script>
201
+
202
+ <div class={classes}>
203
+ {#if controls}
204
+ <button
205
+ type="button"
206
+ class="number-input-dec"
207
+ tabindex="-1"
208
+ aria-label={decreaseLabel}
209
+ disabled={disabled || (min !== undefined && value !== null && value <= min)}
210
+ onclick={() => stepBy(false)}>−</button
211
+ >
212
+ {/if}
213
+ <input
214
+ {...applied.rest}
215
+ bind:this={field}
216
+ type="text"
217
+ inputmode={mode}
218
+ autocomplete="off"
219
+ role="spinbutton"
220
+ aria-valuenow={value ?? undefined}
221
+ aria-valuemin={min}
222
+ aria-valuemax={max}
223
+ class="number-input-field"
224
+ value={text}
225
+ {disabled}
226
+ onbeforeinput={(e) => {
227
+ guard(e);
228
+ theirBeforeInput?.(e);
229
+ }}
230
+ oninput={(e) => {
231
+ sanitise();
232
+ theirInput?.(e);
233
+ }}
234
+ onblur={(e) => {
235
+ tidy();
236
+ theirBlur?.(e);
237
+ }}
238
+ onkeydown={(e) => {
239
+ arrows(e);
240
+ theirKeydown?.(e);
241
+ }}
242
+ />
243
+ {#if name}<input type="hidden" {name} value={format(value)} {disabled} />{/if}
244
+ {#if controls}
245
+ <button
246
+ type="button"
247
+ class="number-input-inc"
248
+ tabindex="-1"
249
+ aria-label={increaseLabel}
250
+ disabled={disabled || (max !== undefined && value !== null && value >= max)}
251
+ onclick={() => stepBy(true)}>+</button
252
+ >
253
+ {/if}
254
+ </div>
255
+ {#if hint}<p class="validator-hint">{@render hint()}</p>{/if}
@@ -0,0 +1,68 @@
1
+ /** What .number-input is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "number-input";
4
+ export declare const AXES: {
5
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
6
+ };
7
+ export declare const FLAGS: {};
8
+ export declare const PARTS: readonly ["dec", "field", "inc"];
9
+ /**
10
+ * A number field, aligned to the end like a figure in a column.
11
+ *
12
+ * NOT an `<input type="number">`: that one follows the browser's locale, and
13
+ * in French Firefox refuses `.` while Chrome takes it, so the same keystroke
14
+ * gives a number in one and an empty, invalid field in the other. This is a
15
+ * text field with a numeric keyboard (`inputmode`), and it reads `.` and `,`
16
+ * alike as the decimal separator. What the native field did, it does:
17
+ *
18
+ * - typing is filtered to what a number can be on its way (`-`, digits,
19
+ * one separator); a paste is cleaned of its spaces first;
20
+ * - ↑ / ↓ step by `step`, within `min` / `max`;
21
+ * - `min`, `max` and `step` set the field's validity, so `:invalid` and
22
+ * the validator behave as before; `required` is the native one;
23
+ * - with a `name`, a hidden input submits the value written with a `.`,
24
+ * whatever separator was typed.
25
+ *
26
+ * With `controls`, a − and a + frame it. The buttons are structure, not
27
+ * paint, so they are a prop and not a variant: a family draws them when
28
+ * they are there.
29
+ *
30
+ * `value` is bindable and always a number: what the field holds, parsed,
31
+ * or `null` when it is empty or holds no finite number. Never a string.
32
+ */
33
+ import { type Snippet } from 'svelte';
34
+ import type { HTMLInputAttributes } from 'svelte/elements';
35
+ import { type AxisProps } from '../variants.js';
36
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLInputAttributes, 'size' | 'value' | 'type'> {
37
+ /** Colour the field by its own validity (`required`, `min`, `max`, `step`)
38
+ and reveal `hint` once the browser marks it invalid. */
39
+ validator?: boolean;
40
+ /** Shown under the field when it is invalid. */
41
+ hint?: Snippet;
42
+ /** Bindable. Always a number, or `null` when the field is empty. */
43
+ value?: number | null;
44
+ /** Draw a − and a + around the field. Off: the field alone. */
45
+ controls?: boolean;
46
+ min?: number;
47
+ max?: number;
48
+ /** `any`: every value is valid, and the buttons move by 1. */
49
+ step?: number | 'any';
50
+ disabled?: boolean;
51
+ /** After every change, typed or stepped. */
52
+ onValue?: (value: number | null) => void;
53
+ decreaseLabel?: string;
54
+ increaseLabel?: string;
55
+ /** Submitted with the form, as a number written with a `.`. */
56
+ name?: string;
57
+ /** What the browser says when the field is invalid. */
58
+ messages?: Partial<{
59
+ nan: string;
60
+ min: string;
61
+ max: string;
62
+ step: string;
63
+ }>;
64
+ class?: string;
65
+ }
66
+ declare const NumberInput: import("svelte").Component<Props, {}, "value">;
67
+ type NumberInput = ReturnType<typeof NumberInput>;
68
+ export default NumberInput;
@@ -0,0 +1,169 @@
1
+ <script module lang="ts">
2
+ /** What .pagination is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015).
4
+
5
+ The pagination draws its own pages: `.pagination-item`,
6
+ `.pagination-gap` and `.pagination-text` are PARTS. The current page is marked by
7
+ `aria-current="page"`, not by a class. */
8
+ export const PREFIX = 'pagination';
9
+
10
+ export const AXES = {
11
+ /* The fill of the current page. */
12
+ color: ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'],
13
+ size: ['xs', 'sm', 'md', 'lg', 'xl'],
14
+ /* How the pages are drawn. All three keep the same parts and props
15
+ (a different structure would be another component):
16
+ dots — a dot per page instead of its number, as under a carousel;
17
+ bars — a segment per page, stretched across the row;
18
+ count — no pages at all, "3 / 8" between previous and next. */
19
+ variant: ['dots', 'bars', 'count']
20
+ } as const;
21
+
22
+ export const FLAGS = {} as const;
23
+ export const PARTS = ['item', 'gap', 'text', 'number'] as const;
24
+ </script>
25
+
26
+ <script lang="ts">
27
+ /**
28
+ * A row of pages to jump between. Pages are numbered from 1. Past a few
29
+ * pages, the ones far from the current page fold into a gap: the first
30
+ * and the last stay, and `siblings` pages on each side of the current one.
31
+ * The dots and bars variants always show every page, and no previous /
32
+ * next; the count variant shows no page, only "3 / 8" between them.
33
+ */
34
+ import type { HTMLAttributes } from 'svelte/elements';
35
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
36
+
37
+ // `color` is ours — the HTML attribute of the same name is long obsolete.
38
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'color'> {
39
+ /** How many pages there are. */
40
+ count: number;
41
+ /** The current page, from 1. Bindable. */
42
+ page?: number;
43
+ /** Pages kept on each side of the current one before a gap. Default 1. */
44
+ siblings?: number;
45
+ /** Keep the first and last pages, with a gap, around the window. Default no.
46
+ Without: only the current page and its siblings — `< 6 >` with
47
+ siblings 0, `< 5 6 7 >` with 1. */
48
+ edges?: boolean;
49
+ /** Draw previous / next. Default: yes, except in the dots and bars variants. */
50
+ controls?: boolean;
51
+ /** Called with the new page after every change. */
52
+ onPage?: (page: number) => void;
53
+ /** The accessible name of a page. Default `Page N`. */
54
+ pageLabel?: (page: number) => string;
55
+ /** The text of the count variant. Default `3 / 8`. */
56
+ countLabel?: (page: number, count: number) => string;
57
+ /** The accessible names of the controls. */
58
+ previousLabel?: string;
59
+ nextLabel?: string;
60
+ class?: string;
61
+ }
62
+
63
+ // The axes are deliberately NOT destructured — except `variant`, which
64
+ // also changes what is drawn. It is handed back to applyAxes below.
65
+ let {
66
+ count,
67
+ page = $bindable(1),
68
+ siblings = 1,
69
+ edges = false,
70
+ controls,
71
+ onPage,
72
+ pageLabel = (p: number) => `Page ${p}`,
73
+ countLabel = (p: number, n: number) => `${p} / ${n}`,
74
+ previousLabel = 'Previous page',
75
+ nextLabel = 'Next page',
76
+ variant,
77
+ class: klass = '',
78
+ ...axes
79
+ }: Props = $props();
80
+
81
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, { ...axes, variant }));
82
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
83
+
84
+ /** Every page drawn, none folded, no number shown. */
85
+ let marks = $derived(variant === 'dots' || variant === 'bars');
86
+ let showControls = $derived(controls ?? !marks);
87
+
88
+ /** The pages to draw, with `null` for a gap. */
89
+ let pages = $derived.by((): (number | null)[] => {
90
+ const all = Array.from({ length: Math.max(0, count) }, (_, i) => i + 1);
91
+ if (variant === 'count') return [];
92
+ if (marks) return all;
93
+ // No edges: a window of 2 * siblings + 1 pages, slid back inside the row at its ends.
94
+ if (!edges) {
95
+ const w = Math.min(count, 2 * siblings + 1);
96
+ const start = Math.min(Math.max(1, page - siblings), count - w + 1);
97
+ return all.slice(start - 1, start - 1 + w);
98
+ }
99
+ // first + last + current + siblings + two gaps: below that, folding saves nothing
100
+ if (count <= 2 * siblings + 5) return all;
101
+ // Always 2 * siblings + 5 slots, so the row keeps its width: near an
102
+ // end, the pages the missing gap would have taken are shown instead.
103
+ const edge = 2 * siblings + 3;
104
+ if (page <= siblings + 3) return [...all.slice(0, edge), null, count];
105
+ if (page >= count - siblings - 2) return [1, null, ...all.slice(count - edge)];
106
+ return [1, null, ...all.slice(page - siblings - 1, page + siblings), null, count];
107
+ });
108
+
109
+ /** Which way the last move went, for a family that animates the numbers.
110
+ Unset until the first move, so nothing plays on load. */
111
+ let direction = $state<'next' | 'previous'>();
112
+
113
+ function go(p: number) {
114
+ const next = Math.min(Math.max(1, p), count);
115
+ if (next === page) return;
116
+ direction = next > page ? 'next' : 'previous';
117
+ page = next;
118
+ onPage?.(next);
119
+ }
120
+ </script>
121
+
122
+ <nav {...applied.rest} class={classes} data-direction={direction}>
123
+ {#if showControls}
124
+ <button
125
+ type="button"
126
+ class={`${PREFIX}-item`}
127
+ aria-label={previousLabel}
128
+ disabled={page <= 1}
129
+ onclick={() => go(page - 1)}
130
+ >
131
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg>
132
+ </button>
133
+ {/if}
134
+
135
+ {#if variant === 'count'}
136
+ <span class={`${PREFIX}-text`} aria-live="polite">{countLabel(page, count)}</span>
137
+ {/if}
138
+
139
+ <!-- Unkeyed: the slots stay put and only their numbers change, so the
140
+ current page's fill moves between fixed buttons. -->
141
+ {#each pages as p}
142
+ {#if p === null}
143
+ <span class={`${PREFIX}-gap`} aria-hidden="true">…</span>
144
+ {:else}
145
+ <button
146
+ type="button"
147
+ class={`${PREFIX}-item`}
148
+ aria-label={pageLabel(p)}
149
+ aria-current={p === page ? 'page' : undefined}
150
+ onclick={() => go(p)}
151
+ >
152
+ <!-- Keyed: a new number is a new span, so its entrance can play. -->
153
+ {#key p}<span class={`${PREFIX}-number`}>{p}</span>{/key}
154
+ </button>
155
+ {/if}
156
+ {/each}
157
+
158
+ {#if showControls}
159
+ <button
160
+ type="button"
161
+ class={`${PREFIX}-item`}
162
+ aria-label={nextLabel}
163
+ disabled={page >= count}
164
+ onclick={() => go(page + 1)}
165
+ >
166
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>
167
+ </button>
168
+ {/if}
169
+ </nav>
@@ -0,0 +1,50 @@
1
+ /** What .pagination is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015).
3
+
4
+ The pagination draws its own pages: `.pagination-item`,
5
+ `.pagination-gap` and `.pagination-text` are PARTS. The current page is marked by
6
+ `aria-current="page"`, not by a class. */
7
+ export declare const PREFIX = "pagination";
8
+ export declare const AXES: {
9
+ readonly color: readonly ["primary", "secondary", "accent", "neutral", "info", "success", "warning", "error"];
10
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
11
+ readonly variant: readonly ["dots", "bars", "count"];
12
+ };
13
+ export declare const FLAGS: {};
14
+ export declare const PARTS: readonly ["item", "gap", "text", "number"];
15
+ /**
16
+ * A row of pages to jump between. Pages are numbered from 1. Past a few
17
+ * pages, the ones far from the current page fold into a gap: the first
18
+ * and the last stay, and `siblings` pages on each side of the current one.
19
+ * The dots and bars variants always show every page, and no previous /
20
+ * next; the count variant shows no page, only "3 / 8" between them.
21
+ */
22
+ import type { HTMLAttributes } from 'svelte/elements';
23
+ import { type AxisProps } from '../variants.js';
24
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'color'> {
25
+ /** How many pages there are. */
26
+ count: number;
27
+ /** The current page, from 1. Bindable. */
28
+ page?: number;
29
+ /** Pages kept on each side of the current one before a gap. Default 1. */
30
+ siblings?: number;
31
+ /** Keep the first and last pages, with a gap, around the window. Default no.
32
+ Without: only the current page and its siblings — `< 6 >` with
33
+ siblings 0, `< 5 6 7 >` with 1. */
34
+ edges?: boolean;
35
+ /** Draw previous / next. Default: yes, except in the dots and bars variants. */
36
+ controls?: boolean;
37
+ /** Called with the new page after every change. */
38
+ onPage?: (page: number) => void;
39
+ /** The accessible name of a page. Default `Page N`. */
40
+ pageLabel?: (page: number) => string;
41
+ /** The text of the count variant. Default `3 / 8`. */
42
+ countLabel?: (page: number, count: number) => string;
43
+ /** The accessible names of the controls. */
44
+ previousLabel?: string;
45
+ nextLabel?: string;
46
+ class?: string;
47
+ }
48
+ declare const Pagination: import("svelte").Component<Props, {}, "page">;
49
+ type Pagination = ReturnType<typeof Pagination>;
50
+ export default Pagination;