finitestate
Version:
1. All actions are functions which have a $$displayName property. 2. All states are strings 3. Json representation for SSR
94 lines (87 loc) • 2.97 kB
JavaScript
import has from "lodash/has";
const _state = Symbol();
const _value = Symbol();
const _actions = Symbol();
export default class FSM {
__control__ = {
[_state]: undefined,
[_value]: undefined,
[_actions]: {}
};
constructor({ startState, startValue, transitions, states, transitionMap }) {
// grab options
this.states = states;
this.startState = startState;
this.startValue = Object.freeze(startValue);
this.transitions = transitions;
this.transitionMap = transitionMap;
// set private fields
this.__control__[_state] = this.startState;
this.__control__[_value] = Object.freeze(this.startValue);
// create corresponding actions for transitions
const actions = {};
this.transitions.forEach(transition => {
actions[transition] = (...args) => {
//if the current state has a valid transition in the transitionMap
if (this.isTransitionValid(transition)) {
const newState = this.transitionMap[this.__control__[_state]][
transition
];
let newValue = this.__control__[_value];
const original = this[transition];
if (typeof original === "function") {
const transformationFn = original(...args);
if (typeof transformationFn === "function") {
newValue = transformationFn(this.__control__[_value]);
}
} else {
throw Error(
`Found transition of type ${typeof transition}. Transition ${transition} should be a function which returns a transformation function.`
);
}
const oldProps = {
state: this.__control__[_state],
value: this.__control__[_value]
};
const newProps = {
state: newState,
value: newValue
};
if (this.shouldTransitionOccur(oldProps, newProps, transition)) {
this.transitionWillOccur(oldProps, newProps, transition);
this.__control__[_value] = Object.freeze(newValue);
this.__control__[_state] = newState;
this.transitionDidOccur(oldProps, newProps, transition);
} else {
console.debug(`transition was not performed`);
}
return this.__control__[_value];
} else {
console.warn(
`Transition ${transition} cannot be performed right now`
);
return this.__control__[_value];
}
};
});
// freeze actions
this.__control__[_actions] = Object.freeze(actions);
}
isTransitionValid(transition) {
return has(this.transitionMap[this.__control__[_state]], transition);
}
shouldTransitionOccur() {
return true;
}
transitionWillOccur() {}
transitionDidOccur() {}
get value() {
return this.__control__[_value];
}
get state() {
return this.__control__[_state];
}
get actions() {
return this.__control__[_actions];
}
}