@@ -332,6 +334,8 @@ export class NbLayoutComponent implements AfterViewInit, OnInit, OnDestroy {
@Inject(NB_DOCUMENT) protected document,
@Inject(PLATFORM_ID) protected platformId: Object,
protected layoutDirectionService: NbLayoutDirectionService,
+ protected scrollService: NbLayoutScrollService,
+ protected rulerService: NbLayoutRulerService,
) {
this.themeService.onThemeChange()
@@ -371,6 +375,24 @@ export class NbLayoutComponent implements AfterViewInit, OnInit, OnDestroy {
}));
this.spinnerService.load();
+ this.rulerService.onGetDimensions()
+ .pipe(
+ takeWhile(() => this.alive),
+ )
+ .subscribe(({ listener }) => {
+ listener.next(this.getDimensions());
+ listener.complete();
+ });
+
+ this.scrollService.onGetPosition()
+ .pipe(
+ takeWhile(() => this.alive),
+ )
+ .subscribe(({ listener }) => {
+ listener.next(this.getScrollPosition());
+ listener.complete();
+ });
+
if (isPlatformBrowser(this.platformId)) {
// trigger first time so that after the change we have the initial value
this.themeService.changeWindowWidth(this.window.innerWidth);
@@ -403,6 +425,10 @@ export class NbLayoutComponent implements AfterViewInit, OnInit, OnDestroy {
this.renderer.setProperty(this.document, 'dir', direction);
});
+ this.scrollService.onManualScroll()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe(({ x, y }: NbScrollPosition) => this.scroll(x, y));
+
this.afterViewInit$.next(true);
}
@@ -415,11 +441,73 @@ export class NbLayoutComponent implements AfterViewInit, OnInit, OnDestroy {
this.alive = false;
}
+ @HostListener('window:scroll', ['$event'])
+ onScroll($event) {
+ this.scrollService.fireScrollChange($event);
+ }
+
@HostListener('window:resize', ['$event'])
onResize(event) {
this.themeService.changeWindowWidth(event.target.innerWidth);
}
+ /**
+ * Returns scroll and client height/width
+ *
+ * Depending on the current scroll mode (`withScroll=true`) returns sizes from the body element
+ * or from the `.scrollable-container`
+ * @returns {NbLayoutDimensions}
+ */
+ getDimensions(): NbLayoutDimensions {
+ let clientWidth, clientHeight, scrollWidth, scrollHeight = 0;
+ if (this.withScrollValue) {
+ const container = this.scrollableContainerRef.nativeElement;
+ clientWidth = container.clientWidth;
+ clientHeight = container.clientHeight;
+ scrollWidth = container.scrollWidth;
+ scrollHeight = container.scrollHeight;
+ } else {
+ const { documentElement, body } = this.document;
+ clientWidth = documentElement.clientWidth || body.clientWidth;
+ clientHeight = documentElement.clientHeight || body.clientHeight;
+ scrollWidth = documentElement.scrollWidth || body.scrollWidth;
+ scrollHeight = documentElement.scrollHeight || body.scrollHeight;
+ }
+
+ return {
+ clientWidth,
+ clientHeight,
+ scrollWidth,
+ scrollHeight,
+ };
+ }
+
+ /**
+ * Returns scroll position of current scroll container.
+ *
+ * If `withScroll` = true, returns scroll position of the `.scrollable-container` element,
+ * otherwise - of the scrollable element of the window (which may be different depending of a browser)
+ *
+ * @returns {NbScrollPosition}
+ */
+ getScrollPosition(): NbScrollPosition {
+ if (this.withScrollValue) {
+ const container = this.scrollableContainerRef.nativeElement;
+ return { x: container.scrollLeft, y: container.scrollTop };
+ }
+
+ const documentRect = this.document.documentElement.getBoundingClientRect();
+
+ const x = -documentRect.left || this.document.body.scrollLeft || this.window.scrollX ||
+ this.document.documentElement.scrollLeft || 0;
+
+ const y = -documentRect.top || this.document.body.scrollTop || this.window.scrollY ||
+ this.document.documentElement.scrollTop || 0;
+
+
+ return { x, y };
+ }
+
private initScrollTop() {
this.router.events
.pipe(
@@ -427,7 +515,24 @@ export class NbLayoutComponent implements AfterViewInit, OnInit, OnDestroy {
filter(event => event instanceof NavigationEnd),
)
.subscribe(() => {
- this.scrollableContainerRef.nativeElement.scrollTo && this.scrollableContainerRef.nativeElement.scrollTo(0, 0);
+ this.scroll(0, 0);
});
}
+
+ private scroll(x: number, y: number) {
+ if (!isPlatformBrowser(this.platformId)) {
+ return;
+ }
+ if (this.withScrollValue) {
+ const scrollable = this.scrollableContainerRef.nativeElement;
+ if (scrollable.scrollTo) {
+ scrollable.scrollTo(x, y);
+ } else {
+ scrollable.scrollLeft = x;
+ scrollable.scrollTop = y;
+ }
+ } else {
+ this.window.scrollTo(x, y);
+ }
+ }
}
diff --git a/src/framework/theme/index.ts b/src/framework/theme/index.ts
index a630261dd1..f06fc08eae 100644
--- a/src/framework/theme/index.ts
+++ b/src/framework/theme/index.ts
@@ -11,6 +11,8 @@ export * from './services/spinner.service';
export * from './services/breakpoints.service';
export * from './services/color.helper';
export * from './services/direction.service';
+export * from './services/scroll.service';
+export * from './services/ruler.service';
export * from './components/card/card.module';
export * from './components/layout/layout.module';
export * from './components/menu/menu.module';
diff --git a/src/framework/theme/services/ruler.service.spec.ts b/src/framework/theme/services/ruler.service.spec.ts
new file mode 100644
index 0000000000..99f1cc3a01
--- /dev/null
+++ b/src/framework/theme/services/ruler.service.spec.ts
@@ -0,0 +1,133 @@
+import { Component, ElementRef, ViewChild } from '@angular/core';
+import { APP_BASE_HREF } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { TestBed, ComponentFixture, fakeAsync, tick, async, inject } from '@angular/core/testing';
+import { NbLayoutRulerService, NbLayoutDimensions } from './ruler.service';
+import { NbLayoutModule } from '../components/layout/layout.module';
+import { NbThemeService } from './theme.service';
+import { NbThemeModule } from '../theme.module';
+import { NB_DOCUMENT } from '../theme.options';
+
+let currentDocument;
+let fixture: ComponentFixture
;
+let componentInstance: RulerTestComponent;
+let rulerService: NbLayoutRulerService;
+
+@Component({
+ template: `
+
+
+
+
+
+ `,
+})
+class RulerTestComponent {
+
+ @ViewChild('resize', { read: ElementRef }) private resizeElement: ElementRef;
+ @ViewChild('layout', { read: ElementRef }) private layout: ElementRef;
+ localScroll = false;
+
+ setSize(width: string, height: string) {
+ this.resizeElement.nativeElement.style.width = width;
+ this.resizeElement.nativeElement.style.height = height;
+ }
+
+ useLocalScroll() {
+ this.localScroll = true;
+ }
+
+ useGlobalScroll() {
+ this.localScroll = false;
+ }
+
+ getScrollableElement() {
+ return this.layout.nativeElement.querySelector('.scrollable-container');
+ }
+}
+
+// This is rather a smoke test
+describe('NbLayoutRulerService', () => {
+
+ beforeEach(() => {
+ fixture = TestBed.configureTestingModule({
+ imports: [ RouterModule.forRoot([]), NbThemeModule.forRoot({ name: 'default' }), NbLayoutModule ],
+ providers: [ NbLayoutRulerService, NbThemeService, { provide: APP_BASE_HREF, useValue: '/' } ],
+ declarations: [ RulerTestComponent ],
+ })
+ .createComponent(RulerTestComponent);
+
+ componentInstance = fixture.componentInstance;
+
+ fixture.detectChanges();
+ });
+
+ beforeEach(async(inject(
+ [NbLayoutRulerService, NB_DOCUMENT],
+ (_rulerService, _document) => {
+ rulerService = _rulerService;
+ currentDocument = _document;
+ },
+ )));
+
+ afterEach(fakeAsync(() => {
+ fixture.destroy();
+ tick();
+ fixture.nativeElement.remove();
+ }));
+
+ it('should get dimensions from document', (done) => {
+ fixture.detectChanges();
+ rulerService.getDimensions()
+ .subscribe((size: NbLayoutDimensions) => {
+ expect(size.clientHeight).toEqual(currentDocument.documentElement.clientHeight);
+ expect(size.clientWidth).toEqual(currentDocument.documentElement.clientWidth);
+ expect(size.scrollHeight).toEqual(currentDocument.documentElement.scrollHeight);
+ expect(size.scrollWidth).toEqual(currentDocument.documentElement.scrollWidth);
+ done();
+ })
+ });
+
+ it('should get dimensions from document when scrolls', (done) => {
+ componentInstance.setSize('10000px', '10000px');
+ fixture.detectChanges();
+ rulerService.getDimensions()
+ .subscribe((size: NbLayoutDimensions) => {
+ expect(size.clientHeight).toEqual(currentDocument.documentElement.clientHeight);
+ expect(size.clientWidth).toEqual(currentDocument.documentElement.clientWidth);
+ expect(size.scrollHeight).toEqual(currentDocument.documentElement.scrollHeight);
+ expect(size.scrollWidth).toEqual(currentDocument.documentElement.scrollWidth);
+ done();
+ })
+ });
+
+ it('should get dimensions from scrollable', (done) => {
+ componentInstance.useLocalScroll();
+ fixture.detectChanges();
+ const scrollable = componentInstance.getScrollableElement();
+ rulerService.getDimensions()
+ .subscribe((size: NbLayoutDimensions) => {
+ expect(size.clientHeight).toEqual(scrollable.clientHeight);
+ expect(size.clientWidth).toEqual(scrollable.clientWidth);
+ expect(size.scrollHeight).toEqual(scrollable.scrollHeight);
+ expect(size.scrollWidth).toEqual(scrollable.scrollWidth);
+ done();
+ })
+ });
+
+ it('should get dimensions from scrollable when scrolls', (done) => {
+ componentInstance.useLocalScroll();
+ componentInstance.setSize('10000px', '10000px');
+ fixture.detectChanges();
+ const scrollable = componentInstance.getScrollableElement();
+ rulerService.getDimensions()
+ .subscribe((size: NbLayoutDimensions) => {
+ expect(size.clientHeight).toEqual(scrollable.clientHeight);
+ expect(size.clientWidth).toEqual(scrollable.clientWidth);
+ expect(size.scrollHeight).toEqual(scrollable.scrollHeight);
+ expect(size.scrollWidth).toEqual(scrollable.scrollWidth);
+ done();
+ })
+ });
+
+});
diff --git a/src/framework/theme/services/ruler.service.ts b/src/framework/theme/services/ruler.service.ts
new file mode 100644
index 0000000000..3ed4adbaba
--- /dev/null
+++ b/src/framework/theme/services/ruler.service.ts
@@ -0,0 +1,63 @@
+import { Injectable } from '@angular/core';
+import { Observable, ReplaySubject, Subject } from 'rxjs';
+
+/**
+ * Layout dimensions type
+ */
+export interface NbLayoutDimensions {
+
+ /**
+ * clientWidth
+ * @type {number}
+ */
+ clientWidth: number;
+
+ /**
+ * clientHeight
+ * @type {number}
+ */
+ clientHeight: number;
+
+ /**
+ * scrollWidth
+ * @type {number}
+ */
+ scrollWidth: number;
+
+ /**
+ * scrollHeight
+ * @type {number}
+ */
+ scrollHeight: number;
+}
+
+/**
+ * Simple helper service to return Layout dimensions
+ * Depending of current Layout scroll mode (default or `withScroll` when scroll is moved to an element
+ * inside of the layout) corresponding dimensions will be returns - of `documentElement` in first case and
+ * `.scrollable-container` in the second.
+ */
+@Injectable()
+export class NbLayoutRulerService {
+
+ private contentDimensionsReq$ = new Subject();
+
+ /**
+ * Content dimensions
+ * @returns {Observable}
+ */
+ getDimensions(): Observable {
+ const listener = new ReplaySubject();
+ this.contentDimensionsReq$.next({ listener });
+
+ return listener.asObservable();
+ }
+
+ /**
+ * @private
+ * @returns {Subject}
+ */
+ onGetDimensions(): Subject {
+ return this.contentDimensionsReq$;
+ }
+}
diff --git a/src/framework/theme/services/scroll.service.spec.ts b/src/framework/theme/services/scroll.service.spec.ts
new file mode 100644
index 0000000000..3f420b0150
--- /dev/null
+++ b/src/framework/theme/services/scroll.service.spec.ts
@@ -0,0 +1,169 @@
+import { Component, ElementRef, ViewChild } from '@angular/core';
+import { APP_BASE_HREF } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { TestBed, ComponentFixture, fakeAsync, tick, async, inject } from '@angular/core/testing';
+import { NbLayoutScrollService, NbScrollPosition } from './scroll.service';
+import { NbLayoutModule } from '../components/layout/layout.module';
+import { NbThemeService } from './theme.service';
+import { NbThemeModule } from '../theme.module';
+import { NB_WINDOW } from '../theme.options';
+
+let currentWindow;
+let fixture: ComponentFixture;
+let componentInstance: ScrollTestComponent;
+let scrollService: NbLayoutScrollService;
+
+@Component({
+ template: `
+
+
+
+
+
+ `,
+ styles: [`
+ ::ng-deep nb-layout.with-scroll .scrollable-container {
+ overflow: auto;
+ height: 100vh;
+ }
+ `],
+})
+class ScrollTestComponent {
+
+ @ViewChild('resize', { read: ElementRef }) private resizeElement: ElementRef;
+ @ViewChild('layout', { read: ElementRef }) private layout: ElementRef;
+ localScroll = false;
+
+ setSize(width: string, height: string) {
+ this.resizeElement.nativeElement.style.width = width;
+ this.resizeElement.nativeElement.style.height = height;
+ }
+
+ useLocalScroll() {
+ this.localScroll = true;
+ }
+
+ useGlobalScroll() {
+ this.localScroll = false;
+ }
+
+ getScrollableElement() {
+ return this.layout.nativeElement.querySelector('.scrollable-container');
+ }
+}
+
+describe('NbScrollService', () => {
+
+ beforeEach(() => {
+ fixture = TestBed.configureTestingModule({
+ imports: [ RouterModule.forRoot([]), NbThemeModule.forRoot({ name: 'default' }), NbLayoutModule ],
+ providers: [ NbLayoutScrollService, NbThemeService, { provide: APP_BASE_HREF, useValue: '/' } ],
+ declarations: [ ScrollTestComponent ],
+ })
+ .createComponent(ScrollTestComponent);
+
+ componentInstance = fixture.componentInstance;
+
+ fixture.detectChanges();
+ });
+
+ beforeEach(async(inject(
+ [NbLayoutScrollService, NB_WINDOW],
+ (_scrollService, _window) => {
+ scrollService = _scrollService;
+ currentWindow = _window;
+ },
+ )));
+
+ afterEach(fakeAsync(() => {
+ fixture.destroy();
+ tick();
+ fixture.nativeElement.remove();
+ }));
+
+ it('should get initial scroll position', (done) => {
+ fixture.detectChanges();
+ scrollService.getPosition()
+ .subscribe((pos: NbScrollPosition) => {
+ expect(pos.x).toEqual(0);
+ expect(pos.y).toEqual(0);
+ done();
+ })
+ });
+
+ it('should get initial scroll position as nothing to scroll', (done) => {
+ currentWindow.scrollTo(10, 10);
+ fixture.detectChanges();
+ scrollService.getPosition()
+ .subscribe((pos: NbScrollPosition) => {
+ expect(pos.x).toEqual(0);
+ expect(pos.y).toEqual(0);
+ done();
+ })
+ });
+
+ it('should get updated scroll position', (done) => {
+ componentInstance.setSize('10000px', '10000px');
+ fixture.detectChanges();
+ currentWindow.scrollTo(10, 10);
+ scrollService.getPosition()
+ .subscribe((pos: NbScrollPosition) => {
+ expect(pos.x).toEqual(10);
+ expect(pos.y).toEqual(10);
+ done();
+ })
+ });
+
+ it('should get initial scroll position on scrollable', (done) => {
+ componentInstance.useLocalScroll();
+ fixture.detectChanges();
+ const scrollable = componentInstance.getScrollableElement();
+ scrollService.getPosition()
+ .subscribe((pos: NbScrollPosition) => {
+ expect(pos.x).toEqual(scrollable.scrollLeft);
+ expect(pos.y).toEqual(scrollable.scrollTop);
+ done();
+ });
+ });
+
+ it('should get updated scroll position on scrollable', (done) => {
+ componentInstance.useLocalScroll();
+ componentInstance.setSize('10000px', '10000px');
+ fixture.detectChanges();
+ const scrollable = componentInstance.getScrollableElement();
+ scrollable.scrollTo(10, 10);
+ fixture.detectChanges();
+ scrollService.getPosition()
+ .subscribe((pos: NbScrollPosition) => {
+ expect(pos.x).toEqual(10);
+ expect(pos.y).toEqual(10);
+ done();
+ });
+ });
+
+ it('should scroll using service', (done) => {
+ componentInstance.useLocalScroll();
+ componentInstance.setSize('10000px', '10000px');
+ fixture.detectChanges();
+ scrollService.scrollTo(10, 10);
+ fixture.detectChanges();
+ scrollService.getPosition()
+ .subscribe((pos: NbScrollPosition) => {
+ expect(pos.x).toEqual(10);
+ expect(pos.y).toEqual(10);
+ done();
+ });
+ });
+
+
+ it('should listen to scroll', (done) => {
+ scrollService.onScroll()
+ .subscribe((event: any) => {
+ expect(event).not.toBeNull();
+ done();
+ });
+
+ currentWindow.dispatchEvent(new Event('scroll'));
+ });
+
+});
diff --git a/src/framework/theme/services/scroll.service.ts b/src/framework/theme/services/scroll.service.ts
new file mode 100644
index 0000000000..0eb6c339a8
--- /dev/null
+++ b/src/framework/theme/services/scroll.service.ts
@@ -0,0 +1,94 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { share } from 'rxjs/operators';
+import { ReplaySubject } from 'rxjs';
+
+/**
+ * Scroll position type
+ */
+export interface NbScrollPosition {
+
+ /**
+ * x - left
+ * @type {number}
+ */
+ x: number;
+
+ /**
+ * y - top
+ * @type {number}
+ */
+ y: number;
+}
+
+/**
+ * Layout scroll service. Provides information about current scroll position,
+ * as well as methods to update position of the scroll.
+ *
+ * The reason we added this service is that in Nebular there are two scroll modes:
+ * - the default mode when scroll is on body
+ * - and the `withScroll` mode, when scroll is removed from the body and moved to an element inside of the
+ * `nb-layout` component
+ */
+@Injectable()
+export class NbLayoutScrollService {
+
+ private scrollPositionReq$ = new Subject();
+ private manualScroll$ = new Subject();
+ private scroll$ = new Subject();
+
+ /**
+ * Returns scroll position
+ *
+ * @returns {Observable}
+ */
+ getPosition(): Observable {
+ const listener = new ReplaySubject();
+ this.scrollPositionReq$.next({ listener });
+
+ return listener.asObservable();
+ }
+
+ /**
+ * Sets scroll position
+ *
+ * @param {number} x
+ * @param {number} y
+ */
+ scrollTo(x: number, y: number) {
+ this.manualScroll$.next({ x, y });
+ }
+
+ /**
+ * Returns a stream of scroll events
+ *
+ * @returns {Observable}
+ */
+ onScroll() {
+ return this.scroll$.pipe(share());
+ }
+
+ /**
+ * @private
+ * @returns Observable.
+ */
+ onManualScroll(): Observable {
+ return this.manualScroll$.pipe(share());
+ }
+
+ /**
+ * @private
+ * @returns {Subject}
+ */
+ onGetPosition(): Subject {
+ return this.scrollPositionReq$;
+ }
+
+ /**
+ * @private
+ * @param {any} event
+ */
+ fireScrollChange(event: any) {
+ this.scroll$.next(event);
+ }
+}
diff --git a/src/framework/theme/theme.module.ts b/src/framework/theme/theme.module.ts
index 611b4958d9..340842a579 100644
--- a/src/framework/theme/theme.module.ts
+++ b/src/framework/theme/theme.module.ts
@@ -26,6 +26,8 @@ import {
NbMediaBreakpointsService,
} from './services/breakpoints.service';
import { NbLayoutDirectionService, NbLayoutDirection, NB_LAYOUT_DIRECTION } from './services/direction.service';
+import { NbLayoutScrollService } from './services/scroll.service';
+import { NbLayoutRulerService } from './services/ruler.service';
export function nbWindowFactory() {
return window;
@@ -47,6 +49,7 @@ export class NbThemeModule {
* @param nbThemeOptions {NbThemeOptions} Main theme options
* @param nbJSThemes {NbJSThemeOptions[]} List of JS Themes, will be merged with default themes
* @param nbMediaBreakpoints {NbMediaBreakpoint} Available media breakpoints
+ * @param layoutDirection {NbLayoutDirection} Layout direction
*
* @returns {ModuleWithProviders}
*/
@@ -70,6 +73,8 @@ export class NbThemeModule {
NbSpinnerService,
{ provide: NB_LAYOUT_DIRECTION, useValue: layoutDirection || NbLayoutDirection.LTR },
NbLayoutDirectionService,
+ NbLayoutScrollService,
+ NbLayoutRulerService,
],
};
}
diff --git a/src/playground/playground-routing.module.ts b/src/playground/playground-routing.module.ts
index f4396ae8ae..29776dc856 100644
--- a/src/playground/playground-routing.module.ts
+++ b/src/playground/playground-routing.module.ts
@@ -132,6 +132,7 @@ import { NbButtonHeroComponent } from './button/button-hero.component';
import { NbButtonOutlineComponent } from './button/button-outline.component';
import { NbButtonSizesComponent } from './button/button-sizes.component';
import { NbButtonTypesComponent } from './button/button-types.component';
+import { NbScrollWindowComponent } from './scroll/scroll-window.component';
export const routes: Routes = [
{
@@ -784,6 +785,15 @@ export const routes: Routes = [
},
],
},
+ {
+ path: 'scroll',
+ children: [
+ {
+ path: 'scroll-window.component',
+ component: NbScrollWindowComponent,
+ },
+ ],
+ },
],
},
{
diff --git a/src/playground/playground.module.ts b/src/playground/playground.module.ts
index 4b877591cc..624f9155de 100644
--- a/src/playground/playground.module.ts
+++ b/src/playground/playground.module.ts
@@ -161,6 +161,7 @@ import { NbButtonHeroComponent } from './button/button-hero.component';
import { NbButtonOutlineComponent } from './button/button-outline.component';
import { NbButtonSizesComponent } from './button/button-sizes.component';
import { NbButtonTypesComponent } from './button/button-types.component';
+import { NbScrollWindowComponent } from './scroll/scroll-window.component';
export const NB_MODULES = [
NbCardModule,
@@ -312,6 +313,7 @@ export const NB_EXAMPLE_COMPONENTS = [
NbButtonOutlineComponent,
NbButtonSizesComponent,
NbButtonTypesComponent,
+ NbScrollWindowComponent,
];
diff --git a/src/playground/scroll/scroll-window.component.html b/src/playground/scroll/scroll-window.component.html
new file mode 100644
index 0000000000..b4db1ce20c
--- /dev/null
+++ b/src/playground/scroll/scroll-window.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Current: {{ mode }}
+
+
+
+ {{ text }}
+
+
+
diff --git a/src/playground/scroll/scroll-window.component.ts b/src/playground/scroll/scroll-window.component.ts
new file mode 100644
index 0000000000..a57ad4bb25
--- /dev/null
+++ b/src/playground/scroll/scroll-window.component.ts
@@ -0,0 +1,36 @@
+import { Component } from '@angular/core';
+import { NbLayoutScrollService, NbLayoutRulerService } from '@nebular/theme';
+
+enum LayoutMode {
+ WINDOW = 'window',
+ LAYOUT = 'layout',
+}
+
+@Component({
+ selector: 'nb-scroll-window',
+ templateUrl: './scroll-window.component.html',
+})
+export class NbScrollWindowComponent {
+
+ mode = LayoutMode.WINDOW;
+ text = 'Hello World! '.repeat(1024 * 10);
+
+ constructor(private scroll: NbLayoutScrollService, private ruler: NbLayoutRulerService) {
+ this.scroll.onScroll()
+ .subscribe((event) => console.info('Scroll', event));
+ }
+
+ changeMode() {
+ this.mode = this.mode === LayoutMode.WINDOW ? LayoutMode.LAYOUT : LayoutMode.WINDOW;
+ }
+
+ scrollTo(x: number, y: number) {
+ this.scroll.scrollTo(x, y);
+
+ this.ruler.getDimensions()
+ .subscribe(position => console.info('Content Dimensions', position));
+
+ this.scroll.getPosition()
+ .subscribe(size => console.info('Scroll Position', size));
+ }
+}