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 #[prop(optional, into)]
15 size: Option<Signal<TagSize>>,
16 #[prop(optional, into)]
18 dismissible: Signal<bool>,
19 #[prop(optional, into)]
21 on_dismiss: Option<ArcOneCallback<ev::MouseEvent>>,
22 #[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}