github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/witch/webroot/viz.html (about)

     1  <script type="text/x-template" id="viz-template">
     2      <grid-layout
     3              :layout="layout"
     4              :col-num="24"
     5              :row-height="24"
     6              :is-draggable="true"
     7              :is-resizable="true"
     8              :is-mirrored="false"
     9              :vertical-compact="false"
    10              :margin="[24, 24]"
    11              :use-css-transforms="false">
    12          <grid-item v-for="item in layout"
    13                     v-show="ptrState[item.i]"
    14                     :x="item.x" :y="item.y"
    15                     :w="item.w" :h="item.h"
    16                     :i="item.i" :key="item.i" :ref="item.i"
    17                     drag-allow-from=".vue-draggable-handle"
    18                     drag-ignore-from=".no-drag"
    19                     @move="onMove" @resize="onMove"
    20                     @moved="onMoved" @resized="onMoved">
    21              <viz-item :addrMap="addrMap" :ptr="item.i"
    22                        @showPtr="showPtr" @hidePtr="hidePtr"/>
    23          </grid-item>
    24          <svg style="position:absolute;left:0px;top:0px;pointer-events: none;"
    25               width="100%" height="100%">
    26              <defs>
    27                  <marker id="triangle" viewBox="0 0 10 10" refX="0" refY="5"
    28                          markerUnits="strokeWidth" markerWidth="10"
    29                          markerHeight="8" orient="auto">
    30                      <path d="M 0 0 L 10 5 L 0 10 z"></path>
    31                  </marker>
    32              </defs>
    33              <g v-for="(connector, connectorId) in connectors"
    34                 :key="connectorId" v-show="!connector.hidden">
    35                  <circle :cx="connector.x1" :cy="connector.y1" r="3" fill="#456"
    36                          stroke="none"/>
    37                  <path :d="curve(connector)" fill="none" stroke="#456" marker-end="url(#triangle)"/>
    38              </g>
    39          </svg>
    40      </grid-layout>
    41  </script>
    42  <style>
    43      table.viz-item, .viz-item table, .viz-item th, .viz-item td {
    44          border: 1px dashed #BBB;
    45      }
    46  
    47      table.viz-item, .viz-item table {
    48          border-bottom: 0;
    49          border-left: 0;
    50      }
    51  
    52      .viz-item td, .viz-item th {
    53          border-top: 0;
    54          border-right: 0;
    55          padding-left: 4px;
    56      }
    57  
    58      .vue-draggable-handle {
    59          position: absolute;
    60          width: 24px;
    61          height: 24px;
    62          top: 0px;
    63          left: 0px;
    64          background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
    65          background-position: bottom right;
    66          padding: 0 8px 8px 0;
    67          background-repeat: no-repeat;
    68          background-origin: content-box;
    69          box-sizing: border-box;
    70          cursor: pointer;
    71      }
    72  </style>
    73  <script>
    74      Vue.component('viz', {
    75          template: '#viz-template',
    76          props: ['addrMap'],
    77          data: function () {
    78              return {
    79                  layout: [{"x": 0, "y": 0, "w": 6, "h": 3, "i": "__root__"}],
    80                  connectors: {},
    81                  endpoints: {},
    82                  ptrState: {'__root__': true}
    83              }
    84          },
    85          watch: {
    86              'addrMap': function() {
    87                  Object.assign(this.$data, this.$options.data());
    88              }
    89          },
    90          methods: {
    91              showPtr: function (e) {
    92                  var connector = this.connectors[e.connectorId];
    93                  if (connector) {
    94                      Vue.set(this.ptrState, e.targetPtr, true);
    95                      connector.hidden = false;
    96                      connector.deleted = false;
    97                      return;
    98                  }
    99                  Vue.set(this.ptrState, e.targetPtr, true);
   100                  if (!this.endpoints[e.targetPtr]) {
   101                      var srcComp = this.$refs[e.sourcePtr][0];
   102                      var targetItem = {"x": 1, "y": srcComp.y + srcComp.h, "w": 2, "h": 2, "i": e.targetPtr};
   103                      chooseX(this.layout, targetItem);
   104                      this.layout.push(targetItem);
   105                  }
   106                  var me = this;
   107                  this.$nextTick(function () {
   108                      var comp = me.$refs[e.targetPtr][0];
   109                      var compEl = comp.$el.getElementsByTagName('table')[0];
   110                      var colWidth = document.documentElement.clientWidth / 24;
   111                      for (var i = 0; i < me.layout.length; i++) {
   112                          if (me.layout[i].i === e.targetPtr) {
   113                              me.layout[i].h = 1 + parseInt(compEl.offsetHeight / 48);
   114                              if (me.layout[i].h < 3) {
   115                                  me.layout[i].h = 3
   116                              }
   117                              me.layout[i].w = 2 + parseInt(compEl.offsetWidth / colWidth);
   118                          }
   119                      }
   120                      me.updatePosition(e);
   121                      e.hidden = false;
   122                      Vue.set(me.connectors, e.connectorId, e);
   123                      var endpointConnectors = me.endpoints[e.sourcePtr];
   124                      if (endpointConnectors) {
   125                          endpointConnectors.push(e);
   126                      } else {
   127                          Vue.set(me.endpoints, e.sourcePtr, [e]);
   128                      }
   129                      endpointConnectors = me.endpoints[e.targetPtr];
   130                      if (endpointConnectors) {
   131                          endpointConnectors.push(e);
   132                      } else {
   133                          Vue.set(me.endpoints, e.targetPtr, [e]);
   134                      }
   135                  });
   136              },
   137              hidePtr: function (e) {
   138                  var connector = this.connectors[e.connectorId];
   139                  if (!connector) {
   140                      return;
   141                  }
   142                  connector.hidden = true;
   143                  connector.deleted = true;
   144                  var endpointConnectors = this.endpoints[e.targetPtr];
   145                  if (!endpointConnectors) {
   146                      return;
   147                  }
   148                  var stillAlive = false;
   149                  for (var i = 0; i < endpointConnectors.length; i++) {
   150                      var connector = endpointConnectors[i];
   151                      if (connector.targetPtr === e.targetPtr && !connector.deleted) {
   152                          stillAlive = true;
   153                          break;
   154                      }
   155                  }
   156                  if (!stillAlive) {
   157                      for (var i = 0; i < endpointConnectors.length; i++) {
   158                          var connector = endpointConnectors[i];
   159                          if (connector.sourcePtr === e.targetPtr) {
   160                              this.hidePtr(connector);
   161                          }
   162                      }
   163                      Vue.set(this.ptrState, e.targetPtr, false);
   164                  }
   165              },
   166              onMove: function (ptr) {
   167                  var connectors = this.endpoints[ptr];
   168                  if (!connectors) {
   169                      return;
   170                  }
   171                  for (var i = 0; i < connectors.length; i++) {
   172                      connectors[i].hidden = true;
   173                  }
   174              },
   175              onMoved: function (ptr) {
   176                  var me = this;
   177                  window.setTimeout(function () {
   178                      var connectors = me.endpoints[ptr];
   179                      if (!connectors) {
   180                          return;
   181                      }
   182                      for (var i = 0; i < connectors.length; i++) {
   183                          var connector = connectors[i];
   184                          if (!connector.deleted) {
   185                              connector.hidden = false;
   186                          }
   187                          me.updatePosition(connector);
   188                      }
   189                  }, 50);
   190              },
   191              updatePosition: function (connector) {
   192                  var sourceComp = this.$refs[connector.sourcePtr][0];
   193                  var sourceStyle = sourceComp.style;
   194                  var pos = relativePos(connector.sourceElem, sourceComp.$el);
   195                  var x1 = parseInt(sourceStyle.left.slice(0, -2));
   196                  connector.x1 = x1 + pos.left + connector.sourceElem.offsetWidth / 2;
   197                  var y1 = parseInt(sourceStyle.top.slice(0, -2));
   198                  connector.y1 = y1 + pos.top + connector.sourceElem.offsetHeight - 8;
   199                  var targetComp = this.$refs[connector.targetPtr][0];
   200                  var targetStyle = targetComp.style;
   201                  var x2 = parseInt(targetStyle.left.slice(0, -2));
   202                  connector.x2 = x2 + 8;
   203                  var y2 = parseInt(targetStyle.top.slice(0, -2));
   204                  connector.y2 = y2;
   205              },
   206              curve: function (connector) {
   207                  var tension = 0;
   208                  if (connector.x1 < connector.x2) {
   209                      tension = 0.2
   210                  } else {
   211                      tension = -0.2
   212                  }
   213                  var x1 = connector.x1;
   214                  var y1 = connector.y1;
   215                  var x2 = connector.x2;
   216                  var y2 = connector.y2;
   217                  var delta = (x2 - x1) * tension;
   218                  var hx1 = x1;
   219                  var hy1 = y1 + delta;
   220                  var hx2 = x2;
   221                  var hy2 = y2 - delta;
   222                  var path = "M " + x1 + " " + y1 +
   223                          " C " + hx1 + " " + hy1
   224                          + " " + hx2 + " " + hy2
   225                          + " " + x2 + " " + y2;
   226                  return path;
   227              }
   228          }
   229      });
   230      function relativePos(child, ancestor) {
   231          if (child === ancestor) {
   232              return {left: 0, top: 0}
   233          }
   234          var offset = relativePos(child.offsetParent, ancestor);
   235          offset.left += child.offsetLeft;
   236          offset.top += child.offsetTop;
   237          return offset;
   238      }
   239      function chooseX(layout, targetItem) {
   240          for (var x = 1; x < 24; x++) {
   241              targetItem.x = x;
   242              if (!findCollision(layout, targetItem)) {
   243                  return
   244              }
   245          }
   246          targetItem.x = 1;
   247      }
   248      function findCollision(layout, targetItem) {
   249          for (var i = 0; i < layout.length; i++) {
   250              var item = layout[i];
   251              if (collides(item, targetItem)) {
   252                  return true
   253              }
   254          }
   255          return false
   256      }
   257      function collides(l1, l2) {
   258          if (l1 === l2) return false; // same element
   259          if (l1.x + l1.w <= l2.x) return false; // l1 is left of l2
   260          if (l1.x >= l2.x + l2.w) return false; // l1 is right of l2
   261          if (l1.y + l1.h <= l2.y) return false; // l1 is above l2
   262          if (l1.y >= l2.y + l2.h) return false; // l1 is below l2
   263          return true; // boxes overlap
   264      }
   265  </script>