Spaces:
Running
Running
| import { useState } from 'react'; | |
| import { VisualElement } from '../../types/blocks'; | |
| import { ChevronRight, ChevronDown } from 'lucide-react'; | |
| interface ElementTreeProps { | |
| elements: VisualElement[]; | |
| selectedId: string | null; | |
| onSelect: (id: string) => void; | |
| onContextMenu?: (e: React.MouseEvent, id: string) => void; | |
| depth?: number; | |
| } | |
| function TreeItem({ | |
| element, selectedId, onSelect, onContextMenu, depth, | |
| }: { | |
| element: VisualElement; selectedId: string | null; onSelect: (id: string) => void; | |
| onContextMenu?: (e: React.MouseEvent, id: string) => void; depth: number; | |
| }) { | |
| const isSelected = selectedId === element.id; | |
| const hasChildren = element.children && element.children.length > 0; | |
| const [expanded, setExpanded] = useState(true); | |
| return ( | |
| <div> | |
| <div | |
| className={`flex items-center gap-1 px-2 py-1 rounded-md cursor-pointer text-xs transition-colors ${ | |
| isSelected ? 'bg-primary-500/20 text-primary-300' : 'text-surface-300 hover:bg-surface-700/50' | |
| }`} | |
| style={{ paddingLeft: `${8 + depth * 14}px` }} | |
| onClick={() => onSelect(element.id)} | |
| onContextMenu={onContextMenu ? (e) => onContextMenu(e, element.id) : undefined} | |
| > | |
| {hasChildren ? ( | |
| <button onClick={(e) => { e.stopPropagation(); setExpanded(!expanded); }} className="p-0.5"> | |
| {expanded ? <ChevronDown className="w-3 h-3 text-surface-500" /> : <ChevronRight className="w-3 h-3 text-surface-500" />} | |
| </button> | |
| ) : ( | |
| <span className="w-4" /> | |
| )} | |
| <span className="text-primary-400"><</span> | |
| <span className="font-medium">{element.tagName}</span> | |
| <span className="text-primary-400">></span> | |
| {element.props?.id && <span className="text-surface-500">#{element.props.id}</span>} | |
| {element.props?.textContent && ( | |
| <span className="text-surface-500 truncate max-w-[100px]">"{element.props.textContent}"</span> | |
| )} | |
| </div> | |
| {hasChildren && expanded && ( | |
| <div> | |
| {element.children!.map((child) => ( | |
| <TreeItem key={child.id} element={child} selectedId={selectedId} | |
| onSelect={onSelect} onContextMenu={onContextMenu} depth={depth + 1} /> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |
| export default function ElementTree(props: ElementTreeProps) { | |
| const { elements, selectedId, onSelect, onContextMenu, depth = 0 } = props; | |
| if (!elements || elements.length === 0) { | |
| return <div className="p-4 text-center text-surface-500 text-xs">No elements on the page</div>; | |
| } | |
| return ( | |
| <div className="p-2 space-y-0.5"> | |
| {elements.map((element) => ( | |
| <TreeItem key={element.id} element={element} selectedId={selectedId} | |
| onSelect={onSelect} onContextMenu={onContextMenu} depth={depth} /> | |
| ))} | |
| </div> | |
| ); | |
| } | |