1
+ import attributeLookup from './lookup' ;
2
+ import deindent from '../../../../utils/deindent' ;
3
+ import { stringify } from '../../../../utils/stringify' ;
4
+ import getExpressionPrecedence from '../../../../utils/getExpressionPrecedence' ;
5
+ import getStaticAttributeValue from '../../../shared/getStaticAttributeValue' ;
6
+ import { DomGenerator } from '../../index' ;
7
+ import Block from '../../Block' ;
8
+ import { Node } from '../../../../interfaces' ;
9
+ import { State } from '../../interfaces' ;
10
+
11
+ export interface StyleProp {
12
+ key : string ;
13
+ value : Node [ ] ;
14
+ }
15
+
16
+ export default function visitStyleAttribute (
17
+ generator : DomGenerator ,
18
+ block : Block ,
19
+ state : State ,
20
+ node : Node ,
21
+ attribute : Node ,
22
+ styleProps : StyleProp [ ]
23
+ ) {
24
+ styleProps . forEach ( ( prop : StyleProp ) => {
25
+ let value ;
26
+
27
+ if ( isDynamic ( prop . value ) ) {
28
+ const allDependencies = new Set ( ) ;
29
+ let shouldCache ;
30
+ let hasChangeableIndex ;
31
+
32
+ value =
33
+ ( ( prop . value . length === 1 || prop . value [ 0 ] . type === 'Text' ) ? '' : `"" + ` ) +
34
+ prop . value
35
+ . map ( ( chunk : Node ) => {
36
+ if ( chunk . type === 'Text' ) {
37
+ return stringify ( chunk . data ) ;
38
+ } else {
39
+ const { snippet, dependencies, indexes } = block . contextualise ( chunk . expression ) ;
40
+
41
+ if ( Array . from ( indexes ) . some ( index => block . changeableIndexes . get ( index ) ) ) {
42
+ hasChangeableIndex = true ;
43
+ }
44
+
45
+ dependencies . forEach ( d => {
46
+ allDependencies . add ( d ) ;
47
+ } ) ;
48
+
49
+ return getExpressionPrecedence ( chunk . expression ) <= 13 ? `( ${ snippet } )` : snippet ;
50
+ }
51
+ } )
52
+ . join ( ' + ' ) ;
53
+
54
+ if ( allDependencies . size || hasChangeableIndex ) {
55
+ const dependencies = Array . from ( allDependencies ) ;
56
+ const condition = (
57
+ ( block . hasOutroMethod ? `#outroing || ` : '' ) +
58
+ dependencies . map ( dependency => `changed.${ dependency } ` ) . join ( ' || ' )
59
+ ) ;
60
+
61
+ block . builders . update . addConditional (
62
+ condition ,
63
+ `${ node . var } .style.setProperty('${ prop . key } ', ${ value } );`
64
+ ) ;
65
+ }
66
+ } else {
67
+ value = stringify ( prop . value [ 0 ] . data ) ;
68
+ }
69
+
70
+ block . builders . hydrate . addLine (
71
+ `${ node . var } .style.setProperty('${ prop . key } ', ${ value } );`
72
+ ) ;
73
+ } ) ;
74
+ }
75
+
76
+ export function optimizeStyle ( value : Node [ ] ) {
77
+ let expectingKey = true ;
78
+ let i = 0 ;
79
+
80
+ const props : { key : string , value : Node [ ] } [ ] = [ ] ;
81
+ let chunks = value . slice ( ) ;
82
+
83
+ while ( chunks . length ) {
84
+ const chunk = chunks [ 0 ] ;
85
+
86
+ if ( chunk . type !== 'Text' ) return null ;
87
+
88
+ const keyMatch = / ^ \s * ( [ \w - ] + ) : \s * / . exec ( chunk . data ) ;
89
+ if ( ! keyMatch ) return null ;
90
+
91
+ const key = keyMatch [ 1 ] ;
92
+
93
+ const offset = keyMatch . index + keyMatch [ 0 ] . length ;
94
+ const remainingData = chunk . data . slice ( offset ) ;
95
+
96
+ if ( remainingData ) {
97
+ chunks [ 0 ] = {
98
+ start : chunk . start + offset ,
99
+ end : chunk . end ,
100
+ type : 'Text' ,
101
+ data : remainingData
102
+ } ;
103
+ } else {
104
+ chunks . shift ( ) ;
105
+ }
106
+
107
+ const result = getStyleValue ( chunks ) ;
108
+ if ( ! result ) return null ;
109
+
110
+ props . push ( { key, value : result . value } ) ;
111
+ chunks = result . chunks ;
112
+ }
113
+
114
+ return props ;
115
+ }
116
+
117
+ function getStyleValue ( chunks : Node [ ] ) {
118
+ const value : Node [ ] = [ ] ;
119
+
120
+ let inUrl = false ;
121
+
122
+ while ( chunks . length ) {
123
+ const chunk = chunks [ 0 ] ;
124
+
125
+ if ( chunk . type === 'Text' ) {
126
+ // TODO annoying special case — urls can contain semicolons
127
+
128
+ let index = chunk . data . indexOf ( ';' ) ;
129
+
130
+ if ( index === - 1 ) {
131
+ value . push ( chunk ) ;
132
+ chunks . shift ( ) ;
133
+ }
134
+
135
+ else {
136
+ if ( index > 0 ) {
137
+ value . push ( {
138
+ type : 'Text' ,
139
+ start : chunk . start ,
140
+ end : chunk . start + index ,
141
+ data : chunk . data . slice ( 0 , index )
142
+ } ) ;
143
+ }
144
+
145
+ while ( / [ ; \s ] / . test ( chunk . data [ index ] ) ) index += 1 ;
146
+
147
+ const remainingData = chunk . data . slice ( index ) ;
148
+
149
+ if ( remainingData ) {
150
+ chunks [ 0 ] = {
151
+ start : chunk . start + index ,
152
+ end : chunk . end ,
153
+ type : 'Text' ,
154
+ data : remainingData
155
+ } ;
156
+ } else {
157
+ chunks . shift ( ) ;
158
+ }
159
+
160
+ break ;
161
+ }
162
+ }
163
+
164
+ else {
165
+ value . push ( chunks . shift ( ) ) ;
166
+ }
167
+ }
168
+
169
+ return {
170
+ chunks,
171
+ value
172
+ } ;
173
+ }
174
+
175
+ function isDynamic ( value : Node [ ] ) {
176
+ return value . length > 1 || value [ 0 ] . type !== 'Text' ;
177
+ }
0 commit comments