jellies-draw 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jellies-draw",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
4
4
  "description": "A drawer for jellies",
5
5
  "private": false,
6
6
  "main": "./src/index.js",
@@ -79,7 +79,8 @@ export default {
79
79
  width: 100%;
80
80
  height: 100%;
81
81
  }
82
- .container-penetrable {
82
+ .container-penetrable,
83
+ .container-penetrable * {
83
84
  pointer-events: none;
84
85
  }
85
86
  .cursor-crosshair {
@@ -26,7 +26,7 @@ export default {
26
26
  default: 'selector'
27
27
  },
28
28
  shortCut: {
29
- type: Number,
29
+ type: [Number, String],
30
30
  default: null
31
31
  },
32
32
  isActive: {
@@ -141,4 +141,14 @@ button {
141
141
  .tool-clear:before {
142
142
  content: "\e7a9";
143
143
  }
144
+ .tool-laser:before {
145
+ content: "";
146
+ display: inline-block;
147
+ width: 8px;
148
+ height: 8px;
149
+ border-radius: 50%;
150
+ background: #f33b29;
151
+ box-shadow: 0 0 4px #f33b29, 0 0 8px rgba(243, 59, 41, 0.6);
152
+ vertical-align: middle;
153
+ }
144
154
  </style>
@@ -42,6 +42,7 @@ export default {
42
42
  data() {
43
43
  return {
44
44
  tools: [
45
+ 'laser',
45
46
  'selector',
46
47
  'rectangle',
47
48
  'ellipse',
@@ -63,7 +64,8 @@ export default {
63
64
  toolsCanBeUsedContinuously: [
64
65
  'pen',
65
66
  'text',
66
- 'eraser'
67
+ 'eraser',
68
+ 'laser'
67
69
  ]
68
70
  }
69
71
  },
@@ -92,13 +94,8 @@ export default {
92
94
  Properties.latestTool = newTool;
93
95
  }
94
96
  },
95
- isCanvasPenetrable: {
96
- get() {
97
- return Properties.isCanvasPenetrable;
98
- },
99
- set(isCanvasPenetrable) {
100
- Properties.isCanvasPenetrable = isCanvasPenetrable;
101
- }
97
+ isCanvasPenetrable() {
98
+ return Properties.isCanvasPenetrable;
102
99
  }
103
100
  },
104
101
  watch: {
@@ -108,7 +105,10 @@ export default {
108
105
  },
109
106
  methods: {
110
107
  toolShortCut(index) {
111
- return Properties.isUsingText || Properties.isCanvasPenetrable ? null : (index + 1) % 10
108
+ if (Properties.isUsingText) return null
109
+ if (this.tools[index] === 'laser') return null
110
+ if (index === 10) return 0
111
+ return index
112
112
  },
113
113
  applyAction(action) {
114
114
  if (action === 'image') {
@@ -131,7 +131,6 @@ export default {
131
131
  }
132
132
  this.isCurrentToolLocked = this.toolsCanBeLocked.includes(action) && this.currentTool === action
133
133
  this.currentTool = action
134
- this.isCanvasPenetrable = this.currentTool === 'selector' && this.isCurrentToolLocked
135
134
  this.releaseInstantTool()
136
135
  },
137
136
  releaseInstantTool() {
@@ -148,12 +147,26 @@ export default {
148
147
  event.target.value = ''
149
148
  },
150
149
  handleShortCutKey(event) {
151
- if (this.hasShortCuts && this.currentTool !== 'text' && !this.isCanvasPenetrable) {
152
- const key = event.key
153
- const index = parseInt(key, 10)
154
- if (index >= 0 && index < this.tools.length) {
155
- this.applyAction(this.tools[(index + 9) % 10])
150
+ if (!this.hasShortCuts) return
151
+ if (this.currentTool === 'text') return
152
+ const tag = document.activeElement && document.activeElement.tagName
153
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || (document.activeElement && document.activeElement.isContentEditable)) return
154
+ if (event.key === '`') {
155
+ if (this.currentTool === 'laser') {
156
+ const fallback = this.latestTool.tool === 'laser' ? 'pen' : this.latestTool.tool
157
+ this.applyAction(fallback)
158
+ } else {
159
+ this.applyAction('laser')
156
160
  }
161
+ return
162
+ }
163
+ if (event.altKey || event.ctrlKey || event.metaKey) return
164
+ const key = event.key
165
+ const index = parseInt(key, 10)
166
+ if (isNaN(index)) return
167
+ const targetIdx = index === 0 ? 10 : index
168
+ if (targetIdx < this.tools.length) {
169
+ this.applyAction(this.tools[targetIdx])
157
170
  }
158
171
  }
159
172
  }
@@ -4,7 +4,9 @@ import Clipboard from './clipboard';
4
4
  import Properties from './properties';
5
5
  import Tools from './tools';
6
6
  import Histories from './histories';
7
+ import Laser from './laser';
7
8
  import AttachedText from './tools/helper/attachedText';
9
+ import Connection from './tools/helper/connection';
8
10
  export default {
9
11
  layer: null,
10
12
  stage: null,
@@ -30,6 +32,15 @@ export default {
30
32
  });
31
33
  this.layer = new Konva.Layer();
32
34
  this.stage.add(this.layer);
35
+ Laser.initialize(this.stage, this.container);
36
+ this.unwatchLaser = Properties.$watch(
37
+ () => Properties.tool === 'laser',
38
+ (isLaserMode) => {
39
+ if (isLaserMode) Laser.activate();
40
+ else Laser.deactivate();
41
+ },
42
+ { immediate: true }
43
+ );
33
44
  AttachedText.attachLayerListeners(this.layer);
34
45
  document.addEventListener('keydown', this.keydownHandler);
35
46
  document.addEventListener('keyup', this.keyupHandler);
@@ -43,6 +54,10 @@ export default {
43
54
  window.addEventListener('pointerup', this.handleGlobalPointerUp, { once: true });
44
55
  });
45
56
  this.stage.on('mousemove', this._handleMouseMove.bind(this));
57
+ this.containerLeaveHandler = () => {
58
+ if (!this.isDrawing) Connection.hideSnapVisuals();
59
+ };
60
+ this.stage.container().addEventListener('mouseleave', this.containerLeaveHandler);
46
61
  },
47
62
  generateEventHandlers() {
48
63
  this.resizeHandler = this._handleResize.bind(this);
@@ -74,6 +89,15 @@ export default {
74
89
  this.stage.off('click tap', this.clickHandler);
75
90
  document.removeEventListener('keydown', this.keydownHandler);
76
91
  document.removeEventListener('keyup', this.keyupHandler);
92
+ if (this.containerLeaveHandler) {
93
+ this.stage.container().removeEventListener('mouseleave', this.containerLeaveHandler);
94
+ }
95
+ if (this.unwatchLaser) {
96
+ this.unwatchLaser();
97
+ this.unwatchLaser = null;
98
+ }
99
+ Laser.destroy();
100
+ Connection.hideSnapVisuals();
77
101
  this.stage.clear()
78
102
  this.stage = null;
79
103
  window.removeEventListener('resize', this.resizeHandler);
@@ -102,12 +126,12 @@ export default {
102
126
  this._handlePropertiesShortCuts(event);
103
127
  }
104
128
  } else if (event.altKey) {
105
- Properties.isCanvasPenetrable = true;
129
+ Properties.isAltHeld = true;
106
130
  }
107
131
  },
108
132
  _handleKeyup(event) {
109
133
  if (event.key === 'Alt') {
110
- Properties.isCanvasPenetrable = false;
134
+ Properties.isAltHeld = false;
111
135
  if (Properties.tool === 'selector') {
112
136
  Properties.isToolLocked = false;
113
137
  Properties.latestTool = {
@@ -197,11 +221,14 @@ export default {
197
221
  },
198
222
  _handleMouseMove(e) {
199
223
  if (Properties.isUsingDrawingTool || Properties.isUsingText) {
224
+ if (Properties.tool === 'line' || Properties.tool === 'arrow') {
225
+ this._handleSnapHoverPreview();
226
+ }
200
227
  return;
201
228
  }
202
229
 
203
230
  const target = e.target;
204
-
231
+
205
232
  if (target === this.stage) {
206
233
  this.stage.container().style.cursor = 'default';
207
234
  return;
@@ -210,9 +237,9 @@ export default {
210
237
  if (target.hasName('node')) {
211
238
  const mousePos = this.stage.getPointerPosition();
212
239
  const canInteract = this._canInteractWithNode(target, mousePos);
213
-
240
+
214
241
  this.stage.container().style.cursor = canInteract ? 'move' : 'default';
215
-
242
+
216
243
  if (!canInteract) {
217
244
  target.draggable(false);
218
245
  } else if (!Properties.isUsingDrawingTool) {
@@ -220,6 +247,17 @@ export default {
220
247
  }
221
248
  }
222
249
  },
250
+ _handleSnapHoverPreview() {
251
+ if (this.isDrawing) return;
252
+ const pos = this.stage.getPointerPosition();
253
+ if (!pos) return;
254
+ const target = Connection.findSnapTarget(pos);
255
+ if (target) {
256
+ Connection.showSnapVisuals(target);
257
+ } else {
258
+ Connection.hideSnapVisuals();
259
+ }
260
+ },
223
261
  _canInteractWithNode(node, mousePos) {
224
262
  const selectedNodes = Transformer._nodes();
225
263
  if (selectedNodes.includes(node)) {
@@ -2,6 +2,7 @@ import Canvas from './canvas';
2
2
  import Transformer from './transformer';
3
3
  import Tools from './tools';
4
4
  import Histories from './histories';
5
+ import Connection from './tools/helper/connection';
5
6
  export default {
6
7
  copiedNodes: [],
7
8
  copy() {
@@ -13,6 +14,21 @@ export default {
13
14
  },
14
15
  paste() {
15
16
  if (this.copiedNodes && this.copiedNodes.length > 0) {
17
+ const idMap = {};
18
+ this.copiedNodes.forEach((node) => {
19
+ if (node.attrs.nodeId) {
20
+ const oldId = node.attrs.nodeId;
21
+ node.setAttr('nodeId', undefined);
22
+ Connection.ensureNodeId(node);
23
+ idMap[oldId] = node.attrs.nodeId;
24
+ }
25
+ });
26
+ this.copiedNodes.forEach((node) => {
27
+ if (node.className === 'Line' || node.className === 'Arrow') {
28
+ this._remapLineAttachment(node, 'start', idMap);
29
+ this._remapLineAttachment(node, 'end', idMap);
30
+ }
31
+ });
16
32
  this.copiedNodes.forEach((node) => {
17
33
  node.x(node.x() + 10);
18
34
  node.y(node.y() + 10);
@@ -24,6 +40,17 @@ export default {
24
40
  Histories.record();
25
41
  }
26
42
  },
43
+ _remapLineAttachment(line, end, idMap) {
44
+ const oldId = line.attrs[`${end}AttachedTo`];
45
+ if (!oldId) return;
46
+ if (idMap[oldId]) {
47
+ line.setAttr(`${end}AttachedTo`, idMap[oldId]);
48
+ } else {
49
+ line.setAttr(`${end}AttachedTo`, undefined);
50
+ line.setAttr(`${end}AttachedLocalX`, undefined);
51
+ line.setAttr(`${end}AttachedLocalY`, undefined);
52
+ }
53
+ },
27
54
  _addEventsToCopiedNode(node) {
28
55
  const className = node.getClassName();
29
56
  if (className === 'Text') {
@@ -0,0 +1,188 @@
1
+ import Konva from 'konva';
2
+
3
+ const FADE_DURATION = 700;
4
+ const LASER_WIDTH = 4;
5
+ const DRAG_THRESHOLD = 2;
6
+ const GLOW_COLOR = '243, 59, 41';
7
+ const CORE_COLOR = '255, 110, 90';
8
+
9
+ export default {
10
+ stage: null,
11
+ container: null,
12
+ layer: null,
13
+ shape: null,
14
+ segments: [],
15
+ isPressed: false,
16
+ hasDragged: false,
17
+ startX: 0,
18
+ startY: 0,
19
+ lastX: 0,
20
+ lastY: 0,
21
+ rafId: null,
22
+ active: false,
23
+ _onMouseDown: null,
24
+ _onMouseMove: null,
25
+ _onMouseUp: null,
26
+ _onTick: null,
27
+
28
+ initialize(stage, container) {
29
+ this.stage = stage;
30
+ this.container = container;
31
+ this._onMouseDown = this._handleMouseDown.bind(this);
32
+ this._onMouseMove = this._handleMouseMove.bind(this);
33
+ this._onMouseUp = this._handleMouseUp.bind(this);
34
+ this._onTick = this._tick.bind(this);
35
+ this.layer = new Konva.Layer({ listening: false });
36
+ this.shape = new Konva.Shape({
37
+ listening: false,
38
+ sceneFunc: (ctx) => this._renderSegments(ctx)
39
+ });
40
+ this.layer.add(this.shape);
41
+ this.layer.hide();
42
+ stage.add(this.layer);
43
+ },
44
+
45
+ destroy() {
46
+ this.deactivate();
47
+ if (this.layer) {
48
+ this.layer.destroy();
49
+ this.layer = null;
50
+ this.shape = null;
51
+ }
52
+ this.stage = null;
53
+ this.container = null;
54
+ },
55
+
56
+ activate() {
57
+ if (this.active || !this.layer) return;
58
+ this.active = true;
59
+ this.layer.show();
60
+ this.layer.moveToTop();
61
+ window.addEventListener('pointerdown', this._onMouseDown, true);
62
+ window.addEventListener('pointermove', this._onMouseMove, true);
63
+ window.addEventListener('pointerup', this._onMouseUp, true);
64
+ window.addEventListener('pointercancel', this._onMouseUp, true);
65
+ this.rafId = requestAnimationFrame(this._onTick);
66
+ },
67
+
68
+ deactivate() {
69
+ if (!this.active) return;
70
+ this.active = false;
71
+ window.removeEventListener('pointerdown', this._onMouseDown, true);
72
+ window.removeEventListener('pointermove', this._onMouseMove, true);
73
+ window.removeEventListener('pointerup', this._onMouseUp, true);
74
+ window.removeEventListener('pointercancel', this._onMouseUp, true);
75
+ if (this.rafId !== null) {
76
+ cancelAnimationFrame(this.rafId);
77
+ this.rafId = null;
78
+ }
79
+ this.segments = [];
80
+ this.isPressed = false;
81
+ this.hasDragged = false;
82
+ if (this.layer) {
83
+ this.layer.hide();
84
+ this.layer.batchDraw();
85
+ }
86
+ },
87
+
88
+ _isWithinContainer(clientX, clientY) {
89
+ if (!this.container) return false;
90
+ const rect = this.container.getBoundingClientRect();
91
+ if (rect.width === 0 || rect.height === 0) return false;
92
+ return clientX >= rect.left && clientX <= rect.right &&
93
+ clientY >= rect.top && clientY <= rect.bottom;
94
+ },
95
+
96
+ _toStageCoords(clientX, clientY) {
97
+ const rect = this.container.getBoundingClientRect();
98
+ return { x: clientX - rect.left, y: clientY - rect.top };
99
+ },
100
+
101
+ _handleMouseDown(e) {
102
+ if (e.button !== 0) return;
103
+ if (!this._isWithinContainer(e.clientX, e.clientY)) return;
104
+ const pt = this._toStageCoords(e.clientX, e.clientY);
105
+ this.isPressed = true;
106
+ this.hasDragged = false;
107
+ this.startX = pt.x;
108
+ this.startY = pt.y;
109
+ this.lastX = pt.x;
110
+ this.lastY = pt.y;
111
+ },
112
+
113
+ _handleMouseMove(e) {
114
+ if ((e.buttons & 1) === 0) {
115
+ this.isPressed = false;
116
+ this.hasDragged = false;
117
+ return;
118
+ }
119
+ if (!this.isPressed) return;
120
+ const pt = this._toStageCoords(e.clientX, e.clientY);
121
+ if (!this.hasDragged) {
122
+ const dx = pt.x - this.startX;
123
+ const dy = pt.y - this.startY;
124
+ if (dx * dx + dy * dy < DRAG_THRESHOLD * DRAG_THRESHOLD) return;
125
+ this.hasDragged = true;
126
+ this.segments.push({
127
+ x1: this.startX, y1: this.startY,
128
+ x2: pt.x, y2: pt.y,
129
+ time: performance.now()
130
+ });
131
+ } else {
132
+ this.segments.push({
133
+ x1: this.lastX, y1: this.lastY,
134
+ x2: pt.x, y2: pt.y,
135
+ time: performance.now()
136
+ });
137
+ }
138
+ this.lastX = pt.x;
139
+ this.lastY = pt.y;
140
+ },
141
+
142
+ _handleMouseUp() {
143
+ this.isPressed = false;
144
+ this.hasDragged = false;
145
+ },
146
+
147
+ _tick() {
148
+ if (!this.active) return;
149
+ const now = performance.now();
150
+ const initialLength = this.segments.length;
151
+ while (this.segments.length && now - this.segments[0].time > FADE_DURATION) {
152
+ this.segments.shift();
153
+ }
154
+ if (this.segments.length > 0 || initialLength > 0) {
155
+ this.layer.batchDraw();
156
+ }
157
+ this.rafId = requestAnimationFrame(this._onTick);
158
+ },
159
+
160
+ _renderSegments(ctx) {
161
+ const segs = this.segments;
162
+ if (segs.length === 0) return;
163
+ const now = performance.now();
164
+ const newestAge = (now - segs[segs.length - 1].time) / FADE_DURATION;
165
+ const opacity = Math.max(0, 1 - newestAge);
166
+ if (opacity <= 0) return;
167
+
168
+ ctx.save();
169
+ ctx.lineCap = 'round';
170
+ ctx.lineJoin = 'round';
171
+
172
+ ctx.beginPath();
173
+ ctx.moveTo(segs[0].x1, segs[0].y1);
174
+ for (let i = 0; i < segs.length; i++) {
175
+ ctx.lineTo(segs[i].x2, segs[i].y2);
176
+ }
177
+
178
+ ctx.strokeStyle = `rgba(${GLOW_COLOR}, ${opacity * 0.3})`;
179
+ ctx.lineWidth = LASER_WIDTH * 2.5;
180
+ ctx.stroke();
181
+
182
+ ctx.strokeStyle = `rgba(${CORE_COLOR}, ${opacity})`;
183
+ ctx.lineWidth = LASER_WIDTH;
184
+ ctx.stroke();
185
+
186
+ ctx.restore();
187
+ }
188
+ };
@@ -1,6 +1,7 @@
1
1
  import Vue from 'vue'
2
2
  import Canvas from './canvas'
3
3
  import Transformer from './transformer'
4
+ import Connection from './tools/helper/connection'
4
5
 
5
6
  export default new Vue({
6
7
  data() {
@@ -17,7 +18,7 @@ export default new Vue({
17
18
  isTextNodesSelected: false,
18
19
  isMassivelyAssigningProperties: false,
19
20
  isToolLocked: false,
20
- isCanvasPenetrable: false,
21
+ isAltHeld: false,
21
22
  fontSizeOptions: {
22
23
  15: 'S',
23
24
  20: 'M',
@@ -105,14 +106,19 @@ export default new Vue({
105
106
  return this.tool === 'text' || this.isTextNodesSelected;
106
107
  },
107
108
  isUsingDrawingTool() {
108
- return (this.tool !== 'selector');
109
+ return this.tool !== 'selector' && this.tool !== 'laser';
110
+ },
111
+ isCanvasPenetrable() {
112
+ return this.tool === 'laser' ||
113
+ this.isAltHeld ||
114
+ (this.tool === 'selector' && this.isToolLocked);
109
115
  }
110
116
  },
111
117
  watch: {
112
- toolSelected() {
118
+ toolSelected(newTool) {
113
119
  this.refreshNodesStatus()
114
- if (this.tool === 'selector' && this.isToolLocked) {
115
- this.isCanvasPenetrable = true;
120
+ if (newTool !== 'line' && newTool !== 'arrow') {
121
+ Connection.hideSnapVisuals()
116
122
  }
117
123
  }
118
124
  },
@@ -4,21 +4,29 @@ import Canvas from '../canvas'
4
4
  import Histories from '../histories'
5
5
  import AngleSnap from './helper/angleSnap'
6
6
  import AttachedText from './helper/attachedText'
7
+ import Connection from './helper/connection'
7
8
 
8
9
  export default {
9
10
  temporalShape: null,
10
11
  startX: 0,
11
12
  startY: 0,
13
+ startSnap: null,
14
+ endSnap: null,
12
15
  show({ offsetX, offsetY }) {
13
16
  Canvas.isDrawing = true;
14
- this.startX = offsetX
15
- this.startY = offsetY
17
+ const startSnap = Connection.findSnapTarget({ x: offsetX, y: offsetY })
18
+ const startX = startSnap ? startSnap.worldX : offsetX
19
+ const startY = startSnap ? startSnap.worldY : offsetY
20
+ this.startX = startX
21
+ this.startY = startY
22
+ this.startSnap = startSnap
23
+ this.endSnap = null
16
24
  const strokeWidth = Properties.strokeWidth
17
25
  const pointerWidth = Math.pow(Properties.strokeWidth, 2) / 2
18
26
  const pointerLength = Math.pow(Properties.strokeWidth, 2) / 2
19
27
  const stroke = Properties.strokeColor
20
- const newArrow = this.generate({
21
- points: [offsetX, offsetY, offsetX, offsetY],
28
+ const newArrow = this.generate({
29
+ points: [startX, startY, startX, startY],
22
30
  stroke,
23
31
  strokeWidth,
24
32
  pointerWidth,
@@ -26,25 +34,47 @@ export default {
26
34
  })
27
35
  this.temporalShape = newArrow
28
36
  Canvas.layer.add(this.temporalShape)
37
+ Connection.hideSnapVisuals()
29
38
  },
30
39
  change({ offsetX, offsetY, shiftKey }) {
31
- if (Canvas.isDrawing) {
32
- const endPoint = AngleSnap.getSnappedEndPoint(
33
- this.startX,
34
- this.startY,
35
- offsetX,
36
- offsetY,
37
- shiftKey
38
- );
39
- this.temporalShape.points([this.startX, this.startY, endPoint.x, endPoint.y]);
40
+ if (!Canvas.isDrawing) return
41
+ const endPoint = AngleSnap.getSnappedEndPoint(
42
+ this.startX,
43
+ this.startY,
44
+ offsetX,
45
+ offsetY,
46
+ shiftKey
47
+ );
48
+ let endX = endPoint.x
49
+ let endY = endPoint.y
50
+ const endSnap = Connection.findSnapTarget({ x: endX, y: endY }, this.temporalShape, 'end')
51
+ if (endSnap) {
52
+ endX = endSnap.worldX
53
+ endY = endSnap.worldY
54
+ Connection.showSnapVisuals(endSnap)
55
+ } else {
56
+ Connection.hideSnapVisuals()
40
57
  }
58
+ this.endSnap = endSnap
59
+ this.temporalShape.points([this.startX, this.startY, endX, endY]);
41
60
  },
42
61
  finish() {
62
+ Connection.hideSnapVisuals()
63
+ if (this.temporalShape) {
64
+ if (this.startSnap) {
65
+ Connection.attachLineEndpoint(this.temporalShape, 'start', this.startSnap)
66
+ }
67
+ if (this.endSnap) {
68
+ Connection.attachLineEndpoint(this.temporalShape, 'end', this.endSnap)
69
+ }
70
+ }
43
71
  if (!Properties.isToolLocked) {
44
72
  Properties.tool = Properties.latestTool.tool
45
73
  Properties.isToolLocked = Properties.latestTool.isToolLocked
46
74
  }
47
75
  this.temporalShape = null
76
+ this.startSnap = null
77
+ this.endSnap = null
48
78
  },
49
79
  bindEvents(newArrow) {
50
80
  newArrow.off('transform')
@@ -60,8 +90,9 @@ export default {
60
90
  newArrow.on('transformend', Histories.record)
61
91
  newArrow.on('dragend', Histories.record)
62
92
  AttachedText.bindShapeEvents(newArrow)
93
+ Connection.bindLineListeners(newArrow)
63
94
  },
64
- generate({ x, y, scaleX, scaleY, skewX, rotation, points, stroke, strokeWidth, pointerWidth, pointerLength, attachedText, attachedFontSize, attachedFill }) {
95
+ generate({ x, y, scaleX, scaleY, skewX, rotation, points, stroke, strokeWidth, pointerWidth, pointerLength, attachedText, attachedFontSize, attachedFill, startAttachedTo, startAttachedLocalX, startAttachedLocalY, endAttachedTo, endAttachedLocalX, endAttachedLocalY }) {
65
96
  const newArrow = new Konva.Arrow({
66
97
  x,
67
98
  y,
@@ -77,6 +108,12 @@ export default {
77
108
  attachedText,
78
109
  attachedFontSize,
79
110
  attachedFill,
111
+ startAttachedTo,
112
+ startAttachedLocalX,
113
+ startAttachedLocalY,
114
+ endAttachedTo,
115
+ endAttachedLocalX,
116
+ endAttachedLocalY,
80
117
  name: 'node',
81
118
  nodeType: 'arrow',
82
119
  lineCap: 'round',
@@ -4,6 +4,7 @@ import Canvas from '../canvas'
4
4
  import Histories from '../histories'
5
5
  import ShapeConstraint from './helper/shapeConstraint'
6
6
  import AttachedText from './helper/attachedText'
7
+ import Connection from './helper/connection'
7
8
 
8
9
  export default {
9
10
  temporalShape: null,
@@ -60,7 +61,7 @@ export default {
60
61
  }
61
62
  }, 200)
62
63
  },
63
- generate({ x, y, radiusX, radiusY, scaleX, scaleY, skewX, rotation, fill, stroke, strokeWidth, attachedText, attachedFontSize, attachedFill }) {
64
+ generate({ x, y, radiusX, radiusY, scaleX, scaleY, skewX, rotation, fill, stroke, strokeWidth, attachedText, attachedFontSize, attachedFill, nodeId }) {
64
65
  const newEllipse = new Konva.Ellipse({
65
66
  x,
66
67
  y,
@@ -76,10 +77,12 @@ export default {
76
77
  attachedText,
77
78
  attachedFontSize,
78
79
  attachedFill,
80
+ nodeId,
79
81
  name: 'node',
80
82
  nodeType: 'ellipse',
81
83
  strokeScaleEnabled: false
82
84
  })
85
+ Connection.ensureNodeId(newEllipse)
83
86
  this.bindEvents(newEllipse)
84
87
  return newEllipse
85
88
  },
@@ -89,5 +92,6 @@ export default {
89
92
  newEllipse.on('transformend', Histories.record)
90
93
  newEllipse.on('dragend', Histories.record)
91
94
  AttachedText.bindShapeEvents(newEllipse)
95
+ Connection.bindShapeListeners(newEllipse)
92
96
  }
93
97
  }