@noahbaumgartner/svelte-ui 0.0.4 → 0.0.6

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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -13
  3. package/dist/components/accordion/Accordion.svelte +44 -0
  4. package/dist/components/accordion/Accordion.svelte.d.ts +13 -0
  5. package/dist/components/accordion/AccordionItem.svelte +145 -0
  6. package/dist/components/accordion/AccordionItem.svelte.d.ts +17 -0
  7. package/dist/components/accordion/context.d.ts +5 -0
  8. package/dist/components/accordion/context.js +2 -0
  9. package/dist/components/alert-dialog/AlertDialog.svelte +108 -76
  10. package/dist/components/alert-dialog/AlertDialog.svelte.d.ts +5 -2
  11. package/dist/components/avatar/Avatar.svelte +71 -49
  12. package/dist/components/avatar/Avatar.svelte.d.ts +5 -2
  13. package/dist/components/avatar-group/AvatarGroup.svelte +103 -0
  14. package/dist/components/avatar-group/AvatarGroup.svelte.d.ts +19 -0
  15. package/dist/components/badge/Badge.svelte +50 -28
  16. package/dist/components/badge/Badge.svelte.d.ts +6 -4
  17. package/dist/components/breadcrumb/Breadcrumb.svelte +21 -17
  18. package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +3 -1
  19. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte +17 -6
  20. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte.d.ts +5 -2
  21. package/dist/components/breadcrumb/BreadcrumbItem.svelte +75 -36
  22. package/dist/components/breadcrumb/BreadcrumbItem.svelte.d.ts +8 -5
  23. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte +32 -9
  24. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte.d.ts +10 -17
  25. package/dist/components/button/Button.svelte +206 -168
  26. package/dist/components/button/Button.svelte.d.ts +9 -5
  27. package/dist/components/button-group/ButtonGroup.svelte +52 -55
  28. package/dist/components/button-group/ButtonGroup.svelte.d.ts +3 -1
  29. package/dist/components/calendar/Calendar.svelte +232 -144
  30. package/dist/components/calendar/Calendar.svelte.d.ts +19 -2
  31. package/dist/components/calendar/dates.d.ts +4 -1
  32. package/dist/components/calendar/dates.js +8 -2
  33. package/dist/components/card/Card.svelte +48 -32
  34. package/dist/components/card/Card.svelte.d.ts +3 -1
  35. package/dist/components/card/CardAction.svelte +11 -7
  36. package/dist/components/card/CardAction.svelte.d.ts +3 -1
  37. package/dist/components/card/CardContent.svelte +8 -4
  38. package/dist/components/card/CardContent.svelte.d.ts +3 -1
  39. package/dist/components/card/CardDescription.svelte +11 -7
  40. package/dist/components/card/CardDescription.svelte.d.ts +3 -1
  41. package/dist/components/card/CardFooter.svelte +15 -11
  42. package/dist/components/card/CardFooter.svelte.d.ts +3 -1
  43. package/dist/components/card/CardHeader.svelte +15 -11
  44. package/dist/components/card/CardHeader.svelte.d.ts +3 -1
  45. package/dist/components/card/CardTitle.svelte +11 -7
  46. package/dist/components/card/CardTitle.svelte.d.ts +3 -1
  47. package/dist/components/checkbox/Checkbox.svelte +152 -61
  48. package/dist/components/checkbox/Checkbox.svelte.d.ts +5 -2
  49. package/dist/components/color-picker/ColorPicker.svelte +131 -78
  50. package/dist/components/color-picker/ColorPicker.svelte.d.ts +10 -1
  51. package/dist/components/context-menu/ContextMenu.svelte +78 -61
  52. package/dist/components/context-menu/ContextMenu.svelte.d.ts +5 -2
  53. package/dist/components/context-menu/ContextMenuInfo.svelte +91 -0
  54. package/dist/components/context-menu/ContextMenuInfo.svelte.d.ts +19 -0
  55. package/dist/components/context-menu/ContextMenuItem.svelte +102 -57
  56. package/dist/components/context-menu/ContextMenuItem.svelte.d.ts +10 -5
  57. package/dist/components/context-menu/ContextMenuLabel.svelte +28 -0
  58. package/dist/components/context-menu/ContextMenuLabel.svelte.d.ts +10 -0
  59. package/dist/components/context-menu/ContextMenuSeparator.svelte +29 -0
  60. package/dist/components/context-menu/ContextMenuSeparator.svelte.d.ts +8 -0
  61. package/dist/components/dialog/Dialog.svelte +149 -108
  62. package/dist/components/dialog/Dialog.svelte.d.ts +7 -2
  63. package/dist/components/drawer/Drawer.svelte +419 -0
  64. package/dist/components/drawer/Drawer.svelte.d.ts +28 -0
  65. package/dist/components/empty-state/EmptyState.svelte +106 -0
  66. package/dist/components/empty-state/EmptyState.svelte.d.ts +19 -0
  67. package/dist/components/entry/Entry.svelte +94 -0
  68. package/dist/components/entry/Entry.svelte.d.ts +16 -0
  69. package/dist/components/field/Field.svelte +66 -45
  70. package/dist/components/field/Field.svelte.d.ts +2 -1
  71. package/dist/components/field/FieldContent.svelte +11 -8
  72. package/dist/components/field/FieldContent.svelte.d.ts +2 -1
  73. package/dist/components/field/FieldDescription.svelte +18 -15
  74. package/dist/components/field/FieldDescription.svelte.d.ts +2 -1
  75. package/dist/components/field/FieldError.svelte +22 -13
  76. package/dist/components/field/FieldError.svelte.d.ts +2 -1
  77. package/dist/components/field/FieldGroup.svelte +15 -12
  78. package/dist/components/field/FieldGroup.svelte.d.ts +2 -1
  79. package/dist/components/field/FieldLegend.svelte +23 -14
  80. package/dist/components/field/FieldLegend.svelte.d.ts +2 -1
  81. package/dist/components/field/FieldSeparator.svelte +12 -9
  82. package/dist/components/field/FieldSeparator.svelte.d.ts +2 -1
  83. package/dist/components/field/FieldSet.svelte +18 -15
  84. package/dist/components/field/FieldSet.svelte.d.ts +2 -1
  85. package/dist/components/footer/Footer.svelte +105 -0
  86. package/dist/components/footer/Footer.svelte.d.ts +14 -0
  87. package/dist/components/footer/FooterColumn.svelte +37 -0
  88. package/dist/components/footer/FooterColumn.svelte.d.ts +11 -0
  89. package/dist/components/image/Image.svelte +61 -0
  90. package/dist/components/image/Image.svelte.d.ts +18 -0
  91. package/dist/components/input/Input.svelte +307 -208
  92. package/dist/components/input/Input.svelte.d.ts +12 -1
  93. package/dist/components/input-group/InputGroup.svelte +101 -61
  94. package/dist/components/input-group/InputGroup.svelte.d.ts +2 -1
  95. package/dist/components/input-group/InputGroupAddon.svelte +58 -49
  96. package/dist/components/kbd/Kbd.svelte +39 -30
  97. package/dist/components/kbd/Kbd.svelte.d.ts +2 -1
  98. package/dist/components/kbd/KbdGroup.svelte +15 -12
  99. package/dist/components/kbd/KbdGroup.svelte.d.ts +2 -1
  100. package/dist/components/label/Label.svelte +47 -28
  101. package/dist/components/label/Label.svelte.d.ts +2 -1
  102. package/dist/components/link/Link.svelte +71 -17
  103. package/dist/components/link/Link.svelte.d.ts +2 -1
  104. package/dist/components/mesh-gradient/MeshGradient.svelte +97 -0
  105. package/dist/components/mesh-gradient/MeshGradient.svelte.d.ts +11 -0
  106. package/dist/components/navbar/Navbar.svelte +137 -99
  107. package/dist/components/navbar/Navbar.svelte.d.ts +4 -2
  108. package/dist/components/navigation-menu/NavigationMenu.svelte +37 -20
  109. package/dist/components/navigation-menu/NavigationMenu.svelte.d.ts +4 -2
  110. package/dist/components/navigation-menu/NavigationMenuItem.svelte +131 -89
  111. package/dist/components/navigation-menu/NavigationMenuItem.svelte.d.ts +4 -2
  112. package/dist/components/navigation-menu/NavigationMenuLink.svelte +90 -51
  113. package/dist/components/navigation-menu/NavigationMenuLink.svelte.d.ts +7 -5
  114. package/dist/components/number-stepper/NumberStepper.svelte +101 -0
  115. package/dist/components/number-stepper/NumberStepper.svelte.d.ts +21 -0
  116. package/dist/components/popover/Popover.svelte +85 -65
  117. package/dist/components/popover/Popover.svelte.d.ts +5 -2
  118. package/dist/components/preview-card/PreviewCard.svelte +184 -0
  119. package/dist/components/preview-card/PreviewCard.svelte.d.ts +23 -0
  120. package/dist/components/progress/Progress.svelte +111 -0
  121. package/dist/components/progress/Progress.svelte.d.ts +13 -0
  122. package/dist/components/radio-group/RadioGroup.svelte +24 -19
  123. package/dist/components/radio-group/RadioGroup.svelte.d.ts +2 -1
  124. package/dist/components/radio-group/RadioGroupItem.svelte +101 -54
  125. package/dist/components/radio-group/RadioGroupItem.svelte.d.ts +2 -1
  126. package/dist/components/section/Section.svelte +114 -0
  127. package/dist/components/section/Section.svelte.d.ts +22 -0
  128. package/dist/components/select/Select.svelte +474 -0
  129. package/dist/components/select/Select.svelte.d.ts +48 -0
  130. package/dist/components/separator/Separator.svelte +21 -10
  131. package/dist/components/separator/Separator.svelte.d.ts +2 -1
  132. package/dist/components/sidebar/Sidebar.svelte +158 -87
  133. package/dist/components/sidebar/Sidebar.svelte.d.ts +6 -1
  134. package/dist/components/sidebar/SidebarContent.svelte +15 -12
  135. package/dist/components/sidebar/SidebarContent.svelte.d.ts +2 -1
  136. package/dist/components/sidebar/SidebarFooter.svelte +10 -7
  137. package/dist/components/sidebar/SidebarFooter.svelte.d.ts +2 -1
  138. package/dist/components/sidebar/SidebarGroup.svelte +35 -25
  139. package/dist/components/sidebar/SidebarGroup.svelte.d.ts +2 -1
  140. package/dist/components/sidebar/SidebarHeader.svelte +37 -8
  141. package/dist/components/sidebar/SidebarHeader.svelte.d.ts +6 -1
  142. package/dist/components/sidebar/SidebarInset.svelte +11 -8
  143. package/dist/components/sidebar/SidebarInset.svelte.d.ts +2 -1
  144. package/dist/components/sidebar/SidebarMenu.svelte +13 -10
  145. package/dist/components/sidebar/SidebarMenu.svelte.d.ts +2 -1
  146. package/dist/components/sidebar/SidebarMenuItem.svelte +195 -114
  147. package/dist/components/sidebar/SidebarMenuItem.svelte.d.ts +11 -5
  148. package/dist/components/sidebar/SidebarProvider.svelte +18 -9
  149. package/dist/components/sidebar/SidebarProvider.svelte.d.ts +2 -1
  150. package/dist/components/sidebar/SidebarSeparator.svelte +6 -1
  151. package/dist/components/sidebar/SidebarSeparator.svelte.d.ts +6 -17
  152. package/dist/components/sidebar/SidebarTrigger.svelte +17 -5
  153. package/dist/components/sidebar/SidebarTrigger.svelte.d.ts +4 -2
  154. package/dist/components/skeleton/Skeleton.svelte +26 -16
  155. package/dist/components/skeleton/Skeleton.svelte.d.ts +4 -2
  156. package/dist/components/spinner/Spinner.svelte +71 -46
  157. package/dist/components/spinner/Spinner.svelte.d.ts +2 -1
  158. package/dist/components/switch/Switch.svelte +126 -65
  159. package/dist/components/switch/Switch.svelte.d.ts +4 -2
  160. package/dist/components/table/Table.svelte +135 -76
  161. package/dist/components/table/Table.svelte.d.ts +12 -2
  162. package/dist/components/tabs/Tabs.svelte +337 -0
  163. package/dist/components/tabs/Tabs.svelte.d.ts +50 -0
  164. package/dist/components/textarea/Textarea.svelte +76 -42
  165. package/dist/components/textarea/Textarea.svelte.d.ts +4 -1
  166. package/dist/components/time-picker/TimePicker.svelte +214 -85
  167. package/dist/components/time-picker/TimePicker.svelte.d.ts +18 -1
  168. package/dist/components/toaster/Toaster.svelte +132 -70
  169. package/dist/components/toaster/Toaster.svelte.d.ts +18 -2
  170. package/dist/components/toaster/toast.svelte.d.ts +3 -3
  171. package/dist/components/toggle/Toggle.svelte +25 -4
  172. package/dist/components/toggle/Toggle.svelte.d.ts +1 -1
  173. package/dist/components/tooltip/Tooltip.svelte +87 -58
  174. package/dist/components/tooltip/Tooltip.svelte.d.ts +4 -2
  175. package/dist/icon.d.ts +12 -0
  176. package/dist/icon.js +1 -0
  177. package/dist/index.d.ts +21 -0
  178. package/dist/index.js +19 -0
  179. package/dist/internal/icons/ArrowDown.svelte +14 -0
  180. package/dist/internal/icons/ArrowDown.svelte.d.ts +4 -0
  181. package/dist/internal/icons/ArrowUp.svelte +14 -0
  182. package/dist/internal/icons/ArrowUp.svelte.d.ts +4 -0
  183. package/dist/internal/icons/Calendar.svelte +16 -0
  184. package/dist/internal/icons/Calendar.svelte.d.ts +4 -0
  185. package/dist/internal/icons/Check.svelte +8 -0
  186. package/dist/internal/icons/Check.svelte.d.ts +4 -0
  187. package/dist/internal/icons/ChevronDown.svelte +8 -0
  188. package/dist/internal/icons/ChevronDown.svelte.d.ts +4 -0
  189. package/dist/internal/icons/ChevronLeft.svelte +8 -0
  190. package/dist/internal/icons/ChevronLeft.svelte.d.ts +4 -0
  191. package/dist/internal/icons/ChevronRight.svelte +8 -0
  192. package/dist/internal/icons/ChevronRight.svelte.d.ts +4 -0
  193. package/dist/internal/icons/ChevronUp.svelte +8 -0
  194. package/dist/internal/icons/ChevronUp.svelte.d.ts +4 -0
  195. package/dist/internal/icons/ChevronsUpDown.svelte +14 -0
  196. package/dist/internal/icons/ChevronsUpDown.svelte.d.ts +4 -0
  197. package/dist/internal/icons/Clock.svelte +14 -0
  198. package/dist/internal/icons/Clock.svelte.d.ts +4 -0
  199. package/dist/internal/icons/Ellipsis.svelte +15 -0
  200. package/dist/internal/icons/Ellipsis.svelte.d.ts +4 -0
  201. package/dist/internal/icons/Icon.svelte +33 -0
  202. package/dist/internal/icons/Icon.svelte.d.ts +8 -0
  203. package/dist/internal/icons/Minus.svelte +8 -0
  204. package/dist/internal/icons/Minus.svelte.d.ts +4 -0
  205. package/dist/internal/icons/PanelLeft.svelte +14 -0
  206. package/dist/internal/icons/PanelLeft.svelte.d.ts +4 -0
  207. package/dist/internal/icons/Plus.svelte +14 -0
  208. package/dist/internal/icons/Plus.svelte.d.ts +4 -0
  209. package/dist/internal/icons/User.svelte +14 -0
  210. package/dist/internal/icons/User.svelte.d.ts +4 -0
  211. package/dist/internal/icons/X.svelte +14 -0
  212. package/dist/internal/icons/X.svelte.d.ts +4 -0
  213. package/dist/styles/tokens.css +39 -41
  214. package/package.json +5 -10
  215. package/dist/icons.d.ts +0 -2
  216. package/dist/icons.js +0 -4
