import type { InertiaOptions } from '@react-hive/honey-utils'; /** * Configuration options for {@link useHoneyDecay}. */ export interface UseHoneyDecayOptions extends Pick { /** * Initial numeric value from which inertial motion starts. * * This typically represents a translated position (e.g. scroll offset or `translateX` value), * but may be any bounded numeric domain. */ initialValue: number; /** * Lower bound for the value (inclusive). * * Movement beyond this boundary is not permitted. */ min: number; /** * Upper bound for the value (inclusive). * * Movement beyond this boundary is not permitted. */ max: number; /** * Optional callback invoked exactly once when inertial motion terminates. * * Triggered when inertia ends due to: * - velocity decaying below `minVelocityPxMs` * - movement being blocked by bounds * - an explicit call to `stop()` * * Not invoked if inertia was never started. */ onStop?: () => void; } /** * Public control API returned by {@link useHoneyDecay}. * * Exposes imperative controls for managing velocity-based inertial motion. */ export interface UseHoneyDecayApi { /** * Current value produced by the decay simulation. * * This value updates over time while inertia is active * and always remains within the configured bounds. */ value: number; /** * Indicates whether inertial motion is currently active. */ isRunning: boolean; /** * Updates the hard bounds used by the decay simulation. * * This method is safe to call **at any time**, including while inertia is actively running. * * ### Behavior * - If the current value lies **within** the new bounds: * - bounds are updated * - inertia (if running) continues uninterrupted * * - If the current value lies **outside** the new bounds: * - the value is immediately clamped to the nearest boundary * - internal velocity is reset to `0` * - any active inertia is **terminated immediately** * - `onStop` is invoked exactly once (if inertia was active) * * This deterministic behavior mirrors native scroll engines and ensures: * - no overshoot * - no extra inertia frames * - consistent `onStop` semantics * * ### Intended usage * - Responding to layout or content changes * - Handling resize / orientation changes * - Updating overscroll or overflow limits dynamically * * ⚠️ This method should **not** be called from inside the RAF frame handler. * * @param min - New lower bound (inclusive) * @param max - New upper bound (inclusive) */ setBounds: (min: number, max: number) => void; /** * Starts inertial motion from the current value using * the provided initial velocity. * * The sign of the velocity determines a direction: * - Positive → movement toward the upper bound * - Negative → movement toward the lower bound * * @param velocityPxMs - Initial velocity expressed in pixels per millisecond (`px/ms`). */ start: (velocityPxMs: number) => void; /** * Immediately sets the value and starts inertial motion from that value in a single atomic operation. * * This is the preferred way to hand off from a gesture (e.g. drag end) to inertia, as it: * - avoids transient intermediate states * - guarantees correct value/velocity ordering * - ensures `onStop` semantics remain consistent * * @param value - Starting value for the inertia simulation * @param velocityPxMs - Initial velocity in pixels per millisecond (`px/ms`) */ startFrom: (value: number, velocityPxMs: number) => void; /** * Immediately stops inertial motion. * * If inertia is currently active, this will: * - cancel the RAF loop * - reset internal velocity * - invoke `onStop` exactly once */ stop: () => void; } /** * A bounded, velocity-based inertia (decay) hook built on top * of {@link useHoneyRafLoop} and {@link applyInertiaStep}. * * This hook models **momentum-driven motion** where: * - Motion starts with an initial velocity * - Velocity decays exponentially over time * - Movement is constrained by hard numeric bounds * - Inertia stops naturally when velocity becomes negligible * * Unlike spring-based motion, this hook has **no target value**. * Motion continues purely based on momentum until it decays * or is blocked by a boundary. * * --- * * ### Key characteristics * - Frame-rate independent (delta-time based) * - Deterministic and interruptible * - Direction-aware and bound-safe (no overshoot or jitter) * - Closely matches native scroll and drag inertia behavior * * --- * * ### Visibility behavior * This hook is a **simulation-based system**: * - Inertia automatically pauses when the document becomes hidden * - No time elapses while hidden * - Motion resumes only when explicitly restarted * * This behavior is inherited from {@link useHoneyRafLoop} and is intentional. * * --- * * ### Common use cases * - Scroll containers with momentum * - Drag-to-scroll interactions * - Carousels and sliders * - Timelines and scrubbers * - Kinetic panning and flinging * * --- * * @example * ```ts * const decay = useHoneyDecay({ * initialValue: 0, * min: -maxOverflow, * max: 0, * }); * * const onRelease = (velocityPxMs: number) => { * decay.start(velocityPxMs); * }; * * return ( *
* ); * ``` */ export declare const useHoneyDecay: ({ initialValue, min, max, friction, minVelocityPxMs, emaAlpha, onStop, }: UseHoneyDecayOptions) => UseHoneyDecayApi;