---
title: "Form Composition-Komponente — zenit Zig UI"
description: "Input, Select und Button gemischt in einer Größe: eine Filterleiste und ein Geschäftsformular."
url: https://zenit.z.express/de/components/formcompose
language: de
alternate_en: https://zenit.z.express/components/formcompose.md
alternate_zh: https://zenit.z.express/zh/components/formcompose.md
alternate_es: https://zenit.z.express/es/components/formcompose.md
alternate_ja: https://zenit.z.express/ja/components/formcompose.md
alternate_ko: https://zenit.z.express/ko/components/formcompose.md
alternate_fr: https://zenit.z.express/fr/components/formcompose.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Form Composition

`Input · Select · Button`

Input, Select und Button gemischt in einer Größe: eine Filterleiste und ein Geschäftsformular.

[Video](https://zenit.z.express/media/stories/formcompose.mp4?v=d8a64b745e)

**BEHAVIOR CONTRACT**

Alle drei teilen sich ControlSize (Boxhöhe XS 20 / SM 24 / MD 32 / LG 40), sodass Höhe, Radius, Schrift, Icons und Abstände in jeder Größe übereinstimmen.

**RECORDED INTERACTION**

Der Harness filtert in der Leiste nach Status und Verantwortlichem und tippt eine Suche, dann füllt er Kunde, Region und Betrag im Formular aus.

`examples/storybook/form_composition.zig:185`

```zig
pub fn build(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
    const a = cx.allocator;
    const root = try ui.box(cx, .{ .direction = .column, .gap = 16 }, .{});
    tag(root, "story.formcompose.root");

    try root.appendChild(a, try heading(cx, "Filter toolbar · same size"));
    try root.appendChild(a, try caption(cx, "Input / Select / Button share ControlSize: XS 20 · SM 24 · MD 32 · LG 40 px"));
    inline for (sizes) |spec| {
        try root.appendChild(a, try filterToolbar(spec, scope, cx));
    }

    try root.appendChild(a, try heading(cx, "Business form · MD"));
    try root.appendChild(a, try orderForm(scope, cx));
    return root;
}
```

[Quellcode auf GitHub ansehenexamples/storybook/form\_composition.zig:185](https://github.com/version-next/zenit/blob/HEAD/examples/storybook/form_composition.zig#L185)

Der unveränderte Quellcode der Story aus dem Storybook (zenit 5f9add5+wip 2026-09-30). `W` steht für `ui.widgets`; `col` / `row` / `label` sind kleine Layout-Helfer des Storybooks.

### SelectProps

`ui.widgets.Select`

[src/ui/components/select/mod.zig:41](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/select/mod.zig#L41)

```zig
fn mountSelect(props: SelectProps, scope: *Scope, cx: *Cx) !SelectMount
```

| Feld | Typ | Standard | Beschreibung |
| --- | --- | --- | --- |
| `options` | `[]const SelectOption` | `&.{}` | — |
| `placeholder` | `[]const u8` | `"Select an option..."` | — |
| `width` | `f32` | `352` | — |
| `size` | `SelectSize` | `.md` | — |
| `mode` | `SelectMode` `.single` | `.single` | — |
| `searchable` | `bool` | `false` | — |
| `show_selected_value_in_search` | `bool` | `true` | Keep the selected label in a searchable single-select trigger. Set to false for search-first presentations where the trigger should retain its search placeholder while the selected row remains checked. |
| `clearable` | `bool` | `false` | — |
| `leading_icon` | `?svg_assets.Asset` | `null` | — |
| `initial_selected` | `[]const usize` | `&.{}` | — |
| `disabled` | `bool` | `false` | — |
| `max_dropdown_height` | `f32` | `280` | — |
| `on_change` | `?core.HandlerRef` | `null` | — |

### InputProps

`ui.widgets.Input`

[src/ui/components/input/mod.zig:56](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/input/mod.zig#L56)

```zig
fn Input(props: InputProps) InputBuilder
```

| Feld | Typ | Standard | Beschreibung |
| --- | --- | --- | --- |
| `initial_value` | `?[]const u8` | `null` | 初始值。\*\*只在 mount 时读取一次\*\* —— 之后改无效。 首帧之后要写入内容，用 \`mountResult()\` 拿 \`result.state\` 再调 \`setText()\`（2026-07-31 新增）。 |
| `growable` | `bool` | `false` | — |
| `placeholder` | `?[]const u8` | `null` | 占位符 |
| `placeholder_color` | `?Color` | `null` | 占位符文本颜色覆盖 |
| `input_type` | `InputType` `.text` `.number` `.email` `.password` `.tel` | `.text` | 输入类型 |
| `size` | `InputSize` | `.md` | 尺寸 (统一 ControlSize，默认 md；外框高度 = padding\_y × 2 + font\_size × line\_height， 默认主题 xs=20 / sm=24 / md=32 / lg=40) |
| `label_text` | `?[]const u8` | `null` | 标签 |
| `helper` | `?[]const u8` | `null` | 辅助文本 |
| `error_msg` | `?[]const u8` | `null` | 错误信息 |
| `required` | `bool` | `false` | 必填 |
| `readonly` | `bool` | `false` | 只读 |
| `disabled` | `bool` | `false` | 禁用 |
| `width` | `?f32` | `null` | 宽度 |
| `leading_icon_asset` | `?svg_assets.Asset` | `null` | 左侧图标 |
| `append_text` | `?[]const u8` | `null` | 右侧附加文本（如计数） |
| `append_icon_asset` | `?svg_assets.Asset` | `null` | 右侧附加图标（如 clear / eye） |
| `embedded` | `bool` | `false` | 嵌入复合控件时仅保留文本编辑能力，由父控件绘制统一 shell。 Select/ComboBox 等组件使用它避免出现双层 border / focus ring。 |
| `state_id` | `?u64` | `null` | 状态 ID (用于 StateStore, 如果为 null 则使用自动生成的 ID) |
| `on_change` | `?core.HandlerRef` | `null` | 值变化回调 2026-07-31 并轨：统一 ?core.HandlerRef（用 cx.strHandlerFrom 构造以拿到文本）。 |

### ButtonProps

`ui.widgets.Button`

[src/ui/components/button/mod.zig:34](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/button/mod.zig#L34)

```zig
fn Button(props: ButtonProps) ButtonBuilder
```

| Feld | Typ | Standard | Beschreibung |
| --- | --- | --- | --- |
| `label` | `?[]const u8` | `null` | 标签文本 |
| `variant` | `ButtonVariant` `.primary` `.secondary` `.ghost` `.danger` `.link` `.field` | `.primary` | 变体 |
| `size` | `ButtonSize` | `.md` | 尺寸 |
| `icon_only` | `bool` | `false` | 图标模式（只显示图标） |
| `loading` | `bool` | `false` | 加载中 |
| `disabled` | `bool` | `false` | 禁用 |
| `block` | `bool` | `false` | 块级（宽度填充） 非 block Button 保持 intrinsic 宽度：父容器交叉轴为 stretch（默认）时按 start 排而不被拉伸；父显式 align\_items=.center/.end 时照常居中/靠尾。 |
| `on_click` | `?core.HandlerRef` | `null` | 点击 handler |
| `icon` | `?u32` | `null` | 图标纹理 ID（放在 label 文本左侧） |
| `icon_asset` | `?svg_assets.Asset` | `null` | SVG 图标资源（优先于 texture icon） |
| `icon_size` | `?f32` | `null` | 图标尺寸（null = 跟随 control metrics 的 icon\_size；应 ≤ 行高） |
| `icon_tint` | `?Color` | `null` | 图标 tint 颜色（默认跟随 textColor） |
| `on_event` | `?GenericEventCallback` | `null` | 通用事件处理（支持 hover/key 等复合事件） |
| `event_context` | `?*anyopaque` | `null` | 事件上下文 |
| **样式覆盖** | | | |
| `style` | `StyleOverride` | `.{}` | 默认态样式覆盖 |
| `hover_style` | `?StyleOverride` | `null` | hover 态样式覆盖 |
| `pressed_style` | `?StyleOverride` | `null` | pressed 态样式覆盖 |

### SelectMount

`ui.widgets.Select`

[src/ui/components/select/mod.zig:217](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/select/mod.zig#L217)

| Feld | Typ | Standard | Beschreibung |
| --- | --- | --- | --- |
| `wrapper` | `*Node` | — | — |
| `trigger` | `*Node` | — | — |
| `panel` | `*Node` | — | — |
| `state` | `*SelectState` | — | — |
| `is_open` | `*Signal(bool)` | — | — |

### \*Node

`ui.widgets.Input`

mount gibt `*Node` direkt zurück – hängen Sie es an einen Elternknoten an.

### \*Node

`ui.widgets.Button`

mount gibt `*Node` direkt zurück – hängen Sie es an einen Elternknoten an.
