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 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,14 +1527,140 @@ 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">
|
||||||
|
{isMobile ? (
|
||||||
|
<>
|
||||||
|
<div className="relative">
|
||||||
|
<button
|
||||||
|
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)' }}
|
||||||
|
>
|
||||||
|
<MoreVertical className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<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' && (
|
{activeSection === 'trash' && (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTrashModal(true)}
|
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`}
|
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)' }}
|
style={{ backgroundColor: 'rgba(239, 68, 68, 0.1)', color: 'var(--color-danger)' }}
|
||||||
>
|
>
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-4 h-4" />
|
||||||
{!isMobile && "Empty Trash"}
|
Empty Trash
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -1551,14 +1679,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
|
|
||||||
<div className="relative group">
|
<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">
|
<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>
|
Sort by: <span className="capitalize">{sortBy}</span>
|
||||||
{sortAsc ? <ArrowUp className="w-3 h-3" /> : <ArrowUp className="w-3 h-3 rotate-180 transition-transform" />}
|
{sortAsc ? <ArrowUp className="w-3 h-3" /> : <ArrowUp className="w-3 h-3 rotate-180 transition-transform" />}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
</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="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)' }}>
|
<div className="w-full rounded-xl shadow-lg border overflow-hidden" style={{ backgroundColor: 'var(--color-bg-elevated)', borderColor: 'var(--color-border)' }}>
|
||||||
|
|
@ -1598,14 +1720,14 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
<button
|
<button
|
||||||
id="upload-button"
|
id="upload-button"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
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`}
|
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium text-white transition-all hover:opacity-90`}
|
||||||
style={{
|
style={{
|
||||||
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
|
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
|
||||||
}}
|
}}
|
||||||
title="Upload"
|
title="Upload"
|
||||||
>
|
>
|
||||||
<Upload className="w-4 h-4" />
|
<Upload className="w-4 h-4" />
|
||||||
{!isMobile && "Upload"}
|
Upload
|
||||||
</button>
|
</button>
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
|
|
@ -1618,6 +1740,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
|
||||||
Reference in a new issue