pepl_ui/components/
list.rs1use crate::accessibility;
7use crate::prop_value::PropValue;
8use crate::surface::SurfaceNode;
9
10pub struct ScrollListBuilder {
17 items: PropValue,
18 render: PropValue,
19 key: PropValue,
20 on_reorder: Option<PropValue>,
21 dividers: Option<bool>,
22}
23
24impl ScrollListBuilder {
25 pub fn new(items: PropValue, render: PropValue, key: PropValue) -> Self {
31 Self {
32 items,
33 render,
34 key,
35 on_reorder: None,
36 dividers: None,
37 }
38 }
39
40 pub fn on_reorder(mut self, on_reorder: PropValue) -> Self {
42 self.on_reorder = Some(on_reorder);
43 self
44 }
45
46 pub fn dividers(mut self, dividers: bool) -> Self {
48 self.dividers = Some(dividers);
49 self
50 }
51
52 pub fn build(self) -> SurfaceNode {
53 let mut node = SurfaceNode::new("ScrollList");
54 node.set_prop("items", self.items);
55 node.set_prop("render", self.render);
56 node.set_prop("key", self.key);
57 if let Some(on_reorder) = self.on_reorder {
58 node.set_prop("on_reorder", on_reorder);
59 }
60 if let Some(dividers) = self.dividers {
61 node.set_prop("dividers", PropValue::Bool(dividers));
62 }
63 accessibility::ensure_accessible(&mut node);
64 node
65 }
66}
67
68pub fn validate_list_node(node: &SurfaceNode) -> Vec<String> {
72 match node.component_type.as_str() {
73 "ScrollList" => validate_scroll_list(node),
74 _ => vec![format!("Unknown list component: {}", node.component_type)],
75 }
76}
77
78fn validate_scroll_list(node: &SurfaceNode) -> Vec<String> {
79 let mut errors = Vec::new();
80
81 match node.props.get("items") {
83 Some(PropValue::List(_)) => {}
84 Some(other) => errors.push(format!(
85 "ScrollList.items: expected list, got {}",
86 other.type_name()
87 )),
88 None => errors.push("ScrollList.items: required prop missing".to_string()),
89 }
90
91 match node.props.get("render") {
93 Some(PropValue::Lambda { .. }) => {}
94 Some(other) => errors.push(format!(
95 "ScrollList.render: expected lambda, got {}",
96 other.type_name()
97 )),
98 None => errors.push("ScrollList.render: required prop missing".to_string()),
99 }
100
101 match node.props.get("key") {
103 Some(PropValue::Lambda { .. }) => {}
104 Some(other) => errors.push(format!(
105 "ScrollList.key: expected lambda, got {}",
106 other.type_name()
107 )),
108 None => errors.push("ScrollList.key: required prop missing".to_string()),
109 }
110
111 if let Some(prop) = node.props.get("on_reorder") {
113 if !matches!(prop, PropValue::Lambda { .. }) {
114 errors.push(format!(
115 "ScrollList.on_reorder: expected lambda, got {}",
116 prop.type_name()
117 ));
118 }
119 }
120
121 if let Some(prop) = node.props.get("dividers") {
123 if !matches!(prop, PropValue::Bool(_)) {
124 errors.push(format!(
125 "ScrollList.dividers: expected bool, got {}",
126 prop.type_name()
127 ));
128 }
129 }
130
131 if !node.children.is_empty() {
133 errors.push(format!(
134 "ScrollList: does not accept children, but got {}",
135 node.children.len()
136 ));
137 }
138
139 if let Some(prop) = node.props.get("accessible") {
141 errors.extend(accessibility::validate_accessible_prop("ScrollList", prop));
142 }
143
144 for key in node.props.keys() {
146 if !matches!(
147 key.as_str(),
148 "items" | "render" | "key" | "on_reorder" | "dividers" | "accessible"
149 ) {
150 errors.push(format!("ScrollList: unknown prop '{key}'"));
151 }
152 }
153
154 errors
155}