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!("{} 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}