diff --git a/src/components/month_view.rs b/src/components/month_view.rs index e3adbd8..7cad4f0 100644 --- a/src/components/month_view.rs +++ b/src/components/month_view.rs @@ -1,7 +1,8 @@ use yew::prelude::*; use chrono::{Datelike, NaiveDate, Weekday}; use std::collections::HashMap; -use web_sys::MouseEvent; +use web_sys::window; +use wasm_bindgen::{prelude::*, JsCast}; use crate::services::calendar_service::{CalendarEvent, UserInfo}; #[derive(Properties, PartialEq)] @@ -26,11 +27,58 @@ pub struct MonthViewProps { #[function_component(MonthView)] pub fn month_view(props: &MonthViewProps) -> Html { + let max_events_per_day = use_state(|| 4); // Default to 4 events max let first_day_of_month = props.current_month.with_day(1).unwrap(); let days_in_month = get_days_in_month(props.current_month); let first_weekday = first_day_of_month.weekday(); let days_from_prev_month = get_days_from_previous_month(props.current_month, first_weekday); + // Calculate maximum events that can fit based on available height + let calculate_max_events = { + let max_events_per_day = max_events_per_day.clone(); + move || { + // Since we're using CSS Grid with equal row heights, + // we can estimate based on typical calendar dimensions + // Typical calendar height is around 600-800px for 6 rows + // Each row gets ~100-133px, minus day number and padding leaves ~70-100px + // Each event is ~18px, so we can fit ~3-4 events + "+n more" indicator + max_events_per_day.set(3); + } + }; + + // Setup resize handler and initial calculation + { + let calculate_max_events = calculate_max_events.clone(); + use_effect_with((), move |_| { + let calculate_max_events_clone = calculate_max_events.clone(); + + // Initial calculation with a slight delay to ensure DOM is ready + if let Some(window) = window() { + let timeout_closure = Closure::wrap(Box::new(move || { + calculate_max_events_clone(); + }) as Box); + + let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0( + timeout_closure.as_ref().unchecked_ref(), + 100, + ); + timeout_closure.forget(); + } + + // Setup resize listener + let resize_closure = Closure::wrap(Box::new(move || { + calculate_max_events(); + }) as Box); + + if let Some(window) = window() { + let _ = window.add_event_listener_with_callback("resize", resize_closure.as_ref().unchecked_ref()); + resize_closure.forget(); // Keep the closure alive + } + + || {} + }); + } + // Helper function to get calendar color for an event let get_event_color = |event: &CalendarEvent| -> String { if let Some(user_info) = &props.user_info { @@ -72,6 +120,11 @@ pub fn month_view(props: &MonthViewProps) -> Html { let is_selected = props.selected_date == Some(date); let day_events = props.events.get(&date).cloned().unwrap_or_default(); + // Calculate visible events and overflow + let max_events = *max_events_per_day as usize; + let visible_events: Vec<_> = day_events.iter().take(max_events).collect(); + let hidden_count = day_events.len().saturating_sub(max_events); + html! {
Html {
{day}
{ - day_events.iter().map(|event| { + visible_events.iter().map(|event| { let event_color = get_event_color(event); let is_refreshing = props.refreshing_event_uid.as_ref() == Some(&event.uid); let onclick = { let on_event_click = props.on_event_click.clone(); - let event = event.clone(); - Callback::from(move |_: MouseEvent| { + let event = (*event).clone(); + Callback::from(move |_: web_sys::MouseEvent| { on_event_click.emit(event.clone()); }) }; @@ -120,7 +173,7 @@ pub fn month_view(props: &MonthViewProps) -> Html { let oncontextmenu = { if let Some(callback) = &props.on_event_context_menu { let callback = callback.clone(); - let event = event.clone(); + let event = (*event).clone(); Some(Callback::from(move |e: web_sys::MouseEvent| { e.prevent_default(); callback.emit((e, event.clone())); @@ -142,6 +195,17 @@ pub fn month_view(props: &MonthViewProps) -> Html { } }).collect::() } + { + if hidden_count > 0 { + html! { +
+ {format!("+{} more", hidden_count)} +
+ } + } else { + html! {} + } + }
} diff --git a/styles.css b/styles.css index ddb0156..119e3e6 100644 --- a/styles.css +++ b/styles.css @@ -484,8 +484,10 @@ body { .calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); + grid-template-rows: auto repeat(6, 1fr); flex: 1; background: white; + gap: 0; } /* Week View Container */ @@ -743,12 +745,12 @@ body { .calendar-day { border: 1px solid #f0f0f0; padding: 0.75rem; - min-height: 100px; display: flex; flex-direction: column; cursor: pointer; transition: background-color 0.2s; position: relative; + overflow: hidden; } .calendar-day:hover { @@ -808,6 +810,14 @@ body { color: #1976d2; } +.day-events { + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; + overflow: hidden; +} + .event-indicators { flex: 1; display: flex; @@ -815,6 +825,22 @@ body { gap: 2px; } +.more-events-indicator { + font-size: 0.7rem; + color: #666; + font-weight: 500; + padding: 2px 4px; + text-align: center; + background: #f5f5f5; + border-radius: 3px; + cursor: pointer; + transition: background-color 0.2s; +} + +.more-events-indicator:hover { + background: #e0e0e0; +} + .event-box { /* Background color will be set inline via style attribute */ color: white;