diff --git a/package-lock.json b/package-lock.json index 2f9eed3b..210572df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8697,6 +8697,17 @@ "node": ">=8" } }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", diff --git a/src/pages/answer/queryGraph/QueryGraph.jsx b/src/pages/answer/queryGraph/QueryGraph.jsx index f7b9c7f8..b73b4913 100644 --- a/src/pages/answer/queryGraph/QueryGraph.jsx +++ b/src/pages/answer/queryGraph/QueryGraph.jsx @@ -24,8 +24,9 @@ const edgeLength = 225; */ export default function QueryGraph({ query_graph }) { const svgRef = useRef(); - const { colorMap } = useContext(BiolinkContext); + const { colorMap, predicates } = useContext(BiolinkContext); const [drawing, setDrawing] = useState(false); + const symmetricPredicates = predicates.filter((predicate) => predicate.symmetric).map((predicate) => predicate.predicate); /** * Initialize the svg size @@ -141,7 +142,7 @@ export default function QueryGraph({ query_graph }) { .attr('fill', 'none') .attr('stroke-width', (d) => d.strokeWidth) .attr('class', 'edge') - .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d) ? 'url(#arrow)' : ''))) + .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d, symmetricPredicates) ? 'url(#arrow)' : ''))) .call((e) => e.append('path') .attr('stroke', 'transparent') .attr('fill', 'none') diff --git a/src/pages/answer/resultsTable/ResultExplorer.jsx b/src/pages/answer/resultsTable/ResultExplorer.jsx index f4ceb3c1..6b65794d 100644 --- a/src/pages/answer/resultsTable/ResultExplorer.jsx +++ b/src/pages/answer/resultsTable/ResultExplorer.jsx @@ -32,7 +32,7 @@ export default function ResultExplorer({ answerStore }) { const node = useRef({}); const edge = useRef({}); const simulation = useRef(); - const { colorMap } = useContext(BiolinkContext); + const { colorMap, predicates } = useContext(BiolinkContext); const [numTrimmedNodes, setNumTrimmedNodes] = useState(answerStore.numQgNodes); const debouncedTrimmedNodes = useDebounce(numTrimmedNodes, 500); const [popoverPosition, setPopoverPosition] = useState({ x: 0, y: 0 }); @@ -40,6 +40,7 @@ export default function ResultExplorer({ answerStore }) { // const [currentEdgeAttributes, setCurrentEdgeAttributes] = useState({}); const [popoverOpen, setPopoverOpen] = useState(false); const [popoverData, setPopoverData] = useState({}); + const symmetricPredicates = predicates.filter((predicate) => predicate.symmetric).map((predicate) => predicate.predicate); /** * Initialize svg object @@ -247,7 +248,7 @@ export default function ResultExplorer({ answerStore }) { .attr('fill', 'none') .attr('stroke-width', (d) => d.strokeWidth) .attr('class', 'result_edge') - .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d) ? 'url(#arrow)' : ''))) + .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d, symmetricPredicates) ? 'url(#arrow)' : ''))) .attr('fill', 'black') .attr('stroke', '#999') .style('transition', 'stroke 100ms ease-in-out, fill 100ms ease-in-out') diff --git a/src/pages/queryBuilder/graphEditor/QueryGraph.jsx b/src/pages/queryBuilder/graphEditor/QueryGraph.jsx index 0e0f7d56..1fbdaa93 100644 --- a/src/pages/queryBuilder/graphEditor/QueryGraph.jsx +++ b/src/pages/queryBuilder/graphEditor/QueryGraph.jsx @@ -26,7 +26,8 @@ export default function QueryGraph({ height, width, clickState, updateClickState, }) { - const { colorMap } = useContext(BiolinkContext); + const { colorMap, predicates } = useContext(BiolinkContext); + const symmetricPredicates = predicates.filter((predicate) => predicate.symmetric).map((predicate) => predicate.predicate); const queryBuilder = useContext(QueryBuilderContext); const { query_graph } = queryBuilder; const { nodes, edges } = useMemo(() => queryGraphUtils.getNodeAndEdgeListsForDisplay(query_graph), [queryBuilder.state]); @@ -253,6 +254,10 @@ export default function QueryGraph({ // edge ends need the x and y of their attached nodes // must come after simulation const edgesWithCurves = edgeUtils.addEdgeCurveProperties(newEdges); + // add symmetricPredicates to each edgesWithCurves + edgesWithCurves.forEach((e) => { + e.symmetric = symmetricPredicates; + }); edge.current = edge.current.data(edgesWithCurves, (d) => d.id) .join( diff --git a/src/stores/useBiolinkModel.js b/src/stores/useBiolinkModel.js index 704f0002..8dd75c9f 100644 --- a/src/stores/useBiolinkModel.js +++ b/src/stores/useBiolinkModel.js @@ -61,6 +61,7 @@ export default function useBiolinkModel() { predicate: strings.edgeFromBiolink(identifier), domain: strings.nodeFromBiolink(predicate.domain), range: strings.nodeFromBiolink(predicate.range), + symmetric: predicate.symmetric || false, })); } diff --git a/src/utils/d3/edges.js b/src/utils/d3/edges.js index 96194399..ca987d4b 100644 --- a/src/utils/d3/edges.js +++ b/src/utils/d3/edges.js @@ -130,7 +130,7 @@ function enter(edge) { .attr('fill', 'none') .attr('stroke-width', (d) => d.strokeWidth) .attr('class', 'edgePath') - .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d) ? 'url(#arrow)' : ''))) + .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d, d.symmetric) ? 'url(#arrow)' : ''))) // wider clickable line .call((e) => e.append('path') .attr('stroke', 'transparent') @@ -232,7 +232,7 @@ function update(edge) { .text((d) => (d.predicates ? d.predicates.map((p) => strings.displayPredicate(p)).join(', ') : ''))) .call((e) => e.select('.edgePath') // .attr('stroke-width', (d) => d.strokeWidth) - .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d) ? 'url(#arrow)' : ''))) + .attr('marker-end', (d) => (graphUtils.shouldShowArrow(d, d.symmetric) ? 'url(#arrow)' : ''))) .call((e) => e.select('text') .select('textPath') .text((d) => (d.predicates ? d.predicates.map((p) => strings.displayPredicate(p)).join(', ') : ''))); diff --git a/src/utils/d3/graph.js b/src/utils/d3/graph.js index b394fbd7..beb62257 100644 --- a/src/utils/d3/graph.js +++ b/src/utils/d3/graph.js @@ -215,8 +215,8 @@ function isInside(x, y, cx, cy, r) { * @param {obj} edge edge object * @returns {str} url(#arrow) or empty string */ -function shouldShowArrow(edge) { - return (edge.predicates && edge.predicates.findIndex((p) => p !== 'biolink:related_to') > -1) || (edge.predicate && edge.predicate !== 'biolink:related_to'); +function shouldShowArrow(edge, symmetricPredicates = ['biolink:related_to']) { + return (edge.predicates && edge.predicates.findIndex((p) => !symmetricPredicates.includes(p)) > -1) || (edge.predicate && !symmetricPredicates.includes(edge.predicate)); } /**