react-to-print
Version:
Print React components in the browser
16 lines (15 loc) • 9.47 kB
JavaScript
;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Y=require("react"),q="printWindow";function V(e){const t=document.createElement("iframe");return t.width=`${document.documentElement.clientWidth}px`,t.height=`${document.documentElement.clientHeight}px`,t.style.position="absolute",t.style.top=`-${document.documentElement.clientHeight+100}px`,t.style.left=`-${document.documentElement.clientWidth+100}px`,t.id=q,t.srcdoc="<!DOCTYPE html>",e&&(e.allow&&(t.allow=e.allow),e.referrerPolicy!==void 0&&(t.referrerPolicy=e.referrerPolicy),e.sandbox!==void 0&&(t.sandbox=e.sandbox)),t}function f({level:e="error",messages:t,suppressErrors:n=!1}){n||(e==="error"?console.error(t):e==="warning"?console.warn(t):console.debug(t))}function j(e,t){if(t||!e){const n=document.getElementById(q);n&&document.body.removeChild(n)}}function D(e){return e instanceof Error?e:new Error("Unknown Error")}function H(e,t){const{documentTitle:n,onAfterPrint:l,onPrintError:y,preserveAfterPrint:m,print:h,suppressErrors:g}=t;setTimeout(()=>{if(e.contentWindow){let a=function(){l?.(),j(m)};if(e.contentWindow.focus(),h)h(e).then(a).catch(c=>{y?y("print",D(c)):f({messages:["An error was thrown by the specified `print` function"],suppressErrors:g})});else{if(e.contentWindow.print){const c=e.contentDocument?.title??"",E=e.ownerDocument.title,p=typeof n=="function"?n():n;p&&(e.ownerDocument.title=p,e.contentDocument&&(e.contentDocument.title=p)),e.contentWindow.print(),p&&(e.ownerDocument.title=E,e.contentDocument&&(e.contentDocument.title=c))}else f({messages:["Printing for this browser is not currently possible: the browser does not have a `print` method available for iframes."],suppressErrors:g});z()?setTimeout(a,500):a()}}else f({messages:["Printing failed because the `contentWindow` of the print iframe did not load. This is possibly an error with `react-to-print`. Please file an issue: https://github.com/MatthewHerbst/react-to-print/issues/"],suppressErrors:g})},500)}function z(){return[/Android/i,/webOS/i,/iPhone/i,/iPad/i,/iPod/i,/BlackBerry/i,/Windows Phone/i].some(t=>(navigator.userAgent??navigator.vendor??("opera"in window&&window.opera)).match(t))}function O(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,null);let l=n.nextNode();for(;l;)t.push(l),l=n.nextNode();return t}function $(e,t,n){const l=O(e),y=O(t);if(l.length!==y.length){f({messages:["When cloning shadow root content, source and target elements have different size. `onBeforePrint` likely resolved too early.",e,t],suppressErrors:n});return}for(let m=0;m<l.length;m++){const h=l[m],g=y[m],a=h.shadowRoot;if(a!==null){const c=g.attachShadow({mode:a.mode});c.innerHTML=a.innerHTML,$(a,c,n)}}}const G=`
@page {
/* Remove browser default header (title) and footer (url) */
margin: 0;
}
@media print {
body {
/* Tell browsers to print background colors */
color-adjust: exact; /* Firefox. This is an older version of "print-color-adjust" */
print-color-adjust: exact; /* Firefox/Safari */
-webkit-print-color-adjust: exact; /* Chrome/Safari/Edge/Opera */
}
}
`;function W(e,t,n){const{contentNode:l,clonedContentNode:y,clonedImgNodes:m,clonedVideoNodes:h,numResourcesToLoad:g,originalCanvasNodes:a}=t,{bodyClass:c,fonts:E,ignoreGlobalStyles:p,pageStyle:C,nonce:T,suppressErrors:A,copyShadowRoots:F}=n,L=[],_=[];function i(k,P){if(L.includes(k)){f({level:"debug",messages:["Tried to mark a resource that has already been handled",k],suppressErrors:A});return}P?(f({messages:['"react-to-print" was unable to load a resource but will continue attempting to print the page',...P],suppressErrors:A}),_.push(k)):L.push(k),L.length+_.length===g&&H(e,n)}e.onload=null;const d=e.contentDocument??e.contentWindow?.document;if(d){const k=d.body.appendChild(y);F&&$(l,k,!!A),E&&(e.contentDocument?.fonts&&e.contentWindow?.FontFace?E.forEach(s=>{const o=new FontFace(s.family,s.source,{weight:s.weight,style:s.style});e.contentDocument.fonts.add(o),o.loaded.then(()=>{i(o)}).catch(b=>{i(o,["Failed loading the font:",o,"Load error:",D(b)])})}):(E.forEach(s=>{i(s)}),f({messages:['"react-to-print" is not able to load custom fonts because the browser does not support the FontFace API but will continue attempting to print the page'],suppressErrors:A})));const P=C??G,x=d.createElement("style");T&&(x.setAttribute("nonce",T),d.head.setAttribute("nonce",T)),x.appendChild(d.createTextNode(P)),d.head.appendChild(x),c&&d.body.classList.add(...c.split(" "));const U=d.querySelectorAll("canvas");for(let s=0;s<a.length;++s){const o=a[s],b=U[s];if(b===void 0){f({messages:["A canvas element could not be copied for printing, has it loaded? `onBeforePrint` likely resolved too early.",o],suppressErrors:A});continue}const r=b.getContext("2d");r&&r.drawImage(o,0,0)}for(let s=0;s<m.length;s++){const o=m[s],b=o.getAttribute("src");if(!b)i(o,['Found an <img> tag with an empty "src" attribute. This prevents pre-loading it.',o]);else{const r=new Image;r.onload=()=>{i(o)},r.onerror=(u,w,S,v,N)=>{i(o,["Error loading <img>",o,"Error",N])},r.src=b}}for(let s=0;s<h.length;s++){const o=h[s];o.preload="auto";const b=o.getAttribute("poster");if(b){const r=new Image;r.onload=()=>{i(o)},r.onerror=(u,w,S,v,N)=>{i(o,["Error loading video poster",b,"for video",o,"Error:",N])},r.src=b}else o.readyState>=2?i(o):o.src?(o.onloadeddata=()=>{i(o)},o.onerror=(r,u,w,S,v)=>{i(o,["Error loading video",o,"Error",v])},o.onstalled=()=>{i(o,["Loading video stalled, skipping",o])}):i(o,["Error loading video, `src` is empty",o])}const R="select",M=l.querySelectorAll(R),B=d.querySelectorAll(R);for(let s=0;s<M.length;s++)B[s].value=M[s].value;if(!p){const s=document.querySelectorAll("style, link[rel~='stylesheet'], link[as='style']");for(let o=0,b=s.length;o<b;++o){const r=s[o];if(r.tagName.toLowerCase()==="style"){const u=d.createElement(r.tagName),w=r.sheet;if(w){let S="";try{const v=w.cssRules.length;for(let N=0;N<v;++N)typeof w.cssRules[N].cssText=="string"&&(S+=`${w.cssRules[N].cssText}\r
`)}catch(v){f({messages:["A stylesheet could not be accessed. This is likely due to the stylesheet having cross-origin imports, and many browsers block script access to cross-origin stylesheets. See https://github.com/MatthewHerbst/react-to-print/issues/429 for details. You may be able to load the sheet by both marking the stylesheet with the cross `crossorigin` attribute, and setting the `Access-Control-Allow-Origin` header on the server serving the stylesheet. Alternatively, host the stylesheet on your domain to avoid this issue entirely.",r,`Original error: ${D(v).message}`],level:"warning"})}u.setAttribute("id",`react-to-print-${o}`),T&&u.setAttribute("nonce",T),u.appendChild(d.createTextNode(S)),d.head.appendChild(u)}}else if(r.getAttribute("href"))if(r.hasAttribute("disabled"))f({messages:["`react-to-print` encountered a <link> tag with a `disabled` attribute and will ignore it. Note that the `disabled` attribute is deprecated, and some browsers ignore it. You should stop using it. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-disabled. The <link> is:",r],level:"warning"}),i(r);else{const u=d.createElement(r.tagName);for(let w=0,S=r.attributes.length;w<S;++w){const v=r.attributes[w];v&&u.setAttribute(v.nodeName,v.nodeValue??"")}u.onload=()=>{i(u)},u.onerror=(w,S,v,N,I)=>{i(u,["Failed to load",u,"Error:",I])},T&&u.setAttribute("nonce",T),d.head.appendChild(u)}else f({messages:["`react-to-print` encountered a <link> tag with an empty `href` attribute. In addition to being invalid HTML, this can cause problems in many browsers, and so the <link> was not loaded. The <link> is:",r],level:"warning"}),i(r)}}}g===0&&H(e,n)}function J(e,t,n){e.onload=()=>{W(e,t,n)},document.body.appendChild(e)}function K({contentRef:e,optionalContent:t,suppressErrors:n}){if(t&&typeof t=="function")return e&&f({level:"warning",messages:['"react-to-print" received a `contentRef` option and an optional-content param passed to its callback. The `contentRef` option will be ignored.']}),t();if(e)return e.current;f({messages:['"react-to-print" did not receive a `contentRef` option or a optional-content param pass to its callback.'],suppressErrors:n})}function Q(e,t){const{contentRef:n,fonts:l,ignoreGlobalStyles:y,suppressErrors:m}=t,h=K({contentRef:n,optionalContent:e,suppressErrors:m});if(!h)return;const g=h.cloneNode(!0),a=document.querySelectorAll("link[rel~='stylesheet'], link[as='style']"),c=g.querySelectorAll("img"),E=g.querySelectorAll("video"),p=l?l.length:0,C=(y?0:a.length)+c.length+E.length+p;return{contentNode:h,clonedContentNode:g,clonedImgNodes:c,clonedVideoNodes:E,numResourcesToLoad:C,originalCanvasNodes:h.querySelectorAll("canvas")}}function X({bodyClass:e,contentRef:t,copyShadowRoots:n,documentTitle:l,fonts:y,ignoreGlobalStyles:m,nonce:h,onAfterPrint:g,onBeforePrint:a,onPrintError:c,pageStyle:E,preserveAfterPrint:p,print:C,printIframeProps:T,suppressErrors:A}){return Y.useCallback(L=>{j(p,!0);function _(){const i={bodyClass:e,contentRef:t,copyShadowRoots:n,documentTitle:l,fonts:y,ignoreGlobalStyles:m,nonce:h,onAfterPrint:g,onPrintError:c,pageStyle:E,preserveAfterPrint:p,print:C,suppressErrors:A},d=V(T),k=Q(L,i);if(!k){f({messages:["There is nothing to print"],suppressErrors:A});return}J(d,k,i)}a?a().then(()=>{_()}).catch(i=>{c?.("onBeforePrint",D(i))}):_()},[e,t,n,l,y,m,h,g,a,c,E,p,T,C,A])}exports.useReactToPrint=X;