text: import {
    Graph,
    RubberBandHandler,
    Point,
    EdgeHandler,
    ConstraintHandler,
    ImageBox,
    Shape,
    TriangleShape,
    constants,
    ConnectionConstraint,
    Client,
  } from '@maxgraph/core';
  
  import { globalTypes } from '../.storybook/preview';
  
  export default {
    title: 'Connections/PortRefs',
    argTypes: {
      ...globalTypes,
    },
  };
  
  const Template = ({ label, ...args }) => {
    Client.setImageBasePath('/images');
  
    const container = document.createElement('div');
    container.style.position = 'relative';
    container.style.overflow = 'hidden';
    container.style.width = `${args.width}px`;
    container.style.height = `${args.height}px`;
    container.style.background = 'url(/images/grid.gif)';
    container.style.cursor = 'default';
  
    // Replaces the port image
    ConstraintHandler.prototype.pointImage = new ImageBox('/images/dot.gif', 10, 10);
  
    const graph = new Graph(container);
    graph.setConnectable(true);
  
    // Disables automatic handling of ports. This disables the reset of the
    // respective style in Graph.cellConnected. Note that this feature may
    // be useful if floating and fixed connections are combined.
    graph.setPortsEnabled(false);
  
    // Enables rubberband selection
    new RubberBandHandler(graph);
  
    // Gets the default parent for inserting new cells. This
    // is normally the first child of the root (ie. layer 0).
    const parent = graph.getDefaultParent();
  
    // Ports are equal for all shapes...
    const ports = new Array();
  
    // NOTE: Constraint is used later for orthogonal edge routing (currently ignored)
    ports.w = { x: 0, y: 0.5, perimeter: true, constraint: 'west' };
    ports.e = { x: 1, y: 0.5, perimeter: true, constraint: 'east' };
    ports.n = { x: 0.5, y: 0, perimeter: true, constraint: 'north' };
    ports.s = { x: 0.5, y: 1, perimeter: true, constraint: 'south' };
    ports.nw = { x: 0, y: 0, perimeter: true, constraint: 'north west' };
    ports.ne = { x: 1, y: 0, perimeter: true, constraint: 'north east' };
    ports.sw = { x: 0, y: 1, perimeter: true, constraint: 'south west' };
    ports.se = { x: 1, y: 1, perimeter: true, constraint: 'south east' };
  
    // ... except for triangles
    const ports2 = new Array();
  
    // NOTE: Constraint is used later for orthogonal edge routing (currently ignored)
    ports2.in1 = { x: 0, y: 0, perimeter: true, constraint: 'west' };
    ports2.in2 = { x: 0, y: 0.25, perimeter: true, constraint: 'west' };
    ports2.in3 = { x: 0, y: 0.5, perimeter: true, constraint: 'west' };
    ports2.in4 = { x: 0, y: 0.75, perimeter: true, constraint: 'west' };
    ports2.in5 = { x: 0, y: 1, perimeter: true, constraint: 'west' };
  
    ports2.out1 = {
      x: 0.5,
      y: 0,
      perimeter: true,
      constraint: 'north east',
    };
    ports2.out2 = { x: 1, y: 0.5, perimeter: true, constraint: 'east' };
    ports2.out3 = {
      x: 0.5,
      y: 1,
      perimeter: true,
      constraint: 'south east',
    };
  
    // Extends shapes classes to return their ports
    Shape.prototype.getPorts = function () {
      return ports;
    };
  
    TriangleShape.prototype.getPorts = function () {
      return ports2;
    };
  
    const connectionHandler = graph.getPlugin('ConnectionHandler');
  
    // Disables floating connections (only connections via ports allowed)
    connectionHandler.isConnectableCell = function (cell) {
      return false;
    };
    EdgeHandler.prototype.isConnectableCell = function (cell) {
      return connectionHandler.isConnectableCell(cell);
    };
  
    // Disables existing port functionality
    graph.view.getTerminalPort = function (state, terminal, source) {
      return terminal;
    };
  
    // Returns all possible ports for a given terminal
    graph.getAllConnectionConstraints = function (terminal, source) {
      if (terminal != null && terminal.shape != null && terminal.shape.stencil != null) {
        // for stencils with existing constraints...
        if (terminal.shape.stencil != null) {
          return terminal.shape.stencil.constraints;
        }
      } else if (terminal != null && terminal.cell.isVertex()) {
        if (terminal.shape != null) {
          const ports = terminal.shape.getPorts();
          const cstrs = new Array();
  
          for (const id in ports) {
            const port = ports[id];
  
            const cstr = new ConnectionConstraint(
              new Point(port.x, port.y),
              port.perimeter
            );
            cstr.id = id;
            cstrs.push(cstr);
          }
  
          return cstrs;
        }
      }
  
      return null;
    };
  
    // Sets the port for the given connection
    graph.setConnectionConstraint = function (edge, terminal, source, constraint) {
      if (constraint != null) {
        const key = source ? 'sourcePort' : 'targetPort';
  
        if (constraint == null || constraint.id == null) {
          this.setCellStyles(key, null, [edge]);
        } else if (constraint.id != null) {
          this.setCellStyles(key, constraint.id, [edge]);
        }
      }
    };
  
    // Returns the port for the given connection
    graph.getConnectionConstraint = function (edge, terminal, source) {
      const key = source ? 'sourcePort' : 'targetPort';
      const id = edge.style[key];
  
      if (id != null) {
        const c = new ConnectionConstraint(null, null);
        c.id = id;
  
        return c;
      }
  
      return null;
    };
  
    // Returns the actual point for a port by redirecting the constraint to the port
    const graphGetConnectionPoint = graph.getConnectionPoint;
    graph.getConnectionPoint = function (vertex, constraint) {
      if (constraint.id != null && vertex != null && vertex.shape != null) {
        const port = vertex.shape.getPorts()[constraint.id];
  
        if (port != null) {
          constraint = new ConnectionConstraint(new Point(port.x, port.y), port.perimeter);
        }
      }
  
      return graphGetConnectionPoint.apply(this, arguments);
    };
  
    // Adds cells to the model in a single step
    graph.batchUpdate(() => {
      const v1 = graph.insertVertex(parent, null, 'A', 20, 20, 100, 40);
      const v2 = graph.insertVertex(
        parent,
        null,
        'B',
        80,
        100,
        100,
        100,
        'shape=ellipse;perimeter=ellipsePerimeter'
      );
      const v3 = graph.insertVertex(
        parent,
        null,
        'C',
        190,
        30,
        100,
        60,
        'shape=triangle;perimeter=trianglePerimeter;direction=south'
      );
      const e1 = graph.insertEdge(parent, null, '', v1, v2, 'sourcePort=s;targetPort=nw');
      const e2 = graph.insertEdge(parent, null, '', v1, v3, 'sourcePort=e;targetPort=out3');
    });
  
    // Comming soon... Integration with orthogonal edge style
    // Sets default edge style to use port constraints (needs to be moved up when uncommented)
    // graph.getStylesheet().getDefaultEdgeStyle()['edgeStyle'] = 'orthogonalEdgeStyle';
    /* let mxUtilsGetPortConstraints = utils.getPortConstraints;
      utils.getPortConstraints = function(terminal, edge, source, defaultValue)
      {
        let key = (source) ? constants.STYLE_SOURCE_PORT : constants.STYLE_TARGET_PORT;
        let id = edge.style[key];
  
        let port = terminal.shape.getPorts()[id];
  
        // TODO: Add support for rotation, direction
        if (port != null)
        {
          return port.constraint;
        }
  
        return mxUtilsGetPortConstraints.apply(this, arguments);
      };
      // Connect preview
      graph.getPlugin('ConnectionHandler').createEdgeState = function(me)
      {
        let edge = graph.createEdge(null, null, null, null, null);
  
        return new CellState(this.graph.view, edge, this.graph.getCellStyle(edge));
      };
      */
  
    return container;
  };
  
  export const Default = Template.bind({});
  