pub use futures_signals::{
signal::Signal,
signal_vec::{SignalVec, SignalVecExt},
};
pub use paste::paste;
pub use silkenweb_macros::rust_to_html_ident;
pub use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};
pub use web_sys;
pub use crate::intern_str;
#[macro_export]
macro_rules! custom_html_element {
(
$(#[$elem_meta:meta])*
$name:ident $( ($text_name: literal) )? = {
$($tail:tt)*
}
) => {
$crate::dom_element!(
$(#[$elem_meta])*
$name $( ($text_name) )? = {
common_attributes = [$crate::elements::HtmlElement, $crate::elements::AriaElement];
common_events = [$crate::elements::HtmlElementEvents];
namespace = &$crate::node::element::Namespace::Html;
$($tail)*
}
);
}
}
macro_rules! html_element {
(
$(#[$elem_meta:meta])*
$name:ident $( ($text_name: literal) )? = {
$($tail:tt)*
}
) => {
$crate::dom_element!(
$(#[$elem_meta])*
$name $( ($text_name) )? = {
common_attributes = [$crate::elements::HtmlElement, $crate::elements::AriaElement];
common_events = [$crate::elements::HtmlElementEvents];
namespace = &$crate::node::element::Namespace::Html;
doc_macro = html_element_doc;
attribute_doc_macro = html_attribute_doc;
$($tail)*
}
);
}
}
macro_rules! html_element_doc {
($prefix:literal, $name:expr) => {
concat!(
$prefix,
" HTML [",
$name,
"](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/",
$name,
") element"
)
};
}
macro_rules! html_attribute_doc {
($element:expr, $name:expr) => {
concat!(
"The [`",
$name,
"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/",
$element,
"#attr-",
$name,
") attribute"
)
};
}
macro_rules! svg_element {
(
$(#[$elem_meta:meta])*
$name:ident $( ($text_name: literal) )? = {
$($tail:tt)*
}
) => {
$crate::dom_element!(
$(#[$elem_meta])*
$name $( ($text_name) )? = {
common_attributes = [$crate::elements::svg::attributes::Core, $crate::elements::AriaElement];
common_events = [];
namespace = &$crate::node::element::Namespace::Svg;
doc_macro = svg_element_doc;
attribute_doc_macro = svg_attribute_doc;
$($tail)*
}
);
}
}
macro_rules! svg_element_doc {
($prefix:literal, $name:expr) => {
concat!(
$prefix,
" SVG [`",
$name,
"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/",
$name,
") element"
)
};
}
macro_rules! svg_attribute_doc {
($element:expr, $name:expr) => {
concat!(
"The SVG [`",
$name,
"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/",
$name,
") attribute"
)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! dom_element {
(
$(#[$elem_meta:meta])*
$snake_name:ident ($text_name:expr) = {
camel_name = $camel_name:ident;
observer_name = $observer_name:ident;
common_attributes = [$($attribute_trait:ty),*];
common_events = [$($event_trait:ty),*];
namespace = $namespace:expr;
$(doc_macro = $doc_macro:ident;)?
$(attribute_doc_macro = $attr_doc_macro:ident;)?
dom_type: $elem_type:ty;
$(attributes { $(
$(#[$attr_meta:meta])*
$attr:ident $( ($text_attr:expr) )? : $typ:ty
),* $(,)? }; )?
$(events {
$(
$(#[$event_meta:meta])*
$event:ident: $event_type:ty
),* $(,)?
};)?
$(properties { $(
$(#[$property_meta:meta])*
$property:ident : $property_type:ty
),* $(,)? }; )?
}
) => {
$(
#[doc = $doc_macro!("Construct the", $text_name)]
#[doc = ""]
)?
$(#[$elem_meta])*
pub fn $snake_name<D:$crate::dom::Dom>() -> $camel_name<D> {
$camel_name::new()
}
$(
#[doc = $doc_macro!("The", $text_name)]
#[doc = ""]
)?
$(#[$elem_meta])*
pub struct $camel_name<
Dom: $crate::dom::Dom = $crate::dom::DefaultDom,
Mutability = $crate::node::element::Mut,
> (
$crate::node::element::GenericElement<Dom, Mutability>
);
impl<Dom: $crate::dom::Dom> $camel_name<Dom> {
pub fn new() -> Self {
Self($crate::node::element::GenericElement::new($namespace, $text_name))
}
pub fn freeze(self) -> $camel_name<Dom, $crate::node::element::Const> {
$camel_name(self.0.freeze())
}
pub fn observe_mutations<F>(self, f: F) -> Self
where
F: for<'a> FnOnce($observer_name<'a, Dom>) -> $observer_name<'a, Dom>
{
Self(self.0.observe_mutations(|observer| f($observer_name(observer)).0))
}
$crate::attributes![
$([
attribute_parent = $text_name,
attribute_doc_macro = $attr_doc_macro
])?
$($($(#[$attr_meta])* pub $attr $( ($text_attr) )?: $typ,)*)?
];
$($crate::events!(
$elem_type {
$(
$(#[$event_meta])*
pub $event: $event_type
),*
}
); )?
$crate::properties![
$($($(#[$property_meta])* pub $property : $property_type,)*)?
];
}
impl<Dom: $crate::dom::Dom> Default for $camel_name<Dom> {
fn default() -> Self {
Self::new()
}
}
impl<InitParam, Dom> $camel_name<$crate::dom::Template<InitParam, Dom>>
where
Dom: $crate::dom::InstantiableDom,
InitParam: 'static
{
pub fn on_instantiate(
self,
f: impl 'static + Fn($camel_name<Dom>, &InitParam) -> $camel_name<Dom>,
) -> Self {
$camel_name(self.0.on_instantiate(
move |elem, param| {
f($camel_name(elem), param).0
}
))
}
}
impl<Dom: $crate::dom::Dom> $crate::node::element::Element for $camel_name<Dom> {
type Dom = Dom;
type DomElement = $elem_type;
fn class<'a, T>(self, class: impl $crate::value::RefSignalOrValue<'a, Item = T>) -> Self
where
T: 'a + AsRef<str>
{
Self(self.0.class(class))
}
fn classes<'a, T, Iter>(
self,
classes: impl $crate::value::RefSignalOrValue<'a, Item = Iter>,
) -> Self
where
T: 'a + AsRef<str>,
Iter: 'a + IntoIterator<Item = T>,
{
Self(self.0.classes(classes))
}
fn attribute<'a>(
self,
name: &str,
value: impl $crate::value::RefSignalOrValue<'a, Item = impl $crate::attribute::Attribute>
) -> Self {
Self(self.0.attribute(name, value))
}
fn style_property<'a>(
self,
name: impl Into<String>,
value: impl $crate::value::RefSignalOrValue<'a, Item = impl AsRef<str> + 'a>
) -> Self
{
Self(self.0.style_property(name, value))
}
fn effect(self, f: impl ::std::ops::FnOnce(&Self::DomElement) + 'static) -> Self {
Self(self.0.effect(|elem| {
f($crate::macros::UnwrapThrowExt::unwrap_throw($crate::macros::JsCast::dyn_ref(elem)))
}))
}
fn effect_signal<T: 'static>(
self,
sig: impl $crate::macros::Signal<Item = T> + 'static,
f: impl Fn(&Self::DomElement, T) + Clone + 'static,
) -> Self {
Self(self.0.effect_signal(
sig,
move |elem, signal| {
f(
$crate::macros::UnwrapThrowExt::unwrap_throw($crate::macros::JsCast::dyn_ref(elem)),
signal,
)
}
))
}
fn map_element(self, f: impl ::std::ops::FnOnce(&Self::DomElement) + 'static) -> Self {
Self(self.0.map_element(|elem| {
f($crate::macros::UnwrapThrowExt::unwrap_throw($crate::macros::JsCast::dyn_ref(elem)))
}))
}
fn map_element_signal<T: 'static>(
self,
sig: impl $crate::macros::Signal<Item = T> + 'static,
f: impl Fn(&Self::DomElement, T) + Clone + 'static,
) -> Self {
Self(self.0.map_element_signal(
sig,
move |elem, signal| {
f(
$crate::macros::UnwrapThrowExt::unwrap_throw($crate::macros::JsCast::dyn_ref(elem)),
signal,
)
}
))
}
fn handle(&self) -> $crate::node::element::ElementHandle<Self::Dom, Self::DomElement> {
self.0.handle().cast()
}
fn spawn_future(self, future: impl ::std::future::Future<Output = ()> + 'static) -> Self {
Self(self.0.spawn_future(future))
}
fn on(
self,
name: &'static str,
f: impl FnMut($crate::macros::JsValue) + 'static
) -> Self {
Self($crate::node::element::Element::on(self.0, name, f))
}
}
impl<Dom: $crate::dom::Dom, Mutability> $crate::value::Value
for $camel_name<Dom, Mutability> {}
impl<Dom: $crate::dom::Dom, Mutability> $crate::dom::InDom
for $camel_name<Dom, Mutability> {
type Dom = Dom;
}
impl<Dom: $crate::dom::Dom, Mutability> From<$camel_name<Dom, Mutability>>
for $crate::node::element::GenericElement<Dom, Mutability> {
fn from(elem: $camel_name<Dom, Mutability>) -> Self {
elem.0
}
}
impl<Dom: $crate::dom::Dom> From<$camel_name<Dom, $crate::node::element::Mut>>
for $crate::node::element::GenericElement<Dom, $crate::node::element::Const> {
fn from(elem: $camel_name<Dom, $crate::node::element::Mut>) -> Self {
elem.0.freeze()
}
}
impl<Dom: $crate::dom::Dom, Mutability> From<$camel_name<Dom, Mutability>>
for $crate::node::Node<Dom> {
fn from(elem: $camel_name<Dom, Mutability>) -> Self {
elem.0.into()
}
}
$(impl<Dom: $crate::dom::Dom> $attribute_trait for $camel_name<Dom> {})*
$(
impl<Dom: $crate::dom::Dom> $event_trait for $camel_name<Dom> {}
)*
impl<Dom: $crate::dom::Dom> $crate::elements::ElementEvents for $camel_name<Dom> {}
impl<Dom> ::std::fmt::Display
for $camel_name<Dom, $crate::node::element::Const>
where
Dom: $crate::dom::Dom,
{
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
impl<InitParam, Dom> $camel_name<
$crate::dom::Template<InitParam, Dom>,
$crate::node::element::Const,
>
where
Dom: $crate::dom::InstantiableDom,
InitParam: 'static
{
pub fn instantiate(&self, param: &InitParam) -> $camel_name<Dom> {
$camel_name(self.0.instantiate(param))
}
}
$(#[$elem_meta])*
pub struct $observer_name<'a, Dom: $crate::dom::Dom = $crate::dom::DefaultDom> (
$crate::node::element::GenericElementObserver<'a, Dom>
);
impl<'a, Dom: $crate::dom::Dom> $observer_name<'a, Dom> {
pub fn attribute(
self,
name: String,
mut f: impl FnMut(
&$elem_type,
::std::option::Option<String>,
) + 'static
) -> Self {
use $crate::macros::JsCast;
Self(
self.0.attribute(
name,
move |elem, prev| f(elem.unchecked_ref(), prev)
)
)
}
$($(
$crate::attribute_observer!($elem_type, $attr $( ($text_attr) )? : $typ);
)*)?
}
};
(
$(#[$elem_meta:meta])*
$name:ident $( ($text_name: literal) )? = {
common_attributes $($tail:tt)*
}
) => { $crate::macros::paste!{
$crate::dom_element!(
$(#[$elem_meta])*
$name($crate::text_name!($name $( ($text_name) )?)) = {
camel_name = [< $name:camel >];
observer_name = [< $name:camel Mutations>];
common_attributes $($tail)*
}
);
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! attribute_observer {
($elem_type:ty, $attr:ident ($text_attr:expr) : $typ:ty) => {
pub fn $attr(
self,
f: impl FnMut(
&$elem_type,
::std::option::Option<String>,
) + 'static
) -> Self {
self.attribute($text_attr.to_string(), f)
}
};
($elem_type:ty, $attr:ident : $typ:ty) => {
$crate::attribute_observer!($elem_type, $attr (stringify!($attr)) : $typ);
};
}
#[macro_export]
macro_rules! parent_element {
($name:ident) => {$crate::macros::paste!{
$crate::text_parent_element!($name);
impl<Dom: $crate::dom::Dom> $crate::node::element::ParentElement<Dom>
for [< $name:camel >] <Dom>
{
fn child(
self,
child: impl $crate::value::SignalOrValue<Item = impl $crate::node::ChildNode<Dom>>
) -> Self
{
Self(self.0.child(child))
}
fn optional_child(
self,
child: impl $crate::value::SignalOrValue<Item = ::std::option::Option<impl $crate::node::ChildNode<Dom>>>
) -> Self
{
Self(self.0.optional_child(child))
}
fn children<N>(self, children: impl IntoIterator<Item = N>) -> Self
where
N: Into<$crate::node::Node<Dom>>
{
Self(self.0.children(children))
}
fn children_signal<N>(
self,
children: impl $crate::macros::SignalVec<Item = N> + 'static,
) -> Self
where
N: Into<$crate::node::Node<Dom>>
{
Self(self.0.children_signal(children))
}
}
}};
}
#[macro_export]
macro_rules! text_parent_element {
($name:ident) => {$crate::macros::paste!{
impl<Dom: $crate::dom::Dom> $crate::node::element::TextParentElement<Dom>
for [< $name:camel >] <Dom>
{
fn text<'a, T>(self, child: impl $crate::value::RefSignalOrValue<'a, Item = T>) -> Self
where
T: 'a + AsRef<str> + Into<String>
{
Self(self.0.text(child))
}
}
}}
}
#[macro_export]
macro_rules! element_slot {
($element:ident, $method_prefix:ident, $slot:expr $(, impl $child_trait:path)? $(,)?) =>
{$crate::macros::paste!{
$crate::element_slot_single!($element, $method_prefix, $slot, $(impl $child_trait)?);
impl<Dom: $crate::dom::Dom> [< $element:camel >] <Dom>
{
pub fn [< $method_prefix _children >]<C>(self, children: impl IntoIterator<Item = C>) -> Self
where
C:
$($child_trait + )?
$crate::elements::HtmlElement +
Into<$crate::node::Node<Dom>>
{
use $crate::node::element::ParentElement;
Self(self.0.children(children.into_iter().map(|child| child.slot($slot))))
}
pub fn [< $method_prefix _children_signal >] <C>(
self,
children: impl $crate::macros::SignalVec<Item = C> + 'static,
) -> Self
where
C:
$($child_trait + )?
$crate::elements::HtmlElement +
Into<$crate::node::Node<Dom>>
{
use $crate::{
macros::SignalVecExt,
node::element::ParentElement,
};
Self(self.0.children_signal(children.map(|child| child.slot($slot))))
}
}
}};
($element:ident, $method_prefix:ident, $slot:expr, $child_type:path $(,)?) => {$crate::macros::paste!{
$crate::element_slot_single!($element, $method_prefix, $slot, $child_type);
impl<Dom: $crate::dom::Dom> [< $element:camel >] <Dom>
{
pub fn [< $method_prefix _children >](self, children: impl IntoIterator<Item = $child_type<Dom>>) -> Self
{
use $crate::{node::element::ParentElement, elements::HtmlElement};
Self(self.0.children(children.into_iter().map(|child| child.slot($slot))))
}
pub fn [< $method_prefix _children_signal >] (
self,
children: impl $crate::macros::SignalVec<Item = $child_type<Dom>> + 'static,
) -> Self
{
use $crate::{
macros::SignalVecExt,
node::element::ParentElement,
elements::HtmlElement,
};
Self(self.0.children_signal(children.map(|child| child.slot($slot))))
}
}
}};
}
#[macro_export]
macro_rules! element_slot_single {
($element:ident, $method_prefix:ident, $slot:expr $(, impl $child_trait:path)? $(,)?) =>
{$crate::macros::paste!{
impl<Dom: $crate::dom::Dom> [< $element:camel >] <Dom>
{
pub fn [< $method_prefix _child >] <C>(
self,
child: impl $crate::value::SignalOrValue<Item = C>
) -> Self
where
C:
$($child_trait + )?
$crate::elements::HtmlElement +
$crate::node::ChildNode<Dom>
{
use $crate::node::element::ParentElement;
Self(self.0.child(child.map(|child| child.slot($slot))))
}
pub fn [< $method_prefix _optional_child >] <C>(
self,
child: impl $crate::value::SignalOrValue<Item = ::std::option::Option<C>>
) -> Self
where
C:
$($child_trait + )?
$crate::elements::HtmlElement +
$crate::node::ChildNode<Dom>
{
use $crate::node::element::ParentElement;
Self(self.0.optional_child(child.map(|child| child.map(|child| child.slot($slot)))))
}
}
}};
($element:ident, $method_prefix:ident, $slot:expr, $child_type:path $(,)?) => {$crate::macros::paste!{
impl<Dom: $crate::dom::Dom> [< $element:camel >] <Dom>
{
pub fn [< $method_prefix _child >](
self,
child: impl $crate::value::SignalOrValue<Item = $child_type<Dom>>
) -> Self
{
use $crate::{node::element::ParentElement, elements::HtmlElement};
Self(self.0.child(child.map(|child| child.slot($slot))))
}
pub fn [< $method_prefix _optional_child >](
self,
child: impl $crate::value::SignalOrValue<Item = ::std::option::Option<$child_type<Dom>>>
) -> Self
{
use $crate::{node::element::ParentElement, elements::HtmlElement};
Self(self.0.optional_child(child.map(|child| child.map(|child| child.slot($slot)))))
}
}
}};
}
#[macro_export]
macro_rules! shadow_parent_element {
($name:ident) => {
$crate::macros::paste! {
impl<Dom: $crate::dom::InstantiableDom> $crate::node::element::ShadowRootParent<Dom>
for [< $name:camel >]<Dom>
{
fn attach_shadow_children<N>(
self,
children: impl IntoIterator<Item = N> + 'static
) -> Self
where
N: Into<$crate::node::Node<Dom>>
{
[< $name:camel >] (self.0.attach_shadow_children(children))
}
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! events {
($elem_type:ty {
$(
$(#[$event_meta:meta])*
$visiblity:vis $name:ident: $event_type:ty
),* $(,)?
}) => { $crate::macros::paste!{
$(
$(#[$event_meta])*
$visiblity fn [<on_ $name >] (
self,
mut f: impl FnMut($event_type, $elem_type) + 'static
) -> Self {
$crate::node::element::Element::on(
self,
$crate::text_name_intern!($name),
move |js_ev| {
use $crate::macros::JsCast;
let event: $event_type = js_ev.into();
let target: $elem_type =
$crate::macros::UnwrapThrowExt::unwrap_throw(
event.current_target()
)
.unchecked_into();
f(event, target);
}
)
}
)*
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! attributes {
(
[
attribute_parent = $element:expr,
attribute_doc_macro = $attr_doc_macro:ident
]
$(
$(#[$attr_meta:meta])*
$visibility:vis $attr:ident $(($text_attr:expr))? : $typ:ty
),* $(,)?
) => {
$(
$crate::attribute!(
[
attribute_parent = $element,
attribute_doc_macro = $attr_doc_macro
]
$(#[$attr_meta])*
$visibility $attr $(($text_attr))?: $typ
);
)*
};
(
$(
$(#[$attr_meta:meta])*
$visibility:vis $attr:ident $(($text_attr:expr))? : $typ:ty
),* $(,)?
) => {
$(
$crate::attribute!(
[]
$(#[$attr_meta])*
$visibility $attr $(($text_attr))?: $typ
);
)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! attribute {
(
[
$(
attribute_parent = $element:expr,
attribute_doc_macro = $attr_doc_macro:ident
)?
]
$(#[$attr_meta:meta])*
$visibility:vis $attr:ident : $typ:ty
) => {
$crate::attribute!(
[
$(
attribute_parent = $element,
attribute_doc_macro = $attr_doc_macro
)?
]
$(#[$attr_meta])*
$visibility $attr ($crate::macros::rust_to_html_ident!($attr)): $typ
);
};
(
[
$(
attribute_parent = $element:expr,
attribute_doc_macro = $attr_doc_macro:ident
)?
]
$(#[$attr_meta:meta])*
$visibility:vis $attr:ident ($text_attr:expr): $typ:ty
) => {
$(
#[doc = $attr_doc_macro!($element, $text_attr)]
#[doc = ""]
)?
$(#[$attr_meta])*
$visibility fn $attr<'a, T>(
self,
value: impl $crate::value::RefSignalOrValue<'a, Item = T>
) -> Self
where
T: $crate::attribute::AsAttribute<$typ>
{
$crate::node::element::Element::attribute(self, $crate::intern_static_str!($text_attr), value)
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! properties {
(
$(
$(#[$property_meta:meta])*
$visibility:vis $name:ident : $typ:ty
),* $(,)?
) => {
$(
$crate::property!(
$(#[$property_meta])*
$visibility $name : $typ
);
)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! property {
(
$(#[$property_meta:meta])*
$visibility:vis $property:ident : $property_type:ty
) => {$crate::macros::paste!{
$(#[$property_meta])*
$visibility fn [< set_ $property >] (
self,
value: impl $crate::macros::Signal<Item = $property_type> + 'static
) -> Self
{
use $crate::{node::element::Element, property::AsProperty};
self.map_element_signal(value, |element, value| {
element. [< set_ $property >] (value.as_property())
})
}
}}
}
#[doc(hidden)]
#[macro_export]
macro_rules! text_name_intern {
($($name:tt)*) => {
$crate::intern_static_str!($crate::text_name!($($name)*))
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! text_name {
($name:ident($text_name:literal)) => {
$text_name
};
($name:ident) => {
$crate::macros::rust_to_html_ident!($name)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! intern_static_str {
($s:expr) => {{
::std::thread_local! {
static NAME: &'static str = $crate::macros::intern_str($s);
}
NAME.with(|name| *name)
}};
}