1use dioxus::prelude::*;
2
3use crate::{Columns, Row};
4use std::marker::PhantomData;
5
6mod column_order;
7pub use column_order::ColumnOrder;
8
9#[derive(Clone, Copy, PartialEq, Debug)]
11pub enum SortDirection {
12 Ascending,
14 Descending,
16}
17
18#[derive(Clone, Copy, PartialEq)]
30pub struct Sort {
31 pub direction: SortDirection,
33}
34
35#[derive(Clone, Copy, PartialEq, Debug)]
52pub struct SortInfo {
53 pub priority: usize,
55 pub direction: SortDirection,
57}
58
59#[derive(Clone, Copy, PartialEq)]
82pub enum SortGesture {
83 Cancel,
85 AddFirst(Sort),
87 AddLast(Sort),
89 Toggle,
92}
93
94#[derive(Clone, Copy, PartialEq)]
95pub struct SortRecord {
96 column: usize,
97 sort: Sort,
98}
99
100#[derive(Clone, Copy, PartialEq)]
101pub(crate) struct TableContextData {
102 sorts: Signal<Vec<SortRecord>>,
103 column_names: Signal<Vec<String>>,
105 column_order: Signal<ColumnOrder>,
107}
108
109#[derive(PartialEq)]
110pub struct TableContext<C: 'static> {
111 pub(crate) data: TableContextData,
112 pub(crate) columns: Signal<C>,
114}
115
116impl<C: 'static> Copy for TableContext<C> {}
117
118impl<C: 'static> Clone for TableContext<C> {
119 fn clone(&self) -> Self {
120 *self
121 }
122}
123
124impl<C> TableContext<C> {
125 pub fn use_table_context<R>(columns: C) -> Self
126 where
127 C: Columns<R>,
128 R: Row,
129 {
130 let sorts = use_signal(Vec::new);
131 let column_names = use_signal(|| columns.column_names());
132 let total_columns = column_names.read().len();
133 let column_order = use_signal(|| ColumnOrder::new(total_columns));
134 let columns = use_signal(|| columns);
135 Self {
136 data: TableContextData {
137 sorts,
138 column_names,
139 column_order,
140 },
141 columns,
142 }
143 }
144
145 pub fn table_data<R>(self, rows: ReadSignal<Vec<R>>) -> TableData<C, R>
146 where
147 C: Columns<R>,
148 R: Row,
149 {
150 TableData {
151 context: self,
152 rows,
153 }
154 }
155
156 fn get_column_order(&self) -> Vec<usize> {
157 self.data.get_column_order()
158 }
159
160 pub fn headers<R>(self) -> impl Iterator<Item = HeaderData<C, R>>
161 where
162 C: Columns<R>,
163 R: Row,
164 {
165 let order = self.get_column_order();
166 order.into_iter().map(move |column_index| HeaderData {
167 context: self,
168 column_index,
169 _phantom: PhantomData,
170 })
171 }
172
173 pub fn cells<R>(self, row: RowData<C, R>) -> impl Iterator<Item = CellData<C, R>>
174 where
175 C: Columns<R>,
176 R: Row,
177 {
178 let order = self.get_column_order();
179 let row_copy = row;
180 order.into_iter().map(move |column_index| CellData {
181 row: row_copy,
182 column_index,
183 })
184 }
185
186 pub fn rows<R>(self, rows: ReadSignal<Vec<R>>) -> impl Iterator<Item = RowData<C, R>>
187 where
188 C: Columns<R>,
189 R: Row,
190 {
191 let rows_data = rows.read();
192 let columns = self.columns.read();
193
194 let mut filtered_indices: Vec<usize> = (0..rows_data.len())
196 .filter(|&i| columns.filter(&rows_data[i]))
197 .collect();
198
199 let sort_records = self.data.sorts.read();
201 if !sort_records.is_empty() {
202 let comparators = columns.compare();
203
204 filtered_indices.sort_by(|&a, &b| {
206 for sort_record in sort_records.iter() {
208 let ordering = comparators[sort_record.column](&rows_data[a], &rows_data[b]);
209
210 let directed_ordering = match sort_record.sort.direction {
212 SortDirection::Ascending => ordering,
213 SortDirection::Descending => ordering.reverse(),
214 };
215
216 if directed_ordering != std::cmp::Ordering::Equal {
218 return directed_ordering;
219 }
220 }
222
223 std::cmp::Ordering::Equal
225 });
226 }
227
228 filtered_indices.into_iter().map(move |i| RowData {
230 context: self,
231 rows,
232 index: i,
233 _phantom: PhantomData,
234 })
235 }
236}
237
238impl TableContextData {
239 pub fn column_context(&self, column: usize) -> ColumnContext {
240 ColumnContext {
241 table_context: *self,
242 column,
243 }
244 }
245
246 pub fn get_column_order(&self) -> Vec<usize> {
247 self.column_order.read().get_order().to_vec()
248 }
249
250 pub fn get_column_name(&self, index: usize) -> String {
251 self.column_names.read()[index].clone()
252 }
253
254 pub fn request_sort(&self, column: usize, sort: SortGesture) {
255 match sort {
256 SortGesture::Cancel => {
257 let mut signal = self.sorts;
258 signal.write().retain(|record| record.column != column);
259 }
260 SortGesture::AddFirst(sort) => {
261 let mut signal = self.sorts;
262 let mut write = signal.write();
263 write.retain(|record| record.column != column);
264 write.insert(0, SortRecord { column, sort });
265 }
266 SortGesture::AddLast(sort) => {
267 let mut signal = self.sorts;
268 let mut write = signal.write();
269 write.retain(|record| record.column != column);
270 write.push(SortRecord { column, sort });
271 }
272 SortGesture::Toggle => {
273 let mut signal = self.sorts;
274 if let Some(record) = signal.write().iter_mut().find(|r| r.column == column) {
275 record.sort.direction = match record.sort.direction {
276 SortDirection::Ascending => SortDirection::Descending,
277 SortDirection::Descending => SortDirection::Ascending,
278 };
279 }
280 }
281 }
282 }
283
284 pub fn swap_columns(&self, col_a: usize, col_b: usize) {
287 let mut signal = self.column_order;
288 signal.write().swap(col_a, col_b);
289 }
290
291 pub fn hide_column(&self, col: usize) {
292 let mut signal = self.column_order;
293 signal.write().hide_column(col);
294 }
295
296 pub fn show_column(&self, col: usize, at_index: Option<usize>) {
297 let mut signal = self.column_order;
298 signal.write().show_column(col, at_index);
299 }
300
301 pub fn move_column_to(&self, col: usize, new_index: usize) {
302 let mut signal = self.column_order;
303 signal.write().move_to(col, new_index);
304 }
305
306 pub fn move_column_forward(&self, col: usize) {
307 let mut signal = self.column_order;
308 signal.write().move_forward(col);
309 }
310
311 pub fn move_column_backward(&self, col: usize) {
312 let mut signal = self.column_order;
313 signal.write().move_backward(col);
314 }
315
316 pub fn is_column_visible(&self, col: usize) -> bool {
317 self.column_order.read().is_visible(col)
318 }
319
320 pub fn column_position(&self, col: usize) -> Option<usize> {
321 self.column_order.read().position(col)
322 }
323
324 pub fn reset_column_order(&self) {
325 let mut signal = self.column_order;
326 signal.write().reset();
327 }
328}
329
330#[derive(Clone, Copy, PartialEq)]
393pub struct ColumnContext {
394 table_context: TableContextData,
395 column: usize,
396}
397
398impl ColumnContext {
399 pub fn request_sort(&self, sort: SortGesture) {
404 self.table_context.request_sort(self.column, sort);
405 }
406
407 pub fn sort_info(&self) -> Option<SortInfo> {
411 let sorts = self.table_context.sorts.read();
412 sorts
413 .iter()
414 .position(|record| record.column == self.column)
415 .map(|priority| SortInfo {
416 priority,
417 direction: sorts[priority].sort.direction,
418 })
419 }
420
421 pub fn swap_with(&self, other_col: usize) {
425 self.table_context.swap_columns(self.column, other_col);
426 }
427
428 pub fn hide(&self) {
430 self.table_context.hide_column(self.column);
431 }
432
433 pub fn show(&self, at_index: Option<usize>) {
435 self.table_context.show_column(self.column, at_index);
436 }
437
438 pub fn move_to(&self, new_index: usize) {
440 self.table_context.move_column_to(self.column, new_index);
441 }
442
443 pub fn move_forward(&self) {
445 self.table_context.move_column_forward(self.column);
446 }
447
448 pub fn move_backward(&self) {
450 self.table_context.move_column_backward(self.column);
451 }
452
453 pub fn is_visible(&self) -> bool {
455 self.table_context.is_column_visible(self.column)
456 }
457
458 pub fn position(&self) -> Option<usize> {
460 self.table_context.column_position(self.column)
461 }
462
463 pub fn reset_order(&self) {
465 self.table_context.reset_column_order();
466 }
467}
468
469#[derive(Copy, Clone, PartialEq)]
473pub struct HeaderData<C: Columns<R>, R: Row> {
474 pub(crate) context: TableContext<C>,
475 pub(crate) column_index: usize,
476 _phantom: PhantomData<R>,
477}
478
479impl<C: Columns<R>, R: Row> HeaderData<C, R> {
480 pub fn key(&self) -> String {
482 self.context.data.get_column_name(self.column_index)
483 }
484
485 pub fn render(&self, attributes: Vec<Attribute>) -> Element {
487 let binding = self.context.columns.read();
488 let headers = binding.headers();
489 headers[self.column_index](&self.context, attributes)
490 }
491}
492
493#[derive(PartialEq)]
497pub struct TableData<C: Columns<R>, R: Row> {
498 pub context: TableContext<C>,
500 pub rows: ReadSignal<Vec<R>>,
502}
503
504impl<C: Columns<R>, R: Row> Clone for TableData<C, R> {
505 fn clone(&self) -> Self {
506 *self
507 }
508}
509
510impl<C: Columns<R>, R: Row> Copy for TableData<C, R> {}
511
512impl<C: Columns<R>, R: Row> TableData<C, R> {
513 pub fn rows(&self) -> impl Iterator<Item = RowData<C, R>> {
515 self.context.rows(self.rows)
516 }
517}
518
519#[derive(Copy, Clone, PartialEq)]
523pub struct CellData<C: Columns<R>, R: Row> {
524 pub(crate) row: RowData<C, R>,
525 pub(crate) column_index: usize,
526}
527
528impl<C: Columns<R>, R: Row> CellData<C, R> {
529 pub fn key(&self) -> String {
531 self.row.context.data.get_column_name(self.column_index)
532 }
533
534 pub fn render(&self, attributes: Vec<Attribute>) -> Element {
536 let binding = self.row.context.columns.read();
537 let columns = binding.columns();
538 columns[self.column_index](
539 &self.row.context,
540 &self.row.rows.read()[self.row.index],
541 attributes,
542 )
543 }
544}
545
546#[derive(PartialEq)]
550pub struct RowData<C: Columns<R>, R: Row> {
551 pub(crate) context: TableContext<C>,
552 pub(crate) rows: ReadSignal<Vec<R>>,
553 pub(crate) index: usize,
554 pub(crate) _phantom: PhantomData<R>,
555}
556
557impl<C: Columns<R>, R: Row> Copy for RowData<C, R> {}
558
559impl<C: Columns<R>, R: Row> Clone for RowData<C, R> {
560 fn clone(&self) -> Self {
561 *self
562 }
563}
564
565impl<C: Columns<R>, R: Row> RowData<C, R> {
566 pub fn key(&self) -> String {
568 self.rows.read()[self.index].key().into()
569 }
570
571 pub fn cells(self) -> impl Iterator<Item = CellData<C, R>> {
573 self.context.cells(self)
574 }
575}
576
577#[cfg(test)]
578mod tests_sort_request;
579
580#[cfg(test)]
581mod tests_rows_filter_and_sort;