UNPKG

preact-missing-hooks

Version:

A lightweight, extendable collection of missing React-like hooks for Preact — plus fresh, powerful new ones designed specifically for modern Preact apps.

117 lines (103 loc) 3.42 kB
import type { RefObject } from "preact"; import { useCallback, useRef } from "preact/hooks"; const PRINT_CLASS = "use-ref-print-target"; const PRINT_STYLE_ID = "use-ref-print-styles"; const PRINT_CSS = ` @media print { body * { visibility: hidden; } .${PRINT_CLASS}, .${PRINT_CLASS} * { visibility: visible; } .${PRINT_CLASS} { position: absolute !important; left: 0 !important; top: 0 !important; width: 100% !important; } } `; export interface UseRefPrintOptions { /** * When true, the print flow is triggered so the user can choose * "Save as PDF" in the native print dialog. Uses the same window.print() path. */ downloadAsPdf?: boolean; /** Title for the print document (e.g. used when saving as PDF). */ documentTitle?: string; } export interface UseRefPrintReturn { /** Triggers native print for the section bound to the ref (opens print dialog; only that section is printed via @media print). */ print: () => void; } /** * A Preact hook that binds a ref to a printable section and provides a function * to print only that section using the native window.print() and @media print CSS. * When print() is called (or user presses Ctrl+P after focusing that section), only * the ref section is visible in the print layout. User can then print or save as PDF. * * @param printRef - Ref to the DOM element (e.g. a div) that should be printed. * @param options - Optional: downloadAsPdf hint, documentTitle for the print document. * @returns Object with a print() function. * * @example * ```tsx * function Report() { * const printRef = useRef<HTMLDivElement>(null); * const { print } = useRefPrint(printRef, { documentTitle: 'Report', downloadAsPdf: true }); * return ( * <div> * <div ref={printRef}>Content to print or save as PDF</div> * <button onClick={print}>Print / Save as PDF</button> * </div> * ); * } * ``` */ export function useRefPrint( printRef: RefObject<HTMLElement | null>, options: UseRefPrintOptions = {} ): UseRefPrintReturn { const { documentTitle } = options; const originalTitleRef = useRef<string>(""); const print = useCallback(() => { const el = printRef.current; if (!el || typeof window === "undefined" || !window.print) { return; } // Ensure print-only styles exist (once per document) let styleEl = document.getElementById( PRINT_STYLE_ID ) as HTMLStyleElement | null; if (!styleEl) { styleEl = document.createElement("style"); styleEl.id = PRINT_STYLE_ID; styleEl.textContent = PRINT_CSS; document.head.appendChild(styleEl); } el.classList.add(PRINT_CLASS); if (documentTitle) { originalTitleRef.current = document.title; document.title = documentTitle; } const cleanup = () => { el.classList.remove(PRINT_CLASS); if (documentTitle && originalTitleRef.current !== undefined) { document.title = originalTitleRef.current; } }; if ("onafterprint" in window) { const onAfterPrint = () => { cleanup(); window.removeEventListener("afterprint", onAfterPrint); }; window.addEventListener("afterprint", onAfterPrint); } window.print(); // Fallback cleanup if afterprint is not fired (e.g. user cancels) setTimeout(cleanup, 1000); }, [printRef, documentTitle]); return { print }; }