@statewalker/fsm-charts 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1 -0
  4. package/dist/StateChart-DNGQd1XG.d.ts +53 -0
  5. package/dist/chunk-3GGM3ZWL.js +2685 -0
  6. package/dist/chunk-NXWOFQMO.js +10 -0
  7. package/dist/chunk-TPNWHU7Y.js +437 -0
  8. package/dist/index-layout.d.ts +96 -0
  9. package/dist/index-layout.js +2 -0
  10. package/dist/index-svg.d.ts +25 -0
  11. package/dist/index-svg.js +2 -0
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.js +3 -0
  14. package/package.json +60 -0
  15. package/src/FsmStateConfig.ts +5 -0
  16. package/src/dagre/acyclic.d.ts +2 -0
  17. package/src/dagre/acyclic.js +60 -0
  18. package/src/dagre/add-border-segments.d.ts +1 -0
  19. package/src/dagre/add-border-segments.js +36 -0
  20. package/src/dagre/coordinate-system.d.ts +2 -0
  21. package/src/dagre/coordinate-system.js +75 -0
  22. package/src/dagre/data/list.d.ts +12 -0
  23. package/src/dagre/data/list.js +55 -0
  24. package/src/dagre/debug.d.ts +2 -0
  25. package/src/dagre/debug.js +32 -0
  26. package/src/dagre/greedy-fas.d.ts +1 -0
  27. package/src/dagre/greedy-fas.js +126 -0
  28. package/src/dagre/index.d.ts +5 -0
  29. package/src/dagre/index.js +6 -0
  30. package/src/dagre/layout.d.ts +1 -0
  31. package/src/dagre/layout.js +466 -0
  32. package/src/dagre/nesting-graph.d.ts +2 -0
  33. package/src/dagre/nesting-graph.js +137 -0
  34. package/src/dagre/normalize.d.ts +2 -0
  35. package/src/dagre/normalize.js +91 -0
  36. package/src/dagre/order/add-subgraph-constraints.d.ts +1 -0
  37. package/src/dagre/order/add-subgraph-constraints.js +53 -0
  38. package/src/dagre/order/barycenter.d.ts +15 -0
  39. package/src/dagre/order/barycenter.js +31 -0
  40. package/src/dagre/order/build-layer-graph.d.ts +2 -0
  41. package/src/dagre/order/build-layer-graph.js +76 -0
  42. package/src/dagre/order/cross-count.d.ts +1 -0
  43. package/src/dagre/order/cross-count.js +82 -0
  44. package/src/dagre/order/index.d.ts +1 -0
  45. package/src/dagre/order/index.js +77 -0
  46. package/src/dagre/order/init-order.d.ts +1 -0
  47. package/src/dagre/order/init-order.js +44 -0
  48. package/src/dagre/order/resolve-conflicts.d.ts +1 -0
  49. package/src/dagre/order/resolve-conflicts.js +128 -0
  50. package/src/dagre/order/sort-subgraph.d.ts +8 -0
  51. package/src/dagre/order/sort-subgraph.js +78 -0
  52. package/src/dagre/order/sort.d.ts +6 -0
  53. package/src/dagre/order/sort.js +59 -0
  54. package/src/dagre/parent-dummy-chains.d.ts +1 -0
  55. package/src/dagre/parent-dummy-chains.js +86 -0
  56. package/src/dagre/position/bk.d.ts +31 -0
  57. package/src/dagre/position/bk.js +423 -0
  58. package/src/dagre/position/index.d.ts +1 -0
  59. package/src/dagre/position/index.js +31 -0
  60. package/src/dagre/rank/feasible-tree.d.ts +2 -0
  61. package/src/dagre/rank/feasible-tree.js +87 -0
  62. package/src/dagre/rank/index.d.ts +1 -0
  63. package/src/dagre/rank/index.js +52 -0
  64. package/src/dagre/rank/network-simplex.d.ts +16 -0
  65. package/src/dagre/rank/network-simplex.js +236 -0
  66. package/src/dagre/rank/util.d.ts +2 -0
  67. package/src/dagre/rank/util.js +63 -0
  68. package/src/dagre/util.d.ts +27 -0
  69. package/src/dagre/util.js +250 -0
  70. package/src/graphlib/alg/components.d.ts +1 -0
  71. package/src/graphlib/alg/components.js +27 -0
  72. package/src/graphlib/alg/dfs.d.ts +1 -0
  73. package/src/graphlib/alg/dfs.js +46 -0
  74. package/src/graphlib/alg/dijkstra-all.d.ts +1 -0
  75. package/src/graphlib/alg/dijkstra-all.js +14 -0
  76. package/src/graphlib/alg/dijkstra.d.ts +1 -0
  77. package/src/graphlib/alg/dijkstra.js +65 -0
  78. package/src/graphlib/alg/find-cycles.d.ts +1 -0
  79. package/src/graphlib/alg/find-cycles.js +10 -0
  80. package/src/graphlib/alg/floyd-warshall.d.ts +1 -0
  81. package/src/graphlib/alg/floyd-warshall.js +55 -0
  82. package/src/graphlib/alg/index.d.ts +24 -0
  83. package/src/graphlib/alg/index.js +25 -0
  84. package/src/graphlib/alg/is-acyclic.d.ts +1 -0
  85. package/src/graphlib/alg/is-acyclic.js +15 -0
  86. package/src/graphlib/alg/postorder.d.ts +1 -0
  87. package/src/graphlib/alg/postorder.js +7 -0
  88. package/src/graphlib/alg/preorder.d.ts +1 -0
  89. package/src/graphlib/alg/preorder.js +7 -0
  90. package/src/graphlib/alg/prim.d.ts +2 -0
  91. package/src/graphlib/alg/prim.js +52 -0
  92. package/src/graphlib/alg/tarjan.d.ts +1 -0
  93. package/src/graphlib/alg/tarjan.js +47 -0
  94. package/src/graphlib/alg/topsort.d.ts +5 -0
  95. package/src/graphlib/alg/topsort.js +36 -0
  96. package/src/graphlib/data/priority-queue.d.ts +59 -0
  97. package/src/graphlib/data/priority-queue.js +152 -0
  98. package/src/graphlib/graph.d.ts +55 -0
  99. package/src/graphlib/graph.js +510 -0
  100. package/src/graphlib/index.d.ts +3 -0
  101. package/src/graphlib/index.js +7 -0
  102. package/src/graphlib/json.d.ts +16 -0
  103. package/src/graphlib/json.js +63 -0
  104. package/src/index-layout.ts +1 -0
  105. package/src/index-svg.ts +1 -0
  106. package/src/index.ts +2 -0
  107. package/src/layout/Dimensions.ts +8 -0
  108. package/src/layout/GraphParamsProvider.ts +17 -0
  109. package/src/layout/Padding.ts +10 -0
  110. package/src/layout/Position.ts +4 -0
  111. package/src/layout/StateChart.ts +8 -0
  112. package/src/layout/StateGraphEdge.ts +14 -0
  113. package/src/layout/StateGraphNode.ts +12 -0
  114. package/src/layout/Transition.ts +5 -0
  115. package/src/layout/TransitionsGraph.ts +13 -0
  116. package/src/layout/buildCharts.ts +74 -0
  117. package/src/layout/buildFlatCharts.ts +142 -0
  118. package/src/layout/getGraphParamsProvider.ts +63 -0
  119. package/src/layout/getLabelDimensions.ts +59 -0
  120. package/src/layout/index.ts +15 -0
  121. package/src/lodash-es/_deleteme.js +291 -0
  122. package/src/lodash-es/index.ts +9 -0
  123. package/src/lodash-es/types.ts +35 -0
  124. package/src/svg/buildStatechartCss.ts +120 -0
  125. package/src/svg/buildStatechartSvg.ts +256 -0
  126. package/src/svg/buildStatechartsPanel.ts +38 -0
  127. package/src/svg/index.ts +3 -0
  128. package/src/utils/BasisCurve.ts +89 -0
  129. package/src/utils/getPadding.ts +13 -0
  130. package/src/utils/serializeAttrs.ts +28 -0
  131. package/src/utils/serializeStyle.ts +11 -0
  132. package/src/utils/toKebabCase.ts +18 -0
