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