@statewalker/fsm-charts 0.0.3 → 0.0.5
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-pFSQIvtE.d.ts +77 -0
- package/dist/chunk-3PNSXN5U.js +538 -0
- package/dist/{chunk-3GGM3ZWL.js → chunk-IT7GBMYV.js} +2 -2
- package/dist/chunk-X2UPWZLX.js +4 -0
- package/dist/index-layout.d.ts +3 -28
- package/dist/index-layout.js +1 -1
- package/dist/index-runtime.d.ts +5 -0
- package/dist/index-runtime.js +1 -0
- package/dist/index-svg-DCwEyX8X.d.ts +33 -0
- package/dist/index-svg.d.ts +2 -25
- package/dist/index-svg.js +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +3 -2
- package/package.json +3 -3
- package/src/index-layout.ts +1 -0
- package/src/index-runtime.ts +2 -0
- package/src/index-svg.ts +1 -0
- package/src/index.ts +4 -2
- package/src/layout/buildCharts.ts +5 -13
- package/src/layout/buildFlatCharts.ts +9 -8
- package/src/layout/getGraphParamsProvider.ts +4 -3
- package/src/layout/getLabelDimensions.ts +2 -2
- package/src/layout/index.ts +0 -10
- package/src/runtime/index.ts +211 -0
- package/src/svg/buildStatechartStyles.ts +3 -0
- package/src/svg/buildStatechartSvg.ts +1 -1
- package/src/svg/buildStatechartsPanel.ts +12 -6
- package/src/types/FsmStateConfig.ts +7 -0
- package/src/{layout → types}/StateChart.ts +1 -2
- package/src/{layout → types}/StateGraphEdge.ts +2 -2
- package/src/{layout → types}/StateGraphNode.ts +2 -2
- package/src/types/TransitionsGraph.ts +13 -0
- package/src/types/index.ts +10 -0
- package/src/utils/getPadding.ts +1 -1
- package/dist/StateChart-DNGQd1XG.d.ts +0 -53
- package/dist/chunk-TPNWHU7Y.js +0 -437
- package/src/FsmStateConfig.ts +0 -5
- package/src/layout/TransitionsGraph.ts +0 -13
- /package/src/{layout → types}/Dimensions.ts +0 -0
- /package/src/{layout → types}/GraphParamsProvider.ts +0 -0
- /package/src/{layout → types}/Padding.ts +0 -0
- /package/src/{layout → types}/Position.ts +0 -0
- /package/src/{layout → types}/Transition.ts +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual element dimensions.
|
|
3
|
+
* @typedef {Object} Dimensions
|
|
4
|
+
*/
|
|
5
|
+
type Dimensions = {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A transition from one state to another.
|
|
12
|
+
* Contains the state before the transition, the event that triggers the transition, and the state after the transition.
|
|
13
|
+
*/
|
|
14
|
+
type Transition = [string, string, string];
|
|
15
|
+
|
|
16
|
+
type FsmStateConfig = {
|
|
17
|
+
key: string;
|
|
18
|
+
transitions: Transition[];
|
|
19
|
+
states?: FsmStateConfig[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Provides default functions measuring dimensions for state nodes and transition edges in the graph.
|
|
24
|
+
*/
|
|
25
|
+
type GraphParamsProvider = {
|
|
26
|
+
getStateParams: (stateKey: string, state?: string) => Dimensions & Record<string, any>;
|
|
27
|
+
getTransitionParams: (fromStateKey: string, event: string, toStateKey: string) => Dimensions & Record<string, any>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Padding type.
|
|
32
|
+
*/
|
|
33
|
+
type Padding = [number, number, number, number] | [number, number] | number;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* X/Y position of a node in the graph.
|
|
37
|
+
*/
|
|
38
|
+
type Position = {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Information about a state graph edge.
|
|
45
|
+
*/
|
|
46
|
+
type StateGraphEdge = Dimensions & Position & {
|
|
47
|
+
id: string;
|
|
48
|
+
from: string;
|
|
49
|
+
event: string;
|
|
50
|
+
to: string;
|
|
51
|
+
points: Position[];
|
|
52
|
+
} & Record<string, any>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Information about a state node in the graph.
|
|
56
|
+
*/
|
|
57
|
+
type StateGraphNode = Dimensions & Position & {
|
|
58
|
+
id: string;
|
|
59
|
+
key: string;
|
|
60
|
+
state: string;
|
|
61
|
+
} & Record<string, any>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Coordinates for state nodes and endges corresponding to state transitions.
|
|
65
|
+
*/
|
|
66
|
+
type TransitionsGraph = Position & Dimensions & {
|
|
67
|
+
nodes: StateGraphNode[];
|
|
68
|
+
edges: StateGraphEdge[];
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type StateChart = TransitionsGraph & {
|
|
72
|
+
id: string;
|
|
73
|
+
key: string;
|
|
74
|
+
children?: StateChart[];
|
|
75
|
+
} & Record<string, any>;
|
|
76
|
+
|
|
77
|
+
export type { Dimensions as D, FsmStateConfig as F, GraphParamsProvider as G, Padding as P, StateChart as S, Transition as T, Position as a, StateGraphEdge as b, StateGraphNode as c, TransitionsGraph as d };
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import { getPadding } from './chunk-NXWOFQMO.js';
|
|
2
|
+
|
|
3
|
+
// src/utils/toKebabCase.ts
|
|
4
|
+
function toKebabCase2(str) {
|
|
5
|
+
str = str.trim().replace(/\s+/gim, "-");
|
|
6
|
+
str = str.replace(
|
|
7
|
+
/([^A-Z]?)([A-Z])/g,
|
|
8
|
+
(_, a, b) => a.toLowerCase() + "-" + b.toLowerCase()
|
|
9
|
+
);
|
|
10
|
+
return str;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/utils/serializeCss.ts
|
|
14
|
+
function serializeCss(obj) {
|
|
15
|
+
return visitStyle(obj).join("\n");
|
|
16
|
+
function visitStyle(obj2, lines = [], prefixes = [""]) {
|
|
17
|
+
for (const [key, style] of Object.entries(obj2)) {
|
|
18
|
+
const selectors = [];
|
|
19
|
+
for (const sel of key.split(",").map((_) => _.trim())) {
|
|
20
|
+
for (const prefix of prefixes) {
|
|
21
|
+
const selector = sel.replace(
|
|
22
|
+
/^(.)/,
|
|
23
|
+
(_, ch) => ch === "&" ? prefix : prefix.length > 0 ? prefix + " " + ch : ch
|
|
24
|
+
);
|
|
25
|
+
selectors.push(selector);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const selectorRules = [];
|
|
29
|
+
const extensions = [];
|
|
30
|
+
for (let [key2, value] of Object.entries(style)) {
|
|
31
|
+
if (typeof value === "object") {
|
|
32
|
+
visitStyle({ [key2]: value }, extensions, selectors);
|
|
33
|
+
} else {
|
|
34
|
+
key2 = toKebabCase2(key2);
|
|
35
|
+
selectorRules.push(` ${key2}: ${value};`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (selectorRules.length) {
|
|
39
|
+
lines.push(`${selectors.join(",\n")} {`);
|
|
40
|
+
lines.push(...selectorRules);
|
|
41
|
+
lines.push(`}`);
|
|
42
|
+
}
|
|
43
|
+
lines.push(...extensions);
|
|
44
|
+
}
|
|
45
|
+
return lines;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/svg/buildStatechartStyles.ts
|
|
50
|
+
function buildStateDetailsStyle() {
|
|
51
|
+
return {
|
|
52
|
+
".state-details": {
|
|
53
|
+
backgroundColor: "var(--state-background-color, white)",
|
|
54
|
+
border: "var(--state-border-width, 1px) solid var(--state-border-color, currentColor)",
|
|
55
|
+
borderRadius: "0.5em",
|
|
56
|
+
padding: "0.5em"
|
|
57
|
+
},
|
|
58
|
+
".state-details__label": {
|
|
59
|
+
cursor: "pointer",
|
|
60
|
+
color: "var(--transition-label-color, currentColor)",
|
|
61
|
+
fontFamily: "var(--state-label-font-family, sans-serif)",
|
|
62
|
+
fontSize: "var(--state-label-font-size, 14px)"
|
|
63
|
+
},
|
|
64
|
+
".state-details__description": {
|
|
65
|
+
display: "flex",
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
gap: "0.5em"
|
|
68
|
+
},
|
|
69
|
+
".state-details__chart": {
|
|
70
|
+
maxWidth: "100%",
|
|
71
|
+
overflow: "auto",
|
|
72
|
+
scrollbarWidth: "thin"
|
|
73
|
+
},
|
|
74
|
+
//
|
|
75
|
+
".state-details--selected": {
|
|
76
|
+
backgroundColor: "var(--state-selected-background-color, white)",
|
|
77
|
+
border: "var(--state-selected-border-width, 1px) solid var(--state-selected-border-color, currentColor)"
|
|
78
|
+
},
|
|
79
|
+
".state-details__label--selected": {
|
|
80
|
+
color: "var(--state-selected-label-color)"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function buildStatechartStyles({
|
|
85
|
+
root = ":root"
|
|
86
|
+
} = {}) {
|
|
87
|
+
return {
|
|
88
|
+
[root]: {
|
|
89
|
+
// --------------------------------------
|
|
90
|
+
// Normal states
|
|
91
|
+
"--state-background-color": "white",
|
|
92
|
+
"--state-border-color": "silver",
|
|
93
|
+
"--state-border-width": "1px",
|
|
94
|
+
"--state-label-color": "gray",
|
|
95
|
+
"--state-label-font-size": "14px",
|
|
96
|
+
"--state-label-font-family": "sans-serif",
|
|
97
|
+
// Selected states
|
|
98
|
+
"--state-selected-background-color": "#eee",
|
|
99
|
+
"--state-selected-border-color": "red",
|
|
100
|
+
"--state-selected-border-width": "2px",
|
|
101
|
+
"--state-selected-label-color": "red",
|
|
102
|
+
// --------------------------------------
|
|
103
|
+
// Normal transitions
|
|
104
|
+
"--transition-background-color": "rgba(255,255,255,0.7)",
|
|
105
|
+
"--transition-border-color": "none",
|
|
106
|
+
"--transition-border-width": "1px",
|
|
107
|
+
"--transition-line-color": "silver",
|
|
108
|
+
"--transition-line-width": "1px",
|
|
109
|
+
"--transition-label-color": "gray",
|
|
110
|
+
"--transition-label-font-size": "12px",
|
|
111
|
+
"--transition-label-font-family": "sans-serif",
|
|
112
|
+
// --------------------------------------
|
|
113
|
+
// Selected transitions
|
|
114
|
+
"--transition-selected-background-color": "#eee",
|
|
115
|
+
"--transition-selected-border-color": "none",
|
|
116
|
+
"--transition-selected-border-width": "0",
|
|
117
|
+
"--transition-selected-line-color": "red",
|
|
118
|
+
"--transition-selected-line-width": "1px",
|
|
119
|
+
"--transition-selected-label-color": "red",
|
|
120
|
+
// --------------------------------------
|
|
121
|
+
// --------------------------------------
|
|
122
|
+
...buildStateDetailsStyle(),
|
|
123
|
+
".transition": {},
|
|
124
|
+
".transition--selected": {
|
|
125
|
+
"--transition-background-color": "var(--transition-selected-background-color)",
|
|
126
|
+
"--transition-border-color": "var(--transition-selected-border-color)",
|
|
127
|
+
"--transition-border-width": "var(--transition-selected-border-width)",
|
|
128
|
+
"--transition-line-color": "var(--transition-selected-line-color)",
|
|
129
|
+
"--transition-line-width": "var(--transition-selected-line-width)",
|
|
130
|
+
"--transition-label-color": "var(--transition-selected-label-color)"
|
|
131
|
+
},
|
|
132
|
+
".transition__marker": {
|
|
133
|
+
fill: "var(--transition-line-color, currentColor)",
|
|
134
|
+
stroke: "none"
|
|
135
|
+
},
|
|
136
|
+
".transition__line": {
|
|
137
|
+
stroke: "var(--transition-line-color, currentColor)",
|
|
138
|
+
strokeWidth: "var(--transition-line-width, 1px)"
|
|
139
|
+
},
|
|
140
|
+
".transition__box": {
|
|
141
|
+
fill: "var(--transition-background-color, rgba(255,255,255,0.7))",
|
|
142
|
+
stroke: "var(--transition-border-color, none)",
|
|
143
|
+
strokeWidth: "var(--transition-border-width, 1px)",
|
|
144
|
+
borderRadius: "0.5em",
|
|
145
|
+
cursor: "pointer"
|
|
146
|
+
},
|
|
147
|
+
".transition__label": {
|
|
148
|
+
fill: "var(--transition-label-color, currentColor)",
|
|
149
|
+
fontSize: "var(--transition-label-font-size, 12px)",
|
|
150
|
+
fontFamily: "var(--transition-label-font-family, sans-serif)",
|
|
151
|
+
cursor: "pointer"
|
|
152
|
+
},
|
|
153
|
+
// ----------------------------------------------
|
|
154
|
+
// States
|
|
155
|
+
".state": {},
|
|
156
|
+
".state--selected": {
|
|
157
|
+
"--state-background-color": "var(--state-selected-background-color)",
|
|
158
|
+
"--state-border-color": "var(--state-selected-border-color)",
|
|
159
|
+
"--state-border-width": "var(--state-selected-border-width)",
|
|
160
|
+
"--state-label-color": "var(--state-selected-label-color)"
|
|
161
|
+
},
|
|
162
|
+
// --------------------------
|
|
163
|
+
// Panels
|
|
164
|
+
".state__details": {
|
|
165
|
+
backgroundColor: "var(--state-background-color, white)",
|
|
166
|
+
border: "var(--state-border-width, 1px) solid var(--state-border-color, currentColor)",
|
|
167
|
+
borderRadius: "0.5em",
|
|
168
|
+
padding: "0.5em"
|
|
169
|
+
},
|
|
170
|
+
".state__description": {
|
|
171
|
+
display: "flex",
|
|
172
|
+
flexDirection: "column",
|
|
173
|
+
gap: "0.5em"
|
|
174
|
+
},
|
|
175
|
+
".state__chart-container": {
|
|
176
|
+
maxWidth: "100%",
|
|
177
|
+
overflow: "auto",
|
|
178
|
+
scrollbarWidth: "thin"
|
|
179
|
+
},
|
|
180
|
+
// --------------------------
|
|
181
|
+
// Statecharts
|
|
182
|
+
".state__box": {
|
|
183
|
+
fill: "var(--state-background-color, white)",
|
|
184
|
+
stroke: "var(--state-border-color, currentColor)",
|
|
185
|
+
strokeWidth: "var(--state-border-width, 1px)",
|
|
186
|
+
borderRadius: "0.5em",
|
|
187
|
+
cursor: "pointer"
|
|
188
|
+
},
|
|
189
|
+
".state__label": {
|
|
190
|
+
color: "var(--state-label-color, currentColor)",
|
|
191
|
+
fill: "var(--state-label-color, currentColor)",
|
|
192
|
+
fontSize: "var(--state-label-font-size, 14px)",
|
|
193
|
+
fontFamily: "var(--state-label-font-family, sans-serif)",
|
|
194
|
+
cursor: "pointer"
|
|
195
|
+
},
|
|
196
|
+
".state__initial": {
|
|
197
|
+
fill: "var(--state-border-color, currentColor)",
|
|
198
|
+
stroke: "var(--state-border-color, currentColor)",
|
|
199
|
+
strokeWidth: "var(--state-border-width, 2px)"
|
|
200
|
+
},
|
|
201
|
+
".state__final": {
|
|
202
|
+
fill: "white",
|
|
203
|
+
stroke: "var(--state-border-color, currentColor)",
|
|
204
|
+
strokeWidth: "var(--state-border-width, 2px)",
|
|
205
|
+
// ---------------------------
|
|
206
|
+
"&.state__final--inner": {
|
|
207
|
+
fill: "var(--state-border-color, currentColor)",
|
|
208
|
+
stroke: "none",
|
|
209
|
+
strokeWidth: "0"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/svg/buildStatechartCss.ts
|
|
217
|
+
function buildStatechartCss({ prefix = "" } = {}) {
|
|
218
|
+
const cssObject = buildStatechartStyles({ root: prefix || ":root" });
|
|
219
|
+
return serializeCss(cssObject);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/utils/serializeAttrs.ts
|
|
223
|
+
function serializeAttrs(attrs = {}, {
|
|
224
|
+
prefix = "",
|
|
225
|
+
transform = false
|
|
226
|
+
} = {}) {
|
|
227
|
+
const result = [];
|
|
228
|
+
for (const [key, value] of Object.entries(attrs || {}).sort(
|
|
229
|
+
([a], [b]) => a > b ? 1 : -1
|
|
230
|
+
)) {
|
|
231
|
+
if (value === void 0 || value === null || value === "")
|
|
232
|
+
continue;
|
|
233
|
+
result.push(
|
|
234
|
+
`${prefix}${escapeValue(key)}${"=" + JSON.stringify(escapeValue(transform ? toKebabCase(value) : value))}`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return result.join(" ");
|
|
238
|
+
function escapeValue(value) {
|
|
239
|
+
return String(value).replaceAll(/</gim, "<").replaceAll(/>/gim, ">").replaceAll(/"/gim, "&qout;");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/utils/BasisCurve.ts
|
|
244
|
+
var BasisCurve = class {
|
|
245
|
+
_context;
|
|
246
|
+
_x0 = 0;
|
|
247
|
+
_y0 = 0;
|
|
248
|
+
_x1 = 0;
|
|
249
|
+
_y1 = 0;
|
|
250
|
+
_point = 0;
|
|
251
|
+
_line = 0;
|
|
252
|
+
constructor(context) {
|
|
253
|
+
this._context = context;
|
|
254
|
+
}
|
|
255
|
+
$point(x, y) {
|
|
256
|
+
this._context.bezierCurveTo(
|
|
257
|
+
(2 * this._x0 + this._x1) / 3,
|
|
258
|
+
(2 * this._y0 + this._y1) / 3,
|
|
259
|
+
(this._x0 + 2 * this._x1) / 3,
|
|
260
|
+
(this._y0 + 2 * this._y1) / 3,
|
|
261
|
+
(this._x0 + 4 * this._x1 + x) / 6,
|
|
262
|
+
(this._y0 + 4 * this._y1 + y) / 6
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
areaStart() {
|
|
266
|
+
this._line = 0;
|
|
267
|
+
}
|
|
268
|
+
areaEnd() {
|
|
269
|
+
this._line = NaN;
|
|
270
|
+
}
|
|
271
|
+
lineStart() {
|
|
272
|
+
this._x0 = this._x1 = this._y0 = this._y1 = NaN;
|
|
273
|
+
this._point = 0;
|
|
274
|
+
}
|
|
275
|
+
lineEnd() {
|
|
276
|
+
switch (this._point) {
|
|
277
|
+
case 3:
|
|
278
|
+
this.$point(this._x1, this._y1);
|
|
279
|
+
case 2:
|
|
280
|
+
this._context.lineTo(this._x1, this._y1);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
if (this._line || this._line !== 0 && this._point === 1)
|
|
284
|
+
this._context.closePath();
|
|
285
|
+
this._line = 1 - this._line;
|
|
286
|
+
}
|
|
287
|
+
point(x, y) {
|
|
288
|
+
x = +x, y = +y;
|
|
289
|
+
switch (this._point) {
|
|
290
|
+
case 0:
|
|
291
|
+
this._point = 1;
|
|
292
|
+
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
|
|
293
|
+
break;
|
|
294
|
+
case 1:
|
|
295
|
+
this._point = 2;
|
|
296
|
+
break;
|
|
297
|
+
case 2:
|
|
298
|
+
this._point = 3;
|
|
299
|
+
this._context.lineTo(
|
|
300
|
+
(5 * this._x0 + this._x1) / 6,
|
|
301
|
+
(5 * this._y0 + this._y1) / 6
|
|
302
|
+
);
|
|
303
|
+
default:
|
|
304
|
+
this.$point(x, y);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
this._x0 = this._x1, this._x1 = x;
|
|
308
|
+
this._y0 = this._y1, this._y1 = y;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/svg/buildStatechartSvg.ts
|
|
313
|
+
function getBoxParams({
|
|
314
|
+
x,
|
|
315
|
+
y,
|
|
316
|
+
width,
|
|
317
|
+
height,
|
|
318
|
+
padding = 0
|
|
319
|
+
}) {
|
|
320
|
+
const [top, right, bottom, left] = getPadding(padding);
|
|
321
|
+
const h = Math.ceil(height - top - bottom);
|
|
322
|
+
return {
|
|
323
|
+
x: x + left,
|
|
324
|
+
y: y - bottom - h,
|
|
325
|
+
width: Math.ceil(width - left - right),
|
|
326
|
+
height: h
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function buildStatechartSvg({
|
|
330
|
+
graph: { width, height, nodes, edges },
|
|
331
|
+
newId,
|
|
332
|
+
stateFontSize = 14,
|
|
333
|
+
transitionFontSize = 12,
|
|
334
|
+
println,
|
|
335
|
+
initialStateKey = "<initial>",
|
|
336
|
+
finalStateKey = "<final>"
|
|
337
|
+
}) {
|
|
338
|
+
width = Math.ceil(width);
|
|
339
|
+
height = Math.ceil(height);
|
|
340
|
+
println(
|
|
341
|
+
`<svg ${serializeAttrs({
|
|
342
|
+
class: "statechart",
|
|
343
|
+
width,
|
|
344
|
+
height,
|
|
345
|
+
viewbox: `0 0 ${width} ${height}`,
|
|
346
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
347
|
+
})}>`
|
|
348
|
+
);
|
|
349
|
+
println(`<g class="statechart__transitions">`);
|
|
350
|
+
const markerAttrs = serializeAttrs({
|
|
351
|
+
refX: 19,
|
|
352
|
+
refY: 7,
|
|
353
|
+
markerWidth: 20,
|
|
354
|
+
markerHeight: 14,
|
|
355
|
+
orient: "auto",
|
|
356
|
+
class: "transition__marker",
|
|
357
|
+
stroke: "none",
|
|
358
|
+
fill: "currentColor"
|
|
359
|
+
});
|
|
360
|
+
for (const edge of edges) {
|
|
361
|
+
const { id, points, event: text } = edge;
|
|
362
|
+
println(` <g class="transition" data-transition-id="${id}">`);
|
|
363
|
+
const markerId = newId("marker");
|
|
364
|
+
println(` <defs>`);
|
|
365
|
+
println(` <marker id="${markerId}" ${markerAttrs}>`);
|
|
366
|
+
println(` <path d="M 19,7 L9,13 L14,7 L9,1 Z"></path>`);
|
|
367
|
+
println(` </marker>`);
|
|
368
|
+
println(` </defs>`);
|
|
369
|
+
const path = [];
|
|
370
|
+
const round = (...n) => n.map(Math.round);
|
|
371
|
+
const base = new BasisCurve({
|
|
372
|
+
bezierCurveTo(x0, y0, x1, y1, x2, y2) {
|
|
373
|
+
path.push("C" + round(x0, y0, x1, y1, x2, y2).join(","));
|
|
374
|
+
},
|
|
375
|
+
moveTo(x, y) {
|
|
376
|
+
path.push("M" + round(x, y).join(","));
|
|
377
|
+
},
|
|
378
|
+
lineTo(x, y) {
|
|
379
|
+
path.push("L" + round(x, y).join(","));
|
|
380
|
+
},
|
|
381
|
+
closePath() {
|
|
382
|
+
path.push("Z");
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
base.lineStart();
|
|
386
|
+
for (const point of points) {
|
|
387
|
+
base.point(point.x, point.y);
|
|
388
|
+
}
|
|
389
|
+
base.lineEnd();
|
|
390
|
+
const serializedAttrs = serializeAttrs({
|
|
391
|
+
class: "transition__line",
|
|
392
|
+
fill: "none",
|
|
393
|
+
stroke: "currentColor",
|
|
394
|
+
strokeWidth: "1px",
|
|
395
|
+
d: path.join(""),
|
|
396
|
+
"marker-end": `url(#${markerId})`
|
|
397
|
+
});
|
|
398
|
+
println(` <path ${serializedAttrs} />`);
|
|
399
|
+
if (text) {
|
|
400
|
+
const { x, y, width: width2, height: height2 } = getBoxParams({
|
|
401
|
+
...edge,
|
|
402
|
+
padding: 0
|
|
403
|
+
});
|
|
404
|
+
const rectAttrs = serializeAttrs({
|
|
405
|
+
x,
|
|
406
|
+
y: y - transitionFontSize,
|
|
407
|
+
width: width2,
|
|
408
|
+
height: height2,
|
|
409
|
+
rx: Math.ceil(transitionFontSize / 2),
|
|
410
|
+
ry: Math.ceil(transitionFontSize / 2),
|
|
411
|
+
fill: "rgba(255,255,255,0.7)",
|
|
412
|
+
stroke: "none",
|
|
413
|
+
strokeWidth: "1",
|
|
414
|
+
class: "transition__box"
|
|
415
|
+
});
|
|
416
|
+
println(` <rect ${rectAttrs} />`);
|
|
417
|
+
const textAttrs = serializeAttrs({
|
|
418
|
+
class: "transition__label",
|
|
419
|
+
"text-anchor": "middle",
|
|
420
|
+
fill: "currentColor",
|
|
421
|
+
x: Math.round(x + width2 / 2),
|
|
422
|
+
y: Math.round(y + height2 / 2 - transitionFontSize / 2)
|
|
423
|
+
});
|
|
424
|
+
println(` <text ${textAttrs}>${text}</text>`);
|
|
425
|
+
}
|
|
426
|
+
println(` </g>`);
|
|
427
|
+
}
|
|
428
|
+
println("</g>");
|
|
429
|
+
const shift = stateFontSize / 4;
|
|
430
|
+
const initialStateRadius = 6;
|
|
431
|
+
const finalStateRadius = initialStateRadius / 2;
|
|
432
|
+
println(`<g class="statechart__states">`);
|
|
433
|
+
for (const node of nodes) {
|
|
434
|
+
const { key, id, state: text } = node;
|
|
435
|
+
const { x, y, width: width2, height: height2 } = getBoxParams({ ...node, padding: shift });
|
|
436
|
+
println(` <g class="state" data-state-id="${id}">`);
|
|
437
|
+
if (key === initialStateKey || key === finalStateKey) {
|
|
438
|
+
const cx = Math.round(x + width2 / 2);
|
|
439
|
+
const cy = Math.round(y + height2 / 2);
|
|
440
|
+
if (key === initialStateKey) {
|
|
441
|
+
const serializedAttrs = serializeAttrs({
|
|
442
|
+
class: "state__initial",
|
|
443
|
+
cx,
|
|
444
|
+
cy,
|
|
445
|
+
r: initialStateRadius,
|
|
446
|
+
fill: "none",
|
|
447
|
+
stroke: "currentColor",
|
|
448
|
+
strokeWidth: "2"
|
|
449
|
+
});
|
|
450
|
+
println(` <circle ${serializedAttrs} />`);
|
|
451
|
+
} else {
|
|
452
|
+
let serializedAttrs = serializeAttrs({
|
|
453
|
+
class: "state__final",
|
|
454
|
+
cx,
|
|
455
|
+
cy,
|
|
456
|
+
r: initialStateRadius,
|
|
457
|
+
fill: "none",
|
|
458
|
+
stroke: "currentColor",
|
|
459
|
+
strokeWidth: 2
|
|
460
|
+
});
|
|
461
|
+
println(` <circle ${serializedAttrs} />`);
|
|
462
|
+
serializedAttrs = serializeAttrs({
|
|
463
|
+
class: "state__final state__final--inner",
|
|
464
|
+
cx,
|
|
465
|
+
cy,
|
|
466
|
+
r: finalStateRadius,
|
|
467
|
+
fill: "currentColor",
|
|
468
|
+
stroke: "none",
|
|
469
|
+
strokeWidth: 0
|
|
470
|
+
});
|
|
471
|
+
println(` <circle ${serializedAttrs} />`);
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
const rectAttrs = serializeAttrs({
|
|
475
|
+
x,
|
|
476
|
+
y,
|
|
477
|
+
width: width2,
|
|
478
|
+
height: height2,
|
|
479
|
+
rx: Math.ceil(stateFontSize / 2),
|
|
480
|
+
ry: Math.ceil(stateFontSize / 2),
|
|
481
|
+
fill: "none",
|
|
482
|
+
stroke: "currentColor",
|
|
483
|
+
strokeWidth: "2",
|
|
484
|
+
class: "state__box"
|
|
485
|
+
});
|
|
486
|
+
println(` <rect ${rectAttrs} />`);
|
|
487
|
+
const textAttrs = serializeAttrs({
|
|
488
|
+
class: "state__label",
|
|
489
|
+
"text-anchor": "middle",
|
|
490
|
+
fill: "currentColor",
|
|
491
|
+
x: Math.round(x + width2 / 2),
|
|
492
|
+
y: Math.round(y + height2 / 2 + stateFontSize / 4)
|
|
493
|
+
});
|
|
494
|
+
println(` <text ${textAttrs}>${text}</text>`);
|
|
495
|
+
}
|
|
496
|
+
println(" </g>");
|
|
497
|
+
}
|
|
498
|
+
println("</g>");
|
|
499
|
+
println("</svg>");
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/svg/buildStatechartsPanel.ts
|
|
503
|
+
function buildStatechartsPanel({
|
|
504
|
+
statechart,
|
|
505
|
+
newId,
|
|
506
|
+
println,
|
|
507
|
+
initialStateKey = "<initial>",
|
|
508
|
+
finalStateKey = "<final>"
|
|
509
|
+
}) {
|
|
510
|
+
render(statechart, println);
|
|
511
|
+
function render(statechart2, println2) {
|
|
512
|
+
println2(
|
|
513
|
+
` <details class="state-details" data-state-id="${statechart2.id}">`
|
|
514
|
+
);
|
|
515
|
+
println2(
|
|
516
|
+
` <summary class="state-details__label">${statechart2.key}</summary>`
|
|
517
|
+
);
|
|
518
|
+
println2(
|
|
519
|
+
` <div class="state-details__description"><div class="state-details__chart">`
|
|
520
|
+
);
|
|
521
|
+
const printChildren = (str) => println2(` ${str}`);
|
|
522
|
+
buildStatechartSvg({
|
|
523
|
+
graph: statechart2,
|
|
524
|
+
newId,
|
|
525
|
+
println: printChildren,
|
|
526
|
+
initialStateKey,
|
|
527
|
+
finalStateKey
|
|
528
|
+
});
|
|
529
|
+
println2(` </div>`);
|
|
530
|
+
for (const child of statechart2.children || []) {
|
|
531
|
+
render(child, printChildren);
|
|
532
|
+
}
|
|
533
|
+
println2(` </div>`);
|
|
534
|
+
println2(` </details>`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export { buildStatechartCss, buildStatechartStyles, buildStatechartSvg, buildStatechartsPanel, serializeCss, toKebabCase2 as toKebabCase };
|
|
@@ -2564,7 +2564,7 @@ function buildCharts({
|
|
|
2564
2564
|
finalStateKey = "<final>"
|
|
2565
2565
|
}) {
|
|
2566
2566
|
return buildChart(config);
|
|
2567
|
-
function buildChart(config2, id = newId("
|
|
2567
|
+
function buildChart(config2, id = newId("s")) {
|
|
2568
2568
|
const graph = buildFlatCharts({
|
|
2569
2569
|
lodash,
|
|
2570
2570
|
transitions: config2.transitions,
|
|
@@ -2589,7 +2589,7 @@ function buildCharts({
|
|
|
2589
2589
|
return {
|
|
2590
2590
|
id,
|
|
2591
2591
|
key: config2.key,
|
|
2592
|
-
graph,
|
|
2592
|
+
...graph,
|
|
2593
2593
|
children
|
|
2594
2594
|
};
|
|
2595
2595
|
}
|
package/dist/index-layout.d.ts
CHANGED
|
@@ -1,24 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
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;
|
|
1
|
+
import { T as Transition, P as Padding, G as GraphParamsProvider, d as TransitionsGraph, F as FsmStateConfig, S as StateChart } from './StateChart-pFSQIvtE.js';
|
|
2
|
+
export { D as Dimensions, a as Position, b as StateGraphEdge, c as StateGraphNode } from './StateChart-pFSQIvtE.js';
|
|
22
3
|
|
|
23
4
|
/**
|
|
24
5
|
* Build graphs corresponding to the specified transitions.
|
|
@@ -41,12 +22,6 @@ declare function buildFlatCharts({ lodash, transitions, newId, getStateParams, g
|
|
|
41
22
|
padding?: Padding;
|
|
42
23
|
} & GraphParamsProvider): TransitionsGraph;
|
|
43
24
|
|
|
44
|
-
type FsmStateConfig = {
|
|
45
|
-
key: string;
|
|
46
|
-
transitions: [string, string, string][];
|
|
47
|
-
states?: FsmStateConfig[];
|
|
48
|
-
};
|
|
49
|
-
|
|
50
25
|
/**
|
|
51
26
|
* Build graphs corresponding to the specified transitions.
|
|
52
27
|
* @param config - State configuration with transitions and sub-states.
|
|
@@ -93,4 +68,4 @@ lineGap, }?: {
|
|
|
93
68
|
height: number;
|
|
94
69
|
};
|
|
95
70
|
|
|
96
|
-
export {
|
|
71
|
+
export { FsmStateConfig, GraphParamsProvider, Padding, StateChart, Transition, TransitionsGraph, buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions };
|
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-IT7GBMYV.js';
|
|
2
2
|
import './chunk-NXWOFQMO.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
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-pFSQIvtE.js';
|
|
2
|
+
|
|
3
|
+
declare const a = "a";
|
|
4
|
+
|
|
5
|
+
export { a };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { a } from './chunk-X2UPWZLX.js';
|