import type { CSSInterpolation, CSSObject } from '@antd-tiny-vue/cssinjs' import type { FullToken, GenerateStyle } from '../../theme/internal' import { genComponentStyleHook, mergeToken } from '../../theme/internal' import { genFocusStyle } from '../../style' import { genCompactItemStyle } from '../../style/compact-item' import { genCompactItemVerticalStyle } from '../../style/compact-item-vertical' import genGroupStyle from './group' /** Component only token. Which will handle additional calculation of alias token */ export interface ComponentToken {} export interface ButtonToken extends FullToken<'Button'> { // FIXME: should be removed colorOutlineDefault: string buttonPaddingHorizontal: number } // ============================== 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.lineWidth}px ${token.lineType} transparent`, cursor: 'pointer', transition: `all ${token.motionDurationMid} ${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 }, '> a': { color: 'currentColor' }, '&:not(:disabled)': { ...genFocusStyle(token) }, // make `btn-icon-only` not too narrow [`&-icon-only${componentCls}-compact-item`]: { flex: 'none' }, // Special styles for Primary Button [`&-compact-item${componentCls}-primary`]: { [`&:not([disabled]) + ${componentCls}-compact-item${componentCls}-primary:not([disabled])`]: { position: 'relative', '&:before': { position: 'absolute', top: -token.lineWidth, insetInlineStart: -token.lineWidth, display: 'inline-block', width: token.lineWidth, height: `calc(100% + ${token.lineWidth * 2}px)`, backgroundColor: token.colorPrimaryHover, content: '""' } } }, // Special styles for Primary Button '&-compact-vertical-item': { [`&${componentCls}-primary`]: { [`&:not([disabled]) + ${componentCls}-compact-vertical-item${componentCls}-primary:not([disabled])`]: { position: 'relative', '&:before': { position: 'absolute', top: -token.lineWidth, insetInlineStart: -token.lineWidth, display: 'inline-block', width: `calc(100% + ${token.lineWidth * 2}px)`, height: token.lineWidth, backgroundColor: token.colorPrimaryHover, content: '""' } } } } } } } const genHoverActiveButtonStyle = (hoverStyle: CSSObject, activeStyle: CSSObject): CSSObject => ({ '&:not(:disabled)': { '&:hover': 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 }) // =============================== Type =============================== const genDisabledStyle: GenerateStyle = token => ({ cursor: 'not-allowed', borderColor: token.colorBorder, color: token.colorTextDisabled, backgroundColor: token.colorBgContainerDisabled, boxShadow: 'none' }) const genGhostButtonStyle = ( btnCls: string, textColor: string | false, borderColor: string | false, textColorDisabled: string | false, borderColorDisabled: string | false, hoverStyle?: CSSObject, activeStyle?: CSSObject ): CSSObject => ({ [`&${btnCls}-background-ghost`]: { color: textColor || undefined, backgroundColor: 'transparent', borderColor: borderColor || undefined, boxShadow: 'none', ...genHoverActiveButtonStyle( { backgroundColor: 'transparent', ...hoverStyle }, { backgroundColor: 'transparent', ...activeStyle } ), '&:disabled': { cursor: 'not-allowed', color: textColorDisabled || undefined, borderColor: borderColorDisabled || undefined } } }) const genSolidDisabledButtonStyle: GenerateStyle = token => ({ '&:disabled': { ...genDisabledStyle(token) } }) const genSolidButtonStyle: GenerateStyle = token => ({ ...genSolidDisabledButtonStyle(token) }) const genPureDisabledButtonStyle: GenerateStyle = token => ({ '&:disabled': { cursor: 'not-allowed', color: token.colorTextDisabled } }) // Type: Default const genDefaultButtonStyle: GenerateStyle = token => ({ ...genSolidButtonStyle(token), backgroundColor: token.colorBgContainer, borderColor: token.colorBorder, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`, ...genHoverActiveButtonStyle( { color: token.colorPrimaryHover, borderColor: token.colorPrimaryHover }, { color: token.colorPrimaryActive, borderColor: token.colorPrimaryActive } ), ...genGhostButtonStyle(token.componentCls, token.colorBgContainer, token.colorBgContainer, token.colorTextDisabled, token.colorBorder), [`&${token.componentCls}-dangerous`]: { color: token.colorError, borderColor: token.colorError, ...genHoverActiveButtonStyle( { color: token.colorErrorHover, borderColor: token.colorErrorBorderHover }, { 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: token.colorTextLightSolid, backgroundColor: token.colorPrimary, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`, ...genHoverActiveButtonStyle( { color: token.colorTextLightSolid, backgroundColor: token.colorPrimaryHover }, { color: token.colorTextLightSolid, backgroundColor: token.colorPrimaryActive } ), ...genGhostButtonStyle( token.componentCls, token.colorPrimary, token.colorPrimary, token.colorTextDisabled, token.colorBorder, { color: token.colorPrimaryHover, borderColor: token.colorPrimaryHover }, { color: token.colorPrimaryActive, borderColor: token.colorPrimaryActive } ), [`&${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, { color: token.colorErrorHover, borderColor: token.colorErrorHover }, { color: token.colorErrorActive, borderColor: token.colorErrorActive } ), ...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 => ({ ...genHoverActiveButtonStyle( { color: token.colorText, backgroundColor: token.colorBgTextHover }, { color: token.colorText, backgroundColor: token.colorBgTextActive } ), ...genPureDisabledButtonStyle(token), [`&${token.componentCls}-dangerous`]: { color: token.colorError, ...genPureDisabledButtonStyle(token), ...genHoverActiveButtonStyle( { color: token.colorErrorHover, backgroundColor: token.colorErrorBg }, { color: token.colorErrorHover, backgroundColor: token.colorErrorBg } ) } }) // Href and Disabled const genDisabledButtonStyle: GenerateStyle = token => ({ ...genDisabledStyle(token), [`&${token.componentCls}:hover`]: { ...genDisabledStyle(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), [`${componentCls}-disabled`]: genDisabledButtonStyle(token) } } // =============================== Size =============================== const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls = ''): CSSInterpolation => { const { componentCls, iconCls, controlHeight, fontSize, lineHeight, lineWidth, borderRadius, buttonPaddingHorizontal } = token const paddingVertical = Math.max(0, (controlHeight - fontSize * lineHeight) / 2 - lineWidth) const paddingHorizontal = buttonPaddingHorizontal - lineWidth const iconOnlyCls = `${componentCls}-icon-only` return [ // Size { [`${componentCls}${sizePrefixCls}`]: { fontSize, height: controlHeight, padding: `${paddingVertical}px ${paddingHorizontal}px`, borderRadius, [`&${iconOnlyCls}`]: { width: controlHeight, paddingInlineStart: 0, paddingInlineEnd: 0, [`&${componentCls}-round`]: { width: 'auto' }, '> 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 > ${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 smallToken = mergeToken(token, { controlHeight: token.controlHeightSM, padding: token.paddingXS, buttonPaddingHorizontal: 8, // Fixed padding borderRadius: token.borderRadiusSM }) return genSizeButtonStyle(smallToken, `${token.componentCls}-sm`) } const genSizeLargeButtonStyle: GenerateStyle = token => { const largeToken = mergeToken(token, { controlHeight: token.controlHeightLG, fontSize: token.fontSizeLG, borderRadius: token.borderRadiusLG }) return genSizeButtonStyle(largeToken, `${token.componentCls}-lg`) } const genBlockButtonStyle: GenerateStyle = token => { const { componentCls } = token return { [componentCls]: { [`&${componentCls}-block`]: { width: '100%' } } } } // ============================== Export ============================== export default genComponentStyleHook('Button', token => { const { controlTmpOutline, paddingContentHorizontal } = token const buttonToken = mergeToken(token, { colorOutlineDefault: controlTmpOutline, buttonPaddingHorizontal: paddingContentHorizontal }) return [ // Shared genSharedButtonStyle(buttonToken), // Size genSizeSmallButtonStyle(buttonToken), genSizeBaseButtonStyle(buttonToken), genSizeLargeButtonStyle(buttonToken), // Block genBlockButtonStyle(buttonToken), // Group (type, ghost, danger, disabled, loading) genTypeButtonStyle(buttonToken), // Button Group genGroupStyle(buttonToken), // Space Compact genCompactItemStyle(token, { focus: false }), genCompactItemVerticalStyle(token) ] })