From cc8e6cc8115c92a7faa8a9de8af9eac2b6efe05f Mon Sep 17 00:00:00 2001 From: Frank Stellmacher <171614930+Psychotoxical@users.noreply.github.com> Date: Fri, 22 May 2026 19:20:34 +0200 Subject: [PATCH] fix(playlist): column picker no longer clipped on short lists (#853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(playlist): columns dropdown no longer clipped on short lists (#839) The column picker rendered inside `.tracklist` (overflow-x: auto, which makes overflow-y compute to auto). On a 1-song playlist the downward popover overflowed the short box → clipped behind suggestions, an extra scrollbar, and the row vanishing when scrolling that inner bar (the virtualizer tracks the main viewport, not the tracklist). Move the picker outside `.tracklist` by reusing the shared TracklistColumnPicker (parametrized with allColumns); fixes the same latent bug in the favorites tracklist and dedupes three inline copies into one. * docs(changelog): playlist/favorites column picker fix (#853) --- CHANGELOG.md | 8 +++ src/components/AlbumTrackList.tsx | 1 + .../albumTrackList/TracklistColumnPicker.tsx | 7 ++- .../favorites/FavoritesSongsTracklist.tsx | 43 +++++---------- src/components/playlist/PlaylistTracklist.tsx | 52 +++++-------------- 5 files changed, 41 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce70c83..42b93086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -183,6 +183,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +### Playlists & Favorites — column picker on short lists + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#853](https://github.com/Psychotoxical/psysonic/pull/853)** + +* On a one-song playlist (or short favorites list) the column menu was clipped behind the list, added a stray scrollbar, and could hide the row when scrolled. The picker now sits outside the scroll area, so it opens fully on lists of any length. + + + ## [1.46.0] - 2026-05-18 > **🙏 Special thanks to [@zz5zz](https://github.com/zz5zz)** for his tireless quirk-spotting and bug reports on the [Psysonic Discord](https://discord.gg/AMnDRErm4u) — several of the polish fixes in this release landed directly off the back of his messages. diff --git a/src/components/AlbumTrackList.tsx b/src/components/AlbumTrackList.tsx index 0048c7e8..a116205e 100644 --- a/src/components/AlbumTrackList.tsx +++ b/src/components/AlbumTrackList.tsx @@ -119,6 +119,7 @@ export default function AlbumTrackList({ return ( <> ; pickerOpen: boolean; setPickerOpen: (updater: (v: boolean) => boolean) => void; @@ -20,6 +22,7 @@ interface Props { * button. */ export function TracklistColumnPicker({ + allColumns, pickerRef, pickerOpen, setPickerOpen, @@ -41,7 +44,7 @@ export function TracklistColumnPicker({ {pickerOpen && (
{t('albumDetail.columns')}
- {COLUMNS.filter(c => !c.required).map(c => { + {allColumns.filter(c => !c.required).map(c => { const label = c.i18nKey ? t(`albumDetail.${c.i18nKey as string}`) : c.key; const isOn = colVisible.has(c.key); return ( diff --git a/src/components/favorites/FavoritesSongsTracklist.tsx b/src/components/favorites/FavoritesSongsTracklist.tsx index cb015180..55f576eb 100644 --- a/src/components/favorites/FavoritesSongsTracklist.tsx +++ b/src/components/favorites/FavoritesSongsTracklist.tsx @@ -1,9 +1,9 @@ import React, { useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; import FavoriteSongRow, { type FavoriteSongRowCallbacks } from './FavoriteSongRow'; +import { TracklistColumnPicker } from '../albumTrackList/TracklistColumnPicker'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; -import { Check, ChevronDown, RotateCcw } from 'lucide-react'; import type { ColDef } from '../../utils/useTracklistColumns'; import type { SubsonicSong } from '../../api/subsonicTypes'; import { usePlayerStore } from '../../store/playerStore'; @@ -168,39 +168,21 @@ export default function FavoritesSongsTracklist({ const virtualItems = rowVirtualizer.getVirtualItems(); return ( + <> +
{ if (inSelectMode && e.target === e.currentTarget) useSelectionStore.getState().clearAll(); }}> - {/* Column visibility picker */} -
-
- - {pickerOpen && ( -
-
{t('albumDetail.columns')}
- {allColumns.filter(c => !c.required).map(c => { - const label = c.i18nKey ? t(`albumDetail.${c.i18nKey}`) : c.key; - const isOn = colVisible.has(c.key); - return ( - - ); - })} -
- -
- )} -
-
-
{visibleCols.map((colDef, colIndex) => { @@ -321,5 +303,6 @@ export default function FavoritesSongsTracklist({
)}
+ ); } diff --git a/src/components/playlist/PlaylistTracklist.tsx b/src/components/playlist/PlaylistTracklist.tsx index c544a11c..be089f90 100644 --- a/src/components/playlist/PlaylistTracklist.tsx +++ b/src/components/playlist/PlaylistTracklist.tsx @@ -1,12 +1,13 @@ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; import PlaylistRow, { type PlaylistRowCallbacks } from './PlaylistRow'; +import { TracklistColumnPicker } from '../albumTrackList/TracklistColumnPicker'; import { useTranslation } from 'react-i18next'; import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll'; import { useElementClientHeightById } from '../../hooks/useResizeClientHeight'; import { useNavigate } from 'react-router-dom'; import { - Check, ChevronDown, ListPlus, RotateCcw, Search, Trash2, X, + ListPlus, Search, Trash2, X, } from 'lucide-react'; import type { ColDef } from '../../utils/useTracklistColumns'; import type { SubsonicSong } from '../../api/subsonicTypes'; @@ -235,6 +236,17 @@ export default function PlaylistTracklist({ } return ( + <> +
{/* Bulk action bar */} @@ -277,43 +289,6 @@ export default function PlaylistTracklist({
)} - {/* Column visibility picker */} -
-
- - {pickerOpen && ( -
-
{t('albumDetail.columns')}
- {allColumns.filter(c => !c.required).map(c => { - const label = c.i18nKey ? t(`albumDetail.${c.i18nKey}`) : c.key; - const isOn = colVisible.has(c.key); - return ( - - ); - })} -
- -
- )} -
-
- {/* Header */}
@@ -481,5 +456,6 @@ export default function PlaylistTracklist({
+ ); }