mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
Improve grid
This commit is contained in:
@@ -1,9 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Button, UnstyledButton, UnstyledButtonProps } from '@mantine/core';
|
import { Box, UnstyledButton, Group, UnstyledButtonProps } from '@mantine/core';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { RiPlayFill } from 'react-icons/ri';
|
import {
|
||||||
import { Play } from '../../../types';
|
RiPlayFill,
|
||||||
|
RiMore2Fill,
|
||||||
|
RiHeartFill,
|
||||||
|
RiHeartLine,
|
||||||
|
} from 'react-icons/ri';
|
||||||
|
import { Button } from '@/renderer/components/button';
|
||||||
|
import { DropdownMenu } from '@/renderer/components/dropdown-menu';
|
||||||
|
import { Play } from '@/renderer/types';
|
||||||
|
|
||||||
type PlayButtonType = UnstyledButtonProps &
|
type PlayButtonType = UnstyledButtonProps &
|
||||||
React.ComponentPropsWithoutRef<'button'>;
|
React.ComponentPropsWithoutRef<'button'>;
|
||||||
@@ -14,23 +21,24 @@ const PlayButton = styled(UnstyledButton)<PlayButtonType>`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border: 1px solid var(--primary-color);
|
background-color: rgb(255, 255, 255);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: default;
|
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
transition: scale 0.2s ease-in;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
scale: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: #000;
|
fill: rgb(0, 0, 0);
|
||||||
stroke: #000;
|
stroke: rgb(0, 0, 0);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const GridCardControlsContainer = styled.div`
|
const GridCardControlsContainer = styled(Box)`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -48,36 +56,45 @@ const TopControls = styled(ControlsRow)`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding: 0.5rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CenterControls = styled(ControlsRow)`
|
const CenterControls = styled(ControlsRow)`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
padding: 0.5rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const BottomControls = styled(ControlsRow)`
|
const BottomControls = styled(ControlsRow)`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FavoriteWrapper = styled.span<{ isFavorite: boolean }>`
|
||||||
|
svg {
|
||||||
|
fill: ${(props) => props.isFavorite && 'var(--primary-color)'};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GridCardControls = ({
|
export const GridCardControls = ({
|
||||||
itemData,
|
itemData,
|
||||||
|
itemType,
|
||||||
handlePlayQueueAdd,
|
handlePlayQueueAdd,
|
||||||
cardControls,
|
|
||||||
}: any) => {
|
}: any) => {
|
||||||
return (
|
return (
|
||||||
<GridCardControlsContainer>
|
<GridCardControlsContainer>
|
||||||
<TopControls />
|
<TopControls />
|
||||||
<CenterControls animate={{ opacity: 1 }} initial={{ opacity: 0 }}>
|
<CenterControls animate={{ opacity: 1 }} initial={{ opacity: 0 }} />
|
||||||
|
<BottomControls>
|
||||||
<PlayButton
|
<PlayButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handlePlayQueueAdd({
|
handlePlayQueueAdd({
|
||||||
byItemType: {
|
byItemType: {
|
||||||
endpoint: cardControls.endpoint,
|
id: itemData.id,
|
||||||
id: itemData[cardControls.idProperty],
|
type: itemType,
|
||||||
type: cardControls.type,
|
|
||||||
},
|
},
|
||||||
play: Play.NOW,
|
play: Play.NOW,
|
||||||
});
|
});
|
||||||
@@ -85,36 +102,32 @@ export const GridCardControls = ({
|
|||||||
>
|
>
|
||||||
<RiPlayFill size={25} />
|
<RiPlayFill size={25} />
|
||||||
</PlayButton>
|
</PlayButton>
|
||||||
</CenterControls>
|
<Group spacing="xs">
|
||||||
<BottomControls>
|
<Button disabled p={5} variant="subtle">
|
||||||
<Button
|
<FavoriteWrapper isFavorite={itemData?.isFavorite}>
|
||||||
onClick={() => {
|
{itemData?.isFavorite ? (
|
||||||
handlePlayQueueAdd({
|
<RiHeartFill size={20} />
|
||||||
byItemType: {
|
) : (
|
||||||
endpoint: cardControls.endpoint,
|
<RiHeartLine color="white" size={20} />
|
||||||
id: itemData[cardControls.idProperty],
|
)}
|
||||||
type: cardControls.type,
|
</FavoriteWrapper>
|
||||||
},
|
</Button>
|
||||||
play: Play.NEXT,
|
<DropdownMenu withinPortal position="bottom-start">
|
||||||
});
|
<DropdownMenu.Target>
|
||||||
}}
|
<Button p={5} variant="subtle">
|
||||||
>
|
<RiMore2Fill color="white" size={20} />
|
||||||
NEXT
|
</Button>
|
||||||
</Button>
|
</DropdownMenu.Target>
|
||||||
<Button
|
<DropdownMenu.Dropdown>
|
||||||
onClick={() => {
|
<DropdownMenu.Item>Play next</DropdownMenu.Item>
|
||||||
handlePlayQueueAdd({
|
<DropdownMenu.Item>Play later</DropdownMenu.Item>
|
||||||
byItemType: {
|
<DropdownMenu.Divider />
|
||||||
endpoint: cardControls.endpoint,
|
<DropdownMenu.Item disabled>Add to playlist</DropdownMenu.Item>
|
||||||
id: itemData[cardControls.idProperty],
|
<DropdownMenu.Divider />
|
||||||
type: cardControls.type,
|
<DropdownMenu.Item disabled>Refresh metadata</DropdownMenu.Item>
|
||||||
},
|
</DropdownMenu.Dropdown>
|
||||||
play: Play.LAST,
|
</DropdownMenu>
|
||||||
});
|
</Group>
|
||||||
}}
|
|
||||||
>
|
|
||||||
LATER
|
|
||||||
</Button>
|
|
||||||
</BottomControls>
|
</BottomControls>
|
||||||
</GridCardControlsContainer>
|
</GridCardControlsContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import React from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Skeleton } from '@mantine/core';
|
import { Skeleton } from '@mantine/core';
|
||||||
import { motion } from 'framer-motion';
|
import { GridCardControls } from '@/renderer/components/virtual-grid/grid-card-controls';
|
||||||
|
import { fadeIn } from '@/renderer/styles';
|
||||||
import { CardRow } from '../../types';
|
import { CardRow } from '../../types';
|
||||||
import { Text } from '../text';
|
import { Text } from '../text';
|
||||||
import { GridCardControls } from './grid-card-controls';
|
|
||||||
|
|
||||||
const CardWrapper = styled(motion.div)<{
|
const CardWrapper = styled.div<{
|
||||||
itemGap: number;
|
itemGap: number;
|
||||||
itemHeight: number;
|
itemHeight: number;
|
||||||
itemWidth: number;
|
itemWidth: number;
|
||||||
@@ -15,11 +14,20 @@ const CardWrapper = styled(motion.div)<{
|
|||||||
width: ${({ itemWidth }) => `${itemWidth}px`};
|
width: ${({ itemWidth }) => `${itemWidth}px`};
|
||||||
height: ${({ itemHeight }) => `${itemHeight}px`};
|
height: ${({ itemHeight }) => `${itemHeight}px`};
|
||||||
margin: ${({ itemGap }) => `0 ${itemGap / 2}px`};
|
margin: ${({ itemGap }) => `0 ${itemGap / 2}px`};
|
||||||
filter: drop-shadow(0 4px 4px #000);
|
|
||||||
transition: border 0.2s ease-in-out;
|
transition: border 0.2s ease-in-out;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: auto; // https://github.com/bvaughn/react-window/issues/128#issuecomment-460166682
|
pointer-events: auto; // https://github.com/bvaughn/react-window/issues/128#issuecomment-460166682
|
||||||
|
|
||||||
|
&:hover div {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover * {
|
||||||
|
&::before {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: 1px solid #fff;
|
outline: 1px solid #fff;
|
||||||
}
|
}
|
||||||
@@ -37,8 +45,8 @@ const StyledCard = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ImageSection = styled.div`
|
const ImageSection = styled.div`
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface ImageProps {
|
interface ImageProps {
|
||||||
@@ -46,31 +54,42 @@ interface ImageProps {
|
|||||||
src: string;
|
src: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const Image = styled(motion.div).attrs((props: ImageProps) => ({
|
const Image = styled.div<ImageProps>`
|
||||||
// style: {
|
${fadeIn};
|
||||||
// background: `url(${props.src})`,
|
|
||||||
// backgroundPosition: 'center',
|
|
||||||
// backgroundSize: 'cover',
|
|
||||||
// },
|
|
||||||
// }))<ImageProps>`
|
|
||||||
// height: ${({ height }) => `${height}px`};
|
|
||||||
// background-position: center;
|
|
||||||
// background-size: cover;
|
|
||||||
// border: 0;
|
|
||||||
// `;
|
|
||||||
|
|
||||||
const Image = styled(motion.div)<ImageProps>`
|
|
||||||
height: ${({ height }) => `${height}px`};
|
height: ${({ height }) => `${height}px`};
|
||||||
background: ${({ src }) => `url(${src})`};
|
background-image: ${({ src }) => `url(${src})`};
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-image 0.5s ease-in-out;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
rgba(0, 0, 0, 100%) 35%,
|
||||||
|
rgba(0, 0, 0, 0%) 100%
|
||||||
|
);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
content: '';
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ControlsContainer = styled.div`
|
const ControlsContainer = styled.div`
|
||||||
display: block;
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 50;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
opacity: 0;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const DetailSection = styled.div`
|
const DetailSection = styled.div`
|
||||||
@@ -94,6 +113,7 @@ export const GridCard = ({ data, index, style }: any) => {
|
|||||||
handlePlayQueueAdd,
|
handlePlayQueueAdd,
|
||||||
cardRows,
|
cardRows,
|
||||||
itemData,
|
itemData,
|
||||||
|
itemType,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
const startIndex = index * columnCount;
|
const startIndex = index * columnCount;
|
||||||
@@ -101,40 +121,67 @@ export const GridCard = ({ data, index, style }: any) => {
|
|||||||
const cards = [];
|
const cards = [];
|
||||||
|
|
||||||
for (let i = startIndex; i <= stopIndex; i += 1) {
|
for (let i = startIndex; i <= stopIndex; i += 1) {
|
||||||
cards.push(
|
if (itemData[i]) {
|
||||||
<React.Fragment key={`card-${i}-${index}`}>
|
cards.push(
|
||||||
<CardWrapper
|
<CardWrapper
|
||||||
|
key={`card-${i}-${index}`}
|
||||||
itemGap={itemGap}
|
itemGap={itemGap}
|
||||||
itemHeight={itemHeight}
|
itemHeight={itemHeight}
|
||||||
itemWidth={itemWidth}
|
itemWidth={itemWidth}
|
||||||
>
|
>
|
||||||
<Skeleton visible={!itemData[i]}>
|
<StyledCard>
|
||||||
<StyledCard>
|
<ImageSection style={{ height: `${itemWidth}px` }}>
|
||||||
<ImageSection>
|
<Image height={itemWidth} src={itemData[i]?.imageUrl} />
|
||||||
<Image height={itemWidth} src={itemData[i]?.imageUrl}>
|
<ControlsContainer>
|
||||||
<ControlsContainer>
|
<GridCardControls
|
||||||
<GridCardControls
|
cardControls={cardControls}
|
||||||
cardControls={cardControls}
|
handlePlayQueueAdd={handlePlayQueueAdd}
|
||||||
handlePlayQueueAdd={handlePlayQueueAdd}
|
itemData={itemData[i]}
|
||||||
itemData={itemData[i]}
|
itemType={itemType}
|
||||||
/>
|
/>
|
||||||
</ControlsContainer>
|
</ControlsContainer>
|
||||||
</Image>
|
</ImageSection>
|
||||||
</ImageSection>
|
<DetailSection>
|
||||||
<DetailSection>
|
{cardRows.map((row: CardRow) => (
|
||||||
{cardRows.map((row: CardRow) => (
|
<Row>
|
||||||
<Row key={`row-${row.prop}`}>
|
<Text overflow="hidden" to={row.route?.route} weight={500}>
|
||||||
<Text overflow="hidden" weight={500}>
|
{itemData[i] && itemData[i][row.prop]}
|
||||||
{itemData[i] && itemData[i][row.prop]}
|
</Text>
|
||||||
</Text>
|
</Row>
|
||||||
</Row>
|
))}
|
||||||
))}
|
</DetailSection>
|
||||||
</DetailSection>
|
</StyledCard>
|
||||||
</StyledCard>
|
|
||||||
</Skeleton>
|
|
||||||
</CardWrapper>
|
</CardWrapper>
|
||||||
</React.Fragment>
|
);
|
||||||
);
|
} else {
|
||||||
|
cards.push(
|
||||||
|
<CardWrapper
|
||||||
|
key={`card-${i}-${index}`}
|
||||||
|
itemGap={itemGap}
|
||||||
|
itemHeight={itemHeight}
|
||||||
|
itemWidth={itemWidth}
|
||||||
|
>
|
||||||
|
<StyledCard>
|
||||||
|
<Skeleton visible radius="sm">
|
||||||
|
<ImageSection style={{ height: `${itemWidth}px` }} />
|
||||||
|
</Skeleton>
|
||||||
|
<DetailSection>
|
||||||
|
{cardRows.map((row: CardRow, index: number) => (
|
||||||
|
<Skeleton
|
||||||
|
key={`row-${row.prop}`}
|
||||||
|
my={2}
|
||||||
|
radius="md"
|
||||||
|
visible={!itemData[i]}
|
||||||
|
width={!itemData[i] ? (index > 0 ? '50%' : '90%') : '100%'}
|
||||||
|
>
|
||||||
|
<Row />
|
||||||
|
</Skeleton>
|
||||||
|
))}
|
||||||
|
</DetailSection>
|
||||||
|
</StyledCard>
|
||||||
|
</CardWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { Ref, useMemo } from 'react';
|
import { Ref, useMemo } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import { FixedSizeList, FixedSizeListProps } from 'react-window';
|
import { FixedSizeList, FixedSizeListProps } from 'react-window';
|
||||||
import { GridCard } from '@/renderer/components/virtual-grid/grid-card';
|
import { GridCard } from '@/renderer/components/virtual-grid/grid-card';
|
||||||
import { usePlayQueueHandler } from '@/renderer/features/player/hooks/use-playqueue-handler';
|
import { usePlayQueueHandler } from '@/renderer/features/player/hooks/use-playqueue-handler';
|
||||||
import { CardRow } from '@/renderer/types';
|
import { CardRow, LibraryItem } from '@/renderer/types';
|
||||||
|
|
||||||
export const VirtualGridWrapper = ({
|
export const VirtualGridWrapper = ({
|
||||||
refInstance,
|
refInstance,
|
||||||
cardControls,
|
|
||||||
cardRows,
|
cardRows,
|
||||||
itemGap,
|
itemGap,
|
||||||
|
itemType,
|
||||||
itemWidth,
|
itemWidth,
|
||||||
itemHeight,
|
itemHeight,
|
||||||
itemCount,
|
itemCount,
|
||||||
@@ -17,12 +18,12 @@ export const VirtualGridWrapper = ({
|
|||||||
itemData,
|
itemData,
|
||||||
...rest
|
...rest
|
||||||
}: Omit<FixedSizeListProps, 'ref' | 'itemSize' | 'children'> & {
|
}: Omit<FixedSizeListProps, 'ref' | 'itemSize' | 'children'> & {
|
||||||
cardControls: any;
|
|
||||||
cardRows: CardRow[];
|
cardRows: CardRow[];
|
||||||
columnCount: number;
|
columnCount: number;
|
||||||
itemData: any[];
|
itemData: any[];
|
||||||
itemGap: number;
|
itemGap: number;
|
||||||
itemHeight: number;
|
itemHeight: number;
|
||||||
|
itemType: LibraryItem;
|
||||||
itemWidth: number;
|
itemWidth: number;
|
||||||
refInstance: Ref<any>;
|
refInstance: Ref<any>;
|
||||||
rowCount: number;
|
rowCount: number;
|
||||||
@@ -31,7 +32,6 @@ export const VirtualGridWrapper = ({
|
|||||||
|
|
||||||
const memo = useMemo(
|
const memo = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
cardControls,
|
|
||||||
cardRows,
|
cardRows,
|
||||||
columnCount,
|
columnCount,
|
||||||
handlePlayQueueAdd,
|
handlePlayQueueAdd,
|
||||||
@@ -39,11 +39,12 @@ export const VirtualGridWrapper = ({
|
|||||||
itemData,
|
itemData,
|
||||||
itemGap,
|
itemGap,
|
||||||
itemHeight,
|
itemHeight,
|
||||||
|
itemType,
|
||||||
itemWidth,
|
itemWidth,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
cardControls,
|
|
||||||
cardRows,
|
cardRows,
|
||||||
|
itemType,
|
||||||
columnCount,
|
columnCount,
|
||||||
handlePlayQueueAdd,
|
handlePlayQueueAdd,
|
||||||
itemCount,
|
itemCount,
|
||||||
@@ -67,3 +68,13 @@ export const VirtualGridWrapper = ({
|
|||||||
</FixedSizeList>
|
</FixedSizeList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const VirtualGridContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const VirtualGridAutoSizerContainer = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|||||||
@@ -3,31 +3,31 @@ import debounce from 'lodash/debounce';
|
|||||||
import { FixedSizeListProps } from 'react-window';
|
import { FixedSizeListProps } from 'react-window';
|
||||||
import InfiniteLoader from 'react-window-infinite-loader';
|
import InfiniteLoader from 'react-window-infinite-loader';
|
||||||
import { VirtualGridWrapper } from '@/renderer/components/virtual-grid/virtual-grid-wrapper';
|
import { VirtualGridWrapper } from '@/renderer/components/virtual-grid/virtual-grid-wrapper';
|
||||||
import { CardRow } from '@/renderer/types';
|
import { CardRow, LibraryItem } from '@/renderer/types';
|
||||||
|
|
||||||
interface VirtualGridProps
|
interface VirtualGridProps
|
||||||
extends Omit<FixedSizeListProps, 'children' | 'itemSize'> {
|
extends Omit<FixedSizeListProps, 'children' | 'itemSize'> {
|
||||||
cardControls: any;
|
|
||||||
cardRows: CardRow[];
|
cardRows: CardRow[];
|
||||||
itemGap?: number;
|
fetchFn: (options: {
|
||||||
|
columnCount: number;
|
||||||
|
skip: number;
|
||||||
|
take: number;
|
||||||
|
}) => Promise<any>;
|
||||||
|
itemGap: number;
|
||||||
itemSize: number;
|
itemSize: number;
|
||||||
|
itemType: LibraryItem;
|
||||||
minimumBatchSize?: number;
|
minimumBatchSize?: number;
|
||||||
query: ({ serverId }: { serverId: string }, props: any) => Promise<any>;
|
|
||||||
queryParams?: Record<string, any>;
|
|
||||||
serverId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VirtualInfiniteGrid = ({
|
export const VirtualInfiniteGrid = ({
|
||||||
itemCount,
|
itemCount,
|
||||||
itemGap,
|
itemGap,
|
||||||
itemSize,
|
itemSize,
|
||||||
cardControls,
|
itemType,
|
||||||
cardRows,
|
cardRows,
|
||||||
minimumBatchSize,
|
minimumBatchSize,
|
||||||
query,
|
fetchFn,
|
||||||
queryParams,
|
|
||||||
height,
|
height,
|
||||||
serverId,
|
|
||||||
width,
|
width,
|
||||||
}: VirtualGridProps) => {
|
}: VirtualGridProps) => {
|
||||||
const [itemData, setItemData] = useState<any[]>([]);
|
const [itemData, setItemData] = useState<any[]>([]);
|
||||||
@@ -36,7 +36,7 @@ export const VirtualInfiniteGrid = ({
|
|||||||
|
|
||||||
const { itemHeight, rowCount, columnCount } = useMemo(() => {
|
const { itemHeight, rowCount, columnCount } = useMemo(() => {
|
||||||
const itemsPerRow = Math.floor(
|
const itemsPerRow = Math.floor(
|
||||||
(Number(width) - itemGap! + 3) / (itemSize! + itemGap! + 2)
|
(Number(width) - itemGap + 3) / (itemSize! + itemGap + 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -60,20 +60,17 @@ export const VirtualInfiniteGrid = ({
|
|||||||
const start = startIndex * columnCount;
|
const start = startIndex * columnCount;
|
||||||
const end = stopIndex * columnCount + columnCount;
|
const end = stopIndex * columnCount + columnCount;
|
||||||
|
|
||||||
const t = await query(
|
const items = await fetchFn({
|
||||||
{ serverId },
|
columnCount,
|
||||||
{
|
skip: start,
|
||||||
skip: start,
|
take: end - start,
|
||||||
take: end - start,
|
});
|
||||||
...queryParams,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const newData: any[] = [...itemData];
|
const newData: any[] = [...itemData];
|
||||||
|
|
||||||
let itemIndex = 0;
|
let itemIndex = 0;
|
||||||
for (let rowIndex = start; rowIndex < end; rowIndex += 1) {
|
for (let rowIndex = start; rowIndex < end; rowIndex += 1) {
|
||||||
newData[rowIndex] = t.data[itemIndex];
|
newData[rowIndex] = items.data[itemIndex];
|
||||||
itemIndex += 1;
|
itemIndex += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,36 +82,34 @@ export const VirtualInfiniteGrid = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loader.current) {
|
if (loader.current) {
|
||||||
listRef.current.scrollTo(0);
|
listRef.current.scrollTo(0);
|
||||||
loader.current.resetloadMoreItemsCache(true);
|
loader.current.resetloadMoreItemsCache(false);
|
||||||
setItemData(() => []);
|
setItemData(() => []);
|
||||||
|
|
||||||
loadMoreItems(0, minimumBatchSize! * 2);
|
loadMoreItems(0, minimumBatchSize! * 2);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [minimumBatchSize, queryParams, setItemData]);
|
}, [minimumBatchSize, fetchFn]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InfiniteLoader
|
<InfiniteLoader
|
||||||
ref={loader}
|
ref={loader}
|
||||||
isItemLoaded={(index) => isItemLoaded(index)}
|
isItemLoaded={(index) => isItemLoaded(index)}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
loadMoreItems={(startIndex, stopIndex) =>
|
loadMoreItems={debouncedLoadMoreItems}
|
||||||
debouncedLoadMoreItems(startIndex, stopIndex)
|
|
||||||
}
|
|
||||||
minimumBatchSize={minimumBatchSize}
|
minimumBatchSize={minimumBatchSize}
|
||||||
threshold={30}
|
threshold={30}
|
||||||
>
|
>
|
||||||
{({ onItemsRendered, ref: infiniteLoaderRef }) => (
|
{({ onItemsRendered, ref: infiniteLoaderRef }) => (
|
||||||
<VirtualGridWrapper
|
<VirtualGridWrapper
|
||||||
useIsScrolling
|
useIsScrolling
|
||||||
cardControls={cardControls}
|
|
||||||
cardRows={cardRows}
|
cardRows={cardRows}
|
||||||
columnCount={columnCount}
|
columnCount={columnCount}
|
||||||
height={height}
|
height={height}
|
||||||
itemCount={itemCount || 0}
|
itemCount={itemCount || 0}
|
||||||
itemData={itemData}
|
itemData={itemData}
|
||||||
itemGap={itemGap!}
|
itemGap={itemGap}
|
||||||
itemHeight={itemHeight! + itemGap! / 2}
|
itemHeight={itemHeight + itemGap / 2}
|
||||||
|
itemType={itemType}
|
||||||
itemWidth={itemSize}
|
itemWidth={itemSize}
|
||||||
refInstance={(list) => {
|
refInstance={(list) => {
|
||||||
infiniteLoaderRef(list);
|
infiniteLoaderRef(list);
|
||||||
@@ -130,7 +125,5 @@ export const VirtualInfiniteGrid = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
VirtualInfiniteGrid.defaultProps = {
|
VirtualInfiniteGrid.defaultProps = {
|
||||||
itemGap: 10,
|
|
||||||
minimumBatchSize: 20,
|
minimumBatchSize: 20,
|
||||||
queryParams: {},
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user