1
+ /**
2
+ * @license
3
+ * Copyright Google Inc. All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+
9
+ import { defineInjectable , inject } from '@angular/core' ;
10
+
11
+ import { DOCUMENT } from './dom_tokens' ;
12
+
13
+ /**
14
+ * @whatItDoes Manages the scroll position.
15
+ */
16
+ export abstract class ViewportScroller {
17
+ // De-sugared tree-shakable injection
18
+ // See #23917
19
+ /** @nocollapse */
20
+ static ngInjectableDef = defineInjectable (
21
+ { providedIn : 'root' , factory : ( ) => new BrowserViewportScroller ( inject ( DOCUMENT ) , window ) } ) ;
22
+
23
+ /**
24
+ * @whatItDoes Configures the top offset used when scrolling to an anchor.
25
+ *
26
+ * When given a tuple with two number, the service will always use the numbers.
27
+ * When given a function, the service will invoke the function every time it restores scroll
28
+ * position.
29
+ */
30
+ abstract setOffset ( offset : [ number , number ] | ( ( ) => [ number , number ] ) ) : void ;
31
+
32
+ /**
33
+ * @whatItDoes Returns the current scroll position.
34
+ */
35
+ abstract getScrollPosition ( ) : [ number , number ] ;
36
+
37
+ /**
38
+ * @whatItDoes Sets the scroll position.
39
+ */
40
+ abstract scrollToPosition ( position : [ number , number ] ) : void ;
41
+
42
+ /**
43
+ * @whatItDoes Scrolls to the provided anchor.
44
+ */
45
+ abstract scrollToAnchor ( anchor : string ) : void ;
46
+
47
+ /**
48
+ * @whatItDoes Disables automatic scroll restoration provided by the browser.
49
+ * See also [window.history.scrollRestoration
50
+ * info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration)
51
+ */
52
+ abstract setHistoryScrollRestoration ( scrollRestoration : 'auto' | 'manual' ) : void ;
53
+ }
54
+
55
+ /**
56
+ * @whatItDoes Manages the scroll position.
57
+ */
58
+ export class BrowserViewportScroller implements ViewportScroller {
59
+ private offset : ( ) => [ number , number ] = ( ) => [ 0 , 0 ] ;
60
+
61
+ constructor ( private document : any , private window : any ) { }
62
+
63
+ /**
64
+ * @whatItDoes Configures the top offset used when scrolling to an anchor.
65
+ *
66
+ * * When given a number, the service will always use the number.
67
+ * * When given a function, the service will invoke the function every time it restores scroll
68
+ * position.
69
+ */
70
+ setOffset ( offset : [ number , number ] | ( ( ) => [ number , number ] ) ) : void {
71
+ if ( Array . isArray ( offset ) ) {
72
+ this . offset = ( ) => offset ;
73
+ } else {
74
+ this . offset = offset ;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * @whatItDoes Returns the current scroll position.
80
+ */
81
+ getScrollPosition ( ) : [ number , number ] {
82
+ if ( this . supportScrollRestoration ( ) ) {
83
+ return [ this . window . scrollX , this . window . scrollY ] ;
84
+ } else {
85
+ return [ 0 , 0 ] ;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * @whatItDoes Sets the scroll position.
91
+ */
92
+ scrollToPosition ( position : [ number , number ] ) : void {
93
+ if ( this . supportScrollRestoration ( ) ) {
94
+ this . window . scrollTo ( position [ 0 ] , position [ 1 ] ) ;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @whatItDoes Scrolls to the provided anchor.
100
+ */
101
+ scrollToAnchor ( anchor : string ) : void {
102
+ if ( this . supportScrollRestoration ( ) ) {
103
+ const elSelectedById = this . document . querySelector ( `#${ anchor } ` ) ;
104
+ if ( elSelectedById ) {
105
+ this . scrollToElement ( elSelectedById ) ;
106
+ return ;
107
+ }
108
+ const elSelectedByName = this . document . querySelector ( `[name='${ anchor } ']` ) ;
109
+ if ( elSelectedByName ) {
110
+ this . scrollToElement ( elSelectedByName ) ;
111
+ return ;
112
+ }
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @whatItDoes Disables automatic scroll restoration provided by the browser.
118
+ */
119
+ setHistoryScrollRestoration ( scrollRestoration : 'auto' | 'manual' ) : void {
120
+ if ( this . supportScrollRestoration ( ) ) {
121
+ const history = this . window . history ;
122
+ if ( history && history . scrollRestoration ) {
123
+ history . scrollRestoration = scrollRestoration ;
124
+ }
125
+ }
126
+ }
127
+
128
+ private scrollToElement ( el : any ) : void {
129
+ const rect = el . getBoundingClientRect ( ) ;
130
+ const left = rect . left + this . window . pageXOffset ;
131
+ const top = rect . top + this . window . pageYOffset ;
132
+ const offset = this . offset ( ) ;
133
+ this . window . scrollTo ( left - offset [ 0 ] , top - offset [ 1 ] ) ;
134
+ }
135
+
136
+ /**
137
+ * We only support scroll restoration when we can get a hold of window.
138
+ * This means that we do not support this behavior when running in a web worker.
139
+ *
140
+ * Lifting this restriction right now would require more changes in the dom adapter.
141
+ * Since webworkers aren't widely used, we will lift it once RouterScroller is
142
+ * battle-tested.
143
+ */
144
+ private supportScrollRestoration ( ) : boolean {
145
+ try {
146
+ return ! ! this . window && ! ! this . window . scrollTo ;
147
+ } catch ( e ) {
148
+ return false ;
149
+ }
150
+ }
151
+ }
152
+
153
+
154
+ /**
155
+ * @whatItDoes Provides an empty implementation of the viewport scroller. This will
156
+ * live in @angular/common as it will be used by both platform-server and platform-webworker.
157
+ */
158
+ export class NullViewportScroller implements ViewportScroller {
159
+ /**
160
+ * @whatItDoes empty implementation
161
+ */
162
+ setOffset ( offset : [ number , number ] | ( ( ) => [ number , number ] ) ) : void { }
163
+
164
+ /**
165
+ * @whatItDoes empty implementation
166
+ */
167
+ getScrollPosition ( ) : [ number , number ] { return [ 0 , 0 ] ; }
168
+
169
+ /**
170
+ * @whatItDoes empty implementation
171
+ */
172
+ scrollToPosition ( position : [ number , number ] ) : void { }
173
+
174
+ /**
175
+ * @whatItDoes empty implementation
176
+ */
177
+ scrollToAnchor ( anchor : string ) : void { }
178
+
179
+ /**
180
+ * @whatItDoes empty implementation
181
+ */
182
+ setHistoryScrollRestoration ( scrollRestoration : 'auto' | 'manual' ) : void { }
183
+ }
0 commit comments