@statewalker/fsm-charts 0.1.8 → 0.1.9

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.
@@ -0,0 +1,207 @@
1
+ // src/dom/index.ts
2
+ function getStateDescriptionRenderer({
3
+ element,
4
+ rootStateKey,
5
+ splitHeader = (header) => {
6
+ if (!header) return [void 0, ""];
7
+ const text = header.innerText.trim();
8
+ let key;
9
+ let label = text;
10
+ text.replace(/^\s*\[(.*)\]\s*(.*)$/, (_, k, l) => {
11
+ key = k.trim();
12
+ label = l.trim() || key;
13
+ return "";
14
+ });
15
+ return [key, label];
16
+ }
17
+ }) {
18
+ const docSections = buildSections(element);
19
+ const rootStateFilter = getStateDescriptionFilter(splitHeader, rootStateKey);
20
+ const rootDescription = findSection(docSections, rootStateFilter);
21
+ if (!rootDescription) {
22
+ return () => {
23
+ };
24
+ }
25
+ for (const node of visitSectionNodes(rootDescription)) {
26
+ if (node.nodeName[0] === "#") continue;
27
+ node.parentElement?.removeChild(node);
28
+ }
29
+ return (statesStack) => {
30
+ const stateKey = [...statesStack].pop();
31
+ const filter = getStateDescriptionFilter(splitHeader, stateKey);
32
+ const description = findSection(rootDescription, filter);
33
+ if (!description) return;
34
+ const elm = renderSections({
35
+ section: description,
36
+ deep: (s) => !rootStateFilter(s),
37
+ header: (header) => {
38
+ const [key, label] = splitHeader(header);
39
+ if (key === stateKey) {
40
+ return label || key;
41
+ }
42
+ return;
43
+ }
44
+ });
45
+ const details = elm?.querySelector("details");
46
+ details && (details.open = true);
47
+ return elm;
48
+ };
49
+ function getStateDescriptionFilter(splitHeader2, stateKey) {
50
+ if (!stateKey) return () => false;
51
+ return (section) => {
52
+ if (!section.header) return false;
53
+ const [key] = splitHeader2(section.header);
54
+ return key === stateKey;
55
+ };
56
+ }
57
+ }
58
+ function* visitDom(elm) {
59
+ for (let node = elm.firstChild; node; node = node.nextSibling) {
60
+ if (isDomElement(node)) {
61
+ if (node.tagName === "DIV") {
62
+ yield* visitDom(node);
63
+ } else {
64
+ yield node;
65
+ }
66
+ } else {
67
+ yield node;
68
+ }
69
+ }
70
+ }
71
+ function* visitSectionNodes(section) {
72
+ if (section.header) yield section.header;
73
+ yield* section.content;
74
+ for (const s of vistSections(section, true)) {
75
+ yield* visitSectionNodes(s);
76
+ }
77
+ }
78
+ function* vistSections(root, ignoreRoot = false) {
79
+ !ignoreRoot && (yield root);
80
+ for (const child of root.children) {
81
+ yield* vistSections(child, false);
82
+ }
83
+ }
84
+ function findSection(section, accept) {
85
+ for (const s of vistSections(section)) {
86
+ if (accept(s) === true) return s;
87
+ }
88
+ }
89
+ function isDomElement(node) {
90
+ return node.nodeType === Node.ELEMENT_NODE;
91
+ }
92
+ function newAligner(context) {
93
+ let level = -1;
94
+ return (newLevel, ...params) => {
95
+ while (level >= 0 && level >= newLevel) {
96
+ context.exit(level--, ...params);
97
+ }
98
+ while (level < newLevel) {
99
+ context.enter(++level, ...params);
100
+ }
101
+ };
102
+ }
103
+ function* toDomNodesIterator(root) {
104
+ yield* root instanceof Node ? visitDom(root) : root;
105
+ }
106
+ function buildSections(root) {
107
+ const nodes = toDomNodesIterator(root);
108
+ let top = null;
109
+ let peek = null;
110
+ const stack = [];
111
+ const aligner = newAligner({
112
+ enter(level, header) {
113
+ const section = {
114
+ header,
115
+ content: [],
116
+ children: []
117
+ };
118
+ top = top || section;
119
+ peek && peek.children.push(section);
120
+ peek = section;
121
+ stack.push(section);
122
+ },
123
+ exit() {
124
+ stack.pop();
125
+ peek = stack[stack.length - 1];
126
+ }
127
+ });
128
+ aligner(0);
129
+ let currentHeaderLevel = 0;
130
+ let currentLevel = 0;
131
+ for (const node of nodes) {
132
+ if (isDomElement(node)) {
133
+ if (/^H[1-6]$/i.test(node.tagName)) {
134
+ const headerLevel = +node.tagName.replace(/^H/, "");
135
+ currentLevel = headerLevel > currentHeaderLevel ? currentLevel + 1 : headerLevel < currentHeaderLevel ? currentLevel - 1 : currentLevel;
136
+ currentHeaderLevel = headerLevel;
137
+ aligner(currentLevel, node);
138
+ } else {
139
+ peek.content.push(node);
140
+ }
141
+ } else {
142
+ peek.content.push(node);
143
+ }
144
+ }
145
+ return top.content.length === 0 && top.children.length === 1 && !top.header ? top.children[0] : top;
146
+ }
147
+ function renderSections({
148
+ section,
149
+ deep = (s) => true,
150
+ header = (headerElm) => headerElm
151
+ }) {
152
+ const container = document.createElement("div");
153
+ const id = `id-${String(Math.random()).split(".")[1]}-${Date.now()}`;
154
+ container.setAttribute("id", id);
155
+ if (!section) return container;
156
+ const sectionView = renderSection(section);
157
+ container.append(sectionView);
158
+ const style = renderSectionStyles(id);
159
+ container.append(style);
160
+ return container;
161
+ function renderSectionStyles(id2) {
162
+ const prefix = id2 ? `#${id2}` : "";
163
+ const style2 = document.createElement("style");
164
+ container.appendChild(style2);
165
+ style2.innerHTML = `
166
+ ${prefix} details {
167
+ border: 1px solid #aaa;
168
+ border-radius: 4px;
169
+ padding: 0.5em 0.5em 0;
170
+ margin-bottom: 0.5em;
171
+ }
172
+
173
+ ${prefix} details > summary {
174
+ font-weight: bold;
175
+ margin: -0.5em -0.5em 0;
176
+ padding: 0.5em;
177
+ cursor: pointer;
178
+ }
179
+
180
+ ${prefix} details[open] {
181
+ padding: 0.5em;
182
+ }
183
+ `;
184
+ return style2;
185
+ }
186
+ function renderSection(section2) {
187
+ const details = document.createElement("details");
188
+ const summary = document.createElement("summary");
189
+ details.appendChild(summary);
190
+ const content = document.createElement("div");
191
+ details.appendChild(content);
192
+ const headerElm = header(section2.header);
193
+ headerElm && summary.append(headerElm);
194
+ for (const node of section2.content) {
195
+ if (node.nodeName[0] === "#") continue;
196
+ content.append(node.cloneNode(true));
197
+ }
198
+ if (deep(section2)) {
199
+ for (const child of section2.children) {
200
+ content.append(renderSection(child));
201
+ }
202
+ }
203
+ return details;
204
+ }
205
+ }
206
+
207
+ export { buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections };
@@ -0,0 +1,22 @@
1
+ type DomSection = {
2
+ header?: HTMLElement;
3
+ content: Node[];
4
+ children: DomSection[];
5
+ };
6
+ declare function getStateDescriptionRenderer({ element, rootStateKey, splitHeader, }: {
7
+ element: HTMLElement;
8
+ rootStateKey: string;
9
+ splitHeader: (header?: HTMLElement) => [stateKey: undefined | string, label: string];
10
+ }): (() => void) | ((statesStack: string[]) => HTMLDivElement | undefined);
11
+ declare function visitDom(elm: Node): Iterable<Node>;
12
+ declare function visitSectionNodes(section: DomSection): Iterable<Node>;
13
+ declare function vistSections(root: DomSection, ignoreRoot?: boolean): Generator<DomSection>;
14
+ declare function findSection(section: DomSection, accept: (section: DomSection) => boolean): DomSection | undefined;
15
+ declare function buildSections(root: HTMLElement | Iterable<Node>): DomSection;
16
+ declare function renderSections({ section, deep, header, }: {
17
+ section: DomSection;
18
+ deep?: (s: DomSection) => boolean;
19
+ header?: (header: HTMLElement) => undefined | string | HTMLElement;
20
+ }): HTMLDivElement;
21
+
22
+ export { type DomSection, buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections };
@@ -0,0 +1 @@
1
+ export { buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections } from './chunk-TZRPE2VD.js';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
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-DlcWaRml.js';
2
+ export { DomSection, buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections } from './index-dom.js';
2
3
  export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig } from './index-config.js';
