Skip to content

Commit f8d18f4

Browse files
committed
ensure the main tree root is marked as inert
As well as the parent dialogs in case of nested dialogs.
1 parent f9a4661 commit f8d18f4

File tree

2 files changed

+52
-6
lines changed
  • packages
    • @headlessui-react/src/components/dialog
    • @headlessui-vue/src/components/dialog

2 files changed

+52
-6
lines changed

packages/@headlessui-react/src/components/dialog/dialog.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import React, {
33
createContext,
44
createRef,
5+
useCallback,
56
useContext,
67
useEffect,
78
useMemo,
@@ -25,7 +26,6 @@ import { Keys } from '../keyboard'
2526
import { isDisabledReactIssue7711 } from '../../utils/bugs'
2627
import { useId } from '../../hooks/use-id'
2728
import { FocusTrap } from '../../components/focus-trap/focus-trap'
28-
import { useInertOthers } from '../../hooks/use-inert-others'
2929
import { Portal } from '../../components/portal/portal'
3030
import { ForcePortalRoot } from '../../internal/portal-force-root'
3131
import { Description, useDescriptions } from '../description/description'
@@ -38,6 +38,7 @@ import { useEventListener } from '../../hooks/use-event-listener'
3838
import { Hidden, Features as HiddenFeatures } from '../../internal/hidden'
3939
import { useEvent } from '../../hooks/use-event'
4040
import { useDocumentOverflowLockedEffect } from '../../hooks/document-overflow/use-document-overflow'
41+
import { useInert } from '../../hooks/use-inert'
4142

4243
enum DialogStates {
4344
Open,
@@ -216,11 +217,33 @@ let DialogRoot = forwardRefWithAs(function Dialog<
216217

217218
// Ensure other elements can't be interacted with
218219
let inertOthersEnabled = (() => {
219-
if (hasNestedDialogs) return false
220+
// Nested dialogs should not modify the `inert` property, only the root one should.
221+
if (hasParentDialog) return false
220222
if (isClosing) return false
221223
return enabled
222224
})()
223-
useInertOthers(internalDialogRef, inertOthersEnabled)
225+
let resolveRootOfMainTreeNode = useCallback(() => {
226+
return (Array.from(ownerDocument?.querySelectorAll('body > *') ?? []).find((root) => {
227+
// Skip the portal root, we don't want to make that one inert
228+
if (root.id === 'headlessui-portal-root') return false
229+
230+
// Find the root of the main tree node
231+
return root.contains(mainTreeNode.current) && root instanceof HTMLElement
232+
}) ?? null) as HTMLElement | null
233+
}, [mainTreeNode])
234+
useInert(resolveRootOfMainTreeNode, inertOthersEnabled)
235+
236+
// This would mark the parent dialogs as inert
237+
let inertParentDialogs = (() => {
238+
if (hasNestedDialogs) return true
239+
return enabled
240+
})()
241+
let resolveRootOfParentDialog = useCallback(() => {
242+
return (Array.from(ownerDocument?.querySelectorAll('[data-headlessui-portal]') ?? []).find(
243+
(root) => root.contains(mainTreeNode.current) && root instanceof HTMLElement
244+
) ?? null) as HTMLElement | null
245+
}, [mainTreeNode])
246+
useInert(resolveRootOfParentDialog, inertParentDialogs)
224247

225248
let resolveContainers = useEvent(() => {
226249
// Third party roots

packages/@headlessui-vue/src/components/dialog/dialog.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { render, Features } from '../../utils/render'
2121
import { Keys } from '../../keyboard'
2222
import { useId } from '../../hooks/use-id'
2323
import { FocusTrap } from '../../components/focus-trap/focus-trap'
24-
import { useInertOthers } from '../../hooks/use-inert-others'
24+
import { useInert } from '../../hooks/use-inert'
2525
import { Portal, PortalGroup } from '../portal/portal'
2626
import { StackMessage, useStackProvider } from '../../internal/stack-context'
2727
import { match } from '../../utils/match'
@@ -145,11 +145,34 @@ export let Dialog = defineComponent({
145145

146146
// Ensure other elements can't be interacted with
147147
let inertOthersEnabled = computed(() => {
148-
if (hasNestedDialogs.value) return false
148+
// Nested dialogs should not modify the `inert` property, only the root one should.
149+
if (hasParentDialog) return false
149150
if (isClosing.value) return false
150151
return enabled.value
151152
})
152-
useInertOthers(internalDialogRef, inertOthersEnabled)
153+
let resolveRootOfMainTreeNode = computed(() => {
154+
return (Array.from(ownerDocument.value?.querySelectorAll('body > *') ?? []).find((root) => {
155+
// Skip the portal root, we don't want to make that one inert
156+
if (root.id === 'headlessui-portal-root') return false
157+
158+
// Find the root of the main tree node
159+
return root.contains(dom(mainTreeNode)) && root instanceof HTMLElement
160+
}) ?? null) as HTMLElement | null
161+
})
162+
useInert(resolveRootOfMainTreeNode, inertOthersEnabled)
163+
164+
// This would mark the parent dialogs as inert
165+
let inertParentDialogs = computed(() => {
166+
if (hasNestedDialogs.value) return true
167+
return enabled.value
168+
})
169+
let resolveRootOfParentDialog = computed(() => {
170+
return (Array.from(
171+
ownerDocument.value?.querySelectorAll('[data-headlessui-portal]') ?? []
172+
).find((root) => root.contains(dom(mainTreeNode)) && root instanceof HTMLElement) ??
173+
null) as HTMLElement | null
174+
})
175+
useInert(resolveRootOfParentDialog, inertParentDialogs)
153176

154177
useStackProvider({
155178
type: 'Dialog',

0 commit comments

Comments
 (0)