mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-12 04:13:13 +08:00
feat: add useComponentStyle (#34802)
* feat: add useComponentStyle * chore: auto generic * feat: add overload * feat: filter component token * feat: genComponentStyleHook * test: fix lint * feat: button for test case * chore: code clean * chore: code clean * feat: add componentCls into token * feat: merge token statistic * chore: code clean * fix: cache token merge * chore: code clean * chore: add comment & rename variables * chore: code enhancement * chore: FullToken<ComponentName> * chore: clean input * chore: code clean * test: fix lint * feat: inset prefixCls Co-authored-by: zombiej <smith3816@gmail.com>
This commit is contained in:
parent
bffdf0dee5
commit
d0c4d7b0ec
67
components/_util/hooks/genComponentStyleHook.ts
Normal file
67
components/_util/hooks/genComponentStyleHook.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/* eslint-disable no-redeclare */
|
||||
import { CSSInterpolation, useStyleRegister } from '@ant-design/cssinjs';
|
||||
import { useContext } from 'react';
|
||||
import { GlobalToken, OverrideToken } from '../theme/interface';
|
||||
import { mergeToken, statisticToken, UseComponentStyleResult, useToken } from '../theme';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
|
||||
export type OverrideTokenWithoutDerivative = Omit<OverrideToken, 'derivative'>;
|
||||
export type OverrideComponent = keyof OverrideTokenWithoutDerivative;
|
||||
export type GlobalTokenWithComponent<ComponentName extends OverrideComponent> = GlobalToken &
|
||||
OverrideToken[ComponentName];
|
||||
export type StyleInfo = {
|
||||
hashId: string;
|
||||
prefixCls: string;
|
||||
rootPrefixCls: string;
|
||||
iconPrefixCls: string;
|
||||
};
|
||||
export type TokenWithComponentCls<T> = T & { componentCls: string; prefixCls: string };
|
||||
export type FullToken<ComponentName extends OverrideComponent> = TokenWithComponentCls<
|
||||
GlobalTokenWithComponent<ComponentName>
|
||||
>;
|
||||
|
||||
function genComponentStyleHook<ComponentName extends OverrideComponent>(
|
||||
component: ComponentName,
|
||||
styleFn: (token: FullToken<ComponentName>, info: StyleInfo) => CSSInterpolation,
|
||||
getDefaultToken?:
|
||||
| OverrideTokenWithoutDerivative[ComponentName]
|
||||
| ((token: GlobalToken) => OverrideTokenWithoutDerivative[ComponentName]),
|
||||
) {
|
||||
return (prefixCls: string): UseComponentStyleResult => {
|
||||
const [theme, token, hashId] = useToken();
|
||||
const { getPrefixCls, iconPrefixCls } = useContext(ConfigContext);
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => {
|
||||
const { token: proxyToken, flush } = statisticToken(token);
|
||||
|
||||
const defaultComponentToken =
|
||||
typeof getDefaultToken === 'function' ? getDefaultToken(token) : getDefaultToken;
|
||||
const overrideComponentToken = token[component] as any;
|
||||
|
||||
// Only merge token specified in interface
|
||||
const mergedComponentToken = { ...defaultComponentToken } as any;
|
||||
if (overrideComponentToken) {
|
||||
Object.keys(mergedComponentToken).forEach(key => {
|
||||
mergedComponentToken[key] = overrideComponentToken[key] ?? mergedComponentToken[key];
|
||||
});
|
||||
}
|
||||
const mergedToken = mergeToken<
|
||||
TokenWithComponentCls<GlobalTokenWithComponent<OverrideComponent>>
|
||||
>(proxyToken, { componentCls: `.${prefixCls}`, prefixCls }, mergedComponentToken);
|
||||
|
||||
const styleInterpolation = styleFn(mergedToken as unknown as FullToken<ComponentName>, {
|
||||
hashId,
|
||||
prefixCls,
|
||||
rootPrefixCls: getPrefixCls(),
|
||||
iconPrefixCls,
|
||||
});
|
||||
flush(component);
|
||||
return styleInterpolation;
|
||||
}),
|
||||
hashId,
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
export default genComponentStyleHook;
|
@ -154,12 +154,11 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { getPrefixCls, autoInsertSpaceInButton, direction, iconPrefixCls } =
|
||||
React.useContext(ConfigContext);
|
||||
const { getPrefixCls, autoInsertSpaceInButton, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('btn', customizePrefixCls);
|
||||
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const size = React.useContext(SizeContext);
|
||||
const [innerLoading, setLoading] = React.useState<Loading>(!!loading);
|
||||
|
@ -1,15 +1,8 @@
|
||||
// deps-lint-skip-all
|
||||
import { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import {
|
||||
AliasToken,
|
||||
UseComponentStyleResult,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
GenerateStyle,
|
||||
statisticToken,
|
||||
mergeToken,
|
||||
} from '../../_util/theme';
|
||||
import genComponentStyleHook, { FullToken } from '../../_util/hooks/genComponentStyleHook';
|
||||
import { GenerateStyle, mergeToken } from '../../_util/theme';
|
||||
|
||||
/** Component only token. Which will handle additional calculation of alias token */
|
||||
export interface ComponentToken {
|
||||
@ -17,17 +10,16 @@ export interface ComponentToken {
|
||||
colorBgTextActive: string;
|
||||
}
|
||||
|
||||
interface ButtonToken extends AliasToken, ComponentToken {
|
||||
btnCls: string;
|
||||
interface ButtonToken extends FullToken<'Button'> {
|
||||
iconPrefixCls: string;
|
||||
}
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSSObject => {
|
||||
const { btnCls, iconPrefixCls } = token;
|
||||
const { componentCls, iconPrefixCls } = token;
|
||||
|
||||
return {
|
||||
[btnCls]: {
|
||||
[componentCls]: {
|
||||
outline: 'none',
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
@ -53,7 +45,7 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
|
||||
marginInlineStart: token.marginXS,
|
||||
},
|
||||
|
||||
[`&${btnCls}-block`]: {
|
||||
[`&${componentCls}-block`]: {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
@ -148,14 +140,14 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
token.btnCls,
|
||||
token.componentCls,
|
||||
token.colorBgComponent,
|
||||
token.colorBgComponent,
|
||||
token.colorTextDisabled,
|
||||
token.colorBorder,
|
||||
),
|
||||
|
||||
[`&${token.btnCls}-dangerous`]: {
|
||||
[`&${token.componentCls}-dangerous`]: {
|
||||
color: token.colorError,
|
||||
borderColor: token.colorError,
|
||||
|
||||
@ -171,7 +163,7 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
token.btnCls,
|
||||
token.componentCls,
|
||||
token.colorError,
|
||||
token.colorError,
|
||||
token.colorTextDisabled,
|
||||
@ -200,14 +192,14 @@ const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
token.btnCls,
|
||||
token.componentCls,
|
||||
token.colorPrimary,
|
||||
token.colorPrimary,
|
||||
token.colorTextDisabled,
|
||||
token.colorBorder,
|
||||
),
|
||||
|
||||
[`&${token.btnCls}-dangerous`]: {
|
||||
[`&${token.componentCls}-dangerous`]: {
|
||||
backgroundColor: token.colorError,
|
||||
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
|
||||
|
||||
@ -221,7 +213,7 @@ const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
token.btnCls,
|
||||
token.componentCls,
|
||||
token.colorError,
|
||||
token.colorError,
|
||||
token.colorTextDisabled,
|
||||
@ -253,7 +245,7 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
|
||||
[`&${token.btnCls}-dangerous`]: {
|
||||
[`&${token.componentCls}-dangerous`]: {
|
||||
color: token.colorError,
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
@ -284,7 +276,7 @@ const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
|
||||
[`&${token.btnCls}-dangerous`]: {
|
||||
[`&${token.componentCls}-dangerous`]: {
|
||||
color: token.colorError,
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
@ -292,20 +284,20 @@ const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = token => ({
|
||||
});
|
||||
|
||||
const genTypeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||
const { btnCls } = token;
|
||||
const { componentCls } = token;
|
||||
|
||||
return {
|
||||
[`${btnCls}-default`]: genDefaultButtonStyle(token),
|
||||
[`${btnCls}-primary`]: genPrimaryButtonStyle(token),
|
||||
[`${btnCls}-dashed`]: genDashedButtonStyle(token),
|
||||
[`${btnCls}-link`]: genLinkButtonStyle(token),
|
||||
[`${btnCls}-text`]: genTextButtonStyle(token),
|
||||
[`${componentCls}-default`]: genDefaultButtonStyle(token),
|
||||
[`${componentCls}-primary`]: genPrimaryButtonStyle(token),
|
||||
[`${componentCls}-dashed`]: genDashedButtonStyle(token),
|
||||
[`${componentCls}-link`]: genLinkButtonStyle(token),
|
||||
[`${componentCls}-text`]: genTextButtonStyle(token),
|
||||
};
|
||||
};
|
||||
|
||||
// =============================== Size ===============================
|
||||
const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSSInterpolation => {
|
||||
const { btnCls, iconPrefixCls } = token;
|
||||
const { componentCls, iconPrefixCls } = token;
|
||||
|
||||
const paddingVertical = Math.max(
|
||||
0,
|
||||
@ -313,12 +305,12 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
|
||||
);
|
||||
const paddingHorizontal = token.padding - token.controlLineWidth;
|
||||
|
||||
const iconOnlyCls = `${btnCls}-icon-only`;
|
||||
const iconOnlyCls = `${componentCls}-icon-only`;
|
||||
|
||||
return [
|
||||
// Size
|
||||
{
|
||||
[`${btnCls}${sizePrefixCls}`]: {
|
||||
[`${componentCls}${sizePrefixCls}`]: {
|
||||
fontSize: token.fontSize,
|
||||
height: token.controlHeight,
|
||||
padding: `${paddingVertical}px ${paddingHorizontal}px`,
|
||||
@ -334,27 +326,28 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
|
||||
},
|
||||
|
||||
// Loading
|
||||
[`&${btnCls}-loading`]: {
|
||||
[`&${componentCls}-loading`]: {
|
||||
opacity: 0.65,
|
||||
cursor: 'default',
|
||||
},
|
||||
|
||||
[`${btnCls}-loading-icon`]: {
|
||||
[`${componentCls}-loading-icon`]: {
|
||||
transition: `width ${token.motionDurationSlow} ${token.motionEaseInOut}, opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
|
||||
},
|
||||
|
||||
[`&:not(${iconOnlyCls}) ${btnCls}-loading-icon:not(:only-child) > .${iconPrefixCls}`]: {
|
||||
marginInlineEnd: token.marginXS,
|
||||
},
|
||||
[`&:not(${iconOnlyCls}) ${componentCls}-loading-icon:not(:only-child) > .${iconPrefixCls}`]:
|
||||
{
|
||||
marginInlineEnd: token.marginXS,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Shape - patch prefixCls again to override solid border radius style
|
||||
{
|
||||
[`${btnCls}${btnCls}-circle${sizePrefixCls}`]: genCircleButtonStyle(token),
|
||||
[`${componentCls}${componentCls}-circle${sizePrefixCls}`]: genCircleButtonStyle(token),
|
||||
},
|
||||
{
|
||||
[`${btnCls}${btnCls}-round${sizePrefixCls}`]: genRoundButtonStyle(token),
|
||||
[`${componentCls}${componentCls}-round${sizePrefixCls}`]: genRoundButtonStyle(token),
|
||||
},
|
||||
];
|
||||
};
|
||||
@ -367,7 +360,7 @@ const genSizeSmallButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||
padding: token.paddingXS,
|
||||
});
|
||||
|
||||
return genSizeButtonStyle(largeToken, `${token.btnCls}-sm`);
|
||||
return genSizeButtonStyle(largeToken, `${token.componentCls}-sm`);
|
||||
};
|
||||
|
||||
const genSizeLargeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||
@ -376,60 +369,44 @@ const genSizeLargeButtonStyle: GenerateStyle<ButtonToken> = token => {
|
||||
fontSize: token.fontSizeLG,
|
||||
});
|
||||
|
||||
return genSizeButtonStyle(largeToken, `${token.btnCls}-lg`);
|
||||
return genSizeButtonStyle(largeToken, `${token.componentCls}-lg`);
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
export default genComponentStyleHook(
|
||||
'Button',
|
||||
(token, { iconPrefixCls }) => {
|
||||
const buttonToken: ButtonToken = {
|
||||
...token,
|
||||
iconPrefixCls,
|
||||
};
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => {
|
||||
const { colorText, Button = {} } = token;
|
||||
const textColor = new TinyColor(colorText);
|
||||
return [
|
||||
// Shared
|
||||
genSharedButtonStyle(buttonToken),
|
||||
|
||||
const { token: proxyToken, flush } = statisticToken(token);
|
||||
// Size
|
||||
genSizeSmallButtonStyle(buttonToken),
|
||||
genSizeBaseButtonStyle(buttonToken),
|
||||
genSizeLargeButtonStyle(buttonToken),
|
||||
|
||||
const buttonToken = mergeToken<ButtonToken>(
|
||||
proxyToken,
|
||||
{
|
||||
colorBgTextHover: textColor
|
||||
.clone()
|
||||
.setAlpha(textColor.getAlpha() * 0.02)
|
||||
.toRgbString(),
|
||||
colorBgTextActive: textColor
|
||||
.clone()
|
||||
.setAlpha(textColor.getAlpha() * 0.03)
|
||||
.toRgbString(),
|
||||
// Group (type, ghost, danger, disabled, loading)
|
||||
genTypeButtonStyle(buttonToken),
|
||||
];
|
||||
},
|
||||
token => {
|
||||
const { colorText } = token;
|
||||
const textColor = new TinyColor(colorText);
|
||||
|
||||
iconPrefixCls,
|
||||
btnCls: `.${prefixCls}`,
|
||||
},
|
||||
|
||||
// Override
|
||||
Button,
|
||||
);
|
||||
|
||||
const styles = [
|
||||
// Shared
|
||||
genSharedButtonStyle(buttonToken),
|
||||
|
||||
// Size
|
||||
genSizeSmallButtonStyle(buttonToken),
|
||||
genSizeBaseButtonStyle(buttonToken),
|
||||
genSizeLargeButtonStyle(buttonToken),
|
||||
|
||||
// Group (type, ghost, danger, disabled, loading)
|
||||
genTypeButtonStyle(buttonToken),
|
||||
];
|
||||
|
||||
flush('Button');
|
||||
|
||||
return styles;
|
||||
}),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
return {
|
||||
colorBgTextHover: textColor
|
||||
.clone()
|
||||
.setAlpha(textColor.getAlpha() * 0.02)
|
||||
.toRgbString(),
|
||||
colorBgTextActive: textColor
|
||||
.clone()
|
||||
.setAlpha(textColor.getAlpha() * 0.03)
|
||||
.toRgbString(),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
@ -8,13 +8,8 @@
|
||||
// // deps-lint-skip: form
|
||||
|
||||
// deps-lint-skip-all
|
||||
import {
|
||||
DerivativeToken,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
UseComponentStyleResult,
|
||||
GenerateStyle,
|
||||
} from '../../_util/theme';
|
||||
import genComponentStyleHook, { FullToken } from '../../_util/hooks/genComponentStyleHook';
|
||||
import { GenerateStyle } from '../../_util/theme';
|
||||
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
|
||||
|
||||
export interface ComponentToken {
|
||||
@ -23,15 +18,12 @@ export interface ComponentToken {
|
||||
dropdownHeight: number;
|
||||
}
|
||||
|
||||
interface CascaderToken extends DerivativeToken, ComponentToken {
|
||||
prefixCls: string;
|
||||
cascaderCls: string;
|
||||
}
|
||||
type CascaderToken = FullToken<'Cascader'>;
|
||||
|
||||
// =============================== Base ===============================
|
||||
const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
const { prefixCls, cascaderCls } = token;
|
||||
const cascaderMenuItemCls = `${cascaderCls}-menu-item`;
|
||||
const { prefixCls, componentCls } = token;
|
||||
const cascaderMenuItemCls = `${componentCls}-menu-item`;
|
||||
const iconCls = `
|
||||
&${cascaderMenuItemCls}-expand ${cascaderMenuItemCls}-expand-icon,
|
||||
${cascaderMenuItemCls}-loading-icon
|
||||
@ -46,7 +38,7 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
// == Control ==
|
||||
// =====================================================
|
||||
{
|
||||
[cascaderCls]: {
|
||||
[componentCls]: {
|
||||
width: token.controlWidth,
|
||||
},
|
||||
},
|
||||
@ -55,11 +47,11 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
// == Popup ==
|
||||
// =====================================================
|
||||
{
|
||||
[`${cascaderCls}-dropdown`]: [
|
||||
[`${componentCls}-dropdown`]: [
|
||||
// ==================== Checkbox ====================
|
||||
getCheckboxStyle(`${prefixCls}-checkbox`, token, hashId!),
|
||||
{
|
||||
[cascaderCls]: {
|
||||
[componentCls]: {
|
||||
// ================== Checkbox ==================
|
||||
'&-checkbox': {
|
||||
top: 0,
|
||||
@ -73,8 +65,8 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'flex-start',
|
||||
|
||||
[`&${cascaderCls}-menu-empty`]: {
|
||||
[`${cascaderCls}-menu`]: {
|
||||
[`&${componentCls}-menu-empty`]: {
|
||||
[`${componentCls}-menu`]: {
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
|
||||
@ -157,7 +149,7 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
// == RTL ==
|
||||
// =====================================================
|
||||
{
|
||||
[`${cascaderCls}-dropdown-rtl`]: {
|
||||
[`${componentCls}-dropdown-rtl`]: {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
@ -165,28 +157,12 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(prefixCls: string): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => {
|
||||
const { Cascader = {} } = token;
|
||||
|
||||
const cascaderToken: CascaderToken = {
|
||||
...token,
|
||||
prefixCls,
|
||||
cascaderCls: `.${prefixCls}`,
|
||||
|
||||
controlWidth: 184,
|
||||
controlItemWidth: 111,
|
||||
dropdownHeight: 180,
|
||||
|
||||
// Override
|
||||
...Cascader,
|
||||
};
|
||||
|
||||
return [genBaseStyle(cascaderToken, hashId)];
|
||||
}),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
export default genComponentStyleHook(
|
||||
'Cascader',
|
||||
(token, { hashId }) => [genBaseStyle(token, hashId)],
|
||||
{
|
||||
controlWidth: 184,
|
||||
controlItemWidth: 111,
|
||||
dropdownHeight: 180,
|
||||
},
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user