UI: Improve queue/history card collapsing and styling

This commit is contained in:
Simon Cordt
2025-09-26 18:09:11 +02:00
parent ee1de9f793
commit 2c16b807c0
4 changed files with 137 additions and 16 deletions
+1 -1
View File
@@ -132,7 +132,7 @@
.pqueue-thumb-placeholder--failed{color:var(--danger-300,#fb7185)}
.pqueue-thumb-placeholder--interrupted{color:var(--warning-300,#facc15)}
.pqueue-thumb-wrap-placeholder{cursor:default;border-style:dashed}
.pqueue-gallery-overlay{position:fixed;inset:0;display:none;align-items:center;justify-content:center;background:rgba(15,23,42,.75);backdrop-filter:blur(6px);z-index:100000}
.pqueue-gallery-overlay{position:fixed;inset:0;display:none;align-items:center;justify-content:center;background:rgba(26, 26, 26, 0.65);backdrop-filter:blur(10px);z-index:100000}
.pqueue-gallery{position:relative;max-width:92vw;max-height:92vh;display:flex;align-items:center;justify-content:center}
.pqueue-gallery__img{max-width:90vw;max-height:90vh;border-radius:12px;box-shadow:0 20px 45px rgba(0,0,0,.6)}
.pqueue-gallery__counter{position:absolute;left:50%;bottom:-32px;transform:translateX(-50%);color:#fff;font-size:.85rem}
+2
View File
@@ -54,6 +54,8 @@
container: null,
dom: {},
uiMetricsCollapsed: false,
uiQueueCollapsed: false,
uiHistoryCollapsed: false,
};
PQ.state = state;
+68 -8
View File
@@ -175,16 +175,76 @@
const base = (count != null) ? `${count} entries` : null;
const range = UI.currentHistoryRangeLabel();
const subtitle = [base, range].filter(Boolean).join(' • ');
const card = UI.card("History", {
icon: "ti ti-clock-bolt",
subtitle,
actions: UI.historyFilters(),
content: grid,
// Collapsible card structure
const bodyInner = grid;
const body = UI.el("div", { class: "pqueue-card__body" }, [bodyInner]);
try {
body.style.overflow = 'hidden';
body.style.transition = 'height 120ms ease, opacity 120ms ease';
} catch (err) { /* noop */ }
const titleWrap = UI.el("div", { class: "pqueue-card__title-wrap" });
titleWrap.appendChild(UI.icon("ti ti-clock-bolt", { size: "md" }));
titleWrap.appendChild(UI.el("h3", { class: "pqueue-card__title", text: "History" }));
if (subtitle) titleWrap.appendChild(UI.el("span", { class: "pqueue-card__subtitle", text: subtitle }));
const toggle = UI.button({ icon: state.uiHistoryCollapsed ? "ti ti-chevron-down" : "ti ti-chevron-up", variant: "ghost", subtle: true, title: state.uiHistoryCollapsed ? "Expand" : "Collapse" });
toggle.addEventListener("click", () => {
try {
const isCollapsed = !!state.uiHistoryCollapsed;
const setIcon = (name) => { try { toggle.innerHTML = ''; toggle.appendChild(UI.icon(name)); } catch (err) { /* noop */ } };
if (!isCollapsed) {
const start = body.scrollHeight;
body.style.height = `${start}px`;
body.style.opacity = '1';
requestAnimationFrame(() => {
body.style.height = '0px';
body.style.opacity = '0';
});
const onEnd = () => {
body.removeEventListener('transitionend', onEnd);
setIcon('ti ti-chevron-down');
state.uiHistoryCollapsed = true;
try { wrap.setAttribute('data-collapsed', 'true'); } catch (err) { /* noop */ }
};
body.addEventListener('transitionend', onEnd);
} else {
const target = bodyInner.scrollHeight;
body.style.height = '0px';
body.style.opacity = '0';
requestAnimationFrame(() => {
const h = bodyInner.scrollHeight || target;
body.style.height = `${h}px`;
body.style.opacity = '1';
});
const onEnd = () => {
body.removeEventListener('transitionend', onEnd);
try { body.style.height = ''; } catch (err) { /* noop */ }
setIcon('ti ti-chevron-up');
state.uiHistoryCollapsed = false;
try { wrap.setAttribute('data-collapsed', 'false'); } catch (err) { /* noop */ }
};
body.addEventListener('transitionend', onEnd);
}
} catch (err) { /* noop */ }
});
state.dom.historyCard = card;
state.dom.historySubtitle = card.querySelector('.pqueue-card__subtitle');
const actions = UI.el("div", { class: "pqueue-card__actions" }, [...UI.historyFilters(), toggle]);
const header = UI.el("header", { class: "pqueue-card__header" }, [titleWrap, actions]);
const wrap = UI.el("section", { class: ["pqueue-card"] }, [header]);
try { wrap.setAttribute('data-collapsed', state.uiHistoryCollapsed ? 'true' : 'false'); } catch (err) { /* noop */ }
if (state.uiHistoryCollapsed) {
try { body.style.height = '0px'; body.style.opacity = '0'; } catch (err) { /* noop */ }
} else {
try { body.style.height = ''; body.style.opacity = '1'; } catch (err) { /* noop */ }
}
wrap.appendChild(body);
state.dom.historyCard = wrap;
state.dom.historySubtitle = wrap.querySelector('.pqueue-card__subtitle');
try { UI.ensureThumbObserver(); } catch (err) { /* noop */ }
return card;
return wrap;
};
UI.ensureHistoryObserver = function ensureHistoryObserver() {
+66 -7
View File
@@ -190,14 +190,73 @@
UI.el("div", { class: "pqueue-table__footer-right" }, [deleteSelected]),
]);
const card = UI.card("Queue", {
icon: "ti ti-stack-front",
subtitle: state.metrics.queueCount ? `${state.metrics.queueCount} pending` : null,
actions: [filter],
content: [wrapper, footer],
// Build collapsible card (mirrors metrics behavior)
const bodyInner = UI.el("div", { class: "pqueue-pending" }, [wrapper, footer]);
const body = UI.el("div", { class: "pqueue-card__body" }, [bodyInner]);
try {
body.style.overflow = 'hidden';
body.style.transition = 'height 120ms ease, opacity 120ms ease';
} catch (err) { /* noop */ }
const titleWrap = UI.el("div", { class: "pqueue-card__title-wrap" });
titleWrap.appendChild(UI.icon("ti ti-stack-front", { size: "md" }));
titleWrap.appendChild(UI.el("h3", { class: "pqueue-card__title", text: "Queue" }));
const subtitleText = state.metrics.queueCount ? `${state.metrics.queueCount} pending` : null;
if (subtitleText) titleWrap.appendChild(UI.el("span", { class: "pqueue-card__subtitle", text: subtitleText }));
const toggle = UI.button({ icon: state.uiQueueCollapsed ? "ti ti-chevron-down" : "ti ti-chevron-up", variant: "ghost", subtle: true, title: state.uiQueueCollapsed ? "Expand" : "Collapse" });
toggle.addEventListener("click", () => {
try {
const isCollapsed = !!state.uiQueueCollapsed;
const setIcon = (name) => { try { toggle.innerHTML = ''; toggle.appendChild(UI.icon(name)); } catch (err) { /* noop */ } };
if (!isCollapsed) {
const start = body.scrollHeight;
body.style.height = `${start}px`;
body.style.opacity = '1';
requestAnimationFrame(() => {
body.style.height = '0px';
body.style.opacity = '0';
});
const onEnd = () => {
body.removeEventListener('transitionend', onEnd);
setIcon('ti ti-chevron-down');
state.uiQueueCollapsed = true;
try { wrap.setAttribute('data-collapsed', 'true'); } catch (err) { /* noop */ }
};
body.addEventListener('transitionend', onEnd);
} else {
const target = bodyInner.scrollHeight;
body.style.height = '0px';
body.style.opacity = '0';
requestAnimationFrame(() => {
const h = bodyInner.scrollHeight || target;
body.style.height = `${h}px`;
body.style.opacity = '1';
});
const onEnd = () => {
body.removeEventListener('transitionend', onEnd);
try { body.style.height = ''; } catch (err) { /* noop */ }
setIcon('ti ti-chevron-up');
state.uiQueueCollapsed = false;
try { wrap.setAttribute('data-collapsed', 'false'); } catch (err) { /* noop */ }
};
body.addEventListener('transitionend', onEnd);
}
} catch (err) { /* noop */ }
});
state.dom.pendingCard = card;
return card;
const actions = UI.el("div", { class: "pqueue-card__actions" }, [filter, toggle]);
const header = UI.el("header", { class: "pqueue-card__header" }, [titleWrap, actions]);
const wrap = UI.el("section", { class: ["pqueue-card"] }, [header]);
try { wrap.setAttribute('data-collapsed', state.uiQueueCollapsed ? 'true' : 'false'); } catch (err) { /* noop */ }
if (state.uiQueueCollapsed) {
try { body.style.height = '0px'; body.style.opacity = '0'; } catch (err) { /* noop */ }
} else {
try { body.style.height = ''; body.style.opacity = '1'; } catch (err) { /* noop */ }
}
wrap.appendChild(body);
state.dom.pendingCard = wrap;
return wrap;
};
UI.pendingRow = function pendingRow(item) {