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"; import type { Orientation } from "../internals/types.ts"; /** * The opaque identifier a consumer attaches to a tab + the matching panel. * `null`/`undefined` disables selection entirely. Mirrors the existing * `` wrapper's `TabsTabValue`. */ export type TabsValue = unknown; /** * Why the value changed. Emitted on the third arg of * `onValueChange(next, event, reason)` and accepted by * `c.setValue(next, event?, reason?)`. * * - `"trigger-press"` — click on a tab slot. * - `"focus"` — focus moved to a tab while `activateOnFocus` is enabled * (driven by the wrapper, not this primitive). * - `"imperative"` — programmatic `setValue` with no originating DOM event. */ export type TabsChangeReason = "trigger-press" | "focus" | "imperative"; /** * Options for {@link createTabs}. * * Every reactive input is `MaybeAccessor`. Read at use time via * `readOption(opt)`. * * **Solid-native shape (2026-04-30).** No internal state. `value` is * required and the consumer always owns the signal. The primitive is * a pure function from `(value, onValueChange) → api`. Migration from * pre-2026-04-30: `createTabs({ defaultValue: "a" })` becomes * `const [v, setV] = createSignal("a"); createTabs({ value: v, onValueChange: setV })`. * See `solidify.md` §6 for rationale. */ export interface CreateTabsOptions { /** * Current selected value. Consumer-owned signal — the primitive reads * through the accessor and does NOT keep an internal copy. Writes happen * via `onValueChange` (the consumer's responsibility to update their * signal in response). `undefined` means nothing is selected. */ value: MaybeAccessor; /** * Whether interaction is suppressed on the entire tabs widget. * @default false */ disabled?: MaybeAccessor; /** * Layout orientation — exposed so wrapper-side keyboard navigation + * `aria-orientation` projection can read off the api. * @default "horizontal" */ orientation?: MaybeAccessor; /** * Pre-change callback. Called synchronously with `(next, event, reason)`. * `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. Reading `c.value()` inside this * handler returns the OLD value. For post-change observation, use * `createEffect(c.value, ...)`. */ onValueChange?: ( value: TabsValue, event: Event, reason: TabsChangeReason, ) => void; } /** * Slot bag for the list slot — `
`. */ export interface TabsListSlotProps { role: "tablist"; "aria-orientation": "vertical" | undefined; } /** * Slot bag for a per-tab slot — `