324 lines
7.9 KiB
Rust
324 lines
7.9 KiB
Rust
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 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! {
|
|
<>
|
|
$(
|
|
<p>
|
|
{ $name }
|
|
{ $prop.view() }
|
|
</p>
|
|
)*
|
|
</>
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! view_struct {
|
|
($struct:path: $title:expr => $(($name:expr, $prop:ident)),* $(,)?) => {
|
|
impl View for $struct {
|
|
fn view(&self) -> Html {
|
|
html! {
|
|
<Block title={$title}>
|
|
{ view_fields!(
|
|
$(($name, self.$prop),)*
|
|
)}
|
|
</Block>
|
|
}
|
|
}
|
|
}
|
|
};
|
|
($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! {
|
|
<>
|
|
<div class="px-3 py-3 rounded-lg border-solid border-gray-800 border-2 my-3">
|
|
<p class="cursor-pointer text-lg" onclick={clone_cb!(open => move |_| open.set(!*open))}>{ &props.title }</p>
|
|
<div {class}>
|
|
<br />
|
|
<div>
|
|
{for props.children.iter()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
}
|
|
}
|
|
|
|
pub trait View {
|
|
fn view(&self) -> Html;
|
|
}
|
|
|
|
impl View for bool {
|
|
fn view(&self) -> Html {
|
|
html! {
|
|
<input type="checkbox" value={self.to_string()} disabled={true} />
|
|
}
|
|
}
|
|
}
|
|
|
|
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! {
|
|
<Block title="List">
|
|
<ul>
|
|
{ self.iter().map(|x| html! { <li>{ x.view() }</li> }).collect::<Html>() }
|
|
</ul>
|
|
</Block>
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
view_iter!(T => Vec<T>);
|
|
view_iter!(T => HashSet<T>);
|
|
view_iter!(T => &[T]);
|
|
|
|
impl<T: View> View for Option<T> {
|
|
fn view(&self) -> Html {
|
|
match self {
|
|
Some(content) => content.view(),
|
|
None => html! { "None" },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<K: View, V: View> View for HashMap<K, V> {
|
|
fn view(&self) -> Html {
|
|
html! {
|
|
<Block title={"Map"}>
|
|
{self.iter().map(|(k, v)| {
|
|
html! { <p><span>{k.view()}</span>{ ": " }<span>{v.view()}</span></p> }
|
|
}).collect::<Html>()}
|
|
</Block>
|
|
}
|
|
}
|
|
}
|
|
|
|
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! {
|
|
<Block title={"FreeForAllGame"}>
|
|
{ view_fields!(("Ranking: ", self.ranking)) }
|
|
{ view_fields!(
|
|
("Participants: ", self.spec.participants),
|
|
("Win rewards: ", self.spec.win_rewards),
|
|
("Lose rewards: ", self.spec.lose_rewards),
|
|
) }
|
|
</Block>
|
|
}
|
|
);
|
|
|
|
view_enum_simple!(
|
|
lan_party_core::event::free_for_all_game::FreeForAllGameRanking: Ranking,
|
|
Scores
|
|
);
|
|
|
|
view_struct!(TeamGame as self =>
|
|
html! {
|
|
<Block title={"TeamGame"}>
|
|
{ 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),
|
|
) }
|
|
</Block>
|
|
}
|
|
);
|
|
|
|
macro_rules! edit_fields {
|
|
($(($name:expr, $prop:expr)),* $(,)?) => {
|
|
html! {
|
|
<>
|
|
$(
|
|
<p>
|
|
{ $name }
|
|
{ $prop.edit() }
|
|
</p>
|
|
)*
|
|
</>
|
|
}
|
|
};
|
|
}
|
|
|
|
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! edit_struct {
|
|
($struct:ident => $(($name:expr, $prop:ident)),* $(,)?) => {
|
|
paste! {
|
|
#[function_component([<$struct Edit>])]
|
|
pub fn event_spec_edit(props: &EditProps<$struct>) -> Html {
|
|
let state = props.state.clone();
|
|
link_fields!(name, description => state as $struct);
|
|
html! {
|
|
<Block title={stringify!($struct)}>
|
|
{ edit_fields!(("Name: ", name), ("Description: ", description)) }
|
|
</Block>
|
|
}
|
|
}
|
|
|
|
impl Editable for $struct {
|
|
type Edit = [<$struct Edit>];
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[derive(PartialEq, Properties)]
|
|
pub struct EditProps<T: PartialEq> {
|
|
pub state: UseStateHandle<T>,
|
|
}
|
|
|
|
pub trait Edit {
|
|
fn edit(&self) -> Html;
|
|
}
|
|
|
|
impl<Comp: Component<Properties = EditProps<Type>>, Type: Editable<Edit = Comp> + PartialEq> Edit
|
|
for UseStateHandle<Type>
|
|
{
|
|
fn edit(&self) -> Html {
|
|
html!(<Comp state={self.clone()} />)
|
|
}
|
|
}
|
|
|
|
pub trait Editable {
|
|
type Edit;
|
|
}
|
|
|
|
edit_struct!(EventSpec => ("Name: ", name), ("Description: ", description));
|
|
|
|
#[function_component(StringEdit)]
|
|
pub fn string_edit(props: &EditProps<String>) -> Html {
|
|
let string = props.state.clone();
|
|
|
|
html! {
|
|
<TextInput
|
|
class={"mx-2 appearance-none block bg-gray-700 text-slate-400 border border-gray-600 rounded py-2 px-2 w-20 leading-tight focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"}
|
|
bind={bind!(string)}
|
|
/>
|
|
}
|
|
}
|
|
|
|
impl Edit for UseStateHandle<String> {
|
|
fn edit(&self) -> Html {
|
|
html!(<StringEdit state={self.clone()} />)
|
|
}
|
|
}
|