Skip to content

Commit

Permalink
Highlight related edges and cardinalities when a TableNode is active.
Browse files Browse the repository at this point in the history
  • Loading branch information
FunamaYukina committed Dec 17, 2024
1 parent 3fe2baa commit a7674be
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 31 deletions.
6 changes: 6 additions & 0 deletions frontend/.changeset/sour-candles-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@liam-hq/erd-core": patch
"@liam-hq/cli": patch
---

Highlight related edges and cardinalities when a TableNode is active.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
useEdgesState,
useNodesState,
} from '@xyflow/react'
import { type FC, useCallback } from 'react'
import { type FC, useCallback, useState } from 'react'
import styles from './ERDContent.module.css'
import { ERDContentProvider, useERDContentContext } from './ERDContentContext'
import { RelationshipEdge } from './RelationshipEdge'
Expand Down Expand Up @@ -65,47 +65,66 @@ export const ERDContentInner: FC<Props> = ({
const {
state: { loading },
} = useERDContentContext()
const [clickedNodeId, setClickedNodeId] = useState<string | null>(null)

useInitialAutoLayout()
useActiveTableNameFromUrl()
useFitViewWhenActiveTableChange(
enabledFeatures?.fitViewWhenActiveTableChange ?? true,
)

const handleMouseEnterNode: NodeMouseHandler<Node> = useCallback(
(_, { id }) => {
const handleNodeClick = useCallback(
(nodeId: string) => {
setClickedNodeId(nodeId)

const relatedEdges = edges.filter(
(e) => e.source === id || e.target === id,
(e) => e.source === nodeId || e.target === nodeId,
)

const updatedEdges = edges.map((e) =>
relatedEdges.includes(e)
? { ...e, animated: true, data: { ...e.data, isHighlighted: true } }
: e,
: {
...e,
animated: false,
data: { ...e.data, isHighlighted: false },
},
)

const updatedNodes = nodes.map((node) => {
if (node.id === id) {
if (node.id === nodeId) {
return { ...node, data: { ...node.data, isHighlighted: true } }
}

const isRelated = isRelatedToTable(relationships, node.id, id)
const isRelated = isRelatedToTable(relationships, node.id, nodeId)

if (isRelated) {
const highlightedTargetHandles = relatedEdges
.filter((edge) => edge.source === nodeId && edge.target === node.id)
.map((edge) => edge.targetHandle)

const highlightedTargetHandles = relatedEdges
.filter((edge) => edge.source === id && edge.target === node.id)
.map((edge) => edge.targetHandle)
const highlightedSourceHandles = relatedEdges
.filter((edge) => edge.target === nodeId && edge.source === node.id)
.map((edge) => edge.sourceHandle)

const highlightedSourceHandles = relatedEdges
.filter((edge) => edge.target === id && edge.source === node.id)
.map((edge) => edge.sourceHandle)
return {
...node,
data: {
...node.data,
isRelated: isRelated,
highlightedHandles:
highlightedTargetHandles.concat(highlightedSourceHandles) || [],
},
}
}

return {
...node,
data: {
...node.data,
isRelated: isRelated,
highlightedHandles:
highlightedTargetHandles.concat(highlightedSourceHandles) || [],
isRelated: false,
isHighlighted: false,
highlightedHandles: [],
},
}
})
Expand All @@ -116,49 +135,196 @@ export const ERDContentInner: FC<Props> = ({
[edges, nodes, setNodes, setEdges, relationships],
)

const handleMouseLeaveNode: NodeMouseHandler<Node> = useCallback(
const handlePaneClick = useCallback(() => {
setClickedNodeId(null)

const updatedEdges = edges.map((e) => ({
...e,
animated: false,
data: { ...e.data, isHighlighted: false },
}))

const updatedNodes = nodes.map((node) => ({
...node,
data: {
...node.data,
isRelated: false,
highlightedHandles: [],
isHighlighted: false,
},
}))

setEdges(updatedEdges)
setNodes(updatedNodes)
}, [edges, nodes, setNodes, setEdges])

const handleMouseEnterNode: NodeMouseHandler<Node> = useCallback(
(_, { id }) => {
const relatedEdges = edges.filter(
(e) => e.source === id || e.target === id,
)

const updatedEdges = edges.map((e) =>
e.source === id || e.target === id
? {
...e,
animated: false,
data: { ...e.data, isHighlighted: false },
}
relatedEdges.includes(e)
? { ...e, animated: true, data: { ...e.data, isHighlighted: true } }
: e,
)

const updatedNodes = nodes.map((node) => {
return {
if (node.id === id) {
return { ...node, data: { ...node.data, isHighlighted: true } }
}

const isRelated = isRelatedToTable(relationships, node.id, id)

if (isRelated) {
const highlightedTargetHandles = relatedEdges
.filter((edge) => edge.source === id && edge.target === node.id)
.map((edge) => edge.targetHandle)

const highlightedSourceHandles = relatedEdges
.filter((edge) => edge.target === id && edge.source === node.id)
.map((edge) => edge.sourceHandle)

return {
...node,
data: {
...node.data,
isRelated: isRelated,
highlightedHandles:
highlightedTargetHandles.concat(highlightedSourceHandles) || [],
},
}
}

return node
})

setEdges(updatedEdges)
setNodes(updatedNodes)
},
[edges, nodes, setNodes, setEdges, relationships],
)

const handleMouseLeaveNode: NodeMouseHandler<Node> = useCallback(
(_, { id }) => {
// If a node is clicked, do not remove the highlight
if (clickedNodeId) {
const relatedEdges = edges.filter(
(e) => e.source === clickedNodeId || e.target === clickedNodeId,
)
const updatedEdges = edges.map((e) =>
relatedEdges.includes(e)
? {
...e,
animated: true,
data: { ...e.data, isHighlighted: true },
}
: {
...e,
animated: false,
data: { ...e.data, isHighlighted: false },
},
)
setEdges(updatedEdges)

const updatedNodes = nodes.map((node) => {
const isRelated = isRelatedToTable(
relationships,
node.id,
clickedNodeId,
)

if (node.id === clickedNodeId || isRelated) {
const highlightedTargetHandles = relatedEdges
.filter(
(edge) =>
edge.source === clickedNodeId && edge.target === node.id,
)
.map((edge) => edge.targetHandle)

const highlightedSourceHandles = relatedEdges
.filter(
(edge) =>
edge.target === clickedNodeId && edge.source === node.id,
)
.map((edge) => edge.sourceHandle)

const isHighlighted = node.id === clickedNodeId

return {
...node,
data: {
...node.data,
isRelated: isRelated,
isHighlighted: isHighlighted,
highlightedHandles:
highlightedTargetHandles.concat(highlightedSourceHandles) ||
[],
},
}
}

return {
...node,
data: {
...node.data,
isRelated: false,
isHighlighted: false,
highlightedHandles: [],
},
}
})

setNodes(updatedNodes)
} else {
const updatedEdges = edges.map((e) =>
e.source === id || e.target === id
? {
...e,
animated: false,
data: { ...e.data, isHighlighted: false },
}
: e,
)
setEdges(updatedEdges)

const updatedNodes = nodes.map((node) => ({
...node,
data: {
...node.data,
isRelated: false,
highlightedHandles: [],
isHighlighted: false,
},
}
})

setEdges(updatedEdges)
setNodes(updatedNodes)
}))
setNodes(updatedNodes)
}
},
[edges, nodes, setNodes, setEdges],
[edges, nodes, setNodes, setEdges, clickedNodeId, relationships],
)

const panOnDrag = [1, 2]

return (
<div className={styles.wrapper} data-loading={loading}>
<ReactFlow
nodes={nodes}
nodes={nodes.map((node) => ({
...node,
data: {
...node.data,
onClick: handleNodeClick,
},
}))}
edges={edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
minZoom={0.1}
maxZoom={2}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={(_, node) => handleNodeClick(node.id)}
onPaneClick={handlePaneClick}
onNodeMouseEnter={handleMouseEnterNode}
onNodeMouseLeave={handleMouseLeaveNode}
panOnScroll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const TableColumn: FC<TableColumnProps> = ({
isSource,
targetCardinality,
}) => {
// console.log('highlightedHandles', highlightedHandles)
const handleId = `${table.name}-${column.name}`

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type Data = {
highlightedHandles: string[]
sourceColumnName: string | undefined
targetColumnCardinalities?: Record<string, Cardinality> | undefined
onClick?: (tableName: string) => void
}

export type TableNodeType = Node<Data, 'table'>

0 comments on commit a7674be

Please sign in to comment.