2022-09-12 16:34:09 +02:00
|
|
|
use std::{
|
|
|
|
collections::{HashMap, HashSet},
|
|
|
|
hash::Hash,
|
|
|
|
marker::PhantomData,
|
2022-09-15 16:32:42 +02:00
|
|
|
ops::Deref,
|
2022-09-12 16:34:09 +02:00
|
|
|
};
|
|
|
|
use sycamore::view::IntoView as _;
|
|
|
|
|
|
|
|
use sycamore::{builder::prelude::*, prelude::*};
|
|
|
|
|
|
|
|
pub mod prelude {
|
|
|
|
pub use super::{IntoView, ViewProps, Viewable, Viewer, WebView};
|
|
|
|
pub use crate::components::Block;
|
|
|
|
pub use paste::paste;
|
|
|
|
pub use sycamore::prelude::*;
|
|
|
|
}
|
|
|
|
|
2022-09-15 16:32:42 +02:00
|
|
|
use crate::{
|
|
|
|
components::{Block, BlockProps},
|
|
|
|
util::WithContext,
|
|
|
|
};
|
2022-09-12 16:34:09 +02:00
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! viewable {
|
|
|
|
($type:ty => $viewer:ty) => {
|
|
|
|
impl<'a, G: Html> Viewable<'a, G> for $type {
|
|
|
|
type Viewer = $viewer;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Prop)]
|
|
|
|
pub struct ViewProps<'a, T> {
|
2022-09-12 18:03:34 +02:00
|
|
|
pub state: &'a T,
|
2022-09-12 16:34:09 +02:00
|
|
|
}
|
|
|
|
|
2022-09-12 18:03:34 +02:00
|
|
|
impl<'a, T> From<&'a T> for ViewProps<'a, T> {
|
|
|
|
fn from(state: &'a T) -> Self {
|
2022-09-12 16:34:09 +02:00
|
|
|
ViewProps { state }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait IntoView<'a, G: Html> {
|
|
|
|
fn view(self, cx: Scope<'a>) -> View<G>;
|
|
|
|
}
|
|
|
|
|
2022-09-12 18:03:34 +02:00
|
|
|
impl<'a, G: Html, T: Viewable<'a, G>> IntoView<'a, G> for &'a T
|
2022-09-12 16:34:09 +02:00
|
|
|
where
|
2022-09-12 18:03:34 +02:00
|
|
|
ViewProps<'a, T>: From<&'a T>,
|
2022-09-12 16:34:09 +02:00
|
|
|
{
|
|
|
|
fn view(self, cx: Scope<'a>) -> View<G> {
|
|
|
|
T::view(cx, self.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait WebView<'a, G: Html>: Sized {
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, Self>) -> View<G>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G, E, Type> WebView<'a, G> for Type
|
|
|
|
where
|
|
|
|
G: Html,
|
|
|
|
E: Viewer<'a, G, Type>,
|
|
|
|
Type: Viewable<'a, G, Viewer = E>,
|
|
|
|
{
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, Self>) -> View<G> {
|
|
|
|
E::view(cx, props)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Viewer<'a, G: Html, Type>: Sized {
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, Type>) -> View<G>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Viewable<'a, G: Html>: Sized {
|
|
|
|
type Viewer: Viewer<'a, G, Self>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct StringView;
|
|
|
|
|
|
|
|
impl<'a, G: Html, T: 'a> Viewer<'a, G, T> for StringView
|
|
|
|
where
|
|
|
|
T: sycamore::view::IntoView<G>,
|
|
|
|
{
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, T>) -> View<G> {
|
|
|
|
view! { cx,
|
2022-09-12 18:03:34 +02:00
|
|
|
(props.state.create())
|
2022-09-12 16:34:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
viewable!(String => StringView);
|
|
|
|
|
|
|
|
viewable!(i64 => StringView);
|
|
|
|
viewable!(i32 => StringView);
|
|
|
|
viewable!(isize => StringView);
|
|
|
|
|
|
|
|
viewable!(u64 => StringView);
|
|
|
|
viewable!(u32 => StringView);
|
|
|
|
viewable!(usize => StringView);
|
|
|
|
|
|
|
|
viewable!(f64 => StringView);
|
|
|
|
viewable!(f32 => StringView);
|
|
|
|
|
|
|
|
pub struct BoolView;
|
|
|
|
|
|
|
|
impl<'a, G: Html> Viewer<'a, G, bool> for BoolView {
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, bool>) -> View<G> {
|
|
|
|
view! { cx,
|
2022-09-12 18:03:34 +02:00
|
|
|
input(type="checkbox", checked=*props.state, disabled=true)
|
2022-09-12 16:34:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
viewable!(bool => BoolView);
|
|
|
|
|
|
|
|
pub struct VecView;
|
|
|
|
|
|
|
|
impl<'a, G, T, I> Viewer<'a, G, I> for VecView
|
|
|
|
where
|
|
|
|
G: Html,
|
|
|
|
T: for<'b> Viewable<'b, G> + Clone + PartialEq + 'a,
|
|
|
|
I: IntoIterator<Item = T> + FromIterator<T> + Clone,
|
|
|
|
{
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, I>) -> View<G> {
|
|
|
|
Block(
|
|
|
|
cx,
|
|
|
|
BlockProps {
|
|
|
|
title: "List".into(),
|
|
|
|
children: Children::new(cx, move |_| {
|
|
|
|
view! { cx,
|
|
|
|
//Block(title="List".into()) {
|
|
|
|
div {
|
2022-09-12 18:03:34 +02:00
|
|
|
(View::new_fragment(props.state.clone().into_iter().map(|x| create_ref(cx, x)).map(|x| {
|
2022-09-12 16:34:09 +02:00
|
|
|
div().c(x.view(cx)).c(br()).view(cx)
|
|
|
|
}).collect()))
|
|
|
|
}
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G, T> Viewable<'a, G> for Vec<T>
|
|
|
|
where
|
|
|
|
G: Html,
|
|
|
|
T: for<'b> Viewable<'b, G> + Clone + PartialEq + 'a,
|
|
|
|
{
|
|
|
|
type Viewer = VecView;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G, T> Viewable<'a, G> for HashSet<T>
|
|
|
|
where
|
|
|
|
G: Html,
|
|
|
|
T: for<'b> Viewable<'b, G> + Clone + PartialEq + Hash + Eq + 'a,
|
|
|
|
{
|
|
|
|
type Viewer = VecView;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G, K, V> Viewable<'a, G> for HashMap<K, V>
|
|
|
|
where
|
|
|
|
G: Html,
|
|
|
|
K: Clone + Hash + Eq,
|
|
|
|
V: Clone,
|
|
|
|
(K, V): for<'b> Viewable<'b, G> + Clone + PartialEq + Hash + Eq + 'a,
|
|
|
|
{
|
|
|
|
type Viewer = VecView;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct TupleView;
|
|
|
|
|
|
|
|
impl<'a, G: Html, A: for<'b> Viewable<'b, G> + Clone, B: for<'b> Viewable<'b, G> + Clone>
|
|
|
|
Viewer<'a, G, (A, B)> for TupleView
|
|
|
|
{
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, (A, B)>) -> View<G> {
|
|
|
|
view! { cx,
|
|
|
|
Block(title="Tuple".into()) {
|
2022-09-12 18:03:34 +02:00
|
|
|
(props.state.0.view(cx))
|
2022-09-13 18:39:38 +02:00
|
|
|
br()
|
2022-09-12 18:03:34 +02:00
|
|
|
(props.state.1.view(cx))
|
2022-09-12 16:34:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G, A, B> Viewable<'a, G> for (A, B)
|
|
|
|
where
|
|
|
|
G: Html,
|
|
|
|
A: for<'b> Viewable<'b, G> + Clone,
|
|
|
|
B: for<'b> Viewable<'b, G> + Clone,
|
|
|
|
{
|
|
|
|
type Viewer = TupleView;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OptionView;
|
|
|
|
|
|
|
|
impl<'a, G: Html, T: for<'b> Viewable<'b, G> + Clone> Viewer<'a, G, Option<T>> for OptionView {
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, Option<T>>) -> View<G> {
|
2022-09-12 18:03:34 +02:00
|
|
|
match props.state {
|
|
|
|
Some(x) => view! { cx, (x.view(cx)) },
|
2022-09-12 16:34:09 +02:00
|
|
|
None => view! { cx, "None" },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G: Html, T: for<'b> Viewable<'b, G> + Clone> Viewable<'a, G> for Option<T> {
|
|
|
|
type Viewer = OptionView;
|
|
|
|
}
|
2022-09-15 16:32:42 +02:00
|
|
|
|
|
|
|
pub struct ContextView;
|
|
|
|
|
|
|
|
impl<'a, G: Html, Ctx, T: for<'b> Viewable<'b, G> + Clone> Viewer<'a, G, WithContext<Ctx, T>>
|
|
|
|
for ContextView
|
|
|
|
{
|
|
|
|
fn view(cx: Scope<'a>, props: ViewProps<'a, WithContext<Ctx, T>>) -> View<G> {
|
|
|
|
props.state.deref().view(cx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, G: Html, Ctx, T: for<'b> Viewable<'b, G> + Clone> Viewable<'a, G> for WithContext<Ctx, T> {
|
|
|
|
type Viewer = ContextView;
|
|
|
|
}
|