mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
fix: hide filter boxes and picker until wired
A filter box read "Filter Options41 rows" in the page text and the file picker an empty "Setup Choose a setup": both do nothing without the script. They are written hidden, keep their room so nothing moves, and site.ts or the picker's script shows them. The count describes the input and is spaced from the label. One box markup for the build and the generator, and the Markdown copy skips hidden chrome.
This commit is contained in:
1 parent
fda6845289
commit
c916503958
10 files changed
+65
-25
No files matched your search
@@ -3818,14 +3818,8 @@ def _platform_file_list(rows: list[str], has_path: bool, anchor: str) -> list[st
|
||||
"""
|
||||
lines: list[str] = []
|
||||
if len(rows) >= FILTER_ROWS:
|
||||
field = f"rb-files-filter-{anchor}"
|
||||
lines += [
|
||||
'<div class="rb-table-filter">'
|
||||
f'<label for="{field}">Filter files</label>'
|
||||
f'<input id="{field}" type="search" autocomplete="off" placeholder="File, path, status, hash...">'
|
||||
f'<span class="rb-table-filter-count" aria-live="polite">{len(rows):,} rows</span></div>',
|
||||
"",
|
||||
]
|
||||
lines += [siteentries.filter_box(f"rb-files-filter-{anchor}", "files",
|
||||
["File", "path", "status", "hash"], len(rows)), ""]
|
||||
kind = "rb-files rb-files-path" if has_path else "rb-files"
|
||||
return [*lines, f'<div class="{kind}">', *rows, "</div>"]
|
||||
|
||||
|
||||
@@ -123,7 +123,10 @@ class _Converter(HTMLParser):
|
||||
self._emit(f"**{' '.join(name.split())}:**")
|
||||
self._block()
|
||||
return
|
||||
if self.skip or tag in SKIPPED or attr.get("aria-hidden") == "true" \
|
||||
# A `hidden` element is chrome the page's script shows (a filter
|
||||
# box, the file picker, a setup's install chooser): a tab panel,
|
||||
# hidden too, was read above.
|
||||
if self.skip or tag in SKIPPED or attr.get("aria-hidden") == "true" or "hidden" in attr \
|
||||
or classes & {"sr-only", "sl-anchor-link", "copy", "rb-anchor"}:
|
||||
if tag not in ("br", "img", "hr", "input", "wbr"):
|
||||
self.skip += 1
|
||||
|
||||
+13
-4
@@ -47,12 +47,21 @@ def filter_placeholder(labels: list[str]) -> str:
|
||||
|
||||
|
||||
def filter_box(ident: str, heading: str, labels: list[str], rows: int, noun: str = "rows") -> str:
|
||||
"""The filter box rehype-stack-tables.mjs writes above a long table."""
|
||||
"""The filter box rehype-stack-tables.mjs writes above a long table.
|
||||
|
||||
It is `hidden` until site.ts wires it: without the script it filters
|
||||
nothing, so a reader with no JavaScript, a text dump and the search
|
||||
skip it while the rows stay. retrobios.css keeps its room meanwhile,
|
||||
so the table does not move when it shows. The count is the input's
|
||||
description, and spaces keep label, field and count apart in any text
|
||||
read from the HTML."""
|
||||
return (
|
||||
f'<div class="rb-table-filter"><label for="{ident}">Filter {html.escape(heading)}</label>'
|
||||
f'<div class="rb-table-filter" hidden {markup.search_exclude()}>'
|
||||
f'<label for="{ident}">Filter {html.escape(heading)}</label> '
|
||||
f'<input id="{ident}" type="search" autocomplete="off" '
|
||||
f'placeholder="{html.escape(filter_placeholder(labels))}">'
|
||||
f'<span class="rb-table-filter-count" aria-live="polite">{rows:,} {noun}</span></div>'
|
||||
f'placeholder="{html.escape(filter_placeholder(labels))}" aria-describedby="{ident}-count"> '
|
||||
f'<span id="{ident}-count" class="rb-table-filter-count" aria-live="polite">'
|
||||
f"{rows:,} {noun}</span></div>"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ class WideTablesStackAndFiltersAreBuilt(unittest.TestCase):
|
||||
|
||||
def test_the_filter_box_is_in_the_html(self):
|
||||
html = _built("gaps-files-to-source")
|
||||
boxes = re.findall(r'<div class="rb-table-filter">(.*?)</div>\s*<table', html, re.S)
|
||||
boxes = re.findall(r'<div class="rb-table-filter"[^>]*>(.*?)</div>\s*<table', html, re.S)
|
||||
self.assertTrue(boxes)
|
||||
for box in boxes:
|
||||
self.assertRegex(box, r'<label for="rb-table-filter-\d+">Filter [^<]+</label>')
|
||||
|
||||
@@ -49,8 +49,9 @@ class TheEntryTable(unittest.TestCase):
|
||||
def test_a_long_table_has_its_filter_box_and_stacks(self):
|
||||
lines = siteentries.entry_table(self._rows(25), gs.ENTRY_HEADS, "rb-entry-filter-x", "Required")
|
||||
text = "\n".join(lines)
|
||||
self.assertRegex(text, r'<div class="rb-table-filter"><label for="rb-entry-filter-x">'
|
||||
r"Filter Required</label><input id=\"rb-entry-filter-x\" type=\"search\"")
|
||||
self.assertRegex(text, r'<div class="rb-table-filter" hidden data-pagefind-ignore>'
|
||||
r'<label for="rb-entry-filter-x">Filter Required</label> '
|
||||
r"<input id=\"rb-entry-filter-x\" type=\"search\"")
|
||||
self.assertIn('aria-live="polite">25 rows</span></div>\n<table class="rb-entries" '
|
||||
'data-rb-stack="true">', text)
|
||||
# The Status column is empty on every row: not written.
|
||||
@@ -210,7 +211,7 @@ class TheWantedListIsOneFilteredTable(unittest.TestCase):
|
||||
self.assertIn("| Nintendo - GameCube - BIOS Images | 16 |", self.page)
|
||||
|
||||
def test_every_dump_is_one_row_under_a_filter_box(self):
|
||||
box = re.search(r'<div class="rb-table-filter">.*?</div>\n<details[^>]*>\n'
|
||||
box = re.search(r'<div class="rb-table-filter"[^>]*>.*?</div>\n<details[^>]*>\n'
|
||||
r"<summary>All 31 wanted dumps</summary>\n<table class=\"rb-wanted\"",
|
||||
self.page, re.S)
|
||||
self.assertIsNotNone(box)
|
||||
|
||||
@@ -153,7 +153,8 @@ class ARowPerFile(unittest.TestCase):
|
||||
def test_a_long_list_gets_a_filter(self):
|
||||
rows = ["<details></details>"] * generate_site.FILTER_ROWS
|
||||
block = generate_site._platform_file_list(rows, False, "arcade")
|
||||
self.assertTrue(block[0].startswith('<div class="rb-table-filter"><label for="rb-files-filter-arcade">'))
|
||||
self.assertTrue(block[0].startswith('<div class="rb-table-filter" hidden data-pagefind-ignore>'
|
||||
'<label for="rb-files-filter-arcade">Filter files</label>'))
|
||||
self.assertNotIn("rb-table-filter", "".join(
|
||||
generate_site._platform_file_list(rows[1:], False, "arcade")))
|
||||
|
||||
|
||||
@@ -20,7 +20,10 @@ const packs = Object.fromEntries(
|
||||
);
|
||||
---
|
||||
|
||||
<div class="rb-picker" data-base={base} data-release={JSON.stringify({ tag: release.tag ?? '', packs })}>
|
||||
{/* hidden until the script fills the setup list: without it the box is an
|
||||
empty "Choose a setup", which a text dump and a reader without the
|
||||
script would read. It keeps its room meanwhile, so nothing moves. */}
|
||||
<div class="rb-picker" hidden data-base={base} data-release={JSON.stringify({ tag: release.tag ?? '', packs })}>
|
||||
<fieldset>
|
||||
<label for="rb-pick-platform">Setup</label>
|
||||
<select id="rb-pick-platform" name="platform">
|
||||
@@ -29,8 +32,8 @@ const packs = Object.fromEntries(
|
||||
</fieldset>
|
||||
<div class="rb-pick-systems" hidden>
|
||||
<p class="rb-pick-systems-head">
|
||||
<strong>Systems</strong>
|
||||
<button type="button" class="rb-text-button" data-all>All</button>
|
||||
<strong>Systems</strong>{' '}
|
||||
<button type="button" class="rb-text-button" data-all>All</button>{' '}
|
||||
<button type="button" class="rb-text-button" data-none>None</button>
|
||||
</p>
|
||||
<input type="search" class="rb-pick-filter" placeholder="Type a console" aria-label="Type a console" />
|
||||
@@ -114,6 +117,7 @@ const packs = Object.fromEntries(
|
||||
const params = new URLSearchParams(location.search);
|
||||
const index: { platform: string; name: string }[] = await fetch(`${base}data/picker/index.json`).then((r) => r.json());
|
||||
for (const entry of index) select.add(new Option(entry.name, entry.platform));
|
||||
root.hidden = false;
|
||||
|
||||
const chosen = () => [...list.querySelectorAll<HTMLInputElement>('input:checked')].map((box) => box.value);
|
||||
|
||||
@@ -227,6 +231,7 @@ const packs = Object.fromEntries(
|
||||
|
||||
<!-- Global: the checkboxes are created by the script, outside Astro's scoped styles. -->
|
||||
<style is:global>
|
||||
.rb-picker[hidden] { display: block; visibility: hidden; }
|
||||
.rb-picker .rb-pick-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
|
||||
|
||||
@@ -108,10 +108,16 @@ const headingBefore = (ctx, table, title) => {
|
||||
// The label a stacked cell shows: the header's title, else its text.
|
||||
export const stackedLabel = (title, text) => String(title ?? '').trim() || text;
|
||||
|
||||
const filterBox = ({ id, heading, labels, rows }) => ({
|
||||
// The same box as siteentries.filter_box: `hidden` until site.ts wires it,
|
||||
// so a reader without the script, a text dump and the search skip a field
|
||||
// that would filter nothing. The count describes the input, and the spaces
|
||||
// keep label, field and count apart in any text read from the HTML.
|
||||
const space = () => ({ type: 'text', value: ' ' });
|
||||
|
||||
export const filterBox = ({ id, heading, labels, rows }) => ({
|
||||
type: 'element',
|
||||
tagName: 'div',
|
||||
properties: { className: ['rb-table-filter'] },
|
||||
properties: { className: ['rb-table-filter'], hidden: true, dataPagefindIgnore: true },
|
||||
children: [
|
||||
{
|
||||
type: 'element',
|
||||
@@ -119,16 +125,24 @@ const filterBox = ({ id, heading, labels, rows }) => ({
|
||||
properties: { htmlFor: id },
|
||||
children: [{ type: 'text', value: `Filter ${heading}` }],
|
||||
},
|
||||
space(),
|
||||
{
|
||||
type: 'element',
|
||||
tagName: 'input',
|
||||
properties: { id, type: 'search', autoComplete: 'off', placeholder: filterPlaceholder(labels) },
|
||||
properties: {
|
||||
id,
|
||||
type: 'search',
|
||||
autoComplete: 'off',
|
||||
placeholder: filterPlaceholder(labels),
|
||||
ariaDescribedBy: `${id}-count`,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
space(),
|
||||
{
|
||||
type: 'element',
|
||||
tagName: 'span',
|
||||
properties: { className: ['rb-table-filter-count'], ariaLive: 'polite' },
|
||||
properties: { id: `${id}-count`, className: ['rb-table-filter-count'], ariaLive: 'polite' },
|
||||
children: [{ type: 'text', value: `${rows.toLocaleString('en')} rows` }],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -158,6 +158,14 @@ const labelCells = (table: HTMLTableElement) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Every filter box is written `hidden` (siteentries.filter_box,
|
||||
// rehype-stack-tables.mjs): it filters only once this script runs, so a
|
||||
// reader without it never meets a field that does nothing. The box keeps
|
||||
// its room while hidden (retrobios.css), so showing it moves nothing.
|
||||
const showFilters = () => {
|
||||
for (const box of document.querySelectorAll<HTMLElement>('.rb-table-filter[hidden]')) box.hidden = false;
|
||||
};
|
||||
|
||||
const enhanceTables = () => {
|
||||
const tables = [...document.querySelectorAll<HTMLTableElement>('.sl-markdown-content table')];
|
||||
const enhance = (table: HTMLTableElement) => {
|
||||
@@ -1450,6 +1458,7 @@ resolveFileAddress();
|
||||
followMovedCard();
|
||||
openTargetBlock();
|
||||
addEventListener('hashchange', openTargetBlock);
|
||||
showFilters();
|
||||
enhanceTables();
|
||||
breakPaths();
|
||||
hashSearch();
|
||||
|
||||
@@ -865,6 +865,10 @@ abbr.rb-term[data-open]::after {
|
||||
align-items: end;
|
||||
margin: 1rem 0 0.45rem;
|
||||
}
|
||||
/* Written `hidden` (siteentries.filter_box, rehype-stack-tables.mjs) and
|
||||
shown by site.ts: until then it keeps its room, so the table below never
|
||||
moves, and nothing reads it. */
|
||||
.rb-table-filter[hidden] { visibility: hidden; }
|
||||
.sl-markdown-content .rb-table-filter > * { margin: 0; }
|
||||
.rb-table-filter label { grid-column: 1 / -1; color: var(--rb-text-secondary); font-size: 0.78rem; font-weight: 700; }
|
||||
.rb-table-filter input {
|
||||
|
||||
Reference in new issue
Block a user