Keep the first-selected cell marked as active during a multi-cell selection
The active cell (bold border/handle, formula bar target) used to track selection.focus, so it followed the mouse/cursor while dragging or shift-extending a range. Switch it to selection.anchor -- the first cell of the current selection action -- so it stays put while the range grows, matching Excel/Numbers. Tab/Enter data-entry flow already keeps anchor == focus while it moves the cursor, so that behaviour is unaffected. - document.ts: activeCell/activeCellName/activeCellInput read the anchor. - FormulaBar.vue: commits to the anchor cell to match what it displays. - TableGrid.vue: isActive() (border + handle) and F2/type-to-edit target the anchor; Home/End keep using the cursor's row via s.focus. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ watch(
|
||||
|
||||
function commit() {
|
||||
const sel = store.doc.selection;
|
||||
if (sel) store.setCellInput(sel.tableId, sel.focus.col, sel.focus.row, draft.value);
|
||||
if (sel) store.setCellInput(sel.tableId, sel.anchor.col, sel.anchor.row, draft.value);
|
||||
}
|
||||
function onEnter() {
|
||||
commit();
|
||||
|
||||
@@ -111,8 +111,12 @@ const mine = computed(
|
||||
);
|
||||
/** All selected rectangles (primary + any additional) for this table. */
|
||||
const selRects = computed(() => (mine.value ? store.selectionRects(mine.value) : []));
|
||||
/** The active cell is the anchor -- the first cell of the current selection --
|
||||
* so it stays marked while a multi-cell selection is dragged/extended out
|
||||
* from it (Excel/Numbers behaviour). During Tab/Enter data-entry flow the
|
||||
* anchor is kept equal to the flowing cell, so this still tracks it there. */
|
||||
const isActive = (c: number, r: number) =>
|
||||
!!mine.value && mine.value.focus.col === c && mine.value.focus.row === r;
|
||||
!!mine.value && mine.value.anchor.col === c && mine.value.anchor.row === r;
|
||||
const inSelection = (c: number, r: number) =>
|
||||
selRects.value.some((k) => c >= k.c0 && c <= k.c1 && r >= k.r0 && r <= k.r1);
|
||||
const colHot = (c: number) => selRects.value.some((k) => c >= k.c0 && c <= k.c1);
|
||||
@@ -481,7 +485,7 @@ function onKeydown(e: KeyboardEvent) {
|
||||
const s = mine.value;
|
||||
const t = table.value;
|
||||
if (!s || !t) return;
|
||||
const { col, row } = s.focus;
|
||||
const { row } = s.focus; // Home/End jump within the cursor's current row
|
||||
const ext = e.shiftKey;
|
||||
const mod = e.ctrlKey || e.metaKey;
|
||||
|
||||
@@ -532,7 +536,7 @@ function onKeydown(e: KeyboardEvent) {
|
||||
case 'F2':
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return beginEdit(col, row);
|
||||
return beginEdit(s.anchor.col, s.anchor.row);
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -566,7 +570,7 @@ function onKeydown(e: KeyboardEvent) {
|
||||
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
beginEdit(col, row, e.key);
|
||||
beginEdit(s.anchor.col, s.anchor.row, e.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,9 @@ describe('seed document acceptance (canvas model)', () => {
|
||||
s.setSelection({ tableId: umsatz, anchor: { col: 1, row: 1 }, focus: { col: 1, row: 5 } });
|
||||
expect(s.selectionAggregate.value.sum).toBe(6175);
|
||||
expect(s.selectionAggregate.value.count).toBe(5);
|
||||
expect(s.activeCellName.value).toBe(`Umsatz::${toA1(1, 5)}`);
|
||||
// the active cell is the anchor -- the first cell of the selection -- not
|
||||
// wherever the selection was extended to.
|
||||
expect(s.activeCellName.value).toBe(`Umsatz::${toA1(1, 1)}`);
|
||||
});
|
||||
|
||||
it('recalculation visits precedents before dependents across tables', () => {
|
||||
|
||||
@@ -993,18 +993,21 @@ function createStore() {
|
||||
});
|
||||
}
|
||||
|
||||
const activeCell = computed<CellCoord>(() => doc.selection?.focus ?? { col: 0, row: 0 });
|
||||
// The "active cell" is always the anchor -- the first cell of the current
|
||||
// selection action (click, shift-click, drag start, ...) -- so it stays put
|
||||
// while a multi-cell selection is being made, matching Excel/Numbers.
|
||||
const activeCell = computed<CellCoord>(() => doc.selection?.anchor ?? { col: 0, row: 0 });
|
||||
|
||||
const activeCellName = computed(() => {
|
||||
const sel = doc.selection;
|
||||
const t = sel ? tableById(sel.tableId) : activeTable.value;
|
||||
const cell = sel?.focus ?? { col: 0, row: 0 };
|
||||
const cell = sel?.anchor ?? { col: 0, row: 0 };
|
||||
return `${t?.name ?? '—'}::${toA1(cell.col, cell.row)}`;
|
||||
});
|
||||
|
||||
const activeCellInput = computed(() => {
|
||||
const sel = doc.selection;
|
||||
return sel ? cellInput(sel.tableId, sel.focus.col, sel.focus.row) : '';
|
||||
return sel ? cellInput(sel.tableId, sel.anchor.col, sel.anchor.row) : '';
|
||||
});
|
||||
|
||||
const selectionAggregate = computed(() => {
|
||||
|
||||
Reference in New Issue
Block a user