@pzerelles/headlessui-svelte 2.1.2-next.22 → 2.1.2-next.24

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 (132) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/button/Button.svelte.d.ts +7 -4
  3. package/dist/checkbox/Checkbox.svelte +173 -120
  4. package/dist/checkbox/Checkbox.svelte.d.ts +7 -4
  5. package/dist/close-button/CloseButton.svelte +12 -6
  6. package/dist/close-button/CloseButton.svelte.d.ts +13 -10
  7. package/dist/combobox/Combobox.svelte +50 -3
  8. package/dist/data-interactive/DataInteractive.svelte +55 -29
  9. package/dist/data-interactive/DataInteractive.svelte.d.ts +7 -5
  10. package/dist/description/Description.svelte +39 -24
  11. package/dist/description/Description.svelte.d.ts +8 -5
  12. package/dist/description/context.svelte.js +13 -15
  13. package/dist/dialog/Dialog.svelte +358 -38
  14. package/dist/dialog/Dialog.svelte.d.ts +10 -7
  15. package/dist/dialog/DialogBackdrop.svelte +30 -13
  16. package/dist/dialog/DialogBackdrop.svelte.d.ts +7 -4
  17. package/dist/dialog/DialogPanel.svelte +49 -26
  18. package/dist/dialog/DialogPanel.svelte.d.ts +7 -4
  19. package/dist/dialog/DialogTitle.svelte +38 -23
  20. package/dist/dialog/DialogTitle.svelte.d.ts +7 -4
  21. package/dist/field/Field.svelte +50 -34
  22. package/dist/field/Field.svelte.d.ts +7 -4
  23. package/dist/fieldset/Fieldset.svelte +50 -29
  24. package/dist/fieldset/Fieldset.svelte.d.ts +7 -4
  25. package/dist/focus-trap/FocusTrap.svelte +419 -283
  26. package/dist/focus-trap/FocusTrap.svelte.d.ts +7 -4
  27. package/dist/hooks/use-disabled.d.ts +4 -1
  28. package/dist/hooks/use-disabled.js +10 -5
  29. package/dist/input/Input.svelte +84 -53
  30. package/dist/input/Input.svelte.d.ts +7 -4
  31. package/dist/internal/FloatingProvider.svelte +14 -9
  32. package/dist/internal/FocusSentinel.svelte +16 -8
  33. package/dist/internal/ForcePortalRoot.svelte +7 -3
  34. package/dist/internal/FormFields.svelte +47 -34
  35. package/dist/internal/FormFieldsProvider.svelte +9 -5
  36. package/dist/internal/FormResolver.svelte +20 -15
  37. package/dist/internal/Hidden.svelte +50 -29
  38. package/dist/internal/Hidden.svelte.d.ts +7 -4
  39. package/dist/internal/MainTreeProvider.svelte +89 -36
  40. package/dist/internal/Portal.svelte +18 -14
  41. package/dist/internal/floating-provider.svelte.js +1 -1
  42. package/dist/internal/floating.svelte.d.ts +5 -5
  43. package/dist/internal/floating.svelte.js +17 -17
  44. package/dist/label/Label.svelte +93 -58
  45. package/dist/label/Label.svelte.d.ts +7 -4
  46. package/dist/legend/Legend.svelte +12 -3
  47. package/dist/listbox/Listbox.svelte +525 -387
  48. package/dist/listbox/Listbox.svelte.d.ts +7 -5
  49. package/dist/listbox/ListboxButton.svelte +173 -127
  50. package/dist/listbox/ListboxButton.svelte.d.ts +7 -5
  51. package/dist/listbox/ListboxOption.svelte +170 -129
  52. package/dist/listbox/ListboxOption.svelte.d.ts +7 -5
  53. package/dist/listbox/ListboxOptions.svelte +400 -304
  54. package/dist/listbox/ListboxOptions.svelte.d.ts +7 -5
  55. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  56. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +7 -4
  57. package/dist/listbox/index.d.ts +4 -4
  58. package/dist/listbox/index.js +1 -1
  59. package/dist/menu/Menu.svelte +78 -57
  60. package/dist/menu/Menu.svelte.d.ts +7 -5
  61. package/dist/menu/MenuButton.svelte +157 -117
  62. package/dist/menu/MenuButton.svelte.d.ts +7 -5
  63. package/dist/menu/MenuHeading.svelte +32 -14
  64. package/dist/menu/MenuHeading.svelte.d.ts +7 -5
  65. package/dist/menu/MenuItem.svelte +142 -107
  66. package/dist/menu/MenuItem.svelte.d.ts +8 -8
  67. package/dist/menu/MenuItems.svelte +301 -229
  68. package/dist/menu/MenuItems.svelte.d.ts +7 -5
  69. package/dist/menu/MenuSection.svelte +24 -9
  70. package/dist/menu/MenuSection.svelte.d.ts +7 -5
  71. package/dist/menu/MenuSeparator.svelte +17 -4
  72. package/dist/menu/MenuSeparator.svelte.d.ts +7 -6
  73. package/dist/menu/context.svelte.d.ts +1 -29
  74. package/dist/menu/context.svelte.js +29 -27
  75. package/dist/menu/index.d.ts +7 -7
  76. package/dist/popover/Popover.svelte +216 -150
  77. package/dist/popover/Popover.svelte.d.ts +7 -4
  78. package/dist/popover/PopoverBackdrop.svelte +67 -41
  79. package/dist/popover/PopoverBackdrop.svelte.d.ts +7 -4
  80. package/dist/popover/PopoverButton.svelte +292 -212
  81. package/dist/popover/PopoverButton.svelte.d.ts +7 -4
  82. package/dist/popover/PopoverGroup.svelte +62 -35
  83. package/dist/popover/PopoverGroup.svelte.d.ts +7 -4
  84. package/dist/popover/PopoverPanel.svelte +311 -229
  85. package/dist/popover/PopoverPanel.svelte.d.ts +7 -4
  86. package/dist/portal/InternalPortal.svelte +141 -85
  87. package/dist/portal/InternalPortal.svelte.d.ts +7 -4
  88. package/dist/portal/Portal.svelte +5 -2
  89. package/dist/portal/PortalGroup.svelte +30 -9
  90. package/dist/portal/PortalGroup.svelte.d.ts +7 -4
  91. package/dist/select/Select.svelte +98 -68
  92. package/dist/select/Select.svelte.d.ts +7 -4
  93. package/dist/switch/Switch.svelte +179 -132
  94. package/dist/switch/Switch.svelte.d.ts +7 -4
  95. package/dist/switch/SwitchGroup.svelte +44 -31
  96. package/dist/switch/SwitchGroup.svelte.d.ts +7 -4
  97. package/dist/tabs/Tab.svelte +194 -143
  98. package/dist/tabs/Tab.svelte.d.ts +7 -4
  99. package/dist/tabs/TabGroup.svelte +81 -214
  100. package/dist/tabs/TabGroup.svelte.d.ts +7 -24
  101. package/dist/tabs/TabList.svelte +31 -11
  102. package/dist/tabs/TabList.svelte.d.ts +7 -4
  103. package/dist/tabs/TabPanel.svelte +67 -43
  104. package/dist/tabs/TabPanel.svelte.d.ts +7 -4
  105. package/dist/tabs/TabPanels.svelte +18 -7
  106. package/dist/tabs/TabPanels.svelte.d.ts +7 -4
  107. package/dist/tabs/context.svelte.d.ts +31 -0
  108. package/dist/tabs/context.svelte.js +134 -0
  109. package/dist/textarea/Textarea.svelte +84 -53
  110. package/dist/textarea/Textarea.svelte.d.ts +7 -4
  111. package/dist/transition/InternalTransitionChild.svelte +259 -170
  112. package/dist/transition/InternalTransitionChild.svelte.d.ts +7 -4
  113. package/dist/transition/Transition.svelte +96 -66
  114. package/dist/transition/Transition.svelte.d.ts +7 -4
  115. package/dist/transition/TransitionChild.svelte +31 -11
  116. package/dist/transition/TransitionChild.svelte.d.ts +7 -4
  117. package/dist/utils/ElementOrComponent.svelte +43 -23
  118. package/dist/utils/ElementOrComponent.svelte.d.ts +10 -4
  119. package/dist/utils/Generic.svelte +36 -22
  120. package/dist/utils/Generic.svelte.d.ts +7 -4
  121. package/dist/utils/StableCollection.svelte +54 -36
  122. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
  123. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
  124. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.js +7 -7
  125. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +1 -1
  126. package/dist/utils/floating-ui/svelte/types.d.ts +4 -4
  127. package/dist/utils/floating-ui/svelte-dom/types.d.ts +2 -2
  128. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +6 -6
  129. package/dist/utils/types.d.ts +11 -4
  130. package/package.json +2 -2
  131. package/dist/dialog/InternalDialog.svelte +0 -233
  132. package/dist/dialog/InternalDialog.svelte.d.ts +0 -42
