-
Notifications
You must be signed in to change notification settings - Fork 6.8k
/
Copy pathblock-scroll-strategy.ts
103 lines (85 loc) · 3.83 KB
/
block-scroll-strategy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ScrollStrategy} from './scroll-strategy';
import {ViewportRuler} from '../../scrolling';
import {coerceCssPixelValue} from '../../coercion';
import {supportsScrollBehavior} from '../../platform';
const scrollBehaviorSupported = supportsScrollBehavior();
/**
* Strategy that will prevent the user from scrolling while the overlay is visible.
*/
export class BlockScrollStrategy implements ScrollStrategy {
private _previousHTMLStyles = {top: '', left: ''};
private _previousScrollPosition: {top: number; left: number};
private _isEnabled = false;
private _document: Document;
constructor(
private _viewportRuler: ViewportRuler,
document: any,
) {
this._document = document;
}
/** Attaches this scroll strategy to an overlay. */
attach() {}
/** Blocks page-level scroll while the attached overlay is open. */
enable() {
if (this._canBeEnabled()) {
const root = this._document.documentElement!;
this._previousScrollPosition = this._viewportRuler.getViewportScrollPosition();
// Cache the previous inline styles in case the user had set them.
this._previousHTMLStyles.left = root.style.left || '';
this._previousHTMLStyles.top = root.style.top || '';
// Note: we're using the `html` node, instead of the `body`, because the `body` may
// have the user agent margin, whereas the `html` is guaranteed not to have one.
root.style.left = coerceCssPixelValue(-this._previousScrollPosition.left);
root.style.top = coerceCssPixelValue(-this._previousScrollPosition.top);
root.classList.add('cdk-global-scrollblock');
this._isEnabled = true;
}
}
/** Unblocks page-level scroll while the attached overlay is open. */
disable() {
if (this._isEnabled) {
const html = this._document.documentElement!;
const body = this._document.body!;
const htmlStyle = html.style;
const bodyStyle = body.style;
const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || '';
const previousBodyScrollBehavior = bodyStyle.scrollBehavior || '';
this._isEnabled = false;
htmlStyle.left = this._previousHTMLStyles.left;
htmlStyle.top = this._previousHTMLStyles.top;
html.classList.remove('cdk-global-scrollblock');
// Disable user-defined smooth scrolling temporarily while we restore the scroll position.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior
// Note that we don't mutate the property if the browser doesn't support `scroll-behavior`,
// because it can throw off feature detections in `supportsScrollBehavior` which
// checks for `'scrollBehavior' in documentElement.style`.
if (scrollBehaviorSupported) {
htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = 'auto';
}
window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top);
if (scrollBehaviorSupported) {
htmlStyle.scrollBehavior = previousHtmlScrollBehavior;
bodyStyle.scrollBehavior = previousBodyScrollBehavior;
}
}
}
private _canBeEnabled(): boolean {
// Since the scroll strategies can't be singletons, we have to use a global CSS class
// (`cdk-global-scrollblock`) to make sure that we don't try to disable global
// scrolling multiple times.
const html = this._document.documentElement!;
if (html.classList.contains('cdk-global-scrollblock') || this._isEnabled) {
return false;
}
const rootElement = this._document.documentElement;
const viewport = this._viewportRuler.getViewportSize();
return rootElement.scrollHeight > viewport.height || rootElement.scrollWidth > viewport.width;
}
}