diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index cebbb74bb9..5d9d4abebb 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -659,7 +659,7 @@ export type ComboboxProps< } | null onClose?(): void - + outsideClickScope?: string __demoMode?: boolean } > @@ -685,6 +685,7 @@ function ComboboxFn actions.closeCombobox() + () => actions.closeCombobox(), + outsideClickScope ) let slot = useMemo(() => { diff --git a/packages/@headlessui-react/src/components/dialog/dialog.tsx b/packages/@headlessui-react/src/components/dialog/dialog.tsx index 590660ee56..2040adc254 100644 --- a/packages/@headlessui-react/src/components/dialog/dialog.tsx +++ b/packages/@headlessui-react/src/components/dialog/dialog.tsx @@ -126,6 +126,7 @@ let InternalDialog = forwardRefWithAs(function InternalDialog< autoFocus = true, __demoMode = false, unmount = false, + outsideClickScope, ...theirProps } = props @@ -213,10 +214,15 @@ let InternalDialog = forwardRefWithAs(function InternalDialog< }) // Close Dialog on outside click - useOutsideClick(enabled, resolveRootContainers, (event) => { - event.preventDefault() - close() - }) + useOutsideClick( + enabled, + resolveRootContainers, + (event) => { + event.preventDefault() + close() + }, + outsideClickScope + ) // Handle `Escape` to close useEscape(enabled, ownerDocument?.defaultView, (event) => { @@ -347,6 +353,7 @@ export type DialogProps = role?: 'dialog' | 'alertdialog' autoFocus?: boolean transition?: boolean + outsideClickScope?: string __demoMode?: boolean } > diff --git a/packages/@headlessui-react/src/components/listbox/listbox.tsx b/packages/@headlessui-react/src/components/listbox/listbox.tsx index 65d06cff81..0a166a53ad 100644 --- a/packages/@headlessui-react/src/components/listbox/listbox.tsx +++ b/packages/@headlessui-react/src/components/listbox/listbox.tsx @@ -492,7 +492,7 @@ export type ListboxProps< form?: string name?: string multiple?: boolean - + outsideClickScope?: string __demoMode?: boolean } > @@ -515,6 +515,7 @@ function ListboxFn< horizontal = false, multiple = false, __demoMode = false, + outsideClickScope, ...theirProps } = props @@ -592,7 +593,8 @@ function ListboxFn< event.preventDefault() data.buttonElement?.focus() } - } + }, + outsideClickScope ) let slot = useMemo(() => { diff --git a/packages/@headlessui-react/src/components/menu/menu.tsx b/packages/@headlessui-react/src/components/menu/menu.tsx index 478b3709ba..4423fa49cf 100644 --- a/packages/@headlessui-react/src/components/menu/menu.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.tsx @@ -382,6 +382,7 @@ export type MenuProps = Prop MenuPropsWeControl, { __demoMode?: boolean + outsideClickScope?: string } > @@ -389,7 +390,7 @@ function MenuFn( props: MenuProps, ref: Ref ) { - let { __demoMode = false, ...theirProps } = props + let { __demoMode = false, outsideClickScope, ...theirProps } = props let reducerBag = useReducer(stateReducer, { __demoMode, menuState: __demoMode ? MenuStates.Open : MenuStates.Closed, @@ -405,14 +406,19 @@ function MenuFn( // Handle outside click let outsideClickEnabled = menuState === MenuStates.Open - useOutsideClick(outsideClickEnabled, [buttonElement, itemsElement], (event, target) => { - dispatch({ type: ActionTypes.CloseMenu }) + useOutsideClick( + outsideClickEnabled, + [buttonElement, itemsElement], + (event, target) => { + dispatch({ type: ActionTypes.CloseMenu }) - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - buttonElement?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + buttonElement?.focus() + } + }, + outsideClickScope + ) let close = useEvent(() => { dispatch({ type: ActionTypes.CloseMenu }) diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index 68e11ca6bb..1f68cdca4d 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -238,6 +238,7 @@ export type PopoverProps PopoverPropsWeControl, { __demoMode?: boolean + outsideClickScope?: string } > @@ -245,7 +246,7 @@ function PopoverFn( props: PopoverProps, ref: Ref ) { - let { __demoMode = false, ...theirProps } = props + let { __demoMode = false, outsideClickScope, ...theirProps } = props let internalPopoverRef = useRef(null) let popoverRef = useSyncRefs( ref, @@ -375,14 +376,19 @@ function PopoverFn( // Handle outside click let outsideClickEnabled = popoverState === PopoverStates.Open - useOutsideClick(outsideClickEnabled, root.resolveContainers, (event, target) => { - dispatch({ type: ActionTypes.ClosePopover }) + useOutsideClick( + outsideClickEnabled, + root.resolveContainers, + (event, target) => { + dispatch({ type: ActionTypes.ClosePopover }) - if (!isFocusableElement(target, FocusableMode.Loose)) { - event.preventDefault() - button?.focus() - } - }) + if (!isFocusableElement(target, FocusableMode.Loose)) { + event.preventDefault() + button?.focus() + } + }, + outsideClickScope + ) let close = useEvent( ( diff --git a/packages/@headlessui-react/src/hooks/use-outside-click.ts b/packages/@headlessui-react/src/hooks/use-outside-click.ts index 43d47d0e1f..63bf63008e 100644 --- a/packages/@headlessui-react/src/hooks/use-outside-click.ts +++ b/packages/@headlessui-react/src/hooks/use-outside-click.ts @@ -21,9 +21,10 @@ const MOVE_THRESHOLD_PX = 30 export function useOutsideClick( enabled: boolean, containers: ContainerInput | (() => ContainerInput), - cb: (event: MouseEvent | PointerEvent | FocusEvent | TouchEvent, target: HTMLElement) => void + cb: (event: MouseEvent | PointerEvent | FocusEvent | TouchEvent, target: HTMLElement) => void, + topLayerScope = 'outside-click' ) { - let isTopLayer = useIsTopLayer(enabled, 'outside-click') + let isTopLayer = useIsTopLayer(enabled, topLayerScope) let cbRef = useLatestValue(cb) let handleOutsideClick = useCallback(