scroll to bottom updates

This commit is contained in:
Nate Kelley 2025-04-10 10:26:25 -06:00
parent cefe43e386
commit beab1f459e
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
6 changed files with 40 additions and 5 deletions

View File

@ -20,6 +20,7 @@ export const AppPageLayout: React.FC<
headerBorderVariant?: 'default' | 'ghost'; headerBorderVariant?: 'default' | 'ghost';
headerClassName?: string; headerClassName?: string;
mainClassName?: string; mainClassName?: string;
contentContainerId?: string;
}> }>
> = ({ > = ({
children, children,
@ -29,7 +30,8 @@ export const AppPageLayout: React.FC<
headerSizeVariant = 'default', headerSizeVariant = 'default',
headerBorderVariant = 'default', headerBorderVariant = 'default',
headerClassName = '', headerClassName = '',
mainClassName = '' mainClassName = '',
contentContainerId
}) => { }) => {
return ( return (
<div <div
@ -49,7 +51,8 @@ export const AppPageLayout: React.FC<
<AppPageLayoutContent <AppPageLayoutContent
className={cn(headerBorderVariant === 'ghost' && 'scroll-shadow-container', mainClassName)} className={cn(headerBorderVariant === 'ghost' && 'scroll-shadow-container', mainClassName)}
scrollable={scrollable}> scrollable={scrollable}
id={contentContainerId}>
{header && scrollable && headerBorderVariant === 'ghost' && ( {header && scrollable && headerBorderVariant === 'ghost' && (
<div className="scroll-header"></div> <div className="scroll-header"></div>
)} )}

View File

@ -6,13 +6,15 @@ export const AppPageLayoutContent: React.FC<
PropsWithChildren<{ PropsWithChildren<{
className?: string; className?: string;
scrollable?: boolean; scrollable?: boolean;
id?: string;
}> }>
> = ({ className = '', children, scrollable = true }) => { > = ({ className = '', children, scrollable = true, id }) => {
const Selector = scrollable ? ScrollArea : 'main'; const Selector = scrollable ? ScrollArea : 'main';
const ChildSelector = scrollable ? 'main' : React.Fragment; const ChildSelector = scrollable ? 'main' : React.Fragment;
return ( return (
<Selector <Selector
id={id}
className={cn('bg-page-background app-content h-full max-h-full overflow-hidden', className)}> className={cn('bg-page-background app-content h-full max-h-full overflow-hidden', className)}>
<ChildSelector>{children}</ChildSelector> <ChildSelector>{children}</ChildSelector>
</Selector> </Selector>

View File

@ -9,8 +9,9 @@ const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>, React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
viewportRef?: React.RefObject<HTMLDivElement>; viewportRef?: React.RefObject<HTMLDivElement>;
id?: string;
} }
>(({ className, children, viewportRef, ...props }, ref) => ( >(({ className, children, viewportRef, id = 'scroll-area-viewport', ...props }, ref) => (
<ScrollAreaPrimitive.Root <ScrollAreaPrimitive.Root
ref={ref} ref={ref}
className={cn('relative overflow-hidden', className)} className={cn('relative overflow-hidden', className)}
@ -18,7 +19,7 @@ const ScrollArea = React.forwardRef<
<ScrollAreaPrimitive.Viewport <ScrollAreaPrimitive.Viewport
ref={viewportRef} ref={viewportRef}
className="h-full w-full rounded-[inherit] [&>div]:!block" className="h-full w-full rounded-[inherit] [&>div]:!block"
id="scroll-area-viewport"> id={id}>
{children} {children}
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>
<ScrollBar /> <ScrollBar />

View File

@ -3,9 +3,12 @@ import { ChatHeader } from './ChatHeader';
import { ChatContent } from './ChatContent'; import { ChatContent } from './ChatContent';
import { AppPageLayout } from '@/components/ui/layouts'; import { AppPageLayout } from '@/components/ui/layouts';
export const CHAT_CONTENT_CONTAINER_ID = 'chat-container-content';
export const ChatContainer = React.memo(() => { export const ChatContainer = React.memo(() => {
return ( return (
<AppPageLayout <AppPageLayout
contentContainerId={CHAT_CONTENT_CONTAINER_ID}
header={<ChatHeader />} header={<ChatHeader />}
headerBorderVariant="ghost" headerBorderVariant="ghost"
scrollable scrollable

View File

@ -1,3 +1,5 @@
'use client';
import React from 'react'; import React from 'react';
import { ChatUserMessage } from './ChatUserMessage'; import { ChatUserMessage } from './ChatUserMessage';
import { ChatResponseMessages } from './ChatResponseMessages'; import { ChatResponseMessages } from './ChatResponseMessages';

View File

@ -0,0 +1,24 @@
import { type useAutoScroll } from '@/hooks/useAutoScroll';
import React from 'react';
import { ChevronDown } from '@/components/ui/icons';
import { cn } from '@/lib/utils';
export const ChatScrollToBottom: React.FC<{
isAutoScrollEnabled: boolean;
scrollToBottom: ReturnType<typeof useAutoScroll>['scrollToBottom'];
}> = React.memo(({ isAutoScrollEnabled, scrollToBottom }) => {
return (
<button
onClick={() => scrollToBottom('instant')}
className={cn(
'bg-background/90 hover:bg-item-hover/90 absolute -top-9 right-3 z-10 rounded-full border p-2 shadow transition-all duration-300 hover:scale-105 hover:shadow-md',
isAutoScrollEnabled
? 'pointer-events-none scale-90 opacity-0'
: 'pointer-events-auto scale-100 cursor-pointer opacity-100'
)}>
<ChevronDown />
</button>
);
});
ChatScrollToBottom.displayName = 'ChatScrollToBottom';