svelora 3.0.0 → 3.0.2

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 (109) hide show
  1. package/dist/Accordion/Accordion.svelte +66 -97
  2. package/dist/Alert/Alert.svelte +39 -64
  3. package/dist/Alert/Alert.svelte.d.ts +1 -1
  4. package/dist/Avatar/Avatar.svelte +35 -75
  5. package/dist/AvatarGroup/AvatarGroup.svelte +38 -55
  6. package/dist/Badge/Badge.svelte +28 -50
  7. package/dist/Banner/Banner.svelte +46 -41
  8. package/dist/Banner/Banner.svelte.d.ts +1 -1
  9. package/dist/Breadcrumb/Breadcrumb.svelte +32 -26
  10. package/dist/Button/Button.svelte +70 -138
  11. package/dist/Calendar/Calendar.svelte +94 -157
  12. package/dist/Calendar/Calendar.svelte.d.ts +1 -1
  13. package/dist/Card/Card.svelte +18 -31
  14. package/dist/Carousel/Carousel.svelte +118 -173
  15. package/dist/Checkbox/Checkbox.svelte +52 -97
  16. package/dist/CheckboxGroup/CheckboxGroup.svelte +62 -107
  17. package/dist/CheckboxGroup/CheckboxGroup.svelte.d.ts +1 -1
  18. package/dist/Chip/Chip.svelte +22 -34
  19. package/dist/CodeBlock/CodeBlock.svelte +42 -59
  20. package/dist/Collapsible/Collapsible.svelte +22 -38
  21. package/dist/Collapsible/Collapsible.svelte.d.ts +1 -1
  22. package/dist/Collapsible/CollapsibleTestWrapper.svelte +2 -5
  23. package/dist/Collapsible/CollapsibleTestWrapper.svelte.d.ts +1 -1
  24. package/dist/Command/Command.svelte +40 -77
  25. package/dist/Command/Command.svelte.d.ts +1 -1
  26. package/dist/Command/CommandTestWrapper.svelte +2 -10
  27. package/dist/Command/CommandTestWrapper.svelte.d.ts +1 -1
  28. package/dist/Container/Container.svelte +11 -14
  29. package/dist/ContextMenu/ContextMenu.svelte +51 -114
  30. package/dist/ContextMenu/ContextMenu.svelte.d.ts +1 -1
  31. package/dist/Drawer/Drawer.svelte +72 -110
  32. package/dist/Drawer/DrawerTriggerTestWrapper.svelte +1 -2
  33. package/dist/DropdownMenu/DropdownMenu.svelte +63 -124
  34. package/dist/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
  35. package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte +2 -5
  36. package/dist/Editor/Editor.svelte +441 -576
  37. package/dist/Editor/Editor.svelte.d.ts +1 -1
  38. package/dist/Editor/EditorUrlPrompt.svelte +40 -53
  39. package/dist/Editor/SlashPopup.svelte +12 -24
  40. package/dist/Empty/Empty.svelte +32 -63
  41. package/dist/FieldGroup/FieldGroup.svelte +23 -38
  42. package/dist/FileUpload/FileUpload.svelte +242 -320
  43. package/dist/FileUpload/FileUpload.svelte.d.ts +1 -1
  44. package/dist/Fonts/Fonts.svelte +15 -37
  45. package/dist/Form/Form.svelte +112 -170
  46. package/dist/FormField/FormField.svelte +102 -135
  47. package/dist/Icon/Icon.svelte +7 -32
  48. package/dist/Input/Input.svelte +71 -141
  49. package/dist/Input/Input.svelte.d.ts +2 -2
  50. package/dist/Kbd/Kbd.svelte +18 -34
  51. package/dist/Link/Link.svelte +129 -196
  52. package/dist/LocaleButton/LocaleButton.svelte +165 -0
  53. package/dist/LocaleButton/LocaleButton.svelte.d.ts +5 -0
  54. package/dist/LocaleButton/index.d.ts +2 -0
  55. package/dist/LocaleButton/index.js +1 -0
  56. package/dist/LocaleButton/locale-button.types.d.ts +182 -0
  57. package/dist/LocaleButton/locale-button.types.js +1 -0
  58. package/dist/LocaleButton/locale-button.variants.d.ts +61 -0
  59. package/dist/LocaleButton/locale-button.variants.js +34 -0
  60. package/dist/Modal/Modal.svelte +52 -106
  61. package/dist/Modal/ModalTriggerTestWrapper.svelte +1 -2
  62. package/dist/Pagination/Pagination.svelte +48 -92
  63. package/dist/Pagination/pagination.variants.d.ts +1 -1
  64. package/dist/PinInput/PinInput.svelte +57 -111
  65. package/dist/PinInput/PinInput.svelte.d.ts +1 -1
  66. package/dist/Popover/Popover.svelte +28 -61
  67. package/dist/Popover/Popover.svelte.d.ts +1 -1
  68. package/dist/Progress/Progress.svelte +75 -94
  69. package/dist/RadioGroup/RadioGroup.svelte +54 -99
  70. package/dist/RadioGroup/RadioGroup.svelte.d.ts +1 -1
  71. package/dist/Select/Select.svelte +112 -269
  72. package/dist/Select/Select.svelte.d.ts +1 -1
  73. package/dist/SelectMenu/SelectMenu.svelte +211 -409
  74. package/dist/SelectMenu/SelectMenu.svelte.d.ts +1 -1
  75. package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte +3 -6
  76. package/dist/Separator/Separator.svelte +29 -44
  77. package/dist/Skeleton/Skeleton.svelte +11 -23
  78. package/dist/Slideover/Slideover.svelte +52 -106
  79. package/dist/Slideover/SlideoverTriggerTestWrapper.svelte +1 -2
  80. package/dist/Slider/Slider.svelte +48 -84
  81. package/dist/Slider/Slider.svelte.d.ts +1 -1
  82. package/dist/Stepper/Stepper.svelte +139 -132
  83. package/dist/Stepper/Stepper.svelte.d.ts +1 -1
  84. package/dist/Switch/Switch.svelte +62 -98
  85. package/dist/Table/Table.svelte +232 -283
  86. package/dist/Table/table.variants.d.ts +1 -1
  87. package/dist/Tabs/Tabs.svelte +96 -129
  88. package/dist/Tabs/Tabs.svelte.d.ts +1 -1
  89. package/dist/Textarea/Textarea.svelte +90 -173
  90. package/dist/Textarea/Textarea.svelte.d.ts +1 -1
  91. package/dist/ThemeModeButton/ThemeModeButton.svelte +16 -38
  92. package/dist/Timeline/Timeline.svelte +75 -54
  93. package/dist/Toast/Toaster.svelte +8 -25
  94. package/dist/Tooltip/Tooltip.svelte +34 -66
  95. package/dist/Tooltip/Tooltip.svelte.d.ts +1 -1
  96. package/dist/Tooltip/TooltipTestWrapper.svelte +2 -5
  97. package/dist/User/User.svelte +33 -49
  98. package/dist/docs/navigation.d.ts +1 -1
  99. package/dist/docs/navigation.js +8 -1
  100. package/dist/hooks/HookContextProbe.svelte +2 -4
  101. package/dist/hooks/HookContextProvider.svelte +8 -6
  102. package/dist/hooks/HookEmitProbe.svelte +8 -11
  103. package/dist/i18n.d.ts +2 -0
  104. package/dist/i18n.js +19 -0
  105. package/dist/index.d.ts +1 -0
  106. package/dist/index.js +1 -0
  107. package/dist/mcp/svelora-docs.data.json +4 -2
  108. package/dist/theme.css +1 -1
  109. package/package.json +16 -8
