From d95201f0764dfbed3e8dbfbdb8328fc25c06787e Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 15 Mar 2022 18:01:25 -0400 Subject: [PATCH] Split out shopping into module per component. --- web/src/components/mod.rs | 10 +- web/src/components/recipe_list.rs | 37 +++++++ web/src/components/recipe_selection.rs | 44 ++++++++ web/src/components/recipe_selector.rs | 64 +++++++++++ .../{shopping.rs => shopping_list.rs} | 103 +----------------- web/src/pages/cook.rs | 2 +- web/src/pages/inventory.rs | 2 +- web/src/pages/plan.rs | 2 +- 8 files changed, 159 insertions(+), 105 deletions(-) create mode 100644 web/src/components/recipe_list.rs create mode 100644 web/src/components/recipe_selection.rs create mode 100644 web/src/components/recipe_selector.rs rename web/src/components/{shopping.rs => shopping_list.rs} (52%) diff --git a/web/src/components/mod.rs b/web/src/components/mod.rs index e39e28b..0e30fa5 100644 --- a/web/src/components/mod.rs +++ b/web/src/components/mod.rs @@ -13,12 +13,18 @@ // limitations under the License. pub mod header; pub mod recipe; +pub mod recipe_list; +pub mod recipe_selection; +pub mod recipe_selector; pub mod root; -pub mod shopping; +pub mod shopping_list; pub mod tabs; pub use header::*; pub use recipe::*; +pub use recipe_list::*; +pub use recipe_selection::*; +pub use recipe_selector::*; pub use root::*; -pub use shopping::*; +pub use shopping_list::*; pub use tabs::*; diff --git a/web/src/components/recipe_list.rs b/web/src/components/recipe_list.rs new file mode 100644 index 0000000..ae48768 --- /dev/null +++ b/web/src/components/recipe_list.rs @@ -0,0 +1,37 @@ +// Copyright 2022 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::console_log; +use crate::{components::Recipe, service::AppService}; + +use sycamore::{context::use_context, prelude::*}; + +#[component(RecipeList)] +pub fn recipe_list() -> View { + let app_service = use_context::(); + let menu_list = create_memo(move || app_service.get_menu_list()); + view! { + h1 { "Recipe List" } + Indexed(IndexedProps{ + iterable: menu_list, + template: |(idx, _count)| { + console_log!("Rendering recipe index: {}", idx); + let idx = Signal::new(idx); + view ! { + Recipe(idx.handle()) + hr() + } + } + }) + } +} diff --git a/web/src/components/recipe_selection.rs b/web/src/components/recipe_selection.rs new file mode 100644 index 0000000..2b746f2 --- /dev/null +++ b/web/src/components/recipe_selection.rs @@ -0,0 +1,44 @@ +// Copyright 2022 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::console_log; +use crate::service::AppService; +use std::rc::Rc; + +use sycamore::{context::use_context, prelude::*}; + +pub struct RecipeCheckBoxProps { + pub i: usize, + pub title: ReadSignal, +} + +#[component(RecipeSelection)] +pub fn recipe_selection(props: RecipeCheckBoxProps) -> View { + let app_service = use_context::(); + // This is total hack but it works around the borrow issues with + // the `view!` macro. + let i = props.i; + let id_as_str = Rc::new(format!("{}", i)); + let id_cloned_2 = id_as_str.clone(); + let count = Signal::new(format!("{}", app_service.get_recipe_count_by_index(i))); + view! { + div(class="form-group col-md-1") { + input(type="number", class="item-count-sel", min="0", bind:value=count.clone(), name=format!("recipe_id:{}", i), value=id_as_str.clone(), on:change=move |_| { + let mut app_service = app_service.clone(); + console_log!("setting recipe id: {} to count: {}", i, *count.get()); + app_service.set_recipe_count_by_index(i, count.get().parse().unwrap()); + }) + label(for=id_cloned_2) { (props.title.get()) } + } + } +} diff --git a/web/src/components/recipe_selector.rs b/web/src/components/recipe_selector.rs new file mode 100644 index 0000000..d558a87 --- /dev/null +++ b/web/src/components/recipe_selector.rs @@ -0,0 +1,64 @@ +// Copyright 2022 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::console_error; +use crate::{components::recipe_selection::*, service::AppService}; + +use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*}; + +#[component(RecipeSelector)] +pub fn recipe_selector() -> View { + let app_service = use_context::(); + let rows = create_memo(cloned!(app_service => move || { + let mut rows = Vec::new(); + for row in app_service.get_recipes().get().as_slice().chunks(4) { + rows.push(Signal::new(Vec::from(row))); + } + rows + })); + let clicked = Signal::new(false); + create_effect(cloned!((clicked, app_service) => move || { + clicked.get(); + spawn_local_in_scope(cloned!((app_service) => { + let mut app_service = app_service.clone(); + async move { + if let Err(e) = app_service.refresh_recipes().await { + console_error!("{}", e); + }; + } + })); + })); + view! { + input(type="button", value="Refresh Recipes", on:click=move |_| { + // Poor man's click event signaling. + let toggle = !*clicked.get(); + clicked.set(toggle); + }) + fieldset(class="recipe_selector no-print container no-left-mgn pad-top") { + (View::new_fragment( + rows.get().iter().cloned().map(|r| { + view ! { + div(class="row") {Indexed(IndexedProps{ + iterable: r.handle(), + template: |(i, recipe)| { + view! { + RecipeSelection(RecipeCheckBoxProps{i: i, title: create_memo(move || recipe.get().title.clone())}) + } + }, + })} + } + }).collect() + )) + } + } +} diff --git a/web/src/components/shopping.rs b/web/src/components/shopping_list.rs similarity index 52% rename from web/src/components/shopping.rs rename to web/src/components/shopping_list.rs index 8d95172..6e06275 100644 --- a/web/src/components/shopping.rs +++ b/web/src/components/shopping_list.rs @@ -11,89 +11,12 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::{components::Recipe, service::AppService}; -use crate::{console_error, console_log}; +use crate::service::AppService; use std::collections::HashMap; -use std::{ - collections::{BTreeMap, HashSet}, - rc::Rc, -}; +use std::collections::{BTreeMap, HashSet}; use recipes::{Ingredient, IngredientKey}; -use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*}; - -struct RecipeCheckBoxProps { - i: usize, - title: ReadSignal, -} - -#[component(RecipeSelection)] -fn recipe_selection(props: RecipeCheckBoxProps) -> View { - let app_service = use_context::(); - // This is total hack but it works around the borrow issues with - // the `view!` macro. - let i = props.i; - let id_as_str = Rc::new(format!("{}", i)); - let id_cloned_2 = id_as_str.clone(); - let count = Signal::new(format!("{}", app_service.get_recipe_count_by_index(i))); - view! { - div(class="form-group col-md-1") { - input(type="number", class="item-count-sel", min="0", bind:value=count.clone(), name=format!("recipe_id:{}", i), value=id_as_str.clone(), on:change=move |_| { - let mut app_service = app_service.clone(); - console_log!("setting recipe id: {} to count: {}", i, *count.get()); - app_service.set_recipe_count_by_index(i, count.get().parse().unwrap()); - }) - label(for=id_cloned_2) { (props.title.get()) } - } - } -} - -#[component(RecipeSelector)] -pub fn recipe_selector() -> View { - let app_service = use_context::(); - let rows = create_memo(cloned!(app_service => move || { - let mut rows = Vec::new(); - for row in app_service.get_recipes().get().as_slice().chunks(4) { - rows.push(Signal::new(Vec::from(row))); - } - rows - })); - let clicked = Signal::new(false); - create_effect(cloned!((clicked, app_service) => move || { - clicked.get(); - spawn_local_in_scope(cloned!((app_service) => { - let mut app_service = app_service.clone(); - async move { - if let Err(e) = app_service.refresh_recipes().await { - console_error!("{}", e); - }; - } - })); - })); - view! { - input(type="button", value="Refresh Recipes", on:click=move |_| { - // Poor man's click event signaling. - let toggle = !*clicked.get(); - clicked.set(toggle); - }) - fieldset(class="recipe_selector no-print container no-left-mgn pad-top") { - (View::new_fragment( - rows.get().iter().cloned().map(|r| { - view ! { - div(class="row") {Indexed(IndexedProps{ - iterable: r.handle(), - template: |(i, recipe)| { - view! { - RecipeSelection(RecipeCheckBoxProps{i: i, title: create_memo(move || recipe.get().title.clone())}) - } - }, - })} - } - }).collect() - )) - } - } -} +use sycamore::{context::use_context, prelude::*}; #[component(ShoppingList)] pub fn shopping_list() -> View { @@ -162,23 +85,3 @@ pub fn shopping_list() -> View { (table_view.get().as_ref().clone()) } } - -#[component(RecipeList)] -pub fn recipe_list() -> View { - let app_service = use_context::(); - let menu_list = create_memo(move || app_service.get_menu_list()); - view! { - h1 { "Recipe List" } - Indexed(IndexedProps{ - iterable: menu_list, - template: |(idx, _count)| { - console_log!("Rendering recipe index: {}", idx); - let idx = Signal::new(idx); - view ! { - Recipe(idx.handle()) - hr() - } - } - }) - } -} diff --git a/web/src/pages/cook.rs b/web/src/pages/cook.rs index f3d02f7..cdd9c95 100644 --- a/web/src/pages/cook.rs +++ b/web/src/pages/cook.rs @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::components::{shopping::RecipeList, tabs::*}; +use crate::components::{recipe_list::*, tabs::*}; use crate::pages::PageState; use sycamore::prelude::*; diff --git a/web/src/pages/inventory.rs b/web/src/pages/inventory.rs index 2ed04af..8b4794b 100644 --- a/web/src/pages/inventory.rs +++ b/web/src/pages/inventory.rs @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::components::{shopping::ShoppingList, tabs::*}; +use crate::components::{shopping_list::*, tabs::*}; use crate::pages::PageState; use sycamore::prelude::*; diff --git a/web/src/pages/plan.rs b/web/src/pages/plan.rs index 9742df0..6340868 100644 --- a/web/src/pages/plan.rs +++ b/web/src/pages/plan.rs @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::components::{shopping::RecipeSelector, tabs::*}; +use crate::components::{recipe_selector::*, tabs::*}; use crate::pages::PageState; use sycamore::prelude::*;