index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. import { unit } from '@ant-design/cssinjs';
  2. import { resetComponent } from '../../style';
  3. import { initFadeMotion } from '../../style/motion/fade';
  4. import { genStyleHooks, mergeToken } from '../../theme/internal';
  5. import getOffset from '../util';
  6. import floatButtonGroupMotion from './keyframes';
  7. // ============================== Group ==============================
  8. const floatButtonGroupStyle = token => {
  9. const {
  10. antCls,
  11. componentCls,
  12. floatButtonSize,
  13. margin,
  14. borderRadiusLG,
  15. borderRadiusSM,
  16. badgeOffset,
  17. floatButtonBodyPadding,
  18. zIndexPopupBase,
  19. calc
  20. } = token;
  21. const groupPrefixCls = `${componentCls}-group`;
  22. return {
  23. [groupPrefixCls]: Object.assign(Object.assign({}, resetComponent(token)), {
  24. zIndex: zIndexPopupBase,
  25. display: 'flex',
  26. flexDirection: 'column',
  27. alignItems: 'center',
  28. justifyContent: 'center',
  29. border: 'none',
  30. position: 'fixed',
  31. height: 'auto',
  32. boxShadow: 'none',
  33. minWidth: floatButtonSize,
  34. minHeight: floatButtonSize,
  35. insetInlineEnd: token.floatButtonInsetInlineEnd,
  36. bottom: token.floatButtonInsetBlockEnd,
  37. borderRadius: borderRadiusLG,
  38. [`${groupPrefixCls}-wrap`]: {
  39. zIndex: -1,
  40. display: 'flex',
  41. justifyContent: 'center',
  42. alignItems: 'center',
  43. position: 'absolute'
  44. },
  45. [`&${groupPrefixCls}-rtl`]: {
  46. direction: 'rtl'
  47. },
  48. [componentCls]: {
  49. position: 'static'
  50. }
  51. }),
  52. [`${groupPrefixCls}-top > ${groupPrefixCls}-wrap`]: {
  53. flexDirection: 'column',
  54. top: 'auto',
  55. bottom: calc(floatButtonSize).add(margin).equal(),
  56. '&::after': {
  57. content: '""',
  58. position: 'absolute',
  59. width: '100%',
  60. height: margin,
  61. bottom: calc(margin).mul(-1).equal()
  62. }
  63. },
  64. [`${groupPrefixCls}-bottom > ${groupPrefixCls}-wrap`]: {
  65. flexDirection: 'column',
  66. top: calc(floatButtonSize).add(margin).equal(),
  67. bottom: 'auto',
  68. '&::after': {
  69. content: '""',
  70. position: 'absolute',
  71. width: '100%',
  72. height: margin,
  73. top: calc(margin).mul(-1).equal()
  74. }
  75. },
  76. [`${groupPrefixCls}-right > ${groupPrefixCls}-wrap`]: {
  77. flexDirection: 'row',
  78. left: {
  79. _skip_check_: true,
  80. value: calc(floatButtonSize).add(margin).equal()
  81. },
  82. right: {
  83. _skip_check_: true,
  84. value: 'auto'
  85. },
  86. '&::after': {
  87. content: '""',
  88. position: 'absolute',
  89. width: margin,
  90. height: '100%',
  91. left: {
  92. _skip_check_: true,
  93. value: calc(margin).mul(-1).equal()
  94. }
  95. }
  96. },
  97. [`${groupPrefixCls}-left > ${groupPrefixCls}-wrap`]: {
  98. flexDirection: 'row',
  99. left: {
  100. _skip_check_: true,
  101. value: 'auto'
  102. },
  103. right: {
  104. _skip_check_: true,
  105. value: calc(floatButtonSize).add(margin).equal()
  106. },
  107. '&::after': {
  108. content: '""',
  109. position: 'absolute',
  110. width: margin,
  111. height: '100%',
  112. right: {
  113. _skip_check_: true,
  114. value: calc(margin).mul(-1).equal()
  115. }
  116. }
  117. },
  118. [`${groupPrefixCls}-circle`]: {
  119. gap: margin,
  120. [`${groupPrefixCls}-wrap`]: {
  121. gap: margin
  122. }
  123. },
  124. [`${groupPrefixCls}-square`]: {
  125. [`${componentCls}-square`]: {
  126. padding: 0,
  127. borderRadius: 0,
  128. [`&${groupPrefixCls}-trigger`]: {
  129. borderRadius: borderRadiusLG
  130. },
  131. '&:first-child': {
  132. borderStartStartRadius: borderRadiusLG,
  133. borderStartEndRadius: borderRadiusLG
  134. },
  135. '&:last-child': {
  136. borderEndStartRadius: borderRadiusLG,
  137. borderEndEndRadius: borderRadiusLG
  138. },
  139. '&:not(:last-child)': {
  140. borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}`
  141. },
  142. [`${antCls}-badge`]: {
  143. [`${antCls}-badge-count`]: {
  144. top: calc(calc(floatButtonBodyPadding).add(badgeOffset)).mul(-1).equal(),
  145. insetInlineEnd: calc(calc(floatButtonBodyPadding).add(badgeOffset)).mul(-1).equal()
  146. }
  147. }
  148. },
  149. [`${groupPrefixCls}-wrap`]: {
  150. borderRadius: borderRadiusLG,
  151. boxShadow: token.boxShadowSecondary,
  152. [`${componentCls}-square`]: {
  153. boxShadow: 'none',
  154. borderRadius: 0,
  155. padding: floatButtonBodyPadding,
  156. [`${componentCls}-body`]: {
  157. width: token.floatButtonBodySize,
  158. height: token.floatButtonBodySize,
  159. borderRadius: borderRadiusSM
  160. }
  161. }
  162. }
  163. },
  164. [`${groupPrefixCls}-top > ${groupPrefixCls}-wrap, ${groupPrefixCls}-bottom > ${groupPrefixCls}-wrap`]: {
  165. [`> ${componentCls}-square`]: {
  166. '&:first-child': {
  167. borderStartStartRadius: borderRadiusLG,
  168. borderStartEndRadius: borderRadiusLG
  169. },
  170. '&:last-child': {
  171. borderEndStartRadius: borderRadiusLG,
  172. borderEndEndRadius: borderRadiusLG
  173. },
  174. '&:not(:last-child)': {
  175. borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}`
  176. }
  177. }
  178. },
  179. [`${groupPrefixCls}-left > ${groupPrefixCls}-wrap, ${groupPrefixCls}-right > ${groupPrefixCls}-wrap`]: {
  180. [`> ${componentCls}-square`]: {
  181. '&:first-child': {
  182. borderStartStartRadius: borderRadiusLG,
  183. borderEndStartRadius: borderRadiusLG
  184. },
  185. '&:last-child': {
  186. borderStartEndRadius: borderRadiusLG,
  187. borderEndEndRadius: borderRadiusLG
  188. },
  189. '&:not(:last-child)': {
  190. borderBottom: 'none',
  191. borderInlineEnd: `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}`
  192. }
  193. }
  194. },
  195. [`${groupPrefixCls}-circle-shadow`]: {
  196. boxShadow: 'none'
  197. },
  198. [`${groupPrefixCls}-square-shadow`]: {
  199. boxShadow: token.boxShadowSecondary,
  200. [`${componentCls}-square`]: {
  201. boxShadow: 'none',
  202. padding: floatButtonBodyPadding,
  203. [`${componentCls}-body`]: {
  204. width: token.floatButtonBodySize,
  205. height: token.floatButtonBodySize,
  206. borderRadius: borderRadiusSM
  207. }
  208. }
  209. }
  210. };
  211. };
  212. // ============================== Shared ==============================
  213. const sharedFloatButtonStyle = token => {
  214. const {
  215. antCls,
  216. componentCls,
  217. floatButtonBodyPadding,
  218. floatButtonIconSize,
  219. floatButtonSize,
  220. borderRadiusLG,
  221. badgeOffset,
  222. dotOffsetInSquare,
  223. dotOffsetInCircle,
  224. zIndexPopupBase,
  225. calc
  226. } = token;
  227. return {
  228. [componentCls]: Object.assign(Object.assign({}, resetComponent(token)), {
  229. border: 'none',
  230. position: 'fixed',
  231. cursor: 'pointer',
  232. zIndex: zIndexPopupBase,
  233. // Do not remove the 'display: block' here.
  234. // Deleting it will cause marginBottom to become ineffective.
  235. // Ref: https://github.com/ant-design/ant-design/issues/44700
  236. display: 'block',
  237. width: floatButtonSize,
  238. height: floatButtonSize,
  239. insetInlineEnd: token.floatButtonInsetInlineEnd,
  240. bottom: token.floatButtonInsetBlockEnd,
  241. boxShadow: token.boxShadowSecondary,
  242. // Pure Panel
  243. '&-pure': {
  244. position: 'relative',
  245. inset: 'auto'
  246. },
  247. '&:empty': {
  248. display: 'none'
  249. },
  250. [`${antCls}-badge`]: {
  251. width: '100%',
  252. height: '100%',
  253. [`${antCls}-badge-count`]: {
  254. transform: 'translate(0, 0)',
  255. transformOrigin: 'center',
  256. top: calc(badgeOffset).mul(-1).equal(),
  257. insetInlineEnd: calc(badgeOffset).mul(-1).equal()
  258. }
  259. },
  260. [`${componentCls}-body`]: {
  261. width: '100%',
  262. height: '100%',
  263. display: 'flex',
  264. justifyContent: 'center',
  265. alignItems: 'center',
  266. transition: `all ${token.motionDurationMid}`,
  267. [`${componentCls}-content`]: {
  268. overflow: 'hidden',
  269. textAlign: 'center',
  270. minHeight: floatButtonSize,
  271. display: 'flex',
  272. flexDirection: 'column',
  273. justifyContent: 'center',
  274. alignItems: 'center',
  275. padding: `${unit(calc(floatButtonBodyPadding).div(2).equal())} ${unit(floatButtonBodyPadding)}`,
  276. [`${componentCls}-icon`]: {
  277. textAlign: 'center',
  278. margin: 'auto',
  279. width: floatButtonIconSize,
  280. fontSize: floatButtonIconSize,
  281. lineHeight: 1
  282. }
  283. }
  284. }
  285. }),
  286. [`${componentCls}-rtl`]: {
  287. direction: 'rtl'
  288. },
  289. [`${componentCls}-circle`]: {
  290. height: floatButtonSize,
  291. borderRadius: '50%',
  292. [`${antCls}-badge`]: {
  293. [`${antCls}-badge-dot`]: {
  294. top: dotOffsetInCircle,
  295. insetInlineEnd: dotOffsetInCircle
  296. }
  297. },
  298. [`${componentCls}-body`]: {
  299. borderRadius: '50%'
  300. }
  301. },
  302. [`${componentCls}-square`]: {
  303. height: 'auto',
  304. minHeight: floatButtonSize,
  305. borderRadius: borderRadiusLG,
  306. [`${antCls}-badge`]: {
  307. [`${antCls}-badge-dot`]: {
  308. top: dotOffsetInSquare,
  309. insetInlineEnd: dotOffsetInSquare
  310. }
  311. },
  312. [`${componentCls}-body`]: {
  313. height: 'auto',
  314. borderRadius: borderRadiusLG
  315. }
  316. },
  317. [`${componentCls}-default`]: {
  318. backgroundColor: token.floatButtonBackgroundColor,
  319. transition: `background-color ${token.motionDurationMid}`,
  320. [`${componentCls}-body`]: {
  321. backgroundColor: token.floatButtonBackgroundColor,
  322. transition: `background-color ${token.motionDurationMid}`,
  323. '&:hover': {
  324. backgroundColor: token.colorFillContent
  325. },
  326. [`${componentCls}-content`]: {
  327. [`${componentCls}-icon`]: {
  328. color: token.colorText
  329. },
  330. [`${componentCls}-description`]: {
  331. display: 'flex',
  332. alignItems: 'center',
  333. lineHeight: unit(token.fontSizeLG),
  334. color: token.colorText,
  335. fontSize: token.fontSizeSM
  336. }
  337. }
  338. }
  339. },
  340. [`${componentCls}-primary`]: {
  341. backgroundColor: token.colorPrimary,
  342. [`${componentCls}-body`]: {
  343. backgroundColor: token.colorPrimary,
  344. transition: `background-color ${token.motionDurationMid}`,
  345. '&:hover': {
  346. backgroundColor: token.colorPrimaryHover
  347. },
  348. [`${componentCls}-content`]: {
  349. [`${componentCls}-icon`]: {
  350. color: token.colorTextLightSolid
  351. },
  352. [`${componentCls}-description`]: {
  353. display: 'flex',
  354. alignItems: 'center',
  355. lineHeight: unit(token.fontSizeLG),
  356. color: token.colorTextLightSolid,
  357. fontSize: token.fontSizeSM
  358. }
  359. }
  360. }
  361. }
  362. };
  363. };
  364. // ============================== Export ==============================
  365. export const prepareComponentToken = token => ({
  366. dotOffsetInCircle: getOffset(token.controlHeightLG / 2),
  367. dotOffsetInSquare: getOffset(token.borderRadiusLG)
  368. });
  369. export default genStyleHooks('FloatButton', token => {
  370. const {
  371. colorTextLightSolid,
  372. colorBgElevated,
  373. controlHeightLG,
  374. marginXXL,
  375. marginLG,
  376. fontSize,
  377. fontSizeIcon,
  378. controlItemBgHover,
  379. paddingXXS,
  380. calc
  381. } = token;
  382. const floatButtonToken = mergeToken(token, {
  383. floatButtonBackgroundColor: colorBgElevated,
  384. floatButtonColor: colorTextLightSolid,
  385. floatButtonHoverBackgroundColor: controlItemBgHover,
  386. floatButtonFontSize: fontSize,
  387. floatButtonIconSize: calc(fontSizeIcon).mul(1.5).equal(),
  388. floatButtonSize: controlHeightLG,
  389. floatButtonInsetBlockEnd: marginXXL,
  390. floatButtonInsetInlineEnd: marginLG,
  391. floatButtonBodySize: calc(controlHeightLG).sub(calc(paddingXXS).mul(2)).equal(),
  392. // 这里的 paddingXXS 是简写,完整逻辑是 (controlHeightLG - (controlHeightLG - paddingXXS * 2)) / 2,
  393. floatButtonBodyPadding: paddingXXS,
  394. badgeOffset: calc(paddingXXS).mul(1.5).equal()
  395. });
  396. return [floatButtonGroupStyle(floatButtonToken), sharedFloatButtonStyle(floatButtonToken), initFadeMotion(token), floatButtonGroupMotion(floatButtonToken)];
  397. }, prepareComponentToken);