From c7c15d917a9f415c15415df985231baabb64d156 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Tue, 7 Apr 2026 11:43:01 -0700 Subject: [PATCH] isolate item card control renders --- .../components/item-card/item-card.tsx | 642 ++++++++++-------- 1 file changed, 340 insertions(+), 302 deletions(-) diff --git a/src/renderer/components/item-card/item-card.tsx b/src/renderer/components/item-card/item-card.tsx index aecb874bf..7c3addab9 100644 --- a/src/renderer/components/item-card/item-card.tsx +++ b/src/renderer/components/item-card/item-card.tsx @@ -169,6 +169,292 @@ export interface ItemCardDerivativeProps extends Omit { showRating: boolean; } +type ItemCardData = NonNullable; + +const ItemCardStandardImageArea = memo(function ItemCardStandardImageArea({ + controls, + data, + enableExpansion, + enableImageViewport = true, + enableNavigation, + handleContextMenu, + handleImageClick, + handleLinkDragStart, + imageAsLink, + imageFetchPriority, + internalState, + isRound, + itemType, + navigationPath, + showRating, + variant, + withControls, +}: { + controls?: ItemControls; + data: ItemCardData; + enableExpansion?: boolean; + enableImageViewport?: boolean; + enableNavigation?: boolean; + handleContextMenu: (e: React.MouseEvent) => void; + handleImageClick: (e: React.MouseEvent) => void; + handleLinkDragStart: (e: React.DragEvent) => void; + imageAsLink?: boolean; + imageFetchPriority?: 'auto' | 'high' | 'low'; + internalState?: ItemListStateActions; + isRound?: boolean; + itemType: LibraryItem; + navigationPath: null | string; + showRating: boolean; + variant: 'default' | 'poster'; + withControls?: boolean; +}) { + const [showControls, setShowControls] = useState(false); + + const handleMouseEnter = () => { + if (withControls) { + setShowControls(true); + } + }; + + const handleMouseLeave = () => { + if (withControls) { + setShowControls(false); + } + }; + + const imageContainerClassName = clsx(styles.imageContainer, { + [styles.isRound]: isRound, + }); + + const isFavorite = 'userFavorite' in data && (data as { userFavorite: boolean }).userFavorite; + const userRating = + 'userRating' in data && + typeof (data as { userRating: null | number }).userRating === 'number' + ? (data as { userRating: null | number }).userRating + : null; + const hasRating = showRating && userRating !== null && userRating > 0; + + const imageContainerContent = ( + <> + {itemType === LibraryItem.GENRE && + data && + 'name' in data && + typeof (data as Genre).name === 'string' ? ( + + ) : ( + + )} + {isFavorite &&
} + {hasRating &&
{userRating}
} + + {withControls && showControls && ( + + )} + + + ); + + return enableNavigation && navigationPath && (imageAsLink ?? !internalState) ? ( + + {imageContainerContent} + + ) : ( +
+ {imageContainerContent} +
+ ); +}); + +ItemCardStandardImageArea.displayName = 'ItemCardStandardImageArea'; + +const CompactItemCardImageArea = memo(function CompactItemCardImageArea({ + controls, + data, + enableExpansion, + enableNavigation, + handleContextMenu, + handleImageClick, + handleLinkDragStart, + imageAsLink, + imageFetchPriority, + internalState, + isRound, + itemType, + navigationPath, + rows, + showRating, + withControls, +}: { + controls?: ItemControls; + data: ItemCardData; + enableExpansion?: boolean; + enableNavigation?: boolean; + handleContextMenu: (e: React.MouseEvent) => void; + handleImageClick: (e: React.MouseEvent) => void; + handleLinkDragStart: (e: React.DragEvent) => void; + imageAsLink?: boolean; + imageFetchPriority?: 'auto' | 'high' | 'low'; + internalState?: ItemListStateActions; + isRound?: boolean; + itemType: LibraryItem; + navigationPath: null | string; + rows: DataRow[]; + showRating: boolean; + withControls?: boolean; +}) { + const [showControls, setShowControls] = useState(false); + + const handleMouseEnter = () => { + if (withControls) { + setShowControls(true); + } + }; + + const handleMouseLeave = () => { + if (withControls) { + setShowControls(false); + } + }; + + const imageContainerClassName = clsx(styles.imageContainer, { + [styles.isRound]: isRound, + }); + + const isFavorite = 'userFavorite' in data && (data as { userFavorite: boolean }).userFavorite; + const userRating = + 'userRating' in data && + typeof (data as { userRating: null | number }).userRating === 'number' + ? (data as { userRating: null | number }).userRating + : null; + const hasRating = showRating && userRating !== null && userRating > 0; + + const imageContainerContent = ( + <> + {itemType === LibraryItem.GENRE && + data && + 'name' in data && + typeof (data as Genre).name === 'string' ? ( + + ) : ( + + )} + {isFavorite &&
} + {hasRating &&
{userRating}
} + + {withControls && showControls && data && ( + + )} + +
+ {rows + .filter( + (row): row is NonNullable => row !== null && row !== undefined, + ) + .map((row, index) => ( + + ))} +
+ + ); + + return enableNavigation && navigationPath && (imageAsLink ?? !internalState) ? ( + + {imageContainerContent} + + ) : ( +
+ {imageContainerContent} +
+ ); +}); + +CompactItemCardImageArea.displayName = 'CompactItemCardImageArea'; + const CompactItemCard = ({ controls, data, @@ -185,7 +471,6 @@ const CompactItemCard = ({ showRating, withControls, }: ItemCardDerivativeProps) => { - const [showControls, setShowControls] = useState(false); const itemRowId = data && internalState && typeof data === 'object' && 'id' in data ? internalState.extractRowId(data) @@ -297,18 +582,6 @@ const CompactItemCard = ({ if (data) { const navigationPath = getItemNavigationPath(data, itemType); - const handleMouseEnter = () => { - if (withControls) { - setShowControls(true); - } - }; - - const handleMouseLeave = () => { - if (withControls) { - setShowControls(false); - } - }; - const handleContextMenu = (e: React.MouseEvent) => { if (!data || !controls) { return; @@ -338,81 +611,6 @@ const CompactItemCard = ({ e.stopPropagation(); }; - const isFavorite = - 'userFavorite' in data && (data as { userFavorite: boolean }).userFavorite; - const userRating = - 'userRating' in data && - typeof (data as { userRating: null | number }).userRating === 'number' - ? (data as { userRating: null | number }).userRating - : null; - const hasRating = showRating && userRating !== null && userRating > 0; - - const imageContainerClassName = clsx(styles.imageContainer, { - [styles.isRound]: isRound, - }); - - const imageContainerContent = ( - <> - {itemType === LibraryItem.GENRE && - data && - 'name' in data && - typeof (data as Genre).name === 'string' ? ( - - ) : ( - - )} - {isFavorite &&
} - {hasRating &&
{userRating}
} - - {withControls && showControls && data && ( - - )} - -
- {rows - .filter( - (row): row is NonNullable => - row !== null && row !== undefined, - ) - .map((row, index) => ( - - ))} -
- - ); - return (
- {enableNavigation && navigationPath && (imageAsLink ?? !internalState) ? ( - - {imageContainerContent} - - ) : ( -
- {imageContainerContent} -
- )} +
); } @@ -491,7 +682,6 @@ const DefaultItemCard = ({ showRating, withControls, }: ItemCardDerivativeProps) => { - const [showControls, setShowControls] = useState(false); const itemRowId = data && internalState && typeof data === 'object' && 'id' in data ? internalState.extractRowId(data) @@ -538,18 +728,6 @@ const DefaultItemCard = ({ if (data) { const navigationPath = getItemNavigationPath(data, itemType); - const handleMouseEnter = () => { - if (withControls) { - setShowControls(true); - } - }; - - const handleMouseLeave = () => { - if (withControls) { - setShowControls(false); - } - }; - const handleContextMenu = (e: React.MouseEvent) => { if (!data || !controls) { return; @@ -579,93 +757,30 @@ const DefaultItemCard = ({ e.stopPropagation(); }; - const imageContainerClassName = clsx(styles.imageContainer, { - [styles.isRound]: isRound, - }); - - const isFavorite = - 'userFavorite' in data && (data as { userFavorite: boolean }).userFavorite; - const userRating = - 'userRating' in data && - typeof (data as { userRating: null | number }).userRating === 'number' - ? (data as { userRating: null | number }).userRating - : null; - const hasRating = showRating && userRating !== null && userRating > 0; - - const imageContainerContent = ( - <> - {itemType === LibraryItem.GENRE && - data && - 'name' in data && - typeof (data as Genre).name === 'string' ? ( - - ) : ( - - )} - {isFavorite &&
} - {hasRating &&
{userRating}
} - - {withControls && showControls && ( - - )} - - - ); - return (
- {enableNavigation && navigationPath && (imageAsLink ?? !internalState) ? ( - - {imageContainerContent} - - ) : ( -
- {imageContainerContent} -
- )} +
{rows .filter( @@ -728,7 +843,6 @@ const PosterItemCard = ({ showRating, withControls, }: ItemCardDerivativeProps) => { - const [showControls, setShowControls] = useState(false); const itemRowId = data && internalState && typeof data === 'object' && 'id' in data ? internalState.extractRowId(data) @@ -840,18 +954,6 @@ const PosterItemCard = ({ if (data) { const navigationPath = getItemNavigationPath(data, itemType); - const handleMouseEnter = () => { - if (withControls) { - setShowControls(true); - } - }; - - const handleMouseLeave = () => { - if (withControls) { - setShowControls(false); - } - }; - const handleContextMenu = (e: React.MouseEvent) => { if (!data || !controls) { return; @@ -881,63 +983,6 @@ const PosterItemCard = ({ e.stopPropagation(); }; - const imageContainerClassName = clsx(styles.imageContainer, { - [styles.isRound]: isRound, - }); - - const isFavorite = - 'userFavorite' in data && (data as { userFavorite: boolean }).userFavorite; - const userRating = - 'userRating' in data && - typeof (data as { userRating: null | number }).userRating === 'number' - ? (data as { userRating: null | number }).userRating - : null; - const hasRating = showRating && userRating !== null && userRating > 0; - - const imageContainerContent = ( - <> - {itemType === LibraryItem.GENRE && - data && - 'name' in data && - typeof (data as Genre).name === 'string' ? ( - - ) : ( - - )} - {isFavorite &&
} - {hasRating &&
{userRating}
} - - {withControls && showControls && data && ( - - )} - - - ); - return (
- {enableNavigation && navigationPath && (imageAsLink ?? !internalState) ? ( - - {imageContainerContent} - - ) : ( -
- {imageContainerContent} -
- )} + {data && (
{rows