@pzerelles/headlessui-svelte 2.1.2-next.31 → 2.1.2-next.33

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