import { onCleanup } from "solid-js"; type TimeoutId = ReturnType; /** * Class-instance-like timeout handle with `start` / `clear` methods. * * Mirrors Base UI React's `Timeout` * (`@base-ui/utils/useTimeout` — `packages/utils/src/useTimeout.ts`) so * component ports read 1:1 against the React source. */ export class Timeout { static create(): Timeout { return new Timeout(); } private currentId: TimeoutId | null = null; /** * Executes `fn` after `delay` ms, clearing any previously scheduled call * from this handle. Calling `start` again cancels the prior scheduling. */ start(delay: number, fn: () => void): void { this.clear(); this.currentId = setTimeout(() => { this.currentId = null; fn(); }, delay); } isStarted(): boolean { return this.currentId !== null; } clear = (): void => { if (this.currentId !== null) { clearTimeout(this.currentId); this.currentId = null; } }; } /** * A `setTimeout` with automatic cleanup via `onCleanup`. * * Returns a {@link Timeout} handle whose `start(delay, fn)` schedules a * callback (cancelling any previously scheduled one) and whose `clear()` * cancels the pending callback. On disposal of the owning scope, `clear()` * fires automatically so outstanding timeouts don't leak. * * Solid 2 notes: * * - Must be called from an owned scope (component body, `createRoot`, etc.) * so `onCleanup` has somewhere to register. Calling this at module scope * would log `NO_OWNER_CLEANUP` and the cleanup would never fire. * - The returned object has stable identity for the scope's lifetime — safe * to capture in closures. */ export function useTimeout(): Timeout { const timeout = Timeout.create(); onCleanup(timeout.clear); return timeout; }