lan-party-backend/web/src/components/event.rs

412 lines
11 KiB
Rust
Raw Normal View History

2022-09-09 20:19:18 +02:00
use std::{cell::RefCell, marker::PhantomData, str::FromStr};
use crate::components::Block;
use lan_party_core::event::{
free_for_all_game::FreeForAllGameSpec, team_game::TeamGameSpec, test::TestSpec, EventSpec,
EventTypeSpec,
};
2022-09-09 13:01:30 +02:00
use log::debug;
use paste::paste;
2022-09-09 20:19:18 +02:00
use sycamore::{builder::prelude::*, component::Prop, prelude::*};
2022-09-06 11:55:22 +02:00
macro_rules! editable {
($type:ty => $editor:ty) => {
impl<'d, 's, 't, G: Html> Editable<'d, 's, 't, G> for $type {
type Editor = $editor;
}
};
}
2022-09-06 13:06:51 +02:00
macro_rules! edit_fields {
($cx:ident, $(($name:expr, $prop:expr)),* $(,)?) => {
view! { $cx,
2022-09-06 13:06:51 +02:00
$(
p {
2022-09-09 13:01:30 +02:00
label { ($name) }
($prop.edit($cx))
}
2022-09-06 13:06:51 +02:00
)*
}
};
}
macro_rules! link_fields {
($cx:ident, $($field:ident),* $(,)? => $state:ident as $t:ident) => {
2022-09-09 16:55:30 +02:00
$(let $field = create_signal($cx, $state.get_untracked().$field.clone());)*
create_effect($cx, || {
$state.set($t {
$($field: $field.get().as_ref().clone(),)*
..Default::default()
});
});
2022-09-06 16:49:49 +02:00
};
}
2022-09-06 13:06:51 +02:00
macro_rules! edit_struct {
($struct:ident => $(($name:expr, $prop:ident)),* $(,)?) => {
paste! {
pub struct [<$struct Edit>];
impl<'d, 's, 't, G: Html> Editor<'d, 's, 't, G, $struct> for [<$struct Edit>] {
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, $struct>) -> View<G> {
let state = props.state;
link_fields!(cx, $($prop,)* => state as $struct);
view! { cx,
Block(title=stringify!($struct).into()) {
(edit_fields!(cx, $(($name, $prop),)*))
}
}
2022-09-06 16:49:49 +02:00
}
}
editable!($struct => [<$struct Edit>]);
2022-09-06 16:49:49 +02:00
}
};
}
2022-09-09 13:01:30 +02:00
macro_rules! link_variants {
($cx:ident, $selected:ident => $($index:literal: $var_name:ident = $variant:ident: $var_type:ty),* $(,)? => $state:ident as $t:ident) => {
let $selected = create_signal($cx, String::from("0"));
2022-09-09 16:55:30 +02:00
$(let $var_name = if let $t::$variant(v) = $state.get_untracked().as_ref().clone() {
2022-09-09 13:01:30 +02:00
create_signal($cx, v.clone())
} else {
create_signal($cx, <$var_type>::default())
};)*
create_effect($cx, || {
debug!("{:#?}", $selected.get());
match $selected.get().as_str() {
$(stringify!($index) => $state.set($t::$variant($var_name.get().as_ref().clone())),)*
_ => unreachable!()
}
});
};
}
macro_rules! edit_enum {
($enum:ident => $selected:ident => $($index:literal: $var_name:ident = $variant:ident: $var_type:ty),* $(,)?) => {
paste! {
pub struct [<$enum Edit>];
impl<'d, 's, 't, G: Html> Editor<'d, 's, 't, G, $enum> for [<$enum Edit>] {
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, $enum>) -> View<G> {
2022-09-09 13:01:30 +02:00
let state = props.state;
link_variants!(cx, $selected =>
$($index: $var_name = $variant: $var_type,)*
=> state as $enum
);
view! { cx,
Block(title=stringify!($enum).to_string()) {
select(bind:value=$selected) {
$(option(value={stringify!($index)}, selected=$index==0) { (stringify!($variant)) })*
}
2022-09-09 16:55:30 +02:00
(match $selected.get().as_str() {
$(stringify!($index) => $var_name.edit(cx),)*
_ => unreachable!()
2022-09-09 13:01:30 +02:00
})
}
}
}
}
editable!($enum => [<$enum Edit>]);
}
};
}
#[derive(Prop)]
pub struct EditProps<'a, T> {
pub state: &'a Signal<T>,
2022-09-06 11:55:22 +02:00
}
impl<'a, T> From<&'a Signal<T>> for EditProps<'a, T> {
fn from(state: &'a Signal<T>) -> Self {
EditProps { state }
2022-09-06 11:55:22 +02:00
}
}
pub trait IntoEdit<'d, 's, G: Html> {
fn edit(self, cx: BoundedScope<'d, 's>) -> View<G>;
2022-09-06 11:55:22 +02:00
}
impl<'d, 's, 't, G: Html, T: Editable<'d, 's, 't, G> + 't> IntoEdit<'d, 's, G> for &'d Signal<T>
2022-09-06 16:49:49 +02:00
where
EditProps<'d, T>: From<&'d Signal<T>>,
2022-09-06 16:49:49 +02:00
{
fn edit(self, cx: BoundedScope<'d, 's>) -> View<G> {
T::edit(cx, self.into())
2022-09-06 11:55:22 +02:00
}
}
pub trait Edit<'d, 's, 't, G: Html>: Sized {
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, Self>) -> View<G>;
2022-09-06 16:49:49 +02:00
}
impl<'d, 's, 't, G, E, Type> Edit<'d, 's, 't, G> for Type
2022-09-09 16:55:30 +02:00
where
G: Html,
E: Editor<'d, 's, 't, G, Type>,
Type: Editable<'d, 's, 't, G, Editor = E> + 't,
2022-09-09 16:55:30 +02:00
{
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, Self>) -> View<G> {
E::edit(cx, props)
2022-09-06 11:55:22 +02:00
}
}
2022-09-06 16:49:49 +02:00
pub trait Editor<'d, 's, 't, G: Html, Type: 't>: Sized {
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, Type>) -> View<G>;
}
pub trait Editable<'d, 's, 't, G: Html>: Sized + 't {
type Editor: Editor<'d, 's, 't, G, Self>;
}
edit_struct!(EventSpec => ("Name", name), ("Description", description), ("Event type", event_type));
2022-09-09 13:01:30 +02:00
edit_enum!(EventTypeSpec => selected =>
0: test = Test: TestSpec,
1: team_game = TeamGame: TeamGameSpec,
2: free_for_all_game = FreeForAllGame: FreeForAllGameSpec
);
edit_struct!(TestSpec => ("Number of players", num_players));
2022-09-09 16:55:30 +02:00
edit_struct!(TeamGameSpec => ("Win rewards", win_rewards));
edit_struct!(FreeForAllGameSpec => );
pub struct StringEdit;
impl<'d, 's, 't, G: Html> Editor<'d, 's, 't, G, String> for StringEdit {
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, String>) -> View<G> {
view! { cx,
2022-09-09 13:01:30 +02:00
input(bind:value=props.state)
}
}
2022-09-06 16:49:49 +02:00
}
editable!(String => StringEdit);
pub struct StubEdit;
impl<'d, 's, 't, G: Html, T> Editor<'d, 's, 't, G, T> for StubEdit
where
T: Editable<'d, 's, 't, G, Editor = StubEdit>,
{
fn edit(cx: BoundedScope<'d, 's>, _props: EditProps<'d, T>) -> View<G> {
view! { cx,
2022-09-09 13:01:30 +02:00
i { "Editor Unimplemented" }
}
}
}
pub struct InputEdit;
impl<'d, 's, 't, G: Html, T> Editor<'d, 's, 't, G, T> for InputEdit
where
T: Editable<'d, 's, 't, G, Editor = InputEdit> + FromStr + ToString + Default,
{
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, T>) -> View<G> {
2022-09-09 16:55:30 +02:00
let value = create_signal(cx, props.state.get_untracked().to_string());
2022-09-09 13:01:30 +02:00
create_effect(cx, || {
props
.state
.set((*value.get()).parse().unwrap_or(T::default()))
});
view! { cx,
2022-09-09 13:01:30 +02:00
input(bind:value=value)
}
}
}
editable!(i64 => InputEdit);
editable!(i32 => InputEdit);
editable!(isize => InputEdit);
editable!(u64 => InputEdit);
editable!(u32 => InputEdit);
editable!(usize => InputEdit);
editable!(f64 => InputEdit);
editable!(f32 => InputEdit);
pub struct BoolEdit;
impl<'d, 's, 't, G: Html> Editor<'d, 's, 't, G, bool> for BoolEdit {
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, bool>) -> View<G> {
view! { cx,
input(type="checkbox", bind:checked=props.state)
}
}
}
editable!(bool => BoolEdit);
2022-09-09 13:01:30 +02:00
2022-09-09 16:55:30 +02:00
pub struct VecEdit;
impl<'d, 's, 't, 'c, G, T, I> Editor<'d, 's, 't, G, I> for VecEdit
2022-09-09 16:55:30 +02:00
where
't: 'c,
's: 'd,
'd: 's,
't: 'd,
2022-09-09 16:55:30 +02:00
G: Html,
T: Editable<'c, 's, 't, G> + Clone + PartialEq + 't,
I: IntoIterator<Item = T> + FromIterator<T> + Clone + 't,
2022-09-09 16:55:30 +02:00
{
fn edit(cx: BoundedScope<'d, 's>, props: EditProps<'d, I>) -> View<G> {
let vec: &'s Signal<Vec<&'s Signal<T>>> = create_signal(
2022-09-09 16:55:30 +02:00
cx,
props
.state
.get_untracked()
.as_ref()
.clone()
.into_iter()
.map(|x| create_signal(cx, x))
.collect::<Vec<_>>(),
);
//let signal = create_signal(cx, 0);
//let vec2 = vec.get().as_ref().clone();
//let signal = create_ref(cx, vec.get(0).unwrap());
create_effect(cx, || {
props.state.set(
vec.get()
.iter()
.cloned()
.map(|x| x.get().as_ref().clone())
.collect(),
)
});
2022-09-09 20:19:18 +02:00
let test = Indexed(
cx,
IndexedProps::builder()
.iterable(vec)
.view(|cx: BoundedScope<'s, 's>, x: &'s Signal<T>| {
2022-09-09 20:19:18 +02:00
//view! { cx, (T::edit(cx, EditProps { state: x })) }
//T::edit(cx, EditProps { state: x })
view! { cx, }
2022-09-09 20:19:18 +02:00
})
.build(),
);
test
2022-09-09 16:55:30 +02:00
/*
2022-09-09 20:19:18 +02:00
//view! { cx, "Vec" }
2022-09-09 16:55:30 +02:00
view! { cx,
Indexed(
iterable=vec,
2022-09-09 20:19:18 +02:00
view=|cx: BoundedScope<'_, 'a>, x: &'a Signal<T>| {
2022-09-09 16:55:30 +02:00
view! { cx,
2022-09-09 20:19:18 +02:00
//(T::edit(cx, EditProps { state: x }))
(something_with_signal(cx, EditProps { state: x }))
2022-09-09 16:55:30 +02:00
}
},
)
}
*/
2022-09-09 20:19:18 +02:00
/*
view! { cx,
(View::new_fragment((vec.get().try_borrow().unwrap().clone().into_iter().map(|s: &'a Signal<T>| {
view! { cx, (T::edit(cx, s.into())) }
})).collect()))
}
*/
2022-09-09 16:55:30 +02:00
}
}
2022-09-09 20:19:18 +02:00
#[component]
pub fn VecTest<
's,
'c,
'd,
't,
2022-09-09 20:19:18 +02:00
G: Html,
T: 't + PartialEq + Clone + Editable<'s, 'd, 't, G, Editor = E>,
E: Editor<'s, 'd, 't, G, T>,
2022-09-09 20:19:18 +02:00
>(
cx: BoundedScope<'d, 's>,
props: EditProps<'d, Vec<T>>,
2022-09-09 20:19:18 +02:00
) -> View<G> {
/*
let signals: &'a Signal<Vec<&'a Signal<String>>> = create_signal(
cx,
vec![
create_signal(cx, String::new()),
create_signal(cx, String::new()),
],
);
*/
let signals = create_signal(
cx,
props
.state
.get()
.iter()
.cloned()
.map(|s| create_signal(cx, s))
.collect::<Vec<_>>(),
);
view! { cx,
Indexed(
iterable=signals,
view=|cx: BoundedScope<'_, 'd>, signal: &'d Signal<T>| {
2022-09-09 20:19:18 +02:00
view! { cx,
(something_with_signal(cx, EditProps { state: signal }))
2022-09-09 20:19:18 +02:00
//(E::edit(cx, EditProps { state: signal }))
//(T::edit(cx, EditProps { state: signal }))
//(signal.edit(cx))
}
},
)
}
}
pub struct SignalWrapper<'a, T>(&'a Signal<T>);
#[component]
pub fn something_with_signal<'a, 'b, 'c, G: Html, T: 'a + Editable<'b, 'c, 'a, G>>(
2022-09-09 20:19:18 +02:00
cx: Scope<'a>,
signal: EditProps<'a, T>,
) -> View<G> {
drop(signal);
view! { cx, "Something" }
}
impl<'d, 's, 't, 'c, G, T> Editable<'d, 's, 't, G> for Vec<T>
2022-09-09 16:55:30 +02:00
where
't: 'c,
's: 'd,
'd: 's,
't: 'd,
2022-09-09 16:55:30 +02:00
G: Html,
T: Editable<'c, 's, 't, G> + Clone + PartialEq + 't,
2022-09-09 16:55:30 +02:00
{
type Editor = VecEdit;
}
#[derive(Default, Clone, Debug)]
2022-09-09 13:01:30 +02:00
pub struct Test {
inner: TestInner,
inner2: TestInner,
}
edit_struct!(Test => ("Inner", inner), ("Inner 2", inner2));
2022-09-09 16:55:30 +02:00
#[derive(Default, Clone, Debug)]
2022-09-09 13:01:30 +02:00
pub struct TestInner {
some_text: String,
some_number: usize,
}
edit_struct!(TestInner => ("Text", some_text), ("Number", some_number));