UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

171 lines (137 loc) 3.67 kB
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;