UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

316 lines (305 loc) 17 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.createPlugin = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _analytics = require("@atlaskit/editor-common/analytics"); var _browser = require("@atlaskit/editor-common/browser"); var _coreUtils = require("@atlaskit/editor-common/core-utils"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _transforms = require("@atlaskit/editor-common/transforms"); var _utils = require("@atlaskit/editor-common/utils"); var _utils2 = require("@atlaskit/editor-prosemirror/utils"); var _editorTables = require("@atlaskit/editor-tables"); var _utils3 = require("@atlaskit/editor-tables/utils"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _tableNodeViews = require("../nodeviews/table-node-views"); var _plugin = require("../pm-plugins/decorations/plugin"); var _types = require("../types"); var _eventHandlers = require("../ui/event-handlers"); var _commands = require("./commands"); var _columnResize = require("./commands/column-resize"); var _misc = require("./commands/misc"); var _defaultTableSelection = require("./default-table-selection"); var _pluginFactory = require("./plugin-factory"); var _pluginKey = require("./plugin-key"); var _fixTables = require("./transforms/fix-tables"); var _replaceTable = require("./transforms/replace-table"); var _decoration = require("./utils/decoration"); var _paste = require("./utils/paste"); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var createPlugin = exports.createPlugin = function createPlugin(dispatchAnalyticsEvent, dispatch, portalProviderAPI, nodeViewPortalProviderAPI, eventDispatcher, pluginConfig, getEditorContainerWidth, getEditorFeatureFlags, getIntl, fullWidthModeEnabled, previousFullWidthModeEnabled, dragAndDropEnabled, editorAnalyticsAPI, pluginInjectionApi, isTableScalingEnabled, shouldUseIncreasedScalingPercent, isCommentEditor, isChromelessEditor) { var _accessibilityUtils; var state = (0, _pluginFactory.createPluginState)(dispatch, _objectSpread(_objectSpread(_objectSpread({ pluginConfig: pluginConfig, isTableHovered: false, insertColumnButtonIndex: undefined, insertRowButtonIndex: undefined, isFullWidthModeEnabled: fullWidthModeEnabled, wasFullWidthModeEnabled: previousFullWidthModeEnabled, isHeaderRowEnabled: !!pluginConfig.allowHeaderRow, isHeaderColumnEnabled: false, isDragAndDropEnabled: dragAndDropEnabled, isTableScalingEnabled: isTableScalingEnabled }, _defaultTableSelection.defaultHoveredCell), _defaultTableSelection.defaultTableSelection), {}, { getIntl: getIntl })); // Used to prevent invalid table cell spans being reported more than once per editor/document var invalidTableIds = []; var editorViewRef = null; var ariaNotifyPlugin = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_accessibilityUtils = pluginInjectionApi.accessibilityUtils) === null || _accessibilityUtils === void 0 ? void 0 : _accessibilityUtils.actions.ariaNotify; var getCurrentEditorState = function getCurrentEditorState() { var editorView = editorViewRef; if (!editorView) { return null; } return editorView.state; }; var getNodeView = function getNodeView() { return { table: (0, _tableNodeViews.tableView)({ portalProviderAPI: portalProviderAPI, eventDispatcher: eventDispatcher, getEditorContainerWidth: getEditorContainerWidth, getEditorFeatureFlags: getEditorFeatureFlags, dispatchAnalyticsEvent: dispatchAnalyticsEvent, pluginInjectionApi: pluginInjectionApi, isCommentEditor: isCommentEditor, isChromelessEditor: isChromelessEditor }), tableRow: (0, _tableNodeViews.tableRowView)({ eventDispatcher: eventDispatcher, pluginInjectionApi: pluginInjectionApi }), tableCell: (0, _tableNodeViews.tableCellView)({ eventDispatcher: eventDispatcher, pluginInjectionApi: pluginInjectionApi }), tableHeader: (0, _tableNodeViews.tableHeaderView)({ eventDispatcher: eventDispatcher, pluginInjectionApi: pluginInjectionApi }) }; }; var nodeViews = getNodeView(); return new _safePlugin.SafePlugin({ state: state, key: _pluginKey.pluginKey, appendTransaction: function appendTransaction(transactions, oldState, newState) { var tr = transactions.find(function (tr) { return tr.getMeta('uiEvent') === 'cut'; }); function reportInvalidTableCellSpanAttrs(invalidNodeAttr) { if (invalidTableIds.find(function (id) { return id === invalidNodeAttr.tableLocalId; })) { return; } invalidTableIds.push(invalidNodeAttr.tableLocalId); dispatchAnalyticsEvent({ action: _analytics.ACTION.INVALID_DOCUMENT_ENCOUNTERED, actionSubject: _analytics.ACTION_SUBJECT.EDITOR, eventType: _analytics.EVENT_TYPE.OPERATIONAL, attributes: { nodeType: invalidNodeAttr.nodeType, reason: "".concat(invalidNodeAttr.attribute, ": ").concat(invalidNodeAttr.reason), tableLocalId: invalidNodeAttr.tableLocalId, spanValue: invalidNodeAttr.spanValue } }); } if (tr) { var _getEditorFeatureFlag = getEditorFeatureFlags(), _getEditorFeatureFlag2 = _getEditorFeatureFlag.tableWithFixedColumnWidthsOption, tableWithFixedColumnWidthsOption = _getEditorFeatureFlag2 === void 0 ? false : _getEditorFeatureFlag2; // "fixTables" removes empty rows as we don't allow that in schema var updatedTr = (0, _eventHandlers.handleCut)(tr, oldState, newState, pluginInjectionApi, editorAnalyticsAPI, editorViewRef || undefined, isTableScalingEnabled, tableWithFixedColumnWidthsOption, shouldUseIncreasedScalingPercent); return (0, _fixTables.fixTables)(updatedTr) || updatedTr; } if (transactions.find(function (tr) { return tr.docChanged; })) { return (0, _fixTables.fixTables)(newState.tr, reportInvalidTableCellSpanAttrs); } }, view: function view(editorView) { var domAtPos = editorView.domAtPos.bind(editorView); editorViewRef = editorView; return { update: function update(view, prevState) { var state = view.state, dispatch = view.dispatch; var selection = state.selection; var pluginState = (0, _pluginFactory.getPluginState)(state); var tableRef; if (pluginState.editorHasFocus) { var parent = (0, _utils2.findParentDomRefOfType)(state.schema.nodes.table, domAtPos)(selection); if (parent) { tableRef = // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting parent.querySelector('table') || undefined; } var tableNode = (0, _utils3.findTable)(state.selection); // when keyboard cursor leaves the table we need to stop column resizing var pluginPrevState = (0, _pluginFactory.getPluginState)(prevState); var isStopKeyboardColumResizing = pluginPrevState.isResizeHandleWidgetAdded && pluginPrevState.isKeyboardResize; if (isStopKeyboardColumResizing) { var isTableNodesDifferent = (pluginPrevState === null || pluginPrevState === void 0 ? void 0 : pluginPrevState.tableNode) !== (tableNode === null || tableNode === void 0 ? void 0 : tableNode.node); if (pluginPrevState !== null && pluginPrevState !== void 0 && pluginPrevState.tableNode && tableNode && isTableNodesDifferent) { var oldRowsNumber = _editorTables.TableMap.get(pluginPrevState.tableNode).height; var newRowsNumber = _editorTables.TableMap.get(tableNode.node).height; if (oldRowsNumber !== newRowsNumber || // Add/delete row tableNode.node.attrs.localId !== pluginPrevState.tableNode.attrs.localId) { // Jump to another table (0, _columnResize.stopKeyboardColumnResizing)({ ariaNotify: ariaNotifyPlugin, getIntl: getIntl })(state, dispatch); } } else if (!tableNode) { // selection outside of table (0, _columnResize.stopKeyboardColumnResizing)({ ariaNotify: ariaNotifyPlugin, getIntl: getIntl })(state, dispatch); } } } if (pluginState.tableRef !== tableRef) { (0, _commands.setTableRef)(tableRef)(state, dispatch); } if (pluginState.editorHasFocus && pluginState.tableRef) { var _ref = state.selection, $cursor = _ref.$cursor; if ($cursor) { // Only update bold when it's a cursor var tableCellHeader = (0, _utils2.findParentNodeOfType)(state.schema.nodes.tableHeader)(state.selection); if (tableCellHeader) { (0, _commands.addBoldInEmptyHeaderCells)(tableCellHeader)(state, dispatch); } } } else if (pluginState.isResizeHandleWidgetAdded) { (0, _misc.removeResizeHandleDecorations)()(state, dispatch); } } }; }, props: { transformPasted: function transformPasted(slice) { var editorState = getCurrentEditorState(); if (!editorState) { return slice; } var schema = editorState.schema; // if we're pasting to outside a table or outside a table // header, ensure that we apply any table headers to the first // row of content we see, if required if (!(0, _coreUtils.insideTable)(editorState) && (0, _paste.isHeaderRowRequired)(editorState)) { slice = (0, _misc.transformSliceToAddTableHeaders)(slice, schema); } // This fixes pasting a table with default layout into comment editor // table lose width and expand to full width if (!(0, _coreUtils.insideTable)(editorState) && isCommentEditor && pluginConfig.allowTableAlignment && isTableScalingEnabled) { slice = (0, _paste.transformSliceTableLayoutDefaultToCenter)(slice, schema); } slice = (0, _paste.transformSliceToFixHardBreakProblemOnCopyFromCell)(slice, schema); // We do this separately, so it also applies to drag/drop events // This needs to go before `transformSliceToRemoveOpenExpand` slice = (0, _transforms.transformSliceToRemoveOpenLayoutNodes)(slice, schema); // If a partial paste of expand, paste only the content // This needs to go before `transformSliceToRemoveOpenTable` slice = (0, _transforms.transformSliceToRemoveOpenExpand)(slice, schema); // transformSliceToRemoveOpenTable() transforms based on the depth of the root node, assuming that the tables will be at the root // Bodied extensions will contribute to the depth of the table selection so we need to remove them first /** If a partial paste of bodied extension, paste only text */ slice = (0, _transforms.transformSliceToRemoveOpenBodiedExtension)(slice, schema); /** If a partial paste of table, paste only table's content */ slice = (0, _paste.transformSliceToRemoveOpenTable)(slice, schema); /** If a partial paste of multi bodied extension, paste only children */ slice = (0, _transforms.transformSliceToRemoveOpenMultiBodiedExtension)(slice, schema); slice = (0, _paste.transformSliceToCorrectEmptyTableCells)(slice, schema); if (!pluginConfig.allowColumnResizing) { slice = (0, _misc.transformSliceToRemoveColumnsWidths)(slice, schema); } // If we don't allow background on cells, we need to remove it // from the paste slice if (!pluginConfig.allowBackgroundColor) { slice = (0, _misc.transformSliceRemoveCellBackgroundColor)(slice, schema); } else { slice = (0, _misc.transformSliceToFixDarkModeDefaultBackgroundColor)(slice, schema); } slice = (0, _transforms.transformSliceToRemoveOpenNestedExpand)(slice, schema); if ((0, _platformFeatureFlags.fg)('platform_editor_use_nested_table_pm_nodes')) { slice = (0, _paste.transformSliceToRemoveNestedTables)(slice, schema, editorState.selection); } return slice; }, handleClick: function handleClick(_ref2, _pos, event) { var state = _ref2.state, dispatch = _ref2.dispatch; var decorationSet = _plugin.pluginKey.getState(state); var browser = (0, _expValEquals.expValEquals)('platform_editor_hydratable_ui', 'isEnabled', true) ? (0, _browser.getBrowserInfo)() : _browser.browser; if ((0, _decoration.findControlsHoverDecoration)(decorationSet).length) { (0, _commands.clearHoverSelection)()(state, dispatch); } // ED-6069: workaround for Chrome given a regression introduced in prosemirror-view@1.6.8 // Returning true prevents that updateSelection() is getting called in the commit below: // @see https://github.com/ProseMirror/prosemirror-view/commit/33fe4a8b01584f6b4103c279033dcd33e8047b95 if (browser.chrome && event.target) { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting var targetClassList = event.target.classList; if (targetClassList.contains(_types.TableCssClassName.CONTROLS_BUTTON) || targetClassList.contains(_types.TableCssClassName.CONTEXTUAL_MENU_BUTTON) || targetClassList.contains(_types.TableCssClassName.DRAG_HANDLE_BUTTON_CLICKABLE_ZONE) || targetClassList.contains(_types.TableCssClassName.DRAG_HANDLE_BUTTON_CONTAINER)) { return true; } } return false; }, handleScrollToSelection: function handleScrollToSelection(view) { // when typing into a sticky header cell, we don't want to scroll // back to the top of the table if the user has already scrolled down var tableHeader = view.state.schema.nodes.tableHeader; var domRef = (0, _utils2.findParentDomRefOfType)(tableHeader, view.domAtPos.bind(view))(view.state.selection); var maybeTr = (0, _utils.closestElement)(domRef, 'tr'); return maybeTr ? maybeTr.classList.contains('sticky') : false; }, handleTextInput: function handleTextInput(view, _from, _to, text) { var state = view.state, dispatch = view.dispatch; var _getPluginState = (0, _pluginFactory.getPluginState)(state), isKeyboardResize = _getPluginState.isKeyboardResize; if (isKeyboardResize) { (0, _columnResize.stopKeyboardColumnResizing)({ ariaNotify: ariaNotifyPlugin, getIntl: getIntl })(state, dispatch); return false; } var tr = (0, _replaceTable.replaceSelectedTable)(state, text, _analytics.INPUT_METHOD.KEYBOARD, editorAnalyticsAPI); if (tr.selectionSet) { dispatch(tr); return true; } return false; }, nodeViews: nodeViews, handleDOMEvents: { focus: _eventHandlers.handleFocus, blur: _eventHandlers.handleBlur, mousedown: (0, _eventHandlers.withCellTracking)(_eventHandlers.handleMouseDown), mouseleave: _eventHandlers.handleMouseLeave, mousemove: (0, _eventHandlers.whenTableInFocus)((0, _eventHandlers.handleMouseMove)(nodeViewPortalProviderAPI), pluginInjectionApi), mouseenter: _eventHandlers.handleMouseEnter, mouseup: (0, _eventHandlers.whenTableInFocus)(_eventHandlers.handleMouseUp), click: (0, _eventHandlers.withCellTracking)((0, _eventHandlers.whenTableInFocus)(_eventHandlers.handleClick)) }, handleTripleClick: _eventHandlers.handleTripleClick } }); };