@sveltier/ui 0.0.0-stage → 0.1.0-alpha.1

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 (331) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -2
  3. package/dist/components/Accordion/Accordion.svelte +74 -0
  4. package/dist/components/Accordion/Accordion.svelte.d.ts +25 -0
  5. package/dist/components/Accordion/accordion.css +88 -0
  6. package/dist/components/Accordion/index.d.ts +2 -0
  7. package/dist/components/Accordion/index.js +1 -0
  8. package/dist/components/Accordion/types.d.ts +64 -0
  9. package/dist/components/Accordion/types.js +1 -0
  10. package/dist/components/Alert/Alert.svelte +88 -0
  11. package/dist/components/Alert/Alert.svelte.d.ts +4 -0
  12. package/dist/components/Alert/alert.css +78 -0
  13. package/dist/components/Alert/index.d.ts +2 -0
  14. package/dist/components/Alert/index.js +1 -0
  15. package/dist/components/Alert/types.d.ts +67 -0
  16. package/dist/components/Alert/types.js +1 -0
  17. package/dist/components/Avatar/Avatar.svelte +47 -0
  18. package/dist/components/Avatar/Avatar.svelte.d.ts +4 -0
  19. package/dist/components/Avatar/avatar.css +34 -0
  20. package/dist/components/Avatar/index.d.ts +2 -0
  21. package/dist/components/Avatar/index.js +1 -0
  22. package/dist/components/Avatar/types.d.ts +47 -0
  23. package/dist/components/Avatar/types.js +1 -0
  24. package/dist/components/Badge/Badge.svelte +53 -0
  25. package/dist/components/Badge/Badge.svelte.d.ts +4 -0
  26. package/dist/components/Badge/badge.css +57 -0
  27. package/dist/components/Badge/index.d.ts +2 -0
  28. package/dist/components/Badge/index.js +1 -0
  29. package/dist/components/Badge/types.d.ts +43 -0
  30. package/dist/components/Badge/types.js +1 -0
  31. package/dist/components/Button/Button.svelte +85 -0
  32. package/dist/components/Button/Button.svelte.d.ts +4 -0
  33. package/dist/components/Button/button.css +132 -0
  34. package/dist/components/Button/index.d.ts +2 -0
  35. package/dist/components/Button/index.js +1 -0
  36. package/dist/components/Button/types.d.ts +76 -0
  37. package/dist/components/Button/types.js +1 -0
  38. package/dist/components/ButtonGroup/ButtonGroup.svelte +61 -0
  39. package/dist/components/ButtonGroup/ButtonGroup.svelte.d.ts +4 -0
  40. package/dist/components/ButtonGroup/button-group.css +39 -0
  41. package/dist/components/ButtonGroup/index.d.ts +2 -0
  42. package/dist/components/ButtonGroup/index.js +1 -0
  43. package/dist/components/ButtonGroup/types.d.ts +48 -0
  44. package/dist/components/ButtonGroup/types.js +1 -0
  45. package/dist/components/Card/Card.svelte +53 -0
  46. package/dist/components/Card/Card.svelte.d.ts +4 -0
  47. package/dist/components/Card/card.css +62 -0
  48. package/dist/components/Card/index.d.ts +2 -0
  49. package/dist/components/Card/index.js +1 -0
  50. package/dist/components/Card/types.d.ts +56 -0
  51. package/dist/components/Card/types.js +1 -0
  52. package/dist/components/Checkbox/Checkbox.svelte +105 -0
  53. package/dist/components/Checkbox/Checkbox.svelte.d.ts +4 -0
  54. package/dist/components/Checkbox/checkbox.css +138 -0
  55. package/dist/components/Checkbox/index.d.ts +2 -0
  56. package/dist/components/Checkbox/index.js +1 -0
  57. package/dist/components/Checkbox/types.d.ts +76 -0
  58. package/dist/components/Checkbox/types.js +1 -0
  59. package/dist/components/Chip/Chip.svelte +109 -0
  60. package/dist/components/Chip/Chip.svelte.d.ts +4 -0
  61. package/dist/components/Chip/chip.css +101 -0
  62. package/dist/components/Chip/index.d.ts +2 -0
  63. package/dist/components/Chip/index.js +1 -0
  64. package/dist/components/Chip/types.d.ts +69 -0
  65. package/dist/components/Chip/types.js +1 -0
  66. package/dist/components/Choice/Choice.svelte +60 -0
  67. package/dist/components/Choice/Choice.svelte.d.ts +4 -0
  68. package/dist/components/Choice/choice.css +75 -0
  69. package/dist/components/Choice/types.d.ts +43 -0
  70. package/dist/components/Choice/types.js +1 -0
  71. package/dist/components/Dialog/Dialog.svelte +36 -0
  72. package/dist/components/Dialog/Dialog.svelte.d.ts +4 -0
  73. package/dist/components/Dialog/index.d.ts +2 -0
  74. package/dist/components/Dialog/index.js +1 -0
  75. package/dist/components/Dialog/types.d.ts +50 -0
  76. package/dist/components/Dialog/types.js +1 -0
  77. package/dist/components/Divider/Divider.svelte +19 -0
  78. package/dist/components/Divider/Divider.svelte.d.ts +4 -0
  79. package/dist/components/Divider/divider.css +23 -0
  80. package/dist/components/Divider/index.d.ts +2 -0
  81. package/dist/components/Divider/index.js +1 -0
  82. package/dist/components/Divider/types.d.ts +10 -0
  83. package/dist/components/Divider/types.js +1 -0
  84. package/dist/components/Field/Field.svelte +95 -0
  85. package/dist/components/Field/Field.svelte.d.ts +4 -0
  86. package/dist/components/Field/field.css +210 -0
  87. package/dist/components/Field/index.d.ts +3 -0
  88. package/dist/components/Field/index.js +2 -0
  89. package/dist/components/Field/types.d.ts +109 -0
  90. package/dist/components/Field/types.js +2 -0
  91. package/dist/components/Form/Form.svelte +93 -0
  92. package/dist/components/Form/Form.svelte.d.ts +8 -0
  93. package/dist/components/Form/form.css +16 -0
  94. package/dist/components/Form/index.d.ts +2 -0
  95. package/dist/components/Form/index.js +1 -0
  96. package/dist/components/Form/types.d.ts +38 -0
  97. package/dist/components/Form/types.js +1 -0
  98. package/dist/components/Icon/Icon.svelte +38 -0
  99. package/dist/components/Icon/Icon.svelte.d.ts +4 -0
  100. package/dist/components/Icon/icon.css +33 -0
  101. package/dist/components/Icon/index.d.ts +2 -0
  102. package/dist/components/Icon/index.js +1 -0
  103. package/dist/components/Icon/types.d.ts +34 -0
  104. package/dist/components/Icon/types.js +1 -0
  105. package/dist/components/List/List.svelte +39 -0
  106. package/dist/components/List/List.svelte.d.ts +4 -0
  107. package/dist/components/List/ListItem.svelte +57 -0
  108. package/dist/components/List/ListItem.svelte.d.ts +4 -0
  109. package/dist/components/List/index.d.ts +3 -0
  110. package/dist/components/List/index.js +2 -0
  111. package/dist/components/List/list.css +127 -0
  112. package/dist/components/List/types.d.ts +67 -0
  113. package/dist/components/List/types.js +1 -0
  114. package/dist/components/Menu/Menu.svelte +195 -0
  115. package/dist/components/Menu/Menu.svelte.d.ts +4 -0
  116. package/dist/components/Menu/index.d.ts +2 -0
  117. package/dist/components/Menu/index.js +1 -0
  118. package/dist/components/Menu/menu.css +103 -0
  119. package/dist/components/Menu/types.d.ts +90 -0
  120. package/dist/components/Menu/types.js +1 -0
  121. package/dist/components/Modal/Modal.svelte +146 -0
  122. package/dist/components/Modal/Modal.svelte.d.ts +4 -0
  123. package/dist/components/Modal/modal.css +150 -0
  124. package/dist/components/Modal/types.d.ts +54 -0
  125. package/dist/components/Modal/types.js +1 -0
  126. package/dist/components/NativeSelect/NativeSelect.svelte +110 -0
  127. package/dist/components/NativeSelect/NativeSelect.svelte.d.ts +25 -0
  128. package/dist/components/NativeSelect/index.d.ts +2 -0
  129. package/dist/components/NativeSelect/index.js +1 -0
  130. package/dist/components/NativeSelect/native-select.css +45 -0
  131. package/dist/components/NativeSelect/types.d.ts +58 -0
  132. package/dist/components/NativeSelect/types.js +1 -0
  133. package/dist/components/NumberField/NumberField.svelte +246 -0
  134. package/dist/components/NumberField/NumberField.svelte.d.ts +4 -0
  135. package/dist/components/NumberField/index.d.ts +2 -0
  136. package/dist/components/NumberField/index.js +1 -0
  137. package/dist/components/NumberField/number-field.css +74 -0
  138. package/dist/components/NumberField/types.d.ts +73 -0
  139. package/dist/components/NumberField/types.js +1 -0
  140. package/dist/components/Popover/Popover.svelte +115 -0
  141. package/dist/components/Popover/Popover.svelte.d.ts +4 -0
  142. package/dist/components/Popover/index.d.ts +2 -0
  143. package/dist/components/Popover/index.js +1 -0
  144. package/dist/components/Popover/popover.css +67 -0
  145. package/dist/components/Popover/types.d.ts +83 -0
  146. package/dist/components/Popover/types.js +1 -0
  147. package/dist/components/Progress/ProgressCircular.svelte +61 -0
  148. package/dist/components/Progress/ProgressCircular.svelte.d.ts +4 -0
  149. package/dist/components/Progress/ProgressLinear.svelte +44 -0
  150. package/dist/components/Progress/ProgressLinear.svelte.d.ts +4 -0
  151. package/dist/components/Progress/index.d.ts +3 -0
  152. package/dist/components/Progress/index.js +2 -0
  153. package/dist/components/Progress/progress-circular.css +61 -0
  154. package/dist/components/Progress/progress-linear.css +67 -0
  155. package/dist/components/Progress/types.d.ts +53 -0
  156. package/dist/components/Progress/types.js +1 -0
  157. package/dist/components/RadioGroup/RadioGroup.svelte +149 -0
  158. package/dist/components/RadioGroup/RadioGroup.svelte.d.ts +25 -0
  159. package/dist/components/RadioGroup/index.d.ts +2 -0
  160. package/dist/components/RadioGroup/index.js +1 -0
  161. package/dist/components/RadioGroup/radio-group.css +139 -0
  162. package/dist/components/RadioGroup/types.d.ts +93 -0
  163. package/dist/components/RadioGroup/types.js +1 -0
  164. package/dist/components/SegmentedControl/SegmentedControl.svelte +70 -0
  165. package/dist/components/SegmentedControl/SegmentedControl.svelte.d.ts +25 -0
  166. package/dist/components/SegmentedControl/index.d.ts +2 -0
  167. package/dist/components/SegmentedControl/index.js +1 -0
  168. package/dist/components/SegmentedControl/segmented-control.css +68 -0
  169. package/dist/components/SegmentedControl/types.d.ts +60 -0
  170. package/dist/components/SegmentedControl/types.js +1 -0
  171. package/dist/components/Sheet/Sheet.svelte +56 -0
  172. package/dist/components/Sheet/Sheet.svelte.d.ts +4 -0
  173. package/dist/components/Sheet/index.d.ts +2 -0
  174. package/dist/components/Sheet/index.js +2 -0
  175. package/dist/components/Sheet/sheet.css +61 -0
  176. package/dist/components/Sheet/types.d.ts +51 -0
  177. package/dist/components/Sheet/types.js +1 -0
  178. package/dist/components/Skeleton/Skeleton.svelte +33 -0
  179. package/dist/components/Skeleton/Skeleton.svelte.d.ts +4 -0
  180. package/dist/components/Skeleton/index.d.ts +2 -0
  181. package/dist/components/Skeleton/index.js +1 -0
  182. package/dist/components/Skeleton/skeleton.css +70 -0
  183. package/dist/components/Skeleton/types.d.ts +25 -0
  184. package/dist/components/Skeleton/types.js +1 -0
  185. package/dist/components/Slider/Slider.svelte +153 -0
  186. package/dist/components/Slider/Slider.svelte.d.ts +4 -0
  187. package/dist/components/Slider/index.d.ts +2 -0
  188. package/dist/components/Slider/index.js +1 -0
  189. package/dist/components/Slider/slider.css +206 -0
  190. package/dist/components/Slider/types.d.ts +102 -0
  191. package/dist/components/Slider/types.js +1 -0
  192. package/dist/components/Switch/Switch.svelte +94 -0
  193. package/dist/components/Switch/Switch.svelte.d.ts +4 -0
  194. package/dist/components/Switch/index.d.ts +2 -0
  195. package/dist/components/Switch/index.js +1 -0
  196. package/dist/components/Switch/switch.css +118 -0
  197. package/dist/components/Switch/types.d.ts +67 -0
  198. package/dist/components/Switch/types.js +1 -0
  199. package/dist/components/Tabs/Tabs.svelte +93 -0
  200. package/dist/components/Tabs/Tabs.svelte.d.ts +25 -0
  201. package/dist/components/Tabs/index.d.ts +2 -0
  202. package/dist/components/Tabs/index.js +1 -0
  203. package/dist/components/Tabs/tabs.css +73 -0
  204. package/dist/components/Tabs/types.d.ts +59 -0
  205. package/dist/components/Tabs/types.js +1 -0
  206. package/dist/components/TextField/TextField.svelte +115 -0
  207. package/dist/components/TextField/TextField.svelte.d.ts +4 -0
  208. package/dist/components/TextField/index.d.ts +2 -0
  209. package/dist/components/TextField/index.js +1 -0
  210. package/dist/components/TextField/types.d.ts +57 -0
  211. package/dist/components/TextField/types.js +1 -0
  212. package/dist/components/Textarea/Textarea.svelte +116 -0
  213. package/dist/components/Textarea/Textarea.svelte.d.ts +4 -0
  214. package/dist/components/Textarea/index.d.ts +2 -0
  215. package/dist/components/Textarea/index.js +1 -0
  216. package/dist/components/Textarea/textarea.css +35 -0
  217. package/dist/components/Textarea/types.d.ts +60 -0
  218. package/dist/components/Textarea/types.js +1 -0
  219. package/dist/components/Tooltip/Tooltip.svelte +161 -0
  220. package/dist/components/Tooltip/Tooltip.svelte.d.ts +4 -0
  221. package/dist/components/Tooltip/index.d.ts +2 -0
  222. package/dist/components/Tooltip/index.js +1 -0
  223. package/dist/components/Tooltip/tooltip.css +44 -0
  224. package/dist/components/Tooltip/types.d.ts +55 -0
  225. package/dist/components/Tooltip/types.js +1 -0
  226. package/dist/core/defaults/DefaultsProvider.svelte +30 -0
  227. package/dist/core/defaults/DefaultsProvider.svelte.d.ts +13 -0
  228. package/dist/core/defaults/context.d.ts +45 -0
  229. package/dist/core/defaults/context.js +38 -0
  230. package/dist/core/display/display.d.ts +39 -0
  231. package/dist/core/display/display.js +53 -0
  232. package/dist/core/errors/codes.d.ts +124 -0
  233. package/dist/core/errors/codes.js +108 -0
  234. package/dist/core/errors/errors.d.ts +8 -0
  235. package/dist/core/errors/errors.js +7 -0
  236. package/dist/core/form/constraints.d.ts +15 -0
  237. package/dist/core/form/constraints.js +42 -0
  238. package/dist/core/form/context.d.ts +19 -0
  239. package/dist/core/form/context.js +4 -0
  240. package/dist/core/form/field.svelte.d.ts +34 -0
  241. package/dist/core/form/field.svelte.js +66 -0
  242. package/dist/core/form/number.d.ts +24 -0
  243. package/dist/core/form/number.js +71 -0
  244. package/dist/core/form/types.d.ts +22 -0
  245. package/dist/core/form/types.js +1 -0
  246. package/dist/core/locale/locale.d.ts +56 -0
  247. package/dist/core/locale/locale.js +82 -0
  248. package/dist/core/overlay/types.d.ts +33 -0
  249. package/dist/core/overlay/types.js +1 -0
  250. package/dist/core/props/classes.d.ts +21 -0
  251. package/dist/core/props/classes.js +44 -0
  252. package/dist/core/props/element.d.ts +18 -0
  253. package/dist/core/props/element.js +1 -0
  254. package/dist/core/props/types.d.ts +47 -0
  255. package/dist/core/props/types.js +16 -0
  256. package/dist/core/provider/UIProvider.svelte +63 -0
  257. package/dist/core/provider/UIProvider.svelte.d.ts +29 -0
  258. package/dist/core/theme/ThemeProvider.svelte +178 -0
  259. package/dist/core/theme/ThemeProvider.svelte.d.ts +25 -0
  260. package/dist/core/theme/color.d.ts +23 -0
  261. package/dist/core/theme/color.js +54 -0
  262. package/dist/core/theme/context.d.ts +25 -0
  263. package/dist/core/theme/context.js +14 -0
  264. package/dist/core/theme/css.d.ts +25 -0
  265. package/dist/core/theme/css.js +73 -0
  266. package/dist/core/theme/define.d.ts +11 -0
  267. package/dist/core/theme/define.js +21 -0
  268. package/dist/core/theme/resolve.d.ts +6 -0
  269. package/dist/core/theme/resolve.js +18 -0
  270. package/dist/core/theme/theme-provider.css +13 -0
  271. package/dist/core/theme/types.d.ts +35 -0
  272. package/dist/core/theme/types.js +61 -0
  273. package/dist/core/toast/ToastOutlet.svelte +192 -0
  274. package/dist/core/toast/ToastOutlet.svelte.d.ts +11 -0
  275. package/dist/core/toast/ToastProvider.svelte +39 -0
  276. package/dist/core/toast/ToastProvider.svelte.d.ts +19 -0
  277. package/dist/core/toast/toast.css +134 -0
  278. package/dist/core/toast/toast.svelte.d.ts +117 -0
  279. package/dist/core/toast/toast.svelte.js +133 -0
  280. package/dist/dev/off.d.ts +5 -0
  281. package/dist/dev/off.js +4 -0
  282. package/dist/dev/on.d.ts +20 -0
  283. package/dist/dev/on.js +49 -0
  284. package/dist/index.d.ts +67 -0
  285. package/dist/index.js +83 -0
  286. package/dist/primitives/anchored/anchored.d.ts +39 -0
  287. package/dist/primitives/anchored/anchored.js +104 -0
  288. package/dist/primitives/anchored/fallback.d.ts +31 -0
  289. package/dist/primitives/anchored/fallback.js +73 -0
  290. package/dist/primitives/focus/focus-trap.d.ts +25 -0
  291. package/dist/primitives/focus/focus-trap.js +86 -0
  292. package/dist/primitives/focus/roving-focus.d.ts +22 -0
  293. package/dist/primitives/focus/roving-focus.js +86 -0
  294. package/dist/primitives/focus/tabbable.d.ts +9 -0
  295. package/dist/primitives/focus/tabbable.js +75 -0
  296. package/dist/primitives/inert/inert-others.d.ts +12 -0
  297. package/dist/primitives/inert/inert-others.js +44 -0
  298. package/dist/primitives/keyboard/keymap.d.ts +17 -0
  299. package/dist/primitives/keyboard/keymap.js +39 -0
  300. package/dist/primitives/keyboard/typeahead.d.ts +18 -0
  301. package/dist/primitives/keyboard/typeahead.js +50 -0
  302. package/dist/primitives/layer/dismissable.d.ts +32 -0
  303. package/dist/primitives/layer/dismissable.js +91 -0
  304. package/dist/primitives/motion/easing.d.ts +9 -0
  305. package/dist/primitives/motion/easing.js +46 -0
  306. package/dist/primitives/motion/motion.d.ts +59 -0
  307. package/dist/primitives/motion/motion.js +80 -0
  308. package/dist/primitives/portal/Portal.svelte +37 -0
  309. package/dist/primitives/portal/Portal.svelte.d.ts +9 -0
  310. package/dist/primitives/portal/PortalContent.svelte +15 -0
  311. package/dist/primitives/portal/PortalContent.svelte.d.ts +9 -0
  312. package/dist/primitives/portal/portal.css +8 -0
  313. package/dist/primitives/scroll-lock/scroll-lock.d.ts +10 -0
  314. package/dist/primitives/scroll-lock/scroll-lock.js +33 -0
  315. package/dist/primitives/virtual/virtual-list.svelte.d.ts +65 -0
  316. package/dist/primitives/virtual/virtual-list.svelte.js +291 -0
  317. package/dist/styles/base.css +44 -0
  318. package/dist/styles/props.css +140 -0
  319. package/dist/styles/tokens.css +84 -0
  320. package/dist/styles/visually-hidden.css +16 -0
  321. package/dist/themes/aurora.d.ts +13 -0
  322. package/dist/themes/aurora.js +103 -0
  323. package/dist/themes/heritage.d.ts +13 -0
  324. package/dist/themes/heritage.js +102 -0
  325. package/dist/themes/maison.d.ts +13 -0
  326. package/dist/themes/maison.js +99 -0
  327. package/dist/themes/milano.d.ts +13 -0
  328. package/dist/themes/milano.js +101 -0
  329. package/dist/themes/monolith.d.ts +13 -0
  330. package/dist/themes/monolith.js +101 -0
  331. package/package.json +87 -5
