This commit is contained in:
Andrew
2015-03-19 12:31:30 -05:00
parent ecd1ca168a
commit d207b8b549
14 changed files with 417 additions and 163 deletions

View File

@@ -1,6 +1,17 @@
<ion-tabbar view-title="My Tabs"></ion-tabbar>
<ion-tabbar view-title="Tabs 2"></ion-tabbar>
<ion-tabbar view-title="Tabs 4"></ion-tabbar>
<ion-side-menu-parent>
<ion-side-menu side="left">
Hello ...
<p>...</p>
<p>...</p>
<p>...</p>
<p>...</p>
<p>...</p>
Side menu!
</ion-side-menu>
<div class="content">
<ion-tabbar view-title="My Tabs"></ion-tabbar>
<ion-tabbar view-title="Tabs 2"></ion-tabbar>
<ion-tabbar view-title="Tabs 4"></ion-tabbar>
<button (click)="showModal()">Show Modal</button>
@@ -11,3 +22,11 @@
<h2>I'm a modal!</h2>
</ion-modal>
-->
<button (click)="showModal()">Show Modal</button>
<!--
<ion-modal>
<h2>I'm a modal!</h2>
</ion-modal>
-->
</div>
</ion-side-menu-content>

View File

@@ -1,16 +1,16 @@
import {bootstrap} from 'angular2/core';
import {Component, Template} from 'angular2/angular2';
import {Tabbar} from 'ionic/components/tabbar/tabbar';
import {Modal} from 'ionic/components/modal/modal';
import {SideMenu, SideMenuParent} from 'ionic/components/sidemenu/sidemenu';
import {Switch} from 'ionic/components/switch/switch';
import {SideMenu, SideMenuParent} from 'ionic/components';
import 'ionic/components/tabbar/mixins/android/android-tabbar';
// import 'ionic/components/tabbar/mixins/android/android-tabbar';
@Component({ selector: '[playground-main]' })
@Template({
url: 'main.html',
directives: [Tabbar, Modal, SideMenu, SideMenuParent, Switch]
directives: [SideMenu, SideMenuParent]
})
class PlaygroundMain {
constructor() {

3
src/components.js Normal file
View File

@@ -0,0 +1,3 @@
export * from './components/sidemenu/sidemenu';
import './components/sidemenu/behaviors/direction/direction';

View File

@@ -1,31 +1,12 @@
import * as Platform from '../platform';
import * as util from '../util';
var ILLEGAL_ASSIGN_FIELDS = {};
export class Ion {
constructor() {
var platformName = Platform.getPlatform();
var platformConfig = this.$config._platforms[platformName];
if (platformConfig) {
for (var i = 0, ii = platformConfig._mixins.length; i < ii; i++) {
platformConfig._mixins[i](this);
}
}
}
extend() {
for (var i = 0, ii = arguments.length; i < ii; i++) {
var obj = arguments[i];
if (obj) {
var keys = Object.keys(obj);
for (var j = 0, jj = keys.length; j < jj; j++) {
var key = keys[j];
if (!ILLEGAL_ASSIGN_FIELDS[key]) {
this[key] = obj[key];
}
}
}
}
extend(...args) {
args.unshift(this);
return util.extend.apply(null, args);
}
}

View File

@@ -0,0 +1,104 @@
import {sideMenuConfig} from '../../sidemenu';
import * as util from '../../../../util';
sideMenuConfig.when(instance => instance.side === 'bottom')
.mixin(function() {
this.gesture.options({
direction: Hammer.DIRECTION_VERTICAL
});
this.domElement.classList.add('bottom');
util.extend(this.dragMethods, {
getMenuStart: (drag, ev) => {
return this.isOpen ? -drag.height : 0;
},
onDrag: (drag, ev) => {
drag.pos = util.clamp(
0, -drag.menuStart + drag.pointerStart - ev.center.y, drag.height
);
this.domElement.style.transform = 'translate3d(0,' +
(drag.height - drag.pos) + 'px,0)';
},
onEnd: (drag, ev) => {
this.setOpen(drag.pos > drag.height / 2);
this.domElement.style.transform = '';
},
getEventPos: ev => {
return ev.center.y;
}
});
});
sideMenuConfig.when(instance => instance.side === 'left')
.mixin(function() {
this.domElement.classList.add('left');
util.extend(this.dragMethods, {
canStart: (ev) => {
return this.isOpen || ev.center.x < this.dragThreshold;
},
getMenuStart: (drag, ev) => {
return this.isOpen ? drag.width : 0;
},
onDrag: (drag, ev) => {
drag.pos = util.clamp(
0, drag.menuStart + ev.center.x - drag.pointerStart, drag.width
);
this.domElement.style.transform = 'translate3d(' + (-drag.width + drag.pos) + 'px, 0, 0)';
},
onEnd: (drag, ev) => {
this.setOpen(drag.pos > drag.width / 2);
this.domElement.style.transform = '';
}
});
});
sideMenuConfig.when(instance => instance.side === 'right')
.mixin(function() {
this.domElement.classList.add('right');
util.extend(this.dragMethods, {
getMenuStart: (drag, ev) => {
return this.isOpen ? -drag.width : 0;
},
onDrag: (drag, ev) => {
drag.pos = util.clamp(
0, -drag.menuStart + drag.pointerStart - ev.center.x, drag.height
);
this.domElement.style.transform = 'translate3d(' +
(drag.width - drag.pos) + 'px,0,0)';
},
onEnd: (drag, ev) => {
this.setOpen(drag.pos > drag.width / 2);
this.domElement.style.transform = '';
}
});
});
sideMenuConfig.when(instance => instance.side === 'top')
.mixin(function() {
this.domElement.classList.add('top');
util.extend(this.dragMethods, {
getMenuStart: (drag, ev) => {
return this.isOpen ? drag.height : 0;
},
onDrag: (drag, ev) => {
drag.pos = util.clamp(
0, drag.menuStart + ev.center.y - drag.pointerStart, drag.height
);
this.domElement.style.transform = 'translate3d(0, ' +
(-drag.height + drag.pos) + 'px, 0)';
},
onEnd: (drag, ev) => {
this.setOpen(drag.pos > drag.height / 2);
this.domElement.style.transform = '';
},
getEventPos: (ev) => {
return ev.center.y;
}
});
});

View File

View File

@@ -1,53 +1,99 @@
import {Component, Template, Inject, Parent, NgElement} from 'angular2/angular2';
import {Ion} from '../ion';
// import {EdgeDragGesture} from '../../core/gestures/edge-drag-gesture';
import {IonConfig} from '../../config';
import {DragGesture} from '../../core/gestures/drag-gesture';
import * as util from '../../util';
export var sideMenuConfig = new IonConfig();
sideMenuConfig.defaults({
side: 'left',
dragThreshold: '50'
});
@Component({
selector: 'ion-side-menu',
bind: {
side: 'side'
}
selector: 'ion-side-menu'
// bind: {
// side: 'side',
// dragThreshold: 'dragThreshold'
// },
})
@Template({
inline: `<content></content>`
})
export class SideMenu extends Ion {
constructor(
@Parent() sideMenuParent: SideMenuParent,
@Parent() sideMenuParent: SideMenuParent,
@NgElement() element: NgElement
) {
this.el = element;
// this.gesture = new EdgeDragGesture(sideMenuParent.el.domElement, this);
debugger;
this.domElement = element.domElement;
this._drag = {};
super();
this.gesture = new DragGesture(sideMenuParent.domElement, {
onDrag: this.onDrag.bind(this),
onDragStart: this.onDragStart.bind(this),
onDragEnd: this.onDragEnd.bind(this)
});
this.dragMethods = {
getMenuStart() { return 0; },
getEventPos(ev) { return ev.center.x; }
};
this.gesture.listen();
this.domElement.addEventListener('transitionend', ev => {
this.setChanging(false);
})
sideMenuConfig(this);
}
onDragStart(ev) {
this._drag = {
width: this.el.domElement.offsetWidth
};
this.el.domElement.classList.add('no-animate');
if (!this.dragMethods.canStart(ev)) {
return false;
}
this.setChanging(true);
this.domElement.classList.add('dragging');
requestAnimationFrame(() => {
this._drag = {
containerWidth: window.innerWidth,
containerHeight: window.innerHeight,
width: this.domElement.offsetWidth,
height: this.domElement.offsetHeight,
pointerStart: this.dragMethods.getEventPos(ev)
};
this._drag.menuStart = this.dragMethods.getMenuStart(this._drag, ev);
this._drag.started = true;
});
}
onDrag(ev) {
var pos = this._drag.pos = Math.max(0, Math.min(ev.center.x, this._drag.width));
this.el.domElement.style.transform = 'translate3d(0,' + pos + 'px,0)';
console.log('ondrag');
if (!this._drag) return;
this.dragMethods.onDrag(this._drag, ev);
}
onDragEnd(ev) {
if (!this._drag) return;
var { pos, width } = this._drag;
this.el.domElement.style.transform = '';
if (pos < width / 2) {
this.close();
} else if (pos > width / 2) {
this.open();
}
this.el.domElement.style.transform = '';
this.el.domElement.classList.remove('no-animate');
this.domElement.classList.remove('dragging');
this.dragMethods.onEnd(this._drag, ev);
this._drag = null;
}
open() {
this.el.domElement.classList.add('open');
setChanging(isChanging) {
if (isChanging !== this.isChanging) {
this.isChanging = isChanging;
this.domElement.classList[isChanging ? 'add' : 'remove']('changing');
}
}
close() {
this.el.domElement.classList.remove('open');
setOpen(isOpen) {
if (isOpen !== this.isOpen) {
this.isOpen = isOpen;
this.setChanging(true);
requestAnimationFrame(() => {
this.domElement.classList[isOpen ? 'add' : 'remove']('open');
})
}
}
}
@@ -57,9 +103,9 @@ export class SideMenu extends Ion {
@Template({
inline: '<content></content>'
})
export class SideMenuParent extends Ion {
export class SideMenuParent {
constructor(@NgElement() element: NgElement) {
this.el = element;
this.domElement = element.domElement;
super();
}
}

View File

@@ -1,20 +1,74 @@
$side-menu-width: 304px;
$side-menu-height: 304px;
ion-side-menu {
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 304px;
background: rgba(255,0,0,0.5);
border-right: 1px solid black;
transition: transform 0.3s;
transform: translate3d(0, -304px, 0);
&.open {
transform: translate3d(0,0,0);
&:not(.open):not(.changing) {
display: none;
}
&.no-animate {
&.dragging {
transition-duration: 0s;
}
&.left {
width: $side-menu-width;
left: 0;
top: 0;
bottom: 0;
transform: translate3d(-$side-menu-width, 0, 0);
&.open {
transform: translate3d(0,0,0);
}
}
&.right {
width: $side-menu-width;
right: 0;
top: 0;
bottom: 0;
transform: translate3d($side-menu-width,0,0);
&.open {
transform: translate3d(0,0,0);
}
}
&.top {
height: $side-menu-width;
top: 0;
left: 0;
right: 0;
transform: translate3d(0,-$side-menu-width,0);
&.open {
transform: translate3d(0,0,0);
}
}
&.bottom {
height: $side-menu-width;
bottom: 0;
left: 0;
right: 0;
transform: translate3d(0,$side-menu-width,0);
&.open {
transform: translate3d(0,0,0);
}
}
}
ion-side-menu-parent {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: block;
overflow:hidden;
}

View File

@@ -1,29 +1,42 @@
import * as Platform from './platform';
import * as util from './util';
var id = 0;
export function IonConfigService() {
export function IonConfig() {
function Config() {
this._platforms = {};
for (var key in Config._platforms) {
this._platforms[key] = Config._platforms[key]._clone();
// TODO automatically add platform class
// TODO do bindings/defaults have to be written twice?
// TODO maybe add config to IonicComponent annotation
function Config(instance) {
util.defaults(instance, Config._defaults || {});
var conditions = Config._conditions;
for (var i = 0, ii = conditions.length; i < ii; i++) {
if (conditions[i]._callback(instance)) {
for (var j = 0, jj = conditions[i]._mixins.length; j < jj; j++) {
conditions[i]._mixins[j].call(instance);
}
}
}
this.id = id++;
}
Config._platforms = {};
Config.platform = platformFn.bind(Config);
Config.prototype.platform = platformFn;
function platformFn(name) {
return this._platforms[name] || (this._platforms[name] = new SubConfig(name));
}
Config._conditions = [];
Config.defaults = function(defaults) {
Config._defaults = defaults;
};
Config.when = function when(callback) {
var condition = new ConfigCondition(callback);
Config._conditions.push(condition);
return condition;
};
Config.platform = function platform(name) {
return Config.when(() => Platform.getPlatform() === name);
};
return Config;
}
class SubConfig {
constructor(name, mixins = [], template = '') {
this._name = name;
class ConfigCondition {
constructor(callback, mixins = [], template = '') {
this._callback = callback;
this._mixins = mixins;
this._template = template;
}
@@ -35,7 +48,4 @@ class SubConfig {
this._template = url;
return this;
}
_clone() {
return new SubConfig(this._name, this._mixins.slice(), this._template);
}
}

View File

@@ -1,31 +1,36 @@
import {Gesture} from './gesture';
var noop = function() {};
import * as util from '../../util';
class DragGesture extends Gesture {
// constructor(element, opts = {}) {
// super(element, opts);
// this.onDrag = opts.onDrag;
// this.onDragStart = opts.onDragStart;
// this.onDragEnd = opts.onDragEnd;
// }
// listen() {
// super.listen();
// this.hammertime.on('dragstart', this._onDragStart.bind(this));
// this.hammertime.on('drag', this._onDrag.bind(this));
// this.hammertime.on('dragend', this._onDragEnd.bind(this));
// }
// unlisten() {
// super.unlisten();
// this.hammertime.destroy();
// }
// _onDragStart(ev) {
// (this.onDragStart || noop)(ev);
// }
// _onDrag(ev) {
// (this.onDrag || noop)(ev);
// }
// _onDragEnd(ev) {
// (this.onDragEnd || noop)(ev);
// }
export class DragGesture extends Gesture {
constructor(element, opts = {}) {
util.extend(this, {
onDrag: opts.onDrag,
onDragEnd: opts.onDragEnd,
onDragStart: opts.onDragStart
});
super(element, opts);
}
listen() {
super.listen();
console.log('listening');
this.hammertime.on('panstart', ev => {
console.log('panstart');
if (this.onDragStart && this.onDragStart(ev) !== false) {
this.dragging = true;
}
});
this.hammertime.on('panmove', ev => {
console.log('panmove');
if (!this.dragging) return;
if (this.onDrag && this.onDrag(ev) === false) {
this.dragging = false;
}
});
this.hammertime.on('panend', ev => {
console.log('panend');
if (!this.dragging) return;
this.onDragEnd && this.onDragEnd(ev);
this.dragging = false;
});
}
}

View File

@@ -1,17 +0,0 @@
import {DragGesture} from './drag-gesture';
class EdgeDragGesture extends DragGesture {
// constructor(element, opts = { edge = 'left', buffer = 25 } = {}) {
// super(element, opts);
// }
// _onDragStart(ev) {
// var { buffer, edge } = this._options;
// var { gesture } = ev;
// if (edge === 'left' && gesture.center.x > buffer) return;
// if (edge === 'top' && gesture.center.y > buffer) return;
// if (edge === 'right' && gesture.center.y < window.innerWidth - buffer) return;
// if (edge === 'bottom' && gesture.center.y < window.innerHeight - buffer) return;
// super._onDragStart(ev);
// }
}

View File

@@ -1,37 +1,22 @@
import * as util from '../../util';
export class Gesture {
// constructor(element, opts = {}) {
// this.element = element;
// this._options = opts;
// }
// options(opts = {}) {
// extend(this._options, opts);
// }
constructor(element, opts = {}) {
this.element = element;
this._options = opts;
}
options(opts = {}) {
util.extend(this._options, opts);
}
// listen() {
// this.hammertime = Hammer(element, this._options);
// }
// unlisten() {
// this.hammertime.destroy();
// this.hammertime = null;
// }
// destroy() {
// this.hammertime.destroy();
// }
}
// TODO make a utils.js
function extend() {
for (var i = 0, ii = arguments.length; i < ii; i++) {
var obj = arguments[i];
if (obj) {
var keys = Object.keys(obj);
for (var j = 0, jj = keys.length; j < jj; j++) {
var key = keys[j];
if (!ILLEGAL_ASSIGN_FIELDS[key]) {
this[key] = obj[key];
}
}
}
listen() {
this.hammertime = Hammer(this.element, this._options);
}
unlisten() {
this.hammertime.destroy();
this.hammertime = null;
}
destroy() {
this.hammertime.destroy();
}
}

View File

@@ -0,0 +1,38 @@
import {DragGesture} from './drag-gesture';
import * as util from '../../util';
export class SlideGesture extends DragGesture {
// These getters are overridden by the implementation
getSlideRange() {
return [0, this.element.offsetWidth];
}
getEventPos(ev, slideRange) {
return -slideRange[1] + n;
}
getElementPos(ev, slideRange) {
return slideRange[0];
}
onDragStart(ev) {
var { direction } = this._options.direction;
var slideRange = this.getSlideRange(ev);
var dragStartPos = this.getEventPos(ev);
var elementStartpos = this.getElementPos(ev, distance);
this._state = { distance, dragStartPos, elementStartPos };
return this.onSlideStart && this.onSlideStart(this._state, ev);
}
onDrag(ev) {
var { distance, dragStartPos, elementStartPos } = this._state;
var pos = elementStartPos + this.getEventPos(ev) - dragStartPos;
this._state.position = util.clamp(slideRange[0], n, slideRange[1]);
return this.onSlide && this.onSlide(this._state, ev);
}
onDragEnd() {
var ret = this.onSlideEnd && this.onSlideEnd(this._state, ev);
this._state = null;
return ret;
}
}

26
src/util.js Normal file
View File

@@ -0,0 +1,26 @@
export function noop() {}
export function extend(dest) {
for (var i = 1, ii = arguments.length; i < ii; i++) {
var source = arguments[i] || {};
for (var key in source) {
if (source.hasOwnProperty(key)) {
dest[key] = source[key];
}
}
}
return dest;
}
export function clamp(min, n, max) {
return Math.max(min, Math.min(n, max));
}
export function defaults(obj, src) {
for (var key in src) {
if (src.hasOwnProperty(key) && !obj.hasOwnProperty(key)) {
obj[key] = src[key];
}
}
return obj;
}