@shopify/polaris
Version:
Shopify’s admin product component library
116 lines (100 loc) • 3.99 kB
JavaScript
import { useState } from 'react';
import { getMediaConditions, breakpoints } from '@shopify/polaris-tokens';
import { isServer } from './target.js';
import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.js';
const Breakpoints = {
// TODO: Update to smDown
navigationBarCollapsed: '767.95px',
// TODO: Update to lgDown
stackedContent: '1039.95px'
};
const noWindowMatches = {
media: '',
addListener: noop,
removeListener: noop,
matches: false,
onchange: noop,
addEventListener: noop,
removeEventListener: noop,
dispatchEvent: _ => true
};
function noop() {}
function navigationBarCollapsed() {
return typeof window === 'undefined' ? noWindowMatches : window.matchMedia(`(max-width: ${Breakpoints.navigationBarCollapsed})`);
}
function stackedContent() {
return typeof window === 'undefined' ? noWindowMatches : window.matchMedia(`(max-width: ${Breakpoints.stackedContent})`);
}
/**
* Directional alias for each Polaris `breakpoints` token.
*
* @example 'smUp' | 'smDown' | 'smOnly' | 'mdUp' | etc.
*/
const breakpointsQueryEntries = getBreakpointsQueryEntries(breakpoints);
function getMatches(defaults) {
if (!isServer) {
return Object.fromEntries(breakpointsQueryEntries.map(([directionAlias, query]) => [directionAlias, window.matchMedia(query).matches]));
}
if (typeof defaults === 'object' && defaults !== null) {
return Object.fromEntries(breakpointsQueryEntries.map(([directionAlias]) => {
var _defaults$directionAl;
return [directionAlias, (_defaults$directionAl = defaults[directionAlias]) !== null && _defaults$directionAl !== void 0 ? _defaults$directionAl : false];
}));
}
return Object.fromEntries(breakpointsQueryEntries.map(([directionAlias]) => [directionAlias, defaults !== null && defaults !== void 0 ? defaults : false]));
}
/**
* Retrieves media query matches for each directional Polaris `breakpoints` alias.
*
* @example
* const {smUp} = useBreakpoints();
* return smUp && 'Hello world';
*
* @example
* const {mdUp} = useBreakpoints({defaults: {mdUp: true}});
* mdUp //=> `true` during SSR
*
* @example
* const breakpoints = useBreakpoints({defaults: true});
* breakpoints //=> All values will be `true` during SSR
*/
function useBreakpoints(options) {
const [breakpoints, setBreakpoints] = useState(getMatches(options === null || options === void 0 ? void 0 : options.defaults));
useIsomorphicLayoutEffect(() => {
const mediaQueryLists = breakpointsQueryEntries.map(([_, query]) => window.matchMedia(query));
const handler = () => setBreakpoints(getMatches());
mediaQueryLists.forEach(mql => {
mql.addEventListener('change', handler);
});
return () => mediaQueryLists.forEach(mql => {
mql.removeEventListener('change', handler);
});
}, []);
return breakpoints;
}
/**
* Converts `breakpoints` tokens into directional media query entries.
*
* @example
* const breakpointsQueryEntries = getBreakpointsQueryEntries(breakpoints);
* breakpointsQueryEntries === [
* ['xsUp', '(min-width: ...)'],
* ['xsDown', '(max-width: ...)'],
* ['xsOnly', '(min-width: ...) and (max-width: ...)'],
* ['smUp', '(min-width: ...) and (max-width: ...)'],
* ['mdUp', '(min-width: ...) and (max-width: ...)'],
* // etc.
* ]
*/
function getBreakpointsQueryEntries(breakpoints) {
const mediaConditionEntries = Object.entries(getMediaConditions(breakpoints));
return mediaConditionEntries.map(([breakpointsToken, mediaConditions]) => Object.entries(mediaConditions).map(([direction, mediaCondition]) => {
const breakpointsAlias = breakpointsToken.split('-')[1]; // e.g. smUp, smDown, smOnly, etc.
const directionAlias = `${breakpointsAlias}${capitalize(direction)}`;
return [directionAlias, mediaCondition];
})).flat();
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
export { getBreakpointsQueryEntries, navigationBarCollapsed, stackedContent, useBreakpoints };