patternfly_yew/components/table/
column.rs1use crate::prelude::{
2 AsClasses, ExtendClasses, Icon, Order, TableHeaderContext, TableHeaderSortBy, TextModifier,
3};
4use std::fmt::Debug;
5use yew::prelude::*;
6
7#[derive(Clone, Debug, PartialEq, Properties)]
9pub struct TableColumnProperties<C>
10where
11 C: Clone + Eq + 'static,
12{
13 pub index: C,
15 #[prop_or_default]
16 pub label: Option<String>,
17 #[prop_or_default]
18 pub center: bool,
19 #[prop_or_default]
20 pub width: ColumnWidth,
21 #[prop_or_default]
22 pub text_modifier: Option<TextModifier>,
23 #[prop_or_default]
24 pub expandable: bool,
25
26 #[doc(hidden)]
27 #[prop_or_default]
28 pub(crate) first_tree_column: bool,
29
30 #[prop_or_default]
32 pub sortby: Option<TableHeaderSortBy<C>>,
33
34 #[prop_or_default]
35 pub onsort: Option<Callback<TableHeaderSortBy<C>>>,
36}
37
38#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
39pub enum ColumnWidth {
40 #[default]
41 Default,
42 Percent(u16),
47 WidthMax,
49 FitContent,
51}
52
53fn round(p: u16) -> u16 {
54 if p <= 10 {
55 return 10;
56 }
57 if p >= 90 {
58 return 90;
59 }
60
61 ((p + 5) / 10) * 10
63}
64
65impl AsClasses for ColumnWidth {
66 fn extend_classes(&self, classes: &mut Classes) {
67 match self {
68 Self::Default => {}
69 Self::Percent(p) => classes.push(classes!(format!("pf-m-width-{}", round(*p)))),
70 Self::WidthMax => classes.push(classes!("pf-m-width-max")),
71 Self::FitContent => classes.push(classes!("pf-m-fit-content")),
72 }
73 }
74}
75
76#[function_component(TableColumn)]
82pub fn table_column<K>(props: &TableColumnProperties<K>) -> Html
83where
84 K: Clone + Eq + 'static,
85{
86 let table_header_context = use_context::<TableHeaderContext<K>>();
87
88 let mut class = classes!("pf-v5-c-table__th");
89
90 if props.first_tree_column {
91 class.push(classes!("pf-v5-c-table__tree-view-title-header-cell"));
92 }
93
94 if props.center {
95 class.push(classes!("pf-m-center"));
96 }
97
98 if props.onsort.is_some() {
99 class.push(classes!("pf-v5-c-table__sort"));
100 }
101
102 class.extend_from(&props.width);
103 class.extend_from(&props.text_modifier);
104
105 match &props.label {
106 None => html! (<th></th>),
107 Some(label) => {
108 let th_content = if let Some(onsort) = &props.onsort {
109 let header_context = table_header_context.expect(
110 "Column must be inside TableHeader, the expected context is defined there",
111 );
112
113 let sortby = if props.sortby.is_some() {
115 &props.sortby
116 } else {
117 &header_context.sortby
118 };
119
120 let sort_by_next_status = match sortby {
121 Some(val) => {
122 if val.index == props.index {
123 class.push(classes!("pf-m-selected"));
124 }
125
126 if val.index == props.index {
127 let icon = match val.order {
128 Order::Ascending => Icon::LongArrowAltUp,
129 Order::Descending => Icon::LongArrowAltDown,
130 };
131 (icon, val.order)
132 } else {
133 (Icon::ArrowsAltV, Order::Descending)
134 }
135 }
136 None => (Icon::ArrowsAltV, Order::Descending),
137 };
138
139 html_nested!(
140 <button
141 title={label.clone()}
142 type="button"
143 class="pf-v5-c-table__button"
144 onclick={
145 {
146 let onsort_context = header_context.onsort.clone();
148 let onsort = onsort.clone();
149
150 let index = props.index.clone();
151 let order = sort_by_next_status.1;
152
153 Callback::from(move |_| {
154 let sort_by = TableHeaderSortBy {
155 index: index.clone(),
156 order: !order
157 };
158 onsort_context.emit(sort_by.clone());
159 onsort.emit(sort_by.clone());
160 })
161 }
162 }
163 >
164 <div class="pf-v5-c-table__button-content">
165 <span class="pf-v5-c-table__text">{ &label }</span>
166 <span class="pf-v5-c-table__sort-indicator">
167 {sort_by_next_status.0}
168 </span>
169 </div>
170 </button>
171 )
172 } else {
173 html_nested!(
174 <>{ &label }</>
175 )
176 };
177
178 html!(
179 <th title={label.clone()} {class} scope="col" role="columnheader">
180 {th_content}
181 </th>
182 )
183 }
184 }
185}
186
187#[cfg(test)]
188mod test {
189 use super::*;
190
191 #[test]
192 fn test_round() {
193 assert_eq!(round(0), 10);
194 assert_eq!(round(10), 10);
195 assert_eq!(round(50), 50);
196 assert_eq!(round(54), 50);
197 assert_eq!(round(55), 60);
198 assert_eq!(round(56), 60);
199 assert_eq!(round(100), 90);
200 assert_eq!(round(100), 90);
201 assert_eq!(round(200), 90);
202 }
203}