File reorganization
This commit is contained in:
parent
fca8a56cb3
commit
b10c5c96b1
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
/target
|
||||
target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
|
3
maple-core/.gitignore
vendored
Normal file
3
maple-core/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
@ -1,6 +1,9 @@
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
name = "vdom_lib"
|
||||
name = "maple-core"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
@ -1,5 +1,7 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::component::ComponentMsg;
|
||||
|
||||
pub trait Callback {
|
||||
type Arg;
|
||||
|
@ -1,3 +1,7 @@
|
||||
use crate::engine::Engine;
|
||||
use crate::renderable::Renderable;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! children {
|
||||
($w:expr) => ($w);
|
||||
@ -6,8 +10,8 @@ macro_rules! children {
|
||||
|
||||
pub struct Children2<E, CH0, CH1>
|
||||
where E: Engine,
|
||||
CH0: Renderable<Engine = E>,
|
||||
CH1: Renderable<Engine = E>
|
||||
CH0: Renderable<E>,
|
||||
CH1: Renderable<E>
|
||||
{
|
||||
_e: PhantomData<E>,
|
||||
|
||||
@ -17,8 +21,8 @@ pub struct Children2<E, CH0, CH1>
|
||||
|
||||
impl<E, CH0, CH1> Children2<E, CH0, CH1>
|
||||
where E: Engine,
|
||||
CH0: Renderable<Engine = E>,
|
||||
CH1: Renderable<Engine = E>
|
||||
CH0: Renderable<E>,
|
||||
CH1: Renderable<E>
|
||||
{
|
||||
pub fn new(child0: CH0, child1: CH1) -> Self {
|
||||
Self {
|
||||
@ -29,14 +33,12 @@ impl<E, CH0, CH1> Children2<E, CH0, CH1>
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, CH0, CH1> Renderable for Children2<E, CH0, CH1>
|
||||
impl<E, CH0, CH1> Renderable<E> for Children2<E, CH0, CH1>
|
||||
where E: Engine,
|
||||
CH0: Renderable<Engine = E>,
|
||||
CH1: Renderable<Engine = E>
|
||||
CH0: Renderable<E>,
|
||||
CH1: Renderable<E>
|
||||
{
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
fn render(&self, eng: &E) {
|
||||
self.child0.render(eng);
|
||||
self.child1.render(eng);
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
pub trait Component {
|
||||
type Props: Default;
|
||||
type Msg = !;
|
||||
@ -10,6 +12,11 @@ pub trait Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait ComponentMsg {
|
||||
type Component: Component;
|
||||
}
|
||||
|
||||
impl Component for ! {
|
||||
type Props = ();
|
||||
|
||||
@ -18,10 +25,6 @@ impl Component for ! {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComponentMsg {
|
||||
type Component: Component;
|
||||
}
|
||||
|
||||
impl ComponentMsg for () {
|
||||
type Component = !;
|
||||
}
|
25
maple-core/src/lib.rs
Normal file
25
maple-core/src/lib.rs
Normal file
@ -0,0 +1,25 @@
|
||||
#![feature(associated_type_defaults, never_type, unsize, specialization)]
|
||||
|
||||
pub mod convert;
|
||||
pub mod engine;
|
||||
pub mod context;
|
||||
pub mod callback;
|
||||
pub mod component;
|
||||
|
||||
#[macro_use]
|
||||
pub mod children;
|
||||
pub mod renderable;
|
||||
pub mod node;
|
||||
pub mod view;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::convert::{MyFrom, MyInto};
|
||||
pub use crate::engine::Engine;
|
||||
pub use crate::context::{Context, DefaultContext};
|
||||
pub use crate::callback::{Callback, Callback1};
|
||||
pub use crate::component::{Component, ComponentMsg};
|
||||
pub use crate::children::{Children2};
|
||||
pub use crate::renderable::{Renderable, RenderImplementation, Stub};
|
||||
pub use crate::node::Node;
|
||||
pub use crate::view::View;
|
||||
}
|
46
maple-core/src/node.rs
Normal file
46
maple-core/src/node.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::engine::Engine;
|
||||
use crate::renderable::{Renderable, RenderImplementation};
|
||||
use std::marker::PhantomData;
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub struct Node <E, I, CHE, CH>
|
||||
where
|
||||
E: Engine,
|
||||
CHE: Engine,
|
||||
I: RenderImplementation<E, CHE>,
|
||||
CH: Renderable<CHE>
|
||||
{
|
||||
item: I,
|
||||
children: CH,
|
||||
_m: PhantomData<(E, CHE)>
|
||||
}
|
||||
|
||||
impl<E, I, CHE, CH> Node<E, I, CHE, CH>
|
||||
where
|
||||
E: Engine,
|
||||
CHE: Engine,
|
||||
I: RenderImplementation<E, CHE>,
|
||||
CH: Renderable<CHE>
|
||||
{
|
||||
pub fn new(item: I, children: CH) -> Self {
|
||||
Self {
|
||||
item,
|
||||
children,
|
||||
_m: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, I, CHE, CH> Renderable<E> for Node<E, I, CHE, CH>
|
||||
where
|
||||
E: Engine,
|
||||
CHE: Engine,
|
||||
I: RenderImplementation<E, CHE>,
|
||||
CH: Renderable<CHE>
|
||||
{
|
||||
fn render(&self, eng: &E) {
|
||||
self.item.render_impl(eng, &self.children)
|
||||
}
|
||||
}
|
76
maple-core/src/renderable.rs
Normal file
76
maple-core/src/renderable.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::engine::Engine;
|
||||
|
||||
/// defines implementation of how the children elements binds to the component
|
||||
/// in dom backend it actually attaches/detaches elements to each other
|
||||
///
|
||||
///
|
||||
pub trait RenderImplementation<E, CE>
|
||||
where E: Engine,
|
||||
CE: Engine,
|
||||
{
|
||||
fn render_impl<C: Renderable<CE>>(&self, eng: &E, children: &C);
|
||||
}
|
||||
|
||||
/// Trait Renderable
|
||||
/// defines implementation of how the component should be rendered itself
|
||||
///
|
||||
///
|
||||
///
|
||||
pub trait Renderable<E>
|
||||
where E: Engine,
|
||||
{
|
||||
fn render(&self, eng: &E);
|
||||
}
|
||||
|
||||
///
|
||||
/// for nodes without children
|
||||
///
|
||||
///
|
||||
///
|
||||
#[derive(Default)]
|
||||
pub struct Stub<E: Engine> {
|
||||
_e: PhantomData<E>
|
||||
}
|
||||
|
||||
impl<E: Engine> Renderable<E> for Stub<E> {
|
||||
fn render(&self, _eng: &E) {}
|
||||
}
|
||||
|
||||
// default impl<E, T> Renderable<E> for T
|
||||
// where E: Engine,
|
||||
// T: RenderImplementation<E, E>,
|
||||
// {
|
||||
// fn render(&self, eng: &E) {
|
||||
// self.render_impl(eng, &Stub::default())
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<E: Engine> Renderable<E> for Box<dyn Renderable<E>> {
|
||||
fn render(&self, eng: &E) {
|
||||
(**self).render(eng);
|
||||
}
|
||||
}
|
||||
|
||||
impl <E, T> Renderable<E> for Vec<T>
|
||||
where E: Engine,
|
||||
T: Renderable<E>
|
||||
{
|
||||
fn render(&self, eng: &E) {
|
||||
for child in self.iter() {
|
||||
child.render(eng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <E, T> Renderable<E> for Option<T>
|
||||
where E: Engine,
|
||||
T: Renderable<E>
|
||||
{
|
||||
fn render(&self, eng: &E) {
|
||||
if let Some(inner) = self {
|
||||
inner.render(eng);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,21 @@
|
||||
use crate::engine::Engine;
|
||||
use crate::renderable::Renderable;
|
||||
use crate::context::Context;
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub trait View {
|
||||
pub trait View<E, CE>
|
||||
where E: Engine,
|
||||
CE: Engine,
|
||||
{
|
||||
type InputContext: Context;
|
||||
type OutputContext: Context;
|
||||
type Renderable: Renderable<E>;
|
||||
|
||||
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext;
|
||||
}
|
||||
|
||||
pub trait ViewX {
|
||||
type Engine: Engine;
|
||||
type ChildrenEngine: Engine = Self::Engine;
|
||||
type Renderable: Renderable<Engine = Self::Engine>;
|
||||
|
||||
fn build<C: Renderable<Engine = Self::ChildrenEngine> + 'static>(self, children: Option<C>) -> Self::Renderable;
|
||||
fn build<C: Renderable<CE> + 'static>(self, children: Option<C>) -> Self::Renderable;
|
||||
}
|
||||
|
||||
// It will have sense when `specialization` will be ready to use
|
3
maple-examples/.gitignore
vendored
Normal file
3
maple-examples/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
8
maple-examples/Cargo.toml
Normal file
8
maple-examples/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "maple-examples"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
maple = {path = "../maple"}
|
||||
maple-stdweb-dom = {path = "../maple-stdweb-dom"}
|
82
maple-examples/examples/simple.rs
Normal file
82
maple-examples/examples/simple.rs
Normal file
@ -0,0 +1,82 @@
|
||||
#![feature(proc_macro_non_items)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate maple;
|
||||
extern crate maple_stdweb_dom;
|
||||
|
||||
macro_rules! children {
|
||||
($w:expr) => ($w);
|
||||
($w1:expr, $($rest:tt)*) => (Children2::new($w1, children!($($rest)*)));
|
||||
}
|
||||
|
||||
use maple::prelude::*;
|
||||
|
||||
use std::default::Default;
|
||||
use maple::prelude::tabs::*;
|
||||
use maple::prelude::prelude::Panel;
|
||||
|
||||
use maple_stdweb_dom::*;
|
||||
|
||||
// fn get_rest(count: usize) -> impl Renderable<HtmlEngine> {
|
||||
// let mut vec = Vec::new();
|
||||
|
||||
// for i in 0..count {
|
||||
// vec.push(view! { <Span> "Number " {format!("{}", i + 1)} </Span> });
|
||||
// }
|
||||
|
||||
// return vec;
|
||||
// }
|
||||
|
||||
|
||||
fn main() {
|
||||
let x = {
|
||||
let ctx_default = DefaultContext;
|
||||
|
||||
let mut p0_props = <Div as Component>::Props::default();
|
||||
let mut comp_p0 = Div::create(p0_props);
|
||||
|
||||
// let ctx_p0 = comp_p0.receive_context(ctx_default.clone());
|
||||
// let p0 = comp_p0.build::<Stub<_>>(None);
|
||||
|
||||
|
||||
// p0
|
||||
0
|
||||
};
|
||||
|
||||
// let x = view! {
|
||||
// <Div>
|
||||
// <Div>
|
||||
// <Canvas>
|
||||
// <Circle cx={95} cy={70} r={20} />
|
||||
// <Rect x1={20} y1={20} x2={150} y2={100} />
|
||||
// </Canvas>
|
||||
// </Div>
|
||||
|
||||
// <Panel>
|
||||
// <Tabs on_tab_change={|idx| println!("Tab has changed! New tab idx {}", idx)}>
|
||||
// <Header />
|
||||
// <Body>
|
||||
// <Tab title="Tab 1">
|
||||
// <Span>"Tab 1 Content"</Span>
|
||||
// </Tab>
|
||||
|
||||
// <Tab title="Tab 2">
|
||||
// <Button>"But in Tab " {1 + 1} </Button>
|
||||
// </Tab>
|
||||
|
||||
// <Tab title="Tab 3">
|
||||
// { ... get_rest(5) }
|
||||
// </Tab>
|
||||
// </Body>
|
||||
// </Tabs>
|
||||
// </Panel>
|
||||
// </Div>
|
||||
// };
|
||||
|
||||
let eng = HtmlEngine::new();
|
||||
|
||||
// x.render(&eng);
|
||||
|
||||
println!("{}", eng.to_string());
|
||||
}
|
7
maple-examples/src/lib.rs
Normal file
7
maple-examples/src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}
|
10
maple-macro/.gitignore
vendored
Normal file
10
maple-macro/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
16
maple-macro/Cargo.toml
Normal file
16
maple-macro/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
name = "maple-macro"
|
||||
version = "0.1.1"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
syn = {version = "*", features=["full", "extra-traits"]}
|
||||
quote = "*"
|
||||
proc-macro2 = {version = "*", features=["nightly", "proc-macro"]}
|
||||
maple-core = { path="../maple-core" }
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
1
maple-macro/README.md
Normal file
1
maple-macro/README.md
Normal file
@ -0,0 +1 @@
|
||||
# maple-macro
|
46
maple-macro/src/attribute.rs
Normal file
46
maple-macro/src/attribute.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use syn::{Expr, ExprLit, Ident};
|
||||
use syn::synom::Synom;
|
||||
use quote::ToTokens;
|
||||
|
||||
#[derive(Debug)]
|
||||
crate struct Attribute {
|
||||
pub name: Ident,
|
||||
pub value: AttributeValue
|
||||
}
|
||||
|
||||
impl Synom for Attribute {
|
||||
named!(parse -> Self, do_parse!(
|
||||
name: syn!(Ident) >>
|
||||
punct!(=) >>
|
||||
value: syn!(AttributeValue) >>
|
||||
|
||||
(Attribute { name, value })
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
crate enum AttributeValue {
|
||||
Expr(Expr),
|
||||
Literal(ExprLit)
|
||||
}
|
||||
|
||||
impl Synom for AttributeValue {
|
||||
named!(parse -> Self, do_parse!(
|
||||
value: alt!(
|
||||
syn!(ExprLit) => { |d| AttributeValue::Literal(d) }
|
||||
|
|
||||
braces!(syn!(Expr)) => { |d| AttributeValue::Expr(d.1) }
|
||||
) >>
|
||||
|
||||
(value)
|
||||
));
|
||||
}
|
||||
|
||||
impl ToTokens for AttributeValue {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
match self {
|
||||
AttributeValue::Literal(s) => s.to_tokens(tokens),
|
||||
AttributeValue::Expr(e) => e.to_tokens(tokens)
|
||||
};
|
||||
}
|
||||
}
|
134
maple-macro/src/lib.rs
Normal file
134
maple-macro/src/lib.rs
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
mod attribute;
|
||||
mod node;
|
||||
mod tag;
|
||||
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::{TokenStream};
|
||||
use syn::synom::Synom;
|
||||
use syn::{TypePath, Ident, Expr};
|
||||
use syn::buffer::{TokenBuffer};
|
||||
// use syn::parsers::{named, do_parse, punct, syn, call, alt, braces, many0};
|
||||
use quote::*;
|
||||
|
||||
use self::node::Node;
|
||||
use self::attribute::{Attribute, AttributeValue};
|
||||
|
||||
fn visit_attribute(ident: &Ident, attr: &Attribute, _ctx: &TypePath) -> impl ToTokens {
|
||||
let attr_name = &attr.name;
|
||||
let attr_value = &attr.value;
|
||||
|
||||
match attr_value {
|
||||
AttributeValue::Expr(Expr::Closure(closure)) => quote! {
|
||||
#ident.#attr_name = Callback1::new({#closure}).my_into();
|
||||
},
|
||||
_ => quote! { #ident.#attr_name = {#attr_value}.my_into(); }
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_node(parent: &str, idx: &mut u32, node: &Node, stream: &mut proc_macro2::TokenStream, thread: &mut proc_macro2::TokenStream) {
|
||||
let ii_name = &("p".to_string() + &idx.to_string());
|
||||
let ii = Ident::new(ii_name, proc_macro2::Span::call_site());
|
||||
let ctx_ii = Ident::new(&("ctx_".to_string() + ii_name), proc_macro2::Span::call_site());
|
||||
let comp_ii = Ident::new(&("comp_".to_string() + ii_name), proc_macro2::Span::call_site());
|
||||
let pp = Ident::new(&("ctx_".to_string() + parent), proc_macro2::Span::call_site());
|
||||
|
||||
match node {
|
||||
Node::Text(txt) => {
|
||||
let toks = quote!{
|
||||
let #ii = Text::create(<Text as Component>::Props::new(#txt));
|
||||
};
|
||||
toks.to_tokens(stream);
|
||||
},
|
||||
|
||||
Node::Expr(exr) => {
|
||||
let toks = quote!{
|
||||
let #ii = Text::create(<Text as Component>::Props::new(format!("{:?}", #exr)));
|
||||
};
|
||||
toks.to_tokens(stream);
|
||||
},
|
||||
|
||||
Node::Embed(exr) => {
|
||||
let toks = quote!{
|
||||
let #ii = #exr;
|
||||
};
|
||||
toks.to_tokens(stream);
|
||||
},
|
||||
|
||||
Node::Tag(tag) => {
|
||||
let iip = Ident::new(&(ii_name.to_string() + "_props"), proc_macro2::Span::call_site());
|
||||
let path = &tag.path;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut children = Vec::new();
|
||||
|
||||
let attributes = tag.attributes
|
||||
.iter()
|
||||
.map(|attr| visit_attribute(&iip, &attr, &path));
|
||||
|
||||
let toks = quote!{
|
||||
#[allow(unused_mut)]
|
||||
let mut #iip = <#path as Component>::Props::default();
|
||||
#(#attributes;)*
|
||||
let mut #comp_ii = #path::create(#iip);
|
||||
#[allow(unused_variables)]
|
||||
let #ctx_ii = #comp_ii.receive_context(#pp.clone());
|
||||
};
|
||||
|
||||
toks.to_tokens(stream);
|
||||
|
||||
for child in tag.body.iter() {
|
||||
*idx += 1;
|
||||
|
||||
let ch_name = "p".to_string() + &idx.to_string();
|
||||
let ch_ii = Ident::new(&ch_name, proc_macro2::Span::call_site());
|
||||
|
||||
children.push(ch_ii);
|
||||
|
||||
visit_node(ii_name, idx, child, stream, thread);
|
||||
}
|
||||
|
||||
let toks = if children.len() > 0 {
|
||||
quote!{
|
||||
let #ii = #comp_ii.build(Some(children![#(#children),*]));
|
||||
}
|
||||
} else {
|
||||
quote!{
|
||||
let #ii = #comp_ii.build::<Stub<_>>(None);
|
||||
}
|
||||
};
|
||||
|
||||
toks.to_tokens(thread);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn view(input: TokenStream) -> TokenStream {
|
||||
let buffer = TokenBuffer::new(input);
|
||||
let cursor = buffer.begin();
|
||||
let res = Node::parse(cursor);
|
||||
|
||||
let mut output = proc_macro2::TokenStream::new();
|
||||
let mut thread = proc_macro2::TokenStream::new();
|
||||
let mut idx = 0;
|
||||
|
||||
match res {
|
||||
Ok((root, _)) => visit_node("default", &mut idx, &root, &mut output, &mut thread),
|
||||
Err(err) => panic!("Error: {:?}", err)
|
||||
};
|
||||
|
||||
let toks = quote! {{
|
||||
let ctx_default = DefaultContext;
|
||||
#output
|
||||
#thread
|
||||
p0
|
||||
}};
|
||||
|
||||
toks.into()
|
||||
}
|
34
maple-macro/src/node.rs
Normal file
34
maple-macro/src/node.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use syn::synom::Synom;
|
||||
use syn::{LitStr, Expr};
|
||||
use crate::tag::Tag;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
crate enum Node {
|
||||
Tag(Tag),
|
||||
Text(LitStr),
|
||||
Expr(Expr),
|
||||
Embed(Expr)
|
||||
}
|
||||
|
||||
impl Synom for Node {
|
||||
named!(parse -> Self, do_parse!(
|
||||
value: alt!(
|
||||
syn!(Tag) => { |tag| Node::Tag(tag) }
|
||||
|
|
||||
braces!(
|
||||
do_parse!(
|
||||
punct!(.) >>
|
||||
punct!(.) >>
|
||||
punct!(.) >>
|
||||
tt: syn!(Expr) >> (tt)
|
||||
)) => { |d| Node::Embed(d.1) }
|
||||
|
|
||||
braces!(syn!(Expr)) => { |d| Node::Expr(d.1) }
|
||||
|
|
||||
syn!(LitStr) => { |_str| Node::Text(_str) }
|
||||
) >>
|
||||
|
||||
(value)
|
||||
));
|
||||
}
|
58
maple-macro/src/tag.rs
Normal file
58
maple-macro/src/tag.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::attribute::Attribute;
|
||||
use crate::node::Node;
|
||||
use syn::TypePath;
|
||||
use syn::synom::Synom;
|
||||
|
||||
#[derive(Debug)]
|
||||
crate struct Tag {
|
||||
pub path: TypePath,
|
||||
pub attributes: Vec<Attribute>,
|
||||
pub body: Vec<Node>
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
pub fn new(open: TypePath, close: Option<TypePath>, attrs: Vec<Attribute>, body: Vec<Node>) -> Result<Self, String> {
|
||||
if let Some(close_ref) = &close {
|
||||
if &open != close_ref {
|
||||
return Err(format!("Open({}) and closing({}) tags are not matching!", quote!{#open}, quote!{#close_ref}))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Tag {
|
||||
path: open,
|
||||
attributes: attrs,
|
||||
body
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Synom for Tag {
|
||||
named!(parse -> Self, do_parse!(
|
||||
t: alt!(
|
||||
do_parse!(
|
||||
punct!(<) >>
|
||||
open: syn!(TypePath) >>
|
||||
attrs: many0!(syn!(Attribute)) >>
|
||||
punct!(>) >>
|
||||
body: many0!(syn!(Node)) >>
|
||||
punct!(<) >> punct!(/) >>
|
||||
close: syn!(TypePath) >>
|
||||
punct!(>) >>
|
||||
|
||||
(match Tag::new(open, Some(close), attrs, body) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("{:?}", e)
|
||||
})
|
||||
) => {|x|x}
|
||||
|
|
||||
do_parse!(
|
||||
punct!(<) >>
|
||||
open: syn!(TypePath) >>
|
||||
attrs: many0!(syn!(Attribute)) >>
|
||||
punct!(/) >>
|
||||
punct!(>) >>
|
||||
(Tag::new(open, None, attrs, vec![]).unwrap())
|
||||
) => {|x|x}
|
||||
) >> (t)
|
||||
));
|
||||
}
|
10
maple-stdui/.gitignore
vendored
Normal file
10
maple-stdui/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
11
maple-stdui/Cargo.toml
Normal file
11
maple-stdui/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
name = "maple-stdui"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
maple-macro = {path = "../maple-macro"}
|
||||
maple-core = {path = "../maple-core"}
|
9
maple-stdui/src/lib.rs
Normal file
9
maple-stdui/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub mod panel;
|
||||
pub mod tabs;
|
||||
pub mod text;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::panel::*;
|
||||
pub use super::tabs;
|
||||
pub use super::text::*;
|
||||
}
|
16
maple-stdui/src/panel.rs
Normal file
16
maple-stdui/src/panel.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use maple_core::prelude::*;
|
||||
|
||||
pub struct Panel {
|
||||
props: PanelProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PanelProps {}
|
||||
|
||||
impl Component for Panel {
|
||||
type Props = PanelProps;
|
||||
|
||||
fn create(props: PanelProps) -> Self {
|
||||
Panel { props }
|
||||
}
|
||||
}
|
168
maple-stdui/src/tabs.rs
Normal file
168
maple-stdui/src/tabs.rs
Normal file
@ -0,0 +1,168 @@
|
||||
use std::ops::Deref;
|
||||
use maple_core::prelude::*;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TabsContext<T: Context> {
|
||||
inner: T,
|
||||
tabs: Rc<RefCell<Vec<Option<&'static str>>>>
|
||||
}
|
||||
|
||||
impl<T: Context> TabsContext<T> {
|
||||
pub fn add_tab(&self, tab_title: Option<&'static str>) {
|
||||
self.tabs.borrow_mut().push(tab_title);
|
||||
}
|
||||
|
||||
pub fn get_tabs(&self) -> Vec<Option<&'static str>> {
|
||||
return self.tabs.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Context> Context for TabsContext<T> {
|
||||
type Context = T;
|
||||
|
||||
fn unwrap(self) -> Self::Context {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn wrap(ctx: Self::Context) -> Self {
|
||||
Self {
|
||||
inner: ctx,
|
||||
tabs: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TabsBodyContext<T: Context> { inner: TabsContext<T> }
|
||||
|
||||
impl<T: Context> Context for TabsBodyContext<T> {
|
||||
type Context = TabsContext<T>;
|
||||
|
||||
fn unwrap(self) -> Self::Context {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn wrap(ctx: Self::Context) -> Self {
|
||||
Self {
|
||||
inner: ctx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Context> Deref for TabsBodyContext<T> {
|
||||
type Target = TabsContext<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
return &self.inner;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tabs {
|
||||
props: TabsProps
|
||||
}
|
||||
|
||||
pub enum TabsEvent {
|
||||
TabChange(usize)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TabsProps {
|
||||
pub on_tab_change: Option<Box<dyn Callback<Arg=usize>>>
|
||||
}
|
||||
|
||||
impl Component for Tabs {
|
||||
type Props = TabsProps;
|
||||
type Msg = TabsEvent;
|
||||
|
||||
fn create(props: TabsProps) -> Self {
|
||||
Tabs { props }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg) -> bool {
|
||||
match msg {
|
||||
TabsEvent::TabChange(idx) => {
|
||||
if let Some(cb) = &self.props.on_tab_change {
|
||||
cb.call(idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Header {
|
||||
props: HeaderProps,
|
||||
tabs_ctx: Option<TabsContext<DefaultContext>>
|
||||
}
|
||||
|
||||
pub enum HeaderEvent {
|
||||
Click(usize)
|
||||
}
|
||||
|
||||
impl ComponentMsg for HeaderEvent {
|
||||
type Component = Header;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HeaderProps {
|
||||
pub on_click: Option<Box<dyn Callback<Arg=usize>>>
|
||||
}
|
||||
|
||||
impl Component for Header {
|
||||
type Props = HeaderProps;
|
||||
type Msg = HeaderEvent;
|
||||
|
||||
fn create(props: HeaderProps) -> Self {
|
||||
Header {
|
||||
props,
|
||||
tabs_ctx: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg) -> bool {
|
||||
match msg {
|
||||
HeaderEvent::Click(idx) => {
|
||||
if let Some(cb) = &self.props.on_click {
|
||||
cb.call(idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Body {
|
||||
props: BodyProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BodyProps {}
|
||||
|
||||
impl Component for Body {
|
||||
type Props = BodyProps;
|
||||
|
||||
fn create(props: BodyProps) -> Self {
|
||||
Body { props }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tab {
|
||||
props: TabProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TabProps {
|
||||
pub title: Option<&'static str>
|
||||
}
|
||||
|
||||
impl Component for Tab {
|
||||
type Props = TabProps;
|
||||
|
||||
fn create(props: TabProps) -> Self {
|
||||
Tab { props }
|
||||
}
|
||||
}
|
27
maple-stdui/src/text.rs
Normal file
27
maple-stdui/src/text.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use std::borrow::Cow;
|
||||
use maple_core::prelude::Component;
|
||||
|
||||
pub struct Text {
|
||||
pub props: TextProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TextProps {
|
||||
pub text: Option<Cow<'static, str>>
|
||||
}
|
||||
|
||||
impl TextProps {
|
||||
pub fn new<T: Into<Cow<'static, str>>>(txt: T) -> Self {
|
||||
TextProps {
|
||||
text: Some(txt.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Text {
|
||||
type Props = TextProps;
|
||||
|
||||
fn create(props: TextProps) -> Self {
|
||||
Self { props }
|
||||
}
|
||||
}
|
3
maple-stdweb-ui/.gitignore
vendored
Normal file
3
maple-stdweb-ui/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
14
maple-stdweb-ui/Cargo.toml
Normal file
14
maple-stdweb-ui/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
name = "maple-stdweb-ui"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
maple-core = {path = "../maple-core"}
|
||||
maple-stdweb = {path = "../maple-stdweb"}
|
||||
maple-stdui = {path = "../maple-stdui"}
|
||||
maple-macro = {path = "../maple-macro"}
|
||||
maple-stdweb-ui = {path = "../maple-stdweb-ui"}
|
9
maple-stdweb-ui/src/lib.rs
Normal file
9
maple-stdweb-ui/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![feature(proc_macro_non_items, existential_type)]
|
||||
|
||||
pub mod panel;
|
||||
pub mod tabs;
|
||||
|
||||
pub use self::panel::*;
|
||||
pub use self::tabs::*;
|
||||
|
||||
pub use maple_stdweb_ui::HtmlEngine;
|
30
maple-stdweb-ui/src/panel.rs
Normal file
30
maple-stdweb-ui/src/panel.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use maple_core::prelude::*;
|
||||
use super::HtmlEngine;
|
||||
use maple_macro::view;
|
||||
use maple_stdweb::*;
|
||||
use maple_stdui::prelude::Panel;
|
||||
|
||||
impl View for Panel {
|
||||
type InputContext = DefaultContext;
|
||||
type OutputContext = DefaultContext;
|
||||
|
||||
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
|
||||
ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewX for Panel {
|
||||
type Engine = HtmlEngine;
|
||||
type ChildrenEngine = HtmlEngine;
|
||||
existential type Renderable: Renderable<Engine = HtmlEngine>;
|
||||
|
||||
fn build<C>(self, children: Option<C>) -> Self::Renderable
|
||||
where C: Renderable<Engine = Self::ChildrenEngine> + 'static
|
||||
{
|
||||
view! {
|
||||
<Div class="panel">
|
||||
{ ...children }
|
||||
</Div>
|
||||
}
|
||||
}
|
||||
}
|
111
maple-stdweb-ui/src/tabs.rs
Normal file
111
maple-stdweb-ui/src/tabs.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use maple_core::prelude::*;
|
||||
use maple_macro::view;
|
||||
use maple_stdweb::*;
|
||||
use maple_stdui::prelude::tabs::*;
|
||||
use super::HtmlEngine;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
|
||||
|
||||
impl View for Tabs {
|
||||
type InputContext = DefaultContext;
|
||||
type OutputContext = TabsContext<DefaultContext>;
|
||||
|
||||
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
|
||||
TabsContext::wrap(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Tabs {
|
||||
pub fn build<C>(self, children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
|
||||
where C: Renderable<Engine = HtmlEngine> + 'static
|
||||
{
|
||||
view! {
|
||||
<Div class="tabs">
|
||||
{ ... children }
|
||||
</Div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View for Header {
|
||||
type InputContext = TabsContext<DefaultContext>;
|
||||
type OutputContext = DefaultContext;
|
||||
|
||||
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
|
||||
self.tabs_ctx = Some(ctx.clone());
|
||||
|
||||
ctx.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn build<C>(self, _children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
|
||||
where C: Renderable<Engine = 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 for Body {
|
||||
type InputContext = TabsContext<DefaultContext>;
|
||||
type OutputContext = TabsBodyContext<DefaultContext>;
|
||||
|
||||
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
|
||||
TabsBodyContext::wrap(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Body {
|
||||
pub fn build<C>(self, children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
|
||||
where C: Renderable<Engine = HtmlEngine> + 'static
|
||||
{
|
||||
view! {
|
||||
<Div class="tab">
|
||||
{ ... children }
|
||||
</Div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View for Tab {
|
||||
type InputContext = TabsBodyContext<DefaultContext>;
|
||||
type OutputContext = DefaultContext;
|
||||
|
||||
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
|
||||
ctx.add_tab(self.props.title);
|
||||
ctx.unwrap().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn build<C>(self, children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
|
||||
where C: Renderable<Engine = HtmlEngine> + 'static
|
||||
{
|
||||
view! {
|
||||
<Div class="tab">
|
||||
{ ... children }
|
||||
</Div>
|
||||
}
|
||||
}
|
||||
}
|
3
maple-stdweb/.gitignore
vendored
Normal file
3
maple-stdweb/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
10
maple-stdweb/Cargo.toml
Normal file
10
maple-stdweb/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
name = "maple-stdweb"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
maple-core = {path = '../maple-core'}
|
30
maple-stdweb/src/button.rs
Normal file
30
maple-stdweb/src/button.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use maple_core::prelude::Component;
|
||||
|
||||
pub enum ButtonEvents {
|
||||
Click
|
||||
}
|
||||
|
||||
pub struct Button {
|
||||
props: ButtonProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ButtonProps {
|
||||
pub id: Option<&'static str>,
|
||||
pub name: Option<&'static str>,
|
||||
pub class: Option<&'static str>,
|
||||
pub click: Option<Box<Fn() -> ButtonEvents>>
|
||||
}
|
||||
|
||||
impl Component for Button {
|
||||
type Props = ButtonProps;
|
||||
type Msg = ButtonEvents;
|
||||
|
||||
fn create(props: ButtonProps) -> Self {
|
||||
Button { props }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
30
maple-stdweb/src/canvas.rs
Normal file
30
maple-stdweb/src/canvas.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use maple_core::prelude::Component;
|
||||
|
||||
pub enum CanvasEvents {
|
||||
Click
|
||||
}
|
||||
|
||||
pub struct Canvas {
|
||||
pub props: CanvasProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CanvasProps {
|
||||
pub id: Option<&'static str>,
|
||||
pub name: Option<&'static str>,
|
||||
pub class: Option<&'static str>,
|
||||
pub click: Option<Box<Fn() -> CanvasEvents>>
|
||||
}
|
||||
|
||||
impl Component for Canvas {
|
||||
type Props = CanvasProps;
|
||||
type Msg = CanvasEvents;
|
||||
|
||||
fn create(props: CanvasProps) -> Self {
|
||||
Canvas { props }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
23
maple-stdweb/src/canvas_comp/circle.rs
Normal file
23
maple-stdweb/src/canvas_comp/circle.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use maple_core::prelude::Component;
|
||||
|
||||
pub struct Circle {
|
||||
pub props: CircleProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CircleProps {
|
||||
pub id: Option<&'static str>,
|
||||
pub fill: Option<&'static str>,
|
||||
pub stroke: Option<&'static str>,
|
||||
pub cx: Option<u32>,
|
||||
pub cy: Option<u32>,
|
||||
pub r: Option<u32>
|
||||
}
|
||||
|
||||
impl Component for Circle {
|
||||
type Props = CircleProps;
|
||||
|
||||
fn create(props: CircleProps) -> Self {
|
||||
Circle { props }
|
||||
}
|
||||
}
|
5
maple-stdweb/src/canvas_comp/mod.rs
Normal file
5
maple-stdweb/src/canvas_comp/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod circle;
|
||||
mod rect;
|
||||
|
||||
pub use self::circle::{Circle, CircleProps};
|
||||
pub use self::rect::{Rect, RectProps};
|
25
maple-stdweb/src/canvas_comp/rect.rs
Normal file
25
maple-stdweb/src/canvas_comp/rect.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use maple_core::prelude::Component;
|
||||
|
||||
pub struct Rect {
|
||||
pub props: RectProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RectProps {
|
||||
pub id: Option<&'static str>,
|
||||
pub name: Option<&'static str>,
|
||||
pub fill: Option<&'static str>,
|
||||
pub stroke: Option<&'static str>,
|
||||
pub x1: Option<u32>,
|
||||
pub y1: Option<u32>,
|
||||
pub x2: Option<u32>,
|
||||
pub y2: Option<u32>
|
||||
}
|
||||
|
||||
impl Component for Rect {
|
||||
type Props = RectProps;
|
||||
|
||||
fn create(props: RectProps) -> Self {
|
||||
Rect { props }
|
||||
}
|
||||
}
|
32
maple-stdweb/src/div.rs
Normal file
32
maple-stdweb/src/div.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use maple_core::prelude::*;
|
||||
|
||||
pub enum DivEvents {
|
||||
Click
|
||||
}
|
||||
|
||||
pub struct Div {
|
||||
pub props: DivProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DivProps {
|
||||
pub id: Option<&'static str>,
|
||||
pub name: Option<&'static str>,
|
||||
pub class: Option<&'static str>,
|
||||
pub click: Option<Box<Fn() -> DivEvents>>
|
||||
}
|
||||
|
||||
impl Component for Div {
|
||||
type Props = DivProps;
|
||||
type Msg = DivEvents;
|
||||
|
||||
fn create(props: DivProps) -> Self {
|
||||
Div {
|
||||
props
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
39
maple-stdweb/src/lib.rs
Normal file
39
maple-stdweb/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
||||
mod div;
|
||||
mod span;
|
||||
mod button;
|
||||
mod canvas;
|
||||
mod canvas_comp;
|
||||
|
||||
pub use self::canvas_comp::*;
|
||||
pub use self::div::{Div, DivEvents, DivProps};
|
||||
pub use self::span::{Span, SpanProps};
|
||||
pub use self::button::{Button, ButtonEvents, ButtonProps};
|
||||
pub use self::canvas::{Canvas, CanvasEvents, CanvasProps};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use maple_core::prelude::Component;
|
||||
|
||||
pub struct Text {
|
||||
pub props: TextProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TextProps {
|
||||
pub text: Option<Cow<'static, str>>
|
||||
}
|
||||
|
||||
impl TextProps {
|
||||
pub fn new<T: Into<Cow<'static, str>>>(txt: T) -> Self {
|
||||
TextProps {
|
||||
text: Some(txt.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Text {
|
||||
type Props = TextProps;
|
||||
|
||||
fn create(props: TextProps) -> Self {
|
||||
Self { props }
|
||||
}
|
||||
}
|
38
maple-stdweb/src/span.rs
Normal file
38
maple-stdweb/src/span.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use maple_core::prelude::*;
|
||||
|
||||
pub enum SpanEvents {
|
||||
Click
|
||||
}
|
||||
|
||||
pub struct Span {
|
||||
props: SpanProps
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SpanProps {
|
||||
pub id: Option<&'static str>,
|
||||
pub name: Option<&'static str>,
|
||||
pub class: Option<&'static str>,
|
||||
pub click: Option<Box<dyn Callback<Arg=()>>>
|
||||
}
|
||||
|
||||
impl Component for Span {
|
||||
type Props = SpanProps;
|
||||
type Msg = SpanEvents;
|
||||
|
||||
fn create(props: SpanProps) -> Self {
|
||||
Span { props }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg) -> bool {
|
||||
match msg {
|
||||
SpanEvents::Click => {
|
||||
if let Some(cb) = &self.props.click {
|
||||
cb.call(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
false
|
||||
}
|
||||
}
|
13
maple/Cargo.toml
Normal file
13
maple/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
name = "maple"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
maple-core = {path = "../maple-core"}
|
||||
maple-macro = {path = "../maple-macro"}
|
||||
maple-stdui = {path = "../maple-stdui"}
|
||||
maple-stdweb = {path = "../maple-stdweb"}
|
9
maple/src/lib.rs
Normal file
9
maple/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[macro_use]
|
||||
extern crate maple_core;
|
||||
|
||||
pub mod prelude {
|
||||
pub use maple_core::prelude::*;
|
||||
pub use maple_macro::view;
|
||||
pub use maple_stdweb::*;
|
||||
pub use maple_stdui::*;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#![feature(associated_type_defaults, never_type, unsize)]
|
||||
|
||||
mod convert;
|
||||
use std::marker::PhantomData;
|
||||
pub use convert::*;
|
43
src/node.rs
43
src/node.rs
@ -1,43 +0,0 @@
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub struct Node <E, I, CHE, CH>
|
||||
where
|
||||
E: Engine,
|
||||
CHE: Engine,
|
||||
I: RenderImplementation<Engine = E, ChildrenEngine = CHE>,
|
||||
CH: Renderable<Engine = CHE>
|
||||
{
|
||||
item: I,
|
||||
children: CH
|
||||
}
|
||||
|
||||
impl<E, I, CHE, CH> Node<E, I, CHE, CH>
|
||||
where
|
||||
E: Engine,
|
||||
CHE: Engine,
|
||||
I: RenderImplementation<Engine = E, ChildrenEngine = CHE>,
|
||||
CH: Renderable<Engine = CHE>
|
||||
{
|
||||
pub fn new(item: I, children: CH) -> Self {
|
||||
Self {
|
||||
item,
|
||||
children
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, I, CHE, CH> Renderable for Node<E, I, CHE, CH>
|
||||
where
|
||||
E: Engine,
|
||||
CHE: Engine,
|
||||
I: RenderImplementation<Engine = E, ChildrenEngine = CHE>,
|
||||
CH: Renderable<Engine = CHE>
|
||||
{
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
self.item.render_impl(eng, &self.children)
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/// defines implementation of how the children elements binds to the component
|
||||
/// in dom backend it actually attaches/detaches elements to each other
|
||||
///
|
||||
///
|
||||
pub trait RenderImplementation {
|
||||
type Engine: Engine;
|
||||
type ChildrenEngine: Engine = Self::Engine;
|
||||
|
||||
fn render_impl<C>(&self, eng: &Self::Engine, children: &C)
|
||||
where C: Renderable<Engine = Self::ChildrenEngine>;
|
||||
}
|
||||
|
||||
/// Trait Renderable
|
||||
/// defines implementation of how the component should be rendered itself
|
||||
///
|
||||
///
|
||||
///
|
||||
pub trait Renderable {
|
||||
type Engine: Engine;
|
||||
|
||||
fn render(&self, eng: &Self::Engine);
|
||||
}
|
||||
|
||||
///
|
||||
/// for nodes without children
|
||||
///
|
||||
///
|
||||
pub struct Stub<E: Engine> {
|
||||
_e: PhantomData<E>
|
||||
}
|
||||
|
||||
impl<E: Engine> Renderable for Stub<E> {
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, _eng: &Self::Engine) {}
|
||||
}
|
||||
|
||||
impl<E: Engine> Stub<E> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
_e: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T> Renderable for T
|
||||
where E: Engine,
|
||||
T: RenderImplementation<Engine = E, ChildrenEngine = E>
|
||||
{
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
self.render_impl(eng, &Stub::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Renderable for Box<dyn Renderable<Engine = E>> {
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
(**self).render(eng);
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a, E> Renderable for Vec<&'a (dyn Renderable<Engine = E> + 'a)>
|
||||
where E: Engine + 'a
|
||||
{
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
for child in self.iter() {
|
||||
child.render(eng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <E, T> Renderable for Vec<T>
|
||||
where E: Engine,
|
||||
T: Renderable<Engine = E>
|
||||
{
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
for child in self.iter() {
|
||||
child.render(eng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <E, T> Renderable for Option<T>
|
||||
where E: Engine,
|
||||
T: Renderable<Engine = E>
|
||||
{
|
||||
type Engine = E;
|
||||
|
||||
fn render(&self, eng: &Self::Engine) {
|
||||
if let Some(inner) = self {
|
||||
inner.render(eng);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user