1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
let selectedIndex = -1;
export function initSelection() {
const grid = document.getElementById('media-grid');
if (!grid) return;
grid.addEventListener('click', (e) => {
const card = e.target.closest('.media-card, .media-row');
if (!card) return;
selectByElement(card);
});
}
function refreshCards() {
return Array.from(document.querySelectorAll('.media-card, .media-row, .folder-card'));
}
// Compute grid geometry from the rendered cards.
function gridGeometry(cards) {
if (!cards.length) return { cols: 0, rowMap: [] };
const first = cards[0].getBoundingClientRect();
let colCount = 0;
for (let i = 0; i < cards.length; i++) {
const r = cards[i].getBoundingClientRect();
if (Math.round(r.top) !== Math.round(first.top)) break;
colCount++;
}
// rowMap[i] = row index of cards[i]
const rowMap = cards.map((_, i) => Math.floor(i / colCount));
return { cols: colCount || 1, rowMap };
}
export function select(index) {
const cards = refreshCards();
if (!cards.length) { selectedIndex = -1; return; }
selectedIndex = Math.max(0, Math.min(index, cards.length - 1));
cards.forEach((c) => c.classList.remove('selected'));
const el = cards[selectedIndex];
if (el) {
el.classList.add('selected');
el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
}
}
export function selectByElement(el) {
const cards = refreshCards();
const idx = cards.indexOf(el);
if (idx >= 0) select(idx);
}
export function next(step = 1) {
const cards = refreshCards();
if (!cards.length) return;
select((selectedIndex + step + cards.length) % cards.length);
}
export function prev(step = 1) { next(-step); }
// Grid-aware keyboard navigation
export function navLeft() {
const cards = refreshCards();
if (!cards.length) return;
const { cols } = gridGeometry(cards);
if (cols === 0) return;
const row = Math.floor(selectedIndex / cols);
const col = selectedIndex % cols;
if (col > 0) {
select(selectedIndex - 1);
} else if (row > 0) {
// wrap to end of previous row
select(row * cols - 1);
}
}
export function navRight() {
const cards = refreshCards();
if (!cards.length) return;
const { cols } = gridGeometry(cards);
if (cols === 0) return;
const row = Math.floor(selectedIndex / cols);
const col = selectedIndex % cols;
const lastInRow = Math.min((row + 1) * cols, cards.length) - 1;
if (col < (lastInRow - row * cols)) {
select(selectedIndex + 1);
} else if (selectedIndex < cards.length - 1) {
// wrap to start of next row
select(Math.min((row + 1) * cols, cards.length - 1));
}
}
export function navUp() {
const cards = refreshCards();
if (!cards.length) return;
const { cols } = gridGeometry(cards);
if (cols === 0) return;
const idxAbove = selectedIndex - cols;
if (idxAbove >= 0) {
select(idxAbove);
}
}
export function navDown() {
const cards = refreshCards();
if (!cards.length) return;
const { cols } = gridGeometry(cards);
if (cols === 0) return;
const idxBelow = selectedIndex + cols;
if (idxBelow < cards.length) {
select(idxBelow);
}
}
export function currentIndex() { return selectedIndex; }
export function currentElement() { return refreshCards()[selectedIndex] ?? null; }
|