feat: resizeable sidebar
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { ActivityBar, Sidebar, Editor, StatusBar, Panel, TabBar, ToastContainer, showToast } from './components';
|
||||
import { ActivityBar, Sidebar, Editor, StatusBar, Panel, TabBar, ToastContainer, showToast, ResizablePanel } from './components';
|
||||
import { useAppStore, PostData, MediaData, TaskProgress, TabState } from './store';
|
||||
import './App.css';
|
||||
|
||||
@@ -357,11 +357,24 @@ const App: React.FC = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { sidebarVisible } = useAppStore();
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<div className="app-main">
|
||||
<ActivityBar />
|
||||
<Sidebar />
|
||||
{sidebarVisible && (
|
||||
<ResizablePanel
|
||||
direction="horizontal"
|
||||
initialSize={280}
|
||||
minSize={200}
|
||||
maxSize={500}
|
||||
storageKey="sidebar-width"
|
||||
resizerPosition="end"
|
||||
>
|
||||
<Sidebar />
|
||||
</ResizablePanel>
|
||||
)}
|
||||
<div className="app-content">
|
||||
<TabBar />
|
||||
<Editor />
|
||||
|
||||
@@ -35,7 +35,7 @@ const SyncIcon = () => (
|
||||
);
|
||||
|
||||
export const ActivityBar: React.FC = () => {
|
||||
const { activeView, setActiveView, syncStatus, pendingChanges, openTab, tabs, activeTabId } = useAppStore();
|
||||
const { activeView, setActiveView, sidebarVisible, toggleSidebar, syncStatus, pendingChanges, openTab, tabs, activeTabId } = useAppStore();
|
||||
|
||||
const totalPending = pendingChanges.posts + pendingChanges.media;
|
||||
|
||||
@@ -45,6 +45,23 @@ export const ActivityBar: React.FC = () => {
|
||||
// Check if tags tab is currently active
|
||||
const isTagsTabActive = tabs.some(t => t.type === 'tags' && t.id === activeTabId);
|
||||
|
||||
// Handle view click - toggle sidebar if clicking on active view, otherwise switch view
|
||||
const handleViewClick = (view: 'posts' | 'media') => {
|
||||
if (activeView === view && sidebarVisible) {
|
||||
// Clicking on active view toggles sidebar off
|
||||
toggleSidebar();
|
||||
} else if (activeView === view && !sidebarVisible) {
|
||||
// Clicking on active view when hidden shows it
|
||||
toggleSidebar();
|
||||
} else {
|
||||
// Switching to a different view - ensure sidebar is visible
|
||||
if (!sidebarVisible) {
|
||||
toggleSidebar();
|
||||
}
|
||||
setActiveView(view);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSettingsClick = () => {
|
||||
// Open settings as a dedicated (non-transient) tab
|
||||
openTab({ type: 'settings', id: 'settings', isTransient: false });
|
||||
@@ -59,16 +76,16 @@ export const ActivityBar: React.FC = () => {
|
||||
<div className="activity-bar">
|
||||
<div className="activity-bar-top">
|
||||
<button
|
||||
className={`activity-bar-item ${activeView === 'posts' ? 'active' : ''}`}
|
||||
onClick={() => setActiveView('posts')}
|
||||
title="Posts"
|
||||
className={`activity-bar-item ${activeView === 'posts' && sidebarVisible ? 'active' : ''}`}
|
||||
onClick={() => handleViewClick('posts')}
|
||||
title="Posts (click again to toggle sidebar)"
|
||||
>
|
||||
<PostsIcon />
|
||||
</button>
|
||||
<button
|
||||
className={`activity-bar-item ${activeView === 'media' ? 'active' : ''}`}
|
||||
onClick={() => setActiveView('media')}
|
||||
title="Media"
|
||||
className={`activity-bar-item ${activeView === 'media' && sidebarVisible ? 'active' : ''}`}
|
||||
onClick={() => handleViewClick('media')}
|
||||
title="Media (click again to toggle sidebar)"
|
||||
>
|
||||
<MediaIcon />
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--vscode-sideBar-background);
|
||||
border-right: 1px solid var(--vscode-sideBar-border);
|
||||
|
||||
@@ -45,6 +45,29 @@
|
||||
background-color: var(--vscode-toolbar-hoverBackground, rgba(90, 93, 94, 0.31));
|
||||
}
|
||||
|
||||
/* Sidebar toggle button */
|
||||
.tab-bar-toggle-sidebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 35px;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-right: 1px solid var(--vscode-tab-border, #252526);
|
||||
color: var(--vscode-icon-foreground, #c5c5c5);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab-bar-toggle-sidebar:hover {
|
||||
background-color: var(--vscode-toolbar-hoverBackground, rgba(90, 93, 94, 0.31));
|
||||
}
|
||||
|
||||
.tab-bar-toggle-sidebar:active {
|
||||
background-color: var(--vscode-toolbar-activeBackground, rgba(99, 102, 103, 0.31));
|
||||
}
|
||||
|
||||
.tab-scroll-left {
|
||||
border-right: 1px solid var(--vscode-tab-border, #252526);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ export const TabBar: React.FC = () => {
|
||||
posts,
|
||||
media,
|
||||
dirtyPosts,
|
||||
sidebarVisible,
|
||||
toggleSidebar,
|
||||
setActiveTab,
|
||||
closeTab,
|
||||
pinTab,
|
||||
@@ -138,7 +140,7 @@ export const TabBar: React.FC = () => {
|
||||
}
|
||||
}, [activeTabId]);
|
||||
|
||||
// Keyboard shortcut handler (Ctrl+W to close active tab)
|
||||
// Keyboard shortcut handler (Ctrl+W to close active tab, Ctrl+B to toggle sidebar)
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'w') {
|
||||
@@ -147,11 +149,15 @@ export const TabBar: React.FC = () => {
|
||||
closeTab(activeTabId);
|
||||
}
|
||||
}
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
|
||||
e.preventDefault();
|
||||
toggleSidebar();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [activeTabId, closeTab]);
|
||||
}, [activeTabId, closeTab, toggleSidebar]);
|
||||
|
||||
if (tabs.length === 0) {
|
||||
return null;
|
||||
@@ -197,6 +203,18 @@ export const TabBar: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="tab-bar">
|
||||
<button
|
||||
className="tab-bar-toggle-sidebar"
|
||||
onClick={toggleSidebar}
|
||||
title={`${sidebarVisible ? 'Hide' : 'Show'} Sidebar (Ctrl+B)`}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M0 1h16v14H0V1zm1 1v12h4V2H1zm5 0v12h9V2H6z"/>
|
||||
{!sidebarVisible && <path d="M2 4h2v1H2V4zm0 2h2v1H2V6zm0 2h2v1H2V8z" opacity="0.5"/>}
|
||||
{sidebarVisible && <path d="M2 4h2v1H2V4zm0 2h2v1H2V6zm0 2h2v1H2V8z"/>}
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{showLeftArrow && (
|
||||
<button
|
||||
className="tab-scroll-button tab-scroll-left"
|
||||
|
||||
Reference in New Issue
Block a user