sv5ui 1.1.3 → 1.3.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 (187) 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/CheckboxGroup/CheckboxGroup.svelte +215 -0
  38. package/dist/CheckboxGroup/CheckboxGroup.svelte.d.ts +5 -0
  39. package/dist/CheckboxGroup/checkbox-group.types.d.ts +130 -0
  40. package/dist/CheckboxGroup/checkbox-group.types.js +1 -0
  41. package/dist/CheckboxGroup/checkbox-group.variants.d.ts +553 -0
  42. package/dist/CheckboxGroup/checkbox-group.variants.js +231 -0
  43. package/dist/CheckboxGroup/index.d.ts +2 -0
  44. package/dist/CheckboxGroup/index.js +1 -0
  45. package/dist/Chip/Chip.svelte +3 -2
  46. package/dist/Chip/Chip.svelte.d.ts +1 -1
  47. package/dist/Chip/chip.types.d.ts +5 -1
  48. package/dist/Chip/chip.variants.d.ts +135 -45
  49. package/dist/Chip/chip.variants.js +9 -9
  50. package/dist/ContextMenu/ContextMenu.svelte +87 -77
  51. package/dist/ContextMenu/ContextMenu.svelte.d.ts +1 -1
  52. package/dist/ContextMenu/context-menu.types.d.ts +9 -3
  53. package/dist/ContextMenu/context-menu.types.js +1 -1
  54. package/dist/ContextMenu/context-menu.variants.d.ts +74 -160
  55. package/dist/ContextMenu/context-menu.variants.js +63 -95
  56. package/dist/DropdownMenu/DropdownMenu.svelte +37 -43
  57. package/dist/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
  58. package/dist/DropdownMenu/dropdown-menu.types.d.ts +9 -3
  59. package/dist/DropdownMenu/dropdown-menu.types.js +1 -1
  60. package/dist/DropdownMenu/dropdown-menu.variants.d.ts +79 -230
  61. package/dist/DropdownMenu/dropdown-menu.variants.js +68 -111
  62. package/dist/DropdownMenu/index.d.ts +1 -1
  63. package/dist/Empty/Empty.svelte +68 -33
  64. package/dist/Empty/Empty.svelte.d.ts +1 -1
  65. package/dist/Empty/empty.types.d.ts +26 -9
  66. package/dist/Empty/empty.variants.d.ts +150 -130
  67. package/dist/Empty/empty.variants.js +33 -324
  68. package/dist/FieldGroup/FieldGroup.svelte +11 -6
  69. package/dist/FieldGroup/FieldGroup.svelte.d.ts +1 -1
  70. package/dist/FieldGroup/field-group.types.d.ts +4 -0
  71. package/dist/FileUpload/FileUpload.svelte +561 -0
  72. package/dist/FileUpload/FileUpload.svelte.d.ts +8 -0
  73. package/dist/FileUpload/file-upload.types.d.ts +164 -0
  74. package/dist/FileUpload/file-upload.types.js +1 -0
  75. package/dist/FileUpload/file-upload.variants.d.ts +397 -0
  76. package/dist/FileUpload/file-upload.variants.js +224 -0
  77. package/dist/FileUpload/index.d.ts +2 -0
  78. package/dist/FileUpload/index.js +1 -0
  79. package/dist/FormField/FormField.svelte +17 -18
  80. package/dist/FormField/FormField.svelte.d.ts +1 -1
  81. package/dist/FormField/form-field.types.d.ts +4 -0
  82. package/dist/Icon/Icon.svelte +13 -7
  83. package/dist/Icon/icon.types.d.ts +18 -9
  84. package/dist/Input/Input.svelte +30 -29
  85. package/dist/Kbd/Kbd.svelte +13 -3
  86. package/dist/Kbd/Kbd.svelte.d.ts +1 -1
  87. package/dist/Kbd/index.d.ts +1 -1
  88. package/dist/Kbd/kbd.types.d.ts +15 -1
  89. package/dist/Kbd/kbd.variants.d.ts +92 -30
  90. package/dist/Kbd/kbd.variants.js +55 -35
  91. package/dist/Kbd/useKbd.svelte.d.ts +2 -2
  92. package/dist/Kbd/useKbd.svelte.js +34 -41
  93. package/dist/Link/Link.svelte +69 -24
  94. package/dist/Link/Link.svelte.d.ts +1 -1
  95. package/dist/Link/link.types.d.ts +26 -8
  96. package/dist/Link/link.variants.d.ts +35 -60
  97. package/dist/Link/link.variants.js +8 -110
  98. package/dist/Modal/Modal.svelte +9 -1
  99. package/dist/Modal/modal.types.d.ts +5 -0
  100. package/dist/Modal/modal.variants.d.ts +5 -0
  101. package/dist/Modal/modal.variants.js +1 -0
  102. package/dist/Pagination/Pagination.svelte +143 -94
  103. package/dist/Pagination/Pagination.svelte.d.ts +1 -1
  104. package/dist/Pagination/index.d.ts +1 -1
  105. package/dist/Pagination/pagination.types.d.ts +21 -2
  106. package/dist/Pagination/pagination.variants.d.ts +21 -387
  107. package/dist/Pagination/pagination.variants.js +63 -59
  108. package/dist/PinInput/PinInput.svelte +150 -0
  109. package/dist/PinInput/PinInput.svelte.d.ts +6 -0
  110. package/dist/PinInput/index.d.ts +2 -0
  111. package/dist/PinInput/index.js +1 -0
  112. package/dist/PinInput/pin-input.types.d.ts +99 -0
  113. package/dist/PinInput/pin-input.types.js +1 -0
  114. package/dist/PinInput/pin-input.variants.d.ts +303 -0
  115. package/dist/PinInput/pin-input.variants.js +196 -0
  116. package/dist/Popover/Popover.svelte +9 -12
  117. package/dist/Popover/Popover.svelte.d.ts +1 -1
  118. package/dist/Popover/popover.types.d.ts +4 -0
  119. package/dist/Popover/popover.variants.d.ts +5 -75
  120. package/dist/Popover/popover.variants.js +6 -16
  121. package/dist/Progress/Progress.svelte +58 -30
  122. package/dist/Progress/progress.types.d.ts +9 -1
  123. package/dist/Progress/progress.variants.d.ts +55 -25
  124. package/dist/Progress/progress.variants.js +34 -28
  125. package/dist/RadioGroup/RadioGroup.svelte +105 -61
  126. package/dist/RadioGroup/RadioGroup.svelte.d.ts +1 -1
  127. package/dist/RadioGroup/radio-group.types.d.ts +16 -1
  128. package/dist/RadioGroup/radio-group.variants.d.ts +90 -0
  129. package/dist/RadioGroup/radio-group.variants.js +73 -4
  130. package/dist/Select/Select.svelte +9 -6
  131. package/dist/Select/Select.svelte.d.ts +1 -1
  132. package/dist/Select/select.types.d.ts +4 -0
  133. package/dist/SelectMenu/SelectMenu.svelte +436 -0
  134. package/dist/SelectMenu/SelectMenu.svelte.d.ts +5 -0
  135. package/dist/SelectMenu/index.d.ts +2 -0
  136. package/dist/SelectMenu/index.js +1 -0
  137. package/dist/SelectMenu/select-menu.types.d.ts +262 -0
  138. package/dist/SelectMenu/select-menu.types.js +1 -0
  139. package/dist/SelectMenu/select-menu.variants.d.ts +759 -0
  140. package/dist/SelectMenu/select-menu.variants.js +33 -0
  141. package/dist/Separator/Separator.svelte +1 -2
  142. package/dist/Separator/separator.variants.d.ts +1 -5
  143. package/dist/Separator/separator.variants.js +2 -2
  144. package/dist/Skeleton/Skeleton.svelte +18 -2
  145. package/dist/Skeleton/Skeleton.svelte.d.ts +1 -1
  146. package/dist/Skeleton/skeleton.types.d.ts +10 -1
  147. package/dist/Slideover/Slideover.svelte +9 -1
  148. package/dist/Slideover/slideover.types.d.ts +5 -0
  149. package/dist/Slideover/slideover.variants.d.ts +20 -5
  150. package/dist/Slideover/slideover.variants.js +4 -29
  151. package/dist/Slider/Slider.svelte +135 -0
  152. package/dist/Slider/Slider.svelte.d.ts +6 -0
  153. package/dist/Slider/index.d.ts +2 -0
  154. package/dist/Slider/index.js +1 -0
  155. package/dist/Slider/slider.types.d.ts +55 -0
  156. package/dist/Slider/slider.types.js +1 -0
  157. package/dist/Slider/slider.variants.d.ts +383 -0
  158. package/dist/Slider/slider.variants.js +102 -0
  159. package/dist/Switch/Switch.svelte +32 -31
  160. package/dist/Switch/Switch.svelte.d.ts +1 -1
  161. package/dist/Switch/switch.types.d.ts +6 -1
  162. package/dist/Switch/switch.variants.js +6 -6
  163. package/dist/Tabs/Tabs.svelte +6 -9
  164. package/dist/Tabs/Tabs.svelte.d.ts +1 -1
  165. package/dist/Tabs/tabs.types.d.ts +4 -0
  166. package/dist/Tabs/tabs.variants.js +2 -0
  167. package/dist/Textarea/Textarea.svelte +26 -25
  168. package/dist/ThemeModeButton/theme-mode-button.types.d.ts +7 -2
  169. package/dist/Timeline/Timeline.svelte +62 -19
  170. package/dist/Timeline/Timeline.svelte.d.ts +1 -1
  171. package/dist/Timeline/index.d.ts +1 -1
  172. package/dist/Timeline/timeline.types.d.ts +8 -0
  173. package/dist/Tooltip/Tooltip.svelte +12 -10
  174. package/dist/Tooltip/Tooltip.svelte.d.ts +1 -1
  175. package/dist/Tooltip/tooltip.types.d.ts +8 -4
  176. package/dist/Tooltip/tooltip.variants.d.ts +10 -75
  177. package/dist/Tooltip/tooltip.variants.js +8 -17
  178. package/dist/User/User.svelte +13 -9
  179. package/dist/User/User.svelte.d.ts +1 -1
  180. package/dist/User/user.types.d.ts +4 -0
  181. package/dist/User/user.variants.d.ts +60 -0
  182. package/dist/User/user.variants.js +13 -1
  183. package/dist/config.d.ts +8 -0
  184. package/dist/config.js +9 -1
  185. package/dist/index.d.ts +5 -0
  186. package/dist/index.js +5 -0
  187. package/package.json +2 -2
