2018-05-17 07:33:50 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. 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.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {defineInjectable, inject} from '@angular/core';
|
|
|
|
|
|
|
|
import {DOCUMENT} from './dom_tokens';
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Manages the scroll position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
export abstract class ViewportScroller {
|
|
|
|
// De-sugared tree-shakable injection
|
|
|
|
// See #23917
|
|
|
|
/** @nocollapse */
|
|
|
|
static ngInjectableDef = defineInjectable(
|
|
|
|
{providedIn: 'root', factory: () => new BrowserViewportScroller(inject(DOCUMENT), window)});
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Configures the top offset used when scrolling to an anchor.
|
2018-05-17 07:33:50 -04:00
|
|
|
*
|
|
|
|
* When given a tuple with two number, the service will always use the numbers.
|
|
|
|
* When given a function, the service will invoke the function every time it restores scroll
|
|
|
|
* position.
|
|
|
|
*/
|
|
|
|
abstract setOffset(offset: [number, number]|(() => [number, number])): void;
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Returns the current scroll position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
abstract getScrollPosition(): [number, number];
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Sets the scroll position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
abstract scrollToPosition(position: [number, number]): void;
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Scrolls to the provided anchor.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
abstract scrollToAnchor(anchor: string): void;
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
*
|
|
|
|
* Disables automatic scroll restoration provided by the browser.
|
|
|
|
*
|
2018-05-17 07:33:50 -04:00
|
|
|
* See also [window.history.scrollRestoration
|
|
|
|
* info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration)
|
|
|
|
*/
|
|
|
|
abstract setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Manages the scroll position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
export class BrowserViewportScroller implements ViewportScroller {
|
|
|
|
private offset: () => [number, number] = () => [0, 0];
|
|
|
|
|
|
|
|
constructor(private document: any, private window: any) {}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Configures the top offset used when scrolling to an anchor.
|
2018-05-17 07:33:50 -04:00
|
|
|
*
|
|
|
|
* * When given a number, the service will always use the number.
|
|
|
|
* * When given a function, the service will invoke the function every time it restores scroll
|
|
|
|
* position.
|
|
|
|
*/
|
|
|
|
setOffset(offset: [number, number]|(() => [number, number])): void {
|
|
|
|
if (Array.isArray(offset)) {
|
|
|
|
this.offset = () => offset;
|
|
|
|
} else {
|
|
|
|
this.offset = offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Returns the current scroll position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
getScrollPosition(): [number, number] {
|
|
|
|
if (this.supportScrollRestoration()) {
|
|
|
|
return [this.window.scrollX, this.window.scrollY];
|
|
|
|
} else {
|
|
|
|
return [0, 0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Sets the scroll position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
scrollToPosition(position: [number, number]): void {
|
|
|
|
if (this.supportScrollRestoration()) {
|
|
|
|
this.window.scrollTo(position[0], position[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Scrolls to the provided anchor.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
scrollToAnchor(anchor: string): void {
|
|
|
|
if (this.supportScrollRestoration()) {
|
|
|
|
const elSelectedById = this.document.querySelector(`#${anchor}`);
|
|
|
|
if (elSelectedById) {
|
|
|
|
this.scrollToElement(elSelectedById);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const elSelectedByName = this.document.querySelector(`[name='${anchor}']`);
|
|
|
|
if (elSelectedByName) {
|
|
|
|
this.scrollToElement(elSelectedByName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Disables automatic scroll restoration provided by the browser.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void {
|
|
|
|
if (this.supportScrollRestoration()) {
|
|
|
|
const history = this.window.history;
|
|
|
|
if (history && history.scrollRestoration) {
|
|
|
|
history.scrollRestoration = scrollRestoration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private scrollToElement(el: any): void {
|
|
|
|
const rect = el.getBoundingClientRect();
|
|
|
|
const left = rect.left + this.window.pageXOffset;
|
|
|
|
const top = rect.top + this.window.pageYOffset;
|
|
|
|
const offset = this.offset();
|
|
|
|
this.window.scrollTo(left - offset[0], top - offset[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We only support scroll restoration when we can get a hold of window.
|
|
|
|
* This means that we do not support this behavior when running in a web worker.
|
|
|
|
*
|
|
|
|
* Lifting this restriction right now would require more changes in the dom adapter.
|
|
|
|
* Since webworkers aren't widely used, we will lift it once RouterScroller is
|
|
|
|
* battle-tested.
|
|
|
|
*/
|
|
|
|
private supportScrollRestoration(): boolean {
|
|
|
|
try {
|
|
|
|
return !!this.window && !!this.window.scrollTo;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Provides an empty implementation of the viewport scroller. This will
|
2018-05-17 07:33:50 -04:00
|
|
|
* live in @angular/common as it will be used by both platform-server and platform-webworker.
|
|
|
|
*/
|
|
|
|
export class NullViewportScroller implements ViewportScroller {
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Empty implementation
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
setOffset(offset: [number, number]|(() => [number, number])): void {}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Empty implementation
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
getScrollPosition(): [number, number] { return [0, 0]; }
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Empty implementation
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
scrollToPosition(position: [number, number]): void {}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Empty implementation
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
scrollToAnchor(anchor: string): void {}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Empty implementation
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void {}
|
|
|
|
}
|