Skip to content

Commit

Permalink
app(ui/ux): add scroll-to-top and scroll-to-botton buttons (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
Charlie-XIAO authored Nov 20, 2024
1 parent cc4ebf5 commit cd83733
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 0 deletions.
2 changes: 2 additions & 0 deletions app/frontend/src/components/ChatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { addMessageUtilities, scrollToBottom } from "../utils";
import { MessageDocs } from "./MessageDocs";
import { RetrievalPanelCommandPalette } from "./RetrievePanelCommandPalette";
import { FCDeleteChatButton } from "./FCDeleteChatButton";
import { FCScrollButtons } from "./FCScrollButtons";

interface ChatPanelProps {
model: ModelType;
Expand Down Expand Up @@ -107,6 +108,7 @@ export const ChatPanel = ({
onClick={() => setMessages(() => [])}
/>,
<FCDeleteChatButton onClick={deleteTab} />,
<FCScrollButtons containerRef={chatPortRef} />,
];

// Persistent right functional components
Expand Down
77 changes: 77 additions & 0 deletions app/frontend/src/components/FCScrollButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* @file FCScrollButtons.tsx
*
* The functional component for the scroll to top and scroll to bottom buttons,
* used at the bottom of the chat input.
*/

import { IconButton, Tooltip } from "@radix-ui/themes";
import { RefObject, useEffect, useRef, useState } from "react";
import { GoMoveToBottom, GoMoveToTop } from "react-icons/go";
import { scrollToBottom, scrollToTop } from "../utils";

interface FCScrollButtonsProps {
containerRef: RefObject<HTMLElement>;
}

export const FCScrollButtons = ({ containerRef }: FCScrollButtonsProps) => {
const [canScrollUp, setCanScrollUp] = useState<boolean>(false);
const [canScrollDown, setCanScrollDown] = useState<boolean>(false);
const debounceTimer = useRef<number | null>(null);

// Handler for the container scroll event; it is debounced for better
// performance
const handleScroll = () => {
if (debounceTimer.current !== null) {
clearTimeout(debounceTimer.current);
}
debounceTimer.current = setTimeout(() => {
if (containerRef.current !== null) {
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
setCanScrollUp(scrollTop > 0);
setCanScrollDown(scrollTop + clientHeight < scrollHeight);
}
}, 300);
};

useEffect(() => {
const container = containerRef.current;
if (container !== null) {
container.addEventListener("scroll", handleScroll, { passive: true });
}

return () => {
if (container !== null) {
container.removeEventListener("scroll", handleScroll);
}
if (debounceTimer.current !== null) {
clearTimeout(debounceTimer.current);
}
};
}, [containerRef]);

return (
<>
<Tooltip content="Scroll to top" side="top">
<IconButton
disabled={!canScrollUp}
variant="ghost"
size="1"
onClick={() => scrollToTop(containerRef)}
>
<GoMoveToTop size="20" />
</IconButton>
</Tooltip>
<Tooltip content="Scroll to bottom" side="top">
<IconButton
disabled={!canScrollDown}
variant="ghost"
size="1"
onClick={() => scrollToBottom(containerRef)}
>
<GoMoveToBottom size="20" />
</IconButton>
</Tooltip>
</>
);
};
2 changes: 2 additions & 0 deletions app/frontend/src/components/RetrievePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ChatErrorMessage } from "./ChatErrorMessage";
import { addMessageUtilities, scrollToBottom } from "../utils";
import { MessageRetrieved } from "./MessageRetrieved";
import { FCTopKSelector } from "./FCTopKSelector";
import { FCScrollButtons } from "./FCScrollButtons";

interface RetrievalPanelProps {
messages: ChatDisplay[];
Expand Down Expand Up @@ -70,6 +71,7 @@ export const RetrievePanel = ({
disabled={messages.length === 0 || loading}
onClick={() => setMessages(() => [])}
/>,
<FCScrollButtons containerRef={chatPortRef} />,
];

// Persistent right functional components
Expand Down
12 changes: 12 additions & 0 deletions app/frontend/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,15 @@ export const scrollToBottom = (ref: RefObject<HTMLElement>) => {
});
}
};

/**
* Scroll to the top of a React ref object.
*/
export const scrollToTop = (ref: RefObject<HTMLElement>) => {
if (ref.current !== null) {
ref.current.scrollTo({
top: 0,
behavior: "smooth",
});
}
};

0 comments on commit cd83733

Please sign in to comment.