mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 04:14:21 +08:00
add tree config with tests
This commit is contained in:
@ -1,4 +1,31 @@
|
|||||||
<ion-aside-parent>
|
<ion-aside-parent>
|
||||||
|
<ion-aside side="right">
|
||||||
|
Hello ...
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
Side menu!
|
||||||
|
</ion-aside>
|
||||||
|
<ion-aside side="bottom">
|
||||||
|
Hello ...
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
Side menu!
|
||||||
|
</ion-aside>
|
||||||
|
<ion-aside side="top">
|
||||||
|
Hello ...
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
Side menu!
|
||||||
|
</ion-aside>
|
||||||
<ion-aside side="left">
|
<ion-aside side="left">
|
||||||
Hello ...
|
Hello ...
|
||||||
<p>...</p>
|
<p>...</p>
|
||||||
@ -13,20 +40,20 @@
|
|||||||
<ion-tabbar view-title="Tabs 2"></ion-tabbar>
|
<ion-tabbar view-title="Tabs 2"></ion-tabbar>
|
||||||
<ion-tabbar view-title="Tabs 4"></ion-tabbar>
|
<ion-tabbar view-title="Tabs 4"></ion-tabbar>
|
||||||
|
|
||||||
<button (click)="showModal()">Show Modal</button>
|
|
||||||
|
|
||||||
|
|
||||||
<ion-switch></ion-switch>
|
|
||||||
<!--
|
|
||||||
<ion-modal>
|
|
||||||
<h2>I'm a modal!</h2>
|
|
||||||
</ion-modal>
|
|
||||||
-->
|
|
||||||
<button (click)="showModal()">Show Modal</button>
|
<button (click)="showModal()">Show Modal</button>
|
||||||
|
|
||||||
|
|
||||||
|
<ion-switch></ion-switch>
|
||||||
<!--
|
<!--
|
||||||
<ion-modal>
|
<ion-modal>
|
||||||
<h2>I'm a modal!</h2>
|
<h2>I'm a modal!</h2>
|
||||||
</ion-modal>
|
</ion-modal>
|
||||||
-->
|
-->
|
||||||
</div>
|
<button (click)="showModal()">Show Modal</button>
|
||||||
|
<!--
|
||||||
|
<ion-modal>
|
||||||
|
<h2>I'm a modal!</h2>
|
||||||
|
</ion-modal>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
</ion-aside-parent>
|
</ion-aside-parent>
|
||||||
|
@ -2,8 +2,6 @@ import {bootstrap} from 'angular2/core';
|
|||||||
import {Component, Template} from 'angular2/angular2';
|
import {Component, Template} from 'angular2/angular2';
|
||||||
import {Aside, AsideParent} from 'ionic/components';
|
import {Aside, AsideParent} from 'ionic/components';
|
||||||
|
|
||||||
// import 'ionic/components/tabbar/mixins/android/android-tabbar';
|
|
||||||
|
|
||||||
@Component({ selector: '[playground-main]' })
|
@Component({ selector: '[playground-main]' })
|
||||||
@Template({
|
@Template({
|
||||||
url: 'main.html',
|
url: 'main.html',
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export * from './components/sidemenu/sidemenu';
|
export * from './components/aside/aside'
|
||||||
import './components/sidemenu/behaviors/direction/direction';
|
import './components/aside/behaviors/direction/direction'
|
||||||
|
@ -4,10 +4,10 @@ import {IonConfig} from '../../config';
|
|||||||
import {DragGesture} from '../../core/gestures/drag-gesture';
|
import {DragGesture} from '../../core/gestures/drag-gesture';
|
||||||
import * as util from '../../util';
|
import * as util from '../../util';
|
||||||
|
|
||||||
export var asideConfig = new IonConfig('sidemenu')
|
export var asideConfig = new IonConfig('aside');
|
||||||
|
|
||||||
// TODO defaults or bindings?
|
// TODO defaults or bindings?
|
||||||
asideConfig.defaults({
|
asideConfig.set({
|
||||||
side: 'left',
|
side: 'left',
|
||||||
dragThreshold: 50
|
dragThreshold: 50
|
||||||
});
|
});
|
||||||
@ -15,7 +15,7 @@ asideConfig.defaults({
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-aside',
|
selector: 'ion-aside',
|
||||||
bind: {
|
bind: {
|
||||||
edge: 'side',
|
side: 'side',
|
||||||
dragThreshold: 'dragThreshold'
|
dragThreshold: 'dragThreshold'
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -38,7 +38,8 @@ export class Aside extends Ion {
|
|||||||
});
|
});
|
||||||
this.dragMethods = {
|
this.dragMethods = {
|
||||||
getMenuStart() { return 0; },
|
getMenuStart() { return 0; },
|
||||||
getEventPos(ev) { return ev.center.x; }
|
getEventPos(ev) { return ev.center.x; },
|
||||||
|
canStart() { return true; }
|
||||||
};
|
};
|
||||||
this.gesture.listen();
|
this.gesture.listen();
|
||||||
|
|
||||||
@ -46,7 +47,10 @@ export class Aside extends Ion {
|
|||||||
this.setChanging(false);
|
this.setChanging(false);
|
||||||
})
|
})
|
||||||
|
|
||||||
asideConfig(this);
|
// TODO: remove this. setTimeout has to be done so the bindings can be applied
|
||||||
|
setTimeout(() => {
|
||||||
|
asideConfig.invoke(this);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
onDragStart(ev) {
|
onDragStart(ev) {
|
||||||
if (!this.dragMethods.canStart(ev)) {
|
if (!this.dragMethods.canStart(ev)) {
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import {asideConfig} from '../../sidemenu';
|
import {asideConfig} from '../../aside';
|
||||||
import * as util from '../../../../util';
|
import * as util from '../../../../util';
|
||||||
|
|
||||||
asideConfig.when(instance => instance.side === 'bottom')
|
asideConfig
|
||||||
.mixin(function() {
|
.behavior(function() {
|
||||||
|
if (this.side !== 'bottom') return;
|
||||||
|
|
||||||
this.gesture.options({
|
this.gesture.options({
|
||||||
direction: Hammer.DIRECTION_VERTICAL
|
direction: Hammer.DIRECTION_VERTICAL
|
||||||
});
|
});
|
||||||
this.domElement.classList.add('bottom');
|
this.domElement.classList.add('bottom');
|
||||||
util.extend(this.dragMethods, {
|
util.extend(this.dragMethods, {
|
||||||
|
canStart: ev => {
|
||||||
|
return this.isOpen || ev.center.y > window.innerHeight - this.dragThreshold;
|
||||||
|
},
|
||||||
getMenuStart: (drag, ev) => {
|
getMenuStart: (drag, ev) => {
|
||||||
return this.isOpen ? -drag.height : 0;
|
return this.isOpen ? -drag.height : 0;
|
||||||
},
|
},
|
||||||
@ -27,13 +31,14 @@ asideConfig.when(instance => instance.side === 'bottom')
|
|||||||
return ev.center.y;
|
return ev.center.y;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
})
|
||||||
});
|
.behavior(function() {
|
||||||
|
if (this.side !== 'left') return;
|
||||||
asideConfig.when(instance => instance.side === 'left')
|
|
||||||
.mixin(function() {
|
|
||||||
|
|
||||||
this.domElement.classList.add('left');
|
this.domElement.classList.add('left');
|
||||||
|
this.gesture.options({
|
||||||
|
direction: Hammer.DIRECTION_HORIZONTAL
|
||||||
|
});
|
||||||
util.extend(this.dragMethods, {
|
util.extend(this.dragMethods, {
|
||||||
canStart: (ev) => {
|
canStart: (ev) => {
|
||||||
return this.isOpen || ev.center.x < this.dragThreshold;
|
return this.isOpen || ev.center.x < this.dragThreshold;
|
||||||
@ -51,21 +56,26 @@ asideConfig.when(instance => instance.side === 'left')
|
|||||||
this.setOpen(drag.pos > drag.width / 2);
|
this.setOpen(drag.pos > drag.width / 2);
|
||||||
this.domElement.style.transform = '';
|
this.domElement.style.transform = '';
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
});
|
})
|
||||||
|
.behavior(function() {
|
||||||
asideConfig.when(instance => instance.side === 'right')
|
if (this.side !== 'right') return;
|
||||||
.mixin(function() {
|
|
||||||
|
|
||||||
this.domElement.classList.add('right');
|
this.domElement.classList.add('right');
|
||||||
|
this.gesture.options({
|
||||||
|
direction: Hammer.DIRECTION_HORIZONTAL
|
||||||
|
});
|
||||||
util.extend(this.dragMethods, {
|
util.extend(this.dragMethods, {
|
||||||
|
canStart: ev => {
|
||||||
|
return this.isOpen || ev.center.x > window.innerWidth - this.dragThreshold;
|
||||||
|
},
|
||||||
getMenuStart: (drag, ev) => {
|
getMenuStart: (drag, ev) => {
|
||||||
return this.isOpen ? -drag.width : 0;
|
return this.isOpen ? -drag.width : 0;
|
||||||
},
|
},
|
||||||
onDrag: (drag, ev) => {
|
onDrag: (drag, ev) => {
|
||||||
drag.pos = util.clamp(
|
drag.pos = util.clamp(
|
||||||
0, -drag.menuStart + drag.pointerStart - ev.center.x, drag.height
|
0, -drag.menuStart + drag.pointerStart - ev.center.x, drag.width
|
||||||
);
|
);
|
||||||
this.domElement.style.transform = 'translate3d(' +
|
this.domElement.style.transform = 'translate3d(' +
|
||||||
(drag.width - drag.pos) + 'px,0,0)';
|
(drag.width - drag.pos) + 'px,0,0)';
|
||||||
@ -76,13 +86,15 @@ asideConfig.when(instance => instance.side === 'right')
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
})
|
||||||
|
.behavior(function() {
|
||||||
asideConfig.when(instance => instance.side === 'top')
|
if (this.side !== 'top') return;
|
||||||
.mixin(function() {
|
|
||||||
|
|
||||||
this.domElement.classList.add('top');
|
this.domElement.classList.add('top');
|
||||||
util.extend(this.dragMethods, {
|
util.extend(this.dragMethods, {
|
||||||
|
canStart: ev => {
|
||||||
|
return this.isOpen || ev.center.y < this.dragThreshold * 5;
|
||||||
|
},
|
||||||
getMenuStart: (drag, ev) => {
|
getMenuStart: (drag, ev) => {
|
||||||
return this.isOpen ? drag.height : 0;
|
return this.isOpen ? drag.height : 0;
|
||||||
},
|
},
|
||||||
|
210
src/config.js
210
src/config.js
@ -1,55 +1,177 @@
|
|||||||
import * as Platform from './platform';
|
import * as Platform from './platform';
|
||||||
import * as util from './util';
|
import * as util from './util';
|
||||||
|
|
||||||
export function IonConfig(name) {
|
/*
|
||||||
|
config
|
||||||
|
.set({ side: 'left' })
|
||||||
|
.set('threshold', 50)
|
||||||
|
.platform('ios')
|
||||||
|
.set('side', 'top')
|
||||||
|
.unset('threshold')
|
||||||
|
.media('lg')
|
||||||
|
.set('side', 'right')
|
||||||
|
|
||||||
// TODO automatically add platform class
|
config.platform('ios')
|
||||||
// TODO do bindings/defaults have to be written twice?
|
.behavior(function() {
|
||||||
// TODO maybe add config to IonicComponent annotation
|
do something
|
||||||
// TODO map options to config
|
})
|
||||||
function Config(instance, element) {
|
.defaults({
|
||||||
var platformName = Platform.getPlatform();
|
side: 'right'
|
||||||
(element.domElement || element).classList.add(`${name}-${platformName}`);
|
})
|
||||||
|
|
||||||
|
config.platform('ios').media('tablet')
|
||||||
|
.defaults({
|
||||||
|
side: 'bottom'
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
util.defaults(instance, Config._defaults || {});
|
|
||||||
var conditions = Config._conditions;
|
/*
|
||||||
for (var i = 0, ii = conditions.length; i < ii; i++) {
|
User wants to remove the default behavior for sidemenu, but that's stuck under `.platform('ios').`
|
||||||
if (conditions[i]._callback(instance)) {
|
|
||||||
for (var j = 0, jj = conditions[i]._mixins.length; j < jj; j++) {
|
config.platform('ios').media('tablet') === config.media('tablet').platform('ios')
|
||||||
conditions[i]._mixins[j].call(instance);
|
*/
|
||||||
}
|
var QUERIES = {
|
||||||
}
|
sm: true,
|
||||||
|
md: true,
|
||||||
|
lg: true
|
||||||
|
};
|
||||||
|
var PLATFORMS = {
|
||||||
|
ios: true,
|
||||||
|
android: true
|
||||||
|
};
|
||||||
|
|
||||||
|
function isPlatform(key = '') {
|
||||||
|
return key.toLowerCase() in PLATFORMS;
|
||||||
|
}
|
||||||
|
function isMedia(key = '') {
|
||||||
|
return key.toLowerCase() in QUERIES;
|
||||||
|
}
|
||||||
|
class ConfigCase {
|
||||||
|
constructor({ root, parent, path }) {
|
||||||
|
this._root = root;
|
||||||
|
this._parent = parent;
|
||||||
|
this._path = path || [];
|
||||||
|
this._values = {};
|
||||||
|
this.behaviors = [];
|
||||||
|
}
|
||||||
|
platform(key = '') {
|
||||||
|
if (isPlatform(key)) return this._root._addCase(key, this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
media(key = '') {
|
||||||
|
if (isMedia(key)) return this._root._addCase(key, this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
when(condition = '') {
|
||||||
|
if (isPlatform(condition) || isMedia(condition)) {
|
||||||
|
return this._root._addCase(condition, this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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 ConfigCondition {
|
|
||||||
constructor(callback, mixins = [], template = '') {
|
|
||||||
this._callback = callback;
|
|
||||||
this._mixins = mixins;
|
|
||||||
this._template = template;
|
|
||||||
}
|
|
||||||
mixin(mixinFn) {
|
|
||||||
this._mixins.push(mixinFn);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
template(url) {
|
behavior(fn) {
|
||||||
this._template = url;
|
this.behaviors.push(fn);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
set(a, b) {
|
||||||
|
if (util.isString(a)) {
|
||||||
|
this._values[a] = b;
|
||||||
|
} else {
|
||||||
|
util.extend(this._values, a || {});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
unset(key) {
|
||||||
|
delete this._values[key];
|
||||||
|
}
|
||||||
|
get(key) {
|
||||||
|
return util.isDefined(this._values[key]) ?
|
||||||
|
this._values[key] :
|
||||||
|
(this._parent ? this._parent.get(key) : undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IonConfig extends ConfigCase {
|
||||||
|
constructor() {
|
||||||
|
this._root = this;
|
||||||
|
this._cases = {};
|
||||||
|
super({
|
||||||
|
root: this,
|
||||||
|
parent: null,
|
||||||
|
path: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
invoke(instance) {
|
||||||
|
return invokeConfig(this, instance);
|
||||||
|
}
|
||||||
|
_addCase(key, baseCase) {
|
||||||
|
var path = baseCase._path.slice();
|
||||||
|
path.push(key);
|
||||||
|
|
||||||
|
// Remove empties & duplicates
|
||||||
|
path = path
|
||||||
|
.filter((value, index) => {
|
||||||
|
return value && path.indexOf(value) === index;
|
||||||
|
})
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
if (path.join(' ') === baseCase._path.join(' ')) {
|
||||||
|
return baseCase;
|
||||||
|
}
|
||||||
|
return this._createCase(path);
|
||||||
|
}
|
||||||
|
_createCase(path) {
|
||||||
|
if (!path.length) return this;
|
||||||
|
var pathStr = path.join(' ');
|
||||||
|
var configCase = this._cases[pathStr];
|
||||||
|
if (!configCase) {
|
||||||
|
var parentPath = path.slice(0, path.length - 1);
|
||||||
|
configCase = this._cases[pathStr] = new ConfigCase({
|
||||||
|
root: this,
|
||||||
|
parent: this._createCase(parentPath),
|
||||||
|
path: path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return configCase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function invokeConfig(config, object, opts = {}) {
|
||||||
|
util.defaults(opts, { media: 'lg', platform: 'ios' });
|
||||||
|
var { platform, media } = opts;
|
||||||
|
|
||||||
|
var passedCases = [config].concat(
|
||||||
|
Object.keys(config._cases)
|
||||||
|
.map(name => config._cases[name])
|
||||||
|
.filter(configCasePasses)
|
||||||
|
.sort(function(a,b) {
|
||||||
|
return a._path.length < b._path.length ? -1 : 1;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Extend the given object with the values of all the passed cases, starting with the
|
||||||
|
// most specific.
|
||||||
|
var defaults = [object];
|
||||||
|
var behaviors = [];
|
||||||
|
for (let i = 0, ii = passedCases.length; i < ii; i++) {
|
||||||
|
defaults.push(passedCases[i]._values);
|
||||||
|
// Avoid allocating a new array for each passed case's array of behaviors
|
||||||
|
behaviors.push.apply(behaviors, passedCases[i].behaviors);
|
||||||
|
}
|
||||||
|
|
||||||
|
util.defaults.apply(null, defaults);
|
||||||
|
|
||||||
|
for (let i = 0, ii = behaviors.length; i < ii; i++) {
|
||||||
|
behaviors[i].call(object, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function configCasePasses(configCase) {
|
||||||
|
var path = configCase._path;
|
||||||
|
var key;
|
||||||
|
for (let i = 0, ii = path.length; i < ii; i++) {
|
||||||
|
if (!(media === path[i] || platform === path[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
137
src/config_spec.js
Normal file
137
src/config_spec.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import {IonConfig} from './config';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
var rootConfig;
|
||||||
|
beforeEach(() => {
|
||||||
|
rootConfig = new IonConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a config one level down', () => {
|
||||||
|
var sub = rootConfig.platform('ios');
|
||||||
|
expect(sub._parent).toBe(rootConfig);
|
||||||
|
expect(sub._path).toEqual(['ios']);
|
||||||
|
expect(rootConfig._cases.ios).toBe(sub);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a config two levels down', () => {
|
||||||
|
var sub1 = rootConfig.platform('ios');
|
||||||
|
var sub2 = sub1.media('lg');
|
||||||
|
expect(sub2._parent).toBe(sub1);
|
||||||
|
expect(sub1._parent).toBe(rootConfig);
|
||||||
|
expect(rootConfig._cases['ios lg']).toBe(sub2);
|
||||||
|
expect(rootConfig._cases.ios).toBe(sub1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set should be chainable', () => {
|
||||||
|
expect(rootConfig.set()).toBe(rootConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set values on the root', () => {
|
||||||
|
rootConfig.set({
|
||||||
|
letter: 'a'
|
||||||
|
});
|
||||||
|
expect(rootConfig.get('letter')).toBe('a');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should always return the same object for the same key', () => {
|
||||||
|
expect(rootConfig.platform('android')).toBe(rootConfig.platform('android'));
|
||||||
|
expect(rootConfig.platform('ios')).toBe(rootConfig.platform('ios'));
|
||||||
|
expect(rootConfig.media('lg')).toBe(rootConfig.media('lg'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the same object when nesting in different order', () => {
|
||||||
|
var sub1 = rootConfig.platform('ios').media('sm');
|
||||||
|
var sub2 = rootConfig.media('sm').platform('ios');
|
||||||
|
expect(sub1).toBe(sub2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the same object when nesting in different order for huge queries', () => {
|
||||||
|
var sub1 = rootConfig.platform('ios').media('sm').platform('android').media('lg');
|
||||||
|
var sub2 = rootConfig.media('sm').media('lg').platform('android').platform('ios');
|
||||||
|
expect(sub1).toBe(sub2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set values one level down and be chainable', () => {
|
||||||
|
rootConfig.set({ letter: 'a' });
|
||||||
|
var sub1 = rootConfig.platform('ios');
|
||||||
|
expect(sub1.get('letter')).toBe('a');
|
||||||
|
expect( sub1.set({ letter: 'b' }) ).toBe(sub1);
|
||||||
|
expect(sub1.get('letter')).toBe('b');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set values two levels down and be chainable', () => {
|
||||||
|
rootConfig.set({ letter: 'a' });
|
||||||
|
var sub1 = rootConfig.platform('ios');
|
||||||
|
sub1.set({ letter: 'b' });
|
||||||
|
var sub2 = sub1.media('lg');
|
||||||
|
expect(sub2.get('letter')).toBe('b');
|
||||||
|
expect( sub2.set({ letter: 'c' }) ).toBe(sub2);
|
||||||
|
expect(sub2.get('letter')).toBe('c');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use parent\'s value if its later set to undefined', () => {
|
||||||
|
rootConfig.set({ letter: 'a' });
|
||||||
|
var sub1 = rootConfig.platform('ios');
|
||||||
|
sub1.set({ letter: 'b' });
|
||||||
|
expect(sub1.get('letter')).toBe('b');
|
||||||
|
sub1.unset('letter');
|
||||||
|
expect(sub1.get('letter')).toBe('a');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when() as alias for media()', () => {
|
||||||
|
expect(rootConfig.when('lg')).toBe(rootConfig.media('lg'));
|
||||||
|
expect(rootConfig.when('bad')).toBe(rootConfig);
|
||||||
|
expect(rootConfig.when('lg')).not.toBe(rootConfig.when('ios'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when() as alias for platform()', () => {
|
||||||
|
expect(rootConfig.platform('ios')).toBe(rootConfig.when('ios'));
|
||||||
|
expect(rootConfig.when('bad')).toBe(rootConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('invokeConfig', function() {
|
||||||
|
|
||||||
|
it('should invoke defaults', () => {
|
||||||
|
var obj = {};
|
||||||
|
rootConfig.set('foo', 'bar');
|
||||||
|
rootConfig.invoke(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoke defaults in nested whens', () => {
|
||||||
|
var obj = {};
|
||||||
|
rootConfig.set({ a: 'root', b: 'root' });
|
||||||
|
rootConfig.when('ios').set({b: 'ios', c: 'ios'});
|
||||||
|
rootConfig.when('ios').when('lg').set({ c: 'ios-lg', d: 'ios-lg' });
|
||||||
|
|
||||||
|
rootConfig.invoke(obj);
|
||||||
|
expect(obj).toEqual({
|
||||||
|
a: 'root',
|
||||||
|
b: 'ios',
|
||||||
|
c: 'ios-lg',
|
||||||
|
d: 'ios-lg'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should run behaviors', () => {
|
||||||
|
var obj = {};
|
||||||
|
rootConfig.behavior(instance => {
|
||||||
|
instance.foo = 'bar';
|
||||||
|
});
|
||||||
|
rootConfig.invoke(obj);
|
||||||
|
expect(obj.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoke behaviors in nested whens', () => {
|
||||||
|
var obj = {};
|
||||||
|
rootConfig.when('ios')
|
||||||
|
.behavior(o => o.ios = true)
|
||||||
|
.when('lg')
|
||||||
|
.behavior(o => o.lg = true)
|
||||||
|
rootConfig.invoke(obj);
|
||||||
|
expect(obj).toEqual({
|
||||||
|
ios: true,
|
||||||
|
lg: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
import * as Platform from '../../platform';
|
|
||||||
import * as util from '../../util';
|
|
||||||
|
|
||||||
export function IonConfig() {
|
|
||||||
|
|
||||||
// TODO automatically add platform class
|
|
||||||
// TODO do bindings/defaults have to be written twice?
|
|
||||||
// TODO maybe add config to IonicComponent annotation
|
|
||||||
// TODO map options to config
|
|
||||||
function Config(instance, element) {
|
|
||||||
(element.domElement || element).classList.add('platform-' + Platform.getPlatform());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ConfigCondition {
|
|
||||||
constructor(callback, mixins = [], template = '') {
|
|
||||||
this._callback = callback;
|
|
||||||
this._mixins = mixins;
|
|
||||||
this._template = template;
|
|
||||||
}
|
|
||||||
mixin(mixinFn) {
|
|
||||||
this._mixins.push(mixinFn);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
template(url) {
|
|
||||||
this._template = url;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
import {Config} from './config';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
}
|
|
@ -2,6 +2,8 @@ import * as util from '../../util';
|
|||||||
|
|
||||||
export class Gesture {
|
export class Gesture {
|
||||||
constructor(element, opts = {}) {
|
constructor(element, opts = {}) {
|
||||||
|
util.defaults(opts, {
|
||||||
|
});
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this._options = opts;
|
this._options = opts;
|
||||||
}
|
}
|
||||||
|
37
src/util.js
37
src/util.js
@ -16,11 +16,38 @@ export function clamp(min, n, max) {
|
|||||||
return Math.max(min, Math.min(n, max));
|
return Math.max(min, Math.min(n, max));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaults(obj, src) {
|
export function defaults(dest) {
|
||||||
for (var key in src) {
|
let extendObj = {};
|
||||||
if (src.hasOwnProperty(key) && !obj.hasOwnProperty(key)) {
|
for (let i = arguments.length - 1; i >= 1; i--) {
|
||||||
obj[key] = src[key];
|
let source = arguments[i] || {};
|
||||||
|
for (let key in source) {
|
||||||
|
if (!dest.hasOwnProperty(key) && !extendObj.hasOwnProperty(key)) {
|
||||||
|
extendObj[key] = source[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj;
|
for (let key in extendObj) {
|
||||||
|
dest[key] = extendObj[key];
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isString(val) {
|
||||||
|
return typeof val === 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFunction(val) {
|
||||||
|
return typeof val === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDefined(val) {
|
||||||
|
return typeof val !== 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
export var array = {
|
||||||
|
unique(array) {
|
||||||
|
return array.filter(function(value, index) {
|
||||||
|
return array.indexOf(value) === index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
52
src/util_spec.js
Normal file
52
src/util_spec.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import * as util from './util';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('extend', function() {
|
||||||
|
|
||||||
|
it('should extend simple', () => {
|
||||||
|
var obj = { a: '0', c: '0' };
|
||||||
|
expect( util.extend(obj, { a: '1', b: '2' }) ).toBe(obj);
|
||||||
|
expect(obj).toEqual({ a: '1', b: '2', c: '0' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extend complex', () => {
|
||||||
|
expect(util.extend(
|
||||||
|
{ a: '0', b: '0' },
|
||||||
|
{ b: '1', c: '1' },
|
||||||
|
{ c: '2', d: '2' }
|
||||||
|
)).toEqual({
|
||||||
|
a: '0',
|
||||||
|
b: '1',
|
||||||
|
c: '2',
|
||||||
|
d: '2'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('defaults', function() {
|
||||||
|
|
||||||
|
it('should simple defaults', () => {
|
||||||
|
var obj = { a: '1' };
|
||||||
|
expect(util.defaults(obj, { a: '2', b: '2' })).toBe(obj);
|
||||||
|
expect(obj).toEqual({
|
||||||
|
a: '1', b: '2'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complex defaults', () => {
|
||||||
|
expect(util.defaults(
|
||||||
|
{ a: '0', b: '0' },
|
||||||
|
{ b: '1', c: '1', e: '1' },
|
||||||
|
{ c: '2', d: '2' }
|
||||||
|
)).toEqual({
|
||||||
|
a: '0',
|
||||||
|
b: '0',
|
||||||
|
c: '2',
|
||||||
|
d: '2',
|
||||||
|
e: '1'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
@ -1,33 +1,25 @@
|
|||||||
import * as util from '../util';
|
import * as util from '../util';
|
||||||
import {ViewHistory} from './view-history';
|
import {ViewHistory} from './view-history';
|
||||||
|
// import {Children} from 'angular2/angular2';
|
||||||
|
|
||||||
export class View extends ViewSwitcher {
|
export class View {
|
||||||
constructor(el) {
|
constructor(
|
||||||
|
// @Children() views: View
|
||||||
|
) {
|
||||||
super(el);
|
super(el);
|
||||||
|
this.children = [];
|
||||||
// A linear history of views that this switcher has gone
|
|
||||||
// through.
|
|
||||||
this.history = new ViewHistory();
|
this.history = new ViewHistory();
|
||||||
this.currentChild = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setChild(view, options = {}) {
|
setSelected(isSelected) {
|
||||||
var direction = options.direction || 'forward';
|
this.isSelected = isSelected;
|
||||||
var viewIndex = this.history.indexOf(view);
|
|
||||||
|
|
||||||
if (viewIndex !== -1) {
|
|
||||||
direction = 'back';
|
|
||||||
this.history.popTo(viewIndex);
|
|
||||||
} else {
|
|
||||||
this.history.push(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.currentView) {
|
|
||||||
this.element.removeChild(this.currentView.element);
|
|
||||||
}
|
|
||||||
this.element.appendChild(view.element);
|
|
||||||
this.currentView = view.element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectChild(child) {
|
||||||
|
if (this.selectedChild) {
|
||||||
|
this.selectedChild.setSelected(false);
|
||||||
|
}
|
||||||
|
child.setSelected(true);
|
||||||
|
this.selectedChild = child;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user