@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
JavaScript
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;
}