Skip to content

Commit

Permalink
move handleLeave to ComboboxOptions
Browse files Browse the repository at this point in the history
This used to be on the `ComboboxOption` itself, this also means that if
you are going from option A to option B, then you would see this:

- Leave option A (goToOption(Nothing))
- Enter option B (goToOption(option b))

Both cases would use a `goToOption` call which re-renders everything.
Instead we can listen for the `(mouse|pointer)Leave` on the
`ComboboxOptions` itself to run `goToOption(Nothing)`.

Now, when you go from option A to option B, then you would only see
this:
- Enter option B (goToOption(option b))

Which internally sets the state to the active option B.
  • Loading branch information
RobinMalfait committed Jul 4, 2023
1 parent e518b20 commit b557346
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5470,21 +5470,21 @@ describe('Mouse interactions', () => {
await mouseMove(options[1])
assertActiveComboboxOption(options[1])

await mouseLeave(options[1])
await mouseLeave(document.querySelector('[id^="headlessui-combobox-options-"]'))
assertNoActiveComboboxOption()

// We should be able to go to the first option
await mouseMove(options[0])
assertActiveComboboxOption(options[0])

await mouseLeave(options[0])
await mouseLeave(document.querySelector('[id^="headlessui-combobox-options-"]'))
assertNoActiveComboboxOption()

// We should be able to go to the last option
await mouseMove(options[2])
assertActiveComboboxOption(options[2])

await mouseLeave(options[2])
await mouseLeave(document.querySelector('[id^="headlessui-combobox-options-"]'))
assertNoActiveComboboxOption()
})
)
Expand Down
25 changes: 8 additions & 17 deletions packages/@headlessui-vue/src/components/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type StateDefinition = {

compare: (a: unknown, z: unknown) => boolean

optionsPropsRef: Ref<{ static: boolean; hold: boolean }>
optionsPropsRef: Ref<{ static: boolean }>

labelRef: Ref<HTMLLabelElement | null>
inputRef: Ref<HTMLInputElement | null>
Expand Down Expand Up @@ -149,7 +149,6 @@ export let Combobox = defineComponent({
) as StateDefinition['optionsRef']
let optionsPropsRef = ref<StateDefinition['optionsPropsRef']['value']>({
static: false,
hold: false,
}) as StateDefinition['optionsPropsRef']
let options = ref<StateDefinition['options']['value']>([])
let activeOptionIndex = ref<StateDefinition['activeOptionIndex']['value']>(null)
Expand Down Expand Up @@ -1022,10 +1021,6 @@ export let ComboboxOptions = defineComponent({
api.optionsPropsRef.value.static = props.static
})

watchEffect(() => {
api.optionsPropsRef.value.hold = props.hold
})

let usesOpenClosedState = useOpenClosed()
let visible = computed(() => {
if (usesOpenClosedState !== null) {
Expand All @@ -1048,16 +1043,22 @@ export let ComboboxOptions = defineComponent({
},
})

function handleLeave() {
api.goToOption(Focus.Nothing)
}

return () => {
let { hold, ...theirProps } = props
let slot = { open: api.comboboxState.value === ComboboxStates.Open }
let ourProps = {
'aria-labelledby': dom(api.labelRef)?.id ?? dom(api.buttonRef)?.id,
id,
ref: api.optionsRef,
role: 'listbox',
'aria-multiselectable': api.mode.value === ValueMode.Multi ? true : undefined,
onPointerleave: hold ? undefined : handleLeave,
onMouseleave: hold ? undefined : handleLeave,
}
let theirProps = omit(props, ['hold'])

return render({
ourProps,
Expand Down Expand Up @@ -1165,14 +1166,6 @@ export let ComboboxOption = defineComponent({
api.goToOption(Focus.Specific, id, ActivationTrigger.Pointer)
}

function handleLeave(evt: PointerEvent) {
if (!pointer.wasMoved(evt)) return
if (props.disabled) return
if (!active.value) return
if (api.optionsPropsRef.value.hold) return
api.goToOption(Focus.Nothing)
}

return () => {
let { disabled } = props
let slot = { active: active.value, selected: selected.value, disabled }
Expand All @@ -1193,8 +1186,6 @@ export let ComboboxOption = defineComponent({
onMouseenter: handleEnter,
onPointermove: handleMove,
onMousemove: handleMove,
onPointerleave: handleLeave,
onMouseleave: handleLeave,
}

let theirProps = props
Expand Down

0 comments on commit b557346

Please sign in to comment.