Skip to content

Commit 461cb11

Browse files
--wip-- [skip ci]
1 parent 75e893e commit 461cb11

34 files changed

+3650
-605
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use client';
2+
3+
import type { ComponentProps } from 'react';
4+
import { useCallback } from 'react';
5+
import { ArrowDownIcon } from 'lucide-react';
6+
import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
7+
import { Button } from '@/components/ui/button';
8+
import { cn } from '@/lib/utils';
9+
10+
export type ConversationProps = ComponentProps<typeof StickToBottom>;
11+
12+
export const Conversation = ({ className, ...props }: ConversationProps) => (
13+
<StickToBottom
14+
className={cn('relative flex-1 overflow-y-hidden', className)}
15+
initial="smooth"
16+
resize="smooth"
17+
role="log"
18+
{...props}
19+
/>
20+
);
21+
22+
export type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;
23+
24+
export const ConversationContent = ({ className, ...props }: ConversationContentProps) => (
25+
<StickToBottom.Content className={cn('flex flex-col gap-8 p-4', className)} {...props} />
26+
);
27+
28+
export type ConversationEmptyStateProps = ComponentProps<'div'> & {
29+
title?: string;
30+
description?: string;
31+
icon?: React.ReactNode;
32+
};
33+
34+
export const ConversationEmptyState = ({
35+
className,
36+
title = 'No messages yet',
37+
description = 'Start a conversation to see messages here',
38+
icon,
39+
children,
40+
...props
41+
}: ConversationEmptyStateProps) => (
42+
<div
43+
className={cn(
44+
'flex size-full flex-col items-center justify-center gap-3 p-8 text-center',
45+
className
46+
)}
47+
{...props}
48+
>
49+
{children ?? (
50+
<>
51+
{icon && <div className="text-muted-foreground">{icon}</div>}
52+
<div className="space-y-1">
53+
<h3 className="text-sm font-medium">{title}</h3>
54+
{description && <p className="text-muted-foreground text-sm">{description}</p>}
55+
</div>
56+
</>
57+
)}
58+
</div>
59+
);
60+
61+
export type ConversationScrollButtonProps = ComponentProps<typeof Button>;
62+
63+
export const ConversationScrollButton = ({
64+
className,
65+
...props
66+
}: ConversationScrollButtonProps) => {
67+
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
68+
69+
const handleScrollToBottom = useCallback(() => {
70+
scrollToBottom();
71+
}, [scrollToBottom]);
72+
73+
return (
74+
!isAtBottom && (
75+
<Button
76+
className={cn('absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full', className)}
77+
onClick={handleScrollToBottom}
78+
size="icon"
79+
type="button"
80+
variant="outline"
81+
{...props}
82+
>
83+
<ArrowDownIcon className="size-4" />
84+
</Button>
85+
)
86+
);
87+
};

0 commit comments

Comments
 (0)