Auto scale grid items (#30)

This commit is contained in:
jeffvli 2023-03-09 02:26:09 -08:00
parent 69292a083d
commit 3153cdd6c4
5 changed files with 252 additions and 410 deletions

View File

@ -1,37 +1,56 @@
import { Center } from '@mantine/core'; import { generatePath, Link } from 'react-router-dom';
import { RiAlbumFill } from 'react-icons/ri'; import { ListChildComponentProps } from 'react-window';
import { generatePath, useNavigate } from 'react-router';
import type { ListChildComponentProps } from 'react-window';
import styled from 'styled-components'; import styled from 'styled-components';
import type { CardRow, CardRoute, Play, PlayQueueAddOptions } from '/@/renderer/types';
import { Skeleton } from '/@/renderer/components/skeleton';
import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/grid-card-controls';
import { Album, AlbumArtist, Artist, LibraryItem } from '/@/renderer/api/types'; import { Album, AlbumArtist, Artist, LibraryItem } from '/@/renderer/api/types';
import { CardRows } from '/@/renderer/components/card'; import { CardRows } from '/@/renderer/components/card';
import { Skeleton } from '/@/renderer/components/skeleton';
import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/grid-card-controls';
import { CardRow, PlayQueueAddOptions, Play, CardRoute } from '/@/renderer/types';
const CardWrapper = styled.div<{ interface BaseGridCardProps {
itemGap: number; columnIndex: number;
itemHeight: number; controls: {
itemWidth: number; cardRows: CardRow<Album | AlbumArtist | Artist>[];
link?: boolean; handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void;
}>` handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
flex: ${({ itemWidth }) => `0 0 ${itemWidth - 12}px`}; itemType: LibraryItem;
width: ${({ itemWidth }) => `${itemWidth}px`}; playButtonBehavior: Play;
height: ${({ itemHeight, itemGap }) => `${itemHeight - 12 - itemGap}px`}; route: CardRoute;
margin: ${({ itemGap }) => `0 ${itemGap / 2}px`}; };
padding: 12px 12px 0; data: any;
isHidden?: boolean;
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
}
const DefaultCardContainer = styled.div<{ $isHidden?: boolean }>`
display: flex;
flex-direction: column;
width: 100%;
height: calc(100% - 2rem);
margin: 1rem;
overflow: hidden;
background: var(--card-default-bg); background: var(--card-default-bg);
border-radius: var(--card-default-radius); border-radius: var(--card-default-radius);
cursor: ${({ link }) => link && 'pointer'}; opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)};
transition: border 0.2s ease-in-out, background 0.2s ease-in-out;
user-select: none;
pointer-events: auto; // https://github.com/bvaughn/react-window/issues/128#issuecomment-460166682
&:hover { &:hover {
background: var(--card-default-bg-hover); background: var(--card-default-bg-hover);
} }
`;
&:hover div { const InnerCardContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
padding: 1rem;
overflow: hidden;
.card-controls {
opacity: 0;
}
&:hover .card-controls {
opacity: 1; opacity: 1;
} }
@ -40,26 +59,16 @@ const CardWrapper = styled.div<{
opacity: 0.5; opacity: 0.5;
} }
} }
&:focus-visible {
outline: 1px solid #fff;
}
`; `;
const StyledCard = styled.div` const ImageContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
height: 100%;
padding: 0;
border-radius: var(--card-default-radius);
`;
const ImageSection = styled.div<{ size?: number }>`
position: relative; position: relative;
width: ${({ size }) => size && `${size - 24}px`}; display: flex;
height: ${({ size }) => size && `${size - 24}px`}; align-items: center;
height: 100%;
aspect-ratio: 1/1;
overflow: hidden;
background: var(--placeholder-bg);
border-radius: var(--card-default-radius); border-radius: var(--card-default-radius);
&::before { &::before {
@ -78,151 +87,77 @@ const ImageSection = styled.div<{ size?: number }>`
`; `;
const Image = styled.img` const Image = styled.img`
object-fit: cover;
border-radius: var(--card-default-radius);
box-shadow: 2px 2px 10px 2px rgba(0, 0, 0, 20%);
`;
const ControlsContainer = styled.div`
position: absolute;
bottom: 0;
z-index: 50;
width: 100%; width: 100%;
opacity: 0; max-width: 100%;
transition: all 0.2s ease-in-out; height: auto;
object-fit: contain;
border: 0;
`; `;
const DetailSection = styled.div` const DetailContainer = styled.div`
display: flex; margin-top: 0.5rem;
flex-direction: column;
`; `;
interface BaseGridCardProps {
columnIndex: number;
controls: {
cardRows: CardRow<Album | AlbumArtist | Artist>[];
handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void;
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
itemType: LibraryItem;
playButtonBehavior: Play;
route: CardRoute;
};
data: any;
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
sizes: {
itemGap: number;
itemHeight: number;
itemWidth: number;
};
}
export const DefaultCard = ({ export const DefaultCard = ({
listChildProps, listChildProps,
data, data,
columnIndex, columnIndex,
controls, controls,
sizes, isHidden,
}: BaseGridCardProps) => { }: BaseGridCardProps) => {
const navigate = useNavigate();
const { index } = listChildProps;
const { itemGap, itemHeight, itemWidth } = sizes;
const { itemType, cardRows, route, handlePlayQueueAdd } = controls;
const cardSize = itemWidth - 24;
if (data) { if (data) {
return ( return (
<CardWrapper <DefaultCardContainer>
key={`card-${columnIndex}-${index}`} <Link
link tabIndex={0}
itemGap={itemGap} to={generatePath(
itemHeight={itemHeight} controls.route.route,
itemWidth={itemWidth} controls.route.slugs?.reduce((acc, slug) => {
onClick={() => return {
navigate( ...acc,
generatePath( [slug.slugProperty]: data[slug.idProperty],
route.route, };
route.slugs?.reduce((acc, slug) => { }, {}),
return { )}
...acc, >
[slug.slugProperty]: data[slug.idProperty], <InnerCardContainer key={`card-${columnIndex}-${listChildProps.index}`}>
}; <ImageContainer>
}, {}),
),
)
}
>
<StyledCard>
<ImageSection size={itemWidth}>
{data?.imageUrl ? (
<Image <Image
height={cardSize} placeholder={data?.imagePlaceholderUrl || 'var(--placeholder-bg)'}
placeholder={data?.imagePlaceholderUrl || 'var(--card-default-bg)'}
src={data?.imageUrl} src={data?.imageUrl}
width={cardSize}
/> />
) : (
<Center
sx={{
background: 'var(--placeholder-bg)',
borderRadius: 'var(--card-default-radius)',
height: '100%',
width: '100%',
}}
>
<RiAlbumFill
color="var(--placeholder-fg)"
size={35}
/>
</Center>
)}
<ControlsContainer>
<GridCardControls <GridCardControls
handleFavorite={controls.handleFavorite} handleFavorite={controls.handleFavorite}
handlePlayQueueAdd={handlePlayQueueAdd} handlePlayQueueAdd={controls.handlePlayQueueAdd}
itemData={data} itemData={data}
itemType={itemType} itemType={controls.itemType}
/> />
</ControlsContainer> </ImageContainer>
</ImageSection> <DetailContainer>
<DetailSection> <CardRows
<CardRows data={data}
data={data} rows={controls.cardRows}
rows={cardRows} />
/> </DetailContainer>
</DetailSection> </InnerCardContainer>
</StyledCard> </Link>
</CardWrapper> </DefaultCardContainer>
); );
} }
return ( return (
<CardWrapper <DefaultCardContainer
key={`card-${columnIndex}-${index}`} key={`card-${columnIndex}-${listChildProps.index}`}
itemGap={itemGap} $isHidden={isHidden}
itemHeight={itemHeight}
itemWidth={itemWidth + 12}
> >
<StyledCard> <InnerCardContainer>
<Skeleton <ImageContainer>
visible <Skeleton
radius="sm" visible
> radius="sm"
<ImageSection size={itemWidth} /> />
</Skeleton> </ImageContainer>
<DetailSection> </InnerCardContainer>
{cardRows.map((row: CardRow<Album | Artist | AlbumArtist>, index: number) => ( </DefaultCardContainer>
<Skeleton
key={`row-${row.property}-${columnIndex}`}
height={20}
my={2}
radius="md"
visible={!data}
width={!data ? (index > 0 ? '50%' : '90%') : '100%'}
/>
))}
</DetailSection>
</StyledCard>
</CardWrapper>
); );
}; };

View File

@ -1,8 +1,7 @@
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import React from 'react'; import React from 'react';
import type { UnstyledButtonProps } from '@mantine/core'; import type { UnstyledButtonProps } from '@mantine/core';
import { Group } from '@mantine/core'; import { RiPlayFill, RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
import { RiPlayFill, RiMore2Fill, RiHeartFill, RiHeartLine } from 'react-icons/ri';
import styled from 'styled-components'; import styled from 'styled-components';
import { _Button } from '/@/renderer/components/button'; import { _Button } from '/@/renderer/components/button';
import type { PlayQueueAddOptions } from '/@/renderer/types'; import type { PlayQueueAddOptions } from '/@/renderer/types';
@ -18,6 +17,7 @@ import {
type PlayButtonType = UnstyledButtonProps & React.ComponentPropsWithoutRef<'button'>; type PlayButtonType = UnstyledButtonProps & React.ComponentPropsWithoutRef<'button'>;
const PlayButton = styled.button<PlayButtonType>` const PlayButton = styled.button<PlayButtonType>`
position: absolute;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -28,7 +28,7 @@ const PlayButton = styled.button<PlayButtonType>`
border-radius: 50%; border-radius: 50%;
opacity: 0.8; opacity: 0.8;
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;
transition: scale 0.2s linear; transition: scale 0.1s ease-in-out;
&:hover { &:hover {
opacity: 1; opacity: 1;
@ -63,6 +63,8 @@ const SecondaryButton = styled(_Button)`
`; `;
const GridCardControlsContainer = styled.div` const GridCardControlsContainer = styled.div`
position: absolute;
z-index: 100;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -76,24 +78,13 @@ const ControlsRow = styled.div`
height: calc(100% / 3); height: calc(100% / 3);
`; `;
// const TopControls = styled(ControlsRow)`
// display: flex;
// align-items: flex-start;
// justify-content: space-between;
// padding: 0.5rem;
// `;
// const CenterControls = styled(ControlsRow)`
// display: flex;
// align-items: center;
// justify-content: center;
// padding: 0.5rem;
// `;
const BottomControls = styled(ControlsRow)` const BottomControls = styled(ControlsRow)`
position: absolute;
bottom: 0;
display: flex; display: flex;
gap: 0.5rem;
align-items: flex-end; align-items: flex-end;
justify-content: space-between; justify-content: flex-end;
padding: 1rem 0.5rem; padding: 1rem 0.5rem;
`; `;
@ -146,45 +137,43 @@ export const GridCardControls = ({
); );
return ( return (
<GridCardControlsContainer> <GridCardControlsContainer className="card-controls">
<PlayButton onClick={handlePlay}>
<RiPlayFill size={25} />
</PlayButton>
<BottomControls> <BottomControls>
<PlayButton onClick={handlePlay}> <SecondaryButton
<RiPlayFill size={25} /> p={5}
</PlayButton> sx={{ svg: { fill: 'white !important' } }}
<Group spacing="xs"> variant="subtle"
<SecondaryButton onClick={handleFavorites}
p={5} >
sx={{ svg: { fill: 'white !important' } }} <FavoriteWrapper isFavorite={itemData?.isFavorite}>
variant="subtle" {itemData?.userFavorite ? (
onClick={handleFavorites} <RiHeartFill size={20} />
> ) : (
<FavoriteWrapper isFavorite={itemData?.isFavorite}> <RiHeartLine
{itemData?.userFavorite ? ( color="white"
<RiHeartFill size={20} /> size={20}
) : ( />
<RiHeartLine )}
color="white" </FavoriteWrapper>
size={20} </SecondaryButton>
/> <SecondaryButton
)} p={5}
</FavoriteWrapper> sx={{ svg: { fill: 'white !important' } }}
</SecondaryButton> variant="subtle"
<SecondaryButton onClick={(e) => {
p={5} e.preventDefault();
sx={{ svg: { fill: 'white !important' } }} e.stopPropagation();
variant="subtle" handleContextMenu(e, [itemData]);
onClick={(e) => { }}
e.preventDefault(); >
e.stopPropagation(); <RiMoreFill
handleContextMenu(e, [itemData]); color="white"
}} size={20}
> />
<RiMore2Fill </SecondaryButton>
color="white"
size={20}
/>
</SecondaryButton>
</Group>
</BottomControls> </BottomControls>
</GridCardControlsContainer> </GridCardControlsContainer>
); );

View File

@ -3,15 +3,11 @@ import type { ListChildComponentProps } from 'react-window';
import { areEqual } from 'react-window'; import { areEqual } from 'react-window';
import { DefaultCard } from '/@/renderer/components/virtual-grid/grid-card/default-card'; import { DefaultCard } from '/@/renderer/components/virtual-grid/grid-card/default-card';
import { PosterCard } from '/@/renderer/components/virtual-grid/grid-card/poster-card'; import { PosterCard } from '/@/renderer/components/virtual-grid/grid-card/poster-card';
import type { GridCardData } from '/@/renderer/types'; import { GridCardData, ListDisplayType } from '/@/renderer/types';
import { ListDisplayType } from '/@/renderer/types';
export const GridCard = memo(({ data, index, style }: ListChildComponentProps) => { export const GridCard = memo(({ data, index, style }: ListChildComponentProps) => {
const { const {
itemHeight,
itemWidth,
columnCount, columnCount,
itemGap,
itemCount, itemCount,
cardRows, cardRows,
itemData, itemData,
@ -27,9 +23,14 @@ export const GridCard = memo(({ data, index, style }: ListChildComponentProps) =
const startIndex = index * columnCount; const startIndex = index * columnCount;
const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1); const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1);
const columnCountInRow = stopIndex - startIndex + 1;
let columnCountToAdd = 0;
if (columnCountInRow !== columnCount) {
columnCountToAdd = columnCount - columnCountInRow;
}
const View = display === ListDisplayType.CARD ? DefaultCard : PosterCard; const View = display === ListDisplayType.CARD ? DefaultCard : PosterCard;
for (let i = startIndex; i <= stopIndex; i += 1) { for (let i = startIndex; i <= stopIndex + columnCountToAdd; i += 1) {
cards.push( cards.push(
<View <View
key={`card-${i}-${index}`} key={`card-${i}-${index}`}
@ -43,24 +44,20 @@ export const GridCard = memo(({ data, index, style }: ListChildComponentProps) =
route, route,
}} }}
data={itemData[i]} data={itemData[i]}
isHidden={i > stopIndex}
listChildProps={{ index }} listChildProps={{ index }}
sizes={{ itemGap, itemHeight, itemWidth }}
/>, />,
); );
} }
return ( return (
<> <div
<div style={{
style={{ ...style,
...style, display: 'flex',
alignItems: 'center', }}
display: 'flex', >
justifyContent: 'start', {cards}
}} </div>
>
{cards}
</div>
</>
); );
}, areEqual); }, areEqual);

View File

@ -1,28 +1,41 @@
import { Center } from '@mantine/core'; import { generatePath, Link } from 'react-router-dom';
import { RiAlbumFill } from 'react-icons/ri'; import { ListChildComponentProps } from 'react-window';
import { generatePath } from 'react-router';
import { Link } from 'react-router-dom';
import type { ListChildComponentProps } from 'react-window';
import styled from 'styled-components'; import styled from 'styled-components';
import { Skeleton } from '/@/renderer/components/skeleton'; import { Album, AlbumArtist, Artist, LibraryItem } from '/@/renderer/api/types';
import type { CardRow, CardRoute, Play, PlayQueueAddOptions } from '/@/renderer/types';
import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/grid-card-controls';
import { Album, Artist, AlbumArtist, LibraryItem } from '/@/renderer/api/types';
import { CardRows } from '/@/renderer/components/card'; import { CardRows } from '/@/renderer/components/card';
import { Skeleton } from '/@/renderer/components/skeleton';
import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/grid-card-controls';
import { CardRow, PlayQueueAddOptions, Play, CardRoute } from '/@/renderer/types';
const CardWrapper = styled.div<{ interface BaseGridCardProps {
itemGap: number; columnIndex: number;
itemHeight: number; controls: {
itemWidth: number; cardRows: CardRow<Album | AlbumArtist | Artist>[];
}>` handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void;
flex: ${({ itemWidth }) => `0 0 ${itemWidth}px`}; handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
width: ${({ itemWidth }) => `${itemWidth}px`}; itemType: LibraryItem;
height: ${({ itemHeight, itemGap }) => `${itemHeight - itemGap}px`}; playButtonBehavior: Play;
margin: ${({ itemGap }) => `0 ${itemGap / 2}px`}; route: CardRoute;
user-select: none; };
pointer-events: auto; // https://github.com/bvaughn/react-window/issues/128#issuecomment-460166682 data: any;
isHidden?: boolean;
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
}
&:hover div { const PosterCardContainer = styled.div<{ $isHidden?: boolean }>`
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
padding: 1rem;
overflow: hidden;
opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)};
.card-controls {
opacity: 0;
}
&:hover .card-controls {
opacity: 1; opacity: 1;
} }
@ -31,30 +44,16 @@ const CardWrapper = styled.div<{
opacity: 0.5; opacity: 0.5;
} }
} }
&:focus-visible {
outline: 1px solid #fff;
}
`; `;
const StyledCard = styled.div` const ImageContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
height: 100%;
padding: 0;
background: var(--card-poster-bg);
border-radius: var(--card-poster-radius);
&:hover {
background: var(--card-poster-bg-hover);
}
`;
const ImageSection = styled.div`
position: relative; position: relative;
width: 100%; display: flex;
align-items: center;
height: 100%;
aspect-ratio: 1/1;
overflow: hidden;
background: var(--card-default-bg);
border-radius: var(--card-poster-radius); border-radius: var(--card-poster-radius);
&::before { &::before {
@ -72,148 +71,74 @@ const ImageSection = styled.div`
} }
`; `;
interface ImageProps { const Image = styled.img`
height: number; width: 100%;
isLoading?: boolean; max-width: 100%;
} height: auto;
const Image = styled.img<ImageProps>`
object-fit: cover; object-fit: cover;
border: 0; border: 0;
border-radius: var(--card-poster-radius);
`; `;
const ControlsContainer = styled.div` const DetailContainer = styled.div`
position: absolute; margin-top: 0.5rem;
bottom: 0;
z-index: 50;
width: 100%;
opacity: 0;
transition: all 0.2s ease-in-out;
`; `;
const DetailSection = styled.div`
display: flex;
flex-direction: column;
`;
interface BaseGridCardProps {
columnIndex: number;
controls: {
cardRows: CardRow<Album | AlbumArtist | Artist>[];
handleFavorite: (options: { id: string[]; isFavorite: boolean; itemType: LibraryItem }) => void;
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
itemType: LibraryItem;
playButtonBehavior: Play;
route: CardRoute;
};
data: any;
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
sizes: {
itemGap: number;
itemHeight: number;
itemWidth: number;
};
}
export const PosterCard = ({ export const PosterCard = ({
listChildProps, listChildProps,
data, data,
columnIndex, columnIndex,
controls, controls,
sizes, isHidden,
}: BaseGridCardProps) => { }: BaseGridCardProps) => {
if (data) { if (data) {
return ( return (
<CardWrapper <PosterCardContainer key={`card-${columnIndex}-${listChildProps.index}`}>
key={`card-${columnIndex}-${listChildProps.index}`} <Link
itemGap={sizes.itemGap} tabIndex={0}
itemHeight={sizes.itemHeight} to={generatePath(
itemWidth={sizes.itemWidth} controls.route.route,
> controls.route.slugs?.reduce((acc, slug) => {
<StyledCard> return {
<Link ...acc,
tabIndex={0} [slug.slugProperty]: data[slug.idProperty],
to={generatePath( };
controls.route.route, }, {}),
controls.route.slugs?.reduce((acc, slug) => { )}
return { >
...acc, <ImageContainer>
[slug.slugProperty]: data[slug.idProperty], <Image
}; placeholder={data?.imagePlaceholderUrl || 'var(--card-default-bg)'}
}, {}), src={data?.imageUrl}
)}
>
<ImageSection style={{ height: `${sizes.itemWidth}px` }}>
{data?.imageUrl ? (
<Image
height={sizes.itemWidth}
placeholder={data?.imagePlaceholderUrl || 'var(--card-default-bg)'}
src={data?.imageUrl}
width={sizes.itemWidth}
/>
) : (
<Center
sx={{
background: 'var(--placeholder-bg)',
borderRadius: 'var(--card-poster-radius)',
height: '100%',
}}
>
<RiAlbumFill
color="var(--placeholder-fg)"
size={35}
/>
</Center>
)}
<ControlsContainer>
<GridCardControls
handleFavorite={controls.handleFavorite}
handlePlayQueueAdd={controls.handlePlayQueueAdd}
itemData={data}
itemType={controls.itemType}
/>
</ControlsContainer>
</ImageSection>
</Link>
<DetailSection>
<CardRows
data={data}
rows={controls.cardRows}
/> />
</DetailSection> <GridCardControls
</StyledCard> handleFavorite={controls.handleFavorite}
</CardWrapper> handlePlayQueueAdd={controls.handlePlayQueueAdd}
itemData={data}
itemType={controls.itemType}
/>
</ImageContainer>
</Link>
<DetailContainer>
<CardRows
data={data}
rows={controls.cardRows}
/>
</DetailContainer>
</PosterCardContainer>
); );
} }
return ( return (
<CardWrapper <PosterCardContainer
key={`card-${columnIndex}-${listChildProps.index}`} key={`card-${columnIndex}-${listChildProps.index}`}
itemGap={sizes.itemGap} $isHidden={isHidden}
itemHeight={sizes.itemHeight}
itemWidth={sizes.itemWidth}
> >
<StyledCard> <ImageContainer>
<Skeleton <Skeleton
visible visible
radius="sm" radius="sm"
> />
<ImageSection style={{ height: `${sizes.itemWidth}px` }} /> </ImageContainer>
</Skeleton> </PosterCardContainer>
<DetailSection>
{controls.cardRows.map((row: CardRow<Album | Artist | AlbumArtist>, index: number) => (
<Skeleton
key={`row-${row.property}-${columnIndex}`}
height={20}
my={2}
radius="md"
visible={!data}
width={!data ? (index > 0 ? '50%' : '90%') : '100%'}
/>
))}
</DetailSection>
</StyledCard>
</CardWrapper>
); );
}; };

View File

@ -29,14 +29,6 @@ interface VirtualGridProps extends Omit<FixedSizeListProps, 'children' | 'itemSi
setItemData: (data: any[]) => void; setItemData: (data: any[]) => void;
} }
// const constrainWidth = (width: number) => {
// if (width < 1920) {
// return width;
// }
// return 1920;
// };
export const VirtualInfiniteGrid = forwardRef( export const VirtualInfiniteGrid = forwardRef(
( (
{ {
@ -64,16 +56,20 @@ export const VirtualInfiniteGrid = forwardRef(
const listRef = useRef<any>(null); const listRef = useRef<any>(null);
const loader = useRef<InfiniteLoader>(null); const loader = useRef<InfiniteLoader>(null);
const sz = itemSize / 2;
const { itemHeight, rowCount, columnCount } = useMemo(() => { const { itemHeight, rowCount, columnCount } = useMemo(() => {
const itemsPerRow = Math.floor((Number(width) - itemGap + 3) / (itemSize! + itemGap + 2)); const itemsPerRow = Math.floor(Number(width) / sz!) - 1;
const widthPerItem = Number(width) / itemsPerRow - 10;
const itemHeight = widthPerItem + cardRows.length * 26;
return { return {
columnCount: itemsPerRow, columnCount: itemsPerRow,
itemHeight: itemSize! + cardRows.length * 22 + itemGap, itemHeight,
itemWidth: itemSize! + itemGap, itemWidth: sz,
rowCount: Math.ceil(itemCount / itemsPerRow), rowCount: Math.ceil(itemCount / itemsPerRow),
}; };
}, [cardRows.length, itemCount, itemGap, itemSize, width]); }, [cardRows.length, itemCount, sz, width]);
const isItemLoaded = useCallback( const isItemLoaded = useCallback(
(index: number) => { (index: number) => {
@ -153,7 +149,7 @@ export const VirtualInfiniteGrid = forwardRef(
itemCount={itemCount || 0} itemCount={itemCount || 0}
itemData={itemData} itemData={itemData}
itemGap={itemGap} itemGap={itemGap}
itemHeight={itemHeight + itemGap / 2} itemHeight={itemHeight}
itemType={itemType} itemType={itemType}
itemWidth={itemSize} itemWidth={itemSize}
refInstance={(list) => { refInstance={(list) => {