Realtime Merge Cells
Use the area-selection feature to select multiple cells, then right-click to open a context menu for merging or splitting cells in realtime.
Implementation Approach
- Enable the
area-selectionfeature and listen to thearea-selection-changeevent to get selection ranges - Listen to the
row-menuevent (right-click) to show a custom context menu - 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
// Merge state storage key: `${rowId}__${colIndex}` value: { rowspan, colspan }
const mergeMap = reactive(new Map<string, { rowspan: number; colspan: number }>());
// mergeCells callback in column config
function mergeCells({ row, col }) {
const colIndex = columns.findIndex(c => c.dataIndex === col.dataIndex);
return mergeMap.get(`${row.id}__${colIndex}`);
}
// Merge selected cells
function mergeSelectedCells() {
const range = selectionRanges.at(-1);
const { minRow, maxRow, minCol, maxCol } = normalizeRange(range);
const startRow = dataSource.value[minRow];
// Clear existing merge info within range to avoid conflicts
for (let r = minRow; r <= maxRow; r++)
for (let c = minCol; c <= maxCol; c++)
mergeMap.delete(`${dataSource.value[r].id}__${c}`);
// Set merge info on the top-left cell
mergeMap.set(`${startRow.id}__${minCol}`, {
rowspan: maxRow - minRow + 1,
colspan: maxCol - minCol + 1,
});
// Force table re-render
dataSource.value = dataSource.value.slice();
}
// Split selected cells
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.value[r].id}__${c}`);
}
dataSource.value = dataSource.value.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