mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
d4d3b0e53f
* refactor(folder-browser): G.87.1 — extract helpers + types Move ColumnKind / NavPos / Column types + entryToAlbumIfPresent / entryToTrack mappers + isFolderBrowserArrowKey / folderBrowserHasKeyModifiers key-event helpers into src/utils/folderBrowserHelpers.ts. Pure code move. * refactor(folder-browser): G.87.2 — extract FolderBrowserColumn component * refactor(folder-browser): G.87.3 — extract useFolderBrowserNowPlayingPath hook * refactor(folder-browser): G.87.4 — extract useFolderBrowserScrolling hook * refactor(folder-browser): G.87.5 — extract useFolderBrowserKeyboardNav hook
92 lines
3.1 KiB
TypeScript
92 lines
3.1 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
import type { Column, NavPos } from '../utils/folderBrowserHelpers';
|
|
|
|
interface Args {
|
|
columns: Column[];
|
|
keyboardPos: NavPos | null;
|
|
keyboardNavActive: boolean;
|
|
setKeyboardNavActive: React.Dispatch<React.SetStateAction<boolean>>;
|
|
}
|
|
|
|
interface Result {
|
|
wrapperRef: React.RefObject<HTMLDivElement | null>;
|
|
columnsViewportWidth: number;
|
|
}
|
|
|
|
export function useFolderBrowserScrolling({
|
|
columns, keyboardPos, keyboardNavActive, setKeyboardNavActive,
|
|
}: Args): Result {
|
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
const [columnsViewportWidth, setColumnsViewportWidth] = useState(0);
|
|
|
|
useEffect(() => {
|
|
const el = wrapperRef.current;
|
|
if (!el) return;
|
|
requestAnimationFrame(() => {
|
|
el.scrollLeft = el.scrollWidth;
|
|
});
|
|
}, [columns.length]);
|
|
|
|
useEffect(() => {
|
|
const el = wrapperRef.current;
|
|
if (!el) return;
|
|
setColumnsViewportWidth(el.clientWidth);
|
|
const observer = new ResizeObserver(() => {
|
|
setColumnsViewportWidth(el.clientWidth);
|
|
});
|
|
observer.observe(el);
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!wrapperRef.current) return;
|
|
requestAnimationFrame(() => {
|
|
columns.forEach((col, colIndex) => {
|
|
const selectedId = col.selectedId;
|
|
if (!selectedId) return;
|
|
const row = wrapperRef.current?.querySelector<HTMLElement>(
|
|
`.folder-col[data-folder-col-index="${colIndex}"] .folder-col-row[data-item-id="${selectedId}"]`,
|
|
);
|
|
row?.scrollIntoView({ block: 'nearest' });
|
|
});
|
|
|
|
if (keyboardPos) {
|
|
const kbdRow = wrapperRef.current?.querySelector<HTMLElement>(
|
|
`.folder-col[data-folder-col-index="${keyboardPos.colIndex}"] .folder-col-row[data-row-index="${keyboardPos.rowIndex}"]`,
|
|
);
|
|
kbdRow?.scrollIntoView({ block: 'nearest' });
|
|
}
|
|
|
|
const fallbackColIndex = [...columns]
|
|
.map((c, i) => (c.selectedId ? i : -1))
|
|
.filter(i => i >= 0)
|
|
.pop();
|
|
const baseColIndex = keyboardPos?.colIndex ?? fallbackColIndex ?? Math.max(0, columns.length - 1);
|
|
const focusColIndex = Math.min(Math.max(0, columns.length - 1), baseColIndex + 1);
|
|
const focusCol = wrapperRef.current?.querySelector<HTMLElement>(
|
|
`.folder-col[data-folder-col-index="${focusColIndex}"]`,
|
|
);
|
|
focusCol?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
});
|
|
}, [columns, keyboardPos]);
|
|
|
|
useEffect(() => {
|
|
const el = wrapperRef.current;
|
|
if (!el) return;
|
|
const hasRows = columns.some(c => !c.loading && !c.error && c.items.length > 0);
|
|
if (!hasRows) return;
|
|
requestAnimationFrame(() => {
|
|
el.focus({ preventScroll: true });
|
|
});
|
|
}, [columns]);
|
|
|
|
useEffect(() => {
|
|
if (!keyboardNavActive) return;
|
|
const onMouseMove = () => setKeyboardNavActive(false);
|
|
window.addEventListener('mousemove', onMouseMove, { once: true });
|
|
return () => window.removeEventListener('mousemove', onMouseMove);
|
|
}, [keyboardNavActive, setKeyboardNavActive]);
|
|
|
|
return { wrapperRef, columnsViewportWidth };
|
|
}
|