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! ({
136         for props.children.clone()
137    }))
138}
139
140#[derive(PartialEq, Properties)]
141pub struct ListItemProperties {
142    #[prop_or_default]
143    pub children: Html,
144    #[prop_or_default]
145    pub icon: Option<Icon>,
146}
147
148#[function_component(ListItem)]
149pub fn list_item(props: &ListItemProperties) -> Html {
150    match props.icon {
151        Some(icon) => {
152            let class = classes!("pf-v6-c-list__item");
153            html!(
154                <li {class}>
155                    <span class={classes!("pf-v6-c-list__item-icon")}>
156                        { icon }
157                    </span>
158                    <span class={classes!("pf-v6-c-list__item-text")}>
159                        { props.children.clone() }
160                    </span>
161                </li>
162            )
163        }
164        None => html!( <li> { props.children.clone() } </li> ),
165    }
166}
167
168#[derive(Clone, PartialEq)]
169pub enum ListChildVariant {
170    Item(VChild<ListItem>),
171    Raw(VChild<Raw>),
172}
173
174impl From<VChild<ListItem>> for ListChildVariant {
175    fn from(value: VChild<ListItem>) -> Self {
176        Self::Item(value)
177    }
178}
179
180impl From<VChild<Raw>> for ListChildVariant {
181    fn from(value: VChild<Raw>) -> Self {
182        Self::Raw(value)
183    }
184}
185
186impl From<ListChildVariant> for Html {
187    fn from(value: ListChildVariant) -> Self {
188        match value {
189            ListChildVariant::Item(child) => child.into(),
190            ListChildVariant::Raw(child) => child.into(),
191        }
192    }
193}