fix(sidebar): hybrid Favorites style like Home with expandable items

- Use nav-item style with heart icon (like Home)
- Add chevron button next to label for expand/collapse
- Show indented sub-items (Artists, Albums, Tracks) when expanded
- Collapsed sidebar shows heart icon with popover menu
This commit is contained in:
vicrodh committed 2026-01-30 00:25:45 -06:00
1 parent 5bb27b919f
commit 71d5a4c53a
1 file changed
+57 -37
+57 -37
View File
@@ -975,7 +975,7 @@
} }
if (showFavoritesMenu) { if (showFavoritesMenu) {
const target = e.target as HTMLElement; const target = e.target as HTMLElement;
if (!target.closest('.favorites-popover') && !target.closest('.collapsed-section-btn')) { if (!target.closest('.favorites-popover') && !target.closest('.favorites-section .nav-item')) {
showFavoritesMenu = false; showFavoritesMenu = false;
} }
} }
@@ -1038,31 +1038,41 @@
</NavigationItem> </NavigationItem>
</nav> </nav>
<!-- Favorites Section (collapsible, matches Playlists style) --> <!-- Favorites Section (hybrid: Home style with expandable sub-items) -->
<div class="section favorites-section"> <nav class="nav-section favorites-section">
{#if isExpanded} {#if isExpanded}
<div class="favorites-header"> <!-- Main Favorites item with chevron -->
<button class="section-header-btn" onclick={() => handleViewChange('favorites')}> <div class="favorites-nav-wrapper">
<span class="section-header">{$t('nav.favorites').toUpperCase()}</span> <button
class="nav-item favorites-nav-item"
class:active={activeView === 'favorites' || activeView.startsWith('favorites-')}
onclick={() => handleViewChange('favorites')}
>
<div class="icon-container">
<Heart size={14} />
</div>
<span class="label">{$t('nav.favorites')}</span>
</button> </button>
<div class="header-actions"> <button
<button class="icon-btn" onclick={() => { favoritesExpanded = !favoritesExpanded; saveSidebarCollapseState(); }} title={favoritesExpanded ? $t('actions.close') : $t('actions.open')}> class="favorites-expand-btn"
{#if favoritesExpanded} onclick={() => { favoritesExpanded = !favoritesExpanded; saveSidebarCollapseState(); }}
<ChevronUp size={14} /> title={favoritesExpanded ? $t('actions.close') : $t('actions.open')}
{:else} >
<ChevronDown size={14} /> <span class="favorites-chevron" class:expanded={favoritesExpanded}>
{/if} <ChevronRight size={12} />
</span>
</button> </button>
</div> </div>
</div> <!-- Sub-items when expanded -->
{#if favoritesExpanded} {#if favoritesExpanded}
<div class="favorites-items"> <div class="favorites-subitems">
{#each favoritesTabOrder as tab} {#each favoritesTabOrder as tab}
<NavigationItem <NavigationItem
label={$t(`favorites.${tab}`)} label={$t(`favorites.${tab}`)}
active={activeView === `favorites-${tab}`} active={activeView === `favorites-${tab}`}
onclick={() => handleViewChange(`favorites-${tab}`)} onclick={() => handleViewChange(`favorites-${tab}`)}
showLabel={true} showLabel={true}
indented={true}
> >
{#snippet icon()} {#snippet icon()}
{#if tab === 'artists'} {#if tab === 'artists'}
@@ -1080,7 +1090,7 @@
{:else} {:else}
<!-- Collapsed sidebar: show heart with menu on click --> <!-- Collapsed sidebar: show heart with menu on click -->
<button <button
class="collapsed-section-btn" class="nav-item"
class:active={activeView.startsWith('favorites-')} class:active={activeView.startsWith('favorites-')}
onclick={(e) => { onclick={(e) => {
const rect = e.currentTarget.getBoundingClientRect(); const rect = e.currentTarget.getBoundingClientRect();
@@ -1089,10 +1099,12 @@
}} }}
title={$t('nav.favorites')} title={$t('nav.favorites')}
> >
<div class="icon-container">
<Heart size={14} /> <Heart size={14} />
</div>
</button> </button>
{/if} {/if}
</div> </nav>
<!-- Favorites menu popover (when sidebar collapsed) --> <!-- Favorites menu popover (when sidebar collapsed) -->
{#if showFavoritesMenu && !isExpanded} {#if showFavoritesMenu && !isExpanded}
@@ -2258,51 +2270,59 @@
text-align: center; text-align: center;
} }
/* Favorites Section (matches Playlists style) */ /* Favorites Section (hybrid: nav-item style with expandable children) */
.favorites-section { .favorites-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.favorites-header { .favorites-nav-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; gap: 0;
padding: 0 8px;
margin-bottom: 6px;
} }
.favorites-header .section-header-btn { .favorites-nav-item {
flex: 1; flex: 1;
justify-content: flex-start; min-width: 0;
} }
.favorites-items { .favorites-expand-btn {
display: flex;
flex-direction: column;
}
.collapsed-section-btn {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 32px; width: 24px;
height: 32px; height: 24px;
margin: 0 auto; margin-right: 4px;
background: none; background: none;
border: none; border: none;
border-radius: 6px; border-radius: 4px;
color: var(--text-muted);
cursor: pointer; cursor: pointer;
color: var(--text-muted);
transition: background-color 150ms ease, color 150ms ease; transition: background-color 150ms ease, color 150ms ease;
} }
.collapsed-section-btn:hover, .favorites-expand-btn:hover {
.collapsed-section-btn.active {
background: var(--bg-hover); background: var(--bg-hover);
color: var(--text-primary); color: var(--text-primary);
} }
.favorites-chevron {
display: flex;
align-items: center;
justify-content: center;
transition: transform 150ms ease;
}
.favorites-chevron.expanded {
transform: rotate(90deg);
}
.favorites-subitems {
display: flex;
flex-direction: column;
}
/* Favorites Popover (collapsed sidebar) */ /* Favorites Popover (collapsed sidebar) */
.favorites-popover { .favorites-popover {
position: fixed; position: fixed;