@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
183 lines (180 loc) • 7.66 kB
JavaScript
import { getTableContainerWidth } from '@atlaskit/editor-common/node-width';
import { tableCellMinWidth } from '@atlaskit/editor-common/styles';
import { akEditorTableNumberColumnWidth } from '@atlaskit/editor-shared-styles';
import { fg } from '@atlaskit/platform-feature-flags';
import { updateColumnWidths } from '../../transforms/column-width';
import { getTableWidth } from '../../utils/nodes';
import { getLayoutSize } from '../utils/misc';
import { reduceSpace } from '../utils/resize-logic';
import { adjustColumnsWidths, getResizeState, getTotalWidth, updateColgroup } from '../utils/resize-state';
import { hasTableBeenResized, insertColgroupFromNode } from './colgroup';
import { syncStickyRowToTable } from './dom';
// Base function to trigger the actual scale on a table node.
// Will only resize/scale if a table has been previously resized.
const scale = (tableRef, options, domAtPos, isTableScalingEnabledOnCurrentTable = false, shouldUseIncreasedScalingPercent = false, isCommentEditor = false) => {
const {
node,
containerWidth,
previousContainerWidth,
prevNode,
start,
layoutChanged,
isTableResizingEnabled
} = options;
const maxSize = isTableResizingEnabled ? getTableContainerWidth(node) : getLayoutSize(node.attrs.layout, containerWidth, {});
const prevTableWidth = getTableWidth(prevNode);
const previousMaxSize = isTableResizingEnabled ? getTableContainerWidth(node) : getLayoutSize(prevNode.attrs.layout, previousContainerWidth, {});
let newWidth = maxSize;
// adjust table width if layout is updated
const hasOverflow = prevTableWidth > previousMaxSize;
if (layoutChanged && hasOverflow) {
// No keep overflow if the old content can be in the new size
const canFitInNewSize = prevTableWidth < maxSize;
if (canFitInNewSize) {
newWidth = maxSize;
} else {
// Keep the same scale.
const overflowScale = prevTableWidth / previousMaxSize;
newWidth = Math.floor(newWidth * overflowScale);
}
}
if (node.attrs.isNumberColumnEnabled) {
newWidth -= akEditorTableNumberColumnWidth;
}
const resizeState = getResizeState({
minWidth: tableCellMinWidth,
maxSize,
table: node,
tableRef,
start,
domAtPos,
isTableScalingEnabled: isTableScalingEnabledOnCurrentTable,
shouldUseIncreasedScalingPercent,
isCommentEditor
});
return scaleTableTo(resizeState, newWidth);
};
const scaleWithParent = (tableRef, parentWidth, table, start, domAtPos, isTableScalingEnabledOnCurrentTable = false, shouldUseIncreasedScalingPercent = false, isCommentEditor = false) => {
const resizeState = getResizeState({
minWidth: tableCellMinWidth,
maxSize: parentWidth,
table,
tableRef,
start,
domAtPos,
isTableScalingEnabled: isTableScalingEnabledOnCurrentTable,
shouldUseIncreasedScalingPercent,
isCommentEditor
});
if (table.attrs.isNumberColumnEnabled) {
parentWidth -= akEditorTableNumberColumnWidth;
}
return scaleTableTo(resizeState, Math.floor(parentWidth));
};
// Scales the table to a given size and updates its colgroup DOM node
export function scaleTableTo(state, maxSize) {
const scaleFactor = maxSize / getTotalWidth(state);
let newState = {
...state,
maxSize,
cols: state.cols.map(col => {
const {
minWidth,
width
} = col;
let newColWidth = Math.floor(width * scaleFactor);
if (newColWidth < minWidth) {
newColWidth = minWidth;
}
return {
...col,
width: newColWidth
};
})
};
const newTotalWidth = getTotalWidth(newState);
if (newTotalWidth > maxSize) {
newState = reduceSpace(newState, newTotalWidth - maxSize);
}
return adjustColumnsWidths(newState, maxSize);
}
export const previewScaleTable = (tableRef, options, domAtPos, isTableScalingEnabled = false, isTableWithFixedColumnWidthsOptionEnabled = false, isCommentEditor = false) => {
const {
node,
start,
parentWidth
} = options;
if (!tableRef) {
return;
}
if (parentWidth) {
const isNumberColumnEnabled = node.attrs.isNumberColumnEnabled;
const width = isNumberColumnEnabled ? parentWidth - akEditorTableNumberColumnWidth : parentWidth;
tableRef.style.width = `${width}px`;
}
let isTableScalingEnabledOnCurrentTable = isTableScalingEnabled;
const isTableScalingWithFixedColumnWidthsOptionEnabled = isTableScalingEnabled && isTableWithFixedColumnWidthsOptionEnabled;
if (isTableScalingWithFixedColumnWidthsOptionEnabled) {
isTableScalingEnabledOnCurrentTable = isTableScalingEnabled && node.attrs.displayMode !== 'fixed';
}
// If the table hasn't been resize, the colgroup 48px width values will gracefully scale down.
// If we are scaling the table down with isTableScalingEnabled, the colgroup widths may be scaled to a value that is not 48px.
if (!hasTableBeenResized(node) && !isTableScalingEnabledOnCurrentTable) {
syncStickyRowToTable(tableRef);
return;
}
const shouldUseIncreasedScalingPercent = isTableScalingWithFixedColumnWidthsOptionEnabled || isTableScalingEnabled && isCommentEditor;
const resizeState = parentWidth ? scaleWithParent(tableRef, parentWidth, node, start, domAtPos, false,
// Here isTableScalingEnabled = false
shouldUseIncreasedScalingPercent) : scale(tableRef, options, domAtPos, false, shouldUseIncreasedScalingPercent);
if (resizeState) {
updateColgroup(resizeState, tableRef, node, false, 1);
}
};
// Scale the table to meet new requirements (col, layout change etc)
export const scaleTable = (tableRef, options, domAtPos, api, isTableScalingEnabledOnCurrentTable = false, shouldUseIncreasedScalingPercent = false, isCommentEditor = false) => tr => {
if (!tableRef) {
return tr;
}
const {
node,
start,
parentWidth,
layoutChanged
} = options;
// If a table has not been resized yet, columns should be auto.
if (hasTableBeenResized(node) === false) {
// If its not a re-sized table, we still want to re-create cols
// To force reflow of columns upon delete.
if (!isTableScalingEnabledOnCurrentTable) {
const isTableScalingEnabled = false;
insertColgroupFromNode(tableRef, node, isTableScalingEnabled, undefined, shouldUseIncreasedScalingPercent, isCommentEditor);
}
tr.setMeta('scrollIntoView', false);
return tr;
}
let resizeState;
if (parentWidth) {
resizeState = scaleWithParent(tableRef, parentWidth, node, start, domAtPos, isTableScalingEnabledOnCurrentTable, shouldUseIncreasedScalingPercent);
} else {
resizeState = scale(tableRef, options, domAtPos, isTableScalingEnabledOnCurrentTable, shouldUseIncreasedScalingPercent);
}
if (resizeState) {
tr = updateColumnWidths(resizeState, node, start, api)(tr);
if (!fg('platform_editor_fix_table_resizing_undo')) {
tr.setMeta('addToHistory', false);
}
if (tr.docChanged) {
tr.setMeta('scrollIntoView', false);
// TODO: ED-8995 - We need to do this check to reduce the number of race conditions when working with tables.
// This metadata is been used in the sendTransaction function in the Collab plugin
/* Added !layoutChanged check here to solve unnecessary scroll bar after publish when click on breakout button multiple times and publish
scaleTable is only called once every time a breakout button is clicked, so it is safe not to add the meta 'scaleTable' to the tr.
Leaving the tr.setMeta('scaleTable', true) here for race conditions that we aren't aware of.
*/
!layoutChanged && tr.setMeta('scaleTable', true);
return tr;
}
}
return tr;
};