@@ -63,22 +63,29 @@ const kebabCasePattern = /-([a-z])/g;
6363const pattern = / ( ' | " ) ( .* ?) \1/ ;
6464
6565/**
66- * Evaluate css-variable and css-calc expressions
66+ * Evaluate css-variable and css-calc expressions.
6767 */
68- function evaluateCssExpressions ( view : ViewBase , property : string , value : string ) {
69- const newValue = _evaluateCssVariableExpression ( view , property , value ) ;
70- if ( newValue === 'unset' ) {
71- return unsetValue ;
68+ function evaluateCssExpressions ( view : ViewBase , property : string , value : string , onCssVarExpressionParse ?: ( cssVarName : string ) => void ) {
69+ if ( typeof value !== 'string' ) {
70+ return value ;
7271 }
7372
74- value = newValue ;
73+ if ( isCssVariableExpression ( value ) ) {
74+ const newValue = _evaluateCssVariableExpression ( view , value , onCssVarExpressionParse ) ;
75+ if ( newValue === undefined ) {
76+ return unsetValue ;
77+ }
7578
76- try {
77- value = _evaluateCssCalcExpression ( value ) ;
78- } catch ( e ) {
79- Trace . write ( `Failed to evaluate css-calc for property [${ property } ] for expression [${ value } ] to ${ view } . ${ e . stack } ` , Trace . categories . Error , Trace . messageType . error ) ;
79+ value = newValue ;
80+ }
8081
81- return unsetValue ;
82+ if ( isCssCalcExpression ( value ) ) {
83+ try {
84+ value = _evaluateCssCalcExpression ( value ) ;
85+ } catch ( e ) {
86+ Trace . write ( `Failed to evaluate css-calc for property [${ property } ] for expression [${ value } ] to ${ view } . ${ e . stack } ` , Trace . categories . Error , Trace . messageType . error ) ;
87+ return unsetValue ;
88+ }
8289 }
8390
8491 return value ;
@@ -710,48 +717,63 @@ export class CssState {
710717 const valuesToApply = { } ;
711718 const cssExpsProperties = { } ;
712719 const replacementFunc = ( g ) => g [ 1 ] . toUpperCase ( ) ;
720+ const onCssVarExpressionParse = ( cssVarName : string ) => {
721+ // If variable name is still in the property bag, parse its value and apply it to the view
722+ if ( cssVarName in cssExpsProperties ) {
723+ cssExpsPropsCallback ( cssVarName ) ;
724+ }
725+ } ;
726+ // This callback is also used for handling nested css variable expressions
727+ const cssExpsPropsCallback = ( property : string ) => {
728+ let value = cssExpsProperties [ property ] ;
713729
714- for ( const property in newPropertyValues ) {
715- const value = cleanupImportantFlags ( newPropertyValues [ property ] , property ) ;
730+ // Remove the property first to avoid recalculating it in a later step using lazy loading
731+ delete cssExpsProperties [ property ] ;
716732
717- const isCssExp = isCssVariableExpression ( value ) || isCssCalcExpression ( value ) ;
733+ value = evaluateCssExpressions ( view , property , value , onCssVarExpressionParse ) ;
718734
719- if ( isCssExp ) {
720- // we handle css exp separately because css vars must be evaluated first
721- cssExpsProperties [ property ] = value ;
722- continue ;
723- }
724- delete oldProperties [ property ] ;
725- if ( property in oldProperties && oldProperties [ property ] === value ) {
726- // Skip unchanged values
727- continue ;
735+ if ( value === unsetValue ) {
736+ delete newPropertyValues [ property ] ;
728737 }
738+
729739 if ( isCssVariable ( property ) ) {
730740 view . style . setScopedCssVariable ( property , value ) ;
731741 delete newPropertyValues [ property ] ;
732- continue ;
733742 }
743+
734744 valuesToApply [ property ] = value ;
735- }
736- //we need to parse CSS vars first before evaluating css expressions
737- for ( const property in cssExpsProperties ) {
745+ } ;
746+
747+ for ( const property in newPropertyValues ) {
748+ const value = cleanupImportantFlags ( newPropertyValues [ property ] , property ) ;
749+ const isCssExp = isCssVariableExpression ( value ) || isCssCalcExpression ( value ) ;
750+
751+ // We can skip the unset part of old values since they will be overwritten by new values
738752 delete oldProperties [ property ] ;
739- const value = evaluateCssExpressions ( view , property , cssExpsProperties [ property ] ) ;
740- if ( property in oldProperties && oldProperties [ property ] === value ) {
741- // Skip unchanged values
753+
754+ if ( isCssExp ) {
755+ // We handle css exp separately because css vars must be evaluated first
756+ cssExpsProperties [ property ] = value ;
742757 continue ;
743758 }
744- if ( value === unsetValue ) {
745- delete newPropertyValues [ property ] ;
746- }
759+
747760 if ( isCssVariable ( property ) ) {
748761 view . style . setScopedCssVariable ( property , value ) ;
749762 delete newPropertyValues [ property ] ;
763+ continue ;
750764 }
751-
752765 valuesToApply [ property ] = value ;
753766 }
754767
768+ const cssExpsPropKeys = Object . keys ( cssExpsProperties ) ;
769+
770+ // We need to parse CSS vars first before evaluating css expressions
771+ for ( const property of cssExpsPropKeys ) {
772+ if ( property in cssExpsProperties ) {
773+ cssExpsPropsCallback ( property ) ;
774+ }
775+ }
776+
755777 // Unset removed values
756778 for ( const property in oldProperties ) {
757779 if ( property in view . style ) {
0 commit comments