import * as I from 'immutable'; import merge from 'lodash.merge'; import {compose, Property} from '@avocado/core'; import {Vector} from '@avocado/math'; import {Resource} from '@avocado/resource'; import {Synchronized} from '@avocado/state'; const decorate = compose( Synchronized, ); export class Trait extends decorate(class {}) { constructor(entity, params, state) { super(); this.entity = entity; const ctor = this.constructor; this.isDirty = true; this._memoizedListeners = undefined; this.params = Object.assign({}, ctor.defaultParams(), params); this.state = I.fromJS(ctor.defaultState()).merge(I.fromJS(state)); this.initializeSynchronizedChildren(); if (this.tick) { this.tick = this.tick.bind(this); } if (this.renderTick) { this.renderTick = this.renderTick.bind(this); } } destroy() {} hooks() { return {}; } hydrate() { return Promise.resolve(); } initialize() {} label() { return this.constructor.name; } listeners() { return {}; } memoizedListeners() { if (!this._memoizedListeners) { this._memoizedListeners = this.listeners(); } return this._memoizedListeners; } methods() { return {}; } patchStateStep(key, step) { if ('state' !== key) { return; } const stateKey = step.path.substr(1); const value = this.transformPatchValue(stateKey, step.value); if (stateKey in this.entity) { this.entity[stateKey] = value; } else { this.state = this.state.set(stateKey, value); } } toJSON() { return { params: this.params, state: this.state.toJS(), }; } transformPatchValue(key, value) { return value; } static contextType() { return {}; } static defaultParams() { return {}; } static defaultState() { return {}; } static dependencies() { return []; } } export function StateProperty(key, meta = {}) { let transformedProperty; if (meta.transformProperty) { transformedProperty = meta.transformProperty(key); } else { transformedProperty = `$$avocado_state_property_${key}`; } return (Superclass) => { meta.emit = meta.emit || function(...args) { this.entity.emit(...args); }; meta.initialize = meta.initialize || function() { this[transformedProperty] = this.state.get(key); } meta.get = meta.get || new Function(` return this.${transformedProperty}; `); meta.set = meta.set || new Function('value', ` this.isDirty = true; this.${transformedProperty} = value; this.state = this.state.set('${key}', value); `); return Property(key, meta)(Superclass); } } StateProperty.Vector = (vector, x, y, meta = {}) => { return (Superclass) => { meta.default = undefined; meta.emit = meta.emit || function(...args) { this.entity.emit(...args); }; meta.get = meta.get || function() { return [ this.state.get(x), this.state.get(y), ]; }; meta.set = meta.set || function(vector) { if (meta.track && meta.emit) { if (this.state.get(x) !== vector[0]) { meta.emit.call(this, `${x}Changed`, this.state.get(x), vector[0]); } if (this.state.get(y) !== vector[1]) { meta.emit.call(this, `${y}Changed`, this.state.get(y), vector[1]); } } this.isDirty = true; this.state = this.state.merge({ [x]: vector[0], [y]: vector[1], }); }; return Vector.Mixin(vector, x, y, meta)(Superclass); }; }