@statewalker/fsm-charts 0.0.1 → 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 -13
- 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,8 +119,7 @@ export function buildStatechartSvg({
|
|
|
119
119
|
base.lineEnd();
|
|
120
120
|
|
|
121
121
|
const serializedAttrs = serializeAttrs({
|
|
122
|
-
class: "
|
|
123
|
-
"transition-id": id,
|
|
122
|
+
class: "transition__line",
|
|
124
123
|
fill: "none",
|
|
125
124
|
stroke: "currentColor",
|
|
126
125
|
strokeWidth: "1px",
|
|
@@ -145,12 +144,12 @@ export function buildStatechartSvg({
|
|
|
145
144
|
fill: "rgba(255,255,255,0.7)",
|
|
146
145
|
stroke: "none",
|
|
147
146
|
strokeWidth: "1",
|
|
148
|
-
class: "
|
|
147
|
+
class: "transition__box",
|
|
149
148
|
});
|
|
150
149
|
println(` <rect ${rectAttrs} />`);
|
|
151
150
|
|
|
152
151
|
const textAttrs = serializeAttrs({
|
|
153
|
-
class: "
|
|
152
|
+
class: "transition__label",
|
|
154
153
|
"text-anchor": "middle",
|
|
155
154
|
fill: "currentColor",
|
|
156
155
|
x: Math.round(x + width / 2),
|
|
@@ -180,7 +179,7 @@ export function buildStatechartSvg({
|
|
|
180
179
|
const initialStateRadius = 6;
|
|
181
180
|
const finalStateRadius = initialStateRadius / 2;
|
|
182
181
|
|
|
183
|
-
println(`<g class="
|
|
182
|
+
println(`<g class="statechart__states">`);
|
|
184
183
|
for (const node of nodes) {
|
|
185
184
|
const { key, id, state: text } = node;
|
|
186
185
|
const { x, y, width, height } = getBoxParams({ ...node, padding: shift });
|
|
@@ -191,7 +190,7 @@ export function buildStatechartSvg({
|
|
|
191
190
|
const cy = Math.round(y + height / 2);
|
|
192
191
|
if (key === initialStateKey) {
|
|
193
192
|
const serializedAttrs = serializeAttrs({
|
|
194
|
-
class: "
|
|
193
|
+
class: "state__initial",
|
|
195
194
|
cx,
|
|
196
195
|
cy,
|
|
197
196
|
r: initialStateRadius,
|
|
@@ -202,7 +201,7 @@ export function buildStatechartSvg({
|
|
|
202
201
|
println(` <circle ${serializedAttrs} />`);
|
|
203
202
|
} else {
|
|
204
203
|
let serializedAttrs = serializeAttrs({
|
|
205
|
-
class: "
|
|
204
|
+
class: "state__final",
|
|
206
205
|
cx,
|
|
207
206
|
cy,
|
|
208
207
|
r: initialStateRadius,
|
|
@@ -213,7 +212,7 @@ export function buildStatechartSvg({
|
|
|
213
212
|
println(` <circle ${serializedAttrs} />`);
|
|
214
213
|
|
|
215
214
|
serializedAttrs = serializeAttrs({
|
|
216
|
-
class: "
|
|
215
|
+
class: "state__final state__final--inner",
|
|
217
216
|
cx,
|
|
218
217
|
cy,
|
|
219
218
|
r: finalStateRadius,
|
|
@@ -234,17 +233,16 @@ export function buildStatechartSvg({
|
|
|
234
233
|
fill: "none",
|
|
235
234
|
stroke: "currentColor",
|
|
236
235
|
strokeWidth: "2",
|
|
237
|
-
class: "
|
|
236
|
+
class: "state__box",
|
|
238
237
|
});
|
|
239
238
|
println(` <rect ${rectAttrs} />`);
|
|
240
239
|
|
|
241
240
|
const textAttrs = serializeAttrs({
|
|
242
|
-
class: "
|
|
241
|
+
class: "state__label",
|
|
243
242
|
"text-anchor": "middle",
|
|
244
243
|
fill: "currentColor",
|
|
245
244
|
x: Math.round(x + width / 2),
|
|
246
245
|
y: Math.round(y + height / 2 + stateFontSize / 4),
|
|
247
|
-
"data-state-id": id,
|
|
248
246
|
});
|
|
249
247
|
println(` <text ${textAttrs}>${text}</text>`);
|
|
250
248
|
}
|
|
@@ -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
|
+
}
|