Implement selective print preview content printing with dynamic restoration
- Replace page body with only .print-preview-content div during printing - Use visibilitychange and focus events to restore original content when print dialog closes - Add 100ms delay before print dialog to show content replacement briefly - Remove debug logging for clean production code - Ensure print output matches preview exactly by sending only preview content to system print dialog 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -71,44 +71,6 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html {
|
||||
})
|
||||
};
|
||||
|
||||
let on_print = {
|
||||
let start_hour = *start_hour;
|
||||
let end_hour = *end_hour;
|
||||
let view_mode = props.view_mode.clone();
|
||||
Callback::from(move |_: MouseEvent| {
|
||||
if let Some(window) = web_sys::window() {
|
||||
if let Some(document) = window.document() {
|
||||
if let Some(body) = document.body() {
|
||||
// Add print attributes to body for CSS targeting
|
||||
if view_mode == ViewMode::Week {
|
||||
let _ = body.set_attribute("data-print-start-hour", &start_hour.to_string());
|
||||
let _ = body.set_attribute("data-print-end-hour", &end_hour.to_string());
|
||||
}
|
||||
let _ = body.set_attribute("data-print-mode", "true");
|
||||
|
||||
// Trigger print
|
||||
if let Err(e) = window.print() {
|
||||
web_sys::console::log_1(&format!("Print failed: {:?}", e).into());
|
||||
}
|
||||
|
||||
// Clean up attributes after a short delay
|
||||
let cleanup_body = body.clone();
|
||||
let cleanup_callback = Closure::wrap(Box::new(move || {
|
||||
let _ = cleanup_body.remove_attribute("data-print-start-hour");
|
||||
let _ = cleanup_body.remove_attribute("data-print-end-hour");
|
||||
let _ = cleanup_body.remove_attribute("data-print-mode");
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0(
|
||||
cleanup_callback.as_ref().unchecked_ref(),
|
||||
1000
|
||||
);
|
||||
cleanup_callback.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let format_hour = |hour: u32| -> String {
|
||||
if hour == 0 {
|
||||
@@ -140,6 +102,148 @@ pub fn PrintPreviewModal(props: &PrintPreviewModalProps) -> Html {
|
||||
// Calculate print dimensions for the current hour range
|
||||
let (base_unit, pixels_per_hour, _available_height) = calculate_print_dimensions(*start_hour, *end_hour, props.time_increment);
|
||||
|
||||
let on_print = {
|
||||
let start_hour = *start_hour;
|
||||
let end_hour = *end_hour;
|
||||
let view_mode = props.view_mode.clone();
|
||||
let base_unit_value = base_unit;
|
||||
let pixels_per_hour_value = pixels_per_hour;
|
||||
Callback::from(move |_: MouseEvent| {
|
||||
if let Some(window) = web_sys::window() {
|
||||
if let Some(document) = window.document() {
|
||||
// Find the print preview content element
|
||||
if let Some(preview_content) = document.query_selector(".print-preview-content").ok().flatten() {
|
||||
// Clone the content to print
|
||||
if let Some(content_clone) = preview_content.clone_node_with_deep(true).ok() {
|
||||
if let Some(body) = document.body() {
|
||||
// Store original body content and styling
|
||||
let original_body_html = body.inner_html();
|
||||
let original_body_style = body.get_attribute("style").unwrap_or_default();
|
||||
|
||||
// Set CSS variables on document root for print
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// Clear body and add only the preview content
|
||||
body.set_inner_html("");
|
||||
let _ = body.set_attribute("style", "margin: 0; padding: 0; background: white;");
|
||||
let _ = body.append_child(&content_clone);
|
||||
|
||||
// Set up restore content variables for later use
|
||||
use std::rc::Rc;
|
||||
let restore_body = Rc::new(body.clone());
|
||||
let restore_document = Rc::new(document.clone());
|
||||
let original_html = Rc::new(original_body_html.clone());
|
||||
let original_style = Rc::new(original_body_style.clone());
|
||||
let setup_window = Rc::new(window.clone());
|
||||
|
||||
// Add a small delay before printing to make the change visible
|
||||
let print_window = window.clone();
|
||||
let print_callback = Closure::wrap(Box::new(move || {
|
||||
|
||||
// Use visibility change event to detect when user returns to page
|
||||
let restore_body = restore_body.clone();
|
||||
let restore_document = restore_document.clone();
|
||||
let original_html = original_html.clone();
|
||||
let original_style = original_style.clone();
|
||||
let setup_document1 = document.clone();
|
||||
let setup_document2 = document.clone();
|
||||
|
||||
// Create restore function for both visibility and focus events
|
||||
let restore_body_vis = restore_body.clone();
|
||||
let restore_document_vis = restore_document.clone();
|
||||
let original_html_vis = original_html.clone();
|
||||
let original_style_vis = original_style.clone();
|
||||
let setup_document_vis = setup_document1.clone();
|
||||
|
||||
let restore_body_focus = restore_body.clone();
|
||||
let restore_document_focus = restore_document.clone();
|
||||
let original_html_focus = original_html.clone();
|
||||
let original_style_focus = original_style.clone();
|
||||
|
||||
// Try multiple approaches - visibility change
|
||||
let visibility_callback = Closure::wrap(Box::new(move || {
|
||||
if !setup_document_vis.hidden() {
|
||||
// Restore original body content and style
|
||||
restore_body_vis.set_inner_html(&original_html_vis);
|
||||
if original_style_vis.is_empty() {
|
||||
let _ = restore_body_vis.remove_attribute("style");
|
||||
} else {
|
||||
let _ = restore_body_vis.set_attribute("style", &original_style_vis);
|
||||
}
|
||||
|
||||
// Clean up CSS variables
|
||||
if let Some(document_element) = restore_document_vis.document_element() {
|
||||
if let Some(html_element) = document_element.dyn_ref::<web_sys::HtmlElement>() {
|
||||
let style = html_element.style();
|
||||
let _ = style.remove_property("--print-base-unit");
|
||||
let _ = style.remove_property("--print-pixels-per-hour");
|
||||
let _ = style.remove_property("--print-start-hour");
|
||||
let _ = style.remove_property("--print-end-hour");
|
||||
}
|
||||
}
|
||||
}
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
// And also try focus event as backup
|
||||
let focus_callback = Closure::wrap(Box::new(move || {
|
||||
// Restore original body content and style
|
||||
restore_body_focus.set_inner_html(&original_html_focus);
|
||||
if original_style_focus.is_empty() {
|
||||
let _ = restore_body_focus.remove_attribute("style");
|
||||
} else {
|
||||
let _ = restore_body_focus.set_attribute("style", &original_style_focus);
|
||||
}
|
||||
|
||||
// Clean up CSS variables
|
||||
if let Some(document_element) = restore_document_focus.document_element() {
|
||||
if let Some(html_element) = document_element.dyn_ref::<web_sys::HtmlElement>() {
|
||||
let style = html_element.style();
|
||||
let _ = style.remove_property("--print-base-unit");
|
||||
let _ = style.remove_property("--print-pixels-per-hour");
|
||||
let _ = style.remove_property("--print-start-hour");
|
||||
let _ = style.remove_property("--print-end-hour");
|
||||
}
|
||||
}
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
// Set up both listeners
|
||||
let _ = setup_document2.add_event_listener_with_callback(
|
||||
"visibilitychange",
|
||||
visibility_callback.as_ref().unchecked_ref()
|
||||
);
|
||||
|
||||
let _ = print_window.add_event_listener_with_callback(
|
||||
"focus",
|
||||
focus_callback.as_ref().unchecked_ref()
|
||||
);
|
||||
|
||||
visibility_callback.forget();
|
||||
focus_callback.forget();
|
||||
|
||||
// Now trigger the print dialog
|
||||
let _ = print_window.print();
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0(
|
||||
print_callback.as_ref().unchecked_ref(),
|
||||
100 // 100ms delay to see the content change
|
||||
);
|
||||
print_callback.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class="modal-backdrop print-preview-modal-backdrop" onclick={backdrop_click}>
|
||||
|
||||
Reference in New Issue
Block a user