UNPKG

nextjs-app-hooks

Version:

A library of custom React hooks for simplified state management and streamlined component logic.

1,954 lines (1,939 loc) 91.8 kB
import { RefObject } from 'react'; /** * Hook to determine if browser APIs are safely available * * @returns {boolean} - Returns true if running in the browser, false if on the server * * @example * ```tsx * 'use client'; * * import { useIsBrowser } from 'nextjs-app-hooks'; * * export default function MyComponent() { * const isBrowser = useIsBrowser(); * * useEffect(() => { * if (isBrowser) { * // Safe to use browser APIs like localStorage, window, etc. * localStorage.setItem('visited', 'true'); * } * }, [isBrowser]); * * return ( * <div> * {isBrowser ? 'Browser APIs are available' : 'Running on the server'} * </div> * ); * } * ``` */ declare function useIsBrowser(): boolean; /** * Hook to detect if the code is running on the server or client * * @returns {boolean} - Returns true if running on the server, false if running on the client * * @example * ```tsx * 'use client'; * * import { useIsServer } from 'nextjs-app-hooks'; * * export default function MyComponent() { * const isServer = useIsServer(); * * return ( * <div> * {isServer ? 'Rendering on server' : 'Rendering on client'} * </div> * ); * } * ``` */ declare function useIsServer(): boolean; /** * Battery status information */ interface BatteryStatus { /** * Whether the battery is currently charging */ charging: boolean; /** * Estimated time in seconds until the battery is fully charged * Returns Infinity if the battery is discharging */ chargingTime: number; /** * Estimated time in seconds until the battery is fully discharged * Returns Infinity if the battery is charging */ dischargingTime: number; /** * Current battery level, from 0.0 to 1.0 */ level: number; /** * Whether the device is connected to power * This can be true even when charging is false (e.g., battery is full) */ connected: boolean; } /** * Hook state for battery information */ interface BatteryHookState { /** * Whether the Battery Status API is supported by the browser */ isSupported: boolean; /** * Whether the battery information is currently loading */ isLoading: boolean; /** * Any error that occurred while accessing the battery */ error: Error | null; /** * The current battery status information * Will be null if API is not supported, loading, or an error occurred */ battery: BatteryStatus | null; } /** * A hook to access and monitor the device's battery status * * @returns {BatteryHookState} The current battery state and status information */ declare function useBattery(): BatteryHookState; /** * Hook options for configuring the click outside detection */ interface UseClickOutsideOptions { /** * Whether the hook is enabled - can be used to conditionally enable/disable * @default true */ enabled?: boolean; /** * Additional elements to ignore when detecting clicks outside * Useful for linked components (e.g., a button that toggles a dropdown) */ ignoreRefs?: RefObject<HTMLElement>[]; /** * Mouse events to listen for * @default ["mousedown"] */ mouseEvents?: Array<"mousedown" | "mouseup" | "click">; /** * Whether to listen for touch events (for mobile devices) * @default true */ listenForTouchEvents?: boolean; /** * Whether to ignore scrollbar clicks * @default true */ ignoreScrollbar?: boolean; } /** * A hook that detects clicks outside of a specified element * * @param onClickOutside - Callback function to be called when a click outside is detected * @param options - Configuration options for the hook * @returns RefObject to be attached to the element we want to detect clicks outside of * * @example * ```tsx * const Modal = ({ isOpen, onClose }) => { * const modalRef = useClickOutside(() => { * if (isOpen) onClose(); * }); * * if (!isOpen) return null; * * return ( * <div className="modal-overlay"> * <div ref={modalRef} className="modal-content"> * Modal content here * </div> * </div> * ); * }; * ``` */ declare function useClickOutside<T extends HTMLElement = HTMLElement>(onClickOutside: (event: MouseEvent | TouchEvent) => void, options?: UseClickOutsideOptions): RefObject<T | null>; /** * An extended version of useClickOutside that maintains an internal isOpen state * * @param initialState - Initial open state * @param options - Configuration options for the hook * @returns [ref, isOpen, setIsOpen] - The element ref, current state, and state setter * * @example * ```tsx * const Dropdown = () => { * const [dropdownRef, isOpen, setIsOpen] = useClickOutsideState(false); * * return ( * <div> * <button onClick={() => setIsOpen(!isOpen)}>Toggle</button> * {isOpen && ( * <div ref={dropdownRef} className="dropdown-menu"> * Dropdown content * </div> * )} * </div> * ); * }; * ``` */ declare function useClickOutsideState<T extends HTMLElement = HTMLElement>(initialState?: boolean, options?: UseClickOutsideOptions): [RefObject<T>, boolean, React.Dispatch<React.SetStateAction<boolean>>]; /** * Options for configuring the clipboard hook */ interface UseClipboardOptions { /** * Duration in milliseconds to show success/error status before resetting * @default 2000 */ resetDelay?: number; /** * Whether to automatically reset the status after copy * @default true */ autoReset?: boolean; /** * Initial text value to be copied * @default "" */ initialValue?: string; /** * Whether to use only the modern Clipboard API (no fallbacks) * @default false */ modernOnly?: boolean; /** * Callback function to execute on successful copy */ onCopySuccess?: (value: string) => void; /** * Callback function to execute on copy failure */ onCopyError?: (error: Error) => void; } /** * Status of the clipboard operation */ type ClipboardStatus = "idle" | "copied" | "error"; /** * Return type for the useClipboard hook */ interface UseClipboardReturn { /** * Function to copy text to clipboard * @param text - The text to copy (optional if initialValue is set) */ copyToClipboard: (text?: string) => Promise<boolean>; /** * Current value that was copied or will be copied */ value: string; /** * Current status of clipboard operation */ status: ClipboardStatus; /** * Whether the copy operation was successful */ isSuccess: boolean; /** * Whether the copy operation failed */ isError: boolean; /** * Function to manually reset status to idle */ reset: () => void; /** * Error object if copy operation failed */ error: Error | null; /** * Whether clipboard API is supported in current environment */ isSupported: boolean; } /** * A hook for copying text to the clipboard with status tracking * * @param options - Configuration options for the hook * @returns Object with clipboard functions and state * * @example * ```tsx * 'use client'; * * import { useClipboard } from 'nextjs-app-hooks'; * * export default function CopyButton() { * const { copyToClipboard, isSuccess, status } = useClipboard(); * * return ( * <button * onClick={() => copyToClipboard("Text to copy")} * className={isSuccess ? "bg-green-500" : "bg-blue-500"} * > * {status === 'idle' && 'Copy to clipboard'} * {status === 'copied' && 'Copied!'} * {status === 'error' && 'Failed to copy'} * </button> * ); * } * ``` * * @example * ```tsx * // With initial value * const { copyToClipboard, value, isSuccess } = useClipboard({ * initialValue: "https://example.com", * resetDelay: 3000 * }); * * return ( * <div> * <div>Link: {value}</div> * <button onClick={() => copyToClipboard()}> * {isSuccess ? 'Copied!' : 'Copy Link'} * </button> * </div> * ); * ``` */ declare function useClipboard(options?: UseClipboardOptions): UseClipboardReturn; /** * Options for configuring the debounce hook */ interface UseDebounceOptions { /** * Delay in milliseconds before the debounced function is called * @default 500 */ delay?: number; /** * Whether to call the function on the leading edge of the timeout * If true, function is called immediately, then wait for delay before it can be called again * If false, function is called after the delay when inputs stop changing * @default false */ leading?: boolean; /** * Maximum time in milliseconds allowed to elapse between calls * If specified, the function will be called at least once every maxWait ms * @default undefined */ maxWait?: number; /** * Whether to track the pending status of the debounced function * @default false */ trackPending?: boolean; } /** * A hook for creating a debounced function * * @param fn - The function to debounce * @param options - Configuration options for debouncing * @returns An array containing the debounced function, cancel function, pending status, and flush function * * @example * ```tsx * 'use client'; * * import { useDebounce } from 'nextjs-app-hooks'; * * export default function SearchInput() { * const [searchTerm, setSearchTerm] = useState(''); * const [results, setResults] = useState([]); * * const searchAPI = async (term) => { * const response = await fetch(`/api/search?q=${term}`); * const data = await response.json(); * setResults(data); * }; * * const [debouncedSearch, cancelSearch, isPending] = useDebounce(searchAPI, { * delay: 300, * trackPending: true * }); * * const handleInputChange = (e) => { * const value = e.target.value; * setSearchTerm(value); * debouncedSearch(value); * }; * * return ( * <div> * <input * type="text" * value={searchTerm} * onChange={handleInputChange} * placeholder="Search..." * /> * {isPending && <span>Searching...</span>} * <ul> * {results.map(result => ( * <li key={result.id}>{result.name}</li> * ))} * </ul> * </div> * ); * } * ``` * * @example * ```tsx * // With leading edge execution * const [debouncedHandleResize] = useDebounce(() => { * // Update layout dimensions * setDimensions({ * width: window.innerWidth, * height: window.innerHeight * }); * }, { delay: 200, leading: true }); * * useEffect(() => { * window.addEventListener('resize', debouncedHandleResize); * return () => window.removeEventListener('resize', debouncedHandleResize); * }, [debouncedHandleResize]); * ``` */ declare function useDebounce<T extends (...args: any[]) => any>(fn: T, options?: UseDebounceOptions): [ (...args: Parameters<T>) => void, () => void, boolean, () => ReturnType<T> | undefined ]; /** * Extended geolocation options */ interface UseGeolocationOptions extends Omit<PositionOptions, "timeout"> { /** * Whether to watch position continuously or just get it once * @default false */ watchPosition?: boolean; /** * Timeout for position request in milliseconds * @default 10000 (10 seconds) */ timeout?: number; /** * Whether to automatically request permission and position on mount * @default true */ autoRequest?: boolean; /** * Callback for successful position updates */ onSuccess?: (position: GeolocationPosition) => void; /** * Callback for position errors */ onError?: (error: GeolocationPositionError | Error) => void; /** * Permission change callback */ onPermissionChange?: (state: GeolocationPermissionState) => void; } /** * Simplified geolocation position */ interface GeolocationPosition { /** * Latitude in decimal degrees */ latitude: number; /** * Longitude in decimal degrees */ longitude: number; /** * Accuracy of latitude and longitude in meters */ accuracy: number; /** * Altitude in meters above the WGS84 ellipsoid */ altitude: number | null; /** * Accuracy of altitude in meters */ altitudeAccuracy: number | null; /** * Direction of travel in degrees clockwise from true north */ heading: number | null; /** * Current velocity in meters per second */ speed: number | null; /** * Timestamp when position was acquired */ timestamp: number; } /** * Geolocation permission states */ type GeolocationPermissionState = "granted" | "denied" | "prompt" | "unknown"; /** * Return type for the useGeolocation hook */ interface UseGeolocationReturn { /** * Current position data, null if not available */ position: GeolocationPosition | null; /** * Error object if geolocation failed */ error: GeolocationPositionError | Error | null; /** * Whether the geolocation request is in progress */ isLoading: boolean; /** * Whether position watching is active */ isWatching: boolean; /** * Current permission state for geolocation */ permissionState: GeolocationPermissionState; /** * Function to request current position */ getPosition: () => Promise<GeolocationPosition>; /** * Function to start watching position updates */ watchPosition: () => void; /** * Function to stop watching position updates */ stopWatching: () => void; /** * Whether browser supports geolocation API */ isSupported: boolean; } /** * A hook for accessing and tracking geolocation data * * @param options - Configuration options for geolocation requests * @returns Object with position data, status, and control functions * * @example * ```tsx * 'use client'; * * import { useGeolocation } from 'nextjs-app-hooks'; * * export default function LocationComponent() { * const { * position, * error, * isLoading, * getPosition, * isSupported * } = useGeolocation({ enableHighAccuracy: true }); * * if (!isSupported) { * return <p>Geolocation is not supported in your browser.</p>; * } * * return ( * <div> * <button onClick={getPosition} disabled={isLoading}> * {isLoading ? 'Getting location...' : 'Get My Location'} * </button> * * {position && ( * <div> * <p>Latitude: {position.latitude}</p> * <p>Longitude: {position.longitude}</p> * <p>Accuracy: {position.accuracy} meters</p> * </div> * )} * * {error && <p>Error: {error.message}</p>} * </div> * ); * } * ``` * * @example * ```tsx * // Continuous position watching * const { position, isWatching, watchPosition, stopWatching } = useGeolocation({ * watchPosition: true, * enableHighAccuracy: true, * timeout: 15000 * }); * * return ( * <div> * <div> * {isWatching ? ( * <button onClick={stopWatching}>Stop tracking</button> * ) : ( * <button onClick={watchPosition}>Start tracking</button> * )} * </div> * * {position && ( * <div> * <p>Current position:</p> * <p>Lat: {position.latitude.toFixed(6)}, Lng: {position.longitude.toFixed(6)}</p> * {position.speed !== null && <p>Speed: {position.speed} m/s</p>} * </div> * )} * </div> * ); * ``` */ declare function useGeolocation(options?: UseGeolocationOptions): UseGeolocationReturn; /** * Options for configuring the hover hook */ interface UseHoverOptions { /** * Delay in milliseconds before hover state activates * @default 0 */ enterDelay?: number; /** * Delay in milliseconds before hover state deactivates * @default 0 */ leaveDelay?: number; /** * Whether the hover detection is enabled * @default true */ enabled?: boolean; /** * Whether to handle touch events as hover events * @default false */ supportTouch?: boolean; /** * Callback when hover begins */ onHoverStart?: (event: MouseEvent | TouchEvent) => void; /** * Callback when hover ends */ onHoverEnd?: (event: MouseEvent | TouchEvent) => void; /** * Callback when disabled/enabled state changes */ onEnabledChange?: (enabled: boolean) => void; } /** * Return type for the useHover hook */ interface UseHoverReturn<T extends HTMLElement = HTMLDivElement> { /** * Ref to attach to the element to track hover state */ hoverRef: RefObject<T | null>; /** * Whether the element is currently being hovered */ isHovered: boolean; /** * Function to enable hover detection */ enable: () => void; /** * Function to disable hover detection */ disable: () => void; /** * Whether hover detection is currently enabled */ isEnabled: boolean; } /** * A hook for tracking hover state of an element * * @param options - Configuration options for hover detection * @returns Object with hover ref, state, and control functions * * @example * ```tsx * 'use client'; * * import { useHover } from 'nextjs-app-hooks'; * * export default function HoverCard() { * const { hoverRef, isHovered } = useHover<HTMLDivElement>({ * enterDelay: 100, * leaveDelay: 300 * }); * * return ( * <div * ref={hoverRef} * className={`card ${isHovered ? 'card-hovered' : ''}`} * > * Hover over me! * {isHovered && <div className="tooltip">Hello there!</div>} * </div> * ); * } * ``` * * @example * ```tsx * // With callbacks and manual control * const { hoverRef, isHovered, disable, enable } = useHover({ * onHoverStart: () => console.log('Hover started'), * onHoverEnd: () => console.log('Hover ended') * }); * * return ( * <> * <button onClick={isHovered ? disable : enable}> * {isHovered ? 'Disable' : 'Enable'} hover * </button> * <div ref={hoverRef} className="hover-element"> * {isHovered ? 'Hovered!' : 'Hover me'} * </div> * </> * ); * ``` */ declare function useHover<T extends HTMLElement = HTMLDivElement>(options?: UseHoverOptions): UseHoverReturn<T>; /** * Events that can reset the idle timer */ type IdleEvents = "mousemove" | "mousedown" | "resize" | "keydown" | "touchstart" | "wheel" | "visibilitychange" | "scroll"; /** * Options for configuring the idle hook */ interface UseIdleOptions { /** * Idle timeout in milliseconds * @default 60000 (1 minute) */ idleTime?: number; /** * Initial idle state * @default false */ initialState?: boolean; /** * Events that reset the idle timer * @default ["mousemove", "mousedown", "resize", "keydown", "touchstart", "wheel"] */ events?: IdleEvents[]; /** * Minimum mouse movement in pixels to reset the idle timer * @default 10 */ minimumMovement?: number; /** * Whether to track idle state when the document is hidden (tab inactive) * @default true */ trackInBackground?: boolean; /** * Callback when user becomes idle */ onIdle?: () => void; /** * Callback when user becomes active */ onActive?: () => void; /** * Whether the idle detection is enabled * @default true */ enabled?: boolean; /** * Whether to use document visibility to immediately set to idle * @default false */ idleOnVisibilityHidden?: boolean; /** * Whether to store and sync idle state in localStorage * Useful for syncing across tabs * @default false */ syncWithStorage?: boolean; /** * Key to use for localStorage syncing * @default "user-idle-state" */ storageKey?: string; /** * Whether to emit a browser event when idle state changes * Useful for cross-component communication * @default false */ emitEvents?: boolean; /** * Event name for idle state change * @default "user-idle-state-change" */ eventName?: string; /** * How frequently to check for idle time in milliseconds * Lower values are more accurate but less performant * @default 1000 */ pollingInterval?: number; } /** * Return type for the useIdle hook */ interface UseIdleReturn { /** * Whether the user is currently idle */ isIdle: boolean; /** * Time in milliseconds since the last activity */ idleTime: number; /** * Time in milliseconds until the user will be considered idle */ remainingTime: number; /** * Timestamp of last activity */ lastActive: number; /** * Percentage of idle timeout passed (0-100) */ idlePercentage: number; /** * Function to manually set the user to idle */ setIdle: () => void; /** * Function to manually set the user to active */ setActive: () => void; /** * Function to manually reset the idle timer */ reset: () => void; /** * Function to enable idle detection */ enable: () => void; /** * Function to disable idle detection */ disable: () => void; /** * Whether idle detection is currently enabled */ isEnabled: boolean; } /** * A hook for tracking user idle state * * @param options - Configuration options for idle detection * @returns Object with idle state and control functions * * @example * ```tsx * 'use client'; * * import { useIdle } from 'nextjs-app-hooks'; * * export default function IdleDetectionExample() { * const { isIdle, reset } = useIdle({ * idleTime: 10000, // 10 seconds * onIdle: () => console.log('User is idle'), * onActive: () => console.log('User is active') * }); * * return ( * <div> * <p>User is currently {isIdle ? 'idle' : 'active'}</p> * {isIdle && ( * <button onClick={reset}> * I'm still here! * </button> * )} * </div> * ); * } * ``` * * @example * ```tsx * // With custom events and tracking options * const { isIdle, idlePercentage, remainingTime } = useIdle({ * idleTime: 5 * 60 * 1000, // 5 minutes * events: ['mousemove', 'keydown', 'touchstart'], * minimumMovement: 20, * trackInBackground: false * }); * * return ( * <> * <div className="idle-indicator"> * <div * className="idle-progress" * style={{ width: `${idlePercentage}%` }} * /> * </div> * <p>Session timeout in: {Math.ceil(remainingTime / 1000)}s</p> * {isIdle && <SessionTimeoutModal />} * </> * ); * ``` */ declare function useIdle(options?: UseIdleOptions): UseIdleReturn; /** * Options for configuring the intersection observer hook */ interface UseIntersectionObserverOptions { /** * The element that is used as the viewport for checking visibility * If null, defaults to browser viewport * @default null */ root?: Element | Document | null; /** * Margin around the root element * Can have values similar to CSS margin property (e.g. "10px 20px 30px 40px") * @default "0px" */ rootMargin?: string; /** * A threshold or array of thresholds between 0 and 1 * Each threshold indicates what percentage of the target's visibility * triggers the observer callback * @default 0 */ threshold?: number | number[]; /** * Callback function that is triggered when element enters the viewport */ onEnter?: (entry: IntersectionObserverEntry) => void; /** * Callback function that is triggered when element exits the viewport */ onExit?: (entry: IntersectionObserverEntry) => void; /** * Whether to trigger once and then disconnect the observer * Useful for one-time animations or lazy loading * @default false */ triggerOnce?: boolean; /** * Whether to skip creating the intersection observer * Can be used to conditionally enable/disable the hook * @default false */ skip?: boolean; /** * Initial value to use before the observer is attached * @default false */ initialInView?: boolean; /** * Delay in milliseconds before observer is initialized * Useful for cases where the element isn't immediately ready * @default 0 */ delay?: number; /** * Whether to track all intersection changes with detailed entries history * @default false */ trackVisibilityChanges?: boolean; /** * Amount of entries to keep in history when tracking visibility changes * @default 10 */ historySize?: number; /** * Whether to use the "isIntersecting" property for intersection detection * If false, will check if intersectionRatio > 0 * @default true */ useIsIntersecting?: boolean; } /** * Return type for the useIntersectionObserver hook */ interface UseIntersectionObserverReturn<T extends Element> { /** * Ref to attach to the element to observe */ ref: RefObject<T>; /** * Whether the element is currently in view according to the threshold */ isInView: boolean; /** * Current intersection ratio (0 to 1) */ intersectionRatio: number; /** * The most recent IntersectionObserverEntry */ entry: IntersectionObserverEntry | null; /** * History of intersection entries if tracking is enabled */ entryHistory: IntersectionObserverEntry[]; /** * Function to manually reset observation */ reset: () => void; /** * Function to disconnect the observer */ disconnect: () => void; /** * Function to reconnect the observer after disconnection */ observe: () => void; /** * Whether the observer is currently connected */ isConnected: boolean; /** * Time the element entered the viewport (most recent) */ enteredAt: number | null; /** * Time the element exited the viewport (most recent) */ exitedAt: number | null; /** * Duration in milliseconds the element has been in viewport * Will be 0 if element is not in view */ inViewDuration: number; } /** * A hook that uses the Intersection Observer API to track element visibility * * @param options - Configuration options for the intersection observer * @returns Object with ref and intersection state * * @example * ```tsx * 'use client'; * * import { useIntersectionObserver } from 'nextjs-app-hooks'; * * export default function LazyImage() { * const { ref, isInView } = useIntersectionObserver<HTMLDivElement>({ * threshold: 0.1, * triggerOnce: true * }); * * return ( * <div ref={ref} className="image-container"> * {isInView ? ( * <img src="https://example.com/image.jpg" alt="Lazy loaded" /> * ) : ( * <div className="placeholder" /> * )} * </div> * ); * } * ``` * * @example * ```tsx * // With animation on scroll * const { ref, isInView, intersectionRatio } = useIntersectionObserver({ * threshold: [0, 0.25, 0.5, 0.75, 1], * rootMargin: "0px 0px -100px 0px" // Trigger 100px before element comes into view * }); * * return ( * <div * ref={ref} * className={`fade-in ${isInView ? 'visible' : 'hidden'}`} * style={{ opacity: intersectionRatio }} * > * This content will fade in as it scrolls into view * </div> * ); * ``` */ declare function useIntersectionObserver<T extends Element = HTMLDivElement>(options?: UseIntersectionObserverOptions): UseIntersectionObserverReturn<T>; /** * A hook that tracks whether the component has been rendered already * It returns false on first render and true afterward * * @returns boolean - Whether the component has rendered before * * @example * ```tsx * 'use client'; * * import { useHasRendered } from 'nextjs-app-hooks'; * * export default function MyComponent() { * const hasRendered = useHasRendered(); * const [data, setData] = useState(null); * * useEffect(() => { * if (!hasRendered) { * // Only fetch data on the first render * fetchData().then(setData); * } * }, [hasRendered]); * * return ( * <div> * {!hasRendered ? 'Loading...' : 'Data loaded'} * {data && <DataDisplay data={data} />} * </div> * ); * } * ``` */ declare function useHasRendered(): boolean; /** * Options for configuring the body scroll lock */ interface UseLockBodyScrollOptions { /** * Whether to disable the scroll lock * @default false */ disabled?: boolean; /** * The element to apply the overflow: hidden to (default is document.body) * @default document.body */ targetElement?: HTMLElement | null; /** * Whether to preserve the scroll position when unlocking * @default true */ preserveScrollPosition?: boolean; /** * Whether to reserve space for scrollbar to prevent layout shift * @default true */ reserveScrollbarGap?: boolean; } /** * Return type for the useLockBodyScroll hook */ interface UseLockBodyScrollReturn { /** * Whether the scroll is currently locked */ isLocked: boolean; /** * Function to lock the body scroll */ lock: () => void; /** * Function to unlock the body scroll */ unlock: () => void; /** * Function to toggle between locked and unlocked states */ toggle: () => void; } /** * A hook that locks/unlocks body scrolling for modals, drawers, etc. * * @param initialLocked - Whether scrolling should be locked initially * @param options - Configuration options for the hook * @returns Object with lock state and control functions * * @example * ```tsx * // Simple usage with a modal * function Modal({ isOpen, onClose, children }) { * // Lock scrolling when modal is open * useLockBodyScroll(isOpen); * * if (!isOpen) return null; * * return ( * <div className="modal-overlay"> * <div className="modal-content"> * {children} * <button onClick={onClose}>Close</button> * </div> * </div> * ); * } * ``` * * @example * ```tsx * // With manual control * function ScrollToggleComponent() { * const { isLocked, toggle } = useLockBodyScroll(false); * * return ( * <button onClick={toggle}> * {isLocked ? 'Enable Scrolling' : 'Disable Scrolling'} * </button> * ); * } * ``` */ declare function useLockBodyScroll(initialLocked?: boolean, options?: UseLockBodyScrollOptions): UseLockBodyScrollReturn; /** * Position coordinates for tracking press movement */ interface PressPosition { x: number; y: number; } /** * Movement data including distance and direction */ interface PressMovement { /** * Distance moved from initial press position in pixels */ distance: number; /** * Horizontal movement from initial press position in pixels */ deltaX: number; /** * Vertical movement from initial press position in pixels */ deltaY: number; /** * Primary direction of movement ('up', 'down', 'left', 'right') */ direction: "up" | "down" | "left" | "right" | null; } /** * Options for configuring the long press hook */ interface UseLongPressOptions { /** * Duration in milliseconds before a press is considered "long" * @default 500 */ delay?: number; /** * Whether to disable the long press detection * @default false */ disabled?: boolean; /** * Whether to prevent default browser behavior on events * @default true */ preventDefault?: boolean; /** * Whether to stop event propagation * @default false */ stopPropagation?: boolean; /** * Whether to detect mouse events in addition to touch events * @default true */ detectMouse?: boolean; /** * How many pixels the user can move while pressing before canceling the long press * @default 10 */ moveTolerance?: number; /** * Whether to enable vibration feedback when long press is detected (mobile only) * @default false */ vibrate?: boolean; /** * Vibration pattern in milliseconds * @default [100] */ vibrationPattern?: number[]; /** * Whether to continue triggering while holding after initial long press * @default false */ continuousLongPress?: boolean; /** * Interval in milliseconds for continuous long press events * @default 100 */ continuousLongPressInterval?: number; /** * Callback when the user starts pressing (before long press threshold) */ onPressStart?: (position: PressPosition) => void; /** * Callback when the user moves while pressing */ onPressMove?: (movement: PressMovement) => void; /** * Callback when the long press is triggered */ onLongPress?: (position: PressPosition, movement: PressMovement) => void; /** * Callback when the long press ends (user releases after long press) */ onLongPressEnd?: (position: PressPosition, movement: PressMovement) => void; /** * Callback when the press is canceled before reaching threshold */ onPressCancel?: () => void; /** * Callback when a normal click/tap is performed (press and release before threshold) */ onClick?: (e: React.MouseEvent | React.TouchEvent) => void; } /** * Return type for the useLongPress hook */ interface UseLongPressReturn { /** * Whether the element is currently being pressed */ isPressed: boolean; /** * Whether the long press threshold has been reached */ isLongPressed: boolean; /** * Object of handler props to spread onto the target element */ handlers: { onMouseDown: (e: React.MouseEvent) => void; onMouseMove: (e: React.MouseEvent) => void; onMouseUp: (e: React.MouseEvent) => void; onMouseLeave: (e: React.MouseEvent) => void; onTouchStart: (e: React.TouchEvent) => void; onTouchMove: (e: React.TouchEvent) => void; onTouchEnd: (e: React.TouchEvent) => void; onContextMenu: (e: React.MouseEvent) => void; onClick: (e: React.MouseEvent) => void; }; /** * Current position data during a press */ position: PressPosition | null; /** * Current movement data during a press */ movement: PressMovement | null; /** * Manually trigger the long press */ triggerLongPress: () => void; /** * Manually cancel the long press detection */ cancelLongPress: () => void; /** * Reset all states to default values */ reset: () => void; } /** * A hook for detecting long press gestures with extensive configuration options * * @param callback - Function to call when long press is detected * @param options - Configuration options for the hook * @returns Object with handler functions and state information * * @example * ```tsx * // Basic usage * function LongPressButton() { * const onLongPress = () => alert('Long pressed!'); * * const { handlers, isLongPressed } = useLongPress(onLongPress); * * return ( * <button * {...handlers} * className={isLongPressed ? 'active' : ''} * > * Press and hold me * </button> * ); * } * ``` * * @example * ```tsx * // Advanced usage with movement tracking * function DragButton() { * const [feedback, setFeedback] = useState(''); * * const { handlers, isPressed, movement } = useLongPress(null, { * moveTolerance: 50, * onPressStart: () => setFeedback('Press started'), * onPressMove: (data) => setFeedback(`Moving: ${data.direction} (${data.deltaX}, ${data.deltaY})`), * onLongPress: () => setFeedback('Long press detected!'), * continuousLongPress: true * }); * * return ( * <div> * <button {...handlers} className={isPressed ? 'active' : ''}> * Drag me around * </button> * <div>{feedback}</div> * </div> * ); * } * ``` */ declare function useLongPress(callback?: ((e: React.MouseEvent | React.TouchEvent) => void) | null, options?: UseLongPressOptions): UseLongPressReturn; /** * Predefined breakpoint values (in pixels) for responsive design */ declare const breakpoints: { readonly xs: 0; readonly sm: 576; readonly md: 768; readonly lg: 992; readonly xl: 1200; readonly xxl: 1400; }; /** * Breakpoint keys type */ type Breakpoint = keyof typeof breakpoints; /** * Media query features that can be used with the hook */ interface MediaQueryFeatures { /** * Min-width in pixels or as a breakpoint key */ minWidth?: number | Breakpoint; /** * Max-width in pixels or as a breakpoint key */ maxWidth?: number | Breakpoint; /** * Min-height in pixels */ minHeight?: number; /** * Max-height in pixels */ maxHeight?: number; /** * Device orientation */ orientation?: "portrait" | "landscape"; /** * Preferred color scheme */ prefersColorScheme?: "dark" | "light"; /** * Preferred reduced motion setting */ prefersReducedMotion?: boolean; /** * Display mode of the application */ displayMode?: "browser" | "standalone" | "minimal-ui" | "fullscreen"; /** * Custom media query string (will be used directly) */ custom?: string; } /** * Options for the useMediaQuery hook */ interface UseMediaQueryOptions { /** * Default value to use on the server or when disabled * @default false */ defaultValue?: boolean; /** * Whether to disable the media query matching * @default false */ disabled?: boolean; /** * Function to call when the media query match changes */ onChange?: (matches: boolean) => void; /** * Whether to initialize with the default value instead of immediately checking * @default false */ initializeWithDefaultValue?: boolean; /** * Whether to convert the query to use em units instead of pixels * @default false */ useEm?: boolean; /** * Base font size in pixels, used when converting to em units * @default 16 */ baseFontSize?: number; } /** * Return type for the useMediaQuery hook */ interface UseMediaQueryReturn { /** * Whether the media query matches */ matches: boolean; /** * The media query string being used */ query: string; /** * Update the media query features */ updateQuery: (features: MediaQueryFeatures) => void; } /** * A hook for responding to media queries with extensive configuration options * * @param features - Media query features or a direct media query string * @param options - Configuration options for the hook * @returns Object with current match state and utility functions * * @example * ```tsx * // Basic usage with breakpoints * function ResponsiveComponent() { * const isMobile = useMediaQuery({ maxWidth: 'sm' }); * const isTablet = useMediaQuery({ minWidth: 'md', maxWidth: 'lg' }); * const isDesktop = useMediaQuery({ minWidth: 'xl' }); * * return ( * <div> * {isMobile.matches && <MobileView />} * {isTablet.matches && <TabletView />} * {isDesktop.matches && <DesktopView />} * </div> * ); * } * ``` * * @example * ```tsx * // Advanced usage with multiple features * function ThemeAwareComponent() { * const { matches: isDarkMode } = useMediaQuery({ * prefersColorScheme: 'dark', * }); * * const { matches: isReducedMotion } = useMediaQuery({ * prefersReducedMotion: true * }); * * return ( * <div className={isDarkMode ? 'dark-theme' : 'light-theme'}> * <Animation disabled={isReducedMotion} /> * </div> * ); * } * ``` * * @example * ```tsx * // With custom query * function PrintView() { * const { matches: isPrinting } = useMediaQuery({ * custom: 'print' * }); * * return ( * <div> * {isPrinting ? 'Optimized for printing' : 'Screen view'} * </div> * ); * } * ``` */ declare function useMediaQuery(features: MediaQueryFeatures | string, options?: UseMediaQueryOptions): UseMediaQueryReturn; /** * Hook that matches multiple media queries and returns their states * * @param queries - Object with named media query features * @param options - Configuration options for the hook * @returns Object with match states for each query * * @example * ```tsx * function ResponsiveLayout() { * const { mobile, tablet, desktop } = useMediaQueries({ * mobile: { maxWidth: 'sm' }, * tablet: { minWidth: 'md', maxWidth: 'lg' }, * desktop: { minWidth: 'xl' } * }); * * return ( * <div className={`layout ${mobile ? 'mobile' : ''} ${tablet ? 'tablet' : ''} ${desktop ? 'desktop' : ''}`}> * {mobile && <MobileMenu />} * {(tablet || desktop) && <DesktopMenu />} * <main>Content</main> * </div> * ); * } * ``` */ declare function useMediaQueries<T extends Record<string, MediaQueryFeatures | string>>(queries: T, options?: UseMediaQueryOptions): { [K in keyof T]: boolean; }; /** * Convenience hook for responsive design using predefined breakpoints * * @param options - Configuration options for the hook * @returns Object with boolean flags for different screen sizes * * @example * ```tsx * function App() { * const { isMobile, isTablet, isDesktop, isLargeDesktop } = useResponsive(); * * return ( * <div> * <h1>Current viewport:</h1> * {isMobile && <p>Mobile</p>} * {isTablet && <p>Tablet</p>} * {isDesktop && <p>Desktop</p>} * {isLargeDesktop && <p>Large Desktop</p>} * </div> * ); * } * ``` */ declare function useResponsive(options?: UseMediaQueryOptions): { isMobile: boolean; isTablet: boolean; isDesktop: boolean; isLargeDesktop: boolean; }; /** * Hook to check if the screen is in dark mode * * @param options - Configuration options for the hook * @returns Boolean indicating if dark mode is active * * @example * ```tsx * function ThemeAwareComponent() { * const isDarkMode = useDarkMode(); * * return ( * <div className={isDarkMode ? 'dark-theme' : 'light-theme'}> * <p>Current theme: {isDarkMode ? 'Dark' : 'Light'}</p> * </div> * ); * } * ``` */ declare function useDarkMode(options?: UseMediaQueryOptions): boolean; /** * Hook to check if the user prefers reduced motion * * @param options - Configuration options for the hook * @returns Boolean indicating if reduced motion is preferred * * @example * ```tsx * function AccessibleAnimation() { * const prefersReducedMotion = usePrefersReducedMotion(); * * return ( * <div> * {prefersReducedMotion ? ( * <StaticContent /> * ) : ( * <AnimatedContent /> * )} * </div> * ); * } * ``` */ declare function usePrefersReducedMotion(options?: UseMediaQueryOptions): boolean; /** * Hook to check the current screen orientation * * @param options - Configuration options for the hook * @returns Object with orientation information * * @example * ```tsx * function OrientationAwareComponent() { * const { isPortrait, isLandscape } = useOrientation(); * * return ( * <div> * <p>Current orientation: {isPortrait ? 'Portrait' : 'Landscape'}</p> * {isPortrait && <PortraitLayout />} * {isLandscape && <LandscapeLayout />} * </div> * ); * } * ``` */ declare function useOrientation(options?: UseMediaQueryOptions): { isPortrait: boolean; isLandscape: boolean; }; /** * Mouse event names for tracking */ type MouseEventName = "mousedown" | "mouseup" | "mousemove" | "mouseenter" | "mouseleave" | "mouseover" | "mouseout" | "click" | "contextmenu" | "dblclick"; /** * Mouse button states */ interface MouseButtons { /** * Left mouse button state */ left: boolean; /** * Right mouse button state */ right: boolean; /** * Middle mouse button state */ middle: boolean; } /** * Mouse position coordinates */ interface MousePosition { /** * X coordinate */ x: number; /** * Y coordinate */ y: number; /** * Client X coordinate (relative to viewport) */ clientX: number; /** * Client Y coordinate (relative to viewport) */ clientY: number; /** * Page X coordinate (including scroll) */ pageX: number; /** * Page Y coordinate (including scroll) */ pageY: number; /** * Screen X coordinate (relative to screen) */ screenX: number; /** * Screen Y coordinate (relative to screen) */ screenY: number; } /** * Mouse movement data */ interface MouseMovement { /** * Horizontal movement delta since last position */ deltaX: number; /** * Vertical movement delta since last position */ deltaY: number; /** * Direction of movement as degrees (0-360, 0 is right, 90 is down) */ direction: number; /** * Speed of movement in pixels per second */ speed: number; /** * Velocity vector [x, y] in pixels per second */ velocity: [number, number]; } /** * Mouse event data */ interface MouseEventData { /** * Current mouse position */ position: MousePosition; /** * Mouse button states */ buttons: MouseButtons; /** * Whether the mouse is inside the target element */ isInside: boolean; /** * Mouse movement data */ movement: MouseMovement; /** * Raw mouse event */ event: MouseEvent | null; } /** * Options for the useMouse hook */ interface UseMouseOptions { /** * Target element to track mouse events on * If not provided, document is used */ target?: React.RefObject<HTMLElement> | HTMLElement | null; /** * Whether to disable the hook * @default false */ disabled?: boolean; /** * Mouse events to track * @default ["mousemove", "mousedown", "mouseup", "mouseenter", "mouseleave"] */ eventTypes?: MouseEventName[]; /** * Whether to use capture phase for event listeners * @default false */ useCapture?: boolean; /** * Whether to prevent default behavior for mouse events * @default false */ preventDefault?: boolean; /** * Whether to stop event propagation * @default false */ stopPropagation?: boolean; /** * How long to keep tracking velocity after movement stops (ms) * @default 50 */ velocityDelay?: number; /** * Number of positions to keep in history for velocity calculations * @default 5 */ positionHistoryCount?: number; /** * Whether to track mouse position relative to the target element * @default true */ trackRelativePosition?: boolean; /** * Callback when mouse enters target */ onEnter?: (data: MouseEventData) => void; /** * Callback when mouse leaves target */ onLeave?: (data: MouseEventData) => void; /** * Callback when mouse moves within target */ onMove?: (data: MouseEventData) => void; /** * Callback when mouse button is pressed within target */ onDown?: (data: MouseEventData, button: number) => void; /** * Callback when mouse button is released within target */ onUp?: (data: MouseEventData, button: number) => void; /** * Callback when mouse button is clicked within target */ onClick?: (data: MouseEventData, button: number) => void; } /** * Return type for the useMouse hook */ interface UseMouseReturn extends MouseEventData { /** * Element that mouse events are being tracked on */ targetElement: HTMLElement | Document | null; /** * Relative position within the target element (0-1) * Only available if trackRelativePosition is true */ relativePosition: { x: number; y: number; } | null; /** * Reset the mouse state */ reset: () => void; } /** * A hook for tracking mouse position, movement, and button states * * @param options - Configuration options for the hook * @returns Object with mouse position, button states, and movement data * * @example * ```tsx * // Basic usage * function MouseTracker() { * const mouse = useMouse(); * * return ( * <div> * <p>Mouse position: {mouse.position.x}, {mouse.position.y}</p> * <p>Left button: {mouse.buttons.left ? 'Pressed' : 'Released'}</p> * </div> * ); * } * ``` * * @example * ```tsx * // Tracking mouse within a specific element * function CanvasInteraction() { * const canvasRef = useRef(null); * const mouse = useMouse({ * target: canvasRef, * trackRelativePosition: true, * onMov