@ -4,7 +4,6 @@ import {
useEffect ,
useLayoutEffect ,
useMemo ,
useRef ,
useState ,
} from "react" ;
import ASTNode , { nodeType } from "../../promql/ast" ;
@ -60,7 +59,7 @@ const TreeNode: FC<{
node : ASTNode ;
selectedNode : { id : string ; node : ASTNode } | null ;
setSelectedNode : ( Node : { id : string ; node : ASTNode } | null ) = > void ;
parent Ref?: React.RefObject < HTMLDivElement > ;
parent El?: HTMLDivElement | null ;
reportNodeState ? : ( childIdx : number , state : NodeState ) = > void ;
reverse : boolean ;
// The index of this node in its parent's children.
@ -69,13 +68,21 @@ const TreeNode: FC<{
node ,
selectedNode ,
setSelectedNode ,
parent Ref ,
parent El ,
reportNodeState ,
reverse ,
childIdx ,
} ) = > {
const nodeID = useId ( ) ;
const nodeRef = useRef < HTMLDivElement > ( null ) ;
// A normal ref won't work properly here because the ref's `current` property
// going from `null` to defined won't trigger a re-render of the child
// component, since it's not a React state update. So we manually have to
// create a state update using a callback ref. See also
// https://tkdodo.eu/blog/avoiding-use-effect-with-callback-refs
const [ nodeEl , setNodeEl ] = useState < HTMLDivElement | null > ( null ) ;
const nodeRef = useCallback ( ( node : HTMLDivElement ) = > setNodeEl ( node ) , [ ] ) ;
const [ connectorStyle , setConnectorStyle ] = useState < CSSProperties > ( {
borderColor :
"light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-3))" ,
@ -97,10 +104,10 @@ const TreeNode: FC<{
// Select the node when it is mounted and it is the root of the tree.
useEffect ( ( ) = > {
if ( parent Ref === undefined ) {
if ( parent El === undefined ) {
setSelectedNode ( { id : nodeID , node : node } ) ;
}
} , [ parent Ref , setSelectedNode , nodeID , node ] ) ;
} , [ parent El , setSelectedNode , nodeID , node ] ) ;
// Deselect node when node is unmounted.
useEffect ( ( ) = > {
@ -173,16 +180,18 @@ const TreeNode: FC<{
// Update the size and position of tree connector lines based on the node's and its parent's position.
useLayoutEffect ( ( ) = > {
if ( parent Ref === undefined ) {
if ( parent El === undefined ) {
// We're the root node.
return ;
}
if ( parentRef . current === null || nodeRef . current === null ) {
if ( parentEl === null || nodeEl === null ) {
// Either of the two connected nodes hasn't been rendered yet.
return ;
}
const parentRect = parentRef . current . getBoundingClientRect ( ) ;
const nodeRect = nodeRef . current . getBoundingClientRect ( ) ;
const parentRect = parentEl . getBoundingClientRect ( ) ;
const nodeRect = nodeEl . getBoundingClientRect ( ) ;
if ( reverse ) {
setConnectorStyle ( ( prevStyle ) = > ( {
. . . prevStyle ,
@ -202,7 +211,7 @@ const TreeNode: FC<{
borderTopLeftRadius : undefined ,
} ) ) ;
}
} , [ parent Ref , reverse , nodeRef , setConnectorStyle ] ) ;
} , [ parent El, nodeEl , reverse , nodeRef , setConnectorStyle ] ) ;
// Update the node info state based on the query result.
useEffect ( ( ) = > {
@ -264,7 +273,7 @@ const TreeNode: FC<{
pos = "relative"
align = "center"
>
{ parent Ref && (
{ parent El !== undefined && (
// Connector line between this node and its parent.
< Box pos = "absolute" display = "inline-block" style = { connectorStyle } / >
) }
@ -391,7 +400,7 @@ const TreeNode: FC<{
node = { children [ 0 ] }
selectedNode = { selectedNode }
setSelectedNode = { setSelectedNode }
parent Ref= { nodeRef }
parent El= { nodeEl }
reverse = { true }
childIdx = { 0 }
reportNodeState = { childReportNodeState }
@ -403,7 +412,7 @@ const TreeNode: FC<{
node = { children [ 1 ] }
selectedNode = { selectedNode }
setSelectedNode = { setSelectedNode }
parent Ref= { nodeRef }
parent El= { nodeEl }
reverse = { false }
childIdx = { 1 }
reportNodeState = { childReportNodeState }
@ -422,7 +431,7 @@ const TreeNode: FC<{
node = { child }
selectedNode = { selectedNode }
setSelectedNode = { setSelectedNode }
parent Ref= { nodeRef }
parent El= { nodeEl }
reverse = { false }
childIdx = { idx }
reportNodeState = { childReportNodeState }