Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@committed/hooks

Package Overview
Dependencies
Maintainers
4
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@committed/hooks - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

dist/useBoolean/index.d.ts

489

dist/hooks.cjs.development.js

@@ -11,2 +11,38 @@ 'use strict';

/**
* 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 = React.useState(startState),
value = _useState[0],
setValue = _useState[1];
var functions = React__default.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];
}
/**
* Debounce the update to a value.

@@ -50,3 +86,68 @@ *

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 = React.useRef(props);
var prevState = React.useRef(state);
React.useEffect(function () {
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.

@@ -62,3 +163,3 @@ * The listener is added to the `window` by default or the target element if provided using a ref object.

* @param handler the callback function to call on the event firing
* @param element (optional) reference for the element to add the listener too
* @param element (optional) reference for the element to add the listener to
*/

@@ -88,3 +189,92 @@

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 = React.useRef(getFavIcon(document));
var _options = options,
_options$retain = _options.retain,
retain = _options$retain === void 0 ? DEFAULT_OPTIONS.retain : _options$retain;
React.useLayoutEffect(function () {
originalRef.current = getFavIcon(document);
if (!retain) {
return function () {
setFavIcon(document, originalRef.current);
};
} else {
return;
}
}, [retain]);
React.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 {
{
console.warn("Unrecognised image type href: " + href);
}
}
}, [href]);
}
/**
* useHover tracks the hovered state of the given element.

@@ -146,3 +336,165 @@ *

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`)
*/
function useKeyboard(keys, handler, options) {
if (options === void 0) {
options = {};
}
var savedHandler = React.useRef();
React.useEffect(function () {
savedHandler.current = handler;
}, [handler]);
var _options = options,
element = _options.element,
_options$event = _options.event,
event = _options$event === void 0 ? 'keydown' : _options$event;
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 = React.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 = React.useCallback(function (e) {
if (savedHandler.current == null) {
return;
}
if (keyFilter(e)) {
savedHandler.current(e);
}
}, [keyFilter]);
useEventListener(event, keyHandler, element);
}
/**
* Default function type guard

@@ -298,33 +650,140 @@ * @param defaultValue

var DEFAULT_OPTIONS$1 = {
append: false,
retain: false,
separator: ''
};
/**
* Utility hook for boolean toggle operations
* useTitle hook allows you to control the document title from your component.
*
* <p> Use with caution, attaching to buttons can cause unintended consequences from double clicks.
* <p> returns the value, a toggle function, and the original setValue in case required.
* @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 useToggle(startState) {
if (startState === void 0) {
startState = false;
function useTitle(title, options) {
if (options === void 0) {
options = {};
}
var _useState = React.useState(startState),
value = _useState[0],
setValue = _useState[1];
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 = React.useRef(document.title);
React.useLayoutEffect(function () {
titleRef.current = document.title;
var toggleValue = function toggleValue() {
return setValue(!value);
};
if (!retain) {
return function () {
document.title = titleRef.current;
};
} else {
return;
}
}, [retain]);
React.useLayoutEffect(function () {
if (append) {
document.title = titleRef.current + separator + title;
} else {
document.title = title;
}
}, [title, separator, append]);
}
return [value, toggleValue, setValue];
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 = React.useState({
current: isInitializer(initialState) ? initialState() : initialState,
undoStack: [],
redoStack: []
}),
tracked = _useState[0],
setTracked = _useState[1];
var undo = React.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 = React.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 = React.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
}];
}
exports.KEYBOARD_MODIFIERS = KEYBOARD_MODIFIERS;
exports.useBoolean = useBoolean;
exports.useDebounce = useDebounce;
exports.useDebug = useDebug;
exports.useEventListener = useEventListener;
exports.useFavicon = useFavicon;
exports.useHover = useHover;
exports.useInterval = useInterval;
exports.useKeyboard = useKeyboard;
exports.useLocalState = useLocalState;
exports.usePoll = usePoll;
exports.useTimeout = useTimeout;
exports.useToggle = useToggle;
exports.useTitle = useTitle;
exports.useTrackedState = useTrackedState;
//# sourceMappingURL=hooks.cjs.development.js.map

2

dist/hooks.cjs.production.min.js

@@ -1,2 +0,2 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),n=(e=t)&&"object"==typeof e&&"default"in e?e.default:e;function r(e,n,r){var u=t.useRef();t.useEffect((function(){u.current=n}),[n]),t.useEffect((function(){var t;function n(e){var t=u.current;null!=t&&t(e)}var o=null!=(t=null==r?void 0:r.current)?t:window;return o.addEventListener(e,n),function(){o.removeEventListener(e,n)}}),[e,r])}exports.useDebounce=function(e,n){var r=t.useState(e),u=r[0],o=r[1];return t.useEffect((function(){if(null!==n){var t=setTimeout((function(){o(e)}),n);return function(){clearTimeout(t)}}o(e)}),[e,n]),[u,function(){return o(e)}]},exports.useEventListener=r,exports.useHover=function(e){var n=t.useState(!1),u=n[0],o=n[1];return r("mouseover",(function(){return o(!0)}),e),r("mouseout",(function(){return o(!1)}),e),[u]},exports.useInterval=function(e,n){var r=t.useRef();t.useEffect((function(){r.current=e}),[e]),t.useEffect((function(){if(null!==n){var e=setInterval((function(){var e=r.current;null!=e&&e()}),n);return function(){return clearInterval(e)}}}),[n])},exports.useLocalState=function(e,t,r){var u=void 0===r?{serialize:JSON.stringify,deserialize:JSON.parse}:r,o=u.serialize,c=u.deserialize,i=n.useState((function(){var n=window.localStorage.getItem(e);if(null!=n)try{return c(n)}catch(t){window.localStorage.removeItem(e)}return function(e){return"function"==typeof e}(t)?t():t})),f=i[0],a=i[1],l=n.useRef(e);n.useEffect((function(){var t=l.current;t!==e&&window.localStorage.removeItem(t),l.current=e,null==f?window.localStorage.removeItem(e):window.localStorage.setItem(e,o(f))}),[e,f,o]);var s=n.useCallback((function(){window.localStorage.removeItem(e)}),[e]);return[f,a,s]},exports.usePoll=function(e,n){var r=t.useRef();t.useEffect((function(){r.current=e}),[e]),t.useEffect((function(){var e=null;function t(){null!==n&&(e=setTimeout(u,n))}function u(){var e=function(){var e=r.current,t=void 0;return null!=e&&(t=e()),t}();e instanceof Promise?e.then(t):t()}return u(),function(){e&&clearTimeout(e),e=null}}),[n])},exports.useTimeout=function(e,n){var r=t.useRef();t.useEffect((function(){r.current=e}),[e]),t.useEffect((function(){if(null!==n){var e=setTimeout((function(){var e=r.current;null!=e&&e()}),n);return function(){return clearTimeout(e)}}}),[n])},exports.useToggle=function(e){void 0===e&&(e=!1);var n=t.useState(e),r=n[0],u=n[1];return[r,function(){return u(!r)},u]};
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),n=(e=t)&&"object"==typeof e&&"default"in e?e.default:e;function r(e,n,r){var u=t.useRef();t.useEffect((function(){u.current=n}),[n]),t.useEffect((function(){var t;function n(e){var t=u.current;null!=t&&t(e)}var o=null!=(t=null==r?void 0:r.current)?t:window;return o.addEventListener(e,n),function(){o.removeEventListener(e,n)}}),[e,r])}var u={gif:"image/gif",ico:"image/x-icon",jpeg:"image/jpeg",jpg:"image/jpeg",png:"image/png",svg:"image/svg+xml"};function o(e){var t=e.querySelector("link[rel*='icon']");return null!==t?{type:t.getAttribute("type"),href:t.getAttribute("href")}:null}function c(e,t){if(null===t||null===t.href||null===t.type){var n;null==(n=e.querySelector("link[rel*='icon']"))||n.remove()}else{var r,u=null!=(r=e.querySelector("link[rel*='icon']"))?r:e.createElement("link");u.type=t.type,u.href=t.href,u.rel="shortcut icon",e.getElementsByTagName("head")[0].appendChild(u)}}var i=["Alt","Control","Meta","OS","Shift"],a={alt:"Alt",ctrl:"Control",control:"Control",shift:"Shift",meta:"Meta",option:"Alt"},f={plus:"+",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",space:" ",esc:"Escape"};function l(e,t){return""===e?function(){return!0}:function(n){if(t.ignoreRepeat&&n.repeat)return!1;var r=e.split("+").filter((function(e){return e.length>0}));return r.length===function(e){var t=0;return i.forEach((function(n){e.getModifierState(n)&&t++})),i.includes(e.key)||t++,t}(n)&&r.every((function(e){var r,u=null!=(r=a[e.toLowerCase()])?r:e;return!!n.getModifierState(u)||n.key===e||n.key===f[e.toLowerCase()]||n.key.length>1&&n.key.toLowerCase()===e.toLowerCase()||n.code===e||!t.ignoreKey&&n.code==="Key"+e.toUpperCase()}))}}function s(e){return"function"==typeof e}exports.KEYBOARD_MODIFIERS=i,exports.useBoolean=function(e){void 0===e&&(e=!1);var r=t.useState(e),u=r[1];return[r[0],n.useMemo((function(){return{toggle:function(){return u((function(e){return!e}))},setTrue:function(){return u(!0)},setFalse:function(){return u(!1)}}}),[u])]},exports.useDebounce=function(e,n){var r=t.useState(e),u=r[0],o=r[1];return t.useEffect((function(){if(null!==n){var t=setTimeout((function(){o(e)}),n);return function(){clearTimeout(t)}}o(e)}),[e,n]),[u,function(){return o(e)}]},exports.useDebug=function(e,n,r){void 0===n&&(n={}),void 0===r&&(r={}),t.useRef(n),t.useRef(r),t.useEffect((function(){}))},exports.useEventListener=r,exports.useFavicon=function(e,n){void 0===n&&(n={});var r=t.useRef(o(document)),i=n.retain,a=void 0!==i&&i;t.useLayoutEffect((function(){return r.current=o(document),a?void 0:function(){c(document,r.current)}}),[a]),t.useLayoutEffect((function(){var t,n=e.toLowerCase().split(".").pop();void 0!==(t=n)&&Object.keys(u).includes(t)&&c(document,{type:u[n],href:e})}),[e])},exports.useHover=function(e){var n=t.useState(!1),u=n[0],o=n[1];return r("mouseover",(function(){return o(!0)}),e),r("mouseout",(function(){return o(!1)}),e),[u]},exports.useInterval=function(e,n){var r=t.useRef();t.useEffect((function(){r.current=e}),[e]),t.useEffect((function(){if(null!==n){var e=setInterval((function(){var e=r.current;null!=e&&e()}),n);return function(){return clearInterval(e)}}}),[n])},exports.useKeyboard=function(e,n,u){void 0===u&&(u={});var o=t.useRef();t.useEffect((function(){o.current=n}),[n]);var c=u.element,i=u.event,a=void 0===i?"keydown":i,f=u.ignoreKey,s=void 0!==f&&f,v=u.ignoreRepeat,d=void 0!==v&&v,p=t.useMemo((function(){var t={ignoreKey:s,ignoreRepeat:d};return"function"==typeof e?e:function(e){return Array.isArray(e)}(e)?function(n){return e.map((function(e){return l(e,t)})).some((function(e){return e(n)}))}:l(e,t)}),[e,s,d]);r(a,t.useCallback((function(e){null!=o.current&&p(e)&&o.current(e)}),[p]),c)},exports.useLocalState=function(e,t,r){var u=void 0===r?{serialize:JSON.stringify,deserialize:JSON.parse}:r,o=u.serialize,c=u.deserialize,i=n.useState((function(){var n=window.localStorage.getItem(e);if(null!=n)try{return c(n)}catch(t){window.localStorage.removeItem(e)}return function(e){return"function"==typeof e}(t)?t():t})),a=i[0],f=i[1],l=n.useRef(e);n.useEffect((function(){var t=l.current;t!==e&&window.localStorage.removeItem(t),l.current=e,null==a?window.localStorage.removeItem(e):window.localStorage.setItem(e,o(a))}),[e,a,o]);var s=n.useCallback((function(){window.localStorage.removeItem(e)}),[e]);return[a,f,s]},exports.usePoll=function(e,n){var r=t.useRef();t.useEffect((function(){r.current=e}),[e]),t.useEffect((function(){var e=null;function t(){null!==n&&(e=setTimeout(u,n))}function u(){var e=function(){var e=r.current,t=void 0;return null!=e&&(t=e()),t}();e instanceof Promise?e.then(t):t()}return u(),function(){e&&clearTimeout(e),e=null}}),[n])},exports.useTimeout=function(e,n){var r=t.useRef();t.useEffect((function(){r.current=e}),[e]),t.useEffect((function(){if(null!==n){var e=setTimeout((function(){var e=r.current;null!=e&&e()}),n);return function(){return clearTimeout(e)}}}),[n])},exports.useTitle=function(e,n){void 0===n&&(n={});var r=n.append,u=void 0!==r&&r,o=n.retain,c=void 0!==o&&o,i=n.separator,a=void 0===i?"":i,f=t.useRef(document.title);t.useLayoutEffect((function(){return f.current=document.title,c?void 0:function(){document.title=f.current}}),[c]),t.useLayoutEffect((function(){document.title=u?f.current+a+e:e}),[e,a,u])},exports.useTrackedState=function(e){var n,r=t.useState({current:(n=e,"function"==typeof n?e():e),undoStack:[],redoStack:[]}),u=r[0],o=r[1],c=t.useCallback((function(){o((function(e){if(0===e.undoStack.length)return e;var t=e.current,n=e.undoStack,r=e.redoStack;return{current:n[n.length-1],undoStack:n.slice(0,n.length-1),redoStack:[].concat(r,[t])}}))}),[]),i=t.useCallback((function(){o((function(e){if(0===e.redoStack.length)return e;var t=e.redoStack;return{current:t[t.length-1],undoStack:[].concat(e.undoStack,[e.current]),redoStack:t.slice(0,t.length-1)}}))}),[]),a=t.useCallback((function(e){o((function(t){var n=t.current,r=t.undoStack;return{current:s(e)?e(n):e,undoStack:[].concat(r,[n]),redoStack:[]}}))}),[]);return[u.current,a,{undo:c,redo:i,canUndo:u.undoStack.length>0,canRedo:u.redoStack.length>0}]};
//# sourceMappingURL=hooks.cjs.production.min.js.map

@@ -1,4 +0,40 @@

import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, 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];
}
/**
* Debounce the update to a value.

@@ -42,3 +78,71 @@ *

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.

@@ -54,3 +158,3 @@ * The listener is added to the `window` by default or the target element if provided using a ref object.

* @param handler the callback function to call on the event firing
* @param element (optional) reference for the element to add the listener too
* @param element (optional) reference for the element to add the listener to
*/

