Source

src/hooks/usePrintReport.js

import { useEffect, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import download from 'downloadjs'
import { toJpeg, toPng, toSvg } from 'html-to-image'
import { reportSelectors, reportActions } from '../stores/reportStore'
const {
    selectPrintStatus,
    selectCurrentPage,
    selectIsLoaded,
    selectCurrentReport,
    selectPrintFileType,
    selectcurrentReportLength,
} = reportSelectors
const { setCurrentPage, setPrintingState } = reportActions
/**
 * @typedef {Object} PrintReportReturn
 * @property {function} handlePrint - function to print current report async (filetype: 'JPG','PNG','PDF') => Promise<void>
 * @property {function} handleRef - function to asign ref to current report page (ref: React.RefObject<HTMLDivElement>) => void
 */

/**
 * Hook to print current report
 * 
 * @category Hooks
 * @returns {PrintReportReturn}
 */
function usePrintReport() {
    const isPrinting = useSelector(selectPrintStatus)
    const printFileType = useSelector(selectPrintFileType)
    const currentReport = useSelector(selectCurrentReport)
    const pageLength = useSelector(selectcurrentReportLength)
    const pageIdx = useSelector(selectCurrentPage)
    const pageIsLoaded = useSelector(selectIsLoaded)

    const currentPageRef = useRef(null)
    const [pageData, setPageData] = useState([])
    const [shouldPrintPdf, setShouldPrintPdf] = useState(false)

    const dispatch = useDispatch()
    const handleRef = (ref) => (currentPageRef.current = ref)
    const handleAddPageData = (data) => setPageData((prev) => [...prev, data])
    const handleResetPdfPrint = () => {
        setPageData([])
        setShouldPrintPdf(false)
    }

    const setPageIdx = (page) => {
        dispatch(setCurrentPage(page))
    }

    const incrementPage = () => {
        if (pageIdx < pageLength - 1) {
            setPageIdx(pageIdx + 1)
        } else {
            handleEndPrinting()
        }
    }
    const handlePrint = (fileType) => {
        dispatch(
            setPrintingState({
                status: true,
                fileType,
            })
        )
    }
    const handleStopPrint = () => {
        if (isPrinting) {
            dispatch(
                setPrintingState({
                    status: false,
                })
            )
        }
    }

    const handleSavePdf = async () => {
        const jspdf = await import('jspdf')
        const jsPDF = jspdf.jsPDF
        const doc = new jsPDF({
            format: 'letter',
            unit: 'in',
        })
        pageData.forEach((page, i) => {
            doc.addImage(page, 0, 0, 8.5, 11)
            i < pageLength - 1 && doc.addPage()
        })
        doc.save(`${currentReport}.pdf`)
    }

    const handleEndPrinting = async () => {
        switch (printFileType) {
            case 'PDF':
                setShouldPrintPdf(true)
                break
            default:
                handleStopPrint()
                break
        }
    }

    const printPage = async () => {
        switch (printFileType) {
            case 'PDF': {
                const png = await toPng(currentPageRef.current, {
                    pixelRatio: 2,
                })
                handleAddPageData(png)
                break
            }
            case 'JPG': {
                const jpg = await toJpeg(currentPageRef.current, {
                    pixelRatio: 2,
                })
                return await download(
                    jpg,
                    `${currentReport}-page-${pageIdx + 1}.jpg`,
                    'image/jpeg'
                )
            }
            case 'PNG': {
                const png = await toPng(currentPageRef.current, {
                    pixelRatio: 2,
                })
                return await download(
                    png,
                    `${currentReport}-page-${pageIdx + 1}.png`,
                    'image/png'
                )
            }
            case 'SVG': {
                const svg = await toSvg(currentPageRef.current, {
                    fontEmbedCSS: false,
                    skipFonts: true,
                })
                // doing this the ol' fashioned way due to issues with SVG encoding :)
                let link = document.createElement('a')
                document.body.appendChild(link)
                link.setAttribute('href', svg)
                link.setAttribute(
                    'download',
                    `${currentReport}-page-${pageIdx + 1}.svg`
                )
                link.click()
                link.remove()
                return
            }
            default:
                break
        }
    }

    useEffect(() => {
        if (pageIdx === -1) {
            incrementPage()
        } else if (isPrinting && pageIsLoaded) {
            printPage().then(() => incrementPage())
        }
    }, [isPrinting, pageIdx, pageIsLoaded])

    useEffect(() => {
        if (shouldPrintPdf && pageData.length === pageLength) {
            handleSavePdf().then(() => {
                handleStopPrint()
                handleResetPdfPrint()
            })
        }
    }, [shouldPrintPdf, pageData.length])

    useEffect(() => {
        handleStopPrint()
    }, [currentReport])

    return {
        handlePrint,
        handleRef,
    }
}

export { usePrintReport }