@@ -1,12 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { tick, type Snippet } from 'svelte';
3
3
  import type { Attachment } from 'svelte/attachments';
4
+ import type { HTMLAttributes } from 'svelte/elements';
4
5
  import { prefersReducedMotion } from 'svelte/motion';
5
6
  import { scale, slide } from 'svelte/transition';
6
- import ChevronDown from '@lucide/svelte/icons/chevron-down';
7
+ import ChevronDown from '../../internal/icons/ChevronDown.svelte';
7
8
  import { getNavigationMenuContext } from './context.js';
8
9
 
9
- type Props = {
10
+ type Props = Omit<HTMLAttributes<HTMLLIElement>, 'children'> & {
10
11
  /** Links the item. Ignored when the item has `items`. */
11
12
  href?: string;
12
13
  /** Marks the item as the current page, or the section containing it. */
@@ -16,16 +17,25 @@
16
17
  /** Number of columns the panel lays its links out in. Ignored in a vertical menu. */
17
18
  columns?: 1 | 2 | 3;
18
19
  children: Snippet;
20
+ ref?: HTMLLIElement | null;
19
21
  };
20
22
 
21
- let { href, active = false, items, columns = 1, children }: Props = $props();
23
+ let {
24
+ href,
25
+ active = false,
26
+ items,
27
+ columns = 1,
28
+ class: className,
29
+ children,
30
+ ref = $bindable(null),
31
+ ...rest
32
+ }: Props = $props();
22
33
 
23
34
  const id = $props.id();
24
35
  const menu = getNavigationMenuContext();
25
36
  const gap = 8;
26
37
  const edge = 8;
27
38
 
28
- let itemEl: HTMLLIElement;
29
39
  let triggerEl: HTMLButtonElement | undefined = $state();
30
40
  let contentEl: HTMLElement | undefined = $state();
31
41
  let position = $state({ top: 0, left: 0 });
@@ -81,11 +91,11 @@
81
91
  if (!open || menu.vertical) return;
82
92
 
83
93
  function handlePointerDown(event: PointerEvent) {
84
- if (!itemEl.contains(event.target as Node)) close();
94
+ if (!ref?.contains(event.target as Node)) close();
85
95
  }
86
96
 
87
97
  function handleKeydown(event: KeyboardEvent) {
88
- if (event.key === 'Escape') close({ restoreFocus: itemEl.contains(document.activeElement) });
98
+ if (event.key === 'Escape') close({ restoreFocus: ref?.contains(document.activeElement) });
89
99
  }
90
100
 
91
101
  window.addEventListener('pointerdown', handlePointerDown);
@@ -103,11 +113,12 @@
103
113
  </script>
104
114
 
105
115
  <li
106
- class={['navigation-menu-item', { 'navigation-menu-item--vertical': menu.vertical }]}
107
- bind:this={itemEl}
116
+ {...rest}
117
+ class={['navigation-menu-item', { 'navigation-menu-item--vertical': menu.vertical }, className]}
118
+ bind:this={ref}
108
119
  onfocusout={(event) => {
109
- if (open && !menu.vertical && !itemEl.contains(event.relatedTarget as Node | null))
110
- menu.close();
120
+ rest.onfocusout?.(event);
121
+ if (open && !menu.vertical && !ref?.contains(event.relatedTarget as Node | null)) menu.close();
111
122
  }}
112
123
  >
113
124
  {#if items}
@@ -163,95 +174,126 @@
163
174
  </li>
164
175
 
165
176
  <style>
166
- .navigation-menu-item {
167
- display: flex;
168
- }
177
+ @layer svelte-ui {
178
+ .navigation-menu-item {
179
+ display: flex;
180
+ }
169
181
 
170
- .navigation-menu-item--vertical {
171
- flex-direction: column;
172
- }
182
+ .navigation-menu-item--vertical {
183
+ flex-direction: column;
184
+ }
173
185
 
174
- .navigation-menu-item--vertical .navigation-menu-trigger {
175
- justify-content: space-between;
176
- width: 100%;
177
- text-align: left;
178
- }
186
+ .navigation-menu-item--vertical .navigation-menu-trigger {
187
+ justify-content: space-between;
188
+ width: 100%;
189
+ text-align: left;
190
+ }
179
191
 
180
- .navigation-menu-trigger {
181
- display: inline-flex;
182
- align-items: center;
183
- gap: 4px;
184
- padding: 0;
185
- background: none;
186
- border: none;
187
- border-radius: 4px;
188
- font: inherit;
189
- font-size: 14px;
190
- line-height: 28px;
191
- color: var(--color-text);
192
- text-decoration: none;
193
- outline: none;
194
- cursor: pointer;
195
- transition: opacity 200ms;
196
- }
192
+ .navigation-menu-trigger {
193
+ display: inline-flex;
194
+ align-items: center;
195
+ gap: 4px;
196
+ padding: 0;
197
+ background: none;
198
+ border: none;
199
+ border-radius: 4px;
200
+ font: inherit;
201
+ font-size: 14px;
202
+ line-height: 28px;
203
+ color: var(--color-text);
204
+ text-decoration: none;
205
+ outline: none;
206
+ cursor: pointer;
207
+ transition: opacity 200ms;
208
+ }
197
209
 
198
- .navigation-menu-trigger:hover {
199
- opacity: 0.6;
200
- }
210
+ .navigation-menu-trigger:hover {
211
+ opacity: 0.6;
212
+ }
201
213
 
202
- .navigation-menu-trigger:focus-visible {
203
- outline: 2px solid var(--color-accent);
204
- outline-offset: 2px;
205
- }
214
+ .navigation-menu-trigger:focus-visible {
215
+ outline: 2px solid var(--color-accent);
216
+ outline-offset: 2px;
217
+ }
206
218
 
207
- .navigation-menu-trigger--active {
208
- font-weight: 500;
209
- }
219
+ .navigation-menu-trigger--active {
220
+ font-weight: 500;
221
+ }
210
222
 
211
- .navigation-menu-item :global(.navigation-menu-chevron) {
212
- width: 14px;
213
- height: 14px;
214
- flex-shrink: 0;
215
- transition: rotate 200ms ease;
216
- }
223
+ .navigation-menu-item :global(.navigation-menu-chevron) {
224
+ width: 14px;
225
+ height: 14px;
226
+ flex-shrink: 0;
227
+ transition: rotate 200ms ease;
228
+ }
217
229
 
218
- .navigation-menu-trigger[aria-expanded='true'] :global(.navigation-menu-chevron) {
219
- rotate: 180deg;
220
- }
230
+ .navigation-menu-trigger[aria-expanded='true'] :global(.navigation-menu-chevron) {
231
+ rotate: 180deg;
232
+ }
221
233
 
222
- .navigation-menu-content {
223
- position: fixed;
224
- inset: auto;
225
- box-sizing: border-box;
226
- max-width: calc(100vw - 16px);
227
- margin: 0;
228
- padding: 6px;
229
- overflow: visible;
230
- background-color: var(--color-bg);
231
- color: var(--color-text);
232
- border: 1px solid var(--color-border);
233
- border-radius: 12px;
234
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
235
- transform-origin: top left;
236
- user-select: none;
237
- }
234
+ .navigation-menu-content {
235
+ position: fixed;
236
+ inset: auto;
237
+ box-sizing: border-box;
238
+ max-width: calc(100vw - 16px);
239
+ margin: 0;
240
+ padding: 6px;
241
+ overflow: visible;
242
+ background-color: var(--color-bg);
243
+ color: var(--color-text);
244
+ border: 1px solid var(--color-border);
245
+ border-radius: 12px;
246
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
247
+ transform-origin: top left;
248
+ user-select: none;
249
+ }
238
250
 
239
- .navigation-menu-links {
240
- display: grid;
241
- grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
242
- width: min(var(--columns) * 240px, 100vw - 30px);
243
- gap: 2px;
244
- margin: 0;
245
- padding: 0;
246
- list-style: none;
247
- }
251
+ .navigation-menu-links {
252
+ display: grid;
253
+ grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
254
+ width: min(var(--columns) * 240px, 100vw - 30px);
255
+ gap: 2px;
256
+ margin: 0;
257
+ padding: 0;
258
+ list-style: none;
259
+ }
260
+
261
+ .navigation-menu-sub {
262
+ display: flex;
263
+ flex-direction: column;
264
+ gap: 2px;
265
+ margin: 4px 0 0;
266
+ padding: 0;
267
+ list-style: none;
268
+ }
248
269
 
249
- .navigation-menu-sub {
250
- display: flex;
251
- flex-direction: column;
252
- gap: 2px;
253
- margin: 4px 0 0;
254
- padding: 0;
255
- list-style: none;
270
+ @media (prefers-reduced-motion: reduce) {
271
+ .navigation-menu-trigger,
272
+ .navigation-menu-item :global(.navigation-menu-chevron) {
273
+ transition: none;
274
+ }
275
+ }
276
+
277
+ @media (forced-colors: active) {
278
+ .navigation-menu-trigger:hover {
279
+ opacity: 1;
280
+ text-decoration: underline;
281
+ }
282
+
283
+ .navigation-menu-trigger--active {
284
+ text-decoration: underline;
285
+ }
286
+
287
+ .navigation-menu-trigger:focus-visible {
288
+ outline-color: Highlight;
289
+ }
290
+ }
291
+
292
+ @media (pointer: coarse) {
293
+ .navigation-menu-trigger {
294
+ min-width: 44px;
295
+ min-height: 44px;
296
+ }
297
+ }
256
298
  }
257
299
  </style>
@@ -1,5 +1,6 @@
1
1
  import { type Snippet } from 'svelte';
2
- type Props = {
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = Omit<HTMLAttributes<HTMLLIElement>, 'children'> & {
3
4
  /** Links the item. Ignored when the item has `items`. */
4
5
  href?: string;
5
6
  /** Marks the item as the current page, or the section containing it. */
@@ -9,7 +10,8 @@ type Props = {
9
10
  /** Number of columns the panel lays its links out in. Ignored in a vertical menu. */
10
11
  columns?: 1 | 2 | 3;
11
12
  children: Snippet;
13
+ ref?: HTMLLIElement | null;
12
14
  };
13
- declare const NavigationMenuItem: import("svelte").Component<Props, {}, "">;
15
+ declare const NavigationMenuItem: import("svelte").Component<Props, {}, "ref">;
14
16
  type NavigationMenuItem = ReturnType<typeof NavigationMenuItem>;
15
17
  export default NavigationMenuItem;
@@ -1,27 +1,38 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { LucideIcon } from '@lucide/svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import type { IconComponent } from '../../icon.js';
4
5
  import { getNavigationMenuContext } from './context.js';
5
6
 
6
- type Props = {
7
+ type Props = Omit<HTMLAttributes<HTMLLIElement>, 'children'> & {
7
8
  href: string;
8
- /** Any Lucide icon (import from `svelte-ui/icons`). */
9
- icon?: LucideIcon;
9
+ /** Any icon component, e.g. from `@lucide/svelte`. */
10
+ icon?: IconComponent;
10
11
  /** Short text below the title, clamped to two lines. */
11
12
  description?: string;
12
13
  /** Marks the link as the current page. */
13
14
  active?: boolean;
14
15
  /** The link's title. */
15
16
  children: Snippet;
17
+ ref?: HTMLLIElement | null;
16
18
  };
17
19
 
18
- let { href, icon: Icon, description, active = false, children }: Props = $props();
20
+ let {
21
+ href,
22
+ icon: Icon,
23
+ description,
24
+ active = false,
25
+ class: className,
26
+ children,
27
+ ref = $bindable(null),
28
+ ...rest
29
+ }: Props = $props();
19
30
 
20
31
  const menu = getNavigationMenuContext();
21
32
  let classes = $derived(['navigation-menu-link', { 'navigation-menu-link--active': active }]);
22
33
  </script>
23
34
 
24
- <li>
35
+ <li {...rest} bind:this={ref} class={className}>
25
36
  <!-- href comes from the consumer, who resolves it; this library has no routes -->
26
37
  <!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
27
38
  <a {href} aria-current={active ? 'page' : undefined} class={classes} onclick={menu.close}>
@@ -36,56 +47,84 @@
36
47
  </li>
37
48
 
38
49
  <style>
39
- .navigation-menu-link {
40
- display: flex;
41
- align-items: flex-start;
42
- gap: 10px;
43
- padding: 8px 10px;
44
- border-radius: 8px;
45
- font-size: 13px;
46
- line-height: 1.4;
47
- color: var(--color-text);
48
- text-decoration: none;
49
- outline: none;
50
- transition: background-color 150ms ease;
51
- }
50
+ @layer svelte-ui {
51
+ .navigation-menu-link {
52
+ display: flex;
53
+ align-items: flex-start;
54
+ gap: 10px;
55
+ padding: 8px 10px;
56
+ border-radius: 8px;
57
+ font-size: 13px;
58
+ line-height: 1.4;
59
+ color: var(--color-text);
60
+ text-decoration: none;
61
+ outline: none;
62
+ transition: background-color 150ms ease;
63
+ }
52
64
 
53
- .navigation-menu-link:hover,
54
- .navigation-menu-link:focus-visible,
55
- .navigation-menu-link--active {
56
- background-color: var(--color-surface);
57
- }
65
+ .navigation-menu-link:hover,
66
+ .navigation-menu-link:focus-visible,
67
+ .navigation-menu-link--active {
68
+ background-color: var(--color-surface);
69
+ }
58
70
 
59
- .navigation-menu-link:focus-visible {
60
- outline: 2px solid var(--color-accent);
61
- outline-offset: -2px;
62
- }
71
+ .navigation-menu-link:focus-visible {
72
+ outline: 2px solid var(--color-accent);
73
+ outline-offset: -2px;
74
+ }
63
75
 
64
- .navigation-menu-link :global(.navigation-menu-link-icon) {
65
- width: 16px;
66
- height: 16px;
67
- margin-top: 1px;
68
- flex-shrink: 0;
69
- }
76
+ .navigation-menu-link :global(.navigation-menu-link-icon) {
77
+ width: 16px;
78
+ height: 16px;
79
+ margin-top: 1px;
80
+ flex-shrink: 0;
81
+ }
70
82
 
71
- .navigation-menu-link-text {
72
- display: flex;
73
- flex-direction: column;
74
- gap: 2px;
75
- min-width: 0;
76
- }
83
+ .navigation-menu-link-text {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 2px;
87
+ min-width: 0;
88
+ }
77
89
 
78
- .navigation-menu-link-title {
79
- font-weight: 500;
80
- }
90
+ .navigation-menu-link-title {
91
+ font-weight: 500;
92
+ }
93
+
94
+ .navigation-menu-link-description {
95
+ display: -webkit-box;
96
+ -webkit-box-orient: vertical;
97
+ -webkit-line-clamp: 2;
98
+ line-clamp: 2;
99
+ overflow: hidden;
100
+ font-size: 12px;
101
+ color: var(--color-text-muted);
102
+ }
103
+
104
+ @media (prefers-reduced-motion: reduce) {
105
+ .navigation-menu-link {
106
+ transition: none;
107
+ }
108
+ }
109
+
110
+ @media (forced-colors: active) {
111
+ .navigation-menu-link:hover,
112
+ .navigation-menu-link--active {
113
+ outline: 1px solid Highlight;
114
+ outline-offset: -1px;
115
+ }
116
+
117
+ .navigation-menu-link:focus-visible {
118
+ outline: 2px solid Highlight;
119
+ outline-offset: -2px;
120
+ }
121
+ }
81
122
 
82
- .navigation-menu-link-description {
83
- display: -webkit-box;
84
- -webkit-box-orient: vertical;
85
- -webkit-line-clamp: 2;
86
- line-clamp: 2;
87
- overflow: hidden;
88
- font-size: 12px;
89
- color: var(--color-text-muted);
123
+ @media (pointer: coarse) {
124
+ .navigation-menu-link {
125
+ min-height: 44px;
126
+ box-sizing: border-box;
127
+ }
128
+ }
90
129
  }
91
130
  </style>
@@ -1,16 +1,18 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { LucideIcon } from '@lucide/svelte';
3
- type Props = {
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { IconComponent } from '../../icon.js';
4
+ type Props = Omit<HTMLAttributes<HTMLLIElement>, 'children'> & {
4
5
  href: string;
5
- /** Any Lucide icon (import from `svelte-ui/icons`). */
6
- icon?: LucideIcon;
6
+ /** Any icon component, e.g. from `@lucide/svelte`. */
7
+ icon?: IconComponent;
7
8
  /** Short text below the title, clamped to two lines. */
8
9
  description?: string;
9
10
  /** Marks the link as the current page. */
10
11
  active?: boolean;
11
12
  /** The link's title. */
12
13
  children: Snippet;
14
+ ref?: HTMLLIElement | null;
13
15
  };
14
- declare const NavigationMenuLink: import("svelte").Component<Props, {}, "">;
16
+ declare const NavigationMenuLink: import("svelte").Component<Props, {}, "ref">;
15
17
  type NavigationMenuLink = ReturnType<typeof NavigationMenuLink>;
16
18
  export default NavigationMenuLink;
@@ -0,0 +1,101 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import Minus from '../../internal/icons/Minus.svelte';
4
+ import Plus from '../../internal/icons/Plus.svelte';
5
+ import Button from '../button/Button.svelte';
6
+
7
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onchange'> & {
8
+ /** The count. Bindable. */
9
+ value?: number;
10
+ min?: number;
11
+ max?: number;
12
+ step?: number;
13
+ size?: 'sm' | 'md' | 'lg';
14
+ disabled?: boolean;
15
+ /** Names what is counted; the accessible name of the group and part of the button labels. */
16
+ label: string;
17
+ /** Prefix of the decrease button's name, followed by `label`. */
18
+ decreaseLabel?: string;
19
+ /** Prefix of the increase button's name, followed by `label`. */
20
+ increaseLabel?: string;
21
+ ref?: HTMLDivElement | null;
22
+ onchange?: (value: number) => void;
23
+ };
24
+
25
+ let {
26
+ value = $bindable(0),
27
+ min = 0,
28
+ max = Infinity,
29
+ step = 1,
30
+ size = 'sm',
31
+ disabled = false,
32
+ label,
33
+ decreaseLabel = 'Decrease',
34
+ increaseLabel = 'Increase',
35
+ ref = $bindable(null),
36
+ class: className,
37
+ onchange,
38
+ ...rest
39
+ }: Props = $props();
40
+
41
+ function set(next: number) {
42
+ value = Math.min(Math.max(next, min), max);
43
+ onchange?.(value);
44
+ }
45
+ </script>
46
+
47
+ <div
48
+ {...rest}
49
+ bind:this={ref}
50
+ role="group"
51
+ aria-label={label}
52
+ class={['number-stepper', `number-stepper--${size}`, className]}
53
+ >
54
+ <Button
55
+ variant="secondary"
56
+ {size}
57
+ icon={Minus}
58
+ label="{decreaseLabel} {label}"
59
+ disabled={disabled || value <= min}
60
+ onclick={() => set(value - step)}
61
+ />
62
+ <output class="number-stepper-value" aria-live="polite">{value}</output>
63
+ <Button
64
+ variant="secondary"
65
+ {size}
66
+ icon={Plus}
67
+ label="{increaseLabel} {label}"
68
+ disabled={disabled || value >= max}
69
+ onclick={() => set(value + step)}
70
+ />
71
+ </div>
72
+
73
+ <style>
74
+ @layer svelte-ui {
75
+ .number-stepper {
76
+ display: inline-flex;
77
+ align-items: center;
78
+ gap: 4px;
79
+ font-size: 13px;
80
+ color: var(--color-text);
81
+ }
82
+
83
+ .number-stepper--sm {
84
+ font-size: 12px;
85
+ }
86
+
87
+ .number-stepper-value {
88
+ min-width: 24px;
89
+ font-weight: 600;
90
+ font-variant-numeric: tabular-nums;
91
+ text-align: center;
92
+ }
93
+
94
+ @media (pointer: coarse) {
95
+ .number-stepper :global(.button) {
96
+ min-width: 44px;
97
+ min-height: 44px;
98
+ }
99
+ }
100
+ }
101
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onchange'> & {
3
+ /** The count. Bindable. */
4
+ value?: number;
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ size?: 'sm' | 'md' | 'lg';
9
+ disabled?: boolean;
10
+ /** Names what is counted; the accessible name of the group and part of the button labels. */
11
+ label: string;
12
+ /** Prefix of the decrease button's name, followed by `label`. */
13
+ decreaseLabel?: string;
14
+ /** Prefix of the increase button's name, followed by `label`. */
15
+ increaseLabel?: string;
16
+ ref?: HTMLDivElement | null;
17
+ onchange?: (value: number) => void;
18
+ };
19
+ declare const NumberStepper: import("svelte").Component<Props, {}, "ref" | "value">;
20
+ type NumberStepper = ReturnType<typeof NumberStepper>;
21
+ export default NumberStepper;