mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
feat(aside): reveal/overlay aside using Animation
This commit is contained in:
14
gulpfile.js
14
gulpfile.js
@ -46,7 +46,7 @@ var tscReporter = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var flagConfig = {
|
var flagConfig = {
|
||||||
string: ['port', 'version', 'ngVersion'],
|
string: ['port', 'version', 'ngVersion', 'animations'],
|
||||||
alias: {'p': 'port', 'v': 'version', 'a': 'ngVersion'},
|
alias: {'p': 'port', 'v': 'version', 'a': 'ngVersion'},
|
||||||
default: { port: 8000 }
|
default: { port: 8000 }
|
||||||
};
|
};
|
||||||
@ -172,11 +172,21 @@ gulp.task('bundle.ionic', ['transpile'], function() {
|
|||||||
var insert = require('gulp-insert');
|
var insert = require('gulp-insert');
|
||||||
var concat = require('gulp-concat');
|
var concat = require('gulp-concat');
|
||||||
|
|
||||||
|
var prepend = [];
|
||||||
|
|
||||||
|
// force the web animations api polyfill to kick in
|
||||||
|
if (flags.animations == 'polyfill') {
|
||||||
|
prepend.push('window.Element.prototype.animate=undefined;');
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend correct system paths
|
||||||
|
prepend.push('System.config({ "paths": { "ionic/*": "ionic/*", "rx": "rx" } });');
|
||||||
|
|
||||||
return gulp.src([
|
return gulp.src([
|
||||||
'dist/src/es5/system/ionic/**/*.js'
|
'dist/src/es5/system/ionic/**/*.js'
|
||||||
])
|
])
|
||||||
.pipe(concat('ionic.js'))
|
.pipe(concat('ionic.js'))
|
||||||
.pipe(insert.prepend('System.config({ "paths": { "ionic/*": "ionic/*", "rx": "rx" } });\n'))
|
.pipe(insert.prepend(prepend.join('\n')))
|
||||||
.pipe(gulp.dest('dist/js/'));
|
.pipe(gulp.dest('dist/js/'));
|
||||||
//TODO minify + sourcemaps
|
//TODO minify + sourcemaps
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {CSS} from '../util/dom';
|
import {CSS} from '../util/dom';
|
||||||
|
import {extend} from '../util/util';
|
||||||
|
|
||||||
const RENDER_DELAY = 36;
|
const RENDER_DELAY = 36;
|
||||||
let AnimationRegistry = {};
|
let AnimationRegistry = {};
|
||||||
@ -123,15 +124,15 @@ export class Animation {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return this._rate || (this._parent && this._parent.playbackRate());
|
return (typeof this._rate !== 'undefined' ? this._rate : this._parent && this._parent.playbackRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
fill(value) {
|
reverse() {
|
||||||
if (arguments.length) {
|
return this.playbackRate(-1);
|
||||||
this._fill = value;
|
}
|
||||||
return this;
|
|
||||||
}
|
forward() {
|
||||||
return this._fill || (this._parent && this._parent.fill());
|
return this.playbackRate(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
from(property, value) {
|
from(property, value) {
|
||||||
@ -193,23 +194,22 @@ export class Animation {
|
|||||||
|
|
||||||
play() {
|
play() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const animations = self._ani;
|
|
||||||
const children = self._chld;
|
|
||||||
let promises = [];
|
|
||||||
let i, l;
|
|
||||||
|
|
||||||
// the actual play() method which may or may not start async
|
// the actual play() method which may or may not start async
|
||||||
function beginPlay() {
|
function beginPlay() {
|
||||||
let i, l;
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
for (i = 0, l = children.length; i < l; i++) {
|
for (let i = 0, l = self._chld.length; i < l; i++) {
|
||||||
promises.push( children[i].play() );
|
promises.push( self._chld[i].play() );
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0, l = animations.length; i < l; i++) {
|
self._ani.forEach(animation => {
|
||||||
promises.push( animations[i].play() );
|
promises.push(
|
||||||
}
|
new Promise(resolve => {
|
||||||
|
animation.play(resolve);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
@ -290,8 +290,7 @@ export class Animation {
|
|||||||
this._to,
|
this._to,
|
||||||
this.duration(),
|
this.duration(),
|
||||||
this.easing(),
|
this.easing(),
|
||||||
this.playbackRate(),
|
this.playbackRate() );
|
||||||
this.fill() );
|
|
||||||
|
|
||||||
if (animation.shouldAnimate) {
|
if (animation.shouldAnimate) {
|
||||||
this._ani.push(animation);
|
this._ani.push(animation);
|
||||||
@ -310,6 +309,7 @@ export class Animation {
|
|||||||
// after the RENDER_DELAY
|
// after the RENDER_DELAY
|
||||||
// before the animations have started
|
// before the animations have started
|
||||||
let i;
|
let i;
|
||||||
|
this._isFinished = false;
|
||||||
|
|
||||||
for (i = 0; i < this._chld.length; i++) {
|
for (i = 0; i < this._chld.length; i++) {
|
||||||
this._chld[i]._onPlay();
|
this._chld[i]._onPlay();
|
||||||
@ -322,7 +322,7 @@ export class Animation {
|
|||||||
|
|
||||||
_onFinish() {
|
_onFinish() {
|
||||||
// after the animations have finished
|
// after the animations have finished
|
||||||
if (!this._isFinished) {
|
if (!this._isFinished && !this.isProgress) {
|
||||||
this._isFinished = true;
|
this._isFinished = true;
|
||||||
|
|
||||||
let i, j, ele;
|
let i, j, ele;
|
||||||
@ -367,8 +367,6 @@ export class Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
this._hasFinished = false;
|
|
||||||
|
|
||||||
let i;
|
let i;
|
||||||
for (i = 0; i < this._chld.length; i++) {
|
for (i = 0; i < this._chld.length; i++) {
|
||||||
this._chld[i].pause();
|
this._chld[i].pause();
|
||||||
@ -379,34 +377,96 @@ export class Animation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progressStart() {
|
||||||
|
this.isProgress = true;
|
||||||
|
for (let i = 0; i < this._chld.length; i++) {
|
||||||
|
this._chld[i].progressStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.play();
|
||||||
|
this.pause();
|
||||||
|
}
|
||||||
|
|
||||||
progress(value) {
|
progress(value) {
|
||||||
|
this.isProgress = true;
|
||||||
let i;
|
let i;
|
||||||
|
|
||||||
for (i = 0; i < this._chld.length; i++) {
|
for (i = 0; i < this._chld.length; i++) {
|
||||||
this._chld[i].progress(value);
|
this._chld[i].progress(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._initProgress) {
|
|
||||||
this._initProgress = true;
|
|
||||||
this.play();
|
|
||||||
this.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < this._ani.length; i++) {
|
for (i = 0; i < this._ani.length; i++) {
|
||||||
this._ani[i].progress(value);
|
this._ani[i].progress(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReady(fn) {
|
progressFinish(shouldComplete, rate=1) {
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
this.isProgress = false;
|
||||||
|
for (let i = 0; i < this._chld.length; i++) {
|
||||||
|
promises.push( this._chld[i].progressFinish(shouldComplete) );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ani.forEach(animation => {
|
||||||
|
if (shouldComplete) {
|
||||||
|
animation.playbackRate(rate);
|
||||||
|
} else {
|
||||||
|
animation.playbackRate(rate * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
new Promise(resolve => {
|
||||||
|
animation.play(resolve);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
onReady(fn, clear) {
|
||||||
|
if (clear) {
|
||||||
|
this._readys = [];
|
||||||
|
}
|
||||||
this._readys.push(fn);
|
this._readys.push(fn);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPlay(fn) {
|
onPlay(fn, clear) {
|
||||||
|
if (clear) {
|
||||||
|
this._plays = [];
|
||||||
|
}
|
||||||
this._plays.push(fn);
|
this._plays.push(fn);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
onFinish(fn) {
|
onFinish(fn, clear) {
|
||||||
|
if (clear) {
|
||||||
|
this._finishes = [];
|
||||||
|
}
|
||||||
this._finishes.push(fn);
|
this._finishes.push(fn);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
|
||||||
|
function copy(dest, src) {
|
||||||
|
// undo what stage() may have already done
|
||||||
|
extend(dest, src);
|
||||||
|
|
||||||
|
dest._isFinished = dest._isStaged = dest.isProgress = false;
|
||||||
|
dest._chld = [];
|
||||||
|
dest._ani = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < src._chld.length; i++) {
|
||||||
|
dest.add( copy(new Animation(), src._chld[i]) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy(new Animation(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
@ -444,7 +504,7 @@ export class Animation {
|
|||||||
|
|
||||||
class Animate {
|
class Animate {
|
||||||
|
|
||||||
constructor(ele, fromEffect, toEffect, duration, easingConfig, playbackRate, fill) {
|
constructor(ele, fromEffect, toEffect, duration, easingConfig, playbackRate) {
|
||||||
// https://w3c.github.io/web-animations/
|
// https://w3c.github.io/web-animations/
|
||||||
// not using the direct API methods because they're still in flux
|
// not using the direct API methods because they're still in flux
|
||||||
// however, element.animate() seems locked in and uses the latest
|
// however, element.animate() seems locked in and uses the latest
|
||||||
@ -462,24 +522,21 @@ class Animate {
|
|||||||
return inlineStyle(ele, this.toEffect);
|
return inlineStyle(ele, this.toEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fill = fill;
|
|
||||||
|
|
||||||
this.ele = ele;
|
this.ele = ele;
|
||||||
this.promise = new Promise(res => { this.resolve = res; });
|
|
||||||
|
|
||||||
// stage where the element will start from
|
// stage where the element will start from
|
||||||
fromEffect = parseEffect(fromEffect);
|
this.fromEffect = parseEffect(fromEffect);
|
||||||
inlineStyle(ele, fromEffect);
|
inlineStyle(ele, this.fromEffect);
|
||||||
|
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.rate = playbackRate;
|
this.rate = (typeof playbackRate !== 'undefined' ? playbackRate : 1);
|
||||||
|
|
||||||
this.easing = easingConfig && easingConfig.name || 'linear';
|
this.easing = easingConfig && easingConfig.name || 'linear';
|
||||||
|
|
||||||
this.effects = [ convertProperties(fromEffect) ];
|
this.effects = [ convertProperties(this.fromEffect) ];
|
||||||
|
|
||||||
if (this.easing in EASING_FN) {
|
if (this.easing in EASING_FN) {
|
||||||
insertEffects(this.effects, fromEffect, this.toEffect, easingConfig);
|
insertEffects(this.effects, this.fromEffect, this.toEffect, easingConfig);
|
||||||
|
|
||||||
} else if (this.easing in CUBIC_BEZIERS) {
|
} else if (this.easing in CUBIC_BEZIERS) {
|
||||||
this.easing = 'cubic-bezier(' + CUBIC_BEZIERS[this.easing] + ')';
|
this.easing = 'cubic-bezier(' + CUBIC_BEZIERS[this.easing] + ')';
|
||||||
@ -488,68 +545,72 @@ class Animate {
|
|||||||
this.effects.push( convertProperties(this.toEffect) );
|
this.effects.push( convertProperties(this.toEffect) );
|
||||||
}
|
}
|
||||||
|
|
||||||
play() {
|
play(callback) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
if (self.player) {
|
if (self.ani) {
|
||||||
self.player.play();
|
self.ani.play();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
self.player = self.ele.animate(self.effects, {
|
// https://developers.google.com/web/updates/2014/05/Web-Animations---element-animate-is-now-in-Chrome-36
|
||||||
|
// https://w3c.github.io/web-animations/
|
||||||
|
// Future versions will use "new window.Animation" rather than "element.animate()"
|
||||||
|
|
||||||
|
self.ani = self.ele.animate(self.effects, {
|
||||||
duration: self.duration || 0,
|
duration: self.duration || 0,
|
||||||
easing: self.easing,
|
easing: self.easing,
|
||||||
playbackRate: self.rate || 1,
|
playbackRate: self.rate // old way of setting playbackRate, but still necessary
|
||||||
fill: self.fill
|
|
||||||
});
|
});
|
||||||
|
self.ani.playbackRate = self.rate;
|
||||||
self.player.onfinish = () => {
|
|
||||||
// lock in where the element will stop at
|
|
||||||
// if the playbackRate is negative then it needs to return
|
|
||||||
// to its "from" effects
|
|
||||||
inlineStyle(self.ele, self.rate < 0 ? self.fromEffect : self.toEffect);
|
|
||||||
|
|
||||||
self.player = null;
|
|
||||||
|
|
||||||
self.resolve();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.promise;
|
self.ani.onfinish = () => {
|
||||||
|
// lock in where the element will stop at
|
||||||
|
// if the playbackRate is negative then it needs to return
|
||||||
|
// to its "from" effects
|
||||||
|
inlineStyle(self.ele, self.rate < 0 ? self.fromEffect : self.toEffect);
|
||||||
|
|
||||||
|
self.ani = null;
|
||||||
|
|
||||||
|
callback && callback();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
this.player && this.player.pause();
|
this.ani && this.ani.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
progress(value) {
|
progress(value) {
|
||||||
let player = this.player;
|
let animation = this.ani;
|
||||||
|
|
||||||
if (player) {
|
if (animation) {
|
||||||
// passed a number between 0 and 1
|
// passed a number between 0 and 1
|
||||||
value = Math.max(0, Math.min(1, value));
|
value = Math.max(0, Math.min(1, value));
|
||||||
|
|
||||||
if (value >= 1) {
|
if (animation.playState !== 'paused') {
|
||||||
player.currentTime = (this.duration * 0.999);
|
animation.pause();
|
||||||
return player.play();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.playState !== 'paused') {
|
if (value < 0.999) {
|
||||||
player.pause();
|
animation.currentTime = (this.duration * value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// don't let the progress finish the animation
|
||||||
|
animation.currentTime = (this.duration * 0.999);
|
||||||
}
|
}
|
||||||
|
|
||||||
player.currentTime = (this.duration * value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackRate(value) {
|
playbackRate(value) {
|
||||||
this.rate = value;
|
this.rate = value;
|
||||||
if (this.player) {
|
if (this.ani) {
|
||||||
this.player.playbackRate = value;
|
this.ani.playbackRate = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.ele = this.player = this.effects = this.toEffect = null;
|
this.ele = this.ani = this.effects = this.toEffect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -595,7 +656,7 @@ function parseEffect(inputEffect) {
|
|||||||
|
|
||||||
for (property in inputEffect) {
|
for (property in inputEffect) {
|
||||||
val = inputEffect[property];
|
val = inputEffect[property];
|
||||||
r = val.toString().match(/(\d*\.?\d*)(.*)/);
|
r = val.toString().match(/(^-?\d*\.?\d*)(.*)/);
|
||||||
num = parseFloat(r[1]);
|
num = parseFloat(r[1]);
|
||||||
|
|
||||||
outputEffect[property] = {
|
outputEffect[property] = {
|
||||||
|
@ -3,6 +3,7 @@ export * from 'ionic/components/app/app'
|
|||||||
export * from 'ionic/components/app/id'
|
export * from 'ionic/components/app/id'
|
||||||
export * from 'ionic/components/action-menu/action-menu'
|
export * from 'ionic/components/action-menu/action-menu'
|
||||||
export * from 'ionic/components/aside/aside'
|
export * from 'ionic/components/aside/aside'
|
||||||
|
export * from 'ionic/components/aside/extensions/types'
|
||||||
export * from 'ionic/components/aside/aside-toggle'
|
export * from 'ionic/components/aside/aside-toggle'
|
||||||
export * from 'ionic/components/button/button'
|
export * from 'ionic/components/button/button'
|
||||||
export * from 'ionic/components/card/card'
|
export * from 'ionic/components/card/card'
|
||||||
|
@ -39,7 +39,7 @@ export class IonicApp {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.overlays = [];
|
this.overlays = [];
|
||||||
this._isTransitioning = false;
|
this._transTime = 0;
|
||||||
|
|
||||||
// Our component registry map
|
// Our component registry map
|
||||||
this.components = {};
|
this.components = {};
|
||||||
@ -82,7 +82,7 @@ export class IonicApp {
|
|||||||
* @param {bool} isTransitioning
|
* @param {bool} isTransitioning
|
||||||
*/
|
*/
|
||||||
setTransitioning(isTransitioning) {
|
setTransitioning(isTransitioning) {
|
||||||
this._isTransitioning = !!isTransitioning;
|
this._transTime = (isTransitioning ? Date.now() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +90,7 @@ export class IonicApp {
|
|||||||
* @return {bool}
|
* @return {bool}
|
||||||
*/
|
*/
|
||||||
isTransitioning() {
|
isTransitioning() {
|
||||||
return this._isTransitioning;
|
return (this._transTime + 800 > Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,6 +33,5 @@ export class AsideToggle {
|
|||||||
*/
|
*/
|
||||||
toggle(event) {
|
toggle(event) {
|
||||||
this.aside && this.aside.toggle();
|
this.aside && this.aside.toggle();
|
||||||
console.log('Aside toggle');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,155 +2,56 @@
|
|||||||
// Aside
|
// Aside
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
$aside-width: 304px !default;
|
$aside-width: 304px !default;
|
||||||
$aside-small-width: $aside-width - 40px !default;
|
$aside-small-width: $aside-width - 40px !default;
|
||||||
|
|
||||||
$aside-transition: 0.2s ease transform !default;
|
$aside-background: $background-color !default;
|
||||||
$aside-backdrop-transition: 0.2s ease background-color !default;
|
$aside-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25) !default;
|
||||||
$aside-background: $background-color !default;
|
|
||||||
$aside-shadow: -1px 0px 8px rgba(0, 0, 0, 0.2) !default;
|
|
||||||
|
|
||||||
|
|
||||||
.aside {
|
ion-aside {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
right: auto;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
left: -$aside-width;
|
|
||||||
width: $aside-width;
|
width: $aside-width;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
background: $aside-background;
|
background: $aside-background;
|
||||||
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
transition: $aside-transition;
|
|
||||||
|
|
||||||
&.no-transition {
|
|
||||||
ion-aside-backdrop {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&[type=overlay] {
|
|
||||||
z-index: $z-index-aside-overlay;
|
|
||||||
|
|
||||||
&:not(.open):not(.changing) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&[type=reveal] {
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
&:not(.open):not(.changing) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.open, {
|
|
||||||
&[type=reveal],
|
|
||||||
&[type=push] {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
&[type=overlay] {
|
|
||||||
transform: translate3d($aside-width,0,0);
|
|
||||||
box-shadow: 1px 2px 16px rgba(0, 0, 0, 0.3);
|
|
||||||
|
|
||||||
ion-aside-backdrop {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[side=right] {
|
|
||||||
width: $aside-width;
|
|
||||||
left: 100%;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
transform: translate3d(0,0,0);
|
|
||||||
&.open,
|
|
||||||
&[type=reveal] {
|
|
||||||
transform: translate3d(-$aside-width,0,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[side=top] {
|
|
||||||
height: $aside-width;
|
|
||||||
top: -$aside-width;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
transform: translate3d(0,0,0);
|
|
||||||
&.open,
|
|
||||||
&[type=reveal] {
|
|
||||||
transform: translate3d(0,$aside-width,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[side=bottom] {
|
|
||||||
height: $aside-width;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
transform: translate3d(0,0,0);
|
|
||||||
&.open,
|
|
||||||
&.type-reveal {
|
|
||||||
transform: translate3d(0,-$aside-width,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-aside-backdrop {
|
ion-aside[side=right] {
|
||||||
z-index: $z-index-aside-backdrop;
|
|
||||||
transition: $aside-backdrop-transition;
|
|
||||||
transform: translateX($aside-width);
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: auto;
|
||||||
bottom: 0;
|
}
|
||||||
position: fixed;
|
|
||||||
background-color: rgb(0,0,0);
|
ion-aside backdrop {
|
||||||
|
z-index: -1;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aside-content {
|
.aside-content {
|
||||||
transition: $aside-transition;
|
transform: translate3d(0px, 0px, 0px);
|
||||||
transform: translate3d(0,0,0);
|
}
|
||||||
|
|
||||||
box-shadow: $aside-shadow;
|
.aside-content-open ion-pane,
|
||||||
&.aside-open-left {
|
.aside-content-open ion-content,
|
||||||
transform: translate3d($aside-width,0,0);
|
.aside-content-open .toolbar {
|
||||||
pointer-events: none;
|
// the containing element itself should be clickable but
|
||||||
}
|
// everything inside of it should not clickable when aside is open
|
||||||
|
pointer-events: none;
|
||||||
&.aside-open-right {
|
|
||||||
transform: translate3d(-$aside-width,0,0);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 340px) {
|
@media (max-width: 340px) {
|
||||||
|
|
||||||
.aside {
|
ion-aside {
|
||||||
left: -$aside-small-width;
|
|
||||||
width: $aside-small-width;
|
width: $aside-small-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aside-content {
|
|
||||||
&.aside-open-left {
|
|
||||||
transform: translate3d($aside-small-width, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.aside-open-right {
|
|
||||||
transform: translate3d(-$aside-small-width, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import {forwardRef, Component, Host, View, EventEmitter, ElementRef} from 'angular2/angular2';
|
import {forwardRef, Directive, Host, View, EventEmitter, ElementRef} from 'angular2/angular2';
|
||||||
|
|
||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {IonicApp} from '../app/app';
|
import {IonicApp} from '../app/app';
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {IonicComponent} from '../../config/annotations';
|
import {IonicComponent} from '../../config/annotations';
|
||||||
import * as types from './extensions/types'
|
import * as gestures from './extensions/gestures';
|
||||||
import * as gestures from './extensions/gestures'
|
|
||||||
import * as util from 'ionic/util/util'
|
|
||||||
import {dom} from 'ionic/util'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aside is a side-menu navigation that can be dragged out or toggled to show. Aside supports two
|
* Aside is a side-menu navigation that can be dragged out or toggled to show. Aside supports two
|
||||||
@ -25,152 +23,74 @@ import {dom} from 'ionic/util'
|
|||||||
'side': 'left',
|
'side': 'left',
|
||||||
'type': 'reveal'
|
'type': 'reveal'
|
||||||
},
|
},
|
||||||
delegates: {
|
|
||||||
gesture: [
|
|
||||||
//[instance => instance.side == 'top', gestures.TopAsideGesture],
|
|
||||||
//[instance => instance.side == 'bottom', gestures.BottomAsideGesture],
|
|
||||||
[instance => instance.side == 'right', gestures.RightAsideGesture],
|
|
||||||
[instance => instance.side == 'left', gestures.LeftAsideGesture],
|
|
||||||
],
|
|
||||||
type: [
|
|
||||||
[instance => instance.type == 'overlay', types.AsideTypeOverlay],
|
|
||||||
[instance => instance.type == 'reveal', types.AsideTypeReveal],
|
|
||||||
//[instance => instance.type == 'push', types.AsideTypePush],
|
|
||||||
]
|
|
||||||
},
|
|
||||||
events: ['opening']
|
events: ['opening']
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
template: '<ng-content></ng-content><ion-aside-backdrop></ion-aside-backdrop>',
|
template: '<ng-content></ng-content><backdrop tappable></backdrop>',
|
||||||
directives: [forwardRef(() => AsideBackdrop)]
|
directives: [forwardRef(() => AsideBackdrop)]
|
||||||
})
|
})
|
||||||
export class Aside extends Ion {
|
export class Aside extends Ion {
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {IonicApp} app TODO
|
|
||||||
* @param {ElementRef} elementRef Reference to the element.
|
|
||||||
*/
|
|
||||||
constructor(app: IonicApp, elementRef: ElementRef, config: IonicConfig) {
|
constructor(app: IonicApp, elementRef: ElementRef, config: IonicConfig) {
|
||||||
super(elementRef, config);
|
super(elementRef, config);
|
||||||
|
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|
||||||
this.opening = new EventEmitter('opening');
|
this.opening = new EventEmitter('opening');
|
||||||
|
this.isOpen = false;
|
||||||
//this.animation = new Animation(element.querySelector('backdrop'));
|
this._disableTime = 0;
|
||||||
this.contentClickFn = (e) => {
|
|
||||||
if(!this.isOpen || this.isChanging) { return; }
|
|
||||||
this.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
this.finishChanging = util.debounce(() => {
|
|
||||||
this.setChanging(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Use Animation Class
|
|
||||||
this.getNativeElement().addEventListener('transitionend', ev => {
|
|
||||||
//this.setChanging(false)
|
|
||||||
clearTimeout(this.setChangeTimeout);
|
|
||||||
this.setChangeTimeout = setInterval(this.finishChanging, 400);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
onDestroy() {
|
|
||||||
app.unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
onInit() {
|
onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
this.contentElement = (this.content instanceof Node) ? this.content : this.content.getNativeElement();
|
this.contentElement = (this.content instanceof Node) ? this.content : this.content.getNativeElement();
|
||||||
|
|
||||||
if(!this.id) {
|
if (!this.contentElement) {
|
||||||
|
return console.error('Aside: must have a [content] element to listen for drag events on. Example:\n\n<ion-aside [content]="content"></ion-aside>\n\n<ion-content #content></ion-content>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.id) {
|
||||||
// Auto register
|
// Auto register
|
||||||
this.app.register('menu', this);
|
this.app.register('menu', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.contentElement) {
|
this._initGesture();
|
||||||
this.contentElement.addEventListener('transitionend', ev => {
|
this._initType(this.type);
|
||||||
//this.setChanging(false)
|
|
||||||
clearTimeout(this.setChangeTimeout);
|
|
||||||
this.setChangeTimeout = setInterval(this.finishChanging, 400);
|
|
||||||
})
|
|
||||||
this.contentElement.addEventListener('click', this.contentClickFn);
|
|
||||||
} else {
|
|
||||||
console.error('Aside: must have a [content] element to listen for drag events on. Supply one like this:\n\n<ion-aside [content]="content"></ion-aside>\n\n<ion-content #content>');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.contentElement.classList.add('aside-content');
|
||||||
|
this.contentElement.classList.add('aside-content-' + this.type);
|
||||||
|
|
||||||
this.gestureDelegate = this.getDelegate('gesture');
|
let self = this;
|
||||||
this.typeDelegate = this.getDelegate('type');
|
this.onContentClick = function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
self.close();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy() {
|
_initGesture() {
|
||||||
this.contentElement.removeEventListener('click', this.contentClickFn);
|
switch(this.side) {
|
||||||
}
|
case 'right':
|
||||||
|
this._gesture = new gestures.RightAsideGesture(this);
|
||||||
|
break;
|
||||||
|
|
||||||
/**
|
case 'left':
|
||||||
* TODO
|
this._gesture = new gestures.LeftAsideGesture(this);
|
||||||
* @return {Element} The Aside's content element.
|
break;
|
||||||
*/
|
|
||||||
getContentElement() {
|
|
||||||
return this.contentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} v TODO
|
|
||||||
*/
|
|
||||||
setOpenAmt(v) {
|
|
||||||
this.opening.next(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {boolean} willOpen TODO
|
|
||||||
*/
|
|
||||||
setDoneTransforming(willOpen) {
|
|
||||||
this.typeDelegate.setDoneTransforming(willOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} transform TODO
|
|
||||||
*/
|
|
||||||
setTransform(transform) {
|
|
||||||
this.typeDelegate.setTransform(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {boolean} isSliding TODO
|
|
||||||
*/
|
|
||||||
setSliding(isSliding) {
|
|
||||||
if (isSliding !== this.isSliding) {
|
|
||||||
this.typeDelegate.setSliding(isSliding)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
_initType(type) {
|
||||||
* TODO
|
type = type && type.trim().toLowerCase() || FALLBACK_ASIDE_TYPE;
|
||||||
* @param {boolean} isChanging TODO
|
|
||||||
*/
|
|
||||||
setChanging(isChanging) {
|
|
||||||
|
|
||||||
// Stop any last changing end operations
|
let asideTypeCls = asideTypes[type];
|
||||||
clearTimeout(this.setChangeTimeout);
|
|
||||||
|
|
||||||
if (isChanging !== this.isChanging) {
|
|
||||||
this.isChanging = isChanging
|
|
||||||
this.getNativeElement().classList[isChanging ? 'add' : 'remove']('changing');
|
|
||||||
|
|
||||||
|
if (!asideTypeCls) {
|
||||||
|
type = FALLBACK_ASIDE_TYPE;
|
||||||
|
asideTypeCls = asideTypes[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._type = new asideTypeCls(this);
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,18 +98,79 @@ export class Aside extends Ion {
|
|||||||
* @param {boolean} isOpen If the Aside is open or not.
|
* @param {boolean} isOpen If the Aside is open or not.
|
||||||
* @return {Promise} TODO
|
* @return {Promise} TODO
|
||||||
*/
|
*/
|
||||||
setOpen(isOpen) {
|
setOpen(shouldOpen) {
|
||||||
if (isOpen !== this.isOpen) {
|
// _isDisabled is used to prevent unwanted opening/closing after swiping open/close
|
||||||
this.isOpen = isOpen;
|
// or swiping open the menu while pressing down on the aside-toggle button
|
||||||
this.setChanging(true);
|
if (shouldOpen === this.isOpen || this._isDisabled()) {
|
||||||
|
return Promise.resolve();
|
||||||
// Set full or closed amount
|
|
||||||
this.setOpenAmt(isOpen ? 1 : 0);
|
|
||||||
|
|
||||||
return dom.rafPromise().then(() => {
|
|
||||||
this.typeDelegate.setOpen(isOpen)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._before();
|
||||||
|
|
||||||
|
return this._type.setOpen(shouldOpen).then(() => {
|
||||||
|
this._after(shouldOpen);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgressStart() {
|
||||||
|
// user started swiping the aside open/close
|
||||||
|
if (this._isDisabled()) return;
|
||||||
|
|
||||||
|
this._before();
|
||||||
|
|
||||||
|
this._type.setProgressStart(this.isOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgess(value) {
|
||||||
|
// user actively dragging the menu
|
||||||
|
this._disable();
|
||||||
|
this._type.setProgess(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgressFinish(shouldComplete) {
|
||||||
|
// user has finished dragging the menu
|
||||||
|
this._disable();
|
||||||
|
this._type.setProgressFinish(shouldComplete).then(isOpen => {
|
||||||
|
this._after(isOpen);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_before() {
|
||||||
|
// this places the aside into the correct location before it animates in
|
||||||
|
// this css class doesn't actually kick off any animations
|
||||||
|
this.getNativeElement().classList.add('show-aside');
|
||||||
|
this.getBackdropElement().classList.add('show-backdrop');
|
||||||
|
|
||||||
|
this._disable();
|
||||||
|
this.app.setTransitioning(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_after(isOpen) {
|
||||||
|
this._disable();
|
||||||
|
this.isOpen = isOpen;
|
||||||
|
|
||||||
|
this.contentElement.classList[isOpen ? 'add' : 'remove']('aside-content-open');
|
||||||
|
|
||||||
|
this.contentElement.removeEventListener('click', this.onContentClick);
|
||||||
|
if (isOpen) {
|
||||||
|
this.contentElement.addEventListener('click', this.onContentClick);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.getNativeElement().classList.remove('show-aside');
|
||||||
|
this.getBackdropElement().classList.remove('show-backdrop');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.app.setTransitioning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_disable() {
|
||||||
|
// used to prevent unwanted opening/closing after swiping open/close
|
||||||
|
// or swiping open the menu while pressing down on the aside-toggle
|
||||||
|
this._disableTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDisabled() {
|
||||||
|
return this._disableTime + 300 > Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,55 +197,75 @@ export class Aside extends Ion {
|
|||||||
return this.setOpen(!this.isOpen);
|
return this.setOpen(!this.isOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @return {Element} The Aside element.
|
||||||
|
*/
|
||||||
|
getAsideElement() {
|
||||||
|
return this.getNativeElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @return {Element} The Aside's associated content element.
|
||||||
|
*/
|
||||||
|
getContentElement() {
|
||||||
|
return this.contentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @return {Element} The Aside's associated content element.
|
||||||
|
*/
|
||||||
|
getBackdropElement() {
|
||||||
|
return this.backdrop.elementRef.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
static register(name, cls) {
|
||||||
|
asideTypes[name] = cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
this.app.unregister(this);
|
||||||
|
this._type && this._type.onDestroy();
|
||||||
|
this.contentElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let asideTypes = {};
|
||||||
|
const FALLBACK_ASIDE_TYPE = 'reveal';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Directive({
|
||||||
selector: 'ion-aside-backdrop',
|
selector: 'backdrop',
|
||||||
host: {
|
host: {
|
||||||
'[style.width]': 'width',
|
|
||||||
'[style.height]': 'height',
|
|
||||||
'[style.opacity]': 'opacity',
|
|
||||||
'(click)': 'clicked($event)'
|
'(click)': 'clicked($event)'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
class AsideBackdrop {
|
||||||
template: ''
|
|
||||||
})
|
|
||||||
export class AsideBackdrop extends Ion {
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
* @param {ElementReg} elementRef TODO
|
|
||||||
* @param {IonicConfig} config TODO
|
|
||||||
* @param {Aside} aside TODO
|
* @param {Aside} aside TODO
|
||||||
*/
|
*/
|
||||||
constructor(elementRef: ElementRef, config: IonicConfig, @Host() aside: Aside) {
|
constructor(@Host() aside: Aside, elementRef: ElementRef) {
|
||||||
super(elementRef, config);
|
|
||||||
|
|
||||||
aside.backdrop = this;
|
|
||||||
|
|
||||||
this.aside = aside;
|
this.aside = aside;
|
||||||
|
this.elementRef = elementRef;
|
||||||
this.opacity = 0;
|
aside.backdrop = this;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
onInit() {
|
|
||||||
let ww = window.innerWidth;
|
|
||||||
let wh = window.innerHeight;
|
|
||||||
this.width = ww + 'px';
|
|
||||||
this.height = wh + 'px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
* @param {TODO} event TODO
|
* @param {TODO} event TODO
|
||||||
*/
|
*/
|
||||||
clicked(event) {
|
clicked(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
this.aside.close();
|
this.aside.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,47 +1,37 @@
|
|||||||
import {Aside} from 'ionic/components/aside/aside';
|
import {Aside} from '../aside';
|
||||||
//TODO: figure out way to get rid of all the ../../../../
|
|
||||||
import {SlideEdgeGesture} from 'ionic/gestures/slide-edge-gesture';
|
import {SlideEdgeGesture} from 'ionic/gestures/slide-edge-gesture';
|
||||||
|
|
||||||
class AsideTargetGesture extends SlideEdgeGesture {
|
|
||||||
constructor(aside: Aside) {
|
class AsideGenericGestureHandler extends SlideEdgeGesture {
|
||||||
let asideElement = aside.getNativeElement();
|
constructor(aside: Aside, targetElement, threshold) {
|
||||||
super(asideElement, {
|
super(targetElement, {
|
||||||
direction: (aside.side === 'left' || aside.side === 'right') ? 'x' : 'y',
|
direction: (aside.side === 'left' || aside.side === 'right') ? 'x' : 'y',
|
||||||
edge: aside.side,
|
edge: aside.side,
|
||||||
threshold: 0
|
threshold: threshold
|
||||||
});
|
});
|
||||||
|
|
||||||
this.aside = aside;
|
this.aside = aside;
|
||||||
|
this.listen();
|
||||||
}
|
}
|
||||||
canStart(ev) {
|
|
||||||
return this.aside.isOpen;
|
|
||||||
}
|
|
||||||
// Set CSS, then wait one frame for it to apply before sliding starts
|
// Set CSS, then wait one frame for it to apply before sliding starts
|
||||||
onSlideBeforeStart(slide, ev) {
|
onSlideBeforeStart(slide, ev) {
|
||||||
this.aside.setSliding(true);
|
this.aside.setProgressStart();
|
||||||
this.aside.setChanging(true);
|
|
||||||
return new Promise(resolve => {
|
|
||||||
requestAnimationFrame(resolve);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSlide(slide, ev) {
|
onSlide(slide, ev) {
|
||||||
this.aside.setOpenAmt(slide.distance / slide.max);
|
this.aside.setProgess(slide.distance / slide.max);
|
||||||
this.aside.setTransform(slide.distance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSlideEnd(slide, ev) {
|
onSlideEnd(slide, ev) {
|
||||||
this.aside.setSliding(false);
|
let shouldComplete = (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
|
||||||
if (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5) {
|
this.aside.setProgressFinish(shouldComplete);
|
||||||
|
|
||||||
this.aside.setOpen(!this.aside.isOpen);
|
|
||||||
this.aside.setDoneTransforming(!this.aside.isOpen);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.aside.setDoneTransforming(this.aside.isOpen);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getElementStartPos(slide, ev) {
|
getElementStartPos(slide, ev) {
|
||||||
return this.aside.isOpen ? slide.max : slide.min;
|
return this.aside.isOpen ? slide.max : slide.min;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSlideBoundaries() {
|
getSlideBoundaries() {
|
||||||
return {
|
return {
|
||||||
min: 0,
|
min: 0,
|
||||||
@ -50,64 +40,26 @@ class AsideTargetGesture extends SlideEdgeGesture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AsideGesture extends SlideEdgeGesture {
|
|
||||||
|
export class AsideContentGesture extends AsideGenericGestureHandler {
|
||||||
constructor(aside: Aside) {
|
constructor(aside: Aside) {
|
||||||
// TODO figure out the sliding element, dont just use the parent
|
super(aside, aside.getContentElement(), 75);
|
||||||
let contentElement = aside.getContentElement();
|
|
||||||
super(contentElement, {
|
|
||||||
direction: (aside.side === 'left' || aside.side === 'right') ? 'x' : 'y',
|
|
||||||
edge: aside.side,
|
|
||||||
threshold: 75
|
|
||||||
});
|
|
||||||
this.aside = aside;
|
|
||||||
this.slideElement = contentElement;
|
|
||||||
this.listen();
|
|
||||||
|
|
||||||
let contentGesture = new AsideTargetGesture(aside);
|
|
||||||
contentGesture.listen();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canStart(ev) {
|
canStart(ev) {
|
||||||
// Only restrict edges if the aside is closed
|
|
||||||
return this.aside.isOpen ? true : super.canStart(ev);
|
return this.aside.isOpen ? true : super.canStart(ev);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set CSS, then wait one frame for it to apply before sliding starts
|
export class LeftAsideGesture extends AsideContentGesture {
|
||||||
onSlideBeforeStart(slide, ev) {
|
constructor(aside: Aside) {
|
||||||
this.aside.setSliding(true);
|
super(aside);
|
||||||
this.aside.setChanging(true);
|
|
||||||
return new Promise(resolve => {
|
|
||||||
requestAnimationFrame(resolve);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onSlide(slide, ev) {
|
|
||||||
this.aside.setOpenAmt(slide.distance / slide.max);
|
|
||||||
this.aside.setTransform(slide.distance);
|
|
||||||
}
|
|
||||||
onSlideEnd(slide, ev) {
|
|
||||||
this.aside.setSliding(false);
|
|
||||||
if (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5) {
|
|
||||||
this.aside.setOpen(!this.aside.isOpen);
|
|
||||||
this.aside.setDoneTransforming(!this.aside.isOpen);
|
|
||||||
} else {
|
|
||||||
this.aside.setDoneTransforming(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getElementStartPos(slide, ev) {
|
|
||||||
return this.aside.isOpen ? slide.max : slide.min;
|
|
||||||
}
|
|
||||||
getSlideBoundaries() {
|
|
||||||
return {
|
|
||||||
min: 0,
|
|
||||||
max: this.aside.width()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LeftAsideGesture extends AsideGesture {}
|
|
||||||
|
|
||||||
export class RightAsideGesture extends LeftAsideGesture {
|
export class RightAsideGesture extends LeftAsideGesture {
|
||||||
|
constructor(aside: Aside) {
|
||||||
|
super(aside);
|
||||||
|
}
|
||||||
getElementStartPos(slide, ev) {
|
getElementStartPos(slide, ev) {
|
||||||
return this.aside.isOpen ? slide.min : slide.max;
|
return this.aside.isOpen ? slide.min : slide.max;
|
||||||
}
|
}
|
||||||
|
41
ionic/components/aside/extensions/types.scss
Normal file
41
ionic/components/aside/extensions/types.scss
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
// Aside Reveal
|
||||||
|
// --------------------------------------------------
|
||||||
|
// The content slides over to reveal the aside underneath.
|
||||||
|
// The aside menu itself, which is under the content, does not move.
|
||||||
|
|
||||||
|
ion-aside[type=reveal] {
|
||||||
|
transform: translate3d(-9999px, 0px, 0px);
|
||||||
|
|
||||||
|
&.show-aside {
|
||||||
|
transform: translate3d(0px, 0px, 0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside-content-reveal {
|
||||||
|
box-shadow: $aside-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Aside Overlay
|
||||||
|
// --------------------------------------------------
|
||||||
|
// The aside menu slides over the content. The content
|
||||||
|
// itself, which is under the aside, does not move.
|
||||||
|
|
||||||
|
ion-aside[type=overlay] {
|
||||||
|
z-index: $z-index-aside-overlay;
|
||||||
|
box-shadow: $aside-shadow;
|
||||||
|
transform: translate3d(-9999px, 0px, 0px);
|
||||||
|
|
||||||
|
backdrop {
|
||||||
|
display: block;
|
||||||
|
transform: translate3d(-9999px, 0px, 0px);
|
||||||
|
opacity: 0.01;
|
||||||
|
width: 3000px;
|
||||||
|
|
||||||
|
&.show-backdrop {
|
||||||
|
transform: translate3d(0px, 0px, 0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,124 +1,147 @@
|
|||||||
import {Aside} from 'ionic/components/aside/aside';
|
import {Aside} from '../aside';
|
||||||
import {Animtion} from 'ionic/aside/aside';
|
import {Animation} from 'ionic/animations/animation';
|
||||||
import {CSS} from 'ionic/util/dom'
|
|
||||||
|
|
||||||
// TODO use setters instead of direct dom manipulation
|
|
||||||
const asideManipulator = {
|
|
||||||
setSliding(sliding) {
|
|
||||||
this.aside.getNativeElement().classList[sliding ? 'add' : 'remove']('no-transition');
|
|
||||||
},
|
|
||||||
setOpen(open) {
|
|
||||||
this.aside.getNativeElement().classList[open ? 'add' : 'remove']('open');
|
|
||||||
},
|
|
||||||
setTransform(t) {
|
|
||||||
if(t === null) {
|
|
||||||
this.aside.getNativeElement().style[CSS.transform] = '';
|
|
||||||
} else {
|
|
||||||
this.aside.getNativeElement().style[CSS.transform] = 'translate3d(' + t + 'px,0,0)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const contentManipulator = {
|
|
||||||
setSliding(sliding) {
|
|
||||||
this.aside.contentElement.classList[sliding ? 'add' : 'remove']('no-transition');
|
|
||||||
},
|
|
||||||
setOpen(open) {
|
|
||||||
this.aside.contentElement.classList[open ? 'add' : 'remove'](
|
|
||||||
`aside-open-${this.aside.side}`
|
|
||||||
)
|
|
||||||
},
|
|
||||||
setTransform(t) {
|
|
||||||
if(t === null) {
|
|
||||||
this.aside.contentElement.style[CSS.transform] = '';
|
|
||||||
} else {
|
|
||||||
this.aside.contentElement.style[CSS.transform] = 'translate3d(' + t + 'px,0,0)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const backdropManipulator = {
|
|
||||||
setSliding(sliding) {
|
|
||||||
this.aside.backdrop.isTransitioning = sliding;
|
|
||||||
//.classList[sliding ? 'add' : 'remove']('no-transition');
|
|
||||||
},
|
|
||||||
setOpen(open) {
|
|
||||||
let amt = open ? 0.5 : 0;
|
|
||||||
this.aside.backdrop.opacity = amt;
|
|
||||||
},
|
|
||||||
setTransform(t) {
|
|
||||||
if(t === null) {
|
|
||||||
t = this.aside.width();
|
|
||||||
}
|
|
||||||
let fade = 0.5 * t / this.aside.width();
|
|
||||||
this.aside.backdrop.opacity = fade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aside Type
|
||||||
|
* Base class which is extended by the various types. Each
|
||||||
|
* type will provide their own animations for open and close
|
||||||
|
* and registers itself with Aside.
|
||||||
|
*/
|
||||||
export class AsideType {
|
export class AsideType {
|
||||||
|
|
||||||
constructor(aside: Aside) {
|
constructor(aside: Aside) {
|
||||||
this.aside = aside;
|
this.open = new Animation();
|
||||||
|
this.close = new Animation();
|
||||||
setTimeout(() => {
|
|
||||||
aside.contentElement.classList.add('aside-content')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOpen(shouldOpen) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (shouldOpen) {
|
||||||
|
this.open.playbackRate(1).onFinish(resolve, true).play();
|
||||||
|
} else {
|
||||||
|
this.close.playbackRate(1).onFinish(resolve, true).play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgressStart(isOpen) {
|
||||||
|
this.isOpening = !isOpen;
|
||||||
|
|
||||||
|
this.seek && this.seek.dispose();
|
||||||
|
|
||||||
|
// clone the correct animation depending on open/close
|
||||||
|
if (this.isOpening) {
|
||||||
|
this.seek = this.open.clone();
|
||||||
|
} else {
|
||||||
|
this.seek = this.close.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// the cloned animation should not use an easing curve during seek
|
||||||
|
this.seek.easing('linear').progressStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgess(value) {
|
||||||
|
// adjust progress value depending if it opening or closing
|
||||||
|
if (!this.isOpening) {
|
||||||
|
value = 1 - value;
|
||||||
|
}
|
||||||
|
this.seek.progress(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setProgressFinish(shouldComplete) {
|
||||||
|
let resolve;
|
||||||
|
let promise = new Promise(res => { resolve = res });
|
||||||
|
|
||||||
|
let isOpen = (this.isOpening && shouldComplete);
|
||||||
|
if (!this.isOpening && !shouldComplete) {
|
||||||
|
isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.seek.progressFinish(shouldComplete).then(() => {
|
||||||
|
this.isOpening = false;
|
||||||
|
resolve(isOpen);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestory() {
|
||||||
|
this.open && this.open.dispose();
|
||||||
|
this.close && this.close.dispose();
|
||||||
|
this.seek && this.seek.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AsideTypeOverlay extends AsideType {
|
|
||||||
setSliding(sliding) {
|
|
||||||
asideManipulator.setSliding.call(this, sliding);
|
|
||||||
backdropManipulator.setSliding.call(this, sliding);
|
|
||||||
}
|
|
||||||
setOpen(open) {
|
|
||||||
asideManipulator.setOpen.call(this, open);
|
|
||||||
backdropManipulator.setOpen.call(this, open);
|
|
||||||
}
|
|
||||||
setTransform(t) {
|
|
||||||
asideManipulator.setTransform.call(this, t);
|
|
||||||
backdropManipulator.setTransform.call(this, t);
|
|
||||||
}
|
|
||||||
setDoneTransforming(willOpen) {
|
|
||||||
asideManipulator.setTransform.call(this, null);
|
|
||||||
backdropManipulator.setTransform.call(this, null);
|
|
||||||
asideManipulator.setOpen.call(this, willOpen);
|
|
||||||
backdropManipulator.setOpen.call(this, willOpen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AsideTypePush extends AsideType {
|
/**
|
||||||
setSliding(sliding) {
|
* Aside Reveal Type
|
||||||
asideManipulator.setSliding.call(this, sliding);
|
* The content slides over to reveal the aside underneath.
|
||||||
contentManipulator.setSliding.call(this, sliding);
|
* The aside menu itself, which is under the content, does not move.
|
||||||
}
|
*/
|
||||||
setOpen(open) {
|
class AsideRevealType extends AsideType {
|
||||||
asideManipulator.setOpen.call(this, open);
|
constructor(aside) {
|
||||||
contentManipulator.setOpen.call(this, open);
|
super();
|
||||||
}
|
|
||||||
setTransform(t) {
|
|
||||||
asideManipulator.setTransform.call(this, t);
|
|
||||||
contentManipulator.setTransform.call(this, t);
|
|
||||||
}
|
|
||||||
setDoneTransforming(willOpen) {
|
|
||||||
asideManipulator.setOpen.call(this, willOpen);
|
|
||||||
asideManipulator.setTransform.call(this, null);
|
|
||||||
contentManipulator.setOpen.call(this, willOpen);
|
|
||||||
contentManipulator.setTransform.call(this, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AsideTypeReveal extends AsideType {
|
let easing = 'ease';
|
||||||
setSliding(sliding) {
|
let duration = 250;
|
||||||
contentManipulator.setSliding.call(this, sliding);
|
|
||||||
}
|
let openedX = (aside.width() * (aside.side == 'right' ? -1 : 1)) + 'px';
|
||||||
setOpen(sliding) {
|
|
||||||
asideManipulator.setOpen.call(this, sliding);
|
this.open.easing(easing).duration(duration);
|
||||||
contentManipulator.setOpen.call(this, sliding);
|
this.close.easing(easing).duration(duration);
|
||||||
}
|
|
||||||
setTransform(t) {
|
let contentOpen = new Animation(aside.getContentElement());
|
||||||
contentManipulator.setTransform.call(this, t);
|
contentOpen.fromTo(TRANSLATE_X, CENTER, openedX);
|
||||||
}
|
this.open.add(contentOpen);
|
||||||
setDoneTransforming(willOpen) {
|
|
||||||
contentManipulator.setOpen.call(this, willOpen);
|
let contentClose = new Animation(aside.getContentElement());
|
||||||
contentManipulator.setTransform.call(this, null);
|
contentClose.fromTo(TRANSLATE_X, openedX, CENTER);
|
||||||
|
this.close.add(contentClose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Aside.register('reveal', AsideRevealType);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aside Overlay Type
|
||||||
|
* The aside menu slides over the content. The content
|
||||||
|
* itself, which is under the aside, does not move.
|
||||||
|
*/
|
||||||
|
class AsideOverlayType extends AsideType {
|
||||||
|
constructor(aside) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
let easing = 'ease';
|
||||||
|
let duration = 250;
|
||||||
|
let backdropOpacity = 0.5;
|
||||||
|
|
||||||
|
let closedX = (aside.width() * (aside.side == 'right' ? 1 : -1)) + 'px';
|
||||||
|
|
||||||
|
this.open.easing(easing).duration(duration);
|
||||||
|
this.close.easing(easing).duration(duration);
|
||||||
|
|
||||||
|
let asideOpen = new Animation(aside.getAsideElement());
|
||||||
|
asideOpen.fromTo(TRANSLATE_X, closedX, CENTER);
|
||||||
|
this.open.add(asideOpen);
|
||||||
|
|
||||||
|
let backdropOpen = new Animation(aside.getBackdropElement());
|
||||||
|
backdropOpen.fromTo(OPACITY, 0.01, backdropOpacity);
|
||||||
|
this.open.add(backdropOpen);
|
||||||
|
|
||||||
|
let asideClose = new Animation(aside.getAsideElement());
|
||||||
|
asideClose.fromTo(TRANSLATE_X, CENTER, closedX);
|
||||||
|
this.close.add(asideClose);
|
||||||
|
|
||||||
|
let backdropClose = new Animation(aside.getBackdropElement());
|
||||||
|
backdropClose.fromTo(OPACITY, backdropOpacity, 0.01);
|
||||||
|
this.close.add(backdropClose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Aside.register('overlay', AsideOverlayType);
|
||||||
|
|
||||||
|
|
||||||
|
const OPACITY = 'opacity';
|
||||||
|
const TRANSLATE_X = 'translateX';
|
||||||
|
const CENTER = '0px';
|
||||||
|
@ -20,6 +20,6 @@
|
|||||||
<button id="e2eContentToggleAside" aside-toggle>Toggle Aside</button>
|
<button id="e2eContentToggleAside" aside-toggle>Toggle Aside</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<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>
|
</ion-content>
|
||||||
|
27
ionic/components/aside/test/overlay/index.ts
Normal file
27
ionic/components/aside/test/overlay/index.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import {App, IonicApp, IonicView} from 'ionic/ionic';
|
||||||
|
|
||||||
|
|
||||||
|
@IonicView({templateUrl: 'page1.html'})
|
||||||
|
class Page1 {}
|
||||||
|
|
||||||
|
|
||||||
|
@App({
|
||||||
|
templateUrl: 'main.html'
|
||||||
|
})
|
||||||
|
class E2EApp {
|
||||||
|
|
||||||
|
constructor(app: IonicApp) {
|
||||||
|
this.app = app;
|
||||||
|
this.rootView = Page1;
|
||||||
|
}
|
||||||
|
|
||||||
|
openPage(aside, page) {
|
||||||
|
// close the menu when clicking a link from the aside
|
||||||
|
aside.close();
|
||||||
|
|
||||||
|
// Reset the content nav to have just this page
|
||||||
|
// we wouldn't want the back button to show in this scenario
|
||||||
|
let nav = this.app.getComponent('nav');
|
||||||
|
nav.setRoot(page.component);
|
||||||
|
}
|
||||||
|
}
|
41
ionic/components/aside/test/overlay/main.html
Normal file
41
ionic/components/aside/test/overlay/main.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<ion-aside [content]="content" id="leftMenu" type="overlay" side="left">
|
||||||
|
|
||||||
|
<ion-toolbar secondary>
|
||||||
|
<ion-title>Left Menu</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
|
||||||
|
<ion-list>
|
||||||
|
|
||||||
|
<button ion-item aside-toggle="leftMenu">
|
||||||
|
Close Left Menu
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</ion-list>
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
</ion-aside>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <ion-aside [content]="content" id="rightMenu" type="reveal" side="right">
|
||||||
|
|
||||||
|
<ion-toolbar secondary>
|
||||||
|
<ion-title>Right Menu</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
|
||||||
|
<ion-list>
|
||||||
|
|
||||||
|
<button ion-item aside-toggle="rightMenu">
|
||||||
|
Close Right Menu
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</ion-list>
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
</ion-aside> -->
|
||||||
|
|
||||||
|
|
||||||
|
<ion-nav id="nav" [root]="rootView" #content swipe-back-enabled="false"></ion-nav>
|
29
ionic/components/aside/test/overlay/page1.html
Normal file
29
ionic/components/aside/test/overlay/page1.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
<ion-navbar *navbar>
|
||||||
|
|
||||||
|
<button aside-toggle="leftMenu">
|
||||||
|
<icon menu></icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ion-title>
|
||||||
|
Overlay Aside
|
||||||
|
</ion-title>
|
||||||
|
|
||||||
|
</ion-navbar>
|
||||||
|
|
||||||
|
|
||||||
|
<ion-content #content padding>
|
||||||
|
|
||||||
|
<h3>Content</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button aside-toggle="leftMenu">Toggle Left Aside</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button aside-toggle="rightMenu">Toggle Right Aside</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
|
||||||
|
|
||||||
|
</ion-content>
|
27
ionic/components/aside/test/reveal/index.ts
Normal file
27
ionic/components/aside/test/reveal/index.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import {App, IonicApp, IonicView} from 'ionic/ionic';
|
||||||
|
|
||||||
|
|
||||||
|
@IonicView({templateUrl: 'page1.html'})
|
||||||
|
class Page1 {}
|
||||||
|
|
||||||
|
|
||||||
|
@App({
|
||||||
|
templateUrl: 'main.html'
|
||||||
|
})
|
||||||
|
class E2EApp {
|
||||||
|
|
||||||
|
constructor(app: IonicApp) {
|
||||||
|
this.app = app;
|
||||||
|
this.rootView = Page1;
|
||||||
|
}
|
||||||
|
|
||||||
|
openPage(aside, page) {
|
||||||
|
// close the menu when clicking a link from the aside
|
||||||
|
aside.close();
|
||||||
|
|
||||||
|
// Reset the content nav to have just this page
|
||||||
|
// we wouldn't want the back button to show in this scenario
|
||||||
|
let nav = this.app.getComponent('nav');
|
||||||
|
nav.setRoot(page.component);
|
||||||
|
}
|
||||||
|
}
|
41
ionic/components/aside/test/reveal/main.html
Normal file
41
ionic/components/aside/test/reveal/main.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<ion-aside [content]="content" id="leftMenu" type="reveal" side="left">
|
||||||
|
|
||||||
|
<ion-toolbar secondary>
|
||||||
|
<ion-title>Left Menu</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
|
||||||
|
<ion-list>
|
||||||
|
|
||||||
|
<button ion-item aside-toggle="leftMenu">
|
||||||
|
Close Left Menu
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</ion-list>
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
</ion-aside>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <ion-aside [content]="content" id="rightMenu" type="reveal" side="right">
|
||||||
|
|
||||||
|
<ion-toolbar secondary>
|
||||||
|
<ion-title>Right Menu</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
|
||||||
|
<ion-list>
|
||||||
|
|
||||||
|
<button ion-item aside-toggle="rightMenu">
|
||||||
|
Close Right Menu
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</ion-list>
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
</ion-aside> -->
|
||||||
|
|
||||||
|
|
||||||
|
<ion-nav id="nav" [root]="rootView" #content swipe-back-enabled="false"></ion-nav>
|
29
ionic/components/aside/test/reveal/page1.html
Normal file
29
ionic/components/aside/test/reveal/page1.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
<ion-navbar *navbar>
|
||||||
|
|
||||||
|
<button aside-toggle="leftMenu">
|
||||||
|
<icon menu></icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ion-title>
|
||||||
|
Reveal Aside
|
||||||
|
</ion-title>
|
||||||
|
|
||||||
|
</ion-navbar>
|
||||||
|
|
||||||
|
|
||||||
|
<ion-content #content padding>
|
||||||
|
|
||||||
|
<h3>Content</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button aside-toggle="leftMenu">Toggle Left Aside</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button aside-toggle="rightMenu">Toggle Right Aside</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
|
||||||
|
|
||||||
|
</ion-content>
|
@ -1,4 +1,5 @@
|
|||||||
import {IonicConfig} from '../config/config';
|
import {IonicConfig} from '../config/config';
|
||||||
|
import {Platform} from '../platform/platform';
|
||||||
import * as util from 'ionic/util';
|
import * as util from 'ionic/util';
|
||||||
|
|
||||||
|
|
||||||
@ -76,11 +77,11 @@ export class Ion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
width() {
|
width() {
|
||||||
return this.getNativeElement().offsetWidth;
|
return Platform.getDimensions(this).w;
|
||||||
}
|
}
|
||||||
|
|
||||||
height() {
|
height() {
|
||||||
return this.getNativeElement().offsetHeight;
|
return Platform.getDimensions(this).h;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ export class SwipeHandle {
|
|||||||
self.onDragHorizontal(ev);
|
self.onDragHorizontal(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
gesture.on('panend', gestureEv => { self.onDragEnd(gestureEv.gesture); });
|
gesture.on('panend', gesture => { self.onDragEnd(gesture); });
|
||||||
gesture.on('panleft', dragHorizontal);
|
gesture.on('panleft', dragHorizontal);
|
||||||
gesture.on('panright', dragHorizontal);
|
gesture.on('panright', dragHorizontal);
|
||||||
});
|
});
|
||||||
@ -85,16 +85,14 @@ export class SwipeHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.viewCtrl.swipeBackEnd(completeSwipeBack, progress, playbackRate);
|
this.viewCtrl.swipeBackFinish(completeSwipeBack, playbackRate);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.startX = null;
|
this.startX = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragHorizontal(gestureEv) {
|
onDragHorizontal(gesture) {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
let gesture = gestureEv.gesture;
|
|
||||||
|
|
||||||
if (this.startX === null) {
|
if (this.startX === null) {
|
||||||
// starting drag
|
// starting drag
|
||||||
gesture.srcEvent.preventDefault();
|
gesture.srcEvent.preventDefault();
|
||||||
|
@ -338,6 +338,8 @@ export class ViewController extends Ion {
|
|||||||
enteringItem.shouldCache = false;
|
enteringItem.shouldCache = false;
|
||||||
enteringItem.willEnter();
|
enteringItem.willEnter();
|
||||||
|
|
||||||
|
this.app.setTransitioning(true);
|
||||||
|
|
||||||
// wait for the new item to complete setup
|
// wait for the new item to complete setup
|
||||||
enteringItem.stage(() => {
|
enteringItem.stage(() => {
|
||||||
|
|
||||||
@ -348,8 +350,7 @@ export class ViewController extends Ion {
|
|||||||
|
|
||||||
// init the transition animation
|
// init the transition animation
|
||||||
this.sbTransition = Transition.create(this, opts);
|
this.sbTransition = Transition.create(this, opts);
|
||||||
this.sbTransition.easing('linear');
|
this.sbTransition.easing('linear').progressStart();
|
||||||
this.sbTransition.stage();
|
|
||||||
|
|
||||||
let swipeBackPromise = new Promise(res => { this.sbResolve = res; });
|
let swipeBackPromise = new Promise(res => { this.sbResolve = res; });
|
||||||
|
|
||||||
@ -408,27 +409,16 @@ export class ViewController extends Ion {
|
|||||||
* @param {TODO} progress TODO
|
* @param {TODO} progress TODO
|
||||||
* @param {TODO} playbackRate TODO
|
* @param {TODO} playbackRate TODO
|
||||||
*/
|
*/
|
||||||
swipeBackEnd(completeSwipeBack, progress, playbackRate) {
|
swipeBackFinish(completeSwipeBack, playbackRate) {
|
||||||
// to reverse the animation use a negative playbackRate
|
// to reverse the animation use a negative playbackRate
|
||||||
if (this.sbTransition && this.sbActive) {
|
if (this.sbTransition && this.sbActive) {
|
||||||
this.sbActive = false;
|
this.sbActive = false;
|
||||||
|
|
||||||
if (progress <= 0) {
|
this.sbTransition.progressFinish(completeSwipeBack, playbackRate).then(() => {
|
||||||
this.swipeBackProgress(0.0001);
|
|
||||||
} else if (progress >= 1) {
|
|
||||||
this.swipeBackProgress(0.9999);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!completeSwipeBack) {
|
|
||||||
playbackRate = playbackRate * -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sbTransition.playbackRate(playbackRate);
|
|
||||||
|
|
||||||
this.sbTransition.play().then(() => {
|
|
||||||
this.sbResolve && this.sbResolve(completeSwipeBack);
|
this.sbResolve && this.sbResolve(completeSwipeBack);
|
||||||
this.sbTransition && this.sbTransition.dispose();
|
this.sbTransition && this.sbTransition.dispose();
|
||||||
this.sbResolve = this.sbTransition = null;
|
this.sbResolve = this.sbTransition = null;
|
||||||
|
this.app.setTransitioning(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"components/toolbar/toolbar",
|
"components/toolbar/toolbar",
|
||||||
"components/action-menu/action-menu",
|
"components/action-menu/action-menu",
|
||||||
"components/aside/aside",
|
"components/aside/aside",
|
||||||
|
"components/aside/extensions/types",
|
||||||
"components/badge/badge",
|
"components/badge/badge",
|
||||||
"components/button/button",
|
"components/button/button",
|
||||||
"components/button/button-clear",
|
"components/button/button-clear",
|
||||||
|
@ -14,6 +14,8 @@ export class PlatformCtrl {
|
|||||||
this._registry = {};
|
this._registry = {};
|
||||||
this._default = null;
|
this._default = null;
|
||||||
this._onResizes = [];
|
this._onResizes = [];
|
||||||
|
this._dimensions = {};
|
||||||
|
this._dimIds = 0;
|
||||||
|
|
||||||
this._readyPromise = new Promise(res => { this._readyResolve = res; } );
|
this._readyPromise = new Promise(res => { this._readyResolve = res; } );
|
||||||
}
|
}
|
||||||
@ -173,11 +175,11 @@ export class PlatformCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
winResize() {
|
winResize() {
|
||||||
Platform._w = Platform._h = 0;
|
|
||||||
|
|
||||||
clearTimeout(Platform._resizeTimer);
|
clearTimeout(Platform._resizeTimer);
|
||||||
|
|
||||||
Platform._resizeTimer = setTimeout(() => {
|
Platform._resizeTimer = setTimeout(() => {
|
||||||
|
Platform.flushDimensions();
|
||||||
|
|
||||||
for (let i = 0; i < Platform._onResizes.length; i++) {
|
for (let i = 0; i < Platform._onResizes.length; i++) {
|
||||||
try {
|
try {
|
||||||
Platform._onResizes[i]();
|
Platform._onResizes[i]();
|
||||||
@ -193,6 +195,35 @@ export class PlatformCtrl {
|
|||||||
this._onResizes.push(cb);
|
this._onResizes.push(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the element offsetWidth and offsetHeight. Values are cached to
|
||||||
|
* reduce DOM reads, and reset on a window resize.
|
||||||
|
* @param {TODO} platformConfig TODO
|
||||||
|
*/
|
||||||
|
getDimensions(component) {
|
||||||
|
// cache
|
||||||
|
if (!component._dimId) {
|
||||||
|
component._dimId = ++this._dimIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dimensions = this._dimensions[component._dimId];
|
||||||
|
if (!dimensions) {
|
||||||
|
let ele = component.getNativeElement();
|
||||||
|
|
||||||
|
dimensions = this._dimensions[component._dimId] = {
|
||||||
|
w: ele.offsetWidth,
|
||||||
|
h: ele.offsetHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
flushDimensions() {
|
||||||
|
this._dimensions = {};
|
||||||
|
this._w = this._h = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Registry
|
// Registry
|
||||||
// **********************************************
|
// **********************************************
|
||||||
@ -237,8 +268,8 @@ export class PlatformCtrl {
|
|||||||
* @returns {boolean} TODO
|
* @returns {boolean} TODO
|
||||||
*/
|
*/
|
||||||
testUserAgent(userAgentExpression) {
|
testUserAgent(userAgentExpression) {
|
||||||
let rx = new RegExp(userAgentExpression, 'i');
|
let rgx = new RegExp(userAgentExpression, 'i');
|
||||||
return rx.test(this._ua);
|
return rgx.test(this._ua);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +43,7 @@ export class Activator {
|
|||||||
|
|
||||||
bindDom('touchcancel', function(ev) {
|
bindDom('touchcancel', function(ev) {
|
||||||
self.isTouch = true;
|
self.isTouch = true;
|
||||||
self.touchCancel(ev);
|
self.pointerCancel(ev);
|
||||||
});
|
});
|
||||||
|
|
||||||
bindDom('mousedown', function(ev) {
|
bindDom('mousedown', function(ev) {
|
||||||
|
@ -127,9 +127,13 @@
|
|||||||
if ((property == 'direction') && (directions.indexOf(timingInput[property]) == -1)) {
|
if ((property == 'direction') && (directions.indexOf(timingInput[property]) == -1)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (property == 'playbackRate' && timingInput[property] !== 1 && shared.isDeprecated('AnimationEffectTiming.playbackRate', '2014-11-28', 'Use Animation.playbackRate instead.')) {
|
|
||||||
return;
|
// IONIC HACK
|
||||||
}
|
// NATIVE CHROME STILL USES THIS, SO DON'T HAVE THE POLYFILL THROW ERRORS
|
||||||
|
// if (property == 'playbackRate' && timingInput[property] !== 1 && shared.isDeprecated('AnimationEffectTiming.playbackRate', '2014-11-28', 'Use Animation.playbackRate instead.')) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
timing[property] = timingInput[property];
|
timing[property] = timingInput[property];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user