Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit 569d6a2

Browse files
committed
feat(core): support beforeprint and afterprint hooks
Extends the PrintHook service to also register beforeprint and afterprint event handlers to synchronously update styles and prevent layout switching races. Related #603
1 parent 31cb34e commit 569d6a2

File tree

1 file changed

+53
-4
lines changed

1 file changed

+53
-4
lines changed

src/lib/core/media-marshaller/print-hook.ts

+53-4
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,63 @@ export class PrintHook {
8383
return mergeAlias(event, bp);
8484
}
8585

86+
87+
// registeredBeforeAfterPrintHooks tracks if we registered the `beforeprint`
88+
// and `afterprint` event listeners.
89+
private registeredBeforeAfterPrintHooks: boolean = false;
90+
91+
// isPrintingBeforeAfterEvent is used to track if we are printing from within
92+
// a `beforeprint` event handler. This prevents the typicall `stopPrinting`
93+
// form `interceptEvents` so that printing is not stopped while the dialog
94+
// is still open. This is an extension of the `isPrinting` property on
95+
// browsers which support `beforeprint` and `afterprint` events.
96+
private isPrintingBeforeAfterEvent: boolean = false;
97+
98+
private registerBeforeAfterPrintHooks(target: HookTarget) {
99+
// Register a `beforeprint` event hook so we can trigger print styles
100+
// synchronously and apply proper layout styles.
101+
if (this.registeredBeforeAfterPrintHooks) {
102+
return;
103+
}
104+
105+
this.registeredBeforeAfterPrintHooks = true;
106+
107+
// Could we have teardown logic to remove if there are no print listeners being used?
108+
window.addEventListener('beforeprint', () => {
109+
// If we aren't already printing, start printing and update the styles as
110+
// if there was a regular print `MediaChange`(from matchMedia).
111+
if (!this.isPrinting) {
112+
this.isPrintingBeforeAfterEvent = true;
113+
this.startPrinting(target, this.getEventBreakpoints(new MediaChange(true, PRINT)));
114+
target.updateStyles();
115+
}
116+
});
117+
118+
window.addEventListener('afterprint', () => {
119+
// If we aren't already printing, start printing and update the styles as
120+
// if there was a regular print `MediaChange`(from matchMedia).
121+
this.isPrintingBeforeAfterEvent = false;
122+
if (this.isPrinting) {
123+
this.stopPrinting(target);
124+
target.updateStyles();
125+
}
126+
});
127+
}
128+
86129
/**
87130
* Prepare RxJs filter operator with partial application
88131
* @return pipeable filter predicate
89132
*/
90133
interceptEvents(target: HookTarget) {
134+
this.registerBeforeAfterPrintHooks(target);
135+
91136
return (event: MediaChange) => {
92137
if (this.isPrintEvent(event)) {
93138
if (event.matches && !this.isPrinting) {
94139
this.startPrinting(target, this.getEventBreakpoints(event));
95140
target.updateStyles();
96141

97-
} else if (!event.matches && this.isPrinting) {
142+
} else if (!event.matches && this.isPrinting && !this.isPrintingBeforeAfterEvent) {
98143
this.stopPrinting(target);
99144
target.updateStyles();
100145
}
@@ -131,7 +176,8 @@ export class PrintHook {
131176
/**
132177
* To restore pre-Print Activations, we must capture the proper
133178
* list of breakpoint activations BEFORE print starts. OnBeforePrint()
134-
* is not supported; so 'print' mediaQuery activations must be used.
179+
* is supported; so 'print' mediaQuery activations are used as a fallback
180+
* in browsers without `beforeprint` support.
135181
*
136182
* > But activated breakpoints are deactivated BEFORE 'print' activation.
137183
*
@@ -146,14 +192,17 @@ export class PrintHook {
146192
* - restore as activatedTargets and clear when stop printing
147193
*/
148194
collectActivations(event: MediaChange) {
149-
if (!this.isPrinting) {
195+
if (!this.isPrinting || this.isPrintingBeforeAfterEvent) {
150196
if (!event.matches) {
151197
const bp = this.breakpoints.findByQuery(event.mediaQuery);
152198
if (bp) { // Deactivating a breakpoint
153199
this.deactivations.push(bp);
154200
this.deactivations.sort(sortDescendingPriority);
155201
}
156-
} else {
202+
} else if (!this.isPrintingBeforeAfterEvent) {
203+
// Only clear deactivations if we aren't printing from a `beforeprint` event.
204+
// Otherwise this will clear before `stopPrinting()` is called to restore
205+
// the pre-Print Activations.
157206
this.deactivations = [];
158207
}
159208
}

0 commit comments

Comments
 (0)