UNPKG

@koordinates/xstate-tree

Version:

Build UIs with Actors using xstate and React

123 lines (122 loc) 4.42 kB
"use strict"; 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;