Add configurable time increment toggle for event creation
- Add toggle button (15/30 minutes) in calendar header next to navigation arrows - Implement circular frosted styling consistent with nav buttons - Add configurable snapping for drag-to-create events in week view - Persist time increment setting across browser sessions using localStorage - Update snap function to accept configurable increment instead of hardcoded 15 minutes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ pub struct WeekViewProps {
|
||||
pub on_create_event: Option<Callback<(NaiveDate, NaiveDateTime, NaiveDateTime)>>,
|
||||
#[prop_or_default]
|
||||
pub context_menus_open: bool,
|
||||
#[prop_or_default]
|
||||
pub time_increment: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
@@ -122,6 +124,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
let onmousedown = {
|
||||
let drag_state = drag_state_clone.clone();
|
||||
let context_menus_open = props.context_menus_open;
|
||||
let time_increment = props.time_increment;
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
// Don't start drag if any context menu is open
|
||||
if context_menus_open {
|
||||
@@ -138,8 +141,8 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
let relative_y = e.layer_y() as f64;
|
||||
let relative_y = if relative_y > 0.0 { relative_y } else { e.offset_y() as f64 };
|
||||
|
||||
// Snap to 15-minute increments
|
||||
let snapped_y = snap_to_15_minutes(relative_y);
|
||||
// Snap to increment
|
||||
let snapped_y = snap_to_increment(relative_y, time_increment);
|
||||
|
||||
drag_state.set(Some(DragState {
|
||||
is_dragging: true,
|
||||
@@ -153,6 +156,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
|
||||
let onmousemove = {
|
||||
let drag_state = drag_state_clone.clone();
|
||||
let time_increment = props.time_increment;
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
if let Some(mut current_drag) = (*drag_state).clone() {
|
||||
if current_drag.is_dragging {
|
||||
@@ -160,8 +164,8 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
let relative_y = e.layer_y() as f64;
|
||||
let relative_y = if relative_y > 0.0 { relative_y } else { e.offset_y() as f64 };
|
||||
|
||||
// Snap to 15-minute increments
|
||||
let snapped_y = snap_to_15_minutes(relative_y);
|
||||
// Snap to increment
|
||||
let snapped_y = snap_to_increment(relative_y, time_increment);
|
||||
|
||||
current_drag.current_y = snapped_y;
|
||||
drag_state.set(Some(current_drag));
|
||||
@@ -214,7 +218,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
{onmousemove}
|
||||
{onmouseup}
|
||||
>
|
||||
// Time slot backgrounds - 24 full hour slots + 1 boundary slot
|
||||
// Time slot backgrounds - 24 hour slots to represent full day
|
||||
{
|
||||
(0..24).map(|_hour| {
|
||||
html! {
|
||||
@@ -225,8 +229,11 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
}
|
||||
}).collect::<Html>()
|
||||
}
|
||||
// Final boundary slot to match the final time label
|
||||
<div class="time-slot boundary-slot"></div>
|
||||
// Final boundary slot to complete the 24-hour visual grid - make it interactive like other slots
|
||||
<div class="time-slot boundary-slot">
|
||||
<div class="time-slot-half"></div>
|
||||
<div class="time-slot-half"></div>
|
||||
</div>
|
||||
|
||||
// Events positioned absolutely based on their actual times
|
||||
<div class="events-container">
|
||||
@@ -398,9 +405,9 @@ fn get_weekday_name(weekday: Weekday) -> &'static str {
|
||||
// Calculate the pixel position of an event based on its time
|
||||
// Each hour is 60px, so we convert time to pixels
|
||||
// Snap pixel position to 15-minute increments (15px = 15 minutes since 60px = 60 minutes)
|
||||
fn snap_to_15_minutes(pixels: f64) -> f64 {
|
||||
let increment = 15.0; // 15px = 15 minutes
|
||||
(pixels / increment).round() * increment
|
||||
fn snap_to_increment(pixels: f64, increment: u32) -> f64 {
|
||||
let increment_px = increment as f64; // Convert to pixels (1px = 1 minute)
|
||||
(pixels / increment_px).round() * increment_px
|
||||
}
|
||||
|
||||
// Convert pixel position to time (inverse of time to pixels)
|
||||
@@ -410,7 +417,12 @@ fn pixels_to_time(pixels: f64) -> NaiveTime {
|
||||
let hours = (total_minutes / 60.0) as u32;
|
||||
let minutes = (total_minutes % 60.0) as u32;
|
||||
|
||||
// Clamp to valid time range
|
||||
// Handle midnight boundary - if we're at exactly 1440 pixels (24:00), return midnight
|
||||
if total_minutes >= 1440.0 {
|
||||
return NaiveTime::from_hms_opt(0, 0, 0).unwrap();
|
||||
}
|
||||
|
||||
// Clamp to valid time range for within-day times
|
||||
let hours = hours.min(23);
|
||||
let minutes = minutes.min(59);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user