mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
164 lines
4.9 KiB
TypeScript
164 lines
4.9 KiB
TypeScript
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
|
|
*/
|
|
@Directive({
|
|
selector: 'ion-item-group',
|
|
host: {
|
|
'class': 'item-group'
|
|
}
|
|
})
|
|
export class ItemGroup {
|
|
/**
|
|
* TODO
|
|
* @param {ElementRef} elementRef TODO
|
|
*/
|
|
constructor(elementRef: ElementRef) {
|
|
this.ele = elementRef.nativeElement;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
@Directive({
|
|
selector: 'ion-item-group-title',
|
|
host: {
|
|
'class': 'item-group-title',
|
|
'[class.sticky]': 'isSticky'
|
|
}
|
|
})
|
|
export class ItemGroupTitle {
|
|
/**
|
|
* TODO
|
|
* @param {ElementRef} elementRef TODO
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
}
|