index.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import { Keyframes, unit } from '@ant-design/cssinjs';
  2. import { resetComponent } from '../../style';
  3. import { genPresetColor, genStyleHooks, mergeToken } from '../../theme/internal';
  4. const antStatusProcessing = new Keyframes('antStatusProcessing', {
  5. '0%': {
  6. transform: 'scale(0.8)',
  7. opacity: 0.5
  8. },
  9. '100%': {
  10. transform: 'scale(2.4)',
  11. opacity: 0
  12. }
  13. });
  14. const antZoomBadgeIn = new Keyframes('antZoomBadgeIn', {
  15. '0%': {
  16. transform: 'scale(0) translate(50%, -50%)',
  17. opacity: 0
  18. },
  19. '100%': {
  20. transform: 'scale(1) translate(50%, -50%)'
  21. }
  22. });
  23. const antZoomBadgeOut = new Keyframes('antZoomBadgeOut', {
  24. '0%': {
  25. transform: 'scale(1) translate(50%, -50%)'
  26. },
  27. '100%': {
  28. transform: 'scale(0) translate(50%, -50%)',
  29. opacity: 0
  30. }
  31. });
  32. const antNoWrapperZoomBadgeIn = new Keyframes('antNoWrapperZoomBadgeIn', {
  33. '0%': {
  34. transform: 'scale(0)',
  35. opacity: 0
  36. },
  37. '100%': {
  38. transform: 'scale(1)'
  39. }
  40. });
  41. const antNoWrapperZoomBadgeOut = new Keyframes('antNoWrapperZoomBadgeOut', {
  42. '0%': {
  43. transform: 'scale(1)'
  44. },
  45. '100%': {
  46. transform: 'scale(0)',
  47. opacity: 0
  48. }
  49. });
  50. const antBadgeLoadingCircle = new Keyframes('antBadgeLoadingCircle', {
  51. '0%': {
  52. transformOrigin: '50%'
  53. },
  54. '100%': {
  55. transform: 'translate(50%, -50%) rotate(360deg)',
  56. transformOrigin: '50%'
  57. }
  58. });
  59. const genSharedBadgeStyle = token => {
  60. const {
  61. componentCls,
  62. iconCls,
  63. antCls,
  64. badgeShadowSize,
  65. textFontSize,
  66. textFontSizeSM,
  67. statusSize,
  68. dotSize,
  69. textFontWeight,
  70. indicatorHeight,
  71. indicatorHeightSM,
  72. marginXS,
  73. calc
  74. } = token;
  75. const numberPrefixCls = `${antCls}-scroll-number`;
  76. const colorPreset = genPresetColor(token, (colorKey, {
  77. darkColor
  78. }) => ({
  79. [`&${componentCls} ${componentCls}-color-${colorKey}`]: {
  80. background: darkColor,
  81. [`&:not(${componentCls}-count)`]: {
  82. color: darkColor
  83. },
  84. 'a:hover &': {
  85. background: darkColor
  86. }
  87. }
  88. }));
  89. return {
  90. [componentCls]: Object.assign(Object.assign(Object.assign(Object.assign({}, resetComponent(token)), {
  91. position: 'relative',
  92. display: 'inline-block',
  93. width: 'fit-content',
  94. lineHeight: 1,
  95. [`${componentCls}-count`]: {
  96. display: 'inline-flex',
  97. justifyContent: 'center',
  98. zIndex: token.indicatorZIndex,
  99. minWidth: indicatorHeight,
  100. height: indicatorHeight,
  101. color: token.badgeTextColor,
  102. fontWeight: textFontWeight,
  103. fontSize: textFontSize,
  104. lineHeight: unit(indicatorHeight),
  105. whiteSpace: 'nowrap',
  106. textAlign: 'center',
  107. background: token.badgeColor,
  108. borderRadius: calc(indicatorHeight).div(2).equal(),
  109. boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`,
  110. transition: `background ${token.motionDurationMid}`,
  111. a: {
  112. color: token.badgeTextColor
  113. },
  114. 'a:hover': {
  115. color: token.badgeTextColor
  116. },
  117. 'a:hover &': {
  118. background: token.badgeColorHover
  119. }
  120. },
  121. [`${componentCls}-count-sm`]: {
  122. minWidth: indicatorHeightSM,
  123. height: indicatorHeightSM,
  124. fontSize: textFontSizeSM,
  125. lineHeight: unit(indicatorHeightSM),
  126. borderRadius: calc(indicatorHeightSM).div(2).equal()
  127. },
  128. [`${componentCls}-multiple-words`]: {
  129. padding: `0 ${unit(token.paddingXS)}`,
  130. bdi: {
  131. unicodeBidi: 'plaintext'
  132. }
  133. },
  134. [`${componentCls}-dot`]: {
  135. zIndex: token.indicatorZIndex,
  136. width: dotSize,
  137. minWidth: dotSize,
  138. height: dotSize,
  139. background: token.badgeColor,
  140. borderRadius: '100%',
  141. boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`
  142. },
  143. [`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
  144. position: 'absolute',
  145. top: 0,
  146. insetInlineEnd: 0,
  147. transform: 'translate(50%, -50%)',
  148. transformOrigin: '100% 0%',
  149. [`&${iconCls}-spin`]: {
  150. animationName: antBadgeLoadingCircle,
  151. animationDuration: '1s',
  152. animationIterationCount: 'infinite',
  153. animationTimingFunction: 'linear'
  154. }
  155. },
  156. [`&${componentCls}-status`]: {
  157. lineHeight: 'inherit',
  158. verticalAlign: 'baseline',
  159. [`${componentCls}-status-dot`]: {
  160. position: 'relative',
  161. top: -1,
  162. // Magic number, but seems better experience
  163. display: 'inline-block',
  164. width: statusSize,
  165. height: statusSize,
  166. verticalAlign: 'middle',
  167. borderRadius: '50%'
  168. },
  169. [`${componentCls}-status-success`]: {
  170. backgroundColor: token.colorSuccess
  171. },
  172. [`${componentCls}-status-processing`]: {
  173. overflow: 'visible',
  174. color: token.colorInfo,
  175. backgroundColor: token.colorInfo,
  176. borderColor: 'currentcolor',
  177. '&::after': {
  178. position: 'absolute',
  179. top: 0,
  180. insetInlineStart: 0,
  181. width: '100%',
  182. height: '100%',
  183. borderWidth: badgeShadowSize,
  184. borderStyle: 'solid',
  185. borderColor: 'inherit',
  186. borderRadius: '50%',
  187. animationName: antStatusProcessing,
  188. animationDuration: token.badgeProcessingDuration,
  189. animationIterationCount: 'infinite',
  190. animationTimingFunction: 'ease-in-out',
  191. content: '""'
  192. }
  193. },
  194. [`${componentCls}-status-default`]: {
  195. backgroundColor: token.colorTextPlaceholder
  196. },
  197. [`${componentCls}-status-error`]: {
  198. backgroundColor: token.colorError
  199. },
  200. [`${componentCls}-status-warning`]: {
  201. backgroundColor: token.colorWarning
  202. },
  203. [`${componentCls}-status-text`]: {
  204. marginInlineStart: marginXS,
  205. color: token.colorText,
  206. fontSize: token.fontSize
  207. }
  208. }
  209. }), colorPreset), {
  210. [`${componentCls}-zoom-appear, ${componentCls}-zoom-enter`]: {
  211. animationName: antZoomBadgeIn,
  212. animationDuration: token.motionDurationSlow,
  213. animationTimingFunction: token.motionEaseOutBack,
  214. animationFillMode: 'both'
  215. },
  216. [`${componentCls}-zoom-leave`]: {
  217. animationName: antZoomBadgeOut,
  218. animationDuration: token.motionDurationSlow,
  219. animationTimingFunction: token.motionEaseOutBack,
  220. animationFillMode: 'both'
  221. },
  222. [`&${componentCls}-not-a-wrapper`]: {
  223. [`${componentCls}-zoom-appear, ${componentCls}-zoom-enter`]: {
  224. animationName: antNoWrapperZoomBadgeIn,
  225. animationDuration: token.motionDurationSlow,
  226. animationTimingFunction: token.motionEaseOutBack
  227. },
  228. [`${componentCls}-zoom-leave`]: {
  229. animationName: antNoWrapperZoomBadgeOut,
  230. animationDuration: token.motionDurationSlow,
  231. animationTimingFunction: token.motionEaseOutBack
  232. },
  233. [`&:not(${componentCls}-status)`]: {
  234. verticalAlign: 'middle'
  235. },
  236. [`${numberPrefixCls}-custom-component, ${componentCls}-count`]: {
  237. transform: 'none'
  238. },
  239. [`${numberPrefixCls}-custom-component, ${numberPrefixCls}`]: {
  240. position: 'relative',
  241. top: 'auto',
  242. display: 'block',
  243. transformOrigin: '50% 50%'
  244. }
  245. },
  246. [numberPrefixCls]: {
  247. overflow: 'hidden',
  248. transition: `all ${token.motionDurationMid} ${token.motionEaseOutBack}`,
  249. [`${numberPrefixCls}-only`]: {
  250. position: 'relative',
  251. display: 'inline-block',
  252. height: indicatorHeight,
  253. transition: `all ${token.motionDurationSlow} ${token.motionEaseOutBack}`,
  254. WebkitTransformStyle: 'preserve-3d',
  255. WebkitBackfaceVisibility: 'hidden',
  256. [`> p${numberPrefixCls}-only-unit`]: {
  257. height: indicatorHeight,
  258. margin: 0,
  259. WebkitTransformStyle: 'preserve-3d',
  260. WebkitBackfaceVisibility: 'hidden'
  261. }
  262. },
  263. [`${numberPrefixCls}-symbol`]: {
  264. verticalAlign: 'top'
  265. }
  266. },
  267. // ====================== RTL =======================
  268. '&-rtl': {
  269. direction: 'rtl',
  270. [`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
  271. transform: 'translate(-50%, -50%)'
  272. }
  273. }
  274. })
  275. };
  276. };
  277. // ============================== Export ==============================
  278. export const prepareToken = token => {
  279. const {
  280. fontHeight,
  281. lineWidth,
  282. marginXS,
  283. colorBorderBg
  284. } = token;
  285. const badgeFontHeight = fontHeight;
  286. const badgeShadowSize = lineWidth;
  287. const badgeTextColor = token.colorTextLightSolid;
  288. const badgeColor = token.colorError;
  289. const badgeColorHover = token.colorErrorHover;
  290. const badgeToken = mergeToken(token, {
  291. badgeFontHeight,
  292. badgeShadowSize,
  293. badgeTextColor,
  294. badgeColor,
  295. badgeColorHover,
  296. badgeShadowColor: colorBorderBg,
  297. badgeProcessingDuration: '1.2s',
  298. badgeRibbonOffset: marginXS,
  299. // Follow token just by Design. Not related with token
  300. badgeRibbonCornerTransform: 'scaleY(0.75)',
  301. badgeRibbonCornerFilter: `brightness(75%)`
  302. });
  303. return badgeToken;
  304. };
  305. export const prepareComponentToken = token => {
  306. const {
  307. fontSize,
  308. lineHeight,
  309. fontSizeSM,
  310. lineWidth
  311. } = token;
  312. return {
  313. indicatorZIndex: 'auto',
  314. indicatorHeight: Math.round(fontSize * lineHeight) - 2 * lineWidth,
  315. indicatorHeightSM: fontSize,
  316. dotSize: fontSizeSM / 2,
  317. textFontSize: fontSizeSM,
  318. textFontSizeSM: fontSizeSM,
  319. textFontWeight: 'normal',
  320. statusSize: fontSizeSM / 2
  321. };
  322. };
  323. export default genStyleHooks('Badge', token => {
  324. const badgeToken = prepareToken(token);
  325. return genSharedBadgeStyle(badgeToken);
  326. }, prepareComponentToken);