Skip to content

Commit

Permalink
Merge pull request #262 from RobokopU24/feature/node-information
Browse files Browse the repository at this point in the history
Feature/node information
  • Loading branch information
Woozl authored Jun 30, 2023
2 parents 2773e3e + 412cda7 commit 666368e
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 48 deletions.
73 changes: 73 additions & 0 deletions src/components/Popover.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { Paper, styled, Popover as MuiPopover } from '@material-ui/core';

const PopoverPaper = styled(Paper)(({ theme }) => ({
filter: 'drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.3))',

position: 'relative',
'&::before': {
content: '""',
display: 'block',
position: 'absolute',
bottom: '-15px',
left: '0',
right: '0',
margin: '0 auto',
clipPath: "path('M 0 0 L 20 0 L 10 15 Z')",
width: '20px',
height: '15px',
backgroundColor: theme.palette.background.paper,
},
}));

const PopoverPaperBelow = styled(PopoverPaper)(() => ({
'&::before': {
top: '-15px',
bottom: 'unset',
clipPath: "path('M 0 15 L 20 15 L 10 0 Z')",
},
}));

const Popover = ({
children, open, onClose, anchorPosition, above,
}) => {
let PositionedPaper = PopoverPaperBelow;
if (above) PositionedPaper = PopoverPaper;

let anchorOriginVertical = 'bottom';
if (above) anchorOriginVertical = 'top';

let transformOriginVertical = 'top';
if (above) transformOriginVertical = 'bottom';

let marginBlockStart = '28px';
if (above) marginBlockStart = '-28px';

return (
<MuiPopover
anchorReference="anchorPosition"
anchorPosition={anchorPosition}
open={open}
onClose={onClose}
anchorOrigin={{
vertical: anchorOriginVertical,
horizontal: 'center',
}}
transformOrigin={{
vertical: transformOriginVertical,
horizontal: 'center',
}}
PaperProps={{
style: {
boxShadow: 'none',
overflow: 'visible',
marginBlockStart,
},
}}
>
<PositionedPaper>{children}</PositionedPaper>
</MuiPopover>
);
};

export default Popover;
48 changes: 47 additions & 1 deletion src/pages/answer/kgBubble/KgBubble.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import Loading from '~/components/loading/Loading';
import useDebounce from '~/stores/useDebounce';

import './kgBubble.css';
import Popover from '~/components/Popover';
import NodeAttributesTable from './NodeAttributesTable';

const nodePadding = 2;
const defaultTrimNum = 25;
Expand All @@ -36,6 +38,9 @@ export default function KgBubble({
const [drawing, setDrawing] = useState(false);
const [numTrimmedNodes, setNumTrimmedNodes] = useState(Math.min(nodes.length, defaultTrimNum));
const debouncedTrimmedNodes = useDebounce(numTrimmedNodes, 500);
const [popoverPosition, setPopoverPosition] = useState({ x: 0, y: 0 });
const [popoverOpen, setPopoverOpen] = useState(false);
const [popoverData, setPopoverData] = useState({});

const trimmedNodes = useMemo(() => nodes.slice(0, debouncedTrimmedNodes), [debouncedTrimmedNodes, nodes]);

Expand All @@ -57,6 +62,22 @@ export default function KgBubble({
.map(([category, { color }]) => [category, color]);
}, [trimmedNodes, colorMap]);

const handleClickNode = (data) => {
const { top, left } = svgRef.current.getBoundingClientRect();
setPopoverPosition({
x: left + data.x,
y: top + data.y,
});

setPopoverData({
name: data.name,
id: data.id,
categories: data.categories,
count: data.count,
});
setPopoverOpen(true);
};

/**
* Initialize the svg size
*/
Expand Down Expand Up @@ -112,7 +133,24 @@ export default function KgBubble({
.attr('r', (d) => getNodeRadius(d.count))
.attr('fill', (d) => colorMap(d.categories)[1])
.call((nCircle) => nCircle.append('title')
.text((d) => d.name)))
.text((d) => d.name))
.style('transition', 'stroke-width 200ms ease-in-out, stroke 200ms ease-in-out, filter 200ms ease-in-out')
.style('cursor', 'pointer')
.on('mouseover', function () {
d3.select(this)
.attr('stroke', '#239cff')
.style('filter', 'brightness(1.1)')
.attr('stroke-width', 3);
})
.on('mouseout', function () {
d3.select(this)
.attr('stroke', 'none')
.style('filter', 'initial')
.attr('stroke-width', 0);
})
.on('click', function () {
handleClickNode(d3.select(this).datum());
}))
.call((n) => n.append('text')
.attr('class', 'nodeLabel')
.style('pointer-events', 'none')
Expand Down Expand Up @@ -202,6 +240,14 @@ export default function KgBubble({
</Paper>
</div>
)}

<Popover
open={popoverOpen}
onClose={() => setPopoverOpen(false)}
anchorPosition={{ top: popoverPosition.y, left: popoverPosition.x }}
>
<NodeAttributesTable nodeData={popoverData} />
</Popover>
</>
);
}
81 changes: 81 additions & 0 deletions src/pages/answer/kgBubble/NodeAttributesTable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';
import {
Box,
Table,
TableBody,
TableCell,
TableRow,
styled,
} from '@material-ui/core';

