Skip to content

Commit 163e38d

Browse files
List the tables beside the one being read (#229)
Reaching a neighbouring table meant going back to the index first. The list now sits to the left of a table's detail, on the same card as the blocks beside it, with a filter over it and the table being read filled in.
1 parent f98cce1 commit 163e38d

2 files changed

Lines changed: 99 additions & 2 deletions

File tree

crates/data-dict-cli/render/app.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,41 @@ function RelatedTablesBox({ table: t }) {
434434
</div>`;
435435
}
436436

437+
/* The list of tables beside the one being read, so a neighbour is one click away
438+
without going back to the index. It sits outside `TablePage` on purpose: that
439+
page is keyed by table name and remounts on every navigation, which would clear
440+
this filter with the very click that used it. */
441+
function TableNav({ current }) {
442+
const [filter, setFilter] = useState("");
443+
/* Starts closed so the list costs no vertical space on a phone; the toggle
444+
is hidden on wide screens, where `open` is ignored. */
445+
const [open, setOpen] = useState(false);
446+
const ql = filter.trim().toLowerCase();
447+
/* Alphabetical, not the dictionary's order: this is for finding a table by
448+
name, which is what the filter above it is for too. */
449+
const names = ALL_TABLES.map((t) => t.name).sort((a, b) => a.localeCompare(b));
450+
const shown = names.filter((n) => !ql || n.toLowerCase().includes(ql));
451+
452+
return html`<nav class=${"tnav" + (open ? " open" : "")} aria-label="Tables">
453+
<button class="tnav-toggle" type="button" aria-expanded=${open}
454+
onClick=${() => setOpen(!open)}>
455+
<span class="chev" aria-hidden="true"></span>
456+
Tables (${names.length})
457+
</button>
458+
<input class="tnav-filter" type="search" placeholder="Filter tables" autocomplete="off"
459+
value=${filter} onInput=${(e) => setFilter(e.target.value)} />
460+
${shown.length
461+
? html`<ul class="tnav-list">
462+
${shown.map((n) => html`<li key=${n}>
463+
<a class=${"tnav-item" + (n === current ? " on" : "")} href=${"#" + n}
464+
aria-current=${n === current ? "page" : null}
465+
onClick=${() => setOpen(false)}>${n}</a>
466+
</li>`)}
467+
</ul>`
468+
: html`<p class="tnav-none">No tables match.</p>`}
469+
</nav>`;
470+
}
471+
437472
/* Mounted per table (keyed by name in App), so filter and sort state start
438473
fresh on every navigation. */
439474
function TablePage({ table: t, targetCol }) {
@@ -593,8 +628,11 @@ function App() {
593628
</section>
594629
</div>
595630
${openTable &&
596-
html`<${TablePage} key=${openTable.name} table=${openTable}
597-
targetCol=${route.col} />`}
631+
html`<div id="table-view">
632+
<${TableNav} current=${openTable.name} />
633+
<${TablePage} key=${openTable.name} table=${openTable}
634+
targetCol=${route.col} />
635+
</div>`}
598636
${glossOpen && html`<${GlossaryModal} onClose=${() => setGlossOpen(false)} />`}`;
599637
}
600638

crates/data-dict-cli/render/tables.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,45 @@ abbr.gterm:hover { text-decoration-color: var(--accent1); }
143143
the card's padding leaves them just inside its border. */
144144
.tpage-list { padding: 0 14px; }
145145

146+
/* ---- the table list beside a table ---- */
147+
148+
#table-view { display: flex; align-items: flex-start; gap: 16px; }
149+
#table-view #table-page { flex: 1 1 auto; min-width: 0; }
150+
151+
/* The same card as the blocks it sits beside. It follows the page down, since a
152+
column list runs long and the point of the list is to leave from anywhere; the
153+
names scroll inside it when there are more than the window holds. */
154+
.tnav {
155+
flex: none; width: 232px;
156+
padding: 12px;
157+
border: 1px solid var(--rule);
158+
border-radius: 10px;
159+
background: var(--paper);
160+
position: sticky; top: 14px;
161+
max-height: calc(100vh - 28px);
162+
display: flex; flex-direction: column; gap: 10px;
163+
}
164+
/* Wide screens show the list outright, so the toggle only exists for the
165+
narrow-width rules in app.css. */
166+
.tnav-toggle { display: none; }
167+
.tnav-filter {
168+
flex: none; min-width: 0; padding: 7px 11px;
169+
border: 1px solid var(--rule); border-radius: 8px;
170+
background: var(--paper); color: var(--ink); font-size: var(--text-sm);
171+
}
172+
.tnav-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 0; padding: 0; list-style: none; }
173+
.tnav-item {
174+
display: block; padding: 5px 8px; border-radius: 6px;
175+
font-family: var(--font-mono); font-size: var(--text-sm);
176+
color: var(--ink); text-decoration: none;
177+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
178+
}
179+
.tnav-item:hover { background: var(--chip); }
180+
.tnav-item:focus-visible { outline: 2px solid var(--link); outline-offset: -2px; }
181+
/* the table being read, wearing the same fill as a held key badge */
182+
.tnav-item.on { background: var(--accent1); color: var(--on-accent1); font-weight: 650; }
183+
.tnav-none { margin: 0; padding: 4px 8px; color: var(--ink-faint); font-size: var(--text-sm); }
184+
146185
/* The name, its stats and the description on the left; the related-tables box
147186
set against the card's right edge, its top level with the name. */
148187
.tpage-top {
@@ -318,3 +357,23 @@ button.val { font: inherit; }
318357
.tpage-headmain { flex: 1 1 100%; }
319358
.tpage-related { max-width: none; }
320359
}
360+
361+
/* Narrow widths: the list folds behind a toggle, since an open list would
362+
push the table itself a screen or more down the page. These rules sit at
363+
the end of the last-concatenated stylesheet so they beat the base rules
364+
above at equal specificity. */
365+
@media (max-width: 720px) {
366+
#table-view { display: block; }
367+
.tnav { width: auto; position: static; max-height: none; margin-bottom: 14px; padding: 0; }
368+
.tnav-toggle {
369+
display: flex; align-items: center; gap: 8px; width: 100%;
370+
padding: 10px 12px; border: 0; background: none; cursor: pointer;
371+
color: var(--ink); font: inherit; text-align: left;
372+
}
373+
.tnav-toggle .chev { display: inline-block; transition: transform 0.15s; color: var(--ink-faint); }
374+
.tnav.open .tnav-toggle .chev { transform: rotate(90deg); }
375+
.tnav:not(.open) .tnav-filter, .tnav:not(.open) .tnav-list, .tnav:not(.open) .tnav-none { display: none; }
376+
.tnav.open { padding: 12px; }
377+
.tnav.open .tnav-toggle { padding: 0 0 2px; }
378+
.tnav.open .tnav-list { max-height: 50vh; }
379+
}

0 commit comments

Comments
 (0)