fix nested playlist folder drag behavior

This commit is contained in:
jeffvli
2026-05-18 21:45:07 -07:00
parent e7b65c8e86
commit 37367a6741
@@ -372,9 +372,25 @@ export const PlaylistFolderDragExpandProvider = ({
expandedSet, expandedSet,
setMany, setMany,
}: PlaylistFolderDragExpandProviderProps) => { }: PlaylistFolderDragExpandProviderProps) => {
const separator = useSidebarPlaylistFolderSeparator();
const autoExpandedRef = useRef<Set<string>>(new Set()); const autoExpandedRef = useRef<Set<string>>(new Set());
const activeHoveredRef = useRef<null | string>(null); const activeHoveredRef = useRef<null | string>(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( const collapseAutoExpanded = useCallback(
(paths: string[]) => { (paths: string[]) => {
const toCollapse = paths.filter((path) => autoExpandedRef.current.has(path)); const toCollapse = paths.filter((path) => autoExpandedRef.current.has(path));
@@ -388,35 +404,60 @@ export const PlaylistFolderDragExpandProvider = ({
const onFolderDragHover = useCallback( const onFolderDragHover = useCallback(
(folderPath: string) => { (folderPath: string) => {
const previous = activeHoveredRef.current; const current = activeHoveredRef.current;
if (previous && previous !== folderPath) { if (
collapseAutoExpanded([previous]); current &&
current !== folderPath &&
current.startsWith(`${folderPath}${separator}`)
) {
return;
} }
const openChain = getOpenChain(folderPath);
activeHoveredRef.current = folderPath; activeHoveredRef.current = folderPath;
if (expandedSet.has(folderPath) || autoExpandedRef.current.has(folderPath)) return; const toCollapse = [...autoExpandedRef.current].filter(
autoExpandedRef.current.add(folderPath); (path) => !openChain.includes(path),
setMany([folderPath], true); );
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( const onFolderDragLeave = useCallback(
(folderPath: string) => { (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; activeHoveredRef.current = null;
} }
collapseAutoExpanded([folderPath]); collapseAutoExpanded([folderPath]);
}, },
[collapseAutoExpanded], [collapseAutoExpanded, separator],
); );
const onFolderDrop = useCallback((folderPath: string) => { const onFolderDrop = useCallback(
autoExpandedRef.current.delete(folderPath); (folderPath: string) => {
if (activeHoveredRef.current === folderPath) { for (const path of getOpenChain(folderPath)) autoExpandedRef.current.delete(path);
activeHoveredRef.current = null; if (activeHoveredRef.current === folderPath) {
} activeHoveredRef.current = null;
}, []); }
},
[getOpenChain],
);
const value = useMemo( const value = useMemo(
() => ({ onFolderDragHover, onFolderDragLeave, onFolderDrop }), () => ({ onFolderDragHover, onFolderDragLeave, onFolderDrop }),