Skip to content

Commit

Permalink
fix yewstack#194: add VElement
Browse files Browse the repository at this point in the history
  • Loading branch information
vitiral committed Apr 10, 2018
1 parent d0854ca commit 1aff464
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 1 deletion.
8 changes: 8 additions & 0 deletions examples/svg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "counter_router"
version = "0.1.0"
authors = ["Garrett Berg <vitiral@gmail.com>"]

[dependencies]
stdweb = "0.4.2"
yew = { path = "../.." }
105 changes: 105 additions & 0 deletions examples/svg/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#![recursion_limit="512"]
#[macro_use]
extern crate stdweb;
#[macro_use]
extern crate yew;

use yew::prelude::*;
use yew::services::console::ConsoleService;

use stdweb::web::Date;

use stdweb::web::{Element, Node};
use stdweb::unstable::TryFrom;
use yew::virtual_dom::{VNode, VElement};

struct Context {
console: ConsoleService,
}

struct Model {
value: i64,
}

enum Msg {
Increment,
Decrement,
None,
Bulk(Vec<Msg>),
}

impl Component<Context> for Model {
type Msg = Msg;
type Properties = ();

fn create(_: Self::Properties, context: &mut Env<Context, Self>) -> Self {
Model {
value: 0,
}
}

fn update(&mut self, msg: Self::Msg, context: &mut Env<Context, Self>) -> ShouldRender {
match msg {
Msg::Increment => {
self.value = self.value + 1;
context.console.log("plus one");
}
Msg::Decrement => {
self.value = self.value - 1;
context.console.log("minus one");
}
Msg::Bulk(list) => for msg in list {
self.update(msg, context);
context.console.log("Bulk action");
},
Msg::None => {
context.console.log("No action");
return false;
}
}
true
}
}


const SVG: &str = r#"
<svg width="400" height="110">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
Sorry, your browser does not support inline SVG.
</svg>
"#;

impl Renderable<Context, Model> for Model {
fn view(&self) -> Html<Context, Self> {
let js_svg = js! {
var div = document.createElement("div");
div.innerHTML = @{SVG.to_string()};
console.log(div);
return div;
};
eprintln!("js_svg: {:?}", js_svg);
let element = Element::try_from(js_svg).expect("convert js_svg");
let velement = VElement::from_element("Element".into(), element);
let svg = VNode::VElement(velement);
eprintln!("svg: {:?}", svg);
// html!{
// <h1>{"See inner html?:"}</h1>
// <div innerHtml=SVG,></div>
// <div>{ SVG }</div>
// <div>{ svg }</div>
// }
svg
}
}

fn main() {
yew::initialize();

let context = Context {
console: ConsoleService,
};

let app: App<_, Model> = App::new(context);
app.mount_to_body();
yew::run_loop();
}
2 changes: 2 additions & 0 deletions src/virtual_dom/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This module contains the implementation of reactive virtual dom concept.

pub mod vcomp;
pub mod velement;
pub mod vlist;
pub mod vnode;
pub mod vtag;
Expand All @@ -11,6 +12,7 @@ use std::fmt;
use stdweb::web::{Element, EventListenerHandle, Node};

pub use self::vcomp::VComp;
pub use self::velement::VElement;
pub use self::vlist::VList;
pub use self::vnode::VNode;
pub use self::vtag::VTag;
Expand Down
140 changes: 140 additions & 0 deletions src/virtual_dom/velement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//! This module contains the implementation of a virtual text node `VElement`.

use super::{Reform, VDiff, VNode};
use html::{Component, ScopeEnv};
use std::cmp::PartialEq;
use std::fmt;
use std::marker::PhantomData;
use stdweb::web::{document, INode, Node, Element};



// pub enum ElementTag {
// /// The value of Attr.name
// Attr,
// /// "#cdata-section"
// CDATASection,
// /// "#comment"
// Comment,
// /// "#document"
// Document,
// /// "#document-fragment"
// DocumentFragment,
// /// The value of DocumentType.name
// DocumentType,
// /// The value of Element.tagName
// Element,
// /// The entity name
// Entity,
// /// The name of entity reference
// EntityReference,
// /// The notation name
// Notation,
// /// The value of ProcessingInstruction.target
// ProcessingInstruction,
// /// "#text"
// Text,
// }

/// A type for a virtual
/// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement)
/// represenation.
pub struct VElement<CTX, COMP: Component<CTX>> {
/// Contains a `tagName` of the element
pub tag: String,
/// A reference to the `Element`.
pub reference: Option<Element>,
_ctx: PhantomData<CTX>,
_comp: PhantomData<COMP>,
}

impl<CTX: 'static, COMP: Component<CTX>> VElement<CTX, COMP> {
/// Creates new virtual text node with a content.
pub fn new(tag: String) -> Self {
VElement {
tag: tag,
reference: None,
_ctx: PhantomData,
_comp: PhantomData,
}
}