@@ -1,421 +1,559 @@
1
- <script lang="ts" module>import { useByComparator } from "../hooks/use-by-comparator.js";
2
- import { useControllable } from "../hooks/use-controllable.svelte.js";
3
- import { useDisabled } from "../hooks/use-disabled.js";
4
- import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js";
5
- import { FocusableMode, isFocusableElement, sortByDomNode } from "../utils/focus-management.js";
6
- import { match } from "../utils/match.js";
7
- import { setContext } from "svelte";
8
- import { ActivationTrigger, ListboxStates, ValueMode } from "./context.svelte.js";
9
- let DEFAULT_LISTBOX_TAG = "svelte:fragment";
10
- export * from "./context.svelte.js";
1
+ <script lang="ts" module>
2
+ import { useByComparator, type ByComparator } from "../hooks/use-by-comparator.js"
3
+ import { useControllable } from "../hooks/use-controllable.svelte.js"
4
+ import { useDisabled } from "../hooks/use-disabled.js"
5
+ import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js"
6
+ import { FocusableMode, isFocusableElement, sortByDomNode } from "../utils/focus-management.js"
7
+ import { match } from "../utils/match.js"
8
+ import type { ElementType, EnsureArray, Props } from "../utils/types.js"
9
+ import { setContext, type Snippet } from "svelte"
10
+ import { ActivationTrigger, ListboxStates, ValueMode } from "./context.svelte.js"
11
+
12
+ let DEFAULT_LISTBOX_TAG = "svelte:fragment"
13
+ type ListboxRenderPropArg<T> = {
14
+ open: boolean
15
+ disabled: boolean
16
+ invalid: boolean
17
+ value: T
18
+ }
19
+
20
+ export type ListboxProps<
21
+ TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG,
22
+ TType = string,
23
+ TActualType = TType extends (infer U)[] ? U : TType,
24
+ > = Props<
25
+ TTag,
26
+ ListboxRenderPropArg<TType>,
27
+ "value" | "defaultValue" | "onchange" | "by" | "disabled" | "horizontal" | "name" | "multiple",
28
+ {
29
+ value?: TType
30
+ defaultValue?: TType
31
+ onchange?: (value: TType) => void
32
+ by?: ByComparator<TActualType>
33
+ disabled?: boolean
34
+ invalid?: boolean
35
+ horizontal?: boolean
36
+ form?: string
37
+ name?: string
38
+ multiple?: boolean
39
+ closeOnSelect?: boolean
40
+
41
+ __demoMode?: boolean
42
+ }
43
+ >
44
+
45
+ export * from "./context.svelte.js"
11
46
  </script>
