Roster: icon view switcher, phone table

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-28 18:58:31 +00:00
parent 0e89f37283
commit a205d429bd
2 changed files with 14 additions and 8 deletions

View file

@ -2665,8 +2665,8 @@ function rosterNav() {
function roster() {
const ordered = [...S.players].sort((a, b) => online(b) - online(a)); // stable: by donations within each
const view = (id, label) => `<button class="tab ${rosterView === id ? "on" : ""}" data-roster-view="${id}">${label}</button>`;
return `<div class="tabs sm roster-views">${view("cards", "▦ Cards")}${view("table", "☰ Table")}</div>`
const view = (id, icon, label) => `<button class="tab ${rosterView === id ? "on" : ""}" data-roster-view="${id}" title="${label}" aria-label="${label}">${icon}</button>`;
return `<div class="tabs sm roster-views">${view("cards", "▦", "Cards")}${view("table", "☰", "Table")}</div>`
+ (rosterView === "table" ? rosterTable(ordered) : rosterCards(ordered));
}
@ -2713,15 +2713,15 @@ function rosterTable(players) {
<td class="left"><div class="who-cell"><span class="avatar">${crest(p.look, 30)}${presence(p)}</span>
<div><button class="who" data-nav="character:${esc(p.id)}" title="Character sheet">${esc(p.name)}</button><div class="small muted">${rosterSub(p)}${lastSeen(p)}</div></div></div></td>
<td class="total">${p.total}</td>
<td class="left"><div class="doing small">${rosterDoing(p)}${p.garrison ? `<span class="tag warn" title="On guard">🛡️</span>` : ""}${joinButton(p)}</div></td>
<td class="left doing-col"><div class="doing small">${rosterDoing(p)}${p.garrison ? `<span class="tag warn" title="On guard">🛡️</span>` : ""}${joinButton(p)}</div></td>
${skills.map((s) => {
const lvl = p.levels[s.id];
return `<td class="${sep(s)} ${lvl <= 1 ? "low" : lvl === best[s.id] ? "best" : ""}" title="${s.name} ${lvl}">${lvl}</td>`;
}).join("")}
<td class="favour">${fmt(p.donated)}</td></tr>`).join("");
return `<div class="panel roster-table"><table>
<thead><tr><th colspan="3"></th>${groups.map(([name, list]) => `<th class="grp sep" colspan="${list.length}">${esc(name)}</th>`).join("")}<th></th></tr>
<tr><th class="left">Player</th><th>Total</th><th class="left">Doing</th>${skills.map((s) => `<th class="${sep(s)}" title="${s.name}">${s.icon}</th>`).join("")}<th class="favour" title="Given to the city">${FAVOUR}</th></tr></thead>
<thead><tr><th colspan="2"></th><th class="doing-col"></th>${groups.map(([name, list]) => `<th class="grp sep" colspan="${list.length}">${esc(name)}</th>`).join("")}<th></th></tr>
<tr><th class="left">Player</th><th>Total</th><th class="left doing-col">Doing</th>${skills.map((s) => `<th class="${sep(s)}" title="${s.name}">${s.icon}</th>`).join("")}<th class="favour" title="Given to the city">${FAVOUR}</th></tr></thead>
<tbody>${rows}</tbody></table></div>`;
}

View file

@ -318,7 +318,8 @@ table.parts td:last-child { text-align: right; font-variant-numeric: tabular-num
.ledger li { padding: 6px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.ledger .when { color: var(--muted); font-size: 12px; display: block; }
.ledger .got { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 4px; }
.roster-views { margin-bottom: 14px; }
.tabs.sm.roster-views { justify-content: flex-end; margin: -6px 0 10px; }
.roster-views .tab { padding: 3px 9px; font-size: 15px; line-height: 1.2; }
.avatar { position: relative; flex: none; line-height: 0; }
.avatar .online { position: absolute; top: -2px; right: -4px; margin: 0; border: 2px solid var(--panel); box-sizing: content-box; }
button.who { background: none; border: 0; padding: 0; font-weight: 700; text-align: left; }
@ -347,8 +348,6 @@ button.who { background: none; border: 0; padding: 0; font-weight: 700; text-ali
.roster-table table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
.roster-table th { color: var(--muted); font-weight: 600; padding: 6px 4px; font-size: 12px; }
.roster-table th .gi { width: 15px; height: 15px; }
/* The player column stays put while the skills scroll on a phone. */
.roster-table tbody td:first-child, .roster-table thead tr:last-child th:first-child { position: sticky; left: 0; z-index: 1; background: var(--panel); }
.roster-table th.grp { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; border-bottom: 1px solid var(--line); }
.roster-table td { padding: 9px 4px; border-top: 1px solid var(--line); text-align: center; white-space: nowrap; }
.roster-table tbody tr { cursor: pointer; }
@ -360,6 +359,13 @@ button.who { background: none; border: 0; padding: 0; font-weight: 700; text-ali
.roster-table td.favour, .roster-table th.favour { text-align: right; }
.who-cell, .roster-table .doing { display: flex; align-items: center; gap: 8px; }
.who-cell { gap: 10px; min-width: 170px; }
/* Phone: crest and name only, no Doing column, so the skills get the room. */
@media (max-width: 700px) {
.roster-table { padding: 4px 8px; }
.roster-table .doing-col, .who-cell .small { display: none; }
.who-cell { min-width: 0; gap: 6px; }
.roster-table td, .roster-table th { padding-left: 3px; padding-right: 3px; }
}
/* --- character sheet -------------------------------------------------- */