feat: add overlay image loading state
This commit is contained in:
@@ -619,6 +619,12 @@ function htmlPage(): string {
|
||||
color: var(--timeline-text);
|
||||
}
|
||||
|
||||
.overlay-media {
|
||||
position: relative;
|
||||
min-height: 48vh;
|
||||
background: var(--timeline-surface-strong);
|
||||
}
|
||||
|
||||
.overlay-card header {
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid var(--timeline-border);
|
||||
@@ -635,6 +641,43 @@ function htmlPage(): string {
|
||||
max-height: 78vh;
|
||||
object-fit: contain;
|
||||
background: var(--timeline-surface-strong);
|
||||
transition: opacity 120ms ease;
|
||||
}
|
||||
|
||||
.overlay-card.loading img {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.overlay-loading {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: none;
|
||||
place-items: center;
|
||||
gap: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: var(--timeline-text-muted);
|
||||
}
|
||||
|
||||
.overlay-loading.active {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.overlay-loading .spinner {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.overlay-loading.error .spinner {
|
||||
animation: none;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.overlay-loading strong {
|
||||
color: var(--timeline-text);
|
||||
font-size: 0.92rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.close {
|
||||
@@ -1015,7 +1058,13 @@ function htmlPage(): string {
|
||||
<span>Close</span>
|
||||
</button>
|
||||
</header>
|
||||
<img id="overlay-image" alt="" />
|
||||
<div class="overlay-media">
|
||||
<div class="overlay-loading" id="overlay-loading" aria-live="polite" aria-atomic="true">
|
||||
<span class="spinner active" aria-hidden="true"></span>
|
||||
<strong id="overlay-loading-text">Loading full-size image...</strong>
|
||||
</div>
|
||||
<img id="overlay-image" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1079,6 +1128,8 @@ function htmlPage(): string {
|
||||
const overlay = mustGet("overlay");
|
||||
const overlayTitle = mustGet("overlay-title");
|
||||
const overlayImage = mustGet("overlay-image");
|
||||
const overlayLoading = mustGet("overlay-loading");
|
||||
const overlayLoadingText = mustGet("overlay-loading-text");
|
||||
const closeOverlay = mustGet("close-overlay");
|
||||
const photoList = mustGet("photo-list");
|
||||
const emptyState = mustGet("empty-state");
|
||||
@@ -1104,6 +1155,7 @@ function htmlPage(): string {
|
||||
const importStatus = mustGet("share-status");
|
||||
const importButton = mustGet("load-share");
|
||||
const stopImportButton = mustGet("cancel-share");
|
||||
let overlayLoadToken = 0;
|
||||
|
||||
function mustGet(id) {
|
||||
const element = document.getElementById(id);
|
||||
@@ -1568,11 +1620,34 @@ function htmlPage(): string {
|
||||
}
|
||||
|
||||
function openOverlay(photo) {
|
||||
const requestToken = ++overlayLoadToken;
|
||||
overlay.classList.add("open");
|
||||
overlay.setAttribute("aria-hidden", "false");
|
||||
overlayTitle.textContent = photo.name;
|
||||
overlayImage.src = photo.fullUrl;
|
||||
overlayImage.alt = photo.name;
|
||||
overlay.classList.add("loading");
|
||||
overlayLoading.classList.add("active");
|
||||
overlayLoading.classList.remove("error");
|
||||
overlayLoadingText.textContent = "Loading full-size image...";
|
||||
overlayImage.onload = () => {
|
||||
if (requestToken !== overlayLoadToken) {
|
||||
return;
|
||||
}
|
||||
overlay.classList.remove("loading");
|
||||
overlayLoading.classList.remove("active");
|
||||
overlayLoading.classList.remove("error");
|
||||
};
|
||||
overlayImage.onerror = () => {
|
||||
if (requestToken !== overlayLoadToken) {
|
||||
return;
|
||||
}
|
||||
overlayLoadingText.textContent = "Could not load full-size image.";
|
||||
overlay.classList.remove("loading");
|
||||
overlayLoading.classList.add("error");
|
||||
overlayLoading.classList.add("active");
|
||||
};
|
||||
overlayImage.removeAttribute("src");
|
||||
overlayImage.src = photo.fullUrl;
|
||||
}
|
||||
|
||||
function createPhotoMarkerIcon(photo) {
|
||||
@@ -1588,8 +1663,13 @@ function htmlPage(): string {
|
||||
}
|
||||
|
||||
function closeOverlayView() {
|
||||
overlayLoadToken += 1;
|
||||
overlay.classList.remove("open");
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
overlay.classList.remove("loading");
|
||||
overlayLoading.classList.remove("active");
|
||||
overlayLoading.classList.remove("error");
|
||||
overlayLoadingText.textContent = "Loading full-size image...";
|
||||
overlayImage.removeAttribute("src");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user