From 65ee2e4d7f2be8036457e3cdca0e26e09eb56e0b Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 1 May 2026 15:57:13 +0300 Subject: add readme and so on --- web/js/selection.js | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'web/js/selection.js') diff --git a/web/js/selection.js b/web/js/selection.js index 00b91b8..c83fd81 100644 --- a/web/js/selection.js +++ b/web/js/selection.js @@ -14,6 +14,21 @@ function refreshCards() { return Array.from(document.querySelectorAll('.media-card, .media-row')); } +// 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; } @@ -39,5 +54,60 @@ export function next(step = 1) { } 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; } -- cgit v1.2.3