@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
188 lines (181 loc) • 7.88 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
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) { _defineProperty(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; }
import { tableCellMinWidth } from '@atlaskit/editor-common/styles';
import { akEditorDefaultLayoutWidth, akEditorWideLayoutWidth } from '@atlaskit/editor-shared-styles';
import { calculateColumnWidth, getCellsRefsInColumn } from '../table-resizing/utils/column-state';
import { contentWidth } from '../table-resizing/utils/content-width';
import { getLayoutSize } from '../table-resizing/utils/misc';
var validateTableCellNodeAttrs = function validateTableCellNodeAttrs(_ref, reportInvalidTableCellSpanAttrs) {
var colspan = _ref.colspan,
rowspan = _ref.rowspan,
tableLocalId = _ref.tableLocalId;
if (colspan && colspan < 0) {
reportInvalidTableCellSpanAttrs({
nodeType: 'tableCell',
attribute: 'colspan',
reason: 'negative value',
tableLocalId: tableLocalId,
spanValue: colspan
});
}
if (rowspan && rowspan < 0) {
reportInvalidTableCellSpanAttrs({
nodeType: 'tableCell',
attribute: 'rowspan',
reason: 'negative value',
tableLocalId: tableLocalId,
spanValue: rowspan
});
}
return;
};
// We attempt to patch the document when we have extra, unneeded, column widths
// Take this node for example:
//
// ['td', { colwidth: [100, 100, 100], colspan: 2 }]
//
// This row only spans two columns, yet it contains widths for 3.
// We remove the third width here, assumed duplicate content.
export var removeExtraneousColumnWidths = function removeExtraneousColumnWidths(node, basePos, tr, reportInvalidTableCellSpanAttrs) {
var hasProblems = false;
tr = replaceCells(tr, node, basePos, function (cell) {
var _cell$attrs = cell.attrs,
colwidth = _cell$attrs.colwidth,
colspan = _cell$attrs.colspan,
rowspan = _cell$attrs.rowspan;
if (reportInvalidTableCellSpanAttrs) {
validateTableCellNodeAttrs({
colspan: colspan,
rowspan: rowspan,
tableLocalId: node.attrs.localId
}, reportInvalidTableCellSpanAttrs);
}
if (colwidth && colwidth.length > colspan) {
hasProblems = true;
return cell.type.createChecked(_objectSpread(_objectSpread({}, cell.attrs), {}, {
colwidth: colwidth.slice(0, colspan)
}), cell.content, cell.marks);
}
return cell;
});
if (hasProblems) {
return true;
}
return false;
};
export var fixTables = function fixTables(tr, reportInvalidTableCellSpanAttrs) {
var hasProblems = false;
tr.doc.descendants(function (node, pos) {
if (node.type.name === 'table') {
// in the unlikely event of having to fix multiple tables at the same time
hasProblems = removeExtraneousColumnWidths(node, tr.mapping.map(pos), tr, reportInvalidTableCellSpanAttrs);
}
});
if (hasProblems) {
return tr;
}
};
// When we get a table with an 'auto' attribute, we want to:
// 1. render with table-layout: auto
// 2. capture the column widths
// 3. set the column widths as attributes, and remove the 'auto' attribute,
// so the table renders the same, but is now fixed-width
//
// This can be used to migrate table appearances from other sources that are
// usually rendered with 'auto'.
//
// We use this when migrating TinyMCE tables for Confluence, for example:
// https://pug.jira-dev.com/wiki/spaces/AEC/pages/3362882215/How+do+we+map+TinyMCE+tables+to+Fabric+tables
export var fixAutoSizedTable = function fixAutoSizedTable(view, tableNode, tableRef, tablePos, opts) {
var tr = view.state.tr;
var domAtPos = view.domAtPos.bind(view);
var tableStart = tablePos + 1;
var colWidths = parseDOMColumnWidths(domAtPos, tableNode, tableStart, tableRef);
var totalContentWidth = colWidths.reduce(function (acc, current) {
return acc + current;
}, 0);
var tableLayout = getLayoutBasedOnWidth(totalContentWidth);
var maxLayoutSize = getLayoutSize(tableLayout, opts.containerWidth, {});
// Content width will generally not meet the constraints of the layout
// whether it be below or above, so we scale our columns widths
// to meet these requirements
var scale = 1;
if (totalContentWidth !== maxLayoutSize) {
scale = maxLayoutSize / totalContentWidth;
}
var scaledColumnWidths = colWidths.map(function (width) {
return Math.floor(width * scale);
});
tr = replaceCells(tr, tableNode, tablePos, function (cell, _rowIndex, colIndex) {
var newColWidths = scaledColumnWidths.slice(colIndex, colIndex + cell.attrs.colspan);
return cell.type.createChecked(_objectSpread(_objectSpread({}, cell.attrs), {}, {
colwidth: newColWidths.length ? newColWidths : null
}), cell.content, cell.marks);
});
// clear autosizing on the table node
return tr.setNodeMarkup(tablePos, undefined, _objectSpread(_objectSpread({}, tableNode.attrs), {}, {
layout: tableLayout,
__autoSize: false
})).setMeta('addToHistory', false);
};
var getLayoutBasedOnWidth = function getLayoutBasedOnWidth(totalWidth) {
if (totalWidth > akEditorWideLayoutWidth) {
return 'full-width';
} else if (totalWidth > akEditorDefaultLayoutWidth && totalWidth < akEditorWideLayoutWidth) {
return 'wide';
} else {
return 'default';
}
};
function parseDOMColumnWidths(domAtPos, tableNode, tableStart, tableRef) {
var row = tableRef.querySelector('tr');
if (!row) {
return [];
}
var cols = [];
for (var col = 0; col < row.childElementCount; col++) {
var currentCol = row.children[col];
var colspan = Number(currentCol.getAttribute('colspan') || 1);
for (var span = 0; span < colspan; span++) {
var colIdx = col + span;
var cells = getCellsRefsInColumn(colIdx, tableNode, tableStart, domAtPos);
var colWidth = calculateColumnWidth(cells, function (_, col) {
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
return contentWidth(col, tableRef).width;
});
cols[colIdx] = Math.max(colWidth, tableCellMinWidth);
}
}
return cols;
}
// TODO: ED-26961 - move to prosemirror-utils
var replaceCells = function replaceCells(tr, table, tablePos, modifyCell) {
var rows = [];
var modifiedCells = 0;
for (var rowIndex = 0; rowIndex < table.childCount; rowIndex++) {
var row = table.child(rowIndex);
var cells = [];
for (var colIndex = 0; colIndex < row.childCount; colIndex++) {
var cell = row.child(colIndex);
// TODO: ED-26961 - The rowIndex and colIndex are not accurate in a merged cell scenario
// e.g. table with 5 columns might have only one cell in a row, colIndex will be 1, where it should be 4
var node = modifyCell(cell, rowIndex, colIndex);
if (node.sameMarkup(cell) === false) {
modifiedCells++;
}
cells.push(node);
}
if (cells.length) {
rows.push(row.type.createChecked(row.attrs, cells, row.marks));
}
}
// Check if the table has changed before replacing.
// If no cells are modified our counter will be zero.
if (rows.length && modifiedCells !== 0) {
var newTable = table.type.createChecked(table.attrs, rows, table.marks);
return tr.replaceWith(tablePos, tablePos + table.nodeSize, newTable);
}
return tr;
};