@koordinates/xstate-tree
Version:
Build UIs with Actors using xstate and React
123 lines (122 loc) • 4.42 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildRoutingMachine = exports.viewToMachine = exports.createXStateTreeMachine = void 0;
const react_1 = __importDefault(require("react"));
const xstate_1 = require("xstate");
const slots_1 = require("./slots");
/**
* @public
* Creates an xstate-tree machine from an xstate-machine
*
* Accepts an options object defining the selectors/actions/slots and view for the xstate-tree machine
*
* Selectors/slots/actions can be omitted from the options object and will default to
* - actions: an empty object
* - selectors: the context of the machine
* - slots: an empty array
*
* @param machine - The xstate machine to create the xstate-tree machine from
* @param options - the xstate-tree options
*/
function createXStateTreeMachine(machine, options) {
const selectors = options.selectors ?? (({ ctx }) => ctx);
const actions = options.actions ?? (() => ({}));
const machineWithMeta = machine;
machineWithMeta._xstateTree = {
selectors: selectors,
actions: actions,
View: options.View,
slots: (options.slots ?? []),
};
return fixProvideLosingXstateTreeMeta(machineWithMeta);
}
exports.createXStateTreeMachine = createXStateTreeMachine;
function fixProvideLosingXstateTreeMeta(machine) {
const originalProvide = machine.provide.bind(machine);
machine.provide = (impl) => {
const result = originalProvide(impl);
result._xstateTree = machine._xstateTree;
fixProvideLosingXstateTreeMeta(result);
return result;
};
return machine;
}
/**
* @public
*
* Simple utility builder to aid in integrating existing React views with xstate-tree
*
* @param view - the React view you want to invoke in an xstate machine
* @returns The view wrapped into an xstate-tree machine, ready to be invoked by other xstate machines or used with `buildRootComponent`
*/
function viewToMachine(view) {
return createXStateTreeMachine((0, xstate_1.createMachine)({
initial: "idle",
states: { idle: {} },
}), {
View: view,
});
}
exports.viewToMachine = viewToMachine;
/**
* @public
*
* Utility to aid in reducing boilerplate of mapping route events to xstate-tree machines
*
* Takes a list of routes and a mapping of route events to xstate-tree machines and returns an xstate-tree machine
* that renders the machines based on the routing events
*
* @param _routes - the array of routes you wish to map to machines
* @param mappings - an object mapping the route events to the machine to invoke
* @returns an xstate-tree machine that will render the right machines based on the routing events
*/
function buildRoutingMachine(_routes, mappings) {
/**
* States in xstate can't contain dots, since the states are named after the routing events
* if the routing event contains a dot that will make a state with a dot in it
* this function sanitizes the event name to remove dots and is used for the state names and targets
*/
function sanitizeEventName(event) {
return event.replace(/\.([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
}
const contentSlot = (0, slots_1.singleSlot)("Content");
const mappingsToStates = Object.entries(mappings).reduce((acc, [event, _machine]) => {
return {
...acc,
[sanitizeEventName(event)]: {
invoke: {
src: event,
id: contentSlot.getId(),
},
},
};
}, {});
const mappingsToEvents = Object.keys(mappings).reduce((acc, event) => ({
...acc,
[event]: {
target: `.${sanitizeEventName(event)}`,
},
}), {});
const machine = (0, xstate_1.setup)({
actors: mappings,
}).createMachine({
on: {
...mappingsToEvents,
},
initial: "idle",
states: {
idle: {},
...mappingsToStates,
},
});
return createXStateTreeMachine(machine, {
slots: [contentSlot],
View: ({ slots }) => {
return react_1.default.createElement(slots.Content, null);
},
});
}
exports.buildRoutingMachine = buildRoutingMachine;