mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
Add lazy image component
This commit is contained in:
Generated
+16
@@ -45,6 +45,7 @@
|
|||||||
"react-player": "^2.10.0",
|
"react-player": "^2.10.0",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.3.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
|
"react-simple-img": "^3.0.0",
|
||||||
"react-slider": "^2.0.0",
|
"react-slider": "^2.0.0",
|
||||||
"react-virtualized-auto-sizer": "^1.0.6",
|
"react-virtualized-auto-sizer": "^1.0.6",
|
||||||
"react-window": "^1.8.7",
|
"react-window": "^1.8.7",
|
||||||
@@ -17399,6 +17400,15 @@
|
|||||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-simple-img": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-simple-img/-/react-simple-img-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-I0sG/GgY9c+04BgWf1YRlipWBQxR3oG2s/bagU8EO7zals3/Vkfk1PJMeYh/wHfjxJtUmal+y7HWEBm4MzXVsQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16.3.0",
|
||||||
|
"react-dom": ">= 16.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-slider": {
|
"node_modules/react-slider": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.0.tgz",
|
||||||
@@ -36732,6 +36742,12 @@
|
|||||||
"react-is": "^16.12.0 || ^17.0.0 || ^18.0.0"
|
"react-is": "^16.12.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-simple-img": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-simple-img/-/react-simple-img-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-I0sG/GgY9c+04BgWf1YRlipWBQxR3oG2s/bagU8EO7zals3/Vkfk1PJMeYh/wHfjxJtUmal+y7HWEBm4MzXVsQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"react-slider": {
|
"react-slider": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.0.tgz",
|
||||||
|
|||||||
@@ -285,6 +285,7 @@
|
|||||||
"react-player": "^2.10.0",
|
"react-player": "^2.10.0",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.3.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
|
"react-simple-img": "^3.0.0",
|
||||||
"react-slider": "^2.0.0",
|
"react-slider": "^2.0.0",
|
||||||
"react-virtualized-auto-sizer": "^1.0.6",
|
"react-virtualized-auto-sizer": "^1.0.6",
|
||||||
"react-window": "^1.8.7",
|
"react-window": "^1.8.7",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Center, Skeleton } from '@mantine/core';
|
|||||||
import { RiAlbumFill } from 'react-icons/ri';
|
import { RiAlbumFill } from 'react-icons/ri';
|
||||||
import { generatePath, useNavigate } from 'react-router';
|
import { generatePath, useNavigate } from 'react-router';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { SimpleImg, initSimpleImg } from 'react-simple-img';
|
||||||
import { ListChildComponentProps } from 'react-window';
|
import { ListChildComponentProps } from 'react-window';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Text } from '@/renderer/components/text';
|
import { Text } from '@/renderer/components/text';
|
||||||
@@ -15,6 +16,8 @@ import {
|
|||||||
CardRoute,
|
CardRoute,
|
||||||
} from '@/renderer/types';
|
} from '@/renderer/types';
|
||||||
|
|
||||||
|
initSimpleImg({ threshold: 0.5 }, true);
|
||||||
|
|
||||||
const CardWrapper = styled.div<{
|
const CardWrapper = styled.div<{
|
||||||
itemGap: number;
|
itemGap: number;
|
||||||
itemHeight: number;
|
itemHeight: number;
|
||||||
@@ -87,14 +90,7 @@ const ImageSection = styled.div<{ size?: number }>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface ImageProps {
|
const Image = styled(SimpleImg)`
|
||||||
height: number;
|
|
||||||
isLoading?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Image = styled.img<ImageProps>`
|
|
||||||
width: ${({ height }) => `${height - 24}px`};
|
|
||||||
height: ${({ height }) => `${height - 24}px`};
|
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: var(--card-default-radius);
|
border-radius: var(--card-default-radius);
|
||||||
@@ -127,6 +123,7 @@ const Row = styled.div<{ $secondary?: boolean }>`
|
|||||||
$secondary ? 'var(--main-fg-secondary)' : 'var(--main-fg)'};
|
$secondary ? 'var(--main-fg-secondary)' : 'var(--main-fg)'};
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
user-select: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface BaseGridCardProps {
|
interface BaseGridCardProps {
|
||||||
@@ -136,7 +133,7 @@ interface BaseGridCardProps {
|
|||||||
cardRows: CardRow[];
|
cardRows: CardRow[];
|
||||||
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
route?: CardRoute;
|
route: CardRoute;
|
||||||
};
|
};
|
||||||
data: any;
|
data: any;
|
||||||
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
||||||
@@ -161,161 +158,37 @@ export const DefaultCard = ({
|
|||||||
controls;
|
controls;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
if (route) {
|
|
||||||
return (
|
|
||||||
<CardWrapper
|
|
||||||
key={`card-${columnIndex}-${index}`}
|
|
||||||
link
|
|
||||||
itemGap={itemGap}
|
|
||||||
itemHeight={itemHeight}
|
|
||||||
itemWidth={itemWidth}
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
generatePath(
|
|
||||||
route.route,
|
|
||||||
route.slugs?.reduce((acc, slug) => {
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[slug.slugProperty]: data[slug.idProperty],
|
|
||||||
};
|
|
||||||
}, {})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StyledCard>
|
|
||||||
<ImageSection size={itemWidth}>
|
|
||||||
{data?.imageUrl ? (
|
|
||||||
<Image height={itemWidth} src={data?.imageUrl} />
|
|
||||||
) : (
|
|
||||||
<Center
|
|
||||||
sx={{
|
|
||||||
background: 'var(--placeholder-bg)',
|
|
||||||
borderRadius: 'var(--card-default-radius)',
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RiAlbumFill color="var(--placeholder-fg)" size={35} />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
<ControlsContainer>
|
|
||||||
{!isScrolling && (
|
|
||||||
<GridCardControls
|
|
||||||
cardControls={cardControls}
|
|
||||||
handlePlayQueueAdd={handlePlayQueueAdd}
|
|
||||||
itemData={data}
|
|
||||||
itemType={itemType}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ControlsContainer>
|
|
||||||
</ImageSection>
|
|
||||||
<DetailSection>
|
|
||||||
{cardRows.map((row: CardRow, index: number) => {
|
|
||||||
if (row.arrayProperty && row.route) {
|
|
||||||
return (
|
|
||||||
<Row
|
|
||||||
key={`row-${row.property}-${columnIndex}`}
|
|
||||||
$secondary={index > 0}
|
|
||||||
>
|
|
||||||
{data[row.property].map(
|
|
||||||
(item: any, itemIndex: number) => (
|
|
||||||
<React.Fragment key={`${data.id}-${item.id}`}>
|
|
||||||
{itemIndex > 0 && (
|
|
||||||
<Text
|
|
||||||
sx={{
|
|
||||||
display: 'inline-block',
|
|
||||||
padding: '0 2px 0 1px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
,
|
|
||||||
</Text>
|
|
||||||
)}{' '}
|
|
||||||
<Text
|
|
||||||
$link
|
|
||||||
$secondary={index > 0}
|
|
||||||
component={Link}
|
|
||||||
overflow="hidden"
|
|
||||||
to={generatePath(
|
|
||||||
row.route!.route,
|
|
||||||
row.route!.slugs?.reduce((acc, slug) => {
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[slug.slugProperty]: data[slug.idProperty],
|
|
||||||
};
|
|
||||||
}, {})
|
|
||||||
)}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{row.arrayProperty && item[row.arrayProperty]}
|
|
||||||
</Text>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.arrayProperty) {
|
|
||||||
return (
|
|
||||||
<Row key={`row-${row.property}-${columnIndex}`}>
|
|
||||||
{data[row.property].map((item: any) => (
|
|
||||||
<Text
|
|
||||||
key={`${data.id}-${item.id}`}
|
|
||||||
$secondary={index > 0}
|
|
||||||
overflow="hidden"
|
|
||||||
>
|
|
||||||
{row.arrayProperty && item[row.arrayProperty]}
|
|
||||||
</Text>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row key={`row-${row.property}-${columnIndex}`}>
|
|
||||||
{row.route ? (
|
|
||||||
<Text
|
|
||||||
$link
|
|
||||||
component={Link}
|
|
||||||
overflow="hidden"
|
|
||||||
to={generatePath(
|
|
||||||
row.route.route,
|
|
||||||
row.route.slugs?.reduce((acc, slug) => {
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[slug.slugProperty]: data[slug.idProperty],
|
|
||||||
};
|
|
||||||
}, {})
|
|
||||||
)}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{data && data[row.property]}
|
|
||||||
</Text>
|
|
||||||
) : (
|
|
||||||
<Text $secondary={index > 0} overflow="hidden">
|
|
||||||
{data && data[row.property]}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</DetailSection>
|
|
||||||
</StyledCard>
|
|
||||||
</CardWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<CardWrapper
|
<CardWrapper
|
||||||
key={`card-${columnIndex}-${index}`}
|
key={`card-${columnIndex}-${index}`}
|
||||||
|
link
|
||||||
itemGap={itemGap}
|
itemGap={itemGap}
|
||||||
itemHeight={itemHeight}
|
itemHeight={itemHeight}
|
||||||
itemWidth={itemWidth}
|
itemWidth={itemWidth}
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
generatePath(
|
||||||
|
route.route,
|
||||||
|
route.slugs?.reduce((acc, slug) => {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[slug.slugProperty]: data[slug.idProperty],
|
||||||
|
};
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<StyledCard>
|
<StyledCard>
|
||||||
<ImageSection size={itemWidth}>
|
<ImageSection size={itemWidth}>
|
||||||
{data?.imageUrl ? (
|
{data?.imageUrl ? (
|
||||||
<Image height={itemWidth} src={data?.imageUrl} />
|
<Image
|
||||||
|
animationDuration={0.5}
|
||||||
|
height={itemWidth - 24}
|
||||||
|
placeholder="var(--card-default-bg)"
|
||||||
|
src={data?.imageUrl}
|
||||||
|
width={itemWidth - 24}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Center
|
<Center
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { Center, Skeleton } from '@mantine/core';
|
|||||||
import { RiAlbumFill } from 'react-icons/ri';
|
import { RiAlbumFill } from 'react-icons/ri';
|
||||||
import { generatePath } from 'react-router';
|
import { generatePath } from 'react-router';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { SimpleImg, initSimpleImg } from 'react-simple-img';
|
||||||
import { ListChildComponentProps } from 'react-window';
|
import { ListChildComponentProps } from 'react-window';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Text } from '@/renderer/components/text';
|
import { Text } from '@/renderer/components/text';
|
||||||
import { GridCardControls } from '@/renderer/components/virtual-grid/grid-card/grid-card-controls';
|
import { GridCardControls } from '@/renderer/components/virtual-grid/grid-card/grid-card-controls';
|
||||||
import { fadeIn } from '@/renderer/styles';
|
|
||||||
import {
|
import {
|
||||||
PlayQueueAddOptions,
|
PlayQueueAddOptions,
|
||||||
LibraryItem,
|
LibraryItem,
|
||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
CardRoute,
|
CardRoute,
|
||||||
} from '@/renderer/types';
|
} from '@/renderer/types';
|
||||||
|
|
||||||
|
initSimpleImg({ threshold: 0.5 }, true);
|
||||||
|
|
||||||
const CardWrapper = styled.div<{
|
const CardWrapper = styled.div<{
|
||||||
itemGap: number;
|
itemGap: number;
|
||||||
itemHeight: number;
|
itemHeight: number;
|
||||||
@@ -87,15 +89,10 @@ interface ImageProps {
|
|||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Image = styled.img<ImageProps>`
|
const Image = styled(SimpleImg)<ImageProps>`
|
||||||
width: ${({ height }) => `${height}px`};
|
|
||||||
height: ${({ height }) => `${height}px`};
|
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: var(--card-poster-radius);
|
border-radius: var(--card-poster-radius);
|
||||||
|
|
||||||
${fadeIn}
|
|
||||||
animation: fadein 0.3s ease-in-out;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ControlsContainer = styled.div`
|
const ControlsContainer = styled.div`
|
||||||
@@ -122,6 +119,7 @@ const Row = styled.div<{ $secondary?: boolean }>`
|
|||||||
$secondary ? 'var(--main-fg-secondary)' : 'var(--main-fg)'};
|
$secondary ? 'var(--main-fg-secondary)' : 'var(--main-fg)'};
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
user-select: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface BaseGridCardProps {
|
interface BaseGridCardProps {
|
||||||
@@ -131,7 +129,7 @@ interface BaseGridCardProps {
|
|||||||
cardRows: CardRow[];
|
cardRows: CardRow[];
|
||||||
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
route?: CardRoute;
|
route: CardRoute;
|
||||||
};
|
};
|
||||||
data: any;
|
data: any;
|
||||||
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
||||||
@@ -163,49 +161,27 @@ export const PosterCard = ({
|
|||||||
itemWidth={itemWidth}
|
itemWidth={itemWidth}
|
||||||
>
|
>
|
||||||
<StyledCard>
|
<StyledCard>
|
||||||
{route ? (
|
<Link
|
||||||
<Link
|
tabIndex={0}
|
||||||
tabIndex={0}
|
to={generatePath(
|
||||||
to={generatePath(
|
route.route,
|
||||||
route.route,
|
route.slugs?.reduce((acc, slug) => {
|
||||||
route.slugs?.reduce((acc, slug) => {
|
return {
|
||||||
return {
|
...acc,
|
||||||
...acc,
|
[slug.slugProperty]: data[slug.idProperty],
|
||||||
[slug.slugProperty]: data[slug.idProperty],
|
};
|
||||||
};
|
}, {})
|
||||||
}, {})
|
)}
|
||||||
)}
|
>
|
||||||
>
|
|
||||||
<ImageSection style={{ height: `${itemWidth}px` }}>
|
|
||||||
{data?.imageUrl ? (
|
|
||||||
<Image height={itemWidth} src={data?.imageUrl} />
|
|
||||||
) : (
|
|
||||||
<Center
|
|
||||||
sx={{
|
|
||||||
background: 'var(--placeholder-bg)',
|
|
||||||
borderRadius: 'var(--card-poster-radius)',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RiAlbumFill color="var(--placeholder-fg)" size={35} />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
<ControlsContainer>
|
|
||||||
{!isScrolling && (
|
|
||||||
<GridCardControls
|
|
||||||
cardControls={cardControls}
|
|
||||||
handlePlayQueueAdd={handlePlayQueueAdd}
|
|
||||||
itemData={data}
|
|
||||||
itemType={itemType}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ControlsContainer>
|
|
||||||
</ImageSection>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<ImageSection style={{ height: `${itemWidth}px` }}>
|
<ImageSection style={{ height: `${itemWidth}px` }}>
|
||||||
{data?.imageUrl ? (
|
{data?.imageUrl ? (
|
||||||
<Image height={itemWidth} src={data?.imageUrl} />
|
<Image
|
||||||
|
animationDuration={0.5}
|
||||||
|
height={itemWidth}
|
||||||
|
placeholder="var(--card-default-bg)"
|
||||||
|
src={data?.imageUrl}
|
||||||
|
width={itemWidth}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Center
|
<Center
|
||||||
sx={{
|
sx={{
|
||||||
@@ -228,7 +204,7 @@ export const PosterCard = ({
|
|||||||
)}
|
)}
|
||||||
</ControlsContainer>
|
</ControlsContainer>
|
||||||
</ImageSection>
|
</ImageSection>
|
||||||
)}
|
</Link>
|
||||||
<DetailSection>
|
<DetailSection>
|
||||||
{cardRows.map((row: CardRow, index: number) => {
|
{cardRows.map((row: CardRow, index: number) => {
|
||||||
if (row.arrayProperty && row.route) {
|
if (row.arrayProperty && row.route) {
|
||||||
|
|||||||
Reference in New Issue
Block a user