chore: update cssinjs
parent
2b8f2adcbf
commit
d0f7c34497
|
@ -1,16 +1,20 @@
|
|||
export type KeyType = string | number;
|
||||
type ValueType = [number, any]; // [times, realValue]
|
||||
|
||||
const SPLIT = '%';
|
||||
class Entity {
|
||||
instanceId: string;
|
||||
constructor(instanceId: string) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
/** @private Internal cache map. Do not access this directly */
|
||||
cache = new Map<string, ValueType>();
|
||||
|
||||
get(keys: KeyType[] | string): ValueType | null {
|
||||
return this.cache.get(Array.isArray(keys) ? keys.join('%') : keys) || null;
|
||||
return this.cache.get(Array.isArray(keys) ? keys.join(SPLIT) : keys) || null;
|
||||
}
|
||||
|
||||
update(keys: KeyType[] | string, valueFn: (origin: ValueType | null) => ValueType | null) {
|
||||
const path = Array.isArray(keys) ? keys.join('%') : keys;
|
||||
const path = Array.isArray(keys) ? keys.join(SPLIT) : keys;
|
||||
const prevValue = this.cache.get(path)!;
|
||||
const nextValue = valueFn(prevValue);
|
||||
|
||||
|
|
|
@ -7,20 +7,22 @@ import { arrayType, booleanType, objectType, someType, stringType, withInstall }
|
|||
import initDefaultProps from '../props-util/initDefaultProps';
|
||||
export const ATTR_TOKEN = 'data-token-hash';
|
||||
export const ATTR_MARK = 'data-css-hash';
|
||||
export const ATTR_DEV_CACHE_PATH = 'data-dev-cache-path';
|
||||
export const ATTR_CACHE_PATH = 'data-cache-path';
|
||||
|
||||
// Mark css-in-js instance in style element
|
||||
export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
|
||||
export const CSS_IN_JS_INSTANCE_ID = Math.random().toString(12).slice(2);
|
||||
|
||||
export function createCache() {
|
||||
const cssinjsInstanceId = Math.random().toString(12).slice(2);
|
||||
|
||||
// Tricky SSR: Move all inline style to the head.
|
||||
// PS: We do not recommend tricky mode.
|
||||
if (typeof document !== 'undefined' && document.head && document.body) {
|
||||
const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
|
||||
const { firstChild } = document.head;
|
||||
|
||||
Array.from(styles).forEach(style => {
|
||||
(style as any)[CSS_IN_JS_INSTANCE] =
|
||||
(style as any)[CSS_IN_JS_INSTANCE] || CSS_IN_JS_INSTANCE_ID;
|
||||
(style as any)[CSS_IN_JS_INSTANCE] = (style as any)[CSS_IN_JS_INSTANCE] || cssinjsInstanceId;
|
||||
|
||||
// Not force move if no head
|
||||
document.head.insertBefore(style, firstChild);
|
||||
|
@ -31,7 +33,7 @@ export function createCache() {
|
|||
Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
|
||||
const hash = style.getAttribute(ATTR_MARK)!;
|
||||
if (styleHash[hash]) {
|
||||
if ((style as any)[CSS_IN_JS_INSTANCE] === CSS_IN_JS_INSTANCE_ID) {
|
||||
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
|
||||
style.parentNode?.removeChild(style);
|
||||
}
|
||||
} else {
|
||||
|
@ -40,7 +42,7 @@ export function createCache() {
|
|||
});
|
||||
}
|
||||
|
||||
return new CacheEntity();
|
||||
return new CacheEntity(cssinjsInstanceId);
|
||||
}
|
||||
|
||||
export type HashPriority = 'low' | 'high';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import hash from '@emotion/hash';
|
||||
import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, CSS_IN_JS_INSTANCE_ID } from '../StyleContext';
|
||||
import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
|
||||
import type Theme from '../theme/Theme';
|
||||
import useGlobalCache from './useGlobalCache';
|
||||
import { flattenToken, token2key } from '../util';
|
||||
|
@ -12,7 +12,7 @@ const EMPTY_OVERRIDE = {};
|
|||
// This helps developer not to do style override directly on the hash id.
|
||||
const hashPrefix = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css';
|
||||
|
||||
export interface Option<DerivativeToken> {
|
||||
export interface Option<DerivativeToken, DesignToken> {
|
||||
/**
|
||||
* Generate token with salt.
|
||||
* This is used to generate different hashId even same derivative token for different version.
|
||||
|
@ -30,6 +30,18 @@ export interface Option<DerivativeToken> {
|
|||
* It's ok to useMemo outside but this has better cache strategy.
|
||||
*/
|
||||
formatToken?: (mergedToken: any) => DerivativeToken;
|
||||
/**
|
||||
* Get final token with origin token, override token and theme.
|
||||
* The parameters do not contain formatToken since it's passed by user.
|
||||
* @param origin The original token.
|
||||
* @param override Extra tokens to override.
|
||||
* @param theme Theme instance. Could get derivative token by `theme.getDerivativeToken`
|
||||
*/
|
||||
getComputedToken?: (
|
||||
origin: DesignToken,
|
||||
override: object,
|
||||
theme: Theme<any, any>,
|
||||
) => DerivativeToken;
|
||||
}
|
||||
|
||||
const tokenKeys = new Map<string, number>();
|
||||
|
@ -37,20 +49,22 @@ function recordCleanToken(tokenKey: string) {
|
|||
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
|
||||
}
|
||||
|
||||
function removeStyleTags(key: string) {
|
||||
function removeStyleTags(key: string, instanceId: string) {
|
||||
if (typeof document !== 'undefined') {
|
||||
const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`);
|
||||
|
||||
styles.forEach(style => {
|
||||
if ((style as any)[CSS_IN_JS_INSTANCE] === CSS_IN_JS_INSTANCE_ID) {
|
||||
if ((style as any)[CSS_IN_JS_INSTANCE] === instanceId) {
|
||||
style.parentNode?.removeChild(style);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const TOKEN_THRESHOLD = 0;
|
||||
|
||||
// Remove will check current keys first
|
||||
function cleanTokenStyle(tokenKey: string) {
|
||||
function cleanTokenStyle(tokenKey: string, instanceId: string) {
|
||||
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
|
||||
|
||||
const tokenKeyList = Array.from(tokenKeys.keys());
|
||||
|
@ -60,14 +74,37 @@ function cleanTokenStyle(tokenKey: string) {
|
|||
return count <= 0;
|
||||
});
|
||||
|
||||
if (cleanableKeyList.length < tokenKeyList.length) {
|
||||
// Should keep tokens under threshold for not to insert style too often
|
||||
if (tokenKeyList.length - cleanableKeyList.length > TOKEN_THRESHOLD) {
|
||||
cleanableKeyList.forEach(key => {
|
||||
removeStyleTags(key);
|
||||
removeStyleTags(key, instanceId);
|
||||
tokenKeys.delete(key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const getComputedToken = <DerivativeToken = object, DesignToken = DerivativeToken>(
|
||||
originToken: DesignToken,
|
||||
overrideToken: object,
|
||||
theme: Theme<any, any>,
|
||||
format?: (token: DesignToken) => DerivativeToken,
|
||||
) => {
|
||||
const derivativeToken = theme.getDerivativeToken(originToken);
|
||||
|
||||
// Merge with override
|
||||
let mergedDerivativeToken = {
|
||||
...derivativeToken,
|
||||
...overrideToken,
|
||||
};
|
||||
|
||||
// Format if needed
|
||||
if (format) {
|
||||
mergedDerivativeToken = format(mergedDerivativeToken);
|
||||
}
|
||||
|
||||
return mergedDerivativeToken;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache theme derivative token as global shared one
|
||||
* @param theme Theme entity
|
||||
|
@ -78,8 +115,10 @@ function cleanTokenStyle(tokenKey: string) {
|
|||
export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>(
|
||||
theme: Ref<Theme<any, any>>,
|
||||
tokens: Ref<Partial<DesignToken>[]>,
|
||||
option: Ref<Option<DerivativeToken>> = ref({}),
|
||||
option: Ref<Option<DerivativeToken, DesignToken>> = ref({}),
|
||||
) {
|
||||
const style = useStyleInject();
|
||||
|
||||
// Basic - We do basic cache here
|
||||
const mergedToken = computed(() => Object.assign({}, ...tokens.value));
|
||||
const tokenStr = computed(() => flattenToken(mergedToken.value));
|
||||
|
@ -94,19 +133,15 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
|
|||
overrideTokenStr.value,
|
||||
]),
|
||||
() => {
|
||||
const { salt = '', override = EMPTY_OVERRIDE, formatToken } = option.value;
|
||||
const derivativeToken = theme.value.getDerivativeToken(mergedToken.value);
|
||||
|
||||
// Merge with override
|
||||
let mergedDerivativeToken = {
|
||||
...derivativeToken,
|
||||
...override,
|
||||
};
|
||||
|
||||
// Format if needed
|
||||
if (formatToken) {
|
||||
mergedDerivativeToken = formatToken(mergedDerivativeToken);
|
||||
}
|
||||
const {
|
||||
salt = '',
|
||||
override = EMPTY_OVERRIDE,
|
||||
formatToken,
|
||||
getComputedToken: compute,
|
||||
} = option.value;
|
||||
const mergedDerivativeToken = compute
|
||||
? compute(mergedToken.value, override, theme.value)
|
||||
: getComputedToken(mergedToken.value, override, theme.value, formatToken);
|
||||
|
||||
// Optimize for `useStyleRegister` performance
|
||||
const tokenKey = token2key(mergedDerivativeToken, salt);
|
||||
|
@ -120,7 +155,7 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
|
|||
},
|
||||
cache => {
|
||||
// Remove token will remove all related style
|
||||
cleanTokenStyle(cache[0]._tokenKey);
|
||||
cleanTokenStyle(cache[0]._tokenKey, style.value?.cache.instanceId);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ if (
|
|||
process.env.NODE_ENV !== 'production' &&
|
||||
typeof module !== 'undefined' &&
|
||||
module &&
|
||||
(module as any).hot
|
||||
(module as any).hot &&
|
||||
typeof window !== 'undefined'
|
||||
) {
|
||||
const win = window as any;
|
||||
if (typeof win.webpackHotUpdate === 'function') {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import canUseDom from '../../../../_util/canUseDom';
|
||||
import { ATTR_MARK } from '../../StyleContext';
|
||||
|
||||
export const ATTR_CACHE_MAP = 'data-ant-cssinjs-cache-path';
|
||||
|
||||
/**
|
||||
* This marks style from the css file.
|
||||
* Which means not exist in `<style />` tag.
|
||||
*/
|
||||
export const CSS_FILE_STYLE = '_FILE_STYLE__';
|
||||
|
||||
export function serialize(cachePathMap: Record<string, string>) {
|
||||
return Object.keys(cachePathMap)
|
||||
.map(path => {
|
||||
const hash = cachePathMap[path];
|
||||
return `${path}:${hash}`;
|
||||
})
|
||||
.join(';');
|
||||
}
|
||||
|
||||
let cachePathMap: Record<string, string>;
|
||||
let fromCSSFile = true;
|
||||
|
||||
/**
|
||||
* @private Test usage only. Can save remove if no need.
|
||||
*/
|
||||
export function reset(mockCache?: Record<string, string>, fromFile = true) {
|
||||
cachePathMap = mockCache!;
|
||||
fromCSSFile = fromFile;
|
||||
}
|
||||
|
||||
export function prepare() {
|
||||
if (!cachePathMap) {
|
||||
cachePathMap = {};
|
||||
|
||||
if (canUseDom()) {
|
||||
const div = document.createElement('div');
|
||||
div.className = ATTR_CACHE_MAP;
|
||||
div.style.position = 'fixed';
|
||||
div.style.visibility = 'hidden';
|
||||
div.style.top = '-9999px';
|
||||
document.body.appendChild(div);
|
||||
|
||||
let content = getComputedStyle(div).content || '';
|
||||
content = content.replace(/^"/, '').replace(/"$/, '');
|
||||
|
||||
// Fill data
|
||||
content.split(';').forEach(item => {
|
||||
const [path, hash] = item.split(':');
|
||||
cachePathMap[path] = hash;
|
||||
});
|
||||
|
||||
// Remove inline record style
|
||||
const inlineMapStyle = document.querySelector(`style[${ATTR_CACHE_MAP}]`);
|
||||
if (inlineMapStyle) {
|
||||
fromCSSFile = false;
|
||||
inlineMapStyle.parentNode?.removeChild(inlineMapStyle);
|
||||
}
|
||||
|
||||
document.body.removeChild(div);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function existPath(path: string) {
|
||||
prepare();
|
||||
|
||||
return !!cachePathMap[path];
|
||||
}
|
||||
|
||||
export function getStyleAndHash(path: string): [style: string | null, hash: string] {
|
||||
const hash = cachePathMap[path];
|
||||
let styleStr: string | null = null;
|
||||
|
||||
if (hash && canUseDom()) {
|
||||
if (fromCSSFile) {
|
||||
styleStr = CSS_FILE_STYLE;
|
||||
} else {
|
||||
const style = document.querySelector(`style[${ATTR_MARK}="${cachePathMap[path]}"]`);
|
||||
|
||||
if (style) {
|
||||
styleStr = style.innerHTML;
|
||||
} else {
|
||||
// Clean up since not exist anymore
|
||||
delete cachePathMap[path];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [styleStr, hash];
|
||||
}
|
|
@ -3,27 +3,33 @@ import type * as CSS from 'csstype';
|
|||
// @ts-ignore
|
||||
import unitless from '@emotion/unitless';
|
||||
import { compile, serialize, stringify } from 'stylis';
|
||||
import type { Theme, Transformer } from '..';
|
||||
import type Cache from '../Cache';
|
||||
import type Keyframes from '../Keyframes';
|
||||
import type { Linter } from '../linters';
|
||||
import { contentQuotesLinter, hashedAnimationLinter } from '../linters';
|
||||
import type { HashPriority } from '../StyleContext';
|
||||
import type { Theme, Transformer } from '../..';
|
||||
import type Cache from '../../Cache';
|
||||
import type Keyframes from '../../Keyframes';
|
||||
import type { Linter } from '../../linters';
|
||||
import { contentQuotesLinter, hashedAnimationLinter } from '../../linters';
|
||||
import type { HashPriority } from '../../StyleContext';
|
||||
import {
|
||||
useStyleInject,
|
||||
ATTR_DEV_CACHE_PATH,
|
||||
ATTR_CACHE_PATH,
|
||||
ATTR_MARK,
|
||||
ATTR_TOKEN,
|
||||
CSS_IN_JS_INSTANCE,
|
||||
CSS_IN_JS_INSTANCE_ID,
|
||||
} from '../StyleContext';
|
||||
import { supportLayer } from '../util';
|
||||
import useGlobalCache from './useGlobalCache';
|
||||
import canUseDom from '../../canUseDom';
|
||||
import { removeCSS, updateCSS } from '../../../vc-util/Dom/dynamicCSS';
|
||||
} from '../../StyleContext';
|
||||
import { supportLayer } from '../../util';
|
||||
import useGlobalCache from '../useGlobalCache';
|
||||
import { removeCSS, updateCSS } from '../../../../vc-util/Dom/dynamicCSS';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import type { VueNode } from '../../type';
|
||||
import type { VueNode } from '../../../type';
|
||||
import canUseDom from '../../../../_util/canUseDom';
|
||||
|
||||
import {
|
||||
ATTR_CACHE_MAP,
|
||||
existPath,
|
||||
getStyleAndHash,
|
||||
serialize as serializeCacheMap,
|
||||
} from './cacheMapUtil';
|
||||
|
||||
const isClientSide = canUseDom();
|
||||
|
||||
|
@ -60,7 +66,7 @@ export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSS
|
|||
// == Parser ==
|
||||
// ============================================================================
|
||||
// Preprocessor style content to browser support one
|
||||
export function normalizeStyle(styleStr: string) {
|
||||
export function normalizeStyle(styleStr: string): string {
|
||||
const serialized = serialize(compile(styleStr), stringify);
|
||||
return serialized.replace(/\{%%%\:[^;];}/g, ';');
|
||||
}
|
||||
|
@ -307,6 +313,14 @@ export default function useStyleRegister(
|
|||
path: string[];
|
||||
hashId?: string;
|
||||
layer?: string;
|
||||
nonce?: string | (() => string);
|
||||
clientOnly?: boolean;
|
||||
/**
|
||||
* Tell cssinjs the insert order of style.
|
||||
* It's useful when you need to insert style
|
||||
* before other style to overwrite for the same selector priority.
|
||||
*/
|
||||
order?: number;
|
||||
}>,
|
||||
styleFn: () => CSSInterpolation,
|
||||
) {
|
||||
|
@ -323,14 +337,32 @@ export default function useStyleRegister(
|
|||
}
|
||||
|
||||
// const [cacheStyle[0], cacheStyle[1], cacheStyle[2]]
|
||||
useGlobalCache(
|
||||
useGlobalCache<
|
||||
[
|
||||
styleStr: string,
|
||||
tokenKey: string,
|
||||
styleId: string,
|
||||
effectStyle: Record<string, string>,
|
||||
clientOnly: boolean | undefined,
|
||||
order: number,
|
||||
]
|
||||
>(
|
||||
'style',
|
||||
fullPath,
|
||||
// Create cache if needed
|
||||
() => {
|
||||
const { path, hashId, layer, nonce, clientOnly, order = 0 } = info.value;
|
||||
const cachePath = fullPath.value.join('|');
|
||||
// Get style from SSR inline style directly
|
||||
if (existPath(cachePath)) {
|
||||
const [inlineCacheStyleStr, styleHash] = getStyleAndHash(cachePath);
|
||||
if (inlineCacheStyleStr) {
|
||||
return [inlineCacheStyleStr, tokenKey.value, styleHash, {}, clientOnly, order];
|
||||
}
|
||||
}
|
||||
const styleObj = styleFn();
|
||||
const { hashPriority, container, transformers, linters } = styleContext.value;
|
||||
const { path, hashId, layer } = info.value;
|
||||
const { hashPriority, container, transformers, linters, cache } = styleContext.value;
|
||||
|
||||
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
|
||||
hashId,
|
||||
hashPriority,
|
||||
|
@ -343,20 +375,29 @@ export default function useStyleRegister(
|
|||
const styleId = uniqueHash(fullPath.value, styleStr);
|
||||
|
||||
if (isMergedClientSide) {
|
||||
const style = updateCSS(styleStr, styleId, {
|
||||
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = {
|
||||
mark: ATTR_MARK,
|
||||
prepend: 'queue',
|
||||
attachTo: container,
|
||||
});
|
||||
priority: order,
|
||||
};
|
||||
|
||||
(style as any)[CSS_IN_JS_INSTANCE] = CSS_IN_JS_INSTANCE_ID;
|
||||
const nonceStr = typeof nonce === 'function' ? nonce() : nonce;
|
||||
|
||||
if (nonceStr) {
|
||||
mergedCSSConfig.csp = { nonce: nonceStr };
|
||||
}
|
||||
|
||||
const style = updateCSS(styleStr, styleId, mergedCSSConfig);
|
||||
|
||||
(style as any)[CSS_IN_JS_INSTANCE] = cache.instanceId;
|
||||
|
||||
// Used for `useCacheToken` to remove on batch when token removed
|
||||
style.setAttribute(ATTR_TOKEN, tokenKey.value);
|
||||
|
||||
// Dev usage to find which cache path made this easily
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
style.setAttribute(ATTR_DEV_CACHE_PATH, fullPath.value.join('|'));
|
||||
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|'));
|
||||
}
|
||||
|
||||
// Inject client side effect style
|
||||
|
@ -374,7 +415,7 @@ export default function useStyleRegister(
|
|||
});
|
||||
}
|
||||
|
||||
return [styleStr, tokenKey.value, styleId];
|
||||
return [styleStr, tokenKey.value, styleId, effectStyle, clientOnly, order];
|
||||
},
|
||||
// Remove cache if no need
|
||||
([, , styleId], fromHMR) => {
|
||||
|
@ -414,20 +455,112 @@ export default function useStyleRegister(
|
|||
// == SSR ==
|
||||
// ============================================================================
|
||||
export function extractStyle(cache: Cache, plain = false) {
|
||||
// prefix with `style` is used for `useStyleRegister` to cache style context
|
||||
const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith('style%'));
|
||||
const matchPrefix = `style%`;
|
||||
|
||||
// const tokenStyles: Record<string, string[]> = {};
|
||||
// prefix with `style` is used for `useStyleRegister` to cache style context
|
||||
const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith(matchPrefix));
|
||||
|
||||
// Common effect styles like animation
|
||||
const effectStyles: Record<string, boolean> = {};
|
||||
|
||||
// Mapping of cachePath to style hash
|
||||
const cachePathMap: Record<string, string> = {};
|
||||
|
||||
let styleText = '';
|
||||
|
||||
styleKeys.forEach(key => {
|
||||
const [styleStr, tokenKey, styleId]: [string, string, string] = cache.cache.get(key)![1];
|
||||
function toStyleStr(
|
||||
style: string,
|
||||
tokenKey?: string,
|
||||
styleId?: string,
|
||||
customizeAttrs: Record<string, string> = {},
|
||||
) {
|
||||
const attrs: Record<string, string | undefined> = {
|
||||
...customizeAttrs,
|
||||
[ATTR_TOKEN]: tokenKey,
|
||||
[ATTR_MARK]: styleId,
|
||||
};
|
||||
|
||||
styleText += plain
|
||||
? styleStr
|
||||
: `<style ${ATTR_TOKEN}="${tokenKey}" ${ATTR_MARK}="${styleId}">${styleStr}</style>`;
|
||||
const attrStr = Object.keys(attrs)
|
||||
.map(attr => {
|
||||
const val = attrs[attr];
|
||||
return val ? `${attr}="${val}"` : null;
|
||||
})
|
||||
.filter(v => v)
|
||||
.join(' ');
|
||||
|
||||
return plain ? style : `<style ${attrStr}>${style}</style>`;
|
||||
}
|
||||
|
||||
// ====================== Fill Style ======================
|
||||
type OrderStyle = [order: number, style: string];
|
||||
|
||||
const orderStyles: OrderStyle[] = styleKeys
|
||||
.map(key => {
|
||||
const cachePath = key.slice(matchPrefix.length).replace(/%/g, '|');
|
||||
|
||||
const [styleStr, tokenKey, styleId, effectStyle, clientOnly, order]: [
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
Record<string, string>,
|
||||
boolean,
|
||||
number,
|
||||
] = cache.cache.get(key)![1];
|
||||
|
||||
// Skip client only style
|
||||
if (clientOnly) {
|
||||
return null! as OrderStyle;
|
||||
}
|
||||
|
||||
// ====================== Style ======================
|
||||
// Used for vc-util
|
||||
const sharedAttrs = {
|
||||
'data-vc-order': 'prependQueue',
|
||||
'data-vc-priority': `${order}`,
|
||||
};
|
||||
|
||||
let keyStyleText = toStyleStr(styleStr, tokenKey, styleId, sharedAttrs);
|
||||
|
||||
// Save cache path with hash mapping
|
||||
cachePathMap[cachePath] = styleId;
|
||||
|
||||
// =============== Create effect style ===============
|
||||
if (effectStyle) {
|
||||
Object.keys(effectStyle).forEach(effectKey => {
|
||||
// Effect style can be reused
|
||||
if (!effectStyles[effectKey]) {
|
||||
effectStyles[effectKey] = true;
|
||||
keyStyleText += toStyleStr(
|
||||
normalizeStyle(effectStyle[effectKey]),
|
||||
tokenKey,
|
||||
`_effect-${effectKey}`,
|
||||
sharedAttrs,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const ret: OrderStyle = [order, keyStyleText];
|
||||
|
||||
return ret;
|
||||
})
|
||||
.filter(o => o);
|
||||
|
||||
orderStyles
|
||||
.sort((o1, o2) => o1[0] - o2[0])
|
||||
.forEach(([, style]) => {
|
||||
styleText += style;
|
||||
});
|
||||
|
||||
// ==================== Fill Cache Path ====================
|
||||
styleText += toStyleStr(
|
||||
`.${ATTR_CACHE_MAP}{content:"${serializeCacheMap(cachePathMap)}";}`,
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
[ATTR_CACHE_MAP]: ATTR_CACHE_MAP,
|
||||
},
|
||||
);
|
||||
|
||||
return styleText;
|
||||
}
|
|
@ -11,6 +11,8 @@ import { createTheme, Theme } from './theme';
|
|||
import type { Transformer } from './transformers/interface';
|
||||
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
|
||||
import px2remTransformer from './transformers/px2rem';
|
||||
import { supportLogicProps, supportWhere } from './util';
|
||||
|
||||
const cssinjs = {
|
||||
Theme,
|
||||
createTheme,
|
||||
|
@ -68,4 +70,8 @@ export type {
|
|||
StyleProviderProps,
|
||||
};
|
||||
|
||||
export const _experimental = {
|
||||
supportModernCSS: () => supportWhere() && supportLogicProps(),
|
||||
};
|
||||
|
||||
export default cssinjs;
|
||||
|
|
|
@ -2,17 +2,30 @@ import hash from '@emotion/hash';
|
|||
import { removeCSS, updateCSS } from '../../vc-util/Dom/dynamicCSS';
|
||||
import canUseDom from '../canUseDom';
|
||||
|
||||
import { Theme } from './theme';
|
||||
|
||||
// Create a cache here to avoid always loop generate
|
||||
const flattenTokenCache = new WeakMap<any, string>();
|
||||
|
||||
export function flattenToken(token: any) {
|
||||
let str = '';
|
||||
let str = flattenTokenCache.get(token) || '';
|
||||
|
||||
if (!str) {
|
||||
Object.keys(token).forEach(key => {
|
||||
const value = token[key];
|
||||
str += key;
|
||||
if (value && typeof value === 'object') {
|
||||
if (value instanceof Theme) {
|
||||
str += value.id;
|
||||
} else if (value && typeof value === 'object') {
|
||||
str += flattenToken(value);
|
||||
} else {
|
||||
str += value;
|
||||
}
|
||||
});
|
||||
|
||||
// Put in cache
|
||||
flattenTokenCache.set(token, str);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -23,12 +36,18 @@ export function token2key(token: any, salt: string): string {
|
|||
return hash(`${salt}_${flattenToken(token)}`);
|
||||
}
|
||||
|
||||
const layerKey = `layer-${Date.now()}-${Math.random()}`.replace(/\./g, '');
|
||||
const layerWidth = '903px';
|
||||
const randomSelectorKey = `random-${Date.now()}-${Math.random()}`.replace(/\./g, '');
|
||||
|
||||
function supportSelector(styleStr: string, handleElement?: (ele: HTMLElement) => void): boolean {
|
||||
// Magic `content` for detect selector support
|
||||
const checkContent = '_bAmBoO_';
|
||||
|
||||
function supportSelector(
|
||||
styleStr: string,
|
||||
handleElement: (ele: HTMLElement) => void,
|
||||
supportCheck?: (ele: HTMLElement) => boolean,
|
||||
): boolean {
|
||||
if (canUseDom()) {
|
||||
updateCSS(styleStr, layerKey);
|
||||
updateCSS(styleStr, randomSelectorKey);
|
||||
|
||||
const ele = document.createElement('div');
|
||||
ele.style.position = 'fixed';
|
||||
|
@ -42,10 +61,12 @@ function supportSelector(styleStr: string, handleElement?: (ele: HTMLElement) =>
|
|||
ele.style.zIndex = '9999999';
|
||||
}
|
||||
|
||||
const support = getComputedStyle(ele).width === layerWidth;
|
||||
const support = supportCheck
|
||||
? supportCheck(ele)
|
||||
: getComputedStyle(ele).content?.includes(checkContent);
|
||||
|
||||
ele.parentNode?.removeChild(ele);
|
||||
removeCSS(layerKey);
|
||||
removeCSS(randomSelectorKey);
|
||||
|
||||
return support;
|
||||
}
|
||||
|
@ -57,12 +78,41 @@ let canLayer: boolean | undefined = undefined;
|
|||
export function supportLayer(): boolean {
|
||||
if (canLayer === undefined) {
|
||||
canLayer = supportSelector(
|
||||
`@layer ${layerKey} { .${layerKey} { width: ${layerWidth}!important; } }`,
|
||||
`@layer ${randomSelectorKey} { .${randomSelectorKey} { content: "${checkContent}"!important; } }`,
|
||||
ele => {
|
||||
ele.className = layerKey;
|
||||
ele.className = randomSelectorKey;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return canLayer!;
|
||||
}
|
||||
|
||||
let canWhere: boolean | undefined = undefined;
|
||||
export function supportWhere(): boolean {
|
||||
if (canWhere === undefined) {
|
||||
canWhere = supportSelector(
|
||||
`:where(.${randomSelectorKey}) { content: "${checkContent}"!important; }`,
|
||||
ele => {
|
||||
ele.className = randomSelectorKey;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return canWhere!;
|
||||
}
|
||||
|
||||
let canLogic: boolean | undefined = undefined;
|
||||
export function supportLogicProps(): boolean {
|
||||
if (canLogic === undefined) {
|
||||
canLogic = supportSelector(
|
||||
`.${randomSelectorKey} { inset-block: 93px !important; }`,
|
||||
ele => {
|
||||
ele.className = randomSelectorKey;
|
||||
},
|
||||
ele => getComputedStyle(ele).bottom === '93px',
|
||||
);
|
||||
}
|
||||
|
||||
return canLogic!;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ interface Options {
|
|||
csp?: { nonce?: string };
|
||||
prepend?: Prepend;
|
||||
mark?: string;
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
function getMark({ mark }: Options = {}) {
|
||||
|
|
Loading…
Reference in New Issue