feat: implement new ui

This commit is contained in:
shadcn 2023-06-07 16:17:59 +04:00
parent 4d5af8c62a
commit 6293470332
28 changed files with 1417 additions and 584 deletions

137
components/icons.tsx Normal file
View file

@ -0,0 +1,137 @@
'use client'
import * as React from 'react'
import { cn } from '@/lib/utils'
export function NextChat({
className,
inverted,
...props
}: React.ComponentProps<'svg'> & { inverted?: boolean }) {
const id = React.useId()
return (
<svg
viewBox="0 0 17 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={cn('w-4 h-4', className)}
{...props}
>
<defs>
<linearGradient
id={`gradient-${id}-1`}
x1="10.6889"
y1="10.3556"
x2="13.8445"
y2="14.2667"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={inverted ? 'white' : 'black'} />
<stop
offset={1}
stopColor={inverted ? 'white' : 'black'}
stopOpacity={0}
/>
</linearGradient>
<linearGradient
id={`gradient-${id}-2`}
x1="11.7555"
y1="4.8"
x2="11.7376"
y2="9.50002"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={inverted ? 'white' : 'black'} />
<stop
offset={1}
stopColor={inverted ? 'white' : 'black'}
stopOpacity={0}
/>
</linearGradient>
</defs>
<path
d="M1 16L2.58314 11.2506C1.83084 9.74642 1.63835 8.02363 2.04013 6.39052C2.4419 4.75741 3.41171 3.32057 4.776 2.33712C6.1403 1.35367 7.81003 0.887808 9.4864 1.02289C11.1628 1.15798 12.7364 1.8852 13.9256 3.07442C15.1148 4.26363 15.842 5.83723 15.9771 7.5136C16.1122 9.18997 15.6463 10.8597 14.6629 12.224C13.6794 13.5883 12.2426 14.5581 10.6095 14.9599C8.97637 15.3616 7.25358 15.1692 5.74942 14.4169L1 16Z"
fill={inverted ? 'black' : 'white'}
stroke={inverted ? 'black' : 'white'}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
<mask
id="mask0_91_2047"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x={1}
y={0}
width={16}
height={16}
>
<circle cx={9} cy={8} r={8} fill={inverted ? 'black' : 'white'} />
</mask>
<g mask="url(#mask0_91_2047)">
<circle cx={9} cy={8} r={8} fill={inverted ? 'black' : 'white'} />
<path
d="M14.2896 14.0018L7.146 4.8H5.80005V11.1973H6.87681V6.16743L13.4444 14.6529C13.7407 14.4545 14.0231 14.2369 14.2896 14.0018Z"
fill={`url(#gradient-${id}-1)`}
/>
<rect
x="11.2222"
y="4.8"
width="1.06667"
height="6.4"
fill={`url(#gradient-${id}-2)`}
/>
</g>
</svg>
)
}
export function OpenAI({ className, ...props }: React.ComponentProps<'svg'>) {
return (
<svg
fill="currentColor"
viewBox="0 0 24 24"
role="img"
xmlns="http://www.w3.org/2000/svg"
className={cn('w-4 h-4', className)}
{...props}
>
<title>OpenAI icon</title>
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
</svg>
)
}
export function Vercel({ className, ...props }: React.ComponentProps<'svg'>) {
return (
<svg
aria-label="Vercel logomark"
role="img"
viewBox="0 0 74 64"
className={cn('w-4 h-4', className)}
{...props}
>
<path
d="M37.5896 0.25L74.5396 64.25H0.639648L37.5896 0.25Z"
fill="currentColor"
></path>
</svg>
)
}
export function GitHub({ className, ...props }: React.ComponentProps<'svg'>) {
return (
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className={cn('w-4 h-4', className)}
{...props}
>
<title>GitHub</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
)
}

View file

@ -0,0 +1,14 @@
export function TailwindIndicator() {
if (process.env.NODE_ENV === 'production') return null
return (
<div className="fixed bottom-1 left-1 z-50 flex h-6 w-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-white">
<div className="block sm:hidden">xs</div>
<div className="hidden sm:block md:hidden">sm</div>
<div className="hidden md:block lg:hidden">md</div>
<div className="hidden lg:block xl:hidden">lg</div>
<div className="hidden xl:block 2xl:hidden">xl</div>
<div className="hidden 2xl:block">2xl</div>
</div>
)
}

View file

