OperationNode.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  3. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  4. import classNames from 'classnames';
  5. import Dropdown from 'rc-dropdown';
  6. import Menu, { MenuItem } from 'rc-menu';
  7. import KeyCode from "rc-util/es/KeyCode";
  8. import * as React from 'react';
  9. import { useEffect, useState } from 'react';
  10. import { getRemovable } from "../util";
  11. import AddButton from "./AddButton";
  12. var OperationNode = /*#__PURE__*/React.forwardRef(function (props, ref) {
  13. var prefixCls = props.prefixCls,
  14. id = props.id,
  15. tabs = props.tabs,
  16. locale = props.locale,
  17. mobile = props.mobile,
  18. _props$more = props.more,
  19. moreProps = _props$more === void 0 ? {} : _props$more,
  20. style = props.style,
  21. className = props.className,
  22. editable = props.editable,
  23. tabBarGutter = props.tabBarGutter,
  24. rtl = props.rtl,
  25. removeAriaLabel = props.removeAriaLabel,
  26. onTabClick = props.onTabClick,
  27. getPopupContainer = props.getPopupContainer,
  28. popupClassName = props.popupClassName;
  29. // ======================== Dropdown ========================
  30. var _useState = useState(false),
  31. _useState2 = _slicedToArray(_useState, 2),
  32. open = _useState2[0],
  33. setOpen = _useState2[1];
  34. var _useState3 = useState(null),
  35. _useState4 = _slicedToArray(_useState3, 2),
  36. selectedKey = _useState4[0],
  37. setSelectedKey = _useState4[1];
  38. var _moreProps$icon = moreProps.icon,
  39. moreIcon = _moreProps$icon === void 0 ? 'More' : _moreProps$icon;
  40. var popupId = "".concat(id, "-more-popup");
  41. var dropdownPrefix = "".concat(prefixCls, "-dropdown");
  42. var selectedItemId = selectedKey !== null ? "".concat(popupId, "-").concat(selectedKey) : null;
  43. var dropdownAriaLabel = locale === null || locale === void 0 ? void 0 : locale.dropdownAriaLabel;
  44. function onRemoveTab(event, key) {
  45. event.preventDefault();
  46. event.stopPropagation();
  47. editable.onEdit('remove', {
  48. key: key,
  49. event: event
  50. });
  51. }
  52. var menu = /*#__PURE__*/React.createElement(Menu, {
  53. onClick: function onClick(_ref) {
  54. var key = _ref.key,
  55. domEvent = _ref.domEvent;
  56. onTabClick(key, domEvent);
  57. setOpen(false);
  58. },
  59. prefixCls: "".concat(dropdownPrefix, "-menu"),
  60. id: popupId,
  61. tabIndex: -1,
  62. role: "listbox",
  63. "aria-activedescendant": selectedItemId,
  64. selectedKeys: [selectedKey],
  65. "aria-label": dropdownAriaLabel !== undefined ? dropdownAriaLabel : 'expanded dropdown'
  66. }, tabs.map(function (tab) {
  67. var closable = tab.closable,
  68. disabled = tab.disabled,
  69. closeIcon = tab.closeIcon,
  70. key = tab.key,
  71. label = tab.label;
  72. var removable = getRemovable(closable, closeIcon, editable, disabled);
  73. return /*#__PURE__*/React.createElement(MenuItem, {
  74. key: key,
  75. id: "".concat(popupId, "-").concat(key),
  76. role: "option",
  77. "aria-controls": id && "".concat(id, "-panel-").concat(key),
  78. disabled: disabled
  79. }, /*#__PURE__*/React.createElement("span", null, label), removable && /*#__PURE__*/React.createElement("button", {
  80. type: "button",
  81. "aria-label": removeAriaLabel || 'remove',
  82. tabIndex: 0,
  83. className: "".concat(dropdownPrefix, "-menu-item-remove"),
  84. onClick: function onClick(e) {
  85. e.stopPropagation();
  86. onRemoveTab(e, key);
  87. }
  88. }, closeIcon || editable.removeIcon || '×'));
  89. }));
  90. function selectOffset(offset) {
  91. var enabledTabs = tabs.filter(function (tab) {
  92. return !tab.disabled;
  93. });
  94. var selectedIndex = enabledTabs.findIndex(function (tab) {
  95. return tab.key === selectedKey;
  96. }) || 0;
  97. var len = enabledTabs.length;
  98. for (var i = 0; i < len; i += 1) {
  99. selectedIndex = (selectedIndex + offset + len) % len;
  100. var tab = enabledTabs[selectedIndex];
  101. if (!tab.disabled) {
  102. setSelectedKey(tab.key);
  103. return;
  104. }
  105. }
  106. }
  107. function onKeyDown(e) {
  108. var which = e.which;
  109. if (!open) {
  110. if ([KeyCode.DOWN, KeyCode.SPACE, KeyCode.ENTER].includes(which)) {
  111. setOpen(true);
  112. e.preventDefault();
  113. }
  114. return;
  115. }
  116. switch (which) {
  117. case KeyCode.UP:
  118. selectOffset(-1);
  119. e.preventDefault();
  120. break;
  121. case KeyCode.DOWN:
  122. selectOffset(1);
  123. e.preventDefault();
  124. break;
  125. case KeyCode.ESC:
  126. setOpen(false);
  127. break;
  128. case KeyCode.SPACE:
  129. case KeyCode.ENTER:
  130. if (selectedKey !== null) {
  131. onTabClick(selectedKey, e);
  132. }
  133. break;
  134. }
  135. }
  136. // ========================= Effect =========================
  137. useEffect(function () {
  138. // We use query element here to avoid React strict warning
  139. var ele = document.getElementById(selectedItemId);
  140. if (ele && ele.scrollIntoView) {
  141. ele.scrollIntoView(false);
  142. }
  143. }, [selectedKey]);
  144. useEffect(function () {
  145. if (!open) {
  146. setSelectedKey(null);
  147. }
  148. }, [open]);
  149. // ========================= Render =========================
  150. var moreStyle = _defineProperty({}, rtl ? 'marginRight' : 'marginLeft', tabBarGutter);
  151. if (!tabs.length) {
  152. moreStyle.visibility = 'hidden';
  153. moreStyle.order = 1;
  154. }
  155. var overlayClassName = classNames(_defineProperty({}, "".concat(dropdownPrefix, "-rtl"), rtl));
  156. var moreNode = mobile ? null : /*#__PURE__*/React.createElement(Dropdown, _extends({
  157. prefixCls: dropdownPrefix,
  158. overlay: menu,
  159. visible: tabs.length ? open : false,
  160. onVisibleChange: setOpen,
  161. overlayClassName: classNames(overlayClassName, popupClassName),
  162. mouseEnterDelay: 0.1,
  163. mouseLeaveDelay: 0.1,
  164. getPopupContainer: getPopupContainer
  165. }, moreProps), /*#__PURE__*/React.createElement("button", {
  166. type: "button",
  167. className: "".concat(prefixCls, "-nav-more"),
  168. style: moreStyle,
  169. "aria-haspopup": "listbox",
  170. "aria-controls": popupId,
  171. id: "".concat(id, "-more"),
  172. "aria-expanded": open,
  173. onKeyDown: onKeyDown
  174. }, moreIcon));
  175. return /*#__PURE__*/React.createElement("div", {
  176. className: classNames("".concat(prefixCls, "-nav-operations"), className),
  177. style: style,
  178. ref: ref
  179. }, moreNode, /*#__PURE__*/React.createElement(AddButton, {
  180. prefixCls: prefixCls,
  181. locale: locale,
  182. editable: editable
  183. }));
  184. });
  185. export default /*#__PURE__*/React.memo(OperationNode, function (_, next) {
  186. return (
  187. // https://github.com/ant-design/ant-design/issues/32544
  188. // We'd better remove syntactic sugar in `rc-menu` since this has perf issue
  189. next.tabMoving
  190. );
  191. });