Fix layout shift from async USD prices and token balances
Reserve vertical space with min-height and placeholders for all elements populated by async data: per-address USD totals, ETH price display, token balance containers, and total value sub-line. Prevents buttons and click targets from moving when price API responds.
This commit is contained in:
@@ -274,11 +274,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="address-usd-total"
|
id="address-usd-total"
|
||||||
class="text-xs text-muted mb-3 text-right"
|
class="text-xs text-muted mb-3 text-right min-h-[1rem]"
|
||||||
></div>
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
<!-- balances -->
|
<!-- balances -->
|
||||||
<div class="border-b border-border-light pb-2 mb-2">
|
<div class="border-b border-border-light pb-2 mb-2">
|
||||||
<div id="address-balances"></div>
|
<div id="address-balances" class="min-h-[1.25rem]">
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
@@ -343,11 +347,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="address-token-usd-total"
|
id="address-token-usd-total"
|
||||||
class="text-xs text-muted mb-3 text-right"
|
class="text-xs text-muted mb-3 text-right min-h-[1rem]"
|
||||||
></div>
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
<!-- single token balance -->
|
<!-- single token balance -->
|
||||||
<div class="border-b border-border-light pb-2 mb-2">
|
<div class="border-b border-border-light pb-2 mb-2">
|
||||||
<div id="address-token-balance"></div>
|
<div id="address-token-balance" class="min-h-[1.25rem]">
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ function show() {
|
|||||||
const addrLink = etherscanAddressLink(addr.address);
|
const addrLink = etherscanAddressLink(addr.address);
|
||||||
$("address-etherscan-link").innerHTML =
|
$("address-etherscan-link").innerHTML =
|
||||||
`<a href="${addrLink}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
|
`<a href="${addrLink}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
|
||||||
$("address-usd-total").textContent = formatUsd(getAddressValueUsd(addr));
|
const usdTotal = formatUsd(getAddressValueUsd(addr));
|
||||||
|
$("address-usd-total").innerHTML = usdTotal || " ";
|
||||||
const ensEl = $("address-ens");
|
const ensEl = $("address-ens");
|
||||||
if (addr.ensName) {
|
if (addr.ensName) {
|
||||||
ensEl.innerHTML =
|
ensEl.innerHTML =
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ function show() {
|
|||||||
|
|
||||||
// USD total for this token only
|
// USD total for this token only
|
||||||
const usdVal = price ? amount * price : 0;
|
const usdVal = price ? amount * price : 0;
|
||||||
$("address-token-usd-total").textContent = formatUsd(usdVal);
|
const usdStr = formatUsd(usdVal);
|
||||||
|
$("address-token-usd-total").innerHTML = usdStr || " ";
|
||||||
|
|
||||||
// Single token balance line (no tokenId — not clickable here)
|
// Single token balance line (no tokenId — not clickable here)
|
||||||
$("address-token-balance").innerHTML = balanceLine(symbol, amount, price);
|
$("address-token-balance").innerHTML = balanceLine(symbol, amount, price);
|
||||||
|
|||||||
@@ -38,13 +38,15 @@ function renderTotalValue() {
|
|||||||
|
|
||||||
const ethPrice = getPrice("ETH");
|
const ethPrice = getPrice("ETH");
|
||||||
if (priceEl) {
|
if (priceEl) {
|
||||||
priceEl.textContent = ethPrice ? formatUsd(ethPrice) + " USD/ETH" : "";
|
priceEl.innerHTML = ethPrice
|
||||||
|
? formatUsd(ethPrice) + " USD/ETH"
|
||||||
|
: " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
const addr = findActiveAddr();
|
const addr = findActiveAddr();
|
||||||
if (!addr) {
|
if (!addr) {
|
||||||
el.textContent = "";
|
el.innerHTML = " ";
|
||||||
if (subEl) subEl.textContent = "";
|
if (subEl) subEl.innerHTML = " ";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ethBal = parseFloat(addr.balance || "0");
|
const ethBal = parseFloat(addr.balance || "0");
|
||||||
@@ -54,8 +56,8 @@ function renderTotalValue() {
|
|||||||
|
|
||||||
if (subEl) {
|
if (subEl) {
|
||||||
const totalUsd = getAddressValueUsd(addr);
|
const totalUsd = getAddressValueUsd(addr);
|
||||||
subEl.textContent =
|
subEl.innerHTML =
|
||||||
totalUsd !== null ? "Total: " + formatUsd(totalUsd) : "";
|
totalUsd !== null ? "Total: " + formatUsd(totalUsd) : " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +278,7 @@ function render(ctx) {
|
|||||||
html += `<span class="flex-shrink-0 ml-1">${infoBtn}</span>`;
|
html += `<span class="flex-shrink-0 ml-1">${infoBtn}</span>`;
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
const addrUsd = formatUsd(getAddressValueUsd(addr));
|
const addrUsd = formatUsd(getAddressValueUsd(addr));
|
||||||
html += `<div class="text-xs text-muted text-right">${addrUsd}</div>`;
|
html += `<div class="text-xs text-muted text-right min-h-[1rem]">${addrUsd || " "}</div>`;
|
||||||
html += balanceLinesForAddress(
|
html += balanceLinesForAddress(
|
||||||
addr,
|
addr,
|
||||||
state.trackedTokens,
|
state.trackedTokens,
|
||||||
|
|||||||
Reference in New Issue
Block a user