import React from 'react'; import { useToken } from '../theme/internal'; export const responsiveArray = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs']; const getResponsiveMap = token => ({ xs: `(max-width: ${token.screenXSMax}px)`, sm: `(min-width: ${token.screenSM}px)`, md: `(min-width: ${token.screenMD}px)`, lg: `(min-width: ${token.screenLG}px)`, xl: `(min-width: ${token.screenXL}px)`, xxl: `(min-width: ${token.screenXXL}px)` }); /** * Ensures that the breakpoints token are valid, in good order * For each breakpoint : screenMin <= screen <= screenMax and screenMax <= nextScreenMin */ const validateBreakpoints = token => { const indexableToken = token; const revBreakpoints = [].concat(responsiveArray).reverse(); revBreakpoints.forEach((breakpoint, i) => { const breakpointUpper = breakpoint.toUpperCase(); const screenMin = `screen${breakpointUpper}Min`; const screen = `screen${breakpointUpper}`; if (!(indexableToken[screenMin] <= indexableToken[screen])) { throw new Error(`${screenMin}<=${screen} fails : !(${indexableToken[screenMin]}<=${indexableToken[screen]})`); } if (i < revBreakpoints.length - 1) { const screenMax = `screen${breakpointUpper}Max`; if (!(indexableToken[screen] <= indexableToken[screenMax])) { throw new Error(`${screen}<=${screenMax} fails : !(${indexableToken[screen]}<=${indexableToken[screenMax]})`); } const nextBreakpointUpperMin = revBreakpoints[i + 1].toUpperCase(); const nextScreenMin = `screen${nextBreakpointUpperMin}Min`; if (!(indexableToken[screenMax] <= indexableToken[nextScreenMin])) { throw new Error(`${screenMax}<=${nextScreenMin} fails : !(${indexableToken[screenMax]}<=${indexableToken[nextScreenMin]})`); } } }); return token; }; export default function useResponsiveObserver() { const [, token] = useToken(); const responsiveMap = getResponsiveMap(validateBreakpoints(token)); // To avoid repeat create instance, we add `useMemo` here. return React.useMemo(() => { const subscribers = new Map(); let subUid = -1; let screens = {}; return { matchHandlers: {}, dispatch(pointMap) { screens = pointMap; subscribers.forEach(func => func(screens)); return subscribers.size >= 1; }, subscribe(func) { if (!subscribers.size) this.register(); subUid += 1; subscribers.set(subUid, func); func(screens); return subUid; }, unsubscribe(paramToken) { subscribers.delete(paramToken); if (!subscribers.size) this.unregister(); }, unregister() { Object.keys(responsiveMap).forEach(screen => { const matchMediaQuery = responsiveMap[screen]; const handler = this.matchHandlers[matchMediaQuery]; handler === null || handler === void 0 ? void 0 : handler.mql.removeListener(handler === null || handler === void 0 ? void 0 : handler.listener); }); subscribers.clear(); }, register() { Object.keys(responsiveMap).forEach(screen => { const matchMediaQuery = responsiveMap[screen]; const listener = _ref => { let { matches } = _ref; this.dispatch(Object.assign(Object.assign({}, screens), { [screen]: matches })); }; const mql = window.matchMedia(matchMediaQuery); mql.addListener(listener); this.matchHandlers[matchMediaQuery] = { mql, listener }; listener(mql); }); }, responsiveMap }; }, [token]); } export const matchScreen = (screens, screenSizes) => { if (screenSizes && typeof screenSizes === 'object') { for (let i = 0; i < responsiveArray.length; i++) { const breakpoint = responsiveArray[i]; if (screens[breakpoint] && screenSizes[breakpoint] !== undefined) { return screenSizes[breakpoint]; } } } };