@statewalker/fsm-charts 0.1.13 → 0.2.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.
- package/dist/{StateChart-CdOU9XAq.d.ts → StateChart-BYBjTmC2.d.ts} +1 -1
- package/dist/{chunk-NWCC4WSF.js → chunk-AZBRQNTU.js} +60 -29
- package/dist/{chunk-GCVBCAOL.js → chunk-G25OZI4I.js} +10 -3
- package/dist/{chunk-AVTSJE25.js → chunk-LRMTXZ55.js} +79 -75
- package/dist/{chunk-N77KKO5K.js → chunk-TZHCBZVP.js} +49 -9
- package/dist/index-config.d.ts +2 -2
- package/dist/index-dom.d.ts +2 -3
- package/dist/index-dom.js +1 -1
- package/dist/{index-html-BKMwwc5Q.d.ts → index-html-DgSCNHkR.d.ts} +1 -1
- package/dist/index-html.d.ts +2 -2
- package/dist/index-html.js +1 -1
- package/dist/index-layout.d.ts +4 -3
- package/dist/index-layout.js +1 -1
- package/dist/index-runtime.d.ts +20 -3
- package/dist/index-runtime.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/src/dom/buildSections.ts +86 -0
- package/src/dom/index.ts +1 -105
- package/src/dom/newTreeBuilder.ts +14 -0
- package/src/full/toFsmStateCharts.ts +1 -0
- package/src/html/buildStatechartStyles.ts +1 -1
- package/src/html/buildStatechartsPanel.ts +9 -2
- package/src/layout/buildCharts.ts +69 -29
- package/src/runtime/RuntimeStatechartApi.ts +36 -20
- package/src/runtime/StateChartIndex.ts +41 -3
- package/src/types/StateGraphNode.ts +2 -1
|
@@ -2571,6 +2571,7 @@ function buildCharts({
|
|
|
2571
2571
|
config,
|
|
2572
2572
|
newId,
|
|
2573
2573
|
getStateParams,
|
|
2574
|
+
getStateLabel = (stateKey) => stateKey.replace(/([a-z])([A-Z])/g, "$1 $2"),
|
|
2574
2575
|
getTransitionParams,
|
|
2575
2576
|
padding = [5, 5],
|
|
2576
2577
|
vertical = false,
|
|
@@ -2579,37 +2580,67 @@ function buildCharts({
|
|
|
2579
2580
|
direction
|
|
2580
2581
|
}) {
|
|
2581
2582
|
return buildChart(config);
|
|
2582
|
-
function buildChart(config2, id = newId("s")) {
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2583
|
+
function buildChart(config2, id = newId("s"), stack = []) {
|
|
2584
|
+
stack.push(config2);
|
|
2585
|
+
try {
|
|
2586
|
+
const buildStateLabel = (state, key = state) => {
|
|
2587
|
+
if (key === initialStateKey || key === finalStateKey) {
|
|
2588
|
+
return "";
|
|
2589
|
+
}
|
|
2590
|
+
let result;
|
|
2591
|
+
for (let i = stack.length - 1; result === void 0 && i >= 0; i--) {
|
|
2592
|
+
const conf = stack[i];
|
|
2593
|
+
if (!conf.states) continue;
|
|
2594
|
+
for (const subState of conf.states) {
|
|
2595
|
+
if (subState.key !== state) continue;
|
|
2596
|
+
result = subState.label ?? getStateLabel(state);
|
|
2597
|
+
break;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
return result ?? getStateLabel(state) ?? state;
|
|
2601
|
+
};
|
|
2602
|
+
const graph = buildFlatCharts({
|
|
2603
|
+
lodash,
|
|
2604
|
+
transitions: config2.transitions || [],
|
|
2605
|
+
newId,
|
|
2606
|
+
getStateParams: (state, key) => {
|
|
2607
|
+
const params = getStateParams(state, key);
|
|
2608
|
+
delete params.state;
|
|
2609
|
+
params.text = buildStateLabel(state, key);
|
|
2610
|
+
return params;
|
|
2611
|
+
},
|
|
2612
|
+
getTransitionParams,
|
|
2613
|
+
padding,
|
|
2614
|
+
initialStateKey,
|
|
2615
|
+
finalStateKey,
|
|
2616
|
+
vertical,
|
|
2617
|
+
direction
|
|
2618
|
+
});
|
|
2619
|
+
const index = graph.nodes.reduce(
|
|
2620
|
+
(index2, node) => {
|
|
2621
|
+
index2[node.key] = node.id;
|
|
2622
|
+
return index2;
|
|
2623
|
+
},
|
|
2624
|
+
{}
|
|
2625
|
+
);
|
|
2626
|
+
const text = config2.label ?? getStateLabel(config2.key) ?? config2.key;
|
|
2627
|
+
const children = [];
|
|
2628
|
+
if (config2.states) {
|
|
2629
|
+
for (const child of config2.states) {
|
|
2630
|
+
const childId = index[child.key];
|
|
2631
|
+
children.push(buildChart(child, childId, [...stack]));
|
|
2632
|
+
}
|
|
2604
2633
|
}
|
|
2634
|
+
return {
|
|
2635
|
+
id,
|
|
2636
|
+
key: config2.key,
|
|
2637
|
+
// state,
|
|
2638
|
+
text,
|
|
2639
|
+
...graph,
|
|
2640
|
+
children
|
|
2641
|
+
};
|
|
2642
|
+
} finally {
|
|
2605
2643
|
}
|
|
2606
|
-
return {
|
|
2607
|
-
id,
|
|
2608
|
-
key: config2.key,
|
|
2609
|
-
state: config2.key,
|
|
2610
|
-
...graph,
|
|
2611
|
-
children
|
|
2612
|
-
};
|
|
2613
2644
|
}
|
|
2614
2645
|
}
|
|
2615
2646
|
|
|
@@ -94,7 +94,7 @@ function buildStateDetailsStyle({
|
|
|
94
94
|
fontFamily: "var(--state-label-font-family, sans-serif)",
|
|
95
95
|
fontSize: "var(--state-label-font-size, 14px)"
|
|
96
96
|
},
|
|
97
|
-
".state-
|
|
97
|
+
".state-details__content": {
|
|
98
98
|
display: "flex",
|
|
99
99
|
flexDirection: "column",
|
|
100
100
|
gap: "0.5em"
|
|
@@ -577,10 +577,13 @@ function buildStatechartsPanel({
|
|
|
577
577
|
` <details class="state-details" data-state-id="${statechart2.id}">`
|
|
578
578
|
);
|
|
579
579
|
println2(
|
|
580
|
-
` <summary class="state-details__label">${statechart2.
|
|
580
|
+
` <summary class="state-details__label">${statechart2.text ?? statechart2.state}</summary>`
|
|
581
581
|
);
|
|
582
582
|
println2(
|
|
583
|
-
` <div class="state-
|
|
583
|
+
` <div class="state-details__content">`
|
|
584
|
+
);
|
|
585
|
+
println2(
|
|
586
|
+
` <div class="state-details__chart">`
|
|
584
587
|
);
|
|
585
588
|
const printChildren = (str) => println2(` ${str}`);
|
|
586
589
|
buildStatechartSvg({
|
|
@@ -591,6 +594,10 @@ function buildStatechartsPanel({
|
|
|
591
594
|
finalStateKey
|
|
592
595
|
});
|
|
593
596
|
println2(` </div>`);
|
|
597
|
+
println2(
|
|
598
|
+
` <div class="state-details__description">`
|
|
599
|
+
);
|
|
600
|
+
println2(` </div>`);
|
|
594
601
|
for (const child of statechart2.children || []) {
|
|
595
602
|
render(child, printChildren);
|
|
596
603
|
}
|
|
@@ -1,3 +1,81 @@
|
|
|
1
|
+
// src/dom/newTreeBuilder.ts
|
|
2
|
+
function newTreeBuilder(context) {
|
|
3
|
+
let level = -1;
|
|
4
|
+
return (newLevel, ...params) => {
|
|
5
|
+
while (level >= 0 && level >= newLevel) {
|
|
6
|
+
context.exit(level--, ...params);
|
|
7
|
+
}
|
|
8
|
+
while (level < newLevel) {
|
|
9
|
+
context.enter(++level, ...params);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/dom/buildSections.ts
|
|
15
|
+
function buildSections(root) {
|
|
16
|
+
const nodes = toDomNodesIterator(root);
|
|
17
|
+
let top = null;
|
|
18
|
+
let peek = null;
|
|
19
|
+
const stack = [];
|
|
20
|
+
const aligner = newTreeBuilder({
|
|
21
|
+
enter(level, header) {
|
|
22
|
+
const section = {
|
|
23
|
+
header,
|
|
24
|
+
content: [],
|
|
25
|
+
children: []
|
|
26
|
+
};
|
|
27
|
+
top = top || section;
|
|
28
|
+
peek && peek.children.push(section);
|
|
29
|
+
peek = section;
|
|
30
|
+
stack.push(section);
|
|
31
|
+
},
|
|
32
|
+
exit() {
|
|
33
|
+
stack.pop();
|
|
34
|
+
peek = stack[stack.length - 1];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
aligner(0);
|
|
38
|
+
let currentHeaderLevel = 0;
|
|
39
|
+
let currentLevel = 0;
|
|
40
|
+
for (const node of nodes) {
|
|
41
|
+
if (isDomElement(node)) {
|
|
42
|
+
if (/^H[1-6]$/i.test(node.tagName)) {
|
|
43
|
+
const headerLevel = +node.tagName.replace(/^H/, "");
|
|
44
|
+
currentLevel = headerLevel > currentHeaderLevel ? currentLevel + 1 : headerLevel < currentHeaderLevel ? currentLevel - 1 : currentLevel;
|
|
45
|
+
currentHeaderLevel = headerLevel;
|
|
46
|
+
aligner(currentLevel, node);
|
|
47
|
+
} else {
|
|
48
|
+
peek.content.push(node);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
peek.content.push(node);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return top.content.length === 0 && top.children.length === 1 && !top.header ? top.children[0] : top;
|
|
55
|
+
}
|
|
56
|
+
function isDomElement(node) {
|
|
57
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
58
|
+
}
|
|
59
|
+
function* toDomNodesIterator(root) {
|
|
60
|
+
yield* root instanceof Node ? visitDom(root) : root;
|
|
61
|
+
}
|
|
62
|
+
function* visitDom(elm) {
|
|
63
|
+
if (isDomElement(elm) && elm.tagName === "TEMPLATE") {
|
|
64
|
+
elm = elm.content;
|
|
65
|
+
}
|
|
66
|
+
for (let node = elm.firstChild; node; node = node.nextSibling) {
|
|
67
|
+
if (isDomElement(node)) {
|
|
68
|
+
if (node.tagName === "DIV" || node.tagName === "TEMPLATE") {
|
|
69
|
+
yield* visitDom(node);
|
|
70
|
+
} else {
|
|
71
|
+
yield node;
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
yield node;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
1
79
|
// src/dom/index.ts
|
|
2
80
|
function getStateDescriptionRenderer({
|
|
3
81
|
element,
|
|
@@ -56,22 +134,6 @@ function getStateDescriptionRenderer({
|
|
|
56
134
|
};
|
|
57
135
|
}
|
|
58
136
|
}
|
|
59
|
-
function* visitDom(elm) {
|
|
60
|
-
if (isDomElement(elm) && elm.tagName === "TEMPLATE") {
|
|
61
|
-
elm = elm.content;
|
|
62
|
-
}
|
|
63
|
-
for (let node = elm.firstChild; node; node = node.nextSibling) {
|
|
64
|
-
if (isDomElement(node)) {
|
|
65
|
-
if (node.tagName === "DIV" || node.tagName === "TEMPLATE") {
|
|
66
|
-
yield* visitDom(node);
|
|
67
|
-
} else {
|
|
68
|
-
yield node;
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
yield node;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
137
|
function* visitSectionNodes(section) {
|
|
76
138
|
if (section.header) yield section.header;
|
|
77
139
|
yield* section.content;
|
|
@@ -90,64 +152,6 @@ function findSection(section, accept) {
|
|
|
90
152
|
if (accept(s) === true) return s;
|
|
91
153
|
}
|
|
92
154
|
}
|
|
93
|
-
function isDomElement(node) {
|
|
94
|
-
return node.nodeType === Node.ELEMENT_NODE;
|
|
95
|
-
}
|
|
96
|
-
function newAligner(context) {
|
|
97
|
-
let level = -1;
|
|
98
|
-
return (newLevel, ...params) => {
|
|
99
|
-
while (level >= 0 && level >= newLevel) {
|
|
100
|
-
context.exit(level--, ...params);
|
|
101
|
-
}
|
|
102
|
-
while (level < newLevel) {
|
|
103
|
-
context.enter(++level, ...params);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
function* toDomNodesIterator(root) {
|
|
108
|
-
yield* root instanceof Node ? visitDom(root) : root;
|
|
109
|
-
}
|
|
110
|
-
function buildSections(root) {
|
|
111
|
-
const nodes = toDomNodesIterator(root);
|
|
112
|
-
let top = null;
|
|
113
|
-
let peek = null;
|
|
114
|
-
const stack = [];
|
|
115
|
-
const aligner = newAligner({
|
|
116
|
-
enter(level, header) {
|
|
117
|
-
const section = {
|
|
118
|
-
header,
|
|
119
|
-
content: [],
|
|
120
|
-
children: []
|
|
121
|
-
};
|
|
122
|
-
top = top || section;
|
|
123
|
-
peek && peek.children.push(section);
|
|
124
|
-
peek = section;
|
|
125
|
-
stack.push(section);
|
|
126
|
-
},
|
|
127
|
-
exit() {
|
|
128
|
-
stack.pop();
|
|
129
|
-
peek = stack[stack.length - 1];
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
aligner(0);
|
|
133
|
-
let currentHeaderLevel = 0;
|
|
134
|
-
let currentLevel = 0;
|
|
135
|
-
for (const node of nodes) {
|
|
136
|
-
if (isDomElement(node)) {
|
|
137
|
-
if (/^H[1-6]$/i.test(node.tagName)) {
|
|
138
|
-
const headerLevel = +node.tagName.replace(/^H/, "");
|
|
139
|
-
currentLevel = headerLevel > currentHeaderLevel ? currentLevel + 1 : headerLevel < currentHeaderLevel ? currentLevel - 1 : currentLevel;
|
|
140
|
-
currentHeaderLevel = headerLevel;
|
|
141
|
-
aligner(currentLevel, node);
|
|
142
|
-
} else {
|
|
143
|
-
peek.content.push(node);
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
peek.content.push(node);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return top.content.length === 0 && top.children.length === 1 && !top.header ? top.children[0] : top;
|
|
150
|
-
}
|
|
151
155
|
function renderSections({
|
|
152
156
|
section,
|
|
153
157
|
deep = (s) => true,
|
|
@@ -208,4 +212,4 @@ function renderSections({
|
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
|
|
211
|
-
export {
|
|
215
|
+
export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections };
|
|
@@ -55,14 +55,26 @@ var RuntimeStatechartApi = class {
|
|
|
55
55
|
this.selectState(id);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
focusStates(...stateIds) {
|
|
59
|
+
for (let i = 0; i < stateIds.length; i++) {
|
|
60
|
+
const stateId = stateIds[i];
|
|
61
|
+
for (const tagName of ["details", "g"]) {
|
|
62
|
+
const elm = this.element.querySelector(
|
|
63
|
+
`${tagName}[data-state-id="${stateId}"]`
|
|
64
|
+
);
|
|
65
|
+
elm?.scrollIntoView();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
setStateDescription(stateId, description) {
|
|
70
|
+
const descriptionContainer = this.element.querySelector(
|
|
71
|
+
`details[data-state-id="${stateId}"] .state-details__description`
|
|
72
|
+
);
|
|
73
|
+
if (descriptionContainer) {
|
|
74
|
+
descriptionContainer.innerHTML = "";
|
|
75
|
+
descriptionContainer.append(description ?? "");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
66
78
|
closeStatePanel(stateId) {
|
|
67
79
|
this._setStatePanelStatus(`details[data-state-id="${stateId}"]`, false);
|
|
68
80
|
}
|
|
@@ -209,13 +221,19 @@ var RuntimeStatechartApi = class {
|
|
|
209
221
|
|
|
210
222
|
// src/runtime/StateChartIndex.ts
|
|
211
223
|
var StateChartIndex = class {
|
|
212
|
-
constructor({
|
|
224
|
+
constructor({
|
|
225
|
+
statechart,
|
|
226
|
+
initialStateKey = "<initial>",
|
|
227
|
+
finalStateKey = "<final>"
|
|
228
|
+
}) {
|
|
213
229
|
this._chartsIndex = {};
|
|
214
230
|
this._nodesIndex = {};
|
|
215
231
|
this._nodesParentsIndex = {};
|
|
216
232
|
this._edgeIndex = {};
|
|
217
233
|
this._edgesParentsIndex = {};
|
|
218
234
|
this.statechart = statechart;
|
|
235
|
+
this.initialStateKey = initialStateKey;
|
|
236
|
+
this.finalStateKey = finalStateKey;
|
|
219
237
|
this._buildIndexes();
|
|
220
238
|
}
|
|
221
239
|
/**
|
|
@@ -242,6 +260,28 @@ var StateChartIndex = class {
|
|
|
242
260
|
getEdgeById(id) {
|
|
243
261
|
return this._edgeIndex[id];
|
|
244
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Returns a list of all nodes in this chart.
|
|
265
|
+
* @return a list of all nodes
|
|
266
|
+
*/
|
|
267
|
+
getAllNodes() {
|
|
268
|
+
return [
|
|
269
|
+
.../* @__PURE__ */ new Set([
|
|
270
|
+
...Object.values(this._chartsIndex),
|
|
271
|
+
...Object.values(this._nodesIndex)
|
|
272
|
+
])
|
|
273
|
+
];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Returns a list of all non-empty state nodes in this chart.
|
|
277
|
+
* The result excludes nodes for initial and fina states.
|
|
278
|
+
* @return a list of all non-empty state nodes
|
|
279
|
+
*/
|
|
280
|
+
getStateNodes() {
|
|
281
|
+
return this.getAllNodes().filter(
|
|
282
|
+
(node) => node.key !== this.initialStateKey && node.key !== this.finalStateKey
|
|
283
|
+
);
|
|
284
|
+
}
|
|
245
285
|
/**
|
|
246
286
|
* Transforms a stack of state keys to the corresponding identifiers.
|
|
247
287
|
* If a key in the stack was not found then this method returns
|
package/dist/index-config.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as Transition, F as FsmStateConfig } from './StateChart-
|
|
2
|
-
export { D as Dimensions, G as GraphParamsProvider, L as Label, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, d as TransitionsGraph } from './StateChart-
|
|
1
|
+
import { T as Transition, F as FsmStateConfig } from './StateChart-BYBjTmC2.js';
|
|
2
|
+
export { D as Dimensions, G as GraphParamsProvider, L as Label, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, d as TransitionsGraph } from './StateChart-BYBjTmC2.js';
|
|
3
3
|
|
|
4
4
|
declare function concatTransitionsToString(transitions: Transition[]): string[];
|
|
5
5
|
|
package/dist/index-dom.d.ts
CHANGED
|
@@ -3,20 +3,19 @@ type DomSection = {
|
|
|
3
3
|
content: Node[];
|
|
4
4
|
children: DomSection[];
|
|
5
5
|
};
|
|
6
|
+
|
|
6
7
|
declare function getStateDescriptionRenderer({ element, rootStateKey, splitHeader, }: {
|
|
7
8
|
element: HTMLElement;
|
|
8
9
|
rootStateKey: string;
|
|
9
10
|
splitHeader?: (header?: HTMLElement) => [stateKey: undefined | string, label: string];
|
|
10
11
|
}): (statesStack: string[]) => HTMLDivElement | undefined;
|
|
11
|
-
declare function visitDom(elm: Node): Iterable<Node>;
|
|
12
12
|
declare function visitSectionNodes(section: DomSection): Iterable<Node>;
|
|
13
13
|
declare function vistSections(root: DomSection, ignoreRoot?: boolean): Generator<DomSection>;
|
|
14
14
|
declare function findSection(section: DomSection, accept: (section: DomSection) => boolean): DomSection | undefined;
|
|
15
|
-
declare function buildSections(root: HTMLElement | Iterable<Node>): DomSection;
|
|
16
15
|
declare function renderSections({ section, deep, header, }: {
|
|
17
16
|
section: DomSection;
|
|
18
17
|
deep?: (s: DomSection) => boolean;
|
|
19
18
|
header?: (header: HTMLElement) => undefined | string | HTMLElement;
|
|
20
19
|
}): HTMLDivElement;
|
|
21
20
|
|
|
22
|
-
export {
|
|
21
|
+
export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections };
|
package/dist/index-dom.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections } from './chunk-LRMTXZ55.js';
|
package/dist/index-html.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, L as Label, 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, L as Label, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, T as Transition, d as TransitionsGraph } from './StateChart-BYBjTmC2.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-DgSCNHkR.js';
|
package/dist/index-html.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle } from './chunk-
|
|
1
|
+
export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle } from './chunk-G25OZI4I.js';
|
|
2
2
|
import './chunk-FIQDLBWD.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, L as Label, 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-BYBjTmC2.js';
|
|
2
|
+
export { D as Dimensions, L as Label, a as Position, b as StateGraphEdge, c as StateGraphNode } from './StateChart-BYBjTmC2.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Build graphs corresponding to the specified transitions.
|
|
@@ -33,7 +33,7 @@ declare function buildFlatCharts({ lodash, transitions, newId, getStateParams, g
|
|
|
33
33
|
* @param finalStateKey - The key for the final state.
|
|
34
34
|
* @returns {StateChart} - The graph with nodes and edges.
|
|
35
35
|
*/
|
|
36
|
-
declare function buildCharts({ lodash, config, newId, getStateParams, getTransitionParams, padding, vertical, initialStateKey, finalStateKey, direction, }: {
|
|
36
|
+
declare function buildCharts({ lodash, config, newId, getStateParams, getStateLabel, getTransitionParams, padding, vertical, initialStateKey, finalStateKey, direction, }: {
|
|
37
37
|
config: FsmStateConfig;
|
|
38
38
|
lodash: any;
|
|
39
39
|
newId: (prefix: string) => string;
|
|
@@ -42,6 +42,7 @@ declare function buildCharts({ lodash, config, newId, getStateParams, getTransit
|
|
|
42
42
|
padding?: Padding;
|
|
43
43
|
vertical?: boolean;
|
|
44
44
|
direction?: "tb" | "bt" | "lr" | "rl";
|
|
45
|
+
getStateLabel?: (stateKey: string) => string | undefined;
|
|
45
46
|
} & GraphParamsProvider): StateChart;
|
|
46
47
|
|
|
47
48
|
/**
|
package/dist/index-layout.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-
|
|
1
|
+
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-AZBRQNTU.js';
|
|
2
2
|
import './chunk-FIQDLBWD.js';
|
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, L as Label, 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-BYBjTmC2.js';
|
|
2
|
+
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, L as Label, P as Padding, a as Position, T as Transition, d as TransitionsGraph } from './StateChart-BYBjTmC2.js';
|
|
3
3
|
|
|
4
4
|
type RuntimeListener = (id: string) => void;
|
|
5
5
|
declare class RuntimeStatechartApi {
|
|
@@ -21,6 +21,8 @@ declare class RuntimeStatechartApi {
|
|
|
21
21
|
isStatePanelOpen(stateId: string): boolean;
|
|
22
22
|
toggleState(...stateIds: string[]): boolean;
|
|
23
23
|
activateStates(...stateIds: string[]): void;
|
|
24
|
+
focusStates(...stateIds: string[]): void;
|
|
25
|
+
setStateDescription(stateId: string, description: HTMLElement | string | undefined): void;
|
|
24
26
|
closeStatePanel(stateId: string): void;
|
|
25
27
|
openStatePanel(stateId: string): void;
|
|
26
28
|
deselectTransitions(modifier?: string): void;
|
|
@@ -50,8 +52,12 @@ declare class StateChartIndex {
|
|
|
50
52
|
_nodesParentsIndex: Record<string, string>;
|
|
51
53
|
_edgeIndex: Record<string, StateGraphEdge>;
|
|
52
54
|
_edgesParentsIndex: Record<string, string>;
|
|
53
|
-
|
|
55
|
+
initialStateKey: string;
|
|
56
|
+
finalStateKey: string;
|
|
57
|
+
constructor({ statechart, initialStateKey, finalStateKey, }: {
|
|
54
58
|
statechart: StateChart;
|
|
59
|
+
initialStateKey?: string;
|
|
60
|
+
finalStateKey?: string;
|
|
55
61
|
});
|
|
56
62
|
/**
|
|
57
63
|
* Returns a state node or a chart by the specified identifier.
|
|
@@ -71,6 +77,17 @@ declare class StateChartIndex {
|
|
|
71
77
|
* @returns a graph edge
|
|
72
78
|
*/
|
|
73
79
|
getEdgeById(id: string): StateGraphEdge | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Returns a list of all nodes in this chart.
|
|
82
|
+
* @return a list of all nodes
|
|
83
|
+
*/
|
|
84
|
+
getAllNodes(): StateGraphNode[];
|
|
85
|
+
/**
|
|
86
|
+
* Returns a list of all non-empty state nodes in this chart.
|
|
87
|
+
* The result excludes nodes for initial and fina states.
|
|
88
|
+
* @return a list of all non-empty state nodes
|
|
89
|
+
*/
|
|
90
|
+
getStateNodes(): StateGraphNode[];
|
|
74
91
|
/**
|
|
75
92
|
* Transforms a stack of state keys to the corresponding identifiers.
|
|
76
93
|
* If a key in the stack was not found then this method returns
|
package/dist/index-runtime.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RuntimeStatechartApi, StateChartIndex } from './chunk-
|
|
1
|
+
export { RuntimeStatechartApi, StateChartIndex } from './chunk-TZHCBZVP.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, L as Label, 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 {
|
|
1
|
+
export { D as Dimensions, F as FsmStateConfig, G as GraphParamsProvider, L as Label, P as Padding, a as Position, S as StateChart, b as StateGraphEdge, c as StateGraphNode, T as Transition, d as TransitionsGraph } from './StateChart-BYBjTmC2.js';
|
|
2
|
+
export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections } from './index-dom.js';
|
|
3
3
|
export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig } from './index-config.js';
|
|
4
4
|
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './index-layout.js';
|
|
5
|
-
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-
|
|
5
|
+
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-DgSCNHkR.js';
|
|
6
6
|
export { RuntimeListener, RuntimeStatechartApi, StateChartIndex } from './index-runtime.js';
|
|
7
7
|
|
|
8
8
|
declare function toKebabCase(str?: string): string;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig } from './chunk-SVKVQY7B.js';
|
|
2
|
-
export {
|
|
3
|
-
export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle, serializeCss, toKebabCase } from './chunk-
|
|
4
|
-
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-
|
|
2
|
+
export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections } from './chunk-LRMTXZ55.js';
|
|
3
|
+
export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle, serializeCss, toKebabCase } from './chunk-G25OZI4I.js';
|
|
4
|
+
export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-AZBRQNTU.js';
|
|
5
5
|
import './chunk-FIQDLBWD.js';
|
|
6
|
-
export { RuntimeStatechartApi, StateChartIndex } from './chunk-
|
|
6
|
+
export { RuntimeStatechartApi, StateChartIndex } from './chunk-TZHCBZVP.js';
|
package/package.json
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { newTreeBuilder } from "./newTreeBuilder.js";
|
|
2
|
+
|
|
3
|
+
export type DomSection = {
|
|
4
|
+
header?: HTMLElement;
|
|
5
|
+
content: Node[];
|
|
6
|
+
children: DomSection[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function buildSections(root: HTMLElement | Iterable<Node>): DomSection {
|
|
10
|
+
const nodes = toDomNodesIterator(root);
|
|
11
|
+
let top: DomSection = null!;
|
|
12
|
+
let peek: DomSection = null!;
|
|
13
|
+
const stack: DomSection[] = [];
|
|
14
|
+
const aligner = newTreeBuilder<[HTMLElement?]>({
|
|
15
|
+
enter(level, header) {
|
|
16
|
+
const section: DomSection = {
|
|
17
|
+
header,
|
|
18
|
+
content: [],
|
|
19
|
+
children: [],
|
|
20
|
+
};
|
|
21
|
+
top = top || section;
|
|
22
|
+
peek && peek.children.push(section);
|
|
23
|
+
peek = section;
|
|
24
|
+
stack.push(section);
|
|
25
|
+
},
|
|
26
|
+
exit() {
|
|
27
|
+
stack.pop();
|
|
28
|
+
peek = stack[stack.length - 1];
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
aligner(0);
|
|
32
|
+
let currentHeaderLevel = 0;
|
|
33
|
+
let currentLevel = 0;
|
|
34
|
+
for (const node of nodes) {
|
|
35
|
+
if (isDomElement(node)) {
|
|
36
|
+
if (/^H[1-6]$/i.test(node.tagName)) {
|
|
37
|
+
const headerLevel = +node.tagName.replace(/^H/, "");
|
|
38
|
+
currentLevel =
|
|
39
|
+
headerLevel > currentHeaderLevel
|
|
40
|
+
? currentLevel + 1
|
|
41
|
+
: headerLevel < currentHeaderLevel
|
|
42
|
+
? currentLevel - 1
|
|
43
|
+
: currentLevel;
|
|
44
|
+
currentHeaderLevel = headerLevel;
|
|
45
|
+
aligner(currentLevel, node);
|
|
46
|
+
} else {
|
|
47
|
+
peek.content.push(node);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
peek.content.push(node);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return top.content.length === 0 && top.children.length === 1 && !top.header
|
|
54
|
+
? top.children[0]
|
|
55
|
+
: top;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// --------------------------------------------
|
|
59
|
+
// DOM Iterator utilities
|
|
60
|
+
|
|
61
|
+
export function isDomElement(node: Node): node is HTMLElement {
|
|
62
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function* toDomNodesIterator(
|
|
66
|
+
root: Node | Iterable<Node>,
|
|
67
|
+
): Iterable<Node> {
|
|
68
|
+
yield* root instanceof Node ? visitDom(root) : root;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function* visitDom(elm: Node): Iterable<Node> {
|
|
72
|
+
if (isDomElement(elm) && elm.tagName === "TEMPLATE") {
|
|
73
|
+
elm = (elm as HTMLTemplateElement).content;
|
|
74
|
+
}
|
|
75
|
+
for (let node = elm.firstChild; node; node = node.nextSibling) {
|
|
76
|
+
if (isDomElement(node)) {
|
|
77
|
+
if (node.tagName === "DIV" || node.tagName === "TEMPLATE") {
|
|
78
|
+
yield* visitDom(node);
|
|
79
|
+
} else {
|
|
80
|
+
yield node;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
yield node;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
package/src/dom/index.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
header?: HTMLElement;
|
|
3
|
-
content: Node[];
|
|
4
|
-
children: DomSection[];
|
|
5
|
-
};
|
|
1
|
+
import { buildSections, DomSection } from "./buildSections.js";
|
|
6
2
|
|
|
7
3
|
export function getStateDescriptionRenderer({
|
|
8
4
|
element,
|
|
@@ -43,17 +39,6 @@ export function getStateDescriptionRenderer({
|
|
|
43
39
|
if (node.nodeName[0] === "#") continue;
|
|
44
40
|
node.parentElement?.removeChild(node);
|
|
45
41
|
}
|
|
46
|
-
// for (const section of vistSections(rootDescription)) {
|
|
47
|
-
// const [key, label] = splitHeader(section.header);
|
|
48
|
-
// console.log("??????", section.header?.innerText, key);
|
|
49
|
-
// if (!key) continue;
|
|
50
|
-
|
|
51
|
-
// section.header?.remove();
|
|
52
|
-
// for (const node of section.content) {
|
|
53
|
-
// if (node.nodeName[0] === "#") continue;
|
|
54
|
-
// node.parentElement?.removeChild(node);
|
|
55
|
-
// }
|
|
56
|
-
// }
|
|
57
42
|
|
|
58
43
|
return (statesStack: string[]) => {
|
|
59
44
|
const stateKey = [...statesStack].pop();
|
|
@@ -89,23 +74,6 @@ export function getStateDescriptionRenderer({
|
|
|
89
74
|
}
|
|
90
75
|
}
|
|
91
76
|
|
|
92
|
-
export function* visitDom(elm: Node): Iterable<Node> {
|
|
93
|
-
if (isDomElement(elm) && elm.tagName === "TEMPLATE") {
|
|
94
|
-
elm = (elm as HTMLTemplateElement).content;
|
|
95
|
-
}
|
|
96
|
-
for (let node = elm.firstChild; node; node = node.nextSibling) {
|
|
97
|
-
if (isDomElement(node)) {
|
|
98
|
-
if (node.tagName === "DIV" || node.tagName === "TEMPLATE") {
|
|
99
|
-
yield* visitDom(node);
|
|
100
|
-
} else {
|
|
101
|
-
yield node;
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
yield node;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
77
|
export function* visitSectionNodes(section: DomSection): Iterable<Node> {
|
|
110
78
|
if (section.header) yield section.header;
|
|
111
79
|
yield* section.content;
|
|
@@ -133,78 +101,6 @@ export function findSection(
|
|
|
133
101
|
}
|
|
134
102
|
}
|
|
135
103
|
|
|
136
|
-
function isDomElement(node: Node): node is HTMLElement {
|
|
137
|
-
return node.nodeType === Node.ELEMENT_NODE;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function newAligner<T extends unknown[] = []>(context: {
|
|
141
|
-
enter: (level: number, ...params: T) => void;
|
|
142
|
-
exit: (level: number, ...params: T) => void;
|
|
143
|
-
}) {
|
|
144
|
-
let level = -1;
|
|
145
|
-
return (newLevel: number, ...params: T) => {
|
|
146
|
-
while (level >= 0 && level >= newLevel) {
|
|
147
|
-
context.exit(level--, ...params);
|
|
148
|
-
}
|
|
149
|
-
while (level < newLevel) {
|
|
150
|
-
context.enter(++level, ...params);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function* toDomNodesIterator(root: Node | Iterable<Node>): Iterable<Node> {
|
|
156
|
-
yield* root instanceof Node ? visitDom(root) : root;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function buildSections(root: HTMLElement | Iterable<Node>): DomSection {
|
|
160
|
-
const nodes = toDomNodesIterator(root);
|
|
161
|
-
let top: DomSection = null!;
|
|
162
|
-
let peek: DomSection = null!;
|
|
163
|
-
const stack: DomSection[] = [];
|
|
164
|
-
const aligner = newAligner<[HTMLElement?]>({
|
|
165
|
-
enter(level, header) {
|
|
166
|
-
const section: DomSection = {
|
|
167
|
-
header,
|
|
168
|
-
content: [],
|
|
169
|
-
children: [],
|
|
170
|
-
};
|
|
171
|
-
top = top || section;
|
|
172
|
-
peek && peek.children.push(section);
|
|
173
|
-
peek = section;
|
|
174
|
-
stack.push(section);
|
|
175
|
-
},
|
|
176
|
-
exit() {
|
|
177
|
-
stack.pop();
|
|
178
|
-
peek = stack[stack.length - 1];
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
aligner(0);
|
|
182
|
-
let currentHeaderLevel = 0;
|
|
183
|
-
let currentLevel = 0;
|
|
184
|
-
for (const node of nodes) {
|
|
185
|
-
if (isDomElement(node)) {
|
|
186
|
-
if (/^H[1-6]$/i.test(node.tagName)) {
|
|
187
|
-
const headerLevel = +node.tagName.replace(/^H/, "");
|
|
188
|
-
currentLevel =
|
|
189
|
-
headerLevel > currentHeaderLevel
|
|
190
|
-
? currentLevel + 1
|
|
191
|
-
: headerLevel < currentHeaderLevel
|
|
192
|
-
? currentLevel - 1
|
|
193
|
-
: currentLevel;
|
|
194
|
-
currentHeaderLevel = headerLevel;
|
|
195
|
-
aligner(currentLevel, node);
|
|
196
|
-
} else {
|
|
197
|
-
peek.content.push(node);
|
|
198
|
-
}
|
|
199
|
-
} else {
|
|
200
|
-
peek.content.push(node);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return top.content.length === 0 && top.children.length === 1 && !top.header
|
|
204
|
-
? top.children[0]
|
|
205
|
-
: top;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
104
|
export function renderSections({
|
|
209
105
|
section,
|
|
210
106
|
deep = (s) => true,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function newTreeBuilder<T extends unknown[] = []>(context: {
|
|
2
|
+
enter: (level: number, ...params: T) => void;
|
|
3
|
+
exit: (level: number, ...params: T) => void;
|
|
4
|
+
}) {
|
|
5
|
+
let level = -1;
|
|
6
|
+
return (newLevel: number, ...params: T) => {
|
|
7
|
+
while (level >= 0 && level >= newLevel) {
|
|
8
|
+
context.exit(level--, ...params);
|
|
9
|
+
}
|
|
10
|
+
while (level < newLevel) {
|
|
11
|
+
context.enter(++level, ...params);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -53,7 +53,7 @@ export function buildStateDetailsStyle({
|
|
|
53
53
|
fontFamily: "var(--state-label-font-family, sans-serif)",
|
|
54
54
|
fontSize: "var(--state-label-font-size, 14px)",
|
|
55
55
|
},
|
|
56
|
-
".state-
|
|
56
|
+
".state-details__content": {
|
|
57
57
|
display: "flex",
|
|
58
58
|
flexDirection: "column",
|
|
59
59
|
gap: "0.5em",
|
|
@@ -21,10 +21,13 @@ export function buildStatechartsPanel({
|
|
|
21
21
|
` <details class="state-details" data-state-id="${statechart.id}">`
|
|
22
22
|
);
|
|
23
23
|
println(
|
|
24
|
-
` <summary class="state-details__label">${statechart.
|
|
24
|
+
` <summary class="state-details__label">${statechart.text ?? statechart.state}</summary>`
|
|
25
25
|
);
|
|
26
26
|
println(
|
|
27
|
-
` <div class="state-
|
|
27
|
+
` <div class="state-details__content">`
|
|
28
|
+
);
|
|
29
|
+
println(
|
|
30
|
+
` <div class="state-details__chart">`
|
|
28
31
|
);
|
|
29
32
|
const printChildren = (str: string) => println(` ${str}`);
|
|
30
33
|
buildStatechartSvg({
|
|
@@ -35,6 +38,10 @@ export function buildStatechartsPanel({
|
|
|
35
38
|
finalStateKey,
|
|
36
39
|
});
|
|
37
40
|
println(` </div>`);
|
|
41
|
+
println(
|
|
42
|
+
` <div class="state-details__description">`
|
|
43
|
+
);
|
|
44
|
+
println(` </div>`);
|
|
38
45
|
for (const child of statechart.children || []) {
|
|
39
46
|
render(child, printChildren);
|
|
40
47
|
}
|
|
@@ -19,6 +19,8 @@ export function buildCharts({
|
|
|
19
19
|
config,
|
|
20
20
|
newId,
|
|
21
21
|
getStateParams,
|
|
22
|
+
getStateLabel = (stateKey: string) =>
|
|
23
|
+
stateKey.replace(/([a-z])([A-Z])/g, "$1 $2"),
|
|
22
24
|
getTransitionParams,
|
|
23
25
|
padding = [5, 5],
|
|
24
26
|
vertical = false,
|
|
@@ -34,43 +36,81 @@ export function buildCharts({
|
|
|
34
36
|
padding?: Padding;
|
|
35
37
|
vertical?: boolean;
|
|
36
38
|
direction?: "tb" | "bt" | "lr" | "rl";
|
|
39
|
+
getStateLabel?: (stateKey: string) => string | undefined;
|
|
37
40
|
} & GraphParamsProvider): StateChart {
|
|
38
41
|
return buildChart(config);
|
|
39
42
|
|
|
40
43
|
function buildChart(
|
|
41
44
|
config: FsmStateConfig,
|
|
42
|
-
id: string = newId("s")
|
|
45
|
+
id: string = newId("s"),
|
|
46
|
+
stack: Array<FsmStateConfig> = [],
|
|
43
47
|
): StateChart {
|
|
44
|
-
|
|
45
|
-
lodash,
|
|
46
|
-
transitions: config.transitions || [],
|
|
47
|
-
newId,
|
|
48
|
-
getStateParams,
|
|
49
|
-
getTransitionParams,
|
|
50
|
-
padding,
|
|
51
|
-
initialStateKey,
|
|
52
|
-
finalStateKey,
|
|
53
|
-
vertical,
|
|
54
|
-
direction
|
|
55
|
-
});
|
|
56
|
-
const index = graph.nodes.reduce((index, node) => {
|
|
57
|
-
index[node.key as string] = node.id as string;
|
|
58
|
-
return index;
|
|
59
|
-
}, {} as Record<string, string>);
|
|
48
|
+
stack.push(config);
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
try {
|
|
51
|
+
const buildStateLabel = (state: string, key: string = state) => {
|
|
52
|
+
if (key === initialStateKey || key === finalStateKey) {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let result: string | undefined;
|
|
57
|
+
for (let i = stack.length - 1; result === undefined && i >= 0; i--) {
|
|
58
|
+
const conf = stack[i];
|
|
59
|
+
if (!conf.states) continue;
|
|
60
|
+
for (const subState of conf.states) {
|
|
61
|
+
if (subState.key !== state) continue;
|
|
62
|
+
result = subState.label ?? getStateLabel(state);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return result ?? getStateLabel(state) ?? state;
|
|
68
|
+
};
|
|
69
|
+
const graph = buildFlatCharts({
|
|
70
|
+
lodash,
|
|
71
|
+
transitions: config.transitions || [],
|
|
72
|
+
newId,
|
|
73
|
+
getStateParams: (state: string, key?: string) => {
|
|
74
|
+
const params = getStateParams(state, key);
|
|
75
|
+
delete params.state;
|
|
76
|
+
params.text = buildStateLabel(state, key);
|
|
77
|
+
return params;
|
|
78
|
+
},
|
|
79
|
+
getTransitionParams,
|
|
80
|
+
padding,
|
|
81
|
+
initialStateKey,
|
|
82
|
+
finalStateKey,
|
|
83
|
+
vertical,
|
|
84
|
+
direction,
|
|
85
|
+
});
|
|
86
|
+
const index = graph.nodes.reduce(
|
|
87
|
+
(index, node) => {
|
|
88
|
+
index[node.key as string] = node.id as string;
|
|
89
|
+
return index;
|
|
90
|
+
},
|
|
91
|
+
{} as Record<string, string>,
|
|
92
|
+
);
|
|
93
|
+
const text = config.label ?? getStateLabel(config.key) ?? config.key;
|
|
94
|
+
|
|
95
|
+
const children: StateChart[] = [];
|
|
96
|
+
if (config.states) {
|
|
97
|
+
for (const child of config.states) {
|
|
98
|
+
const childId = index[child.key];
|
|
99
|
+
children.push(buildChart(child, childId, [...stack]));
|
|
100
|
+
}
|
|
66
101
|
}
|
|
102
|
+
|
|
103
|
+
// const state = text;
|
|
104
|
+
return {
|
|
105
|
+
id,
|
|
106
|
+
key: config.key,
|
|
107
|
+
// state,
|
|
108
|
+
text,
|
|
109
|
+
...graph,
|
|
110
|
+
children,
|
|
111
|
+
};
|
|
112
|
+
} finally {
|
|
113
|
+
// stack.pop();
|
|
67
114
|
}
|
|
68
|
-
return {
|
|
69
|
-
id,
|
|
70
|
-
key: config.key,
|
|
71
|
-
state: config.key,
|
|
72
|
-
...graph,
|
|
73
|
-
children,
|
|
74
|
-
};
|
|
75
115
|
}
|
|
76
116
|
}
|
|
@@ -43,7 +43,7 @@ export class RuntimeStatechartApi {
|
|
|
43
43
|
}
|
|
44
44
|
isStatePanelOpen(stateId: string) {
|
|
45
45
|
const elm = this.element.querySelector(
|
|
46
|
-
`details[data-state-id="${stateId}"]
|
|
46
|
+
`details[data-state-id="${stateId}"]`,
|
|
47
47
|
) as HTMLDetailsElement;
|
|
48
48
|
return elm && !!elm.open;
|
|
49
49
|
}
|
|
@@ -68,15 +68,31 @@ export class RuntimeStatechartApi {
|
|
|
68
68
|
this.selectState(id);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
focusStates(...stateIds: string[]) {
|
|
73
|
+
for (let i = 0; i < stateIds.length; i++) {
|
|
74
|
+
const stateId = stateIds[i];
|
|
75
|
+
for (const tagName of ["details", "g"]) {
|
|
76
|
+
const elm = this.element.querySelector(
|
|
77
|
+
`${tagName}[data-state-id="${stateId}"]`,
|
|
78
|
+
);
|
|
79
|
+
elm?.scrollIntoView();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
71
83
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
setStateDescription(
|
|
85
|
+
stateId: string,
|
|
86
|
+
description: HTMLElement | string | undefined,
|
|
87
|
+
) {
|
|
88
|
+
const descriptionContainer = this.element.querySelector(
|
|
89
|
+
`details[data-state-id="${stateId}"] .state-details__description`,
|
|
90
|
+
);
|
|
91
|
+
if (descriptionContainer) {
|
|
92
|
+
descriptionContainer.innerHTML = "";
|
|
93
|
+
descriptionContainer.append(description ?? "");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
80
96
|
|
|
81
97
|
closeStatePanel(stateId: string) {
|
|
82
98
|
this._setStatePanelStatus(`details[data-state-id="${stateId}"]`, false);
|
|
@@ -138,11 +154,11 @@ export class RuntimeStatechartApi {
|
|
|
138
154
|
const bindListeners = (
|
|
139
155
|
index: Record<string, RuntimeListener>,
|
|
140
156
|
dataField: string,
|
|
141
|
-
eventType: string
|
|
157
|
+
eventType: string,
|
|
142
158
|
) => {
|
|
143
159
|
function newEventListener(
|
|
144
160
|
index: Record<string, RuntimeListener>,
|
|
145
|
-
elm: Element
|
|
161
|
+
elm: Element,
|
|
146
162
|
) {
|
|
147
163
|
return (ev: Event) => {
|
|
148
164
|
ev.preventDefault();
|
|
@@ -162,7 +178,7 @@ export class RuntimeStatechartApi {
|
|
|
162
178
|
const eventListener = newEventListener(index, elm);
|
|
163
179
|
elm.addEventListener(eventType, eventListener);
|
|
164
180
|
this._addListener(this._registry, () =>
|
|
165
|
-
elm.removeEventListener(eventType, eventListener)
|
|
181
|
+
elm.removeEventListener(eventType, eventListener),
|
|
166
182
|
);
|
|
167
183
|
});
|
|
168
184
|
};
|
|
@@ -170,7 +186,7 @@ export class RuntimeStatechartApi {
|
|
|
170
186
|
bindListeners(
|
|
171
187
|
this._transitionsClickListeners,
|
|
172
188
|
"data-transition-id",
|
|
173
|
-
"click"
|
|
189
|
+
"click",
|
|
174
190
|
);
|
|
175
191
|
}
|
|
176
192
|
|
|
@@ -185,20 +201,20 @@ export class RuntimeStatechartApi {
|
|
|
185
201
|
this._applyStateModifier(
|
|
186
202
|
cssSelector,
|
|
187
203
|
modifier,
|
|
188
|
-
(elm, cls) => elm && elm.classList.add(cls)
|
|
204
|
+
(elm, cls) => elm && elm.classList.add(cls),
|
|
189
205
|
);
|
|
190
206
|
}
|
|
191
207
|
_removeStateModifier(cssSelector: string, modifier: string = "selected") {
|
|
192
208
|
this._applyStateModifier(
|
|
193
209
|
cssSelector,
|
|
194
210
|
modifier,
|
|
195
|
-
(elm, cls) => elm && elm.classList.remove(cls)
|
|
211
|
+
(elm, cls) => elm && elm.classList.remove(cls),
|
|
196
212
|
);
|
|
197
213
|
}
|
|
198
214
|
_applyStateModifier(
|
|
199
215
|
cssSelector: string,
|
|
200
216
|
modifier: string = "selected",
|
|
201
|
-
apply: (elm: Element | null, className: string) => void
|
|
217
|
+
apply: (elm: Element | null, className: string) => void,
|
|
202
218
|
) {
|
|
203
219
|
const detailsModifier = `state-details--${modifier}`;
|
|
204
220
|
const stateLabelsModifier = `state-details__label--${modifier}`;
|
|
@@ -218,23 +234,23 @@ export class RuntimeStatechartApi {
|
|
|
218
234
|
this._applyTransitionsModifier(
|
|
219
235
|
cssSelector,
|
|
220
236
|
modifier,
|
|
221
|
-
(elm, cls) => elm && elm.classList.add(cls)
|
|
237
|
+
(elm, cls) => elm && elm.classList.add(cls),
|
|
222
238
|
);
|
|
223
239
|
}
|
|
224
240
|
_removeTransitionsModifier(
|
|
225
241
|
cssSelector: string,
|
|
226
|
-
modifier: string = "selected"
|
|
242
|
+
modifier: string = "selected",
|
|
227
243
|
) {
|
|
228
244
|
this._applyTransitionsModifier(
|
|
229
245
|
cssSelector,
|
|
230
246
|
modifier,
|
|
231
|
-
(elm, cls) => elm && elm.classList.remove(cls)
|
|
247
|
+
(elm, cls) => elm && elm.classList.remove(cls),
|
|
232
248
|
);
|
|
233
249
|
}
|
|
234
250
|
_applyTransitionsModifier(
|
|
235
251
|
cssSelector: string,
|
|
236
252
|
modifier: string = "selected",
|
|
237
|
-
apply: (elm: Element | null, className: string) => void
|
|
253
|
+
apply: (elm: Element | null, className: string) => void,
|
|
238
254
|
) {
|
|
239
255
|
const transitionModifier = `transition--${modifier}`;
|
|
240
256
|
const transitionLineModifier = `transition__line--${modifier}`;
|
|
@@ -11,9 +11,22 @@ export class StateChartIndex {
|
|
|
11
11
|
_nodesParentsIndex: Record<string, string> = {};
|
|
12
12
|
_edgeIndex: Record<string, StateGraphEdge> = {};
|
|
13
13
|
_edgesParentsIndex: Record<string, string> = {};
|
|
14
|
+
initialStateKey: string;
|
|
15
|
+
finalStateKey: string;
|
|
14
16
|
|
|
15
|
-
constructor({
|
|
17
|
+
constructor({
|
|
18
|
+
statechart,
|
|
19
|
+
initialStateKey = "<initial>",
|
|
20
|
+
finalStateKey = "<final>",
|
|
21
|
+
}: {
|
|
22
|
+
statechart: StateChart;
|
|
23
|
+
initialStateKey?: string;
|
|
24
|
+
finalStateKey?: string;
|
|
25
|
+
}) {
|
|
16
26
|
this.statechart = statechart;
|
|
27
|
+
this.initialStateKey = initialStateKey;
|
|
28
|
+
this.finalStateKey = finalStateKey;
|
|
29
|
+
|
|
17
30
|
this._buildIndexes();
|
|
18
31
|
}
|
|
19
32
|
|
|
@@ -44,6 +57,31 @@ export class StateChartIndex {
|
|
|
44
57
|
return this._edgeIndex[id];
|
|
45
58
|
}
|
|
46
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Returns a list of all nodes in this chart.
|
|
62
|
+
* @return a list of all nodes
|
|
63
|
+
*/
|
|
64
|
+
getAllNodes(): StateGraphNode[] {
|
|
65
|
+
return [
|
|
66
|
+
...new Set([
|
|
67
|
+
...Object.values(this._chartsIndex),
|
|
68
|
+
...Object.values(this._nodesIndex),
|
|
69
|
+
]),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Returns a list of all non-empty state nodes in this chart.
|
|
75
|
+
* The result excludes nodes for initial and fina states.
|
|
76
|
+
* @return a list of all non-empty state nodes
|
|
77
|
+
*/
|
|
78
|
+
getStateNodes(): StateGraphNode[] {
|
|
79
|
+
return this.getAllNodes().filter(
|
|
80
|
+
(node) =>
|
|
81
|
+
node.key !== this.initialStateKey && node.key !== this.finalStateKey,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
47
85
|
/**
|
|
48
86
|
* Transforms a stack of state keys to the corresponding identifiers.
|
|
49
87
|
* If a key in the stack was not found then this method returns
|
|
@@ -96,7 +134,7 @@ export class StateChartIndex {
|
|
|
96
134
|
if (!state || !stack.length) return;
|
|
97
135
|
const parent = stack[stack.length - 1];
|
|
98
136
|
const t = parent.edges.find(
|
|
99
|
-
(t: StateGraphEdge) => t.from === state.key && t.event === event
|
|
137
|
+
(t: StateGraphEdge) => t.from === state.key && t.event === event,
|
|
100
138
|
);
|
|
101
139
|
return t;
|
|
102
140
|
}
|
|
@@ -140,7 +178,7 @@ export class StateChartIndex {
|
|
|
140
178
|
* @returns an array with two identifiers - [sourceStateId, targetStateId] - the source state id and target state id
|
|
141
179
|
*/
|
|
142
180
|
getTransitionStateIds(
|
|
143
|
-
transition?: StateGraphEdge
|
|
181
|
+
transition?: StateGraphEdge,
|
|
144
182
|
): [string | undefined, string | undefined] {
|
|
145
183
|
const result: [string | undefined, string | undefined] = [
|
|
146
184
|
undefined,
|