Tldraw/packages/ui/src/lib/hooks/usePrint.ts

217 wiersze
5.0 KiB
TypeScript

import { uniqueId, useApp } from '@tldraw/editor'
import { useCallback, useRef } from 'react'
/** @public */
export function usePrint() {
const app = useApp()
const prevPrintEl = useRef<HTMLDivElement | null>(null)
const prevStyleEl = useRef<HTMLStyleElement | null>(null)
return useCallback(
async function printSelectionOrPages() {
const el = document.createElement('div')
const style = document.createElement('style')
const clearElements = (printEl: HTMLDivElement | null, styleEl: HTMLStyleElement | null) => {
if (printEl) printEl.innerHTML = ''
if (styleEl && document.head.contains(styleEl)) document.head.removeChild(styleEl)
if (printEl && document.body.contains(printEl)) {
document.body.removeChild(printEl)
}
}
// Always make sure we have a clean root element.
clearElements(prevPrintEl.current, prevStyleEl.current)
prevPrintEl.current = el
prevStyleEl.current = style
// Random because this isn't for end users
const className = `tl-print-surface-${uniqueId()}`
el.className = className
// NOTE: Works in most envs except safari, needs further review
const enableMargins = false
// NOTE: Currently buggy needs further investigation
const allowAllPages = false
style.innerHTML = `
.${className} {
display: none;
}
.${className} svg {
max-width: 100%;
height: 100%;
display: block;
}
@media print {
html, body {
min-height: 100%;
height: 100%;
margin: 0;
}
body {
position: relative;
}
body > * {
display: none;
}
.${className} {
display: block !important;
background: white;
min-height: 100%;
height: 100%;
max-width: 100%;
}
.${className}__item {
padding: 10mm;
display: flex;
min-height: 100%;
flex-direction: column;
page-break-after: always;
position: relative;
overflow: hidden;
height: 100%;
}
.${className}__item__main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
max-height: 100%;
}
.${className}__item__header {
display: none;
}
.${className}__item__footer {
display: none;
text-align: right;
}
.${className}__item__footer__hide {
display: none;
}
${
!enableMargins
? ''
: `
/**
* Note: Safari doesn't support removing the page margins to remove them all!
*/
@page {
margin:0;
}
.${className} .${className}__item__header {
display: block;
}
.${className} .${className}__item__footer {
display: block;
}
`
}
}
`
const beforePrintHandler = () => {
document.head.appendChild(style)
document.body.appendChild(el)
}
const afterPrintHandler = () => {
app.once('change-history', () => {
clearElements(el, style)
})
}
window.addEventListener('beforeprint', beforePrintHandler)
window.addEventListener('afterprint', afterPrintHandler)
function addPageToPrint(title: string, footer: string | null, svg: SVGElement) {
try {
el.innerHTML += `<div class="${className}__item">
<div class="${className}__item__header">
${title.replace(/</g, '&lt;').replace(/>/g, '&gt;')}
</div>
<div class="${className}__item__main">
${svg.outerHTML}
</div>
<div class="${className}__item__footer ${className}__item__footer__${footer ? '' : 'hide'}">
${footer ?? ''}
</div>
</div>`
} catch (e) {
console.error(e)
}
}
function triggerPrint() {
if (app.isChromeForIos) {
beforePrintHandler()
window.print()
} else if (app.isSafari) {
beforePrintHandler()
document.execCommand('print', false)
} else {
window.print()
}
}
const { pages, currentPageId, selectedIds } = app
const preserveAspectRatio = 'xMidYMid meet'
const svgOpts = {
scale: 1,
background: false,
darkMode: false,
preserveAspectRatio,
}
if (app.selectedIds.length > 0) {
// Print the selected ids from the current page
const svg = await app.getSvg(selectedIds, svgOpts)
if (svg) {
const page = pages.find((p) => p.id === currentPageId)
addPageToPrint(`tldraw — ${page?.name}`, null, svg)
triggerPrint()
}
} else {
if (allowAllPages) {
// Print all pages
for (let i = 0; i < pages.length; i++) {
const page = pages[i]
const svg = await app.getSvg(app.getSortedChildIds(page.id), svgOpts)
if (svg) {
addPageToPrint(`tldraw — ${page.name}`, `${i}/${pages.length}`, svg)
}
}
triggerPrint()
} else {
const page = app.currentPage
const svg = await app.getSvg(app.getSortedChildIds(page.id), svgOpts)
if (svg) {
addPageToPrint(`tldraw — ${page.name}`, null, svg)
triggerPrint()
}
}
}
window.removeEventListener('beforeprint', beforePrintHandler)
window.removeEventListener('afterprint', afterPrintHandler)
},
[app]
)
}