diff --git a/Cargo.lock b/Cargo.lock index 23c99c9..dd928cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1111,6 +1111,7 @@ dependencies = [ "anyhow", "js-sys", "lan_party_core", + "paste", "serde", "serde_json", "thiserror", diff --git a/web/Cargo.toml b/web/Cargo.toml index d458cca..5bcc4f9 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -18,3 +18,4 @@ thiserror = "1" yew-hooks = "0.1" anyhow = "1.0" js-sys = "0.3" +paste = "1" diff --git a/web/dist/index.html b/web/dist/index.html index 722323c..f279540 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 025a746..8eaef7f 100644 --- a/web/src/components/event.rs +++ b/web/src/components/event.rs @@ -7,6 +7,7 @@ use lan_party_core::event::{ test::{Test, TestSpec}, *, }; +use paste::paste; use wasm_bindgen::JsValue; use yew::prelude::*; @@ -225,6 +226,61 @@ view_struct!(TeamGame as self => } ); +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! 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! { + + { edit_fields!(("Name: ", name), ("Description: ", description)) } + + } + } + + impl Editable for $struct { + type Edit = [<$struct Edit>]; + } + } + }; +} + #[derive(PartialEq, Properties)] pub struct EditProps { pub state: UseStateHandle, @@ -234,43 +290,20 @@ 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 { +impl>, Type: Editable + PartialEq> Edit + for UseStateHandle +{ fn edit(&self) -> Html { - html!() + html!() } } +pub trait Editable { + type Edit; +} + +edit_struct!(EventSpec => ("Name: ", name), ("Description: ", description)); + #[function_component(StringEdit)] pub fn string_edit(props: &EditProps) -> Html { let string = props.state.clone(); @@ -288,73 +321,3 @@ impl Edit for UseStateHandle { 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), - } - } -} -*/