UNPKG

use-container-queries

Version:

A react hook that tracks a containers size and the range that width falls into within a list of breakpoints. This allows better responsive styling, where the user can style DOM elements based on their container, rather than the browser viewport.

157 lines (128 loc) 5.14 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? react.useLayoutEffect : react.useEffect; /** * useContainerQueries. * * A react hook utilizing the Resize Observer API that observes a containing elements width. * It matches that width with the users predefined breakpoint ranges and determines the containers size. * The main purpose of this is to allow web developers to style DOM elements based on * the size of a containing element rather than the size of the browser viewport. * * @param {QueryBreakpoints} breakpoints A key/value mapping of explicit breakpoint ranges to be * tested against the current observed elements width to find a match. * @param {boolean} ignoreDimensions Flag stating the user doesn't care about the changing container width, * and only the breakpoint changes. The app will only update state on these changes. */ function useContainerQueries(_ref) { var breakpoints = _ref.breakpoints, _ref$ignoreDimensions = _ref.ignoreDimensions, ignoreDimensions = _ref$ignoreDimensions === void 0 ? true : _ref$ignoreDimensions; var initialBreakpoint = Object.keys(breakpoints)[0]; var _useState = react.useState({ activeBreakpoint: initialBreakpoint, width: 0 }), state = _useState[0], setState = _useState[1]; // Store refs for the resize observer instance, and observed element we are tracking. var observerRef = react.useRef(null); var elementRef = react.useRef(null); var matchBreakpoint = react.useCallback(function (prevActive, width) { var currentActive; for (var _i = 0, _Object$entries = Object.entries(breakpoints); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _Object$entries[_i], key = _Object$entries$_i[0], _Object$entries$_i$ = _Object$entries$_i[1], min = _Object$entries$_i$[0], max = _Object$entries$_i$[1]; if (width >= min) { if (max === undefined) { currentActive = key; break; } else if (width <= max) { currentActive = key; break; } } } return { activeBreakpoint: currentActive || prevActive, width: width }; }, [breakpoints]); /** * Callback triggered on from the resize observer on change. */ var handleResize = react.useCallback(function (_ref2) { var entry = _ref2[0]; var width; if (entry.borderBoxSize) { // Checking for chrome as using a non-standard array (https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) width = Math.round(Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0].inlineSize : entry.borderBoxSize.inlineSize); } else { width = Math.round(entry.contentRect.width); } if (ignoreDimensions) { var _matchBreakpoint = matchBreakpoint(state.activeBreakpoint, width), activeBreakpoint = _matchBreakpoint.activeBreakpoint; if (activeBreakpoint !== state.activeBreakpoint) { setState(function (prev) { return _extends({}, prev, { activeBreakpoint: activeBreakpoint }); }); } } else { setState(matchBreakpoint(state.activeBreakpoint, width)); } }, [state.activeBreakpoint, ignoreDimensions, matchBreakpoint]); useIsomorphicLayoutEffect(function () { if (!observerRef.current) { observerRef.current = new ResizeObserver(handleResize); } if (elementRef.current) { observerRef.current.observe(elementRef.current, { box: 'border-box' }); } return function () { var _observerRef$current; (_observerRef$current = observerRef.current) == null ? void 0 : _observerRef$current.disconnect(); observerRef.current = null; }; }, [handleResize]); // Ref callback passed to user // todo: allow user to provide their own ref. var assignRef = react.useCallback(function (node) { if (elementRef.current) { var _observerRef$current2; (_observerRef$current2 = observerRef.current) == null ? void 0 : _observerRef$current2.unobserve(elementRef.current); } elementRef.current = node; if (node) { var _observerRef$current3; (_observerRef$current3 = observerRef.current) == null ? void 0 : _observerRef$current3.observe(node); } }, []); return { ref: assignRef, width: state.width, active: state.activeBreakpoint }; } exports.useContainerQueries = useContainerQueries; //# sourceMappingURL=use-container-queries.cjs.development.js.map