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