diff --git a/docker/frontend/scripts/library-check.mjs b/docker/frontend/scripts/library-check.mjs index 7ea460e..b7b85ff 100644 --- a/docker/frontend/scripts/library-check.mjs +++ b/docker/frontend/scripts/library-check.mjs @@ -386,10 +386,21 @@ await page.waitForFunction(() => document.querySelectorAll('[data-key^="lib-node }); const deepRows = await page.$$eval('[data-key^="lib-node-"]', (els) => els.length); const deepTiles = await page.$$eval(thumbs, (els) => els.length); +// The row is where a reader right-clicks, so fold-the-tree leads that row's own +// menu rather than living only on the column's name above it. +await page.click('[data-key="lib-node-CheckRoll"]', { button: 'right' }); +await page.waitForSelector('[data-key="lib-menu"]', { timeout: 10_000 }); +const rowMenu = await page.$$eval('[data-key="lib-menu"] button', (els) => els.map((b) => b.dataset.key)); +step( + 'a right click on the top-level folder row offers to fold the tree', + rowMenu.join(',') === 'lib-menu-collapse,lib-rename-CheckRoll,lib-rescan-CheckRoll,lib-drop-CheckRoll', + JSON.stringify(rowMenu), +); +await page.keyboard.press('Escape'); await page.click('[data-key="lib-root-name"]', { button: 'right' }); await page.waitForSelector('[data-key="lib-menu"]', { timeout: 10_000 }); const head = await page.$$eval('[data-key="lib-menu"] button', (els) => els.map((b) => b.dataset.key)); -step('a right click on the root name offers to fold the tree', head.join(',') === 'lib-menu-collapse', JSON.stringify(head)); +step('a right click on the column name offers it alone', head.join(',') === 'lib-menu-collapse', JSON.stringify(head)); step( 'the column name still reads as the folder it belongs to', (await page.$eval('[data-key="lib-root-name"]', (e) => e.textContent)) === 'Roll A', diff --git a/docker/frontend/src/Library.tsx b/docker/frontend/src/Library.tsx index c814586..c121cc8 100644 --- a/docker/frontend/src/Library.tsx +++ b/docker/frontend/src/Library.tsx @@ -107,8 +107,9 @@ export function Library() { const [note, setNote] = useState(null); // The folder menu: what a right click opens, and where. No folder means the // right click landed on the empty part of the column, which is the way in for - // one more folder; `root` means it landed on the column's own name — the head - // of the tree, whose one act is folding the whole roll. + // one more folder; `root` means it landed on the head of a tree — the column's + // own name, or the row of a folder picked at the top level — which carries + // folding every row that has something under it. const [menu, setMenu] = useState<{ folder: LibraryFolder | null; root?: boolean; x: number; y: number } | null>(null); // Where the drag of the column divider started, and how wide the column was // then — the pointer only ever reports a position, not a distance moved. @@ -563,14 +564,18 @@ export function Library() { }} // A folder's own menu: rename, scan again, forget. All three // act on the folder that was picked, so a right click on a - // subfolder row brings up its roll's menu. Shift+F10 raises - // the same event, which is the keyboard's way in. + // subfolder row brings up its roll's menu. A row at the top + // level is a whole tree's head, so it also carries folding + // the tree — the item the column's own name has, where a + // reader actually right-clicks. Shift+F10 raises the same + // event, which is the keyboard's way in. onContextMenu={(e) => { if (!folder) return; e.preventDefault(); const box = e.currentTarget.getBoundingClientRect(); setMenu({ folder, + root: n.depth === 0, x: Math.min(e.clientX || box.left, window.innerWidth - 190), y: Math.min(e.clientY || box.top, window.innerHeight - 96), }); @@ -715,14 +720,15 @@ export function Library() { {/* The context menu: a folder gets what belongs to a folder rather than to a frame, the empty part of the column gets the folder that is missing, and - the column's own name gets the whole tree folded. A folder whose + the head of the tree gets the whole tree folded. A folder whose permission has lapsed gets the one thing that can be done about it. */} {menu ? (
e.preventDefault()}> + {/* Fold-the-tree leads the menu of a top-level row, and stands alone on + the column's own name: every row that has something under it goes, + so a long roll comes back to one line per folder and the click that + folds a row still opens it again one at a time. */} {menu.root ? ( - // Every row that has something under it, the root's own included: a - // long roll comes back to one line per folder, and the click that - // folds a row still opens it again one at a time. - ) : !menu.folder ? ( - - ) : ready.has(menu.folder.name) ? ( + ) : null} + {menu.folder ? ( + ready.has(menu.folder.name) ? ( <> + ) + ) : menu.root ? null : ( + )}
) : null}