import type { MaybeGetter } from "$lib/types.js"; import { isFunction } from "$lib/utils/is.js"; import { extract } from "./extract.svelte"; type SyncedArgs = | { value: MaybeGetter; onChange?: (value: T) => void; } | { value: MaybeGetter; onChange?: (value: T) => void; defaultValue: T; }; /** * Setting `current` calls the `onChange` callback with the new value. * * If the value arg is static, it will be used as the default value, * and subsequent sets will set an internal state that gets read as `current`. * * Otherwise, if it is a getter, it will be called every time `current` is read, * and no internal state is used. */ export class Synced { #internalValue = $state() as T; #valueArg: SyncedArgs["value"]; #onChange?: SyncedArgs["onChange"]; #defaultValue?: T; constructor({ value, onChange, ...args }: SyncedArgs) { this.#valueArg = value; this.#onChange = onChange; this.#defaultValue = "defaultValue" in args ? args?.defaultValue : undefined; this.#internalValue = extract(value, this.#defaultValue) as T; } get current() { return isFunction(this.#valueArg) ? (this.#valueArg() ?? this.#defaultValue ?? this.#internalValue) : this.#internalValue; } set current(value: T) { if (this.current === value) return; if (isFunction(this.#valueArg)) { this.#onChange?.(value); return; } this.#internalValue = value; this.#onChange?.(value); } }