{"version":3,"file":"react-util.mjs","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts"],"sourcesContent":["import { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAsyncFnOptions<F extends UseAsyncFn> {\n  initialValue?: InferValue<F>;\n  debounceMs?: number;\n  debounceType?: \"leading\" | \"trailing\";\n}\n\nexport type UseAsyncFn = (signal: AbortSignal, ...args: any[]) => Promise<any>;\nexport type UseAsyncFnRun<Value, Args> = Args extends unknown[]\n  ? (...args: Args) => Promise<Value>\n  : () => Promise<Value>;\n\nexport interface UseAsyncFnStateBase<Value, Args> {\n  run: UseAsyncFnRun<Value, Args>;\n}\n\nexport interface UseAsyncFnStateLoading<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n  loading: true;\n  error?: Error | undefined;\n  value?: Value;\n}\n\nexport interface UseAsyncFnStateError<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n  loading: false;\n  error: Error;\n  value?: undefined;\n}\n\nexport interface UseAsyncFnStateResolved<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n  loading: false;\n  error?: undefined;\n  value: Value;\n}\n\ntype InferValue<F extends UseAsyncFn> = Awaited<ReturnType<F>>;\ntype InferArgs<F extends UseAsyncFn> = Parameters<F> extends [any, ...infer Rest] ? Rest : [];\n\nexport type UseAsyncFnState<Value, Args> =\n  | UseAsyncFnStateLoading<Value, Args>\n  | UseAsyncFnStateError<Value, Args>\n  | UseAsyncFnStateResolved<Value, Args>;\n\nexport const useAsyncFn = <F extends UseAsyncFn, Value = InferValue<F>, Args = InferArgs<F>>(\n  fn: F,\n  deps: DependencyList = [],\n  options?: UseAsyncFnOptions<F>,\n): UseAsyncFnState<Value, Args> => {\n  const { debounceType = \"leading\", debounceMs = 650 } = options ?? {};\n\n  const timeoutRef = useRef<number | undefined>(undefined);\n  const abortControllerRef = useRef<AbortController | undefined>(undefined);\n\n  const [state, setState] = useState<Omit<UseAsyncFnState<Value, Args>, \"run\">>(\n    options?.initialValue ? { loading: false, value: options?.initialValue } : { loading: true },\n  );\n\n  const run = useCallback((...args: Parameters<F>) => {\n    window.clearTimeout(timeoutRef.current);\n\n    abortControllerRef.current?.abort();\n    const abortController = new AbortController();\n    abortControllerRef.current = abortController;\n\n    if (!state?.loading) {\n      setState((prevState) => ({ ...prevState, loading: true }));\n    }\n\n    return new Promise((resolve, reject) => {\n      const timeoutMs = debounceType === \"leading\" && !timeoutRef.current ? 0 : debounceMs;\n\n      timeoutRef.current = window.setTimeout(() => {\n        fn(abortController.signal, ...args)\n          .then(\n            (value) => {\n              if (!abortController.signal.aborted) {\n                setState({ value, loading: false });\n              }\n              resolve(value);\n            },\n            (error) => {\n              if (!abortController.signal.aborted) {\n                setState({ error, loading: false });\n              }\n              reject(error);\n            },\n          )\n          .finally(() => {\n            timeoutRef.current = undefined;\n          });\n      }, timeoutMs);\n    });\n\n    // biome-ignore lint/correctness/useExhaustiveDependencies: deps are controlled by caller\n  }, deps);\n\n  useEffect(() => {\n    return () => abortControllerRef.current?.abort();\n  }, []);\n\n  return { ...state, run } as UseAsyncFnState<Value, Args>;\n};\n","import { type DependencyList, useEffect, useRef } from \"react\";\nimport { type UseAsyncFn, type UseAsyncFnOptions, useAsyncFn } from \"./useAsyncFn\";\n\nexport const useAsync = <F extends UseAsyncFn>(\n  fn: F,\n  deps: DependencyList = [],\n  options?: UseAsyncFnOptions<F> & { runImmediately?: boolean },\n) => {\n  const firstRun = useRef(true);\n\n  const { runImmediately = true, ...useAsyncFnOptions } = options ?? {};\n\n  const asyncFn = useAsyncFn(fn, deps, useAsyncFnOptions);\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: option changes should not trigger effect\n  useEffect(() => {\n    const isFirstRun = firstRun.current;\n    firstRun.current = false;\n\n    if (!runImmediately && isFirstRun) return;\n\n    // TODO: how should errors be handled? this isn't great...\n    void asyncFn.run();\n  }, [asyncFn.run]);\n\n  return asyncFn;\n};\n","import { type DependencyList, useCallback, useEffect, useRef } from \"react\";\n\nexport interface UseDebouncedCallbackOptions {\n  leading?: boolean;\n  delayMs?: number;\n}\n\nexport function useDebouncedCallback<T extends (...args: any) => any>(\n  callback: T,\n  deps: DependencyList,\n  options?: UseDebouncedCallbackOptions,\n) {\n  const ref = useRef<\n    UseDebouncedCallbackOptions & {\n      timeout: number | null;\n      mounted: boolean;\n      leadingTriggered: boolean;\n    }\n  >({\n    ...options,\n    timeout: null,\n    mounted: false,\n    leadingTriggered: options?.leading ?? false,\n  });\n\n  useEffect(() => {\n    const currentRef = ref.current;\n    currentRef.mounted = true;\n    currentRef.leadingTriggered = false;\n    return () => {\n      currentRef.mounted = false;\n      window.clearTimeout(currentRef.timeout!);\n    };\n  }, []);\n\n  return useCallback((...params: Parameters<T>) => {\n    const currentRef = ref.current;\n    window.clearTimeout(currentRef.timeout!);\n    if (!currentRef.leadingTriggered && currentRef.leading) {\n      currentRef.leadingTriggered = true;\n      callback(...params);\n      currentRef.timeout = window.setTimeout(() => {\n        currentRef.leadingTriggered = true;\n      }, currentRef.delayMs ?? 1600);\n    } else {\n      currentRef.timeout = window.setTimeout(() => {\n        if (!currentRef.mounted) return;\n        callback(...params);\n        currentRef.leadingTriggered = false;\n      }, currentRef.delayMs ?? 1600);\n    }\n    // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n  }, deps) as T;\n}\n","import { type DependencyList, type EffectCallback, useEffect } from \"react\";\nimport { type UseDebouncedCallbackOptions, useDebouncedCallback } from \"./useDebouncedCallback\";\n\nexport function useDebouncedEffect(\n  callback: EffectCallback,\n  deps: DependencyList,\n  options?: UseDebouncedCallbackOptions,\n) {\n  const debouncedCallback = useDebouncedCallback(callback, deps, { leading: true, ...options });\n  // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n  return useEffect(debouncedCallback, deps);\n}\n","import { useEffect } from \"react\";\n\nexport const useMountEffect = (effectFn: React.EffectCallback): void => useEffect(effectFn, []);\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport const useMountedState = (): (() => boolean) => {\n  const mountedRef = useRef<boolean>(false);\n  const get = useCallback(() => mountedRef.current, []);\n\n  useEffect(() => {\n    mountedRef.current = true;\n\n    return () => {\n      mountedRef.current = false;\n    };\n  }, []);\n\n  return get;\n};\n"],"mappings":"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"}