@operato/image-marker 8.0.0-alpha.50 β 8.0.0-beta.1
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 +17 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/.editorconfig +0 -29
- package/.storybook/main.js +0 -3
- package/.storybook/preview.js +0 -52
- package/.storybook/server.mjs +0 -8
- package/src/base-image-marker.ts +0 -268
- package/src/default-image-provider.ts +0 -7
- package/src/index.ts +0 -6
- package/src/ox-image-marker-mode.ts +0 -158
- package/src/ox-image-marker-view.ts +0 -24
- package/src/ox-image-marker.ts +0 -857
- package/src/types.ts +0 -34
- package/stories/ox-image-marker-mode.stories.ts +0 -41
- package/stories/ox-image-marker-view.stories.ts +0 -134
- package/stories/ox-image-marker.stories.ts +0 -118
- package/stories/test-image-provider.stories.ts +0 -135
- package/stories/test-image-provider.ts +0 -51
- package/tsconfig.json +0 -25
- package/web-dev-server.config.mjs +0 -27
- package/web-test-runner.config.mjs +0 -41
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@operato/image-marker",
|
|
3
3
|
"description": "Webcomponents for image-marker chart following open-wc recommendations",
|
|
4
4
|
"author": "heartyoh@hatiolab.com",
|
|
5
|
-
"version": "8.0.0-
|
|
5
|
+
"version": "8.0.0-beta.1",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
7
7
|
"module": "dist/src/index.js",
|
|
8
8
|
"license": "MIT",
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@operato/i18n": "^8.0.0-
|
|
54
|
-
"@operato/styles": "^8.0.0-
|
|
55
|
-
"@operato/utils": "^8.0.0-
|
|
53
|
+
"@operato/i18n": "^8.0.0-beta.1",
|
|
54
|
+
"@operato/styles": "^8.0.0-beta.1",
|
|
55
|
+
"@operato/utils": "^8.0.0-beta.1",
|
|
56
56
|
"d3": "^7.8.5",
|
|
57
57
|
"d3-time-format": "^4.1.0",
|
|
58
58
|
"i18next": "^23.11.5",
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"prettier --write"
|
|
95
95
|
]
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "d5b28a2e9deb632c0dc80132f6a7196dd6fe4220"
|
|
98
98
|
}
|
package/.editorconfig
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# EditorConfig helps developers define and maintain consistent
|
|
2
|
-
# coding styles between different editors and IDEs
|
|
3
|
-
# editorconfig.org
|
|
4
|
-
|
|
5
|
-
root = true
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
[*]
|
|
9
|
-
|
|
10
|
-
# Change these settings to your own preference
|
|
11
|
-
indent_style = space
|
|
12
|
-
indent_size = 2
|
|
13
|
-
|
|
14
|
-
# We recommend you to keep these unchanged
|
|
15
|
-
end_of_line = lf
|
|
16
|
-
charset = utf-8
|
|
17
|
-
trim_trailing_whitespace = true
|
|
18
|
-
insert_final_newline = true
|
|
19
|
-
|
|
20
|
-
[*.md]
|
|
21
|
-
trim_trailing_whitespace = false
|
|
22
|
-
|
|
23
|
-
[*.json]
|
|
24
|
-
indent_size = 2
|
|
25
|
-
|
|
26
|
-
[*.{html,js,md}]
|
|
27
|
-
block_comment_start = /**
|
|
28
|
-
block_comment = *
|
|
29
|
-
block_comment_end = */
|
package/.storybook/main.js
DELETED
package/.storybook/preview.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { i18next } from '@operato/i18n'
|
|
2
|
-
|
|
3
|
-
export const globalTypes = {
|
|
4
|
-
locale: {
|
|
5
|
-
name: 'Locale',
|
|
6
|
-
description: 'Internationalization locale',
|
|
7
|
-
toolbar: {
|
|
8
|
-
icon: 'globe',
|
|
9
|
-
items: [
|
|
10
|
-
{ value: 'en', right: 'πΊπΈ', title: 'English' },
|
|
11
|
-
{ value: 'ko', right: 'π°π·', title: 'νκ΅μ΄' },
|
|
12
|
-
{ value: 'zh', right: 'π¨π³', title: 'δΈζ' },
|
|
13
|
-
{ value: 'ja', right: 'π―π΅', title: 'ζ₯ζ¬θͺ' },
|
|
14
|
-
{ value: 'ms', right: 'π²πΎ', title: 'Bahasa Melayu' }
|
|
15
|
-
],
|
|
16
|
-
showName: true
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
theme: {
|
|
20
|
-
name: 'Theme',
|
|
21
|
-
description: 'Global theme for components',
|
|
22
|
-
toolbar: {
|
|
23
|
-
icon: 'paintbrush',
|
|
24
|
-
items: [
|
|
25
|
-
{ value: 'light', title: 'Light' },
|
|
26
|
-
{ value: 'dark', title: 'Dark' }
|
|
27
|
-
],
|
|
28
|
-
showName: true
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const decorators = [
|
|
34
|
-
(Story, context) => {
|
|
35
|
-
const { locale, theme } = context.globals
|
|
36
|
-
|
|
37
|
-
if (locale) {
|
|
38
|
-
i18next.changeLanguage(locale)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Set the theme class for the document
|
|
42
|
-
if (theme === 'dark') {
|
|
43
|
-
document.documentElement.classList.add('dark')
|
|
44
|
-
document.documentElement.classList.remove('light')
|
|
45
|
-
} else {
|
|
46
|
-
document.documentElement.classList.add('light')
|
|
47
|
-
document.documentElement.classList.remove('dark')
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return Story()
|
|
51
|
-
}
|
|
52
|
-
]
|
package/.storybook/server.mjs
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { storybookPlugin } from '@web/dev-server-storybook'
|
|
2
|
-
import baseConfig from '../web-dev-server.config.mjs'
|
|
3
|
-
|
|
4
|
-
export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
|
|
5
|
-
...baseConfig,
|
|
6
|
-
open: '/',
|
|
7
|
-
plugins: [storybookPlugin({ type: 'web-components' }), ...baseConfig.plugins]
|
|
8
|
-
})
|
package/src/base-image-marker.ts
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { LitElement, css, html, svg, nothing, PropertyValues } from 'lit'
|
|
2
|
-
import { property, state, query } from 'lit/decorators.js'
|
|
3
|
-
import { debounce } from 'lodash-es'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
GestureEventDoubleTap,
|
|
7
|
-
GestureEventDrag,
|
|
8
|
-
GestureEventPinch,
|
|
9
|
-
GestureHelper
|
|
10
|
-
} from '@operato/utils/gesture-helper.js'
|
|
11
|
-
import { ImageProvider, Shape } from './types'
|
|
12
|
-
import { DefaultImageProvider } from './default-image-provider'
|
|
13
|
-
|
|
14
|
-
export class BaseImageMarker extends LitElement {
|
|
15
|
-
static styles = [
|
|
16
|
-
css`
|
|
17
|
-
:host {
|
|
18
|
-
display: flex;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.container {
|
|
22
|
-
flex: 1;
|
|
23
|
-
position: relative;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
svg {
|
|
27
|
-
position: absolute;
|
|
28
|
-
top: 0;
|
|
29
|
-
left: 0;
|
|
30
|
-
cursor: pointer;
|
|
31
|
-
width: 100%;
|
|
32
|
-
height: 100%;
|
|
33
|
-
touch-action: none;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.link:hover {
|
|
37
|
-
cursor: pointer;
|
|
38
|
-
}
|
|
39
|
-
`
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
@property({ type: String }) imageUrl: string = ''
|
|
43
|
-
@property({ type: Array }) shapes: Shape[] = []
|
|
44
|
-
|
|
45
|
-
@state() protected currentImage?: string
|
|
46
|
-
@state() protected viewBox = '0 0 800 600'
|
|
47
|
-
@state() protected zoomLevel = 1
|
|
48
|
-
@state() private imageProvider?: ImageProvider = new DefaultImageProvider()
|
|
49
|
-
|
|
50
|
-
protected debouncedUpdateImage = debounce(this.updateImage.bind(this), 300)
|
|
51
|
-
protected zoomFactor = 0.1
|
|
52
|
-
protected zoomThreshold = 0.05
|
|
53
|
-
|
|
54
|
-
protected initialViewBox: string | null = null
|
|
55
|
-
|
|
56
|
-
@query('svg') protected svg!: SVGSVGElement
|
|
57
|
-
@query('image') protected image!: SVGImageElement
|
|
58
|
-
|
|
59
|
-
private gestureHelper: GestureHelper | null = null
|
|
60
|
-
|
|
61
|
-
connectedCallback(): void {
|
|
62
|
-
super.connectedCallback()
|
|
63
|
-
requestAnimationFrame(() => {
|
|
64
|
-
this.gestureHelper = new GestureHelper(this.svg)
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
disconnectedCallback(): void {
|
|
69
|
-
super.disconnectedCallback()
|
|
70
|
-
|
|
71
|
-
this.gestureHelper?.dispose()
|
|
72
|
-
this.gestureHelper = null
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
protected updated(changes: PropertyValues): void {
|
|
76
|
-
if (changes.has('imageUrl') || changes.has('imageProvider')) {
|
|
77
|
-
this.zoomLevel = 1
|
|
78
|
-
this.initialViewBox = null
|
|
79
|
-
this.updateImage()
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public setImageProvider(provider: ImageProvider) {
|
|
84
|
-
this.imageProvider = provider
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
protected onImageLoad(event: Event) {
|
|
88
|
-
const element = event.target as SVGImageElement
|
|
89
|
-
const img = new Image()
|
|
90
|
-
|
|
91
|
-
img.onload = () => {
|
|
92
|
-
const width = img.naturalWidth
|
|
93
|
-
const height = img.naturalHeight
|
|
94
|
-
|
|
95
|
-
if (!this.initialViewBox) {
|
|
96
|
-
this.initialViewBox = `0 0 ${width} ${height}`
|
|
97
|
-
this.viewBox = this.initialViewBox
|
|
98
|
-
|
|
99
|
-
element.setAttribute('width', width.toString())
|
|
100
|
-
element.setAttribute('height', height.toString())
|
|
101
|
-
element.setAttribute('preserveAspectRatio', 'xMidYMid meet')
|
|
102
|
-
} else {
|
|
103
|
-
const [initialX, initialY, initialWidth, initialHeight] = this.initialViewBox.split(' ').map(Number)
|
|
104
|
-
const scale = initialWidth / width
|
|
105
|
-
|
|
106
|
-
element.setAttribute('width', (width * scale).toString())
|
|
107
|
-
element.setAttribute('height', (height * scale).toString())
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
img.src = this.currentImage!
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
protected async updateImage() {
|
|
115
|
-
if (!this.imageProvider) {
|
|
116
|
-
throw new Error('ImageProvider is not set')
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!this.imageUrl) {
|
|
120
|
-
return
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const options = {
|
|
124
|
-
id: this.imageUrl, // μ΄λ―Έμ§ IDλ imageUrl μμ±μ μ¬μ©
|
|
125
|
-
scale: this.zoomLevel, // scaleμ zoomLevel μμ±μ μ¬μ©
|
|
126
|
-
offsetX: 0, // offsetXλ 0μΌλ‘ μ€μ
|
|
127
|
-
offsetY: 0, // offsetYλ 0μΌλ‘ μ€μ
|
|
128
|
-
currentImage: this.currentImage // κΈ°μ‘΄ μ΄λ―Έμ§ κ°μ²΄ μ λ¬
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
this.currentImage = await this.imageProvider.getImage(options)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
protected onSvgWheel(event: WheelEvent) {
|
|
135
|
-
event.preventDefault()
|
|
136
|
-
|
|
137
|
-
const { offsetX, offsetY, deltaY } = event
|
|
138
|
-
const scale = deltaY > 0 ? 1 - this.zoomFactor : 1 + this.zoomFactor
|
|
139
|
-
|
|
140
|
-
this.zoom(scale, offsetX, offsetY)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
protected onSvgPinch(e: GestureEventPinch) {
|
|
144
|
-
e.stopPropagation()
|
|
145
|
-
|
|
146
|
-
const { scale, centerX, centerY } = e.detail
|
|
147
|
-
|
|
148
|
-
console.log('pinch', e.detail)
|
|
149
|
-
|
|
150
|
-
this.zoom(scale, centerX, centerY)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
protected onSvgDoubleTap(e: GestureEventDoubleTap) {
|
|
154
|
-
e.stopPropagation()
|
|
155
|
-
|
|
156
|
-
this.reset()
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
protected onSvgDrag(e: GestureEventDrag) {
|
|
160
|
-
const { deltaX, deltaY } = e.detail
|
|
161
|
-
console.log('drag', e.detail)
|
|
162
|
-
|
|
163
|
-
const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)
|
|
164
|
-
|
|
165
|
-
const scaleX = vbWidth / this.svg.clientWidth
|
|
166
|
-
const scaleY = vbHeight / this.svg.clientHeight
|
|
167
|
-
const scale = Math.max(scaleX, scaleY)
|
|
168
|
-
|
|
169
|
-
const newVbX = vbX - deltaX * scale
|
|
170
|
-
const newVbY = vbY - deltaY * scale
|
|
171
|
-
|
|
172
|
-
this.viewBox = `${newVbX} ${newVbY} ${vbWidth} ${vbHeight}`
|
|
173
|
-
|
|
174
|
-
this.debouncedUpdateImage()
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
zoom(scale: number, offsetX: number, offsetY: number) {
|
|
178
|
-
if (Math.abs(scale) > this.zoomThreshold) {
|
|
179
|
-
this.zoomLevel *= scale
|
|
180
|
-
|
|
181
|
-
const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)
|
|
182
|
-
const mouseX = vbX + (offsetX / this.svg.clientWidth) * vbWidth
|
|
183
|
-
const mouseY = vbY + (offsetY / this.svg.clientHeight) * vbHeight
|
|
184
|
-
|
|
185
|
-
const newVbWidth = vbWidth / scale
|
|
186
|
-
const newVbHeight = vbHeight / scale
|
|
187
|
-
|
|
188
|
-
const newVbX = mouseX - (offsetX / this.svg.clientWidth) * newVbWidth
|
|
189
|
-
const newVbY = mouseY - (offsetY / this.svg.clientHeight) * newVbHeight
|
|
190
|
-
|
|
191
|
-
this.viewBox = `${newVbX} ${newVbY} ${newVbWidth} ${newVbHeight}`
|
|
192
|
-
|
|
193
|
-
this.debouncedUpdateImage()
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
public reset() {
|
|
198
|
-
if (!this.initialViewBox) {
|
|
199
|
-
this.zoomLevel = 1
|
|
200
|
-
return
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
this.viewBox = this.initialViewBox!
|
|
204
|
-
this.zoomLevel = 1
|
|
205
|
-
|
|
206
|
-
this.debouncedUpdateImage()
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
protected handleLinkClick(shape: Shape) {
|
|
210
|
-
this.dispatchEvent(
|
|
211
|
-
new CustomEvent('link-clicked', {
|
|
212
|
-
detail: { id: shape.id, link: shape.link },
|
|
213
|
-
bubbles: true,
|
|
214
|
-
composed: true
|
|
215
|
-
})
|
|
216
|
-
)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
protected renderShape(shape: Shape) {
|
|
220
|
-
const commonAttrs = {
|
|
221
|
-
stroke: 'red',
|
|
222
|
-
'stroke-width': 1,
|
|
223
|
-
fill: 'transparent'
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
switch (shape.type) {
|
|
227
|
-
case 'text':
|
|
228
|
-
return svg`<text x="${shape.x}" y="${shape.y}" text-anchor="middle" alignment-baseline="middle" fill="black">${shape.text}</text>`
|
|
229
|
-
case 'rectangle':
|
|
230
|
-
return svg`<rect x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}" stroke="${commonAttrs.stroke}" stroke-width="${commonAttrs['stroke-width']}" fill="${commonAttrs.fill}"></rect>`
|
|
231
|
-
case 'circle':
|
|
232
|
-
return svg`<circle cx="${shape.cx}" cy="${shape.cy}" r="${shape.r}" stroke="${commonAttrs.stroke}" stroke-width="${commonAttrs['stroke-width']}" fill="${commonAttrs.fill}"></circle>`
|
|
233
|
-
case 'line':
|
|
234
|
-
return svg`<line x1="${shape.x1}" y1="${shape.y1}" x2="${shape.x2}" y2="${shape.y2}" stroke="${commonAttrs.stroke}" stroke-width="${commonAttrs['stroke-width']}"></line>`
|
|
235
|
-
case 'arrow':
|
|
236
|
-
return svg`
|
|
237
|
-
<defs>
|
|
238
|
-
<marker id="arrowhead-${shape.id}" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
|
239
|
-
<polygon points="0 0, 10 3.5, 0 7" fill="${commonAttrs.stroke}"></polygon>
|
|
240
|
-
</marker>
|
|
241
|
-
</defs>
|
|
242
|
-
<line x1="${shape.x1}" y1="${shape.y1}" x2="${shape.x2}" y2="${shape.y2}" stroke="${commonAttrs.stroke}" stroke-width="${commonAttrs['stroke-width']}" marker-end="url(#arrowhead-${shape.id})"></line>`
|
|
243
|
-
case 'link':
|
|
244
|
-
return svg`
|
|
245
|
-
<rect class="link" x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}"
|
|
246
|
-
stroke="${commonAttrs.stroke}" stroke-width="0"
|
|
247
|
-
fill="${commonAttrs.stroke}" fill-opacity="0.3" @click="${() => this.handleLinkClick(shape)}">
|
|
248
|
-
</rect>`
|
|
249
|
-
default:
|
|
250
|
-
return nothing
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
protected getPointFromEvent(event: MouseEvent | PointerEvent | GestureEventDrag) {
|
|
255
|
-
if ('clientX' in event) {
|
|
256
|
-
return {
|
|
257
|
-
x: event.clientX,
|
|
258
|
-
y: event.clientY
|
|
259
|
-
}
|
|
260
|
-
} else {
|
|
261
|
-
const { clientX, clientY } = event.detail
|
|
262
|
-
return {
|
|
263
|
-
x: clientX,
|
|
264
|
-
y: clientY
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import '@material/web/icon/icon.js'
|
|
2
|
-
|
|
3
|
-
import { LitElement, html, css } from 'lit'
|
|
4
|
-
import { customElement, property } from 'lit/decorators.js'
|
|
5
|
-
import { ImageMarkerMode, ShapeType } from './types'
|
|
6
|
-
|
|
7
|
-
@customElement('ox-image-marker-mode')
|
|
8
|
-
export class OxImageMarkerMode extends LitElement {
|
|
9
|
-
static styles = css`
|
|
10
|
-
:host {
|
|
11
|
-
display: flex;
|
|
12
|
-
flex-direction: column;
|
|
13
|
-
gap: var(--spacing-medium);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.palette {
|
|
17
|
-
display: flex;
|
|
18
|
-
flex-direction: column;
|
|
19
|
-
gap: var(--spacing-small);
|
|
20
|
-
justify-content: center;
|
|
21
|
-
margin: var(--spacing-medium);
|
|
22
|
-
|
|
23
|
-
--md-icon-size: 16px;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.palette > * {
|
|
27
|
-
padding: var(--spacing-small);
|
|
28
|
-
cursor: pointer;
|
|
29
|
-
background-color: #f0f0f0;
|
|
30
|
-
border: 1px solid #ccc;
|
|
31
|
-
border-radius: 4px;
|
|
32
|
-
width: 12px;
|
|
33
|
-
height: 12px;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.palette [selected] {
|
|
37
|
-
background-color: #d0d0d0;
|
|
38
|
-
}
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
@property({ type: String }) currentMode: ImageMarkerMode = 'edit'
|
|
42
|
-
@property({ type: String }) currentShapeType?: ShapeType
|
|
43
|
-
|
|
44
|
-
render() {
|
|
45
|
-
return html`
|
|
46
|
-
<div class="palette">
|
|
47
|
-
<md-icon
|
|
48
|
-
title="Edit or View"
|
|
49
|
-
?selected=${this.currentMode !== 'shape'}
|
|
50
|
-
@click=${() => this.setMode(this.currentMode !== 'edit' ? 'edit' : 'view')}
|
|
51
|
-
>${this.currentMode !== 'view' ? 'brush' : 'drag_pan'}</md-icon
|
|
52
|
-
>
|
|
53
|
-
|
|
54
|
-
<md-icon
|
|
55
|
-
title="Text"
|
|
56
|
-
?selected=${this.currentMode === 'shape' && this.currentShapeType === 'text'}
|
|
57
|
-
@click=${() => this.setShape('text')}
|
|
58
|
-
>match_case</md-icon
|
|
59
|
-
>
|
|
60
|
-
|
|
61
|
-
<md-icon
|
|
62
|
-
title="Rectangle"
|
|
63
|
-
?selected=${this.currentMode === 'shape' && this.currentShapeType === 'rectangle'}
|
|
64
|
-
@click=${() => this.setShape('rectangle')}
|
|
65
|
-
>rectangle</md-icon
|
|
66
|
-
>
|
|
67
|
-
|
|
68
|
-
<md-icon
|
|
69
|
-
title="Circle"
|
|
70
|
-
?selected=${this.currentMode === 'shape' && this.currentShapeType === 'circle'}
|
|
71
|
-
@click=${() => this.setShape('circle')}
|
|
72
|
-
>circle</md-icon
|
|
73
|
-
>
|
|
74
|
-
|
|
75
|
-
<md-icon
|
|
76
|
-
title="Triangle"
|
|
77
|
-
?selected=${this.currentMode === 'shape' && this.currentShapeType === 'triangle'}
|
|
78
|
-
@click=${() => this.setShape('triangle')}
|
|
79
|
-
>change_history</md-icon
|
|
80
|
-
>
|
|
81
|
-
|
|
82
|
-
<md-icon
|
|
83
|
-
title="Line"
|
|
84
|
-
?selected=${this.currentMode === 'shape' && this.currentShapeType === 'line'}
|
|
85
|
-
@click=${() => this.setShape('line')}
|
|
86
|
-
>shape_line</md-icon
|
|
87
|
-
>
|
|
88
|
-
|
|
89
|
-
<md-icon
|
|
90
|
-
title="Arrow"
|
|
91
|
-
?selected=${this.currentMode === 'shape' && this.currentShapeType === 'arrow'}
|
|
92
|
-
@click=${() => this.setShape('arrow')}
|
|
93
|
-
>line_end_arrow</md-icon
|
|
94
|
-
>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<div class="palette">
|
|
98
|
-
<md-icon title="Fit size" @click=${() => this.onClickFitSize()}>recenter</md-icon>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<div class="palette">
|
|
102
|
-
<md-icon title="Delete" @click=${() => this.onClickDelete()}>delete</md-icon>
|
|
103
|
-
<md-icon title="Save" @click=${() => this.onClickSave()}>save</md-icon>
|
|
104
|
-
</div>
|
|
105
|
-
`
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
public setMode(mode: ImageMarkerMode) {
|
|
109
|
-
this.currentMode = mode
|
|
110
|
-
|
|
111
|
-
this.dispatchEvent(
|
|
112
|
-
new CustomEvent('mode-changed', {
|
|
113
|
-
detail: { mode: this.currentMode, shapeType: this.currentShapeType },
|
|
114
|
-
bubbles: true,
|
|
115
|
-
composed: true
|
|
116
|
-
})
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
protected setShape(shapeType: ShapeType) {
|
|
121
|
-
this.currentMode = 'shape'
|
|
122
|
-
this.currentShapeType = shapeType
|
|
123
|
-
this.dispatchEvent(
|
|
124
|
-
new CustomEvent('mode-changed', {
|
|
125
|
-
detail: { mode: 'shape', shapeType: this.currentShapeType },
|
|
126
|
-
bubbles: true,
|
|
127
|
-
composed: true
|
|
128
|
-
})
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private onClickDelete() {
|
|
133
|
-
this.dispatchEvent(
|
|
134
|
-
new CustomEvent('tap-delete', {
|
|
135
|
-
bubbles: true,
|
|
136
|
-
composed: true
|
|
137
|
-
})
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private onClickSave() {
|
|
142
|
-
this.dispatchEvent(
|
|
143
|
-
new CustomEvent('tap-save', {
|
|
144
|
-
bubbles: true,
|
|
145
|
-
composed: true
|
|
146
|
-
})
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private onClickFitSize() {
|
|
151
|
-
this.dispatchEvent(
|
|
152
|
-
new CustomEvent('tap-fit-size', {
|
|
153
|
-
bubbles: true,
|
|
154
|
-
composed: true
|
|
155
|
-
})
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { html } from 'lit'
|
|
2
|
-
import { customElement } from 'lit/decorators.js'
|
|
3
|
-
|
|
4
|
-
import { BaseImageMarker } from './base-image-marker.js'
|
|
5
|
-
|
|
6
|
-
@customElement('ox-image-marker-view')
|
|
7
|
-
export class OxImageMarkerView extends BaseImageMarker {
|
|
8
|
-
render() {
|
|
9
|
-
return html`
|
|
10
|
-
<div class="container">
|
|
11
|
-
<svg
|
|
12
|
-
@wheel=${this.onSvgWheel}
|
|
13
|
-
@doubletap=${this.onSvgDoubleTap}
|
|
14
|
-
@pinch=${this.onSvgPinch}
|
|
15
|
-
@drag=${this.onSvgDrag}
|
|
16
|
-
viewBox=${this.viewBox}
|
|
17
|
-
>
|
|
18
|
-
<image href=${this.currentImage} @load=${this.onImageLoad}></image>
|
|
19
|
-
${this.shapes.map(shape => this.renderShape(shape))}
|
|
20
|
-
</svg>
|
|
21
|
-
</div>
|
|
22
|
-
`
|
|
23
|
-
}
|
|
24
|
-
}
|