Skip to content

Commit

Permalink
feat: add useModal hook; refactor modal, drawer
Browse files Browse the repository at this point in the history
  • Loading branch information
TomatoVan committed Mar 3, 2024
1 parent 3ad3f87 commit cd48677
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 47 deletions.
62 changes: 62 additions & 0 deletions src/shared/lib/hook/useModal/useModal.tsx
Original file line number Diff line number Diff line change
@@ -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<ReturnType<typeof setTimeout>>;

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,
};
}
6 changes: 6 additions & 0 deletions src/shared/ui/Drawer/Drawer.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,9 @@
transform: translateY(0%);
}
}

.isClosing {
.content {
transform: translateY(100%);
}
}
20 changes: 19 additions & 1 deletion src/shared/ui/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -10,6 +11,7 @@ interface DrawerProps {
children?: React.ReactNode;
isOpen?: boolean;
onClose?: () => void;
lazy?: boolean;
}

export const Drawer = memo((props: DrawerProps) => {
Expand All @@ -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 (
<Portal>
<div className={classNames(cls.Drawer, mods, [className, theme])}>
<Overlay onClick={onClose} />
<Overlay onClick={close} />
<div className={cls.content}>
{children}
</div>
Expand Down
59 changes: 13 additions & 46 deletions src/shared/ui/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -17,7 +11,7 @@ interface ModalProps {
children?: ReactNode;
isOpen?: boolean;
onClose?: () => void;
lazy?:boolean;
lazy?: boolean;
}

const ANIMATION_DELAY = 300;
Expand All @@ -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<ReturnType<typeof setTimeout>>;

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,
Expand All @@ -82,7 +49,7 @@ export const Modal = (props: ModalProps) => {
return (
<Portal>
<div className={classNames(cls.Modal, mods, [className, theme])}>
<Overlay onClick={closeHandler} />
<Overlay onClick={close} />
<div className={cls.content}>
{children}
</div>
Expand Down

0 comments on commit cd48677

Please sign in to comment.