3
4
  export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './index-layout.js';
4
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-De8e33ol.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { buildSections, findSection, getStateDescriptionRenderer, renderSections, visitDom, visitSectionNodes, vistSections } from './chunk-TZRPE2VD.js';
1
2
  export { concatTransitionsToString, serializeProcessConfig, splitTransitionString, toFsmStateConfig } from './chunk-SLWDNGWK.js';
2
3
  export { buildCharts, buildFlatCharts, getGraphParamsProvider, getLabelDimensions } from './chunk-U5LUJ7JS.js';
3
4
  export { activeStatesStyle, activeTransitionsStyle, buildStateDetailsStyle, buildStateDetailsStyleWithModifier, buildStatechartCss, buildStatechartStyles, buildStatechartStylesWithModifier, buildStatechartSvg, buildStatechartsPanel, selectedStatesStyle, selectedTransitionsStyle, serializeCss, toKebabCase } from './chunk-2XPO6T2M.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm-charts",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "HFSM Implementation",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker-fsm-charts",
@@ -0,0 +1,267 @@
1
+ export type DomSection = {
2
+ header?: HTMLElement;
3
+ content: Node[];
4
+ children: DomSection[];
5
+ };
6
+
7
+ export function getStateDescriptionRenderer({
8
+ element,
9
+ rootStateKey,
10
+ splitHeader = (
11
+ header?: HTMLElement,
12
+ ): [stateKey: undefined | string, label: string] => {
13
+ if (!header) return [undefined, ""];
14
+ const text = header.innerText.trim();
15
+ let key: string | undefined;
16
+ let label: string = text;
17
+ text.replace(/^\s*\[(.*)\]\s*(.*)$/, (_, k, l) => {
18
+ key = k.trim();
19
+ label = l.trim() || key;
20
+ return "";
21
+ });
22
+ return [key, label];
23
+ },
24
+ }: {
25
+ element: HTMLElement;
26
+ rootStateKey: string;
27
+ splitHeader: (
28
+ header?: HTMLElement,
29
+ ) => [stateKey: undefined | string, label: string];
30
+ }) {
31
+ const docSections = buildSections(element);
32
+ const rootStateFilter = getStateDescriptionFilter(splitHeader, rootStateKey);
33
+ const rootDescription = findSection(docSections, rootStateFilter);
34
+
35
+ if (!rootDescription) {
36
+ return () => {};
37
+ }
38
+
39
+ // Remove description nodes from DOM
40
+ for (const node of visitSectionNodes(rootDescription)) {
41
+ if (node.nodeName[0] === "#") continue;
42
+ node.parentElement?.removeChild(node);
43
+ }
44
+ // for (const section of vistSections(rootDescription)) {
45
+ // const [key, label] = splitHeader(section.header);
46
+ // console.log("??????", section.header?.innerText, key);
47
+ // if (!key) continue;
48
+
49
+ // section.header?.remove();
50
+ // for (const node of section.content) {
51
+ // if (node.nodeName[0] === "#") continue;
52
+ // node.parentElement?.removeChild(node);
53
+ // }
54
+ // }
55
+
56
+ return (statesStack: string[]) => {
57
+ const stateKey = [...statesStack].pop();
58
+ const filter = getStateDescriptionFilter(splitHeader, stateKey);
59
+ const description = findSection(rootDescription, filter);
60
+ if (!description) return;
61
+ const elm = renderSections({
62
+ section: description,
63
+ deep: (s) => !rootStateFilter(s),
64
+ header: (header) => {
65
+ const [key, label] = splitHeader(header);
66
+ if (key === stateKey) {
67
+ return label || key;
68
+ }
69
+ return;
70
+ },
71
+ });
72
+ const details = elm?.querySelector("details");
73
+ details && (details.open = true);
74
+ return elm;
75
+ };
76
+
77
+ function getStateDescriptionFilter(
78
+ splitHeader: (header: HTMLElement) => [stateKey?: string, label?: string],
79
+ stateKey?: string,
80
+ ) {
81
+ if (!stateKey) return () => false;
82
+ return (section: DomSection) => {
83
+ if (!section.header) return false;
84
+ const [key] = splitHeader(section.header);
85
+ return key === stateKey;
86
+ };
87
+ }
88
+ }
89
+
90
+ export function* visitDom(elm: Node): Iterable<Node> {
91
+ for (let node = elm.firstChild; node; node = node.nextSibling) {
92
+ if (isDomElement(node)) {
93
+ if (node.tagName === "DIV") {
94
+ yield* visitDom(node);
95
+ } else {
96
+ yield node;
97
+ }
98
+ } else {
99
+ yield node;
100
+ }
101
+ }
102
+ }
103
+
104
+ export function* visitSectionNodes(section: DomSection): Iterable<Node> {
105
+ if (section.header) yield section.header;
106
+ yield* section.content;
107
+ for (const s of vistSections(section, true)) {
108
+ yield* visitSectionNodes(s);
109
+ }
110
+ }
111
+
112
+ export function* vistSections(
113
+ root: DomSection,
114
+ ignoreRoot: boolean = false,
115
+ ): Generator<DomSection> {
116
+ !ignoreRoot && (yield root);
117
+ for (const child of root.children) {
118
+ yield* vistSections(child, false);
119
+ }
120
+ }
121
+
122
+ export function findSection(
123
+ section: DomSection,
124
+ accept: (section: DomSection) => boolean,
125
+ ) {
126
+ for (const s of vistSections(section)) {
127
+ if (accept(s) === true) return s;
128
+ }
129
+ }
130
+
131
+ function isDomElement(node: Node): node is HTMLElement {
132
+ return node.nodeType === Node.ELEMENT_NODE;
133
+ }
134
+
135
+ function newAligner<T extends unknown[] = []>(context: {
136
+ enter: (level: number, ...params: T) => void;
137
+ exit: (level: number, ...params: T) => void;
138
+ }) {
139
+ let level = -1;
140
+ return (newLevel: number, ...params: T) => {
141
+ while (level >= 0 && level >= newLevel) {
142
+ context.exit(level--, ...params);
143
+ }
144
+ while (level < newLevel) {
145
+ context.enter(++level, ...params);
146
+ }
147
+ };
148
+ }
149
+
150
+ function* toDomNodesIterator(root: Node | Iterable<Node>): Iterable<Node> {
151
+ yield* root instanceof Node ? visitDom(root) : root;
152
+ }
153
+
154
+ export function buildSections(root: HTMLElement | Iterable<Node>): DomSection {
155
+ const nodes = toDomNodesIterator(root);
156
+ let top: DomSection = null!;
157
+ let peek: DomSection = null!;
158
+ const stack: DomSection[] = [];
159
+ const aligner = newAligner<[HTMLElement?]>({
160
+ enter(level, header) {
161
+ const section: DomSection = {
162
+ header,
163
+ content: [],
164
+ children: [],
165
+ };
166
+ top = top || section;
167
+ peek && peek.children.push(section);
168
+ peek = section;
169
+ stack.push(section);
170
+ },
171
+ exit() {
172
+ stack.pop();
173
+ peek = stack[stack.length - 1];
174
+ },
175
+ });
176
+ aligner(0);
177
+ let currentHeaderLevel = 0;
178
+ let currentLevel = 0;
179
+ for (const node of nodes) {
180
+ if (isDomElement(node)) {
181
+ if (/^H[1-6]$/i.test(node.tagName)) {
182
+ const headerLevel = +node.tagName.replace(/^H/, "");
183
+ currentLevel =
184
+ headerLevel > currentHeaderLevel
185
+ ? currentLevel + 1
186
+ : headerLevel < currentHeaderLevel
187
+ ? currentLevel - 1
188
+ : currentLevel;
189
+ currentHeaderLevel = headerLevel;
190
+ aligner(currentLevel, node);
191
+ } else {
192
+ peek.content.push(node);
193
+ }
194
+ } else {
195
+ peek.content.push(node);
196
+ }
197
+ }
198
+ return top.content.length === 0 && top.children.length === 1 && !top.header
199
+ ? top.children[0]
200
+ : top;
201
+ }
202
+
203
+ export function renderSections({
204
+ section,
205
+ deep = (s) => true,
206
+ header = (headerElm) => headerElm,
207
+ }: {
208
+ section: DomSection;
209
+ deep?: (s: DomSection) => boolean;
210
+ header?: (header: HTMLElement) => undefined | string | HTMLElement;
211
+ }) {
212
+ const container = document.createElement("div");
213
+ const id = `id-${String(Math.random()).split(".")[1]}-${Date.now()}`;
214
+ container.setAttribute("id", id);
215
+ if (!section) return container;
216
+
217
+ const sectionView = renderSection(section);
218
+ container.append(sectionView);
219
+ const style = renderSectionStyles(id);
220
+ container.append(style);
221
+ return container;
222
+
223
+ function renderSectionStyles(id?: string) {
224
+ const prefix = id ? `#${id}` : "";
225
+ const style = document.createElement("style");
226
+ container.appendChild(style);
227
+ style.innerHTML = `
228
+ ${prefix} details {
229
+ border: 1px solid #aaa;
230
+ border-radius: 4px;
231
+ padding: 0.5em 0.5em 0;
232
+ margin-bottom: 0.5em;
233
+ }
234
+
235
+ ${prefix} details > summary {
236
+ font-weight: bold;
237
+ margin: -0.5em -0.5em 0;
238
+ padding: 0.5em;
239
+ cursor: pointer;
240
+ }
241
+
242
+ ${prefix} details[open] {
243
+ padding: 0.5em;
244
+ }
245
+ `;
246
+ return style;
247
+ }
248
+ function renderSection(section: DomSection) {
249
+ const details = document.createElement("details");
250
+ const summary = document.createElement("summary");
251
+ details.appendChild(summary);
252
+ const content = document.createElement("div");
253
+ details.appendChild(content);
254
+ const headerElm = header(section.header!);
255
+ headerElm && summary.append(headerElm);
256
+ for (const node of section.content) {
257
+ if (node.nodeName[0] === "#") continue;
258
+ content.append(node.cloneNode(true));
259
+ }
260
+ if (deep(section)) {
261
+ for (const child of section.children) {
262
+ content.append(renderSection(child));
263
+ }
264
+ }
265
+ return details;
266
+ }
267
+ }
@@ -0,0 +1 @@
1
+ export * from "./dom/index.ts";
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type * from "./types/index.ts";
2
+ export * from "./dom/index.ts";
2
3
  export * from "./config/index.ts";
3
4
  export * from "./layout/index.ts";
4
5
  export * from "./html/index.ts";