@committed/hooks
Advanced tools
Comparing version 0.6.1 to 0.6.2
@@ -1,828 +0,2 @@ | ||
import React, { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from 'react'; | ||
/** | ||
* Utility hook for boolean state | ||
* | ||
* returns the value, an object containing function for toggle, setTrue and setFalse. | ||
* | ||
* Use with caution, attaching to buttons can cause unintended consequences from double clicks. | ||
* @params startState (optional) starting value | ||
*/ | ||
function useBoolean(startState) { | ||
if (startState === void 0) { | ||
startState = false; | ||
} | ||
var _useState = useState(startState), | ||
value = _useState[0], | ||
setValue = _useState[1]; | ||
var functions = React.useMemo(function () { | ||
return { | ||
toggle: function toggle() { | ||
return setValue(function (state) { | ||
return !state; | ||
}); | ||
}, | ||
setTrue: function setTrue() { | ||
return setValue(true); | ||
}, | ||
setFalse: function setFalse() { | ||
return setValue(false); | ||
} | ||
}; | ||
}, [setValue]); | ||
return [value, functions]; | ||
} | ||
/** type guard to check if value or function */ | ||
function isValue(arg) { | ||
return typeof arg !== 'function'; | ||
} | ||
/** no operation */ | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
function noop() {} | ||
/** | ||
* useControllableState hook for when the state may be controlled or uncontrolled. | ||
* | ||
* Returns as the standard useState hook, but has additional props of a controlled value and a controlled change handler. | ||
* Set these using the components incoming props for the state, if defined they will be used, if not you get the standard useState behaviour. | ||
* | ||
* @param {T | undefined} value The controlled value (of type T) or undefined for an uncontrolled value | ||
* @param {React.Dispatch<React.SetStateAction<T>> | undefined} setValue The dispatch handler for state changes or undefined for when an uncontrolled value, ignored if uncontrolled | ||
* @param {T | (() => T | undefined) | undefined} initialState The initial state value, or state initializer for when uncontrolled, ignored if controlled | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function useControllableState(value, setValue, initialState) { | ||
var _useRef = useRef(value !== undefined), | ||
wasControlled = _useRef.current; | ||
var isControlled = value !== undefined; | ||
var _useState = useState(function () { | ||
if (value === undefined) { | ||
if (initialState !== undefined) { | ||
return isValue(initialState) ? initialState : initialState(); | ||
} | ||
} | ||
return undefined; | ||
}), | ||
uncontrolled = _useState[0], | ||
setUncontrolled = _useState[1]; | ||
var effect = noop; | ||
if (process.env.NODE_ENV !== 'production') { | ||
effect = function effect() { | ||
if (wasControlled !== isControlled) { | ||
console.warn('Components should not switch from uncontrolled to controlled (or vice versa)'); | ||
} | ||
}; | ||
} | ||
useEffect(effect, [effect, isControlled]); | ||
return [wasControlled ? value : uncontrolled, wasControlled ? setValue : setUncontrolled]; | ||
} | ||
/** | ||
* Debounce the update to a value. | ||
* | ||
* The returned value will only update when the useDebounce hook has not been called for the full delay specified. | ||
* | ||
* A set function is also returned so the value can be forced or flushed, say on dismount. | ||
* | ||
* Adapted from <https://usehooks.com/>. | ||
* | ||
* @param {T} value The value to debounce updates for | ||
* @param { number | null} delay The time to delay updates by (ms) | ||
*/ | ||
function useDebounce(value, delay) { | ||
var _useState = useState(value), | ||
debouncedValue = _useState[0], | ||
setDebouncedValue = _useState[1]; | ||
useEffect(function () { | ||
if (delay !== null) { | ||
var handler = setTimeout(function () { | ||
setDebouncedValue(value); | ||
}, delay); | ||
return function () { | ||
clearTimeout(handler); | ||
}; | ||
} else { | ||
setDebouncedValue(value); | ||
return; | ||
} | ||
}, [value, delay]); | ||
var flush = function flush() { | ||
return setDebouncedValue(value); | ||
}; | ||
return [debouncedValue, flush]; | ||
} | ||
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); | ||
} | ||
function changed(previous, current) { | ||
var allKeys = Object.keys(_extends({}, previous, current)); | ||
var changed = {}; | ||
allKeys.forEach(function (key) { | ||
if (previous["" + key] !== current["" + key]) { | ||
changed["" + key] = { | ||
from: previous["" + key], | ||
to: current["" + key] | ||
}; | ||
} | ||
}); | ||
return changed; | ||
} | ||
/** | ||
* useDebug hook will log props and state changes to help identify the cause and frequency of component updates, when not in `production`. | ||
* | ||
* @param name the name for this component instance | ||
* @param props the props for the component | ||
* @param state the state for the component | ||
*/ | ||
function useDebug(name, props, state) { | ||
if (props === void 0) { | ||
props = {}; | ||
} | ||
if (state === void 0) { | ||
state = {}; | ||
} | ||
var prevProps = useRef(props); | ||
var prevState = useRef(state); | ||
useEffect(function () { | ||
if (process.env.NODE_ENV === 'production') { | ||
return; | ||
} | ||
var changedProps = changed(prevProps.current, props); | ||
var changedState = changed(prevState.current, state); | ||
if (Object.keys(_extends({}, changedProps, changedState)).length) { | ||
console.log(name + " updated:", 'props', changedProps, 'state', changedState); | ||
} | ||
prevProps.current = props; | ||
prevState.current = state; | ||
}); | ||
} | ||
/** | ||
* useEventListener hook adds an event listener to the given event type and calls the handler when fired. | ||
* The listener is added to the `window` by default or the target element if provided using a ref object. | ||
* It is removed automatically on unmounting. | ||
* | ||
* For event types reference see <https://developer.mozilla.org/en-US/docs/Web/Events>. | ||
* | ||
* (Derived from <https://usehooks-typescript.com/use-event-listener>) | ||
* | ||
* @param eventName the name of the event to listen to | ||
* @param handler the callback function to call on the event firing | ||
* @param element (optional) reference for the element to add the listener to | ||
* @param development (optional) mark true to apply only when not in `production` | ||
*/ | ||
function useEventListener(eventName, handler, element, development) { | ||
var savedHandler = useRef(); | ||
useEffect(function () { | ||
savedHandler.current = handler; | ||
}, [handler]); | ||
useEffect(function () { | ||
var _element$current; | ||
if (development === true && process.env.NODE_ENV === 'production') { | ||
return; | ||
} | ||
function eventListener(event) { | ||
var current = savedHandler.current; | ||
if (current != null) current(event); | ||
} | ||
var targetElement = (_element$current = element == null ? void 0 : element.current) != null ? _element$current : window; | ||
targetElement.addEventListener(eventName, eventListener); | ||
return function () { | ||
targetElement.removeEventListener(eventName, eventListener); | ||
}; // False positive request for E and T | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [eventName, element]); | ||
} | ||
var ImageTypes = { | ||
gif: 'image/gif', | ||
ico: 'image/x-icon', | ||
jpeg: 'image/jpeg', | ||
jpg: 'image/jpeg', | ||
png: 'image/png', | ||
svg: 'image/svg+xml' | ||
}; | ||
function getFavIcon(document) { | ||
var link = document.querySelector("link[rel*='icon']"); | ||
if (link !== null) { | ||
return { | ||
type: link.getAttribute('type'), | ||
href: link.getAttribute('href') | ||
}; | ||
} else { | ||
return null; | ||
} | ||
} | ||
function setFavIcon(document, data) { | ||
if (data === null || data.href === null || data.type === null) { | ||
var _document$querySelect; | ||
(_document$querySelect = document.querySelector("link[rel*='icon']")) == null ? void 0 : _document$querySelect.remove(); | ||
} else { | ||
var _document$querySelect2; | ||
var link = (_document$querySelect2 = document.querySelector("link[rel*='icon']")) != null ? _document$querySelect2 : document.createElement('link'); | ||
link.type = data.type; | ||
link.href = data.href; | ||
link.rel = 'shortcut icon'; | ||
document.getElementsByTagName('head')[0].appendChild(link); | ||
} | ||
} | ||
function isImageType(input) { | ||
return input !== undefined && Object.keys(ImageTypes).includes(input); | ||
} | ||
var DEFAULT_OPTIONS = { | ||
retain: false | ||
}; | ||
/** | ||
* useFavicon changes (or creates) the favicon for the given href. | ||
* | ||
* @param href The url of the favicon | ||
*/ | ||
function useFavicon(href, options) { | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var originalRef = useRef(getFavIcon(document)); | ||
var _options = options, | ||
_options$retain = _options.retain, | ||
retain = _options$retain === void 0 ? DEFAULT_OPTIONS.retain : _options$retain; | ||
useLayoutEffect(function () { | ||
originalRef.current = getFavIcon(document); | ||
if (!retain) { | ||
return function () { | ||
setFavIcon(document, originalRef.current); | ||
}; | ||
} else { | ||
return; | ||
} | ||
}, [retain]); | ||
useLayoutEffect(function () { | ||
var imageType = href.toLowerCase().split('.').pop(); | ||
if (isImageType(imageType)) { | ||
// eslint-disable-next-line security/detect-object-injection | ||
var type = ImageTypes[imageType]; | ||
setFavIcon(document, { | ||
type: type, | ||
href: href | ||
}); | ||
} else { | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.warn("Unrecognised image type href: " + href); | ||
} | ||
} | ||
}, [href]); | ||
} | ||
/** | ||
* useHover tracks the hovered state of the given element. | ||
* | ||
* @param element reference to track hover on | ||
*/ | ||
function useHover(element) { | ||
var _useState = useState(false), | ||
hovered = _useState[0], | ||
setHovered = _useState[1]; | ||
var handleMouseOver = function handleMouseOver() { | ||
return setHovered(true); | ||
}; | ||
var handleMouseOut = function handleMouseOut() { | ||
return setHovered(false); | ||
}; | ||
useEventListener('mouseover', handleMouseOver, element); | ||
useEventListener('mouseout', handleMouseOut, element); | ||
return [hovered]; | ||
} | ||
/** | ||
* Call the callback at the given rate. | ||
* | ||
* Based on https://overreacted.io/making-setinterval-declarative-with-react-hooks/ | ||
* | ||
* @param callback the callback | ||
* @param delay the time between calls (ms) | ||
*/ | ||
function useInterval(callback, delay) { | ||
var savedCallback = useRef(); // Remember the latest callback. | ||
useEffect(function () { | ||
savedCallback.current = callback; | ||
}, [callback]); // Set up the interval. | ||
useEffect(function () { | ||
function tick() { | ||
var current = savedCallback.current; | ||
if (current != null) current(); | ||
} | ||
if (delay !== null) { | ||
var id = setInterval(tick, delay); | ||
return function () { | ||
return clearInterval(id); | ||
}; | ||
} else { | ||
return; | ||
} | ||
}, [delay]); | ||
} | ||
var KEYBOARD_MODIFIERS = ['Alt', 'Control', 'Meta', 'OS', 'Shift']; | ||
var MODIFIER_ALIASES = { | ||
alt: 'Alt', | ||
ctrl: 'Control', | ||
control: 'Control', | ||
shift: 'Shift', | ||
meta: 'Meta', | ||
option: 'Alt' | ||
}; | ||
var KEY_ALIASES = { | ||
plus: '+', | ||
up: 'ArrowUp', | ||
down: 'ArrowDown', | ||
left: 'ArrowLeft', | ||
right: 'ArrowRight', | ||
space: ' ', | ||
esc: 'Escape' | ||
}; | ||
function isKeyFilter(input) { | ||
return typeof input === 'function'; | ||
} | ||
function isKeyArray(input) { | ||
return Array.isArray(input); | ||
} | ||
var DEFAULT_KEYBOARD_FILTER_OPTIONS = { | ||
ignoreKey: false, | ||
ignoreRepeat: false | ||
}; | ||
function eventLength(e) { | ||
var length = 0; | ||
KEYBOARD_MODIFIERS.forEach(function (modifier) { | ||
if (e.getModifierState(modifier)) { | ||
length++; | ||
} | ||
}); | ||
if (!KEYBOARD_MODIFIERS.includes(e.key)) { | ||
length++; | ||
} | ||
return length; | ||
} | ||
function createKeysFilter(keys, options) { | ||
// Convenience code for any key | ||
if (keys === '') { | ||
return function () { | ||
return true; | ||
}; | ||
} | ||
return function (e) { | ||
if (options.ignoreRepeat && e.repeat) { | ||
return false; | ||
} | ||
var splitKeys = keys.split('+').filter(function (key) { | ||
return key.length > 0; | ||
}); | ||
if (splitKeys.length !== eventLength(e)) { | ||
return false; | ||
} | ||
return splitKeys.every(function (key) { | ||
var _MODIFIER_ALIASES$key; | ||
var modifier = (_MODIFIER_ALIASES$key = MODIFIER_ALIASES[key.toLowerCase()]) != null ? _MODIFIER_ALIASES$key : key; | ||
if (e.getModifierState(modifier)) { | ||
return true; | ||
} | ||
if (e.key === key || e.key === KEY_ALIASES[key.toLowerCase()]) { | ||
return true; | ||
} | ||
if (e.key.length > 1 && e.key.toLowerCase() === key.toLowerCase()) { | ||
return true; | ||
} | ||
if (e.code === key || !options.ignoreKey && e.code === 'Key' + key.toUpperCase()) { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}; | ||
} | ||
/** | ||
* useKeyboard hook to add a callback to be called on the use of the keyboard under specified circumstances. | ||
* | ||
* | ||
* @param keys {Keys} The definition of the key filter. | ||
* The basic definition is a string filter separated with the `+` e.g. `'a'` or `'ctrl+a'` | ||
* An array can be provided with alternatives, so matching any filter in the array will call the handler. | ||
* Finally, you can provide your own function `(event: KeyboardEvent) => boolean` | ||
* @param handler {((event: KeyboardEvent) => void) | null} the callback function to call on a key event firing and passing the filter | ||
* @param options options options object | ||
* @param options.element {RefObejct} provide a ref for the element to bind to (defaults to `window`) | ||
* @param options.event {keyup | keydown} a ref for the element to bind to (defaults to `keydown`) | ||
* @param options.ignoreKey {boolean} set `true` to turn off the `KeyCode` test no other match (defaults to `false`) | ||
* @param options.ignoreRepeat {boolean} set `true` to ignore repeat events (defaults to `false`) | ||
* @param options.development {boolean} set `true` to remove in production, using `process.env.NODE_ENV` (defaults to `false`) | ||
*/ | ||
function useKeyboard(keys, handler, options) { | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var savedHandler = useRef(); | ||
useEffect(function () { | ||
savedHandler.current = handler; | ||
}, [handler]); | ||
var _options = options, | ||
element = _options.element, | ||
_options$event = _options.event, | ||
event = _options$event === void 0 ? 'keydown' : _options$event, | ||
development = _options.development; | ||
var _options2 = options, | ||
_options2$ignoreKey = _options2.ignoreKey, | ||
ignoreKey = _options2$ignoreKey === void 0 ? DEFAULT_KEYBOARD_FILTER_OPTIONS.ignoreKey : _options2$ignoreKey, | ||
_options2$ignoreRepea = _options2.ignoreRepeat, | ||
ignoreRepeat = _options2$ignoreRepea === void 0 ? DEFAULT_KEYBOARD_FILTER_OPTIONS.ignoreRepeat : _options2$ignoreRepea; | ||
var keyFilter = useMemo(function () { | ||
var filterOptions = { | ||
ignoreKey: ignoreKey, | ||
ignoreRepeat: ignoreRepeat | ||
}; | ||
if (isKeyFilter(keys)) { | ||
return keys; | ||
} | ||
if (isKeyArray(keys)) { | ||
return function (e) { | ||
return keys.map(function (key) { | ||
return createKeysFilter(key, filterOptions); | ||
}).some(function (filter) { | ||
return filter(e); | ||
}); | ||
}; | ||
} else { | ||
return createKeysFilter(keys, filterOptions); | ||
} | ||
}, [keys, ignoreKey, ignoreRepeat]); | ||
var keyHandler = useCallback(function (e) { | ||
if (savedHandler.current == null) { | ||
return; | ||
} | ||
if (keyFilter(e)) { | ||
savedHandler.current(e); | ||
} | ||
}, [keyFilter]); | ||
useEventListener(event, keyHandler, element, development); | ||
} | ||
/** | ||
* Default function type guard | ||
* @param defaultValue | ||
*/ | ||
function isFunction(defaultValue) { | ||
return typeof defaultValue === 'function'; | ||
} | ||
/** | ||
* useLocalState hook behaves like `React.useState`, returning the state and a function to set the value. | ||
* In addition, the value is put in local storage against the given key and is persisted through page refresh. | ||
*/ | ||
function useLocalState(key, defaultValue, _temp) { | ||
var _ref = _temp === void 0 ? { | ||
serialize: JSON.stringify, | ||
deserialize: JSON.parse | ||
} : _temp, | ||
serialize = _ref.serialize, | ||
deserialize = _ref.deserialize; | ||
var _React$useState = React.useState(function () { | ||
var valueInLocalStorage = window.localStorage.getItem(key); | ||
if (valueInLocalStorage != null) { | ||
try { | ||
return deserialize(valueInLocalStorage); | ||
} catch (error) { | ||
window.localStorage.removeItem(key); | ||
} | ||
} | ||
if (isFunction(defaultValue)) { | ||
return defaultValue(); | ||
} else { | ||
return defaultValue; | ||
} | ||
}), | ||
state = _React$useState[0], | ||
setState = _React$useState[1]; | ||
var prevKeyRef = React.useRef(key); | ||
React.useEffect(function () { | ||
var prevKey = prevKeyRef.current; | ||
if (prevKey !== key) { | ||
window.localStorage.removeItem(prevKey); | ||
} | ||
prevKeyRef.current = key; | ||
if (state === undefined || state === null) { | ||
window.localStorage.removeItem(key); | ||
} else { | ||
window.localStorage.setItem(key, serialize(state)); | ||
} | ||
}, [key, state, serialize]); | ||
var clear = React.useCallback(function () { | ||
window.localStorage.removeItem(key); | ||
}, [key]); | ||
return [state, setState, clear]; | ||
} | ||
/** | ||
* Call the callback with a fixed delay (between completions) | ||
* | ||
* The first call will be made immediately. | ||
* | ||
* Based on https://www.aaron-powell.com/posts/2019-09-23-recursive-settimeout-with-react-hooks/ | ||
* | ||
* | ||
* @param callback the callback | ||
* @param delay the time between calls (ms) | ||
*/ | ||
function usePoll(callback, delay) { | ||
var savedCallback = useRef(); // Remember the latest callback. | ||
useEffect(function () { | ||
savedCallback.current = callback; | ||
}, [callback]); | ||
useEffect(function () { | ||
var id = null; | ||
function call() { | ||
var current = savedCallback.current; | ||
var ret = undefined; | ||
if (current != null) { | ||
ret = current(); | ||
} | ||
return ret; | ||
} | ||
function reschedule() { | ||
if (delay !== null) { | ||
id = setTimeout(tick, delay); | ||
} | ||
} | ||
function tick() { | ||
var ret = call(); | ||
if (ret instanceof Promise) { | ||
void ret.then(reschedule); | ||
} else { | ||
reschedule(); | ||
} | ||
} | ||
tick(); | ||
return function () { | ||
id && clearTimeout(id); | ||
id = null; | ||
}; | ||
}, [delay]); | ||
} | ||
/** | ||
* Call the callback after a period of delay. | ||
* | ||
* Based on https://overreacted.io/making-setinterval-declarative-with-react-hooks/ | ||
* | ||
* @param callback the callback | ||
* @param delay the timeout before the call (in ms) | ||
*/ | ||
function useTimeout(callback, delay) { | ||
var savedCallback = useRef(); | ||
useEffect(function () { | ||
savedCallback.current = callback; | ||
}, [callback]); | ||
useEffect(function () { | ||
function tick() { | ||
var current = savedCallback.current; | ||
if (current != null) current(); | ||
} | ||
if (delay !== null) { | ||
var id = setTimeout(tick, delay); | ||
return function () { | ||
return clearTimeout(id); | ||
}; | ||
} else { | ||
return; | ||
} | ||
}, [delay]); | ||
} | ||
var DEFAULT_OPTIONS$1 = { | ||
append: false, | ||
retain: false, | ||
separator: '' | ||
}; | ||
/** | ||
* useTitle hook allows you to control the document title from your component. | ||
* | ||
* @param title The string to set the title to or to be appended. | ||
* @param options The options to configure the useTitle | ||
* @param options.append Set true to append the given string to the current title | ||
* @param options.retain Set true to keep the title even after the component has unmounted | ||
* @param options.separator The separator to use when appending | ||
*/ | ||
function useTitle(title, options) { | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var _options = options, | ||
_options$append = _options.append, | ||
append = _options$append === void 0 ? DEFAULT_OPTIONS$1.append : _options$append, | ||
_options$retain = _options.retain, | ||
retain = _options$retain === void 0 ? DEFAULT_OPTIONS$1.retain : _options$retain, | ||
_options$separator = _options.separator, | ||
separator = _options$separator === void 0 ? DEFAULT_OPTIONS$1.separator : _options$separator; | ||
var titleRef = useRef(document.title); | ||
useLayoutEffect(function () { | ||
titleRef.current = document.title; | ||
if (!retain) { | ||
return function () { | ||
document.title = titleRef.current; | ||
}; | ||
} else { | ||
return; | ||
} | ||
}, [retain]); | ||
useLayoutEffect(function () { | ||
if (append) { | ||
document.title = titleRef.current + separator + title; | ||
} else { | ||
document.title = title; | ||
} | ||
}, [title, separator, append]); | ||
} | ||
function isInitializer(candidate) { | ||
return typeof candidate === 'function'; | ||
} | ||
function isModifier(candidate) { | ||
return typeof candidate === 'function'; | ||
} | ||
/** | ||
* useTrackedState hook provides the standard `[value, setValue]` array with an additional object providing | ||
* `undo` and `redo` functions with convenience `boolean`s for `canUndo` and `canRedo`. | ||
* | ||
* @param initialState (optional) starting state or function to provide starting state | ||
*/ | ||
function useTrackedState(initialState) { | ||
var _useState = useState({ | ||
current: isInitializer(initialState) ? initialState() : initialState, | ||
undoStack: [], | ||
redoStack: [] | ||
}), | ||
tracked = _useState[0], | ||
setTracked = _useState[1]; | ||
var undo = useCallback(function () { | ||
setTracked(function (currentTracked) { | ||
if (currentTracked.undoStack.length === 0) { | ||
return currentTracked; | ||
} | ||
var current = currentTracked.current, | ||
undoStack = currentTracked.undoStack, | ||
redoStack = currentTracked.redoStack; | ||
return { | ||
current: undoStack[undoStack.length - 1], | ||
undoStack: undoStack.slice(0, undoStack.length - 1), | ||
redoStack: [].concat(redoStack, [current]) | ||
}; | ||
}); | ||
}, []); | ||
var redo = useCallback(function () { | ||
setTracked(function (currentTracked) { | ||
if (currentTracked.redoStack.length === 0) { | ||
return currentTracked; | ||
} | ||
var current = currentTracked.current, | ||
undoStack = currentTracked.undoStack, | ||
redoStack = currentTracked.redoStack; | ||
return { | ||
current: redoStack[redoStack.length - 1], | ||
undoStack: [].concat(undoStack, [current]), | ||
redoStack: redoStack.slice(0, redoStack.length - 1) | ||
}; | ||
}); | ||
}, []); | ||
var setValue = useCallback(function (setState) { | ||
setTracked(function (_ref) { | ||
var current = _ref.current, | ||
undoStack = _ref.undoStack; | ||
return { | ||
current: isModifier(setState) ? setState(current) : setState, | ||
undoStack: [].concat(undoStack, [current]), | ||
redoStack: [] | ||
}; | ||
}); // Incorrectly suggests adding T | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
return [tracked.current, setValue, { | ||
undo: undo, | ||
redo: redo, | ||
canUndo: tracked.undoStack.length > 0, | ||
canRedo: tracked.redoStack.length > 0 | ||
}]; | ||
} | ||
export { KEYBOARD_MODIFIERS, useBoolean, useControllableState, useDebounce, useDebug, useEventListener, useFavicon, useHover, useInterval, useKeyboard, useLocalState, usePoll, useTimeout, useTitle, useTrackedState }; | ||
import n,{useState as t,useRef as e,useEffect as r,useLayoutEffect as o,useMemo as u,useCallback as c}from"react";function i(e){void 0===e&&(e=!1);var r=t(e),o=r[1];return[r[0],n.useMemo(function(){return{toggle:function(){return o(function(n){return!n})},setTrue:function(){return o(!0)},setFalse:function(){return o(!1)}}},[o])]}function a(){}function l(n,o,u){var c=e(void 0!==n).current,i=void 0!==n,l=t(function(){if(void 0===n&&void 0!==u)return"function"!=typeof u?u:u()}),f=l[0],v=l[1],d=a;return"production"!==process.env.NODE_ENV&&(d=function(){c!==i&&console.warn("Components should not switch from uncontrolled to controlled (or vice versa)")}),r(d,[d,i]),[c?n:f,c?o:v]}function f(n,e){var o=t(n),u=o[0],c=o[1];return r(function(){if(null!==e){var t=setTimeout(function(){c(n)},e);return function(){clearTimeout(t)}}c(n)},[n,e]),[u,function(){return c(n)}]}function v(){return v=Object.assign||function(n){for(var t=1;t<arguments.length;t++){var e=arguments[t];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r])}return n},v.apply(this,arguments)}function d(n,t){var e=Object.keys(v({},n,t)),r={};return e.forEach(function(e){n[""+e]!==t[""+e]&&(r[""+e]={from:n[""+e],to:t[""+e]})}),r}function s(n,t,o){void 0===t&&(t={}),void 0===o&&(o={});var u=e(t),c=e(o);r(function(){if("production"!==process.env.NODE_ENV){var e=d(u.current,t),r=d(c.current,o);Object.keys(v({},e,r)).length&&console.log(n+" updated:","props",e,"state",r),u.current=t,c.current=o}})}function p(n,t,o,u){var c=e();r(function(){c.current=t},[t]),r(function(){var t;if(!0!==u||"production"!==process.env.NODE_ENV){var e=null!=(t=null==o?void 0:o.current)?t:window;return e.addEventListener(n,r),function(){e.removeEventListener(n,r)}}function r(n){var t=c.current;null!=t&&t(n)}},[n,o])}var g={gif:"image/gif",ico:"image/x-icon",jpeg:"image/jpeg",jpg:"image/jpeg",png:"image/png",svg:"image/svg+xml"};function m(n){var t=n.querySelector("link[rel*='icon']");return null!==t?{type:t.getAttribute("type"),href:t.getAttribute("href")}:null}function y(n,t){if(null===t||null===t.href||null===t.type){var e;null==(e=n.querySelector("link[rel*='icon']"))||e.remove()}else{var r,o=null!=(r=n.querySelector("link[rel*='icon']"))?r:n.createElement("link");o.type=t.type,o.href=t.href,o.rel="shortcut icon",n.getElementsByTagName("head")[0].appendChild(o)}}function h(n,t){void 0===t&&(t={});var r=e(m(document)),u=t.retain,c=void 0!==u&&u;o(function(){return r.current=m(document),c?void 0:function(){y(document,r.current)}},[c]),o(function(){var t,e=n.toLowerCase().split(".").pop();void 0!==(t=e)&&Object.keys(g).includes(t)?y(document,{type:g[e],href:n}):"production"!==process.env.NODE_ENV&&console.warn("Unrecognised image type href: "+n)},[n])}function S(n){var e=t(!1),r=e[0],o=e[1];return p("mouseover",function(){return o(!0)},n),p("mouseout",function(){return o(!1)},n),[r]}function k(n,t){var o=e();r(function(){o.current=n},[n]),r(function(){if(null!==t){var n=setInterval(function(){var n=o.current;null!=n&&n()},t);return function(){return clearInterval(n)}}},[t])}var w=["Alt","Control","Meta","OS","Shift"],E={alt:"Alt",ctrl:"Control",control:"Control",shift:"Shift",meta:"Meta",option:"Alt"},O={plus:"+",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",space:" ",esc:"Escape"};function C(n,t){return""===n?function(){return!0}:function(e){if(t.ignoreRepeat&&e.repeat)return!1;var r=n.split("+").filter(function(n){return n.length>0});return r.length===function(n){var t=0;return w.forEach(function(e){n.getModifierState(e)&&t++}),w.includes(n.key)||t++,t}(e)&&r.every(function(n){var r,o=null!=(r=E[n.toLowerCase()])?r:n;return!!e.getModifierState(o)||e.key===n||e.key===O[n.toLowerCase()]||e.key.length>1&&e.key.toLowerCase()===n.toLowerCase()||e.code===n||!t.ignoreKey&&e.code==="Key"+n.toUpperCase()})}}function A(n,t,o){void 0===o&&(o={});var i=e();r(function(){i.current=t},[t]);var a=o.element,l=o.event,f=void 0===l?"keydown":l,v=o.development,d=o.ignoreKey,s=void 0!==d&&d,g=o.ignoreRepeat,m=void 0!==g&&g,y=u(function(){var t={ignoreKey:s,ignoreRepeat:m};return"function"==typeof n?n:Array.isArray(n)?function(e){return n.map(function(n){return C(n,t)}).some(function(n){return n(e)})}:C(n,t)},[n,s,m]);p(f,c(function(n){null!=i.current&&y(n)&&i.current(n)},[y]),a,v)}function N(t,e,r){var o=void 0===r?{serialize:JSON.stringify,deserialize:JSON.parse}:r,u=o.serialize,c=o.deserialize,i=n.useState(function(){var n=window.localStorage.getItem(t);if(null!=n)try{return c(n)}catch(n){window.localStorage.removeItem(t)}return function(n){return"function"==typeof n}(e)?e():e}),a=i[0],l=i[1],f=n.useRef(t);n.useEffect(function(){var n=f.current;n!==t&&window.localStorage.removeItem(n),f.current=t,null==a?window.localStorage.removeItem(t):window.localStorage.setItem(t,u(a))},[t,a,u]);var v=n.useCallback(function(){window.localStorage.removeItem(t)},[t]);return[a,l,v]}function j(n){void 0===n&&(n=!1);var t=i(n),e=t[1];return[t[0],e.setTrue,e.setFalse]}function T(n,t){var o=e();r(function(){o.current=n},[n]),r(function(){var n=null;function e(){null!==t&&(n=setTimeout(r,t))}function r(){var n=function(){var n=o.current,t=void 0;return null!=n&&(t=n()),t}();n instanceof Promise?n.then(e):e()}return r(),function(){n&&clearTimeout(n),n=null}},[t])}function b(n,t){var o=e();r(function(){o.current=n},[n]),r(function(){if(null!==t){var n=setTimeout(function(){var n=o.current;null!=n&&n()},t);return function(){return clearTimeout(n)}}},[t])}function I(n,t){void 0===t&&(t={});var r=t.append,u=void 0!==r&&r,c=t.retain,i=void 0!==c&&c,a=t.separator,l=void 0===a?"":a,f=e(document.title);o(function(){return f.current=document.title,i?void 0:function(){document.title=f.current}},[i]),o(function(){document.title=u?f.current+l+n:n},[n,l,u])}function L(n){return"function"==typeof n}function R(n){var e,r=t({current:(e=n,"function"==typeof e?n():n),undoStack:[],redoStack:[]}),o=r[0],u=r[1],i=c(function(){u(function(n){if(0===n.undoStack.length)return n;var t=n.current,e=n.undoStack,r=n.redoStack;return{current:e[e.length-1],undoStack:e.slice(0,e.length-1),redoStack:[].concat(r,[t])}})},[]),a=c(function(){u(function(n){if(0===n.redoStack.length)return n;var t=n.redoStack;return{current:t[t.length-1],undoStack:[].concat(n.undoStack,[n.current]),redoStack:t.slice(0,t.length-1)}})},[]),l=c(function(n){u(function(t){var e=t.current,r=t.undoStack;return{current:L(n)?n(e):n,undoStack:[].concat(r,[e]),redoStack:[]}})},[]);return[o.current,l,{undo:i,redo:a,canUndo:o.undoStack.length>0,canRedo:o.redoStack.length>0}]}export{w as KEYBOARD_MODIFIERS,i as useBoolean,l as useControllableState,f as useDebounce,s as useDebug,p as useEventListener,h as useFavicon,S as useHover,k as useInterval,A as useKeyboard,N as useLocalState,j as useModal,T as usePoll,b as useTimeout,I as useTitle,R as useTrackedState}; | ||
//# sourceMappingURL=hooks.esm.js.map |
@@ -11,2 +11,3 @@ export * from './useBoolean'; | ||
export * from './useLocalState'; | ||
export * from './useModal'; | ||
export * from './usePoll'; | ||
@@ -13,0 +14,0 @@ export * from './useTimeout'; |
@@ -9,6 +9,9 @@ /** | ||
*/ | ||
export declare function useBoolean(startState?: boolean): [boolean, { | ||
toggle: () => void; | ||
setTrue: () => void; | ||
setFalse: () => void; | ||
}]; | ||
export declare function useBoolean(startState?: boolean): [ | ||
boolean, | ||
{ | ||
toggle: () => void; | ||
setTrue: () => void; | ||
setFalse: () => void; | ||
} | ||
]; |
@@ -6,8 +6,12 @@ /// <reference types="react" /> | ||
*/ | ||
export declare function useTrackedState<T = undefined>(): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>, { | ||
undo: () => void; | ||
redo: () => void; | ||
canUndo: boolean; | ||
canRedo: boolean; | ||
}]; | ||
export declare function useTrackedState<T = undefined>(): [ | ||
T | undefined, | ||
React.Dispatch<React.SetStateAction<T | undefined>>, | ||
{ | ||
undo: () => void; | ||
redo: () => void; | ||
canUndo: boolean; | ||
canRedo: boolean; | ||
} | ||
]; | ||
/** | ||
@@ -19,7 +23,11 @@ * useTrackedState hook provides the standard `[value, setValue]` array with an additional object providing | ||
*/ | ||
export declare function useTrackedState<T>(initialState: T | (() => T)): [T, React.Dispatch<React.SetStateAction<T>>, { | ||
undo: () => void; | ||
redo: () => void; | ||
canUndo: boolean; | ||
canRedo: boolean; | ||
}]; | ||
export declare function useTrackedState<T>(initialState: T | (() => T)): [ | ||
T, | ||
React.Dispatch<React.SetStateAction<T>>, | ||
{ | ||
undo: () => void; | ||
redo: () => void; | ||
canUndo: boolean; | ||
canRedo: boolean; | ||
} | ||
]; |
{ | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"name": "@committed/hooks", | ||
@@ -9,3 +9,6 @@ "description": "Committed hooks library", | ||
"repository": "git://github.com/commitd/hooks.git", | ||
"main": "dist/index.js", | ||
"source": "src/index.ts", | ||
"main": "dist/hooks.js", | ||
"umd:main": "dist/hooks.umd.js", | ||
"module": "dist/hooks.esm.js", | ||
"typings": "dist/index.d.ts", | ||
@@ -24,4 +27,4 @@ "publishConfig": { | ||
"commit": "cz", | ||
"start": "tsdx watch", | ||
"build": "tsdx build", | ||
"start": "microbundle watch", | ||
"build": "yarn clean ; microbundle", | ||
"test": "tsdx test", | ||
@@ -37,3 +40,3 @@ "lint": "tsdx lint", | ||
"generate": "plop --plopfile ./generators/plopfile.js", | ||
"prepare": "husky install && tsdx build", | ||
"postinstall": "husky install", | ||
"semantic-release": "semantic-release" | ||
@@ -54,6 +57,5 @@ }, | ||
}, | ||
"module": "dist/hooks.esm.js", | ||
"size-limit": [ | ||
{ | ||
"path": "dist/hooks.cjs.production.min.js", | ||
"path": "dist/hooks.js", | ||
"limit": "10 KB" | ||
@@ -126,2 +128,3 @@ }, | ||
"jest-sonar-reporter": "^2.0.0", | ||
"microbundle": "^0.15.0", | ||
"plop": "^2.7.4", | ||
@@ -140,6 +143,6 @@ "prettier": "^2.1.2", | ||
"tsdx": "^0.14.1", | ||
"tslib": "^2.3.1", | ||
"typescript": "^4.6.3" | ||
"tslib": "^2.4.0", | ||
"typescript": "^4.6.4" | ||
}, | ||
"dependencies": {} | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
42
194735
45
366
4
1
17
1