Skip to content

feat(style): 添加 layer 属性以避免全局样式冲突 #8130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions components/_util/cssinjs/StyleContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export interface StyleContextProps {
* Please note that `linters` do not support dynamic update.
*/
linters?: Linter[];
/** Wrap css in a layer to avoid global style conflict */
layer?: boolean;
}

const StyleContextKey: InjectionKey<ShallowRef<Partial<StyleContextProps>>> =
Expand Down Expand Up @@ -174,6 +176,8 @@ export const styleProviderProps = () => ({
* Please note that `linters` do not support dynamic update.
*/
linters: arrayType<Linter[]>(),
/** Wrap css in a layer to avoid global style conflict */
layer: booleanType(),
});
export type StyleProviderProps = Partial<ExtractPropTypes<ReturnType<typeof styleProviderProps>>>;
export const StyleProvider = withInstall(
Expand Down
99 changes: 72 additions & 27 deletions components/_util/cssinjs/hooks/useStyleRegister/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ const isClientSide = canUseDom();

const SKIP_CHECK = '_skip_check_';
const MULTI_VALUE = '_multi_value_';

export interface LayerConfig {
name: string;
dependencies?: string[];
}

export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
};
Expand Down Expand Up @@ -102,7 +108,7 @@ function injectSelectorHash(key: string, hashId: string, hashPriority?: HashPrio
export interface ParseConfig {
hashId?: string;
hashPriority?: HashPriority;
layer?: string;
layer?: LayerConfig;
path?: string;
transformers?: Transformer[];
linters?: Linter[];
Expand Down Expand Up @@ -278,14 +284,14 @@ export const parseStyle = (
if (!root) {
styleStr = `{${styleStr}}`;
} else if (layer && supportLayer()) {
const layerCells = layer.split(',');
const layerName = layerCells[layerCells.length - 1].trim();
styleStr = `@layer ${layerName} {${styleStr}}`;

// Order of layer if needed
if (layerCells.length > 1) {
// zombieJ: stylis do not support layer order, so we need to handle it manually.
styleStr = `@layer ${layer}{%%%:%}${styleStr}`;
if (styleStr) {
styleStr = `@layer ${layer.name} {${styleStr}}`;
}

if (layer.dependencies) {
effectStyle[`@layer ${layer.name}`] = layer.dependencies
.map(deps => `@layer ${deps}, ${layer.name};`)
.join('\n');
}
}

Expand All @@ -312,7 +318,7 @@ export default function useStyleRegister(
token: any;
path: string[];
hashId?: string;
layer?: string;
layer?: LayerConfig;
nonce?: string | (() => string);
clientOnly?: boolean;
/**
Expand All @@ -328,7 +334,11 @@ export default function useStyleRegister(

const tokenKey = computed(() => info.value.token._tokenKey as string);

const fullPath = computed(() => [tokenKey.value, ...info.value.path]);
const fullPath = computed(() => [
tokenKey.value,
...(styleContext.value.layer ? ['layer'] : []),
...info.value.path,
]);

// Check if need insert style
let isMergedClientSide = isClientSide;
Expand Down Expand Up @@ -361,12 +371,19 @@ export default function useStyleRegister(
}
}
const styleObj = styleFn();
const { hashPriority, container, transformers, linters, cache } = styleContext.value;
const {
hashPriority,
container,
transformers,
linters,
cache,
layer: enableLayer,
} = styleContext.value;

const [parsedStyle, effectStyle] = parseStyle(styleObj, {
hashId,
hashPriority,
layer,
layer: enableLayer ? layer : undefined,
path: path.join('-'),
transformers,
linters,
Expand All @@ -377,7 +394,7 @@ export default function useStyleRegister(
if (isMergedClientSide) {
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = {
mark: ATTR_MARK,
prepend: 'queue',
prepend: enableLayer ? false : 'queue',
attachTo: container,
priority: order,
};
Expand All @@ -388,6 +405,30 @@ export default function useStyleRegister(
mergedCSSConfig.csp = { nonce: nonceStr };
}

// ================= Split Effect Style =================
// We will split effectStyle here since @layer should be at the top level
const effectLayerKeys: string[] = [];
const effectRestKeys: string[] = [];

Object.keys(effectStyle).forEach(key => {
if (key.startsWith('@layer')) {
effectLayerKeys.push(key);
} else {
effectRestKeys.push(key);
}
});

// ================= Inject Layer Style =================
// Inject layer style
effectLayerKeys.forEach(effectKey => {
updateCSS(normalizeStyle(effectStyle[effectKey]), `_layer-${effectKey}`, {
...mergedCSSConfig,
prepend: true,
});
});

// ==================== Inject Style ====================
// Inject style
const style = updateCSS(styleStr, styleId, mergedCSSConfig);

(style as any)[CSS_IN_JS_INSTANCE] = cache.instanceId;
Expand All @@ -400,18 +441,14 @@ export default function useStyleRegister(
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|'));
}

// ================ Inject Effect Style =================
// Inject client side effect style
Object.keys(effectStyle).forEach(effectKey => {
if (!globalEffectStyleKeys.has(effectKey)) {
globalEffectStyleKeys.add(effectKey);

// Inject
updateCSS(normalizeStyle(effectStyle[effectKey]), `_effect-${effectKey}`, {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
});
}
effectRestKeys.forEach(effectKey => {
updateCSS(
normalizeStyle(effectStyle[effectKey]),
`_effect-${effectKey}`,
mergedCSSConfig,
);
});
}

Expand Down Expand Up @@ -530,12 +567,20 @@ export function extractStyle(cache: Cache, plain = false) {
// Effect style can be reused
if (!effectStyles[effectKey]) {
effectStyles[effectKey] = true;
keyStyleText += toStyleStr(
normalizeStyle(effectStyle[effectKey]),

const effectStyleStr = normalizeStyle(effectStyle[effectKey]);
const effectStyleHTML = toStyleStr(
effectStyleStr,
tokenKey,
`_effect-${effectKey}`,
sharedAttrs,
);

if (effectKey.startsWith('@layer')) {
keyStyleText = effectStyleHTML + keyStyleText;
} else {
keyStyleText += effectStyleHTML;
}
}
});
}
Expand Down
1 change: 1 addition & 0 deletions components/config-provider/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const useStyle = (iconPrefixCls: Ref<string>) => {
token: token.value,
hashId: '',
path: ['ant-design-icons', iconPrefixCls.value],
layer: { name: 'antd' },
})),
() => [
{
Expand Down
2 changes: 2 additions & 0 deletions components/theme/util/genComponentStyleHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default function genComponentStyleHook<ComponentName extends OverrideComp
token: token.value,
hashId: hashId.value,
path: ['Shared', rootPrefixCls.value],
layer: { name: 'antd' },
};
});
// Generate style for all a tags in antd component.
Expand All @@ -70,6 +71,7 @@ export default function genComponentStyleHook<ComponentName extends OverrideComp
token: token.value,
hashId: hashId.value,
path: [component, prefixCls.value, iconPrefixCls.value],
layer: { name: 'antd' },
};
});
return [
Expand Down