From 53259b610584922bc7b96e0e8efa617007ef9b6b Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:44:45 +0530 Subject: [PATCH] feat(sidebar): introduce SidebarListItem component for consistent sidebar item styling - Added SidebarListItem component to standardize the appearance of sidebar items across DocumentNode and FolderNode. - Refactored DocumentNode and FolderNode to utilize SidebarListItem for improved layout and interaction. - Updated ChatListItem to leverage sidebarListItemClassName for consistent styling. - Enhanced SidebarSection to improve content rendering and visibility transitions. --- .../components/documents/DocumentNode.tsx | 13 +++-- .../components/documents/FolderNode.tsx | 11 ++--- .../layout/ui/sidebar/ChatListItem.tsx | 12 ++--- .../layout/ui/sidebar/DocumentsSidebar.tsx | 41 +++++++++------- .../layout/ui/sidebar/SidebarListItem.tsx | 47 +++++++++++++++++++ .../layout/ui/sidebar/SidebarSection.tsx | 20 ++++++-- 6 files changed, 104 insertions(+), 40 deletions(-) create mode 100644 surfsense_web/components/layout/ui/sidebar/SidebarListItem.tsx diff --git a/surfsense_web/components/documents/DocumentNode.tsx b/surfsense_web/components/documents/DocumentNode.tsx index a13bd0079..1d627ee36 100644 --- a/surfsense_web/components/documents/DocumentNode.tsx +++ b/surfsense_web/components/documents/DocumentNode.tsx @@ -40,6 +40,7 @@ import { Spinner } from "@/components/ui/spinner"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import type { DocumentTypeEnum } from "@/contracts/types/document.types"; import { cn } from "@/lib/utils"; +import { SidebarListItem } from "../layout/ui/sidebar/SidebarListItem"; import { DND_TYPES } from "./FolderNode"; import { isVersionableType } from "./version-history"; @@ -165,13 +166,11 @@ export const DocumentNode = React.memo(function DocumentNode({ return ( -
{(() => { @@ -358,7 +357,7 @@ export const DocumentNode = React.memo(function DocumentNode({ -
+
{contextMenuOpen && ( diff --git a/surfsense_web/components/documents/FolderNode.tsx b/surfsense_web/components/documents/FolderNode.tsx index 2506713a4..849de2ea9 100644 --- a/surfsense_web/components/documents/FolderNode.tsx +++ b/surfsense_web/components/documents/FolderNode.tsx @@ -5,7 +5,6 @@ import { ChevronDown, ChevronRight, Download, - Eye, EyeOff, Folder, FolderOpen, @@ -18,6 +17,7 @@ import { } from "lucide-react"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { useDrag, useDrop } from "react-dnd"; +import { SidebarListItem } from "@/components/layout/ui/sidebar/SidebarListItem"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { @@ -254,15 +254,14 @@ export const FolderNode = React.memo(function FolderNode({ return ( - {/* biome-ignore lint/a11y/useSemanticElements: div required for drag/drop refs */} -
)} -
+
{!isRenaming && contextMenuOpen && ( diff --git a/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx b/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx index c854225a2..a027cfb9c 100644 --- a/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx +++ b/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx @@ -14,6 +14,7 @@ import { useLongPress } from "@/hooks/use-long-press"; import { useIsMobile } from "@/hooks/use-mobile"; import { useTypewriter } from "@/hooks/use-typewriter"; import { cn } from "@/lib/utils"; +import { sidebarListItemClassName } from "./SidebarListItem"; interface ChatListItemProps { name: string; @@ -66,12 +67,11 @@ export function ChatListItem({ onMouseEnter={onPrefetch} onFocus={onPrefetch} {...(isMobile ? longPressHandlers : {})} - className={cn( - "h-auto w-full justify-start gap-2 overflow-hidden px-2 py-1.5 text-left font-normal", - "group-hover/item:bg-accent group-hover/item:text-accent-foreground", - "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", - isActive && "bg-accent text-accent-foreground" - )} + className={sidebarListItemClassName({ + active: isActive, + className: + "justify-start gap-2 overflow-hidden px-2 py-1.5 font-normal focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", + })} > {animatedName} diff --git a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx index 09f53db11..234ce7124 100644 --- a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx @@ -1556,22 +1556,31 @@ function AuthenticatedDocumentsSidebarBase({ if (embedded) { return ( - handleCreateFolder(null)} - /> - } - > - {renderDocumentTree()} - + <> + handleCreateFolder(null)} + /> + } + > + {renderDocumentTree()} + + + + ); } diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarListItem.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarListItem.tsx new file mode 100644 index 000000000..4799970f4 --- /dev/null +++ b/surfsense_web/components/layout/ui/sidebar/SidebarListItem.tsx @@ -0,0 +1,47 @@ +"use client"; + +import type React from "react"; +import { forwardRef } from "react"; +import { cn } from "@/lib/utils"; + +type SidebarListItemElementProps = React.HTMLAttributes; + +interface SidebarListItemProps extends SidebarListItemElementProps { + active?: boolean; + dragging?: boolean; + interactive?: boolean; +} + +export const SidebarListItem = forwardRef( + ({ active, dragging, interactive = true, className, children, ...props }, ref) => ( +
+ {children} +
+ ) +); + +SidebarListItem.displayName = "SidebarListItem"; + +export function sidebarListItemClassName({ + active, + dragging, + interactive = true, + className, +}: { + active?: boolean; + dragging?: boolean; + interactive?: boolean; + className?: string; +}) { + return cn( + "group group/sidebar-list-item flex h-8 w-full items-center rounded-md text-left text-sm select-none", + interactive && "cursor-pointer hover:bg-accent hover:text-accent-foreground", + active && "bg-accent text-accent-foreground", + dragging && "opacity-40", + className + ); +} diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarSection.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarSection.tsx index ac995094a..73cf8485f 100644 --- a/surfsense_web/components/layout/ui/sidebar/SidebarSection.tsx +++ b/surfsense_web/components/layout/ui/sidebar/SidebarSection.tsx @@ -2,7 +2,7 @@ import { ChevronRight } from "lucide-react"; import { useState } from "react"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; +import { Collapsible, CollapsibleTrigger } from "@/components/ui/collapsible"; import { cn } from "@/lib/utils"; interface SidebarSectionProps { @@ -78,11 +78,21 @@ export function SidebarSection({ )} - -
- {children} +
+
+
+ {children} +
- +
); }