Spaces:
Running
Running
File size: 3,649 Bytes
3ba9c0c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
'use client'
import * as React from 'react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { motion } from 'framer-motion'
import { buttonVariants } from '@/components/ui/button'
import { IconMessage, IconUsers } from '@/components/ui/icons'
import {
Tooltip,
TooltipContent,
TooltipTrigger
} from '@/components/ui/tooltip'
import { useLocalStorage } from '@/lib/hooks/use-local-storage'
import { type Chat } from '@/lib/types'
import { cn } from '@/lib/utils'
interface SidebarItemProps {
index: number
chat: Chat
children: React.ReactNode
}
export function SidebarItem({ index, chat, children }: SidebarItemProps) {
const pathname = usePathname()
const isActive = pathname === chat.path
const [newChatId, setNewChatId] = useLocalStorage('newChatId', null)
const shouldAnimate = index === 0 && isActive && newChatId
if (!chat?.id) return null
return (
<motion.div
className="relative h-8"
variants={{
initial: {
height: 0,
opacity: 0
},
animate: {
height: 'auto',
opacity: 1
}
}}
initial={shouldAnimate ? 'initial' : undefined}
animate={shouldAnimate ? 'animate' : undefined}
transition={{
duration: 0.25,
ease: 'easeIn'
}}
>
<div className="absolute left-2 top-1 flex size-6 items-center justify-center">
{chat.sharePath ? (
<Tooltip delayDuration={1000}>
<TooltipTrigger
tabIndex={-1}
className="focus:bg-muted focus:ring-1 focus:ring-ring"
>
<IconUsers className="mr-2" />
</TooltipTrigger>
<TooltipContent>This is a shared chat.</TooltipContent>
</Tooltip>
) : (
<IconMessage className="mr-2" />
)}
</div>
<Link
href={chat.path}
className={cn(
buttonVariants({ variant: 'ghost' }),
'group w-full px-8 transition-colors hover:bg-zinc-200/40 dark:hover:bg-zinc-300/10',
isActive && 'bg-zinc-200 pr-16 font-semibold dark:bg-zinc-800'
)}
>
<div
className="relative max-h-5 flex-1 select-none overflow-hidden text-ellipsis break-all"
title={chat.title}
>
<span className="whitespace-nowrap">
{shouldAnimate ? (
chat.title.split('').map((character, index) => (
<motion.span
key={index}
variants={{
initial: {
opacity: 0,
x: -100
},
animate: {
opacity: 1,
x: 0
}
}}
initial={shouldAnimate ? 'initial' : undefined}
animate={shouldAnimate ? 'animate' : undefined}
transition={{
duration: 0.25,
ease: 'easeIn',
delay: index * 0.05,
staggerChildren: 0.05
}}
onAnimationComplete={() => {
if (index === chat.title.length - 1) {
setNewChatId(null)
}
}}
>
{character}
</motion.span>
))
) : (
<span>{chat.title}</span>
)}
</span>
</div>
</Link>
{isActive && <div className="absolute right-2 top-1">{children}</div>}
</motion.div>
)
}
|