add item card navigation

This commit is contained in:
jeffvli
2025-11-19 22:28:03 -08:00
parent 8ac0a27f33
commit ff776293a6
2 changed files with 202 additions and 85 deletions
@@ -21,9 +21,12 @@
.image-container {
position: relative;
display: block;
width: 100%;
aspect-ratio: 1;
overflow: hidden;
color: inherit;
text-decoration: none;
&::before {
position: absolute;
+158 -44
View File
@@ -8,6 +8,7 @@ import styles from './item-card.module.css';
import { ItemCardControls } from '/@/renderer/components/item-card/item-card-controls';
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
import { getTitlePath } from '/@/renderer/components/item-list/helpers/get-title-path';
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
import { ItemControls } from '/@/renderer/components/item-list/types';
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
@@ -170,6 +171,8 @@ const CompactItemCard = ({
});
if (data) {
const navigationPath = getItemNavigationPath(data, itemType);
const handleMouseEnter = () => {
if (withControls) {
setShowControls(true);
@@ -182,7 +185,7 @@ const CompactItemCard = ({
}
};
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
const handleContextMenu = (e: React.MouseEvent<HTMLElement>) => {
if (!data || !controls) {
return;
}
@@ -197,19 +200,20 @@ const CompactItemCard = ({
});
};
return (
<div
className={clsx(styles.container, styles.compact, {
[styles.selected]: isSelected,
})}
>
<div
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
onClick={handleClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
const handleImageClick = (e: React.MouseEvent<HTMLElement>) => {
// Prevent navigation on double-click, let the double-click handler work
if (e.detail === 2 && navigationPath) {
e.preventDefault();
}
handleClick(e as any);
};
const imageContainerClassName = clsx(styles.imageContainer, {
[styles.isRound]: isRound,
});
const imageContainerContent = (
<>
<Image
className={clsx(styles.image, { [styles.isRound]: isRound })}
src={imageUrl}
@@ -240,7 +244,37 @@ const CompactItemCard = ({
/>
))}
</div>
</>
);
return (
<div
className={clsx(styles.container, styles.compact, {
[styles.selected]: isSelected,
})}
>
{navigationPath ? (
<Link
className={imageContainerClassName}
onClick={handleImageClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
to={navigationPath}
>
{imageContainerContent}
</Link>
) : (
<div
className={imageContainerClassName}
onClick={handleImageClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{imageContainerContent}
</div>
)}
</div>
);
}
@@ -325,6 +359,8 @@ const DefaultItemCard = ({
});
if (data) {
const navigationPath = getItemNavigationPath(data, itemType);
const handleMouseEnter = () => {
if (withControls) {
setShowControls(true);
@@ -337,7 +373,7 @@ const DefaultItemCard = ({
}
};
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
const handleContextMenu = (e: React.MouseEvent<HTMLElement>) => {
if (!data || !controls) {
return;
}
@@ -352,19 +388,20 @@ const DefaultItemCard = ({
});
};
return (
<div
className={clsx(styles.container, {
[styles.selected]: isSelected,
})}
>
<div
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
onClick={handleClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
const handleImageClick = (e: React.MouseEvent<HTMLElement>) => {
// Prevent navigation on double-click, let the double-click handler work
if (e.detail === 2 && navigationPath) {
e.preventDefault();
}
handleClick(e as any);
};
const imageContainerClassName = clsx(styles.imageContainer, {
[styles.isRound]: isRound,
});
const imageContainerContent = (
<>
<Image
className={clsx(styles.image, { [styles.isRound]: isRound })}
src={imageUrl}
@@ -379,7 +416,37 @@ const DefaultItemCard = ({
/>
)}
</AnimatePresence>
</>
);
return (
<div
className={clsx(styles.container, {
[styles.selected]: isSelected,
})}
>
{navigationPath ? (
<Link
className={imageContainerClassName}
onClick={handleImageClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
to={navigationPath}
>
{imageContainerContent}
</Link>
) : (
<div
className={imageContainerClassName}
onClick={handleImageClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{imageContainerContent}
</div>
)}
<div className={styles.detailContainer}>
{rows
.filter(
@@ -525,6 +592,8 @@ const PosterItemCard = ({
});
if (data) {
const navigationPath = getItemNavigationPath(data, itemType);
const handleMouseEnter = () => {
if (withControls) {
setShowControls(true);
@@ -537,7 +606,7 @@ const PosterItemCard = ({
}
};
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
const handleContextMenu = (e: React.MouseEvent<HTMLElement>) => {
if (!data || !controls) {
return;
}
@@ -552,21 +621,20 @@ const PosterItemCard = ({
});
};
return (
<div
className={clsx(styles.container, styles.poster, {
[styles.dragging]: isDragging,
[styles.selected]: isSelected,
})}
ref={ref}
>
<div
className={clsx(styles.imageContainer, { [styles.isRound]: isRound })}
onClick={handleClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
const handleImageClick = (e: React.MouseEvent<HTMLElement>) => {
// Prevent navigation on double-click, let the double-click handler work
if (e.detail === 2 && navigationPath) {
e.preventDefault();
}
handleClick(e as any);
};
const imageContainerClassName = clsx(styles.imageContainer, {
[styles.isRound]: isRound,
});
const imageContainerContent = (
<>
<Image
className={clsx(styles.image, { [styles.isRound]: isRound })}
src={imageUrl}
@@ -582,7 +650,39 @@ const PosterItemCard = ({
/>
)}
</AnimatePresence>
</>
);
return (
<div
className={clsx(styles.container, styles.poster, {
[styles.dragging]: isDragging,
[styles.selected]: isSelected,
})}
ref={ref}
>
{navigationPath ? (
<Link
className={imageContainerClassName}
onClick={handleImageClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
to={navigationPath}
>
{imageContainerContent}
</Link>
) : (
<div
className={imageContainerClassName}
onClick={handleImageClick}
onContextMenu={handleContextMenu}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{imageContainerContent}
</div>
)}
{data && (
<div className={styles.detailContainer}>
{rows
@@ -868,6 +968,20 @@ const getImageUrl = (data: Album | AlbumArtist | Artist | Playlist | Song | unde
return undefined;
};
const getItemNavigationPath = (
data: Album | AlbumArtist | Artist | Playlist | Song | undefined,
itemType: LibraryItem,
): null | string => {
if (!data || !('id' in data) || !data.id) {
return null;
}
// Check if data has _itemType (like in title row logic)
const effectiveItemType = '_itemType' in data && data._itemType ? data._itemType : itemType;
return getTitlePath(effectiveItemType, data.id);
};
const ItemCardRow = ({
data,
index,