From fbe6db37a6ef9e75a0be4486915c530bddd0ce28 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sun, 29 Jun 2025 11:58:23 -0300 Subject: [PATCH] feat: Enhance file manager with mobile support and UI improvements --- app/drive/page.tsx | 323 +++++++++++++++++++++--------------- app/prompt.txt | 64 +++++++ public/output.css | 19 +++ src/components/ui/sheet.tsx | 1 + 4 files changed, 277 insertions(+), 130 deletions(-) create mode 100644 app/prompt.txt diff --git a/app/drive/page.tsx b/app/drive/page.tsx index 8563a1d..a56676d 100644 --- a/app/drive/page.tsx +++ b/app/drive/page.tsx @@ -1,14 +1,30 @@ "use client"; -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; import { Search, Download, Trash2, Share, Star, - MoreVertical, Home, ChevronRight, + MoreVertical, Home, ChevronRight, ChevronLeft, Folder, File, Image, Video, Music, FileText, Code, Database, Clock, Users, Eye, Edit3, Copy, Scissors, - FolderPlus, Info, Lock, - ExternalLink, History} from 'lucide-react'; + FolderPlus, Info, Lock, Menu, + ExternalLink, History, X +} from 'lucide-react'; import { cn } from "@/lib/utils"; import Footer from '../footer'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { Button } from '@/components/ui/button'; +import { Separator } from '@/components/ui/separator'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from '@/components/ui/context-menu'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "@/components/ui/resizable"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Input } from '@/components/ui/input'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; // Simple date formatting functions const formatDate = (dateString) => { @@ -46,46 +62,6 @@ const formatDistanceToNow = (date) => { return formatDate(date); }; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Separator } from "@/components/ui/separator"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { - ContextMenu, - ContextMenuContent, - ContextMenuItem, - ContextMenuTrigger, - ContextMenuSeparator, - ContextMenuSub, - ContextMenuSubContent, - ContextMenuSubTrigger, -} from "@/components/ui/context-menu"; -import { - Tooltip, - TooltipContent, - TooltipTrigger, - TooltipProvider, -} from "@/components/ui/tooltip"; -import { - ResizableHandle, - ResizablePanel, - ResizablePanelGroup, -} from "@/components/ui/resizable"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; - // File system data const fileSystemData = { "": { @@ -193,7 +169,7 @@ const fileSystemData = { const getFileIcon = (item) => { if (item.is_dir) { - return ; + return ; } const iconMap = { @@ -209,7 +185,7 @@ const getFileIcon = (item) => { mp3: }; - return iconMap[item.type] || ; + return iconMap[item.type] || ; }; const formatFileSize = (bytes) => { @@ -264,7 +240,7 @@ const FileContextMenu = ({ file, children, onAction }) => { {item.label} - + {item.submenu.map((subItem, subIndex) => ( { {children} - + {contextMenuItems.map(renderContextMenuItem)} ); }; -const FolderTree = ({ onSelect, selectedPath, isCollapsed }) => { +const FolderTree = ({ onSelect, selectedPath = undefined, isCollapsed = undefined, isMobile = undefined }) => { const [expanded, setExpanded] = useState({ "": true, "projects": true }); const toggleExpand = (path) => { @@ -336,7 +312,7 @@ const FolderTree = ({ onSelect, selectedPath, isCollapsed }) => { onClick={() => toggleExpand(path)} className={cn( "flex items-center w-full px-2 py-1.5 text-sm rounded-md hover:bg-accent transition-colors", - isSelected && "bg-muted", + isSelected && "bg-accent", isCollapsed ? "justify-center" : "justify-start" )} > @@ -363,7 +339,7 @@ const FolderTree = ({ onSelect, selectedPath, isCollapsed }) => { }; return ( -
+
{!isCollapsed && ( <> - + {renderTreeItem("")} @@ -407,7 +383,7 @@ const FolderTree = ({ onSelect, selectedPath, isCollapsed }) => { ); }; -const FileList = ({ path, searchTerm, filterType, selectedFile, setSelectedFile, onContextAction }) => { +const FileList = ({ path, searchTerm, filterType, selectedFile, setSelectedFile, onContextAction, isMobile }) => { const files = useMemo(() => { const currentItem = fileSystemData[path]; if (!currentItem || !currentItem.is_dir || !currentItem.children) return []; @@ -440,7 +416,7 @@ const FileList = ({ path, searchTerm, filterType, selectedFile, setSelectedFile, }, [path, searchTerm, filterType]); return ( - +
{files.map((item) => ( - - Download - - - - - - Share - - - - - - Star - + + + + + + Download + + + + + + + + Share + + + + + + + + Star + +
- - + Rename Make a copy - Move to trash + Move to trash
- +
-
+
{getFileIcon(file)}
@@ -552,7 +534,7 @@ const FileDisplay = ({ file }) => {
)}
- +
Location
@@ -582,16 +564,13 @@ export default function FileManager() { const [searchTerm, setSearchTerm] = useState(''); const [filterType, setFilterType] = useState('all'); const [selectedFile, setSelectedFile] = useState(null); + const [isMobile, setIsMobile] = useState(false); + const [showMobileMenu, setShowMobileMenu] = useState(false); + const [activePanel, setActivePanel] = useState('files'); // 'tree', 'files', 'preview' const currentItem = fileSystemData[currentPath]; - // Drive-specific keyboard shortcuts - // XTreeGold classic shortcut layout - two rows - // XTree/NC-style: only unique, non-browser, non-reserved keys for file ops - // No F1-F12, Ctrl+F, Ctrl+T, Ctrl+W, Ctrl+N, Ctrl+R, Ctrl+P, etc. - // Use Q, W, E, R, T, Y, U, I, O, P, A, S, D, G, H, J, K, L, Z, X, C, V, B, M with Ctrl/Shift if needed const shortcuts = [ - // File operations row (unique qkeys) [ { key: 'Q', label: 'Rename', action: () => console.log('Rename') }, { key: 'W', label: 'View', action: () => console.log('View') }, @@ -604,7 +583,6 @@ export default function FileManager() { { key: 'O', label: 'Paste', action: () => console.log('Paste') }, { key: 'P', label: 'Duplicate', action: () => console.log('Duplicate') }, ], - // Navigation/info row (unique qkeys) [ { key: 'A', label: 'Select', action: () => console.log('Select') }, { key: 'S', label: 'Select All', action: () => console.log('Select All') }, @@ -618,14 +596,11 @@ export default function FileManager() { { key: 'X', label: 'Refresh', action: () => console.log('Refresh') }, ] ]; - const handleContextAction = (action, file) => { console.log(`Context action: ${action}`, file); - // Handle context menu actions here switch (action) { case 'star': - // Toggle star status console.log('Toggle star for', file.name); break; case 'share': @@ -652,14 +627,24 @@ export default function FileManager() { case 'details': console.log('Show details for', file.name); setSelectedFile(file); + if (isMobile) setActivePanel('preview'); break; default: console.log('Unknown action:', action); } }; - // Keyboard shortcut handler - React.useEffect(() => { + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < 768); + }; + + handleResize(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + useEffect(() => { const handleKeyDown = (e) => { const isCtrl = e.ctrlKey || e.metaKey; const isShift = e.shiftKey; @@ -675,7 +660,7 @@ export default function FileManager() { console.log('Upload shortcut'); } else if (isCtrl && e.key === 'f') { e.preventDefault(); - (document.querySelector('input[placeholder="Search files"]') as HTMLInputElement | null)?.focus(); + document.querySelector('input[placeholder="Search files"]').focus(); } else if (e.key === 'Delete' && selectedFile) { e.preventDefault(); console.log('Delete shortcut'); @@ -689,11 +674,93 @@ export default function FileManager() { return () => document.removeEventListener('keydown', handleKeyDown); }, [selectedFile]); + if (isMobile) { + return ( + +
+
+
+ + + + + + { + setCurrentPath(path); + setShowMobileMenu(false); + setActivePanel('files'); + }} + selectedPath={currentPath} + isMobile={true} + /> + + +

{currentItem?.name || 'My Drive'}

+
+
+ {activePanel !== 'preview' && ( + + )} + {activePanel === 'preview' && ( + + )} +
+
+ +
+ {activePanel === 'files' && ( +
+
+
+ + setSearchTerm(e.target.value)} + /> +
+
+ { + setSelectedFile(file); + setActivePanel('preview'); + }} + onContextAction={handleContextAction} + isMobile={true} + /> +
+ )} + + {activePanel === 'preview' && ( + + )} +
+ +
+
+
+ ); + } + return ( -
{/* Adjust based on your nav height */} - + +
- {/* Left Sidebar */} - + - {/* Middle File List */} - + -
+

{currentItem?.name || 'My Drive'}

- - All - Starred + + All + Starred
- -
+ +
setSearchTerm(e.target.value)} />