@@ -13,6 +13,7 @@ import {BreakPoint} from '../breakpoints/break-point';
13
13
import { LAYOUT_CONFIG , LayoutConfigOptions } from '../tokens/library-config' ;
14
14
import { BreakPointRegistry , OptionalBreakPoint } from '../breakpoints/break-point-registry' ;
15
15
import { sortDescendingPriority } from '../utils/sort' ;
16
+ import { DOCUMENT } from '@angular/common' ;
16
17
17
18
/**
18
19
* Interface to apply PrintHook to call anonymous `target.updateStyles()`
@@ -39,7 +40,8 @@ export const BREAKPOINT_PRINT = {
39
40
export class PrintHook {
40
41
constructor (
41
42
protected breakpoints : BreakPointRegistry ,
42
- @Inject ( LAYOUT_CONFIG ) protected layoutConfig : LayoutConfigOptions ) {
43
+ @Inject ( LAYOUT_CONFIG ) protected layoutConfig : LayoutConfigOptions ,
44
+ @Inject ( DOCUMENT ) protected _document : any ) {
43
45
}
44
46
45
47
/** Add 'print' mediaQuery: to listen for matchMedia activations */
@@ -83,18 +85,66 @@ export class PrintHook {
83
85
return mergeAlias ( event , bp ) ;
84
86
}
85
87
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
+
86
134
/**
87
135
* Prepare RxJs filter operator with partial application
88
136
* @return pipeable filter predicate
89
137
*/
90
138
interceptEvents ( target : HookTarget ) {
139
+ this . registerBeforeAfterPrintHooks ( target ) ;
140
+
91
141
return ( event : MediaChange ) => {
92
142
if ( this . isPrintEvent ( event ) ) {
93
143
if ( event . matches && ! this . isPrinting ) {
94
144
this . startPrinting ( target , this . getEventBreakpoints ( event ) ) ;
95
145
target . updateStyles ( ) ;
96
146
97
- } else if ( ! event . matches && this . isPrinting ) {
147
+ } else if ( ! event . matches && this . isPrinting && ! this . isPrintingBeforeAfterEvent ) {
98
148
this . stopPrinting ( target ) ;
99
149
target . updateStyles ( ) ;
100
150
}
@@ -131,7 +181,8 @@ export class PrintHook {
131
181
/**
132
182
* To restore pre-Print Activations, we must capture the proper
133
183
* 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.
135
186
*
136
187
* > But activated breakpoints are deactivated BEFORE 'print' activation.
137
188
*
@@ -146,14 +197,17 @@ export class PrintHook {
146
197
* - restore as activatedTargets and clear when stop printing
147
198
*/
148
199
collectActivations ( event : MediaChange ) {
149
- if ( ! this . isPrinting ) {
200
+ if ( ! this . isPrinting || this . isPrintingBeforeAfterEvent ) {
150
201
if ( ! event . matches ) {
151
202
const bp = this . breakpoints . findByQuery ( event . mediaQuery ) ;
152
203
if ( bp ) { // Deactivating a breakpoint
153
204
this . deactivations . push ( bp ) ;
154
205
this . deactivations . sort ( sortDescendingPriority ) ;
155
206
}
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.
157
211
this . deactivations = [ ] ;
158
212
}
159
213
}
0 commit comments