const StyledTableBody = styled(TableBody)(() => ({
'& .MuiTableRow-root:last-of-type .MuiTableCell-root': {
borderBottom: 'none',
},
}));

const ValueCell = ({ value }) => (
<TableCell>
<ul style={{ padding: 0, margin: 0, listStyleType: 'none' }}>
{Array.isArray(value) ? (
value.map((valueItem, valueItemIndex) => (
<li key={valueItemIndex}>{valueItem}</li>
))
) : (
<li>{value}</li>
)}
</ul>
</TableCell>
);

const NodeAttributesTable = ({ nodeData }) => {
const {
name, id, categories, count,
} = nodeData;

return (
<Box style={{ maxHeight: 500, overflow: 'auto' }}>
<Table size="small" aria-label="node attributes table">
<StyledTableBody>
{Boolean(name) && (
<TableRow style={{ verticalAlign: 'top' }}>
<TableCell>
Name
</TableCell>
<ValueCell value={name} />
</TableRow>
)}

{Boolean(id) && (
<TableRow style={{ verticalAlign: 'top' }}>
<TableCell>
ID
</TableCell>
<ValueCell value={id} />
</TableRow>
)}

{Boolean(categories) && (
<TableRow style={{ verticalAlign: 'top' }}>
<TableCell>
Categories
</TableCell>
<ValueCell value={categories} />
</TableRow>
)}

{Boolean(count) && (
<TableRow style={{ verticalAlign: 'top' }}>
<TableCell>
Count
</TableCell>
<ValueCell value={count} />
</TableRow>
)}
</StyledTableBody>
</Table>
</Box>
);
};

export default NodeAttributesTable;
105 changes: 58 additions & 47 deletions src/pages/answer/resultsTable/ResultExplorer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Paper from '@material-ui/core/Paper';
import Box from '@material-ui/core/Box';
import Slider from '@material-ui/core/Slider';

import { Popover, styled } from '@material-ui/core';
import BiolinkContext from '~/context/biolink';
import dragUtils from '~/utils/d3/drag';
import graphUtils from '~/utils/d3/graph';
Expand All @@ -16,28 +15,11 @@ import stringUtils from '~/utils/strings';
import useDebounce from '~/stores/useDebounce';
import ResultMetaData from './ResultMetaData';
import AttributesTable from './AttributesTable';
import Popover from '~/components/Popover';
import NodeAttributesTable from '../kgBubble/NodeAttributesTable';

const nodeRadius = 40;

const PopoverPaper = styled(Paper)(({ theme }) => ({
filter: 'drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.3))',

position: 'relative',
'&::before': {
content: '""',
display: 'block',
position: 'absolute',
bottom: '-15px',
left: '0',
right: '0',
margin: '0 auto',
clipPath: "path('M 0 0 L 20 0 L 10 15 Z')",
width: '20px',
height: '15px',
backgroundColor: theme.palette.background.paper,
},
}));

