Mobile UI scrolling fix and top bar redesign
All checks were successful
Automated Container Build / build-and-push (push) Successful in 40s

This commit is contained in:
Elijah 2026-05-24 09:03:27 -07:00
parent 7997ccb293
commit 14289b741f

View file

@ -208,6 +208,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [mobileSearchOpen, setMobileSearchOpen] = useState(false); const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [mobileFabOpen, setMobileFabOpen] = useState(false);
const [currentPath, setCurrentPath] = useState(() => { const [currentPath, setCurrentPath] = useState(() => {
@ -1278,7 +1280,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
}, [displayFiles, activeSection, trashAutoPurgeDays]); }, [displayFiles, activeSection, trashAutoPurgeDays]);
return ( return (
<div className="flex h-screen w-full overflow-hidden" style={{ backgroundColor: 'var(--color-bg-primary)' }}> <div className={`flex ${isMobile ? 'h-[100dvh]' : 'h-screen'} w-full overflow-hidden`} style={{ backgroundColor: 'var(--color-bg-primary)' }}>
{isMobile && sidebarOpen && ( {isMobile && sidebarOpen && (
<div <div
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-40" className="fixed inset-0 bg-black/50 backdrop-blur-sm z-40"
@ -1525,101 +1527,223 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
{(!isMobile || !mobileSearchOpen) && ( {(!isMobile || !mobileSearchOpen) && (
<div className="flex items-center gap-1 sm:gap-2"> <div className="flex items-center gap-1 sm:gap-2">
{activeSection === 'trash' && ( {isMobile ? (
<button <>
onClick={() => setShowTrashModal(true)} <div className="relative">
className={`flex items-center gap-2 ${isMobile ? 'p-2' : 'px-4 py-2'} rounded-lg text-sm font-medium transition-all hover:opacity-90`}
style={{ backgroundColor: 'rgba(239, 68, 68, 0.1)', color: 'var(--color-danger)' }}
>
<Trash2 className="w-4 h-4" />
{!isMobile && "Empty Trash"}
</button>
)}
{(activeSection === 'files' || activeSection === 'trash' || activeSection === 'shared') && (
<>
<button
onClick={toggleTheme}
className="p-2 rounded-lg transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-text-secondary)' }}
title={theme === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
>
{theme === 'dark' ? <Sun className="w-4.5 h-4.5" /> : <Moon className="w-4.5 h-4.5" />}
</button>
<div className="w-px h-5 mx-1" style={{ backgroundColor: 'var(--color-border-subtle)' }} />
<div className="relative group">
<button className="flex items-center gap-1.5 p-2 rounded-lg transition-colors text-sm font-medium" style={{ color: 'var(--color-text-secondary)' }} title="Sort">
{isMobile ? (
<ArrowUp className={`w-4.5 h-4.5 ${sortAsc ? '' : 'rotate-180 transition-transform'}`} />
) : (
<>
Sort by: <span className="capitalize">{sortBy}</span>
{sortAsc ? <ArrowUp className="w-3 h-3" /> : <ArrowUp className="w-3 h-3 rotate-180 transition-transform" />}
</>
)}
</button>
<div className="absolute right-0 top-full pt-1 w-40 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all z-50">
<div className="w-full rounded-xl shadow-lg border overflow-hidden" style={{ backgroundColor: 'var(--color-bg-elevated)', borderColor: 'var(--color-border)' }}>
{['name', 'size', 'date', 'type'].map(opt => (
<button key={opt} onClick={() => { if (sortBy === opt) setSortAsc(!sortAsc); else setSortBy(opt as any); }} className="w-full text-left px-4 py-2 text-sm transition-colors hover:bg-white/5 capitalize" style={{ color: sortBy === opt ? 'var(--color-accent)' : 'var(--color-text-primary)' }}>
{opt}
</button>
))}
</div>
</div>
</div>
<button
id="view-toggle"
onClick={() => setViewMode(viewMode === 'grid' ? 'list' : 'grid')}
className="p-2 rounded-lg transition-colors"
style={{ color: 'var(--color-text-secondary)' }}
title={viewMode === 'grid' ? 'Switch to list view' : 'Switch to grid view'}
>
{viewMode === 'grid' ? <List className="w-4.5 h-4.5" /> : <Grid3X3 className="w-4.5 h-4.5" />}
</button>
{activeSection === 'files' && (
<button
id="new-folder-button"
onClick={() => setShowNewFolder(true)}
className="p-2 rounded-lg transition-colors"
style={{ color: 'var(--color-text-secondary)' }}
title="New folder"
>
<FolderPlus className="w-4.5 h-4.5" />
</button>
)}
{activeSection === 'files' && (
<>
<button <button
id="upload-button" onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
onClick={() => fileInputRef.current?.click()} className="p-2 rounded-lg transition-colors hover:bg-black/5 dark:hover:bg-white/5"
className={`flex items-center gap-2 ${isMobile ? 'p-2' : 'px-4 py-2'} rounded-lg text-sm font-medium text-white transition-all hover:opacity-90`} style={{ color: 'var(--color-text-secondary)' }}
style={{
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
}}
title="Upload"
> >
<Upload className="w-4 h-4" /> <MoreVertical className="w-5 h-5" />
{!isMobile && "Upload"}
</button> </button>
<input <AnimatePresence>
ref={fileInputRef} {mobileMenuOpen && (
type="file" <>
multiple <motion.div
className="hidden" initial={{ opacity: 0 }}
onChange={handleFileUpload} animate={{ opacity: 1 }}
/> exit={{ opacity: 0 }}
</> className="fixed inset-0 z-40"
)} onClick={() => setMobileMenuOpen(false)}
</> />
)} <motion.div
</div> initial={{ opacity: 0, scale: 0.95, y: -10 }}
)} animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: -10 }}
className="absolute right-0 top-full mt-2 w-48 py-2 rounded-xl shadow-2xl border z-50 flex flex-col"
style={{ backgroundColor: 'var(--color-bg-elevated)', borderColor: 'var(--color-border)' }}
>
<button
onClick={() => { setMobileMenuOpen(false); toggleTheme(); }}
className="flex items-center gap-3 px-4 py-3 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-text-primary)' }}
>
{theme === 'dark' ? <Sun className="w-4.5 h-4.5" /> : <Moon className="w-4.5 h-4.5" />}
Theme: {theme === 'dark' ? 'Dark' : 'Light'}
</button>
<button
onClick={() => { setMobileMenuOpen(false); setViewMode(viewMode === 'grid' ? 'list' : 'grid'); }}
className="flex items-center gap-3 px-4 py-3 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-text-primary)' }}
>
{viewMode === 'grid' ? <List className="w-4.5 h-4.5" /> : <Grid3X3 className="w-4.5 h-4.5" />}
View: {viewMode === 'grid' ? 'Grid' : 'List'}
</button>
<div className="w-full h-px my-1" style={{ backgroundColor: 'var(--color-border-subtle)' }} />
<div className="px-4 py-2 text-xs font-semibold uppercase tracking-wider" style={{ color: 'var(--color-text-tertiary)' }}>Sort By</div>
{['name', 'size', 'date', 'type'].map(opt => (
<button
key={opt}
onClick={() => { setMobileMenuOpen(false); if (sortBy === opt) setSortAsc(!sortAsc); else setSortBy(opt as any); }}
className="flex items-center gap-3 px-4 py-2.5 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5 capitalize"
style={{ color: sortBy === opt ? 'var(--color-accent)' : 'var(--color-text-primary)' }}
>
{opt}
{sortBy === opt && <ArrowUp className={`w-3.5 h-3.5 ml-auto ${sortAsc ? '' : 'rotate-180 transition-transform'}`} />}
</button>
))}
{activeSection === 'trash' && (
<>
<div className="w-full h-px my-1" style={{ backgroundColor: 'var(--color-border-subtle)' }} />
<button
onClick={() => { setMobileMenuOpen(false); setShowTrashModal(true); }}
className="flex items-center gap-3 px-4 py-3 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-danger)' }}
>
<Trash2 className="w-4.5 h-4.5" />
Empty Trash
</button>
</>
)}
</motion.div>
</>
)}
</AnimatePresence>
</div>
{activeSection === 'files' && (
<div className="relative">
<button
onClick={() => setMobileFabOpen(!mobileFabOpen)}
className="p-2 rounded-lg text-white transition-all hover:opacity-90 ml-1"
style={{ background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)' }}
>
<Plus className={`w-5 h-5 transition-transform ${mobileFabOpen ? 'rotate-45' : ''}`} />
</button>
<AnimatePresence>
{mobileFabOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-40"
onClick={() => setMobileFabOpen(false)}
/>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: -10 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: -10 }}
className="absolute right-0 top-full mt-2 w-48 py-2 rounded-xl shadow-2xl border z-50 flex flex-col"
style={{ backgroundColor: 'var(--color-bg-elevated)', borderColor: 'var(--color-border)' }}
>
<button
onClick={() => { setMobileFabOpen(false); setShowNewFolder(true); }}
className="flex items-center gap-3 px-4 py-3 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-text-primary)' }}
>
<FolderPlus className="w-4.5 h-4.5" />
New Folder
</button>
<button
onClick={() => { setMobileFabOpen(false); fileInputRef.current?.click(); }}
className="flex items-center gap-3 px-4 py-3 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-text-primary)' }}
>
<Upload className="w-4.5 h-4.5" />
Upload File
</button>
<input ref={fileInputRef} type="file" multiple className="hidden" onChange={handleFileUpload} />
</motion.div>
</>
)}
</AnimatePresence>
</div>
)}
</>
) : (
<>
{activeSection === 'trash' && (
<button
onClick={() => setShowTrashModal(true)}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all hover:opacity-90`}
style={{ backgroundColor: 'rgba(239, 68, 68, 0.1)', color: 'var(--color-danger)' }}
>
<Trash2 className="w-4 h-4" />
Empty Trash
</button>
)}
{(activeSection === 'files' || activeSection === 'trash' || activeSection === 'shared') && (
<>
<button
onClick={toggleTheme}
className="p-2 rounded-lg transition-colors hover:bg-black/5 dark:hover:bg-white/5"
style={{ color: 'var(--color-text-secondary)' }}
title={theme === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
>
{theme === 'dark' ? <Sun className="w-4.5 h-4.5" /> : <Moon className="w-4.5 h-4.5" />}
</button>
<div className="w-px h-5 mx-1" style={{ backgroundColor: 'var(--color-border-subtle)' }} />
<div className="relative group">
<button className="flex items-center gap-1.5 p-2 rounded-lg transition-colors text-sm font-medium" style={{ color: 'var(--color-text-secondary)' }} title="Sort">
Sort by: <span className="capitalize">{sortBy}</span>
{sortAsc ? <ArrowUp className="w-3 h-3" /> : <ArrowUp className="w-3 h-3 rotate-180 transition-transform" />}
</button>
<div className="absolute right-0 top-full pt-1 w-40 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all z-50">
<div className="w-full rounded-xl shadow-lg border overflow-hidden" style={{ backgroundColor: 'var(--color-bg-elevated)', borderColor: 'var(--color-border)' }}>
{['name', 'size', 'date', 'type'].map(opt => (
<button key={opt} onClick={() => { if (sortBy === opt) setSortAsc(!sortAsc); else setSortBy(opt as any); }} className="w-full text-left px-4 py-2 text-sm transition-colors hover:bg-white/5 capitalize" style={{ color: sortBy === opt ? 'var(--color-accent)' : 'var(--color-text-primary)' }}>
{opt}
</button>
))}
</div>
</div>
</div>
<button
id="view-toggle"
onClick={() => setViewMode(viewMode === 'grid' ? 'list' : 'grid')}
className="p-2 rounded-lg transition-colors"
style={{ color: 'var(--color-text-secondary)' }}
title={viewMode === 'grid' ? 'Switch to list view' : 'Switch to grid view'}
>
{viewMode === 'grid' ? <List className="w-4.5 h-4.5" /> : <Grid3X3 className="w-4.5 h-4.5" />}
</button>
{activeSection === 'files' && (
<button
id="new-folder-button"
onClick={() => setShowNewFolder(true)}
className="p-2 rounded-lg transition-colors"
style={{ color: 'var(--color-text-secondary)' }}
title="New folder"
>
<FolderPlus className="w-4.5 h-4.5" />
</button>
)}
{activeSection === 'files' && (
<>
<button
id="upload-button"
onClick={() => fileInputRef.current?.click()}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium text-white transition-all hover:opacity-90`}
style={{
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
}}
title="Upload"
>
<Upload className="w-4 h-4" />
Upload
</button>
<input
ref={fileInputRef}
type="file"
multiple
className="hidden"
onChange={handleFileUpload}
/>
</>
)}
</>
)}
</>
)}
</div>
)}
</header> </header>
{activeSection === 'settings' ? ( {activeSection === 'settings' ? (