@statewalker/fsm-charts 0.1.6 → 0.1.7
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.
- package/dist/{StateChart-PdtJDEPy.d.ts → StateChart-4_Yew7nT.d.ts} +1 -1
- package/dist/chunk-SHSJVXEB.js +144 -0
- package/dist/index-config.d.ts +12 -0
- package/dist/index-config.js +2 -0
- package/dist/{index-html-D4UI0s_n.d.ts → index-html-C3JXXoPE.d.ts} +1 -1
- package/dist/index-html.d.ts +2 -2
- package/dist/index-layout.d.ts +2 -2
- package/dist/index-runtime.d.ts +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/src/config/concatTransitionsToString.ts +21 -0
- package/src/config/index.ts +4 -0
- package/src/config/serializeProcessConfig.ts +47 -0
- package/src/config/splitTransitionString.ts +24 -0
- package/src/config/toFsmStateConfig.ts +57 -0
- package/src/full/index.ts +1 -0
- package/src/full/toFsmStateCharts.ts +118 -0
- package/src/index-config.ts +2 -0
- package/src/index.ts +1 -0
- package/src/types/FsmStateConfig.ts +10 -1
- package/src/utils/domToText.ts +27 -0
- package/src/utils/newId.ts +5 -0
- package/src/utils/newRegistry.ts +33 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// src/config/concatTransitionsToString.ts
|
|
2
|
+
function concatTransitionsToString(transitions) {
|
|
3
|
+
let list = [];
|
|
4
|
+
let str = "";
|
|
5
|
+
let prev;
|
|
6
|
+
for (const [from, event, to] of transitions) {
|
|
7
|
+
if (prev !== from) {
|
|
8
|
+
if (str)
|
|
9
|
+
list.push(str);
|
|
10
|
+
str = from;
|
|
11
|
+
}
|
|
12
|
+
str += ` -${escape(event)}-> ${escape(to)}`;
|
|
13
|
+
prev = to;
|
|
14
|
+
}
|
|
15
|
+
if (str)
|
|
16
|
+
list.push(str);
|
|
17
|
+
return list;
|
|
18
|
+
function escape(str2) {
|
|
19
|
+
return str2;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/config/serializeProcessConfig.ts
|
|
24
|
+
function serializeProcessConfig(config) {
|
|
25
|
+
if (!config)
|
|
26
|
+
return "";
|
|
27
|
+
let lines = [];
|
|
28
|
+
visit(config, lines);
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
function visit(config2, lines2, shift = " ") {
|
|
31
|
+
lines2.push(`${shift}{`);
|
|
32
|
+
const { key, states, transitions, ...params } = config2;
|
|
33
|
+
lines2.push(`${shift} "key": ${JSON.stringify(key)}`);
|
|
34
|
+
if (transitions && transitions.length > 0) {
|
|
35
|
+
lines2[lines2.length - 1] += ",";
|
|
36
|
+
lines2.push(`${shift} "transitions": [`);
|
|
37
|
+
for (let i = 0; i < transitions.length; i++) {
|
|
38
|
+
if (i > 0) {
|
|
39
|
+
lines2[lines2.length - 1] += ",";
|
|
40
|
+
}
|
|
41
|
+
lines2.push(`${shift} ${JSON.stringify(transitions[i])}`);
|
|
42
|
+
}
|
|
43
|
+
lines2.push(`${shift} ]`);
|
|
44
|
+
}
|
|
45
|
+
if (states && states.length > 0) {
|
|
46
|
+
lines2[lines2.length - 1] += ",";
|
|
47
|
+
lines2.push(`${shift} "states": [`);
|
|
48
|
+
for (let i = 0; i < states.length; i++) {
|
|
49
|
+
if (i > 0) {
|
|
50
|
+
lines2[lines2.length - 1] += ",";
|
|
51
|
+
}
|
|
52
|
+
visit(states[i], lines2, shift + " ");
|
|
53
|
+
}
|
|
54
|
+
lines2.push(`${shift} ]`);
|
|
55
|
+
}
|
|
56
|
+
if (params && Object.keys(params).length > 0) {
|
|
57
|
+
lines2[lines2.length - 1] += ",";
|
|
58
|
+
const str = JSON.stringify(params, null, 2);
|
|
59
|
+
const paramsLines = str.split("\n");
|
|
60
|
+
paramsLines.shift();
|
|
61
|
+
paramsLines.pop();
|
|
62
|
+
lines2.push(...paramsLines.map((line) => shift + line));
|
|
63
|
+
}
|
|
64
|
+
lines2.push(`${shift}}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/config/splitTransitionString.ts
|
|
69
|
+
function splitTransitionString(str) {
|
|
70
|
+
let list = [];
|
|
71
|
+
let pos = 0;
|
|
72
|
+
const matches = Array.from(
|
|
73
|
+
str.matchAll(/-\s*("[^"]*?"|'[^']*?'|[^-]*?)\s*->/g)
|
|
74
|
+
);
|
|
75
|
+
for (const match of matches) {
|
|
76
|
+
list.push(str.substring(pos, match.index));
|
|
77
|
+
list.push(match[1]);
|
|
78
|
+
pos = match.index + match[0].length;
|
|
79
|
+
}
|
|
80
|
+
list.push(str.substring(pos));
|
|
81
|
+
list = list.map((str2) => str2.trim());
|
|
82
|
+
const transitions = [];
|
|
83
|
+
for (let i = 0; i < list.length - 2; ) {
|
|
84
|
+
const from = list[i++] || "";
|
|
85
|
+
const event = list[i++] || "";
|
|
86
|
+
const to = list[i] || "";
|
|
87
|
+
transitions.push([from, event, to]);
|
|
88
|
+
}
|
|
89
|
+
return transitions;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/config/toFsmStateConfig.ts
|
|
93
|
+
function toFsmStateConfig(json) {
|
|
94
|
+
if (!json || typeof json !== "object")
|
|
95
|
+
return;
|
|
96
|
+
const { key, transitions, states, ...options } = json;
|
|
97
|
+
if (typeof key !== "string")
|
|
98
|
+
return;
|
|
99
|
+
const stateTransitions = toFsmTransitions(transitions);
|
|
100
|
+
const substates = toArray(states).map(toFsmStateConfig).filter((v) => !!v);
|
|
101
|
+
const state = {
|
|
102
|
+
key,
|
|
103
|
+
transitions: stateTransitions
|
|
104
|
+
};
|
|
105
|
+
if (substates && substates.length > 0)
|
|
106
|
+
state.states = substates;
|
|
107
|
+
return Object.assign(state, options);
|
|
108
|
+
function toFsmTransitions(json2) {
|
|
109
|
+
const transitions2 = [];
|
|
110
|
+
const index = {};
|
|
111
|
+
const array = toArray(json2);
|
|
112
|
+
for (let line of array) {
|
|
113
|
+
const lineTransitions = typeof line === "string" ? splitTransitionString(line) : [toArray(line)];
|
|
114
|
+
for (const transition of lineTransitions.map(validateTransition)) {
|
|
115
|
+
if (!transition)
|
|
116
|
+
continue;
|
|
117
|
+
const key2 = transition.join("|");
|
|
118
|
+
if (key2 in index)
|
|
119
|
+
continue;
|
|
120
|
+
index[key2] = true;
|
|
121
|
+
transitions2.push(transition);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return transitions2;
|
|
125
|
+
}
|
|
126
|
+
function validateTransition(value) {
|
|
127
|
+
const array = toArray(value);
|
|
128
|
+
if (array.length !== 3)
|
|
129
|
+
return;
|
|
130
|
+
const [from, event, to] = array;
|
|
131
|
+
if (typeof from !== "string" || typeof event !== "string" || typeof to !== "string")
|
|
132
|
+
return;
|
|
133
|
+
return [from, event, to];
|
|
134
|
+
}
|
|
135
|
+
function toArray(value) {
|
|
136
|
+
if (value === void 0)
|
|
137
|
+
return [];
|
|
138
|
+
if (Array.isArray(value))
|
|
139
|
+
return value;
|
|
140
|
+
return [value];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { T as Transition, F as FsmStateConfig } from './StateChart-4_Yew7nT.js';
|
|
2
|
+
export { D as Dimensions, G as GraphParamsProvider, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, d as TransitionsGraph } from './StateChart-4_Yew7nT.js';
|
|
3
|
+
|
|
4
|
+
declare function concatTransitionsToString(transitions: Transition[]): string[];
|
|
5
|
+
|
|
6
|
+
declare function serializeProcessConfig(config: FsmStateConfig | undefined): string;
|
|
7
|
+
|
|
8
|
+
declare function splitTransitionString(str: string): Transition[];
|
|
9
|
+
|
|
10
|
+
declare function toFsmStateConfig(json: any): FsmStateConfig | undefined;
|
|
11
|
+
|
|
12
|
+
export { FsmStateConfig, Transition, concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig };
|
package/dist/index-html.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, T as Transition, d as TransitionsGraph } from './StateChart-
|
|
2
|
-
export { c as activeStatesStyle, d as activeTransitionsStyle, f as buildStateDetailsStyle, g as buildStateDetailsStyleWithModifier, b as buildStatechartCss, i as buildStatechartStyles, h as buildStatechartStylesWithModifier, j as buildStatechartSvg, a as buildStatechartsPanel, s as selectedStatesStyle, e as selectedTransitionsStyle } from './index-html-
|
|
1
|
+
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, T as Transition, d as TransitionsGraph } from './StateChart-4_Yew7nT.js';
|
|
2
|
+
export { c as activeStatesStyle, d as activeTransitionsStyle, f as buildStateDetailsStyle, g as buildStateDetailsStyleWithModifier, b as buildStatechartCss, i as buildStatechartStyles, h as buildStatechartStylesWithModifier, j as buildStatechartSvg, a as buildStatechartsPanel, s as selectedStatesStyle, e as selectedTransitionsStyle } from './index-html-C3JXXoPE.js';
|
package/dist/index-layout.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as Transition, P as Padding, G as GraphParamsProvider, d as TransitionsGraph, F as FsmStateConfig, S as StateChart } from './StateChart-
|
|
2
|
-
export { D as Dimensions, a as Position, b as StateGraphEdge, c as StateGraphNode } from './StateChart-
|
|
1
|
+
import { T as Transition, P as Padding, G as GraphParamsProvider, d as TransitionsGraph, F as FsmStateConfig, S as StateChart } from './StateChart-4_Yew7nT.js';
|
|
2
|
+
export { D as Dimensions, a as Position, b as StateGraphEdge, c as StateGraphNode } from './StateChart-4_Yew7nT.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Build graphs corresponding to the specified transitions.
|
package/dist/index-runtime.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as StateChart, c as StateGraphNode, b as StateGraphEdge } from './StateChart-
|
|
2
|
-
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, P as Padding, a as Position, T as Transition, d as TransitionsGraph } from './StateChart-
|
|
1
|
+
import { S as StateChart, c as StateGraphNode, b as StateGraphEdge } from './StateChart-4_Yew7nT.js';
|
|
2
|
+
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, P as Padding, a as Position, T as Transition, d as TransitionsGraph } from './StateChart-4_Yew7nT.js';
|
|
3
3
|
|
|
4
4
|
type RuntimeListener = (id: string) => void;
|
|
5
5
|
declare class RuntimeStatechartApi {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, T as Transition, d as TransitionsGraph } from './StateChart-
|
|
1
|
+
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, T as Transition, d as TransitionsGraph } from './StateChart-4_Yew7nT.js';
|
|
2
|
+
export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig } from './index-config.js';
|
|
2
3
|
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './index-layout.js';
|
|
3
|
-
export { C as CssTree, c as activeStatesStyle, d as activeTransitionsStyle, f as buildStateDetailsStyle, g as buildStateDetailsStyleWithModifier, b as buildStatechartCss, i as buildStatechartStyles, h as buildStatechartStylesWithModifier, j as buildStatechartSvg, a as buildStatechartsPanel, s as selectedStatesStyle, e as selectedTransitionsStyle, k as serializeCss } from './index-html-
|
|
4
|
+
export { C as CssTree, c as activeStatesStyle, d as activeTransitionsStyle, f as buildStateDetailsStyle, g as buildStateDetailsStyleWithModifier, b as buildStatechartCss, i as buildStatechartStyles, h as buildStatechartStylesWithModifier, j as buildStatechartSvg, a as buildStatechartsPanel, s as selectedStatesStyle, e as selectedTransitionsStyle, k as serializeCss } from './index-html-C3JXXoPE.js';
|
|
4
5
|
export { RuntimeListener, RuntimeStatechartApi, StateChartIndex } from './index-runtime.js';
|
|
5
6
|
|
|
6
7
|
declare function toKebabCase(str?: string): string;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig } from './chunk-SHSJVXEB.js';
|
|
1
2
|
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-OTGN3OYG.js';
|
|
2
3
|
export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle, serializeCss, toKebabCase } from './chunk-2BBNBZQI.js';
|
|
3
4
|
import './chunk-NXWOFQMO.js';
|
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Transition } from "../types/index.ts";
|
|
2
|
+
|
|
3
|
+
export function concatTransitionsToString(transitions: Transition[]) {
|
|
4
|
+
let list: string[] = [];
|
|
5
|
+
let str: string = "";
|
|
6
|
+
let prev: undefined | string;
|
|
7
|
+
for (const [from, event, to] of transitions) {
|
|
8
|
+
if (prev !== from) {
|
|
9
|
+
if (str) list.push(str);
|
|
10
|
+
str = from;
|
|
11
|
+
}
|
|
12
|
+
str += ` -${escape(event)}-> ${escape(to)}`;
|
|
13
|
+
prev = to;
|
|
14
|
+
}
|
|
15
|
+
if (str) list.push(str);
|
|
16
|
+
return list;
|
|
17
|
+
|
|
18
|
+
function escape(str: string): string {
|
|
19
|
+
return str;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { FsmStateConfig } from "../types/index.ts";
|
|
2
|
+
|
|
3
|
+
export function serializeProcessConfig(config: FsmStateConfig | undefined) {
|
|
4
|
+
if (!config) return "";
|
|
5
|
+
let lines: string[] = [];
|
|
6
|
+
visit(config, lines);
|
|
7
|
+
return lines.join("\n");
|
|
8
|
+
|
|
9
|
+
function visit(config: FsmStateConfig, lines: string[], shift = " ") {
|
|
10
|
+
lines.push(`${shift}{`);
|
|
11
|
+
const { key, states, transitions, ...params } = config;
|
|
12
|
+
lines.push(`${shift} "key": ${JSON.stringify(key)}`);
|
|
13
|
+
if (transitions && transitions.length > 0) {
|
|
14
|
+
lines[lines.length - 1] += ",";
|
|
15
|
+
lines.push(`${shift} "transitions": [`);
|
|
16
|
+
for (let i = 0; i < transitions.length; i++) {
|
|
17
|
+
if (i > 0) {
|
|
18
|
+
lines[lines.length - 1] += ",";
|
|
19
|
+
}
|
|
20
|
+
lines.push(`${shift} ${JSON.stringify(transitions[i])}`);
|
|
21
|
+
}
|
|
22
|
+
lines.push(`${shift} ]`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (states && states.length > 0) {
|
|
26
|
+
lines[lines.length - 1] += ",";
|
|
27
|
+
lines.push(`${shift} "states": [`);
|
|
28
|
+
for (let i = 0; i < states.length; i++) {
|
|
29
|
+
if (i > 0) {
|
|
30
|
+
lines[lines.length - 1] += ",";
|
|
31
|
+
}
|
|
32
|
+
visit(states[i], lines, shift + " ");
|
|
33
|
+
}
|
|
34
|
+
lines.push(`${shift} ]`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (params && Object.keys(params).length > 0) {
|
|
38
|
+
lines[lines.length - 1] += ",";
|
|
39
|
+
const str = JSON.stringify(params, null, 2);
|
|
40
|
+
const paramsLines = str.split("\n");
|
|
41
|
+
paramsLines.shift();
|
|
42
|
+
paramsLines.pop();
|
|
43
|
+
lines.push(...paramsLines.map((line) => shift + line));
|
|
44
|
+
}
|
|
45
|
+
lines.push(`${shift}}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Transition } from "../types/index.ts";
|
|
2
|
+
|
|
3
|
+
export function splitTransitionString(str: string): Transition[] {
|
|
4
|
+
let list: string[] = [];
|
|
5
|
+
let pos = 0;
|
|
6
|
+
const matches = Array.from(
|
|
7
|
+
str.matchAll(/-\s*("[^"]*?"|'[^']*?'|[^-]*?)\s*->/g)
|
|
8
|
+
);
|
|
9
|
+
for (const match of matches) {
|
|
10
|
+
list.push(str.substring(pos, match.index));
|
|
11
|
+
list.push(match[1]);
|
|
12
|
+
pos = match.index + match[0].length;
|
|
13
|
+
}
|
|
14
|
+
list.push(str.substring(pos));
|
|
15
|
+
list = list.map((str) => str.trim());
|
|
16
|
+
const transitions: Transition[] = [];
|
|
17
|
+
for (let i = 0; i < list.length - 2; ) {
|
|
18
|
+
const from = list[i++] || "";
|
|
19
|
+
const event = list[i++] || "";
|
|
20
|
+
const to = list[i] || "";
|
|
21
|
+
transitions.push([from, event, to]);
|
|
22
|
+
}
|
|
23
|
+
return transitions;
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { splitTransitionString } from "./splitTransitionString.ts";
|
|
2
|
+
import type { FsmStateConfig, Transition } from "../types/index.ts";
|
|
3
|
+
|
|
4
|
+
export function toFsmStateConfig(json: any): FsmStateConfig | undefined {
|
|
5
|
+
if (!json || typeof json !== "object") return;
|
|
6
|
+
const { key, transitions, states, ...options } = json;
|
|
7
|
+
if (typeof key !== "string") return;
|
|
8
|
+
const stateTransitions = toFsmTransitions(transitions);
|
|
9
|
+
const substates = toArray(states)
|
|
10
|
+
.map(toFsmStateConfig)
|
|
11
|
+
.filter((v) => !!v) as FsmStateConfig[];
|
|
12
|
+
const state: FsmStateConfig = {
|
|
13
|
+
key,
|
|
14
|
+
transitions: stateTransitions,
|
|
15
|
+
};
|
|
16
|
+
if (substates && substates.length > 0) state.states = substates;
|
|
17
|
+
return Object.assign(state, options);
|
|
18
|
+
|
|
19
|
+
function toFsmTransitions(json: any): Transition[] {
|
|
20
|
+
const transitions: Transition[] = [];
|
|
21
|
+
const index: Record<string, boolean> = {};
|
|
22
|
+
const array = toArray(json);
|
|
23
|
+
for (let line of array) {
|
|
24
|
+
const lineTransitions =
|
|
25
|
+
typeof line === "string"
|
|
26
|
+
? splitTransitionString(line)
|
|
27
|
+
: [toArray(line)];
|
|
28
|
+
for (const transition of lineTransitions.map(validateTransition)) {
|
|
29
|
+
if (!transition) continue;
|
|
30
|
+
const key = transition.join("|");
|
|
31
|
+
if (key in index) continue;
|
|
32
|
+
index[key] = true;
|
|
33
|
+
transitions.push(transition);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return transitions;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function validateTransition(value: any): Transition | undefined {
|
|
40
|
+
const array = toArray(value);
|
|
41
|
+
if (array.length !== 3) return;
|
|
42
|
+
const [from, event, to] = array;
|
|
43
|
+
if (
|
|
44
|
+
typeof from !== "string" ||
|
|
45
|
+
typeof event !== "string" ||
|
|
46
|
+
typeof to !== "string"
|
|
47
|
+
)
|
|
48
|
+
return;
|
|
49
|
+
return [from, event, to];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function toArray(value: any) {
|
|
53
|
+
if (value === undefined) return [];
|
|
54
|
+
if (Array.isArray(value)) return value;
|
|
55
|
+
return [value];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./toFsmStateCharts.ts";
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { newId } from "../utils/newId.ts";
|
|
2
|
+
import { newRegistry } from "../utils/newRegistry.ts";
|
|
3
|
+
import { RuntimeStatechartApi, StateChartIndex } from "../runtime/index.ts";
|
|
4
|
+
import {
|
|
5
|
+
buildStatechartCss,
|
|
6
|
+
buildStatechartsPanel,
|
|
7
|
+
FsmStateConfig,
|
|
8
|
+
StateChart,
|
|
9
|
+
StateGraphEdge,
|
|
10
|
+
} from "../index-html.ts";
|
|
11
|
+
import { buildCharts, getGraphParamsProvider } from "../index-layout.ts";
|
|
12
|
+
|
|
13
|
+
export type FsmStateCharts = {
|
|
14
|
+
api: RuntimeStatechartApi;
|
|
15
|
+
index: StateChartIndex;
|
|
16
|
+
statechart: StateChart;
|
|
17
|
+
html: string;
|
|
18
|
+
element: HTMLElement;
|
|
19
|
+
|
|
20
|
+
register: (action: () => void) => () => void;
|
|
21
|
+
cleanup: () => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function toFsmStateCharts({
|
|
25
|
+
config,
|
|
26
|
+
lodash,
|
|
27
|
+
onTransitionClick,
|
|
28
|
+
}: {
|
|
29
|
+
onTransitionClick: (transition: StateGraphEdge) => unknown;
|
|
30
|
+
config: FsmStateConfig;
|
|
31
|
+
lodash: any;
|
|
32
|
+
}): FsmStateCharts {
|
|
33
|
+
const statechart = buildCharts({
|
|
34
|
+
lodash,
|
|
35
|
+
newId,
|
|
36
|
+
config,
|
|
37
|
+
padding: [15, 15],
|
|
38
|
+
...getGraphParamsProvider({
|
|
39
|
+
// fontSize: 14,
|
|
40
|
+
stateFontSize: 14,
|
|
41
|
+
stateTextPadding: [14, 16],
|
|
42
|
+
transitionsFontSize: 12,
|
|
43
|
+
transitionsTextPadding: 6,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
const index = new StateChartIndex({ statechart });
|
|
47
|
+
|
|
48
|
+
const lines: string[] = [];
|
|
49
|
+
buildStatechartsPanel({
|
|
50
|
+
statechart,
|
|
51
|
+
newId,
|
|
52
|
+
println: (str) => lines.push(str),
|
|
53
|
+
});
|
|
54
|
+
const html = lines.join("\n");
|
|
55
|
+
const statechartId = newId("statechart");
|
|
56
|
+
const css = buildStatechartCss({
|
|
57
|
+
// prefix: `#${statechartId}`
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const container = document.createElement("div");
|
|
61
|
+
container.innerHTML = `${html}
|
|
62
|
+
<style>
|
|
63
|
+
${css}
|
|
64
|
+
#${statechartId} {
|
|
65
|
+
padding: 0.5em 0;
|
|
66
|
+
}
|
|
67
|
+
</style>`;
|
|
68
|
+
container.id = statechartId;
|
|
69
|
+
// const style = container.querySelector("style");
|
|
70
|
+
|
|
71
|
+
const [register, cleanup] = newRegistry();
|
|
72
|
+
register(() => {
|
|
73
|
+
container.remove();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const api = new RuntimeStatechartApi({
|
|
77
|
+
element: container,
|
|
78
|
+
statechart,
|
|
79
|
+
});
|
|
80
|
+
const topNodeId = index.getStackByStateId(statechart.id)[0].id;
|
|
81
|
+
// api.selectState(topNodeId);
|
|
82
|
+
api.openStatePanel(topNodeId);
|
|
83
|
+
|
|
84
|
+
register(
|
|
85
|
+
api.onStateClick((stateId) => {
|
|
86
|
+
const nodes = index.getStackByStateId(stateId);
|
|
87
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
88
|
+
api.deselectTransitions();
|
|
89
|
+
const selected = api.toggleState(...nodeIds);
|
|
90
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
91
|
+
const node = nodes[i];
|
|
92
|
+
const stateId = node.id;
|
|
93
|
+
if (i < nodes.length - 1 || selected) {
|
|
94
|
+
const transitions = index.getTransitions(stateId);
|
|
95
|
+
transitions.forEach((t) => api.selectTransition(t.id));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (onTransitionClick) {
|
|
102
|
+
register(
|
|
103
|
+
api.onTransitionClick((transitionId) => {
|
|
104
|
+
const transition = index.getEdgeById(transitionId);
|
|
105
|
+
transition && onTransitionClick(transition);
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
register,
|
|
111
|
+
cleanup,
|
|
112
|
+
statechart,
|
|
113
|
+
api,
|
|
114
|
+
html,
|
|
115
|
+
element: container,
|
|
116
|
+
index,
|
|
117
|
+
};
|
|
118
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import type { Transition } from "./Transition.ts";
|
|
2
2
|
|
|
3
|
+
// export type FsmStateKey = string;
|
|
4
|
+
// export type FsmEventKey = string;
|
|
5
|
+
// export type FsmTransition = [FsmStateKey, FsmEventKey, FsmStateKey];
|
|
6
|
+
// export type FsmState = {
|
|
7
|
+
// key: string;
|
|
8
|
+
// transitions: FsmTransition[];
|
|
9
|
+
// states?: FsmState[];
|
|
10
|
+
// } & Record<string, any>;
|
|
11
|
+
|
|
3
12
|
export type FsmStateConfig = {
|
|
4
13
|
key: string;
|
|
5
14
|
transitions: Transition[];
|
|
6
15
|
states?: FsmStateConfig[];
|
|
7
|
-
}
|
|
16
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function domToText(elm: HTMLElement) {
|
|
2
|
+
const lines: string[] = [];
|
|
3
|
+
visit(elm, (node) => {
|
|
4
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
5
|
+
lines.push(node.textContent || "");
|
|
6
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
7
|
+
if (
|
|
8
|
+
node.nodeName === "BR" ||
|
|
9
|
+
node.nodeName === "P" ||
|
|
10
|
+
node.nodeName === "DIV"
|
|
11
|
+
) {
|
|
12
|
+
lines.push("\n");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return lines.join("");
|
|
17
|
+
|
|
18
|
+
function visit(el: HTMLElement, visitor: (node: Node) => void) {
|
|
19
|
+
for (var i = 0; i < el.childNodes.length; i++) {
|
|
20
|
+
const child = el.childNodes[i];
|
|
21
|
+
const result = visitor(el.childNodes[i]);
|
|
22
|
+
if (child.nodeType !== Node.ELEMENT_NODE) continue;
|
|
23
|
+
if (result !== undefined && !Boolean(result)) continue;
|
|
24
|
+
visit(child as HTMLElement, visitor);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type Registry = [
|
|
2
|
+
register: (action: () => unknown) => (skipCall?: boolean) => void,
|
|
3
|
+
clear: () => void,
|
|
4
|
+
unregister: (action: () => unknown) => void,
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
export function newRegistry(
|
|
8
|
+
onError: (error: any) => unknown = console.error,
|
|
9
|
+
): Registry {
|
|
10
|
+
let counter = 0;
|
|
11
|
+
const registrations: Record<
|
|
12
|
+
number,
|
|
13
|
+
((skip?: boolean) => void) & { action: () => void }
|
|
14
|
+
> = {};
|
|
15
|
+
const register = (action: () => unknown) => {
|
|
16
|
+
const id = counter++;
|
|
17
|
+
return (registrations[id] = Object.assign(
|
|
18
|
+
function (skip?: boolean) {
|
|
19
|
+
try {
|
|
20
|
+
delete registrations[id];
|
|
21
|
+
return !skip && action && action();
|
|
22
|
+
} catch (error) {
|
|
23
|
+
onError(error);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{ action },
|
|
27
|
+
));
|
|
28
|
+
};
|
|
29
|
+
const unregister = (action: () => void) =>
|
|
30
|
+
Object.values(registrations).forEach((r) => r.action === action && r(true));
|
|
31
|
+
const clear = () => Object.values(registrations).forEach((r) => r());
|
|
32
|
+
return [register, clear, unregister];
|
|
33
|
+
}
|