From 53cb2a22b17e98767ca3b5d37fcb736374bbb918 Mon Sep 17 00:00:00 2001 From: Daan Vanoverloop Date: Tue, 6 Sep 2022 11:55:22 +0200 Subject: [PATCH] Reactive experiments --- core/src/event.rs | 22 ++- web/dist/index.html | 6 +- web/src/components/event.rs | 353 +++++++++++++++++++++++++++++++++--- web/src/components/mod.rs | 4 +- web/src/pages/events.rs | 16 +- 5 files changed, 357 insertions(+), 44 deletions(-) diff --git a/core/src/event.rs b/core/src/event.rs index ded74b5..e8a4b51 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -48,7 +48,7 @@ impl Event { /// # EventSpec /// /// A specification of an event -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EventSpec { @@ -63,7 +63,7 @@ macro_rules! events { /// # EventTypeSpec /// /// A specification of an event type - #[derive(Clone, Debug)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum EventTypeSpec { @@ -146,7 +146,7 @@ pub trait EventTrait { } } -mod test { +pub mod test { use super::*; #[derive(Clone, Debug)] @@ -156,7 +156,7 @@ mod test { pub num_players: i64, } - #[derive(Clone, Debug)] + #[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TestSpec { @@ -195,7 +195,7 @@ mod test { } } -mod team_game { +pub mod team_game { use super::{ free_for_all_game::{ FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate, @@ -216,7 +216,7 @@ mod team_game { pub ffa_game: FreeForAllGame, } - #[derive(Clone, Debug)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TeamGameSpec { @@ -339,7 +339,7 @@ mod team_game { } } -mod free_for_all_game { +pub mod free_for_all_game { use std::collections::HashSet; use super::*; @@ -385,7 +385,7 @@ mod free_for_all_game { vec![-3, -2, -1] } - #[derive(Clone, Debug)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FreeForAllGameSpec { @@ -554,3 +554,9 @@ mod free_for_all_game { } events!(test => Test, team_game => TeamGame, free_for_all_game => FreeForAllGame); + +impl Default for EventTypeSpec { + fn default() -> Self { + Self::Test(test::TestSpec::default()) + } +} diff --git a/web/dist/index.html b/web/dist/index.html index 824f509..722323c 100644 --- a/web/dist/index.html +++ b/web/dist/index.html @@ -4,9 +4,9 @@ Yew App - - + + - \ No newline at end of file + \ No newline at end of file diff --git a/web/src/components/event.rs b/web/src/components/event.rs index bf913f4..025a746 100644 --- a/web/src/components/event.rs +++ b/web/src/components/event.rs @@ -1,31 +1,110 @@ -use lan_party_core::event::*; +use crate::components::*; +use std::collections::{HashMap, HashSet}; + +use lan_party_core::event::{ + free_for_all_game::FreeForAllGame, + team_game::TeamGame, + test::{Test, TestSpec}, + *, +}; +use wasm_bindgen::JsValue; use yew::prelude::*; +use crate::{bind, bind_change, bind_value, clone, clone_cb}; + +macro_rules! view_fields { + ($(($name:expr, $prop:expr)),* $(,)?) => { + html! { + <> + $( +

+ { $name } + { $prop.view() } +

+ )* + + } + }; +} + +macro_rules! view_struct { + ($struct:path: $title:expr => $(($name:expr, $prop:ident)),* $(,)?) => { + impl View for $struct { + fn view(&self) -> Html { + html! { + + { view_fields!( + $(($name, self.$prop),)* + )} + + } + } + } + }; + ($struct:path as $self:ident => $body:expr) => { + impl View for $struct { + fn view(&$self) -> Html { + $body + } + } + }; +} + +macro_rules! view_enum_simple { + ($enum:path: $($variant:ident),* $(,)?) => { + impl View for $enum { + fn view(&self) -> Html { + html! { + {match self { + $( + Self::$variant(i) => html! { + <> + { i.view() } + + }, + )* + }} + } + } + } + }; +} + #[derive(Debug, Clone, Properties, PartialEq)] pub struct BlockProps { + title: String, children: Children, } #[function_component(Block)] pub fn block(props: &BlockProps) -> Html { + let open = use_state(|| false); + let mut class = classes!("overflow-hidden"); + + if !*open { + class.push("max-h-1"); + } + html! { + <>
- {for props.children.iter()} +

move |_| open.set(!*open))}>{ &props.title }