@@ -0,0 +1,153 @@
1
+ <script lang="ts">
2
+ // A number picked along a track, with its label, its value and validation. See SliderProps.
3
+ import { untrack } from 'svelte';
4
+ import { DEV, warnUnnamed } from '#dev';
5
+ import { useDefaults } from '../../core/defaults/context.js';
6
+ import { createField } from '../../core/form/field.svelte.js';
7
+ import { useLocale } from '../../core/locale/locale.js';
8
+ import { sharedClasses } from '../../core/props/classes.js';
9
+ import './slider.css';
10
+ import type { SliderDefaults, SliderProps } from './types.js';
11
+
12
+ let {
13
+ color,
14
+ size,
15
+ density,
16
+ disabled,
17
+ value = $bindable(),
18
+ min = 0,
19
+ max = 100,
20
+ step = 1,
21
+ ticks,
22
+ format,
23
+ label,
24
+ hint,
25
+ error,
26
+ rules,
27
+ validateOn,
28
+ id: idProp,
29
+ 'aria-describedby': describedBy,
30
+ class: className,
31
+ style,
32
+ ref = $bindable(null),
33
+ ...rest
34
+ }: SliderProps = $props();
35
+
36
+ const uid = $props.id();
37
+ const id = $derived(idProp ?? uid);
38
+ const resolve = useDefaults<SliderDefaults>('Slider', {
39
+ color: 'primary',
40
+ size: 'md',
41
+ density: 'default',
42
+ disabled: false
43
+ });
44
+ const p = $derived(resolve({ color, size, density, disabled }));
45
+ const locale = useLocale();
46
+
47
+ const current = $derived(value ?? min);
48
+ // A form reset goes back to the value the slider started at.
49
+ const initial = untrack(() => value ?? min);
50
+ // Where a value sits along the track, from 0 to 1.
51
+ const along = (at: number) =>
52
+ max > min ? Math.min(1, Math.max(0, (at - min) / (max - min))) : 0;
53
+
54
+ // Steps in decimals add up with float errors (0.1 + 0.2), so tick values are rounded to the
55
+ // decimals of min and step, the same values the input stops at.
56
+ const decimals = (at: number) => `${at}`.split('.')[1]?.length ?? 0;
57
+ const marks = $derived.by((): Array<{ value: number; label?: string }> => {
58
+ if (!ticks) return [];
59
+ if (ticks !== true) {
60
+ return ticks.map((tick) => (typeof tick === 'number' ? { value: tick } : tick));
61
+ }
62
+ if (!(step > 0) || max <= min) return [];
63
+ const places = Math.max(decimals(min), decimals(step));
64
+ const count = Math.floor((max - min) / step + 1e-9);
65
+ return Array.from({ length: count + 1 }, (_, index) => ({
66
+ value: Number((min + index * step).toFixed(places))
67
+ }));
68
+ });
69
+
70
+ const tickLabel = $derived(marks.find((mark) => mark.value === current && mark.label)?.label);
71
+ const text = $derived(format?.(current) ?? tickLabel ?? locale.formatNumber(current));
72
+
73
+ const field = createField<number>({
74
+ value: () => current,
75
+ // A range always holds a value; the browser keeps it between min and max, on a step.
76
+ empty: () => false,
77
+ check: () => undefined,
78
+ rules: () => rules,
79
+ error: () => error,
80
+ validateOn: () => validateOn,
81
+ control: () => ref
82
+ });
83
+
84
+ const message = $derived(typeof field.error === 'string' ? field.error : undefined);
85
+
86
+ if (DEV) $effect(() => warnUnnamed('Slider', ref));
87
+
88
+ // Letting go of the thumb, or a key step, finishes an edit, so it also counts as leaving.
89
+ function committed() {
90
+ field.changed();
91
+ field.left();
92
+ }
93
+ </script>
94
+
95
+ <div
96
+ class={[
97
+ 'sv-slider',
98
+ sharedClasses(p, 'Slider'),
99
+ field.error !== false && 'sv-slider--invalid',
100
+ p.disabled && 'sv-slider--disabled',
101
+ className
102
+ ]}
103
+ {style}
104
+ style:--sv-slider-fraction={along(current)}
105
+ oninput={field.changed}
106
+ onchange={committed}
107
+ >
108
+ {#if label}
109
+ <div class="sv-slider__header">
110
+ <label class="sv-slider__label" for={id}>{label}</label>
111
+ <!-- Shown for sight; screen readers hear the slider's own value. -->
112
+ <span class="sv-slider__value" aria-hidden="true">{text}</span>
113
+ </div>
114
+ {/if}
115
+ <input
116
+ bind:this={ref}
117
+ defaultValue={initial}
118
+ {...rest}
119
+ {id}
120
+ type="range"
121
+ class="sv-slider__input"
122
+ {min}
123
+ {max}
124
+ {step}
125
+ disabled={p.disabled}
126
+ aria-describedby={[describedBy, message ? `${id}-error` : hint ? `${id}-hint` : undefined]
127
+ .filter(Boolean)
128
+ .join(' ') || undefined}
129
+ aria-invalid={field.error !== false || undefined}
130
+ aria-valuetext={format || tickLabel ? text : undefined}
131
+ bind:value={() => current, (next: number) => (value = next)}
132
+ />
133
+ {#if marks.length}
134
+ <div class="sv-slider__ticks" aria-hidden="true">
135
+ {#each marks as mark (mark.value)}
136
+ <span
137
+ class={[
138
+ 'sv-slider__tick',
139
+ mark.value <= min && 'sv-slider__tick--start',
140
+ mark.value >= max && 'sv-slider__tick--end'
141
+ ]}
142
+ style:--sv-slider-at={along(mark.value)}
143
+ >{#if mark.label}<span class="sv-slider__tick-label">{mark.label}</span>{/if}</span
144
+ >
145
+ {/each}
146
+ </div>
147
+ {/if}
148
+ {#if hint && !message}<p class="sv-slider__hint" id="{id}-hint">{hint}</p>{/if}
149
+ <!-- Announces an error that appears after the user has moved on. -->
150
+ <div aria-live="polite">
151
+ {#if message}<p class="sv-slider__error" id="{id}-error">{message}</p>{/if}
152
+ </div>
153
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { SliderProps } from './types.js';
2
+ declare const Slider: import("svelte").Component<SliderProps, {}, "ref" | "value">;
3
+ type Slider = ReturnType<typeof Slider>;
4
+ export default Slider;
@@ -0,0 +1,2 @@
1
+ export { default as Slider } from './Slider.svelte';
2
+ export type { SliderDefaults, SliderProps, SliderTick } from './types.js';
@@ -0,0 +1 @@
1
+ export { default as Slider } from './Slider.svelte';
@@ -0,0 +1,206 @@
1
+ @layer sv.tokens, sv.base, sv.components, sv.utilities;
2
+
3
+ @layer sv.components {
4
+ /*
5
+ * A label row with the value at its end, the platform's range input, ticks and a hint or an
6
+ * error. The thumb is φ⁻¹·⁵ of the control height and the track a quarter of the thumb. The
7
+ * filled part ends under the thumb's centre, which travels the track less one thumb.
8
+ */
9
+ .sv-slider {
10
+ --sv-slider-thumb: calc(var(--sv-size-height) * 0.472);
11
+ --sv-slider-track: calc(var(--sv-slider-thumb) * 0.236);
12
+ --sv-slider-fill: calc(
13
+ var(--sv-slider-thumb) / 2 + var(--sv-slider-fraction) * (100% - var(--sv-slider-thumb))
14
+ );
15
+ display: flex;
16
+ flex-direction: column;
17
+ min-inline-size: 0;
18
+ color: var(--sv-color-text);
19
+ font-family: var(--sv-font-ui);
20
+ font-size: var(--sv-size-text);
21
+ line-height: var(--sv-leading-snug);
22
+ }
23
+
24
+ .sv-slider--invalid {
25
+ --sv-color: var(--sv-color-error);
26
+ }
27
+
28
+ .sv-slider__header {
29
+ display: flex;
30
+ gap: var(--sv-space-8);
31
+ align-items: baseline;
32
+ justify-content: space-between;
33
+ }
34
+
35
+ /* The theme's label voice, as a field's label. */
36
+ .sv-slider__label {
37
+ font-size: var(--sv-label-size);
38
+ font-weight: var(--sv-weight-medium);
39
+ letter-spacing: var(--sv-label-tracking);
40
+ text-transform: var(--sv-label-transform);
41
+ }
42
+
43
+ .sv-slider__value {
44
+ font-weight: var(--sv-weight-medium);
45
+ font-variant-numeric: tabular-nums;
46
+ }
47
+
48
+ /* The input is the press target, at least 24px tall. */
49
+ .sv-slider__input {
50
+ box-sizing: border-box;
51
+ inline-size: 100%;
52
+ block-size: max(1.5rem, var(--sv-size-height) * var(--sv-density) * 0.618);
53
+ margin: 0;
54
+ padding: 0;
55
+ border-radius: var(--sv-radius-sm);
56
+ background: transparent;
57
+ cursor: pointer;
58
+ appearance: none;
59
+ }
60
+
61
+ .sv-slider__input:focus-visible {
62
+ outline-color: var(--sv-color);
63
+ }
64
+
65
+ .sv-slider__input:disabled {
66
+ cursor: not-allowed;
67
+ }
68
+
69
+ /* Chromium and Safari: the filled part is a gradient on the track. */
70
+ .sv-slider__input::-webkit-slider-runnable-track {
71
+ block-size: var(--sv-slider-track);
72
+ border-radius: var(--sv-radius-full);
73
+ background: linear-gradient(
74
+ to right,
75
+ var(--sv-color) var(--sv-slider-fill),
76
+ var(--sv-color-border-strong) var(--sv-slider-fill)
77
+ );
78
+ }
79
+
80
+ /* An attribute selector, as minifiers rewrite :dir() into language guesses. */
81
+ :where([dir='rtl']) .sv-slider__input::-webkit-slider-runnable-track {
82
+ background: linear-gradient(
83
+ to left,
84
+ var(--sv-color) var(--sv-slider-fill),
85
+ var(--sv-color-border-strong) var(--sv-slider-fill)
86
+ );
87
+ }
88
+
89
+ .sv-slider__input::-webkit-slider-thumb {
90
+ box-sizing: border-box;
91
+ inline-size: var(--sv-slider-thumb);
92
+ block-size: var(--sv-slider-thumb);
93
+ margin-block-start: calc((var(--sv-slider-track) - var(--sv-slider-thumb)) / 2);
94
+ border: calc(var(--sv-hairline) * 2) solid var(--sv-color);
95
+ border-radius: var(--sv-radius-full);
96
+ background: var(--sv-color-surface);
97
+ appearance: none;
98
+ }
99
+
100
+ /* Firefox draws the filled part itself. */
101
+ .sv-slider__input::-moz-range-track {
102
+ block-size: var(--sv-slider-track);
103
+ border-radius: var(--sv-radius-full);
104
+ background: var(--sv-color-border-strong);
105
+ }
106
+
107
+ .sv-slider__input::-moz-range-progress {
108
+ block-size: var(--sv-slider-track);
109
+ border-radius: var(--sv-radius-full);
110
+ background: var(--sv-color);
111
+ }
112
+
113
+ .sv-slider__input::-moz-range-thumb {
114
+ box-sizing: border-box;
115
+ inline-size: var(--sv-slider-thumb);
116
+ block-size: var(--sv-slider-thumb);
117
+ border: calc(var(--sv-hairline) * 2) solid var(--sv-color);
118
+ border-radius: var(--sv-radius-full);
119
+ background: var(--sv-color-surface);
120
+ }
121
+
122
+ /* Hover and press: a halo in the accent around the thumb. */
123
+ @media (hover: hover) {
124
+ .sv-slider__input:enabled:hover::-webkit-slider-thumb {
125
+ box-shadow: 0 0 0 calc(var(--sv-slider-thumb) * 0.382)
126
+ color-mix(in oklch, var(--sv-color) var(--sv-state-hover), transparent);
127
+ }
128
+
129
+ .sv-slider__input:enabled:hover::-moz-range-thumb {
130
+ box-shadow: 0 0 0 calc(var(--sv-slider-thumb) * 0.382)
131
+ color-mix(in oklch, var(--sv-color) var(--sv-state-hover), transparent);
132
+ }
133
+ }
134
+
135
+ .sv-slider__input:enabled:active::-webkit-slider-thumb {
136
+ box-shadow: 0 0 0 calc(var(--sv-slider-thumb) * 0.382)
137
+ color-mix(in oklch, var(--sv-color) var(--sv-state-press), transparent);
138
+ }
139
+
140
+ .sv-slider__input:enabled:active::-moz-range-thumb {
141
+ box-shadow: 0 0 0 calc(var(--sv-slider-thumb) * 0.382)
142
+ color-mix(in oklch, var(--sv-color) var(--sv-state-press), transparent);
143
+ }
144
+
145
+ /*
146
+ * Ticks share one grid cell, each pushed along by the same distance as the thumb's centre.
147
+ * They are as wide as nothing, so a label centres on its tick; the end labels lean inwards.
148
+ */
149
+ .sv-slider__ticks {
150
+ display: grid;
151
+ }
152
+
153
+ .sv-slider__tick {
154
+ display: flex;
155
+ flex-direction: column;
156
+ grid-area: 1 / 1;
157
+ align-items: center;
158
+ inline-size: 0;
159
+ margin-inline-start: calc(
160
+ var(--sv-slider-thumb) / 2 + var(--sv-slider-at) * (100% - var(--sv-slider-thumb))
161
+ );
162
+ }
163
+
164
+ .sv-slider__tick--start {
165
+ align-items: flex-start;
166
+ }
167
+
168
+ .sv-slider__tick--end {
169
+ align-items: flex-end;
170
+ }
171
+
172
+ .sv-slider__tick::before {
173
+ content: '';
174
+ inline-size: var(--sv-hairline);
175
+ block-size: 0.382em;
176
+ background-color: var(--sv-color-border-strong);
177
+ }
178
+
179
+ .sv-slider__tick-label {
180
+ margin-block-start: var(--sv-space-3);
181
+ color: var(--sv-color-text-muted);
182
+ font-size: var(--sv-text-xs);
183
+ white-space: nowrap;
184
+ }
185
+
186
+ /* Padding, not margin: an app's paragraph reset (p { margin: 0 }) would win over a layer. */
187
+ .sv-slider__hint,
188
+ .sv-slider__error {
189
+ margin: 0;
190
+ padding-block-start: var(--sv-space-5);
191
+ font-size: var(--sv-text-xs);
192
+ }
193
+
194
+ .sv-slider__hint {
195
+ color: var(--sv-color-text-muted);
196
+ }
197
+
198
+ .sv-slider__error {
199
+ color: var(--sv-color-error);
200
+ }
201
+
202
+ /* The hint stays legible when the slider is off: it often says why. */
203
+ .sv-slider--disabled > :is(.sv-slider__header, .sv-slider__input, .sv-slider__ticks) {
204
+ opacity: 0.382;
205
+ }
206
+ }
@@ -0,0 +1,102 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import type { FieldError, Rule, ValidateOn } from '../../core/form/types.js';
3
+ import type { ElementProps } from '../../core/props/element.js';
4
+ import type { Color, Density, Size } from '../../core/props/types.js';
5
+ /** The Slider props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
6
+ export interface SliderDefaults {
7
+ /**
8
+ * The colour of the filled part of the track and of the thumb's ring. An invalid slider uses
9
+ * `error`.
10
+ * @default 'primary'
11
+ */
12
+ color: Color;
13
+ /**
14
+ * The size of the thumb, the track and the text.
15
+ * @default 'md'
16
+ */
17
+ size: Size;
18
+ /**
19
+ * Multiplies the height of the slider's row, never the text.
20
+ * @default 'default'
21
+ */
22
+ density: Density;
23
+ /**
24
+ * Mutes the slider and disables its input.
25
+ * @default false
26
+ */
27
+ disabled: boolean;
28
+ }
29
+ declare module '../../core/defaults/context.js' {
30
+ interface ComponentDefaults {
31
+ Slider: SliderDefaults;
32
+ }
33
+ }
34
+ /** A labelled tick under a Slider's track. */
35
+ export interface SliderTick {
36
+ value: number;
37
+ /** What the tick says. Read out as the slider's value while it sits on the tick. */
38
+ label: string;
39
+ }
40
+ type SliderOwnProps = Partial<SliderDefaults> & {
41
+ /**
42
+ * The value, between `min` and `max`, on a step. Bindable.
43
+ * @default min
44
+ */
45
+ value?: number;
46
+ /** @default 0 */
47
+ min?: number;
48
+ /** @default 100 */
49
+ max?: number;
50
+ /**
51
+ * The distance between the values the slider stops at.
52
+ * @default 1
53
+ */
54
+ step?: number;
55
+ /**
56
+ * Marks under the track: `true` for every step, a list of values, or values with labels such
57
+ * as `{ value: 50, label: 'Balanced' }`.
58
+ */
59
+ ticks?: boolean | readonly number[] | readonly SliderTick[];
60
+ /**
61
+ * How the value reads, beside the label and to screen readers, for example
62
+ * `(value) => value + '%'`. Defaults to the number in the locale's format, or the label of the
63
+ * tick the slider sits on.
64
+ */
65
+ format?: (value: number) => string;
66
+ /** The visible label, linked to the input. Without it, name the slider with `aria-label`. */
67
+ label?: string;
68
+ /** Help under the slider. An error replaces it. */
69
+ hint?: string;
70
+ /**
71
+ * An error from outside the slider, such as the server's answer: a message, a list of which
72
+ * the first is shown, or `true` for an invalid state described elsewhere.
73
+ */
74
+ error?: FieldError;
75
+ /** Checks run in order on the value. The first message is shown. */
76
+ rules?: readonly Rule<number>[];
77
+ /** When the message shows. Defaults to the form's setting, else `'blur'`. */
78
+ validateOn?: ValidateOn;
79
+ /** Inline style of the slider's root element. */
80
+ style?: string | null;
81
+ };
82
+ /**
83
+ * A number picked by dragging a thumb along a track: the platform's own range input, so the
84
+ * keyboard, touch, forms and right-to-left pages work as they always do. The value shows beside
85
+ * the label. A range always holds a value, so it takes no `required`. `class` and `style` apply
86
+ * to the root; `id`, `ref`, `name` and every other attribute go to the `<input type="range">`.
87
+ *
88
+ * @example
89
+ * <Slider
90
+ * label="Risk"
91
+ * name="risk"
92
+ * step={25}
93
+ * ticks={[
94
+ * { value: 0, label: 'Careful' },
95
+ * { value: 50, label: 'Balanced' },
96
+ * { value: 100, label: 'Bold' }
97
+ * ]}
98
+ * bind:value={risk}
99
+ * />
100
+ */
101
+ export type SliderProps = ElementProps<HTMLInputElement, Omit<HTMLInputAttributes, 'type' | 'children' | 'required'>, SliderOwnProps>;
102
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ // A setting that is on or off, with its label beside it and validation. See SwitchProps.
3
+ import { untrack } from 'svelte';
4
+ import { DEV, warnUnnamed } from '#dev';
5
+ import { useDefaults } from '../../core/defaults/context.js';
6
+ import { createField } from '../../core/form/field.svelte.js';
7
+ import { useLocale } from '../../core/locale/locale.js';
8
+ import { sharedClasses } from '../../core/props/classes.js';
9
+ import Choice from '../Choice/Choice.svelte';
10
+ import './switch.css';
11
+ import type { SwitchDefaults, SwitchProps } from './types.js';
12
+
13
+ let {
14
+ color,
15
+ size,
16
+ density,
17
+ disabled,
18
+ checked = $bindable(false),
19
+ label,
20
+ hint,
21
+ error,
22
+ rules,
23
+ validateOn,
24
+ required = false,
25
+ id,
26
+ 'aria-describedby': describedBy,
27
+ class: className,
28
+ style,
29
+ ref = $bindable(null),
30
+ ...rest
31
+ }: SwitchProps = $props();
32
+
33
+ const resolve = useDefaults<SwitchDefaults>('Switch', {
34
+ color: 'primary',
35
+ size: 'md',
36
+ density: 'default',
37
+ disabled: false
38
+ });
39
+ const p = $derived(resolve({ color, size, density, disabled }));
40
+ const locale = useLocale();
41
+ // A form reset goes back to the state the switch started in.
42
+ const initial = untrack(() => checked ?? false);
43
+
44
+ const field = createField<boolean>({
45
+ value: () => checked ?? false,
46
+ // Rules run on a switch that is on only, as they run on a filled field only.
47
+ empty: (on) => !on,
48
+ check: (on) => (!on && required ? locale.t('required') : undefined),
49
+ rules: () => rules,
50
+ error: () => error,
51
+ validateOn: () => validateOn,
52
+ control: () => ref
53
+ });
54
+
55
+ if (DEV) $effect(() => warnUnnamed('Switch', ref));
56
+
57
+ // A toggle is a whole edit, so it also counts as leaving: the message shows at once, even in
58
+ // Safari, where a click does not focus the input.
59
+ function toggled() {
60
+ field.changed();
61
+ field.left();
62
+ }
63
+ </script>
64
+
65
+ <Choice
66
+ {id}
67
+ {label}
68
+ {hint}
69
+ error={field.error}
70
+ {required}
71
+ disabled={p.disabled}
72
+ class={['sv-switch', sharedClasses(p, 'Switch'), className]}
73
+ {style}
74
+ onchange={toggled}
75
+ >
76
+ {#snippet children(attributes)}
77
+ <span class="sv-switch__control">
78
+ <input
79
+ bind:this={ref}
80
+ defaultChecked={initial}
81
+ {...rest}
82
+ {...attributes}
83
+ aria-describedby={[describedBy, attributes['aria-describedby']].filter(Boolean).join(' ') ||
84
+ undefined}
85
+ type="checkbox"
86
+ role="switch"
87
+ class="sv-switch__input"
88
+ bind:checked
89
+ />
90
+ <span class="sv-switch__track" aria-hidden="true"><span class="sv-switch__thumb"></span></span
91
+ >
92
+ </span>
93
+ {/snippet}
94
+ </Choice>
@@ -0,0 +1,4 @@
1
+ import type { SwitchProps } from './types.js';
2
+ declare const Switch: import("svelte").Component<SwitchProps, {}, "ref" | "checked">;
3
+ type Switch = ReturnType<typeof Switch>;
4
+ export default Switch;
@@ -0,0 +1,2 @@
1
+ export { default as Switch } from './Switch.svelte';
2
+ export type { SwitchDefaults, SwitchProps } from './types.js';
@@ -0,0 +1 @@
1
+ export { default as Switch } from './Switch.svelte';