11import hash from '@emotion/hash' ;
2- import { ATTR_TOKEN , CSS_IN_JS_INSTANCE , CSS_IN_JS_INSTANCE_ID } from '../StyleContext' ;
2+ import { ATTR_TOKEN , CSS_IN_JS_INSTANCE , useStyleInject } from '../StyleContext' ;
33import type Theme from '../theme/Theme' ;
44import useGlobalCache from './useGlobalCache' ;
55import { flattenToken , token2key } from '../util' ;
@@ -12,7 +12,7 @@ const EMPTY_OVERRIDE = {};
1212// This helps developer not to do style override directly on the hash id.
1313const hashPrefix = process . env . NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css' ;
1414
15- export interface Option < DerivativeToken > {
15+ export interface Option < DerivativeToken , DesignToken > {
1616 /**
1717 * Generate token with salt.
1818 * This is used to generate different hashId even same derivative token for different version.
@@ -30,27 +30,41 @@ export interface Option<DerivativeToken> {
3030 * It's ok to useMemo outside but this has better cache strategy.
3131 */
3232 formatToken ?: ( mergedToken : any ) => DerivativeToken ;
33+ /**
34+ * Get final token with origin token, override token and theme.
35+ * The parameters do not contain formatToken since it's passed by user.
36+ * @param origin The original token.
37+ * @param override Extra tokens to override.
38+ * @param theme Theme instance. Could get derivative token by `theme.getDerivativeToken`
39+ */
40+ getComputedToken ?: (
41+ origin : DesignToken ,
42+ override : object ,
43+ theme : Theme < any , any > ,
44+ ) => DerivativeToken ;
3345}
3446
3547const tokenKeys = new Map < string , number > ( ) ;
3648function recordCleanToken ( tokenKey : string ) {
3749 tokenKeys . set ( tokenKey , ( tokenKeys . get ( tokenKey ) || 0 ) + 1 ) ;
3850}
3951
40- function removeStyleTags ( key : string ) {
52+ function removeStyleTags ( key : string , instanceId : string ) {
4153 if ( typeof document !== 'undefined' ) {
4254 const styles = document . querySelectorAll ( `style[${ ATTR_TOKEN } ="${ key } "]` ) ;
4355
4456 styles . forEach ( style => {
45- if ( ( style as any ) [ CSS_IN_JS_INSTANCE ] === CSS_IN_JS_INSTANCE_ID ) {
57+ if ( ( style as any ) [ CSS_IN_JS_INSTANCE ] === instanceId ) {
4658 style . parentNode ?. removeChild ( style ) ;
4759 }
4860 } ) ;
4961 }
5062}
5163
64+ const TOKEN_THRESHOLD = 0 ;
65+
5266// Remove will check current keys first
53- function cleanTokenStyle ( tokenKey : string ) {
67+ function cleanTokenStyle ( tokenKey : string , instanceId : string ) {
5468 tokenKeys . set ( tokenKey , ( tokenKeys . get ( tokenKey ) || 0 ) - 1 ) ;
5569
5670 const tokenKeyList = Array . from ( tokenKeys . keys ( ) ) ;
@@ -60,14 +74,37 @@ function cleanTokenStyle(tokenKey: string) {
6074 return count <= 0 ;
6175 } ) ;
6276
63- if ( cleanableKeyList . length < tokenKeyList . length ) {
77+ // Should keep tokens under threshold for not to insert style too often
78+ if ( tokenKeyList . length - cleanableKeyList . length > TOKEN_THRESHOLD ) {
6479 cleanableKeyList . forEach ( key => {
65- removeStyleTags ( key ) ;
80+ removeStyleTags ( key , instanceId ) ;
6681 tokenKeys . delete ( key ) ;
6782 } ) ;
6883 }
6984}
7085
86+ export const getComputedToken = < DerivativeToken = object , DesignToken = DerivativeToken > (
87+ originToken : DesignToken ,
88+ overrideToken : object ,
89+ theme : Theme < any , any > ,
90+ format ?: ( token : DesignToken ) => DerivativeToken ,
91+ ) => {
92+ const derivativeToken = theme . getDerivativeToken ( originToken ) ;
93+
94+ // Merge with override
95+ let mergedDerivativeToken = {
96+ ...derivativeToken ,
97+ ...overrideToken ,
98+ } ;
99+
100+ // Format if needed
101+ if ( format ) {
102+ mergedDerivativeToken = format ( mergedDerivativeToken ) ;
103+ }
104+
105+ return mergedDerivativeToken ;
106+ } ;
107+
71108/**
72109 * Cache theme derivative token as global shared one
73110 * @param theme Theme entity
@@ -78,8 +115,10 @@ function cleanTokenStyle(tokenKey: string) {
78115export default function useCacheToken < DerivativeToken = object , DesignToken = DerivativeToken > (
79116 theme : Ref < Theme < any , any > > ,
80117 tokens : Ref < Partial < DesignToken > [ ] > ,
81- option : Ref < Option < DerivativeToken > > = ref ( { } ) ,
118+ option : Ref < Option < DerivativeToken , DesignToken > > = ref ( { } ) ,
82119) {
120+ const style = useStyleInject ( ) ;
121+
83122 // Basic - We do basic cache here
84123 const mergedToken = computed ( ( ) => Object . assign ( { } , ...tokens . value ) ) ;
85124 const tokenStr = computed ( ( ) => flattenToken ( mergedToken . value ) ) ;
@@ -94,19 +133,15 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
94133 overrideTokenStr . value ,
95134 ] ) ,
96135 ( ) => {
97- const { salt = '' , override = EMPTY_OVERRIDE , formatToken } = option . value ;
98- const derivativeToken = theme . value . getDerivativeToken ( mergedToken . value ) ;
99-
100- // Merge with override
101- let mergedDerivativeToken = {
102- ...derivativeToken ,
103- ...override ,
104- } ;
105-
106- // Format if needed
107- if ( formatToken ) {
108- mergedDerivativeToken = formatToken ( mergedDerivativeToken ) ;
109- }
136+ const {
137+ salt = '' ,
138+ override = EMPTY_OVERRIDE ,
139+ formatToken,
140+ getComputedToken : compute ,
141+ } = option . value ;
142+ const mergedDerivativeToken = compute
143+ ? compute ( mergedToken . value , override , theme . value )
144+ : getComputedToken ( mergedToken . value , override , theme . value , formatToken ) ;
110145
111146 // Optimize for `useStyleRegister` performance
112147 const tokenKey = token2key ( mergedDerivativeToken , salt ) ;
@@ -120,7 +155,7 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
120155 } ,
121156 cache => {
122157 // Remove token will remove all related style
123- cleanTokenStyle ( cache [ 0 ] . _tokenKey ) ;
158+ cleanTokenStyle ( cache [ 0 ] . _tokenKey , style . value ?. cache . instanceId ) ;
124159 } ,
125160 ) ;
126161
0 commit comments