Skip to content

Commit

Permalink
add useModal, upgrade drawer
Browse files Browse the repository at this point in the history
  • Loading branch information
Bender101 committed Nov 1, 2023
1 parent bff9c6c commit e10563f
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 91 deletions.
57 changes: 57 additions & 0 deletions src/shared/lib/hooks/useModal/useModal.tsx
Original file line number Diff line number Diff line change
@@ -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<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%);
}
}
74 changes: 42 additions & 32 deletions src/shared/ui/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Portal>
<div className={classNames(cls.Drawer, mods, [className, theme, 'app_drawer'])}>
<Overlay onClick={onClose} />
<div
className={cls.content}
>
{children}
</div>
</div>
</Portal>
);
const mods: Mods = {
[cls.opened]: isOpen,
[cls.isClosing]: isClosing,
};

if (lazy && !isMounted) {
return null;
}

return (
<Portal>
<div
className={classNames(cls.Drawer, mods, [
className,
theme,
"app_drawer",
])}
>
<Overlay onClick={close} />
<div className={cls.content}>{children}</div>
</div>
</Portal>
);
});
71 changes: 12 additions & 59 deletions src/shared/ui/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<ReturnType<typeof setTimeout>>;
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,
Expand All @@ -86,10 +41,8 @@ export const Modal = (props: ModalProps) => {
<div
className={classNames(cls.Modal, mods, [className, theme, "app_modal"])}
>
<Overlay onClick={closeHandler} />
<div className={cls.content} onClick={onContentClick}>
{children}
</div>
<Overlay onClick={close} />
<div className={cls.content}>{children}</div>
</div>
</Portal>
);
Expand Down

0 comments on commit e10563f

Please sign in to comment.