Skip to main content

impulse_thaw/tag/
mod.rs

1mod interaction_tag;
2mod tag_group;
3
4pub use interaction_tag::*;
5pub use tag_group::*;
6
7use leptos::{either::Either, ev, prelude::*};
8use thaw_utils::{class_list, mount_style, ArcOneCallback};
9
10#[component]
11pub fn Tag(
12    #[prop(optional, into)] class: MaybeProp<String>,
13    /// Size of the tag.
14    #[prop(optional, into)]
15    size: Option<Signal<TagSize>>,
16    /// A Tag can be dismissible.
17    #[prop(optional, into)]
18    dismissible: Signal<bool>,
19    /// Callback for when a tag is dismissed.
20    #[prop(optional, into)]
21    on_dismiss: Option<ArcOneCallback<ev::MouseEvent>>,
22    /// Unique value identifying the tag within a TagGroup.
23    #[prop(optional, into)]
24    value: Option<String>,
25    children: Children,
26) -> impl IntoView {
27    mount_style("tag", include_str!("./tag.css"));
28    let (group_size, group_on_dismiss, group_dismissible) = TagGroupInjection::use_context()
29        .map(
30            |TagGroupInjection {
31                 size,
32                 on_dismiss,
33                 dismissible,
34             }| {
35                if value.is_none() {
36                    (Some(size), None, None)
37                } else {
38                    (Some(size), on_dismiss, Some(dismissible))
39                }
40            },
41        )
42        .unwrap_or_default();
43
44    let size_class = {
45        if let Some(size) = size {
46            Some(size)
47        } else if let Some(group_size) = group_size {
48            Some(group_size)
49        } else {
50            None
51        }
52    };
53
54    view! {
55        <span class=class_list![
56            "thaw-tag",
57                ("thaw-tag--dismissible", move || group_dismissible.map_or_else(|| dismissible.get(), |d| d.get())),
58                size_class.map(|size| move || format!("thaw-tag--{}", size.get().as_str())),
59                class
60        ]>
61
62            <span class="thaw-tag__primary-text">{children()}</span>
63
64            {move || {
65                if group_dismissible.map_or_else(|| dismissible.get(), |d| d.get()) {
66                    let on_dismiss = on_dismiss.clone();
67                    let group_on_dismiss = group_on_dismiss.clone();
68                    let value = value.clone();
69                    let on_dismiss = move |event: ev::MouseEvent| {
70                        if let Some(on_dismiss) = group_on_dismiss.as_ref() {
71                            event.prevent_default();
72                            on_dismiss(value.clone().unwrap());
73                        }
74                        let Some(on_dismiss) = on_dismiss.as_ref() else {
75                            return;
76                        };
77                        on_dismiss(event);
78                    };
79                    Either::Left(
80                        view! {
81                            <button class="thaw-tag__dismiss" on:click=on_dismiss>
82                                <svg
83                                    fill="currentColor"
84                                    aria-hidden="true"
85                                    width="1em"
86                                    height="1em"
87                                    viewBox="0 0 20 20"
88                                >
89                                    <path
90                                        d="m4.09 4.22.06-.07a.5.5 0 0 1 .63-.06l.07.06L10 9.29l5.15-5.14a.5.5 0 0 1 .63-.06l.07.06c.18.17.2.44.06.63l-.06.07L10.71 10l5.14 5.15c.18.17.2.44.06.63l-.06.07a.5.5 0 0 1-.63.06l-.07-.06L10 10.71l-5.15 5.14a.5.5 0 0 1-.63.06l-.07-.06a.5.5 0 0 1-.06-.63l.06-.07L9.29 10 4.15 4.85a.5.5 0 0 1-.06-.63l.06-.07-.06.07Z"
91                                        fill="currentColor"
92                                    ></path>
93                                </svg>
94                            </button>
95                        },
96                    )
97                } else {
98                    Either::Right(())
99                }
100            }}
101
102        </span>
103    }
104}
105
106#[derive(Debug, Default, Clone, Copy)]
107pub enum TagSize {
108    #[default]
109    Medium,
110    Small,
111    ExtraSmall,
112}
113
114impl TagSize {
115    pub fn as_str(&self) -> &'static str {
116        match self {
117            Self::Medium => "medium",
118            Self::Small => "small",
119            Self::ExtraSmall => "extra-small",
120        }
121    }
122}