From e10563fc249b21dd633cfa4f4f4eee8a6dbda00b Mon Sep 17 00:00:00 2001 From: bender101 Date: Wed, 1 Nov 2023 19:08:51 +0300 Subject: [PATCH] add useModal, upgrade drawer --- src/shared/lib/hooks/useModal/useModal.tsx | 57 +++++++++++++++++ src/shared/ui/Drawer/Drawer.module.scss | 6 ++ src/shared/ui/Drawer/Drawer.tsx | 74 ++++++++++++---------- src/shared/ui/Modal/Modal.tsx | 71 ++++----------------- 4 files changed, 117 insertions(+), 91 deletions(-) create mode 100644 src/shared/lib/hooks/useModal/useModal.tsx diff --git a/src/shared/lib/hooks/useModal/useModal.tsx b/src/shared/lib/hooks/useModal/useModal.tsx new file mode 100644 index 0000000..63db5a7 --- /dev/null +++ b/src/shared/lib/hooks/useModal/useModal.tsx @@ -0,0 +1,57 @@ +import { + MutableRefObject, useCallback, useEffect, useRef, useState, +} from 'react'; + +interface UseModalProps { + onClose?: () => void; + isOpen?: boolean; + animationDelay: number; +} + +export function useModal({ + animationDelay, isOpen, onClose, +}: 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 e440393..b622975 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 16b3114..2cc79bb 100644 --- a/src/shared/ui/Drawer/Drawer.tsx +++ b/src/shared/ui/Drawer/Drawer.tsx @@ -1,40 +1,50 @@ -import { classNames, Mods } from 'shared/lib/classNames/classNames'; -import React, { memo, ReactNode } from 'react'; -import { useTheme } from 'app/providers/ThemeProvider'; -import { Overlay } from '../Overlay/Overlay'; -import cls from './Drawer.module.scss'; -import { Portal } from '../Portal/Portal'; +import { classNames, Mods } from "shared/lib/classNames/classNames"; +import React, { memo, ReactNode } from "react"; +import { useTheme } from "app/providers/ThemeProvider"; +import { useModal } from "shared/lib/hooks/useModal/useModal"; +import { Overlay } from "../Overlay/Overlay"; +import cls from "./Drawer.module.scss"; +import { Portal } from "../Portal/Portal"; interface DrawerProps { - className?: string; - children: ReactNode; - isOpen?: boolean; - onClose?: () => void; + className?: string; + children: ReactNode; + isOpen?: boolean; + onClose?: () => void; + lazy?: boolean; } export const Drawer = memo((props: DrawerProps) => { - const { - className, - children, - onClose, - isOpen, - } = props; - const { theme } = useTheme(); + const { className, children, onClose, isOpen, lazy } = props; + const { theme } = useTheme(); - const mods: Mods = { - [cls.opened]: isOpen, - }; + const { close, isClosing, isMounted } = useModal({ + animationDelay: 300, + onClose, + isOpen, + }); - return ( - -
- -
- {children} -
-
-
- ); + 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 73a2110..6f98a34 100644 --- a/src/shared/ui/Modal/Modal.tsx +++ b/src/shared/ui/Modal/Modal.tsx @@ -1,17 +1,10 @@ import { classNames, Mods } from "shared/lib/classNames/classNames"; -import { - ReactNode, - useCallback, - useEffect, - useRef, - useState, - MouseEvent, - MutableRefObject, -} from "react"; -import { Portal } from "../Portal/Portal"; +import React, { ReactNode } from "react"; import { useTheme } from "app/providers/ThemeProvider"; -import cls from "./Modal.module.scss"; +import { useModal } from "shared/lib/hooks/useModal/useModal"; import { Overlay } from "../Overlay/Overlay"; +import { Portal } from "../Portal/Portal"; +import cls from "./Modal.module.scss"; interface ModalProps { className?: string; @@ -26,51 +19,13 @@ const ANIMATION_DELAY = 300; export const Modal = (props: ModalProps) => { const { className, children, isOpen, onClose, lazy } = props; - const [isClosing, setIsClosing] = useState(false); - const [isMounted, setIsMounted] = useState(false); - const timerRef = useRef() as MutableRefObject>; - const { theme } = useTheme(); - - useEffect(() => { - if (isOpen) { - setIsMounted(true); - } - }, [isOpen]); - - const closeHandler = useCallback(() => { - if (onClose) { - setIsClosing(true); - timerRef.current = setTimeout(() => { - onClose(); - setIsClosing(false); - }, ANIMATION_DELAY); - } - }, [onClose]); + const { close, isClosing, isMounted } = useModal({ + animationDelay: ANIMATION_DELAY, + onClose, + isOpen, + }); - // Новые ссылки!!! - const onKeyDown = useCallback( - (e: KeyboardEvent) => { - if (e.key === "Escape") { - closeHandler(); - } - }, - [closeHandler] - ); - - const onContentClick = (e: MouseEvent) => { - e.stopPropagation(); - }; - - useEffect(() => { - if (isOpen) { - window.addEventListener("keydown", onKeyDown); - } - - return () => { - clearTimeout(timerRef.current); - window.removeEventListener("keydown", onKeyDown); - }; - }, [isOpen, onKeyDown]); + const { theme } = useTheme(); const mods: Mods = { [cls.opened]: isOpen, @@ -86,10 +41,8 @@ export const Modal = (props: ModalProps) => {
- -
- {children} -
+ +
{children}
);