diff --git a/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx b/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx index d5f5b91783..00b775ad29 100644 --- a/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx +++ b/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx @@ -10,8 +10,8 @@ import React, { import { useDisposables } from '../../hooks/use-disposables' import { useEvent } from '../../hooks/use-event' import { useEventListener } from '../../hooks/use-event-listener' -import { useHierarchy } from '../../hooks/use-hierarchy' import { useIsMounted } from '../../hooks/use-is-mounted' +import { useIsTopLayer } from '../../hooks/use-is-top-layer' import { useOnUnmount } from '../../hooks/use-on-unmount' import { useOwnerDocument } from '../../hooks/use-owner' import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete' @@ -142,7 +142,7 @@ function FocusTrapFn( }) }) - let tabLockEnabled = useHierarchy( + let tabLockEnabled = useIsTopLayer( Boolean(features & FocusTrapFeatures.TabLock), 'focus-trap#tab-lock' ) @@ -313,7 +313,7 @@ function useInitialFocus( } ) { let previousActiveElement = useRef(null) - let enabled = useHierarchy( + let enabled = useIsTopLayer( Boolean(features & FocusTrapFeatures.InitialFocus), 'focus-trap#initial-focus' ) diff --git a/packages/@headlessui-react/src/hooks/use-escape.ts b/packages/@headlessui-react/src/hooks/use-escape.ts index 0ed45ff8d5..520700f5ad 100644 --- a/packages/@headlessui-react/src/hooks/use-escape.ts +++ b/packages/@headlessui-react/src/hooks/use-escape.ts @@ -1,13 +1,13 @@ import { Keys } from '../components/keyboard' import { useEventListener } from './use-event-listener' -import { useHierarchy } from './use-hierarchy' +import { useIsTopLayer } from './use-is-top-layer' export function useEscape( enabled: boolean, view = typeof document !== 'undefined' ? document.defaultView : null, cb: (event: KeyboardEvent) => void ) { - let isTopLayer = useHierarchy(enabled, 'escape') + let isTopLayer = useIsTopLayer(enabled, 'escape') useEventListener(view, 'keydown', (event) => { if (!isTopLayer) return diff --git a/packages/@headlessui-react/src/hooks/use-inert-others.tsx b/packages/@headlessui-react/src/hooks/use-inert-others.tsx index 0f5cafa740..21e8d6a6da 100644 --- a/packages/@headlessui-react/src/hooks/use-inert-others.tsx +++ b/packages/@headlessui-react/src/hooks/use-inert-others.tsx @@ -1,6 +1,6 @@ import { disposables } from '../utils/disposables' import { getOwnerDocument } from '../utils/owner' -import { useHierarchy } from './use-hierarchy' +import { useIsTopLayer } from './use-is-top-layer' import { useIsoMorphicEffect } from './use-iso-morphic-effect' let originals = new Map() @@ -80,7 +80,7 @@ export function useInertOthers( disallowed, }: { allowed?: () => (HTMLElement | null)[]; disallowed?: () => (HTMLElement | null)[] } = {} ) { - let isTopLayer = useHierarchy(enabled, 'inert-others') + let isTopLayer = useIsTopLayer(enabled, 'inert-others') useIsoMorphicEffect(() => { if (!isTopLayer) return diff --git a/packages/@headlessui-react/src/hooks/use-hierarchy.ts b/packages/@headlessui-react/src/hooks/use-is-top-layer.ts similarity index 87% rename from packages/@headlessui-react/src/hooks/use-hierarchy.ts rename to packages/@headlessui-react/src/hooks/use-is-top-layer.ts index f507555638..8583753e9f 100644 --- a/packages/@headlessui-react/src/hooks/use-hierarchy.ts +++ b/packages/@headlessui-react/src/hooks/use-is-top-layer.ts @@ -24,9 +24,9 @@ let hierarchyStores = new DefaultMap(() => ) /** - * A hook that returns whether the current node in the hierarchy is at the very - * top for a given scope. The hierarchy is based on the order of the hooks being - * called. + * A hook that returns whether the current node is on the top of the hierarchy, + * aka "top layer". Note: this does not use the native DOM "top-layer" but + * conceptually it's the same thing. * * The hierarchy is also shared across multiple components that use the same * scope. @@ -36,14 +36,17 @@ let hierarchyStores = new DefaultMap(() => * * A use case for this is to use this inside of a `useOutsideClick` hook where * only the last rendered component should handle the outside click event. + * + * ```ts * * * // Pressing escape on an open `Menu` should close the `Menu` and not the `Dialog`. * // … * * + * ``` */ -export function useHierarchy(enabled: boolean, scope: string) { +export function useIsTopLayer(enabled: boolean, scope: string) { let hierarchyStore = hierarchyStores.get(scope) let id = useId() let hierarchy = useStore(hierarchyStore) diff --git a/packages/@headlessui-react/src/hooks/use-outside-click.ts b/packages/@headlessui-react/src/hooks/use-outside-click.ts index 00c5fb51c7..c4cb88a97f 100644 --- a/packages/@headlessui-react/src/hooks/use-outside-click.ts +++ b/packages/@headlessui-react/src/hooks/use-outside-click.ts @@ -2,7 +2,7 @@ import { useEffect, useRef, type MutableRefObject } from 'react' import { FocusableMode, isFocusableElement } from '../utils/focus-management' import { isMobile } from '../utils/platform' import { useDocumentEvent } from './use-document-event' -import { useHierarchy } from './use-hierarchy' +import { useIsTopLayer } from './use-is-top-layer' import { useWindowEvent } from './use-window-event' type Container = MutableRefObject | HTMLElement | null @@ -14,7 +14,7 @@ export function useOutsideClick( containers: ContainerInput | (() => ContainerInput), cb: (event: MouseEvent | PointerEvent | FocusEvent | TouchEvent, target: HTMLElement) => void ) { - let isTopLayer = useHierarchy(enabled, 'outside-click') + let isTopLayer = useIsTopLayer(enabled, 'outside-click') // TODO: remove this once the React bug has been fixed: https://github.com/facebook/react/issues/24657 let enabledRef = useRef(false) diff --git a/packages/@headlessui-react/src/hooks/use-scroll-lock.ts b/packages/@headlessui-react/src/hooks/use-scroll-lock.ts index 0bc1b02550..a07630c8c3 100644 --- a/packages/@headlessui-react/src/hooks/use-scroll-lock.ts +++ b/packages/@headlessui-react/src/hooks/use-scroll-lock.ts @@ -1,12 +1,12 @@ import { useDocumentOverflowLockedEffect } from './document-overflow/use-document-overflow' -import { useHierarchy } from './use-hierarchy' +import { useIsTopLayer } from './use-is-top-layer' export function useScrollLock( enabled: boolean, ownerDocument: Document | null, resolveAllowedContainers: () => HTMLElement[] = () => [document.body] ) { - let isTopLayer = useHierarchy(enabled, 'scroll-lock') + let isTopLayer = useIsTopLayer(enabled, 'scroll-lock') useDocumentOverflowLockedEffect(isTopLayer, ownerDocument, (meta) => ({ containers: [...(meta.containers ?? []), resolveAllowedContainers],