import { type Accessor, createSignal } from "solid-js"; export interface CreateControllableSignalOptions { /** Returns the controlled value, or undefined if uncontrolled. */ value: Accessor; /** Default value used when uncontrolled. Only read once at creation time — not reactive after mount. */ defaultValue: Accessor; /** Called whenever the value changes (both modes). */ onChange?: ((value: T) => void) | undefined; } /** * Handles controlled vs uncontrolled state for any stateful component. * When `value()` is not undefined, the component is controlled — the external * value is the source of truth. Otherwise, an internal signal manages state. */ export function createControllableSignal( options: CreateControllableSignalOptions, ): [Accessor, (value: T) => void] { const [internalValue, setInternalValue] = createSignal(options.defaultValue()); const get: Accessor = () => { const controlled = options.value(); if (controlled !== undefined) return controlled; return internalValue(); }; const set = (value: T) => { const isControlled = options.value() !== undefined; if (!isControlled) { // Use functional form so SolidJS does not interpret T-as-function as an updater. setInternalValue(() => value); } options.onChange?.(value); }; return [get, set]; }