mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Revert "Revert "Merge branch 'master' of https://github.com/driftyco/ionic""
This reverts commit 4a6086c1f8.
This commit is contained in:
@@ -304,6 +304,7 @@ class E2EPage {
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
Hi, I'm Bob, and I'm a modal.
|
||||
<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>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser';
|
||||
import { ClickBlock } from '../../util/click-block';
|
||||
import { Config } from '../../config/config';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { isTabs, isNav } from '../nav/nav-controller-base';
|
||||
import { NavOptions } from '../nav/nav-interfaces';
|
||||
import { NavPortal } from '../nav/nav-portal';
|
||||
import { Platform } from '../../platform/platform';
|
||||
@@ -31,6 +32,11 @@ export class App {
|
||||
*/
|
||||
clickBlock: ClickBlock;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
appRoot: AppRoot;
|
||||
|
||||
viewDidLoad: EventEmitter<any> = new EventEmitter();
|
||||
viewWillEnter: EventEmitter<any> = new EventEmitter();
|
||||
viewDidEnter: EventEmitter<any> = new EventEmitter();
|
||||
@@ -86,6 +92,17 @@ export class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setScrollDisabled(disabled: boolean) {
|
||||
if (!this.appRoot) {
|
||||
console.error('appRoot is missing, scrolling can not be enabled/disabled');
|
||||
return;
|
||||
}
|
||||
this.appRoot.disableScroll = disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Boolean if the app is actively enabled or not.
|
||||
@@ -179,13 +196,7 @@ export class App {
|
||||
// function used to climb up all parent nav controllers
|
||||
function navPop(nav: any): Promise<any> {
|
||||
if (nav) {
|
||||
if (nav.length && nav.length() > 1) {
|
||||
// this nav controller has more than one view
|
||||
// pop the current view on this nav and we're done here
|
||||
console.debug('app, goBack pop nav');
|
||||
return nav.pop();
|
||||
|
||||
} else if (nav.previousTab) {
|
||||
if (isTabs(nav)) {
|
||||
// FYI, using "nav instanceof Tabs" throws a Promise runtime error for whatever reason, idk
|
||||
// this is a Tabs container
|
||||
// see if there is a valid previous tab to go to
|
||||
@@ -195,6 +206,12 @@ export class App {
|
||||
nav.select(prevTab);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
} else if (isNav(nav) && nav.length() > 1) {
|
||||
// this nav controller has more than one view
|
||||
// pop the current view on this nav and we're done here
|
||||
console.debug('app, goBack pop nav');
|
||||
return nav.pop();
|
||||
}
|
||||
|
||||
// try again using the parent nav (if there is one)
|
||||
@@ -228,10 +245,9 @@ export class App {
|
||||
console.debug('app, goBack exitApp');
|
||||
this._platform.exitApp();
|
||||
}
|
||||
|
||||
} else {
|
||||
return navPromise;
|
||||
}
|
||||
|
||||
return navPromise;
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
@@ -282,9 +298,13 @@ export class AppRoot {
|
||||
@ViewChild('anchor', {read: ViewContainerRef}) private _viewport: ViewContainerRef;
|
||||
|
||||
constructor(
|
||||
private _cmp: UserComponent,
|
||||
private _cr: ComponentResolver,
|
||||
private _renderer: Renderer) {}
|
||||
private _cmp: UserComponent,
|
||||
private _cr: ComponentResolver,
|
||||
private _renderer: Renderer,
|
||||
app: App
|
||||
) {
|
||||
app.appRoot = this;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
// load the user app's root component
|
||||
|
||||
@@ -219,7 +219,6 @@ ion-content.js-scroll > scroll-content {
|
||||
|
||||
[nav-viewport],
|
||||
[nav-portal],
|
||||
[tab-portal],
|
||||
.nav-decor {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import {ionicBootstrap, Config, Animation} from '../../../../../src';
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EPage {
|
||||
duration;
|
||||
easing;
|
||||
duration: string;
|
||||
easing: string;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.duration = '1000';
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {App, Nav, Tabs, Tab, NavOptions, Config, ViewController, Platform} from '../../../../src';
|
||||
import { Component } from '@angular/core';
|
||||
import { App, Config, Nav, NavOptions, Platform, Tab, Tabs, ViewController } from '../../../../src';
|
||||
import { mockNavController, mockTab, mockTabs } from '../../../../src/util/mock-providers';
|
||||
|
||||
export function run() {
|
||||
|
||||
|
||||
describe('App', () => {
|
||||
|
||||
describe('navPop', () => {
|
||||
|
||||
it('should select the previous tab', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -40,8 +40,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should pop from the active tab, when tabs is nested is the root nav', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -91,9 +91,9 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should pop the root nav when nested nav has less than 2 views', () => {
|
||||
let rootNav = mockNav();
|
||||
let nestedNav = mockNav();
|
||||
let portal = mockNav();
|
||||
let rootNav = mockNavController();
|
||||
let nestedNav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
rootNav.registerChildNav(nestedNav);
|
||||
nestedNav.parent = rootNav;
|
||||
@@ -120,9 +120,9 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should pop a view from the nested nav that has more than 1 view', () => {
|
||||
let rootNav = mockNav();
|
||||
let nestedNav = mockNav();
|
||||
let portal = mockNav();
|
||||
let rootNav = mockNavController();
|
||||
let nestedNav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(rootNav);
|
||||
rootNav.registerChildNav(nestedNav);
|
||||
@@ -149,8 +149,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should pop the overlay in the portal of the root nav', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -173,8 +173,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should pop the second view in the root nav', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -194,8 +194,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should exit app when only one view in the root nav', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -217,8 +217,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should not exit app when only one view in the root nav, but navExitApp config set', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -242,8 +242,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should not go back if app is not enabled', () => {
|
||||
let nav = mockNav();
|
||||
let portal = mockNav();
|
||||
let nav = mockNavController();
|
||||
let portal = mockNavController();
|
||||
app.setPortal(portal);
|
||||
app.setRootNav(nav);
|
||||
|
||||
@@ -276,7 +276,7 @@ describe('App', () => {
|
||||
describe('getActiveNav', () => {
|
||||
|
||||
it('should get active NavController when using tabs with nested nav', () => {
|
||||
let nav = mockNav();
|
||||
let nav = mockNavController();
|
||||
app.setRootNav(nav);
|
||||
|
||||
let tabs = mockTabs();
|
||||
@@ -285,9 +285,9 @@ describe('App', () => {
|
||||
nav.registerChildNav(tabs);
|
||||
|
||||
tab2.setSelected(true);
|
||||
let nav2 = mockNav();
|
||||
let nav3 = mockNav();
|
||||
let nav4 = mockNav();
|
||||
let nav2 = mockNavController();
|
||||
let nav3 = mockNavController();
|
||||
let nav4 = mockNavController();
|
||||
tab1.registerChildNav(nav4);
|
||||
tab2.registerChildNav(nav2);
|
||||
tab2.registerChildNav(nav3);
|
||||
@@ -296,7 +296,7 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should get active NavController when using tabs, nested in a root nav', () => {
|
||||
let nav = mockNav();
|
||||
let nav = mockNavController();
|
||||
app.setRootNav(nav);
|
||||
|
||||
let tabs = mockTabs();
|
||||
@@ -331,9 +331,9 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should get active NavController when nested 3 deep', () => {
|
||||
let nav1 = mockNav();
|
||||
let nav2 = mockNav();
|
||||
let nav3 = mockNav();
|
||||
let nav1 = mockNavController();
|
||||
let nav2 = mockNavController();
|
||||
let nav3 = mockNavController();
|
||||
app.setRootNav(nav1);
|
||||
|
||||
nav1.registerChildNav(nav2);
|
||||
@@ -343,8 +343,8 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should get active NavController when nested 2 deep', () => {
|
||||
let nav1 = mockNav();
|
||||
let nav2 = mockNav();
|
||||
let nav1 = mockNavController();
|
||||
let nav2 = mockNavController();
|
||||
app.setRootNav(nav1);
|
||||
|
||||
nav1.registerChildNav(nav2);
|
||||
@@ -352,13 +352,13 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should get active NavController when only one nav controller', () => {
|
||||
let nav = mockNav();
|
||||
let nav = mockNavController();
|
||||
app.setRootNav(nav);
|
||||
expect(app.getActiveNav()).toBe(nav);
|
||||
});
|
||||
|
||||
it('should set/get the root nav controller', () => {
|
||||
let nav = mockNav();
|
||||
let nav = mockNavController();
|
||||
app.setRootNav(nav);
|
||||
expect(app.getRootNav()).toBe(nav);
|
||||
});
|
||||
@@ -443,40 +443,13 @@ describe('App', () => {
|
||||
var app: App;
|
||||
var config: Config;
|
||||
var platform: Platform;
|
||||
var _cd: any;
|
||||
|
||||
function mockNav(): Nav {
|
||||
return new Nav(null, null, null, config, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function mockTabs(): Tabs {
|
||||
return new Tabs(null, null, null, config, null, null, null);
|
||||
}
|
||||
|
||||
function mockTab(parentTabs: Tabs): Tab {
|
||||
var tab = new Tab(parentTabs, app, config, null, null, null, null, null, _cd, null);
|
||||
parentTabs.add(tab);
|
||||
tab.root = SomePage;
|
||||
tab.load = function(opts: any, cb: Function) {
|
||||
cb();
|
||||
};
|
||||
return tab;
|
||||
}
|
||||
|
||||
@Component({})
|
||||
class SomePage {}
|
||||
|
||||
beforeEach(() => {
|
||||
config = new Config();
|
||||
platform = new Platform();
|
||||
app = new App(config, platform);
|
||||
_cd = {
|
||||
reattach: function(){},
|
||||
detach: function(){}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
0
src/components/app/test/gesture-collision/e2e.ts
Normal file
0
src/components/app/test/gesture-collision/e2e.ts
Normal file
70
src/components/app/test/gesture-collision/index.ts
Normal file
70
src/components/app/test/gesture-collision/index.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { ionicBootstrap, MenuController, NavController, AlertController, Nav, Refresher } from '../../../../../src';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'page1.html'
|
||||
})
|
||||
class Page1 {
|
||||
constructor(private nav: NavController, private alertCtrl: AlertController) {}
|
||||
|
||||
presentAlert() {
|
||||
let alert = this.alertCtrl.create({
|
||||
title: 'New Friend!',
|
||||
message: 'Your friend, Obi wan Kenobi, just accepted your friend request!',
|
||||
cssClass: 'my-alert',
|
||||
buttons: ['Ok']
|
||||
});
|
||||
alert.present();
|
||||
}
|
||||
|
||||
goToPage1() {
|
||||
this.nav.push(Page1);
|
||||
}
|
||||
|
||||
doRefresh(refresher: Refresher) {
|
||||
setTimeout(() => {
|
||||
refresher.complete();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EPage {
|
||||
rootPage: any;
|
||||
changeDetectionCount: number = 0;
|
||||
pages: Array<{title: string, component: any}>;
|
||||
@ViewChild(Nav) nav: Nav;
|
||||
|
||||
constructor(private menu: MenuController) {
|
||||
this.rootPage = Page1;
|
||||
|
||||
this.pages = [
|
||||
{ title: 'Page 1', component: Page1 },
|
||||
{ title: 'Page 2', component: Page1 },
|
||||
{ title: 'Page 3', component: Page1 },
|
||||
];
|
||||
}
|
||||
|
||||
openPage(page: any) {
|
||||
// Reset the content nav to have just this page
|
||||
// we wouldn't want the back button to show in this scenario
|
||||
this.nav.setRoot(page.component).then(() => {
|
||||
// wait for the root page to be completely loaded
|
||||
// then close the menu
|
||||
this.menu.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<ion-nav [root]="rootPage"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
rootPage = E2EPage;
|
||||
}
|
||||
|
||||
ionicBootstrap(E2EApp);
|
||||
159
src/components/app/test/gesture-collision/main.html
Normal file
159
src/components/app/test/gesture-collision/main.html
Normal file
@@ -0,0 +1,159 @@
|
||||
<ion-menu [content]="content" side="left" persistent="true">
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar secondary>
|
||||
<ion-title>Left Menu</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item-sliding *ngFor="let p of pages">
|
||||
<button ion-item (click)="openPage(p)">
|
||||
{{p.title}}
|
||||
</button>
|
||||
<ion-item-options side="left">
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
<ion-item-options>
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<button ion-item menuClose="left" class="e2eCloseLeftMenu" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar secondary>
|
||||
<ion-title>Footer</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
|
||||
</ion-menu>
|
||||
|
||||
|
||||
<ion-menu [content]="content" side="right">
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar danger>
|
||||
<ion-title>Right Menu</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<button ion-item *ngFor="let p of pages" (click)="openPage(p)">
|
||||
{{p.title}}
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" class="e2eCloseRightMenu" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
||||
|
||||
</ion-menu>
|
||||
|
||||
<ion-nav [root]="rootPage" #content swipeBackEnabled="true"></ion-nav>
|
||||
100
src/components/app/test/gesture-collision/page1.html
Normal file
100
src/components/app/test/gesture-collision/page1.html
Normal file
@@ -0,0 +1,100 @@
|
||||
<ion-header>
|
||||
|
||||
<ion-navbar>
|
||||
|
||||
<button menuToggle="left">
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
|
||||
<ion-title>
|
||||
Menu
|
||||
</ion-title>
|
||||
|
||||
<button menuToggle="right" right secondary>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
|
||||
</ion-navbar>
|
||||
|
||||
</ion-header>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-refresher (ionRefresh)="doRefresh($event)">
|
||||
|
||||
<ion-refresher-content
|
||||
pullingText="Pull to refresh..."
|
||||
refreshingSpinner="bubbles"
|
||||
refreshingText="Refreshing...">
|
||||
</ion-refresher-content>
|
||||
|
||||
</ion-refresher>
|
||||
|
||||
<h3>Page 1</h3>
|
||||
|
||||
<ion-list padding>
|
||||
<ion-item-sliding>
|
||||
<button ion-item class="e2eContentToggleLeftMenu" menuToggle="left">Toggle Left Menu</button>
|
||||
|
||||
<ion-item-options side="left">
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
<ion-item-options>
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
</ion-list>
|
||||
<ion-list>
|
||||
|
||||
<ion-item-sliding>
|
||||
<button ion-item class="e2eContentToggleRightMenu" menuToggle="right">Toggle Right Menu</button>
|
||||
|
||||
<ion-item-options side="left">
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
<ion-item-options>
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding>
|
||||
<ion-item>LEFT button</ion-item>
|
||||
|
||||
<ion-item-options side="left">
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding>
|
||||
<ion-item>RIGHT button</ion-item>
|
||||
|
||||
<ion-item-options>
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<button ion-item (click)="goToPage1()">Push same page</button>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>RANGE</ion-label>
|
||||
<ion-range></ion-range>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-sliding>
|
||||
<ion-item>
|
||||
<ion-label>SLIDING ITEM + RANGE</ion-label>
|
||||
<ion-range></ion-range>
|
||||
</ion-item>
|
||||
<ion-item-options side="left">
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
<ion-item-options>
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-list>
|
||||
|
||||
<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>
|
||||
|
||||
</ion-content>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Directive, ElementRef, Input } from '@angular/core';
|
||||
|
||||
import { AppRoot } from '../app/app';
|
||||
import { DisableScroll, GestureController, GestureDelegate } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
|
||||
|
||||
@@ -16,41 +16,21 @@ import { isTrueProperty } from '../../util/util';
|
||||
},
|
||||
})
|
||||
export class Backdrop {
|
||||
private static nuBackDrops: number = 0;
|
||||
|
||||
private static push(appRoot: AppRoot) {
|
||||
if (this.nuBackDrops === 0) {
|
||||
appRoot.disableScroll = true;
|
||||
}
|
||||
this.nuBackDrops++;
|
||||
}
|
||||
|
||||
private static pop(appRoot: AppRoot) {
|
||||
if (this.nuBackDrops > 0) {
|
||||
this.nuBackDrops--;
|
||||
|
||||
if (this.nuBackDrops === 0) {
|
||||
appRoot.disableScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private pushed: boolean = false;
|
||||
private _gestureID: number = null;
|
||||
@Input() disableScroll = true;
|
||||
|
||||
constructor(private _appRoot: AppRoot, private _elementRef: ElementRef) {}
|
||||
constructor(private _gestureCtrl: GestureController, private _elementRef: ElementRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (isTrueProperty(this.disableScroll)) {
|
||||
Backdrop.push(this._appRoot);
|
||||
this.pushed = true;
|
||||
this._gestureID = this._gestureCtrl.newID();
|
||||
this._gestureCtrl.disableScroll(this._gestureID);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.pushed) {
|
||||
Backdrop.pop(this._appRoot);
|
||||
this.pushed = false;
|
||||
if (this._gestureID) {
|
||||
this._gestureCtrl.enableScroll(this._gestureID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -524,7 +524,7 @@ export class Content extends Ion {
|
||||
ele = parentEle;
|
||||
let tabbarEle: HTMLElement;
|
||||
|
||||
while (ele && ele.tagName !== 'ION-MODAL' && !ele.classList.contains('tab-subpage')) {
|
||||
while (ele && ele.tagName !== 'ION-MODAL') {
|
||||
|
||||
if (ele.tagName === 'ION-TABS') {
|
||||
tabbarEle = <HTMLElement>ele.firstElementChild;
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Form } from '../../util/form';
|
||||
import { Item } from '../item/item';
|
||||
import { merge, isBlank, isPresent, isTrueProperty, isArray, isString } from '../../util/util';
|
||||
import { dateValueRange, renderDateTime, renderTextFormat, convertFormatToKey, getValueFromFormat, parseTemplate, parseDate, updateDate, DateTimeData, convertDataToISO, daysInMonth, dateSortValue, dateDataSortValue, LocaleData } from '../../util/datetime-util';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
|
||||
export const DATETIME_VALUE_ACCESSOR = new Provider(
|
||||
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DateTime), multi: true});
|
||||
|
||||
@@ -7,7 +7,7 @@ import {ionicBootstrap, InfiniteScroll, NavController} from '../../../../../src'
|
||||
})
|
||||
class E2EPage1 {
|
||||
@ViewChild(InfiniteScroll) infiniteScroll: InfiniteScroll;
|
||||
items = [];
|
||||
items: number[] = [];
|
||||
enabled: boolean = true;
|
||||
|
||||
constructor(private nav: NavController) {
|
||||
@@ -68,7 +68,7 @@ function getAsyncData(): Promise<any[]> {
|
||||
return new Promise(resolve => {
|
||||
|
||||
setTimeout(() => {
|
||||
let data = [];
|
||||
let data: number[] = [];
|
||||
for (var i = 0; i < 30; i++) {
|
||||
data.push(i);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { isTrueProperty } from '../../util/util';
|
||||
import { Item } from '../item/item';
|
||||
import { NativeInput, NextInput } from './native-input';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
import { Platform } from '../../platform/platform';
|
||||
|
||||
|
||||
@@ -27,6 +28,7 @@ export class InputBase {
|
||||
protected _autoFocusAssist: string;
|
||||
protected _autoComplete: string;
|
||||
protected _autoCorrect: string;
|
||||
protected _nav: NavControllerBase;
|
||||
|
||||
inputControl: NgControl;
|
||||
|
||||
@@ -44,9 +46,10 @@ export class InputBase {
|
||||
protected _platform: Platform,
|
||||
protected _elementRef: ElementRef,
|
||||
protected _scrollView: Content,
|
||||
protected _nav: NavController,
|
||||
nav: NavController,
|
||||
ngControl: NgControl
|
||||
) {
|
||||
this._nav = <NavControllerBase>nav;
|
||||
this._useAssist = config.getBoolean('scrollAssist', false);
|
||||
this._usePadding = config.getBoolean('scrollPadding', this._useAssist);
|
||||
this._keyboardHeight = config.getNumber('keyboardHeight');
|
||||
|
||||
@@ -16,6 +16,14 @@ $text-input-ios-input-clear-icon-color: rgba(0, 0, 0, .5) !default;
|
||||
$text-input-ios-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='" + $text-input-ios-input-clear-icon-color + "' d='M403.1,108.9c-81.2-81.2-212.9-81.2-294.2,0s-81.2,212.9,0,294.2c81.2,81.2,212.9,81.2,294.2,0S484.3,190.1,403.1,108.9z M352,340.2L340.2,352l-84.4-84.2l-84,83.8L160,339.8l84-83.8l-84-83.8l11.8-11.8l84,83.8l84.4-84.2l11.8,11.8L267.6,256L352,340.2z'/></svg>" !default;
|
||||
$text-input-ios-input-clear-icon-size: 18px !default;
|
||||
|
||||
$text-input-ios-show-focus-highlight: false !default;
|
||||
$text-input-ios-show-valid-highlight: $text-input-ios-show-focus-highlight !default;
|
||||
$text-input-ios-show-invalid-highlight: $text-input-ios-show-focus-highlight !default;
|
||||
|
||||
$text-input-ios-highlight-color: color($colors-ios, primary) !default;
|
||||
$text-input-ios-highlight-color-valid: color($colors-ios, secondary) !default;
|
||||
$text-input-ios-highlight-color-invalid: color($colors-ios, danger) !default;
|
||||
|
||||
|
||||
// iOS Default Input
|
||||
// --------------------------------------------------
|
||||
@@ -82,3 +90,61 @@ ion-input[clearInput] {
|
||||
|
||||
background-size: $text-input-ios-input-clear-icon-size;
|
||||
}
|
||||
|
||||
|
||||
// iOS Highlighted Input
|
||||
// --------------------------------------------------
|
||||
|
||||
// Input highlight mixin for focus, valid, and invalid states
|
||||
@mixin ios-input-highlight($highlight-color) {
|
||||
border-bottom-color: $highlight-color;
|
||||
}
|
||||
|
||||
// Show the focus highlight when the input has focus
|
||||
@if ($text-input-ios-show-focus-highlight) {
|
||||
// In order to get a 2px border we need to add an inset
|
||||
// box-shadow 1px (this is to avoid the div resizing)
|
||||
.item-input.input-has-focus .item-inner {
|
||||
@include ios-input-highlight($text-input-ios-highlight-color);
|
||||
}
|
||||
|
||||
// The last item in a list has a border on the item, not the
|
||||
// inner item, so add it to the item itself
|
||||
ion-list .item-input.input-has-focus:last-child {
|
||||
@include ios-input-highlight($text-input-ios-highlight-color);
|
||||
|
||||
.item-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show the valid highlight when it has the .ng-valid class and a value
|
||||
@if ($text-input-ios-show-valid-highlight) {
|
||||
.item-input.ng-valid.input-has-value:not(.input-has-focus) .item-inner {
|
||||
@include ios-input-highlight($text-input-ios-highlight-color-valid);
|
||||
}
|
||||
|
||||
ion-list .item-input.ng-valid.input-has-value:not(.input-has-focus):last-child {
|
||||
@include ios-input-highlight($text-input-ios-highlight-color-valid);
|
||||
|
||||
.item-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show the invalid highlight when it has the invalid class and has been touched
|
||||
@if ($text-input-ios-show-invalid-highlight) {
|
||||
.item-input.ng-invalid.ng-touched:not(.input-has-focus) .item-inner {
|
||||
@include ios-input-highlight($text-input-ios-highlight-color-invalid);
|
||||
}
|
||||
|
||||
ion-list .item-input.ng-invalid.ng-touched:not(.input-has-focus):last-child {
|
||||
@include ios-input-highlight($text-input-ios-highlight-color-invalid);
|
||||
|
||||
.item-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
$text-input-md-background-color: $list-md-background-color !default;
|
||||
$text-input-md-highlight-color: color($colors-md, primary) !default;
|
||||
$text-input-md-hightlight-color-valid: color($colors-md, secondary) !default;
|
||||
$text-input-md-hightlight-color-invalid: color($colors-md, danger) !default;
|
||||
|
||||
$text-input-md-margin-top: $item-md-padding-top !default;
|
||||
$text-input-md-margin-right: ($item-md-padding-right / 2) !default;
|
||||
@@ -19,8 +16,13 @@ $text-input-md-input-clear-icon-color: #5b5b5b !default;
|
||||
$text-input-md-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><polygon fill='" + $text-input-md-input-clear-icon-color + "' points='405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798 375.202,405 405,375.202 285.798,256'/></svg>" !default;
|
||||
$text-input-md-input-clear-icon-size: 22px !default;
|
||||
|
||||
$text-input-md-show-success-highlight: true !default;
|
||||
$text-input-md-show-error-highlight: true !default;
|
||||
$text-input-md-show-focus-highlight: true !default;
|
||||
$text-input-md-show-valid-highlight: $text-input-md-show-focus-highlight !default;
|
||||
$text-input-md-show-invalid-highlight: $text-input-md-show-focus-highlight !default;
|
||||
|
||||
$text-input-md-highlight-color: color($colors-md, primary) !default;
|
||||
$text-input-md-highlight-color-valid: color($colors-md, secondary) !default;
|
||||
$text-input-md-highlight-color-invalid: color($colors-md, danger) !default;
|
||||
|
||||
|
||||
// Material Design Default Input
|
||||
@@ -46,42 +48,57 @@ $text-input-md-show-error-highlight: true !default;
|
||||
// Material Design Highlighted Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-input::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: $item-md-padding-left;
|
||||
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: transparent;
|
||||
content: "";
|
||||
// Input highlight mixin for focus, valid, and invalid states
|
||||
@mixin md-input-highlight($highlight-color) {
|
||||
border-bottom-color: $highlight-color;
|
||||
box-shadow: inset 0 -1px 0 0 $highlight-color;
|
||||
}
|
||||
|
||||
.item-input.input-has-focus::after {
|
||||
border-bottom-color: $text-input-md-highlight-color;
|
||||
}
|
||||
// Show the focus highlight when the input has focus
|
||||
@if ($text-input-md-show-focus-highlight) {
|
||||
// In order to get a 2px border we need to add an inset
|
||||
// box-shadow 1px (this is to avoid the div resizing)
|
||||
.item-input.input-has-focus .item-inner {
|
||||
@include md-input-highlight($text-input-md-highlight-color);
|
||||
}
|
||||
|
||||
@if($text-input-md-show-success-highlight) {
|
||||
.item-input.ng-valid.input-has-value {
|
||||
&::after {
|
||||
border-bottom-color: $text-input-md-hightlight-color-valid;
|
||||
}
|
||||
// The last item in a list has a border on the item, not the
|
||||
// inner item, so add it to the item itself
|
||||
ion-list .item-input.input-has-focus:last-child {
|
||||
@include md-input-highlight($text-input-md-highlight-color);
|
||||
|
||||
&.input-has-focus::after {
|
||||
border-bottom-color: $text-input-md-highlight-color;
|
||||
.item-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if($text-input-md-show-error-highlight) {
|
||||
.item-input.ng-invalid.ng-touched {
|
||||
&::after {
|
||||
border-bottom-color: $text-input-md-hightlight-color-invalid;
|
||||
}
|
||||
// Show the valid highlight when it has the .ng-valid class and a value
|
||||
@if ($text-input-md-show-valid-highlight) {
|
||||
.item-input.ng-valid.input-has-value:not(.input-has-focus) .item-inner {
|
||||
@include md-input-highlight($text-input-md-highlight-color-valid);
|
||||
}
|
||||
|
||||
&.input-has-focus::after {
|
||||
border-bottom-color: $text-input-md-highlight-color;
|
||||
ion-list .item-input.ng-valid.input-has-value:not(.input-has-focus):last-child {
|
||||
@include md-input-highlight($text-input-md-highlight-color-valid);
|
||||
|
||||
.item-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show the invalid highlight when it has the invalid class and has been touched
|
||||
@if ($text-input-md-show-invalid-highlight) {
|
||||
.item-input.ng-invalid.ng-touched:not(.input-has-focus) .item-inner {
|
||||
@include md-input-highlight($text-input-md-highlight-color-invalid);
|
||||
}
|
||||
|
||||
ion-list .item-input.ng-invalid.ng-touched:not(.input-has-focus):last-child {
|
||||
@include md-input-highlight($text-input-md-highlight-color-invalid);
|
||||
|
||||
.item-inner {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@ $text-input-wp-padding-vertical: 0 !default;
|
||||
$text-input-wp-padding-horizontal: 8px !default;
|
||||
$text-input-wp-line-height: 3rem !default;
|
||||
|
||||
$text-input-wp-highlight-color: color($colors-wp, primary) !default;
|
||||
$text-input-wp-hightlight-color-valid: color($colors-wp, secondary) !default;
|
||||
$text-input-wp-hightlight-color-invalid: color($colors-wp, danger) !default;
|
||||
|
||||
$text-input-wp-input-clear-icon-width: 30px !default;
|
||||
$text-input-wp-input-clear-icon-color: $input-wp-border-color !default;
|
||||
$text-input-wp-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><polygon fill='" + $text-input-wp-input-clear-icon-color + "' points='405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798 375.202,405 405,375.202 285.798,256'/></svg>" !default;
|
||||
$text-input-wp-input-clear-icon-size: 22px !default;
|
||||
|
||||
$text-input-wp-show-focus-highlight: true !default;
|
||||
$text-input-wp-show-valid-highlight: $text-input-wp-show-focus-highlight !default;
|
||||
$text-input-wp-show-invalid-highlight: $text-input-wp-show-focus-highlight !default;
|
||||
|
||||
$text-input-wp-highlight-color: color($colors-wp, primary) !default;
|
||||
$text-input-wp-highlight-color-valid: color($colors-wp, secondary) !default;
|
||||
$text-input-wp-highlight-color-invalid: color($colors-wp, danger) !default;
|
||||
|
||||
|
||||
// Windows Default Input
|
||||
@@ -53,16 +56,25 @@ $text-input-wp-input-clear-icon-size: 22px !default;
|
||||
// Windows Highlighted Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.input-has-focus .text-input {
|
||||
border-color: $text-input-wp-highlight-color;
|
||||
// Show the focus highlight when the input has focus
|
||||
@if ($text-input-wp-show-focus-highlight) {
|
||||
.item-input.input-has-focus .text-input {
|
||||
border-color: $text-input-wp-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
ion-input.ng-valid.input-has-value .text-input {
|
||||
border-color: $text-input-wp-hightlight-color-valid;
|
||||
// Show the valid highlight when it has the .ng-valid class and a value
|
||||
@if ($text-input-wp-show-valid-highlight) {
|
||||
.item-input.ng-valid.input-has-value:not(.input-has-focus) .text-input {
|
||||
border-color: $text-input-wp-highlight-color-valid;
|
||||
}
|
||||
}
|
||||
|
||||
ion-input.ng-invalid.ng-touched .text-input {
|
||||
border-color: $text-input-wp-hightlight-color-invalid;
|
||||
// Show the invalid highlight when it has the invalid class and has been touched
|
||||
@if ($text-input-wp-show-invalid-highlight) {
|
||||
.item-input.ng-invalid.ng-touched:not(.input-has-focus) .text-input {
|
||||
border-color: $text-input-wp-highlight-color-invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
0
src/components/input/test/highlight/e2e.ts
Normal file
0
src/components/input/test/highlight/e2e.ts
Normal file
54
src/components/input/test/highlight/index.ts
Normal file
54
src/components/input/test/highlight/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, Validators } from '@angular/common';
|
||||
import { ionicBootstrap } from '../../../../../src';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EPage {
|
||||
loginForm: any;
|
||||
|
||||
login = {
|
||||
email: 'help@ionic.io',
|
||||
username: 'admin',
|
||||
};
|
||||
|
||||
submitted: boolean = false;
|
||||
|
||||
constructor(fb: FormBuilder) {
|
||||
this.loginForm = fb.group({
|
||||
email: ["", Validators.compose([
|
||||
Validators.required,
|
||||
this.emailValidator
|
||||
])],
|
||||
username: [""],
|
||||
password: ["", Validators.required],
|
||||
comments: ["", Validators.required],
|
||||
inset: ["", Validators.required]
|
||||
});
|
||||
}
|
||||
|
||||
emailValidator(control: any) {
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
||||
|
||||
if (!EMAIL_REGEXP.test(control.value)) {
|
||||
return {invalidEmail: true};
|
||||
}
|
||||
}
|
||||
|
||||
submit(ev: UIEvent, value: any) {
|
||||
console.log("Submitted", value);
|
||||
this.submitted = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<ion-nav [root]="root"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
root = E2EPage;
|
||||
}
|
||||
|
||||
ionicBootstrap(E2EApp);
|
||||
105
src/components/input/test/highlight/main.html
Normal file
105
src/components/input/test/highlight/main.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<ion-header>
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Form Inputs</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
</ion-header>
|
||||
|
||||
|
||||
<ion-content class="outer-content">
|
||||
<form [formGroup]="loginForm" #mf="ngForm" novalidate>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label stacked>Stacked</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.email" formControlName="email" type="email" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.username" formControlName="username"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>Fixed</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.password" formControlName="password" type="password" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Inline</ion-label>
|
||||
<ion-textarea clearInput [(ngModel)]="login.comments" formControlName="comments" required>Comment value</ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item inset>
|
||||
<ion-label>Inset</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.inset" formControlName="inset" required></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-icon item-left name="logo-buffer"></ion-icon>
|
||||
<ion-label stacked>Stacked</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.email" formControlName="email" type="email" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon item-left name="apps"></ion-icon>
|
||||
<ion-label floating>Floating</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.username" formControlName="username"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon item-left name="browsers"></ion-icon>
|
||||
<ion-label fixed>Fixed</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.password" formControlName="password" type="password" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon item-left name="card"></ion-icon>
|
||||
<ion-label>Inline</ion-label>
|
||||
<ion-textarea clearInput [(ngModel)]="login.comments" formControlName="comments" required>Comment value</ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item inset>
|
||||
<ion-icon item-left name="checkmark-circle"></ion-icon>
|
||||
<ion-label>Inset</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.inset" formControlName="inset" required></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-card>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-icon item-left name="logo-buffer"></ion-icon>
|
||||
<ion-label stacked>Stacked</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.email" formControlName="email" type="email" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon item-left name="apps"></ion-icon>
|
||||
<ion-label floating>Floating</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.username" formControlName="username"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon item-left name="browsers"></ion-icon>
|
||||
<ion-label fixed>Fixed</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.password" formControlName="password" type="password" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon item-left name="card"></ion-icon>
|
||||
<ion-label>Inline</ion-label>
|
||||
<ion-textarea clearInput [(ngModel)]="login.comments" formControlName="comments" required>Comment value</ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item inset>
|
||||
<ion-icon item-left name="checkmark-circle"></ion-icon>
|
||||
<ion-label>Inset</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.inset" formControlName="inset" required></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
</form>
|
||||
</ion-content>
|
||||
@@ -25,12 +25,13 @@ export class ItemReorderGesture {
|
||||
|
||||
private events: UIEventManager = new UIEventManager(false);
|
||||
|
||||
constructor(public list: ItemReorder) {
|
||||
let element = this.list.getNativeElement();
|
||||
this.events.pointerEvents(element,
|
||||
this.onDragStart.bind(this),
|
||||
this.onDragMove.bind(this),
|
||||
this.onDragEnd.bind(this));
|
||||
constructor(public reorderList: ItemReorder) {
|
||||
this.events.pointerEvents({
|
||||
element: this.reorderList.getNativeElement(),
|
||||
pointerDown: this.onDragStart.bind(this),
|
||||
pointerMove: this.onDragMove.bind(this),
|
||||
pointerUp: this.onDragEnd.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
private onDragStart(ev: any): boolean {
|
||||
@@ -44,7 +45,7 @@ export class ItemReorderGesture {
|
||||
console.error('ion-reorder does not contain $ionComponent');
|
||||
return false;
|
||||
}
|
||||
this.list.reorderPrepare();
|
||||
this.reorderList.reorderPrepare();
|
||||
|
||||
let item = reorderMark.getReorderNode();
|
||||
if (!item) {
|
||||
@@ -60,13 +61,13 @@ export class ItemReorderGesture {
|
||||
this.lastToIndex = indexForItem(item);
|
||||
|
||||
this.windowHeight = window.innerHeight - AUTO_SCROLL_MARGIN;
|
||||
this.lastScrollPosition = this.list.scrollContent(0);
|
||||
this.lastScrollPosition = this.reorderList.scrollContent(0);
|
||||
|
||||
this.offset = pointerCoord(ev);
|
||||
this.offset.y += this.lastScrollPosition;
|
||||
|
||||
item.classList.add(ITEM_REORDER_ACTIVE);
|
||||
this.list.reorderStart();
|
||||
this.reorderList.reorderStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,7 +95,7 @@ export class ItemReorderGesture {
|
||||
this.lastToIndex = toIndex;
|
||||
this.lastYcoord = posY;
|
||||
this.emptyZone = false;
|
||||
this.list.reorderMove(fromIndex, toIndex, this.selectedItemHeight);
|
||||
this.reorderList.reorderMove(fromIndex, toIndex, this.selectedItemHeight);
|
||||
}
|
||||
} else {
|
||||
this.emptyZone = true;
|
||||
@@ -125,7 +126,7 @@ export class ItemReorderGesture {
|
||||
} else {
|
||||
reorderInactive();
|
||||
}
|
||||
this.list.reorderEmit(fromIndex, toIndex);
|
||||
this.reorderList.reorderEmit(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
private itemForCoord(coord: Coordinates): HTMLElement {
|
||||
@@ -134,9 +135,9 @@ export class ItemReorderGesture {
|
||||
|
||||
private scroll(posY: number): number {
|
||||
if (posY < AUTO_SCROLL_MARGIN) {
|
||||
this.lastScrollPosition = this.list.scrollContent(-SCROLL_JUMP);
|
||||
this.lastScrollPosition = this.reorderList.scrollContent(-SCROLL_JUMP);
|
||||
} else if (posY > this.windowHeight) {
|
||||
this.lastScrollPosition = this.list.scrollContent(SCROLL_JUMP);
|
||||
this.lastScrollPosition = this.reorderList.scrollContent(SCROLL_JUMP);
|
||||
}
|
||||
return this.lastScrollPosition;
|
||||
}
|
||||
@@ -148,17 +149,11 @@ export class ItemReorderGesture {
|
||||
this.onDragEnd();
|
||||
this.events.unlistenAll();
|
||||
this.events = null;
|
||||
this.list = null;
|
||||
this.reorderList = null;
|
||||
}
|
||||
}
|
||||
|
||||
function itemForPosition(x: number, y: number): HTMLElement {
|
||||
let element = <HTMLElement>document.elementFromPoint(x, y);
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
if (element.nodeName !== 'ION-ITEM' && !element.hasAttribute('ion-item')) {
|
||||
return null;
|
||||
}
|
||||
return findReorderItem(element);
|
||||
}
|
||||
|
||||
@@ -1,111 +1,102 @@
|
||||
import {DragGesture} from '../../gestures/drag-gesture';
|
||||
import {ItemSliding} from './item-sliding';
|
||||
import {List} from '../list/list';
|
||||
import { ItemSliding } from './item-sliding';
|
||||
import { List } from '../list/list';
|
||||
|
||||
import {closest} from '../../util/dom';
|
||||
import { closest, Coordinates, pointerCoord } from '../../util/dom';
|
||||
import { PointerEvents, UIEventManager } from '../../util/ui-event-manager';
|
||||
import { GestureDelegate, GestureOptions, GesturePriority } from '../../gestures/gesture-controller';
|
||||
import { PanGesture } from '../../gestures/drag-gesture';
|
||||
|
||||
const DRAG_THRESHOLD = 20;
|
||||
const DRAG_THRESHOLD = 10;
|
||||
const MAX_ATTACK_ANGLE = 20;
|
||||
|
||||
export class ItemSlidingGesture extends DragGesture {
|
||||
onTap: any;
|
||||
selectedContainer: ItemSliding = null;
|
||||
openContainer: ItemSliding = null;
|
||||
export class ItemSlidingGesture extends PanGesture {
|
||||
private preSelectedContainer: ItemSliding = null;
|
||||
private selectedContainer: ItemSliding = null;
|
||||
private openContainer: ItemSliding = null;
|
||||
private firstCoordX: number;
|
||||
private firstTimestamp: number;
|
||||
|
||||
constructor(public list: List) {
|
||||
super(list.getNativeElement(), {
|
||||
direction: 'x',
|
||||
threshold: DRAG_THRESHOLD
|
||||
maxAngle: MAX_ATTACK_ANGLE,
|
||||
threshold: DRAG_THRESHOLD,
|
||||
gesture: list.gestureCtrl.create('item-sliding', {
|
||||
priority: GesturePriority.SlidingItem,
|
||||
})
|
||||
});
|
||||
this.listen();
|
||||
}
|
||||
|
||||
onTapCallback(ev: any) {
|
||||
if (isFromOptionButtons(ev)) {
|
||||
return;
|
||||
canStart(ev: any): boolean {
|
||||
if (this.selectedContainer) {
|
||||
return false;
|
||||
}
|
||||
let didClose = this.closeOpened();
|
||||
if (didClose) {
|
||||
console.debug('tap close sliding item, preventDefault');
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onDragStart(ev: any): boolean {
|
||||
let angle = Math.abs(ev.angle);
|
||||
if (angle > MAX_ATTACK_ANGLE && Math.abs(angle - 180) > MAX_ATTACK_ANGLE) {
|
||||
// Get swiped sliding container
|
||||
let container = getContainer(ev);
|
||||
if (!container) {
|
||||
this.closeOpened();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.selectedContainer) {
|
||||
console.debug('onDragStart, another container is already selected');
|
||||
return false;
|
||||
}
|
||||
|
||||
let container = getContainer(ev);
|
||||
if (!container) {
|
||||
console.debug('onDragStart, no itemContainerEle');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close open container if it is not the selected one.
|
||||
if (container !== this.openContainer) {
|
||||
this.closeOpened();
|
||||
}
|
||||
|
||||
this.selectedContainer = container;
|
||||
this.openContainer = container;
|
||||
container.startSliding(ev.center.x);
|
||||
|
||||
let coord = pointerCoord(ev);
|
||||
this.preSelectedContainer = container;
|
||||
this.firstCoordX = coord.x;
|
||||
this.firstTimestamp = Date.now();
|
||||
return true;
|
||||
}
|
||||
|
||||
onDrag(ev: any): boolean {
|
||||
if (this.selectedContainer) {
|
||||
this.selectedContainer.moveSliding(ev.center.x);
|
||||
ev.preventDefault();
|
||||
}
|
||||
return;
|
||||
onDragStart(ev: any) {
|
||||
ev.preventDefault();
|
||||
|
||||
let coord = pointerCoord(ev);
|
||||
this.selectedContainer = this.openContainer = this.preSelectedContainer;
|
||||
this.selectedContainer.startSliding(coord.x);
|
||||
}
|
||||
|
||||
onDragMove(ev: any) {
|
||||
ev.preventDefault();
|
||||
|
||||
let coordX = pointerCoord(ev).x;
|
||||
this.selectedContainer.moveSliding(coordX);
|
||||
}
|
||||
|
||||
onDragEnd(ev: any) {
|
||||
if (!this.selectedContainer) {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
|
||||
let openAmount = this.selectedContainer.endSliding(ev.velocityX);
|
||||
let coordX = pointerCoord(ev).x;
|
||||
let deltaX = (coordX - this.firstCoordX);
|
||||
let deltaT = (Date.now() - this.firstTimestamp);
|
||||
let openAmount = this.selectedContainer.endSliding(deltaX / deltaT);
|
||||
this.selectedContainer = null;
|
||||
this.preSelectedContainer = null;
|
||||
}
|
||||
|
||||
// TODO: I am not sure listening for a tap event is the best idea
|
||||
// we should try mousedown/touchstart
|
||||
if (openAmount === 0) {
|
||||
this.openContainer = null;
|
||||
this.off('tap', this.onTap);
|
||||
this.onTap = null;
|
||||
} else if (!this.onTap) {
|
||||
this.onTap = (event: any) => this.onTapCallback(event);
|
||||
this.on('tap', this.onTap);
|
||||
}
|
||||
notCaptured(ev: any) {
|
||||
this.closeOpened();
|
||||
}
|
||||
|
||||
closeOpened(): boolean {
|
||||
if (!this.openContainer) {
|
||||
return false;
|
||||
}
|
||||
this.openContainer.close();
|
||||
this.openContainer = null;
|
||||
this.selectedContainer = null;
|
||||
this.off('tap', this.onTap);
|
||||
this.onTap = null;
|
||||
return true;
|
||||
|
||||
if (this.openContainer) {
|
||||
this.openContainer.close();
|
||||
this.openContainer = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unlisten() {
|
||||
destroy() {
|
||||
super.destroy();
|
||||
this.closeOpened();
|
||||
super.unlisten();
|
||||
|
||||
this.list = null;
|
||||
this.preSelectedContainer = null;
|
||||
this.selectedContainer = null;
|
||||
this.openContainer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +106,4 @@ function getContainer(ev: any): ItemSliding {
|
||||
return (<any>ele)['$ionComponent'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isFromOptionButtons(ev: any): boolean {
|
||||
let button = closest(ev.target, '.button', true);
|
||||
if (!button) {
|
||||
return false;
|
||||
}
|
||||
return !!closest(button, 'ion-item-options', true);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { Item } from './item';
|
||||
import { isPresent } from '../../util/util';
|
||||
import { List } from '../list/list';
|
||||
|
||||
const SWIPE_MARGIN = 20;
|
||||
const SWIPE_MARGIN = 30;
|
||||
const ELASTIC_FACTOR = 0.55;
|
||||
|
||||
export const enum ItemSideFlags {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController, NavParams} from '../../../../../src';
|
||||
import { Component } from '@angular/core';
|
||||
import { ionicBootstrap, NavController, NavParams } from '../../../../../src';
|
||||
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Content } from '../content/content';
|
||||
import { Ion } from '../ion';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { ItemSlidingGesture } from '../item/item-sliding-gesture';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
|
||||
/**
|
||||
* The List is a widely used interface element in almost any mobile app,
|
||||
@@ -29,7 +30,10 @@ export class List extends Ion {
|
||||
private _containsSlidingItems: boolean = false;
|
||||
private _slidingGesture: ItemSlidingGesture;
|
||||
|
||||
constructor(elementRef: ElementRef, private _rendered: Renderer) {
|
||||
constructor(
|
||||
elementRef: ElementRef,
|
||||
private _rendered: Renderer,
|
||||
public gestureCtrl: GestureController) {
|
||||
super(elementRef);
|
||||
}
|
||||
|
||||
@@ -78,16 +82,17 @@ export class List extends Ion {
|
||||
this._updateSlidingState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private _updateSlidingState() {
|
||||
let shouldSlide = this._enableSliding && this._containsSlidingItems;
|
||||
if (!shouldSlide) {
|
||||
this._slidingGesture && this._slidingGesture.unlisten();
|
||||
this._slidingGesture && this._slidingGesture.destroy();
|
||||
this._slidingGesture = null;
|
||||
|
||||
} else if (!this._slidingGesture) {
|
||||
console.debug('enableSlidingItems');
|
||||
this._slidingGesture = new ItemSlidingGesture(this);
|
||||
this._slidingGesture.listen();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,10 @@ export class MenuController {
|
||||
open(menuId?: string): Promise<boolean> {
|
||||
let menu = this.get(menuId);
|
||||
if (menu) {
|
||||
let openedMenu = this.getOpen();
|
||||
if (openedMenu && menu !== openedMenu) {
|
||||
openedMenu.setOpen(false, false);
|
||||
}
|
||||
return menu.open();
|
||||
}
|
||||
|
||||
@@ -147,7 +151,7 @@ export class MenuController {
|
||||
|
||||
} else {
|
||||
// find the menu that is open
|
||||
menu = this._menus.find(m => m.isOpen);
|
||||
menu = this.getOpen();
|
||||
}
|
||||
|
||||
if (menu) {
|
||||
@@ -158,11 +162,6 @@ export class MenuController {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
tempDisable(temporarilyDisable: boolean) {
|
||||
this._menus.forEach(menu => {
|
||||
menu.tempDisable(temporarilyDisable);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the menu. If it's closed, it will open, and if opened, it
|
||||
@@ -173,6 +172,10 @@ export class MenuController {
|
||||
toggle(menuId?: string): Promise<boolean> {
|
||||
let menu = this.get(menuId);
|
||||
if (menu) {
|
||||
let openedMenu = this.getOpen();
|
||||
if (openedMenu && menu !== openedMenu) {
|
||||
openedMenu.setOpen(false, false);
|
||||
}
|
||||
return menu.toggle();
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
@@ -229,7 +232,7 @@ export class MenuController {
|
||||
* provided, then it'll try to find the menu using the menu's `id`
|
||||
* property. If a menu is not found then it'll return `null`.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Menu} Returns the instance of the menu if found, otherwise `null`.
|
||||
* @return {Menu} Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
get(menuId?: string): Menu {
|
||||
var menu: Menu;
|
||||
@@ -252,12 +255,21 @@ export class MenuController {
|
||||
|
||||
// return the first enabled menu
|
||||
menu = this._menus.find(m => m.enabled);
|
||||
if (menu) return menu;
|
||||
if (menu) {
|
||||
return menu;
|
||||
}
|
||||
|
||||
// get the first menu in the array, if one exists
|
||||
return (this._menus.length ? this._menus[0] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Menu} Returns the instance of the menu already opened, otherwise `null`.
|
||||
*/
|
||||
getOpen(): Menu {
|
||||
return this._menus.find(m => m.isOpen);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array<Menu>} Returns an array of all menu instances.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {Menu} from './menu';
|
||||
import {SlideEdgeGesture} from '../../gestures/slide-edge-gesture';
|
||||
import {SlideData} from '../../gestures/slide-gesture';
|
||||
import {assign} from '../../util/util';
|
||||
|
||||
import { Menu } from './menu';
|
||||
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
|
||||
import { SlideData } from '../../gestures/slide-gesture';
|
||||
import { assign } from '../../util/util';
|
||||
import { GesturePriority } from '../../gestures/gesture-controller';
|
||||
|
||||
/**
|
||||
* Gesture attached to the content which the menu is assigned to
|
||||
@@ -10,61 +10,29 @@ import {assign} from '../../util/util';
|
||||
export class MenuContentGesture extends SlideEdgeGesture {
|
||||
|
||||
constructor(public menu: Menu, contentEle: HTMLElement, options: any = {}) {
|
||||
|
||||
super(contentEle, assign({
|
||||
direction: 'x',
|
||||
edge: menu.side,
|
||||
threshold: 0,
|
||||
maxEdgeStart: menu.maxEdgeStart || 75
|
||||
maxEdgeStart: menu.maxEdgeStart || 50,
|
||||
maxAngle: 40,
|
||||
gesture: menu.gestureCtrl.create('menu-swipe', {
|
||||
priority: GesturePriority.MenuSwipe,
|
||||
})
|
||||
}, options));
|
||||
}
|
||||
|
||||
canStart(ev: any) {
|
||||
canStart(ev: any): boolean {
|
||||
let menu = this.menu;
|
||||
|
||||
if (!menu.enabled || !menu.swipeEnabled) {
|
||||
console.debug('menu can not start, isEnabled:', menu.enabled, 'isSwipeEnabled:', menu.swipeEnabled, 'side:', menu.side);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ev.distance > 50) {
|
||||
// the distance is longer than you'd expect a side menu swipe to be
|
||||
console.debug('menu can not start, distance too far:', ev.distance, 'side:', menu.side);
|
||||
if (menu.isOpen) {
|
||||
return true;
|
||||
} else if (menu.getMenuController().getOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.debug('menu canStart,', menu.side, 'isOpen', menu.isOpen, 'angle', ev.angle, 'distance', ev.distance);
|
||||
|
||||
if (menu.side === 'right') {
|
||||
// right side
|
||||
if (menu.isOpen) {
|
||||
// right side, opened
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// right side, closed
|
||||
if ((ev.angle > 140 && ev.angle <= 180) || (ev.angle > -140 && ev.angle <= -180)) {
|
||||
return super.canStart(ev);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// left side
|
||||
if (menu.isOpen) {
|
||||
// left side, opened
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// left side, closed
|
||||
if (ev.angle > -40 && ev.angle < 40) {
|
||||
return super.canStart(ev);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// didn't pass the test, don't open this menu
|
||||
return false;
|
||||
return super.canStart(ev);
|
||||
}
|
||||
|
||||
// Set CSS, then wait one frame for it to apply before sliding starts
|
||||
@@ -77,7 +45,6 @@ export class MenuContentGesture extends SlideEdgeGesture {
|
||||
let z = (this.menu.side === 'right' ? slide.min : slide.max);
|
||||
let stepValue = (slide.distance / z);
|
||||
console.debug('menu gesture, onSlide', this.menu.side, 'distance', slide.distance, 'min', slide.min, 'max', slide.max, 'z', z, 'stepValue', stepValue);
|
||||
ev.srcEvent.preventDefault();
|
||||
ev.preventDefault();
|
||||
this.menu.swipeProgress(stepValue);
|
||||
}
|
||||
@@ -85,25 +52,24 @@ export class MenuContentGesture extends SlideEdgeGesture {
|
||||
onSlideEnd(slide: SlideData, ev: any) {
|
||||
let z = (this.menu.side === 'right' ? slide.min : slide.max);
|
||||
let currentStepValue = (slide.distance / z);
|
||||
|
||||
let velocity = slide.velocity;
|
||||
z = Math.abs(z * 0.5);
|
||||
let shouldCompleteRight = (ev.velocityX >= 0)
|
||||
&& (ev.velocityX > 0.2 || slide.delta > z);
|
||||
|
||||
let shouldCompleteLeft = (ev.velocityX <= 0)
|
||||
&& (ev.velocityX < -0.2 || slide.delta < -z);
|
||||
|
||||
let shouldCompleteRight = (velocity >= 0)
|
||||
&& (velocity > 0.2 || slide.delta > z);
|
||||
|
||||
let shouldCompleteLeft = (velocity <= 0)
|
||||
&& (velocity < -0.2 || slide.delta < -z);
|
||||
|
||||
console.debug(
|
||||
'menu gesture, onSlide', this.menu.side,
|
||||
'distance', slide.distance,
|
||||
'delta', slide.delta,
|
||||
'velocityX', ev.velocityX,
|
||||
'velocity', velocity,
|
||||
'min', slide.min,
|
||||
'max', slide.max,
|
||||
'shouldCompleteLeft', shouldCompleteLeft,
|
||||
'shouldCompleteRight', shouldCompleteRight,
|
||||
'currentStepValue', currentStepValue);
|
||||
|
||||
this.menu.swipeEnd(shouldCompleteLeft, shouldCompleteRight, currentStepValue);
|
||||
}
|
||||
|
||||
@@ -134,14 +100,3 @@ export class MenuContentGesture extends SlideEdgeGesture {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gesture attached to the actual menu itself
|
||||
*/
|
||||
export class MenuTargetGesture extends MenuContentGesture {
|
||||
constructor(menu: Menu, menuEle: HTMLElement) {
|
||||
super(menu, menuEle, {
|
||||
maxEdgeStart: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,15 @@ export class MenuType {
|
||||
ani: Animation = new Animation();
|
||||
isOpening: boolean;
|
||||
|
||||
setOpen(shouldOpen: boolean, done: Function) {
|
||||
this.ani
|
||||
.onFinish(done, true)
|
||||
.reverse(!shouldOpen)
|
||||
.play();
|
||||
setOpen(shouldOpen: boolean, animated: boolean, done: Function) {
|
||||
let ani = this.ani
|
||||
.onFinish(done, true)
|
||||
.reverse(!shouldOpen);
|
||||
if (animated) {
|
||||
ani.play();
|
||||
} else {
|
||||
ani.play({ duration: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
setProgressStart(isOpen: boolean) {
|
||||
|
||||
@@ -5,10 +5,11 @@ import { Config } from '../../config/config';
|
||||
import { Ion } from '../ion';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { MenuContentGesture, MenuTargetGesture } from './menu-gestures';
|
||||
import { MenuContentGesture } from './menu-gestures';
|
||||
import { MenuController } from './menu-controller';
|
||||
import { MenuType } from './menu-types';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
|
||||
|
||||
/**
|
||||
@@ -190,15 +191,13 @@ import { Platform } from '../../platform/platform';
|
||||
export class Menu extends Ion {
|
||||
private _preventTime: number = 0;
|
||||
private _cntEle: HTMLElement;
|
||||
private _cntGesture: MenuTargetGesture;
|
||||
private _menuGesture: MenuContentGesture;
|
||||
private _cntGesture: MenuContentGesture;
|
||||
private _type: MenuType;
|
||||
private _resizeUnreg: Function;
|
||||
private _isEnabled: boolean = true;
|
||||
private _isSwipeEnabled: boolean = true;
|
||||
private _isPers: boolean = false;
|
||||
private _init: boolean = false;
|
||||
private _prevEnabled: boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -302,7 +301,8 @@ export class Menu extends Ion {
|
||||
private _platform: Platform,
|
||||
private _renderer: Renderer,
|
||||
private _keyboard: Keyboard,
|
||||
private _zone: NgZone
|
||||
private _zone: NgZone,
|
||||
public gestureCtrl: GestureController
|
||||
) {
|
||||
super(_elementRef);
|
||||
}
|
||||
@@ -335,8 +335,7 @@ export class Menu extends Ion {
|
||||
self._renderer.setElementAttribute(self._elementRef.nativeElement, 'type', self.type);
|
||||
|
||||
// add the gestures
|
||||
self._cntGesture = new MenuContentGesture(self, self.getContentElement());
|
||||
self._menuGesture = new MenuTargetGesture(self, self.getNativeElement());
|
||||
self._cntGesture = new MenuContentGesture(self, document.body);
|
||||
|
||||
// register listeners if this menu is enabled
|
||||
// check if more than one menu is on the same side
|
||||
@@ -387,16 +386,12 @@ export class Menu extends Ion {
|
||||
if (self._isEnabled && self._isSwipeEnabled && !self._cntGesture.isListening) {
|
||||
// should listen, but is not currently listening
|
||||
console.debug('menu, gesture listen', self.side);
|
||||
self._zone.runOutsideAngular(function() {
|
||||
self._cntGesture.listen();
|
||||
self._menuGesture.listen();
|
||||
});
|
||||
self._cntGesture.listen();
|
||||
|
||||
} else if (self._cntGesture.isListening && (!self._isEnabled || !self._isSwipeEnabled)) {
|
||||
// should not listen, but is currently listening
|
||||
console.debug('menu, gesture unlisten', self.side);
|
||||
self._cntGesture.unlisten();
|
||||
self._menuGesture.unlisten();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,7 +413,7 @@ export class Menu extends Ion {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setOpen(shouldOpen: boolean): Promise<boolean> {
|
||||
setOpen(shouldOpen: boolean, animated: boolean = true): Promise<boolean> {
|
||||
// _isPrevented is used to prevent unwanted opening/closing after swiping open/close
|
||||
// or swiping open the menu while pressing down on the MenuToggle button
|
||||
if ((shouldOpen && this.isOpen) || this._isPrevented()) {
|
||||
@@ -428,7 +423,7 @@ export class Menu extends Ion {
|
||||
this._before();
|
||||
|
||||
return new Promise(resolve => {
|
||||
this._getType().setOpen(shouldOpen, () => {
|
||||
this._getType().setOpen(shouldOpen, animated, () => {
|
||||
this._after(shouldOpen);
|
||||
resolve(this.isOpen);
|
||||
});
|
||||
@@ -519,21 +514,6 @@ export class Menu extends Ion {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
tempDisable(temporarilyDisable: boolean) {
|
||||
if (temporarilyDisable) {
|
||||
this._prevEnabled = this._isEnabled;
|
||||
this._getType().setProgessStep(0);
|
||||
this.enable(false);
|
||||
|
||||
} else {
|
||||
this.enable(this._prevEnabled);
|
||||
this._after(false);
|
||||
}
|
||||
}
|
||||
|
||||
private _prevent() {
|
||||
// used to prevent unwanted opening/closing after swiping open/close
|
||||
// or swiping open the menu while pressing down on the MenuToggle
|
||||
@@ -617,13 +597,19 @@ export class Menu extends Ion {
|
||||
return this.backdrop.getNativeElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getMenuController(): MenuController {
|
||||
return this._menuCtrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this._menuCtrl.unregister(this);
|
||||
this._cntGesture && this._cntGesture.destroy();
|
||||
this._menuGesture && this._menuGesture.destroy();
|
||||
this._type && this._type.destroy();
|
||||
this._resizeUnreg && this._resizeUnreg();
|
||||
this._cntEle = null;
|
||||
|
||||
@@ -67,6 +67,14 @@ class E2EPage {
|
||||
});
|
||||
}
|
||||
|
||||
openRightMenu() {
|
||||
this.menu.open('right');
|
||||
}
|
||||
|
||||
openLeftMenu() {
|
||||
this.menu.open('left');
|
||||
}
|
||||
|
||||
onDrag(ev: any) {
|
||||
console.log('Menu is being dragged', ev);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
{{p.title}}
|
||||
</button>
|
||||
|
||||
<button ion-item (click)="openRightMenu()">
|
||||
Open Right Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="left" class="e2eCloseLeftMenu" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
@@ -90,6 +94,10 @@
|
||||
{{p.title}}
|
||||
</button>
|
||||
|
||||
<button ion-item (click)="openLeftMenu()">
|
||||
Open Left Menu
|
||||
</button>
|
||||
|
||||
<button ion-item menuClose="right" class="e2eCloseRightMenu" detail-none>
|
||||
Close Menu
|
||||
</button>
|
||||
@@ -148,6 +156,6 @@
|
||||
|
||||
</ion-menu>
|
||||
|
||||
<ion-nav [root]="rootPage" #content></ion-nav>
|
||||
<ion-nav [root]="rootPage" #content swipeBackEnabled="true"></ion-nav>
|
||||
|
||||
<div [hidden]="isChangeDetecting()"></div>
|
||||
|
||||
@@ -35,9 +35,19 @@
|
||||
|
||||
<h3>Page 1</h3>
|
||||
|
||||
<p>
|
||||
<button class="e2eContentToggleLeftMenu" menuToggle="left">Toggle Left Menu</button>
|
||||
</p>
|
||||
<ion-list>
|
||||
<ion-item-sliding>
|
||||
<button ion-item class="e2eContentToggleLeftMenu" menuToggle="left">Toggle Left Menu</button>
|
||||
|
||||
<ion-item-options side="left">
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
<ion-item-options>
|
||||
<button>Test</button>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-list>
|
||||
|
||||
|
||||
<p>
|
||||
<button class="e2eContentToggleRightMenu" menuToggle="right">Toggle Right Menu</button>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Component, Injectable } from '@angular/core';
|
||||
|
||||
import { ActionSheetController, Config, ionicBootstrap, ModalController, NavController, NavParams, PageTransition, Platform, TransitionOptions, ViewController } from '../../../../../src';
|
||||
import { ActionSheetController, App, Config, ionicBootstrap, ModalController, NavController, NavParams, PageTransition, Platform, TransitionOptions, ViewController } from '../../../../../src';
|
||||
|
||||
|
||||
@Injectable()
|
||||
class SomeComponentProvider {
|
||||
constructor(private config: Config) {
|
||||
console.log('SomeComponentProvider constructor')
|
||||
console.log('SomeComponentProvider constructor');
|
||||
}
|
||||
|
||||
getName() {
|
||||
@@ -17,7 +17,7 @@ class SomeComponentProvider {
|
||||
@Injectable()
|
||||
class SomeAppProvider {
|
||||
constructor(private config: Config) {
|
||||
console.log('SomeAppProvider constructor')
|
||||
console.log('SomeAppProvider constructor');
|
||||
}
|
||||
|
||||
getData() {
|
||||
@@ -84,7 +84,7 @@ class E2EPage {
|
||||
}
|
||||
|
||||
presentModalWithInputs() {
|
||||
let modal = this.modalCtrl.create(ModalWithInputs);
|
||||
let modal = this.modalCtrl.create(ModalWithInputs);
|
||||
modal.onDidDismiss((data: any) => {
|
||||
console.log('Modal with inputs data:', data);
|
||||
});
|
||||
@@ -98,7 +98,7 @@ class E2EPage {
|
||||
});
|
||||
}
|
||||
|
||||
presentNavigableModal(){
|
||||
presentNavigableModal() {
|
||||
this.modalCtrl.create(NavigableModal).present();
|
||||
}
|
||||
}
|
||||
@@ -117,11 +117,11 @@ class E2EPage {
|
||||
`
|
||||
})
|
||||
class NavigableModal {
|
||||
constructor(private navController:NavController) {
|
||||
constructor(private nav: NavController) {
|
||||
}
|
||||
|
||||
submit(){
|
||||
this.navController.push(NavigableModal2);
|
||||
this.nav.push(NavigableModal2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,10 +139,10 @@ class NavigableModal {
|
||||
`
|
||||
})
|
||||
class NavigableModal2 {
|
||||
constructor(private navController:NavController) {
|
||||
constructor(private navController: NavController) {
|
||||
}
|
||||
|
||||
submit(){
|
||||
submit() {
|
||||
this.navController.pop();
|
||||
}
|
||||
}
|
||||
@@ -188,23 +188,23 @@ class ModalPassData {
|
||||
this.viewCtrl.dismiss(this.data);
|
||||
}
|
||||
|
||||
ionViewLoaded(){
|
||||
ionViewLoaded() {
|
||||
console.log('ModalPassData ionViewLoaded fired');
|
||||
}
|
||||
|
||||
ionViewWillEnter(){
|
||||
ionViewWillEnter() {
|
||||
console.log('ModalPassData ionViewWillEnter fired');
|
||||
}
|
||||
|
||||
ionViewDidEnter(){
|
||||
ionViewDidEnter() {
|
||||
console.log('ModalPassData ionViewDidEnter fired');
|
||||
}
|
||||
|
||||
ionViewWillLeave(){
|
||||
ionViewWillLeave() {
|
||||
console.log('ModalPassData ionViewWillLeave fired');
|
||||
}
|
||||
|
||||
ionViewDidLeave(){
|
||||
ionViewDidLeave() {
|
||||
console.log('ModalPassData ionViewDidLeave fired');
|
||||
}
|
||||
}
|
||||
@@ -375,10 +375,10 @@ class ContactUs {
|
||||
})
|
||||
class ModalFirstPage {
|
||||
|
||||
private items:any[];
|
||||
constructor(private nav: NavController, private actionSheetCtrl: ActionSheetController) {
|
||||
private items: any[];
|
||||
constructor(private nav: NavController, private app: App, private actionSheetCtrl: ActionSheetController) {
|
||||
this.items = [];
|
||||
for ( let i = 0; i < 50; i++ ){
|
||||
for ( let i = 0; i < 50; i++ ) {
|
||||
this.items.push({
|
||||
value: (i + 1)
|
||||
});
|
||||
@@ -387,24 +387,24 @@ class ModalFirstPage {
|
||||
|
||||
push() {
|
||||
let page = ModalSecondPage;
|
||||
let params = { id: 8675309, myData: [1,2,3,4] };
|
||||
let params = { id: 8675309, myData: [1, 2, 3, 4] };
|
||||
|
||||
this.nav.push(page, params);
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.nav.rootNav.pop();
|
||||
this.app.getRootNav().pop();
|
||||
}
|
||||
|
||||
ionViewLoaded(){
|
||||
ionViewLoaded() {
|
||||
console.log('ModalFirstPage ionViewLoaded fired');
|
||||
}
|
||||
|
||||
ionViewWillEnter(){
|
||||
ionViewWillEnter() {
|
||||
console.log('ModalFirstPage ionViewWillEnter fired');
|
||||
}
|
||||
|
||||
ionViewDidEnter(){
|
||||
ionViewDidEnter() {
|
||||
console.log('ModalFirstPage ionViewDidEnter fired');
|
||||
}
|
||||
|
||||
@@ -430,8 +430,8 @@ class ModalFirstPage {
|
||||
// overlays are added and removed from the root navigation
|
||||
// find the root navigation, and pop this alert
|
||||
// when the alert is done animating out, then pop off the modal
|
||||
this.nav.rootNav.pop().then(() => {
|
||||
this.nav.rootNav.pop();
|
||||
this.app.getRootNav().pop().then(() => {
|
||||
this.app.getRootNav().pop();
|
||||
});
|
||||
|
||||
// by default an alert will dismiss itself
|
||||
@@ -477,15 +477,15 @@ class ModalSecondPage {
|
||||
console.log('Second page params:', params);
|
||||
}
|
||||
|
||||
ionViewLoaded(){
|
||||
ionViewLoaded() {
|
||||
console.log('ModalSecondPage ionViewLoaded');
|
||||
}
|
||||
|
||||
ionViewWillEnter(){
|
||||
ionViewWillEnter() {
|
||||
console.log('ModalSecondPage ionViewWillEnter');
|
||||
}
|
||||
|
||||
ionViewDidEnter(){
|
||||
ionViewDidEnter() {
|
||||
console.log('ModalSecondPage ionViewDidEnter');
|
||||
}
|
||||
}
|
||||
|
||||
1303
src/components/nav/nav-controller-base.ts
Normal file
1303
src/components/nav/nav-controller-base.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,3 +13,6 @@ export interface NavOptions {
|
||||
climbNav?: boolean;
|
||||
ev?: any;
|
||||
}
|
||||
|
||||
export const DIRECTION_BACK = 'back';
|
||||
export const DIRECTION_FORWARD = 'forward';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Directive, Optional } from '@angular/core';
|
||||
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { NavController } from './nav-controller';
|
||||
|
||||
import { noop } from '../../util/util';
|
||||
|
||||
/**
|
||||
* @name NavPop
|
||||
* @description
|
||||
* Directive for declaratively pop the current page off from the navigation stack.
|
||||
* Directive to declaratively pop the current page off from the
|
||||
* navigation stack.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
@@ -22,11 +23,7 @@ import { NavController } from './nav-controller';
|
||||
* @see {@link ../NavPush NavPush API Docs}
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[nav-pop]',
|
||||
host: {
|
||||
'(click)': 'onClick()',
|
||||
'role': 'link'
|
||||
}
|
||||
selector: '[navPop]'
|
||||
})
|
||||
export class NavPop {
|
||||
|
||||
@@ -36,10 +33,15 @@ export class NavPop {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onClick() {
|
||||
this._nav && this._nav.pop();
|
||||
@HostListener('click')
|
||||
onClick(): boolean {
|
||||
// If no target, or if target is _self, prevent default browser behavior
|
||||
if (this._nav) {
|
||||
this._nav.pop(null, noop);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { ComponentResolver, Directive, ElementRef, forwardRef, Inject, NgZone, O
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { MenuController } from '../menu/menu-controller';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -12,7 +12,7 @@ import { NavController } from '../nav/nav-controller';
|
||||
@Directive({
|
||||
selector: '[nav-portal]'
|
||||
})
|
||||
export class NavPortal extends NavController {
|
||||
export class NavPortal extends NavControllerBase {
|
||||
constructor(
|
||||
@Inject(forwardRef(() => App)) app: App,
|
||||
config: Config,
|
||||
@@ -21,11 +21,11 @@ export class NavPortal extends NavController {
|
||||
zone: NgZone,
|
||||
renderer: Renderer,
|
||||
compiler: ComponentResolver,
|
||||
menuCtrl: MenuController,
|
||||
gestureCtrl: GestureController,
|
||||
viewPort: ViewContainerRef
|
||||
) {
|
||||
super(null, app, config, keyboard, elementRef, zone, renderer, compiler, menuCtrl);
|
||||
this.isPortal = true;
|
||||
super(null, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||
this._isPortal = true;
|
||||
this.setViewport(viewPort);
|
||||
app.setPortal(this);
|
||||
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
import { Directive, Input, Optional } from '@angular/core';
|
||||
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
|
||||
import { NavController } from './nav-controller';
|
||||
import { noop } from '../../util/util';
|
||||
|
||||
/**
|
||||
* @name NavPush
|
||||
* @description
|
||||
* Directive for declaratively linking to a new page instead of using
|
||||
* {@link ../NavController/#push NavController.push}. Similar to ui-router's `ui-sref`.
|
||||
* Directive to declaratively push a new page to the current nav
|
||||
* stack.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <button [navPush]="pushPage"></button>
|
||||
* ```
|
||||
* To specify parameters you can use array syntax or the `nav-params` property:
|
||||
*
|
||||
* To specify parameters you can use array syntax or the `navParams`
|
||||
* property:
|
||||
*
|
||||
* ```html
|
||||
* <button [navPush]="pushPage" [navParams]="params"></button>
|
||||
* <button [navPush]="pushPage" [navParams]="params">Go</button>
|
||||
* ```
|
||||
* Where `pushPage` and `params` are specified in your component, and `pushPage`
|
||||
* contains a reference to a [@Page component](../../../config/Page/):
|
||||
*
|
||||
* Where `pushPage` and `params` are specified in your component,
|
||||
* and `pushPage` contains a reference to a
|
||||
* [@Page component](../../../config/Page/):
|
||||
*
|
||||
* ```ts
|
||||
* import {LoginPage} from 'login';
|
||||
* import { LoginPage } from './login';
|
||||
*
|
||||
* @Component({
|
||||
* template: `<button [navPush]="pushPage" [navParams]="params"></button>`
|
||||
* template: `<button [navPush]="pushPage" [navParams]="params">Go</button>`
|
||||
* })
|
||||
* class MyPage {
|
||||
* constructor(){
|
||||
@@ -32,61 +39,42 @@ import { NavController } from './nav-controller';
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### Alternate syntax
|
||||
* You can also use syntax similar to Angular2's router, passing an array to
|
||||
* NavPush:
|
||||
* ```html
|
||||
* <button [navPush]="[pushPage, params]"></button>
|
||||
* ```
|
||||
* @demo /docs/v2/demos/navigation/
|
||||
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
||||
* @see {@link ../NavPop NavPop API Docs}
|
||||
*
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[navPush]',
|
||||
host: {
|
||||
'(click)': 'onClick()',
|
||||
'role': 'link'
|
||||
}
|
||||
selector: '[navPush]'
|
||||
})
|
||||
export class NavPush {
|
||||
|
||||
/**
|
||||
* @input {Page} the page you want to push
|
||||
*/
|
||||
@Input() navPush: any;
|
||||
|
||||
/**
|
||||
* @input {any} Any parameters you want to pass along
|
||||
*/
|
||||
@Input() navParams: any;
|
||||
|
||||
constructor(
|
||||
@Optional() private _nav: NavController
|
||||
) {
|
||||
if (!_nav) {
|
||||
console.error('nav-push must be within a NavController');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @input {Page} The Page to push onto the Nav.
|
||||
*/
|
||||
onClick() {
|
||||
let destination: any, params: any;
|
||||
@Input() navPush: any[]|string;
|
||||
|
||||
if (this.navPush instanceof Array) {
|
||||
if (this.navPush.length > 2) {
|
||||
throw 'Too many [navPush] arguments, expects [View, { params }]';
|
||||
}
|
||||
destination = this.navPush[0];
|
||||
params = this.navPush[1] || this.navParams;
|
||||
/**
|
||||
* @input {any} Parameters to pass to the page.
|
||||
*/
|
||||
@Input() navParams: {[k: string]: any};
|
||||
|
||||
} else {
|
||||
destination = this.navPush;
|
||||
params = this.navParams;
|
||||
|
||||
constructor(@Optional() private _nav: NavController) {
|
||||
if (!_nav) {
|
||||
console.error('navPush must be within a NavController');
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
onClick(): boolean {
|
||||
// If no target, or if target is _self, prevent default browser behavior
|
||||
if (this._nav) {
|
||||
this._nav.push(this.navPush, this.navParams, noop);
|
||||
return false;
|
||||
}
|
||||
|
||||
this._nav && this._nav.push(destination, params);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { AfterViewInit, Component, ComponentResolver, ElementRef, Input, Optiona
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
import { Keyboard } from '../../util/keyboard';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { MenuController } from '../menu/menu-controller';
|
||||
import { NavController } from './nav-controller';
|
||||
import { NavControllerBase } from './nav-controller-base';
|
||||
import { ViewController } from './view-controller';
|
||||
|
||||
/**
|
||||
@@ -114,13 +114,13 @@ import { ViewController } from './view-controller';
|
||||
`,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Nav extends NavController implements AfterViewInit {
|
||||
export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
private _root: any;
|
||||
private _hasInit: boolean = false;
|
||||
|
||||
constructor(
|
||||
@Optional() viewCtrl: ViewController,
|
||||
@Optional() parent: NavController,
|
||||
@Optional() parent: NavControllerBase,
|
||||
app: App,
|
||||
config: Config,
|
||||
keyboard: Keyboard,
|
||||
@@ -128,9 +128,9 @@ export class Nav extends NavController implements AfterViewInit {
|
||||
zone: NgZone,
|
||||
renderer: Renderer,
|
||||
compiler: ComponentResolver,
|
||||
menuCtrl: MenuController
|
||||
gestureCtrl: GestureController
|
||||
) {
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, compiler, menuCtrl);
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||
|
||||
if (viewCtrl) {
|
||||
// an ion-nav can also act as an ion-page within a parent ion-nav
|
||||
@@ -164,9 +164,6 @@ export class Nav extends NavController implements AfterViewInit {
|
||||
this._hasInit = true;
|
||||
|
||||
if (this._root) {
|
||||
if (typeof this._root !== 'function') {
|
||||
throw 'The [root] property in <ion-nav> must be given a reference to a component class from within the constructor.';
|
||||
}
|
||||
this.push(this._root);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { assign } from '../../util/util';
|
||||
import { GestureController, GestureDelegate, GesturePriority } from '../../gestures/gesture-controller';
|
||||
import { MenuController } from '../menu/menu-controller';
|
||||
import { NavController } from './nav-controller';
|
||||
import { NavControllerBase } from './nav-controller-base';
|
||||
import { SlideData } from '../../gestures/slide-gesture';
|
||||
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
|
||||
|
||||
@@ -10,36 +11,32 @@ export class SwipeBackGesture extends SlideEdgeGesture {
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
options: any,
|
||||
private _nav: NavController,
|
||||
private _menuCtrl: MenuController
|
||||
private _nav: NavControllerBase,
|
||||
gestureCtlr: GestureController
|
||||
) {
|
||||
super(element, assign({
|
||||
direction: 'x',
|
||||
maxEdgeStart: 75
|
||||
maxEdgeStart: 75,
|
||||
gesture: gestureCtlr.create('goback-swipe', {
|
||||
priority: GesturePriority.GoBackSwipe,
|
||||
})
|
||||
}, options));
|
||||
}
|
||||
|
||||
canStart(ev: any) {
|
||||
canStart(ev: any): boolean {
|
||||
// the gesture swipe angle must be mainly horizontal and the
|
||||
// gesture distance would be relatively short for a swipe back
|
||||
// and swipe back must be possible on this nav controller
|
||||
if (ev.angle > -40 &&
|
||||
ev.angle < 40 &&
|
||||
ev.distance < 50 &&
|
||||
this._nav.canSwipeBack()) {
|
||||
// passed the tests, now see if the super says it's cool or not
|
||||
return super.canStart(ev);
|
||||
}
|
||||
|
||||
// nerp, not today
|
||||
return false;
|
||||
return (
|
||||
this._nav.canSwipeBack() &&
|
||||
super.canStart(ev)
|
||||
);
|
||||
}
|
||||
|
||||
onSlideBeforeStart(slideData: SlideData, ev: any) {
|
||||
console.debug('swipeBack, onSlideBeforeStart', ev.srcEvent.type);
|
||||
this._nav.swipeBackStart();
|
||||
|
||||
this._menuCtrl.tempDisable(true);
|
||||
onSlideBeforeStart(slideData: SlideData, ev: any) {
|
||||
console.debug('swipeBack, onSlideBeforeStart', ev.type);
|
||||
this._nav.swipeBackStart();
|
||||
}
|
||||
|
||||
onSlide(slide: SlideData) {
|
||||
@@ -49,15 +46,10 @@ export class SwipeBackGesture extends SlideEdgeGesture {
|
||||
}
|
||||
|
||||
onSlideEnd(slide: SlideData, ev: any) {
|
||||
let shouldComplete = (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
|
||||
|
||||
let shouldComplete = (Math.abs(slide.velocity) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
|
||||
let currentStepValue = (slide.distance / slide.max);
|
||||
|
||||
console.debug('swipeBack, onSlideEnd, shouldComplete', shouldComplete, 'currentStepValue', currentStepValue);
|
||||
|
||||
this._nav.swipeBackEnd(shouldComplete, currentStepValue);
|
||||
|
||||
this._menuCtrl.tempDisable(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { NavController, AlertController, Content } from '../../../../../src';
|
||||
import { ionicBootstrap } from '../../../../../src';
|
||||
import { ionicBootstrap, App } from '../../../../../src';
|
||||
import { NavParams, ViewController } from '../../../../../src';;
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ class FirstPage {
|
||||
class FullPage {
|
||||
constructor(
|
||||
private nav: NavController,
|
||||
private app: App,
|
||||
private alertCtrl: AlertController,
|
||||
private params: NavParams
|
||||
) {}
|
||||
@@ -184,8 +185,8 @@ class FullPage {
|
||||
// overlays are added and removed from the root navigation
|
||||
// ensure you using the root navigation, and pop this alert
|
||||
// when the alert is done animating out, then pop off the active page
|
||||
this.nav.rootNav.pop().then(() => {
|
||||
this.nav.rootNav.pop();
|
||||
this.app.getRootNav().pop().then(() => {
|
||||
this.app.getRootNav().pop();
|
||||
});
|
||||
|
||||
// by default an alert will dismiss itself
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController} from '../../../../../src';
|
||||
import { Component} from '@angular/core';
|
||||
import { ionicBootstrap, NavController } from '../../../../../src';
|
||||
|
||||
@Component({
|
||||
template: `<ion-nav [root]="root"></ion-nav>`,
|
||||
@@ -29,11 +29,11 @@ ionicBootstrap(E2EApp);
|
||||
})
|
||||
class LandingPage{
|
||||
|
||||
constructor(private _navController: NavController){
|
||||
constructor(private nav: NavController){
|
||||
}
|
||||
|
||||
goToPage(){
|
||||
this._navController.push(FirstPage);
|
||||
this.nav.push(FirstPage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController} from '../../../../../src';
|
||||
import { Component} from '@angular/core';
|
||||
import { ionicBootstrap, NavController } from '../../../../../src';
|
||||
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController} from '../../../../../src';
|
||||
import { Component} from '@angular/core';
|
||||
import { ionicBootstrap, NavController } from '../../../../../src';
|
||||
|
||||
|
||||
let delay = 100;
|
||||
@@ -16,7 +16,7 @@ let count = 0;
|
||||
`
|
||||
})
|
||||
class Page1 {
|
||||
tmr;
|
||||
tmr: number;
|
||||
|
||||
constructor(private nav: NavController) {}
|
||||
|
||||
@@ -50,7 +50,7 @@ class Page1 {
|
||||
`
|
||||
})
|
||||
class Page2 {
|
||||
tmr;
|
||||
tmr: number;
|
||||
|
||||
constructor(private nav: NavController) {}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ import { ViewController } from '../nav/view-controller';
|
||||
template: `
|
||||
<div *ngIf="col.prefix" class="picker-prefix" [style.width]="col.prefixWidth">{{col.prefix}}</div>
|
||||
<div class="picker-opts" #colEle [style.width]="col.optionsWidth">
|
||||
<button *ngFor="let o of col.options; let i=index" [style.transform]="o._trans" [style.transitionDuration]="o._dur" [class.picker-opt-selected]="col.selectedIndex === i" [class.picker-opt-disabled]="o.disabled" (click)="optClick($event, i)" type="button" category="picker-opt">
|
||||
<button *ngFor="let o of col.options; let i=index" [style.transform]="o._trans" [style.transitionDuration]="o._dur" [style.webkitTransform]="o._trans" [style.webkitTransitionDuration]="o._dur" [class.picker-opt-selected]="col.selectedIndex === i" [class.picker-opt-disabled]="o.disabled" (click)="optClick($event, i)" type="button" category="picker-opt">
|
||||
{{o.text}}
|
||||
</button>
|
||||
</div>
|
||||
@@ -74,11 +74,12 @@ export class PickerColumnCmp {
|
||||
this.setSelected(this.col.selectedIndex, 0);
|
||||
|
||||
// Listening for pointer events
|
||||
this.events.pointerEventsRef(this.elementRef,
|
||||
(ev: any) => this.pointerStart(ev),
|
||||
(ev: any) => this.pointerMove(ev),
|
||||
(ev: any) => this.pointerEnd(ev)
|
||||
);
|
||||
this.events.pointerEvents({
|
||||
elementRef: this.elementRef,
|
||||
pointerDown: this.pointerStart.bind(this),
|
||||
pointerMove: this.pointerMove.bind(this),
|
||||
pointerUp: this.pointerEnd.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -364,11 +364,12 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
|
||||
this._renderer.setElementStyle(this._bar.nativeElement, 'right', barR);
|
||||
|
||||
// add touchstart/mousedown listeners
|
||||
this._events.pointerEventsRef(this._slider,
|
||||
this.pointerDown.bind(this),
|
||||
this.pointerMove.bind(this),
|
||||
this.pointerUp.bind(this));
|
||||
|
||||
this._events.pointerEvents({
|
||||
elementRef: this._slider,
|
||||
pointerDown: this.pointerDown.bind(this),
|
||||
pointerMove: this.pointerMove.bind(this),
|
||||
pointerUp: this.pointerUp.bind(this)
|
||||
});
|
||||
this.createTicks();
|
||||
}
|
||||
|
||||
@@ -430,18 +431,12 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this._start !== null && this._active !== null) {
|
||||
// only use pointer move if it's a valid pointer
|
||||
// and we already have start coordinates
|
||||
// update the ratio for the active knob
|
||||
this.updateKnob(pointerCoord(ev), this._rect);
|
||||
|
||||
// update the ratio for the active knob
|
||||
this.updateKnob(pointerCoord(ev), this._rect);
|
||||
|
||||
// update the active knob's position
|
||||
this._active.position();
|
||||
this._pressed = this._active.pressed = true;
|
||||
|
||||
}
|
||||
// update the active knob's position
|
||||
this._active.position();
|
||||
this._pressed = this._active.pressed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Directive, EventEmitter, Host, Input, Output, NgZone } from '@angular/c
|
||||
|
||||
import { Content } from '../content/content';
|
||||
import { CSS, pointerCoord } from '../../util/dom';
|
||||
import { GestureController, GestureDelegate, GesturePriority } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { PointerEvents, UIEventManager } from '../../util/ui-event-manager';
|
||||
|
||||
@@ -98,6 +99,7 @@ export class Refresher {
|
||||
private _didStart: boolean;
|
||||
private _lastCheck: number = 0;
|
||||
private _isEnabled: boolean = true;
|
||||
private _gesture: GestureDelegate;
|
||||
private _events: UIEventManager = new UIEventManager(false);
|
||||
private _pointerEvents: PointerEvents;
|
||||
private _top: string = '';
|
||||
@@ -196,8 +198,11 @@ export class Refresher {
|
||||
@Output() ionStart: EventEmitter<Refresher> = new EventEmitter<Refresher>();
|
||||
|
||||
|
||||
constructor(@Host() private _content: Content, private _zone: NgZone) {
|
||||
constructor(@Host() private _content: Content, private _zone: NgZone, gestureCtrl: GestureController) {
|
||||
_content.addCssClass('has-refresher');
|
||||
this._gesture = gestureCtrl.create('refresher', {
|
||||
priority: GesturePriority.Refresher,
|
||||
});
|
||||
}
|
||||
|
||||
private _onStart(ev: TouchEvent): any {
|
||||
@@ -216,6 +221,10 @@ export class Refresher {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this._gesture.canStart()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let coord = pointerCoord(ev);
|
||||
console.debug('Pull-to-refresh, onStart', ev.type, 'y:', coord.y);
|
||||
|
||||
@@ -228,7 +237,7 @@ export class Refresher {
|
||||
|
||||
this.startY = this.currentY = coord.y;
|
||||
this.progress = 0;
|
||||
this.state = STATE_PULLING;
|
||||
this.state = STATE_INACTIVE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -242,6 +251,10 @@ export class Refresher {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!this._gesture.canStart()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do nothing if it's actively refreshing
|
||||
// or it's in the process of closing
|
||||
// or this was never a startY
|
||||
@@ -462,10 +475,12 @@ export class Refresher {
|
||||
this._events.unlistenAll();
|
||||
this._pointerEvents = null;
|
||||
if (shouldListen) {
|
||||
this._pointerEvents = this._events.pointerEvents(this._content.getScrollElement(),
|
||||
this._onStart.bind(this),
|
||||
this._onMove.bind(this),
|
||||
this._onEnd.bind(this));
|
||||
this._pointerEvents = this._events.pointerEvents({
|
||||
element: this._content.getScrollElement(),
|
||||
pointerDown: this._onStart.bind(this),
|
||||
pointerMove: this._onMove.bind(this),
|
||||
pointerUp: this._onEnd.bind(this)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,6 +497,7 @@ export class Refresher {
|
||||
* @private
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this._gesture.destroy();
|
||||
this._setListeners(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Refresher, Content, Config, Ion} from '../../../../src';
|
||||
import { Refresher, Content, Config, GestureController, Ion } from '../../../../src';
|
||||
|
||||
export function run() {
|
||||
|
||||
@@ -218,17 +218,19 @@ describe('Refresher', () => {
|
||||
let refresher: Refresher;
|
||||
let content: Content;
|
||||
let contentElementRef;
|
||||
let gestureController: GestureController;
|
||||
let zone = {
|
||||
run: function(cb) {cb()},
|
||||
runOutsideAngular: function(cb) {cb()}
|
||||
run: function (cb) { cb(); },
|
||||
runOutsideAngular: function (cb) { cb(); }
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
contentElementRef = mockElementRef();
|
||||
content = new Content(contentElementRef, config, null, null, null);
|
||||
gestureController = new GestureController();
|
||||
content = new Content(contentElementRef, config, null, null, zone, null, null);
|
||||
content._scrollEle = document.createElement('scroll-content');
|
||||
|
||||
refresher = new Refresher(content, zone, mockElementRef());
|
||||
refresher = new Refresher(content, zone, gestureController);
|
||||
});
|
||||
|
||||
function touchEv(y: number) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController} from '../../../../../src';
|
||||
import { Component} from '@angular/core';
|
||||
import { ionicBootstrap, NavController } from '../../../../../src';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -8,7 +8,7 @@ import {ionicBootstrap, NavController} from '../../../../../src';
|
||||
class IntroPage {
|
||||
continueText: string = "Skip";
|
||||
startingIndex: number = 1;
|
||||
mySlideOptions;
|
||||
mySlideOptions: any;
|
||||
showSlide: boolean = true;
|
||||
|
||||
constructor(private nav: NavController) {
|
||||
@@ -20,16 +20,16 @@ class IntroPage {
|
||||
};
|
||||
}
|
||||
|
||||
onSlideChanged(slider) {
|
||||
console.log("Slide changed", slider);
|
||||
onSlideChanged(slider: any) {
|
||||
console.log('Slide changed', slider);
|
||||
}
|
||||
|
||||
onSlideChangeStart(slider) {
|
||||
console.log("Slide change start", slider);
|
||||
slider.isEnd ? this.continueText = "Continue" : this.continueText = "Skip";
|
||||
onSlideChangeStart(slider: any) {
|
||||
console.log('Slide change start', slider);
|
||||
slider.isEnd ? this.continueText = 'Continue' : this.continueText = "Skip";
|
||||
}
|
||||
|
||||
onSlideMove(slider) {
|
||||
onSlideMove(slider: any) {
|
||||
console.log("Slide move", slider);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@ class E2EApp {
|
||||
constructor() {
|
||||
this.slides = [
|
||||
{
|
||||
name: "Slide 1",
|
||||
class: "yellow"
|
||||
name: 'Slide 1',
|
||||
class: 'yellow'
|
||||
},
|
||||
{
|
||||
name: "Slide 2",
|
||||
class: "red"
|
||||
name: 'Slide 2',
|
||||
class: 'red'
|
||||
},
|
||||
{
|
||||
name: "Slide 3",
|
||||
class: "blue"
|
||||
name: 'Slide 3',
|
||||
class: 'blue'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ChangeDetectorRef, Component, ComponentResolver, ElementRef, EventEmitter, forwardRef, Input, Inject, NgZone, Output, Renderer, ViewChild, ViewEncapsulation, ViewContainerRef } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ComponentResolver, ElementRef, EventEmitter, forwardRef, Input, Inject, NgZone, Optional, Output, Renderer, ViewChild, ViewEncapsulation, ViewContainerRef } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty} from '../../util/util';
|
||||
import { Keyboard} from '../../util/keyboard';
|
||||
import { MenuController } from '../menu/menu-controller';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
import { NavOptions} from '../nav/nav-interfaces';
|
||||
import { TabButton} from './tab-button';
|
||||
import { Tabs} from './tabs';
|
||||
@@ -128,7 +128,7 @@ import { ViewController} from '../nav/view-controller';
|
||||
template: '<div #viewport></div><div class="nav-decor"></div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Tab extends NavController {
|
||||
export class Tab extends NavControllerBase {
|
||||
private _isInitial: boolean;
|
||||
private _isEnabled: boolean = true;
|
||||
private _isShown: boolean = true;
|
||||
@@ -229,17 +229,13 @@ export class Tab extends NavController {
|
||||
renderer: Renderer,
|
||||
compiler: ComponentResolver,
|
||||
private _cd: ChangeDetectorRef,
|
||||
menuCtrl: MenuController
|
||||
gestureCtrl: GestureController
|
||||
) {
|
||||
// A Tab is a NavController for its child pages
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, compiler, menuCtrl);
|
||||
super(parent, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||
|
||||
parent.add(this);
|
||||
|
||||
if (parent.rootNav) {
|
||||
this._sbEnabled = parent.rootNav.isSwipeBackEnabled();
|
||||
}
|
||||
|
||||
this._tabId = 'tabpanel-' + this.id;
|
||||
this._btnId = 'tab-' + this.id;
|
||||
}
|
||||
@@ -264,7 +260,7 @@ export class Tab extends NavController {
|
||||
*/
|
||||
load(opts: NavOptions, done?: Function) {
|
||||
if (!this._loaded && this.root) {
|
||||
this.push(this.root, this.rootParams, opts).then(() => {
|
||||
this.push(this.root, this.rootParams, opts, () => {
|
||||
done(true);
|
||||
});
|
||||
this._loaded = true;
|
||||
@@ -294,20 +290,7 @@ export class Tab extends NavController {
|
||||
* @private
|
||||
*/
|
||||
loadPage(viewCtrl: ViewController, viewport: ViewContainerRef, opts: NavOptions, done: Function) {
|
||||
let isTabSubPage = (this.parent.subPages && viewCtrl.index > 0);
|
||||
|
||||
if (isTabSubPage) {
|
||||
viewport = this.parent.portal;
|
||||
}
|
||||
|
||||
super.loadPage(viewCtrl, viewport, opts, () => {
|
||||
if (isTabSubPage) {
|
||||
// add the .tab-subpage css class to tabs pages that should act like subpages
|
||||
let pageEleRef = viewCtrl.pageRef();
|
||||
if (pageEleRef) {
|
||||
this._renderer.setElementClass(pageEleRef.nativeElement, 'tab-subpage', true);
|
||||
}
|
||||
}
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ $tabbar-md-item-icon-size: 2.4rem !default;
|
||||
$tabbar-md-item-height: 4.8rem !default;
|
||||
|
||||
$tab-button-md-active-color: $toolbar-md-active-color !default;
|
||||
$tab-button-md-inactive-color: rgba($toolbar-md-inactive-color, .7) !default;
|
||||
$tab-button-md-inactive-opacity: .7 !default;
|
||||
$tab-button-md-inactive-color: rgba($toolbar-md-inactive-color, $tab-button-md-inactive-opacity) !default;
|
||||
|
||||
|
||||
ion-tabbar {
|
||||
@@ -103,7 +104,7 @@ tab-highlight {
|
||||
background-color: $color-base;
|
||||
|
||||
.tab-button {
|
||||
color: $color-contrast;
|
||||
color: rgba($color-contrast, $tab-button-md-inactive-opacity);
|
||||
}
|
||||
|
||||
.tab-button:hover:not(.disable-hover),
|
||||
|
||||
@@ -7,9 +7,11 @@ import { Config } from '../../config/config';
|
||||
import { Content } from '../content/content';
|
||||
import { Icon } from '../icon/icon';
|
||||
import { Ion } from '../ion';
|
||||
import { isBlank, isTrueProperty } from '../../util/util';
|
||||
import { isBlank, isPresent, isTrueProperty } from '../../util/util';
|
||||
import { nativeRaf } from '../../util/dom';
|
||||
import { NavController, DIRECTION_FORWARD } from '../nav/nav-controller';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||
import { NavOptions, DIRECTION_FORWARD } from '../nav/nav-interfaces';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { Tab } from './tab';
|
||||
import { TabButton } from './tab-button';
|
||||
@@ -148,7 +150,6 @@ import { ViewController } from '../nav/view-controller';
|
||||
<tab-highlight></tab-highlight>
|
||||
</ion-tabbar>
|
||||
<ng-content></ng-content>
|
||||
<div #portal tab-portal></div>
|
||||
`,
|
||||
directives: [Badge, Icon, NgClass, NgFor, NgIf, TabButton, TabHighlight],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
@@ -165,18 +166,13 @@ export class Tabs extends Ion {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
id: number;
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
selectHistory: string[] = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
subPages: boolean;
|
||||
|
||||
/**
|
||||
* @input {number} The default selected tab index when first loaded. If a selected index isn't provided then it will use `0`, the first tab.
|
||||
*/
|
||||
@@ -225,12 +221,7 @@ export class Tabs extends Ion {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ViewChild('portal', {read: ViewContainerRef}) portal: ViewContainerRef;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
parent: NavController;
|
||||
parent: NavControllerBase;
|
||||
|
||||
constructor(
|
||||
@Optional() parent: NavController,
|
||||
@@ -243,16 +234,14 @@ export class Tabs extends Ion {
|
||||
) {
|
||||
super(_elementRef);
|
||||
|
||||
this.parent = parent;
|
||||
this.id = ++tabIds;
|
||||
this.parent = <NavControllerBase>parent;
|
||||
this.id = 't' + (++tabIds);
|
||||
this._sbPadding = _config.getBoolean('statusbarPadding');
|
||||
this.subPages = _config.getBoolean('tabsHideOnSubPages');
|
||||
this._useHighlight = _config.getBoolean('tabsHighlight');
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
if (_config.get('tabSubPages') !== null) {
|
||||
console.warn('Config option "tabSubPages" has been deprecated. Please use "tabsHideOnSubPages" instead.');
|
||||
this.subPages = _config.getBoolean('tabSubPages');
|
||||
console.warn('Config option "tabSubPages" has been deprecated. The Material Design spec now supports Bottom Navigation: https://material.google.com/components/bottom-navigation.html');
|
||||
}
|
||||
|
||||
// TODO deprecated 07-07-2016 beta.11
|
||||
@@ -261,9 +250,9 @@ export class Tabs extends Ion {
|
||||
this._useHighlight = _config.getBoolean('tabbarHighlight');
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
if (this.parent) {
|
||||
// this Tabs has a parent Nav
|
||||
parent.registerChildNav(this);
|
||||
this.parent.registerChildNav(this);
|
||||
|
||||
} else if (this._app) {
|
||||
// this is the root navcontroller for the entire app
|
||||
@@ -333,41 +322,32 @@ export class Tabs extends Ion {
|
||||
* @private
|
||||
*/
|
||||
initTabs() {
|
||||
// first check if preloadTab is set as an input @Input, then check the config
|
||||
let preloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
|
||||
// get the selected index from the input
|
||||
// otherwise default it to use the first index
|
||||
let selectedIndex = (isBlank(this.selectedIndex) ? 0 : parseInt(this.selectedIndex, 10));
|
||||
|
||||
// get the selected index
|
||||
let selectedIndex = this.selectedIndex ? parseInt(this.selectedIndex, 10) : 0;
|
||||
|
||||
// ensure the selectedIndex isn't a hidden or disabled tab
|
||||
// also find the first available index incase we need it later
|
||||
let availableIndex = -1;
|
||||
this._tabs.forEach((tab, index) => {
|
||||
if (tab.enabled && tab.show && availableIndex < 0) {
|
||||
// we know this tab index is safe to show
|
||||
availableIndex = index;
|
||||
}
|
||||
|
||||
if (index === selectedIndex && (!tab.enabled || !tab.show)) {
|
||||
// the selectedIndex is not safe to show
|
||||
selectedIndex = -1;
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedIndex < 0) {
|
||||
// the selected index wasn't safe to show
|
||||
// instead use an available index found to be safe to show
|
||||
selectedIndex = availableIndex;
|
||||
// get the selectedIndex and ensure it isn't hidden or disabled
|
||||
let selectedTab = this._tabs.find((t, i) => i === selectedIndex && t.enabled && t.show);
|
||||
if (!selectedTab) {
|
||||
// wasn't able to select the tab they wanted
|
||||
// try to find the first tab that's available
|
||||
selectedTab = this._tabs.find(t => t.enabled && t.show);
|
||||
}
|
||||
|
||||
this._tabs.forEach((tab, index) => {
|
||||
if (index === selectedIndex) {
|
||||
this.select(tab);
|
||||
if (selectedTab) {
|
||||
// we found a tab to select
|
||||
this.select(selectedTab);
|
||||
}
|
||||
|
||||
} else if (preloadTabs) {
|
||||
tab.preload(1000 * index);
|
||||
}
|
||||
});
|
||||
// check if preloadTab is set as an input @Input
|
||||
// otherwise check the preloadTabs config
|
||||
let shouldPreloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
|
||||
if (shouldPreloadTabs) {
|
||||
// preload all the tabs which isn't the selected tab
|
||||
this._tabs.filter((t) => t !== selectedTab).forEach((tab, index) => {
|
||||
tab.preload(this._config.getNumber('tabsPreloadDelay', 1000) * index);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,31 +372,33 @@ export class Tabs extends Ion {
|
||||
/**
|
||||
* @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select.
|
||||
*/
|
||||
select(tabOrIndex: number | Tab) {
|
||||
select(tabOrIndex: number | Tab, opts: NavOptions = {}, done?: Function): Promise<any> {
|
||||
let promise: Promise<any>;
|
||||
if (!done) {
|
||||
promise = new Promise(res => { done = res; });
|
||||
}
|
||||
|
||||
let selectedTab: Tab = (typeof tabOrIndex === 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex);
|
||||
if (isBlank(selectedTab)) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let deselectedTab = this.getSelected();
|
||||
|
||||
if (selectedTab === deselectedTab) {
|
||||
// no change
|
||||
return this._touchActive(selectedTab);
|
||||
this._touchActive(selectedTab);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
console.debug(`Tabs, select: ${selectedTab.id}`);
|
||||
|
||||
let opts = {
|
||||
animate: false
|
||||
};
|
||||
|
||||
let deselectedPage: ViewController;
|
||||
if (deselectedTab) {
|
||||
deselectedPage = deselectedTab.getActive();
|
||||
deselectedPage && deselectedPage.fireWillLeave();
|
||||
}
|
||||
|
||||
opts.animate = false;
|
||||
|
||||
let selectedPage = selectedTab.getActive();
|
||||
selectedPage && selectedPage.fireWillEnter();
|
||||
|
||||
@@ -464,7 +446,11 @@ export class Tabs extends Ion {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,6 +512,13 @@ export class Tabs extends Ion {
|
||||
return this._tabs.indexOf(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
length(): number {
|
||||
return this._tabs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* "Touch" the active tab, going back to the root view of the tab
|
||||
@@ -561,20 +554,6 @@ export class Tabs extends Ion {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Returns the root NavController. Returns `null` if Tabs is not
|
||||
* within a NavController.
|
||||
* @returns {NavController}
|
||||
*/
|
||||
get rootNav(): NavController {
|
||||
let nav = this.parent;
|
||||
while (nav && nav.parent) {
|
||||
nav = nav.parent;
|
||||
}
|
||||
return nav;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* DOM WRITE
|
||||
|
||||
@@ -12,7 +12,8 @@ $tabbar-wp-item-icon-size: 2.4rem !default;
|
||||
$tabbar-wp-item-height: 4.8rem !default;
|
||||
|
||||
$tab-button-wp-active-color: $toolbar-wp-active-color !default;
|
||||
$tab-button-wp-inactive-color: rgba($toolbar-wp-inactive-color, .7) !default;
|
||||
$tab-button-wp-inactive-opacity: .7 !default;
|
||||
$tab-button-wp-inactive-color: rgba($toolbar-wp-inactive-color, $tab-button-wp-inactive-opacity) !default;
|
||||
|
||||
$tab-button-wp-background-activated: rgba(0, 0, 0, .1) !default;
|
||||
|
||||
@@ -96,7 +97,7 @@ ion-tabbar {
|
||||
background-color: $color-base;
|
||||
|
||||
.tab-button {
|
||||
color: $color-contrast;
|
||||
color: rgba($color-contrast, $tab-button-wp-inactive-opacity);
|
||||
}
|
||||
|
||||
.tab-button:hover:not(.disable-hover),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
|
||||
import { ionicBootstrap, NavController, NavParams, ModalController, ViewController, Tabs, Tab } from '../../../../../src';
|
||||
import { App, ionicBootstrap, NavController, NavParams, ModalController, ViewController, Tabs, Tab } from '../../../../../src';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -92,7 +92,7 @@ class TabsPage {
|
||||
class Tab1Page1 {
|
||||
userId: string;
|
||||
|
||||
constructor(private nav: NavController, private tabs: Tabs, private params: NavParams) {
|
||||
constructor(private nav: NavController, private app: App, private tabs: Tabs, private params: NavParams) {
|
||||
this.userId = params.get('userId');
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ class Tab1Page1 {
|
||||
|
||||
goBack() {
|
||||
console.log('go back begin');
|
||||
this.nav.pop().then((val) => {
|
||||
this.nav.pop().then((val: any) => {
|
||||
console.log('go back completed', val);
|
||||
});;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class Tab1Page1 {
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.nav.rootNav.setRoot(SignIn, null, { animate: true, direction: 'back' });
|
||||
this.app.getRootNav().setRoot(SignIn, null, { animate: true, direction: 'back' });
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
@@ -323,10 +323,8 @@ class Tab3Page1 {
|
||||
|
||||
|
||||
@Component({
|
||||
template: '<ion-nav [root]="root" swipeBackEnabled="false"></ion-nav>'
|
||||
template: '<ion-nav swipeBackEnabled="false"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
root = SignIn;
|
||||
}
|
||||
class E2EApp {}
|
||||
|
||||
ionicBootstrap(E2EApp);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController, App, AlertController, ModalController, ViewController, Tab, Tabs} from '../../../../../src';
|
||||
import { Component} from '@angular/core';
|
||||
import { ionicBootstrap, App, AlertController, ModalController, ViewController, Tab, Tabs } from '../../../../../src';
|
||||
|
||||
//
|
||||
// Modal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {ionicBootstrap, NavController, App, Alert, Modal, ViewController, Tab, Tabs} from '../../../../../src';
|
||||
import { Component } from '@angular/core';
|
||||
import { ionicBootstrap, NavController, App, Alert, Modal, ViewController, Tab, Tabs } from '../../../../../src';
|
||||
|
||||
|
||||
//
|
||||
|
||||
@@ -1,10 +1,89 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {App, Nav, Tabs, Tab, NavOptions, Config, ViewController, Platform} from '../../../../src';
|
||||
import { Component } from '@angular/core';
|
||||
import { App, Config, Nav, NavOptions, Platform, Tab, Tabs, ViewController } from '../../../../src';
|
||||
import { mockTab, mockTabs } from '../../../../src/util/mock-providers';
|
||||
|
||||
export function run() {
|
||||
|
||||
describe('Tabs', () => {
|
||||
|
||||
describe('initTabs', () => {
|
||||
|
||||
it('should preload all tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
tab0.preload = () => {};
|
||||
tab1.preload = () => {};
|
||||
|
||||
spyOn(tab0, 'preload');
|
||||
spyOn(tab1, 'preload');
|
||||
|
||||
tabs.preloadTabs = true;
|
||||
|
||||
tabs.initTabs();
|
||||
|
||||
expect(tab0.isSelected).toEqual(true);
|
||||
expect(tab1.isSelected).toEqual(false);
|
||||
|
||||
expect(tab0.preload).not.toHaveBeenCalled();
|
||||
expect(tab1.preload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not select a hidden or disabled tab', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
tab1.enabled = false;
|
||||
tab1.show = false;
|
||||
|
||||
tabs.selectedIndex = '1';
|
||||
tabs.initTabs();
|
||||
|
||||
expect(tab0.isSelected).toEqual(true);
|
||||
expect(tab1.isSelected).toEqual(false);
|
||||
});
|
||||
|
||||
it('should select the second tab from selectedIndex input', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
tabs.selectedIndex = '1';
|
||||
tabs.initTabs();
|
||||
|
||||
expect(tab0.isSelected).toEqual(false);
|
||||
expect(tab1.isSelected).toEqual(true);
|
||||
});
|
||||
|
||||
it('should select the first tab by default', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
spyOn(tab0, 'preload');
|
||||
spyOn(tab1, 'preload');
|
||||
|
||||
tabs.initTabs();
|
||||
|
||||
expect(tab0.isSelected).toEqual(true);
|
||||
expect(tab1.isSelected).toEqual(false);
|
||||
|
||||
expect(tab0.preload).not.toHaveBeenCalled();
|
||||
expect(tab1.preload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('previousTab', () => {
|
||||
|
||||
it('should find the previous tab when there has been 3 selections', () => {
|
||||
@@ -12,9 +91,6 @@ describe('Tabs', () => {
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
var tab2 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
tabs.add(tab2);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
tab2.root = SomePage;
|
||||
@@ -36,8 +112,6 @@ describe('Tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
@@ -56,8 +130,6 @@ describe('Tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
@@ -87,8 +159,6 @@ describe('Tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
@@ -103,12 +173,11 @@ describe('Tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
|
||||
tab0.root = SomePage;
|
||||
tab1.root = SomePage;
|
||||
|
||||
expect(tabs.length()).toEqual(2);
|
||||
expect(tab0.isSelected).toBeUndefined();
|
||||
expect(tab1.isSelected).toBeUndefined();
|
||||
|
||||
@@ -118,16 +187,6 @@ describe('Tabs', () => {
|
||||
expect(tab1.isSelected).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not select an invalid tab index', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
|
||||
expect(tabs.select(22)).toBeUndefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getByIndex', () => {
|
||||
@@ -137,8 +196,6 @@ describe('Tabs', () => {
|
||||
var tab0 = mockTab(tabs);
|
||||
tab0.setRoot(<any>{});
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
|
||||
expect(tabs.getIndex(tab0)).toEqual(0);
|
||||
expect(tabs.getIndex(tab1)).toEqual(1);
|
||||
@@ -152,8 +209,6 @@ describe('Tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
|
||||
tab1.setSelected(true);
|
||||
|
||||
@@ -164,48 +219,15 @@ describe('Tabs', () => {
|
||||
var tabs = mockTabs();
|
||||
var tab0 = mockTab(tabs);
|
||||
var tab1 = mockTab(tabs);
|
||||
tabs.add(tab0);
|
||||
tabs.add(tab1);
|
||||
|
||||
expect(tabs.getSelected()).toEqual(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var app: App;
|
||||
var config: Config;
|
||||
var platform: Platform;
|
||||
var _cd: any;
|
||||
|
||||
function mockNav(): Nav {
|
||||
return new Nav(null, null, null, config, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function mockTabs(): Tabs {
|
||||
return new Tabs(null, null, null, config, null, null, null);
|
||||
}
|
||||
|
||||
function mockTab(parentTabs: Tabs): Tab {
|
||||
var tab = new Tab(parentTabs, app, config, null, null, null, null, null, _cd);
|
||||
tab.load = function(opts: any, cb: Function) {
|
||||
cb();
|
||||
};
|
||||
return tab;
|
||||
}
|
||||
|
||||
@Component({})
|
||||
class SomePage {}
|
||||
|
||||
beforeEach(() => {
|
||||
config = new Config();
|
||||
platform = new Platform();
|
||||
app = new App(config, platform);
|
||||
_cd = {
|
||||
reattach: function(){},
|
||||
detach: function(){}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { NgIf } from '@angular/common';
|
||||
import { Animation } from '../../animations/animation';
|
||||
import { Config } from '../../config/config';
|
||||
import { isPresent } from '../../util/util';
|
||||
import { NavController } from '../nav/nav-controller';
|
||||
import { NavParams } from '../nav/nav-params';
|
||||
import { Transition, TransitionOptions } from '../../transitions/transition';
|
||||
import { ViewController } from '../nav/view-controller';
|
||||
@@ -44,7 +43,6 @@ export class ToastCmp implements AfterViewInit {
|
||||
private id: number;
|
||||
|
||||
constructor(
|
||||
private _nav: NavController,
|
||||
private _viewCtrl: ViewController,
|
||||
private _config: Config,
|
||||
private _elementRef: ElementRef,
|
||||
|
||||
@@ -242,11 +242,12 @@ export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy
|
||||
*/
|
||||
ngAfterContentInit() {
|
||||
this._init = true;
|
||||
this._events.pointerEventsRef(this._elementRef,
|
||||
(ev: any) => this.pointerDown(ev),
|
||||
(ev: any) => this.pointerMove(ev),
|
||||
(ev: any) => this.pointerUp(ev)
|
||||
);
|
||||
this._events.pointerEvents({
|
||||
elementRef: this._elementRef,
|
||||
pointerDown: this.pointerDown.bind(this),
|
||||
pointerMove: this.pointerMove.bind(this),
|
||||
pointerUp: this.pointerUp.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user