12
47
 
13
- <script lang="ts" generics="TType, TActualType, TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG">import { disposables } from "../utils/disposables.js";
14
- import FormFields from "../internal/FormFields.svelte";
15
- import { useFloatingProvider } from "../internal/floating.svelte.js";
16
- import { createOpenClosedContext, State } from "../internal/open-closed.js";
17
- import { useLabels } from "../label/context.svelte.js";
18
- import { useOutsideClick } from "../hooks/use-outside-click.svelte.js";
19
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
20
- import { SvelteMap } from "svelte/reactivity";
21
- function adjustOrderedState(state, adjustment = (i) => i) {
22
- let currentActiveOption = state.activeOptionIndex !== null ? state.options[state.activeOptionIndex] : null;
23
- let sortedOptions = sortByDomNode(
24
- adjustment(state.options.slice()),
25
- (option) => option.dataRef.current.domRef.current
26
- );
27
- let adjustedActiveOptionIndex = currentActiveOption ? sortedOptions.indexOf(currentActiveOption) : null;
28
- if (adjustedActiveOptionIndex === -1) {
29
- adjustedActiveOptionIndex = null;
48
+ <script lang="ts" generics="TType, TActualType, TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG">
49
+ import { disposables } from "../utils/disposables.js"
50
+ import FormFields from "../internal/FormFields.svelte"
51
+ import { useFloatingProvider } from "../internal/floating.svelte.js"
52
+ import { createOpenClosedContext, State } from "../internal/open-closed.js"
53
+ import { useLabels } from "../label/context.svelte.js"
54
+ import { useOutsideClick } from "../hooks/use-outside-click.svelte.js"
55
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
56
+ import type {
57
+ ListboxActionsContext,
58
+ ListboxDataContext,
59
+ ListboxOptionDataRef,
60
+ StateActions,
61
+ StateDefinition,
62
+ } from "./context.svelte.js"
63
+ import { SvelteMap } from "svelte/reactivity"
64
+
65
+ function adjustOrderedState<T>(
66
+ state: StateDefinition<T>,
67
+ adjustment: (options: StateDefinition<T>["options"]) => StateDefinition<T>["options"] = (i) => i
68
+ ) {
69
+ let currentActiveOption = state.activeOptionIndex !== null ? state.options[state.activeOptionIndex] : null
70
+
71
+ let sortedOptions = sortByDomNode(
72
+ adjustment(state.options.slice()),
73
+ (option) => option.dataRef.current.domRef.current
74
+ )
75
+
76
+ // If we inserted an option before the current active option then the active option index
77
+ // would be wrong. To fix this, we will re-lookup the correct index.
78
+ let adjustedActiveOptionIndex = currentActiveOption ? sortedOptions.indexOf(currentActiveOption) : null
79
+
80
+ // Reset to `null` in case the currentActiveOption was removed.
81
+ if (adjustedActiveOptionIndex === -1) {
82
+ adjustedActiveOptionIndex = null
83
+ }
84
+
85
+ return {
86
+ options: sortedOptions,
87
+ activeOptionIndex: adjustedActiveOptionIndex,
88
+ }
30
89
  }
31
- return {
32
- options: sortedOptions,
33
- activeOptionIndex: adjustedActiveOptionIndex
34
- };
35
- }
36
- const stateReducer = (initialState) => {
37
- let _state2 = $state(initialState);
38
- return {
90
+
91
+ const stateReducer = (initialState: StateDefinition<TActualType>) => {
92
+ let _state = $state(initialState)
93
+ return {
94
+ get listboxState() {
95
+ return _state.listboxState
96
+ },
97
+ get options() {
98
+ return _state.options
99
+ },
100
+ get searchQuery() {
101
+ return _state.searchQuery
102
+ },
103
+ get activeOptionIndex() {
104
+ return _state.activeOptionIndex
105
+ },
106
+ get activationTrigger() {
107
+ return _state.activationTrigger
108
+ },
109
+ get __demoMode() {
110
+ return _state.__demoMode
111
+ },
112
+ closeListbox() {
113
+ if (disabled) return _state
114
+ if (_state.listboxState === ListboxStates.Closed) return _state
115
+ _state.activeOptionIndex = null
116
+ _state.listboxState = ListboxStates.Closed
117
+ _state.__demoMode = false
118
+ return _state
119
+ },
120
+ openListbox() {
121
+ if (disabled) return _state
122
+ if (_state.listboxState === ListboxStates.Open) return _state
123
+
124
+ // Check if we have a selected value that we can make active
125
+ let activeOptionIndex = _state.activeOptionIndex
126
+ let optionIdx = _state.options.findIndex((option) => isSelected(option.dataRef.current.value))
127
+
128
+ if (optionIdx !== -1) {
129
+ activeOptionIndex = optionIdx
130
+ }
131
+ _state.listboxState = ListboxStates.Open
132
+ _state.activeOptionIndex = activeOptionIndex
133
+ _state.__demoMode = false
134
+ return _state
135
+ },
136
+ goToOption(action) {
137
+ if (disabled) return _state
138
+ if (_state.listboxState === ListboxStates.Closed) return _state
139
+
140
+ _state.searchQuery = ""
141
+ _state.activationTrigger = action.trigger ?? ActivationTrigger.Other
142
+ _state.__demoMode = false
143
+
144
+ // Optimization:
145
+ //
146
+ // There is no need to sort the DOM nodes if we know that we don't want to focus anything
147
+ if (action.focus === Focus.Nothing) {
148
+ _state.activeOptionIndex = null
149
+ return _state
150
+ }
151
+
152
+ // Optimization:
153
+ //
154
+ // There is no need to sort the DOM nodes if we know exactly where to go
155
+ if (action.focus === Focus.Specific) {
156
+ _state.activeOptionIndex = _state.options.findIndex((o) => o.id === action.id)
157
+ return _state
158
+ }
159
+
160
+ // Optimization:
161
+ //
162
+ // If the current DOM node and the previous DOM node are next to each other,
163
+ // or if the previous DOM node is already the first DOM node, then we don't
164
+ // have to sort all the DOM nodes.
165
+ else if (action.focus === Focus.Previous) {
166
+ let activeOptionIdx = _state.activeOptionIndex
167
+ if (activeOptionIdx !== null) {
168
+ let currentDom = _state.options[activeOptionIdx].dataRef.current.domRef
169
+ let previousOptionIndex = calculateActiveIndex(action, {
170
+ resolveItems: () => _state.options,
171
+ resolveActiveIndex: () => _state.activeOptionIndex,
172
+ resolveId: (option) => option.id,
173
+ resolveDisabled: (option) => option.dataRef.current.disabled ?? false,
174
+ })
175
+ if (previousOptionIndex !== null) {
176
+ let previousDom = _state.options[previousOptionIndex].dataRef.current.domRef
177
+ if (
178
+ // Next to each other
179
+ currentDom.current?.previousElementSibling === previousDom?.current ||
180
+ // Or already the first element
181
+ previousDom.current?.previousElementSibling === null
182
+ ) {
183
+ _state.activeOptionIndex = previousOptionIndex
184
+ return _state
185
+ }
186
+ }
187
+ }
188
+ }
189
+
190
+ // Optimization:
191
+ //
192
+ // If the current DOM node and the next DOM node are next to each other, or
193
+ // if the next DOM node is already the last DOM node, then we don't have to
194
+ // sort all the DOM nodes.
195
+ else if (action.focus === Focus.Next) {
196
+ let activeOptionIdx = _state.activeOptionIndex
197
+ if (activeOptionIdx !== null) {
198
+ let currentDom = _state.options[activeOptionIdx].dataRef.current.domRef
199
+ let nextOptionIndex = calculateActiveIndex(action, {
200
+ resolveItems: () => _state.options,
201
+ resolveActiveIndex: () => _state.activeOptionIndex,
202
+ resolveId: (option) => option.id,
203
+ resolveDisabled: (option) => option.dataRef.current.disabled ?? false,
204
+ })
205
+ if (nextOptionIndex !== null) {
206
+ let nextDom = _state.options[nextOptionIndex].dataRef.current.domRef
207
+ if (
208
+ // Next to each other
209
+ currentDom.current?.nextElementSibling === nextDom.current ||
210
+ // Or already the last element
211
+ nextDom.current?.nextElementSibling === null
212
+ ) {
213
+ _state.activeOptionIndex = nextOptionIndex
214
+ return _state
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ // Slow path:
221
+ //
222
+ // Ensure all the options are correctly sorted according to DOM position
223
+ let adjustedState = adjustOrderedState(_state)
224
+ let activeOptionIndex = calculateActiveIndex(action, {
225
+ resolveItems: () => adjustedState.options,
226
+ resolveActiveIndex: () => adjustedState.activeOptionIndex,
227
+ resolveId: (option) => option.id,
228
+ resolveDisabled: (option) => option.dataRef.current.disabled ?? false,
229
+ })
230
+
231
+ _state.options = adjustedState.options
232
+ _state.activeOptionIndex = activeOptionIndex
233
+ return _state
234
+ },
235
+ search(value) {
236
+ if (disabled) return _state
237
+ if (_state.listboxState === ListboxStates.Closed) return _state
238
+
239
+ let wasAlreadySearching = _state.searchQuery !== ""
240
+ let offset = wasAlreadySearching ? 0 : 1
241
+
242
+ let searchQuery = _state.searchQuery + value.toLowerCase()
243
+
244
+ let reOrderedOptions =
245
+ _state.activeOptionIndex !== null
246
+ ? _state.options
247
+ .slice(_state.activeOptionIndex + offset)
248
+ .concat(_state.options.slice(0, _state.activeOptionIndex + offset))
249
+ : _state.options
250
+
251
+ let matchingOption = reOrderedOptions.find(
252
+ (option) => !option.dataRef.current.disabled && option.dataRef.current.textValue?.startsWith(searchQuery)
253
+ )
254
+
255
+ let matchIdx = matchingOption ? _state.options.indexOf(matchingOption) : -1
256
+
257
+ if (matchIdx === -1 || matchIdx === _state.activeOptionIndex) {
258
+ _state.searchQuery = searchQuery
259
+ } else {
260
+ _state.searchQuery = searchQuery
261
+ _state.activeOptionIndex = matchIdx
262
+ _state.activationTrigger = ActivationTrigger.Other
263
+ }
264
+ return _state
265
+ },
266
+ clearSearch() {
267
+ if (disabled) return _state
268
+ if (_state.listboxState === ListboxStates.Closed) return _state
269
+ if (_state.searchQuery === "") return _state
270
+ _state.searchQuery = ""
271
+ return _state
272
+ },
273
+ registerOption(id, dataRef) {
274
+ let option = { id, dataRef }
275
+ let adjustedState = adjustOrderedState(_state, (options) => [...options, option])
276
+
277
+ // Check if we need to make the newly registered option active.
278
+ if (_state.activeOptionIndex === null) {
279
+ if (isSelected(dataRef.current.value as any)) {
280
+ adjustedState.activeOptionIndex = adjustedState.options.indexOf(option)
281
+ }
282
+ }
283
+
284
+ _state.options = adjustedState.options
285
+ _state.activeOptionIndex = adjustedState.activeOptionIndex
286
+ return _state
287
+ },
288
+ unregisterOption(id) {
289
+ let adjustedState = adjustOrderedState(_state, (options) => {
290
+ let idx = options.findIndex((a) => a.id === id)
291
+ if (idx !== -1) options.splice(idx, 1)
292
+ return options
293
+ })
294
+
295
+ _state.options = adjustedState.options
296
+ _state.activeOptionIndex = adjustedState.activeOptionIndex
297
+ _state.activationTrigger = ActivationTrigger.Other
298
+ return _state
299
+ },
300
+ } satisfies StateDefinition<TActualType> & StateActions<TActualType>
301
+ }
302
+
303
+ let {
304
+ ref = $bindable(),
305
+ value: controlledValue = $bindable(),
306
+ defaultValue,
307
+ form,
308
+ name,
309
+ onchange: controlledOnChange,
310
+ by,
311
+ invalid = false,
312
+ disabled: ownDisabled = false,
313
+ horizontal = false,
314
+ multiple = false,
315
+ closeOnSelect,
316
+ __demoMode = false,
317
+ ...theirProps
318
+ }: { as?: TTag } & ListboxProps<TTag, TType, TActualType> = $props()
319
+
320
+ const providedDisabled = useDisabled()
321
+ const disabled = $derived(providedDisabled.current || ownDisabled)
322
+
323
+ const orientation = horizontal ? "horizontal" : "vertical"
324
+ const controllable = useControllable<any>(
325
+ {
326
+ get controlledValue() {
327
+ return controlledValue
328
+ },
329
+ set controlledValue(value) {
330
+ controlledValue = value
331
+ },
332
+ },
333
+ controlledOnChange,
334
+ defaultValue
335
+ )
336
+ const { value = multiple ? [] : undefined, onchange: theirOnChange } = $derived(controllable)
337
+
338
+ const _state = stateReducer({
339
+ listboxState: __demoMode ? ListboxStates.Open : ListboxStates.Closed,
340
+ options: [],
341
+ searchQuery: "",
342
+ activeOptionIndex: null,
343
+ activationTrigger: ActivationTrigger.Other,
344
+ optionsVisible: false,
345
+ __demoMode,
346
+ } as StateDefinition<TActualType>)
347
+
348
+ type _Data = ListboxDataContext<TActualType>
349
+
350
+ const optionsProps = $state<_Data["optionsProps"]>({ static: false, hold: false })
351
+
352
+ let buttonElement = $state<_Data["buttonElement"]>(null)
353
+ let optionsElement = $state<_Data["optionsElement"]>(null)
354
+ const listElements = new SvelteMap<string, HTMLElement | null>()
355
+
356
+ const compare = useByComparator(by)
357
+
358
+ const isSelected = (compareValue: TActualType): boolean =>
359
+ match(data.mode, {
360
+ [ValueMode.Multi]: () => {
361
+ return (value as EnsureArray<TType>).some((option) => compare(option, compareValue))
362
+ },
363
+ [ValueMode.Single]: () => {
364
+ return compare(value as TActualType, compareValue)
365
+ },
366
+ })
367
+
368
+ const data: ListboxDataContext<TActualType> = {
39
369
  get listboxState() {
40
- return _state2.listboxState;
370
+ return _state.listboxState
41
371
  },
42
372
  get options() {
43
- return _state2.options;
373
+ return _state.options
44
374
  },
45
375
  get searchQuery() {
46
- return _state2.searchQuery;
376
+ return _state.searchQuery
47
377
  },
48
378
  get activeOptionIndex() {
49
- return _state2.activeOptionIndex;
379
+ return _state.activeOptionIndex
50
380
  },
51
381
  get activationTrigger() {
52
- return _state2.activationTrigger;
382
+ return _state.activationTrigger
53
383
  },
54
384
  get __demoMode() {
55
- return _state2.__demoMode;
385
+ return _state.__demoMode
56
386
  },
57
- closeListbox() {
58
- if (disabled) return _state2;
59
- if (_state2.listboxState === ListboxStates.Closed) return _state2;
60
- _state2.activeOptionIndex = null;
61
- _state2.listboxState = ListboxStates.Closed;
62
- _state2.__demoMode = false;
63
- return _state2;
387
+ get value() {
388
+ return value
64
389
  },
65
- openListbox() {
66
- if (disabled) return _state2;
67
- if (_state2.listboxState === ListboxStates.Open) return _state2;
68
- let activeOptionIndex = _state2.activeOptionIndex;
69
- let optionIdx = _state2.options.findIndex((option) => isSelected(option.dataRef.current.value));
70
- if (optionIdx !== -1) {
71
- activeOptionIndex = optionIdx;
72
- }
73
- _state2.listboxState = ListboxStates.Open;
74
- _state2.activeOptionIndex = activeOptionIndex;
75
- _state2.__demoMode = false;
76
- return _state2;
390
+ get disabled() {
391
+ return disabled
77
392
  },
78
- goToOption(action) {
79
- if (disabled) return _state2;
80
- if (_state2.listboxState === ListboxStates.Closed) return _state2;
81
- _state2.searchQuery = "";
82
- _state2.activationTrigger = action.trigger ?? ActivationTrigger.Other;
83
- _state2.__demoMode = false;
84
- if (action.focus === Focus.Nothing) {
85
- _state2.activeOptionIndex = null;
86
- return _state2;
87
- }
88
- if (action.focus === Focus.Specific) {
89
- _state2.activeOptionIndex = _state2.options.findIndex((o) => o.id === action.id);
90
- return _state2;
91
- } else if (action.focus === Focus.Previous) {
92
- let activeOptionIdx = _state2.activeOptionIndex;
93
- if (activeOptionIdx !== null) {
94
- let currentDom = _state2.options[activeOptionIdx].dataRef.current.domRef;
95
- let previousOptionIndex = calculateActiveIndex(action, {
96
- resolveItems: () => _state2.options,
97
- resolveActiveIndex: () => _state2.activeOptionIndex,
98
- resolveId: (option) => option.id,
99
- resolveDisabled: (option) => option.dataRef.current.disabled ?? false
100
- });
101
- if (previousOptionIndex !== null) {
102
- let previousDom = _state2.options[previousOptionIndex].dataRef.current.domRef;
103
- if (
104
- // Next to each other
105
- currentDom.current?.previousElementSibling === previousDom?.current || // Or already the first element
106
- previousDom.current?.previousElementSibling === null
107
- ) {
108
- _state2.activeOptionIndex = previousOptionIndex;
109
- return _state2;
110
- }
111
- }
112
- }
113
- } else if (action.focus === Focus.Next) {
114
- let activeOptionIdx = _state2.activeOptionIndex;
115
- if (activeOptionIdx !== null) {
116
- let currentDom = _state2.options[activeOptionIdx].dataRef.current.domRef;
117
- let nextOptionIndex = calculateActiveIndex(action, {
118
- resolveItems: () => _state2.options,
119
- resolveActiveIndex: () => _state2.activeOptionIndex,
120
- resolveId: (option) => option.id,
121
- resolveDisabled: (option) => option.dataRef.current.disabled ?? false
122
- });
123
- if (nextOptionIndex !== null) {
124
- let nextDom = _state2.options[nextOptionIndex].dataRef.current.domRef;
125
- if (
126
- // Next to each other
127
- currentDom.current?.nextElementSibling === nextDom.current || // Or already the last element
128
- nextDom.current?.nextElementSibling === null
129
- ) {
130
- _state2.activeOptionIndex = nextOptionIndex;
131
- return _state2;
132
- }
133
- }
134
- }
135
- }
136
- let adjustedState = adjustOrderedState(_state2);
137
- let activeOptionIndex = calculateActiveIndex(action, {
138
- resolveItems: () => adjustedState.options,
139
- resolveActiveIndex: () => adjustedState.activeOptionIndex,
140
- resolveId: (option) => option.id,
141
- resolveDisabled: (option) => option.dataRef.current.disabled ?? false
142
- });
143
- _state2.options = adjustedState.options;
144
- _state2.activeOptionIndex = activeOptionIndex;
145
- return _state2;
393
+ get invalid() {
394
+ return invalid
146
395
  },
147
- search(value2) {
148
- if (disabled) return _state2;
149
- if (_state2.listboxState === ListboxStates.Closed) return _state2;
150
- let wasAlreadySearching = _state2.searchQuery !== "";
151
- let offset = wasAlreadySearching ? 0 : 1;
152
- let searchQuery = _state2.searchQuery + value2.toLowerCase();
153
- let reOrderedOptions = _state2.activeOptionIndex !== null ? _state2.options.slice(_state2.activeOptionIndex + offset).concat(_state2.options.slice(0, _state2.activeOptionIndex + offset)) : _state2.options;
154
- let matchingOption = reOrderedOptions.find(
155
- (option) => !option.dataRef.current.disabled && option.dataRef.current.textValue?.startsWith(searchQuery)
156
- );
157
- let matchIdx = matchingOption ? _state2.options.indexOf(matchingOption) : -1;
158
- if (matchIdx === -1 || matchIdx === _state2.activeOptionIndex) {
159
- _state2.searchQuery = searchQuery;
160
- } else {
161
- _state2.searchQuery = searchQuery;
162
- _state2.activeOptionIndex = matchIdx;
163
- _state2.activationTrigger = ActivationTrigger.Other;
164
- }
165
- return _state2;
396
+ get mode() {
397
+ return multiple ? ValueMode.Multi : ValueMode.Single
166
398
  },
167
- clearSearch() {
168
- if (disabled) return _state2;
169
- if (_state2.listboxState === ListboxStates.Closed) return _state2;
170
- if (_state2.searchQuery === "") return _state2;
171
- _state2.searchQuery = "";
172
- return _state2;
399
+ get orientation() {
400
+ return orientation
173
401
  },
174
- registerOption(id, dataRef) {
175
- let option = { id, dataRef };
176
- let adjustedState = adjustOrderedState(_state2, (options) => [...options, option]);
177
- if (_state2.activeOptionIndex === null) {
178
- if (isSelected(dataRef.current.value)) {
179
- adjustedState.activeOptionIndex = adjustedState.options.indexOf(option);
180
- }
181
- }
182
- _state2.options = adjustedState.options;
183
- _state2.activeOptionIndex = adjustedState.activeOptionIndex;
184
- return _state2;
402
+ get closeOnSelect() {
403
+ return closeOnSelect
185
404
  },
186
- unregisterOption(id) {
187
- let adjustedState = adjustOrderedState(_state2, (options) => {
188
- let idx = options.findIndex((a) => a.id === id);
189
- if (idx !== -1) options.splice(idx, 1);
190
- return options;
191
- });
192
- _state2.options = adjustedState.options;
193
- _state2.activeOptionIndex = adjustedState.activeOptionIndex;
194
- _state2.activationTrigger = ActivationTrigger.Other;
195
- return _state2;
196
- }
197
- };
198
- };
199
- let {
200
- ref = $bindable(),
201
- value: controlledValue = $bindable(),
202
- defaultValue,
203
- form,
204
- name,
205
- onchange: controlledOnChange,
206
- by,
207
- invalid = false,
208
- disabled: ownDisabled = false,
209
- horizontal = false,
210
- multiple = false,
211
- closeOnSelect,
212
- __demoMode = false,
213
- ...theirProps
214
- } = $props();
215
- const providedDisabled = useDisabled();
216
- const disabled = $derived(providedDisabled.value || ownDisabled);
217
- const orientation = horizontal ? "horizontal" : "vertical";
218
- const controllable = useControllable(
219
- {
220
- get controlledValue() {
221
- return controlledValue;
405
+ compare,
406
+ isSelected,
407
+ get optionsProps() {
408
+ return optionsProps
409
+ },
410
+ get buttonElement() {
411
+ return buttonElement
412
+ },
413
+ set buttonElement(value) {
414
+ buttonElement = value
415
+ },
416
+ get optionsElement() {
417
+ return optionsElement
418
+ },
419
+ set optionsElement(value) {
420
+ optionsElement = value
421
+ },
422
+ get listElements() {
423
+ return listElements
222
424
  },
223
- set controlledValue(value2) {
224
- controlledValue = value2;
225
- }
226
- },
227
- controlledOnChange,
228
- defaultValue
229
- );
230
- const { value = multiple ? [] : void 0, onchange: theirOnChange } = $derived(controllable);
231
- const _state = stateReducer({
232
- listboxState: __demoMode ? ListboxStates.Open : ListboxStates.Closed,
233
- options: [],
234
- searchQuery: "",
235
- activeOptionIndex: null,
236
- activationTrigger: ActivationTrigger.Other,
237
- optionsVisible: false,
238
- __demoMode
239
- });
240
- const optionsProps = $state({ static: false, hold: false });
241
- let buttonElement = $state(null);
242
- let optionsElement = $state(null);
243
- const listElements = new SvelteMap();
244
- const compare = useByComparator(by);
245
- const isSelected = (compareValue) => match(data.mode, {
246
- [ValueMode.Multi]: () => {
247
- return value.some((option) => compare(option, compareValue));
248
- },
249
- [ValueMode.Single]: () => {
250
- return compare(value, compareValue);
251
425
  }
252
- });
253
- const data = {
254
- get listboxState() {
255
- return _state.listboxState;
256
- },
257
- get options() {
258
- return _state.options;
259
- },
260
- get searchQuery() {
261
- return _state.searchQuery;
262
- },
263
- get activeOptionIndex() {
264
- return _state.activeOptionIndex;
265
- },
266
- get activationTrigger() {
267
- return _state.activationTrigger;
268
- },
269
- get __demoMode() {
270
- return _state.__demoMode;
271
- },
272
- get value() {
273
- return value;
274
- },
275
- get disabled() {
276
- return disabled;
277
- },
278
- get invalid() {
279
- return invalid;
280
- },
281
- get mode() {
282
- return multiple ? ValueMode.Multi : ValueMode.Single;
283
- },
284
- get orientation() {
285
- return orientation;
286
- },
287
- get closeOnSelect() {
288
- return closeOnSelect;
289
- },
290
- compare,
291
- isSelected,
292
- get optionsProps() {
293
- return optionsProps;
294
- },
295
- get buttonElement() {
296
- return buttonElement;
297
- },
298
- set buttonElement(value2) {
299
- buttonElement = value2;
300
- },
301
- get optionsElement() {
302
- return optionsElement;
303
- },
304
- set optionsElement(value2) {
305
- optionsElement = value2;
306
- },
307
- get listElements() {
308
- return listElements;
426
+ setContext<ListboxDataContext<TActualType>>("ListboxDataContext", data)
427
+
428
+ // Handle outside click
429
+ const outsideClickEnabled = $derived(data.listboxState === ListboxStates.Open)
430
+ useOutsideClick({
431
+ get enabled() {
432
+ return outsideClickEnabled
433
+ },
434
+ get containers() {
435
+ return [data.buttonElement, data.optionsElement]
436
+ },
437
+ cb: (event, target) => {
438
+ _state.closeListbox()
439
+
440
+ if (!isFocusableElement(target, FocusableMode.Loose)) {
441
+ event.preventDefault()
442
+ data.buttonElement?.focus()
443
+ }
444
+ },
445
+ })
446
+
447
+ const slot = $derived({
448
+ open: _state.listboxState === ListboxStates.Open,
449
+ disabled,
450
+ invalid,
451
+ value,
452
+ } satisfies ListboxRenderPropArg<TType>)
453
+
454
+ const selectOption = (id: string) => {
455
+ let option = _state.options.find((item) => item.id === id)
456
+ if (!option) return
457
+
458
+ //onchange(option.dataRef.current.value)
309
459
  }
310
- };
311
- setContext("ListboxDataContext", data);
312
- const outsideClickEnabled = $derived(data.listboxState === ListboxStates.Open);
313
- useOutsideClick({
314
- get enabled() {
315
- return outsideClickEnabled;
316
- },
317
- get containers() {
318
- return [data.buttonElement, data.optionsElement];
319
- },
320
- cb: (event, target) => {
321
- _state.closeListbox();
322
- if (!isFocusableElement(target, FocusableMode.Loose)) {
323
- event.preventDefault();
324
- data.buttonElement?.focus();
460
+
461
+ const selectActiveOption = () => {
462
+ if (_state.activeOptionIndex !== null) {
463
+ let { dataRef, id } = _state.options[_state.activeOptionIndex]
464
+ //onchange(dataRef.current.value)
465
+
466
+ // It could happen that the `activeOptionIndex` stored in state is actually null,
467
+ // but we are getting the fallback ace2437tive option back instead.424323
468
+ _state.goToOption({ focus: Focus.Specific, id })
325
469
  }
470
+ 7
326
471
  }
327
- });
328
- const slot = $derived({
329
- open: _state.listboxState === ListboxStates.Open,
330
- disabled,
331
- invalid,
332
- value
333
- });
334
- const selectOption = (id) => {
335
- let option = _state.options.find((item) => item.id === id);
336
- if (!option) return;
337
- };
338
- const selectActiveOption = () => {
339
- if (_state.activeOptionIndex !== null) {
340
- let { dataRef, id } = _state.options[_state.activeOptionIndex];
341
- _state.goToOption({ focus: Focus.Specific, id });
472
+
473
+ const d = disposables()
474
+ const goToOption = (focus: Focus, id: string, trigger: ActivationTrigger) => {
475
+ d.dispose()
476
+ d.microTask(() => {
477
+ if (focus === Focus.Specific) {
478
+ return _state.goToOption({ focus: Focus.Specific, id: id!, trigger })
479
+ }
480
+
481
+ return _state.goToOption({ focus, trigger })
482
+ })
342
483
  }
343
- 7;
344
- };
345
- const d = disposables();
346
- const goToOption = (focus, id, trigger) => {
347
- d.dispose();
348
- d.microTask(() => {
349
- if (focus === Focus.Specific) {
350
- return _state.goToOption({ focus: Focus.Specific, id, trigger });
351
- }
352
- return _state.goToOption({ focus, trigger });
353
- });
354
- };
355
- const registerOption = (id, dataRef) => {
356
- _state.registerOption(id, dataRef);
357
- return () => _state.unregisterOption(id);
358
- };
359
- const onChange = (value2) => {
360
- return match(data.mode, {
361
- [ValueMode.Single]() {
362
- return theirOnChange?.(value2);
484
+
485
+ const registerOption = (id: string, dataRef: ListboxOptionDataRef<TActualType>) => {
486
+ _state.registerOption(id, dataRef)
487
+ return () => _state.unregisterOption(id)
488
+ }
489
+
490
+ const onChange = (value: unknown) => {
491
+ return match(data.mode, {
492
+ [ValueMode.Single]() {
493
+ return theirOnChange?.(value as TType)
494
+ },
495
+ [ValueMode.Multi]() {
496
+ let copy = (data.value as TActualType[]).slice()
497
+
498
+ let idx = copy.findIndex((item) => compare(item, value as TActualType))
499
+ if (idx === -1) {
500
+ copy.push(value as TActualType)
501
+ } else {
502
+ copy.splice(idx, 1)
503
+ }
504
+
505
+ return theirOnChange?.(copy as unknown as TType[])
506
+ },
507
+ })
508
+ }
509
+
510
+ setContext<ListboxActionsContext<TActualType>>("ListboxActionsContext", {
511
+ onChange,
512
+ registerOption,
513
+ goToOption,
514
+ closeListbox: _state.closeListbox,
515
+ openListbox: _state.openListbox,
516
+ selectActiveOption,
517
+ selectOption,
518
+ search: _state.search,
519
+ clearSearch: _state.clearSearch,
520
+ })
521
+
522
+ useFloatingProvider()
523
+
524
+ const openClosed = $derived(
525
+ match(data.listboxState, {
526
+ [ListboxStates.Open]: State.Open,
527
+ [ListboxStates.Closed]: State.Closed,
528
+ })
529
+ )
530
+ createOpenClosedContext({
531
+ get value() {
532
+ return openClosed
363
533
  },
364
- [ValueMode.Multi]() {
365
- let copy = data.value.slice();
366
- let idx = copy.findIndex((item) => compare(item, value2));
367
- if (idx === -1) {
368
- copy.push(value2);
369
- } else {
370
- copy.splice(idx, 1);
371
- }
372
- return theirOnChange?.(copy);
373
- }
374
- });
375
- };
376
- setContext("ListboxActionsContext", {
377
- onChange,
378
- registerOption,
379
- goToOption,
380
- closeListbox: _state.closeListbox,
381
- openListbox: _state.openListbox,
382
- selectActiveOption,
383
- selectOption,
384
- search: _state.search,
385
- clearSearch: _state.clearSearch
386
- });
387
- useFloatingProvider();
388
- const openClosed = $derived(
389
- match(data.listboxState, {
390
- [ListboxStates.Open]: State.Open,
391
- [ListboxStates.Closed]: State.Closed
392
534
  })
393
- );
394
- createOpenClosedContext({
395
- get value() {
396
- return openClosed;
397
- }
398
- });
399
- useLabels({
400
- inherit: true,
401
- props: {
402
- get htmlFor() {
403
- return data.buttonElement?.id;
404
- }
405
- },
406
- slot: {
407
- get open() {
408
- return data.listboxState === ListboxStates.Open;
535
+
536
+ useLabels({
537
+ inherit: true,
538
+ props: {
539
+ get htmlFor() {
540
+ return data.buttonElement?.id
541
+ },
409
542
  },
410
- get disabled() {
411
- return disabled;
412
- }
543
+ slot: {
544
+ get open() {
545
+ return data.listboxState === ListboxStates.Open
546
+ },
547
+ get disabled() {
548
+ return disabled
549
+ },
550
+ },
551
+ })
552
+
553
+ const reset = () => {
554
+ if (defaultValue === undefined) return
555
+ return theirOnChange?.(defaultValue)
413
556
  }
414
- });
415
- const reset = () => {
416
- if (defaultValue === void 0) return;
417
- return theirOnChange?.(defaultValue);
418
- };
419
557
  </script>
420
558
 
421
559
  {#if name && value}