@teleporthq/teleport-plugin-css 0.42.35 → 0.43.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/src/index.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  PropDefaultValueTypes,
28
28
  UIDLStyleInlineAsset,
29
29
  UIDLFontAsset,
30
+ UIDLGlobalStateDefinition,
30
31
  } from '@teleporthq/teleport-types'
31
32
  import { createStyleSheetPlugin } from './style-sheet'
32
33
  import { createConditionalStatement } from './utils'
@@ -85,7 +86,12 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
85
86
  propDefinitions = {},
86
87
  stateDefinitions = {},
87
88
  } = uidl
88
- const { projectStyleSet, designLanguage: { tokens = {} } = {}, isRootComponent } = options || {}
89
+ const {
90
+ projectStyleSet,
91
+ designLanguage: { tokens = {} } = {},
92
+ isRootComponent,
93
+ globalStateDefinitions = {},
94
+ } = options || {}
89
95
  const {
90
96
  styleSetDefinitions = {},
91
97
  fileName: projectStyleSheetName,
@@ -138,6 +144,9 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
138
144
  elementType,
139
145
  dependency,
140
146
  } = element
147
+ const { dynamicStyleBindings } = element
148
+ const hasDynamicBindings =
149
+ dynamicStyleBindings && Object.keys(dynamicStyleBindings).length > 0
141
150
 
142
151
  const root = jsxNodesLookup[key]
143
152
  if (!root) {
@@ -160,7 +169,8 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
160
169
  if (
161
170
  Object.keys(style).length === 0 &&
162
171
  Object.keys(referencedStyles).length === 0 &&
163
- Object.keys(componentStyleSet).length === 0
172
+ Object.keys(componentStyleSet).length === 0 &&
173
+ !hasDynamicBindings
164
174
  ) {
165
175
  return
166
176
  }
@@ -180,28 +190,58 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
180
190
  classNamesToAppend.add(className)
181
191
  }
182
192
 