+
+
+
+ {for props.children.iter()} +
+
+ } } -pub trait RenderEvent { +pub trait View { fn view(&self) -> Html; } -impl RenderEvent for String { - fn view(&self) -> Html { - html! { self } - } -} - -impl RenderEvent for bool { +impl View for bool { fn view(&self) -> Html { html! { @@ -33,33 +112,249 @@ impl RenderEvent for bool { } } -impl RenderEvent for Vec { +macro_rules! view_html { + ($t:ty) => { + impl View for $t { + fn view(&self) -> Html { + html! { self } + } + } + }; +} + +view_html!(i64); +view_html!(i32); +view_html!(isize); +view_html!(u64); +view_html!(u32); +view_html!(usize); +view_html!(f64); +view_html!(f32); +view_html!(String); +view_html!(&'static str); + +macro_rules! view_iter { + ($t:ident => $type:ty) => { + impl<$t: View> View for $type { + fn view(&self) -> Html { + html! { + +
    + { self.iter().map(|x| html! {
  • { x.view() }
  • }).collect::() } +
+
+ } + } + } + }; +} + +view_iter!(T => Vec); +view_iter!(T => HashSet); +view_iter!(T => &[T]); + +impl View for Option { fn view(&self) -> Html { - html! { - self.iter().map(|x| x.view()).collect::() + match self { + Some(content) => content.view(), + None => html! { "None" }, } } } -impl RenderEvent for &[T] { +impl View for HashMap { fn view(&self) -> Html { html! { - self.iter().map(|x| x.view()).collect::() - } - } -} - -impl RenderEvent for lan_party_core::event::Event { - fn view(&self) -> Html { - html! { - - { "Name: " } - { self.name.view() }
- { "Description: " } - { self.description.view() }
- { "Concluded: " } - { self.concluded.view() }
+ + {self.iter().map(|(k, v)| { + html! {

{k.view()}{ ": " }{v.view()}

} + }).collect::()}
} } } + +view_struct!( + lan_party_core::event::Event: "Event" => + ("Name: ", name), + ("Description: ", description), + ("Concluded: ", concluded), + ("Event type: ", event_type), +); + +view_enum_simple!( + lan_party_core::event::EventType: Test, + TeamGame, + FreeForAllGame +); + +view_struct!( + lan_party_core::event::test::Test: "Test" => + ("Number of players: ", num_players) +); + +view_struct!(FreeForAllGame as self => + html! { + + { view_fields!(("Ranking: ", self.ranking)) } + { view_fields!( + ("Participants: ", self.spec.participants), + ("Win rewards: ", self.spec.win_rewards), + ("Lose rewards: ", self.spec.lose_rewards), + ) } + + } +); + +view_enum_simple!( + lan_party_core::event::free_for_all_game::FreeForAllGameRanking: Ranking, + Scores +); + +view_struct!(TeamGame as self => + html! { + + { view_fields!(("Teams: ", self.teams)) } + { view_fields!(("Ranking: ", self.ffa_game.ranking)) } + { view_fields!( + ("Participants: ", self.ffa_game.spec.participants), + ("Win rewards: ", self.ffa_game.spec.win_rewards), + ("Lose rewards: ", self.ffa_game.spec.lose_rewards), + ) } + + } +); + +#[derive(PartialEq, Properties)] +pub struct EditProps { + pub state: UseStateHandle, +} + +pub trait Edit { + fn edit(&self) -> Html; +} + +#[function_component(EventSpecEdit)] +pub fn event_spec_edit(props: &EditProps) -> Html { + let state = props.state.clone(); + let name = use_state(|| state.name.clone()); + let description = use_state(|| state.description.clone()); + + use_effect_with_deps( + clone!(name, description, state => + move |_| { + web_sys::console::log_1(&JsValue::from("Update EventSpec")); + state.set(EventSpec { + name: (*name).clone(), + description: (*description).clone(), + ..Default::default() + }); + || { drop(state); drop(name); drop(description) } + } + ), + (name.clone(), description.clone()), + ); + + html! { + <> + { "Name: " } + { name.edit() } + { "Description: " } + { description.edit() } + + } +} + +impl Edit for UseStateHandle { + fn edit(&self) -> Html { + html!() + } +} + +#[function_component(StringEdit)] +pub fn string_edit(props: &EditProps) -> Html { + let string = props.state.clone(); + + html! { + + } +} + +impl Edit for UseStateHandle { + fn edit(&self) -> Html { + html!() + } +} + +/* +pub trait Edit { + type Type; + + fn to_edit(t: Self::Type) -> Self; + fn edit(&self) -> Html; + fn build(&self) -> Self::Type; +} + +pub struct EventSpecEditHandle { + name: UseStateHandle, + description: UseStateHandle, +} + +impl Edit for UseStateHandle { + type Type = String; + + fn to_edit(t: Self::Type) -> Self { + use_state(|| t) + } + + fn edit(&self) -> Html { + let this = self.clone(); + + html! { + + } + } + + fn build(&self) -> Self::Type { + (*self.clone()).clone() + } +} + +impl Edit for EventSpecEditHandle { + type Type = EventSpec; + + fn to_edit(t: Self::Type) -> Self { + Self { + name: use_state(|| t.name), + description: use_state(|| t.description), + } + } + + fn edit(&self) -> Html { + html! { + <> + { "Name: " } + { self.name.edit() } + { "Description: " } + { self.description.edit() } + + } + } + + fn build(&self) -> Self::Type { + let test_spec = TestSpec { num_players: 0 }; + + Self::Type { + name: self.name.build(), + description: self.description.build(), + event_type: EventTypeSpec::Test(test_spec), + } + } +} +*/ diff --git a/web/src/components/mod.rs b/web/src/components/mod.rs index f7fc7ee..1202e50 100644 --- a/web/src/components/mod.rs +++ b/web/src/components/mod.rs @@ -1,9 +1,9 @@ mod button; -mod event; +pub mod event; mod table; pub use button::Button; -pub use event::RenderEvent; +pub use event::View; pub use table::Table; use yew::{function_component, html, Children, Properties}; diff --git a/web/src/pages/events.rs b/web/src/pages/events.rs index 00d6682..c2d0dd3 100644 --- a/web/src/pages/events.rs +++ b/web/src/pages/events.rs @@ -1,12 +1,17 @@ use crate::{ - components::{Page, RenderEvent}, + components::{ + event::{Edit, EventSpecEdit}, + Button, Page, View, + }, init, }; +use lan_party_core::event::EventSpec; +use wasm_bindgen::JsValue; use wasm_bindgen_futures::spawn_local; use yew::prelude::*; use yew_hooks::*; -use crate::{clone, util::api_request}; +use crate::{clone, clone_cb, util::api_request}; #[function_component(EventsPage)] pub fn events_page() -> Html { @@ -19,9 +24,16 @@ pub fn events_page() -> Html { .unwrap()) }); + //let edit_event = use_state(|| EventSpecEditHandle::to_edit(EventSpec::default())); + let event_spec = use_state(|| EventSpec::default()); + html! { { events.view() } + + { event_spec.edit() } + +