sv5ui 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +6 -0
  2. package/dist/Alert/Alert.svelte +33 -22
  3. package/dist/Alert/Alert.svelte.d.ts +1 -1
  4. package/dist/Alert/alert.types.d.ts +4 -0
  5. package/dist/Avatar/Avatar.svelte +72 -46
  6. package/dist/Avatar/avatar.types.d.ts +36 -3
  7. package/dist/Avatar/avatar.variants.d.ts +138 -0
  8. package/dist/Avatar/avatar.variants.js +23 -12
  9. package/dist/Avatar/index.d.ts +1 -1
  10. package/dist/AvatarGroup/AvatarGroup.svelte +11 -6
  11. package/dist/AvatarGroup/AvatarGroup.svelte.d.ts +1 -1
  12. package/dist/AvatarGroup/avatar-group.types.d.ts +18 -3
  13. package/dist/AvatarGroup/avatar-group.variants.d.ts +85 -0
  14. package/dist/AvatarGroup/avatar-group.variants.js +19 -29
  15. package/dist/Badge/Badge.svelte +4 -3
  16. package/dist/Badge/Badge.svelte.d.ts +1 -1
  17. package/dist/Badge/badge.types.d.ts +9 -0
  18. package/dist/Breadcrumb/Breadcrumb.svelte +20 -7
  19. package/dist/Breadcrumb/Breadcrumb.svelte.d.ts +1 -1
  20. package/dist/Breadcrumb/breadcrumb.types.d.ts +5 -1
  21. package/dist/Breadcrumb/breadcrumb.variants.d.ts +15 -5
  22. package/dist/Breadcrumb/breadcrumb.variants.js +7 -3
  23. package/dist/Button/Button.svelte +71 -16
  24. package/dist/Button/Button.svelte.d.ts +0 -1
  25. package/dist/Button/button.types.d.ts +61 -2
  26. package/dist/Calendar/Calendar.svelte +4 -0
  27. package/dist/Calendar/Calendar.svelte.d.ts +1 -1
  28. package/dist/Calendar/calendar.types.d.ts +4 -0
  29. package/dist/Card/Card.svelte +5 -4
  30. package/dist/Card/Card.svelte.d.ts +1 -1
  31. package/dist/Card/card.types.d.ts +5 -1
  32. package/dist/Checkbox/Checkbox.svelte +37 -11
  33. package/dist/Checkbox/Checkbox.svelte.d.ts +1 -1
  34. package/dist/Checkbox/checkbox.types.d.ts +16 -1
  35. package/dist/Checkbox/checkbox.variants.d.ts +90 -0
  36. package/dist/Checkbox/checkbox.variants.js +73 -4
  37. package/dist/Chip/Chip.svelte +3 -2
  38. package/dist/Chip/Chip.svelte.d.ts +1 -1
  39. package/dist/Chip/chip.types.d.ts +5 -1
  40. package/dist/Chip/chip.variants.d.ts +135 -45
  41. package/dist/Chip/chip.variants.js +9 -9
  42. package/dist/ContextMenu/ContextMenu.svelte +87 -77
  43. package/dist/ContextMenu/ContextMenu.svelte.d.ts +1 -1
  44. package/dist/ContextMenu/context-menu.types.d.ts +9 -3
  45. package/dist/ContextMenu/context-menu.types.js +1 -1
  46. package/dist/ContextMenu/context-menu.variants.d.ts +74 -160
  47. package/dist/ContextMenu/context-menu.variants.js +63 -95
  48. package/dist/DropdownMenu/DropdownMenu.svelte +37 -43
  49. package/dist/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
  50. package/dist/DropdownMenu/dropdown-menu.types.d.ts +9 -3
  51. package/dist/DropdownMenu/dropdown-menu.types.js +1 -1
  52. package/dist/DropdownMenu/dropdown-menu.variants.d.ts +79 -230
  53. package/dist/DropdownMenu/dropdown-menu.variants.js +68 -111
  54. package/dist/DropdownMenu/index.d.ts +1 -1
  55. package/dist/Empty/Empty.svelte +68 -33
  56. package/dist/Empty/Empty.svelte.d.ts +1 -1
  57. package/dist/Empty/empty.types.d.ts +26 -9
  58. package/dist/Empty/empty.variants.d.ts +150 -130
  59. package/dist/Empty/empty.variants.js +33 -324
  60. package/dist/FieldGroup/FieldGroup.svelte +11 -6
  61. package/dist/FieldGroup/FieldGroup.svelte.d.ts +1 -1
  62. package/dist/FieldGroup/field-group.types.d.ts +4 -0
  63. package/dist/FormField/FormField.svelte +17 -18
  64. package/dist/FormField/FormField.svelte.d.ts +1 -1
  65. package/dist/FormField/form-field.types.d.ts +4 -0
  66. package/dist/Icon/Icon.svelte +13 -7
  67. package/dist/Icon/icon.types.d.ts +18 -9
  68. package/dist/Input/Input.svelte +30 -29
  69. package/dist/Kbd/Kbd.svelte +13 -3
  70. package/dist/Kbd/Kbd.svelte.d.ts +1 -1
  71. package/dist/Kbd/index.d.ts +1 -1
  72. package/dist/Kbd/kbd.types.d.ts +15 -1
  73. package/dist/Kbd/kbd.variants.d.ts +92 -30
  74. package/dist/Kbd/kbd.variants.js +55 -35
  75. package/dist/Kbd/useKbd.svelte.d.ts +2 -2
  76. package/dist/Kbd/useKbd.svelte.js +34 -41
  77. package/dist/Link/Link.svelte +69 -24
  78. package/dist/Link/Link.svelte.d.ts +1 -1
  79. package/dist/Link/link.types.d.ts +26 -8
  80. package/dist/Link/link.variants.d.ts +35 -60
  81. package/dist/Link/link.variants.js +8 -110
  82. package/dist/Modal/Modal.svelte +9 -1
  83. package/dist/Modal/modal.types.d.ts +5 -0
  84. package/dist/Modal/modal.variants.d.ts +5 -0
  85. package/dist/Modal/modal.variants.js +1 -0
  86. package/dist/Pagination/Pagination.svelte +143 -94
  87. package/dist/Pagination/Pagination.svelte.d.ts +1 -1
  88. package/dist/Pagination/index.d.ts +1 -1
  89. package/dist/Pagination/pagination.types.d.ts +21 -2
  90. package/dist/Pagination/pagination.variants.d.ts +21 -387
  91. package/dist/Pagination/pagination.variants.js +63 -59
  92. package/dist/Popover/Popover.svelte +9 -12
  93. package/dist/Popover/Popover.svelte.d.ts +1 -1
  94. package/dist/Popover/popover.types.d.ts +4 -0
  95. package/dist/Popover/popover.variants.d.ts +5 -75
  96. package/dist/Popover/popover.variants.js +6 -16
  97. package/dist/Progress/Progress.svelte +58 -30
  98. package/dist/Progress/progress.types.d.ts +9 -1
  99. package/dist/Progress/progress.variants.d.ts +55 -25
  100. package/dist/Progress/progress.variants.js +34 -28
  101. package/dist/RadioGroup/RadioGroup.svelte +105 -61
  102. package/dist/RadioGroup/RadioGroup.svelte.d.ts +1 -1
  103. package/dist/RadioGroup/radio-group.types.d.ts +16 -1
  104. package/dist/RadioGroup/radio-group.variants.d.ts +90 -0
  105. package/dist/RadioGroup/radio-group.variants.js +73 -4
  106. package/dist/Select/Select.svelte +9 -6
  107. package/dist/Select/Select.svelte.d.ts +1 -1
  108. package/dist/Select/select.types.d.ts +4 -0
  109. package/dist/SelectMenu/SelectMenu.svelte +436 -0
  110. package/dist/SelectMenu/SelectMenu.svelte.d.ts +5 -0
  111. package/dist/SelectMenu/index.d.ts +2 -0
  112. package/dist/SelectMenu/index.js +1 -0
  113. package/dist/SelectMenu/select-menu.types.d.ts +262 -0
  114. package/dist/SelectMenu/select-menu.types.js +1 -0
  115. package/dist/SelectMenu/select-menu.variants.d.ts +759 -0
  116. package/dist/SelectMenu/select-menu.variants.js +33 -0
  117. package/dist/Separator/Separator.svelte +1 -2
  118. package/dist/Separator/separator.variants.d.ts +1 -5
  119. package/dist/Separator/separator.variants.js +2 -2
  120. package/dist/Skeleton/Skeleton.svelte +18 -2
  121. package/dist/Skeleton/Skeleton.svelte.d.ts +1 -1
  122. package/dist/Skeleton/skeleton.types.d.ts +10 -1
  123. package/dist/Slideover/Slideover.svelte +9 -1
  124. package/dist/Slideover/slideover.types.d.ts +5 -0
  125. package/dist/Slideover/slideover.variants.d.ts +20 -5
  126. package/dist/Slideover/slideover.variants.js +4 -29
  127. package/dist/Switch/Switch.svelte +32 -31
  128. package/dist/Switch/Switch.svelte.d.ts +1 -1
  129. package/dist/Switch/switch.types.d.ts +6 -1
  130. package/dist/Switch/switch.variants.js +6 -6
  131. package/dist/Tabs/Tabs.svelte +6 -9
  132. package/dist/Tabs/Tabs.svelte.d.ts +1 -1
  133. package/dist/Tabs/tabs.types.d.ts +4 -0
  134. package/dist/Tabs/tabs.variants.js +2 -0
  135. package/dist/Textarea/Textarea.svelte +26 -25
  136. package/dist/ThemeModeButton/theme-mode-button.types.d.ts +7 -2
  137. package/dist/Timeline/Timeline.svelte +62 -19
  138. package/dist/Timeline/Timeline.svelte.d.ts +1 -1
  139. package/dist/Timeline/index.d.ts +1 -1
  140. package/dist/Timeline/timeline.types.d.ts +8 -0
  141. package/dist/Tooltip/Tooltip.svelte +12 -10
  142. package/dist/Tooltip/Tooltip.svelte.d.ts +1 -1
  143. package/dist/Tooltip/tooltip.types.d.ts +8 -4
  144. package/dist/Tooltip/tooltip.variants.d.ts +10 -75
  145. package/dist/Tooltip/tooltip.variants.js +8 -17
  146. package/dist/User/User.svelte +13 -9
  147. package/dist/User/User.svelte.d.ts +1 -1
  148. package/dist/User/user.types.d.ts +4 -0
  149. package/dist/User/user.variants.d.ts +60 -0
  150. package/dist/User/user.variants.js +13 -1
  151. package/dist/config.d.ts +4 -0
  152. package/dist/config.js +4 -0
  153. package/dist/index.d.ts +1 -0
  154. package/dist/index.js +1 -0
  155. package/package.json +2 -2