@@ -80,3 +184,92 @@

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.

@@ -138,3 +331,165 @@ *

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`)
*/
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;
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);
}
/**
* Default function type guard

@@ -290,26 +645,127 @@ * @param defaultValue

var DEFAULT_OPTIONS$1 = {
append: false,
retain: false,
separator: ''
};
/**
* Utility hook for boolean toggle operations
* useTitle hook allows you to control the document title from your component.
*
* <p> Use with caution, attaching to buttons can cause unintended consequences from double clicks.
* <p> returns the value, a toggle function, and the original setValue in case required.
* @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 useToggle(startState) {
if (startState === void 0) {
startState = false;
function useTitle(title, options) {
if (options === void 0) {
options = {};
}
var _useState = useState(startState),
value = _useState[0],
setValue = _useState[1];
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;
var toggleValue = function toggleValue() {
return setValue(!value);
};
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]);
}
return [value, toggleValue, setValue];
function isInitializer(candidate) {
return typeof candidate === 'function';
}
export { useDebounce, useEventListener, useHover, useInterval, useLocalState, usePoll, useTimeout, useToggle };
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, useDebounce, useDebug, useEventListener, useFavicon, useHover, useInterval, useKeyboard, useLocalState, usePoll, useTimeout, useTitle, useTrackedState };
//# sourceMappingURL=hooks.esm.js.map

@@ -0,8 +1,13 @@

export * from './useBoolean';
export * from './useDebounce';
export * from './useDebug';
export * from './useEventListener';
export * from './useFavicon';
export * from './useHover';
export * from './useInterval';
export * from './useKeyboard';
export * from './useLocalState';
export * from './usePoll';
export * from './useTimeout';
export * from './useToggle';
export * from './useTitle';
export * from './useTrackedState';

@@ -13,4 +13,4 @@ import { RefObject } from 'react';

* @param handler the callback function to call on the event firing
* @param element (optional) reference for the element to add the listener too
* @param element (optional) reference for the element to add the listener to
*/
export declare function useEventListener<T extends HTMLElement = HTMLDivElement, E extends Event = Event>(eventName: string, handler: ((event: E) => void) | null, element?: RefObject<T>): void;
{
"version": "0.2.0",
"version": "0.3.0",
"name": "@committed/hooks",

@@ -4,0 +4,0 @@ "description": "Committed hooks library",

@@ -111,2 +111,2 @@ <h1 align="center">Committed Hooks</h1>

[MIT](/LICENSE) - © Committed Software 2020 https://committed.io
[MIT](/LICENSE) - © Committed Software 2020 <https://committed.io>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc