mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(web): add mobile responsive layout with bottom navigation
Add useMobile hook for viewport detection (<768px). Update AppLayout with hamburger menu, slide-over sidebar overlay, and fixed bottom navigation bar (Tools, Automate, Files, Settings) for mobile. Update Sidebar with expanded mode for mobile overlay. Update ToolPage to stack settings above dropzone on mobile with collapsible panel. Add new routes for /automate and /fullscreen. Integrate Settings dialog and keyboard shortcuts at the App root level.
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
FolderOpen,
|
||||
HelpCircle,
|
||||
Settings,
|
||||
Grid3x3,
|
||||
} from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
|
||||
@@ -18,7 +19,7 @@ interface SidebarItem {
|
||||
|
||||
const topItems: SidebarItem[] = [
|
||||
{ icon: LayoutGrid, label: "Tools", href: "/" },
|
||||
{ icon: BookOpen, label: "Reader", href: "/reader" },
|
||||
{ icon: Grid3x3, label: "Grid", href: "/fullscreen" },
|
||||
{ icon: Workflow, label: "Automate", href: "/automate" },
|
||||
{ icon: FolderOpen, label: "Files", href: "/files" },
|
||||
];
|
||||
@@ -30,13 +31,27 @@ const bottomItems: SidebarItem[] = [
|
||||
|
||||
interface SidebarProps {
|
||||
onSettingsClick: () => void;
|
||||
/** When true, renders in expanded mode (for mobile overlay). */
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
export function Sidebar({ onSettingsClick }: SidebarProps) {
|
||||
export function Sidebar({ onSettingsClick, expanded = false }: SidebarProps) {
|
||||
const location = useLocation();
|
||||
|
||||
const renderItem = (item: SidebarItem, isActive: boolean) => {
|
||||
const content = (
|
||||
const content = expanded ? (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-4 py-2.5 rounded-lg cursor-pointer transition-colors",
|
||||
isActive
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
<item.icon className="h-5 w-5" />
|
||||
<span className="text-sm font-medium">{item.label}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-1 p-2 rounded-lg cursor-pointer transition-colors",
|
||||
@@ -64,6 +79,18 @@ export function Sidebar({ onSettingsClick }: SidebarProps) {
|
||||
);
|
||||
};
|
||||
|
||||
if (expanded) {
|
||||
return (
|
||||
<div className="flex flex-col p-3 gap-1">
|
||||
{topItems.map((item) =>
|
||||
renderItem(item, location.pathname === item.href)
|
||||
)}
|
||||
<div className="border-t border-border my-2" />
|
||||
{bottomItems.map((item) => renderItem(item, false))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="flex flex-col items-center w-16 bg-sidebar border-r border-border py-3 gap-1 shrink-0">
|
||||
<div className="flex flex-col gap-1 flex-1">
|
||||
|
||||
Reference in New Issue
Block a user