index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import { TinyColor } from '@ctrl/tinycolor';
  3. import { genCollapseMotion, initSlideMotion, initZoomMotion } from '../../style/motion';
  4. import { genComponentStyleHook, mergeToken } from '../../theme/internal';
  5. import getHorizontalStyle from './horizontal';
  6. import getRTLStyle from './rtl';
  7. import getThemeStyle from './theme';
  8. import getVerticalStyle from './vertical';
  9. import { clearFix, resetComponent, resetIcon } from '../../style';
  10. import 'vue';
  11. const genMenuItemStyle = token => {
  12. const {
  13. componentCls,
  14. fontSize,
  15. motionDurationSlow,
  16. motionDurationMid,
  17. motionEaseInOut,
  18. motionEaseOut,
  19. iconCls,
  20. controlHeightSM
  21. } = token;
  22. return {
  23. // >>>>> Item
  24. [`${componentCls}-item, ${componentCls}-submenu-title`]: {
  25. position: 'relative',
  26. display: 'block',
  27. margin: 0,
  28. whiteSpace: 'nowrap',
  29. cursor: 'pointer',
  30. transition: [`border-color ${motionDurationSlow}`, `background ${motionDurationSlow}`, `padding ${motionDurationSlow} ${motionEaseInOut}`].join(','),
  31. [`${componentCls}-item-icon, ${iconCls}`]: {
  32. minWidth: fontSize,
  33. fontSize,
  34. transition: [`font-size ${motionDurationMid} ${motionEaseOut}`, `margin ${motionDurationSlow} ${motionEaseInOut}`, `color ${motionDurationSlow}`].join(','),
  35. '+ span': {
  36. marginInlineStart: controlHeightSM - fontSize,
  37. opacity: 1,
  38. transition: [`opacity ${motionDurationSlow} ${motionEaseInOut}`, `margin ${motionDurationSlow}`, `color ${motionDurationSlow}`].join(',')
  39. }
  40. },
  41. [`${componentCls}-item-icon`]: _extends({}, resetIcon()),
  42. [`&${componentCls}-item-only-child`]: {
  43. [`> ${iconCls}, > ${componentCls}-item-icon`]: {
  44. marginInlineEnd: 0
  45. }
  46. }
  47. },
  48. // Disabled state sets text to gray and nukes hover/tab effects
  49. [`${componentCls}-item-disabled, ${componentCls}-submenu-disabled`]: {
  50. background: 'none !important',
  51. cursor: 'not-allowed',
  52. '&::after': {
  53. borderColor: 'transparent !important'
  54. },
  55. a: {
  56. color: 'inherit !important'
  57. },
  58. [`> ${componentCls}-submenu-title`]: {
  59. color: 'inherit !important',
  60. cursor: 'not-allowed'
  61. }
  62. }
  63. };
  64. };
  65. const genSubMenuArrowStyle = token => {
  66. const {
  67. componentCls,
  68. motionDurationSlow,
  69. motionEaseInOut,
  70. borderRadius,
  71. menuArrowSize,
  72. menuArrowOffset
  73. } = token;
  74. return {
  75. [`${componentCls}-submenu`]: {
  76. [`&-expand-icon, &-arrow`]: {
  77. position: 'absolute',
  78. top: '50%',
  79. insetInlineEnd: token.margin,
  80. width: menuArrowSize,
  81. color: 'currentcolor',
  82. transform: 'translateY(-50%)',
  83. transition: `transform ${motionDurationSlow} ${motionEaseInOut}, opacity ${motionDurationSlow}`
  84. },
  85. '&-arrow': {
  86. // →
  87. '&::before, &::after': {
  88. position: 'absolute',
  89. width: menuArrowSize * 0.6,
  90. height: menuArrowSize * 0.15,
  91. backgroundColor: 'currentcolor',
  92. borderRadius,
  93. transition: [`background ${motionDurationSlow} ${motionEaseInOut}`, `transform ${motionDurationSlow} ${motionEaseInOut}`, `top ${motionDurationSlow} ${motionEaseInOut}`, `color ${motionDurationSlow} ${motionEaseInOut}`].join(','),
  94. content: '""'
  95. },
  96. '&::before': {
  97. transform: `rotate(45deg) translateY(-${menuArrowOffset})`
  98. },
  99. '&::after': {
  100. transform: `rotate(-45deg) translateY(${menuArrowOffset})`
  101. }
  102. }
  103. }
  104. };
  105. };
  106. // =============================== Base ===============================
  107. const getBaseStyle = token => {
  108. const {
  109. antCls,
  110. componentCls,
  111. fontSize,
  112. motionDurationSlow,
  113. motionDurationMid,
  114. motionEaseInOut,
  115. lineHeight,
  116. paddingXS,
  117. padding,
  118. colorSplit,
  119. lineWidth,
  120. zIndexPopup,
  121. borderRadiusLG,
  122. radiusSubMenuItem,
  123. menuArrowSize,
  124. menuArrowOffset,
  125. lineType,
  126. menuPanelMaskInset
  127. } = token;
  128. return [
  129. // Misc
  130. {
  131. '': {
  132. [`${componentCls}`]: _extends(_extends({}, clearFix()), {
  133. // Hidden
  134. [`&-hidden`]: {
  135. display: 'none'
  136. }
  137. })
  138. },
  139. [`${componentCls}-submenu-hidden`]: {
  140. display: 'none'
  141. }
  142. }, {
  143. [componentCls]: _extends(_extends(_extends(_extends(_extends(_extends(_extends({}, resetComponent(token)), clearFix()), {
  144. marginBottom: 0,
  145. paddingInlineStart: 0,
  146. // Override default ul/ol
  147. fontSize,
  148. lineHeight: 0,
  149. listStyle: 'none',
  150. outline: 'none',
  151. transition: `width ${motionDurationSlow} cubic-bezier(0.2, 0, 0, 1) 0s`,
  152. [`ul, ol`]: {
  153. margin: 0,
  154. padding: 0,
  155. listStyle: 'none'
  156. },
  157. // Overflow ellipsis
  158. [`&-overflow`]: {
  159. display: 'flex',
  160. [`${componentCls}-item`]: {
  161. flex: 'none'
  162. }
  163. },
  164. [`${componentCls}-item, ${componentCls}-submenu, ${componentCls}-submenu-title`]: {
  165. borderRadius: token.radiusItem
  166. },
  167. [`${componentCls}-item-group-title`]: {
  168. padding: `${paddingXS}px ${padding}px`,
  169. fontSize,
  170. lineHeight,
  171. transition: `all ${motionDurationSlow}`
  172. },
  173. [`&-horizontal ${componentCls}-submenu`]: {
  174. transition: [`border-color ${motionDurationSlow} ${motionEaseInOut}`, `background ${motionDurationSlow} ${motionEaseInOut}`].join(',')
  175. },
  176. [`${componentCls}-submenu, ${componentCls}-submenu-inline`]: {
  177. transition: [`border-color ${motionDurationSlow} ${motionEaseInOut}`, `background ${motionDurationSlow} ${motionEaseInOut}`, `padding ${motionDurationMid} ${motionEaseInOut}`].join(',')
  178. },
  179. [`${componentCls}-submenu ${componentCls}-sub`]: {
  180. cursor: 'initial',
  181. transition: [`background ${motionDurationSlow} ${motionEaseInOut}`, `padding ${motionDurationSlow} ${motionEaseInOut}`].join(',')
  182. },
  183. [`${componentCls}-title-content`]: {
  184. transition: `color ${motionDurationSlow}`
  185. },
  186. [`${componentCls}-item a`]: {
  187. '&::before': {
  188. position: 'absolute',
  189. inset: 0,
  190. backgroundColor: 'transparent',
  191. content: '""'
  192. }
  193. },
  194. // Removed a Badge related style seems it's safe
  195. // https://github.com/ant-design/ant-design/issues/19809
  196. // >>>>> Divider
  197. [`${componentCls}-item-divider`]: {
  198. overflow: 'hidden',
  199. lineHeight: 0,
  200. borderColor: colorSplit,
  201. borderStyle: lineType,
  202. borderWidth: 0,
  203. borderTopWidth: lineWidth,
  204. marginBlock: lineWidth,
  205. padding: 0,
  206. '&-dashed': {
  207. borderStyle: 'dashed'
  208. }
  209. }
  210. }), genMenuItemStyle(token)), {
  211. [`${componentCls}-item-group`]: {
  212. [`${componentCls}-item-group-list`]: {
  213. margin: 0,
  214. padding: 0,
  215. [`${componentCls}-item, ${componentCls}-submenu-title`]: {
  216. paddingInline: `${fontSize * 2}px ${padding}px`
  217. }
  218. }
  219. },
  220. // ======================= Sub Menu =======================
  221. '&-submenu': {
  222. '&-popup': {
  223. position: 'absolute',
  224. zIndex: zIndexPopup,
  225. background: 'transparent',
  226. borderRadius: borderRadiusLG,
  227. boxShadow: 'none',
  228. transformOrigin: '0 0',
  229. // https://github.com/ant-design/ant-design/issues/13955
  230. '&::before': {
  231. position: 'absolute',
  232. inset: `${menuPanelMaskInset}px 0 0`,
  233. zIndex: -1,
  234. width: '100%',
  235. height: '100%',
  236. opacity: 0,
  237. content: '""'
  238. }
  239. },
  240. // https://github.com/ant-design/ant-design/issues/13955
  241. '&-placement-rightTop::before': {
  242. top: 0,
  243. insetInlineStart: menuPanelMaskInset
  244. },
  245. [`> ${componentCls}`]: _extends(_extends(_extends({
  246. borderRadius: borderRadiusLG
  247. }, genMenuItemStyle(token)), genSubMenuArrowStyle(token)), {
  248. [`${componentCls}-item, ${componentCls}-submenu > ${componentCls}-submenu-title`]: {
  249. borderRadius: radiusSubMenuItem
  250. },
  251. [`${componentCls}-submenu-title::after`]: {
  252. transition: `transform ${motionDurationSlow} ${motionEaseInOut}`
  253. }
  254. })
  255. }
  256. }), genSubMenuArrowStyle(token)), {
  257. [`&-inline-collapsed ${componentCls}-submenu-arrow,
  258. &-inline ${componentCls}-submenu-arrow`]: {
  259. // ↓
  260. '&::before': {
  261. transform: `rotate(-45deg) translateX(${menuArrowOffset})`
  262. },
  263. '&::after': {
  264. transform: `rotate(45deg) translateX(-${menuArrowOffset})`
  265. }
  266. },
  267. [`${componentCls}-submenu-open${componentCls}-submenu-inline > ${componentCls}-submenu-title > ${componentCls}-submenu-arrow`]: {
  268. // ↑
  269. transform: `translateY(-${menuArrowSize * 0.2}px)`,
  270. '&::after': {
  271. transform: `rotate(-45deg) translateX(-${menuArrowOffset})`
  272. },
  273. '&::before': {
  274. transform: `rotate(45deg) translateX(${menuArrowOffset})`
  275. }
  276. }
  277. })
  278. },
  279. // Integration with header element so menu items have the same height
  280. {
  281. [`${antCls}-layout-header`]: {
  282. [componentCls]: {
  283. lineHeight: 'inherit'
  284. }
  285. }
  286. }];
  287. };
  288. // ============================== Export ==============================
  289. export default ((prefixCls, injectStyle) => {
  290. const useOriginHook = genComponentStyleHook('Menu', (token, _ref) => {
  291. let {
  292. overrideComponentToken
  293. } = _ref;
  294. // Dropdown will handle menu style self. We do not need to handle this.
  295. if ((injectStyle === null || injectStyle === void 0 ? void 0 : injectStyle.value) === false) {
  296. return [];
  297. }
  298. const {
  299. colorBgElevated,
  300. colorPrimary,
  301. colorError,
  302. colorErrorHover,
  303. colorTextLightSolid
  304. } = token;
  305. const {
  306. controlHeightLG,
  307. fontSize
  308. } = token;
  309. const menuArrowSize = fontSize / 7 * 5;
  310. // Menu Token
  311. const menuToken = mergeToken(token, {
  312. menuItemHeight: controlHeightLG,
  313. menuItemPaddingInline: token.margin,
  314. menuArrowSize,
  315. menuHorizontalHeight: controlHeightLG * 1.15,
  316. menuArrowOffset: `${menuArrowSize * 0.25}px`,
  317. menuPanelMaskInset: -7,
  318. menuSubMenuBg: colorBgElevated
  319. });
  320. const colorTextDark = new TinyColor(colorTextLightSolid).setAlpha(0.65).toRgbString();
  321. const menuDarkToken = mergeToken(menuToken, {
  322. colorItemText: colorTextDark,
  323. colorItemTextHover: colorTextLightSolid,
  324. colorGroupTitle: colorTextDark,
  325. colorItemTextSelected: colorTextLightSolid,
  326. colorItemBg: '#001529',
  327. colorSubItemBg: '#000c17',
  328. colorItemBgActive: 'transparent',
  329. colorItemBgSelected: colorPrimary,
  330. colorActiveBarWidth: 0,
  331. colorActiveBarHeight: 0,
  332. colorActiveBarBorderSize: 0,
  333. // Disabled
  334. colorItemTextDisabled: new TinyColor(colorTextLightSolid).setAlpha(0.25).toRgbString(),
  335. // Danger
  336. colorDangerItemText: colorError,
  337. colorDangerItemTextHover: colorErrorHover,
  338. colorDangerItemTextSelected: colorTextLightSolid,
  339. colorDangerItemBgActive: colorError,
  340. colorDangerItemBgSelected: colorError,
  341. menuSubMenuBg: '#001529',
  342. // Horizontal
  343. colorItemTextSelectedHorizontal: colorTextLightSolid,
  344. colorItemBgSelectedHorizontal: colorPrimary
  345. }, _extends({}, overrideComponentToken));
  346. return [
  347. // Basic
  348. getBaseStyle(menuToken),
  349. // Horizontal
  350. getHorizontalStyle(menuToken),
  351. // Vertical
  352. getVerticalStyle(menuToken),
  353. // Theme
  354. getThemeStyle(menuToken, 'light'), getThemeStyle(menuDarkToken, 'dark'),
  355. // RTL
  356. getRTLStyle(menuToken),
  357. // Motion
  358. genCollapseMotion(menuToken), initSlideMotion(menuToken, 'slide-up'), initSlideMotion(menuToken, 'slide-down'), initZoomMotion(menuToken, 'zoom-big')];
  359. }, token => {
  360. const {
  361. colorPrimary,
  362. colorError,
  363. colorTextDisabled,
  364. colorErrorBg,
  365. colorText,
  366. colorTextDescription,
  367. colorBgContainer,
  368. colorFillAlter,
  369. colorFillContent,
  370. lineWidth,
  371. lineWidthBold,
  372. controlItemBgActive,
  373. colorBgTextHover
  374. } = token;
  375. return {
  376. dropdownWidth: 160,
  377. zIndexPopup: token.zIndexPopupBase + 50,
  378. radiusItem: token.borderRadiusLG,
  379. radiusSubMenuItem: token.borderRadiusSM,
  380. colorItemText: colorText,
  381. colorItemTextHover: colorText,
  382. colorItemTextHoverHorizontal: colorPrimary,
  383. colorGroupTitle: colorTextDescription,
  384. colorItemTextSelected: colorPrimary,
  385. colorItemTextSelectedHorizontal: colorPrimary,
  386. colorItemBg: colorBgContainer,
  387. colorItemBgHover: colorBgTextHover,
  388. colorItemBgActive: colorFillContent,
  389. colorSubItemBg: colorFillAlter,
  390. colorItemBgSelected: controlItemBgActive,
  391. colorItemBgSelectedHorizontal: 'transparent',
  392. colorActiveBarWidth: 0,
  393. colorActiveBarHeight: lineWidthBold,
  394. colorActiveBarBorderSize: lineWidth,
  395. // Disabled
  396. colorItemTextDisabled: colorTextDisabled,
  397. // Danger
  398. colorDangerItemText: colorError,
  399. colorDangerItemTextHover: colorError,
  400. colorDangerItemTextSelected: colorError,
  401. colorDangerItemBgActive: colorErrorBg,
  402. colorDangerItemBgSelected: colorErrorBg,
  403. itemMarginInline: token.marginXXS
  404. };
  405. });
  406. return useOriginHook(prefixCls);
  407. });