import { useEffect, useState } from 'react'; const voidNoop = () => {}; const booleanNoop = () => false; let SUPPORTS_MATCH_MEDIA: boolean; function isMatchMediaSupported(): boolean { if (SUPPORTS_MATCH_MEDIA === undefined) { const targetWindow = getMatchMediaWindow(); const testingMediaQueryList = 'matchMedia' in targetWindow ? targetWindow.matchMedia('only screen') : null; SUPPORTS_MATCH_MEDIA = Boolean( testingMediaQueryList && 'addEventListener' in testingMediaQueryList && 'removeEventListener' in testingMediaQueryList ); } return SUPPORTS_MATCH_MEDIA; } function createMediaQueryList(query: string): MediaQueryList { if (isMatchMediaSupported()) { const targetWindow = getMatchMediaWindow(); return targetWindow.matchMedia(query); } else { return { addEventListener: voidNoop, addListener: voidNoop, dispatchEvent: booleanNoop, matches: false, media: query, onchange: null, removeEventListener: voidNoop, removeListener: voidNoop, }; } } function getMatchMediaWindow(): Window { try { if (window.top && window.top !== window) { // Accessing a property on window.top will throw if cross-origin void window.top.document; return window.top; } return window; } catch { return window; } } // TODO: Use values from tokens.ts – https://shoptet.atlassian.net/browse/FRONTEND-2304 // References, see: // - cms/js/_repo-shared/main.js // - frontend/libs/design-system/ui/react/src/tokens/dimensions.less // - https://www.figma.com/design/jgLdzvUYrsslfYAAPHcrzT/Shoptet-%E2%80%93-Design-System?node-id=8849-14026 const breakpointQueries = Object.freeze({ sm: createMediaQueryList('only screen and (min-width: 576px)'), md: createMediaQueryList('only screen and (min-width: 768px)'), lg: createMediaQueryList('only screen and (min-width: 992px)'), xl: createMediaQueryList('only screen and (min-width: 1024px)'), xxl: createMediaQueryList('only screen and (min-width: 1400px)'), }); export type BreakpointKey = keyof typeof breakpointQueries; /** * Hook that returns whether the screen has a minimum width of the specified breakpoint. * * This is hook might be too specific, consider using `useLayoutBreakpoint` instead that * is intended for more general use cases. * * @param key - The breakpoint key. */ export function useScreenHasMinWidth(key: BreakpointKey): boolean { const [matches, setMatches] = useState(breakpointQueries[key].matches); useEffect(() => { function handleMediaQueryChange() { setMatches(breakpointQueries[key].matches); } breakpointQueries[key].addEventListener('change', handleMediaQueryChange); return () => { breakpointQueries[key].removeEventListener('change', handleMediaQueryChange); }; }, [key]); return matches; }