Conversation.tsx48 lines · main
1import { ArrowDownIcon } from 'lucide-react'
2import type { ComponentProps } from 'react'
3import { useCallback } from 'react'
4import { Button, cn } from 'ui'
5import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom'
6
7type ConversationProps = ComponentProps<typeof StickToBottom>
8type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>
9type ConversationScrollButtonProps = ComponentProps<typeof Button>
10
11export const Conversation = ({ className, ...props }: ConversationProps) => (
12 <StickToBottom
13 className={cn('relative flex-1 overflow-y-auto', className)}
14 initial="smooth"
15 resize="smooth"
16 role="log"
17 {...props}
18 />
19)
20
21export const ConversationContent = ({ className, ...props }: ConversationContentProps) => (
22 <StickToBottom.Content className={cn('p-4', className)} {...props} />
23)
24
25export const ConversationScrollButton = ({
26 className,
27 ...props
28}: ConversationScrollButtonProps) => {
29 const { isAtBottom, scrollToBottom } = useStickToBottomContext()
30
31 const handleScrollToBottom = useCallback(() => {
32 scrollToBottom()
33 }, [scrollToBottom])
34
35 return (
36 !isAtBottom && (
37 <Button
38 className={cn('absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full', className)}
39 onClick={handleScrollToBottom}
40 size="tiny"
41 type="default"
42 {...props}
43 >
44 <ArrowDownIcon className="size-4" />
45 </Button>
46 )
47 )
48}