Link = (props) => {
  const { input, output, link, onDelete } = props;
  const pathRef = useRef();
  const [labelPosition, setLabelPosition] = useState();
  const { canvas, portRefs, nodeRefs } = useContextRefs();
  const inputPoint = useMemo(() => getCoords(input, portRefs, nodeRefs, canvas), [input, portRefs, nodeRefs, canvas]);
  /* eslint-disable max-len */
  const classList = useMemo(() => classNames('bi-diagram-link', { 'readonly-link': link.readonly }, link.className), [link.readonly, link.className]);
  const outputPoint = useMemo(() => getCoords(output, portRefs, nodeRefs, canvas), [output, portRefs, nodeRefs, canvas]);
  /* eslint-enable max-len */
  const pathOptions = {
    type: (input.type === 'port' || output.type === 'port') ? 'bezier' : 'curve',
    inputAlignment: input.entity.alignment || null,
    outputAlignment: output.entity.alignment || null,
  };
  const path = useMemo(() => makeSvgPath(inputPoint, outputPoint, pathOptions), [inputPoint, outputPoint]);

  // calculates label position
  useEffect(() => {
    if (link.label && inputPoint && outputPoint && pathRef.current) {
      const pos = getPathMidpoint(pathRef.current);
      setLabelPosition(pos);
    }
  }, [pathRef.current, link.label, inputPoint, outputPoint]);

  // on link delete
  const onDoubleClick = useCallback(() => {
    if (onDelete && !link.readonly) {
      onDelete(link);
    }
  }, [link.readonly, onDelete]);

  return (
    <g className={classList}>
      {!link.readonly && (<path d={path} className="bi-link-ghost" onDoubleClick={onDoubleClick} />)}
      <path d={path} ref={pathRef} className="bi-link-path" onDoubleClick={onDoubleClick} />
      {link.label && labelPosition && (<LinkLabel position={labelPosition} label={link.label} />)}
    </g>
  );
}
A Diagram link component displays the link between two diagram nodes or two node ports.