I want a dashboard with a top and side bar and then the content in the middle. I want the content to be scrollable but I want it to fill the available height. The only issue is that when I try to fill the height it goes past the screen making the whole window scrollable and then is no longer scrollable itself. I have been reading its something to do with flex box but cannot tell. Im using React with typescript and Tailwindcss
App.tsx
import "./App.css";import { Route, Routes, BrowserRouter } from "react-router-dom";import Chat from "./components/Chat";import ChatList from "./components/ChatList/ChatList";import Topbar from "./components/Navigation/TopBar";import Bottombar from "./components/Navigation/BottomBar";function App() { return (<BrowserRouter><div className="flex h-screen flex-col"><Topbar /><div className="grid grid-cols-8"><div className="col-span-1"></div><div className="col-span-7"><Routes><Route path="/chat/:id" element={<Chat />}></Route><Route path="/list" element={<ChatList />}></Route></Routes></div></div><Bottombar /></div></BrowserRouter> );}export default App;
Chat.tsx
import MessagePane from "./MessagePane/MessagePane";import "../App.css";import { useParams } from "react-router-dom";interface ChatProps { id: number;}export default function Chat() { const { id } = useParams<{ id?: string }>(); const chatId = id ? parseInt(id, 10) : undefined; if (chatId === undefined) { return <div>Invalid chat ID</div>; // Display an error message or handle the case where id is undefined } return (<div className="flex-grow overflow-hidden bg-red-400"><MessagePane id={chatId} /></div> );}
MessagePane.tsx
import { useEffect, useRef, useState } from "react";import { Message } from "../../interfaces/message.interface";import ChatBubble from "./ChatBubble";import "../../App.css";interface MessagePaneProps { id: number;}export default function MessagePane({ id }: MessagePaneProps) { const [messages, setMessages] = useState<Message[]>([]); const [renderedMessages, setRenderedMessages] = useState<Message[]>([]); const [loadCount, setLoadCount] = useState<number>(10); // Number of messages to load at a time const chatContainerRef = useRef<HTMLDivElement>(null); const topMessageIdRef = useRef<number | null>(null); useEffect(() => { const fetchMessages = async () => { if (messages.length === 0) { try { const response = await fetch( `http://localhost:3000/messages/${id}` ); if (response.ok) { const data = await response.json(); setMessages(data); // Ensure messages are in the correct order } } catch (error) { console.error("Error fetching messages", error); } } }; fetchMessages(); }, [id]); useEffect(() => { if (messages.length > 0) { setRenderedMessages(messages.slice(-loadCount)); setTimeout(() => { if (chatContainerRef.current) { chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; } }, 0); } }, [messages]); const loadMoreMessages = () => { if (chatContainerRef.current) { const scrollTopBefore = chatContainerRef.current.scrollTop; const scrollHeightBefore = chatContainerRef.current.scrollHeight; const newRenderCount = Math.min( renderedMessages.length + loadCount, messages.length ); setRenderedMessages(messages.slice(-newRenderCount)); requestAnimationFrame(() => { if (chatContainerRef.current) { const scrollHeightAfter = chatContainerRef.current.scrollHeight; chatContainerRef.current.scrollTop = scrollHeightAfter - scrollHeightBefore + scrollTopBefore; } }); } }; const handleScroll = () => { if (chatContainerRef.current) { const { scrollTop } = chatContainerRef.current; if (scrollTop === 0) { // Get the ROWID of the top message const topMessageElement = chatContainerRef.current.querySelector(".chat-container > div:first-child" ); if (topMessageElement) { const topMessageId = parseInt( topMessageElement.getAttribute("data-rowid") || "" ); topMessageIdRef.current = topMessageId; } loadMoreMessages(); } } }; useEffect(() => { const chatContainer = chatContainerRef.current; if (chatContainer) { chatContainer.addEventListener("scroll", handleScroll); } return () => { if (chatContainer) { chatContainer.removeEventListener("scroll", handleScroll); } }; }, [renderedMessages]); // Scroll to the top message when new messages are loaded useEffect(() => { if (topMessageIdRef.current !== null) { const topMessageElement = document.querySelector( `#message-${topMessageIdRef.current}` ) as HTMLElement; if (topMessageElement && chatContainerRef.current) { chatContainerRef.current.scrollTop = topMessageElement.offsetTop - chatContainerRef.current.offsetTop; } } }, [renderedMessages]); return (<div className="chat-container flex-grow overflow-y-auto" ref={chatContainerRef}> {renderedMessages.map((message: Message, index: number) => (<div key={index} id={`message-${message.ROWID}`} data-rowid={message.ROWID} // Store the ROWID as data attribute><p>{message.ROWID}</p><ChatBubble sender={message.is_from_me} > {message.text}</ChatBubble></div> ))}</div> );}