Skip to content

Commit f21dd35

Browse files
Merge pull request #222 from neo4j-labs/Add-legends-or-hover-to-graph-model
Add legends or hover to graph model
2 parents 440d8ac + 96dfc38 commit f21dd35

File tree

4 files changed

+96
-49
lines changed

4 files changed

+96
-49
lines changed

frontend/src/App.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,28 @@
190190
padding: 20px;
191191
align-items: center;
192192
justify-content: center;
193+
}
194+
195+
.legend_div {
196+
display: flex;
197+
flex: 0.2;
198+
flex-wrap: wrap;
199+
padding: 15px;
200+
gap: 8px;
201+
background-color: #ffff;
202+
height: max-content
203+
}
204+
205+
.legend {
206+
display: inline-block;
207+
min-width: 0px;
208+
cursor: pointer;
209+
border-radius: 12px;
210+
padding: 2px 8px;
211+
font-size: var(--font-size-label);
212+
font-weight: var(--font-weight-bold);
213+
letter-spacing: 0;
214+
line-height: 1.25rem;
215+
width: max-content;
216+
height: 30px
193217
}

frontend/src/components/GraphViewModal.tsx

Lines changed: 67 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { Banner, Checkbox, Dialog, IconButton, IconButtonArray, LoadingSpinner, TextInput } from '@neo4j-ndl/react';
1+
import {
2+
Banner,
3+
Checkbox,
4+
Dialog,
5+
Flex,
6+
IconButton,
7+
IconButtonArray,
8+
LoadingSpinner,
9+
TextInput,
10+
} from '@neo4j-ndl/react';
211
import { useEffect, useRef, useState } from 'react';
312
import { GraphType, GraphViewModalProps } from '../types';
413
import { InteractiveNvlWrapper } from '@neo4j-nvl/react';
514
import NVL, { NvlOptions } from '@neo4j-nvl/core';
6-
// import { driver } from '../utils/Driver';
715
import type { Node, Relationship } from '@neo4j-nvl/core';
16+
817
import {
918
FitToScreenIcon,
1019
MagnifyingGlassMinusIconOutline,
@@ -43,6 +52,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
4352
const [statusMessage, setStatusMessage] = useState<string>('');
4453
const [docLimit, setDocLimit] = useState<string>('3');
4554
const { driver } = useCredentials();
55+
const [scheme, setScheme] = useState<Scheme>({});
4656

4757
const handleCheckboxChange = (graph: GraphType) => {
4858
const currentIndex = graphType.indexOf(graph);
@@ -98,27 +108,27 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
98108
graphType.length === 3
99109
? queryMap.DocChunkEntities
100110
: graphType.includes('Entities') && graphType.includes('Chunks')
101-
? queryMap.ChunksEntities
102-
: graphType.includes('Entities') && graphType.includes('Document')
103-
? queryMap.DocEntities
104-
: graphType.includes('Document') && graphType.includes('Chunks')
105-
? queryMap.DocChunks
106-
: graphType.includes('Entities') && graphType.length === 1
107-
? queryMap.Entities
108-
: graphType.includes('Chunks') && graphType.length === 1
109-
? queryMap.Chunks
110-
: queryMap.Document;
111+
? queryMap.ChunksEntities
112+
: graphType.includes('Entities') && graphType.includes('Document')
113+
? queryMap.DocEntities
114+
: graphType.includes('Document') && graphType.includes('Chunks')
115+
? queryMap.DocChunks
116+
: graphType.includes('Entities') && graphType.length === 1
117+
? queryMap.Entities
118+
: graphType.includes('Chunks') && graphType.length === 1
119+
? queryMap.Chunks
120+
: queryMap.Document;
111121
if (viewPoint === 'showGraphView') {
112122
queryToRun = constructQuery(newCheck, documentNo);
113-
console.log('inside If QueryToRun', queryToRun);
123+
console.log('showGraph', queryToRun);
114124
} else {
115-
queryToRun = constructDocQuery(newCheck, inspectedName);
116-
console.log('outside QueryToRun', queryToRun);
125+
queryToRun = constructDocQuery(newCheck);
126+
console.log('table', queryToRun);
117127
}
118128
const session = driver?.session();
119129
setLoading(true);
120130
session
121-
?.run(queryToRun, { 'document_name': inspectedName })
131+
?.run(queryToRun, { document_name: inspectedName })
122132
.then((results) => {
123133
if (results.records && results.records.length > 0) {
124134
// @ts-ignore
@@ -128,19 +138,17 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
128138

129139
// Infer color schema dynamically
130140
let iterator = 0;
131-
const scheme: Scheme = {};
141+
const schemeVal: Scheme = {};
132142

133143
neo4jNodes.forEach((node) => {
134144
const labels = node.map((f: any) => f.labels);
135-
136145
labels.forEach((label: any) => {
137-
if (scheme[label] == undefined) {
138-
scheme[label] = colors[iterator % colors.length];
146+
if (schemeVal[label] == undefined) {
147+
schemeVal[label] = colors[iterator % colors.length];
139148
iterator += 1;
140149
}
141150
});
142151
});
143-
144152
const newNodes = neo4jNodes.map((n) => {
145153
const totalNodes = n.map((g: any) => {
146154
return {
@@ -149,7 +157,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
149157
captionAlign: 'bottom',
150158
iconAlign: 'bottom',
151159
captionHtml: <b>Test</b>,
152-
caption: getNodeCaption(g),
160+
caption: `${g.labels}: ${getNodeCaption(g)}`,
153161
color: scheme[g.labels[0]],
154162
icon: getIcon(g),
155163
};
@@ -171,6 +179,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
171179
const finalRels = newRels.flat();
172180
setNodes(finalNodes);
173181
setRelationships(finalRels);
182+
setScheme(schemeVal);
174183
setLoading(false);
175184
console.log('nodes', nodes);
176185
console.log('relations', relationships);
@@ -229,11 +238,11 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
229238
nvlRef.current?.setZoom(nvlRef.current.getScale() * 0.7);
230239
};
231240

232-
const onClose=()=>{
241+
const onClose = () => {
233242
setStatus('unknown');
234243
setStatusMessage('');
235-
setGraphViewOpen(false)
236-
}
244+
setGraphViewOpen(false);
245+
};
237246

238247
return (
239248
<>
@@ -306,28 +315,39 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
306315
</div>
307316
) : (
308317
<>
309-
<InteractiveNvlWrapper
310-
nodes={nodes}
311-
rels={relationships}
312-
nvlOptions={nvlOptions}
313-
ref={nvlRef}
314-
mouseEventCallbacks={{ ...mouseEventCallbacks }}
315-
interactionOptions={{
316-
selectOnClick: true,
317-
}}
318-
nvlCallbacks={nvlCallbacks}
319-
/>
320-
<IconButtonArray orientation='vertical' floating className='absolute bottom-4 right-4'>
321-
<ButtonWithToolTip text='Zoom in' onClick={handleZoomIn}>
322-
<MagnifyingGlassPlusIconOutline />
323-
</ButtonWithToolTip>
324-
<ButtonWithToolTip text='Zoom out' onClick={handleZoomOut}>
325-
<MagnifyingGlassMinusIconOutline />
326-
</ButtonWithToolTip>
327-
<ButtonWithToolTip text='Zoom to fit' onClick={handleZoomToFit}>
328-
<FitToScreenIcon />
329-
</ButtonWithToolTip>
330-
</IconButtonArray>
318+
<Flex flexDirection='row' justifyContent='space-between' style={{ height: '100%', padding: '20px' }}>
319+
<div className='legend_div'>
320+
{Object.keys(scheme).map((key) => (
321+
<div className='legend' key={scheme.key} style={{ backgroundColor: `${scheme[key]}` }}>
322+
{key}
323+
</div>
324+
))}
325+
</div>
326+
<div style={{ flex: '0.8' }}>
327+
<InteractiveNvlWrapper
328+
nodes={nodes}
329+
rels={relationships}
330+
nvlOptions={nvlOptions}
331+
ref={nvlRef}
332+
mouseEventCallbacks={{ ...mouseEventCallbacks }}
333+
interactionOptions={{
334+
selectOnClick: true,
335+
}}
336+
nvlCallbacks={nvlCallbacks}
337+
/>
338+
<IconButtonArray orientation='vertical' floating className='absolute bottom-4 right-4'>
339+
<ButtonWithToolTip text='Zoom in' onClick={handleZoomIn}>
340+
<MagnifyingGlassPlusIconOutline />
341+
</ButtonWithToolTip>
342+
<ButtonWithToolTip text='Zoom out' onClick={handleZoomOut}>
343+
<MagnifyingGlassMinusIconOutline />
344+
</ButtonWithToolTip>
345+
<ButtonWithToolTip text='Zoom to fit' onClick={handleZoomToFit}>
346+
<FitToScreenIcon />
347+
</ButtonWithToolTip>
348+
</IconButtonArray>
349+
</div>
350+
</Flex>
331351
</>
332352
)}
333353
</div>

frontend/src/utils/Constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export const colors = [
3939
'#d9ad7c',
4040
'#a2836e',
4141
'#674d3c',
42+
'#d7b69f',
43+
'#00ffff',
44+
'##8eb9ff',
4245
];
4346
export const llms =
4447
process.env?.LLM_MODELS?.trim() != ''

frontend/src/utils/Utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ export const constructQuery = (queryTochange: string, docLimit: string) => {
9292
RETURN nodes, rels`;
9393
};
9494

95-
export const constructDocQuery = (queryTochange: string, inspectedName: string) => {
95+
export const constructDocQuery = (queryTochange: string) => {
9696
return `
9797
MATCH docs = (d:Document {status:'Completed'})
98-
WHERE d.fileName = '${inspectedName}'
98+
WHERE d.fileName = $document_name
9999
WITH docs, d ORDER BY d.createdAt DESC
100100
OPTIONAL MATCH chunks=(d)<-[:PART_OF]-(c:Chunk)
101101
WITH []

0 commit comments

Comments
 (0)