@@ -1,104 +1,59 @@
1
- <script lang="ts" module>
2
- import type { RadioGroupProps } from './radio-group.types.js'
3
-
4
- export type Props = RadioGroupProps
1
+ <script lang="ts" module>export {};
5
2
  </script>
6
3
 
7
- <script lang="ts">
8
- import { Label, RadioGroup, useId } from 'bits-ui'
9
- import { getComponentConfig, iconsDefaults } from '../config.js'
10
- import { useFormField, useFormFieldEmit } from '../hooks/useFormField.svelte.js'
11
- import Icon from '../Icon/Icon.svelte'
12
- import type { RadioGroupItem } from './radio-group.types.js'
13
- import { radioGroupDefaults, radioGroupVariants } from './radio-group.variants.js'
14
-
15
- const config = getComponentConfig('radioGroup', radioGroupDefaults)
16
- const icons = getComponentConfig('icons', iconsDefaults)
17
-
18
- let {
19
- ref = $bindable(null),
20
- value = $bindable(''),
21
- onValueChange,
22
- items = [],
23
- ui,
24
- id,
25
- name,
26
- color = config.defaultVariants.color,
27
- size,
28
- variant = config.defaultVariants.variant,
29
- indicator = config.defaultVariants.indicator,
30
- orientation = config.defaultVariants.orientation,
31
- disabled = false,
32
- required = false,
33
- readonly = false,
34
- loop = true,
35
- loading = false,
36
- loadingIcon = icons.loading,
37
- legend,
38
- legendSlot,
39
- labelSlot,
40
- descriptionSlot,
41
- class: className,
42
- ...restProps
43
- }: Props = $props()
44
-
45
- const formFieldContext = useFormField()
46
- const emit = useFormFieldEmit()
47
-
48
- const hasError = $derived(
49
- formFieldContext?.error !== undefined && formFieldContext?.error !== false
50
- )
51
- const resolvedSize = $derived(size ?? formFieldContext?.size ?? config.defaultVariants.size)
52
- const resolvedColor = $derived(hasError ? 'error' : color)
53
- const autoId = useId()
54
- const resolvedId = $derived(id ?? formFieldContext?.ariaId ?? autoId)
55
- const resolvedName = $derived(name ?? formFieldContext?.name)
56
- const isDisabled = $derived(disabled || loading)
57
- const legendId = $derived(`${resolvedId}-legend`)
58
-
59
- const ariaDescribedBy = $derived(
60
- !formFieldContext
61
- ? undefined
62
- : hasError
63
- ? `${formFieldContext.ariaId}-error`
64
- : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`
65
- )
66
-
67
- const slots = $derived(
68
- radioGroupVariants({
69
- color: resolvedColor,
70
- size: resolvedSize,
71
- variant,
72
- indicator,
73
- orientation,
74
- loading,
75
- required,
76
- disabled: isDisabled ? true : undefined
77
- })
78
- )
79
-
80
- const layoutClasses = $derived.by(() => ({
81
- root: slots.root({ class: [config.slots.root, className, ui?.root] }),
82
- fieldset: slots.fieldset({ class: [config.slots.fieldset, ui?.fieldset] }),
83
- legend: slots.legend({ class: [config.slots.legend, ui?.legend] }),
84
- item: slots.item({ class: [config.slots.item, ui?.item] }),
85
- container: slots.container({ class: [config.slots.container, ui?.container] }),
86
- wrapper: slots.wrapper({ class: [config.slots.wrapper, ui?.wrapper] })
87
- }))
88
-
89
- const elementClasses = $derived.by(() => ({
90
- base: slots.base({ class: [config.slots.base, ui?.base] }),
91
- indicator: slots.indicator({ class: [config.slots.indicator, ui?.indicator] }),
92
- loadingIcon: slots.loadingIcon({ class: [config.slots.loadingIcon, ui?.loadingIcon] }),
93
- label: slots.label({ class: [config.slots.label, ui?.label] }),
94
- description: slots.description({ class: [config.slots.description, ui?.description] })
95
- }))
96
-
97
- function handleCardItemClick(e: MouseEvent, btnId: string, itemDisabled: boolean) {
98
- if (itemDisabled) return
99
- if ((e.target as Element).closest('button')) return
100
- document.getElementById(btnId)?.click()
101
- }
4
+ <script lang="ts">import { Label, RadioGroup, useId } from "bits-ui";
5
+ import { getComponentConfig, iconsDefaults } from "../config.js";
6
+ import { useFormField, useFormFieldEmit } from "../hooks/useFormField.svelte.js";
7
+ import Icon from "../Icon/Icon.svelte";
8
+ import { radioGroupDefaults, radioGroupVariants } from "./radio-group.variants.js";
9
+ const config = getComponentConfig("radioGroup", radioGroupDefaults);
10
+ const icons = getComponentConfig("icons", iconsDefaults);
11
+ let { ref = $bindable(null), value = $bindable(""), onValueChange, items = [], ui, id, name, color = config.defaultVariants.color, size, variant = config.defaultVariants.variant, indicator = config.defaultVariants.indicator, orientation = config.defaultVariants.orientation, disabled = false, required = false, readonly = false, loop = true, loading = false, loadingIcon = icons.loading, legend, legendSlot, labelSlot, descriptionSlot, class: className, ...restProps } = $props();
12
+ const formFieldContext = useFormField();
13
+ const emit = useFormFieldEmit();
14
+ const hasError = $derived(formFieldContext?.error !== undefined && formFieldContext?.error !== false);
15
+ const resolvedSize = $derived(size ?? formFieldContext?.size ?? config.defaultVariants.size);
16
+ const resolvedColor = $derived(hasError ? "error" : color);
17
+ const autoId = useId();
18
+ const resolvedId = $derived(id ?? formFieldContext?.ariaId ?? autoId);
19
+ const resolvedName = $derived(name ?? formFieldContext?.name);
20
+ const isDisabled = $derived(disabled || loading);
21
+ const legendId = $derived(`${resolvedId}-legend`);
22
+ const ariaDescribedBy = $derived(!formFieldContext ? undefined : hasError ? `${formFieldContext.ariaId}-error` : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`);
23
+ const slots = $derived(radioGroupVariants({
24
+ color: resolvedColor,
25
+ size: resolvedSize,
26
+ variant,
27
+ indicator,
28
+ orientation,
29
+ loading,
30
+ required,
31
+ disabled: isDisabled ? true : undefined
32
+ }));
33
+ const layoutClasses = $derived.by(() => ({
34
+ root: slots.root({ class: [
35
+ config.slots.root,
36
+ className,
37
+ ui?.root
38
+ ] }),
39
+ fieldset: slots.fieldset({ class: [config.slots.fieldset, ui?.fieldset] }),
40
+ legend: slots.legend({ class: [config.slots.legend, ui?.legend] }),
41
+ item: slots.item({ class: [config.slots.item, ui?.item] }),
42
+ container: slots.container({ class: [config.slots.container, ui?.container] }),
43
+ wrapper: slots.wrapper({ class: [config.slots.wrapper, ui?.wrapper] })
44
+ }));
45
+ const elementClasses = $derived.by(() => ({
46
+ base: slots.base({ class: [config.slots.base, ui?.base] }),
47
+ indicator: slots.indicator({ class: [config.slots.indicator, ui?.indicator] }),
48
+ loadingIcon: slots.loadingIcon({ class: [config.slots.loadingIcon, ui?.loadingIcon] }),
49
+ label: slots.label({ class: [config.slots.label, ui?.label] }),
50
+ description: slots.description({ class: [config.slots.description, ui?.description] })
51
+ }));
52
+ function handleCardItemClick(e, btnId, itemDisabled) {
53
+ if (itemDisabled) return;
54
+ if (e.target.closest("button")) return;
55
+ document.getElementById(btnId)?.click();
56
+ }
102
57
  </script>
