@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.
@@ -53,7 +53,7 @@ type Position = {
53
53
  type StateGraphNode = Dimensions & Position & {
54
54
  id: string;
55
55
  key: string;
56
- state: string;
56
+ text: string;
57
57
  } & Record<string, any>;
58
58
 
59
59
  /**
@@ -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
- const graph = buildFlatCharts({
2584
- lodash,
2585
- transitions: config2.transitions || [],
2586
- newId,
2587
- getStateParams,
2588
- getTransitionParams,
2589
- padding,
2590
- initialStateKey,
2591
- finalStateKey,
2592
- vertical,
2593
- direction
2594
- });
2595
- const index = graph.nodes.reduce((index2, node) => {
2596
- index2[node.key] = node.id;
2597
- return index2;
2598
- }, {});
2599
- const children = [];
2600
- if (config2.states) {
2601
- for (const child of config2.states) {
2602
- const childId = index[child.key];
2603
- children.push(buildChart(child, childId));
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-details__description": {
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.key}</summary>`
580
+ ` <summary class="state-details__label">${statechart2.text ?? statechart2.state}</summary>`
581
581
  );
582
582
  println2(
583
- ` <div class="state-details__description"><div class="state-details__chart">`
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 { buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections };
215
+ export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections };
@@ -55,14 +55,26 @@ var RuntimeStatechartApi = class {
55
55
  this.selectState(id);
56
56
  }
57
57
  }
58
- // toggleStatePanel(stateId) {
59
- // const elm = this.element.querySelector(
60
- // `details[data-state-id="${stateId}"]`
61
- // );
62
- // if (elm) {
63
- // elm.open = !elm.open;
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({ statechart }) {
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
@@ -1,5 +1,5 @@
1
- import { T as Transition, F as FsmStateConfig } from './StateChart-CdOU9XAq.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-CdOU9XAq.js';
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
 
@@ -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 { type DomSection, buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections };
21
+ export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections };
package/dist/index-dom.js CHANGED
@@ -1 +1 @@
1
- export { buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections } from './chunk-AVTSJE25.js';
1
+ export { findSection, getStateDescriptionRenderer, renderSections, visitSectionNodes, vistSections } from './chunk-LRMTXZ55.js';
@@ -1,4 +1,4 @@
1
- import { S as StateChart, d as TransitionsGraph } from './StateChart-CdOU9XAq.js';
1
+ import { S as StateChart, d as TransitionsGraph } from './StateChart-BYBjTmC2.js';
2
2
 
3
3
  declare function buildStatechartCss({ prefix }?: {
4
4
  prefix?: string | undefined;
@@ -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-CdOU9XAq.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-BKMwwc5Q.js';
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';
@@ -1,2 +1,2 @@
1
- export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle } from './chunk-GCVBCAOL.js';
1
+ export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle } from './chunk-G25OZI4I.js';
2
2
  import './chunk-FIQDLBWD.js';
@@ -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-CdOU9XAq.js';
2
- export { D as Dimensions, L as Label, a as Position, b as StateGraphEdge, c as StateGraphNode } from './StateChart-CdOU9XAq.js';
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
  /**
@@ -1,2 +1,2 @@
1
- export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-NWCC4WSF.js';
1
+ export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-AZBRQNTU.js';
2
2
  import './chunk-FIQDLBWD.js';
@@ -1,5 +1,5 @@
1
- import { S as StateChart, c as StateGraphNode, b as StateGraphEdge } from './StateChart-CdOU9XAq.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-CdOU9XAq.js';
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
- constructor({ statechart }: {
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
@@ -1 +1 @@
1
- export { RuntimeStatechartApi, StateChartIndex } from './chunk-N77KKO5K.js';
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-CdOU9XAq.js';
2
- export { DomSection, buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections } from './index-dom.js';
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-BKMwwc5Q.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-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 { buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections } from './chunk-AVTSJE25.js';
3
- export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle, serializeCss, toKebabCase } from './chunk-GCVBCAOL.js';
4
- export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-NWCC4WSF.js';
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-N77KKO5K.js';
6
+ export { RuntimeStatechartApi, StateChartIndex } from './chunk-TZHCBZVP.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm-charts",
3
- "version": "0.1.13",
3
+ "version": "0.2.1",
4
4
  "description": "HFSM Implementation",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker-fsm-charts",
@@ -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
- export type DomSection = {
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
+ }
@@ -95,6 +95,7 @@ ${css}
95
95
  transitions.forEach((t) => api.selectTransition(t.id));
96
96
  }
97
97
  }
98
+ api.focusStates(...nodeIds);
98
99
  })
99
100
  );
100
101
 
@@ -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-details__description": {
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.key}</summary>`
24
+ ` <summary class="state-details__label">${statechart.text ?? statechart.state}</summary>`
25
25
  );
26
26
  println(
27
- ` <div class="state-details__description"><div class="state-details__chart">`
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
- const graph = buildFlatCharts({
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
- const children: StateChart[] = [];
62
- if (config.states) {
63
- for (const child of config.states) {
64
- const childId = index[child.key];
65
- children.push(buildChart(child, childId));
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
- // toggleStatePanel(stateId) {
73
- // const elm = this.element.querySelector(
74
- // `details[data-state-id="${stateId}"]`
75
- // );
76
- // if (elm) {
77
- // elm.open = !elm.open;
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({ statechart }: { statechart: StateChart }) {
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,
@@ -8,5 +8,6 @@ export type StateGraphNode = Dimensions &
8
8
  Position & {
9
9
  id: string;
10
10
  key: string;
11
- state: string;
11
+ text: string;
12
+ // state: string;
12
13
  } & Record<string, any>;