@operato/image-marker 8.0.0-alpha.18 → 8.0.0-alpha.23
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/CHANGELOG.md +18 -0
- package/dist/src/base-image-marker.d.ts +19 -4
- package/dist/src/base-image-marker.js +116 -15
- package/dist/src/base-image-marker.js.map +1 -1
- package/dist/src/ox-image-marker-mode.d.ts +1 -0
- package/dist/src/ox-image-marker-mode.js +12 -2
- package/dist/src/ox-image-marker-mode.js.map +1 -1
- package/dist/src/ox-image-marker-view.js +3 -2
- package/dist/src/ox-image-marker-view.js.map +1 -1
- package/dist/src/ox-image-marker.d.ts +7 -6
- package/dist/src/ox-image-marker.js +165 -71
- package/dist/src/ox-image-marker.js.map +1 -1
- package/dist/stories/ox-image-marker.stories.js +16 -0
- package/dist/stories/ox-image-marker.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/base-image-marker.ts +143 -18
- package/src/ox-image-marker-mode.ts +15 -2
- package/src/ox-image-marker-view.ts +3 -2
- package/src/ox-image-marker.ts +193 -80
- package/stories/ox-image-marker.stories.ts +16 -0
package/src/ox-image-marker.ts
CHANGED
|
@@ -52,28 +52,15 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
52
52
|
|
|
53
53
|
connectedCallback() {
|
|
54
54
|
super.connectedCallback()
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
this.setAttribute('tabindex', '0')
|
|
56
|
+
this.addEventListener('focus', this.onFocus)
|
|
57
|
+
this.addEventListener('blur', this.onBlur)
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
disconnectedCallback() {
|
|
60
61
|
super.disconnectedCallback()
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
private handleKeyDown = (event: KeyboardEvent) => {
|
|
66
|
-
if ((event.key === 'Delete' || event.key === 'Backspace') && this.selectedShape) {
|
|
67
|
-
this.deleteSelectedShape()
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private deleteSelectedShape() {
|
|
72
|
-
if (this.selectedShape) {
|
|
73
|
-
this.shapes = this.shapes.filter(s => s.id !== this.selectedShape!.id)
|
|
74
|
-
this.selectedShape = null
|
|
75
|
-
this.requestUpdate()
|
|
76
|
-
}
|
|
62
|
+
this.removeEventListener('focus', this.onFocus)
|
|
63
|
+
this.removeEventListener('blur', this.onBlur)
|
|
77
64
|
}
|
|
78
65
|
|
|
79
66
|
render() {
|
|
@@ -81,8 +68,9 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
81
68
|
<div class="container">
|
|
82
69
|
<svg
|
|
83
70
|
@mousedown=${this.onSvgMouseDown}
|
|
84
|
-
@
|
|
85
|
-
@
|
|
71
|
+
@touchstart=${this.onSvgTouchStart}
|
|
72
|
+
@wheel=${this.onSvgWheel}
|
|
73
|
+
@dblclick=${this.onSvgDoubleClick}
|
|
86
74
|
viewBox=${this.viewBox}
|
|
87
75
|
>
|
|
88
76
|
<image href=${this.imageUrl} @load=${this.onImageLoad}></image>
|
|
@@ -97,8 +85,9 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
97
85
|
const { mode, shapeType } = e.detail
|
|
98
86
|
this.setMode(mode, shapeType)
|
|
99
87
|
}}
|
|
100
|
-
@
|
|
101
|
-
@
|
|
88
|
+
@tap-fit-size=${() => this.resetZoom()}
|
|
89
|
+
@tap-delete=${() => this.deleteSelectedShape()}
|
|
90
|
+
@tap-save=${() => {
|
|
102
91
|
this.dispatchEvent(
|
|
103
92
|
new CustomEvent('shapes-changed', {
|
|
104
93
|
detail: this.shapes,
|
|
@@ -112,40 +101,44 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
112
101
|
}
|
|
113
102
|
|
|
114
103
|
protected onSvgMouseDown(event: MouseEvent) {
|
|
115
|
-
|
|
104
|
+
event.preventDefault()
|
|
116
105
|
|
|
117
106
|
this.selectedShape = null
|
|
118
107
|
this.shapes = this.shapes.map(s => {
|
|
119
108
|
return { ...s, selected: false }
|
|
120
109
|
})
|
|
121
110
|
|
|
122
|
-
this.selectedShape = null
|
|
123
111
|
if (this.currentMode === 'move') {
|
|
124
|
-
this.
|
|
125
|
-
this.panStart = { x: event.clientX, y: event.clientY }
|
|
126
|
-
svg.classList.add('dragging')
|
|
127
|
-
svg.addEventListener('mousemove', this.onPanMouseMove)
|
|
128
|
-
svg.addEventListener('mouseup', this.onPanMouseUp)
|
|
129
|
-
} else if (this.currentMode === 'select') {
|
|
130
|
-
this.startShapeDragging(event)
|
|
131
|
-
svg.addEventListener('mousemove', this.onShapeMouseMove)
|
|
132
|
-
svg.addEventListener('mouseup', this.onShapeMouseUp)
|
|
112
|
+
this.startPanning(event)
|
|
133
113
|
} else if (this.currentMode === 'draw') {
|
|
134
114
|
this.startDrawing(event)
|
|
135
|
-
svg.addEventListener('mousemove', this.onMouseMove)
|
|
136
|
-
svg.addEventListener('mouseup', this.onMouseUp)
|
|
137
115
|
}
|
|
138
116
|
}
|
|
139
117
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.
|
|
118
|
+
protected onSvgTouchStart = (event: TouchEvent) => {
|
|
119
|
+
event.preventDefault()
|
|
120
|
+
|
|
121
|
+
if (this.isDoubleTouch(event)) {
|
|
122
|
+
this.resetZoom()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (event.touches.length === 1) {
|
|
126
|
+
this.selectedShape = null
|
|
127
|
+
this.shapes = this.shapes.map(s => {
|
|
128
|
+
return { ...s, selected: false }
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
if (this.currentMode === 'move') {
|
|
132
|
+
this.startPanning(event)
|
|
133
|
+
} else if (this.currentMode === 'draw') {
|
|
134
|
+
this.startDrawing(event)
|
|
135
|
+
}
|
|
136
|
+
} else if (event.touches.length === 2) {
|
|
137
|
+
this.startPinchZooming(event)
|
|
145
138
|
}
|
|
146
139
|
}
|
|
147
140
|
|
|
148
|
-
private startDrawing(event: MouseEvent) {
|
|
141
|
+
private startDrawing(event: MouseEvent | TouchEvent) {
|
|
149
142
|
const { x, y } = this.getSvgPoint(event)
|
|
150
143
|
|
|
151
144
|
if (this.currentShapeType === 'text') {
|
|
@@ -165,6 +158,14 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
165
158
|
this.startY = y
|
|
166
159
|
this.drawing = true
|
|
167
160
|
}
|
|
161
|
+
|
|
162
|
+
if ('touches' in event) {
|
|
163
|
+
this.svg.addEventListener('touchmove', this.onDrawingMouseMove)
|
|
164
|
+
this.svg.addEventListener('touchend', this.onDrawingMouseUp)
|
|
165
|
+
} else {
|
|
166
|
+
this.svg.addEventListener('mousemove', this.onDrawingMouseMove)
|
|
167
|
+
this.svg.addEventListener('mouseup', this.onDrawingMouseUp)
|
|
168
|
+
}
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
private setMode(mode: ImageMarkerMode, shapeType: ShapeType) {
|
|
@@ -172,8 +173,12 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
172
173
|
this.currentShapeType = shapeType
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
private
|
|
176
|
-
if (!this.drawing)
|
|
176
|
+
private onDrawingMouseMove = (event: MouseEvent | TouchEvent) => {
|
|
177
|
+
if (!this.drawing) {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
event.preventDefault()
|
|
177
182
|
|
|
178
183
|
const { x, y } = this.getSvgPoint(event)
|
|
179
184
|
|
|
@@ -273,11 +278,20 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
private
|
|
281
|
+
private onDrawingMouseUp = (event: MouseEvent | TouchEvent) => {
|
|
282
|
+
event.preventDefault()
|
|
283
|
+
|
|
277
284
|
this.drawing = false
|
|
285
|
+
|
|
278
286
|
const svg = this.svg as SVGSVGElement
|
|
279
|
-
|
|
280
|
-
|
|
287
|
+
|
|
288
|
+
if ('touches' in event) {
|
|
289
|
+
svg.removeEventListener('touchmove', this.onDrawingMouseMove)
|
|
290
|
+
svg.removeEventListener('touchend', this.onDrawingMouseUp)
|
|
291
|
+
} else {
|
|
292
|
+
svg.removeEventListener('mousemove', this.onDrawingMouseMove)
|
|
293
|
+
svg.removeEventListener('mouseup', this.onDrawingMouseUp)
|
|
294
|
+
}
|
|
281
295
|
|
|
282
296
|
if (this.selectedShape) {
|
|
283
297
|
this.selectedShape.selected = true
|
|
@@ -286,12 +300,15 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
286
300
|
this.requestUpdate()
|
|
287
301
|
}
|
|
288
302
|
|
|
289
|
-
private getSvgPoint(event: MouseEvent) {
|
|
303
|
+
private getSvgPoint(event: MouseEvent | TouchEvent) {
|
|
304
|
+
const { x, y } = this.getPointFromEvent(event)
|
|
305
|
+
|
|
290
306
|
const svg = this.svg
|
|
291
307
|
const point = svg.createSVGPoint()
|
|
292
|
-
point.x =
|
|
293
|
-
point.y =
|
|
308
|
+
point.x = x
|
|
309
|
+
point.y = y
|
|
294
310
|
const ctm = svg.getScreenCTM()!.inverse()
|
|
311
|
+
|
|
295
312
|
return point.matrixTransform(ctm)
|
|
296
313
|
}
|
|
297
314
|
|
|
@@ -312,6 +329,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
312
329
|
fill="${commonAttrs.stroke}"
|
|
313
330
|
style="cursor: ${commonAttrs.cursor}; font-size: 16px;"
|
|
314
331
|
@mousedown="${(e: MouseEvent) => this.onShapeMouseDown(e, shape)}"
|
|
332
|
+
@touchstart="${(e: TouchEvent) => this.onShapeMouseDown(e, shape)}"
|
|
315
333
|
@dblclick="${(e: MouseEvent) => this.onTextDoubleClick(e, shape)}"
|
|
316
334
|
>${shape.text}</text>
|
|
317
335
|
`
|
|
@@ -328,6 +346,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
328
346
|
fill="${commonAttrs.fill}"
|
|
329
347
|
style="cursor: ${commonAttrs.cursor}"
|
|
330
348
|
@mousedown="${(e: MouseEvent) => this.onShapeMouseDown(e, shape)}"
|
|
349
|
+
@touchstart="${(e: TouchEvent) => this.onShapeMouseDown(e, shape)}"
|
|
331
350
|
></rect>
|
|
332
351
|
${shape.selected ? this.renderResizeHandles(shape) : ''}
|
|
333
352
|
`
|
|
@@ -343,6 +362,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
343
362
|
fill="${commonAttrs.fill}"
|
|
344
363
|
style="cursor: ${commonAttrs.cursor}"
|
|
345
364
|
@mousedown="${(e: MouseEvent) => this.onShapeMouseDown(e, shape)}"
|
|
365
|
+
@touchstart="${(e: TouchEvent) => this.onShapeMouseDown(e, shape)}"
|
|
346
366
|
></circle>
|
|
347
367
|
${shape.selected ? this.renderResizeHandles(shape) : ''}
|
|
348
368
|
`
|
|
@@ -356,6 +376,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
356
376
|
fill="${commonAttrs.fill}"
|
|
357
377
|
style="cursor: ${commonAttrs.cursor}"
|
|
358
378
|
@mousedown="${(e: MouseEvent) => this.onShapeMouseDown(e, shape)}"
|
|
379
|
+
@touchstart="${(e: TouchEvent) => this.onShapeMouseDown(e, shape)}"
|
|
359
380
|
></polygon>
|
|
360
381
|
${shape.selected ? this.renderResizeHandles(shape) : ''}
|
|
361
382
|
`
|
|
@@ -371,6 +392,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
371
392
|
stroke-width="${commonAttrs['stroke-width']}"
|
|
372
393
|
style="cursor: ${commonAttrs.cursor}"
|
|
373
394
|
@mousedown="${(e: MouseEvent) => this.onShapeMouseDown(e, shape)}"
|
|
395
|
+
@touchstart="${(e: TouchEvent) => this.onShapeMouseDown(e, shape)}"
|
|
374
396
|
></line>
|
|
375
397
|
${shape.selected ? this.renderResizeHandles(shape) : ''}
|
|
376
398
|
`
|
|
@@ -392,6 +414,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
392
414
|
marker-end="url(#arrowhead-${shape.id})"
|
|
393
415
|
style="cursor: ${commonAttrs.cursor}"
|
|
394
416
|
@mousedown="${(e: MouseEvent) => this.onShapeMouseDown(e, shape)}"
|
|
417
|
+
@touchstart="${(e: TouchEvent) => this.onShapeMouseDown(e, shape)}"
|
|
395
418
|
></line>
|
|
396
419
|
${shape.selected ? this.renderResizeHandles(shape) : ''}
|
|
397
420
|
`
|
|
@@ -401,8 +424,9 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
401
424
|
}
|
|
402
425
|
}
|
|
403
426
|
|
|
404
|
-
private onShapeMouseDown(event: MouseEvent, shape: Shape) {
|
|
427
|
+
private onShapeMouseDown(event: MouseEvent | TouchEvent, shape: Shape) {
|
|
405
428
|
event.stopPropagation()
|
|
429
|
+
event.preventDefault()
|
|
406
430
|
|
|
407
431
|
this.shapes = this.shapes.map(s => ({
|
|
408
432
|
...s,
|
|
@@ -438,12 +462,22 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
438
462
|
}
|
|
439
463
|
|
|
440
464
|
const svg = this.svg as SVGSVGElement
|
|
441
|
-
|
|
442
|
-
|
|
465
|
+
|
|
466
|
+
if ('touches' in event) {
|
|
467
|
+
svg.addEventListener('touchmove', this.onShapeMouseMove)
|
|
468
|
+
svg.addEventListener('touchend', this.onShapeMouseUp)
|
|
469
|
+
} else {
|
|
470
|
+
svg.addEventListener('mousemove', this.onShapeMouseMove)
|
|
471
|
+
svg.addEventListener('mouseup', this.onShapeMouseUp)
|
|
472
|
+
}
|
|
443
473
|
}
|
|
444
474
|
|
|
445
|
-
private onShapeMouseMove = (event: MouseEvent) => {
|
|
446
|
-
if (!this.selectedShape)
|
|
475
|
+
private onShapeMouseMove = (event: MouseEvent | TouchEvent) => {
|
|
476
|
+
if (!this.selectedShape) {
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
event.preventDefault()
|
|
447
481
|
|
|
448
482
|
const { x, y } = this.getSvgPoint(event)
|
|
449
483
|
|
|
@@ -481,21 +515,18 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
481
515
|
this.requestUpdate()
|
|
482
516
|
}
|
|
483
517
|
|
|
484
|
-
private onShapeMouseUp = () => {
|
|
485
|
-
|
|
486
|
-
svg.removeEventListener('mousemove', this.onShapeMouseMove)
|
|
487
|
-
svg.removeEventListener('mouseup', this.onShapeMouseUp)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
private getScale(): number {
|
|
491
|
-
if (!this.svg) return 1
|
|
492
|
-
|
|
493
|
-
const [vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)
|
|
518
|
+
private onShapeMouseUp = (event: MouseEvent | TouchEvent) => {
|
|
519
|
+
event.preventDefault()
|
|
494
520
|
|
|
495
|
-
const
|
|
496
|
-
const scaleY = vbHeight / this.svg.clientHeight
|
|
521
|
+
const svg = this.svg as SVGSVGElement
|
|
497
522
|
|
|
498
|
-
|
|
523
|
+
if ('touches' in event) {
|
|
524
|
+
svg.removeEventListener('touchmove', this.onShapeMouseMove)
|
|
525
|
+
svg.removeEventListener('touchend', this.onShapeMouseUp)
|
|
526
|
+
} else {
|
|
527
|
+
svg.removeEventListener('mousemove', this.onShapeMouseMove)
|
|
528
|
+
svg.removeEventListener('mouseup', this.onShapeMouseUp)
|
|
529
|
+
}
|
|
499
530
|
}
|
|
500
531
|
|
|
501
532
|
private renderResizeHandles(shape: Shape) {
|
|
@@ -549,6 +580,7 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
549
580
|
class="resize-handle"
|
|
550
581
|
style="cursor: ${handle.cursor};"
|
|
551
582
|
@mousedown="${(e: MouseEvent) => this.onHandleMouseDown(e, shape, handle)}"
|
|
583
|
+
@touchstart="${(e: TouchEvent) => this.onHandleMouseDown(e, shape, handle)}"
|
|
552
584
|
></rect>
|
|
553
585
|
</g>
|
|
554
586
|
`
|
|
@@ -556,27 +588,76 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
556
588
|
`
|
|
557
589
|
}
|
|
558
590
|
|
|
559
|
-
private onHandleMouseDown(event: MouseEvent, shape: Shape, handle: any) {
|
|
591
|
+
private onHandleMouseDown(event: MouseEvent | TouchEvent, shape: Shape, handle: any) {
|
|
560
592
|
event.stopPropagation()
|
|
593
|
+
event.preventDefault()
|
|
594
|
+
|
|
561
595
|
this.selectedShape = shape
|
|
562
596
|
this.offsetX = handle.x
|
|
563
597
|
this.offsetY = handle.y
|
|
564
598
|
const svg = this.svg as SVGSVGElement
|
|
565
|
-
|
|
566
|
-
|
|
599
|
+
|
|
600
|
+
if ('touches' in event) {
|
|
601
|
+
svg.addEventListener('touchmove', this.onHandleMouseMove)
|
|
602
|
+
svg.addEventListener('touchend', this.onHandleMouseUp)
|
|
603
|
+
} else {
|
|
604
|
+
svg.addEventListener('mousemove', this.onHandleMouseMove)
|
|
605
|
+
svg.addEventListener('mouseup', this.onHandleMouseUp)
|
|
606
|
+
}
|
|
567
607
|
}
|
|
568
608
|
|
|
569
|
-
private onHandleMouseMove = (event: MouseEvent) => {
|
|
570
|
-
if (!this.selectedShape)
|
|
609
|
+
private onHandleMouseMove = (event: MouseEvent | TouchEvent) => {
|
|
610
|
+
if (!this.selectedShape) {
|
|
611
|
+
return
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
event.preventDefault()
|
|
615
|
+
|
|
571
616
|
const { x, y } = this.getSvgPoint(event)
|
|
572
617
|
|
|
573
618
|
switch (this.selectedShape.type) {
|
|
574
|
-
case 'text':
|
|
619
|
+
// case 'text':
|
|
620
|
+
// this.selectedShape.width = Math.abs(x - this.selectedShape.x!)
|
|
621
|
+
// this.selectedShape.height = Math.abs(y - this.selectedShape.y!)
|
|
622
|
+
// if (x < this.selectedShape.x!) this.selectedShape.x = x
|
|
623
|
+
// if (y < this.selectedShape.y!) this.selectedShape.y = y
|
|
624
|
+
// break
|
|
575
625
|
case 'rectangle':
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
626
|
+
const startX = this.selectedShape.x!
|
|
627
|
+
const startY = this.selectedShape.y!
|
|
628
|
+
const width = this.selectedShape.width!
|
|
629
|
+
const height = this.selectedShape.height!
|
|
630
|
+
|
|
631
|
+
// 핸들의 좌표에 따라 너비 및 높이 조정
|
|
632
|
+
if (this.offsetX === startX && this.offsetY === startY) {
|
|
633
|
+
// 상좌 핸들 (왼쪽 위)
|
|
634
|
+
this.selectedShape.x = x
|
|
635
|
+
this.selectedShape.y = y
|
|
636
|
+
this.selectedShape.width = width + (startX - x)
|
|
637
|
+
this.selectedShape.height = height + (startY - y)
|
|
638
|
+
} else if (this.offsetX === startX + width && this.offsetY === startY) {
|
|
639
|
+
// 상우 핸들 (오른쪽 위)
|
|
640
|
+
this.selectedShape.y = y
|
|
641
|
+
this.selectedShape.width = Math.abs(x - startX)
|
|
642
|
+
this.selectedShape.height = height + (startY - y)
|
|
643
|
+
} else if (this.offsetX === startX && this.offsetY === startY + height) {
|
|
644
|
+
// 하좌 핸들 (왼쪽 아래)
|
|
645
|
+
this.selectedShape.x = x
|
|
646
|
+
this.selectedShape.width = width + (startX - x)
|
|
647
|
+
this.selectedShape.height = Math.abs(y - startY)
|
|
648
|
+
} else if (this.offsetX === startX + width && this.offsetY === startY + height) {
|
|
649
|
+
// 하우 핸들 (오른쪽 아래)
|
|
650
|
+
this.selectedShape.width = Math.abs(x - startX)
|
|
651
|
+
this.selectedShape.height = Math.abs(y - startY)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// 최소 너비 및 높이 유지
|
|
655
|
+
this.selectedShape.width = Math.max(10, this.selectedShape.width!)
|
|
656
|
+
this.selectedShape.height = Math.max(10, this.selectedShape.height!)
|
|
657
|
+
|
|
658
|
+
this.offsetX = x
|
|
659
|
+
this.offsetY = y
|
|
660
|
+
|
|
580
661
|
break
|
|
581
662
|
case 'circle':
|
|
582
663
|
const dx = x - this.selectedShape.cx!
|
|
@@ -610,15 +691,25 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
610
691
|
this.requestUpdate()
|
|
611
692
|
}
|
|
612
693
|
|
|
613
|
-
private onHandleMouseUp = () => {
|
|
694
|
+
private onHandleMouseUp = (event: MouseEvent | TouchEvent) => {
|
|
695
|
+
event.preventDefault()
|
|
696
|
+
|
|
614
697
|
const svg = this.svg as SVGSVGElement
|
|
615
|
-
|
|
616
|
-
|
|
698
|
+
|
|
699
|
+
if ('touches' in event) {
|
|
700
|
+
svg.removeEventListener('touchmove', this.onHandleMouseMove)
|
|
701
|
+
svg.removeEventListener('touchend', this.onHandleMouseUp)
|
|
702
|
+
} else {
|
|
703
|
+
svg.removeEventListener('mousemove', this.onHandleMouseMove)
|
|
704
|
+
svg.removeEventListener('mouseup', this.onHandleMouseUp)
|
|
705
|
+
}
|
|
706
|
+
|
|
617
707
|
this.selectedShape = null
|
|
618
708
|
}
|
|
619
709
|
|
|
620
710
|
private onTextDoubleClick(event: MouseEvent, shape: Shape) {
|
|
621
711
|
event.stopPropagation()
|
|
712
|
+
event.preventDefault()
|
|
622
713
|
|
|
623
714
|
const textElement = event.target as SVGTextElement
|
|
624
715
|
|
|
@@ -740,4 +831,26 @@ export class OxImageMarker extends BaseImageMarker {
|
|
|
740
831
|
|
|
741
832
|
return context.measureText(text).width
|
|
742
833
|
}
|
|
834
|
+
|
|
835
|
+
private onFocus = () => {
|
|
836
|
+
this.addEventListener('keydown', this.handleKeyDown)
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
private onBlur = () => {
|
|
840
|
+
this.removeEventListener('keydown', this.handleKeyDown)
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
private handleKeyDown = (event: KeyboardEvent) => {
|
|
844
|
+
if ((event.key === 'Delete' || event.key === 'Backspace') && this.selectedShape) {
|
|
845
|
+
this.deleteSelectedShape()
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
private deleteSelectedShape() {
|
|
850
|
+
if (this.selectedShape) {
|
|
851
|
+
this.shapes = this.shapes.filter(s => s.id !== this.selectedShape!.id)
|
|
852
|
+
this.selectedShape = null
|
|
853
|
+
this.requestUpdate()
|
|
854
|
+
}
|
|
855
|
+
}
|
|
743
856
|
}
|
|
@@ -55,6 +55,22 @@ const Template: Story<ArgTypes> = ({ imageUrl, shapes = [] }: ArgTypes) => html`
|
|
|
55
55
|
</style>
|
|
56
56
|
|
|
57
57
|
<style>
|
|
58
|
+
html,
|
|
59
|
+
body {
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding: 0;
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
|
|
64
|
+
overscroll-behavior-y: none;
|
|
65
|
+
|
|
66
|
+
/* This is a font-stack that tries to use the system-default sans-serifs first */
|
|
67
|
+
font-family: Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
68
|
+
line-height: 1.5;
|
|
69
|
+
-webkit-font-smoothing: antialiased;
|
|
70
|
+
|
|
71
|
+
accent-color: var(--md-sys-color-primary);
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
body {
|
|
59
75
|
width: 100%;
|
|
60
76
|
height: 800px;
|