@react-md/utils
Version:
General utils for react-md.
179 lines • 6.7 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
import { ResizeObserver } from "@juggle/resize-observer";
import { useEnsuredRef } from "../useEnsuredRef";
import { useIsomorphicLayoutEffect } from "../useIsomorphicLayoutEffect";
/**
* @internal
*/
function isHeightChange(prevSize, nextSize) {
return (!prevSize ||
prevSize.height !== nextSize.height ||
prevSize.scrollHeight !== nextSize.scrollHeight);
}
/**
* @internal
*/
function isWidthChange(prevSize, nextSize) {
return (!prevSize ||
prevSize.width !== nextSize.width ||
prevSize.scrollWidth !== nextSize.scrollWidth);
}
/**
* Why is there a single shared observer instead of multiple and a
* "subscription" model?
*
* Note: Probably a bit of a premature optimization right now...
*
* @see https://github.com/WICG/resize-observer/issues/59
* @internal
*/
var sharedObserver;
/**
*
* @internal
*/
var subscriptions = [];
/**
* Lazy initializes the shared resize observer which will loop through all the
* subscriptions when a resize event is called.
*
* @internal
*/
function init() {
if (sharedObserver || typeof document === "undefined") {
return;
}
sharedObserver = new ResizeObserver(function (entries) {
var _loop_1 = function (i) {
var entry = entries[i];
var currentSubscriptions = subscriptions.filter(function (_a) {
var target = _a.target;
return target === entry.target;
});
if (!currentSubscriptions.length) {
return { value: void 0 };
}
var _a = entry.contentRect, height = _a.height, width = _a.width;
var _b = entry.target, scrollHeight = _b.scrollHeight, scrollWidth = _b.scrollWidth;
var nextSize = {
height: height,
width: width,
scrollHeight: scrollHeight,
scrollWidth: scrollWidth,
};
for (var j = 0; j < currentSubscriptions.length; j += 1) {
var subscription = currentSubscriptions[j];
var handler = subscription.handler, prevSize = subscription.prevSize, disableHeight = subscription.disableHeight, disableWidth = subscription.disableWidth;
var isNewHeight = isHeightChange(prevSize, nextSize);
var isNewWidth = isWidthChange(prevSize, nextSize);
if ((isNewHeight && !disableHeight) || (isNewWidth && !disableWidth)) {
subscription.prevSize = nextSize;
handler(__assign(__assign({}, nextSize), { element: entry.target }));
}
}
};
// Note: might need to wait until an requestAnimationFrame has completed to
// fix the resize observer loop exceeded error if switching to
// `useIsomorphicLayoutEffect` and a shared observer didn't fix that error:
// https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
for (var i = 0; i < entries.length; i += 1) {
var state_1 = _loop_1(i);
if (typeof state_1 === "object")
return state_1.value;
}
});
}
/**
*
* @internal
*/
function subscribe(target, onResize, disableHeight, disableWidth) {
var exists = subscriptions.find(function (sub) { return sub.target === target; });
subscriptions.push({
target: target,
handler: onResize,
disableWidth: disableWidth,
disableHeight: disableHeight,
prevSize: undefined,
});
if (!exists) {
// I'll silently fail non-initialized observers for now until it becomes an
// issue... But how will I ever know?
sharedObserver === null || sharedObserver === void 0 ? void 0 : sharedObserver.observe(target);
}
}
/**
*
* @internal
*/
function unsubscribe(target, onResize, disableHeight, disableWidth) {
var i = subscriptions.findIndex(function (sub) {
return sub.target === target &&
sub.handler === onResize &&
sub.disableWidth === disableWidth &&
sub.disableHeight === disableHeight;
});
if (i !== -1) {
subscriptions.splice(i, 1);
}
var remaining = subscriptions.some(function (sub) { return sub.target === target; });
if (!remaining) {
// I'll silently fail non-initialized observers for now until it becomes an
// issue... But how will I ever know?
sharedObserver === null || sharedObserver === void 0 ? void 0 : sharedObserver.unobserve(target);
}
}
/**
* The new resize observer API that returns a `refHandler` to attach to a DOM
* node instead of using the weird `target` API.
*
* @remarks \@since 2.3.0
* @param onResize - The resize handler to call when the element has changed
* height or width. If you notice performance issues or other oddities, it is
* recommended to wrap this function in `useCallback`.
* @param options - Any additional options to use for the resize observer.
*/
export function useResizeObserver(onResize, options) {
if (options === void 0) { options = {}; }
var propRef = options.ref, _a = options.disableWidth, disableWidth = _a === void 0 ? false : _a, _b = options.disableHeight, disableHeight = _b === void 0 ? false : _b;
var _c = __read(useEnsuredRef(propRef), 2), ref = _c[0], refHandler = _c[1];
useIsomorphicLayoutEffect(function () {
var target = ref.current;
if ((disableHeight && disableWidth) || !target) {
return;
}
init();
subscribe(target, onResize, disableHeight, disableWidth);
return function () {
unsubscribe(target, onResize, disableHeight, disableWidth);
};
}, [disableHeight, disableWidth, onResize]);
return [ref, refHandler];
}
//# sourceMappingURL=useResizeObserver.js.map