5
5
* Use of this source code is governed by an MIT-style license that can be
6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
- import { Inject , Injectable } from '@angular/core' ;
8
+ import { Inject , Injectable , PLATFORM_ID } from '@angular/core' ;
9
9
10
10
import { mergeAlias } from '../add-alias' ;
11
11
import { MediaChange } from '../media-change' ;
12
12
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 { isPlatformBrowser } 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 ( PLATFORM_ID ) protected _platformId : Object ) {
43
45
}
44
46
45
47
/** Add 'print' mediaQuery: to listen for matchMedia activations */
@@ -83,18 +85,65 @@ 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 the platform is
103
+ // not a browser(fallsback to mql print media queries).
104
+ private registerBeforeAfterPrintHooks ( target : HookTarget ) {
105
+ if ( ! isPlatformBrowser ( this . _platformId ) || this . registeredBeforeAfterPrintHooks ) {
106
+ return ;
107
+ }
108
+
109
+ this . registeredBeforeAfterPrintHooks = true ;
110
+
111
+ // Could we have teardown logic to remove if there are no print listeners being used?
112
+ ( < Window > window ) . addEventListener ( 'beforeprint' , ( ) => {
113
+ // If we aren't already printing, start printing and update the styles as
114
+ // if there was a regular print `MediaChange`(from matchMedia).
115
+ if ( ! this . isPrinting ) {
116
+ this . isPrintingBeforeAfterEvent = true ;
117
+ this . startPrinting ( target , this . getEventBreakpoints ( new MediaChange ( true , PRINT ) ) ) ;
118
+ target . updateStyles ( ) ;
119
+ }
120
+ } ) ;
121
+
122
+ ( < Window > window ) . addEventListener ( 'afterprint' , ( ) => {
123
+ // If we aren't already printing, start printing and update the styles as
124
+ // if there was a regular print `MediaChange`(from matchMedia).
125
+ this . isPrintingBeforeAfterEvent = false ;
126
+ if ( this . isPrinting ) {
127
+ this . stopPrinting ( target ) ;
128
+ target . updateStyles ( ) ;
129
+ }
130
+ } ) ;
131
+ }
132
+
86
133
/**
87
134
* Prepare RxJs filter operator with partial application
88
135
* @return pipeable filter predicate
89
136
*/
90
137
interceptEvents ( target : HookTarget ) {
138
+ this . registerBeforeAfterPrintHooks ( target ) ;
139
+
91
140
return ( event : MediaChange ) => {
92
141
if ( this . isPrintEvent ( event ) ) {
93
142
if ( event . matches && ! this . isPrinting ) {
94
143
this . startPrinting ( target , this . getEventBreakpoints ( event ) ) ;
95
144
target . updateStyles ( ) ;
96
145
97
- } else if ( ! event . matches && this . isPrinting ) {
146
+ } else if ( ! event . matches && this . isPrinting && ! this . isPrintingBeforeAfterEvent ) {
98
147
this . stopPrinting ( target ) ;
99
148
target . updateStyles ( ) ;
100
149
}
@@ -131,7 +180,8 @@ export class PrintHook {
131
180
/**
132
181
* To restore pre-Print Activations, we must capture the proper
133
182
* list of breakpoint activations BEFORE print starts. OnBeforePrint()
134
- * is not supported; so 'print' mediaQuery activations must be used.
183
+ * is supported; so 'print' mediaQuery activations are used as a fallback
184
+ * in browsers without `beforeprint` support.
135
185
*
136
186
* > But activated breakpoints are deactivated BEFORE 'print' activation.
137
187
*
@@ -146,14 +196,17 @@ export class PrintHook {
146
196
* - restore as activatedTargets and clear when stop printing
147
197
*/
148
198
collectActivations ( event : MediaChange ) {
149
- if ( ! this . isPrinting ) {
199
+ if ( ! this . isPrinting || this . isPrintingBeforeAfterEvent ) {
150
200
if ( ! event . matches ) {
151
201
const bp = this . breakpoints . findByQuery ( event . mediaQuery ) ;
152
202
if ( bp ) { // Deactivating a breakpoint
153
203
this . deactivations . push ( bp ) ;
154
204
this . deactivations . sort ( sortDescendingPriority ) ;
155
205
}
156
- } else {
206
+ } else if ( ! this . isPrintingBeforeAfterEvent ) {
207
+ // Only clear deactivations if we aren't printing from a `beforeprint` event.
208
+ // Otherwise this will clear before `stopPrinting()` is called to restore
209
+ // the pre-Print Activations.
157
210
this . deactivations = [ ] ;
158
211
}
159
212
}
0 commit comments