Calendar (WIP): user can select a day
authorGreg Burri <greg.burri@gmail.com>
Sun, 2 Feb 2025 11:50:12 +0000 (12:50 +0100)
committerGreg Burri <greg.burri@gmail.com>
Sun, 2 Feb 2025 11:50:12 +0000 (12:50 +0100)
Cargo.lock
backend/scss/calendar.scss
backend/templates/calendar.html
frontend/src/calendar.rs

index 04b5c9f..aef3812 100644 (file)
@@ -123,9 +123,9 @@ dependencies = [
 
 [[package]]
 name = "async-trait"
-version = "0.1.85"
+version = "0.1.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
+checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -333,9 +333,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
 
 [[package]]
 name = "cc"
-version = "1.2.10"
+version = "1.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
+checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf"
 dependencies = [
  "shlex",
 ]
@@ -2126,9 +2126,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.23.21"
+version = "0.23.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
+checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7"
 dependencies = [
  "log",
  "once_cell",
@@ -2605,9 +2605,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
-version = "2.0.96"
+version = "2.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3154,9 +3154,9 @@ dependencies = [
 
 [[package]]
 name = "webpki-roots"
-version = "0.26.7"
+version = "0.26.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
+checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
 dependencies = [
  "rustls-pki-types",
 ]
index 77e5a9b..c14668b 100644 (file)
             &.current-month {
                 background-color: blue;
             }
+
+            &.today {
+                font-weight: bold;
+            }
+
+            &.selected-day {
+                background-color: red;
+            }
         }
     }
 }
\ No newline at end of file
index 435e48e..9793da8 100644 (file)
         {% endfor %}
 
     <ul class="days">
-        {% for i in 0..7 %}
-            {% for j in 0..5 %}
-                <li id="day-{{i}}{{j}}"><div class="number"></div><div class="scheduled-recipes"></div></li>
+        {% for i in 0..5 %}
+            {% for j in 0..7 %}
+                <li id="day-grid-{{i}}{{j}}">
+                    <div class="number"></div>
+                    <div class="scheduled-recipes"></div>
+                </li>
             {% endfor %}
         {% endfor %}
     </ul>
index b715117..dec6540 100644 (file)
@@ -1,9 +1,6 @@
-use std::sync::{
-    atomic::{AtomicI32, AtomicU32, Ordering},
-    Arc, Mutex,
-};
+use std::sync::{Arc, Mutex};
 
-use chrono::{offset::Local, DateTime, Datelike, Days, Months, NaiveDate, Weekday};
+use chrono::{offset::Local, DateTime, Datelike, Days, Months, Weekday};
 use common::ron_api;
 use gloo::{console::log, events::EventListener};
 use wasm_bindgen::prelude::*;
@@ -12,11 +9,11 @@ use web_sys::Element;
 
 use crate::{
     request,
-    utils::{by_id, selector, SelectorExt},
+    utils::{by_id, SelectorExt},
 };
 
 struct CalendarStateInternal {
-    current_date: DateTime<Local>,
+    displayed_date: DateTime<Local>,
     selected_date: DateTime<Local>,
 }
 
@@ -30,39 +27,33 @@ impl CalendarState {
         let current_date = Local::now();
         Self {
             internal_state: Arc::new(Mutex::new(CalendarStateInternal {
-                current_date,
+                displayed_date: current_date,
                 selected_date: current_date,
             })),
         }
     }
 
