Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for svg by creating elements with correct namespace #550

Merged
merged 1 commit into from
Aug 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions crates/macro/src/html_tree/html_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,20 @@ impl HtmlComponent {
}

fn peek_type(mut cursor: Cursor) -> Option<()> {
let mut type_str: String = "".to_owned();
let mut colons_optional = true;
let mut last_ident = None;

loop {
let mut found_colons = false;
let mut post_colons_cursor = cursor;
if let Some(c) = Self::double_colon(post_colons_cursor) {
found_colons = true;
post_colons_cursor = c;
} else if !colons_optional {
break;
}

if let Some((ident, c)) = post_colons_cursor.ident() {
cursor = c;
if found_colons {
type_str += "::";
}
type_str += &ident.to_string();
last_ident = Some(ident);
} else {
break;
}
Expand All @@ -163,8 +158,9 @@ impl HtmlComponent {
colons_optional = false;
}

(!type_str.is_empty()).as_option()?;
(type_str.to_lowercase() != type_str).as_option()
let type_str = last_ident?.to_string();
type_str.is_ascii().as_option()?;
type_str.bytes().next()?.is_ascii_uppercase().as_option()
}
}

Expand Down
14 changes: 4 additions & 10 deletions crates/macro/src/html_tree/html_dashed_name.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::Peek;
use crate::{non_capitalized_ascii, Peek};
use boolinator::Boolinator;
use proc_macro2::Ident;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use std::fmt;
use syn::buffer::Cursor;
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream, Result as ParseResult};
use syn::Token;

