Rewrite tx list with innerHTML, fix scrollbar overlay and overflow
Some checks failed
check / check (push) Has been cancelled

- Rebuilt tx list rendering using innerHTML instead of createElement
- scrollbar-gutter: stable on body to prevent content shift
- max-width:42ch instead of width:42ch to prevent horizontal overflow
- overflow-x:hidden on body and #app
This commit is contained in:
2026-02-26 03:12:33 +07:00
parent 197f40bde5
commit 75ec67617b
5 changed files with 24 additions and 49 deletions

View File

@@ -201,7 +201,7 @@
class="flex text-xs mb-3 cursor-pointer" class="flex text-xs mb-3 cursor-pointer"
title="Click to copy" title="Click to copy"
> >
<span id="address-full" style="width: 42ch"></span> <span id="address-full" style="max-width: 42ch"></span>
<span <span
id="address-usd-total" id="address-usd-total"
class="text-right text-muted flex-1" class="text-right text-muted flex-1"

View File

@@ -16,4 +16,6 @@
body { body {
width: 396px; width: 396px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto;
scrollbar-gutter: stable;
} }

View File

@@ -99,57 +99,30 @@ function renderTransactions(txs) {
'<div class="text-muted text-xs py-1">No transactions found.</div>'; '<div class="text-muted text-xs py-1">No transactions found.</div>';
return; return;
} }
list.innerHTML = ""; let html = "";
txs.forEach((tx, i) => { let i = 0;
for (const tx of txs) {
const counterparty = tx.direction === "sent" ? tx.to : tx.from; const counterparty = tx.direction === "sent" ? tx.to : tx.from;
const dirLabel = tx.direction === "sent" ? "Sent" : "Received"; const dirLabel = tx.direction === "sent" ? "Sent" : "Received";
const errorStyle = tx.isError ? " opacity:0.5" : ""; const amountStr = escapeHtml(tx.value + " " + tx.symbol);
const row = document.createElement("div");
row.style.cssText =
"padding:0.5rem 0;border-bottom:1px solid #ccc;font-size:12px;cursor:pointer;overflow:hidden;" +
errorStyle;
const line1 = document.createElement("div");
line1.style.cssText =
"display:flex;justify-content:space-between;overflow:hidden;";
const age = document.createElement("span");
age.style.cssText =
"color:#666;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;";
age.textContent = timeAgo(tx.timestamp);
age.title = isoDate(tx.timestamp);
const dir = document.createElement("span");
dir.style.cssText =
"flex-shrink:0;padding-left:0.5rem;white-space:nowrap;" +
(tx.isError ? "color:#666;" : "");
dir.textContent = dirLabel + (tx.isError ? " (failed)" : "");
line1.appendChild(age);
line1.appendChild(dir);
const amountStr = tx.value + " " + tx.symbol;
const line2 = document.createElement("div");
line2.style.cssText =
"display:flex;justify-content:space-between;overflow:hidden;";
const addr = document.createElement("span");
addr.style.cssText =
"overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;";
const maxAddr = Math.max(10, 38 - Math.max(0, amountStr.length - 10)); const maxAddr = Math.max(10, 38 - Math.max(0, amountStr.length - 10));
addr.textContent = truncateMiddle(counterparty, maxAddr); const addrStr = escapeHtml(truncateMiddle(counterparty, maxAddr));
addr.title = counterparty; const err = tx.isError ? " (failed)" : "";
const amount = document.createElement("span"); const opacity = tx.isError ? " opacity:0.5;" : "";
amount.style.cssText = const ago = escapeHtml(timeAgo(tx.timestamp));
"flex-shrink:0;padding-left:0.5rem;white-space:nowrap;"; const iso = escapeHtml(isoDate(tx.timestamp));
amount.textContent = amountStr; html += `<div class="tx-row py-2 border-b border-border-light text-xs cursor-pointer hover:bg-hover" data-tx="${i}" style="${opacity}">`;
line2.appendChild(addr); html += `<div class="flex justify-between"><span class="text-muted" title="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
line2.appendChild(amount); html += `<div class="flex justify-between"><span>${addrStr}</span><span>${amountStr}</span></div>`;
html += `</div>`;
row.appendChild(line1); i++;
row.appendChild(line2); }
list.innerHTML = html;
list.querySelectorAll(".tx-row").forEach((row) => {
row.addEventListener("click", () => { row.addEventListener("click", () => {
state.selectedTx = i; const idx = parseInt(row.dataset.tx, 10);
showTxDetail(tx); showTxDetail(loadedTxs[idx]);
}); });
list.appendChild(row);
}); });
} }

View File

@@ -76,7 +76,7 @@ function balanceLine(symbol, amount, price) {
const usd = price ? formatUsd(amount * price) : ""; const usd = price ? formatUsd(amount * price) : "";
return ( return (
`<div class="flex text-xs">` + `<div class="flex text-xs">` +
`<span class="flex justify-between" style="width:42ch">` + `<span class="flex justify-between" style="max-width:42ch">` +
`<span>${symbol}</span>` + `<span>${symbol}</span>` +
`<span>${qty}</span>` + `<span>${qty}</span>` +
`</span>` + `</span>` +

View File

@@ -40,7 +40,7 @@ function render(ctx) {
} }
const addrUsd = formatUsd(getAddressValueUsd(addr)); const addrUsd = formatUsd(getAddressValueUsd(addr));
html += `<div class="flex text-xs">`; html += `<div class="flex text-xs">`;
html += `<span style="width:42ch">${addr.address}</span>`; html += `<span style="max-width:42ch">${addr.address}</span>`;
html += `<span class="text-right text-muted flex-1">${addrUsd}</span>`; html += `<span class="text-right text-muted flex-1">${addrUsd}</span>`;
html += `</div>`; html += `</div>`;
html += balanceLinesForAddress(addr); html += balanceLinesForAddress(addr);