use crate::components::*; use std::collections::{HashMap, HashSet}; use lan_party_core::event::{ free_for_all_game::{FreeForAllGame, FreeForAllGameRanking, FreeForAllGameSpec}, team_game::{TeamGame, TeamGameSpec}, test::{Test, TestSpec}, *, }; use paste::paste; 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! { <>

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


{for props.children.iter()}
} } pub trait View { fn view(&self) -> Html; } impl View for bool { fn view(&self) -> Html { html! { } } } pub trait ViewPlain: Into + std::fmt::Display {} impl View for T where T: ViewPlain, { fn view(&self) -> Html { self.into() } } impl ViewPlain for i64 {} impl ViewPlain for i32 {} impl ViewPlain for isize {} impl ViewPlain for u64 {} impl ViewPlain for u32 {} impl ViewPlain for usize {} impl ViewPlain for f64 {} impl ViewPlain for f32 {} impl ViewPlain for String {} impl<'a> ViewPlain for &'a str {} macro_rules! view_iter { ($t:ident => $type:ty) => { impl<$t: View> View for $type { fn view(&self) -> Html { html! { } } } }; } view_iter!(T => Vec); view_iter!(T => HashSet); view_iter!(T => &[T]); impl View for Option { fn view(&self) -> Html { match self { Some(content) => content.view(), None => html! { "None" }, } } } impl View for HashMap { fn view(&self) -> Html { html! { {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), ) } } ); macro_rules! edit_fields { ($(($name:expr, $prop:expr)),* $(,)?) => { html! { <> $(

{ $name } { $prop.edit() }

)* } }; } macro_rules! link_fields { ($($field:ident),* $(,)? => $state:ident as $t:ident) => { $(let $field = use_state(|| $state.$field.clone());)* use_effect_with_deps( clone!($($field,)* $state => move |_| { $state.set($t { $($field: (*$field).clone(),)* ..Default::default() }); || { $(drop($field);)* drop($state); } } ), ($($field.clone(),)*), ); }; } macro_rules! link_variants { ($selected:ident => $($index:literal: $var_name:ident = $variant:ident: $var_type:ty),* $(,)? => $state:ident as $t:ident) => { let $selected = use_state(|| 0 as usize); $(let $var_name = if let $t::$variant(v) = &*$state { use_state(|| v.clone()) } else { use_state(|| <$var_type>::default()) };)* use_effect_with_deps( clone!($($var_name,)* $state, $selected => move |_| { match *$selected { $($index => $state.set($t::$variant((*$var_name).clone())),)* _ => unreachable!() } || { $(drop($var_name);)* drop($selected); drop($state); } } ), ($($var_name.clone(),)* $selected.clone()), ); }; } macro_rules! edit_struct { ($struct:ident => $(($name:expr, $prop:ident)),* $(,)?) => { paste! { #[function_component([<$struct Edit>])] pub fn [<$struct:lower _edit>](props: &EditProps<$struct>) -> Html { let state = props.state.clone(); link_fields!($($prop,)* => state as $struct); html! { { edit_fields!($(($name, $prop)),*) } } } impl Editable for $struct { type Edit = [<$struct Edit>]; } } }; } macro_rules! edit_enum { ($enum:ident => $selected:ident => $($index:literal: $var_name:ident = $variant:ident: $var_type:ty),* $(,)?) => { paste! { #[function_component([<$enum Edit>])] pub fn [<$enum:lower _edit>](props: &EditProps<$enum>) -> Html { let state = props.state.clone(); link_variants!($selected => $($index: $var_name = $variant: $var_type,)* => state as $enum ); html! { { match &*state { $($enum::$variant(_) => $var_name.edit(),)* }} } } impl Editable for $enum { type Edit = [<$enum Edit>]; } } }; } #[derive(PartialEq, Properties)] pub struct EditProps { pub state: UseStateHandle, } pub trait Edit { fn edit(&self) -> Html; } impl>, Type: Editable + PartialEq> Edit for UseStateHandle { fn edit(&self) -> Html { html!() } } pub trait Editable { type Edit; } #[function_component(InputEdit)] pub fn input_edit(props: &EditProps) -> Html where T: PartialEq + Clone + 'static, Binding: From>, { let string = props.state.clone(); html! { } } impl Editable for T where T: PartialEq + Clone + 'static, Binding: From>, { type Edit = InputEdit; } #[function_component(Stub)] pub fn stub(props: &EditProps) -> Html { html! { "stub" } } impl Editable for Vec { type Edit = Stub>; } impl Editable for HashMap { type Edit = Stub>; } edit_struct!(EventSpec => ("Name: ", name), ("Description: ", description), ("Event type: ", event_type)); edit_struct!(TestSpec => ("Number of players: ", num_players)); edit_struct!(TeamGameSpec => ); edit_struct!(FreeForAllGameSpec => ); edit_enum!(EventTypeSpec => selected => 0: test = Test: TestSpec, 1: team_game = TeamGame: TeamGameSpec, 2: free_for_all_game = FreeForAllGame: FreeForAllGameSpec ); edit_enum!(FreeForAllGameRanking => selected => 0: ranking = Ranking: Vec, 1: scores = Scores: HashMap, ); /* #[function_component(EventTypeSpecEdit)] pub fn event_type_spec_edit(props: &EditProps) -> Html { let state = props.state.clone(); link_variants!(selected => 0: test = Test: TestSpec, 1: team_game = TeamGame: TeamGameSpec, 2: free_for_all_game = FreeForAllGame: FreeForAllGameSpec => state as EventTypeSpec ); html! { { match &*state { EventTypeSpec::Test(_) => test.edit(), EventTypeSpec::TeamGame(_) => team_game.edit(), EventTypeSpec::FreeForAllGame(_) => free_for_all_game.edit(), }} } } impl Editable for EventTypeSpec { type Edit = EventTypeSpecEdit; } */