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 { IoMenu, IoClose } from 'react-icons/io5/index';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
@ -20,22 +20,52 @@ import { Notepad, Pomodoro } from '@/components/toolbox';
import { fade, mix, slideY } from '@/lib/motion'; import { fade, mix, slideY } from '@/lib/motion';
import styles from './menu.module.css'; import styles from './menu.module.css';
import { useCloseListener } from '@/hooks/use-close-listener';
export function Menu() { export function Menu() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [showPresets, setShowPresets] = useState(false); const initial = useMemo(
const [showShareLink, setShowShareLink] = useState(false); () => ({
const [showNotepad, setShowNotepad] = useState(false); notepad: false,
const [showPomodoro, setShowPomodoro] = useState(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+m', () => setIsOpen(prev => !prev));
useHotkeys('shift+n', () => setShowNotepad(prev => !prev)); useHotkeys('shift+n', () => open('notepad'));
useHotkeys('shift+p', () => setShowPomodoro(prev => !prev)); useHotkeys('shift+p', () => open('pomodoro'));
useHotkeys('shift+alt+p', () => setShowPresets(prev => !prev)); useHotkeys('shift+alt+p', () => open('presets'));
useHotkeys('shift+s', () => setShowShareLink(prev => !prev)); useHotkeys('shift+s', () => open('shareLink'));
useCloseListener(closeAll);
const variants = mix(fade(), slideY());
return ( return (
<> <>
@ -64,12 +94,12 @@ export function Menu() {
initial="hidden" initial="hidden"
variants={variants} variants={variants}
> >
<PresetsItem open={() => setShowPresets(true)} /> <PresetsItem open={() => open('presets')} />
<ShareItem open={() => setShowShareLink(true)} /> <ShareItem open={() => open('shareLink')} />
<ShuffleItem /> <ShuffleItem />
<Divider /> <Divider />
<NotepadItem open={() => setShowNotepad(true)} /> <NotepadItem open={() => open('notepad')} />
<PomodoroItem open={() => setShowPomodoro(true)} /> <PomodoroItem open={() => open('pomodoro')} />
<Divider /> <Divider />
<DonateItem /> <DonateItem />
<SourceItem /> <SourceItem />
@ -82,12 +112,16 @@ export function Menu() {
</div> </div>
<ShareLinkModal <ShareLinkModal
show={showShareLink} show={modals.shareLink}
onClose={() => setShowShareLink(false)} 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(() => { useEffect(() => {
function keyListener(e: KeyboardEvent) { function keyListener(e: KeyboardEvent) {
if (e.key === 'escape') { if (show && e.key === 'Escape') {
onClose(); onClose();
} }
} }
@ -48,7 +48,7 @@ export function Modal({
document.addEventListener('keydown', keyListener); document.addEventListener('keydown', keyListener);
return () => document.removeEventListener('keydown', keyListener); return () => document.removeEventListener('keydown', keyListener);
}, [onClose]); }, [onClose, show]);
return ( return (
<Portal> <Portal>

View file

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

View file

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