@@ -0,0 +1,224 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const fileUploadVariants = tv({
3
+ slots: {
4
+ root: 'relative flex flex-col',
5
+ base: [
6
+ 'w-full bg-surface-container border border-outline-variant select-none',
7
+ 'flex flex-col items-stretch justify-center rounded-lg',
8
+ 'focus-visible:outline-2 focus-visible:outline-offset-2',
9
+ 'transition-colors duration-200'
10
+ ],
11
+ wrapper: 'flex flex-col items-center justify-center text-center',
12
+ icon: 'shrink-0 text-on-surface-variant',
13
+ label: 'font-medium text-on-surface mt-2',
14
+ description: 'text-on-surface-variant mt-1',
15
+ actions: 'flex flex-wrap gap-1.5 shrink-0 mt-4',
16
+ files: '',
17
+ file: 'relative',
18
+ fileLeading: 'shrink-0',
19
+ fileWrapper: 'flex flex-col min-w-0',
20
+ fileName: 'text-on-surface truncate',
21
+ fileSize: 'text-on-surface-variant truncate',
22
+ fileTrailing: '',
23
+ previewContent: '',
24
+ previewBody: ''
25
+ },
26
+ variants: {
27
+ color: {
28
+ primary: { base: 'focus-visible:outline-primary' },
29
+ secondary: { base: 'focus-visible:outline-secondary' },
30
+ tertiary: { base: 'focus-visible:outline-tertiary' },
31
+ success: { base: 'focus-visible:outline-success' },
32
+ warning: { base: 'focus-visible:outline-warning' },
33
+ error: { base: 'focus-visible:outline-error' },
34
+ info: { base: 'focus-visible:outline-info' },
35
+ surface: { base: 'focus-visible:outline-outline' }
36
+ },
37
+ variant: {
38
+ area: {
39
+ base: 'p-4 min-h-[120px]',
40
+ wrapper: 'py-6'
41
+ },
42
+ button: {}
43
+ },
44
+ size: {
45
+ xs: {
46
+ base: 'text-xs',
47
+ icon: 'size-5',
48
+ label: 'text-xs',
49
+ description: 'text-xs',
50
+ file: 'text-xs px-2 py-1 gap-1',
51
+ fileWrapper: 'flex-row gap-1'
52
+ },
53
+ sm: {
54
+ base: 'text-xs',
55
+ icon: 'size-6',
56
+ label: 'text-sm',
57
+ description: 'text-xs',
58
+ file: 'text-xs px-2.5 py-1.5 gap-1.5',
59
+ fileWrapper: 'flex-row gap-1'
60
+ },
61
+ md: {
62
+ base: 'text-sm',
63
+ icon: 'size-8',
64
+ label: 'text-sm',
65
+ description: 'text-sm',
66
+ file: 'text-xs px-2.5 py-1.5 gap-1.5'
67
+ },
68
+ lg: {
69
+ base: 'text-sm',
70
+ icon: 'size-10',
71
+ label: 'text-base',
72
+ description: 'text-sm',
73
+ file: 'text-sm px-3 py-2 gap-2',
74
+ fileSize: 'text-xs'
75
+ },
76
+ xl: {
77
+ base: 'text-base',
78
+ icon: 'size-12',
79
+ label: 'text-base',
80
+ description: 'text-base',
81
+ file: 'text-sm px-3 py-2 gap-2'
82
+ }
83
+ },
84
+ layout: {
85
+ list: {
86
+ root: 'gap-2 items-start',
87
+ files: 'flex flex-col w-full gap-1.5',
88
+ file: 'min-w-0 flex items-center bg-surface-container border border-outline-variant rounded-md w-full',
89
+ fileTrailing: 'ms-auto'
90
+ },
91
+ grid: {
92
+ root: 'gap-2',
93
+ files: 'grid grid-cols-2 gap-3 w-full',
94
+ file: 'relative aspect-square rounded-lg border border-outline-variant',
95
+ fileWrapper: 'hidden',
96
+ fileLeading: 'size-full',
97
+ fileTrailing: ''
98
+ }
99
+ },
100
+ dropzone: {
101
+ true: {
102
+ base: 'border-dashed data-[dragging=true]:bg-surface-container-high data-[dragging=true]:border-solid'
103
+ }
104
+ },
105
+ interactive: {
106
+ true: {
107
+ base: 'cursor-pointer hover:bg-surface-container-high'
108
+ }
109
+ },
110
+ highlight: {
111
+ true: {}
112
+ },
113
+ multiple: {
114
+ true: {}
115
+ },
116
+ disabled: {
117
+ true: {
118
+ base: 'cursor-not-allowed opacity-75 hover:bg-surface-container'
119
+ }
120
+ }
121
+ },
122
+ compoundVariants: [
123
+ // Highlight: colored border
124
+ { color: 'primary', highlight: true, class: { base: 'border-primary' } },
125
+ { color: 'secondary', highlight: true, class: { base: 'border-secondary' } },
126
+ { color: 'tertiary', highlight: true, class: { base: 'border-tertiary' } },
127
+ { color: 'success', highlight: true, class: { base: 'border-success' } },
128
+ { color: 'warning', highlight: true, class: { base: 'border-warning' } },
129
+ { color: 'error', highlight: true, class: { base: 'border-error' } },
130
+ { color: 'info', highlight: true, class: { base: 'border-info' } },
131
+ { color: 'surface', highlight: true, class: { base: 'border-outline' } },
132
+ // Dragging: colored border per color
133
+ {
134
+ color: 'primary',
135
+ dropzone: true,
136
+ class: { base: 'data-[dragging=true]:border-primary' }
137
+ },
138
+ {
139
+ color: 'secondary',
140
+ dropzone: true,
141
+ class: { base: 'data-[dragging=true]:border-secondary' }
142
+ },
143
+ {
144
+ color: 'tertiary',
145
+ dropzone: true,
146
+ class: { base: 'data-[dragging=true]:border-tertiary' }
147
+ },
148
+ {
149
+ color: 'success',
150
+ dropzone: true,
151
+ class: { base: 'data-[dragging=true]:border-success' }
152
+ },
153
+ {
154
+ color: 'warning',
155
+ dropzone: true,
156
+ class: { base: 'data-[dragging=true]:border-warning' }
157
+ },
158
+ { color: 'error', dropzone: true, class: { base: 'data-[dragging=true]:border-error' } },
159
+ { color: 'info', dropzone: true, class: { base: 'data-[dragging=true]:border-info' } },
160
+ // Button variant size padding
161
+ { variant: 'button', size: 'xs', class: { base: 'p-1' } },
162
+ { variant: 'button', size: 'sm', class: { base: 'p-1.5' } },
163
+ { variant: 'button', size: 'md', class: { base: 'p-1.5' } },
164
+ { variant: 'button', size: 'lg', class: { base: 'p-2' } },
165
+ { variant: 'button', size: 'xl', class: { base: 'p-2' } },
166
+ // Grid + multiple: 3 cols on larger screens, remove button outside top-right corner
167
+ {
168
+ layout: 'grid',
169
+ multiple: true,
170
+ class: { files: 'sm:grid-cols-3', fileTrailing: 'absolute -top-1.5 -end-1.5' }
171
+ },
172
+ // Grid + single: file overlays the base, remove button top-right inside
173
+ // variant:'area' guard is required — without it, variant='button' would also get
174
+ // absolute inset-0 on the external file item, making it invisible.
175
+ {
176
+ layout: 'grid',
177
+ multiple: false,
178
+ variant: 'area',
179
+ class: {
180
+ base: 'relative overflow-hidden',
181
+ file: 'absolute inset-0 p-0 border-0 rounded-none aspect-auto',
182
+ fileTrailing: 'absolute top-2 end-2'
183
+ }
184
+ },
185
+ // List size trailing adjustments
186
+ { size: 'xs', layout: 'list', class: { fileTrailing: '-me-1' } },
187
+ { size: 'sm', layout: 'list', class: { fileTrailing: '-me-1.5' } },
188
+ { size: 'md', layout: 'list', class: { fileTrailing: '-me-1.5' } },
189
+ { size: 'lg', layout: 'list', class: { fileTrailing: '-me-2' } },
190
+ { size: 'xl', layout: 'list', class: { fileTrailing: '-me-2' } }
191
+ ],
192
+ defaultVariants: {
193
+ color: 'primary',
194
+ variant: 'area',
195
+ size: 'md',
196
+ layout: 'list'
197
+ }
198
+ });
199
+ export const fileUploadDefaults = {
200
+ defaultVariants: {
201
+ color: 'primary',
202
+ variant: 'area',
203
+ size: 'md',
204
+ layout: 'list'
205
+ },
206
+ slots: {
207
+ root: '',
208
+ base: '',
209
+ wrapper: '',
210
+ icon: '',
211
+ label: '',
212
+ description: '',
213
+ actions: '',
214
+ files: '',
215
+ file: '',
216
+ fileLeading: '',
217
+ fileWrapper: '',
218
+ fileName: '',
219
+ fileSize: '',
220
+ fileTrailing: '',
221
+ previewContent: '',
222
+ previewBody: ''
223
+ }
224
+ };
@@ -0,0 +1,2 @@
1
+ export { default as FileUpload } from './FileUpload.svelte';
2
+ export type { FileUploadProps } from './file-upload.types.js';
@@ -0,0 +1 @@
1
+ export { default as FileUpload } from './FileUpload.svelte';
@@ -13,6 +13,7 @@
13
13
  const config = getComponentConfig('formField', formFieldDefaults)
