Unite with maple-stdweb-dom

This commit is contained in:
Andrey Tkachenko 2022-03-07 16:01:12 +04:00
parent 16ea88b644
commit 8ff19b3bf2
19 changed files with 448 additions and 24 deletions

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[workspace]
members = [
"maple",
"maple-core",
"maple-examples",
"maple-macro",
"maple-stdui",
"maple-stdweb",
"maple-stdweb-dom",
]

View File

@ -1,9 +1,7 @@
cargo-features = ["edition"]
[package] [package]
name = "maple-core" name = "maple-core"
version = "0.1.0" version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"] authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018" edition = "2021"
[dependencies] [dependencies]

View File

@ -2,7 +2,8 @@
name = "maple-examples" name = "maple-examples"
version = "0.1.0" version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"] authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2021"
[dependencies] [dependencies]
maple = {path = "../maple"} maple = {path = "../maple"}
maple-stdweb-dom = {path = "../../maple-stdweb-dom"} maple-stdweb-dom = {path = "../maple-stdweb-dom"}

View File

@ -1,10 +1,8 @@
cargo-features = ["edition"]
[package] [package]
name = "maple-macro" name = "maple-macro"
version = "0.1.1" version = "0.1.1"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"] authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018" edition = "2021"
[dependencies] [dependencies]
syn = {version = "*", features=["full", "extra-traits"]} syn = {version = "*", features=["full", "extra-traits"]}

View File

@ -1,10 +1,8 @@
cargo-features = ["edition"]
[package] [package]
name = "maple-stdui" name = "maple-stdui"
version = "0.1.0" version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"] authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018" edition = "2021"
[dependencies] [dependencies]
maple-macro = {path = "../maple-macro"} maple-macro = {path = "../maple-macro"}

3
maple-stdweb-dom/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -0,0 +1,11 @@
[package]
name = "maple-stdweb-dom"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2021"
[dependencies]
maple-core = {path = "../maple-core"}
maple-stdweb = {path = "../maple-stdweb"}
maple-stdui = {path = "../maple-stdui"}
maple-macro = {path = "../maple-macro"}

View File

@ -0,0 +1 @@
# Maple Web DOM Backend

View File

