sourcebot/packages/web/src/features/fileTree/components/fileTreeItemComponent.tsx

85 lines
2.7 KiB
TypeScript
Raw Normal View History

2025-06-06 19:38:16 +00:00
'use client';
import { FileTreeItem } from "../actions";
2025-06-09 19:51:35 +00:00
import { useEffect, useRef } from "react";
2025-06-06 19:38:16 +00:00
import clsx from "clsx";
import scrollIntoView from 'scroll-into-view-if-needed';
import { ChevronDownIcon, ChevronRightIcon } from "@radix-ui/react-icons";
2025-06-09 19:51:35 +00:00
import { FileTreeItemIcon } from "./fileTreeItemIcon";
2025-06-06 19:38:16 +00:00
export const FileTreeItemComponent = ({
node,
isActive,
depth,
isCollapsed = false,
isCollapseChevronVisible = true,
onClick,
parentRef,
}: {
node: FileTreeItem,
isActive: boolean,
depth: number,
isCollapsed?: boolean,
isCollapseChevronVisible?: boolean,
onClick: () => void,
parentRef: React.RefObject<HTMLDivElement>,
}) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isActive && ref.current) {
scrollIntoView(ref.current, {
scrollMode: 'if-needed',
block: 'center',
behavior: 'instant',
// We only want to scroll if the element is hidden vertically
// in the parent element.
boundary: () => {
if (!parentRef.current || !ref.current) {
return false;
}
const rect = ref.current.getBoundingClientRect();
const parentRect = parentRef.current.getBoundingClientRect();
const completelyAbove = rect.bottom <= parentRect.top;
const completelyBelow = rect.top >= parentRect.bottom;
return completelyAbove || completelyBelow;
}
});
}
}, [isActive, parentRef]);
return (
<div
ref={ref}
className={clsx("flex flex-row gap-1 items-center hover:bg-accent hover:text-accent-foreground rounded-sm cursor-pointer p-0.5", {
'bg-accent': isActive,
})}
style={{ paddingLeft: `${depth * 16}px` }}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
onClick();
}
}}
onClick={onClick}
>
<div
className="flex flex-row gap-1 cursor-pointer w-4 h-4 flex-shrink-0"
>
{isCollapseChevronVisible && (
isCollapsed ? (
<ChevronRightIcon className="w-4 h-4 flex-shrink-0" />
) : (
<ChevronDownIcon className="w-4 h-4 flex-shrink-0" />
)
)}
</div>
2025-06-09 19:51:35 +00:00
<FileTreeItemIcon item={node} />
2025-06-06 19:38:16 +00:00
<span className="text-sm">{node.name}</span>
</div>
)
}