Fix event positioning in print copy with dynamic base-unit recalculation

- Measure actual print copy div height after aspect-ratio scaling
- Recalculate base-unit based on measured height vs original 720px assumption
- Apply position scaling to .week-event elements in print copy only
- Parse and recalculate top/height pixel values using scale factor
- Add landscape orientation and fit-to-page CSS hints for better printing
- Preserve hour filtering with proper data attributes and CSS variables

This ensures events display correctly when print copy is scaled to fit page
while maintaining proper aspect ratio and hour range filtering.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-09-12 14:10:43 -04:00
parent 96585440d1
commit 64dbf65beb
2 changed files with 90 additions and 21 deletions

View File

@@ -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 */

View File

@@ -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::<web_sys::HtmlElement>() {
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::<web_sys::HtmlElement>() {
// Copy CSS custom properties (variables)
if let Some(print_copy_html) = print_copy.dyn_ref::<web_sys::HtmlElement>() {
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::<web_sys::HtmlElement>() {
// 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::<web_sys::HtmlElement>() {
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::<f64>() {
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::<f64>() {
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());
}
}
}