Reactive experiments
This commit is contained in:
parent
cdc056dadc
commit
53cb2a22b1
|
@ -48,7 +48,7 @@ impl Event {
|
||||||
/// # EventSpec
|
/// # EventSpec
|
||||||
///
|
///
|
||||||
/// A specification of an event
|
/// A specification of an event
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct EventSpec {
|
pub struct EventSpec {
|
||||||
|
@ -63,7 +63,7 @@ macro_rules! events {
|
||||||
/// # EventTypeSpec
|
/// # EventTypeSpec
|
||||||
///
|
///
|
||||||
/// A specification of an event type
|
/// A specification of an event type
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum EventTypeSpec {
|
pub enum EventTypeSpec {
|
||||||
|
@ -146,7 +146,7 @@ pub trait EventTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -156,7 +156,7 @@ mod test {
|
||||||
pub num_players: i64,
|
pub num_players: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TestSpec {
|
pub struct TestSpec {
|
||||||
|
@ -195,7 +195,7 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod team_game {
|
pub mod team_game {
|
||||||
use super::{
|
use super::{
|
||||||
free_for_all_game::{
|
free_for_all_game::{
|
||||||
FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate,
|
FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate,
|
||||||
|
@ -216,7 +216,7 @@ mod team_game {
|
||||||
pub ffa_game: FreeForAllGame,
|
pub ffa_game: FreeForAllGame,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TeamGameSpec {
|
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 std::collections::HashSet;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -385,7 +385,7 @@ mod free_for_all_game {
|
||||||
vec![-3, -2, -1]
|
vec![-3, -2, -1]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct FreeForAllGameSpec {
|
pub struct FreeForAllGameSpec {
|
||||||
|
@ -554,3 +554,9 @@ mod free_for_all_game {
|
||||||
}
|
}
|
||||||
|
|
||||||
events!(test => Test, team_game => TeamGame, free_for_all_game => FreeForAllGame);
|
events!(test => Test, team_game => TeamGame, free_for_all_game => FreeForAllGame);
|
||||||
|
|
||||||
|
impl Default for EventTypeSpec {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Test(test::TestSpec::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
|
||||||
<title>Yew App</title>
|
<title>Yew App</title>
|
||||||
|
|
||||||
<link rel="preload" href="/index-b24e51ea036df368_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
<link rel="preload" href="/index-eca47f078afaab22_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||||
<link rel="modulepreload" href="/index-b24e51ea036df368.js"></head>
|
<link rel="modulepreload" href="/index-eca47f078afaab22.js"></head>
|
||||||
<body class="base theme-dark bg-gray-900 text-gray-400">
|
<body class="base theme-dark bg-gray-900 text-gray-400">
|
||||||
|
|
||||||
|
|
||||||
<script type="module">import init from '/index-b24e51ea036df368.js';init('/index-b24e51ea036df368_bg.wasm');</script></body></html>
|
<script type="module">import init from '/index-eca47f078afaab22.js';init('/index-eca47f078afaab22_bg.wasm');</script></body></html>
|
|
@ -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 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)]
|
#[derive(Debug, Clone, Properties, PartialEq)]
|
||||||
pub struct BlockProps {
|
pub struct BlockProps {
|
||||||
|
title: String,
|
||||||
children: Children,
|
children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Block)]
|
#[function_component(Block)]
|
||||||
pub fn block(props: &BlockProps) -> Html {
|
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! {
|
html! {
|
||||||
|
<>
|
||||||
<div class="px-3 py-3 rounded-lg border-solid border-gray-800 border-2 my-3">
|
<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()}
|
{for props.children.iter()}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RenderEvent {
|
pub trait View {
|
||||||
fn view(&self) -> Html;
|
fn view(&self) -> Html;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderEvent for String {
|
impl View for bool {
|
||||||
fn view(&self) -> Html {
|
|
||||||
html! { self }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderEvent for bool {
|
|
||||||
fn view(&self) -> Html {
|
fn view(&self) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<input type="checkbox" value={self.to_string()} disabled={true} />
|
<input type="checkbox" value={self.to_string()} disabled={true} />
|
||||||
|
@ -33,33 +112,249 @@ impl RenderEvent for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: RenderEvent> RenderEvent for Vec<T> {
|
macro_rules! view_html {
|
||||||
|
($t:ty) => {
|
||||||
|
impl View for $t {
|
||||||
fn view(&self) -> Html {
|
fn view(&self) -> Html {
|
||||||
html! {
|
html! { self }
|
||||||
self.iter().map(|x| x.view()).collect::<Html>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: RenderEvent> RenderEvent for &[T] {
|
view_html!(i64);
|
||||||
fn view(&self) -> Html {
|
view_html!(i32);
|
||||||
html! {
|
view_html!(isize);
|
||||||
self.iter().map(|x| x.view()).collect::<Html>()
|
view_html!(u64);
|
||||||
}
|
view_html!(u32);
|
||||||
}
|
view_html!(usize);
|
||||||
}
|
view_html!(f64);
|
||||||
|
view_html!(f32);
|
||||||
|
view_html!(String);
|
||||||
|
view_html!(&'static str);
|
||||||
|
|
||||||
impl RenderEvent for lan_party_core::event::Event {
|
macro_rules! view_iter {
|
||||||
|
($t:ident => $type:ty) => {
|
||||||
|
impl<$t: View> View for $type {
|
||||||
fn view(&self) -> Html {
|
fn view(&self) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<Block>
|
<Block title="List">
|
||||||
{ "Name: " }
|
<ul>
|
||||||
{ self.name.view() }<br />
|
{ self.iter().map(|x| html! { <li>{ x.view() }</li> }).collect::<Html>() }
|
||||||
{ "Description: " }
|
</ul>
|
||||||
{ self.description.view() }<br />
|
|
||||||
{ "Concluded: " }
|
|
||||||
{ self.concluded.view() }<br />
|
|
||||||
</Block>
|
</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>
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Properties)]
|
||||||
|
pub struct EditProps<T: PartialEq> {
|
||||||
|
pub state: UseStateHandle<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Edit {
|
||||||
|
fn edit(&self) -> Html;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component(EventSpecEdit)]
|
||||||
|
pub fn event_spec_edit(props: &EditProps<EventSpec>) -> 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<EventSpec> {
|
||||||
|
fn edit(&self) -> Html {
|
||||||
|
html!(<EventSpecEdit state={self.clone()} />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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()} />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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<String>,
|
||||||
|
description: UseStateHandle<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Edit for UseStateHandle<String> {
|
||||||
|
type Type = String;
|
||||||
|
|
||||||
|
fn to_edit(t: Self::Type) -> Self {
|
||||||
|
use_state(|| t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit(&self) -> Html {
|
||||||
|
let this = self.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!(this)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
mod button;
|
mod button;
|
||||||
mod event;
|
pub mod event;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
pub use button::Button;
|
pub use button::Button;
|
||||||
pub use event::RenderEvent;
|
pub use event::View;
|
||||||
pub use table::Table;
|
pub use table::Table;
|
||||||
use yew::{function_component, html, Children, Properties};
|
use yew::{function_component, html, Children, Properties};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{Page, RenderEvent},
|
components::{
|
||||||
|
event::{Edit, EventSpecEdit},
|
||||||
|
Button, Page, View,
|
||||||
|
},
|
||||||
init,
|
init,
|
||||||
};
|
};
|
||||||
|
use lan_party_core::event::EventSpec;
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_hooks::*;
|
use yew_hooks::*;
|
||||||
|
|
||||||
use crate::{clone, util::api_request};
|
use crate::{clone, clone_cb, util::api_request};
|
||||||
|
|
||||||
#[function_component(EventsPage)]
|
#[function_component(EventsPage)]
|
||||||
pub fn events_page() -> Html {
|
pub fn events_page() -> Html {
|
||||||
|
@ -19,9 +24,16 @@ pub fn events_page() -> Html {
|
||||||
.unwrap())
|
.unwrap())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//let edit_event = use_state(|| EventSpecEditHandle::to_edit(EventSpec::default()));
|
||||||
|
let event_spec = use_state(|| EventSpec::default());
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Page>
|
<Page>
|
||||||
{ events.view() }
|
{ events.view() }
|
||||||
|
|
||||||
|
{ event_spec.edit() }
|
||||||
|
|
||||||
|
<Button text="Create" onclick={clone_cb!(event_spec => move |_| web_sys::console::log_1(&JsValue::from_serde(&*event_spec).unwrap()))} />
|
||||||
</Page>
|
</Page>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue