ngx-print
Version:
Plug n' Play Angular (2++) directive to print your stuff
489 lines (480 loc) • 17.4 kB
JavaScript
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