@@ -15,6 +15,7 @@ import EventHandler from './EventHandler';
15
15
import Transition from './Transition' ;
16
16
import Animation from './Animation' ;
17
17
import Action from './Action' ;
18
+ import Class from './Class' ;
18
19
import Text from './Text' ;
19
20
import * as namespaces from '../../utils/namespaces' ;
20
21
import mapChildren from './shared/mapChildren' ;
@@ -68,6 +69,8 @@ export default class Element extends Node {
68
69
attributes : Attribute [ ] ;
69
70
actions : Action [ ] ;
70
71
bindings : Binding [ ] ;
72
+ classes : Class [ ] ;
73
+ classDependencies : string [ ] ;
71
74
handlers : EventHandler [ ] ;
72
75
intro ?: Transition ;
73
76
outro ?: Transition ;
@@ -90,6 +93,8 @@ export default class Element extends Node {
90
93
this . attributes = [ ] ;
91
94
this . actions = [ ] ;
92
95
this . bindings = [ ] ;
96
+ this . classes = [ ] ;
97
+ this . classDependencies = [ ] ;
93
98
this . handlers = [ ] ;
94
99
95
100
this . intro = null ;
@@ -144,6 +149,10 @@ export default class Element extends Node {
144
149
this . bindings . push ( new Binding ( compiler , this , scope , node ) ) ;
145
150
break ;
146
151
152
+ case 'Class' :
153
+ this . classes . push ( new Class ( compiler , this , scope , node ) ) ;
154
+ break ;
155
+
147
156
case 'EventHandler' :
148
157
this . handlers . push ( new EventHandler ( compiler , this , scope , node ) ) ;
149
158
break ;
@@ -228,6 +237,13 @@ export default class Element extends Node {
228
237
block . addDependencies ( binding . value . dependencies ) ;
229
238
} ) ;
230
239
240
+ this . classes . forEach ( classDir => {
241
+ this . parent . cannotUseInnerHTML ( ) ;
242
+ if ( classDir . expression ) {
243
+ block . addDependencies ( classDir . expression . dependencies ) ;
244
+ }
245
+ } ) ;
246
+
231
247
this . handlers . forEach ( handler => {
232
248
this . parent . cannotUseInnerHTML ( ) ;
233
249
block . addDependencies ( handler . dependencies ) ;
@@ -403,6 +419,7 @@ export default class Element extends Node {
403
419
this . addTransitions ( block ) ;
404
420
this . addAnimation ( block ) ;
405
421
this . addActions ( block ) ;
422
+ this . addClasses ( block ) ;
406
423
407
424
if ( this . initialUpdate ) {
408
425
block . builders . mount . addBlock ( this . initialUpdate ) ;
@@ -584,6 +601,9 @@ export default class Element extends Node {
584
601
}
585
602
586
603
this . attributes . forEach ( ( attribute : Attribute ) => {
604
+ if ( attribute . name === 'class' && attribute . isDynamic ) {
605
+ this . classDependencies . push ( ...attribute . dependencies ) ;
606
+ }
587
607
attribute . render ( block ) ;
588
608
} ) ;
589
609
}
@@ -867,6 +887,26 @@ export default class Element extends Node {
867
887
} ) ;
868
888
}
869
889
890
+ addClasses ( block : Block ) {
891
+ this . classes . forEach ( classDir => {
892
+ const { expression : { snippet, dependencies} , name } = classDir ;
893
+ const updater = `@toggleClass(${ this . var } , "${ name } ", ${ snippet } );` ;
894
+
895
+ block . builders . hydrate . addLine ( updater ) ;
896
+
897
+ if ( ( dependencies && dependencies . size > 0 ) || this . classDependencies . length ) {
898
+ const allDeps = this . classDependencies . concat ( ...dependencies ) ;
899
+ const deps = allDeps . map ( dependency => `changed.${ dependency } ` ) . join ( ' || ' ) ;
900
+ const condition = allDeps . length > 1 ? `(${ deps } )` : deps ;
901
+
902
+ block . builders . update . addConditional (
903
+ condition ,
904
+ updater
905
+ ) ;
906
+ }
907
+ } ) ;
908
+ }
909
+
870
910
getStaticAttributeValue ( name : string ) {
871
911
const attribute = this . attributes . find (
872
912
( attr : Attribute ) => attr . type === 'Attribute' && attr . name . toLowerCase ( ) === name
@@ -937,6 +977,13 @@ export default class Element extends Node {
937
977
appendTarget . slots [ slotName ] = '' ;
938
978
}
939
979
980
+ const classExpr = this . classes . map ( ( classDir : Class ) => {
981
+ const { expression : { snippet } , name } = classDir ;
982
+ return `${ snippet } ? "${ name } " : ""` ;
983
+ } ) . join ( ', ' ) ;
984
+
985
+ let addClassAttribute = classExpr ? true : false ;
986
+
940
987
if ( this . attributes . find ( attr => attr . isSpread ) ) {
941
988
// TODO dry this out
942
989
const args = [ ] ;
@@ -977,12 +1024,19 @@ export default class Element extends Node {
977
1024
) {
978
1025
// a boolean attribute with one non-Text chunk
979
1026
openingTag += '${' + attribute . chunks [ 0 ] . snippet + ' ? " ' + attribute . name + '" : "" }' ;
1027
+ } else if ( attribute . name === 'class' && classExpr ) {
1028
+ addClassAttribute = false ;
1029
+ openingTag += ` class="\${ [\`${ attribute . stringifyForSsr ( ) } \`, ${ classExpr } ].join(' ') }"` ;
980
1030
} else {
981
1031
openingTag += ` ${ attribute . name } ="${ attribute . stringifyForSsr ( ) } "` ;
982
1032
}
983
1033
} ) ;
984
1034
}
985
1035
1036
+ if ( addClassAttribute ) {
1037
+ openingTag += ` class="\${ [${ classExpr } ].join(' ') }"` ;
1038
+ }
1039
+
986
1040
openingTag += '>' ;
987
1041
988
1042
compiler . target . append ( openingTag ) ;
0 commit comments