fix(ui): improve drag selection scroll behavior, add right-click to unpin sidebar items, close preview on back navigation, fix shared files alignment
Some checks failed
Automated Container Build / build-and-push (push) Failing after 9s
Some checks failed
Automated Container Build / build-and-push (push) Failing after 9s
This commit is contained in:
parent
fbad71cbf2
commit
59407278d1
2 changed files with 52 additions and 19 deletions
|
|
@ -220,6 +220,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
const handlePopState = (e: PopStateEvent) => {
|
||||
if (e.state && e.state.path) setCurrentPath(e.state.path);
|
||||
else setCurrentPath(new URLSearchParams(window.location.search).get('path') || '.');
|
||||
setPreviewFile(null);
|
||||
};
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
|
|
@ -273,7 +274,9 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
const [pinnedOrder, setPinnedOrder] = useState<string[]>([]);
|
||||
const [dragOverPinnedItem, setDragOverPinnedItem] = useState<string | null>(null);
|
||||
const [pinnedRefreshCounter, setPinnedRefreshCounter] = useState(0);
|
||||
const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set());
|
||||
const [selectionBox, setSelectionBox] = useState<{ startX: number; startY: number; currentX: number; currentY: number } | null>(null);
|
||||
const lastMousePos = useRef<{clientX: number, clientY: number} | null>(null);
|
||||
const [initialSelectedFiles, setInitialSelectedFiles] = useState<Set<string>>(new Set());
|
||||
const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
const [downloadToken, setDownloadToken] = useState<string>('');
|
||||
|
|
@ -527,12 +530,18 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
if (target.closest('button') || target.closest('input') || target.closest('[data-file-item]')) {
|
||||
return;
|
||||
}
|
||||
const rect = mainContainerRef.current!.getBoundingClientRect();
|
||||
const scrollLeft = mainContainerRef.current!.scrollLeft;
|
||||
const scrollTop = mainContainerRef.current!.scrollTop;
|
||||
|
||||
setSelectionBox({
|
||||
startX: e.clientX,
|
||||
startY: e.clientY,
|
||||
currentX: e.clientX,
|
||||
currentY: e.clientY,
|
||||
startX: e.clientX - rect.left + scrollLeft,
|
||||
startY: e.clientY - rect.top + scrollTop,
|
||||
currentX: e.clientX - rect.left + scrollLeft,
|
||||
currentY: e.clientY - rect.top + scrollTop,
|
||||
});
|
||||
lastMousePos.current = { clientX: e.clientX, clientY: e.clientY };
|
||||
|
||||
if (e.shiftKey || e.ctrlKey || e.metaKey) {
|
||||
setInitialSelectedFiles(new Set(selectedFiles));
|
||||
} else {
|
||||
|
|
@ -541,26 +550,35 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
}
|
||||
}
|
||||
|
||||
function handlePointerMove(e: React.PointerEvent) {
|
||||
if (!selectionBox) return;
|
||||
setSelectionBox(prev => prev ? { ...prev, currentX: e.clientX, currentY: e.clientY } : null);
|
||||
function updateSelection(clientX: number, clientY: number) {
|
||||
setSelectionBox(prev => {
|
||||
if (!prev || !mainContainerRef.current) return null;
|
||||
const rect = mainContainerRef.current.getBoundingClientRect();
|
||||
const scrollLeft = mainContainerRef.current.scrollLeft;
|
||||
const scrollTop = mainContainerRef.current.scrollTop;
|
||||
|
||||
if (mainContainerRef.current) {
|
||||
const boxLeft = Math.min(selectionBox.startX, e.clientX);
|
||||
const boxRight = Math.max(selectionBox.startX, e.clientX);
|
||||
const boxTop = Math.min(selectionBox.startY, e.clientY);
|
||||
const boxBottom = Math.max(selectionBox.startY, e.clientY);
|
||||
const next = { ...prev, currentX: clientX - rect.left + scrollLeft, currentY: clientY - rect.top + scrollTop };
|
||||
|
||||
const boxLeft = Math.min(next.startX, next.currentX);
|
||||
const boxRight = Math.max(next.startX, next.currentX);
|
||||
const boxTop = Math.min(next.startY, next.currentY);
|
||||
const boxBottom = Math.max(next.startY, next.currentY);
|
||||
|
||||
const viewportBoxLeft = boxLeft + rect.left - scrollLeft;
|
||||
const viewportBoxRight = boxRight + rect.left - scrollLeft;
|
||||
const viewportBoxTop = boxTop + rect.top - scrollTop;
|
||||
const viewportBoxBottom = boxBottom + rect.top - scrollTop;
|
||||
|
||||
const fileNodes = mainContainerRef.current.querySelectorAll('[data-file-item]');
|
||||
const newSelected = new Set(initialSelectedFiles);
|
||||
|
||||
fileNodes.forEach(node => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
const fileRect = node.getBoundingClientRect();
|
||||
const intersects = !(
|
||||
rect.right < boxLeft ||
|
||||
rect.left > boxRight ||
|
||||
rect.bottom < boxTop ||
|
||||
rect.top > boxBottom
|
||||
fileRect.right < viewportBoxLeft ||
|
||||
fileRect.left > viewportBoxRight ||
|
||||
fileRect.bottom < viewportBoxTop ||
|
||||
fileRect.top > viewportBoxBottom
|
||||
);
|
||||
|
||||
const filePath = node.getAttribute('data-file-path');
|
||||
|
|
@ -574,6 +592,19 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
});
|
||||
|
||||
setSelectedFiles(newSelected);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
function handlePointerMove(e: React.PointerEvent) {
|
||||
if (!selectionBox) return;
|
||||
lastMousePos.current = { clientX: e.clientX, clientY: e.clientY };
|
||||
updateSelection(e.clientX, e.clientY);
|
||||
}
|
||||
|
||||
function handleScroll(e: React.UIEvent) {
|
||||
if (selectionBox && lastMousePos.current) {
|
||||
updateSelection(lastMousePos.current.clientX, lastMousePos.current.clientY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1270,6 +1301,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
borderTop: dragOverPinnedItem === file.path ? '2px solid var(--color-accent)' : '2px solid transparent',
|
||||
}}
|
||||
onClick={() => { setActiveSection('files'); navigateTo(file.path); }}
|
||||
onContextMenu={(e) => handleContextMenu(e, file)}
|
||||
>
|
||||
<div className="w-4 h-4 flex items-center justify-center flex-shrink-0 [&>svg]:w-4 [&>svg]:h-4">
|
||||
{getFileIcon(file)}
|
||||
|
|
@ -1490,10 +1522,11 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerLeave={handlePointerUp}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
{selectionBox && (
|
||||
<div
|
||||
className="fixed z-50 pointer-events-none"
|
||||
className="absolute z-50 pointer-events-none"
|
||||
style={{
|
||||
left: Math.min(selectionBox.startX, selectionBox.currentX),
|
||||
top: Math.min(selectionBox.startY, selectionBox.currentY),
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export default function SharedFilesPage() {
|
|||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="w-full">
|
||||
<h2 className="text-xl font-semibold mb-6" style={{ color: 'var(--color-text-primary)' }}>Shared Files</h2>
|
||||
|
||||
{shares.length === 0 ? (
|
||||
|
|
|
|||
Reference in a new issue