UNPKG

ngx-print

Version:

Plug n' Play Angular (2++) directive to print your stuff

489 lines (480 loc) 17.4 kB
import * as i0 from '@angular/core'; import { inject, CSP_NONCE, Injectable, output, HostListener, Input, Directive, NgModule } from '@angular/core'; import { Subject, take } from 'rxjs'; class PrintBase { nonce = inject(CSP_NONCE, { optional: true }); _printStyle = []; _styleSheetFile = ''; printComplete = new Subject(); //#region Getters and Setters /** * Sets the print styles based on the provided values. * * @param {Object} values - Key-value pairs representing print styles. * @protected */ setPrintStyle(values) { this._printStyle = []; for (const key in values) { if (Object.prototype.hasOwnProperty.call(values, key)) { this._printStyle.push((key + JSON.stringify(values[key])).replace(/['"]+/g, '')); } } } /** * * * @returns the string that create the stylesheet which will be injected * later within <style></style> tag. * * -join/replace to transform an array objects to css-styled string */ returnStyleValues() { const styleNonce = this.nonce ? ` nonce="${this.nonce}"` : ''; return `<style${styleNonce}> ${this._printStyle.join(' ').replace(/,/g, ';')} </style>`; } /** * @returns string which contains the link tags containing the css which will * be injected later within <head></head> tag. * */ returnStyleSheetLinkTags() { return this._styleSheetFile; } /** * Sets the style sheet file based on the provided CSS list. * * @param {string} cssList - CSS file or list of CSS files. * @protected */ setStyleSheetFile(cssList) { const linkTagFn = function (cssFileName) { return `<link rel="stylesheet" type="text/css" href="${cssFileName}">`; }; if (cssList.indexOf(',') !== -1) { const valueArr = cssList.split(','); this._styleSheetFile = valueArr.map(val => linkTagFn(val)).join(''); } else { this._styleSheetFile = linkTagFn(cssList); } } //#endregion //#region Private methods used by PrintBase /** * Updates the default values for input elements. * * @param {HTMLCollectionOf<HTMLInputElement>} elements - Collection of input elements. * @private */ updateInputDefaults(elements) { for (let i = 0; i < elements.length; i++) { const element = elements[i]; element['defaultValue'] = element.value; if (element['checked']) element['defaultChecked'] = true; } } /** * Updates the default values for select elements. * * @param {HTMLCollectionOf<HTMLSelectElement>} elements - Collection of select elements. * @private */ updateSelectDefaults(elements) { for (let i = 0; i < elements.length; i++) { const element = elements[i]; const selectedIdx = element.selectedIndex; const selectedOption = element.options[selectedIdx]; selectedOption.defaultSelected = true; } } /** * Updates the default values for textarea elements. * * @param {HTMLCollectionOf<HTMLTextAreaElement>} elements - Collection of textarea elements. * @private */ updateTextAreaDefaults(elements) { for (let i = 0; i < elements.length; i++) { const element = elements[i]; element['defaultValue'] = element.value; } } /** * Converts a canvas element to an image and returns its HTML string. * * @param {HTMLCanvasElement} element - The canvas element to convert. * @returns {string} - HTML string of the image. * @private */ canvasToImageHtml(element) { const dataUrl = element.toDataURL(); return `<img src="${dataUrl}" style="max-width: 100%;">`; } /** * Includes canvas contents in the print section via img tags. * * @param {HTMLCollectionOf<HTMLCanvasElement>} elements - Collection of canvas elements. * @private */ updateCanvasToImage(elements) { for (let i = 0; i < elements.length; i++) { const element = this.canvasToImageHtml(elements[i]); elements[i].insertAdjacentHTML('afterend', element); elements[i].remove(); } } /** * Retrieves the HTML content of a specified printing section. * * @param {string} printSectionId - Id of the printing section. * @returns {string | null} - HTML content of the printing section, or null if not found. * @private */ getHtmlContents(printSectionId) { const printContents = document.getElementById(printSectionId); if (!printContents) return null; const inputEls = printContents.getElementsByTagName('input'); const selectEls = printContents.getElementsByTagName('select'); const textAreaEls = printContents.getElementsByTagName('textarea'); const canvasEls = printContents.getElementsByTagName('canvas'); this.updateInputDefaults(inputEls); this.updateSelectDefaults(selectEls); this.updateTextAreaDefaults(textAreaEls); this.updateCanvasToImage(canvasEls); return printContents.innerHTML; } /** * Retrieves the HTML content of elements with the specified tag. * * @param {keyof HTMLElementTagNameMap} tag - HTML tag name. * @returns {string} - Concatenated outerHTML of elements with the specified tag. * @private */ getElementTag(tag) { const html = []; const elements = document.getElementsByTagName(tag); for (let index = 0; index < elements.length; index++) { html.push(elements[index].outerHTML); } return html.join('\r\n'); } //#endregion notifyPrintComplete() { this.printComplete.next(); } /** * Prints the specified content using the provided print options. * * @param {PrintOptions} printOptions - Options for printing. * @public */ print(printOptions) { let styles = '', links = '', popOut = 'top=0,left=0,height=auto,width=auto'; const baseTag = this.getElementTag('base'); if (printOptions.useExistingCss) { styles = this.getElementTag('style'); links = this.getElementTag('link'); } // If the openNewTab option is set to true, then set the popOut option to an empty string. // This will cause the print dialog to open in a new tab. if (printOptions.openNewTab) { popOut = ''; } const printContents = this.getHtmlContents(printOptions.printSectionId); if (!printContents) { // Handle the case where the specified print section is not found. console.error(`Print section with id ${printOptions.printSectionId} not found.`); return; } const popupWin = window.open('', '_blank', popOut); if (!popupWin) { // the popup window could not be opened. console.error('Could not open print window.'); return; } popupWin.document.open(); // Create the HTML structure const doc = popupWin.document; // Set up the basic HTML structure const html = doc.createElement('html'); const head = doc.createElement('head'); const body = doc.createElement('body'); // Set title const title = doc.createElement('title'); title.textContent = printOptions.printTitle || ''; head.appendChild(title); // Add base tag, styles, and links if (baseTag) { head.innerHTML += baseTag; } head.innerHTML += this.returnStyleValues(); head.innerHTML += this.returnStyleSheetLinkTags(); head.innerHTML += styles; head.innerHTML += links; // Set body class if provided if (printOptions.bodyClass) { body.className = printOptions.bodyClass; } // Insert print contents body.innerHTML += printContents; // Add script const script = doc.createElement('script'); script.defer = true; if (this.nonce) { script.setAttribute('nonce', this.nonce); } script.textContent = ` function triggerPrint(event) { window.removeEventListener('load', triggerPrint, false); ${printOptions.previewOnly ? '' : `setTimeout(function() { closeWindow(window.print()); }, ${printOptions.printDelay});`} } function closeWindow(){ ${printOptions.closeWindow ? 'window.close();' : ''} } window.addEventListener('load', triggerPrint, false); window.addEventListener('afterprint', function () { if (window.opener) { window.opener.postMessage({ type: 'print-complete' }, '*'); } closeWindow(); }, { once: true }); `; body.appendChild(script); // Assemble the document html.appendChild(head); html.appendChild(body); doc.appendChild(html); popupWin.document.close(); const handleMessage = (event) => { if (event.data?.type === 'print-complete') { // Notify of print completion this.notifyPrintComplete(); window.removeEventListener('message', handleMessage); } }; window.addEventListener('message', handleMessage); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: PrintBase, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: PrintBase, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: PrintBase, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); /** * Service for handling printing functionality in Angular applications. * Extends the base printing class (PrintBase). * * @export * @class NgxPrintService * @extends {PrintBase} */ class NgxPrintService extends PrintBase { printComplete$ = this.printComplete.asObservable(); /** * Initiates the printing process using the provided print options. * * @param {PrintOptions} printOptions - Options for configuring the printing process. * @memberof NgxPrintService * @returns {void} */ print(printOptions) { // Call the print method in the parent class super.print(printOptions); } /** * Sets the print style for the printing process. * * @param {{ [key: string]: { [key: string]: string } }} values - A dictionary representing the print styles. * @memberof NgxPrintService * @setter */ set printStyle(values) { super.setPrintStyle(values); } /** * Sets the stylesheet file for the printing process. * * @param {string} cssList - A string representing the path to the stylesheet file. * @memberof NgxPrintService * @setter */ set styleSheetFile(cssList) { super.setStyleSheetFile(cssList); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class PrintOptions { printSectionId = ''; printTitle = ''; useExistingCss = false; bodyClass = ''; openNewTab = false; previewOnly = false; closeWindow = true; printDelay = 0; constructor(options) { if (options) { Object.assign(this, options); } } } class NgxPrintDirective extends PrintBase { printOptions = new PrintOptions(); /** * Prevents the print dialog from opening on the window * * @memberof NgxPrintDirective */ set previewOnly(value) { this.printOptions = { ...this.printOptions, previewOnly: value }; } /** * * * @memberof NgxPrintDirective */ set printSectionId(value) { this.printOptions = { ...this.printOptions, printSectionId: value }; } /** * * * @memberof NgxPrintDirective */ set printTitle(value) { this.printOptions = { ...this.printOptions, printTitle: value }; } /** * * * @memberof NgxPrintDirective */ set useExistingCss(value) { this.printOptions = { ...this.printOptions, useExistingCss: value }; } /** * A delay in milliseconds to force the print dialog to wait before opened. Default: 0 * * @memberof NgxPrintDirective */ set printDelay(value) { this.printOptions = { ...this.printOptions, printDelay: value }; } /** * Whether to close the window after print() returns. * */ set closeWindow(value) { this.printOptions = { ...this.printOptions, closeWindow: value }; } /** * Class attribute to apply to the body element. * */ set bodyClass(value) { this.printOptions = { ...this.printOptions, bodyClass: value }; } /** * Whether to open a new window or default to new window. * */ set openNewTab(value) { this.printOptions = { ...this.printOptions, openNewTab: value }; } /** * * * @memberof NgxPrintDirective */ set printStyle(values) { super.setPrintStyle(values); } /** * @memberof NgxPrintDirective * @param cssList */ set styleSheetFile(cssList) { super.setStyleSheetFile(cssList); } /** * * * @memberof NgxPrintDirective */ print() { super.print(this.printOptions); this.printComplete.pipe(take(1)).subscribe(() => { this.printCompleted.emit(undefined); }); } printCompleted = output(); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.0", type: NgxPrintDirective, isStandalone: true, selector: "[ngxPrint]", inputs: { previewOnly: "previewOnly", printSectionId: "printSectionId", printTitle: "printTitle", useExistingCss: "useExistingCss", printDelay: "printDelay", closeWindow: "closeWindow", bodyClass: "bodyClass", openNewTab: "openNewTab", printStyle: "printStyle", styleSheetFile: "styleSheetFile" }, outputs: { printCompleted: "printCompleted" }, host: { listeners: { "click": "print()" } }, usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintDirective, decorators: [{ type: Directive, args: [{ selector: '[ngxPrint]', standalone: true, }] }], propDecorators: { previewOnly: [{ type: Input }], printSectionId: [{ type: Input }], printTitle: [{ type: Input }], useExistingCss: [{ type: Input }], printDelay: [{ type: Input }], closeWindow: [{ type: Input }], bodyClass: [{ type: Input }], openNewTab: [{ type: Input }], printStyle: [{ type: Input }], styleSheetFile: [{ type: Input }], print: [{ type: HostListener, args: ['click'] }] } }); class NgxPrintModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintModule, imports: [NgxPrintDirective], exports: [NgxPrintDirective] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintModule }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: NgxPrintModule, decorators: [{ type: NgModule, args: [{ imports: [NgxPrintDirective], exports: [NgxPrintDirective], }] }] }); /* * Public API Surface of ngx-print */ /** * Generated bundle index. Do not edit. */ export { NgxPrintDirective, NgxPrintModule, NgxPrintService, PrintOptions }; //# sourceMappingURL=ngx-print.mjs.map