patternfly_yew/components/
chip.rs

1//! Chip
2use crate::ouia;
3use crate::prelude::{Button, ButtonVariant, Icon, OuiaComponentType};
4use crate::utils::{Ouia, OuiaSafe};
5use std::fmt::Debug;
6use yew::prelude::*;
7
8const OUIA: Ouia = ouia!("Chip");
9
10/// Properties for [`Chip`]
11#[derive(Clone, Debug, PartialEq, Properties)]
12pub struct ChipProperties {
13    #[prop_or_default]
14    pub text: String,
15    #[prop_or_default]
16    pub badge: Option<String>,
17    #[prop_or_default]
18    pub overflow: bool,
19    #[prop_or_default]
20    pub draggable: bool,
21    #[prop_or_default]
22    pub onclose: Option<Callback<()>>,
23    #[prop_or_default]
24    pub icon: Option<Icon>,
25
26    /// OUIA Component id
27    #[prop_or_default]
28    pub ouia_id: Option<String>,
29    /// OUIA Component Type
30    #[prop_or(OUIA.component_type())]
31    pub ouia_type: OuiaComponentType,
32    /// OUIA Component Safe
33    #[prop_or(OuiaSafe::TRUE)]
34    pub ouia_safe: OuiaSafe,
35}
36
37/// Chip component
38///
39/// > A **chip** is used to communicate a value or a set of attribute-value pairs within workflows that involve filtering a set of objects.
40///
41/// See: <https://www.patternfly.org/components/chip>
42///
43/// ## Properties
44///
45/// Defined by [`ChipProperties`].
46#[function_component(Chip)]
47pub fn chip(props: &ChipProperties) -> Html {
48    let ouia_id = use_memo(props.ouia_id.clone(), |id| {
49        id.clone().unwrap_or(OUIA.generated_id())
50    });
51    let mut classes = Classes::from("pf-v5-c-chip");
52
53    if props.draggable {
54        classes.push("pf-m-draggable");
55    }
56
57    // this is only used in the chip group component
58    if props.overflow {
59        classes.push("pf-m-overflow");
60    }
61
62    let body = html! {
63        <>
64            <span class="pf-v5-c-chip__content">
65                { render_icon(props) }
66                <span class="pf-v5-c-chip__text">{props.text.clone()}</span>
67                { render_badge(props) }
68                { render_close(props) }
69            </span>
70        </>
71    };
72    let component = if props.overflow { "button" } else { "div" };
73
74    html! {
75        <@{component}
76            class={classes}
77            data-ouia-component-id={(*ouia_id).clone()}
78            data-ouia-component-type={props.ouia_type}
79            data-ouia-safe={props.ouia_safe}
80        >
81            {body}
82        </@>
83    }
84}
85
86fn render_icon(props: &ChipProperties) -> Html {
87    html! (
88        if let Some(icon) = &props.icon {
89            <span class="pf-v5-c-chip__icon"> { icon.as_html() } </span>
90        }
91    )
92}
93
94fn render_badge(props: &ChipProperties) -> Html {
95    html! (
96        if let Some(badge) = &props.badge {
97            <span class="pf-v5-c-badge pf-m-read"> {badge} </span>
98        }
99    )
100}
101
102fn render_close(props: &ChipProperties) -> Html {
103    html! (
104        if let Some(onclose) = &props.onclose {
105            <span class="pf-v5-c-chip__actions">
106                <Button variant={ButtonVariant::Plain} icon={Icon::Times} onclick={onclose.reform(|e: MouseEvent| {
107                    // This should work, but right now doesn't due to yewstack/yew#3041
108                    e.stop_propagation();
109                })} />
110            </span>
111        }
112    )
113}