Input
ui.widgets.InputSaisie sur une ligne pour texte, e-mail, mot de passe et recherche.
BEHAVIOR CONTRACT
label, helper, error, required, lecture seule, disabled, icône de tête et IME partagent le cœur d'édition complet.
RECORDED INTERACTION
Le harness met le focus sur le champ Name et saisit du vrai texte.
// ── Input ──
pub fn buildInput(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
const a = cx.allocator;
const icons = ui.assets.common;
const c = try col(cx, 12);
// ── 类型 ──
try c.appendChild(a, try label(cx, "Types"));
const n1 = try W.Input(.{ .label_text = "Name", .placeholder = "Your name", .width = 320 }).mount(scope, cx);
n1.meta.ownership.meta.test_id = "story.input.name";
try c.appendChild(a, n1);
try c.appendChild(a, try W.Input(.{ .label_text = "Email", .input_type = .email, .placeholder = "you@example.com", .width = 320 }).mount(scope, cx));
try c.appendChild(a, try W.Input(.{ .label_text = "Password", .input_type = .password, .placeholder = "••••••••", .width = 320 }).mount(scope, cx));
// ── 尺寸 ──
try c.appendChild(a, try label(cx, "Sizes (sm / md / lg)"));
inline for (.{ W.input.InputSize.sm, .md, .lg }, .{ "Small", "Medium", "Large" }) |sz, ph| {
try c.appendChild(a, try W.Input(.{ .size = sz, .placeholder = ph, .width = 320 }).mount(scope, cx));
}
// ── 带图标 / append ──
try c.appendChild(a, try label(cx, "With icon / append"));
try c.appendChild(a, try W.Input(.{ .label_text = "Search", .placeholder = "Type to search…", .leading_icon_asset = icons.search, .width = 320 }).mount(scope, cx));
try c.appendChild(a, try W.Input(.{ .label_text = "Website", .placeholder = "mysite", .append_text = ".com", .width = 320 }).mount(scope, cx));
// ── 状态:required / helper / error / readonly / disabled ──
try c.appendChild(a, try label(cx, "States"));
try c.appendChild(a, try W.Input(.{ .label_text = "Required", .placeholder = "Mandatory", .required = true, .width = 320 }).mount(scope, cx));
try c.appendChild(a, try W.Input(.{ .label_text = "With helper", .placeholder = "Username", .helper = "3–20 characters", .width = 320 }).mount(scope, cx));
try c.appendChild(a, try W.Input(.{ .label_text = "With error", .initial_value = "bad value", .error_msg = "This field is invalid", .width = 320 }).mount(scope, cx));
try c.appendChild(a, try W.Input(.{ .label_text = "Readonly", .initial_value = "Read only value", .readonly = true, .width = 320 }).mount(scope, cx));
try c.appendChild(a, try W.Input(.{ .label_text = "Disabled", .placeholder = "Cannot type", .disabled = true, .width = 320 }).mount(scope, cx));
return c;
}Le code source tel quel de la story dans le Storybook (zenit 5f9add5+wip 2026-09-30). W désigne ui.widgets ; col / row / label sont de petits helpers de mise en page du Storybook.
InputProps
ui.widgets.Inputfn Input(props: InputProps) InputBuilderChamp
Type
Défaut
Description
initial_value?[]const u8null初始值。**只在 mount 时读取一次** —— 之后改无效。 首帧之后要写入内容,用 `mountResult()` 拿 `result.state` 再调 `setText()`(2026-07-31 新增)。
growableboolfalse—
placeholder?[]const u8null占位符
placeholder_color?Colornull占位符文本颜色覆盖
input_typeInputType.text.number.email.password.tel.text输入类型
sizeInputSize.md尺寸 (统一 ControlSize,默认 md;外框高度 = padding_y × 2 + font_size × line_height, 默认主题 xs=20 / sm=24 / md=32 / lg=40)
label_text?[]const u8null标签
helper?[]const u8null辅助文本
error_msg?[]const u8null错误信息
requiredboolfalse必填
readonlyboolfalse只读
disabledboolfalse禁用
width?f32null宽度
leading_icon_asset?svg_assets.Assetnull左侧图标
append_text?[]const u8null右侧附加文本(如计数)
append_icon_asset?svg_assets.Assetnull右侧附加图标(如 clear / eye)
embeddedboolfalse嵌入复合控件时仅保留文本编辑能力,由父控件绘制统一 shell。 Select/ComboBox 等组件使用它避免出现双层 border / focus ring。
state_id?u64null状态 ID (用于 StateStore, 如果为 null 则使用自动生成的 ID)
on_change?core.HandlerRefnull值变化回调 2026-07-31 并轨:统一 ?core.HandlerRef(用 cx.strHandlerFrom 构造以拿到文本)。
*Node
ui.widgets.Inputmount renvoie directement *Node : ajoutez-le à un parent.