@@ -13,6 +13,7 @@ import {BreakPoint} from '../breakpoints/break-point';
1313import { LAYOUT_CONFIG , LayoutConfigOptions } from '../tokens/library-config' ;
1414import { BreakPointRegistry , OptionalBreakPoint } from '../breakpoints/break-point-registry' ;
1515import { sortDescendingPriority } from '../utils/sort' ;
16+ import { DOCUMENT } from '@angular/common' ;
1617
1718/**
1819 * Interface to apply PrintHook to call anonymous `target.updateStyles()`
@@ -39,7 +40,8 @@ export const BREAKPOINT_PRINT = {
3940export class PrintHook {
4041 constructor (
4142 protected breakpoints : BreakPointRegistry ,
42- @Inject ( LAYOUT_CONFIG ) protected layoutConfig : LayoutConfigOptions ) {
43+ @Inject ( LAYOUT_CONFIG ) protected layoutConfig : LayoutConfigOptions ,
44+ @Inject ( DOCUMENT ) protected _document : any ) {
4345 }
4446
4547 /** Add 'print' mediaQuery: to listen for matchMedia activations */
@@ -83,18 +85,66 @@ export class PrintHook {
8385 return mergeAlias ( event , bp ) ;
8486 }
8587
88+
89+ // registeredBeforeAfterPrintHooks tracks if we registered the `beforeprint`
90+ // and `afterprint` event listeners.
91+ private registeredBeforeAfterPrintHooks : boolean = false ;
92+
93+ // isPrintingBeforeAfterEvent is used to track if we are printing from within
94+ // a `beforeprint` event handler. This prevents the typicall `stopPrinting`
95+ // form `interceptEvents` so that printing is not stopped while the dialog
96+ // is still open. This is an extension of the `isPrinting` property on
97+ // browsers which support `beforeprint` and `afterprint` events.
98+ private isPrintingBeforeAfterEvent : boolean = false ;
99+
100+ // registerBeforeAfterPrintHooks registers a `beforeprint` event hook so we can
101+ // trigger print styles synchronously and apply proper layout styles.
102+ // It is a noop if the hooks have already been registered or if the document's
103+ // `defaultView` is not available.
104+ private registerBeforeAfterPrintHooks ( target : HookTarget ) {
105+ // `defaultView` may be null when rendering on the server or in other contexts.
106+ if ( ! this . _document . defaultView || this . registeredBeforeAfterPrintHooks ) {
107+ return ;
108+ }
109+
110+ this . registeredBeforeAfterPrintHooks = true ;
111+
112+ // Could we have teardown logic to remove if there are no print listeners being used?
113+ this . _document . defaultView . addEventListener ( 'beforeprint' , ( ) => {
114+ // If we aren't already printing, start printing and update the styles as
115+ // if there was a regular print `MediaChange`(from matchMedia).
116+ if ( ! this . isPrinting ) {
117+ this . isPrintingBeforeAfterEvent = true ;
118+ this . startPrinting ( target , this . getEventBreakpoints ( new MediaChange ( true , PRINT ) ) ) ;
119+ target . updateStyles ( ) ;
120+ }
121+ } ) ;
122+
123+ this . _document . defaultView . addEventListener ( 'afterprint' , ( ) => {
124+ // If we aren't already printing, start printing and update the styles as
125+ // if there was a regular print `MediaChange`(from matchMedia).
126+ this . isPrintingBeforeAfterEvent = false ;
127+ if ( this . isPrinting ) {
128+ this . stopPrinting ( target ) ;
129+ target . updateStyles ( ) ;
130+ }
131+ } ) ;
132+ }
133+
86134 /**
87135 * Prepare RxJs filter operator with partial application
88136 * @return pipeable filter predicate
89137 */
90138 interceptEvents ( target : HookTarget ) {
139+ this . registerBeforeAfterPrintHooks ( target ) ;
140+
91141 return ( event : MediaChange ) => {
92142 if ( this . isPrintEvent ( event ) ) {
93143 if ( event . matches && ! this . isPrinting ) {
94144 this . startPrinting ( target , this . getEventBreakpoints ( event ) ) ;
95145 target . updateStyles ( ) ;
96146
97- } else if ( ! event . matches && this . isPrinting ) {
147+ } else if ( ! event . matches && this . isPrinting && ! this . isPrintingBeforeAfterEvent ) {
98148 this . stopPrinting ( target ) ;
99149 target . updateStyles ( ) ;
100150 }
@@ -131,7 +181,8 @@ export class PrintHook {
131181 /**
132182 * To restore pre-Print Activations, we must capture the proper
133183 * list of breakpoint activations BEFORE print starts. OnBeforePrint()
134- * is not supported; so 'print' mediaQuery activations must be used.
184+ * is supported; so 'print' mediaQuery activations are used as a fallback
185+ * in browsers without `beforeprint` support.
135186 *
136187 * > But activated breakpoints are deactivated BEFORE 'print' activation.
137188 *
@@ -146,14 +197,17 @@ export class PrintHook {
146197 * - restore as activatedTargets and clear when stop printing
147198 */
148199 collectActivations ( event : MediaChange ) {
149- if ( ! this . isPrinting ) {
200+ if ( ! this . isPrinting || this . isPrintingBeforeAfterEvent ) {
150201 if ( ! event . matches ) {
151202 const bp = this . breakpoints . findByQuery ( event . mediaQuery ) ;
152203 if ( bp ) { // Deactivating a breakpoint
153204 this . deactivations . push ( bp ) ;
154205 this . deactivations . sort ( sortDescendingPriority ) ;
155206 }
156- } else {
207+ } else if ( ! this . isPrintingBeforeAfterEvent ) {
208+ // Only clear deactivations if we aren't printing from a `beforeprint` event.
209+ // Otherwise this will clear before `stopPrinting()` is called to restore
210+ // the pre-Print Activations.
157211 this . deactivations = [ ] ;
158212 }
159213 }
0 commit comments