11import * as createHash from "murmurhash-js/murmurhash3_gc" ;
22import isUnitlessNumber from "../utils/react/isUnitlessNumber" ;
33import { Theme } from "./getTheme" ;
4- import { CSSProperties } from "react" ;
54
65export const replace2Dashes = ( key : string ) => key . replace ( / [ A - Z ] / g, $1 => `-${ $1 . toLowerCase ( ) } ` ) ;
76export const getStyleValue = ( key : string , value : string ) => ( ( typeof value === "number" && ! isUnitlessNumber [ key ] ) ? `${ value } px` : value ) ;
@@ -21,6 +20,7 @@ export interface StyleClasses {
2120}
2221
2322export interface Sheet {
23+ rules ?: Map < string , boolean > ;
2424 CSSText ?: string ;
2525 className ?: string ;
2626 classNameWithHash ?: string ;
@@ -39,45 +39,56 @@ export interface StyleManagerConfig {
3939
4040export class StyleManager {
4141 prefixClassName : string ;
42- theme : Theme ;
4342 sheets : {
44- [ key : string ] : Sheet
43+ [ key : string ] : Sheet ;
4544 } = { } ;
46- CSSText : string = "" ;
45+ resultCSSText : string = "" ;
4746 addedCSSText : {
48- [ key : string ] : boolean ;
47+ [ key : string ] : {
48+ CSSText ?: string ;
49+ rules ?: Map < string , boolean > ;
50+ } ;
4951 } = { } ;
52+ allRules : Map < string , boolean > = new Map ( ) ;
5053 onAddCSSText ( CSSText ?: string ) { }
54+ onAddRules ( rules ?: Map < string , boolean > ) { }
5155 onRemoveCSSText ( CSSText ?: string ) { }
5256
5357 constructor ( config ?: StyleManagerConfig ) {
5458 const { prefixClassName } = config || { } ;
5559 this . prefixClassName = prefixClassName ? `${ prefixClassName } -` : "" ;
5660 }
5761
62+ setRules2allRules ( rules : Map < string , boolean > , rule : string ) {
63+ if ( this . allRules . get ( rule ) ) {
64+ rules . set ( rule , true ) ;
65+ } else {
66+ rules . set ( rule , false ) ;
67+ this . allRules . set ( rule , false )
68+ }
69+ }
70+
5871 cleanAllStyles ( ) {
5972 this . cleanSheets ( ) ;
6073 this . cleanCSSText ( ) ;
6174 }
6275
6376 cleanSheets = ( ) : void => {
64- this . theme = null ;
6577 this . sheets = { } ;
6678 }
6779
6880 cleanCSSText ( ) {
69- this . theme = null ;
7081 this . addedCSSText = { } ;
71- this . CSSText = "" ;
82+ this . resultCSSText = "" ;
7283 }
7384
7485 style2CSSText = ( style : React . CSSProperties ) : string => style ? Object . keys ( style ) . map ( key => (
75- ` ${ replace2Dashes ( key ) } : ${ getStyleValue ( key , style [ key ] ) } ;`
76- ) ) . join ( "\n " ) : void 0
86+ `${ replace2Dashes ( key ) } : ${ getStyleValue ( key , style [ key ] ) } ;`
87+ ) ) . join ( " " ) : void 0
7788
78- sheetsToString = ( ) => `\n ${ Object . keys ( this . sheets ) . map ( id => this . sheets [ id ] . CSSText ) . join ( "" ) } ` ;
89+ sheetsToString = ( ) => `${ Object . keys ( this . sheets ) . map ( id => this . sheets [ id ] . CSSText ) . join ( "" ) } ` ;
7990
80- getAllCSSText = ( ) => `${ this . sheetsToString ( ) } \n${ this . CSSText } ` ;
91+ getAllCSSText = ( ) => `${ this . sheetsToString ( ) } \n${ this . resultCSSText } ` ;
8192
8293 addStyle = ( style : CustomCSSProperties , className = "" ) : Sheet => {
8394 const id = createHash ( JSON . stringify ( style ) ) ;
@@ -88,27 +99,33 @@ export class StyleManager {
8899
89100 const classNameWithHash = `${ this . prefixClassName } ${ className } -${ id } ` ;
90101 let CSSText = "" ;
91- let contentCSSText = "" ;
92- let extendsCSSText = "" ;
102+ let currStyleText = "" ;
103+ let extendsRules = "" ;
104+ const rules = new Map < string , boolean > ( ) ;
93105
94106 for ( const styleKey in style ) {
95107 if ( extendsStyleKeys [ styleKey ] ) {
96108 const extendsStyle = style [ styleKey ] ;
97109 if ( extendsStyle ) {
98- extendsCSSText += `.${ classNameWithHash } ${ styleKey . slice ( 1 ) } {\n${ this . style2CSSText ( extendsStyle ) } \n}\n` ;
110+ const newRules = `.${ classNameWithHash } ${ styleKey . slice ( 1 ) } { ${ this . style2CSSText ( extendsStyle ) } }` ;
111+ this . setRules2allRules ( rules , newRules ) ;
112+ extendsRules += newRules ;
99113 }
100114 } else {
101115 if ( style [ styleKey ] !== void 0 ) {
102- contentCSSText += ` ${ replace2Dashes ( styleKey ) } : ${ getStyleValue ( styleKey , style [ styleKey ] ) } ;\n ` ;
116+ currStyleText += `${ replace2Dashes ( styleKey ) } : ${ getStyleValue ( styleKey , style [ styleKey ] ) } ; ` ;
103117 }
104118 }
105119 }
106120
107- CSSText += `.${ classNameWithHash } {\n${ contentCSSText } \n}\n` ;
108- CSSText += extendsCSSText ;
121+ const currRules = `.${ classNameWithHash } { ${ currStyleText } }` ;
122+ this . setRules2allRules ( rules , currRules ) ;
123+ CSSText += currRules ;
124+ CSSText += extendsRules ;
109125
110- this . sheets [ id ] = { CSSText, classNameWithHash, id, className } ;
111- this . onAddCSSText ( CSSText ) ;
126+ this . sheets [ id ] = { CSSText, classNameWithHash, id, className, rules } ;
127+ this . onAddCSSText ( currRules + extendsRules ) ;
128+ this . onAddRules ( rules ) ;
112129
113130 return this . sheets [ id ] ;
114131 }
@@ -117,26 +134,41 @@ export class StyleManager {
117134 const hash = createHash ( CSSText ) ;
118135 const shouldUpdate = ! this . addedCSSText [ hash ] ;
119136 if ( shouldUpdate ) {
120- this . addedCSSText [ hash ] = true ;
121- this . CSSText += CSSText ;
137+ this . resultCSSText += CSSText ;
138+ const textSize = CSSText . length ;
139+ let currRule = "" ;
140+ let leftBraces = 0 ;
141+ const rules = new Map < string , boolean > ( ) ;
142+ for ( let i = 0 ; i < textSize ; i ++ ) {
143+ const char = CSSText [ i ] ;
144+ currRule += char ;
145+ if ( char === "{" ) {
146+ leftBraces += 1 ;
147+ }
148+ if ( char === "}" ) {
149+ leftBraces -= 1 ;
150+ if ( leftBraces === 0 ) {
151+ this . setRules2allRules ( rules , currRule ) ;
152+ currRule = "" ;
153+ }
154+ }
155+ }
156+ this . addedCSSText [ hash ] = { CSSText, rules } ;
122157 this . onAddCSSText ( CSSText ) ;
158+ this . onAddRules ( rules ) ;
123159 }
124160 }
125161
126162 removeCSSText = ( CSSText : string ) => {
127163 const hash = createHash ( CSSText ) ;
128- this . addedCSSText [ hash ] = false ;
129- this . CSSText = this . CSSText . replace ( CSSText , "" ) ;
164+ delete this . addedCSSText [ hash ] ;
165+ this . resultCSSText = this . resultCSSText . replace ( CSSText , "" ) ;
130166 this . onRemoveCSSText ( CSSText ) ;
131167 }
132168
133- setStyleToManager ( config ?: {
134- style ?: CustomCSSProperties ;
135- className ?: string ;
136- } , callback ?: ( theme ?: Theme ) => StyleClasses ) : StyleClasses {
169+ setStyleToManager ( config ?: { style ?: CustomCSSProperties ; className ?: string ; } ) : StyleClasses {
137170 let newStyles : StyleClasses = { } ;
138171 let { style, className } = config || { } as StyleClasses ;
139- if ( callback ) style = callback ( this . theme ) as CustomCSSProperties ;
140172
141173 const { dynamicStyle, ...styleProperties } = style ;
142174 className = className || "" ;
@@ -149,18 +181,14 @@ export class StyleManager {
149181 return newStyles ;
150182 }
151183
152- setStylesToManager ( config ?: {
153- styles : { [ key : string ] : StyleClasses | CustomCSSProperties } ;
154- className ?: string ;
155- } , callback ?: ( theme ?: Theme ) => { [ key : string ] : StyleClasses } ) : { [ key : string ] : StyleClasses } {
184+ setStylesToManager ( config ?: { styles : { [ key : string ] : StyleClasses | CustomCSSProperties } ; className ?: string ; } ) : { [ key : string ] : StyleClasses } {
156185 const newStyles : {
157186 [ key : string ] : {
158187 className ?: string ;
159188 style ?: React . CSSProperties ;
160189 }
161190 } = { } ;
162191 let { className, styles } = config ;
163- if ( callback ) styles = callback ( this . theme ) ;
164192 className = className || "" ;
165193 const keys = Object . keys ( styles ) ;
166194
@@ -190,6 +218,47 @@ export class StyleManager {
190218
191219 return newStyles ;
192220 }
221+
222+ insertRule2el ( styleEl : HTMLStyleElement , rule : string , index ?: number ) {
223+ if ( rule && styleEl && ! this . allRules . get ( rule ) ) {
224+ const { sheet } = styleEl ;
225+ const rulesSize = sheet [ "rules" ] . size ;
226+
227+ try {
228+ if ( "insertRule" in sheet ) {
229+ ( sheet as any ) [ "insertRule" ] ( rule , index === void 0 ? rulesSize : index ) ;
230+ } else if ( "appendRule" in sheet ) {
231+ ( sheet as any ) [ "appendRule" ] ( rule ) ;
232+ } else {
233+ styleEl . textContent += rule ;
234+ }
235+ } catch ( error ) {
236+ console . error ( error ) ;
237+ }
238+ }
239+ }
240+
241+ inserAllRule2el ( styleEl : HTMLStyleElement ) {
242+ this . allRules . forEach ( ( inserted , rule ) => {
243+ if ( ! inserted ) {
244+ this . insertRule2el ( styleEl , rule ) ;
245+ this . allRules . set ( rule , true ) ;
246+ }
247+ } ) ;
248+ }
249+
250+ cleanRules ( styleEl : HTMLStyleElement ) {
251+ if ( styleEl ) {
252+ const { sheet } = styleEl ;
253+ const rules = sheet [ "rules" ] as any ;
254+ if ( rules && rules . length > 0 ) {
255+ for ( const rule of rules ) {
256+ ( sheet as any ) [ "deleteRule" ] ( rule ) ;
257+ }
258+ }
259+ this . cleanAllStyles ( ) ;
260+ }
261+ }
193262}
194263
195264export default StyleManager ;
0 commit comments