@ -12,7 +12,7 @@ export function ThemeToggle() {
return (
<Button
variant="ghost"
size="xs"
size="sm"
className="p-0 leading-4 h-4 font-normal w-full"
onClick={() => {
startTransition(() => {

View file

@ -1,5 +1,6 @@
import * as React from 'react'
import { VariantProps, cva } from 'class-variance-authority'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
@ -21,8 +22,7 @@ const buttonVariants = cva(
size: {
default: 'h-10 py-2 px-4',
sm: 'h-9 px-3 rounded-md',
lg: 'h-11 px-8 rounded-md',
xs: 'h-6 px-3 rounded-md'
lg: 'h-11 px-8 rounded-md'
}
},
defaultVariants: {
@ -34,12 +34,15 @@ const buttonVariants = cva(
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button'
return (
<button
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}

View file

@ -1,14 +0,0 @@
'use client'
import { signIn } from '@auth/nextjs/client'
export function Login() {
return (
<button
className="inline-flex w-full items-center justify-center rounded border border-zinc-800 bg-white h-8 px-4 -my-1.5 text-sm leading-6 tracking-tight text-zinc-900 transition-colors ease-in-out hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-50"
onClick={() => signIn('github')}
>
<span className="font-medium">Login</span>
</button>
)
}

View file

@ -1,85 +0,0 @@
import { useId } from 'react'
export function NextChatLogo(
props: JSX.IntrinsicElements['svg'] & { inverted?: boolean }
) {
const id = useId()
return (
<svg
width={17}
height={17}
viewBox="0 0 17 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<defs>
<linearGradient
id={`gradient-${id}-1`}
x1="10.6889"
y1="10.3556"
x2="13.8445"
y2="14.2667"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={props.inverted ? 'white' : 'black'} />
<stop
offset={1}
stopColor={props.inverted ? 'white' : 'black'}
stopOpacity={0}
/>
</linearGradient>
<linearGradient
id={`gradient-${id}-2`}
x1="11.7555"
y1="4.8"
x2="11.7376"
y2="9.50002"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={props.inverted ? 'white' : 'black'} />
<stop
offset={1}
stopColor={props.inverted ? 'white' : 'black'}
stopOpacity={0}
/>
</linearGradient>
</defs>
<path
d="M1 16L2.58314 11.2506C1.83084 9.74642 1.63835 8.02363 2.04013 6.39052C2.4419 4.75741 3.41171 3.32057 4.776 2.33712C6.1403 1.35367 7.81003 0.887808 9.4864 1.02289C11.1628 1.15798 12.7364 1.8852 13.9256 3.07442C15.1148 4.26363 15.842 5.83723 15.9771 7.5136C16.1122 9.18997 15.6463 10.8597 14.6629 12.224C13.6794 13.5883 12.2426 14.5581 10.6095 14.9599C8.97637 15.3616 7.25358 15.1692 5.74942 14.4169L1 16Z"
fill={props.inverted ? 'black' : 'white'}
stroke={props.inverted ? 'black' : 'white'}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
<mask
id="mask0_91_2047"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x={1}
y={0}
width={16}
height={16}
>
<circle cx={9} cy={8} r={8} fill={props.inverted ? 'black' : 'white'} />
</mask>
<g mask="url(#mask0_91_2047)">
<circle cx={9} cy={8} r={8} fill={props.inverted ? 'black' : 'white'} />
<path
d="M14.2896 14.0018L7.146 4.8H5.80005V11.1973H6.87681V6.16743L13.4444 14.6529C13.7407 14.4545 14.0231 14.2369 14.2896 14.0018Z"
fill={`url(#gradient-${id}-1)`}
/>
<rect
x="11.2222"
y="4.8"
width="1.06667"
height="6.4"
fill={`url(#gradient-${id}-2)`}
/>
</g>
</svg>
)
}
NextChatLogo.displayName = 'DevGPT-logo'

146
components/ui/sheet.tsx Normal file
View file

@ -0,0 +1,146 @@
'use client'
import * as React from 'react'
import * as SheetPrimitive from '@radix-ui/react-dialog'
import { cva, type VariantProps } from 'class-variance-authority'
import { X } from 'lucide-react'
import { cn } from '@/lib/utils'
const Sheet = SheetPrimitive.Root
const SheetTrigger = SheetPrimitive.Trigger
const SheetClose = SheetPrimitive.Close
const SheetPortal = ({
className,
children,
...props
}: SheetPrimitive.DialogPortalProps) => (
<SheetPrimitive.Portal
className={cn('fixed inset-0 z-50 flex', className)}
{...props}
>
{children}
</SheetPrimitive.Portal>
)
SheetPortal.displayName = SheetPrimitive.Portal.displayName
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, children, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in',
className
)}
{...props}
ref={ref}
/>
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
const sheetVariants = cva(
'fixed z-50 scale-100 gap-4 bg-background p-6 opacity-100 shadow-lg border h-full',
{
variants: {
position: {
left: 'data-[state=open]:animate-slide-from-left data-[state=closed]:animate-slide-to-left',
right: 'animate-in slide-in-from-right h-full duration-300'
}
},
defaultVariants: {
position: 'left'
}
}
)
export interface DialogContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
DialogContentProps
>(({ position, className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ position }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
))
SheetContent.displayName = SheetPrimitive.Content.displayName
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className
)}
{...props}
/>
)
SheetHeader.displayName = 'SheetHeader'
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className
)}
{...props}
/>
)
SheetFooter.displayName = 'SheetFooter'
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold text-foreground', className)}
{...props}
/>
))
SheetTitle.displayName = SheetPrimitive.Title.displayName
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
))
SheetDescription.displayName = SheetPrimitive.Description.displayName
export {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription
}

