Skip to content

Commit cec8a6e

Browse files
committed
* Add styles changes according to designer doc
* Prettify property table and fix edge style on hover info * Fix duplication of edges
1 parent 8111e2d commit cec8a6e

File tree

5 files changed

+269
-259
lines changed

5 files changed

+269
-259
lines changed

redisinsight/ui/src/packages/redisgraph/src/Graph.tsx

Lines changed: 136 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,112 @@ import {
99
EuiToolTip,
1010
} from '@elastic/eui'
1111

12+
enum EntityType {
13+
Node = 'Node',
14+
Edge = 'Edge'
15+
}
16+
1217
interface ISelectedEntityProps {
1318
property: string
1419
color: string
1520
backgroundColor: string
1621
props: { [key: string]: string | number | object }
22+
type: EntityType
23+
}
24+
25+
const isDarkTheme = document.body.classList.contains('theme_DARK')
26+
27+
const colorPicker = (COLORS: Utils.IGoodColor[], isDarkTheme: boolean) => {
28+
const color = new Utils.GoodColorPicker(COLORS, isDarkTheme)
29+
return (label: string) => color.getColor(label)
1730
}
1831

19-
export default function Graph2(props: { graphKey: string, data: any[] }) {
32+
const labelColors = colorPicker(Utils.NODE_COLORS, isDarkTheme)
33+
const edgeColors = colorPicker(Utils.EDGE_COLORS, isDarkTheme)
34+
35+
export default function Graph(props: { graphKey: string, data: any[] }) {
2036

2137
const d3Container = useRef<HTMLDivElement>()
2238
const [container, setContainer] = useState(null)
2339
const [selectedEntity, setSelectedEntity] = useState<ISelectedEntityProps | null>(null)
2440
const [start, setStart] = useState(false)
2541

26-
const colorPicker = (COLORS: Utils.IGoodColor[]) => {
27-
const color = new Utils.GoodColorPicker(COLORS)
28-
return (label: string) => color.getColor(label)
29-
}
30-
31-
const labelColors = colorPicker(Utils.NODE_COLORS)
32-
const edgeColors = colorPicker(Utils.EDGE_COLORS)
33-
3442
const parsedResponse = responseParser(props.data)
35-
let nodeIds = parsedResponse.nodes.map(n => n.id)
43+
let nodeIds = new Set(parsedResponse.nodes.map(n => n.id))
44+
let edgeIds = new Set(parsedResponse.edges.map(e => e.id))
3645
let data = {
3746
results: [{
3847
columns: parsedResponse.headers,
3948
data: [{
4049
graph: {
4150
nodes: parsedResponse.nodes,
42-
relationships: parsedResponse.edges.filter(e => nodeIds.includes(e.source) && nodeIds.includes(e.target)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
51+
relationships: parsedResponse.edges.filter(e => nodeIds.has(e.source) && nodeIds.has(e.target)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
4352
}
4453
}]
4554
}],
4655
errors: [],
4756
}
4857

58+
let nodeLabels = new Set(parsedResponse.labels)
59+
let edgeTypes = new Set(parsedResponse.types)
60+
4961
const [graphData, setGraphData] = useState(data)
5062

5163
useMemo(async () => {
5264

5365
let newGraphData = graphData
5466

5567
if (parsedResponse.nodeIds.length > 0) {
56-
/* Fetch named path nodes */
57-
const resp = await globalThis.PluginSDK?.executeRedisCommand(`graph.ro_query "${props.graphKey}" "MATCH (n) WHERE id(n) IN [${parsedResponse.nodeIds}] RETURN n"`);
68+
try {
69+
/* Fetch named path nodes */
70+
const resp = await globalThis.PluginSDK?.executeRedisCommand(`graph.ro_query "${props.graphKey}" "MATCH (n) WHERE id(n) IN [${[...parsedResponse.nodeIds]}] RETURN n"`);
71+
72+
if (Array.isArray(resp) && (resp.length >= 1 || resp[0].status === 'success')) {
73+
const parsedData = responseParser(resp[0].response)
74+
parsedData.nodes.forEach(n => {
75+
nodeIds.add(n.id)
76+
n.labels.forEach(nodeLabels.add, nodeLabels)
77+
})
78+
79+
parsedData.edges.forEach(e => {
80+
edgeTypes.add(e.type)
81+
})
82+
83+
newGraphData = {
84+
...newGraphData,
85+
results: [
86+
...newGraphData.results,
87+
{
88+
columns: parsedData.headers,
89+
data: [{
90+
graph: {
91+
nodes: parsedData.nodes,
92+
relationships: parsedData.edges.filter(e => nodeIds.has(e.source) && nodeIds.has(e.target) && !edgeIds.has(e.id)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
93+
}
94+
}]
95+
}
96+
]
97+
}
98+
}
99+
} catch {}
100+
}
101+
102+
try {
103+
/* Fetch neighbours automatically */
104+
const resp = await globalThis.PluginSDK?.executeRedisCommand(`graph.ro_query "${props.graphKey}" "MATCH (n)-[t]->(m) WHERE ID(n) IN [${[...nodeIds]}] OR ID(m) IN [${[...nodeIds]}] RETURN DISTINCT t"`);
58105

59106
if (Array.isArray(resp) && (resp.length >= 1 || resp[0].status === 'success')) {
60107
const parsedData = responseParser(resp[0].response)
61-
nodeIds = [...new Set(nodeIds.concat(parsedData.nodes.map(n => n.id)))]
108+
parsedData.nodes.forEach(n => {
109+
nodeIds.add(n.id)
110+
n.labels.forEach(nodeLabels.add, nodeLabels)
111+
})
62112

63-
newGraphData = {
113+
parsedData.edges.forEach(e => {
114+
edgeTypes.add(e.type)
115+
})
116+
117+
setGraphData({
64118
...newGraphData,
65119
results: [
66120
...newGraphData.results,
@@ -69,43 +123,21 @@ export default function Graph2(props: { graphKey: string, data: any[] }) {
69123
data: [{
70124
graph: {
71125
nodes: parsedData.nodes,
72-
relationships: parsedData.edges.filter(e => nodeIds.includes(e.source) && nodeIds.includes(e.target)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
126+
/* TODO:
127+
* track newly added edges
128+
*/
129+
relationships: parsedData.edges.filter(e => nodeIds.has(e.source) && nodeIds.has(e.target) && !edgeIds.has(e.id)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
73130
}
74131
}]
75132
}
76133
]
77-
}
134+
})
78135
}
79-
}
80-
81-
/* Fetch neighbours automatically */
82-
const resp = await globalThis.PluginSDK?.executeRedisCommand(`graph.ro_query "${props.graphKey}" "MATCH (n)-[t]->(m) WHERE ID(n) IN [${nodeIds}] OR ID(m) IN [${nodeIds}] RETURN DISTINCT t"`);
83-
84-
if (Array.isArray(resp) && (resp.length >= 1 || resp[0].status === 'success')) {
85-
const parsedData = responseParser(resp[0].response)
86-
nodeIds = [...new Set(nodeIds.concat(parsedData.nodes.map(n => n.id)))]
87-
88-
setGraphData({
89-
...newGraphData,
90-
results: [
91-
...newGraphData.results,
92-
{
93-
columns: parsedData.headers,
94-
data: [{
95-
graph: {
96-
nodes: parsedData.nodes,
97-
relationships: parsedData.edges.filter(e => nodeIds.includes(e.source) && nodeIds.includes(e.target)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
98-
}
99-
}]
100-
}
101-
]
102-
})
103-
}
136+
} catch {}
104137

105138
setStart(true)
106139
}, [])
107140

108-
109141
const zoom = d3.zoom().scaleExtent([0, 3]) /* min, mac of zoom */
110142
useEffect(() => {
111143
if (container != null) return;
@@ -143,7 +175,7 @@ export default function Graph2(props: { graphKey: string, data: any[] }) {
143175
data: [{
144176
graph: {
145177
nodes: parsedData.nodes,
146-
relationships: parsedData.edges.map(e => ({ ...e, startNode: e.source, endNode: e.target }))
178+
relationships: parsedData.edges.filter(e => !edgeIds.has(e.id)).map(e => ({ ...e, startNode: e.source, endNode: e.target }))
147179
}
148180
}]
149181
}],
@@ -152,23 +184,27 @@ export default function Graph2(props: { graphKey: string, data: any[] }) {
152184
},
153185
onRelationshipDoubleClick(relationship) {
154186
},
155-
onDisplayInfo: (infoSvg, node) => {
156-
let property;
157-
let color;
187+
onDisplayInfo: (infoSvg, entity) => {
188+
let property: string;
189+
let entityColor: Utils.IGoodColor;
190+
let t: EntityType;
158191

159-
if (node.labels) {
160-
[property] = node.labels;
161-
color = labelColors(property).color
192+
if (entity.labels) {
193+
[property] = entity.labels;
194+
entityColor = labelColors(property)
195+
t = EntityType.Node
162196
} else {
163-
property = node.type;
164-
color = edgeColors(property).color
197+
property = entity.type;
198+
entityColor = edgeColors(property)
199+
t = EntityType.Edge
165200
}
166201

167202
setSelectedEntity({
168203
property,
169-
backgroundColor: color,
170-
props: { '<id>': node.id, ...node.properties },
171-
color: graphd3.invertColor(color)
204+
type: t,
205+
backgroundColor: entityColor.color,
206+
props: { '<id>': entity.id, ...entity.properties },
207+
color: entityColor.textColor
172208
})
173209
},
174210
zoomFit: false,
@@ -180,55 +216,62 @@ export default function Graph2(props: { graphKey: string, data: any[] }) {
180216
return (
181217
<div className="core-container">
182218
<div className="d3-info">
183-
{
184-
parsedResponse.nodes.length > 0 && (
185-
<div className="d3-info-labels">
219+
<div className="graph-legends">
220+
{
221+
parsedResponse.nodes.length > 0 && (
222+
<div className="d3-info-labels">
186223
{
187-
parsedResponse.labels.map((item, i) => (
188-
<div className="d3-info-item" key={item + i}>
189-
<div className="node-label" style={{ background: labelColors(item).color }}></div>
190-
<div className='label-name' style={{ color: labelColors(item).color }}><code>{item}</code></div>
224+
[...nodeLabels].map((item, i) => (
225+
<div
226+
className="box-node-label"
227+
style={{backgroundColor: labelColors(item).color, color: labelColors(item).textColor}}
228+
key={item + i}
229+
>
230+
{item}
191231
</div>
192232
))
193233
}
194234
</div>
195-
)
196-
}
197-
{
198-
parsedResponse.edges.length > 0 && (
199-
<div className="d3-info-labels">
200-
{
201-
[...(new Set(parsedResponse.edges.map(e => e.type)))].map((item, i) => {
202-
return (
203-
<div key={item + i.toString()} className="d3-info-item">
204-
<div className="edge-label-line" style={{ borderColor: edgeColors(item).color }}></div>
205-
<div className="edge-label-arrow" style={{ borderLeftColor: edgeColors(item).color }}></div>
206-
<div className="label-name" style={{ color: edgeColors(item).color }}>{item}</div>
235+
)
236+
}
237+
{
238+
parsedResponse.edges.length > 0 && (
239+
<div className="d3-info-labels">
240+
{
241+
[...edgeTypes].map((item, i) => (
242+
<div
243+
key={item + i.toString()}
244+
className="box-edge-type"
245+
style={{ borderColor: edgeColors(item).color, color: edgeColors(item).color }}
246+
>
247+
{item}
207248
</div>
208-
)
209-
})
210-
}
211-
</div>
212-
)
213-
}
249+
))
250+
}
251+
</div>
252+
)
253+
}
254+
</div>
214255
{
215256
selectedEntity &&
216257
<div className="info-component">
217258
<div className="info-header">
218-
<div className="info-header-type" style={{ backgroundColor: selectedEntity.backgroundColor, color: selectedEntity.color }}>{selectedEntity.property}</div>
219-
<EuiButtonIcon onClick={() => setSelectedEntity(null)} display="empty" iconType="cross" aria-label="Close" />
259+
{
260+
selectedEntity.type === EntityType.Node ?
261+
<div className="box-node-label" style={{ backgroundColor: selectedEntity.backgroundColor, color: selectedEntity.color }}>{selectedEntity.property}</div>
262+
:
263+
<div className='box-edge-type' style={{borderColor: selectedEntity.backgroundColor, color: selectedEntity.backgroundColor }}>{selectedEntity.property}</div>
264+
}
265+
<EuiButtonIcon color="text" onClick={() => setSelectedEntity(null)} display="empty" iconType="cross" aria-label="Close" />
220266
</div>
221267
<div className="info-props">
222-
<table>
223-
{
224-
Object.keys(selectedEntity.props).map(k =>
225-
<tr>
226-
<td>{k}</td>
227-
<td>{selectedEntity.props[k]}</td>
228-
</tr>
229-
)
230-
}
231-
</table>
268+
{
269+
Object.keys(selectedEntity.props).map(k => [k, selectedEntity.props[k]]).reduce(
270+
(a, b) => a.concat(b), []
271+
).map(k =>
272+
<div>{k}</div>
273+
)
274+
}
232275
</div>
233276
</div>
234277
}
@@ -261,16 +304,6 @@ export default function Graph2(props: { graphKey: string, data: any[] }) {
261304
onClick: () => container.zoomFuncs.resetZoom(),
262305
icon: 'bullseye'
263306
},
264-
{
265-
name: 'Pan Left',
266-
onClick: () => container.zoomFuncs.panLeft(),
267-
icon: 'editorItemAlignLeft'
268-
},
269-
{
270-
name: 'Pan Right',
271-
onClick: () => container.zoomFuncs.panRight(),
272-
icon: 'editorItemAlignRight'
273-
},
274307
{
275308
name: 'Center',
276309
onClick: () => container.zoomFuncs.center(),
@@ -279,6 +312,7 @@ export default function Graph2(props: { graphKey: string, data: any[] }) {
279312
].map(item => (
280313
<EuiToolTip position="left" content={item.name}>
281314
<EuiButtonIcon
315+
color='text'
282316
onClick={item.onClick}
283317
iconType={item.icon}
284318
aria-label={item.name}

redisinsight/ui/src/packages/redisgraph/src/graphd3.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,6 @@ function GraphD3(_selector: HTMLDivElement, _options: any) {
128128
zoomOut: () => mainSvg.transition().call(zoom.scaleBy, ZOOM_PROPS.ZOOM_OUT),
129129
resetZoom: () => mainSvg.transition().call(zoom.scaleTo, ZOOM_PROPS.ZOOM_RESET),
130130
center: () => mainSvg.transition().call(zoom.translateTo, ...ZOOM_PROPS.CAMERA_CENTER(mainSvg.node().getBoundingClientRect().width, mainSvg.node().getBoundingClientRect().height)),
131-
panLeft: () => mainSvg.transition().call(zoom.translateBy, ZOOM_PROPS.CAMERA_LEFT, 0),
132-
panRight: () => mainSvg.transition().call(zoom.translateBy, ZOOM_PROPS.CAMERA_RIGHT, 0)
133131
}
134132

135133
svgRelationships = svg.append('g')
@@ -400,7 +398,7 @@ function GraphD3(_selector: HTMLDivElement, _options: any) {
400398
function appendTextToRelationship(r) {
401399
return r.append('text')
402400
.attr('class', 'text')
403-
.attr('fill', d => edgeColors(d.type).textColor)
401+
.attr('fill', d => edgeColors(d.type).color)
404402
.attr('font-size', '12px')
405403
.attr('pointer-events', 'none')
406404
.attr('text-anchor', 'middle')

redisinsight/ui/src/packages/redisgraph/src/main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const renderGraphTable = (props: Props) => {
8080
style: { ...style, backgroundColor: undefined }, // removing default background color from styles
8181
}),
8282
}}
83+
labelRenderer={key => key ? key : null}
8384
hideRoot
8485
data={d}
8586
/>

0 commit comments

Comments
 (0)