Skip to content

Commit 47e97b2

Browse files
committed
Display result view in graph
1 parent 040e1fa commit 47e97b2

11 files changed

+1174
-1573
lines changed

package-lock.json

Lines changed: 1034 additions & 1470 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/FairDOElasticSearch.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function FairDOElasticSearch({
7373
<SearchProvider config={elasticConfig}>
7474
<FairDOSearchProvider config={rawConfig}>
7575
<TooltipProvider>
76-
<GlobalModalProvider>
76+
<GlobalModalProvider resultView={actualResultView}>
7777
<WithSearch
7878
mapContextToProps={({ wasSearched, isLoading }: SearchContextState) => ({
7979
wasSearched,

src/components/GlobalModalProvider.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
"use client"
22

3-
import type { RelationNode } from "@/lib/RelationNode"
4-
import type { PropsWithChildren } from "react"
3+
import type { ComponentType, PropsWithChildren } from "react"
54
import { RelationsGraphModal } from "@/components/graph/RelationsGraphModal"
65
import { ReactFlowProvider } from "@xyflow/react"
76
import { useCallback, useState } from "react"
87
import { RFS_GlobalModalContext } from "./RFS_GlobalModalContext"
8+
import { ResultViewProps } from "@elastic/react-search-ui-views"
99

10-
export function GlobalModalProvider(props: PropsWithChildren) {
10+
export function GlobalModalProvider(props: PropsWithChildren<{ resultView: ComponentType<ResultViewProps> }>) {
1111
const [relationGraphState, setRelationGraphState] = useState<{
12-
source: RelationNode[]
13-
target: RelationNode[]
14-
base: RelationNode
12+
source: string[]
13+
target: string[]
14+
base: string
1515
isOpen: boolean
1616
}>({
1717
source: [],
1818
target: [],
19-
base: { id: "", label: "" },
19+
base: "",
2020
isOpen: false
2121
})
2222

23-
const openRelationGraph = useCallback((source: RelationNode[], base: RelationNode, target: RelationNode[]) => {
23+
const openRelationGraph = useCallback((source: string[], base: string, target: string[]) => {
2424
setRelationGraphState({
2525
source,
2626
base,
@@ -42,6 +42,7 @@ export function GlobalModalProvider(props: PropsWithChildren) {
4242
referencedBy={relationGraphState.source}
4343
references={relationGraphState.target}
4444
base={relationGraphState.base}
45+
resultView={props.resultView}
4546
/>
4647

4748
{props.children}

src/components/RFS_GlobalModalContext.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import type { RelationNode } from "@/lib/RelationNode"
21
import { createContext } from "react"
32

43
export const RFS_GlobalModalContext = createContext<{
5-
openRelationGraph: (source: RelationNode[], base: RelationNode, target: RelationNode[]) => void
4+
openRelationGraph: (source: string[], base: string, target: string[]) => void
65
}>({
76
openRelationGraph: (): void => {
87
throw "GlobalModalProvider not mounted"

src/components/graph/RelationsGraph.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,52 @@
1-
import type { RelationNode } from "@/lib/RelationNode"
2-
3-
import { buildGraphForReferences, getLayoutedElements } from "@/components/graph/helpers"
4-
import { PlainNode } from "@/components/graph/PlainNode"
5-
import { Background, BackgroundVariant, ReactFlow, useEdgesState, useNodesInitialized, useNodesState, useReactFlow } from "@xyflow/react"
6-
import { useCallback, useEffect, useMemo, useRef } from "react"
1+
import { buildGraphForReferences, getLayoutedElements, ResultPID } from "@/components/graph/helpers"
2+
import {
3+
Background,
4+
BackgroundVariant,
5+
NodeProps,
6+
ReactFlow,
7+
useEdgesState,
8+
useNodesInitialized,
9+
useNodesState,
10+
useReactFlow,
11+
useUpdateNodeInternals
12+
} from "@xyflow/react"
13+
import { ComponentType, useCallback, useEffect, useMemo, useRef } from "react"
714
import "@xyflow/react/dist/style.css"
8-
9-
const nodeTypes = {
10-
plain: PlainNode
11-
}
15+
import { useStore } from "zustand"
16+
import { resultCache } from "@/lib/ResultCache"
17+
import { ResultViewWrapper } from "@/components/graph/ResultViewWrapper"
18+
import { ResultViewProps } from "@elastic/react-search-ui-views"
1219

1320
/**
1421
* Renders an interactive graph for the specified RelationNodes.
1522
*/
16-
export function RelationsGraph(props: { base: RelationNode; referencedBy: RelationNode[]; references: RelationNode[] }) {
23+
export function RelationsGraph(props: { base: string; referencedBy: string[]; references: string[]; resultView: ComponentType<ResultViewProps> }) {
24+
const getFromCache = useStore(resultCache, (s) => s.get)
25+
1726
const { initialEdges, initialNodes } = useMemo(() => {
18-
return buildGraphForReferences(props.base, props.referencedBy, props.references)
19-
}, [props.base, props.referencedBy, props.references])
27+
const base: ResultPID = { pid: props.base, result: getFromCache(props.base) }
28+
const referencedBy = props.referencedBy.map((pid) => ({ pid, result: getFromCache(pid) }))
29+
const references = props.references.map((pid) => ({ pid, result: getFromCache(pid) }))
30+
31+
return buildGraphForReferences(base, referencedBy, references)
32+
}, [getFromCache, props.base, props.referencedBy, props.references])
33+
34+
const nodeTypes = useMemo(() => {
35+
return {
36+
plain: (nodeProps: NodeProps) => <ResultViewWrapper {...nodeProps} resultView={props.resultView} />
37+
}
38+
}, [props.resultView])
2039

2140
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
2241
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
2342
const { fitView } = useReactFlow()
2443
const nodesInitialized = useNodesInitialized()
44+
const updateNodeInternals = useUpdateNodeInternals()
45+
46+
useEffect(() => {
47+
setNodes(initialNodes)
48+
setEdges(initialEdges)
49+
}, [initialEdges, initialNodes, setEdges, setNodes])
2550

2651
const onLayout = useCallback(() => {
2752
const layouted = getLayoutedElements(nodes, edges)
@@ -30,9 +55,12 @@ export function RelationsGraph(props: { base: RelationNode; referencedBy: Relati
3055
setEdges([...layouted.edges])
3156

3257
window.requestAnimationFrame(() => {
33-
fitView()
58+
setTimeout(() => {
59+
fitView({ nodes: [{ id: props.base }], duration: 200, padding: 1 })
60+
updateNodeInternals(nodes.map((n) => n.id))
61+
}, 100)
3462
})
35-
}, [nodes, edges, setNodes, setEdges, fitView])
63+
}, [nodes, edges, setNodes, setEdges, fitView, props.base, updateNodeInternals])
3664

3765
const onLayoutDebounced = useRef(onLayout)
3866
useEffect(() => {

src/components/graph/RelationsGraphModal.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
import type { RelationNode } from "@/lib/RelationNode"
21
import { FairDOSearchContext } from "@/components/FairDOSearchContext"
32
import { RelationsGraph } from "@/components/graph/RelationsGraph"
43
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"
54
import * as VisuallyHidden from "@radix-ui/react-visually-hidden"
6-
import { useCallback, useContext } from "react"
5+
import { ComponentType, useCallback, useContext } from "react"
6+
import { ResultViewProps } from "@elastic/react-search-ui-views"
77

88
export function RelationsGraphModal({
99
isOpen,
1010
onOpenChange,
1111
referencedBy,
1212
references,
13-
base
13+
base,
14+
resultView
1415
}: {
1516
isOpen: boolean
1617
onOpenChange: (val: boolean) => void
17-
referencedBy: RelationNode[]
18-
references: RelationNode[]
19-
base: RelationNode
18+
referencedBy: string[]
19+
references: string[]
20+
base: string
21+
resultView: ComponentType<ResultViewProps>
2022
}) {
2123
const searchContext = useContext(FairDOSearchContext)
2224

@@ -30,11 +32,9 @@ export function RelationsGraphModal({
3032

3133
return (
3234
<Dialog open={isOpen} onOpenChange={onOpenChange}>
33-
<DialogContent className="rfs-h-max rfs-max-h-[min(100vh,800px)] rfs-min-h-[500px] rfs-min-w-[500px] !rfs-max-w-[min(calc(100vw-40px),1500px)] rfs-p-0">
35+
<DialogContent className="rfs-h-max rfs-max-h-[min(100vh,800px)] rfs-min-h-[500px] rfs-min-w-[500px] !rfs-max-w-[min(calc(100vw-40px),1500px)] !rfs-p-0">
3436
<VisuallyHidden.Root>
35-
<DialogTitle>
36-
Relationship graph between {referencedBy.map((s) => s.label).join(", ")} and {references.map((s) => s.label).join(", ")}
37-
</DialogTitle>
37+
<DialogTitle>Relationship graph for PID {base}</DialogTitle>
3838
</VisuallyHidden.Root>
3939

4040
<FairDOSearchContext.Provider
@@ -44,7 +44,7 @@ export function RelationsGraphModal({
4444
searchForBackground: searchContext.searchForBackground
4545
}}
4646
>
47-
<RelationsGraph referencedBy={referencedBy} references={references} base={base} />
47+
<RelationsGraph referencedBy={referencedBy} references={references} base={base} resultView={resultView} />
4848
</FairDOSearchContext.Provider>
4949
</DialogContent>
5050
</Dialog>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Handle, NodeProps, Position } from "@xyflow/react"
2+
import { ComponentType, useMemo } from "react"
3+
import { ResultViewProps } from "@elastic/react-search-ui-views"
4+
5+
export function ResultViewWrapper({ resultView: ResultView, data, id }: NodeProps & { resultView: ComponentType<ResultViewProps> }) {
6+
const dataEmpty = useMemo(() => {
7+
return Object.keys(data).length === 0
8+
}, [data])
9+
10+
return (
11+
<div className="rfs-w-[800px] -rfs-m-2">
12+
<Handle type="target" position={Position.Left} />
13+
{dataEmpty ? (
14+
<div className="rfs-m-2 rfs-p-4 rfs-rounded-lg rfs-bg-background rfs-border">{id}</div>
15+
) : (
16+
<ResultView result={data} onClickLink={() => {}} />
17+
)}
18+
<Handle type="source" position={Position.Right} />
19+
</div>
20+
)
21+
}

src/components/graph/helpers.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,55 @@
1-
import type { RelationNode } from "@/lib/RelationNode"
21
import { Edge, Node } from "@xyflow/react"
32
import Dagre from "@dagrejs/dagre"
43

5-
export function buildGraphForReferences(base: RelationNode, parents: RelationNode[], _children: RelationNode[]) {
6-
const children = _children.filter((pid) => !parents.find((e) => e.id === pid.id))
4+
export type ResultPID = {
5+
pid: string
6+
result?: Record<string, unknown>
7+
}
8+
9+
export function buildGraphForReferences(base: ResultPID, parents: ResultPID[], _children: ResultPID[]) {
10+
const children = _children.filter((pid) => !parents.find((e) => e.pid === pid.pid))
711
const yStartParents = -((parents.length - 1) * 100) / 2
812
const yStartChildren = -((children.length - 1) * 100) / 2
913
const nodes: { id: string; type: string; position: { x: number; y: number }; data: Record<string, unknown> }[] = [
1014
{
11-
id: base.id,
15+
id: base.pid,
1216
type: "plain",
1317
position: { x: 0, y: 0 },
14-
data: { ...base }
18+
data: { ...base.result }
1519
}
1620
]
1721
const edges: { id: string; source: string; target: string }[] = []
1822

1923
for (let i = 0; i < parents.length; i++) {
2024
nodes.push({
21-
id: parents[i].id,
25+
id: parents[i].pid,
2226
type: "plain",
2327
position: { x: -1000, y: yStartParents + i * 100 },
24-
data: { ...parents[i] }
28+
data: { ...parents[i].result }
2529
})
2630

2731
edges.push({
28-
id: `e-${parents[i].id}-base`,
29-
source: parents[i].id,
30-
target: base.id
32+
id: `e-${parents[i].pid}-base`,
33+
source: parents[i].pid,
34+
target: base.pid
3135
})
3236
}
3337

3438
for (let i = 0; i < children.length; i++) {
3539
nodes.push({
36-
id: children[i].id,
40+
id: children[i].pid,
3741
type: "plain",
3842
position: { x: 1000, y: yStartChildren + i * 100 },
39-
data: { ...children[i] }
43+
data: { ...children[i].result }
4044
})
4145

4246
edges.push({
43-
id: `e-base-${children[i].id}`,
44-
source: base.id,
45-
target: children[i].id
47+
id: `e-base-${children[i].pid}`,
48+
source: base.pid,
49+
target: children[i].pid
4650
})
4751
}
4852

49-
console.log(nodes, edges)
50-
5153
return { initialNodes: nodes, initialEdges: edges }
5254
}
5355

0 commit comments

Comments
 (0)