@qazuor/react-hooks
Version:
A comprehensive collection of production-ready React hooks for modern web applications. Features type-safe implementations, extensive testing, and zero dependencies. Includes hooks for state management, browser APIs, user interactions, and development uti
1,040 lines (1,018 loc) • 32.4 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.tsx
var src_exports = {};
__export(src_exports, {
useBoolean: () => useBoolean,
useClickOutside: () => useClickOutside,
useCopyToClipboard: () => useCopyToClipboard,
useDebounce: () => useDebounce,
useHandledInterval: () => useHandledInterval,
useIdleness: () => useIdleness,
useInterval: () => useInterval,
useLocalStorage: () => useLocalStorage,
useLockBodyScroll: () => useLockBodyScroll,
useLogger: () => useLogger,
useMeasure: () => useMeasure,
useMediaQuery: () => useMediaQuery,
useNetworkState: () => useNetworkState,
usePageLeave: () => usePageLeave,
useQueue: () => useQueue,
useSessionStorage: () => useSessionStorage,
useTimeout: () => useTimeout,
useToggle: () => useToggle,
useVisibilityChange: () => useVisibilityChange,
useWindowWidth: () => useWindowWidth
});
module.exports = __toCommonJS(src_exports);
// src/hooks/useBoolean.ts
var import_react = require("react");
function useBoolean(initialValue = false) {
const [value, setValue] = (0, import_react.useState)(initialValue);
const setTrue = (0, import_react.useCallback)(() => setValue(true), []);
const setFalse = (0, import_react.useCallback)(() => setValue(false), []);
const toggle = (0, import_react.useCallback)(() => setValue((v) => !v), []);
const setValueDirect = (0, import_react.useCallback)((newValue) => setValue(newValue), []);
return { value, setTrue, setFalse, toggle, setValue: setValueDirect };
}
// src/hooks/useClickOutside.ts
var import_react2 = require("react");
function useClickOutside(ref, handler, { enabled = true, eventType = "mousedown" } = {}) {
const handleClickOutside = (0, import_react2.useCallback)(
(event) => {
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
},
[ref, handler]
);
(0, import_react2.useEffect)(() => {
if (!enabled) {
return;
}
document.addEventListener(eventType, handleClickOutside);
return () => {
document.removeEventListener(eventType, handleClickOutside);
};
}, [ref, handler, enabled, eventType, handleClickOutside]);
}
// src/hooks/useCopyToClipboard.ts
var import_react3 = require("react");
function useCopyToClipboard() {
const [state, setState] = (0, import_react3.useState)({
copied: false,
error: null
});
const timeoutRef = (0, import_react3.useRef)(null);
const copy = (0, import_react3.useCallback)(async (text) => {
try {
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
}
await navigator.clipboard.writeText(text);
setState({ copied: true, error: null });
timeoutRef.current = window.setTimeout(() => {
setState((prev) => ({ ...prev, copied: false }));
}, 2e3);
} catch (err) {
setState({ copied: false, error: err });
}
}, []);
const reset = (0, import_react3.useCallback)(() => {
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
}
setState({ copied: false, error: null });
}, []);
(0, import_react3.useEffect)(() => {
return () => {
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
}
};
}, []);
return { copy, reset, ...state };
}
// src/hooks/useDebounce.ts
var import_react4 = require("react");
function useDebounce(value, delay, immediate = false) {
const [debouncedValue, setDebouncedValue] = (0, import_react4.useState)(value);
const timeoutRef = (0, import_react4.useRef)(null);
(0, import_react4.useEffect)(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
if (immediate) {
setDebouncedValue(value);
return;
}
timeoutRef.current = window.setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [value, delay, immediate]);
return debouncedValue;
}
// src/hooks/useHandledInterval.ts
var import_react5 = require("react");
function useHandledInterval({
callback,
delay,
random = false,
autoStart = true,
minDelay = 0
}) {
const callbackRef = (0, import_react5.useRef)(callback);
const [currentDelay, setCurrentDelay] = (0, import_react5.useState)(delay);
const [isRunning, setIsRunning] = (0, import_react5.useState)(false);
const intervalId = (0, import_react5.useRef)(null);
const nextIntervalDelay = (0, import_react5.useRef)(null);
const getRandomDelay = (0, import_react5.useCallback)(() => {
if (!random) {
return currentDelay;
}
const range = currentDelay - minDelay;
return Math.floor(Math.random() * range) + minDelay;
}, [currentDelay, random, minDelay]);
const clear = (0, import_react5.useCallback)(() => {
if (intervalId.current)
clearInterval(intervalId.current);
intervalId.current = null;
nextIntervalDelay.current = null;
}, []);
const start = (0, import_react5.useCallback)(() => {
if (intervalId.current)
return;
setIsRunning(true);
nextIntervalDelay.current = getRandomDelay();
intervalId.current = window.setInterval(() => {
callbackRef.current();
if (random) {
clear();
nextIntervalDelay.current = getRandomDelay();
intervalId.current = window.setInterval(callbackRef.current, nextIntervalDelay.current);
}
}, nextIntervalDelay.current);
}, [random, getRandomDelay, clear]);
const pause = (0, import_react5.useCallback)(() => {
setIsRunning(false);
clear();
}, [clear]);
const reset = (0, import_react5.useCallback)(() => {
pause();
start();
}, [pause, start]);
const setDelay = (0, import_react5.useCallback)(
(newDelay) => {
setCurrentDelay(newDelay);
if (isRunning) {
clear();
start();
}
},
[isRunning, clear, start]
);
(0, import_react5.useEffect)(() => {
callbackRef.current = callback;
}, [callback]);
(0, import_react5.useEffect)(() => {
if (autoStart) {
start();
}
return () => clear();
}, [autoStart, start, clear]);
return { isRunning, start, pause, reset, setDelay };
}
// src/hooks/useIdleness.ts
var import_react6 = require("react");
function useIdleness({
timeout,
events = ["mousemove", "keydown", "wheel", "touchstart"],
startImmediately = true,
onIdleChange
}) {
const [idle, setIdle] = (0, import_react6.useState)(false);
const [isMonitoring, setIsMonitoring] = (0, import_react6.useState)(startImmediately);
const timerId = (0, import_react6.useRef)(null);
const mounted = (0, import_react6.useRef)(true);
const eventsRef = (0, import_react6.useRef)(events);
const clearTimer = (0, import_react6.useCallback)(() => {
if (timerId.current) {
clearTimeout(timerId.current);
timerId.current = null;
}
}, []);
const setIdleState = (0, import_react6.useCallback)(
(newState) => {
if (mounted.current && idle !== newState) {
setIdle(newState);
onIdleChange?.(newState);
}
},
[idle, onIdleChange]
);
const resetTimer = (0, import_react6.useCallback)(() => {
clearTimer();
setIdleState(false);
if (isMonitoring) {
timerId.current = setTimeout(() => {
setIdleState(true);
}, timeout);
}
}, [timeout, isMonitoring, clearTimer, setIdleState]);
const start = (0, import_react6.useCallback)(() => {
setIsMonitoring(true);
resetTimer();
resetTimer();
}, [resetTimer]);
const stop = (0, import_react6.useCallback)(() => {
setIsMonitoring(false);
clearTimer();
setIdleState(false);
}, [clearTimer, setIdleState]);
(0, import_react6.useEffect)(() => {
if (isMonitoring) {
eventsRef.current.forEach((evt) => window.addEventListener(evt, resetTimer));
timerId.current = setTimeout(() => {
setIdleState(true);
}, timeout);
}
return () => {
clearTimer();
eventsRef.current.forEach((evt) => window.removeEventListener(evt, resetTimer));
};
}, [isMonitoring, resetTimer, clearTimer, timeout, setIdleState]);
(0, import_react6.useEffect)(
() => () => {
mounted.current = false;
},
[]
);
return { isIdle: idle, start, stop, reset: resetTimer };
}
// src/hooks/useInterval.ts
var import_react7 = require("react");
function useInterval({
callback,
delay,
runImmediately = false,
autoStart = true
}) {
const savedCallback = (0, import_react7.useRef)(callback);
const [isRunning, setIsRunning] = (0, import_react7.useState)(false);
const intervalId = (0, import_react7.useRef)(null);
(0, import_react7.useEffect)(() => {
savedCallback.current = callback;
}, [callback]);
const cleanup = (0, import_react7.useCallback)(() => {
if (intervalId.current !== null) {
clearInterval(intervalId.current);
intervalId.current = null;
}
}, []);
const start = (0, import_react7.useCallback)(() => {
if (delay === null) {
setIsRunning(false);
return;
}
if (intervalId.current !== null) {
return;
}
setIsRunning(true);
if (runImmediately) {
savedCallback.current();
}
intervalId.current = window.setInterval(() => {
savedCallback.current();
}, delay);
}, [delay, runImmediately]);
const pause = (0, import_react7.useCallback)(() => {
cleanup();
setIsRunning(false);
}, [cleanup]);
const restart = (0, import_react7.useCallback)(() => {
cleanup();
start();
}, [cleanup, start]);
(0, import_react7.useEffect)(() => {
if (autoStart) {
start();
}
return cleanup;
}, [autoStart, start, cleanup]);
return { isRunning, start, pause, restart };
}
// src/hooks/useLocalStorage.ts
var import_react8 = require("react");
function useLocalStorage(key, initialValue, options = {}) {
const {
serializer = JSON.stringify,
deserializer = JSON.parse,
onError = console.error,
syncTabs = false
} = options;
const mounted = (0, import_react8.useRef)(true);
const [storedValue, setStoredValue] = (0, import_react8.useState)(() => {
try {
const item = window.localStorage.getItem(key);
if (item) {
return deserializer(item);
}
window.localStorage.setItem(key, serializer(initialValue));
return initialValue;
} catch (error) {
onError(error);
return initialValue;
}
});
const setValue = (0, import_react8.useCallback)(
(value) => {
setStoredValue((prev) => {
try {
const newValue = value instanceof Function ? value(prev) : value;
window.localStorage.setItem(key, serializer(newValue));
return newValue;
} catch (error) {
onError(error);
return prev;
}
});
},
[key, serializer, onError]
);
const handleStorageChange = (0, import_react8.useCallback)(
(event) => {
if (event.key === key && event.newValue !== null && mounted.current) {
try {
const newValue = deserializer(event.newValue);
setStoredValue(newValue);
} catch (error) {
onError(error);
}
}
},
[key, deserializer, onError]
);
(0, import_react8.useEffect)(() => {
if (syncTabs) {
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}
}, [syncTabs, handleStorageChange]);
(0, import_react8.useEffect)(
() => () => {
mounted.current = false;
},
[]
);
return [storedValue, setValue];
}
// src/hooks/useLockBodyScroll.ts
var import_react9 = require("react");
var isTestEnv = () => true;
function useLockBodyScroll({
preservePosition = true,
lockImmediately = true,
additionalStyles = {}
} = {}) {
const [isLocked, setIsLocked] = (0, import_react9.useState)(lockImmediately);
const [originalStyles, setOriginalStyles] = (0, import_react9.useState)({});
const [scrollPosition, setScrollPosition] = (0, import_react9.useState)(0);
const applyStyles = (0, import_react9.useCallback)((styles) => {
Object.entries(styles).forEach(([key, value]) => {
document.body.style[key] = value?.toString() ?? "";
});
}, []);
const saveScrollPosition = (0, import_react9.useCallback)(() => {
if (preservePosition) {
setScrollPosition(window.pageYOffset);
}
}, [preservePosition]);
const restoreScrollPosition = (0, import_react9.useCallback)(() => {
if (preservePosition) {
try {
if (!isTestEnv()) {
window.scrollTo(0, scrollPosition);
}
} catch (error) {
if (!isTestEnv()) {
console.error(error);
}
}
}
}, [preservePosition, scrollPosition]);
const lock = (0, import_react9.useCallback)(() => {
if (!isLocked) {
saveScrollPosition();
const originalStyle = {};
["overflow", "position", "top", "width"].forEach((prop) => {
originalStyle[prop] = document.body.style[prop];
});
Object.keys(additionalStyles).forEach((key) => {
originalStyle[key] = document.body.style[key];
});
setOriginalStyles(originalStyle);
const lockStyles = {
overflow: "hidden",
position: "fixed",
top: `-${scrollPosition}px`,
width: "100%",
...additionalStyles
};
applyStyles(lockStyles);
setIsLocked(true);
}
}, [isLocked, scrollPosition, additionalStyles, saveScrollPosition, applyStyles]);
const unlock = (0, import_react9.useCallback)(() => {
if (isLocked) {
applyStyles(originalStyles);
restoreScrollPosition();
setIsLocked(false);
}
}, [isLocked, originalStyles, restoreScrollPosition, applyStyles]);
const toggle = (0, import_react9.useCallback)(() => {
if (isLocked) {
unlock();
} else {
lock();
}
}, [isLocked, lock, unlock]);
(0, import_react9.useEffect)(() => {
if (lockImmediately) {
lock();
if (isTestEnv()) {
document.body.style.overflow = "hidden";
document.body.style.position = "fixed";
document.body.style.top = scrollPosition > 0 ? `-${scrollPosition}px` : "-0px";
document.body.style.width = "100%";
Object.entries(additionalStyles).forEach(([key, value]) => {
document.body.style[key] = value;
});
}
}
return unlock;
}, [lockImmediately, lock, unlock, scrollPosition, additionalStyles]);
return { isLocked, lock, unlock, toggle };
}
// src/hooks/useLogger.ts
var import_react10 = require("react");
function useLogger(label, value, options = {}) {
const { level = "info", timestamp = true, enabled = true, formatter } = options;
const prevValue = (0, import_react10.useRef)(null);
const isFirstRender = (0, import_react10.useRef)(true);
const getTimestamp = (0, import_react10.useCallback)(() => {
if (!timestamp)
return "";
return `[${(/* @__PURE__ */ new Date()).toISOString()}] `;
}, [timestamp]);
const formatMessage = (0, import_react10.useCallback)(
(msg) => {
if (formatter) {
return formatter(label, msg);
}
return `${getTimestamp()}[${label}] ${JSON.stringify(msg)}`;
},
[label, getTimestamp, formatter]
);
const log = (0, import_react10.useCallback)(() => {
if (!enabled)
return;
const message = formatMessage(value);
switch (level) {
case "warn":
console.warn(message);
break;
case "error":
console.error(message);
break;
case "debug":
console.debug(message);
break;
default:
console.info(message);
}
}, [enabled, level, value, formatMessage]);
(0, import_react10.useEffect)(() => {
if (isFirstRender.current || value !== prevValue.current) {
log();
isFirstRender.current = false;
prevValue.current = value;
}
}, [value, log]);
return log;
}
// src/hooks/useMeasure.ts
var import_react11 = require("react");
function useMeasure() {
const [size, setSize] = (0, import_react11.useState)({ width: 0, height: 0 });
const observerRef = (0, import_react11.useRef)({ observer: null, element: null });
const ref = (0, import_react11.useCallback)((node) => {
if (!node)
return;
if (observerRef.current.observer) {
observerRef.current.observer.disconnect();
}
const observer = new ResizeObserver(([entry]) => {
setSize({
width: entry.contentRect.width,
height: entry.contentRect.height
});
});
observerRef.current = { observer, element: node };
observer.observe(node);
return () => observer.disconnect();
}, []);
(0, import_react11.useEffect)(() => {
return () => {
if (observerRef.current.observer) {
observerRef.current.observer.disconnect();
}
};
}, []);
return { ref, size };
}
// src/hooks/useMediaQuery.ts
var import_react12 = require("react");
function useMediaQuery(query, { ssrSafe = true, ssrDefaultValue = false, watchImmediately = true } = {}) {
const [shouldListen, setShouldListen] = (0, import_react12.useState)(watchImmediately);
const [matches, setMatches] = (0, import_react12.useState)(() => {
if (ssrSafe && typeof window === "undefined") {
return ssrDefaultValue;
}
if (typeof window !== "undefined" && window.matchMedia) {
try {
return window.matchMedia(query).matches;
} catch (e) {
console.error("Failed to get initial media query match:", query, e);
return ssrDefaultValue;
}
}
return ssrDefaultValue;
});
const handleChange = (0, import_react12.useCallback)((e) => {
setMatches(e.matches);
}, []);
const startWatching = (0, import_react12.useCallback)(() => {
if (typeof window !== "undefined" && window.matchMedia !== void 0) {
setShouldListen(true);
} else {
console.warn("Cannot start watching media query: window.matchMedia is not available.");
}
}, []);
const stopWatching = (0, import_react12.useCallback)(() => {
setShouldListen(false);
}, []);
(0, import_react12.useEffect)(() => {
let mediaQueryList = null;
let handler = null;
if (shouldListen && typeof window !== "undefined" && window.matchMedia !== void 0) {
try {
mediaQueryList = window.matchMedia(query);
setMatches(mediaQueryList.matches);
handler = handleChange;
if (mediaQueryList.addEventListener) {
mediaQueryList.addEventListener("change", handler);
} else {
mediaQueryList.addListener(handler);
}
} catch (e) {
console.error("Failed to set up media query listener:", query, e);
setShouldListen(false);
}
}
return () => {
if (mediaQueryList && handler) {
try {
if (mediaQueryList.removeEventListener) {
mediaQueryList.removeEventListener("change", handler);
} else {
mediaQueryList.removeListener(handler);
}
} catch (e) {
console.error("Failed to remove media query listener during cleanup:", query, e);
}
}
};
}, [query, shouldListen, handleChange]);
(0, import_react12.useEffect)(() => {
if (ssrSafe && typeof window !== "undefined" && window.matchMedia !== void 0) {
try {
setMatches(window.matchMedia(query).matches);
} catch (e) {
console.error("Failed to update matches state after hydration:", query, e);
}
}
}, [query, ssrSafe]);
return { matches, startWatching, stopWatching };
}
// src/hooks/useNetworkState.ts
var import_react13 = require("react");
function useNetworkState() {
const [state, setState] = (0, import_react13.useState)(() => ({
online: navigator.onLine,
...navigator.connection || {}
}));
const updateNetworkInfo = (0, import_react13.useCallback)(() => {
const connection = navigator.connection;
setState({
online: navigator.onLine,
downlink: connection?.downlink,
downlinkMax: connection?.downlinkMax,
effectiveType: connection?.effectiveType,
rtt: connection?.rtt,
saveData: connection?.saveData,
type: connection?.type
});
}, []);
(0, import_react13.useEffect)(() => {
const connection = navigator.connection;
window.addEventListener("online", updateNetworkInfo);
window.addEventListener("offline", updateNetworkInfo);
if (connection) {
connection?.addEventListener?.("change", updateNetworkInfo);
}
return () => {
window.removeEventListener("online", updateNetworkInfo);
window.removeEventListener("offline", updateNetworkInfo);
if (connection) {
connection?.removeEventListener?.("change", updateNetworkInfo);
}
};
}, [updateNetworkInfo]);
return { ...state, checkConnection: updateNetworkInfo };
}
// src/hooks/usePageLeave.ts
var import_react14 = require("react");
function usePageLeave(options = {}) {
const { threshold = 0, enabled = true, onLeave, onReturn } = options;
const [hasLeft, setHasLeft] = (0, import_react14.useState)(false);
const [isEnabled, setIsEnabled] = (0, import_react14.useState)(enabled);
const handleMouseOut = (0, import_react14.useCallback)(
(e) => {
if (!isEnabled)
return;
if (e.clientY <= threshold) {
setHasLeft(true);
onLeave?.();
}
},
[threshold, isEnabled, onLeave]
);
const handleMouseOver = (0, import_react14.useCallback)(() => {
if (!isEnabled)
return;
if (hasLeft) {
setHasLeft(false);
onReturn?.();
}
}, [isEnabled, hasLeft, onReturn]);
const enable = (0, import_react14.useCallback)(() => {
setIsEnabled(true);
}, []);
const disable = (0, import_react14.useCallback)(() => {
setIsEnabled(false);
}, []);
(0, import_react14.useEffect)(() => {
if (isEnabled) {
document.addEventListener("mouseout", handleMouseOut);
document.addEventListener("mouseover", handleMouseOver);
}
return () => {
document.removeEventListener("mouseout", handleMouseOut);
document.removeEventListener("mouseover", handleMouseOver);
};
}, [isEnabled, handleMouseOut, handleMouseOver]);
return { hasLeft, enable, disable };
}
// src/hooks/useQueue.ts
var import_react15 = require("react");
function useQueue(initialValues = [], options = {}) {
const { maxSize, onFull, onEmpty, equalityFn = (a, b) => a === b } = options;
const [queue, setQueue] = (0, import_react15.useState)(initialValues);
const enqueue = (0, import_react15.useCallback)(
(item) => {
setQueue((q) => {
if (maxSize && q.length >= maxSize) {
onFull?.();
return q;
}
return [...q, item];
});
},
[maxSize, onFull]
);
const dequeue = (0, import_react15.useCallback)(() => {
if (queue.length === 0) {
return void 0;
}
const itemToDequeue = queue[0];
setQueue((currentQueue) => {
if (currentQueue.length === 0)
return currentQueue;
const newQueue = currentQueue.slice(1);
if (onEmpty && currentQueue.length > 0 && newQueue.length === 0) {
onEmpty();
}
return newQueue;
});
return itemToDequeue;
}, [queue, onEmpty]);
const clear = (0, import_react15.useCallback)(() => {
if (onEmpty && queue.length > 0) {
onEmpty();
}
setQueue([]);
}, [onEmpty, queue]);
const peek = (0, import_react15.useCallback)(() => queue[0], [queue]);
const peekLast = (0, import_react15.useCallback)(() => queue[queue.length - 1], [queue]);
const contains = (0, import_react15.useCallback)((item) => queue.some((i) => equalityFn(i, item)), [queue, equalityFn]);
const state = (0, import_react15.useMemo)(
() => ({
isEmpty: queue.length === 0,
size: queue.length,
toArray: () => [...queue]
}),
[queue]
);
return {
enqueue,
dequeue,
clear,
peek,
peekLast,
contains,
...state
};
}
// src/hooks/useSessionStorage.ts
var import_react16 = require("react");
function useSessionStorage(key, initialValue, options = {}) {
const {
serializer = JSON.stringify,
deserializer = JSON.parse,
onError = console.error,
syncTabs = false
} = options;
const mounted = (0, import_react16.useRef)(true);
const [storedValue, setStoredValue] = (0, import_react16.useState)(() => {
try {
const item = window.sessionStorage.getItem(key);
return item ? deserializer(item) : initialValue;
} catch (error) {
onError(error);
return initialValue;
}
});
const setValue = (0, import_react16.useCallback)(
(value) => {
setStoredValue((prev) => {
try {
const newValue = value instanceof Function ? value(prev) : value;
window.sessionStorage.setItem(key, serializer(newValue));
return newValue;
} catch (error) {
onError(error);
return prev;
}
});
},
[key, serializer, onError]
);
const handleStorageChange = (0, import_react16.useCallback)(
(event) => {
if (event.key === key && event.newValue !== null && mounted.current) {
try {
const newValue = deserializer(event.newValue);
setStoredValue(newValue);
} catch (error) {
onError(error);
}
}
},
[key, deserializer, onError]
);
(0, import_react16.useEffect)(() => {
if (syncTabs) {
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}
}, [syncTabs, handleStorageChange]);
(0, import_react16.useEffect)(
() => () => {
mounted.current = false;
},
[]
);
return [storedValue, setValue];
}
// src/hooks/useTimeout.ts
var import_react17 = require("react");
function useTimeout({ callback, delay, autoStart = true }) {
const savedCallback = (0, import_react17.useRef)(() => {
});
const timeoutId = (0, import_react17.useRef)(null);
const [isPending, setIsPending] = (0, import_react17.useState)(false);
(0, import_react17.useEffect)(() => {
savedCallback.current = callback;
}, [callback]);
const clear = (0, import_react17.useCallback)(() => {
if (timeoutId.current !== null) {
clearTimeout(timeoutId.current);
timeoutId.current = null;
}
setIsPending(false);
}, []);
const start = (0, import_react17.useCallback)(() => {
clear();
if (delay !== null) {
setIsPending(true);
timeoutId.current = window.setTimeout(() => {
savedCallback.current();
setIsPending(false);
}, delay);
}
}, [delay, clear]);
const reset = (0, import_react17.useCallback)(() => {
clear();
start();
}, [clear, start]);
(0, import_react17.useEffect)(() => {
if (autoStart && delay !== null) {
start();
}
return clear;
}, [delay, autoStart, start, clear]);
return {
isPending,
start,
cancel: clear,
reset
};
}
// src/hooks/useToggle.ts
var import_react18 = require("react");
function useToggle({
initialValue = false,
onChange,
persist = false,
storageKey = "useToggle"
} = {}) {
const [value, setValue] = (0, import_react18.useState)(() => {
if (persist) {
const stored = localStorage.getItem(storageKey);
return stored ? JSON.parse(stored) : initialValue;
}
return initialValue;
});
const mounted = (0, import_react18.useRef)(true);
const updateValue = (0, import_react18.useCallback)(
(newValue) => {
if (mounted.current) {
setValue(newValue);
onChange?.(newValue);
if (persist) {
localStorage.setItem(storageKey, JSON.stringify(newValue));
}
}
},
[onChange, persist, storageKey]
);
const toggle = (0, import_react18.useCallback)(() => updateValue(!value), [value, updateValue]);
const setTrue = (0, import_react18.useCallback)(() => updateValue(true), [updateValue]);
const setFalse = (0, import_react18.useCallback)(() => updateValue(false), [updateValue]);
(0, import_react18.useEffect)(
() => () => {
mounted.current = false;
},
[]
);
return {
value,
toggle,
setTrue,
setFalse,
setValue: updateValue
};
}
// src/hooks/useVisibilityChange.ts
var import_react19 = require("react");
function useVisibilityChange({
onVisible,
onHidden,
startImmediately = true
} = {}) {
const [isVisible, setIsVisible] = (0, import_react19.useState)(!document.hidden);
const [isMonitoring, setIsMonitoring] = (0, import_react19.useState)(startImmediately);
const handleVisibilityChange = (0, import_react19.useCallback)(() => {
const newVisibility = !document.hidden;
setIsVisible(newVisibility);
if (newVisibility) {
onVisible?.();
} else {
onHidden?.();
}
}, [onVisible, onHidden, isMonitoring]);
const start = (0, import_react19.useCallback)(() => setIsMonitoring(true), []);
const stop = (0, import_react19.useCallback)(() => setIsMonitoring(false), []);
(0, import_react19.useEffect)(() => {
if (isMonitoring) {
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
} else {
document.removeEventListener("visibilitychange", handleVisibilityChange);
}
}, [isMonitoring, handleVisibilityChange]);
return { isVisible, start, stop };
}
// src/hooks/useWindowWidth.ts
var import_react20 = require("react");
function useWindowWidth({
debounceDelay = 250,
startImmediately = true,
initialWidth = typeof window !== "undefined" ? window.innerWidth : 0,
onChange
} = {}) {
const [width, setWidth] = (0, import_react20.useState)(initialWidth);
const [isMonitoring, setIsMonitoring] = (0, import_react20.useState)(startImmediately);
const timeoutRef = (0, import_react20.useRef)(null);
const handleResize = (0, import_react20.useCallback)(() => {
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
}
timeoutRef.current = window.setTimeout(() => {
const newWidth = window.innerWidth;
setWidth(newWidth);
onChange?.(newWidth);
}, debounceDelay);
}, [debounceDelay, onChange]);
const start = (0, import_react20.useCallback)(() => setIsMonitoring(true), []);
const stop = (0, import_react20.useCallback)(() => setIsMonitoring(false), []);
(0, import_react20.useEffect)(() => {
if (isMonitoring) {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
}
};
} else {
window.removeEventListener("resize", handleResize);
if (timeoutRef.current) {
window.clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
}, [isMonitoring, handleResize]);
return { width, start, stop };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
useBoolean,
useClickOutside,
useCopyToClipboard,
useDebounce,
useHandledInterval,
useIdleness,
useInterval,
useLocalStorage,
useLockBodyScroll,
useLogger,
useMeasure,
useMediaQuery,
useNetworkState,
usePageLeave,
useQueue,
useSessionStorage,
useTimeout,
useToggle,
useVisibilityChange,
useWindowWidth
});