feat: sidebar for media now shows thumbnails
This commit is contained in:
@@ -1636,6 +1636,36 @@ button {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.media-thumbnail.has-image {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media-thumbnail-fallback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.media-thumbnail-image {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.media-thumbnail.is-loaded .media-thumbnail-image {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.media-thumbnail.is-loaded .media-thumbnail-fallback {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.media-item-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
@@ -439,6 +439,7 @@ function renderSidebarMediaItem(item, view) {
|
||||
const itemRoute = item.route || view.editor_route;
|
||||
const tabId = tabIdForItem(item, itemRoute);
|
||||
const active = tabRef && tabRef.type === itemRoute && tabRef.id === tabId;
|
||||
const thumbnail = renderMediaThumbnail(item);
|
||||
|
||||
return `
|
||||
<button
|
||||
@@ -449,7 +450,7 @@ function renderSidebarMediaItem(item, view) {
|
||||
type="button"
|
||||
title="${escapeHtmlAttribute(item.title || "") }"
|
||||
>
|
||||
<span class="media-thumbnail">${escapeHtml(mediaThumbnailGlyph(item.mime_type))}</span>
|
||||
${thumbnail}
|
||||
<span class="media-item-info">
|
||||
<span class="media-item-name">${escapeHtml(item.title || "")}</span>
|
||||
<span class="media-item-size">${escapeHtml(item.meta || "")}</span>
|
||||
@@ -458,6 +459,28 @@ function renderSidebarMediaItem(item, view) {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderMediaThumbnail(item) {
|
||||
const fallback = escapeHtml(mediaThumbnailGlyph(item.mime_type));
|
||||
|
||||
if (!String(item.mime_type || "").startsWith("image/")) {
|
||||
return `<span class="media-thumbnail"><span class="media-thumbnail-fallback">${fallback}</span></span>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<span class="media-thumbnail has-image" data-media-thumbnail-state="pending">
|
||||
<span class="media-thumbnail-fallback">${fallback}</span>
|
||||
<img
|
||||
class="media-thumbnail-image"
|
||||
data-media-thumbnail-image
|
||||
src="${escapeHtmlAttribute(mediaThumbnailUrl(item.id))}"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderSidebarEntityList(data, view) {
|
||||
const items = Array.isArray(data.items) ? data.items : [];
|
||||
|
||||
@@ -1258,6 +1281,25 @@ function bindEvents() {
|
||||
};
|
||||
});
|
||||
|
||||
root.querySelectorAll("[data-media-thumbnail-image]").forEach((image) => {
|
||||
const container = image.closest(".media-thumbnail");
|
||||
|
||||
image.onload = () => {
|
||||
container?.classList.add("is-loaded");
|
||||
container?.classList.remove("is-error");
|
||||
};
|
||||
|
||||
image.onerror = () => {
|
||||
container?.classList.add("is-error");
|
||||
container?.classList.remove("is-loaded");
|
||||
};
|
||||
|
||||
if (image.complete && image.naturalWidth > 0) {
|
||||
container?.classList.add("is-loaded");
|
||||
container?.classList.remove("is-error");
|
||||
}
|
||||
});
|
||||
|
||||
root.querySelectorAll("[data-open-tab]").forEach((button) => {
|
||||
button.onclick = () => {
|
||||
openTab(button.dataset.openRoute, button.dataset.openTab, button.dataset.openTitle, true);
|
||||
@@ -2420,6 +2462,10 @@ function mediaThumbnailGlyph(mimeType) {
|
||||
return "📄";
|
||||
}
|
||||
|
||||
function mediaThumbnailUrl(mediaId) {
|
||||
return `/api/media-thumbnail/${encodeURIComponent(mediaId)}`;
|
||||
}
|
||||
|
||||
function buildDashboardTagCloudItems(items) {
|
||||
if (!Array.isArray(items) || !items.length) {
|
||||
return [];
|
||||
|
||||
Reference in New Issue
Block a user