Skip to content

Commit ceea6ef

Browse files
authored
Fix #3231 (#3234)
* Fix #3231 * fix comment * fix comments
1 parent 54da667 commit ceea6ef

File tree

3 files changed

+115
-61
lines changed

3 files changed

+115
-61
lines changed

packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { DragAndDropHelper } from '../../../pluginUtils/DragAndDrop/DragAndDropH
33
import { getCMTableFromTable } from '../utils/getTableFromContentModel';
44
import type { TableEditFeature } from './TableEditFeature';
55
import {
6-
isElementOfType,
76
normalizeRect,
87
MIN_ALLOWED_TABLE_CELL_WIDTH,
98
mutateBlock,
@@ -125,6 +124,7 @@ export interface CellResizerContext {
125124
export interface CellResizerInitValue {
126125
cmTable: ReadonlyContentModelTable | undefined;
127126
anchorColumn: number | undefined;
127+
nextColumn: number;
128128
anchorRow: number | undefined;
129129
anchorRowHeight: number;
130130
allWidths: number[];
@@ -139,45 +139,67 @@ export function onDragStart(context: CellResizerContext, event: MouseEvent): Cel
139139
const rect = normalizeRect(td.getBoundingClientRect());
140140

141141
// Get cell coordinates
142-
const columnIndex = td.cellIndex;
143-
const row =
144-
td.parentElement && isElementOfType(td.parentElement, 'tr') ? td.parentElement : undefined;
145-
const rowIndex = row?.rowIndex;
146-
147-
if (rowIndex == undefined) {
148-
return {
149-
cmTable: undefined,
150-
anchorColumn: undefined,
151-
anchorRow: undefined,
152-
anchorRowHeight: -1,
153-
allWidths: [],
154-
}; // Just a fallback
155-
}
142+
let rowIndex: number | undefined;
143+
let columnIndex: number | undefined;
144+
let nextColumnIndex = -1;
156145

157146
const { editor, table } = context;
158147

159148
// Get Table block in content model
160149
const cmTable = getCMTableFromTable(editor, table);
161150

162151
if (rect && cmTable) {
152+
for (let r = 0; r < cmTable?.rows.length; r++) {
153+
for (let c = 0; c < cmTable.rows[r].cells.length; c++) {
154+
const cell = cmTable.rows[r].cells[c];
155+
156+
if (cell.cachedElement == td) {
157+
// Target cell found, record its position
158+
rowIndex = r;
159+
columnIndex = c;
160+
} else if (rowIndex != undefined && columnIndex != undefined) {
161+
// rowIndex and columnIndex are already found, we can find nextColumnIndex now
162+
if (!cell.cachedElement) {
163+
// No cached element means this cell is merged, so this could potentially be the right side of column
164+
// We are trying to find the last merged cell so we can modify its width later
165+
columnIndex = c;
166+
} else {
167+
// Since we already found the target cell, the first cell with cachedElement after that must be the next column
168+
nextColumnIndex = c;
169+
break;
170+
}
171+
}
172+
}
173+
174+
// As long as rowIndex is found, we can break here, no matter if nextColumnIndex is found or not
175+
// because for the last column case, nextColumnIndex will be -1
176+
if (rowIndex != undefined) {
177+
break;
178+
}
179+
}
180+
163181
onStart();
164182

165-
return {
166-
cmTable,
167-
anchorColumn: columnIndex,
168-
anchorRow: rowIndex,
169-
anchorRowHeight: cmTable.rows[rowIndex].height,
170-
allWidths: [...cmTable.widths],
171-
};
172-
} else {
173-
return {
174-
cmTable,
175-
anchorColumn: undefined,
176-
anchorRow: undefined,
177-
anchorRowHeight: -1,
178-
allWidths: [],
179-
}; // Just a fallback
183+
if (rowIndex !== undefined) {
184+
return {
185+
cmTable,
186+
anchorColumn: columnIndex,
187+
nextColumn: nextColumnIndex,
188+
anchorRow: rowIndex,
189+
anchorRowHeight: cmTable.rows[rowIndex].height,
190+
allWidths: [...cmTable.widths],
191+
};
192+
}
180193
}
194+
195+
return {
196+
cmTable,
197+
anchorColumn: undefined,
198+
anchorRow: undefined,
199+
anchorRowHeight: -1,
200+
allWidths: [],
201+
nextColumn: -1,
202+
}; // Just a fallback
181203
}
182204

183205
/**
@@ -191,7 +213,6 @@ export function onDraggingHorizontal(
191213
deltaX: number,
192214
deltaY: number
193215
) {
194-
const { table } = context;
195216
const { cmTable, anchorRow, anchorRowHeight } = initValue;
196217

197218
// Assign new widths and heights to the CM table
@@ -203,11 +224,15 @@ export function onDraggingHorizontal(
203224
const newHeight = Math.max(cmTable.rows[anchorRow].height, MIN_ALLOWED_TABLE_CELL_HEIGHT);
204225

205226
// Writeback CM Table size changes to DOM Table
206-
const tableRow = table.rows[anchorRow];
207-
for (let col = 0; col < tableRow.cells.length; col++) {
208-
const td = tableRow.cells[col];
209-
td.style.height = newHeight + 'px';
210-
td.style.boxSizing = 'border-box';
227+
const tableRow = cmTable.rows[anchorRow].cells;
228+
229+
for (let col = 0; col < tableRow.length; col++) {
230+
const td = tableRow[col].cachedElement;
231+
232+
if (td) {
233+
td.style.height = newHeight + 'px';
234+
td.style.boxSizing = 'border-box';
235+
}
211236
}
212237

213238
return true;
@@ -227,17 +252,15 @@ export function onDraggingVertical(
227252
deltaX: number
228253
) {
229254
const { table, isRTL } = context;
230-
const { cmTable, anchorColumn, allWidths } = initValue;
255+
const { cmTable, anchorColumn, nextColumn, allWidths } = initValue;
231256

232257
// Assign new widths and heights to the CM table
233258
if (cmTable && anchorColumn != undefined) {
234259
const mutableTable = mutateBlock(cmTable);
235260

236-
// Modify the CM Table size
237-
const lastColumn = anchorColumn == cmTable.widths.length - 1;
238261
const change = deltaX * (isRTL ? -1 : 1);
239262
// This is the last column
240-
if (lastColumn) {
263+
if (nextColumn == -1) {
241264
// Only the last column changes
242265
// Normalize the new width value
243266
const newWidth = Math.max(
@@ -248,25 +271,42 @@ export function onDraggingVertical(
248271
} else {
249272
// Any other two columns
250273
const anchorChange = allWidths[anchorColumn] + change;
251-
const nextAnchorChange = allWidths[anchorColumn + 1] - change;
274+
const nextAnchorChange = allWidths[nextColumn] - change;
252275
if (
253276
anchorChange < MIN_ALLOWED_TABLE_CELL_WIDTH ||
254277
nextAnchorChange < MIN_ALLOWED_TABLE_CELL_WIDTH
255278
) {
256279
return false;
257280
}
281+
258282
mutableTable.widths[anchorColumn] = anchorChange;
259-
mutableTable.widths[anchorColumn + 1] = nextAnchorChange;
283+
mutableTable.widths[nextColumn] = nextAnchorChange;
260284
}
261285

262-
// Writeback CM Table size changes to DOM Table
263-
for (let row = 0; row < table.rows.length; row++) {
264-
const tableRow = table.rows[row];
265-
for (let col = 0; col < tableRow.cells.length; col++) {
266-
const td = tableRow.cells[col];
267-
268-
td.style.width = cmTable.widths[col] + 'px';
269-
td.style.boxSizing = 'border-box';
286+
// Write back CM Table size changes to DOM Table
287+
for (let row = 0; row < cmTable.rows.length; row++) {
288+
const tableRow = cmTable.rows[row].cells;
289+
let lastTd: HTMLTableCellElement | null = null;
290+
let lastWidth = 0;
291+
292+
for (let col = 0; col < tableRow.length; col++) {
293+
const td = tableRow[col].cachedElement;
294+
295+
if (td) {
296+
td.style.boxSizing = 'border-box';
297+
lastTd = td;
298+
lastWidth = cmTable.widths[col];
299+
} else if (lastTd && tableRow[col].spanLeft) {
300+
lastWidth += cmTable.widths[col];
301+
} else if (tableRow[col].spanAbove) {
302+
// For span above case, we don't need to adjust width, just clear lastTd and lastWidth
303+
lastTd = null;
304+
lastWidth = 0;
305+
}
306+
307+
if (lastTd) {
308+
lastTd.style.width = lastWidth + 'px';
309+
}
270310
}
271311
}
272312

packages/roosterjs-content-model-plugins/lib/tableEdit/editors/utils/getTableFromContentModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export function getCMTableFromTable(editor: IEditor, table: HTMLTableElement) {
1010
const context = createDomToModelContext({
1111
zoomScale: editor.getDOMHelper().calculateZoomScale(),
1212
recalculateTableSize: true,
13+
allowCacheElement: true, // We need this cache so we can retrieve TD element and update TD width and height when resizing table
1314
});
1415

1516
context.elementProcessors.element(model, table, context);

packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ describe('Cell Resizer tests', () => {
1818
let targetId = 'tableCellResizerTestId';
1919
let tableEdit: TableEditPlugin;
2020
let node: HTMLDivElement;
21-
const cmTable: ContentModelTable = getModelTable(targetId);
21+
let cmTable: ContentModelTable;
2222

2323
beforeEach(() => {
2424
document.body.innerHTML = '';
2525
node = document.createElement('div');
2626
node.id = id;
2727
document.body.insertBefore(node, document.body.childNodes[0]);
2828
tableEdit = new TableEditPlugin();
29+
cmTable = getModelTable(targetId);
2930

3031
let options: EditorOptions = {
3132
plugins: [tableEdit],
@@ -88,7 +89,12 @@ describe('Cell Resizer tests', () => {
8889
describe('Resize - onDragging', () => {
8990
/************************ Resizing row related tests ************************/
9091

91-
function resizeRowTest(growth: number, cellRow: number, cellColumn: number) {
92+
function resizeRowTest(
93+
growth: number,
94+
cellRow: number,
95+
cellColumn: number,
96+
nextColumn: number
97+
) {
9298
//Arrange
9399
node.style.height = '500px';
94100
node.style.width = '500px';
@@ -108,6 +114,7 @@ describe('Cell Resizer tests', () => {
108114
anchorRow: cellRow,
109115
anchorRowHeight: cmTable.rows[cellRow].height,
110116
allWidths: cmTable.widths,
117+
nextColumn,
111118
};
112119

113120
const targetTd = (target as HTMLTableElement).rows[cellRow].cells[cellColumn];
@@ -145,26 +152,31 @@ describe('Cell Resizer tests', () => {
145152
}
146153

147154
it('increases the height of the first row', () => {
148-
resizeRowTest(1, 0, 0);
155+
resizeRowTest(1, 0, 0, 1);
149156
});
150157

151158
it('increases the height of the last row', () => {
152159
const MODEL_TABLE = cmTable;
153-
resizeRowTest(1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1);
160+
resizeRowTest(1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1, -1);
154161
});
155162

156163
it('decreases the height of the first row', () => {
157-
resizeRowTest(-1, 0, 0);
164+
resizeRowTest(-1, 0, 0, 1);
158165
});
159166

160167
it('decreases the height of the last row', () => {
161168
const MODEL_TABLE = cmTable;
162-
resizeRowTest(-1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1);
169+
resizeRowTest(-1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1, -1);
163170
});
164171

165172
/************************ Resizing column related tests ************************/
166173

167-
function resizeColumnTest(growth: number, cellRow: number, cellColumn: number) {
174+
function resizeColumnTest(
175+
growth: number,
176+
cellRow: number,
177+
cellColumn: number,
178+
nextColumn: number
179+
) {
168180
//Arrange
169181
node.style.height = '500px';
170182
node.style.width = '500px';
@@ -184,6 +196,7 @@ describe('Cell Resizer tests', () => {
184196
anchorRow: cellRow,
185197
anchorRowHeight: cmTable.rows[cellRow].height,
186198
allWidths: cmTable.widths,
199+
nextColumn,
187200
};
188201

189202
const targetTd = (target as HTMLTableElement).rows[cellRow].cells[cellColumn];
@@ -235,21 +248,21 @@ describe('Cell Resizer tests', () => {
235248
}
236249

237250
it('increases the width of the first column', () => {
238-
resizeColumnTest(1, 0, 0);
251+
resizeColumnTest(1, 0, 0, 1);
239252
});
240253

241254
it('increases the width of the last column', () => {
242255
const MODEL_TABLE = cmTable;
243-
resizeColumnTest(1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1);
256+
resizeColumnTest(1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1, -1);
244257
});
245258

246259
it('decreases the width of the first column', () => {
247-
resizeColumnTest(-1, 0, 0);
260+
resizeColumnTest(-1, 0, 0, 1);
248261
});
249262

250263
it('decreases the width of the last column', () => {
251264
const MODEL_TABLE = cmTable;
252-
resizeColumnTest(-1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1);
265+
resizeColumnTest(-1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1, -1);
253266
});
254267
});
255268
});

0 commit comments

Comments
 (0)