mirror of
https://github.com/remvze/moodist.git
synced 2025-12-17 08:54:13 +00:00
feat: add close event for modals
This commit is contained in:
parent
f81ea9e7bd
commit
af92b1ed90
7 changed files with 104 additions and 24 deletions
|
|
@ -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)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
11
src/hooks/use-close-listener.ts
Normal file
11
src/hooks/use-close-listener.ts
Normal 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
13
src/lib/event.ts
Normal 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
11
src/lib/modal.ts
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue