diff --git a/src/hooks/useBreakpoint.ts b/src/hooks/useBreakpoint.ts new file mode 100644 index 00000000..8643297e --- /dev/null +++ b/src/hooks/useBreakpoint.ts @@ -0,0 +1,45 @@ +import { useSyncExternalStore } from 'react'; + +import { Breakpoint, breakpoints } from '@/styles/mediaQuery'; + +function getBreakpoint(): Breakpoint { + if (window.matchMedia(`(min-width: ${breakpoints.desktopLarge}px)`).matches) + return 'desktopLarge'; + if (window.matchMedia(`(min-width: ${breakpoints.desktop}px)`).matches) + return 'desktop'; + if (window.matchMedia(`(min-width: ${breakpoints.tablet}px)`).matches) + return 'tablet'; + return 'mobile'; +} + +function subscribe(callback: () => void) { + const mqls = [ + window.matchMedia(`(max-width: ${breakpoints.tablet - 1}px)`), + window.matchMedia( + `(min-width: ${breakpoints.tablet}px) and (max-width: ${breakpoints.desktop - 1}px)`, + ), + window.matchMedia( + `(min-width: ${breakpoints.desktop}px) and (max-width: ${breakpoints.desktopLarge - 1}px)`, + ), + window.matchMedia(`(min-width: ${breakpoints.desktopLarge}px)`), + ]; + mqls.forEach((m) => m.addEventListener('change', callback)); + return () => mqls.forEach((m) => m.removeEventListener('change', callback)); +} + +export function useBreakpoint() { + const bp = useSyncExternalStore( + subscribe, + getBreakpoint, + () => 'desktop' as Breakpoint, + ); + + return { + breakpoint: bp, + isMobile: bp === 'mobile', + isTablet: bp === 'tablet', + isDesktop: bp === 'desktop', + isDesktopLarge: bp === 'desktopLarge', + isMobileOrTablet: bp === 'mobile' || bp === 'tablet', + }; +} diff --git a/src/styles/mediaQuery.ts b/src/styles/mediaQuery.ts index d3b23ada..bab953bb 100644 --- a/src/styles/mediaQuery.ts +++ b/src/styles/mediaQuery.ts @@ -1,20 +1,15 @@ -const bp = { - mobile: 480, - tablet: 1024, -}; - -const mq = (label: keyof typeof bp) => { - const bpArray = Object.keys(bp).map((key) => [ - key, - bp[key as keyof typeof bp], - ]); - - const [result] = bpArray.reduce((acc, [name, size]) => { - if (label === name) return [...acc, `@media (max-width: ${size}px)`]; - return acc; - }, []); - - return result; -}; - -export default mq; +export const breakpoints = { + mobile: 0, + tablet: 768, + desktop: 1024, + desktopLarge: 1260, +} as const; + +export const media = { + mobile: `@media (max-width: ${breakpoints.tablet - 1}px)`, + tablet: `@media (min-width: ${breakpoints.tablet}px) and (max-width: ${breakpoints.desktop - 1}px)`, + desktop: `@media (min-width: ${breakpoints.desktop}px) and (max-width: ${breakpoints.desktopLarge - 1}px)`, + desktopLarge: `@media (min-width: ${breakpoints.desktopLarge}px)`, +} as const; + +export type Breakpoint = keyof typeof media;