2019-03-20 11:49:41 +01:00
|
|
|
import { EventBus } from "./event_bus";
|
2019-03-22 09:52:48 +01:00
|
|
|
import { Component } from "./component";
|
2019-04-09 10:09:29 +02:00
|
|
|
import { shallowEqual } from "./utils";
|
2019-03-20 11:49:41 +01:00
|
|
|
|
|
|
|
|
interface StoreConfig {
|
2019-03-29 11:23:44 +01:00
|
|
|
env?: any;
|
2019-03-20 11:49:41 +01:00
|
|
|
state?: any;
|
|
|
|
|
actions?: any;
|
|
|
|
|
mutations?: any;
|
|
|
|
|
}
|
2019-03-22 10:56:34 +01:00
|
|
|
|
|
|
|
|
interface StoreOption {
|
|
|
|
|
debug?: boolean;
|
|
|
|
|
}
|
2019-03-20 11:49:41 +01:00
|
|
|
export class Store extends EventBus {
|
2019-04-08 23:02:23 +02:00
|
|
|
state: any;
|
|
|
|
|
mstate: any;
|
2019-03-20 11:49:41 +01:00
|
|
|
actions: any;
|
|
|
|
|
mutations: any;
|
|
|
|
|
_isMutating: boolean = false;
|
2019-04-08 23:02:23 +02:00
|
|
|
_isDirty: boolean = false;
|
2019-03-22 10:56:34 +01:00
|
|
|
history: any[] = [];
|
|
|
|
|
debug: boolean;
|
2019-03-29 11:23:44 +01:00
|
|
|
env: any;
|
2019-03-20 11:49:41 +01:00
|
|
|
|
2019-03-22 10:56:34 +01:00
|
|
|
constructor(config: StoreConfig, options: StoreOption = {}) {
|
2019-03-20 11:49:41 +01:00
|
|
|
super();
|
2019-03-22 10:56:34 +01:00
|
|
|
this.debug = options.debug || false;
|
2019-04-08 23:02:23 +02:00
|
|
|
this.state = Object.assign({}, config.state);
|
2019-04-08 23:27:57 +02:00
|
|
|
const self = this;
|
2019-04-08 23:02:23 +02:00
|
|
|
this.mstate = magify({
|
|
|
|
|
raw: this.state,
|
|
|
|
|
key: "state",
|
|
|
|
|
parent: this,
|
2019-04-08 23:27:57 +02:00
|
|
|
onDirty: function() {
|
|
|
|
|
self._isDirty = true;
|
|
|
|
|
}
|
2019-04-08 23:02:23 +02:00
|
|
|
});
|
2019-03-20 11:49:41 +01:00
|
|
|
this.actions = config.actions;
|
|
|
|
|
this.mutations = config.mutations;
|
2019-03-29 11:23:44 +01:00
|
|
|
this.env = config.env;
|
2019-03-22 10:56:34 +01:00
|
|
|
|
|
|
|
|
if (this.debug) {
|
|
|
|
|
this.history.push({ state: this.state });
|
|
|
|
|
}
|
2019-03-20 11:49:41 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-29 11:23:44 +01:00
|
|
|
dispatch(action, payload?: any) {
|
2019-03-20 11:49:41 +01:00
|
|
|
if (!this.actions[action]) {
|
|
|
|
|
throw new Error(`[Error] action ${action} is undefined`);
|
|
|
|
|
}
|
|
|
|
|
this.actions[action](
|
|
|
|
|
{
|
|
|
|
|
commit: this.commit.bind(this),
|
2019-04-08 22:58:45 +02:00
|
|
|
dispatch: this.dispatch.bind(this),
|
|
|
|
|
env: this.env,
|
|
|
|
|
state: this.state
|
2019-03-20 11:49:41 +01:00
|
|
|
},
|
|
|
|
|
payload
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-08 23:02:23 +02:00
|
|
|
async commit(type, payload?: any) {
|
2019-03-20 11:49:41 +01:00
|
|
|
if (!this.mutations[type]) {
|
|
|
|
|
throw new Error(`[Error] mutation ${type} is undefined`);
|
|
|
|
|
}
|
|
|
|
|
this._isMutating = true;
|
2019-03-21 09:11:44 +01:00
|
|
|
|
2019-04-08 23:02:23 +02:00
|
|
|
this.mutations[type].call(null, this.mstate, payload);
|
2019-03-22 10:56:34 +01:00
|
|
|
if (this.debug) {
|
|
|
|
|
this.history.push({
|
|
|
|
|
state: this.state,
|
|
|
|
|
mutation: type,
|
|
|
|
|
payload: payload
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-03-21 09:11:44 +01:00
|
|
|
await Promise.resolve();
|
|
|
|
|
if (this._isMutating) {
|
|
|
|
|
this._isMutating = false;
|
2019-04-08 23:02:23 +02:00
|
|
|
if (this._isDirty) {
|
|
|
|
|
this._isDirty = false;
|
|
|
|
|
this.trigger("update", this.state);
|
|
|
|
|
}
|
2019-03-21 09:11:44 +01:00
|
|
|
}
|
2019-03-20 11:49:41 +01:00
|
|
|
}
|
2019-04-08 23:02:23 +02:00
|
|
|
}
|
2019-03-20 11:49:41 +01:00
|
|
|
|
2019-04-08 23:02:23 +02:00
|
|
|
export function connect(mapStateToProps) {
|
|
|
|
|
return function(Comp) {
|
|
|
|
|
return class extends Comp {
|
|
|
|
|
constructor(parent, props?: any) {
|
|
|
|
|
const env = parent instanceof Component ? parent.env : parent;
|
|
|
|
|
const ownProps = Object.assign({}, props || {});
|
|
|
|
|
const storeProps = mapStateToProps(env.store.state, ownProps);
|
|
|
|
|
const mergedProps = Object.assign(props || {}, storeProps);
|
|
|
|
|
super(parent, mergedProps);
|
|
|
|
|
this.__widget__.currentStoreProps = storeProps;
|
|
|
|
|
this.__widget__.ownProps = ownProps;
|
|
|
|
|
}
|
|
|
|
|
mounted() {
|
|
|
|
|
this.env.store.on("update", this, () => {
|
|
|
|
|
const ownProps = this.__widget__.ownProps;
|
|
|
|
|
const storeProps = mapStateToProps(this.env.store.state, ownProps);
|
|
|
|
|
if (!shallowEqual(storeProps, this.__widget__.currentStoreProps)) {
|
|
|
|
|
this.__widget__.currentStoreProps = storeProps;
|
|
|
|
|
this.updateProps(ownProps, false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
super.mounted();
|
|
|
|
|
}
|
|
|
|
|
willUnmount() {
|
|
|
|
|
this.env.store.off("update", this);
|
|
|
|
|
super.willUnmount();
|
|
|
|
|
}
|
|
|
|
|
updateProps(nextProps, forceUpdate) {
|
|
|
|
|
if (this.__widget__.ownProps !== nextProps) {
|
|
|
|
|
this.__widget__.currentStoreProps = mapStateToProps(
|
|
|
|
|
this.env.store.state,
|
|
|
|
|
nextProps
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
this.__widget__.ownProps = nextProps;
|
|
|
|
|
const mergedProps = Object.assign(
|
|
|
|
|
{},
|
|
|
|
|
nextProps,
|
|
|
|
|
this.__widget__.currentStoreProps
|
|
|
|
|
);
|
|
|
|
|
return super.updateProps(mergedProps, forceUpdate);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
2019-03-20 11:49:41 +01:00
|
|
|
}
|
2019-04-09 10:09:29 +02:00
|
|
|
|
|
|
|
|
function _magifyArray({ raw, key, parent, magic, onDirty }) {
|
|
|
|
|
Object.defineProperty(magic, "length", {
|
|
|
|
|
get() {
|
|
|
|
|
return magic.raw.length;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Object.assign(magic, {
|
|
|
|
|
push: function(item) {
|
|
|
|
|
onDirty();
|
|
|
|
|
parent.raw[key] = [...magic.raw, item];
|
|
|
|
|
magic.raw = parent.raw[key];
|
|
|
|
|
const index = magic.raw.length - 1;
|
|
|
|
|
let prop = magify({ raw: item, key: index, parent: magic, onDirty });
|
|
|
|
|
Object.defineProperty(magic, index, {
|
|
|
|
|
set(newVal) {
|
|
|
|
|
onDirty();
|
|
|
|
|
parent.raw[key] = [...magic.raw];
|
|
|
|
|
parent.raw[key][index] = newVal;
|
|
|
|
|
magic.raw = parent.raw[key];
|
|
|
|
|
prop = magify({ raw: newVal, key: index, parent: magic, onDirty });
|
|
|
|
|
},
|
|
|
|
|
get() {
|
|
|
|
|
return prop;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
raw.forEach((value, index) => {
|
|
|
|
|
let prop = magify({
|
|
|
|
|
raw: value,
|
|
|
|
|
key: index,
|
|
|
|
|
parent: magic,
|
|
|
|
|
onDirty
|
|
|
|
|
});
|
|
|
|
|
Object.defineProperty(magic, index, {
|
|
|
|
|
set(newVal) {
|
|
|
|
|
onDirty();
|
|
|
|
|
parent.raw[key] = [...magic.raw];
|
|
|
|
|
parent.raw[key][index] = newVal;
|
|
|
|
|
magic.raw = parent.raw[key];
|
|
|
|
|
prop = magify({ raw: newVal, key: index, parent: magic, onDirty });
|
|
|
|
|
},
|
|
|
|
|
get() {
|
|
|
|
|
return prop;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function magify({ raw, key, parent, onDirty }) {
|
|
|
|
|
if (!parent.magic) {
|
|
|
|
|
parent = {
|
|
|
|
|
raw: parent,
|
|
|
|
|
magic: true,
|
|
|
|
|
parent: null
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if (raw.magic) {
|
|
|
|
|
return raw;
|
|
|
|
|
}
|
|
|
|
|
if (typeof raw !== "object") {
|
|
|
|
|
return raw;
|
|
|
|
|
}
|
|
|
|
|
let magic = { raw, key, parent, magic: true };
|
|
|
|
|
if (Array.isArray(raw)) {
|
|
|
|
|
_magifyArray({ raw, key, parent, magic, onDirty });
|
|
|
|
|
} else {
|
|
|
|
|
Object.keys(raw).forEach(propKey => {
|
|
|
|
|
let prop = magify({
|
|
|
|
|
raw: raw[propKey],
|
|
|
|
|
key: propKey,
|
|
|
|
|
parent: magic,
|
|
|
|
|
onDirty
|
|
|
|
|
});
|
|
|
|
|
Object.defineProperty(magic, propKey, {
|
|
|
|
|
set(newVal) {
|
|
|
|
|
onDirty();
|
|
|
|
|
parent.raw[key] = { ...magic.raw, [propKey]: newVal };
|
|
|
|
|
magic.raw = parent.raw[key];
|
|
|
|
|
prop = magify({ raw: newVal, key: propKey, parent: magic, onDirty });
|
|
|
|
|
},
|
|
|
|
|
get() {
|
|
|
|
|
return prop;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return magic;
|
|
|
|
|
}
|