diff --git a/crates/xilem_html/src/context.rs b/crates/xilem_html/src/context.rs index 50087da8d..c727eaa36 100644 --- a/crates/xilem_html/src/context.rs +++ b/crates/xilem_html/src/context.rs @@ -10,7 +10,7 @@ use crate::{ app::AppRunner, diff::{diff_kv_iterables, Diff}, vecmap::VecMap, - AttributeValue, Message, HTML_NS, SVG_NS, + AttributeValue, Message, }; type CowStr = std::borrow::Cow<'static, str>; @@ -110,18 +110,25 @@ impl Cx { &self.document } - pub fn create_element(&self, ns: &str, name: &str) -> web_sys::Element { - self.document + pub(crate) fn build_element( + &mut self, + ns: &str, + name: &str, + ) -> (web_sys::Element, VecMap) { + let el = self + .document .create_element_ns(Some(ns), name) - .expect("could not create element") - } - - pub fn create_html_element(&self, name: &str) -> web_sys::HtmlElement { - self.create_element(HTML_NS, name).unchecked_into() + .expect("could not create element"); + let attributes = self.apply_attributes(&el); + (el, attributes) } - pub fn create_svg_element(&self, name: &str) -> web_sys::SvgElement { - self.create_element(SVG_NS, name).unchecked_into() + pub(crate) fn rebuild_element( + &mut self, + element: &web_sys::Element, + attributes: &mut VecMap, + ) -> ChangeFlags { + self.apply_attribute_changes(element, attributes) } // TODO Not sure how multiple attribute definitions with the same name should be handled (e.g. `e.attr("class", "a").attr("class", "b")`) diff --git a/crates/xilem_html/src/elements.rs b/crates/xilem_html/src/elements.rs index d89f91af3..15bb03d9a 100644 --- a/crates/xilem_html/src/elements.rs +++ b/crates/xilem_html/src/elements.rs @@ -5,7 +5,7 @@ use xilem_core::{Id, MessageResult, VecSplice}; use crate::{ interfaces::sealed::Sealed, vecmap::VecMap, view::DomNode, AttributeValue, ChangeFlags, Cx, - Pod, View, ViewMarker, ViewSequence, + Pod, View, ViewMarker, ViewSequence, HTML_NS, MATHML_NS, SVG_NS, }; use super::interfaces::Element; @@ -63,9 +63,7 @@ where type Element = web_sys::HtmlElement; fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) { - let el = cx.create_html_element(&self.name); - - let attributes = cx.apply_attributes(&el); + let (el, attributes) = cx.build_element(HTML_NS, &self.name); let mut child_elements = vec![]; let (id, children_states) = @@ -108,7 +106,8 @@ where .parent_element() .expect_throw("this element was mounted and so should have a parent"); parent.remove_child(element).unwrap_throw(); - let new_element = cx.create_html_element(self.node_name()); + let (new_element, attributes) = cx.build_element(HTML_NS, self.node_name()); + state.attributes = attributes; // TODO could this be combined with child updates? while element.child_element_count() > 0 { new_element @@ -119,7 +118,7 @@ where changed |= ChangeFlags::STRUCTURE; } - changed |= cx.apply_attribute_changes(element, &mut state.attributes); + changed |= cx.rebuild_element(element, &mut state.attributes); // update children let mut splice = VecSplice::new(&mut state.child_elements, &mut state.scratch); @@ -171,11 +170,11 @@ macro_rules! generate_dom_interface_impl { // TODO maybe it's possible to reduce even more in the impl function bodies and put into impl_functions // (should improve compile times and probably wasm binary size) -macro_rules! define_html_element { - (($ty_name:ident, $name:ident, $dom_interface:ident)) => { - define_html_element!(($ty_name, $name, $dom_interface, T, A, VS)); +macro_rules! define_element { + (($ns:expr, $ty_name:ident, $name:ident, $dom_interface:ident)) => { + define_element!(($ns, $ty_name, $name, $dom_interface, T, A, VS)); }; - (($ty_name:ident, $name:ident, $dom_interface:ident, $t:ident, $a: ident, $vs: ident)) => { + (($ns:expr, $ty_name:ident, $name:ident, $dom_interface:ident, $t:ident, $a: ident, $vs: ident)) => { pub struct $ty_name<$t, $a = (), $vs = ()>($vs, PhantomData ($t, $a)>); impl<$t, $a, $vs> ViewMarker for $ty_name<$t, $a, $vs> {} @@ -186,9 +185,7 @@ macro_rules! define_html_element { type Element = web_sys::$dom_interface; fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) { - let el = cx.create_html_element(stringify!($name)); - - let attributes = cx.apply_attributes(&el); + let (el, attributes) = cx.build_element($ns, stringify!($name)); let mut child_elements = vec![]; let (id, children_states) = @@ -275,7 +272,7 @@ macro_rules! define_html_element { macro_rules! define_elements { ($($element_def:tt,)*) => { - $(define_html_element!($element_def);)* + $(define_element!($element_def);)* }; } @@ -287,122 +284,122 @@ define_elements!( // TODO include document metadata elements? // content sectioning - (Address, address, HtmlElement), - (Article, article, HtmlElement), - (Aside, aside, HtmlElement), - (Footer, footer, HtmlElement), - (Header, header, HtmlElement), - (H1, h1, HtmlHeadingElement), - (H2, h2, HtmlHeadingElement), - (H3, h3, HtmlHeadingElement), - (H4, h4, HtmlHeadingElement), - (H5, h5, HtmlHeadingElement), - (H6, h6, HtmlHeadingElement), - (Hgroup, hgroup, HtmlElement), - (Main, main, HtmlElement), - (Nav, nav, HtmlElement), - (Section, section, HtmlElement), + (HTML_NS, Address, address, HtmlElement), + (HTML_NS, Article, article, HtmlElement), + (HTML_NS, Aside, aside, HtmlElement), + (HTML_NS, Footer, footer, HtmlElement), + (HTML_NS, Header, header, HtmlElement), + (HTML_NS, H1, h1, HtmlHeadingElement), + (HTML_NS, H2, h2, HtmlHeadingElement), + (HTML_NS, H3, h3, HtmlHeadingElement), + (HTML_NS, H4, h4, HtmlHeadingElement), + (HTML_NS, H5, h5, HtmlHeadingElement), + (HTML_NS, H6, h6, HtmlHeadingElement), + (HTML_NS, Hgroup, hgroup, HtmlElement), + (HTML_NS, Main, main, HtmlElement), + (HTML_NS, Nav, nav, HtmlElement), + (HTML_NS, Section, section, HtmlElement), // text content - (Blockquote, blockquote, HtmlQuoteElement), - (Dd, dd, HtmlElement), - (Div, div, HtmlDivElement), - (Dl, dl, HtmlDListElement), - (Dt, dt, HtmlElement), - (Figcaption, figcaption, HtmlElement), - (Figure, figure, HtmlElement), - (Hr, hr, HtmlHrElement), - (Li, li, HtmlLiElement), - (Link, link, HtmlLinkElement), - (Menu, menu, HtmlMenuElement), - (Ol, ol, HtmlOListElement), - (P, p, HtmlParagraphElement), - (Pre, pre, HtmlPreElement), - (Ul, ul, HtmlUListElement), + (HTML_NS, Blockquote, blockquote, HtmlQuoteElement), + (HTML_NS, Dd, dd, HtmlElement), + (HTML_NS, Div, div, HtmlDivElement), + (HTML_NS, Dl, dl, HtmlDListElement), + (HTML_NS, Dt, dt, HtmlElement), + (HTML_NS, Figcaption, figcaption, HtmlElement), + (HTML_NS, Figure, figure, HtmlElement), + (HTML_NS, Hr, hr, HtmlHrElement), + (HTML_NS, Li, li, HtmlLiElement), + (HTML_NS, Link, link, HtmlLinkElement), + (HTML_NS, Menu, menu, HtmlMenuElement), + (HTML_NS, Ol, ol, HtmlOListElement), + (HTML_NS, P, p, HtmlParagraphElement), + (HTML_NS, Pre, pre, HtmlPreElement), + (HTML_NS, Ul, ul, HtmlUListElement), // inline text - (A, a, HtmlAnchorElement, T, A_, VS), - (Abbr, abbr, HtmlElement), - (B, b, HtmlElement), - (Bdi, bdi, HtmlElement), - (Bdo, bdo, HtmlElement), - (Br, br, HtmlBrElement), - (Cite, cite, HtmlElement), - (Code, code, HtmlElement), - (Data, data, HtmlDataElement), - (Dfn, dfn, HtmlElement), - (Em, em, HtmlElement), - (I, i, HtmlElement), - (Kbd, kbd, HtmlElement), - (Mark, mark, HtmlElement), - (Q, q, HtmlQuoteElement), - (Rp, rp, HtmlElement), - (Rt, rt, HtmlElement), - (Ruby, ruby, HtmlElement), - (S, s, HtmlElement), - (Samp, samp, HtmlElement), - (Small, small, HtmlElement), - (Span, span, HtmlSpanElement), - (Strong, strong, HtmlElement), - (Sub, sub, HtmlElement), - (Sup, sup, HtmlElement), - (Time, time, HtmlTimeElement), - (U, u, HtmlElement), - (Var, var, HtmlElement), - (Wbr, wbr, HtmlElement), + (HTML_NS, A, a, HtmlAnchorElement, T, A_, VS), + (HTML_NS, Abbr, abbr, HtmlElement), + (HTML_NS, B, b, HtmlElement), + (HTML_NS, Bdi, bdi, HtmlElement), + (HTML_NS, Bdo, bdo, HtmlElement), + (HTML_NS, Br, br, HtmlBrElement), + (HTML_NS, Cite, cite, HtmlElement), + (HTML_NS, Code, code, HtmlElement), + (HTML_NS, Data, data, HtmlDataElement), + (HTML_NS, Dfn, dfn, HtmlElement), + (HTML_NS, Em, em, HtmlElement), + (HTML_NS, I, i, HtmlElement), + (HTML_NS, Kbd, kbd, HtmlElement), + (HTML_NS, Mark, mark, HtmlElement), + (HTML_NS, Q, q, HtmlQuoteElement), + (HTML_NS, Rp, rp, HtmlElement), + (HTML_NS, Rt, rt, HtmlElement), + (HTML_NS, Ruby, ruby, HtmlElement), + (HTML_NS, S, s, HtmlElement), + (HTML_NS, Samp, samp, HtmlElement), + (HTML_NS, Small, small, HtmlElement), + (HTML_NS, Span, span, HtmlSpanElement), + (HTML_NS, Strong, strong, HtmlElement), + (HTML_NS, Sub, sub, HtmlElement), + (HTML_NS, Sup, sup, HtmlElement), + (HTML_NS, Time, time, HtmlTimeElement), + (HTML_NS, U, u, HtmlElement), + (HTML_NS, Var, var, HtmlElement), + (HTML_NS, Wbr, wbr, HtmlElement), // image and multimedia - (Area, area, HtmlAreaElement), - (Audio, audio, HtmlAudioElement), - (Canvas, canvas, HtmlCanvasElement), - (Img, img, HtmlImageElement), - (Map, map, HtmlMapElement), - (Track, track, HtmlTrackElement), - (Video, video, HtmlVideoElement), + (HTML_NS, Area, area, HtmlAreaElement), + (HTML_NS, Audio, audio, HtmlAudioElement), + (HTML_NS, Canvas, canvas, HtmlCanvasElement), + (HTML_NS, Img, img, HtmlImageElement), + (HTML_NS, Map, map, HtmlMapElement), + (HTML_NS, Track, track, HtmlTrackElement), + (HTML_NS, Video, video, HtmlVideoElement), // embedded content - (Embed, embed, HtmlEmbedElement), - (Iframe, iframe, HtmlIFrameElement), - (Object, object, HtmlObjectElement), - (Picture, picture, HtmlPictureElement), - (Portal, portal, HtmlElement), - (Source, source, HtmlSourceElement), - // SVG and MathML (TODO, svg and mathml elements) - (Svg, svg, HtmlElement), - (Math, math, HtmlElement), + (HTML_NS, Embed, embed, HtmlEmbedElement), + (HTML_NS, Iframe, iframe, HtmlIFrameElement), + (HTML_NS, Object, object, HtmlObjectElement), + (HTML_NS, Picture, picture, HtmlPictureElement), + (HTML_NS, Portal, portal, HtmlElement), + (HTML_NS, Source, source, HtmlSourceElement), // scripting - (Noscript, noscript, HtmlElement), - (Script, script, HtmlScriptElement), + (HTML_NS, Noscript, noscript, HtmlElement), + (HTML_NS, Script, script, HtmlScriptElement), // demarcating edits - (Del, del, HtmlModElement), - (Ins, ins, HtmlModElement), + (HTML_NS, Del, del, HtmlModElement), + (HTML_NS, Ins, ins, HtmlModElement), // tables - (Caption, caption, HtmlTableCaptionElement), - (Col, col, HtmlTableColElement), - (Colgroup, colgroup, HtmlTableColElement), - (Table, table, HtmlTableElement), - (Tbody, tbody, HtmlTableSectionElement), - (Td, td, HtmlTableCellElement), - (Tfoot, tfoot, HtmlTableSectionElement), - (Th, th, HtmlTableCellElement), - (Thead, thead, HtmlTableSectionElement), - (Tr, tr, HtmlTableRowElement), + (HTML_NS, Caption, caption, HtmlTableCaptionElement), + (HTML_NS, Col, col, HtmlTableColElement), + (HTML_NS, Colgroup, colgroup, HtmlTableColElement), + (HTML_NS, Table, table, HtmlTableElement), + (HTML_NS, Tbody, tbody, HtmlTableSectionElement), + (HTML_NS, Td, td, HtmlTableCellElement), + (HTML_NS, Tfoot, tfoot, HtmlTableSectionElement), + (HTML_NS, Th, th, HtmlTableCellElement), + (HTML_NS, Thead, thead, HtmlTableSectionElement), + (HTML_NS, Tr, tr, HtmlTableRowElement), // forms - (Button, button, HtmlButtonElement), - (Datalist, datalist, HtmlDataListElement), - (Fieldset, fieldset, HtmlFieldSetElement), - (Form, form, HtmlFormElement), - (Input, input, HtmlInputElement), - (Label, label, HtmlLabelElement), - (Legend, legend, HtmlLegendElement), - (Meter, meter, HtmlMeterElement), - (Optgroup, optgroup, HtmlOptGroupElement), - (OptionElement, option, HtmlOptionElement), // Avoid cluttering the namespace with `Option` - (Output, output, HtmlOutputElement), - (Progress, progress, HtmlProgressElement), - (Select, select, HtmlSelectElement), - (Textarea, textarea, HtmlTextAreaElement), + (HTML_NS, Button, button, HtmlButtonElement), + (HTML_NS, Datalist, datalist, HtmlDataListElement), + (HTML_NS, Fieldset, fieldset, HtmlFieldSetElement), + (HTML_NS, Form, form, HtmlFormElement), + (HTML_NS, Input, input, HtmlInputElement), + (HTML_NS, Label, label, HtmlLabelElement), + (HTML_NS, Legend, legend, HtmlLegendElement), + (HTML_NS, Meter, meter, HtmlMeterElement), + (HTML_NS, Optgroup, optgroup, HtmlOptGroupElement), + (HTML_NS, OptionElement, option, HtmlOptionElement), // Avoid cluttering the namespace with `Option` + (HTML_NS, Output, output, HtmlOutputElement), + (HTML_NS, Progress, progress, HtmlProgressElement), + (HTML_NS, Select, select, HtmlSelectElement), + (HTML_NS, Textarea, textarea, HtmlTextAreaElement), // interactive elements, - (Details, details, HtmlDetailsElement), - (Dialog, dialog, HtmlDialogElement), - (Summary, summary, HtmlElement), + (HTML_NS, Details, details, HtmlDetailsElement), + (HTML_NS, Dialog, dialog, HtmlDialogElement), + (HTML_NS, Summary, summary, HtmlElement), // web components, - (Slot, slot, HtmlSlotElement), - (Template, template, HtmlTemplateElement), + (HTML_NS, Slot, slot, HtmlSlotElement), + (HTML_NS, Template, template, HtmlTemplateElement), + // SVG and MathML (TODO, svg and mathml elements) + (SVG_NS, Svg, svg, SvgElement), + (MATHML_NS, Math, math, Element), ); diff --git a/crates/xilem_html/src/interfaces.rs b/crates/xilem_html/src/interfaces.rs index 65dd08c00..173c0deb1 100644 --- a/crates/xilem_html/src/interfaces.rs +++ b/crates/xilem_html/src/interfaces.rs @@ -404,4 +404,8 @@ dom_interface_macro_and_trait_definitions!( HtmlUListElement { methods: {}, child_interfaces: {} }, } }, + SvgElement { + methods: {}, + child_interfaces: {} + }, );