Add Pinned sidebar section to Docs and Slides mode
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
3cedbac51c
commit
707719f4af
1 changed files with 81 additions and 70 deletions
|
|
@ -1308,6 +1308,84 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
return sortedGroupNames.map(name => ({ name, files: groupsMap[name] }));
|
return sortedGroupNames.map(name => ({ name, files: groupsMap[name] }));
|
||||||
}, [displayFiles, activeSection, trashAutoPurgeDays]);
|
}, [displayFiles, activeSection, trashAutoPurgeDays]);
|
||||||
|
|
||||||
|
const renderPinnedSidebarSection = (filterExt?: string) => {
|
||||||
|
const displayedPinned = filterExt ? pinnedFiles.filter(f => f.name.endsWith(filterExt)) : pinnedFiles;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="py-1">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const next = !pinnedExpanded;
|
||||||
|
setPinnedExpanded(next);
|
||||||
|
api.updateSettings({ pinned_sidebar_expanded: next ? 'true' : 'false' }).catch(console.error);
|
||||||
|
}}
|
||||||
|
className="w-full flex items-center justify-between px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-150 hover:bg-white/5"
|
||||||
|
style={{ color: 'var(--color-text-secondary)' }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Pin className="w-4.5 h-4.5" />
|
||||||
|
Pinned
|
||||||
|
</div>
|
||||||
|
<ChevronRight className={`w-4 h-4 transition-transform duration-200 ${pinnedExpanded ? 'rotate-90' : ''}`} />
|
||||||
|
</button>
|
||||||
|
<AnimatePresence>
|
||||||
|
{pinnedExpanded && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ height: 0, opacity: 0 }}
|
||||||
|
animate={{ height: 'auto', opacity: 1 }}
|
||||||
|
exit={{ height: 0, opacity: 0 }}
|
||||||
|
className="overflow-hidden flex flex-col gap-0.5 mt-1"
|
||||||
|
>
|
||||||
|
{displayedPinned.length === 0 ? (
|
||||||
|
<div className="px-10 py-2 text-xs" style={{ color: 'var(--color-text-tertiary)' }}>No pinned items</div>
|
||||||
|
) : (
|
||||||
|
displayedPinned.map((file) => (
|
||||||
|
<div
|
||||||
|
key={file.path}
|
||||||
|
data-file-item
|
||||||
|
draggable
|
||||||
|
onDragStart={(e) => {
|
||||||
|
e.dataTransfer.setData('text/plain', file.path);
|
||||||
|
}}
|
||||||
|
onDragOver={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setDragOverPinnedItem(file.path);
|
||||||
|
}}
|
||||||
|
onDragLeave={() => {
|
||||||
|
setDragOverPinnedItem(null);
|
||||||
|
}}
|
||||||
|
onDrop={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setDragOverPinnedItem(null);
|
||||||
|
const source = e.dataTransfer.getData('text/plain');
|
||||||
|
if (source && source !== file.path) {
|
||||||
|
handlePinnedDragEnd(source, file.path);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="group flex items-center justify-between px-10 py-2 text-sm font-medium cursor-pointer rounded-lg transition-colors hover:bg-white/5 gap-3"
|
||||||
|
style={{
|
||||||
|
backgroundColor: activeSection === 'files' && currentPath === file.path ? 'var(--color-accent-subtle)' : 'transparent',
|
||||||
|
color: activeSection === 'files' && currentPath === file.path ? 'var(--color-accent)' : 'var(--color-text-secondary)',
|
||||||
|
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)}
|
||||||
|
</div>
|
||||||
|
<span className="truncate flex-1" title={getFileDisplayName(file.name, file.is_dir)}>{getFileDisplayName(file.name, file.is_dir)}</span>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex ${isMobile ? 'h-[100dvh]' : '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 && (
|
||||||
|
|
@ -1425,76 +1503,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-1">
|
{renderPinnedSidebarSection()}
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
const next = !pinnedExpanded;
|
|
||||||
setPinnedExpanded(next);
|
|
||||||
api.updateSettings({ pinned_sidebar_expanded: next ? 'true' : 'false' }).catch(console.error);
|
|
||||||
}}
|
|
||||||
className="w-full flex items-center justify-between px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-150 hover:bg-white/5"
|
|
||||||
style={{ color: 'var(--color-text-secondary)' }}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Pin className="w-4.5 h-4.5" />
|
|
||||||
Pinned
|
|
||||||
</div>
|
|
||||||
<ChevronRight className={`w-4 h-4 transition-transform duration-200 ${pinnedExpanded ? 'rotate-90' : ''}`} />
|
|
||||||
</button>
|
|
||||||
<AnimatePresence>
|
|
||||||
{pinnedExpanded && (
|
|
||||||
<motion.div
|
|
||||||
initial={{ height: 0, opacity: 0 }}
|
|
||||||
animate={{ height: 'auto', opacity: 1 }}
|
|
||||||
exit={{ height: 0, opacity: 0 }}
|
|
||||||
className="overflow-hidden flex flex-col gap-0.5 mt-1"
|
|
||||||
>
|
|
||||||
{pinnedFiles.length === 0 ? (
|
|
||||||
<div className="px-10 py-2 text-xs" style={{ color: 'var(--color-text-tertiary)' }}>No pinned items</div>
|
|
||||||
) : (
|
|
||||||
pinnedFiles.map((file) => (
|
|
||||||
<div
|
|
||||||
key={file.path}
|
|
||||||
data-file-item
|
|
||||||
draggable
|
|
||||||
onDragStart={(e) => {
|
|
||||||
e.dataTransfer.setData('text/plain', file.path);
|
|
||||||
}}
|
|
||||||
onDragOver={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setDragOverPinnedItem(file.path);
|
|
||||||
}}
|
|
||||||
onDragLeave={() => {
|
|
||||||
setDragOverPinnedItem(null);
|
|
||||||
}}
|
|
||||||
onDrop={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setDragOverPinnedItem(null);
|
|
||||||
const source = e.dataTransfer.getData('text/plain');
|
|
||||||
if (source && source !== file.path) {
|
|
||||||
handlePinnedDragEnd(source, file.path);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="group flex items-center justify-between px-10 py-2 text-sm font-medium cursor-pointer rounded-lg transition-colors hover:bg-white/5 gap-3"
|
|
||||||
style={{
|
|
||||||
backgroundColor: activeSection === 'files' && currentPath === file.path ? 'var(--color-accent-subtle)' : 'transparent',
|
|
||||||
color: activeSection === 'files' && currentPath === file.path ? 'var(--color-accent)' : 'var(--color-text-secondary)',
|
|
||||||
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)}
|
|
||||||
</div>
|
|
||||||
<span className="truncate flex-1" title={getFileDisplayName(file.name, file.is_dir)}>{getFileDisplayName(file.name, file.is_dir)}</span>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{[
|
{[
|
||||||
{ id: 'shared' as SidebarSection, icon: LinkIcon, label: 'Shared Files' },
|
{ id: 'shared' as SidebarSection, icon: LinkIcon, label: 'Shared Files' },
|
||||||
|
|
@ -1532,6 +1541,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
<FileText className="w-4.5 h-4.5" />
|
<FileText className="w-4.5 h-4.5" />
|
||||||
Recent Documents
|
Recent Documents
|
||||||
</button>
|
</button>
|
||||||
|
{renderPinnedSidebarSection('.docx')}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -1549,6 +1559,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
<Presentation className="w-4.5 h-4.5" />
|
<Presentation className="w-4.5 h-4.5" />
|
||||||
Recent Presentations
|
Recent Presentations
|
||||||
</button>
|
</button>
|
||||||
|
{renderPinnedSidebarSection('.pptx')}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
Reference in a new issue