Mobile UI scrolling fix and top bar redesign
All checks were successful
Automated Container Build / build-and-push (push) Successful in 40s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 40s
This commit is contained in:
parent
7997ccb293
commit
14289b741f
1 changed files with 217 additions and 93 deletions
|
|
@ -208,6 +208,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
const isMobile = useIsMobile();
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [mobileFabOpen, setMobileFabOpen] = useState(false);
|
||||
|
||||
|
||||
const [currentPath, setCurrentPath] = useState(() => {
|
||||
|
|
@ -1278,7 +1280,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
}, [displayFiles, activeSection, trashAutoPurgeDays]);
|
||||
|
||||
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 && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-40"
|
||||
|
|
@ -1525,101 +1527,223 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
|||
|
||||
{(!isMobile || !mobileSearchOpen) && (
|
||||
<div className="flex items-center gap-1 sm:gap-2">
|
||||
{activeSection === 'trash' && (
|
||||
<button
|
||||
onClick={() => setShowTrashModal(true)}
|
||||
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' && (
|
||||
<>
|
||||
{isMobile ? (
|
||||
<>
|
||||
<div className="relative">
|
||||
<button
|
||||
id="upload-button"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
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={{
|
||||
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
|
||||
}}
|
||||
title="Upload"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
className="p-2 rounded-lg transition-colors hover:bg-black/5 dark:hover:bg-white/5"
|
||||
style={{ color: 'var(--color-text-secondary)' }}
|
||||
>
|
||||
<Upload className="w-4 h-4" />
|
||||
{!isMobile && "Upload"}
|
||||
<MoreVertical className="w-5 h-5" />
|
||||
</button>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
multiple
|
||||
className="hidden"
|
||||
onChange={handleFileUpload}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{mobileMenuOpen && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-40"
|
||||
onClick={() => setMobileMenuOpen(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={() => { 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>
|
||||
|
||||
{activeSection === 'settings' ? (
|
||||
|
|
|
|||
Reference in a new issue