diff --git a/ionic/components/item/item-group.ts b/ionic/components/item/item-group.ts index c3b8e80244..83abbd93c6 100644 --- a/ionic/components/item/item-group.ts +++ b/ionic/components/item/item-group.ts @@ -1,4 +1,8 @@ -import {Directive, ElementRef} from 'angular2/angular2'; +import {Directive, ElementRef, Host, Optional} from 'angular2/angular2'; +import {Content} from '../content/content'; +import {throttle} from '../../util/util'; +import {position, offset, CSS} from '../../util/dom'; +import {IonicConfig} from '../../config/config'; /** * TODO @@ -34,8 +38,126 @@ export class ItemGroupTitle { * TODO * @param {ElementRef} elementRef TODO */ - constructor(elementRef: ElementRef) { + constructor(elementRef: ElementRef, config: IonicConfig, @Host() content: Content) { this.isSticky = true; + this.content = content; this.ele = elementRef.nativeElement; + this.parent = this.ele.parentNode; + } + + onInit() { + + this.scrollContent = this.content.elementRef.nativeElement.children[0]; + + this.scrollMin = 0; + this.scrollMax = 0; + this.scrollTransition = 0; + this.isSticking = false; + + + this.scrollContent.addEventListener('scroll', event => this.scrollEvent(event)); + + this.calculateScrollLimits = scrollTop => { + var containerPosition = position(this.parent); + var elementOffset = offset(this.ele); + + var containerTop = containerPosition.top; + var containerHeight = containerPosition.height; + + var affixHeight = elementOffset.height; + + this.scrollMin = containerTop; + this.scrollMax = this.scrollMin + containerHeight; + this.scrollTransition = this.scrollMax - affixHeight; + }; + + // throttled version of the same calculation + let CALCULATION_THROTTLE_MS = 500; + this.throttledCalculateScrollLimits = throttle( + this.calculateScrollLimits, + CALCULATION_THROTTLE_MS, + {trailing: false} + ); + } + + applyTransform(element, transformString) { + // do not apply the transformation if it is already applied + if (element.style[CSS.transform] == transformString) { + } + else { + element.style[CSS.transform] = transformString; + } + } + + translateUp(element, dy, executeImmediately) { + var translateDyPixelsUp = dy == 0 ? 'translate3d(0px, 0px, 0px)' : 'translate3d(0px, -' + dy + 'px, 0px)'; + // if immediate execution is requested, then just execute immediately + // if not, execute in the animation frame. + if (executeImmediately) { + this.applyTransform(element, translateDyPixelsUp); + } + else { + // see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ + // see http://ionicframework.com/docs/api/utility/ionic.DomUtil/ + requestAnimationFrame( a => this.applyTransform(element, translateDyPixelsUp) ); + } + } + + createAffixClone() { + var clone = this.ele.cloneNode(true); + clone.style.position = 'absolute'; + clone.style.top = 0; + clone.style.left = 0; + clone.style.right = 0; + + this.scrollContent.parentNode.appendChild(clone); + return clone; + } + + scrollEvent(event) { + var scrollTop = event.target.scrollTop; + + // when scroll to top, we should always execute the immediate calculation. + // this is because of some weird problem which is hard to describe. + // if you want to experiment, always use the throttled one and just click on the page + // you will see all affix elements stacked on top + if (scrollTop == 0) { + this.calculateScrollLimits(scrollTop); + } + else { + this.throttledCalculateScrollLimits(scrollTop); + } + + // when we scrolled to the container, create the clone of element and place it on top + if (scrollTop >= this.scrollMin && scrollTop <= this.scrollMax) { + // we need to track if we created the clone just now + // that is important since normally we apply the transforms in the animation frame + // but, we need to apply the transform immediately when we add the element for the first time. otherwise it is too late! + var cloneCreatedJustNow = false; + + if (!this.affixClone) { + this.affixClone = this.createAffixClone(); + cloneCreatedJustNow = true; + this.isSticking = true; + } + + // if we're reaching towards the end of the container, apply some nice translation to move up/down the clone + // but if we're reached already to the container and we're far away than the end, move clone to top + if (scrollTop > this.scrollTransition) { + this.translateUp(this.affixClone, Math.floor(scrollTop - this.scrollTransition), cloneCreatedJustNow); + } else { + this.translateUp(this.affixClone, 0, cloneCreatedJustNow); + } + } else { + this.removeAffixClone(); + this.isSticking = false; + } + } + + removeAffixClone() { + if (this.affixClone) { + this.scrollContent.parentNode.removeChild(this.affixClone); + this.affixClone = null; + } } } diff --git a/ionic/components/item/modes/ios.scss b/ionic/components/item/modes/ios.scss index 1c102a7e4d..357731011e 100644 --- a/ionic/components/item/modes/ios.scss +++ b/ionic/components/item/modes/ios.scss @@ -26,14 +26,16 @@ $item-ios-divider-bg: #f5f5f5 !default; $item-ios-divider-color: #222 !default; $item-ios-divider-padding: 5px 15px !default; +.item-group-title { + padding: $item-ios-padding-top $item-ios-padding-right $item-ios-padding-bottom $item-ios-padding-left; + background-color: $item-ios-divider-bg; + color: $item-ios-divider-color; +} + .list { - .item-group-title { - padding: $item-ios-padding-top $item-ios-padding-right $item-ios-padding-bottom $item-ios-padding-left; - background-color: $item-ios-divider-bg; - color: $item-ios-divider-color; - } + .item-group { // Make sure the first and last items don't have borders > .item:first-of-type:before { diff --git a/ionic/components/list/test/infinite/main.html b/ionic/components/list/test/infinite/main.html index f7ce1dc08b..d6f233e191 100644 --- a/ionic/components/list/test/infinite/main.html +++ b/ionic/components/list/test/infinite/main.html @@ -1,15 +1,10 @@ + - + + + {{item.title}} + + + - - - - - {{item.title}} - - - - - - - + diff --git a/ionic/components/scroll/test/basic/main.html b/ionic/components/scroll/test/basic/main.html index 849a3fa850..3637a85aae 100644 --- a/ionic/components/scroll/test/basic/main.html +++ b/ionic/components/scroll/test/basic/main.html @@ -1,4 +1,4 @@ - + Scroll @@ -20,7 +20,7 @@ - +