tachys/svg/
mod.rs

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                /// An SVG element.
21                // `tag()` function
22                #[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                /// An SVG element.
37                #[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/// An SVG element.
162#[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/// An SVG element.
176#[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
195/// An element that contains no interactivity, and whose contents can be known at compile time.
196pub struct InertElement {
197    html: Cow<'static, str>,
198}
199
200impl InertElement {
201    /// Creates a new inert svg element.
202    pub fn new(html: impl Into<Cow<'static, str>>) -> Self {
203        Self { html: html.into() }
204    }
205}
206
207/// Retained view state for [`InertElement`].
208pub 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}