feat: enhance DocumentsFilters and ModelSelector components with scroll position tracking and improved styling, improving user experience and visual consistency across the application

This commit is contained in:
Anish Sarkar 2026-03-06 22:22:28 +05:30
parent 378c72c564
commit aaa8840e1d
6 changed files with 67 additions and 27 deletions

View file

@ -103,6 +103,7 @@ export function DocumentsSidebar({ open, onOpenChange }: DocumentsSidebarProps)
try {
await deleteDocumentMutation({ id });
toast.success(t("delete_success") || "Document deleted");
setSidebarDocs((prev) => prev.filter((d) => d.id !== id));
if (isSearchMode) {
searchRemoveItems([id]);
}
@ -112,7 +113,7 @@ export function DocumentsSidebar({ open, onOpenChange }: DocumentsSidebarProps)
return false;
}
},
[deleteDocumentMutation, isSearchMode, t, searchRemoveItems]
[deleteDocumentMutation, isSearchMode, t, searchRemoveItems, setSidebarDocs]
);
const sortKeyRef = useRef(sortKey);

View file

@ -712,7 +712,7 @@ export function InboxSidebar({
</Tooltip>
<DropdownMenuContent
align="end"
className={cn("z-80 select-none max-h-[60vh] overflow-hidden flex flex-col", activeTab === "status" ? "w-52" : "w-44")}
className={cn("z-80 select-none max-h-[60vh] overflow-hidden flex flex-col bg-muted dark:border dark:border-neutral-700", activeTab === "status" ? "w-52" : "w-44")}
>
<DropdownMenuLabel className="text-xs text-muted-foreground/80 font-normal">
{t("filter") || "Filter"}
@ -1097,7 +1097,7 @@ export function InboxSidebar({
if (isDocked && open && !isMobile) {
return (
<aside
className="h-full w-[360px] shrink-0 bg-background flex flex-col border-r"
className="h-full w-[360px] shrink-0 bg-sidebar text-sidebar-foreground flex flex-col border-r"
aria-label={t("inbox") || "Inbox"}
>
{inboxContent}

View file

@ -65,7 +65,7 @@ export function SidebarSlideOutPanel({
exit={{ x: "-100%" }}
transition={{ type: "tween", duration: 0.2, ease: [0.4, 0, 0.2, 1] }}
className={cn(
"h-full w-full bg-background flex flex-col pointer-events-auto select-none",
"h-full w-full bg-sidebar text-sidebar-foreground flex flex-col pointer-events-auto select-none",
"sm:border-r sm:shadow-xl"
)}
role="dialog"

View file

@ -2,7 +2,7 @@
import { useAtomValue } from "jotai";
import { Bot, Check, ChevronDown, Edit3, ImageIcon, Plus, Zap } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useMemo, useState, type UIEvent } from "react";
import { toast } from "sonner";
import {
globalImageGenConfigsAtom,
@ -57,6 +57,17 @@ export function ModelSelector({
const [activeTab, setActiveTab] = useState<"llm" | "image">("llm");
const [llmSearchQuery, setLlmSearchQuery] = useState("");
const [imageSearchQuery, setImageSearchQuery] = useState("");
const [llmScrollPos, setLlmScrollPos] = useState<"top" | "middle" | "bottom">("top");
const [imageScrollPos, setImageScrollPos] = useState<"top" | "middle" | "bottom">("top");
const handleListScroll = useCallback(
(setter: typeof setLlmScrollPos) => (e: UIEvent<HTMLDivElement>) => {
const el = e.currentTarget;
const atTop = el.scrollTop <= 2;
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= 2;
setter(atTop ? "top" : atBottom ? "bottom" : "middle");
},
[]
);
// LLM data
const { data: llmUserConfigs, isLoading: llmUserLoading } = useAtomValue(newLLMConfigsAtom);
@ -325,7 +336,14 @@ export function ModelSelector({
</div>
)}
<CommandList className="max-h-[300px] md:max-h-[400px] overflow-y-auto">
<CommandList
className="max-h-[300px] md:max-h-[400px] overflow-y-auto"
onScroll={handleListScroll(setLlmScrollPos)}
style={{
maskImage: `linear-gradient(to bottom, ${llmScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${llmScrollPos === "bottom" ? "black" : "transparent"})`,
WebkitMaskImage: `linear-gradient(to bottom, ${llmScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${llmScrollPos === "bottom" ? "black" : "transparent"})`,
}}
>
<CommandEmpty className="py-8 text-center">
<div className="flex flex-col items-center gap-2">
<Bot className="size-8 text-muted-foreground" />
@ -505,7 +523,14 @@ export function ModelSelector({
/>
</div>
)}
<CommandList className="max-h-[300px] md:max-h-[400px] overflow-y-auto">
<CommandList
className="max-h-[300px] md:max-h-[400px] overflow-y-auto"
onScroll={handleListScroll(setImageScrollPos)}
style={{
maskImage: `linear-gradient(to bottom, ${imageScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${imageScrollPos === "bottom" ? "black" : "transparent"})`,
WebkitMaskImage: `linear-gradient(to bottom, ${imageScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${imageScrollPos === "bottom" ? "black" : "transparent"})`,
}}
>
<CommandEmpty className="py-8 text-center">
<div className="flex flex-col items-center gap-2">
<ImageIcon className="size-8 text-muted-foreground" />