/// Create from element.
///
/// You must specify the correct tag.
pub fn from_element(tag: String, element: Element) -> Self {
VElement {
tag: tag,
reference: Some(element),
_ctx: PhantomData,
_comp: PhantomData,
}

}
}

impl<CTX: 'static, COMP: Component<CTX>> VDiff for VElement<CTX, COMP> {
type Context = CTX;
type Component = COMP;

/// Remove VTag from parent.
fn remove(self, parent: &Node) -> Option<Node> {
let node = self.reference
.expect("tried to remove not rendered VElement from DOM");
let sibling = node.next_sibling();
if let Err(_) = parent.remove_child(&node) {
warn!("Node not found to remove VElement");
}
sibling
}

fn apply(
&mut self,
parent: &Node,
_precursor: Option<&Node>,
opposite: Option<VNode<Self::Context, Self::Component>>,
_scope: ScopeEnv<Self::Context, Self::Component>,
) -> Option<Node> {
let reform = {
match opposite {
// // If element matched this type
// Some(VNode::VText(mut vtext)) => {
// self.reference = vtext.reference.take();
// if self.text != vtext.text {
// if let Some(ref element) = self.reference {
// element.set_node_value(Some(&self.text));
// }
// }
// Reform::Keep
// }
Some(vnode) => Reform::Before(vnode.remove(parent)),
None => Reform::Before(None),
}
};
// note: https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
// SVG element is a part of "Element"
match reform {
Reform::Keep => { unreachable!() }
Reform::Before(node) => {
let element = document().create_element(&self.tag).unwrap();
if let Some(sibling) = node {
eprintln!("inserting before");
parent
.insert_before(&element, &sibling)
.expect("can't insert element before sibling");
} else {
eprintln!("appending child");
parent.append_child(&element);
}
self.reference = Some(element);
}
}
self.reference.as_ref().map(|t| t.as_node().to_owned())
}
}

impl<CTX, COMP: Component<CTX>> fmt::Debug for VElement<CTX, COMP> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VElement {{ tag: {} }}", self.tag)
}
}
11 changes: 10 additions & 1 deletion src/virtual_dom/vnode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! This module contains the implementation of abstract virtual node.

use super::{VComp, VDiff, VList, VTag, VText};
use super::{VComp, VDiff, VElement, VList, VTag, VText};
use html::{Component, Renderable, ScopeEnv};
use std::cmp::PartialEq;
use std::fmt;
Expand All @@ -16,6 +16,8 @@ pub enum VNode<CTX, COMP: Component<CTX>> {
VComp(VComp<CTX, COMP>),
/// A holder for a list of other nodes.
VList(VList<CTX, COMP>),
/// A holder for an arbitrary element.
VElement(VElement<CTX, COMP>),
/// A holder for any `Node` (necessary for replacing node).
VRef(Node),
}
Expand All @@ -31,6 +33,7 @@ impl<CTX: 'static, COMP: Component<CTX>> VDiff for VNode<CTX, COMP> {
VNode::VText(vtext) => vtext.remove(parent),
VNode::VComp(vcomp) => vcomp.remove(parent),
VNode::VList(vlist) => vlist.remove(parent),
VNode::VElement(velement) => velement.remove(parent),
VNode::VRef(node) => {
let sibling = node.next_sibling();
parent
Expand All @@ -55,6 +58,7 @@ impl<CTX: 'static, COMP: Component<CTX>> VDiff for VNode<CTX, COMP> {
VNode::VText(ref mut vtext) => vtext.apply(parent, precursor, opposite, env),
VNode::VComp(ref mut vcomp) => vcomp.apply(parent, precursor, opposite, env),
VNode::VList(ref mut vlist) => vlist.apply(parent, precursor, opposite, env),
VNode::VElement(ref mut velement) => velement.apply(parent, precursor, opposite, env),
VNode::VRef(_) => {
// TODO use it for rendering any tag
unimplemented!("node can't be rendered now");
Expand Down Expand Up @@ -106,6 +110,7 @@ impl<CTX, COMP: Component<CTX>> fmt::Debug for VNode<CTX, COMP> {
&VNode::VText(ref vtext) => vtext.fmt(f),
&VNode::VComp(_) => "Component<>".fmt(f),
&VNode::VList(_) => "List<>".fmt(f),
&VNode::VElement(_) => "Element<>".fmt(f),
&VNode::VRef(_) => "NodeReference<>".fmt(f),
}
}
Expand All @@ -130,6 +135,10 @@ impl<CTX, COMP: Component<CTX>> PartialEq for VNode<CTX, COMP> {
// TODO Implement it
false
}
VNode::VElement(_) => {
// TODO Implement it
false
}
VNode::VRef(_) => {
// TODO Implement it
false
Expand Down
Loading

0 comments on commit 1aff464

Please sign in to comment.