diff --git a/common/changes/@visactor/vtable/fix-issue-5146-shift-arrow-selection_2026-05-25-16-36.json b/common/changes/@visactor/vtable/fix-issue-5146-shift-arrow-selection_2026-05-25-16-36.json new file mode 100644 index 000000000..fb0e0a9c5 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-issue-5146-shift-arrow-selection_2026-05-25-16-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "Fix an issue where keyboard multi-selection with Shift plus arrow keys stopped expanding after the second cell (GitHub #5146)", + "type": "patch" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} diff --git a/packages/vtable/examples/debug/issue-5146.ts b/packages/vtable/examples/debug/issue-5146.ts new file mode 100644 index 000000000..05cbb8d20 --- /dev/null +++ b/packages/vtable/examples/debug/issue-5146.ts @@ -0,0 +1,84 @@ +import * as VTable from '../../src'; + +const CONTAINER_ID = 'vTable'; +const TIP_ID = 'issue-5146-tip'; + +function createRecords(count: number) { + return Array.from({ length: count }, (_, index) => ({ + id: index + 1, + name: `name-${index + 1}`, + city: ['Beijing', 'Shanghai', 'Hangzhou', 'Shenzhen'][index % 4], + score: 60 + (index % 40), + email: `user${index + 1}@visactor.io` + })); +} + +function ensureTip() { + const existed = document.getElementById(TIP_ID); + if (existed) { + existed.remove(); + } + const tip = document.createElement('div'); + tip.id = TIP_ID; + tip.style.margin = '8px 0'; + tip.style.font = '14px/1.5 sans-serif'; + tip.textContent = [ + 'Issue #5146:', + 'click any body cell or use the preselected cell,', + 'then keep pressing Shift + Arrow keys to verify the selection range expands continuously.' + ].join(' '); + const container = document.getElementById(CONTAINER_ID); + container?.parentElement?.insertBefore(tip, container); +} + +export function createTable() { + ensureTip(); + + const option: VTable.ListTableConstructorOptions = { + container: document.getElementById(CONTAINER_ID), + records: createRecords(30), + columns: [ + { + field: 'id', + title: 'ID', + width: 80 + }, + { + field: 'name', + title: 'Name', + width: 180 + }, + { + field: 'city', + title: 'City', + width: 160 + }, + { + field: 'score', + title: 'Score', + width: 120 + }, + { + field: 'email', + title: 'Email', + width: 240 + } + ], + keyboardOptions: { + moveSelectedCellOnArrowKeys: true, + shiftMultiSelect: true + }, + select: { + headerSelectMode: 'cell' + }, + widthMode: 'standard', + defaultRowHeight: 36 + }; + + const table = new VTable.ListTable(option); + table.selectCell(1, 2); + table.getElement()?.focus(); + + const w = window as unknown as { tableInstance?: VTable.ListTable }; + w.tableInstance = table; +} diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index 9b1f6fa60..a729ef918 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -33,6 +33,10 @@ export const menus = [ { path: 'debug', name: 'issue-5114' + }, + { + path: 'debug', + name: 'issue-5146' } ] }, diff --git a/packages/vtable/src/state/select/update-position.ts b/packages/vtable/src/state/select/update-position.ts index 083ebd0aa..02c93abf7 100644 --- a/packages/vtable/src/state/select/update-position.ts +++ b/packages/vtable/src/state/select/update-position.ts @@ -145,6 +145,9 @@ export function updateSelectPosition( if (!enableShiftSelectMode) { currentRange.end = currentRange.start; } + // Keep the focus cell in sync with the latest keyboard expansion target. + cellPos.col = col; + cellPos.row = row; scenegraph.deleteLastSelectedRangeComponents(); scenegraph.updateCellSelectBorder(currentRange); // } else if (isCtrl) {