#![allow(non_upper_case_globals)] use dioxus_core::HasAttributes; use dioxus_core::IntoAttributeValue; #[cfg(feature = "hot-reload-context")] use dioxus_core_types::HotReloadingContext; use dioxus_html_internal_macro::impl_extension_attributes; #[cfg(feature = "hot-reload-context")] use crate::{map_global_attributes, map_svg_attributes}; pub type AttributeDescription = (&'static str, Option<&'static str>, bool); macro_rules! impl_attribute { ( $element:ident { $(#[$attr_method:meta])* $fil:ident: $vil:ident (DEFAULT), } ) => { $(#[$attr_method])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; #[doc = concat!("let ", stringify!($fil), " = \"value\";")] /// /// rsx! { /// // Attributes need to be under the element they modify #[doc = concat!(" ", stringify!($element), " {")] /// // Attributes are followed by a colon and then the value of the attribute #[doc = concat!(" ", stringify!($fil), ": \"value\"")] /// } #[doc = concat!(" ", stringify!($element), " {")] /// // Or you can use the shorthand syntax if you have a variable in scope that has the same name as the attribute #[doc = concat!(" ", stringify!($fil), ",")] /// } /// }; /// ``` pub const $fil: AttributeDescription = (stringify!($fil), None, false); }; ( $element:ident { $(#[$attr_method:meta])* $fil:ident: $vil:ident ($name:literal), } ) => { $(#[$attr_method])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; #[doc = concat!("let ", stringify!($fil), " = \"value\";")] /// /// rsx! { /// // Attributes need to be under the element they modify #[doc = concat!(" ", stringify!($element), " {")] /// // Attributes are followed by a colon and then the value of the attribute #[doc = concat!(" ", stringify!($fil), ": \"value\"")] /// } #[doc = concat!(" ", stringify!($element), " {")] /// // Or you can use the shorthand syntax if you have a variable in scope that has the same name as the attribute #[doc = concat!(" ", stringify!($fil), ",")] /// } /// }; /// ``` pub const $fil: AttributeDescription = ($name, None, false); }; ( $element:ident { $(#[$attr_method:meta])* $fil:ident: $vil:ident (volatile), } ) => { $(#[$attr_method])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; #[doc = concat!("let ", stringify!($fil), " = \"value\";")] /// /// rsx! { /// // Attributes need to be under the element they modify #[doc = concat!(" ", stringify!($element), " {")] /// // Attributes are followed by a colon and then the value of the attribute #[doc = concat!(" ", stringify!($fil), ": \"value\"")] /// } #[doc = concat!(" ", stringify!($element), " {")] /// // Or you can use the shorthand syntax if you have a variable in scope that has the same name as the attribute #[doc = concat!(" ", stringify!($fil), ",")] /// } /// }; /// ``` pub const $fil: AttributeDescription = (stringify!($fil), None, true); }; ( $element:ident { $(#[$attr_method:meta])* $fil:ident: $vil:ident (in $ns:literal), } ) => { $(#[$attr_method])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; #[doc = concat!("let ", stringify!($fil), " = \"value\";")] /// /// rsx! { /// // Attributes need to be under the element they modify #[doc = concat!(" ", stringify!($element), " {")] /// // Attributes are followed by a colon and then the value of the attribute #[doc = concat!(" ", stringify!($fil), ": \"value\"")] /// } #[doc = concat!(" ", stringify!($element), " {")] /// // Or you can use the shorthand syntax if you have a variable in scope that has the same name as the attribute #[doc = concat!(" ", stringify!($fil), ",")] /// } /// }; /// ``` pub const $fil: AttributeDescription = (stringify!($fil), Some($ns), false) }; ( $element:ident { $(#[$attr_method:meta])* $fil:ident: $vil:ident (in $ns:literal : volatile), } ) => { $(#[$attr_method])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; #[doc = concat!("let ", stringify!($fil), " = \"value\";")] /// /// rsx! { /// // Attributes need to be under the element they modify #[doc = concat!(" ", stringify!($element), " {")] /// // Attributes are followed by a colon and then the value of the attribute #[doc = concat!(" ", stringify!($fil), ": \"value\"")] /// } #[doc = concat!(" ", stringify!($element), " {")] /// // Or you can use the shorthand syntax if you have a variable in scope that has the same name as the attribute #[doc = concat!(" ", stringify!($fil), ",")] /// } /// }; /// ``` pub const $fil: AttributeDescription = (stringify!($fil), Some($ns), true) }; } #[cfg(feature = "hot-reload-context")] macro_rules! impl_attribute_match { ( $attr:ident $fil:ident: $vil:ident (DEFAULT), ) => { if $attr == stringify!($fil) { return Some((stringify!($fil), None)); } }; ( $attr:ident $fil:ident: $vil:ident (volatile), ) => { if $attr == stringify!($fil) { return Some((stringify!($fil), None)); } }; ( $attr:ident $fil:ident: $vil:ident ($name:literal), ) => { if $attr == stringify!($fil) { return Some(($name, None)); } }; ( $attr:ident $fil:ident: $vil:ident (in $ns:literal), ) => { if $attr == stringify!($fil) { return Some((stringify!($fil), Some($ns))); } }; } #[cfg(feature = "html-to-rsx")] macro_rules! impl_html_to_rsx_attribute_match { ( $attr:ident $fil:ident $name:literal ) => { if $attr == $name { return Some(stringify!($fil)); } }; ( $attr:ident $fil:ident $_:tt ) => { if $attr == stringify!($fil) { return Some(stringify!($fil)); } }; } macro_rules! impl_element { ( $(#[$attr:meta])* $name:ident None { $( $(#[$attr_method:meta])* $fil:ident: $vil:ident $extra:tt, )* } ) => { #[allow(non_camel_case_types)] $(#[$attr])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # let attributes = vec![]; /// # fn ChildComponent() -> Element { unimplemented!() } /// # let raw_expression: Element = rsx! {}; /// rsx! { /// // Elements are followed by braces that surround any attributes and children for that element #[doc = concat!(" ", stringify!($name), " {")] /// // Add any attributes first /// class: "my-class", /// "custom-attribute-name": "value", /// // Then add any attributes you are spreading into this element /// ..attributes, /// // Then add any children elements, components, text nodes, or raw expressions /// div {} /// ChildComponent {} /// "child text" /// {raw_expression} /// } /// }; /// ``` pub mod $name { #[allow(unused)] use super::*; pub use crate::attribute_groups::global_attributes::*; pub const TAG_NAME: &'static str = stringify!($name); pub const NAME_SPACE: Option<&'static str> = None; $( impl_attribute!( $name { $(#[$attr_method])* $fil: $vil ($extra), } ); )* } }; ( $(#[$attr:meta])* $name:ident $namespace:literal { $( $(#[$attr_method:meta])* $fil:ident: $vil:ident $extra:tt, )* } ) => { $(#[$attr])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # let attributes = vec![]; /// # fn ChildComponent() -> Element { unimplemented!() } /// # let raw_expression: Element = rsx! {}; /// rsx! { /// // Elements are followed by braces that surround any attributes and children for that element #[doc = concat!(" ", stringify!($name), " {")] /// // Add any attributes first /// color: "red", /// "custom-attribute-name": "value", /// // Then add any attributes you are spreading into this element /// ..attributes, /// // Then add any children elements, components, text nodes, or raw expressions /// circle { cx: "10", cy: "10", r: "2", fill: "red" } /// ChildComponent {} /// "child text" /// {raw_expression} /// } /// }; /// ``` pub mod $name { #[allow(unused)] use super::*; pub use crate::attribute_groups::svg_attributes::*; pub const TAG_NAME: &'static str = stringify!($name); pub const NAME_SPACE: Option<&'static str> = Some($namespace); $( impl_attribute!( $name { $(#[$attr_method])* $fil: $vil ($extra), } ); )* } }; ( $(#[$attr:meta])* $element:ident [$name:literal, $namespace:tt] { $( $(#[$attr_method:meta])* $fil:ident: $vil:ident $extra:tt, )* } ) => { #[allow(non_camel_case_types)] $(#[$attr])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # let attributes = vec![]; /// # fn ChildComponent() -> Element { unimplemented!() } /// # let raw_expression: Element = rsx! {}; /// rsx! { /// // Elements are followed by braces that surround any attributes and children for that element #[doc = concat!(" ", stringify!($element), " {")] /// // Add any attributes first /// color: "red", /// "custom-attribute-name": "value", /// // Then add any attributes you are spreading into this element /// ..attributes, /// // Then add any children elements, components, text nodes, or raw expressions /// circle { cx: "10", cy: "10", r: "2", fill: "red" } /// ChildComponent {} /// "child text" /// {raw_expression} /// } /// }; /// ``` pub mod $element { #[allow(unused)] use super::*; pub use crate::attribute_groups::svg_attributes::*; pub const TAG_NAME: &'static str = $name; pub const NAME_SPACE: Option<&'static str> = Some($namespace); $( impl_attribute!( $element { $(#[$attr_method])* $fil: $vil ($extra), } ); )* } } } #[cfg(feature = "hot-reload-context")] macro_rules! impl_element_match { ( $el:ident $name:ident None { $( $fil:ident: $vil:ident $extra:tt, )* } ) => { if $el == stringify!($name) { return Some((stringify!($name), None)); } }; ( $el:ident $name:ident $namespace:literal { $( $fil:ident: $vil:ident $extra:tt, )* } ) => { if $el == stringify!($name) { return Some((stringify!($name), Some($namespace))); } }; ( $el:ident $name:ident [$_:literal, $namespace:tt] { $( $fil:ident: $vil:ident $extra:tt, )* } ) => { if $el == stringify!($name) { return Some((stringify!($name), Some($namespace))); } }; } #[cfg(feature = "hot-reload-context")] macro_rules! impl_element_match_attributes { ( $el:ident $attr:ident $name:ident None { $( $fil:ident: $vil:ident $extra:tt, )* } ) => { if $el == stringify!($name) { $( impl_attribute_match!( $attr $fil: $vil ($extra), ); )* return impl_map_global_attributes!($el $attr $name None); } }; ( $el:ident $attr:ident $name:ident $namespace:tt { $( $fil:ident: $vil:ident $extra:tt, )* } ) => { if $el == stringify!($name) { $( impl_attribute_match!( $attr $fil: $vil ($extra), ); )* return impl_map_global_attributes!($el $attr $name $namespace); } } } #[cfg(feature = "hot-reload-context")] macro_rules! impl_map_global_attributes { ( $el:ident $attr:ident $element:ident None ) => { map_global_attributes($attr) }; ( $el:ident $attr:ident $element:ident $namespace:literal ) => { if $namespace == "http://www.w3.org/2000/svg" { map_svg_attributes($attr) } else { map_global_attributes($attr) } }; ( $el:ident $attr:ident $element:ident [$name:literal, $namespace:tt] ) => { if $namespace == "http://www.w3.org/2000/svg" { map_svg_attributes($attr) } else { map_global_attributes($attr) } }; } macro_rules! builder_constructors { ( $( $(#[$attr:meta])* $name:ident $namespace:tt { $( $(#[$attr_method:meta])* $fil:ident: $vil:ident $extra:tt, )* }; )* ) => { #[cfg(feature = "hot-reload-context")] pub struct HtmlCtx; #[cfg(feature = "hot-reload-context")] impl HotReloadingContext for HtmlCtx { fn map_attribute(element: &str, attribute: &str) -> Option<(&'static str, Option<&'static str>)> { $( impl_element_match_attributes!( element attribute $name $namespace { $( $fil: $vil $extra, )* } ); )* None } fn map_element(element: &str) -> Option<(&'static str, Option<&'static str>)> { $( impl_element_match!( element $name $namespace { $( $fil: $vil $extra, )* } ); )* None } } #[cfg(feature = "html-to-rsx")] pub fn map_html_attribute_to_rsx(html: &str) -> Option<&'static str> { $( $( impl_html_to_rsx_attribute_match!( html $fil $extra ); )* )* if let Some(name) = crate::map_html_global_attributes_to_rsx(html) { return Some(name); } if let Some(name) = crate::map_html_svg_attributes_to_rsx(html) { return Some(name); } None } #[cfg(feature = "html-to-rsx")] pub fn map_html_element_to_rsx(html: &str) -> Option<&'static str> { $( if html == stringify!($name) { return Some(stringify!($name)); } )* None } $( impl_element!( $(#[$attr])* $name $namespace { $( $(#[$attr_method])* $fil: $vil $extra, )* } ); )* /// This module contains helpers for rust analyzer autocompletion #[doc(hidden)] pub mod completions { /// This helper tells rust analyzer that it should autocomplete the element name with braces. #[allow(non_camel_case_types)] pub enum CompleteWithBraces { $( $(#[$attr])* /// /// ## Usage in rsx /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # let attributes = vec![]; /// # fn ChildComponent() -> Element { unimplemented!() } /// # let raw_expression: Element = rsx! {}; /// rsx! { /// // Elements are followed by braces that surround any attributes and children for that element #[doc = concat!(" ", stringify!($name), " {")] /// // Add any attributes first /// class: "my-class", /// "custom-attribute-name": "value", /// // Then add any attributes you are spreading into this element /// ..attributes, /// // Then add any children elements, components, text nodes, or raw expressions /// div {} /// ChildComponent {} /// "child text" /// {raw_expression} /// } /// }; /// ``` $name {} ),* } } pub(crate) mod extensions { use super::*; $( impl_extension_attributes![$name { $($fil,)* }]; )* } }; } // Organized in the same order as // https://developer.mozilla.org/en-US/docs/Web/HTML/Element // // Does not include obsolete elements. // // This namespace represents a collection of modern HTML-5 compatible elements. // // This list does not include obsolete, deprecated, experimental, or poorly supported elements. builder_constructors! { // Document metadata /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) /// element. /// base None { href: Uri DEFAULT, target: Target DEFAULT, }; /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) /// element. head None {}; /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) /// element. link None { // as: Mime, crossorigin: CrossOrigin DEFAULT, href: Uri DEFAULT, hreflang: LanguageTag DEFAULT, media: String DEFAULT, // FIXME media query rel: LinkType DEFAULT, sizes: String DEFAULT, // FIXME title: String DEFAULT, // FIXME r#type: Mime "type", integrity: String DEFAULT, disabled: Bool DEFAULT, referrerpolicy: ReferrerPolicy DEFAULT, fetchpriority: FetchPriority DEFAULT, blocking: Blocking DEFAULT, r#as: As "as", }; /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) /// element. meta None { charset: String DEFAULT, // FIXME IANA standard names content: String DEFAULT, http_equiv: String "http-equiv", name: Metadata DEFAULT, property: Metadata DEFAULT, }; /// Build a /// [`