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 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">
|
||||
Hello ...
|
||||
<p>...</p>
|
||||
|
@ -2,8 +2,6 @@ import {bootstrap} from 'angular2/core';
|
||||
import {Component, Template} from 'angular2/angular2';
|
||||
import {Aside, AsideParent} from 'ionic/components';
|
||||
|
||||
// import 'ionic/components/tabbar/mixins/android/android-tabbar';
|
||||
|
||||
@Component({ selector: '[playground-main]' })
|
||||
@Template({
|
||||
url: 'main.html',
|
||||
|
@ -1,2 +1,2 @@
|
||||
export * from './components/sidemenu/sidemenu';
|
||||
import './components/sidemenu/behaviors/direction/direction';
|
||||
export * from './components/aside/aside'
|
||||
import './components/aside/behaviors/direction/direction'
|
||||
|
@ -4,10 +4,10 @@ import {IonConfig} from '../../config';
|
||||
import {DragGesture} from '../../core/gestures/drag-gesture';
|
||||
import * as util from '../../util';
|
||||
|
||||
export var asideConfig = new IonConfig('sidemenu')
|
||||
export var asideConfig = new IonConfig('aside');
|
||||
|
||||
// TODO defaults or bindings?
|
||||
asideConfig.defaults({
|
||||
asideConfig.set({
|
||||
side: 'left',
|
||||
dragThreshold: 50
|
||||
});
|
||||
@ -15,7 +15,7 @@ asideConfig.defaults({
|
||||
@Component({
|
||||
selector: 'ion-aside',
|
||||
bind: {
|
||||
edge: 'side',
|
||||
side: 'side',
|
||||
dragThreshold: 'dragThreshold'
|
||||
},
|
||||
})
|
||||
@ -38,7 +38,8 @@ export class Aside extends Ion {
|
||||
});
|
||||
this.dragMethods = {
|
||||
getMenuStart() { return 0; },
|
||||
getEventPos(ev) { return ev.center.x; }
|
||||
getEventPos(ev) { return ev.center.x; },
|
||||
canStart() { return true; }
|
||||
};
|
||||
this.gesture.listen();
|
||||
|
||||
@ -46,7 +47,10 @@ export class Aside extends Ion {
|
||||
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) {
|
||||
if (!this.dragMethods.canStart(ev)) {
|
||||
|
@ -1,14 +1,18 @@
|
||||
import {asideConfig} from '../../sidemenu';
|
||||
import {asideConfig} from '../../aside';
|
||||
import * as util from '../../../../util';
|
||||
|
||||
asideConfig.when(instance => instance.side === 'bottom')
|
||||
.mixin(function() {
|
||||
asideConfig
|
||||
.behavior(function() {
|
||||
if (this.side !== 'bottom') return;
|
||||
|
||||
this.gesture.options({
|
||||
direction: Hammer.DIRECTION_VERTICAL
|
||||
});
|
||||
this.domElement.classList.add('bottom');
|
||||
util.extend(this.dragMethods, {
|
||||
canStart: ev => {
|
||||
return this.isOpen || ev.center.y > window.innerHeight - this.dragThreshold;
|
||||
},
|
||||
getMenuStart: (drag, ev) => {
|
||||
return this.isOpen ? -drag.height : 0;
|
||||
},
|
||||
@ -27,13 +31,14 @@ asideConfig.when(instance => instance.side === 'bottom')
|
||||
return ev.center.y;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
asideConfig.when(instance => instance.side === 'left')
|
||||
.mixin(function() {
|
||||
})
|
||||
.behavior(function() {
|
||||
if (this.side !== 'left') return;
|
||||
|
||||
this.domElement.classList.add('left');
|
||||
this.gesture.options({
|
||||
direction: Hammer.DIRECTION_HORIZONTAL
|
||||
});
|
||||
util.extend(this.dragMethods, {
|
||||
canStart: (ev) => {
|
||||
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.domElement.style.transform = '';
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
asideConfig.when(instance => instance.side === 'right')
|
||||
.mixin(function() {
|
||||
})
|
||||
.behavior(function() {
|
||||
if (this.side !== 'right') return;
|
||||
|
||||
this.domElement.classList.add('right');
|
||||
this.gesture.options({
|
||||
direction: Hammer.DIRECTION_HORIZONTAL
|
||||
});
|
||||
util.extend(this.dragMethods, {
|
||||
canStart: ev => {
|
||||
return this.isOpen || ev.center.x > window.innerWidth - this.dragThreshold;
|
||||
},
|
||||
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
|
||||
0, -drag.menuStart + drag.pointerStart - ev.center.x, drag.width
|
||||
);
|
||||
this.domElement.style.transform = 'translate3d(' +
|
||||
(drag.width - drag.pos) + 'px,0,0)';
|
||||
@ -76,13 +86,15 @@ asideConfig.when(instance => instance.side === 'right')
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
asideConfig.when(instance => instance.side === 'top')
|
||||
.mixin(function() {
|
||||
})
|
||||
.behavior(function() {
|
||||
if (this.side !== 'top') return;
|
||||
|
||||
this.domElement.classList.add('top');
|
||||
util.extend(this.dragMethods, {
|
||||
canStart: ev => {
|
||||
return this.isOpen || ev.center.y < this.dragThreshold * 5;
|
||||
},
|
||||
getMenuStart: (drag, ev) => {
|
||||
return this.isOpen ? drag.height : 0;
|
||||
},
|
||||
|
200
src/config.js
200
src/config.js
@ -1,55 +1,177 @@
|
||||
import * as Platform from './platform';
|
||||
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
|
||||
// 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) {
|
||||
var platformName = Platform.getPlatform();
|
||||
(element.domElement || element).classList.add(`${name}-${platformName}`);
|
||||
config.platform('ios')
|
||||
.behavior(function() {
|
||||
do something
|
||||
})
|
||||
.defaults({
|
||||
side: 'right'
|
||||
})
|
||||
|
||||
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.platform('ios').media('tablet')
|
||||
.defaults({
|
||||
side: 'bottom'
|
||||
});
|
||||
*/
|
||||
|
||||
Config._conditions = [];
|
||||
Config.defaults = function(defaults) {
|
||||
Config._defaults = defaults;
|
||||
|
||||
/*
|
||||
User wants to remove the default behavior for sidemenu, but that's stuck under `.platform('ios').`
|
||||
|
||||
config.platform('ios').media('tablet') === config.media('tablet').platform('ios')
|
||||
*/
|
||||
var QUERIES = {
|
||||
sm: true,
|
||||
md: true,
|
||||
lg: true
|
||||
};
|
||||
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);
|
||||
var PLATFORMS = {
|
||||
ios: true,
|
||||
android: true
|
||||
};
|
||||
|
||||
return Config;
|
||||
function isPlatform(key = '') {
|
||||
return key.toLowerCase() in PLATFORMS;
|
||||
}
|
||||
|
||||
class ConfigCondition {
|
||||
constructor(callback, mixins = [], template = '') {
|
||||
this._callback = callback;
|
||||
this._mixins = mixins;
|
||||
this._template = template;
|
||||
function isMedia(key = '') {
|
||||
return key.toLowerCase() in QUERIES;
|
||||
}
|
||||
mixin(mixinFn) {
|
||||
this._mixins.push(mixinFn);
|
||||
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;
|
||||
}
|
||||
template(url) {
|
||||
this._template = url;
|
||||
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);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
behavior(fn) {
|
||||
this.behaviors.push(fn);
|
||||
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 {
|
||||
constructor(element, opts = {}) {
|
||||
util.defaults(opts, {
|
||||
});
|
||||
this.element = element;
|
||||
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));
|
||||
}
|
||||
|
||||
export function defaults(obj, src) {
|
||||
for (var key in src) {
|
||||
if (src.hasOwnProperty(key) && !obj.hasOwnProperty(key)) {
|
||||
obj[key] = src[key];
|
||||
export function defaults(dest) {
|
||||
let extendObj = {};
|
||||
for (let i = arguments.length - 1; i >= 1; i--) {
|
||||
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 {ViewHistory} from './view-history';
|
||||
// import {Children} from 'angular2/angular2';
|
||||
|
||||
export class View extends ViewSwitcher {
|
||||
constructor(el) {
|
||||
export class View {
|
||||
constructor(
|
||||
// @Children() views: View
|
||||
) {
|
||||
super(el);
|
||||
|
||||
// A linear history of views that this switcher has gone
|
||||
// through.
|
||||
this.children = [];
|
||||
this.history = new ViewHistory();
|
||||
this.currentChild = null;
|
||||
}
|
||||
|
||||
setChild(view, options = {}) {
|
||||
var direction = options.direction || 'forward';
|
||||
var viewIndex = this.history.indexOf(view);
|
||||
|
||||
if (viewIndex !== -1) {
|
||||
direction = 'back';
|
||||
this.history.popTo(viewIndex);
|
||||
} else {
|
||||
this.history.push(view);
|
||||
setSelected(isSelected) {
|
||||
this.isSelected = isSelected;
|
||||
}
|
||||
|
||||
if (this.currentView) {
|
||||
this.element.removeChild(this.currentView.element);
|
||||
selectChild(child) {
|
||||
if (this.selectedChild) {
|
||||
this.selectedChild.setSelected(false);
|
||||
}
|
||||
this.element.appendChild(view.element);
|
||||
this.currentView = view.element;
|
||||
child.setSelected(true);
|
||||
this.selectedChild = child;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user