mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 01:53:56 -06:00
feat: list long file pages as filtered tables
MAME's card pages, GroovyMAME and yaps2 read 13,000 to 16,700 words of cards. Each entry is now a table row (name, members, status, short hash) that opens to its card in a details block, with the site's filter box and row counter. Pages from 150 entries and every split part use it. Visible words: mame-files-1 16,680 to 1,612, groovymame 14,396 to 1,876. A row costs about five elements, so a part holds 180 cards: MAME goes from 8 parts to 9, the largest at 567 KB and 8,989 elements. Each wanted list (TOSEC, No-Intro, Redump) opens on its counts, then one table folded under a filter box that opens it, in place of a block per DAT. Only the names are searched: indexed descriptions and hashes put the PlayStation page ahead of the PlayStation 2 one. A filtered row stayed visible on a phone: a stacked row's display:block outweighed the hidden attribute on every stacked table.
This commit is contained in:
1 parent
7ec4d00375
commit
1409a8db6a
8 files changed
+658
-28
No files matched your search
@@ -87,9 +87,19 @@ const setScrollable = (table: HTMLTableElement, overflows: boolean) => {
|
||||
// The filter box above a long table is written when the page is built
|
||||
// (src/plugins/rehype-stack-tables.mjs), so the table never moves down once
|
||||
// drawn. Here it only filters.
|
||||
const filterTable = (table: HTMLTableElement) => {
|
||||
// A table folded in a closed block can have its box above the block
|
||||
// (the provenance lists): typing opens the block on the rows it keeps.
|
||||
const filterControls = (table: HTMLTableElement) => {
|
||||
const controls = table.previousElementSibling;
|
||||
if (!controls?.matches('.rb-table-filter')) return;
|
||||
if (controls?.matches('.rb-table-filter')) return controls;
|
||||
const block = table.closest('details');
|
||||
return block?.previousElementSibling?.matches('.rb-table-filter') ? block.previousElementSibling : null;
|
||||
};
|
||||
|
||||
const filterTable = (table: HTMLTableElement) => {
|
||||
const controls = filterControls(table);
|
||||
if (!controls) return;
|
||||
const block = controls.nextElementSibling instanceof HTMLDetailsElement ? controls.nextElementSibling : null;
|
||||
const input = controls.querySelector('input');
|
||||
const count = controls.querySelector('.rb-table-filter-count');
|
||||
if (!input || !count || input.dataset.rbWired) return;
|
||||
@@ -100,6 +110,7 @@ const filterTable = (table: HTMLTableElement) => {
|
||||
input.addEventListener('input', () => {
|
||||
searchable ??= rows.map((row) => ({ row, text: row.textContent!.normalize('NFKD').toLocaleLowerCase() }));
|
||||
const query = input.value.trim().normalize('NFKD').toLocaleLowerCase();
|
||||
if (block && query) block.open = true;
|
||||
let visible = 0;
|
||||
for (const entry of searchable) {
|
||||
entry.row.hidden = !!query && !entry.text.includes(query);
|
||||
@@ -171,6 +182,9 @@ const enhanceTables = () => {
|
||||
});
|
||||
if (document.readyState === 'complete') start();
|
||||
else addEventListener('load', start, { once: true });
|
||||
// A box above a closed block is typed in before its table is ever near
|
||||
// the screen: it is wired now.
|
||||
for (const table of tables) if (table.closest('details:not([open])') && filterControls(table)) filterTable(table);
|
||||
let timer = 0;
|
||||
addEventListener('resize', () => {
|
||||
clearTimeout(timer);
|
||||
@@ -743,6 +757,18 @@ const followMovedCard = () => {
|
||||
if (match && !target) location.replace(new URL(`files/?sha1=${match[1]}`, siteRoot()));
|
||||
};
|
||||
|
||||
// A link to a card folded in a table row (emulators with many entries)
|
||||
// opens its row. Chrome does it on its own. Where the browser left the
|
||||
// target hidden, the same address is followed again once the row is open,
|
||||
// so the browser brings the card into view as for any link.
|
||||
const openTargetBlock = () => {
|
||||
const id = decodeURIComponent(location.hash.slice(1));
|
||||
const block = id ? document.getElementById(id)?.closest('details') : null;
|
||||
if (!block || block.open) return;
|
||||
block.open = true;
|
||||
location.replace(location.hash);
|
||||
};
|
||||
|
||||
// The site search holds no hashes. A hash typed in it gets "Look up this
|
||||
// hash" as its first result, and Enter opens the lookup. The pages Pagefind
|
||||
// finds for the same characters (a file name that holds them, a word that
|
||||
@@ -948,6 +974,8 @@ const notFound = () => {
|
||||
|
||||
resolveFileAddress();
|
||||
followMovedCard();
|
||||
openTargetBlock();
|
||||
addEventListener('hashchange', openTargetBlock);
|
||||
enhanceTables();
|
||||
breakPaths();
|
||||
hashSearch();
|
||||
|
||||
@@ -530,6 +530,44 @@ body:has(.rb-sys-file, .rb-file-entry) { --sl-content-width: var(--rb-grid-width
|
||||
}
|
||||
:is(.rb-sys-file, .rb-file-entry):has(> .rb-file-title:target) { background: var(--rb-surface); }
|
||||
|
||||
/* Entries as table rows (scripts/siteentries.py): the name takes the room
|
||||
and its card opens under it. On a phone the row stacks like any table
|
||||
with a hash column, the name on the full width. */
|
||||
body:has(table.rb-entries) { --sl-content-width: var(--rb-grid-width); }
|
||||
/* A row the filter leaves out stays out on a phone, where a stacked row is
|
||||
a block: `display: block` outweighed the hidden attribute, and every
|
||||
stacked table showed all its rows under "11 of 1,037 rows". */
|
||||
.sl-markdown-content table[data-rb-stack] tr[hidden] { display: none; }
|
||||
@media (min-width: 50rem) {
|
||||
.sl-markdown-content table.rb-wanted td.rb-hash { min-width: 10rem; }
|
||||
}
|
||||
.sl-markdown-content .rb-entries-wrap { container-type: inline-size; }
|
||||
.sl-markdown-content table.rb-entries { width: 100%; }
|
||||
@media (min-width: 50rem) {
|
||||
.sl-markdown-content table.rb-entries { display: table; }
|
||||
.sl-markdown-content table.rb-entries td:first-child { width: 100%; }
|
||||
/* An open card takes the table's width, not its column's: the other
|
||||
cells of its row keep to one line, so the room under them is free. */
|
||||
.sl-markdown-content table.rb-entries tr:has(> td > details[open]) > td:not(:first-child) { white-space: nowrap; }
|
||||
.sl-markdown-content details.rb-entry[open] { contain: inline-size; }
|
||||
.sl-markdown-content details.rb-entry[open] > :not(summary) { width: 100cqi; }
|
||||
}
|
||||
.sl-markdown-content details.rb-entry { border: 0; padding: 0; }
|
||||
.sl-markdown-content details.rb-entry > summary { min-height: 24px; font-weight: 400; }
|
||||
.sl-markdown-content details.rb-entry > summary > .rb-file-title { display: inline; margin: 0; }
|
||||
.sl-markdown-content details.rb-entry[open] > summary { margin-bottom: 0.4rem; }
|
||||
.sl-markdown-content details.rb-entry ul { margin: 0.3rem 0 0; padding-left: 1.2rem; font-size: 0.85rem; }
|
||||
.sl-markdown-content details.rb-entry li { margin: 0.1rem 0; }
|
||||
.sl-markdown-content details.rb-entry:has(.rb-file-title:target) { background: var(--rb-surface); }
|
||||
@media (max-width: 50rem) {
|
||||
.sl-markdown-content table.rb-entries td:first-child { padding-left: 0.6rem; font-size: 1rem; }
|
||||
.sl-markdown-content table.rb-entries td:first-child::before { content: none; }
|
||||
/* "required for Data East DECO Cassette System" ran 12 px past a phone
|
||||
beside its label: a long badge wraps in its cell. */
|
||||
.sl-markdown-content table.rb-entries .rb-badge { white-space: normal; }
|
||||
.sl-markdown-content details.rb-entry ul { font-size: 0.75rem; }
|
||||
}
|
||||
|
||||
/* Collapsible blocks are quiet: a closed block must not outweigh the
|
||||
command it explains. Colour is kept for caution and danger. */
|
||||
.sl-markdown-content details.rb-details {
|
||||
|
||||
Reference in new issue
Block a user