@ -0,0 +1,56 @@
use super::CanvasContextEngine;
use maple_core::prelude::*;
use maple_stdweb::*;
use maple_macro::view;
impl RenderImplementation<CanvasContextEngine, !> for Circle {
fn render_impl<C: Renderable<!>>(&self, eng: &CanvasContextEngine, _children: &C) {
eng.add(&format!("
ctx.beginPath();
ctx.arc({},{},{},0,2*Math.PI);
ctx.stroke();
", self.props.cx.unwrap(), self.props.cy.unwrap(), self.props.r.unwrap()));
}
}
impl View<CanvasContextEngine, !> for Circle {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<!> + 'static> = impl Renderable<CanvasContextEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C: Renderable<!> + 'static>(self, children: Option<C>) -> Self::Renderable<C> {
Node::new(self, children)
}
}
impl RenderImplementation<CanvasContextEngine, !> for Rect {
fn render_impl<C>(&self, eng: &CanvasContextEngine, _children: &C)
where C: Renderable<!>
{
eng.add(&format!("
ctx.rect({},{},{},{});
ctx.stroke();
", self.props.x1.unwrap(), self.props.y1.unwrap(), self.props.x2.unwrap(), self.props.y2.unwrap()));
}
}
impl View<CanvasContextEngine, !> for Rect {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<!> + 'static> = impl Renderable<CanvasContextEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<!> + 'static
{
Node::new(self, children)
}
}

View File

@ -0,0 +1,139 @@
use super::{HtmlEngine, CanvasContextEngine};
use std::collections::HashMap;
use maple_core::prelude::*;
use maple_stdweb::*;
/** Text **/
impl Renderable<HtmlEngine> for Text {
fn render(&self, eng: &HtmlEngine) {
if let Some(text_ref) = &self.props.text {
HtmlEngine::text(eng, text_ref);
}
}
}
/** Div **/
impl RenderImplementation<HtmlEngine, HtmlEngine> for Div {
fn render_impl<C>(&self, eng: &HtmlEngine, children: &C)
where C: Renderable<HtmlEngine>
{
let mut attrs = HashMap::new();
if let Some(class) = self.props.class {
attrs.insert("class".into(), class.into());
}
HtmlEngine::open(eng, "div", Some(attrs));
children.render(eng);
HtmlEngine::close(eng, "div");
}
}
impl View<HtmlEngine, HtmlEngine> for Div {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
Node::new(self, children)
}
}
/** Span **/
impl RenderImplementation<HtmlEngine, HtmlEngine> for Span {
fn render_impl<C>(&self, eng: &HtmlEngine, children: &C)
where C: Renderable<HtmlEngine>
{
HtmlEngine::open(eng, "span", None);
children.render(eng);
HtmlEngine::close(eng, "span");
}
}
impl View<HtmlEngine, HtmlEngine> for Span {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
Node::new(self, children)
}
}
/** Button **/
impl RenderImplementation<HtmlEngine, HtmlEngine> for Button {
fn render_impl<C>(&self, eng: &HtmlEngine, children: &C)
where C: Renderable<HtmlEngine>
{
HtmlEngine::open(eng, "button", None);
children.render(eng);
HtmlEngine::close(eng, "button");
}
}
impl View<HtmlEngine, HtmlEngine> for Button {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
Node::new(self, children)
}
}
impl RenderImplementation<HtmlEngine, CanvasContextEngine> for Canvas {
fn render_impl<C>(&self, eng: &HtmlEngine, children: &C)
where C: Renderable<CanvasContextEngine>
{
let can_eng = CanvasContextEngine::new();
let mut attrs = HashMap::new();
attrs.insert("id".to_string(), "myCanvas".to_string());
eng.open("canvas", Some(attrs));
children.render(&can_eng);
eng.close("canvas");
eng.script(&format!("
var c=document.getElementById(\"myCanvas\");
var ctx=c.getContext(\"2d\"); {};", can_eng.to_string()));
}
}
impl View<HtmlEngine, CanvasContextEngine> for Canvas {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<CanvasContextEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<CanvasContextEngine> + 'static
{
Node::new(self, children)
}
}

View File

@ -0,0 +1,85 @@
#![feature(never_type, proc_macro_hygiene)]
#![feature(generic_associated_types)]
#![feature(type_alias_impl_trait)]
#[macro_use]
extern crate maple_core;
extern crate maple_stdweb;
mod components;
mod canvas;
mod panel;
mod tabs;
use std::cell::RefCell;
pub use self::components::*;
pub use self::canvas::*;
pub use self::panel::*;
pub use self::tabs::*;
use maple_core::prelude::*;
use std::collections::HashMap;
pub struct CanvasContextEngine {
data: RefCell<String>
}
impl Engine for CanvasContextEngine {}
impl CanvasContextEngine {
pub fn new() -> Self {
return Self {
data: RefCell::new(String::new())
}
}
pub fn to_string(&self) -> String {
self.data.borrow().clone()
}
pub fn add(&self, code: &str) {
self.data.borrow_mut().push_str(code);
}
}
pub struct HtmlEngine {
data: RefCell<String>
}
impl Engine for HtmlEngine {}
impl HtmlEngine {
pub fn new() -> Self {
return Self {
data: RefCell::new(String::new())
}
}
pub fn to_string(&self) -> String {
self.data.borrow().clone()
}
pub fn open(&self, name: &str, attrs: Option<HashMap<String, String>>) {
let attrs_str = if let Some(attrs_map) = attrs {
attrs_map.iter().fold(" ".to_string(), |acc, (ref l, ref r)| acc + l + "=\"" + r + "\" ")
} else {
"".to_string()
};
self.data.borrow_mut()
.push_str(&format!("<{}{}>\n", name, attrs_str));
}
pub fn close(&self, name: &str) {
self.data.borrow_mut()
.push_str(&format!("</{}>\n", name));
}
pub fn text(&self, dat: &str) {
self.data.borrow_mut()
.push_str(dat);
}
pub fn script(&self, data: &str) {
self.data.borrow_mut()
.push_str(&("<script>".to_string() + data + "</script>"));
}
}

View File

@ -0,0 +1,25 @@
use maple_core::prelude::*;
use maple_macro::view;
use maple_stdweb::*;
use maple_stdui::prelude::Panel;
use super::HtmlEngine;
impl View<HtmlEngine, HtmlEngine> for Panel {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
view! {
<Div class="panel">
{ ...children }
</Div>
}
}
}

View File

@ -0,0 +1,103 @@
use maple_core::prelude::*;
use maple_macro::view;
use maple_stdweb::*;
use maple_stdui::prelude::tabs::*;
use super::HtmlEngine;
impl View<HtmlEngine, HtmlEngine> for Tabs {
type InputContext = DefaultContext;
type OutputContext = TabsContext<DefaultContext>;
type Renderable<C: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
TabsContext::wrap(ctx)
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
view! {
<Div class="tabs">
{ ... children }
</Div>
}
}
}
impl View<HtmlEngine, HtmlEngine> for Header {
type InputContext = TabsContext<DefaultContext>;
type OutputContext = DefaultContext;
type Renderable<C: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
self.tabs_ctx = Some(ctx.clone());
ctx.unwrap()
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
let tabs = self.tabs_ctx.map(|ctx|
ctx.get_tabs()
.iter()
.enumerate()
.map(|(i, tab)| view! {
<Span class="tab-title" click={move |_| HeaderEvent::Click(i)}>
{match tab {
Some(val) => val,
None => "<No title>"
}}
</Span>
})
.collect::<Vec<_>>());
view! {
<Div class="tabs-header">
{ ...tabs }
</Div>
}
}
}
impl View<HtmlEngine, HtmlEngine> for Body {
type InputContext = TabsContext<DefaultContext>;
type OutputContext = TabsBodyContext<DefaultContext>;
type Renderable<T: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
TabsBodyContext::wrap(ctx)
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
view! {
<Div class="tab">
{ ... children }
</Div>
}
}
}
impl View<HtmlEngine, HtmlEngine> for Tab {
type InputContext = TabsBodyContext<DefaultContext>;
type OutputContext = DefaultContext;
type Renderable<T: Renderable<HtmlEngine> + 'static> = impl Renderable<HtmlEngine>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx.add_tab(self.props.title);
ctx.unwrap().unwrap()
}
fn build<C>(self, children: Option<C>) -> Self::Renderable<C>
where C: Renderable<HtmlEngine> + 'static
{
view! {
<Div class="tab">
{ ... children }
</Div>
}
}
}

