@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.
Files changed (64) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/checkbox/Checkbox.svelte +174 -120
  3. package/dist/close-button/CloseButton.svelte +12 -6
  4. package/dist/combobox/Combobox.svelte +50 -3
  5. package/dist/data-interactive/DataInteractive.svelte +57 -29
  6. package/dist/description/Description.svelte +32 -21
  7. package/dist/dialog/Dialog.svelte +69 -34
  8. package/dist/dialog/DialogBackdrop.svelte +29 -12
  9. package/dist/dialog/DialogPanel.svelte +49 -26
  10. package/dist/dialog/DialogTitle.svelte +38 -23
  11. package/dist/dialog/InternalDialog.svelte +263 -202
  12. package/dist/field/Field.svelte +49 -26
  13. package/dist/fieldset/Fieldset.svelte +50 -29
  14. package/dist/focus-trap/FocusTrap.svelte +436 -290
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/input/Input.svelte +85 -53
  18. package/dist/internal/FocusSentinel.svelte +16 -8
  19. package/dist/internal/ForcePortalRoot.svelte +7 -3
  20. package/dist/internal/FormFields.svelte +31 -20
  21. package/dist/internal/FormResolver.svelte +20 -15
  22. package/dist/internal/Hidden.svelte +44 -27
  23. package/dist/internal/Hidden.svelte.d.ts +2 -5
  24. package/dist/internal/HiddenFeatures.d.ts +5 -0
  25. package/dist/internal/HiddenFeatures.js +9 -0
  26. package/dist/internal/HoistFormFields.svelte +7 -4
  27. package/dist/internal/MainTreeProvider.svelte +89 -36
  28. package/dist/internal/Portal.svelte +18 -14
  29. package/dist/label/Label.svelte +91 -57
  30. package/dist/legend/Legend.svelte +18 -3
  31. package/dist/listbox/Listbox.svelte +600 -409
  32. package/dist/listbox/ListboxButton.svelte +176 -127
  33. package/dist/listbox/ListboxOption.svelte +166 -125
  34. package/dist/listbox/ListboxOptions.svelte +340 -244
  35. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  36. package/dist/menu/Menu.svelte +307 -218
  37. package/dist/menu/MenuButton.svelte +157 -115
  38. package/dist/menu/MenuHeading.svelte +34 -14
  39. package/dist/menu/MenuItem.svelte +145 -107
  40. package/dist/menu/MenuItems.svelte +298 -224
  41. package/dist/menu/MenuSection.svelte +26 -9
  42. package/dist/menu/MenuSeparator.svelte +20 -4
  43. package/dist/portal/InternalPortal.svelte +141 -85
  44. package/dist/portal/Portal.svelte +5 -2
  45. package/dist/portal/PortalGroup.svelte +30 -9
  46. package/dist/switch/Switch.svelte +179 -122
  47. package/dist/switch/Switch.svelte.d.ts +4 -4
  48. package/dist/switch/SwitchGroup.svelte +44 -31
  49. package/dist/tabs/Tab.svelte +195 -143
  50. package/dist/tabs/TabGroup.svelte +292 -205
  51. package/dist/tabs/TabList.svelte +31 -11
  52. package/dist/tabs/TabPanel.svelte +68 -43
  53. package/dist/tabs/TabPanels.svelte +18 -7
  54. package/dist/textarea/Textarea.svelte +97 -0
  55. package/dist/textarea/Textarea.svelte.d.ts +47 -0
  56. package/dist/textarea/index.d.ts +1 -0
  57. package/dist/textarea/index.js +1 -0
  58. package/dist/transition/InternalTransitionChild.svelte +259 -170
  59. package/dist/transition/Transition.svelte +96 -66
  60. package/dist/transition/TransitionChild.svelte +31 -11
  61. package/dist/utils/ElementOrComponent.svelte +44 -23
  62. package/dist/utils/Generic.svelte +29 -17
  63. package/dist/utils/StableCollection.svelte +54 -36
  64. package/package.json +10 -10
