@statewalker/fsm-charts 0.0.2 → 0.0.3
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/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/layout/buildCharts.ts +1 -1
- package/src/svg/buildStatechartCss.ts +5 -118
- package/src/svg/buildStatechartStyles.ts +176 -0
- package/src/svg/buildStatechartSvg.ts +11 -11
- package/src/svg/buildStatechartsPanel.ts +6 -6
- package/src/svg/index.ts +1 -0
- package/src/utils/serializeCss.ts +42 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ export function buildCharts({
|
|
|
41
41
|
} & GraphParamsProvider): StateChart {
|
|
42
42
|
return buildChart(config);
|
|
43
43
|
|
|
44
|
-
function buildChart(config: FsmStateConfig, id: string = newId("
|
|
44
|
+
function buildChart(config: FsmStateConfig, id: string = newId("s")) {
|
|
45
45
|
const graph = buildFlatCharts({
|
|
46
46
|
lodash,
|
|
47
47
|
transitions: config.transitions,
|
|
@@ -1,120 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
--state--fill: white;
|
|
4
|
-
--state--stroke-color: silver;
|
|
5
|
-
--state--stroke-width: 1px;
|
|
6
|
-
|
|
7
|
-
--state-initial--fill: var(--state--stroke-color, silver);
|
|
8
|
-
--state-initial--stroke-color: var(--state--stroke-color, none);
|
|
9
|
-
--state-initial--stroke-width: var(--state--stroke-width, 0);
|
|
10
|
-
|
|
11
|
-
--state-final--fill: var(--state--fill, white);
|
|
12
|
-
--state-final--stroke-color: var(--state--stroke-color, silver);
|
|
13
|
-
--state-final--stroke-width: var(--state--stroke-width, 1px);
|
|
14
|
-
|
|
15
|
-
--state-label--color: gray;
|
|
16
|
-
--state-label--font-size: 14px;
|
|
17
|
-
--state-label--font-family: sans-serif;
|
|
18
|
-
|
|
19
|
-
--transition--fill: rgba(255,255,255,0.7);
|
|
20
|
-
--transition--stroke-color: none;
|
|
21
|
-
--transition--stroke-width: 1;
|
|
22
|
-
|
|
23
|
-
--transition-line--stroke-color: silver;
|
|
24
|
-
--transition-line--stroke-width: 1px;
|
|
25
|
-
|
|
26
|
-
--transition-label--color: gray;
|
|
27
|
-
--transition-label--font-size: 12px;
|
|
28
|
-
--transition-label--font-family: sans-serif;
|
|
29
|
-
|
|
30
|
-
--transition-marker--fill: var(--transition-line--stroke-color, gray);
|
|
31
|
-
|
|
32
|
-
.state-details {
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.state-details {
|
|
37
|
-
background-color: var(--state--fill, white);
|
|
38
|
-
border: var(--state--stroke-width, 1px) solid var(--state--stroke-color, silver);
|
|
39
|
-
border-radius: 0.5em;
|
|
40
|
-
padding: 0.5em;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.state-details .state-label {
|
|
44
|
-
cursor: pointer;
|
|
45
|
-
color: var(--transition-label--color, gray);
|
|
46
|
-
font-family: var(--state-label--font-family, sans-serif);
|
|
47
|
-
font-size: var(--state-label--font-size, 14px);
|
|
48
|
-
}
|
|
49
|
-
.state-details .state-description {
|
|
50
|
-
display: flex;
|
|
51
|
-
flex-direction: column;
|
|
52
|
-
gap: 0.5em;
|
|
53
|
-
}
|
|
54
|
-
.state-details .state-chart-container {
|
|
55
|
-
max-width: 100%;
|
|
56
|
-
overflow: auto;
|
|
57
|
-
scrollbar-width: thin;
|
|
58
|
-
}
|
|
1
|
+
import { serializeCss } from "../utils/serializeCss";
|
|
2
|
+
import { buildStatechartStyles } from "./buildStatechartStyles";
|
|
59
3
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.state-details .statechart .transition-marker {
|
|
65
|
-
fill : var(--transition-marker--fill, silver);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.state-details .statechart .transitions {
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.state-details .statechart .transitions .transition .transition-line {
|
|
72
|
-
stroke: var(--transition-line--stroke-color, silver);
|
|
73
|
-
stroke-width: var(--transition-line--stroke-width, 1px);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.state-details .statechart .transitions .transition .transition-box {
|
|
77
|
-
fill: var(--transition--fill, rgba(255,255,255,0.7));
|
|
78
|
-
stroke: var(--transition--stroke-color, none);
|
|
79
|
-
stroke-width: var(--transition--stroke-width, 1);
|
|
80
|
-
border-radius: 0.5em;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.state-details .statechart .transitions .transition .transition-label {
|
|
84
|
-
fill: var(--transition-label--color, gray);
|
|
85
|
-
font-size: var(--transition-label--font-size, 12px);
|
|
86
|
-
font-family: var(--transition-label--font-family, sans-serif);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/* ------------------------------------------ */
|
|
90
|
-
.state-details .statechart .states .state {
|
|
91
|
-
}
|
|
92
|
-
.state-details .statechart .states .state .state-box {
|
|
93
|
-
fill: var(--state--fill, white);
|
|
94
|
-
stroke: var(--state--stroke-color, silver);
|
|
95
|
-
stroke-width: var(--state--stroke-width, 1px);
|
|
96
|
-
border-radius: 0.5em;
|
|
97
|
-
}
|
|
98
|
-
.state-details .statechart .states .state .state-initial {
|
|
99
|
-
fill: var(--state-initial--fill, gray);
|
|
100
|
-
stroke: var(--state-initial--stroke-color, none);
|
|
101
|
-
stroke-width: var(--state-initial--stroke-width, 0);
|
|
102
|
-
}
|
|
103
|
-
.state-details .statechart .states .state .state-final {
|
|
104
|
-
fill: var(--state-final--fill, white);
|
|
105
|
-
stroke: var(--state-final--stroke-color, silver);
|
|
106
|
-
stroke-width: var(--state-final--stroke-width, 2);
|
|
107
|
-
}
|
|
108
|
-
.state-details .statechart .states .state .state-final.inner {
|
|
109
|
-
fill: var(--state-final--stroke-color, silver);
|
|
110
|
-
stroke: none;
|
|
111
|
-
stroke-width: 0;
|
|
112
|
-
}
|
|
113
|
-
.state-details .statechart .states .state .state-label {
|
|
114
|
-
fill: var(--state-label--color, gray);
|
|
115
|
-
font-size: var(--state-label--font-size, 14px);
|
|
116
|
-
font-family: var(--state-label--font-family, sans-serif);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
`;
|
|
4
|
+
export function buildStatechartCss({ prefix = "" } = {}): string {
|
|
5
|
+
const cssObject = buildStatechartStyles({ root: prefix || ":root" });
|
|
6
|
+
return serializeCss(cssObject);
|
|
120
7
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { CssTree } from "../utils/serializeCss";
|
|
2
|
+
|
|
3
|
+
function buildStateDetailsStyle() {
|
|
4
|
+
return {
|
|
5
|
+
".state-details": {
|
|
6
|
+
backgroundColor: "var(--state-background-color, white)",
|
|
7
|
+
border:
|
|
8
|
+
"var(--state-border-width, 1px) solid var(--state-border-color, currentColor)",
|
|
9
|
+
borderRadius: "0.5em",
|
|
10
|
+
padding: "0.5em",
|
|
11
|
+
},
|
|
12
|
+
".state-details__label": {
|
|
13
|
+
cursor: "pointer",
|
|
14
|
+
color: "var(--transition-label-color, currentColor)",
|
|
15
|
+
fontFamily: "var(--state-label-font-family, sans-serif)",
|
|
16
|
+
fontSize: "var(--state-label-font-size, 14px)",
|
|
17
|
+
},
|
|
18
|
+
".state-details__description": {
|
|
19
|
+
display: "flex",
|
|
20
|
+
flexDirection: "column",
|
|
21
|
+
gap: "0.5em",
|
|
22
|
+
},
|
|
23
|
+
".state-details__chart": {
|
|
24
|
+
maxWidth: "100%",
|
|
25
|
+
overflow: "auto",
|
|
26
|
+
scrollbarWidth: "thin",
|
|
27
|
+
},
|
|
28
|
+
//
|
|
29
|
+
".state-details--selected": {
|
|
30
|
+
backgroundColor: "var(--state-selected-background-color, white)",
|
|
31
|
+
border:
|
|
32
|
+
"var(--state-selected-border-width, 1px) solid var(--state-selected-border-color, currentColor)",
|
|
33
|
+
},
|
|
34
|
+
".state-details__label--selected": {
|
|
35
|
+
color: "var(--state-selected-label-color)",
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function buildStatechartStyles({
|
|
40
|
+
root = ":root",
|
|
41
|
+
}: { root?: string } = {}): CssTree {
|
|
42
|
+
return {
|
|
43
|
+
[root]: {
|
|
44
|
+
// --------------------------------------
|
|
45
|
+
// Normal states
|
|
46
|
+
"--state-background-color": "white",
|
|
47
|
+
"--state-border-color": "silver",
|
|
48
|
+
"--state-border-width": "1px",
|
|
49
|
+
"--state-label-color": "gray",
|
|
50
|
+
"--state-label-font-size": "14px",
|
|
51
|
+
"--state-label-font-family": "sans-serif",
|
|
52
|
+
|
|
53
|
+
// Selected states
|
|
54
|
+
"--state-selected-background-color": "#eee",
|
|
55
|
+
"--state-selected-border-color": "red",
|
|
56
|
+
"--state-selected-border-width": "2px",
|
|
57
|
+
"--state-selected-label-color": "red",
|
|
58
|
+
|
|
59
|
+
// --------------------------------------
|
|
60
|
+
// Normal transitions
|
|
61
|
+
"--transition-background-color": "rgba(255,255,255,0.7)",
|
|
62
|
+
"--transition-border-color": "none",
|
|
63
|
+
"--transition-border-width": "1px",
|
|
64
|
+
"--transition-line-color": "silver",
|
|
65
|
+
"--transition-line-width": "1px",
|
|
66
|
+
"--transition-label-color": "gray",
|
|
67
|
+
"--transition-label-font-size": "12px",
|
|
68
|
+
"--transition-label-font-family": "sans-serif",
|
|
69
|
+
|
|
70
|
+
// --------------------------------------
|
|
71
|
+
// Selected transitions
|
|
72
|
+
"--transition-selected-background-color": "#eee",
|
|
73
|
+
"--transition-selected-border-color": "none",
|
|
74
|
+
"--transition-selected-border-width": "0",
|
|
75
|
+
"--transition-selected-line-color": "red",
|
|
76
|
+
"--transition-selected-line-width": "1px",
|
|
77
|
+
"--transition-selected-label-color": "red",
|
|
78
|
+
|
|
79
|
+
// --------------------------------------
|
|
80
|
+
// --------------------------------------
|
|
81
|
+
...buildStateDetailsStyle(),
|
|
82
|
+
".transition": {},
|
|
83
|
+
".transition--selected": {
|
|
84
|
+
"--transition-background-color":
|
|
85
|
+
"var(--transition-selected-background-color)",
|
|
86
|
+
"--transition-border-color": "var(--transition-selected-border-color)",
|
|
87
|
+
"--transition-border-width": "var(--transition-selected-border-width)",
|
|
88
|
+
"--transition-line-color": "var(--transition-selected-line-color)",
|
|
89
|
+
"--transition-line-width": "var(--transition-selected-line-width)",
|
|
90
|
+
"--transition-label-color": "var(--transition-selected-label-color)",
|
|
91
|
+
},
|
|
92
|
+
".transition__marker": {
|
|
93
|
+
fill: "var(--transition-line-color, currentColor)",
|
|
94
|
+
stroke: "none",
|
|
95
|
+
},
|
|
96
|
+
".transition__line": {
|
|
97
|
+
stroke: "var(--transition-line-color, currentColor)",
|
|
98
|
+
strokeWidth: "var(--transition-line-width, 1px)",
|
|
99
|
+
},
|
|
100
|
+
".transition__box": {
|
|
101
|
+
fill: "var(--transition-background-color, rgba(255,255,255,0.7))",
|
|
102
|
+
stroke: "var(--transition-border-color, none)",
|
|
103
|
+
strokeWidth: "var(--transition-border-width, 1px)",
|
|
104
|
+
borderRadius: "0.5em",
|
|
105
|
+
},
|
|
106
|
+
".transition__label": {
|
|
107
|
+
fill: "var(--transition-label-color, currentColor)",
|
|
108
|
+
fontSize: "var(--transition-label-font-size, 12px)",
|
|
109
|
+
fontFamily: "var(--transition-label-font-family, sans-serif)",
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// ----------------------------------------------
|
|
113
|
+
// States
|
|
114
|
+
|
|
115
|
+
".state": {},
|
|
116
|
+
".state--selected": {
|
|
117
|
+
"--state-background-color": "var(--state-selected-background-color)",
|
|
118
|
+
"--state-border-color": "var(--state-selected-border-color)",
|
|
119
|
+
"--state-border-width": "var(--state-selected-border-width)",
|
|
120
|
+
"--state-label-color": "var(--state-selected-label-color)",
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// --------------------------
|
|
124
|
+
// Panels
|
|
125
|
+
".state__details": {
|
|
126
|
+
backgroundColor: "var(--state-background-color, white)",
|
|
127
|
+
border:
|
|
128
|
+
"var(--state-border-width, 1px) solid var(--state-border-color, currentColor)",
|
|
129
|
+
borderRadius: "0.5em",
|
|
130
|
+
padding: "0.5em",
|
|
131
|
+
},
|
|
132
|
+
".state__description": {
|
|
133
|
+
display: "flex",
|
|
134
|
+
flexDirection: "column",
|
|
135
|
+
gap: "0.5em",
|
|
136
|
+
},
|
|
137
|
+
".state__chart-container": {
|
|
138
|
+
maxWidth: "100%",
|
|
139
|
+
overflow: "auto",
|
|
140
|
+
scrollbarWidth: "thin",
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// --------------------------
|
|
144
|
+
// Statecharts
|
|
145
|
+
".state__box": {
|
|
146
|
+
fill: "var(--state-background-color, white)",
|
|
147
|
+
stroke: "var(--state-border-color, currentColor)",
|
|
148
|
+
strokeWidth: "var(--state-border-width, 1px)",
|
|
149
|
+
borderRadius: "0.5em",
|
|
150
|
+
},
|
|
151
|
+
".state__label": {
|
|
152
|
+
color: "var(--state-label-color, currentColor)",
|
|
153
|
+
fill: "var(--state-label-color, currentColor)",
|
|
154
|
+
fontSize: "var(--state-label-font-size, 14px)",
|
|
155
|
+
fontFamily: "var(--state-label-font-family, sans-serif)",
|
|
156
|
+
cursor: "pointer",
|
|
157
|
+
},
|
|
158
|
+
".state__initial": {
|
|
159
|
+
fill: "var(--state-border-color, currentColor)",
|
|
160
|
+
stroke: "var(--state-border-color, currentColor)",
|
|
161
|
+
strokeWidth: "var(--state-border-width, 2px)",
|
|
162
|
+
},
|
|
163
|
+
".state__final": {
|
|
164
|
+
fill: "white",
|
|
165
|
+
stroke: "var(--state-border-color, currentColor)",
|
|
166
|
+
strokeWidth: "var(--state-border-width, 2px)",
|
|
167
|
+
// ---------------------------
|
|
168
|
+
"&.state__final--inner": {
|
|
169
|
+
fill: "var(--state-border-color, currentColor)",
|
|
170
|
+
stroke: "none",
|
|
171
|
+
strokeWidth: "0",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -65,7 +65,7 @@ export function buildStatechartSvg({
|
|
|
65
65
|
.transitions .transition .transition-label
|
|
66
66
|
---------------------------------------------------
|
|
67
67
|
*/
|
|
68
|
-
println(`<g class="
|
|
68
|
+
println(`<g class="statechart__transitions">`);
|
|
69
69
|
|
|
70
70
|
const markerAttrs = serializeAttrs({
|
|
71
71
|
refX: 19,
|
|
@@ -73,7 +73,7 @@ export function buildStatechartSvg({
|
|
|
73
73
|
markerWidth: 20,
|
|
74
74
|
markerHeight: 14,
|
|
75
75
|
orient: "auto",
|
|
76
|
-
class: "
|
|
76
|
+
class: "transition__marker",
|
|
77
77
|
stroke: "none",
|
|
78
78
|
fill: "currentColor",
|
|
79
79
|
});
|
|
@@ -119,7 +119,7 @@ export function buildStatechartSvg({
|
|
|
119
119
|
base.lineEnd();
|
|
120
120
|
|
|
121
121
|
const serializedAttrs = serializeAttrs({
|
|
122
|
-
class: "
|
|
122
|
+
class: "transition__line",
|
|
123
123
|
fill: "none",
|
|
124
124
|
stroke: "currentColor",
|
|
125
125
|
strokeWidth: "1px",
|
|
@@ -144,12 +144,12 @@ export function buildStatechartSvg({
|
|
|
144
144
|
fill: "rgba(255,255,255,0.7)",
|
|
145
145
|
stroke: "none",
|
|
146
146
|
strokeWidth: "1",
|
|
147
|
-
class: "
|
|
147
|
+
class: "transition__box",
|
|
148
148
|
});
|
|
149
149
|
println(` <rect ${rectAttrs} />`);
|
|
150
150
|
|
|
151
151
|
const textAttrs = serializeAttrs({
|
|
152
|
-
class: "
|
|
152
|
+
class: "transition__label",
|
|
153
153
|
"text-anchor": "middle",
|
|
154
154
|
fill: "currentColor",
|
|
155
155
|
x: Math.round(x + width / 2),
|
|
@@ -179,7 +179,7 @@ export function buildStatechartSvg({
|
|
|
179
179
|
const initialStateRadius = 6;
|
|
180
180
|
const finalStateRadius = initialStateRadius / 2;
|
|
181
181
|
|
|
182
|
-
println(`<g class="
|
|
182
|
+
println(`<g class="statechart__states">`);
|
|
183
183
|
for (const node of nodes) {
|
|
184
184
|
const { key, id, state: text } = node;
|
|
185
185
|
const { x, y, width, height } = getBoxParams({ ...node, padding: shift });
|
|
@@ -190,7 +190,7 @@ export function buildStatechartSvg({
|
|
|
190
190
|
const cy = Math.round(y + height / 2);
|
|
191
191
|
if (key === initialStateKey) {
|
|
192
192
|
const serializedAttrs = serializeAttrs({
|
|
193
|
-
class: "
|
|
193
|
+
class: "state__initial",
|
|
194
194
|
cx,
|
|
195
195
|
cy,
|
|
196
196
|
r: initialStateRadius,
|
|
@@ -201,7 +201,7 @@ export function buildStatechartSvg({
|
|
|
201
201
|
println(` <circle ${serializedAttrs} />`);
|
|
202
202
|
} else {
|
|
203
203
|
let serializedAttrs = serializeAttrs({
|
|
204
|
-
class: "
|
|
204
|
+
class: "state__final",
|
|
205
205
|
cx,
|
|
206
206
|
cy,
|
|
207
207
|
r: initialStateRadius,
|
|
@@ -212,7 +212,7 @@ export function buildStatechartSvg({
|
|
|
212
212
|
println(` <circle ${serializedAttrs} />`);
|
|
213
213
|
|
|
214
214
|
serializedAttrs = serializeAttrs({
|
|
215
|
-
class: "
|
|
215
|
+
class: "state__final state__final--inner",
|
|
216
216
|
cx,
|
|
217
217
|
cy,
|
|
218
218
|
r: finalStateRadius,
|
|
@@ -233,12 +233,12 @@ export function buildStatechartSvg({
|
|
|
233
233
|
fill: "none",
|
|
234
234
|
stroke: "currentColor",
|
|
235
235
|
strokeWidth: "2",
|
|
236
|
-
class: "
|
|
236
|
+
class: "state__box",
|
|
237
237
|
});
|
|
238
238
|
println(` <rect ${rectAttrs} />`);
|
|
239
239
|
|
|
240
240
|
const textAttrs = serializeAttrs({
|
|
241
|
-
class: "
|
|
241
|
+
class: "state__label",
|
|
242
242
|
"text-anchor": "middle",
|
|
243
243
|
fill: "currentColor",
|
|
244
244
|
x: Math.round(x + width / 2),
|
|
@@ -17,9 +17,9 @@ export function buildStatechartsPanel({
|
|
|
17
17
|
render(statechart, println);
|
|
18
18
|
|
|
19
19
|
function render(statechart: StateChart, println: (str: string) => void) {
|
|
20
|
-
println(
|
|
21
|
-
println(`
|
|
22
|
-
println(`
|
|
20
|
+
println(` <details class="state-details" data-state-id="${statechart.id}">`);
|
|
21
|
+
println(` <summary class="state-details__label">${statechart.key}</summary>`);
|
|
22
|
+
println(` <div class="state-details__description"><div class="state-details__chart">`);
|
|
23
23
|
const printChildren = (str: string) => println(` ${str}`);
|
|
24
24
|
buildStatechartSvg({
|
|
25
25
|
graph: statechart.graph,
|
|
@@ -28,11 +28,11 @@ export function buildStatechartsPanel({
|
|
|
28
28
|
initialStateKey,
|
|
29
29
|
finalStateKey,
|
|
30
30
|
});
|
|
31
|
-
println(`
|
|
31
|
+
println(` </div>`);
|
|
32
32
|
for (const child of statechart.children || []) {
|
|
33
33
|
render(child, printChildren);
|
|
34
34
|
}
|
|
35
|
-
println(`
|
|
36
|
-
println(
|
|
35
|
+
println(` </div>`);
|
|
36
|
+
println(` </details>`);
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/svg/index.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { toKebabCase } from "./toKebabCase.ts";
|
|
2
|
+
|
|
3
|
+
export interface CssTree extends Record<string, string | number | CssTree> {}
|
|
4
|
+
|
|
5
|
+
export function serializeCss(obj: CssTree) {
|
|
6
|
+
return visitStyle(obj).join("\n");
|
|
7
|
+
|
|
8
|
+
function visitStyle(
|
|
9
|
+
obj: CssTree,
|
|
10
|
+
lines: string[] = [],
|
|
11
|
+
prefixes: string[] = [""]
|
|
12
|
+
) {
|
|
13
|
+
for (const [key, style] of Object.entries(obj)) {
|
|
14
|
+
const selectors = [];
|
|
15
|
+
for (const sel of key.split(",").map((_) => _.trim())) {
|
|
16
|
+
for (const prefix of prefixes) {
|
|
17
|
+
const selector = sel.replace(/^(.)/, (_, ch) =>
|
|
18
|
+
ch === "&" ? prefix : prefix.length > 0 ? prefix + " " + ch : ch
|
|
19
|
+
);
|
|
20
|
+
selectors.push(selector);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const selectorRules = [];
|
|
24
|
+
const extensions: string[] = [];
|
|
25
|
+
for (let [key, value] of Object.entries(style)) {
|
|
26
|
+
if (typeof value === "object") {
|
|
27
|
+
visitStyle({ [key]: value }, extensions, selectors);
|
|
28
|
+
} else {
|
|
29
|
+
key = toKebabCase(key);
|
|
30
|
+
selectorRules.push(` ${key}: ${value};`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (selectorRules.length) {
|
|
34
|
+
lines.push(`${selectors.join(",\n")} {`);
|
|
35
|
+
lines.push(...selectorRules);
|
|
36
|
+
lines.push(`}`);
|
|
37
|
+
}
|
|
38
|
+
lines.push(...extensions);
|
|
39
|
+
}
|
|
40
|
+
return lines;
|
|
41
|
+
}
|
|
42
|
+
}
|