mirror of
https://codeberg.org/MarkusThielker/finances.git
synced 2025-04-12 05:08:43 +00:00
N-FIN-34: navigation becomes drawer on small screens (#37)
Resolves #34
This commit is contained in:
commit
a90eb64301
6 changed files with 241 additions and 44 deletions
13
package-lock.json
generated
13
package-lock.json
generated
|
@ -40,6 +40,7 @@
|
||||||
"swr": "^2.2.5",
|
"swr": "^2.2.5",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"vaul": "^0.9.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -6184,6 +6185,18 @@
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/vaul": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vaul/-/vaul-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-bZSySGbAHiTXmZychprnX/dE0EsSige88xtyyL3/MCRbrFotRPQZo7UdydGXZWw+CKbNOw5Ow8gwAo93/nB/Cg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-dialog": "^1.0.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
"swr": "^2.2.5",
|
"swr": "^2.2.5",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"vaul": "^0.9.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default function DashboardPageClientContent(
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className="flex flex-col space-y-4">
|
<div className="flex flex-col space-y-4">
|
||||||
<div className="flex items-center justify-between w-full">
|
<div className="flex items-center justify-between w-full space-x-8">
|
||||||
|
|
||||||
<p className="text-3xl font-semibold">Dashboard</p>
|
<p className="text-3xl font-semibold">Dashboard</p>
|
||||||
|
|
||||||
|
|
|
@ -2,65 +2,125 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NavigationMenu,
|
NavigationMenu,
|
||||||
|
navigationMenuIconTriggerStyle,
|
||||||
NavigationMenuItem,
|
NavigationMenuItem,
|
||||||
NavigationMenuLink,
|
NavigationMenuLink,
|
||||||
NavigationMenuList,
|
NavigationMenuList,
|
||||||
navigationMenuTriggerStyle,
|
navigationMenuTriggerStyle,
|
||||||
} from '@/components/ui/navigation-menu';
|
} from '@/components/ui/navigation-menu';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { User } from 'lucide-react';
|
import { Banknote, Home, Menu, Tag, User, UserSearch } from 'lucide-react';
|
||||||
|
import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
export default function Navigation() {
|
export default function Navigation() {
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex sticky items-center border-b border-border bg-background">
|
<div className="flex sticky items-center border-b border-border bg-background">
|
||||||
<NavigationMenu>
|
<div className="md:hidden">
|
||||||
<NavigationMenuList className="flex w-screen items-center justify-between sm:px-4 py-2">
|
<Drawer open={open} onOpenChange={open => setOpen(open)}>
|
||||||
<div className="inline-flex space-x-2">
|
<DrawerTrigger asChild>
|
||||||
|
<Button size="icon" variant="ghost" className="m-2">
|
||||||
<img src={'/logo_white.png'} alt="Finances" className="h-10 w-10 mx-3"/>
|
<Menu/>
|
||||||
|
</Button>
|
||||||
<NavigationMenuItem>
|
</DrawerTrigger>
|
||||||
<Link href="/" legacyBehavior passHref>
|
<DrawerContent>
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
<div className="flex flex-col space-y-2 w-full rounded-none p-4">
|
||||||
Dashboard
|
<Link
|
||||||
</NavigationMenuLink>
|
href="/"
|
||||||
|
className={navigationMenuIconTriggerStyle()}
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
passHref>
|
||||||
|
<Home/>
|
||||||
|
<span>Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenuItem>
|
<Link
|
||||||
<NavigationMenuItem>
|
href="/payments"
|
||||||
<Link href="/payments" legacyBehavior passHref>
|
className={navigationMenuIconTriggerStyle()}
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
onClick={() => setOpen(false)}
|
||||||
Payments
|
passHref>
|
||||||
</NavigationMenuLink>
|
<Banknote/>
|
||||||
|
<span>Payments</span>
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenuItem>
|
<Link
|
||||||
<NavigationMenuItem>
|
href="/entities"
|
||||||
<Link href="/entities" legacyBehavior passHref>
|
className={navigationMenuIconTriggerStyle()}
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
onClick={() => setOpen(false)}
|
||||||
Entities
|
passHref>
|
||||||
</NavigationMenuLink>
|
<UserSearch/>
|
||||||
|
<span>Entities</span>
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenuItem>
|
<Link
|
||||||
<NavigationMenuItem>
|
href="/categories"
|
||||||
<Link href="/categories" legacyBehavior passHref>
|
className={navigationMenuIconTriggerStyle()}
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
onClick={() => setOpen(false)}
|
||||||
Categories
|
passHref>
|
||||||
</NavigationMenuLink>
|
<Tag/>
|
||||||
|
<span>Categories</span>
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenuItem>
|
<Link
|
||||||
</div>
|
href="/account"
|
||||||
|
className={navigationMenuIconTriggerStyle()}
|
||||||
<NavigationMenuItem>
|
onClick={() => setOpen(false)}
|
||||||
<Link href="/account" legacyBehavior passHref>
|
passHref>
|
||||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
|
||||||
<span className="sr-only">Account</span>
|
|
||||||
<User/>
|
<User/>
|
||||||
</NavigationMenuLink>
|
<span>Account</span>
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenuItem>
|
</div>
|
||||||
</NavigationMenuList>
|
</DrawerContent>
|
||||||
</NavigationMenu>
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
<div className="hidden md:flex">
|
||||||
|
<NavigationMenu>
|
||||||
|
<NavigationMenuList className="flex w-screen items-center justify-between sm:px-4 py-2">
|
||||||
|
<div className="inline-flex space-x-2">
|
||||||
|
|
||||||
|
<img src={'/logo_white.png'} alt="Finances" className="h-10 w-10 mx-3"/>
|
||||||
|
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<Link href="/" legacyBehavior passHref>
|
||||||
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
|
Dashboard
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<Link href="/payments" legacyBehavior passHref>
|
||||||
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
|
Payments
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<Link href="/entities" legacyBehavior passHref>
|
||||||
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
|
Entities
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<Link href="/categories" legacyBehavior passHref>
|
||||||
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
|
Categories
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<Link href="/account" legacyBehavior passHref>
|
||||||
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
|
<span className="sr-only">Account</span>
|
||||||
|
<User/>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
</NavigationMenuList>
|
||||||
|
</NavigationMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
118
src/components/ui/drawer.tsx
Normal file
118
src/components/ui/drawer.tsx
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Drawer as DrawerPrimitive } from 'vaul';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Drawer = ({
|
||||||
|
shouldScaleBackground = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
||||||
|
<DrawerPrimitive.Root
|
||||||
|
shouldScaleBackground={shouldScaleBackground}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
Drawer.displayName = 'Drawer';
|
||||||
|
|
||||||
|
const DrawerTrigger = DrawerPrimitive.Trigger;
|
||||||
|
|
||||||
|
const DrawerPortal = DrawerPrimitive.Portal;
|
||||||
|
|
||||||
|
const DrawerClose = DrawerPrimitive.Close;
|
||||||
|
|
||||||
|
const DrawerOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
||||||
|
>(({className, ...props}, ref) => (
|
||||||
|
<DrawerPrimitive.Overlay
|
||||||
|
ref={ref}
|
||||||
|
className={cn('fixed inset-0 z-50 bg-black/80', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
||||||
|
|
||||||
|
const DrawerContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||||
|
>(({className, children, ...props}, ref) => (
|
||||||
|
<DrawerPortal>
|
||||||
|
<DrawerOverlay/>
|
||||||
|
<DrawerPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted"/>
|
||||||
|
{children}
|
||||||
|
</DrawerPrimitive.Content>
|
||||||
|
</DrawerPortal>
|
||||||
|
));
|
||||||
|
DrawerContent.displayName = 'DrawerContent';
|
||||||
|
|
||||||
|
const DrawerHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn('grid gap-1.5 p-4 text-center sm:text-left', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
DrawerHeader.displayName = 'DrawerHeader';
|
||||||
|
|
||||||
|
const DrawerFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
DrawerFooter.displayName = 'DrawerFooter';
|
||||||
|
|
||||||
|
const DrawerTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
||||||
|
>(({className, ...props}, ref) => (
|
||||||
|
<DrawerPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'text-lg font-semibold leading-none tracking-tight',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
||||||
|
|
||||||
|
const DrawerDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||||
|
>(({className, ...props}, ref) => (
|
||||||
|
<DrawerPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn('text-sm text-muted-foreground', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Drawer,
|
||||||
|
DrawerPortal,
|
||||||
|
DrawerOverlay,
|
||||||
|
DrawerTrigger,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerFooter,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerDescription,
|
||||||
|
};
|
|
@ -44,6 +44,10 @@ const navigationMenuTriggerStyle = cva(
|
||||||
'group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
|
'group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const navigationMenuIconTriggerStyle = cva(
|
||||||
|
'group inline-flex h-10 w-full items-center justify-start rounded-md bg-background px-4 py-2 space-x-4 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
|
||||||
|
);
|
||||||
|
|
||||||
const NavigationMenuTrigger = React.forwardRef<
|
const NavigationMenuTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||||
|
@ -117,6 +121,7 @@ NavigationMenuIndicator.displayName =
|
||||||
|
|
||||||
export {
|
export {
|
||||||
navigationMenuTriggerStyle,
|
navigationMenuTriggerStyle,
|
||||||
|
navigationMenuIconTriggerStyle,
|
||||||
NavigationMenu,
|
NavigationMenu,
|
||||||
NavigationMenuList,
|
NavigationMenuList,
|
||||||
NavigationMenuItem,
|
NavigationMenuItem,
|
||||||
|
|
Loading…
Add table
Reference in a new issue