@@ -0,0 +1,33 @@
1
+ import { tv } from 'tailwind-variants';
2
+ import { selectVariants } from '../Select/select.variants.js';
3
+ export const selectMenuVariants = tv({
4
+ extend: selectVariants,
5
+ slots: {
6
+ content: [
7
+ 'z-50 max-h-60 w-(--bits-floating-anchor-width)',
8
+ 'bg-surface-container-low text-on-surface',
9
+ 'rounded-md',
10
+ 'ring ring-outline-variant/50',
11
+ 'shadow-lg',
12
+ 'focus:outline-none',
13
+ 'overflow-hidden',
14
+ 'flex flex-col'
15
+ ],
16
+ input: 'border-b border-outline-variant',
17
+ viewport: 'p-1 flex-1 overflow-y-auto',
18
+ empty: 'text-center text-on-surface-variant'
19
+ },
20
+ variants: {
21
+ size: {
22
+ xs: { empty: 'p-2 text-xs' },
23
+ sm: { empty: 'p-2.5 text-xs' },
24
+ md: { empty: 'p-2.5 text-sm' },
25
+ lg: { empty: 'p-3 text-sm' },
26
+ xl: { empty: 'p-3 text-base' }
27
+ }
28
+ }
29
+ });
30
+ export const selectMenuDefaults = {
31
+ defaultVariants: selectMenuVariants.defaultVariants,
32
+ slots: {}
33
+ };
@@ -40,7 +40,6 @@
40
40
  container: slots.container({ class: [config.slots.container, ui?.container] }),
