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 all_headers<R>(self) -> impl Iterator<Item = HeaderData<C, R>>
175 where
176 C: Columns<R>,
177 R: Row,
178 {
179 let num_columns = self.data.num_columns();
180 (0..num_columns).map(move |column_index| HeaderData {
181 context: self,
182 column_index,
183 _phantom: PhantomData,
184 })
185 }
186
187 pub fn cells<R>(self, row: RowData<C, R>) -> impl Iterator<Item = CellData<C, R>>
188 where
189 C: Columns<R>,
190 R: Row,
191 {
192 let order = self.get_column_order();
193 let row_copy = row;
194 order.into_iter().map(move |column_index| CellData {
195 row: row_copy,
196 column_index,
197 })
198 }
199
200 pub fn rows<R>(self, rows: ReadSignal<Vec<R>>) -> impl Iterator<Item = RowData<C, R>>
201 where
202 C: Columns<R>,
203 R: Row,
204 {
205 let rows_data = rows.read();
206 let columns = self.columns.read();
207
208 let mut filtered_indices: Vec<usize> = (0..rows_data.len())
210 .filter(|&i| columns.filter(&rows_data[i]))
211 .collect();
212
213 let sort_records = self.data.sorts.read();
215 if !sort_records.is_empty() {
216 let comparators = columns.compare();
217
218 filtered_indices.sort_by(|&a, &b| {
220 for sort_record in sort_records.iter() {
222 let ordering = comparators[sort_record.column](&rows_data[a], &rows_data[b]);
223
224 let directed_ordering = match sort_record.sort.direction {
226 SortDirection::Ascending => ordering,
227 SortDirection::Descending => ordering.reverse(),
228 };
229
230 if directed_ordering != std::cmp::Ordering::Equal {
232 return directed_ordering;
233 }
234 }
236
237 std::cmp::Ordering::Equal
239 });
240 }
241
242 filtered_indices.into_iter().map(move |i| RowData {
244 context: self,
245 rows,
246 index: i,
247 _phantom: PhantomData,
248 })
249 }
250}
251
252impl TableContextData {
253 pub fn column_context(&self, column: usize) -> ColumnContext {
254 ColumnContext {
255 table_context: *self,
256 column,
257 }
258 }
259
260 pub fn get_column_order(&self) -> Vec<usize> {
261 self.column_order.read().get_order().to_vec()
262 }
263
264 pub fn num_columns(&self) -> usize {
265 self.column_names.read().len()
266 }
267
268 pub fn get_column_name(&self, index: usize) -> String {
269 self.column_names.read()[index].clone()
270 }
271
272 pub fn request_sort(&self, column: usize, sort: SortGesture) {
273 match sort {
274 SortGesture::Cancel => {
275 let mut signal = self.sorts;
276 signal.write().retain(|record| record.column != column);
277 }
278 SortGesture::AddFirst(sort) => {
279 let mut signal = self.sorts;
280 let mut write = signal.write();
281 write.retain(|record| record.column != column);
282 write.insert(0, SortRecord { column, sort });
283 }
284 SortGesture::AddLast(sort) => {
285 let mut signal = self.sorts;
286 let mut write = signal.write();
287 write.retain(|record| record.column != column);
288 write.push(SortRecord { column, sort });
289 }
290 SortGesture::Toggle => {
291 let mut signal = self.sorts;
292 if let Some(record) = signal.write().iter_mut().find(|r| r.column == column) {
293 record.sort.direction = match record.sort.direction {
294 SortDirection::Ascending => SortDirection::Descending,
295 SortDirection::Descending => SortDirection::Ascending,
296 };
297 }
298 }
299 }
300 }
301
302 pub fn swap_columns(&self, col_a: usize, col_b: usize) {
305 let mut signal = self.column_order;
306 signal.write().swap(col_a, col_b);
307 }
308
309 pub fn hide_column(&self, col: usize) {
310 let mut signal = self.column_order;
311 signal.write().hide_column(col);
312 }
313
314 pub fn show_column(&self, col: usize, at_index: Option<usize>) {
315 let mut signal = self.column_order;
316 signal.write().show_column(col, at_index);
317 }
318
319 pub fn move_column_to(&self, col: usize, new_index: usize) {
320 let mut signal = self.column_order;
321 signal.write().move_to(col, new_index);
322 }
323
324 pub fn move_column_forward(&self, col: usize) {
325 let mut signal = self.column_order;
326 signal.write().move_forward(col);
327 }
328
329 pub fn move_column_backward(&self, col: usize) {
330 let mut signal = self.column_order;
331 signal.write().move_backward(col);
332 }
333
334 pub fn is_column_visible(&self, col: usize) -> bool {
335 self.column_order.read().is_visible(col)
336 }
337
338 pub fn column_position(&self, col: usize) -> Option<usize> {
339 self.column_order.read().position(col)
340 }
341
342 pub fn reset_column_order(&self) {
343 let mut signal = self.column_order;
344 signal.write().reset();
345 }
346}
347
348#[derive(Clone, Copy, PartialEq)]
411pub struct ColumnContext {
412 table_context: TableContextData,
413 column: usize,
414}
415
416impl ColumnContext {
417 pub fn request_sort(&self, sort: SortGesture) {
422 self.table_context.request_sort(self.column, sort);
423 }
424
425 pub fn sort_info(&self) -> Option<SortInfo> {
429 let sorts = self.table_context.sorts.read();
430 sorts
431 .iter()
432 .position(|record| record.column == self.column)
433 .map(|priority| SortInfo {
434 priority,
435 direction: sorts[priority].sort.direction,
436 })
437 }
438
439 pub fn swap_with(&self, other_col: usize) {
443 self.table_context.swap_columns(self.column, other_col);
444 }
445
446 pub fn hide(&self) {
448 self.table_context.hide_column(self.column);
449 }
450
451 pub fn show(&self, at_index: Option<usize>) {
453 self.table_context.show_column(self.column, at_index);
454 }
455
456 pub fn move_to(&self, new_index: usize) {
458 self.table_context.move_column_to(self.column, new_index);
459 }
460
461 pub fn move_forward(&self) {
463 self.table_context.move_column_forward(self.column);
464 }
465
466 pub fn move_backward(&self) {
468 self.table_context.move_column_backward(self.column);
469 }
470
471 pub fn is_visible(&self) -> bool {
473 self.table_context.is_column_visible(self.column)
474 }
475
476 pub fn position(&self) -> Option<usize> {
478 self.table_context.column_position(self.column)
479 }
480
481 pub fn reset_order(&self) {
483 self.table_context.reset_column_order();
484 }
485}
486
487#[derive(Copy, Clone, PartialEq)]
491pub struct HeaderData<C: Columns<R>, R: Row> {
492 pub(crate) context: TableContext<C>,
493 pub(crate) column_index: usize,
494 _phantom: PhantomData<R>,
495}
496
497impl<C: Columns<R>, R: Row> HeaderData<C, R> {
498 pub fn key(&self) -> String {
500 self.context.data.get_column_name(self.column_index)
501 }
502
503 pub fn column_context(&self) -> ColumnContext {
505 self.context.data.column_context(self.column_index)
506 }
507
508 pub fn render(&self, attributes: Vec<Attribute>) -> Element {
510 let binding = self.context.columns.read();
511 let headers = binding.headers();
512 headers[self.column_index](&self.context, attributes)
513 }
514}
515
516#[derive(PartialEq)]
520pub struct TableData<C: Columns<R>, R: Row> {
521 pub context: TableContext<C>,
523 pub rows: ReadSignal<Vec<R>>,
525}
526
527impl<C: Columns<R>, R: Row> Clone for TableData<C, R> {
528 fn clone(&self) -> Self {
529 *self
530 }
531}
532
533impl<C: Columns<R>, R: Row> Copy for TableData<C, R> {}
534
535impl<C: Columns<R>, R: Row> TableData<C, R> {
536 pub fn rows(&self) -> impl Iterator<Item = RowData<C, R>> {
538 self.context.rows(self.rows)
539 }
540}
541
542#[derive(Copy, Clone, PartialEq)]
546pub struct CellData<C: Columns<R>, R: Row> {
547 pub(crate) row: RowData<C, R>,
548 pub(crate) column_index: usize,
549}
550
551impl<C: Columns<R>, R: Row> CellData<C, R> {
552 pub fn key(&self) -> String {
554 self.row.context.data.get_column_name(self.column_index)
555 }
556
557 pub fn render(&self, attributes: Vec<Attribute>) -> Element {
559 let binding = self.row.context.columns.read();
560 let columns = binding.columns();
561 columns[self.column_index](
562 &self.row.context,
563 &self.row.rows.read()[self.row.index],
564 attributes,
565 )
566 }
567}
568
569#[derive(PartialEq)]
573pub struct RowData<C: Columns<R>, R: Row> {
574 pub(crate) context: TableContext<C>,
575 pub(crate) rows: ReadSignal<Vec<R>>,
576 pub(crate) index: usize,
577 pub(crate) _phantom: PhantomData<R>,
578}
579
580impl<C: Columns<R>, R: Row> Copy for RowData<C, R> {}
581
582impl<C: Columns<R>, R: Row> Clone for RowData<C, R> {
583 fn clone(&self) -> Self {
584 *self
585 }
586}
587
588impl<C: Columns<R>, R: Row> RowData<C, R> {
589 pub fn key(&self) -> String {
591 self.rows.read()[self.index].key().into()
592 }
593
594 pub fn cells(self) -> impl Iterator<Item = CellData<C, R>> {
596 self.context.cells(self)
597 }
598
599 pub fn data(
601 &self,
602 ) -> impl Readable<Target = R> + Copy + std::ops::Deref<Target = dyn Fn() -> R> + 'static {
603 let index = self.index;
604 self.rows.map(move |rows| &rows[index])
605 }
606}
607
608#[cfg(test)]
609mod tests_sort_request;
610
611#[cfg(test)]
612mod tests_rows_filter_and_sort;
613
614#[cfg(test)]
615mod tests_column_context;