@react-hook/media-query
Version:
React hooks that update when media queries change between matched and unmatched states.
141 lines (119 loc) • 4.74 kB
JavaScript
;
exports.__esModule = true;
exports.useMediaQueries = useMediaQueries;
exports.useMediaQuery = useMediaQuery;
var React = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("react"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function queriesDidChange(prevQueries, nextQueries) {
if (nextQueries === prevQueries) return false;
const nextQueriesArr = Object.values(nextQueries);
const prevQueriesArr = Object.values(prevQueries);
if (nextQueriesArr.length !== prevQueriesArr.length) return true;
if (nextQueriesArr.some((q, i) => q !== prevQueriesArr[i])) return true;
const prevKeys = Object.keys(prevQueries);
return Object.keys(nextQueries).some((n, i) => n !== prevKeys[i]);
}
function _ref(curr, key) {
curr.matches[key] = false;
curr.mediaQueries[key] = {};
return curr;
}
function init(queries) {
const queryKeys = Object.keys(queries);
/* istanbul ignore next */
if (typeof window === 'undefined') return queryKeys.reduce(_ref, {
mediaQueries: {},
matches: {}
});
return queryKeys.reduce((state, name) => {
const mql = window.matchMedia(queries[name]);
state.mediaQueries[name] = mql;
state.matches[name] = mql.matches;
return state;
}, {
mediaQueries: {},
matches: {}
});
}
function reducer(state, action) {
function _ref2(prev, key) {
prev[key] = state.mediaQueries[key].matches;
return prev;
}
switch (action.type) {
case 'updateMatches':
return {
matches: Object.keys(state.mediaQueries).reduce(_ref2, {}),
mediaQueries: state.mediaQueries
};
case 'setQueries':
return init(action.queries);
}
}
/**
* A hook that returns a [`MediaQueryMatches`](#mediaquerymatches) object which will
* tell you if specific media queries matched, all media queries matched, or
* any media queries matched. Matches in this hook will always return `false` when
* rendering on the server.
*
* @param queryMap The media queries you want to match against e.g. `{screen: "screen", width: "(min-width: 12em)"}`
*/
function useMediaQueries(queryMap) {
const prevQueries = React.useRef(queryMap);
const [state, dispatch] = React.useReducer(reducer, queryMap, init);
React.useEffect(() => {
if (queriesDidChange(queryMap, prevQueries.current)) {
dispatch({
type: 'setQueries',
queries: queryMap
});
prevQueries.current = queryMap;
}
}, [queryMap]);
function _ref3() {
return dispatch({
type: 'updateMatches'
});
}
function _ref4(mq) {
const callback = _ref3;
if (typeof mq.addListener !== 'undefined') mq.addListener(callback);else mq.addEventListener('change', callback);
return callback;
}
React.useEffect(() => {
const queries = Object.values(state.mediaQueries);
const callbacks = queries.map(_ref4);
function _ref5(mq, i) {
if (typeof mq.addListener !== 'undefined') mq.removeListener(callbacks[i]);else mq.removeEventListener('change', callbacks[i]);
}
return () => {
queries.forEach(_ref5);
};
}, [state.mediaQueries]);
const {
matches
} = state;
const matchValues = React.useMemo(() => Object.values(matches), [matches]);
return {
matches,
matchesAny: matchValues.some(Boolean),
matchesAll: matchValues.length > 0 && matchValues.every(Boolean)
};
}
/**
* A hook that returns `true` if the media query matched and `false` if not. This
* hook will always return `false` when rendering on the server.
*
* @param query The media query you want to match against e.g. `"only screen and (min-width: 12em)"`
*/
function useMediaQuery(query) {
return useMediaQueries(getObj(query)).matchesAll;
}
const cache = {};
function getObj(query) {
if (cache[query] === void 0) cache[query] = {
default: query
};
return cache[query];
}