saagie-ui
Version:
Saagie UI from Saagie Design System
171 lines (137 loc) • 3.67 kB
JavaScript
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import SVG from 'svg.js';
import extendSVGPath from './utils/extendSVGPath';
if (SVG && SVG.extend) {
extendSVGPath();
}
const propTypes = {
containerId: PropTypes.string.isRequired,
};
const getOffset = (el, offset = { top: 0, left: 0 }) => {
if (!el) {
return offset;
}
const newOffset = {
top: offset.top + el.offsetTop,
left: offset.left + el.offsetLeft,
};
return getOffset(el.offsetParent, newOffset);
};
const getPosition = (container, fromElement, toElement) => {
if (!container || !fromElement || !toElement) {
return {
start: { x: 0, y: 0 },
end: { x: 0, y: 0 },
};
}
const containerPosition = getOffset(container);
const fromPosition = getOffset(fromElement);
const toPosition = getOffset(toElement);
return {
start: {
x: fromPosition.left - containerPosition.left + fromElement.offsetWidth,
y: fromPosition.top - containerPosition.top + (fromElement.offsetHeight / 2),
},
end: {
x: toPosition.left - containerPosition.left,
y: toPosition.top - containerPosition.top + 12,
},
};
};
const getCurv = (start, end) => {
const xRatio = (end.x - start.x) * 0.5;
const yRatio = (end.y - start.y) * 0.2;
return {
start: {
x: start.x + xRatio,
y: start.y - yRatio,
},
end: {
x: end.x - xRatio,
y: end.y + yRatio,
},
};
};
const getLineClassName = (toElement) => {
if (!toElement) {
return '';
}
const allStatus = [
'awaiting',
'requested',
'queued',
'running',
'succeeded',
'stopping',
'stopped',
'failed',
'skipped',
'out_of_memory',
];
return allStatus.reduce((classNames, status) => {
if (!toElement) {
return classNames;
}
const hasStatus = toElement.classList.contains(`as--${status}`);
if (!hasStatus) {
return classNames;
}
return `${classNames} as--${status}`;
}, '');
};
const drawLine = (draw, container, fromElement, toElement) => {
if (!fromElement || !toElement) {
return;
}
const { start, end } = getPosition(container, fromElement, toElement);
const curv = getCurv(start, end);
if (!draw.path || !draw.path().M || !draw.path().C || !draw.path().addClass) {
return;
}
draw
.path()
.M(start)
.C(curv.start, curv.end, end)
.addClass(`sui-prj-pipeline-connectors__line ${getLineClassName(toElement)}`);
};
const connectElements = (draw, container, elementSelector) => {
if (!draw || !container) {
return;
}
const jobs = container.querySelectorAll(elementSelector);
jobs.forEach((job, index) => {
if (index <= 0 || index >= jobs.length) {
return;
}
drawLine(draw, container, jobs[index - 1], job);
});
};
const destroy = (draw, svgContainer) => {
if (draw) {
draw.clear();
}
const mainSvg = svgContainer ? svgContainer.querySelector('svg') : false;
if (mainSvg) {
svgContainer.removeChild(mainSvg);
}
};
export const PipelineConnectors = ({ containerId }) => {
const svgContainerId = `${containerId}-connectors`;
useEffect(() => {
const draw = SVG(svgContainerId).size('100%', '100%');
const svgContainer = document.getElementById(svgContainerId);
const container = document.getElementById(containerId);
connectElements(draw, container, '.sui-prj-pipeline-job');
return () => {
destroy(draw, svgContainer);
};
}, [containerId]);
return (
<div
className="sui-prj-pipeline__connectors"
id={svgContainerId}
/>
);
};
PipelineConnectors.propTypes = propTypes;