feat: add overlay image loading state
This commit is contained in:
@@ -619,6 +619,12 @@ function htmlPage(): string {
|
|||||||
color: var(--timeline-text);
|
color: var(--timeline-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overlay-media {
|
||||||
|
position: relative;
|
||||||
|
min-height: 48vh;
|
||||||
|
background: var(--timeline-surface-strong);
|
||||||
|
}
|
||||||
|
|
||||||
.overlay-card header {
|
.overlay-card header {
|
||||||
padding: 16px 18px;
|
padding: 16px 18px;
|
||||||
border-bottom: 1px solid var(--timeline-border);
|
border-bottom: 1px solid var(--timeline-border);
|
||||||
@@ -635,6 +641,43 @@ function htmlPage(): string {
|
|||||||
max-height: 78vh;
|
max-height: 78vh;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
background: var(--timeline-surface-strong);
|
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 {
|
.close {
|
||||||
@@ -1015,9 +1058,15 @@ function htmlPage(): string {
|
|||||||
<span>Close</span>
|
<span>Close</span>
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
<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="" />
|
<img id="overlay-image" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -1079,6 +1128,8 @@ function htmlPage(): string {
|
|||||||
const overlay = mustGet("overlay");
|
const overlay = mustGet("overlay");
|
||||||
const overlayTitle = mustGet("overlay-title");
|
const overlayTitle = mustGet("overlay-title");
|
||||||
const overlayImage = mustGet("overlay-image");
|
const overlayImage = mustGet("overlay-image");
|
||||||
|
const overlayLoading = mustGet("overlay-loading");
|
||||||
|
const overlayLoadingText = mustGet("overlay-loading-text");
|
||||||
const closeOverlay = mustGet("close-overlay");
|
const closeOverlay = mustGet("close-overlay");
|
||||||
const photoList = mustGet("photo-list");
|
const photoList = mustGet("photo-list");
|
||||||
const emptyState = mustGet("empty-state");
|
const emptyState = mustGet("empty-state");
|
||||||
@@ -1104,6 +1155,7 @@ function htmlPage(): string {
|
|||||||
const importStatus = mustGet("share-status");
|
const importStatus = mustGet("share-status");
|
||||||
const importButton = mustGet("load-share");
|
const importButton = mustGet("load-share");
|
||||||
const stopImportButton = mustGet("cancel-share");
|
const stopImportButton = mustGet("cancel-share");
|
||||||
|
let overlayLoadToken = 0;
|
||||||
|
|
||||||
function mustGet(id) {
|
function mustGet(id) {
|
||||||
const element = document.getElementById(id);
|
const element = document.getElementById(id);
|
||||||
@@ -1568,11 +1620,34 @@ function htmlPage(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openOverlay(photo) {
|
function openOverlay(photo) {
|
||||||
|
const requestToken = ++overlayLoadToken;
|
||||||
overlay.classList.add("open");
|
overlay.classList.add("open");
|
||||||
overlay.setAttribute("aria-hidden", "false");
|
overlay.setAttribute("aria-hidden", "false");
|
||||||
overlayTitle.textContent = photo.name;
|
overlayTitle.textContent = photo.name;
|
||||||
overlayImage.src = photo.fullUrl;
|
|
||||||
overlayImage.alt = photo.name;
|
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) {
|
function createPhotoMarkerIcon(photo) {
|
||||||
@@ -1588,8 +1663,13 @@ function htmlPage(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeOverlayView() {
|
function closeOverlayView() {
|
||||||
|
overlayLoadToken += 1;
|
||||||
overlay.classList.remove("open");
|
overlay.classList.remove("open");
|
||||||
overlay.setAttribute("aria-hidden", "true");
|
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");
|
overlayImage.removeAttribute("src");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user