use yew::prelude::*; use chrono::{Datelike, Local, NaiveDate, Duration, Weekday}; use std::collections::HashMap; #[derive(Properties, PartialEq)] pub struct CalendarProps { #[prop_or_default] pub events: HashMap>, } #[function_component] pub fn Calendar(props: &CalendarProps) -> Html { let today = Local::now().date_naive(); let current_month = use_state(|| today); let first_day_of_month = current_month.with_day(1).unwrap(); let days_in_month = get_days_in_month(*current_month); let first_weekday = first_day_of_month.weekday(); let days_from_prev_month = get_days_from_previous_month(*current_month, first_weekday); let prev_month = { let current_month = current_month.clone(); Callback::from(move |_| { let prev = *current_month - Duration::days(1); let first_of_prev = prev.with_day(1).unwrap(); current_month.set(first_of_prev); }) }; let next_month = { let current_month = current_month.clone(); Callback::from(move |_| { let next = if current_month.month() == 12 { NaiveDate::from_ymd_opt(current_month.year() + 1, 1, 1).unwrap() } else { NaiveDate::from_ymd_opt(current_month.year(), current_month.month() + 1, 1).unwrap() }; current_month.set(next); }) }; html! {

{format!("{} {}", get_month_name(current_month.month()), current_month.year())}

// Weekday headers
{"Sun"}
{"Mon"}
{"Tue"}
{"Wed"}
{"Thu"}
{"Fri"}
{"Sat"}
// Days from previous month (grayed out) { days_from_prev_month.iter().map(|day| { html! {
{*day}
} }).collect::() } // Days of current month { (1..=days_in_month).map(|day| { let date = current_month.with_day(day).unwrap(); let is_today = date == today; let events = props.events.get(&date).cloned().unwrap_or_default(); let mut classes = vec!["calendar-day", "current-month"]; if is_today { classes.push("today"); } if !events.is_empty() { classes.push("has-events"); } html! {
{day}
{ if !events.is_empty() { html! {
{ events.iter().take(2).map(|event| { html! {
{ if event.len() > 15 { format!("{}...", &event[..12]) } else { event.clone() } }
} }).collect::() } { if events.len() > 2 { html! {
{format!("+{} more", events.len() - 2)}
} } else { html! {} } }
} } else { html! {} } }
} }).collect::() } { render_next_month_days(days_from_prev_month.len(), days_in_month) }
} } fn render_next_month_days(prev_days_count: usize, current_days_count: u32) -> Html { let total_slots = 42; // 6 rows x 7 days let used_slots = prev_days_count + current_days_count as usize; let remaining_slots = if used_slots < total_slots { total_slots - used_slots } else { 0 }; (1..=remaining_slots).map(|day| { html! {
{day}
} }).collect::() } fn get_days_in_month(date: NaiveDate) -> u32 { NaiveDate::from_ymd_opt( if date.month() == 12 { date.year() + 1 } else { date.year() }, if date.month() == 12 { 1 } else { date.month() + 1 }, 1 ) .unwrap() .pred_opt() .unwrap() .day() } fn get_days_from_previous_month(current_month: NaiveDate, first_weekday: Weekday) -> Vec { let days_before = match first_weekday { Weekday::Sun => 0, Weekday::Mon => 1, Weekday::Tue => 2, Weekday::Wed => 3, Weekday::Thu => 4, Weekday::Fri => 5, Weekday::Sat => 6, }; if days_before == 0 { vec![] } else { // Calculate the previous month let prev_month = if current_month.month() == 1 { NaiveDate::from_ymd_opt(current_month.year() - 1, 12, 1).unwrap() } else { NaiveDate::from_ymd_opt(current_month.year(), current_month.month() - 1, 1).unwrap() }; let prev_month_days = get_days_in_month(prev_month); ((prev_month_days - days_before as u32 + 1)..=prev_month_days).collect() } } fn get_month_name(month: u32) -> &'static str { match month { 1 => "January", 2 => "February", 3 => "March", 4 => "April", 5 => "May", 6 => "June", 7 => "July", 8 => "August", 9 => "September", 10 => "October", 11 => "November", 12 => "December", _ => "Invalid" } }