41
41
  icon: slots.icon({ class: [config.slots.icon, ui?.icon] }),
42
42
  avatar: slots.avatar({ class: [config.slots.avatar, ui?.avatar] }),
43
- avatarSize: slots.avatarSize() as AvatarSize,
44
43
  label: slots.label({ class: [config.slots.label, ui?.label] })
45
44
  }
46
45
  })
@@ -56,7 +55,7 @@
56
55
  {:else if avatar}
57
56
  <Avatar
58
57
  {...avatar}
59
- size={avatar.size ?? classes.avatarSize}
58
+ size={avatar.size ?? (config.avatarSize as AvatarSize)}
60
59
  class={classes.avatar}
61
60
  />
62
61
  {:else if icon}
@@ -93,7 +93,6 @@ export declare const separatorVariants: import("tailwind-variants").TVReturnType
93
93
  container: string;
94
94
  icon: string;
95
95
  avatar: string;
96
- avatarSize: string;
97
96
  label: string;
98
97
  }, undefined, {
99
98
  color: {
@@ -189,7 +188,6 @@ export declare const separatorVariants: import("tailwind-variants").TVReturnType
189
188
  container: string;
190
189
  icon: string;
191
190
  avatar: string;
192
- avatarSize: string;
193
191
  label: string;
194
192
  }, import("tailwind-variants").TVReturnType<{
195
193
  color: {
@@ -285,7 +283,6 @@ export declare const separatorVariants: import("tailwind-variants").TVReturnType
285
283
  container: string;
286
284
  icon: string;
287
285
  avatar: string;
288
- avatarSize: string;
289
286
  label: string;
290
287
  }, undefined, unknown, unknown, undefined>>;
291
288
  export type SeparatorVariantProps = VariantProps<typeof separatorVariants>;
@@ -385,7 +382,6 @@ export declare const separatorDefaults: {
385
382
  container: string;
386
383
  icon: string;
387
384
  avatar: string;
388
- avatarSize: string;
389
385
  label: string;
390
386
  }, {
391
387
  color: {
@@ -481,8 +477,8 @@ export declare const separatorDefaults: {
481
477
  container: string;
482
478
  icon: string;
483
479
  avatar: string;
484
- avatarSize: string;
485
480
  label: string;
486
481
  }>;
487
482
  slots: Partial<Record<SeparatorSlots, string>>;
483
+ avatarSize: string;
488
484
  };
@@ -6,7 +6,6 @@ export const separatorVariants = tv({
6
6
  container: 'font-medium flex',
7
7
  icon: 'shrink-0',
8
8
  avatar: 'shrink-0',
9
- avatarSize: '2xs',
10
9
  label: ''
11
10
  },
12
11
  variants: {
@@ -100,5 +99,6 @@ export const separatorVariants = tv({
100
99
  });
101
100
  export const separatorDefaults = {
102
101
  defaultVariants: separatorVariants.defaultVariants,
103
- slots: {}
102
+ slots: {},
103
+ avatarSize: '2xs'
104
104
  };
@@ -10,7 +10,14 @@
10
10
 
11
11
  const config = getComponentConfig('skeleton', skeletonDefaults)
12
12
 
13
- let { as = 'div', ui, class: className, children, ...restProps }: Props = $props()
13
+ let {
14
+ ref = $bindable(null),
15
+ as = 'div',
16
+ ui,
17
+ class: className,
18
+ children,
19
+ ...restProps
20
+ }: Props = $props()
14
21
 
15
22
  const classes = $derived.by(() => {
16
23
  const slots = skeletonVariants()
@@ -20,6 +27,15 @@
20
27
  })
21
28
  </script>
22
29
 
23
- <svelte:element this={as} class={classes.root} {...restProps}>
30
+ <svelte:element
31
+ this={as}
32
+ bind:this={ref}
33
+ class={classes.root}
34
+ role="alert"
35
+ aria-busy="true"
36
+ aria-label="loading"
37
+ aria-live="polite"
38
+ {...restProps}
39
+ >
24
40
  {@render children?.()}
25
41
  </svelte:element>
@@ -1,5 +1,5 @@
1
1
  import type { SkeletonProps } from './skeleton.types.js';
2
2
  export type Props = SkeletonProps;
3
- declare const Skeleton: import("svelte").Component<SkeletonProps, {}, "">;
3
+ declare const Skeleton: import("svelte").Component<SkeletonProps, {}, "ref">;
4
4
  type Skeleton = ReturnType<typeof Skeleton>;
5
5
  export default Skeleton;
@@ -1,18 +1,27 @@
1
+ import type { Snippet } from 'svelte';
1
2
  import type { HTMLAttributes } from 'svelte/elements';
2
3
  import type { ClassNameValue } from 'tailwind-merge';
3
4
  import type { SkeletonSlots } from './skeleton.variants.js';
4
5
  export type SkeletonProps = Omit<HTMLAttributes<HTMLElement>, 'class'> & {
6
+ /**
7
+ * Bindable reference to the root DOM element.
8
+ */
9
+ ref?: HTMLElement | null;
5
10
  /**
6
11
  * Renders the skeleton as a different HTML element.
7
12
  * @default 'div'
8
13
  */
9
14
  as?: keyof HTMLElementTagNameMap;
10
15
  /**
11
- * Override styles for specific skeleton slots (root).
16
+ * Override styles for specific skeleton slots.
12
17
  */
13
18
  ui?: Partial<Record<SkeletonSlots, ClassNameValue>>;
14
19
  /**
15
20
  * Additional CSS classes for the root element.
16
21
  */
17
22
  class?: ClassNameValue;
23
+ /**
24
+ * Default slot content.
25
+ */
26
+ children?: Snippet;
18
27
  };
@@ -40,6 +40,7 @@
40
40
  header: headerSlot,
41
41
  titleSlot,
42
42
  descriptionSlot,
43
+ actions: actionsSlot,
43
44
  body: bodySlot,
44
45
  footer: footerSlot,
45
46
  closeSlot
@@ -51,7 +52,9 @@
51
52
  const hasTitle = $derived(!!title || !!titleSlot)
52
53
  const hasDescription = $derived(!!description || !!descriptionSlot)
53
54
  const hasHeading = $derived(hasTitle || hasDescription)
54
- const hasHeader = $derived(!!headerSlot || hasHeading || showClose || !!closeSlot)
55
+ const hasHeader = $derived(
56
+ !!headerSlot || hasHeading || !!actionsSlot || showClose || !!closeSlot
57
+ )
55
58
 
56
59
  const variantSlots = $derived(
57
60
  slideoverVariants({ transition, side, inset, overlay: showOverlay })
@@ -66,6 +69,7 @@
66
69
  description: variantSlots.description({
67
70
  class: [config.slots.description, ui?.description]
68
71
  }),
72
+ actions: variantSlots.actions({ class: [config.slots.actions, ui?.actions] }),
69
73
  body: variantSlots.body({ class: [config.slots.body, ui?.body] }),
70
74
  footer: variantSlots.footer({ class: [config.slots.footer, ui?.footer] }),
71
75
  close: variantSlots.close({ class: [config.slots.close, ui?.close] })
@@ -132,6 +136,10 @@
132
136
  {/if}
133
137
  </div>
134
138
 
139
+ {#if actionsSlot}
140
+ <div class={classes.actions}>{@render actionsSlot()}</div>
141
+ {/if}
142
+
135
143
  {#if showClose || closeSlot}
136
144
  <Dialog.Close>
137
145
  {#snippet child({ props })}
@@ -112,6 +112,11 @@ export interface SlideoverProps extends RootProps, ContentProps {
112
112
  * Custom description slot. Overrides the `description` prop when provided.
113
113
  */
114
114
  descriptionSlot?: Snippet;
115
+ /**
116
+ * Actions slot rendered between the title/description wrapper
117
+ * and the close button inside the header.
118
+ */
119
+ actions?: Snippet;
115
120
  /**
116
121
  * Body content rendered between the header and footer.
117
122
  */
@@ -21,7 +21,9 @@ export declare const slideoverVariants: import("tailwind-variants").TVReturnType
21
21
  };
22
22
  inset: {
23
23
  true: {};
24
- false: {};
24
+ false: {
25
+ content: string;
26
+ };
25
27
  };
26
28
  overlay: {
27
29
  true: {
@@ -37,6 +39,7 @@ export declare const slideoverVariants: import("tailwind-variants").TVReturnType
37
39
  footer: string;
38
40
  title: string;
39
41
  description: string;
42
+ actions: string;
40
43
  close: string;
41
44
  }, undefined, {
42
45
  transition: {
@@ -60,7 +63,9 @@ export declare const slideoverVariants: import("tailwind-variants").TVReturnType
60
63
  };
61
64
  inset: {
62
65
  true: {};
63
- false: {};
66
+ false: {
67
+ content: string;
68
+ };
64
69
  };
65
70
  overlay: {
66
71
  true: {
@@ -76,6 +81,7 @@ export declare const slideoverVariants: import("tailwind-variants").TVReturnType
76
81
  footer: string;
77
82
  title: string;
78
83
  description: string;
84
+ actions: string;
79
85
  close: string;
80
86
  }, import("tailwind-variants").TVReturnType<{
81
87
  transition: {
@@ -99,7 +105,9 @@ export declare const slideoverVariants: import("tailwind-variants").TVReturnType
99
105
  };
100
106
  inset: {
101
107
  true: {};
102
- false: {};
108
+ false: {
109
+ content: string;
110
+ };
103
111
  };
104
112
  overlay: {
105
113
  true: {
@@ -115,6 +123,7 @@ export declare const slideoverVariants: import("tailwind-variants").TVReturnType
115
123
  footer: string;
116
124
  title: string;
117
125
  description: string;
126
+ actions: string;
118
127
  close: string;
119
128
  }, undefined, unknown, unknown, undefined>>;
120
129
  export type SlideoverVariantProps = VariantProps<typeof slideoverVariants>;
@@ -142,7 +151,9 @@ export declare const slideoverDefaults: {
142
151
  };
143
152
  inset: {
144
153
  true: {};
145
- false: {};
154
+ false: {
155
+ content: string;
156
+ };
146
157
  };
147
158
  overlay: {
148
159
  true: {
@@ -158,6 +169,7 @@ export declare const slideoverDefaults: {
158
169
  footer: string;
159
170
  title: string;
160
171
  description: string;
172
+ actions: string;
161
173
  close: string;
162
174
  }, {
163
175
  transition: {
@@ -181,7 +193,9 @@ export declare const slideoverDefaults: {
181
193
  };
182
194
  inset: {
183
195
  true: {};
184
- false: {};
196
+ false: {
197
+ content: string;
198
+ };
185
199
  };
186
200
  overlay: {
187
201
  true: {
@@ -197,6 +211,7 @@ export declare const slideoverDefaults: {
197
211
  footer: string;
198
212
  title: string;
199
213
  description: string;
214
+ actions: string;
200
215
  close: string;
201
216
  }>;
202
217
  slots: Partial<Record<SlideoverSlots, string>>;
@@ -9,6 +9,7 @@ export const slideoverVariants = tv({
9
9
  footer: 'flex items-center gap-1.5 p-4 sm:px-6',
10
10
  title: 'text-on-surface font-semibold',
11
11
  description: 'mt-1 text-on-surface-variant text-sm',
12
+ actions: 'flex items-center gap-1.5 shrink-0',
12
13
  close: 'absolute top-4 end-4'
13
14
  },
14
15
  variants: {
@@ -33,7 +34,9 @@ export const slideoverVariants = tv({
33
34
  },
34
35
  inset: {
35
36
  true: {},
36
- false: {}
37
+ false: {
38
+ content: 'shadow-lg'
39
+ }
37
40
  },
38
41
  overlay: {
39
42
  true: {
@@ -97,34 +100,6 @@ export const slideoverVariants = tv({
97
100
  class: {
98
101
  content: 'left-4 inset-y-4 rounded-xl shadow-lg ring ring-outline-variant'
99
102
  }
100
- },
101
- {
102
- inset: false,
103
- side: 'top',
104
- class: {
105
- content: 'shadow-lg'
106
- }
107
- },
108
- {
109
- inset: false,
110
- side: 'right',
111
- class: {
112
- content: 'shadow-lg'
113
- }
114
- },
115
- {
116
- inset: false,
117
- side: 'bottom',
118
- class: {
119
- content: 'shadow-lg'
120
- }
121
- },
122
- {
123
- inset: false,
124
- side: 'left',
125
- class: {
126
- content: 'shadow-lg'
127
- }
128
103
  }
129
104
  ],
130
105
  defaultVariants: {
@@ -16,6 +16,7 @@
16
16
  const icons = getComponentConfig('icons', iconsDefaults)
17
17
 
18
18
  let {
19
+ ref = $bindable(null),
19
20
  checked = $bindable(false),
20
21
  onCheckedChange,
21
22
  ui,
@@ -34,7 +35,8 @@
34
35
  description,
35
36
  labelSlot,
36
37
  descriptionSlot,
37
- class: className
38
+ class: className,
39
+ ...restProps
38
40
  }: Props = $props()
39
41
 
40
42
  const formFieldContext = getContext<
@@ -57,11 +59,13 @@
57
59
  const resolvedName = $derived(name ?? formFieldContext?.name)
58
60
  const isDisabled = $derived(disabled || loading)
59
61
 
60
- const ariaDescribedBy = $derived.by(() => {
61
- if (!formFieldContext) return undefined
62
- const fid = formFieldContext.ariaId
63
- return hasError ? `${fid}-error` : `${fid}-description ${fid}-help`
64
- })
62
+ const ariaDescribedBy = $derived(
63
+ !formFieldContext
64
+ ? undefined
65
+ : hasError
66
+ ? `${formFieldContext.ariaId}-error`
67
+ : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`
68
+ )
65
69
 
66
70
  const hasCheckedIcon = $derived(loading || !!checkedIcon)
67
71
  const hasUncheckedIcon = $derived(loading || !!uncheckedIcon)
@@ -74,7 +78,7 @@
74
78
  loading,
75
79
  required,
76
80
  disabled: isDisabled ? true : undefined
77
- } as const)
81
+ })
78
82
 
79
83
  const classes = $derived.by(() => {
80
84
  const slots = switchVariants(variantOpts)
@@ -85,34 +89,31 @@
85
89
  thumb: slots.thumb({ class: [config.slots.thumb, ui?.thumb] }),
86
90
  wrapper: slots.wrapper({ class: [config.slots.wrapper, ui?.wrapper] }),
87
91
  label: slots.label({ class: [config.slots.label, ui?.label] }),
88
- description: slots.description({
89
- class: [config.slots.description, ui?.description]
90
- })
92
+ description: slots.description({ class: [config.slots.description, ui?.description] })
91
93
  }
92
94
  })
93
95
 
94
- const iconUiClass = $derived([config.slots.icon, ui?.icon])
95
-
96
- const checkedIconClass = $derived.by(() => {
97
- if (!hasCheckedIcon) return undefined
98
- return switchVariants({
99
- ...variantOpts,
100
- checked: true,
101
- unchecked: loading ? true : undefined
102
- }).icon({ class: iconUiClass })
103
- })
104
-
105
- const uncheckedIconClass = $derived.by(() => {
106
- if (!hasUncheckedIcon) return undefined
107
- return switchVariants({
108
- ...variantOpts,
109
- unchecked: true,
110
- checked: loading ? true : undefined
111
- }).icon({ class: iconUiClass })
96
+ const iconClasses = $derived.by(() => {
97
+ if (!hasCheckedIcon && !hasUncheckedIcon)
98
+ return { checked: undefined, unchecked: undefined }
99
+ const iconUiClass = [config.slots.icon, ui?.icon]
100
+ return {
101
+ checked: hasCheckedIcon
102
+ ? switchVariants({ ...variantOpts, checked: true, unchecked: loading }).icon({
103
+ class: iconUiClass
104
+ })
105
+ : undefined,
106
+ unchecked:
107
+ hasUncheckedIcon && !loading
108
+ ? switchVariants({ ...variantOpts, unchecked: true }).icon({
109
+ class: iconUiClass
110
+ })
111
+ : undefined
112
+ }
112
113
  })
113
114
  </script>
114
115
 
115
- <div class={classes.root}>
116
+ <div {...restProps} bind:this={ref} class={classes.root}>
116
117
  <div class={classes.container}>
117
118
  <Switch.Root
118
119
  bind:checked
@@ -128,10 +129,10 @@
128
129
  >
129
130
  <Switch.Thumb class={classes.thumb}>
130
131
  {#if hasCheckedIcon && checkedIconName}
131
- <Icon name={checkedIconName} class={checkedIconClass} />
132
+ <Icon name={checkedIconName} class={iconClasses.checked} />
132
133
  {/if}
133
134
  {#if hasUncheckedIcon && uncheckedIconName && !loading}
134
- <Icon name={uncheckedIconName} class={uncheckedIconClass} />
135
+ <Icon name={uncheckedIconName} class={iconClasses.unchecked} />
135
136
  {/if}
136
137
  </Switch.Thumb>
137
138
  </Switch.Root>
@@ -1,6 +1,6 @@
1
1
  import type { SwitchProps } from './switch.types.js';
2
2
  export type Props = SwitchProps;
3
3
  import { Switch } from 'bits-ui';
4
- declare const Switch: import("svelte").Component<SwitchProps, {}, "checked">;
4
+ declare const Switch: import("svelte").Component<SwitchProps, {}, "ref" | "checked">;
5
5
  type Switch = ReturnType<typeof Switch>;
6
6
  export default Switch;
@@ -1,8 +1,13 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { Switch as SwitchPrimitive } from 'bits-ui';
3
3
  import type { ClassNameValue } from 'tailwind-merge';
4
+ import type { HTMLAttributes } from 'svelte/elements';
4
5
  import type { SwitchVariantProps, SwitchSlots } from './switch.variants.js';
5
- export type SwitchProps = Pick<SwitchPrimitive.RootProps, 'disabled' | 'name' | 'value' | 'required'> & {
6
+ export type SwitchProps = Pick<SwitchPrimitive.RootProps, 'disabled' | 'name' | 'value' | 'required'> & Omit<HTMLAttributes<HTMLElement>, 'class'> & {
7
+ /**
8
+ * Bindable reference to the root DOM element.
9
+ */
10
+ ref?: HTMLElement | null;
6
11
  /**
7
12
  * The checked state of the switch. Supports two-way binding with `bind:checked`.
8
13
  * @default false
@@ -10,7 +10,7 @@ export const switchVariants = tv({
10
10
  ],
11
11
  container: 'flex items-center',
12
12
  thumb: [
13
- 'group pointer-events-none rounded-full bg-white shadow-lg ring-0',
13
+ 'group pointer-events-none rounded-full bg-surface shadow-lg ring-0',
14
14
  'flex items-center justify-center',
15
15
  'transition-transform duration-200',
16
16
  'data-[state=unchecked]:translate-x-0'
@@ -39,35 +39,35 @@ export const switchVariants = tv({
39
39
  xs: {
40
40
  base: 'w-7',
41
41
  container: 'h-4',
42
- thumb: 'size-3 data-[state=checked]:translate-x-3',
42
+ thumb: 'size-3 data-[state=checked]:translate-x-3 rtl:data-[state=checked]:-translate-x-3',
43
43
  icon: 'size-2',
44
44
  wrapper: 'text-xs'
45
45
  },
46
46
  sm: {
47
47
  base: 'w-8',
48
48
  container: 'h-4',
49
- thumb: 'size-3.5 data-[state=checked]:translate-x-3.5',
49
+ thumb: 'size-3.5 data-[state=checked]:translate-x-3.5 rtl:data-[state=checked]:-translate-x-3.5',
50
50
  icon: 'size-2.5',
51
51
  wrapper: 'text-xs'
52
52
  },
53
53
  md: {
54
54
  base: 'w-9',
55
55
  container: 'h-5',
56
- thumb: 'size-4 data-[state=checked]:translate-x-4',
56
+ thumb: 'size-4 data-[state=checked]:translate-x-4 rtl:data-[state=checked]:-translate-x-4',
57
57
  icon: 'size-3',
58
58
  wrapper: 'text-sm'
59
59
  },
60
60
  lg: {
61
61
  base: 'w-10',
62
62
  container: 'h-5',
63
- thumb: 'size-4.5 data-[state=checked]:translate-x-4.5',
63
+ thumb: 'size-4.5 data-[state=checked]:translate-x-4.5 rtl:data-[state=checked]:-translate-x-4.5',
64
64
  icon: 'size-3.5',
65
65
  wrapper: 'text-sm'
66
66
  },
67
67
  xl: {
68
68
  base: 'w-11',
69
69
  container: 'h-6',
70
- thumb: 'size-5 data-[state=checked]:translate-x-5',
70
+ thumb: 'size-5 data-[state=checked]:translate-x-5 rtl:data-[state=checked]:-translate-x-5',
71
71
  icon: 'size-4',
72
72
  wrapper: 'text-base'
73
73
  }
@@ -14,6 +14,7 @@
14
14
  const config = getComponentConfig('tabs', tabsDefaults)
15
15
 
16
16
  let {
17
+ ref = $bindable(null),
17
18
  items = [],
18
19
  value = $bindable(),
19
20
  defaultValue,
@@ -46,7 +47,7 @@
46
47
 
47
48
  const variantSlots = $derived(tabsVariants({ variant, color, size, orientation }))
48
49
 
49
- const classes = $derived.by(() => ({
50
+ const classes = $derived({
50
51
  root: variantSlots.root({ class: [config.slots.root, className, ui?.root] }),
51
52
  list: variantSlots.list({ class: [config.slots.list, ui?.list] }),
52
53
  indicator: variantSlots.indicator({
@@ -58,7 +59,7 @@
58
59
  }),
59
60
  label: variantSlots.label({ class: [config.slots.label, ui?.label] }),
60
61
  content: variantSlots.content({ class: [config.slots.content, ui?.content] })
61
- }))
62
+ })
62
63
 
63
64
  let rafId = 0
64
65
 
@@ -107,11 +108,6 @@
107
108
  rafId = requestAnimationFrame(updateIndicator)
108
109
  }
109
110
 
110
- function handleValueChange(newValue: string) {
111
- value = newValue
112
- onValueChange?.(newValue)
113
- }
114
-
115
111
  // Update indicator on mount and when dependencies change
116
112
  $effect(() => {
117
113
  void value
@@ -135,8 +131,9 @@
135
131
  </script>
136
132
 
137
133
  <Tabs.Root
138
- {value}
139
- onValueChange={handleValueChange}
134
+ bind:ref
135
+ bind:value
136
+ {onValueChange}
140
137
  {orientation}
141
138
  {activationMode}
142
139
  {disabled}
@@ -1,6 +1,6 @@
1
1
  import type { TabsProps } from './tabs.types.js';
2
2
  export type Props = TabsProps;
3
3
  import { Tabs } from 'bits-ui';
4
- declare const Tabs: import("svelte").Component<TabsProps, {}, "value">;
4
+ declare const Tabs: import("svelte").Component<TabsProps, {}, "ref" | "value">;
5
5
  type Tabs = ReturnType<typeof Tabs>;
6
6
  export default Tabs;
@@ -87,6 +87,10 @@ export interface TabsSlotProps {
87
87
  * @see https://bits-ui.com/docs/components/tabs
88
88
  */
89
89
  export interface TabsProps {
90
+ /**
91
+ * Bindable reference to the root DOM element.
92
+ */
93
+ ref?: HTMLElement | null;
90
94
  /**
91
95
  * Array of tab items to render.
92
96
  * Each item can have a label, icon, content, and optional styling overrides.
@@ -7,6 +7,8 @@ export const tabsVariants = tv({
7
7
  trigger: [
8
8
  'relative inline-flex items-center justify-center whitespace-nowrap',
9
9
  'font-medium cursor-pointer select-none',
10
+ 'data-[state=inactive]:text-on-surface-variant',
11
+ 'hover:data-[state=inactive]:not-disabled:text-on-surface',
10
12
  'focus-visible:outline-none focus-visible:ring-2',
11
13
  'disabled:cursor-not-allowed disabled:opacity-50',
12
14
  'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',