feat: resizeable sidebar
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect } from 'react';
|
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 { useAppStore, PostData, MediaData, TaskProgress, TabState } from './store';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
@@ -357,11 +357,24 @@ const App: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const { sidebarVisible } = useAppStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<div className="app-main">
|
<div className="app-main">
|
||||||
<ActivityBar />
|
<ActivityBar />
|
||||||
<Sidebar />
|
{sidebarVisible && (
|
||||||
|
<ResizablePanel
|
||||||
|
direction="horizontal"
|
||||||
|
initialSize={280}
|
||||||
|
minSize={200}
|
||||||
|
maxSize={500}
|
||||||
|
storageKey="sidebar-width"
|
||||||
|
resizerPosition="end"
|
||||||
|
>
|
||||||
|
<Sidebar />
|
||||||
|
</ResizablePanel>
|
||||||
|
)}
|
||||||
<div className="app-content">
|
<div className="app-content">
|
||||||
<TabBar />
|
<TabBar />
|
||||||
<Editor />
|
<Editor />
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const SyncIcon = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const ActivityBar: React.FC = () => {
|
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;
|
const totalPending = pendingChanges.posts + pendingChanges.media;
|
||||||
|
|
||||||
@@ -45,6 +45,23 @@ export const ActivityBar: React.FC = () => {
|
|||||||
// Check if tags tab is currently active
|
// Check if tags tab is currently active
|
||||||
const isTagsTabActive = tabs.some(t => t.type === 'tags' && t.id === activeTabId);
|
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 = () => {
|
const handleSettingsClick = () => {
|
||||||
// Open settings as a dedicated (non-transient) tab
|
// Open settings as a dedicated (non-transient) tab
|
||||||
openTab({ type: 'settings', id: 'settings', isTransient: false });
|
openTab({ type: 'settings', id: 'settings', isTransient: false });
|
||||||
@@ -59,16 +76,16 @@ export const ActivityBar: React.FC = () => {
|
|||||||
<div className="activity-bar">
|
<div className="activity-bar">
|
||||||
<div className="activity-bar-top">
|
<div className="activity-bar-top">
|
||||||
<button
|
<button
|
||||||
className={`activity-bar-item ${activeView === 'posts' ? 'active' : ''}`}
|
className={`activity-bar-item ${activeView === 'posts' && sidebarVisible ? 'active' : ''}`}
|
||||||
onClick={() => setActiveView('posts')}
|
onClick={() => handleViewClick('posts')}
|
||||||
title="Posts"
|
title="Posts (click again to toggle sidebar)"
|
||||||
>
|
>
|
||||||
<PostsIcon />
|
<PostsIcon />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`activity-bar-item ${activeView === 'media' ? 'active' : ''}`}
|
className={`activity-bar-item ${activeView === 'media' && sidebarVisible ? 'active' : ''}`}
|
||||||
onClick={() => setActiveView('media')}
|
onClick={() => handleViewClick('media')}
|
||||||
title="Media"
|
title="Media (click again to toggle sidebar)"
|
||||||
>
|
>
|
||||||
<MediaIcon />
|
<MediaIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.sidebar {
|
.sidebar {
|
||||||
width: 280px;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--vscode-sideBar-background);
|
background-color: var(--vscode-sideBar-background);
|
||||||
border-right: 1px solid var(--vscode-sideBar-border);
|
border-right: 1px solid var(--vscode-sideBar-border);
|
||||||
|
|||||||
@@ -45,6 +45,29 @@
|
|||||||
background-color: var(--vscode-toolbar-hoverBackground, rgba(90, 93, 94, 0.31));
|
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 {
|
.tab-scroll-left {
|
||||||
border-right: 1px solid var(--vscode-tab-border, #252526);
|
border-right: 1px solid var(--vscode-tab-border, #252526);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ export const TabBar: React.FC = () => {
|
|||||||
posts,
|
posts,
|
||||||
media,
|
media,
|
||||||
dirtyPosts,
|
dirtyPosts,
|
||||||
|
sidebarVisible,
|
||||||
|
toggleSidebar,
|
||||||
setActiveTab,
|
setActiveTab,
|
||||||
closeTab,
|
closeTab,
|
||||||
pinTab,
|
pinTab,
|
||||||
@@ -138,7 +140,7 @@ export const TabBar: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [activeTabId]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'w') {
|
if ((e.ctrlKey || e.metaKey) && e.key === 'w') {
|
||||||
@@ -147,11 +149,15 @@ export const TabBar: React.FC = () => {
|
|||||||
closeTab(activeTabId);
|
closeTab(activeTabId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleSidebar();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||||
}, [activeTabId, closeTab]);
|
}, [activeTabId, closeTab, toggleSidebar]);
|
||||||
|
|
||||||
if (tabs.length === 0) {
|
if (tabs.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -197,6 +203,18 @@ export const TabBar: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tab-bar">
|
<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 && (
|
{showLeftArrow && (
|
||||||
<button
|
<button
|
||||||
className="tab-scroll-button tab-scroll-left"
|
className="tab-scroll-button tab-scroll-left"
|
||||||
|
|||||||
Reference in New Issue
Block a user