diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/features/ark.js | 59 | ||||
| -rw-r--r-- | src/i18n.js | 9 | ||||
| -rw-r--r-- | src/style.css | 22 |
3 files changed, 49 insertions, 41 deletions
diff --git a/src/features/ark.js b/src/features/ark.js index fe1fe90..fd4b175 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -2104,37 +2104,36 @@ export function arkFeature(ctx) { // header row and no horizontal budget at all. `.coin` brings the // on-chain coin-control look — hairlines between rows, big accent // checkboxes — so the two coin pages read as siblings. - h('div', { class: 'col', style: 'margin-top:4px' }, - h('label', { class: 'coin' }, - tick(sel.size === ids.size, (e) => { - ui.arkCoinsSel = e.target.checked ? new Set(ids) : new Set(); - render(); - }, t('arkCoinsSelectAll')), - h('span', { class: 'small faint' }, t('arkCoinsSelectAll'))), - ...spend.map((v) => { - const f = feeNowOf(v); - const xf = exitFeeOf(v); - return h('label', { class: 'coin' }, - tick(sel.has(v.id), (e) => { - e.target.checked ? sel.add(v.id) : sel.delete(v.id); + // One coin per row in a headed table — the labels live in the header + // once instead of on every row as chips. It scrolls sideways on a + // narrow screen (the wrapper, not the page) rather than wrapping. + // A tap anywhere on a row toggles it. + h('div', { class: 'coin-scroll' }, + h('table', { class: 'coin-table' }, + h('thead', {}, h('tr', {}, + h('th', {}, tick(sel.size === ids.size, (e) => { + ui.arkCoinsSel = e.target.checked ? new Set(ids) : new Set(); render(); - }), - h('div', { class: 'col grow', style: 'gap:3px;min-width:0' }, - h('span', {}, fmtAmount(v.amountSat), - h('span', { class: 'small faint' }, ' ' + unitLabel())), - // little labeled chips instead of a dot-separated line: each - // fact wears its own pill, wrapping freely on narrow screens — - // the renewal price rides here too, a chip like its siblings - h('div', { class: 'coin-chips' }, - h('span', { - class: 'coin-chip' + (v.expiryHeight && tip && v.expiryHeight - tip <= 0 ? ' warn' : ''), - title: t('arkCoinsChipExpTitle'), - }, '⏳ ' + t('arkCoinsChipExp', { d: expiresOf(v) })), - h('span', { class: 'coin-chip', title: t('arkCoinsChipExitTitle') }, - t('arkCoinsChipExit', { fee: xf == null ? '—' : fmtAmount(xf) + ' ' + unitLabel() })), - h('span', { class: 'coin-chip', title: t('arkCoinsChipRenewTitle') }, - t('arkCoinsChipRenew', { fee: renewChip(f) }))))); - })), + }, t('arkCoinsSelectAll'))), + h('th', { class: 'num' }, t('arkCoinsColAmount')), + h('th', { class: 'num', title: t('arkCoinsChipExpTitle') }, t('arkCoinsColExpiry')), + h('th', { class: 'num', title: t('arkCoinsChipExitTitle') }, t('arkCoinsColExit')), + h('th', { class: 'num', title: t('arkCoinsChipRenewTitle') }, t('arkCoinsColRenew')))), + h('tbody', {}, ...spend.map((v) => { + const f = feeNowOf(v); + const xf = exitFeeOf(v); + const due = !!(v.expiryHeight && tip && v.expiryHeight - tip <= 0); + const toggle = () => { sel.has(v.id) ? sel.delete(v.id) : sel.add(v.id); render(); }; + return h('tr', { + class: 'clickable', + onClick: (e) => { if (e.target.tagName !== 'INPUT') toggle(); }, + }, + h('td', {}, tick(sel.has(v.id), toggle)), + h('td', { class: 'num' }, fmtAmount(v.amountSat), h('span', { class: 'small faint' }, ' ' + unitLabel())), + h('td', { class: 'num' + (due ? ' warn' : '') }, expiresOf(v)), + h('td', { class: 'num' }, xf == null ? '—' : fmtAmount(xf), xf == null ? null : h('span', { class: 'small faint' }, ' ' + unitLabel())), + h('td', { class: 'num' }, renewChip(f))); + })))), h('p', { class: 'small faint', style: 'margin:4px 0 0' }, t('arkCoinsExpiryNote'))), h('div', { class: 'card col', style: 'gap:8px' }, h('h4', { style: 'margin:0' }, t('arkCoinsExitTitle')), diff --git a/src/i18n.js b/src/i18n.js index bfd6ff8..2d4e8c6 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -307,11 +307,12 @@ const STR = { arkCoinsTitle: 'Spending coins', arkCoinsIntro: 'Your Spending balance lives in {n} coins totalling {total}. Each can be pulled on-chain without the server’s help — at a cost that grows with the coin’s history.', arkCoinsIntroOne: 'Your Spending balance lives in a single coin of {total}. It can be pulled on-chain without the server’s help — at a cost that grows with the coin’s history.', - arkCoinsChipExp: 'expiry {d}', - arkCoinsChipExit: 'exit cost {fee}', - arkCoinsChipRenew: 'renewal cost {fee}', + arkCoinsColAmount: 'Amount', + arkCoinsColExpiry: 'Expiry', + arkCoinsColExit: 'Exit cost', + arkCoinsColRenew: 'Renewal', arkCoinsChipRenewTitle: 'This coin’s share of the renewal fee — the fee is priced on all the coins together and rounded up once, so tiny shares add almost nothing', - arkCoinsChipRenewTiny: 'under 1 sat', + arkCoinsChipRenewTiny: '< 1 sat', arkCoinsChipExpTitle: 'Time until this coin needs renewal', arkCoinsChipExitTitle: 'What pulling this coin on-chain by itself would cost', arkFeeChain: 'On-chain fee', diff --git a/src/style.css b/src/style.css index bcec4a5..dd90ec1 100644 --- a/src/style.css +++ b/src/style.css @@ -284,14 +284,22 @@ textarea { resize: vertical; min-height: 84px; font-family: var(--mono); font-si .coin { display: flex; align-items: center; gap: 11px; padding: 11px 2px; border-top: 1px solid var(--line); } .coin:first-child { border-top: 0; } .coin input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--accent); } -/* per-coin fact pills: each datum wears its own little labeled chip */ -.coin-chips { display: flex; gap: 6px; flex-wrap: wrap; } -.coin-chip { - display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; - border: 1px solid var(--line); border-radius: 999px; - font-size: 11.5px; color: var(--muted); white-space: nowrap; +/* the Spending coins table: labels once in the header, numbers right- + aligned in tabular figures; on a narrow screen the WRAPPER scrolls + sideways so the page itself never does */ +.coin-scroll { overflow-x: auto; margin: 4px -2px 0; -webkit-overflow-scrolling: touch; } +.coin-table { border-collapse: collapse; width: 100%; font-size: 13.5px; white-space: nowrap; } +.coin-table th { + text-align: left; font-weight: 600; font-size: 12px; color: var(--muted); + padding: 4px 5px 8px; border-bottom: 1px solid var(--line-strong); } -.coin-chip.warn { color: var(--accent-ink); border-color: var(--accent); } +.coin-table td { padding: 10px 5px; border-top: 1px solid var(--line); vertical-align: middle; } +.coin-table tbody tr:first-child td { border-top: 0; } +.coin-table th:first-child, .coin-table td:first-child { width: 22px; padding-left: 2px; padding-right: 2px; } +.coin-table .num { text-align: right; font-variant-numeric: tabular-nums; } +.coin-table td.warn { color: var(--accent-ink); font-weight: 600; } +.coin-table tr.clickable { cursor: pointer; } +.coin-table input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; display: block; } /* ---- review / summary ---- */ .summary { background: var(--surface2); border: 1px solid var(--line); border-radius: 12px; padding: 14px; } |
