feat(ui): make desktop sidebar collapsible
Add a header toggle and a panel close button so the desktop file sidebar collapses with a width transition, mirroring the journal panel. - split onClose (close button) from onNavigate (file selection) so clicking a file no longer collapses the sidebar on desktop - give the aside h-full so the file tree and outline keep bounded scroll regions inside the new wrapper - swap the aside shadow for a gray right border to match the journal
This commit is contained in:
+27
-2
@@ -11,6 +11,8 @@ import {
|
||||
TbOutlineMenu2,
|
||||
TbOutlineClipboardList,
|
||||
TbOutlineHelpCircle,
|
||||
TbOutlineLayoutSidebarLeftCollapse,
|
||||
TbOutlineLayoutSidebarLeftExpand,
|
||||
} from "solid-icons/tb";
|
||||
import { useJournalStream } from "./components/stores/journalStream";
|
||||
|
||||
@@ -51,6 +53,7 @@ const App: Component = () => {
|
||||
const location = useLocation();
|
||||
const stream = useJournalStream();
|
||||
const [isSidebarOpen, setIsSidebarOpen] = createSignal(false);
|
||||
const [isDesktopSidebarOpen, setIsDesktopSidebarOpen] = createSignal(true);
|
||||
const [isDocOpen, setIsDocOpen] = createSignal(false);
|
||||
const [isDataSourceOpen, setIsDataSourceOpen] = createSignal(false);
|
||||
const [isJournalOpen, setIsJournalOpen] = createSignal(false);
|
||||
@@ -98,7 +101,7 @@ const App: Component = () => {
|
||||
{/* Header */}
|
||||
<header class="shrink-0 h-16 bg-white shadow z-30">
|
||||
<div class="h-full max-w-4xl mx-auto px-4 flex items-center gap-4">
|
||||
{/* 仅在移动端显示菜单按钮 */}
|
||||
{/* 移动端:打开抽屉式侧边栏 */}
|
||||
<button
|
||||
onClick={() => setIsSidebarOpen(true)}
|
||||
class="md:hidden text-gray-600 hover:text-gray-900 p-1 rounded hover:bg-gray-100"
|
||||
@@ -106,6 +109,18 @@ const App: Component = () => {
|
||||
>
|
||||
<TbOutlineMenu2 class="inline-block align-[-0.125em]" />
|
||||
</button>
|
||||
{/* 桌面端:折叠/展开固定侧边栏 */}
|
||||
<button
|
||||
onClick={() => setIsDesktopSidebarOpen((v) => !v)}
|
||||
class="hidden md:block text-gray-600 hover:text-gray-900 p-1 rounded hover:bg-gray-100"
|
||||
title={isDesktopSidebarOpen() ? "收起目录" : "展开目录"}
|
||||
>
|
||||
{isDesktopSidebarOpen() ? (
|
||||
<TbOutlineLayoutSidebarLeftCollapse class="inline-block align-[-0.125em]" />
|
||||
) : (
|
||||
<TbOutlineLayoutSidebarLeftExpand class="inline-block align-[-0.125em]" />
|
||||
)}
|
||||
</button>
|
||||
<h1 class="text-2xl font-bold text-gray-900">Tabletop Toolkit</h1>
|
||||
<div class="flex-1" />
|
||||
<button
|
||||
@@ -150,12 +165,22 @@ const App: Component = () => {
|
||||
|
||||
{/* Body: sidebar + main + journal */}
|
||||
<div class="flex flex-1 min-h-0">
|
||||
{/* 桌面端固定侧边栏 (flex child) */}
|
||||
{/* 桌面端侧边栏 — flex child,可折叠 */}
|
||||
<div
|
||||
class="hidden md:block md:shrink-0 md:overflow-hidden md:transition-all md:duration-300"
|
||||
classList={{
|
||||
"md:w-64": isDesktopSidebarOpen(),
|
||||
"md:w-0": !isDesktopSidebarOpen(),
|
||||
}}
|
||||
inert={!isDesktopSidebarOpen()}
|
||||
>
|
||||
<DesktopSidebar
|
||||
fileTree={fileTree()}
|
||||
pathHeadings={pathHeadings()}
|
||||
onDataSourceOpen={() => setIsDataSourceOpen(true)}
|
||||
onClose={() => setIsDesktopSidebarOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Main content — the scrollable region */}
|
||||
<ScrollContainerCtx.Provider value={() => mainRef}>
|
||||
|
||||
@@ -18,7 +18,7 @@ interface SidebarContentProps {
|
||||
pathHeadings: Record<string, TocNode[]>;
|
||||
currentPath: string;
|
||||
onClose: () => void;
|
||||
isDesktop?: boolean;
|
||||
onNavigate?: () => void;
|
||||
onDataSourceOpen?: () => void;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ const SidebarContent: Component<SidebarContentProps> = (props) => {
|
||||
>
|
||||
<TbOutlineFolderOpen class="inline-block align-[-0.125em]" />
|
||||
</button>
|
||||
<Show when={!props.isDesktop}>
|
||||
<button
|
||||
onClick={props.onClose}
|
||||
class="text-gray-500 hover:text-gray-700"
|
||||
@@ -68,7 +67,6 @@ const SidebarContent: Component<SidebarContentProps> = (props) => {
|
||||
>
|
||||
<TbOutlineX class="inline-block align-[-0.125em]" />
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,7 +82,7 @@ const SidebarContent: Component<SidebarContentProps> = (props) => {
|
||||
currentPath={props.currentPath}
|
||||
pathHeadings={props.pathHeadings}
|
||||
depth={0}
|
||||
onClose={props.onClose}
|
||||
onClose={() => props.onNavigate?.()}
|
||||
isHidden={isFileHidden}
|
||||
/>
|
||||
))}
|
||||
@@ -151,6 +149,7 @@ export const MobileSidebar: Component<SidebarProps> = (props) => {
|
||||
pathHeadings={pathHeadings()}
|
||||
currentPath={location.pathname}
|
||||
onClose={props.onClose}
|
||||
onNavigate={props.onClose}
|
||||
onDataSourceOpen={props.onDataSourceOpen}
|
||||
/>
|
||||
</aside>
|
||||
@@ -164,6 +163,7 @@ export const MobileSidebar: Component<SidebarProps> = (props) => {
|
||||
export const DesktopSidebar: Component<{
|
||||
fileTree?: FileNode[];
|
||||
pathHeadings?: Record<string, TocNode[]>;
|
||||
onClose: () => void;
|
||||
onDataSourceOpen?: () => void;
|
||||
}> = (props) => {
|
||||
const location = useLocation();
|
||||
@@ -183,13 +183,12 @@ export const DesktopSidebar: Component<{
|
||||
});
|
||||
|
||||
return (
|
||||
<aside class="hidden md:flex flex-col w-64 shrink-0 bg-white shadow-lg overflow-hidden">
|
||||
<aside class="hidden md:flex flex-col h-full w-64 shrink-0 bg-white border-r border-gray-200 overflow-hidden">
|
||||
<SidebarContent
|
||||
fileTree={fileTree()}
|
||||
pathHeadings={pathHeadings()}
|
||||
currentPath={location.pathname}
|
||||
onClose={() => {}}
|
||||
isDesktop
|
||||
onClose={props.onClose}
|
||||
onDataSourceOpen={props.onDataSourceOpen}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user