Realtime Merge Cells
Use the areaSelection feature to select multiple cells, then right-click to open a context menu for merging or splitting cells in realtime.
Implementation Approach
- Enable the
areaSelectionfeature and listen to theonareaselectionchangeevent to get selection ranges - Listen to the
onrowmenuevent (right-click) and show a context menu with ja-contextmenu - On "Merge Cells" click, convert the selection range into
rowspan/colspaninfo stored in a Map - The
mergeCellscallback in column config reads merge info from the Map - On "Split Cells" click, remove merge info within the selection range
loading
Core Logic
typescript
// 合并状态存储 key: `${rowId}__${colIndex}` value: { rowspan, colspan }
const mergeMap = new Map<string, { rowspan: number; colspan: number }>();
// 列配置中的 mergeCells 回调
function mergeCells({ row, col }) {
const colIndex = columns.findIndex(c => c.dataIndex === col.dataIndex);
return mergeMap.get(`${row.id}__${colIndex}`);
}
// 合并选中的单元格
function mergeSelectedCells() {
const range = selectionRanges().at(-1);
const { minRow, maxRow, minCol, maxCol } = normalizeRange(range);
const startRow = dataSource()[minRow];
// 清除范围内已有的合并信息,避免冲突
for (let r = minRow; r <= maxRow; r++)
for (let c = minCol; c <= maxCol; c++)
mergeMap.delete(`${dataSource()[r].id}__${c}`);
// 在左上角单元格设置合并信息
mergeMap.set(`${startRow.id}__${minCol}`, {
rowspan: maxRow - minRow + 1,
colspan: maxCol - minCol + 1,
});
// 强制表格重新渲染
setDataSource(ds => ds.slice());
}
// 拆分选中的单元格
function splitSelectedCells() {
for (const range of selectionRanges()) {
const { minRow, maxRow, minCol, maxCol } = normalizeRange(range);
for (let r = minRow; r <= maxRow; r++)
for (let c = minCol; c <= maxCol; c++)
mergeMap.delete(`${dataSource()[r].id}__${c}`);
}
setDataSource(ds => ds.slice());
}Notes
- After merging, only the top-left cell content is displayed. Covered cell data remains in the data source and will be restored after splitting
- A new merge operation automatically clears existing merge info within the selection range to avoid conflicts
- This approach also works with virtual scrolling mode