From cd83733465869e62289b50d357e3ac10f0ce1bcb Mon Sep 17 00:00:00 2001
From: Yao Xiao <108576690+Charlie-XIAO@users.noreply.github.com>
Date: Tue, 19 Nov 2024 21:59:26 -0500
Subject: [PATCH] app(ui/ux): add scroll-to-top and scroll-to-botton buttons
(#13)
---
app/frontend/src/components/ChatPanel.tsx | 2 +
.../src/components/FCScrollButtons.tsx | 77 +++++++++++++++++++
app/frontend/src/components/RetrievePanel.tsx | 2 +
app/frontend/src/utils.ts | 12 +++
4 files changed, 93 insertions(+)
create mode 100644 app/frontend/src/components/FCScrollButtons.tsx
diff --git a/app/frontend/src/components/ChatPanel.tsx b/app/frontend/src/components/ChatPanel.tsx
index 7b16696..165ba0e 100644
--- a/app/frontend/src/components/ChatPanel.tsx
+++ b/app/frontend/src/components/ChatPanel.tsx
@@ -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;
@@ -107,6 +108,7 @@ export const ChatPanel = ({
onClick={() => setMessages(() => [])}
/>,
,
+ ,
];
// Persistent right functional components
diff --git a/app/frontend/src/components/FCScrollButtons.tsx b/app/frontend/src/components/FCScrollButtons.tsx
new file mode 100644
index 0000000..d5f6d6a
--- /dev/null
+++ b/app/frontend/src/components/FCScrollButtons.tsx
@@ -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;
+}
+
+export const FCScrollButtons = ({ containerRef }: FCScrollButtonsProps) => {
+ const [canScrollUp, setCanScrollUp] = useState(false);
+ const [canScrollDown, setCanScrollDown] = useState(false);
+ const debounceTimer = useRef(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 (
+ <>
+
+ scrollToTop(containerRef)}
+ >
+
+
+
+
+ scrollToBottom(containerRef)}
+ >
+
+
+
+ >
+ );
+};
diff --git a/app/frontend/src/components/RetrievePanel.tsx b/app/frontend/src/components/RetrievePanel.tsx
index 90201c9..871b1a8 100644
--- a/app/frontend/src/components/RetrievePanel.tsx
+++ b/app/frontend/src/components/RetrievePanel.tsx
@@ -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[];
@@ -70,6 +71,7 @@ export const RetrievePanel = ({
disabled={messages.length === 0 || loading}
onClick={() => setMessages(() => [])}
/>,
+ ,
];
// Persistent right functional components
diff --git a/app/frontend/src/utils.ts b/app/frontend/src/utils.ts
index 6dcf5f8..f9aac90 100644
--- a/app/frontend/src/utils.ts
+++ b/app/frontend/src/utils.ts
@@ -41,3 +41,15 @@ export const scrollToBottom = (ref: RefObject) => {
});
}
};
+
+/**
+ * Scroll to the top of a React ref object.
+ */
+export const scrollToTop = (ref: RefObject) => {
+ if (ref.current !== null) {
+ ref.current.scrollTo({
+ top: 0,
+ behavior: "smooth",
+ });
+ }
+};