🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
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

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