feat: add close event for modals

This commit is contained in:
MAZE 2024-04-24 19:13:29 +03:30
parent f81ea9e7bd
commit af92b1ed90
7 changed files with 104 additions and 24 deletions

View file

@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useMemo, useCallback } from 'react';
import { IoMenu, IoClose } from 'react-icons/io5/index';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { useHotkeys } from 'react-hotkeys-hook';
@ -20,22 +20,52 @@ import { Notepad, Pomodoro } from '@/components/toolbox';
import { fade, mix, slideY } from '@/lib/motion';
import styles from './menu.module.css';
import { useCloseListener } from '@/hooks/use-close-listener';
export function Menu() {
const [isOpen, setIsOpen] = useState(false);
const [showPresets, setShowPresets] = useState(false);
const [showShareLink, setShowShareLink] = useState(false);
const [showNotepad, setShowNotepad] = useState(false);
const [showPomodoro, setShowPomodoro] = useState(false);
const initial = useMemo(
() => ({
notepad: false,
pomodoro: false,
presets: false,
shareLink: false,
}),
[],
);
const variants = mix(fade(), slideY());
const [modals, setModals] = useState<{
notepad: boolean;
pomodoro: boolean;
presets: boolean;
shareLink: boolean;
}>(initial);
const close = useCallback((name: string) => {
setModals(prev => ({ ...prev, [name]: false }));
}, []);
const closeAll = useCallback(() => setModals(initial), [initial]);
const open = useCallback(
(name: string) => {
closeAll();
setIsOpen(false);
setModals(prev => ({ ...prev, [name]: true }));
},
[closeAll],
);
useHotkeys('shift+m', () => setIsOpen(prev => !prev));
useHotkeys('shift+n', () => setShowNotepad(prev => !prev));
useHotkeys('shift+p', () => setShowPomodoro(prev => !prev));
useHotkeys('shift+alt+p', () => setShowPresets(prev => !prev));
useHotkeys('shift+s', () => setShowShareLink(prev => !prev));
useHotkeys('shift+n', () => open('notepad'));
useHotkeys('shift+p', () => open('pomodoro'));
useHotkeys('shift+alt+p', () => open('presets'));
useHotkeys('shift+s', () => open('shareLink'));
useCloseListener(closeAll);
const variants = mix(fade(), slideY());
return (
<>
@ -64,12 +94,12 @@ export function Menu() {
initial="hidden"
variants={variants}
>
<PresetsItem open={() => setShowPresets(true)} />
<ShareItem open={() => setShowShareLink(true)} />
<PresetsItem open={() => open('presets')} />
<ShareItem open={() => open('shareLink')} />
<ShuffleItem />
<Divider />
<NotepadItem open={() => setShowNotepad(true)} />
<PomodoroItem open={() => setShowPomodoro(true)} />
<NotepadItem open={() => open('notepad')} />
<PomodoroItem open={() => open('pomodoro')} />
<Divider />
<DonateItem />
<SourceItem />
@ -82,12 +112,16 @@ export function Menu() {
</div>
<ShareLinkModal
show={showShareLink}
onClose={() => setShowShareLink(false)}
show={modals.shareLink}
onClose={() => close('shareLink')}
/>
<PresetsModal show={modals.presets} onClose={() => close('presets')} />
<Notepad show={modals.notepad} onClose={() => close('notepad')} />
<Pomodoro
open={() => open('pomodoro')}
show={modals.pomodoro}
onClose={() => close('pomodoro')}
/>
<PresetsModal show={showPresets} onClose={() => setShowPresets(false)} />
<Notepad show={showNotepad} onClose={() => setShowNotepad(false)} />
<Pomodoro show={showPomodoro} onClose={() => setShowPomodoro(false)} />
</>
);
}

View file

@ -40,7 +40,7 @@ export function Modal({
useEffect(() => {
function keyListener(e: KeyboardEvent) {
if (e.key === 'escape') {
if (show && e.key === 'Escape') {
onClose();
}
}
@ -48,7 +48,7 @@ export function Modal({
document.addEventListener('keydown', keyListener);
return () => document.removeEventListener('keydown', keyListener);
}, [onClose]);
}, [onClose, show]);
return (
<Portal>

View file

@ -4,6 +4,7 @@ import { Modal } from '@/components/modal';
import { useSoundStore } from '@/store';
import { useSnackbar } from '@/contexts/snackbar';
import { useCloseListener } from '@/hooks/use-close-listener';
import { cn } from '@/helpers/styles';
import { sounds } from '@/data/sounds';
@ -77,6 +78,8 @@ export function SharedModal() {
showSnackbar('Done! You can now play the new selection.');
};
useCloseListener(() => setIsOpen(false));
return (
<Modal show={isOpen} onClose={() => setIsOpen(false)}>
<h1 className={styles.heading}>New sound mix detected!</h1>

View file

@ -16,10 +16,11 @@ import styles from './pomodoro.module.css';
interface PomodoroProps {
onClose: () => void;
open: () => void;
show: boolean;
}
export function Pomodoro({ onClose, show }: PomodoroProps) {
export function Pomodoro({ onClose, open, show }: PomodoroProps) {
const [showSetting, setShowSetting] = useState(false);
const [selectedTab, setSelectedTab] = useState('pomodoro');
@ -125,7 +126,10 @@ export function Pomodoro({ onClose, show }: PomodoroProps) {
<Button
icon={<IoMdSettings />}
tooltip="Change Times"
onClick={() => setShowSetting(true)}
onClick={() => {
onClose();
setShowSetting(true);
}}
/>
</div>
</header>
@ -157,10 +161,14 @@ export function Pomodoro({ onClose, show }: PomodoroProps) {
<Setting
show={showSetting}
times={times}
onClose={() => setShowSetting(false)}
onChange={times => {
setShowSetting(false);
setTimes(times);
open();
}}
onClose={() => {
setShowSetting(false);
open();
}}
/>
</>

View file

@ -0,0 +1,11 @@
import { useEffect } from 'react';
import { onCloseModal } from '@/lib/modal';
export function useCloseListener(listener: () => void) {
useEffect(() => {
const unsubscribe = onCloseModal(listener);
return unsubscribe;
}, [listener]);
}

13
src/lib/event.ts Normal file
View file

@ -0,0 +1,13 @@
export function dispatch(eventName: string) {
const event = new Event(eventName);
document.dispatchEvent(event);
}
export function subscribe(eventName: string, listener: () => void) {
document.addEventListener(eventName, listener);
}
export function unsubscribe(eventName: string, listener: () => void) {
document.removeEventListener(eventName, listener);
}

11
src/lib/modal.ts Normal file
View file

@ -0,0 +1,11 @@
import { dispatch, subscribe, unsubscribe } from './event';
export function closeModals() {
dispatch('closeModals');
}
export function onCloseModal(listener: () => void) {
subscribe('closeModals', listener);
return () => unsubscribe('closeModals', listener);
}