From fc4b8633211d7781633d1c6d1434bef707e9b5c1 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Tue, 18 Aug 2026 15:31:16 +0200 Subject: [PATCH 1/2] fix(print): replace load callback with refHandler --- src/component/elements/print/PrintContent.tsx | 64 ++++++------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/src/component/elements/print/PrintContent.tsx b/src/component/elements/print/PrintContent.tsx index 1ff8670ef..d2bf91833 100644 --- a/src/component/elements/print/PrintContent.tsx +++ b/src/component/elements/print/PrintContent.tsx @@ -3,7 +3,7 @@ import { revalidateLogic } from '@tanstack/react-form'; import { useSelector } from '@tanstack/react-store'; import type { PrintPageOptions } from '@zakodium/nmrium-core'; import type { CSSProperties, ReactNode } from 'react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; import { AppForm, Button, coerceNumberInput, useForm } from 'react-science/ui'; import { z } from 'zod'; @@ -14,8 +14,6 @@ import { StyledDialogBody } from '../StyledDialogBody.js'; import { PrintProvider } from './PrintProvider.js'; import { getSizesList, pageSizes } from './pageSize.js'; -const isFirefox = navigator.userAgent.toLowerCase().includes('firefox'); - interface BasePrintProps { onPrint: (options: PrintPageOptions) => void; defaultPrintPageOptions: Partial; @@ -122,80 +120,58 @@ function InnerPrintFrame(props: InnerPrintFrameProps) { margin = 0, layout = 'landscape', } = printPageOptions || {}; - - const frameRef = useRef(null); - const [content, setContent] = useState(); + const [iframeDocument, setIframeDocument] = useState(); const { width = 0, height = 0 } = pageSizes.find((pageItem) => pageItem.name === size)?.[layout] || {}; const handleAfterPrint = useCallback(() => { onAfterPrint?.(); }, [onAfterPrint]); + const handleBeforePrint = useCallback(() => { onBeforePrint?.(); }, [onBeforePrint]); - const load = useCallback(() => { - const contentWindow = frameRef.current?.contentWindow; - if (!contentWindow) return; - const document = contentWindow.document; - - setContent(document.body); + function refHandler(frame: HTMLIFrameElement | null) { + if (!frame) return; + const document = frame.contentWindow?.document; + if (!document) return; transferStyles(document); appendPrintPageStyle(document, { size, layout, margin }); - - contentWindow.addEventListener('afterprint', handleAfterPrint); - contentWindow.addEventListener('beforeprint', handleBeforePrint); - - return contentWindow; - }, [handleAfterPrint, handleBeforePrint, layout, margin, size]); + setIframeDocument(document); + } useEffect(() => { - const contentWindow = frameRef.current?.contentWindow; + const contentWindow = iframeDocument?.defaultView; + if (!contentWindow) return; - if (!isFirefox) { - load(); - } + contentWindow.addEventListener('afterprint', handleAfterPrint); + contentWindow.addEventListener('beforeprint', handleBeforePrint); return () => { - if (!contentWindow) return; - contentWindow.removeEventListener('afterprint', handleAfterPrint); contentWindow.removeEventListener('beforeprint', handleBeforePrint); }; - }, [ - onBeforePrint, - onAfterPrint, - handleBeforePrint, - handleAfterPrint, - size, - layout, - margin, - load, - ]); + }, [iframeDocument, handleAfterPrint, handleBeforePrint]); return ( From 570337adc1a197f0492630e2fca16985593524a2 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Tue, 18 Aug 2026 15:43:16 +0200 Subject: [PATCH 2/2] refactor: add error fallback for iframe/print failures --- src/component/elements/print/PrintContent.tsx | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/component/elements/print/PrintContent.tsx b/src/component/elements/print/PrintContent.tsx index d2bf91833..2d6082e20 100644 --- a/src/component/elements/print/PrintContent.tsx +++ b/src/component/elements/print/PrintContent.tsx @@ -22,6 +22,7 @@ interface InnerPrintFrameProps { children: ReactNode; onAfterPrint?: () => void; onBeforePrint?: () => void; + onError?: (error: Error) => void; printPageOptions?: Partial; } interface PrintFrameProps @@ -40,6 +41,7 @@ export function PrintContent(props: PrintFrameProps) { printPageOptions, defaultPrintPageOptions, onPrint, + onError, } = props; useEffect(() => { @@ -100,6 +102,10 @@ export function PrintContent(props: PrintFrameProps) { onBeforePrint={() => { onBeforePrint?.(); }} + onError={(error) => { + setPageOptions(null); + onError?.(error); + }} > {children} @@ -112,6 +118,7 @@ function InnerPrintFrame(props: InnerPrintFrameProps) { children, onAfterPrint, onBeforePrint, + onError, printPageOptions = {}, } = props; @@ -134,8 +141,13 @@ function InnerPrintFrame(props: InnerPrintFrameProps) { function refHandler(frame: HTMLIFrameElement | null) { if (!frame) return; + const document = frame.contentWindow?.document; - if (!document) return; + + if (!document) { + onError?.(new Error('Print document is not available')); + return; + } transferStyles(document); appendPrintPageStyle(document, { size, layout, margin }); @@ -170,8 +182,14 @@ function InnerPrintFrame(props: InnerPrintFrameProps) { { const contentWindow = iframeDocument.defaultView; - contentWindow?.focus(); - contentWindow?.print(); + + if (!contentWindow) { + onError?.(new Error('Print content window is not available')); + return; + } + + contentWindow.focus(); + contentWindow.print(); }} style={{ width: `${width - margin}cm`,