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
|
|
|
|
*/
|
|
|
|
|
2019-05-17 21:49:21 -04:00
|
|
|
import {ErrorHandler, ɵɵdefineInjectable, ɵɵinject} from '@angular/core';
|
2018-05-17 07:33:50 -04:00
|
|
|
|
|
|
|
import {DOCUMENT} from './dom_tokens';
|
|
|
|
|
2019-03-28 18:00:51 -04:00
|
|
|
|
|
|
|
|
2018-05-17 07:33:50 -04:00
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Defines a scroll position manager. Implemented by `BrowserViewportScroller`.
|
2018-10-19 10:06:08 -04:00
|
|
|
*
|
|
|
|
* @publicApi
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
export abstract class ViewportScroller {
|
|
|
|
// De-sugared tree-shakable injection
|
|
|
|
// See #23917
|
|
|
|
/** @nocollapse */
|
2019-05-17 21:49:21 -04:00
|
|
|
static ngInjectableDef = ɵɵdefineInjectable({
|
2019-03-28 18:00:51 -04:00
|
|
|
providedIn: 'root',
|
2019-05-17 21:49:21 -04:00
|
|
|
factory: () => new BrowserViewportScroller(ɵɵinject(DOCUMENT), window, ɵɵinject(ErrorHandler))
|
2019-03-28 18:00:51 -04:00
|
|
|
});
|
2018-05-17 07:33:50 -04:00
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Configures the top offset used when scrolling to an anchor.
|
2018-11-30 15:16:35 -05:00
|
|
|
* @param offset A position in screen coordinates (a tuple with x and y values)
|
|
|
|
* or a function that returns the top offset position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
abstract setOffset(offset: [number, number]|(() => [number, number])): void;
|
|
|
|
|
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Retrieves the current scroll position.
|
|
|
|
* @returns A position in screen coordinates (a tuple with x and y values).
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
abstract getScrollPosition(): [number, number];
|
|
|
|
|
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Scrolls to a specified position.
|
|
|
|
* @param position A position in screen coordinates (a tuple with x and y values).
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
abstract scrollToPosition(position: [number, number]): void;
|
|
|
|
|
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Scrolls to an anchor element.
|
|
|
|
* @param anchor The ID of the anchor element.
|
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
|
2018-11-30 15:16:35 -05:00
|
|
|
* info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration).
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
abstract setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Manages the scroll position for a browser window.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
export class BrowserViewportScroller implements ViewportScroller {
|
|
|
|
private offset: () => [number, number] = () => [0, 0];
|
|
|
|
|
2019-03-28 18:00:51 -04:00
|
|
|
constructor(private document: any, private window: any, private errorHandler: ErrorHandler) {}
|
2018-05-17 07:33:50 -04:00
|
|
|
|
|
|
|
/**
|
2018-09-20 09:28:00 -04:00
|
|
|
* Configures the top offset used when scrolling to an anchor.
|
2018-11-30 15:16:35 -05:00
|
|
|
* @param offset A position in screen coordinates (a tuple with x and y values)
|
|
|
|
* or a function that returns the top offset position.
|
2018-05-17 07:33:50 -04:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
setOffset(offset: [number, number]|(() => [number, number])): void {
|
|
|
|
if (Array.isArray(offset)) {
|
|
|
|
this.offset = () => offset;
|
|
|
|
} else {
|
|
|
|
this.offset = offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Retrieves the current scroll position.
|
|
|
|
* @returns The position in screen coordinates.
|
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-11-30 15:16:35 -05:00
|
|
|
* @param position The new position in screen coordinates.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
scrollToPosition(position: [number, number]): void {
|
|
|
|
if (this.supportScrollRestoration()) {
|
|
|
|
this.window.scrollTo(position[0], position[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-11-30 15:16:35 -05:00
|
|
|
* Scrolls to an anchor element.
|
|
|
|
* @param anchor The ID of the anchor element.
|
2018-05-17 07:33:50 -04:00
|
|
|
*/
|
|
|
|
scrollToAnchor(anchor: string): void {
|
|
|
|
if (this.supportScrollRestoration()) {
|
2019-03-28 18:00:51 -04:00
|
|
|
// Escape anything passed to `querySelector` as it can throw errors and stop the application
|
|
|
|
// from working if invalid values are passed.
|
|
|
|
if (this.window.CSS && this.window.CSS.escape) {
|
|
|
|
anchor = this.window.CSS.escape(anchor);
|
|
|
|
} else {
|
|
|
|
anchor = anchor.replace(/(\"|\'\ |:|\.|\[|\]|,|=)/g, '\\$1');
|
2018-05-17 07:33:50 -04:00
|
|
|
}
|
2019-03-28 18:00:51 -04:00
|
|
|
try {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
this.errorHandler.handleError(e);
|
2018-05-17 07:33:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
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;
|
2018-08-14 09:34:51 -04:00
|
|
|
} catch {
|
2018-05-17 07:33:50 -04:00
|
|
|
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 {}
|
2019-03-11 20:20:40 -04:00
|
|
|
}
|