import type { JSX } from "@solidjs/web"; import { type Accessor, createContext, createMemo, untrack, useContext, } from "solid-js"; import { makeSlotRefDirective } from "../internals/make-slot-ref-directive"; import { type MaybeAccessor, readOption } from "../internals/maybe-accessor"; import { mergeRootProps } from "../internals/merge-root-props"; import { SNAPSHOT_KEYS, type Snapshottable, snapshotKeys, } from "../internals/observability/snapshot"; /** * Why the checked state changed. Emitted on the third arg of * `onCheckedChange(next, event, reason)`. * * - `"trigger-press"` — click / keyboard activation that bubbles to click on * the switch root, OR a change on the co-located hidden `` (AT / * keyboard activating the checkbox directly). * - `"imperative"` — programmatic `setChecked` / `toggle` with no originating * DOM event. */ export type SwitchChangeReason = "trigger-press" | "imperative"; /** * Options for {@link createSwitch}. Every reactive input is * `MaybeAccessor` — pass an accessor to make the value reactive. * * **Solid-native shape (2026-04-30).** No internal state. `checked` is * required and the consumer always owns the signal. The primitive is a pure * function from `(checked, onCheckedChange) → api`. Migration: * `createSwitch({ defaultChecked: false })` becomes * `const [checked, setChecked] = createSignal(false); createSwitch({ checked, onCheckedChange: setChecked })`. * See `solidify.md` §6 for rationale. */ export interface CreateSwitchOptions { /** * Checked state. Consumer-owned signal — the primitive reads through * the accessor and does NOT keep an internal copy. Writes happen * via `onCheckedChange` (the consumer's responsibility to update their * signal in response). */ checked: MaybeAccessor; /** * Whether interaction is suppressed. * @default false */ disabled?: MaybeAccessor; /** * Whether the switch is read-only — focusable but not activatable. * @default false */ readOnly?: MaybeAccessor; /** * Whether the switch must be on for form submission. * @default false */ required?: MaybeAccessor; /** * Pre-change callback. `event.preventDefault()` is now a no-op for * the primitive (the consumer's signal write is the only writer); * left in the signature for parity with other event channels. * * Pre/post asymmetry: reading `c.checked()` inside this handler returns * the OLD value (the consumer's signal write hasn't flushed yet). For * post-change observation use `createEffect(c.checked, ...)`. */ onCheckedChange?: ( checked: boolean, event: Event, reason: SwitchChangeReason, ) => void; } /** * Public API returned by {@link createSwitch}. Flat accessors and setters at * the root; no `state` snapshot blob. */ export interface SwitchApi { checked: Accessor; disabled: Accessor; readOnly: Accessor; required: Accessor; /** * Flip `checked`. No-op while `disabled` or `readOnly`. Same `event?` / * `reason?` contract as {@link setChecked}. */ toggle: (event?: Event, reason?: SwitchChangeReason) => void; /** * Pure event-emitter: fires `onCheckedChange(next, event, reason)`. * No-op while `disabled` or `readOnly`. The consumer's signal write * (in their `onCheckedChange` handler) is the only writer. */ setChecked: ( next: boolean, event?: Event, reason?: SwitchChangeReason, ) => void; /** * Trigger slot — getter-bag of attributes ready for spreading onto a * `