Skip to main content

patternfly_yew/components/
list.rs

1//! List
2use crate::icon::Icon;
3use crate::prelude::{AsClasses, ExtendClasses};
4use crate::utils::Raw;
5use yew::html::ChildrenRenderer;
6use yew::virtual_dom::VChild;
7use yew::{html::IntoPropValue, prelude::*, virtual_dom::AttrValue};
8
9#[derive(Copy, Clone, Default, PartialEq, Eq)]
10pub enum ListType {
11    #[default]
12    Basic,
13    Inline,
14    Ordered(ListOrder),
15    Plain,
16    Bordered,
17}
18
19impl AsClasses for ListType {
20    fn extend_classes(&self, classes: &mut Classes) {
21        match self {
22            ListType::Inline => {
23                classes.push(classes!("pf-m-inline"));
24            }
25            ListType::Plain => {
26                classes.push(classes!("pf-m-plain"));
27            }
28            ListType::Bordered => {
29                classes.push(classes!("pf-m-plain", "pf-m-bordered"));
30            }
31            _ => {}
32        }
33    }
34}
35
36#[derive(Copy, Clone, Default, PartialEq, Eq)]
37pub enum ListOrder {
38    #[default]
39    Number,
40    LowercaseLetter,
41    UppercaseLetter,
42    LowercaseRomanNumber,
43    UppercaseRomanNumber,
44}
45
46impl IntoPropValue<Option<AttrValue>> for ListOrder {
47    fn into_prop_value(self) -> Option<AttrValue> {
48        Some(AttrValue::Static(match self {
49            Self::Number => "1",
50            Self::LowercaseLetter => "a",
51            Self::UppercaseLetter => "A",
52            Self::LowercaseRomanNumber => "i",
53            Self::UppercaseRomanNumber => "I",
54        }))
55    }
56}
57
58/// Properties for [`List`]
59#[derive(PartialEq, Properties)]
60pub struct ListProperties {
61    #[prop_or_default]
62    pub children: ChildrenRenderer<ListChildVariant>,
63    #[prop_or_default]
64    pub r#type: ListType,
65    #[prop_or_default]
66    pub icon_size: ListIconSize,
67}
68
69#[derive(Clone, Copy, Default, Eq, PartialEq, Debug)]
70pub enum ListIconSize {
71    #[default]
72    Default,
73    Large,
74}
75
76impl AsClasses for ListIconSize {
77    fn extend_classes(&self, classes: &mut Classes) {
78        match self {
79            Self::Default => {}
80            Self::Large => classes.extend(classes!("pf-m-icon-lg")),
81        }
82    }
83}
84
85/// List component
86///
87/// > A **list** component embeds a formatted list (bulleted or numbered list) into page content.
88///
89/// See: <https://www.patternfly.org/components/list>
90///
91/// ## Properties
92///
93/// Defined by [`ListProperties`].
94///
95/// ## Children
96///
97/// Requires to use [`ListItem`] as children.
98///
99/// ## Example
100///
101/// ```rust
102/// use yew::prelude::*;
103/// use patternfly_yew::prelude::*;
104///
105/// #[function_component(Example)]
106/// fn example() -> Html {
107///   html!(
108///     <List>
109///       <ListItem>{"Foo"}</ListItem>
110///       <ListItem>{"Bar"}</ListItem>
111///       // you can also inject a "raw" item, just be sure to add the `li` or `ListItem` element.
112///       <Raw>
113///         <li>{"Baz"}</li>
114///       </Raw>
115///     </List>
116///   )
117/// }
118/// ```
119#[function_component(List)]
120pub fn list(props: &ListProperties) -> Html {
121    let mut classes = Classes::from("pf-v6-c-list");
122
123    classes.extend_from(&props.r#type);
124    classes.extend_from(&props.icon_size);
125
126    let l = |items| match props.r#type {
127        ListType::Basic | ListType::Inline | ListType::Plain | ListType::Bordered => {
128            html! (<ul class={classes} role="list">{ items }</ul>)
129        }
130        ListType::Ordered(n) => {
131            html! (<ol type={n} class={classes} role="list">{ items }</ol>)
132        }
133    };
134
135    l(html! ({ for props.children.clone() }))
136}
137
138#[derive(PartialEq, Properties)]
139pub struct ListItemProperties {
140    #[prop_or_default]
141    pub children: Html,
142    #[prop_or_default]
143    pub icon: Option<Icon>,
144}
145
146#[function_component(ListItem)]
147pub fn list_item(props: &ListItemProperties) -> Html {
148    match props.icon {
149        Some(icon) => {
150            let class = classes!("pf-v6-c-list__item");
151            html!(
152                <li {class}>
153                    <span class={classes!("pf-v6-c-list__item-icon")}>{ icon }</span>
154                    <span class={classes!("pf-v6-c-list__item-text")}>
155                        { props.children.clone() }
156                    </span>
157                </li>
158            )
159        }
160        None => html!(<li>{ props.children.clone() }</li>),
161    }
162}
163
164#[derive(Clone, PartialEq)]
165pub enum ListChildVariant {
166    Item(VChild<ListItem>),
167    Raw(VChild<Raw>),
168}
169
170impl From<VChild<ListItem>> for ListChildVariant {
171    fn from(value: VChild<ListItem>) -> Self {
172        Self::Item(value)
173    }
174}
175
176impl From<VChild<Raw>> for ListChildVariant {
177    fn from(value: VChild<Raw>) -> Self {
178        Self::Raw(value)
179    }
180}
181
182impl From<ListChildVariant> for Html {
183    fn from(value: ListChildVariant) -> Self {
184        match value {
185            ListChildVariant::Item(child) => child.into(),
186            ListChildVariant::Raw(child) => child.into(),
187        }
188    }
189}