feat: VOD bulk-action bar — slide-in animation + style extracted from inline

The bulk-action bar (the purple-tinted row that appears between the
VOD filter and the grid when 1+ VOD checkbox is ticked) was 100%
inline-styled in HTML, which meant:
- No animation when it appears — it just popped into existence
- No reusable styling for similar action surfaces later
- Layout debugging meant editing HTML, not CSS

Extracted to a proper .vod-bulk-bar class, plus .vod-bulk-count for
the "N selected" label and a .vod-bulk-spacer for the flex push.
The CSS rule also picks up a 4px soft purple shadow + a slightly
richer gradient background that matches the rest of the purple
surfaces in the app.

Animation: vod-bulk-bar-slide @keyframes fires every time the JS
flips display:none -> display:flex, because @keyframes restart on
each display change. 220ms, cubic-bezier(0.16, 1, 0.3, 1) for a
quick spring landing, 10px translateY-from + opacity 0->1. The
appear feels intentional now instead of jarring.

Disappear (display:flex -> none) still snaps because CSS cannot
transition through display:none — adding that would require a
class-toggle refactor and an explicit timer to defer the actual
removal. Not worth the complexity for the polish-grade improvement
this is going for.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 01:41:08 +02:00
parent fdeb1697de
commit 9115819bb0
2 changed files with 41 additions and 3 deletions

View File

@ -301,9 +301,9 @@
<span id="vodHideDownloadedText">Hide downloaded</span> <span id="vodHideDownloadedText">Hide downloaded</span>
</label> </label>
</div> </div>
<div id="vodBulkBar" class="vod-bulk-bar" style="display:none; align-items:center; gap:10px; padding:8px 12px; background: rgba(145, 70, 255, 0.12); border:1px solid rgba(145, 70, 255, 0.4); border-radius:6px; margin-bottom:12px; flex-wrap:wrap;"> <div id="vodBulkBar" class="vod-bulk-bar" style="display:none;">
<span id="vodBulkCount" style="color: var(--text); font-size:13px; font-weight:600;">0 selected</span> <span id="vodBulkCount" class="vod-bulk-count">0 selected</span>
<span style="flex:1;"></span> <span class="vod-bulk-spacer"></span>
<button id="vodBulkAddBtn" class="btn-pill primary" type="button" onclick="bulkAddSelectedVodsToQueue()">+ Queue</button> <button id="vodBulkAddBtn" class="btn-pill primary" type="button" onclick="bulkAddSelectedVodsToQueue()">+ Queue</button>
<button id="vodBulkMarkBtn" class="btn-pill" type="button" onclick="bulkMarkSelectedDownloaded(true)">Mark as downloaded</button> <button id="vodBulkMarkBtn" class="btn-pill" type="button" onclick="bulkMarkSelectedDownloaded(true)">Mark as downloaded</button>
<button id="vodBulkUnmarkBtn" class="btn-pill" type="button" onclick="bulkMarkSelectedDownloaded(false)">Unmark</button> <button id="vodBulkUnmarkBtn" class="btn-pill" type="button" onclick="bulkMarkSelectedDownloaded(false)">Unmark</button>

View File

@ -150,6 +150,44 @@ body {
font-weight: 600; font-weight: 600;
} }
/* ============================================
VOD BULK-ACTION BAR slides in when 1+ VOD is selected
============================================
Lives between the filter row and the VOD grid. Used to be all
inline-styled in HTML; extracted to a class so the slide-in
animation has somewhere to live and the styling stays consistent
with the rest of the action surfaces. */
.vod-bulk-bar {
align-items: center;
gap: 10px;
padding: 10px 14px;
background: linear-gradient(135deg, rgba(145, 70, 255, 0.15) 0%, rgba(145, 70, 255, 0.08) 100%);
border: 1px solid rgba(145, 70, 255, 0.42);
border-radius: 8px;
margin-bottom: 12px;
flex-wrap: wrap;
box-shadow: 0 4px 16px rgba(145, 70, 255, 0.10);
/* Animation fires whenever the JS flips display:none -> display:flex,
because Animation events restart on each display change. */
animation: vod-bulk-bar-slide 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes vod-bulk-bar-slide {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.vod-bulk-count {
color: var(--text);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.2px;
}
.vod-bulk-spacer {
flex: 1;
}
.streamer-item .remove { .streamer-item .remove {
margin-left: auto; margin-left: auto;
opacity: 0; opacity: 0;