On this pageFunctions
Ui/Tabs
/**
* Pairs the tabs `view`, `update`, and `selectTab` behind a single
* Value-typed entry point. Declare once at module scope so consumers
* receive `tab.value: Value` in `toView` without an `as` cast:
*
* ```ts
* const DemoTabs = Ui.Tabs.create<DemoTab>()
*
* // In view:
* h.submodel({ view: DemoTabs.view, ... })
*
* // In update:
* const [next, commands] = DemoTabs.update(model, message)
* ```
*
* The internal view stays typed `ReadonlyArray<string>`; consumers can
* pass a `ReadonlyArray<MyUnion>` (assignable) and the fenced cast inside
* `create` types `TabInfo.value` as `MyUnion`.
*/
<Value extends string = string>(): Readonly<{
reflectSelectedTab: Reflect2<Model, Value, ReadonlyArray<Value>>
selectTab: (model: Model, value: Value, index: number) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Value>>]
update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Value>>]
view: SubmodelView<Model, Message, ViewInputs<Value>>
}>/** Creates an initial tabs model from a config. Defaults to first tab and automatic activation. */
(config: InitConfig): Tabs.Model/** Controls whether tabs activate on focus (`Automatic`) or require an explicit selection (`Manual`). */
type ActivationMode = Literals<readonly ["Automatic", "Manual"]>/** Configuration for creating a tabs model with `init`. */
type InitConfig = Readonly<{
activationMode: ActivationMode
activeIndex: number
id: string
}>/** Controls the tab list layout direction and which arrow keys navigate between tabs. */
type Orientation = Literals<readonly ["Horizontal", "Vertical"]>/**
* Generic over `Value extends string` so consumers using
* `Ui.Tabs.create<MyUnion>()` receive `value: MyUnion` in the
* `Selected` OutMessage. Defaults to `string`.
*/
type OutMessage = Selected<Value>/**
* Render-time payload published to the consumer's `toView`.
*
* - `tablist`: ARIA + role attributes for the wrapping tablist element.
* - `tabs`: one entry per tab in `viewInputs.tabs`, in the same order, with
* the tab button's attribute bundle, the panel's attribute bundle,
* and derived state.
* - `activeIndex`: the currently-active tab index, convenient when the
* consumer wants to render only the active panel (vs all panels with
* `Hidden` for transitions).
*/
type RenderInfo = Readonly<{
activeIndex: number
tablist: ReadonlyArray<ChildAttribute>
tabs: ReadonlyArray<TabInfo<Value>>
}>/** Sent to the parent when a tab is committed via click or keyboard. Carries both the tab's value (typed as `Value` via `Ui.Tabs.create<Value>()`) and its index. Generic at the type level; the schema stores `value: string` and the factory's fenced cast types it as `Value`. */
type Selected = Readonly<{
_tag: "Selected"
index: number
value: Value
}>/**
* Per-tab render info passed to the consumer's `toView`. Generic over
* `Value extends string`: when `Ui.Tabs.create<MyUnion>()` is declared,
* `tab.value` is typed `MyUnion` so the consumer can switch on it without
* casting.
*/
type TabInfo = Readonly<{
index: number
isActive: boolean
isDisabled: boolean
isFocused: boolean
panel: ReadonlyArray<ChildAttribute>
tab: ReadonlyArray<ChildAttribute>
value: Value
}>/**
* Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field.
* Generic over `Value extends string` so consumers using
* `Ui.Tabs.create<MyUnion>()` receive `tab.value: MyUnion` in `toView`
* and `(value: MyUnion, index) => boolean` in `isTabDisabled`, without
* casting.
*/
type ViewInputs = Readonly<{
ariaLabel: string
isTabDisabled: (value: Value, index: number) => boolean
orientation: Orientation
tabs: ReadonlyArray<Value>
toView: (render: RenderInfo<Value>) => Html
}>/** Sent when the focus-tab command completes. */
const CompletedFocusTab: CallableTaggedStruct<"CompletedFocusTab", {}>/** Moves focus to the tab at the given index. */
const FocusTab: CommandDefinitionWithArgs<"FocusTab", {
id: String
index: Number
}, Effect<{
_tag: "CompletedFocusTab"
}, never, never>>/** Sent when a tab receives keyboard focus in `Manual` mode without being activated. */
const FocusedTab: CallableTaggedStruct<"FocusedTab", {
index: Number
}>/** Union of all messages the tabs component can produce. */
const Message: S.Union<[typeof SelectedTab, typeof FocusedTab, typeof CompletedFocusTab]>/** Schema for the tabs component's state, tracking active/focused indices and activation mode. */
const Model: Struct<{
activationMode: Literals<readonly ["Automatic", "Manual"]>
activeIndex: Number
focusedIndex: Number
id: String
}>/** Union of out-messages the tabs component can produce. Surfaced as the third element of `update`'s return tuple and pattern-matched by the parent. */
const OutMessage: Union<readonly [
CallableTaggedStruct<"Selected", {
index: Number
value: String
}>
]>/** Sent to the parent when a tab is committed via click or keyboard. Carries both the tab's value (typed as `Value` via `Ui.Tabs.create<Value>()`) and its index. Generic at the type level; the schema stores `value: string` and the factory's fenced cast types it as `Value`. */
const Selected: CallableTaggedStruct<"Selected", {
index: Number
value: String
}>