handle image drag from item detail list

This commit is contained in:
jeffvli
2026-02-12 01:37:59 -08:00
parent dc957cb3cc
commit 1d8e1957ba
2 changed files with 109 additions and 31 deletions
@@ -196,6 +196,17 @@
min-width: 0; min-width: 0;
} }
.image-wrapper-outer {
position: relative;
display: block;
width: 100%;
aspect-ratio: 1;
}
.image-wrapper-outer.image-wrapper-dragging {
opacity: 0.5;
}
.image-wrapper { .image-wrapper {
position: relative; position: relative;
display: block; display: block;
@@ -32,10 +32,12 @@ import styles from './item-detail-list.module.css';
import { ItemCardControls } from '/@/renderer/components/item-card/item-card-controls'; import { ItemCardControls } from '/@/renderer/components/item-card/item-card-controls';
import { ItemImage } from '/@/renderer/components/item-image/item-image'; import { ItemImage } from '/@/renderer/components/item-image/item-image';
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls'; import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import { import {
ItemListStateActions, ItemListStateActions,
ItemListStateItemWithRequiredProperties, ItemListStateItemWithRequiredProperties,
useItemDraggingState,
useItemListState, useItemListState,
useItemSelectionState, useItemSelectionState,
} from '/@/renderer/components/item-list/helpers/item-list-state'; } from '/@/renderer/components/item-list/helpers/item-list-state';
@@ -62,6 +64,7 @@ import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation'; import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation'; import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
import { songsQueries } from '/@/renderer/features/songs/api/songs-api'; import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useSettingsStore, useShowRatings } from '/@/renderer/store'; import { useSettingsStore, useShowRatings } from '/@/renderer/store';
import { formatDateAbsoluteUTC, formatDurationString } from '/@/renderer/utils'; import { formatDateAbsoluteUTC, formatDurationString } from '/@/renderer/utils';
@@ -422,6 +425,61 @@ const MetadataSection = memo(
const [isImageHovered, setIsImageHovered] = useState(false); const [isImageHovered, setIsImageHovered] = useState(false);
const [isMetadataHovered, setIsMetadataHovered] = useState(false); const [isMetadataHovered, setIsMetadataHovered] = useState(false);
const getId = useCallback(() => {
const draggedItems = getDraggedItems(item, internalState, false);
return draggedItems.map((i) => i.id);
}, [item, internalState]);
const getItem = useCallback(() => {
return getDraggedItems(item, internalState, false);
}, [item, internalState]);
const onDragStart = useCallback(() => {
const draggedItems = getDraggedItems(item, internalState, false);
internalState?.setDragging(draggedItems);
}, [item, internalState]);
const onDrop = useCallback(() => {
internalState?.setDragging([]);
}, [internalState]);
const drag = useMemo(() => {
const playlistSongs = (item as { _playlistSongs?: Song[] })._playlistSongs;
if (playlistSongs && playlistSongs.length > 0) {
return {
getId,
getItem: () => playlistSongs,
itemType: LibraryItem.SONG,
onDragStart,
onDrop,
operation: [DragOperation.ADD],
target: DragTarget.SONG,
};
}
return {
getId,
getItem,
itemType: item._itemType,
onDragStart,
onDrop,
operation: [DragOperation.ADD],
target: DragTarget.ALBUM,
};
}, [getId, getItem, item, onDragStart, onDrop]);
const { isDragging: isDraggingLocal, ref: dragRef } = useDragDrop<HTMLDivElement>({
drag,
isEnabled: !!item,
});
const isDraggingState = useItemDraggingState(internalState, item.id);
const isDragging = isDraggingState || isDraggingLocal;
const handleLinkDragStart = useCallback((e: React.DragEvent<HTMLAnchorElement>) => {
e.preventDefault();
e.stopPropagation();
}, []);
const isFavorite = item.userFavorite ?? false; const isFavorite = item.userFavorite ?? false;
const userRating = item.userRating ?? null; const userRating = item.userRating ?? null;
const hasRating = showRatings && userRating !== null && userRating > 0; const hasRating = showRatings && userRating !== null && userRating > 0;
@@ -482,9 +540,17 @@ const MetadataSection = memo(
className={styles.metadata} className={styles.metadata}
onMouseEnter={() => setIsMetadataHovered(true)} onMouseEnter={() => setIsMetadataHovered(true)}
onMouseLeave={() => setIsMetadataHovered(false)} onMouseLeave={() => setIsMetadataHovered(false)}
>
<div
className={clsx(styles.imageWrapperOuter, {
[styles.imageWrapperDragging]: isDragging,
})}
ref={dragRef ?? undefined}
> >
<Link <Link
className={styles.imageWrapper} className={styles.imageWrapper}
draggable={false}
onDragStart={handleLinkDragStart}
onMouseEnter={() => setIsImageHovered(true)} onMouseEnter={() => setIsImageHovered(true)}
onMouseLeave={() => setIsImageHovered(false)} onMouseLeave={() => setIsImageHovered(false)}
state={{ item }} state={{ item }}
@@ -516,6 +582,7 @@ const MetadataSection = memo(
)} )}
</AnimatePresence> </AnimatePresence>
</Link> </Link>
</div>
<Link <Link
className={styles.title} className={styles.title}
state={{ item }} state={{ item }}