View File

@ -1,10 +1,8 @@
cargo-features = ["edition"]
[package] [package]
name = "maple-stdweb" name = "maple-stdweb"
version = "0.1.0" version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"] authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018" edition = "2021"
[dependencies] [dependencies]
maple-core = {path = '../maple-core'} maple-core = {path = '../maple-core'}

View File

@ -13,7 +13,7 @@ pub struct ButtonProps {
pub id: Option<&'static str>, pub id: Option<&'static str>,
pub name: Option<&'static str>, pub name: Option<&'static str>,
pub class: Option<&'static str>, pub class: Option<&'static str>,
pub click: Option<Box<Fn() -> ButtonEvents>> pub click: Option<Box<dyn Fn() -> ButtonEvents>>
} }
impl Component for Button { impl Component for Button {

View File

@ -13,7 +13,7 @@ pub struct CanvasProps {
pub id: Option<&'static str>, pub id: Option<&'static str>,
pub name: Option<&'static str>, pub name: Option<&'static str>,
pub class: Option<&'static str>, pub class: Option<&'static str>,
pub click: Option<Box<Fn() -> CanvasEvents>> pub click: Option<Box<dyn Fn() -> CanvasEvents>>
} }
impl Component for Canvas { impl Component for Canvas {

View File

@ -13,7 +13,7 @@ pub struct DivProps {
pub id: Option<&'static str>, pub id: Option<&'static str>,
pub name: Option<&'static str>, pub name: Option<&'static str>,
pub class: Option<&'static str>, pub class: Option<&'static str>,
pub click: Option<Box<Fn() -> DivEvents>> pub click: Option<Box<dyn Fn() -> DivEvents>>
} }
impl Component for Div { impl Component for Div {

View File

@ -1,10 +1,8 @@
cargo-features = ["edition"]
[package] [package]
name = "maple" name = "maple"
version = "0.1.0" version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"] authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018" edition = "2021"
[dependencies] [dependencies]
maple-core = {path = "../maple-core"} maple-core = {path = "../maple-core"}