Redesign Explorer controls — prominent Explore button, icons, polish
Explore button moved to its own row below playlist cards with gradient, search icon, and "Explore Selected Playlist" label. Impossible to miss. Mode toggle redesigned as pill segmented control with grid/list icons. Action bar buttons get inline SVG icons (checkmark, square, heart). Primary buttons use gradient + glow shadow treatment. Build hint shows "Select a playlist above, then explore" → updates to "Ready: [name]" when playlist selected. Discovery poller refreshes cards every 5s while active. Button re-enables as "Open" after modal launch so user can reopen closed discovery modal.
This commit is contained in:
parent
8b58434c17
commit
95b1665e19
3 changed files with 112 additions and 46 deletions
|
|
@ -3691,34 +3691,56 @@
|
|||
<h2 class="header-title"><img src="/static/explorer.png" class="page-header-icon" alt=""><span>Playlist Explorer</span></h2>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Playlist picker + controls inline -->
|
||||
<!-- Playlist picker + controls -->
|
||||
<div class="explorer-playlist-picker" id="explorer-playlist-picker">
|
||||
<div class="explorer-picker-top">
|
||||
<div class="explorer-picker-tabs" id="explorer-picker-tabs"></div>
|
||||
<div class="explorer-controls">
|
||||
<div class="explorer-mode-toggle">
|
||||
<button class="explorer-mode-btn active" data-mode="albums"
|
||||
onclick="explorerSetMode('albums')">Albums</button>
|
||||
onclick="explorerSetMode('albums')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Albums
|
||||
</button>
|
||||
<button class="explorer-mode-btn" data-mode="discographies"
|
||||
onclick="explorerSetMode('discographies')">Discographies</button>
|
||||
onclick="explorerSetMode('discographies')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
||||
Full Discog
|
||||
</button>
|
||||
</div>
|
||||
<button class="explorer-build-btn" id="explorer-build-btn"
|
||||
onclick="explorerBuildTree()">Explore</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="explorer-picker-scroll" id="explorer-picker-scroll">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
<div class="explorer-build-row">
|
||||
<div class="explorer-build-hint" id="explorer-build-hint">Select a playlist above, then explore</div>
|
||||
<button class="explorer-build-btn" id="explorer-build-btn"
|
||||
onclick="explorerBuildTree()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
Explore Selected Playlist
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action bar (sticky, appears after tree built) -->
|
||||
<div class="explorer-action-bar" id="explorer-action-bar" style="display: none;">
|
||||
<span class="explorer-selection-count" id="explorer-selection-count">0 albums selected</span>
|
||||
<div class="explorer-action-left">
|
||||
<span class="explorer-selection-count" id="explorer-selection-count">0 albums selected</span>
|
||||
</div>
|
||||
<div class="explorer-action-buttons">
|
||||
<button class="explorer-action-btn" onclick="explorerSelectAll()">Select All</button>
|
||||
<button class="explorer-action-btn" onclick="explorerDeselectAll()">Deselect</button>
|
||||
<button class="explorer-action-btn" onclick="explorerSelectAll()">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
Select All
|
||||
</button>
|
||||
<button class="explorer-action-btn" onclick="explorerDeselectAll()">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>
|
||||
Deselect
|
||||
</button>
|
||||
<button class="explorer-action-btn primary" onclick="explorerAddToWishlist()">
|
||||
Add to Wishlist</button>
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
|
||||
Add to Wishlist
|
||||
</button>
|
||||
</div>
|
||||
<span class="explorer-nav-hint">Scroll to zoom · Right-drag to pan · Double-click album for tracks</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -67137,6 +67137,11 @@ function explorerSelectPlaylist(id, el) {
|
|||
_explorer.playlistId = id;
|
||||
document.querySelectorAll('.explorer-picker-card').forEach(c => c.classList.remove('active'));
|
||||
if (el) el.classList.add('active');
|
||||
// Update hint text
|
||||
const hint = document.getElementById('explorer-build-hint');
|
||||
const pl = _explorer._playlists.find(p => p.id === id);
|
||||
if (hint && pl) hint.textContent = `Ready: ${pl.name}`;
|
||||
else if (hint) hint.textContent = '';
|
||||
}
|
||||
|
||||
function explorerRedirectToDiscover(playlistId) {
|
||||
|
|
|
|||
|
|
@ -50757,54 +50757,77 @@ tr.tag-diff-same {
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Mode toggle — segmented control */
|
||||
/* Mode toggle — pill segmented control */
|
||||
.explorer-mode-toggle {
|
||||
display: flex;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 3px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.explorer-mode-btn {
|
||||
padding: 10px 18px;
|
||||
font-size: 12px;
|
||||
padding: 7px 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: all 0.2s;
|
||||
border-radius: 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
.explorer-mode-btn svg { opacity: 0.5; transition: opacity 0.2s; }
|
||||
|
||||
.explorer-mode-btn.active {
|
||||
color: #fff;
|
||||
background: rgba(var(--accent-rgb), 0.25);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
background: rgba(var(--accent-rgb), 0.2);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.explorer-mode-btn.active svg { opacity: 1; }
|
||||
|
||||
.explorer-mode-btn:hover:not(.active) {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
/* Explore button row — prominent CTA below playlist cards */
|
||||
.explorer-build-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
padding: 14px 0 4px;
|
||||
}
|
||||
|
||||
.explorer-build-hint {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.25);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Explore button — primary CTA */
|
||||
.explorer-build-btn {
|
||||
padding: 10px 24px;
|
||||
padding: 11px 28px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
background: var(--accent);
|
||||
background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 80%, #fff));
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
letter-spacing: 0.3px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
box-shadow: 0 2px 12px rgba(var(--accent-rgb), 0.25);
|
||||
}
|
||||
|
||||
.explorer-build-btn::after {
|
||||
|
|
@ -50813,11 +50836,12 @@ tr.tag-diff-same {
|
|||
inset: 0;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, transparent 50%);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.explorer-build-btn:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 24px rgba(var(--accent-rgb), 0.35);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 28px rgba(var(--accent-rgb), 0.4);
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
|
|
@ -50830,21 +50854,22 @@ tr.tag-diff-same {
|
|||
opacity: 0.5;
|
||||
cursor: default;
|
||||
filter: saturate(0.5);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Action bar — sticky at top */
|
||||
/* Action bar — polished sticky toolbar */
|
||||
.explorer-action-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 12px 20px;
|
||||
background: rgba(18, 18, 22, 0.9);
|
||||
gap: 10px;
|
||||
padding: 10px 20px;
|
||||
background: linear-gradient(180deg, rgba(18, 18, 26, 0.95) 0%, rgba(18, 18, 22, 0.95) 100%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 12px;
|
||||
border-radius: 14px;
|
||||
margin-bottom: 16px;
|
||||
backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(16px);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
|
|
@ -50856,9 +50881,15 @@ tr.tag-diff-same {
|
|||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
|
||||
.explorer-action-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.explorer-nav-hint {
|
||||
font-size: 10px;
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
color: rgba(255, 255, 255, 0.18);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 2px;
|
||||
|
|
@ -50866,44 +50897,51 @@ tr.tag-diff-same {
|
|||
|
||||
.explorer-selection-count {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.explorer-action-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.explorer-action-btn {
|
||||
padding: 7px 16px;
|
||||
padding: 7px 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
letter-spacing: 0.2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.explorer-action-btn svg { opacity: 0.5; }
|
||||
|
||||
.explorer-action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
.explorer-action-btn:hover svg { opacity: 0.8; }
|
||||
|
||||
.explorer-action-btn.primary {
|
||||
background: var(--accent);
|
||||
background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 80%, #fff));
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
padding: 8px 22px;
|
||||
padding: 8px 20px;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(var(--accent-rgb), 0.2);
|
||||
}
|
||||
.explorer-action-btn.primary svg { opacity: 1; }
|
||||
|
||||
.explorer-action-btn.primary::after {
|
||||
content: '';
|
||||
|
|
@ -50911,10 +50949,11 @@ tr.tag-diff-same {
|
|||
inset: 0;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, transparent 50%);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.explorer-action-btn.primary:hover {
|
||||
box-shadow: 0 4px 18px rgba(var(--accent-rgb), 0.4);
|
||||
box-shadow: 0 6px 22px rgba(var(--accent-rgb), 0.4);
|
||||
filter: brightness(1.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue