UNPKG

rooks

Version:

Essential React custom hooks ⚓ to super charge your components!

1,735 lines (1,671 loc) 113 kB
// src/hooks/useOnLongHover.ts import { useCallback as useCallback2, useEffect as useEffect4, useState } from "react"; // src/hooks/useFreshCallback.ts import { useCallback } from "react"; // src/hooks/useFreshRef.ts import { useEffect as useEffect2, useRef } from "react"; // src/hooks/useIsomorphicEffect.ts import { useEffect, useLayoutEffect } from "react"; var useIsomorphicEffect = typeof window === "undefined" ? useEffect : useLayoutEffect; // src/hooks/useFreshRef.ts function useFreshRef(value, preferLayoutEffect = false) { const useEffectToUse = preferLayoutEffect ? useIsomorphicEffect : useEffect2; const ref = useRef(value); useEffectToUse(() => { ref.current = value; }); return ref; } // src/hooks/useFreshCallback.ts function useFreshCallback(callback) { const freshRef = useFreshRef(callback); const tick = useCallback( (...args) => { return freshRef.current(...args); }, [freshRef] ); return tick; } // src/hooks/useTimeoutWhen.ts import { useEffect as useEffect3 } from "react"; // src/utils/noop.ts var noop = () => { }; // src/hooks/useTimeoutWhen.ts function useTimeoutWhen(callback, timeoutDelayMs = 0, when = true, key = 0) { const freshCallback = useFreshCallback(callback); useEffect3(() => { if (when) { let internalCallback2 = function() { freshCallback(); }; var internalCallback = internalCallback2; if (typeof window !== "undefined") { const timeout = window.setTimeout(internalCallback2, timeoutDelayMs); return () => { window.clearTimeout(timeout); }; } else { console.warn("useTimeoutWhen: window is undefined."); } } return noop; }, [timeoutDelayMs, when, key, freshCallback]); } // src/hooks/useOnLongHover.ts var useOnLongHover = (callback, { duration = 300 } = {}) => { const [targetNode, setTargetNode] = useState(null); const ref = useCallback2((node) => { setTargetNode(node); }, []); const [isHovering, setIsHovering] = useState(false); const freshCallback = useFreshCallback(callback); useTimeoutWhen( () => { freshCallback(); }, duration, isHovering ); const start = useCallback2(() => { setIsHovering(true); }, []); const handleOnMouseLeave = useCallback2((_) => { setIsHovering(false); }, []); useEffect4(() => { if (targetNode) { targetNode.addEventListener("mouseenter", start); targetNode.addEventListener("mouseleave", handleOnMouseLeave); } return () => { if (targetNode) { targetNode.removeEventListener("mouseenter", start); targetNode.removeEventListener("mouseleave", handleOnMouseLeave); } }; }, [start, handleOnMouseLeave, targetNode]); return ref; }; // src/hooks/useOnLongPress.ts import { useCallback as useCallback3, useEffect as useEffect5, useState as useState2 } from "react"; var defaultOnClick = () => { }; var useOnLongPress = (callback, { onClick, duration = 300 } = {}) => { const [targetNode, setTargetNode] = useState2(null); const ref = useCallback3((node) => { setTargetNode(node); }, []); const [isPressing, setIsPressing] = useState2(false); const freshCallback = useFreshCallback(callback); const freshClick = useFreshCallback(onClick ?? defaultOnClick); useTimeoutWhen(freshCallback, duration, isPressing); const start = useCallback3((_) => { setIsPressing(true); }, []); const handleOnClick = useCallback3( (event) => { setIsPressing(false); freshClick(event); }, [freshClick] ); useEffect5(() => { if (targetNode) { targetNode.addEventListener("mousedown", start); targetNode.addEventListener("mouseup", handleOnClick); targetNode.addEventListener("mouseleave", handleOnClick); targetNode.addEventListener("touchstart", start); targetNode.addEventListener("touchend", handleOnClick); targetNode.addEventListener("touchcancel", handleOnClick); } return () => { if (targetNode) { targetNode.removeEventListener("mousedown", start); targetNode.removeEventListener("mouseup", handleOnClick); targetNode.removeEventListener("mouseleave", handleOnClick); targetNode.removeEventListener("touchstart", start); targetNode.removeEventListener("touchend", handleOnClick); targetNode.removeEventListener("touchcancel", handleOnClick); } }; }, [start, handleOnClick, targetNode]); return ref; }; // src/hooks/useMapState.ts import { useCallback as useCallback4, useState as useState3 } from "react"; function useMapState(initialValue) { const [map, setMap] = useState3(initialValue); const set = useCallback4((key, value) => { setMap((currentMap) => ({ ...currentMap, [key]: value })); }, []); const has = useCallback4( (key) => { return typeof map[key] !== "undefined"; }, [map] ); const setMultiple = useCallback4((nextMap) => { setMap((currentMap) => ({ ...currentMap, ...nextMap })); }, []); const removeMultiple = useCallback4( (...keys) => { setMap((currentMap) => { const nextMap = { ...currentMap }; for (const key of keys) { delete nextMap[key]; } return nextMap; }); }, [setMap] ); const remove = useCallback4( (key) => { setMap((currentMap) => { const nextMap = { ...currentMap }; delete nextMap[key]; return nextMap; }); }, [setMap] ); const removeAll = useCallback4(() => { setMap((currentMap) => { const nextMap = { ...currentMap }; for (const key in nextMap) { if (nextMap.hasOwnProperty(key)) { delete nextMap[key]; } } return nextMap; }); }, [setMap]); return [ map, { has, remove, removeAll, removeMultiple, set, setMultiple } ]; } // src/hooks/useArrayState.ts import { useCallback as useCallback5, useMemo, useState as useState4 } from "react"; function useArrayState(initial) { const [array, setArray] = useState4(initial ?? []); const push = useCallback5( (value) => { setArray([...array, value]); }, [array] ); const pop = useCallback5(() => { setArray(array.slice(0, array.length - 1)); }, [array]); const clear = useCallback5(() => { setArray([]); }, []); const unshift = useCallback5( (value) => { setArray([value, ...array]); }, [array] ); const shift = useCallback5(() => { setArray(array.slice(1)); }, [array]); const reverse = useCallback5(() => { setArray([...array].reverse()); }, [array]); const concat = useCallback5( (value) => { setArray([...array, ...value]); }, [array] ); const fill = useCallback5( (value, start, end) => { setArray([...array].fill(value, start, end)); }, [array] ); const updateItemAtIndex = useCallback5( (index, value) => { setArray((prevArray) => { const newArray = [...prevArray]; newArray[index] = value; return newArray; }); }, [setArray] ); const splice = useCallback5( (...args) => { setArray((prevArray) => { const newArray = [...prevArray]; newArray.splice(...args); return newArray; }); }, [setArray] ); const removeItemAtIndex = useCallback5( (index) => { setArray((prevArray) => { const newArray = [...prevArray]; newArray.splice(index, 1); return newArray; }); }, [setArray] ); const replaceItemAtIndex = useCallback5( (index, value) => { setArray((prevArray) => { const newArray = [...prevArray]; newArray.splice(index, 1, value); return newArray; }); }, [setArray] ); const insertItemAtIndex = useCallback5( (index, value) => { setArray((prevArray) => { const newArray = [...prevArray]; newArray.splice(index, 0, value); return newArray; }); }, [setArray] ); const sort = useCallback5( (compareFn) => { setArray([...array].sort(compareFn)); }, [array] ); const controls = useMemo(() => { return { push, pop, clear, unshift, shift, reverse, concat, fill, updateItemAtIndex, setArray, splice, removeItemAtIndex, replaceItemAtIndex, insertItemAtIndex, sort }; }, [ push, pop, clear, unshift, shift, reverse, concat, fill, updateItemAtIndex, setArray, splice, removeItemAtIndex, replaceItemAtIndex, insertItemAtIndex, sort ]); const returnValue = useMemo(() => { return [array, controls]; }, [array, controls]); return returnValue; } // src/hooks/useAsyncEffect.ts import { useEffect as useEffect7, useRef as useRef3, useCallback as useCallback7 } from "react"; // src/hooks/useGetIsMounted.ts import { useCallback as useCallback6, useEffect as useEffect6, useRef as useRef2 } from "react"; var useGetIsMounted = () => { const isMountedRef = useRef2(false); const get = useCallback6(() => isMountedRef.current, []); useEffect6(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); return get; }; // src/hooks/useAsyncEffect.ts function useAsyncEffect(effect, deps, cleanup) { const lastCallId = useRef3(0); const getIsMounted = useGetIsMounted(); const effectRef = useFreshRef(effect); const callback = useCallback7(async () => { const callId = ++lastCallId.current; const shouldContinueEffect = () => { return getIsMounted() && callId === lastCallId.current; }; try { return await effectRef.current(shouldContinueEffect); } catch (error) { throw error; } }, [getIsMounted, ...deps]); useEffect7(() => { let result; callback().then((value) => { result = value; }); return () => { cleanup?.(result); }; }, [callback, cleanup]); } // src/hooks/useAudio.ts import { useState as useState5, useEffect as useEffect8 } from "react"; var noop2 = () => { }; function useAudio(options = {}, callbacks = {}) { const { autoPlay = false, isMuted: initialIsMuted = false } = options; const [isPlaying, setIsPlaying] = useState5(autoPlay); const [isMuted, setIsMuted] = useState5(initialIsMuted); const [audioNode, setAudioNode] = useState5(null); const onPlay = useFreshCallback(callbacks.onPlay ?? noop2); const onPause = useFreshCallback(callbacks.onPause ?? noop2); const onMute = useFreshCallback(callbacks.onMute ?? noop2); const onUnmute = useFreshCallback(callbacks.onUnmute ?? noop2); const onLoadedMetadata = useFreshCallback(callbacks.onLoadedMetadata ?? noop2); const audioCallbackRef = (node) => { if (node !== null) { setAudioNode(node); } }; useEffect8(() => { if (!audioNode) return; if (isPlaying) { audioNode.play(); } else { audioNode.pause(); } }, [audioNode, isPlaying]); useEffect8(() => { if (!audioNode) return; audioNode.muted = isMuted; }, [audioNode, isMuted]); useEffect8(() => { if (!audioNode) return; const handleLoadedMetadata = () => { if (autoPlay) { audioNode?.play(); } onLoadedMetadata(); }; const handlePlay = () => { onPlay(); }; const handlePause = () => { onPause(); }; const handleEnded = () => { setIsPlaying(false); }; audioNode?.addEventListener("loadedmetadata", handleLoadedMetadata); audioNode?.addEventListener("play", handlePlay); audioNode?.addEventListener("pause", handlePause); audioNode?.addEventListener("ended", handleEnded); return () => { audioNode?.removeEventListener("loadedmetadata", handleLoadedMetadata); audioNode?.removeEventListener("play", handlePlay); audioNode?.removeEventListener("pause", handlePause); audioNode?.removeEventListener("ended", handleEnded); }; }, [autoPlay, onLoadedMetadata, onPlay, onPause, audioNode]); const play = () => { setIsPlaying(true); }; const pause = () => { setIsPlaying(false); }; const togglePlay = () => { setIsPlaying(!isPlaying); }; const mute = () => { setIsMuted(true); onMute(); }; const unmute = () => { setIsMuted(false); onUnmute(); }; const toggleMute = () => { if (isMuted) { unmute(); } else { mute(); } }; const controls = { play, pause, togglePlay, mute, unmute, toggleMute }; const state = { isPlaying, isMuted }; return [audioCallbackRef, state, controls]; } // src/hooks/useBoundingclientrect.ts import { useState as useState6, useCallback as useCallback8 } from "react"; // src/hooks/useDidMount.ts import { useEffect as useEffect9 } from "react"; function useDidMount(callback) { useEffect9(() => { if (typeof callback === "function") { callback(); } }, []); } // src/hooks/useMutationObserver.ts import { useEffect as useEffect10 } from "react"; var config = { attributes: true, characterData: true, childList: true, subtree: true }; function useMutationObserver(ref, callback, options = config) { useEffect10(() => { if (ref.current) { const observer = new MutationObserver(callback); observer.observe(ref.current, options); return () => { observer.disconnect(); }; } return noop; }, [callback, options, ref]); } // src/hooks/useBoundingclientrect.ts function getBoundingClientRect(element) { return element.getBoundingClientRect(); } function useBoundingclientrect(ref) { const [domRect, setDomRect] = useState6(null); const update = useCallback8(() => { setDomRect(ref.current ? getBoundingClientRect(ref.current) : null); }, [ref]); useDidMount(() => { update(); }); useMutationObserver(ref, update); return domRect; } // src/hooks/useBoundingclientrectRef.ts import { useState as useState8, useEffect as useEffect12, useCallback as useCallback10 } from "react"; // src/hooks/useForkRef.ts import { useMemo as useMemo2 } from "react"; function setRef(ref, value) { if (typeof ref === "function") { ref(value); } else if (ref !== null && ref !== void 0) { ref.current = value; } } function useForkRef(refA, refB) { return useMemo2(() => { if (refA === null && refB === null) { return null; } return (refValue) => { setRef(refA, refValue); setRef(refB, refValue); }; }, [refA, refB]); } // src/hooks/useMutationObserverRef.ts import { useEffect as useEffect11, useCallback as useCallback9, useState as useState7 } from "react"; var config2 = { attributes: true, characterData: true, childList: true, subtree: true }; function useMutationObserverRef(callback, options = config2) { const [node, setNode] = useState7(null); useEffect11(() => { if (node) { const observer = new MutationObserver(callback); observer.observe(node, options); return () => { observer.disconnect(); }; } return noop; }, [node, callback, options]); const ref = useCallback9((nodeElement) => { setNode(nodeElement); }, []); return [ref]; } // src/hooks/useBoundingclientrectRef.ts function getBoundingClientRect2(element) { return element.getBoundingClientRect(); } function useBoundingclientrectRef() { const [domRect, setDomRect] = useState8(null); const [node, setNode] = useState8(null); const update = useCallback10(() => { setDomRect(node ? getBoundingClientRect2(node) : null); }, [node]); useEffect12(() => { update(); }, [update]); const ref = useCallback10((nodeElement) => { setNode(nodeElement); }, []); const [mutationObserverRef] = useMutationObserverRef(update); const forkedRef = useForkRef(ref, mutationObserverRef); return [forkedRef, domRect, update]; } // src/hooks/useCountdown.ts import { useState as useState9 } from "react"; // src/hooks/useIntervalWhen.ts import { useRef as useRef4, useEffect as useEffect13 } from "react"; function useIntervalWhen(callback, intervalDurationMs = 0, when = true, startImmediate = false) { const savedRefCallback = useRef4(void 0); useEffect13(() => { savedRefCallback.current = callback; }); useEffect13(() => { if (when) { let internalCallback2 = function() { savedRefCallback.current?.(); }; var internalCallback = internalCallback2; if (startImmediate) { internalCallback2(); } const interval = window.setInterval(internalCallback2, intervalDurationMs); return () => { window.clearInterval(interval); }; } return noop; }, [when, intervalDurationMs, startImmediate]); } // src/hooks/useCountdown.ts function useCountdown(endTime, options = {}) { const { interval = 1e3, onDown, onEnd } = options; const [time, setTime] = useState9(() => /* @__PURE__ */ new Date()); const restTime = endTime.getTime() - time.getTime(); const count = restTime > 0 ? Math.ceil(restTime / interval) : 0; useIntervalWhen(onTick, count ? interval : void 0, true, true); return count; function onTick() { const newTime = /* @__PURE__ */ new Date(); if (newTime > endTime) { if (onEnd) { onEnd(newTime); } setTime(endTime); return; } if (onDown) { onDown(restTime, newTime); } setTime(newTime); } } // src/hooks/useCounter.ts import { useCallback as useCallback11, useState as useState10 } from "react"; function useCounter(initialValue) { const [counter, setCounter] = useState10(initialValue); const incrementBy = useCallback11((incrAmount) => { setCounter((currentCounter) => currentCounter + incrAmount); }, []); const decrementBy = useCallback11( (decrAmount) => { incrementBy(-decrAmount); }, [incrementBy] ); const increment = useCallback11(() => { incrementBy(1); }, [incrementBy]); const decrement = useCallback11(() => { incrementBy(-1); }, [incrementBy]); const reset = useCallback11(() => { setCounter(initialValue); }, [initialValue]); return { decrement, decrementBy, increment, incrementBy, reset, value: counter }; } // src/hooks/useDebounce.ts import debounce from "lodash.debounce"; import { useRef as useRef5, useCallback as useCallback12 } from "react"; // src/hooks/useWillUnmount.ts import { useEffect as useEffect14 } from "react"; function useWillUnmount(callback) { useEffect14(() => { return callback; }, []); } // src/hooks/useDebounce.ts function useDebounce(callback, wait, options) { const createDebouncedCallback = useCallback12( (function_) => { return debounce(function_, wait, options); }, [wait, options] ); const freshCallback = useFreshRef(callback); function tick(...args) { freshCallback.current?.(...args); } const debouncedCallbackRef = useRef5( createDebouncedCallback(tick) ); useWillUnmount(() => { debouncedCallbackRef.current?.cancel(); }); return debouncedCallbackRef.current; } // src/hooks/useDebounceFn.ts import { useRef as useRef6, useCallback as useCallback13, useState as useState11 } from "react"; function useDebounceFn(func, delay, options = { leading: false, trailing: true }) { const { leading, trailing, maxWait } = options; if (!leading && !trailing) { throw new Error("leading and trailing cannot both be false"); } else if (typeof maxWait !== "undefined" && maxWait < delay) { throw new Error("maxWait cannot be less than delay"); } const funcRef = useFreshCallback(func); const [isTimeoutEnabled, setIsTimeoutEnabled] = useState11(false); const [key, setKey] = useState11(0); const lastExecutionTimeRef = useRef6(0); const argsRef = useRef6(void 0); const debouncedFn = useCallback13( (...args) => { argsRef.current = args; const overrideTimeout = typeof maxWait !== "undefined" && lastExecutionTimeRef.current && Date.now() - lastExecutionTimeRef.current > maxWait; if (leading) { if (isTimeoutEnabled && overrideTimeout) { setKey((prevKey) => prevKey + 1); lastExecutionTimeRef.current = Date.now(); try { funcRef(...args); } catch (error) { console.warn(error); } } else if (overrideTimeout && !isTimeoutEnabled) { setIsTimeoutEnabled(true); lastExecutionTimeRef.current = Date.now(); try { funcRef(...args); } catch (error) { console.warn(error); } } else if (isTimeoutEnabled && !overrideTimeout) { } else { setIsTimeoutEnabled(true); lastExecutionTimeRef.current = Date.now(); try { funcRef(...args); } catch (error) { console.warn(error); } } } if (trailing) { if (isTimeoutEnabled) { setKey((prevKey) => prevKey + 1); } else { setIsTimeoutEnabled(true); } } }, [maxWait, isTimeoutEnabled, leading, trailing, funcRef] ); useTimeoutWhen( () => { if (typeof maxWait !== "undefined" && trailing) { if (!argsRef.current) return; lastExecutionTimeRef.current = Date.now(); try { funcRef(...argsRef.current); } catch (error) { console.warn(error); } } }, maxWait ?? Infinity, isTimeoutEnabled && typeof maxWait !== "undefined" && trailing, key ); useTimeoutWhen( () => { if (trailing) { if (!argsRef.current) return; lastExecutionTimeRef.current = Date.now(); try { funcRef(...argsRef.current); } catch (error) { console.warn(error); } } setIsTimeoutEnabled(false); }, delay, isTimeoutEnabled && trailing, key ); const freshDebouncedFn = useFreshCallback(debouncedFn); return [freshDebouncedFn, isTimeoutEnabled]; } // src/hooks/useDebouncedValue.ts import { useState as useState12 } from "react"; // src/hooks/useDidUpdate.ts import { useEffect as useEffect15, useMemo as useMemo3, useRef as useRef7 } from "react"; function useDidUpdate(callback, conditions) { const hasMountedRef = useRef7(false); const internalConditions = useMemo3(() => { if (typeof conditions !== "undefined" && !Array.isArray(conditions)) { return [conditions]; } else if (Array.isArray(conditions) && conditions.length === 0) { console.warn( "Using [] as the second argument makes useDidUpdate a noop. The second argument should either be `undefined` or an array of length greater than 0." ); } return conditions; }, [conditions]); useEffect15(() => { if (hasMountedRef.current) { callback(); } }, internalConditions); useDidMount(() => { hasMountedRef.current = true; }); useWillUnmount(() => { hasMountedRef.current = false; }); } // src/hooks/useDebouncedValue.ts var defaultUseDebounceValueOptions = { initializeWithNull: false }; var useDebouncedValue = (value, timeout, options = {}) => { const { initializeWithNull } = Object.assign( {}, defaultUseDebounceValueOptions, options ); const [updatedValue, setUpdatedValue] = useState12( initializeWithNull ? null : value ); const debouncedSetUpdatedValue = useDebounce(setUpdatedValue, timeout); useDidMount(() => { if (initializeWithNull) { debouncedSetUpdatedValue(value); } }); useDidUpdate(() => { debouncedSetUpdatedValue(value); }, [value]); return [updatedValue, setUpdatedValue]; }; // src/hooks/useDeepCompareEffect.ts import { useEffect as useEffect16, useRef as useRef8 } from "react"; import isEqual from "fast-deep-equal"; // src/hooks/warning.ts var isDevelopmentEnvironment = process.env.NODE_ENV !== "production"; var warning = () => { }; if (isDevelopmentEnvironment) { const printWarning = (actualMessage) => { const message = `Warning: ${actualMessage}`; if (typeof console !== "undefined") { console.error(message); } try { throw new Error(message); } catch { } }; warning = function(condition, actualMessage) { if (!condition) { printWarning(actualMessage); } }; } // src/hooks/useDeepCompareEffect.ts function isPrimitive(value) { const valueType = typeof value; return valueType === "string" || valueType === "number" || valueType === "bigint" || valueType === "boolean" || valueType === "undefined" || valueType === "symbol"; } function useDeepCompareEffect(callback, dependencies) { const previousDeps = useRef8(dependencies); if (!Array.isArray(dependencies)) { throw new Error( "useDeepCompareEffect should be used with an array of dependencies" ); } const hasPrimitives = dependencies.every(isPrimitive); warning( !hasPrimitives, "useDeepCompareEffect should not be used with primitive values as dependencies" ); if (!isEqual(previousDeps.current, dependencies)) { previousDeps.current = dependencies; } useEffect16(callback, previousDeps.current); } // src/hooks/useDimensionsRef.ts import { useState as useState13, useCallback as useCallback14, useLayoutEffect as useLayoutEffect2 } from "react"; // src/hooks/useGlobalObjectEventListener.ts import { useEffect as useEffect17 } from "react"; // src/hooks/useFreshTick.ts function useFreshTick(callback) { const freshRef = useFreshRef(callback); function tick(...args) { if (typeof freshRef.current === "function") { freshRef.current(...args); } } return tick; } // src/hooks/useGlobalObjectEventListener.ts function useGlobalObjectEventListener(globalObject, eventName, callback, listenerOptions = {}, when = true, isLayoutEffect = false) { const freshCallback = useFreshTick(callback); const useEffectToRun = isLayoutEffect ? useIsomorphicEffect : useEffect17; useEffectToRun(() => { warning( typeof globalObject !== "undefined", "[useGlobalObjectEventListener]: Cannot attach event handlers to undefined." ); if (typeof globalObject !== "undefined" && when) { globalObject.addEventListener(eventName, freshCallback, listenerOptions); return () => { globalObject.removeEventListener( eventName, freshCallback, listenerOptions ); }; } return () => { }; }, [eventName, listenerOptions]); } // src/hooks/useOnWindowResize.ts function useOnWindowResize(callback, when = true, isLayoutEffect = false) { useGlobalObjectEventListener( global.window, "resize", callback, { passive: true }, when, isLayoutEffect ); } // src/hooks/useOnWindowScroll.ts function useOnWindowScroll(callback, when = true, isLayoutEffect = false) { useGlobalObjectEventListener( global.window, "scroll", callback, { passive: true }, when, isLayoutEffect ); } // src/hooks/useDimensionsRef.ts var getDimensionObject = (node) => { const rect = node.getBoundingClientRect(); return { bottom: rect.bottom, height: rect.height, left: rect.left, right: rect.right, top: rect.top, width: rect.width, x: rect.left, y: rect.top }; }; var noWindowReturnValue = [void 0, null, null]; var useDimensionsRef = ({ updateOnScroll = true, updateOnResize = true } = {}) => { const [dimensions, setDimensions] = useState13(null); const [node, setNode] = useState13(null); const ref = useCallback14((nodeFromCallback) => { setNode(nodeFromCallback); }, []); const measure = useCallback14(() => { window.requestAnimationFrame(() => { if (node) { setDimensions(getDimensionObject(node)); } }); }, [node]); useLayoutEffect2(() => { measure(); }, [measure]); useOnWindowResize( () => { measure(); }, updateOnResize, true ); useOnWindowScroll( () => { measure(); }, updateOnScroll, true ); if (typeof window === "undefined") { console.warn("useDimensionsRef: window is undefined."); return noWindowReturnValue; } return [ref, dimensions, node]; }; // src/hooks/useDocumentEventListener.ts function useDocumentEventListener(eventName, callback, listenerOptions = {}, isLayoutEffect = false) { useGlobalObjectEventListener( global.document, eventName, callback, listenerOptions, true, isLayoutEffect ); } // src/hooks/useDocumentTitle.ts import { useEffect as useEffect18, useRef as useRef9 } from "react"; function useDocumentTitle(title, options = {}) { const isBrowser = typeof window !== "undefined"; const prevTitleRef = useRef9(isBrowser ? document.title : ""); const { resetOnUnmount = false } = options; useEffect18(() => { if (isBrowser) { document.title = title; const lastTitle = prevTitleRef.current; if (resetOnUnmount) { return () => { document.title = lastTitle; }; } } return noop; }, [title, isBrowser, resetOnUnmount]); } // src/hooks/useDocumentVisibilityState.ts import { useCallback as useCallback15, useState as useState14 } from "react"; function useDocumentVisibilityState() { const [visibilityState, setVisibilityState] = useState14( document ? document.visibilityState : null ); const handleVisibilityChange = useCallback15(() => { setVisibilityState(document ? document.visibilityState : null); }, []); useGlobalObjectEventListener( global.document, "visibilitychange", handleVisibilityChange, {}, true, true ); return visibilityState; } // src/hooks/useEffectOnceWhen.ts import { useEffect as useEffect19, useRef as useRef10 } from "react"; function useEffectOnceWhen(callback, when = true) { const hasRunOnceRef = useRef10(false); const callbackRef = useRef10(callback); useEffect19(() => { callbackRef.current = callback; }); useEffect19(() => { if (when && !hasRunOnceRef.current) { callbackRef.current(); hasRunOnceRef.current = true; } }, [when]); } // src/hooks/useEventListenerRef.ts import { useEffect as useEffect20 } from "react"; // src/hooks/useRefElement.ts import { useCallback as useCallback16, useState as useState15 } from "react"; function useRefElement() { const [refElement, setRefElement] = useState15(null); const ref = useCallback16( (element) => { setRefElement(element); }, [] ); return [ref, refElement]; } // src/hooks/useEventListenerRef.ts function useEventListenerRef(eventName, callback, listenerOptions = {}, isLayoutEffect = false) { const [ref, element] = useRefElement(); const freshCallback = useFreshTick(callback); const useEffectToRun = isLayoutEffect ? useIsomorphicEffect : useEffect20; useEffectToRun(() => { if (!element?.addEventListener) { return noop; } element.addEventListener(eventName, freshCallback, listenerOptions); return () => { element.removeEventListener(eventName, freshCallback, listenerOptions); }; }, [element, eventName, listenerOptions]); return ref; } // src/hooks/useFileDropRef.ts import { useCallback as useCallback17, useState as useState16, useEffect as useEffect21 } from "react"; function useFileDropRef(options = {}, callbacks = {}) { const { accept, maxFileSize, maxFiles } = options; const { onDrop = noop, onFileAccepted = noop, onFileRejected = noop, onDragEnter = noop, onDragLeave = noop } = callbacks; const [targetNode, setTargetNode] = useState16(null); const freshOnDrop = useFreshCallback(onDrop); const freshOnFileAccepted = useFreshCallback(onFileAccepted); const freshOnFileRejected = useFreshCallback(onFileRejected); const freshOnDragEnter = useFreshCallback(onDragEnter); const freshOnDragLeave = useFreshCallback(onDragLeave); useCallback17((node) => { setTargetNode(node); }, []); const fileIsValid = useCallback17( (file) => { if (accept && !accept.includes(file.type)) { return { valid: false, reason: "File type not allowed" }; } if (maxFileSize && file.size > maxFileSize) { return { valid: false, reason: "File size exceeds the limit" }; } return { valid: true }; }, [accept, maxFileSize] ); const handleDrop = useCallback17( (event) => { event.preventDefault(); const files = Array.from(event.dataTransfer?.files || []); const acceptedFiles = []; const rejectedFiles = []; if (maxFiles && files.length > maxFiles) { for (const file of files) { freshOnFileRejected(file, "Exceeded maximum number of files"); } } else { files.forEach((file) => { const validationResult = fileIsValid(file); if (validationResult.valid) { acceptedFiles.push(file); freshOnFileAccepted(file); } else { rejectedFiles.push(file); freshOnFileRejected( file, validationResult.reason || "Unknown reason" ); } }); } freshOnDrop(acceptedFiles, rejectedFiles); }, [ fileIsValid, freshOnFileAccepted, freshOnFileRejected, maxFiles, freshOnDrop ] ); const handleDragOver = useCallback17((event) => { event.preventDefault(); }, []); useEffect21(() => { if (targetNode) { targetNode.addEventListener("drop", handleDrop); targetNode.addEventListener("dragover", handleDragOver); targetNode.addEventListener("dragenter", freshOnDragEnter); targetNode.addEventListener("dragleave", freshOnDragLeave); return () => { targetNode.removeEventListener("drop", handleDrop); targetNode.removeEventListener("dragover", handleDragOver); targetNode.removeEventListener("dragenter", freshOnDragEnter); targetNode.removeEventListener("dragleave", freshOnDragLeave); }; } else { return () => { }; } }, [ targetNode, handleDrop, handleDragOver, freshOnDragEnter, freshOnDragLeave ]); return useCallback17((node) => { setTargetNode(node); }, []); } // src/hooks/useFullscreen.ts import { useState as useState17, useCallback as useCallback18, useEffect as useEffect22 } from "react"; var FullscreenApi = { DOM_UNAVAILABLE_ERROR: "DOM is unavailable server-side. Please use this method client-side only.", FULLSCREEN_UNSUPPORTED_ERROR: "Your browser does not support Fullscreen API.", getEventsNames() { if (typeof document === "undefined") return null; const _document = document; if ("exitFullscreen" in _document) return ["fullscreenchange", "fullscreenerror"]; if ("webkitExitFullscreen" in _document) return ["webkitfullscreenchange", "webkitfullscreenerror"]; if ("webkitCancelFullScreen" in _document) return ["webkitfullscreenchange", "webkitfullscreenerror"]; if ("mozCancelFullScreen" in _document) return ["mozfullscreenchange", "mozfullscreenerror"]; if ("msExitFullscreen" in _document) return ["MSFullscreenChange", "MSFullscreenError"]; return null; }, getEventName(eventType) { const eventsNames = this.getEventsNames(); if (!eventsNames) return null; if (eventType === "change") return eventsNames[0]; return eventsNames[1]; }, get fullscreenEnabled() { if (typeof document === "undefined") return false; const _document = document; return _document.fullscreenEnabled || _document.webkitFullscreenEnabled || !!_document.webkitCancelFullScreen || _document.mozFullScreenEnabled || _document.msFullscreenEnabled || false; }, get fullscreenElement() { if (typeof document === "undefined") return null; const _document = document; return _document.fullscreenElement || _document.webkitFullscreenElement || _document.webkitCurrentFullScreenElement || _document.mozFullScreenElement || _document.msFullscreenElement || null; }, requestFullscreen(element, options) { if (typeof document === "undefined") throw new Error(this.DOM_UNAVAILABLE_ERROR); const target = element ?? document.documentElement; const method = target.requestFullscreen || target.webkitRequestFullscreen || target.webkitRequestFullScreen || target.mozRequestFullScreen || target.msRequestFullscreen; if (!method) throw new Error(this.FULLSCREEN_UNSUPPORTED_ERROR); return method.call(target, options); }, exitFullscreen() { if (typeof document === "undefined") throw new Error(this.DOM_UNAVAILABLE_ERROR); const _document = document; const method = _document.exitFullscreen || _document.webkitExitFullscreen || _document.webkitCancelFullScreen || _document.mozCancelFullScreen || _document.msExitFullscreen; if (!method) throw new Error(this.FULLSCREEN_UNSUPPORTED_ERROR); return method.call(_document); }, on(eventType, callback) { const eventName = this.getEventName(eventType); if (!eventName) return; document.addEventListener(eventName, callback); }, off(eventType, callback) { const eventName = this.getEventName(eventType); if (!eventName) return; document.removeEventListener(eventName, callback); } }; function useFullscreen(props = {}) { const { target, onChange, onError, requestFullScreenOptions } = props; const [isFullscreenAvailable, setIsFullscreenAvailable] = useState17(false); const [fullscreenElement, setFullscreenElement] = useState17( null ); const [isFullscreenEnabled, setIsFullscreenEnabled] = useState17(false); const enableFullscreen = useCallback18(() => { return FullscreenApi.requestFullscreen( target?.current || null, requestFullScreenOptions ); }, [target, requestFullScreenOptions]); const disableFullscreen = useCallback18(() => { return FullscreenApi.exitFullscreen(); }, []); const toggleFullscreen = useCallback18(() => { if (!!FullscreenApi.fullscreenElement) return disableFullscreen(); return enableFullscreen(); }, [enableFullscreen, disableFullscreen]); const onChangeHandler = useCallback18( (event) => { const fullscreenElement2 = FullscreenApi.fullscreenElement; setFullscreenElement(fullscreenElement2); setIsFullscreenEnabled(!!fullscreenElement2); onChange?.(event); }, [onChange] ); const onErrorHandler = useCallback18( (event) => { const fullscreenElement2 = FullscreenApi.fullscreenElement; setFullscreenElement(fullscreenElement2); setIsFullscreenEnabled(!!fullscreenElement2); onError?.(event); }, [onError] ); useEffect22(() => { setIsFullscreenAvailable(FullscreenApi.fullscreenEnabled); }, []); useEffect22(() => { FullscreenApi.on("change", onChangeHandler); FullscreenApi.on("error", onErrorHandler); return () => { FullscreenApi.off("change", onChangeHandler); FullscreenApi.off("error", onErrorHandler); }; }, [onChangeHandler, onErrorHandler]); return { isFullscreenAvailable, fullscreenElement, isFullscreenEnabled, enableFullscreen, disableFullscreen, toggleFullscreen }; } // src/hooks/useFocus.ts import { useCallback as useCallback19 } from "react"; var useFocus = (props) => { const { onBlur: propsOnBlur, onFocus: propsOnFocus, onFocusChange: propsOnFocusChange } = props; const onBlur = useCallback19( (e) => { if (e.target === e.currentTarget) { if (propsOnBlur) propsOnBlur(e); if (propsOnFocusChange) propsOnFocusChange(false); } }, [propsOnBlur, propsOnFocusChange] ); const onFocus = useCallback19( (e) => { if (e.target === e.currentTarget) { if (propsOnFocus) propsOnFocus(e); if (propsOnFocusChange) propsOnFocusChange(true); } }, [propsOnFocusChange, propsOnFocus] ); return { focusProps: { onFocus, onBlur } }; }; // src/hooks/useFocusWithin.ts import { useCallback as useCallback20, useRef as useRef11 } from "react"; var useFocusWithin = (props) => { const { onBlurWithin, onFocusWithin, onFocusWithinChange } = props; const state = useRef11({ isFocusWithin: false }); const onBlur = useCallback20( (e) => { if (state.current.isFocusWithin && !e.currentTarget.contains(e.relatedTarget)) { state.current.isFocusWithin = false; if (onBlurWithin) onBlurWithin(e); if (onFocusWithinChange) onFocusWithinChange(false); } }, [onBlurWithin, onFocusWithinChange] ); const onFocus = useCallback20( (e) => { if (!state.current.isFocusWithin) { if (onFocusWithin) onFocusWithin(e); if (onFocusWithinChange) onFocusWithinChange(true); state.current.isFocusWithin = true; } }, [onFocusWithin, onFocusWithinChange] ); return { focusWithinProps: { onFocus, onBlur } }; }; // src/hooks/useGeolocation.ts import { useEffect as useEffect23, useState as useState18 } from "react"; function getGeoLocation(options) { return new Promise((resolve, reject) => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { const { coords } = position; const { latitude, longitude } = coords; resolve({ isError: false, lat: latitude, lng: longitude, message: "" }); }, (error) => { resolve({ isError: true, message: error && typeof error === "object" && "message" in error ? error.message : "Geolocation error" }); }, options ); } else { resolve({ isError: true, message: "Geolocation is not supported for this Browser/OS." }); } }); } var defaultGeoLocationOptions = { enableHighAccuracy: false, maximumAge: 0, timeout: Number.POSITIVE_INFINITY, when: true }; var useGeolocation = (geoLocationOptions = defaultGeoLocationOptions) => { const [geoObject, setGeoObject] = useState18( null ); const { when, enableHighAccuracy, timeout, maximumAge } = geoLocationOptions; const getIsMounted = useGetIsMounted(); useEffect23(() => { async function getGeoCode() { try { const value = await getGeoLocation({ enableHighAccuracy, maximumAge, timeout, when }); if (getIsMounted()) { setGeoObject(value); } } catch (error) { if (getIsMounted()) { setGeoObject({ isError: true, message: error instanceof Error ? error.message : String(error) }); } } } if (when) { void getGeoCode(); } }, [when, enableHighAccuracy, timeout, maximumAge, getIsMounted]); return geoObject; }; // src/hooks/useInput.ts import { useState as useState19, useEffect as useEffect24, useCallback as useCallback21 } from "react"; var defaultOptions = {}; function useInput(initialValue = "", options = defaultOptions) { const [value, setValue] = useState19(initialValue); const onChange = useCallback21( (event) => { const newValue = event.target.value; let shouldUpdate = true; if (typeof options.validate === "function") { shouldUpdate = options.validate(newValue, value); } if (shouldUpdate) { setValue(newValue); } }, [options, value] ); useEffect24(() => { setValue(initialValue); }, [initialValue]); const handler = { onChange, value }; return handler; } // src/hooks/useIntersectionObserverRef.ts import { useEffect as useEffect25, useCallback as useCallback22, useState as useState20, useRef as useRef12 } from "react"; var config3 = { root: null, rootMargin: "0px 0px 0px 0px", threshold: [0, 1] }; function useIntersectionObserverRef(callback, options = config3) { const { root = null, rootMargin, threshold } = options; const [node, setNode] = useState20(null); const callbackRef = useRef12(callback); useEffect25(() => { callbackRef.current = callback; }); const handleIntersectionObserver = useCallback22( (...args) => { return callbackRef.current?.(...args); }, [] ); useEffect25(() => { if (node) { const observer = new IntersectionObserver(handleIntersectionObserver, { root, rootMargin, threshold }); observer.observe(node); return () => { observer.disconnect(); }; } return noop; }, [node, handleIntersectionObserver, root, rootMargin, threshold]); const ref = useCallback22((nodeElement) => { setNode(nodeElement); }, []); return [ref]; } // src/hooks/useInViewRef.ts import { useEffect as useEffect26, useCallback as useCallback23, useState as useState21 } from "react"; var config4 = { root: null, rootMargin: "0px 0px 0px 0px", threshold: [0, 1] }; function useInViewRef(callbackOrOptions, options) { const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : noop; const opts = typeof callbackOrOptions === "object" ? callbackOrOptions : options || config4; const { root = null, rootMargin, threshold } = opts; const [node, setNode] = useState21(null); const [inView, setInView] = useState21(false); useEffect26(() => { if (node) { const observer = new IntersectionObserver((entries, observerRef) => { for (const { isIntersecting } of entries) setInView(isIntersecting); callback(entries, observerRef); }, opts); observer.observe(node); return () => { observer.disconnect(); }; } return noop; }, [node, callback, root, rootMargin, threshold, opts]); const ref = useCallback23((nodeElement) => { setNode(nodeElement); }, []); return [ref, inView]; } // src/hooks/useIsDroppingFiles.ts import { useState as useState22, useEffect as useEffect27, useCallback as useCallback24 } from "react"; function useIsDroppingFiles(isTargetWindow = false) { const [targetNode, setTargetNode] = useState22(null); const ref = useCallback24((node) => { setTargetNode(node); }, []); const [isDroppingFiles, setIsDroppingFiles] = useState22(false); const handleDragEnter = useCallback24( (e) => { e.preventDefault(); if (e.dataTransfer?.types.includes("Files")) { if (!isDroppingFiles) { setIsDroppingFiles(true); } } else { setIsDroppingFiles(false); } }, [isDroppingFiles] ); const handleDragOver = useCallback24( (e) => { e.preventDefault(); if (e.dataTransfer?.types.includes("Files")) { if (!isDroppingFiles) { setIsDroppingFiles(true); } } else { setIsDroppingFiles(false); } }, [isDroppingFiles] ); const handleDragLeave = useCallback24((e) => { e.preventDefault(); setIsDroppingFiles(false); }, []); const handleDrop = useCallback24((e) => { e.preventDefault(); setIsDroppingFiles(false); }, []); const freshHandleDragEnter = useFreshCallback(handleDragEnter); const freshHandleDragOver = useFreshCallback(handleDragOver); const freshHandleDragLeave = useFreshCallback(handleDragLeave); const freshHandleDrop = useFreshCallback(handleDrop); useEffect27(() => { const target = isTargetWindow ? window : targetNode; if (target) { target.addEventListener("dragenter", freshHandleDragEnter); target.addEventListener("dragover", freshHandleDragOver); target.addEventListener("dragleave", freshHandleDragLeave); target.addEventListener("drop", freshHandleDrop); } return () => { if (target) { target.removeEventListener("dragenter", freshHandleDragEnter); target.removeEventListener("dragover", freshHandleDragOver); target.removeEventListener("dragleave", freshHandleDragLeave); target.removeEventListener("drop", freshHandleDrop); } }; }, [ freshHandleDragEnter, freshHandleDragLeave, freshHandleDragOver, freshHandleDrop, isDroppingFiles, isTargetWindow, targetNode ]); if (isTargetWindow) { return isDroppingFiles; } return [ref, isDroppingFiles]; } // src/hooks/useKey.ts import { useEffect as useEffect28, useCallback as useCallback25, useRef as useRef13, useMemo as useMemo4 } from "react"; // src/utils/doesIdentifierMatchKeyboardEvent.ts var doesIdentifierMatchKeyboardEvent = (error, identifier) => { if (error.key === identifier || error.code === identifier || error.keyCode === identifier || error.which === identifier || error.charCode === identifier) { return true; } return false; }; // src/hooks/useKey.ts var defaultOptions2 = { eventTypes: ["keydown"], when: true }; function useKey(keys, callback, options) { const keyList = useMemo4(() => { if (Array.isArray(keys)) { return keys; } else { return [keys]; } }, [keys]); const internalOptions = useMemo4(() => { return { ...defaultOptions2, ...options }; }, [options]); const { when, eventTypes } = internalOptions; const callbackRef = useRef13(callback); const { target } = internalOptions; useEffect28(() => { callbackRef.current = callback; }); const handle = useCallback25( (event) => { if (keyList.some( (identifier) => doesIdentifierMatchKeyboardEvent(event, identifier) )) { callbackRef.current(event); } }, [keyList] ); useEffect28(() => { if (when && typeof window !== "undefined") { if (target) { const targetNode = target.current; if (targ