diff --git a/packages/circuit-ui/components/Modal/Modal.tsx b/packages/circuit-ui/components/Modal/Modal.tsx index f7b13d1ca3..6165383b10 100644 --- a/packages/circuit-ui/components/Modal/Modal.tsx +++ b/packages/circuit-ui/components/Modal/Modal.tsx @@ -36,7 +36,6 @@ import { deprecate } from '../../util/logger.js'; import type { Locale } from '../../util/i18n.js'; import classes from './Modal.module.css'; -import { createUseModal } from './createUseModal.js'; import { getFirstFocusableElement, hasNativeDialogSupport, @@ -304,5 +303,3 @@ export const Modal = forwardRef((props, ref) => { }); Modal.displayName = 'Modal'; - -export const useModal = createUseModal(Modal); diff --git a/packages/circuit-ui/components/Modal/ModalContext.tsx b/packages/circuit-ui/components/Modal/ModalContext.tsx index 7069f6d937..369affb05b 100644 --- a/packages/circuit-ui/components/Modal/ModalContext.tsx +++ b/packages/circuit-ui/components/Modal/ModalContext.tsx @@ -15,15 +15,11 @@ 'use client'; -import { - createContext, - type ReactNode, - useCallback, - useMemo, - useState, -} from 'react'; +import { createContext, type ReactNode, useCallback, useMemo } from 'react'; -import type { ModalProps } from './Modal.js'; +import { useStack } from '../../hooks/useStack/index.js'; + +import { ANIMATION_DURATION, type ModalProps } from './Modal.js'; import type { ModalDialogComponent } from './createUseModal.js'; export type SetModalArgs = Omit; @@ -60,18 +56,30 @@ export function ModalProvider({ initialState, ...defaultModalProps }: ModalProviderProps) { - const [modals, setModals] = useState(initialState ?? []); + const [modals, dispatch] = useStack>(initialState); - const setModal = useCallback((modal: ModalState) => { - setModals((prevValue) => [...prevValue, modal]); - }, []); + const setModal = useCallback( + (modal: ModalState) => { + dispatch({ type: 'push', item: modal }); + }, + [dispatch], + ); - const removeModal = useCallback((modal: ModalState) => { - if (modal.onClose) { - modal.onClose(); - } - setModals((prevValue) => prevValue.filter((m) => m.id !== modal.id)); - }, []); + const removeModal = useCallback( + (modal: ModalState) => { + if (modal.onClose) { + modal.onClose(); + } + dispatch({ + type: 'remove', + id: modal.id, + transition: { + duration: ANIMATION_DURATION, + }, + }); + }, + [dispatch], + ); const context = useMemo( () => ({ setModal, removeModal }), diff --git a/packages/circuit-ui/components/Modal/index.ts b/packages/circuit-ui/components/Modal/index.ts index a8393ac95d..3e721d7c1c 100644 --- a/packages/circuit-ui/components/Modal/index.ts +++ b/packages/circuit-ui/components/Modal/index.ts @@ -13,6 +13,9 @@ * limitations under the License. */ +import { createUseModal } from './createUseModal.js'; +import { Modal } from './Modal.js'; + export { Modal } from './Modal.js'; -export { useModal } from './Modal.js'; export type { ModalProps } from './Modal.js'; +export const useModal = createUseModal(Modal); diff --git a/packages/circuit-ui/index.ts b/packages/circuit-ui/index.ts index 7ffd415b4f..f5500bbf39 100644 --- a/packages/circuit-ui/index.ts +++ b/packages/circuit-ui/index.ts @@ -157,9 +157,9 @@ export type { } from './components/Popover/index.js'; export { ModalProvider } from './components/Modal/ModalContext.js'; export type { ModalProviderProps } from './components/Modal/ModalContext.js'; -export { useModal } from './components/Modal/Modal.js'; -export type { ModalProps } from './components/Modal/Modal.js'; -export { Modal } from './components/Modal/Modal.js'; +export { useModal } from './components/Modal/index.js'; +export type { ModalProps } from './components/Modal/index.js'; +export { Modal } from './components/Modal/index.js'; export { useNotificationModal } from './components/NotificationModal/index.js'; export type { NotificationModalProps } from './components/NotificationModal/index.js'; export { NotificationModal } from './components/NotificationModal/index.js';