import { SpringRest } from './spring';

/** Current value of every channel on an object, keyed by channel name. */
export type SpringValues = Readonly<Record<string, number>>;
/** Injectable clock, so a scene can be stepped deterministically in tests. */
export interface SceneClock {
    /** Monotonic clock in ms. Defaults to `performance.now`. */
    now?: () => number;
    /** Frame scheduler. Defaults to `requestAnimationFrame`. */
    raf?: (cb: (timeMs: number) => void) => number;
    /** Cancels a scheduled frame. Defaults to `cancelAnimationFrame`. */
    caf?: (handle: number) => void;
}
export interface SceneObjectOptions {
    /** `[stiffness, damping]` for this object. Defaults to the scene's spring. */
    spring?: readonly [number, number];
    /** Rest thresholds for this object's channels. Defaults to the scene's. */
    rest?: SpringRest;
    /**
     * Rest thresholds for individual channels, overriding {@link rest}. An object
     * whose channels are not all in the same units needs this: a Tree node mixes
     * two SVG-coordinate channels with a 0..1 reveal.
     */
    channelRest?: Record<string, SpringRest>;
    /** Stagger (ms) counted down before this object's springs start integrating. */
    delayMs?: number;
    /**
     * Stop stepping and writing this object once it comes to rest, instead of
     * keeping it in the frame while other objects still move. The Tree camera
     * needs this: once it has settled it must hand the viewBox back, or it would
     * overwrite a pan the user starts while nodes are still travelling.
     */
    pauseOnSettle?: boolean;
}
export interface SpringSceneOptions<T = undefined> {
    /** `[stiffness, damping]` for objects that do not override it. Defaults to `crisp`. */
    spring?: readonly [number, number];
    /** Rest thresholds for objects that do not override them. Defaults to the spring defaults. */
    rest?: SpringRest;
    /**
     * Host element for reduced motion: the scene mirrors the OS preference onto
     * it as {@link reducedMotionClass}, and treats the class being present (from
     * the OS or set by the app) as a request to snap.
     */
    element?: HTMLElement | null;
    /** Class mirrored onto {@link element} while `prefers-reduced-motion` is active. */
    reducedMotionClass?: string;
    /**
     * Force reduced motion on or off, bypassing the media query entirely. For
     * tests and for callers that resolve the preference themselves.
     */
    reducedMotion?: boolean;
    /** Injectable clock. */
    clock?: SceneClock;
    /** Writes one object's current values wherever they belong. Called per object, per frame. */
    write?: (id: string, values: SpringValues, data: T) => void;
    /**
     * Called after every object in a frame has been written. Where derived
     * geometry goes: a Tree edge follows two nodes, so it can only be recomputed
     * once both have been written.
     */
    onFrame?: () => void;
    /**
     * Called once each time an object reaches rest (not repeatedly while it sits
     * there). Return `true` to drop the object from the scene, which is how an
     * exiting node unmounts itself when its retraction finishes.
     */
    onSettle?: (id: string, data: T) => boolean | void;
}
export declare class SpringScene<T = undefined> {
    private readonly objects;
    private readonly stiffness;
    private readonly damping;
    private readonly rest?;
    private readonly opts;
    private readonly now;
    private readonly raf;
    private readonly caf;
    private readonly reducedMotionOverride?;
    private readonly reducedMotionWatcher;
    private frameHandle;
    private lastTime;
    private disposed;
    constructor(options?: SpringSceneOptions<T>);
    /** True when motion should be suppressed for this scene. */
    get reducedMotion(): boolean;
    /** Whether an object is currently tracked. */
    has(id: string): boolean;
    /** Number of tracked objects. */
    get size(): number;
    /** Ids of every tracked object, in insertion order. */
    ids(): IterableIterator<string>;
    /** The payload registered with an object, or `undefined` when untracked. */
    dataOf(id: string): T | undefined;
    /** Current interpolated values of an object, or `undefined` when untracked. */
    valuesOf(id: string): SpringValues | undefined;
    /** Current value of one channel, or `undefined` when the object or channel is untracked. */
    channelValue(id: string, channel: string): number | undefined;
    /** Current target of one channel, or `undefined` when the object or channel is untracked. */
    targetOf(id: string, channel: string): number | undefined;
    /**
     * Register an object with its channels at their starting values. Re-adding an
     * existing id replaces its payload and options but keeps its live springs, so
     * a re-registered object never restarts from a standstill.
     */
    add(id: string, channels: Record<string, number>, data: T, options?: SceneObjectOptions): void;
    /** Replace an object's payload without touching its springs. */
    setData(id: string, data: T): void;
    /**
     * Steer any subset of an object's channels toward new targets, preserving
     * value and velocity so a retarget mid-flight redirects rather than restarts.
     * Under reduced motion the channels snap instead. Unpauses the object and
     * re-arms its settle callback; does not start the loop.
     */
    retarget(id: string, targets: Record<string, number>, options?: {
        delayMs?: number;
    }): void;
    /** Snap channels to values with zero velocity: no animation, no settle callback. */
    snap(id: string, values: Record<string, number>): void;
    /**
     * Stop stepping and writing an object without discarding its springs. Used
     * when something outside the scene takes ownership of what the object drives
     * (a user pan takes the viewBox from the camera). A retarget resumes it.
     */
    pause(id: string): void;
    /** Stop tracking an object. */
    remove(id: string): void;
    /** Forget every object and cancel the loop. The scene stays usable. */
    clear(): void;
    /** True when every active object sits exactly on its targets with zero velocity. */
    atRest(): boolean;
    /**
     * Write every tracked object's current values through the writer, then run
     * `onFrame`, without advancing time. Called right after a reconcile so the
     * first painted frame shows objects at their starting values rather than
     * flashing at the final layout for one frame.
     *
     * @param include Optional filter. Objects it rejects are left untouched, for
     *   a caller that wants to republish most of the scene but not the one object
     *   whose value something else currently owns.
     */
    writeAll(include?: (id: string, data: T) => boolean): void;
    /**
     * Advance every active object by `dt` seconds, write it, and fire `onSettle`
     * for each one that has just come to rest. Returns `true` once the whole
     * scene is at rest. Exposed for manual stepping and tests; the loop uses it.
     */
    step(dt: number): boolean;
    /**
     * Start (or restart) the rAF loop, ticking until everything rests and then
     * stopping on its own. Safe to call after each batch of retargets: a loop
     * already running just keeps going toward the new targets, which is what
     * makes updates interruptible.
     *
     * Under reduced motion the scheduled frame snaps everything instead of
     * integrating. The snap is deliberately deferred rather than run inline:
     * callers register objects and their dependants (edges that follow two nodes)
     * across a whole synchronous render pass, and settling an object before that
     * pass finishes would tear down DOM the rest of the pass still expects. Use
     * {@link settleNow} for the synchronous variant.
     */
    start(): void;
    /**
     * Snap every active object to its targets, publish one frame, and run the
     * settle callbacks now rather than on the next frame. The reduced-motion
     * shortcut for callers whose work is already complete when they call it.
     */
    settleNow(): void;
    /** Cancel the running loop without discarding any spring state. */
    stop(): void;
    /** True while the rAF loop is scheduled. */
    get running(): boolean;
    /**
     * Cancel the loop, drop every object, and detach the reduced-motion listener.
     * Call from the owner's `destroy()`: without it a chart torn down mid-flight
     * leaves a loop ticking and writing into detached DOM. Idempotent, and the
     * scene stays inert afterwards (a later `start()` does nothing).
     */
    dispose(): void;
    /** Whether {@link dispose} has been called. */
    get isDisposed(): boolean;
    /** Reduced-motion path: snap everything to target, publish one frame, settle. */
    private snapAll;
    /** Run the settle callback; returns true when it asked for the object to be dropped. */
    private settle;
    private write;
}
//# sourceMappingURL=SpringScene.d.ts.map