mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 19:57:22 +08:00
initial version of switching with animations. needs work for logic
This commit is contained in:
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
|
@ -32,6 +32,7 @@
|
||||
"../item/item",
|
||||
"../list/list",
|
||||
"../modal/modal",
|
||||
"../nav-view/nav-view",
|
||||
"../radio/radio",
|
||||
"../search-bar/search-bar",
|
||||
"../switch/switch",
|
||||
|
@ -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) {
|
||||
|
@ -27,20 +27,82 @@ import {
|
||||
<content select="ion-secondary-options"></content>
|
||||
<content select="ion-secondary-swipe-buttons"></content>
|
||||
`,
|
||||
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 {
|
||||
}
|
||||
|
@ -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: `<content></content>`
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
16
src/components/nav-view/nav-view.scss
Normal file
16
src/components/nav-view/nav-view.scss
Normal file
@ -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;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
<ion-nav [initial]="initial">
|
||||
</ion-nav>
|
143
src/components/nav-viewport/nav-viewport.js
Normal file
143
src/components/nav-viewport/nav-viewport.js
Normal file
@ -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: `
|
||||
<section class="nav-view" *for="#item of stack.rawItems()" [item]="item">
|
||||
</section>
|
||||
`,
|
||||
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()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
2
src/components/nav-viewport/test/basic/main.html
Normal file
2
src/components/nav-viewport/test/basic/main.html
Normal file
@ -0,0 +1,2 @@
|
||||
<ion-nav-viewport [initial]="initial">
|
||||
</ion-nav-viewport>
|
@ -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 {
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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'
|
||||
}
|
||||
|
@ -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('?')
|
||||
|
Reference in New Issue
Block a user