1use crate::{
2 html::{
3 attribute::{any_attribute::AnyAttribute, Attribute},
4 element::{ElementType, ElementWithChildren, HtmlElement},
5 },
6 hydration::Cursor,
7 prelude::{AddAnyAttr, Mountable},
8 renderer::{
9 dom::{Element, Node},
10 CastFrom, Rndr,
11 },
12 view::{Position, PositionState, Render, RenderHtml},
13};
14use std::{borrow::Cow, fmt::Debug};
15
16macro_rules! svg_elements {
17 ($($tag:ident [$($attr:ty),*]),* $(,)?) => {
18 paste::paste! {
19 $(
20 #[allow(non_snake_case)]
23 #[track_caller]
24 pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
25 where
26 {
27 HtmlElement {
28 #[cfg(any(debug_assertions, leptos_debuginfo))]
29 defined_at: std::panic::Location::caller(),
30 tag: [<$tag:camel>],
31 attributes: (),
32 children: (),
33 }
34 }
35
36 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
38 pub struct [<$tag:camel>];
39
40 impl<At, Ch> HtmlElement<[<$tag:camel>], At, Ch>
41 where
42 At: Attribute,
43 Ch: Render,
44
45 {
46 $(
47 pub fn $attr<V>(self, value: V) -> HtmlElement <
48 [<$tag:camel>],
49 <At as $crate::html::attribute::NextAttribute<Attr<$crate::html::attribute::[<$attr:camel>], V>>>::Output,
50 Ch
51 >
52 where
53 V: AttributeValue,
54 At: $crate::html::attribute::NextAttribute<Attr<$crate::html::attribute::[<$attr:camel>], V>>,
55 <At as $crate::html::attribute::NextAttribute<Attr<$crate::html::attribute::[<$attr:camel>], V>>>::Output: Attribute,
56 {
57 let HtmlElement { tag, children, attributes,
58 #[cfg(any(debug_assertions, leptos_debuginfo))]
59 defined_at
60 } = self;
61 HtmlElement {
62 tag,
63
64 children,
65 attributes: attributes.add_any_attr($crate::html::attribute::$attr(value)),
66 #[cfg(any(debug_assertions, leptos_debuginfo))]
67 defined_at
68 }
69 }
70 )*
71 }
72
73 impl ElementType for [<$tag:camel>] {
74 type Output = web_sys::SvgElement;
75
76 const TAG: &'static str = stringify!($tag);
77 const SELF_CLOSING: bool = false;
78 const ESCAPE_CHILDREN: bool = true;
79 const NAMESPACE: Option<&'static str> = Some("http://www.w3.org/2000/svg");
80
81 #[inline(always)]
82 fn tag(&self) -> &str {
83 Self::TAG
84 }
85 }
86
87 impl ElementWithChildren for [<$tag:camel>] {}
88 )*
89 }
90 }
91}
92
93svg_elements![
94 a [],
95 animate [],
96 animateMotion [],
97 animateTransform [],
98 circle [],
99 clipPath [],
100 defs [],
101 desc [],
102 discard [],
103 ellipse [],
104 feBlend [],
105 feColorMatrix [],
106 feComponentTransfer [],
107 feComposite [],
108 feConvolveMatrix [],
109 feDiffuseLighting [],
110 feDisplacementMap [],
111 feDistantLight [],
112 feDropShadow [],
113 feFlood [],
114 feFuncA [],
115 feFuncB [],
116 feFuncG [],
117 feFuncR [],
118 feGaussianBlur [],
119 feImage [],
120 feMerge [],
121 feMergeNode [],
122 feMorphology [],
123 feOffset [],
124 fePointLight [],
125 feSpecularLighting [],
126 feSpotLight [],
127 feTile [],
128 feTurbulence [],
129 filter [],
130 foreignObject [],
131 g [],
132 hatch [],
133 hatchpath [],
134 image [],
135 line [],
136 linearGradient [],
137 marker [],
138 mask [],
139 metadata [],
140 mpath [],
141 path [],
142 pattern [],
143 polygon [],
144 polyline [],
145 radialGradient [],
146 rect [],
147 script [],
148 set [],
149 stop [],
150 style [],
151 svg [],
152 switch [],
153 symbol [],
154 text [],
155 textPath [],
156 title [],
157 tspan [],
158 view [],
159];
160
161#[allow(non_snake_case)]
163#[track_caller]
164pub fn r#use() -> HtmlElement<Use, (), ()>
165where {
166 HtmlElement {
167 #[cfg(any(debug_assertions, leptos_debuginfo))]
168 defined_at: std::panic::Location::caller(),
169 tag: Use,
170 attributes: (),
171 children: (),
172 }
173}
174
175#[derive(Debug, Copy, Clone, PartialEq, Eq)]
177pub struct Use;
178
179impl ElementType for Use {
180 type Output = web_sys::SvgElement;
181
182 const TAG: &'static str = "use";
183 const SELF_CLOSING: bool = false;
184 const ESCAPE_CHILDREN: bool = true;
185 const NAMESPACE: Option<&'static str> = Some("http://www.w3.org/2000/svg");
186
187 #[inline(always)]
188 fn tag(&self) -> &str {
189 Self::TAG
190 }
191}
192
193impl ElementWithChildren for Use {}
194
195pub struct InertElement {
197 html: Cow<'static, str>,
198}
199
200impl InertElement {
201 pub fn new(html: impl Into<Cow<'static, str>>) -> Self {
203 Self { html: html.into() }
204 }
205}
206
207pub struct InertElementState(Cow<'static, str>, Element);
209
210impl Mountable for InertElementState {
211 fn unmount(&mut self) {
212 self.1.unmount();
213 }
214
215 fn mount(&mut self, parent: &Element, marker: Option<&Node>) {
216 self.1.mount(parent, marker)
217 }
218
219 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
220 self.1.insert_before_this(child)
221 }
222
223 fn elements(&self) -> Vec<crate::renderer::types::Element> {
224 vec![self.1.clone()]
225 }
226}
227
228impl Render for InertElement {
229 type State = InertElementState;
230
231 fn build(self) -> Self::State {
232 let el = Rndr::create_svg_element_from_html(self.html.clone());
233 InertElementState(self.html, el)
234 }
235
236 fn rebuild(self, state: &mut Self::State) {
237 let InertElementState(prev, el) = state;
238 if &self.html != prev {
239 let mut new_el =
240 Rndr::create_svg_element_from_html(self.html.clone());
241 el.insert_before_this(&mut new_el);
242 el.unmount();
243 *el = new_el;
244 *prev = self.html;
245 }
246 }
247}
248
249impl AddAnyAttr for InertElement {
250 type Output<SomeNewAttr: Attribute> = Self;
251
252 fn add_any_attr<NewAttr: Attribute>(
253 self,
254 _attr: NewAttr,
255 ) -> Self::Output<NewAttr>
256 where
257 Self::Output<NewAttr>: RenderHtml,
258 {
259 panic!(
260 "InertElement does not support adding attributes. It should only \
261 be used as a child, and not returned at the top level."
262 )
263 }
264}
265
266impl RenderHtml for InertElement {
267 type AsyncOutput = Self;
268 type Owned = Self;
269
270 const MIN_LENGTH: usize = 0;
271
272 fn html_len(&self) -> usize {
273 self.html.len()
274 }
275
276 fn dry_resolve(&mut self) {}
277
278 async fn resolve(self) -> Self {
279 self
280 }
281
282 fn to_html_with_buf(
283 self,
284 buf: &mut String,
285 position: &mut Position,
286 _escape: bool,
287 _mark_branches: bool,
288 _extra_attrs: Vec<AnyAttribute>,
289 ) {
290 buf.push_str(&self.html);
291 *position = Position::NextChild;
292 }
293
294 fn hydrate<const FROM_SERVER: bool>(
295 self,
296 cursor: &Cursor,
297 position: &PositionState,
298 ) -> Self::State {
299 let curr_position = position.get();
300 if curr_position == Position::FirstChild {
301 cursor.child();
302 } else if curr_position != Position::Current {
303 cursor.sibling();
304 }
305 let el = crate::renderer::types::Element::cast_from(cursor.current())
306 .unwrap();
307 position.set(Position::NextChild);
308 InertElementState(self.html, el)
309 }
310
311 fn into_owned(self) -> Self::Owned {
312 self
313 }
314}