Expand Down Expand Up @@ -36,7 +37,7 @@ impl fmt::Display for HtmlDashedName {
impl Peek<'_, Self> for HtmlDashedName {
fn peek(cursor: Cursor) -> Option<(Self, Cursor)> {
let (name, cursor) = cursor.ident()?;
(name.to_string().to_lowercase() == name.to_string()).as_option()?;
non_capitalized_ascii(&name.to_string()).as_option()?;

let mut extended = Vec::new();
let mut cursor = cursor;
Expand All @@ -58,14 +59,7 @@ impl Peek<'_, Self> for HtmlDashedName {

impl Parse for HtmlDashedName {
fn parse(input: ParseStream) -> ParseResult<Self> {
let name = if let Ok(token) = input.parse::<Token![type]>() {
Ident::new("type", token.span).into()
} else if let Ok(token) = input.parse::<Token![for]>() {
Ident::new("for", token.span).into()
} else {
input.parse::<Ident>()?.into()
};

let name = input.call(Ident::parse_any)?;
let mut extended = Vec::new();
while input.peek(Token![-]) {
extended.push((input.parse::<Token![-]>()?, input.parse::<Ident>()?));
Expand Down
8 changes: 4 additions & 4 deletions crates/macro/src/html_tree/html_tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::HtmlDashedName as TagName;
use super::HtmlProp as TagAttribute;
use super::HtmlPropSuffix as TagSuffix;
use super::HtmlTree;
use crate::{Peek, PeekValue};
use crate::{non_capitalized_ascii, Peek, PeekValue};
use boolinator::Boolinator;
use proc_macro2::{Delimiter, Span};
use quote::{quote, quote_spanned, ToTokens};
Expand Down Expand Up @@ -198,7 +198,7 @@ impl PeekValue<TagName> for HtmlSelfClosingTag {
(punct.as_char() == '<').as_option()?;

let (name, mut cursor) = TagName::peek(cursor)?;
(name.to_string().to_lowercase() == name.to_string()).as_option()?;
non_capitalized_ascii(&name.to_string()).as_option()?;

let mut after_slash = false;
loop {
Expand Down Expand Up @@ -261,7 +261,7 @@ impl PeekValue<TagName> for HtmlTagOpen {
(punct.as_char() == '<').as_option()?;

let (name, _) = TagName::peek(cursor)?;
(name.to_string().to_lowercase() == name.to_string()).as_option()?;
non_capitalized_ascii(&name.to_string()).as_option()?;

Some(name)
}
Expand Down Expand Up @@ -320,7 +320,7 @@ impl PeekValue<TagName> for HtmlTagClose {
(punct.as_char() == '/').as_option()?;

let (name, cursor) = TagName::peek(cursor)?;
(name.to_string().to_lowercase() == name.to_string()).as_option()?;
non_capitalized_ascii(&name.to_string()).as_option()?;

let (punct, _) = cursor.punct()?;
(punct.as_char() == '>').as_option()?;
Expand Down
10 changes: 10 additions & 0 deletions crates/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ trait PeekValue<T> {
fn peek(cursor: Cursor) -> Option<T>;
}

fn non_capitalized_ascii(string: &str) -> bool {
if !string.is_ascii() {
false
} else if let Some(c) = string.bytes().next() {
c.is_ascii_lowercase()
} else {
false
}
}

#[proc_macro_derive(Properties, attributes(props))]
pub fn derive_props(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DerivePropsInput);
Expand Down
9 changes: 5 additions & 4 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ struct CreatedState<COMP: Component> {
impl<COMP: Component + Renderable<COMP>> CreatedState<COMP> {
fn update(mut self) -> Self {
let mut next_frame = self.component.view();
let node = next_frame.apply(self.element.as_node(), None, self.last_frame, &self.env);
let node = next_frame.apply(&self.element, None, self.last_frame, &self.env);
if let Some(ref mut cell) = self.occupied {
*cell.borrow_mut() = node;
}
Expand Down Expand Up @@ -229,7 +229,8 @@ impl<COMP> Scope<COMP>
where
COMP: Component + Renderable<COMP>,
{
pub(crate) fn new() -> Self {
/// visible for testing
pub fn new() -> Self {
let shared_state = Rc::new(RefCell::new(ComponentState::Empty));
Scope { shared_state }
}
Expand Down Expand Up @@ -298,12 +299,12 @@ where
ComponentState::Created(mut this) => {
this.component.destroy();
if let Some(last_frame) = &mut this.last_frame {
last_frame.detach(this.element.as_node());
last_frame.detach(&this.element);
}
}
ComponentState::Ready(mut this) => {
if let Some(ancestor) = &mut this.ancestor {
ancestor.detach(this.element.as_node());
ancestor.detach(&this.element);
}
}
ComponentState::Empty | ComponentState::Destroyed => {}
Expand Down
4 changes: 2 additions & 2 deletions src/virtual_dom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub trait VDiff {
type Component: Component;

/// Remove itself from parent and return the next sibling.
fn detach(&mut self, parent: &Node) -> Option<Node>;
fn detach(&mut self, parent: &Element) -> Option<Node>;

/// Scoped diff apply to other tree.
///
Expand All @@ -101,7 +101,7 @@ pub trait VDiff {
/// (always removes the `Node` that exists).
fn apply(
&mut self,
parent: &Node,
parent: &Element,
precursor: Option<&Node>,
ancestor: Option<VNode<Self::Component>>,
scope: &Scope<Self::Component>,
Expand Down
4 changes: 2 additions & 2 deletions src/virtual_dom/vcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ where
type Component = COMP;

/// Remove VComp from parent.
fn detach(&mut self, parent: &Node) -> Option<Node> {
fn detach(&mut self, parent: &Element) -> Option<Node> {
match self.state.replace(MountState::Detached) {
MountState::Mounted(this) => {
(this.destroyer)();
Expand All @@ -226,7 +226,7 @@ where
/// It compares this with an ancestor `VComp` and overwrites it if it is the same type.
fn apply(
&mut self,
parent: &Node,
parent: &Element,
precursor: Option<&Node>,
ancestor: Option<VNode<Self::Component>>,
env: &Scope<Self::Component>,
Expand Down
6 changes: 3 additions & 3 deletions src/virtual_dom/vlist.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This module contains fragments implementation.
use super::{VDiff, VNode, VText};
use crate::html::{Component, Scope};
use stdweb::web::Node;
use stdweb::web::{Element, Node};

/// This struct represents a fragment of the Virtual DOM tree.
pub struct VList<COMP: Component> {
Expand All @@ -24,7 +24,7 @@ impl<COMP: Component> VList<COMP> {
impl<COMP: Component> VDiff for VList<COMP> {
type Component = COMP;

fn detach(&mut self, parent: &Node) -> Option<Node> {
fn detach(&mut self, parent: &Element) -> Option<Node> {
let mut last_sibling = None;
for mut child in self.childs.drain(..) {
last_sibling = child.detach(parent);
Expand All @@ -34,7 +34,7 @@ impl<COMP: Component> VDiff for VList<COMP> {

fn apply(
&mut self,
parent: &Node,
parent: &Element,
precursor: Option<&Node>,
ancestor: Option<VNode<Self::Component>>,
env: &Scope<Self::Component>,
Expand Down
6 changes: 3 additions & 3 deletions src/virtual_dom/vnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{VComp, VDiff, VList, VTag, VText};
use crate::html::{Component, Renderable, Scope};
use std::cmp::PartialEq;
use std::fmt;
use stdweb::web::{INode, Node};
use stdweb::web::{Element, INode, Node};

/// Bind virtual element to a DOM reference.
pub enum VNode<COMP: Component> {
Expand All @@ -24,7 +24,7 @@ impl<COMP: Component> VDiff for VNode<COMP> {
type Component = COMP;

/// Remove VNode from parent.
fn detach(&mut self, parent: &Node) -> Option<Node> {
fn detach(&mut self, parent: &Element) -> Option<Node> {
match *self {
VNode::VTag(ref mut vtag) => vtag.detach(parent),
VNode::VText(ref mut vtext) => vtext.detach(parent),
Expand All @@ -42,7 +42,7 @@ impl<COMP: Component> VDiff for VNode<COMP> {

fn apply(
&mut self,
parent: &Node,
parent: &Element,
precursor: Option<&Node>,
ancestor: Option<VNode<Self::Component>>,
env: &Scope<Self::Component>,
Expand Down
38 changes: 25 additions & 13 deletions src/virtual_dom/vtag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ use stdweb::web::{document, Element, EventListenerHandle, IElement, INode, Node}
#[allow(unused_imports)]
use stdweb::{_js_impl, js};

/// SVG namespace string used for creating svg elements
pub const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";

/// Default namespace for html elements
pub const HTML_NAMESPACE: &str = "http://www.w3.org/1999/xhtml";

/// A type for a virtual
/// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)
/// representation.
Expand Down Expand Up @@ -344,8 +350,8 @@ impl<COMP: Component> VTag<COMP> {
}
}

// IMPORTANT! This parameters have to be set every time
// to prevent strange behaviour in browser when DOM changed
// IMPORTANT! This parameter has to be set every time
// to prevent strange behaviour in the browser when the DOM changes
set_checked(&input, self.checked);
} else if let Ok(tae) = TextAreaElement::try_from(element.clone()) {
if let Some(change) = self.diff_value(ancestor) {
Expand All @@ -366,7 +372,7 @@ impl<COMP: Component> VDiff for VTag<COMP> {
type Component = COMP;

/// Remove VTag from parent.
fn detach(&mut self, parent: &Node) -> Option<Node> {
fn detach(&mut self, parent: &Element) -> Option<Node> {
let node = self
.reference
.take()
Expand All @@ -382,7 +388,7 @@ impl<COMP: Component> VDiff for VTag<COMP> {
/// to compute what to patch in the actual DOM nodes.
fn apply(
&mut self,
parent: &Node,
parent: &Element,
precursor: Option<&Node>,
ancestor: Option<VNode<Self::Component>>,
env: &Scope<Self::Component>,
Expand Down Expand Up @@ -421,9 +427,18 @@ impl<COMP: Component> VDiff for VTag<COMP> {
match reform {
Reform::Keep => {}
Reform::Before(before) => {
let element = document()
.create_element(&self.tag)
.expect("can't create element for vtag");
let element = if self.tag == "svg"
|| parent.namespace_uri() == Some(SVG_NAMESPACE.to_string())
{
document()
.create_element_ns(SVG_NAMESPACE, &self.tag)
.expect("can't create namespaced element for vtag")
} else {
document()
.create_element(&self.tag)
.expect("can't create element for vtag")
};

if let Some(sibling) = before {
parent
.insert_before(&element, &sibling)
Expand Down Expand Up @@ -472,14 +487,11 @@ impl<COMP: Component> VDiff for VTag<COMP> {
let mut ancestor_childs = ancestor_childs.drain(..);
loop {
match (self_childs.next(), ancestor_childs.next()) {
(Some(left), Some(right)) => {
precursor = left.apply(element.as_node(), precursor.as_ref(), Some(right), &env);
}
(Some(left), None) => {
precursor = left.apply(element.as_node(), precursor.as_ref(), None, &env);
(Some(left), right) => {
precursor = left.apply(&element, precursor.as_ref(), right, &env);
}
(None, Some(ref mut right)) => {
right.detach(element.as_node());
right.detach(&element);
}
(None, None) => break,
}
Expand Down
6 changes: 3 additions & 3 deletions src/virtual_dom/vtext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use log::warn;
use std::cmp::PartialEq;
use std::fmt;
use std::marker::PhantomData;
use stdweb::web::{document, INode, Node, TextNode};
use stdweb::web::{document, Element, INode, Node, TextNode};

/// A type for a virtual
/// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
Expand Down Expand Up @@ -34,7 +34,7 @@ impl<COMP: Component> VDiff for VText<COMP> {
type Component = COMP;

/// Remove VTag from parent.
fn detach(&mut self, parent: &Node) -> Option<Node> {
fn detach(&mut self, parent: &Element) -> Option<Node> {
let node = self
.reference
.take()
Expand All @@ -52,7 +52,7 @@ impl<COMP: Component> VDiff for VText<COMP> {
/// has children and renders them.
fn apply(
&mut self,
parent: &Node,
parent: &Element,
_: Option<&Node>,
opposite: Option<VNode<Self::Component>>,
_: &Scope<Self::Component>,
Expand Down
17 changes: 16 additions & 1 deletion tests/macro/html-tag-pass.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![recursion_limit = "256"]
#![recursion_limit = "512"]

#[macro_use]
mod helpers;
Expand All @@ -18,6 +18,21 @@ pass_helper! {
<option selected=false disabled=true value="">{"Unselected"}</option>
</select>
</div>
<svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
<path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter0_d)">
<circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
<circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
</g>
<circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
<defs>
<filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
</filter>
</defs>
</svg>
<img class=("avatar", "hidden") src="http://pic.com" />
<img class="avatar hidden", />
<button onclick=|e| panic!(e) />
Expand Down
Loading