From 37367a6741f41a321826b9c15518664674b4d9d7 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Mon, 18 May 2026 21:45:07 -0700 Subject: [PATCH] fix nested playlist folder drag behavior --- .../components/playlist-folder-tree.tsx | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/renderer/features/sidebar/components/playlist-folder-tree.tsx b/src/renderer/features/sidebar/components/playlist-folder-tree.tsx index 486bce57f..8c3cb9cee 100644 --- a/src/renderer/features/sidebar/components/playlist-folder-tree.tsx +++ b/src/renderer/features/sidebar/components/playlist-folder-tree.tsx @@ -372,9 +372,25 @@ export const PlaylistFolderDragExpandProvider = ({ expandedSet, setMany, }: PlaylistFolderDragExpandProviderProps) => { + const separator = useSidebarPlaylistFolderSeparator(); const autoExpandedRef = useRef>(new Set()); const activeHoveredRef = useRef(null); + const getOpenChain = useCallback( + (folderPath: string) => { + const segments = folderPath.split(separator).filter((segment) => segment.length > 0); + const chain: string[] = []; + + for (let index = 1; index < segments.length; index++) { + chain.push(segments.slice(0, index).join(separator)); + } + + chain.push(folderPath); + return chain; + }, + [separator], + ); + const collapseAutoExpanded = useCallback( (paths: string[]) => { const toCollapse = paths.filter((path) => autoExpandedRef.current.has(path)); @@ -388,35 +404,60 @@ export const PlaylistFolderDragExpandProvider = ({ const onFolderDragHover = useCallback( (folderPath: string) => { - const previous = activeHoveredRef.current; - if (previous && previous !== folderPath) { - collapseAutoExpanded([previous]); + const current = activeHoveredRef.current; + if ( + current && + current !== folderPath && + current.startsWith(`${folderPath}${separator}`) + ) { + return; } + + const openChain = getOpenChain(folderPath); activeHoveredRef.current = folderPath; - if (expandedSet.has(folderPath) || autoExpandedRef.current.has(folderPath)) return; - autoExpandedRef.current.add(folderPath); - setMany([folderPath], true); + const toCollapse = [...autoExpandedRef.current].filter( + (path) => !openChain.includes(path), + ); + if (toCollapse.length > 0) { + collapseAutoExpanded(toCollapse); + } + + const toExpand = openChain.filter( + (path) => !expandedSet.has(path) && !autoExpandedRef.current.has(path), + ); + if (toExpand.length === 0) return; + + for (const path of toExpand) autoExpandedRef.current.add(path); + setMany(toExpand, true); }, - [collapseAutoExpanded, expandedSet, setMany], + [collapseAutoExpanded, expandedSet, getOpenChain, separator, setMany], ); const onFolderDragLeave = useCallback( (folderPath: string) => { - if (activeHoveredRef.current === folderPath) { + const active = activeHoveredRef.current; + if (active && active !== folderPath && active.startsWith(`${folderPath}${separator}`)) { + return; + } + + if (active === folderPath) { activeHoveredRef.current = null; } collapseAutoExpanded([folderPath]); }, - [collapseAutoExpanded], + [collapseAutoExpanded, separator], ); - const onFolderDrop = useCallback((folderPath: string) => { - autoExpandedRef.current.delete(folderPath); - if (activeHoveredRef.current === folderPath) { - activeHoveredRef.current = null; - } - }, []); + const onFolderDrop = useCallback( + (folderPath: string) => { + for (const path of getOpenChain(folderPath)) autoExpandedRef.current.delete(path); + if (activeHoveredRef.current === folderPath) { + activeHoveredRef.current = null; + } + }, + [getOpenChain], + ); const value = useMemo( () => ({ onFolderDragHover, onFolderDragLeave, onFolderDrop }),