2021-10-22 15:46:46 -07:00
|
|
|
import React, { SVGProps, useEffect, useMemo, useState } from 'react';
|
2021-04-06 18:21:33 -07:00
|
|
|
import { PlusOutlined, MinusOutlined } from '@ant-design/icons';
|
|
|
|
import styled from 'styled-components';
|
2021-08-31 22:00:56 -07:00
|
|
|
import { Button } from 'antd';
|
2021-04-06 18:21:33 -07:00
|
|
|
import { ProvidedZoom, TransformMatrix } from '@vx/zoom/lib/types';
|
|
|
|
|
|
|
|
import LineageTree from './LineageTree';
|
|
|
|
import constructTree from './utils/constructTree';
|
2021-04-09 11:55:25 -07:00
|
|
|
import { Direction, EntityAndType, EntitySelectParams, FetchedEntity } from './types';
|
|
|
|
import { useEntityRegistry } from '../useEntityRegistry';
|
2021-04-06 18:21:33 -07:00
|
|
|
|
2021-08-31 22:00:56 -07:00
|
|
|
const ZoomContainer = styled.div`
|
|
|
|
position: relative;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const ZoomControls = styled.div`
|
2021-04-06 18:21:33 -07:00
|
|
|
position: absolute;
|
2021-08-31 22:00:56 -07:00
|
|
|
top: 20px;
|
2021-04-06 18:21:33 -07:00
|
|
|
right: 20px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const ZoomButton = styled(Button)`
|
|
|
|
display: block;
|
|
|
|
margin-bottom: 12px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const RootSvg = styled.svg<{ isDragging: boolean } & SVGProps<SVGSVGElement>>`
|
|
|
|
cursor: ${(props) => (props.isDragging ? 'grabbing' : 'grab')};
|
2021-10-29 11:20:02 -07:00
|
|
|
@keyframes spin {
|
|
|
|
0% {
|
|
|
|
transform: rotate(0deg);
|
|
|
|
}
|
|
|
|
100% {
|
|
|
|
transform: rotate(359deg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.lineageExpandLoading {
|
|
|
|
transform-box: fill-box;
|
|
|
|
transform-origin: 50% 50%;
|
|
|
|
animation: spin 2s linear infinite;
|
|
|
|
}
|
2021-04-06 18:21:33 -07:00
|
|
|
`;
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
margin: { top: number; right: number; bottom: number; left: number };
|
2021-04-09 11:55:25 -07:00
|
|
|
entityAndType?: EntityAndType | null;
|
2021-04-06 18:21:33 -07:00
|
|
|
fetchedEntities: { [x: string]: FetchedEntity };
|
|
|
|
onEntityClick: (EntitySelectParams) => void;
|
2021-04-23 00:18:39 -07:00
|
|
|
onEntityCenter: (EntitySelectParams) => void;
|
2021-04-06 18:21:33 -07:00
|
|
|
onLineageExpand: (LineageExpandParams) => void;
|
|
|
|
selectedEntity?: EntitySelectParams;
|
|
|
|
zoom: ProvidedZoom & {
|
|
|
|
transformMatrix: TransformMatrix;
|
|
|
|
isDragging: boolean;
|
|
|
|
};
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default function LineageVizInsideZoom({
|
|
|
|
zoom,
|
|
|
|
margin,
|
2021-04-09 11:55:25 -07:00
|
|
|
entityAndType,
|
2021-04-06 18:21:33 -07:00
|
|
|
fetchedEntities,
|
|
|
|
onEntityClick,
|
2021-04-23 00:18:39 -07:00
|
|
|
onEntityCenter,
|
2021-04-06 18:21:33 -07:00
|
|
|
onLineageExpand,
|
|
|
|
selectedEntity,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
}: Props) {
|
2021-10-22 15:46:46 -07:00
|
|
|
const [draggedNodes, setDraggedNodes] = useState<Record<string, { x: number; y: number }>>({});
|
|
|
|
|
|
|
|
const [hoveredEntity, setHoveredEntity] = useState<EntitySelectParams | undefined>(undefined);
|
|
|
|
const [isDraggingNode, setIsDraggingNode] = useState(false);
|
|
|
|
|
2021-04-09 11:55:25 -07:00
|
|
|
const entityRegistry = useEntityRegistry();
|
2021-04-06 18:21:33 -07:00
|
|
|
|
2021-04-09 11:55:25 -07:00
|
|
|
const downstreamData = useMemo(
|
2021-10-22 15:46:46 -07:00
|
|
|
() => constructTree(entityAndType, fetchedEntities, Direction.Downstream, entityRegistry),
|
2021-04-09 11:55:25 -07:00
|
|
|
[entityAndType, fetchedEntities, entityRegistry],
|
|
|
|
);
|
2021-10-22 15:46:46 -07:00
|
|
|
|
2021-04-09 11:55:25 -07:00
|
|
|
const upstreamData = useMemo(
|
2021-10-22 15:46:46 -07:00
|
|
|
() => constructTree(entityAndType, fetchedEntities, Direction.Upstream, entityRegistry),
|
2021-04-09 11:55:25 -07:00
|
|
|
[entityAndType, fetchedEntities, entityRegistry],
|
|
|
|
);
|
2021-04-06 18:21:33 -07:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
zoom.setTransformMatrix({ ...zoom.transformMatrix, translateY: 0, translateX: width / 2 });
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2021-04-09 11:55:25 -07:00
|
|
|
}, [entityAndType?.entity?.urn]);
|
2021-10-22 15:46:46 -07:00
|
|
|
|
2021-10-29 11:20:02 -07:00
|
|
|
// we want to clear all the dragged nodes after recentering
|
|
|
|
useEffect(() => {
|
|
|
|
setDraggedNodes({});
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [entityAndType?.entity?.urn]);
|
|
|
|
|
2021-04-06 18:21:33 -07:00
|
|
|
return (
|
2021-08-31 22:00:56 -07:00
|
|
|
<ZoomContainer>
|
|
|
|
<ZoomControls>
|
2021-04-06 18:21:33 -07:00
|
|
|
<ZoomButton onClick={() => zoom.scale({ scaleX: 1.2, scaleY: 1.2 })}>
|
|
|
|
<PlusOutlined />
|
|
|
|
</ZoomButton>
|
|
|
|
<Button onClick={() => zoom.scale({ scaleX: 0.8, scaleY: 0.8 })}>
|
|
|
|
<MinusOutlined />
|
|
|
|
</Button>
|
2021-08-31 22:00:56 -07:00
|
|
|
</ZoomControls>
|
2021-04-06 18:21:33 -07:00
|
|
|
<RootSvg
|
|
|
|
width={width}
|
|
|
|
height={height}
|
|
|
|
onMouseDown={zoom.dragStart}
|
|
|
|
onMouseUp={zoom.dragEnd}
|
2021-10-22 15:46:46 -07:00
|
|
|
onMouseMove={(e) => {
|
|
|
|
if (!isDraggingNode) {
|
|
|
|
zoom.dragMove(e);
|
|
|
|
}
|
|
|
|
}}
|
2021-04-06 18:21:33 -07:00
|
|
|
onTouchStart={zoom.dragStart}
|
2021-10-22 15:46:46 -07:00
|
|
|
onTouchMove={(e) => {
|
|
|
|
if (!isDraggingNode) {
|
|
|
|
zoom.dragMove(e);
|
|
|
|
}
|
|
|
|
}}
|
2021-04-06 18:21:33 -07:00
|
|
|
onTouchEnd={zoom.dragEnd}
|
|
|
|
isDragging={zoom.isDragging}
|
|
|
|
>
|
|
|
|
<defs>
|
|
|
|
<marker
|
|
|
|
id="triangle-downstream"
|
|
|
|
viewBox="0 0 10 10"
|
|
|
|
refX="10"
|
|
|
|
refY="5"
|
|
|
|
markerUnits="strokeWidth"
|
|
|
|
markerWidth="10"
|
|
|
|
markerHeight="10"
|
|
|
|
orient="auto"
|
|
|
|
>
|
2021-08-31 22:00:56 -07:00
|
|
|
<path d="M 0 0 L 10 5 L 0 10 z" fill="#BFBFBF" />
|
2021-04-06 18:21:33 -07:00
|
|
|
</marker>
|
|
|
|
<marker
|
|
|
|
id="triangle-upstream"
|
|
|
|
viewBox="0 0 10 10"
|
|
|
|
refX="0"
|
|
|
|
refY="5"
|
|
|
|
markerUnits="strokeWidth"
|
|
|
|
markerWidth="10"
|
|
|
|
markerHeight="10"
|
|
|
|
orient="auto"
|
|
|
|
>
|
2021-08-31 22:00:56 -07:00
|
|
|
<path d="M 0 5 L 10 10 L 10 0 L 0 5 z" fill="#BFBFBF" />
|
2021-04-06 18:21:33 -07:00
|
|
|
</marker>
|
2021-10-22 15:46:46 -07:00
|
|
|
<marker
|
|
|
|
id="triangle-downstream-highlighted"
|
|
|
|
viewBox="0 0 10 10"
|
|
|
|
refX="10"
|
|
|
|
refY="5"
|
|
|
|
markerUnits="strokeWidth"
|
|
|
|
markerWidth="10"
|
|
|
|
markerHeight="10"
|
|
|
|
orient="auto"
|
|
|
|
>
|
|
|
|
<path d="M 0 0 L 10 5 L 0 10 z" fill="#1890FF" />
|
|
|
|
</marker>
|
|
|
|
<marker
|
|
|
|
id="triangle-upstream-highlighted"
|
|
|
|
viewBox="0 0 10 10"
|
|
|
|
refX="0"
|
|
|
|
refY="5"
|
|
|
|
markerUnits="strokeWidth"
|
|
|
|
markerWidth="10"
|
|
|
|
markerHeight="10"
|
|
|
|
orient="auto"
|
|
|
|
>
|
|
|
|
<path d="M 0 5 L 10 10 L 10 0 L 0 5 z" fill="#1890FF" />
|
|
|
|
</marker>
|
2021-04-06 18:21:33 -07:00
|
|
|
<linearGradient id="gradient-Downstream" x1="1" x2="0" y1="0" y2="0">
|
2021-10-22 15:46:46 -07:00
|
|
|
<stop offset="0%" stopColor="#1890FF" />
|
|
|
|
<stop offset="100%" stopColor="#1890FF" stopOpacity="0" />
|
2021-04-06 18:21:33 -07:00
|
|
|
</linearGradient>
|
|
|
|
<linearGradient id="gradient-Upstream" x1="0" x2="1" y1="0" y2="0">
|
2021-10-22 15:46:46 -07:00
|
|
|
<stop offset="0%" stopColor="#1890FF" />
|
|
|
|
<stop offset="100%" stopColor="#1890FF" stopOpacity="0" />
|
2021-04-06 18:21:33 -07:00
|
|
|
</linearGradient>
|
2021-04-23 00:18:39 -07:00
|
|
|
<filter id="shadow1">
|
|
|
|
<feDropShadow
|
2021-08-31 22:00:56 -07:00
|
|
|
dx="0"
|
|
|
|
dy="0"
|
2021-04-23 00:18:39 -07:00
|
|
|
stdDeviation="4"
|
2021-08-31 22:00:56 -07:00
|
|
|
floodColor="rgba(72, 106, 108, 0.15)"
|
|
|
|
floodOpacity="1"
|
|
|
|
/>
|
|
|
|
</filter>
|
|
|
|
<filter id="shadow1-selected">
|
|
|
|
<feDropShadow
|
|
|
|
dx="0"
|
|
|
|
dy="0"
|
|
|
|
stdDeviation="6"
|
|
|
|
floodColor="rgba(24, 144, 255, .15)"
|
2021-04-23 00:18:39 -07:00
|
|
|
floodOpacity="1"
|
|
|
|
/>
|
|
|
|
</filter>
|
2021-04-06 18:21:33 -07:00
|
|
|
</defs>
|
2021-08-31 22:00:56 -07:00
|
|
|
<rect width={width} height={height} fill="#fafafa" />
|
2021-04-06 18:21:33 -07:00
|
|
|
<LineageTree
|
|
|
|
data={upstreamData}
|
|
|
|
zoom={zoom}
|
|
|
|
onEntityClick={onEntityClick}
|
2021-04-23 00:18:39 -07:00
|
|
|
onEntityCenter={onEntityCenter}
|
2021-04-06 18:21:33 -07:00
|
|
|
onLineageExpand={onLineageExpand}
|
|
|
|
margin={margin}
|
|
|
|
selectedEntity={selectedEntity}
|
2021-10-22 15:46:46 -07:00
|
|
|
hoveredEntity={hoveredEntity}
|
|
|
|
setHoveredEntity={setHoveredEntity}
|
2021-04-06 18:21:33 -07:00
|
|
|
direction={Direction.Upstream}
|
2021-10-22 15:46:46 -07:00
|
|
|
canvasHeight={height}
|
|
|
|
setIsDraggingNode={setIsDraggingNode}
|
|
|
|
draggedNodes={draggedNodes}
|
|
|
|
setDraggedNodes={setDraggedNodes}
|
2021-04-06 18:21:33 -07:00
|
|
|
/>
|
|
|
|
<LineageTree
|
|
|
|
data={downstreamData}
|
|
|
|
zoom={zoom}
|
|
|
|
onEntityClick={onEntityClick}
|
2021-04-23 00:18:39 -07:00
|
|
|
onEntityCenter={onEntityCenter}
|
2021-04-06 18:21:33 -07:00
|
|
|
onLineageExpand={onLineageExpand}
|
|
|
|
margin={margin}
|
|
|
|
selectedEntity={selectedEntity}
|
2021-10-22 15:46:46 -07:00
|
|
|
hoveredEntity={hoveredEntity}
|
|
|
|
setHoveredEntity={setHoveredEntity}
|
2021-04-06 18:21:33 -07:00
|
|
|
direction={Direction.Downstream}
|
2021-10-22 15:46:46 -07:00
|
|
|
canvasHeight={height}
|
|
|
|
setIsDraggingNode={setIsDraggingNode}
|
|
|
|
draggedNodes={draggedNodes}
|
|
|
|
setDraggedNodes={setDraggedNodes}
|
2021-04-06 18:21:33 -07:00
|
|
|
/>
|
|
|
|
</RootSvg>
|
2021-08-31 22:00:56 -07:00
|
|
|
</ZoomContainer>
|
2021-04-06 18:21:33 -07:00
|
|
|
);
|
|
|
|
}
|