From 044dde493a5c80fc299b4bb5a0b632e16f28affc Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Thu, 16 Oct 2025 16:08:30 +0000 Subject: [PATCH 1/2] fix(Modal): hide nested parent modal --- easy-ui-react/src/Modal/context.tsx | 50 +++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/easy-ui-react/src/Modal/context.tsx b/easy-ui-react/src/Modal/context.tsx index e9889b3b..a861ada4 100644 --- a/easy-ui-react/src/Modal/context.tsx +++ b/easy-ui-react/src/Modal/context.tsx @@ -1,11 +1,15 @@ -import React, { ReactNode } from "react"; import { FocusableElement } from "@react-types/shared"; -import { +import React, { DOMAttributes, + ReactNode, RefObject, createContext, + useCallback, useContext, + useId, + useLayoutEffect, useMemo, + useRef, useState, } from "react"; import { OverlayTriggerState } from "react-stately"; @@ -24,7 +28,7 @@ type ModalTriggerContextType = { isDismissable: boolean; state: OverlayTriggerState; hasOpenNestedModal: boolean; - setHasOpenNestedModal: (hasOpenNestedModal: boolean) => void; + setNestedModalOpen: (id: string, open: boolean) => void; }; export type ModalTriggerProviderProps = Pick< @@ -65,10 +69,42 @@ export function ModalTriggerProvider({ isDismissable, children, }: ModalTriggerProviderProps) { - const [hasOpenNestedModal, setHasOpenNestedModal] = useState(false); - const context = useMemo(() => { - return { hasOpenNestedModal, setHasOpenNestedModal, state, isDismissable }; - }, [hasOpenNestedModal, state, isDismissable]); + const id = useId(); + const idRef = useRef(id); + const [, forceRerender] = useState([]); + const nestedModalSetRef = useRef>(new Set()); + const parentContext = useContext(ModalTriggerContext); + const openNestedModalCount = nestedModalSetRef.current.size; + const hasOpenNestedModal = openNestedModalCount > 0; + + const setNestedModalOpen = useCallback((id: string, isOpen: boolean) => { + if (isOpen && !nestedModalSetRef.current.has(id)) { + nestedModalSetRef.current.add(id); + forceRerender([]); + } + if (!isOpen && nestedModalSetRef.current.has(id)) { + nestedModalSetRef.current.delete(id); + forceRerender([]); + } + }, []); + + const context = useMemo( + () => ({ state, hasOpenNestedModal, setNestedModalOpen, isDismissable }), + [state, hasOpenNestedModal, setNestedModalOpen, isDismissable], + ); + + useLayoutEffect(() => { + if (!parentContext) { + return; + } + const id = idRef.current; + const isOpen = state.isOpen || hasOpenNestedModal; + parentContext.setNestedModalOpen(id, isOpen); + return () => { + parentContext.setNestedModalOpen(id, false); + }; + }, [parentContext, state.isOpen, hasOpenNestedModal]); + return ( {children} From b2df06041ec1a169ee2e3d02f4edbbe2187e44ac Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Thu, 16 Oct 2025 16:08:57 +0000 Subject: [PATCH 2/2] changeset --- .changeset/tough-dragons-fry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tough-dragons-fry.md diff --git a/.changeset/tough-dragons-fry.md b/.changeset/tough-dragons-fry.md new file mode 100644 index 00000000..6de330a9 --- /dev/null +++ b/.changeset/tough-dragons-fry.md @@ -0,0 +1,5 @@ +--- +"@easypost/easy-ui": patch +--- + +fix(Modal): hide nested parent modal