2021-04-03 11:13:25 -07:00
|
|
|
import React from 'react';
|
|
|
|
import { Group } from '@vx/group';
|
2021-04-05 19:23:07 -07:00
|
|
|
import { LinkHorizontal } from '@vx/shape';
|
2021-04-03 11:13:25 -07:00
|
|
|
import styled from 'styled-components';
|
|
|
|
|
|
|
|
import { NodeData, Direction } from './types';
|
|
|
|
|
|
|
|
function truncate(input, length) {
|
|
|
|
if (!input) return '';
|
|
|
|
if (input.length > length) {
|
|
|
|
return `${input.substring(0, length)}...`;
|
|
|
|
}
|
|
|
|
return input;
|
|
|
|
}
|
|
|
|
|
2021-04-09 01:24:33 +08:00
|
|
|
export const width = 212;
|
2021-04-05 12:15:05 -07:00
|
|
|
export const height = 80;
|
2021-04-09 01:24:33 +08:00
|
|
|
const iconWidth = 42;
|
|
|
|
const iconHeight = 42;
|
|
|
|
const iconX = -width / 2 + 8;
|
|
|
|
const iconY = -iconHeight / 2;
|
2021-04-03 11:13:25 -07:00
|
|
|
const centerX = -width / 2;
|
|
|
|
const centerY = -height / 2;
|
2021-04-09 01:24:33 +08:00
|
|
|
const textX = iconX + iconWidth + 8;
|
2021-04-03 11:13:25 -07:00
|
|
|
|
|
|
|
const PointerGroup = styled(Group)`
|
|
|
|
cursor: pointer;
|
|
|
|
`;
|
|
|
|
|
|
|
|
export default function LineageEntityNode({
|
|
|
|
node,
|
|
|
|
isSelected,
|
|
|
|
isHovered,
|
|
|
|
onEntityClick,
|
|
|
|
onHover,
|
|
|
|
onExpandClick,
|
|
|
|
direction,
|
|
|
|
isCenterNode,
|
2021-04-05 19:23:07 -07:00
|
|
|
nodesToRenderByUrn,
|
2021-04-03 11:13:25 -07:00
|
|
|
}: {
|
|
|
|
node: { x: number; y: number; data: Omit<NodeData, 'children'> };
|
|
|
|
isSelected: boolean;
|
|
|
|
isHovered: boolean;
|
|
|
|
isCenterNode: boolean;
|
|
|
|
onEntityClick: (EntitySelectParams) => void;
|
|
|
|
onHover: (EntitySelectParams) => void;
|
|
|
|
onExpandClick: (LineageExpandParams) => void;
|
|
|
|
direction: Direction;
|
2021-04-05 19:23:07 -07:00
|
|
|
nodesToRenderByUrn: { [key: string]: { x: number; y: number; data: Omit<NodeData, 'children'> }[] };
|
2021-04-03 11:13:25 -07:00
|
|
|
}) {
|
2021-04-05 19:23:07 -07:00
|
|
|
const unexploredHiddenChildren =
|
|
|
|
node?.data?.countercurrentChildrenUrns?.filter((urn) => !(urn in nodesToRenderByUrn))?.length || 0;
|
|
|
|
|
2021-04-03 11:13:25 -07:00
|
|
|
return (
|
|
|
|
<PointerGroup data-testid={`node-${node.data.urn}-${direction}`} top={node.x} left={node.y}>
|
2021-04-09 01:24:33 +08:00
|
|
|
{unexploredHiddenChildren ? (
|
2021-04-05 19:23:07 -07:00
|
|
|
<Group>
|
|
|
|
{[...Array(unexploredHiddenChildren)].map((_, index) => {
|
|
|
|
const link = {
|
|
|
|
source: {
|
|
|
|
x: 0,
|
|
|
|
y: direction === Direction.Upstream ? 70 : -70,
|
|
|
|
},
|
|
|
|
target: {
|
|
|
|
x: (0.5 / (index + 1)) * 80 * (index % 2 === 0 ? 1 : -1),
|
|
|
|
y: direction === Direction.Upstream ? 150 : -150,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
return (
|
|
|
|
<LinkHorizontal
|
2021-04-16 02:47:32 +08:00
|
|
|
key={`node-${_}-${direction}`}
|
2021-04-05 19:23:07 -07:00
|
|
|
data={link}
|
|
|
|
stroke={`url(#gradient-${direction})`}
|
|
|
|
strokeWidth="1"
|
|
|
|
fill="none"
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</Group>
|
2021-04-09 01:24:33 +08:00
|
|
|
) : null}
|
|
|
|
{node.data.unexploredChildren ? (
|
2021-04-03 11:13:25 -07:00
|
|
|
<Group
|
|
|
|
onClick={() => {
|
2021-04-09 11:55:25 -07:00
|
|
|
onExpandClick({ urn: node.data.urn, type: node.data.type, direction });
|
2021-04-03 11:13:25 -07:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<circle
|
|
|
|
fill="white"
|
|
|
|
cy={centerY + height / 2}
|
|
|
|
cx={direction === Direction.Upstream ? centerX - 10 : centerX + width + 10}
|
|
|
|
r="20"
|
|
|
|
/>
|
|
|
|
<g
|
|
|
|
fill="grey"
|
|
|
|
transform={`translate(${
|
2021-04-09 01:24:33 +08:00
|
|
|
direction === Direction.Upstream ? centerX - 32 : width / 2 - 10
|
2021-04-03 11:13:25 -07:00
|
|
|
} -21.5) scale(0.04 0.04)`}
|
|
|
|
>
|
|
|
|
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm192 472c0 4.4-3.6 8-8 8H544v152c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V544H328c-4.4 0-8-3.6-8-8v-48c0-4.4 3.6-8 8-8h152V328c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v152h152c4.4 0 8 3.6 8 8v48z" />
|
|
|
|
</g>
|
|
|
|
</Group>
|
2021-04-09 01:24:33 +08:00
|
|
|
) : null}
|
2021-04-03 11:13:25 -07:00
|
|
|
<Group
|
|
|
|
onClick={() => {
|
2021-04-09 11:55:25 -07:00
|
|
|
onEntityClick({ urn: node.data.urn, type: node.data.type });
|
2021-04-03 11:13:25 -07:00
|
|
|
}}
|
|
|
|
onMouseOver={() => {
|
2021-04-09 11:55:25 -07:00
|
|
|
onHover({ urn: node.data.urn, type: node.data.type });
|
2021-04-03 11:13:25 -07:00
|
|
|
}}
|
|
|
|
onMouseOut={() => {
|
|
|
|
onHover(undefined);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<rect
|
|
|
|
height={height}
|
|
|
|
width={width}
|
|
|
|
y={centerY}
|
|
|
|
x={centerX}
|
|
|
|
fill="white"
|
|
|
|
// eslint-disable-next-line no-nested-ternary
|
|
|
|
stroke={isSelected ? 'blue' : isHovered ? 'lightblue' : 'black'}
|
|
|
|
strokeWidth={isCenterNode ? 4 : 2}
|
|
|
|
strokeOpacity={1}
|
|
|
|
rx={10}
|
|
|
|
/>
|
2021-04-09 01:24:33 +08:00
|
|
|
{node.data.icon ? (
|
|
|
|
<image href={node.data.icon} height={iconHeight} width={iconWidth} x={iconX} y={iconY} />
|
|
|
|
) : null}
|
|
|
|
<text dy=".33em" x={textX} fontSize={16} fontFamily="Arial" textAnchor="start" fill="black">
|
2021-04-03 11:13:25 -07:00
|
|
|
{truncate(node.data.name?.split('.').slice(-1)[0], 16)}
|
|
|
|
</text>
|
2021-04-09 01:24:33 +08:00
|
|
|
{unexploredHiddenChildren && isHovered ? (
|
|
|
|
<text
|
|
|
|
dy=".33em"
|
|
|
|
dx={textX}
|
|
|
|
fontSize={16}
|
|
|
|
fontFamily="Arial"
|
|
|
|
textAnchor="middle"
|
|
|
|
fill="black"
|
|
|
|
y={centerY - 20}
|
|
|
|
>
|
2021-04-05 19:23:07 -07:00
|
|
|
{unexploredHiddenChildren} hidden {direction === Direction.Upstream ? 'downstream' : 'upstream'}{' '}
|
|
|
|
{unexploredHiddenChildren > 1 ? 'dependencies' : 'dependency'}
|
|
|
|
</text>
|
2021-04-09 01:24:33 +08:00
|
|
|
) : null}
|
2021-04-03 11:13:25 -07:00
|
|
|
</Group>
|
|
|
|
</PointerGroup>
|
|
|
|
);
|
|
|
|
}
|