Skip to content

Commit

Permalink
feat: add initial dashboard layout
Browse files Browse the repository at this point in the history
  • Loading branch information
dinkelspiel committed Apr 25, 2024
1 parent decdc35 commit 8a26651
Show file tree
Hide file tree
Showing 9 changed files with 3,588 additions and 3,935 deletions.
20 changes: 18 additions & 2 deletions app/(app)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import React from 'react';
import {
Header,
HeaderDescription,
HeaderHeader,
HeaderTitle,
} from '@/components/header';

const Page = () => {
return <div>Page</div>;
return (
<>
<Header>
<HeaderHeader>
<HeaderTitle>My Media</HeaderTitle>
<HeaderDescription>
Search through your entire media catalogue
</HeaderDescription>
</HeaderHeader>
</Header>
</>
);
};

export default Page;
14 changes: 11 additions & 3 deletions app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Sidebar from '@/components/sidebar';
import { Sidebar, SidebarButton, SidebarFooter } from '@/components/sidebar';
import { Toaster } from '@/components/ui/sonner';
import { CornerDownLeft, Home, Shield, UsersRound } from 'lucide-react';
import React, { ReactNode } from 'react';

const Layout = ({ children }: { children: ReactNode }) => {
Expand All @@ -7,8 +9,14 @@ const Layout = ({ children }: { children: ReactNode }) => {
<body
className={`grid grid-cols-1 grid-rows-[70px,1fr] lg:grid-rows-1 lg:grid-cols-[256px,1fr] min-h-[100dvh]`}
>
<Sidebar />
{children}
<Sidebar header={<>Medialog</>}>
<SidebarButton href="/dashboard">
<Home size={20} />
Home
</SidebarButton>
</Sidebar>

<main className="px-6 py-4 flex flex-col gap-4">{children}</main>
</body>
</html>
);
Expand Down
7 changes: 7 additions & 0 deletions app/(guest)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
File renamed without changes.
16 changes: 5 additions & 11 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Metadata, Viewport } from "next";
import "../styles/globals.css";
import { Metadata, Viewport } from 'next';
import '../styles/globals.css';

export default function RootLayout({
// Layouts must accept a children prop.
Expand All @@ -8,21 +8,15 @@ export default function RootLayout({
}: {
children: React.ReactNode;
}) {
return (
<>
<html lang="en">
<body className="fill-black">{children}</body>
</html>
</>
);
return children;
}

export const viewport: Viewport = {
initialScale: 1,
width: "device-width",
width: 'device-width',
maximumScale: 1,
};

export const metadata: Metadata = {
title: "Medialog",
title: 'Medialog',
};
74 changes: 74 additions & 0 deletions components/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { cn } from '../lib/utils';
import React from 'react';

export const Header = ({
className,
children,
...props
}: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div className={cn('flex flex-row', className)} {...props}>
{children}
</div>
);
};

export const HeaderHeader = ({
className,
children,
...props
}: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div className={cn('flex flex-col xl:w-max w-full', className)} {...props}>
{children}
</div>
);
};

export const HeaderTitle = ({
className,
children,
...props
}: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
className={cn(
'xl:w-max w-full scroll-m-20 text-2xl font-semibold tracking-tight',
className
)}
{...props}
>
{children}
</div>
);
};

export const HeaderDescription = ({
className,
children,
...props
}: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
className={cn('xl:w-max w-full text-muted-foreground', className)}
{...props}
>
{children}
</div>
);
};

export const HeaderContent = ({
className,
children,
...props
}: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
className={cn('flex justify-end w-full items-center gap-2', className)}
{...props}
>
{children}
</div>
);
};
128 changes: 124 additions & 4 deletions components/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,127 @@
import React from 'react';
'use client';

const Sidebar = () => {
return <div>Sidebar</div>;
import { cn } from '../lib/utils';
import React, { ReactNode, useState } from 'react';
import Link from 'next/link';
import { Menu } from 'lucide-react';
import { Sheet, SheetContent } from './ui/sheet';
import { Button } from './ui/button';
import { usePathname } from 'next/navigation';

export const Sidebar = ({
children,
className,
header,
headerProps,
...props
}: React.HTMLAttributes<HTMLDivElement> & {
header: ReactNode;
headerProps?: React.HTMLAttributes<HTMLDivElement>;
}) => {
const [sheetOpen, setSheetOpen] = useState(false);

return (
<div
id="sidebar"
className={cn(
`z-40 h-auto lg:h-[100dvh] sticky top-0 w-full transition-transform`,
className
)}
{...props}
aria-label="Sidebar"
>
<div className="flex bg-neutral-100 shadow-[inset_0_0px_8px_0_rgb(0_0_0_/_0.02)] h-[75px] justify-center lg:justify-start border-b lg:border-b-0 lg:h-full flex-col overflow-y-auto border-slate-200 px-3 py-4 dark:border-slate-700 dark:bg-slate-900">
<SidebarHeader {...headerProps}>
{header}
<button className="lg:hidden w-full justify-end flex">
<div className="sr-only">Öppna meny</div>
<Menu onClick={() => setSheetOpen(true)} className="w-5 h-5" />
</button>
</SidebarHeader>
<ul className="space-y-2 text-sm font-medium flex-col h-full lg:flex hidden">
{children}
</ul>
<Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
<SheetContent className="list-none pt-12 gap-2 flex flex-col bg-neutral-100">
{children}
</SheetContent>
</Sheet>
</div>
</div>
);
};

export default Sidebar;
export const SidebarHeader = ({
children,
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
`lg:mb-12 flex whitespace-nowrap items-center rounded-lg px-3 py-2 text-slate-900 dark:text-white gap-3 text-base font-semibold [&>svg]:size-5`,
className
)}
{...props}
>
{children}
</div>
);

export const SidebarButton = ({
children,
href,
selectedVariant,
...props
}: React.HTMLAttributes<HTMLAnchorElement> & {
href: string;
selectedVariant?: string;
}) => (
<li>
<Link href={href} {...props}>
<ClientSidebarButton selectedVariant={selectedVariant} href={href}>
{children}
</ClientSidebarButton>
</Link>
</li>
);

export const SidebarFooter = ({
children,
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(`flex-1 flex flex-col justify-end gap-2`, className)}
{...props}
>
{children}
</div>
);

const ClientSidebarButton = ({
children,
className,
href,
selectedVariant,
...props
}: React.HTMLAttributes<HTMLButtonElement> & {
href: string;
selectedVariant?: string;
}) => {
const pathname = usePathname();

return (
<Button
variant={
pathname.endsWith(href)
? (selectedVariant as any) ?? 'default'
: 'ghost'
}
className={cn(`w-full justify-start select-none`, className)}
tabIndex={-1}
{...props}
>
{children}
</Button>
);
};
43 changes: 21 additions & 22 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';

import { cn } from "@/lib/utils";
import { cn } from '../../lib/utils';

const buttonVariants = cva(
"inline-flex gap-2 items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
'gap-3 [&>svg]:size-5 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground fill-primary-foreground shadow hover:bg-primary/90",
default: 'bg-white text-black hover:bg-white/70 shadow-sm',
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground fill-black",
link: "text-primary underline-offset-4 hover:underline",
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: "default",
size: "default",
variant: 'default',
size: 'default',
},
},
}
);

export interface ButtonProps
Expand All @@ -42,16 +41,16 @@ export interface ButtonProps

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
}
);
Button.displayName = "Button";
Button.displayName = 'Button';

export { Button, buttonVariants };
Loading

0 comments on commit 8a26651

Please sign in to comment.