node.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <template>
  2. <g
  3. class="flow-node"
  4. :class="{'flow-node--dragging':dragging}"
  5. @mousedown="$emit('nodePointerDown',$event)"
  6. >
  7. <rect
  8. ref="body"
  9. class="flow-node__body"
  10. :class="{'flow-node__body--dragging':dragging}"
  11. v-bind="bodyProps"
  12. />
  13. <text
  14. ref="label"
  15. class="flow-node__label"
  16. v-bind="labelProps">
  17. {{ label }}
  18. </text>
  19. <!-- input -->
  20. <circle
  21. class="flow-node__socket flow-node__socket--inputs"
  22. v-for="(inp,i) in inputs"
  23. v-bind="inputProps(i)"
  24. :data-nodeid="id"
  25. :data-in="i"
  26. :class="{'flow-node__socket--match': inp == match.in}"
  27. :key="'in'+i"
  28. @mousedown.stop.prevent="socketPointerDown($event, {in:i})"
  29. />
  30. <!-- output -->
  31. <circle
  32. v-if="output"
  33. class="flow-node__socket flow-node__socket--outputs"
  34. :data-nodeid="id"
  35. :data-out="0"
  36. :key="'out'+0"
  37. :class="{ 'flow-node__socket--match': output == match.out}"
  38. v-bind="outputProps(0)"
  39. @mousedown.stop.prevent="socketPointerDown($event, {out:0})"
  40. />
  41. </g>
  42. </template>
  43. <script>
  44. export default {
  45. name: 'FlowNode',
  46. props: {
  47. 'id': {type: String, required: true},
  48. 'label': {type: String, default: ''},
  49. 'type': {type: String, default: ''},
  50. 'inputs': {type: Array, default: () => []},
  51. 'output': {type: String, default: ''},
  52. 'match': {type: Object, default: () => {}},
  53. 'dragging': {type: Boolean, default: false},
  54. 'color': {type: String, default: '#777'},
  55. 'textColor': {type: String, default: '#fff'}
  56. // 'value': {type: Object, default: () => {}}
  57. },
  58. data () {
  59. return {
  60. // maintain reference here?
  61. labelRect: {x: 0, y: 0, width: 0, height: 0},
  62. bodyRect: {x: 0, y: 0, width: 0, height: 0}
  63. }
  64. },
  65. computed: {
  66. labelProps () {
  67. return {
  68. x: -this.labelRect.width / 2,
  69. y: 5,
  70. fill: this.textColor
  71. }
  72. },
  73. bodyProps () {
  74. const width = this.labelRect.width + 32
  75. const rect = {
  76. x: -width / 2,
  77. y: -50,
  78. width: width,
  79. height: 100,
  80. fill: this.color
  81. }
  82. if (this.type === 'circle') {
  83. rect.rx = width
  84. rect.ry = width
  85. }
  86. return rect
  87. },
  88. inputProps () {
  89. return (i) => {
  90. const {x, y} = this.inputPos(i)
  91. return {
  92. transform: `translate(${x} ${y})`,
  93. r: 5
  94. }
  95. }
  96. },
  97. outputProps () {
  98. return (i) => {
  99. const {x, y} = this.outputPos(i)
  100. return {
  101. transform: `translate(${x} ${y})`,
  102. r: 5
  103. }
  104. }
  105. }
  106. },
  107. watch: {
  108. // the only thing now that affects geometry
  109. 'label' () {
  110. this.$nextTick(() => {
  111. this.labelRect = this.$refs.label.getBBox()
  112. })
  113. }
  114. },
  115. mounted () {
  116. // After render
  117. this.$nextTick(() => { // after mount we reupdate with new values
  118. if (!this.$refs.label) return
  119. this.labelRect = this.$refs.label.getBBox()
  120. this.$forceUpdate()
  121. })
  122. },
  123. methods: {
  124. inputPos (i) {
  125. const ilen = this.inputs.length
  126. if (ilen === 0) return {}
  127. const d = this.bodyProps.height / (ilen * 2)
  128. return {
  129. x: this.bodyProps.x + 8,
  130. y: this.bodyProps.y + d + (i * 2 * d)
  131. }
  132. },
  133. outputPos (i) {
  134. const rect = this.bodyProps
  135. return {
  136. x: rect.x + rect.width - 8,
  137. y: 0
  138. }
  139. },
  140. socketPointerDown (ev, socket) {
  141. this.$emit('socketPointerDown', ev, socket)
  142. }
  143. }
  144. }
  145. </script>
  146. <style lang="scss">
  147. .flow-node {
  148. filter: url(#highlight-border);
  149. }
  150. svg:not(.activity) .flow-node:hover .flow-node__body{
  151. stroke-width:1;
  152. stroke:black;
  153. cursor:move;
  154. }
  155. .flow-node__socket {
  156. stroke-width:1;
  157. cursor:pointer;
  158. fill: #AFAFAF;
  159. stroke: #AFAFAF;
  160. }
  161. .flow-node__socket:not(.flow-node__socket--match):hover {
  162. stroke-width:10;
  163. }
  164. .flow-node__socket--match {
  165. stroke-width:10;
  166. stroke:#2a2;
  167. fill:#2a2;
  168. filter: url(#highlight-border);
  169. }
  170. .flow-node__label {
  171. stroke:none;
  172. pointer-events:none;
  173. user-select:none;
  174. fill:white;
  175. }
  176. </style>