dioxus_element_plug/components/
transfer.rs1use dioxus::prelude::*;
2
3#[derive(Clone, PartialEq)]
5pub struct TransferItem {
6 pub key: String,
7 pub label: String,
8 pub disabled: bool,
9}
10
11impl TransferItem {
12 pub fn new(key: &str, label: &str) -> Self {
13 Self {
14 key: key.to_string(),
15 label: label.to_string(),
16 disabled: false,
17 }
18 }
19
20 pub fn disabled(mut self, disabled: bool) -> Self {
21 self.disabled = disabled;
22 self
23 }
24}
25
26#[derive(Props, Clone, PartialEq)]
28pub struct TransferProps {
29 #[props(default)]
31 pub data: Vec<TransferItem>,
32
33 #[props(default)]
35 pub model_value: Vec<String>,
36
37 #[props(default = false)]
39 pub filterable: bool,
40
41 #[props(default)]
43 pub titles: Vec<String>,
44
45 #[props(default = "Enter keyword".to_string())]
47 pub filter_placeholder: String,
48
49 #[props(default = "No Data".to_string())]
51 pub empty_text: String,
52
53 #[props(default)]
55 pub on_change: Option<EventHandler<Vec<String>>>,
56
57 #[props(default)]
59 pub on_left_check: Option<EventHandler<Vec<String>>>,
60
61 #[props(default)]
63 pub on_right_check: Option<EventHandler<Vec<String>>>,
64
65 #[props(default)]
66 pub class: Option<String>,
67
68 #[props(default)]
69 pub style: Option<String>,
70}
71
72#[component]
97pub fn Transfer(props: TransferProps) -> Element {
98 let mut class_names = vec!["el-transfer".to_string()];
99 if let Some(ref c) = props.class {
100 class_names.push(c.clone());
101 }
102
103 let right_keys: std::collections::HashSet<&String> = props.model_value.iter().collect();
105
106 let left_items: Vec<(String, String, bool)> = props
107 .data
108 .iter()
109 .filter(|item| !right_keys.contains(&item.key))
110 .map(|item| (item.key.clone(), item.label.clone(), item.disabled))
111 .collect();
112
113 let right_items: Vec<(String, String, bool)> = props
114 .data
115 .iter()
116 .filter(|item| right_keys.contains(&item.key))
117 .map(|item| (item.key.clone(), item.label.clone(), item.disabled))
118 .collect();
119
120 let left_title = props.titles.first().cloned().unwrap_or_else(|| "List 1".to_string());
121 let right_title = props.titles.get(1).cloned().unwrap_or_else(|| "List 2".to_string());
122
123 let left_count = left_items.len();
124 let right_count = right_items.len();
125 let left_empty = left_items.is_empty();
126 let right_empty = right_items.is_empty();
127 let empty_text = props.empty_text.clone();
128 let filterable = props.filterable;
129 let filter_placeholder = props.filter_placeholder.clone();
130
131 let on_change = props.on_change;
132
133 rsx! {
134 div {
135 class: "{class_names.join(\" \")}",
136 style: props.style.clone().unwrap_or_default(),
137
138 div {
140 class: "el-transfer-panel",
141
142 p {
143 class: "el-transfer-panel__header",
144 span {
145 class: "el-checkbox__label",
146 "{left_title}"
147 }
148 span {
149 class: "el-transfer-panel__header-count",
150 "{left_count} items"
151 }
152 }
153
154 if filterable {
155 div {
156 class: "el-transfer-panel__filter",
157 input {
158 class: "el-input__inner",
159 r#type: "text",
160 placeholder: "{filter_placeholder}",
161 }
162 }
163 }
164
165 div {
166 class: "el-transfer-panel__body",
167
168 if left_empty {
169 p {
170 class: "el-transfer-panel__empty",
171 "{empty_text}"
172 }
173 } else {
174 for (key, label, disabled) in left_items.into_iter() {
175 label {
176 class: "el-transfer-panel__item",
177 input {
178 r#type: "checkbox",
179 disabled: disabled,
180 value: "{key}",
181 }
182 span {
183 class: "el-checkbox__label",
184 "{label}"
185 }
186 }
187 }
188 }
189 }
190 }
191
192 div {
194 class: "el-transfer__buttons",
195
196 button {
197 class: "el-button el-transfer__button el-button--primary",
198 r#type: "button",
199 onclick: move |_| {
200 let new_right: Vec<String> = props.data.iter()
203 .filter(|item| !item.disabled)
204 .map(|item| item.key.clone())
205 .collect();
206 if let Some(handler) = on_change.as_ref() {
207 handler.call(new_right);
208 }
209 },
210 disabled: left_count == 0,
211 i { class: "el-icon-arrow-right" }
212 }
213
214 button {
215 class: "el-button el-transfer__button el-button--primary",
216 r#type: "button",
217 onclick: move |_| {
218 if let Some(handler) = on_change.as_ref() {
220 handler.call(vec![]);
221 }
222 },
223 disabled: right_count == 0,
224 i { class: "el-icon-arrow-left" }
225 }
226 }
227
228 div {
230 class: "el-transfer-panel",
231
232 p {
233 class: "el-transfer-panel__header",
234 span {
235 class: "el-checkbox__label",
236 "{right_title}"
237 }
238 span {
239 class: "el-transfer-panel__header-count",
240 "{right_count} items"
241 }
242 }
243
244 if filterable {
245 div {
246 class: "el-transfer-panel__filter",
247 input {
248 class: "el-input__inner",
249 r#type: "text",
250 placeholder: "{filter_placeholder}",
251 }
252 }
253 }
254
255 div {
256 class: "el-transfer-panel__body",
257
258 if right_empty {
259 p {
260 class: "el-transfer-panel__empty",
261 "{empty_text}"
262 }
263 } else {
264 for (key, label, disabled) in right_items.into_iter() {
265 label {
266 class: "el-transfer-panel__item",
267 input {
268 r#type: "checkbox",
269 disabled: disabled,
270 value: "{key}",
271 checked: true,
272 }
273 span {
274 class: "el-checkbox__label",
275 "{label}"
276 }
277 }
278 }
279 }
280 }
281 }
282 }
283 }
284}