// deps-lint-skip-all import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs'; import { TinyColor } from '@ctrl/tinycolor'; import type { FullToken, GenerateStyle } from '../../_util/theme'; import { genComponentStyleHook, mergeToken } from '../../_util/theme'; import genGroupStyle from './group'; /** Component only token. Which will handle additional calculation of alias token */ export interface ComponentToken { colorBgTextHover: string; colorBgTextActive: string; // FIXME: should be removed colorOutlineDefault: string; } export interface ButtonToken extends FullToken<'Button'> {} // ============================== Shared ============================== const genSharedButtonStyle: GenerateStyle = (token): CSSObject => { const { componentCls, iconCls } = token; return { [componentCls]: { outline: 'none', position: 'relative', display: 'inline-block', fontWeight: 400, whiteSpace: 'nowrap', textAlign: 'center', backgroundImage: 'none', backgroundColor: 'transparent', border: `${token.controlLineWidth}px ${token.controlLineType} transparent`, cursor: 'pointer', transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`, userSelect: 'none', touchAction: 'manipulation', lineHeight: token.lineHeight, color: token.colorText, '> span': { display: 'inline-block', }, // Leave a space between icon and text. [`> ${iconCls} + span, > span + ${iconCls}`]: { marginInlineStart: token.marginXS, }, [`&${componentCls}-block`]: { width: '100%', }, }, }; }; const genHoverActiveButtonStyle = (hoverStyle: CSSObject, activeStyle: CSSObject): CSSObject => ({ '&:not(:disabled)': { '&:hover, &:focus': hoverStyle, '&:active': activeStyle, }, }); // ============================== Shape =============================== const genCircleButtonStyle: GenerateStyle = token => ({ minWidth: token.controlHeight, paddingInlineStart: 0, paddingInlineEnd: 0, borderRadius: '50%', }); const genRoundButtonStyle: GenerateStyle = token => ({ borderRadius: token.controlHeight, paddingInlineStart: token.controlHeight / 2, paddingInlineEnd: token.controlHeight / 2, width: 'auto', }); // =============================== Type =============================== const genGhostButtonStyle = ( btnCls: string, textColor: string | false, borderColor: string | false, textColorDisabled: string | false, borderColorDisabled: string | false, ): CSSObject => ({ [`&${btnCls}-background-ghost`]: { color: textColor || undefined, backgroundColor: 'transparent', borderColor: borderColor || undefined, boxShadow: 'none', '&:hover': { backgroundColor: 'transparent', }, '&:disabled': { cursor: 'not-allowed', color: textColorDisabled || undefined, borderColor: borderColorDisabled || undefined, }, }, }); const genSolidDisabledButtonStyle: GenerateStyle = token => ({ '&:disabled': { cursor: 'not-allowed', borderColor: token.colorBorder, color: token.colorTextDisabled, backgroundColor: token.colorBgComponentDisabled, boxShadow: 'none', }, }); const genSolidButtonStyle: GenerateStyle = token => ({ borderRadius: token.controlRadius, ...genSolidDisabledButtonStyle(token), }); const genPureDisabledButtonStyle: GenerateStyle = token => ({ '&:disabled': { cursor: 'not-allowed', color: token.colorTextDisabled, }, }); // Type: Default const genDefaultButtonStyle: GenerateStyle = token => ({ ...genSolidButtonStyle(token), backgroundColor: token.colorBgComponent, borderColor: token.colorBorder, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorDefaultOutline}`, ...genHoverActiveButtonStyle( { color: token.colorPrimaryHover, borderColor: token.colorPrimaryHover, }, { color: token.colorPrimaryActive, borderColor: token.colorPrimaryActive, }, ), ...genGhostButtonStyle( token.componentCls, token.colorBgComponent, token.colorBgComponent, token.colorTextDisabled, token.colorBorder, ), [`&${token.componentCls}-dangerous`]: { color: token.colorError, borderColor: token.colorError, ...genHoverActiveButtonStyle( { color: token.colorErrorHover, borderColor: token.colorErrorHover, }, { color: token.colorErrorActive, borderColor: token.colorErrorActive, }, ), ...genGhostButtonStyle( token.componentCls, token.colorError, token.colorError, token.colorTextDisabled, token.colorBorder, ), ...genSolidDisabledButtonStyle(token), }, }); // Type: Primary const genPrimaryButtonStyle: GenerateStyle = token => ({ ...genSolidButtonStyle(token), color: '#FFF', backgroundColor: token.colorPrimary, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorPrimaryOutline}`, ...genHoverActiveButtonStyle( { backgroundColor: token.colorPrimaryHover, }, { backgroundColor: token.colorPrimaryActive, }, ), ...genGhostButtonStyle( token.componentCls, token.colorPrimary, token.colorPrimary, token.colorTextDisabled, token.colorBorder, ), [`&${token.componentCls}-dangerous`]: { backgroundColor: token.colorError, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`, ...genHoverActiveButtonStyle( { backgroundColor: token.colorErrorHover, }, { backgroundColor: token.colorErrorActive, }, ), ...genGhostButtonStyle( token.componentCls, token.colorError, token.colorError, token.colorTextDisabled, token.colorBorder, ), ...genSolidDisabledButtonStyle(token), }, }); // Type: Dashed const genDashedButtonStyle: GenerateStyle = token => ({ ...genDefaultButtonStyle(token), borderStyle: 'dashed', }); // Type: Link const genLinkButtonStyle: GenerateStyle = token => ({ color: token.colorLink, ...genHoverActiveButtonStyle( { color: token.colorLinkHover, }, { color: token.colorLinkActive, }, ), ...genPureDisabledButtonStyle(token), [`&${token.componentCls}-dangerous`]: { color: token.colorError, ...genHoverActiveButtonStyle( { color: token.colorErrorHover, }, { color: token.colorErrorActive, }, ), ...genPureDisabledButtonStyle(token), }, }); // Type: Text const genTextButtonStyle: GenerateStyle = token => ({ borderRadius: token.controlRadius, ...genHoverActiveButtonStyle( { backgroundColor: token.colorBgTextHover, }, { backgroundColor: token.colorBgTextActive, }, ), ...genPureDisabledButtonStyle(token), [`&${token.componentCls}-dangerous`]: { color: token.colorError, ...genPureDisabledButtonStyle(token), }, }); const genTypeButtonStyle: GenerateStyle = token => { const { componentCls } = token; return { [`${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 { componentCls, iconCls } = token; const paddingVertical = Math.max( 0, (token.controlHeight - token.fontSize * token.lineHeight) / 2 - token.controlLineWidth, ); const paddingHorizontal = token.padding - token.controlLineWidth; const iconOnlyCls = `${componentCls}-icon-only`; return [ // Size { [`${componentCls}${sizePrefixCls}`]: { fontSize: token.fontSize, height: token.controlHeight, padding: `${paddingVertical}px ${paddingHorizontal}px`, [`&${iconOnlyCls}`]: { width: token.controlHeight, paddingInlineStart: 0, paddingInlineEnd: 0, '> span': { transform: 'scale(1.143)', // 14px -> 16px }, }, // Loading [`&${componentCls}-loading`]: { opacity: token.opacityLoading, cursor: 'default', }, [`${componentCls}-loading-icon`]: { transition: `width ${token.motionDurationSlow} ${token.motionEaseInOut}, opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`, }, [`&:not(${iconOnlyCls}) ${componentCls}-loading-icon:not(:only-child) > ${iconCls}`]: { marginInlineEnd: token.marginXS, }, }, }, // Shape - patch prefixCls again to override solid border radius style { [`${componentCls}${componentCls}-circle${sizePrefixCls}`]: genCircleButtonStyle(token), }, { [`${componentCls}${componentCls}-round${sizePrefixCls}`]: genRoundButtonStyle(token), }, ]; }; const genSizeBaseButtonStyle: GenerateStyle = token => genSizeButtonStyle(token); const genSizeSmallButtonStyle: GenerateStyle = token => { const largeToken = mergeToken(token, { controlHeight: token.controlHeightSM, padding: token.paddingXS, }); return genSizeButtonStyle(largeToken, `${token.componentCls}-sm`); }; const genSizeLargeButtonStyle: GenerateStyle = token => { const largeToken = mergeToken(token, { controlHeight: token.controlHeightLG, fontSize: token.fontSizeLG, }); return genSizeButtonStyle(largeToken, `${token.componentCls}-lg`); }; // ============================== Export ============================== export default genComponentStyleHook( 'Button', token => [ // Shared genSharedButtonStyle(token), // Size genSizeSmallButtonStyle(token), genSizeBaseButtonStyle(token), genSizeLargeButtonStyle(token), // Group (type, ghost, danger, disabled, loading) genTypeButtonStyle(token), // Button Group genGroupStyle(token), ], token => { const { colorText } = token; const textColor = new TinyColor(colorText); return { colorBgTextHover: textColor .clone() .setAlpha(textColor.getAlpha() * 0.02) .toRgbString(), colorBgTextActive: textColor .clone() .setAlpha(textColor.getAlpha() * 0.03) .toRgbString(), colorOutlineDefault: new TinyColor({ h: 0, s: 0, v: 96 }).toHexString(), }; }, );