95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
import { type Component, type JSX, createSignal, Show, lazy, Suspense, onMount } from "solid-js";
|
|
import { Sidebar, BottomNav } from "./Navigation";
|
|
import { store, activeTaskId, setActiveTaskId } from "@/store";
|
|
import { PanelLeftOpen } from "lucide-solid";
|
|
|
|
const TaskDetail = lazy(() => import("./TaskDetail").then(m => ({ default: m.TaskDetail })));
|
|
const QuickEntry = lazy(() => import("./QuickEntry").then(m => ({ default: m.QuickEntry })));
|
|
import { Button } from "./ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
import { FilterBar } from "./FilterBar";
|
|
|
|
interface LayoutProps {
|
|
children: JSX.Element;
|
|
currentView: string;
|
|
setView: (v: string) => void;
|
|
}
|
|
|
|
export const Layout: Component<LayoutProps> = (props) => {
|
|
const [isSidebarLocked, setIsSidebarLocked] = createSignal(true);
|
|
const [isSidebarPeeking, setIsSidebarPeeking] = createSignal(false);
|
|
|
|
onMount(() => {
|
|
// Preload heavy components in background when idle
|
|
const idleCallback = (window as any).requestIdleCallback || ((cb: any) => setTimeout(cb, 2000));
|
|
idleCallback(() => {
|
|
import("./TaskDetail");
|
|
import("./QuickEntry");
|
|
import("./TaskEditor");
|
|
});
|
|
});
|
|
|
|
// Derived active task for the detail view
|
|
const activeTask = () => store.tasks.find(t => t.id === activeTaskId());
|
|
|
|
return (
|
|
<div class="flex h-screen w-full min-w-0 bg-background overflow-hidden relative font-sans antialiased text-foreground">
|
|
<Sidebar
|
|
currentView={props.currentView}
|
|
setView={props.setView}
|
|
isLocked={isSidebarLocked()}
|
|
setIsLocked={setIsSidebarLocked}
|
|
isPeeking={isSidebarPeeking()}
|
|
setIsPeeking={setIsSidebarPeeking}
|
|
/>
|
|
|
|
<div class={cn(
|
|
"flex-1 flex flex-col h-full transition-all duration-300 ease-in-out relative",
|
|
isSidebarLocked() ? "md:ml-48" : "ml-0"
|
|
)}>
|
|
{/* Expand Trigger Button (visible when not locked) */}
|
|
{!isSidebarLocked() && (
|
|
<div class="fixed top-4 left-4 z-[60] md:flex hidden">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onMouseEnter={() => setIsSidebarPeeking(true)}
|
|
onClick={() => setIsSidebarLocked(true)}
|
|
class="bg-background/80 backdrop-blur-sm border border-border hover:bg-muted shadow-sm"
|
|
>
|
|
<PanelLeftOpen size={18} />
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Main Content Area */}
|
|
<main class="flex-1 min-w-0 overflow-y-auto overflow-x-hidden pb-32 sm:pb-6 relative scroll-smooth">
|
|
<div class="w-full max-w-screen-2xl mx-auto px-4 sm:px-8 lg:px-12 pt-16 sm:pt-20 pb-8 space-y-6 sm:space-y-8">
|
|
<Show when={props.currentView.toLowerCase() !== "settings"}>
|
|
<FilterBar />
|
|
</Show>
|
|
|
|
{props.children}
|
|
</div>
|
|
</main>
|
|
{/* Mobile Bottom Nav */}
|
|
<BottomNav currentView={props.currentView} setView={props.setView} />
|
|
</div>
|
|
|
|
<Suspense>
|
|
<QuickEntry />
|
|
</Suspense>
|
|
|
|
<Show when={activeTask()}>
|
|
<Suspense>
|
|
<TaskDetail
|
|
task={activeTask()!}
|
|
isOpen={!!activeTaskId()}
|
|
onClose={() => setActiveTaskId(null)}
|
|
/>
|
|
</Suspense>
|
|
</Show>
|
|
</div>
|
|
);
|
|
};
|