View file

@ -0,0 +1,24 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"
export { Textarea }

30
components/ui/tooltip.tsx Normal file
View file

@ -0,0 +1,30 @@
'use client'
import * as React from 'react'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
import { cn } from '@/lib/utils'
const TooltipProvider = TooltipPrimitive.Provider
const Tooltip = TooltipPrimitive.Root
const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 overflow-hidden rounded-md border bg-popover font-medium px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1',
className
)}
{...props}
/>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

View file

@ -1,96 +0,0 @@
'use client'
import { ThemeToggle } from '@/components/theme-toggle'
import { signOut } from '@auth/nextjs/client'
import { type Session } from '@auth/nextjs/types'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import Image from 'next/image'
export interface UserMenuProps {
session: Session
}
export function UserMenu({ session }: UserMenuProps) {
return (
<div className="flex items-center justify-between">
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="focus:outline-none">
{session.user?.image ? (
<Image
width={24}
height={24}
className="h-6 w-6 rounded-full select-none ring-zinc-100/10 ring-1 hover:opacity-80 transition-opacity duration-300"
src={session.user?.image ? `${session.user.image}&s=60` : ''}
alt={session.user.name ?? 'Avatar'}
/>
) : (
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-r from-cyan-500 to-blue-500 p-2 select-none">
<div
className="font-medium uppercase text-zinc-100"
style={{ fontSize: 12 }}
>
{session.user?.name ? session.user?.name.slice(0, 2) : null}
</div>
</div>
)}
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="min-w-[200px] bg-white dark:bg-zinc-950 rounded-lg shadow-lg text-zinc-900 dark:text-zinc-400 overflow-hidden border border-transparent dark:border-zinc-700 focus:outline-none relative z-20 py-2"
sideOffset={8}
align="end"
>
<DropdownMenu.Item className="py-2 px-3 focus:outline-none">
<div className="text-xs font-medium">{session.user?.name}</div>
<div className="text-xs text-zinc-500">{session.user?.email}</div>
</DropdownMenu.Item>
<DropdownMenu.Separator className="my-1 h-[1px] bg-zinc-100 dark:bg-zinc-800" />
<DropdownMenu.Item className="py-2 px-3 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors duration-200 cursor-pointer text-xs focus:outline-none">
<ThemeToggle />
</DropdownMenu.Item>
<DropdownMenu.Separator className="my-1 h-[1px] bg-zinc-100 dark:bg-zinc-800" />
<DropdownMenu.Item className="py-2 px-3 hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors duration-200 cursor-pointer text-xs focus:outline-none ">
<a
href="https://vercel.com"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-between w-full"
>
<span>Vercel Homepage</span>
<span>
<svg
fill="none"
height={16}
shapeRendering="geometricPrecision"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
viewBox="0 0 24 24"
width={16}
aria-hidden="true"
className="mr-1"
>
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" />
<path d="M15 3h6v6" />
<path d="M10 14L21 3" />
</svg>
</span>
</a>
</DropdownMenu.Item>
<DropdownMenu.Item
className="py-2 px-3 hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors duration-200 cursor-pointer text-xs focus:outline-none"
onClick={() => signOut()}
>
Log Out
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</div>
)
}
UserMenu.displayName = 'UserMenu'

View file

@ -1,14 +0,0 @@
import { cn } from '@/lib/utils'
export const VercelLogo = ({ className }: { className?: string }) => (
<svg
height={22}
viewBox="0 0 235 203"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="Vercel Logo"
className={cn(className, 'dark:fill-white fill-black')}
>
<path d="M117.082 0L234.164 202.794H0L117.082 0Z" fill="currentColor" />
</svg>
)

29
components/user-menu.tsx Normal file
View file

@ -0,0 +1,29 @@
'use client'
import { Button } from '@/components/ui/button'
import { signIn } from '@auth/nextjs/client'
import { type Session } from '@auth/nextjs/types'
export interface UserMenuProps {
session: Session
}
export function UserMenu({ session }: UserMenuProps) {
if (!session) {
return (
<Button variant="ghost" size="sm" onClick={() => signIn('github')}>
Login
</Button>
)
}
return (
<div className="flex items-center justify-between text-sm">
{session?.user && (
<Button variant="link" size="sm">
{session.user?.name}
</Button>
)}
</div>
)
}