refactor(classifier): dedupe seltable CSS + tidy From-a-list scratch edges

- #7 seltable CSS: the classifier carried its own copy of the base seltable
  rules (diverged from shared/seltable.css — a stale sticky offset + a dead
  .seltable__filter). Bundle shared/seltable.css and keep only the
  classifier-specific catalog bits (.seltable__extra, .mdl-rev__input,
  .fromlist-*, .src-badge, #mdlTree). One source of truth, shared with tables.
- #9 Clear list now confirms when it would strand files that still need a
  revision (on a "pending" leaf) — they stay assigned under By tracking number,
  but the row to finish them here is going away, so warn first.
- #10 serialize() strips the transient row→keys hint (`placed`) — it's rebuilt
  as drops happen and was needlessly bloating every workspace autosave.

(#8, renaming the internal mdl* identifiers to match "From a list", is left as
deliberate churn-avoidance — purely cosmetic, spans template/css/js/tests/DOM
ids, and the user-facing strings already read "From a list".)

Full suite 340 passed / 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-06-13 12:16:58 -05:00
parent 921713d0a4
commit 51f5947716
4 changed files with 18 additions and 32 deletions

View file

@ -26,6 +26,7 @@ concat_files \
"../shared/profile-menu.css" \ "../shared/profile-menu.css" \
"../shared/logo.css" \ "../shared/logo.css" \
"css/base.css" \ "css/base.css" \
"../shared/seltable.css" \
"css/layout.css" \ "css/layout.css" \
"css/spreadsheet.css" \ "css/spreadsheet.css" \
> "$css_temp" > "$css_temp"

View file

@ -628,36 +628,10 @@ input.tfile__name:focus { border-color: var(--primary); background: var(--bg); o
.scratch-match__tn { font-family: var(--mono, monospace); } .scratch-match__tn { font-family: var(--mono, monospace); }
.scratch-match__conf { color: var(--text-muted); font-size: 0.72rem; width: 3rem; text-align: right; } .scratch-match__conf { color: var(--text-muted); font-size: 0.72rem; width: 3rem; text-align: right; }
/* ── Shared selectable + autofilter table (seltable) ────────────────────── */ /* The base seltable rules live in shared/seltable.css (bundled by build.sh and
.seltable { display: flex; flex-direction: column; min-height: 0; height: 100%; } shared with the tables tool); only the classifier-specific catalog bits
.seltable__bar { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--border); flex: 0 0 auto; } (.seltable__extra, .mdl-rev__input, .fromlist-*, .src-badge, #mdlTree) are
.seltable__filter { here. */
flex: 1; min-width: 8rem; padding: 0.3rem 0.5rem;
border: 1px solid var(--border); border-radius: var(--radius);
background: var(--bg-secondary, var(--bg)); color: var(--text); font-size: 0.85rem;
}
.seltable__count { color: var(--text-muted); font-size: 0.78rem; white-space: nowrap; }
.seltable__scroll { flex: 1; min-height: 0; overflow: auto; }
/* width:auto + nowrap cells → each column shrinks to fit its header/longest cell. */
.seltable__table { border-collapse: separate; border-spacing: 0; width: auto; font-size: 0.82rem; }
.seltable__table th, .seltable__table td { border-bottom: 1px solid var(--border); padding: 0.25rem 0.5rem; text-align: left; white-space: nowrap; }
.seltable__table thead th {
position: sticky; top: 0; z-index: 2; background: var(--bg-secondary, var(--bg));
color: var(--text-muted); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
}
/* Per-column filter inputs: fill the column (min-width:0-ish) so they never
force a column wider than its header/cells. */
.seltable__table thead tr.seltable__filters th { padding: 0.08rem 0.3rem; }
.seltable__colfilter {
width: 100%; min-width: 2rem; box-sizing: border-box;
padding: 0.1rem 0.3rem; border: 1px solid var(--border); border-radius: var(--radius);
background: var(--bg); color: var(--text); font-size: 0.72rem; font-weight: 400; letter-spacing: 0; text-transform: none;
}
.seltable__row { cursor: pointer; user-select: none; }
.seltable__row:hover { background: var(--bg-hover); }
.seltable__row.is-selected { background: var(--primary-light, rgba(37,99,235,0.12)); }
.seltable__row.is-selected:hover { background: var(--primary-light, rgba(37,99,235,0.18)); }
.seltable__row.drop-hover { outline: 2px solid var(--primary); outline-offset: -2px; }
/* ── Copy destination dialog ────────────────────────────────────────────── */ /* ── Copy destination dialog ────────────────────────────────────────────── */
.copy-choice__backdrop { .copy-choice__backdrop {

View file

@ -415,7 +415,11 @@
transmittalTree: state.transmittalTree, transmittalTree: state.transmittalTree,
outputName: state.outputName, outputName: state.outputName,
config: state.config, config: state.config,
mdlList: state.mdlList, // Strip the transient row→keys hint (`placed`) — it's rebuilt as
// drops happen and would otherwise bloat every autosave.
mdlList: state.mdlList.map(function (r) {
return { id: r.id, party: r.party, trackingNumber: r.trackingNumber, title: r.title, revisionCell: r.revisionCell, source: r.source, archiveRevisions: r.archiveRevisions };
}),
}; };
} }
function load(obj) { function load(obj) {

View file

@ -55,7 +55,14 @@
if (els.pasteRowsBtn) els.pasteRowsBtn.addEventListener('click', function () { openPasteDialog(''); }); if (els.pasteRowsBtn) els.pasteRowsBtn.addEventListener('click', function () { openPasteDialog(''); });
if (els.matchNamesBtn) els.matchNamesBtn.addEventListener('click', openMatchDialog); if (els.matchNamesBtn) els.matchNamesBtn.addEventListener('click', openMatchDialog);
if (els.clearListBtn) els.clearListBtn.addEventListener('click', function () { if (els.clearListBtn) els.clearListBtn.addEventListener('click', function () {
if (!C().getMdlList().length) return; var list = C().getMdlList();
if (!list.length) return;
// Warn before stranding files that still need a revision: they stay
// assigned (on a "pending" leaf under By tracking number), but the
// row you'd use to finish them here is about to disappear.
var pending = 0;
list.forEach(function (r) { if (!(r.revisionCell || '').trim()) pending += Object.keys(r.placed || {}).length; });
if (pending && !confirm(pending + ' file' + (pending === 1 ? '' : 's') + ' still need a revision. They stay assigned (a “pending” folder under By tracking number), but the list row to finish them here goes away. Clear anyway?')) return;
C().clearMdlList(); C().clearMdlList();
window.zddc.toast('List cleared — every assignment is kept (see By tracking number).', 'info'); window.zddc.toast('List cleared — every assignment is kept (see By tracking number).', 'info');
}); });