patternfly_yew/components/table/
composable.rs1use crate::prelude::{Button, ButtonVariant};
2use yew::prelude::*;
3
4use super::*;
5
6const OUIA: Ouia = ouia!("Table");
7
8#[derive(Debug, Clone, PartialEq, Properties)]
10pub struct ComposableTableProperties {
11 #[prop_or_default]
12 pub class: Classes,
13 #[prop_or_default]
14 pub children: Html,
15 #[prop_or_default]
16 pub mode: TableMode,
17 #[prop_or_default]
18 pub sticky_header: bool,
19 #[prop_or_default]
20 pub grid: Option<TableGridMode>,
21 #[prop_or(true)]
22 pub borders: bool,
23 #[prop_or_default]
24 pub id: AttrValue,
25 #[prop_or_default]
27 pub ouia_id: Option<String>,
28 #[prop_or(OUIA.component_type())]
30 pub ouia_type: OuiaComponentType,
31 #[prop_or(OuiaSafe::TRUE)]
33 pub ouia_safe: OuiaSafe,
34}
35
36#[function_component(ComposableTable)]
46pub fn composable_table(props: &ComposableTableProperties) -> Html {
47 let ouia_id = use_memo(props.ouia_id.clone(), |id| {
48 id.clone().unwrap_or(OUIA.generated_id())
49 });
50 let mut class = classes!("pf-v6-c-table", props.class.clone());
51 if props.sticky_header {
52 class.push(classes!("pf-m-sticky-header"));
53 }
54 class.extend_from(&props.grid);
55
56 if !props.borders {
57 class.push(classes!("pf-m-no-border-rows"));
58 }
59
60 match props.mode {
61 TableMode::Compact => {
62 class.push(classes!("pf-m-compact"));
63 }
64 TableMode::CompactNoBorders => {
65 class.push(classes!("pf-m-compact", "pf-m-no-border-rows"));
66 }
67 TableMode::CompactExpandable => {
68 class.push(classes!("pf-m-compact"));
69 }
70 TableMode::Expandable => {
71 class.push(classes!("pf-m-expandable"));
72 }
73 TableMode::Default => {}
74 }
75
76 html! {
77 <table
78 id={&props.id}
79 {class}
80 role="grid"
81 data-ouia-component-id={(*ouia_id).clone()}
82 data-ouia-component-type={props.ouia_type}
83 data-ouia-safe={props.ouia_safe}
84 >
85 { props.children.clone() }
86 </table>
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Properties)]
91pub struct CaptionProperties {
92 #[prop_or_default]
93 pub children: Html,
94}
95
96#[function_component(Caption)]
97pub fn caption(props: &CaptionProperties) -> Html {
98 html! { <caption class="pf-v6-c-table__caption">{ props.children.clone() }</caption> }
99}
100
101#[derive(Debug, Clone, PartialEq, Properties)]
102pub struct TableBodyProperties {
103 #[prop_or_default]
104 pub class: Classes,
105 #[prop_or_default]
106 pub children: Html,
107 #[prop_or_default]
108 pub expanded: bool,
109}
110
111#[function_component(TableBody)]
112pub fn table_body(props: &TableBodyProperties) -> Html {
113 let mut class = classes!("pf-v6-c-table__tbody", props.class.clone());
114 if props.expanded {
115 class.push("pf-m-expanded");
116 }
117 html! { <tbody {class} role="rowgroup">{ props.children.clone() }</tbody> }
118}
119
120#[derive(Debug, Clone, PartialEq, Properties)]
121pub struct TableRowProperties {
122 #[prop_or_default]
123 pub class: Classes,
124 #[prop_or_default]
125 pub children: Html,
126 #[prop_or_default]
127 pub onclick: Option<Callback<MouseEvent>>,
128 #[prop_or_default]
129 pub selected: bool,
130 #[prop_or_default]
131 pub expandable: bool,
132 #[prop_or_default]
133 pub expanded: bool,
134 #[prop_or_default]
135 pub control_row: bool,
136}
137
138#[function_component(TableRow)]
139pub fn table_row(props: &TableRowProperties) -> Html {
140 let mut class = classes!("pf-v6-c-table__tr", props.class.clone());
141 if props.onclick.is_some() {
142 class.push("pf-m-clickable");
143 }
144 if props.selected {
145 class.push("pf-m-selected");
146 }
147 if props.expanded {
148 class.push("pf-m-expanded");
149 }
150 if props.expandable {
151 class.push("pf-v6-c-table__expandable-row");
152 }
153 if props.control_row {
154 class.push("pf-v6-c-table__control-row");
155 }
156 html! {
157 <tr class={class.clone()} role="row" onclick={props.onclick.clone()}>
158 { props.children.clone() }
159 </tr>
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Properties)]
164pub struct ExpandableRowContentProperties {
165 #[prop_or_default]
166 pub children: Html,
167 #[prop_or_default]
168 pub class: Classes,
169}
170
171#[function_component(ExpandableRowContent)]
172pub fn expandable_row_content(props: &ExpandableRowContentProperties) -> Html {
173 let class = classes!("pf-v6-c-table__expandable-row-content", props.class.clone());
174 html! { <div {class}>{ props.children.clone() }</div> }
175}
176
177#[derive(Debug, Clone, PartialEq)]
178pub struct ExpandParams {
179 pub r#type: ExpandType,
180 pub expanded: bool,
181 pub ontoggle: Callback<()>,
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub enum ExpandType {
186 Row,
187 Column,
188}
189
190#[derive(Debug, Clone, PartialEq, Properties)]
191pub struct TableDataProperties {
192 #[prop_or_default]
193 pub class: Classes,
194 #[prop_or_default]
195 pub children: Html,
196 #[prop_or_default]
197 pub center: bool,
198 #[prop_or_default]
199 pub text_modifier: Option<TextModifier>,
200 #[prop_or_default]
201 pub expandable: Option<ExpandParams>,
202 #[prop_or_default]
203 pub data_label: Option<AttrValue>,
204 #[prop_or_default]
205 pub span_modifiers: Vec<SpanModifiers>,
206 #[prop_or_default]
207 pub colspan: Option<usize>,
208 #[prop_or_default]
209 pub action: bool,
210}
211
212#[function_component(TableData)]
213pub fn table_data(props: &TableDataProperties) -> Html {
214 let mut class = classes!("pf-v6-c-table__td", props.class.clone());
215 if props.center {
216 class.push(classes!("pf-m-center"))
217 }
218 if props.action {
219 class.push("pf-v6-c-table__action");
220 }
221 class.extend_from(&props.text_modifier);
222 class.extend_from(&props.span_modifiers);
223
224 let mut content = props.children.clone();
225 if let Some(expandable) = props.expandable.as_ref() {
226 let onclick = {
227 let ontoggle = expandable.ontoggle.clone();
228 Callback::from(move |_| ontoggle.emit(()))
229 };
230 class.push(match expandable.r#type {
231 ExpandType::Column => "pf-v6-c-table__compound-expansion-toggle",
232 ExpandType::Row => "pf-v6-c-table__toggle",
233 });
234 content = match expandable.r#type {
235 ExpandType::Column => {
236 if expandable.expanded {
237 class.push("pf-m-expanded");
238 }
239 html! {
240 <button class="pf-v6-c-table__button" {onclick}>
241 <span class="pf-v6-c-table__text">{ content }</span>
242 </button>
243 }
244 }
245 ExpandType::Row => {
246 let mut button_class = classes!();
247 if expandable.expanded {
248 button_class.push("pf-m-expanded");
249 }
250 html! {
251 <Button
252 variant={ButtonVariant::Plain}
253 class={button_class}
254 {onclick}
255 aria_expanded={expandable.expanded.to_string()}
256 >
257 <div class="pf-v6-c-table__toggle-icon">{ Icon::AngleDown }</div>
258 </Button>
259 }
260 }
261 };
262 }
263
264 let colspan = props.colspan.as_ref().map(|cols| cols.to_string());
265 html! {
266 <td {class} role="cell" data-label={props.data_label.clone()} {colspan}>{ content }</td>
267 }
268}