index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import { unit } from '@ant-design/cssinjs';
  2. import { genFocusStyle, resetComponent } from '../../style';
  3. import { initMoveMotion, initSlideMotion, initZoomMotion, slideDownIn, slideDownOut, slideUpIn, slideUpOut } from '../../style/motion';
  4. import getArrowStyle, { getArrowOffsetToken } from '../../style/placementArrow';
  5. import { getArrowToken } from '../../style/roundedArrow';
  6. import { genStyleHooks, mergeToken } from '../../theme/internal';
  7. import genStatusStyle from './status';
  8. // =============================== Base ===============================
  9. const genBaseStyle = token => {
  10. const {
  11. componentCls,
  12. menuCls,
  13. zIndexPopup,
  14. dropdownArrowDistance,
  15. sizePopupArrow,
  16. antCls,
  17. iconCls,
  18. motionDurationMid,
  19. paddingBlock,
  20. fontSize,
  21. dropdownEdgeChildPadding,
  22. colorTextDisabled,
  23. fontSizeIcon,
  24. controlPaddingHorizontal,
  25. colorBgElevated
  26. } = token;
  27. return [{
  28. [componentCls]: {
  29. position: 'absolute',
  30. top: -9999,
  31. left: {
  32. _skip_check_: true,
  33. value: -9999
  34. },
  35. zIndex: zIndexPopup,
  36. display: 'block',
  37. // A placeholder out of dropdown visible range to avoid close when user moving
  38. '&::before': {
  39. position: 'absolute',
  40. insetBlock: token.calc(sizePopupArrow).div(2).sub(dropdownArrowDistance).equal(),
  41. // insetInlineStart: -7, // FIXME: Seems not work for hidden element
  42. zIndex: -9999,
  43. opacity: 0.0001,
  44. content: '""'
  45. },
  46. // Makes vertical dropdowns have a scrollbar once they become taller than the viewport.
  47. '&-menu-vertical': {
  48. maxHeight: '100vh',
  49. overflowY: 'auto'
  50. },
  51. [`&-trigger${antCls}-btn`]: {
  52. [`& > ${iconCls}-down, & > ${antCls}-btn-icon > ${iconCls}-down`]: {
  53. fontSize: fontSizeIcon
  54. }
  55. },
  56. [`${componentCls}-wrap`]: {
  57. position: 'relative',
  58. [`${antCls}-btn > ${iconCls}-down`]: {
  59. fontSize: fontSizeIcon
  60. },
  61. [`${iconCls}-down::before`]: {
  62. transition: `transform ${motionDurationMid}`
  63. }
  64. },
  65. [`${componentCls}-wrap-open`]: {
  66. [`${iconCls}-down::before`]: {
  67. transform: `rotate(180deg)`
  68. }
  69. },
  70. [`
  71. &-hidden,
  72. &-menu-hidden,
  73. &-menu-submenu-hidden
  74. `]: {
  75. display: 'none'
  76. },
  77. // =============================================================
  78. // == Motion ==
  79. // =============================================================
  80. // When position is not enough for dropdown, the placement will revert.
  81. // We will handle this with revert motion name.
  82. [`&${antCls}-slide-down-enter${antCls}-slide-down-enter-active${componentCls}-placement-bottomLeft,
  83. &${antCls}-slide-down-appear${antCls}-slide-down-appear-active${componentCls}-placement-bottomLeft,
  84. &${antCls}-slide-down-enter${antCls}-slide-down-enter-active${componentCls}-placement-bottom,
  85. &${antCls}-slide-down-appear${antCls}-slide-down-appear-active${componentCls}-placement-bottom,
  86. &${antCls}-slide-down-enter${antCls}-slide-down-enter-active${componentCls}-placement-bottomRight,
  87. &${antCls}-slide-down-appear${antCls}-slide-down-appear-active${componentCls}-placement-bottomRight`]: {
  88. animationName: slideUpIn
  89. },
  90. [`&${antCls}-slide-up-enter${antCls}-slide-up-enter-active${componentCls}-placement-topLeft,
  91. &${antCls}-slide-up-appear${antCls}-slide-up-appear-active${componentCls}-placement-topLeft,
  92. &${antCls}-slide-up-enter${antCls}-slide-up-enter-active${componentCls}-placement-top,
  93. &${antCls}-slide-up-appear${antCls}-slide-up-appear-active${componentCls}-placement-top,
  94. &${antCls}-slide-up-enter${antCls}-slide-up-enter-active${componentCls}-placement-topRight,
  95. &${antCls}-slide-up-appear${antCls}-slide-up-appear-active${componentCls}-placement-topRight`]: {
  96. animationName: slideDownIn
  97. },
  98. [`&${antCls}-slide-down-leave${antCls}-slide-down-leave-active${componentCls}-placement-bottomLeft,
  99. &${antCls}-slide-down-leave${antCls}-slide-down-leave-active${componentCls}-placement-bottom,
  100. &${antCls}-slide-down-leave${antCls}-slide-down-leave-active${componentCls}-placement-bottomRight`]: {
  101. animationName: slideUpOut
  102. },
  103. [`&${antCls}-slide-up-leave${antCls}-slide-up-leave-active${componentCls}-placement-topLeft,
  104. &${antCls}-slide-up-leave${antCls}-slide-up-leave-active${componentCls}-placement-top,
  105. &${antCls}-slide-up-leave${antCls}-slide-up-leave-active${componentCls}-placement-topRight`]: {
  106. animationName: slideDownOut
  107. }
  108. }
  109. },
  110. // =============================================================
  111. // == Arrow style ==
  112. // =============================================================
  113. getArrowStyle(token, colorBgElevated, {
  114. arrowPlacement: {
  115. top: true,
  116. bottom: true
  117. }
  118. }), {
  119. // =============================================================
  120. // == Menu ==
  121. // =============================================================
  122. [`${componentCls} ${menuCls}`]: {
  123. position: 'relative',
  124. margin: 0
  125. },
  126. [`${menuCls}-submenu-popup`]: {
  127. position: 'absolute',
  128. zIndex: zIndexPopup,
  129. background: 'transparent',
  130. boxShadow: 'none',
  131. transformOrigin: '0 0',
  132. 'ul, li': {
  133. listStyle: 'none',
  134. margin: 0
  135. }
  136. },
  137. [`${componentCls}, ${componentCls}-menu-submenu`]: Object.assign(Object.assign({}, resetComponent(token)), {
  138. [menuCls]: Object.assign(Object.assign({
  139. padding: dropdownEdgeChildPadding,
  140. listStyleType: 'none',
  141. backgroundColor: colorBgElevated,
  142. backgroundClip: 'padding-box',
  143. borderRadius: token.borderRadiusLG,
  144. outline: 'none',
  145. boxShadow: token.boxShadowSecondary
  146. }, genFocusStyle(token)), {
  147. '&:empty': {
  148. padding: 0,
  149. boxShadow: 'none'
  150. },
  151. [`${menuCls}-item-group-title`]: {
  152. padding: `${unit(paddingBlock)} ${unit(controlPaddingHorizontal)}`,
  153. color: token.colorTextDescription,
  154. transition: `all ${motionDurationMid}`
  155. },
  156. // ======================= Item Content =======================
  157. [`${menuCls}-item`]: {
  158. position: 'relative',
  159. display: 'flex',
  160. alignItems: 'center'
  161. },
  162. [`${menuCls}-item-icon`]: {
  163. minWidth: fontSize,
  164. marginInlineEnd: token.marginXS,
  165. fontSize: token.fontSizeSM
  166. },
  167. [`${menuCls}-title-content`]: {
  168. flex: 'auto',
  169. '&-with-extra': {
  170. display: 'inline-flex',
  171. alignItems: 'center',
  172. width: '100%'
  173. },
  174. '> a': {
  175. color: 'inherit',
  176. transition: `all ${motionDurationMid}`,
  177. '&:hover': {
  178. color: 'inherit'
  179. },
  180. '&::after': {
  181. position: 'absolute',
  182. inset: 0,
  183. content: '""'
  184. }
  185. },
  186. [`${menuCls}-item-extra`]: {
  187. paddingInlineStart: token.padding,
  188. marginInlineStart: 'auto',
  189. fontSize: token.fontSizeSM,
  190. color: token.colorTextDescription
  191. }
  192. },
  193. // =========================== Item ===========================
  194. [`${menuCls}-item, ${menuCls}-submenu-title`]: Object.assign(Object.assign({
  195. display: 'flex',
  196. margin: 0,
  197. padding: `${unit(paddingBlock)} ${unit(controlPaddingHorizontal)}`,
  198. color: token.colorText,
  199. fontWeight: 'normal',
  200. fontSize,
  201. lineHeight: token.lineHeight,
  202. cursor: 'pointer',
  203. transition: `all ${motionDurationMid}`,
  204. borderRadius: token.borderRadiusSM,
  205. '&:hover, &-active': {
  206. backgroundColor: token.controlItemBgHover
  207. }
  208. }, genFocusStyle(token)), {
  209. '&-selected': {
  210. color: token.colorPrimary,
  211. backgroundColor: token.controlItemBgActive,
  212. '&:hover, &-active': {
  213. backgroundColor: token.controlItemBgActiveHover
  214. }
  215. },
  216. '&-disabled': {
  217. color: colorTextDisabled,
  218. cursor: 'not-allowed',
  219. '&:hover': {
  220. color: colorTextDisabled,
  221. backgroundColor: colorBgElevated,
  222. cursor: 'not-allowed'
  223. },
  224. a: {
  225. pointerEvents: 'none'
  226. }
  227. },
  228. '&-divider': {
  229. height: 1,
  230. // By design
  231. margin: `${unit(token.marginXXS)} 0`,
  232. overflow: 'hidden',
  233. lineHeight: 0,
  234. backgroundColor: token.colorSplit
  235. },
  236. [`${componentCls}-menu-submenu-expand-icon`]: {
  237. position: 'absolute',
  238. insetInlineEnd: token.paddingXS,
  239. [`${componentCls}-menu-submenu-arrow-icon`]: {
  240. marginInlineEnd: '0 !important',
  241. color: token.colorIcon,
  242. fontSize: fontSizeIcon,
  243. fontStyle: 'normal'
  244. }
  245. }
  246. }),
  247. [`${menuCls}-item-group-list`]: {
  248. margin: `0 ${unit(token.marginXS)}`,
  249. padding: 0,
  250. listStyle: 'none'
  251. },
  252. [`${menuCls}-submenu-title`]: {
  253. paddingInlineEnd: token.calc(controlPaddingHorizontal).add(token.fontSizeSM).equal()
  254. },
  255. [`${menuCls}-submenu-vertical`]: {
  256. position: 'relative'
  257. },
  258. [`${menuCls}-submenu${menuCls}-submenu-disabled ${componentCls}-menu-submenu-title`]: {
  259. [`&, ${componentCls}-menu-submenu-arrow-icon`]: {
  260. color: colorTextDisabled,
  261. backgroundColor: colorBgElevated,
  262. cursor: 'not-allowed'
  263. }
  264. },
  265. // https://github.com/ant-design/ant-design/issues/19264
  266. [`${menuCls}-submenu-selected ${componentCls}-menu-submenu-title`]: {
  267. color: token.colorPrimary
  268. }
  269. })
  270. })
  271. },
  272. // Follow code may reuse in other components
  273. [initSlideMotion(token, 'slide-up'), initSlideMotion(token, 'slide-down'), initMoveMotion(token, 'move-up'), initMoveMotion(token, 'move-down'), initZoomMotion(token, 'zoom-big')]];
  274. };
  275. // ============================== Export ==============================
  276. export const prepareComponentToken = token => Object.assign(Object.assign({
  277. zIndexPopup: token.zIndexPopupBase + 50,
  278. paddingBlock: (token.controlHeight - token.fontSize * token.lineHeight) / 2
  279. }, getArrowOffsetToken({
  280. contentRadius: token.borderRadiusLG,
  281. limitVerticalRadius: true
  282. })), getArrowToken(token));
  283. export default genStyleHooks('Dropdown', token => {
  284. const {
  285. marginXXS,
  286. sizePopupArrow,
  287. paddingXXS,
  288. componentCls
  289. } = token;
  290. const dropdownToken = mergeToken(token, {
  291. menuCls: `${componentCls}-menu`,
  292. dropdownArrowDistance: token.calc(sizePopupArrow).div(2).add(marginXXS).equal(),
  293. dropdownEdgeChildPadding: paddingXXS
  294. });
  295. return [genBaseStyle(dropdownToken), genStatusStyle(dropdownToken)];
  296. }, prepareComponentToken, {
  297. resetStyle: false
  298. });