@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,105 @@
1
+ <script module lang="ts">
2
+ /** What .switch is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'switch';
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
+ block: 'Fill the width, the items sharing it equally.'
13
+ } as const;
14
+ export const PARTS = ['item', 'input', 'indicator'] as const;
15
+ </script>
16
+
17
+ <script lang="ts">
18
+ /**
19
+ * One choice among a few, as a pill track: a radio group in disguise.
20
+ *
21
+ * `options` are strings, or `{ value, label, disabled }`. `value` is
22
+ * bindable — it is the field's own value.
23
+ *
24
+ * An indicator slides from the old choice to the new one (`animated`,
25
+ * off when the visitor asks for reduced motion).
26
+ */
27
+ import type { HTMLAttributes } from 'svelte/elements';
28
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
29
+
30
+ type Option = string | { value: string; label?: string; disabled?: boolean };
31
+
32
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'color' | 'onchange'> {
33
+ options: Option[];
34
+ /** The checked option's value. Bindable. */
35
+ value?: string;
36
+ /** The radios' name, for a form. */
37
+ name?: string;
38
+ /** The group's accessible name. */
39
+ label?: string;
40
+ /** After the user picked another option. */
41
+ onChange?: (value: string) => void;
42
+ /** Slide the checked look from one option to the next. */
43
+ animated?: boolean;
44
+ class?: string;
45
+ }
46
+
47
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
48
+ // resolved by name, so an axis a family drops changes nothing down here.
49
+ let {
50
+ options,
51
+ value = $bindable(),
52
+ name = `switch-${Math.random().toString(36).slice(2, 9)}`,
53
+ label,
54
+ onChange,
55
+ animated = true,
56
+ class: klass = '',
57
+ ...axes
58
+ }: Props = $props();
59
+
60
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
61
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
62
+
63
+ let list = $derived(options.map((o) => (typeof o === 'string' ? { value: o, label: o, disabled: false } : { label: o.value, disabled: false, ...o })));
64
+
65
+ let root: HTMLDivElement | undefined = $state();
66
+ let x = $state(0);
67
+ let w = $state(0);
68
+
69
+ /* The indicator follows the checked item: measured after every change of
70
+ value, and again whenever the track resizes (a font, a wrap). */
71
+ $effect(() => {
72
+ if (!animated || !root) return;
73
+ void value;
74
+ const measure = () => {
75
+ const item = root?.querySelector<HTMLElement>(`.${PREFIX}-item:has(:checked)`);
76
+ x = item?.offsetLeft ?? 0;
77
+ w = item?.offsetWidth ?? 0;
78
+ };
79
+ measure();
80
+ const observer = new ResizeObserver(measure);
81
+ observer.observe(root);
82
+ return () => observer.disconnect();
83
+ });
84
+ </script>
85
+
86
+ <div {...applied.rest} bind:this={root} class={classes} role="radiogroup" aria-label={label} style:--switch-x="{x}px" style:--switch-w="{w}px">
87
+ {#if animated}<span class="{PREFIX}-indicator" aria-hidden="true"></span>{/if}
88
+ {#each list as o (o.value)}
89
+ <label class="switch-item">
90
+ <input
91
+ type="radio"
92
+ class="switch-input"
93
+ {name}
94
+ value={o.value}
95
+ checked={value === o.value}
96
+ disabled={o.disabled}
97
+ onchange={() => {
98
+ value = o.value;
99
+ onChange?.(o.value);
100
+ }}
101
+ />
102
+ {o.label}
103
+ </label>
104
+ {/each}
105
+ </div>
@@ -0,0 +1,44 @@
1
+ /** What .switch is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "switch";
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 block: "Fill the width, the items sharing it equally.";
10
+ };
11
+ export declare const PARTS: readonly ["item", "input", "indicator"];
12
+ /**
13
+ * One choice among a few, as a pill track: a radio group in disguise.
14
+ *
15
+ * `options` are strings, or `{ value, label, disabled }`. `value` is
16
+ * bindable — it is the field's own value.
17
+ *
18
+ * An indicator slides from the old choice to the new one (`animated`,
19
+ * off when the visitor asks for reduced motion).
20
+ */
21
+ import type { HTMLAttributes } from 'svelte/elements';
22
+ import { type AxisProps } from '../variants.js';
23
+ type Option = string | {
24
+ value: string;
25
+ label?: string;
26
+ disabled?: boolean;
27
+ };
28
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLDivElement>, 'color' | 'onchange'> {
29
+ options: Option[];
30
+ /** The checked option's value. Bindable. */
31
+ value?: string;
32
+ /** The radios' name, for a form. */
33
+ name?: string;
34
+ /** The group's accessible name. */
35
+ label?: string;
36
+ /** After the user picked another option. */
37
+ onChange?: (value: string) => void;
38
+ /** Slide the checked look from one option to the next. */
39
+ animated?: boolean;
40
+ class?: string;
41
+ }
42
+ declare const Switch: import("svelte").Component<Props, {}, "value">;
43
+ type Switch = ReturnType<typeof Switch>;
44
+ export default Switch;
@@ -0,0 +1,243 @@
1
+ <script module lang="ts">
2
+ /** What .table is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015).
4
+
5
+ The table draws its own head, rows and cells: every class below the
6
+ root that is not a flag is a PART. */
7
+ export const PREFIX = 'table';
8
+
9
+ export const AXES = {
10
+ size: ['xs', 'sm', 'md', 'lg', 'xl']
11
+ } as const;
12
+
13
+ export const FLAGS = {
14
+ zebra: 'Fill every other row.',
15
+ hover: 'Fill the row under the pointer.',
16
+ pinned: 'Keep the head at the top while the rows scroll under it (give the table a `maxHeight`).',
17
+ loading: 'Fade the rows while new ones are on their way.'
18
+ } as const;
19
+ export const PARTS = [
20
+ 'scroll',
21
+ 'head',
22
+ 'row',
23
+ 'row-selected',
24
+ 'row-dirty',
25
+ 'cell',
26
+ 'sort',
27
+ 'sticky',
28
+ 'sticky-last',
29
+ 'empty'
30
+ ] as const;
31
+
32
+ import type { Snippet } from 'svelte';
33
+ import type { EditableKind } from './Editable.svelte';
34
+
35
+ /** One column of a table. */
36
+ export interface TableColumn<Row = any> {
37
+ /** What identifies the column — and, by default, the field of a row it shows. */
38
+ key: string;
39
+ /** The header, as text or as a snippet. */
40
+ label?: string | Snippet;
41
+ /** The column's width, any CSS length (`20ch`, `12em`). A minimum: text wraps inside it. */
42
+ width?: string;
43
+ /** Pin the column to the start edge while the others scroll. */
44
+ sticky?: boolean;
45
+ /** Leave the column out. */
46
+ hidden?: boolean;
47
+ /** Make the header a button that asks for a sort on this column. */
48
+ sortable?: boolean;
49
+ /** How the cell's text lines up. */
50
+ align?: 'start' | 'center' | 'end';
51
+ /** The cell's text when there is no `cell` snippet. Defaults to `row[key]`. */
52
+ value?: (row: Row) => unknown;
53
+ /**
54
+ * Make the cell editable in place with the editor for this shape of value
55
+ * (an `Editable`). The edit is handed to `onEdit`. Ignored when a `cell`
56
+ * snippet draws the cell — put an `Editable` in it yourself.
57
+ */
58
+ edit?: EditableKind;
59
+ }
60
+
61
+ export type TableSortDirection = 'asc' | 'desc';
62
+ export interface TableSort {
63
+ key: string;
64
+ direction: TableSortDirection;
65
+ }
66
+ </script>
67
+
68
+ <script lang="ts" generics="Row">
69
+ /**
70
+ * A real `<table>`. Columns are described once — width, sticky, sortable,
71
+ * hidden — and each cell is the column's field, or whatever the `cell`
72
+ * snippet draws. The rows are plain data: where they come from is not the
73
+ * table's business.
74
+ *
75
+ * Selection, sorting and loading are states every table has, so they are
76
+ * here as plain props — `selected`, `sort` / `onSort`, `loading` — and the
77
+ * table only draws them.
78
+ */
79
+ import type { HTMLTableAttributes } from 'svelte/elements';
80
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
81
+ import Editable from './Editable.svelte';
82
+
83
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLTableAttributes {
84
+ /** The columns, in order. */
85
+ columns: TableColumn<Row>[];
86
+ /** The rows: your data, as is. */
87
+ rows?: Row[];
88
+ /** A row's identity, for `selected`, `dirty` and keyed rendering. Defaults to its index. */
89
+ key?: (row: Row, index: number) => string;
90
+ /** How one cell is drawn. Without it, the cell is the column's value as text. */
91
+ cell?: Snippet<[Row, TableColumn<Row>, number]>;
92
+ /**
93
+ * How a whole row is drawn, `<td>`s included — the escape hatch for a
94
+ * row `cell` cannot express. The table still writes the `<tr>`.
95
+ */
96
+ row?: Snippet<[Row, number]>;
97
+ /** Drawn in place of the rows when there are none. */
98
+ empty?: Snippet;
99
+ /** Keys of the rows to draw as selected. */
100
+ selected?: Iterable<string>;
101
+ /** Keys of the rows to draw as changed and not yet saved. */
102
+ dirty?: Iterable<string>;
103
+ /** The current sort, drawn on its header. */
104
+ sort?: TableSort;
105
+ /** A sortable header was clicked: ascending, then descending, then none. */
106
+ onSort?: (sort: TableSort | undefined) => void;
107
+ /** An editable cell (a column with `edit`) was changed: the row, the column's key, the new value. */
108
+ onEdit?: (row: Row, key: string, value: unknown) => void;
109
+ /** The scroll box's maximum height, for a long table with a pinned head. */
110
+ maxHeight?: string;
111
+ class?: string;
112
+ }
113
+
114
+ let {
115
+ columns,
116
+ rows = [],
117
+ key = (_row: Row, i: number) => String(i),
118
+ cell,
119
+ row,
120
+ empty,
121
+ selected,
122
+ dirty,
123
+ sort,
124
+ onSort,
125
+ onEdit,
126
+ maxHeight,
127
+ class: klass = '',
128
+ ...axes
129
+ }: Props = $props();
130
+
131
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
132
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
133
+
134
+ let visible = $derived(columns.filter((c) => !c.hidden));
135
+ let selectedSet = $derived(new Set(selected ?? []));
136
+ let dirtySet = $derived(new Set(dirty ?? []));
137
+
138
+ /* Sticky columns are offset by the width of the sticky columns before
139
+ them, measured on their header cells. */
140
+ let widths: number[] = $state([]);
141
+ let offsets = $derived.by(() => {
142
+ const out: (number | undefined)[] = [];
143
+ let sum = 0;
144
+ visible.forEach((c, i) => {
145
+ if (!c.sticky) return;
146
+ out[i] = sum;
147
+ sum += widths[i] ?? 0;
148
+ });
149
+ return out;
150
+ });
151
+ let lastSticky = $derived(visible.findLastIndex((c) => c.sticky));
152
+
153
+ function cellClass(c: TableColumn<Row>, i: number) {
154
+ return cx(
155
+ `${PREFIX}-cell`,
156
+ c.sticky && `${PREFIX}-sticky`,
157
+ i === lastSticky && `${PREFIX}-sticky-last`
158
+ );
159
+ }
160
+ function cellStyle(c: TableColumn<Row>, i: number, head: boolean) {
161
+ const parts: string[] = [];
162
+ if (c.sticky) parts.push(`inset-inline-start:${offsets[i] ?? 0}px`);
163
+ if (c.align) parts.push(`text-align:${c.align}`);
164
+ if (head && c.width) parts.push(`width:${c.width}`, `min-width:${c.width}`);
165
+ return parts.join(';') || undefined;
166
+ }
167
+
168
+ const ariaSort = (c: TableColumn<Row>) =>
169
+ sort?.key === c.key ? (sort.direction === 'asc' ? 'ascending' : 'descending') : undefined;
170
+
171
+ function next(c: TableColumn<Row>) {
172
+ if (sort?.key !== c.key) return onSort?.({ key: c.key, direction: 'asc' });
173
+ if (sort.direction === 'asc') return onSort?.({ key: c.key, direction: 'desc' });
174
+ onSort?.(undefined);
175
+ }
176
+
177
+ const raw = (r: Row, c: TableColumn<Row>) =>
178
+ c.value ? c.value(r) : (r as Record<string, unknown>)?.[c.key];
179
+ const text = (r: Row, c: TableColumn<Row>) => {
180
+ const v = raw(r, c);
181
+ return v == null ? '' : String(v);
182
+ };
183
+ </script>
184
+
185
+ <div class={`${PREFIX}-scroll`} style={maxHeight ? `max-height:${maxHeight}` : undefined}>
186
+ <table {...applied.rest} class={classes}>
187
+ <thead>
188
+ <tr class={`${PREFIX}-head`}>
189
+ {#each visible as c, i (c.key)}
190
+ <th
191
+ class={cellClass(c, i)}
192
+ style={cellStyle(c, i, true)}
193
+ scope="col"
194
+ aria-sort={c.sortable ? (ariaSort(c) ?? 'none') : undefined}
195
+ bind:offsetWidth={widths[i]}
196
+ >
197
+ {#if c.sortable}
198
+ <button type="button" class={`${PREFIX}-sort`} onclick={() => next(c)}>
199
+ {#if typeof c.label === 'function'}{@render c.label()}{:else}{c.label ?? c.key}{/if}
200
+ </button>
201
+ {:else if typeof c.label === 'function'}
202
+ {@render c.label()}
203
+ {:else}
204
+ {c.label ?? c.key}
205
+ {/if}
206
+ </th>
207
+ {/each}
208
+ </tr>
209
+ </thead>
210
+ <tbody>
211
+ {#each rows as r, ri (key(r, ri))}
212
+ {@const k = key(r, ri)}
213
+ <tr
214
+ class={cx(
215
+ `${PREFIX}-row`,
216
+ selectedSet.has(k) && `${PREFIX}-row-selected`,
217
+ dirtySet.has(k) && `${PREFIX}-row-dirty`
218
+ )}
219
+ >
220
+ {#if row}
221
+ {@render row(r, ri)}
222
+ {:else}
223
+ {#each visible as c, i (c.key)}
224
+ <td class={cellClass(c, i)} style={cellStyle(c, i, false)}>
225
+ {#if cell}{@render cell(r, c, ri)}{:else if c.edit}<Editable
226
+ value={raw(r, c)}
227
+ kind={c.edit}
228
+ onChange={(v) => onEdit?.(r, c.key, v)}
229
+ />{:else}{text(r, c)}{/if}
230
+ </td>
231
+ {/each}
232
+ {/if}
233
+ </tr>
234
+ {:else}
235
+ {#if empty}
236
+ <tr>
237
+ <td class={`${PREFIX}-empty`} colspan={visible.length}>{@render empty()}</td>
238
+ </tr>
239
+ {/if}
240
+ {/each}
241
+ </tbody>
242
+ </table>
243
+ </div>
@@ -0,0 +1,121 @@
1
+ /** What .table is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015).
3
+
4
+ The table draws its own head, rows and cells: every class below the
5
+ root that is not a flag is a PART. */
6
+ export declare const PREFIX = "table";
7
+ export declare const AXES: {
8
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
9
+ };
10
+ export declare const FLAGS: {
11
+ readonly zebra: "Fill every other row.";
12
+ readonly hover: "Fill the row under the pointer.";
13
+ readonly pinned: "Keep the head at the top while the rows scroll under it (give the table a `maxHeight`).";
14
+ readonly loading: "Fade the rows while new ones are on their way.";
15
+ };
16
+ export declare const PARTS: readonly ["scroll", "head", "row", "row-selected", "row-dirty", "cell", "sort", "sticky", "sticky-last", "empty"];
17
+ import type { Snippet } from 'svelte';
18
+ import type { EditableKind } from './Editable.svelte';
19
+ /** One column of a table. */
20
+ export interface TableColumn<Row = any> {
21
+ /** What identifies the column — and, by default, the field of a row it shows. */
22
+ key: string;
23
+ /** The header, as text or as a snippet. */
24
+ label?: string | Snippet;
25
+ /** The column's width, any CSS length (`20ch`, `12em`). A minimum: text wraps inside it. */
26
+ width?: string;
27
+ /** Pin the column to the start edge while the others scroll. */
28
+ sticky?: boolean;
29
+ /** Leave the column out. */
30
+ hidden?: boolean;
31
+ /** Make the header a button that asks for a sort on this column. */
32
+ sortable?: boolean;
33
+ /** How the cell's text lines up. */
34
+ align?: 'start' | 'center' | 'end';
35
+ /** The cell's text when there is no `cell` snippet. Defaults to `row[key]`. */
36
+ value?: (row: Row) => unknown;
37
+ /**
38
+ * Make the cell editable in place with the editor for this shape of value
39
+ * (an `Editable`). The edit is handed to `onEdit`. Ignored when a `cell`
40
+ * snippet draws the cell — put an `Editable` in it yourself.
41
+ */
42
+ edit?: EditableKind;
43
+ }
44
+ export type TableSortDirection = 'asc' | 'desc';
45
+ export interface TableSort {
46
+ key: string;
47
+ direction: TableSortDirection;
48
+ }
49
+ /**
50
+ * A real `<table>`. Columns are described once — width, sticky, sortable,
51
+ * hidden — and each cell is the column's field, or whatever the `cell`
52
+ * snippet draws. The rows are plain data: where they come from is not the
53
+ * table's business.
54
+ *
55
+ * Selection, sorting and loading are states every table has, so they are
56
+ * here as plain props — `selected`, `sort` / `onSort`, `loading` — and the
57
+ * table only draws them.
58
+ */
59
+ import type { HTMLTableAttributes } from 'svelte/elements';
60
+ declare function $$render<Row>(): {
61
+ props: {
62
+ readonly size?: "xs" | "sm" | "md" | "lg" | "xl" | undefined;
63
+ } & {
64
+ loading?: boolean | undefined;
65
+ zebra?: boolean | undefined;
66
+ hover?: boolean | undefined;
67
+ pinned?: boolean | undefined;
68
+ } & {
69
+ background?: string;
70
+ } & HTMLTableAttributes & {
71
+ /** The columns, in order. */
72
+ columns: TableColumn<Row>[];
73
+ /** The rows: your data, as is. */
74
+ rows?: Row[];
75
+ /** A row's identity, for `selected`, `dirty` and keyed rendering. Defaults to its index. */
76
+ key?: (row: Row, index: number) => string;
77
+ /** How one cell is drawn. Without it, the cell is the column's value as text. */
78
+ cell?: Snippet<[Row, TableColumn<Row>, number]>;
79
+ /**
80
+ * How a whole row is drawn, `<td>`s included — the escape hatch for a
81
+ * row `cell` cannot express. The table still writes the `<tr>`.
82
+ */
83
+ row?: Snippet<[Row, number]>;
84
+ /** Drawn in place of the rows when there are none. */
85
+ empty?: Snippet;
86
+ /** Keys of the rows to draw as selected. */
87
+ selected?: Iterable<string>;
88
+ /** Keys of the rows to draw as changed and not yet saved. */
89
+ dirty?: Iterable<string>;
90
+ /** The current sort, drawn on its header. */
91
+ sort?: TableSort;
92
+ /** A sortable header was clicked: ascending, then descending, then none. */
93
+ onSort?: (sort: TableSort | undefined) => void;
94
+ /** An editable cell (a column with `edit`) was changed: the row, the column's key, the new value. */
95
+ onEdit?: (row: Row, key: string, value: unknown) => void;
96
+ /** The scroll box's maximum height, for a long table with a pinned head. */
97
+ maxHeight?: string;
98
+ class?: string;
99
+ };
100
+ exports: {};
101
+ bindings: "";
102
+ slots: {};
103
+ events: {};
104
+ };
105
+ declare class __sveltets_Render<Row> {
106
+ props(): ReturnType<typeof $$render<Row>>['props'];
107
+ events(): ReturnType<typeof $$render<Row>>['events'];
108
+ slots(): ReturnType<typeof $$render<Row>>['slots'];
109
+ bindings(): "";
110
+ exports(): {};
111
+ }
112
+ interface $$IsomorphicComponent {
113
+ new <Row>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Row>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Row>['props']>, ReturnType<__sveltets_Render<Row>['events']>, ReturnType<__sveltets_Render<Row>['slots']>> & {
114
+ $$bindings?: ReturnType<__sveltets_Render<Row>['bindings']>;
115
+ } & ReturnType<__sveltets_Render<Row>['exports']>;
116
+ <Row>(internal: unknown, props: ReturnType<__sveltets_Render<Row>['props']> & {}): ReturnType<__sveltets_Render<Row>['exports']>;
117
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
118
+ }
119
+ declare const Table: $$IsomorphicComponent;
120
+ type Table<Row> = InstanceType<typeof Table<Row>>;
121
+ export default Table;
@@ -0,0 +1,142 @@
1
+ <script module lang="ts">
2
+ /** What .tabs is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015).
4
+
5
+ One namespace, `tabs`: the strip AND the tabs in it (ADR 0009). The
6
+ component draws every tab, so `.tabs-item` and its states are PARTS.
7
+ A panel is rendered right after its tab because the CSS reaches it with
8
+ `+` — moving it elsewhere would break the selector. */
9
+ export const PREFIX = 'tabs';
10
+
11
+ export const AXES = {
12
+ size: ['xs', 'sm', 'md', 'lg', 'xl'],
13
+ /* How the strip is drawn. */
14
+ variant: ['border', 'box', 'lift'],
15
+ /* Which edge the strip sits on, which decides where the seam goes. */
16
+ placement: ['top', 'bottom']
17
+ } as const;
18
+
19
+ export const FLAGS = {} as const;
20
+ export const PARTS = ['item', 'item-active', 'item-disabled', 'item-content', 'indicator'] as const;
21
+
22
+ import type { Snippet } from 'svelte';
23
+
24
+ /** One tab, and optionally the panel it reveals. */
25
+ export interface TabItem<V = string | number> {
26
+ /** The tab's text, or a snippet drawing whatever belongs in its place. */
27
+ label: string | Snippet;
28
+ /** What identifies the tab when the strip is driven by `value` rather than by index. */
29
+ value?: V;
30
+ /** Unreachable, and drawn as such. */
31
+ disabled?: boolean;
32
+ /** What the tab shows when selected. Omit to place panels yourself. */
33
+ content?: Snippet;
34
+ }
35
+ </script>
36
+
37
+ <script lang="ts" generics="V extends string | number">
38
+ import type { HTMLAttributes } from 'svelte/elements';
39
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
40
+
41
+ interface Props
42
+ extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>,
43
+ HTMLAttributes<HTMLDivElement> {
44
+ /**
45
+ * The tabs, in order. A tab's `label` takes a snippet as readily as a
46
+ * string, for the one tab that carries an icon or a count.
47
+ */
48
+ tabs?: TabItem<V>[];
49
+ /**
50
+ * How to draw the inside of every tab. Written once for the whole strip;
51
+ * a tab whose own `label` is a snippet skips it.
52
+ */
53
+ tab?: Snippet<[TabItem<V>]>;
54
+ /** Index of the selected tab. Bindable, so a parent can drive it. */
55
+ active?: number;
56
+ /** `value` of the selected tab, for tabs keyed by their own id rather than by
57
+ position. Bindable. When set, it decides the selection over `active`. */
58
+ value?: V;
59
+ /** Called with the new index, and the tab's `value`, when a tab is chosen. */
60
+ onSelect?: (index: number, value: V | undefined) => void;
61
+ /** Slide the selection from one tab to the next. Not with `lift`, whose
62
+ tab opens into its panel. */
63
+ animated?: boolean;
64
+ class?: string;
65
+ }
66
+
67
+ let {
68
+ tabs = [],
69
+ tab,
70
+ active = $bindable(0),
71
+ value = $bindable(),
72
+ onSelect,
73
+ animated = true,
74
+ class: klass = '',
75
+ ...axes
76
+ }: Props = $props();
77
+
78
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
79
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
80
+
81
+ /* The selected index: from `value` when the strip is keyed by value, else `active`. */
82
+ let current = $derived(value !== undefined ? tabs.findIndex((t) => t.value === value) : active);
83
+
84
+ let root: HTMLDivElement | undefined = $state();
85
+ let box = $state({ x: 0, y: 0, w: 0, h: 0 });
86
+
87
+ /* The indicator follows the selected tab: measured after every change,
88
+ and again whenever the strip resizes (a font, a wrap, a panel). */
89
+ $effect(() => {
90
+ if (!animated || !root) return;
91
+ void current;
92
+ void tabs.length;
93
+ const measure = () => {
94
+ const item = root?.querySelectorAll<HTMLElement>(`:scope > [role="tab"]`)[current];
95
+ box = item
96
+ ? { x: item.offsetLeft, y: item.offsetTop, w: item.offsetWidth, h: item.offsetHeight }
97
+ : { x: 0, y: 0, w: 0, h: 0 };
98
+ };
99
+ measure();
100
+ const observer = new ResizeObserver(measure);
101
+ observer.observe(root);
102
+ return () => observer.disconnect();
103
+ });
104
+
105
+ function select(i: number) {
106
+ active = i;
107
+ if (tabs[i]?.value !== undefined) value = tabs[i].value;
108
+ onSelect?.(i, tabs[i]?.value);
109
+ }
110
+ </script>
111
+
112
+ {#snippet inside(item: TabItem<V>)}
113
+ {#if typeof item.label === 'function'}{@render item.label()}
114
+ {:else if tab}{@render tab(item)}
115
+ {:else}{item.label}{/if}
116
+ {/snippet}
117
+
118
+ <div {...applied.rest} bind:this={root} role="tablist" class={classes}
119
+ style:--tabs-x="{box.x}px"
120
+ style:--tabs-y="{box.y}px"
121
+ style:--tabs-w="{box.w}px"
122
+ style:--tabs-h="{box.h}px"
123
+ >
124
+ {#if animated}<span class="{PREFIX}-item {PREFIX}-item-active {PREFIX}-indicator" aria-hidden="true"></span>{/if}
125
+ {#each tabs as item, i (i)}
126
+ <button
127
+ type="button"
128
+ role="tab"
129
+ class={cx(
130
+ `${PREFIX}-item`,
131
+ i === current && `${PREFIX}-item-active`,
132
+ item.disabled && `${PREFIX}-item-disabled`
133
+ )}
134
+ aria-selected={i === current}
135
+ disabled={item.disabled}
136
+ onclick={() => select(i)}
137
+ >{@render inside(item)}</button>
138
+ {#if item.content}
139
+ <div class={`${PREFIX}-item-content`}>{@render item.content()}</div>
140
+ {/if}
141
+ {/each}
142
+ </div>