|
import { interpret, Machine } from "xstate"; |
|
import { Graph } from "./graph.js"; |
|
|
|
// basic machine: one state, invokes a service. |
|
const basicMachine = Machine({ |
|
id: "root", |
|
states: { |
|
"Search Bar": { |
|
id: "Search Bar", |
|
states: { |
|
Inactive: { |
|
id: "Inactive", |
|
states: {}, |
|
on: { |
|
focused: "#Active", |
|
}, |
|
}, |
|
Active: { |
|
id: "Active", |
|
states: { |
|
Empty: { id: "Empty", states: {} }, |
|
"Text Entry": { |
|
id: "Text Entry", |
|
states: {}, |
|
on: { submitted: "#Results" }, |
|
}, |
|
Results: { id: "Results", states: {} }, |
|
}, |
|
initial: "Empty", |
|
on: { canceled: "#Inactive", typed: "#Text Entry" }, |
|
}, |
|
}, |
|
initial: "Inactive", |
|
on: {}, |
|
}, |
|
}, |
|
initial: "Search Bar", |
|
on: {}, |
|
}); |
|
|
|
const graph = new Graph({ |
|
nodeFormatter(node, config) { |
|
if (config) { |
|
return `"${node}"[${config}]`; |
|
} |
|
}, |
|
edgeSimplifier(start, label, end) { |
|
return [start, label, end]; |
|
}, |
|
edgeFormatter(a, b, c) { |
|
if (b) { |
|
return `"${a}" -> "${c}" [label="${b}"]`; |
|
} else { |
|
return `"${a}" -> "${c}"`; |
|
} |
|
}, |
|
makeGraphContext({ nodes, edges }) { |
|
const nodeBody = nodes.length > 0 ? `${nodes.join(";\n ")};` : ""; |
|
return `digraph { |
|
${nodeBody} |
|
${edges.join(";\n ")}; |
|
}`; |
|
}, |
|
}) |
|
.defineNode("<start>", "shape=box,color=green") |
|
.defineNode("<stop>", "shape=box,color=red"); |
|
|
|
export const service = interpret(basicMachine); |
|
|
|
let transitions = 0; |
|
service.onTransition((state, e) => { |
|
transitions++; |
|
const { length: l1, [l1 - 1]: target } = state.toStrings() ?? ["<start>"]; |
|
const { length: l2, [l2 - 1]: source } = state.history?.toStrings?.() ?? [ |
|
"<start>", |
|
]; |
|
graph.defineNode(e.type, "shape=diamond,rank=0"); |
|
graph.recordEdge(source, e.type, target); |
|
}); |
|
|
|
service.start(); |
|
|
|
service.send("focused"); |
|
service.send("typed"); |
|
service.send("typed"); |
|
service.send("canceled"); |
|
|
|
service.stop(); |
|
transitions++; |
|
const finalState = service.state.toStrings(); |
|
graph.recordEdge(finalState[finalState.length - 1], `${transitions}`, "<stop>"); |
|
|
|
console.log(graph.dump()); |