From 7acaf08fff4ca901318acca888c61a11a5fd4b77 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 3 Jun 2022 16:12:08 +0200 Subject: [PATCH] add explicit `enabled` value to the `useOutsideClick` hook --- .../src/components/combobox/combobox.tsx | 12 +++++------ .../src/components/dialog/dialog.tsx | 9 ++------- .../src/components/listbox/listbox.tsx | 20 ++++++++++--------- .../src/components/menu/menu.tsx | 20 ++++++++++--------- .../src/components/popover/popover.tsx | 20 ++++++++++--------- .../src/components/combobox/combobox.ts | 9 +++++---- .../src/components/dialog/dialog.ts | 6 ++---- .../src/components/listbox/listbox.ts | 20 ++++++++++--------- .../src/components/menu/menu.ts | 20 ++++++++++--------- .../src/components/popover/popover.ts | 20 ++++++++++--------- 10 files changed, 81 insertions(+), 75 deletions(-) diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index 1e68c36cb3..e00d55fdd9 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -34,7 +34,7 @@ import { forwardRefWithAs, render, compact, PropsForFeatures, Features } from '. import { isDisabledReactIssue7711 } from '../../utils/bugs' import { match } from '../../utils/match' import { objectToFormEntries } from '../../utils/form' -import { sortByDomNode } from '../../utils/focus-management' +import { FocusableMode, isFocusableElement, sortByDomNode } from '../../utils/focus-management' import { Hidden, Features as HiddenFeatures } from '../../internal/hidden' import { useOpenClosed, State, OpenClosedProvider } from '../../internal/open-closed' @@ -415,11 +415,11 @@ let ComboboxRoot = forwardRefWithAs(function Combobox< }, [data]) // Handle outside click - useOutsideClick([data.buttonRef, data.inputRef, data.optionsRef], () => { - if (data.comboboxState !== ComboboxState.Open) return - - dispatch({ type: ActionTypes.CloseCombobox }) - }) + useOutsideClick( + [data.buttonRef, data.inputRef, data.optionsRef], + () => dispatch({ type: ActionTypes.CloseCombobox }), + data.comboboxState === ComboboxState.Open + ) let slot = useMemo>( () => ({ diff --git a/packages/@headlessui-react/src/components/dialog/dialog.tsx b/packages/@headlessui-react/src/components/dialog/dialog.tsx index 859fda748f..4f3d0ed6ff 100644 --- a/packages/@headlessui-react/src/components/dialog/dialog.tsx +++ b/packages/@headlessui-react/src/components/dialog/dialog.tsx @@ -212,13 +212,8 @@ let DialogRoot = forwardRefWithAs(function Dialog< state.panelRef.current ?? internalDialogRef.current, ] as HTMLElement[] }, - () => { - if (dialogState !== DialogStates.Open) return - if (hasNestedDialogs) return - - close() - }, - enabled + close, + enabled && !hasNestedDialogs ) // Handle `Escape` to close diff --git a/packages/@headlessui-react/src/components/listbox/listbox.tsx b/packages/@headlessui-react/src/components/listbox/listbox.tsx index b6643312dd..1c1c6472ff 100644 --- a/packages/@headlessui-react/src/components/listbox/listbox.tsx +++ b/packages/@headlessui-react/src/components/listbox/listbox.tsx @@ -396,16 +396,18 @@ let ListboxRoot = forwardRefWithAs(function Listbox< ) // Handle outside click - useOutsideClick([buttonRef, optionsRef], (event, target) => { - if (listboxState !== ListboxStates.Open) return - - dispatch({ type: ActionTypes.CloseListbox }) + useOutsideClick( + [buttonRef, optionsRef], + (event, target) => { + dispatch({ type: ActionTypes.CloseListbox }) - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - buttonRef.current?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + buttonRef.current?.focus() + } + }, + listboxState === ListboxStates.Open + ) let slot = useMemo( () => ({ open: listboxState === ListboxStates.Open, disabled }), diff --git a/packages/@headlessui-react/src/components/menu/menu.tsx b/packages/@headlessui-react/src/components/menu/menu.tsx index a46609b2eb..2c7e3d1ac2 100644 --- a/packages/@headlessui-react/src/components/menu/menu.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.tsx @@ -239,16 +239,18 @@ let MenuRoot = forwardRefWithAs(function Menu { - if (menuState !== MenuStates.Open) return - - dispatch({ type: ActionTypes.CloseMenu }) + useOutsideClick( + [buttonRef, itemsRef], + (event, target) => { + dispatch({ type: ActionTypes.CloseMenu }) - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - buttonRef.current?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + buttonRef.current?.focus() + } + }, + menuState === MenuStates.Open + ) let slot = useMemo( () => ({ open: menuState === MenuStates.Open }), diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index a1fa990ee0..9d9314fb15 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -258,16 +258,18 @@ let PopoverRoot = forwardRefWithAs(function Popover< ) // Handle outside click - useOutsideClick([button, panel], (event, target) => { - if (popoverState !== PopoverStates.Open) return - - dispatch({ type: ActionTypes.ClosePopover }) + useOutsideClick( + [button, panel], + (event, target) => { + dispatch({ type: ActionTypes.ClosePopover }) - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - button?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + button?.focus() + } + }, + popoverState === PopoverStates.Open + ) let close = useEvent((focusableElement?: HTMLElement | MutableRefObject) => { dispatch({ type: ActionTypes.ClosePopover }) diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts index 76bb0c425b..4bdb9c1936 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts @@ -396,10 +396,11 @@ export let Combobox = defineComponent({ } // Handle outside click - useOutsideClick([inputRef, buttonRef, optionsRef], () => { - if (comboboxState.value !== ComboboxStates.Open) return - api.closeCombobox() - }) + useOutsideClick( + [inputRef, buttonRef, optionsRef], + () => api.closeCombobox(), + computed(() => comboboxState.value === ComboboxStates.Open) + ) watch([api.value, api.inputRef], () => api.syncInputValue(), { immediate: true, diff --git a/packages/@headlessui-vue/src/components/dialog/dialog.ts b/packages/@headlessui-vue/src/components/dialog/dialog.ts index 0cf90f18de..217ea2f873 100644 --- a/packages/@headlessui-vue/src/components/dialog/dialog.ts +++ b/packages/@headlessui-vue/src/components/dialog/dialog.ts @@ -191,12 +191,10 @@ export let Dialog = defineComponent({ }, (_event, target) => { - if (dialogState.value !== DialogStates.Open) return - if (hasNestedDialogs.value) return - api.close() nextTick(() => target?.focus()) - } + }, + computed(() => dialogState.value === DialogStates.Open && !hasNestedDialogs.value) ) // Handle `Escape` to close diff --git a/packages/@headlessui-vue/src/components/listbox/listbox.ts b/packages/@headlessui-vue/src/components/listbox/listbox.ts index b3a123ab3d..5bd9062a10 100644 --- a/packages/@headlessui-vue/src/components/listbox/listbox.ts +++ b/packages/@headlessui-vue/src/components/listbox/listbox.ts @@ -298,16 +298,18 @@ export let Listbox = defineComponent({ } // Handle outside click - useOutsideClick([buttonRef, optionsRef], (event, target) => { - if (listboxState.value !== ListboxStates.Open) return - - api.closeListbox() + useOutsideClick( + [buttonRef, optionsRef], + (event, target) => { + api.closeListbox() - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - dom(buttonRef)?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + dom(buttonRef)?.focus() + } + }, + computed(() => listboxState.value === ListboxStates.Open) + ) // @ts-expect-error Types of property 'dataRef' are incompatible. provide(ListboxContext, api) diff --git a/packages/@headlessui-vue/src/components/menu/menu.ts b/packages/@headlessui-vue/src/components/menu/menu.ts index 5e29c4c18d..4a4a543221 100644 --- a/packages/@headlessui-vue/src/components/menu/menu.ts +++ b/packages/@headlessui-vue/src/components/menu/menu.ts @@ -201,16 +201,18 @@ export let Menu = defineComponent({ } // Handle outside click - useOutsideClick([buttonRef, itemsRef], (event, target) => { - if (menuState.value !== MenuStates.Open) return - - api.closeMenu() + useOutsideClick( + [buttonRef, itemsRef], + (event, target) => { + api.closeMenu() - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - dom(buttonRef)?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + dom(buttonRef)?.focus() + } + }, + computed(() => menuState.value === MenuStates.Open) + ) // @ts-expect-error Types of property 'dataRef' are incompatible. provide(MenuContext, api) diff --git a/packages/@headlessui-vue/src/components/popover/popover.ts b/packages/@headlessui-vue/src/components/popover/popover.ts index fc9a26f212..d79f645876 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.ts @@ -211,16 +211,18 @@ export let Popover = defineComponent({ ) // Handle outside click - useOutsideClick([button, panel], (event, target) => { - if (popoverState.value !== PopoverStates.Open) return - - api.closePopover() + useOutsideClick( + [button, panel], + (event, target) => { + api.closePopover() - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - dom(button)?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + dom(button)?.focus() + } + }, + computed(() => popoverState.value === PopoverStates.Open) + ) return () => { let slot = { open: popoverState.value === PopoverStates.Open, close: api.close }