@atlaskit/onboarding
Version:
An onboarding spotlight introduces new features to users through focused messages or multi-step tours.
101 lines (97 loc) • 4.13 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useElementObserver = useElementObserver;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = require("react");
/**
* A hook that watches for changes to a node and forces a re-render when it changes.
*
* @param element The node to watch for changes.
* @param opts Options for the hook.
* @param [opts.disableWatch=false] Whether to watch for changes or not.
* @param [opts.onChange] A callback that is called when the node changes.
*
* @returns A version number that is incremented when the node changes to force a re-render.
*
* @internal
*/
function useElementObserver(element) {
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var onChange = opts.onChange,
_opts$disableWatch = opts.disableWatch,
disableWatch = _opts$disableWatch === void 0 ? false : _opts$disableWatch;
// This state is used to force a re-render when the target changes
var _useState = (0, _react.useState)(0),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
targetContentVersion = _useState2[0],
setTargetContentVersion = _useState2[1];
var mutationObserver = (0, _react.useRef)(undefined);
var cancelIdleCallback = (0, _react.useRef)(undefined);
var disposer = (0, _react.useCallback)(function () {
var _cancelIdleCallback$c, _mutationObserver$cur;
(_cancelIdleCallback$c = cancelIdleCallback.current) === null || _cancelIdleCallback$c === void 0 || _cancelIdleCallback$c.call(cancelIdleCallback);
cancelIdleCallback.current = undefined;
(_mutationObserver$cur = mutationObserver.current) === null || _mutationObserver$cur === void 0 || _mutationObserver$cur.disconnect();
mutationObserver.current = undefined;
}, []);
var onMutation = (0, _react.useCallback)(function () {
var _cancelIdleCallback$c2;
// MutationObserver can make multiple calls to the callback in quick succession so debounce when idle
(_cancelIdleCallback$c2 = cancelIdleCallback.current) === null || _cancelIdleCallback$c2 === void 0 || _cancelIdleCallback$c2.call(cancelIdleCallback);
cancelIdleCallback.current = callWhenIdle(function () {
return setTargetContentVersion(function (v) {
var version = v + 1;
onChange === null || onChange === void 0 || onChange(version);
return version;
});
});
}, [onChange]);
(0, _react.useEffect)(function () {
// Clean up any existing observer in the case that the target node has changed
disposer();
if (!disableWatch && element) {
mutationObserver.current = new MutationObserver(onMutation);
mutationObserver.current.observe(element, {
attributes: true,
childList: true,
subtree: true
});
}
return disposer;
}, [disableWatch, disposer, element, onMutation]);
return targetContentVersion;
}
/**
* When the browser is idle call the callback.
*
* This function will use `requestIdleCallback` if available, otherwise it will fallback to `setTimeout`.
*
* @param callback The function to call when the browser is idle.
* @param [opts] Options for the idle callback.
* @param [opts.timeout=100] The maximum time to wait for the browser to be idle.
*
* @returns A function that can be called to cancel the idle callback.
*
* @internal
*/
function callWhenIdle(callback) {
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var _opts$timeout = opts.timeout,
timeout = _opts$timeout === void 0 ? 100 : _opts$timeout;
if (typeof window.requestIdleCallback === 'function') {
var idleHandle = window.requestIdleCallback(callback, {
timeout: timeout
});
return function () {
return cancelIdleCallback(idleHandle);
};
}
// Fallback to setTimeout if requestIdleCallback is not available
var timeoutHandle = setTimeout(callback, timeout);
return function () {
return clearTimeout(timeoutHandle);
};
}