diff --git a/src/shared/lib/hook/useModal/useModal.tsx b/src/shared/lib/hook/useModal/useModal.tsx new file mode 100644 index 0000000..326437c --- /dev/null +++ b/src/shared/lib/hook/useModal/useModal.tsx @@ -0,0 +1,62 @@ +import { + MutableRefObject, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; + +interface useModalProps { + onClose?: () => void; + isOpen?: boolean; + animationDelay?: number; +} + +export function useModal({ + onClose, + animationDelay = 300, + isOpen, +}: useModalProps) { + const [isClosing, setIsClosing] = useState(false); + const [isMounted, setIsMounted] = useState(false); + const timerRef = useRef() as MutableRefObject>; + + useEffect(() => { + if (isOpen) { + setIsMounted(true); + } + }, [isOpen]); + + const close = useCallback(() => { + if (onClose) { + setIsClosing(true); + timerRef.current = setTimeout(() => { + onClose(); + setIsClosing(false); + }, animationDelay); + } + }, [animationDelay, onClose]); + + const onKeyDown = useCallback((e: KeyboardEvent) => { + if (e.key === 'Escape') { + close(); + } + }, [close]); + + useEffect(() => { + if (isOpen) { + window.addEventListener('keydown', onKeyDown); + } + + return (() => { + clearTimeout(timerRef.current); + window.removeEventListener('keydown', onKeyDown); + }); + }, [isOpen, onKeyDown]); + + return { + isClosing, + isMounted, + close, + }; +} diff --git a/src/shared/ui/Drawer/Drawer.module.scss b/src/shared/ui/Drawer/Drawer.module.scss index 7739544..5197604 100644 --- a/src/shared/ui/Drawer/Drawer.module.scss +++ b/src/shared/ui/Drawer/Drawer.module.scss @@ -49,3 +49,9 @@ transform: translateY(0%); } } + +.isClosing { + .content { + transform: translateY(100%); + } +} diff --git a/src/shared/ui/Drawer/Drawer.tsx b/src/shared/ui/Drawer/Drawer.tsx index 5bba622..69b19a7 100644 --- a/src/shared/ui/Drawer/Drawer.tsx +++ b/src/shared/ui/Drawer/Drawer.tsx @@ -1,6 +1,7 @@ import { classNames, Mods } from 'shared/lib/classNames/classNames'; import React, { memo } from 'react'; import { useTheme } from 'app/providers/ThemeProvider'; +import { useModal } from 'shared/lib/hook/useModal/useModal'; import { Portal } from '../Portal/Portal'; import { Overlay } from '../Overlay/Overlay'; import cls from './Drawer.module.scss'; @@ -10,6 +11,7 @@ interface DrawerProps { children?: React.ReactNode; isOpen?: boolean; onClose?: () => void; + lazy?: boolean; } export const Drawer = memo((props: DrawerProps) => { @@ -18,18 +20,34 @@ export const Drawer = memo((props: DrawerProps) => { children, isOpen, onClose, + lazy, } = props; const { theme } = useTheme(); + const { + isClosing, + close, + isMounted, + } = useModal({ + onClose, + animationDelay: 300, + isOpen, + }); + const mods: Mods = { [cls.opened]: isOpen, + [cls.isClosing]: isClosing, }; + if (lazy && !isMounted) { + return null; + } + return (
- +
{children}
diff --git a/src/shared/ui/Modal/Modal.tsx b/src/shared/ui/Modal/Modal.tsx index 895363f..dd6a93b 100644 --- a/src/shared/ui/Modal/Modal.tsx +++ b/src/shared/ui/Modal/Modal.tsx @@ -1,13 +1,7 @@ -import React, { - MutableRefObject, - ReactNode, - useCallback, - useEffect, - useRef, - useState, -} from 'react'; +import React, { ReactNode } from 'react'; import { classNames, Mods } from 'shared/lib/classNames/classNames'; import { useTheme } from 'app/providers/ThemeProvider'; +import { useModal } from 'shared/lib/hook/useModal/useModal'; import { Overlay } from '../Overlay/Overlay'; import { Portal } from '../Portal/Portal'; import cls from './Modal.module.scss'; @@ -17,7 +11,7 @@ interface ModalProps { children?: ReactNode; isOpen?: boolean; onClose?: () => void; - lazy?:boolean; + lazy?: boolean; } const ANIMATION_DELAY = 300; @@ -33,42 +27,15 @@ export const Modal = (props: ModalProps) => { const { theme } = useTheme(); - const [isClosing, setIsClosing] = useState(false); - const [isMounted, setIsMounted] = useState(false); - const timerRef = useRef() as MutableRefObject>; - - useEffect(() => { - if (isOpen) { - setIsMounted(true); - } - }, [isOpen]); - - const closeHandler = useCallback(() => { - if (onClose) { - setIsClosing(true); - timerRef.current = setTimeout(() => { - onClose(); - setIsClosing(false); - }, ANIMATION_DELAY); - } - }, [onClose]); - - const onKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === 'Escape') { - closeHandler(); - } - }, [closeHandler]); - - useEffect(() => { - if (isOpen) { - window.addEventListener('keydown', onKeyDown); - } - - return (() => { - clearTimeout(timerRef.current); - window.removeEventListener('keydown', onKeyDown); - }); - }, [isOpen, onKeyDown]); + const { + isClosing, + close, + isMounted, + } = useModal({ + onClose, + animationDelay: ANIMATION_DELAY, + isOpen, + }); const mods:Mods = { [cls.opened]: isOpen, @@ -82,7 +49,7 @@ export const Modal = (props: ModalProps) => { return (
- +
{children}