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-v6-c-table__th");
89
90 if props.first_tree_column {
91 class.push(classes!("pf-v6-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-v6-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 />),
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-v6-c-table__button"
144 onclick={{
145 let onsort_context = header_context.onsort.clone();
147 let onsort = onsort.clone();
148
149 let index = props.index.clone();
150 let order = sort_by_next_status.1;
151
152 Callback::from(move |_| {
153 let sort_by = TableHeaderSortBy {
154 index: index.clone(),
155 order: !order
156 };
157 onsort_context.emit(sort_by.clone());
158 onsort.emit(sort_by.clone());
159 })
160 }}
161 >
162 <div class="pf-v6-c-table__button-content">
163 <span class="pf-v6-c-table__text">{ label }</span>
164 <span class="pf-v6-c-table__sort-indicator">
165 { sort_by_next_status.0 }
166 </span>
167 </div>
168 </button>
169 )
170 } else {
171 html_nested!({ label.into() })
172 };
173
174 html!(
175 <th title={label.clone()} {class} scope="col" role="columnheader">
176 { th_content }
177 </th>
178 )
179 }
180 }
181}
182
183#[cfg(test)]
184mod test {
185 use super::*;
186
187 #[test]
188 fn test_round() {
189 assert_eq!(round(0), 10);
190 assert_eq!(round(10), 10);
191 assert_eq!(round(50), 50);
192 assert_eq!(round(54), 50);
193 assert_eq!(round(55), 60);
194 assert_eq!(round(56), 60);
195 assert_eq!(round(100), 90);
196 assert_eq!(round(100), 90);
197 assert_eq!(round(200), 90);
198 }
199}