import * as I from 'immutable'; import {Vector} from '@avocado/math'; import {Property} from '@avocado/mixins'; import {Resource} from '@avocado/resource'; import {StateSynchronizer} from '@avocado/state'; export class Trait { constructor(entity, params, state) { this.entity = entity; const ctor = this.constructor; this.params = I.fromJS(ctor.defaultParams()).merge(I.fromJS(params)); this.state = I.fromJS(ctor.defaultState()).merge(I.fromJS(state)); } destroy() {} hooks() { return {}; } hydrate() { return Promise.resolve(); } initialize() {} label() { return this.constructor.name; } listeners() { return {}; } methods() { return {}; } patchState(patch) { for (const step of patch) { const {op, path, value} = step; if ('/' === path) { if (value.state) { this.patchStateEntity(value.state); } } else { const [stepKey, substep] = StateSynchronizer.forwardStep(step); if ('state' === stepKey) { const key = substep.path.substr(1); this.patchStateEntity({[key]: substep.value}); } } } } patchStateEntity(state) { const undefinedProperties = {}; for (const key in state) { const value = state[key]; if (key in this.entity) { this.entity[key] = value; } else { undefinedProperties[key] = value; } } this.state = this.state.merge(undefinedProperties); } toJSON() { return { params: this.params.toJS(), state: this.state.toJS(), }; } static contextType() { return {}; } static defaultParams() { return {}; } static defaultState() { return {}; } static dependencies() { return []; } static type() { return this.name.toLowerCase(); } } export function StateProperty(key, meta = {}) { return (Superclass) => { meta.emit = meta.emit || function(...args) { this.entity.emit(...args); }; meta.get = meta.get || function(value) { return this.state.get(key); }; meta.set = meta.set || function(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.state = this.state.merge({ [x]: vector[0], [y]: vector[1], }); }; return Vector.Mixin(vector, x, y, meta)(Superclass); }; }