1use dioxus::prelude::*;
2
3#[derive(Clone, Copy, PartialEq, Debug)]
5pub enum IconKind {
6 ChevronRight,
7 ChevronDown,
8 ChevronLeft,
9 ChevronUp,
10 Check,
11 Copy,
12 X,
13 AlertTriangle,
14 AlertCircle,
15 Info,
16 CheckCircle,
17 XCircle,
18 Search,
19 Sparkles,
20 Settings,
21 Code,
22 Layers,
23 Terminal,
24 Palette,
25 Eye,
26 EyeOff,
27 ExternalLink,
28 Sun,
29 Moon,
30 Box,
31 Menu,
32 Spinner,
33}
34
35#[derive(Clone, PartialEq, Debug)]
38pub enum SvgIcon {
39 Kind(IconKind),
41 Custom(Element),
43}
44
45impl SvgIcon {
46 pub fn kind(kind: IconKind) -> Self {
48 Self::Kind(kind)
49 }
50
51 pub fn custom(element: Element) -> Self {
53 Self::Custom(element)
54 }
55}
56
57impl From<IconKind> for SvgIcon {
58 fn from(kind: IconKind) -> Self {
59 SvgIcon::Kind(kind)
60 }
61}
62
63impl From<Element> for SvgIcon {
64 fn from(element: Element) -> Self {
65 SvgIcon::Custom(element)
66 }
67}
68
69#[component]
71pub fn RenderSvgIcon(
72 icon: SvgIcon,
73 #[props(default = 16)] size: u32,
74 #[props(default)] class: String,
75) -> Element {
76 match icon {
77 SvgIcon::Kind(kind) => rsx! {
78 Icon { kind, size, class }
79 },
80 SvgIcon::Custom(elem) => rsx! {
81 span {
82 class: "shrink-0 inline-flex items-center justify-center {class}",
83 {elem}
84 }
85 },
86 }
87}
88
89#[component]
90pub fn Icon(
91 kind: IconKind,
92 #[props(default = 16)] size: u32,
93 #[props(default)] class: String,
94) -> Element {
95 let size_str = size.to_string();
96
97 rsx! {
98 svg {
99 xmlns: "http://www.w3.org/2000/svg",
100 width: "{size_str}",
101 height: "{size_str}",
102 view_box: "0 0 24 24",
103 fill: "none",
104 stroke: "currentColor",
105 stroke_width: "1.5",
106 stroke_linecap: "round",
107 stroke_linejoin: "round",
108 class: "shrink-0 {class}",
109 match kind {
110 IconKind::ChevronRight => rsx! {
111 path { d: "m9 18 6-6-6-6" }
112 },
113 IconKind::ChevronDown => rsx! {
114 path { d: "m6 9 6 6 6-6" }
115 },
116 IconKind::ChevronLeft => rsx! {
117 path { d: "m15 18-6-6 6-6" }
118 },
119 IconKind::ChevronUp => rsx! {
120 path { d: "m18 15-6-6-6 6" }
121 },
122 IconKind::Check => rsx! {
123 path { d: "M20 6 9 17l-5-5" }
124 },
125 IconKind::Copy => rsx! {
126 rect { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }
127 path { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" }
128 },
129 IconKind::X => rsx! {
130 path { d: "M18 6 6 18" }
131 path { d: "m6 6 12 12" }
132 },
133 IconKind::AlertTriangle => rsx! {
134 path { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }
135 line { x1: "12", x2: "12", y1: "9", y2: "13" }
136 line { x1: "12", x2: "12.01", y1: "17", y2: "17" }
137 },
138 IconKind::AlertCircle => rsx! {
139 circle { cx: "12", cy: "12", r: "10" }
140 line { x1: "12", x2: "12", y1: "8", y2: "12" }
141 line { x1: "12", x2: "12.01", y1: "16", y2: "16" }
142 },
143 IconKind::Info => rsx! {
144 circle { cx: "12", cy: "12", r: "10" }
145 line { x1: "12", x2: "12", y1: "16", y2: "12" }
146 line { x1: "12", x2: "12.01", y1: "8", y2: "8" }
147 },
148 IconKind::CheckCircle => rsx! {
149 circle { cx: "12", cy: "12", r: "10" }
150 path { d: "m9 12 2 2 4-4" }
151 },
152 IconKind::XCircle => rsx! {
153 circle { cx: "12", cy: "12", r: "10" }
154 path { d: "m15 9-6 6" }
155 path { d: "m9 9 6 6" }
156 },
157 IconKind::Search => rsx! {
158 circle { cx: "11", cy: "11", r: "8" }
159 path { d: "m21 21-4.3-4.3" }
160 },
161 IconKind::Sparkles => rsx! {
162 path { d: "m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3L12 3Z" }
163 },
164 IconKind::Settings => rsx! {
165 path { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }
166 circle { cx: "12", cy: "12", r: "3" }
167 },
168 IconKind::Code => rsx! {
169 polyline { points: "16 18 22 12 16 6" }
170 polyline { points: "8 6 2 12 8 18" }
171 },
172 IconKind::Layers => rsx! {
173 path { d: "m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.84Z" }
174 path { d: "m22 12.5-9.42 4.29a2 2 0 0 1-1.16 0L2 12.5" }
175 path { d: "m22 17.5-9.42 4.29a2 2 0 0 1-1.16 0L2 17.5" }
176 },
177 IconKind::Terminal => rsx! {
178 polyline { points: "4 17 10 11 4 5" }
179 line { x1: "12", x2: "20", y1: "19", y2: "19" }
180 },
181 IconKind::Palette => rsx! {
182 circle { cx: "13.5", cy: "6.5", r: ".5", fill: "currentColor" }
183 circle { cx: "17.5", cy: "10.5", r: ".5", fill: "currentColor" }
184 circle { cx: "8.5", cy: "7.5", r: ".5", fill: "currentColor" }
185 circle { cx: "6.5", cy: "12.5", r: ".5", fill: "currentColor" }
186 path { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z" }
187 },
188 IconKind::Eye => rsx! {
189 path { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }
190 circle { cx: "12", cy: "12", r: "3" }
191 },
192 IconKind::EyeOff => rsx! {
193 path { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }
194 path { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }
195 path { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }
196 line { x1: "2", x2: "22", y1: "2", y2: "22" }
197 },
198 IconKind::ExternalLink => rsx! {
199 path { d: "M15 3h6v6" }
200 path { d: "M10 14 21 3" }
201 path { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }
202 },
203 IconKind::Sun => rsx! {
204 circle { cx: "12", cy: "12", r: "4" }
205 path { d: "M12 2v2" }
206 path { d: "M12 20v2" }
207 path { d: "m4.93 4.93 1.41 1.41" }
208 path { d: "m17.66 17.66 1.41 1.41" }
209 path { d: "M2 12h2" }
210 path { d: "M20 12h2" }
211 path { d: "m6.34 17.66-1.41 1.41" }
212 path { d: "m19.07 4.93-1.41 1.41" }
213 },
214 IconKind::Moon => rsx! {
215 path { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }
216 },
217 IconKind::Box => rsx! {
218 path { d: "M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" }
219 path { d: "m3.3 7 8.7 5 8.7-5" }
220 path { d: "M12 22V12" }
221 },
222 IconKind::Menu => rsx! {
223 line { x1: "4", x2: "20", y1: "12", y2: "12" }
224 line { x1: "4", x2: "20", y1: "6", y2: "6" }
225 line { x1: "4", x2: "20", y1: "18", y2: "18" }
226 },
227 IconKind::Spinner => rsx! {
228 path {
229 class: "animate-spin origin-center",
230 d: "M21 12a9 9 0 1 1-6.219-8.56"
231 }
232 },
233 }
234 }
235 }
236}