@alfalab/hooks
Advanced tools
Comparing version 1.12.12 to 1.12.13
@@ -1,358 +0,16 @@ | ||
import React, { useRef, useState, useCallback, useEffect, useLayoutEffect } from 'react'; | ||
import { hasScrolledToBottomOfPage } from '@alfalab/utils'; | ||
import { v4 } from 'uuid'; | ||
function useClickOutside(ref, cb) { | ||
React.useEffect(function () { | ||
var handler = function (event) { | ||
var checkClickedElement = function (el) { | ||
return !el.current || el.current.contains(event.target); | ||
}; | ||
if ((Array.isArray(ref) && ref.find(checkClickedElement)) || | ||
(!Array.isArray(ref) && checkClickedElement(ref))) { | ||
return; | ||
} | ||
cb(event); | ||
}; | ||
document.addEventListener('mousedown', handler); | ||
document.addEventListener('touchstart', handler); | ||
return function () { | ||
document.removeEventListener('mousedown', handler); | ||
document.removeEventListener('touchstart', handler); | ||
}; | ||
}, [ref, cb]); | ||
} | ||
/** | ||
* Хук обратного отсчёта времени. | ||
* Возвращает оставшееся количество секунд до определённой даты. | ||
* | ||
* @param params.endDate - Дата окончания | ||
* @param params.onStart - Функция, которая будет вызвана при запуске счётчика | ||
* @param params.onEnd - Функция, которая будет вызвана при окончании счётчика | ||
*/ | ||
function useCountdown(_a) { | ||
var endDate = _a.endDate, onStart = _a.onStart, onEnd = _a.onEnd; | ||
var intervalId = useRef(null); | ||
var _b = useState(differenceInSeconds(endDate, new Date())), seconds = _b[0], setSeconds = _b[1]; | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
var clear = useCallback(function () { return clearInterval(intervalId.current); }, []); | ||
useEffect(function () { | ||
if (onStart) { | ||
onStart(); | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
useEffect(function () { | ||
intervalId.current = setInterval(function () { | ||
setSeconds(function (sec) { return sec - 1; }); | ||
}, 1000); | ||
return clear; | ||
}, [clear]); | ||
useEffect(function () { | ||
if (seconds <= 0) { | ||
clear(); | ||
if (onEnd) { | ||
onEnd(); | ||
} | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [seconds]); | ||
return [seconds]; | ||
} | ||
function differenceInSeconds(dateLeft, dateRight) { | ||
var diff = (dateLeft.getTime() - dateRight.getTime()) / 1000; | ||
return diff > 0 ? Math.floor(diff) : Math.ceil(diff); | ||
} | ||
function getDimensionObject(node) { | ||
return node.getBoundingClientRect(); | ||
} | ||
function useDimensions(_a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.liveMeasure, liveMeasure = _c === void 0 ? true : _c; | ||
var _d = useState(), dimensions = _d[0], setDimensions = _d[1]; | ||
var _e = useState(), node = _e[0], setNode = _e[1]; | ||
var ref = useCallback(function (_node) { | ||
setNode(_node); | ||
}, []); | ||
useLayoutEffect(function () { | ||
if (node) { | ||
var measure_1 = function () { | ||
return window.requestAnimationFrame(function () { return setDimensions(getDimensionObject(node)); }); | ||
}; | ||
measure_1(); | ||
if (liveMeasure) { | ||
window.addEventListener('resize', measure_1); | ||
window.addEventListener('scroll', measure_1); | ||
return function () { | ||
window.removeEventListener('resize', measure_1); | ||
window.removeEventListener('scroll', measure_1); | ||
}; | ||
} | ||
} | ||
return function () { return undefined; }; | ||
}, [node, liveMeasure]); | ||
return [ref, dimensions, node]; | ||
} | ||
/** | ||
* https://github.com/facebook/react/issues/14099#issuecomment-440013892 | ||
* | ||
* @param {function} fn | ||
*/ | ||
function useEventCallback(fn) { | ||
var ref = React.useRef(fn); | ||
React.useEffect(function () { | ||
ref.current = fn; | ||
}); | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
return React.useCallback(function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
return (ref.current).apply(void 0, args); | ||
}, []); | ||
} | ||
var prevInputMethod; | ||
function handleKeyDown(event) { | ||
if (event.key === 'Tab') { | ||
prevInputMethod = 'keyboard'; | ||
} | ||
} | ||
function handleMouseDown() { | ||
prevInputMethod = 'mouse'; | ||
} | ||
function handleTouchStart() { | ||
prevInputMethod = 'mouse'; | ||
} | ||
/** | ||
* Навешивает несколько глобальных обработчиков и отслеживает метод ввода - мышь или клавиатура. | ||
* Note: Повторный вызов функции не дублирует обработчики | ||
*/ | ||
function addGlobalListeners() { | ||
document.addEventListener('keydown', handleKeyDown); | ||
document.addEventListener('mousedown', handleMouseDown); | ||
document.addEventListener('touchstart', handleTouchStart); | ||
} | ||
/** | ||
* Хук устанавливает обработчик события на focusin и focusout | ||
* по конкретному типу события | ||
* @param node Элемент на котором установится обработчик (default = document) | ||
* @param inputMethod Если параметр не задан, установит обработчик по любому событию фокуса | ||
*/ | ||
function useFocus(ref, inputMethod) { | ||
var _a = React.useState(false), focus = _a[0], setFocus = _a[1]; | ||
var handleFocus = React.useCallback(function () { | ||
if (!inputMethod || inputMethod === prevInputMethod) { | ||
setFocus(true); | ||
} | ||
}, [inputMethod]); | ||
var handleBlur = React.useCallback(function () { | ||
setFocus(false); | ||
}, []); | ||
React.useEffect(function () { | ||
var node = ref.current; | ||
if (node) { | ||
node.addEventListener('focusin', handleFocus); | ||
node.addEventListener('focusout', handleBlur); | ||
} | ||
return function () { | ||
if (node) { | ||
node.removeEventListener('focusin', handleFocus); | ||
node.removeEventListener('focusout', handleBlur); | ||
} | ||
}; | ||
}, [handleBlur, handleFocus, ref]); | ||
React.useEffect(addGlobalListeners, []); | ||
return [focus]; | ||
} | ||
function useKeydownOutside(ref, cb) { | ||
React.useEffect(function () { | ||
var handler = function (event) { | ||
if (!ref.current || (event.target instanceof Node && ref.current.contains(event.target))) { | ||
return; | ||
} | ||
cb(event); | ||
}; | ||
document.addEventListener('keydown', handler); | ||
return function () { | ||
document.removeEventListener('keydown', handler); | ||
}; | ||
}, [ref, cb]); | ||
} | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
function getValue(list) { | ||
return list.map( | ||
// eslint-disable-next-line no-confusing-arrow | ||
function (_a) { | ||
var value = _a[0], query = _a[1]; | ||
return query.matches ? value : null; | ||
}).filter(Boolean); | ||
} | ||
function useMedia(list, defaultValue) { | ||
var _a = React.useState([defaultValue]), value = _a[0], setValue = _a[1]; | ||
var _b = React.useState([]), mediaQueryList = _b[0], setMediaQueryList = _b[1]; | ||
var isClient = typeof window !== 'undefined'; | ||
React.useEffect(function () { | ||
if (isClient && window.matchMedia) { | ||
var queryList = list.map(function (_a) { | ||
var x = _a[0], y = _a[1]; | ||
return [x, window.matchMedia(y)]; | ||
}); | ||
setMediaQueryList(queryList); | ||
setValue(getValue(queryList)); | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [isClient]); | ||
React.useEffect(function () { | ||
var handler = function () { | ||
setValue(getValue(mediaQueryList)); | ||
}; | ||
mediaQueryList.forEach(function (_a) { | ||
_a[0]; var mediaQuery = _a[1]; | ||
return mediaQuery.addListener(handler); | ||
}); | ||
return function () { | ||
mediaQueryList | ||
.forEach(function (_a) { | ||
_a[0]; var mediaQuery = _a[1]; | ||
return mediaQuery.removeListener(handler); | ||
}); | ||
}; | ||
}, [value, mediaQueryList]); | ||
return value; | ||
} | ||
function usePrevious(value) { | ||
var ref = React.useRef(); | ||
React.useEffect(function () { | ||
ref.current = value; | ||
}); | ||
return ref.current; | ||
} | ||
function useDidUpdateEffect(effect, deps) { | ||
var didMountRef = React.useRef(false); | ||
// eslint-disable-next-line consistent-return | ||
React.useEffect(function () { | ||
if (didMountRef.current) { | ||
var cleanup_1 = effect(); | ||
if (cleanup_1) { | ||
return function () { return cleanup_1(); }; | ||
} | ||
} | ||
didMountRef.current = true; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps); | ||
} | ||
var UseLoadingStates; | ||
(function (UseLoadingStates) { | ||
UseLoadingStates["LOADED"] = "loaded"; | ||
UseLoadingStates["LOADING"] = "loading"; | ||
UseLoadingStates["ERROR"] = "error"; | ||
})(UseLoadingStates || (UseLoadingStates = {})); | ||
/** | ||
* Возвращает результат загрузки изображения | ||
* @param params.src url изображения | ||
* @return результат загрузки (loading | loaded | error) | ||
*/ | ||
function useImageLoadingState(_a) { | ||
var src = _a.src; | ||
var _b = useState(UseLoadingStates.LOADING), loadingState = _b[0], setLoadingState = _b[1]; | ||
useEffect(function () { | ||
var active = true; | ||
setLoadingState(UseLoadingStates.LOADING); | ||
var image = new Image(); | ||
image.onload = function () { | ||
if (!active) { | ||
return; | ||
} | ||
setLoadingState(UseLoadingStates.LOADED); | ||
}; | ||
image.onerror = function () { | ||
if (!active) { | ||
return; | ||
} | ||
setLoadingState(UseLoadingStates.ERROR); | ||
}; | ||
image.srcset = src; | ||
return function () { | ||
active = false; | ||
}; | ||
}, [src]); | ||
return loadingState; | ||
} | ||
function useScrolledToBottomOfPage(cb, once) { | ||
if (once === void 0) { once = true; } | ||
React.useEffect(function () { | ||
var handler = function () { | ||
if (hasScrolledToBottomOfPage()) { | ||
cb(); | ||
if (once) { | ||
document.removeEventListener('scroll', handler); | ||
} | ||
} | ||
}; | ||
document.addEventListener('scroll', handler); | ||
return function () { | ||
document.removeEventListener('scroll', handler); | ||
}; | ||
}, [cb]); | ||
} | ||
function useForceUpdate() { | ||
var _a = useState(Object.create(null)), dispatch = _a[1]; | ||
var memoizedDispatch = useCallback(function () { | ||
dispatch(Object.create(null)); | ||
}, [dispatch]); | ||
return memoizedDispatch; | ||
} | ||
/** | ||
* Хук получения состояния доступности компонента. | ||
*/ | ||
var useIsMounted = function () { | ||
var isMounted = useRef(true); | ||
useEffect(function () { return function () { | ||
isMounted.current = false; | ||
}; }, []); | ||
return useCallback(function () { return isMounted.current; }, []); | ||
}; | ||
var useId = React.useId || function useUuid() { | ||
/* | ||
* Utilize useState instead of useMemo because React | ||
* makes no guarantees that the memo store is durable | ||
*/ | ||
var id = React.useState(function () { return v4(); })[0]; | ||
return id; | ||
}; | ||
/** | ||
* Обновление состояния, только если компонент смонтирован | ||
* Устраняет утечку и варнинги | ||
*/ | ||
var useStateIfMounted = function (initialValue) { | ||
var getIsMounted = useIsMounted(); | ||
var _a = React.useState(initialValue), storedValue = _a[0], setStoredValue = _a[1]; | ||
var setState = React.useCallback(function (value) { | ||
var isMounted = getIsMounted(); | ||
if (isMounted) { | ||
setStoredValue(value); | ||
} | ||
}, [getIsMounted]); | ||
return [storedValue, setState]; | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
var useLayoutEffect_SAFE_FOR_SSR = typeof document !== 'undefined' ? useLayoutEffect : useEffect; | ||
export { useClickOutside, useCountdown, useDidUpdateEffect, useDimensions, useEventCallback, useFocus, useForceUpdate, useId, useImageLoadingState, useIsMounted, useKeydownOutside, useLayoutEffect_SAFE_FOR_SSR, useMedia, usePrevious, useScrolledToBottomOfPage, useStateIfMounted }; | ||
export { useClickOutside } from './useClickOutside/hook.js'; | ||
export { useCountdown } from './useCountdown/hook.js'; | ||
export { useDimensions } from './useDimensions/hook.js'; | ||
export { useEventCallback } from './useEventCallback/hook.js'; | ||
export { useFocus } from './useFocus/hook.js'; | ||
export { useKeydownOutside } from './useKeydownOutside/hook.js'; | ||
export { useMedia } from './useMedia/hook.js'; | ||
export { usePrevious } from './usePrevious/hook.js'; | ||
export { useDidUpdateEffect } from './useDidUpdateEffect/hook.js'; | ||
export { useImageLoadingState } from './useImageLoadingState/hook.js'; | ||
export { useScrolledToBottomOfPage } from './useScrolledToBottomOfPage/hook.js'; | ||
export { useForceUpdate } from './useForceUpdate/hook.js'; | ||
export { useIsMounted } from './useIsMounted/hook.js'; | ||
export { useId } from './useId/hook.js'; | ||
export { useStateIfMounted } from './useStateIfMounted/hook.js'; | ||
export { useLayoutEffect_SAFE_FOR_SSR } from './useLayoutEffect_SAFE_FOR_SSR/hook.js'; |
{ | ||
"name": "@alfalab/hooks", | ||
"version": "1.12.12", | ||
"version": "1.12.13", | ||
"description": "common hooks", | ||
@@ -13,5 +13,7 @@ "sideEffects": false, | ||
"scripts": { | ||
"build": "yarn build:ts && yarn build:dts", | ||
"build:ts": "rollup -c", | ||
"build:dts": "dts-bundle-generator --no-banner --no-check --config dts.json" | ||
"build": "yarn build:ts:es5 && yarn build:ts:es6 && yarn build:dts && yarn size", | ||
"build:ts:es5": "rollup --config './rollup.config.js'", | ||
"build:ts:es6": "rollup --config './rollup.config.esm.js'", | ||
"build:dts": "dts-bundle-generator --no-banner --no-check --config dts.json", | ||
"size": "yarn size-limit --json 2>&1 | grep -v '^$ ' > size-stat.json" | ||
}, | ||
@@ -22,3 +24,3 @@ "keywords": [], | ||
"dependencies": { | ||
"@alfalab/utils": "^1.16.0", | ||
"@alfalab/utils": "^1.16.1", | ||
"uuid": "^8.3.2" | ||
@@ -33,3 +35,2 @@ }, | ||
"@types/uuid": "^8.3.4", | ||
"dts-bundle-generator": "^6.12.0", | ||
"react": "^18.2.0", | ||
@@ -44,3 +45,3 @@ "react-dom": "^18.2.0" | ||
}, | ||
"gitHead": "81776918516e2a75f44ac1b04c177e3ca8f942e4" | ||
"gitHead": "e1feb71a758c0841dcf994c66d73da1f8445a381" | ||
} |
42911
8
24
813
Updated@alfalab/utils@^1.16.1