@@ -0,0 +1,8 @@
1
+ import type { TransitionsGraph } from "./TransitionsGraph.ts";
2
+
3
+ export type StateChart = {
4
+ id: string;
5
+ key: string;
6
+ children?: StateChart[];
7
+ graph: TransitionsGraph;
8
+ } & Record<string, any>;
@@ -0,0 +1,14 @@
1
+ import { Dimensions } from "./Dimensions.ts";
2
+ import { Position } from "./Position.ts";
3
+
4
+ /**
5
+ * Information about a state graph edge.
6
+ */
7
+ export type StateGraphEdge = Dimensions &
8
+ Position & {
9
+ id: string;
10
+ from: string;
11
+ event: string;
12
+ to: string;
13
+ points: Position[];
14
+ } & Record<string, any>;
@@ -0,0 +1,12 @@
1
+ import { Dimensions } from "./Dimensions";
2
+ import { Position } from "./Position";
3
+
4
+ /**
5
+ * Information about a state node in the graph.
6
+ */
7
+ export type StateGraphNode = Dimensions &
8
+ Position & {
9
+ id: string;
10
+ key: string;
11
+ state: string;
12
+ } & Record<string, any>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * A transition from one state to another.
3
+ * Contains the state before the transition, the event that triggers the transition, and the state after the transition.
4
+ */
5
+ export type Transition = [string, string, string];
@@ -0,0 +1,13 @@
1
+ import { Dimensions } from "./Dimensions.ts";
2
+ import { Position } from "./Position.ts";
3
+ import { StateGraphEdge } from "./StateGraphEdge.ts";
4
+ import { StateGraphNode } from "./StateGraphNode.ts";
5
+
6
+ /**
7
+ * Coordinates for state nodes and endges corresponding to state transitions.
8
+ */
9
+ export type TransitionsGraph = Position &
10
+ Dimensions & {
11
+ nodes: StateGraphNode[];
12
+ edges: StateGraphEdge[];
13
+ };
@@ -0,0 +1,74 @@
1
+ import type { GraphParamsProvider } from "./GraphParamsProvider.ts";
2
+ import type { StateGraphNode } from "./StateGraphNode.ts";
3
+ import type { StateGraphEdge } from "./StateGraphEdge.ts";
4
+ import type { Padding } from "./Padding.ts";
5
+ import type { Position } from "./Position.ts";
6
+ import type { Dimensions } from "./Dimensions.ts";
7
+ import type { FsmStateConfig } from "../FsmStateConfig.ts";
8
+ import type { StateChart } from "./StateChart.ts";
9
+
10
+ import { layout } from "../dagre/layout.js";
11
+ import { Graph } from "../graphlib/graph.js";
12
+ import { getPadding } from "../utils/getPadding.ts";
13
+ import { getLodash, setLodash } from "../lodash-es/index.ts";
14
+ import { buildFlatCharts } from "./buildFlatCharts.ts";
15
+ /**
16
+ * Build graphs corresponding to the specified transitions.
17
+ * @param config - State configuration with transitions and sub-states.
18
+ * @param newId - A function that generates unique ids for nodes and edges.
19
+ * @param getStateParams - A function that returns the dimensions of a state node.
20
+ * @param getTransitionParams - A function that returns the dimensions of a transition edge.
21
+ * @param initialStateKey - The key for the initial state.
22
+ * @param finalStateKey - The key for the final state.
23
+ * @returns {StateChart} - The graph with nodes and edges.
24
+ */
25
+ export function buildCharts({
26
+ lodash,
27
+ config,
28
+ newId,
29
+ getStateParams,
30
+ getTransitionParams,
31
+ padding = [5, 5],
32
+ initialStateKey = "<initial>",
33
+ finalStateKey = "<final>",
34
+ }: {
35
+ config: FsmStateConfig;
36
+ lodash: any;
37
+ newId: (prefix: string) => string;
38
+ initialStateKey?: string;
39
+ finalStateKey?: string;
40
+ padding?: Padding;
41
+ } & GraphParamsProvider): StateChart {
42
+ return buildChart(config);
43
+
44
+ function buildChart(config: FsmStateConfig, id: string = newId("state")) {
45
+ const graph = buildFlatCharts({
46
+ lodash,
47
+ transitions: config.transitions,
48
+ newId,
49
+ getStateParams,
50
+ getTransitionParams,
51
+ padding,
52
+ initialStateKey,
53
+ finalStateKey,
54
+ });
55
+ const index = graph.nodes.reduce((index, node) => {
56
+ index[node.key as string] = node.id as string;
57
+ return index;
58
+ }, {} as Record<string, string>);
59
+
60
+ const children: StateChart[] = [];
61
+ if (config.states) {
62
+ for (const child of config.states) {
63
+ const childId = index[child.key];
64
+ children.push(buildChart(child, childId));
65
+ }
66
+ }
67
+ return {
68
+ id,
69
+ key: config.key,
70
+ graph,
71
+ children,
72
+ };
73
+ }
74
+ }
@@ -0,0 +1,142 @@
1
+ import { layout } from "../dagre/layout.js";
2
+ import { Graph } from "../graphlib/graph.js";
3
+
4
+ import type { GraphParamsProvider } from "./GraphParamsProvider.ts";
5
+ import type { Transition } from "./Transition.ts";
6
+ import type { TransitionsGraph } from "./TransitionsGraph.ts";
7
+ import type { StateGraphNode } from "./StateGraphNode.ts";
8
+ import type { StateGraphEdge } from "./StateGraphEdge.ts";
9
+ import { Padding } from "./Padding.ts";
10
+ import { getPadding } from "../utils/getPadding.ts";
11
+ import { Position } from "./Position.ts";
12
+ import { Dimensions } from "./Dimensions.ts";
13
+ import { getLodash, setLodash } from "../lodash-es/index.ts";
14
+
15
+ /**
16
+ * Build graphs corresponding to the specified transitions.
17
+ * @param transitions - The transitions between states.
18
+ * @param newId - A function that generates unique ids for nodes and edges.
19
+ * @param getStateParams - A function that returns the dimensions of a state node.
20
+ * @param getTransitionParams - A function that returns the dimensions of a transition edge.
21
+ * @param initialStateKey - The key for the initial state.
22
+ * @param finalStateKey - The key for the final state.
23
+ * @param vertical - Whether the graph should be vertical.
24
+ * @returns {TransitionsGraph} - The graph with nodes and edges.
25
+ */
26
+
27
+ export function buildFlatCharts({
28
+ lodash,
29
+ transitions,
30
+ newId,
31
+ getStateParams,
32
+ getTransitionParams,
33
+ padding = [5, 5],
34
+ initialStateKey = "<initial>",
35
+ finalStateKey = "<final>",
36
+ vertical = false,
37
+ }: {
38
+ transitions: Transition[];
39
+ lodash: any;
40
+ newId: (prefix: string) => string;
41
+ initialStateKey?: string;
42
+ finalStateKey?: string;
43
+ vertical?: boolean;
44
+ padding?: Padding;
45
+ } & GraphParamsProvider): TransitionsGraph {
46
+ let prevLodash = getLodash();
47
+ try {
48
+ setLodash(lodash);
49
+ const graph = new Graph({
50
+ directed: true,
51
+ })
52
+ .setGraph({ rankdir: vertical ? "tb" : "lr" })
53
+ .setDefaultEdgeLabel(() => ({}));
54
+
55
+ const nodesIndex: Record<string, string> = {};
56
+
57
+ const getStateNodeId = (key: string, state: string = key) => {
58
+ let id = nodesIndex[key];
59
+ if (!id) {
60
+ id = nodesIndex[key] = newId("s");
61
+ const params = getStateParams(key, state);
62
+ graph.setNode(id, { id, ...params });
63
+ }
64
+ return id;
65
+ };
66
+
67
+ for (const [fromState, event, toState] of transitions) {
68
+ const fromStateKey = fromState || initialStateKey;
69
+ const from = getStateNodeId(fromStateKey, fromState);
70
+ const toStateKey = toState || finalStateKey;
71
+ const to = getStateNodeId(toStateKey, toState);
72
+ const params = getTransitionParams(fromStateKey, event, toStateKey);
73
+ graph.setEdge(from, to, {
74
+ id: newId("t"),
75
+ ...params,
76
+ });
77
+ }
78
+ layout(graph, { rankdir: vertical ? "tb" : "lr" });
79
+
80
+ const [top, right, bottom, left] = getPadding(padding);
81
+
82
+ const nodes = graph.nodes().map((id: string) => {
83
+ const n = graph.node(id) as StateGraphNode;
84
+ n.x += -n.width / 2;
85
+ n.y += -n.height / 2;
86
+ return n;
87
+ });
88
+ const edges = graph
89
+ .edges()
90
+ .map(({ v, w, name }: { v: string; w: string; name: string }) => {
91
+ const e = graph.edge(v, w, name) as StateGraphEdge;
92
+ e.x += -e.width / 2;
93
+ e.y += -e.height / 2;
94
+ return e;
95
+ });
96
+
97
+ // Get the bounding box of the graph
98
+ let x = 0;
99
+ let y = 0;
100
+ let width = 0;
101
+ let height = 0;
102
+ const addCoords = (node: StateGraphNode | StateGraphEdge) => {
103
+ x = Math.min(x, node.x);
104
+ y = Math.min(y, node.y);
105
+ width = Math.max(width, node.x + node.width);
106
+ height = Math.max(height, node.y + node.height);
107
+ };
108
+ nodes.forEach(addCoords);
109
+ edges.forEach(addCoords);
110
+ edges.forEach((edge) => {
111
+ for (const point of edge.points) {
112
+ x = Math.min(x, edge.x);
113
+ y = Math.min(y, edge.y);
114
+ width = Math.max(width, point.x);
115
+ height = Math.max(height, point.y);
116
+ }
117
+ });
118
+
119
+ // Shift the graph to the origin taking into account the padding
120
+ // Invert coordinates.
121
+ width += left + right;
122
+ height += bottom + top;
123
+ const updateCoords = (node: Position) => {
124
+ node.x = Math.round(node.x + left);
125
+ node.y = Math.round(node.y + bottom);
126
+ // node.y = node.y + bottom;
127
+ };
128
+ const updateBox = (node: Position & Dimensions) => {
129
+ updateCoords(node);
130
+ node.y = Math.round(node.y + node.height);
131
+ };
132
+ nodes.forEach(updateBox);
133
+ edges.forEach(updateBox);
134
+ edges.forEach((edge) => {
135
+ edge.points.forEach(updateCoords);
136
+ });
137
+
138
+ return { x, y, width, height, nodes, edges };
139
+ } finally {
140
+ setLodash(prevLodash);
141
+ }
142
+ }
@@ -0,0 +1,63 @@
1
+ import type { Dimensions } from "./Dimensions.ts";
2
+ import { getLabelDimensions } from "./getLabelDimensions.ts";
3
+ import { getPadding } from "../utils/getPadding.ts";
4
+ import type { GraphParamsProvider } from "./GraphParamsProvider.ts";
5
+ import type { Padding } from "./Padding.ts";
6
+
7
+ /**
8
+ * Provides default functions measuring dimensions for state nodes and transition edges in the graph.
9
+ * @param fontSize - The font size for the state and transition labels.
10
+ * @param stateFontSize - The font size for the state labels.
11
+ * @param stateTextPadding - The padding around the state labels.
12
+ * @param transitionsFontSize - The font size for the transition labels.
13
+ * @param transitionsTextPadding - The padding around the transition labels.
14
+ */
15
+ export function getGraphParamsProvider({
16
+ fontSize = 14,
17
+ stateFontSize = fontSize,
18
+ stateTextPadding = 0,
19
+ transitionsFontSize = fontSize,
20
+ transitionsTextPadding = 0,
21
+ }: {
22
+ fontSize?: number;
23
+ stateFontSize?: number;
24
+ stateTextPadding?: Padding;
25
+ transitionsFontSize?: number;
26
+ transitionsTextPadding?: Padding;
27
+ } = {}): GraphParamsProvider {
28
+ return {
29
+ getStateParams: (stateKey: string, state: string = stateKey) => {
30
+ const padding = getPadding(stateTextPadding);
31
+ const fontSize = stateFontSize;
32
+ return {
33
+ ...getLabelDimensions(state, {
34
+ padding,
35
+ fontSize,
36
+ }),
37
+ state,
38
+ key: stateKey,
39
+ padding,
40
+ fontSize,
41
+ };
42
+ },
43
+ getTransitionParams: (
44
+ fromStateKey: string,
45
+ event: string,
46
+ toStateKey: string
47
+ ): Dimensions & Record<string, any> => {
48
+ const padding = getPadding(transitionsTextPadding);
49
+ const fontSize = transitionsFontSize;
50
+ return {
51
+ ...getLabelDimensions(event, {
52
+ padding,
53
+ fontSize,
54
+ }),
55
+ from: fromStateKey,
56
+ event,
57
+ to: toStateKey,
58
+ padding,
59
+ fontSize,
60
+ };
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,59 @@
1
+ import type { Dimensions } from "./Dimensions.ts";
2
+ import type { Padding } from "./Padding.ts";
3
+ import { getPadding } from "../utils/getPadding.ts";
4
+
5
+ export function getLabelDimensions(
6
+ label: string,
7
+ {
8
+ padding = 0,
9
+ fontSize = 14,
10
+ getCharWidth = () => fontSize / 1.61, // Golden ratio and all that jazz :-)
11
+ lineGap = fontSize * 0.2,
12
+ }: {
13
+ padding?: Padding;
14
+ fontSize?: number;
15
+ getCharWidth?: (char: string) => number;
16
+ lineGap?: number;
17
+ } = {}
18
+ ) {
19
+ const lines = [label]; // label.split("\n");
20
+ let dimensions: Dimensions = { width: 0, height: 0 };
21
+ let lineNumber = 0;
22
+ for (const line of lines) {
23
+ if (lineNumber++) dimensions.height += lineGap;
24
+ dimensions = expandBox(
25
+ dimensions,
26
+ getLineDimensions({
27
+ line,
28
+ fontSize,
29
+ getCharWidth,
30
+ })
31
+ );
32
+ }
33
+ const [top, right, bottom, left] = getPadding(padding);
34
+ return {
35
+ width: Math.round(dimensions.width + left + right),
36
+ height: Math.round(dimensions.height + top + bottom),
37
+ };
38
+ function expandBox(dim1: Dimensions, dim2: Dimensions) {
39
+ return {
40
+ width: Math.round(Math.max(dim1.width, dim2.width)),
41
+ height: Math.round(dim1.height + dim2.height),
42
+ };
43
+ }
44
+
45
+ function getLineDimensions({
46
+ line,
47
+ fontSize = 14,
48
+ getCharWidth = () => fontSize,
49
+ }: {
50
+ line: string;
51
+ fontSize?: number;
52
+ getCharWidth?: (char: string[1]) => number;
53
+ }) {
54
+ return {
55
+ width: line.split("").reduce((w, c) => (w += getCharWidth(c)), 0),
56
+ height: fontSize,
57
+ };
58
+ }
59
+ }
@@ -0,0 +1,15 @@
1
+ // Methods
2
+ export * from "./buildFlatCharts.ts";
3
+ export * from "./buildCharts.ts";
4
+ export * from "./getGraphParamsProvider.ts";
5
+ export * from "./getLabelDimensions.ts";
6
+
7
+ // Types
8
+ export * from "./Dimensions.ts";
9
+ export * from "./GraphParamsProvider.ts";
10
+ export * from "./Padding.ts";
11
+ export * from "./Position.ts";
12
+ export * from "./StateGraphEdge.ts";
13
+ export * from "./StateGraphNode.ts";
14
+ export * from "./Transition.ts";
15
+ export * from "./TransitionsGraph.ts";