mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 04:14:21 +08:00
refactor(NavController): restructuring and perf improvements
This commit is contained in:
@ -251,7 +251,7 @@ export class Animation {
|
||||
});
|
||||
}
|
||||
|
||||
if (self._duration > 64) {
|
||||
if (self._duration > 32) {
|
||||
// begin each animation when everything is rendered in their starting point
|
||||
// give the browser some time to render everything in place before starting
|
||||
setTimeout(kickoff, this._opts.renderDelay);
|
||||
@ -550,7 +550,7 @@ class Animate {
|
||||
|
||||
this.toEffect = parseEffect(toEffect);
|
||||
|
||||
this.shouldAnimate = (duration > 64);
|
||||
this.shouldAnimate = (duration > 32);
|
||||
|
||||
if (!this.shouldAnimate) {
|
||||
return inlineStyle(ele, this.toEffect);
|
||||
|
@ -82,79 +82,123 @@ body {
|
||||
background-color: $background-color;
|
||||
}
|
||||
|
||||
ion-app {
|
||||
ion-app,
|
||||
ion-nav,
|
||||
ion-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
}
|
||||
|
||||
ion-nav {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ion-pane,
|
||||
ion-view.pane-view {
|
||||
ion-navbar-section {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
ion-content-section {
|
||||
display: block;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ion-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
ion-content {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
background-color: $background-color;
|
||||
}
|
||||
|
||||
scroll-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: block;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: scroll-position;
|
||||
}
|
||||
|
||||
ion-view.pane-view {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
ion-view {
|
||||
display: none;
|
||||
ion-tab-bar {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
ion-tab-section {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: $background-color;
|
||||
|
||||
transform: translateZ(0px);
|
||||
|
||||
&.show-view {
|
||||
display: flex;
|
||||
}
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[no-navbar] > ion-navbar-section {
|
||||
display: none;
|
||||
ion-page.tab-subpage {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ion-navbar-section {
|
||||
position: relative;
|
||||
min-height: 4.4rem;
|
||||
z-index: $z-index-navbar-section;
|
||||
ion-navbar {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
min-height: 50px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ion-content-section {
|
||||
position: relative;
|
||||
z-index: $z-index-content-section;
|
||||
flex: 1;
|
||||
order: $flex-order-view-content;
|
||||
ion-navbar-section ion-navbar.toolbar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[hidden],
|
||||
template,
|
||||
root-anchor {
|
||||
display: none !important;
|
||||
ion-toolbar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
ion-toolbar[position=bottom] {
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ion-scrollbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.hide-navtive-scrollbar ion-content {
|
||||
padding-right: 20px;
|
||||
width: calc(100% + 20px);
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ button,
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
will-change: background-color, opacity;
|
||||
transition: background-color, opacity 100ms linear;
|
||||
|
||||
margin: $button-margin;
|
||||
|
@ -1,65 +0,0 @@
|
||||
|
||||
// Content
|
||||
// --------------------------------------------------
|
||||
|
||||
$content-background-color: $background-color !default;
|
||||
|
||||
|
||||
ion-content {
|
||||
position: relative;
|
||||
display: block;
|
||||
flex: 1;
|
||||
background-color: $content-background-color;
|
||||
}
|
||||
|
||||
scroll-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
overflow-y: scroll; // has to be scroll for momentum scrolling, not auto
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: scroll-position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
$content-padding: 16px !default;
|
||||
|
||||
|
||||
[padding],
|
||||
[padding] > scroll-content {
|
||||
padding: $content-padding;
|
||||
}
|
||||
|
||||
[padding-top] {
|
||||
padding-top: $content-padding;
|
||||
}
|
||||
|
||||
[padding-right] {
|
||||
padding-right: $content-padding;
|
||||
}
|
||||
|
||||
[padding-bottom] {
|
||||
padding-bottom: $content-padding;
|
||||
}
|
||||
|
||||
[padding-left] {
|
||||
padding-left: $content-padding;
|
||||
}
|
||||
|
||||
[padding-vertical] {
|
||||
padding-top: $content-padding;
|
||||
padding-bottom: $content-padding;
|
||||
}
|
||||
|
||||
[padding-horizontal] {
|
||||
padding-right: $content-padding;
|
||||
padding-left: $content-padding;
|
||||
}
|
@ -42,6 +42,7 @@ export class Content extends Ion {
|
||||
|
||||
if (viewCtrl) {
|
||||
viewCtrl.setContent(this);
|
||||
viewCtrl.setContentRef(elementRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,11 @@ export class Menu extends Ion {
|
||||
|
||||
this._type = new menuTypeCls(this);
|
||||
this.type = type;
|
||||
|
||||
if (this.config.get('animate') === false) {
|
||||
this._type.open.duration(33);
|
||||
this._type.close.duration(33);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,22 +10,3 @@ $modal-inset-mode-right: 20% !default;
|
||||
$modal-inset-mode-bottom: 20% !default;
|
||||
$modal-inset-mode-left: 20% !default;
|
||||
$modal-inset-mode-min-height: 240px !default;
|
||||
|
||||
|
||||
ion-modal {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: $z-index-overlay;
|
||||
overflow: hidden;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
background-color: $modal-background-color;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
transform: translate3d(0px, 100%, 0px);
|
||||
&.show-overlay {
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import {Injectable} from 'angular2/angular2';
|
||||
import {OverlayController} from '../overlay/overlay-controller';
|
||||
import {Config} from '../../config/config';
|
||||
import {Animation} from '../../animations/animation';
|
||||
import {makeComponent} from '../../config/decorators';
|
||||
import * as util from 'ionic/util';
|
||||
|
||||
/**
|
||||
@ -46,11 +45,7 @@ export class Modal {
|
||||
* @returns {TODO} TODO
|
||||
*/
|
||||
open(componentType: Type, opts={}) {
|
||||
let modalComponent = makeComponent(componentType, {
|
||||
selector: 'ion-modal'
|
||||
});
|
||||
|
||||
return this.ctrl.open(OVERLAY_TYPE, modalComponent, util.extend(this._defaults, opts));
|
||||
return this.ctrl.open(OVERLAY_TYPE, componentType, util.extend(this._defaults, opts));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,3 +8,7 @@ $navbar-ios-height: 4.4rem !default;
|
||||
ion-navbar-section {
|
||||
min-height: $navbar-ios-height;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
transform: translateZ(0px);
|
||||
}
|
||||
|
@ -4,17 +4,11 @@
|
||||
|
||||
|
||||
ion-navbar.toolbar {
|
||||
position: absolute;
|
||||
display: none;
|
||||
|
||||
&.show-navbar {
|
||||
display: flex;
|
||||
}
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
order: map-get($toolbar-order, backButton);
|
||||
transform: translateZ(0px);
|
||||
order: map-get($toolbar-order, backButton);
|
||||
|
||||
display: none;
|
||||
&.show-back-button {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Component, Directive, Optional, ElementRef, Renderer, TemplateRef, forwardRef, Inject} from 'angular2/angular2';
|
||||
import {Component, Directive, Optional, ElementRef, Renderer, TemplateRef, forwardRef, Inject, ViewContainerRef} from 'angular2/angular2';
|
||||
|
||||
import {Ion} from '../ion';
|
||||
import {Icon} from '../icon/icon';
|
||||
@ -65,6 +65,9 @@ class BackButtonText extends Ion {
|
||||
'<ng-content select="ion-nav-items[secondary]"></ng-content>' +
|
||||
'</div>' +
|
||||
'<div class="toolbar-background"></div>',
|
||||
host: {
|
||||
'[hidden]': '_hidden'
|
||||
},
|
||||
directives: [BackButton, BackButtonText, Icon]
|
||||
})
|
||||
export class Navbar extends ToolbarBase {
|
||||
@ -105,6 +108,10 @@ export class Navbar extends ToolbarBase {
|
||||
this.app.setTitle(this.getTitleText());
|
||||
}
|
||||
|
||||
setHidden(isHidden) {
|
||||
this._hidden = isHidden
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -118,9 +125,13 @@ export class Navbar extends ToolbarBase {
|
||||
})
|
||||
export class NavbarTemplate {
|
||||
constructor(
|
||||
@Optional() viewCtrl: ViewController,
|
||||
@Optional() templateRef: TemplateRef
|
||||
viewContainerRef: ViewContainerRef,
|
||||
templateRef: TemplateRef,
|
||||
@Optional() viewCtrl: ViewController
|
||||
) {
|
||||
viewCtrl && viewCtrl.setNavbarTemplateRef(templateRef);
|
||||
if (viewCtrl) {
|
||||
viewCtrl.setNavbarTemplateRef(templateRef);
|
||||
viewCtrl.setNavbarViewRef(viewContainerRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {Compiler, ElementRef, Injector, provide, NgZone, DynamicComponentLoader, AppViewManager, Renderer} from 'angular2/angular2';
|
||||
|
||||
import {Ion} from '../ion';
|
||||
import {makeComponent} from '../../config/decorators';
|
||||
import {IonicApp} from '../app/app';
|
||||
import {Config} from '../../config/config';
|
||||
import {ViewController} from './view-controller';
|
||||
@ -445,6 +444,9 @@ export class NavController extends Ion {
|
||||
if (!opts.animation) {
|
||||
opts.animation = this.config.get('viewTransition');
|
||||
}
|
||||
if (this.config.get('animate') === false) {
|
||||
opts.animate = false;
|
||||
}
|
||||
|
||||
// wait for the new view to complete setup
|
||||
enteringView.stage(() => {
|
||||
@ -508,34 +510,51 @@ export class NavController extends Ion {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
*/
|
||||
compileView(componentType) {
|
||||
// create a new ion-view annotation
|
||||
let viewComponentType = makeComponent(componentType, {
|
||||
selector: 'ion-view',
|
||||
host: {
|
||||
'[class.pane-view]': '_paneView'
|
||||
}
|
||||
});
|
||||
|
||||
// compile the Component
|
||||
return this._compiler.compileInHost(viewComponentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
*/
|
||||
loadNextToAnchor(type, location, viewCtrl) {
|
||||
loadPage(viewCtrl, navbarContainerRef, done) {
|
||||
let providers = this.providers.concat(Injector.resolve([
|
||||
provide(ViewController, {useValue: viewCtrl}),
|
||||
provide(NavParams, {useValue: viewCtrl.params})
|
||||
]));
|
||||
|
||||
return this._loader.loadNextToLocation(type, location, providers);
|
||||
this._loader.loadIntoLocation(viewCtrl.componentType, this.elementRef, 'contents', providers).then(componentRef => {
|
||||
|
||||
viewCtrl.addDestroy(() => {
|
||||
componentRef.dispose();
|
||||
});
|
||||
|
||||
// a new ComponentRef has been created
|
||||
// set the ComponentRef's instance to this ViewController
|
||||
viewCtrl.setInstance(componentRef.instance);
|
||||
|
||||
// remember the ElementRef to the ion-page elementRef that was just created
|
||||
viewCtrl.setPageRef(componentRef.location);
|
||||
|
||||
if (!navbarContainerRef) {
|
||||
navbarContainerRef = viewCtrl.getNavbarViewRef();
|
||||
}
|
||||
|
||||
let navbarTemplateRef = viewCtrl.getNavbarTemplateRef();
|
||||
if (navbarContainerRef && navbarTemplateRef) {
|
||||
let navbarView = navbarContainerRef.createEmbeddedView(navbarTemplateRef);
|
||||
|
||||
viewCtrl.addDestroy(() => {
|
||||
let index = navbarContainerRef.indexOf(navbarView);
|
||||
if (index > -1) {
|
||||
navbarContainerRef.remove(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this._views.length === 1) {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
setTimeout(() => {
|
||||
this.renderer.setElementClass(this.elementRef, 'has-views', true);
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
done(viewCtrl);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -543,6 +562,7 @@ export class NavController extends Ion {
|
||||
* TODO
|
||||
*/
|
||||
swipeBackStart() {
|
||||
return;
|
||||
if (!this.app.isEnabled() || !this.canSwipeBack()) {
|
||||
return;
|
||||
}
|
||||
@ -594,6 +614,7 @@ export class NavController extends Ion {
|
||||
* @param {TODO} progress TODO
|
||||
*/
|
||||
swipeBackProgress(value) {
|
||||
return;
|
||||
if (this._sbTrans) {
|
||||
// continue to disable the app while actively dragging
|
||||
this.app.setEnabled(false, 4000);
|
||||
@ -610,6 +631,7 @@ export class NavController extends Ion {
|
||||
* @param {number} rate How fast it closes
|
||||
*/
|
||||
swipeBackEnd(completeSwipeBack, rate) {
|
||||
return;
|
||||
if (!this._sbTrans) return;
|
||||
|
||||
// disables the app during the transition
|
||||
@ -672,6 +694,7 @@ export class NavController extends Ion {
|
||||
* TODO
|
||||
*/
|
||||
_sbComplete() {
|
||||
return;
|
||||
if (this.canSwipeBack()) {
|
||||
// it is possible to swipe back
|
||||
|
||||
@ -789,16 +812,6 @@ export class NavController extends Ion {
|
||||
});
|
||||
}
|
||||
|
||||
addHasViews() {
|
||||
if (this._views.length === 1) {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
setTimeout(() => {
|
||||
this.renderer.setElementClass(this.elementRef, 'has-views', true);
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* @param {TODO} nbContainer TODO
|
||||
|
@ -130,18 +130,10 @@ import {NavController} from './nav-controller';
|
||||
defaultInputs: {
|
||||
'swipeBackEnabled': true
|
||||
},
|
||||
template: '<template pane-anchor></template>',
|
||||
directives: [forwardRef(() => NavPaneAnchor)]
|
||||
template: '<template #contents></template>'
|
||||
})
|
||||
export class Nav extends NavController {
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* @param {NavController} hostNavCtrl TODO
|
||||
* @param {Injector} injector TODO
|
||||
* @param {ElementRef} elementRef TODO
|
||||
* @param {NgZone} zone TODO
|
||||
*/
|
||||
constructor(
|
||||
@Optional() hostNavCtrl: NavController,
|
||||
app: IonicApp,
|
||||
@ -154,7 +146,6 @@ export class Nav extends NavController {
|
||||
renderer: Renderer
|
||||
) {
|
||||
super(hostNavCtrl, app, config, elementRef, compiler, loader, viewManager, zone, renderer);
|
||||
this.panes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,263 +162,7 @@ export class Nav extends NavController {
|
||||
}
|
||||
|
||||
// default the swipe back to be enabled
|
||||
let isSwipeBackEnabled = (this.swipeBackEnabled || '').toString() !== 'false';
|
||||
this.isSwipeBackEnabled( isSwipeBackEnabled );
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
* @param {TODO} componentType TODO
|
||||
* @param {TODO} hostProtoViewRef TODO
|
||||
* @param {TODO} viewCtrl TODO
|
||||
* @param {Function} done TODO
|
||||
* @return {TODO} TODO
|
||||
*/
|
||||
loadContainer(componentType, hostProtoViewRef, viewCtrl, done) {
|
||||
// this gets or creates the Pane which similar nav items live in
|
||||
// Nav items with just a navbar/content would all use the same Pane
|
||||
// Tabs and view's without a navbar would get a different Panes
|
||||
let structure = this.getStructure(hostProtoViewRef);
|
||||
|
||||
if (structure.tabs) {
|
||||
// the component being loaded is an <ion-tabs>
|
||||
// Tabs is essentially a pane, cuz it has its own navbar and content containers
|
||||
this.loadNextToAnchor(componentType, this.anchorElementRef(), viewCtrl).then(componentRef => {
|
||||
|
||||
componentRef.instance._paneView = true;
|
||||
|
||||
viewCtrl.disposals.push(() => {
|
||||
componentRef.dispose();
|
||||
});
|
||||
|
||||
viewCtrl.onReady().then(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} else {
|
||||
// normal ion-view going into pane
|
||||
this.getPane(structure, viewCtrl, (pane) => {
|
||||
// add the content of the view into the pane's content area
|
||||
this.loadNextToAnchor(componentType, pane.contentAnchorRef, viewCtrl).then(componentRef => {
|
||||
|
||||
viewCtrl.disposals.push(() => {
|
||||
componentRef.dispose();
|
||||
|
||||
// remove the pane if there are no view items left
|
||||
pane.totalViews--;
|
||||
if (pane.totalViews === 0) {
|
||||
pane.dispose && pane.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// count how many ViewControllers are in this pane
|
||||
pane.totalViews++;
|
||||
|
||||
// a new ComponentRef has been created
|
||||
// set the ComponentRef's instance to this ViewController
|
||||
viewCtrl.setInstance(componentRef.instance);
|
||||
|
||||
// remember the ElementRef to the content that was just created
|
||||
viewCtrl.setContentRef(componentRef.location);
|
||||
|
||||
// get the NavController's container for navbars, which is
|
||||
// the place this NavController will add each ViewController's navbar
|
||||
let navbarContainerRef = pane.navbarContainerRef;
|
||||
|
||||
// get this ViewController's navbar TemplateRef, which may not
|
||||
// exist if the ViewController's template didn't have an <ion-navbar *navbar>
|
||||
let navbarTemplateRef = viewCtrl.getNavbarTemplateRef();
|
||||
|
||||
// create the navbar view if the pane has a navbar container, and the
|
||||
// ViewController's instance has a navbar TemplateRef to go to inside of it
|
||||
if (navbarContainerRef && navbarTemplateRef) {
|
||||
let navbarView = navbarContainerRef.createEmbeddedView(navbarTemplateRef, -1);
|
||||
|
||||
viewCtrl.disposals.push(() => {
|
||||
let index = navbarContainerRef.indexOf(navbarView);
|
||||
if (index > -1) {
|
||||
navbarContainerRef.remove(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.addHasViews();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
* @param {TODO} structure TODO
|
||||
* @param {TODO} viewCtrl TODO
|
||||
* @param {Function} done TODO
|
||||
* @return {TODO} TODO
|
||||
*/
|
||||
getPane(structure, viewCtrl, done) {
|
||||
let pane = this.panes[this.panes.length - 1];
|
||||
|
||||
if (pane && pane.navbar === structure.navbar) {
|
||||
// the last pane's structure is the same as the one
|
||||
// this ViewController will need, so reuse it
|
||||
done(pane);
|
||||
|
||||
} else {
|
||||
// create a new nav pane
|
||||
this._loader.loadNextToLocation(Pane, this.anchorElementRef(), this.bindings).then(componentRef => {
|
||||
|
||||
// get the pane reference
|
||||
pane = this.newPane;
|
||||
this.newPane = null;
|
||||
|
||||
pane.showNavbar(structure.navbar);
|
||||
pane.dispose = () => {
|
||||
componentRef.dispose();
|
||||
this.panes.splice(this.panes.indexOf(pane), 1);
|
||||
};
|
||||
|
||||
this.panes.push(pane);
|
||||
|
||||
done(pane);
|
||||
|
||||
}, loaderErr => {
|
||||
console.error(loaderErr);
|
||||
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
* @param {TODO} pane TODO
|
||||
* @return {TODO} TODO
|
||||
*/
|
||||
addPane(pane) {
|
||||
this.newPane = pane;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
* @param {TODO} componentProtoViewRef TODO
|
||||
* @return {TODO} TODO
|
||||
*/
|
||||
getStructure(componentProtoViewRef) {
|
||||
let templateCmds = componentProtoViewRef._protoView.templateCmds;
|
||||
let compiledTemplateData, directives;
|
||||
let i, ii, j, jj, k, kk;
|
||||
|
||||
for (i = 0, ii = templateCmds.length; i < ii; i++) {
|
||||
if (templateCmds[i].template) {
|
||||
compiledTemplateData = templateCmds[i].template.getData(templateCmds[i].templateId);
|
||||
if (compiledTemplateData) {
|
||||
for (j = 0, jj = compiledTemplateData.commands.length; j < jj; j++) {
|
||||
directives = compiledTemplateData.commands[j].directives;
|
||||
|
||||
if (directives && (kk = directives.length)) {
|
||||
|
||||
for (k = 0; k < kk; k++) {
|
||||
|
||||
if (directives[k].name == 'NavbarTemplate') {
|
||||
return { navbar: true };
|
||||
}
|
||||
|
||||
if (directives[k].name == 'Tabs') {
|
||||
return { tabs: true };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({selector: 'template[pane-anchor]'})
|
||||
class NavPaneAnchor {
|
||||
constructor(@Host() nav: Nav, elementRef: ElementRef) {
|
||||
nav.anchorElementRef(elementRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({selector: 'template[navbar-anchor]'})
|
||||
class NavBarAnchor {
|
||||
constructor(
|
||||
@Host() @Inject(forwardRef(() => Pane)) pane: Pane,
|
||||
viewContainerRef: ViewContainerRef
|
||||
) {
|
||||
pane.navbarContainerRef = viewContainerRef;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({selector: 'template[content-anchor]'})
|
||||
class ContentAnchor {
|
||||
constructor(
|
||||
@Host() @Inject(forwardRef(() => Pane)) pane: Pane,
|
||||
elementRef: ElementRef
|
||||
) {
|
||||
pane.contentAnchorRef = elementRef;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-pane',
|
||||
template:
|
||||
'<ion-navbar-section>' +
|
||||
'<template navbar-anchor></template>' +
|
||||
'</ion-navbar-section>' +
|
||||
'<ion-content-section>' +
|
||||
'<template content-anchor></template>' +
|
||||
'</ion-content-section>',
|
||||
directives: [NavBarAnchor, ContentAnchor]
|
||||
})
|
||||
class Pane {
|
||||
constructor(
|
||||
nav: Nav,
|
||||
elementRef: ElementRef,
|
||||
renderer: Renderer
|
||||
) {
|
||||
this.zIndex = (nav.panes.length ? nav.panes[nav.panes.length - 1].zIndex + 1 : 0);
|
||||
renderer.setElementStyle(elementRef, 'zIndex', this.zIndex);
|
||||
|
||||
nav.addPane(this);
|
||||
this.totalViews = 0;
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
showNavbar(hasNavbar) {
|
||||
this.navbar = hasNavbar;
|
||||
this.renderer.setElementAttribute(this.elementRef, 'no-navbar', hasNavbar ? null : '' );
|
||||
this.isSwipeBackEnabled( (this.swipeBackEnabled || '').toString() !== 'false' );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
|
||||
it('should go from 1 to 2', function() {
|
||||
element(by.css('#from1To2')).click();
|
||||
element(by.css('.e2eFrom1To2')).click();
|
||||
});
|
||||
|
||||
it('should go from 2 to 3', function() {
|
||||
element(by.css('#from2To3')).click();
|
||||
element(by.css('.e2eFrom2To3')).click();
|
||||
});
|
||||
|
||||
it('should go from 3 to 2', function() {
|
||||
element(by.css('#from3To2')).click();
|
||||
element(by.css('.e2eFrom3To2')).click();
|
||||
});
|
||||
|
||||
it('should go from 2 to 1', function() {
|
||||
element(by.css('#from2To1')).click();
|
||||
element(by.css('.e2eFrom2To1')).click();
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ import {NavParams, NavController} from 'ionic/ionic';
|
||||
@Page({
|
||||
template: `
|
||||
<ion-navbar *navbar>
|
||||
<ion-title><ion-segment><ion-segment-button>Friends</ion-segment-button><ion-segment-button>Enemies</ion-segment-button></ion-segment></ion-title>
|
||||
<ion-title>{{title}}</ion-title>
|
||||
<ion-nav-items primary>
|
||||
<button><icon star></icon></button>
|
||||
</ion-nav-items>
|
||||
@ -16,14 +16,15 @@ import {NavParams, NavController} from 'ionic/ionic';
|
||||
</ion-navbar>
|
||||
<ion-content padding>
|
||||
<p>{{title}}</p>
|
||||
<p><button id="from1To2" primary (click)="push()">Push (Go to 2nd)</button></p>
|
||||
<p><button [nav-push]="[pushPage, {id: 42}]">Push w/ [nav-push] array (Go to 2nd)</button></p>
|
||||
<p><button [nav-push]="pushPage" [nav-params]="{id:40}">Push w/ [nav-push] and [nav-params] (Go to 2nd)</button></p>
|
||||
<p><button [nav-push]="[\'FirstPage\', {id: 22}]">Push w/ [nav-push] array and string view name (Go to 2nd)</button></p>
|
||||
<p><button nav-push="FirstPage" [nav-params]="{id: 23}">Push w/ nav-push and [nav-params] (Go to 2nd)</button></p>
|
||||
<p><button (click)="setViews()">setViews() (Go to 3rd, no history)</button></p>
|
||||
<icon class="ion-ios-arrow-back"></icon>
|
||||
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
|
||||
<p><button class="e2eFrom1To2" (click)="pushFullPage()">Push to FullPage</button></p>
|
||||
<p><button (click)="pushPrimaryHeaderPage()">Push to PrimaryHeaderPage</button></p>
|
||||
<p><button (click)="pushAnother()">Push to AnotherPage</button></p>
|
||||
<p><button [nav-push]="[pushPage, {id: 42}]">Push FullPage w/ [nav-push] array</button></p>
|
||||
<p><button [nav-push]="pushPage" [nav-params]="{id:40}">Push w/ [nav-push] and [nav-params]</button></p>
|
||||
<p><button [nav-push]="[\'FirstPage\', {id: 22}]">Push w/ [nav-push] array and string view name</button></p>
|
||||
<p><button nav-push="FirstPage" [nav-params]="{id: 23}">Push w/ nav-push and [nav-params]</button></p>
|
||||
<p><button (click)="setViews()">setViews() (Go to PrimaryHeaderPage)</button></p>
|
||||
<p><button (click)="nav.pop()">Pop</button></p>
|
||||
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
|
||||
</ion-content>`
|
||||
})
|
||||
@ -36,19 +37,27 @@ class FirstPage {
|
||||
this.nav = nav;
|
||||
this.title = 'First Page';
|
||||
|
||||
this.pushPage = SecondPage;
|
||||
this.pushPage = FullPage;
|
||||
}
|
||||
|
||||
setViews() {
|
||||
let items = [
|
||||
ThirdPage
|
||||
PrimaryHeaderPage
|
||||
];
|
||||
|
||||
this.nav.setViews(items);
|
||||
}
|
||||
|
||||
push() {
|
||||
this.nav.push(SecondPage, { id: 8675309, myData: [1,2,3,4] } );
|
||||
pushPrimaryHeaderPage() {
|
||||
this.nav.push(PrimaryHeaderPage);
|
||||
}
|
||||
|
||||
pushFullPage() {
|
||||
this.nav.push(FullPage, { id: 8675309, myData: [1,2,3,4] } );
|
||||
}
|
||||
|
||||
pushAnother() {
|
||||
this.nav.push(AnotherPage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,42 +65,45 @@ class FirstPage {
|
||||
@Page({
|
||||
template: `
|
||||
<ion-content padding>
|
||||
<h1>Second page</h1>
|
||||
<h1>Full page</h1>
|
||||
<p>This page does not have a nav bar!</p>
|
||||
<p><button (click)="pop()">Pop (Go back to 1st)</button></p>
|
||||
<p><button id="from2To1" nav-pop>Pop with NavPop (Go back to 1st)</button></p>
|
||||
<p><button id="from2To3" (click)="push()">Push (Go to 3rd)</button></p>
|
||||
<p><button (click)="setViews()">setViews() (Go to 3rd, FirstPage 1st in history)</button></p>
|
||||
<div class="green"><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f></div>
|
||||
<p><button (click)="nav.pop()">Pop</button></p>
|
||||
<p><button class="e2eFrom2To3" (click)="pushPrimaryHeaderPage()">Push to PrimaryHeaderPage</button></p>
|
||||
<p><button (click)="pushAnother()">Push to AnotherPage</button></p>
|
||||
<p><button (click)="pushFirstPage()">Push to FirstPage</button></p>
|
||||
<p><button class="e2eFrom2To1" nav-pop>Pop with NavPop (Go back to 1st)</button></p>
|
||||
<p><button (click)="setViews()">setViews() (Go to PrimaryHeaderPage, FirstPage 1st in history)</button></p>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
class SecondPage {
|
||||
class FullPage {
|
||||
constructor(
|
||||
nav: NavController,
|
||||
params: NavParams
|
||||
) {
|
||||
this.nav = nav;
|
||||
this.params = params;
|
||||
|
||||
console.log('Second page params:', params);
|
||||
}
|
||||
|
||||
setViews() {
|
||||
let items = [
|
||||
FirstPage,
|
||||
ThirdPage
|
||||
PrimaryHeaderPage
|
||||
];
|
||||
|
||||
this.nav.setViews(items);
|
||||
}
|
||||
|
||||
pop() {
|
||||
this.nav.pop();
|
||||
pushPrimaryHeaderPage() {
|
||||
this.nav.push(PrimaryHeaderPage);
|
||||
}
|
||||
|
||||
push() {
|
||||
this.nav.push(ThirdPage);
|
||||
pushAnother() {
|
||||
this.nav.push(AnotherPage);
|
||||
}
|
||||
|
||||
pushFirstPage() {
|
||||
this.nav.push(FirstPage);
|
||||
}
|
||||
|
||||
}
|
||||
@ -99,29 +111,32 @@ class SecondPage {
|
||||
|
||||
@Page({
|
||||
template: `
|
||||
<ion-navbar *navbar><ion-title>Third Page Header</ion-title></ion-navbar>
|
||||
<ion-navbar *navbar primary>
|
||||
<ion-title>Primary Color Page Header</ion-title>
|
||||
</ion-navbar>
|
||||
<ion-content padding>
|
||||
<p><button (click)="push()">Push (Go to 4th)</button></p>
|
||||
<p><button id="from3To2" (click)="pop()">Pop (Go back to 2nd)</button></p>
|
||||
<p><button class="e2eFrom3To2" (click)="nav.pop()">Pop</button></p>
|
||||
<p><button (click)="pushAnother()">Push to AnotherPage</button></p>
|
||||
<p><button (click)="pushFullPage()">Push to FullPage</button></p>
|
||||
<p><button id="insert" (click)="insert()">Insert first page into history before this</button></p>
|
||||
<p><button id="remove" (click)="removeSecond()">Remove second page in history</button></p>
|
||||
<div class="yellow"><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f></div>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
class ThirdPage {
|
||||
class PrimaryHeaderPage {
|
||||
constructor(
|
||||
nav: NavController
|
||||
) {
|
||||
this.nav = nav
|
||||
}
|
||||
|
||||
push() {
|
||||
this.nav.push(FourthPage);
|
||||
pushAnother() {
|
||||
this.nav.push(AnotherPage);
|
||||
}
|
||||
|
||||
pop() {
|
||||
this.nav.pop()
|
||||
pushFullPage() {
|
||||
this.nav.push(FullPage, { id: 8675309, myData: [1,2,3,4] } );
|
||||
}
|
||||
|
||||
insert() {
|
||||
@ -137,28 +152,40 @@ class ThirdPage {
|
||||
|
||||
@Page({
|
||||
template: `
|
||||
<ion-navbar *navbar><ion-title>Fourth Page Header</ion-title></ion-navbar>
|
||||
<ion-navbar *navbar>
|
||||
<ion-title>Another Page Header</ion-title>
|
||||
</ion-navbar>
|
||||
<ion-content padding>
|
||||
<p>
|
||||
<button (click)="nav.pop()">Pop (Go back to 3rd)</button>
|
||||
</p>
|
||||
<p><button (click)="nav.pop()">Pop</button></p>
|
||||
<p><button (click)="pushFullPage()">Push to FullPage</button></p>
|
||||
<p><button (click)="pushPrimaryHeaderPage()">Push to PrimaryHeaderPage</button></p>
|
||||
<p><button (click)="pushFirstPage()">Push to FirstPage</button></p>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
class FourthPage {
|
||||
class AnotherPage {
|
||||
constructor(
|
||||
nav: NavController
|
||||
) {
|
||||
this.nav = nav
|
||||
}
|
||||
|
||||
pushFullPage() {
|
||||
this.nav.push(FullPage);
|
||||
}
|
||||
|
||||
pushPrimaryHeaderPage() {
|
||||
this.nav.push(PrimaryHeaderPage);
|
||||
}
|
||||
|
||||
pushFirstPage() {
|
||||
this.nav.push(FirstPage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@App({
|
||||
template: `
|
||||
<ion-nav [root]="root"></ion-nav>
|
||||
`,
|
||||
views: [FirstPage, SecondPage, ThirdPage]
|
||||
template: `<ion-nav [root]="root"></ion-nav>`
|
||||
})
|
||||
class E2EApp {
|
||||
constructor() {
|
||||
|
@ -12,15 +12,7 @@ export class ViewController {
|
||||
this.params = new NavParams(params);
|
||||
this.instance = null;
|
||||
this.state = 0;
|
||||
this.disposals = [];
|
||||
}
|
||||
|
||||
setContent(content) {
|
||||
this._content = content;
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this._content;
|
||||
this._destroys = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,25 +26,18 @@ export class ViewController {
|
||||
return done();
|
||||
}
|
||||
|
||||
// compile the component and create a ProtoViewRef
|
||||
navCtrl.compileView(this.componentType).then(hostProtoViewRef => {
|
||||
// get the pane the NavController wants to use
|
||||
// the pane is where all this content will be placed into
|
||||
navCtrl.loadPage(this, null, () => {
|
||||
|
||||
if (this.shouldDestroy) return done();
|
||||
|
||||
// get the pane the NavController wants to use
|
||||
// the pane is where all this content will be placed into
|
||||
navCtrl.loadContainer(this.componentType, hostProtoViewRef, this, () => {
|
||||
|
||||
// this ViewController instance has finished loading
|
||||
try {
|
||||
this.loaded();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
// this ViewController instance has finished loading
|
||||
try {
|
||||
this.loaded();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
@ -87,55 +72,66 @@ export class ViewController {
|
||||
return this.index === 0;
|
||||
}
|
||||
|
||||
addDestroy(destroyFn) {
|
||||
this._destroys.push(destroyFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
destroy() {
|
||||
for (let i = 0; i < this.disposals.length; i++) {
|
||||
this.disposals[i]();
|
||||
for (let i = 0; i < this._destroys.length; i++) {
|
||||
this._destroys[i]();
|
||||
}
|
||||
this._destroys = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setNavbarTemplateRef(templateRef) {
|
||||
this._nbTmpRef = templateRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getNavbarTemplateRef() {
|
||||
return this._nbTmpRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* @returns {TODO} TODO
|
||||
*/
|
||||
setContentRef(contentElementRef) {
|
||||
this._cntRef = contentElementRef;
|
||||
getNavbarViewRef() {
|
||||
return this._nbVwRef;
|
||||
}
|
||||
|
||||
setNavbarViewRef(viewContainerRef) {
|
||||
this._nbVwRef = viewContainerRef;
|
||||
}
|
||||
|
||||
setPageRef(elementRef) {
|
||||
this._pgRef = elementRef;
|
||||
}
|
||||
|
||||
pageRef() {
|
||||
return this._pgRef;
|
||||
}
|
||||
|
||||
setContentRef(elementRef) {
|
||||
this._cntRef = elementRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* @returns {TODO} TODO
|
||||
*/
|
||||
contentRef() {
|
||||
return this._cntRef;
|
||||
}
|
||||
|
||||
setNavbar(navbarView) {
|
||||
this._nbVw = navbarView;
|
||||
setContent(directive) {
|
||||
this._cntDir = directive;
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this._cntDir;
|
||||
}
|
||||
|
||||
setNavbar(directive) {
|
||||
this._nbDir = directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* @returns {TODO} TODO
|
||||
*/
|
||||
getNavbar() {
|
||||
return this._nbVw;
|
||||
return this._nbDir;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,18 +259,8 @@ export class ViewController {
|
||||
}
|
||||
|
||||
domCache(isActiveView, isPreviousView) {
|
||||
let renderInDom = (isActiveView || isPreviousView);
|
||||
|
||||
let contentRef = this.contentRef();
|
||||
if (contentRef) {
|
||||
// the active view, and the previous view should have the 'show-view' css class
|
||||
// all others, like a cached page 2 back, should now have 'show-view' so it's not rendered
|
||||
contentRef.nativeElement.classList[renderInDom ? 'add' : 'remove' ]('show-view');
|
||||
}
|
||||
|
||||
let navbarRef = this.getNavbar();
|
||||
if (navbarRef) {
|
||||
navbarRef.elementRef.nativeElement.classList[renderInDom ? 'add' : 'remove' ]('show-navbar');
|
||||
if (this.instance) {
|
||||
this.instance._hidden = (!isActiveView && !isPreviousView);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {Component, NgZone, Injectable, Renderer} from 'angular2/angular2';
|
||||
|
||||
import {IonicApp} from '../app/app';
|
||||
import {Config} from '../../config/config';
|
||||
import {Animation} from '../../animations/animation';
|
||||
import * as util from 'ionic/util';
|
||||
|
||||
@ -8,8 +9,9 @@ import * as util from 'ionic/util';
|
||||
@Injectable()
|
||||
export class OverlayController {
|
||||
|
||||
constructor(app: IonicApp, zone: NgZone, renderer: Renderer) {
|
||||
constructor(app: IonicApp, config: Config, zone: NgZone, renderer: Renderer) {
|
||||
this.app = app;
|
||||
this.config = config;
|
||||
this.zone = zone;
|
||||
this.renderer = renderer;
|
||||
this.refs = [];
|
||||
@ -54,6 +56,9 @@ export class OverlayController {
|
||||
instance.onPageWillEnter && instance.onPageWillEnter();
|
||||
|
||||
let animation = Animation.create(ref.location.nativeElement, opts.enterAnimation);
|
||||
if (this.config.get('animate') === false) {
|
||||
animation.duration(0);
|
||||
}
|
||||
animation.before.addClass('show-overlay');
|
||||
|
||||
this.app.setEnabled(false, animation.duration());
|
||||
@ -95,6 +100,9 @@ export class OverlayController {
|
||||
instance.onPageWillUnload && instance.onPageWillUnload();
|
||||
|
||||
let animation = Animation.create(ref.location.nativeElement, opts.leaveAnimation);
|
||||
if (this.config.get('animate') === false) {
|
||||
animation.duration(0);
|
||||
}
|
||||
animation.after.removeClass('show-overlay');
|
||||
|
||||
this.app.setEnabled(false, animation.duration());
|
||||
|
@ -15,15 +15,11 @@ it('custom cancel button should focus', function() {
|
||||
element(by.css('.e2eCustomCancelButtonFloatingSearchBar input')).sendKeys("DD");
|
||||
});
|
||||
|
||||
it('custom cancel button long text should focus', function() {
|
||||
element(by.css('.e2eCustomCancelButtonLongTextFloatingSearchBar input')).sendKeys("EE");
|
||||
});
|
||||
|
||||
it('custom cancel action should focus', function() {
|
||||
element(by.css('.e2eCustomCancelActionFloatingSearchBar input')).sendKeys("FF");
|
||||
});
|
||||
|
||||
// TODO - this test will work on iOS but fail on Android
|
||||
// TODO - this test will work on iOS but fail on Android
|
||||
// it('custom cancel action should alert', function() {
|
||||
// element(by.css('.e2eCustomCancelActionFloatingSearchBar .search-bar-cancel')).click();
|
||||
// });
|
||||
|
@ -11,9 +11,6 @@
|
||||
<h5 padding-left> Search - Custom Cancel Button </h5>
|
||||
<ion-search-bar [(ng-model)]="customCancel" show-cancel="true" cancel-text="Done" class="e2eCustomCancelButtonFloatingSearchBar"></ion-search-bar>
|
||||
|
||||
<h5 padding-left> Search - Custom Cancel Button Long</h5>
|
||||
<ion-search-bar [(ng-model)]="customCancelLong" show-cancel="true" cancel-text="I Am So Done" class="e2eCustomCancelButtonLongTextFloatingSearchBar"></ion-search-bar>
|
||||
|
||||
<h5 padding-left> Search - Custom Cancel Action</h5>
|
||||
<ion-search-bar [(ng-model)]="customCancelAction" show-cancel="true" cancel-text="Done" [cancel-action]="myCancelAction" class="e2eCustomCancelActionFloatingSearchBar"></ion-search-bar>
|
||||
|
||||
|
@ -1,16 +1,10 @@
|
||||
|
||||
it('friends should be selected', function() {
|
||||
it('friends and standard should be selected', function() {
|
||||
element(by.css('.e2eSegmentFriends')).click();
|
||||
element(by.css('.e2eSegmentStandard')).click();
|
||||
});
|
||||
|
||||
it('standard should be selected', function() {
|
||||
element(by.css('.e2eSegmentStandard')).click();
|
||||
});
|
||||
|
||||
it('model c should be selected', function() {
|
||||
it('model c and top grossing should be selected', function() {
|
||||
element(by.css('.e2eSegmentModelC')).click();
|
||||
});
|
||||
|
||||
it('top grossing should be selected', function() {
|
||||
element(by.css('.e2eSegmentTopGrossing')).click();
|
||||
element(by.css('.e2eSegmentTopGrossing')).click();
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Component, Directive, Host, ElementRef, Compiler, DynamicComponentLoader, AppViewManager, forwardRef, NgZone, Renderer} from 'angular2/angular2';
|
||||
import {Component, Directive, Host, ElementRef, Compiler, DynamicComponentLoader, AppViewManager, NgZone, Renderer} from 'angular2/angular2';
|
||||
|
||||
import {IonicApp} from '../app/app';
|
||||
import {Config} from '../../config/config';
|
||||
@ -64,8 +64,7 @@ import {Tabs} from './tabs';
|
||||
'[class.show-tab]': 'isSelected',
|
||||
'role': 'tabpanel'
|
||||
},
|
||||
template: '<template content-anchor></template><ng-content></ng-content>',
|
||||
directives: [forwardRef(() => TabContentAnchor)]
|
||||
template: '<template #contents></template>'
|
||||
})
|
||||
export class Tab extends NavController {
|
||||
|
||||
@ -83,91 +82,71 @@ export class Tab extends NavController {
|
||||
// A Tab is a NavController for its child pages
|
||||
super(tabs, app, config, elementRef, compiler, loader, viewManager, zone, renderer);
|
||||
this.tabs = tabs;
|
||||
|
||||
this._isInitial = tabs.add(this);
|
||||
}
|
||||
|
||||
onInit() {
|
||||
console.debug('Tab onInit', this.getIndex());
|
||||
console.debug('Tab onInit', this.index);
|
||||
|
||||
if (this._isInitial) {
|
||||
this.tabs.select(this);
|
||||
|
||||
} else if (this.tabs.preloadTabs) {
|
||||
// setTimeout(() => {
|
||||
// this.load(() => {
|
||||
// console.debug('preloaded tab', this.getIndex());
|
||||
// });
|
||||
// }, 500 * this.getIndex());
|
||||
setTimeout(() => {
|
||||
this.load(() => {
|
||||
console.debug('preloaded tab', this.index);
|
||||
this.hideNavbars(true);
|
||||
});
|
||||
}, 500 * this.index);
|
||||
}
|
||||
}
|
||||
|
||||
load(callback) {
|
||||
load(done) {
|
||||
if (!this._loaded && this.root) {
|
||||
let opts = {
|
||||
animate: false
|
||||
};
|
||||
this.push(this.root, null, opts).then(callback);
|
||||
this.push(this.root, null, opts).then(done);
|
||||
this._loaded = true;
|
||||
|
||||
} else {
|
||||
callback();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
loadContainer(componentType, hostProtoViewRef, viewCtrl, done) {
|
||||
loadPage(viewCtrl, navbarContainerRef, done) {
|
||||
// by default a page's navbar goes into the shared tab's navbar section
|
||||
navbarContainerRef = this.tabs.navbarContainerRef;
|
||||
|
||||
this.loadNextToAnchor(componentType, this.contentAnchorRef, viewCtrl).then(componentRef => {
|
||||
let isTabSubPage = (this.tabs.subPages && viewCtrl.index > 0);
|
||||
if (isTabSubPage) {
|
||||
// a subpage, that's not the first index
|
||||
// should not use the shared tabs navbar section, but use it's own
|
||||
navbarContainerRef = null;
|
||||
}
|
||||
|
||||
viewCtrl.disposals.push(() => {
|
||||
componentRef.dispose();
|
||||
});
|
||||
|
||||
// a new ComponentRef has been created
|
||||
// set the ComponentRef's instance to this ViewController
|
||||
viewCtrl.setInstance(componentRef.instance);
|
||||
|
||||
// remember the ElementRef to the content that was just created
|
||||
viewCtrl.setContentRef(componentRef.location);
|
||||
|
||||
// get the NavController's container for navbars, which is
|
||||
// the place this NavController will add each ViewController's navbar
|
||||
let navbarContainerRef = this.tabs.navbarContainerRef;
|
||||
|
||||
// get this ViewController's navbar TemplateRef, which may not
|
||||
// exist if the ViewController's template didn't have an <ion-navbar *navbar>
|
||||
let navbarTemplateRef = viewCtrl.getNavbarTemplateRef();
|
||||
|
||||
// create the navbar view if the pane has a navbar container, and the
|
||||
// ViewController's instance has a navbar TemplateRef to go to inside of it
|
||||
if (navbarContainerRef && navbarTemplateRef) {
|
||||
let navbarView = navbarContainerRef.createEmbeddedView(navbarTemplateRef, -1);
|
||||
|
||||
viewCtrl.disposals.push(() => {
|
||||
let index = navbarContainerRef.indexOf(navbarView);
|
||||
if (index > -1) {
|
||||
navbarContainerRef.remove(index);
|
||||
}
|
||||
});
|
||||
super.loadPage(viewCtrl, navbarContainerRef, () => {
|
||||
if (viewCtrl.instance) {
|
||||
viewCtrl.instance._tabSubPage = isTabSubPage;
|
||||
}
|
||||
|
||||
this.addHasViews();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
getIndex() {
|
||||
setSelected(isSelected) {
|
||||
this.isSelected = isSelected;
|
||||
this.hideNavbars(!isSelected);
|
||||
}
|
||||
|
||||
hideNavbars(shouldHideNavbars) {
|
||||
this._views.forEach(viewCtrl => {
|
||||
let navbar = viewCtrl.getNavbar();
|
||||
navbar && navbar.setHidden(shouldHideNavbars);
|
||||
});
|
||||
}
|
||||
|
||||
get index() {
|
||||
return this.tabs.getIndex(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'template[content-anchor]'})
|
||||
class TabContentAnchor {
|
||||
constructor(@Host() tab: Tab, elementRef: ElementRef) {
|
||||
tab.contentAnchorRef = elementRef;
|
||||
}
|
||||
}
|
||||
|
@ -46,10 +46,6 @@ ion-tabs > ion-navbar-section {
|
||||
order: $flex-order-tab-bar-navbar;
|
||||
}
|
||||
|
||||
ion-tabs ion-navbar.toolbar.deselected-tab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ion-tab-bar-section {
|
||||
position: relative;
|
||||
order: $flex-order-tab-bar-bottom;
|
||||
@ -161,4 +157,3 @@ tab-highlight {
|
||||
[tab-bar-icons=hide] .tab-button-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Directive, ElementRef, Optional, Host, NgFor, forwardRef, ViewContainerRef} from 'angular2/angular2';
|
||||
import {Directive, ElementRef, Optional, Host, NgFor, NgIf, forwardRef, ViewContainerRef} from 'angular2/angular2';
|
||||
|
||||
import {Ion} from '../ion';
|
||||
import {IonicApp} from '../app/app';
|
||||
@ -71,7 +71,7 @@ import {Icon} from '../icon/icon';
|
||||
'</ion-navbar-section>' +
|
||||
'<ion-tab-bar-section>' +
|
||||
'<tab-bar role="tablist">' +
|
||||
'<a *ng-for="#t of _tabs" [tab]="t" class="tab-button" role="tab">' +
|
||||
'<a *ng-for="#t of tabs" [tab]="t" class="tab-button" role="tab">' +
|
||||
'<icon [name]="t.tabIcon" [is-active]="t.isSelected" class="tab-button-icon"></icon>' +
|
||||
'<span class="tab-button-text">{{t.tabTitle}}</span>' +
|
||||
'</a>' +
|
||||
@ -84,6 +84,7 @@ import {Icon} from '../icon/icon';
|
||||
directives: [
|
||||
Icon,
|
||||
NgFor,
|
||||
NgIf,
|
||||
forwardRef(() => TabButton),
|
||||
forwardRef(() => TabHighlight),
|
||||
forwardRef(() => TabNavBarAnchor)
|
||||
@ -108,18 +109,22 @@ export class Tabs extends Ion {
|
||||
super(elementRef, config);
|
||||
this.app = app;
|
||||
this.preload = config.get('preloadTabs');
|
||||
this.subPages = config.get('tabSubPages');
|
||||
|
||||
// collection of children "Tab" instances, which extends NavController
|
||||
this._tabs = [];
|
||||
this.tabs = [];
|
||||
|
||||
// Tabs may also be an actual ViewController which was navigated to
|
||||
// if Tabs is static and not navigated to within a NavController
|
||||
// then skip this and don't treat it as it's own ViewController
|
||||
if (viewCtrl) {
|
||||
this._ready = new Promise(res => { this._isReady = res; });
|
||||
viewCtrl.setContent(this);
|
||||
viewCtrl.setContentRef(elementRef);
|
||||
|
||||
// TODO: improve how this works, probably not use promises here
|
||||
this.readyPromise = new Promise(res => { this.isReady = res; });
|
||||
viewCtrl.onReady = () => {
|
||||
return this._ready;
|
||||
return this.readyPromise;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -131,9 +136,9 @@ export class Tabs extends Ion {
|
||||
tab.id = ++_tabIds;
|
||||
tab.btnId = 'tab-' + tab.id;
|
||||
tab.panelId = 'tabpanel-' + tab.id;
|
||||
this._tabs.push(tab);
|
||||
this.tabs.push(tab);
|
||||
|
||||
return (this._tabs.length === 1);
|
||||
return (this.tabs.length === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,40 +147,25 @@ export class Tabs extends Ion {
|
||||
* @returns {TODO} TODO
|
||||
*/
|
||||
select(tabOrIndex) {
|
||||
let selectedTab = null;
|
||||
|
||||
if (typeof tabOrIndex === 'number') {
|
||||
selectedTab = this.getByIndex(tabOrIndex);
|
||||
|
||||
} else {
|
||||
selectedTab = tabOrIndex;
|
||||
}
|
||||
|
||||
if (!selectedTab || !this.app.isEnabled()) {
|
||||
let selectedTab = (typeof tabOrIndex === 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex);
|
||||
if (!selectedTab) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
console.debug('select tab', selectedTab.id);
|
||||
|
||||
let deselectedTab = this.getSelected();
|
||||
|
||||
if (selectedTab === deselectedTab) {
|
||||
// no change
|
||||
return this._touchActive(selectedTab);
|
||||
return this.touchActive(selectedTab);
|
||||
}
|
||||
|
||||
console.debug('select tab', selectedTab.id);
|
||||
|
||||
selectedTab.load(() => {
|
||||
this._isReady && this._isReady();
|
||||
this.isReady && this.isReady();
|
||||
|
||||
this._tabs.forEach(tab => {
|
||||
tab.isSelected = (tab === selectedTab);
|
||||
|
||||
tab._views.forEach(viewCtrl => {
|
||||
let navbarRef = viewCtrl.navbarRef();
|
||||
if (navbarRef) {
|
||||
navbarRef.nativeElement.classList[tab.isSelected ? 'remove': 'add']('deselected-tab');
|
||||
}
|
||||
});
|
||||
this.tabs.forEach(tab => {
|
||||
tab.setSelected(tab === selectedTab);
|
||||
});
|
||||
|
||||
this.highlight && this.highlight.select(selectedTab);
|
||||
@ -188,23 +178,23 @@ export class Tabs extends Ion {
|
||||
* @returns {TODO} TODO
|
||||
*/
|
||||
getByIndex(index) {
|
||||
if (index < this._tabs.length && index > -1) {
|
||||
return this._tabs[index];
|
||||
if (index < this.tabs.length && index > -1) {
|
||||
return this.tabs[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getSelected() {
|
||||
for (let i = 0; i < this._tabs.length; i++) {
|
||||
if (this._tabs[i].isSelected) {
|
||||
return this._tabs[i];
|
||||
for (let i = 0; i < this.tabs.length; i++) {
|
||||
if (this.tabs[i].isSelected) {
|
||||
return this.tabs[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getIndex(tab) {
|
||||
return this._tabs.indexOf(tab);
|
||||
return this.tabs.indexOf(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,10 +202,8 @@ export class Tabs extends Ion {
|
||||
* "Touch" the active tab, either going back to the root view of the tab
|
||||
* or scrolling the tab to the top
|
||||
*/
|
||||
_touchActive(tab) {
|
||||
let stateLen = tab.length();
|
||||
|
||||
if(stateLen > 1) {
|
||||
touchActive(tab) {
|
||||
if (tab.length() > 1) {
|
||||
// Pop to the root view
|
||||
return tab.popToRoot();
|
||||
}
|
||||
@ -230,7 +218,6 @@ let _tabIds = -1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
*/
|
||||
@Directive({
|
||||
selector: '.tab-button',
|
||||
@ -243,17 +230,15 @@ let _tabIds = -1;
|
||||
'[class.has-icon]': 'hasIcon',
|
||||
'[class.has-title-only]': 'hasTitleOnly',
|
||||
'[class.icon-only]': 'hasIconOnly',
|
||||
'(click)': 'onClick($event)',
|
||||
'[class.disable-hover]': 'disHover',
|
||||
'(click)': 'onClick()',
|
||||
}
|
||||
})
|
||||
class TabButton extends Ion {
|
||||
constructor(@Host() tabs: Tabs, config: Config, elementRef: ElementRef) {
|
||||
super(elementRef, config);
|
||||
this.tabs = tabs;
|
||||
|
||||
if (config.get('hoverCSS') === false) {
|
||||
elementRef.nativeElement.classList.add('disable-hover');
|
||||
}
|
||||
this.disHover = (config.get('hoverCSS') === false);
|
||||
}
|
||||
|
||||
onInit() {
|
||||
@ -264,16 +249,14 @@ class TabButton extends Ion {
|
||||
this.hasIconOnly = (this.hasIcon && !this.hasTitle);
|
||||
}
|
||||
|
||||
onClick(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
onClick() {
|
||||
this.tabs.select(this.tab);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'tab-highlight'
|
||||
@ -306,14 +289,10 @@ class TabHighlight {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO
|
||||
*/
|
||||
@Directive({selector: 'template[navbar-anchor]'})
|
||||
class TabNavBarAnchor {
|
||||
constructor(
|
||||
@Host() tabs: Tabs,
|
||||
viewContainerRef: ViewContainerRef
|
||||
) {
|
||||
constructor(@Host() tabs: Tabs, viewContainerRef: ViewContainerRef) {
|
||||
tabs.navbarContainerRef = viewContainerRef;
|
||||
}
|
||||
}
|
||||
|
@ -5,3 +5,5 @@ import {App} from 'ionic/ionic';
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
||||
|
||||
document.body.innerHTML += '<link href="styles.css" rel="stylesheet">'
|
||||
|
@ -59,11 +59,3 @@
|
||||
<ion-tab tab-title="Indiana Jones and the Temple of Doom"></ion-tab>
|
||||
<ion-tab tab-title="Indiana Jones and the Last Crusade"></ion-tab>
|
||||
</ion-tabs>
|
||||
|
||||
|
||||
<style>
|
||||
ion-tabs {
|
||||
height: auto;
|
||||
border-top: 1px solid gray;
|
||||
}
|
||||
</style>
|
||||
|
12
ionic/components/tabs/test/tab-bar-scenarios/styles.css
Normal file
12
ionic/components/tabs/test/tab-bar-scenarios/styles.css
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
ion-tabs {
|
||||
position: relative;
|
||||
top: auto;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-navbar-section,
|
||||
ion-content-section {
|
||||
display: none !important;
|
||||
}
|
@ -84,6 +84,7 @@ ion-title {
|
||||
height: 100%;
|
||||
padding: 0px 90px 1px 90px;
|
||||
pointer-events: none;
|
||||
transform: translateZ(0px);
|
||||
}
|
||||
|
||||
.toolbar-title {
|
||||
@ -96,6 +97,7 @@ ion-title {
|
||||
ion-nav-items {
|
||||
flex: 1;
|
||||
order: map-get($toolbar-order-ios, primary);
|
||||
transform: translateZ(0px);
|
||||
}
|
||||
|
||||
ion-nav-items[secondary] {
|
||||
|
@ -75,7 +75,6 @@ ion-title {
|
||||
order: map-get($toolbar-order, title);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transform: translateZ(0px);
|
||||
}
|
||||
|
||||
.toolbar-title {
|
||||
@ -109,7 +108,6 @@ ion-title {
|
||||
ion-nav-items {
|
||||
display: block;
|
||||
margin: 0 0.2rem;
|
||||
transform: translateZ(0px);
|
||||
pointer-events: none;
|
||||
order: map-get($toolbar-order, primary);
|
||||
}
|
||||
|
@ -127,6 +127,11 @@ export class Config {
|
||||
let configObj = null;
|
||||
|
||||
if (this._platform) {
|
||||
let queryStringValue = this._platform.query('ionic' + key.toLowerCase());
|
||||
if (isDefined(queryStringValue)) {
|
||||
return this._c[key] = (queryStringValue === 'true' ? true : queryStringValue === 'false' ? false : queryStringValue);
|
||||
}
|
||||
|
||||
// check the platform settings object for this value
|
||||
// loop though each of the active platforms
|
||||
|
||||
|
@ -1,18 +1,9 @@
|
||||
import {Component, Directive, View, bootstrap} from 'angular2/angular2'
|
||||
import {Component, bootstrap} from 'angular2/angular2'
|
||||
|
||||
import * as util from 'ionic/util';
|
||||
import {ionicProviders} from './bootstrap';
|
||||
import {IONIC_DIRECTIVES} from './directives';
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
class PageImpl extends View {
|
||||
constructor(args = {}) {
|
||||
args.directives = (args.directives || []).concat(IONIC_DIRECTIVES);
|
||||
super(args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _For more information on how pages are created, see the [NavController API
|
||||
@ -72,30 +63,30 @@ class PageImpl extends View {
|
||||
* you may see these tags if you inspect your markup, you don't need to include
|
||||
* them in your templates.
|
||||
*/
|
||||
export function Page(args) {
|
||||
export function Page(config={}) {
|
||||
return function(cls) {
|
||||
config.selector = 'ion-page';
|
||||
config.directives = config.directives ? config.directives.concat(IONIC_DIRECTIVES) : IONIC_DIRECTIVES;
|
||||
config.host = config.host || {};
|
||||
config.host['[hidden]'] = '_hidden';
|
||||
config.host['[class.tab-subpage]'] = '_tabSubPage';
|
||||
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||
annotations.push(new PageImpl(args));
|
||||
annotations.push(new Component(config));
|
||||
Reflect.defineMetadata('annotations', annotations, cls);
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
|
||||
export function ConfigComponent(config) {
|
||||
return function(cls) {
|
||||
return makeComponent(cls, appendConfig(cls, config));
|
||||
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||
annotations.push(new Component(config));
|
||||
Reflect.defineMetadata('annotations', annotations, cls);
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
|
||||
export function makeComponent(cls, config) {
|
||||
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||
annotations.push(new Component(config));
|
||||
Reflect.defineMetadata('annotations', annotations, cls);
|
||||
return cls;
|
||||
}
|
||||
|
||||
function appendConfig(cls, config) {
|
||||
config.host = config.host || {};
|
||||
|
@ -48,6 +48,7 @@ Config.setModeConfig('md', {
|
||||
popupPopIn: 'popup-md-pop-in',
|
||||
popupPopOut: 'popup-md-pop-out',
|
||||
|
||||
tabSubPages: true,
|
||||
type: 'overlay',
|
||||
mdRipple: true,
|
||||
});
|
||||
|
@ -57,6 +57,42 @@ export function run() {
|
||||
expect(config.get('tabBarPlacement')).toEqual('top');
|
||||
});
|
||||
|
||||
it('should get boolean value from querystring', () => {
|
||||
let config = new Config();
|
||||
let platform = new Platform();
|
||||
platform.url('http://biff.com/?ionicanimate=true')
|
||||
config.setPlatform(platform);
|
||||
expect(config.get('animate')).toEqual(true);
|
||||
|
||||
config = new Config();
|
||||
platform = new Platform();
|
||||
platform.url('http://biff.com/?ionicanimate=false')
|
||||
config.setPlatform(platform);
|
||||
expect(config.get('animate')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should get value from case insensitive querystring key', () => {
|
||||
let config = new Config({
|
||||
mode: 'a'
|
||||
});
|
||||
let platform = new Platform();
|
||||
platform.url('http://biff.com/?ionicConfigKey=b')
|
||||
config.setPlatform(platform);
|
||||
|
||||
expect(config.get('configKey')).toEqual('b');
|
||||
});
|
||||
|
||||
it('should get value from querystring', () => {
|
||||
let config = new Config({
|
||||
mode: 'modeA'
|
||||
});
|
||||
let platform = new Platform();
|
||||
platform.url('http://biff.com/?ionicmode=modeB')
|
||||
config.setPlatform(platform);
|
||||
|
||||
expect(config.get('mode')).toEqual('modeB');
|
||||
});
|
||||
|
||||
it('should override mode platform', () => {
|
||||
let config = new Config({
|
||||
mode: 'modeA',
|
||||
|
@ -165,7 +165,7 @@ function ifUndefined(val1, val2) {
|
||||
*/
|
||||
function addEventListeners(target, types, handler) {
|
||||
each(splitStr(types), function(type) {
|
||||
console.debug('hammer addEventListener', type, target.tagName);
|
||||
//console.debug('hammer addEventListener', type, target.tagName);
|
||||
target.addEventListener(type, handler, false);
|
||||
});
|
||||
}
|
||||
@ -178,7 +178,7 @@ function addEventListeners(target, types, handler) {
|
||||
*/
|
||||
function removeEventListeners(target, types, handler) {
|
||||
each(splitStr(types), function(type) {
|
||||
console.debug('hammer removeEventListener', type, target.tagName);
|
||||
//console.debug('hammer removeEventListener', type, target.tagName);
|
||||
target.removeEventListener(type, handler, false);
|
||||
});
|
||||
}
|
||||
@ -394,7 +394,7 @@ Input.prototype = {
|
||||
* bind the events
|
||||
*/
|
||||
init: function() {
|
||||
console.debug('hammer Input init')
|
||||
//console.debug('hammer Input init')
|
||||
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
|
||||
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
|
||||
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
|
||||
|
@ -24,7 +24,6 @@
|
||||
"components/button/button-icon",
|
||||
"components/button/button-fab",
|
||||
"components/checkbox/checkbox",
|
||||
"components/content/content",
|
||||
"components/icon/icon",
|
||||
"components/item/item",
|
||||
"components/item/item-media",
|
||||
|
@ -21,6 +21,20 @@ export function run() {
|
||||
expect(platform.is('ios')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should get case insensitive querystring value', () => {
|
||||
let platform = new Platform();
|
||||
platform.url('/?KEY=value');
|
||||
|
||||
expect(platform.query('key')).toEqual('value');
|
||||
});
|
||||
|
||||
it('should get querystring value', () => {
|
||||
let platform = new Platform();
|
||||
platform.url('/?key=value');
|
||||
|
||||
expect(platform.query('key')).toEqual('value');
|
||||
});
|
||||
|
||||
it('should set ios via platformOverride, despite android querystring', () => {
|
||||
let platform = new Platform();
|
||||
platform.url('/?ionicplatform=android');
|
||||
|
@ -9,8 +9,6 @@ const OFF_RIGHT = '99.5%';
|
||||
const OFF_LEFT = '-33%';
|
||||
const CENTER = '0%'
|
||||
const OFF_OPACITY = 0.8;
|
||||
const SHOW_NAVBAR_CSS = 'show-navbar';
|
||||
const SHOW_VIEW_CSS = 'show-view';
|
||||
const SHOW_BACK_BTN_CSS = 'show-back-button';
|
||||
|
||||
|
||||
@ -36,29 +34,24 @@ class IOSTransition extends Animation {
|
||||
|
||||
// entering content
|
||||
let enteringContent = new Animation(enteringView.contentRef());
|
||||
enteringContent
|
||||
.before.addClass(SHOW_VIEW_CSS)
|
||||
.before.setStyles({ zIndex: enteringView.index });
|
||||
this.add(enteringContent);
|
||||
|
||||
if (backDirection) {
|
||||
// back direction
|
||||
// entering content, back direction
|
||||
enteringContent
|
||||
.fromTo(TRANSLATEX, OFF_LEFT, CENTER)
|
||||
.fromTo(OPACITY, OFF_OPACITY, 1);
|
||||
|
||||
} else {
|
||||
// forward direction
|
||||
// entering content, forward direction
|
||||
enteringContent
|
||||
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER)
|
||||
.fromTo(OPACITY, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
// entering navbar
|
||||
if (enteringHasNavbar) {
|
||||
// entering page has a navbar
|
||||
let enteringNavBar = new Animation(enteringView.navbarRef());
|
||||
enteringNavBar.before.addClass(SHOW_NAVBAR_CSS);
|
||||
this.add(enteringNavBar);
|
||||
|
||||
let enteringTitle = new Animation(enteringView.titleRef());
|
||||
@ -76,62 +69,68 @@ class IOSTransition extends Animation {
|
||||
|
||||
// set properties depending on direction
|
||||
if (backDirection) {
|
||||
// back direction
|
||||
// entering navbar, back direction
|
||||
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER);
|
||||
|
||||
if (enteringView.enableBack()) {
|
||||
enteringBackButton.before.addClass(SHOW_BACK_BTN_CSS);
|
||||
// back direction, entering page has a back button
|
||||
enteringBackButton.fadeIn();
|
||||
}
|
||||
|
||||
} else {
|
||||
// forward direction
|
||||
// entering navbar, forward direction
|
||||
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER);
|
||||
|
||||
if (enteringView.enableBack()) {
|
||||
enteringBackButton.before.addClass(SHOW_BACK_BTN_CSS);
|
||||
enteringBackButton.fadeIn();
|
||||
|
||||
let enteringBackBtnText = new Animation(enteringView.backBtnTextRef());
|
||||
enteringBackBtnText.fromTo(TRANSLATEX, '150px', '0px');
|
||||
enteringNavBar.add(enteringBackBtnText);
|
||||
}
|
||||
|
||||
if (leavingHasNavbar) {
|
||||
// if there is a leaving navbar, then just fade this one in
|
||||
// entering navbar, forward direction, and there's a leaving navbar
|
||||
// should just fade in, no sliding
|
||||
enteringNavbarBg
|
||||
.fromTo(TRANSLATEX, CENTER, CENTER)
|
||||
.fadeIn();
|
||||
|
||||
} else {
|
||||
enteringNavbarBg.fromTo(TRANSLATEX, OFF_RIGHT, CENTER);
|
||||
// entering navbar, forward direction, and there's no leaving navbar
|
||||
// should just slide in, no fading in
|
||||
enteringNavbarBg
|
||||
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER)
|
||||
.fromTo(OPACITY, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
if (enteringView.enableBack()) {
|
||||
// forward direction, entering page has a back button
|
||||
enteringBackButton
|
||||
.before.addClass(SHOW_BACK_BTN_CSS)
|
||||
.fadeIn();
|
||||
|
||||
let enteringBackBtnText = new Animation(enteringView.backBtnTextRef());
|
||||
enteringBackBtnText.fromTo(TRANSLATEX, '100px', '0px');
|
||||
enteringNavBar.add(enteringBackBtnText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// setup leaving view
|
||||
if (leavingView) {
|
||||
// leaving content
|
||||
let leavingContent = new Animation(leavingView.contentRef());
|
||||
this.add(leavingContent);
|
||||
leavingContent
|
||||
.before.addClass(SHOW_VIEW_CSS)
|
||||
.before.setStyles({ zIndex: leavingView.index });
|
||||
|
||||
if (backDirection) {
|
||||
// leaving content, back direction
|
||||
leavingContent
|
||||
.fromTo(TRANSLATEX, CENTER, '100%')
|
||||
.fromTo(OPACITY, 1, 1);
|
||||
|
||||
} else {
|
||||
// leaving content, forward direction
|
||||
leavingContent
|
||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
||||
.fromTo(OPACITY, 1, OFF_OPACITY);
|
||||
}
|
||||
|
||||
if (leavingHasNavbar) {
|
||||
// leaving page has a navbar
|
||||
let leavingNavBar = new Animation(leavingView.navbarRef());
|
||||
let leavingBackButton = new Animation(leavingView.backBtnRef());
|
||||
let leavingTitle = new Animation(leavingView.titleRef());
|
||||
@ -145,25 +144,28 @@ class IOSTransition extends Animation {
|
||||
.add(leavingNavbarBg);
|
||||
this.add(leavingNavBar);
|
||||
|
||||
leavingBackButton
|
||||
.after.removeClass(SHOW_BACK_BTN_CSS)
|
||||
.fadeOut();
|
||||
|
||||
// fade out leaving navbar items
|
||||
leavingBackButton.fadeOut();
|
||||
leavingTitle.fadeOut();
|
||||
leavingNavbarItems.fadeOut();
|
||||
|
||||
// set properties depending on direction
|
||||
if (backDirection) {
|
||||
// back direction
|
||||
// leaving navbar, back direction
|
||||
leavingTitle.fromTo(TRANSLATEX, CENTER, '100%');
|
||||
|
||||
if (enteringHasNavbar) {
|
||||
// this is an entering navbar, just fade this out
|
||||
// leaving navbar, back direction, and there's an entering navbar
|
||||
// should just fade out, no sliding
|
||||
leavingNavbarBg
|
||||
.fromTo(TRANSLATEX, CENTER, CENTER)
|
||||
.fadeOut();
|
||||
|
||||
} else {
|
||||
leavingNavbarBg.fromTo(TRANSLATEX, CENTER, '100%');
|
||||
// leaving navbar, back direction, and there's no entering navbar
|
||||
// should just slide out, no fading out
|
||||
leavingNavbarBg
|
||||
.fromTo(TRANSLATEX, CENTER, '100%')
|
||||
.fromTo(OPACITY, 1, 1);
|
||||
}
|
||||
|
||||
let leavingBackBtnText = new Animation(leavingView.backBtnTextRef());
|
||||
@ -171,7 +173,7 @@ class IOSTransition extends Animation {
|
||||
leavingNavBar.add(leavingBackBtnText);
|
||||
|
||||
} else {
|
||||
// forward direction
|
||||
// leaving navbar, forward direction
|
||||
leavingTitle.fromTo(TRANSLATEX, CENTER, OFF_LEFT);
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,13 @@ import {Animation} from '../animations/animation';
|
||||
const TRANSLATEY = 'translateY';
|
||||
const OFF_BOTTOM = '40px';
|
||||
const CENTER = '0px'
|
||||
const SHOW_NAVBAR_CSS = 'show-navbar';
|
||||
const SHOW_VIEW_CSS = 'show-view';
|
||||
const SHOW_BACK_BTN_CSS = 'show-back-button';
|
||||
const TABBAR_HEIGHT = '69px';
|
||||
|
||||
|
||||
class MDTransition extends Animation {
|
||||
|
||||
constructor(navCtrl, opts) {
|
||||
opts.renderDelay = 160;
|
||||
//opts.renderDelay = 80;
|
||||
super(null, opts);
|
||||
|
||||
// what direction is the transition going
|
||||
@ -27,98 +24,34 @@ class MDTransition extends Animation {
|
||||
let enteringHasNavbar = enteringView.hasNavbar();
|
||||
let leavingHasNavbar = leavingView && leavingView.hasNavbar();
|
||||
|
||||
|
||||
// entering content item moves in bottom to center
|
||||
let enteringContent = new Animation(enteringView.contentRef());
|
||||
enteringContent
|
||||
.before.addClass(SHOW_VIEW_CSS)
|
||||
.before.setStyles({ zIndex: enteringView.index });
|
||||
this.add(enteringContent);
|
||||
let enteringPage = new Animation(enteringView.pageRef());
|
||||
this.add(enteringPage);
|
||||
|
||||
if (backDirection) {
|
||||
this.duration(200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
enteringContent.fromTo(TRANSLATEY, CENTER, CENTER);
|
||||
enteringPage.fromTo(TRANSLATEY, CENTER, CENTER);
|
||||
|
||||
} else {
|
||||
this.duration(280).easing('cubic-bezier(0.36,0.66,0.04,1)');
|
||||
enteringContent
|
||||
enteringPage
|
||||
.fromTo(TRANSLATEY, OFF_BOTTOM, CENTER)
|
||||
.fadeIn();
|
||||
}
|
||||
|
||||
|
||||
// entering navbar
|
||||
if (enteringHasNavbar) {
|
||||
let enteringNavBar = new Animation(enteringView.navbarRef());
|
||||
enteringNavBar
|
||||
.before.addClass(SHOW_NAVBAR_CSS)
|
||||
.before.setStyles({ zIndex: enteringView.index + 10 });
|
||||
this.add(enteringNavBar);
|
||||
|
||||
if (backDirection) {
|
||||
enteringNavBar.fromTo(TRANSLATEY, CENTER, CENTER);
|
||||
|
||||
} else {
|
||||
enteringNavBar
|
||||
.fromTo(TRANSLATEY, OFF_BOTTOM, CENTER)
|
||||
.fadeIn();
|
||||
}
|
||||
|
||||
if (enteringView.enableBack()) {
|
||||
let enteringBackButton = new Animation(enteringView.backBtnRef());
|
||||
enteringBackButton.before.addClass(SHOW_BACK_BTN_CSS);
|
||||
enteringNavBar.add(enteringBackButton);
|
||||
}
|
||||
if (enteringHasNavbar && enteringView.enableBack()) {
|
||||
let enteringBackButton = new Animation(enteringView.backBtnRef());
|
||||
enteringBackButton.before.addClass(SHOW_BACK_BTN_CSS);
|
||||
this.add(enteringBackButton);
|
||||
}
|
||||
|
||||
// setup leaving view
|
||||
if (leavingView) {
|
||||
if (leavingView && backDirection) {
|
||||
// leaving content
|
||||
let leavingContent = new Animation(leavingView.contentRef());
|
||||
this.add(leavingContent);
|
||||
leavingContent
|
||||
.before.addClass(SHOW_VIEW_CSS)
|
||||
.before.setStyles({ zIndex: leavingView.index });
|
||||
|
||||
|
||||
if (backDirection) {
|
||||
this.duration(200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
leavingContent
|
||||
.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM)
|
||||
.fadeOut();
|
||||
}
|
||||
|
||||
|
||||
if (leavingHasNavbar) {
|
||||
if (backDirection) {
|
||||
let leavingNavBar = new Animation(leavingView.navbarRef());
|
||||
this.add(leavingNavBar);
|
||||
|
||||
leavingNavBar
|
||||
.before.setStyles({ zIndex: leavingView.index + 10 })
|
||||
.fadeOut();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let viewLength = navCtrl.length();
|
||||
if ((viewLength === 1 || viewLength === 2) && navCtrl.tabs) {
|
||||
let tabBarEle = navCtrl.tabs.elementRef.nativeElement.querySelector('ion-tab-bar-section');
|
||||
let tabBar = new Animation(tabBarEle);
|
||||
|
||||
if (viewLength === 1 && backDirection) {
|
||||
tabBar
|
||||
.fromTo('height', '0px', TABBAR_HEIGHT)
|
||||
.fadeIn();
|
||||
|
||||
} else if (viewLength === 2 && !backDirection) {
|
||||
tabBar
|
||||
.fromTo('height', TABBAR_HEIGHT, '0px')
|
||||
.fadeOut();
|
||||
}
|
||||
|
||||
this.add(tabBar);
|
||||
this.duration(200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
let leavingPage = new Animation(leavingView.pageRef());
|
||||
this.add(leavingPage.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM).fadeOut());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,49 @@
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.hide.hide.hide {
|
||||
display: none;
|
||||
.hide,
|
||||
[hidden],
|
||||
template,
|
||||
root-anchor {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
// Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
$content-padding: 16px !default;
|
||||
|
||||
|
||||
[padding],
|
||||
[padding] > scroll-content {
|
||||
padding: $content-padding;
|
||||
}
|
||||
|
||||
[padding-top] {
|
||||
padding-top: $content-padding;
|
||||
}
|
||||
|
||||
[padding-right] {
|
||||
padding-right: $content-padding;
|
||||
}
|
||||
|
||||
[padding-bottom] {
|
||||
padding-bottom: $content-padding;
|
||||
}
|
||||
|
||||
[padding-left] {
|
||||
padding-left: $content-padding;
|
||||
}
|
||||
|
||||
[padding-vertical] {
|
||||
padding-top: $content-padding;
|
||||
padding-bottom: $content-padding;
|
||||
}
|
||||
|
||||
[padding-horizontal] {
|
||||
padding-right: $content-padding;
|
||||
padding-left: $content-padding;
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,12 +167,10 @@ export function getQuerystring(url, key) {
|
||||
const startIndex = url.indexOf('?');
|
||||
if (startIndex !== -1) {
|
||||
const queries = url.slice(startIndex + 1).split('&');
|
||||
if (queries.length) {
|
||||
queries.forEach((param) => {
|
||||
var split = param.split('=');
|
||||
queryParams[split[0]] = split[1].split('#')[0];
|
||||
});
|
||||
}
|
||||
queries.forEach((param) => {
|
||||
var split = param.split('=');
|
||||
queryParams[split[0].toLowerCase()] = split[1].split('#')[0];
|
||||
});
|
||||
}
|
||||
if (key) {
|
||||
return queryParams[key] || '';
|
||||
|
@ -53,7 +53,6 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"node-libs-browser": "^0.5.2",
|
||||
"node-uuid": "^1.4.1",
|
||||
"q": "^1.4.1",
|
||||
"request": "2.53.0",
|
||||
"run-sequence": "^1.1.0",
|
||||
|
@ -2,22 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- https://www.chromium.org/developers/design-documents/chromium-graphics/how-to-get-gpu-rasterization -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<link href="../../../css/ionic.css" rel="stylesheet">
|
||||
|
||||
<script>
|
||||
// dynamically load the stylesheet depending on theme querystring
|
||||
// defaults to use the light-theme
|
||||
var theme = 'ionic.css';
|
||||
if (location.href.indexOf('ionictheme=dark') > -1) {
|
||||
theme = 'ionic.dark.css';
|
||||
}
|
||||
|
||||
var stylesheet = document.createElement('link');
|
||||
stylesheet.setAttribute('rel', 'stylesheet');
|
||||
stylesheet.setAttribute('href', '../../../css/' + theme);
|
||||
document.getElementsByTagName('head')[0].appendChild(stylesheet);
|
||||
|
||||
if (location.href.indexOf('snapshot=true') > -1) {
|
||||
document.documentElement.classList.add('snapshot');
|
||||
} else {
|
||||
@ -27,7 +15,12 @@
|
||||
|
||||
<style>
|
||||
.snapshot body {
|
||||
max-height: 700px;
|
||||
/* crop an exact size */
|
||||
max-height: 700px !important;
|
||||
}
|
||||
.snapshot scroll-content {
|
||||
/* disable scrollbars */
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* hack to create tall scrollable areas for testing using <f></f> */
|
||||
|
@ -1,7 +1,7 @@
|
||||
describe('<%= relativePath %>: <%= platform %>', function() {
|
||||
|
||||
it('should init', function() {
|
||||
browser.get('http://localhost:<%= buildConfig.protractorPort %>/dist/e2e/<%= relativePath %>/index.html?ionicplatform=<%= platform %>&snapshot=true');
|
||||
browser.get('http://localhost:<%= buildConfig.protractorPort %>/dist/e2e/<%= relativePath %>/index.html?ionicplatform=<%= platform %>&ionicanimate=false&snapshot=true');
|
||||
});
|
||||
|
||||
<%= contents %>
|
||||
|
@ -116,7 +116,7 @@ var IonicSnapshot = function(options) {
|
||||
self.testData.description = spec.getFullName();
|
||||
self.testData.highest_mismatch = self.highestMismatch;
|
||||
self.testData.png_base64 = pngBase64;
|
||||
self.testData.url = currentUrl.replace('dist/', '/');
|
||||
self.testData.url = currentUrl.replace('dist/', '/').replace('&ionicanimate=false', '');
|
||||
pngBase64 = null;
|
||||
|
||||
var requestDeferred = q.defer();
|
||||
|
@ -9,9 +9,9 @@ exports.config = {
|
||||
//domain: 'localhost:8080',
|
||||
|
||||
specs: 'dist/e2e/**/*e2e.js',
|
||||
//specs: 'dist/e2e/tabs/**/*e2e.js',
|
||||
//specs: 'dist/e2e/search-bar/**/*e2e.js',
|
||||
|
||||
sleepBetweenSpecs: 600,
|
||||
sleepBetweenSpecs: 300,
|
||||
|
||||
platformDefauls: {
|
||||
browser: 'chrome',
|
||||
|
@ -8,7 +8,6 @@ module.exports = function(gulp, argv, buildConfig) {
|
||||
var serveStatic = require('serve-static');
|
||||
var cp = require('child_process');
|
||||
var path = require('canonical-path');
|
||||
var uuid = require('node-uuid');
|
||||
|
||||
var projectRoot = path.resolve(__dirname, '../..');
|
||||
var protractorHttpServer;
|
||||
@ -29,7 +28,7 @@ module.exports = function(gulp, argv, buildConfig) {
|
||||
});
|
||||
|
||||
gulp.task('e2e-publish', function(done) {
|
||||
var testId = uuid.v4().split('-')[0];
|
||||
var testId = generateTestId();
|
||||
e2ePublish(testId, true);
|
||||
});
|
||||
|
||||
@ -40,7 +39,7 @@ module.exports = function(gulp, argv, buildConfig) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var testId = uuid.v4().split('-')[0];
|
||||
var testId = generateTestId();
|
||||
|
||||
var protractorConfigFile = path.resolve(projectRoot, 'scripts/snapshot/protractor.config.js');
|
||||
|
||||
@ -91,4 +90,14 @@ module.exports = function(gulp, argv, buildConfig) {
|
||||
require('../e2e/e2e-publish')(snapshotConfig);
|
||||
}
|
||||
|
||||
function generateTestId() {
|
||||
var chars = 'abcdefghijklmnopqrstuvwxyz';
|
||||
var id = chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
chars += '0123456789';
|
||||
while (id.length < 3) {
|
||||
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
};
|
||||
|
Reference in New Issue
Block a user