rooks
Version:
Collection of awesome react hooks
1,731 lines (1,669 loc) • 172 kB
JavaScript
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