@@ -7,8 +7,9 @@ import { b, x } from 'code-red';
7
7
import Expression from '../../../nodes/shared/Expression' ;
8
8
import Text from '../../../nodes/Text' ;
9
9
import handle_select_value_binding from './handle_select_value_binding' ;
10
+ import { Identifier , Node } from 'estree' ;
10
11
11
- export default class AttributeWrapper {
12
+ export class BaseAttributeWrapper {
12
13
node : Attribute ;
13
14
parent : ElementWrapper ;
14
15
@@ -21,7 +22,29 @@ export default class AttributeWrapper {
21
22
parent . not_static_content ( ) ;
22
23
23
24
block . add_dependencies ( node . dependencies ) ;
25
+ }
26
+ }
27
+
28
+ render ( _block : Block ) { }
29
+ }
24
30
31
+ export default class AttributeWrapper extends BaseAttributeWrapper {
32
+ node : Attribute ;
33
+ parent : ElementWrapper ;
34
+ metadata : any ;
35
+ name : string ;
36
+ property_name : string ;
37
+ is_indirectly_bound_value : boolean ;
38
+ is_src : boolean ;
39
+ is_select_value_attribute : boolean ;
40
+ is_input_value : boolean ;
41
+ should_cache : boolean ;
42
+ last : Identifier ;
43
+
44
+ constructor ( parent : ElementWrapper , block : Block , node : Attribute ) {
45
+ super ( parent , block , node ) ;
46
+
47
+ if ( node . dependencies . size > 0 ) {
25
48
// special case — <option value={foo}> — see below
26
49
if ( this . parent . node . name === 'option' && node . name === 'value' ) {
27
50
let select : ElementWrapper = this . parent ;
@@ -42,31 +65,22 @@ export default class AttributeWrapper {
42
65
handle_select_value_binding ( this , node . dependencies ) ;
43
66
}
44
67
}
45
- }
46
68
47
- is_indirectly_bound_value ( ) {
48
- const element = this . parent ;
49
- const name = fix_attribute_casing ( this . node . name ) ;
50
- return name === 'value' &&
51
- ( element . node . name === 'option' || // TODO check it's actually bound
52
- ( element . node . name === 'input' &&
53
- element . node . bindings . some (
54
- ( binding ) =>
55
- / c h e c k e d | g r o u p / . test ( binding . name )
56
- ) ) ) ;
69
+ this . name = fix_attribute_casing ( this . node . name ) ;
70
+ this . metadata = this . get_metadata ( ) ;
71
+ this . is_indirectly_bound_value = is_indirectly_bound_value ( this ) ;
72
+ this . property_name = this . is_indirectly_bound_value
73
+ ? '__value'
74
+ : this . metadata && this . metadata . property_name ;
75
+ this . is_src = this . name === 'src' ; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
76
+ this . is_select_value_attribute = this . name === 'value' && this . parent . node . name === 'select' ;
77
+ this . is_input_value = this . name === 'value' && this . parent . node . name === 'input' ;
78
+ this . should_cache = should_cache ( this ) ;
57
79
}
58
80
59
81
render ( block : Block ) {
60
82
const element = this . parent ;
61
- const name = fix_attribute_casing ( this . node . name ) ;
62
-
63
- const metadata = this . get_metadata ( ) ;
64
-
65
- const is_indirectly_bound_value = this . is_indirectly_bound_value ( ) ;
66
-
67
- const property_name = is_indirectly_bound_value
68
- ? '__value'
69
- : metadata && metadata . property_name ;
83
+ const { name, property_name, should_cache, is_indirectly_bound_value } = this ;
70
84
71
85
// xlink is a special case... we could maybe extend this to generic
72
86
// namespaced attributes but I'm not sure that's applicable in
@@ -82,29 +96,15 @@ export default class AttributeWrapper {
82
96
const dependencies = this . get_dependencies ( ) ;
83
97
const value = this . get_value ( block ) ;
84
98
85
- const is_src = this . node . name === 'src' ; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
86
- const is_select_value_attribute =
87
- name === 'value' && element . node . name === 'select' ;
88
-
89
- const is_input_value = name === 'value' && element . node . name === 'input' ;
90
-
91
- const should_cache = is_src || this . node . should_cache ( ) ;
92
-
93
- const last = should_cache && block . get_unique_name (
94
- `${ element . var . name } _${ name . replace ( / [ ^ a - z A - Z _ $ ] / g, '_' ) } _value`
95
- ) ;
96
-
97
- if ( should_cache ) block . add_variable ( last ) ;
98
-
99
99
let updater ;
100
- const init = should_cache ? x ` ${ last } = ${ value } ` : value ;
100
+ const init = this . get_init ( block , value ) ;
101
101
102
102
if ( is_legacy_input_type ) {
103
103
block . chunks . hydrate . push (
104
104
b `@set_input_type(${ element . var } , ${ init } );`
105
105
) ;
106
- updater = b `@set_input_type(${ element . var } , ${ should_cache ? last : value } );` ;
107
- } else if ( is_select_value_attribute ) {
106
+ updater = b `@set_input_type(${ element . var } , ${ should_cache ? this . last : value } );` ;
107
+ } else if ( this . is_select_value_attribute ) {
108
108
// annoying special case
109
109
const is_multiple_select = element . node . get_static_attribute_value ( 'multiple' ) ;
110
110
@@ -117,45 +117,37 @@ export default class AttributeWrapper {
117
117
block . chunks . mount . push ( b `
118
118
${ updater }
119
119
` ) ;
120
- } else if ( is_src ) {
120
+ } else if ( this . is_src ) {
121
121
block . chunks . hydrate . push (
122
- b `if (${ element . var } .src !== ${ init } ) ${ method } (${ element . var } , "${ name } ", ${ last } );`
122
+ b `if (${ element . var } .src !== ${ init } ) ${ method } (${ element . var } , "${ name } ", ${ this . last } );`
123
123
) ;
124
- updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? last : value } );` ;
124
+ updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? this . last : value } );` ;
125
125
} else if ( property_name ) {
126
126
block . chunks . hydrate . push (
127
127
b `${ element . var } .${ property_name } = ${ init } ;`
128
128
) ;
129
129
updater = block . renderer . options . dev
130
- ? b `@prop_dev(${ element . var } , "${ property_name } ", ${ should_cache ? last : value } );`
131
- : b `${ element . var } .${ property_name } = ${ should_cache ? last : value } ;` ;
130
+ ? b `@prop_dev(${ element . var } , "${ property_name } ", ${ should_cache ? this . last : value } );`
131
+ : b `${ element . var } .${ property_name } = ${ should_cache ? this . last : value } ;` ;
132
132
} else {
133
133
block . chunks . hydrate . push (
134
134
b `${ method } (${ element . var } , "${ name } ", ${ init } );`
135
135
) ;
136
- updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? last : value } );` ;
136
+ updater = b `${ method } (${ element . var } , "${ name } ", ${ should_cache ? this . last : value } );` ;
137
137
}
138
138
139
- if ( dependencies . length > 0 ) {
140
- let condition = block . renderer . dirty ( dependencies ) ;
141
-
142
- if ( should_cache ) {
143
- condition = is_src
144
- ? x `${ condition } && (${ element . var } .src !== (${ last } = ${ value } ))`
145
- : x `${ condition } && (${ last } !== (${ last } = ${ value } ))` ;
146
- }
147
-
148
- if ( is_input_value ) {
149
- const type = element . node . get_static_attribute_value ( 'type' ) ;
139
+ if ( is_indirectly_bound_value ) {
140
+ const update_value = b `${ element . var } .value = ${ element . var } .__value;` ;
141
+ block . chunks . hydrate . push ( update_value ) ;
150
142
151
- if ( type === null || type === "" || type === "text" || type === "email" || type === "password" ) {
152
- condition = x `${ condition } && ${ element . var } .${ property_name } !== ${ should_cache ? last : value } ` ;
153
- }
154
- }
143
+ updater = b `
144
+ ${ updater }
145
+ ${ update_value } ;
146
+ ` ;
147
+ }
155
148
156
- if ( block . has_outros ) {
157
- condition = x `!#current || ${ condition } ` ;
158
- }
149
+ if ( dependencies . length > 0 ) {
150
+ const condition = this . get_dom_update_conditions ( block , block . renderer . dirty ( dependencies ) ) ;
159
151
160
152
block . chunks . update . push ( b `
161
153
if (${ condition } ) {
@@ -167,13 +159,44 @@ export default class AttributeWrapper {
167
159
if ( this . node . is_true && name === 'autofocus' ) {
168
160
block . autofocus = element . var ;
169
161
}
162
+ }
170
163
171
- if ( is_indirectly_bound_value ) {
172
- const update_value = b `${ element . var } .value = ${ element . var } .__value;` ;
164
+ get_init ( block : Block , value ) {
165
+ this . last = this . should_cache && block . get_unique_name (
166
+ `${ this . parent . var . name } _${ this . name . replace ( / [ ^ a - z A - Z _ $ ] / g, '_' ) } _value`
167
+ ) ;
173
168
174
- block . chunks . hydrate . push ( update_value ) ;
175
- if ( dependencies . length > 0 ) block . chunks . update . push ( update_value ) ;
169
+ if ( this . should_cache ) block . add_variable ( this . last ) ;
170
+
171
+ return this . should_cache ? x `${ this . last } = ${ value } ` : value ;
172
+ }
173
+
174
+ get_dom_update_conditions ( block : Block , dependency_condition : Node ) {
175
+ const { property_name, should_cache, last } = this ;
176
+ const element = this . parent ;
177
+ const value = this . get_value ( block ) ;
178
+
179
+ let condition = dependency_condition ;
180
+
181
+ if ( should_cache ) {
182
+ condition = this . is_src
183
+ ? x `${ condition } && (${ element . var } .src !== (${ last } = ${ value } ))`
184
+ : x `${ condition } && (${ last } !== (${ last } = ${ value } ))` ;
185
+ }
186
+
187
+ if ( this . is_input_value ) {
188
+ const type = element . node . get_static_attribute_value ( 'type' ) ;
189
+
190
+ if ( type === null || type === "" || type === "text" || type === "email" || type === "password" ) {
191
+ condition = x `${ condition } && ${ element . var } .${ property_name } !== ${ should_cache ? last : value } ` ;
192
+ }
193
+ }
194
+
195
+ if ( block . has_outros ) {
196
+ condition = x `!#current || ${ condition } ` ;
176
197
}
198
+
199
+ return condition ;
177
200
}
178
201
179
202
get_dependencies ( ) {
@@ -194,15 +217,14 @@ export default class AttributeWrapper {
194
217
195
218
get_metadata ( ) {
196
219
if ( this . parent . node . namespace ) return null ;
197
- const metadata = attribute_lookup [ fix_attribute_casing ( this . node . name ) ] ;
220
+ const metadata = attribute_lookup [ this . name ] ;
198
221
if ( metadata && metadata . applies_to && ! metadata . applies_to . includes ( this . parent . node . name ) ) return null ;
199
222
return metadata ;
200
223
}
201
224
202
225
get_value ( block ) {
203
226
if ( this . node . is_true ) {
204
- const metadata = this . get_metadata ( ) ;
205
- if ( metadata && boolean_attribute . has ( metadata . property_name . toLowerCase ( ) ) ) {
227
+ if ( this . metadata && boolean_attribute . has ( this . metadata . property_name . toLowerCase ( ) ) ) {
206
228
return x `true` ;
207
229
}
208
230
return x `""` ;
@@ -350,4 +372,19 @@ const boolean_attribute = new Set([
350
372
'required' ,
351
373
'reversed' ,
352
374
'selected'
353
- ] ) ;
375
+ ] ) ;
376
+
377
+ function should_cache ( attribute : AttributeWrapper ) {
378
+ return attribute . is_src || attribute . node . should_cache ( ) ;
379
+ }
380
+
381
+ function is_indirectly_bound_value ( attribute : AttributeWrapper ) {
382
+ const element = attribute . parent ;
383
+ return attribute . name === 'value' &&
384
+ ( element . node . name === 'option' || // TODO check it's actually bound
385
+ ( element . node . name === 'input' &&
386
+ element . node . bindings . some (
387
+ ( binding ) =>
388
+ / c h e c k e d | g r o u p / . test ( binding . name )
389
+ ) ) ) ;
390
+ }
0 commit comments