rooks
Version:
Essential React custom hooks ⚓ to super charge your components!
1,719 lines (1,661 loc) • 137 kB
JavaScript
// 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