/**
* Selected result graph
* @param {object} answerStore - answer store hook
Expand All @@ -53,10 +35,11 @@ export default function ResultExplorer({ answerStore }) {
const { colorMap } = useContext(BiolinkContext);
const [numTrimmedNodes, setNumTrimmedNodes] = useState(answerStore.numQgNodes);
const debouncedTrimmedNodes = useDebounce(numTrimmedNodes, 500);
const [mouseX, setMouseX] = useState(0);
const [mouseY, setMouseY] = useState(0);
const [attributesPopoverOpen, setAttributesPopoverOpen] = useState(false);
const [currentEdgeAttributes, setCurrentEdgeAttributes] = useState({});
const [popoverPosition, setPopoverPosition] = useState({ x: 0, y: 0 });
// const [attributesPopoverOpen, setAttributesPopoverOpen] = useState(false);
// const [currentEdgeAttributes, setCurrentEdgeAttributes] = useState({});
const [popoverOpen, setPopoverOpen] = useState(false);
const [popoverData, setPopoverData] = useState({});

/**
* Initialize svg object
Expand Down Expand Up @@ -197,7 +180,24 @@ export default function ResultExplorer({ answerStore }) {
.attr('r', nodeRadius)
.attr('fill', (d) => colorMap(d.categories)[1])
.call((nCircle) => nCircle.append('title')
.text((d) => d.name)))
.text((d) => d.name))
.style('transition', 'stroke-width 200ms ease-in-out, stroke 200ms ease-in-out, filter 200ms ease-in-out')
.style('cursor', 'pointer')
.on('mouseover', function () {
d3.select(this)
.attr('stroke', '#239cff')
.style('filter', 'brightness(1.1)')
.attr('stroke-width', 3);
})
.on('mouseout', function () {
d3.select(this)
.attr('stroke', 'none')
.style('filter', 'initial')
.attr('stroke-width', 0);
})
.on('click', function () {
handleClickNode(d3.select(this).datum());
}))
.call((n) => n.append('text')
.attr('class', 'result_node_label')
.style('pointer-events', 'none')
Expand Down Expand Up @@ -253,8 +253,8 @@ export default function ResultExplorer({ answerStore }) {
.style('cursor', 'pointer')
.on('mouseover', function () {
d3.select(this)
.attr('fill', '#008cff')
.attr('stroke', '#008cff');
.attr('fill', '#239cff')
.attr('stroke', '#239cff');
})
.on('mouseout', function () {
d3.select(this)
Expand Down Expand Up @@ -314,11 +314,24 @@ export default function ResultExplorer({ answerStore }) {
}

const handleClickEdge = (event, attributes) => {
setMouseX(event.clientX);
setMouseY(event.clientY);
setPopoverPosition({ x: event.clientX, y: event.clientY });

setPopoverData(attributes);
setPopoverOpen('edge');
};

setCurrentEdgeAttributes(attributes);
setAttributesPopoverOpen(true);
const handleClickNode = (data) => {
const { top, left } = svgRef.current.getBoundingClientRect();
setPopoverPosition({
x: left + data.x,
y: top + data.y,
});
setPopoverData({
name: data.name,
id: data.id,
categories: data.categories,
});
setPopoverOpen('node');
};

useEffect(() => {
Expand Down Expand Up @@ -360,23 +373,21 @@ export default function ResultExplorer({ answerStore }) {
)}

<Popover
anchorReference="anchorPosition"
anchorPosition={{ top: mouseY, left: mouseX }}
open={attributesPopoverOpen}
onClose={() => setAttributesPopoverOpen(false)}
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
PaperProps={{ style: { boxShadow: 'none', overflow: 'visible', marginBlock: '-28px' } }}
open={popoverOpen === 'edge'}
onClose={() => setPopoverOpen(null)}
anchorPosition={{ top: popoverPosition.y, left: popoverPosition.x }}
above
>
<AttributesTable attributes={popoverData} />
</Popover>

<Popover
open={popoverOpen === 'node'}
onClose={() => setPopoverOpen(null)}
anchorPosition={{ top: popoverPosition.y, left: popoverPosition.x }}
above
>
<PopoverPaper>
<AttributesTable attributes={currentEdgeAttributes} />
</PopoverPaper>
<NodeAttributesTable nodeData={popoverData} />
</Popover>
</Paper>
);
Expand Down

0 comments on commit 666368e

Please sign in to comment.