-    pub fn to_next_month(&self) -> DateTime<Local> {
+    pub fn displayed_date_next_month(&self) {
         let mut locker = self.internal_state.lock().unwrap();
-        let new_date = locker
-            .current_date
-            .checked_add_months(Months::new(1))
-            .unwrap();
-        locker.current_date = new_date;
-        new_date
+        locker.displayed_date = locker.displayed_date + Months::new(1);
     }
 
-    pub fn to_previous_month(&self) -> DateTime<Local> {
+    pub fn displayed_date_previous_month(&self) {
         let mut locker = self.internal_state.lock().unwrap();
-        let new_date = locker
-            .current_date
-            .checked_sub_months(Months::new(1))
-            .unwrap();
-        locker.current_date = new_date;
-        new_date
+        locker.displayed_date = locker.displayed_date - Months::new(1);
     }
 
-    pub fn get_current_date(&self) -> DateTime<Local> {
-        self.internal_state.lock().unwrap().current_date
+    pub fn get_displayed_date(&self) -> DateTime<Local> {
+        self.internal_state.lock().unwrap().displayed_date
     }
 
     pub fn get_selected_date(&self) -> DateTime<Local> {
         self.internal_state.lock().unwrap().selected_date
     }
+
+    pub fn set_selected_date(&self, date: DateTime<Local>) {
+        self.internal_state.lock().unwrap().selected_date = date;
+    }
 }
 
 pub fn setup(calendar: Element) {
@@ -71,55 +62,49 @@ pub fn setup(calendar: Element) {
 
     let state = CalendarState::new();
 
-    display_month(&calendar, state.get_current_date());
+    display_month(&calendar, state.clone());
 
+    // Click on previous month.
     let calendar_clone = calendar.clone();
     let state_clone = state.clone();
     EventListener::new(&prev, "click", move |_event| {
-        let m = state_clone.to_previous_month();
-        display_month(&calendar_clone, m);
+        state_clone.displayed_date_previous_month();
+        display_month(&calendar_clone, state_clone.clone());
     })
     .forget();
 
+    // Click on next month.
     let calendar_clone = calendar.clone();
     let state_clone = state.clone();
     EventListener::new(&next, "click", move |_event| {
-        let m = state_clone.to_next_month();
-        display_month(&calendar_clone, m);
+        state_clone.displayed_date_next_month();
+        display_month(&calendar_clone, state_clone.clone());
     })
     .forget();
 
-    // let days: Element = calendar.selector(".days");
-    // let state_clone = state.clone();
-    // EventListener::new(&days, "click", move |event| {
-    //     log!(event);
-    //     let target: Element = event.target().unwrap().dyn_into().unwrap();
-    //     if
-    // })
-    // .forget();
-
-    // let calendar_clone = calendar.clone();
-    // let current_month_clone = current_month.clone();
-    // let current_year_clone = current_year.clone();
-    // EventListener::new(&next, "click", move |_event| {
-    //     let mut m = current_month_clone.load(Ordering::Relaxed) + 1;
-    //     if m == 13 {
-    //         current_year_clone.fetch_add(1, Ordering::Relaxed);
-    //         m = 1
-    //     }
-    //     current_month_clone.store(m, Ordering::Relaxed);
-    //     display_month(
-    //         &calendar_clone,
-    //         current_year_clone.load(Ordering::Relaxed),
-    //         m,
-    //     );
-    // })
-    // .forget();
+    // Click on a day of the current month.
+    let days: Element = calendar.selector(".days");
+    let calendar_clone = calendar.clone();
+    let state_clone = state.clone();
+    EventListener::new(&days, "click", move |event| {
+        let target: Element = event.target().unwrap().dyn_into().unwrap();
+        if target.class_name() == "number" {
+            let first_day = first_grid_day(state_clone.get_displayed_date());
+            let day_grid_id = target.parent_element().unwrap().id();
+            let day_offset = day_grid_id[9..10].parse::<u64>().unwrap() * 7
+                + day_grid_id[10..11].parse::<u64>().unwrap();
+            state_clone.set_selected_date(first_day + Days::new(day_offset));
+            display_month(&calendar_clone, state_clone.clone());
+        }
+    })
+    .forget();
 }
 
 const NB_CALENDAR_ROW: u64 = 5;
 
-fn display_month(calendar: &Element, date: DateTime<Local>) {
+fn display_month(calendar: &Element, state: CalendarState) {
+    let date = state.get_displayed_date();
+
     calendar
         .selector::<Element>(".year")
         .set_inner_html(&date.year().to_string());
@@ -136,31 +121,36 @@ fn display_month(calendar: &Element, date: DateTime<Local>) {
         }
     }
 
-    let mut current = date;
-
-    while (current - Days::new(1)).month() == date.month() {
-        current = current - Days::new(1);
-    }
-
-    while current.weekday() != Weekday::Mon {
-        current = current - Days::new(1);
-    }
-
-    let first_day = current;
+    let first_day = first_grid_day(date);
+    let mut current = first_day;
 
-    for i in 0..7 {
-        for j in 0..NB_CALENDAR_ROW {
-            let day_element: Element = by_id(&format!("day-{}{}", i, j));
+    for i in 0..NB_CALENDAR_ROW {
+        for j in 0..7 {
+            let day_element: Element = by_id(&format!("day-grid-{}{}", i, j));
             let day_content: Element = day_element.selector(".number");
             day_content.set_inner_html(&current.day().to_string());
 
-            if current == Local::now() {
-                day_element.set_class_name("current-month today");
-            } else if current.month() == date.month() {
-                day_element.set_class_name("current-month");
-            } else {
-                day_element.set_class_name("");
+            let mut class_name = String::new();
+
+            if current.month() == date.month() {
+                if !class_name.is_empty() {
+                    class_name += " ";
+                }
+                class_name += "current-month";
+            }
+            if current.date_naive() == Local::now().date_naive() {
+                if !class_name.is_empty() {
+                    class_name += " ";
+                }
+                class_name += "today";
+            }
+            if current.date_naive() == state.get_selected_date().date_naive() {
+                if !class_name.is_empty() {
+                    class_name += " ";
+                }
+                class_name += "selected-day"
             }
+            day_element.set_class_name(&class_name);
 
             current = current + Days::new(1);
         }
@@ -185,7 +175,15 @@ fn display_month(calendar: &Element, date: DateTime<Local>) {
         for recipe in scheduled_recipes.recipes {
             log!(recipe.1);
         }
-
-        // create_tag_elements(recipe_id, &tags.tags);
     });
 }
+
+fn first_grid_day(mut date: DateTime<Local>) -> DateTime<Local> {
+    while (date - Days::new(1)).month() == date.month() {
+        date = date - Days::new(1);
+    }
+    while date.weekday() != Weekday::Mon {
+        date = date - Days::new(1);
+    }
+    date
+}