#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
use super::{html::HTML_ELEMENT_DEREF_UNIMPLEMENTED_MSG, HydrationKey};
use super::{AnyElement, ElementDescriptor, HtmlElement};
use crate::HydrationCtx;
use leptos_reactive::Oco;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use once_cell::unsync::Lazy as LazyCell;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
macro_rules! generate_svg_tags {
(
$(
#[$meta:meta]
$(#[$void:ident])?
$tag:ident $(- $second:ident $(- $third:ident)?)? $(@ $trailing_:pat)?
),* $(,)?
) => {
paste::paste! {
$(
#[cfg(all(target_arch = "wasm32", feature = "web"))]
thread_local! {
static [<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]: LazyCell<web_sys::HtmlElement> = LazyCell::new(|| {
crate::document()
.create_element_ns(
Some(wasm_bindgen::intern("http://www.w3.org/2000/svg")),
concat![
stringify!($tag),
$(
"-", stringify!($second),
$(
"-", stringify!($third)
)?
)?
],
)
.unwrap()
.unchecked_into()
});
}
#[derive(Clone, Debug)]
#[$meta]
pub struct [<$tag:camel $($second:camel $($third:camel)?)?>] {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
element: web_sys::HtmlElement,
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
id: Option<HydrationKey>,
}
impl Default for [<$tag:camel $($second:camel $($third:camel)?)?>] {
fn default() -> Self {
#[allow(unused)]
let id = HydrationCtx::id();
#[cfg(all(target_arch = "wasm32", feature = "hydrate"))]
let element = if HydrationCtx::is_hydrating() && id.is_some() {
let id = id.unwrap();
if let Some(el) = crate::hydration::get_element(&id.to_string()) {
#[cfg(debug_assertions)]
assert_eq!(
el.node_name().to_ascii_uppercase(),
stringify!([<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]),
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.unchecked_into()
} else {
crate::warn!(
"element with id {id} not found, ignoring it for hydration"
);
[<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]
.with(|el|
el.clone_node()
.unwrap()
.unchecked_into()
)
}
} else {
[<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]
.with(|el|
el.clone_node()
.unwrap()
.unchecked_into()
)
};
#[cfg(all(target_arch = "wasm32", feature = "web", not(feature = "hydrate")))]
let element = [<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]
.with(|el|
el.clone_node()
.unwrap()
.unchecked_into()
);
Self {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
element,
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
id
}
}
}
impl std::ops::Deref for [<$tag:camel $($second:camel $($third:camel)?)?>] {
type Target = web_sys::SvgElement;
fn deref(&self) -> &Self::Target {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
{
use wasm_bindgen::JsCast;
return &self.element.unchecked_ref();
}
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
unimplemented!("{HTML_ELEMENT_DEREF_UNIMPLEMENTED_MSG}");
}
}
impl std::convert::AsRef<web_sys::HtmlElement> for [<$tag:camel $($second:camel $($third:camel)?)?>] {
fn as_ref(&self) -> &web_sys::HtmlElement {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
return &self.element;
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
unimplemented!("{HTML_ELEMENT_DEREF_UNIMPLEMENTED_MSG}");
}
}
impl ElementDescriptor for [<$tag:camel $($second:camel $($third:camel)?)?>] {
fn name(&self) -> Oco<'static, str> {
stringify!($tag).into()
}
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
fn hydration_id(&self) -> &Option<HydrationKey> {
&self.id
}
generate_svg_tags! { @void $($void)? }
}
impl From<HtmlElement<[<$tag:camel $($second:camel $($third:camel)?)?>]>> for HtmlElement<AnyElement> {
fn from(element: HtmlElement<[<$tag:camel $($second:camel $($third:camel)?)?>]>) -> Self {
element.into_any()
}
}
#[$meta]
#[allow(non_snake_case)]
pub fn [<$tag $(_ $second $(_ $third)?)? $($trailing_)?>]() -> HtmlElement<[<$tag:camel $($second:camel $($third:camel)?)?>]> {
HtmlElement::new([<$tag:camel $($second:camel $($third:camel)?)?>]::default())
}
)*
}
};
(@void) => {};
(@void void) => {
fn is_void(&self) -> bool {
true
}
};
}
generate_svg_tags![
a,
animate,
animateMotion,
animateTransform,
circle,
clipPath,
defs,
desc,
discard,
ellipse,
feBlend,
feColorMatrix,
feComponentTransfer,
feComposite,
feConvolveMatrix,
feDiffuseLighting,
feDisplacementMap,
feDistantLight,
feDropShadow,
feFlood,
feFuncA,
feFuncB,
feFuncG,
feFuncR,
feGaussianBlur,
feImage,
feMerge,
feMergeNode,
feMorphology,
feOffset,
fePointLight,
feSpecularLighting,
feSpotLight,
feTile,
feTurbulence,
filter,
foreignObject,
g,
hatch,
hatchpath,
image,
line,
linearGradient,
marker,
mask,
metadata,
mpath,
path,
pattern,
polygon,
polyline,
radialGradient,
rect,
script,
set,
stop,
style,
svg,
switch,
symbol,
text,
textPath,
title,
tspan,
use @_,
view,
];