@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
372 lines (363 loc) • 15.9 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 { getTableContainerWidth } from '@atlaskit/editor-common/node-width';
import { tableCellMinWidth, tableNewColumnMinWidth } from '@atlaskit/editor-common/styles';
import { calcTableColumnWidths } from '@atlaskit/editor-common/utils';
import { getSelectedTableInfo } from '../../utils/analytics';
import { getColWidthFix, hasTableBeenResized, insertColgroupFromNode } from './colgroup';
import { getCellsRefsInColumn, getColumnStateFromDOM } from './column-state';
import { syncStickyRowToTable } from './dom';
import { getTableMaxWidth, getTableScalingPercent } from './misc';
export var getResizeState = function getResizeState(_ref) {
var _table$attrs;
var minWidth = _ref.minWidth,
maxSize = _ref.maxSize,
table = _ref.table,
tableRef = _ref.tableRef,
start = _ref.start,
domAtPos = _ref.domAtPos,
_ref$isTableScalingEn = _ref.isTableScalingEnabled,
isTableScalingEnabled = _ref$isTableScalingEn === void 0 ? false : _ref$isTableScalingEn,
_ref$shouldUseIncreas = _ref.shouldUseIncreasedScalingPercent,
shouldUseIncreasedScalingPercent = _ref$shouldUseIncreas === void 0 ? false : _ref$shouldUseIncreas,
_ref$isCommentEditor = _ref.isCommentEditor,
isCommentEditor = _ref$isCommentEditor === void 0 ? false : _ref$isCommentEditor;
if (isTableScalingEnabled && !isCommentEditor || isTableScalingEnabled && isCommentEditor && (_table$attrs = table.attrs) !== null && _table$attrs !== void 0 && _table$attrs.width) {
var scalePercent = getTableScalingPercent(table, tableRef, shouldUseIncreasedScalingPercent);
minWidth = Math.ceil(minWidth / scalePercent);
}
if (hasTableBeenResized(table)) {
// If the table has been resized, we can use the column widths from the table node
var _cols = calcTableColumnWidths(table).map(function (width, index) {
return {
width: width === 0 ? tableNewColumnMinWidth : width,
minWidth: width === 0 ? tableNewColumnMinWidth : minWidth,
index: index
};
});
var _widths = _cols.map(function (col) {
return col.width;
});
var _tableWidth = _widths.reduce(function (sum, width) {
return sum + width;
}, 0);
var _overflow = _tableWidth > maxSize;
return {
cols: _cols,
widths: _widths,
maxSize: maxSize,
tableWidth: _tableWidth,
overflow: _overflow,
isScaled: isTableScalingEnabled
};
}
var shouldReinsertColgroup = !isTableScalingEnabled;
// Getting the resize state from DOM
var colgroupChildren = insertColgroupFromNode(tableRef, table, isTableScalingEnabled, shouldReinsertColgroup,
// don't reinsert colgroup when preserving table width - this causes widths to jump
shouldUseIncreasedScalingPercent, isCommentEditor);
var cols = Array.from(colgroupChildren).map(function (_, index) {
// If the table hasn't been resized and we have a table width attribute, we can use it
// to calculate the widths of the columns
if (isTableScalingEnabled) {
var _table$attrs2;
// isCommentEditor when table cols were not resized,
// we want to use tableRef.parentElement.clientWidth, which is the same as maxSize
var tableNodeWidth = isCommentEditor && !((_table$attrs2 = table.attrs) !== null && _table$attrs2 !== void 0 && _table$attrs2.width) ? maxSize : getTableContainerWidth(table);
return {
index: index,
width: tableNodeWidth / colgroupChildren.length,
minWidth: minWidth
};
}
var cellsRefs = getCellsRefsInColumn(index, table, start, domAtPos);
return getColumnStateFromDOM(cellsRefs, index, minWidth);
});
var widths = cols.map(function (col) {
return col.width;
});
var tableWidth = widths.reduce(function (sum, width) {
return sum + width;
}, 0);
var overflow = tableWidth > maxSize;
return {
cols: cols,
widths: widths,
maxSize: maxSize,
tableWidth: tableWidth,
overflow: overflow,
isScaled: isTableScalingEnabled
};
};
// updates Colgroup DOM node with new widths
export var updateColgroup = function updateColgroup(state, tableRef, tableNode, isTableScalingEnabled, scalePercent) {
var cols = tableRef === null || tableRef === void 0 ? void 0 : tableRef.querySelectorAll(':scope > colgroup > col');
var columnsCount = cols === null || cols === void 0 ? void 0 : cols.length;
/**
updateColgroup will update whole table scale when click the column resize handle, this behavior will affect when table overflowed, when now resize handle been dragged and extend to make table overflowed, table will show overflow. This need to be confirmed because it conflict with how isTableScalingEnabled work.
We don't want to scale the table when resizing columns, only when viewpoint shrinks the table.
We need to remove !isColumnResizing if we handled auto scale table when mouseUp event.
* */
if (isTableScalingEnabled && tableNode) {
state.cols.filter(function (column) {
return column && !!column.width;
}) // if width is 0, we dont want to apply that.
.forEach(function (column, i) {
var fixedColWidth = getColWidthFix(column.width, columnsCount !== null && columnsCount !== void 0 ? columnsCount : 0);
var scaledWidth = fixedColWidth * (scalePercent || 1);
var finalWidth = Math.max(scaledWidth, tableCellMinWidth);
// we aren't handling the remaining pixels here when the 48px min width is reached
if (cols !== null && cols !== void 0 && cols[i]) {
cols[i].style.width = "".concat(finalWidth, "px");
}
});
} else {
state.cols.filter(function (column) {
return column && !!column.width;
}) // if width is 0, we dont want to apply that.
.forEach(function (column, i) {
if (cols !== null && cols !== void 0 && cols[i]) {
cols[i].style.width = "".concat(getColWidthFix(column.width, columnsCount !== null && columnsCount !== void 0 ? columnsCount : 0), "px");
}
});
}
// colgroup has updated, reflect new widths in sticky header
syncStickyRowToTable(tableRef);
};
export var getTotalWidth = function getTotalWidth(_ref2) {
var cols = _ref2.cols;
return cols.reduce(function (totalWidth, col) {
return totalWidth + col.width;
}, 0);
};
// adjust columns to take up the total width
// difference in total columns widths vs table width happens due to the "Math.floor"
export var adjustColumnsWidths = function adjustColumnsWidths(resizeState, maxSize) {
var totalWidth = getTotalWidth(resizeState);
var diff = maxSize - totalWidth;
if (diff > 0 || diff < 0 && Math.abs(diff) < tableCellMinWidth) {
var updated = false;
return _objectSpread(_objectSpread({}, resizeState), {}, {
cols: resizeState.cols.map(function (col) {
if (!updated && col.width + diff > col.minWidth) {
updated = true;
return _objectSpread(_objectSpread({}, col), {}, {
width: col.width + diff
});
}
return col;
})
});
}
return resizeState;
};
export var evenAllColumnsWidths = function evenAllColumnsWidths(resizeState) {
var maxSize = getTotalWidth(resizeState);
var evenWidth = Math.floor(maxSize / resizeState.cols.length);
var cols = resizeState.cols.map(function (col) {
return _objectSpread(_objectSpread({}, col), {}, {
width: evenWidth
});
});
return adjustColumnsWidths(_objectSpread(_objectSpread({}, resizeState), {}, {
cols: cols
}), maxSize);
};
var getSpace = function getSpace(columns, start, end) {
return columns.slice(start, end).map(function (col) {
return col.width;
}).reduce(function (sum, width) {
return sum + width;
}, 0);
};
var evenSelectedColumnsWidths = function evenSelectedColumnsWidths(resizeState, rect) {
var cols = resizeState.cols;
var selectedSpace = getSpace(cols, rect.left, rect.right);
var allSpace = getSpace(cols, 0, cols.length);
var allWidth = allSpace / cols.length;
var width = selectedSpace / (rect.right - rect.left);
// Result equals even distribution of all columns -
// unset widths of all columns
if (allWidth === width) {
return _objectSpread(_objectSpread({}, resizeState), {}, {
widths: cols.map(function () {
return width;
}),
cols: resizeState.cols.map(function (col) {
return _objectSpread(_objectSpread({}, col), {}, {
width: 0
});
})
});
}
return _objectSpread(_objectSpread({}, resizeState), {}, {
widths: cols.map(function (col, i) {
return i >= rect.left && i < rect.right ? width : col.width;
}),
cols: cols.map(function (col, i) {
return _objectSpread(_objectSpread({}, col), {}, {
width: i >= rect.left && i < rect.right ? width : col.width
});
})
});
};
export var bulkColumnsResize = function bulkColumnsResize(resizeState, columnsIndexes, sourceColumnIndex) {
var currentTableWidth = getTotalWidth(resizeState);
var colIndex = columnsIndexes.indexOf(sourceColumnIndex) > -1 ? sourceColumnIndex : sourceColumnIndex + 1;
var sourceCol = resizeState.cols[colIndex];
var seenColumns = {};
var widthsDiffs = [];
var cols = resizeState.cols.map(function (col) {
if (columnsIndexes.indexOf(col.index) > -1) {
var diff = col.width - sourceCol.width;
if (diff !== 0) {
widthsDiffs.push(diff);
}
return _objectSpread(_objectSpread({}, col), {}, {
width: sourceCol.width
});
}
return col;
});
var newState = _objectSpread(_objectSpread({}, resizeState), {}, {
cols: cols.map(function (col) {
if (columnsIndexes.indexOf(col.index) > -1 ||
// take from prev columns only if dragging the first handle to the left
columnsIndexes.indexOf(sourceColumnIndex) > -1 && col.index < colIndex) {
return col;
}
while (widthsDiffs.length) {
var diff = widthsDiffs.shift() || 0;
var column = seenColumns[col.index] || col;
var maybeWidth = column.width + diff;
if (maybeWidth > column.minWidth) {
seenColumns[column.index] = _objectSpread(_objectSpread({}, column), {}, {
width: maybeWidth
});
} else {
widthsDiffs.push(maybeWidth - column.minWidth);
seenColumns[column.index] = _objectSpread(_objectSpread({}, column), {}, {
width: column.minWidth
});
break;
}
}
return seenColumns[col.index] || col;
})
});
// minimum possible table widths at the current layout
var minTableWidth = resizeState.maxSize;
// new table widths after bulk resize
var newTotalWidth = getTotalWidth(newState);
// when all columns are selected, what do we do when sum of columns widths is lower than min possible table widths?
if (columnsIndexes.length === resizeState.cols.length && newTotalWidth < minTableWidth) {
// table is not in overflow -> normal resize of a single column
if (currentTableWidth === minTableWidth) {
return resizeState;
}
// table is in overflow: keep the dragged column at its widths and evenly distribute columns
// to recover from overflow state
else {
var columnWidth = Math.floor((minTableWidth - sourceCol.width) / (newState.cols.length - 1));
newState = _objectSpread(_objectSpread({}, resizeState), {}, {
cols: newState.cols.map(function (col) {
if (col.index === sourceCol.index) {
return col;
}
return _objectSpread(_objectSpread({}, col), {}, {
width: columnWidth
});
})
});
}
}
// fixes total table widths by adding missing pixels to columns widths here and there
return adjustColumnsWidths(newState, resizeState.maxSize);
};
// Get the layout
var normaliseTableLayout = function normaliseTableLayout(input) {
switch (input) {
case 'wide':
return input;
case 'full-width':
return input;
default:
return 'default';
}
};
export var getNewResizeStateFromSelectedColumns = function getNewResizeStateFromSelectedColumns(rect, state, domAtPos, getEditorContainerWidth) {
var isTableScalingEnabled = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var isTableFixedColumnWidthsOptionEnabled = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
var isCommentEditor = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
// Fail early so that we don't do complex calculations for no reason
var numColumnsSelected = rect.right - rect.left;
if (numColumnsSelected <= 1) {
return;
}
var _getSelectedTableInfo = getSelectedTableInfo(state.selection),
totalRowCount = _getSelectedTableInfo.totalRowCount,
totalColumnCount = _getSelectedTableInfo.totalColumnCount,
table = _getSelectedTableInfo.table;
if (!table) {
return;
}
// Fail early so that we don't do complex calculations for no reason
if (!hasTableBeenResized(table.node)) {
return;
}
var maybeTable = domAtPos(table.start).node;
var maybeTableElement = maybeTable instanceof HTMLElement ? maybeTable : null;
var tableRef = maybeTableElement === null || maybeTableElement === void 0 ? void 0 : maybeTableElement.closest('table');
if (!tableRef) {
return;
}
var layout = normaliseTableLayout(tableRef === null || tableRef === void 0 ? void 0 : tableRef.dataset.layout);
var maxSize = getTableMaxWidth({
table: table.node,
tableStart: table.start,
state: state,
layout: layout,
getEditorContainerWidth: getEditorContainerWidth
});
var isTableScalingEnabledOnCurrentTable = isTableScalingEnabled;
var isTableScalingWithFixedColumnWidthsOptionEnabled = isTableScalingEnabled && isTableFixedColumnWidthsOptionEnabled;
if (isTableScalingWithFixedColumnWidthsOptionEnabled) {
isTableScalingEnabledOnCurrentTable = table.node.attrs.displayMode !== 'fixed';
}
var shouldUseIncreasedScalingPercent = isTableScalingWithFixedColumnWidthsOptionEnabled;
if (isTableScalingEnabled && isCommentEditor) {
isTableScalingEnabledOnCurrentTable = true;
shouldUseIncreasedScalingPercent = true;
}
var resizeState = getResizeState({
minWidth: tableCellMinWidth,
maxSize: maxSize,
table: table.node,
tableRef: tableRef,
start: table.start,
domAtPos: domAtPos,
isTableScalingEnabled: isTableScalingEnabledOnCurrentTable,
shouldUseIncreasedScalingPercent: shouldUseIncreasedScalingPercent,
isCommentEditor: isCommentEditor
});
var newResizeState = evenSelectedColumnsWidths(resizeState, rect);
var widthsBefore = resizeState.widths;
var widthsAfter = newResizeState.widths;
var changed = resizeState.widths.some(function (widthBefore, index) {
return widthBefore !== widthsAfter[index];
});
return {
resizeState: newResizeState,
table: table,
changed: changed,
attributes: {
position: rect.left,
count: rect.right - rect.left,
totalRowCount: totalRowCount,
totalColumnCount: totalColumnCount,
widthsBefore: widthsBefore,
widthsAfter: widthsAfter
}
};
};