UNPKG

usehooks-ts

Version:

React hook library, ready to use, written in Typescript.

1,047 lines (1,036 loc) 31.3 kB
import { useState, useCallback, useLayoutEffect, useEffect, useRef, useMemo } from 'react'; import debounce from 'lodash.debounce'; // src/useBoolean/useBoolean.ts function useBoolean(defaultValue = false) { if (typeof defaultValue !== "boolean") { throw new Error("defaultValue must be `true` or `false`"); } const [value, setValue] = useState(defaultValue); const setTrue = useCallback(() => { setValue(true); }, []); const setFalse = useCallback(() => { setValue(false); }, []); const toggle = useCallback(() => { setValue((x) => !x); }, []); return { value, setValue, setTrue, setFalse, toggle }; } var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; // src/useEventListener/useEventListener.ts function useEventListener(eventName, handler, element, options) { const savedHandler = useRef(handler); useIsomorphicLayoutEffect(() => { savedHandler.current = handler; }, [handler]); useEffect(() => { const targetElement = (element == null ? void 0 : element.current) ?? window; if (!(targetElement && targetElement.addEventListener)) return; const listener = (event) => { savedHandler.current(event); }; targetElement.addEventListener(eventName, listener, options); return () => { targetElement.removeEventListener(eventName, listener, options); }; }, [eventName, element, options]); } // src/useClickAnyWhere/useClickAnyWhere.ts function useClickAnyWhere(handler) { useEventListener("click", (event) => { handler(event); }); } function useCopyToClipboard() { const [copiedText, setCopiedText] = useState(null); const copy = useCallback(async (text) => { if (!(navigator == null ? void 0 : navigator.clipboard)) { console.warn("Clipboard not supported"); return false; } try { await navigator.clipboard.writeText(text); setCopiedText(text); return true; } catch (error) { console.warn("Copy failed", error); setCopiedText(null); return false; } }, []); return [copiedText, copy]; } function useCounter(initialValue) { const [count, setCount] = useState(initialValue ?? 0); const increment = useCallback(() => { setCount((x) => x + 1); }, []); const decrement = useCallback(() => { setCount((x) => x - 1); }, []); const reset = useCallback(() => { setCount(initialValue ?? 0); }, [initialValue]); return { count, increment, decrement, reset, setCount }; } function useInterval(callback, delay) { const savedCallback = useRef(callback); useIsomorphicLayoutEffect(() => { savedCallback.current = callback; }, [callback]); useEffect(() => { if (delay === null) { return; } const id = setInterval(() => { savedCallback.current(); }, delay); return () => { clearInterval(id); }; }, [delay]); } // src/useCountdown/useCountdown.ts function useCountdown({ countStart, countStop = 0, intervalMs = 1e3, isIncrement = false }) { const { count, increment, decrement, reset: resetCounter } = useCounter(countStart); const { value: isCountdownRunning, setTrue: startCountdown, setFalse: stopCountdown } = useBoolean(false); const resetCountdown = useCallback(() => { stopCountdown(); resetCounter(); }, [stopCountdown, resetCounter]); const countdownCallback = useCallback(() => { if (count === countStop) { stopCountdown(); return; } if (isIncrement) { increment(); } else { decrement(); } }, [count, countStop, decrement, increment, isIncrement, stopCountdown]); useInterval(countdownCallback, isCountdownRunning ? intervalMs : null); return [count, { startCountdown, stopCountdown, resetCountdown }]; } function useEventCallback(fn) { const ref = useRef(() => { throw new Error("Cannot call an event handler while rendering."); }); useIsomorphicLayoutEffect(() => { ref.current = fn; }, [fn]); return useCallback((...args) => { var _a; return (_a = ref.current) == null ? void 0 : _a.call(ref, ...args); }, [ref]); } // src/useLocalStorage/useLocalStorage.ts var IS_SERVER = typeof window === "undefined"; function useLocalStorage(key, initialValue, options = {}) { const { initializeWithValue = true } = options; const serializer = useCallback( (value) => { if (options.serializer) { return options.serializer(value); } return JSON.stringify(value); }, [options] ); const deserializer = useCallback( (value) => { if (options.deserializer) { return options.deserializer(value); } if (value === "undefined") { return void 0; } const defaultValue = initialValue instanceof Function ? initialValue() : initialValue; let parsed; try { parsed = JSON.parse(value); } catch (error) { console.error("Error parsing JSON:", error); return defaultValue; } return parsed; }, [options, initialValue] ); const readValue = useCallback(() => { const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue; if (IS_SERVER) { return initialValueToUse; } try { const raw = window.localStorage.getItem(key); return raw ? deserializer(raw) : initialValueToUse; } catch (error) { console.warn(`Error reading localStorage key \u201C${key}\u201D:`, error); return initialValueToUse; } }, [initialValue, key, deserializer]); const [storedValue, setStoredValue] = useState(() => { if (initializeWithValue) { return readValue(); } return initialValue instanceof Function ? initialValue() : initialValue; }); const setValue = useEventCallback((value) => { if (IS_SERVER) { console.warn( `Tried setting localStorage key \u201C${key}\u201D even though environment is not a client` ); } try { const newValue = value instanceof Function ? value(readValue()) : value; window.localStorage.setItem(key, serializer(newValue)); setStoredValue(newValue); window.dispatchEvent(new StorageEvent("local-storage", { key })); } catch (error) { console.warn(`Error setting localStorage key \u201C${key}\u201D:`, error); } }); const removeValue = useEventCallback(() => { if (IS_SERVER) { console.warn( `Tried removing localStorage key \u201C${key}\u201D even though environment is not a client` ); } const defaultValue = initialValue instanceof Function ? initialValue() : initialValue; window.localStorage.removeItem(key); setStoredValue(defaultValue); window.dispatchEvent(new StorageEvent("local-storage", { key })); }); useEffect(() => { setStoredValue(readValue()); }, [key]); const handleStorageChange = useCallback( (event) => { if (event.key && event.key !== key) { return; } setStoredValue(readValue()); }, [key, readValue] ); useEventListener("storage", handleStorageChange); useEventListener("local-storage", handleStorageChange); return [storedValue, setValue, removeValue]; } var IS_SERVER2 = typeof window === "undefined"; function useMediaQuery(query, { defaultValue = false, initializeWithValue = true } = {}) { const getMatches = (query2) => { if (IS_SERVER2) { return defaultValue; } return window.matchMedia(query2).matches; }; const [matches, setMatches] = useState(() => { if (initializeWithValue) { return getMatches(query); } return defaultValue; }); function handleChange() { setMatches(getMatches(query)); } useIsomorphicLayoutEffect(() => { const matchMedia = window.matchMedia(query); handleChange(); if (matchMedia.addListener) { matchMedia.addListener(handleChange); } else { matchMedia.addEventListener("change", handleChange); } return () => { if (matchMedia.removeListener) { matchMedia.removeListener(handleChange); } else { matchMedia.removeEventListener("change", handleChange); } }; }, [query]); return matches; } // src/useDarkMode/useDarkMode.ts var COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)"; var LOCAL_STORAGE_KEY = "usehooks-ts-dark-mode"; function useDarkMode(options = {}) { const { defaultValue, localStorageKey = LOCAL_STORAGE_KEY, initializeWithValue = true } = options; const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY, { initializeWithValue, defaultValue }); const [isDarkMode, setDarkMode] = useLocalStorage( localStorageKey, defaultValue ?? isDarkOS ?? false, { initializeWithValue } ); useIsomorphicLayoutEffect(() => { if (isDarkOS !== isDarkMode) { setDarkMode(isDarkOS); } }, [isDarkOS]); return { isDarkMode, toggle: () => { setDarkMode((prev) => !prev); }, enable: () => { setDarkMode(true); }, disable: () => { setDarkMode(false); }, set: (value) => { setDarkMode(value); } }; } function useUnmount(func) { const funcRef = useRef(func); funcRef.current = func; useEffect( () => () => { funcRef.current(); }, [] ); } // src/useDebounceCallback/useDebounceCallback.ts function useDebounceCallback(func, delay = 500, options) { const debouncedFunc = useRef(); useUnmount(() => { if (debouncedFunc.current) { debouncedFunc.current.cancel(); } }); const debounced = useMemo(() => { const debouncedFuncInstance = debounce(func, delay, options); const wrappedFunc = (...args) => { return debouncedFuncInstance(...args); }; wrappedFunc.cancel = () => { debouncedFuncInstance.cancel(); }; wrappedFunc.isPending = () => { return !!debouncedFunc.current; }; wrappedFunc.flush = () => { return debouncedFuncInstance.flush(); }; return wrappedFunc; }, [func, delay, options]); useEffect(() => { debouncedFunc.current = debounce(func, delay, options); }, [func, delay, options]); return debounced; } function useDebounceValue(initialValue, delay, options) { const eq = (options == null ? void 0 : options.equalityFn) ?? ((left, right) => left === right); const unwrappedInitialValue = initialValue instanceof Function ? initialValue() : initialValue; const [debouncedValue, setDebouncedValue] = useState(unwrappedInitialValue); const previousValueRef = useRef(unwrappedInitialValue); const updateDebouncedValue = useDebounceCallback( setDebouncedValue, delay, options ); if (!eq(previousValueRef.current, unwrappedInitialValue)) { updateDebouncedValue(unwrappedInitialValue); previousValueRef.current = unwrappedInitialValue; } return [debouncedValue, updateDebouncedValue]; } function useDocumentTitle(title, options = {}) { const { preserveTitleOnUnmount = true } = options; const defaultTitle = useRef(null); useIsomorphicLayoutEffect(() => { defaultTitle.current = window.document.title; }, []); useIsomorphicLayoutEffect(() => { window.document.title = title; }, [title]); useUnmount(() => { if (!preserveTitleOnUnmount && defaultTitle.current) { window.document.title = defaultTitle.current; } }); } function useHover(elementRef) { const [value, setValue] = useState(false); const handleMouseEnter = () => { setValue(true); }; const handleMouseLeave = () => { setValue(false); }; useEventListener("mouseenter", handleMouseEnter, elementRef); useEventListener("mouseleave", handleMouseLeave, elementRef); return value; } function useIntersectionObserver({ threshold = 0, root = null, rootMargin = "0%", freezeOnceVisible = false, initialIsIntersecting = false, onChange } = {}) { var _a; const [ref, setRef] = useState(null); const [state, setState] = useState(() => ({ isIntersecting: initialIsIntersecting, entry: void 0 })); const callbackRef = useRef(); callbackRef.current = onChange; const frozen = ((_a = state.entry) == null ? void 0 : _a.isIntersecting) && freezeOnceVisible; useEffect(() => { if (!ref) return; if (!("IntersectionObserver" in window)) return; if (frozen) return; let unobserve; const observer = new IntersectionObserver( (entries) => { const thresholds = Array.isArray(observer.thresholds) ? observer.thresholds : [observer.thresholds]; entries.forEach((entry) => { const isIntersecting = entry.isIntersecting && thresholds.some((threshold2) => entry.intersectionRatio >= threshold2); setState({ isIntersecting, entry }); if (callbackRef.current) { callbackRef.current(isIntersecting, entry); } if (isIntersecting && freezeOnceVisible && unobserve) { unobserve(); unobserve = void 0; } }); }, { threshold, root, rootMargin } ); observer.observe(ref); return () => { observer.disconnect(); }; }, [ ref, // eslint-disable-next-line react-hooks/exhaustive-deps JSON.stringify(threshold), root, rootMargin, frozen, freezeOnceVisible ]); const prevRef = useRef(null); useEffect(() => { var _a2; if (!ref && ((_a2 = state.entry) == null ? void 0 : _a2.target) && !freezeOnceVisible && !frozen && prevRef.current !== state.entry.target) { prevRef.current = state.entry.target; setState({ isIntersecting: initialIsIntersecting, entry: void 0 }); } }, [ref, state.entry, freezeOnceVisible, frozen, initialIsIntersecting]); const result = [ setRef, !!state.isIntersecting, state.entry ]; result.ref = result[0]; result.isIntersecting = result[1]; result.entry = result[2]; return result; } function useIsClient() { const [isClient, setClient] = useState(false); useEffect(() => { setClient(true); }, []); return isClient; } function useIsMounted() { const isMounted = useRef(false); useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }, []); return useCallback(() => isMounted.current, []); } function useMap(initialState = /* @__PURE__ */ new Map()) { const [map, setMap] = useState(new Map(initialState)); const actions = { set: useCallback((key, value) => { setMap((prev) => { const copy = new Map(prev); copy.set(key, value); return copy; }); }, []), setAll: useCallback((entries) => { setMap(() => new Map(entries)); }, []), remove: useCallback((key) => { setMap((prev) => { const copy = new Map(prev); copy.delete(key); return copy; }); }, []), reset: useCallback(() => { setMap(() => /* @__PURE__ */ new Map()); }, []) }; return [map, actions]; } // src/useOnClickOutside/useOnClickOutside.ts function useOnClickOutside(ref, handler, eventType = "mousedown", eventListenerOptions = {}) { useEventListener( eventType, (event) => { const target = event.target; if (!target || !target.isConnected) { return; } const isOutside = Array.isArray(ref) ? ref.filter((r) => Boolean(r.current)).every((r) => r.current && !r.current.contains(target)) : ref.current && !ref.current.contains(target); if (isOutside) { handler(event); } }, void 0, eventListenerOptions ); } var IS_SERVER3 = typeof window === "undefined"; function useReadLocalStorage(key, options = {}) { let { initializeWithValue = true } = options; if (IS_SERVER3) { initializeWithValue = false; } const deserializer = useCallback( (value) => { if (options.deserializer) { return options.deserializer(value); } if (value === "undefined") { return void 0; } let parsed; try { parsed = JSON.parse(value); } catch (error) { console.error("Error parsing JSON:", error); return null; } return parsed; }, [options] ); const readValue = useCallback(() => { if (IS_SERVER3) { return null; } try { const raw = window.localStorage.getItem(key); return raw ? deserializer(raw) : null; } catch (error) { console.warn(`Error reading localStorage key \u201C${key}\u201D:`, error); return null; } }, [key, deserializer]); const [storedValue, setStoredValue] = useState(() => { if (initializeWithValue) { return readValue(); } return void 0; }); useEffect(() => { setStoredValue(readValue()); }, [key]); const handleStorageChange = useCallback( (event) => { if (event.key && event.key !== key) { return; } setStoredValue(readValue()); }, [key, readValue] ); useEventListener("storage", handleStorageChange); useEventListener("local-storage", handleStorageChange); return storedValue; } var initialSize = { width: void 0, height: void 0 }; function useResizeObserver(options) { const { ref, box = "content-box" } = options; const [{ width, height }, setSize] = useState(initialSize); const isMounted = useIsMounted(); const previousSize = useRef({ ...initialSize }); const onResize = useRef(void 0); onResize.current = options.onResize; useEffect(() => { if (!ref.current) return; if (typeof window === "undefined" || !("ResizeObserver" in window)) return; const observer = new ResizeObserver(([entry]) => { const boxProp = box === "border-box" ? "borderBoxSize" : box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize"; const newWidth = extractSize(entry, boxProp, "inlineSize"); const newHeight = extractSize(entry, boxProp, "blockSize"); const hasChanged = previousSize.current.width !== newWidth || previousSize.current.height !== newHeight; if (hasChanged) { const newSize = { width: newWidth, height: newHeight }; previousSize.current.width = newWidth; previousSize.current.height = newHeight; if (onResize.current) { onResize.current(newSize); } else { if (isMounted()) { setSize(newSize); } } } }); observer.observe(ref.current, { box }); return () => { observer.disconnect(); }; }, [box, ref, isMounted]); return { width, height }; } function extractSize(entry, box, sizeType) { if (!entry[box]) { if (box === "contentBoxSize") { return entry.contentRect[sizeType === "inlineSize" ? "width" : "height"]; } return void 0; } return Array.isArray(entry[box]) ? entry[box][0][sizeType] : ( // @ts-ignore Support Firefox's non-standard behavior entry[box][sizeType] ); } var IS_SERVER4 = typeof window === "undefined"; function useScreen(options = {}) { let { initializeWithValue = true } = options; if (IS_SERVER4) { initializeWithValue = false; } const readScreen = () => { if (IS_SERVER4) { return void 0; } return window.screen; }; const [screen, setScreen] = useState(() => { if (initializeWithValue) { return readScreen(); } return void 0; }); const debouncedSetScreen = useDebounceCallback( setScreen, options.debounceDelay ); function handleSize() { const newScreen = readScreen(); const setSize = options.debounceDelay ? debouncedSetScreen : setScreen; if (newScreen) { const { width, height, availHeight, availWidth, colorDepth, orientation, pixelDepth } = newScreen; setSize({ width, height, availHeight, availWidth, colorDepth, orientation, pixelDepth }); } } useEventListener("resize", handleSize); useIsomorphicLayoutEffect(() => { handleSize(); }, []); return screen; } var cachedScriptStatuses = /* @__PURE__ */ new Map(); function getScriptNode(src) { const node = document.querySelector( `script[src="${src}"]` ); const status = node == null ? void 0 : node.getAttribute("data-status"); return { node, status }; } function useScript(src, options) { const [status, setStatus] = useState(() => { if (!src || (options == null ? void 0 : options.shouldPreventLoad)) { return "idle"; } if (typeof window === "undefined") { return "loading"; } return cachedScriptStatuses.get(src) ?? "loading"; }); useEffect(() => { if (!src || (options == null ? void 0 : options.shouldPreventLoad)) { return; } const cachedScriptStatus = cachedScriptStatuses.get(src); if (cachedScriptStatus === "ready" || cachedScriptStatus === "error") { setStatus(cachedScriptStatus); return; } const script = getScriptNode(src); let scriptNode = script.node; if (!scriptNode) { scriptNode = document.createElement("script"); scriptNode.src = src; scriptNode.async = true; if (options == null ? void 0 : options.id) { scriptNode.id = options.id; } scriptNode.setAttribute("data-status", "loading"); document.body.appendChild(scriptNode); const setAttributeFromEvent = (event) => { const scriptStatus = event.type === "load" ? "ready" : "error"; scriptNode == null ? void 0 : scriptNode.setAttribute("data-status", scriptStatus); }; scriptNode.addEventListener("load", setAttributeFromEvent); scriptNode.addEventListener("error", setAttributeFromEvent); } else { setStatus(script.status ?? cachedScriptStatus ?? "loading"); } const setStateFromEvent = (event) => { const newStatus = event.type === "load" ? "ready" : "error"; setStatus(newStatus); cachedScriptStatuses.set(src, newStatus); }; scriptNode.addEventListener("load", setStateFromEvent); scriptNode.addEventListener("error", setStateFromEvent); return () => { if (scriptNode) { scriptNode.removeEventListener("load", setStateFromEvent); scriptNode.removeEventListener("error", setStateFromEvent); } if (scriptNode && (options == null ? void 0 : options.removeOnUnmount)) { scriptNode.remove(); cachedScriptStatuses.delete(src); } }; }, [src, options == null ? void 0 : options.shouldPreventLoad, options == null ? void 0 : options.removeOnUnmount, options == null ? void 0 : options.id]); return status; } var IS_SERVER5 = typeof window === "undefined"; function useScrollLock(options = {}) { const { autoLock = true, lockTarget, widthReflow = true } = options; const [isLocked, setIsLocked] = useState(false); const target = useRef(null); const originalStyle = useRef(null); const lock = () => { if (target.current) { const { overflow, paddingRight } = target.current.style; originalStyle.current = { overflow, paddingRight }; if (widthReflow) { const offsetWidth = target.current === document.body ? window.innerWidth : target.current.offsetWidth; const currentPaddingRight = parseInt(window.getComputedStyle(target.current).paddingRight, 10) || 0; const scrollbarWidth = offsetWidth - target.current.scrollWidth; target.current.style.paddingRight = `${scrollbarWidth + currentPaddingRight}px`; } target.current.style.overflow = "hidden"; setIsLocked(true); } }; const unlock = () => { if (target.current && originalStyle.current) { target.current.style.overflow = originalStyle.current.overflow; if (widthReflow) { target.current.style.paddingRight = originalStyle.current.paddingRight; } } setIsLocked(false); }; useIsomorphicLayoutEffect(() => { if (IS_SERVER5) return; if (lockTarget) { target.current = typeof lockTarget === "string" ? document.querySelector(lockTarget) : lockTarget; } if (!target.current) { target.current = document.body; } if (autoLock) { lock(); } return () => { unlock(); }; }, [autoLock, lockTarget, widthReflow]); return { isLocked, lock, unlock }; } var IS_SERVER6 = typeof window === "undefined"; function useSessionStorage(key, initialValue, options = {}) { const { initializeWithValue = true } = options; const serializer = useCallback( (value) => { if (options.serializer) { return options.serializer(value); } return JSON.stringify(value); }, [options] ); const deserializer = useCallback( (value) => { if (options.deserializer) { return options.deserializer(value); } if (value === "undefined") { return void 0; } const defaultValue = initialValue instanceof Function ? initialValue() : initialValue; let parsed; try { parsed = JSON.parse(value); } catch (error) { console.error("Error parsing JSON:", error); return defaultValue; } return parsed; }, [options, initialValue] ); const readValue = useCallback(() => { const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue; if (IS_SERVER6) { return initialValueToUse; } try { const raw = window.sessionStorage.getItem(key); return raw ? deserializer(raw) : initialValueToUse; } catch (error) { console.warn(`Error reading sessionStorage key \u201C${key}\u201D:`, error); return initialValueToUse; } }, [initialValue, key, deserializer]); const [storedValue, setStoredValue] = useState(() => { if (initializeWithValue) { return readValue(); } return initialValue instanceof Function ? initialValue() : initialValue; }); const setValue = useEventCallback((value) => { if (IS_SERVER6) { console.warn( `Tried setting sessionStorage key \u201C${key}\u201D even though environment is not a client` ); } try { const newValue = value instanceof Function ? value(readValue()) : value; window.sessionStorage.setItem(key, serializer(newValue)); setStoredValue(newValue); window.dispatchEvent(new StorageEvent("session-storage", { key })); } catch (error) { console.warn(`Error setting sessionStorage key \u201C${key}\u201D:`, error); } }); const removeValue = useEventCallback(() => { if (IS_SERVER6) { console.warn( `Tried removing sessionStorage key \u201C${key}\u201D even though environment is not a client` ); } const defaultValue = initialValue instanceof Function ? initialValue() : initialValue; window.sessionStorage.removeItem(key); setStoredValue(defaultValue); window.dispatchEvent(new StorageEvent("session-storage", { key })); }); useEffect(() => { setStoredValue(readValue()); }, [key]); const handleStorageChange = useCallback( (event) => { if (event.key && event.key !== key) { return; } setStoredValue(readValue()); }, [key, readValue] ); useEventListener("storage", handleStorageChange); useEventListener("session-storage", handleStorageChange); return [storedValue, setValue, removeValue]; } function useStep(maxStep) { const [currentStep, setCurrentStep] = useState(1); const canGoToNextStep = currentStep + 1 <= maxStep; const canGoToPrevStep = currentStep - 1 > 0; const setStep = useCallback( (step) => { const newStep = step instanceof Function ? step(currentStep) : step; if (newStep >= 1 && newStep <= maxStep) { setCurrentStep(newStep); return; } throw new Error("Step not valid"); }, [maxStep, currentStep] ); const goToNextStep = useCallback(() => { if (canGoToNextStep) { setCurrentStep((step) => step + 1); } }, [canGoToNextStep]); const goToPrevStep = useCallback(() => { if (canGoToPrevStep) { setCurrentStep((step) => step - 1); } }, [canGoToPrevStep]); const reset = useCallback(() => { setCurrentStep(1); }, []); return [ currentStep, { goToNextStep, goToPrevStep, canGoToNextStep, canGoToPrevStep, setStep, reset } ]; } // src/useTernaryDarkMode/useTernaryDarkMode.ts var COLOR_SCHEME_QUERY2 = "(prefers-color-scheme: dark)"; var LOCAL_STORAGE_KEY2 = "usehooks-ts-ternary-dark-mode"; function useTernaryDarkMode({ defaultValue = "system", localStorageKey = LOCAL_STORAGE_KEY2, initializeWithValue = true } = {}) { const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY2, { initializeWithValue }); const [mode, setMode] = useLocalStorage(localStorageKey, defaultValue, { initializeWithValue }); const isDarkMode = mode === "dark" || mode === "system" && isDarkOS; const toggleTernaryDarkMode = () => { const modes = ["light", "system", "dark"]; setMode((prevMode) => { const nextIndex = (modes.indexOf(prevMode) + 1) % modes.length; return modes[nextIndex]; }); }; return { isDarkMode, ternaryDarkMode: mode, setTernaryDarkMode: setMode, toggleTernaryDarkMode }; } function useTimeout(callback, delay) { const savedCallback = useRef(callback); useIsomorphicLayoutEffect(() => { savedCallback.current = callback; }, [callback]); useEffect(() => { if (!delay && delay !== 0) { return; } const id = setTimeout(() => { savedCallback.current(); }, delay); return () => { clearTimeout(id); }; }, [delay]); } function useToggle(defaultValue) { const [value, setValue] = useState(!!defaultValue); const toggle = useCallback(() => { setValue((x) => !x); }, []); return [value, toggle, setValue]; } var IS_SERVER7 = typeof window === "undefined"; function useWindowSize(options = {}) { let { initializeWithValue = true } = options; if (IS_SERVER7) { initializeWithValue = false; } const [windowSize, setWindowSize] = useState(() => { if (initializeWithValue) { return { width: window.innerWidth, height: window.innerHeight }; } return { width: void 0, height: void 0 }; }); const debouncedSetWindowSize = useDebounceCallback( setWindowSize, options.debounceDelay ); function handleSize() { const setSize = options.debounceDelay ? debouncedSetWindowSize : setWindowSize; setSize({ width: window.innerWidth, height: window.innerHeight }); } useEventListener("resize", handleSize); useIsomorphicLayoutEffect(() => { handleSize(); }, []); return windowSize; } export { useBoolean, useClickAnyWhere, useCopyToClipboard, useCountdown, useCounter, useDarkMode, useDebounceCallback, useDebounceValue, useDocumentTitle, useEventCallback, useEventListener, useHover, useIntersectionObserver, useInterval, useIsClient, useIsMounted, useIsomorphicLayoutEffect, useLocalStorage, useMap, useMediaQuery, useOnClickOutside, useReadLocalStorage, useResizeObserver, useScreen, useScript, useScrollLock, useSessionStorage, useStep, useTernaryDarkMode, useTimeout, useToggle, useUnmount, useWindowSize };