index.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. import { unit } from '@ant-design/cssinjs';
  2. import { genFocusStyle, resetIcon } from '../../style';
  3. import { PresetColors } from '../../theme/interface';
  4. import { genStyleHooks, mergeToken } from '../../theme/internal';
  5. import genGroupStyle from './group';
  6. import { prepareComponentToken, prepareToken } from './token';
  7. // ============================== Shared ==============================
  8. const genSharedButtonStyle = token => {
  9. const {
  10. componentCls,
  11. iconCls,
  12. fontWeight,
  13. opacityLoading,
  14. motionDurationSlow,
  15. motionEaseInOut,
  16. iconGap,
  17. calc
  18. } = token;
  19. return {
  20. [componentCls]: {
  21. outline: 'none',
  22. position: 'relative',
  23. display: 'inline-flex',
  24. gap: iconGap,
  25. alignItems: 'center',
  26. justifyContent: 'center',
  27. fontWeight,
  28. whiteSpace: 'nowrap',
  29. textAlign: 'center',
  30. backgroundImage: 'none',
  31. background: 'transparent',
  32. border: `${unit(token.lineWidth)} ${token.lineType} transparent`,
  33. cursor: 'pointer',
  34. transition: `all ${token.motionDurationMid} ${token.motionEaseInOut}`,
  35. userSelect: 'none',
  36. touchAction: 'manipulation',
  37. color: token.colorText,
  38. '&:disabled > *': {
  39. pointerEvents: 'none'
  40. },
  41. // https://github.com/ant-design/ant-design/issues/51380
  42. [`${componentCls}-icon > svg`]: resetIcon(),
  43. '> a': {
  44. color: 'currentColor'
  45. },
  46. '&:not(:disabled)': genFocusStyle(token),
  47. [`&${componentCls}-two-chinese-chars::first-letter`]: {
  48. letterSpacing: '0.34em'
  49. },
  50. [`&${componentCls}-two-chinese-chars > *:not(${iconCls})`]: {
  51. marginInlineEnd: '-0.34em',
  52. letterSpacing: '0.34em'
  53. },
  54. [`&${componentCls}-icon-only`]: {
  55. paddingInline: 0,
  56. // make `btn-icon-only` not too narrow
  57. [`&${componentCls}-compact-item`]: {
  58. flex: 'none'
  59. }
  60. },
  61. // Loading
  62. [`&${componentCls}-loading`]: {
  63. opacity: opacityLoading,
  64. cursor: 'default'
  65. },
  66. [`${componentCls}-loading-icon`]: {
  67. transition: ['width', 'opacity', 'margin'].map(transition => `${transition} ${motionDurationSlow} ${motionEaseInOut}`).join(',')
  68. },
  69. // iconPosition
  70. [`&:not(${componentCls}-icon-end)`]: {
  71. [`${componentCls}-loading-icon-motion`]: {
  72. '&-appear-start, &-enter-start': {
  73. marginInlineEnd: calc(iconGap).mul(-1).equal()
  74. },
  75. '&-appear-active, &-enter-active': {
  76. marginInlineEnd: 0
  77. },
  78. '&-leave-start': {
  79. marginInlineEnd: 0
  80. },
  81. '&-leave-active': {
  82. marginInlineEnd: calc(iconGap).mul(-1).equal()
  83. }
  84. }
  85. },
  86. '&-icon-end': {
  87. flexDirection: 'row-reverse',
  88. [`${componentCls}-loading-icon-motion`]: {
  89. '&-appear-start, &-enter-start': {
  90. marginInlineStart: calc(iconGap).mul(-1).equal()
  91. },
  92. '&-appear-active, &-enter-active': {
  93. marginInlineStart: 0
  94. },
  95. '&-leave-start': {
  96. marginInlineStart: 0
  97. },
  98. '&-leave-active': {
  99. marginInlineStart: calc(iconGap).mul(-1).equal()
  100. }
  101. }
  102. }
  103. }
  104. };
  105. };
  106. const genHoverActiveButtonStyle = (btnCls, hoverStyle, activeStyle) => ({
  107. [`&:not(:disabled):not(${btnCls}-disabled)`]: {
  108. '&:hover': hoverStyle,
  109. '&:active': activeStyle
  110. }
  111. });
  112. // ============================== Shape ===============================
  113. const genCircleButtonStyle = token => ({
  114. minWidth: token.controlHeight,
  115. paddingInline: 0,
  116. borderRadius: '50%'
  117. });
  118. const genDisabledStyle = token => ({
  119. cursor: 'not-allowed',
  120. borderColor: token.borderColorDisabled,
  121. color: token.colorTextDisabled,
  122. background: token.colorBgContainerDisabled,
  123. boxShadow: 'none'
  124. });
  125. const genGhostButtonStyle = (btnCls, background, textColor, borderColor, textColorDisabled, borderColorDisabled, hoverStyle, activeStyle) => ({
  126. [`&${btnCls}-background-ghost`]: Object.assign(Object.assign({
  127. color: textColor || undefined,
  128. background,
  129. borderColor: borderColor || undefined,
  130. boxShadow: 'none'
  131. }, genHoverActiveButtonStyle(btnCls, Object.assign({
  132. background
  133. }, hoverStyle), Object.assign({
  134. background
  135. }, activeStyle))), {
  136. '&:disabled': {
  137. cursor: 'not-allowed',
  138. color: textColorDisabled || undefined,
  139. borderColor: borderColorDisabled || undefined
  140. }
  141. })
  142. });
  143. const genSolidDisabledButtonStyle = token => ({
  144. [`&:disabled, &${token.componentCls}-disabled`]: Object.assign({}, genDisabledStyle(token))
  145. });
  146. const genPureDisabledButtonStyle = token => ({
  147. [`&:disabled, &${token.componentCls}-disabled`]: {
  148. cursor: 'not-allowed',
  149. color: token.colorTextDisabled
  150. }
  151. });
  152. // ============================== Variant =============================
  153. const genVariantButtonStyle = (token, hoverStyle, activeStyle, variant) => {
  154. const isPureDisabled = variant && ['link', 'text'].includes(variant);
  155. const genDisabledButtonStyle = isPureDisabled ? genPureDisabledButtonStyle : genSolidDisabledButtonStyle;
  156. return Object.assign(Object.assign({}, genDisabledButtonStyle(token)), genHoverActiveButtonStyle(token.componentCls, hoverStyle, activeStyle));
  157. };
  158. const genSolidButtonStyle = (token, textColor, background, hoverStyle, activeStyle) => ({
  159. [`&${token.componentCls}-variant-solid`]: Object.assign({
  160. color: textColor,
  161. background
  162. }, genVariantButtonStyle(token, hoverStyle, activeStyle))
  163. });
  164. const genOutlinedDashedButtonStyle = (token, borderColor, background, hoverStyle, activeStyle) => ({
  165. [`&${token.componentCls}-variant-outlined, &${token.componentCls}-variant-dashed`]: Object.assign({
  166. borderColor,
  167. background
  168. }, genVariantButtonStyle(token, hoverStyle, activeStyle))
  169. });
  170. const genDashedButtonStyle = token => ({
  171. [`&${token.componentCls}-variant-dashed`]: {
  172. borderStyle: 'dashed'
  173. }
  174. });
  175. const genFilledButtonStyle = (token, background, hoverStyle, activeStyle) => ({
  176. [`&${token.componentCls}-variant-filled`]: Object.assign({
  177. boxShadow: 'none',
  178. background
  179. }, genVariantButtonStyle(token, hoverStyle, activeStyle))
  180. });
  181. const genTextLinkButtonStyle = (token, textColor, variant, hoverStyle, activeStyle) => ({
  182. [`&${token.componentCls}-variant-${variant}`]: Object.assign({
  183. color: textColor,
  184. boxShadow: 'none'
  185. }, genVariantButtonStyle(token, hoverStyle, activeStyle, variant))
  186. });
  187. // =============================== Color ==============================
  188. const genPresetColorStyle = token => {
  189. const {
  190. componentCls
  191. } = token;
  192. return PresetColors.reduce((prev, colorKey) => {
  193. const darkColor = token[`${colorKey}6`];
  194. const lightColor = token[`${colorKey}1`];
  195. const hoverColor = token[`${colorKey}5`];
  196. const lightHoverColor = token[`${colorKey}2`];
  197. const lightBorderColor = token[`${colorKey}3`];
  198. const activeColor = token[`${colorKey}7`];
  199. return Object.assign(Object.assign({}, prev), {
  200. [`&${componentCls}-color-${colorKey}`]: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({
  201. color: darkColor,
  202. boxShadow: token[`${colorKey}ShadowColor`]
  203. }, genSolidButtonStyle(token, token.colorTextLightSolid, darkColor, {
  204. background: hoverColor
  205. }, {
  206. background: activeColor
  207. })), genOutlinedDashedButtonStyle(token, darkColor, token.colorBgContainer, {
  208. color: hoverColor,
  209. borderColor: hoverColor,
  210. background: token.colorBgContainer
  211. }, {
  212. color: activeColor,
  213. borderColor: activeColor,
  214. background: token.colorBgContainer
  215. })), genDashedButtonStyle(token)), genFilledButtonStyle(token, lightColor, {
  216. color: darkColor,
  217. background: lightHoverColor
  218. }, {
  219. color: darkColor,
  220. background: lightBorderColor
  221. })), genTextLinkButtonStyle(token, darkColor, 'link', {
  222. color: hoverColor
  223. }, {
  224. color: activeColor
  225. })), genTextLinkButtonStyle(token, darkColor, 'text', {
  226. color: hoverColor,
  227. background: lightColor
  228. }, {
  229. color: activeColor,
  230. background: lightBorderColor
  231. }))
  232. });
  233. }, {});
  234. };
  235. const genDefaultButtonStyle = token => Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({
  236. color: token.defaultColor,
  237. boxShadow: token.defaultShadow
  238. }, genSolidButtonStyle(token, token.solidTextColor, token.colorBgSolid, {
  239. color: token.solidTextColor,
  240. background: token.colorBgSolidHover
  241. }, {
  242. color: token.solidTextColor,
  243. background: token.colorBgSolidActive
  244. })), genDashedButtonStyle(token)), genFilledButtonStyle(token, token.colorFillTertiary, {
  245. color: token.defaultColor,
  246. background: token.colorFillSecondary
  247. }, {
  248. color: token.defaultColor,
  249. background: token.colorFill
  250. })), genGhostButtonStyle(token.componentCls, token.ghostBg, token.defaultGhostColor, token.defaultGhostBorderColor, token.colorTextDisabled, token.colorBorder)), genTextLinkButtonStyle(token, token.textTextColor, 'link', {
  251. color: token.colorLinkHover,
  252. background: token.linkHoverBg
  253. }, {
  254. color: token.colorLinkActive
  255. }));
  256. const genPrimaryButtonStyle = token => Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({
  257. color: token.colorPrimary,
  258. boxShadow: token.primaryShadow
  259. }, genOutlinedDashedButtonStyle(token, token.colorPrimary, token.colorBgContainer, {
  260. color: token.colorPrimaryTextHover,
  261. borderColor: token.colorPrimaryHover,
  262. background: token.colorBgContainer
  263. }, {
  264. color: token.colorPrimaryTextActive,
  265. borderColor: token.colorPrimaryActive,
  266. background: token.colorBgContainer
  267. })), genDashedButtonStyle(token)), genFilledButtonStyle(token, token.colorPrimaryBg, {
  268. color: token.colorPrimary,
  269. background: token.colorPrimaryBgHover
  270. }, {
  271. color: token.colorPrimary,
  272. background: token.colorPrimaryBorder
  273. })), genTextLinkButtonStyle(token, token.colorPrimaryText, 'text', {
  274. color: token.colorPrimaryTextHover,
  275. background: token.colorPrimaryBg
  276. }, {
  277. color: token.colorPrimaryTextActive,
  278. background: token.colorPrimaryBorder
  279. })), genTextLinkButtonStyle(token, token.colorPrimaryText, 'link', {
  280. color: token.colorPrimaryTextHover,
  281. background: token.linkHoverBg
  282. }, {
  283. color: token.colorPrimaryTextActive
  284. })), genGhostButtonStyle(token.componentCls, token.ghostBg, token.colorPrimary, token.colorPrimary, token.colorTextDisabled, token.colorBorder, {
  285. color: token.colorPrimaryHover,
  286. borderColor: token.colorPrimaryHover
  287. }, {
  288. color: token.colorPrimaryActive,
  289. borderColor: token.colorPrimaryActive
  290. }));
  291. const genDangerousStyle = token => Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({
  292. color: token.colorError,
  293. boxShadow: token.dangerShadow
  294. }, genSolidButtonStyle(token, token.dangerColor, token.colorError, {
  295. background: token.colorErrorHover
  296. }, {
  297. background: token.colorErrorActive
  298. })), genOutlinedDashedButtonStyle(token, token.colorError, token.colorBgContainer, {
  299. color: token.colorErrorHover,
  300. borderColor: token.colorErrorBorderHover
  301. }, {
  302. color: token.colorErrorActive,
  303. borderColor: token.colorErrorActive
  304. })), genDashedButtonStyle(token)), genFilledButtonStyle(token, token.colorErrorBg, {
  305. color: token.colorError,
  306. background: token.colorErrorBgFilledHover
  307. }, {
  308. color: token.colorError,
  309. background: token.colorErrorBgActive
  310. })), genTextLinkButtonStyle(token, token.colorError, 'text', {
  311. color: token.colorErrorHover,
  312. background: token.colorErrorBg
  313. }, {
  314. color: token.colorErrorHover,
  315. background: token.colorErrorBgActive
  316. })), genTextLinkButtonStyle(token, token.colorError, 'link', {
  317. color: token.colorErrorHover
  318. }, {
  319. color: token.colorErrorActive
  320. })), genGhostButtonStyle(token.componentCls, token.ghostBg, token.colorError, token.colorError, token.colorTextDisabled, token.colorBorder, {
  321. color: token.colorErrorHover,
  322. borderColor: token.colorErrorHover
  323. }, {
  324. color: token.colorErrorActive,
  325. borderColor: token.colorErrorActive
  326. }));
  327. const genLinkStyle = token => Object.assign(Object.assign({}, genTextLinkButtonStyle(token, token.colorLink, 'link', {
  328. color: token.colorLinkHover
  329. }, {
  330. color: token.colorLinkActive
  331. })), genGhostButtonStyle(token.componentCls, token.ghostBg, token.colorInfo, token.colorInfo, token.colorTextDisabled, token.colorBorder, {
  332. color: token.colorInfoHover,
  333. borderColor: token.colorInfoHover
  334. }, {
  335. color: token.colorInfoActive,
  336. borderColor: token.colorInfoActive
  337. }));
  338. const genColorButtonStyle = token => {
  339. const {
  340. componentCls
  341. } = token;
  342. return Object.assign({
  343. [`${componentCls}-color-default`]: genDefaultButtonStyle(token),
  344. [`${componentCls}-color-primary`]: genPrimaryButtonStyle(token),
  345. [`${componentCls}-color-dangerous`]: genDangerousStyle(token),
  346. [`${componentCls}-color-link`]: genLinkStyle(token)
  347. }, genPresetColorStyle(token));
  348. };
  349. // =========== Compatible with versions earlier than 5.21.0 ===========
  350. const genCompatibleButtonStyle = token => Object.assign(Object.assign(Object.assign(Object.assign({}, genOutlinedDashedButtonStyle(token, token.defaultBorderColor, token.defaultBg, {
  351. color: token.defaultHoverColor,
  352. borderColor: token.defaultHoverBorderColor,
  353. background: token.defaultHoverBg
  354. }, {
  355. color: token.defaultActiveColor,
  356. borderColor: token.defaultActiveBorderColor,
  357. background: token.defaultActiveBg
  358. })), genTextLinkButtonStyle(token, token.textTextColor, 'text', {
  359. color: token.textTextHoverColor,
  360. background: token.textHoverBg
  361. }, {
  362. color: token.textTextActiveColor,
  363. background: token.colorBgTextActive
  364. })), genSolidButtonStyle(token, token.primaryColor, token.colorPrimary, {
  365. background: token.colorPrimaryHover,
  366. color: token.primaryColor
  367. }, {
  368. background: token.colorPrimaryActive,
  369. color: token.primaryColor
  370. })), genTextLinkButtonStyle(token, token.colorLink, 'link', {
  371. color: token.colorLinkHover,
  372. background: token.linkHoverBg
  373. }, {
  374. color: token.colorLinkActive
  375. }));
  376. // =============================== Size ===============================
  377. const genButtonStyle = (token, prefixCls = '') => {
  378. const {
  379. componentCls,
  380. controlHeight,
  381. fontSize,
  382. borderRadius,
  383. buttonPaddingHorizontal,
  384. iconCls,
  385. buttonPaddingVertical,
  386. buttonIconOnlyFontSize
  387. } = token;
  388. return [{
  389. [prefixCls]: {
  390. fontSize,
  391. height: controlHeight,
  392. padding: `${unit(buttonPaddingVertical)} ${unit(buttonPaddingHorizontal)}`,
  393. borderRadius,
  394. [`&${componentCls}-icon-only`]: {
  395. width: controlHeight,
  396. [iconCls]: {
  397. fontSize: buttonIconOnlyFontSize
  398. }
  399. }
  400. }
  401. },
  402. // Shape - patch prefixCls again to override solid border radius style
  403. {
  404. [`${componentCls}${componentCls}-circle${prefixCls}`]: genCircleButtonStyle(token)
  405. }, {
  406. [`${componentCls}${componentCls}-round${prefixCls}`]: {
  407. borderRadius: token.controlHeight,
  408. [`&:not(${componentCls}-icon-only)`]: {
  409. paddingInline: token.buttonPaddingHorizontal
  410. }
  411. }
  412. }];
  413. };
  414. const genSizeBaseButtonStyle = token => {
  415. const baseToken = mergeToken(token, {
  416. fontSize: token.contentFontSize
  417. });
  418. return genButtonStyle(baseToken, token.componentCls);
  419. };
  420. const genSizeSmallButtonStyle = token => {
  421. const smallToken = mergeToken(token, {
  422. controlHeight: token.controlHeightSM,
  423. fontSize: token.contentFontSizeSM,
  424. padding: token.paddingXS,
  425. buttonPaddingHorizontal: token.paddingInlineSM,
  426. buttonPaddingVertical: 0,
  427. borderRadius: token.borderRadiusSM,
  428. buttonIconOnlyFontSize: token.onlyIconSizeSM
  429. });
  430. return genButtonStyle(smallToken, `${token.componentCls}-sm`);
  431. };
  432. const genSizeLargeButtonStyle = token => {
  433. const largeToken = mergeToken(token, {
  434. controlHeight: token.controlHeightLG,
  435. fontSize: token.contentFontSizeLG,
  436. buttonPaddingHorizontal: token.paddingInlineLG,
  437. buttonPaddingVertical: 0,
  438. borderRadius: token.borderRadiusLG,
  439. buttonIconOnlyFontSize: token.onlyIconSizeLG
  440. });
  441. return genButtonStyle(largeToken, `${token.componentCls}-lg`);
  442. };
  443. const genBlockButtonStyle = token => {
  444. const {
  445. componentCls
  446. } = token;
  447. return {
  448. [componentCls]: {
  449. [`&${componentCls}-block`]: {
  450. width: '100%'
  451. }
  452. }
  453. };
  454. };
  455. // ============================== Export ==============================
  456. export default genStyleHooks('Button', token => {
  457. const buttonToken = prepareToken(token);
  458. return [
  459. // Shared
  460. genSharedButtonStyle(buttonToken),
  461. // Size
  462. genSizeBaseButtonStyle(buttonToken), genSizeSmallButtonStyle(buttonToken), genSizeLargeButtonStyle(buttonToken),
  463. // Block
  464. genBlockButtonStyle(buttonToken),
  465. // Color
  466. genColorButtonStyle(buttonToken),
  467. // https://github.com/ant-design/ant-design/issues/50969
  468. genCompatibleButtonStyle(buttonToken),
  469. // Button Group
  470. genGroupStyle(buttonToken)];
  471. }, prepareComponentToken, {
  472. unitless: {
  473. fontWeight: true,
  474. contentLineHeight: true,
  475. contentLineHeightSM: true,
  476. contentLineHeightLG: true
  477. }
  478. });