UNPKG

rooks

Version:

Essential React custom hooks ⚓ to super charge your components!

1,719 lines (1,661 loc) 137 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, volume: initialVolume = 1, playbackRate: initialPlaybackRate = 1, loop: initialLoop = false, preload = "metadata" } = options; const [audioNode, setAudioNode] = useState5(null); const [state, setState] = useState5({ isPlaying: false, isMuted: initialIsMuted, volume: initialVolume, currentTime: 0, duration: 0, playbackRate: initialPlaybackRate, isLoading: false, isBuffering: false, loop: initialLoop, hasError: false }); const onPlay = useFreshCallback(callbacks.onPlay ?? noop2); const onPause = useFreshCallback(callbacks.onPause ?? noop2); const onEnded = useFreshCallback(callbacks.onEnded ?? noop2); const onMute = useFreshCallback(callbacks.onMute ?? noop2); const onUnmute = useFreshCallback(callbacks.onUnmute ?? noop2); const onLoadedMetadata = useFreshCallback(callbacks.onLoadedMetadata ?? noop2); const onTimeUpdate = useFreshCallback(callbacks.onTimeUpdate ?? noop2); const onDurationChange = useFreshCallback(callbacks.onDurationChange ?? noop2); const onVolumeChange = useFreshCallback(callbacks.onVolumeChange ?? noop2); const onRateChange = useFreshCallback(callbacks.onRateChange ?? noop2); const onError = useFreshCallback(callbacks.onError ?? noop2); const onLoadStart = useFreshCallback(callbacks.onLoadStart ?? noop2); const onCanPlay = useFreshCallback(callbacks.onCanPlay ?? noop2); const onWaiting = useFreshCallback(callbacks.onWaiting ?? noop2); const audioCallbackRef = (node) => { if (node !== null) { setAudioNode(node); } }; useEffect8(() => { if (!audioNode) return; audioNode.muted = state.isMuted; audioNode.volume = state.volume; audioNode.playbackRate = state.playbackRate; audioNode.loop = state.loop; audioNode.preload = preload; if (autoPlay) { audioNode.autoplay = true; } }, [audioNode, autoPlay, preload]); useEffect8(() => { if (!audioNode) return; const handleLoadStart = () => { setState((prev) => ({ ...prev, isLoading: true, hasError: false })); onLoadStart(); }; const handleLoadedMetadata = () => { setState((prev) => ({ ...prev, duration: audioNode.duration, isLoading: false, hasError: false })); onLoadedMetadata(); onDurationChange(audioNode.duration); }; const handleCanPlay = () => { setState((prev) => ({ ...prev, isBuffering: false, hasError: false })); onCanPlay(); }; const handleWaiting = () => { setState((prev) => ({ ...prev, isBuffering: true })); onWaiting(); }; const handlePlay = () => { setState((prev) => ({ ...prev, isPlaying: true, hasError: false })); onPlay(); }; const handlePause = () => { setState((prev) => ({ ...prev, isPlaying: false })); onPause(); }; const handleEnded = () => { setState((prev) => ({ ...prev, isPlaying: false })); onEnded(); }; const handleTimeUpdate = () => { const currentTime = audioNode.currentTime; setState((prev) => ({ ...prev, currentTime })); onTimeUpdate(currentTime); }; const handleDurationChange = () => { const duration = audioNode.duration; setState((prev) => ({ ...prev, duration })); onDurationChange(duration); }; const handleVolumeChange = () => { const volume = audioNode.volume; const isMuted = audioNode.muted; setState((prev) => { const newState = { ...prev, volume, isMuted }; if (isMuted !== prev.isMuted) { if (isMuted) { onMute(); } else { onUnmute(); } } return newState; }); onVolumeChange(volume); }; const handleRateChange = () => { const playbackRate = audioNode.playbackRate; setState((prev) => ({ ...prev, playbackRate })); onRateChange(playbackRate); }; const handleError = () => { const error = audioNode.error; const errorMessage = error ? `Audio error (${error.code}): ${error.message}` : "Unknown audio error occurred"; setState((prev) => ({ ...prev, hasError: true, error: errorMessage, isLoading: false, isBuffering: false, isPlaying: false })); onError(errorMessage); }; audioNode.addEventListener("loadstart", handleLoadStart); audioNode.addEventListener("loadedmetadata", handleLoadedMetadata); audioNode.addEventListener("canplay", handleCanPlay); audioNode.addEventListener("waiting", handleWaiting); audioNode.addEventListener("play", handlePlay); audioNode.addEventListener("pause", handlePause); audioNode.addEventListener("ended", handleEnded); audioNode.addEventListener("timeupdate", handleTimeUpdate); audioNode.addEventListener("durationchange", handleDurationChange); audioNode.addEventListener("volumechange", handleVolumeChange); audioNode.addEventListener("ratechange", handleRateChange); audioNode.addEventListener("error", handleError); return () => { audioNode.removeEventListener("loadstart", handleLoadStart); audioNode.removeEventListener("loadedmetadata", handleLoadedMetadata); audioNode.removeEventListener("canplay", handleCanPlay); audioNode.removeEventListener("waiting", handleWaiting); audioNode.removeEventListener("play", handlePlay); audioNode.removeEventListener("pause", handlePause); audioNode.removeEventListener("ended", handleEnded); audioNode.removeEventListener("timeupdate", handleTimeUpdate); audioNode.removeEventListener("durationchange", handleDurationChange); audioNode.removeEventListener("volumechange", handleVolumeChange); audioNode.removeEventListener("ratechange", handleRateChange); audioNode.removeEventListener("error", handleError); }; }, [ audioNode, onLoadStart, onLoadedMetadata, onCanPlay, onWaiting, onPlay, onPause, onEnded, onTimeUpdate, onDurationChange, onVolumeChange, onRateChange, onError, onMute, onUnmute ]); const play = () => { if (!audioNode) return; audioNode.play().catch((error) => { const errorMessage = error instanceof Error ? error.message : "Failed to play audio"; setState((prev) => ({ ...prev, hasError: true, error: errorMessage, isPlaying: false })); onError(errorMessage); }); }; const pause = () => { if (!audioNode) return; audioNode.pause(); }; const togglePlay = () => { if (state.isPlaying) { pause(); } else { play(); } }; const mute = () => { if (!audioNode) return; audioNode.muted = true; }; const unmute = () => { if (!audioNode) return; audioNode.muted = false; }; const toggleMute = () => { if (state.isMuted) { unmute(); } else { mute(); } }; const setVolume = (volume) => { if (!audioNode) return; const clampedVolume = Math.max(0, Math.min(1, volume)); audioNode.volume = clampedVolume; }; const setCurrentTime = (time) => { if (!audioNode) return; const clampedTime = Math.max(0, Math.min(state.duration || 0, time)); audioNode.currentTime = clampedTime; }; const setPlaybackRate = (rate) => { if (!audioNode) return; const clampedRate = Math.max(0.25, Math.min(4, rate)); audioNode.playbackRate = clampedRate; }; const seek = (seconds) => { setCurrentTime(state.currentTime + seconds); }; const fastForward = (seconds = 10) => { seek(seconds); }; const rewind = (seconds = 10) => { seek(-seconds); }; const setLoop = (loop) => { if (!audioNode) return; audioNode.loop = loop; setState((prev) => ({ ...prev, loop })); }; const controls = { play, pause, togglePlay, mute, unmute, toggleMute, setVolume, setCurrentTime, setPlaybackRate, seek, fastForward, rewind, setLoop }; 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 { useSyncExternalStore } from "react"; function getVisibilityStateSnapshot() { if (typeof document !== "undefined") { return document.visibilityState; } else { return null; } } function getVisibilityStateSubscription(callback) { if (typeof document !== "undefined") { document.addEventListener("visibilitychange", callback); return () => { document.removeEventListener("visibilitychange", callback); }; } return () => { }; } function getVisibilityStateServerSnapshot() { return null; } function useDocumentVisibilityState() { return useSyncExternalStore(getVisibilityStateSubscription, getVisibilityStateSnapshot, getVisibilityStateServerSnapshot); } // 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 useCallback15, useState as useState14 } from "react"; function useRefElement() { const [refElement, setRefElement] = useState14(null); const ref = useCallback15( (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 useCallback16, useState as useState15, 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] = useState15(null); const freshOnDrop = useFreshCallback(onDrop); const freshOnFileAccepted = useFreshCallback(onFileAccepted); const freshOnFileRejected = useFreshCallback(onFileRejected); const freshOnDragEnter = useFreshCallback(onDragEnter); const freshOnDragLeave = useFreshCallback(onDragLeave); useCallback16((node) => { setTargetNode(node); }, []); const fileIsValid = useCallback16( (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 = useCallback16( (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 = useCallback16((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 useCallback16((node) => { setTargetNode(node); }, []); } // src/hooks/useFullscreen.ts import { useState as useState16, useCallback as useCallback17, 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] = useState16(false); const [fullscreenElement, setFullscreenElement] = useState16( null ); const [isFullscreenEnabled, setIsFullscreenEnabled] = useState16(false); const enableFullscreen = useCallback17(() => { return FullscreenApi.requestFullscreen( target?.current || null, requestFullScreenOptions ); }, [target, requestFullScreenOptions]); const disableFullscreen = useCallback17(() => { return FullscreenApi.exitFullscreen(); }, []); const toggleFullscreen = useCallback17(() => { if (!!FullscreenApi.fullscreenElement) return disableFullscreen(); return enableFullscreen(); }, [enableFullscreen, disableFullscreen]); const onChangeHandler = useCallback17( (event) => { const fullscreenElement2 = FullscreenApi.fullscreenElement; setFullscreenElement(fullscreenElement2); setIsFullscreenEnabled(!!fullscreenElement2); onChange?.(event); }, [onChange] ); const onErrorHandler = useCallback17( (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 useCallback18 } from "react"; var useFocus = (props) => { const { onBlur: propsOnBlur, onFocus: propsOnFocus, onFocusChange: propsOnFocusChange } = props; const onBlur = useCallback18( (e) => { if (e.target === e.currentTarget) { if (propsOnBlur) propsOnBlur(e); if (propsOnFocusChange) propsOnFocusChange(false); } }, [propsOnBlur, propsOnFocusChange] ); const onFocus = useCallback18( (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 useCallback19, useRef as useRef11 } from "react"; var useFocusWithin = (props) => { const { onBlurWithin, onFocusWithin, onFocusWithinChange } = props; const state = useRef11({ isFocusWithin: false }); const onBlur = useCallback19( (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 = useCallback19( (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 useState17 } 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] = useState17( 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/useIdleDetectionApi.ts import { useCallback as useCallback20, useEffect as useEffect24, useRef as useRef12, useState as useState18 } from "react"; var IdlePolyfill = class { threshold; isIdle = false; _userState = "active"; _screenState = "unlocked"; listeners = []; timeoutId = null; started = false; constructor(threshold) { this.threshold = Math.max(threshold, 6e4); } handleUserActivity = () => { if (this.isIdle) { this.isIdle = false; this._userState = "active"; this.notifyListeners(); } this.resetIdleTimer(); }; handleVisibilityChange = () => { this._screenState = document.hidden ? "locked" : "unlocked"; this.notifyListeners(); }; resetIdleTimer() { if (this.timeoutId) { clearTimeout(this.timeoutId); } this.timeoutId = window.setTimeout(() => { this.isIdle = true; this._userState = "idle"; this.notifyListeners(); }, this.threshold); } notifyListe