@@ -1,139 +1,188 @@
1
- <script lang="ts" module>import { useId } from "../hooks/use-id.js";
2
- import { getIdContext } from "../utils/id.js";
3
- import { ListboxStates, useActions, useData } from "./Listbox.svelte";
4
- import { attemptSubmit } from "../utils/form.js";
5
- import { Focus } from "../utils/calculate-active-index.js";
6
- import { useFocusRing } from "../hooks/use-focus-ring.svelte.js";
7
- import { useActivePress } from "../hooks/use-active-press.svelte.js";
8
- import { useResolveButtonType } from "../hooks/use-resolve-button-type.svelte.js";
9
- import { useFloating } from "../internal/floating.svelte.js";
10
- import { stateFromSlot } from "../utils/state.js";
11
- import { useLabelledBy } from "../label/context.svelte.js";
12
- import { useDescribedBy } from "../description/context.svelte.js";
13
- import { useHover } from "../hooks/use-hover.svelte.js";
14
- import { mergeProps } from "../utils/render.js";
15
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
16
- const DEFAULT_BUTTON_TAG = "button";
17
- </script>
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
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_BUTTON_TAG">const data = useData("ListboxButton");
20
- const actions = useActions("ListboxButton");
21
- const internalId = useId();
22
- const providedId = getIdContext();
23
- let {
24
- as = DEFAULT_BUTTON_TAG,
25
- ref = $bindable(),
26
- id = providedId || `headlessui-listbox-button-${internalId}`,
27
- disabled: ownDisabled = false,
28
- autofocus = false,
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
- const handleKeyPress = (event) => event.preventDefault();
72
- const labelledBy = useLabelledBy();
73
- const describedBy = useDescribedBy();
74
- const { isHovered: hover, hoverProps } = $derived(
75
- useHover({
76
- get disabled() {
77
- return disabled;
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 { pressed: active, pressProps } = $derived(
82
- useActivePress({
83
- get disabled() {
84
- return disabled;
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 { isFocusVisible: focus, focusProps } = $derived(
89
- useFocusRing({
90
- get autofocus() {
91
- return autofocus;
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 ourProps = $derived(
114
- mergeProps(
115
- {
116
- ...getFloatingReferenceProps(),
117
- id,
118
- type: buttonType.type,
119
- "aria-haspopup": "listbox",
120
- "aria-controls": data.optionsRef.current?.id,
121
- "aria-expanded": data.listboxState === ListboxStates.Open,
122
- "aria-labelledby": labelledBy.value,
123
- "aria-describedby": describedBy.value,
124
- disabled: disabled || void 0,
125
- autofocus,
126
- onkeydown: handleKeyDown,
127
- onkeyup: handleKeyUp,
128
- onkeypress: handleKeyPress,
129
- onclick: handleClick
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
- focusProps,
132
- hoverProps,
133
- pressProps,
134
- stateFromSlot(slot)
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>const DEFAULT_OPTION_TAG = "div";
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">import { useId } from "../hooks/use-id.js";
5
- import {
6
- ActivationTrigger,
7
- ListboxStates,
8
- useActions,
9
- useData,
10
- ValueMode
11
- } from "./Listbox.svelte";
12
- import { disposables } from "../utils/disposables.js";
13
- import { Focus } from "../utils/calculate-active-index.js";
14
- import { getContext, onMount } from "svelte";
15
- import { useTextValue } from "../hooks/use-text-value.svelte.js";
16
- import { useTrackedPointer } from "../hooks/use-tracked-pointer.js";
17
- import { stateFromSlot } from "../utils/state.js";
18
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
19
- const internalId = useId();
20
- let {
21
- as = DEFAULT_OPTION_TAG,
22
- ref = $bindable(),
23
- id = `headlessui-listbox-option-${internalId}`,
24
- disabled = false,
25
- value,
26
- ...theirProps
27
- } = $props();
28
- const usedInSelectedOption = getContext("SelectedOptionContext") === true;
29
- const data = useData("ListboxOption");
30
- const actions = useActions("ListboxOption");
31
- const active = $derived(data.activeOptionIndex !== null ? data.options[data.activeOptionIndex].id === id : false);
32
- const selected = $derived(data.isSelected(value));
33
- const getTextValue = useTextValue({
34
- get element() {
35
- return ref;
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 bag = $derived({
39
- disabled,
40
- value,
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
- $effect(() => {
47
- if (!ref) {
48
- data.listRef.current.delete(id);
49
- } else {
50
- data.listRef.current.set(id, ref);
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
- $effect(() => {
54
- if (data.__demoMode) return;
55
- if (data.listboxState !== ListboxStates.Open) return;
56
- if (!active) return;
57
- if (data.activationTrigger === ActivationTrigger.Pointer) return;
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 handleFocus = () => {
80
- if (disabled) return actions.goToOption(Focus.Nothing);
81
- actions.goToOption(Focus.Specific, id);
82
- };
83
- const pointer = useTrackedPointer();
84
- const handleEnter = (evt) => {
85
- pointer.update(evt);
86
- if (disabled) return;
87
- if (active) return;
88
- actions.goToOption(Focus.Specific, id, ActivationTrigger.Pointer);
89
- };
90
- const handleMove = (evt) => {
91
- if (!pointer.wasMoved(evt)) return;
92
- if (disabled) return;
93
- if (active) return;
94
- actions.goToOption(Focus.Specific, id, ActivationTrigger.Pointer);
95
- };
96
- const handleLeave = (evt) => {
97
- if (!pointer.wasMoved(evt)) return;
98
- if (disabled) return;
99
- if (!active) return;
100
- actions.goToOption(Focus.Nothing);
101
- };
102
- const slot = $derived({
103
- active,
104
- focus: active,
105
- selected,
106
- disabled,
107
- selectedOption: selected && usedInSelectedOption
108
- });
109
- const ourProps = $derived(
110
- !usedInSelectedOption ? {
111
- id,
112
- role: "option",
113
- tabIndex: disabled === true ? void 0 : -1,
114
- "aria-disabled": disabled === true ? true : void 0,
115
- // According to the WAI-ARIA best practices, we should use aria-checked for
116
- // multi-select,but Voice-Over disagrees. So we use aria-checked instead for
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}