File size: 2,866 Bytes
d081604
ce2d6ca
 
 
 
 
 
 
d081604
ce2d6ca
 
 
 
d081604
ce2d6ca
d081604
 
ce2d6ca
 
 
d081604
ce2d6ca
 
 
 
d081604
 
ce2d6ca
 
 
d081604
ce2d6ca
d081604
 
 
 
 
 
 
ce2d6ca
 
 
d081604
 
 
ce2d6ca
 
d081604
ce2d6ca
 
d081604
 
ce2d6ca
 
 
 
 
 
 
d081604
 
 
 
ce2d6ca
 
 
 
d081604
 
ce2d6ca
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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">&lt;</span>
        <span className="font-medium">{element.tagName}</span>
        <span className="text-primary-400">&gt;</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>
  );
}