@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
@@ -16,11 +16,13 @@
16
16
 
17
17
  <script lang="ts">
18
18
  import type { Snippet } from 'svelte';
19
+ import type { HTMLAttributes } from 'svelte/elements';
20
+ import { prefersReducedMotion } from 'svelte/motion';
19
21
  import { scale } from 'svelte/transition';
20
22
 
21
23
  type Side = 'top' | 'right' | 'bottom' | 'left';
22
24
 
23
- type Props = {
25
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> & {
24
26
  /** Whether the popover is open. Bindable. */
25
27
  open?: boolean;
26
28
  /** Opens on hover and keyboard focus instead of on click, like a hover card. */
@@ -36,6 +38,8 @@
36
38
  /** Renders the trigger. Spread the given props onto it, e.g. `<Button {...props} />`. */
37
39
  trigger: Snippet<[PopoverTriggerProps]>;
38
40
  children?: Snippet;
41
+ /** The popup element, only set while open. Bindable. */
42
+ ref?: HTMLDivElement | null;
39
43
  };
40
44
 
41
45
  let {
@@ -47,7 +51,10 @@
47
51
  title,
48
52
  description,
49
53
  trigger,
50
- children
54
+ children,
55
+ class: className,
56
+ ref = $bindable(null),
57
+ ...rest
51
58
  }: Props = $props();
52
59
 
53
60
  const id = $props.id();
@@ -58,7 +65,6 @@
58
65
 
59
66
  let wrapperEl: HTMLSpanElement;
60
67
  let triggerEl: HTMLElement | undefined;
61
- let contentEl: HTMLDivElement | undefined = $state();
62
68
  let placement: Side = $state('bottom');
63
69
  let position = $state({ top: 0, left: 0 });
64
70
  let timer: ReturnType<typeof setTimeout> | undefined;
@@ -126,9 +132,9 @@
126
132
  }
127
133
 
