Cover Files keyboard row selection

This commit is contained in:
mirivlad 2026-07-01 03:18:27 +08:00
parent d3dfa2d652
commit b0d80e230f
2 changed files with 71 additions and 4 deletions

View File

@ -174,6 +174,29 @@ test.describe('G: Files Plugin', () => {
await expect(page.locator('[data-file-name="DragTwo.txt"]')).toBeVisible(); await expect(page.locator('[data-file-name="DragTwo.txt"]')).toBeVisible();
}); });
test('files explorer supports keyboard row selection and clearing selection', async ({ page }) => {
await page.locator('.wt-label').filter({ hasText: 'Project' }).click();
await openFilesTool(page);
await expect(page.locator('.files-breadcrumb')).toContainText('Project', { timeout: 10000 });
const rows = page.locator('.files-item');
await expect(rows.nth(1)).toBeVisible();
await rows.nth(0).click();
await expect(rows.nth(0)).toHaveClass(/selected/);
await page.locator('.files-root').focus();
await page.keyboard.press('ArrowDown');
await expect(rows.nth(1)).toHaveClass(/selected/);
await expect(rows.nth(0)).not.toHaveClass(/selected/);
await page.keyboard.press('ArrowUp');
await expect(rows.nth(0)).toHaveClass(/selected/);
await page.keyboard.press('Escape');
await expect(page.locator('.files-item.selected')).toHaveCount(0);
});
test('files history persists in workspace context and handles mouse back forward buttons', async ({ page }) => { test('files history persists in workspace context and handles mouse back forward buttons', async ({ page }) => {
await page.locator('.wt-label').filter({ hasText: 'Project' }).click(); await page.locator('.wt-label').filter({ hasText: 'Project' }).click();
await openFilesTool(page); await openFilesTool(page);

View File

@ -979,11 +979,55 @@
window.addEventListener('auxclick', mouseHistory, true); window.addEventListener('auxclick', mouseHistory, true);
window.addEventListener('keydown', keyHistory); window.addEventListener('keydown', keyHistory);
c.addEventListener('keydown', function (ev) { c.addEventListener('keydown', function (ev) {
if (ev.target && ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].indexOf(ev.target.tagName) !== -1) return;
var ctrl = ev.ctrlKey || ev.metaKey; var ctrl = ev.ctrlKey || ev.metaKey;
if (ctrl && ev.key.toLowerCase() === 'a') { ev.preventDefault(); selected = {}; visible().forEach(function (item) { selected[item.relativePath] = true; }); render(); } var key = ev.key || '';
if (ctrl && ev.key.toLowerCase() === 'x') { ev.preventDefault(); cutSelection(); }
if (ctrl && ev.key.toLowerCase() === 'c') { ev.preventDefault(); copySelection(); } function currentIndex(shown) {
if (ctrl && ev.key.toLowerCase() === 'v') { ev.preventDefault(); paste(); } if (lastClicked) {
for (var i = 0; i < shown.length; i++) {
if (shown[i].relativePath === lastClicked) return i;
}
}
var keys = Object.keys(selected);
if (keys.length) {
for (var j = 0; j < shown.length; j++) {
if (selected[shown[j].relativePath]) return j;
}
}
return 0;
}
function moveSelection(delta) {
var shown = visible();
if (!shown.length) return;
var index = currentIndex(shown) + delta;
if (index < 0) index = 0;
if (index >= shown.length) index = shown.length - 1;
selected = {};
selected[shown[index].relativePath] = true;
lastClicked = shown[index].relativePath;
render();
var rows = list.querySelectorAll('.files-item');
if (rows[index]) rows[index].scrollIntoView({ block: 'nearest' });
}
if (key === 'Escape') {
ev.preventDefault();
selected = {};
lastClicked = '';
render();
return;
}
if (key === 'ArrowDown' || key === 'ArrowUp') {
ev.preventDefault();
moveSelection(key === 'ArrowDown' ? 1 : -1);
return;
}
if (ctrl && key.toLowerCase() === 'a') { ev.preventDefault(); selected = {}; visible().forEach(function (item) { selected[item.relativePath] = true; }); render(); }
if (ctrl && key.toLowerCase() === 'x') { ev.preventDefault(); cutSelection(); }
if (ctrl && key.toLowerCase() === 'c') { ev.preventDefault(); copySelection(); }
if (ctrl && key.toLowerCase() === 'v') { ev.preventDefault(); paste(); }
}); });
c.__filesCleanup = function () { c.__filesCleanup = function () {
window.removeEventListener('mousedown', mouseHistory, true); window.removeEventListener('mousedown', mouseHistory, true);