import { useEffect, useState } from 'react' import { useGraphStore, RawNodeType, RawEdgeType } from '@/stores/graph' import Text from '@/components/ui/Text' import Button from '@/components/ui/Button' import useLightragGraph from '@/hooks/useLightragGraph' import { useTranslation } from 'react-i18next' import { GitBranchPlus, Scissors } from 'lucide-react' import EditablePropertyRow from './EditablePropertyRow' /** * Component that view properties of elements in graph. */ const PropertiesView = () => { const { getNode, getEdge } = useLightragGraph() const selectedNode = useGraphStore.use.selectedNode() const focusedNode = useGraphStore.use.focusedNode() const selectedEdge = useGraphStore.use.selectedEdge() const focusedEdge = useGraphStore.use.focusedEdge() const [currentElement, setCurrentElement] = useState(null) const [currentType, setCurrentType] = useState<'node' | 'edge' | null>(null) useEffect(() => { let type: 'node' | 'edge' | null = null let element: RawNodeType | RawEdgeType | null = null if (focusedNode) { type = 'node' element = getNode(focusedNode) } else if (selectedNode) { type = 'node' element = getNode(selectedNode) } else if (focusedEdge) { type = 'edge' element = getEdge(focusedEdge, true) } else if (selectedEdge) { type = 'edge' element = getEdge(selectedEdge, true) } if (element) { if (type == 'node') { setCurrentElement(refineNodeProperties(element as any)) } else { setCurrentElement(refineEdgeProperties(element as any)) } setCurrentType(type) } else { setCurrentElement(null) setCurrentType(null) } }, [ focusedNode, selectedNode, focusedEdge, selectedEdge, setCurrentElement, setCurrentType, getNode, getEdge ]) if (!currentElement) { return <> } return (
{currentType == 'node' ? ( ) : ( )}
) } type NodeType = RawNodeType & { relationships: { type: string id: string label: string }[] } type EdgeType = RawEdgeType & { sourceNode?: RawNodeType targetNode?: RawNodeType } const refineNodeProperties = (node: RawNodeType): NodeType => { const state = useGraphStore.getState() const relationships = [] if (state.sigmaGraph && state.rawGraph) { try { if (!state.sigmaGraph.hasNode(node.id)) { return { ...node, relationships: [] } } const edges = state.sigmaGraph.edges(node.id) for (const edgeId of edges) { if (!state.sigmaGraph.hasEdge(edgeId)) continue; const edge = state.rawGraph.getEdge(edgeId, true) if (edge) { const isTarget = node.id === edge.source const neighbourId = isTarget ? edge.target : edge.source if (!state.sigmaGraph.hasNode(neighbourId)) continue; const neighbour = state.rawGraph.getNode(neighbourId) if (neighbour) { relationships.push({ type: 'Neighbour', id: neighbourId, label: neighbour.properties['entity_id'] ? neighbour.properties['entity_id'] : neighbour.labels.join(', ') }) } } } } catch (error) { console.error('Error refining node properties:', error) } } return { ...node, relationships } } const refineEdgeProperties = (edge: RawEdgeType): EdgeType => { const state = useGraphStore.getState() let sourceNode: RawNodeType | undefined = undefined let targetNode: RawNodeType | undefined = undefined if (state.sigmaGraph && state.rawGraph) { try { if (!state.sigmaGraph.hasEdge(edge.id)) { return { ...edge, sourceNode: undefined, targetNode: undefined } } if (state.sigmaGraph.hasNode(edge.source)) { sourceNode = state.rawGraph.getNode(edge.source) } if (state.sigmaGraph.hasNode(edge.target)) { targetNode = state.rawGraph.getNode(edge.target) } } catch (error) { console.error('Error refining edge properties:', error) } } return { ...edge, sourceNode, targetNode } } const PropertyRow = ({ name, value, onClick, tooltip, entityId, entityType, sourceId, targetId, isEditable = false }: { name: string value: any onClick?: () => void tooltip?: string entityId?: string entityType?: 'node' | 'edge' sourceId?: string targetId?: string isEditable?: boolean }) => { const { t } = useTranslation() const getPropertyNameTranslation = (name: string) => { const translationKey = `graphPanel.propertiesView.node.propertyNames.${name}` const translation = t(translationKey) return translation === translationKey ? name : translation } // Use EditablePropertyRow for editable fields (description, entity_id and keywords) if (isEditable && (name === 'description' || name === 'entity_id' || name === 'keywords')) { return ( ) } // For non-editable fields, use the regular Text component return (
{getPropertyNameTranslation(name)}:
) } const NodePropertiesView = ({ node }: { node: NodeType }) => { const { t } = useTranslation() const handleExpandNode = () => { useGraphStore.getState().triggerNodeExpand(node.id) } const handlePruneNode = () => { useGraphStore.getState().triggerNodePrune(node.id) } return (

{t('graphPanel.propertiesView.node.title')}

{ useGraphStore.getState().setSelectedNode(node.id, true) }} />

{t('graphPanel.propertiesView.node.properties')}

{Object.keys(node.properties) .sort() .map((name) => { return ( ) })}
{node.relationships.length > 0 && ( <>

{t('graphPanel.propertiesView.node.relationships')}

{node.relationships.map(({ type, id, label }) => { return ( { useGraphStore.getState().setSelectedNode(id, true) }} /> ) })}
)}
) } const EdgePropertiesView = ({ edge }: { edge: EdgeType }) => { const { t } = useTranslation() return (

{t('graphPanel.propertiesView.edge.title')}

{edge.type && } { useGraphStore.getState().setSelectedNode(edge.source, true) }} /> { useGraphStore.getState().setSelectedNode(edge.target, true) }} />

{t('graphPanel.propertiesView.edge.properties')}

{Object.keys(edge.properties) .sort() .map((name) => { return ( ) })}
) } export default PropertiesView