UNPKG

@fluent-windows/hooks

Version:
90 lines (82 loc) 2.07 kB
/** * Tracks state of a CSS media query. * * Demo * import { useMedia } from '@fluent-windows/hooks' * * const isWide = useMedia('(min-width: 600px)') * * <div> * Screen is wide: {isWide ? 'Yes' : 'No'} * </div> * * // or you can use breakpoints * * const isWide = useMedia('sm') * <div> * Screen is wide: {isWide ? 'Yes' : 'No'} * </div> */ import * as React from 'react'; import * as json2mq_ from 'json2mq'; import useGlobal from '../useGlobal'; // Cannot call a namespace ('json2mq') https://github.com/rollup/rollup/issues/670 // eslint-disable-next-line @typescript-eslint/no-angle-bracket-type-assertion const json2mq = json2mq_.default || json2mq_; const defaultBreakpoints = { sm: 600, md: 960, lg: 1280, xl: 1920 }; const model = ({ sm, md, lg, xl }) => ({ sm: json2mq({ screen: true, minWidth: sm }), md: json2mq({ screen: true, minWidth: md }), lg: json2mq({ screen: true, minWidth: lg }), xl: json2mq({ screen: true, minWidth: xl }) }); function useMedia(mediaQuery, option = { breakpoints: defaultBreakpoints }) { const { breakpoints } = option; const isBreakpoint = Object.prototype.hasOwnProperty.call(breakpoints, mediaQuery); const transformedModel = React.useMemo(() => model(breakpoints), [breakpoints]); const global = useGlobal(); const query = isBreakpoint ? transformedModel[mediaQuery] : typeof mediaQuery === 'string' ? mediaQuery : json2mq(mediaQuery); const mql = global && global.matchMedia && global.matchMedia(query); if (!mql) return false; // eslint-disable-next-line const [state, setState] = React.useState(() => mql.matches); // eslint-disable-next-line React.useLayoutEffect(() => { let mounted = true; const handleChange = () => { if (!mounted) return; setState(mql.matches); }; setState(mql.matches); mql.addListener(handleChange); return () => { mounted = false; mql.removeListener(handleChange); }; }, [mql, query]); return state; } export default useMedia;