use crate::{Attribute, Backend, Component, Widget};
use sauron::{
html::{attributes::*, div, events::mapper, input, text},
Component as SauronComponent, DomUpdater, Program,
};
use sauron_vdom::Callback;
use std::{cell::RefCell, fmt::Debug, marker::PhantomData, rc::Rc};
use wasm_bindgen::JsCast;
pub struct HtmlApp<APP, MSG>
where
MSG: Clone + Debug + 'static,
APP: Component<MSG> + 'static,
{
app: APP,
_phantom_data: PhantomData<MSG>,
}
pub struct HtmlBackend<APP, MSG>
where
MSG: Clone + Debug + 'static,
APP: Component<MSG> + 'static,
{
program: Rc<Program<HtmlApp<APP, MSG>, MSG>>,
}
impl<APP, MSG> HtmlApp<APP, MSG>
where
MSG: Clone + Debug + 'static,
APP: Component<MSG> + 'static,
{
fn new(app: APP) -> Self {
HtmlApp {
app,
_phantom_data: PhantomData,
}
}
}
impl<APP, MSG> sauron::Component<MSG> for HtmlApp<APP, MSG>
where
MSG: Clone + Debug + 'static,
APP: Component<MSG> + 'static,
{
fn update(&mut self, msg: MSG) -> sauron_vdom::Cmd<sauron::Program<Self, MSG>, MSG> {
self.app.update(msg);
sauron_vdom::Cmd::none()
}
fn view(&self) -> sauron::Node<MSG> {
let view = self.app.view();
let html_view = widget_tree_to_html_node(view);
html_view
}
}
impl<APP, MSG> Backend<APP, MSG> for HtmlBackend<APP, MSG>
where
MSG: Clone + Debug + 'static,
APP: Component<MSG> + 'static,
{
fn init(app: APP) -> Rc<Self> {
let html_app = HtmlApp::new(app);
let program = sauron::Program::mount_to_body(html_app);
let backend = HtmlBackend { program };
Rc::new(backend)
}
}
fn widget_to_html<MSG>(widget: &Widget, attrs: &Vec<Attribute<MSG>>) -> sauron::Node<MSG>
where
MSG: Clone + Debug + 'static,
{
match widget {
Widget::Vbox => div(
vec![styles(vec![
("display", "flex"),
("flex-direction", "column"),
("border", "1px solid blue"),
])],
vec![],
),
Widget::Hbox => div(
vec![styles(vec![
("display", "flex"),
("flex-direction", "row"),
("border", "1px solid green"),
])],
vec![],
),
Widget::Button => {
let ce = if let Some(ce) = attrs.iter().find(|att| att.name == "click") {
vec![ce.clone().reform(map_to_event)]
} else {
vec![]
};
input(vec![r#type("button")], vec![]).attributes(ce)
}
Widget::Text(txt) => text(&txt),
Widget::TextBox(txt) => {
let ie = if let Some(ie) = attrs.iter().find(|att| att.name == "input") {
vec![ie.clone().reform(map_to_event)]
} else {
vec![]
};
input(vec![r#type("text"), value(txt)], vec![]).attributes(ie)
}
Widget::Block(title) => div(vec![], vec![text(title)]),
}
}
pub fn widget_tree_to_html_node<MSG>(widget_node: crate::Node<MSG>) -> sauron::Node<MSG>
where
MSG: Clone + Debug + 'static,
{
match widget_node {
crate::Node::Element(widget) => {
let mut html_node: sauron::Node<MSG> = widget_to_html(&widget.tag, &widget.attrs);
if let Some(html_element) = html_node.as_element() {
for attr in widget.attributes() {
html_element.attrs.push(attr.reform(map_to_event));
}
for widget_child in widget.children {
let mut html_child: sauron::Node<MSG> = widget_tree_to_html_node(widget_child);
html_element.children.push(html_child);
}
}
html_node
}
crate::Node::Text(txt) => text(txt.text),
}
}
fn map_to_event(event: sauron::Event) -> sauron_vdom::Event {
if let Some(_mouse_event) = event.dyn_ref::<web_sys::MouseEvent>() {
let me = mapper::mouse_event_mapper(event);
sauron_vdom::Event::MouseEvent(me)
} else if let Some(_input_event) = event.dyn_ref::<web_sys::InputEvent>() {
let input_event = mapper::input_event_mapper(event);
sauron_vdom::Event::InputEvent(input_event)
} else if let Some(_key_event) = event.dyn_ref::<web_sys::KeyboardEvent>() {
let key_event = mapper::keyboard_event_mapper(event);
sauron_vdom::Event::KeyEvent(key_event)
} else {
panic!("unsupported event!")
}
}