@teleporthq/teleport-plugin-css 0.40.0 → 0.40.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/src/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared'
2
- import { StyleUtils, StyleBuilders, HASTUtils, ASTUtils } from '@teleporthq/teleport-plugin-common'
2
+ import {
3
+ StyleUtils,
4
+ StyleBuilders,
5
+ HASTUtils,
6
+ ASTUtils,
7
+ createBinaryExpression,
8
+ } from '@teleporthq/teleport-plugin-common'
3
9
  import * as types from '@babel/types'
4
10
  import {
5
11
  ComponentPluginFactory,
@@ -14,6 +20,9 @@ import {
14
20
  PluginCSS,
15
21
  UIDLElement,
16
22
  UIDLElementNode,
23
+ UIDLExpressionValue,
24
+ StateDefaultValueTypes,
25
+ PropDefaultValueTypes,
17
26
  } from '@teleporthq/teleport-types'
18
27
  import { createStyleSheetPlugin } from './style-sheet'
19
28
  import { createConditionalStatement } from './utils'
@@ -45,7 +54,12 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
45
54
 
46
55
  const cssPlugin: ComponentPlugin = async (structure) => {
47
56
  const { uidl, chunks, dependencies, options } = structure
48
- const { node, styleSetDefinitions: componentStyleSet = {}, propDefinitions = {} } = uidl
57
+ const {
58
+ node,
59
+ styleSetDefinitions: componentStyleSet = {},
60
+ propDefinitions = {},
61
+ stateDefinitions = {},
62
+ } = uidl
49
63
  const { projectStyleSet, designLanguage: { tokens = {} } = {}, isRootComponent } = options || {}
50
64
  const {
51
65
  styleSetDefinitions = {},
@@ -89,7 +103,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
89
103
 
90
104
  const generateStylesForElementNode = (element: UIDLElement) => {
91
105
  const classNamesToAppend: Set<string> = new Set()
92
- const dynamicVariantsToAppend: Set<string> = new Set()
106
+ const dynamicVariantsToAppend: Set<string | types.ConditionalExpression> = new Set()
93
107
  const {
94
108
  style = {},
95
109
  key,
@@ -255,59 +269,170 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
255
269
  }
256
270
 
257
271
  if (styleRef.content.condition) {
258
- const {
259
- value: staticValue,
260
- reference,
261
- expression: { conditions, matchingCriteria },
262
- } = styleRef.content.condition
263
-
264
- const {
265
- content: { referenceType, id, refPath = [] },
266
- } = reference
267
-
268
- switch (referenceType) {
269
- case 'prop': {
270
- const usedProp = propDefinitions[id]
271
- if (usedProp === undefined || usedProp.defaultValue === undefined) {
272
- throw new PluginCSS(`Prop with ${id} is missing in the propDefinitions.`)
272
+ if (templateStyle === 'html') {
273
+ const {
274
+ value: staticValue,
275
+ reference,
276
+ expression: { conditions, matchingCriteria },
277
+ } = styleRef.content.condition
278
+
279
+ const {
280
+ content: { referenceType, id, refPath = [] },
281
+ } = reference
282
+
283
+ switch (referenceType) {
284
+ case 'prop': {
285
+ const usedProp = propDefinitions[id]
286
+ if (usedProp === undefined || usedProp.defaultValue === undefined) {
287
+ throw new PluginCSS(`Prop with ${id} is missing in the propDefinitions.`)
288
+ }
289
+
290
+ let defaultValue = usedProp.defaultValue
291
+ let operandDefaultValue: StateDefaultValueTypes | PropDefaultValueTypes | string
292
+ for (const path of refPath) {
293
+ defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
294
+ }
295
+
296
+ // If defaultValue is undefined or null after path traversal, use original default
297
+ defaultValue = defaultValue ?? usedProp.defaultValue
298
+
299
+ // our conditions can be of type 'expr' or they can be a dynamic reference
300
+ const rightSideCondition = conditions[0].operand
301
+ const rightSideConditionType = typeof rightSideCondition
302
+ if (rightSideConditionType === 'object') {
303
+ const type = (
304
+ rightSideCondition as UIDLDynamicReference | UIDLExpressionValue
305
+ ).type
306
+ if (type === 'dynamic') {
307
+ const dynamicRef = rightSideCondition as UIDLDynamicReference
308
+ if (dynamicRef.content.referenceType === 'prop') {
309
+ const prop = propDefinitions[dynamicRef.content.id]
310
+ if (prop && prop.defaultValue) {
311
+ operandDefaultValue = prop.defaultValue
312
+ }
313
+ }
314
+ if (dynamicRef.content.referenceType === 'state') {
315
+ const state = stateDefinitions[dynamicRef.content.id]
316
+ if (state && state.defaultValue) {
317
+ defaultValue = state.defaultValue
318
+ }
319
+ }
320
+ }
321
+ if (type === 'expr') {
322
+ operandDefaultValue = (rightSideCondition as UIDLExpressionValue).content
323
+ }
324
+ } else {
325
+ operandDefaultValue = rightSideCondition
326
+ }
327
+
328
+ // Since we know the operand and the default value from the prop.
329
+ // We can try building the condition and check if the condition is true or false.
330
+ // @todo: You can only use a 'value' in UIDL or 'conditions' but not both.
331
+ // UIDL validations need to be improved on this aspect.
332
+ const resolvedConditions = [
333
+ {
334
+ operation: conditions[0].operation,
335
+ operand: operandDefaultValue as string,
336
+ },
337
+ ]
338
+
339
+ const dynamicConditions = createConditionalStatement(
340
+ staticValue !== undefined
341
+ ? [{ operand: staticValue, operation: '===' }]
342
+ : resolvedConditions,
343
+ defaultValue
344
+ )
345
+ const matchCondition =
346
+ matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
347
+ const conditionString = dynamicConditions.join(` ${matchCondition} `)
348
+
349
+ // tslint:disable-next-line function-constructor
350
+ const isConditionPassing = new Function(`return ${conditionString}`)()
351
+ if (isConditionPassing) {
352
+ classNamesToAppend.add(styleRef.content.referenceId)
353
+ return
354
+ }
355
+ return
273
356
  }
274
-
275
- let defaultValue = usedProp.defaultValue
276
- for (const path of refPath) {
277
- defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
357
+ case 'state': {
358
+ const usedState = stateDefinitions[id]
359
+ if (usedState === undefined || usedState.defaultValue === undefined) {
360
+ throw new PluginCSS(`State with ${id} is missing in the stateDefinitions.`)
361
+ }
362
+ let defaultValue = usedState.defaultValue
363
+ for (const path of refPath) {
364
+ defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
365
+ }
366
+ // If defaultValue is undefined or null after path traversal, use original default
367
+ defaultValue = defaultValue ?? usedState.defaultValue
368
+ // Since we know the operand and the default value from the state.
369
+ // We can try building the condition and check if the condition is true or false.
370
+
371
+ const dynamicConditions = createConditionalStatement(
372
+ staticValue !== undefined
373
+ ? [{ operand: staticValue, operation: '===' }]
374
+ : conditions,
375
+ defaultValue
376
+ )
377
+ const matchCondition =
378
+ matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
379
+ const conditionString = dynamicConditions.join(` ${matchCondition} `)
380
+ // tslint:disable-next-line function-constructor
381
+ const isConditionPassing = new Function(`return ${conditionString}`)()
382
+ if (isConditionPassing) {
383
+ classNamesToAppend.add(styleRef.content.referenceId)
384
+ return
385
+ }
386
+ return
278
387
  }
279
388
 
280
- // If defaultValue is undefined or null after path traversal, use original default
281
- defaultValue = defaultValue ?? usedProp.defaultValue
282
-
283
- // Since we know the operand and the default value from the prop.
284
- // We can try building the condition and check if the condition is true or false.
285
- // @todo: You can only use a 'value' in UIDL or 'conditions' but not both.
286
- // UIDL validations need to be improved on this aspect.
287
- const dynamicConditions = createConditionalStatement(
288
- staticValue !== undefined
289
- ? [{ operand: staticValue, operation: '===' }]
290
- : conditions,
291
- defaultValue
292
- )
293
- const matchCondition =
294
- matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
295
- const conditionString = dynamicConditions.join(` ${matchCondition} `)
296
-
297
- // tslint:disable-next-line function-constructor
298
- const isConditionPassing = new Function(`return ${conditionString}`)()
299
- if (isConditionPassing) {
389
+ default: {
300
390
  classNamesToAppend.add(styleRef.content.referenceId)
301
391
  return
302
392
  }
303
- return
304
393
  }
305
- case 'state':
306
- throw new PluginCSS(`State reference is not supported in the project stylesheet.`)
307
- default: {
308
- classNamesToAppend.add(styleRef.content.referenceId)
309
- return
394
+ } else {
395
+ const nameToAppend = styleRef.content.condition.reference.content.id
396
+
397
+ const { conditions } = styleRef.content.condition.expression
398
+
399
+ const operator = conditions[0].operation as '===' | '!==' | '<' | '<=' | '>' | '>='
400
+ const right = conditions[0].operand as string | number | boolean
401
+ const referenceType = styleRef.content.condition.reference.content.referenceType
402
+
403
+ let binaryExpressionType = ''
404
+ switch (referenceType) {
405
+ case 'prop': {
406
+ binaryExpressionType = propDefinitions[nameToAppend].type
407
+ break
408
+ }
409
+ case 'state': {
410
+ binaryExpressionType = stateDefinitions[nameToAppend].type
411
+ break
412
+ }
413
+ default: {
414
+ throw new PluginCSS(
415
+ `Un-supported reference type ${referenceType} for ${nameToAppend}`
416
+ )
417
+ }
310
418
  }
419
+
420
+ const binaryExpression = createBinaryExpression(
421
+ { operation: operator, operand: right },
422
+ {
423
+ key: (referenceType === 'prop' ? 'props?.' : '') + nameToAppend,
424
+ type: binaryExpressionType,
425
+ }
426
+ )
427
+
428
+ const conditionalExpression = types.conditionalExpression(
429
+ binaryExpression,
430
+ types.stringLiteral(content.referenceId),
431
+ types.stringLiteral('')
432
+ )
433
+
434
+ dynamicVariantsToAppend.add(conditionalExpression)
435
+ return
311
436
  }
312
437
  } else {
313
438
  classNamesToAppend.add(content.referenceId)
@@ -352,7 +477,12 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
352
477
  ? types.identifier(dynamicVariantPrefix)
353
478
  : types.identifier(propsPrefix)
354
479
 
355
- return types.memberExpression(dynamicAttrValueIdentifier, types.identifier(variant))
480
+ if (typeof variant === 'string') {
481
+ return types.memberExpression(dynamicAttrValueIdentifier, types.identifier(variant))
482
+ } else {
483
+ // variant is a ConditionalExpression, so return it directly
484
+ return variant
485
+ }
356
486
  })
357
487
  )
358
488
  }