UNPKG

@ehsaneha/react-media-query

Version:

A custom React hook that returns the current responsive breakpoint index or evaluates media query rules based on the window's width.

132 lines (131 loc) 3.59 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React from "react"; // // // // // --- Constants --- const MEDIA_QUERY_DEFAULT_SIZES = { xs: 576, sm: 768, md: 992, lg: 1200, xl: 1700, }; export const BREAKPOINTS = { XS: 0, SM: 1, MD: 2, LG: 3, XL: 4, }; export const MEDIA_QUERRY_RULES = { XS: "xs", EXS: "exs", SM: "sm", ESM: "esm", MD: "md", EMD: "emd", LG: "lg", ELG: "elg", XL: "xl", EXL: "exl", }; const ruleToIndexMap = { xs: 0, exs: 0, sm: 1, esm: 1, md: 2, emd: 2, lg: 3, elg: 3, xl: 4, exl: 4, }; // // // // // --- Context --- const MediaQueryContext = React.createContext({}); MediaQueryContext.displayName = "MediaQueryContext"; export function MediaQueryProvider(_a) { var { children } = _a, props = __rest(_a, ["children"]); return (React.createElement(MediaQueryContext.Provider, { value: props }, children)); } function useMediaQueryContext() { const _context = React.useContext(MediaQueryContext); return { sizes: Object.assign(Object.assign({}, MEDIA_QUERY_DEFAULT_SIZES), _context.sizes), }; } // // // // // --- Utility --- function getCurrentBreakPointIndex(sizes) { const index = ["xs", "sm", "md", "lg", "xl"].findIndex((bp) => window.innerWidth < sizes[bp]); return index < 0 ? 5 : index; } function evaluateRule(size, rule) { if (typeof rule === "number") { return size <= rule; } // else if (typeof rule === "string") { const index = ruleToIndexMap[rule]; return rule.startsWith("e") ? size === index : size <= index; } // else if (typeof rule === "object") { const entries = Object.entries(rule); const enabledKeys = entries.filter(([_, v]) => v); const hasOnlyOneEnabledKey = enabledKeys.length === 1; for (const [key] of enabledKeys) { if (size <= ruleToIndexMap[key]) { return hasOnlyOneEnabledKey ? true : ruleToIndexMap[key]; } } return hasOnlyOneEnabledKey ? false : 5; } } function getResult(sizes, rule) { const size = getCurrentBreakPointIndex(sizes); const result = rule === undefined ? size : evaluateRule(size, rule); return result; } // // // // --- Hook --- export function useMediaQueryStateless(onChange, { rule, immediate } = {}) { const { sizes } = useMediaQueryContext(); React.useEffect(() => { const handleResize = () => { onChange(getResult(sizes, rule)); }; if (immediate) { onChange(getResult(sizes, rule)); } window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); } export default function useMediaQuery(rule) { const { sizes } = useMediaQueryContext(); const [state, setState] = React.useState(getResult(sizes, rule)); useMediaQueryStateless(setState, { rule }); return state; }