128
134
  function place() {
129
- if (!triggerEl || !contentEl) return;
135
+ if (!triggerEl || !ref) return;
130
136
  const rect = triggerEl.getBoundingClientRect();
131
- const { offsetWidth: width, offsetHeight: height } = contentEl;
137
+ const { offsetWidth: width, offsetHeight: height } = ref;
132
138
  const s =
133
139
  fits(rect, width, height, side) || !fits(rect, width, height, opposite[side])
134
140
  ? side
@@ -190,23 +196,29 @@
190
196
 
191
197
  {#if open}
192
198
  <div
199
+ {...rest}
193
200
  id="{id}-popover"
194
201
  role={hover ? undefined : 'dialog'}
195
202
  aria-labelledby={title ? `${id}-title` : undefined}
196
203
  aria-describedby={description ? `${id}-description` : undefined}
197
204
  tabindex="-1"
198
205
  popover="manual"
199
- class={['popover-content', `popover-content--${placement}`, `popover-content--${align}`]}
206
+ class={[
207
+ 'popover-content',
208
+ `popover-content--${placement}`,
209
+ `popover-content--${align}`,
210
+ className
211
+ ]}
200
212
  style:top="{position.top}px"
201
213
  style:left="{position.left}px"
202
- bind:this={contentEl}
214
+ bind:this={ref}
203
215
  onpointerenter={() => hover && clearTimeout(timer)}
204
216
  onpointerleave={() => hover && schedule(false, closeDelay)}
205
217
  onfocusout={(event) => {
206
218
  if (leftWrapper(event)) close();
207
219
  }}
208
220
  {@attach popover}
209
- transition:scale={{ duration: 140, start: 0.95 }}
221
+ transition:scale={{ duration: prefersReducedMotion.current ? 0 : 140, start: 0.95 }}
210
222
  >
211
223
  {#if title}
212
224
  <h3 id="{id}-title" class="popover-title">{title}</h3>
@@ -222,74 +234,82 @@
222
234
  </span>
223
235
 
224
236
  <style>
225
- .popover {
226
- display: inline-flex;
227
- height: fit-content;
228
- }
237
+ @layer svelte-ui {
238
+ .popover {
239
+ display: inline-flex;
240
+ height: fit-content;
241
+ }
229
242
 
230
- .popover-content {
231
- position: fixed;
232
- inset: auto;
233
- box-sizing: border-box;
234
- width: 288px;
235
- max-width: calc(100vw - 16px);
236
- margin: 0;
237
- padding: 16px;
238
- overflow: visible;
239
- font-size: 13px;
240
- line-height: 1.5;
241
- background-color: var(--color-bg);
242
- color: var(--color-text);
243
- border: 1px solid var(--color-border);
244
- border-radius: 12px;
245
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
246
- outline: none;
247
- }
243
+ .popover-content {
244
+ position: fixed;
245
+ inset: auto;
246
+ box-sizing: border-box;
247
+ width: 288px;
248
+ max-width: calc(100vw - 16px);
249
+ margin: 0;
250
+ padding: 16px;
251
+ overflow: visible;
252
+ font-size: 13px;
253
+ line-height: 1.5;
254
+ background-color: var(--color-bg);
255
+ color: var(--color-text);
256
+ border: 1px solid var(--color-border);
257
+ border-radius: 12px;
258
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
259
+ outline: none;
260
+ }
248
261
 
249
- .popover-title {
250
- margin: 0;
251
- font-size: 14px;
252
- font-weight: 600;
253
- }
262
+ .popover-title {
263
+ margin: 0;
264
+ font-size: 14px;
265
+ font-weight: 600;
266
+ }
254
267
 
255
- .popover-description {
256
- margin: 4px 0 0;
257
- color: var(--color-text-muted);
258
- }
268
+ .popover-description {
269
+ margin: 4px 0 0;
270
+ color: var(--color-text-muted);
271
+ }
259
272
 
260
- :is(.popover-title, .popover-description) + .popover-body {
261
- margin-top: 14px;
262
- }
273
+ :is(.popover-title, .popover-description) + .popover-body {
274
+ margin-top: 14px;
275
+ }
263
276
 
264
- .popover-content--bottom {
265
- transform-origin: top center;
266
- }
277
+ .popover-content--bottom {
278
+ transform-origin: top center;
279
+ }
267
280
 
268
- .popover-content--top {
269
- transform-origin: bottom center;
270
- }
281
+ .popover-content--top {
282
+ transform-origin: bottom center;
283
+ }
271
284
 
272
- .popover-content--left {
273
- transform-origin: center right;
274
- }
285
+ .popover-content--left {
286
+ transform-origin: center right;
287
+ }
275
288
 
276
- .popover-content--right {
277
- transform-origin: center left;
278
- }
289
+ .popover-content--right {
290
+ transform-origin: center left;
291
+ }
279
292
 
280
- .popover-content--bottom.popover-content--start {
281
- transform-origin: top left;
282
- }
293
+ .popover-content--bottom.popover-content--start {
294
+ transform-origin: top left;
295
+ }
283
296
 
284
- .popover-content--bottom.popover-content--end {
285
- transform-origin: top right;
286
- }
297
+ .popover-content--bottom.popover-content--end {
298
+ transform-origin: top right;
299
+ }
287
300
 
288
- .popover-content--top.popover-content--start {
289
- transform-origin: bottom left;
290
- }
301
+ .popover-content--top.popover-content--start {
302
+ transform-origin: bottom left;
303
+ }
291
304
 
292
- .popover-content--top.popover-content--end {
293
- transform-origin: bottom right;
305
+ .popover-content--top.popover-content--end {
306
+ transform-origin: bottom right;
307
+ }
308
+
309
+ @media (forced-colors: active) {
310
+ .popover-content {
311
+ border-color: CanvasText;
312
+ }
313
+ }
294
314
  }
295
315
  </style>
@@ -11,8 +11,9 @@ export type PopoverTriggerProps = {
11
11
  [key: symbol]: Attachment<HTMLElement>;
12
12
  };
13
13
  import type { Snippet } from 'svelte';
14
+ import type { HTMLAttributes } from 'svelte/elements';
14
15
  type Side = 'top' | 'right' | 'bottom' | 'left';
15
- type Props = {
16
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> & {
16
17
  /** Whether the popover is open. Bindable. */
17
18
  open?: boolean;
18
19
  /** Opens on hover and keyboard focus instead of on click, like a hover card. */
@@ -28,7 +29,9 @@ type Props = {
28
29
  /** Renders the trigger. Spread the given props onto it, e.g. `<Button {...props} />`. */
29
30
  trigger: Snippet<[PopoverTriggerProps]>;
30
31
  children?: Snippet;
32
+ /** The popup element, only set while open. Bindable. */
33
+ ref?: HTMLDivElement | null;
31
34
  };
32
- declare const Popover: import("svelte").Component<Props, {}, "open">;
35
+ declare const Popover: import("svelte").Component<Props, {}, "ref" | "open">;
33
36
  type Popover = ReturnType<typeof Popover>;
34
37
  export default Popover;
@@ -0,0 +1,184 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
4
+ import type { IconComponent } from '../../icon.js';
5
+
6
+ type Props = Omit<HTMLAnchorAttributes, 'children' | 'title' | 'href' | 'media'> & {
7
+ /** Makes the whole card a link. */
8
+ href?: string;
9
+ /** Bold lead-in before the description, followed by a colon. */
10
+ title: string;
11
+ description?: string;
12
+ /** Muted text below, e.g. a date. */
13
+ meta?: string;
14
+ /** Any icon component, e.g. from `@lucide/svelte`, shown in the top-left corner of the media. */
15
+ icon?: IconComponent;
16
+ orientation?: 'vertical' | 'horizontal';
17
+ /** Square media that fills the frame, e.g. a MeshGradient or an `<img>`. */
18
+ media: Snippet;
19
+ /** Custom description content instead of `description`. */
20
+ children?: Snippet;
21
+ ref?: HTMLElement | null;
22
+ };
23
+
24
+ let {
25
+ href,
26
+ title,
27
+ description,
28
+ meta,
29
+ icon: Icon,
30
+ orientation = 'vertical',
31
+ media,
32
+ children,
33
+ class: className,
34
+ ref = $bindable(null),
35
+ ...rest
36
+ }: Props = $props();
37
+ </script>
38
+
39
+ <svelte:element
40
+ this={href ? 'a' : 'div'}
41
+ {...rest}
42
+ bind:this={ref}
43
+ {href}
44
+ class={['preview-card', `preview-card--${orientation}`, className]}
45
+ >
46
+ <div class="preview-card-media">
47
+ {@render media()}
48
+ {#if Icon}
49
+ <span class="preview-card-icon"><Icon aria-hidden="true" /></span>
50
+ {/if}
51
+ </div>
52
+ <div class="preview-card-info">
53
+ <p class="preview-card-headline">
54
+ <span class="preview-card-title">{title}{description || children ? ':' : ''}</span>
55
+ {#if children}{@render children()}{:else}{description}{/if}
56
+ </p>
57
+ {#if meta}
58
+ <span class="preview-card-meta">{meta}</span>
59
+ {/if}
60
+ </div>
61
+ </svelte:element>
62
+
63
+ <style>
64
+ @layer svelte-ui {
65
+ .preview-card {
66
+ display: flex;
67
+ gap: 20px;
68
+ color: var(--color-text);
69
+ text-decoration: none;
70
+ border-radius: 10px;
71
+ outline: none;
72
+ }
73
+
74
+ a.preview-card:focus-visible {
75
+ outline: 2px solid var(--color-accent);
76
+ outline-offset: 2px;
77
+ }
78
+
79
+ .preview-card--vertical {
80
+ flex-direction: column;
81
+ max-width: 380px;
82
+ }
83
+
84
+ .preview-card--horizontal {
85
+ align-items: center;
86
+ }
87
+
88
+ .preview-card-media {
89
+ position: relative;
90
+ flex-shrink: 0;
91
+ aspect-ratio: 1;
92
+ overflow: hidden;
93
+ border-radius: 10px;
94
+ }
95
+
96
+ .preview-card--horizontal .preview-card-media {
97
+ width: 140px;
98
+ }
99
+
100
+ .preview-card-media > :global(:not(.preview-card-icon)) {
101
+ position: absolute;
102
+ inset: 0;
103
+ width: 100%;
104
+ height: 100%;
105
+ object-fit: cover;
106
+ border-radius: inherit;
107
+ transition: transform 300ms ease;
108
+ }
109
+
110
+ a.preview-card:hover .preview-card-media > :global(:not(.preview-card-icon)) {
111
+ transform: scale(1.05);
112
+ }
113
+
114
+ .preview-card-icon {
115
+ position: absolute;
116
+ top: 0;
117
+ left: 0;
118
+ z-index: 1;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ width: 40px;
123
+ height: 40px;
124
+ color: var(--color-base);
125
+ background-color: var(--color-ink);
126
+ border-radius: 10px 0 0 0;
127
+ }
128
+
129
+ .preview-card-icon :global(svg) {
130
+ width: 20px;
131
+ height: 20px;
132
+ }
133
+
134
+ .preview-card--horizontal .preview-card-icon {
135
+ width: 32px;
136
+ height: 32px;
137
+ }
138
+
139
+ .preview-card--horizontal .preview-card-icon :global(svg) {
140
+ width: 16px;
141
+ height: 16px;
142
+ }
143
+
144
+ .preview-card-info {
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 8px;
148
+ min-width: 0;
149
+ }
150
+
151
+ .preview-card-headline {
152
+ margin: 0;
153
+ font-size: 16px;
154
+ }
155
+
156
+ .preview-card-title {
157
+ font-weight: 600;
158
+ }
159
+
160
+ .preview-card-meta {
161
+ font-size: 14px;
162
+ color: var(--color-text-muted);
163
+ }
164
+
165
+ @media (min-width: 640px) {
166
+ .preview-card--horizontal .preview-card-media {
167
+ width: 168px;
168
+ }
169
+ }
170
+
171
+ @media (prefers-reduced-motion: reduce) {
172
+ .preview-card-media > :global(*) {
173
+ transition: none;
174
+ }
175
+ }
176
+
177
+ @media (forced-colors: active) {
178
+ .preview-card-icon {
179
+ outline: 1px solid CanvasText;
180
+ outline-offset: -1px;
181
+ }
182
+ }
183
+ }
184
+ </style>
@@ -0,0 +1,23 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
3
+ import type { IconComponent } from '../../icon.js';
4
+ type Props = Omit<HTMLAnchorAttributes, 'children' | 'title' | 'href' | 'media'> & {
5
+ /** Makes the whole card a link. */
6
+ href?: string;
7
+ /** Bold lead-in before the description, followed by a colon. */
8
+ title: string;
9
+ description?: string;
10
+ /** Muted text below, e.g. a date. */
11
+ meta?: string;
12
+ /** Any icon component, e.g. from `@lucide/svelte`, shown in the top-left corner of the media. */
13
+ icon?: IconComponent;
14
+ orientation?: 'vertical' | 'horizontal';
15
+ /** Square media that fills the frame, e.g. a MeshGradient or an `<img>`. */
16
+ media: Snippet;
17
+ /** Custom description content instead of `description`. */
18
+ children?: Snippet;
19
+ ref?: HTMLElement | null;
20
+ };
21
+ declare const PreviewCard: import("svelte").Component<Props, {}, "ref">;
22
+ type PreviewCard = ReturnType<typeof PreviewCard>;
23
+ export default PreviewCard;
@@ -0,0 +1,111 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+
4
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
5
+ /** Progress between 0 and `max`. Omit while the amount is unknown: the bar then runs back and forth. */
6
+ value?: number;
7
+ max?: number;
8
+ size?: 'sm' | 'md';
9
+ /** Accessible name, e.g. what is loading. */
10
+ label?: string;
11
+ ref?: HTMLDivElement | null;
12
+ };
13
+
14
+ let {
15
+ value,
16
+ max = 100,
17
+ size = 'md',
18
+ label,
19
+ class: className,
20
+ ref = $bindable(null),
21
+ ...rest
22
+ }: Props = $props();
23
+
24
+ let fraction = $derived(value === undefined ? undefined : Math.min(Math.max(value / max, 0), 1));
25
+ </script>
26
+
27
+ <div
28
+ {...rest}
29
+ bind:this={ref}
30
+ role="progressbar"
31
+ aria-label={label}
32
+ aria-valuemin={0}
33
+ aria-valuemax={max}
34
+ aria-valuenow={fraction === undefined ? undefined : fraction * max}
35
+ class={[
36
+ 'progress',
37
+ `progress--${size}`,
38
+ { 'progress--indeterminate': fraction === undefined },
39
+ className
40
+ ]}
41
+ >
42
+ <div
43
+ class="progress-bar"
44
+ style:width={fraction === undefined ? undefined : `${fraction * 100}%`}
45
+ ></div>
46
+ </div>
47
+
48
+ <style>
49
+ @layer svelte-ui {
50
+ .progress {
51
+ width: 100%;
52
+ overflow: hidden;
53
+ background-color: var(--color-surface);
54
+ border-radius: 999px;
55
+ }
56
+
57
+ .progress--sm {
58
+ height: 4px;
59
+ }
60
+
61
+ .progress--md {
62
+ height: 8px;
63
+ }
64
+
65
+ .progress-bar {
66
+ height: 100%;
67
+ background-color: var(--color-accent);
68
+ border-radius: inherit;
69
+ transition: width 300ms ease;
70
+ }
71
+
72
+ .progress--indeterminate .progress-bar {
73
+ width: 40%;
74
+ animation: progress-slide 1.4s ease-in-out infinite;
75
+ }
76
+
77
+ @keyframes progress-slide {
78
+ from {
79
+ translate: -100% 0;
80
+ }
81
+
82
+ to {
83
+ translate: 250% 0;
84
+ }
85
+ }
86
+
87
+ @media (prefers-reduced-motion: reduce) {
88
+ .progress-bar {
89
+ transition: none;
90
+ }
91
+
92
+ /* A full, dimmed bar instead of the movement */
93
+ .progress--indeterminate .progress-bar {
94
+ width: 100%;
95
+ opacity: 0.4;
96
+ animation: none;
97
+ }
98
+ }
99
+
100
+ @media (forced-colors: active) {
101
+ .progress {
102
+ border: 1px solid CanvasText;
103
+ }
104
+
105
+ .progress-bar {
106
+ forced-color-adjust: none;
107
+ background-color: Highlight;
108
+ }
109
+ }
110
+ }
111
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
3
+ /** Progress between 0 and `max`. Omit while the amount is unknown: the bar then runs back and forth. */
4
+ value?: number;
5
+ max?: number;
6
+ size?: 'sm' | 'md';
7
+ /** Accessible name, e.g. what is loading. */
8
+ label?: string;
9
+ ref?: HTMLDivElement | null;
10
+ };
11
+ declare const Progress: import("svelte").Component<Props, {}, "ref">;
12
+ type Progress = ReturnType<typeof Progress>;
13
+ export default Progress;
@@ -11,6 +11,7 @@
11
11
  disabled?: boolean;
12
12
  required?: boolean;
13
13
  orientation?: 'vertical' | 'horizontal';
14
+ ref?: HTMLDivElement | null;
14
15
  /** Horizontal Fields, each with a RadioGroupItem and a Label. */
15
16
  children: Snippet;
16
17
  };
@@ -21,6 +22,7 @@
21
22
  disabled = false,
22
23
  required = false,
23
24
  orientation = 'vertical',
25
+ ref = $bindable(null),
24
26
  class: className,
25
27
  children,
26
28
  ...rest
@@ -49,6 +51,7 @@
49
51
 
50
52
  <div
51
53
  {...rest}
54
+ bind:this={ref}
52
55
  role="radiogroup"
53
56
  aria-disabled={disabled || undefined}
54
57
  aria-required={required || undefined}
@@ -59,28 +62,30 @@
59
62
  </div>
60
63
 
61
64
  <style>
62
- .radio-group {
63
- display: flex;
64
- flex-direction: column;
65
- gap: 12px;
66
- }
65
+ @layer svelte-ui {
66
+ .radio-group {
67
+ display: flex;
68
+ flex-direction: column;
69
+ gap: 12px;
70
+ }
67
71
 
68
- .radio-group--horizontal {
69
- flex-direction: row;
70
- flex-wrap: wrap;
71
- gap: 12px 24px;
72
- }
72
+ .radio-group--horizontal {
73
+ flex-direction: row;
74
+ flex-wrap: wrap;
75
+ gap: 12px 24px;
76
+ }
73
77
 
74
- .radio-group--horizontal > :global(.field) {
75
- width: auto;
76
- }
78
+ .radio-group--horizontal > :global(.field) {
79
+ width: auto;
80
+ }
77
81
 
78
- /* aria-invalid is not allowed on a radio, so the group carries it */
79
- .radio-group[aria-invalid='true'] :global(.radio input) {
80
- border-color: var(--color-destructive);
81
- }
82
+ /* aria-invalid is not allowed on a radio, so the group carries it */
83
+ .radio-group[aria-invalid='true'] :global(.radio input) {
84
+ border-color: var(--color-destructive);
85
+ }
82
86
 
83
- .radio-group[aria-invalid='true'] :global(.label) {
84
- color: var(--color-destructive);
87
+ .radio-group[aria-invalid='true'] :global(.label) {
88
+ color: var(--color-destructive);
89
+ }
85
90
  }
86
91
  </style>
@@ -8,9 +8,10 @@ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
8
8
  disabled?: boolean;
9
9
  required?: boolean;
10
10
  orientation?: 'vertical' | 'horizontal';
11
+ ref?: HTMLDivElement | null;
11
12
  /** Horizontal Fields, each with a RadioGroupItem and a Label. */
12
13
  children: Snippet;
13
14
  };
14
- declare const RadioGroup: import("svelte").Component<Props, {}, "value">;
15
+ declare const RadioGroup: import("svelte").Component<Props, {}, "ref" | "value">;
15
16
  type RadioGroup = ReturnType<typeof RadioGroup>;
16
17
  export default RadioGroup;