dioxus_dnd/
multiselect.rs1#![doc = include_str!("../docs/api/multiselect.md")]
2
3use dioxus::prelude::*;
4
5use crate::core::{use_dnd, Draggable, DropEffect, ZoneId};
6
7pub struct Selection<K: Clone + PartialEq + 'static> {
9 items: Signal<Vec<K>>,
10}
11
12impl<K: Clone + PartialEq + 'static> Copy for Selection<K> {}
13impl<K: Clone + PartialEq + 'static> Clone for Selection<K> {
14 fn clone(&self) -> Self {
15 *self
16 }
17}
18impl<K: Clone + PartialEq + 'static> PartialEq for Selection<K> {
19 fn eq(&self, other: &Self) -> bool {
20 self.items == other.items
21 }
22}
23
24impl<K: Clone + PartialEq + 'static> Selection<K> {
25 pub fn from_signal(items: Signal<Vec<K>>) -> Self {
27 Self { items }
28 }
29
30 pub fn is_selected(&self, key: &K) -> bool {
32 self.items.read().contains(key)
33 }
34
35 pub fn select_only(&mut self, key: K) {
37 self.items.set(vec![key]);
38 }
39
40 pub fn toggle(&mut self, key: K) {
42 let mut items = self.items.write();
43 if let Some(ix) = items.iter().position(|k| *k == key) {
44 items.remove(ix);
45 } else {
46 items.push(key);
47 }
48 }
49
50 pub fn clear(&mut self) {
52 self.items.write().clear();
53 }
54
55 pub fn items(&self) -> Vec<K> {
57 self.items.read().clone()
58 }
59
60 pub fn len(&self) -> usize {
62 self.items.read().len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.items.read().is_empty()
68 }
69
70 pub fn click(&mut self, key: K, modifiers: Modifiers) {
73 if modifiers.contains(Modifiers::CONTROL) || modifiers.contains(Modifiers::META) {
74 self.toggle(key);
75 } else {
76 self.select_only(key);
77 }
78 }
79}
80
81pub fn use_selection<K: Clone + PartialEq + 'static>() -> Selection<K> {
83 Selection {
84 items: use_signal(Vec::new),
85 }
86}
87
88#[component]
100pub fn SelectableDraggable<K: Clone + PartialEq + 'static>(
101 item: K,
103 selection: Selection<K>,
105 #[props(default)]
107 zone: Option<ZoneId>,
108 #[props(default)]
110 effect: DropEffect,
111 #[props(default)]
113 label: Option<String>,
114 #[props(extends = div, extends = GlobalAttributes)] attributes: Vec<Attribute>,
115 children: Element,
116) -> Element {
117 let _ = use_dnd::<Vec<K>>(); let selected = selection.is_selected(&item);
119 let payload = if selected {
122 selection.items()
123 } else {
124 vec![item.clone()]
125 };
126 let click_key = item.clone();
127 let mut selection = selection;
128 let mut dragged = use_signal(|| false);
133
134 rsx! {
135 div {
136 "data-selected": if selected { "true" },
137 onclick: move |evt: MouseEvent| {
138 if *dragged.peek() {
139 dragged.set(false);
140 return;
141 }
142 selection.click(click_key.clone(), evt.modifiers());
143 },
144 ..attributes,
145 Draggable::<Vec<K>> {
146 payload,
147 zone,
148 effect,
149 label,
150 on_drag_start: move |_| dragged.set(true),
151 {children}
152 }
153 }
154 }
155}
156
157#[component]
160pub fn SelectionCount<K: Clone + PartialEq + 'static>(
161 #[props(default)]
163 phantom: std::marker::PhantomData<K>,
164) -> Element {
165 let _ = phantom;
166 let dnd = use_dnd::<Vec<K>>();
167 let strings = crate::core::use_dnd_strings();
168 let n = dnd.payload().map(|p| p.len()).unwrap_or(0);
169 let text = (strings.selection_count)(n);
170 rsx! {
171 span { "{text}" }
172 }
173}