UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

172 lines (165 loc) 9.73 kB
"use strict"; var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _hooks = require("@atlaskit/editor-common/hooks"); var _ui = require("@atlaskit/editor-common/ui"); var _uiReact = require("@atlaskit/editor-common/ui-react"); var _editorSharedStyles = require("@atlaskit/editor-shared-styles"); var _cellSelection = require("@atlaskit/editor-tables/cell-selection"); var _toolbarKeyboardNavigationProvider = require("@atlaskit/editor-toolbar/toolbar-keyboard-navigation-provider"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _commands = require("../../pm-plugins/commands"); var _types = require("../../types"); var _consts = require("../consts"); var _keys = require("../TableMenu/column/keys"); var _keys2 = require("../TableMenu/row/keys"); var _consts2 = require("../TableMenu/shared/consts"); var _TableMenu = require("../TableMenu/shared/TableMenu"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } var PopupWithListeners = (0, _uiReact.withReactEditorViewOuterListeners)(_ui.Popup); // Defer drag-handle clicks to the drag handle's own toggle/select handlers — those own // the open/switch/close semantics for moving between rows/columns. var DRAG_HANDLE_CONTROLS_SELECTOR = ".".concat(_types.TableCssClassName.DRAG_ROW_CONTROLS, ", .").concat(_types.TableCssClassName.DRAG_COLUMN_CONTROLS); var NESTED_DROPDOWN_SELECTOR = '[data-toolbar-nested-dropdown-menu]'; // Marks the menu subtree that ToolbarKeyboardNavigationProvider scopes its // keyboard handling to. The provider only reacts to events whose target sits // inside this selector. var TABLE_MENU_NAV_SELECTOR = '[data-table-drag-menu-nav="true"]'; var TABLE_MENU_OFFSET = _consts.dragTableInsertColumnButtonSize + 4; var POPUP_OFFSET = [TABLE_MENU_OFFSET, 0]; /** * Row and column menu for table. */ var FloatingTableMenu = function FloatingTableMenu(_ref) { var api = _ref.api, boundariesElement = _ref.boundariesElement, editorView = _ref.editorView, mountPoint = _ref.mountPoint, scrollableElement = _ref.scrollableElement, stickyHeaders = _ref.stickyHeaders, tableWrapper = _ref.tableWrapper, targetCellPosition = _ref.targetCellPosition; var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['table'], function (states) { var _states$tableState; return { activeTableMenu: (_states$tableState = states.tableState) === null || _states$tableState === void 0 ? void 0 : _states$tableState.activeTableMenu }; }), activeTableMenu = _useSharedPluginState.activeTableMenu; var isDragMenuOpen = (activeTableMenu === null || activeTableMenu === void 0 ? void 0 : activeTableMenu.type) === 'row' || (activeTableMenu === null || activeTableMenu === void 0 ? void 0 : activeTableMenu.type) === 'column'; var dragMenuDirection = isDragMenuOpen ? activeTableMenu.type : undefined; var isOpenedByKeyboard = isDragMenuOpen && activeTableMenu.openedBy === 'keyboard'; var popupContentRef = (0, _react.useRef)(null); var setOutsideClickTargetRef = (0, _react.useContext)(_uiReact.OutsideClickTargetRefContext); var navWrapperRef = (0, _react.useRef)(null); var handlePopupRef = (0, _react.useCallback)(function (el) { popupContentRef.current = el; setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 || setOutsideClickTargetRef(el); }, [setOutsideClickTargetRef]); var returnFocusToDragHandle = (0, _react.useCallback)(function () { // Match legacy DragMenu's closeMenu('handle') behaviour. var handleId = dragMenuDirection === 'row' ? '#drag-handle-button-row' : '#drag-handle-button-column'; var handle = document.querySelector(handleId); handle === null || handle === void 0 || handle.focus(); }, [dragMenuDirection]); var focusFirstMenuItem = (0, _react.useCallback)(function () { var root = navWrapperRef.current; if (!root) { return; } var firstItem = root.querySelector('[role="menuitem"]:not([disabled]), [role="menuitemcheckbox"]:not([disabled]), [role="menuitemradio"]:not([disabled]), button:not([disabled])'); firstItem === null || firstItem === void 0 || firstItem.focus(); }, []); // Focus the first menu item when the menu opens via keyboard (Enter/Space on // the drag handle). Mouse-opened menus leave focus where the user clicked. var setNavWrapperRef = (0, _react.useCallback)(function (el) { navWrapperRef.current = el; if (el && isOpenedByKeyboard) { // rAF allows the popup to finish positioning before focusing. requestAnimationFrame(function () { focusFirstMenuItem(); }); } }, [focusFirstMenuItem, isOpenedByKeyboard]); var handleKeyboardFocus = (0, _react.useCallback)(function (_event) { focusFirstMenuItem(); }, [focusFirstMenuItem]); var handleEscape = (0, _react.useCallback)(function (event) { event.preventDefault(); event.stopPropagation(); api === null || api === void 0 || api.core.actions.execute((0, _commands.closeActiveTableMenu)(api)); returnFocusToDragHandle(); }, [api, returnFocusToDragHandle]); // Memoize the editor DOM reference so the provider doesn't re-bind listeners // on every render (the provider depends on `dom` in its effect's deps). var editorDom = (0, _react.useMemo)(function () { return editorView.dom instanceof HTMLElement ? editorView.dom : undefined; }, [editorView.dom]); // The drag menu is opened by interacting with the drag handle directly, not // by a global page-level shortcut. var isShortcutToFocusToolbar = (0, _react.useCallback)(function () { return false; }, []); var handleClickOutside = (0, _react.useCallback)(function (event) { var _popupContentRef$curr; var target = event.target; // Ignore clicks handled by this popup, drag handles, or nested portalled // dropdowns so those controls can manage their own open/close behavior. if (target instanceof Node && (_popupContentRef$curr = popupContentRef.current) !== null && _popupContentRef$curr !== void 0 && _popupContentRef$curr.contains(target) || target instanceof Element && (target.closest(DRAG_HANDLE_CONTROLS_SELECTOR) || target.closest(NESTED_DROPDOWN_SELECTOR))) { return; } api === null || api === void 0 || api.core.actions.execute((0, _commands.closeActiveTableMenu)(api)); }, [api]); if (!isDragMenuOpen || !targetCellPosition || editorView.state.doc.nodeSize <= targetCellPosition) { return null; } var inStickyMode = (stickyHeaders === null || stickyHeaders === void 0 ? void 0 : stickyHeaders.sticky) || (tableWrapper === null || tableWrapper === void 0 ? void 0 : tableWrapper.classList.contains(_types.TableCssClassName.TABLE_NODE_WRAPPER_NO_OVERFLOW)) && (0, _platformFeatureFlags.fg)('platform_editor_table_sticky_header_patch_7'); var targetHandleRef = dragMenuDirection === 'row' ? document.querySelector('#drag-handle-button-row') : document.querySelector('#drag-handle-button-column'); if (!targetHandleRef || !(editorView.state.selection instanceof _cellSelection.CellSelection)) { return null; } return /*#__PURE__*/_react.default.createElement(PopupWithListeners, { alignX: dragMenuDirection === 'row' ? 'right' : undefined, alignY: dragMenuDirection === 'row' ? 'start' : undefined // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting , target: targetHandleRef, mountTo: mountPoint, boundariesElement: boundariesElement, scrollableElement: scrollableElement, fitWidth: _consts2.TABLE_MENU_WIDTH, fitHeight: _consts.tablePopupMenuFitHeight // z-index value below is to ensure that this menu is above other floating menu // in table, but below floating dialogs like typeaheads, pickers, etc. // In sticky mode, we want to show the menu above the sticky header , zIndex: inStickyMode ? _editorSharedStyles.akEditorFloatingDialogZIndex : _editorSharedStyles.akEditorFloatingOverlapPanelZIndex, forcePlacement: true, preventOverflow: dragMenuDirection === 'row', offset: POPUP_OFFSET, stick: true, handleClickOutside: handleClickOutside }, /*#__PURE__*/_react.default.createElement("div", { ref: handlePopupRef }, /*#__PURE__*/_react.default.createElement(_toolbarKeyboardNavigationProvider.ToolbarKeyboardNavigationProvider, { childComponentSelector: TABLE_MENU_NAV_SELECTOR, dom: editorDom, isShortcutToFocusToolbar: isShortcutToFocusToolbar, handleFocus: handleKeyboardFocus, handleEscape: handleEscape }, /*#__PURE__*/_react.default.createElement("div", { "data-table-drag-menu-nav": "true", ref: setNavWrapperRef }, /*#__PURE__*/_react.default.createElement(_TableMenu.TableMenu, { api: api, editorView: editorView, surface: dragMenuDirection === 'row' ? _keys2.ROW_MENU : _keys.COLUMN_MENU }))))); }; FloatingTableMenu.displayName = 'FloatingTableMenu'; var _default = exports.default = FloatingTableMenu;