UNPKG

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