Skip to main content

patternfly_yew/components/card/header/
mod.rs

1use yew::prelude::*;
2
3use super::CardContext;
4use crate::prelude::*;
5
6mod main;
7mod selectable;
8
9pub use main::*;
10pub use selectable::*;
11
12/// Actions that can be performed by the card.
13#[derive(Debug, Clone, PartialEq, Properties)]
14pub struct CardHeaderActionsObject {
15    /// The actual actions.
16    #[prop_or_default]
17    pub actions: Html,
18    /// Remove the offset of the position of the actions to the header content.
19    /// This looks better if using large card titles or tall header images, for example.
20    #[prop_or_default]
21    pub has_no_offset: bool,
22    /// Additional classes to the actions object.
23    #[prop_or_default]
24    pub class: Classes,
25}
26
27/// Properties of the CardHeader object.
28#[derive(Debug, Clone, PartialEq, Properties)]
29pub struct CardHeaderProperties {
30    /// Content inside the header.
31    #[prop_or_default]
32    pub children: OptionalHtml,
33    /// Additional classes to the object.
34    #[prop_or_default]
35    pub class: Classes,
36    /// Actions that can be performed by the card.
37    #[prop_or_default]
38    pub actions: Option<CardHeaderActionsObject>,
39    /// Actions that select the card or occur when clicking the card.
40    #[prop_or_default]
41    pub selectable_actions: Option<CardSelectableActionsObjectProperties>,
42    /// Html id
43    #[prop_or_default]
44    pub id: String,
45    /// Callback to run if a user clicks the expand toggle.
46    #[prop_or_default]
47    pub onexpand: Option<Callback<AttrValue>>,
48    /// Sets whether the expand toggle should be on the right or the left of the card.
49    #[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">
139                    { Icon::AngleRight }
140                </span>
141            </Button>
142        </div>
143    }
144}