UNPKG

@atlaskit/onboarding

Version:

An onboarding spotlight introduces new features to users through focused messages or multi-step tours.

94 lines (91 loc) 3.85 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import { useCallback, useEffect, useRef, useState } from '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 */ export 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 = useState(0), _useState2 = _slicedToArray(_useState, 2), targetContentVersion = _useState2[0], setTargetContentVersion = _useState2[1]; var mutationObserver = useRef(undefined); var cancelIdleCallback = useRef(undefined); var disposer = 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 = 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]); 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); }; }