UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

191 lines (186 loc) 9.9 kB
import { tableCellMinWidth } from '@atlaskit/editor-common/styles'; import { calcTableColumnWidths, getFragmentBackingArray } from '@atlaskit/editor-common/utils'; import { DOMSerializer } from '@atlaskit/editor-prosemirror/model'; import { akEditorTableNumberColumnWidth } from '@atlaskit/editor-shared-styles'; import { TableMap } from '@atlaskit/editor-tables/table-map'; import { COLUMN_MIN_WIDTH, MAX_SCALING_PERCENT, MAX_SCALING_PERCENT_TABLES_WITH_FIXED_COLUMN_WIDTHS_OPTION } from './consts'; import { getScalingPercentForTableWithoutWidth, getTableContainerElementWidth, getTableScalingPercent } from './misc'; /** * This ensures the combined width of the columns (and tbody) of table is always smaller or equal * than the table and table wrapper elements. This is necessary as there is no longer * padding on the .pm-table-wrapper, so all elements need to be the same width to avoid * overflow. */ export const getColWidthFix = (colwidth, tableColumnCount) => colwidth - 1 / tableColumnCount; const generateColStyle = (fixedColWidth, tableWidth, isCommentEditor, isChromelessEditor, isNested, shouldUseIncreasedScalingPercent, isNumberColumnEnabled, isTableHasWidth, hasTableBeenResized) => { const maxScalingPercent = shouldUseIncreasedScalingPercent ? MAX_SCALING_PERCENT_TABLES_WITH_FIXED_COLUMN_WIDTHS_OPTION : MAX_SCALING_PERCENT; const maxScaledRatio = 1 - maxScalingPercent; const isFullPageEditor = !isChromelessEditor && !isCommentEditor; // for nested tables, or chromeless editor, which used non resizable table container if (isNested || isChromelessEditor) { if (hasTableBeenResized) { return `width: max(${fixedColWidth}px, ${tableCellMinWidth}px)`; } return `width: ${tableCellMinWidth}px)`; } if (isFullPageEditor || !isFullPageEditor && isTableHasWidth) { const scaledPercent = isNumberColumnEnabled ? `calc(calc(var(--ak-editor-table-width) - ${akEditorTableNumberColumnWidth}px - 1px)/${tableWidth})` : `calc(calc(var(--ak-editor-table-width) - 1px)/${tableWidth})`; return `width: max(calc(${fixedColWidth}px * ${maxScaledRatio}), calc(${fixedColWidth} * ${scaledPercent}), ${tableCellMinWidth}px)`; } // table resized to full-width in comment editor // it doesn't have a width attribute, and cols has been resized if (hasTableBeenResized) { const scaledPercent = isNumberColumnEnabled ? `calc(calc(var(--ak-editor-table-width) - ${akEditorTableNumberColumnWidth}px - 1px)/${tableWidth})` : `calc(calc(var(--ak-editor-table-width) - 1px)/${tableWidth})`; return `width: max(calc(${fixedColWidth} * ${scaledPercent}), ${tableCellMinWidth}px)`; } return `width: ${tableCellMinWidth}px`; }; export const generateColgroupFromNode = (table, isCommentEditor, isChromelessEditor, isNested, isTableScalingEnabled, shouldUseIncreasedScalingPercent) => { const cols = []; const map = TableMap.get(table); const isTableHasWidth = !!table.attrs.width; const isNumberColumnEnabled = table.attrs.isNumberColumnEnabled || false; const isFullPageEditor = !isChromelessEditor && !isCommentEditor; // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion table.content.firstChild.content.forEach(cell => { const colspan = cell.attrs.colspan || 1; // if the table has been resized if (Array.isArray(cell.attrs.colwidth)) { cell.attrs.colwidth.slice(0, colspan).forEach(width => { // existing logic for calculating the width of the column const fixedColWidth = getColWidthFix(width, map.width); const tableWidth = isFullPageEditor || !isFullPageEditor && isTableHasWidth ? getTableContainerElementWidth(table) : calcTableColumnWidths(table).reduce((sum, width) => sum + width, 0); if (isTableScalingEnabled) { cols.push(['col', { style: generateColStyle(fixedColWidth, tableWidth, isCommentEditor, isChromelessEditor, isNested, shouldUseIncreasedScalingPercent, isNumberColumnEnabled, isTableHasWidth, true) }]); } else { cols.push(['col', { style: `width: max(${fixedColWidth}px, ${tableCellMinWidth}px)` }]); } }); } else { if (!isTableScalingEnabled) { cols.push(...Array.from({ length: colspan }, _ => ['col', { style: `width: ${tableCellMinWidth}px;` }])); } else { // columns has not been resized, so distribute the width evenly cols.push(...Array.from({ length: colspan }, _ => { const tableWidth = getTableContainerElementWidth(table); const columnWidth = tableWidth / map.width || 0; const fixedColWidth = getColWidthFix(columnWidth, map.width || 0); return ['col', { style: generateColStyle(fixedColWidth, tableWidth, isCommentEditor, isChromelessEditor, isNested, shouldUseIncreasedScalingPercent, isNumberColumnEnabled, isTableHasWidth) }]; })); } } }); return cols; }; export const generateColgroup = (table, tableRef, shouldUseIncreasedScalingPercent, isCommentEditor) => { const cols = []; const map = TableMap.get(table); // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion table.content.firstChild.content.forEach(cell => { const colspan = cell.attrs.colspan || 1; if (Array.isArray(cell.attrs.colwidth)) { // We slice here to guard against our colwidth array having more entries // Than the we actually span. We'll patch the document at a later point. if (tableRef) { var _table$attrs; // if we have tableRef here, isTableScalingEnabled is true let scalePercent = 1; if (isCommentEditor && !((_table$attrs = table.attrs) !== null && _table$attrs !== void 0 && _table$attrs.width)) { scalePercent = getScalingPercentForTableWithoutWidth(table, tableRef); } else { scalePercent = getTableScalingPercent(table, tableRef, shouldUseIncreasedScalingPercent); } cell.attrs.colwidth.slice(0, colspan).forEach(width => { // existing logic for calculating the width of the column const fixedColWidth = getColWidthFix(width, map.width); const scaledWidth = fixedColWidth * scalePercent; const finalWidth = Math.max(scaledWidth, tableCellMinWidth); cols.push(['col', { style: `width: ${finalWidth}px;` }]); }); } else { cell.attrs.colwidth.slice(0, colspan).forEach(width => { cols.push(['col', { style: `width: ${getColWidthFix(width ? Math.max(width, tableCellMinWidth) : tableCellMinWidth, map.width)}px;` }]); }); } } else { // When we have merged cells on the first row (firstChild), // We want to ensure we're creating the appropriate amount of // cols the table still has. cols.push(...Array.from({ length: colspan }, _ => ['col', { style: `width: ${tableCellMinWidth}px;` }])); } }); return cols; }; export const insertColgroupFromNode = (tableRef, table, isTableScalingEnabled = false, shouldRemove = true, shouldUseIncreasedScalingPercent = false, isCommentEditor = false) => { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting let colgroup = tableRef === null || tableRef === void 0 ? void 0 : tableRef.querySelector('colgroup'); if (colgroup && shouldRemove) { tableRef === null || tableRef === void 0 ? void 0 : tableRef.removeChild(colgroup); } colgroup = renderColgroupFromNode(table, isTableScalingEnabled ? tableRef !== null && tableRef !== void 0 ? tableRef : undefined : undefined, shouldUseIncreasedScalingPercent, isCommentEditor); if (shouldRemove) { tableRef === null || tableRef === void 0 ? void 0 : tableRef.insertBefore(colgroup, tableRef === null || tableRef === void 0 ? void 0 : tableRef.firstChild); } return colgroup.children; }; export const hasTableBeenResized = table => { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return !!getFragmentBackingArray(table.content.firstChild.content).find(cell => cell.attrs.colwidth); }; export const hasTableColumnBeenResized = hasTableBeenResized; /** * Check if a table has all the column width set to tableCellMinWidth(48px) or null * * @param table * @returns true if all column width is equal to tableCellMinWidth or null, false otherwise */ export const isMinCellWidthTable = table => { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const cellArray = getFragmentBackingArray(table.content.firstChild.content); const isTableMinCellWidth = cellArray.every(cell => { return cell.attrs.colwidth && cell.attrs.colwidth[0] === tableCellMinWidth || cell.attrs.colwidth === null; }); return isTableMinCellWidth; }; function renderColgroupFromNode(table, maybeTableRef, shouldUseIncreasedScalingPercent, isCommentEditor) { const rendered = DOMSerializer.renderSpec(document, ['colgroup', {}, ...generateColgroup(table, maybeTableRef, shouldUseIncreasedScalingPercent, isCommentEditor)]); // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting return rendered.dom; } export const getColgroupChildrenLength = table => { const map = TableMap.get(table); return map.width; }; export const getResizerMinWidth = node => { const currentColumnCount = getColgroupChildrenLength(node); const minColumnWidth = Math.min(3, currentColumnCount) * COLUMN_MIN_WIDTH; // add an extra pixel as the scale table logic will scale columns to be tableContainerWidth - 1 // the table can't scale past its min-width, so instead restrict table container min width to avoid that situation return minColumnWidth + 1; };