Skip to content
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

feat(cssvar): Migrate cssvar features of antd v5.13.2 version #7220

Open
wants to merge 10 commits into
base: feat-4.3
Choose a base branch
from
112 changes: 112 additions & 0 deletions components/_theme/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { ShallowRef, InjectionKey, ExtractPropTypes, ComputedRef } from 'vue';
import {
defineComponent,
inject,
provide,
shallowRef,
unref,
triggerRef,
watch,
computed,
} from 'vue';
import type { Theme } from '../_util/_cssinjs';
import { createTheme } from '../_util/_cssinjs';

import { objectType, someType } from '../_util/type';
import type { AliasToken, MapToken, OverrideToken, SeedToken } from './interface';
import defaultDerivative from './themes/default';
import defaultSeedToken from './themes/seed';

export const defaultTheme = createTheme(defaultDerivative);

// ================================ Context =================================
// To ensure snapshot stable. We disable hashed in test env.
const DesignTokenContextKey: InjectionKey<ShallowRef<Partial<DesignTokenProviderProps>>> =
Symbol('DesignTokenContextKey');

export const globalDesignTokenApi = shallowRef<DesignTokenProviderProps>();

export const defaultConfig = {
token: defaultSeedToken,
override: { override: defaultSeedToken },
hashed: true,
};

export type ComponentsToken = {
[key in keyof OverrideToken]?: OverrideToken[key] & {
theme?: Theme<SeedToken, MapToken>;
};
};
export const styleProviderProps = () => ({
token: objectType<AliasToken>(),
theme: objectType<Theme<SeedToken, MapToken>>(),
components: objectType<ComponentsToken>(),
/** Just merge `token` & `override` at top to save perf */
override: objectType<{ override: Partial<AliasToken> } & ComponentsToken>(),
hashed: someType<string | boolean>(),
cssVar: someType<{
prefix?: string;
key?: string;
}>(),
});

export type StyleProviderProps = Partial<ExtractPropTypes<ReturnType<typeof styleProviderProps>>>;
export interface DesignTokenProviderProps {
token: Partial<AliasToken>;
theme?: Theme<SeedToken, MapToken>;
components?: ComponentsToken;
/** Just merge `token` & `override` at top to save perf */
override?: { override: Partial<AliasToken> } & ComponentsToken;
hashed?: string | boolean;
cssVar?: {
prefix?: string;
key?: string;
};
}

export const useDesignTokenInject = () => {
return inject(
DesignTokenContextKey,
computed(() => globalDesignTokenApi.value || defaultConfig),
);
};

export const useDesignTokenProvider = (props: ComputedRef<DesignTokenProviderProps>) => {
const parentContext = useDesignTokenInject();
const context = shallowRef<Partial<DesignTokenProviderProps>>(defaultConfig);
watch(
computed(() => [props.value, parentContext.value]),
([propsValue, parentContextValue]) => {
const mergedContext: Partial<DesignTokenProviderProps> = {
...parentContextValue,
};
Object.keys(propsValue).forEach(key => {
const value = propsValue[key];
if (propsValue[key] !== undefined) {
mergedContext[key] = value;
}
});

context.value = mergedContext;
globalDesignTokenApi.value = unref(mergedContext as any);
triggerRef(globalDesignTokenApi);
},
{ immediate: true, deep: true },
);
provide(DesignTokenContextKey, context);
return context;
};

export const DesignTokenProvider = defineComponent({
props: {
value: objectType<DesignTokenProviderProps>(),
},
setup(props, { slots }) {
useDesignTokenProvider(computed(() => props.value));
return () => {
return slots.default?.();
};
},
});

export default { useDesignTokenInject, useDesignTokenProvider, DesignTokenProvider };
17 changes: 17 additions & 0 deletions components/_theme/getDesignToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createTheme, getComputedToken } from '../_util/_cssinjs';
import type { ThemeConfig } from '../config-provider/context';
import type { AliasToken } from './interface';
import defaultDerivative from './themes/default';
import seedToken from './themes/seed';
import formatToken from './util/alias';

const getDesignToken = (config?: ThemeConfig): AliasToken => {
const theme = config?.algorithm ? createTheme(config.algorithm) : createTheme(defaultDerivative);
const mergedToken = {
...seedToken,
...config?.token,
};
return getComputedToken(mergedToken, { override: config?.token }, theme, formatToken);
};

export default getDesignToken;
33 changes: 33 additions & 0 deletions components/_theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable import/prefer-default-export */
import getDesignToken from './getDesignToken';
import type { GlobalToken, MappingAlgorithm } from './interface';
import { defaultConfig, useToken as useInternalToken } from './internal';
import compactAlgorithm from './themes/compact';
import darkAlgorithm from './themes/dark';
import defaultAlgorithm from './themes/default';

// ZombieJ: We export as object to user but array in internal.
// This is used to minimize the bundle size for antd package but safe to refactor as object also.
// Please do not export internal `useToken` directly to avoid something export unexpected.
/** Get current context Design Token. Will be different if you are using nest theme config. */
function useToken() {
const [theme, token, hashId] = useInternalToken();

return { theme, token, hashId };
}

export type { GlobalToken, MappingAlgorithm };

export default {
/** @private Test Usage. Do not use in production. */
defaultConfig,

/** Default seedToken */
defaultSeed: defaultConfig.token,

useToken,
defaultAlgorithm,
darkAlgorithm,
compactAlgorithm,
getDesignToken,
};