yew_table/
component.rs

1use yew::prelude::*;
2use yew::html;
3use std::cmp::Reverse;
4use crate::table::*;
5
6/// Properties of the Table component.
7#[derive(Clone, PartialEq, Default)]
8pub struct TableProps<T> where T: TableData {
9    pub columns: Vec<Column>,
10    pub data: Vec<T>,
11    pub options: Option<TableOptions>,
12}
13
14#[derive(Debug)]
15pub enum Msg {
16    SortColumn(usize),
17}
18
19impl<T> Component for Table<T> where T: TableData {
20    type Message = Msg;
21    type Properties = TableProps<T>;
22
23    fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
24        let column_number = props.columns.len();
25        Table {
26            columns: props.columns,
27            data: props.data,
28            options: props.options,
29            state: TableState {
30                order: vec![TableOrder::default(); column_number],
31            },
32        }
33    }
34
35    fn update(&mut self, msg: Self::Message) -> ShouldRender {
36        match msg {
37            Msg::SortColumn(i) => {
38                use TableOrder::*;
39
40                for (j, x) in self.state.order.iter_mut().enumerate() {
41                    if j != i {
42                        *x = Unordered
43                    } else {
44                        *x = x.rotate()
45                    }
46                }
47                
48                match self.columns[i].data_property.as_ref() {
49                    Some(f) => {
50                        match self.state.order[i] {
51                            Unordered => self.data.sort(),
52                            Ascending => self.data.sort_by_cached_key(|x| x.get_field_as_value(&f).unwrap()),
53                            Descending => self.data.sort_by_cached_key(|x| Reverse(x.get_field_as_value(&f).unwrap())),
54                        }
55                        true
56                    },
57                    None => false
58                }
59            },
60        }
61    }
62
63    fn change(&mut self, props: Self::Properties) -> ShouldRender {
64        self.columns = props.columns;
65        self.data = props.data;
66        true
67    }
68}
69
70impl<T> Renderable<Table<T>> for Table<T> where T: TableData {
71    fn view(&self) -> Html<Self> {
72        let get_orderable_class = || {
73            if self.is_orderable() {
74                "is-orderable"
75            } else {
76                ""
77            }
78        }; 
79
80        html! {
81            <table class=("yew-table", get_orderable_class()),>
82                <thead>
83                    { for self.columns.iter().enumerate().map(|(i, col)| self.view_column(i, &col)) }
84                </thead>
85                <tbody>
86                    { for self.data.iter().map(|d| self.view_row(&d)) }
87                </tbody>
88            </table>
89        }
90    }
91}
92
93impl<T> Table<T> where T: TableData {
94    fn view_column<'a>(&'a self, index: usize, column: &'a Column) -> Html<Table<T>> {
95        let get_header_sorting_class = |index: usize| {
96            use TableOrder::*;
97            match self.state.order[index] {
98                Unordered => "",
99                Ascending => "is-sorting-ascending",
100                Descending => "is-sorting-descending",
101            }
102        };
103
104        let th_view = |child| {
105            if self.is_orderable() {
106                html! { <th onclick=|_| Msg::SortColumn(index),>{ child }</th> }
107            } else {
108                html! { <th>{ child }</th> }
109            }
110        };
111
112        th_view(html! {
113            <span>
114                <abbr title=&column.name,>
115                    { column }
116                </abbr><i class=("sorting-control", get_header_sorting_class(index)),></i>
117            </span>
118        })
119    }
120
121    fn view_row(&self, datum: &T) -> Html<Table<T>> {
122        html! {
123            <tr>
124                { 
125                    for self.columns.iter()
126                        .map(|c| { c.data_property.as_ref().unwrap_or(&c.name) })
127                        .map(|name| { datum.get_field_as_html(name) })
128                        .filter_map(|h| h.ok())
129                        .map(|el| html! { <td>{ el }</td> })
130                }
131            </tr>
132        }
133    }
134}