193
+ let jsxInlineStyles: Record<string, unknown> | null = null
194
+
183
195
  if (Object.keys(dynamicStyles).length > 0) {
196
+ for (const styleValue of Object.values(dynamicStyles)) {
197
+ if (
198
+ styleValue.type === 'dynamic' &&
199
+ styleValue.content.referenceType === 'global' &&
200
+ templateChunk.meta.globalReferences
201
+ ) {
202
+ ;(templateChunk.meta.globalReferences as string[]).push(styleValue.content.id)
203
+ }
204
+ }
205
+
184
206
  /* If dynamic styles are on nested-styles they are unfortunately lost,
185
207
  since inline style does not support that */
186
208
  if (templateStyle === 'html') {
187
209
  const inlineStyles = createDynamicInlineStyle(dynamicStyles)
188
- HASTUtils.addAttributeToNode(
189
- root as HastNode,
190
- inlineStyleAttributeKey,
191
- `{${inlineStyles}}`
192
- )
210
+ const bindingParts = hasDynamicBindings
211
+ ? createDynamicBindingInlineStyle(dynamicStyleBindings)
212
+ : ''
213
+ const combined = bindingParts ? `${inlineStyles}, ${bindingParts}` : inlineStyles
214
+ HASTUtils.addAttributeToNode(root as HastNode, inlineStyleAttributeKey, `{${combined}}`)
193
215
  } else {
194
- const inlineStyles = UIDLUtils.transformDynamicStyles(dynamicStyles, (styleValue) =>
216
+ jsxInlineStyles = UIDLUtils.transformDynamicStyles(dynamicStyles, (styleValue) =>
195
217
  StyleBuilders.createDynamicStyleExpression(styleValue, propsPrefix)
196
218
  )
197
- ASTUtils.addAttributeToJSXTag(
198
- root as types.JSXElement,
199
- inlineStyleAttributeKey,
200
- inlineStyles
219
+ }
220
+ } else if (hasDynamicBindings && templateStyle === 'html') {
221
+ const bindingParts = createDynamicBindingInlineStyle(dynamicStyleBindings)
222
+ HASTUtils.addAttributeToNode(root as HastNode, inlineStyleAttributeKey, `{${bindingParts}}`)
223
+ }
224
+
225
+ if (hasDynamicBindings && templateStyle !== 'html') {
226
+ if (!jsxInlineStyles) {
227
+ jsxInlineStyles = {}
228
+ }
229
+ for (const [cssProperty, binding] of Object.entries(dynamicStyleBindings)) {
230
+ const camelCaseProperty = cssProperty.replace(/-([a-z])/g, (_, letter: string) =>
231
+ letter.toUpperCase()
201
232
  )
233
+ jsxInlineStyles[camelCaseProperty] = StyleBuilders.createDynamicBindingExpression(binding)
202
234
  }
203
235
  }
204
236
 
237
+ if (jsxInlineStyles) {
238
+ ASTUtils.addAttributeToJSXTag(
239
+ root as types.JSXElement,
240
+ inlineStyleAttributeKey,
241
+ jsxInlineStyles
242
+ )
243
+ }
244
+
205
245
  Object.values(referencedStyles).forEach((styleRef: UIDLElementNodeReferenceStyles) => {
206
246
  switch (styleRef.content.mapType) {
207
247
  case 'inlined': {
@@ -289,9 +329,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
289
329
  const { content } = styleRef
290
330
  const referedStyle = styleSetDefinitions[content.referenceId]
291
331
  if (!referedStyle) {
292
- throw new PluginCSS(
293
- `Style used from global stylesheet is missing - ${content.referenceId}`
294
- )
332
+ return
295
333
  }
296
334
 
297
335
  usedProjectStyleIds.add(content.referenceId)
@@ -391,10 +429,18 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
391
429
  for (const path of refPath) {
392
430
  defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
393
431
  }
394
- // If defaultValue is undefined or null after path traversal, use original default
432
+ if (
433
+ typeof defaultValue === 'object' &&
434
+ defaultValue !== null &&
435
+ 'type' in (defaultValue as Record<string, unknown>) &&
436
+ 'content' in (defaultValue as Record<string, unknown>)
437
+ ) {
438
+ const entry = defaultValue as { type: string; content: unknown }
439
+ if (entry.type === 'static') {
440
+ defaultValue = entry.content as typeof defaultValue
441
+ }
442
+ }
395
443
  defaultValue = defaultValue ?? usedState.defaultValue
396
- // Since we know the operand and the default value from the state.
397
- // We can try building the condition and check if the condition is true or false.
398
444
 
399
445
  const dynamicConditions = createConditionalStatement(
400
446
  staticValue !== undefined
@@ -414,6 +460,38 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
414
460
  return
415
461
  }
416
462
 
463
+ case 'globalState' as any: {
464
+ const gsDef = findGlobalStateDefinitionById(id, globalStateDefinitions)
465
+ if (!gsDef) {
466
+ classNamesToAppend.add(styleRef.content.referenceId)
467
+ return
468
+ }
469
+ let defaultValue: StateDefaultValueTypes | PropDefaultValueTypes =
470
+ gsDef.defaultValue as StateDefaultValueTypes
471
+ for (const path of refPath) {
472
+ defaultValue = (defaultValue as Record<string, unknown>)?.[
473
+ path
474
+ ] as typeof defaultValue
475
+ }
476
+ defaultValue = defaultValue ?? (gsDef.defaultValue as StateDefaultValueTypes)
477
+ const dynamicConditions = createConditionalStatement(
478
+ staticValue !== undefined
479
+ ? [{ operand: staticValue, operation: '===' }]
480
+ : conditions,
481
+ defaultValue
482
+ )
483
+ const matchCondition =
484
+ matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
485
+ const conditionString = dynamicConditions.join(` ${matchCondition} `)
486
+ // tslint:disable-next-line function-constructor
487
+ const isConditionPassing = new Function(`return ${conditionString}`)()
488
+ if (isConditionPassing) {
489
+ classNamesToAppend.add(styleRef.content.referenceId)
490
+ return
491
+ }
492
+ return
493
+ }
494
+
417
495
  default: {
418
496
  classNamesToAppend.add(styleRef.content.referenceId)
419
497
  return
@@ -447,16 +525,34 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
447
525
  break
448
526
  }
449
527
  default: {
528
+ if ((referenceType as string) === 'globalState') {
529
+ const gsDef = findGlobalStateDefinitionById(
530
+ nameToAppend,
531
+ globalStateDefinitions
532
+ )
533
+ if (gsDef) {
534
+ binaryExpressionType = gsDef.type
535
+ }
536
+ break
537
+ }
450
538
  throw new PluginCSS(
451
539
  `Un-supported reference type ${referenceType} for ${nameToAppend}`
452
540
  )
453
541
  }
454
542
  }
455
543
 
544
+ let binaryKey = nameToAppend
545
+ if (referenceType === 'prop') {
546
+ binaryKey = 'props?.' + nameToAppend
547
+ } else if ((referenceType as string) === 'globalState') {
548
+ const gsDef = findGlobalStateDefinitionById(nameToAppend, globalStateDefinitions)
549
+ binaryKey = gsDef ? gsDef.name : nameToAppend
550
+ }
551
+
456
552
  const binaryExpression = createBinaryExpression(
457
553
  { operation: operator, operand: right },
458
554
  {
459
- key: (referenceType === 'prop' ? 'props?.' : '') + nameToAppend,
555
+ key: binaryKey,
460
556
  type: binaryExpressionType,
461
557
  }
462
558
  )
@@ -651,6 +747,23 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
651
747
  if (className && usedProjectStyles.includes(className)) {
652
748
  return true
653
749
  }
750
+ // Include compound selectors where the base class (before the dot or space) is a used style
751
+ // e.g., "dashboard-sidebar.collapsed" should be included if "dashboard-sidebar" is used
752
+ // e.g., "dashboard-sidebar.collapsed .sidebar-brand-text" should also be included
753
+ const dotIdx = key.indexOf('.')
754
+ const spaceIdx = key.indexOf(' ')
755
+ if (dotIdx > 0) {
756
+ const baseClass = key.substring(0, dotIdx)
757
+ if (usedProjectStyles.includes(baseClass)) {
758
+ return true
759
+ }
760
+ }
761
+ if (spaceIdx > 0 && dotIdx < 0) {
762
+ const baseClass = key.substring(0, spaceIdx)
763
+ if (usedProjectStyles.includes(baseClass)) {
764
+ return true
765
+ }
766
+ }
654
767
  return false
655
768
  })
656
769
  )
@@ -789,6 +902,18 @@ export { createStyleSheetPlugin, createCSSPlugin }
789
902
 
790
903
  export default createCSSPlugin()
791
904
 
905
+ const findGlobalStateDefinitionById = (
906
+ id: string,
907
+ definitions: Record<string, UIDLGlobalStateDefinition>
908
+ ): UIDLGlobalStateDefinition | undefined => {
909
+ for (const def of Object.values(definitions)) {
910
+ if (def.id === id) {
911
+ return def
912
+ }
913
+ }
914
+ return undefined
915
+ }
916
+
792
917
  const createDynamicInlineStyle = (styles: UIDLStyleDefinitions) => {
793
918
  return Object.keys(styles)
794
919
  .map((styleKey) => {
@@ -796,3 +921,26 @@ const createDynamicInlineStyle = (styles: UIDLStyleDefinitions) => {
796
921
  })
797
922
  .join(', ')
798
923
  }
924
+
925
+ const createDynamicBindingInlineStyle = (
926
+ bindings: Record<
927
+ string,
928
+ {
929
+ referenceType: string
930
+ stateKey: string
931
+ defaultValue: string
932
+ contextName?: string
933
+ stateDefinitionId?: string
934
+ }
935
+ >
936
+ ): string => {
937
+ return Object.entries(bindings)
938
+ .map(([prop, binding]) => {
939
+ const value =
940
+ binding.referenceType === 'ctx' && binding.contextName
941
+ ? `${binding.contextName}.${binding.stateKey}`
942
+ : binding.stateKey
943
+ return `${prop}: ${value} != null ? ${value} : '${binding.defaultValue || ''}'`
944
+ })
945
+ .join(', ')
946
+ }