feat: enhance RightPanel component with animation effects and dynamic width adjustments; refactor ReportPanelContent to streamline loading state handling

This commit is contained in:
Anish Sarkar 2026-03-11 01:44:55 +05:30
parent 414dceff2f
commit c304cc992c
2 changed files with 68 additions and 51 deletions

View file

@ -2,6 +2,7 @@
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { PanelRight, PanelRightClose } from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import { useEffect } from "react";
import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
@ -66,6 +67,8 @@ export function RightPanelExpandButton() {
);
}
const PANEL_WIDTHS = { sources: 420, report: 640 } as const;
export function RightPanel({ documentsPanel }: RightPanelProps) {
const [activeTab] = useAtom(rightPanelTabAtom);
const reportState = useAtomValue(reportPanelAtom);
@ -75,21 +78,16 @@ export function RightPanel({ documentsPanel }: RightPanelProps) {
const documentsOpen = documentsPanel?.open ?? false;
const reportOpen = reportState.isOpen && !!reportState.reportId;
// Close report on Escape key (works on Windows, macOS, and Linux)
// Must be called before early returns to satisfy Rules of Hooks.
useEffect(() => {
if (!reportOpen) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
closeReport();
}
if (e.key === "Escape") closeReport();
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [reportOpen, closeReport]);
if (!documentsOpen && !reportOpen) return null;
if (collapsed) return null;
const isVisible = (documentsOpen || reportOpen) && !collapsed;
const effectiveTab =
activeTab === "report" && !reportOpen
@ -98,32 +96,72 @@ export function RightPanel({ documentsPanel }: RightPanelProps) {
? "report"
: activeTab;
const targetWidth = PANEL_WIDTHS[effectiveTab];
const collapseButton = <CollapseButton onClick={() => setCollapsed(true)} />;
const panelWidth = effectiveTab === "sources" ? "w-[420px]" : "w-[640px]";
const contentKey =
effectiveTab === "sources" && documentsOpen
? "sources"
: effectiveTab === "report" && reportOpen
? "report"
: null;
return (
<aside className={`flex h-full ${panelWidth} shrink-0 flex-col border-l bg-background animate-in slide-in-from-right-4 duration-200 ease-out transition-[width] `}>
<div className="flex-1 min-h-0 overflow-hidden">
{effectiveTab === "sources" && documentsOpen && documentsPanel && (
<DocumentsSidebar
open={documentsPanel.open}
onOpenChange={documentsPanel.onOpenChange}
embedded
headerAction={collapseButton}
/>
)}
{effectiveTab === "report" && reportOpen && (
<div className="flex h-full flex-col">
<ReportPanelContent
reportId={reportState.reportId!}
title={reportState.title || "Report"}
onClose={closeReport}
shareToken={reportState.shareToken}
/>
<AnimatePresence>
{isVisible && (
<motion.aside
key="right-panel"
initial={{ width: 0, opacity: 0 }}
animate={{ width: targetWidth, opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{
width: { type: "tween", duration: 0.25, ease: [0.4, 0, 0.2, 1] },
opacity: { duration: 0.15 },
}}
className="flex h-full shrink-0 flex-col border-l bg-background overflow-hidden"
>
<div className="flex-1 min-h-0 overflow-hidden">
<AnimatePresence mode="wait" initial={false}>
{contentKey === "sources" && documentsPanel && (
<motion.div
key="sources"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.12 }}
className="h-full"
>
<DocumentsSidebar
open={documentsPanel.open}
onOpenChange={documentsPanel.onOpenChange}
embedded
headerAction={collapseButton}
/>
</motion.div>
)}
{contentKey === "report" && (
<motion.div
key="report"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.12 }}
className="h-full"
>
<div className="flex h-full flex-col">
<ReportPanelContent
reportId={reportState.reportId!}
title={reportState.title || "Report"}
onClose={closeReport}
shareToken={reportState.shareToken}
/>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
)}
</div>
</aside>
</motion.aside>
)}
</AnimatePresence>
);
}