From dfdcf2003de67b9832e38b84b093d46fa40467d3 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 26 Apr 2017 15:26:25 -0500 Subject: [PATCH] fix(build): update angular build process --- scripts/gulp/tasks/core.ts | 17 +- scripts/gulp/tasks/e2e.dev.ts | 2 +- scripts/gulp/tasks/e2e.prod.ts | 4 +- src/components/badge/badge.ts | 2 +- src/components/card/card-content.scss | 2 + src/components/card/card-content.ts | 2 +- src/components/card/card-header.scss | 2 + src/components/card/card-header.ts | 2 +- src/components/card/card-title.ts | 2 +- src/components/card/card.ios.scss | 2 + src/components/card/card.md.scss | 2 + src/components/card/card.scss | 2 + src/components/card/card.ts | 2 +- src/components/card/card.wp.scss | 2 + src/components/gesture/gesture.ts | 39 +- src/components/index.ts | 15 + src/components/scroll/scroll-interface.ts | 14 + src/components/scroll/scroll.scss | 34 -- src/components/scroll/scroll.ts | 420 ++++++++++++++---- .../scroll/test/basic/app.module.ts | 28 -- .../scroll/test/basic/eight_horns.png | Bin 2493 -> 0 bytes src/components/scroll/test/basic/main.html | 66 --- src/components/scroll/test/basic/main.ts | 5 - src/components/toggle/toggle.ts | 4 +- src/index.ts | 20 - src/util/interfaces.ts | 89 +++- 26 files changed, 484 insertions(+), 295 deletions(-) create mode 100644 src/components/index.ts create mode 100644 src/components/scroll/scroll-interface.ts delete mode 100644 src/components/scroll/scroll.scss delete mode 100644 src/components/scroll/test/basic/app.module.ts delete mode 100644 src/components/scroll/test/basic/eight_horns.png delete mode 100644 src/components/scroll/test/basic/main.html delete mode 100644 src/components/scroll/test/basic/main.ts diff --git a/scripts/gulp/tasks/core.ts b/scripts/gulp/tasks/core.ts index ce49509afb..0a59e1e1e6 100644 --- a/scripts/gulp/tasks/core.ts +++ b/scripts/gulp/tasks/core.ts @@ -1,15 +1,26 @@ -import { DIST_BUILD_ROOT, PROJECT_ROOT } from '../constants'; +import { SRC_ROOT, DIST_BUILD_ROOT, PROJECT_ROOT } from '../constants'; import { task } from 'gulp'; import { accessSync } from 'fs'; import { join } from 'path'; task('core', (done) => { + buildAngularBinding(false, done); +}); + +task('core.watch', (done) => { + buildAngularBinding(true, done); +}); + + +function buildAngularBinding(skipBuildingCore: boolean, done: Function) { const cwd = join(PROJECT_ROOT, '../ionic-core'); const args = [ 'run', 'build.angular', - DIST_BUILD_ROOT + SRC_ROOT, + DIST_BUILD_ROOT, + skipBuildingCore ? 'skip-core' : 'do-not-skip-core' ]; try { @@ -33,4 +44,4 @@ task('core', (done) => { ls.on('close', (code) => { done(); }); -}); +} diff --git a/scripts/gulp/tasks/e2e.dev.ts b/scripts/gulp/tasks/e2e.dev.ts index 86c032f242..cabcb3ea21 100644 --- a/scripts/gulp/tasks/e2e.dev.ts +++ b/scripts/gulp/tasks/e2e.dev.ts @@ -6,7 +6,7 @@ import { task } from 'gulp'; import { ES_2015, PROJECT_ROOT } from '../constants'; import { createTempTsConfig, getFolderInfo, runAppScriptsServe } from '../util'; -task('e2e.watch', ['e2e.prepare'], (done: Function) => { +task('e2e.watch', ['e2e.prepare', 'core.watch'], (done: Function) => { const folderInfo = getFolderInfo(); if (!folderInfo || !folderInfo.componentName || !folderInfo.componentTest) { done(new Error(`Usage: gulp e2e.watch --folder nav/basic`)); diff --git a/scripts/gulp/tasks/e2e.prod.ts b/scripts/gulp/tasks/e2e.prod.ts index 7b64f037af..c9bd259a28 100644 --- a/scripts/gulp/tasks/e2e.prod.ts +++ b/scripts/gulp/tasks/e2e.prod.ts @@ -15,7 +15,7 @@ import { createTempTsConfig, createTimestamp, getFolderInfo, readFileAsync, runA import * as pAll from 'p-all'; task('e2e.prepare', (done: Function) => { - runSequence('e2e.clean', 'core', 'e2e.polyfill', 'e2e.prepareSass', (err: any) => done(err)); + runSequence('e2e.clean', 'e2e.polyfill', 'e2e.prepareSass', (err: any) => done(err)); }); task('e2e.prepareSass', (done: Function) => { @@ -24,7 +24,7 @@ task('e2e.prepareSass', (done: Function) => { done(); }); -task('e2e.prod', ['e2e.prepare'], (done: Function) => { +task('e2e.prod', ['e2e.prepare', 'core'], (done: Function) => { // okay, first find out all of the e2e tests to run by finding all of the 'main.ts' files filterE2eTestfiles().then((filePaths: string[]) => { if (filePaths && filePaths.length > 0) { diff --git a/src/components/badge/badge.ts b/src/components/badge/badge.ts index 543012dba8..8d3ef7ada6 100644 --- a/src/components/badge/badge.ts +++ b/src/components/badge/badge.ts @@ -1,4 +1,4 @@ -import { Component } from '../../index'; +import { Component } from '../index'; @Component({ diff --git a/src/components/card/card-content.scss b/src/components/card/card-content.scss index 9d8f45cddb..1fb53d976a 100644 --- a/src/components/card/card-content.scss +++ b/src/components/card/card-content.scss @@ -1,9 +1,11 @@ @import "../../themes/ionic.globals"; + // Card // -------------------------------------------------- ion-card-content, :host { display: block; + visibility: inherit !important; } diff --git a/src/components/card/card-content.ts b/src/components/card/card-content.ts index 992b0b9f11..1e43b6ae5c 100644 --- a/src/components/card/card-content.ts +++ b/src/components/card/card-content.ts @@ -1,4 +1,4 @@ -import { Component } from '../../index'; +import { Component } from '../index'; @Component({ diff --git a/src/components/card/card-header.scss b/src/components/card/card-header.scss index 707b9d5a54..9106d52f39 100644 --- a/src/components/card/card-header.scss +++ b/src/components/card/card-header.scss @@ -1,5 +1,6 @@ @import "../../themes/ionic.globals"; + // Card Header // -------------------------------------------------- @@ -8,6 +9,7 @@ ion-card-header, :host { display: block; overflow: hidden; + visibility: inherit !important; text-overflow: ellipsis; white-space: nowrap; diff --git a/src/components/card/card-header.ts b/src/components/card/card-header.ts index 28ba5a2c6f..de6326a81a 100644 --- a/src/components/card/card-header.ts +++ b/src/components/card/card-header.ts @@ -1,4 +1,4 @@ -import { Component } from '../../index'; +import { Component } from '../index'; @Component({ diff --git a/src/components/card/card-title.ts b/src/components/card/card-title.ts index 709f6768a5..de33ed99fc 100644 --- a/src/components/card/card-title.ts +++ b/src/components/card/card-title.ts @@ -1,4 +1,4 @@ -import { Component } from '../../index'; +import { Component } from '../index'; @Component({ diff --git a/src/components/card/card.ios.scss b/src/components/card/card.ios.scss index 25378efd4b..7b394c832e 100644 --- a/src/components/card/card.ios.scss +++ b/src/components/card/card.ios.scss @@ -1,4 +1,6 @@ @import "../../themes/ionic.globals.ios"; +@import "./card"; + // iOS Card // -------------------------------------------------- diff --git a/src/components/card/card.md.scss b/src/components/card/card.md.scss index 4875196481..79fd69a7c7 100644 --- a/src/components/card/card.md.scss +++ b/src/components/card/card.md.scss @@ -1,4 +1,6 @@ @import "../../themes/ionic.globals.md"; +@import "./card"; + // Material Design Card // -------------------------------------------------- diff --git a/src/components/card/card.scss b/src/components/card/card.scss index facebf4d63..dbc57555e6 100644 --- a/src/components/card/card.scss +++ b/src/components/card/card.scss @@ -1,5 +1,6 @@ @import "../../themes/ionic.globals"; + // Card // -------------------------------------------------- @@ -8,6 +9,7 @@ ion-card, :host { display: block; overflow: hidden; + visibility: inherit !important; } ion-card img, diff --git a/src/components/card/card.ts b/src/components/card/card.ts index 845f5b7661..0b35759fef 100644 --- a/src/components/card/card.ts +++ b/src/components/card/card.ts @@ -1,4 +1,4 @@ -import { Component } from '../../index'; +import { Component } from '../index'; @Component({ diff --git a/src/components/card/card.wp.scss b/src/components/card/card.wp.scss index f24f49ec89..2350386f7e 100644 --- a/src/components/card/card.wp.scss +++ b/src/components/card/card.wp.scss @@ -1,4 +1,6 @@ @import "../../themes/ionic.globals.wp"; +@import "./card"; + // Windows Card // -------------------------------------------------- diff --git a/src/components/gesture/gesture.ts b/src/components/gesture/gesture.ts index 27e59a015e..c02395440a 100644 --- a/src/components/gesture/gesture.ts +++ b/src/components/gesture/gesture.ts @@ -1,5 +1,5 @@ import { applyStyles, getElementReference, pointerCoordX, pointerCoordY } from '../../util/dom'; -import { Component, Listen, Ionic, Prop } from '../../index'; +import { Component, Listen, Ionic, Prop } from '../index'; import { GestureCallback, GestureDetail } from '../../util/interfaces'; import { GestureController, GestureDelegate } from './gesture-controller'; import { PanRecognizer } from './recognizers'; @@ -20,6 +20,7 @@ export class Gesture { private hasPress = false; private hasStartedPan = false; private requiresMove = false; + private isMoveQueued = false; @Prop() direction: string = 'x'; @Prop() gestureName: string = ''; @@ -153,15 +154,21 @@ export class Gesture { if (this.pan) { if (this.hasCapturedPan) { - Ionic.dom.write(() => { - detail.type = 'pan'; - if (this.onMove) { - this.onMove(detail); - } else { - Ionic.emit(this, 'ionGestureMove', this.detail); - } - }); + if (!this.isMoveQueued) { + this.isMoveQueued = true; + + Ionic.dom.write(() => { + this.isMoveQueued = false; + detail.type = 'pan'; + + if (this.onMove) { + this.onMove(detail); + } else { + Ionic.emit(this, 'ionGestureMove', { detail: this.detail }); + } + }); + } } else if (this.pan.detect(detail.currentX, detail.currentY)) { if (this.pan.isGesture() !== 0) { @@ -201,14 +208,14 @@ export class Gesture { // compute relative movement between these two points var movedX = (positions[startPos - 2] - positions[endPos - 2]); var movedY = (positions[startPos - 1] - positions[endPos - 1]); - var factor = 16 / (positions[endPos] - positions[startPos]); + var factor = 16.67 / (positions[endPos] - positions[startPos]); // based on XXms compute the movement to apply for each render step detail.velocityX = movedX * factor; detail.velocityY = movedY * factor; - detail.velocityDirectionX = (detail.velocityX > 0 ? 'left' : (detail.velocityX < 0 ? 'right' : null)); - detail.velocityDirectionY = (detail.velocityY > 0 ? 'up' : (detail.velocityY < 0 ? 'down' : null)); + detail.velocityDirectionX = (movedX > 0 ? 'left' : (movedX < 0 ? 'right' : null)); + detail.velocityDirectionY = (movedY > 0 ? 'up' : (movedY < 0 ? 'down' : null)); } } @@ -222,7 +229,7 @@ export class Gesture { if (this.onStart) { this.onStart(this.detail); } else { - Ionic.emit(this, 'ionGestureStart', this.detail); + Ionic.emit(this, 'ionGestureStart', { detail: this.detail }); } this.hasCapturedPan = true; @@ -279,7 +286,7 @@ export class Gesture { if (this.onEnd) { this.onEnd(detail); } else { - Ionic.emit(this, 'ionGestureEnd', detail); + Ionic.emit(this, 'ionGestureEnd', { detail: detail }); } } else if (this.hasPress) { @@ -289,7 +296,7 @@ export class Gesture { if (this.notCaptured) { this.notCaptured(detail); } else { - Ionic.emit(this, 'ionGestureNotCaptured', detail); + Ionic.emit(this, 'ionGestureNotCaptured', { detail: detail }); } } @@ -311,7 +318,7 @@ export class Gesture { if (this.onPress) { this.onPress(detail); } else { - Ionic.emit(this, 'ionPress', detail); + Ionic.emit(this, 'ionPress', { detail: detail }); } } } diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000000..7113f8a1bf --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,15 @@ + +import * as interfaces from '../util/interfaces'; + + +export declare const Component: interfaces.ComponentDecorator; + +export declare const h: interfaces.Hyperscript; + +export declare const Ionic: interfaces.Ionic; + +export declare const Listen: interfaces.ListenDecorator; + +export declare const Prop: interfaces.PropDecorator; + +export declare const Watch: interfaces.WatchDecorator; diff --git a/src/components/scroll/scroll-interface.ts b/src/components/scroll/scroll-interface.ts new file mode 100644 index 0000000000..0b53870056 --- /dev/null +++ b/src/components/scroll/scroll-interface.ts @@ -0,0 +1,14 @@ +import { ScrollCallback } from '../../util/interfaces'; + + +export interface Scroll { + enabled: boolean; + jsScroll: boolean; + + ionScrollStart: ScrollCallback; + ionScroll: ScrollCallback; + ionScrollEnd: ScrollCallback; + + scrollToTop: (duration: number) => Promise; + scrollToBottom: (duration: number) => Promise; +} diff --git a/src/components/scroll/scroll.scss b/src/components/scroll/scroll.scss deleted file mode 100644 index f68893f714..0000000000 --- a/src/components/scroll/scroll.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import "../../themes/ionic.globals"; - -ion-scroll { - position: relative; - display: block; -} - -ion-scroll.scroll-x .scroll-content { - overflow-x: auto; -} - -ion-scroll.scroll-y .scroll-content { - overflow-y: auto; -} - -ion-scroll[center] .scroll-content { - display: flex; - - align-items: center; - justify-content: center; -} - -ion-scroll .scroll-content { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - - overflow-y: hidden; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - will-change: scroll-position; -} diff --git a/src/components/scroll/scroll.ts b/src/components/scroll/scroll.ts index d5efbff06a..ff9d78d795 100644 --- a/src/components/scroll/scroll.ts +++ b/src/components/scroll/scroll.ts @@ -1,120 +1,360 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, Listen, Ionic, Prop } from '../index'; +import { ScrollCallback, ScrollDetail } from '../../util/interfaces'; +import { GestureController, GestureDelegate } from '../gesture/gesture-controller'; +import { Scroll as IScroll } from './scroll-interface'; -import { isTrueProperty, assert } from '../../util/util'; -/** - * @name Scroll - * @description - * Scroll is a non-flexboxed scroll area that can scroll horizontally or vertically. `ion-Scroll` Can be used in places where you may not need a full page scroller, but a highly customized one, such as image scubber or comment scroller. - * @usage - * ```html - * - * - * - * - * - * - * - * - * ``` - * @demo /docs/demos/src/scroll/ - */ @Component({ - selector: 'ion-scroll', - template: - '
' + - '
' + - '' + - '
' + - '
', - host: { - '[class.scroll-x]': 'scrollX', - '[class.scroll-y]': 'scrollY' - }, - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, + tag: 'ion-scroll', + shadow: false }) -export class Scroll { +export class Scroll implements IScroll { + private $el: HTMLElement; + private gesture: GestureDelegate; + private positions: number[] = []; + private _l: number; + private _t: number; + private tmr: any; + private queued = false; + + isScrolling: boolean = false; + detail: ScrollDetail = {}; + + @Prop() enabled: boolean = true; + @Prop() jsScroll: boolean = false; + @Prop() ionScrollStart: ScrollCallback; + @Prop() ionScroll: ScrollCallback; + @Prop() ionScrollEnd: ScrollCallback; + + + ionViewDidLoad() { + Ionic.controllers.gesture = (Ionic.controllers.gesture || new GestureController()); + + this.gesture = (Ionic.controllers.gesture).createGesture('scroll', 100, false); + } + + + // Native Scroll ************************* + + @Listen('scroll', { passive: true }) + onNativeScroll() { + const self = this; + + if (!self.queued && self.enabled) { + self.queued = true; + + Ionic.dom.read(function(timeStamp) { + self.queued = false; + self.onScroll(timeStamp || Date.now()); + }); + } + } + + onScroll(timeStamp: number) { + const self = this; + const detail = self.detail; + const positions = self.positions; + + detail.timeStamp = timeStamp; + + // get the current scrollTop + // ******** DOM READ **************** + detail.scrollTop = self.getTop(); + + // get the current scrollLeft + // ******** DOM READ **************** + detail.scrollLeft = self.getLeft(); + + if (!self.isScrolling) { + // currently not scrolling, so this is a scroll start + self.isScrolling = true; + + // remember the start positions + detail.startY = detail.scrollTop; + detail.startX = detail.scrollLeft; + + // new scroll, so do some resets + detail.velocityY = detail.velocityX = detail.deltaY = detail.deltaX = positions.length = 0; + + // emit only on the first scroll event + if (self.ionScrollStart) { + self.ionScrollStart(detail); + } else { + Ionic.emit(this, 'ionScrollStart', { detail: detail }); + } + } + + detail.directionX = detail.velocityDirectionX = (detail.deltaX > 0 ? 'left' : (detail.deltaX < 0 ? 'right' : null)); + detail.directionY = detail.velocityDirectionY = (detail.deltaY > 0 ? 'up' : (detail.deltaY < 0 ? 'down' : null)); + + // actively scrolling + positions.push(detail.scrollTop, detail.scrollLeft, detail.timeStamp); + + if (positions.length > 3) { + // we've gotten at least 2 scroll events so far + detail.deltaY = (detail.scrollTop - detail.startY); + detail.deltaX = (detail.scrollLeft - detail.startX); + + var endPos = (positions.length - 1); + var startPos = endPos; + var timeRange = (detail.timeStamp - 100); + + // move pointer to position measured 100ms ago + for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) { + startPos = i; + } + + if (startPos !== endPos) { + // compute relative movement between these two points + var movedTop = (positions[startPos - 2] - positions[endPos - 2]); + var movedLeft = (positions[startPos - 1] - positions[endPos - 1]); + var factor = 16.67 / (positions[endPos] - positions[startPos]); + + // based on XXms compute the movement to apply for each render step + detail.velocityY = movedTop * factor; + detail.velocityX = movedLeft * factor; + + // figure out which direction we're scrolling + detail.velocityDirectionX = (movedLeft > 0 ? 'left' : (movedLeft < 0 ? 'right' : null)); + detail.velocityDirectionY = (movedTop > 0 ? 'up' : (movedTop < 0 ? 'down' : null)); + } + } + + clearTimeout(self.tmr); + self.tmr = setTimeout(function() { + + // haven't scrolled in a while, so it's a scrollend + self.isScrolling = false; + + Ionic.dom.read(function(timeStamp) { + if (!self.isScrolling) { + self.onEnd(timeStamp); + } + }); + }, 80); + + // emit on each scroll event + if (self.ionScrollStart) { + self.ionScroll(detail); + } else { + Ionic.emit(this, 'ionScroll', { detail: detail }); + } + } + + + onEnd(timeStamp: number) { + const self = this; + const detail = self.detail; + + detail.timeStamp = timeStamp || Date.now(); + + // emit that the scroll has ended + if (self.ionScrollEnd) { + self.ionScrollEnd(detail); + + } else { + Ionic.emit(this, 'ionScrollEnd', { detail: detail }); + } + } + + + enableJsScroll(contentTop: number, contentBottom: number) { + this.jsScroll = true; + + Ionic.listener.enable(this, 'scroll', false); + + Ionic.listener.enable(this, 'touchstart', true); + + contentTop; contentBottom; + } + + + // Touch Scroll ************************* + + @Listen('touchstart', { passive: true, enabled: false }) + onTouchStart() { + if (!this.enabled) { + return; + } + + Ionic.listener.enable(this, 'touchmove', true); + Ionic.listener.enable(this, 'touchend', true); + + throw 'jsScroll: TODO!'; + } + + @Listen('touchmove', { passive: true, enabled: false }) + onTouchMove() { + if (!this.enabled) { + return; + } + } + + @Listen('touchend', { passive: true, enabled: false }) + onTouchEnd() { + Ionic.listener.enable(this, 'touchmove', false); + Ionic.listener.enable(this, 'touchend', false); + + if (!this.enabled) { + return; + } + } - _scrollX: boolean = false; - _scrollY: boolean = false; - _zoom: boolean = false; - _maxZoom: number = 1; /** - * @input {boolean} If true, scrolling along the X axis is enabled. + * DOM READ */ - @Input() - get scrollX() { - return this._scrollX; - } - set scrollX(val: any) { - this._scrollX = isTrueProperty(val); + getTop() { + if (this.jsScroll) { + return this._t; + } + return this._t = this.$el.scrollTop; } /** - * @input {boolean} If true, scrolling along the Y axis is enabled; requires the following CSS declaration: ion-scroll { white-space: nowrap; } + * DOM READ */ - @Input() - get scrollY() { - return this._scrollY; - } - set scrollY(val: any) { - this._scrollY = isTrueProperty(val); + getLeft() { + if (this.jsScroll) { + return 0; + } + return this._l = this.$el.scrollLeft; } /** - * @input {boolean} If true, zooming is enabled. + * DOM WRITE */ - @Input() - get zoom() { - return this._zoom; - } - set zoom(val: any) { - this._zoom = isTrueProperty(val); + setTop(top: number) { + this._t = top; + + if (this.jsScroll) { + this.$el.style.transform = this.$el.style.webkitTransform = `translate3d(${this._l * -1}px,${top * -1}px,0px)`; + + } else { + this.$el.scrollTop = top; + } } /** - * @input {number} Set the max zoom amount. + * DOM WRITE */ - @Input() - get maxZoom() { - return this._maxZoom; - } - set maxZoom(val: any) { - this._maxZoom = val; + setLeft(left: number) { + this._l = left; + + if (this.jsScroll) { + this.$el.style.transform = this.$el.style.webkitTransform = `translate3d(${left * -1}px,${this._t * -1}px,0px)`; + + } else { + this.$el.scrollLeft = left; + } } - /** - * @hidden - */ - maxScale: number = 3; - /** - * @hidden - */ - zoomDuration: number = 250; + scrollTo(x: number, y: number, duration: number, done?: Function): Promise { + // scroll animation loop w/ easing + // credit https://gist.github.com/dezinezync/5487119 - /** @internal */ - @ViewChild('scrollContent', { read: ElementRef }) _scrollContent: ElementRef; + let promise: Promise; + if (done === undefined) { + // only create a promise if a done callback wasn't provided + // done can be a null, which avoids any functions + promise = new Promise(resolve => { + done = resolve; + }); + } - constructor(private _elementRef: ElementRef) { } + const self = this; + const el = self.$el; + if (!el) { + // invalid element + done(); + return promise; + } - /** - * @hidden - * Add a scroll event handler to the scroll element if it exists. - * @param {Function} handler The scroll handler to add to the scroll element. - * @returns {?Function} a function to remove the specified handler, otherwise - * undefined if the scroll element doesn't exist. - */ - addScrollEventListener(handler: any) { - assert(this._scrollContent, 'scroll element is missing'); + if (duration < 32) { + self.setTop(y); + self.setLeft(x); + done(); + return promise; + } - const ele = this._scrollContent.nativeElement; - ele.addEventListener('scroll', handler); + const fromY = el.scrollTop; + const fromX = el.scrollLeft; - return () => { - ele.removeEventListener('scroll', handler); - }; + const maxAttempts = (duration / 16) + 100; + + let startTime: number; + let attempts = 0; + let stopScroll = false; + + // scroll loop + function step(timeStamp: number) { + attempts++; + + if (!self.$el || stopScroll || attempts > maxAttempts) { + self.isScrolling = false; + el.style.transform = el.style.webkitTransform = ''; + done(); + return; + } + + let time = Math.min(1, ((timeStamp - startTime) / duration)); + + // where .5 would be 50% of time on a linear scale easedT gives a + // fraction based on the easing method + let easedT = (--time) * time * time + 1; + + if (fromY !== y) { + self.setTop((easedT * (y - fromY)) + fromY); + } + + if (fromX !== x) { + self.setLeft(Math.floor((easedT * (x - fromX)) + fromX)); + } + + if (easedT < 1) { + // do not use DomController here + // must use nativeRaf in order to fire in the next frame + Ionic.dom.raf(step); + + } else { + stopScroll = true; + self.isScrolling = false; + el.style.transform = el.style.webkitTransform = ''; + done(); + } + } + + // start scroll loop + self.isScrolling = true; + + // chill out for a frame first + Ionic.dom.write(() => { + Ionic.dom.write(timeStamp => { + startTime = timeStamp; + step(timeStamp); + }); + }); + + return promise; + } + + scrollToTop(duration: number): Promise { + return this.scrollTo(0, 0, duration); + } + + scrollToBottom(duration: number): Promise { + let y = 0; + if (this.$el) { + y = this.$el.scrollHeight - this.$el.clientHeight; + } + return this.scrollTo(0, y, duration); + } + + + ionViewWillUnload() { + this.gesture && this.gesture.destroy(); + this.gesture = this.detail = this.detail.event = null; } } + diff --git a/src/components/scroll/test/basic/app.module.ts b/src/components/scroll/test/basic/app.module.ts deleted file mode 100644 index f4d2971969..0000000000 --- a/src/components/scroll/test/basic/app.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component, NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { IonicApp, IonicModule } from '../../../..'; - - -@Component({ - templateUrl: 'main.html' -}) -export class AppComponent { - doRefresh() { - console.log('DOREFRESH'); - } -} - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule, - IonicModule.forRoot(AppComponent) - ], - bootstrap: [IonicApp], - entryComponents: [ - AppComponent - ] -}) -export class AppModule {} diff --git a/src/components/scroll/test/basic/eight_horns.png b/src/components/scroll/test/basic/eight_horns.png deleted file mode 100644 index 103fa9cce794bab2b3ae6341fd99499d750a5f41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2493 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 diff --git a/src/components/scroll/test/basic/main.html b/src/components/scroll/test/basic/main.html deleted file mode 100644 index 38c0db5b49..0000000000 --- a/src/components/scroll/test/basic/main.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - Scroll - - - - - - - -

Horizontal

- -
-
- -

Vertical

- -
-
- -

Both

- -
-
- -
- - - diff --git a/src/components/scroll/test/basic/main.ts b/src/components/scroll/test/basic/main.ts deleted file mode 100644 index 6af7a5b2ae..0000000000 --- a/src/components/scroll/test/basic/main.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app.module'; - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/components/toggle/toggle.ts b/src/components/toggle/toggle.ts index 8a86f95c14..286bc26d15 100644 --- a/src/components/toggle/toggle.ts +++ b/src/components/toggle/toggle.ts @@ -1,5 +1,5 @@ import { BooleanInputComponent, GestureDetail } from '../../util/interfaces'; -import { Component, h, Ionic, Listen, Prop, Watch } from '../../index'; +import { Component, h, Ionic, Listen, Prop, Watch } from '../index'; @Component({ @@ -24,7 +24,7 @@ export class Toggle implements BooleanInputComponent { @Watch('checked') changed(val: boolean) { - Ionic.emit(this, 'ionChange', { checked: val }); + Ionic.emit(this, 'ionChange', { detail: { checked: val } }); } diff --git a/src/index.ts b/src/index.ts index 0d8cfd7402..2218ecc014 100644 --- a/src/index.ts +++ b/src/index.ts @@ -164,23 +164,3 @@ export { registerModeConfigs } from './config/mode-registry'; export { IonicGestureConfig } from './gestures/gesture-config'; export { IonicModule, IonicPageModule, provideLocationStrategy } from './module'; - - - -/** - * Core - */ - -import * as interfaces from './util/interfaces'; - -export declare const Component: interfaces.ComponentDecorator; - -export declare const h: interfaces.Hyperscript; - -export declare const Ionic: interfaces.Ionic; - -export declare const Listen: interfaces.ListenDecorator; - -export declare const Prop: interfaces.PropDecorator; - -export declare const Watch: interfaces.WatchDecorator; diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index acd71a70d8..0653d4731d 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -9,11 +9,20 @@ export interface Ionic { gesture?: any; }; dom: DomControllerApi; + config: ConfigApi; } export interface EventEmit { - (instance: any, eventName: string, data?: any): void; + (instance: any, eventName: string, data?: CustomEventOptions): void; +} + + +export interface CustomEventOptions { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; + detail?: any; } @@ -52,15 +61,54 @@ export interface GestureCallback { } +export interface ScrollDetail extends GestureDetail { + scrollTop?: number; + scrollLeft?: number; + scrollHeight?: number; + scrollWidth?: number; + contentHeight?: number; + contentWidth?: number; + contentTop?: number; + contentBottom?: number; + domWrite?: RequestAnimationFrame; + contentElement?: HTMLElement; + fixedElement?: HTMLElement; + scrollElement?: HTMLElement; + headerElement?: HTMLElement; + footerElement?: HTMLElement; +} + + +export interface ScrollCallback { + (detail?: ScrollDetail): boolean|void; +} + + +export interface ContentDimensions { + contentHeight: number; + contentTop: number; + contentBottom: number; + + contentWidth: number; + contentLeft: number; + + scrollHeight: number; + scrollTop: number; + + scrollWidth: number; + scrollLeft: number; +} + + export interface IonicGlobal { staticDir?: string; components?: LoadComponents; loadComponents?: {(bundleId: string): void}; - config?: Object; - configCtrl?: ConfigApi; - domCtrl?: DomControllerApi; - nextTickCtrl?: NextTickApi; eventNamePrefix?: string; + config?: Object; + ConfigCtrl?: ConfigApi; + DomCtrl?: DomControllerApi; + NextTickCtrl?: NextTickApi; } @@ -74,22 +122,12 @@ export interface NextTick { } -export interface DomRead { - (cb: Function): void; -} - - -export interface DomWrite { - (cb: Function): void; -} - - export interface DomControllerApi { - read: DomRead; - write: DomWrite; + read: RequestAnimationFrame; + write: RequestAnimationFrame; + raf: RequestAnimationFrame; } - export interface RafCallback { (timeStamp?: number): void; } @@ -251,7 +289,7 @@ export interface Watchers { export interface IonicTheme { - (instance: any, cssClassName: string, vnodeData: VNodeData): VNodeData; + (instance: any, cssClassName: string, vnodeData?: VNodeData): VNodeData; } @@ -346,7 +384,7 @@ export interface ProxyElement extends HTMLElement { export interface RendererApi { - (oldVnode: VNode | Element, vnode: VNode): VNode; + (oldVnode: VNode | Element, vnode: VNode, manualSlotProjection?: boolean): VNode; } @@ -394,8 +432,6 @@ export interface PlatformApi { getComponentMeta: (tag: string) => ComponentMeta; loadComponent: (bundleId: string, cb: Function) => void; nextTick: NextTick; - domRead: DomRead; - domWrite: DomWrite; isElement: (node: Node) => node is Element; isText: (node: Node) => node is Text; @@ -414,7 +450,14 @@ export interface PlatformApi { $setTextContent: (node: Node, text: string | null) => void; $getTextContent: (node: Node) => string | null; $getAttribute: (elm: Element, attrName: string) => string; - $attachShadow: (elm: Element, cmpMode: ComponentMode, cmpModeId: string) => ShadowRoot; + $attachComponent: (elm: Element, cmpMeta: ComponentMeta, instance: Component) => void; +} + + +export interface PlatformConfig { + name: string; + isMatch?: {(url: string, userAgent: string): boolean}; + settings?: any; }