Initial Commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
// saladui/core/state-machine.js
|
||||
/**
|
||||
* StateMachine class for SaladUI framework
|
||||
* Handles state transitions, event processing, and state-specific behavior
|
||||
*/
|
||||
class StateMachine {
|
||||
/**
|
||||
* Create a state machine
|
||||
*
|
||||
* @param {Object} stateConfig - Configuration object defining states and transitions
|
||||
* @param {string} initialState - The initial state to start in
|
||||
* @param {Object} options - Optional configuration options. Currently supports:
|
||||
* - onStateChanged: A callback function to be called when the state changes
|
||||
*/
|
||||
constructor(stateConfig, initialState, options) {
|
||||
this.stateConfig = stateConfig;
|
||||
this.state = initialState || "idle";
|
||||
this.previousState = null;
|
||||
this.options = options || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a transition based on an event
|
||||
*
|
||||
* @param {string} event - The event triggering the transition
|
||||
* @param {Object} params - Parameters to pass to the handlers
|
||||
* @returns {boolean} Whether the transition was successful
|
||||
*/
|
||||
transition(event, params = {}) {
|
||||
const currentStateConfig = this.stateConfig[this.state];
|
||||
if (!currentStateConfig) return false;
|
||||
|
||||
const transition = currentStateConfig.transitions?.[event];
|
||||
if (!transition) return false;
|
||||
|
||||
const nextState = this.determineNextState(transition, params);
|
||||
if (!nextState) return false;
|
||||
|
||||
const prevState = this.state;
|
||||
|
||||
this.executeTransition(prevState, nextState, params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the next state based on the transition definition
|
||||
*
|
||||
* @param {string|Function|Object} transition - Transition definition
|
||||
* @param {Object} params - Parameters to help determine the next state
|
||||
* @returns {string|null} The next state or null if not determinable
|
||||
*/
|
||||
determineNextState(transition, params) {
|
||||
if (typeof transition === "string") {
|
||||
return transition;
|
||||
} else if (typeof transition === "function") {
|
||||
return transition(params);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a transition between states, with optional animation
|
||||
*
|
||||
* @param {string} prevState - The state we're coming from
|
||||
* @param {string} nextState - The state we're going to
|
||||
* @param {Object} params - Parameters to pass to handlers
|
||||
*/
|
||||
executeTransition(prevState, nextState, params = {}) {
|
||||
// Execute exit handlers
|
||||
this.executeStateHandler(prevState, "exit", params);
|
||||
|
||||
// Update state
|
||||
this.previousState = prevState;
|
||||
this.state = nextState;
|
||||
|
||||
let callbackResult;
|
||||
// Execute state change hook
|
||||
if (typeof this.options.onStateChanged === "function") {
|
||||
callbackResult = this.options.onStateChanged(
|
||||
prevState,
|
||||
nextState,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
if (callbackResult && typeof callbackResult.then === "function") {
|
||||
// If it returns a promise, wait for completion before executing enter handler
|
||||
callbackResult
|
||||
.then(() => {
|
||||
this.executeStateHandler(nextState, "enter", params);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Animation promise rejected:", error);
|
||||
// Still execute enter handler even if animation fails
|
||||
this.executeStateHandler(nextState, "enter", params);
|
||||
});
|
||||
} else {
|
||||
// If it doesn't return a promise, execute enter handler immediately
|
||||
this.executeStateHandler(nextState, "enter", params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a state handler (enter or exit)
|
||||
*
|
||||
* @param {string} stateName - The state whose handler to execute
|
||||
* @param {string} handlerType - 'enter' or 'exit'
|
||||
* @param {Object} params - Parameters to pass to the handler
|
||||
*/
|
||||
executeStateHandler(stateName, handlerType, params) {
|
||||
const stateConfig = this.stateConfig[stateName];
|
||||
if (!stateConfig) return;
|
||||
|
||||
const handler = stateConfig[handlerType];
|
||||
|
||||
if (typeof handler === "function") {
|
||||
handler(params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the state has changed since last transition
|
||||
*
|
||||
* @returns {boolean} Whether the state has changed
|
||||
*/
|
||||
hasStateChanged() {
|
||||
return this.state !== this.previousState;
|
||||
}
|
||||
}
|
||||
|
||||
export default StateMachine;
|
||||
Reference in New Issue
Block a user