From da82a1fa61ff678ce29216d56625c7a04986c297 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Fri, 16 Jun 2017 11:12:09 -0700 Subject: [PATCH 01/16] add lib files for sticky-header add chose parent add support to 'optional 'cdkStickyRegion' input ' add app-demo for sticky-header fix bugs and deleted unused tag id in HTML files modify fix some code according to PR review comments change some format to pass TSlint check add '_' before private elements delete @Injectable for StickyHeaderDirective. Because we do not need @Injectable refine code encapsulate 'set style for element' change @Input() Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')' add const STICK_START_CLASS and STICK_END_CLASS Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'. change 'MdStickyHeaderModule' to 'CdkStickyHeaderModule'; encapsulate reset css style operation for sticky header. delete unnecessary gloable variables delete global variable '_width' Add doc for 'sticker()' function. explained how it works. add more doc for 'sticker()', explaining 'isStuck' flag 2 space for indent fix delete sticky-header demo part from this branch revert firebase file change code according to comments in PR revert firbaserc revert demo-app.ts revert routes.ts revert demo-app-module.ts change fix the problem of : 'this.stickyParent' might be 'null' change doc Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }' Added prefix 'mat-' for CSS class Delete 'public' before variables Object.assign isn't supported in IE11; use extendObject from src/lib/core/util. IE11 will have trouble with `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);' Added docs for all variables extract 'generate CSS style' created a generateStyleCSS() function, let it be responsible for generating all those CSS styles. reformat add debounce to solve 'getBoundingClientRect() cause slow down' problem. add position:sticky and check whether browser support it. If not , use the naive implementation Removed unused 'scrollableRegion' and 'parentRegion' removed commented lines default public Add comments about why setting style top and position for iPhone and not IE. And extract detectBrowser() as a new function format consider all circumstances of browser. use "===" instead of '==' make 'navigator.userAgent.toLocaleLowerCase()' a local variable optimize Added comments on const 'STICK_START_CLASS' and 'STICK_END_CLASS'. change their content to cdk-sticky-header-start and cdk-sticky-header-end Added comments for STICK_START_CLASS and STICK_END_CLASS. Changed the format of one-line JsDoc unsubscribe sbscriptions onDestory Use what modernizr does on compatibility instead of get the browser version directly. add 'padding' and 'stickyRegionHeight' variables to avoid calling 'getComputedStyle()' too many times (which is expensive). move docs above @Directive removed the underscore in'_element: ElementRef', expand 'reg' to 'region' use 'if (this.isIE)' instead of 'if(this.isIE === true)' added more newlines between params in 'generateCssStyle()' function to make it easier to understand. Added reference link to Modernizer in docs of getSupportList() Deleted "_supportList" variable renamed 'isIE' to 'isStickyPositionSupported', and removed extra space before Observable Set debounce time as a const variable Added docs for 'const DEBOUNCE_TIME: number = 5;' Changed ' if(this.stickyParent == null)' to ' if(!this.stickyParent)' Removed the @param and @returns and make sure the types are correct in the function signature in 'generateCssStyle(...)' function Added docs for `isStickyPositionSupported` variable changed '+=' to '=' of 'stickyText' in getSupportList() function nit added " " between 'if' and '(' nit Added comments deleted unused import change comments optimize comments deleted unnecessary global variables(padding and stickyRegionHeight) Added check whether we are on browser Array to string[] test? try to reopen the old PR fix after rebase revert list.ts test test 222 revert demo revert list.ts second time Move code to 'src/cdk' revert 'move code to 'src/cdk'' , it should be done in a new PR revert avoid calling 'getComputedStyle()' too many times. rename as sticky-header.ts Add blank lines between these top-level symbol make '_isStickyPositionSupported' private Changed the originalCSS to private and use '{} as CSSStyleDeclaration' instead of ''any. Rename '_containerStart' to '_stickyRegionTop' rename optimize discription for '_stickyRegionBottomThreshold' private _originalStyles = { position: '', top: '', right: '', left: '', bottom: '', width: '', zIndex: ''}; Deleted 'generateCssStyle()' and 'getCssNumber()' function Deleted 'getCssValue()' function fix CSSStyleDeclaration change sticky width to 'this.upperScrollableContainer.clientWidth' fix nit Added the 'isPositionStickySupported() ' function to src/cdk/platform/features.ts. Consume that function in this component and just always use both the webkit and unprefixed styles. nit update doc 'Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the positioning adversely.' changed the doc to '/** z-index to be applied to the sticky header (default is 10). */' fix tslint error for comment 'Can you evaluate each method to make sure their accessor privacy is right? E.g. see which functions need to be public, private, static, etc' Deleted variable 'elemHeight' Chaned to 'if (!this.stickyParent)' Simplified Docs for 'sticker()'. set 'defineRestriction()' function to private use 'RxChain' rename to '_isPositionStickySupported' Use // for comments, /* */ for docs @angular/cdk rename : values -> headerStyles Move closing brace to the next line optimized: [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription] .forEach(s => s && s.unsubscribe()); You should be able to do just '0' instead of '0px' Format like TODO(sllethe): ... private _attachEventListeners? Add a description like "Add listeners for events that affect sticky positioning." optimize doc Rename 'defineRestrictions' to '_measureStickyRegionBounds' rename: private _resetElementStyles let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right; chaned 'any' to 'number' nit change doc '/** * Unsticks the header so that it goes back to scrolling normally. * * This should be called when the element reaches the bottom of its cdkStickyRegion so that it * smoothly scrolls out of view as the next sticky-header moves in. */' _unstuckElement -> _unstickElement rename 'sticker()' to '_applyStickyPositionStyles()' rename 'defineRestrictionsAndStick()' to '_updateStickyPositioning()' Add demo to sticky-header add lib files for sticky-header add chose parent add support to 'optional 'cdkStickyRegion' input ' add app-demo for sticky-header fix bugs and deleted unused tag id in HTML files modify fix some code according to PR review comments change some format to pass TSlint check add '_' before private elements delete @Injectable for StickyHeaderDirective. Because we do not need @Injectable --- .firebaserc | 5 + src/demo-app/demo-app-module.ts | 8 +- src/demo-app/demo-app/demo-app.ts | 3 +- src/demo-app/demo-app/routes.ts | 3 + .../sticky-header/sticky-header-demo.html | 100 ++++++ .../sticky-header/sticky-header-demo.scss | 88 +++++ .../sticky-header/sticky-header-demo.ts | 13 + src/lib/sticky-header/sticky-header-dir.ts | 314 ++++++++++++++++++ src/lib/sticky-header/sticky-header.ts | 5 +- src/lib/sticky-header/sticky-header.ts~HEAD | 289 ++++++++++++++++ 10 files changed, 822 insertions(+), 6 deletions(-) create mode 100644 .firebaserc create mode 100644 src/demo-app/sticky-header/sticky-header-demo.html create mode 100644 src/demo-app/sticky-header/sticky-header-demo.scss create mode 100644 src/demo-app/sticky-header/sticky-header-demo.ts create mode 100644 src/lib/sticky-header/sticky-header-dir.ts create mode 100644 src/lib/sticky-header/sticky-header.ts~HEAD diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 000000000000..e0b738683d94 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "material2-dev" + } +} diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index 98b2c3dc8b1e..0563f49cb234 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -76,10 +76,12 @@ import { MdToolbarModule, MdTooltipModule, OverlayContainer, - StyleModule + StyleModule, + StickyHeaderModule } from '@angular/material'; import {CdkTableModule} from '@angular/cdk'; import {TableHeaderDemo} from './table/table-header-demo'; +import {StickyHeaderDemo} from './sticky-header/sticky-header-demo'; /** * NgModule that includes all Material modules that are required to serve the demo-app. @@ -118,7 +120,8 @@ import {TableHeaderDemo} from './table/table-header-demo'; MdTooltipModule, MdNativeDateModule, CdkTableModule, - StyleModule + StyleModule, + StickyHeaderModule ] }) export class DemoMaterialModule {} @@ -184,6 +187,7 @@ export class DemoMaterialModule {} PlatformDemo, TypographyDemo, ExpansionDemo, + StickyHeaderDemo ], providers: [ {provide: OverlayContainer, useClass: FullscreenOverlayContainer}, diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts index 3f422c3ccd5c..924e1b4dac5e 100644 --- a/src/demo-app/demo-app/demo-app.ts +++ b/src/demo-app/demo-app/demo-app.ts @@ -67,7 +67,8 @@ export class DemoApp { {name: 'Tooltip', route: 'tooltip'}, {name: 'Platform', route: 'platform'}, {name: 'Style', route: 'style'}, - {name: 'Typography', route: 'typography'} + {name: 'Typography', route: 'typography'}, + {name: 'Sticky Header', route: 'sticky-header'}, ]; constructor(private _element: ElementRef) { diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts index 875dc907e604..ee602e0c24fa 100644 --- a/src/demo-app/demo-app/routes.ts +++ b/src/demo-app/demo-app/routes.ts @@ -36,6 +36,8 @@ import {DatepickerDemo} from '../datepicker/datepicker-demo'; import {TableDemo} from '../table/table-demo'; import {TypographyDemo} from '../typography/typography-demo'; import {ExpansionDemo} from '../expansion/expansion-demo'; +import {StickyHeaderDemo} from '../sticky-header/sticky-header-demo'; + export const DEMO_APP_ROUTES: Routes = [ {path: '', component: Home}, @@ -74,4 +76,5 @@ export const DEMO_APP_ROUTES: Routes = [ {path: 'style', component: StyleDemo}, {path: 'typography', component: TypographyDemo}, {path: 'expansion', component: ExpansionDemo}, + {path: 'sticky-header', component: StickyHeaderDemo} ]; diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html new file mode 100644 index 000000000000..d3099043d39b --- /dev/null +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -0,0 +1,100 @@ +
+
+
+

Sticky-header Demo

+
+ +
+ +
+
+

Heading 1

+
+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+
+ + +
+
+

Heading 2

+
+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+
+ +
+
+

Heading 3

+
+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+

test sticky-header

