{"version":3,"file":"useDebouncedValue.mjs","sources":["../../../src/lib/hooks/useDebouncedValue.ts"],"sourcesContent":["/**\n * @file useDebouncedValue Hook\n * @description Hook for debouncing values with configurable options\n */\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from 'react';\n\n/**\n * Debounce options\n */\nexport interface DebounceOptions {\n  /** Debounce delay in milliseconds */\n  delay?: number;\n\n  /** Leading edge trigger */\n  leading?: boolean;\n\n  /** Trailing edge trigger */\n  trailing?: boolean;\n\n  /** Maximum wait time before forced update */\n  maxWait?: number;\n}\n\n/**\n * Hook for debouncing a value\n */\nexport function useDebouncedValue<T>(\n  value: T,\n  delay: number = 300,\n  options: Omit<DebounceOptions, 'delay'> = {}\n): T {\n  const { leading = false, trailing = true, maxWait } = options;\n\n  const [debouncedValue, setDebouncedValue] = useState<T>(value);\n  const lastValue = useRef<T>(value);\n  const lastCallTime = useRef<number | null>(null);\n  const lastInvokeTime = useRef<number>(0);\n  const timerId = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const maxTimerId = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  // Cleanup on unmount\n  useEffect(() => {\n    return () => {\n      if (timerId.current !== null && timerId.current !== undefined) clearTimeout(timerId.current);\n      if (maxTimerId.current !== null && maxTimerId.current !== undefined)\n        clearTimeout(maxTimerId.current);\n    };\n  }, []);\n\n  useEffect(() => {\n    const now = Date.now();\n    const isInvoking = shouldInvoke(now);\n\n    lastValue.current = value;\n    lastCallTime.current = now;\n\n    if (isInvoking) {\n      if (timerId.current === null && leading) {\n        // Leading edge - use useLayoutEffect pattern to avoid cascading renders\n        // or delay the setState to avoid synchronous setState in effect\n        lastInvokeTime.current = now;\n        // Delay setState slightly to avoid synchronous update\n        Promise.resolve()\n          .then(() => {\n            setDebouncedValue(value);\n          })\n          .catch(() => {\n            // Ignore errors in cleanup\n          });\n        startMaxWaitTimer();\n      }\n    }\n\n    // Reset debounce timer\n    if (timerId.current !== null && timerId.current !== undefined) clearTimeout(timerId.current);\n\n    if (trailing) {\n      timerId.current = setTimeout(() => {\n        if (lastCallTime.current !== null) {\n          lastInvokeTime.current = Date.now();\n          setDebouncedValue(lastValue.current);\n          timerId.current = null;\n          if (maxTimerId.current !== null && maxTimerId.current !== undefined) {\n            clearTimeout(maxTimerId.current);\n            maxTimerId.current = null;\n          }\n        }\n      }, delay);\n    }\n\n    function shouldInvoke(time: number): boolean {\n      const timeSinceLastCall = lastCallTime.current === null ? delay : time - lastCallTime.current;\n      const timeSinceLastInvoke = time - lastInvokeTime.current;\n\n      return (\n        lastCallTime.current === null ||\n        timeSinceLastCall >= delay ||\n        timeSinceLastCall < 0 ||\n        (maxWait !== undefined && timeSinceLastInvoke >= maxWait)\n      );\n    }\n\n    function startMaxWaitTimer(): void {\n      if (maxWait !== undefined && maxTimerId.current === null) {\n        maxTimerId.current = setTimeout(() => {\n          if (lastCallTime.current !== null) {\n            lastInvokeTime.current = Date.now();\n            setDebouncedValue(lastValue.current);\n            maxTimerId.current = null;\n          }\n        }, maxWait);\n      }\n    }\n  }, [value, delay, leading, trailing, maxWait]);\n\n  return debouncedValue;\n}\n\n/**\n * Hook for debouncing a callback function\n */\nexport function useDebouncedCallback<T extends (...args: unknown[]) => unknown>(\n  callback: T,\n  delay: number = 300,\n  options: Omit<DebounceOptions, 'delay'> = {}\n): {\n  callback: (...args: Parameters<T>) => void;\n  cancel: () => void;\n  flush: () => void;\n  pending: () => boolean;\n} {\n  const { leading = false, trailing = true, maxWait } = options;\n\n  const callbackRef = useRef(callback);\n  const timerId = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const maxTimerId = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const lastArgs = useRef<Parameters<T> | null>(null);\n  const lastInvokeTime = useRef<number>(0);\n  const leadingInvoked = useRef(false);\n\n  // Update callback ref\n  useEffect(() => {\n    callbackRef.current = callback;\n  }, [callback]);\n\n  // Cleanup on unmount\n  useEffect(() => {\n    return () => {\n      if (timerId.current !== null && timerId.current !== undefined) clearTimeout(timerId.current);\n      if (maxTimerId.current !== null && maxTimerId.current !== undefined)\n        clearTimeout(maxTimerId.current);\n    };\n  }, []);\n\n  const invokeCallback = useCallback(() => {\n    if (lastArgs.current !== null) {\n      callbackRef.current(...lastArgs.current);\n      lastArgs.current = null;\n      lastInvokeTime.current = Date.now();\n    }\n  }, []);\n\n  const cancel = useCallback(() => {\n    if (timerId.current !== null && timerId.current !== undefined) {\n      clearTimeout(timerId.current);\n      timerId.current = null;\n    }\n    if (maxTimerId.current !== null && maxTimerId.current !== undefined) {\n      clearTimeout(maxTimerId.current);\n      maxTimerId.current = null;\n    }\n    lastArgs.current = null;\n    leadingInvoked.current = false;\n  }, []);\n\n  const flush = useCallback(() => {\n    if (\n      (timerId.current !== null && timerId.current !== undefined) ||\n      (maxTimerId.current !== null && maxTimerId.current !== undefined)\n    ) {\n      invokeCallback();\n      cancel();\n    }\n  }, [invokeCallback, cancel]);\n\n  const pending = useCallback(() => {\n    return timerId.current !== null || maxTimerId.current !== null;\n  }, []);\n\n  const debouncedCallback = useCallback(\n    (...args: Parameters<T>) => {\n      lastArgs.current = args;\n\n      // Cancel existing timers\n      if (timerId.current !== null && timerId.current !== undefined) clearTimeout(timerId.current);\n\n      // Leading edge\n      if (leading && !leadingInvoked.current) {\n        leadingInvoked.current = true;\n        invokeCallback();\n\n        // Start max wait timer\n        if (maxWait !== undefined && maxTimerId.current === null) {\n          maxTimerId.current = setTimeout(() => {\n            invokeCallback();\n            maxTimerId.current = null;\n          }, maxWait);\n        }\n        return;\n      }\n\n      // Trailing edge timer\n      if (trailing) {\n        timerId.current = setTimeout(() => {\n          invokeCallback();\n          timerId.current = null;\n          leadingInvoked.current = false;\n          if (maxTimerId.current !== null && maxTimerId.current !== undefined) {\n            clearTimeout(maxTimerId.current);\n            maxTimerId.current = null;\n          }\n        }, delay);\n      }\n\n      // Max wait timer (if not already set)\n      if (maxWait !== undefined && maxTimerId.current === null) {\n        maxTimerId.current = setTimeout(() => {\n          invokeCallback();\n          maxTimerId.current = null;\n          if (timerId.current !== null && timerId.current !== undefined) {\n            clearTimeout(timerId.current);\n            timerId.current = null;\n          }\n        }, maxWait);\n      }\n    },\n    [delay, leading, trailing, maxWait, invokeCallback]\n  );\n\n  return useMemo(\n    () => ({\n      callback: debouncedCallback,\n      cancel,\n      flush,\n      pending,\n    }),\n    [debouncedCallback, cancel, flush, pending]\n  );\n}\n\n/**\n * Hook for throttling a value\n */\nexport function useThrottledValue<T>(value: T, limit: number = 300): T {\n  const [throttledValue, setThrottledValue] = useState<T>(value);\n  const lastRun = useRef<number>(0);\n\n  useEffect(() => {\n    const now = Date.now();\n    const timeSinceLastRun = lastRun.current === 0 ? limit : now - lastRun.current;\n\n    if (timeSinceLastRun >= limit) {\n      lastRun.current = now;\n      // Use microtask to avoid setState in effect\n      Promise.resolve()\n        .then(() => {\n          setThrottledValue(value);\n        })\n        .catch(() => {\n          // Ignore errors in cleanup\n        });\n      return undefined;\n    } else {\n      const timeoutId = setTimeout(() => {\n        lastRun.current = Date.now();\n        setThrottledValue(value);\n      }, limit - timeSinceLastRun);\n\n      return () => clearTimeout(timeoutId);\n    }\n  }, [value, limit]);\n\n  return throttledValue;\n}\n"],"names":["useDebouncedValue","value","delay","options","leading","trailing","maxWait","debouncedValue","setDebouncedValue","useState","lastValue","useRef","lastCallTime","lastInvokeTime","timerId","maxTimerId","useEffect","now","isInvoking","shouldInvoke","startMaxWaitTimer","time","timeSinceLastCall","timeSinceLastInvoke","useDebouncedCallback","callback","callbackRef","lastArgs","leadingInvoked","invokeCallback","useCallback","cancel","flush","pending","debouncedCallback","args","useMemo","useThrottledValue","limit","throttledValue","setThrottledValue","lastRun","timeSinceLastRun","timeoutId"],"mappings":"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"}