mirror of
https://github.com/aleksey-hoffman/sigma-file-manager.git
synced 2026-10-19 01:31:05 -05:00
Refine (regression): grid navigation is broken
This commit is contained in:
1 parent
195ad1cf4e
commit
a3ead6ff10
6 files changed
+163
-8
No files matched your search
+23
@@ -6,6 +6,7 @@ import { describe, expect, it } from 'vitest';
|
||||
import type { DirEntry } from '@/types/dir-entry';
|
||||
import {
|
||||
getFileBrowserGridEntryOrder,
|
||||
getFileBrowserVisualEntryOrder,
|
||||
groupFileBrowserEntries,
|
||||
} from '../file-browser-entry-groups';
|
||||
import { sortFileBrowserEntries, type DirSizesStore } from '../utils/file-browser-sort';
|
||||
@@ -119,4 +120,26 @@ describe('file browser entry groups', () => {
|
||||
'notes.txt',
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns flat order for list layout and grid order for grid layout', () => {
|
||||
const entries = [
|
||||
createEntry('photo.png', { ext: 'png' }),
|
||||
createEntry('clips', {
|
||||
is_dir: true,
|
||||
is_file: false,
|
||||
}),
|
||||
createEntry('notes.txt', { ext: 'txt' }),
|
||||
];
|
||||
|
||||
expect(getFileBrowserVisualEntryOrder(entries, 'list').map(entry => entry.name)).toEqual([
|
||||
'photo.png',
|
||||
'clips',
|
||||
'notes.txt',
|
||||
]);
|
||||
expect(getFileBrowserVisualEntryOrder(entries, 'grid').map(entry => entry.name)).toEqual([
|
||||
'clips',
|
||||
'photo.png',
|
||||
'notes.txt',
|
||||
]);
|
||||
});
|
||||
});
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// License: GNU GPLv3 or later. See the license file in the project root for more information.
|
||||
// Copyright © 2021 - present Aleksey Hoffman. All rights reserved.
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { DirEntry } from '@/types/dir-entry';
|
||||
import { useFileBrowserKeyboardNavigation } from '../use-file-browser-keyboard-navigation';
|
||||
|
||||
function createEntry(name: string, options: Partial<DirEntry> = {}): DirEntry {
|
||||
const isDirectory = options.is_dir ?? false;
|
||||
|
||||
return {
|
||||
name,
|
||||
path: `/${name}`,
|
||||
is_dir: isDirectory,
|
||||
is_file: !isDirectory,
|
||||
is_hidden: false,
|
||||
is_symlink: false,
|
||||
size: 100,
|
||||
created_time: 0,
|
||||
modified_time: 0,
|
||||
accessed_time: 0,
|
||||
item_count: isDirectory ? 0 : null,
|
||||
ext: null,
|
||||
mime: null,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
function createKeyboardNavigationHarness(options: {
|
||||
entries: DirEntry[];
|
||||
layout?: () => 'list' | 'grid' | undefined;
|
||||
initialSelection?: DirEntry;
|
||||
}) {
|
||||
const entries = ref(options.entries);
|
||||
const selectedEntries = ref<DirEntry[]>(
|
||||
options.initialSelection ? [options.initialSelection] : [],
|
||||
);
|
||||
|
||||
const selectEntryByPath = vi.fn((path: string) => {
|
||||
const entry = entries.value.find(item => item.path === path);
|
||||
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedEntries.value = [entry];
|
||||
return true;
|
||||
});
|
||||
|
||||
const navigation = useFileBrowserKeyboardNavigation({
|
||||
entries,
|
||||
selectedEntries,
|
||||
layout: options.layout ?? (() => 'grid'),
|
||||
selectEntryByPath,
|
||||
scrollToPath: vi.fn(async () => true),
|
||||
goBack: vi.fn(),
|
||||
openEntry: vi.fn(),
|
||||
entriesContainerRef: ref(null),
|
||||
});
|
||||
|
||||
return {
|
||||
navigation,
|
||||
selectedEntries,
|
||||
selectEntryByPath,
|
||||
};
|
||||
}
|
||||
|
||||
describe('useFileBrowserKeyboardNavigation', () => {
|
||||
it('moves through grid visual order with arrow left and right', () => {
|
||||
const clips = createEntry('clips', {
|
||||
is_dir: true,
|
||||
is_file: false,
|
||||
});
|
||||
const photo = createEntry('photo.png', {
|
||||
ext: 'png',
|
||||
mime: 'image/png',
|
||||
});
|
||||
const movie = createEntry('movie.mp4', {
|
||||
ext: 'mp4',
|
||||
mime: 'video/mp4',
|
||||
});
|
||||
const notes = createEntry('notes.txt', {
|
||||
ext: 'txt',
|
||||
mime: 'text/plain',
|
||||
});
|
||||
const { navigation, selectedEntries } = createKeyboardNavigationHarness({
|
||||
entries: [clips, movie, notes, photo],
|
||||
initialSelection: clips,
|
||||
});
|
||||
|
||||
navigation.navigateRight();
|
||||
expect(selectedEntries.value.map(entry => entry.name)).toEqual(['photo.png']);
|
||||
|
||||
navigation.navigateRight();
|
||||
expect(selectedEntries.value.map(entry => entry.name)).toEqual(['movie.mp4']);
|
||||
|
||||
navigation.navigateRight();
|
||||
expect(selectedEntries.value.map(entry => entry.name)).toEqual(['notes.txt']);
|
||||
|
||||
navigation.navigateLeft();
|
||||
expect(selectedEntries.value.map(entry => entry.name)).toEqual(['movie.mp4']);
|
||||
});
|
||||
|
||||
it('uses flat entry order in list layout', () => {
|
||||
const first = createEntry('alpha.txt', { ext: 'txt' });
|
||||
const second = createEntry('beta.png', { ext: 'png', mime: 'image/png' });
|
||||
const { navigation, selectedEntries } = createKeyboardNavigationHarness({
|
||||
entries: [first, second],
|
||||
layout: () => 'list',
|
||||
initialSelection: first,
|
||||
});
|
||||
|
||||
navigation.navigateRight();
|
||||
expect(selectedEntries.value.map(entry => entry.name)).toEqual(['beta.png']);
|
||||
});
|
||||
});
|
||||
+2
-6
@@ -4,7 +4,7 @@
|
||||
|
||||
import { nextTick, type Ref } from 'vue';
|
||||
import type { DirEntry } from '@/types/dir-entry';
|
||||
import { getFileBrowserGridEntryOrder } from '../file-browser-entry-groups';
|
||||
import { getFileBrowserVisualEntryOrder } from '../file-browser-entry-groups';
|
||||
|
||||
const ROW_TOLERANCE_PX = 30;
|
||||
const OVERLAP_TOLERANCE_PX = 2;
|
||||
@@ -53,11 +53,7 @@ export function useFileBrowserKeyboardNavigation(options: {
|
||||
}
|
||||
|
||||
function getNavigationEntries(): DirEntry[] {
|
||||
if (options.layout() === 'grid') {
|
||||
return getFileBrowserGridEntryOrder(options.entries.value);
|
||||
}
|
||||
|
||||
return options.entries.value;
|
||||
return getFileBrowserVisualEntryOrder(options.entries.value, options.layout());
|
||||
}
|
||||
|
||||
async function selectAndFocusEntry(entry: DirEntry) {
|
||||
|
||||
+8
-2
@@ -34,6 +34,7 @@ import { usePlatformStore } from '@/stores/runtime/platform';
|
||||
import { isContextMenuActionVisible } from '@/modules/navigator/components/file-browser/utils/context-menu-action-visibility';
|
||||
import { resolveNavigableItemTarget } from '@/utils/resolve-navigable-item-target';
|
||||
import type { CreateLinksResult, LinkCreationKind } from '@/utils/link-operations';
|
||||
import { getFileBrowserVisualEntryOrder } from '../file-browser-entry-groups';
|
||||
|
||||
export const FILE_BROWSER_REVEAL_STALE_FOCUS_GUARD_MS = 500;
|
||||
|
||||
@@ -44,6 +45,7 @@ export function useFileBrowserSelection(
|
||||
onOpen: (entry: DirEntry) => void,
|
||||
onOpenProperties: (entries: DirEntry[]) => void,
|
||||
onRefresh: () => void,
|
||||
layout?: () => 'list' | 'grid' | undefined,
|
||||
) {
|
||||
const { t } = useI18n();
|
||||
const platformStore = usePlatformStore();
|
||||
@@ -158,12 +160,16 @@ export function useFileBrowserSelection(
|
||||
return selectedEntries.value.some(selectedEntry => selectedEntry.path === entry.path);
|
||||
}
|
||||
|
||||
function getOrderedEntries(): DirEntry[] {
|
||||
return getFileBrowserVisualEntryOrder(entriesRef.value, layout?.());
|
||||
}
|
||||
|
||||
function getEntryIndex(entry: DirEntry): number {
|
||||
return entriesRef.value.findIndex(item => item.path === entry.path);
|
||||
return getOrderedEntries().findIndex(item => item.path === entry.path);
|
||||
}
|
||||
|
||||
function selectRange(fromEntry: DirEntry, toEntry: DirEntry) {
|
||||
const entries = entriesRef.value;
|
||||
const entries = getOrderedEntries();
|
||||
let startIndex = getEntryIndex(fromEntry);
|
||||
let endIndex = getEntryIndex(toEntry);
|
||||
|
||||
|
||||
@@ -288,6 +288,7 @@ export function useFileBrowser(options: UseFileBrowserOptions) {
|
||||
openPropertiesForSelection(entries);
|
||||
},
|
||||
dataSource.silentRefresh,
|
||||
options.layout,
|
||||
);
|
||||
|
||||
const virtualLayout = useFileBrowserVirtualLayout({
|
||||
|
||||
@@ -56,3 +56,14 @@ export function getFileBrowserGridEntryOrder(entries: readonly DirEntry[]): DirE
|
||||
...groupedEntries.others,
|
||||
];
|
||||
}
|
||||
|
||||
export function getFileBrowserVisualEntryOrder(
|
||||
entries: readonly DirEntry[],
|
||||
layout: 'list' | 'grid' | undefined,
|
||||
): DirEntry[] {
|
||||
if (layout === 'grid') {
|
||||
return getFileBrowserGridEntryOrder(entries);
|
||||
}
|
||||
|
||||
return [...entries];
|
||||
}
|
||||
Reference in new issue
Block a user