+
+
+
+ +
diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss new file mode 100644 index 000000000000..3d33f404fc00 --- /dev/null +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -0,0 +1,88 @@ +.demo-sticky { + .centered { + text-align: center; + -webkit-appearance: none; + -moz-appearance: none; + height: 500px; + overflow: auto;} + + button { + position: fixed; + margin: 16px; + z-index: 99; + } + + * { + margin: 0; + padding: 0; + } + + body { + font: 14px/1.5 Helvetica, Arial, sans-serif; + -webkit-appearance: none; + -moz-appearance: none; + + } + + h2 { + font-size: 20px; + padding: 20px; + -webkit-appearance: none; + -moz-appearance: none; + } + + p { + margin-bottom: 1.5em; + -webkit-appearance: none; + -moz-appearance: none; + } + + div{ + margin: 0; border: 0; padding: 0; + -webkit-appearance: none; + -moz-appearance: none; + } +} + +.mat-radio-button { + display: block; +} + +.centered { + text-align: center; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 500px; + //width: 900px; + overflow: auto;} + +.container { + margin: 0; + -webkit-appearance: none; + -moz-appearance: none; +} + +.rside { + background: white; + -webkit-appearance: none; + padding: 20px; +} + +.rside1 { + background: #cecece; + -webkit-appearance: none; + padding: 20px; +} + +.rside2 { + background: #cea7a7; + -webkit-appearance: none; + padding: 20px; +} + +.rside3 { + background: #c2eae5; + -webkit-appearance: none; + padding: 20px; +} diff --git a/src/demo-app/sticky-header/sticky-header-demo.ts b/src/demo-app/sticky-header/sticky-header-demo.ts new file mode 100644 index 000000000000..482c8e20128e --- /dev/null +++ b/src/demo-app/sticky-header/sticky-header-demo.ts @@ -0,0 +1,13 @@ +import {Component} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {CdkStickyRegion, CdkStickyHeader} from '@angular/material'; + + +@Component({ + moduleId: module.id, + selector: 'sticky-header-demo', + templateUrl: 'sticky-header-demo.html', + styleUrls: ['sticky-header-demo.css'], +}) + +export class StickyHeaderDemo { } diff --git a/src/lib/sticky-header/sticky-header-dir.ts b/src/lib/sticky-header/sticky-header-dir.ts new file mode 100644 index 000000000000..abb3e93be9f4 --- /dev/null +++ b/src/lib/sticky-header/sticky-header-dir.ts @@ -0,0 +1,314 @@ +/** + * @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 {Component, Directive, Input, Output, EventEmitter, + OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; + +import {Observable} from 'rxjs/Observable'; +import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher'; +import {Scrollable} from '../core/overlay/scroll/scrollable'; +import {Subject} from 'rxjs/Subject'; + + +@Directive({ + selector: '[cdkStickyRegion]', +}) +export class StickyParentDirective { + constructor(private element: ElementRef) { } + + getElementRef(): ElementRef { + return this.element; + } +} + + +@Directive({ + selector: '[cdkStickyHeader]', +}) +export class StickyHeaderDirective implements OnDestroy, AfterViewInit { + + /**Set the sticky-header's z-index as 10 in default. Make it as an input + * variable to make user be able to customize the zIndex when + * the sticky-header's zIndex is not the largest in current page. + * Because if the sticky-header's zIndex is not the largest in current page, + * it may be sheltered by other element when being sticked. + */ + @Input('sticky-zIndex') zIndex: number = 10; + @Input() cdkStickyParentRegion: any; + @Input() scrollableRegion: any; + + + private _activated = new EventEmitter(); + private _deactivated = new EventEmitter(); + + private _onScrollBind: EventListener = this.onScroll.bind(this); + private _onResizeBind: EventListener = this.onResize.bind(this); + private _onTouchMoveBind: EventListener = this.onTouchMove.bind(this); + + public STICK_START_CLASS: string = 'sticky'; + public STICK_END_CLASS: string = 'sticky-end'; + public isStuck: boolean = false; + + // the element with the 'md-sticky' tag + public elem: any; + + // the uppercontainer element with the 'md-sticky-viewport' tag + public stickyParent: any; + + // the upper scrollable container + public upperScrollableContainer: any; + + /** + * the original css of the sticky element, used to reset the sticky element + * when it is being unstuck + */ + public originalCss: any; + + // the height of 'stickyParent' + public containerHeight: number; + + // the height of 'elem' + public elemHeight: number; + + private _containerStart: number; + private _scrollFinish: number; + + private _scrollingWidth: number; + private _scrollingRight: number; + + // the padding of 'elem' + private _elementPadding: any; + private _paddingNumber: number; + + // sticky element's width + private _width: string = 'auto'; + + constructor(private element: ElementRef, + public scrollable: Scrollable, + @Optional() public parentReg: StickyParentDirective) { + this.elem = element.nativeElement; + this.upperScrollableContainer = scrollable.getElementRef().nativeElement; + this.scrollableRegion = scrollable.getElementRef().nativeElement; + if (parentReg != null) { + this.cdkStickyParentRegion = parentReg.getElementRef().nativeElement; + } + } + + ngAfterViewInit(): void { + + if (this.cdkStickyParentRegion != null) { + this.stickyParent = this.cdkStickyParentRegion; + }else { + this.stickyParent = this.elem.parentNode; + } + + this.originalCss = { + zIndex: this.getCssValue(this.elem, 'zIndex'), + position: this.getCssValue(this.elem, 'position'), + top: this.getCssValue(this.elem, 'top'), + right: this.getCssValue(this.elem, 'right'), + left: this.getCssValue(this.elem, 'left'), + bottom: this.getCssValue(this.elem, 'bottom'), + width: this.getCssValue(this.elem, 'width'), + }; + + this._scrollingWidth = this.upperScrollableContainer.scrollWidth; + + this.attach(); + + if (this._width == 'auto') { + this._width = this.originalCss.width; + } + + this.defineRestrictions(); + this.sticker(); + } + + ngOnDestroy(): void { + this.detach(); + } + + attach() { + this.upperScrollableContainer.addEventListener('scroll', this._onScrollBind, false); + this.upperScrollableContainer.addEventListener('resize', this._onResizeBind, false); + + // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones + this.upperScrollableContainer.addEventListener('touchmove', this._onTouchMoveBind, false); + + Observable.fromEvent(this.upperScrollableContainer, 'scroll') + .subscribe(() => this.defineRestrictionsAndStick()); + + Observable.fromEvent(this.upperScrollableContainer, 'touchmove') + .subscribe(() => this.defineRestrictionsAndStick()); + } + + detach() { + this.upperScrollableContainer.removeEventListener('scroll', this._onScrollBind); + this.upperScrollableContainer.removeEventListener('resize', this._onResizeBind); + this.upperScrollableContainer.removeEventListener('touchmove', this._onTouchMoveBind); + } + + onScroll(): void { + this.defineRestrictionsAndStick(); + } + + onTouchMove(): void { + this.defineRestrictionsAndStick(); + } + + onResize(): void { + this.defineRestrictionsAndStick(); + + /** + * If there's already a header being sticked when the page is + * resized. The CSS style of the sticky-header may be not fit + * the resized window. So we need to unstick it then restick it. + */ + if (this.isStuck) { + this.unstuckElement(); + this.stickElement(); + } + } + + /** + * define the restrictions of the sticky header(including stickyWidth, + * when to start, when to finish) + */ + defineRestrictions(): void { + let containerTop: any = this.stickyParent.getBoundingClientRect(); + this.elemHeight = this.elem.offsetHeight; + this.containerHeight = this.getCssNumber(this.stickyParent, 'height'); + this._containerStart = containerTop.top; + + // the padding of the element being sticked + this._elementPadding = this.getCssValue(this.elem, 'padding'); + + this._paddingNumber = Number(this._elementPadding.slice(0, -2)); + this._scrollingWidth = this.upperScrollableContainer.clientWidth - + this._paddingNumber - this._paddingNumber; + + this._scrollFinish = this._containerStart + (this.containerHeight - this.elemHeight); + } + + /** + * reset element to its original CSS + */ + resetElement(): void { + this.elem.classList.remove(this.STICK_START_CLASS); + Object.assign(this.elem.style, this.originalCss); + } + + /** + * stuck element, make the element stick to the top of the scrollable container. + */ + stickElement(): void { + this.isStuck = true; + + this.elem.classList.remove(this.STICK_END_CLASS); + this.elem.classList.add(this.STICK_START_CLASS); + + /** Have to add the translate3d function for the sticky element's css style. + * Because iPhone and iPad's browser is using its owning rendering engine. And + * even if you are using Chrome on an iPhone, you are just using Safari with + * a Chrome skin around it. + * + * Safari on iPad and Safari on iPhone do not have resizable windows. + * In Safari on iPhone and iPad, the window size is set to the size of + * the screen (minus Safari user interface controls), and cannot be changed + * by the user. To move around a webpage, the user changes the zoom level and position + * of the viewport as they double tap or pinch to zoom in or out, or by touching + * and dragging to pan the page. As a user changes the zoom level and position of the + * viewport they are doing so within a viewable content area of fixed size + * (that is, the window). This means that webpage elements that have their position + * "fixed" to the viewport can end up outside the viewable content area, offscreen. + * + * So the 'position: fixed' does not work on iPhone and iPad. To make it work, + * I need to use translate3d(0,0,0) to force Safari rerendering the sticky element. + **/ + this.elem.style.transform = 'translate3d(0,0,0)'; + + this.elem.style.zIndex = this.zIndex; + this.elem.style.position = 'fixed'; + this.elem.style.top = this.getCssNumber(this.upperScrollableContainer, 'top') + 'px'; + + this._scrollingRight = this.upperScrollableContainer.offsetLeft + + this.upperScrollableContainer.offsetWidth; + let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right; + this.elem.style.right = stuckRight + 'px'; + + this.elem.style.left = this.upperScrollableContainer.offsetLeft + 'px'; + this.elem.style.bottom = 'auto'; + this.elem.style.width = this._scrollingWidth + 'px'; + + // Set style for sticky element again for Mobile Views. + this.elem.style.setProperty('zIndex', this.zIndex); + this.elem.style.setProperty('position', 'fixed'); + this.elem.style.setProperty('top', this.upperScrollableContainer.offsetTop + 'px'); + this.elem.style.setProperty('right', stuckRight + 'px'); + this.elem.style.setProperty('left', this.upperScrollableContainer.offsetLeft + 'px'); + this.elem.style.setProperty('width', this._scrollingWidth + 'px'); + + this._activated.next(this.elem); + } + + /** + * unstuck element + */ + unstuckElement(): void { + this.isStuck = false; + + this.elem.classList.add(this.STICK_END_CLASS); + + this.stickyParent.style.position = 'relative'; + this.elem.style.position = 'absolute'; + this.elem.style.top = 'auto'; + this.elem.style.right = 0; + this.elem.style.left = 'auto'; + this.elem.style.bottom = 0; + this.elem.style.width = this._width; + + this._deactivated.next(this.elem); + } + + + sticker(): void { + let currentPosition: number = this.upperScrollableContainer.offsetTop; + + // unstick when the element is scrolled out of the sticky region + if (this.isStuck && (currentPosition < this._containerStart || + currentPosition > this._scrollFinish) || currentPosition >= this._scrollFinish) { + this.resetElement(); + if (currentPosition >= this._scrollFinish) { + this.unstuckElement(); + } + this.isStuck = false; // stick when the element is within the sticky region + }else if ( this.isStuck === false && + currentPosition > this._containerStart && currentPosition < this._scrollFinish) { + this.stickElement(); + } + } + + defineRestrictionsAndStick(): void { + this.defineRestrictions(); + this.sticker(); + } + + + private getCssValue(element: any, property: string): any { + let result: any = ''; + if (typeof window.getComputedStyle !== 'undefined') { + result = window.getComputedStyle(element, '').getPropertyValue(property); + }else if (typeof element.currentStyle !== 'undefined') { + result = element.currentStyle.property; + } + return result; + } + + private getCssNumber(element: any, property: string): number { + return parseInt(this.getCssValue(element, property), 10) || 0; + } +} diff --git a/src/lib/sticky-header/sticky-header.ts b/src/lib/sticky-header/sticky-header.ts index 1c520ed7c8df..2606cf439e51 100644 --- a/src/lib/sticky-header/sticky-header.ts +++ b/src/lib/sticky-header/sticky-header.ts @@ -5,9 +5,8 @@ * 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 {Directive, Input, - OnDestroy, AfterViewInit, ElementRef, Optional} from '@angular/core'; -import {Platform} from '../core/platform'; +import {Component, Directive, Input, Output, + OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; import {Scrollable} from '../core/overlay/scroll/scrollable'; import {extendObject} from '../core/util/object-extend'; import {Subscription} from 'rxjs/Subscription'; diff --git a/src/lib/sticky-header/sticky-header.ts~HEAD b/src/lib/sticky-header/sticky-header.ts~HEAD new file mode 100644 index 000000000000..1c520ed7c8df --- /dev/null +++ b/src/lib/sticky-header/sticky-header.ts~HEAD @@ -0,0 +1,289 @@ +/** + * @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 {Directive, Input, + OnDestroy, AfterViewInit, ElementRef, Optional} from '@angular/core'; +import {Platform} from '../core/platform'; +import {Scrollable} from '../core/overlay/scroll/scrollable'; +import {extendObject} from '../core/util/object-extend'; +import {Subscription} from 'rxjs/Subscription'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {RxChain, debounceTime} from '../core/rxjs/index'; +import {isPositionStickySupported} from '@angular/cdk'; + + +/** + * Directive that marks an element as a "sticky region", meant to contain exactly one sticky-header + * along with the content associated with that header. The sticky-header inside of the region will + * "stick" to the top of the scrolling container as long as this region is within the scrolling + * viewport. + * + * If a user does not explicitly define a sticky-region for a sticky-header, the direct + * parent node of the sticky-header will be used as the sticky-region. + */ +@Directive({ + selector: '[cdkStickyRegion]', +}) +export class CdkStickyRegion { + constructor(public readonly _elementRef: ElementRef) { } +} + + +/** Class applied when the header is "stuck" */ +const STICK_START_CLASS = 'cdk-sticky-header-start'; + +/** Class applied when the header is not "stuck" */ +const STICK_END_CLASS = 'cdk-sticky-header-end'; + +/** + * Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, + * touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the + * positioning adversely. + */ +const DEBOUNCE_TIME: number = 5; + +/** + * Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with + * cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky + * region (see cdkStickyRegion) is in view. + */ +@Directive({ + selector: '[cdkStickyHeader]', +}) +export class CdkStickyHeader implements OnDestroy, AfterViewInit { + + /** z-index to be applied to the sticky header (default is 10). */ + @Input('cdkStickyHeaderZIndex') zIndex: number = 10; + + /** boolean value to mark whether the current header is stuck*/ + isStuck: boolean = false; + /** Whether the browser support CSS sticky positioning. */ + private _isPositionStickySupported: boolean = false; + + /** The element with the 'cdkStickyHeader' tag. */ + element: HTMLElement; + /** The upper container element with the 'cdkStickyRegion' tag. */ + stickyParent: HTMLElement | null; + /** The upper scrollable container. */ + upperScrollableContainer: HTMLElement; + /** + * The original css of the sticky element, used to reset the sticky element + * when it is being unstuck + */ + private _originalStyles = {} as CSSStyleDeclaration; + /** + * 'getBoundingClientRect().top' of CdkStickyRegion of current sticky header. + * It is used with '_stickyRegionBottomThreshold' to judge whether the current header + * need to be stuck. + */ + private _stickyRegionTop: number; + /** + * Bottom of the sticky region offset by the height of the sticky header. + * Once the sticky header is scrolled to this position it will stay in place + * so that it will scroll naturally out of view with the rest of the sticky region. + */ + private _stickyRegionBottomThreshold: number; + + private _onScrollSubscription: Subscription; + + private _onTouchSubscription: Subscription; + + private _onResizeSubscription: Subscription; + + constructor(element: ElementRef, + scrollable: Scrollable, + @Optional() public parentRegion: CdkStickyRegion, + platform: Platform) { + if (platform.isBrowser) { + this.element = element.nativeElement; + this.upperScrollableContainer = scrollable.getElementRef().nativeElement; + this._setStrategyAccordingToCompatibility(); + } + } + + ngAfterViewInit(): void { + if (!this._isPositionStickySupported) { + + this.stickyParent = this.parentRegion != null ? + this.parentRegion._elementRef.nativeElement : this.element.parentElement; + + let headerStyles = window.getComputedStyle(this.element, ''); + this._originalStyles = { + position: headerStyles.position, + top: headerStyles.top, + right: headerStyles.right, + left: headerStyles.left, + bottom: headerStyles.bottom, + width: headerStyles.width, + zIndex: headerStyles.zIndex + } as CSSStyleDeclaration; + + this._attachEventListeners(); + this._updateStickyPositioning(); + } + } + + ngOnDestroy(): void { + [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription] + .forEach(s => s && s.unsubscribe()); + } + + /** + * Check if current browser supports sticky positioning. If yes, apply + * sticky positioning. If not, use the original implementation. + */ + private _setStrategyAccordingToCompatibility(): void { + this._isPositionStickySupported = isPositionStickySupported(); + if (this._isPositionStickySupported) { + this.element.style.top = '0'; + this.element.style.cssText += 'position: -webkit-sticky; position: sticky; '; + // TODO(sllethe): add css class with both 'sticky' and '-webkit-sticky' on position + // when @Directory supports adding CSS class + } + } + + /** Add listeners for events that affect sticky positioning. */ + private _attachEventListeners() { + this._onScrollSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'scroll')) + .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); + + // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones + this._onTouchSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'touchmove')) + .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); + + this._onResizeSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'resize')) + .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this.onResize()); + } + + onResize(): void { + this._updateStickyPositioning(); + // If there's already a header being stick when the page is + // resized. The CSS style of the cdkStickyHeader element may be not fit + // the resized window. So we need to unstuck it then re-stick it. + // unstuck() can set 'isStuck' to FALSE. Then _stickElement() can work. + if (this.isStuck) { + this._unstickElement(); + this._stickElement(); + } + } + + /** Measures the boundaries of the sticky regions to be used in subsequent positioning. */ + private _measureStickyRegionBounds(): void { + if (!this.stickyParent) { + return; + } + const boundingClientRect: any = this.stickyParent.getBoundingClientRect(); + this._stickyRegionTop = boundingClientRect.top; + let stickRegionHeight = boundingClientRect.height; + + this._stickyRegionBottomThreshold = this._stickyRegionTop + + (stickRegionHeight - this.element.offsetHeight); + } + + /** Reset element to its original CSS. */ + private _resetElementStyles(): void { + this.element.classList.remove(STICK_START_CLASS); + extendObject(this.element.style, this._originalStyles); + } + + /** Stuck element, make the element stick to the top of the scrollable container. */ + private _stickElement(): void { + this.isStuck = true; + + this.element.classList.remove(STICK_END_CLASS); + this.element.classList.add(STICK_START_CLASS); + + // Have to add the translate3d function for the sticky element's css style. + // Because iPhone and iPad's browser is using its owning rendering engine. And + // even if you are using Chrome on an iPhone, you are just using Safari with + // a Chrome skin around it. + // + // Safari on iPad and Safari on iPhone do not have resizable windows. + // In Safari on iPhone and iPad, the window size is set to the size of + // the screen (minus Safari user interface controls), and cannot be changed + // by the user. To move around a webpage, the user changes the zoom level and position + // of the viewport as they double tap or pinch to zoom in or out, or by touching + // and dragging to pan the page. As a user changes the zoom level and position of the + // viewport they are doing so within a viewable content area of fixed size + // (that is, the window). This means that webpage elements that have their position + // "fixed" to the viewport can end up outside the viewable content area, offscreen. + // + // So the 'position: fixed' does not work on iPhone and iPad. To make it work, + // 'translate3d(0,0,0)' needs to be used to force Safari re-rendering the sticky element. + this.element.style.transform = 'translate3d(0px,0px,0px)'; + + let stuckRight: number = this.upperScrollableContainer.getBoundingClientRect().right; + + let stickyCss = { + position: 'fixed', + top: this.upperScrollableContainer.offsetTop + 'px', + right: stuckRight + 'px', + left: this.upperScrollableContainer.offsetLeft + 'px', + bottom: 'auto', + width: this._originalStyles.width, + zIndex: this.zIndex + '' + }; + extendObject(this.element.style, stickyCss); + } + + /** + * Unsticks the header so that it goes back to scrolling normally. + * + * This should be called when the element reaches the bottom of its cdkStickyRegion so that it + * smoothly scrolls out of view as the next sticky-header moves in. + */ + private _unstickElement(): void { + this.isStuck = false; + + if (!this.stickyParent) { + return; + } + + this.element.classList.add(STICK_END_CLASS); + this.stickyParent.style.position = 'relative'; + let unstuckCss = { + position: 'absolute', + top: 'auto', + right: '0', + left: 'auto', + bottom: '0', + width: this._originalStyles.width + }; + extendObject(this.element.style, unstuckCss); + } + + + /** + * '_applyStickyPositionStyles()' function contains the main logic of sticky-header. It decides when + * a header should be stick and when should it be unstuck by comparing the offsetTop + * of scrollable container with the top and bottom of the sticky region. + */ + _applyStickyPositionStyles(): void { + let currentPosition: number = this.upperScrollableContainer.offsetTop; + + // unstuck when the element is scrolled out of the sticky region + if (this.isStuck && + (currentPosition < this._stickyRegionTop || + currentPosition > this._stickyRegionBottomThreshold) || + currentPosition >= this._stickyRegionBottomThreshold) { + this._resetElementStyles(); + if (currentPosition >= this._stickyRegionBottomThreshold) { + this._unstickElement(); + } + this.isStuck = false; // stick when the element is within the sticky region + } else if ( this.isStuck === false && + currentPosition > this._stickyRegionTop && + currentPosition < this._stickyRegionBottomThreshold) { + this._stickElement(); + } + } + + _updateStickyPositioning(): void { + this._measureStickyRegionBounds(); + this._applyStickyPositionStyles(); + } +} From 52c6bce68671dabcb2bd443748a93c0b45f9a1d0 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Wed, 21 Jun 2017 15:39:07 -0700 Subject: [PATCH 02/16] # This is a combination of 7 commits. # This is the 1st commit message: # This is a combination of 3 commits. # This is the 1st commit message: # This is a combination of 6 commits. # This is the 1st commit message: refine code encapsulate 'set style for element' change @Input() Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')' add const STICK_START_CLASS and STICK_END_CLASS Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'. encapsulate reset css style operation for sticky header. delete unnecessary gloable variables delete global variable '_width' Add doc for 'sticker()' function. explained how it works. add more doc for 'sticker()', explaining 'isStuck' flag 2 space for indent fix delete sticky-header demo part from this branch # This is the commit message #2: revert firebase file # This is the commit message #3: change code according to comments in PR # This is the commit message #4: revert firbaserc # This is the commit message #5: revert demo-app.ts # This is the commit message #6: revert routes.ts # This is the commit message #2: change # This is the commit message #3: fix the problem of : 'this.stickyParent' might be 'null' # This is the commit message #2: change doc # This is the commit message #3: Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }' # This is the commit message #4: Added prefix 'mat-' for CSS class # This is the commit message #5: Delete 'public' before variables # This is the commit message #6: Object.assign isn't supported in IE11; use extendObject from src/lib/core/util. # This is the commit message #7: IE11 will have trouble with `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);' --- .firebaserc | 5 - src/demo-app/demo-app/demo-app.ts | 3 +- src/demo-app/demo-app/routes.ts | 2 - src/lib/sticky-header/sticky-header-dir.ts | 546 ++++++++++----------- 4 files changed, 263 insertions(+), 293 deletions(-) delete mode 100644 .firebaserc diff --git a/.firebaserc b/.firebaserc deleted file mode 100644 index e0b738683d94..000000000000 --- a/.firebaserc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "projects": { - "default": "material2-dev" - } -} diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts index 924e1b4dac5e..3f422c3ccd5c 100644 --- a/src/demo-app/demo-app/demo-app.ts +++ b/src/demo-app/demo-app/demo-app.ts @@ -67,8 +67,7 @@ export class DemoApp { {name: 'Tooltip', route: 'tooltip'}, {name: 'Platform', route: 'platform'}, {name: 'Style', route: 'style'}, - {name: 'Typography', route: 'typography'}, - {name: 'Sticky Header', route: 'sticky-header'}, + {name: 'Typography', route: 'typography'} ]; constructor(private _element: ElementRef) { diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts index ee602e0c24fa..64615b68f397 100644 --- a/src/demo-app/demo-app/routes.ts +++ b/src/demo-app/demo-app/routes.ts @@ -36,8 +36,6 @@ import {DatepickerDemo} from '../datepicker/datepicker-demo'; import {TableDemo} from '../table/table-demo'; import {TypographyDemo} from '../typography/typography-demo'; import {ExpansionDemo} from '../expansion/expansion-demo'; -import {StickyHeaderDemo} from '../sticky-header/sticky-header-demo'; - export const DEMO_APP_ROUTES: Routes = [ {path: '', component: Home}, diff --git a/src/lib/sticky-header/sticky-header-dir.ts b/src/lib/sticky-header/sticky-header-dir.ts index abb3e93be9f4..7222af2f1b03 100644 --- a/src/lib/sticky-header/sticky-header-dir.ts +++ b/src/lib/sticky-header/sticky-header-dir.ts @@ -5,310 +5,288 @@ * 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 {Component, Directive, Input, Output, EventEmitter, - OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; - -import {Observable} from 'rxjs/Observable'; -import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher'; +import {Component, Directive, Input, Output, + OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; import {Scrollable} from '../core/overlay/scroll/scrollable'; -import {Subject} from 'rxjs/Subject'; +import {extendObject} from '../core/util/object-extend'; +/** + * Directive that marks an element as a "sticky region", meant to contain exactly one sticky-header + * along with the content associated with that header. The sticky-header inside of the region will + * "stick" to the top of the scrolling container as long as this region is within the scrolling + * viewport. + * + * If a user does not explicitly define a sticky-region for a sticky-header, the direct + * parent node of the sticky-header will be used as the sticky-region. + */ @Directive({ - selector: '[cdkStickyRegion]', + selector: '[cdkStickyRegion]', }) -export class StickyParentDirective { - constructor(private element: ElementRef) { } - - getElementRef(): ElementRef { - return this.element; - } +export class CdkStickyRegion { + constructor(public readonly _elementRef: ElementRef) { } } +const STICK_START_CLASS = 'mat-stick-start'; +const STICK_END_CLASS = 'mat-stick-end'; @Directive({ - selector: '[cdkStickyHeader]', + selector: '[cdkStickyHeader]', }) -export class StickyHeaderDirective implements OnDestroy, AfterViewInit { - - /**Set the sticky-header's z-index as 10 in default. Make it as an input - * variable to make user be able to customize the zIndex when - * the sticky-header's zIndex is not the largest in current page. - * Because if the sticky-header's zIndex is not the largest in current page, - * it may be sheltered by other element when being sticked. - */ - @Input('sticky-zIndex') zIndex: number = 10; - @Input() cdkStickyParentRegion: any; - @Input() scrollableRegion: any; - - - private _activated = new EventEmitter(); - private _deactivated = new EventEmitter(); - - private _onScrollBind: EventListener = this.onScroll.bind(this); - private _onResizeBind: EventListener = this.onResize.bind(this); - private _onTouchMoveBind: EventListener = this.onTouchMove.bind(this); - - public STICK_START_CLASS: string = 'sticky'; - public STICK_END_CLASS: string = 'sticky-end'; - public isStuck: boolean = false; - - // the element with the 'md-sticky' tag - public elem: any; - - // the uppercontainer element with the 'md-sticky-viewport' tag - public stickyParent: any; - - // the upper scrollable container - public upperScrollableContainer: any; - - /** - * the original css of the sticky element, used to reset the sticky element - * when it is being unstuck - */ - public originalCss: any; - - // the height of 'stickyParent' - public containerHeight: number; - - // the height of 'elem' - public elemHeight: number; - - private _containerStart: number; - private _scrollFinish: number; - - private _scrollingWidth: number; - private _scrollingRight: number; - - // the padding of 'elem' - private _elementPadding: any; - private _paddingNumber: number; - - // sticky element's width - private _width: string = 'auto'; - - constructor(private element: ElementRef, - public scrollable: Scrollable, - @Optional() public parentReg: StickyParentDirective) { - this.elem = element.nativeElement; - this.upperScrollableContainer = scrollable.getElementRef().nativeElement; - this.scrollableRegion = scrollable.getElementRef().nativeElement; - if (parentReg != null) { - this.cdkStickyParentRegion = parentReg.getElementRef().nativeElement; - } - } - - ngAfterViewInit(): void { - - if (this.cdkStickyParentRegion != null) { - this.stickyParent = this.cdkStickyParentRegion; - }else { - this.stickyParent = this.elem.parentNode; - } - - this.originalCss = { - zIndex: this.getCssValue(this.elem, 'zIndex'), - position: this.getCssValue(this.elem, 'position'), - top: this.getCssValue(this.elem, 'top'), - right: this.getCssValue(this.elem, 'right'), - left: this.getCssValue(this.elem, 'left'), - bottom: this.getCssValue(this.elem, 'bottom'), - width: this.getCssValue(this.elem, 'width'), - }; - - this._scrollingWidth = this.upperScrollableContainer.scrollWidth; - - this.attach(); - - if (this._width == 'auto') { - this._width = this.originalCss.width; - } - - this.defineRestrictions(); - this.sticker(); - } - - ngOnDestroy(): void { - this.detach(); - } - - attach() { - this.upperScrollableContainer.addEventListener('scroll', this._onScrollBind, false); - this.upperScrollableContainer.addEventListener('resize', this._onResizeBind, false); - - // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones - this.upperScrollableContainer.addEventListener('touchmove', this._onTouchMoveBind, false); - - Observable.fromEvent(this.upperScrollableContainer, 'scroll') - .subscribe(() => this.defineRestrictionsAndStick()); - - Observable.fromEvent(this.upperScrollableContainer, 'touchmove') - .subscribe(() => this.defineRestrictionsAndStick()); - } - - detach() { - this.upperScrollableContainer.removeEventListener('scroll', this._onScrollBind); - this.upperScrollableContainer.removeEventListener('resize', this._onResizeBind); - this.upperScrollableContainer.removeEventListener('touchmove', this._onTouchMoveBind); - } - - onScroll(): void { - this.defineRestrictionsAndStick(); - } - - onTouchMove(): void { - this.defineRestrictionsAndStick(); - } - - onResize(): void { - this.defineRestrictionsAndStick(); - - /** - * If there's already a header being sticked when the page is - * resized. The CSS style of the sticky-header may be not fit - * the resized window. So we need to unstick it then restick it. - */ - if (this.isStuck) { - this.unstuckElement(); - this.stickElement(); - } - } - - /** - * define the restrictions of the sticky header(including stickyWidth, - * when to start, when to finish) - */ - defineRestrictions(): void { - let containerTop: any = this.stickyParent.getBoundingClientRect(); - this.elemHeight = this.elem.offsetHeight; - this.containerHeight = this.getCssNumber(this.stickyParent, 'height'); - this._containerStart = containerTop.top; - - // the padding of the element being sticked - this._elementPadding = this.getCssValue(this.elem, 'padding'); - - this._paddingNumber = Number(this._elementPadding.slice(0, -2)); - this._scrollingWidth = this.upperScrollableContainer.clientWidth - - this._paddingNumber - this._paddingNumber; - - this._scrollFinish = this._containerStart + (this.containerHeight - this.elemHeight); - } - - /** - * reset element to its original CSS - */ - resetElement(): void { - this.elem.classList.remove(this.STICK_START_CLASS); - Object.assign(this.elem.style, this.originalCss); +/** + * Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with + * cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky + * region (see cdkStickyRegion) is in view. + */ +export class CdkStickyHeader implements OnDestroy, AfterViewInit { + + /** + * Set the sticky-header's z-index as 10 in default. Make it as an input + * variable to make user be able to customize the zIndex when + * the sticky-header's zIndex is not the largest in current page. + * Because if the sticky-header's zIndex is not the largest in current page, + * it may be sheltered by other element when being stuck. + */ + @Input('cdkStickyHeaderZIndex') zIndex: number = 10; + @Input('cdkStickyParentRegion') parentRegion: HTMLElement; + @Input('cdkStickyScrollableRegion') scrollableRegion: HTMLElement; + + private _onScrollBind: EventListener = this.onScroll.bind(this); + private _onResizeBind: EventListener = this.onResize.bind(this); + private _onTouchMoveBind: EventListener = this.onTouchMove.bind(this); + isStuck: boolean = false; + + // the element with the 'cdkStickyHeader' tag + element: HTMLElement; + + // the upper container element with the 'cdkStickyRegion' tag + stickyParent: HTMLElement | null; + upperScrollableContainer: HTMLElement; + + /** + * the original css of the sticky element, used to reset the sticky element + * when it is being unstuck + */ + originalCss: any; + + private _containerStart: number; + private _scrollFinish: number; + + private _scrollingWidth: number; + + constructor(_element: ElementRef, + scrollable: Scrollable, + @Optional() public parentReg: CdkStickyRegion) { + this.element = _element.nativeElement; + this.upperScrollableContainer = scrollable.getElementRef().nativeElement; + this.scrollableRegion = scrollable.getElementRef().nativeElement; + } + + ngAfterViewInit(): void { + this.stickyParent = this.parentReg != null ? + this.parentReg._elementRef.nativeElement : this.element.parentElement; + this.originalCss = { + zIndex: this.getCssValue(this.element, 'zIndex'), + position: this.getCssValue(this.element, 'position'), + top: this.getCssValue(this.element, 'top'), + right: this.getCssValue(this.element, 'right'), + left: this.getCssValue(this.element, 'left'), + bottom: this.getCssValue(this.element, 'bottom'), + width: this.getCssValue(this.element, 'width'), + }; + this.attach(); + this.defineRestrictionsAndStick(); + } + + ngOnDestroy(): void { + this.upperScrollableContainer.removeEventListener('scroll', this._onScrollBind); + this.upperScrollableContainer.removeEventListener('resize', this._onResizeBind); + this.upperScrollableContainer.removeEventListener('touchmove', this._onTouchMoveBind); + } + + attach() { + this.upperScrollableContainer.addEventListener('scroll', this._onScrollBind, false); + this.upperScrollableContainer.addEventListener('resize', this._onResizeBind, false); + + // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones + this.upperScrollableContainer.addEventListener('touchmove', this._onTouchMoveBind, false); + } + + onScroll(): void { + this.defineRestrictionsAndStick(); + } + + onTouchMove(): void { + this.defineRestrictionsAndStick(); + } + + onResize(): void { + this.defineRestrictionsAndStick(); + // If there's already a header being stick when the page is + // resized. The CSS style of the cdkStickyHeader element may be not fit + // the resized window. So we need to unstuck it then re-stick it. + // unstuck() can set 'isStuck' to FALSE. Then stickElement() can work. + if (this.isStuck) { + this.unstuckElement(); + this.stickElement(); } - - /** - * stuck element, make the element stick to the top of the scrollable container. - */ - stickElement(): void { - this.isStuck = true; - - this.elem.classList.remove(this.STICK_END_CLASS); - this.elem.classList.add(this.STICK_START_CLASS); - - /** Have to add the translate3d function for the sticky element's css style. - * Because iPhone and iPad's browser is using its owning rendering engine. And - * even if you are using Chrome on an iPhone, you are just using Safari with - * a Chrome skin around it. - * - * Safari on iPad and Safari on iPhone do not have resizable windows. - * In Safari on iPhone and iPad, the window size is set to the size of - * the screen (minus Safari user interface controls), and cannot be changed - * by the user. To move around a webpage, the user changes the zoom level and position - * of the viewport as they double tap or pinch to zoom in or out, or by touching - * and dragging to pan the page. As a user changes the zoom level and position of the - * viewport they are doing so within a viewable content area of fixed size - * (that is, the window). This means that webpage elements that have their position - * "fixed" to the viewport can end up outside the viewable content area, offscreen. - * - * So the 'position: fixed' does not work on iPhone and iPad. To make it work, - * I need to use translate3d(0,0,0) to force Safari rerendering the sticky element. - **/ - this.elem.style.transform = 'translate3d(0,0,0)'; - - this.elem.style.zIndex = this.zIndex; - this.elem.style.position = 'fixed'; - this.elem.style.top = this.getCssNumber(this.upperScrollableContainer, 'top') + 'px'; - - this._scrollingRight = this.upperScrollableContainer.offsetLeft + - this.upperScrollableContainer.offsetWidth; - let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right; - this.elem.style.right = stuckRight + 'px'; - - this.elem.style.left = this.upperScrollableContainer.offsetLeft + 'px'; - this.elem.style.bottom = 'auto'; - this.elem.style.width = this._scrollingWidth + 'px'; - - // Set style for sticky element again for Mobile Views. - this.elem.style.setProperty('zIndex', this.zIndex); - this.elem.style.setProperty('position', 'fixed'); - this.elem.style.setProperty('top', this.upperScrollableContainer.offsetTop + 'px'); - this.elem.style.setProperty('right', stuckRight + 'px'); - this.elem.style.setProperty('left', this.upperScrollableContainer.offsetLeft + 'px'); - this.elem.style.setProperty('width', this._scrollingWidth + 'px'); - - this._activated.next(this.elem); + } + + /** + * define the restrictions of the sticky header(including stickyWidth, + * when to start, when to finish) + */ + defineRestrictions(): void { + if(this.stickyParent == null) { + return; } + let containerTop: any = this.stickyParent.getBoundingClientRect(); + let elemHeight: number = this.element.offsetHeight; + let containerHeight: number = this.getCssNumber(this.stickyParent, 'height'); + this._containerStart = containerTop.top; + + // the padding of the element being sticked + let elementPadding: any = this.getCssValue(this.element, 'padding'); + + let paddingNumber: any = Number(elementPadding.slice(0, -2)); + this._scrollingWidth = this.upperScrollableContainer.clientWidth - + paddingNumber - paddingNumber; + + this._scrollFinish = this._containerStart + (containerHeight - elemHeight); + } + + /** + * Reset element to its original CSS + */ + resetElement(): void { + this.element.classList.remove(STICK_START_CLASS); + extendObject(this.element.style, this.originalCss); + } + + /** + * Stuck element, make the element stick to the top of the scrollable container. + */ + stickElement(): void { + this.isStuck = true; + + this.element.classList.remove(STICK_END_CLASS); + this.element.classList.add(STICK_START_CLASS); /** - * unstuck element - */ - unstuckElement(): void { - this.isStuck = false; - - this.elem.classList.add(this.STICK_END_CLASS); - - this.stickyParent.style.position = 'relative'; - this.elem.style.position = 'absolute'; - this.elem.style.top = 'auto'; - this.elem.style.right = 0; - this.elem.style.left = 'auto'; - this.elem.style.bottom = 0; - this.elem.style.width = this._width; - - this._deactivated.next(this.elem); + * Have to add the translate3d function for the sticky element's css style. + * Because iPhone and iPad's browser is using its owning rendering engine. And + * even if you are using Chrome on an iPhone, you are just using Safari with + * a Chrome skin around it. + * + * Safari on iPad and Safari on iPhone do not have resizable windows. + * In Safari on iPhone and iPad, the window size is set to the size of + * the screen (minus Safari user interface controls), and cannot be changed + * by the user. To move around a webpage, the user changes the zoom level and position + * of the viewport as they double tap or pinch to zoom in or out, or by touching + * and dragging to pan the page. As a user changes the zoom level and position of the + * viewport they are doing so within a viewable content area of fixed size + * (that is, the window). This means that webpage elements that have their position + * "fixed" to the viewport can end up outside the viewable content area, offscreen. + * + * So the 'position: fixed' does not work on iPhone and iPad. To make it work, + * 'translate3d(0,0,0)' needs to be used to force Safari re-rendering the sticky element. + **/ + this.element.style.transform = 'translate3d(0px,0px,0px)'; + + let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right; + + let stickyCss:any = { + zIndex: this.zIndex, + position: 'fixed', + top: this.upperScrollableContainer.offsetTop + 'px', + right: stuckRight + 'px', + left: this.upperScrollableContainer.offsetLeft + 'px', + bottom: 'auto', + width: this._scrollingWidth + 'px', + }; + extendObject(this.element.style, stickyCss); + } + + /** + * Unstuck element: When an element reaches the bottom of its cdkStickyRegion, + * It should be unstuck. And its position will be set as 'relative', its bottom + * will be set as '0'. So it will be stick at the bottom of its cdkStickyRegion and + * will be scrolled up with its cdkStickyRegion element. In this way, the sticky header + * can be changed smoothly when two sticky header meet and the later one need to replace + * the former one. + */ + unstuckElement(): void { + this.isStuck = false; + + if(this.stickyParent == null) { + return; } - - sticker(): void { - let currentPosition: number = this.upperScrollableContainer.offsetTop; - - // unstick when the element is scrolled out of the sticky region - if (this.isStuck && (currentPosition < this._containerStart || - currentPosition > this._scrollFinish) || currentPosition >= this._scrollFinish) { - this.resetElement(); - if (currentPosition >= this._scrollFinish) { - this.unstuckElement(); - } - this.isStuck = false; // stick when the element is within the sticky region - }else if ( this.isStuck === false && - currentPosition > this._containerStart && currentPosition < this._scrollFinish) { - this.stickElement(); - } + this.element.classList.add(STICK_END_CLASS); + this.stickyParent.style.position = 'relative'; + let unstuckCss: any = { + position: 'absolute', + top: 'auto', + right: '0', + left: 'auto', + bottom: '0', + width: this.originalCss.width, + }; + extendObject(this.element.style, unstuckCss); + } + + + /** + * 'sticker()' function contains the main logic of sticky-header. It decides when + * a header should be stick and when should it be unstuck. It will first get + * the offsetTop of the upper scrollable container. And then get the Start and End + * of the sticky-header's stickyRegion. + * The header will be stick if 'stickyRegion Start < container offsetTop < stickyRegion End'. + * And when 'stickyRegion End < container offsetTop', the header will be unstuck. It will be + * stick to the bottom of its stickyRegion container and being scrolled up with its stickyRegion + * container. + * When 'stickyRegion Start > container offsetTop', which means the header come back to the + * middle of the scrollable container, the header will be reset to its + * original CSS. + * A flag, isStuck. is used in this function. When a header is stick, isStuck = true. + * And when the 'isStuck' flag is TRUE, the sticky-header will not be repaint, which + * decreases the times on repainting sticky-header. + */ + sticker(): void { + let currentPosition: number = this.upperScrollableContainer.offsetTop; + + // unstuck when the element is scrolled out of the sticky region + if (this.isStuck && + (currentPosition < this._containerStart || currentPosition > this._scrollFinish) || + currentPosition >= this._scrollFinish) { + this.resetElement(); + if (currentPosition >= this._scrollFinish) { + this.unstuckElement(); + } + this.isStuck = false; // stick when the element is within the sticky region + } else if ( this.isStuck === false && + currentPosition > this._containerStart && currentPosition < this._scrollFinish) { + this.stickElement(); } + } - defineRestrictionsAndStick(): void { - this.defineRestrictions(); - this.sticker(); - } + defineRestrictionsAndStick(): void { + this.defineRestrictions(); + this.sticker(); + } - private getCssValue(element: any, property: string): any { - let result: any = ''; - if (typeof window.getComputedStyle !== 'undefined') { - result = window.getComputedStyle(element, '').getPropertyValue(property); - }else if (typeof element.currentStyle !== 'undefined') { - result = element.currentStyle.property; - } - return result; + private getCssValue(element: any, property: string): any { + let result: any = ''; + if (typeof window.getComputedStyle !== 'undefined') { + result = window.getComputedStyle(element, '').getPropertyValue(property); + } else if (typeof element.currentStyle !== 'undefined') { + result = element.currentStyle.property; } + return result; + } - private getCssNumber(element: any, property: string): number { - return parseInt(this.getCssValue(element, property), 10) || 0; - } + private getCssNumber(element: any, property: string): number { + return parseInt(this.getCssValue(element, property), 10) || 0; + } } From 96e6d0c6cf6ba05dad70a2a5077fdebffadbf3ae Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Mon, 26 Jun 2017 16:31:24 -0700 Subject: [PATCH 03/16] refine code encapsulate 'set style for element' change @Input() Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')' add const STICK_START_CLASS and STICK_END_CLASS Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'. encapsulate reset css style operation for sticky header. delete unnecessary gloable variables delete global variable '_width' Add doc for 'sticker()' function. explained how it works. add more doc for 'sticker()', explaining 'isStuck' flag 2 space for indent fix delete sticky-header demo part from this branch revert firebase file change code according to comments in PR revert firbaserc revert demo-app.ts revert routes.ts change fix the problem of : 'this.stickyParent' might be 'null' change doc Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }' Added prefix 'mat-' for CSS class Delete 'public' before variables Object.assign isn't supported in IE11; use extendObject from src/lib/core/util. IE11 will have trouble with `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);' Added docs for all variables extract 'generate CSS style' created a generateStyleCSS() function, let it be responsible for generating all those CSS styles. reformat add debounce to solve 'getBoundingClientRect() cause slow down' problem. add position:sticky and check whether browser support it. If not , use the naive implementation removed unused import Removed unused 'scrollableRegion' and 'parentRegion' removed commented lines default public Add comments about why setting style top and position for iPhone and not IE. And extract detectBrowser() as a new function format consider all circumstances of browser. use "===" instead of '==' make 'navigator.userAgent.toLocaleLowerCase()' a local variable optimize Added comments on const 'STICK_START_CLASS' and 'STICK_END_CLASS'. change their content to cdk-sticky-header-start and cdk-sticky-header-end Added comments for STICK_START_CLASS and STICK_END_CLASS. Changed the format of one-line JsDoc unsubscribe sbscriptions onDestory Use what modernizr does on compatibility instead of get the browser version directly. add 'padding' and 'stickyRegionHeight' variables to avoid calling 'getComputedStyle()' too many times (which is expensive). move docs above @Directive removed the underscore in'_element: ElementRef', expand 'reg' to 'region' use 'if (this.isIE)' instead of 'if(this.isIE === true)' added more newlines between params in 'generateCssStyle()' function to make it easier to understand. Added reference link to Modernizer in docs of getSupportList() Deleted "_supportList" variable renamed 'isIE' to 'isStickyPositionSupported', and removed extra space before Observable Set debounce time as a const variable Added docs for 'const DEBOUNCE_TIME: number = 5;' Changed ' if(this.stickyParent == null)' to ' if(!this.stickyParent)' Removed the @param and @returns and make sure the types are correct in the function signature in 'generateCssStyle(...)' function Added docs for `isStickyPositionSupported` variable changed '+=' to '=' of 'stickyText' in getSupportList() function nit added " " between 'if' and '(' nit Added comments deleted unused import change comments optimize comments deleted unnecessary global variables(padding and stickyRegionHeight) Added check whether we are on browser Array to string[] test? try to reopen the old PR fix after rebase revert list.ts test test 222 revert demo revert list.ts second time Move code to 'src/cdk' revert 'move code to 'src/cdk'' , it should be done in a new PR revert avoid calling 'getComputedStyle()' too many times. rename as sticky-header.ts imported PlatformModule Add blank lines between these top-level symbol make '_isStickyPositionSupported' private Changed the originalCSS to private and use '{} as CSSStyleDeclaration' instead of ''any. Rename '_containerStart' to '_stickyRegionTop' rename optimize discription for '_stickyRegionBottomThreshold' private _originalStyles = { position: '', top: '', right: '', left: '', bottom: '', width: '', zIndex: ''}; Deleted 'generateCssStyle()' and 'getCssNumber()' function Deleted 'getCssValue()' function fix CSSStyleDeclaration change sticky width to 'this.upperScrollableContainer.clientWidth' fix nit Added the 'isPositionStickySupported() ' function to src/cdk/platform/features.ts. Consume that function in this component and just always use both the webkit and unprefixed styles. nit nit update doc 'Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the positioning adversely.' changed the doc to '/** z-index to be applied to the sticky header (default is 10). */' fix tslint error for comment 'Can you evaluate each method to make sure their accessor privacy is right? E.g. see which functions need to be public, private, static, etc' Deleted variable 'elemHeight' Chaned to 'if (!this.stickyParent)' Simplified Docs for 'sticker()'. set 'defineRestriction()' function to private use 'RxChain' deleted unused 'tableModule' in modules.ts rename to '_isPositionStickySupported' Use // for comments, /* */ for docs @angular/cdk rename : values -> headerStyles Move closing brace to the next line optimized: [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription] .forEach(s => s && s.unsubscribe()); You should be able to do just '0' instead of '0px' Format like TODO(sllethe): ... private _attachEventListeners? Add a description like "Add listeners for events that affect sticky positioning." optimize doc Rename 'defineRestrictions' to '_measureStickyRegionBounds' rename: private _resetElementStyles let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right; chaned 'any' to 'number' nit change doc '/** * Unsticks the header so that it goes back to scrolling normally. * * This should be called when the element reaches the bottom of its cdkStickyRegion so that it * smoothly scrolls out of view as the next sticky-header moves in. */' _unstuckElement -> _unstickElement rename 'sticker()' to '_applyStickyPositionStyles()' rename 'defineRestrictionsAndStick()' to '_updateStickyPositioning()' --- src/demo-app/demo-app-module.ts | 1 - src/lib/sticky-header/sticky-header-dir.ts | 292 ------------------ src/lib/sticky-header/sticky-header.ts | 5 +- src/lib/sticky-header/sticky-header.ts~HEAD_0 | 288 +++++++++++++++++ 4 files changed, 291 insertions(+), 295 deletions(-) delete mode 100644 src/lib/sticky-header/sticky-header-dir.ts create mode 100644 src/lib/sticky-header/sticky-header.ts~HEAD_0 diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index 0563f49cb234..077acc830146 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -81,7 +81,6 @@ import { } from '@angular/material'; import {CdkTableModule} from '@angular/cdk'; import {TableHeaderDemo} from './table/table-header-demo'; -import {StickyHeaderDemo} from './sticky-header/sticky-header-demo'; /** * NgModule that includes all Material modules that are required to serve the demo-app. diff --git a/src/lib/sticky-header/sticky-header-dir.ts b/src/lib/sticky-header/sticky-header-dir.ts deleted file mode 100644 index 7222af2f1b03..000000000000 --- a/src/lib/sticky-header/sticky-header-dir.ts +++ /dev/null @@ -1,292 +0,0 @@ -/** - * @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 {Component, Directive, Input, Output, - OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; -import {Scrollable} from '../core/overlay/scroll/scrollable'; -import {extendObject} from '../core/util/object-extend'; - - -/** - * Directive that marks an element as a "sticky region", meant to contain exactly one sticky-header - * along with the content associated with that header. The sticky-header inside of the region will - * "stick" to the top of the scrolling container as long as this region is within the scrolling - * viewport. - * - * If a user does not explicitly define a sticky-region for a sticky-header, the direct - * parent node of the sticky-header will be used as the sticky-region. - */ -@Directive({ - selector: '[cdkStickyRegion]', -}) -export class CdkStickyRegion { - constructor(public readonly _elementRef: ElementRef) { } -} - - -const STICK_START_CLASS = 'mat-stick-start'; -const STICK_END_CLASS = 'mat-stick-end'; -@Directive({ - selector: '[cdkStickyHeader]', -}) -/** - * Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with - * cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky - * region (see cdkStickyRegion) is in view. - */ -export class CdkStickyHeader implements OnDestroy, AfterViewInit { - - /** - * Set the sticky-header's z-index as 10 in default. Make it as an input - * variable to make user be able to customize the zIndex when - * the sticky-header's zIndex is not the largest in current page. - * Because if the sticky-header's zIndex is not the largest in current page, - * it may be sheltered by other element when being stuck. - */ - @Input('cdkStickyHeaderZIndex') zIndex: number = 10; - @Input('cdkStickyParentRegion') parentRegion: HTMLElement; - @Input('cdkStickyScrollableRegion') scrollableRegion: HTMLElement; - - private _onScrollBind: EventListener = this.onScroll.bind(this); - private _onResizeBind: EventListener = this.onResize.bind(this); - private _onTouchMoveBind: EventListener = this.onTouchMove.bind(this); - isStuck: boolean = false; - - // the element with the 'cdkStickyHeader' tag - element: HTMLElement; - - // the upper container element with the 'cdkStickyRegion' tag - stickyParent: HTMLElement | null; - upperScrollableContainer: HTMLElement; - - /** - * the original css of the sticky element, used to reset the sticky element - * when it is being unstuck - */ - originalCss: any; - - private _containerStart: number; - private _scrollFinish: number; - - private _scrollingWidth: number; - - constructor(_element: ElementRef, - scrollable: Scrollable, - @Optional() public parentReg: CdkStickyRegion) { - this.element = _element.nativeElement; - this.upperScrollableContainer = scrollable.getElementRef().nativeElement; - this.scrollableRegion = scrollable.getElementRef().nativeElement; - } - - ngAfterViewInit(): void { - this.stickyParent = this.parentReg != null ? - this.parentReg._elementRef.nativeElement : this.element.parentElement; - this.originalCss = { - zIndex: this.getCssValue(this.element, 'zIndex'), - position: this.getCssValue(this.element, 'position'), - top: this.getCssValue(this.element, 'top'), - right: this.getCssValue(this.element, 'right'), - left: this.getCssValue(this.element, 'left'), - bottom: this.getCssValue(this.element, 'bottom'), - width: this.getCssValue(this.element, 'width'), - }; - this.attach(); - this.defineRestrictionsAndStick(); - } - - ngOnDestroy(): void { - this.upperScrollableContainer.removeEventListener('scroll', this._onScrollBind); - this.upperScrollableContainer.removeEventListener('resize', this._onResizeBind); - this.upperScrollableContainer.removeEventListener('touchmove', this._onTouchMoveBind); - } - - attach() { - this.upperScrollableContainer.addEventListener('scroll', this._onScrollBind, false); - this.upperScrollableContainer.addEventListener('resize', this._onResizeBind, false); - - // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones - this.upperScrollableContainer.addEventListener('touchmove', this._onTouchMoveBind, false); - } - - onScroll(): void { - this.defineRestrictionsAndStick(); - } - - onTouchMove(): void { - this.defineRestrictionsAndStick(); - } - - onResize(): void { - this.defineRestrictionsAndStick(); - // If there's already a header being stick when the page is - // resized. The CSS style of the cdkStickyHeader element may be not fit - // the resized window. So we need to unstuck it then re-stick it. - // unstuck() can set 'isStuck' to FALSE. Then stickElement() can work. - if (this.isStuck) { - this.unstuckElement(); - this.stickElement(); - } - } - - /** - * define the restrictions of the sticky header(including stickyWidth, - * when to start, when to finish) - */ - defineRestrictions(): void { - if(this.stickyParent == null) { - return; - } - let containerTop: any = this.stickyParent.getBoundingClientRect(); - let elemHeight: number = this.element.offsetHeight; - let containerHeight: number = this.getCssNumber(this.stickyParent, 'height'); - this._containerStart = containerTop.top; - - // the padding of the element being sticked - let elementPadding: any = this.getCssValue(this.element, 'padding'); - - let paddingNumber: any = Number(elementPadding.slice(0, -2)); - this._scrollingWidth = this.upperScrollableContainer.clientWidth - - paddingNumber - paddingNumber; - - this._scrollFinish = this._containerStart + (containerHeight - elemHeight); - } - - /** - * Reset element to its original CSS - */ - resetElement(): void { - this.element.classList.remove(STICK_START_CLASS); - extendObject(this.element.style, this.originalCss); - } - - /** - * Stuck element, make the element stick to the top of the scrollable container. - */ - stickElement(): void { - this.isStuck = true; - - this.element.classList.remove(STICK_END_CLASS); - this.element.classList.add(STICK_START_CLASS); - - /** - * Have to add the translate3d function for the sticky element's css style. - * Because iPhone and iPad's browser is using its owning rendering engine. And - * even if you are using Chrome on an iPhone, you are just using Safari with - * a Chrome skin around it. - * - * Safari on iPad and Safari on iPhone do not have resizable windows. - * In Safari on iPhone and iPad, the window size is set to the size of - * the screen (minus Safari user interface controls), and cannot be changed - * by the user. To move around a webpage, the user changes the zoom level and position - * of the viewport as they double tap or pinch to zoom in or out, or by touching - * and dragging to pan the page. As a user changes the zoom level and position of the - * viewport they are doing so within a viewable content area of fixed size - * (that is, the window). This means that webpage elements that have their position - * "fixed" to the viewport can end up outside the viewable content area, offscreen. - * - * So the 'position: fixed' does not work on iPhone and iPad. To make it work, - * 'translate3d(0,0,0)' needs to be used to force Safari re-rendering the sticky element. - **/ - this.element.style.transform = 'translate3d(0px,0px,0px)'; - - let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right; - - let stickyCss:any = { - zIndex: this.zIndex, - position: 'fixed', - top: this.upperScrollableContainer.offsetTop + 'px', - right: stuckRight + 'px', - left: this.upperScrollableContainer.offsetLeft + 'px', - bottom: 'auto', - width: this._scrollingWidth + 'px', - }; - extendObject(this.element.style, stickyCss); - } - - /** - * Unstuck element: When an element reaches the bottom of its cdkStickyRegion, - * It should be unstuck. And its position will be set as 'relative', its bottom - * will be set as '0'. So it will be stick at the bottom of its cdkStickyRegion and - * will be scrolled up with its cdkStickyRegion element. In this way, the sticky header - * can be changed smoothly when two sticky header meet and the later one need to replace - * the former one. - */ - unstuckElement(): void { - this.isStuck = false; - - if(this.stickyParent == null) { - return; - } - - this.element.classList.add(STICK_END_CLASS); - this.stickyParent.style.position = 'relative'; - let unstuckCss: any = { - position: 'absolute', - top: 'auto', - right: '0', - left: 'auto', - bottom: '0', - width: this.originalCss.width, - }; - extendObject(this.element.style, unstuckCss); - } - - - /** - * 'sticker()' function contains the main logic of sticky-header. It decides when - * a header should be stick and when should it be unstuck. It will first get - * the offsetTop of the upper scrollable container. And then get the Start and End - * of the sticky-header's stickyRegion. - * The header will be stick if 'stickyRegion Start < container offsetTop < stickyRegion End'. - * And when 'stickyRegion End < container offsetTop', the header will be unstuck. It will be - * stick to the bottom of its stickyRegion container and being scrolled up with its stickyRegion - * container. - * When 'stickyRegion Start > container offsetTop', which means the header come back to the - * middle of the scrollable container, the header will be reset to its - * original CSS. - * A flag, isStuck. is used in this function. When a header is stick, isStuck = true. - * And when the 'isStuck' flag is TRUE, the sticky-header will not be repaint, which - * decreases the times on repainting sticky-header. - */ - sticker(): void { - let currentPosition: number = this.upperScrollableContainer.offsetTop; - - // unstuck when the element is scrolled out of the sticky region - if (this.isStuck && - (currentPosition < this._containerStart || currentPosition > this._scrollFinish) || - currentPosition >= this._scrollFinish) { - this.resetElement(); - if (currentPosition >= this._scrollFinish) { - this.unstuckElement(); - } - this.isStuck = false; // stick when the element is within the sticky region - } else if ( this.isStuck === false && - currentPosition > this._containerStart && currentPosition < this._scrollFinish) { - this.stickElement(); - } - } - - defineRestrictionsAndStick(): void { - this.defineRestrictions(); - this.sticker(); - } - - - private getCssValue(element: any, property: string): any { - let result: any = ''; - if (typeof window.getComputedStyle !== 'undefined') { - result = window.getComputedStyle(element, '').getPropertyValue(property); - } else if (typeof element.currentStyle !== 'undefined') { - result = element.currentStyle.property; - } - return result; - } - - private getCssNumber(element: any, property: string): number { - return parseInt(this.getCssValue(element, property), 10) || 0; - } -} diff --git a/src/lib/sticky-header/sticky-header.ts b/src/lib/sticky-header/sticky-header.ts index 2606cf439e51..1c520ed7c8df 100644 --- a/src/lib/sticky-header/sticky-header.ts +++ b/src/lib/sticky-header/sticky-header.ts @@ -5,8 +5,9 @@ * 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 {Component, Directive, Input, Output, - OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; +import {Directive, Input, + OnDestroy, AfterViewInit, ElementRef, Optional} from '@angular/core'; +import {Platform} from '../core/platform'; import {Scrollable} from '../core/overlay/scroll/scrollable'; import {extendObject} from '../core/util/object-extend'; import {Subscription} from 'rxjs/Subscription'; diff --git a/src/lib/sticky-header/sticky-header.ts~HEAD_0 b/src/lib/sticky-header/sticky-header.ts~HEAD_0 new file mode 100644 index 000000000000..2606cf439e51 --- /dev/null +++ b/src/lib/sticky-header/sticky-header.ts~HEAD_0 @@ -0,0 +1,288 @@ +/** + * @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 {Component, Directive, Input, Output, + OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; +import {Scrollable} from '../core/overlay/scroll/scrollable'; +import {extendObject} from '../core/util/object-extend'; +import {Subscription} from 'rxjs/Subscription'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {RxChain, debounceTime} from '../core/rxjs/index'; +import {isPositionStickySupported} from '@angular/cdk'; + + +/** + * Directive that marks an element as a "sticky region", meant to contain exactly one sticky-header + * along with the content associated with that header. The sticky-header inside of the region will + * "stick" to the top of the scrolling container as long as this region is within the scrolling + * viewport. + * + * If a user does not explicitly define a sticky-region for a sticky-header, the direct + * parent node of the sticky-header will be used as the sticky-region. + */ +@Directive({ + selector: '[cdkStickyRegion]', +}) +export class CdkStickyRegion { + constructor(public readonly _elementRef: ElementRef) { } +} + + +/** Class applied when the header is "stuck" */ +const STICK_START_CLASS = 'cdk-sticky-header-start'; + +/** Class applied when the header is not "stuck" */ +const STICK_END_CLASS = 'cdk-sticky-header-end'; + +/** + * Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, + * touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the + * positioning adversely. + */ +const DEBOUNCE_TIME: number = 5; + +/** + * Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with + * cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky + * region (see cdkStickyRegion) is in view. + */ +@Directive({ + selector: '[cdkStickyHeader]', +}) +export class CdkStickyHeader implements OnDestroy, AfterViewInit { + + /** z-index to be applied to the sticky header (default is 10). */ + @Input('cdkStickyHeaderZIndex') zIndex: number = 10; + + /** boolean value to mark whether the current header is stuck*/ + isStuck: boolean = false; + /** Whether the browser support CSS sticky positioning. */ + private _isPositionStickySupported: boolean = false; + + /** The element with the 'cdkStickyHeader' tag. */ + element: HTMLElement; + /** The upper container element with the 'cdkStickyRegion' tag. */ + stickyParent: HTMLElement | null; + /** The upper scrollable container. */ + upperScrollableContainer: HTMLElement; + /** + * The original css of the sticky element, used to reset the sticky element + * when it is being unstuck + */ + private _originalStyles = {} as CSSStyleDeclaration; + /** + * 'getBoundingClientRect().top' of CdkStickyRegion of current sticky header. + * It is used with '_stickyRegionBottomThreshold' to judge whether the current header + * need to be stuck. + */ + private _stickyRegionTop: number; + /** + * Bottom of the sticky region offset by the height of the sticky header. + * Once the sticky header is scrolled to this position it will stay in place + * so that it will scroll naturally out of view with the rest of the sticky region. + */ + private _stickyRegionBottomThreshold: number; + + private _onScrollSubscription: Subscription; + + private _onTouchSubscription: Subscription; + + private _onResizeSubscription: Subscription; + + constructor(element: ElementRef, + scrollable: Scrollable, + @Optional() public parentRegion: CdkStickyRegion, + platform: Platform) { + if (platform.isBrowser) { + this.element = element.nativeElement; + this.upperScrollableContainer = scrollable.getElementRef().nativeElement; + this._setStrategyAccordingToCompatibility(); + } + } + + ngAfterViewInit(): void { + if (!this._isPositionStickySupported) { + + this.stickyParent = this.parentRegion != null ? + this.parentRegion._elementRef.nativeElement : this.element.parentElement; + + let headerStyles = window.getComputedStyle(this.element, ''); + this._originalStyles = { + position: headerStyles.position, + top: headerStyles.top, + right: headerStyles.right, + left: headerStyles.left, + bottom: headerStyles.bottom, + width: headerStyles.width, + zIndex: headerStyles.zIndex + } as CSSStyleDeclaration; + + this._attachEventListeners(); + this._updateStickyPositioning(); + } + } + + ngOnDestroy(): void { + [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription] + .forEach(s => s && s.unsubscribe()); + } + + /** + * Check if current browser supports sticky positioning. If yes, apply + * sticky positioning. If not, use the original implementation. + */ + private _setStrategyAccordingToCompatibility(): void { + this._isPositionStickySupported = isPositionStickySupported(); + if (this._isPositionStickySupported) { + this.element.style.top = '0'; + this.element.style.cssText += 'position: -webkit-sticky; position: sticky; '; + // TODO(sllethe): add css class with both 'sticky' and '-webkit-sticky' on position + // when @Directory supports adding CSS class + } + } + + /** Add listeners for events that affect sticky positioning. */ + private _attachEventListeners() { + this._onScrollSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'scroll')) + .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); + + // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones + this._onTouchSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'touchmove')) + .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); + + this._onResizeSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'resize')) + .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this.onResize()); + } + + onResize(): void { + this._updateStickyPositioning(); + // If there's already a header being stick when the page is + // resized. The CSS style of the cdkStickyHeader element may be not fit + // the resized window. So we need to unstuck it then re-stick it. + // unstuck() can set 'isStuck' to FALSE. Then _stickElement() can work. + if (this.isStuck) { + this._unstickElement(); + this._stickElement(); + } + } + + /** Measures the boundaries of the sticky regions to be used in subsequent positioning. */ + private _measureStickyRegionBounds(): void { + if (!this.stickyParent) { + return; + } + const boundingClientRect: any = this.stickyParent.getBoundingClientRect(); + this._stickyRegionTop = boundingClientRect.top; + let stickRegionHeight = boundingClientRect.height; + + this._stickyRegionBottomThreshold = this._stickyRegionTop + + (stickRegionHeight - this.element.offsetHeight); + } + + /** Reset element to its original CSS. */ + private _resetElementStyles(): void { + this.element.classList.remove(STICK_START_CLASS); + extendObject(this.element.style, this._originalStyles); + } + + /** Stuck element, make the element stick to the top of the scrollable container. */ + private _stickElement(): void { + this.isStuck = true; + + this.element.classList.remove(STICK_END_CLASS); + this.element.classList.add(STICK_START_CLASS); + + // Have to add the translate3d function for the sticky element's css style. + // Because iPhone and iPad's browser is using its owning rendering engine. And + // even if you are using Chrome on an iPhone, you are just using Safari with + // a Chrome skin around it. + // + // Safari on iPad and Safari on iPhone do not have resizable windows. + // In Safari on iPhone and iPad, the window size is set to the size of + // the screen (minus Safari user interface controls), and cannot be changed + // by the user. To move around a webpage, the user changes the zoom level and position + // of the viewport as they double tap or pinch to zoom in or out, or by touching + // and dragging to pan the page. As a user changes the zoom level and position of the + // viewport they are doing so within a viewable content area of fixed size + // (that is, the window). This means that webpage elements that have their position + // "fixed" to the viewport can end up outside the viewable content area, offscreen. + // + // So the 'position: fixed' does not work on iPhone and iPad. To make it work, + // 'translate3d(0,0,0)' needs to be used to force Safari re-rendering the sticky element. + this.element.style.transform = 'translate3d(0px,0px,0px)'; + + let stuckRight: number = this.upperScrollableContainer.getBoundingClientRect().right; + + let stickyCss = { + position: 'fixed', + top: this.upperScrollableContainer.offsetTop + 'px', + right: stuckRight + 'px', + left: this.upperScrollableContainer.offsetLeft + 'px', + bottom: 'auto', + width: this._originalStyles.width, + zIndex: this.zIndex + '' + }; + extendObject(this.element.style, stickyCss); + } + + /** + * Unsticks the header so that it goes back to scrolling normally. + * + * This should be called when the element reaches the bottom of its cdkStickyRegion so that it + * smoothly scrolls out of view as the next sticky-header moves in. + */ + private _unstickElement(): void { + this.isStuck = false; + + if (!this.stickyParent) { + return; + } + + this.element.classList.add(STICK_END_CLASS); + this.stickyParent.style.position = 'relative'; + let unstuckCss = { + position: 'absolute', + top: 'auto', + right: '0', + left: 'auto', + bottom: '0', + width: this._originalStyles.width + }; + extendObject(this.element.style, unstuckCss); + } + + + /** + * '_applyStickyPositionStyles()' function contains the main logic of sticky-header. It decides when + * a header should be stick and when should it be unstuck by comparing the offsetTop + * of scrollable container with the top and bottom of the sticky region. + */ + _applyStickyPositionStyles(): void { + let currentPosition: number = this.upperScrollableContainer.offsetTop; + + // unstuck when the element is scrolled out of the sticky region + if (this.isStuck && + (currentPosition < this._stickyRegionTop || + currentPosition > this._stickyRegionBottomThreshold) || + currentPosition >= this._stickyRegionBottomThreshold) { + this._resetElementStyles(); + if (currentPosition >= this._stickyRegionBottomThreshold) { + this._unstickElement(); + } + this.isStuck = false; // stick when the element is within the sticky region + } else if ( this.isStuck === false && + currentPosition > this._stickyRegionTop && + currentPosition < this._stickyRegionBottomThreshold) { + this._stickElement(); + } + } + + _updateStickyPositioning(): void { + this._measureStickyRegionBounds(); + this._applyStickyPositionStyles(); + } +} From 983b0f08bc0290b8b90dc125b200b563543485a8 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 10:37:59 -0700 Subject: [PATCH 04/16] Add demo to sticky-header --- src/demo-app/demo-app-module.ts | 1 + src/demo-app/demo-app/routes.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index 077acc830146..0563f49cb234 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -81,6 +81,7 @@ import { } from '@angular/material'; import {CdkTableModule} from '@angular/cdk'; import {TableHeaderDemo} from './table/table-header-demo'; +import {StickyHeaderDemo} from './sticky-header/sticky-header-demo'; /** * NgModule that includes all Material modules that are required to serve the demo-app. diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts index 64615b68f397..fc285e69d9d2 100644 --- a/src/demo-app/demo-app/routes.ts +++ b/src/demo-app/demo-app/routes.ts @@ -36,6 +36,7 @@ import {DatepickerDemo} from '../datepicker/datepicker-demo'; import {TableDemo} from '../table/table-demo'; import {TypographyDemo} from '../typography/typography-demo'; import {ExpansionDemo} from '../expansion/expansion-demo'; +import {StickyHeaderDemo} from '../sticky-header/sticky-header-demo'; export const DEMO_APP_ROUTES: Routes = [ {path: '', component: Home}, From 352f87be196c6f90bf25d6d1d81ae8d1ed81221c Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 13:11:46 -0700 Subject: [PATCH 05/16] revert --- src/lib/sticky-header/sticky-header.ts~HEAD | 289 -------------------- 1 file changed, 289 deletions(-) delete mode 100644 src/lib/sticky-header/sticky-header.ts~HEAD diff --git a/src/lib/sticky-header/sticky-header.ts~HEAD b/src/lib/sticky-header/sticky-header.ts~HEAD deleted file mode 100644 index 1c520ed7c8df..000000000000 --- a/src/lib/sticky-header/sticky-header.ts~HEAD +++ /dev/null @@ -1,289 +0,0 @@ -/** - * @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 {Directive, Input, - OnDestroy, AfterViewInit, ElementRef, Optional} from '@angular/core'; -import {Platform} from '../core/platform'; -import {Scrollable} from '../core/overlay/scroll/scrollable'; -import {extendObject} from '../core/util/object-extend'; -import {Subscription} from 'rxjs/Subscription'; -import {fromEvent} from 'rxjs/observable/fromEvent'; -import {RxChain, debounceTime} from '../core/rxjs/index'; -import {isPositionStickySupported} from '@angular/cdk'; - - -/** - * Directive that marks an element as a "sticky region", meant to contain exactly one sticky-header - * along with the content associated with that header. The sticky-header inside of the region will - * "stick" to the top of the scrolling container as long as this region is within the scrolling - * viewport. - * - * If a user does not explicitly define a sticky-region for a sticky-header, the direct - * parent node of the sticky-header will be used as the sticky-region. - */ -@Directive({ - selector: '[cdkStickyRegion]', -}) -export class CdkStickyRegion { - constructor(public readonly _elementRef: ElementRef) { } -} - - -/** Class applied when the header is "stuck" */ -const STICK_START_CLASS = 'cdk-sticky-header-start'; - -/** Class applied when the header is not "stuck" */ -const STICK_END_CLASS = 'cdk-sticky-header-end'; - -/** - * Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, - * touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the - * positioning adversely. - */ -const DEBOUNCE_TIME: number = 5; - -/** - * Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with - * cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky - * region (see cdkStickyRegion) is in view. - */ -@Directive({ - selector: '[cdkStickyHeader]', -}) -export class CdkStickyHeader implements OnDestroy, AfterViewInit { - - /** z-index to be applied to the sticky header (default is 10). */ - @Input('cdkStickyHeaderZIndex') zIndex: number = 10; - - /** boolean value to mark whether the current header is stuck*/ - isStuck: boolean = false; - /** Whether the browser support CSS sticky positioning. */ - private _isPositionStickySupported: boolean = false; - - /** The element with the 'cdkStickyHeader' tag. */ - element: HTMLElement; - /** The upper container element with the 'cdkStickyRegion' tag. */ - stickyParent: HTMLElement | null; - /** The upper scrollable container. */ - upperScrollableContainer: HTMLElement; - /** - * The original css of the sticky element, used to reset the sticky element - * when it is being unstuck - */ - private _originalStyles = {} as CSSStyleDeclaration; - /** - * 'getBoundingClientRect().top' of CdkStickyRegion of current sticky header. - * It is used with '_stickyRegionBottomThreshold' to judge whether the current header - * need to be stuck. - */ - private _stickyRegionTop: number; - /** - * Bottom of the sticky region offset by the height of the sticky header. - * Once the sticky header is scrolled to this position it will stay in place - * so that it will scroll naturally out of view with the rest of the sticky region. - */ - private _stickyRegionBottomThreshold: number; - - private _onScrollSubscription: Subscription; - - private _onTouchSubscription: Subscription; - - private _onResizeSubscription: Subscription; - - constructor(element: ElementRef, - scrollable: Scrollable, - @Optional() public parentRegion: CdkStickyRegion, - platform: Platform) { - if (platform.isBrowser) { - this.element = element.nativeElement; - this.upperScrollableContainer = scrollable.getElementRef().nativeElement; - this._setStrategyAccordingToCompatibility(); - } - } - - ngAfterViewInit(): void { - if (!this._isPositionStickySupported) { - - this.stickyParent = this.parentRegion != null ? - this.parentRegion._elementRef.nativeElement : this.element.parentElement; - - let headerStyles = window.getComputedStyle(this.element, ''); - this._originalStyles = { - position: headerStyles.position, - top: headerStyles.top, - right: headerStyles.right, - left: headerStyles.left, - bottom: headerStyles.bottom, - width: headerStyles.width, - zIndex: headerStyles.zIndex - } as CSSStyleDeclaration; - - this._attachEventListeners(); - this._updateStickyPositioning(); - } - } - - ngOnDestroy(): void { - [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription] - .forEach(s => s && s.unsubscribe()); - } - - /** - * Check if current browser supports sticky positioning. If yes, apply - * sticky positioning. If not, use the original implementation. - */ - private _setStrategyAccordingToCompatibility(): void { - this._isPositionStickySupported = isPositionStickySupported(); - if (this._isPositionStickySupported) { - this.element.style.top = '0'; - this.element.style.cssText += 'position: -webkit-sticky; position: sticky; '; - // TODO(sllethe): add css class with both 'sticky' and '-webkit-sticky' on position - // when @Directory supports adding CSS class - } - } - - /** Add listeners for events that affect sticky positioning. */ - private _attachEventListeners() { - this._onScrollSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'scroll')) - .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); - - // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones - this._onTouchSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'touchmove')) - .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); - - this._onResizeSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'resize')) - .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this.onResize()); - } - - onResize(): void { - this._updateStickyPositioning(); - // If there's already a header being stick when the page is - // resized. The CSS style of the cdkStickyHeader element may be not fit - // the resized window. So we need to unstuck it then re-stick it. - // unstuck() can set 'isStuck' to FALSE. Then _stickElement() can work. - if (this.isStuck) { - this._unstickElement(); - this._stickElement(); - } - } - - /** Measures the boundaries of the sticky regions to be used in subsequent positioning. */ - private _measureStickyRegionBounds(): void { - if (!this.stickyParent) { - return; - } - const boundingClientRect: any = this.stickyParent.getBoundingClientRect(); - this._stickyRegionTop = boundingClientRect.top; - let stickRegionHeight = boundingClientRect.height; - - this._stickyRegionBottomThreshold = this._stickyRegionTop + - (stickRegionHeight - this.element.offsetHeight); - } - - /** Reset element to its original CSS. */ - private _resetElementStyles(): void { - this.element.classList.remove(STICK_START_CLASS); - extendObject(this.element.style, this._originalStyles); - } - - /** Stuck element, make the element stick to the top of the scrollable container. */ - private _stickElement(): void { - this.isStuck = true; - - this.element.classList.remove(STICK_END_CLASS); - this.element.classList.add(STICK_START_CLASS); - - // Have to add the translate3d function for the sticky element's css style. - // Because iPhone and iPad's browser is using its owning rendering engine. And - // even if you are using Chrome on an iPhone, you are just using Safari with - // a Chrome skin around it. - // - // Safari on iPad and Safari on iPhone do not have resizable windows. - // In Safari on iPhone and iPad, the window size is set to the size of - // the screen (minus Safari user interface controls), and cannot be changed - // by the user. To move around a webpage, the user changes the zoom level and position - // of the viewport as they double tap or pinch to zoom in or out, or by touching - // and dragging to pan the page. As a user changes the zoom level and position of the - // viewport they are doing so within a viewable content area of fixed size - // (that is, the window). This means that webpage elements that have their position - // "fixed" to the viewport can end up outside the viewable content area, offscreen. - // - // So the 'position: fixed' does not work on iPhone and iPad. To make it work, - // 'translate3d(0,0,0)' needs to be used to force Safari re-rendering the sticky element. - this.element.style.transform = 'translate3d(0px,0px,0px)'; - - let stuckRight: number = this.upperScrollableContainer.getBoundingClientRect().right; - - let stickyCss = { - position: 'fixed', - top: this.upperScrollableContainer.offsetTop + 'px', - right: stuckRight + 'px', - left: this.upperScrollableContainer.offsetLeft + 'px', - bottom: 'auto', - width: this._originalStyles.width, - zIndex: this.zIndex + '' - }; - extendObject(this.element.style, stickyCss); - } - - /** - * Unsticks the header so that it goes back to scrolling normally. - * - * This should be called when the element reaches the bottom of its cdkStickyRegion so that it - * smoothly scrolls out of view as the next sticky-header moves in. - */ - private _unstickElement(): void { - this.isStuck = false; - - if (!this.stickyParent) { - return; - } - - this.element.classList.add(STICK_END_CLASS); - this.stickyParent.style.position = 'relative'; - let unstuckCss = { - position: 'absolute', - top: 'auto', - right: '0', - left: 'auto', - bottom: '0', - width: this._originalStyles.width - }; - extendObject(this.element.style, unstuckCss); - } - - - /** - * '_applyStickyPositionStyles()' function contains the main logic of sticky-header. It decides when - * a header should be stick and when should it be unstuck by comparing the offsetTop - * of scrollable container with the top and bottom of the sticky region. - */ - _applyStickyPositionStyles(): void { - let currentPosition: number = this.upperScrollableContainer.offsetTop; - - // unstuck when the element is scrolled out of the sticky region - if (this.isStuck && - (currentPosition < this._stickyRegionTop || - currentPosition > this._stickyRegionBottomThreshold) || - currentPosition >= this._stickyRegionBottomThreshold) { - this._resetElementStyles(); - if (currentPosition >= this._stickyRegionBottomThreshold) { - this._unstickElement(); - } - this.isStuck = false; // stick when the element is within the sticky region - } else if ( this.isStuck === false && - currentPosition > this._stickyRegionTop && - currentPosition < this._stickyRegionBottomThreshold) { - this._stickElement(); - } - } - - _updateStickyPositioning(): void { - this._measureStickyRegionBounds(); - this._applyStickyPositionStyles(); - } -} From a3c1845794df146ae53e906b09a31b19033678eb Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 13:55:16 -0700 Subject: [PATCH 06/16] deleted HEAD file --- src/lib/sticky-header/sticky-header.ts~HEAD_0 | 288 ------------------ 1 file changed, 288 deletions(-) delete mode 100644 src/lib/sticky-header/sticky-header.ts~HEAD_0 diff --git a/src/lib/sticky-header/sticky-header.ts~HEAD_0 b/src/lib/sticky-header/sticky-header.ts~HEAD_0 deleted file mode 100644 index 2606cf439e51..000000000000 --- a/src/lib/sticky-header/sticky-header.ts~HEAD_0 +++ /dev/null @@ -1,288 +0,0 @@ -/** - * @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 {Component, Directive, Input, Output, - OnDestroy, AfterViewInit, ElementRef, Injectable, Optional} from '@angular/core'; -import {Scrollable} from '../core/overlay/scroll/scrollable'; -import {extendObject} from '../core/util/object-extend'; -import {Subscription} from 'rxjs/Subscription'; -import {fromEvent} from 'rxjs/observable/fromEvent'; -import {RxChain, debounceTime} from '../core/rxjs/index'; -import {isPositionStickySupported} from '@angular/cdk'; - - -/** - * Directive that marks an element as a "sticky region", meant to contain exactly one sticky-header - * along with the content associated with that header. The sticky-header inside of the region will - * "stick" to the top of the scrolling container as long as this region is within the scrolling - * viewport. - * - * If a user does not explicitly define a sticky-region for a sticky-header, the direct - * parent node of the sticky-header will be used as the sticky-region. - */ -@Directive({ - selector: '[cdkStickyRegion]', -}) -export class CdkStickyRegion { - constructor(public readonly _elementRef: ElementRef) { } -} - - -/** Class applied when the header is "stuck" */ -const STICK_START_CLASS = 'cdk-sticky-header-start'; - -/** Class applied when the header is not "stuck" */ -const STICK_END_CLASS = 'cdk-sticky-header-end'; - -/** - * Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, - * touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the - * positioning adversely. - */ -const DEBOUNCE_TIME: number = 5; - -/** - * Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with - * cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky - * region (see cdkStickyRegion) is in view. - */ -@Directive({ - selector: '[cdkStickyHeader]', -}) -export class CdkStickyHeader implements OnDestroy, AfterViewInit { - - /** z-index to be applied to the sticky header (default is 10). */ - @Input('cdkStickyHeaderZIndex') zIndex: number = 10; - - /** boolean value to mark whether the current header is stuck*/ - isStuck: boolean = false; - /** Whether the browser support CSS sticky positioning. */ - private _isPositionStickySupported: boolean = false; - - /** The element with the 'cdkStickyHeader' tag. */ - element: HTMLElement; - /** The upper container element with the 'cdkStickyRegion' tag. */ - stickyParent: HTMLElement | null; - /** The upper scrollable container. */ - upperScrollableContainer: HTMLElement; - /** - * The original css of the sticky element, used to reset the sticky element - * when it is being unstuck - */ - private _originalStyles = {} as CSSStyleDeclaration; - /** - * 'getBoundingClientRect().top' of CdkStickyRegion of current sticky header. - * It is used with '_stickyRegionBottomThreshold' to judge whether the current header - * need to be stuck. - */ - private _stickyRegionTop: number; - /** - * Bottom of the sticky region offset by the height of the sticky header. - * Once the sticky header is scrolled to this position it will stay in place - * so that it will scroll naturally out of view with the rest of the sticky region. - */ - private _stickyRegionBottomThreshold: number; - - private _onScrollSubscription: Subscription; - - private _onTouchSubscription: Subscription; - - private _onResizeSubscription: Subscription; - - constructor(element: ElementRef, - scrollable: Scrollable, - @Optional() public parentRegion: CdkStickyRegion, - platform: Platform) { - if (platform.isBrowser) { - this.element = element.nativeElement; - this.upperScrollableContainer = scrollable.getElementRef().nativeElement; - this._setStrategyAccordingToCompatibility(); - } - } - - ngAfterViewInit(): void { - if (!this._isPositionStickySupported) { - - this.stickyParent = this.parentRegion != null ? - this.parentRegion._elementRef.nativeElement : this.element.parentElement; - - let headerStyles = window.getComputedStyle(this.element, ''); - this._originalStyles = { - position: headerStyles.position, - top: headerStyles.top, - right: headerStyles.right, - left: headerStyles.left, - bottom: headerStyles.bottom, - width: headerStyles.width, - zIndex: headerStyles.zIndex - } as CSSStyleDeclaration; - - this._attachEventListeners(); - this._updateStickyPositioning(); - } - } - - ngOnDestroy(): void { - [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription] - .forEach(s => s && s.unsubscribe()); - } - - /** - * Check if current browser supports sticky positioning. If yes, apply - * sticky positioning. If not, use the original implementation. - */ - private _setStrategyAccordingToCompatibility(): void { - this._isPositionStickySupported = isPositionStickySupported(); - if (this._isPositionStickySupported) { - this.element.style.top = '0'; - this.element.style.cssText += 'position: -webkit-sticky; position: sticky; '; - // TODO(sllethe): add css class with both 'sticky' and '-webkit-sticky' on position - // when @Directory supports adding CSS class - } - } - - /** Add listeners for events that affect sticky positioning. */ - private _attachEventListeners() { - this._onScrollSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'scroll')) - .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); - - // Have to add a 'onTouchMove' listener to make sticky header work on mobile phones - this._onTouchSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'touchmove')) - .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this._updateStickyPositioning()); - - this._onResizeSubscription = RxChain.from(fromEvent(this.upperScrollableContainer, 'resize')) - .call(debounceTime, DEBOUNCE_TIME).subscribe(() => this.onResize()); - } - - onResize(): void { - this._updateStickyPositioning(); - // If there's already a header being stick when the page is - // resized. The CSS style of the cdkStickyHeader element may be not fit - // the resized window. So we need to unstuck it then re-stick it. - // unstuck() can set 'isStuck' to FALSE. Then _stickElement() can work. - if (this.isStuck) { - this._unstickElement(); - this._stickElement(); - } - } - - /** Measures the boundaries of the sticky regions to be used in subsequent positioning. */ - private _measureStickyRegionBounds(): void { - if (!this.stickyParent) { - return; - } - const boundingClientRect: any = this.stickyParent.getBoundingClientRect(); - this._stickyRegionTop = boundingClientRect.top; - let stickRegionHeight = boundingClientRect.height; - - this._stickyRegionBottomThreshold = this._stickyRegionTop + - (stickRegionHeight - this.element.offsetHeight); - } - - /** Reset element to its original CSS. */ - private _resetElementStyles(): void { - this.element.classList.remove(STICK_START_CLASS); - extendObject(this.element.style, this._originalStyles); - } - - /** Stuck element, make the element stick to the top of the scrollable container. */ - private _stickElement(): void { - this.isStuck = true; - - this.element.classList.remove(STICK_END_CLASS); - this.element.classList.add(STICK_START_CLASS); - - // Have to add the translate3d function for the sticky element's css style. - // Because iPhone and iPad's browser is using its owning rendering engine. And - // even if you are using Chrome on an iPhone, you are just using Safari with - // a Chrome skin around it. - // - // Safari on iPad and Safari on iPhone do not have resizable windows. - // In Safari on iPhone and iPad, the window size is set to the size of - // the screen (minus Safari user interface controls), and cannot be changed - // by the user. To move around a webpage, the user changes the zoom level and position - // of the viewport as they double tap or pinch to zoom in or out, or by touching - // and dragging to pan the page. As a user changes the zoom level and position of the - // viewport they are doing so within a viewable content area of fixed size - // (that is, the window). This means that webpage elements that have their position - // "fixed" to the viewport can end up outside the viewable content area, offscreen. - // - // So the 'position: fixed' does not work on iPhone and iPad. To make it work, - // 'translate3d(0,0,0)' needs to be used to force Safari re-rendering the sticky element. - this.element.style.transform = 'translate3d(0px,0px,0px)'; - - let stuckRight: number = this.upperScrollableContainer.getBoundingClientRect().right; - - let stickyCss = { - position: 'fixed', - top: this.upperScrollableContainer.offsetTop + 'px', - right: stuckRight + 'px', - left: this.upperScrollableContainer.offsetLeft + 'px', - bottom: 'auto', - width: this._originalStyles.width, - zIndex: this.zIndex + '' - }; - extendObject(this.element.style, stickyCss); - } - - /** - * Unsticks the header so that it goes back to scrolling normally. - * - * This should be called when the element reaches the bottom of its cdkStickyRegion so that it - * smoothly scrolls out of view as the next sticky-header moves in. - */ - private _unstickElement(): void { - this.isStuck = false; - - if (!this.stickyParent) { - return; - } - - this.element.classList.add(STICK_END_CLASS); - this.stickyParent.style.position = 'relative'; - let unstuckCss = { - position: 'absolute', - top: 'auto', - right: '0', - left: 'auto', - bottom: '0', - width: this._originalStyles.width - }; - extendObject(this.element.style, unstuckCss); - } - - - /** - * '_applyStickyPositionStyles()' function contains the main logic of sticky-header. It decides when - * a header should be stick and when should it be unstuck by comparing the offsetTop - * of scrollable container with the top and bottom of the sticky region. - */ - _applyStickyPositionStyles(): void { - let currentPosition: number = this.upperScrollableContainer.offsetTop; - - // unstuck when the element is scrolled out of the sticky region - if (this.isStuck && - (currentPosition < this._stickyRegionTop || - currentPosition > this._stickyRegionBottomThreshold) || - currentPosition >= this._stickyRegionBottomThreshold) { - this._resetElementStyles(); - if (currentPosition >= this._stickyRegionBottomThreshold) { - this._unstickElement(); - } - this.isStuck = false; // stick when the element is within the sticky region - } else if ( this.isStuck === false && - currentPosition > this._stickyRegionTop && - currentPosition < this._stickyRegionBottomThreshold) { - this._stickElement(); - } - } - - _updateStickyPositioning(): void { - this._measureStickyRegionBounds(); - this._applyStickyPositionStyles(); - } -} From 7945a22e48e6507d57c1100a5abbc14e0a083396 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 14:22:04 -0700 Subject: [PATCH 07/16] removed unused 'id' --- src/demo-app/sticky-header/sticky-header-demo.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index d3099043d39b..1a3585dca396 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -4,7 +4,7 @@

Sticky-header Demo

-
+
From da065738eef0082c8378d35cf0c8389a07d28fe9 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 14:25:06 -0700 Subject: [PATCH 08/16] changed class name to a more meaningful name --- src/demo-app/sticky-header/sticky-header-demo.html | 6 +++--- src/demo-app/sticky-header/sticky-header-demo.scss | 12 +++--------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index 1a3585dca396..32090f6ae4fa 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -7,7 +7,7 @@

Sticky-header Demo

-
+

Heading 1

test sticky-header

@@ -27,7 +27,7 @@

Heading 1

-
+

Heading 2

test sticky-header

@@ -51,7 +51,7 @@

Heading 2

-
+

Heading 3

test sticky-header

diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index 3d33f404fc00..92590335efb4 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -63,25 +63,19 @@ -moz-appearance: none; } -.rside { - background: white; - -webkit-appearance: none; - padding: 20px; -} - -.rside1 { +.headerStyle1 { background: #cecece; -webkit-appearance: none; padding: 20px; } -.rside2 { +.headerStyle2 { background: #cea7a7; -webkit-appearance: none; padding: 20px; } -.rside3 { +.headerStyle3 { background: #c2eae5; -webkit-appearance: none; padding: 20px; From e43a250ccb7c20f14445bc103ba9d13de6d93609 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 14:26:24 -0700 Subject: [PATCH 09/16] removed unused CSS --- src/demo-app/sticky-header/sticky-header-demo.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index 92590335efb4..28a65d9df397 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -54,7 +54,6 @@ -moz-appearance: none; appearance: none; height: 500px; - //width: 900px; overflow: auto;} .container { From e47492b309d9a9c8e7ecdd770c200687a604b132 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 14:29:50 -0700 Subject: [PATCH 10/16] You have lots of duplicate of -webkit-appearance: none;padding: 20px;. Merge them into one --- .../sticky-header/sticky-header-demo.html | 6 +++--- .../sticky-header/sticky-header-demo.scss | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index 32090f6ae4fa..37042a2eeb1a 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -7,7 +7,7 @@

Sticky-header Demo

-
+

Heading 1

test sticky-header

@@ -27,7 +27,7 @@

Heading 1

-
+

Heading 2

test sticky-header

@@ -51,7 +51,7 @@

Heading 2

-
+

Heading 3

test sticky-header

diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index 28a65d9df397..9dec17cd5ef3 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -62,20 +62,19 @@ -moz-appearance: none; } -.headerStyle1 { - background: #cecece; +.headerStyle{ -webkit-appearance: none; padding: 20px; } -.headerStyle2 { +.headerColor1 { + background: #cecece; +} + +.headerColor2 { background: #cea7a7; - -webkit-appearance: none; - padding: 20px; } -.headerStyle3 { +.headerColor3 { background: #c2eae5; - -webkit-appearance: none; - padding: 20px; } From b9d8523602cfb74451272c0d4d4fc11e5c1d29b0 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Tue, 25 Jul 2017 14:32:45 -0700 Subject: [PATCH 11/16] nit --- src/demo-app/sticky-header/sticky-header-demo.html | 1 - 1 file changed, 1 deletion(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index 37042a2eeb1a..01f8ce625b4c 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -25,7 +25,6 @@

Heading 1

test sticky-header

-

Heading 2

From c15c8081768b941ff6e00144ce9d90ff1b29a6c1 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Thu, 27 Jul 2017 11:39:11 -0700 Subject: [PATCH 12/16] nit: use header-style and header-color1 --- src/demo-app/sticky-header/sticky-header-demo.html | 6 +++--- src/demo-app/sticky-header/sticky-header-demo.scss | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index 01f8ce625b4c..1c2288e49d6c 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -7,7 +7,7 @@

Sticky-header Demo

-
+

Heading 1

test sticky-header

@@ -26,7 +26,7 @@

Heading 1

-
+

Heading 2

test sticky-header

@@ -50,7 +50,7 @@

Heading 2

-
+

Heading 3

test sticky-header

diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index 9dec17cd5ef3..c550e5de5fed 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -62,19 +62,19 @@ -moz-appearance: none; } -.headerStyle{ +.header-style{ -webkit-appearance: none; padding: 20px; } -.headerColor1 { +.header-color1 { background: #cecece; } -.headerColor2 { +.header-color2 { background: #cea7a7; } -.headerColor3 { +.header-color3 { background: #c2eae5; } From d329aaadbb24af464d0935b4f98b9c1d26808d80 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Thu, 27 Jul 2017 11:53:47 -0700 Subject: [PATCH 13/16] very nit: add some different text so we can see the changes? --- .../sticky-header/sticky-header-demo.html | 130 ++++++++---------- .../sticky-header/sticky-header-demo.scss | 10 +- 2 files changed, 64 insertions(+), 76 deletions(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index 1c2288e49d6c..cbadb2a3e875 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -7,91 +7,71 @@

Sticky-header Demo

-
-

Heading 1

+
+

Unread Messages

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Looking

+

Chan: Forward

+

Chan: To It !

+

Min: Branch is this weekend?

+

Li: Yes

+

Eat: Green Peppers

+

Chan: Where?

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Where?

+

Eat: Green Peppers

-

Heading 2

+

Late Messages

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Looking

+

Chan: Forward

+

Chan: To It !

+

Min: Branch is this weekend?

+

Li: Yes

+

Eat: Green Peppers

+

Chan: Where?

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Where?

+

Eat: Green Peppers

+

Min: Branch is this weekend?

+

Li: Yes

-
-

Heading 3

+
+

Read Messages

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

-

test sticky-header

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Looking

+

Chan: Forward

+

Chan: To It !

+

Min: Branch is this weekend?

+

Li: Yes

+

Eat: Green Peppers

+

Chan: Where?

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Where?

+

Eat: Green Peppers

+

Min: Branch is this weekend?

+

Li: Yes

+

Min: Branch is this weekend?

+

Li: Yes

+

Chan: Looking

+

Chan: Forward

+

Chan: To It !

+

Min: Branch is this weekend?

+

Li: Yes

diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index c550e5de5fed..b0251604a13c 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -1,6 +1,6 @@ .demo-sticky { .centered { - text-align: center; + text-align: left; -webkit-appearance: none; -moz-appearance: none; height: 500px; @@ -24,6 +24,13 @@ } + h1 { + font-size: 20px; + padding: 20px; + -webkit-appearance: none; + -moz-appearance: none; + } + h2 { font-size: 20px; padding: 20px; @@ -32,6 +39,7 @@ } p { + text-indent: 4em; margin-bottom: 1.5em; -webkit-appearance: none; -moz-appearance: none; From 3f88ac960e202be008e9ea9aa6f47578dc7b64c7 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Mon, 31 Jul 2017 13:30:28 -0700 Subject: [PATCH 14/16] fix according to comments in pr --- src/demo-app/demo-app/demo-app.ts | 3 +- .../sticky-header/sticky-header-demo.html | 66 +++---------------- .../sticky-header/sticky-header-demo.scss | 20 ++---- .../sticky-header/sticky-header-demo.ts | 16 ++++- 4 files changed, 33 insertions(+), 72 deletions(-) diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts index 3f422c3ccd5c..7f57dcfc4aa2 100644 --- a/src/demo-app/demo-app/demo-app.ts +++ b/src/demo-app/demo-app/demo-app.ts @@ -67,7 +67,8 @@ export class DemoApp { {name: 'Tooltip', route: 'tooltip'}, {name: 'Platform', route: 'platform'}, {name: 'Style', route: 'style'}, - {name: 'Typography', route: 'typography'} + {name: 'Typography', route: 'typography'}, + {name: 'Sticky Header', route: 'sticky-header'} ]; constructor(private _element: ElementRef) { diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index cbadb2a3e875..41697b71936f 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -1,79 +1,33 @@
-
+

Sticky-header Demo

-
+
-
+

Unread Messages

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Looking

-

Chan: Forward

-

Chan: To It !

-

Min: Branch is this weekend?

-

Li: Yes

-

Eat: Green Peppers

-

Chan: Where?

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Where?

-

Eat: Green Peppers

+

{{item.name}} : {{item.messages}}

-
+

Late Messages

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Looking

-

Chan: Forward

-

Chan: To It !

-

Min: Branch is this weekend?

-

Li: Yes

-

Eat: Green Peppers

-

Chan: Where?

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Where?

-

Eat: Green Peppers

-

Min: Branch is this weekend?

-

Li: Yes

+

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}

-
+

Read Messages

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Looking

-

Chan: Forward

-

Chan: To It !

-

Min: Branch is this weekend?

-

Li: Yes

-

Eat: Green Peppers

-

Chan: Where?

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Where?

-

Eat: Green Peppers

-

Min: Branch is this weekend?

-

Li: Yes

-

Min: Branch is this weekend?

-

Li: Yes

-

Chan: Looking

-

Chan: Forward

-

Chan: To It !

-

Min: Branch is this weekend?

-

Li: Yes

+

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}

-
diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index b0251604a13c..3d7943c22480 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -1,5 +1,5 @@ .demo-sticky { - .centered { + .demo-centered { text-align: left; -webkit-appearance: none; -moz-appearance: none; @@ -56,33 +56,25 @@ display: block; } -.centered { - text-align: center; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - height: 500px; - overflow: auto;} - -.container { +.demo-container { margin: 0; -webkit-appearance: none; -moz-appearance: none; } -.header-style{ +.demo-header-style{ -webkit-appearance: none; padding: 20px; } -.header-color1 { +.demo-header-color1 { background: #cecece; } -.header-color2 { +.demo-header-color2 { background: #cea7a7; } -.header-color3 { +.demo-header-color3 { background: #c2eae5; } diff --git a/src/demo-app/sticky-header/sticky-header-demo.ts b/src/demo-app/sticky-header/sticky-header-demo.ts index 482c8e20128e..760ed1ee1ab0 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.ts +++ b/src/demo-app/sticky-header/sticky-header-demo.ts @@ -10,4 +10,18 @@ import {CdkStickyRegion, CdkStickyHeader} from '@angular/material'; styleUrls: ['sticky-header-demo.css'], }) -export class StickyHeaderDemo { } +export class StickyHeaderDemo { + items: any[] = [ + {name: 'Min', messages: 'Brunch is this weekend?'}, + {name: 'Li', messages: 'Yes'}, + {name: 'Chan', messages: 'Looking'}, + {name: 'Chan', messages: 'Forward'}, + {name: 'Chan', messages: 'To It !'}, + {name: 'Min', messages: 'Branch is this weekend?'}, + {name: 'Eat', messages: 'Green Peppers'}, + {name: 'Chan', messages: 'Where?'}, + {name: 'Jack', messages: 'Pirate!'}, + {name: 'Jone', messages: 'Black pearl'}, + {name: 'Jack', messages: 'Back to the sea!'}, + ]; +} From 7caff970c60f8aa1fd84ee929b6264cee917ba91 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Mon, 31 Jul 2017 15:30:40 -0700 Subject: [PATCH 15/16] fix white space between different
s --- src/demo-app/sticky-header/sticky-header-demo.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.scss b/src/demo-app/sticky-header/sticky-header-demo.scss index 3d7943c22480..cff291ea425f 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.scss +++ b/src/demo-app/sticky-header/sticky-header-demo.scss @@ -40,7 +40,7 @@ p { text-indent: 4em; - margin-bottom: 1.5em; + padding:15px; -webkit-appearance: none; -moz-appearance: none; } From 1dc7439ca9dc1cdbdd1f59ca6ccd7477280c0494 Mon Sep 17 00:00:00 2001 From: Lin Sun <2808411862@qq.com> Date: Mon, 31 Jul 2017 15:53:36 -0700 Subject: [PATCH 16/16] P -> p --- src/demo-app/sticky-header/sticky-header-demo.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/demo-app/sticky-header/sticky-header-demo.html b/src/demo-app/sticky-header/sticky-header-demo.html index 41697b71936f..2710f172590e 100644 --- a/src/demo-app/sticky-header/sticky-header-demo.html +++ b/src/demo-app/sticky-header/sticky-header-demo.html @@ -10,23 +10,23 @@

Sticky-header Demo

Unread Messages

-

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}

Late Messages

-

{{item.name}} : {{item.messages}}

-

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}

Read Messages

-

{{item.name}} : {{item.messages}}

-

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}

+

{{item.name}} : {{item.messages}}