@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
464 lines (460 loc) • 21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shiftArrowUpFromTable = exports.selectRows = exports.selectColumns = exports.modASelectTable = exports.arrowRightFromTable = exports.arrowLeftFromTable = void 0;
var _messages = require("@atlaskit/editor-common/messages");
var _selection = require("@atlaskit/editor-common/selection");
var _state = require("@atlaskit/editor-prosemirror/state");
var _cellSelection = require("@atlaskit/editor-tables/cell-selection");
var _tableMap = require("@atlaskit/editor-tables/table-map");
var _utils = require("@atlaskit/editor-tables/utils");
var _toolbar = require("../../ui/toolbar");
var _pluginFactory = require("../plugin-factory");
var _misc = require("./misc");
var TableSelectionDirection = /*#__PURE__*/function (TableSelectionDirection) {
TableSelectionDirection["TopToBottom"] = "TopToBottom";
TableSelectionDirection["BottomToTop"] = "BottomToTop";
return TableSelectionDirection;
}(TableSelectionDirection || {});
var arrowLeftFromTable = exports.arrowLeftFromTable = function arrowLeftFromTable(editorSelectionAPI) {
return function () {
return function (state, dispatch) {
var selection = state.selection;
if (selection instanceof _cellSelection.CellSelection) {
return arrowLeftFromCellSelection(editorSelectionAPI)(selection)(state, dispatch);
} else if (selection instanceof _selection.GapCursorSelection) {
return arrowLeftFromGapCursor(editorSelectionAPI)(selection)(state, dispatch);
} else if (selection instanceof _state.TextSelection) {
return arrowLeftFromText(editorSelectionAPI)(selection)(state, dispatch);
}
return false;
};
};
};
var arrowRightFromTable = exports.arrowRightFromTable = function arrowRightFromTable(editorSelectionAPI) {
return function () {
return function (state, dispatch) {
var selection = state.selection;
if (selection instanceof _cellSelection.CellSelection) {
return arrowRightFromCellSelection(editorSelectionAPI)(selection)(state, dispatch);
} else if (selection instanceof _selection.GapCursorSelection) {
return arrowRightFromGapCursor(editorSelectionAPI)(selection)(state, dispatch);
} else if (selection instanceof _state.TextSelection) {
return arrowRightFromText(editorSelectionAPI)(selection)(state, dispatch);
}
return false;
};
};
};
var arrowLeftFromCellSelection = function arrowLeftFromCellSelection(editorSelectionAPI) {
return function (selection) {
return function (state, dispatch) {
if ((0, _utils.isTableSelected)(state.selection) && editorSelectionAPI) {
var selectionState = editorSelectionAPI.sharedState.currentState() || {};
if ((selectionState === null || selectionState === void 0 ? void 0 : selectionState.selectionRelativeToNode) === _selection.RelativeSelectionPos.Start) {
// we have full table cell selection and want to set gap cursor selection before table
return setGapCursorBeforeTable(editorSelectionAPI)()(state, dispatch);
} else if ((selectionState === null || selectionState === void 0 ? void 0 : selectionState.selectionRelativeToNode) === _selection.RelativeSelectionPos.End) {
// we have full table cell selection and want to set selection at end of last cell
return setSelectionAtEndOfLastCell(editorSelectionAPI)(selection)(state, dispatch);
} else if ((selectionState === null || selectionState === void 0 ? void 0 : selectionState.selectionRelativeToNode) === undefined) {
// we have full table cell selection and want to set selection at start of first cell
return setSelectionAtStartOfFirstCell(editorSelectionAPI)(selection, _selection.RelativeSelectionPos.Before)(state, dispatch);
}
}
return false;
};
};
};
var arrowRightFromCellSelection = function arrowRightFromCellSelection(editorSelectionAPI) {
return function (selection) {
return function (state, dispatch) {
if ((0, _utils.isTableSelected)(state.selection) && editorSelectionAPI) {
var _ref = editorSelectionAPI.sharedState.currentState() || {},
selectionRelativeToNode = _ref.selectionRelativeToNode;
if (selectionRelativeToNode === _selection.RelativeSelectionPos.Start) {
// we have full table cell selection and want to set selection at start of first cell
return setSelectionAtStartOfFirstCell(editorSelectionAPI)(selection)(state, dispatch);
} else if (selectionRelativeToNode === _selection.RelativeSelectionPos.End || selectionRelativeToNode === undefined) {
// we have full table cell selection and want to set gap cursor selection after table
return setGapCursorAfterTable(editorSelectionAPI)()(state, dispatch);
}
}
return false;
};
};
};
var selectColumns = exports.selectColumns = function selectColumns(editorSelectionAPI, ariaNotify, getIntl) {
return function () {
var triggeredByKeyboard = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
return function (state, dispatch) {
var selection = state.selection;
var table = (0, _utils.findTable)(selection);
var rect = (0, _utils.selectedRect)(state);
if (table && (0, _utils.isRowSelected)(rect.top)(selection)) {
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
if (table && rect) {
var selectColumnCommand = (0, _misc.selectColumn)(rect.left, undefined, triggeredByKeyboard)(state, dispatch);
var map = _tableMap.TableMap.get(table.node);
if (ariaNotify && getIntl) {
ariaNotify(getIntl().formatMessage(_messages.tableMessages.columnSelected, {
index: rect.left + 1,
total: map.width
}), {
priority: 'important'
});
}
return selectColumnCommand;
}
return false;
};
};
};
var selectRows = exports.selectRows = function selectRows(editorSelectionAPI, ariaNotify, getIntl) {
return function () {
var triggeredByKeyboard = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
return function (state, dispatch) {
var selection = state.selection;
var table = (0, _utils.findTable)(selection);
var rect = (0, _utils.selectedRect)(state);
if (table && (0, _utils.isColumnSelected)(rect.left)(selection)) {
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
if (table && rect) {
var selectRowCommand = (0, _misc.selectRow)(rect.top, undefined, triggeredByKeyboard)(state, dispatch);
var map = _tableMap.TableMap.get(table.node);
if (ariaNotify && getIntl) {
ariaNotify(getIntl().formatMessage(_messages.tableMessages.rowSelected, {
index: rect.top + 1,
total: map.height
}), {
priority: 'important'
});
}
return selectRowCommand;
}
return false;
};
};
};
var arrowLeftFromGapCursor = function arrowLeftFromGapCursor(editorSelectionAPI) {
return function (selection) {
return function (state, dispatch) {
var doc = state.doc;
var $from = selection.$from,
from = selection.from,
side = selection.side;
if (side === _selection.Side.RIGHT) {
if ($from.nodeBefore && $from.nodeBefore.type.name === 'table') {
// we have a gap cursor after a table node and want to set a full table cell selection
return selectFullTable(editorSelectionAPI)({
node: $from.nodeBefore,
startPos: doc.resolve(from - 1).start($from.depth + 1),
dir: TableSelectionDirection.TopToBottom
})(state, dispatch);
}
} else if (side === _selection.Side.LEFT) {
var table = (0, _utils.findTable)(selection);
if (table && isSelectionAtStartOfTable($from, selection) && editorSelectionAPI) {
var selectionState = editorSelectionAPI.sharedState.currentState() || {};
if ((selectionState === null || selectionState === void 0 ? void 0 : selectionState.selectionRelativeToNode) === _selection.RelativeSelectionPos.Before) {
// we have a gap cursor at start of first table cell and want to set a gap cursor selection before table
return setGapCursorBeforeTable(editorSelectionAPI)()(state, dispatch);
} else {
// we have a gap cursor at start of first table cell and want to set a full table cell selection
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
}
}
return false;
};
};
};
var arrowRightFromGapCursor = function arrowRightFromGapCursor(editorSelectionAPI) {
return function (selection) {
return function (state, dispatch) {
var $from = selection.$from,
from = selection.from,
$to = selection.$to,
side = selection.side;
if (side === _selection.Side.LEFT) {
if ($from.nodeAfter && $from.nodeAfter.type.name === 'table') {
// we have a gap cursor before a table node and want to set a full table cell selection
return selectFullTable(editorSelectionAPI)({
node: $from.nodeAfter,
startPos: from + 1,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
} else if (side === _selection.Side.RIGHT) {
var table = (0, _utils.findTable)(selection);
if (table && isSelectionAtEndOfTable($to, selection)) {
// we have a gap cursor at end of last table cell and want to set a full table cell selection
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.TopToBottom
})(state, dispatch);
}
}
return false;
};
};
};
var arrowLeftFromText = function arrowLeftFromText(editorSelectionAPI) {
return function (selection) {
return function (state, dispatch) {
var table = (0, _utils.findTable)(selection);
if (table) {
var $from = selection.$from;
var columResizePluginState = (0, _pluginFactory.getPluginState)(state) || {};
var isColumnResizing = Boolean(columResizePluginState === null || columResizePluginState === void 0 ? void 0 : columResizePluginState.isKeyboardResize);
if (isSelectionAtStartOfTable($from, selection) && $from.parent.type.name === 'paragraph' && $from.depth === table.depth + 3 &&
// + 3 for: row, cell & paragraph nodes
editorSelectionAPI && !isColumnResizing) {
var selectionState = editorSelectionAPI.sharedState.currentState() || {};
if ((selectionState === null || selectionState === void 0 ? void 0 : selectionState.selectionRelativeToNode) === _selection.RelativeSelectionPos.Before) {
// we have a text selection at start of first table cell, directly inside a top level paragraph,
// and want to set gap cursor selection before table
return setGapCursorBeforeTable(editorSelectionAPI)()(state, dispatch);
} else {
// we have a text selection at start of first table cell, directly inside a top level paragraph,
// and want to set a full table cell selection
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
}
}
return false;
};
};
};
var arrowRightFromText = function arrowRightFromText(editorSelectionAPI) {
return function (selection) {
return function (state, dispatch) {
var table = (0, _utils.findTable)(selection);
if (table) {
var $to = selection.$to;
var columResizePluginState = (0, _pluginFactory.getPluginState)(state) || {};
var isColumnResizing = Boolean(columResizePluginState === null || columResizePluginState === void 0 ? void 0 : columResizePluginState.isKeyboardResize);
if (isSelectionAtEndOfTable($to, selection) && $to.parent.type.name === 'paragraph' && $to.depth === table.depth + 3 &&
// + 3 for: row, cell & paragraph nodes
!isColumnResizing) {
// we have a text selection at end of last table cell, directly inside a top level paragraph,
// and want to set a full table cell selection
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.TopToBottom
})(state, dispatch);
}
}
return false;
};
};
};
/**
* Sets a cell selection over all the cells in the table node
* We use this instead of selectTable from prosemirror-utils so we can control which
* cell is the anchor and which is the head, and also so we can set the relative selection
* pos in the selection plugin
*/
var selectFullTable = function selectFullTable(editorSelectionAPI) {
return function (_ref2) {
var node = _ref2.node,
startPos = _ref2.startPos,
dir = _ref2.dir;
return function (state, dispatch) {
var doc = state.doc;
var _TableMap$get = _tableMap.TableMap.get(node),
map = _TableMap$get.map;
var $firstCell = doc.resolve(startPos + map[0]);
var $lastCell = doc.resolve(startPos + map[map.length - 1]);
var fullTableSelection;
var selectionRelativeToNode;
if (dir === TableSelectionDirection.TopToBottom) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fullTableSelection = new _cellSelection.CellSelection($firstCell, $lastCell);
selectionRelativeToNode = _selection.RelativeSelectionPos.End;
} else {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fullTableSelection = new _cellSelection.CellSelection($lastCell, $firstCell);
selectionRelativeToNode = _selection.RelativeSelectionPos.Start;
}
if (editorSelectionAPI) {
var tr = editorSelectionAPI.actions.selectNearNode({
selectionRelativeToNode: selectionRelativeToNode,
selection: fullTableSelection
})(state);
if (dispatch) {
dispatch(tr);
return true;
}
}
return false;
};
};
};
var setSelectionAtStartOfFirstCell = function setSelectionAtStartOfFirstCell(editorSelectionAPI) {
return function (selection, selectionRelativeToNode) {
return function (state, dispatch) {
var $anchorCell = selection.$anchorCell,
$headCell = selection.$headCell;
var $firstCell = $anchorCell.pos < $headCell.pos ? $anchorCell : $headCell;
var $firstPosInsideCell = state.doc.resolve($firstCell.pos + 1);
// check if first pos should have a gap cursor, otherwise find closest text selection
var selectionAtStartOfCell = _selection.GapCursorSelection.valid($firstPosInsideCell) ? new _selection.GapCursorSelection($firstPosInsideCell, _selection.Side.LEFT) : _state.Selection.findFrom($firstPosInsideCell, 1);
if (editorSelectionAPI) {
var tr = editorSelectionAPI.actions.selectNearNode({
selectionRelativeToNode: selectionRelativeToNode,
selection: selectionAtStartOfCell
})(state);
if (dispatch) {
dispatch(tr);
return true;
}
}
return false;
};
};
};
var setSelectionAtEndOfLastCell = function setSelectionAtEndOfLastCell(editorSelectionAPI) {
return function (selection, selectionRelativeToNode) {
return function (state, dispatch) {
var $anchorCell = selection.$anchorCell,
$headCell = selection.$headCell;
var $lastCell = $anchorCell.pos > $headCell.pos ? $anchorCell : $headCell;
var lastPosInsideCell = $lastCell.pos + ($lastCell.nodeAfter ? $lastCell.nodeAfter.content.size : 0) + 1;
var $lastPosInsideCell = state.doc.resolve(lastPosInsideCell);
// check if last pos should have a gap cursor, otherwise find closest text selection
var selectionAtEndOfCell = _selection.GapCursorSelection.valid($lastPosInsideCell) ? new _selection.GapCursorSelection($lastPosInsideCell, _selection.Side.RIGHT) : _state.Selection.findFrom($lastPosInsideCell, -1);
if (editorSelectionAPI) {
var tr = editorSelectionAPI.actions.selectNearNode({
selectionRelativeToNode: selectionRelativeToNode,
selection: selectionAtEndOfCell
})(state);
if (dispatch) {
dispatch(tr);
return true;
}
}
return false;
};
};
};
var setGapCursorBeforeTable = function setGapCursorBeforeTable(editorSelectionAPI) {
return function () {
return function (state, dispatch) {
var table = (0, _utils.findTable)(state.selection);
if (table) {
var $beforeTablePos = state.doc.resolve(table.pos);
if (_selection.GapCursorSelection.valid($beforeTablePos)) {
var selectionBeforeTable = new _selection.GapCursorSelection($beforeTablePos, _selection.Side.LEFT);
if (editorSelectionAPI) {
var tr = editorSelectionAPI.actions.selectNearNode({
selectionRelativeToNode: undefined,
selection: selectionBeforeTable
})(state);
if (dispatch) {
dispatch(tr);
return true;
}
}
}
}
return false;
};
};
};
var setGapCursorAfterTable = function setGapCursorAfterTable(editorSelectionAPI) {
return function () {
return function (state, dispatch) {
var table = (0, _utils.findTable)(state.selection);
if (table) {
var $afterTablePos = state.doc.resolve(table.pos + table.node.nodeSize);
if (_selection.GapCursorSelection.valid($afterTablePos)) {
var selectionAfterTable = new _selection.GapCursorSelection($afterTablePos, _selection.Side.RIGHT);
if (editorSelectionAPI) {
var tr = editorSelectionAPI.actions.selectNearNode({
selectionRelativeToNode: undefined,
selection: selectionAfterTable
})(state);
if (dispatch) {
dispatch(tr);
return true;
}
}
return false;
}
}
return false;
};
};
};
var isSelectionAtStartOfTable = function isSelectionAtStartOfTable($pos, selection) {
var _findTable;
return (0, _selection.isSelectionAtStartOfNode)($pos, (_findTable = (0, _utils.findTable)(selection)) === null || _findTable === void 0 ? void 0 : _findTable.node);
};
var isSelectionAtEndOfTable = function isSelectionAtEndOfTable($pos, selection) {
var _findTable2;
return (0, _selection.isSelectionAtEndOfNode)($pos, (_findTable2 = (0, _utils.findTable)(selection)) === null || _findTable2 === void 0 ? void 0 : _findTable2.node);
};
var shiftArrowUpFromTable = exports.shiftArrowUpFromTable = function shiftArrowUpFromTable(editorSelectionAPI) {
return function () {
return function (state, dispatch) {
var selection = state.selection;
var table = (0, _utils.findTable)(selection);
var selectionRect = (0, _toolbar.getClosestSelectionRect)(state);
var index = selectionRect === null || selectionRect === void 0 ? void 0 : selectionRect.top;
if (table && index === 0) {
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
return false;
};
};
};
var modASelectTable = exports.modASelectTable = function modASelectTable(editorSelectionAPI) {
return function () {
return function (state, dispatch) {
var selection = state.selection;
var table = (0, _utils.findTable)(selection);
if (!table) {
return false;
}
var $from = selection.$from,
$to = selection.$to;
var tableSelected = (0, _utils.isTableSelected)(selection);
if (!tableSelected && $from.pos > table.start + 1 && $to.pos < table.start + table.node.nodeSize) {
return selectFullTable(editorSelectionAPI)({
node: table.node,
startPos: table.start,
dir: TableSelectionDirection.BottomToTop
})(state, dispatch);
}
return false;
};
};
};