@pzerelles/headlessui-svelte 2.1.2-next.7 → 2.1.2-next.8

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