diff --git a/package.json b/package.json index 414ee2f3a2..92b7454802 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,11 @@ "rx": "^2.4.6", "traceur": "0.0.87", "zone.js": "0.4.1" + }, + "registry": "jspm", + "directories": { + "baseURL": "n", + "lib": "n", + "packages": "n/jspm_packages" } } diff --git a/src/components.js b/src/components.js index 756b9ffbd2..77e25eefd5 100644 --- a/src/components.js +++ b/src/components.js @@ -5,6 +5,7 @@ export * from 'ionic2/components/icon/icon' 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/tabs/tabs' export * from 'ionic2/components/toolbar/toolbar' export * from 'ionic2/components/view/view' diff --git a/src/components/app/ionic.scss b/src/components/app/ionic.scss index 50878bc7eb..a9d2fededb 100644 --- a/src/components/app/ionic.scss +++ b/src/components/app/ionic.scss @@ -32,6 +32,7 @@ "../item/item", "../list/list", "../modal/modal", + "../nav-view/nav-view", "../radio/radio", "../search-bar/search-bar", "../switch/switch", diff --git a/src/components/item/item-swipe-buttons.js b/src/components/item/item-swipe-buttons.js index 605bd1f112..f37087c493 100644 --- a/src/components/item/item-swipe-buttons.js +++ b/src/components/item/item-swipe-buttons.js @@ -10,14 +10,11 @@ export class ItemPrimarySwipeButtons { @NgElement() element: NgElement, @Parent() item: Item ) { + item.primarySwipeButtons = this this.domElement = element.domElement this.parentItem = item this.gesture = new ItemSlideGesture(this) this.gesture.listen() - - this.domElement.addEventListener('transitionend', () => { - this.domElement.classList.remove('changing') - }) } setOpen(isOpen) { diff --git a/src/components/item/item.js b/src/components/item/item.js index f2e40cbec9..16d7664086 100644 --- a/src/components/item/item.js +++ b/src/components/item/item.js @@ -27,20 +27,82 @@ import { `, - direcetives: [ + directives: [ ItemPrimarySwipeButtons, - ItemSecondarySwipeButtons, - ItemPrimaryOptions, - ItemSecondaryOptions + // ItemSecondarySwipeButtons, + // ItemPrimaryOptions, + // ItemSecondaryOptions ] }) export class Item { constructor( @NgElement() ele:NgElement ) { + this._isOpen = false + this._isSlideActive = false + this._isTransitioning = false + this._transform = '' + this.domElement = ele.domElement + this.swipeButtons = {} + this.optionButtons = {} Item.config.invoke(this) } } new IonicComponent(Item, {}) + + +function clsSetter(el, name) { + return (isSet) => el.classList[isSet?'add':'remove'](name) +} + +import {dom} from 'ionic2/util' + +class Slideable { + constructor(slideElement: Element) { + } + + // override + onTransform(str: String) {} + // override + onTransitionActive(active: Boolean) {} + //override + onSlideActive(active: boolean) {} + + transform(str: String) { + if (arguments.length && str !== this._transform) { + this.onTransform() + } + } + + isTransitionActive(active: Boolean) { + if (arguments.length && active !== this._isTransitionActive) { + this._isTransitionActive = active + this.onSetTransitionActive(active) + } + return this._isTransitioning + } + + isSlideActive(active: Boolean) { + if (arguments.length && active !== this._isSlideActive) { + this._isSlideActive = active + this.onSetDragActive(active) + } + return this._isSlideActive + } + + isOpen(open: Boolean) { + if (arguments.length && open !== this._isOpen) { + this.isTransitionActive(true) + dom.rafPromise().then(() => { + this.isOpen = isOpen + this.onSetIsOpen(open) + }) + } + } + +} + +class ItemSlideGesture { +} diff --git a/src/components/nav-view/nav-view.js b/src/components/nav-view/nav-view.js index 8affa8d28a..67e2228949 100644 --- a/src/components/nav-view/nav-view.js +++ b/src/components/nav-view/nav-view.js @@ -1,171 +1,30 @@ -import {Component, Template, NgElement} from 'angular2/angular2' -import {ComponentConfig} from 'ionic2/config/component-config' -import {Log} from 'ionic2/util' - -import {Compiler} from 'angular2/src/core/compiler/compiler' -import {EventManager} from 'angular2/src/core/events/event_manager' -import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location' +import {DynamicComponent, Parent, NgElement} from 'angular2/angular2' +import {NavViewport} from 'ionic2/components' import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader' -import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader' -import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy' +import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location' -@Component({ - selector: 'ion-nav', +@DynamicComponent({ + selector: '.nav-view', bind: { - initial: 'initial' - }, - services: [] -}) -@Template({ - inline: `` + item: 'item' + } }) export class NavView { constructor( - @NgElement() element: NgElement, - eventManager: EventManager, - compiler: Compiler, - location: PrivateComponentLocation, loader: PrivateComponentLoader, - directiveMetadataReader: DirectiveMetadataReader, - shadowDomStrategy: ShadowDomStrategy + location: PrivateComponentLocation, + @Parent() viewport: NavViewport, + @NgElement() element: NgElement ) { - this.attacher = new Attacher({ - compiler, - element, - location, - loader, - directiveMetadataReader, - eventManager, - shadowDomStrategy - }) - - this._children = [] - console.log('Nav View constructed') - } - - set initial(Type) { - if (!this.initialized) { - this.initialized = true - this.push(Type) - } - } - - /** - * Push a new view into the history stack. - * - * @param view the new view - * @param shouldAnimate whether to animate - */ - push(Type, shouldAnimate) { - //TODO animation - - let current = this._children[this._children.length - 1] - current && current.hide() - - this.attacher.attachComponent(Type).then(child => { - this._children.push(child) - }) - Log.log('NAV: PUSH', Type.name, 'Animate?', shouldAnimate) - } - - /** - * Pop a view off the history - * - * @param shouldAnimate whether to animate - */ - pop(shouldAnimate) { - // TODO animation - let current = this._children.pop() - if (current) { - current.remove() - } - let last = this._children[this._children.length - 1] - if (last) { - last.show() - } - return last - } - - get views() { - return this._views - } - /** - * Set the view stack explicitly. - */ - set views(v) { - this._views = v - } - - // Animate a new view *in* - _animateIn(view) { - - } - - // Animate an old view *out* - _animateOut(view) { - } - -} - -class Attacher { - constructor({ - location, - element, - directiveMetadataReader, - compiler, - shadowDomStrategy, - eventManager - }) { + this.loader = loader this.location = location - this.element = element - this.directiveMetadataReader = directiveMetadataReader - this.compiler = compiler - this.shadowDomStrategy = shadowDomStrategy - this.eventManager = eventManager + this.viewport = viewport + this.domElement = element.domElement } - attachComponent(Type) { - let parentInjector = this.location._elementInjector - let childInjector = parentInjector._proto.instantiate( - /* parent */ parentInjector, - /* host */ null - ) - - let childContainer = document.createElement('div') - childContainer.classList.add('test-child-container') - this.element.domElement.appendChild(childContainer) - - let childNgElement = new NgElement(childContainer) - childInjector._preBuiltObjects = parentInjector._preBuiltObjects - childInjector._preBuiltObjects.element = childNgElement - - let annotation = this.directiveMetadataReader.read(Type).annotation - return this.compiler.compile(Type).then((protoView) => { - let context = childInjector.createPrivateComponent(Type, annotation); - let view = protoView.instantiate(childInjector, this.eventManager) - - view.hydrate(parentInjector.getShadowDomAppInjector(), childInjector, null, context, null) - this.shadowDomStrategy.attachTemplate(childNgElement.domElement, view) - - this.location._view.componentChildViews.push(view) - this.location._view.changeDetector.addChild(view.changeDetector) - - return { - remove() { - // TODO actually remove it from the angular tree - // setTimeout(() => { view.dehydrate() }) - childContainer.parentNode.removeChild(childContainer) - }, - hide() { - // view.dehydrate() - childContainer.style.display = 'none' - }, - show() { - childContainer.style.display = '' - // view.hydrate() - }, - } + set item(navItem) { + this.loader.load(navItem.Class, this.location).then(instance => { + navItem.finishSetup(this, instance) }) } - } diff --git a/src/components/nav-view/nav-view.scss b/src/components/nav-view/nav-view.scss new file mode 100644 index 0000000000..3b7cc3b202 --- /dev/null +++ b/src/components/nav-view/nav-view.scss @@ -0,0 +1,16 @@ +.nav-view { + opacity: 0; + transition: 0.25s; + width: 100%; + height: 100%; +} + +.nav-view.in { + opacity: 1; +} +.nav-view.out { + position: absolute; + top: 0; + left: 0; + opacity: 0; +} diff --git a/src/components/nav-view/test/basic/main.html b/src/components/nav-view/test/basic/main.html deleted file mode 100644 index a942ebb790..0000000000 --- a/src/components/nav-view/test/basic/main.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/components/nav-viewport/nav-viewport.js b/src/components/nav-viewport/nav-viewport.js new file mode 100644 index 0000000000..a3442cf4f6 --- /dev/null +++ b/src/components/nav-viewport/nav-viewport.js @@ -0,0 +1,143 @@ +import {Component, Template, For} from 'angular2/angular2' +import {ComponentConfig} from 'ionic2/config/component-config' +import {NavView} from 'ionic2/components/nav-view/nav-view' +import * as util from 'ionic2/util' + +@Component({ + selector: 'ion-nav-viewport', + bind: { + initial: 'initial' + }, +}) +@Template({ + inline: ` + + `, + directives: [NavView, For] +}) +export class NavViewport { + constructor( + ) { + this.stack = new NavStack(); + } + + set initial(Class) { + if (!this.initialized) { + this.initialized = true + this.push(Class) + } + } + + getAnimation(opts) { + return 'fade' + } + + /** + * Push a new view into the history stack. + * + * @param view the new view + * @param shouldAnimate whether to animate + */ + push(Class, opts = {}) { + //TODO animation + return this.stack.push(Class) + } + + /** + * Pop a view off the history + * + * @param shouldAnimate whether to animate + */ + pop() { + // TODO animation + return this.stack.pop() + } + + // Animate a new view *in* + _animateIn(view) { + + } + + // Animate an old view *out* + _animateOut(view) { + } + +} + +class NavStack { + constructor(navView) { + // array to communicate with angular for loop. When an element is in this array, it really + // exists in the DOM. + this._array = [] + } + rawItems() { + return this._array + } + + push(ComponentClass) { + const item = new NavStackItem(ComponentClass) + let last = this._array[this._array.length - 1] + + this._array.push(item) + return item.setupPromise.then(() => { + // Once the item is successfully instantiated, add it to our public history + item.animateIn() + last && last.animateOut() + }) + } + + pop() { + // public registry: instantly remove to make the removal seem 'synchronous' for our data + const current = this._array[this._array.length - 1] + const previous = this._array[this._array.length - 2] + current.animateOut().then(() => { + util.array.remove(current) + }) + return previous && previous.animateIn() + } +} + +class NavStackItem { + constructor(ComponentClass) { + this.setupPromise = new Promise((resolve) => { + this.finishSetup = (navView, componentInstance) => { + this.navView = navView + this.instance = componentInstance + resolve() + } + }) + this.Class = ComponentClass + } + setInstance(instance) { + this.instance = instance + } + setNavView(navView) { + this.navView = navView + } + + animateIn() { + this.navView.domElement.classList.remove('out') + this.navView.domElement.classList.add('in') + return new Promise(resolve => { + this.navView.domElement.addEventListener('transitionend', (ev) => { + if (ev.target !== this.navView.domElement) { + return + } + resolve() + }) + }) + } + animateOut() { + this.navView.domElement.classList.add('out') + this.navView.domElement.classList.remove('in') + return new Promise(resolve => { + this.navView.domElement.addEventListener('transitionend', (ev) => { + if (ev.target !== this.navView.domElement) { + return + } + resolve() + }) + }) + } +} diff --git a/src/components/nav-viewport/test/basic/main.html b/src/components/nav-viewport/test/basic/main.html new file mode 100644 index 0000000000..9de8f8a062 --- /dev/null +++ b/src/components/nav-viewport/test/basic/main.html @@ -0,0 +1,2 @@ + + diff --git a/src/components/nav-view/test/basic/main.js b/src/components/nav-viewport/test/basic/main.js similarity index 81% rename from src/components/nav-view/test/basic/main.js rename to src/components/nav-viewport/test/basic/main.js index 6e51e918de..103a74c63c 100644 --- a/src/components/nav-view/test/basic/main.js +++ b/src/components/nav-viewport/test/basic/main.js @@ -1,12 +1,12 @@ import {bootstrap} from 'angular2/core' import {Component, Template} from 'angular2/angular2' -import {NavView} from 'ionic2/components/nav-view/nav-view' +import {NavViewport} from 'ionic2/components' import {Log} from 'ionic2/util' import {FirstPage} from 'app/pages/first-page' @Component({ selector: '[ion-app]' }) @Template({ - directives: [NavView], + directives: [NavViewport], url: 'main.html' }) class IonicApp { diff --git a/src/components/nav-view/test/basic/pages/first-page.html b/src/components/nav-viewport/test/basic/pages/first-page.html similarity index 100% rename from src/components/nav-view/test/basic/pages/first-page.html rename to src/components/nav-viewport/test/basic/pages/first-page.html diff --git a/src/components/nav-view/test/basic/pages/first-page.js b/src/components/nav-viewport/test/basic/pages/first-page.js similarity index 69% rename from src/components/nav-view/test/basic/pages/first-page.js rename to src/components/nav-viewport/test/basic/pages/first-page.js index 7e26fc8e30..c6af15e970 100644 --- a/src/components/nav-view/test/basic/pages/first-page.js +++ b/src/components/nav-viewport/test/basic/pages/first-page.js @@ -1,5 +1,5 @@ import {Component, Template, Parent} from 'angular2/angular2' -import {NavView} from 'ionic2/components/nav-view/nav-view' +import {NavViewport} from 'ionic2/components' import {View} from 'ionic2/components/view/view' import {SecondPage} from 'app/pages/second-page' @@ -12,12 +12,12 @@ import {SecondPage} from 'app/pages/second-page' }) export class FirstPage { constructor( - @Parent() navView: NavView + @Parent() viewport: NavViewport ) { - this.navView = navView + this.viewport = viewport } nextPage() { - this.navView.push(SecondPage) + this.viewport.push(SecondPage) } } diff --git a/src/components/nav-view/test/basic/pages/second-page.html b/src/components/nav-viewport/test/basic/pages/second-page.html similarity index 100% rename from src/components/nav-view/test/basic/pages/second-page.html rename to src/components/nav-viewport/test/basic/pages/second-page.html diff --git a/src/components/nav-view/test/basic/pages/second-page.js b/src/components/nav-viewport/test/basic/pages/second-page.js similarity index 67% rename from src/components/nav-view/test/basic/pages/second-page.js rename to src/components/nav-viewport/test/basic/pages/second-page.js index e223650e48..1a146d6889 100644 --- a/src/components/nav-view/test/basic/pages/second-page.js +++ b/src/components/nav-viewport/test/basic/pages/second-page.js @@ -1,5 +1,5 @@ import {Component, Template, Parent} from 'angular2/angular2' -import {NavView} from 'ionic2/components/nav-view/nav-view' +import {NavViewport} from 'ionic2/components' import {View} from 'ionic2/components/view/view' @Component({ @@ -11,11 +11,11 @@ import {View} from 'ionic2/components/view/view' }) export class SecondPage { constructor( - @Parent() navView: NavView + @Parent() viewport: NavViewport ) { - this.navView = navView + this.viewport = viewport } prevPage() { - this.navView.pop() + this.viewport.pop() } } diff --git a/src/components/nav-view/nav-view.spec.js b/src/components/nav-viewport/test/nav-viewport.spec.js similarity index 100% rename from src/components/nav-view/nav-view.spec.js rename to src/components/nav-viewport/test/nav-viewport.spec.js diff --git a/src/util/dom.js b/src/util/dom.js index 06fee40e12..9f02a17782 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -12,7 +12,6 @@ export const raf = nativeRaf || function(callback) { export const rafCancel = nativeRaf ? nativeCancelRaf : function(id) { return window.cancelTimeout(id) } - export function rafPromise() { return new Promise(resolve => raf(resolve)) } @@ -25,9 +24,11 @@ export let css = {} if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) { css.prefix = 'webkit' css.transition = 'webkitTransition' + css.transform = 'webkitTransform' css.transitionEnd = 'webkitTransitionEnd transitionend' } else { css.prefix = '' + css.transform = 'transform' css.transition = 'transition' css.transitionEnd = 'tranistionend' } diff --git a/src/util/util.js b/src/util/util.js index e236709ae3..b60836a834 100644 --- a/src/util/util.js +++ b/src/util/util.js @@ -55,6 +55,22 @@ export class Log { } } +export let array = { + find(arr, cb) { + for (let i = 0, ii = arr.length; i < ii; i++) { + if (cb(arr[i], i)) return arr[i]; + } + }, + remove(arr, item) { + const index = arr.indexOf(item) + if (index === -1) { + return false + } + arr.splice(index, 1) + return true + } +} + export function readQueryParams() { var queryParams = {} const startIndex = window.location.href.indexOf('?')