-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support colspan and rowspan in table actions (#356)
- Loading branch information
Showing
7 changed files
with
340 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,70 @@ | ||
import {findChildren, findParentNodeClosestToPos} from 'prosemirror-utils'; | ||
import {findParentNodeClosestToPos} from 'prosemirror-utils'; | ||
|
||
import {isTableNode, isTableRowNode} from '..'; | ||
import {isTableNode} from '..'; | ||
import type {CommandWithAttrs} from '../../core'; | ||
import {findChildIndex} from '../helpers'; | ||
import {findChildTableCells, isTableBodyNode, isTableCellNode} from '../utils'; | ||
import {CellPos, TableDesc} from '../table-desc'; | ||
|
||
export const appendColumn: CommandWithAttrs<{ | ||
tablePos: number; | ||
columnNumber?: number; | ||
direction?: 'before' | 'after'; | ||
// eslint-disable-next-line complexity | ||
}> = (state, dispatch, _, attrs) => { | ||
if (!attrs) return false; | ||
const {tablePos, columnNumber, direction} = attrs; | ||
const parentTable = findParentNodeClosestToPos(state.doc.resolve(tablePos + 1), isTableNode) | ||
?.node; | ||
|
||
if (!parentTable) return false; | ||
let parentCell; | ||
let parentRow; | ||
const {tablePos, columnNumber, direction = 'after'} = attrs; | ||
const res = findParentNodeClosestToPos(state.doc.resolve(tablePos + 1), isTableNode); | ||
if (!res) return false; | ||
|
||
const tableBody = findChildren(parentTable, isTableBodyNode, false).pop(); | ||
if (!tableBody) return false; | ||
const tableNode = res.node; | ||
const tableDesc = TableDesc.create(tableNode); | ||
if (!tableDesc) return false; | ||
|
||
if (columnNumber !== undefined) { | ||
parentCell = findChildTableCells(parentTable)[columnNumber]; | ||
parentRow = findParentNodeClosestToPos( | ||
state.doc.resolve(tablePos + parentCell.pos + 1), | ||
isTableRowNode, | ||
); | ||
} else { | ||
parentRow = findChildren(tableBody.node, isTableRowNode, false).pop(); | ||
if (!parentRow) return false; | ||
const columnIndex = columnNumber ?? tableDesc.cols - 1; // if columnNumber is not defined, that means last row | ||
const isFirstColumn = columnIndex === 0; | ||
const isLastColumn = columnIndex === tableDesc.cols - 1; | ||
|
||
parentCell = findChildren(parentRow.node, isTableCellNode, false).pop(); | ||
} | ||
|
||
if (!parentCell || !parentRow || !parentTable) { | ||
return false; | ||
} | ||
let pos: number[] | null = null; | ||
if (isFirstColumn && direction === 'before') | ||
pos = tableDesc.getRelativePosForColumn(0).map(fromOrClosest); | ||
if (isLastColumn && direction === 'after') | ||
pos = tableDesc.getRelativePosForColumn(tableDesc.cols - 1).map(toOrClosest); | ||
|
||
const parentCellIndex = columnNumber || findChildIndex(parentRow.node, parentCell.node); | ||
if (!pos) { | ||
if (tableDesc.cols <= columnIndex) return false; | ||
|
||
if (parentCellIndex < 0) { | ||
return false; | ||
if (tableDesc.isSafeColumn(columnIndex)) { | ||
const columnPos = tableDesc.getRelativePosForColumn(columnIndex); | ||
if (direction === 'before') pos = columnPos.map(fromOrClosest); | ||
if (direction === 'after') pos = columnPos.map(toOrClosest); | ||
} else { | ||
if (direction === 'before' && tableDesc.isSafeColumn(columnIndex - 1)) | ||
pos = tableDesc.getRelativePosForColumn(columnIndex - 1).map(toOrClosest); | ||
if (direction === 'after' && tableDesc.isSafeColumn(columnIndex + 1)) | ||
pos = tableDesc.getRelativePosForColumn(columnIndex + 1).map(fromOrClosest); | ||
} | ||
} | ||
|
||
if (dispatch) { | ||
const allRows = findChildren(tableBody.node, isTableRowNode, false); | ||
if (!pos) return false; | ||
|
||
let tr = state.tr; | ||
for (const row of allRows) { | ||
const rowCells = findChildren(row.node, isTableCellNode, false); | ||
const cell = rowCells[parentCellIndex]; | ||
|
||
let position = tablePos + row.pos + cell.pos + 3; | ||
position += direction === 'before' ? 0 : cell.node.nodeSize; | ||
if (dispatch) { | ||
const cellType = tableDesc.getCellNodeType(); | ||
const {tr} = state; | ||
|
||
tr = tr.insert( | ||
tr.mapping.map(position), | ||
cell.node.type.createAndFill(cell.node.attrs)!, | ||
); | ||
for (const p of pos) { | ||
tr.insert(tr.mapping.map(res.pos + p), cellType.createAndFill()!); | ||
} | ||
|
||
dispatch(tr.scrollIntoView()); | ||
dispatch(tr); | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
function fromOrClosest(pos: CellPos): number { | ||
return pos.type === 'real' ? pos.from : pos.closestPos; | ||
} | ||
|
||
function toOrClosest(pos: CellPos): number { | ||
return pos.type === 'real' ? pos.to : pos.closestPos; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,62 @@ | ||
import {Fragment, Node} from 'prosemirror-model'; | ||
import {findChildren, findParentNodeClosestToPos} from 'prosemirror-utils'; | ||
import {Node, NodeType} from 'prosemirror-model'; | ||
import {findParentNodeClosestToPos} from 'prosemirror-utils'; | ||
|
||
import {findChildTableRows, isTableBodyNode, isTableNode, isTableRowNode} from '..'; | ||
import {isTableNode} from '..'; | ||
import type {CommandWithAttrs} from '../../core'; | ||
import {TableDesc} from '../table-desc'; | ||
|
||
export const appendRow: CommandWithAttrs<{ | ||
tablePos: number; | ||
rowNumber?: number; | ||
direction?: 'before' | 'after'; | ||
// eslint-disable-next-line complexity | ||
}> = (state, dispatch, _, attrs) => { | ||
if (!attrs) return false; | ||
const {tablePos, rowNumber, direction} = attrs; | ||
|
||
const tableNode = findParentNodeClosestToPos(state.doc.resolve(tablePos + 1), isTableNode) | ||
?.node; | ||
|
||
if (!tableNode) return false; | ||
|
||
const parentBody = findChildren(tableNode, isTableBodyNode, false).pop(); | ||
if (!parentBody) return false; | ||
|
||
let parentRow; | ||
if (rowNumber !== undefined) { | ||
parentRow = findChildTableRows(tableNode)[rowNumber]; | ||
} else { | ||
parentRow = findChildren(parentBody.node, isTableRowNode, false).pop(); | ||
const {tablePos, rowNumber, direction = 'after'} = attrs; | ||
const res = findParentNodeClosestToPos(state.doc.resolve(tablePos + 1), isTableNode); | ||
if (!res) return false; | ||
|
||
const tableNode = res.node; | ||
const tableDesc = TableDesc.create(tableNode); | ||
if (!tableDesc) return false; | ||
|
||
const rowIndex = rowNumber ?? tableDesc.rows - 1; // if rowNumber is not defined, that means last row | ||
const isFirstRow = rowIndex === 0; | ||
const isLastRow = rowIndex === tableDesc.rows - 1; | ||
|
||
let pos = -1; | ||
if (isFirstRow && direction === 'before') pos = tableDesc.getRelativePosForRow(0).from; | ||
if (isLastRow && direction === 'after') | ||
pos = tableDesc.getRelativePosForRow(tableDesc.rows - 1).to; | ||
|
||
if (pos === -1) { | ||
if (tableDesc.rows <= rowIndex) return false; | ||
|
||
if (tableDesc.isSafeRow(rowIndex)) { | ||
const rowPos = tableDesc.getRelativePosForRow(rowIndex); | ||
if (direction === 'before') pos = rowPos.from; | ||
if (direction === 'after') pos = rowPos.to; | ||
} else { | ||
if (direction === 'before' && tableDesc.isSafeRow(rowIndex - 1)) | ||
pos = tableDesc.getRelativePosForRow(rowIndex - 1).to; | ||
if (direction === 'after' && tableDesc.isSafeRow(rowIndex + 1)) | ||
pos = tableDesc.getRelativePosForRow(rowIndex + 1).from; | ||
} | ||
} | ||
|
||
if (!parentRow) { | ||
return false; | ||
} | ||
if (pos === -1) return false; | ||
|
||
if (dispatch) { | ||
const newCellNodes: Node[] = []; | ||
parentRow.node.forEach((node) => { | ||
newCellNodes.push(node.type.createAndFill(node.attrs)!); | ||
}); | ||
|
||
let position = tablePos + parentRow.pos; | ||
position += direction === 'before' ? 1 : parentRow.node.nodeSize + 1; | ||
|
||
dispatch(state.tr.insert(position, parentRow.node.copy(Fragment.from(newCellNodes)))); | ||
const cells = getNodes(tableDesc.getCellNodeType(), tableDesc.cols); | ||
dispatch(state.tr.insert(res.pos + pos, tableDesc.getRowNodeType().create(null, cells))); | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
function getNodes(type: NodeType, count: number) { | ||
const nodes: Node[] = []; | ||
for (let i = 0; i < count; i++) nodes.push(type.createAndFill()!); | ||
return nodes; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.