diff --git a/src/renderer/components/virtual-grid/grid-card/default-card.tsx b/src/renderer/components/virtual-grid/grid-card/default-card.tsx index abec5fc9..35a5faca 100644 --- a/src/renderer/components/virtual-grid/grid-card/default-card.tsx +++ b/src/renderer/components/virtual-grid/grid-card/default-card.tsx @@ -1,4 +1,4 @@ -import { generatePath, Link } from 'react-router-dom'; +import { generatePath, useNavigate } from 'react-router-dom'; import { ListChildComponentProps } from 'react-window'; import styled from 'styled-components'; import { Album, AlbumArtist, Artist, LibraryItem } from '/@/renderer/api/types'; @@ -31,6 +31,7 @@ const DefaultCardContainer = styled.div<{ $isHidden?: boolean }>` overflow: hidden; background: var(--card-default-bg); border-radius: var(--card-default-radius); + cursor: pointer; opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)}; pointer-events: auto; @@ -106,42 +107,44 @@ export const DefaultCard = ({ controls, isHidden, }: BaseGridCardProps) => { + const navigate = useNavigate(); + if (data) { + const path = generatePath( + controls.route.route, + controls.route.slugs?.reduce((acc, slug) => { + return { + ...acc, + [slug.slugProperty]: data[slug.idProperty], + }; + }, {}), + ); + return ( - - { - return { - ...acc, - [slug.slugProperty]: data[slug.idProperty], - }; - }, {}), - )} - > - - - - - - - - - - + navigate(path)} + > + + + + + + + + + ); } diff --git a/src/renderer/components/virtual-grid/grid-card/poster-card.tsx b/src/renderer/components/virtual-grid/grid-card/poster-card.tsx index d7b5e68d..3fe045b9 100644 --- a/src/renderer/components/virtual-grid/grid-card/poster-card.tsx +++ b/src/renderer/components/virtual-grid/grid-card/poster-card.tsx @@ -1,4 +1,5 @@ -import { generatePath, Link } from 'react-router-dom'; +import { Stack } from '@mantine/core'; +import { generatePath, useNavigate } from 'react-router-dom'; import { ListChildComponentProps } from 'react-window'; import styled from 'styled-components'; import { Album, AlbumArtist, Artist, LibraryItem } from '/@/renderer/api/types'; @@ -27,7 +28,7 @@ const PosterCardContainer = styled.div<{ $isHidden?: boolean }>` flex-direction: column; width: 100%; height: 100%; - margin: 0.5rem; + margin: 1rem; overflow: hidden; opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)}; pointer-events: auto; @@ -35,16 +36,10 @@ const PosterCardContainer = styled.div<{ $isHidden?: boolean }>` .card-controls { opacity: 0; } +`; - &:hover .card-controls { - opacity: 1; - } - - &:hover * { - &::before { - opacity: 0.5; - } - } +const LinkContainer = styled.div` + cursor: pointer; `; const ImageContainer = styled.div` @@ -70,6 +65,16 @@ const ImageContainer = styled.div` content: ''; user-select: none; } + + &:hover { + &::before { + opacity: 0.5; + } + } + + &:hover .card-controls { + opacity: 1; + } `; const Image = styled.img` @@ -91,21 +96,22 @@ export const PosterCard = ({ controls, isHidden, }: BaseGridCardProps) => { + const navigate = useNavigate(); + if (data) { + const path = generatePath( + controls.route.route, + controls.route.slugs?.reduce((acc, slug) => { + return { + ...acc, + [slug.slugProperty]: data[slug.idProperty], + }; + }, {}), + ); + return ( - { - return { - ...acc, - [slug.slugProperty]: data[slug.idProperty], - }; - }, {}), - )} - > + navigate(path)}> - + - - - - + + + + + + {controls.cardRows.map((row) => ( + + ))} + + ); }; diff --git a/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx b/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx index d9c2d430..fae1632e 100644 --- a/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx +++ b/src/renderer/components/virtual-grid/virtual-infinite-grid.tsx @@ -56,20 +56,17 @@ export const VirtualInfiniteGrid = forwardRef( const listRef = useRef(null); const loader = useRef(null); - const sz = itemSize / 2; - const { itemHeight, rowCount, columnCount } = useMemo(() => { - const itemsPerRow = Math.floor(Number(width) / sz!) - 1; - const widthPerItem = Number(width) / itemsPerRow - 10; + const itemsPerRow = itemSize; + const widthPerItem = Number(width) / itemsPerRow; const itemHeight = widthPerItem + cardRows.length * 26; return { columnCount: itemsPerRow, itemHeight, - itemWidth: sz, rowCount: Math.ceil(itemCount / itemsPerRow), }; - }, [cardRows.length, itemCount, sz, width]); + }, [cardRows.length, itemCount, itemSize, width]); const isItemLoaded = useCallback( (index: number) => { diff --git a/src/renderer/features/albums/components/album-list-content.tsx b/src/renderer/features/albums/components/album-list-content.tsx index 539ce262..d89889fb 100644 --- a/src/renderer/features/albums/components/album-list-content.tsx +++ b/src/renderer/features/albums/components/album-list-content.tsx @@ -318,7 +318,7 @@ export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListCont itemCount={itemCount || 0} itemData={itemData} itemGap={20} - itemSize={150 + (grid?.size || 0)} + itemSize={grid?.itemsPerRow || 5} itemType={LibraryItem.ALBUM} loading={itemCount === undefined || itemCount === null} minimumBatchSize={40} diff --git a/src/renderer/features/albums/components/album-list-header-filters.tsx b/src/renderer/features/albums/components/album-list-header-filters.tsx index 64ea672d..d9b47f84 100644 --- a/src/renderer/features/albums/components/album-list-header-filters.tsx +++ b/src/renderer/features/albums/components/album-list-header-filters.tsx @@ -331,7 +331,7 @@ export const AlbumListHeaderFilters = ({ if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) { setTable({ data: { rowHeight: e }, key: 'album' }); } else { - setGrid({ data: { size: e }, key: 'album' }); + setGrid({ data: { itemsPerRow: e }, key: 'album' }); } }; @@ -564,17 +564,21 @@ export const AlbumListHeaderFilters = ({ Table (paginated) - Item size + + {display === ListDisplayType.CARD || display === ListDisplayType.POSTER + ? 'Items per row' + : 'Item size'} + diff --git a/src/renderer/features/artists/components/album-artist-list-content.tsx b/src/renderer/features/artists/components/album-artist-list-content.tsx index 11e163a5..b600a2fc 100644 --- a/src/renderer/features/artists/components/album-artist-list-content.tsx +++ b/src/renderer/features/artists/components/album-artist-list-content.tsx @@ -276,7 +276,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon itemCount={checkAlbumArtistList?.data?.totalRecordCount || 0} itemData={itemData} itemGap={20} - itemSize={150 + (grid?.size || 0)} + itemSize={grid?.itemsPerRow || 5} itemType={LibraryItem.ALBUM_ARTIST} loading={checkAlbumArtistList.isLoading} minimumBatchSize={40} diff --git a/src/renderer/features/artists/components/album-artist-list-header-filters.tsx b/src/renderer/features/artists/components/album-artist-list-header-filters.tsx index 1095af6f..eb3580a4 100644 --- a/src/renderer/features/artists/components/album-artist-list-header-filters.tsx +++ b/src/renderer/features/artists/components/album-artist-list-header-filters.tsx @@ -96,7 +96,7 @@ export const AlbumArtistListHeaderFilters = ({ if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) { setTable({ data: { rowHeight: e }, key: pageKey }); } else { - setGrid({ data: { size: e }, key: pageKey }); + setGrid({ data: { itemsPerRow: e }, key: pageKey }); } }; @@ -432,17 +432,23 @@ export const AlbumArtistListHeaderFilters = ({ Item size - + {display === ListDisplayType.CARD || display === ListDisplayType.POSTER ? ( + + ) : ( + + )} {(display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) && ( <> diff --git a/src/renderer/store/list.store.ts b/src/renderer/store/list.store.ts index 4924412c..2c3c7ac9 100644 --- a/src/renderer/store/list.store.ts +++ b/src/renderer/store/list.store.ts @@ -34,8 +34,8 @@ type ListTableProps = { } & DataTableProps; type ListGridProps = { + itemsPerRow?: number; scrollOffset?: number; - size?: number; }; type ItemProps = { @@ -165,8 +165,9 @@ export const useListStore = create()( state.detail[args.key] = { filter: {} as FilterType, grid: { + itemsPerRow: + state.item[page as keyof ListState['item']].grid?.itemsPerRow || 5, scrollOffset: 0, - size: state.item[page as keyof ListState['item']].grid?.size || 200, }, table: { pagination: { @@ -282,7 +283,7 @@ export const useListStore = create()( sortBy: AlbumListSort.RECENTLY_ADDED, sortOrder: SortOrder.DESC, }, - grid: { scrollOffset: 0, size: 200 }, + grid: { itemsPerRow: 5, scrollOffset: 0 }, table: { autoFit: true, columns: [ @@ -323,7 +324,7 @@ export const useListStore = create()( sortBy: AlbumArtistListSort.NAME, sortOrder: SortOrder.DESC, }, - grid: { scrollOffset: 0, size: 200 }, + grid: { itemsPerRow: 5, scrollOffset: 0 }, table: { autoFit: true, columns: [ @@ -409,7 +410,7 @@ export const useListStore = create()( sortBy: SongListSort.RECENTLY_ADDED, sortOrder: SortOrder.DESC, }, - grid: { scrollOffset: 0, size: 0 }, + grid: { itemsPerRow: 5, scrollOffset: 0 }, table: { autoFit: true, columns: [ @@ -470,7 +471,7 @@ export const useListStore = create()( Object.entries(state).filter(([key]) => !['detail'].includes(key)), ); }, - version: 1, + version: 2, }, ), );