optimize various base components

This commit is contained in:
jeffvli
2026-01-02 12:46:35 -08:00
parent a66c67e86d
commit d06d1674d1
31 changed files with 669 additions and 393 deletions
+171 -115
View File
@@ -1,6 +1,6 @@
import clsx from 'clsx';
import { AnimatePresence } from 'motion/react';
import { Fragment, memo, ReactNode, useState } from 'react';
import { Fragment, memo, ReactNode, useCallback, useMemo, useState } from 'react';
import { generatePath, Link } from 'react-router';
import styles from './item-card.module.css';
@@ -84,7 +84,7 @@ export const ItemCard = ({
switch (type) {
case 'compact':
return (
<CompactItemCard
<MemoizedCompactItemCard
controls={controls}
data={data}
enableDrag={enableDrag}
@@ -101,7 +101,7 @@ export const ItemCard = ({
);
case 'poster':
return (
<PosterItemCard
<MemoizedPosterItemCard
controls={controls}
data={data}
enableDrag={enableDrag}
@@ -119,7 +119,7 @@ export const ItemCard = ({
case 'default':
default:
return (
<DefaultItemCard
<MemoizedDefaultItemCard
controls={controls}
data={data}
enableDrag={enableDrag}
@@ -167,46 +167,64 @@ const CompactItemCard = ({
: undefined;
const isSelected = useItemSelectionState(internalState, itemRowId || undefined);
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
drag: {
getId: () => {
if (!data) {
return [];
}
const getId = useCallback(() => {
if (!data) {
return [];
}
const draggedItems = getDraggedItems(data, internalState);
return draggedItems.map((item) => item.id);
},
getItem: () => {
if (!data) {
return [];
}
const draggedItems = getDraggedItems(data, internalState);
return draggedItems.map((item) => item.id);
}, [data, internalState]);
const draggedItems = getDraggedItems(data, internalState);
return draggedItems;
},
const getItem = useCallback(() => {
if (!data) {
return [];
}
const draggedItems = getDraggedItems(data, internalState);
return draggedItems;
}, [data, internalState]);
const onDragStart = useCallback(() => {
if (!data) {
return;
}
const draggedItems = getDraggedItems(data, internalState);
if (internalState) {
internalState.setDragging(draggedItems);
}
}, [data, internalState]);
const onDrop = useCallback(() => {
if (internalState) {
internalState.setDragging([]);
}
}, [internalState]);
const dragOperation = useMemo(
() =>
itemType === LibraryItem.QUEUE_SONG
? [DragOperation.REORDER, DragOperation.ADD]
: [DragOperation.ADD],
[itemType],
);
const drag = useMemo(
() => ({
getId,
getItem,
itemType,
onDragStart: () => {
if (!data) {
return;
}
const draggedItems = getDraggedItems(data, internalState);
if (internalState) {
internalState.setDragging(draggedItems);
}
},
onDrop: () => {
if (internalState) {
internalState.setDragging([]);
}
},
operation:
itemType === LibraryItem.QUEUE_SONG
? [DragOperation.REORDER, DragOperation.ADD]
: [DragOperation.ADD],
onDragStart,
onDrop,
operation: dragOperation,
target: DragTarget.ALBUM,
},
}),
[getId, getItem, itemType, onDragStart, onDrop, dragOperation],
);
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
drag,
isEnabled: !!enableDrag && !!data,
});
@@ -649,46 +667,64 @@ const PosterItemCard = ({
: undefined;
const isSelected = useItemSelectionState(internalState, itemRowId || undefined);
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
drag: {
getId: () => {
if (!data) {
return [];
}
const getId = useCallback(() => {
if (!data) {
return [];
}
const draggedItems = getDraggedItems(data, internalState);
return draggedItems.map((item) => item.id);
},
getItem: () => {
if (!data) {
return [];
}
const draggedItems = getDraggedItems(data, internalState);
return draggedItems.map((item) => item.id);
}, [data, internalState]);
const draggedItems = getDraggedItems(data, internalState);
return draggedItems;
},
const getItem = useCallback(() => {
if (!data) {
return [];
}
const draggedItems = getDraggedItems(data, internalState);
return draggedItems;
}, [data, internalState]);
const onDragStart = useCallback(() => {
if (!data) {
return;
}
const draggedItems = getDraggedItems(data, internalState);
if (internalState) {
internalState.setDragging(draggedItems);
}
}, [data, internalState]);
const onDrop = useCallback(() => {
if (internalState) {
internalState.setDragging([]);
}
}, [internalState]);
const dragOperation = useMemo(
() =>
itemType === LibraryItem.QUEUE_SONG
? [DragOperation.REORDER, DragOperation.ADD]
: [DragOperation.ADD],
[itemType],
);
const drag = useMemo(
() => ({
getId,
getItem,
itemType,
onDragStart: () => {
if (!data) {
return;
}
const draggedItems = getDraggedItems(data, internalState);
if (internalState) {
internalState.setDragging(draggedItems);
}
},
onDrop: () => {
if (internalState) {
internalState.setDragging([]);
}
},
operation:
itemType === LibraryItem.QUEUE_SONG
? [DragOperation.REORDER, DragOperation.ADD]
: [DragOperation.ADD],
onDragStart,
onDrop,
operation: dragOperation,
target: DragTarget.ALBUM,
},
}),
[getId, getItem, itemType, onDragStart, onDrop, dragOperation],
);
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
drag,
isEnabled: !!enableDrag && !!data,
});
@@ -896,6 +932,15 @@ const PosterItemCard = ({
);
};
const MemoizedPosterItemCard = memo(PosterItemCard);
MemoizedPosterItemCard.displayName = 'MemoizedPosterItemCard';
const MemoizedCompactItemCard = memo(CompactItemCard);
MemoizedCompactItemCard.displayName = 'MemoizedCompactItemCard';
const MemoizedDefaultItemCard = memo(DefaultItemCard);
MemoizedDefaultItemCard.displayName = 'MemoizedDefaultItemCard';
export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[] => {
return [
{
@@ -1160,56 +1205,67 @@ const getItemNavigationPath = (
return getTitlePath(effectiveItemType, data.id);
};
const ItemCardRow = ({
data,
index,
row,
type,
}: {
data: Album | AlbumArtist | Artist | Playlist | Song | undefined;
index: number;
row: DataRow;
type?: 'compact' | 'default' | 'poster';
}) => {
const alignmentClass =
row.align === 'center'
? styles['align-center']
: row.align === 'end'
? styles['align-end']
: styles['align-start'];
const ItemCardRow = memo(
({
data,
index,
row,
type,
}: {
data: Album | AlbumArtist | Artist | Playlist | Song | undefined;
index: number;
row: DataRow;
type?: 'compact' | 'default' | 'poster';
}) => {
const alignmentClass =
row.align === 'center'
? styles['align-center']
: row.align === 'end'
? styles['align-end']
: styles['align-start'];
// All rows except the first one (index 0) should be muted
const isMuted = index > 0 || row.isMuted;
// All rows except the first one (index 0) should be muted
const isMuted = index > 0 || row.isMuted;
const formattedContent = useMemo(() => {
if (!data) {
return null;
}
return row.format(data);
}, [data, row]);
if (!data) {
return (
<div
className={clsx(styles.row, alignmentClass, {
[styles.compact]: type === 'compact',
[styles.default]: type === 'default',
[styles.muted]: isMuted,
[styles.poster]: type === 'poster',
})}
>
&nbsp;
</div>
);
}
if (!data) {
return (
<div
<Text
className={clsx(styles.row, alignmentClass, {
[styles.bold]: index === 0,
[styles.compact]: type === 'compact',
[styles.default]: type === 'default',
[styles.muted]: isMuted,
[styles.poster]: type === 'poster',
})}
size={index > 0 ? 'sm' : 'md'}
>
&nbsp;
</div>
{formattedContent}
</Text>
);
}
},
);
return (
<Text
className={clsx(styles.row, alignmentClass, {
[styles.bold]: index === 0,
[styles.compact]: type === 'compact',
[styles.default]: type === 'default',
[styles.muted]: isMuted,
[styles.poster]: type === 'poster',
})}
size={index > 0 ? 'sm' : 'md'}
>
{row.format(data)}
</Text>
);
};
ItemCardRow.displayName = 'ItemCardRow';
export const MemoizedItemCard = memo(ItemCard);