UMD of @mantine/hooks
This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/499180/1510107/%40mantine%E1%9C%B5hooks-umd.js
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MantineHooks = {}, global.React)); })(this, (function (exports, React) { 'use strict'; function clamp(value, min, max) { if (min === void 0 && max === void 0) { return value; } if (min !== void 0 && max === void 0) { return Math.max(value, min); } if (min === void 0 && max !== void 0) { return Math.min(value, max); } return Math.min(Math.max(value, min), max); } function lowerFirst(value) { return typeof value !== "string" ? "" : value.charAt(0).toLowerCase() + value.slice(1); } function randomId(prefix = "mantine-") { return `${prefix}${Math.random().toString(36).slice(2, 11)}`; } function range(start, end) { const length = Math.abs(end - start) + 1; const reversed = start > end; if (!reversed) { return Array.from({ length }, (_, index) => index + start); } return Array.from({ length }, (_, index) => start - index); } function shallowEqual(a, b) { if (a === b) { return true; } if (!(a instanceof Object) || !(b instanceof Object)) { return false; } const keys = Object.keys(a); const { length } = keys; if (length !== Object.keys(b).length) { return false; } for (let i = 0; i < length; i += 1) { const key = keys[i]; if (!(key in b)) { return false; } if (a[key] !== b[key]) { return false; } } return true; } function upperFirst(value) { return typeof value !== "string" ? "" : value.charAt(0).toUpperCase() + value.slice(1); } function useCallbackRef(callback) { const callbackRef = React.useRef(callback); React.useEffect(() => { callbackRef.current = callback; }); return React.useMemo(() => (...args) => callbackRef.current?.(...args), []); } function useDebouncedCallback(callback, delay) { const handleCallback = useCallbackRef(callback); const debounceTimerRef = React.useRef(0); React.useEffect(() => () => window.clearTimeout(debounceTimerRef.current), []); return React.useCallback( (...args) => { window.clearTimeout(debounceTimerRef.current); debounceTimerRef.current = window.setTimeout(() => handleCallback(...args), delay); }, [handleCallback, delay] ); } var DEFAULT_EVENTS = ["mousedown", "touchstart"]; function useClickOutside(handler, events, nodes) { const ref = React.useRef(null); React.useEffect(() => { const listener = (event) => { const { target } = event ?? {}; if (Array.isArray(nodes)) { const shouldIgnore = target?.hasAttribute("data-ignore-outside-clicks") || !document.body.contains(target) && target.tagName !== "HTML"; const shouldTrigger = nodes.every((node) => !!node && !event.composedPath().includes(node)); shouldTrigger && !shouldIgnore && handler(); } else if (ref.current && !ref.current.contains(target)) { handler(); } }; (events || DEFAULT_EVENTS).forEach((fn) => document.addEventListener(fn, listener)); return () => { (events || DEFAULT_EVENTS).forEach((fn) => document.removeEventListener(fn, listener)); }; }, [ref, handler, nodes]); return ref; } function useClipboard({ timeout = 2e3 } = {}) { const [error, setError] = React.useState(null); const [copied, setCopied] = React.useState(false); const [copyTimeout, setCopyTimeout] = React.useState(null); const handleCopyResult = (value) => { window.clearTimeout(copyTimeout); setCopyTimeout(window.setTimeout(() => setCopied(false), timeout)); setCopied(value); }; const copy = (valueToCopy) => { if ("clipboard" in navigator) { navigator.clipboard.writeText(valueToCopy).then(() => handleCopyResult(true)).catch((err) => setError(err)); } else { setError(new Error("useClipboard: navigator.clipboard is not supported")); } }; const reset = () => { setCopied(false); setError(null); window.clearTimeout(copyTimeout); }; return { copy, reset, error, copied }; } function attachMediaListener(query, callback) { try { query.addEventListener("change", callback); return () => query.removeEventListener("change", callback); } catch (e) { query.addListener(callback); return () => query.removeListener(callback); } } function getInitialValue(query, initialValue) { if (typeof window !== "undefined" && "matchMedia" in window) { return window.matchMedia(query).matches; } return false; } function useMediaQuery(query, initialValue, { getInitialValueInEffect } = { getInitialValueInEffect: true }) { const [matches, setMatches] = React.useState( getInitialValueInEffect ? initialValue : getInitialValue(query) ); const queryRef = React.useRef(null); React.useEffect(() => { if ("matchMedia" in window) { queryRef.current = window.matchMedia(query); setMatches(queryRef.current.matches); return attachMediaListener(queryRef.current, (event) => setMatches(event.matches)); } return void 0; }, [query]); return matches; } function useColorScheme(initialValue, options) { return useMediaQuery("(prefers-color-scheme: dark)", initialValue === "dark", options) ? "dark" : "light"; } var DEFAULT_OPTIONS = { min: -Infinity, max: Infinity }; function useCounter(initialValue = 0, options) { const { min, max } = { ...DEFAULT_OPTIONS, ...options }; const [count, setCount] = React.useState(clamp(initialValue, min, max)); const increment = () => setCount((current) => clamp(current + 1, min, max)); const decrement = () => setCount((current) => clamp(current - 1, min, max)); const set = (value) => setCount(clamp(value, min, max)); const reset = () => setCount(clamp(initialValue, min, max)); return [count, { increment, decrement, set, reset }]; } function useDebouncedState(defaultValue, wait, options = { leading: false }) { const [value, setValue] = React.useState(defaultValue); const timeoutRef = React.useRef(null); const leadingRef = React.useRef(true); const clearTimeout2 = () => window.clearTimeout(timeoutRef.current); React.useEffect(() => clearTimeout2, []); const debouncedSetValue = React.useCallback( (newValue) => { clearTimeout2(); if (leadingRef.current && options.leading) { setValue(newValue); } else { timeoutRef.current = window.setTimeout(() => { leadingRef.current = true; setValue(newValue); }, wait); } leadingRef.current = false; }, [options.leading] ); return [value, debouncedSetValue]; } function useDebouncedValue(value, wait, options = { leading: false }) { const [_value, setValue] = React.useState(value); const mountedRef = React.useRef(false); const timeoutRef = React.useRef(null); const cooldownRef = React.useRef(false); const cancel = () => window.clearTimeout(timeoutRef.current); React.useEffect(() => { if (mountedRef.current) { if (!cooldownRef.current && options.leading) { cooldownRef.current = true; setValue(value); } else { cancel(); timeoutRef.current = window.setTimeout(() => { cooldownRef.current = false; setValue(value); }, wait); } } }, [value, options.leading, wait]); React.useEffect(() => { mountedRef.current = true; return cancel; }, []); return [_value, cancel]; } var useIsomorphicEffect = typeof document !== "undefined" ? React.useLayoutEffect : React.useEffect; function useDocumentTitle(title) { useIsomorphicEffect(() => { if (typeof title === "string" && title.trim().length > 0) { document.title = title.trim(); } }, [title]); } function useDocumentVisibility() { const [documentVisibility, setDocumentVisibility] = React.useState("visible"); React.useEffect(() => { const listener = () => setDocumentVisibility(document.visibilityState); document.addEventListener("visibilitychange", listener); return () => document.removeEventListener("visibilitychange", listener); }, []); return documentVisibility; } function useDidUpdate(fn, dependencies) { const mounted = React.useRef(false); React.useEffect( () => () => { mounted.current = false; }, [] ); React.useEffect(() => { if (mounted.current) { return fn(); } mounted.current = true; return void 0; }, dependencies); } function useFocusReturn({ opened, shouldReturnFocus = true }) { const lastActiveElement = React.useRef(null); const returnFocus = () => { if (lastActiveElement.current && "focus" in lastActiveElement.current && typeof lastActiveElement.current.focus === "function") { lastActiveElement.current?.focus({ preventScroll: true }); } }; useDidUpdate(() => { let timeout = -1; const clearFocusTimeout = (event) => { if (event.key === "Tab") { window.clearTimeout(timeout); } }; document.addEventListener("keydown", clearFocusTimeout); if (opened) { lastActiveElement.current = document.activeElement; } else if (shouldReturnFocus) { timeout = window.setTimeout(returnFocus, 10); } return () => { window.clearTimeout(timeout); document.removeEventListener("keydown", clearFocusTimeout); }; }, [opened, shouldReturnFocus]); return returnFocus; } var TABBABLE_NODES = /input|select|textarea|button|object/; var FOCUS_SELECTOR = "a, input, select, textarea, button, object, [tabindex]"; function hidden(element) { return element.style.display === "none"; } function visible(element) { const isHidden = element.getAttribute("aria-hidden") || element.getAttribute("hidden") || element.getAttribute("type") === "hidden"; if (isHidden) { return false; } let parentElement = element; while (parentElement) { if (parentElement === document.body || parentElement.nodeType === 11) { break; } if (hidden(parentElement)) { return false; } parentElement = parentElement.parentNode; } return true; } function getElementTabIndex(element) { let tabIndex = element.getAttribute("tabindex"); if (tabIndex === null) { tabIndex = void 0; } return parseInt(tabIndex, 10); } function focusable(element) { const nodeName = element.nodeName.toLowerCase(); const isTabIndexNotNaN = !Number.isNaN(getElementTabIndex(element)); const res = ( TABBABLE_NODES.test(nodeName) && !element.disabled || (element instanceof HTMLAnchorElement ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) ); return res && visible(element); } function tabbable(element) { const tabIndex = getElementTabIndex(element); const isTabIndexNaN = Number.isNaN(tabIndex); return (isTabIndexNaN || tabIndex >= 0) && focusable(element); } function findTabbableDescendants(element) { return Array.from(element.querySelectorAll(FOCUS_SELECTOR)).filter(tabbable); } function scopeTab(node, event) { const tabbable2 = findTabbableDescendants(node); if (!tabbable2.length) { event.preventDefault(); return; } const finalTabbable = tabbable2[event.shiftKey ? 0 : tabbable2.length - 1]; const root = node.getRootNode(); let leavingFinalTabbable = finalTabbable === root.activeElement || node === root.activeElement; const activeElement = root.activeElement; const activeElementIsRadio = activeElement.tagName === "INPUT" && activeElement.getAttribute("type") === "radio"; if (activeElementIsRadio) { const activeRadioGroup = tabbable2.filter( (element) => element.getAttribute("type") === "radio" && element.getAttribute("name") === activeElement.getAttribute("name") ); leavingFinalTabbable = activeRadioGroup.includes(finalTabbable); } if (!leavingFinalTabbable) { return; } event.preventDefault(); const target = tabbable2[event.shiftKey ? tabbable2.length - 1 : 0]; if (target) { target.focus(); } } function useFocusTrap(active = true) { const ref = React.useRef(null); const focusNode = (node) => { let focusElement = node.querySelector("[data-autofocus]"); if (!focusElement) { const children = Array.from(node.querySelectorAll(FOCUS_SELECTOR)); focusElement = children.find(tabbable) || children.find(focusable) || null; if (!focusElement && focusable(node)) { focusElement = node; } } if (focusElement) { focusElement.focus({ preventScroll: true }); } else { console.warn( "[@mantine/hooks/use-focus-trap] Failed to find focusable element within provided node", node ); } }; const setRef = React.useCallback( (node) => { if (!active) { return; } if (node === null) { return; } if (ref.current === node) { return; } if (node) { setTimeout(() => { if (node.getRootNode()) { focusNode(node); } else { console.warn("[@mantine/hooks/use-focus-trap] Ref node is not part of the dom", node); } }); ref.current = node; } else { ref.current = null; } }, [active] ); React.useEffect(() => { if (!active) { return void 0; } ref.current && setTimeout(() => focusNode(ref.current)); const handleKeyDown = (event) => { if (event.key === "Tab" && ref.current) { scopeTab(ref.current, event); } }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [active]); return setRef; } var reducer = (value) => (value + 1) % 1e6; function useForceUpdate() { const [, update] = React.useReducer(reducer, 0); return update; } var __useId = React["useId".toString()] || (() => void 0); function useReactId() { const id = __useId(); return id ? `mantine-${id.replace(/:/g, "")}` : ""; } function useId(staticId) { const reactId = useReactId(); const [uuid, setUuid] = React.useState(reactId); useIsomorphicEffect(() => { setUuid(randomId()); }, []); if (typeof staticId === "string") { return staticId; } if (typeof window === "undefined") { return reactId; } return uuid; } var DEFAULT_EVENTS2 = [ "keypress", "mousemove", "touchmove", "click", "scroll" ]; var DEFAULT_OPTIONS2 = { events: DEFAULT_EVENTS2, initialState: true }; function useIdle(timeout, options) { const { events, initialState } = { ...DEFAULT_OPTIONS2, ...options }; const [idle, setIdle] = React.useState(initialState); const timer = React.useRef(-1); React.useEffect(() => { const handleEvents = () => { setIdle(false); if (timer.current) { window.clearTimeout(timer.current); } timer.current = window.setTimeout(() => { setIdle(true); }, timeout); }; events.forEach((event) => document.addEventListener(event, handleEvents)); timer.current = window.setTimeout(() => { setIdle(true); }, timeout); return () => { events.forEach((event) => document.removeEventListener(event, handleEvents)); }; }, [timeout]); return idle; } function useInterval(fn, interval, { autoInvoke = false } = {}) { const [active, setActive] = React.useState(false); const intervalRef = React.useRef(null); const fnRef = React.useRef(null); const start = () => { setActive((old) => { if (!old && (!intervalRef.current || intervalRef.current === -1)) { intervalRef.current = window.setInterval(fnRef.current, interval); } return true; }); }; const stop = () => { setActive(false); window.clearInterval(intervalRef.current || -1); intervalRef.current = -1; }; const toggle = () => { if (active) { stop(); } else { start(); } }; React.useEffect(() => { fnRef.current = fn; active && start(); return stop; }, [fn, active, interval]); React.useEffect(() => { if (autoInvoke) { start(); } }, []); return { start, stop, toggle, active }; } function useListState(initialValue = []) { const [state, setState] = React.useState(initialValue); const append = (...items) => setState((current) => [...current, ...items]); const prepend = (...items) => setState((current) => [...items, ...current]); const insert = (index, ...items) => setState((current) => [...current.slice(0, index), ...items, ...current.slice(index)]); const apply = (fn) => setState((current) => current.map((item, index) => fn(item, index))); const remove = (...indices) => setState((current) => current.filter((_, index) => !indices.includes(index))); const pop = () => setState((current) => { const cloned = [...current]; cloned.pop(); return cloned; }); const shift = () => setState((current) => { const cloned = [...current]; cloned.shift(); return cloned; }); const reorder = ({ from, to }) => setState((current) => { const cloned = [...current]; const item = current[from]; cloned.splice(from, 1); cloned.splice(to, 0, item); return cloned; }); const swap = ({ from, to }) => setState((current) => { const cloned = [...current]; const fromItem = cloned[from]; const toItem = cloned[to]; cloned.splice(to, 1, fromItem); cloned.splice(from, 1, toItem); return cloned; }); const setItem = (index, item) => setState((current) => { const cloned = [...current]; cloned[index] = item; return cloned; }); const setItemProp = (index, prop, value) => setState((current) => { const cloned = [...current]; cloned[index] = { ...cloned[index], [prop]: value }; return cloned; }); const applyWhere = (condition, fn) => setState( (current) => current.map((item, index) => condition(item, index) ? fn(item, index) : item) ); const filter = (fn) => { setState((current) => current.filter(fn)); }; return [ state, { setState, append, prepend, insert, pop, shift, apply, applyWhere, remove, reorder, swap, setItem, setItemProp, filter } ]; } function useWindowEvent(type, listener, options) { React.useEffect(() => { window.addEventListener(type, listener, options); return () => window.removeEventListener(type, listener, options); }, [type, listener]); } function serializeJSON(value, hookName = "use-local-storage") { try { return JSON.stringify(value); } catch (error) { throw new Error(`@mantine/hooks ${hookName}: Failed to serialize the value`); } } function deserializeJSON(value) { try { return value && JSON.parse(value); } catch { return value; } } function createStorageHandler(type) { const getItem = (key) => { try { return window[type].getItem(key); } catch (error) { console.warn("use-local-storage: Failed to get value from storage, localStorage is blocked"); return null; } }; const setItem = (key, value) => { try { window[type].setItem(key, value); } catch (error) { console.warn("use-local-storage: Failed to set value to storage, localStorage is blocked"); } }; const removeItem = (key) => { try { window[type].removeItem(key); } catch (error) { console.warn( "use-local-storage: Failed to remove value from storage, localStorage is blocked" ); } }; return { getItem, setItem, removeItem }; } function createStorage(type, hookName) { const eventName = type === "localStorage" ? "mantine-local-storage" : "mantine-session-storage"; const { getItem, setItem, removeItem } = createStorageHandler(type); return function useStorage({ key, defaultValue, getInitialValueInEffect = true, deserialize = deserializeJSON, serialize = (value) => serializeJSON(value, hookName) }) { const readStorageValue = React.useCallback( (skipStorage) => { let storageBlockedOrSkipped; try { storageBlockedOrSkipped = typeof window === "undefined" || !(type in window) || window[type] === null || !!skipStorage; } catch (_e) { storageBlockedOrSkipped = true; } if (storageBlockedOrSkipped) { return defaultValue; } const storageValue = getItem(key); return storageValue !== null ? deserialize(storageValue) : defaultValue; }, [key, defaultValue] ); const [value, setValue] = React.useState(readStorageValue(getInitialValueInEffect)); const setStorageValue = React.useCallback( (val) => { if (val instanceof Function) { setValue((current) => { const result = val(current); setItem(key, serialize(result)); window.dispatchEvent( new CustomEvent(eventName, { detail: { key, value: val(current) } }) ); return result; }); } else { setItem(key, serialize(val)); window.dispatchEvent(new CustomEvent(eventName, { detail: { key, value: val } })); setValue(val); } }, [key] ); const removeStorageValue = React.useCallback(() => { removeItem(key); window.dispatchEvent(new CustomEvent(eventName, { detail: { key, value: defaultValue } })); }, []); useWindowEvent("storage", (event) => { if (event.storageArea === window[type] && event.key === key) { setValue(deserialize(event.newValue ?? void 0)); } }); useWindowEvent(eventName, (event) => { if (event.detail.key === key) { setValue(event.detail.value); } }); React.useEffect(() => { if (defaultValue !== void 0 && value === void 0) { setStorageValue(defaultValue); } }, [defaultValue, value, setStorageValue]); React.useEffect(() => { const val = readStorageValue(); val !== void 0 && setStorageValue(val); }, [key]); return [value === void 0 ? defaultValue : value, setStorageValue, removeStorageValue]; }; } function readValue(type) { const { getItem } = createStorageHandler(type); return function read({ key, defaultValue, deserialize = deserializeJSON }) { let storageBlockedOrSkipped; try { storageBlockedOrSkipped = typeof window === "undefined" || !(type in window) || window[type] === null; } catch (_e) { storageBlockedOrSkipped = true; } if (storageBlockedOrSkipped) { return defaultValue; } const storageValue = getItem(key); return storageValue !== null ? deserialize(storageValue) : defaultValue; }; } function useLocalStorage(props) { return createStorage("localStorage", "use-local-storage")(props); } var readLocalStorageValue = readValue("localStorage"); function useSessionStorage(props) { return createStorage("sessionStorage", "use-session-storage")(props); } var readSessionStorageValue = readValue("sessionStorage"); function assignRef(ref, value) { if (typeof ref === "function") { ref(value); } else if (typeof ref === "object" && ref !== null && "current" in ref) { ref.current = value; } } function mergeRefs(...refs) { return (node) => { refs.forEach((ref) => assignRef(ref, node)); }; } function useMergedRef(...refs) { return React.useCallback(mergeRefs(...refs), refs); } function useMouse(options = { resetOnExit: false }) { const [position, setPosition] = React.useState({ x: 0, y: 0 }); const ref = React.useRef(null); const setMousePosition = (event) => { if (ref.current) { const rect = event.currentTarget.getBoundingClientRect(); const x = Math.max( 0, Math.round(event.pageX - rect.left - (window.pageXOffset || window.scrollX)) ); const y = Math.max( 0, Math.round(event.pageY - rect.top - (window.pageYOffset || window.scrollY)) ); setPosition({ x, y }); } else { setPosition({ x: event.clientX, y: event.clientY }); } }; const resetMousePosition = () => setPosition({ x: 0, y: 0 }); React.useEffect(() => { const element = ref?.current ? ref.current : document; element.addEventListener("mousemove", setMousePosition); if (options.resetOnExit) { element.addEventListener("mouseleave", resetMousePosition); } return () => { element.removeEventListener("mousemove", setMousePosition); if (options.resetOnExit) { element.removeEventListener("mouseleave", resetMousePosition); } }; }, [ref.current]); return { ref, ...position }; } function clampUseMovePosition(position) { return { x: clamp(position.x, 0, 1), y: clamp(position.y, 0, 1) }; } function useMove(onChange, handlers, dir = "ltr") { const ref = React.useRef(null); const mounted = React.useRef(false); const isSliding = React.useRef(false); const frame = React.useRef(0); const [active, setActive] = React.useState(false); React.useEffect(() => { mounted.current = true; }, []); React.useEffect(() => { const onScrub = ({ x, y }) => { cancelAnimationFrame(frame.current); frame.current = requestAnimationFrame(() => { if (mounted.current && ref.current) { ref.current.style.userSelect = "none"; const rect = ref.current.getBoundingClientRect(); if (rect.width && rect.height) { const _x = clamp((x - rect.left) / rect.width, 0, 1); onChange({ x: dir === "ltr" ? _x : 1 - _x, y: clamp((y - rect.top) / rect.height, 0, 1) }); } } }); }; const bindEvents = () => { document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", stopScrubbing); document.addEventListener("touchmove", onTouchMove); document.addEventListener("touchend", stopScrubbing); }; const unbindEvents = () => { document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", stopScrubbing); document.removeEventListener("touchmove", onTouchMove); document.removeEventListener("touchend", stopScrubbing); }; const startScrubbing = () => { if (!isSliding.current && mounted.current) { isSliding.current = true; typeof handlers?.onScrubStart === "function" && handlers.onScrubStart(); setActive(true); bindEvents(); } }; const stopScrubbing = () => { if (isSliding.current && mounted.current) { isSliding.current = false; setActive(false); unbindEvents(); setTimeout(() => { typeof handlers?.onScrubEnd === "function" && handlers.onScrubEnd(); }, 0); } }; const onMouseDown = (event) => { startScrubbing(); event.preventDefault(); onMouseMove(event); }; const onMouseMove = (event) => onScrub({ x: event.clientX, y: event.clientY }); const onTouchStart = (event) => { if (event.cancelable) { event.preventDefault(); } startScrubbing(); onTouchMove(event); }; const onTouchMove = (event) => { if (event.cancelable) { event.preventDefault(); } onScrub({ x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY }); }; ref.current?.addEventListener("mousedown", onMouseDown); ref.current?.addEventListener("touchstart", onTouchStart, { passive: false }); return () => { if (ref.current) { ref.current.removeEventListener("mousedown", onMouseDown); ref.current.removeEventListener("touchstart", onTouchStart); } }; }, [dir, onChange]); return { ref, active }; } function useUncontrolled({ value, defaultValue, finalValue, onChange = () => { } }) { const [uncontrolledValue, setUncontrolledValue] = React.useState( defaultValue !== void 0 ? defaultValue : finalValue ); const handleUncontrolledChange = (val, ...payload) => { setUncontrolledValue(val); onChange?.(val, ...payload); }; if (value !== void 0) { return [value, onChange, true]; } return [uncontrolledValue, handleUncontrolledChange, false]; } function range2(start, end) { const length = end - start + 1; return Array.from({ length }, (_, index) => index + start); } var DOTS = "dots"; function usePagination({ total, siblings = 1, boundaries = 1, page, initialPage = 1, onChange }) { const _total = Math.max(Math.trunc(total), 0); const [activePage, setActivePage] = useUncontrolled({ value: page, onChange, defaultValue: initialPage, finalValue: initialPage }); const setPage = (pageNumber) => { if (pageNumber <= 0) { setActivePage(1); } else if (pageNumber > _total) { setActivePage(_total); } else { setActivePage(pageNumber); } }; const next = () => setPage(activePage + 1); const previous = () => setPage(activePage - 1); const first = () => setPage(1); const last = () => setPage(_total); const paginationRange = React.useMemo(() => { const totalPageNumbers = siblings * 2 + 3 + boundaries * 2; if (totalPageNumbers >= _total) { return range2(1, _total); } const leftSiblingIndex = Math.max(activePage - siblings, boundaries); const rightSiblingIndex = Math.min(activePage + siblings, _total - boundaries); const shouldShowLeftDots = leftSiblingIndex > boundaries + 2; const shouldShowRightDots = rightSiblingIndex < _total - (boundaries + 1); if (!shouldShowLeftDots && shouldShowRightDots) { const leftItemCount = siblings * 2 + boundaries + 2; return [...range2(1, leftItemCount), DOTS, ...range2(_total - (boundaries - 1), _total)]; } if (shouldShowLeftDots && !shouldShowRightDots) { const rightItemCount = boundaries + 1 + 2 * siblings; return [...range2(1, boundaries), DOTS, ...range2(_total - rightItemCount, _total)]; } return [ ...range2(1, boundaries), DOTS, ...range2(leftSiblingIndex, rightSiblingIndex), DOTS, ...range2(_total - boundaries + 1, _total) ]; }, [_total, siblings, activePage]); return { range: paginationRange, active: activePage, setPage, next, previous, first, last }; } function useQueue({ initialValues = [], limit }) { const [state, setState] = React.useState({ state: initialValues.slice(0, limit), queue: initialValues.slice(limit) }); const add = (...items) => setState((current) => { const results = [...current.state, ...current.queue, ...items]; return { state: results.slice(0, limit), queue: results.slice(limit) }; }); const update = (fn) => setState((current) => { const results = fn([...current.state, ...current.queue]); return { state: results.slice(0, limit), queue: results.slice(limit) }; }); const cleanQueue = () => setState((current) => ({ state: current.state, queue: [] })); return { state: state.state, queue: state.queue, add, update, cleanQueue }; } function usePageLeave(onPageLeave) { React.useEffect(() => { document.documentElement.addEventListener("mouseleave", onPageLeave); return () => document.documentElement.removeEventListener("mouseleave", onPageLeave); }, []); } function useReducedMotion(initialValue, options) { return useMediaQuery("(prefers-reduced-motion: reduce)", initialValue, options); } var easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; var getRelativePosition = ({ axis, target, parent, alignment, offset, isList }) => { if (!target || !parent && typeof document === "undefined") { return 0; } const isCustomParent = !!parent; const parentElement = parent || document.body; const parentPosition = parentElement.getBoundingClientRect(); const targetPosition = target.getBoundingClientRect(); const getDiff = (property) => targetPosition[property] - parentPosition[property]; if (axis === "y") { const diff = getDiff("top"); if (diff === 0) { return 0; } if (alignment === "start") { const distance = diff - offset; const shouldScroll = distance <= targetPosition.height * (isList ? 0 : 1) || !isList; return shouldScroll ? distance : 0; } const parentHeight = isCustomParent ? parentPosition.height : window.innerHeight; if (alignment === "end") { const distance = diff + offset - parentHeight + targetPosition.height; const shouldScroll = distance >= -targetPosition.height * (isList ? 0 : 1) || !isList; return shouldScroll ? distance : 0; } if (alignment === "center") { return diff - parentHeight / 2 + targetPosition.height / 2; } return 0; } if (axis === "x") { const diff = getDiff("left"); if (diff === 0) { return 0; } if (alignment === "start") { const distance = diff - offset; const shouldScroll = distance <= targetPosition.width || !isList; return shouldScroll ? distance : 0; } const parentWidth = isCustomParent ? parentPosition.width : window.innerWidth; if (alignment === "end") { const distance = diff + offset - parentWidth + targetPosition.width; const shouldScroll = distance >= -targetPosition.width || !isList; return shouldScroll ? distance : 0; } if (alignment === "center") { return diff - parentWidth / 2 + targetPosition.width / 2; } return 0; } return 0; }; var getScrollStart = ({ axis, parent }) => { if (!parent && typeof document === "undefined") { return 0; } const method = axis === "y" ? "scrollTop" : "scrollLeft"; if (parent) { return parent[method]; } const { body, documentElement } = document; return body[method] + documentElement[method]; }; var setScrollParam = ({ axis, parent, distance }) => { if (!parent && typeof document === "undefined") { return; } const method = axis === "y" ? "scrollTop" : "scrollLeft"; if (parent) { parent[method] = distance; } else { const { body, documentElement } = document; body[method] = distance; documentElement[method] = distance; } }; function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing = easeInOutQuad, offset = 0, cancelable = true, isList = false } = {}) { const frameID = React.useRef(0); const startTime = React.useRef(0); const shouldStop = React.useRef(false); const scrollableRef = React.useRef(null); const targetRef = React.useRef(null); const reducedMotion = useReducedMotion(); const cancel = () => { if (frameID.current) { cancelAnimationFrame(frameID.current); } }; const scrollIntoView = React.useCallback( ({ alignment = "start" } = {}) => { shouldStop.current = false; if (frameID.current) { cancel(); } const start = getScrollStart({ parent: scrollableRef.current, axis }) ?? 0; const change = getRelativePosition({ parent: scrollableRef.current, target: targetRef.current, axis, alignment, offset, isList }) - (scrollableRef.current ? 0 : start); function animateScroll() { if (startTime.current === 0) { startTime.current = performance.now(); } const now = performance.now(); const elapsed = now - startTime.current; const t = reducedMotion || duration === 0 ? 1 : elapsed / duration; const distance = start + change * easing(t); setScrollParam({ parent: scrollableRef.current, axis, distance }); if (!shouldStop.current && t < 1) { frameID.current = requestAnimationFrame(animateScroll); } else { typeof onScrollFinish === "function" && onScrollFinish(); startTime.current = 0; frameID.current = 0; cancel(); } } animateScroll(); }, [axis, duration, easing, isList, offset, onScrollFinish, reducedMotion] ); const handleStop = () => { if (cancelable) { shouldStop.current = true; } }; useWindowEvent("wheel", handleStop, { passive: true }); useWindowEvent("touchmove", handleStop, { passive: true }); React.useEffect(() => cancel, []); return { scrollableRef, targetRef, scrollIntoView, cancel }; } var defaultState = { x: 0, y: 0, width: 0, height: 0, top: 0, left: 0, bottom: 0, right: 0 }; function useResizeObserver(options) { const frameID = React.useRef(0); const ref = React.useRef(null); const [rect, setRect] = React.useState(defaultState); const observer = React.useMemo( () => typeof window !== "undefined" ? new ResizeObserver((entries) => { const entry = entries[0]; if (entry) { cancelAnimationFrame(frameID.current); frameID.current = requestAnimationFrame(() => { if (ref.current) { setRect(entry.contentRect); } }); } }) : null, [] ); React.useEffect(() => { if (ref.current) { observer?.observe(ref.current, options); } return () => { observer?.disconnect(); if (frameID.current) { cancelAnimationFrame(frameID.current); } }; }, [ref.current]); return [ref, rect]; } function useElementSize(options) { const [ref, { width, height }] = useResizeObserver(options); return { ref, width, height }; } function shallowCompare(prevValue, currValue) { if (!prevValue || !currValue) { return false; } if (prevValue === currValue) { return true; } if (prevValue.length !== currValue.length) { return false; } for (let i = 0; i < prevValue.length; i += 1) { if (!shallowEqual(prevValue[i], currValue[i])) { return false; } } return true; } function useShallowCompare(dependencies) { const ref = React.useRef([]); const updateRef = React.useRef(0); if (!shallowCompare(ref.current, dependencies)) { ref.current = dependencies; updateRef.current += 1; } return [updateRef.current]; } function useShallowEffect(cb, dependencies) { React.useEffect(cb, useShallowCompare(dependencies)); } function useToggle(options = [false, true]) { const [[option], toggle] = React.useReducer((state, action) => { const value = action instanceof Function ? action(state[0]) : action; const index = Math.abs(state.indexOf(value)); return state.slice(index).concat(state.slice(0, index)); }, options); return [option, toggle]; } var eventListerOptions = { passive: true }; function useViewportSize() { const [windowSize, setWindowSize] = React.useState({ width: 0, height: 0 }); const setSize = React.useCallback(() => { setWindowSize({ width: window.innerWidth || 0, height: window.innerHeight || 0 }); }, []); useWindowEvent("resize", setSize, eventListerOptions); useWindowEvent("orientationchange", setSize, eventListerOptions); React.useEffect(setSize, []); return windowSize; } function getScrollPosition() { return typeof window !== "undefined" ? { x: window.pageXOffset, y: window.pageYOffset } : { x: 0, y: 0 }; } function scrollTo({ x, y }) { if (typeof window !== "undefined") { const scrollOptions = { behavior: "smooth" }; if (typeof x === "number") { scrollOptions.left = x; } if (typeof y === "number") { scrollOptions.top = y; } window.scrollTo(scrollOptions); } } function useWindowScroll() { const [position, setPosition] = React.useState({ x: 0, y: 0 }); useWindowEvent("scroll", () => setPosition(getScrollPosition())); useWindowEvent("resize", () => setPosition(getScrollPosition())); React.useEffect(() => { setPosition(getScrollPosition()); }, []); return [position, scrollTo]; } function useIntersection(options) { const [entry, setEntry] = React.useState(null); const observer = React.useRef(null); const ref = React.useCallback( (element) => { if (observer.current) { observer.current.disconnect(); observer.current = null; } if (element === null) { setEntry(null); return; } observer.current = new IntersectionObserver(([_entry]) => { setEntry(_entry); }, options); observer.current.observe(element); }, [options?.rootMargin, options?.root, options?.threshold] ); return { ref, entry }; } function useHash({ getInitialValueInEffect = true } = {}) { const [hash, setHash] = React.useState( getInitialValueInEffect ? "" : window.location.hash || "" ); const setHashHandler = (value) => { const valueWithHash = value.startsWith("#") ? value : `#${value}`; window.location.hash = valueWithHash; setHash(valueWithHash); }; useWindowEvent("hashchange", () => { const newHash = window.location.hash; if (hash !== newHash) { setHash(newHash); } }); React.useEffect(() => { if (getInitialValueInEffect) { setHash(window.location.hash); } }, []); return [hash, setHashHandler]; } function parseHotkey(hotkey) { const keys = hotkey.toLowerCase().split("+").map((part) => part.trim()); const modifiers = { alt: keys.includes("alt"), ctrl: keys.includes("ctrl"), meta: keys.includes("meta"), mod: keys.includes("mod"), shift: keys.includes("shift"), plus: keys.includes("[plus]") }; const reservedKeys = ["alt", "ctrl", "meta", "shift", "mod"]; const freeKey = keys.find((key) => !reservedKeys.includes(key)); return { ...modifiers, key: freeKey === "[plus]" ? "+" : freeKey }; } function isExactHotkey(hotkey, event) { const { alt, ctrl, meta, mod, shift, key } = hotkey; const { altKey, ctrlKey, metaKey, shiftKey, key: pressedKey } = event; if (alt !== altKey) { return false; } if (mod) { if (!ctrlKey && !metaKey) { return false; } } else { if (ctrl !== ctrlKey) { return false; } if (meta !== metaKey) { return false; } } if (shift !== shiftKey) { return false; } if (key && (pressedKey.toLowerCase() === key.toLowerCase() || event.code.replace("Key", "").toLowerCase() === key.toLowerCase())) { return true; } return false; } function getHotkeyMatcher(hotkey) { return (event) => isExactHotkey(parseHotkey(hotkey), event); } function getHotkeyHandler(hotkeys) { return (event) => { const _event = "nativeEvent" in event ? event.nativeEvent : event; hotkeys.forEach(([hotkey, handler, options = { preventDefault: true }]) => { if (getHotkeyMatcher(hotkey)(_event)) { if (options.preventDefault) { event.preventDefault(); } handler(_event); } }); }; } function shouldFireEvent(event, tagsToIgnore, triggerOnContentEditable = false) { if (event.target instanceof HTMLElement) { if (triggerOnContentEditable) { return !tagsToIgnore.includes(event.target.tagName); } return !event.target.isContentEditable && !tagsToIgnore.includes(event.target.tagName); } return true; } function useHotkeys(hotkeys, tagsToIgnore = ["INPUT", "TEXTAREA", "SELECT"], triggerOnContentEditable = false) { React.useEffect(() => { const keydownListener = (event) => { hotkeys.forEach(([hotkey, handler, options = { preventDefault: true }]) => { if (getHotkeyMatcher(hotkey)(event) && shouldFireEvent(event, tagsToIgnore, triggerOnContentEditable)) { if (options.preventDefault) { event.preventDefault(); } handler(event); } }); }; document.documentElement.addEventListener("keydown", keydownListener); return () => document.documentElement.removeEventListener("keydown", keydownListener); }, [hotkeys]); } function getFullscreenElement() { const _document = window.document; const fullscreenElement = _document.fullscreenElement || _document.webkitFullscreenElement || _document.mozFullScreenElement || _document.msFullscreenElement; return fullscreenElement; } function exitFullscreen() { const _document = window.document; if (typeof _document.exitFullscreen === "function") { return _document.exitFullscreen(); } if (typeof _document.msExitFullscreen === "function") { return _document.msExitFullscreen(); } if (typeof _document.webkitExitFullscreen === "function") { return _document.webkitExitFullscreen(); } if (typeof _document.mozCancelFullScreen === "function") { return _document.mozCancelFullScreen(); } return null; } function enterFullScreen(element) { const _element = element; return _element.requestFullscreen?.() || _element.msRequestFullscreen?.() || _element.webkitEnterFullscreen?.() || _element.webkitRequestFullscreen?.() || _element.mozRequestFullscreen?.(); } var prefixes = ["", "webkit", "moz", "ms"]; function addEvents(element, { onFullScreen, onError }) { prefixes.forEach((prefix) => { element.addEventListener(`${prefix}fullscreenchange`, onFullScreen); element.addEventListener(`${prefix}fullscreenerror`, onError); }); return () => { prefixes.forEach((prefix) => { element.removeEventListener(`${prefix}fullscreenchange`, onFullScreen); element.removeEventListener(`${prefix}fullscreenerror`, onError); }); }; } function useFullscreen() { const [fullscreen, setFullscreen] = React.useState(false); const _ref = React.useRef(null); const handleFullscreenChange = React.useCallback( (event) => { setFullscreen(event.target === getFullscreenElement()); }, [setFullscreen] ); const handleFullscreenError = React.useCallback( (event) => { setFullscreen(false); console.error( `[@mantine/hooks] use-fullscreen: Error attempting full-screen mode method: ${event} (${event.target})` ); }, [setFullscreen] ); const toggle = React.useCallback(async () => { if (!getFullscreenElement()) { await enterFullScreen(_ref.current); } else { await exitFullscreen(); } }, []); const ref = React.useCallback((element) => { if (element === null) { _ref.current = window.document.documentElement; } else { _ref.current = element; } }, []); React.useEffect(() => { if (!_ref.current && window.document) { _ref.current = window.document.documentElement; return addEvents(_ref.current, { onFullScreen: handleFullscreenChange, onError: handleFullscreenError }); } if (_ref.current) { return addEvents(_ref.current, { onFullScreen: handleFullscreenChange, onError: handleFullscreenError }); } return void 0; }, [_ref.current]); return { ref, toggle, fullscreen }; } function useLogger(componentName, props) { React.useEffect(() => { console.log(`${componentName} mounted`, ...props); return () => console.log(`${componentName} unmounted`); }, []); useDidUpdate(() => { console.log(`${componentName} updated`, ...props); }, props); return null; } function useHover() { const [hovered, setHovered] = React.useState(false); const ref = React.useRef(null); const onMouseEnter = React.useCallback(() => setHovered(true), []); const onMouseLeave = React.useCallback(() => setHovered(false), []); React.useEffect(() => { if (ref.current) { ref.current.addEventListener("mouseenter", onMouseEnter); ref.current.addEventListener("mouseleave", onMouseLeave); return () => { ref.current?.removeEventListener("mouseenter", onMouseEnter); ref.current?.removeEventListener("mouseleave", onMouseLeave); }; } return void 0; }, [ref.current]); return { ref, hovered }; } function useValidatedState(initialValue, validation, initialValidationState) { const [value, setValue] = React.useState(initialValue); const [lastValidValue, setLastValidValue] = React.useState( validation(initialValue) ? initialValue : void 0 ); const [valid, setValid] = React.useState( typeof initialValidationState === "boolean" ? initialValidationState : validation(initialValue) ); const onChange = (val) => { if (validation(val)) { setLastValidValue(val); setValid(true); } else { setValid(false); } setValue(val); }; return [{ value, lastValidValue, valid }, onChange]; } function isMacOS(userAgent) { const macosPattern = /(Macintosh)|(MacIntel)|(MacPPC)|(Mac68K)/i; return macosPattern.test(userAgent); } function isIOS(userAgent) { const iosPattern = /(iPhone)|(iPad)|(iPod)/i; return iosPattern.test(userAgent); } function isWindows(userAgent) { const windowsPattern = /(Win32)|(Win64)|(Windows)|(WinCE)/i; return windowsPattern.test(userAgent); } function isAndroid(userAgent) { const androidPattern = /Android/i; return androidPattern.test(userAgent); } function isLinux(userAgent) { const linuxPattern = /Linux/i; return linuxPattern.test(userAgent); } function getOS() { if (typeof window === "undefined") { return "undetermined"; } const { userAgent } = window.navigator; if (isIOS(userAgent) || isMacOS(userAgent) && "ontouchend" in document) { return "ios"; } if (isMacOS(userAgent)) { return "macos"; } if (isWindows(userAgent)) { return "windows"; } if (isAndroid(userAgent)) { return "android"; } if (isLinux(userAgent)) { return "linux"; } return "undetermined"; } function useOs(options = { getValueInEffect: true }) { const [value, setValue] = React.useState(options.getValueInEffect ? "undetermined" : getOS()); useIsomorphicEffect(() => { if (options.getValueInEffect) { setValue(getOS); } }, []); return value; } function useSetState(initialState) { const [state, setState] = React.useState(initialState); const _setState = React.useCallback( (statePartial) => setState((current) => ({ ...current, ...typeof statePartial === "function" ? statePartial(current) : statePartial })), [] ); return [state, _setState]; } function getInputOnChange(setValue) { return (val) => { if (!val) { setValue(val); } else if (typeof val === "function") { setValue(val); } else if (typeof val === "object" && "nativeEvent" in val) { const { currentTarget } = val; if (currentTarget.type === "checkbox") { setValue(currentTarget.checked); } else { setValue(currentTarget.value); } } else { setValue(val); } }; } function useInputState(initialState) { const [value, setValue] = React.useState(initialState); return [value, getInputOnChange(setValue)]; } function useEventListener(type, listener, options) { const ref = React.useRef(null); React.useEffect(() => { if (ref.current) { ref.current.addEventListener(type, listener, options); return () => ref.current?.removeEventListener(type, listener, options); } return void 0; }, [listener, options]); return ref; } function useDisclosure(initialState = false, callbacks) { const { onOpen, onClose } = callbacks || {}; const [opened, setOpened] = React.useState(initialState); const open = React.useCallback(() => { setOpened((isOpened) => { if (!isOpened) { onOpen?.(); return true; } return isOpened; }); }, [onOpen]); const close = React.useCallback(() => { setOpened((isOpened) => { if (isOpened) { onClose?.(); return false; } return isOpened; }); }, [onClose]); const toggle = React.useCallback(() => { opened ? close() : open(); }, [close, open, opened]); return [opened, { open, close, toggle }]; } function containsRelatedTarget(event) { if (event.currentTarget instanceof HTMLElement && event.relatedTarget instanceof HTMLElement) { return event.currentTarget.contains(event.relatedTarget); } return false; } function useFocusWithin({ onBlur, onFocus } = {}) { const ref = React.useRef(null); const [focused, setFocused] = React.useState(false); const focusedRef = React.useRef(false); const _setFocused = (value) => { setFocused(value); focusedRef.current = value; }; const handleFocusIn = (event) => { if (!focusedRef.current) { _setFocused(true); onFocus?.(event); } }; const handleFocusOut = (event) => { if (focusedRef.current && !containsRelatedTarget(event)) { _setFocused(false); onBlur?.(event); } }; React.useEffect(() => { if (ref.current) { ref.current.addEventListener("focusin", handleFocusIn); ref.current.addEventListener("focusout", handleFocusOut); return () => { ref.current?.removeEventListener("focusin", handleFocusIn); ref.current?.removeEventListener("focusout", handleFocusOut); }; } return void 0; }, [handleFocusIn, handleFocusOut]); return { ref, focused }; } function getConnection() { if (typeof navigator === "undefined") { return {}; } const _navigator = navigator; const connection = _navigator.connection || _navigator.mozConnection || _navigator.webkitConnection; if (!connection) { return {}; } return { downlink: connection?.downlink, downlinkMax: connection?.downlinkMax, effectiveType: connection?.effectiveType, rtt: connection?.rtt, saveData: connection?.saveData, type: connection?.type }; } function useNetwork() { const [status, setStatus] = React.useState({ online: true }); const handleConnectionChange = React.useCallback( () => setStatus((current) => ({ ...current, ...getConnection() })), [] ); useWindowEvent("online", () => setStatus({ online: true, ...getConnection() })); useWindowEvent("offline", () => setStatus({ online: false, ...getConnection() })); React.useEffect(() => { const _navigator = navigator; if (_navigator.connection) { setStatus({ online: _navigator.onLine, ...getConnection() }); _navigator.connection.addEventListener("change", handleConnectionChange); return () => _navigator.connection.removeEventListener("change", handleConnectionChange); } if (typeof _navigator.onLine === "boolean") { setStatus((current) => ({ ...current, online: _navigator.onLine })); } return void 0; }, []); return status; } function useTimeout(callback, delay, options = { autoInvoke: false }) { const timeoutRef = React.useRef(null); const start = React.useCallback( (...callbackParams) => { if (!timeoutRef.current) { timeoutRef.current = window.setTimeout(() => { callback(callbackParams); timeoutRef.current = null; }, delay); } }, [delay] ); const clear = React.useCallback(() => { if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); timeoutRef.current = null; } }, []); React.useEffect(() => { if (options.autoInvoke) { start(); } return clear; }, [clear, start]); return { start, clear }; } function useTextSelection() { const forceUpdate = useForceUpdate(); const [selection, setSelection] = React.useState(null); const handleSelectionChange = () => { setSelection(document.getSelection()); forceUpdate(); }; React.useEffect(() => { setSelection(document.getSelection()); document.addEventListener("selectionchange", handleSelectionChange); return () => document.removeEventListener("selectionchange", handleSelectionChange); }, []); return selection; } function usePrevious(value) { const ref = React.useRef(void 0); React.useEffect(() => { ref.current = value; }, [value]); return ref.current; } var MIME_TYPES = { ico: "image/x-icon", png: "image/png", svg: "image/svg+xml", gif: "image/gif" }; function useFavicon(url) { const link = React.useRef(null); useIsomorphicEffect(() => { if (!url) { return; } if (!link.current) { const existingElements = document.querySelectorAll('link[rel*="icon"]'); existingElements.forEach((element2) => document.head.removeChild(element2)); const element = document.createElement("link"); element.rel = "shortcut icon"; link.current = element; document.querySelector("head").appendChild(element); } const splittedUrl = url.split("."); link.current.setAttribute( "type", MIME_TYPES[splittedUrl[splittedUrl.length - 1].toLowerCase()] ); link.current.setAttribute("href", url); }, [url]); } var isFixed = (current, fixedAt) => current <= fixedAt; var isPinnedOrReleased = (current, fixedAt, isCurrentlyPinnedRef, isScrollingUp, onPin, onRelease) => { const isInFixedPosition = isFixed(current, fixedAt); if (isInFixedPosition && !isCurrentlyPinnedRef.current) { isCurrentlyPinnedRef.current = true; onPin?.(); } else if (!isInFixedPosition && isScrollingUp && !isCurrentlyPinnedRef.current) { isCurrentlyPinnedRef.current = true; onPin?.(); } else if (!isInFixedPosition && isCurrentlyPinnedRef.current) { isCurrentlyPinnedRef.current = false; onRelease?.(); } }; var useScrollDirection = () => { const [lastScrollTop, setLastScrollTop] = React.useState(0); const [isScrollingUp, setIsScrollingUp] = React.useState(false); const [isResizing, setIsResizing] = React.useState(false); React.useEffect(() => { let resizeTimer; const onResize = () => { setIsResizing(true); clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { setIsResizing(false); }, 300); }; const onScroll = () => { if (isResizing) { return; } const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop; setIsScrollingUp(currentScrollTop < lastScrollTop); setLastScrollTop(currentScrollTop); }; window.addEventListener("scroll", onScroll); window.addEventListener("resize", onResize); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onResize); }; }, [lastScrollTop, isResizing]); return isScrollingUp; }; function useHeadroom({ fixedAt = 0, onPin, onFix, onRelease } = {}) { const isCurrentlyPinnedRef = React.useRef(false); const isScrollingUp = useScrollDirection(); const [{ y: scrollPosition }] = useWindowScroll(); useIsomorphicEffect(() => { isPinnedOrReleased( scrollPosition, fixedAt, isCurrentlyPinnedRef, isScrollingUp, onPin, onRelease ); }, [scrollPosition]); useIsomorphicEffect(() => { if (isFixed(scrollPosition, fixedAt)) { onFix?.(); } }, [scrollPosition, fixedAt, onFix]); if (isFixed(scrollPosition, fixedAt) || isScrollingUp) { return true; } return false; } function isOpera() { return navigator.userAgent.includes("OPR"); } function useEyeDropper() { const [supported, setSupported] = React.useState(false); useIsomorphicEffect(() => { setSupported(typeof window !== "undefined" && !isOpera() && "EyeDropper" in window); }, []); const open = React.useCallback( (options = {}) => { if (supported) { const eyeDropper = new window.EyeDropper(); return eyeDropper.open(options); } return Promise.resolve(void 0); }, [supported] ); return { supported, open }; } function useInViewport() { const observer = React.useRef(null); const [inViewport, setInViewport] = React.useState(false); const ref = React.useCallback((node) => { if (typeof IntersectionObserver !== "undefined") { if (node && !observer.current) { observer.current = new IntersectionObserver( ([entry]) => setInViewport(entry.isIntersecting) ); } else { observer.current?.disconnect(); } if (node) { observer.current?.observe(node); } else { setInViewport(false); } } }, []); return { ref, inViewport }; } function useMutationObserver(callback, options, target) { const observer = React.useRef(null); const ref = React.useRef(null); React.useEffect(() => { const targetElement = typeof target === "function" ? target() : target; if (targetElement || ref.current) { observer.current = new MutationObserver(callback); observer.current.observe(targetElement || ref.current, options); } return () => { observer.current?.disconnect(); }; }, [callback, options]); return ref; } function useMounted() { const [mounted, setMounted] = React.useState(false); React.useEffect(() => setMounted(true), []); return mounted; } function useStateHistory(initialValue) { const [state, setState] = React.useState({ history: [initialValue], current: 0 }); const set = React.useCallback( (val) => setState((currentState) => { const nextState = [...currentState.history.slice(0, currentState.current + 1), val]; return { history: nextState, current: nextState.length - 1 }; }), [] ); const back = React.useCallback( (steps = 1) => setState((currentState) => ({ history: currentState.history, current: Math.max(0, currentState.current - steps) })), [] ); const forward = React.useCallback( (steps = 1) => setState((currentState) => ({ history: currentState.history, current: Math.min(currentState.history.length - 1, currentState.current + steps) })), [] ); const reset = React.useCallback(() => { setState({ history: [initialValue], current: 0 }); }, [initialValue]); const handlers = React.useMemo(() => ({ back, forward, reset, set }), [back, forward, reset, set]); return [state.history[state.current], handlers, state]; } function useMap(initialState) { const mapRef = React.useRef(new Map(initialState)); const forceUpdate = useForceUpdate(); mapRef.current.set = (...args) => { Map.prototype.set.apply(mapRef.current, args); forceUpdate(); return mapRef.current; }; mapRef.current.clear = (...args) => { Map.prototype.clear.apply(mapRef.current, args); forceUpdate(); }; mapRef.current.delete = (...args) => { const res = Map.prototype.delete.apply(mapRef.current, args); forceUpdate(); return res; }; return mapRef.current; } function useSet(values) { const setRef = React.useRef(new Set(values)); const forceUpdate = useForceUpdate(); setRef.current.add = (...args) => { const res = Set.prototype.add.apply(setRef.current, args); forceUpdate(); return res; }; setRef.current.clear = (...args) => { Set.prototype.clear.apply(setRef.current, args); forceUpdate(); }; setRef.current.delete = (...args) => { const res = Set.prototype.delete.apply(setRef.current, args); forceUpdate(); return res; }; return setRef.current; } function useThrottledCallbackWithClearTimeout(callback, wait) { const handleCallback = useCallbackRef(callback); const latestInArgsRef = React.useRef(null); const latestOutArgsRef = React.useRef(null); const active = React.useRef(true); const waitRef = React.useRef(wait); const timeoutRef = React.useRef(-1); const clearTimeout2 = () => window.clearTimeout(timeoutRef.current); const callThrottledCallback = React.useCallback( (...args) => { handleCallback(...args); latestInArgsRef.current = args; latestOutArgsRef.current = args; active.current = false; }, [handleCallback] ); const timerCallback = React.useCallback(() => { if (latestInArgsRef.current && latestInArgsRef.current !== latestOutArgsRef.current) { callThrottledCallback(...latestInArgsRef.current); timeoutRef.current = window.setTimeout(timerCallback, waitRef.current); } else { active.current = true; } }, [callThrottledCallback]); const throttled = React.useCallback( (...args) => { if (active.current) { callThrottledCallback(...args); timeoutRef.current = window.setTimeout(timerCallback, waitRef.current); } else { latestInArgsRef.current = args; } }, [callThrottledCallback, timerCallback] ); React.useEffect(() => { waitRef.current = wait; }, [wait]); return [throttled, clearTimeout2]; } function useThrottledCallback(callback, wait) { return useThrottledCallbackWithClearTimeout(callback, wait)[0]; } function useThrottledState(defaultValue, wait) { const [value, setValue] = React.useState(defaultValue); const [setThrottledValue, clearTimeout2] = useThrottledCallbackWithClearTimeout(setValue, wait); React.useEffect(() => clearTimeout2, []); return [value, setThrottledValue]; } function useThrottledValue(value, wait) { const [throttledValue, setThrottledValue] = React.useState(value); const valueRef = React.useRef(value); const [throttledSetValue, clearTimeout2] = useThrottledCallbackWithClearTimeout( setThrottledValue, wait ); React.useEffect(() => { if (value !== valueRef.current) { valueRef.current = value; throttledSetValue(value); } }, [throttledSetValue, value]); React.useEffect(() => clearTimeout2, []); return throttledValue; } function useIsFirstRender() { const renderRef = React.useRef(true); if (renderRef.current === true) { renderRef.current = false; return true; } return renderRef.current; } function useOrientation() { const [orientation, setOrientation] = React.useState({ angle: 0, type: "landscape-primary" }); const handleOrientationChange = (event) => { const target = event.currentTarget; setOrientation({ angle: target?.angle || 0, type: target?.type || "landscape-primary" }); }; useIsomorphicEffect(() => { window.screen.orientation?.addEventListener("change", handleOrientationChange); return () => window.screen.orientation?.removeEventListener("change", handleOrientationChange); }, []); return orientation; } function useFetch(url, { autoInvoke = true, ...options } = {}) { const [data, setData] = React.useState(null); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(null); const controller = React.useRef(null); const refetch = React.useCallback(() => { if (!url) { return; } if (controller.current) { controller.current.abort(); } controller.current = new AbortController(); setLoading(true); return fetch(url, { signal: controller.current.signal, ...options }).then((res) => res.json()).then((res) => { setData(res); setLoading(false); return res; }).catch((err) => { setLoading(false); if (err.name !== "AbortError") { setError(err); } return err; }); }, [url]); const abort = React.useCallback(() => { if (controller.current) { controller.current?.abort(""); } }, []); React.useEffect(() => { if (autoInvoke) { refetch(); } return () => { if (controller.current) { controller.current.abort(""); } }; }, [refetch, autoInvoke]); return { data, loading, error, refetch, abort }; } function radiansToDegrees(radians) { return radians * (180 / Math.PI); } function getElementCenter(element) { const rect = element.getBoundingClientRect(); return [rect.left + rect.width / 2, rect.top + rect.height / 2]; } function getAngle(coordinates, element) { const center = getElementCenter(element); const x = coordinates[0] - center[0]; const y = coordinates[1] - center[1]; const deg = radiansToDegrees(Math.atan2(x, y)) + 180; return 360 - deg; } function toFixed(value, digits) { return parseFloat(value.toFixed(digits)); } function getDigitsAfterDot(value) { return value.toString().split(".")[1]?.length || 0; } function normalizeRadialValue(degree, step) { const clamped = clamp(degree, 0, 360); const high = Math.ceil(clamped / step); const low = Math.round(clamped / step); return toFixed( high >= clamped / step ? high * step === 360 ? 0 : high * step : low * step, getDigitsAfterDot(step) ); } function useRadialMove(onChange, { step = 0.01, onChangeEnd, onScrubStart, onScrubEnd } = {}) { const ref = React.useRef(null); const mounted = React.useRef(false); const [active, setActive] = React.useState(false); React.useEffect(() => { mounted.current = true; }, []); React.useEffect(() => { const update = (event, done = false) => { if (ref.current) { ref.current.style.userSelect = "none"; const deg = getAngle([event.clientX, event.clientY], ref.current); const newValue = normalizeRadialValue(deg, step || 1); onChange(newValue); done && onChangeEnd?.(newValue); } }; const beginTracking = () => { onScrubStart?.(); setActive(true); document.addEventListener("mousemove", handleMouseMove, false); document.addEventListener("mouseup", handleMouseUp, false); document.addEventListener("touchmove", handleTouchMove, { passive: false }); document.addEventListener("touchend", handleTouchEnd, false); }; const endTracking = () => { onScrubEnd?.(); setActive(false); document.removeEventListener("mousemove", handleMouseMove, false); document.removeEventListener("mouseup", handleMouseUp, false); document.removeEventListener("touchmove", handleTouchMove, false); document.removeEventListener("touchend", handleTouchEnd, false); }; const onMouseDown = (event) => { beginTracking(); update(event); }; const handleMouseMove = (event) => { update(event); }; const handleMouseUp = (event) => { update(event, true); endTracking(); }; const handleTouchMove = (event) => { event.preventDefault(); update(event.touches[0]); }; const handleTouchEnd = (event) => { update(event.changedTouches[0], true); endTracking(); }; const handleTouchStart = (event) => { event.preventDefault(); beginTracking(); update(event.touches[0]); }; ref.current?.addEventListener("mousedown", onMouseDown); ref.current?.addEventListener("touchstart", handleTouchStart, { passive: false }); return () => { if (ref.current) { ref.current.removeEventListener("mousedown", onMouseDown); ref.current.removeEventListener("touchstart", handleTouchStart); } }; }, [onChange]); return { ref, active }; } exports.assignRef = assignRef; exports.clamp = clamp; exports.clampUseMovePosition = clampUseMovePosition; exports.getHotkeyHandler = getHotkeyHandler; exports.lowerFirst = lowerFirst; exports.mergeRefs = mergeRefs; exports.normalizeRadialValue = normalizeRadialValue; exports.randomId = randomId; exports.range = range; exports.readLocalStorageValue = readLocalStorageValue; exports.readSessionStorageValue = readSessionStorageValue; exports.shallowEqual = shallowEqual; exports.upperFirst = upperFirst; exports.useCallbackRef = useCallbackRef; exports.useClickOutside = useClickOutside; exports.useClipboard = useClipboard; exports.useColorScheme = useColorScheme; exports.useCounter = useCounter; exports.useDebouncedCallback = useDebouncedCallback; exports.useDebouncedState = useDebouncedState; exports.useDebouncedValue = useDebouncedValue; exports.useDidUpdate = useDidUpdate; exports.useDisclosure = useDisclosure; exports.useDocumentTitle = useDocumentTitle; exports.useDocumentVisibility = useDocumentVisibility; exports.useElementSize = useElementSize; exports.useEventListener = useEventListener; exports.useEyeDropper = useEyeDropper; exports.useFavicon = useFavicon; exports.useFetch = useFetch; exports.useFocusReturn = useFocusReturn; exports.useFocusTrap = useFocusTrap; exports.useFocusWithin = useFocusWithin; exports.useForceUpdate = useForceUpdate; exports.useFullscreen = useFullscreen; exports.useHash = useHash; exports.useHeadroom = useHeadroom; exports.useHotkeys = useHotkeys; exports.useHover = useHover; exports.useId = useId; exports.useIdle = useIdle; exports.useInViewport = useInViewport; exports.useInputState = useInputState; exports.useIntersection = useIntersection; exports.useInterval = useInterval; exports.useIsFirstRender = useIsFirstRender; exports.useIsomorphicEffect = useIsomorphicEffect; exports.useListState = useListState; exports.useLocalStorage = useLocalStorage; exports.useLogger = useLogger; exports.useMap = useMap; exports.useMediaQuery = useMediaQuery; exports.useMergedRef = useMergedRef; exports.useMounted = useMounted; exports.useMouse = useMouse; exports.useMove = useMove; exports.useMutationObserver = useMutationObserver; exports.useNetwork = useNetwork; exports.useOrientation = useOrientation; exports.useOs = useOs; exports.usePageLeave = usePageLeave; exports.usePagination = usePagination; exports.usePrevious = usePrevious; exports.useQueue = useQueue; exports.useRadialMove = useRadialMove; exports.useReducedMotion = useReducedMotion; exports.useResizeObserver = useResizeObserver; exports.useScrollIntoView = useScrollIntoView; exports.useSessionStorage = useSessionStorage; exports.useSet = useSet; exports.useSetState = useSetState; exports.useShallowEffect = useShallowEffect; exports.useStateHistory = useStateHistory; exports.useTextSelection = useTextSelection; exports.useThrottledCallback = useThrottledCallback; exports.useThrottledState = useThrottledState; exports.useThrottledValue = useThrottledValue; exports.useTimeout = useTimeout; exports.useToggle = useToggle; exports.useUncontrolled = useUncontrolled; exports.useValidatedState = useValidatedState; exports.useViewportSize = useViewportSize; exports.useWindowEvent = useWindowEvent; exports.useWindowScroll = useWindowScroll; }));