Select
ui.widgets.Select단일 선택, 다중 선택, 지우기 가능, 검색 가능한 선택기.
BEHAVIOR CONTRACT
트리거, 옵션 패널, 키보드 하이라이트, 지우기 버튼, 선택된 값을 하나의 mount 결과가 관리합니다.
RECORDED INTERACTION
harness가 아이콘이 있는 선택기를 열고 Option 2를 선택합니다.
pub fn build(scope: *ui.Scope, cx: *ui.Cx) !*ui.Node {
const root = try ui.box(cx, .{
.width = .{ .px = 352 },
.direction = .column,
.gap = 16,
}, .{});
root.meta.ownership.meta.test_id = "story.select.family";
try root.appendChild(cx.allocator, try ui.text(cx, "Select", .{
.font_size = 12,
.font_weight = 600,
.line_height = 1.2,
.color = ui.Color.hex(0x1A1A1A),
}));
const with_icon = try appendSelect(root, "With icon", .{
.size = .lg, // puQEf 设计稿为 LG;Select 默认已统一为 md
.options = &icon_options,
.leading_icon = ui.icons.globe,
}, "story.select.with-icon", scope, cx);
with_icon.panel.meta.ownership.meta.test_id = "story.select.with-icon.panel";
with_icon.state.chevron_down.meta.ownership.meta.test_id = "story.select.with-icon.chevron";
for (with_icon.state.option_nodes, 0..) |node, i| {
node.meta.ownership.meta.test_id = switch (i) {
0 => "story.select.with-icon.option-1",
1 => "story.select.with-icon.option-2",
2 => "story.select.with-icon.option-3",
else => "story.select.with-icon.option-4",
};
}
const clearable = try appendSelect(root, "Clearable", .{
.size = .lg, // puQEf 设计稿为 LG;Select 默认已统一为 md
.options = &options,
.clearable = true,
.initial_selected = &.{0},
}, "story.select.clearable", scope, cx);
clearable.panel.meta.ownership.meta.test_id = "story.select.clearable.panel";
clearable.state.clear_button.?.meta.ownership.meta.test_id = "story.select.clear";
clearable.state.append_slot.meta.ownership.meta.test_id = "story.select.clearable.append";
clearable.state.append_comp.meta.ownership.meta.test_id = "story.select.clearable.append-comp";
clearable.state.chevron_slot.meta.ownership.meta.test_id = "story.select.clearable.chevron";
clearable.state.chevron_up.meta.ownership.meta.test_id = "story.select.clearable.chevron-up";
clearable.state.chevron_down.meta.ownership.meta.test_id = "story.select.clearable.chevron-down";
const searchable = try appendSelect(root, "Searchable", .{
.size = .lg, // puQEf 设计稿为 LG;Select 默认已统一为 md
.options = &search_options,
.searchable = true,
.placeholder = "Search options...",
.initial_selected = &.{0},
.show_selected_value_in_search = false,
}, "story.select.trigger", scope, cx);
searchable.panel.meta.ownership.meta.test_id = "story.select.panel";
searchable.state.leading_icon_node.?.meta.ownership.meta.test_id = "story.select.search.icon";
searchable.state.append_slot.meta.ownership.meta.test_id = "story.select.search.append";
searchable.state.input_state.?.input_container_node.?.meta.ownership.meta.test_id = "story.select.search.input";
for (searchable.state.option_nodes, 0..) |node, i| {
node.meta.ownership.meta.test_id = switch (i) {
0 => "story.select.search.option-1",
1 => "story.select.search.option-2",
2 => "story.select.search.option-3",
else => unreachable,
};
}
const multi_lg = try appendSelect(root, "Multiple · LG", .{
.size = .lg, // puQEf 设计稿为 LG;Select 默认已统一为 md
.options = &options,
.mode = .multiple,
.initial_selected = &.{ 0, 1 },
}, "story.select.multiple-lg", scope, cx);
multi_lg.panel.meta.ownership.meta.test_id = "story.select.multiple-lg.panel";
for (multi_lg.state.option_nodes, 0..) |node, i| {
node.meta.ownership.meta.test_id = switch (i) {
0 => "story.select.multiple-lg.option-1",
1 => "story.select.multiple-lg.option-2",
2 => "story.select.multiple-lg.option-3",
else => "story.select.multiple-lg.option-4",
};
}
multi_lg.state.tag_nodes[2].meta.ownership.meta.test_id = "story.select.multiple-lg.tag-3";
multi_lg.state.tag_nodes[2].children.items[1].meta.ownership.meta.test_id = "story.select.multiple-lg.tag-3-close";
const multi_search_lg = try appendSelect(root, "Multiple searchable · LG", .{
.size = .lg, // puQEf 设计稿为 LG;Select 默认已统一为 md
.options = &search_options,
.mode = .multiple,
.searchable = true,
.initial_selected = &.{0},
}, "story.select.multiple-search-lg", scope, cx);
multi_search_lg.panel.meta.ownership.meta.test_id = "story.select.multiple-search-lg.panel";
multi_search_lg.state.content_slot.meta.ownership.meta.test_id = "story.select.multiple-search-lg.content";
multi_search_lg.state.append_slot.meta.ownership.meta.test_id = "story.select.multiple-search-lg.append";
multi_search_lg.state.input_state.?.input_container_node.?.meta.ownership.meta.test_id = "story.select.multiple-search-lg.input";
multi_search_lg.state.option_nodes[2].meta.ownership.meta.test_id = "story.select.multiple-search-lg.option-3";
const multi_md = try appendSelect(root, "Multiple · MD", .{
.options = &options,
.mode = .multiple,
.size = .md,
.initial_selected = &.{ 0, 1 },
}, "story.select.multiple-md", scope, cx);
multi_md.panel.meta.ownership.meta.test_id = "story.select.multiple-md.panel";
const multi_search_md = try appendSelect(root, "Multiple searchable · MD", .{
.options = &search_options,
.mode = .multiple,
.size = .md,
.searchable = true,
.initial_selected = &.{0},
}, "story.select.multiple-search-md", scope, cx);
multi_search_md.panel.meta.ownership.meta.test_id = "story.select.multiple-search-md.panel";
multi_search_md.state.input_state.?.input_container_node.?.meta.ownership.meta.test_id = "story.select.multiple-search-md.input";
multi_search_md.state.option_nodes[2].meta.ownership.meta.test_id = "story.select.multiple-search-md.option-3";
const multi_xs = try appendSelect(root, "Multiple · XS", .{
.options = &options,
.mode = .multiple,
.size = .xs,
.initial_selected = &.{ 0, 1 },
}, "story.select.multiple-xs", scope, cx);
multi_xs.panel.meta.ownership.meta.test_id = "story.select.multiple-xs.panel";
const multi_search_xs = try appendSelect(root, "Multiple searchable · XS", .{
.options = &search_options,
.mode = .multiple,
.size = .xs,
.searchable = true,
.initial_selected = &.{0},
}, "story.select.multiple-search-xs", scope, cx);
multi_search_xs.panel.meta.ownership.meta.test_id = "story.select.multiple-search-xs.panel";
multi_search_xs.state.input_state.?.input_container_node.?.meta.ownership.meta.test_id = "story.select.multiple-search-xs.input";
multi_search_xs.state.option_nodes[2].meta.ownership.meta.test_id = "story.select.multiple-search-xs.option-3";
return root;
}Storybook에 있는 이 story의 원본 코드입니다(zenit 5f9add5+wip 2026-09-30). W는 ui.widgets이며, col / row / label은 Storybook 내부의 작은 레이아웃 헬퍼입니다.
SelectProps
ui.widgets.Selectfn mountSelect(props: SelectProps, scope: *Scope, cx: *Cx) !SelectMount필드
타입
기본값
설명
options[]const SelectOption&.{}—
placeholder[]const u8"Select an option..."—
widthf32352—
sizeSelectSize.md—
modeSelectMode.single.single—
searchableboolfalse—
show_selected_value_in_searchbooltrueKeep 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.
clearableboolfalse—
leading_icon?svg_assets.Assetnull—
initial_selected[]const usize&.{}—
disabledboolfalse—
max_dropdown_heightf32280—
on_change?core.HandlerRefnull—
SelectMount
ui.widgets.Select필드
타입
기본값
설명
wrapper*Node—
—
trigger*Node—
—
panel*Node—
—
state*SelectState—
—
is_open*Signal(bool)—
—