@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,550 @@
1
+ <script module lang="ts">
2
+ /** What .carousel is made of, classified. The props follow the
3
+ families the project registers (variants.ts, ADR 0015).
4
+
5
+ The carousel draws its own parts: the track, one item per entry of
6
+ `items` (and the duplicates of an infinite carousel), the cells its
7
+ arrows and pagination are drawn in, and the row of upcoming items. */
8
+ export const PREFIX = 'carousel';
9
+
10
+ export const AXES = {
11
+ /* Where an item comes to rest when several are visible. */
12
+ align: ['start', 'center', 'end']
13
+ } as const;
14
+
15
+ export const FLAGS = {
16
+ vertical: 'Slide up and down instead of sideways.'
17
+ } as const;
18
+ export const PARTS = ['track', 'item', 'prev', 'next', 'pagination', 'upcoming', 'upcoming-item'] as const;
19
+ </script>
20
+
21
+ <script lang="ts" generics="Item">
22
+ /**
23
+ * A row of items, one per entry of `items`, each drawn by `children` with
24
+ * the entry and its index. Scrolling is the browser's own — scroll-snap —
25
+ * and the script reads which item the track rests on from the measured
26
+ * items, so items may differ in size.
27
+ *
28
+ * The index is a projection of the scroll position, never bound
29
+ * (ADR 0011): `index` is the initial item, `goto` / `prev` / `next`
30
+ * (through `bind:this`) move, `onIndex` tells. The script holds no
31
+ * `$effect`: the observers, the drag, the wheel and the autoplay are
32
+ * attachments, and only exist while their prop asks for them.
33
+ *
34
+ * `arrow`, `pagination` and `upcoming` are each: absent (nothing drawn),
35
+ * `true` (a default), or a snippet of your own, handed what it needs to
36
+ * act — it does not have to know the carousel.
37
+ */
38
+ import { tick, untrack, type Snippet } from 'svelte';
39
+ import type { Attachment } from 'svelte/attachments';
40
+ import type { HTMLAttributes } from 'svelte/elements';
41
+ import { applyAxes, cx, type AxisProps } from '../variants.js';
42
+ import Pagination from './Pagination.svelte';
43
+
44
+ /** What an arrow snippet is given: drawn once per side. */
45
+ type Arrow = { direction: 'prev' | 'next'; go: () => void; disabled: boolean };
46
+ /** What a pagination snippet is given. `index` counts from 0. */
47
+ type Pages = { count: number; index: number; goto: (index: number) => void };
48
+ /** What an upcoming snippet is given: the items after the current one. */
49
+ type Upcoming = { items: { item: Item; index: number }[]; goto: (index: number) => void };
50
+
51
+ interface Props
52
+ extends AxisProps<typeof AXES, typeof FLAGS, typeof PREFIX>,
53
+ Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
54
+ /** The items' data. */
55
+ items: Item[];
56
+ /** One item, from its entry and its index. */
57
+ children: Snippet<[Item, number]>;
58
+ /** Previous / next: `true` for chevron buttons, or a snippet drawn on each side. */
59
+ arrow?: true | null | Snippet<[Arrow]>;
60
+ /** Under the items: `true` for the pagination in its dots variant, or a snippet. */
61
+ pagination?: true | null | Snippet<[Pages]>;
62
+ /**
63
+ * The items that come next, small, under the track: `true` for the
64
+ * next 3 drawn by `children`, a number for that many, or a snippet.
65
+ */
66
+ upcoming?: true | number | null | Snippet<[Upcoming]>;
67
+ /** The item to start on, from 0. Not bound: move with `goto`, listen with `onIndex`. */
68
+ index?: number;
69
+ /** At the last item, next goes back to the first, and the other way round. */
70
+ loop?: boolean;
71
+ /** No end: items are duplicated on both sides so the row runs on with no jump. Wins over `loop`. */
72
+ infinite?: boolean;
73
+ /**
74
+ * Move on by itself every this many milliseconds (`true`: 5000). It
75
+ * pauses under the pointer, while focus is inside, while the tab is
76
+ * hidden, and does not run at all with prefers-reduced-motion. At the
77
+ * end it starts over from the first item; infinite, it runs on.
78
+ */
79
+ autoplay?: boolean | number;
80
+ /** Drag with the mouse. Default `true`. */
81
+ drag?: boolean;
82
+ /** Scroll with the wheel, the trackpad or a finger. Default `true`. */
83
+ scroll?: boolean;
84
+ /** Move with the arrow keys, Home and End once the track has the focus. Default `true`. */
85
+ keyboard?: boolean;
86
+ /** Called with the new index whenever the current item changes. */
87
+ onIndex?: (index: number) => void;
88
+ /** Accessible names. */
89
+ previousLabel?: string;
90
+ nextLabel?: string;
91
+ itemLabel?: (index: number, count: number) => string;
92
+ class?: string;
93
+ }
94
+
95
+ // The axes are deliberately NOT destructured: they travel in `axes` and are
96
+ // resolved by name. The scrolling reads the direction and the resting
97
+ // point from the CSS the browser applied, not from the props.
98
+ let {
99
+ items,
100
+ children,
101
+ arrow = null,
102
+ pagination = null,
103
+ upcoming = null,
104
+ index = 0,
105
+ loop = false,
106
+ infinite = false,
107
+ autoplay = false,
108
+ drag = true,
109
+ scroll = true,
110
+ keyboard = true,
111
+ onIndex,
112
+ previousLabel = 'Previous item',
113
+ nextLabel = 'Next item',
114
+ itemLabel = (i: number, n: number) => `${i + 1} / ${n}`,
115
+ class: klass = '',
116
+ ...axes
117
+ }: Props = $props();
118
+
119
+ let applied = $derived(applyAxes(PREFIX, AXES, FLAGS, axes));
120
+ let classes = $derived(cx(PREFIX, applied.classes, klass));
121
+
122
+ let track: HTMLDivElement | undefined = $state();
123
+ let count = $derived(items.length);
124
+ let wraps = $derived(loop || infinite);
125
+
126
+ const mod = (i: number, n: number) => ((i % n) + n) % n;
127
+
128
+ /* ── Where we are ─────────────────────────────────────────────────── */
129
+
130
+ /** The item the track rests on — read from the scroll position, never set. */
131
+ let current = $state(untrack(() => index));
132
+ let atStart = $state(true);
133
+ let atEnd = $state(false);
134
+ /** Duplicates drawn on EACH side of an infinite carousel. 0 until measured. */
135
+ let clones = $state(0);
136
+ /** The child of the track resting in view, duplicates counted. */
137
+ let rendered = 0;
138
+
139
+ /** Up and down, as the stylesheet lays the track out. */
140
+ const isVertical = () => !!track && getComputedStyle(track).flexDirection.startsWith('column');
141
+ const isRtl = () => !!track && getComputedStyle(track).direction === 'rtl';
142
+
143
+ /** The scroll position at which an item rests, from its own snap point. */
144
+ function anchor(item: Element, vertical: boolean, view: number) {
145
+ const el = item as HTMLElement;
146
+ const start = vertical ? el.offsetTop : el.offsetLeft;
147
+ const size = vertical ? el.offsetHeight : el.offsetWidth;
148
+ const snap = getComputedStyle(el).scrollSnapAlign;
149
+ return snap.includes('center') ? start - (view - size) / 2 : snap.includes('end') ? start - view + size : start;
150
+ }
151
+
152
+ /** Scroll position ↔ index. One reading, done on demand: the DOM is the truth. */
153
+ function measure() {
154
+ if (!track || count === 0) return;
155
+ const vertical = isVertical();
156
+ const pos = vertical ? track.scrollTop : track.scrollLeft;
157
+ const view = vertical ? track.clientHeight : track.clientWidth;
158
+ const full = vertical ? track.scrollHeight : track.scrollWidth;
159
+ // A track that cannot scroll (a stylesheet swapping) reports 0, not
160
+ // where it rests: keep the last reading.
161
+ if (full <= view + 1 && count > 1) return;
162
+ let best = 0;
163
+ let bestDistance = Infinity;
164
+ Array.from(track.children).forEach((el, r) => {
165
+ const distance = Math.abs(anchor(el, vertical, view) - pos);
166
+ if (distance < bestDistance) {
167
+ bestDistance = distance;
168
+ best = r;
169
+ }
170
+ });
171
+ rendered = best;
172
+ atStart = pos <= 1;
173
+ atEnd = pos + view >= full - 1;
174
+ const i = mod(best - clones, count);
175
+ if (i !== current) {
176
+ current = i;
177
+ onIndex?.(i);
178
+ }
179
+ }
180
+
181
+ /** Scroll so the track's child `r` rests in view. */
182
+ function toChild(r: number, instant = false) {
183
+ const item = track?.children[r];
184
+ if (!track || !item) return;
185
+ const vertical = isVertical();
186
+ const at = anchor(item, vertical, vertical ? track.clientHeight : track.clientWidth);
187
+ // Otherwise the track's own scroll-behavior decides: smooth, unless reduced motion.
188
+ const behavior = instant ? 'instant' : undefined;
189
+ track.scrollTo(vertical ? { top: at, behavior } : { left: at, behavior });
190
+ }
191
+
192
+ /** How many duplicates fill a view on each side, plus one to step into. */
193
+ function fit() {
194
+ if (!track || count < 2) return 0;
195
+ const vertical = isVertical();
196
+ const view = vertical ? track.clientHeight : track.clientWidth;
197
+ const own = Array.from(track.children).slice(clones, clones + count) as HTMLElement[];
198
+ const smallest = Math.min(...own.map((el) => (vertical ? el.offsetHeight : el.offsetWidth)));
199
+ // Nothing laid out (a stylesheet swapping, a hidden parent): keep what is
200
+ // drawn, or the items shift under the scroll position and the index with them.
201
+ if (!(smallest > 0) || !view) return clones;
202
+ return Math.min(count, Math.ceil(view / smallest) + 1);
203
+ }
204
+
205
+ /** After a resize or new items: draw the duplicates needed, rest on the current item. */
206
+ async function layout() {
207
+ if (!track) return;
208
+ const wanted = infinite ? fit() : 0;
209
+ if (wanted !== clones) {
210
+ clones = wanted;
211
+ await tick();
212
+ }
213
+ toChild(clones + Math.min(current, count - 1), true);
214
+ measure();
215
+ }
216
+
217
+ /* Once the scroll stops: an infinite carousel resting on a duplicate is
218
+ moved, with no animation, onto the item it duplicates — the same
219
+ picture, one round further. */
220
+ let frame = 0;
221
+ let settling: ReturnType<typeof setTimeout> | undefined;
222
+ let dragged = false;
223
+
224
+ function settle() {
225
+ if (!track) return;
226
+ measure();
227
+ if (dragged) {
228
+ dragged = false;
229
+ track.style.scrollSnapType = '';
230
+ }
231
+ if (!infinite || !clones || (rendered >= clones && rendered < clones + count)) return;
232
+ const vertical = isVertical();
233
+ const view = vertical ? track.clientHeight : track.clientWidth;
234
+ const from = track.children[rendered];
235
+ const to = track.children[rendered < clones ? rendered + count : rendered - count];
236
+ const shift = anchor(to!, vertical, view) - anchor(from!, vertical, view);
237
+ track.scrollBy(vertical ? { top: shift, behavior: 'instant' } : { left: shift, behavior: 'instant' });
238
+ rendered += rendered < clones ? count : -count;
239
+ }
240
+
241
+ function onscroll() {
242
+ cancelAnimationFrame(frame);
243
+ frame = requestAnimationFrame(measure);
244
+ clearTimeout(settling);
245
+ settling = setTimeout(settle, 120);
246
+ }
247
+
248
+ /* ── Moving ───────────────────────────────────────────────────────── */
249
+
250
+ /** Scroll to item `i` — wrapped with `loop` or `infinite`, clamped otherwise. */
251
+ export function goto(i: number) {
252
+ if (!track || count === 0) return;
253
+ if (infinite && clones) {
254
+ // The same item exists up to three times: take the nearest copy.
255
+ const own = clones + mod(i, count);
256
+ const copies = [own - count, own, own + count].filter((r) => r >= 0 && r < count + 2 * clones);
257
+ toChild(copies.reduce((a, b) => (Math.abs(b - rendered) < Math.abs(a - rendered) ? b : a)));
258
+ } else {
259
+ toChild(clones + (wraps ? mod(i, count) : Math.min(Math.max(0, i), count - 1)));
260
+ }
261
+ }
262
+
263
+ export function prev() {
264
+ if (!infinite && atStart && loop) goto(count - 1);
265
+ else goto(current - 1);
266
+ }
267
+
268
+ export function next(wrap = loop) {
269
+ if (!infinite && atEnd && wrap) goto(0);
270
+ else goto(current + 1);
271
+ }
272
+
273
+ /* ── Interactions: each one exists only while its prop asks for it ── */
274
+
275
+ /** Measure once laid out, and whenever the track, an item, or the items change. */
276
+ const observe: Attachment<HTMLDivElement> = (node) => {
277
+ const sizes = new ResizeObserver(() => layout());
278
+ const watch = () => {
279
+ sizes.disconnect();
280
+ sizes.observe(node);
281
+ for (const child of node.children) sizes.observe(child);
282
+ };
283
+ const list = new MutationObserver(watch);
284
+ list.observe(node, { childList: true });
285
+ watch();
286
+ return () => {
287
+ sizes.disconnect();
288
+ list.disconnect();
289
+ };
290
+ };
291
+
292
+ /* A vertical wheel moves a sideways track one item per gesture, and
293
+ hands the page back at the ends. A horizontal one (a trackpad) is the
294
+ browser's. */
295
+ const wheeling: Attachment<HTMLDivElement> = (node) => {
296
+ let locked = false;
297
+ let quiet: ReturnType<typeof setTimeout> | undefined;
298
+ const onwheel = (e: WheelEvent) => {
299
+ if (isVertical() || Math.abs(e.deltaX) >= Math.abs(e.deltaY)) return;
300
+ const forward = e.deltaY > 0;
301
+ if (!infinite && (forward ? atEnd : atStart)) return;
302
+ e.preventDefault();
303
+ // One gesture, one item: unlocked once the wheel has been quiet a moment.
304
+ clearTimeout(quiet);
305
+ quiet = setTimeout(() => (locked = false), 200);
306
+ if (locked) return;
307
+ locked = true;
308
+ if (forward) next(false);
309
+ else goto(current - 1);
310
+ };
311
+ node.addEventListener('wheel', onwheel, { passive: false });
312
+ return () => {
313
+ clearTimeout(quiet);
314
+ node.removeEventListener('wheel', onwheel);
315
+ };
316
+ };
317
+
318
+ /* The mouse drags the row; on release it comes to rest on the nearest
319
+ item, or on the next one in the drag's direction after a real pull. */
320
+ const dragging: Attachment<HTMLDivElement> = (node) => {
321
+ let start = 0;
322
+ let origin = 0;
323
+ let from = 0;
324
+ let delta = 0;
325
+ let vertical = false;
326
+
327
+ const move = (e: PointerEvent) => {
328
+ delta = (vertical ? e.clientY : e.clientX) - start;
329
+ if (!dragged && Math.abs(delta) < 5) return;
330
+ if (!dragged) {
331
+ dragged = true;
332
+ node.style.scrollSnapType = 'none';
333
+ }
334
+ node.scrollTo(vertical ? { top: origin - delta, behavior: 'instant' } : { left: origin - delta, behavior: 'instant' });
335
+ };
336
+ const up = () => {
337
+ window.removeEventListener('pointermove', move);
338
+ window.removeEventListener('pointerup', up);
339
+ if (!dragged) return;
340
+ // The release is not a click on what lies under it.
341
+ const swallow = (e: Event) => {
342
+ e.preventDefault();
343
+ e.stopPropagation();
344
+ };
345
+ node.addEventListener('click', swallow, { capture: true, once: true });
346
+ setTimeout(() => node.removeEventListener('click', swallow, { capture: true }));
347
+ measure();
348
+ let r = rendered;
349
+ if (r === from && Math.abs(delta) > 30) r += (delta < 0 ? 1 : -1) * (isRtl() && !vertical ? -1 : 1);
350
+ toChild(Math.min(Math.max(0, r), node.children.length - 1));
351
+ // The snap comes back once the row is at rest (settle).
352
+ clearTimeout(settling);
353
+ settling = setTimeout(settle, 120);
354
+ };
355
+ const down = (e: PointerEvent) => {
356
+ if (e.pointerType !== 'mouse' || e.button !== 0) return;
357
+ vertical = isVertical();
358
+ start = vertical ? e.clientY : e.clientX;
359
+ origin = vertical ? node.scrollTop : node.scrollLeft;
360
+ from = rendered;
361
+ delta = 0;
362
+ window.addEventListener('pointermove', move);
363
+ window.addEventListener('pointerup', up);
364
+ };
365
+ const nodrag = (e: DragEvent) => e.preventDefault();
366
+
367
+ node.addEventListener('pointerdown', down);
368
+ node.addEventListener('dragstart', nodrag);
369
+ return () => {
370
+ node.removeEventListener('pointerdown', down);
371
+ node.removeEventListener('dragstart', nodrag);
372
+ window.removeEventListener('pointermove', move);
373
+ window.removeEventListener('pointerup', up);
374
+ };
375
+ };
376
+
377
+ /* The keys are ours rather than the browser's, so they move one item
378
+ whether the track can scroll or not — and do nothing when switched off. */
379
+ function onkeydown(e: KeyboardEvent) {
380
+ if (e.target !== track) return;
381
+ const vertical = isVertical();
382
+ const flip = !vertical && isRtl();
383
+ const back = vertical ? 'ArrowUp' : flip ? 'ArrowRight' : 'ArrowLeft';
384
+ const forth = vertical ? 'ArrowDown' : flip ? 'ArrowLeft' : 'ArrowRight';
385
+ const keys = [back, forth, 'Home', 'End', 'PageUp', 'PageDown', ' '];
386
+ if (!keys.includes(e.key)) return;
387
+ e.preventDefault();
388
+ if (!keyboard) return;
389
+ if (e.key === back) prev();
390
+ else if (e.key === forth) next();
391
+ else if (e.key === 'Home') goto(0);
392
+ else if (e.key === 'End') goto(count - 1);
393
+ }
394
+
395
+ /* ── Autoplay ─────────────────────────────────────────────────────── */
396
+
397
+ let delay = $derived(autoplay === true ? 5000 : typeof autoplay === 'number' ? autoplay : 0);
398
+
399
+ const autoplaying =
400
+ (ms: number): Attachment<HTMLDivElement> =>
401
+ (node) => {
402
+ if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
403
+ let hovered = false;
404
+ let focused = false;
405
+ let id: ReturnType<typeof setInterval> | undefined;
406
+ const run = () => {
407
+ clearInterval(id);
408
+ if (hovered || focused || document.visibilityState === 'hidden') return;
409
+ id = setInterval(() => count > 1 && next(true), ms);
410
+ };
411
+ const enter = () => ((hovered = true), run());
412
+ const leave = () => ((hovered = false), run());
413
+ const focusin = () => ((focused = true), run());
414
+ const focusout = (e: FocusEvent) => ((focused = node.contains(e.relatedTarget as Node | null)), run());
415
+ node.addEventListener('pointerenter', enter);
416
+ node.addEventListener('pointerleave', leave);
417
+ node.addEventListener('focusin', focusin);
418
+ node.addEventListener('focusout', focusout);
419
+ document.addEventListener('visibilitychange', run);
420
+ run();
421
+ return () => {
422
+ clearInterval(id);
423
+ node.removeEventListener('pointerenter', enter);
424
+ node.removeEventListener('pointerleave', leave);
425
+ node.removeEventListener('focusin', focusin);
426
+ node.removeEventListener('focusout', focusout);
427
+ document.removeEventListener('visibilitychange', run);
428
+ };
429
+ };
430
+
431
+ /* ── What the snippets are given ─────────────────────────────────── */
432
+
433
+ /* A scrolling region has to take the focus, or the keyboard can not reach
434
+ it (Safari does not make scrollers focusable on its own). Switched off,
435
+ it leaves the tab order. The a11y lint only sees a div with a tabindex;
436
+ spread, it lets the attribute through. */
437
+ let focusable = $derived({ tabindex: keyboard ? 0 : -1 });
438
+
439
+ let prevArrow = $derived<Arrow>({ direction: 'prev', go: prev, disabled: !wraps && atStart });
440
+ let nextArrow = $derived<Arrow>({ direction: 'next', go: () => next(), disabled: !wraps && atEnd });
441
+ let pages = $derived<Pages>({ count, index: current, goto });
442
+
443
+ let ahead = $derived.by(() => {
444
+ const n = typeof upcoming === 'number' ? upcoming : 3;
445
+ const list: { item: Item; index: number }[] = [];
446
+ for (let step = 1; step <= Math.min(n, count - 1); step++) {
447
+ const i = current + step;
448
+ if (i >= count && !wraps) break;
449
+ list.push({ item: items[mod(i, count)]!, index: mod(i, count) });
450
+ }
451
+ return list;
452
+ });
453
+ let coming = $derived<Upcoming>({ items: ahead, goto });
454
+ </script>
455
+
456
+ {#snippet chevron({ direction, go, disabled }: Arrow)}
457
+ <button
458
+ type="button"
459
+ class="btn btn-circle btn-sm"
460
+ aria-label={direction === 'prev' ? previousLabel : nextLabel}
461
+ {disabled}
462
+ onclick={go}
463
+ >
464
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
465
+ <path d={direction === 'prev' ? 'm15 18-6-6 6-6' : 'm9 18 6-6-6-6'} />
466
+ </svg>
467
+ </button>
468
+ {/snippet}
469
+
470
+ {#snippet thumbnails({ items: next, goto }: Upcoming)}
471
+ {#each next as { item, index: i } (i)}
472
+ <button type="button" class={`${PREFIX}-upcoming-item`} aria-label={itemLabel(i, count)} onclick={() => goto(i)}>
473
+ {@render children(item, i)}
474
+ </button>
475
+ {/each}
476
+ {/snippet}
477
+
478
+ {#snippet duplicate(i: number)}
479
+ <!-- A duplicate is only a picture: out of the accessibility tree, out of reach. -->
480
+ <div class={`${PREFIX}-item`} aria-hidden="true" inert>
481
+ {@render children(items[i]!, i)}
482
+ </div>
483
+ {/snippet}
484
+
485
+ <div
486
+ {...applied.rest}
487
+ class={classes}
488
+ role="region"
489
+ aria-roledescription="carousel"
490
+ {@attach delay ? autoplaying(delay) : undefined}
491
+ >
492
+ <div
493
+ bind:this={track}
494
+ class={`${PREFIX}-track`}
495
+ style={scroll ? undefined : 'overflow: hidden'}
496
+ {...focusable}
497
+ {onscroll}
498
+ {onkeydown}
499
+ {@attach observe}
500
+ {@attach scroll ? wheeling : undefined}
501
+ {@attach drag ? dragging : undefined}
502
+ >
503
+ {#each { length: clones }, c}
504
+ {@render duplicate(mod(count - clones + c, count))}
505
+ {/each}
506
+ {#each items as item, i (i)}
507
+ <div
508
+ class={`${PREFIX}-item`}
509
+ role="group"
510
+ aria-roledescription="item"
511
+ aria-label={itemLabel(i, count)}
512
+ >
513
+ {@render children(item, i)}
514
+ </div>
515
+ {/each}
516
+ {#each { length: clones }, c}
517
+ {@render duplicate(c)}
518
+ {/each}
519
+ </div>
520
+
521
+ {#if arrow}
522
+ <div class={`${PREFIX}-prev`}>
523
+ {#if typeof arrow === 'function'}{@render arrow(prevArrow)}{:else}{@render chevron(prevArrow)}{/if}
524
+ </div>
525
+ <div class={`${PREFIX}-next`}>
526
+ {#if typeof arrow === 'function'}{@render arrow(nextArrow)}{:else}{@render chevron(nextArrow)}{/if}
527
+ </div>
528
+ {/if}
529
+
530
+ {#if upcoming && ahead.length}
531
+ <div class={`${PREFIX}-upcoming`}>
532
+ {#if typeof upcoming === 'function'}{@render upcoming(coming)}{:else}{@render thumbnails(coming)}{/if}
533
+ </div>
534
+ {/if}
535
+
536
+ {#if pagination}
537
+ <div class={`${PREFIX}-pagination`}>
538
+ {#if typeof pagination === 'function'}{@render pagination(pages)}{:else}
539
+ <Pagination
540
+ variant="dots"
541
+ {count}
542
+ page={current + 1}
543
+ onPage={(p) => goto(p - 1)}
544
+ pageLabel={(p) => itemLabel(p - 1, count)}
545
+ aria-label="Items"
546
+ />
547
+ {/if}
548
+ </div>
549
+ {/if}
550
+ </div>
@@ -0,0 +1,124 @@
1
+ /** What .carousel is made of, classified. The props follow the
2
+ families the project registers (variants.ts, ADR 0015).
3
+
4
+ The carousel draws its own parts: the track, one item per entry of
5
+ `items` (and the duplicates of an infinite carousel), the cells its
6
+ arrows and pagination are drawn in, and the row of upcoming items. */
7
+ export declare const PREFIX = "carousel";
8
+ export declare const AXES: {
9
+ readonly align: readonly ["start", "center", "end"];
10
+ };
11
+ export declare const FLAGS: {
12
+ readonly vertical: "Slide up and down instead of sideways.";
13
+ };
14
+ export declare const PARTS: readonly ["track", "item", "prev", "next", "pagination", "upcoming", "upcoming-item"];
15
+ /**
16
+ * A row of items, one per entry of `items`, each drawn by `children` with
17
+ * the entry and its index. Scrolling is the browser's own — scroll-snap —
18
+ * and the script reads which item the track rests on from the measured
19
+ * items, so items may differ in size.
20
+ *
21
+ * The index is a projection of the scroll position, never bound
22
+ * (ADR 0011): `index` is the initial item, `goto` / `prev` / `next`
23
+ * (through `bind:this`) move, `onIndex` tells. The script holds no
24
+ * `$effect`: the observers, the drag, the wheel and the autoplay are
25
+ * attachments, and only exist while their prop asks for them.
26
+ *
27
+ * `arrow`, `pagination` and `upcoming` are each: absent (nothing drawn),
28
+ * `true` (a default), or a snippet of your own, handed what it needs to
29
+ * act — it does not have to know the carousel.
30
+ */
31
+ import { type Snippet } from 'svelte';
32
+ import type { HTMLAttributes } from 'svelte/elements';
33
+ declare function $$render<Item>(): {
34
+ props: {
35
+ readonly align?: "start" | "end" | "center" | undefined;
36
+ } & {
37
+ vertical?: boolean | undefined;
38
+ } & {
39
+ background?: string;
40
+ } & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
41
+ /** The items' data. */
42
+ items: Item[];
43
+ /** One item, from its entry and its index. */
44
+ children: Snippet<[Item, number]>;
45
+ /** Previous / next: `true` for chevron buttons, or a snippet drawn on each side. */
46
+ arrow?: true | null | Snippet<[{
47
+ direction: "prev" | "next";
48
+ go: () => void;
49
+ disabled: boolean;
50
+ }]>;
51
+ /** Under the items: `true` for the pagination in its dots variant, or a snippet. */
52
+ pagination?: true | null | Snippet<[{
53
+ count: number;
54
+ index: number;
55
+ goto: (index: number) => void;
56
+ }]>;
57
+ /**
58
+ * The items that come next, small, under the track: `true` for the
59
+ * next 3 drawn by `children`, a number for that many, or a snippet.
60
+ */
61
+ upcoming?: true | number | null | Snippet<[{
62
+ items: {
63
+ item: Item;
64
+ index: number;
65
+ }[];
66
+ goto: (index: number) => void;
67
+ }]>;
68
+ /** The item to start on, from 0. Not bound: move with `goto`, listen with `onIndex`. */
69
+ index?: number;
70
+ /** At the last item, next goes back to the first, and the other way round. */
71
+ loop?: boolean;
72
+ /** No end: items are duplicated on both sides so the row runs on with no jump. Wins over `loop`. */
73
+ infinite?: boolean;
74
+ /**
75
+ * Move on by itself every this many milliseconds (`true`: 5000). It
76
+ * pauses under the pointer, while focus is inside, while the tab is
77
+ * hidden, and does not run at all with prefers-reduced-motion. At the
78
+ * end it starts over from the first item; infinite, it runs on.
79
+ */
80
+ autoplay?: boolean | number;
81
+ /** Drag with the mouse. Default `true`. */
82
+ drag?: boolean;
83
+ /** Scroll with the wheel, the trackpad or a finger. Default `true`. */
84
+ scroll?: boolean;
85
+ /** Move with the arrow keys, Home and End once the track has the focus. Default `true`. */
86
+ keyboard?: boolean;
87
+ /** Called with the new index whenever the current item changes. */
88
+ onIndex?: (index: number) => void;
89
+ /** Accessible names. */
90
+ previousLabel?: string;
91
+ nextLabel?: string;
92
+ itemLabel?: (index: number, count: number) => string;
93
+ class?: string;
94
+ };
95
+ exports: {
96
+ goto: (i: number) => void;
97
+ prev: () => void;
98
+ next: (wrap?: boolean) => void;
99
+ };
100
+ bindings: "";
101
+ slots: {};
102
+ events: {};
103
+ };
104
+ declare class __sveltets_Render<Item> {
105
+ props(): ReturnType<typeof $$render<Item>>['props'];
106
+ events(): ReturnType<typeof $$render<Item>>['events'];
107
+ slots(): ReturnType<typeof $$render<Item>>['slots'];
108
+ bindings(): "";
109
+ exports(): {
110
+ goto: (i: number) => void;
111
+ prev: () => void;
112
+ next: (wrap?: boolean) => void;
113
+ };
114
+ }
115
+ interface $$IsomorphicComponent {
116
+ new <Item>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Item>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Item>['props']>, ReturnType<__sveltets_Render<Item>['events']>, ReturnType<__sveltets_Render<Item>['slots']>> & {
117
+ $$bindings?: ReturnType<__sveltets_Render<Item>['bindings']>;
118
+ } & ReturnType<__sveltets_Render<Item>['exports']>;
119
+ <Item>(internal: unknown, props: ReturnType<__sveltets_Render<Item>['props']> & {}): ReturnType<__sveltets_Render<Item>['exports']>;
120
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
121
+ }
122
+ declare const Carousel: $$IsomorphicComponent;
123
+ type Carousel<Item> = InstanceType<typeof Carousel<Item>>;
124
+ export default Carousel;