@hakuna-matata-ui/hooks
Version:
React hooks for Chakra components
1,119 lines (969 loc) • 30.9 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
var utils = require('@hakuna-matata-ui/utils');
var copy = require('copy-to-clipboard');
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var copy__default = /*#__PURE__*/_interopDefault(copy);
/**
* React hook to manage boolean (on - off) states
*
* @param initialState the initial boolean state value
*/
function useBoolean(initialState) {
if (initialState === void 0) {
initialState = false;
}
var _useState = React.useState(initialState),
value = _useState[0],
setValue = _useState[1];
var on = React.useCallback(function () {
setValue(true);
}, []);
var off = React.useCallback(function () {
setValue(false);
}, []);
var toggle = React.useCallback(function () {
setValue(function (prev) {
return !prev;
});
}, []);
return [value, {
on: on,
off: off,
toggle: toggle
}];
}
/**
* useSafeLayoutEffect enables us to safely call `useLayoutEffect` on the browser
* (for SSR reasons)
*
* React currently throws a warning when using useLayoutEffect on the server.
* To get around it, we can conditionally useEffect on the server (no-op) and
* useLayoutEffect in the browser.
*
* @see https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
*/
var useSafeLayoutEffect = utils.isBrowser ? React__namespace.useLayoutEffect : React__namespace.useEffect;
/**
* React hook to persist any value between renders,
* but keeps it up-to-date if it changes.
*
* @param value the value or function to persist
*/
function useCallbackRef(fn, deps) {
if (deps === void 0) {
deps = [];
}
var ref = React__namespace.useRef(fn);
useSafeLayoutEffect(function () {
ref.current = fn;
}); // eslint-disable-next-line react-hooks/exhaustive-deps
return React__namespace.useCallback(function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return ref.current == null ? void 0 : ref.current.apply(ref, args);
}, deps);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
var _excluded = ["timeout"];
/**
* React hook to copy content to clipboard
*
* @param text the text or value to copy
* @param {Number} [optionsOrTimeout=1500] optionsOrTimeout - delay (in ms) to switch back to initial state once copied.
* @param {Object} optionsOrTimeout
* @param {string} optionsOrTimeout.format - set the desired MIME type
* @param {number} optionsOrTimeout.timeout - delay (in ms) to switch back to initial state once copied.
*/
function useClipboard(text, optionsOrTimeout) {
if (optionsOrTimeout === void 0) {
optionsOrTimeout = {};
}
var _useState = React.useState(false),
hasCopied = _useState[0],
setHasCopied = _useState[1];
var _ref = typeof optionsOrTimeout === "number" ? {
timeout: optionsOrTimeout
} : optionsOrTimeout,
_ref$timeout = _ref.timeout,
timeout = _ref$timeout === void 0 ? 1500 : _ref$timeout,
copyOptions = _objectWithoutPropertiesLoose(_ref, _excluded);
var onCopy = React.useCallback(function () {
var didCopy = copy__default["default"](text, copyOptions);
setHasCopied(didCopy);
}, [text, copyOptions]);
React.useEffect(function () {
var timeoutId = null;
if (hasCopied) {
timeoutId = window.setTimeout(function () {
setHasCopied(false);
}, timeout);
}
return function () {
if (timeoutId) {
window.clearTimeout(timeoutId);
}
};
}, [timeout, hasCopied]);
return {
value: text,
onCopy: onCopy,
hasCopied: hasCopied
};
}
/**
* Creates a constant value over the lifecycle of a component.
*
* Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
* a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
* you can ensure that initialisers don't execute twice or more.
*/
function useConst(init) {
var ref = React.useRef(null);
if (ref.current === null) {
ref.current = typeof init === "function" ? init() : init;
}
return ref.current;
}
function useControllableProp(prop, state) {
var isControlled = prop !== undefined;
var value = isControlled && typeof prop !== "undefined" ? prop : state;
return [isControlled, value];
}
/**
* React hook for using controlling component state.
* @param props
*/
function useControllableState(props) {
var valueProp = props.value,
defaultValue = props.defaultValue,
onChange = props.onChange,
_props$shouldUpdate = props.shouldUpdate,
shouldUpdate = _props$shouldUpdate === void 0 ? function (prev, next) {
return prev !== next;
} : _props$shouldUpdate;
var onChangeProp = useCallbackRef(onChange);
var shouldUpdateProp = useCallbackRef(shouldUpdate);
var _React$useState = React__namespace.useState(defaultValue),
valueState = _React$useState[0],
setValue = _React$useState[1];
var isControlled = valueProp !== undefined;
var value = isControlled ? valueProp : valueState;
var updateValue = React__namespace.useCallback(function (next) {
var nextValue = utils.runIfFn(next, value);
if (!shouldUpdateProp(value, nextValue)) {
return;
}
if (!isControlled) {
setValue(nextValue);
}
onChangeProp(nextValue);
}, [isControlled, onChangeProp, value, shouldUpdateProp]);
return [value, updateValue];
}
/**
* Reack hook to measure a component's dimensions
*
* @param ref ref of the component to measure
* @param observe if `true`, resize and scroll observers will be turned on
*/
function useDimensions(ref, observe) {
var _React$useState = React__namespace.useState(null),
dimensions = _React$useState[0],
setDimensions = _React$useState[1];
var rafId = React__namespace.useRef();
useSafeLayoutEffect(function () {
if (!ref.current) return undefined;
var node = ref.current;
function measure() {
rafId.current = requestAnimationFrame(function () {
var boxModel = utils.getBox(node);
setDimensions(boxModel);
});
}
measure();
if (observe) {
window.addEventListener("resize", measure);
window.addEventListener("scroll", measure);
}
return function () {
if (observe) {
window.removeEventListener("resize", measure);
window.removeEventListener("scroll", measure);
}
if (rafId.current) {
cancelAnimationFrame(rafId.current);
}
};
}, [observe]);
return dimensions;
}
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);
}
// This implementation is heavily inspired by react-aria's implementation
var defaultIdContext = {
prefix: Math.round(Math.random() * 10000000000),
current: 0
};
var IdContext = /*#__PURE__*/React__namespace.createContext(defaultIdContext);
var IdProvider = /*#__PURE__*/React__namespace.memo(function (_ref) {
var children = _ref.children;
var currentContext = React__namespace.useContext(IdContext);
var isRoot = currentContext === defaultIdContext;
var context = React__namespace.useMemo(function () {
return {
prefix: isRoot ? 0 : ++currentContext.prefix,
current: 0
};
}, [isRoot, currentContext]);
return /*#__PURE__*/React__namespace.createElement(IdContext.Provider, {
value: context
}, children);
});
function useId(idProp, prefix) {
var context = React__namespace.useContext(IdContext);
return React__namespace.useMemo(function () {
return idProp || [prefix, context.prefix, ++context.current].filter(Boolean).join("-");
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[idProp, prefix]);
}
/**
* Reack hook to generate ids for use in compound components
*
* @param idProp the external id passed from the user
* @param prefixes array of prefixes to use
*
* @example
*
* ```js
* const [buttonId, menuId] = useIds("52", "button", "menu")
*
* // buttonId will be `button-52`
* // menuId will be `menu-52`
* ```
*/
function useIds(idProp) {
for (var _len = arguments.length, prefixes = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
prefixes[_key - 1] = arguments[_key];
}
var id = useId(idProp);
return React__namespace.useMemo(function () {
return prefixes.map(function (prefix) {
return prefix + "-" + id;
});
}, [id, prefixes]);
}
/**
* Used to generate an id, and after render, check if that id is rendered so we know
* if we can use it in places such as `aria-labelledby`.
*
* @param partId - The unique id for the component part
*
* @example
* const { ref, id } = useOptionalPart<HTMLInputElement>(`${id}-label`)
*/
function useOptionalPart(partId) {
var _React$useState = React__namespace.useState(null),
id = _React$useState[0],
setId = _React$useState[1];
var ref = React__namespace.useCallback(function (node) {
setId(node ? partId : null);
}, [partId]);
return {
ref: ref,
id: id,
isRendered: Boolean(id)
};
}
function useDisclosure(props) {
if (props === void 0) {
props = {};
}
var _props = props,
onCloseProp = _props.onClose,
onOpenProp = _props.onOpen,
isOpenProp = _props.isOpen,
idProp = _props.id;
var onOpenPropCallbackRef = useCallbackRef(onOpenProp);
var onClosePropCallbackRef = useCallbackRef(onCloseProp);
var _React$useState = React__namespace.useState(props.defaultIsOpen || false),
isOpenState = _React$useState[0],
setIsOpen = _React$useState[1];
var _useControllableProp = useControllableProp(isOpenProp, isOpenState),
isControlled = _useControllableProp[0],
isOpen = _useControllableProp[1];
var id = useId(idProp, "disclosure");
var onClose = React__namespace.useCallback(function () {
if (!isControlled) {
setIsOpen(false);
}
onClosePropCallbackRef == null ? void 0 : onClosePropCallbackRef();
}, [isControlled, onClosePropCallbackRef]);
var onOpen = React__namespace.useCallback(function () {
if (!isControlled) {
setIsOpen(true);
}
onOpenPropCallbackRef == null ? void 0 : onOpenPropCallbackRef();
}, [isControlled, onOpenPropCallbackRef]);
var onToggle = React__namespace.useCallback(function () {
var action = isOpen ? onClose : onOpen;
action();
}, [isOpen, onOpen, onClose]);
return {
isOpen: !!isOpen,
onOpen: onOpen,
onClose: onClose,
onToggle: onToggle,
isControlled: isControlled,
getButtonProps: function getButtonProps(props) {
if (props === void 0) {
props = {};
}
return _extends({}, props, {
"aria-expanded": "true",
"aria-controls": id,
onClick: utils.callAllHandlers(props.onClick, onToggle)
});
},
getDisclosureProps: function getDisclosureProps(props) {
if (props === void 0) {
props = {};
}
return _extends({}, props, {
hidden: !isOpen,
id: id
});
}
};
}
/**
* React hook for performant `useCallbacks`
*
* @see https://github.com/facebook/react/issues/14099#issuecomment-440013892
*
* @deprecated Use `useCallbackRef` instead. `useEventCallback` will be removed
* in a future version.
*/
function useEventCallback(callback) {
var ref = React__namespace.useRef(callback);
useSafeLayoutEffect(function () {
ref.current = callback;
});
return React__namespace.useCallback(function (event) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return ref.current.apply(ref, [event].concat(args));
}, []);
}
/**
* React hook to manage browser event listeners
*
* @param event the event name
* @param handler the event handler function to execute
* @param doc the dom environment to execute against (defaults to `document`)
* @param options the event listener options
*
* @internal
*/
function useEventListener(event, handler, env, options) {
var listener = useCallbackRef(handler);
React__namespace.useEffect(function () {
var _runIfFn;
var node = (_runIfFn = utils.runIfFn(env)) != null ? _runIfFn : document;
node.addEventListener(event, listener, options);
return function () {
node.removeEventListener(event, listener, options);
};
}, [event, env, options, listener]);
return function () {
var _runIfFn2;
var node = (_runIfFn2 = utils.runIfFn(env)) != null ? _runIfFn2 : document;
node.removeEventListener(event, listener, options);
};
}
function useEventListenerMap() {
var listeners = React__namespace.useRef(new Map());
var currentListeners = listeners.current;
var add = React__namespace.useCallback(function (el, type, listener, options) {
var pointerEventListener = utils.wrapPointerEventHandler(listener, type === "pointerdown");
listeners.current.set(listener, {
__listener: pointerEventListener,
type: utils.getPointerEventName(type),
el: el,
options: options
});
el.addEventListener(type, pointerEventListener, options);
}, []);
var remove = React__namespace.useCallback(function (el, type, listener, options) {
var _listeners$current$ge = listeners.current.get(listener),
pointerEventListener = _listeners$current$ge.__listener;
el.removeEventListener(type, pointerEventListener, options);
listeners.current["delete"](pointerEventListener);
}, []);
React__namespace.useEffect(function () {
return function () {
currentListeners.forEach(function (value, key) {
remove(value.el, value.type, key, value.options);
});
};
}, [remove, currentListeners]);
return {
add: add,
remove: remove
};
}
/**
* React effect hook that invokes only on update.
* It doesn't invoke on mount
*/
var useUpdateEffect = function useUpdateEffect(effect, deps) {
var mounted = React__namespace.useRef(false);
React__namespace.useEffect(function () {
if (mounted.current) {
return effect();
}
mounted.current = true;
return undefined; // eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return mounted.current;
};
/**
* React hook to focus an element conditionally
*
* @param ref the ref of the element to focus
* @param options focus management options
*/
function useFocusEffect(ref, options) {
var shouldFocus = options.shouldFocus,
preventScroll = options.preventScroll;
useUpdateEffect(function () {
var node = ref.current;
if (!node || !shouldFocus) return;
if (!utils.hasFocusWithin(node)) {
utils.focus(node, {
preventScroll: preventScroll,
nextTick: true
});
}
}, [shouldFocus, ref, preventScroll]);
}
function preventReturnFocus(containerRef) {
var el = containerRef.current;
if (!el) return false;
var activeElement = utils.getActiveElement(el);
if (!activeElement) return false;
if (utils.contains(el, activeElement)) return false;
if (utils.isTabbable(activeElement)) return true;
return false;
}
/**
* Popover hook to manage the focus when the popover closes or hides.
*
* We either want to return focus back to the popover trigger or
* let focus proceed normally if user moved to another interactive
* element in the viewport.
*/
function useFocusOnHide(containerRef, options) {
var shouldFocusProp = options.shouldFocus,
visible = options.visible,
focusRef = options.focusRef;
var shouldFocus = shouldFocusProp && !visible;
useUpdateEffect(function () {
if (!shouldFocus) return;
if (preventReturnFocus(containerRef)) {
return;
}
var el = (focusRef == null ? void 0 : focusRef.current) || containerRef.current;
if (el) {
utils.focus(el, {
nextTick: true
});
}
}, [shouldFocus, containerRef, focusRef]);
}
/**
* Credit goes to `framer-motion` of this useful utilities.
* License can be found here: https://github.com/framer/motion
*/
/**
* @internal
*/
function usePointerEvent(env, eventName, handler, options) {
return useEventListener(utils.getPointerEventName(eventName), utils.wrapPointerEventHandler(handler, eventName === "pointerdown"), env, options);
}
/**
* Polyfill to get `relatedTarget` working correctly consistently
* across all browsers.
*
* It ensures that elements receives focus on pointer down if
* it's not the active active element.
*
* @internal
*/
function useFocusOnPointerDown(props) {
var ref = props.ref,
elements = props.elements,
enabled = props.enabled;
var isSafari = utils.detectBrowser("Safari");
var doc = function doc() {
return utils.getOwnerDocument(ref.current);
};
usePointerEvent(doc, "pointerdown", function (event) {
if (!isSafari || !enabled) return;
var target = event.target;
var els = elements != null ? elements : [ref];
var isValidTarget = els.some(function (elementOrRef) {
var el = utils.isRefObject(elementOrRef) ? elementOrRef.current : elementOrRef;
return utils.contains(el, target);
});
if (!utils.isActiveElement(target) && isValidTarget) {
event.preventDefault();
utils.focus(target);
}
});
}
var defaultOptions = {
preventScroll: true,
shouldFocus: false
};
function useFocusOnShow(target, options) {
if (options === void 0) {
options = defaultOptions;
}
var _options = options,
focusRef = _options.focusRef,
preventScroll = _options.preventScroll,
shouldFocus = _options.shouldFocus,
visible = _options.visible;
var element = utils.isRefObject(target) ? target.current : target;
var autoFocus = shouldFocus && visible;
var onFocus = React.useCallback(function () {
if (!element || !autoFocus) return;
if (utils.contains(element, document.activeElement)) return;
if (focusRef != null && focusRef.current) {
utils.focus(focusRef.current, {
preventScroll: preventScroll,
nextTick: true
});
} else {
var tabbableEls = utils.getAllFocusable(element);
if (tabbableEls.length > 0) {
utils.focus(tabbableEls[0], {
preventScroll: preventScroll,
nextTick: true
});
}
}
}, [autoFocus, preventScroll, element, focusRef]);
useUpdateEffect(function () {
onFocus();
}, [onFocus]);
useEventListener("transitionend", onFocus, element);
}
function useUnmountEffect(fn, deps) {
if (deps === void 0) {
deps = [];
}
return React__namespace.useEffect(function () {
return function () {
return fn();
};
}, // eslint-disable-next-line react-hooks/exhaustive-deps
deps);
}
function useForceUpdate() {
var unloadingRef = React__namespace.useRef(false);
var _React$useState = React__namespace.useState(0),
count = _React$useState[0],
setCount = _React$useState[1];
useUnmountEffect(function () {
unloadingRef.current = true;
});
return React__namespace.useCallback(function () {
if (!unloadingRef.current) {
setCount(count + 1);
}
}, [count]);
}
/**
* React Hook that provides a declarative `setInterval`
*
* @param callback the callback to execute at interval
* @param delay the `setInterval` delay (in ms)
*/
function useInterval(callback, delay) {
var fn = useCallbackRef(callback);
React__namespace.useEffect(function () {
var intervalId = null;
var tick = function tick() {
return fn();
};
if (delay !== null) {
intervalId = window.setInterval(tick, delay);
}
return function () {
if (intervalId) {
window.clearInterval(intervalId);
}
};
}, [delay, fn]);
}
/**
* React hook to persist any value between renders,
* but keeps it up-to-date if it changes.
*
* @param value the value or function to persist
*/
function useLatestRef(value) {
var ref = React__namespace.useRef(null);
ref.current = value;
return ref;
}
/* eslint-disable react-hooks/exhaustive-deps */
function assignRef(ref, value) {
if (ref == null) return;
if (typeof ref === "function") {
ref(value);
return;
}
try {
// @ts-ignore
ref.current = value;
} catch (error) {
throw new Error("Cannot assign value '" + value + "' to ref '" + ref + "'");
}
}
/**
* React hook that merges react refs into a single memoized function
*
* @example
* import React from "react";
* import { useMergeRefs } from `@hakuna-matata-ui/hooks`;
*
* const Component = React.forwardRef((props, ref) => {
* const internalRef = React.useRef();
* return <div {...props} ref={useMergeRefs(internalRef, ref)} />;
* });
*/
function useMergeRefs() {
for (var _len = arguments.length, refs = new Array(_len), _key = 0; _key < _len; _key++) {
refs[_key] = arguments[_key];
}
return React__namespace.useMemo(function () {
if (refs.every(function (ref) {
return ref == null;
})) {
return null;
}
return function (node) {
refs.forEach(function (ref) {
if (ref) assignRef(ref, node);
});
};
}, refs);
}
/**
* @deprecated `useMouseDownRef` will be removed in a future version.
*/
function useMouseDownRef(shouldListen) {
if (shouldListen === void 0) {
shouldListen = true;
}
var mouseDownRef = React__namespace["default"].useRef();
useEventListener("mousedown", function (event) {
if (shouldListen) {
mouseDownRef.current = event.target;
}
});
return mouseDownRef;
}
/**
* Example, used in components like Dialogs and Popovers so they can close
* when a user clicks outside them.
*/
function useOutsideClick(props) {
var ref = props.ref,
handler = props.handler,
_props$enabled = props.enabled,
enabled = _props$enabled === void 0 ? true : _props$enabled;
var savedHandler = useCallbackRef(handler);
var stateRef = React.useRef({
isPointerDown: false,
ignoreEmulatedMouseEvents: false
});
var state = stateRef.current;
React.useEffect(function () {
if (!enabled) return;
var onPointerDown = function onPointerDown(e) {
if (isValidEvent(e, ref)) {
state.isPointerDown = true;
}
};
var onMouseUp = function onMouseUp(event) {
if (state.ignoreEmulatedMouseEvents) {
state.ignoreEmulatedMouseEvents = false;
return;
}
if (state.isPointerDown && handler && isValidEvent(event, ref)) {
state.isPointerDown = false;
savedHandler(event);
}
};
var onTouchEnd = function onTouchEnd(event) {
state.ignoreEmulatedMouseEvents = true;
if (handler && state.isPointerDown && isValidEvent(event, ref)) {
state.isPointerDown = false;
savedHandler(event);
}
};
var doc = utils.getOwnerDocument(ref.current);
doc.addEventListener("mousedown", onPointerDown, true);
doc.addEventListener("mouseup", onMouseUp, true);
doc.addEventListener("touchstart", onPointerDown, true);
doc.addEventListener("touchend", onTouchEnd, true);
return function () {
doc.removeEventListener("mousedown", onPointerDown, true);
doc.removeEventListener("mouseup", onMouseUp, true);
doc.removeEventListener("touchstart", onPointerDown, true);
doc.removeEventListener("touchend", onTouchEnd, true);
};
}, [handler, ref, savedHandler, state, enabled]);
}
function isValidEvent(event, ref) {
var _ref$current;
var target = event.target;
if (event.button > 0) return false; // if the event target is no longer in the document
if (target) {
var doc = utils.getOwnerDocument(target);
if (!doc.body.contains(target)) return false;
}
return !((_ref$current = ref.current) != null && _ref$current.contains(target));
}
function usePanGesture(ref, props) {
var onPan = props.onPan,
onPanStart = props.onPanStart,
onPanEnd = props.onPanEnd,
onPanSessionStart = props.onPanSessionStart,
onPanSessionEnd = props.onPanSessionEnd,
threshold = props.threshold;
var hasPanEvents = Boolean(onPan || onPanStart || onPanEnd || onPanSessionStart || onPanSessionEnd);
var panSession = React.useRef(null);
var handlers = {
onSessionStart: onPanSessionStart,
onSessionEnd: onPanSessionEnd,
onStart: onPanStart,
onMove: onPan,
onEnd: function onEnd(event, info) {
panSession.current = null;
onPanEnd == null ? void 0 : onPanEnd(event, info);
}
};
React.useEffect(function () {
var _panSession$current;
(_panSession$current = panSession.current) == null ? void 0 : _panSession$current.updateHandlers(handlers);
});
function onPointerDown(event) {
panSession.current = new utils.PanSession(event, handlers, threshold);
}
usePointerEvent(function () {
return ref.current;
}, "pointerdown", hasPanEvents ? onPointerDown : utils.noop);
useUnmountEffect(function () {
var _panSession$current2;
(_panSession$current2 = panSession.current) == null ? void 0 : _panSession$current2.end();
panSession.current = null;
});
}
function usePrevious(value) {
var ref = React.useRef();
React.useEffect(function () {
ref.current = value;
}, [value]);
return ref.current;
}
/**
* Checks if the key pressed is a printable character
* and can be used for shortcut navigation
*/
function isPrintableCharacter(event) {
var key = event.key;
return key.length === 1 || key.length > 1 && /[^a-zA-Z0-9]/.test(key);
}
/**
* React hook that provides an enhanced keydown handler,
* that's used for key navigation within menus, select dropdowns.
*/
function useShortcut(props) {
if (props === void 0) {
props = {};
}
var _props = props,
_props$timeout = _props.timeout,
timeout = _props$timeout === void 0 ? 300 : _props$timeout,
_props$preventDefault = _props.preventDefault,
preventDefault = _props$preventDefault === void 0 ? function () {
return true;
} : _props$preventDefault;
var _React$useState = React__namespace.useState([]),
keys = _React$useState[0],
setKeys = _React$useState[1];
var timeoutRef = React__namespace.useRef();
var flush = function flush() {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
};
var clearKeysAfterDelay = function clearKeysAfterDelay() {
flush();
timeoutRef.current = setTimeout(function () {
setKeys([]);
timeoutRef.current = null;
}, timeout);
};
React__namespace.useEffect(function () {
return flush;
}, []);
function onKeyDown(fn) {
return function (event) {
if (event.key === "Backspace") {
var keysCopy = [].concat(keys);
keysCopy.pop();
setKeys(keysCopy);
return;
}
if (isPrintableCharacter(event)) {
var _keysCopy = keys.concat(event.key);
if (preventDefault(event)) {
event.preventDefault();
event.stopPropagation();
}
setKeys(_keysCopy);
fn(_keysCopy.join(""));
clearKeysAfterDelay();
}
};
}
return onKeyDown;
}
/**
* React hook that provides a declarative `setTimeout`
*
* @param callback the callback to run after specified delay
* @param delay the delay (in ms)
*/
function useTimeout(callback, delay) {
var fn = useCallbackRef(callback);
React__namespace.useEffect(function () {
if (delay == null) return undefined;
var timeoutId = null;
timeoutId = window.setTimeout(function () {
fn();
}, delay);
return function () {
if (timeoutId) {
window.clearTimeout(timeoutId);
}
};
}, [delay, fn]);
}
function useWhyDidYouUpdate(name, props) {
var previousProps = React__namespace.useRef();
React__namespace.useEffect(function () {
if (previousProps.current) {
var allKeys = Object.keys(_extends({}, previousProps.current, props));
var changesObj = {};
allKeys.forEach(function (key) {
if (previousProps.current[key] !== props[key]) {
changesObj[key] = {
from: previousProps.current[key],
to: props[key]
};
}
});
if (Object.keys(changesObj).length) {
console.log("[why-did-you-update]", name, changesObj);
}
}
previousProps.current = props;
});
}
exports.IdProvider = IdProvider;
exports.assignRef = assignRef;
exports.useBoolean = useBoolean;
exports.useCallbackRef = useCallbackRef;
exports.useClipboard = useClipboard;
exports.useConst = useConst;
exports.useControllableProp = useControllableProp;
exports.useControllableState = useControllableState;
exports.useDimensions = useDimensions;
exports.useDisclosure = useDisclosure;
exports.useEventCallback = useEventCallback;
exports.useEventListener = useEventListener;
exports.useEventListenerMap = useEventListenerMap;
exports.useFocusEffect = useFocusEffect;
exports.useFocusOnHide = useFocusOnHide;
exports.useFocusOnPointerDown = useFocusOnPointerDown;
exports.useFocusOnShow = useFocusOnShow;
exports.useForceUpdate = useForceUpdate;
exports.useId = useId;
exports.useIds = useIds;
exports.useInterval = useInterval;
exports.useLatestRef = useLatestRef;
exports.useMergeRefs = useMergeRefs;
exports.useMouseDownRef = useMouseDownRef;
exports.useOptionalPart = useOptionalPart;
exports.useOutsideClick = useOutsideClick;
exports.usePanGesture = usePanGesture;
exports.usePointerEvent = usePointerEvent;
exports.usePrevious = usePrevious;
exports.useSafeLayoutEffect = useSafeLayoutEffect;
exports.useShortcut = useShortcut;
exports.useTimeout = useTimeout;
exports.useUnmountEffect = useUnmountEffect;
exports.useUpdateEffect = useUpdateEffect;
exports.useWhyDidYouUpdate = useWhyDidYouUpdate;
;