remove scroll shadows from fullscreen player

This commit is contained in:
jeffvli
2026-01-03 03:23:58 -08:00
parent 968d991a1a
commit 8c91f1c52d
4 changed files with 182 additions and 163 deletions
@@ -103,6 +103,7 @@ interface VirtualizedTableGridProps {
enableHeader: boolean; enableHeader: boolean;
enableHorizontalBorders: boolean; enableHorizontalBorders: boolean;
enableRowHoverHighlight: boolean; enableRowHoverHighlight: boolean;
enableScrollShadow: boolean;
enableSelection: boolean; enableSelection: boolean;
enableVerticalBorders: boolean; enableVerticalBorders: boolean;
getRowHeight: (index: number, cellProps: TableItemProps) => number; getRowHeight: (index: number, cellProps: TableItemProps) => number;
@@ -146,6 +147,7 @@ const VirtualizedTableGrid = ({
enableHeader, enableHeader,
enableHorizontalBorders, enableHorizontalBorders,
enableRowHoverHighlight, enableRowHoverHighlight,
enableScrollShadow,
enableSelection, enableSelection,
enableVerticalBorders, enableVerticalBorders,
getRowHeight, getRowHeight,
@@ -429,7 +431,7 @@ const VirtualizedTableGrid = ({
/> />
</div> </div>
)} )}
{enableHeader && showTopShadow && ( {enableHeader && enableScrollShadow && showTopShadow && (
<div className={styles.itemTableTopScrollShadow} /> <div className={styles.itemTableTopScrollShadow} />
)} )}
{!!pinnedLeftColumnCount && ( {!!pinnedLeftColumnCount && (
@@ -489,7 +491,7 @@ const VirtualizedTableGrid = ({
/> />
</div> </div>
)} )}
{enableHeader && showTopShadow && ( {enableHeader && enableScrollShadow && showTopShadow && (
<div className={styles.itemTableTopScrollShadow} /> <div className={styles.itemTableTopScrollShadow} />
)} )}
<div className={styles.itemTableGridContainer} ref={mergedRowRef}> <div className={styles.itemTableGridContainer} ref={mergedRowRef}>
@@ -507,10 +509,10 @@ const VirtualizedTableGrid = ({
return getRowHeight(index + pinnedRowCount, cellProps); return getRowHeight(index + pinnedRowCount, cellProps);
}} }}
/> />
{pinnedLeftColumnCount > 0 && showLeftShadow && ( {pinnedLeftColumnCount > 0 && enableScrollShadow && showLeftShadow && (
<div className={styles.itemTableLeftScrollShadow} /> <div className={styles.itemTableLeftScrollShadow} />
)} )}
{pinnedRightColumnCount > 0 && showRightShadow && ( {pinnedRightColumnCount > 0 && enableScrollShadow && showRightShadow && (
<div className={styles.itemTableRightScrollShadow} /> <div className={styles.itemTableRightScrollShadow} />
)} )}
</div> </div>
@@ -560,7 +562,7 @@ const VirtualizedTableGrid = ({
/> />
</div> </div>
)} )}
{enableHeader && showTopShadow && ( {enableHeader && enableScrollShadow && showTopShadow && (
<div className={styles.itemTableTopScrollShadow} /> <div className={styles.itemTableTopScrollShadow} />
)} )}
<div <div
@@ -607,6 +609,7 @@ const MemoizedVirtualizedTableGrid = memo(VirtualizedTableGrid, (prevProps, next
prevProps.enableHeader === nextProps.enableHeader && prevProps.enableHeader === nextProps.enableHeader &&
prevProps.enableHorizontalBorders === nextProps.enableHorizontalBorders && prevProps.enableHorizontalBorders === nextProps.enableHorizontalBorders &&
prevProps.enableRowHoverHighlight === nextProps.enableRowHoverHighlight && prevProps.enableRowHoverHighlight === nextProps.enableRowHoverHighlight &&
prevProps.enableScrollShadow === nextProps.enableScrollShadow &&
prevProps.enableSelection === nextProps.enableSelection && prevProps.enableSelection === nextProps.enableSelection &&
prevProps.enableVerticalBorders === nextProps.enableVerticalBorders && prevProps.enableVerticalBorders === nextProps.enableVerticalBorders &&
prevProps.getRowHeight === nextProps.getRowHeight && prevProps.getRowHeight === nextProps.getRowHeight &&
@@ -695,6 +698,7 @@ interface ItemTableListProps {
enableHeader?: boolean; enableHeader?: boolean;
enableHorizontalBorders?: boolean; enableHorizontalBorders?: boolean;
enableRowHoverHighlight?: boolean; enableRowHoverHighlight?: boolean;
enableScrollShadow?: boolean;
enableSelection?: boolean; enableSelection?: boolean;
enableSelectionDialog?: boolean; enableSelectionDialog?: boolean;
enableStickyGroupRows?: boolean; enableStickyGroupRows?: boolean;
@@ -737,6 +741,7 @@ const BaseItemTableList = ({
enableHeader = true, enableHeader = true,
enableHorizontalBorders = false, enableHorizontalBorders = false,
enableRowHoverHighlight = true, enableRowHoverHighlight = true,
enableScrollShadow = true,
enableSelection = true, enableSelection = true,
enableStickyGroupRows = false, enableStickyGroupRows = false,
enableStickyHeader = false, enableStickyHeader = false,
@@ -2386,6 +2391,7 @@ const BaseItemTableList = ({
enableHeader={enableHeader} enableHeader={enableHeader}
enableHorizontalBorders={enableHorizontalBorders} enableHorizontalBorders={enableHorizontalBorders}
enableRowHoverHighlight={enableRowHoverHighlight} enableRowHoverHighlight={enableRowHoverHighlight}
enableScrollShadow={enableScrollShadow}
enableSelection={enableSelection} enableSelection={enableSelection}
enableVerticalBorders={enableVerticalBorders} enableVerticalBorders={enableVerticalBorders}
getRowHeight={getRowHeight} getRowHeight={getRowHeight}
@@ -39,182 +39,190 @@ import { DragTarget } from '/@/shared/types/drag-and-drop';
import { ItemListKey, Play, PlayerQueueType } from '/@/shared/types/types'; import { ItemListKey, Play, PlayerQueueType } from '/@/shared/types/types';
type QueueProps = { type QueueProps = {
enableScrollShadow?: boolean;
listKey: ItemListKey; listKey: ItemListKey;
searchTerm: string | undefined; searchTerm: string | undefined;
}; };
export const PlayQueue = forwardRef<ItemListHandle, QueueProps>(({ listKey, searchTerm }, ref) => { export const PlayQueue = forwardRef<ItemListHandle, QueueProps>(
const { table } = useListSettings(listKey) || {}; ({ enableScrollShadow = true, listKey, searchTerm }, ref) => {
const { table } = useListSettings(listKey) || {};
const isFetching = useIsPlayerFetching(); const isFetching = useIsPlayerFetching();
const tableRef = useRef<ItemListHandle>(null); const tableRef = useRef<ItemListHandle>(null);
const mergedRef = useMergedRef(ref, tableRef); const mergedRef = useMergedRef(ref, tableRef);
const { getQueue } = usePlayerActions(); const { getQueue } = usePlayerActions();
const queueType = usePlayerQueueType(); const queueType = usePlayerQueueType();
const followCurrentSong = useFollowCurrentSong(); const followCurrentSong = useFollowCurrentSong();
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 200); const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 200);
const [data, setData] = useState<QueueSong[]>([]); const [data, setData] = useState<QueueSong[]>([]);
const [groups, setGroups] = useState<TableGroupHeader[]>([]); const [groups, setGroups] = useState<TableGroupHeader[]>([]);
useEffect(() => { useEffect(() => {
const setQueue = () => { const setQueue = () => {
const queue = getQueue() || { groups: [], items: [] }; const queue = getQueue() || { groups: [], items: [] };
setData(queue.items); setData(queue.items);
if (queueType === PlayerQueueType.PRIORITY && queue.groups && queue.groups.length > 0) { if (
const transformedGroups: TableGroupHeader[] = queue.groups.map((group) => ({ queueType === PlayerQueueType.PRIORITY &&
itemCount: group.count, queue.groups &&
render: (): ReactElement => { queue.groups.length > 0
return ( ) {
<div className={styles.groupRow}> const transformedGroups: TableGroupHeader[] = queue.groups.map((group) => ({
<Text itemCount: group.count,
fw={600} render: (): ReactElement => {
overflow="visible" return (
size="md" <div className={styles.groupRow}>
style={{ <Text
textWrap: 'nowrap', fw={600}
whiteSpace: 'nowrap', overflow="visible"
}} size="md"
> style={{
{group.name} textWrap: 'nowrap',
</Text> whiteSpace: 'nowrap',
</div> }}
); >
}, {group.name}
rowHeight: 40, </Text>
})); </div>
setGroups(transformedGroups); );
} else { },
setGroups([]); rowHeight: 40,
} }));
}; setGroups(transformedGroups);
} else {
const unsub = subscribePlayerQueue(() => { setGroups([]);
setQueue();
});
const unsubCurrentTrack = subscribeCurrentTrack((e) => {
if (followCurrentSong && e.index !== -1) {
tableRef.current?.scrollToIndex(e.index, {
align: 'center',
behavior: 'auto',
});
}
});
const handleAutoDJQueueAdded = () => {
if (followCurrentSong) {
const state = usePlayerStore.getState();
let index = state.player.index;
if (isShuffleEnabled(state)) {
index = mapShuffledToQueueIndex(index, state.queue.shuffled);
} }
};
if (index !== -1) { const unsub = subscribePlayerQueue(() => {
// Use setTimeout to ensure the DOM has updated with the new queue items setQueue();
setTimeout(() => { });
tableRef.current?.scrollToIndex(index, {
align: 'center', const unsubCurrentTrack = subscribeCurrentTrack((e) => {
behavior: 'auto', if (followCurrentSong && e.index !== -1) {
}); tableRef.current?.scrollToIndex(e.index, {
}, 0); align: 'center',
behavior: 'auto',
});
} }
} });
};
eventEmitter.on('AUTODJ_QUEUE_ADDED', handleAutoDJQueueAdded); const handleAutoDJQueueAdded = () => {
if (followCurrentSong) {
const state = usePlayerStore.getState();
let index = state.player.index;
setQueue(); if (isShuffleEnabled(state)) {
index = mapShuffledToQueueIndex(index, state.queue.shuffled);
return () => {
unsub();
unsubCurrentTrack();
eventEmitter.off('AUTODJ_QUEUE_ADDED', handleAutoDJQueueAdded);
};
}, [getQueue, queueType, tableRef, followCurrentSong]);
const filteredData: QueueSong[] = useMemo(() => {
if (debouncedSearchTerm) {
const searched = searchLibraryItems(data, debouncedSearchTerm, LibraryItem.SONG);
return searched;
}
return data;
}, [data, debouncedSearchTerm]);
const isEmpty = filteredData.length === 0;
const { handleColumnReordered } = useItemListColumnReorder({
itemListKey: listKey,
});
const { handleColumnResized } = useItemListColumnResize({
itemListKey: listKey,
});
const currentSong = usePlayerSong();
const currentSongUniqueId = currentSong?._uniqueId;
const { focused, ref: containerFocusRef } = useFocusWithin();
const player = usePlayer();
useHotkeys([
[
'delete',
() => {
if (focused) {
const selectedItems =
tableRef.current?.internalState.getSelected() as QueueSong[];
if (!selectedItems || selectedItems.length === 0) {
return;
} }
player.clearSelected(selectedItems); if (index !== -1) {
// Use setTimeout to ensure the DOM has updated with the new queue items
setTimeout(() => {
tableRef.current?.scrollToIndex(index, {
align: 'center',
behavior: 'auto',
});
}, 0);
}
} }
}, };
],
]);
return ( eventEmitter.on('AUTODJ_QUEUE_ADDED', handleAutoDJQueueAdded);
<div className={styles.container} ref={containerFocusRef}>
<LoadingOverlay pos="absolute" visible={isFetching} /> setQueue();
<ItemTableList
activeRowId={currentSongUniqueId} return () => {
autoFitColumns={table.autoFitColumns} unsub();
CellComponent={ItemTableListColumn} unsubCurrentTrack();
columns={table.columns} eventEmitter.off('AUTODJ_QUEUE_ADDED', handleAutoDJQueueAdded);
data={filteredData} };
enableAlternateRowColors={table.enableAlternateRowColors} }, [getQueue, queueType, tableRef, followCurrentSong]);
enableDrag
enableExpansion={false} const filteredData: QueueSong[] = useMemo(() => {
enableHeader if (debouncedSearchTerm) {
enableHorizontalBorders={table.enableHorizontalBorders} const searched = searchLibraryItems(data, debouncedSearchTerm, LibraryItem.SONG);
enableRowHoverHighlight={table.enableRowHoverHighlight} return searched;
enableSelection }
enableSelectionDialog={false}
enableVerticalBorders={table.enableVerticalBorders} return data;
getRowId="_uniqueId" }, [data, debouncedSearchTerm]);
groups={groups.length > 0 ? groups : undefined}
initialTop={{ const isEmpty = filteredData.length === 0;
to: 0,
type: 'offset', const { handleColumnReordered } = useItemListColumnReorder({
}} itemListKey: listKey,
itemType={LibraryItem.QUEUE_SONG} });
onColumnReordered={handleColumnReordered}
onColumnResized={handleColumnResized} const { handleColumnResized } = useItemListColumnResize({
ref={mergedRef} itemListKey: listKey,
size={table.size} });
/>
{isEmpty && <EmptyQueueDropZone />} const currentSong = usePlayerSong();
</div>
); const currentSongUniqueId = currentSong?._uniqueId;
});
const { focused, ref: containerFocusRef } = useFocusWithin();
const player = usePlayer();
useHotkeys([
[
'delete',
() => {
if (focused) {
const selectedItems =
tableRef.current?.internalState.getSelected() as QueueSong[];
if (!selectedItems || selectedItems.length === 0) {
return;
}
player.clearSelected(selectedItems);
}
},
],
]);
return (
<div className={styles.container} ref={containerFocusRef}>
<LoadingOverlay pos="absolute" visible={isFetching} />
<ItemTableList
activeRowId={currentSongUniqueId}
autoFitColumns={table.autoFitColumns}
CellComponent={ItemTableListColumn}
columns={table.columns}
data={filteredData}
enableAlternateRowColors={table.enableAlternateRowColors}
enableDrag
enableExpansion={false}
enableHeader
enableHorizontalBorders={table.enableHorizontalBorders}
enableRowHoverHighlight={table.enableRowHoverHighlight}
enableScrollShadow={enableScrollShadow}
enableSelection
enableSelectionDialog={false}
enableVerticalBorders={table.enableVerticalBorders}
getRowId="_uniqueId"
groups={groups.length > 0 ? groups : undefined}
initialTop={{
to: 0,
type: 'offset',
}}
itemType={LibraryItem.QUEUE_SONG}
onColumnReordered={handleColumnReordered}
onColumnResized={handleColumnResized}
ref={mergedRef}
size={table.size}
/>
{isEmpty && <EmptyQueueDropZone />}
</div>
);
},
);
const EmptyQueueDropZone = () => { const EmptyQueueDropZone = () => {
const playerContext = usePlayer(); const playerContext = usePlayer();
@@ -106,7 +106,11 @@ export const FullScreenPlayerQueue = () => {
</Group> </Group>
{activeTab === 'queue' ? ( {activeTab === 'queue' ? (
<div className={styles.queueContainer}> <div className={styles.queueContainer}>
<PlayQueue listKey={ItemListKey.FULL_SCREEN} searchTerm={undefined} /> <PlayQueue
enableScrollShadow={false}
listKey={ItemListKey.FULL_SCREEN}
searchTerm={undefined}
/>
</div> </div>
) : activeTab === 'related' ? ( ) : activeTab === 'related' ? (
<div className={styles.queueContainer}> <div className={styles.queueContainer}>
@@ -64,6 +64,7 @@ export const SimilarSongsList = ({ count, song }: SimilarSongsListProps) => {
enableHeader enableHeader
enableHorizontalBorders={fullScreenTable?.enableHorizontalBorders} enableHorizontalBorders={fullScreenTable?.enableHorizontalBorders}
enableRowHoverHighlight={fullScreenTable?.enableRowHoverHighlight} enableRowHoverHighlight={fullScreenTable?.enableRowHoverHighlight}
enableScrollShadow={false}
enableSelection enableSelection
enableSelectionDialog={false} enableSelectionDialog={false}
enableVerticalBorders={fullScreenTable?.enableVerticalBorders} enableVerticalBorders={fullScreenTable?.enableVerticalBorders}