UNPKG

rooks

Version:

Collection of awesome react hooks

1,731 lines (1,669 loc) 172 kB
import { useBroadcastChannel } from "./chunk-4BDODBWX.js"; // 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/useAnimation.ts import { useState as useState4, useEffect as useEffect7, useRef as useRef3 } from "react"; // src/hooks/useRaf.ts import raf from "raf"; import { useRef as useRef2, useEffect as useEffect6 } from "react"; function useRaf(callback, isActive) { const savedCallback = useRef2(null); useEffect6(() => { savedCallback.current = callback; }, [callback]); useEffect6(() => { let animationFrame; let startTime = Date.now(); function tick() { const timeElapsed = Date.now() - startTime; startTime = Date.now(); loop(); savedCallback.current?.(timeElapsed); } function loop() { animationFrame = raf(tick); } if (isActive) { startTime = Date.now(); loop(); return () => { if (animationFrame) { raf.cancel(animationFrame); } }; } else { return noop; } }, [isActive]); } // src/hooks/useAnimation.ts var hasWarnedDeprecation = false; function useAnimation(options) { if (process.env.NODE_ENV === "development" && !hasWarnedDeprecation) { console.warn( "[rooks] useAnimation is deprecated and will be removed in a future major version. Please migrate to useEasing for better control and features. See: https://rooks.vercel.app/docs/hooks/useEasing" ); hasWarnedDeprecation = true; } const { duration, easing = (t) => t, delay = 0, loop = false } = options; const [value, setValue] = useState4(0); const startTimeRef = useRef3(null); const delayRef = useRef3(delay); useRaf(() => { if (startTimeRef.current === null) { startTimeRef.current = performance.now(); } const elapsed = performance.now() - startTimeRef.current; if (elapsed < delayRef.current) { return true; } const timeSinceStart = elapsed - delayRef.current; const progress = Math.min(timeSinceStart / duration, 1); const easedProgress = easing(progress); setValue(easedProgress); if (progress < 1) { return true; } if (loop) { startTimeRef.current = performance.now(); delayRef.current = 0; return true; } return false; }, true); useEffect7(() => { startTimeRef.current = null; delayRef.current = delay; }, [duration, easing, delay, loop]); return value; } // src/hooks/useArrayState.ts import { useCallback as useCallback5, useMemo, useState as useState5 } from "react"; function useArrayState(initial) { const [array, setArray] = useState5(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 useEffect9, useRef as useRef5, useCallback as useCallback7 } from "react"; // src/hooks/useGetIsMounted.ts import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef4 } from "react"; var useGetIsMounted = () => { const isMountedRef = useRef4(false); const get = useCallback6(() => isMountedRef.current, []); useEffect8(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); return get; }; // src/hooks/useAsyncEffect.ts function useAsyncEffect(effect, deps, cleanup) { const lastCallId = useRef5(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]); useEffect9(() => { let result; callback().then((value) => { result = value; }); return () => { cleanup?.(result); }; }, [callback, cleanup]); } // src/hooks/useAudio.ts import { useState as useState6, useEffect as useEffect10 } 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] = useState6(null); const [state, setState] = useState6({ 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); } }; useEffect10(() => { 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]); useEffect10(() => { 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 useState7, useCallback as useCallback8 } from "react"; // src/hooks/useDidMount.ts import { useEffect as useEffect11 } from "react"; function useDidMount(callback) { useEffect11(() => { if (typeof callback === "function") { callback(); } }, []); } // src/hooks/useMutationObserver.ts import { useEffect as useEffect12 } from "react"; var config = { attributes: true, characterData: true, childList: true, subtree: true }; function useMutationObserver(ref, callback, options = config) { useEffect12(() => { 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] = useState7(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 useState9, useEffect as useEffect14, 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 useEffect13, useCallback as useCallback9, useState as useState8 } from "react"; var config2 = { attributes: true, characterData: true, childList: true, subtree: true }; function useMutationObserverRef(callback, options = config2) { const [node, setNode] = useState8(null); useEffect13(() => { 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] = useState9(null); const [node, setNode] = useState9(null); const update = useCallback10(() => { setDomRect(node ? getBoundingClientRect2(node) : null); }, [node]); useEffect14(() => { update(); }, [update]); const ref = useCallback10((nodeElement) => { setNode(nodeElement); }, []); const [mutationObserverRef] = useMutationObserverRef(update); const forkedRef = useForkRef(ref, mutationObserverRef); return [forkedRef, domRect, update]; } // src/hooks/useCheckboxInputState.ts import { useState as useState10, useCallback as useCallback11, useMemo as useMemo3 } from "react"; function useCheckboxInputState(initialValue) { const [checked, setInternalChecked] = useState10(initialValue); const setChecked = useCallback11((newChecked) => { setInternalChecked(newChecked); }, []); const toggle = useCallback11(() => { setInternalChecked((prev) => !prev); }, []); const handleChange = useCallback11( (event) => { setInternalChecked(event.target.checked); }, [] ); const inputProps = useMemo3( () => ({ checked, onChange: handleChange }), [checked, handleChange] ); return { checked, toggle, setChecked, inputProps }; } // src/hooks/useClipboard.ts import { useState as useState11, useCallback as useCallback12 } from "react"; function useClipboard() { const [text, setText] = useState11(null); const [error, setError] = useState11(null); const isSupported = typeof window !== "undefined" && typeof navigator !== "undefined" && "clipboard" in navigator; const copy = useCallback12( async (value) => { if (!isSupported) { const err = new Error("Clipboard API is not supported"); setError(err); throw err; } try { await navigator.clipboard.writeText(value); setText(value); setError(null); } catch (err) { const error2 = err instanceof Error ? err : new Error("Failed to copy to clipboard"); setError(error2); throw error2; } }, [isSupported] ); const paste = useCallback12(async () => { if (!isSupported) { const err = new Error("Clipboard API is not supported"); setError(err); throw err; } try { const clipboardText = await navigator.clipboard.readText(); setText(clipboardText); setError(null); } catch (err) { const error2 = err instanceof Error ? err : new Error("Failed to read from clipboard"); setError(error2); throw error2; } }, [isSupported]); return { text, copy, paste, isSupported, error }; } // src/hooks/useCountdown.ts import { useState as useState12 } from "react"; // src/hooks/useIntervalWhen.ts import { useRef as useRef6, useEffect as useEffect15 } from "react"; function useIntervalWhen(callback, intervalDurationMs = 0, when = true, startImmediate = false) { const savedRefCallback = useRef6(void 0); useEffect15(() => { savedRefCallback.current = callback; }); useEffect15(() => { 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] = useState12(() => /* @__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 useCallback13, useState as useState13 } from "react"; function useCounter(initialValue) { const [counter, setCounter] = useState13(initialValue); const incrementBy = useCallback13((incrAmount) => { setCounter((currentCounter) => currentCounter + incrAmount); }, []); const decrementBy = useCallback13( (decrAmount) => { incrementBy(-decrAmount); }, [incrementBy] ); const increment = useCallback13(() => { incrementBy(1); }, [incrementBy]); const decrement = useCallback13(() => { incrementBy(-1); }, [incrementBy]); const reset = useCallback13(() => { setCounter(initialValue); }, [initialValue]); return { decrement, decrementBy, increment, incrementBy, reset, value: counter }; } // src/hooks/useDebounce.ts import debounce from "lodash.debounce"; import { useRef as useRef7, useCallback as useCallback14 } from "react"; // src/hooks/useWillUnmount.ts import { useEffect as useEffect16 } from "react"; function useWillUnmount(callback) { useEffect16(() => { return callback; }, []); } // src/hooks/useDebounce.ts function useDebounce(callback, wait, options) { const createDebouncedCallback = useCallback14( (function_) => { return debounce(function_, wait, options); }, [wait, options] ); const freshCallback = useFreshRef(callback); function tick(...args) { freshCallback.current?.(...args); } const debouncedCallbackRef = useRef7( createDebouncedCallback(tick) ); useWillUnmount(() => { debouncedCallbackRef.current?.cancel(); }); return debouncedCallbackRef.current; } // src/hooks/useDebounceFn.ts import { useRef as useRef8, useCallback as useCallback15, useState as useState14 } 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] = useState14(false); const [key, setKey] = useState14(0); const lastExecutionTimeRef = useRef8(0); const argsRef = useRef8(void 0); const debouncedFn = useCallback15( (...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/useDebouncedAsyncEffect.ts import { useEffect as useEffect17, useRef as useRef9, useCallback as useCallback16 } from "react"; function useDebouncedAsyncEffect(effect, deps, delay = 500, cleanup, options) { const lastCallId = useRef9(0); const getIsMounted = useGetIsMounted(); const effectRef = useFreshRef(effect); const resultRef = useRef9(void 0); const asyncCallback = useCallback16(async () => { const callId = ++lastCallId.current; const shouldContinueEffect = () => { return getIsMounted() && callId === lastCallId.current; }; try { const result = await effectRef.current(shouldContinueEffect); if (shouldContinueEffect()) { resultRef.current = result; } return result; } catch (error) { if (shouldContinueEffect()) { throw error; } } }, [getIsMounted, effectRef]); const debouncedAsyncCallback = useDebounce(asyncCallback, delay, options); const cleanupRef = useFreshRef(cleanup); useEffect17(() => { debouncedAsyncCallback(); const currentCleanup = cleanupRef.current; return () => { debouncedAsyncCallback.cancel(); if (currentCleanup) { currentCleanup(resultRef.current); } }; }, deps); } // src/hooks/useDebouncedEffect.ts import { useEffect as useEffect18, useRef as useRef10 } from "react"; function useDebouncedEffect(effect, deps, delay = 500, options) { const cleanupRef = useRef10(void 0); const wrappedEffect = () => { if (typeof cleanupRef.current === "function") { cleanupRef.current(); } cleanupRef.current = effect(); }; const debouncedEffect = useDebounce(wrappedEffect, delay, options); useEffect18(() => { debouncedEffect(); return () => { debouncedEffect.cancel(); if (typeof cleanupRef.current === "function") { cleanupRef.current(); cleanupRef.current = void 0; } }; }, deps); } // src/hooks/useDebouncedValue.ts import { useState as useState15 } from "react"; // src/hooks/useDidUpdate.ts import { useEffect as useEffect19, useMemo as useMemo4, useRef as useRef11 } from "react"; function useDidUpdate(callback, conditions) { const hasMountedRef = useRef11(false); const internalConditions = useMemo4(() => { 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]); useEffect19(() => { 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] = useState15( 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 useEffect20, useRef as useRef12 } 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 = useRef12(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; } useEffect20(callback, previousDeps.current); } // src/hooks/useDimensionsRef.ts import { useState as useState16, useCallback as useCallback17, useLayoutEffect as useLayoutEffect2 } from "react"; // src/hooks/useGlobalObjectEventListener.ts import { useEffect as useEffect21 } 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 : useEffect21; 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( typeof window !== "undefined" ? window : void 0, "resize", callback, { passive: true }, when, isLayoutEffect ); } // src/hooks/useOnWindowScroll.ts function useOnWindowScroll(callback, when = true, isLayoutEffect = false) { useGlobalObjectEventListener( typeof window !== "undefined" ? window : void 0, "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] = useState16(null); const [node, setNode] = useState16(null); const ref = useCallback17((nodeFromCallback) => { setNode(nodeFromCallback); }, []); const measure = useCallback17(() => { 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( typeof document !== "undefined" ? document : void 0, eventName, callback, listenerOptions, true, isLayoutEffect ); } // src/hooks/useDocumentTitle.ts import { useEffect as useEffect22, useRef as useRef13 } from "react"; function useDocumentTitle(title, options = {}) { const isBrowser = typeof window !== "undefined"; const prevTitleRef = useRef13(isBrowser ? document.title : ""); const { resetOnUnmount = false } = options; useEffect22(() => { if (isBrowser) { document.title = title; const lastTitle = prevTitleRef.current; if (resetOnUnmount) { return () => { document.title = lastTitle; }; } } return noop; }, [title, isBrowser, resetOnUnmount]); } // src/hooks/useEasing.ts import { useState as useState17, useEffect as useEffect23, useRef as useRef14, useCallback as useCallback18, useMemo as useMemo5 } from "react"; var Easing = { linear: (t) => t, easeInQuad: (t) => t * t, easeOutQuad: (t) => t * (2 - t), easeInOutQuad: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, easeInCubic: (t) => t * t * t, easeOutCubic: (t) => --t * t * t + 1, easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1 }; function useEasing(duration, options = {}) { const { easing = Easing.linear, autoStart = true, loop = false, alternate = false, delay = 0, onEnd } = options; const [progress, setProgress] = useState17(0); const [state, setState] = useState17(autoStart ? "running" : "idle"); const [direction, setDirection] = useState17("forward"); const [endCount, setEndCount] = useState17(0); const startTimeRef = useRef14(null); const pausedRawProgressRef = useRef14(0); const delayRemainingRef = useRef14(delay); const isFirstRunRef = useRef14(true); const hasCompletedRef = useRef14(false); const currentRawProgressRef = useRef14(0); const onEndRef = useRef14(onEnd); onEndRef.current = onEnd; const directionRef = useRef14(direction); directionRef.current = direction; const start = useCallback18(() => { if (state === "running") return; startTimeRef.current = null; hasCompletedRef.current = false; setState("running"); }, [state]); const stop = useCallback18(() => { if (state === "idle") return; pausedRawProgressRef.current = currentRawProgressRef.current; setState("idle"); }, [state]); const reset = useCallback18(() => { setState("idle"); setProgress(0); setDirection("forward"); setEndCount(0); startTimeRef.current = null; pausedRawProgressRef.current = 0; currentRawProgressRef.current = 0; delayRemainingRef.current = delay; isFirstRunRef.current = true; hasCompletedRef.current = false; directionRef.current = "forward"; }, [delay]); const restart = useCallback18(() => { reset(); setTimeout(() => { setState("running"); }, 0); }, [reset]); useRaf(() => { if (startTimeRef.current === null) { const resumeOffset = pausedRawProgressRef.current * duration; startTimeRef.current = performance.now() - resumeOffset; } const elapsed = performance.now() - startTimeRef.current; if (isFirstRunRef.current && delayRemainingRef.current > 0) { if (elapsed < delayRemainingRef.current) { return; } startTimeRef.current = performance.now() - (elapsed - delayRemainingRef.current); delayRemainingRef.current = 0; isFirstRunRef.current = false; } const adjustedElapsed = performance.now() - startTimeRef.current; const rawProgress = Math.min(adjustedElapsed / duration, 1); currentRawProgressRef.current = rawProgress; const directionalProgress = directionRef.current === "forward" ? rawProgress : 1 - rawProgress; const easedProgress = easing(directionalProgress); setProgress(easedProgress); if (rawProgress >= 1 && !hasCompletedRef.current) { hasCompletedRef.current = true; setEndCount((prev) => prev + 1); onEndRef.current?.(); if (loop) { startTimeRef.current = performance.now(); hasCompletedRef.current = false; if (alternate) { const newDirection = directionRef.current === "forward" ? "backward" : "forward"; directionRef.current = newDirection; setDirection(newDirection); } } else { setState("idle"); } } }, state === "running"); useEffect23(() => { if (autoStart) { setState("running"); } }, []); const controls = useMemo5( () => ({ start, stop, reset, restart, state, direction, endCount }), [start, stop, reset, restart, state, direction, endCount] ); return [progress, controls]; } // 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 useEffect24, useRef as useRef15 } from "react"; function useEffectOnceWhen(callback, when = true) { const hasRunOnceRef = useRef15(false); const callbackRef = useRef15(callback); useEffect24(() => { callbackRef.current = callback; }); useEffect24(() => { if (when && !hasRunOnceRef.current) { callbackRef.current(); hasRunOnceRef.current = true; } }, [when]); } // src/hooks/useEventListenerRef.ts import { useEffect as useEffect25 } from "react"; // src/hooks/useRefElement.ts import { useCallback as useCallback19, useState as useState18 } from "react"; function useRefElement() { const [refElement, setRefElement] = useState18(null); const ref = useCallback19( (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 : useEffect25; 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 useCallback20, useState as useState19, useEffect as useEffect26 } 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] = useState19(null); const freshOnDrop = useFreshCallback(onDrop); const freshOnFileAccepted = useFreshCallback(onFileAccepted); const freshOnFileRejected = useFreshCallback(onFileRejected); const freshOnDragEnter = useFreshCallback(onDragEnter); const freshOnDragLeave = useFreshCallback(onDragLeave); useCallback20((node) => { setTargetNode(node); }, []); const fileIsValid = useCallback20( (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 = useCallback20( (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, freshOnFileReje