UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

292 lines 11.6 kB
import { tableMessages as messages } from '@atlaskit/editor-common/messages'; import { tableCellMinWidth } from '@atlaskit/editor-common/styles'; import { TableMap } from '@atlaskit/editor-tables'; import { findCellClosestToPos, findCellRectClosestToPos, findTableClosestToPos, getSelectionRect, isSelectionType, nextCell } from '@atlaskit/editor-tables/utils'; import { TableDecorations } from '../../types'; import { getDecorations } from '../decorations/plugin'; import { buildColumnResizingDecorations, clearColumnResizingDecorations } from '../decorations/utils/column-resizing'; import { createCommand, getPluginState } from '../plugin-factory'; import { getPluginState as getTableResizingPluginState, createCommand as tableResizingPluginCreateCommand } from '../table-resizing/plugin-factory'; import { pluginKey as tableResizingPK } from '../table-resizing/plugin-key'; import { updateControls } from '../table-resizing/utils/dom'; import { currentColWidth, getTableMaxWidth, getScalingPercentForTableWithoutWidth, getTableScalingPercent } from '../table-resizing/utils/misc'; import { resizeColumn } from '../table-resizing/utils/resize-column'; import { getResizeState } from '../table-resizing/utils/resize-state'; import { updateColumnWidths } from '../transforms/column-width'; import { createColumnLineResize, updateDecorations } from '../utils/decoration'; import { getSelectedColumnIndexes } from '../utils/selection'; const getTablePluginCommand = (actionPayload, originalTr) => { return createCommand(() => actionPayload, tr => (originalTr || tr).setMeta('addToHistory', false)); }; const updateResizeHandleAndStatePosition = (rowIndex, columnIndex, nextResizeHandlePos, nodeViewPortalProviderAPI) => (state, dispatch) => { let customTr = state.tr; const { pluginConfig: { allowColumnResizing }, getIntl, isDragAndDropEnabled } = getPluginState(state); const fakeDispatch = tr => { customTr = tr; }; if (!allowColumnResizing) { return false; } const decorationsWithWidget = buildColumnResizingDecorations(rowIndex, columnIndex, true, getIntl, nodeViewPortalProviderAPI)({ tr: customTr, decorationSet: getDecorations(state) }); const decorationsWithWidgetAndHandle = updateDecorations(customTr.doc, decorationsWithWidget, createColumnLineResize(state.selection, { right: columnIndex }, isDragAndDropEnabled), TableDecorations.COLUMN_RESIZING_HANDLE_LINE); getTablePluginCommand({ type: 'START_KEYBOARD_COLUMN_RESIZE', data: { resizeHandleRowIndex: rowIndex, resizeHandleColumnIndex: columnIndex, resizeHandleIncludeTooltip: true, isKeyboardResize: true, decorationSet: decorationsWithWidgetAndHandle } }, customTr)(state, fakeDispatch); customTr.setMeta(tableResizingPK, { type: 'SET_RESIZE_HANDLE_POSITION', data: { resizeHandlePos: nextResizeHandlePos } }); if (dispatch) { dispatch(customTr); return true; } return false; }; export const initiateKeyboardColumnResizing = ({ ariaNotify, getIntl, nodeViewPortalProviderAPI }) => (state, dispatch, view) => { const { selection } = state; const selectionRect = isSelectionType(selection, 'cell') ? // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion getSelectionRect(selection) : findCellRectClosestToPos(selection.$from); const cell = findCellClosestToPos(selection.$from); if (ariaNotify && getIntl) { ariaNotify(getIntl().formatMessage(messages.startedColumnResize), { priority: 'important' }); } if (selectionRect && cell && view) { return updateResizeHandleAndStatePosition(selectionRect.top, selectionRect.right, cell.pos, nodeViewPortalProviderAPI)(state, dispatch); } return false; }; export const activateNextResizeArea = ({ direction, ariaNotify, getIntl, nodeViewPortalProviderAPI }) => (state, dispatch, view) => { const { resizeHandlePos } = getTableResizingPluginState(state) || {}; // If No resizing has initiated, skip to regular handler if (!resizeHandlePos) { return false; } const { selection } = state; const cell = findCellClosestToPos(selection.$from); if (!cell) { // cursor position may be changed by mouse to be outside of table; return false; } const $currentCell = state.doc.resolve(resizeHandlePos); if (!$currentCell) { return false; } const tableNode = $currentCell.node(-1); const closestTable = findTableClosestToPos($currentCell); const tableMap = TableMap.get(tableNode); if (!tableNode || !closestTable || !tableMap) { return false; } const currentCellRect = tableMap.findCell($currentCell.pos - $currentCell.start(-1)); const $nextCell = nextCell($currentCell, 'horiz', direction); if (ariaNotify && getIntl) { if (direction === 1) { ariaNotify(getIntl().formatMessage(messages.focusedOtherResize, { direction: 'right' }), { priority: 'important' }); } if (direction === -1) { ariaNotify(getIntl().formatMessage(messages.focusedOtherResize, { direction: 'left' }), { priority: 'important' }); } } if ($nextCell) { // we are somewhere in between the side columns of the table const offset = $nextCell.pos - $nextCell.start(-1); const rectForNextCell = tableMap.findCell(offset); return updateResizeHandleAndStatePosition(rectForNextCell.top, rectForNextCell.right, $nextCell.pos, nodeViewPortalProviderAPI)(state, dispatch, view); } else { // current position is in the one of the side columns of the table(left or right) if (currentCellRect.left === 0) { const lastCellInCurrentRow = tableMap.positionAt(currentCellRect.top, tableMap.width - 1, tableNode) + closestTable.start; const $lastCell = state.doc.resolve(lastCellInCurrentRow); return updateResizeHandleAndStatePosition(currentCellRect.top, tableMap.width, $lastCell.pos, nodeViewPortalProviderAPI)(state, dispatch, view); } else if (tableMap.width === currentCellRect.right) { const firsCellInCurrentRow = tableMap.positionAt(currentCellRect.top, 0, tableNode) + closestTable.start; const $nextCell = state.doc.resolve(firsCellInCurrentRow); return updateResizeHandleAndStatePosition(currentCellRect.top, 1, $nextCell.pos, nodeViewPortalProviderAPI)(state, dispatch); } } return false; }; export const changeColumnWidthByStep = ({ stepSize, getEditorContainerWidth, isTableScalingEnabled, isTableFixedColumnWidthsOptionEnabled, isCommentEditor, ariaNotify, api, getIntl }) => (state, dispatch, view) => { var _originalTable$attrs; let customTr = state.tr; const fakeDispatch = tr => { customTr = tr; }; const { resizeHandlePos } = getTableResizingPluginState(state); const cell = findCellClosestToPos(state.selection.$from); if (!view || !resizeHandlePos || !cell) { return false; } const $cell = state.doc.resolve(resizeHandlePos); const tableStartPosition = $cell.start(-1); const originalTable = $cell.node(-1); const map = TableMap.get(originalTable); const domAtPos = view.domAtPos.bind(view); const colIndex = map.colCount($cell.pos - tableStartPosition) + ($cell.nodeAfter ? $cell.nodeAfter.attrs.colspan : 1) - 1; let dom = null; const domAtPosition = domAtPos(tableStartPosition); dom = domAtPosition.node instanceof HTMLTableElement ? domAtPosition.node : null; if (dom && dom.nodeName !== 'TABLE') { dom = dom.closest('table'); } const cellAttrs = cell === null || cell === void 0 ? void 0 : cell.node.attrs; const width = currentColWidth(view, cell === null || cell === void 0 ? void 0 : cell.pos, cellAttrs); tableResizingPluginCreateCommand({ type: 'SET_DRAGGING', data: { dragging: { startX: 0, startWidth: width } } })(state, fakeDispatch); const maxSize = getTableMaxWidth({ table: originalTable, tableStart: tableStartPosition, state, layout: originalTable.attrs.layout, getEditorContainerWidth }); let isTableScalingEnabledOnCurrentTable = isTableScalingEnabled; const isTableScalingWithFixedColumnWidthsOptionEnabled = isTableScalingEnabled && isTableFixedColumnWidthsOptionEnabled; if (isTableScalingWithFixedColumnWidthsOptionEnabled) { isTableScalingEnabledOnCurrentTable = originalTable.attrs.displayMode !== 'fixed'; } let shouldUseIncreasedScalingPercent = isTableScalingWithFixedColumnWidthsOptionEnabled; if (isTableScalingEnabled && isCommentEditor) { isTableScalingEnabledOnCurrentTable = true; shouldUseIncreasedScalingPercent = true; } const initialResizeState = getResizeState({ minWidth: tableCellMinWidth, maxSize, table: originalTable, tableRef: dom, start: tableStartPosition, domAtPos, isTableScalingEnabled: isTableScalingEnabledOnCurrentTable, shouldUseIncreasedScalingPercent, isCommentEditor }); updateControls()(state); const selectionRect = getSelectionRect(state.selection); const selectedColumns = selectionRect ? getSelectedColumnIndexes(selectionRect) : []; // only selected (or selected - 1) columns should be distributed const resizingSelectedColumns = selectedColumns.indexOf(colIndex) > -1 || selectedColumns.indexOf(colIndex + 1) > -1; const scalePercent = isTableScalingEnabled && isCommentEditor && !((_originalTable$attrs = originalTable.attrs) !== null && _originalTable$attrs !== void 0 && _originalTable$attrs.width) ? getScalingPercentForTableWithoutWidth(originalTable, dom) : getTableScalingPercent(originalTable, dom, shouldUseIncreasedScalingPercent); const newResizeState = resizeColumn(initialResizeState, colIndex, stepSize, dom, originalTable, resizingSelectedColumns ? selectedColumns : undefined, isTableScalingEnabled, scalePercent); customTr = updateColumnWidths(newResizeState, originalTable, tableStartPosition, api)(customTr); if (dispatch) { dispatch(customTr); } if (ariaNotify && getIntl) { ariaNotify(getIntl().formatMessage(messages.changedColumnWidth, { width: Math.floor(newResizeState.cols[colIndex].width) })); if (newResizeState.cols.length === colIndex + 1) { if (newResizeState.overflow === true) { ariaNotify(getIntl().formatMessage(messages.columnResizeLast), { priority: 'important' }); } if (newResizeState.overflow === false) { ariaNotify(getIntl().formatMessage(messages.columnResizeOverflow), { priority: 'important' }); } } } return true; }; export const stopKeyboardColumnResizing = ({ ariaNotify, getIntl, originalTr }) => (state, dispatch) => { let customTr = originalTr || state.tr; const fakeDispatch = tr => { customTr = tr; }; const decorationWithoutWidget = clearColumnResizingDecorations()({ tr: customTr, decorationSet: getDecorations(state) }); const decorationWithoutWidgetAndHandle = updateDecorations(customTr.doc, decorationWithoutWidget, [], TableDecorations.COLUMN_RESIZING_HANDLE_LINE); getTablePluginCommand({ type: 'STOP_KEYBOARD_COLUMN_RESIZE', data: { decorationSet: decorationWithoutWidgetAndHandle } }, customTr)(state, fakeDispatch); tableResizingPluginCreateCommand({ type: 'STOP_RESIZING' }, () => customTr.setMeta('scrollIntoView', false))(state, fakeDispatch); if (ariaNotify && getIntl) { ariaNotify(getIntl().formatMessage(messages.columnResizeStop), { priority: 'important' }); } if (dispatch) { dispatch(customTr); return true; } return false; };