1515 */
1616
1717import { ALIASES , DEFAULT_VALUES , PROPERTY_LIST , PROPERTY_VARIANTS , STATE_LIST , VALUE_WRAPPER } from "./list" ;
18- import { HASH_VAR_PREFIX , PROPERTY_REGEX , HASH_CLASS_PREFIX } from "./helpers" ;
18+ import { HASH_VAR_PREFIX , PROPERTY_REGEX , HASH_CLASS_PREFIX , trim } from "./helpers" ;
1919import { Root } from "./Root" ;
2020import type { UserSettings } from "./helpers" ;
21+ import { resolveMixin } from "./mixins" ;
2122
2223type PropertyInfo = {
2324 entry : string ,
@@ -32,9 +33,20 @@ type PropertyInfo = {
3233 rank : number
3334} ;
3435
36+ type AssemblerEntry = {
37+ n : string , //name
38+ p : string , //property
39+ e : string , //class|entry
40+ }
3541
3642const VAR_REGEX = / @ ( [ a - z A - Z 0 - 9 \- _ ] + ) / g;
3743const REPLACE_REGEX = / \$ ( s e l e c t o r | b o d y | c l a s s | v a l u e | p r o p e r t y | s t a t e | v a r i a n t s | v a r ) / g;
44+ const SPLIT_REGEX = / (?< ! \\ ) ; / ;
45+ const MIXIN_PREFIX = '^' ;
46+
47+ // do not match comma inside parenthesis
48+ // 2px, linear-gradient(blue, red), inline => [2px, linear-gradient(blue, red), inline]
49+ const COMMA_DELIMITED = / \s * , \s * (? ! [ ^ ( ] * \) ) / gm;
3850
3951export default class StyleHandler {
4052 readonly style : CSSStyleSheet ;
@@ -61,30 +73,29 @@ export default class StyleHandler {
6173 return this . settings ;
6274 }
6375
64- handleStyleChange ( element : HTMLElement , oldContent : string | null , content : string | null ) : void {
76+ handleStyleChange ( element : HTMLElement , content : string | null , old : AssemblerEntry [ ] ) : AssemblerEntry [ ] {
6577
6678 if ( content === null ) {
67- return this . handleStyleRemoved ( element , oldContent ) ;
79+ return this . handleStyleRemoved ( element , old ) ;
6880 }
6981
7082 const newEntries = this . getStyleEntries ( content ) ;
7183 const classList = element . hasAttribute ( 'class' ) ? element . getAttribute ( 'class' ) . split ( ' ' ) : [ ] ;
84+ const assemblerEntries : AssemblerEntry [ ] = [ ] ;
7285
7386 // remove old entries
74- if ( oldContent !== null ) {
75- for ( const { name, property, entry} of this . getStyleProperties ( oldContent ) ) {
76- if ( ! newEntries . has ( name ) ) {
77- const index = classList . indexOf ( entry ) ;
78- if ( index >= 0 ) {
79- classList . splice ( index , 1 ) ;
80- }
81- element . style . removeProperty ( property ) ;
87+ for ( const { n :name , p :property , e :entry } of old ) {
88+ if ( ! newEntries . has ( name ) ) {
89+ const index = classList . indexOf ( entry ) ;
90+ if ( index >= 0 ) {
91+ classList . splice ( index , 1 ) ;
8292 }
93+ element . style . removeProperty ( property ) ;
8394 }
8495 }
8596
8697 for ( const info of newEntries . values ( ) ) {
87- const { entry, property, hash, value} = info ;
98+ const { entry, property, hash, value, name } = info ;
8899 const index = classList . indexOf ( entry ) ;
89100 if ( index < 0 ) {
90101 classList . push ( entry ) ;
@@ -93,16 +104,19 @@ export default class StyleHandler {
93104 this . generateCSS ( info ) ;
94105 }
95106 element . style . setProperty ( property , value ) ;
107+ assemblerEntries . push ( { e :entry , n : name , p : property } ) ;
96108 }
97109
98110 element . setAttribute ( 'class' , classList . join ( ' ' ) ) ;
111+
112+ return assemblerEntries ;
99113 }
100114
101- handleStyleRemoved ( element : HTMLElement , content : string ) : void {
115+ handleStyleRemoved ( element : HTMLElement , old : AssemblerEntry [ ] ) : AssemblerEntry [ ] {
102116
103117 const classList = element . hasAttribute ( 'class' ) ? element . getAttribute ( 'class' ) . split ( ' ' ) : [ ] ;
104118
105- for ( const { property, entry} of this . getStyleProperties ( content ) ) {
119+ for ( const { p : property , e : entry } of old ) {
106120 const index = classList . indexOf ( entry ) ;
107121 if ( index >= 0 ) {
108122 classList . splice ( index , 1 ) ;
@@ -111,6 +125,8 @@ export default class StyleHandler {
111125 }
112126
113127 element . setAttribute ( 'class' , classList . join ( ' ' ) ) ;
128+
129+ return [ ] ;
114130 }
115131
116132 private extract ( attr : string , value : string | string [ ] | null = null ) : PropertyInfo [ ] {
@@ -192,22 +208,15 @@ export default class StyleHandler {
192208 return result ;
193209 }
194210
195- private getStyleEntries ( content : string , resolve : boolean = true ) : Map < string , PropertyInfo > {
211+ private getStyleEntries ( content : string ) : Map < string , PropertyInfo > {
196212 const entries = new Map < string , PropertyInfo > ( ) ;
197213
198- for ( let name of content . split ( ';' ) ) {
199- name = name . trim ( ) ;
200- if ( name === '' ) {
201- continue ;
202- }
203-
214+ for ( let name of this . getResolvedProperties ( content ) ) {
204215 let value = null ;
205-
206216 const pos = name . indexOf ( ':' ) ;
207- if ( pos < 0 ) {
208- name = name . trim ( ) ;
209- } else {
210- value = resolve ? name . substr ( pos + 1 ) : null ;
217+
218+ if ( pos >= 0 ) {
219+ value = name . substr ( pos + 1 ) ;
211220 name = name . substr ( 0 , pos ) . trim ( ) ;
212221 }
213222
@@ -219,64 +228,42 @@ export default class StyleHandler {
219228 return entries ;
220229 }
221230
222- private * getStyleProperties ( content : string ) : Iterable < { property : string , name : string , entry : string } > {
223- const base = STATE_LIST . length ;
224- const MEDIA_LIST = this . breakpoints ;
225-
226- for ( let attr of content . split ( ';' ) ) {
227- let value = null ;
228- const pos = attr . indexOf ( ':' ) ;
229- if ( pos < 0 ) {
230- attr = attr . trim ( ) ;
231- } else {
232- value = attr . substr ( pos + 1 ) ;
233- attr = attr . substr ( 0 , pos ) . trim ( ) ;
234- }
235-
236- const m = PROPERTY_REGEX . exec ( attr ) ?. groups ;
237-
238- if ( ! m || ! m . property ) {
239- continue ;
240- }
241-
242- const media = MEDIA_LIST . indexOf ( m . media || 'all' ) ;
243- const state = STATE_LIST . indexOf ( m . state || 'normal' ) ;
244-
245- if ( media < 0 || state < 0 ) {
231+ private getResolvedProperties ( content : string , stack : string [ ] = [ ] ) : string [ ] {
232+ const entries = [ ] ;
233+ for ( let name of content . split ( SPLIT_REGEX ) ) {
234+ name = name . trim ( ) ;
235+ if ( name === '' ) {
246236 continue ;
247237 }
248-
249- let properties : string | string [ ] = m . property ;
250-
251- if ( ALIASES . hasOwnProperty ( properties ) ) {
252- properties = ALIASES [ properties ] ;
253- if ( typeof properties === 'function' ) {
254- properties = ( properties as ( a : string | null ) => string [ ] ) ( value as string | null ) ;
238+ // extract mixin
239+ if ( name . startsWith ( MIXIN_PREFIX ) ) {
240+ const pos = name . indexOf ( ':' ) ;
241+ let mixin , args ;
242+
243+ if ( pos < 0 ) {
244+ mixin = name . substr ( 1 ) ;
245+ args = [ ] ;
246+ } else {
247+ mixin = name . substr ( 1 , pos - 1 ) ;
248+ args = name . substr ( pos + 1 ) . split ( COMMA_DELIMITED ) . map ( trim ) ;
255249 }
256- }
257-
258- if ( ! Array . isArray ( properties ) ) {
259- properties = [ properties ] ;
260- }
261-
262- for ( const property of properties ) {
263- const name = PROPERTY_LIST . indexOf ( property ) ;
264250
265- if ( name < 0 ) {
266- continue ;
251+ if ( stack . indexOf ( mixin ) >= 0 ) {
252+ stack . push ( mixin ) ;
253+ throw new Error ( 'Recursive mixin detected: ' + stack . join ( '->' ) ) ;
267254 }
268255
269- const scope = m . scope || '' ;
270- const hash = ( ( ( name * base ) + media ) * base + state ) . toString ( 16 ) + ( scope ? `- ${ scope } ` : '' ) ;
271- const internalProperty = ( m . media ? m . media + '--' : '' ) + ( scope ? scope + '__' : '' ) + property + ( m . state ? '__' + m . state : '' ) ;
256+ stack . push ( mixin ) ;
257+ entries . push ( ... this . getResolvedProperties ( resolveMixin ( this . settings , mixin , args ) , stack ) )
258+ stack . pop ( ) ;
272259
273- yield {
274- name : ( m . media ? m . media + '|' : '' ) + ( scope ? scope + '!' : '' ) + property + ( m . state ? '.' + m . state : '' ) ,
275- property : HASH_VAR_PREFIX + internalProperty ,
276- entry : HASH_CLASS_PREFIX + '#' + hash ,
277- } ;
260+ continue ;
278261 }
262+
263+ entries . push ( name ) ;
279264 }
265+
266+ return entries ;
280267 }
281268
282269 private generateCSS ( info : PropertyInfo ) : void {
0 commit comments