MCPcopy Create free account
hub / github.com/bytebase/bytebase / Tree

Function Tree

frontend/src/react/components/ui/tree.tsx:51–216  ·  view source on GitHub ↗
({
  data,
  renderNode,
  selectedIds,
  expandedIds,
  onSelect,
  onToggle,
  searchTerm,
  searchMatch,
  includeChildrenOnSearchMatch = false,
  height = 300,
  rowHeight = 28,
  indent = 16,
  onMove,
  disableDrag = true,
  disableDrop = true,
  className,
}: TreeProps<T>)

Source from the content-addressed store, hash-verified

49}
50
51export function Tree<T>({
52 data,
53 renderNode,
54 selectedIds,
55 expandedIds,
56 onSelect,
57 onToggle,
58 searchTerm,
59 searchMatch,
60 includeChildrenOnSearchMatch = false,
61 height = 300,
62 rowHeight = 28,
63 indent = 16,
64 onMove,
65 disableDrag = true,
66 disableDrop = true,
67 className,
68}: TreeProps<T>) {
69 const { t } = useTranslation();
70 const treeRef = useRef<TreeApi<TreeDataNode<T>> | null>(null);
71 const prevExpandedRef = useRef<readonly string[] | undefined>(undefined);
72 // react-arborist fires `onToggle` synchronously inside `tree.open()` /
73 // `tree.close()` calls. If we forward those programmatic notifications to
74 // the consumer's onToggle (which usually toggles the id in/out of the
75 // expanded set), we get a feedback loop: setting expandedIds to N keys
76 // → primitive opens N keys → Arborist fires onToggle for each → consumer
77 // toggles keys back out → primitive closes them → onToggle fires again.
78 // The flag below suppresses consumer notifications during the
79 // programmatic batch so the consumer's state stays stable.
80 const programmaticToggleRef = useRef(false);
81 // When the user clicks the chevron, react-arborist toggles its INTERNAL
82 // open state synchronously AND fires onToggle. The consumer then mirrors
83 // that change in `expandedIds`, which fires the sync effect below — but
84 // a re-applied `tree.open(id)` / `tree.close(id)` on the same id forces
85 // react-arborist to recompute its visible-rows list a second time. For
86 // a "Columns" node with hundreds of descendants that doubled the cost
87 // of every collapse. Track the id the user just toggled and skip
88 // syncing it on the next effect pass — react-arborist's internal state
89 // is already in sync for that node.
90 const userToggledIdRef = useRef<string | null>(null);
91
92 // Sync expandedIds to the arborist tree programmatically.
93 //
94 // We use `useLayoutEffect` (not `useEffect`) deliberately: when the
95 // consumer flips `expandedIds`, the FIRST commit shows the tree with
96 // the new `expandedIds` prop but arborist's internal open-state is
97 // still the OLD value, so the rendered rows haven't changed yet. The
98 // sync below calls `tree.open(id)` / `tree.close(id)` which triggers
99 // a SECOND commit inside arborist. With `useEffect` (passive), the
100 // browser can paint the first (stale) commit before the second one
101 // runs, and on some interactions only the next input event seemed to
102 // give the browser a chance to flush the second paint — users saw
103 // "click did nothing until I moved the mouse." `useLayoutEffect`
104 // runs after commit but BEFORE paint, so the sync happens between
105 // the two commits and the browser paints the final state in one go.
106 useLayoutEffect(() => {
107 const tree = treeRef.current;
108 if (!tree) return;

Callers

nothing calls this directly

Calls 6

cnFunction · 0.90
renderNodeFunction · 0.85
openMethod · 0.80
searchMatchFunction · 0.50
tFunction · 0.50
closeMethod · 0.45

Tested by

no test coverage detected