@prokodo/ui
Version:
UI components for production-grade Next.js + Headless CMS (Strapi, Contentful, Headless WordPress) websites by prokodo – built for Core Web Vitals & SEO.
124 lines (123 loc) • 4.08 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { useState, useRef, useEffect, useMemo } from "react";
function addMQChangeListener(mql, handler) {
var _a;
if ("addEventListener" in mql) {
mql.addEventListener("change", handler);
return () => mql.removeEventListener("change", handler);
}
const legacy = mql;
(_a = legacy.addListener) == null ? void 0 : _a.call(legacy, handler);
return () => {
var _a2;
return (_a2 = legacy.removeListener) == null ? void 0 : _a2.call(legacy, handler);
};
}
__name(addMQChangeListener, "addMQChangeListener");
function useResponsiveValue(opts) {
const { fallback } = opts;
const [val, setVal] = useState(fallback);
const containerRef = useRef(null);
const [containerWidth, setContainerWidth] = useState(0);
useEffect(() => {
const hasContainerRules = Array.isArray(opts.containerRules) && opts.containerRules.length > 0;
if (!hasContainerRules) return;
const el = containerRef.current;
if (!el || typeof ResizeObserver === "undefined") return;
const ro = new ResizeObserver((entries) => {
var _a, _b;
const w = ((_b = (_a = entries[0]) == null ? void 0 : _a.contentRect) == null ? void 0 : _b.width) ?? 0;
setContainerWidth(w);
});
ro.observe(el);
return () => ro.disconnect();
}, [opts.containerRules]);
const queriesKey = useMemo(
() => JSON.stringify(opts.valuesByQueries ?? []),
[opts.valuesByQueries]
);
const valuesByBreakpointKey = useMemo(
() => JSON.stringify(opts.valuesByBreakpoint ?? {}),
[opts.valuesByBreakpoint]
);
const breakpointsKey = useMemo(
() => JSON.stringify(opts.breakpoints ?? {}),
[opts.breakpoints]
);
const containerRulesKey = useMemo(
() => JSON.stringify(opts.containerRules ?? []),
[opts.containerRules]
);
useEffect(() => {
if (typeof window === "undefined" || !("matchMedia" in window)) return;
const disposers = [];
const queryPairs = opts.valuesByQueries ?? [];
const queryMQLs = queryPairs.map(([q]) => window.matchMedia(q));
queryMQLs.forEach((mql) => disposers.push(addMQChangeListener(mql, compute)));
const bpOrder = ["xl", "lg", "md", "sm", "xs"];
const bps = opts.breakpoints;
const valuesByBp = opts.valuesByBreakpoint;
const bpMQLs = [];
if (bps && valuesByBp) {
for (const k of bpOrder) {
if (k in valuesByBp && bps[k] != null) {
const mql = window.matchMedia(`(min-width:${bps[k]}px)`);
bpMQLs.push({ key: k, mql });
disposers.push(addMQChangeListener(mql, compute));
}
}
}
function compute() {
var _a;
const rules = opts.containerRules ?? [];
if (rules.length) {
const hit = rules.find((r) => {
const minOk = r.min == null || containerWidth >= r.min;
const maxOk = r.max == null || containerWidth < r.max;
return minOk && maxOk;
});
if (hit) {
setVal(hit.value);
return;
}
}
for (let i = 0; i < queryPairs.length; i++) {
const mql = queryMQLs[i];
if (mql != null && mql.matches === true) {
const v = (_a = queryPairs[i]) == null ? void 0 : _a[1];
setVal(v);
return;
}
}
if (bps && valuesByBp) {
for (const { key, mql } of bpMQLs) {
if (mql.matches && key in valuesByBp) {
setVal(valuesByBp[key]);
return;
}
}
}
setVal(fallback);
}
__name(compute, "compute");
compute();
return () => disposers.forEach((d) => d());
}, [
opts.breakpoints,
opts.containerRules,
opts.valuesByBreakpoint,
opts.valuesByQueries,
fallback,
containerWidth,
queriesKey,
valuesByBreakpointKey,
breakpointsKey,
containerRulesKey
]);
return useMemo(() => ({ value: val, ref: containerRef }), [val]);
}
__name(useResponsiveValue, "useResponsiveValue");
export {
useResponsiveValue
};