patternfly_yew/components/card/header/
mod.rs1use yew::prelude::*;
2
3use super::CardContext;
4use crate::prelude::*;
5
6mod main;
7mod selectable;
8
9pub use main::*;
10pub use selectable::*;
11
12#[derive(Debug, Clone, PartialEq, Properties)]
14pub struct CardHeaderActionsObject {
15 #[prop_or_default]
17 pub actions: Html,
18 #[prop_or_default]
21 pub has_no_offset: bool,
22 #[prop_or_default]
24 pub class: Classes,
25}
26
27#[derive(Debug, Clone, PartialEq, Properties)]
29pub struct CardHeaderProperties {
30 #[prop_or_default]
32 pub children: OptionalHtml,
33 #[prop_or_default]
35 pub class: Classes,
36 #[prop_or_default]
38 pub actions: Option<CardHeaderActionsObject>,
39 #[prop_or_default]
41 pub selectable_actions: Option<CardSelectableActionsObjectProperties>,
42 #[prop_or_default]
44 pub id: String,
45 #[prop_or_default]
47 pub onexpand: Option<Callback<AttrValue>>,
48 #[prop_or_default]
50 pub toggle_right_aligned: bool,
51}
52
53#[function_component(CardHeader)]
54pub fn header(props: &CardHeaderProperties) -> Html {
55 let context = use_context::<CardContext>().expect("Couldn't find card context");
56
57 let is_clickable_xor_selectable = context.clickable != context.selectable;
58 let has_actions = props.actions.as_ref().is_some();
59 if has_actions && is_clickable_xor_selectable {
60 log::warn!(
61 "{} only cards should not contain any other actions. If you wish to include additional actions, use a clickable and selectable card",
62 if context.clickable {
63 "Clickable"
64 } else {
65 "Selectable"
66 }
67 );
68 }
69
70 let mut class = classes!(props.class.clone());
71 if props.toggle_right_aligned {
72 class.push("pf-m-toggle-right");
73 }
74 class.push("pf-v6-c-card__header");
75
76 let selectable_actions = if context.clickable || context.selectable {
77 props.selectable_actions.as_ref()
78 } else {
79 None
80 };
81 html! {
82 <div {class} id={props.id.clone()}>
83 if !props.toggle_right_aligned {
84 <ExpandToggle onexpand={props.onexpand.clone()} />
85 }
86 if props.actions.is_some() || selectable_actions.is_some() {
87 <CardActions
88 class={props.actions.as_ref().map(|a| a.class.clone()).unwrap_or_default()}
89 has_no_offset={props.actions.as_ref().map(|a| a.has_no_offset).unwrap_or_default()}
90 >
91 if let Some(actions) = &props.actions {
92 { actions.actions.clone() }
93 }
94 if let Some(selectable_actions) = selectable_actions {
95 <CardSelectableActions class={selectable_actions.base.class.clone()}>
96 <CardSelectableActionsObject ..selectable_actions.clone() />
97 </CardSelectableActions>
98 }
99 </CardActions>
100 }
101 if let Some(children) = props.children.as_ref() {
102 <CardHeaderMain>{ children.clone() }</CardHeaderMain>
103 }
104 if props.toggle_right_aligned {
105 <ExpandToggle onexpand={props.onexpand.clone()} />
106 }
107 </div>
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Properties)]
112struct ExpandToggleProperties {
113 onexpand: Option<Callback<AttrValue>>,
114}
115
116#[function_component(ExpandToggle)]
117fn expand_toggle(props: &ExpandToggleProperties) -> Html {
118 let CardContext { card_id, .. } = use_context().expect("Couldn't get card context");
119 let onclick = use_callback(
120 (card_id.clone(), props.onexpand.clone()),
121 |_, (id, onexpand)| {
122 if let Some(f) = onexpand {
123 f.emit(id.clone())
124 }
125 },
126 );
127 if props.onexpand.is_none() {
128 return html!();
129 }
130 html! {
131 <div class="pf-v6-c-card__header-toggle">
132 <Button
133 variant={ButtonVariant::Plain}
134 r#type={ButtonType::Button}
135 aria_label="Details"
136 {onclick}
137 >
138 <span class="pf-v6-c-card__header-toggle-icon">{ Icon::AngleRight }</span>
139 </Button>
140 </div>
141 }
142}