From 065214526fb8c4e22b733344090b0cba603a4a99 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 7 Apr 2015 17:09:38 -0600 Subject: [PATCH] add initial split view, build to es5 --- gulpfile.js | 34 ++++++-- scripts/build/config.js | 1 + src/components.js | 1 + src/components/aside/aside.js | 8 +- src/components/aside/extensions/gestures.js | 3 +- src/components/nav-view/nav-view.js | 56 +++++++++++-- src/components/nav-viewport/nav-viewport.js | 77 ++++++++---------- src/components/split-view/split-nav-view.js | 4 + src/components/split-view/split-view.js | 78 +++++++++++++++++++ .../test/settings/groups/general.js | 16 ++++ .../split-view/test/settings/main.html | 2 + .../split-view/test/settings/main.js | 38 +++++++++ src/components/tabs/tab.js | 3 +- src/components/tabs/test/advanced/main.html | 2 +- src/components/tabs/test/advanced/main.js | 2 +- .../tabs/test/advanced/pages/sign-in.js | 10 +-- .../tabs/test/advanced/pages/tabs.js | 39 ++++++---- src/gestures/drag-gesture.js | 5 +- src/gestures/gesture.js | 16 +++- src/gestures/slide-edge-gesture.js | 2 +- src/gestures/slide-gesture.js | 4 +- src/ionic2.js | 2 + 22 files changed, 312 insertions(+), 91 deletions(-) create mode 100644 src/components/split-view/split-nav-view.js create mode 100644 src/components/split-view/split-view.js create mode 100644 src/components/split-view/test/settings/groups/general.js create mode 100644 src/components/split-view/test/settings/main.html create mode 100644 src/components/split-view/test/settings/main.js create mode 100644 src/ionic2.js diff --git a/gulpfile.js b/gulpfile.js index 760f9e28b8..dd3f1c9cd6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,7 +1,3 @@ -// -// Mostly stolen from https://github.com/pkozlowski-opensource/ng2-play -// - var _ = require('lodash') var buildConfig = require('./scripts/build/config') var SystemJsBuilder = require('systemjs-builder') @@ -55,9 +51,33 @@ gulp.task('dependencies', function() { .pipe(gulp.dest(buildConfig.distLib)) }) -gulp.task('ionic-js', function() { - return gulp.src(['src/**/*.js', '!src/**/test/*/**/*.js']) - .pipe(gulp.dest(buildConfig.distLib + '/ionic2')) +gulp.task('ionic-compile', function() { + var builder = new SystemJsBuilder() + builder.config({ + traceurOptions: buildConfig.traceurOptions, + meta: { + 'angular2/angular2': { build: false }, + 'angular2/src/di/annotations': { build: false }, + 'angular2/src/core/compiler/private_component_loader': { build: false }, + 'angular2/src/core/compiler/private_component_location': { build: false }, + 'hammer': { build: false }, + }, + map: { + 'ionic2': 'src' + } + }) + return builder.build('ionic2/ionic2', buildConfig.distLib + '/ionic2.js') +}) + +gulp.task('ionic-js', ['ionic-compile'], function() { + return gulp.src(buildConfig.distLib + '/ionic2.js') + .pipe(through2.obj(function(file, enc, next) { + var contents = file.contents.toString() + contents = contents.replace(/"src\//g, '"ionic2/') + file.contents = new Buffer(contents) + next(null, file) + })) + .pipe(gulp.dest(buildConfig.distLib)) }) gulp.task('sass', function(done) { diff --git a/scripts/build/config.js b/scripts/build/config.js index 803174c84f..4105f453e0 100644 --- a/scripts/build/config.js +++ b/scripts/build/config.js @@ -29,6 +29,7 @@ module.exports = { 'long-stack-trace-zone.js', 'hammer.js', 'angular2.js', + 'ionic2.js', ], traceurOptions: { diff --git a/src/components.js b/src/components.js index 4698481368..3e693abec7 100644 --- a/src/components.js +++ b/src/components.js @@ -10,6 +10,7 @@ export * from 'ionic2/components/item/item' export * from 'ionic2/components/list/list' export * from 'ionic2/components/nav-view/nav-view' export * from 'ionic2/components/nav-viewport/nav-viewport' +export * from 'ionic2/components/split-view/split-view' export * from 'ionic2/components/tabs/tabs' export * from 'ionic2/components/tabs/tab' export * from 'ionic2/components/toolbar/toolbar' diff --git a/src/components/aside/aside.js b/src/components/aside/aside.js index 512465efab..33c69f85d1 100644 --- a/src/components/aside/aside.js +++ b/src/components/aside/aside.js @@ -1,9 +1,13 @@ -import {Component, Template, Inject, Parent, NgElement} from 'angular2/angular2' +import {Component, Template, Inject, Parent, NgElement, EventEmitter} from 'angular2/angular2' import * as types from 'ionic2/components/aside/extensions/types' -import * as gestures from 'ionic2/components/aside/extensions/gestures'; +import * as gestures from 'ionic2/components/aside/extensions/gestures' import {dom} from 'ionic2/util' import {IonicComponent} from 'ionic2/config/component' +/** + * TODO (?) add docs about how to have a root aside and a nested aside, then hide the root one + */ + @Component({ selector: 'ion-aside', bind: { diff --git a/src/components/aside/extensions/gestures.js b/src/components/aside/extensions/gestures.js index 6d3e44303a..69ebc861d1 100644 --- a/src/components/aside/extensions/gestures.js +++ b/src/components/aside/extensions/gestures.js @@ -15,7 +15,7 @@ class AsideGesture extends SlideEdgeGesture { this.slideElement = slideElement; this.listen(); } - + canStart(ev) { // Only restrict edges if the aside is closed return this.aside.isOpen ? true : super.canStart(ev); @@ -89,4 +89,3 @@ export class BottomAsideGesture extends TopAsideGesture { }; } } - diff --git a/src/components/nav-view/nav-view.js b/src/components/nav-view/nav-view.js index 1d63c56623..b2c8feb694 100644 --- a/src/components/nav-view/nav-view.js +++ b/src/components/nav-view/nav-view.js @@ -1,7 +1,7 @@ -import {DynamicComponent, Ancestor, NgElement} from 'angular2/angular2' +import {DynamicComponent, Parent, NgElement} from 'angular2/angular2' import {Optional} from 'angular2/src/di/annotations' import {NavViewport} from 'ionic2/components/nav-viewport/nav-viewport' -import {Tabs} from 'ionic2/components/tabs/tabs' +import {Tab} from 'ionic2/components/tabs/tab' import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader' import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location' @@ -18,18 +18,64 @@ export class NavView { @NgElement() element: NgElement, // FIXME: this is temporary until ng2 lets us inject tabs as a NavViewport - @Optional() @Ancestor() viewportNav: NavViewport, - @Optional() @Ancestor() viewportTabs: Tabs + @Optional() @Parent() viewportNav: NavViewport, + @Optional() @Parent() viewportTab: Tab ) { this.loader = loader this.location = location - this.viewport = viewportTabs || viewportNav + this.viewport = viewportTab || viewportNav this.domElement = element.domElement } set item(navItem) { + if (this.initialized) return; + this.initialized = true; this.loader.load(navItem.Class, this.location).then(instance => { navItem.finishSetup(this, instance) }) } + + /** + * Push out of this view into another view + */ + push(Class: Function, opts = {}) { + return this.viewport.push(Class, opts) + } + + /** + * Go back + */ + pop() { + return this.viewport.pop() + } + + popTo(index: Number) { + if (this._stack.length < index + 1) return + while (tab._stack.length > index + 1) { + tab.pop({ sync: true }) // pop with no animation + } + return tab.pop() //pop last one with animation + } } + +/* +beforePush() +beforePop() +beforeReenter() +beforePushedOut() + +beforeEnter() +afterEnter() +beforeLeave() +afterLeave() + +splitView: + + beforeEnter: setup this view as the side view, next view in main area. + + - any time a push happens in this view, bring the new component into the main view. + - any time a pop happens in this view, actually pop the stack (if we can). + + any time a push happens in the main view, act normally. + the main view thinks it is the first component in the stack (does it have its own nav-viewport?) + */ diff --git a/src/components/nav-viewport/nav-viewport.js b/src/components/nav-viewport/nav-viewport.js index 959785066b..f0770a729b 100644 --- a/src/components/nav-viewport/nav-viewport.js +++ b/src/components/nav-viewport/nav-viewport.js @@ -1,13 +1,13 @@ import {Component, Template, For, NgElement} from 'angular2/angular2' import {ComponentConfig} from 'ionic2/config/component-config' import {NavView} from 'ionic2/components/nav-view/nav-view' -import {array as arrayUtil, dom as domUtil, isFunction} from 'ionic2/util' +import * as util from 'ionic2/util' @Component({ selector: 'ion-nav-viewport', bind: { initial: 'initial' - }, + } }) @Template({ inline: ` @@ -26,7 +26,7 @@ export class NavViewport { // is removed even if it's still animating out. this._stack = [] - // _ngForLoopArray is actually adds/removes components from the dom. It won't + // _ngForLoopArray is actually adds/removes components from the dom. It won't // remove a component until it's done animating out. this._ngForLoopArray = [] } @@ -41,6 +41,7 @@ export class NavViewport { this.push(Class) } } + //TODO let the view handle enter/leave so splitview can override /** * Push a new view into the history stack. @@ -54,19 +55,15 @@ export class NavViewport { // TODO allow starting an animation in the middle (eg gestures). Leave // most of this up to the animation's implementation. push(Class: Function, { sync = this._stack.length === 0 } = {}) { + let opts = {sync} let pushedItem = new NavItem(Class) this._stack.push(pushedItem) this._ngForLoopArray.push(pushedItem) return pushedItem.waitForSetup().then(() => { let current = this.getPrevious(pushedItem) - if (sync) { - current && current.leaveSync() - pushedItem.enterSync() - } else { - current && current.leaveReverse() - return pushedItem.enter() - } + current && current.leave( util.extend({reverse:true}, opts) ) + return pushedItem.enter(opts) }) } @@ -76,18 +73,12 @@ export class NavViewport { * @param shouldAnimate whether to animate */ pop({ sync = false } = {}) { + let opts = {sync} let current = this._stack.pop() let previous = this._stack[this._stack.length - 1] - if (sync) { - previous && previous.enterSync() - return Promise.resolve(remove()) - } else { - previous && previous.enterReverse() - return current.leave().then(remove) - } - function remove() { - arrayUtil.remove(this._ngForLoopArray, current) - } + + previous && previous.enter( util.extend({reverse:true}, opts) ) + return current && current.leave(opts).then(remove) } getPrevious(item) { @@ -102,7 +93,6 @@ export class NavViewport { // Animate an old view *out* _animateOut(view) { } - } class NavItem { @@ -137,33 +127,28 @@ class NavItem { _animate({ isShown, animation }) { this.setAnimation(animation) this.setShown(isShown) - // We have to wait two rafs for the element to show. Yawn. - return domUtil.rafPromise().then(domUtil.rafPromise).then(() => { - this.startAnimation() - return domUtil.transitionEndPromise(this.navView.domElement).then(() => { - this.setAnimation(null) + if (animation) { + // We have to wait two rafs for the element to show. Yawn. + return util.dom.rafPromise().then(util.dom.rafPromise).then(() => { + this.startAnimation() + return util.dom.transitionEndPromise(this.navView.domElement).then(() => { + this.setAnimation(null) + }) }) + } else { + return Promise.resolve() + } + } + enter({ reverse = false, sync = false } = {}) { + return this._animate({ + isShown: true, + animation: sync ? null : (reverse ? 'enter-reverse' : 'enter') }) } - enterSync() { - this.setAnimation(null) - return this.setShown(true) - } - leaveSync() { - this.setAnimation(null) - return this.setShown(false) - } - enter() { - return this._animate({ isShown: true, animation: 'enter' }) - } - enterReverse() { - return this._animate({ isShown: true, animation: 'enter-reverse' }) - } - leave() { - return this._animate({ isShown: false, animation: 'leave' }) - } - leaveReverse() { - return this._animate({ isShown: false, animation: 'leave-reverse' }) + leave({ reverse = false, sync = false } = {}) { + return this._animate({ + isShown: false, + animation: sync ? null : (reverse ? 'leave-reverse' : 'leave') + }) } } - diff --git a/src/components/split-view/split-nav-view.js b/src/components/split-view/split-nav-view.js new file mode 100644 index 0000000000..5141b587fe --- /dev/null +++ b/src/components/split-view/split-nav-view.js @@ -0,0 +1,4 @@ + +export class SplitNavView extends NavView { + +} diff --git a/src/components/split-view/split-view.js b/src/components/split-view/split-view.js new file mode 100644 index 0000000000..d048a42007 --- /dev/null +++ b/src/components/split-view/split-view.js @@ -0,0 +1,78 @@ +import {Component, Parent, Decorator, Template, NgElement} from 'angular2/angular2' +import {NavViewport} from 'ionic2/components/nav-viewport/nav-viewport' +import {View} from 'ionic2/components/view/view' +import {NavView} from 'ionic2/components/nav-view/nav-view' + +/** + * SplitViewportDecorator is temporary until the SplitView is able to query + * its children for viewports. + */ +@Decorator({ + selector: 'ion-nav-viewport[split-viewport]' +}) +class SplitViewportDecorator { + constructor( + @Parent() splitView: SplitView, + navViewport: NavViewport + ) { + splitView.setNavViewport(navViewport) + } +} + +@Component({ + selector: 'ion-split-view', + bind: { + defaultView: 'defaultView', + viewTitle: 'viewTitle' + } +}) +@Template({ + inline: ` + +
+ +
+
+ + + `, + directives: [SplitViewportDecorator, NavViewport, View] +}) +export class SplitView { + constructor( + element: NgElement, + @Parent() navView: NavView + ) { + this.domElement = element.domElement + this.navView = navView + } + + set defaultView(def) { + this.splitViewport.push(def) + } + + setNavViewport(viewport) { + this.splitViewport = viewport + + this.navView.push = function(Class, opts) { + opts = opts || {} + util.defaults(opts, { sync: true }) + if (this.splitViewport._stack.indexOf(Class) !== -1) { + this.splitViewport.popTo(0) + } else { + while (this.splitViewport._stack.length) { + this.splitViewport.pop({sync: true}) + } + this.splitViewport.push(Class, opts) + } + } + + } + + // TODO set enabled depending on some condition (media query in this case) + setEnabled(isEnabled) { + this.isSplitView = isEnabled + if (isEnabled) { + } + } +} diff --git a/src/components/split-view/test/settings/groups/general.js b/src/components/split-view/test/settings/groups/general.js new file mode 100644 index 0000000000..182c02ddf2 --- /dev/null +++ b/src/components/split-view/test/settings/groups/general.js @@ -0,0 +1,16 @@ +import {Component, Template} from 'angular2/angular2' +import {View} from 'ionic2/components' + +@Component({ + selector: 'settings-general' +}) +@Template({ + inline: ` + + General Settings + + `, + directives: [View] +}) +export class SettingsGeneral { +} diff --git a/src/components/split-view/test/settings/main.html b/src/components/split-view/test/settings/main.html new file mode 100644 index 0000000000..9de8f8a062 --- /dev/null +++ b/src/components/split-view/test/settings/main.html @@ -0,0 +1,2 @@ + + diff --git a/src/components/split-view/test/settings/main.js b/src/components/split-view/test/settings/main.js new file mode 100644 index 0000000000..9d9ec72f40 --- /dev/null +++ b/src/components/split-view/test/settings/main.js @@ -0,0 +1,38 @@ +import {Component, Template, bootstrap} from 'angular2/angular2' +import {SplitView, NavViewport} from 'ionic2/components' +import {SettingsGeneral} from 'app/groups/general' + +@Component({ + selector: 'settings-split' +}) +@Template({ + inline: ` + + Hello, split + + `, + directives: [SplitView] +}) +class SettingsSplit { + constructor() { + this.default = SettingsGeneral + } +} + +@Component({ + selector: '[ion-app]' +}) +@Template({ + inline: ` + + + `, + directives: [NavViewport] +}) +class App { + constructor() { + this.initial = SettingsSplit + } +} + +bootstrap(App) diff --git a/src/components/tabs/tab.js b/src/components/tabs/tab.js index 5edf451e21..2fc3de83e9 100644 --- a/src/components/tabs/tab.js +++ b/src/components/tabs/tab.js @@ -46,5 +46,4 @@ export class Tab extends NavViewport { } } -new IonicComponent(Tab, { -}) +new IonicComponent(Tab, {}) diff --git a/src/components/tabs/test/advanced/main.html b/src/components/tabs/test/advanced/main.html index 4757597ce9..1569d08797 100644 --- a/src/components/tabs/test/advanced/main.html +++ b/src/components/tabs/test/advanced/main.html @@ -1,5 +1,5 @@ - Hello! + Hello! I'm a side menu in the root. diff --git a/src/components/tabs/test/advanced/main.js b/src/components/tabs/test/advanced/main.js index ce0fca7ab8..46ccf1d2c2 100644 --- a/src/components/tabs/test/advanced/main.js +++ b/src/components/tabs/test/advanced/main.js @@ -3,7 +3,7 @@ import {NavViewport, Aside} from 'ionic2/components' import {SignInPage} from 'app/pages/sign-in' @Component({ - selector: '[ion-app]' + selector: '[ion-app]', }) @Template({ url: 'main.html', diff --git a/src/components/tabs/test/advanced/pages/sign-in.js b/src/components/tabs/test/advanced/pages/sign-in.js index f4cb94ef8f..8674d413cd 100644 --- a/src/components/tabs/test/advanced/pages/sign-in.js +++ b/src/components/tabs/test/advanced/pages/sign-in.js @@ -1,5 +1,5 @@ -import {Component, Template, Parent} from 'angular2/angular2' -import {View, NavViewport} from 'ionic2/components' +import {Component, Template} from 'angular2/angular2' +import {View, NavView} from 'ionic2/components' import {TabsPage} from 'app/pages/tabs' @Component({ @@ -11,11 +11,11 @@ import {TabsPage} from 'app/pages/tabs' }) export class SignInPage { constructor( - @Parent() viewport: NavViewport + navView: NavView ) { - this.viewport = viewport + this.navView = navView } signIn() { - this.viewport.push(TabsPage) + this.navView.push(TabsPage) } } diff --git a/src/components/tabs/test/advanced/pages/tabs.js b/src/components/tabs/test/advanced/pages/tabs.js index 7b77a0c6b5..2977dde5bf 100644 --- a/src/components/tabs/test/advanced/pages/tabs.js +++ b/src/components/tabs/test/advanced/pages/tabs.js @@ -1,6 +1,6 @@ import {Component, Template, Parent} from 'angular2/angular2' -import {View, Tabs, Tab} from 'ionic2/components' -import {NavViewport} from 'ionic2/components' +import {View, Tabs, Tab, Aside} from 'ionic2/components' +import {NavView} from 'ionic2/components' @Component({ selector: 'tabs-page' @@ -26,11 +26,11 @@ export class TabsPage { directives: [View] }) class Tab1Page1 { - constructor(@Parent() tab: Tab) { - this.tab = tab + constructor(navView: NavView) { + this.navView = navView } next() { - this.tab.push(Tab1Page2) + this.navView.push(Tab1Page2) } } @@ -40,8 +40,10 @@ class Tab1Page1 { directives: [View] }) class Tab1Page2 { - constructor(@Parent() tab: Tab) { this.tab = tab } - pop() { this.tab.pop() } + constructor(navView: NavView) { + this.navView = navView + } + pop() { this.navView.pop() } } @@ -50,15 +52,22 @@ class Tab1Page2 { // @Component({ selector: 't2p1' }) @Template({ - inline: '

Tab 2 Page 1.
', - directives: [View] + inline: ` + Hello, I'm a deeper aside. + +