14
14
 
15
15
  let {
16
+ ref = $bindable(null),
16
17
  ui,
17
18
  name,
18
19
  label,
@@ -36,23 +37,21 @@
36
37
  const id = useId()
37
38
  const ariaId = $derived(name ? `form-field-${name}` : id)
38
39
 
39
- const classes = $derived.by(() => {
40
- const slots = formFieldVariants({ size, required, orientation })
41
- return {
42
- root: slots.root({ class: [config.slots.root, className, ui?.root] }),
43
- wrapper: slots.wrapper({ class: [config.slots.wrapper, ui?.wrapper] }),
44
- labelWrapper: slots.labelWrapper({
45
- class: [config.slots.labelWrapper, ui?.labelWrapper]
46
- }),
47
- label: slots.label({ class: [config.slots.label, ui?.label] }),
48
- container: slots.container({ class: [config.slots.container, ui?.container] }),
49
- description: slots.description({
50
- class: [config.slots.description, ui?.description]
51
- }),
52
- error: slots.error({ class: [config.slots.error, ui?.error] }),
53
- hint: slots.hint({ class: [config.slots.hint, ui?.hint] }),
54
- help: slots.help({ class: [config.slots.help, ui?.help] })
55
- }
40
+ const variantSlots = $derived(formFieldVariants({ size, required, orientation }))
41
+ const classes = $derived({
42
+ root: variantSlots.root({ class: [config.slots.root, className, ui?.root] }),
43
+ wrapper: variantSlots.wrapper({ class: [config.slots.wrapper, ui?.wrapper] }),
44
+ labelWrapper: variantSlots.labelWrapper({
45
+ class: [config.slots.labelWrapper, ui?.labelWrapper]
46
+ }),
47
+ label: variantSlots.label({ class: [config.slots.label, ui?.label] }),
48
+ container: variantSlots.container({ class: [config.slots.container, ui?.container] }),
49
+ description: variantSlots.description({
50
+ class: [config.slots.description, ui?.description]
51
+ }),
52
+ error: variantSlots.error({ class: [config.slots.error, ui?.error] }),
53
+ hint: variantSlots.hint({ class: [config.slots.hint, ui?.hint] }),
54
+ help: variantSlots.help({ class: [config.slots.help, ui?.help] })
56
55
  })
57
56
 
58
57
  const hasError = $derived(error !== undefined && error !== false)
@@ -79,7 +78,7 @@
79
78
  })
80
79
  </script>
81
80
 
82
- <div class={classes.root} {...restProps}>
81
+ <div bind:this={ref} class={classes.root} {...restProps}>
83
82
  <div class={classes.wrapper}>
84
83
  {#if label || labelSlot || hint || hintSlot}
85
84
  <div class={classes.labelWrapper}>
@@ -1,5 +1,5 @@
1
1
  import type { FormFieldProps } from './form-field.types.js';
2
2
  export type Props = FormFieldProps;
3
- declare const FormField: import("svelte").Component<FormFieldProps, {}, "">;
3
+ declare const FormField: import("svelte").Component<FormFieldProps, {}, "ref">;
4
4
  type FormField = ReturnType<typeof FormField>;
5
5
  export default FormField;
@@ -3,6 +3,10 @@ import type { HTMLAttributes } from 'svelte/elements';
3
3
  import type { ClassNameValue } from 'tailwind-merge';
4
4
  import type { FormFieldSlots, FormFieldVariantProps } from './form-field.variants.js';
5
5
  export type FormFieldProps = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
6
+ /**
7
+ * Bindable reference to the root DOM element.
8
+ */
9
+ ref?: HTMLElement | null;
6
10
  /**
7
11
  * The name of the form field, used for matching form errors.
8
12
  */
@@ -6,6 +6,7 @@
6
6
 
7
7
  <script lang="ts">
8
8
  import Icon from '@iconify/svelte'
9
+ import { twMerge } from 'tailwind-merge'
9
10
 
10
11
  let {
11
12
  name,
@@ -18,14 +19,19 @@
18
19
  ...restProps
19
20
  }: Props = $props()
20
21
 
21
- const flip = $derived.by(() => {
22
- if (flipH && flipV) return 'horizontal,vertical'
23
- if (flipH) return 'horizontal'
24
- if (flipV) return 'vertical'
25
- return undefined
26
- })
22
+ const flip = $derived(
23
+ flipH && flipV
24
+ ? 'horizontal,vertical'
25
+ : flipH
26
+ ? 'horizontal'
27
+ : flipV
28
+ ? 'vertical'
29
+ : undefined
30
+ )
27
31
 
28
32
  const rotateValue = $derived(rotate ? rotate / 90 : undefined)
33
+
34
+ const iconClass = $derived(twMerge('shrink-0', className))
29
35
  </script>
30
36
 
31
37
  <Icon
@@ -35,6 +41,6 @@
35
41
  {color}
36
42
  {flip}
37
43
  rotate={rotateValue}
38
- class="shrink-0 {className ?? ''}"
44
+ class={iconClass}
39
45
  {...restProps}
40
46
  />
@@ -1,7 +1,6 @@
1
1
  import type { IconProps as IconifyProps } from '@iconify/svelte';
2
+ import type { ClassNameValue } from 'tailwind-merge';
2
3
  export interface IconProps extends Omit<IconifyProps, 'icon' | 'width' | 'height' | 'rotate' | 'flip' | 'class'> {
3
- /** Additional CSS classes */
4
- class?: string;
5
4
  /**
6
5
  * Icon name in Iconify format: "collection:icon-name"
7
6
  * @example "lucide:home", "mdi:account", "heroicons:star"
@@ -9,29 +8,39 @@ export interface IconProps extends Omit<IconifyProps, 'icon' | 'width' | 'height
9
8
  */
10
9
  name: string;
11
10
  /**
12
- * Icon size (applied to both width and height)
13
- * @default "1em"
11
+ * Icon size (applied to both width and height).
12
+ * Accepts a number (pixels) or CSS string value.
13
+ * @default 24
14
14
  * @example 24, "1.5rem", "20px"
15
15
  */
16
16
  size?: number | string;
17
17
  /**
18
- * Icon color (CSS color value)
19
- * @example "red", "#ff0000", "rgb(255, 0, 0)", "currentColor"
18
+ * Icon color (CSS color value).
19
+ * Defaults to `currentColor`, inheriting the parent's text color.
20
+ * Use Tailwind `text-*` classes on the parent or via `class` prop as an alternative.
21
+ * @default "currentColor"
22
+ * @example "red", "#ff0000", "rgb(255, 0, 0)"
20
23
  */
21
24
  color?: string;
22
25
  /**
23
- * Flip icon horizontally
26
+ * Flip icon horizontally.
24
27
  * @default false
25
28
  */
26
29
  flipH?: boolean;
27
30
  /**
28
- * Flip icon vertically
31
+ * Flip icon vertically.
29
32
  * @default false
30
33
  */
31
34
  flipV?: boolean;
32
35
  /**
33
- * Rotate icon by specified degrees
36
+ * Rotate icon by specified degrees (quarter turns only).
37
+ * For arbitrary rotation, use a CSS class like `rotate-45`.
34
38
  * @default 0
35
39
  */
36
40
  rotate?: 0 | 90 | 180 | 270;
41
+ /**
42
+ * Additional CSS classes for the icon.
43
+ * Merged with `shrink-0` via tailwind-merge, so conflicting utilities are resolved correctly.
44
+ */
45
+ class?: ClassNameValue;
37
46
  }
@@ -94,15 +94,16 @@
94
94
  loading && isTrailing ? loadingIcon : trailingIcon || (trailing ? icon : undefined)
95
95
  )
96
96
 
97
- const ariaDescribedBy = $derived.by(() => {
98
- if (!formFieldContext) return undefined
99
- const id = formFieldContext.ariaId
100
- if (hasError) return `${id}-error`
101
- return `${id}-description ${id}-help`
102
- })
97
+ const ariaDescribedBy = $derived(
98
+ !formFieldContext
99
+ ? undefined
100
+ : hasError
101
+ ? `${formFieldContext.ariaId}-error`
102
+ : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`
103
+ )
103
104
 
104
- const classes = $derived.by(() => {
105
- const slots = inputVariants({
105
+ const variantSlots = $derived(
106
+ inputVariants({
106
107
  variant,
107
108
  color: resolvedColor,
108
109
  size: resolvedSize,
@@ -111,26 +112,26 @@
111
112
  loading,
112
113
  highlight: resolvedHighlight
113
114
  })
114
- return {
115
- root: slots.root({
116
- class: [config.slots.root, fieldGroupClass?.root, className, ui?.root]
117
- }),
118
- base: slots.base({
119
- class: [config.slots.base, fieldGroupClass?.base, ui?.base]
120
- }),
121
- leading: slots.leading({ class: [config.slots.leading, ui?.leading] }),
122
- leadingIcon: slots.leadingIcon({
123
- class: [config.slots.leadingIcon, ui?.leadingIcon]
124
- }),
125
- leadingAvatar: slots.leadingAvatar({
126
- class: [config.slots.leadingAvatar, ui?.leadingAvatar]
127
- }),
128
- leadingAvatarSize: slots.leadingAvatarSize() as AvatarSize,
129
- trailing: slots.trailing({ class: [config.slots.trailing, ui?.trailing] }),
130
- trailingIcon: slots.trailingIcon({
131
- class: [config.slots.trailingIcon, ui?.trailingIcon]
132
- })
133
- }
115
+ )
116
+ const classes = $derived({
117
+ root: variantSlots.root({
118
+ class: [config.slots.root, fieldGroupClass?.root, className, ui?.root]
119
+ }),
120
+ base: variantSlots.base({
121
+ class: [config.slots.base, fieldGroupClass?.base, ui?.base]
122
+ }),
123
+ leading: variantSlots.leading({ class: [config.slots.leading, ui?.leading] }),
124
+ leadingIcon: variantSlots.leadingIcon({
125
+ class: [config.slots.leadingIcon, ui?.leadingIcon]
126
+ }),
127
+ leadingAvatar: variantSlots.leadingAvatar({
128
+ class: [config.slots.leadingAvatar, ui?.leadingAvatar]
129
+ }),
130
+ leadingAvatarSize: variantSlots.leadingAvatarSize() as AvatarSize,
131
+ trailing: variantSlots.trailing({ class: [config.slots.trailing, ui?.trailing] }),
132
+ trailingIcon: variantSlots.trailingIcon({
133
+ class: [config.slots.trailingIcon, ui?.trailingIcon]
134
+ })
134
135
  })
135
136
  </script>
136
137
 
@@ -150,6 +151,7 @@
150
151
  {/if}
151
152
 
152
153
  <input
154
+ {...restProps}
153
155
  bind:this={ref}
154
156
  bind:value
155
157
  {type}
@@ -159,7 +161,6 @@
159
161
  aria-describedby={ariaDescribedBy}
160
162
  aria-invalid={resolvedHighlight ? true : undefined}
161
163
  class={classes.base}
162
- {...restProps}
163
164
  />
164
165
 
165
166
  {#if trailingSlot}
@@ -6,27 +6,37 @@
6
6
  </script>
7
7
 
8
8
  <script lang="ts">
9
+ import { onMount } from 'svelte'
9
10
  import { kbdVariants, kbdDefaults } from './kbd.variants.js'
10
11
  import { getComponentConfig } from '../config.js'
11
12
 
12
13
  const config = getComponentConfig('kbd', kbdDefaults)
13
14
 
14
15
  let {
16
+ ref = $bindable(null),
15
17
  as = 'kbd',
16
18
  value,
17
19
  color = config.defaultVariants.color,
18
20
  size = config.defaultVariants.size,
19
21
  variant = config.defaultVariants.variant,
22
+ ui,
20
23
  class: className,
21
24
  children,
22
25
  ...restProps
23
26
  }: Props = $props()
24
27
 
25
- const displayValue = $derived(resolveKey(value))
26
- const kbdClass = $derived(kbdVariants({ color, size, variant, class: className }))
28
+ let mounted = $state(false)
29
+ onMount(() => (mounted = true))
30
+
31
+ const displayValue = $derived(resolveKey(value, mounted))
32
+ const kbdClass = $derived(
33
+ kbdVariants({ color, size, variant }).base({
34
+ class: [config.slots.base, className, ui?.base]
35
+ })
36
+ )
27
37
  </script>
28
38
 
29
- <svelte:element this={as} class={kbdClass} {...restProps}>
39
+ <svelte:element this={as} bind:this={ref} class={kbdClass} {...restProps}>
30
40
  {#if children}
31
41
  {@render children()}
32
42
  {:else if displayValue}
@@ -1,5 +1,5 @@
1
1
  import type { KbdProps } from './kbd.types.js';
2
2
  export type Props = KbdProps;
3
- declare const Kbd: import("svelte").Component<KbdProps, {}, "">;
3
+ declare const Kbd: import("svelte").Component<KbdProps, {}, "ref">;
4
4
  type Kbd = ReturnType<typeof Kbd>;
5
5
  export default Kbd;
@@ -1,3 +1,3 @@
1
1
  export { default as Kbd } from './Kbd.svelte';
2
- export type { KbdProps } from './kbd.types.js';
2
+ export type { KbdProps, UseKbdOptions, UseKbdReturn } from './kbd.types.js';
3
3
  export { formatShortcut, useKbd } from './useKbd.svelte.js';
@@ -1,6 +1,7 @@
1
+ import type { Snippet } from 'svelte';
1
2
  import type { HTMLAttributes } from 'svelte/elements';
2
3
  import type { ClassNameValue } from 'tailwind-merge';
3
- import type { KbdVariantProps } from './kbd.variants.js';
4
+ import type { KbdVariantProps, KbdSlots } from './kbd.variants.js';
4
5
  export type KeyboardShortcutCallback = (event: KeyboardEvent) => void;
5
6
  export interface UseKbdOptions {
6
7
  /**
@@ -43,6 +44,10 @@ export interface UseKbdReturn {
43
44
  readonly pressedKeys: ReadonlySet<string>;
44
45
  }
45
46
  export type KbdProps = Omit<HTMLAttributes<HTMLElement>, 'class'> & {
47
+ /**
48
+ * Bindable reference to the root DOM element.
49
+ */
50
+ ref?: HTMLElement | null;
46
51
  /**
47
52
  * Sets the HTML element type to render.
48
53
  * @default 'kbd'
@@ -70,8 +75,17 @@ export type KbdProps = Omit<HTMLAttributes<HTMLElement>, 'class'> & {
70
75
  * @default 'outline'
71
76
  */
72
77
  variant?: NonNullable<KbdVariantProps['variant']>;
78
+ /**
79
+ * Override styles for specific kbd slots.
80
+ * Available slots: base.
81
+ */
82
+ ui?: Partial<Record<KbdSlots, ClassNameValue>>;
73
83
  /**
74
84
  * Additional CSS classes for the root element.
75
85
  */
76
86
  class?: ClassNameValue;
87
+ /**
88
+ * Default content slot for custom key display.
89
+ */
90
+ children?: Snippet;
77
91
  };