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! {
{ 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 {
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: Component;
}
#[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;
}
*/