Tab 2 Page 1. + +
I've got an aside on the left. +
+ `, + directives: [View, Aside] }) class Tab2Page1 { - constructor(@Parent() tab: Tab) { - this.tab = tab + constructor(navView: NavView) { + this.navView = navView } next() { - this.tab.push(Tab2Page2) + this.navView.push(Tab1Page2) } } @@ -68,6 +77,8 @@ class Tab2Page1 { directives: [View] }) class Tab2Page2 { - constructor(@Parent() tab: Tab) { this.tab = tab } - pop() { this.tab.pop() } + constructor(navView: NavView) { + this.navView = navView + } + pop() { this.navView.pop() } } diff --git a/src/gestures/drag-gesture.js b/src/gestures/drag-gesture.js index 9f974926c1..1530dc43a7 100644 --- a/src/gestures/drag-gesture.js +++ b/src/gestures/drag-gesture.js @@ -17,17 +17,20 @@ export class DragGesture extends Gesture { if (this.onDragStart(ev) !== false) { this.dragging = true; } - }); + // ev.stopPropagation(); + }) this.on('panmove', ev => { if (!this.dragging) return; if (this.onDrag(ev) === false) { this.dragging = false; } + // ev.stopPropagation() }); this.on('panend', ev => { if (!this.dragging) return; this.onDragEnd(ev); this.dragging = false; + // ev.stopPropagation() }); } onDrag() {} diff --git a/src/gestures/gesture.js b/src/gestures/gesture.js index 3d212d0c34..c9228e9b7e 100644 --- a/src/gestures/gesture.js +++ b/src/gestures/gesture.js @@ -3,6 +3,9 @@ import Hammer from 'hammer'; export class Gesture { constructor(element, opts = {}) { + util.defaults(opts, { + domEvents: true + }); this.element = element; // Map 'x' or 'y' string to hammerjs opts @@ -12,14 +15,17 @@ export class Gesture { Hammer.DIRECTION_VERTICAL; this._options = opts; + this._callbacks = {}; } options(opts = {}) { util.extend(this._options, opts); } - on(...args) { - return this.hammertime.on.apply(this.hammertime, args) + on(type, cb) { + this.hammertime.on(type, util.noop); + (this._callbacks[type] || (this._callbacks[type] = [])).push(cb); + this.element.addEventListener(type, cb); } listen() { @@ -28,6 +34,12 @@ export class Gesture { unlisten() { this.hammertime.destroy(); this.hammertime = null; + for (let type in this._callbacks) { + for (let i = 0; i < this._callbacks[type].length; i++) { + this.element.removeEventListener(type, this._callbacks[type][i]); + } + } + this._callbacks = {} } destroy() { this.unlisten() diff --git a/src/gestures/slide-edge-gesture.js b/src/gestures/slide-edge-gesture.js index ad70fbf428..6656eeb7a9 100644 --- a/src/gestures/slide-edge-gesture.js +++ b/src/gestures/slide-edge-gesture.js @@ -15,7 +15,7 @@ export class SlideEdgeGesture extends SlideGesture { canStart(ev) { this._containerRect = this.getContainerDimensions(); - return this.edges.every(edge => this._checkEdge(edge, ev.center)); + return this.edges.every(edge => this._checkEdge(edge, ev.gesture.center)); } getContainerDimensions() { diff --git a/src/gestures/slide-gesture.js b/src/gestures/slide-gesture.js index e1965bf1bf..3d3f2e9e38 100644 --- a/src/gestures/slide-gesture.js +++ b/src/gestures/slide-gesture.js @@ -40,7 +40,7 @@ export class SlideGesture extends DragGesture { this.slide.min = min; this.slide.max = max; this.slide.elementStartPos = this.getElementStartPos(this.slide, ev); - this.slide.pointerStartPos = ev.center[this.direction]; + this.slide.pointerStartPos = ev.gesture.center[this.direction]; this.slide.started = true; this.onSlideStart(this.slide, ev); }).catch(() => { @@ -49,7 +49,7 @@ export class SlideGesture extends DragGesture { } onDrag(ev) { if (!this.slide || !this.slide.started) return; - this.slide.pos = ev.center[this.direction]; + this.slide.pos = ev.gesture.center[this.direction]; this.slide.distance = util.clamp( this.slide.min, this.slide.pos - this.slide.pointerStartPos + this.slide.elementStartPos, diff --git a/src/ionic2.js b/src/ionic2.js new file mode 100644 index 0000000000..5eaa385732 --- /dev/null +++ b/src/ionic2.js @@ -0,0 +1,2 @@ + +export * from 'ionic2/components'