@pzerelles/headlessui-svelte 2.1.2-next.29 → 2.1.2-next.30

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