@teleporthq/teleport-plugin-css 0.44.0-alpha.0 → 0.44.0
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/__tests__/component-scoped.ts +4 -2
- package/__tests__/referenced-styles.ts +61 -0
- package/__tests__/selector-paren-sanitize.ts +69 -0
- package/dist/cjs/index.d.ts +9 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +510 -48
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/style-sheet.d.ts.map +1 -1
- package/dist/cjs/style-sheet.js +8 -2
- package/dist/cjs/style-sheet.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +11 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +125 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/index.d.ts +9 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +512 -50
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/style-sheet.d.ts.map +1 -1
- package/dist/esm/style-sheet.js +9 -3
- package/dist/esm/style-sheet.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +11 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +120 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +663 -64
- package/src/style-sheet.ts +10 -4
- package/src/utils.ts +132 -0
package/src/index.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
import { StringUtils, UIDLUtils, GenericUtils } from '@teleporthq/teleport-shared'
|
|
3
|
+
import {
|
|
4
|
+
StyleUtils,
|
|
5
|
+
StyleBuilders,
|
|
6
|
+
HASTUtils,
|
|
7
|
+
HASTBuilders,
|
|
8
|
+
ASTUtils,
|
|
9
|
+
createBinaryExpression,
|
|
10
|
+
} from '@teleporthq/teleport-plugin-common'
|
|
3
11
|
import * as types from '@babel/types'
|
|
4
12
|
import {
|
|
5
13
|
ComponentPluginFactory,
|
|
6
14
|
ComponentPlugin,
|
|
15
|
+
UIDLConditionExpressionEntry,
|
|
7
16
|
UIDLDynamicReference,
|
|
8
17
|
UIDLStyleDefinitions,
|
|
9
18
|
ChunkType,
|
|
@@ -12,8 +21,17 @@ import {
|
|
|
12
21
|
UIDLElementNodeReferenceStyles,
|
|
13
22
|
UIDLStyleMediaQueryScreenSizeCondition,
|
|
14
23
|
PluginCSS,
|
|
24
|
+
UIDLElement,
|
|
25
|
+
UIDLElementNode,
|
|
26
|
+
UIDLExpressionValue,
|
|
27
|
+
StateDefaultValueTypes,
|
|
28
|
+
PropDefaultValueTypes,
|
|
29
|
+
UIDLStyleInlineAsset,
|
|
30
|
+
UIDLFontAsset,
|
|
31
|
+
UIDLGlobalStateDefinition,
|
|
15
32
|
} from '@teleporthq/teleport-types'
|
|
16
33
|
import { createStyleSheetPlugin } from './style-sheet'
|
|
34
|
+
import { createConditionalStatement } from './utils'
|
|
17
35
|
|
|
18
36
|
interface CSSPluginConfig {
|
|
19
37
|
chunkName: string
|
|
@@ -21,11 +39,38 @@ interface CSSPluginConfig {
|
|
|
21
39
|
componentDecoratorChunkName: string
|
|
22
40
|
inlineStyleAttributeKey: string // style vs :style vs ...
|
|
23
41
|
classAttributeName: string // class vs className
|
|
24
|
-
forceScoping: boolean // class names get the component name prefix
|
|
25
42
|
templateStyle: 'html' | 'jsx'
|
|
26
43
|
declareDependency: 'import' | 'decorator' | 'none'
|
|
27
44
|
dynamicVariantPrefix?: string
|
|
28
45
|
staticPropReferences?: boolean
|
|
46
|
+
standaloneHtmlComponents?: boolean
|
|
47
|
+
/**
|
|
48
|
+
* An element's own styles written under its class twice (`.title.title`),
|
|
49
|
+
* the weight of two classes. The Next export's styled-jsx scopes them with a
|
|
50
|
+
* second class and the editor weighs them the same, so an element's own
|
|
51
|
+
* colour beats a two-class combinator like `.hero .title` there; under one
|
|
52
|
+
* class it lost in the static HTML export, which showed another colour.
|
|
53
|
+
*/
|
|
54
|
+
doubleElementClass?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const prefixUrlPathsInCss = (css: string, folderPath: string[]): string => {
|
|
58
|
+
const relativePrefix = GenericUtils.localRelativePath(join(...folderPath), './')
|
|
59
|
+
if (!relativePrefix) {
|
|
60
|
+
return css
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return css.replace(/url\(["']?(.*?)["']?\)/g, (match, url) => {
|
|
64
|
+
if (
|
|
65
|
+
url.startsWith('http') ||
|
|
66
|
+
url.startsWith('data:') ||
|
|
67
|
+
url.startsWith('../') ||
|
|
68
|
+
url.startsWith('#')
|
|
69
|
+
) {
|
|
70
|
+
return match
|
|
71
|
+
}
|
|
72
|
+
return `url("${join(relativePrefix, url)}")`
|
|
73
|
+
})
|
|
29
74
|
}
|
|
30
75
|
|
|
31
76
|
const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
@@ -37,15 +82,27 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
37
82
|
classAttributeName = 'class',
|
|
38
83
|
templateStyle = 'html',
|
|
39
84
|
declareDependency = 'none',
|
|
40
|
-
forceScoping = false,
|
|
41
85
|
dynamicVariantPrefix,
|
|
42
86
|
staticPropReferences = false,
|
|
87
|
+
standaloneHtmlComponents = false,
|
|
88
|
+
doubleElementClass = false,
|
|
43
89
|
} = config || {}
|
|
44
90
|
|
|
45
91
|
const cssPlugin: ComponentPlugin = async (structure) => {
|
|
46
92
|
const { uidl, chunks, dependencies, options } = structure
|
|
47
|
-
const {
|
|
48
|
-
|
|
93
|
+
const {
|
|
94
|
+
node,
|
|
95
|
+
styleSetDefinitions: componentStyleSet = {},
|
|
96
|
+
propDefinitions = {},
|
|
97
|
+
stateDefinitions = {},
|
|
98
|
+
} = uidl
|
|
99
|
+
const {
|
|
100
|
+
projectStyleSet,
|
|
101
|
+
designLanguage: { tokens = {} } = {},
|
|
102
|
+
isRootComponent,
|
|
103
|
+
globalStateDefinitions = {},
|
|
104
|
+
prefixInlineClasses,
|
|
105
|
+
} = options || {}
|
|
49
106
|
const {
|
|
50
107
|
styleSetDefinitions = {},
|
|
51
108
|
fileName: projectStyleSheetName,
|
|
@@ -71,7 +128,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
71
128
|
(chunk) => chunk.name === componentDecoratorChunkName
|
|
72
129
|
)
|
|
73
130
|
|
|
74
|
-
const
|
|
131
|
+
const jsxNodesLookup = templateChunk.meta.nodesLookup as Record<
|
|
75
132
|
string,
|
|
76
133
|
HastNode | types.JSXElement
|
|
77
134
|
>
|
|
@@ -81,49 +138,65 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
81
138
|
: ('' as string)
|
|
82
139
|
|
|
83
140
|
const cssMap: string[] = []
|
|
141
|
+
// A doubled element class nests its rule one level down (see `doubleElementClass`).
|
|
84
142
|
const mediaStylesMap: Record<
|
|
85
143
|
string,
|
|
86
|
-
Array<{ [x: string]: Record<string, string | number
|
|
144
|
+
Array<{ [x: string]: Record<string, string | number | Record<string, string | number>> }>
|
|
87
145
|
> = {}
|
|
146
|
+
const usedProjectStyleIds: Set<string> = new Set()
|
|
88
147
|
|
|
89
|
-
|
|
148
|
+
const generateStylesForElementNode = (element: UIDLElement) => {
|
|
90
149
|
const classNamesToAppend: Set<string> = new Set()
|
|
91
|
-
const dynamicVariantsToAppend: Set<string> = new Set()
|
|
150
|
+
const dynamicVariantsToAppend: Set<string | types.ConditionalExpression> = new Set()
|
|
92
151
|
const {
|
|
93
152
|
style = {},
|
|
94
153
|
key,
|
|
95
154
|
referencedStyles = {},
|
|
96
|
-
dependency,
|
|
97
155
|
attrs = {},
|
|
98
156
|
elementType,
|
|
157
|
+
dependency,
|
|
99
158
|
} = element
|
|
100
|
-
const
|
|
159
|
+
const { dynamicStyleBindings } = element
|
|
160
|
+
const hasDynamicBindings =
|
|
161
|
+
dynamicStyleBindings && Object.keys(dynamicStyleBindings).length > 0
|
|
101
162
|
|
|
102
|
-
|
|
163
|
+
const root = jsxNodesLookup[key]
|
|
164
|
+
if (!root) {
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Refer to line 323 all component scoped styles are appended with component name by default
|
|
169
|
+
if (dependency?.type === 'local') {
|
|
103
170
|
StyleBuilders.setPropValueForCompStyle({
|
|
104
171
|
attrs,
|
|
105
|
-
|
|
106
|
-
jsxNodesLookup: templateLookup,
|
|
172
|
+
root,
|
|
107
173
|
templateStyle,
|
|
174
|
+
// elementType is used here in-order to target the component name that the class is actually defined.
|
|
175
|
+
// Here we are appendigng to the node where the component is being called.
|
|
108
176
|
getClassName: (styleName: string) =>
|
|
109
|
-
|
|
177
|
+
StringUtils.camelCaseToDashCase(elementType + styleName),
|
|
110
178
|
})
|
|
111
179
|
}
|
|
112
180
|
|
|
113
181
|
if (
|
|
114
182
|
Object.keys(style).length === 0 &&
|
|
115
183
|
Object.keys(referencedStyles).length === 0 &&
|
|
116
|
-
Object.keys(componentStyleSet).length === 0
|
|
184
|
+
Object.keys(componentStyleSet).length === 0 &&
|
|
185
|
+
!hasDynamicBindings
|
|
117
186
|
) {
|
|
118
187
|
return
|
|
119
188
|
}
|
|
120
189
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
190
|
+
// Inline (node-key-derived) classes can share a name with a real project
|
|
191
|
+
// class. When `prefixInlineClasses` is set (AI editor codegen only), tag
|
|
192
|
+
// every inline class so the consumer can tell fake from real by name.
|
|
193
|
+
const className = prefixInlineClasses
|
|
194
|
+
? `${prefixInlineClasses}${StringUtils.camelCaseToDashCase(key)}`
|
|
195
|
+
: StringUtils.camelCaseToDashCase(key)
|
|
196
|
+
// `&` is the class; doubled it reads `.name.name`. Nested, because JSS
|
|
197
|
+
// escapes a dot written into the class name itself. The element still
|
|
198
|
+
// wears the class once.
|
|
199
|
+
const own = doubleElementClass ? `&.${className}` : '&'
|
|
127
200
|
|
|
128
201
|
const { staticStyles, dynamicStyles, tokenStyles } =
|
|
129
202
|
UIDLUtils.splitDynamicAndStaticStyles(style)
|
|
@@ -134,32 +207,64 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
134
207
|
...StyleUtils.getCSSVariablesContentFromTokenStyles(tokenStyles),
|
|
135
208
|
} as Record<string, string | number>
|
|
136
209
|
|
|
137
|
-
cssMap.push(
|
|
210
|
+
cssMap.push(
|
|
211
|
+
doubleElementClass
|
|
212
|
+
? StyleBuilders.createCSSClassWithSelector(className, own, collectedStyles)
|
|
213
|
+
: StyleBuilders.createCSSClass(className, collectedStyles)
|
|
214
|
+
)
|
|
138
215
|
classNamesToAppend.add(className)
|
|
139
216
|
}
|
|
140
217
|
|
|
218
|
+
let jsxInlineStyles: Record<string, unknown> | null = null
|
|
219
|
+
|
|
141
220
|
if (Object.keys(dynamicStyles).length > 0) {
|
|
221
|
+
for (const styleValue of Object.values(dynamicStyles)) {
|
|
222
|
+
if (
|
|
223
|
+
styleValue.type === 'dynamic' &&
|
|
224
|
+
styleValue.content.referenceType === 'global' &&
|
|
225
|
+
templateChunk.meta.globalReferences
|
|
226
|
+
) {
|
|
227
|
+
;(templateChunk.meta.globalReferences as string[]).push(styleValue.content.id)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
142
231
|
/* If dynamic styles are on nested-styles they are unfortunately lost,
|
|
143
232
|
since inline style does not support that */
|
|
144
233
|
if (templateStyle === 'html') {
|
|
145
234
|
const inlineStyles = createDynamicInlineStyle(dynamicStyles)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
235
|
+
const bindingParts = hasDynamicBindings
|
|
236
|
+
? createDynamicBindingInlineStyle(dynamicStyleBindings)
|
|
237
|
+
: ''
|
|
238
|
+
const combined = bindingParts ? `${inlineStyles}, ${bindingParts}` : inlineStyles
|
|
239
|
+
HASTUtils.addAttributeToNode(root as HastNode, inlineStyleAttributeKey, `{${combined}}`)
|
|
151
240
|
} else {
|
|
152
|
-
|
|
241
|
+
jsxInlineStyles = UIDLUtils.transformDynamicStyles(dynamicStyles, (styleValue) =>
|
|
153
242
|
StyleBuilders.createDynamicStyleExpression(styleValue, propsPrefix)
|
|
154
243
|
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
244
|
+
}
|
|
245
|
+
} else if (hasDynamicBindings && templateStyle === 'html') {
|
|
246
|
+
const bindingParts = createDynamicBindingInlineStyle(dynamicStyleBindings)
|
|
247
|
+
HASTUtils.addAttributeToNode(root as HastNode, inlineStyleAttributeKey, `{${bindingParts}}`)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (hasDynamicBindings && templateStyle !== 'html') {
|
|
251
|
+
if (!jsxInlineStyles) {
|
|
252
|
+
jsxInlineStyles = {}
|
|
253
|
+
}
|
|
254
|
+
for (const [cssProperty, binding] of Object.entries(dynamicStyleBindings)) {
|
|
255
|
+
const camelCaseProperty = StringUtils.cssPropertyToInlineStyleKey(cssProperty)
|
|
256
|
+
jsxInlineStyles[camelCaseProperty] = StyleBuilders.createDynamicBindingExpression(binding)
|
|
160
257
|
}
|
|
161
258
|
}
|
|
162
259
|
|
|
260
|
+
if (jsxInlineStyles) {
|
|
261
|
+
ASTUtils.addAttributeToJSXTag(
|
|
262
|
+
root as types.JSXElement,
|
|
263
|
+
inlineStyleAttributeKey,
|
|
264
|
+
jsxInlineStyles
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
163
268
|
Object.values(referencedStyles).forEach((styleRef: UIDLElementNodeReferenceStyles) => {
|
|
164
269
|
switch (styleRef.content.mapType) {
|
|
165
270
|
case 'inlined': {
|
|
@@ -176,14 +281,16 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
176
281
|
if (!mediaStylesMap[String(maxWidth)]) {
|
|
177
282
|
mediaStylesMap[String(maxWidth)] = []
|
|
178
283
|
}
|
|
179
|
-
mediaStylesMap[String(maxWidth)].push({
|
|
284
|
+
mediaStylesMap[String(maxWidth)].push({
|
|
285
|
+
[className]: doubleElementClass ? { [own]: collectedStyles } : collectedStyles,
|
|
286
|
+
})
|
|
180
287
|
}
|
|
181
288
|
|
|
182
289
|
if (condition.conditionType === 'element-state') {
|
|
183
290
|
cssMap.push(
|
|
184
291
|
StyleBuilders.createCSSClassWithSelector(
|
|
185
292
|
className,
|
|
186
|
-
|
|
293
|
+
`${own}:${condition.content}`,
|
|
187
294
|
collectedStyles
|
|
188
295
|
)
|
|
189
296
|
)
|
|
@@ -206,24 +313,21 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
206
313
|
propDefinitions[styleRef.content.content.content.id]?.defaultValue
|
|
207
314
|
|
|
208
315
|
if (defaultPropValue) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
propDefinitions[styleRef.content.content.content.id].defaultValue = getClassName(
|
|
212
|
-
forceScoping,
|
|
213
|
-
uidl.name,
|
|
214
|
-
String(defaultPropValue)
|
|
215
|
-
)
|
|
316
|
+
propDefinitions[styleRef.content.content.content.id].defaultValue =
|
|
317
|
+
StringUtils.camelCaseToDashCase(String(defaultPropValue))
|
|
216
318
|
}
|
|
217
319
|
|
|
320
|
+
// staticPropReferences flag is only used for just html-code generation.
|
|
321
|
+
// This is used to append the class name to the node where the component is being called.
|
|
322
|
+
// Instead of how frameworks handle them using props at runtime. This makes the props
|
|
323
|
+
// to behave statically during the generation time instead by appending them directly.
|
|
218
324
|
if (staticPropReferences) {
|
|
219
|
-
if (
|
|
325
|
+
if (defaultPropValue === undefined || typeof defaultPropValue !== 'string') {
|
|
220
326
|
return
|
|
221
327
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
)
|
|
226
|
-
}
|
|
328
|
+
classNamesToAppend.add(
|
|
329
|
+
StringUtils.camelCaseToDashCase(uidl.name + defaultPropValue)
|
|
330
|
+
)
|
|
227
331
|
} else {
|
|
228
332
|
dynamicVariantsToAppend.add(styleRef.content.content.content.id)
|
|
229
333
|
}
|
|
@@ -239,7 +343,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
239
343
|
)
|
|
240
344
|
}
|
|
241
345
|
classNamesToAppend.add(
|
|
242
|
-
|
|
346
|
+
StringUtils.camelCaseToDashCase(uidl.name + styleRef.content.content.content.id)
|
|
243
347
|
)
|
|
244
348
|
}
|
|
245
349
|
|
|
@@ -250,12 +354,252 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
250
354
|
const { content } = styleRef
|
|
251
355
|
const referedStyle = styleSetDefinitions[content.referenceId]
|
|
252
356
|
if (!referedStyle) {
|
|
253
|
-
|
|
254
|
-
`Style used from global stylesheet is missing - ${content.referenceId}`
|
|
255
|
-
)
|
|
357
|
+
return
|
|
256
358
|
}
|
|
257
359
|
|
|
258
|
-
|
|
360
|
+
usedProjectStyleIds.add(content.referenceId)
|
|
361
|
+
|
|
362
|
+
if (styleRef.content.condition) {
|
|
363
|
+
if (templateStyle === 'html') {
|
|
364
|
+
const {
|
|
365
|
+
value: staticValue,
|
|
366
|
+
reference,
|
|
367
|
+
expression: { matchingCriteria },
|
|
368
|
+
} = styleRef.content.condition
|
|
369
|
+
// Class conditions are single-reference flat chains: no writer
|
|
370
|
+
// emits per-entry references or nested groups here.
|
|
371
|
+
const conditions = styleRef.content.condition.expression
|
|
372
|
+
.conditions as UIDLConditionExpressionEntry[]
|
|
373
|
+
|
|
374
|
+
const {
|
|
375
|
+
content: { referenceType, id, refPath = [] },
|
|
376
|
+
} = reference
|
|
377
|
+
|
|
378
|
+
switch (referenceType) {
|
|
379
|
+
case 'prop': {
|
|
380
|
+
const usedProp = propDefinitions[id]
|
|
381
|
+
if (usedProp === undefined || usedProp.defaultValue === undefined) {
|
|
382
|
+
throw new PluginCSS(`Prop with ${id} is missing in the propDefinitions.`)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let defaultValue = usedProp.defaultValue
|
|
386
|
+
let operandDefaultValue: StateDefaultValueTypes | PropDefaultValueTypes | string
|
|
387
|
+
for (const path of refPath) {
|
|
388
|
+
defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// If defaultValue is undefined or null after path traversal, use original default
|
|
392
|
+
defaultValue = defaultValue ?? usedProp.defaultValue
|
|
393
|
+
|
|
394
|
+
// our conditions can be of type 'expr' or they can be a dynamic reference
|
|
395
|
+
const rightSideCondition = conditions[0].operand
|
|
396
|
+
const rightSideConditionType = typeof rightSideCondition
|
|
397
|
+
if (rightSideConditionType === 'object') {
|
|
398
|
+
const type = (
|
|
399
|
+
rightSideCondition as UIDLDynamicReference | UIDLExpressionValue
|
|
400
|
+
).type
|
|
401
|
+
if (type === 'dynamic') {
|
|
402
|
+
const dynamicRef = rightSideCondition as UIDLDynamicReference
|
|
403
|
+
if (dynamicRef.content.referenceType === 'prop') {
|
|
404
|
+
const prop = propDefinitions[dynamicRef.content.id]
|
|
405
|
+
if (prop && prop.defaultValue) {
|
|
406
|
+
operandDefaultValue = prop.defaultValue
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (dynamicRef.content.referenceType === 'state') {
|
|
410
|
+
const state = stateDefinitions[dynamicRef.content.id]
|
|
411
|
+
if (state && state.defaultValue) {
|
|
412
|
+
defaultValue = state.defaultValue
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (type === 'expr') {
|
|
417
|
+
operandDefaultValue = (rightSideCondition as UIDLExpressionValue).content
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
operandDefaultValue = rightSideCondition
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Since we know the operand and the default value from the prop.
|
|
424
|
+
// We can try building the condition and check if the condition is true or false.
|
|
425
|
+
// @todo: You can only use a 'value' in UIDL or 'conditions' but not both.
|
|
426
|
+
// UIDL validations need to be improved on this aspect.
|
|
427
|
+
const resolvedConditions = [
|
|
428
|
+
{
|
|
429
|
+
operation: conditions[0].operation,
|
|
430
|
+
operand: operandDefaultValue as string,
|
|
431
|
+
},
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
const dynamicConditions = createConditionalStatement(
|
|
435
|
+
staticValue !== undefined
|
|
436
|
+
? [{ operand: staticValue, operation: '===' }]
|
|
437
|
+
: resolvedConditions,
|
|
438
|
+
defaultValue
|
|
439
|
+
)
|
|
440
|
+
const matchCondition =
|
|
441
|
+
matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
|
|
442
|
+
const conditionString = dynamicConditions.join(` ${matchCondition} `)
|
|
443
|
+
|
|
444
|
+
// tslint:disable-next-line function-constructor
|
|
445
|
+
const isConditionPassing = new Function(`return ${conditionString}`)()
|
|
446
|
+
if (isConditionPassing) {
|
|
447
|
+
classNamesToAppend.add(styleRef.content.referenceId)
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
return
|
|
451
|
+
}
|
|
452
|
+
case 'state': {
|
|
453
|
+
const usedState = stateDefinitions[id]
|
|
454
|
+
if (usedState === undefined || usedState.defaultValue === undefined) {
|
|
455
|
+
throw new PluginCSS(`State with ${id} is missing in the stateDefinitions.`)
|
|
456
|
+
}
|
|
457
|
+
let defaultValue = usedState.defaultValue
|
|
458
|
+
for (const path of refPath) {
|
|
459
|
+
defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
|
|
460
|
+
}
|
|
461
|
+
if (
|
|
462
|
+
typeof defaultValue === 'object' &&
|
|
463
|
+
defaultValue !== null &&
|
|
464
|
+
'type' in (defaultValue as Record<string, unknown>) &&
|
|
465
|
+
'content' in (defaultValue as Record<string, unknown>)
|
|
466
|
+
) {
|
|
467
|
+
const entry = defaultValue as { type: string; content: unknown }
|
|
468
|
+
if (entry.type === 'static') {
|
|
469
|
+
defaultValue = entry.content as typeof defaultValue
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
defaultValue = defaultValue ?? usedState.defaultValue
|
|
473
|
+
|
|
474
|
+
const dynamicConditions = createConditionalStatement(
|
|
475
|
+
staticValue !== undefined
|
|
476
|
+
? [{ operand: staticValue, operation: '===' }]
|
|
477
|
+
: conditions,
|
|
478
|
+
defaultValue
|
|
479
|
+
)
|
|
480
|
+
const matchCondition =
|
|
481
|
+
matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
|
|
482
|
+
const conditionString = dynamicConditions.join(` ${matchCondition} `)
|
|
483
|
+
// tslint:disable-next-line function-constructor
|
|
484
|
+
const isConditionPassing = new Function(`return ${conditionString}`)()
|
|
485
|
+
if (isConditionPassing) {
|
|
486
|
+
classNamesToAppend.add(styleRef.content.referenceId)
|
|
487
|
+
return
|
|
488
|
+
}
|
|
489
|
+
return
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
case 'globalState' as any: {
|
|
493
|
+
const gsDef = findGlobalStateDefinitionById(id, globalStateDefinitions)
|
|
494
|
+
if (!gsDef) {
|
|
495
|
+
classNamesToAppend.add(styleRef.content.referenceId)
|
|
496
|
+
return
|
|
497
|
+
}
|
|
498
|
+
let defaultValue: StateDefaultValueTypes | PropDefaultValueTypes =
|
|
499
|
+
gsDef.defaultValue as StateDefaultValueTypes
|
|
500
|
+
for (const path of refPath) {
|
|
501
|
+
defaultValue = (defaultValue as Record<string, unknown>)?.[
|
|
502
|
+
path
|
|
503
|
+
] as typeof defaultValue
|
|
504
|
+
}
|
|
505
|
+
defaultValue = defaultValue ?? (gsDef.defaultValue as StateDefaultValueTypes)
|
|
506
|
+
const dynamicConditions = createConditionalStatement(
|
|
507
|
+
staticValue !== undefined
|
|
508
|
+
? [{ operand: staticValue, operation: '===' }]
|
|
509
|
+
: conditions,
|
|
510
|
+
defaultValue
|
|
511
|
+
)
|
|
512
|
+
const matchCondition =
|
|
513
|
+
matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
|
|
514
|
+
const conditionString = dynamicConditions.join(` ${matchCondition} `)
|
|
515
|
+
// tslint:disable-next-line function-constructor
|
|
516
|
+
const isConditionPassing = new Function(`return ${conditionString}`)()
|
|
517
|
+
if (isConditionPassing) {
|
|
518
|
+
classNamesToAppend.add(styleRef.content.referenceId)
|
|
519
|
+
return
|
|
520
|
+
}
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
default: {
|
|
525
|
+
classNamesToAppend.add(styleRef.content.referenceId)
|
|
526
|
+
return
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
const referenceContent = styleRef.content.condition.reference.content
|
|
531
|
+
const referenceType = referenceContent.referenceType
|
|
532
|
+
const nameToAppend =
|
|
533
|
+
referenceType === 'local' && referenceContent.refPath?.length
|
|
534
|
+
? referenceContent.refPath.join('.')
|
|
535
|
+
: referenceContent.id
|
|
536
|
+
|
|
537
|
+
// Same flat-leaf contract as the html branch above.
|
|
538
|
+
const conditions = styleRef.content.condition.expression
|
|
539
|
+
.conditions as UIDLConditionExpressionEntry[]
|
|
540
|
+
|
|
541
|
+
const operator = conditions[0].operation as '===' | '!==' | '<' | '<=' | '>' | '>='
|
|
542
|
+
const right = conditions[0].operand as string | number | boolean
|
|
543
|
+
|
|
544
|
+
let binaryExpressionType = ''
|
|
545
|
+
switch (referenceType) {
|
|
546
|
+
case 'prop': {
|
|
547
|
+
binaryExpressionType = propDefinitions[nameToAppend].type
|
|
548
|
+
break
|
|
549
|
+
}
|
|
550
|
+
case 'state': {
|
|
551
|
+
binaryExpressionType = stateDefinitions[nameToAppend].type
|
|
552
|
+
break
|
|
553
|
+
}
|
|
554
|
+
case 'local': {
|
|
555
|
+
binaryExpressionType = typeof right as string
|
|
556
|
+
break
|
|
557
|
+
}
|
|
558
|
+
default: {
|
|
559
|
+
if ((referenceType as string) === 'globalState') {
|
|
560
|
+
const gsDef = findGlobalStateDefinitionById(
|
|
561
|
+
nameToAppend,
|
|
562
|
+
globalStateDefinitions
|
|
563
|
+
)
|
|
564
|
+
if (gsDef) {
|
|
565
|
+
binaryExpressionType = gsDef.type
|
|
566
|
+
}
|
|
567
|
+
break
|
|
568
|
+
}
|
|
569
|
+
throw new PluginCSS(
|
|
570
|
+
`Un-supported reference type ${referenceType} for ${nameToAppend}`
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
let binaryKey = nameToAppend
|
|
576
|
+
if (referenceType === 'prop') {
|
|
577
|
+
binaryKey = 'props?.' + nameToAppend
|
|
578
|
+
} else if ((referenceType as string) === 'globalState') {
|
|
579
|
+
const gsDef = findGlobalStateDefinitionById(nameToAppend, globalStateDefinitions)
|
|
580
|
+
binaryKey = gsDef ? gsDef.name : nameToAppend
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const binaryExpression = createBinaryExpression(
|
|
584
|
+
{ operation: operator, operand: right },
|
|
585
|
+
{
|
|
586
|
+
key: binaryKey,
|
|
587
|
+
type: binaryExpressionType,
|
|
588
|
+
}
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
const conditionalExpression = types.conditionalExpression(
|
|
592
|
+
binaryExpression,
|
|
593
|
+
types.stringLiteral(content.referenceId),
|
|
594
|
+
types.stringLiteral('')
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
dynamicVariantsToAppend.add(conditionalExpression)
|
|
598
|
+
return
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
classNamesToAppend.add(content.referenceId)
|
|
602
|
+
}
|
|
259
603
|
return
|
|
260
604
|
}
|
|
261
605
|
|
|
@@ -296,18 +640,35 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
296
640
|
? types.identifier(dynamicVariantPrefix)
|
|
297
641
|
: types.identifier(propsPrefix)
|
|
298
642
|
|
|
299
|
-
|
|
643
|
+
if (typeof variant === 'string') {
|
|
644
|
+
return types.memberExpression(dynamicAttrValueIdentifier, types.identifier(variant))
|
|
645
|
+
} else {
|
|
646
|
+
// variant is a ConditionalExpression, so return it directly
|
|
647
|
+
return variant
|
|
648
|
+
}
|
|
300
649
|
})
|
|
301
650
|
)
|
|
302
651
|
}
|
|
303
|
-
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
UIDLUtils.traverseElements(node, generateStylesForElementNode)
|
|
655
|
+
for (const prop of Object.values(propDefinitions)) {
|
|
656
|
+
if (prop.type === 'element' && prop.defaultValue) {
|
|
657
|
+
UIDLUtils.traverseElements(
|
|
658
|
+
prop.defaultValue as UIDLElementNode,
|
|
659
|
+
generateStylesForElementNode
|
|
660
|
+
)
|
|
661
|
+
}
|
|
662
|
+
}
|
|
304
663
|
|
|
305
664
|
if (Object.keys(componentStyleSet).length > 0) {
|
|
306
665
|
StyleBuilders.generateStylesFromStyleSetDefinitions(
|
|
307
666
|
componentStyleSet,
|
|
308
667
|
cssMap,
|
|
309
668
|
mediaStylesMap,
|
|
310
|
-
(styleName: string) =>
|
|
669
|
+
(styleName: string) => {
|
|
670
|
+
return StringUtils.camelCaseToDashCase(uidl.name + styleName)
|
|
671
|
+
}
|
|
311
672
|
)
|
|
312
673
|
}
|
|
313
674
|
|
|
@@ -315,7 +676,210 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
315
676
|
cssMap.push(...StyleBuilders.generateMediaStyle(mediaStylesMap))
|
|
316
677
|
}
|
|
317
678
|
|
|
318
|
-
|
|
679
|
+
// Handle inline style tag for HTML templates (self-contained fragments)
|
|
680
|
+
if (standaloneHtmlComponents && templateStyle === 'html' && templateChunk) {
|
|
681
|
+
const inlineCssMap: string[] = []
|
|
682
|
+
const hasTokens = Object.keys(tokens).length > 0
|
|
683
|
+
const hasUsedProjectStyles = usedProjectStyleIds.size > 0
|
|
684
|
+
const globalAssets = options.globalAssets || []
|
|
685
|
+
|
|
686
|
+
// Get the scoped root class from the template chunk meta
|
|
687
|
+
const scopedRootClass = templateChunk.meta?.scopedRootClass as string | undefined
|
|
688
|
+
|
|
689
|
+
// Utility function to scope CSS selectors
|
|
690
|
+
const scopeCssSelectors = (css: string, rootSelector: string): string => {
|
|
691
|
+
// Replace :root with scoped root class
|
|
692
|
+
let scopedCss = css.replace(/:root\s*\{/g, `${rootSelector} {`)
|
|
693
|
+
|
|
694
|
+
// Scope regular selectors (but not inside @media or @keyframes)
|
|
695
|
+
// This regex matches CSS rules outside of @ blocks
|
|
696
|
+
scopedCss = scopedCss.replace(/(?:^|\})\s*([^@{}]+?)\s*\{/gm, (match, selectors) => {
|
|
697
|
+
// Skip if it's just closing brace or empty
|
|
698
|
+
if (!selectors || selectors.trim() === '') {
|
|
699
|
+
return match
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Skip if selector is already the root class
|
|
703
|
+
if (selectors.trim() === rootSelector) {
|
|
704
|
+
return match
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Handle multiple selectors (comma-separated)
|
|
708
|
+
const scopedSelectors = selectors
|
|
709
|
+
.split(',')
|
|
710
|
+
.map((sel: string) => {
|
|
711
|
+
const trimmedSel = sel.trim()
|
|
712
|
+
// Skip empty selectors
|
|
713
|
+
if (!trimmedSel) {
|
|
714
|
+
return ''
|
|
715
|
+
}
|
|
716
|
+
// Skip if already scoped
|
|
717
|
+
if (trimmedSel.startsWith(rootSelector)) {
|
|
718
|
+
return trimmedSel
|
|
719
|
+
}
|
|
720
|
+
// Replace html/body with root selector
|
|
721
|
+
if (trimmedSel === 'html' || trimmedSel === 'body') {
|
|
722
|
+
return rootSelector
|
|
723
|
+
}
|
|
724
|
+
// For universal selector, scope it
|
|
725
|
+
if (trimmedSel === '*') {
|
|
726
|
+
return `${rootSelector} *`
|
|
727
|
+
}
|
|
728
|
+
// Prefix other selectors with root
|
|
729
|
+
return `${rootSelector} ${trimmedSel}`
|
|
730
|
+
})
|
|
731
|
+
.filter(Boolean)
|
|
732
|
+
.join(', ')
|
|
733
|
+
|
|
734
|
+
return match.replace(selectors, scopedSelectors)
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
return scopedCss
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// 0. Add reset and default styles from global assets (scoped)
|
|
741
|
+
const inlineStyleAssets = globalAssets.filter(
|
|
742
|
+
(asset): asset is UIDLStyleInlineAsset => asset.type === 'style' && 'content' in asset
|
|
743
|
+
)
|
|
744
|
+
for (const styleAsset of inlineStyleAssets) {
|
|
745
|
+
if (styleAsset.content) {
|
|
746
|
+
const css = scopedRootClass
|
|
747
|
+
? scopeCssSelectors(styleAsset.content, `.${scopedRootClass}`)
|
|
748
|
+
: styleAsset.content
|
|
749
|
+
inlineCssMap.push(css)
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// 1. Add CSS variables (tokens) - scoped to root class instead of :root
|
|
754
|
+
if (hasTokens) {
|
|
755
|
+
const tokenSelector = scopedRootClass ? `.${scopedRootClass}` : ':root'
|
|
756
|
+
inlineCssMap.push(
|
|
757
|
+
StyleBuilders.createCSSClassWithSelector(
|
|
758
|
+
'@global',
|
|
759
|
+
tokenSelector,
|
|
760
|
+
StyleUtils.getTokensContentFromTokensObject(tokens)
|
|
761
|
+
)
|
|
762
|
+
)
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// 2. Add project-referenced styles that this component uses (scoped)
|
|
766
|
+
if (hasUsedProjectStyles) {
|
|
767
|
+
const usedProjectStyles = Array.from(usedProjectStyleIds)
|
|
768
|
+
// Include exact matches and compound selectors that reference the same base class
|
|
769
|
+
const filteredDefinitions = Object.fromEntries(
|
|
770
|
+
Object.entries(styleSetDefinitions).filter(([key, definition]) => {
|
|
771
|
+
// Include if exact match
|
|
772
|
+
if (usedProjectStyles.includes(key)) {
|
|
773
|
+
return true
|
|
774
|
+
}
|
|
775
|
+
// Include compound selectors where className matches a used project style
|
|
776
|
+
// e.g., "navigation-mobile-overlay.navigation-mobile-overlay-active" has className "navigation-mobile-overlay"
|
|
777
|
+
const className = (definition as { className?: string }).className
|
|
778
|
+
if (className && usedProjectStyles.includes(className)) {
|
|
779
|
+
return true
|
|
780
|
+
}
|
|
781
|
+
// Include compound selectors where the base class (before the dot or space) is a used style
|
|
782
|
+
// e.g., "dashboard-sidebar.collapsed" should be included if "dashboard-sidebar" is used
|
|
783
|
+
// e.g., "dashboard-sidebar.collapsed .sidebar-brand-text" should also be included
|
|
784
|
+
const dotIdx = key.indexOf('.')
|
|
785
|
+
const spaceIdx = key.indexOf(' ')
|
|
786
|
+
if (dotIdx > 0) {
|
|
787
|
+
const baseClass = key.substring(0, dotIdx)
|
|
788
|
+
if (usedProjectStyles.includes(baseClass)) {
|
|
789
|
+
return true
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (spaceIdx > 0 && dotIdx < 0) {
|
|
793
|
+
const baseClass = key.substring(0, spaceIdx)
|
|
794
|
+
if (usedProjectStyles.includes(baseClass)) {
|
|
795
|
+
return true
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return false
|
|
799
|
+
})
|
|
800
|
+
)
|
|
801
|
+
const projectStylesCssMap: string[] = []
|
|
802
|
+
const projectMediaStylesMap: Record<
|
|
803
|
+
string,
|
|
804
|
+
Array<{ [x: string]: Record<string, string | number> }>
|
|
805
|
+
> = {}
|
|
806
|
+
StyleBuilders.generateStylesFromStyleSetDefinitions(
|
|
807
|
+
filteredDefinitions,
|
|
808
|
+
projectStylesCssMap,
|
|
809
|
+
projectMediaStylesMap,
|
|
810
|
+
(styleName) => styleName
|
|
811
|
+
)
|
|
812
|
+
// Scope the project styles if scoped root class is available
|
|
813
|
+
if (scopedRootClass) {
|
|
814
|
+
const scopedProjectStyles = projectStylesCssMap.map((css) =>
|
|
815
|
+
scopeCssSelectors(css, `.${scopedRootClass}`)
|
|
816
|
+
)
|
|
817
|
+
inlineCssMap.push(...scopedProjectStyles)
|
|
818
|
+
} else {
|
|
819
|
+
inlineCssMap.push(...projectStylesCssMap)
|
|
820
|
+
}
|
|
821
|
+
if (Object.keys(projectMediaStylesMap).length > 0) {
|
|
822
|
+
const mediaStyles = StyleBuilders.generateMediaStyle(projectMediaStylesMap)
|
|
823
|
+
if (scopedRootClass) {
|
|
824
|
+
const scopedMediaStyles = mediaStyles.map((css) =>
|
|
825
|
+
scopeCssSelectors(css, `.${scopedRootClass}`)
|
|
826
|
+
)
|
|
827
|
+
inlineCssMap.push(...scopedMediaStyles)
|
|
828
|
+
} else {
|
|
829
|
+
inlineCssMap.push(...mediaStyles)
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// 3. Add component-specific styles (scoped)
|
|
835
|
+
if (scopedRootClass) {
|
|
836
|
+
// Scope the component CSS
|
|
837
|
+
const scopedCssMap = cssMap.map((css) => scopeCssSelectors(css, `.${scopedRootClass}`))
|
|
838
|
+
inlineCssMap.push(...scopedCssMap)
|
|
839
|
+
} else {
|
|
840
|
+
inlineCssMap.push(...cssMap)
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// 4. Create font link tags from global assets
|
|
844
|
+
const fontAssets = globalAssets.filter(
|
|
845
|
+
(asset): asset is UIDLFontAsset => asset.type === 'font' && 'path' in asset
|
|
846
|
+
)
|
|
847
|
+
const fontLinkNodes: HastNode[] = []
|
|
848
|
+
for (const fontAsset of fontAssets) {
|
|
849
|
+
const linkNode = HASTBuilders.createHTMLNode('link')
|
|
850
|
+
HASTUtils.addAttributeToNode(linkNode, 'rel', 'stylesheet')
|
|
851
|
+
HASTUtils.addAttributeToNode(linkNode, 'href', fontAsset.path)
|
|
852
|
+
if (fontAsset.attrs) {
|
|
853
|
+
for (const [attrKey, attrValue] of Object.entries(fontAsset.attrs)) {
|
|
854
|
+
const value =
|
|
855
|
+
typeof attrValue === 'object' && 'content' in attrValue
|
|
856
|
+
? String(attrValue.content)
|
|
857
|
+
: String(attrValue)
|
|
858
|
+
HASTUtils.addAttributeToNode(linkNode, attrKey, value)
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
fontLinkNodes.push(linkNode)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// 5. Create <style> node and prepend to HTML (fonts first, then styles)
|
|
865
|
+
if (inlineCssMap.length > 0 || fontLinkNodes.length > 0) {
|
|
866
|
+
const nodesToPrepend: HastNode[] = []
|
|
867
|
+
|
|
868
|
+
// Add font links first
|
|
869
|
+
nodesToPrepend.push(...fontLinkNodes)
|
|
870
|
+
|
|
871
|
+
// Add style tag with all CSS
|
|
872
|
+
if (inlineCssMap.length > 0) {
|
|
873
|
+
const styleNode = HASTBuilders.createHTMLNode('style')
|
|
874
|
+
const cssContent = HASTBuilders.createTextNode(inlineCssMap.join('\n\n'))
|
|
875
|
+
styleNode.children.push(cssContent)
|
|
876
|
+
nodesToPrepend.push(styleNode)
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
;(templateChunk.content as HastNode).children.unshift(...nodesToPrepend)
|
|
880
|
+
}
|
|
881
|
+
} else if (cssMap.length > 0) {
|
|
882
|
+
// Original behavior: create separate CSS file
|
|
319
883
|
/**
|
|
320
884
|
* Setup an import statement for the styles
|
|
321
885
|
* The name of the file is either in the meta of the component generator
|
|
@@ -344,11 +908,17 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
|
|
|
344
908
|
}
|
|
345
909
|
}
|
|
346
910
|
|
|
911
|
+
const folderPath = uidl.outputOptions?.folderPath
|
|
912
|
+
const cssContent =
|
|
913
|
+
folderPath?.length > 0
|
|
914
|
+
? cssMap.map((css) => prefixUrlPathsInCss(css, folderPath)).join('\n \n')
|
|
915
|
+
: cssMap.join('\n \n')
|
|
916
|
+
|
|
347
917
|
chunks.push({
|
|
348
918
|
type: ChunkType.STRING,
|
|
349
919
|
name: chunkName,
|
|
350
920
|
fileType: FileType.CSS,
|
|
351
|
-
content:
|
|
921
|
+
content: cssContent,
|
|
352
922
|
linkAfter: [],
|
|
353
923
|
})
|
|
354
924
|
}
|
|
@@ -363,6 +933,18 @@ export { createStyleSheetPlugin, createCSSPlugin }
|
|
|
363
933
|
|
|
364
934
|
export default createCSSPlugin()
|
|
365
935
|
|
|
936
|
+
const findGlobalStateDefinitionById = (
|
|
937
|
+
id: string,
|
|
938
|
+
definitions: Record<string, UIDLGlobalStateDefinition>
|
|
939
|
+
): UIDLGlobalStateDefinition | undefined => {
|
|
940
|
+
for (const def of Object.values(definitions)) {
|
|
941
|
+
if (def.id === id) {
|
|
942
|
+
return def
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return undefined
|
|
946
|
+
}
|
|
947
|
+
|
|
366
948
|
const createDynamicInlineStyle = (styles: UIDLStyleDefinitions) => {
|
|
367
949
|
return Object.keys(styles)
|
|
368
950
|
.map((styleKey) => {
|
|
@@ -371,8 +953,25 @@ const createDynamicInlineStyle = (styles: UIDLStyleDefinitions) => {
|
|
|
371
953
|
.join(', ')
|
|
372
954
|
}
|
|
373
955
|
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
956
|
+
const createDynamicBindingInlineStyle = (
|
|
957
|
+
bindings: Record<
|
|
958
|
+
string,
|
|
959
|
+
{
|
|
960
|
+
referenceType: string
|
|
961
|
+
stateKey: string
|
|
962
|
+
defaultValue: string
|
|
963
|
+
contextName?: string
|
|
964
|
+
stateDefinitionId?: string
|
|
965
|
+
}
|
|
966
|
+
>
|
|
967
|
+
): string => {
|
|
968
|
+
return Object.entries(bindings)
|
|
969
|
+
.map(([prop, binding]) => {
|
|
970
|
+
const value =
|
|
971
|
+
binding.referenceType === 'ctx' && binding.contextName
|
|
972
|
+
? `${binding.contextName}.${binding.stateKey}`
|
|
973
|
+
: binding.stateKey
|
|
974
|
+
return `${prop}: ${value} != null ? ${value} : '${binding.defaultValue || ''}'`
|
|
975
|
+
})
|
|
976
|
+
.join(', ')
|
|
378
977
|
}
|