Add the fill handle: drag the selection's corner to copy/fill, Excel-style
- store: fillSelection(tableId, source, target) repeats a source rectangle's cells (values, formulas -- reference-adjusted via the same shiftFormula() a copy uses -- and formatting) across a target rectangle. target may extend the source in any single direction (up/down/left/right); a source bigger than one row/column cycles through modulo its own size, so it still fills correctly past a non-exact-multiple trailing repetition. - TableGrid.vue: replaces the old decorative per-active-cell square with a proper fill handle -- a small square overlaid at the bottom-right corner of the *selection* (not just the anchor cell), draggable. Dragging shows a live dashed preview of the cells about to be filled (snapping to whichever axis -- vertical or horizontal -- is dragged furthest along, same as Excel), and on mouseup commits via fillSelection() and grows the selection to source + filled area, keeping the original anchor active. Verified interactively against the dev server with Playwright: a formula (=A1*2) filled down four rows lands as =A2*2/=A3*2/=A4*2 with correct values, and a two-row source dragged right/down six cells cycles its pattern correctly (1,2,1,2,1,2, including the trailing partial cycle). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -620,7 +620,61 @@ describe('clipboard: copy / cut / paste', () => {
|
||||
expect(s.cellInput(id, coords('D2').col, coords('D2').row)).toBe('=$D$1+$A$1');
|
||||
expect(val(s, name, 'D2')).toBe(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fill handle (drag to copy/fill)', () => {
|
||||
it('fills down: repeats the source rows, formulas shifted per repetition', () => {
|
||||
const s = fresh();
|
||||
const id = s.addTable(s.doc.activeSheetId);
|
||||
const name = s.tableById(id)!.name;
|
||||
setCell(s, name, 'A1', '10');
|
||||
setCell(s, name, 'B1', '=A1*2');
|
||||
|
||||
s.fillSelection(id, { c0: 1, c1: 1, r0: 0, r1: 0 }, { c0: 1, c1: 1, r0: 1, r1: 3 });
|
||||
|
||||
expect(s.cellInput(id, coords('B2').col, coords('B2').row)).toBe('=A2*2');
|
||||
expect(s.cellInput(id, coords('B3').col, coords('B3').row)).toBe('=A3*2');
|
||||
expect(s.cellInput(id, coords('B4').col, coords('B4').row)).toBe('=A4*2');
|
||||
});
|
||||
|
||||
it('fills right: a multi-column source cycles, not just repeats the last column', () => {
|
||||
const s = fresh();
|
||||
const id = s.addTable(s.doc.activeSheetId);
|
||||
const name = s.tableById(id)!.name;
|
||||
setCell(s, name, 'A1', '1');
|
||||
setCell(s, name, 'B1', '2');
|
||||
|
||||
// 2-wide source dragged 5 columns right -> C,D,E,F,G cycle 1,2,1,2,1
|
||||
s.fillSelection(id, { c0: 0, c1: 1, r0: 0, r1: 0 }, { c0: 2, c1: 6, r0: 0, r1: 0 });
|
||||
|
||||
expect(val(s, name, 'C1')).toBe(1);
|
||||
expect(val(s, name, 'D1')).toBe(2);
|
||||
expect(val(s, name, 'E1')).toBe(1);
|
||||
expect(val(s, name, 'F1')).toBe(2);
|
||||
expect(val(s, name, 'G1')).toBe(1); // partial trailing cycle, still fills
|
||||
});
|
||||
|
||||
it('fills up/left too (a drag past the source in either direction)', () => {
|
||||
const s = fresh();
|
||||
const id = s.addTable(s.doc.activeSheetId);
|
||||
const name = s.tableById(id)!.name;
|
||||
setCell(s, name, 'C3', '9');
|
||||
|
||||
s.fillSelection(id, { c0: 2, c1: 2, r0: 2, r1: 2 }, { c0: 2, c1: 2, r0: 0, r1: 1 }); // up
|
||||
expect(val(s, name, 'C1')).toBe(9);
|
||||
expect(val(s, name, 'C2')).toBe(9);
|
||||
});
|
||||
|
||||
it('carries formatting along, overwriting the destination', () => {
|
||||
const s = fresh();
|
||||
const id = s.addTable(s.doc.activeSheetId);
|
||||
setCell(s, s.tableById(id)!.name, 'A1', 'x');
|
||||
s.setRangeFormat(id, coords('A1'), coords('A1'), { bold: true });
|
||||
|
||||
s.fillSelection(id, { c0: 0, c1: 0, r0: 0, r1: 0 }, { c0: 0, c1: 0, r0: 1, r1: 1 });
|
||||
|
||||
expect(s.cellFormat(id, coords('A2').col, coords('A2').row)).toEqual({ bold: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('text box objects', () => {
|
||||
|
||||
Reference in New Issue
Block a user