diff --git a/frontend/print-preview.css b/frontend/print-preview.css index 1884695..5da387d 100644 --- a/frontend/print-preview.css +++ b/frontend/print-preview.css @@ -1128,7 +1128,7 @@ .print-preview-paper[data-start-hour="7"][data-end-hour="19"]:has(.time-slot.quarter-mode) .time-labels, .print-preview-paper[data-start-hour="7"][data-end-hour="19"]:has(.time-slot.quarter-mode) .week-days-grid { min-height: 1530px !important; } -/* Print Page Setup - Force landscape orientation */ +/* Print Page Setup - Force landscape orientation and fit to page */ @page { size: landscape; margin: 0.5in; @@ -1136,6 +1136,11 @@ /* Print Media Rules - Show only the print-preview-copy when printing */ @media print { + html { + print-color-adjust: exact; /* Preserve colors when printing */ + -webkit-print-color-adjust: exact; + } + /* Hide all top-level app content */ .app { display: none !important; @@ -1147,11 +1152,19 @@ position: absolute !important; left: 0 !important; top: 0 !important; - width: 960px !important; - height: 720px !important; + /* width: 960px !important; */ + /* height: 720px !important; */ + aspect-ratio: 1.2941176470588236 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; + /* Properties to help browsers scale to fit page */ + max-width: 100vw; + max-height: 100vh; + object-fit: contain; + page-break-inside: avoid; + orphans: 1; + widows: 1; } /* Ensure print content uses the full page */ diff --git a/frontend/src/components/print_preview_modal.rs b/frontend/src/components/print_preview_modal.rs index b5255c1..2e72988 100644 --- a/frontend/src/components/print_preview_modal.rs +++ b/frontend/src/components/print_preview_modal.rs @@ -106,8 +106,8 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html { { let start_hour = *start_hour; let end_hour = *end_hour; - let base_unit_value = base_unit; - let pixels_per_hour_value = pixels_per_hour; + let time_increment = props.time_increment; + let original_base_unit = base_unit; use_effect(move || { if let Some(window) = web_sys::window() { if let Some(document) = window.document() { @@ -115,8 +115,6 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html { if let Some(document_element) = document.document_element() { if let Some(html_element) = document_element.dyn_ref::() { let style = html_element.style(); - let _ = style.set_property("--print-base-unit", &format!("{:.2}", base_unit_value)); - let _ = style.set_property("--print-pixels-per-hour", &format!("{:.2}", pixels_per_hour_value)); let _ = style.set_property("--print-start-hour", &start_hour.to_string()); let _ = style.set_property("--print-end-hour", &end_hour.to_string()); } @@ -132,22 +130,80 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html { print_copy.set_inner_html(""); let _ = print_copy.append_child(&content_clone); - // Copy the CSS variables and data attributes from the print-preview-paper - if let Some(preview_paper) = document.query_selector(".print-preview-paper").ok().flatten() { - if let Some(_paper_element) = preview_paper.dyn_ref::() { - // Copy CSS custom properties (variables) - if let Some(print_copy_html) = print_copy.dyn_ref::() { - let style = print_copy_html.style(); - let _ = style.set_property("--print-start-hour", &start_hour.to_string()); - let _ = style.set_property("--print-end-hour", &end_hour.to_string()); - let _ = style.set_property("--print-base-unit", &format!("{:.2}", base_unit_value)); - let _ = style.set_property("--print-pixels-per-hour", &format!("{:.2}", pixels_per_hour_value)); + // Get the actual rendered height of the print copy div and recalculate base-unit + if let Some(print_copy_html) = print_copy.dyn_ref::() { + // Temporarily make visible to measure height, then hide again + let original_display = print_copy_html.style().get_property_value("display").unwrap_or_default(); + let _ = print_copy_html.style().set_property("display", "block"); + let _ = print_copy_html.style().set_property("visibility", "hidden"); + let _ = print_copy_html.style().set_property("position", "absolute"); + let _ = print_copy_html.style().set_property("top", "-9999px"); + + // Now measure the height + let actual_height = print_copy_html.client_height() as f64; + + // Restore original display + let _ = print_copy_html.style().set_property("display", &original_display); + let _ = print_copy_html.style().remove_property("visibility"); + let _ = print_copy_html.style().remove_property("position"); + let _ = print_copy_html.style().remove_property("top"); + + // Recalculate base-unit and pixels-per-hour based on actual height + let visible_hours = (end_hour - start_hour) as f64; + let slots_per_hour = if time_increment == 15 { 4.0 } else { 2.0 }; + let header_height = 50.0; + let header_border = 2.0; + let container_spacing = 8.0; + let total_overhead = header_height + header_border + container_spacing; + let available_height = actual_height - total_overhead; + let actual_base_unit = available_height / (visible_hours * slots_per_hour); + let actual_pixels_per_hour = actual_base_unit * slots_per_hour; + + + // Set CSS variables with recalculated values + let style = print_copy_html.style(); + let _ = style.set_property("--print-base-unit", &format!("{:.2}", actual_base_unit)); + let _ = style.set_property("--print-pixels-per-hour", &format!("{:.2}", actual_pixels_per_hour)); + let _ = style.set_property("--print-start-hour", &start_hour.to_string()); + let _ = style.set_property("--print-end-hour", &end_hour.to_string()); + + // Copy data attributes + let _ = print_copy.set_attribute("data-start-hour", &start_hour.to_string()); + let _ = print_copy.set_attribute("data-end-hour", &end_hour.to_string()); + + // Recalculate event positions using the new base-unit + let events = print_copy.query_selector_all(".week-event").unwrap(); + let scale_factor = actual_base_unit / original_base_unit; + + for i in 0..events.length() { + if let Some(event_element) = events.get(i) { + if let Some(event_html) = event_element.dyn_ref::() { + let event_style = event_html.style(); + + // Get current positioning values and recalculate + if let Ok(current_top) = event_style.get_property_value("top") { + if current_top.ends_with("px") { + if let Ok(top_px) = current_top[..current_top.len()-2].parse::() { + let new_top = top_px * scale_factor; + let _ = event_style.set_property("top", &format!("{:.2}px", new_top)); + } + } + } + + if let Ok(current_height) = event_style.get_property_value("height") { + if current_height.ends_with("px") { + if let Ok(height_px) = current_height[..current_height.len()-2].parse::() { + let new_height = height_px * scale_factor; + let _ = event_style.set_property("height", &format!("{:.2}px", new_height)); + } + } + } + } } - - // Copy data attributes - let _ = print_copy.set_attribute("data-start-hour", &start_hour.to_string()); - let _ = print_copy.set_attribute("data-end-hour", &end_hour.to_string()); } + + web_sys::console::log_1(&format!("Height: {:.2}, Original base-unit: {:.2}, New base-unit: {:.2}, Scale factor: {:.2}", + actual_height, original_base_unit, actual_base_unit, scale_factor).into()); } } }