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!("{} only cards should not contain any other actions. If you wish to include additional actions, use a clickable and selectable card", if context.clickable { "Clickable" } else { "Selectable" });
61 }
62
63 let mut class = classes!(props.class.clone());
64 if props.toggle_right_aligned {
65 class.push("pf-m-toggle-right");
66 }
67 class.push("pf-v5-c-card__header");
68
69 let selectable_actions = if context.clickable || context.selectable {
70 props.selectable_actions.as_ref()
71 } else {
72 None
73 };
74 html! {
75 <div {class} id={props.id.clone()}>
76 if !props.toggle_right_aligned {
77 <ExpandToggle onexpand={props.onexpand.clone()} />
78 }
79 if props.actions.is_some() || selectable_actions.is_some() {
80 <CardActions
81 class={props.actions.as_ref().map(|a| a.class.clone()).unwrap_or_default()}
82 has_no_offset={props.actions.as_ref().map(|a| a.has_no_offset).unwrap_or_default()}
83 >
84 if let Some(actions) = &props.actions {
85 {actions.actions.clone()}
86 }
87 if let Some(selectable_actions) = selectable_actions {
88 <CardSelectableActions class={selectable_actions.base.class.clone()}>
89 <CardSelectableActionsObject ..selectable_actions.clone() />
90 </CardSelectableActions>
91 }
92 </CardActions>
93 }
94 if let Some(children) = props.children.as_ref() {
95 <CardHeaderMain>{children.clone()}</CardHeaderMain>
96 }
97 if props.toggle_right_aligned {
98 <ExpandToggle onexpand={props.onexpand.clone()} />
99 }
100 </div>
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Properties)]
105struct ExpandToggleProperties {
106 onexpand: Option<Callback<AttrValue>>,
107}
108
109#[function_component(ExpandToggle)]
110fn expand_toggle(props: &ExpandToggleProperties) -> Html {
111 let CardContext { card_id, .. } = use_context().expect("Couldn't get card context");
112 let onclick = use_callback(
113 (card_id.clone(), props.onexpand.clone()),
114 |_, (id, onexpand)| {
115 if let Some(f) = onexpand {
116 f.emit(id.clone())
117 }
118 },
119 );
120 if props.onexpand.is_none() {
121 return html!();
122 }
123 html! {
124 <div class="pf-v5-c-card__header-toggle">
125 <Button
126 variant={ ButtonVariant::Plain }
127 r#type={ ButtonType::Button }
128 aria_label="Details"
129 { onclick }
130 >
131 <span class="pf-v5-c-card__header-toggle-icon">
132 { Icon::AngleRight }
133 </span>
134 </Button>
135 </div>
136 }
137}