import { useEffect, useMemo, useRef, useState } from "react"; import { ChevronLeft, Expand, Eye, EyeOff } from "lucide-react"; import { cn } from "../../lib/cn"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../ui/accordion"; import { Button } from "../ui/button"; import { Input } from "../ui/input"; import FileSheet from "./FileSheet"; const fieldLabelClasses = "block text-xs font-medium text-muted-foreground"; const compactButtonClasses = "h-8 px-2 text-xs"; const compactIconButtonClasses = "size-8"; const compactInputClasses = "h-8 text-[11px] md:text-[11px] placeholder:text-[11px]"; export default function StepAssemblyFileSheet({ open, isDesktop, width, selectedEntry, viewerLoading, isAssemblyView, assemblyParts, filteredAssemblyParts, selectedPartIds, hoveredPartId, hiddenPartIds, togglePartSelection, clearAssemblySelection, setHoveredListPartId, handleInspectAssemblyPart, handleBackAssemblyInspection, togglePartVisibility, hideSelectedParts, showAllHiddenParts, handleExitAssemblyPartInspection, inspectedAssemblyPart, assemblyBreadcrumbNodes }) { const [partQuery, setPartQuery] = useState(""); const partCount = Array.isArray(assemblyParts) ? assemblyParts.length : 0; const filteredParts = Array.isArray(filteredAssemblyParts) ? filteredAssemblyParts : []; const normalizedPartQuery = partQuery.trim().toLowerCase(); const visibleAssemblyParts = useMemo(() => { if (!normalizedPartQuery) { return filteredParts; } return filteredParts.filter((part) => { const name = String(part?.name || "").toLowerCase(); const label = String(part?.label || "").toLowerCase(); const id = String(part?.id || "").toLowerCase(); return name.includes(normalizedPartQuery) || label.includes(normalizedPartQuery) || id.includes(normalizedPartQuery); }); }, [filteredParts, normalizedPartQuery]); const selectedPartCount = Array.isArray(selectedPartIds) ? selectedPartIds.length : 0; const hiddenPartCount = Array.isArray(hiddenPartIds) ? hiddenPartIds.length : 0; const breadcrumbNodes = Array.isArray(assemblyBreadcrumbNodes) ? assemblyBreadcrumbNodes : []; const canGoBack = breadcrumbNodes.length > 1; const isInspectingLeafPart = String(inspectedAssemblyPart?.nodeType || "").trim() === "part"; const showBackButton = canGoBack || isInspectingLeafPart; const showHideAllAction = selectedPartCount > 1; const showShowAllAction = hiddenPartCount > 1; const partRowRefs = useRef(new Map()); const activePartId = inspectedAssemblyPart?.id || selectedPartIds[selectedPartIds.length - 1] || ""; useEffect(() => { if (!activePartId) { return; } const target = partRowRefs.current.get(activePartId); target?.scrollIntoView?.({ block: "nearest" }); }, [activePartId]); if (!selectedEntry || !isAssemblyView) { return null; } return ( Parts
{showBackButton ? ( ) : null}
{ if (event.target === event.currentTarget) { clearAssemblySelection(); } }} > {viewerLoading && !partCount ? (

Loading assembly parts...

) : null} {visibleAssemblyParts.map((part) => { const selected = selectedPartIds.includes(part.id); const hovered = hoveredPartId === part.id; const leafPartIds = Array.isArray(part.leafPartIds) && part.leafPartIds.length ? part.leafPartIds : [part.id]; const hidden = leafPartIds.every((id) => hiddenPartIds.includes(id)); const inspecting = inspectedAssemblyPart?.id === part.id; const VisibilityIcon = hidden ? EyeOff : Eye; const partLabel = part.name || part.displayName || part.id; const visibilityLabel = hidden ? "Show" : "Hide"; return (
{ if (node) { partRowRefs.current.set(part.id, node); return; } partRowRefs.current.delete(part.id); }} className={cn( "rounded-md", selected || inspecting ? "bg-[var(--ui-panel-muted)] shadow-[inset_2px_0_0_var(--ui-accent)]" : hovered ? "bg-[var(--ui-panel-muted)]" : "bg-transparent", hidden && "opacity-45" )} >
); })} {!visibleAssemblyParts.length && !viewerLoading ? (

{normalizedPartQuery ? "No assembly parts match this filter." : "No assembly parts are available."}

) : null}
); }