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 { 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)} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
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