1use yew::prelude::*;
2use yew::html;
3use std::cmp::Reverse;
4use crate::table::*;
5
6#[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}