knsv/mermaid

View on GitHub
packages/mermaid/src/dagre-wrapper/blockArrowHelper.ts

Summary

Maintainability
D
1 day
Test Coverage
import type { Direction } from '../../src/diagrams/block/blockTypes.js';

const expandAndDeduplicateDirections = (directions: Direction[]) => {
  const uniqueDirections = new Set();

  for (const direction of directions) {
    switch (direction) {
      case 'x':
        uniqueDirections.add('right');
        uniqueDirections.add('left');
        break;
      case 'y':
        uniqueDirections.add('up');
        uniqueDirections.add('down');
        break;
      default:
        uniqueDirections.add(direction);
        break;
    }
  }

  return uniqueDirections;
};
export const getArrowPoints = (
  duplicatedDirections: Direction[],
  bbox: { width: number; height: number },
  node: any
) => {
  // Expand and deduplicate the provided directions.
  // for instance: x, right => right, left
  const directions = expandAndDeduplicateDirections(duplicatedDirections);

  // Factor to divide height for some calculations.
  const f = 2;

  // Calculated height of the bounding box, accounting for node padding.
  const height = bbox.height + 2 * node.padding;
  // Midpoint calculation based on height.
  const midpoint = height / f;
  // Calculated width of the bounding box, accounting for additional width and node padding.
  const width = bbox.width + 2 * midpoint + node.padding;
  // Padding to use, half of the node padding.
  const padding = node.padding / 2;

  // Initialize an empty array to store points for the arrow.
  const points = [];

  if (
    directions.has('right') &&
    directions.has('left') &&
    directions.has('up') &&
    directions.has('down')
  ) {
    // SQUARE
    return [
      // Bottom
      { x: 0, y: 0 },
      { x: midpoint, y: 0 },
      { x: width / 2, y: 2 * padding },
      { x: width - midpoint, y: 0 },
      { x: width, y: 0 },

      // Right
      { x: width, y: -height / 3 },
      { x: width + 2 * padding, y: -height / 2 },
      { x: width, y: (-2 * height) / 3 },
      { x: width, y: -height },

      // Top
      { x: width - midpoint, y: -height },
      { x: width / 2, y: -height - 2 * padding },
      { x: midpoint, y: -height },

      // Left
      { x: 0, y: -height },
      { x: 0, y: (-2 * height) / 3 },
      { x: -2 * padding, y: -height / 2 },
      { x: 0, y: -height / 3 },
    ];
  }
  if (directions.has('right') && directions.has('left') && directions.has('up')) {
    // RECTANGLE_VERTICAL (Top Open)
    return [
      { x: midpoint, y: 0 },
      { x: width - midpoint, y: 0 },
      { x: width, y: -height / 2 },
      { x: width - midpoint, y: -height },
      { x: midpoint, y: -height },
      { x: 0, y: -height / 2 },
    ];
  }
  if (directions.has('right') && directions.has('left') && directions.has('down')) {
    // RECTANGLE_VERTICAL (Bottom Open)
    return [
      { x: 0, y: 0 },
      { x: midpoint, y: -height },
      { x: width - midpoint, y: -height },
      { x: width, y: 0 },
    ];
  }
  if (directions.has('right') && directions.has('up') && directions.has('down')) {
    // RECTANGLE_HORIZONTAL (Right Open)
    return [
      { x: 0, y: 0 },
      { x: width, y: -midpoint },
      { x: width, y: -height + midpoint },
      { x: 0, y: -height },
    ];
  }
  if (directions.has('left') && directions.has('up') && directions.has('down')) {
    // RECTANGLE_HORIZONTAL (Left Open)
    return [
      { x: width, y: 0 },
      { x: 0, y: -midpoint },
      { x: 0, y: -height + midpoint },
      { x: width, y: -height },
    ];
  }
  if (directions.has('right') && directions.has('left')) {
    // HORIZONTAL_LINE
    return [
      { x: midpoint, y: 0 },
      { x: midpoint, y: -padding },
      { x: width - midpoint, y: -padding },
      { x: width - midpoint, y: 0 },
      { x: width, y: -height / 2 },
      { x: width - midpoint, y: -height },
      { x: width - midpoint, y: -height + padding },
      { x: midpoint, y: -height + padding },
      { x: midpoint, y: -height },
      { x: 0, y: -height / 2 },
    ];
  }
  if (directions.has('up') && directions.has('down')) {
    // VERTICAL_LINE
    return [
      // Bottom center
      { x: width / 2, y: 0 },
      // Left pont of bottom arrow
      { x: 0, y: -padding },
      { x: midpoint, y: -padding },
      // Left top over vertical section
      { x: midpoint, y: -height + padding },
      { x: 0, y: -height + padding },
      // Top of arrow
      { x: width / 2, y: -height },
      { x: width, y: -height + padding },
      // Top of right vertical bar
      { x: width - midpoint, y: -height + padding },
      { x: width - midpoint, y: -padding },
      { x: width, y: -padding },
    ];
  }
  if (directions.has('right') && directions.has('up')) {
    // ANGLE_RT
    return [
      { x: 0, y: 0 },
      { x: width, y: -midpoint },
      { x: 0, y: -height },
    ];
  }
  if (directions.has('right') && directions.has('down')) {
    // ANGLE_RB
    return [
      { x: 0, y: 0 },
      { x: width, y: 0 },
      { x: 0, y: -height },
    ];
  }
  if (directions.has('left') && directions.has('up')) {
    // ANGLE_LT
    return [
      { x: width, y: 0 },
      { x: 0, y: -midpoint },
      { x: width, y: -height },
    ];
  }
  if (directions.has('left') && directions.has('down')) {
    // ANGLE_LB
    return [
      { x: width, y: 0 },
      { x: 0, y: 0 },
      { x: width, y: -height },
    ];
  }
  if (directions.has('right')) {
    // ARROW_RIGHT
    return [
      { x: midpoint, y: -padding },
      { x: midpoint, y: -padding },
      { x: width - midpoint, y: -padding },
      { x: width - midpoint, y: 0 },
      { x: width, y: -height / 2 },
      { x: width - midpoint, y: -height },
      { x: width - midpoint, y: -height + padding },
      // top left corner of arrow
      { x: midpoint, y: -height + padding },
      { x: midpoint, y: -height + padding },
    ];
  }
  if (directions.has('left')) {
    // ARROW_LEFT
    return [
      { x: midpoint, y: 0 },
      { x: midpoint, y: -padding },
      // Two points, the right corners
      { x: width - midpoint, y: -padding },
      { x: width - midpoint, y: -height + padding },
      { x: midpoint, y: -height + padding },
      { x: midpoint, y: -height },
      { x: 0, y: -height / 2 },
    ];
  }
  if (directions.has('up')) {
    // ARROW_TOP
    return [
      // Bottom center
      { x: midpoint, y: -padding },
      // Left top over vertical section
      { x: midpoint, y: -height + padding },
      { x: 0, y: -height + padding },
      // Top of arrow
      { x: width / 2, y: -height },
      { x: width, y: -height + padding },
      // Top of right vertical bar
      { x: width - midpoint, y: -height + padding },
      { x: width - midpoint, y: -padding },
    ];
  }
  if (directions.has('down')) {
    // ARROW_BOTTOM
    return [
      // Bottom center
      { x: width / 2, y: 0 },
      // Left pont of bottom arrow
      { x: 0, y: -padding },
      { x: midpoint, y: -padding },
      // Left top over vertical section
      { x: midpoint, y: -height + padding },
      { x: width - midpoint, y: -height + padding },
      { x: width - midpoint, y: -padding },
      { x: width, y: -padding },
    ];
  }

  // POINT
  return [{ x: 0, y: 0 }];
};