| import { useEffect, RefObject, useState } from 'react'; |
|
|
| export function useOutsideAlerter<T extends HTMLElement>( |
| ref: RefObject<T>, |
| handler: () => void, |
| additionalDeps: unknown[], |
| handleEscapeKey?: boolean, |
| ) { |
| useEffect(() => { |
| function handleClickOutside(event: MouseEvent) { |
| if (ref.current && !ref.current.contains(event.target as Node)) { |
| handler(); |
| } |
| } |
|
|
| function handleEscape(event: KeyboardEvent) { |
| if (event.key === 'Escape') { |
| handler(); |
| } |
| } |
|
|
| document.addEventListener('mousedown', handleClickOutside); |
| if (handleEscapeKey) { |
| document.addEventListener('keydown', handleEscape); |
| } |
|
|
| return () => { |
| document.removeEventListener('mousedown', handleClickOutside); |
| if (handleEscapeKey) { |
| document.removeEventListener('keydown', handleEscape); |
| } |
| }; |
| }, [ref, ...additionalDeps]); |
| } |
|
|
| export function useMediaQuery() { |
| const mobileQuery = '(max-width: 768px)'; |
| const darkModeQuery = '(prefers-color-scheme: dark)'; |
| const desktopQuery = '(min-width: 960px)'; |
| const [isMobile, setIsMobile] = useState(false); |
| const [isDesktop, setIsDesktop] = useState(false); |
| const [isDarkMode, setIsDarkMode] = useState(false); |
|
|
| useEffect(() => { |
| const mobileMedia = window.matchMedia(mobileQuery); |
| const desktopMedia = window.matchMedia(desktopQuery); |
| const darkModeMedia = window.matchMedia(darkModeQuery); |
|
|
| const updateMediaQueries = () => { |
| setIsMobile(mobileMedia.matches); |
| setIsDesktop(desktopMedia.matches); |
| setIsDarkMode(darkModeMedia.matches); |
| }; |
|
|
| updateMediaQueries(); |
|
|
| const listener = () => updateMediaQueries(); |
| window.addEventListener('resize', listener); |
|
|
| return () => { |
| window.removeEventListener('resize', listener); |
| }; |
| }, [mobileQuery, desktopQuery, darkModeQuery]); |
|
|
| return { isMobile, isDesktop, isDarkMode }; |
| } |
|
|