@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.
- package/CHANGELOG.md +1 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/StateChart-DNGQd1XG.d.ts +53 -0
- package/dist/chunk-3GGM3ZWL.js +2685 -0
- package/dist/chunk-NXWOFQMO.js +10 -0
- package/dist/chunk-TPNWHU7Y.js +437 -0
- package/dist/index-layout.d.ts +96 -0
- package/dist/index-layout.js +2 -0
- package/dist/index-svg.d.ts +25 -0
- package/dist/index-svg.js +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +60 -0
- package/src/FsmStateConfig.ts +5 -0
- package/src/dagre/acyclic.d.ts +2 -0
- package/src/dagre/acyclic.js +60 -0
- package/src/dagre/add-border-segments.d.ts +1 -0
- package/src/dagre/add-border-segments.js +36 -0
- package/src/dagre/coordinate-system.d.ts +2 -0
- package/src/dagre/coordinate-system.js +75 -0
- package/src/dagre/data/list.d.ts +12 -0
- package/src/dagre/data/list.js +55 -0
- package/src/dagre/debug.d.ts +2 -0
- package/src/dagre/debug.js +32 -0
- package/src/dagre/greedy-fas.d.ts +1 -0
- package/src/dagre/greedy-fas.js +126 -0
- package/src/dagre/index.d.ts +5 -0
- package/src/dagre/index.js +6 -0
- package/src/dagre/layout.d.ts +1 -0
- package/src/dagre/layout.js +466 -0
- package/src/dagre/nesting-graph.d.ts +2 -0
- package/src/dagre/nesting-graph.js +137 -0
- package/src/dagre/normalize.d.ts +2 -0
- package/src/dagre/normalize.js +91 -0
- package/src/dagre/order/add-subgraph-constraints.d.ts +1 -0
- package/src/dagre/order/add-subgraph-constraints.js +53 -0
- package/src/dagre/order/barycenter.d.ts +15 -0
- package/src/dagre/order/barycenter.js +31 -0
- package/src/dagre/order/build-layer-graph.d.ts +2 -0
- package/src/dagre/order/build-layer-graph.js +76 -0
- package/src/dagre/order/cross-count.d.ts +1 -0
- package/src/dagre/order/cross-count.js +82 -0
- package/src/dagre/order/index.d.ts +1 -0
- package/src/dagre/order/index.js +77 -0
- package/src/dagre/order/init-order.d.ts +1 -0
- package/src/dagre/order/init-order.js +44 -0
- package/src/dagre/order/resolve-conflicts.d.ts +1 -0
- package/src/dagre/order/resolve-conflicts.js +128 -0
- package/src/dagre/order/sort-subgraph.d.ts +8 -0
- package/src/dagre/order/sort-subgraph.js +78 -0
- package/src/dagre/order/sort.d.ts +6 -0
- package/src/dagre/order/sort.js +59 -0
- package/src/dagre/parent-dummy-chains.d.ts +1 -0
- package/src/dagre/parent-dummy-chains.js +86 -0
- package/src/dagre/position/bk.d.ts +31 -0
- package/src/dagre/position/bk.js +423 -0
- package/src/dagre/position/index.d.ts +1 -0
- package/src/dagre/position/index.js +31 -0
- package/src/dagre/rank/feasible-tree.d.ts +2 -0
- package/src/dagre/rank/feasible-tree.js +87 -0
- package/src/dagre/rank/index.d.ts +1 -0
- package/src/dagre/rank/index.js +52 -0
- package/src/dagre/rank/network-simplex.d.ts +16 -0
- package/src/dagre/rank/network-simplex.js +236 -0
- package/src/dagre/rank/util.d.ts +2 -0
- package/src/dagre/rank/util.js +63 -0
- package/src/dagre/util.d.ts +27 -0
- package/src/dagre/util.js +250 -0
- package/src/graphlib/alg/components.d.ts +1 -0
- package/src/graphlib/alg/components.js +27 -0
- package/src/graphlib/alg/dfs.d.ts +1 -0
- package/src/graphlib/alg/dfs.js +46 -0
- package/src/graphlib/alg/dijkstra-all.d.ts +1 -0
- package/src/graphlib/alg/dijkstra-all.js +14 -0
- package/src/graphlib/alg/dijkstra.d.ts +1 -0
- package/src/graphlib/alg/dijkstra.js +65 -0
- package/src/graphlib/alg/find-cycles.d.ts +1 -0
- package/src/graphlib/alg/find-cycles.js +10 -0
- package/src/graphlib/alg/floyd-warshall.d.ts +1 -0
- package/src/graphlib/alg/floyd-warshall.js +55 -0
- package/src/graphlib/alg/index.d.ts +24 -0
- package/src/graphlib/alg/index.js +25 -0
- package/src/graphlib/alg/is-acyclic.d.ts +1 -0
- package/src/graphlib/alg/is-acyclic.js +15 -0
- package/src/graphlib/alg/postorder.d.ts +1 -0
- package/src/graphlib/alg/postorder.js +7 -0
- package/src/graphlib/alg/preorder.d.ts +1 -0
- package/src/graphlib/alg/preorder.js +7 -0
- package/src/graphlib/alg/prim.d.ts +2 -0
- package/src/graphlib/alg/prim.js +52 -0
- package/src/graphlib/alg/tarjan.d.ts +1 -0
- package/src/graphlib/alg/tarjan.js +47 -0
- package/src/graphlib/alg/topsort.d.ts +5 -0
- package/src/graphlib/alg/topsort.js +36 -0
- package/src/graphlib/data/priority-queue.d.ts +59 -0
- package/src/graphlib/data/priority-queue.js +152 -0
- package/src/graphlib/graph.d.ts +55 -0
- package/src/graphlib/graph.js +510 -0
- package/src/graphlib/index.d.ts +3 -0
- package/src/graphlib/index.js +7 -0
- package/src/graphlib/json.d.ts +16 -0
- package/src/graphlib/json.js +63 -0
- package/src/index-layout.ts +1 -0
- package/src/index-svg.ts +1 -0
- package/src/index.ts +2 -0
- package/src/layout/Dimensions.ts +8 -0
- package/src/layout/GraphParamsProvider.ts +17 -0
- package/src/layout/Padding.ts +10 -0
- package/src/layout/Position.ts +4 -0
- package/src/layout/StateChart.ts +8 -0
- package/src/layout/StateGraphEdge.ts +14 -0
- package/src/layout/StateGraphNode.ts +12 -0
- package/src/layout/Transition.ts +5 -0
- package/src/layout/TransitionsGraph.ts +13 -0
- package/src/layout/buildCharts.ts +74 -0
- package/src/layout/buildFlatCharts.ts +142 -0
- package/src/layout/getGraphParamsProvider.ts +63 -0
- package/src/layout/getLabelDimensions.ts +59 -0
- package/src/layout/index.ts +15 -0
- package/src/lodash-es/_deleteme.js +291 -0
- package/src/lodash-es/index.ts +9 -0
- package/src/lodash-es/types.ts +35 -0
- package/src/svg/buildStatechartCss.ts +120 -0
- package/src/svg/buildStatechartSvg.ts +256 -0
- package/src/svg/buildStatechartsPanel.ts +38 -0
- package/src/svg/index.ts +3 -0
- package/src/utils/BasisCurve.ts +89 -0
- package/src/utils/getPadding.ts +13 -0
- package/src/utils/serializeAttrs.ts +28 -0
- package/src/utils/serializeStyle.ts +11 -0
- package/src/utils/toKebabCase.ts +18 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/utils/getPadding.ts
|
|
2
|
+
function getPadding(padding) {
|
|
3
|
+
if (typeof padding === "number")
|
|
4
|
+
return [padding, padding, padding, padding];
|
|
5
|
+
if (padding.length === 2)
|
|
6
|
+
return [padding[0], padding[1], padding[0], padding[1]];
|
|
7
|
+
return padding;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { getPadding };
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { getPadding } from './chunk-NXWOFQMO.js';
|
|
2
|
+
|
|
3
|
+
// src/svg/buildStatechartCss.ts
|
|
4
|
+
function buildStatechartCss({ prefix = "" } = {}) {
|
|
5
|
+
return `
|
|
6
|
+
--state--fill: white;
|
|
7
|
+
--state--stroke-color: silver;
|
|
8
|
+
--state--stroke-width: 1px;
|
|
9
|
+
|
|
10
|
+
--state-initial--fill: var(--state--stroke-color, silver);
|
|
11
|
+
--state-initial--stroke-color: var(--state--stroke-color, none);
|
|
12
|
+
--state-initial--stroke-width: var(--state--stroke-width, 0);
|
|
13
|
+
|
|
14
|
+
--state-final--fill: var(--state--fill, white);
|
|
15
|
+
--state-final--stroke-color: var(--state--stroke-color, silver);
|
|
16
|
+
--state-final--stroke-width: var(--state--stroke-width, 1px);
|
|
17
|
+
|
|
18
|
+
--state-label--color: gray;
|
|
19
|
+
--state-label--font-size: 14px;
|
|
20
|
+
--state-label--font-family: sans-serif;
|
|
21
|
+
|
|
22
|
+
--transition--fill: rgba(255,255,255,0.7);
|
|
23
|
+
--transition--stroke-color: none;
|
|
24
|
+
--transition--stroke-width: 1;
|
|
25
|
+
|
|
26
|
+
--transition-line--stroke-color: silver;
|
|
27
|
+
--transition-line--stroke-width: 1px;
|
|
28
|
+
|
|
29
|
+
--transition-label--color: gray;
|
|
30
|
+
--transition-label--font-size: 12px;
|
|
31
|
+
--transition-label--font-family: sans-serif;
|
|
32
|
+
|
|
33
|
+
--transition-marker--fill: var(--transition-line--stroke-color, gray);
|
|
34
|
+
|
|
35
|
+
.state-details {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.state-details {
|
|
40
|
+
background-color: var(--state--fill, white);
|
|
41
|
+
border: var(--state--stroke-width, 1px) solid var(--state--stroke-color, silver);
|
|
42
|
+
border-radius: 0.5em;
|
|
43
|
+
padding: 0.5em;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.state-details .state-label {
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
color: var(--transition-label--color, gray);
|
|
49
|
+
font-family: var(--state-label--font-family, sans-serif);
|
|
50
|
+
font-size: var(--state-label--font-size, 14px);
|
|
51
|
+
}
|
|
52
|
+
.state-details .state-description {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
gap: 0.5em;
|
|
56
|
+
}
|
|
57
|
+
.state-details .state-chart-container {
|
|
58
|
+
max-width: 100%;
|
|
59
|
+
overflow: auto;
|
|
60
|
+
scrollbar-width: thin;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.state-details .statechart {
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.state-details .statechart .transition-marker {
|
|
68
|
+
fill : var(--transition-marker--fill, silver);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.state-details .statechart .transitions {
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.state-details .statechart .transitions .transition .transition-line {
|
|
75
|
+
stroke: var(--transition-line--stroke-color, silver);
|
|
76
|
+
stroke-width: var(--transition-line--stroke-width, 1px);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.state-details .statechart .transitions .transition .transition-box {
|
|
80
|
+
fill: var(--transition--fill, rgba(255,255,255,0.7));
|
|
81
|
+
stroke: var(--transition--stroke-color, none);
|
|
82
|
+
stroke-width: var(--transition--stroke-width, 1);
|
|
83
|
+
border-radius: 0.5em;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.state-details .statechart .transitions .transition .transition-label {
|
|
87
|
+
fill: var(--transition-label--color, gray);
|
|
88
|
+
font-size: var(--transition-label--font-size, 12px);
|
|
89
|
+
font-family: var(--transition-label--font-family, sans-serif);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* ------------------------------------------ */
|
|
93
|
+
.state-details .statechart .states .state {
|
|
94
|
+
}
|
|
95
|
+
.state-details .statechart .states .state .state-box {
|
|
96
|
+
fill: var(--state--fill, white);
|
|
97
|
+
stroke: var(--state--stroke-color, silver);
|
|
98
|
+
stroke-width: var(--state--stroke-width, 1px);
|
|
99
|
+
border-radius: 0.5em;
|
|
100
|
+
}
|
|
101
|
+
.state-details .statechart .states .state .state-initial {
|
|
102
|
+
fill: var(--state-initial--fill, gray);
|
|
103
|
+
stroke: var(--state-initial--stroke-color, none);
|
|
104
|
+
stroke-width: var(--state-initial--stroke-width, 0);
|
|
105
|
+
}
|
|
106
|
+
.state-details .statechart .states .state .state-final {
|
|
107
|
+
fill: var(--state-final--fill, white);
|
|
108
|
+
stroke: var(--state-final--stroke-color, silver);
|
|
109
|
+
stroke-width: var(--state-final--stroke-width, 2);
|
|
110
|
+
}
|
|
111
|
+
.state-details .statechart .states .state .state-final.inner {
|
|
112
|
+
fill: var(--state-final--stroke-color, silver);
|
|
113
|
+
stroke: none;
|
|
114
|
+
stroke-width: 0;
|
|
115
|
+
}
|
|
116
|
+
.state-details .statechart .states .state .state-label {
|
|
117
|
+
fill: var(--state-label--color, gray);
|
|
118
|
+
font-size: var(--state-label--font-size, 14px);
|
|
119
|
+
font-family: var(--state-label--font-family, sans-serif);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/utils/serializeAttrs.ts
|
|
126
|
+
function serializeAttrs(attrs = {}, {
|
|
127
|
+
prefix = "",
|
|
128
|
+
transform = false
|
|
129
|
+
} = {}) {
|
|
130
|
+
const result = [];
|
|
131
|
+
for (const [key, value] of Object.entries(attrs || {}).sort(
|
|
132
|
+
([a], [b]) => a > b ? 1 : -1
|
|
133
|
+
)) {
|
|
134
|
+
if (value === void 0 || value === null || value === "")
|
|
135
|
+
continue;
|
|
136
|
+
result.push(
|
|
137
|
+
`${prefix}${escapeValue(key)}${"=" + JSON.stringify(escapeValue(transform ? toKebabCase(value) : value))}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return result.join(" ");
|
|
141
|
+
function escapeValue(value) {
|
|
142
|
+
return String(value).replaceAll(/</gim, "<").replaceAll(/>/gim, ">").replaceAll(/"/gim, "&qout;");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/utils/BasisCurve.ts
|
|
147
|
+
var BasisCurve = class {
|
|
148
|
+
_context;
|
|
149
|
+
_x0 = 0;
|
|
150
|
+
_y0 = 0;
|
|
151
|
+
_x1 = 0;
|
|
152
|
+
_y1 = 0;
|
|
153
|
+
_point = 0;
|
|
154
|
+
_line = 0;
|
|
155
|
+
constructor(context) {
|
|
156
|
+
this._context = context;
|
|
157
|
+
}
|
|
158
|
+
$point(x, y) {
|
|
159
|
+
this._context.bezierCurveTo(
|
|
160
|
+
(2 * this._x0 + this._x1) / 3,
|
|
161
|
+
(2 * this._y0 + this._y1) / 3,
|
|
162
|
+
(this._x0 + 2 * this._x1) / 3,
|
|
163
|
+
(this._y0 + 2 * this._y1) / 3,
|
|
164
|
+
(this._x0 + 4 * this._x1 + x) / 6,
|
|
165
|
+
(this._y0 + 4 * this._y1 + y) / 6
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
areaStart() {
|
|
169
|
+
this._line = 0;
|
|
170
|
+
}
|
|
171
|
+
areaEnd() {
|
|
172
|
+
this._line = NaN;
|
|
173
|
+
}
|
|
174
|
+
lineStart() {
|
|
175
|
+
this._x0 = this._x1 = this._y0 = this._y1 = NaN;
|
|
176
|
+
this._point = 0;
|
|
177
|
+
}
|
|
178
|
+
lineEnd() {
|
|
179
|
+
switch (this._point) {
|
|
180
|
+
case 3:
|
|
181
|
+
this.$point(this._x1, this._y1);
|
|
182
|
+
case 2:
|
|
183
|
+
this._context.lineTo(this._x1, this._y1);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
if (this._line || this._line !== 0 && this._point === 1)
|
|
187
|
+
this._context.closePath();
|
|
188
|
+
this._line = 1 - this._line;
|
|
189
|
+
}
|
|
190
|
+
point(x, y) {
|
|
191
|
+
x = +x, y = +y;
|
|
192
|
+
switch (this._point) {
|
|
193
|
+
case 0:
|
|
194
|
+
this._point = 1;
|
|
195
|
+
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
|
|
196
|
+
break;
|
|
197
|
+
case 1:
|
|
198
|
+
this._point = 2;
|
|
199
|
+
break;
|
|
200
|
+
case 2:
|
|
201
|
+
this._point = 3;
|
|
202
|
+
this._context.lineTo(
|
|
203
|
+
(5 * this._x0 + this._x1) / 6,
|
|
204
|
+
(5 * this._y0 + this._y1) / 6
|
|
205
|
+
);
|
|
206
|
+
default:
|
|
207
|
+
this.$point(x, y);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
this._x0 = this._x1, this._x1 = x;
|
|
211
|
+
this._y0 = this._y1, this._y1 = y;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/svg/buildStatechartSvg.ts
|
|
216
|
+
function getBoxParams({
|
|
217
|
+
x,
|
|
218
|
+
y,
|
|
219
|
+
width,
|
|
220
|
+
height,
|
|
221
|
+
padding = 0
|
|
222
|
+
}) {
|
|
223
|
+
const [top, right, bottom, left] = getPadding(padding);
|
|
224
|
+
const h = Math.ceil(height - top - bottom);
|
|
225
|
+
return {
|
|
226
|
+
x: x + left,
|
|
227
|
+
y: y - bottom - h,
|
|
228
|
+
width: Math.ceil(width - left - right),
|
|
229
|
+
height: h
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function buildStatechartSvg({
|
|
233
|
+
graph: { width, height, nodes, edges },
|
|
234
|
+
newId,
|
|
235
|
+
stateFontSize = 14,
|
|
236
|
+
transitionFontSize = 12,
|
|
237
|
+
println,
|
|
238
|
+
initialStateKey = "<initial>",
|
|
239
|
+
finalStateKey = "<final>"
|
|
240
|
+
}) {
|
|
241
|
+
width = Math.ceil(width);
|
|
242
|
+
height = Math.ceil(height);
|
|
243
|
+
println(
|
|
244
|
+
`<svg ${serializeAttrs({
|
|
245
|
+
class: "statechart",
|
|
246
|
+
width,
|
|
247
|
+
height,
|
|
248
|
+
viewbox: `0 0 ${width} ${height}`,
|
|
249
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
250
|
+
})}>`
|
|
251
|
+
);
|
|
252
|
+
println(`<g class="transitions">`);
|
|
253
|
+
const markerAttrs = serializeAttrs({
|
|
254
|
+
refX: 19,
|
|
255
|
+
refY: 7,
|
|
256
|
+
markerWidth: 20,
|
|
257
|
+
markerHeight: 14,
|
|
258
|
+
orient: "auto",
|
|
259
|
+
class: "transition-marker",
|
|
260
|
+
stroke: "none",
|
|
261
|
+
fill: "currentColor"
|
|
262
|
+
});
|
|
263
|
+
for (const edge of edges) {
|
|
264
|
+
const { id, points, event: text } = edge;
|
|
265
|
+
println(` <g class="transition" data-transition-id="${id}">`);
|
|
266
|
+
const markerId = newId("marker");
|
|
267
|
+
println(` <defs>`);
|
|
268
|
+
println(` <marker id="${markerId}" ${markerAttrs}>`);
|
|
269
|
+
println(` <path d="M 19,7 L9,13 L14,7 L9,1 Z"></path>`);
|
|
270
|
+
println(` </marker>`);
|
|
271
|
+
println(` </defs>`);
|
|
272
|
+
const path = [];
|
|
273
|
+
const round = (...n) => n.map(Math.round);
|
|
274
|
+
const base = new BasisCurve({
|
|
275
|
+
bezierCurveTo(x0, y0, x1, y1, x2, y2) {
|
|
276
|
+
path.push("C" + round(x0, y0, x1, y1, x2, y2).join(","));
|
|
277
|
+
},
|
|
278
|
+
moveTo(x, y) {
|
|
279
|
+
path.push("M" + round(x, y).join(","));
|
|
280
|
+
},
|
|
281
|
+
lineTo(x, y) {
|
|
282
|
+
path.push("L" + round(x, y).join(","));
|
|
283
|
+
},
|
|
284
|
+
closePath() {
|
|
285
|
+
path.push("Z");
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
base.lineStart();
|
|
289
|
+
for (const point of points) {
|
|
290
|
+
base.point(point.x, point.y);
|
|
291
|
+
}
|
|
292
|
+
base.lineEnd();
|
|
293
|
+
const serializedAttrs = serializeAttrs({
|
|
294
|
+
class: "transition-line",
|
|
295
|
+
"transition-id": id,
|
|
296
|
+
fill: "none",
|
|
297
|
+
stroke: "currentColor",
|
|
298
|
+
strokeWidth: "1px",
|
|
299
|
+
d: path.join(""),
|
|
300
|
+
"marker-end": `url(#${markerId})`
|
|
301
|
+
});
|
|
302
|
+
println(` <path ${serializedAttrs} />`);
|
|
303
|
+
if (text) {
|
|
304
|
+
const { x, y, width: width2, height: height2 } = getBoxParams({
|
|
305
|
+
...edge,
|
|
306
|
+
padding: 0
|
|
307
|
+
});
|
|
308
|
+
const rectAttrs = serializeAttrs({
|
|
309
|
+
x,
|
|
310
|
+
y: y - transitionFontSize,
|
|
311
|
+
width: width2,
|
|
312
|
+
height: height2,
|
|
313
|
+
rx: Math.ceil(transitionFontSize / 2),
|
|
314
|
+
ry: Math.ceil(transitionFontSize / 2),
|
|
315
|
+
fill: "rgba(255,255,255,0.7)",
|
|
316
|
+
stroke: "none",
|
|
317
|
+
strokeWidth: "1",
|
|
318
|
+
class: "transition-box"
|
|
319
|
+
});
|
|
320
|
+
println(` <rect ${rectAttrs} />`);
|
|
321
|
+
const textAttrs = serializeAttrs({
|
|
322
|
+
class: "transition-label",
|
|
323
|
+
"text-anchor": "middle",
|
|
324
|
+
fill: "currentColor",
|
|
325
|
+
x: Math.round(x + width2 / 2),
|
|
326
|
+
y: Math.round(y + height2 / 2 - transitionFontSize / 2)
|
|
327
|
+
});
|
|
328
|
+
println(` <text ${textAttrs}>${text}</text>`);
|
|
329
|
+
}
|
|
330
|
+
println(` </g>`);
|
|
331
|
+
}
|
|
332
|
+
println("</g>");
|
|
333
|
+
const shift = stateFontSize / 4;
|
|
334
|
+
const initialStateRadius = 6;
|
|
335
|
+
const finalStateRadius = initialStateRadius / 2;
|
|
336
|
+
println(`<g class="states">`);
|
|
337
|
+
for (const node of nodes) {
|
|
338
|
+
const { key, id, state: text } = node;
|
|
339
|
+
const { x, y, width: width2, height: height2 } = getBoxParams({ ...node, padding: shift });
|
|
340
|
+
println(` <g class="state" data-state-id="${id}">`);
|
|
341
|
+
if (key === initialStateKey || key === finalStateKey) {
|
|
342
|
+
const cx = Math.round(x + width2 / 2);
|
|
343
|
+
const cy = Math.round(y + height2 / 2);
|
|
344
|
+
if (key === initialStateKey) {
|
|
345
|
+
const serializedAttrs = serializeAttrs({
|
|
346
|
+
class: "state-initial",
|
|
347
|
+
cx,
|
|
348
|
+
cy,
|
|
349
|
+
r: initialStateRadius,
|
|
350
|
+
fill: "none",
|
|
351
|
+
stroke: "currentColor",
|
|
352
|
+
strokeWidth: "2"
|
|
353
|
+
});
|
|
354
|
+
println(` <circle ${serializedAttrs} />`);
|
|
355
|
+
} else {
|
|
356
|
+
let serializedAttrs = serializeAttrs({
|
|
357
|
+
class: "state-final",
|
|
358
|
+
cx,
|
|
359
|
+
cy,
|
|
360
|
+
r: initialStateRadius,
|
|
361
|
+
fill: "none",
|
|
362
|
+
stroke: "currentColor",
|
|
363
|
+
strokeWidth: 2
|
|
364
|
+
});
|
|
365
|
+
println(` <circle ${serializedAttrs} />`);
|
|
366
|
+
serializedAttrs = serializeAttrs({
|
|
367
|
+
class: "state-final inner",
|
|
368
|
+
cx,
|
|
369
|
+
cy,
|
|
370
|
+
r: finalStateRadius,
|
|
371
|
+
fill: "currentColor",
|
|
372
|
+
stroke: "none",
|
|
373
|
+
strokeWidth: 0
|
|
374
|
+
});
|
|
375
|
+
println(` <circle ${serializedAttrs} />`);
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
const rectAttrs = serializeAttrs({
|
|
379
|
+
x,
|
|
380
|
+
y,
|
|
381
|
+
width: width2,
|
|
382
|
+
height: height2,
|
|
383
|
+
rx: Math.ceil(stateFontSize / 2),
|
|
384
|
+
ry: Math.ceil(stateFontSize / 2),
|
|
385
|
+
fill: "none",
|
|
386
|
+
stroke: "currentColor",
|
|
387
|
+
strokeWidth: "2",
|
|
388
|
+
class: "state-box"
|
|
389
|
+
});
|
|
390
|
+
println(` <rect ${rectAttrs} />`);
|
|
391
|
+
const textAttrs = serializeAttrs({
|
|
392
|
+
class: "state-label",
|
|
393
|
+
"text-anchor": "middle",
|
|
394
|
+
fill: "currentColor",
|
|
395
|
+
x: Math.round(x + width2 / 2),
|
|
396
|
+
y: Math.round(y + height2 / 2 + stateFontSize / 4),
|
|
397
|
+
"data-state-id": id
|
|
398
|
+
});
|
|
399
|
+
println(` <text ${textAttrs}>${text}</text>`);
|
|
400
|
+
}
|
|
401
|
+
println(" </g>");
|
|
402
|
+
}
|
|
403
|
+
println("</g>");
|
|
404
|
+
println("</svg>");
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/svg/buildStatechartsPanel.ts
|
|
408
|
+
function buildStatechartsPanel({
|
|
409
|
+
statechart,
|
|
410
|
+
newId,
|
|
411
|
+
println,
|
|
412
|
+
initialStateKey = "<initial>",
|
|
413
|
+
finalStateKey = "<final>"
|
|
414
|
+
}) {
|
|
415
|
+
render(statechart, println);
|
|
416
|
+
function render(statechart2, println2) {
|
|
417
|
+
println2(`<details class="state-details" data-state-id="${statechart2.id}">`);
|
|
418
|
+
println2(` <summary class="state-label">${statechart2.key}</summary>`);
|
|
419
|
+
println2(` <div class="state-description"><div class="state-chart-container">`);
|
|
420
|
+
const printChildren = (str) => println2(` ${str}`);
|
|
421
|
+
buildStatechartSvg({
|
|
422
|
+
graph: statechart2.graph,
|
|
423
|
+
newId,
|
|
424
|
+
println: printChildren,
|
|
425
|
+
initialStateKey,
|
|
426
|
+
finalStateKey
|
|
427
|
+
});
|
|
428
|
+
println2(` </div>`);
|
|
429
|
+
for (const child of statechart2.children || []) {
|
|
430
|
+
render(child, printChildren);
|
|
431
|
+
}
|
|
432
|
+
println2(` </div>`);
|
|
433
|
+
println2(`</details>`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export { buildStatechartCss, buildStatechartSvg, buildStatechartsPanel };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { D as Dimensions, T as TransitionsGraph, b as StateChart } from './StateChart-DNGQd1XG.js';
|
|
2
|
+
export { P as Position, S as StateGraphEdge, a as StateGraphNode } from './StateChart-DNGQd1XG.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Provides default functions measuring dimensions for state nodes and transition edges in the graph.
|
|
6
|
+
*/
|
|
7
|
+
type GraphParamsProvider = {
|
|
8
|
+
getStateParams: (stateKey: string, state?: string) => Dimensions & Record<string, any>;
|
|
9
|
+
getTransitionParams: (fromStateKey: string, event: string, toStateKey: string) => Dimensions & Record<string, any>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A transition from one state to another.
|
|
14
|
+
* Contains the state before the transition, the event that triggers the transition, and the state after the transition.
|
|
15
|
+
*/
|
|
16
|
+
type Transition = [string, string, string];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Padding type.
|
|
20
|
+
*/
|
|
21
|
+
type Padding = [number, number, number, number] | [number, number] | number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build graphs corresponding to the specified transitions.
|
|
25
|
+
* @param transitions - The transitions between states.
|
|
26
|
+
* @param newId - A function that generates unique ids for nodes and edges.
|
|
27
|
+
* @param getStateParams - A function that returns the dimensions of a state node.
|
|
28
|
+
* @param getTransitionParams - A function that returns the dimensions of a transition edge.
|
|
29
|
+
* @param initialStateKey - The key for the initial state.
|
|
30
|
+
* @param finalStateKey - The key for the final state.
|
|
31
|
+
* @param vertical - Whether the graph should be vertical.
|
|
32
|
+
* @returns {TransitionsGraph} - The graph with nodes and edges.
|
|
33
|
+
*/
|
|
34
|
+
declare function buildFlatCharts({ lodash, transitions, newId, getStateParams, getTransitionParams, padding, initialStateKey, finalStateKey, vertical, }: {
|
|
35
|
+
transitions: Transition[];
|
|
36
|
+
lodash: any;
|
|
37
|
+
newId: (prefix: string) => string;
|
|
38
|
+
initialStateKey?: string;
|
|
39
|
+
finalStateKey?: string;
|
|
40
|
+
vertical?: boolean;
|
|
41
|
+
padding?: Padding;
|
|
42
|
+
} & GraphParamsProvider): TransitionsGraph;
|
|
43
|
+
|
|
44
|
+
type FsmStateConfig = {
|
|
45
|
+
key: string;
|
|
46
|
+
transitions: [string, string, string][];
|
|
47
|
+
states?: FsmStateConfig[];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build graphs corresponding to the specified transitions.
|
|
52
|
+
* @param config - State configuration with transitions and sub-states.
|
|
53
|
+
* @param newId - A function that generates unique ids for nodes and edges.
|
|
54
|
+
* @param getStateParams - A function that returns the dimensions of a state node.
|
|
55
|
+
* @param getTransitionParams - A function that returns the dimensions of a transition edge.
|
|
56
|
+
* @param initialStateKey - The key for the initial state.
|
|
57
|
+
* @param finalStateKey - The key for the final state.
|
|
58
|
+
* @returns {StateChart} - The graph with nodes and edges.
|
|
59
|
+
*/
|
|
60
|
+
declare function buildCharts({ lodash, config, newId, getStateParams, getTransitionParams, padding, initialStateKey, finalStateKey, }: {
|
|
61
|
+
config: FsmStateConfig;
|
|
62
|
+
lodash: any;
|
|
63
|
+
newId: (prefix: string) => string;
|
|
64
|
+
initialStateKey?: string;
|
|
65
|
+
finalStateKey?: string;
|
|
66
|
+
padding?: Padding;
|
|
67
|
+
} & GraphParamsProvider): StateChart;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Provides default functions measuring dimensions for state nodes and transition edges in the graph.
|
|
71
|
+
* @param fontSize - The font size for the state and transition labels.
|
|
72
|
+
* @param stateFontSize - The font size for the state labels.
|
|
73
|
+
* @param stateTextPadding - The padding around the state labels.
|
|
74
|
+
* @param transitionsFontSize - The font size for the transition labels.
|
|
75
|
+
* @param transitionsTextPadding - The padding around the transition labels.
|
|
76
|
+
*/
|
|
77
|
+
declare function getGraphParamsProvider({ fontSize, stateFontSize, stateTextPadding, transitionsFontSize, transitionsTextPadding, }?: {
|
|
78
|
+
fontSize?: number;
|
|
79
|
+
stateFontSize?: number;
|
|
80
|
+
stateTextPadding?: Padding;
|
|
81
|
+
transitionsFontSize?: number;
|
|
82
|
+
transitionsTextPadding?: Padding;
|
|
83
|
+
}): GraphParamsProvider;
|
|
84
|
+
|
|
85
|
+
declare function getLabelDimensions(label: string, { padding, fontSize, getCharWidth, // Golden ratio and all that jazz :-)
|
|
86
|
+
lineGap, }?: {
|
|
87
|
+
padding?: Padding;
|
|
88
|
+
fontSize?: number;
|
|
89
|
+
getCharWidth?: (char: string) => number;
|
|
90
|
+
lineGap?: number;
|
|
91
|
+
}): {
|
|
92
|
+
width: number;
|
|
93
|
+
height: number;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export { Dimensions, type GraphParamsProvider, type Padding, type Transition, TransitionsGraph, buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { b as StateChart, T as TransitionsGraph } from './StateChart-DNGQd1XG.js';
|
|
2
|
+
|
|
3
|
+
declare function buildStatechartCss({ prefix }?: {
|
|
4
|
+
prefix?: string | undefined;
|
|
5
|
+
}): string;
|
|
6
|
+
|
|
7
|
+
declare function buildStatechartsPanel({ statechart, newId, println, initialStateKey, finalStateKey, }: {
|
|
8
|
+
statechart: StateChart;
|
|
9
|
+
newId: (prefix: string) => string;
|
|
10
|
+
println: (str: string) => void;
|
|
11
|
+
initialStateKey?: string;
|
|
12
|
+
finalStateKey?: string;
|
|
13
|
+
}): void;
|
|
14
|
+
|
|
15
|
+
declare function buildStatechartSvg({ graph: { width, height, nodes, edges }, newId, stateFontSize, transitionFontSize, println, initialStateKey, finalStateKey, }: {
|
|
16
|
+
graph: TransitionsGraph;
|
|
17
|
+
newId: (prefix: string) => string;
|
|
18
|
+
println: (str: string) => void;
|
|
19
|
+
stateFontSize?: number;
|
|
20
|
+
transitionFontSize?: number;
|
|
21
|
+
initialStateKey?: string;
|
|
22
|
+
finalStateKey?: string;
|
|
23
|
+
}): void;
|
|
24
|
+
|
|
25
|
+
export { buildStatechartCss, buildStatechartSvg, buildStatechartsPanel };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { GraphParamsProvider, Padding, Transition, buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './index-layout.js';
|
|
2
|
+
export { D as Dimensions, P as Position, S as StateGraphEdge, a as StateGraphNode, T as TransitionsGraph } from './StateChart-DNGQd1XG.js';
|
|
3
|
+
export { buildStatechartCss, buildStatechartSvg, buildStatechartsPanel } from './index-svg.js';
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@statewalker/fsm-charts",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "HFSM Implementation",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"homepage": "https://github.com/statewalker/statewalker-fsm-charts",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Mikhail Kotelnikov",
|
|
9
|
+
"email": "mikhail.kotelnikov@gmail.com"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"jsdelivr": "./dist/index.js",
|
|
20
|
+
"unpkg": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./src/index.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@statewalker/typescript-config": "*",
|
|
29
|
+
"@types/lodash-es": "^4.17.12",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
|
31
|
+
"eslint": "^8.0.1",
|
|
32
|
+
"eslint-config-standard-with-typescript": "^43.0.1",
|
|
33
|
+
"eslint-plugin-import": "^2.25.2",
|
|
34
|
+
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
|
|
35
|
+
"eslint-plugin-promise": "^6.0.0",
|
|
36
|
+
"lodash": "^4.17.21",
|
|
37
|
+
"lodash-es": "^4.17.21",
|
|
38
|
+
"tsup": "^8.0.2",
|
|
39
|
+
"typescript": "*",
|
|
40
|
+
"vitest": "^1.6.0"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git@github.com:statewalker/statewalker-fsm-charts.git"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build_": "yarn test && tsup",
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"watch": "tsup --watch",
|
|
50
|
+
"clean": "rm -rf dist",
|
|
51
|
+
"lint": "eslint",
|
|
52
|
+
"lint:fix": "yarn lint -- --fix",
|
|
53
|
+
"test": "vitest --run",
|
|
54
|
+
"test:watch": "vitest"
|
|
55
|
+
},
|
|
56
|
+
"sideEffects": false,
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
}
|
|
60
|
+
}
|