103
58
 
104
59
  {#snippet itemContent(radioItem: RadioGroupItem)}
@@ -1,6 +1,6 @@
1
1
  import type { RadioGroupProps } from './radio-group.types.js';
2
2
  export type Props = RadioGroupProps;
3
3
  import { RadioGroup } from 'bits-ui';
4
- declare const RadioGroup: import("svelte").Component<RadioGroupProps, {}, "value" | "ref">;
4
+ declare const RadioGroup: import("svelte").Component<RadioGroupProps, {}, "ref" | "value">;
5
5
  type RadioGroup = ReturnType<typeof RadioGroup>;
6
6
  export default RadioGroup;
@@ -1,274 +1,117 @@
1
- <script lang="ts" module>
2
- import type { SelectItem, SelectItemType, SelectProps } from './select.types.js'
3
-
4
- export type Props = SelectProps
1
+ <script lang="ts" module>export {};
5
2
  </script>
6
3
 
7
- <script lang="ts">
8
- import { Select } from 'bits-ui'
9
- import { getContext } from 'svelte'
10
- import Avatar from '../Avatar/Avatar.svelte'
11
- import type { AvatarSize } from '../Avatar/avatar.types.js'
12
- import { getComponentConfig, iconsDefaults } from '../config.js'
13
- import {
14
- type FieldGroupVariantProps,
15
- fieldGroupVariantWithRoot
16
- } from '../FieldGroup/field-group.variants.js'
17
- import { useFormField, useFormFieldEmit } from '../hooks/useFormField.svelte.js'
18
- import Icon from '../Icon/Icon.svelte'
19
- import { selectDefaults, selectVariants } from './select.variants.js'
20
-
21
- const config = getComponentConfig('select', selectDefaults)
22
- const icons = getComponentConfig('icons', iconsDefaults)
23
-
24
- let {
25
- ref = $bindable(null),
26
- value = $bindable(),
27
- open = $bindable(false),
28
- onOpenChange,
29
- items = [],
30
- placeholder,
31
- name,
32
- required = false,
33
- disabled = false,
34
- multiple = false,
35
- separator = ', ',
36
- ui,
37
- id,
38
- color = config.defaultVariants.color,
39
- variant = config.defaultVariants.variant,
40
- size,
41
- highlight,
42
- loading = false,
43
- loadingIcon = icons.loading,
44
- icon,
45
- leadingIcon,
46
- trailingIcon = icons.chevronDown,
47
- selectedIcon = icons.check,
48
- avatar,
49
- transition = config.defaultVariants.transition ?? true,
50
- portal = true,
51
- side = config.defaultVariants.side ?? 'bottom',
52
- sideOffset = 4,
53
- align = 'start',
54
- alignOffset = 0,
55
- avoidCollisions = true,
56
- collisionBoundary,
57
- collisionPadding = 8,
58
- onEscapeKeydown,
59
- onInteractOutside,
60
- forceMount,
61
- loop = true,
62
- class: className,
63
- leadingSlot,
64
- trailingSlot,
65
- item: itemSlot,
66
- itemLeading,
67
- itemLabel: itemLabelSlot,
68
- itemTrailing,
69
- selected: selectedSlot,
70
- content: contentSlot,
71
- ...restProps
72
- }: Props = $props()
73
-
74
- // ---- Form context ----
75
- const formFieldContext = useFormField()
76
- const emit = useFormFieldEmit()
77
-
78
- const fieldGroupContext = getContext<
79
- | {
80
- orientation: NonNullable<FieldGroupVariantProps['orientation']>
81
- size: NonNullable<FieldGroupVariantProps['size']>
82
- }
83
- | undefined
84
- >('fieldGroup')
85
-
86
- const hasError = $derived(
87
- formFieldContext?.error !== undefined && formFieldContext?.error !== false
88
- )
89
- const resolvedSize = $derived(
90
- size ?? formFieldContext?.size ?? fieldGroupContext?.size ?? config.defaultVariants.size
91
- )
92
- const resolvedColor = $derived(hasError ? 'error' : color)
93
- const resolvedHighlight = $derived(highlight ?? hasError)
94
- const fieldGroupClass = $derived(
95
- fieldGroupContext
96
- ? fieldGroupVariantWithRoot.fieldGroup[fieldGroupContext.orientation ?? 'horizontal']
97
- : undefined
98
- )
99
- const resolvedId = $derived(id ?? formFieldContext?.ariaId)
100
- const resolvedName = $derived(name ?? formFieldContext?.name)
101
-
102
- // ---- ARIA ----
103
- const ariaDescribedBy = $derived(
104
- !formFieldContext
105
- ? undefined
106
- : hasError
107
- ? `${formFieldContext.ariaId}-error`
108
- : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`
109
- )
110
-
111
- // ---- Items lookup (O(1) via Map) ----
112
- const itemsMap = $derived(
113
- new Map(
114
- (items as SelectItemType[])
115
- .filter((i): i is SelectItem => !('type' in i))
116
- .map((i) => [i.value, i])
117
- )
118
- )
119
-
120
- // ---- Selection (single + multiple) ----
121
- const selectedValues = $derived(
122
- multiple
123
- ? Array.isArray(value)
124
- ? (value as string[])
125
- : []
126
- : typeof value === 'string' && value !== ''
127
- ? [value]
128
- : []
129
- )
130
- const selectedItems = $derived(
131
- selectedValues.map((v) => itemsMap.get(v)).filter((i): i is SelectItem => i !== undefined)
132
- )
133
- const hasSelection = $derived(selectedValues.length > 0)
134
- const singleSelectedItem = $derived(multiple ? undefined : selectedItems[0])
135
- const displayLabel = $derived(
136
- multiple
137
- ? selectedItems.map((i) => i.label ?? i.value).join(separator)
138
- : (singleSelectedItem?.label ?? singleSelectedItem?.value ?? '')
139
- )
140
-
141
- function removeValue(val: string) {
142
- if (!multiple) return
143
- value = selectedValues.filter((v) => v !== val)
144
- emit.onChange()
145
- }
146
-
147
- function clearSelection() {
148
- if (!multiple) return
149
- value = []
150
- emit.onChange()
151
- }
152
-
153
- // ---- Leading / trailing ----
154
- const displayAvatar = $derived(multiple ? avatar : (singleSelectedItem?.avatar ?? avatar))
155
- const displayIcon = $derived(
156
- multiple ? (leadingIcon ?? icon) : (singleSelectedItem?.icon ?? leadingIcon ?? icon)
157
- )
158
- const isLeading = $derived(!!leadingSlot || !!displayAvatar || !!displayIcon)
159
- const leadingIconName = $derived(
160
- loading && isLeading ? loadingIcon : !displayAvatar ? displayIcon : undefined
161
- )
162
-
163
- // ---- bits-ui items for typeahead ----
164
- const bitsItems = $derived(
165
- [...itemsMap.values()].map((i) => ({
166
- value: i.value,
167
- label: i.label ?? i.value,
168
- disabled: i.disabled
169
- }))
170
- )
171
-
172
- // ---- Trailing icon ----
173
- const trailingIconName = $derived(loading && !isLeading ? loadingIcon : trailingIcon)
174
- const trailingIconClass = $derived(
175
- `${loading && !isLeading ? 'animate-spin' : 'transition-transform'} ${open && !loading ? 'rotate-180' : ''}`
176
- )
177
-
178
- // ---- Variant slots ----
179
- const variantSlots = $derived(
180
- selectVariants({
181
- variant,
182
- color: resolvedColor,
183
- size: resolvedSize,
184
- leading: isLeading,
185
- trailing: true,
186
- loading,
187
- highlight: resolvedHighlight,
188
- side,
189
- transition
190
- })
191
- )
192
-
193
- // ---- Trigger classes (root, base, leading, trailing, value, placeholder) ----
194
- const rootClass = $derived(
195
- variantSlots.root({
196
- class: [config.slots.root, fieldGroupClass?.root, className, ui?.root]
197
- })
198
- )
199
- const baseClass = $derived(
200
- variantSlots.base({
201
- class: [config.slots.base, fieldGroupClass?.base, ui?.base]
202
- })
203
- )
204
- const leadingClass = $derived(
205
- variantSlots.leading({ class: [config.slots.leading, ui?.leading] })
206
- )
207
- const leadingIconClass = $derived(
208
- variantSlots.leadingIcon({ class: [config.slots.leadingIcon, ui?.leadingIcon] })
209
- )
210
- const leadingAvatarClass = $derived(
211
- variantSlots.leadingAvatar({ class: [config.slots.leadingAvatar, ui?.leadingAvatar] })
212
- )
213
- const leadingAvatarSizeClass = $derived(variantSlots.leadingAvatarSize() as AvatarSize)
214
- const trailingClass = $derived(
215
- variantSlots.trailing({ class: [config.slots.trailing, ui?.trailing] })
216
- )
217
- const trailingIconBaseClass = $derived(
218
- variantSlots.trailingIcon({ class: [config.slots.trailingIcon, ui?.trailingIcon] })
219
- )
220
- const valueClass = $derived(variantSlots.value({ class: [config.slots.value, ui?.value] }))
221
- const placeholderClass = $derived(
222
- variantSlots.placeholder({ class: [config.slots.placeholder, ui?.placeholder] })
223
- )
224
-
225
- // ---- Content classes ----
226
- const contentClass = $derived(
227
- variantSlots.content({ class: [config.slots.content, ui?.content] })
228
- )
229
- const viewportClass = $derived(
230
- variantSlots.viewport({ class: [config.slots.viewport, ui?.viewport] })
231
- )
232
- const groupLabelClass = $derived(
233
- variantSlots.groupLabel({ class: [config.slots.groupLabel, ui?.groupLabel] })
234
- )
235
- const separatorClass = $derived(
236
- variantSlots.separator({ class: [config.slots.separator, ui?.separator] })
237
- )
238
-
239
- // ---- Item classes ----
240
- const itemClass = $derived(variantSlots.item({ class: [config.slots.item, ui?.item] }))
241
- const itemIconClass = $derived(
242
- variantSlots.itemIcon({ class: [config.slots.itemIcon, ui?.itemIcon] })
243
- )
244
- const itemAvatarClass = $derived(
245
- variantSlots.itemAvatar({ class: [config.slots.itemAvatar, ui?.itemAvatar] })
246
- )
247
- const itemAvatarSizeClass = $derived(variantSlots.itemAvatarSize() as AvatarSize)
248
- const itemLabelClass = $derived(
249
- variantSlots.itemLabel({ class: [config.slots.itemLabel, ui?.itemLabel] })
250
- )
251
- const itemDescriptionClass = $derived(
252
- variantSlots.itemDescription({
253
- class: [config.slots.itemDescription, ui?.itemDescription]
254
- })
255
- )
256
- const itemIndicatorClass = $derived(
257
- variantSlots.itemIndicator({ class: [config.slots.itemIndicator, ui?.itemIndicator] })
258
- )
259
-
260
- // ---- Type guards ----
261
- function isSelectItem(item: SelectItemType): item is SelectItem {
262
- return !('type' in item)
263
- }
264
-
265
- function isSeparator(item: SelectItemType): item is { type: 'separator' } {
266
- return 'type' in item && item.type === 'separator'
267
- }
268
-
269
- function isLabel(item: SelectItemType): item is { type: 'label'; label: string } {
270
- return 'type' in item && item.type === 'label'
271
- }
4
+ <script lang="ts">import { Select } from "bits-ui";
5
+ import { getContext } from "svelte";
6
+ import Avatar from "../Avatar/Avatar.svelte";
7
+ import { getComponentConfig, iconsDefaults } from "../config.js";
8
+ import { fieldGroupVariantWithRoot } from "../FieldGroup/field-group.variants.js";
9
+ import { useFormField, useFormFieldEmit } from "../hooks/useFormField.svelte.js";
10
+ import Icon from "../Icon/Icon.svelte";
11
+ import { selectDefaults, selectVariants } from "./select.variants.js";
12
+ const config = getComponentConfig("select", selectDefaults);
13
+ const icons = getComponentConfig("icons", iconsDefaults);
14
+ let { ref = $bindable(null), value = $bindable(), open = $bindable(false), onOpenChange, items = [], placeholder, name, required = false, disabled = false, multiple = false, separator = ", ", ui, id, color = config.defaultVariants.color, variant = config.defaultVariants.variant, size, highlight, loading = false, loadingIcon = icons.loading, icon, leadingIcon, trailingIcon = icons.chevronDown, selectedIcon = icons.check, avatar, transition = config.defaultVariants.transition ?? true, portal = true, side = config.defaultVariants.side ?? "bottom", sideOffset = 4, align = "start", alignOffset = 0, avoidCollisions = true, collisionBoundary, collisionPadding = 8, onEscapeKeydown, onInteractOutside, forceMount, loop = true, class: className, leadingSlot, trailingSlot, item: itemSlot, itemLeading, itemLabel: itemLabelSlot, itemTrailing, selected: selectedSlot, content: contentSlot, ...restProps } = $props();
15
+ // ---- Form context ----
16
+ const formFieldContext = useFormField();
17
+ const emit = useFormFieldEmit();
18
+ const fieldGroupContext = getContext("fieldGroup");
19
+ const hasError = $derived(formFieldContext?.error !== undefined && formFieldContext?.error !== false);
20
+ const resolvedSize = $derived(size ?? formFieldContext?.size ?? fieldGroupContext?.size ?? config.defaultVariants.size);
21
+ const resolvedColor = $derived(hasError ? "error" : color);
22
+ const resolvedHighlight = $derived(highlight ?? hasError);
23
+ const fieldGroupClass = $derived(fieldGroupContext ? fieldGroupVariantWithRoot.fieldGroup[fieldGroupContext.orientation ?? "horizontal"] : undefined);
24
+ const resolvedId = $derived(id ?? formFieldContext?.ariaId);
25
+ const resolvedName = $derived(name ?? formFieldContext?.name);
26
+ // ---- ARIA ----
27
+ const ariaDescribedBy = $derived(!formFieldContext ? undefined : hasError ? `${formFieldContext.ariaId}-error` : `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`);
28
+ // ---- Items lookup (O(1) via Map) ----
29
+ const itemsMap = $derived(new Map(items.filter((i) => !("type" in i)).map((i) => [i.value, i])));
30
+ // ---- Selection (single + multiple) ----
31
+ const selectedValues = $derived(multiple ? Array.isArray(value) ? value : [] : typeof value === "string" && value !== "" ? [value] : []);
32
+ const selectedItems = $derived(selectedValues.map((v) => itemsMap.get(v)).filter((i) => i !== undefined));
33
+ const hasSelection = $derived(selectedValues.length > 0);
34
+ const singleSelectedItem = $derived(multiple ? undefined : selectedItems[0]);
35
+ const displayLabel = $derived(multiple ? selectedItems.map((i) => i.label ?? i.value).join(separator) : singleSelectedItem?.label ?? singleSelectedItem?.value ?? "");
36
+ function removeValue(val) {
37
+ if (!multiple) return;
38
+ value = selectedValues.filter((v) => v !== val);
39
+ emit.onChange();
40
+ }
41
+ function clearSelection() {
42
+ if (!multiple) return;
43
+ value = [];
44
+ emit.onChange();
45
+ }
46
+ // ---- Leading / trailing ----
47
+ const displayAvatar = $derived(multiple ? avatar : singleSelectedItem?.avatar ?? avatar);
48
+ const displayIcon = $derived(multiple ? leadingIcon ?? icon : singleSelectedItem?.icon ?? leadingIcon ?? icon);
49
+ const isLeading = $derived(!!leadingSlot || !!displayAvatar || !!displayIcon);
50
+ const leadingIconName = $derived(loading && isLeading ? loadingIcon : !displayAvatar ? displayIcon : undefined);
51
+ // ---- bits-ui items for typeahead ----
52
+ const bitsItems = $derived([...itemsMap.values()].map((i) => ({
53
+ value: i.value,
54
+ label: i.label ?? i.value,
55
+ disabled: i.disabled
56
+ })));
57
+ // ---- Trailing icon ----
58
+ const trailingIconName = $derived(loading && !isLeading ? loadingIcon : trailingIcon);
59
+ const trailingIconClass = $derived(`${loading && !isLeading ? "animate-spin" : "transition-transform"} ${open && !loading ? "rotate-180" : ""}`);
60
+ // ---- Variant slots ----
61
+ const variantSlots = $derived(selectVariants({
62
+ variant,
63
+ color: resolvedColor,
64
+ size: resolvedSize,
65
+ leading: isLeading,
66
+ trailing: true,
67
+ loading,
68
+ highlight: resolvedHighlight,
69
+ side,
70
+ transition
71
+ }));
72
+ // ---- Trigger classes (root, base, leading, trailing, value, placeholder) ----
73
+ const rootClass = $derived(variantSlots.root({ class: [
74
+ config.slots.root,
75
+ fieldGroupClass?.root,
76
+ className,
77
+ ui?.root
78
+ ] }));
79
+ const baseClass = $derived(variantSlots.base({ class: [
80
+ config.slots.base,
81
+ fieldGroupClass?.base,
82
+ ui?.base
83
+ ] }));
84
+ const leadingClass = $derived(variantSlots.leading({ class: [config.slots.leading, ui?.leading] }));
85
+ const leadingIconClass = $derived(variantSlots.leadingIcon({ class: [config.slots.leadingIcon, ui?.leadingIcon] }));
86
+ const leadingAvatarClass = $derived(variantSlots.leadingAvatar({ class: [config.slots.leadingAvatar, ui?.leadingAvatar] }));
87
+ const leadingAvatarSizeClass = $derived(variantSlots.leadingAvatarSize());
88
+ const trailingClass = $derived(variantSlots.trailing({ class: [config.slots.trailing, ui?.trailing] }));
89
+ const trailingIconBaseClass = $derived(variantSlots.trailingIcon({ class: [config.slots.trailingIcon, ui?.trailingIcon] }));
90
+ const valueClass = $derived(variantSlots.value({ class: [config.slots.value, ui?.value] }));
91
+ const placeholderClass = $derived(variantSlots.placeholder({ class: [config.slots.placeholder, ui?.placeholder] }));
92
+ // ---- Content classes ----
93
+ const contentClass = $derived(variantSlots.content({ class: [config.slots.content, ui?.content] }));
94
+ const viewportClass = $derived(variantSlots.viewport({ class: [config.slots.viewport, ui?.viewport] }));
95
+ const groupLabelClass = $derived(variantSlots.groupLabel({ class: [config.slots.groupLabel, ui?.groupLabel] }));
96
+ const separatorClass = $derived(variantSlots.separator({ class: [config.slots.separator, ui?.separator] }));
97
+ // ---- Item classes ----
98
+ const itemClass = $derived(variantSlots.item({ class: [config.slots.item, ui?.item] }));
99
+ const itemIconClass = $derived(variantSlots.itemIcon({ class: [config.slots.itemIcon, ui?.itemIcon] }));
100
+ const itemAvatarClass = $derived(variantSlots.itemAvatar({ class: [config.slots.itemAvatar, ui?.itemAvatar] }));
101
+ const itemAvatarSizeClass = $derived(variantSlots.itemAvatarSize());
102
+ const itemLabelClass = $derived(variantSlots.itemLabel({ class: [config.slots.itemLabel, ui?.itemLabel] }));
103
+ const itemDescriptionClass = $derived(variantSlots.itemDescription({ class: [config.slots.itemDescription, ui?.itemDescription] }));
104
+ const itemIndicatorClass = $derived(variantSlots.itemIndicator({ class: [config.slots.itemIndicator, ui?.itemIndicator] }));
105
+ // ---- Type guards ----
106
+ function isSelectItem(item) {
107
+ return !("type" in item);
108
+ }
109
+ function isSeparator(item) {
110
+ return "type" in item && item.type === "separator";
111
+ }
112
+ function isLabel(item) {
113
+ return "type" in item && item.type === "label";
114
+ }
272
115
  </script>
273
116
 
274
117
  {#snippet renderItem(item: SelectItem, index: number)}
@@ -1,6 +1,6 @@
1
1
  import type { SelectProps } from './select.types.js';
2
2
  export type Props = SelectProps;
3
3
  import { Select } from 'bits-ui';
4
- declare const Select: import("svelte").Component<SelectProps, {}, "value" | "ref" | "open">;
4
+ declare const Select: import("svelte").Component<SelectProps, {}, "open" | "ref" | "value">;
5
5
  type Select = ReturnType<typeof Select>;
6
6
  export default Select;