From ce2317722870f932792dc6456a63235ff4611466 Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Sun, 8 Mar 2026 09:35:59 -0400 Subject: [PATCH] feat(quick-18): replace track-info with multi-column grid layout in playlist-details - Remove track-info component, add formatMilliseconds import - Add 5-column grid (#, Title, Artist, Album, Duration) with header row - Display 1-indexed playlist order numbers in # column - Phantom tracks span full grid width with grid-column: 1 / -1 - Add column-specific CSS: tabular-nums, text truncation, alignment --- .../playlist-details/playlist-details.ts | 75 +++++++++++++++---- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/playlist-details/playlist-details.ts b/frontend/src/components/playlist-details/playlist-details.ts index 5ba3564..a56769c 100644 --- a/frontend/src/components/playlist-details/playlist-details.ts +++ b/frontend/src/components/playlist-details/playlist-details.ts @@ -43,7 +43,6 @@ import { removeDragImage, } from '@utils/drag-image'; import { libraryStore } from '@store/library-store'; -import '@components/track-info/track-info'; import '@components/playlist-picker/playlist-picker.js'; import '@components/track-details/track-details.js'; import type { TrackDetails } from '@components/track-details/track-details.js'; @@ -52,6 +51,7 @@ import '@components/phantom-resolver/phantom-resolver.js'; import type { PhantomResolver } from '@components/phantom-resolver/phantom-resolver.js'; import '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js'; import type { DuplicateTracksDialog } from '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js'; +import { formatMilliseconds } from '@utils/time'; import { designTokens } from '../../styles/tokens.css'; @customElement('playlist-details') @@ -925,8 +925,55 @@ export class PlaylistDetails color: var(--yj-accent, #ffd43b); } + /* Column grid layout */ + .track-header, .track-item { - padding: 6px 0; + display: grid; + grid-template-columns: 40px 1fr 1fr 1fr 80px; + align-items: center; + gap: 0; + } + + .track-header { + padding: 6px 8px; + font-size: 11px; + font-weight: 600; + color: var(--yj-text-secondary, #b3b3b3); + text-transform: uppercase; + letter-spacing: 0.03em; + border-bottom: 1px solid var(--yj-text-tertiary, #666); + user-select: none; + } + + .header-cell, + .cell { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; + padding: 0 4px; + } + + .col-number { + text-align: center; + color: var(--yj-text-tertiary, #888); + font-variant-numeric: tabular-nums; + } + + .col-duration { + text-align: right; + color: var(--yj-text-tertiary, #888); + font-variant-numeric: tabular-nums; + } + + /* Phantom rows span full grid */ + .track-item.phantom { + display: grid; + grid-template-columns: 40px 1fr 1fr 1fr 80px; + } + + .track-item { + padding: 6px 8px; border-bottom: 1px solid rgba(255, 255, 255, 0.03); cursor: default; @@ -969,6 +1016,7 @@ export class PlaylistDetails } .phantom-row { + grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; @@ -1030,10 +1078,6 @@ export class PlaylistDetails background: rgba(224, 49, 49, 0.12); } - .track-item:last-child { - border-bottom: none; - } - .tracks-empty { padding: 12px 0; color: var(--yj-text-tertiary, #666); @@ -1158,6 +1202,13 @@ export class PlaylistDetails Play All +
+
#
+
Title
+
Artist
+
Album
+
Duration
+
${visibleTracks.map( ({ track, trackIndex }) => { const isPhantom = track.Phantom; @@ -1276,13 +1327,11 @@ export class PlaylistDetails ` - : html``} + : html`${trackIndex + 1} + ${track.Title || track.FilePath} + ${track.Artist} + ${track.Album} + ${formatMilliseconds(track.Duration)}`} `; },