UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

324 lines (315 loc) 17.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.createPlugin = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = require("react"); var _reactIntl = require("react-intl"); var _v = _interopRequireDefault(require("uuid/v4")); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _types = require("@atlaskit/editor-common/types"); var _view = require("@atlaskit/editor-prosemirror/view"); var _tableMap = require("@atlaskit/editor-tables/table-map"); var _SortingIconWrapper = require("../../ui/icons/SortingIconWrapper"); var _pluginFactory = require("../plugin-factory"); var _consts = require("./consts"); var _pluginKey = require("./plugin-key"); var _utils = require("./utils"); function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } 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; } /** * This plugin allows sorting of table nodes in the Editor without modifying the underlying ProseMirror document. * Instead of making changes to the ProseMirror document, the plugin sorts the table rows in the DOM. This allows the sorting to be * visible to the user without affecting the document's content. */ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead var createPlugin = exports.createPlugin = function createPlugin(api, nodeViewPortalProviderAPI) { return new _safePlugin.SafePlugin({ state: { init: function init() { return { decorations: _view.DecorationSet.empty, sort: {}, allTables: [] }; }, apply: function apply(tr, pluginState, oldState) { var _api$editorViewMode; // TODO: ED-26961 - move this mode check to plugin creation if possible. Right now it's here because the initial state // does not appear correct when the plugin is created. var _ref = ((_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.currentState()) || {}, mode = _ref.mode; if (mode !== 'view') { var _pluginState$decorati, _pluginState$decorati2; var _sortingDecorations = pluginState === null || pluginState === void 0 || (_pluginState$decorati = pluginState.decorations) === null || _pluginState$decorati === void 0 ? void 0 : _pluginState$decorati.find(undefined, undefined, function (s) { return (s === null || s === void 0 ? void 0 : s.type) === 'sorting-decoration'; }); return _objectSpread(_objectSpread({}, pluginState), {}, { decorations: pluginState === null || pluginState === void 0 || (_pluginState$decorati2 = pluginState.decorations) === null || _pluginState$decorati2 === void 0 ? void 0 : _pluginState$decorati2.remove(_sortingDecorations) }); } var decorations = pluginState.decorations, sort = pluginState.sort, allTables = pluginState.allTables; var sortMeta = tr.getMeta('tableSortMeta'); var hoverTableMeta = tr.getMeta('mouseEnterTable'); var removeTableMeta = tr.getMeta('removeTable'); var tableId = ''; // Remove the table from the state if (removeTableMeta) { allTables = allTables.filter(function (_ref2) { var _ref3 = (0, _slicedToArray2.default)(_ref2, 1), id = _ref3[0]; return id !== removeTableMeta; }); } else { tableId = hoverTableMeta === null || hoverTableMeta === void 0 ? void 0 : hoverTableMeta[0]; } sort = _objectSpread(_objectSpread({}, sort), sortMeta); var isTableInState = allTables.some(function (_ref4) { var _ref5 = (0, _slicedToArray2.default)(_ref4, 1), id = _ref5[0]; return id === tableId; }); // Update the table in the state if (hoverTableMeta) { allTables = allTables.filter(function (_ref6) { var _ref7 = (0, _slicedToArray2.default)(_ref6, 1), id = _ref7[0]; return id !== hoverTableMeta[0]; }); allTables.push(hoverTableMeta); } /** * Create decorations for the sorting icons */ var decs = []; var sortingDecorations = pluginState.decorations.find(undefined, undefined, function (spec) { return spec.tableId === tableId && spec.type === 'sorting-decoration'; }); // TODO: ED-26961 - add support for keyboard only users if (hoverTableMeta && !isTableInState || sortMeta || isTableInState && !sortingDecorations.length) { allTables.forEach(function (table) { var _table = (0, _slicedToArray2.default)(table, 3), tableId = _table[0], _node = _table[1], pos = _table[2]; var tableNode = tr.doc.nodeAt(tr.mapping.map(pos)); if (!tableNode || tableNode.type.name !== 'table') { return pluginState; } var map = _tableMap.TableMap.get(tableNode); var hasMergedCells = new Set(map.map).size !== map.map.length; map.mapByRow[0].forEach(function (cell, index) { // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead var decorationRenderKey = (0, _v.default)(); decs.push(_view.Decoration.widget(cell + pos + 2, function () { var _sort$tableId; var element = document.createElement('div'); element.setAttribute(_consts.SORT_INDEX_DATA_ATTRIBUTE, "".concat(index)); element.classList.add(_consts.SORTING_ICON_CLASS_NAME); if (hasMergedCells) { element.classList.add(_consts.IS_DISABLED_CLASS_NAME); } var sortOrdered; if (index === ((_sort$tableId = sort[tableId]) === null || _sort$tableId === void 0 ? void 0 : _sort$tableId.index)) { var _sort$tableId2; sortOrdered = (_sort$tableId2 = sort[tableId]) === null || _sort$tableId2 === void 0 ? void 0 : _sort$tableId2.direction; } else { sortOrdered = _types.SortOrder.NO_ORDER; } var _getPluginState = (0, _pluginFactory.getPluginState)(oldState), getIntl = _getPluginState.getIntl; nodeViewPortalProviderAPI.render(function () { return /*#__PURE__*/(0, _react.createElement)(_reactIntl.RawIntlProvider, { value: getIntl() }, /*#__PURE__*/(0, _react.createElement)(_SortingIconWrapper.SortingIconWrapper, { isSortingAllowed: !hasMergedCells, sortOrdered: sortOrdered, onClick: function onClick() {}, onKeyDown: function onKeyDown() {}, api: api })); }, element, decorationRenderKey); return element; }, { destroy: function destroy(node) { nodeViewPortalProviderAPI.remove(decorationRenderKey); }, type: 'sorting-decoration', tableId: tableId })); }); }); decorations = _view.DecorationSet.create(tr.doc, decs); } /** * Map the decorations to the new document if there are changes */ if (tr.docChanged) { decorations = decorations.map(tr.mapping, tr.doc); allTables = allTables.map(function (table) { return [table[0], table[1], tr.mapping.map(table[2])]; }); } return { decorations: decorations, sort: sort, allTables: allTables }; } }, key: _pluginKey.tableViewModeSortPluginKey, appendTransaction: function appendTransaction(trs, oldState, newState) { var _api$editorViewMode2, _key$getState; // return newState.tr; var _ref8 = (api === null || api === void 0 || (_api$editorViewMode2 = api.editorViewMode) === null || _api$editorViewMode2 === void 0 ? void 0 : _api$editorViewMode2.sharedState.currentState()) || {}, mode = _ref8.mode; if (mode !== 'view') { return newState.tr; } var allTables = ((_key$getState = _pluginKey.tableViewModeSortPluginKey.getState(newState)) === null || _key$getState === void 0 ? void 0 : _key$getState.allTables) || []; /** * If incoming changes have affected a table node, remove the sorting. This prevents the * table from breaking if changes like merged cells are incoming. */ var _iterator = _createForOfIteratorHelper(trs), _step; try { var _loop = function _loop() { var tr = _step.value; var hoverTableMeta = tr.getMeta('mouseEnterTable'); if (hoverTableMeta) { allTables = allTables.filter(function (_ref9) { var _ref0 = (0, _slicedToArray2.default)(_ref9, 1), id = _ref0[0]; return id !== hoverTableMeta[0]; }); allTables.push(hoverTableMeta); } var isRemote = tr.getMeta('isRemote'); var isDocChanged = tr.docChanged; var isChangesIncoming = isRemote && isDocChanged; var oldPluginState = _pluginKey.tableViewModeSortPluginKey.getState(oldState); var newPluginState = _pluginKey.tableViewModeSortPluginKey.getState(newState); var _iterator2 = _createForOfIteratorHelper(allTables), _step2; try { var _loop2 = function _loop2() { var _oldPluginState$sort, _newPluginState$sort; var table = _step2.value; var _table2 = (0, _slicedToArray2.default)(table, 3), tableId = _table2[0], node = _table2[1], pos = _table2[2]; var _ref1 = (oldPluginState === null || oldPluginState === void 0 || (_oldPluginState$sort = oldPluginState.sort) === null || _oldPluginState$sort === void 0 ? void 0 : _oldPluginState$sort[tableId]) || {}, oldOrder = _ref1.order, oldDirection = _ref1.direction, oldIndex = _ref1.index; if (isChangesIncoming) { var _maybeTableNode$attrs; var maybeTableNode = tr.doc.nodeAt(pos); var isTableNodeChanged = (maybeTableNode === null || maybeTableNode === void 0 || (_maybeTableNode$attrs = maybeTableNode.attrs) === null || _maybeTableNode$attrs === void 0 ? void 0 : _maybeTableNode$attrs.localId) !== tableId || !node.eq(maybeTableNode); if (isTableNodeChanged) { var newtr = newState.tr; newtr.setMeta('tableSortMeta', (0, _defineProperty2.default)({}, tableId, {})); newtr.setMeta('removeTable', tableId); // Unsort the table here if (oldOrder !== undefined) { var _getTableElements = (0, _utils.getTableElements)(tableId), rows = _getTableElements.rows, tbody = _getTableElements.tbody; if (!rows || !tbody) { return { v: { v: newtr } }; } var sortedOrder = (0, _toConsumableArray2.default)(oldOrder).sort(function (a, b) { return a.value - b.value; }); sortedOrder.forEach(function (index, i) { tbody.appendChild(rows[index.index + 1]); }); return { v: { v: newtr } }; } } } /** * Sort the table if the sort order has changed */ var _ref10 = (newPluginState === null || newPluginState === void 0 || (_newPluginState$sort = newPluginState.sort) === null || _newPluginState$sort === void 0 ? void 0 : _newPluginState$sort[tableId]) || {}, newOrder = _ref10.order, newDirection = _ref10.direction, newIndex = _ref10.index; var orderChanged = oldDirection !== newDirection || oldIndex !== newIndex; if (orderChanged) { if (!isRemote && newDirection !== _types.SortOrder.NO_ORDER) { var _getTableElements2 = (0, _utils.getTableElements)(tableId), _rows = _getTableElements2.rows, _tbody = _getTableElements2.tbody; if (_rows && newOrder) { newOrder.forEach(function (index, i) { _tbody === null || _tbody === void 0 || _tbody.appendChild(_rows[index.value + 1]); }); } } } }, _ret2; for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { _ret2 = _loop2(); if (_ret2) return _ret2.v; } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } }, _ret; for (_iterator.s(); !(_step = _iterator.n()).done;) { _ret = _loop(); if (_ret) return _ret.v; } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return newState.tr; }, props: { handleDOMEvents: { keydown: function keydown(view, event) { // TODO: ED-26961 - fix the focus issue here, where toggling sort with a keypress loses focus if (event.key === 'Enter' || event.key === ' ') { var _key$getState2; var pluginState = ((_key$getState2 = _pluginKey.tableViewModeSortPluginKey.getState(view.state)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.sort) || {}; (0, _utils.toggleSort)(view, event, pluginState); } }, click: function click(view, event) { var _key$getState3; var pluginState = ((_key$getState3 = _pluginKey.tableViewModeSortPluginKey.getState(view.state)) === null || _key$getState3 === void 0 ? void 0 : _key$getState3.sort) || {}; (0, _utils.toggleSort)(view, event, pluginState); } }, decorations: function decorations(state) { var _key$getState4; var decs = ((_key$getState4 = _pluginKey.tableViewModeSortPluginKey.getState(state)) === null || _key$getState4 === void 0 ? void 0 : _key$getState4.decorations) || _view.DecorationSet.empty; return decs; } } }); };