Modal preview: click Preview opens fullscreen video player
Previously: clicking Preview in jobs list showed a small inline video within the job card row. Now: clicking Preview opens a centered fullscreen modal with: - Large video player (up to 95vw × 85vh) — same experience as bottom live-preview but accessible from jobs list - Auto-play, controls, native HTML5 video player - Title shown below video for context - Download button + Close button - Click outside or ESC key to close - Backdrop blur for focus Removes the obsolete inline <video> element that was rendered hidden in each job card. Body scroll locked while modal open.
This commit is contained in:
parent
4efd726176
commit
389c26d012
@ -180,6 +180,89 @@
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.progress-bar.smooth { transition: width 0.4s ease; }
|
||||
|
||||
/* ─── Video preview modal ─── */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
backdrop-filter: blur(4px);
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
.modal-content {
|
||||
position: relative;
|
||||
max-width: 95vw;
|
||||
max-height: 95vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.modal-content video {
|
||||
max-width: 100%;
|
||||
max-height: 85vh;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
|
||||
background: black;
|
||||
}
|
||||
.modal-title {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
transition: transform 0.15s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
.modal-close:hover { transform: scale(1.1); background: var(--accent-2); }
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-actions button {
|
||||
padding: 10px 18px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--panel);
|
||||
color: var(--text);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.modal-actions button.primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
.modal-actions button:hover { background: var(--panel-2); }
|
||||
.modal-actions button.primary:hover { background: var(--accent-2); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -687,7 +770,7 @@
|
||||
const actions = [];
|
||||
if (job.status === "done") {
|
||||
actions.push(`<button class="small" onclick="window.open('/api/download/${job.id}')">⬇ Download</button>`);
|
||||
actions.push(`<button class="small ghost" onclick="previewJob('${job.id}')">▶ Preview</button>`);
|
||||
actions.push(`<button class="small ghost" onclick="previewJob('${job.id}', ${JSON.stringify(title)})">▶ Preview</button>`);
|
||||
}
|
||||
actions.push(`<button class="small ghost" onclick="deleteJob('${job.id}')">✕</button>`);
|
||||
|
||||
@ -706,7 +789,6 @@
|
||||
${job.lang ? `<span>${job.lang}</span>` : ""}
|
||||
</div>
|
||||
<div class="actions">${actions.join("")}</div>
|
||||
${job.status === "done" ? `<video id="video-${job.id}" class="hidden" controls></video>` : ""}
|
||||
`;
|
||||
return el;
|
||||
}
|
||||
@ -717,11 +799,55 @@
|
||||
refreshJobs();
|
||||
}
|
||||
|
||||
function previewJob(id) {
|
||||
const v = document.getElementById(`video-${id}`);
|
||||
v.src = `/api/preview/${id}`;
|
||||
v.classList.remove("hidden");
|
||||
v.play();
|
||||
function previewJob(id, title) {
|
||||
// Odpre velik modal z reel videom
|
||||
const overlay = document.createElement("div");
|
||||
overlay.className = "modal-overlay";
|
||||
overlay.innerHTML = `
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<button class="modal-close" title="Zapri (ESC)">×</button>
|
||||
<video src="/api/preview/${id}" controls autoplay playsinline></video>
|
||||
${title ? `<div class="modal-title">${title}</div>` : ""}
|
||||
<div class="modal-actions">
|
||||
<button class="primary" onclick="window.open('/api/download/${id}')">⬇ Prenesi reel</button>
|
||||
<button onclick="closeModal()">Zapri</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const close = () => closeModal();
|
||||
overlay.addEventListener("click", close);
|
||||
overlay.querySelector(".modal-close").addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
close();
|
||||
});
|
||||
|
||||
// ESC key to close
|
||||
const escHandler = (e) => {
|
||||
if (e.key === "Escape") {
|
||||
close();
|
||||
document.removeEventListener("keydown", escHandler);
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", escHandler);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
document.body.style.overflow = "hidden"; // prevent scroll behind modal
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
const overlay = document.querySelector(".modal-overlay");
|
||||
if (overlay) {
|
||||
// Stop video before removing (prevents memory leak)
|
||||
const video = overlay.querySelector("video");
|
||||
if (video) {
|
||||
video.pause();
|
||||
video.removeAttribute("src");
|
||||
video.load();
|
||||
}
|
||||
overlay.remove();
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
}
|
||||
|
||||
refreshJobs();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user