@pzerelles/headlessui-svelte 2.1.2-next.30 → 2.1.2-next.32
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.
- package/dist/button/Button.svelte +84 -54
- package/dist/button/Button.svelte.d.ts +2 -3
- package/dist/checkbox/Checkbox.svelte +173 -120
- package/dist/checkbox/Checkbox.svelte.d.ts +2 -3
- package/dist/close-button/CloseButton.svelte +12 -6
- package/dist/close-button/CloseButton.svelte.d.ts +7 -8
- package/dist/combobox/Combobox.svelte +50 -3
- package/dist/data-interactive/DataInteractive.svelte +55 -29
- package/dist/data-interactive/DataInteractive.svelte.d.ts +2 -3
- package/dist/description/Description.svelte +31 -21
- package/dist/description/Description.svelte.d.ts +1 -2
- package/dist/dialog/Dialog.svelte +320 -228
- package/dist/dialog/Dialog.svelte.d.ts +2 -3
- package/dist/dialog/DialogBackdrop.svelte +30 -13
- package/dist/dialog/DialogBackdrop.svelte.d.ts +2 -3
- package/dist/dialog/DialogPanel.svelte +49 -26
- package/dist/dialog/DialogPanel.svelte.d.ts +2 -3
- package/dist/dialog/DialogTitle.svelte +38 -23
- package/dist/dialog/DialogTitle.svelte.d.ts +2 -3
- package/dist/field/Field.svelte +47 -25
- package/dist/field/Field.svelte.d.ts +1 -2
- package/dist/fieldset/Fieldset.svelte +50 -29
- package/dist/fieldset/Fieldset.svelte.d.ts +1 -2
- package/dist/focus-trap/FocusTrap.svelte +419 -283
- package/dist/focus-trap/FocusTrap.svelte.d.ts +1 -2
- package/dist/input/Input.svelte +84 -53
- package/dist/input/Input.svelte.d.ts +2 -3
- package/dist/internal/FloatingProvider.svelte +14 -9
- package/dist/internal/FocusSentinel.svelte +16 -8
- package/dist/internal/ForcePortalRoot.svelte +7 -3
- package/dist/internal/FormFields.svelte +47 -34
- package/dist/internal/FormFieldsProvider.svelte +9 -5
- package/dist/internal/FormResolver.svelte +20 -15
- package/dist/internal/Hidden.svelte +50 -29
- package/dist/internal/Hidden.svelte.d.ts +1 -2
- package/dist/internal/MainTreeProvider.svelte +89 -36
- package/dist/internal/Portal.svelte +18 -14
- package/dist/internal/floating.svelte.d.ts +2 -3
- package/dist/internal/floating.svelte.js +0 -1
- package/dist/label/Label.svelte +93 -58
- package/dist/label/Label.svelte.d.ts +1 -2
- package/dist/legend/Legend.svelte +12 -3
- package/dist/listbox/Listbox.svelte +525 -387
- package/dist/listbox/Listbox.svelte.d.ts +2 -3
- package/dist/listbox/ListboxButton.svelte +173 -127
- package/dist/listbox/ListboxButton.svelte.d.ts +2 -3
- package/dist/listbox/ListboxOption.svelte +170 -129
- package/dist/listbox/ListboxOption.svelte.d.ts +2 -3
- package/dist/listbox/ListboxOptions.svelte +400 -304
- package/dist/listbox/ListboxOptions.svelte.d.ts +2 -3
- package/dist/listbox/ListboxSelectedOption.svelte +38 -15
- package/dist/listbox/ListboxSelectedOption.svelte.d.ts +1 -2
- package/dist/menu/Menu.svelte +77 -51
- package/dist/menu/Menu.svelte.d.ts +2 -4
- package/dist/menu/MenuButton.svelte +157 -117
- package/dist/menu/MenuButton.svelte.d.ts +2 -3
- package/dist/menu/MenuHeading.svelte +32 -14
- package/dist/menu/MenuHeading.svelte.d.ts +1 -2
- package/dist/menu/MenuItem.svelte +142 -107
- package/dist/menu/MenuItem.svelte.d.ts +2 -3
- package/dist/menu/MenuItems.svelte +301 -229
- package/dist/menu/MenuItems.svelte.d.ts +2 -3
- package/dist/menu/MenuSection.svelte +24 -9
- package/dist/menu/MenuSection.svelte.d.ts +1 -2
- package/dist/menu/MenuSeparator.svelte +17 -4
- package/dist/menu/MenuSeparator.svelte.d.ts +1 -2
- package/dist/popover/Popover.svelte +216 -150
- package/dist/popover/Popover.svelte.d.ts +2 -3
- package/dist/popover/PopoverBackdrop.svelte +67 -41
- package/dist/popover/PopoverBackdrop.svelte.d.ts +2 -3
- package/dist/popover/PopoverButton.svelte +292 -212
- package/dist/popover/PopoverButton.svelte.d.ts +2 -3
- package/dist/popover/PopoverGroup.svelte +62 -35
- package/dist/popover/PopoverGroup.svelte.d.ts +1 -2
- package/dist/popover/PopoverPanel.svelte +311 -229
- package/dist/popover/PopoverPanel.svelte.d.ts +2 -3
- package/dist/portal/InternalPortal.svelte +141 -85
- package/dist/portal/InternalPortal.svelte.d.ts +1 -2
- package/dist/portal/Portal.svelte +5 -2
- package/dist/portal/PortalGroup.svelte +30 -9
- package/dist/portal/PortalGroup.svelte.d.ts +1 -2
- package/dist/select/Select.svelte +98 -68
- package/dist/select/Select.svelte.d.ts +2 -3
- package/dist/switch/Switch.svelte +179 -132
- package/dist/switch/Switch.svelte.d.ts +2 -3
- package/dist/switch/SwitchGroup.svelte +44 -31
- package/dist/switch/SwitchGroup.svelte.d.ts +1 -2
- package/dist/tabs/Tab.svelte +194 -142
- package/dist/tabs/Tab.svelte.d.ts +2 -3
- package/dist/tabs/TabGroup.svelte +86 -56
- package/dist/tabs/TabGroup.svelte.d.ts +2 -3
- package/dist/tabs/TabList.svelte +31 -11
- package/dist/tabs/TabList.svelte.d.ts +2 -3
- package/dist/tabs/TabPanel.svelte +67 -42
- package/dist/tabs/TabPanel.svelte.d.ts +2 -3
- package/dist/tabs/TabPanels.svelte +18 -7
- package/dist/tabs/TabPanels.svelte.d.ts +2 -3
- package/dist/textarea/Textarea.svelte +84 -53
- package/dist/textarea/Textarea.svelte.d.ts +2 -3
- package/dist/transition/InternalTransitionChild.svelte +259 -170
- package/dist/transition/InternalTransitionChild.svelte.d.ts +2 -3
- package/dist/transition/Transition.svelte +96 -66
- package/dist/transition/Transition.svelte.d.ts +2 -3
- package/dist/transition/TransitionChild.svelte +31 -11
- package/dist/transition/TransitionChild.svelte.d.ts +2 -3
- package/dist/utils/DisabledProvider.svelte +7 -3
- package/dist/utils/ElementOrComponent.svelte +46 -23
- package/dist/utils/ElementOrComponent.svelte.d.ts +8 -10
- package/dist/utils/Generic.svelte +30 -19
- package/dist/utils/Generic.svelte.d.ts +6 -7
- package/dist/utils/StableCollection.svelte +54 -36
- package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
- package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
- package/dist/utils/types.d.ts +4 -5
- package/package.json +1 -1
|
@@ -1,424 +1,562 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { useByComparator, type ByComparator } from "../hooks/use-by-comparator.js"
|
|
3
|
+
import { useControllable } from "../hooks/use-controllable.svelte.js"
|
|
4
|
+
import { useDisabled } from "../hooks/use-disabled.js"
|
|
5
|
+
import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js"
|
|
6
|
+
import { FocusableMode, isFocusableElement, sortByDomNode } from "../utils/focus-management.js"
|
|
7
|
+
import { match } from "../utils/match.js"
|
|
8
|
+
import type { ElementType, EnsureArray, Props } from "../utils/types.js"
|
|
9
|
+
import { setContext, type Snippet } from "svelte"
|
|
10
|
+
import { ActivationTrigger, ListboxStates, ValueMode } from "./context.svelte.js"
|
|
11
|
+
|
|
12
|
+
let DEFAULT_LISTBOX_TAG = "svelte:fragment"
|
|
13
|
+
type ListboxRenderPropArg<T> = {
|
|
14
|
+
open: boolean
|
|
15
|
+
disabled: boolean
|
|
16
|
+
invalid: boolean
|
|
17
|
+
value: T
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ListboxProps<
|
|
21
|
+
TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG,
|
|
22
|
+
TType = string,
|
|
23
|
+
TActualType = TType extends (infer U)[] ? U : TType,
|
|
24
|
+
> = Props<
|
|
25
|
+
TTag,
|
|
26
|
+
ListboxRenderPropArg<TType>,
|
|
27
|
+
"value" | "defaultValue" | "onchange" | "by" | "disabled" | "horizontal" | "name" | "multiple",
|
|
28
|
+
{
|
|
29
|
+
value?: TType
|
|
30
|
+
defaultValue?: TType
|
|
31
|
+
onchange?: (value: TType) => void
|
|
32
|
+
by?: ByComparator<TActualType>
|
|
33
|
+
disabled?: boolean
|
|
34
|
+
invalid?: boolean
|
|
35
|
+
horizontal?: boolean
|
|
36
|
+
form?: string
|
|
37
|
+
name?: string
|
|
38
|
+
multiple?: boolean
|
|
39
|
+
closeOnSelect?: boolean
|
|
40
|
+
|
|
41
|
+
__demoMode?: boolean
|
|
42
|
+
}
|
|
43
|
+
>
|
|
44
|
+
|
|
45
|
+
export * from "./context.svelte.js"
|
|
11
46
|
</script>
|
|
12
47
|
|
|
13
48
|
<script
|
|
14
49
|
lang="ts"
|
|
15
50
|
generics="TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG, TType = string, TActualType = TType extends (infer U)[] ? U : TType"
|
|
16
|
-
>
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
>
|
|
52
|
+
import { disposables } from "../utils/disposables.js"
|
|
53
|
+
import FormFields from "../internal/FormFields.svelte"
|
|
54
|
+
import { useFloatingProvider } from "../internal/floating-provider.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
|
+
}
|
|
33
92
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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> = {
|
|
42
372
|
get listboxState() {
|
|
43
|
-
return
|
|
373
|
+
return _state.listboxState
|
|
44
374
|
},
|
|
45
375
|
get options() {
|
|
46
|
-
return
|
|
376
|
+
return _state.options
|
|
47
377
|
},
|
|
48
378
|
get searchQuery() {
|
|
49
|
-
return
|
|
379
|
+
return _state.searchQuery
|
|
50
380
|
},
|
|
51
381
|
get activeOptionIndex() {
|
|
52
|
-
return
|
|
382
|
+
return _state.activeOptionIndex
|
|
53
383
|
},
|
|
54
384
|
get activationTrigger() {
|
|
55
|
-
return
|
|
385
|
+
return _state.activationTrigger
|
|
56
386
|
},
|
|
57
387
|
get __demoMode() {
|
|
58
|
-
return
|
|
388
|
+
return _state.__demoMode
|
|
59
389
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (_state2.listboxState === ListboxStates.Closed) return _state2;
|
|
63
|
-
_state2.activeOptionIndex = null;
|
|
64
|
-
_state2.listboxState = ListboxStates.Closed;
|
|
65
|
-
_state2.__demoMode = false;
|
|
66
|
-
return _state2;
|
|
390
|
+
get value() {
|
|
391
|
+
return value
|
|
67
392
|
},
|
|
68
|
-
|
|
69
|
-
|
|
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;
|
|
393
|
+
get disabled() {
|
|
394
|
+
return disabled
|
|
80
395
|
},
|
|
81
|
-
|
|
82
|
-
|
|
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;
|
|
396
|
+
get invalid() {
|
|
397
|
+
return invalid
|
|
149
398
|
},
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
|
399
|
+
get mode() {
|
|
400
|
+
return multiple ? ValueMode.Multi : ValueMode.Single
|
|
169
401
|
},
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (_state2.listboxState === ListboxStates.Closed) return _state2;
|
|
173
|
-
if (_state2.searchQuery === "") return _state2;
|
|
174
|
-
_state2.searchQuery = "";
|
|
175
|
-
return _state2;
|
|
402
|
+
get orientation() {
|
|
403
|
+
return orientation
|
|
176
404
|
},
|
|
177
|
-
|
|
178
|
-
|
|
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;
|
|
405
|
+
get closeOnSelect() {
|
|
406
|
+
return closeOnSelect
|
|
188
407
|
},
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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;
|
|
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
|
|
424
|
+
},
|
|
425
|
+
get listElements() {
|
|
426
|
+
return listElements
|
|
225
427
|
},
|
|
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
428
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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;
|
|
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
|
|
436
|
+
},
|
|
437
|
+
get containers() {
|
|
438
|
+
return [data.buttonElement, data.optionsElement]
|
|
439
|
+
},
|
|
440
|
+
cb: (event, target) => {
|
|
441
|
+
_state.closeListbox()
|
|
442
|
+
|
|
443
|
+
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
|
444
|
+
event.preventDefault()
|
|
445
|
+
data.buttonElement?.focus()
|
|
446
|
+
}
|
|
447
|
+
},
|
|
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)
|
|
312
462
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
},
|
|
323
|
-
cb: (event, target) => {
|
|
324
|
-
_state.closeListbox();
|
|
325
|
-
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
|
326
|
-
event.preventDefault();
|
|
327
|
-
data.buttonElement?.focus();
|
|
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 })
|
|
328
472
|
}
|
|
473
|
+
7
|
|
329
474
|
}
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
const selectActiveOption = () => {
|
|
342
|
-
if (_state.activeOptionIndex !== null) {
|
|
343
|
-
let { dataRef, id } = _state.options[_state.activeOptionIndex];
|
|
344
|
-
_state.goToOption({ focus: Focus.Specific, id });
|
|
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
|
+
})
|
|
345
486
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
487
|
+
|
|
488
|
+
const registerOption = (id: string, dataRef: ListboxOptionDataRef<TActualType>) => {
|
|
489
|
+
_state.registerOption(id, dataRef)
|
|
490
|
+
return () => _state.unregisterOption(id)
|
|
491
|
+
}
|
|
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
|
+
})
|
|
511
|
+
}
|
|
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
|
|
366
536
|
},
|
|
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
537
|
})
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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;
|
|
538
|
+
|
|
539
|
+
useLabels({
|
|
540
|
+
inherit: true,
|
|
541
|
+
props: {
|
|
542
|
+
get htmlFor() {
|
|
543
|
+
return data.buttonElement?.id
|
|
544
|
+
},
|
|
412
545
|
},
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
546
|
+
slot: {
|
|
547
|
+
get open() {
|
|
548
|
+
return data.listboxState === ListboxStates.Open
|
|
549
|
+
},
|
|
550
|
+
get disabled() {
|
|
551
|
+
return disabled
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
const reset = () => {
|
|
557
|
+
if (defaultValue === undefined) return
|
|
558
|
+
return theirOnChange?.(defaultValue)
|
|
416
559
|
}
|
|
417
|
-
});
|
|
418
|
-
const reset = () => {
|
|
419
|
-
if (defaultValue === void 0) return;
|
|
420
|
-
return theirOnChange?.(defaultValue);
|
|
421
|
-
};
|
|
422
560
|
</script>
|
|
423
561
|
|
|
424
562
|
{#if name && value}
|