refactor(album-track-list): I.2 — split AlbumTrackList.tsx 662 → 187 LOC across 7 files (#672)

* refactor(album-track-list): extract helpers + types

Pull formatDuration / codecLabel, the COLUMNS / CENTERED_COLS / SORTABLE_COLS
tables, ColKey / SortKey types, and the isSortable type guard into
utils/albumTrackListHelpers.ts. SortKey is re-exported from
AlbumTrackList.tsx so existing imports stay valid.

AlbumTrackList.tsx: 662 → 633 LOC.

* refactor(album-track-list): extract TrackRow subcomponent

Move the memoised tracklist row (~220 LOC including renderCell switch and
mouse handlers) into components/albumTrackList/TrackRow.tsx. It still
subscribes to its own selection + preview state via primitive selectors,
so per-row re-render scope is unchanged.

AlbumTrackList.tsx: 633 → 404 LOC.

* refactor(album-track-list): extract AlbumTrackListMobile subcomponent

Move the narrow-viewport branch (compact tracklist with disc separators
and no column grid) into components/albumTrackList/AlbumTrackListMobile.tsx.

AlbumTrackList.tsx: 404 → 376 LOC.

* refactor(album-track-list): extract TracklistColumnPicker subcomponent

The column visibility dropdown lives outside .tracklist to avoid the
overflow box clipping its menu — pull the wrapper + button + popover into
components/albumTrackList/TracklistColumnPicker.tsx.

AlbumTrackList.tsx: 376 → 347 LOC.

* refactor(album-track-list): extract TracklistHeaderRow subcomponent

The fixed header (sortable + resizable per-column with the bulk-select
toggle on the num cell) moves into
components/albumTrackList/TracklistHeaderRow.tsx, taking 85+ LOC of cell
rendering with it.

AlbumTrackList.tsx: 347 → 254 LOC.

* refactor(album-track-list): extract useAlbumTrackListSelection hook

Pull bulk-selection state (selectedIds-size subscription, shift-range
toggle, click-outside-clear, song-list-change clear) and the drag-start
dispatcher (single vs multi-song drag) into
hooks/useAlbumTrackListSelection.ts.

AlbumTrackList.tsx: 254 → 187 LOC.
This commit is contained in:
Frank Stellmacher
2026-05-14 00:01:36 +02:00
committed by GitHub
parent b591a1cb5f
commit f14c8f21e6
7 changed files with 726 additions and 522 deletions
+39
View File
@@ -0,0 +1,39 @@
import type { ColDef } from './useTracklistColumns';
export function formatDuration(seconds: number): string {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
return `${m}:${s.toString().padStart(2, '0')}`;
}
export function codecLabel(song: { suffix?: string; bitRate?: number }, showBitrate: boolean): string {
const parts: string[] = [];
if (song.suffix) parts.push(song.suffix.toUpperCase());
if (showBitrate && song.bitRate) parts.push(`${song.bitRate} kbps`);
return parts.join(' · ');
}
export const COLUMNS: readonly ColDef[] = [
{ key: 'num', i18nKey: null, minWidth: 60, defaultWidth: 60, required: true },
{ key: 'title', i18nKey: 'trackTitle', minWidth: 150, defaultWidth: 0, required: true, flex: true },
{ key: 'artist', i18nKey: 'trackArtist', minWidth: 80, defaultWidth: 180, required: false },
{ key: 'favorite', i18nKey: 'trackFavorite', minWidth: 50, defaultWidth: 70, required: false },
{ key: 'rating', i18nKey: 'trackRating', minWidth: 80, defaultWidth: 120, required: false },
{ key: 'duration', i18nKey: 'trackDuration', minWidth: 72, defaultWidth: 92, required: false },
{ key: 'format', i18nKey: 'trackFormat', minWidth: 60, defaultWidth: 90, required: false },
{ key: 'genre', i18nKey: 'trackGenre', minWidth: 60, defaultWidth: 90, required: false },
];
export type ColKey = 'num' | 'title' | 'artist' | 'favorite' | 'rating' | 'duration' | 'format' | 'genre';
export const CENTERED_COLS = new Set<ColKey>(['favorite', 'rating', 'duration']);
export type SortKey = 'natural' | 'title' | 'artist' | 'album' | 'favorite' | 'rating' | 'duration';
export const SORTABLE_COLS = new Set<ColKey | 'album'>(['title', 'artist', 'album', 'favorite', 'rating', 'duration']);
export function isSortable(key: ColKey | string): key is SortKey {
return SORTABLE_COLS.has(key as ColKey);
}