add item card skeleton

This commit is contained in:
jeffvli
2025-09-26 10:44:03 -07:00
parent 81bf610912
commit fa13e4afc6
2 changed files with 124 additions and 57 deletions
@@ -67,7 +67,6 @@
max-width: 100%; max-width: 100%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
line-height: 1.6;
white-space: nowrap; white-space: nowrap;
a { a {
+84 -16
View File
@@ -8,6 +8,7 @@ import styles from './item-card.module.css';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { Image } from '/@/shared/components/image/image'; import { Image } from '/@/shared/components/image/image';
import { Separator } from '/@/shared/components/separator/separator'; import { Separator } from '/@/shared/components/separator/separator';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Text } from '/@/shared/components/text/text'; import { Text } from '/@/shared/components/text/text';
import { import {
Album, Album,
@@ -31,8 +32,9 @@ type DataRow = {
}; };
interface ItemCardProps { interface ItemCardProps {
data: Album | AlbumArtist | Artist | Playlist | Song; data: Album | AlbumArtist | Artist | Playlist | Song | undefined;
isRound?: boolean; isRound?: boolean;
itemType: LibraryItem;
onClick?: () => void; onClick?: () => void;
onItemExpand?: () => void; onItemExpand?: () => void;
onItemSelect?: () => void; onItemSelect?: () => void;
@@ -43,17 +45,18 @@ interface ItemCardProps {
export const ItemCard = ({ export const ItemCard = ({
data, data,
isRound, isRound,
itemType,
onClick, onClick,
onItemExpand, onItemExpand,
onItemSelect, onItemSelect,
type = 'poster', type = 'poster',
withControls, withControls,
}: ItemCardProps) => { }: ItemCardProps) => {
const imageUrl = getImageUrl(data);
const rows = getDataRows(data);
const [showControls, setShowControls] = useState(false); const [showControls, setShowControls] = useState(false);
const imageUrl = getImageUrl(data);
const rows = getDataRows(itemType);
switch (type) { switch (type) {
case 'compact': case 'compact':
return ( return (
@@ -61,6 +64,7 @@ export const ItemCard = ({
data={data} data={data}
imageUrl={imageUrl} imageUrl={imageUrl}
isRound={isRound} isRound={isRound}
itemType={itemType}
onClick={onClick} onClick={onClick}
onItemExpand={onItemExpand} onItemExpand={onItemExpand}
onItemSelect={onItemSelect} onItemSelect={onItemSelect}
@@ -76,6 +80,7 @@ export const ItemCard = ({
data={data} data={data}
imageUrl={imageUrl} imageUrl={imageUrl}
isRound={isRound} isRound={isRound}
itemType={itemType}
onClick={onClick} onClick={onClick}
onItemExpand={onItemExpand} onItemExpand={onItemExpand}
onItemSelect={onItemSelect} onItemSelect={onItemSelect}
@@ -92,6 +97,7 @@ export const ItemCard = ({
data={data} data={data}
imageUrl={imageUrl} imageUrl={imageUrl}
isRound={isRound} isRound={isRound}
itemType={itemType}
onClick={onClick} onClick={onClick}
onItemExpand={onItemExpand} onItemExpand={onItemExpand}
onItemSelect={onItemSelect} onItemSelect={onItemSelect}
@@ -123,6 +129,7 @@ const CompactItemCard = ({
showControls, showControls,
withControls, withControls,
}: ItemCardDerivativeProps) => { }: ItemCardDerivativeProps) => {
if (data) {
return ( return (
<div className={clsx(styles.container, styles.compact)}> <div className={clsx(styles.container, styles.compact)}>
<div <div
@@ -140,7 +147,23 @@ const CompactItemCard = ({
</AnimatePresence> </AnimatePresence>
<div className={clsx(styles.detailContainer, styles.compact)}> <div className={clsx(styles.detailContainer, styles.compact)}>
{rows.map((row) => ( {rows.map((row) => (
<ItemCardRow data={data} key={row.id} row={row} type="compact" /> <ItemCardRow data={data!} key={row.id} row={row} type="compact" />
))}
</div>
</div>
</div>
);
}
return (
<div className={clsx(styles.container, styles.compact)}>
<div className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}>
<Skeleton className={styles.image} enableAnimation={false} />
<div className={clsx(styles.detailContainer, styles.compact)}>
{rows.map((row) => (
<div className={styles.row} key={row.id}>
&nbsp;
</div>
))} ))}
</div> </div>
</div> </div>
@@ -160,6 +183,7 @@ const DefaultItemCard = ({
showControls, showControls,
withControls, withControls,
}: ItemCardDerivativeProps) => { }: ItemCardDerivativeProps) => {
if (data) {
return ( return (
<div className={clsx(styles.container)}> <div className={clsx(styles.container)}>
<div <div
@@ -179,9 +203,23 @@ const DefaultItemCard = ({
</div> </div>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
{rows.map((row) => ( {rows.map((row) => (
<Fragment key={row.id}> <ItemCardRow data={data!} key={row.id} row={row} type="default" />
<ItemCardRow data={data} row={row} type="default" /> ))}
</Fragment> </div>
</div>
);
}
return (
<div className={clsx(styles.container)}>
<div className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}>
<Skeleton className={styles.image} enableAnimation={false} />
</div>
<div className={styles.detailContainer}>
{rows.map((row) => (
<div className={styles.row} key={row.id}>
&nbsp;
</div>
))} ))}
</div> </div>
</div> </div>
@@ -200,6 +238,7 @@ const PosterItemCard = ({
showControls, showControls,
withControls, withControls,
}: ItemCardDerivativeProps) => { }: ItemCardDerivativeProps) => {
if (data) {
return ( return (
<div className={clsx(styles.container, styles.poster)}> <div className={clsx(styles.container, styles.poster)}>
<div <div
@@ -218,17 +257,29 @@ const PosterItemCard = ({
</div> </div>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
{rows.map((row) => ( {rows.map((row) => (
<Fragment key={row.id}> <ItemCardRow data={data!} key={row.id} row={row} type="poster" />
<ItemCardRow data={data} row={row} type="poster" /> ))}
</Fragment> </div>
</div>
);
}
return (
<div className={clsx(styles.container, styles.poster)}>
<div className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}>
<Skeleton className={clsx(styles.image, { [styles.isRound]: isRound })} />
</div>
<div className={styles.detailContainer}>
{rows.map((row) => (
<ItemCardRow data={undefined} key={row.id} row={row} type="poster" />
))} ))}
</div> </div>
</div> </div>
); );
}; };
const getDataRows = (data: Album | AlbumArtist | Artist | Playlist | Song): DataRow[] => { const getDataRows = (itemType: LibraryItem): DataRow[] => {
switch (data.itemType) { switch (itemType) {
case LibraryItem.ALBUM: case LibraryItem.ALBUM:
return [ return [
{ {
@@ -274,11 +325,13 @@ const getDataRows = (data: Album | AlbumArtist | Artist | Playlist | Song): Data
return [{ format: (data) => (data as Playlist).name, id: 'name' }]; return [{ format: (data) => (data as Playlist).name, id: 'name' }];
case LibraryItem.SONG: case LibraryItem.SONG:
return [{ format: (data) => (data as Song).name, id: 'name' }]; return [{ format: (data) => (data as Song).name, id: 'name' }];
default:
return [];
} }
}; };
const getImageUrl = (data: Album | AlbumArtist | Artist | Playlist | Song) => { const getImageUrl = (data: Album | AlbumArtist | Artist | Playlist | Song | undefined) => {
if ('imageUrl' in data) { if (data && 'imageUrl' in data) {
return data.imageUrl || undefined; return data.imageUrl || undefined;
} }
@@ -290,10 +343,25 @@ const ItemCardRow = ({
row, row,
type, type,
}: { }: {
data: Album | AlbumArtist | Artist | Playlist | Song; data: Album | AlbumArtist | Artist | Playlist | Song | undefined;
row: DataRow; row: DataRow;
type?: 'compact' | 'default' | 'poster'; type?: 'compact' | 'default' | 'poster';
}) => { }) => {
if (!data) {
return (
<div
className={clsx(styles.row, {
[styles.compact]: type === 'compact',
[styles.default]: type === 'default',
[styles.muted]: row.isMuted,
[styles.poster]: type === 'poster',
})}
>
&nbsp;
</div>
);
}
return ( return (
<Text <Text
className={clsx(styles.row, { className={clsx(styles.row, {