@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
324 lines (315 loc) • 17.7 kB
JavaScript
;
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;
}
}
});
};