Skip to content

Commit

Permalink
Run hello world
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed Jan 12, 2024
1 parent b0b9ca9 commit 6f70136
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 4 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
@@ -1,3 +1,4 @@
workspace = { members = ["web_examples/counter"] }
[package]
name = "viewbuilder"
version = "0.10.0-alpha.5"
Expand All @@ -12,7 +13,7 @@ full = []

[dependencies]
slotmap = "1.0.7"
web-sys = { version = "0.3.66", features = ["Document", "HtmlElement", "HtmlCollection", "Text"] }
web-sys = { version = "0.3.66", features = ["Document", "HtmlElement", "HtmlCollection", "Text", "Window"] }

[package.metadata.docs.rs]
features = ["full"]
Expand Down
108 changes: 105 additions & 3 deletions src/lib.rs
@@ -1,5 +1,5 @@
use std::{mem, rc::Rc};
use web_sys::{wasm_bindgen::JsCast, Document, HtmlElement, Node};
use web_sys::{wasm_bindgen::JsCast, Document, HtmlElement, Node, Text};

pub trait Update<T, A> {
fn update(self: Rc<Self>, updater: &dyn Fn(&mut T) -> Option<A>);
Expand All @@ -9,7 +9,7 @@ pub struct Context<'a, T, A> {
pub update: &'a Rc<dyn Update<T, A>>,
}

pub trait View<P, T, A> {
pub trait View<P, T, A = ()> {
type State;

fn build(&mut self, cx: &mut Context<T, A>, platform: &mut P) -> Self::State;
Expand All @@ -29,6 +29,27 @@ impl<P, T, A> View<P, T, A> for () {
fn remove(&mut self, _cx: &mut Context<T, A>, _platform: &mut P, _state: &mut Self::State) {}
}

impl<T, A> View<Web, T, A> for &'static str {
type State = (Self, Text);

fn build(&mut self, cx: &mut Context<T, A>, platform: &mut Web) -> Self::State {
let node = platform.document.create_text_node(self);
platform.parent.append_child(&node).unwrap();
(self, node)
}

fn rebuild(&mut self, cx: &mut Context<T, A>, platform: &mut Web, state: &mut Self::State) {
if *self != state.0 {
state.1.set_text_content(Some(*self));
state.0 = *self;
}
}

fn remove(&mut self, cx: &mut Context<T, A>, platform: &mut Web, state: &mut Self::State) {
state.1.remove();
}
}

macro_rules! impl_view_for_tuple {
($($t:tt: $idx:tt),*) => {
impl<P, T, A, $($t: View<P, T, A>),*> View<P, T, A> for ($($t),*) {
Expand Down Expand Up @@ -63,7 +84,64 @@ pub struct Web {
}

pub struct Attributes {
node: Node,
node: HtmlElement,
}

pub fn class<Name>(name: Name) -> Class<Name>
where
Name: AsRef<str>,
{
Class { name: Some(name) }
}

pub struct Class<Name> {
name: Option<Name>,
}

impl<T, A, Name> View<Attributes, T, A> for Class<Name>
where
Name: AsRef<str>,
{
type State = Name;

fn build(&mut self, cx: &mut Context<T, A>, platform: &mut Attributes) -> Self::State {
let name = self.name.take().unwrap();
platform.node.set_class_name(name.as_ref());
name
}

fn rebuild(
&mut self,
cx: &mut Context<T, A>,
platform: &mut Attributes,
state: &mut Self::State,
) {
let name = self.name.take().unwrap();
if name.as_ref() != state.as_ref() {
platform.node.set_class_name(name.as_ref());
*state = name;
}
}

fn remove(
&mut self,
cx: &mut Context<T, A>,
platform: &mut Attributes,
state: &mut Self::State,
) {
platform.node.set_class_name("");
}
}

pub fn div<Attrs, Content>(
attrs: Attrs,
content: Content,
) -> Element<&'static str, Attrs, Content> {
Element {
tag: "div",
attrs,
content,
}
}

pub struct Element<Tag, Attrs, Content> {
Expand Down Expand Up @@ -92,6 +170,8 @@ where
let child_state = self.content.build(cx, platform);
platform.parent = parent;

platform.parent.append_child(&elem).unwrap();

(elem.unchecked_into(), attrs_state, child_state)
}

Expand All @@ -110,3 +190,25 @@ where
state.0.remove();
}
}

struct AppUpdate;

impl<T> Update<T, ()> for AppUpdate {
fn update(self: Rc<Self>, updater: &dyn Fn(&mut T) -> Option<()>) {
todo!()
}
}

pub fn run<T>(mut view: impl View<Web, T>) {
let document = web_sys::window().unwrap().document().unwrap();

let mut platform = Web {
parent: document.body().unwrap().unchecked_into(),
document,
};

let update: Rc<dyn Update<T, ()>> = Rc::new(AppUpdate);
let mut cx = Context { update: &update };

view.build(&mut cx, &mut platform);
}
8 changes: 8 additions & 0 deletions web_examples/counter/Cargo.toml
@@ -0,0 +1,8 @@
[package]
name = "viewbuilder-counter-example"
version = "0.1.0"
edition = "2021"

[dependencies]
viewbuilder = { path = "../../" }
console_error_panic_hook = "0.1.7"
1 change: 1 addition & 0 deletions web_examples/counter/index.html
@@ -0,0 +1 @@
<html><body></body></html>
11 changes: 11 additions & 0 deletions web_examples/counter/src/main.rs
@@ -0,0 +1,11 @@
use viewbuilder::{class, div, View, Web};

fn app() -> impl View<Web, ()> {
div(class("class"), "Hello World!")
}

fn main() {
console_error_panic_hook::set_once();

viewbuilder::run(app())
}

0 comments on commit 6f70136

Please sign in to comment.