@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,338 @@
1
+ <script module lang="ts">
2
+ /** What .select is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'select';
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 = {} as const;
13
+ export const PARTS = [] as const;
14
+ </script>
15
+
16
+ <script lang="ts">
17
+ /**
18
+ * A choice among options. The options are written as children, or given as
19
+ * data (`options`) — objects read through `optionvalue` and `optionname`.
20
+ *
21
+ * One choice is a native `<select>`. Several (`multiple`) is a .select frame
22
+ * that opens a list of checkable rows, because a native multi-select is a
23
+ * tall box nobody knows how to use; a hidden native `<select multiple>`
24
+ * still carries the value into the form and its validation.
25
+ */
26
+ import type { Snippet } from 'svelte';
27
+ import type { HTMLSelectAttributes } from 'svelte/elements';
28
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
29
+
30
+ // `size` is ours — the HTML attribute of the same name is a character count
31
+ // our CSS overrides anyway.
32
+ interface Props
33
+ extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>,
34
+ Omit<HTMLSelectAttributes, 'size' | 'color' | 'value' | 'multiple'> {
35
+ /**
36
+ * Colour the field by its own validity and reveal `hint` once the
37
+ * browser marks it invalid. The CSS drives this off `:user-invalid`,
38
+ * so it only fires after the visitor has actually had a go.
39
+ */
40
+ validator?: boolean;
41
+ /**
42
+ * Shown under the field when it is invalid. It has to be the field's
43
+ * next sibling for the CSS to reach it, which is the whole reason this
44
+ * is a prop and not something you write yourself. Without it, the
45
+ * message is the browser's own.
46
+ */
47
+ hint?: Snippet;
48
+ /**
49
+ * An error from outside (the server said no): marks the field invalid and
50
+ * shows this, for as long as it is given. The parent owns it — it knows
51
+ * the value and is the one able to test it — so the field never clears it.
52
+ */
53
+ error?: string | null;
54
+ /** The message of the last check, `false` when the field passed. Bindable. */
55
+ invalid?: string | false;
56
+ /** The options, as data. Written after any `children`. */
57
+ options?: any[];
58
+ /** The key of an option object holding its value. Default `value`. */
59
+ optionvalue?: string;
60
+ /** The key of an option object holding its text. Default `name`. */
61
+ optionname?: string;
62
+ /** How one option is drawn. Defaults to its `optionname` text. */
63
+ option?: Snippet<[any]>;
64
+ /** Shown while nothing is chosen, which is shown empty without it. */
65
+ placeholder?: string;
66
+ /** Nothing can be chosen back, to clear the field. */
67
+ nullable?: boolean;
68
+ /** Several choices: `value` is then an array. */
69
+ multiple?: boolean;
70
+ /** The text of a multiple field with several choices. Default `N selected`. */
71
+ summary?: (chosen: any[]) => string;
72
+ /** Called with the new value after every change. */
73
+ onValue?: (value: any) => void;
74
+ /** The native `<select>`. Bindable. */
75
+ element?: HTMLSelectElement;
76
+ class?: string;
77
+ value?: any;
78
+ children?: Snippet;
79
+ }
80
+
81
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
82
+ // resolved by name, so an axis a family drops changes nothing down here.
83
+ let {
84
+ validator = false,
85
+ hint,
86
+ error = null,
87
+ invalid = $bindable(false),
88
+ options = [],
89
+ optionvalue = 'value',
90
+ optionname = 'name',
91
+ option,
92
+ placeholder,
93
+ nullable = false,
94
+ multiple = false,
95
+ summary = (chosen: any[]) => `${chosen.length} selected`,
96
+ onValue,
97
+ element = $bindable(),
98
+ value = $bindable(),
99
+ class: klass = '',
100
+ disabled = false,
101
+ children,
102
+ onchange,
103
+ onblur,
104
+ ...axes
105
+ }: Props = $props();
106
+
107
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
108
+
109
+ const uid = $props.id();
110
+
111
+ /** What is shown under the field. A disabled field shows nothing. */
112
+ let message = $derived(disabled ? '' : error || (typeof invalid === 'string' ? invalid : ''));
113
+
114
+ /* An error from outside is the field's validity too: a form holding it
115
+ does not submit. */
116
+ $effect(() => {
117
+ element?.setCustomValidity(disabled || !error ? '' : error);
118
+ });
119
+
120
+ let classes = $derived(cx(PREFIX, applied.classes, (validator || !!message) && 'validator', klass));
121
+
122
+ const keyOf = (o: any) => (o !== null && typeof o === 'object' ? o[optionvalue] : o);
123
+ const nameOf = (o: any) => (o !== null && typeof o === 'object' ? o[optionname] : `${o}`);
124
+
125
+ /** Check the field now and show the browser's message if it fails. */
126
+ export function isValid(): boolean {
127
+ if (!element || disabled) return !error;
128
+ invalid = element.validity.valid ? false : element.validationMessage;
129
+ return !error && !invalid;
130
+ }
131
+
132
+ /** Open the list of a multiple field. */
133
+ export function show() {
134
+ if (multiple && !disabled) open = true;
135
+ }
136
+
137
+ function changed() {
138
+ if (invalid) isValid();
139
+ onValue?.(value);
140
+ }
141
+
142
+ function handleChange(e: Event & { currentTarget: EventTarget & HTMLSelectElement }) {
143
+ changed();
144
+ onchange?.(e);
145
+ }
146
+
147
+ function handleBlur(e: FocusEvent & { currentTarget: EventTarget & HTMLSelectElement }) {
148
+ if (validator) isValid();
149
+ onblur?.(e);
150
+ }
151
+
152
+ /* ── Multiple ─────────────────────────────────────────────────────── */
153
+
154
+ let open = $state(false);
155
+ /** Keyboard position in the list; -1 is the frame itself. */
156
+ let active = $state(-1);
157
+ let frame: HTMLDivElement | undefined = $state();
158
+
159
+ let chosen = $derived(Array.isArray(value) ? value : []);
160
+ let chosenOptions = $derived(options.filter((o) => chosen.includes(keyOf(o))));
161
+
162
+ let text = $derived(
163
+ chosenOptions.length === 0
164
+ ? (placeholder ?? '')
165
+ : chosenOptions.length === 1
166
+ ? nameOf(chosenOptions[0])
167
+ : summary(chosenOptions)
168
+ );
169
+
170
+ /** Rows the keyboard walks through: the clearing row, then the options. */
171
+ let rows = $derived(options.length + (nullable ? 1 : 0));
172
+
173
+ function toggle(o: any) {
174
+ const k = keyOf(o);
175
+ value = chosen.includes(k) ? chosen.filter((v: any) => v !== k) : [...chosen, k];
176
+ changed();
177
+ }
178
+
179
+ function reset() {
180
+ value = [];
181
+ changed();
182
+ }
183
+
184
+ function activate(i: number) {
185
+ if (nullable && i === 0) reset();
186
+ else toggle(options[i - (nullable ? 1 : 0)]);
187
+ }
188
+
189
+ function close() {
190
+ open = false;
191
+ active = -1;
192
+ }
193
+
194
+ function frameKeydown(e: KeyboardEvent) {
195
+ if (disabled) return;
196
+ if (e.key === 'ArrowDown') {
197
+ e.preventDefault();
198
+ if (!open) open = true;
199
+ else if (rows) active = (active + 1) % rows;
200
+ } else if (e.key === 'ArrowUp' && open && rows) {
201
+ e.preventDefault();
202
+ active = active <= 0 ? rows - 1 : active - 1;
203
+ } else if (e.key === 'Enter' || e.key === ' ') {
204
+ e.preventDefault();
205
+ if (!open) open = true;
206
+ else if (active >= 0) activate(active);
207
+ } else if (e.key === 'Escape' && open) {
208
+ e.preventDefault();
209
+ close();
210
+ }
211
+ }
212
+
213
+ function frameClick(e: MouseEvent) {
214
+ // A row already acted on mousedown; its click must not close the list.
215
+ if ((e.target as Element).closest('.list-suggestions') || disabled) return;
216
+ if (open) close();
217
+ else open = true;
218
+ }
219
+
220
+ function frameFocusout(e: FocusEvent) {
221
+ // Focus moving inside the frame (the hidden select) is not leaving it.
222
+ if (frame?.contains(e.relatedTarget as Node | null)) return;
223
+ close();
224
+ if (validator) isValid();
225
+ }
226
+ </script>
227
+
228
+ {#if multiple}
229
+ <!-- The frame is the control: it takes the focus and the keys. A row
230
+ acts on mousedown and keeps the focus on the frame, so picking never
231
+ closes the list. -->
232
+ <div
233
+ bind:this={frame}
234
+ class={classes}
235
+ role="combobox"
236
+ tabindex={disabled ? -1 : 0}
237
+ aria-expanded={open}
238
+ aria-haspopup="listbox"
239
+ aria-controls="{uid}-list"
240
+ aria-disabled={disabled || undefined}
241
+ aria-invalid={message ? 'true' : undefined}
242
+ aria-activedescendant={open && active >= 0 ? `${uid}-${active}` : undefined}
243
+ onclick={frameClick}
244
+ onkeydown={frameKeydown}
245
+ onfocusout={frameFocusout}
246
+ >
247
+ <span style="overflow: hidden; text-overflow: ellipsis; flex: 1; opacity: {chosenOptions.length ? 1 : 0.6};"
248
+ >{text}</span
249
+ >
250
+
251
+ <!-- Carries the value into the form, and `required` into validation. -->
252
+ <select
253
+ {...applied.rest}
254
+ bind:this={element}
255
+ bind:value
256
+ {disabled}
257
+ multiple
258
+ tabindex="-1"
259
+ aria-hidden="true"
260
+ style="position: absolute; inset: 0; opacity: 0; pointer-events: none;"
261
+ >
262
+ {#each options as o (keyOf(o))}
263
+ <option value={keyOf(o)}>{nameOf(o)}</option>
264
+ {/each}
265
+ </select>
266
+
267
+ {#if open}
268
+ <ul id="{uid}-list" class="list list-suggestions" role="listbox" aria-multiselectable="true">
269
+ {#if nullable}
270
+ <li
271
+ id="{uid}-0"
272
+ class="list-row"
273
+ role="option"
274
+ aria-selected={active === 0}
275
+ onmousedown={(e) => {
276
+ e.preventDefault();
277
+ reset();
278
+ }}
279
+ >
280
+ {placeholder ?? '—'}
281
+ </li>
282
+ {/if}
283
+ {#each options as o, i (keyOf(o))}
284
+ {@const row = i + (nullable ? 1 : 0)}
285
+ <li
286
+ id="{uid}-{row}"
287
+ class="list-row"
288
+ role="option"
289
+ aria-selected={active === row}
290
+ aria-checked={chosen.includes(keyOf(o))}
291
+ onmousedown={(e) => {
292
+ e.preventDefault();
293
+ toggle(o);
294
+ }}
295
+ >
296
+ <input
297
+ type="checkbox"
298
+ class="checkbox checkbox-sm"
299
+ checked={chosen.includes(keyOf(o))}
300
+ tabindex="-1"
301
+ aria-hidden="true"
302
+ style="pointer-events: none;"
303
+ />
304
+ {#if option}{@render option(o)}{:else}{nameOf(o)}{/if}
305
+ </li>
306
+ {/each}
307
+ </ul>
308
+ {/if}
309
+ </div>
310
+ {:else}
311
+ <!-- Ours first: what the caller writes in the spread still wins. -->
312
+ <select
313
+ aria-invalid={message ? 'true' : undefined}
314
+ {...applied.rest}
315
+ bind:this={element}
316
+ class={classes}
317
+ bind:value
318
+ {disabled}
319
+ onchange={handleChange}
320
+ onblur={handleBlur}
321
+ >
322
+ <!-- Always there : without it, the browser shows the first option as chosen while the value stays undefined -->
323
+ <option value={undefined} disabled={!nullable} hidden={!nullable}>{placeholder ?? ''}</option>
324
+ {@render children?.()}
325
+ {#each options as o (keyOf(o))}
326
+ <option value={keyOf(o)}>{#if option}{@render option(o)}{:else}{nameOf(o)}{/if}</option>
327
+ {/each}
328
+ </select>
329
+ {/if}
330
+ <!-- An error from outside first, then the caller's hint, then the browser's
331
+ own message. -->
332
+ {#if error && !disabled}
333
+ <p class="validator-hint">{error}</p>
334
+ {:else if hint}
335
+ <p class="validator-hint">{@render hint()}</p>
336
+ {:else if message}
337
+ <p class="validator-hint">{message}</p>
338
+ {/if}
@@ -0,0 +1,74 @@
1
+ /** What .select is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "select";
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
+ export declare const PARTS: readonly [];
11
+ /**
12
+ * A choice among options. The options are written as children, or given as
13
+ * data (`options`) — objects read through `optionvalue` and `optionname`.
14
+ *
15
+ * One choice is a native `<select>`. Several (`multiple`) is a .select frame
16
+ * that opens a list of checkable rows, because a native multi-select is a
17
+ * tall box nobody knows how to use; a hidden native `<select multiple>`
18
+ * still carries the value into the form and its validation.
19
+ */
20
+ import type { Snippet } from 'svelte';
21
+ import type { HTMLSelectAttributes } from 'svelte/elements';
22
+ import { type AxisProps } from '../variants.js';
23
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLSelectAttributes, 'size' | 'color' | 'value' | 'multiple'> {
24
+ /**
25
+ * Colour the field by its own validity and reveal `hint` once the
26
+ * browser marks it invalid. The CSS drives this off `:user-invalid`,
27
+ * so it only fires after the visitor has actually had a go.
28
+ */
29
+ validator?: boolean;
30
+ /**
31
+ * Shown under the field when it is invalid. It has to be the field's
32
+ * next sibling for the CSS to reach it, which is the whole reason this
33
+ * is a prop and not something you write yourself. Without it, the
34
+ * message is the browser's own.
35
+ */
36
+ hint?: Snippet;
37
+ /**
38
+ * An error from outside (the server said no): marks the field invalid and
39
+ * shows this, for as long as it is given. The parent owns it — it knows
40
+ * the value and is the one able to test it — so the field never clears it.
41
+ */
42
+ error?: string | null;
43
+ /** The message of the last check, `false` when the field passed. Bindable. */
44
+ invalid?: string | false;
45
+ /** The options, as data. Written after any `children`. */
46
+ options?: any[];
47
+ /** The key of an option object holding its value. Default `value`. */
48
+ optionvalue?: string;
49
+ /** The key of an option object holding its text. Default `name`. */
50
+ optionname?: string;
51
+ /** How one option is drawn. Defaults to its `optionname` text. */
52
+ option?: Snippet<[any]>;
53
+ /** Shown while nothing is chosen, which is shown empty without it. */
54
+ placeholder?: string;
55
+ /** Nothing can be chosen back, to clear the field. */
56
+ nullable?: boolean;
57
+ /** Several choices: `value` is then an array. */
58
+ multiple?: boolean;
59
+ /** The text of a multiple field with several choices. Default `N selected`. */
60
+ summary?: (chosen: any[]) => string;
61
+ /** Called with the new value after every change. */
62
+ onValue?: (value: any) => void;
63
+ /** The native `<select>`. Bindable. */
64
+ element?: HTMLSelectElement;
65
+ class?: string;
66
+ value?: any;
67
+ children?: Snippet;
68
+ }
69
+ declare const Select: import("svelte").Component<Props, {
70
+ isValid: () => boolean;
71
+ show: () => void;
72
+ }, "value" | "invalid" | "element">;
73
+ type Select = ReturnType<typeof Select>;
74
+ export default Select;
@@ -0,0 +1,100 @@
1
+ <script module lang="ts">
2
+ /** What .sidebar is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015).
4
+
5
+ `.sidebar-layout` is the row it may sit in: written by hand around the
6
+ page, never by the component, so it is a PART too. */
7
+ export const PREFIX = 'sidebar';
8
+
9
+ export const AXES = {
10
+ size: ['xs', 'sm', 'md', 'lg', 'xl'],
11
+ /* The page's side it stands on. The start by default. */
12
+ side: ['end']
13
+ } as const;
14
+
15
+ export const FLAGS = {} as const;
16
+ export const PARTS = ['head', 'body', 'foot', 'toggle', 'collapsed', 'keep', 'layout'] as const;
17
+ </script>
18
+
19
+ <script lang="ts">
20
+ /**
21
+ * An app's side column — a brand, a menu, an account — that folds to a
22
+ * rail. In the flow, unlike a Drawer: the page beside it narrows or
23
+ * widens, it is never covered.
24
+ *
25
+ * `collapsed` is bindable. The toggle sits in the head, after `head`;
26
+ * folded, only the toggle stays (and anything marked `.sidebar-keep`).
27
+ */
28
+ import type { Snippet } from 'svelte';
29
+ import type { HTMLAttributes } from 'svelte/elements';
30
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
31
+
32
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, Omit<HTMLAttributes<HTMLElement>, 'children'> {
33
+ /** Folded to a rail. Bindable. */
34
+ collapsed?: boolean;
35
+ /** The top of the column: a brand, a picker. The toggle follows it. */
36
+ head?: Snippet<[{ collapsed: boolean }]>;
37
+ /** The bottom of the column: an account, a setting. */
38
+ foot?: Snippet<[{ collapsed: boolean }]>;
39
+ /** Hide the toggle: the app folds it some other way. */
40
+ noToggle?: boolean;
41
+ /** The toggle's accessible name, open and folded. */
42
+ toggleLabel?: { open: string; collapsed: string };
43
+ /** After every fold or unfold. */
44
+ onToggle?: (collapsed: boolean) => void;
45
+ class?: string;
46
+ /** The body: usually a Menu. */
47
+ children?: Snippet<[{ collapsed: boolean }]>;
48
+ }
49
+
50
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
51
+ // resolved by name, so an axis a family drops changes nothing down here.
52
+ let {
53
+ collapsed = $bindable(false),
54
+ head,
55
+ foot,
56
+ noToggle = false,
57
+ toggleLabel = { open: 'Collapse the sidebar', collapsed: 'Expand the sidebar' },
58
+ onToggle,
59
+ class: klass = '',
60
+ children,
61
+ ...axes
62
+ }: Props = $props();
63
+
64
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
65
+ let classes = $derived(cx(PREFIX, applied.classes, collapsed && `${PREFIX}-collapsed`, klass));
66
+
67
+ const uid = $props.id();
68
+
69
+ /** Fold or unfold it. */
70
+ export function toggle(value = !collapsed) {
71
+ if (value === collapsed) return;
72
+ collapsed = value;
73
+ onToggle?.(value);
74
+ }
75
+ </script>
76
+
77
+ <aside {...applied.rest} class={classes}>
78
+ <div class="{PREFIX}-head">
79
+ {@render head?.({ collapsed })}
80
+ {#if !noToggle}
81
+ <button
82
+ type="button"
83
+ class="{PREFIX}-toggle"
84
+ aria-expanded={!collapsed}
85
+ aria-controls="{uid}-body"
86
+ aria-label={collapsed ? toggleLabel.collapsed : toggleLabel.open}
87
+ title={collapsed ? toggleLabel.collapsed : toggleLabel.open}
88
+ onclick={() => toggle()}
89
+ >
90
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="3" /><path d="M9 4v16M15.5 10l-2 2 2 2" /></svg>
91
+ </button>
92
+ {/if}
93
+ </div>
94
+ <div class="{PREFIX}-body" id="{uid}-body" inert={collapsed}>
95
+ {@render children?.({ collapsed })}
96
+ </div>
97
+ {#if foot}
98
+ <div class="{PREFIX}-foot" inert={collapsed}>{@render foot({ collapsed })}</div>
99
+ {/if}
100
+ </aside>
@@ -0,0 +1,54 @@
1
+ /** What .sidebar is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015).
3
+
4
+ `.sidebar-layout` is the row it may sit in: written by hand around the
5
+ page, never by the component, so it is a PART too. */
6
+ export declare const PREFIX = "sidebar";
7
+ export declare const AXES: {
8
+ readonly size: readonly ["xs", "sm", "md", "lg", "xl"];
9
+ readonly side: readonly ["end"];
10
+ };
11
+ export declare const FLAGS: {};
12
+ export declare const PARTS: readonly ["head", "body", "foot", "toggle", "collapsed", "keep", "layout"];
13
+ /**
14
+ * An app's side column — a brand, a menu, an account — that folds to a
15
+ * rail. In the flow, unlike a Drawer: the page beside it narrows or
16
+ * widens, it is never covered.
17
+ *
18
+ * `collapsed` is bindable. The toggle sits in the head, after `head`;
19
+ * folded, only the toggle stays (and anything marked `.sidebar-keep`).
20
+ */
21
+ import type { Snippet } from 'svelte';
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>, 'children'> {
25
+ /** Folded to a rail. Bindable. */
26
+ collapsed?: boolean;
27
+ /** The top of the column: a brand, a picker. The toggle follows it. */
28
+ head?: Snippet<[{
29
+ collapsed: boolean;
30
+ }]>;
31
+ /** The bottom of the column: an account, a setting. */
32
+ foot?: Snippet<[{
33
+ collapsed: boolean;
34
+ }]>;
35
+ /** Hide the toggle: the app folds it some other way. */
36
+ noToggle?: boolean;
37
+ /** The toggle's accessible name, open and folded. */
38
+ toggleLabel?: {
39
+ open: string;
40
+ collapsed: string;
41
+ };
42
+ /** After every fold or unfold. */
43
+ onToggle?: (collapsed: boolean) => void;
44
+ class?: string;
45
+ /** The body: usually a Menu. */
46
+ children?: Snippet<[{
47
+ collapsed: boolean;
48
+ }]>;
49
+ }
50
+ declare const Sidebar: import("svelte").Component<Props, {
51
+ toggle: (value?: boolean) => void;
52
+ }, "collapsed">;
53
+ type Sidebar = ReturnType<typeof Sidebar>;
54
+ export default Sidebar;
@@ -0,0 +1,40 @@
1
+ <script module lang="ts">
2
+ /** What .skeleton is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015). */
4
+ export const PREFIX = 'skeleton';
5
+
6
+ export const AXES = {
7
+ /* A block by default; `text` makes it a line of type instead. */
8
+ variant: ['text']
9
+ } as const;
10
+
11
+ export const FLAGS = {} as const;
12
+ export const PARTS = [] as const;
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ import type { Snippet } from 'svelte';
17
+ import type { HTMLAttributes } from 'svelte/elements';
18
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
19
+
20
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
21
+ /** Repeat the placeholder this many times, stacked. */
22
+ lines?: number;
23
+ class?: string;
24
+ }
25
+
26
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
27
+ // resolved by name, so an axis a family drops changes nothing down here.
28
+ let {
29
+ lines = 1,
30
+ class: klass = '',
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
+ {#each { length: lines } as _, i (i)}
39
+ <div {...applied.rest} class={classes}></div>
40
+ {/each}
@@ -0,0 +1,18 @@
1
+ /** What .skeleton is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015). */
3
+ export declare const PREFIX = "skeleton";
4
+ export declare const AXES: {
5
+ readonly variant: readonly ["text"];
6
+ };
7
+ export declare const FLAGS: {};
8
+ export declare const PARTS: readonly [];
9
+ import type { HTMLAttributes } from 'svelte/elements';
10
+ import { type AxisProps } from '../variants.js';
11
+ interface Props extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>, HTMLAttributes<HTMLDivElement> {
12
+ /** Repeat the placeholder this many times, stacked. */
13
+ lines?: number;
14
+ class?: string;
15
+ }
16
+ declare const Skeleton: import("svelte").Component<Props, {}, "">;
17
+ type Skeleton = ReturnType<typeof Skeleton>;
18
+ export default Skeleton;