@pzerelles/headlessui-svelte 2.1.2-next.2 → 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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- 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 +97 -0
- package/dist/textarea/Textarea.svelte.d.ts +47 -0
- package/dist/textarea/index.d.ts +1 -0
- package/dist/textarea/index.js +1 -0
- 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,139 +1,188 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { useId } from "../hooks/use-id.js"
|
|
3
|
+
import { getIdContext } from "../utils/id.js"
|
|
4
|
+
import type { ElementType, Props } from "../utils/types.js"
|
|
5
|
+
import { ListboxStates, useActions, useData } from "./Listbox.svelte"
|
|
6
|
+
import { attemptSubmit } from "../utils/form.js"
|
|
7
|
+
import { Focus } from "../utils/calculate-active-index.js"
|
|
8
|
+
import { useFocusRing } from "../hooks/use-focus-ring.svelte.js"
|
|
9
|
+
import { useActivePress } from "../hooks/use-active-press.svelte.js"
|
|
10
|
+
import { useResolveButtonType } from "../hooks/use-resolve-button-type.svelte.js"
|
|
11
|
+
import { useFloating } from "../internal/floating.svelte.js"
|
|
12
|
+
import { stateFromSlot } from "../utils/state.js"
|
|
13
|
+
import type { Snippet } from "svelte"
|
|
14
|
+
import { useLabelledBy } from "../label/context.svelte.js"
|
|
15
|
+
import { useDescribedBy } from "../description/context.svelte.js"
|
|
16
|
+
import { useHover } from "../hooks/use-hover.svelte.js"
|
|
17
|
+
import { mergeProps } from "../utils/render.js"
|
|
18
|
+
import ElementOrComponent from "../utils/ElementOrComponent.svelte"
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
...theirProps
|
|
30
|
-
} = $props();
|
|
31
|
-
const { setReference, getReferenceProps: getFloatingReferenceProps } = useFloating();
|
|
32
|
-
$effect(() => {
|
|
33
|
-
data.buttonRef.current = ref || null;
|
|
34
|
-
setReference(ref);
|
|
35
|
-
});
|
|
36
|
-
const disabled = $derived(data.disabled || ownDisabled);
|
|
37
|
-
const handleKeyDown = (event) => {
|
|
38
|
-
switch (event.key) {
|
|
39
|
-
case "Enter":
|
|
40
|
-
if (event.currentTarget) attemptSubmit(event.currentTarget);
|
|
41
|
-
break;
|
|
42
|
-
case " ":
|
|
43
|
-
case "ArrowDown":
|
|
44
|
-
event.preventDefault();
|
|
45
|
-
actions.openListbox();
|
|
46
|
-
if (!data.value) actions.goToOption(Focus.First);
|
|
47
|
-
break;
|
|
48
|
-
case "ArrowUp":
|
|
49
|
-
event.preventDefault();
|
|
50
|
-
actions.openListbox();
|
|
51
|
-
if (!data.value) actions.goToOption(Focus.Last);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
const handleKeyUp = (event) => {
|
|
56
|
-
switch (event.key) {
|
|
57
|
-
case " ":
|
|
58
|
-
event.preventDefault();
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
const handleClick = (event) => {
|
|
63
|
-
if (data.listboxState === ListboxStates.Open) {
|
|
64
|
-
actions.closeListbox();
|
|
65
|
-
data.buttonRef.current?.focus({ preventScroll: true });
|
|
66
|
-
} else {
|
|
67
|
-
event.preventDefault();
|
|
68
|
-
actions.openListbox();
|
|
20
|
+
const DEFAULT_BUTTON_TAG = "button" as const
|
|
21
|
+
type ButtonRenderPropArg = {
|
|
22
|
+
disabled: boolean
|
|
23
|
+
invalid: boolean
|
|
24
|
+
hover: boolean
|
|
25
|
+
focus: boolean
|
|
26
|
+
autofocus: boolean
|
|
27
|
+
open: boolean
|
|
28
|
+
active: boolean
|
|
29
|
+
value: any
|
|
69
30
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
31
|
+
type ButtonPropsWeControl = "aria-controls" | "aria-expanded" | "aria-haspopup" | "aria-labelledby" | "disabled"
|
|
32
|
+
|
|
33
|
+
export type ListboxButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<
|
|
34
|
+
TTag,
|
|
35
|
+
ButtonRenderPropArg,
|
|
36
|
+
ButtonPropsWeControl,
|
|
37
|
+
{
|
|
38
|
+
id?: string
|
|
39
|
+
autofocus?: boolean
|
|
40
|
+
disabled?: boolean
|
|
78
41
|
}
|
|
42
|
+
>
|
|
43
|
+
|
|
44
|
+
export type ListboxButtonChildren = Snippet<[ButtonRenderPropArg]>
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_BUTTON_TAG">
|
|
48
|
+
const data = useData("ListboxButton")
|
|
49
|
+
const actions = useActions("ListboxButton")
|
|
50
|
+
|
|
51
|
+
const internalId = useId()
|
|
52
|
+
const providedId = getIdContext()
|
|
53
|
+
let {
|
|
54
|
+
as = DEFAULT_BUTTON_TAG as TTag,
|
|
55
|
+
ref = $bindable(),
|
|
56
|
+
id = providedId || `headlessui-listbox-button-${internalId}`,
|
|
57
|
+
disabled: ownDisabled = false,
|
|
58
|
+
autofocus = false,
|
|
59
|
+
...theirProps
|
|
60
|
+
}: { as?: TTag } & ListboxButtonProps<TTag> = $props()
|
|
61
|
+
const { setReference, getReferenceProps: getFloatingReferenceProps } = useFloating()
|
|
62
|
+
$effect(() => {
|
|
63
|
+
data.buttonRef.current = ref || null
|
|
64
|
+
setReference(ref)
|
|
79
65
|
})
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
66
|
+
|
|
67
|
+
const disabled = $derived(data.disabled || ownDisabled)
|
|
68
|
+
|
|
69
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
70
|
+
switch (event.key) {
|
|
71
|
+
// Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/#keyboard-interaction-13
|
|
72
|
+
|
|
73
|
+
case "Enter":
|
|
74
|
+
if (event.currentTarget) attemptSubmit(event.currentTarget as HTMLElement)
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
case " ":
|
|
78
|
+
case "ArrowDown":
|
|
79
|
+
event.preventDefault()
|
|
80
|
+
actions.openListbox()
|
|
81
|
+
if (!data.value) actions.goToOption(Focus.First)
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
case "ArrowUp":
|
|
85
|
+
event.preventDefault()
|
|
86
|
+
actions.openListbox()
|
|
87
|
+
if (!data.value) actions.goToOption(Focus.Last)
|
|
88
|
+
break
|
|
85
89
|
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const handleKeyUp = (event: KeyboardEvent) => {
|
|
93
|
+
switch (event.key) {
|
|
94
|
+
case " ":
|
|
95
|
+
// Required for firefox, event.preventDefault() in handleKeyDown for
|
|
96
|
+
// the Space key doesn't cancel the handleKeyUp, which in turn
|
|
97
|
+
// triggers a *click*.
|
|
98
|
+
event.preventDefault()
|
|
99
|
+
break
|
|
92
100
|
}
|
|
93
|
-
})
|
|
94
|
-
);
|
|
95
|
-
const slot = $derived({
|
|
96
|
-
open: data.listboxState === ListboxStates.Open,
|
|
97
|
-
active: active || data.listboxState === ListboxStates.Open,
|
|
98
|
-
disabled,
|
|
99
|
-
invalid: data.invalid,
|
|
100
|
-
value: data.value,
|
|
101
|
-
hover,
|
|
102
|
-
focus,
|
|
103
|
-
autofocus: autofocus ?? false
|
|
104
|
-
});
|
|
105
|
-
const buttonType = useResolveButtonType({
|
|
106
|
-
get props() {
|
|
107
|
-
return { type: theirProps.type, as };
|
|
108
|
-
},
|
|
109
|
-
get ref() {
|
|
110
|
-
return data.buttonRef;
|
|
111
101
|
}
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
102
|
+
|
|
103
|
+
const handleClick = (event: MouseEvent) => {
|
|
104
|
+
//if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()
|
|
105
|
+
if (data.listboxState === ListboxStates.Open) {
|
|
106
|
+
actions.closeListbox()
|
|
107
|
+
data.buttonRef.current?.focus({ preventScroll: true })
|
|
108
|
+
} else {
|
|
109
|
+
event.preventDefault()
|
|
110
|
+
actions.openListbox()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// This is needed so that we can "cancel" the click event when we use the `Enter` key on a button.
|
|
115
|
+
const handleKeyPress = (event: KeyboardEvent) => event.preventDefault()
|
|
116
|
+
|
|
117
|
+
const labelledBy = useLabelledBy()
|
|
118
|
+
const describedBy = useDescribedBy()
|
|
119
|
+
|
|
120
|
+
const { isHovered: hover, hoverProps } = $derived(
|
|
121
|
+
useHover({
|
|
122
|
+
get disabled() {
|
|
123
|
+
return disabled
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
const { pressed: active, pressProps } = $derived(
|
|
128
|
+
useActivePress({
|
|
129
|
+
get disabled() {
|
|
130
|
+
return disabled
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
)
|
|
134
|
+
const { isFocusVisible: focus, focusProps } = $derived(
|
|
135
|
+
useFocusRing({
|
|
136
|
+
get autofocus() {
|
|
137
|
+
return autofocus
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const slot = $derived({
|
|
143
|
+
open: data.listboxState === ListboxStates.Open,
|
|
144
|
+
active: active || data.listboxState === ListboxStates.Open,
|
|
145
|
+
disabled,
|
|
146
|
+
invalid: data.invalid,
|
|
147
|
+
value: data.value,
|
|
148
|
+
hover,
|
|
149
|
+
focus,
|
|
150
|
+
autofocus: autofocus ?? false,
|
|
151
|
+
} satisfies ButtonRenderPropArg)
|
|
152
|
+
|
|
153
|
+
const buttonType = useResolveButtonType({
|
|
154
|
+
get props() {
|
|
155
|
+
return { type: theirProps.type, as }
|
|
156
|
+
},
|
|
157
|
+
get ref() {
|
|
158
|
+
return data.buttonRef
|
|
130
159
|
},
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const ourProps = $derived(
|
|
163
|
+
mergeProps(
|
|
164
|
+
{
|
|
165
|
+
...getFloatingReferenceProps(),
|
|
166
|
+
id,
|
|
167
|
+
type: buttonType.type,
|
|
168
|
+
"aria-haspopup": "listbox",
|
|
169
|
+
"aria-controls": data.optionsRef.current?.id,
|
|
170
|
+
"aria-expanded": data.listboxState === ListboxStates.Open,
|
|
171
|
+
"aria-labelledby": labelledBy.value,
|
|
172
|
+
"aria-describedby": describedBy.value,
|
|
173
|
+
disabled: disabled || undefined,
|
|
174
|
+
autofocus,
|
|
175
|
+
onkeydown: handleKeyDown,
|
|
176
|
+
onkeyup: handleKeyUp,
|
|
177
|
+
onkeypress: handleKeyPress,
|
|
178
|
+
onclick: handleClick,
|
|
179
|
+
},
|
|
180
|
+
focusProps,
|
|
181
|
+
hoverProps,
|
|
182
|
+
pressProps,
|
|
183
|
+
stateFromSlot(slot)
|
|
184
|
+
)
|
|
135
185
|
)
|
|
136
|
-
);
|
|
137
186
|
</script>
|
|
138
187
|
|
|
139
188
|
<ElementOrComponent {ourProps} {theirProps} {slot} defaultTag={DEFAULT_BUTTON_TAG} name="ListboxButton" bind:ref />
|
|
@@ -1,134 +1,175 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { ElementType, Props } from "../utils/types.js"
|
|
3
|
+
|
|
4
|
+
const DEFAULT_OPTION_TAG = "div" as const
|
|
5
|
+
type OptionRenderPropArg = {
|
|
6
|
+
/** @deprecated use `focus` instead */
|
|
7
|
+
active: boolean
|
|
8
|
+
focus: boolean
|
|
9
|
+
selected: boolean
|
|
10
|
+
disabled: boolean
|
|
11
|
+
|
|
12
|
+
selectedOption: boolean
|
|
13
|
+
}
|
|
14
|
+
type OptionPropsWeControl = "aria-disabled" | "aria-selected" | "role" | "tabIndex"
|
|
15
|
+
|
|
16
|
+
export type ListboxOptionProps<TTag extends ElementType = typeof DEFAULT_OPTION_TAG, TType = string> = Props<
|
|
17
|
+
TTag,
|
|
18
|
+
OptionRenderPropArg,
|
|
19
|
+
OptionPropsWeControl,
|
|
20
|
+
{
|
|
21
|
+
id?: string
|
|
22
|
+
disabled?: boolean
|
|
23
|
+
value: TType
|
|
24
|
+
}
|
|
25
|
+
>
|
|
26
|
+
|
|
27
|
+
export type ListboxOptionChildren = Snippet<[OptionRenderPropArg]>
|
|
2
28
|
</script>
|
|
3
29
|
|
|
4
|
-
<script lang="ts" generics="TType, TTag extends ElementType = typeof DEFAULT_OPTION_TAG">
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
<script lang="ts" generics="TType, TTag extends ElementType = typeof DEFAULT_OPTION_TAG">
|
|
31
|
+
import { useId } from "../hooks/use-id.js"
|
|
32
|
+
import {
|
|
33
|
+
ActivationTrigger,
|
|
34
|
+
ListboxStates,
|
|
35
|
+
useActions,
|
|
36
|
+
useData,
|
|
37
|
+
ValueMode,
|
|
38
|
+
type ListboxOptionDataRef,
|
|
39
|
+
} from "./Listbox.svelte"
|
|
40
|
+
import { disposables } from "../utils/disposables.js"
|
|
41
|
+
import { Focus } from "../utils/calculate-active-index.js"
|
|
42
|
+
import { getContext, onMount, type Snippet } from "svelte"
|
|
43
|
+
import { useTextValue } from "../hooks/use-text-value.svelte.js"
|
|
44
|
+
import { useTrackedPointer } from "../hooks/use-tracked-pointer.js"
|
|
45
|
+
import { stateFromSlot } from "../utils/state.js"
|
|
46
|
+
import ElementOrComponent from "../utils/ElementOrComponent.svelte"
|
|
47
|
+
|
|
48
|
+
const internalId = useId()
|
|
49
|
+
let {
|
|
50
|
+
as = DEFAULT_OPTION_TAG as TTag,
|
|
51
|
+
ref = $bindable(),
|
|
52
|
+
id = `headlessui-listbox-option-${internalId}`,
|
|
53
|
+
disabled = false,
|
|
54
|
+
value,
|
|
55
|
+
...theirProps
|
|
56
|
+
}: { as?: TTag } & ListboxOptionProps<TTag, TType> = $props()
|
|
57
|
+
const usedInSelectedOption = getContext<boolean>("SelectedOptionContext") === true
|
|
58
|
+
const data = useData("ListboxOption")
|
|
59
|
+
const actions = useActions("ListboxOption")
|
|
60
|
+
|
|
61
|
+
const active = $derived(data.activeOptionIndex !== null ? data.options[data.activeOptionIndex].id === id : false)
|
|
62
|
+
|
|
63
|
+
const selected = $derived(data.isSelected(value))
|
|
64
|
+
const getTextValue = useTextValue({
|
|
65
|
+
get element() {
|
|
66
|
+
return ref as HTMLElement
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
const bag: ListboxOptionDataRef<TType>["current"] = $derived({
|
|
70
|
+
disabled,
|
|
71
|
+
value,
|
|
72
|
+
domRef: { current: ref || null },
|
|
73
|
+
get textValue() {
|
|
74
|
+
return getTextValue()
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
$effect(() => {
|
|
79
|
+
if (!ref) {
|
|
80
|
+
data.listRef.current.delete(id)
|
|
81
|
+
} else {
|
|
82
|
+
data.listRef.current.set(id, ref)
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
$effect(() => {
|
|
87
|
+
if (data.__demoMode) return
|
|
88
|
+
if (data.listboxState !== ListboxStates.Open) return
|
|
89
|
+
if (!active) return
|
|
90
|
+
if (data.activationTrigger === ActivationTrigger.Pointer) return
|
|
91
|
+
return disposables().requestAnimationFrame(() => {
|
|
92
|
+
;(ref as HTMLElement)?.scrollIntoView?.({ block: "nearest" })
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
onMount(() => {
|
|
97
|
+
if (usedInSelectedOption) return
|
|
98
|
+
return actions.registerOption(id, {
|
|
99
|
+
get current() {
|
|
100
|
+
return bag
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const handleClick = (event: { preventDefault: Function }) => {
|
|
106
|
+
if (disabled) return event.preventDefault()
|
|
107
|
+
actions.onChange(value)
|
|
108
|
+
if (data.mode === ValueMode.Single) {
|
|
109
|
+
actions.closeListbox()
|
|
110
|
+
data.buttonRef.current?.focus({ preventScroll: true })
|
|
111
|
+
}
|
|
36
112
|
}
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
domRef: { current: ref || null },
|
|
42
|
-
get textValue() {
|
|
43
|
-
return getTextValue();
|
|
113
|
+
|
|
114
|
+
const handleFocus = () => {
|
|
115
|
+
if (disabled) return actions.goToOption(Focus.Nothing)
|
|
116
|
+
actions.goToOption(Focus.Specific, id)
|
|
44
117
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
118
|
+
|
|
119
|
+
const pointer = useTrackedPointer()
|
|
120
|
+
|
|
121
|
+
const handleEnter = (evt: PointerEvent) => {
|
|
122
|
+
pointer.update(evt)
|
|
123
|
+
if (disabled) return
|
|
124
|
+
if (active) return
|
|
125
|
+
actions.goToOption(Focus.Specific, id, ActivationTrigger.Pointer)
|
|
51
126
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return disposables().requestAnimationFrame(() => {
|
|
59
|
-
;
|
|
60
|
-
ref?.scrollIntoView?.({ block: "nearest" });
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
onMount(() => {
|
|
64
|
-
if (usedInSelectedOption) return;
|
|
65
|
-
return actions.registerOption(id, {
|
|
66
|
-
get current() {
|
|
67
|
-
return bag;
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
const handleClick = (event) => {
|
|
72
|
-
if (disabled) return event.preventDefault();
|
|
73
|
-
actions.onChange(value);
|
|
74
|
-
if (data.mode === ValueMode.Single) {
|
|
75
|
-
actions.closeListbox();
|
|
76
|
-
data.buttonRef.current?.focus({ preventScroll: true });
|
|
127
|
+
|
|
128
|
+
const handleMove = (evt: PointerEvent) => {
|
|
129
|
+
if (!pointer.wasMoved(evt)) return
|
|
130
|
+
if (disabled) return
|
|
131
|
+
if (active) return
|
|
132
|
+
actions.goToOption(Focus.Specific, id, ActivationTrigger.Pointer)
|
|
77
133
|
}
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// both single and multi-select.
|
|
118
|
-
"aria-selected": selected,
|
|
119
|
-
disabled: void 0,
|
|
120
|
-
// Never forward the `disabled` prop
|
|
121
|
-
onclick: handleClick,
|
|
122
|
-
onfocus: handleFocus,
|
|
123
|
-
onpointerenter: handleEnter,
|
|
124
|
-
onmouseenter: handleEnter,
|
|
125
|
-
onpointermove: handleMove,
|
|
126
|
-
onmousemove: handleMove,
|
|
127
|
-
onpointerleave: handleLeave,
|
|
128
|
-
onmouseleave: handleLeave,
|
|
129
|
-
...stateFromSlot(slot)
|
|
130
|
-
} : {}
|
|
131
|
-
);
|
|
134
|
+
|
|
135
|
+
const handleLeave = (evt: PointerEvent) => {
|
|
136
|
+
if (!pointer.wasMoved(evt)) return
|
|
137
|
+
if (disabled) return
|
|
138
|
+
if (!active) return
|
|
139
|
+
actions.goToOption(Focus.Nothing)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const slot = $derived({
|
|
143
|
+
active,
|
|
144
|
+
focus: active,
|
|
145
|
+
selected,
|
|
146
|
+
disabled,
|
|
147
|
+
selectedOption: selected && usedInSelectedOption,
|
|
148
|
+
} satisfies OptionRenderPropArg)
|
|
149
|
+
const ourProps = $derived(
|
|
150
|
+
!usedInSelectedOption
|
|
151
|
+
? {
|
|
152
|
+
id,
|
|
153
|
+
role: "option",
|
|
154
|
+
tabIndex: disabled === true ? undefined : -1,
|
|
155
|
+
"aria-disabled": disabled === true ? true : undefined,
|
|
156
|
+
// According to the WAI-ARIA best practices, we should use aria-checked for
|
|
157
|
+
// multi-select,but Voice-Over disagrees. So we use aria-checked instead for
|
|
158
|
+
// both single and multi-select.
|
|
159
|
+
"aria-selected": selected,
|
|
160
|
+
disabled: undefined, // Never forward the `disabled` prop
|
|
161
|
+
onclick: handleClick,
|
|
162
|
+
onfocus: handleFocus,
|
|
163
|
+
onpointerenter: handleEnter,
|
|
164
|
+
onmouseenter: handleEnter,
|
|
165
|
+
onpointermove: handleMove,
|
|
166
|
+
onmousemove: handleMove,
|
|
167
|
+
onpointerleave: handleLeave,
|
|
168
|
+
onmouseleave: handleLeave,
|
|
169
|
+
...stateFromSlot(slot),
|
|
170
|
+
}
|
|
171
|
+
: {}
|
|
172
|
+
)
|
|
132
173
|
</script>
|
|
133
174
|
|
|
134
175
|
{#if selected || !usedInSelectedOption}
|