{"version":3,"file":"useThrottler.cjs","names":["useDefaultPacerOptions","Throttler","shallow"],"sources":["../../src/throttler/useThrottler.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { Throttler } from '@tanstack/pacer/throttler'\nimport { shallow, useSelector } from '@tanstack/react-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/react-store'\nimport type { AnyFunction } from '@tanstack/pacer/types'\nimport type {\n  ThrottlerOptions,\n  ThrottlerState,\n} from '@tanstack/pacer/throttler'\nimport type { FunctionComponent, ReactNode } from 'react'\n\nexport interface ReactThrottlerOptions<\n  TFn extends AnyFunction,\n  TSelected = {},\n> extends ThrottlerOptions<TFn> {\n  /**\n   * Optional callback invoked when the component unmounts. Receives the throttler instance.\n   * When provided, replaces the default cleanup (cancel); use it to call flush(), reset(), cancel(), add logging, etc.\n   */\n  onUnmount?: (throttler: ReactThrottler<TFn, TSelected>) => void\n}\n\nexport interface ReactThrottler<\n  TFn extends AnyFunction,\n  TSelected = {},\n> extends Omit<Throttler<TFn>, 'store'> {\n  /**\n   * A React HOC (Higher Order Component) that allows you to subscribe to the throttler state.\n   *\n   * This is useful for opting into state re-renders for specific parts of the throttler state\n   * deep in your component tree without needing to pass a selector to the hook.\n   *\n   * @example\n   * <throttler.Subscribe selector={(state) => ({ isPending: state.isPending })}>\n   *   {({ isPending }) => (\n   *     <div>{isPending ? 'Throttling...' : 'Ready'}</div>\n   *   )}\n   * </throttler.Subscribe>\n   */\n  Subscribe: <TSelected>(props: {\n    selector: (state: ThrottlerState<TFn>) => TSelected\n    children: ((state: TSelected) => ReactNode) | ReactNode\n  }) => ReturnType<FunctionComponent>\n  /**\n   * Reactive state that will be updated and re-rendered when the throttler state changes\n   *\n   * Use this instead of `throttler.store.state`\n   */\n  readonly state: Readonly<TSelected>\n  /**\n   * @deprecated Use `throttler.state` instead of `throttler.store.state` if you want to read reactive state.\n   * The state on the store object is not reactive, as it has not been wrapped in a `useSelector` hook internally.\n   * Although, you can make the state reactive by using the `useSelector` in your own usage.\n   */\n  readonly store: Store<Readonly<ThrottlerState<TFn>>>\n}\n\n/**\n * A low-level React hook that creates a `Throttler` instance that limits how often the provided function can execute.\n *\n * This hook is designed to be flexible and state-management agnostic - it simply returns a throttler instance that\n * you can integrate with any state management solution (useState, Redux, Zustand, Jotai, etc). For a simpler and higher-level hook that\n * integrates directly with React's useState, see useThrottledState.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. You can subscribe to state changes\n * in two ways:\n *\n * **1. Using `throttler.Subscribe` HOC (Recommended for component tree subscriptions)**\n *\n * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without\n * needing to pass a selector to the hook. This is ideal when you want to subscribe to state\n * in child components.\n *\n * **2. Using the `selector` parameter (For hook-level subscriptions)**\n *\n * The `selector` parameter allows you to specify which state changes will trigger a re-render\n * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant\n * state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary\n * re-renders and gives you full control over when your component updates.\n *\n * Available state properties:\n * - `executionCount`: Number of function executions that have been completed\n * - `lastArgs`: The arguments from the most recent call to maybeExecute\n * - `lastExecutionTime`: Timestamp of the last function execution in milliseconds\n * - `nextExecutionTime`: Timestamp when the next execution can occur in milliseconds\n * - `isPending`: Whether the throttler is waiting for the timeout to trigger execution\n * - `status`: Current execution status ('disabled' | 'idle' | 'pending')\n *\n * ## Unmount behavior\n *\n * By default, the hook cancels any pending execution when the component unmounts.\n * Use the `onUnmount` option to customize this. For example, to flush pending work instead:\n *\n * ```tsx\n * const throttler = useThrottler(fn, {\n *   wait: 1000,\n *   onUnmount: (t) => t.flush()\n * });\n * ```\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const [value, setValue] = useState(0);\n * const throttler = useThrottler(setValue, { wait: 1000 });\n *\n * // Subscribe to state changes deep in component tree using Subscribe HOC\n * <throttler.Subscribe selector={(state) => ({ isPending: state.isPending })}>\n *   {({ isPending }) => (\n *     <div>{isPending ? 'Throttling...' : 'Ready'}</div>\n *   )}\n * </throttler.Subscribe>\n *\n * // Opt-in to re-render when execution count changes at hook level (optimized for tracking executions)\n * const [value, setValue] = useState(0);\n * const throttler = useThrottler(\n *   setValue,\n *   { wait: 1000 },\n *   (state) => ({ executionCount: state.executionCount })\n * );\n *\n * // Opt-in to re-render when throttling state changes (optimized for loading indicators)\n * const [value, setValue] = useState(0);\n * const throttler = useThrottler(\n *   setValue,\n *   { wait: 1000 },\n *   (state) => ({\n *     isPending: state.isPending,\n *     status: state.status\n *   })\n * );\n *\n * // Opt-in to re-render when timing information changes (optimized for timing displays)\n * const [value, setValue] = useState(0);\n * const throttler = useThrottler(\n *   setValue,\n *   { wait: 1000 },\n *   (state) => ({\n *     lastExecutionTime: state.lastExecutionTime,\n *     nextExecutionTime: state.nextExecutionTime\n *   })\n * );\n *\n * // With any state manager\n * const throttler = useThrottler(\n *   (value) => stateManager.setState(value),\n *   {\n *     wait: 2000,\n *     leading: true,   // Execute immediately on first call\n *     trailing: false  // Skip trailing edge updates\n *   }\n * );\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { executionCount, isPending } = throttler.state;\n * ```\n */\nexport function useThrottler<TFn extends AnyFunction, TSelected = {}>(\n  fn: TFn,\n  options: ReactThrottlerOptions<TFn, TSelected>,\n  selector: (state: ThrottlerState<TFn>) => TSelected = () => ({}) as TSelected,\n): ReactThrottler<TFn, TSelected> {\n  const mergedOptions = {\n    ...useDefaultPacerOptions().throttler,\n    ...options,\n  } as ReactThrottlerOptions<TFn, TSelected>\n\n  const [throttler] = useState(() => {\n    const throttlerInstance = new Throttler<TFn>(\n      fn,\n      mergedOptions,\n    ) as unknown as ReactThrottler<TFn, TSelected>\n\n    /* eslint-disable-next-line @eslint-react/component-hook-factories -- Subscribe attached once in useState lazy init; stable per instance */\n    throttlerInstance.Subscribe = function Subscribe<TSelected>(props: {\n      selector: (state: ThrottlerState<TFn>) => TSelected\n      children: ((state: TSelected) => ReactNode) | ReactNode\n    }) {\n      const selected = useSelector(throttlerInstance.store, props.selector, {\n        compare: shallow,\n      })\n\n      return typeof props.children === 'function'\n        ? props.children(selected)\n        : props.children\n    }\n\n    return throttlerInstance\n  })\n\n  throttler.fn = fn\n  throttler.setOptions(mergedOptions)\n\n  const state = useSelector(throttler.store, selector, { compare: shallow })\n\n  /* eslint-disable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler -- unmount cleanup only; empty deps keep teardown stable */\n  useEffect(() => {\n    return () => {\n      if (mergedOptions.onUnmount) {\n        mergedOptions.onUnmount(throttler)\n      } else {\n        throttler.cancel()\n      }\n    }\n  }, [])\n  /* eslint-enable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler */\n\n  return useMemo(\n    () =>\n      ({\n        ...throttler,\n        state,\n      }) as ReactThrottler<TFn, TSelected>, // omit `store` in favor of `state`\n    [throttler, state],\n  )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuKA,SAAgB,aACd,IACA,SACA,kBAA6D,EAAE,GAC/B;CAChC,MAAM,gBAAgB;EACpB,GAAGA,8CAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,uCAA4B;EACjC,MAAM,oBAAoB,IAAIC,oCAC5B,IACA,cACD;AAGD,oBAAkB,YAAY,SAAS,UAAqB,OAGzD;GACD,MAAM,kDAAuB,kBAAkB,OAAO,MAAM,UAAU,EACpE,SAASC,+BACV,CAAC;AAEF,UAAO,OAAO,MAAM,aAAa,aAC7B,MAAM,SAAS,SAAS,GACxB,MAAM;;AAGZ,SAAO;GACP;AAEF,WAAU,KAAK;AACf,WAAU,WAAW,cAAc;CAEnC,MAAM,+CAAoB,UAAU,OAAO,UAAU,EAAE,SAASA,+BAAS,CAAC;AAG1E,4BAAgB;AACd,eAAa;AACX,OAAI,cAAc,UAChB,eAAc,UAAU,UAAU;OAElC,WAAU,QAAQ;;IAGrB,EAAE,CAAC;AAGN,kCAEK;EACC,GAAG;EACH;EACD,GACH,CAAC,WAAW,MAAM,CACnB"}