use std::iter;
use crate::vdom::*;
#[derive(PartialEq, Debug)]
pub enum Handler<Message> {
Msg(Message),
Event(fn(web_sys::Event) -> Option<Message>),
MsgEvent(Message, fn(Message, web_sys::Event) -> Option<Message>),
InputValue(fn(String) -> Option<Message>),
InputEvent(fn(web_sys::InputEvent) -> Option<Message>),
}
#[derive(Debug)]
pub struct Event<Message> {
trigger: &'static str,
handler: Handler<Message>,
}
#[derive(Debug)]
pub enum Node<Message, Command> {
Elem {
name: &'static str,
},
Text {
text: String,
},
Component {
msg: Message,
create: fn(Dispatcher<Message, Command>) -> Box<dyn Component<Message>>,
},
}
impl<Message, Command> Node<Message, Command> {
pub fn elem(name: &'static str) -> Self {
Node::Elem { name }
}
pub fn text(value: String) -> Self {
Node::Text { text: value }
}
pub fn component(msg: Message, create: fn(Dispatcher<Message, Command>) -> Box<dyn Component<Message>>) -> Self {
Node::Component { msg, create }
}
}
#[derive(PartialEq, Debug)]
pub struct Attr {
name: &'static str,
value: String,
}
impl Attr {
fn new(name: &'static str, value: &str) -> Self {
Attr { name, value: value.into() }
}
}
impl From<(&'static str, &str)> for Attr {
fn from(data: (&'static str, &str)) -> Self {
let (name, value) = data;
Attr::new(name, value)
}
}
impl From<(&'static str, String)> for Attr {
fn from(data: (&'static str, String)) -> Self {
let (name, value) = data;
Attr { name, value }
}
}
#[derive(Debug)]
pub struct Dom<Message = (), Command = (), Key = ()> {
element: Node<Message, Command>,
inner_html: Option<String>,
key: Option<Key>,
pub attributes: Vec<Attr>,
pub events: Vec<Event<Message>>,
pub children: Vec<Dom<Message, Command, Key>>,
}
impl<Message, Command, Key> Dom<Message, Command, Key> {
pub fn elem(element: &'static str) -> Self {
Dom {
element: Node::elem(element),
key: None,
events: vec![],
attributes: vec![],
children: vec![],
inner_html: None,
}
}
pub fn text(value: impl Into<String>) -> Self {
Dom {
element: Node::text(value.into()),
key: None,
events: vec![],
attributes: vec![],
children: vec![],
inner_html: None,
}
}
pub fn component(msg: Message, create: fn(Dispatcher<Message, Command>) -> Box<dyn Component<Message>>) -> Self {
Dom {
element: Node::component(msg, create),
key: None,
events: vec![],
attributes: vec![],
children: vec![],
inner_html: None,
}
}
pub fn key(mut self, key: impl Into<Key>) -> Self
{
self.key = Some(key.into());
self
}
pub unsafe fn inner_html(mut self, value: impl Into<String>) -> Self {
self.inner_html = Some(value.into());
self
}
pub fn attr(mut self, name: &'static str, value: impl Into<String>) -> Self {
self.attributes.push(Attr { name, value: value.into() });
self
}
pub fn event(self, trigger: &'static str, msg: Message) -> Self {
self.on(trigger, Handler::Msg(msg))
}
pub fn on(mut self, trigger: &'static str, handler: Handler<Message>) -> Self {
self.events.push(
Event {
trigger: trigger,
handler: handler,
}
);
self
}
pub fn onchange(self, handler: fn(String) -> Option<Message>) -> Self {
self.on("change", Handler::InputValue(handler))
}
pub fn oninput(self, handler: fn(web_sys::InputEvent) -> Option<Message>) -> Self {
self.on("input", Handler::InputEvent(handler))
}
pub fn push(mut self, child: impl Into<Dom<Message, Command, Key>>) -> Self {
self.children.push(child.into());
self
}
pub fn extend(mut self, iter: impl IntoIterator<Item = Dom<Message, Command, Key>>) -> Self {
self.children.extend(iter);
self
}
}
impl<Message, Command, K> Into<Dom<Message, Command, K>> for String {
fn into(self) -> Dom<Message, Command, K> {
Dom::text(self)
}
}
impl<Message, Command, K> Into<Dom<Message, Command, K>> for &str {
fn into(self) -> Dom<Message, Command, K> {
Dom::text(self)
}
}
impl<Message: Clone, Command, K> DomIter<Message, Command, K> for Dom<Message, Command, K> {
fn dom_iter<'a>(&'a self) -> Box<dyn Iterator<Item = DomItem<'a, Message, Command, K>> + 'a>
{
let iter = iter::once((&self.element, &self.key))
.map(|(node, key)| match node {
Node::Elem { name } => DomItem::Element { name, key: key.as_ref() },
Node::Text { text } => DomItem::Text(text),
Node::Component { msg, create } => DomItem::Component { msg: msg.clone(), create: *create, key: key.as_ref() },
})
.chain(self.attributes.iter()
.map(|attr| DomItem::Attr {
name: attr.name,
value: &attr.value
})
)
.chain(self.inner_html.iter()
.map(|html| DomItem::UnsafeInnerHtml(html))
)
.chain(self.events.iter()
.map(|Event { trigger, handler }|
DomItem::Event {
trigger: trigger,
handler: match handler {
Handler::Msg(m) => EventHandler::Msg(m),
Handler::Event(h) => EventHandler::Fn(*h),
Handler::MsgEvent(m, h) => EventHandler::FnMsg(m, *h),
Handler::InputValue(h) => EventHandler::InputValue(*h),
Handler::InputEvent(h) => EventHandler::InputEvent(*h),
},
}
)
)
.chain(self.children.iter()
.flat_map(|c| c.dom_iter())
)
.chain(iter::once(DomItem::Up));
Box::new(iter)
}
}
#[derive(Debug)]
pub struct DomVec<Message = (), Command = (), Key = ()>(Vec<Dom<Message, Command, Key>>);
impl<Message, Command, Key>
DomIter<Message, Command, Key>
for DomVec<Message, Command, Key>
where
Message: Clone + PartialEq,
{
fn dom_iter<'a>(&'a self) -> Box<dyn Iterator<Item = DomItem<'a, Message, Command, Key>> + 'a> {
Box::new(self.0.iter().flat_map(|i| i.dom_iter()))
}
}
impl<Message, Command, K> From<Vec<Dom<Message, Command, K>>> for DomVec<Message, Command, K> {
fn from(v: Vec<Dom<Message, Command, K>>) -> Self {
DomVec(v)
}
}
impl<Message, Command, K> Into<Vec<Dom<Message, Command, K>>> for DomVec<Message, Command, K> {
fn into(self) -> Vec<Dom<Message, Command, K>> {
self.0
}
}
impl<Message, Command, K> IntoIterator for DomVec<Message, Command, K> {
type Item = Dom<Message, Command, K>;
type IntoIter = ::std::vec::IntoIter<Dom<Message, Command, K>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}