@operato/image-marker 8.0.0-alpha.10
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/.editorconfig +29 -0
- package/.storybook/main.js +3 -0
- package/.storybook/preview.js +52 -0
- package/.storybook/server.mjs +8 -0
- package/CHANGELOG.md +11 -0
- package/README.md +0 -0
- package/assets/images/no-image.png +0 -0
- package/demo/index.html +22 -0
- package/dist/src/base-image-marker.d.ts +24 -0
- package/dist/src/base-image-marker.js +137 -0
- package/dist/src/base-image-marker.js.map +1 -0
- package/dist/src/index.d.ts +0 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/ox-image-marker-mode.d.ts +13 -0
- package/dist/src/ox-image-marker-mode.js +140 -0
- package/dist/src/ox-image-marker-mode.js.map +1 -0
- package/dist/src/ox-image-marker-view.d.ts +4 -0
- package/dist/src/ox-image-marker-view.js +26 -0
- package/dist/src/ox-image-marker-view.js.map +1 -0
- package/dist/src/ox-image-marker.d.ts +33 -0
- package/dist/src/ox-image-marker.js +583 -0
- package/dist/src/ox-image-marker.js.map +1 -0
- package/dist/src/types.d.ts +20 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/stories/ox-image-marker-mode.stories.d.ts +16 -0
- package/dist/stories/ox-image-marker-mode.stories.js +31 -0
- package/dist/stories/ox-image-marker-mode.stories.js.map +1 -0
- package/dist/stories/ox-image-marker-view.stories.d.ts +41 -0
- package/dist/stories/ox-image-marker-view.stories.js +103 -0
- package/dist/stories/ox-image-marker-view.stories.js.map +1 -0
- package/dist/stories/ox-image-marker.stories.d.ts +33 -0
- package/dist/stories/ox-image-marker.stories.js +86 -0
- package/dist/stories/ox-image-marker.stories.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +97 -0
- package/src/base-image-marker.ts +144 -0
- package/src/index.ts +0 -0
- package/src/ox-image-marker-mode.ts +145 -0
- package/src/ox-image-marker-view.ts +23 -0
- package/src/ox-image-marker.ts +626 -0
- package/src/types.ts +20 -0
- package/stories/ox-image-marker-mode.stories.ts +41 -0
- package/stories/ox-image-marker-view.stories.ts +134 -0
- package/stories/ox-image-marker.stories.ts +102 -0
- package/themes/app-theme.css +138 -0
- package/themes/calendar-theme.css +59 -0
- package/themes/dark.css +51 -0
- package/themes/form-theme.css +70 -0
- package/themes/grist-theme.css +175 -0
- package/themes/layout-theme.css +94 -0
- package/themes/light.css +51 -0
- package/themes/material-theme.css +23 -0
- package/themes/md-typescale-styles.css +100 -0
- package/themes/oops-theme.css +22 -0
- package/themes/report-theme.css +47 -0
- package/themes/spacing.css +23 -0
- package/themes/state-color.css +6 -0
- package/themes/tooltip-theme.css +11 -0
- package/translations/en.json +1 -0
- package/translations/ja.json +1 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
- package/tsconfig.json +25 -0
- package/web-dev-server.config.mjs +27 -0
- package/web-test-runner.config.mjs +41 -0
package/.editorconfig
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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 = */
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
]
|
|
@@ -0,0 +1,8 @@
|
|
|
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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [8.0.0-alpha.10](https://github.com/hatiolab/operato/compare/v8.0.0-alpha.9...v8.0.0-alpha.10) (2024-09-18)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
* image-marker module ([eb86f2a](https://github.com/hatiolab/operato/commit/eb86f2a97813e2cbd90712f862b519ae3d0a2096))
|
package/README.md
ADDED
|
File without changes
|
|
Binary file
|
package/demo/index.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-GB">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1" />
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
background: #fafafa;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
a {
|
|
12
|
+
display: block;
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
15
|
+
<link href="/themes/app-theme.css" rel="stylesheet" />
|
|
16
|
+
<link href="/themes/input-theme.css" rel="stylesheet" />
|
|
17
|
+
<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet" />
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<a href="./index-select.html">select</a>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { LitElement, nothing } from 'lit';
|
|
2
|
+
import { Shape } from './types';
|
|
3
|
+
export declare class BaseImageMarker extends LitElement {
|
|
4
|
+
static styles: import("lit").CSSResult[];
|
|
5
|
+
imageUrl: string;
|
|
6
|
+
shapes: Shape[];
|
|
7
|
+
protected viewBox: string;
|
|
8
|
+
protected zoomLevel: number;
|
|
9
|
+
protected zoomFactor: number;
|
|
10
|
+
protected isPanning: boolean;
|
|
11
|
+
protected panStart: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
};
|
|
15
|
+
protected initialViewBox: string;
|
|
16
|
+
protected svg: SVGSVGElement;
|
|
17
|
+
protected onImageLoad(event: Event): void;
|
|
18
|
+
protected onWheel(event: WheelEvent): void;
|
|
19
|
+
protected onSvgMouseDown(event: MouseEvent): void;
|
|
20
|
+
protected onPanMouseMove: (event: MouseEvent) => void;
|
|
21
|
+
protected onPanMouseUp: () => void;
|
|
22
|
+
protected onDoubleClick(): void;
|
|
23
|
+
protected renderShape(shape: Shape): import("lit-html").TemplateResult<2> | typeof nothing;
|
|
24
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { LitElement, css, svg, nothing } from 'lit';
|
|
3
|
+
import { property, state, query } from 'lit/decorators.js';
|
|
4
|
+
export class BaseImageMarker extends LitElement {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.imageUrl = '';
|
|
8
|
+
this.shapes = [];
|
|
9
|
+
this.viewBox = '0 0 800 600';
|
|
10
|
+
this.zoomLevel = 1;
|
|
11
|
+
this.zoomFactor = 0.1;
|
|
12
|
+
this.isPanning = false;
|
|
13
|
+
this.panStart = { x: 0, y: 0 };
|
|
14
|
+
this.initialViewBox = '0 0 800 600';
|
|
15
|
+
this.onPanMouseMove = (event) => {
|
|
16
|
+
if (!this.isPanning)
|
|
17
|
+
return;
|
|
18
|
+
const dx = event.clientX - this.panStart.x;
|
|
19
|
+
const dy = event.clientY - this.panStart.y;
|
|
20
|
+
const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number);
|
|
21
|
+
const scaleX = vbWidth / this.svg.clientWidth;
|
|
22
|
+
const scaleY = vbHeight / this.svg.clientHeight;
|
|
23
|
+
const scale = Math.max(scaleX, scaleY);
|
|
24
|
+
const newVbX = vbX - dx * scale;
|
|
25
|
+
const newVbY = vbY - dy * scale;
|
|
26
|
+
this.viewBox = `${newVbX} ${newVbY} ${vbWidth} ${vbHeight}`;
|
|
27
|
+
this.panStart = { x: event.clientX, y: event.clientY };
|
|
28
|
+
};
|
|
29
|
+
this.onPanMouseUp = () => {
|
|
30
|
+
const svg = this.svg;
|
|
31
|
+
this.isPanning = false;
|
|
32
|
+
svg.classList.remove('dragging');
|
|
33
|
+
svg.removeEventListener('mousemove', this.onPanMouseMove);
|
|
34
|
+
svg.removeEventListener('mouseup', this.onPanMouseUp);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
static { this.styles = [
|
|
38
|
+
css `
|
|
39
|
+
:host {
|
|
40
|
+
display: flex;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.container {
|
|
44
|
+
flex: 1;
|
|
45
|
+
position: relative;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
svg {
|
|
49
|
+
position: absolute;
|
|
50
|
+
top: 0;
|
|
51
|
+
left: 0;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
width: 100%;
|
|
54
|
+
height: 100%;
|
|
55
|
+
}
|
|
56
|
+
`
|
|
57
|
+
]; }
|
|
58
|
+
onImageLoad(event) {
|
|
59
|
+
const img = new Image();
|
|
60
|
+
img.onload = () => {
|
|
61
|
+
const width = img.naturalWidth;
|
|
62
|
+
const height = img.naturalHeight;
|
|
63
|
+
this.viewBox = `0 0 ${width} ${height}`;
|
|
64
|
+
this.initialViewBox = `0 0 ${width} ${height}`;
|
|
65
|
+
};
|
|
66
|
+
img.src = this.imageUrl;
|
|
67
|
+
}
|
|
68
|
+
onWheel(event) {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
const { offsetX, offsetY, deltaY } = event;
|
|
71
|
+
const scale = deltaY > 0 ? 1 - this.zoomFactor : 1 + this.zoomFactor;
|
|
72
|
+
this.zoomLevel *= scale;
|
|
73
|
+
const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number);
|
|
74
|
+
const mouseX = vbX + (offsetX / this.svg.clientWidth) * vbWidth;
|
|
75
|
+
const mouseY = vbY + (offsetY / this.svg.clientHeight) * vbHeight;
|
|
76
|
+
const newVbWidth = vbWidth / scale;
|
|
77
|
+
const newVbHeight = vbHeight / scale;
|
|
78
|
+
const newVbX = mouseX - (offsetX / this.svg.clientWidth) * newVbWidth;
|
|
79
|
+
const newVbY = mouseY - (offsetY / this.svg.clientHeight) * newVbHeight;
|
|
80
|
+
this.viewBox = `${newVbX} ${newVbY} ${newVbWidth} ${newVbHeight}`;
|
|
81
|
+
}
|
|
82
|
+
onSvgMouseDown(event) {
|
|
83
|
+
const svg = this.svg;
|
|
84
|
+
this.isPanning = true;
|
|
85
|
+
this.panStart = { x: event.clientX, y: event.clientY };
|
|
86
|
+
svg.classList.add('dragging');
|
|
87
|
+
svg.addEventListener('mousemove', this.onPanMouseMove);
|
|
88
|
+
svg.addEventListener('mouseup', this.onPanMouseUp);
|
|
89
|
+
}
|
|
90
|
+
onDoubleClick() {
|
|
91
|
+
this.viewBox = this.initialViewBox;
|
|
92
|
+
this.zoomLevel = 1;
|
|
93
|
+
}
|
|
94
|
+
renderShape(shape) {
|
|
95
|
+
const commonAttrs = {
|
|
96
|
+
stroke: 'red',
|
|
97
|
+
'stroke-width': 1,
|
|
98
|
+
fill: 'transparent'
|
|
99
|
+
};
|
|
100
|
+
switch (shape.type) {
|
|
101
|
+
case 'text':
|
|
102
|
+
return svg `<text x="${shape.x}" y="${shape.y}" text-anchor="middle" alignment-baseline="middle" fill="black">${shape.text}</text>`;
|
|
103
|
+
case 'rectangle':
|
|
104
|
+
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>`;
|
|
105
|
+
case 'circle':
|
|
106
|
+
return svg `<circle cx="${shape.cx}" cy="${shape.cy}" r="${shape.r}" stroke="${commonAttrs.stroke}" stroke-width="${commonAttrs['stroke-width']}" fill="${commonAttrs.fill}"></circle>`;
|
|
107
|
+
case 'line':
|
|
108
|
+
return svg `<line x1="${shape.x1}" y1="${shape.y1}" x2="${shape.x2}" y2="${shape.y2}" stroke="${commonAttrs.stroke}" stroke-width="${commonAttrs['stroke-width']}"></line>`;
|
|
109
|
+
case 'arrow':
|
|
110
|
+
return svg `
|
|
111
|
+
<defs>
|
|
112
|
+
<marker id="arrowhead-${shape.id}" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
|
113
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="${commonAttrs.stroke}"></polygon>
|
|
114
|
+
</marker>
|
|
115
|
+
</defs>
|
|
116
|
+
<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>`;
|
|
117
|
+
default:
|
|
118
|
+
return nothing;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
__decorate([
|
|
123
|
+
property({ type: String })
|
|
124
|
+
], BaseImageMarker.prototype, "imageUrl", void 0);
|
|
125
|
+
__decorate([
|
|
126
|
+
property({ type: Array })
|
|
127
|
+
], BaseImageMarker.prototype, "shapes", void 0);
|
|
128
|
+
__decorate([
|
|
129
|
+
state()
|
|
130
|
+
], BaseImageMarker.prototype, "viewBox", void 0);
|
|
131
|
+
__decorate([
|
|
132
|
+
state()
|
|
133
|
+
], BaseImageMarker.prototype, "zoomLevel", void 0);
|
|
134
|
+
__decorate([
|
|
135
|
+
query('svg')
|
|
136
|
+
], BaseImageMarker.prototype, "svg", void 0);
|
|
137
|
+
//# sourceMappingURL=base-image-marker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-image-marker.js","sourceRoot":"","sources":["../../src/base-image-marker.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAQ,GAAG,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAI1D,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAA/C;;QAuB8B,aAAQ,GAAW,EAAE,CAAA;QACtB,WAAM,GAAY,EAAE,CAAA;QAE5B,YAAO,GAAG,aAAa,CAAA;QACvB,cAAS,GAAG,CAAC,CAAA;QAEtB,eAAU,GAAG,GAAG,CAAA;QAChB,cAAS,GAAG,KAAK,CAAA;QACjB,aAAQ,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QACzB,mBAAc,GAAG,aAAa,CAAA;QA8C9B,mBAAc,GAAG,CAAC,KAAiB,EAAE,EAAE;YAC/C,IAAI,CAAC,IAAI,CAAC,SAAS;gBAAE,OAAM;YAE3B,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;YAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;YAE1C,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YACzE,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAA;YAC7C,MAAM,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAA;YAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YAEtC,MAAM,MAAM,GAAG,GAAG,GAAG,EAAE,GAAG,KAAK,CAAA;YAC/B,MAAM,MAAM,GAAG,GAAG,GAAG,EAAE,GAAG,KAAK,CAAA;YAE/B,IAAI,CAAC,OAAO,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAA;YAC3D,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;QACxD,CAAC,CAAA;QAES,iBAAY,GAAG,GAAG,EAAE;YAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAA;YAEpB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;YAChC,GAAG,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;YACzD,GAAG,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QACvD,CAAC,CAAA;IAmCH,CAAC;aAzIQ,WAAM,GAAG;QACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;KAkBF;KACF,AApBY,CAoBZ;IAeS,WAAW,CAAC,KAAY;QAChC,MAAM,GAAG,GAAG,IAAI,KAAK,EAAE,CAAA;QACvB,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;YAChB,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAA;YAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAA;YAChC,IAAI,CAAC,OAAO,GAAG,OAAO,KAAK,IAAI,MAAM,EAAE,CAAA;YACvC,IAAI,CAAC,cAAc,GAAG,OAAO,KAAK,IAAI,MAAM,EAAE,CAAA;QAChD,CAAC,CAAA;QACD,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAA;IACzB,CAAC;IAES,OAAO,CAAC,KAAiB;QACjC,KAAK,CAAC,cAAc,EAAE,CAAA;QAEtB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;QAC1C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,CAAA;QACpE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAA;QAEvB,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACzE,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,OAAO,CAAA;QAC/D,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAA;QAEjE,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,CAAA;QAClC,MAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAA;QAEpC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,UAAU,CAAA;QACrE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,WAAW,CAAA;QAEvE,IAAI,CAAC,OAAO,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,UAAU,IAAI,WAAW,EAAE,CAAA;IACnE,CAAC;IAES,cAAc,CAAC,KAAiB;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAA;QAEpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;QACtD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAE7B,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;QACtD,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACpD,CAAC;IA6BS,aAAa;QACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,cAAc,CAAA;QAClC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;IACpB,CAAC;IAES,WAAW,CAAC,KAAY;QAChC,MAAM,WAAW,GAAG;YAClB,MAAM,EAAE,KAAK;YACb,cAAc,EAAE,CAAC;YACjB,IAAI,EAAE,aAAa;SACpB,CAAA;QAED,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,MAAM;gBACT,OAAO,GAAG,CAAA,YAAY,KAAK,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,mEAAmE,KAAK,CAAC,IAAI,SAAS,CAAA;YACpI,KAAK,WAAW;gBACd,OAAO,GAAG,CAAA,YAAY,KAAK,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,KAAK,aAAa,KAAK,CAAC,MAAM,aAAa,WAAW,CAAC,MAAM,mBAAmB,WAAW,CAAC,cAAc,CAAC,WAAW,WAAW,CAAC,IAAI,WAAW,CAAA;YACjN,KAAK,QAAQ;gBACX,OAAO,GAAG,CAAA,eAAe,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC,aAAa,WAAW,CAAC,MAAM,mBAAmB,WAAW,CAAC,cAAc,CAAC,WAAW,WAAW,CAAC,IAAI,aAAa,CAAA;YACxL,KAAK,MAAM;gBACT,OAAO,GAAG,CAAA,aAAa,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,aAAa,WAAW,CAAC,MAAM,mBAAmB,WAAW,CAAC,cAAc,CAAC,WAAW,CAAA;YAC5K,KAAK,OAAO;gBACV,OAAO,GAAG,CAAA;;oCAEkB,KAAK,CAAC,EAAE;yDACa,WAAW,CAAC,MAAM;;;sBAGrD,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,aAAa,WAAW,CAAC,MAAM,mBAAmB,WAAW,CAAC,cAAc,CAAC,gCAAgC,KAAK,CAAC,EAAE,YAAY,CAAA;YAC5M;gBACE,OAAO,OAAO,CAAA;QAClB,CAAC;IACH,CAAC;;AAlH2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;iDAAsB;AACtB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;+CAAqB;AAE5B;IAAlB,KAAK,EAAE;gDAAkC;AACvB;IAAlB,KAAK,EAAE;kDAAwB;AAOR;IAAvB,KAAK,CAAC,KAAK,CAAC;4CAA8B","sourcesContent":["import { LitElement, css, html, svg, nothing } from 'lit'\nimport { property, state, query } from 'lit/decorators.js'\n\nimport { Shape } from './types'\n\nexport class BaseImageMarker extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n }\n\n .container {\n flex: 1;\n position: relative;\n }\n\n svg {\n position: absolute;\n top: 0;\n left: 0;\n cursor: pointer;\n width: 100%;\n height: 100%;\n }\n `\n ]\n\n @property({ type: String }) imageUrl: string = ''\n @property({ type: Array }) shapes: Shape[] = []\n\n @state() protected viewBox = '0 0 800 600'\n @state() protected zoomLevel = 1\n\n protected zoomFactor = 0.1\n protected isPanning = false\n protected panStart = { x: 0, y: 0 }\n protected initialViewBox = '0 0 800 600'\n\n @query('svg') protected svg!: SVGSVGElement\n\n protected onImageLoad(event: Event) {\n const img = new Image()\n img.onload = () => {\n const width = img.naturalWidth\n const height = img.naturalHeight\n this.viewBox = `0 0 ${width} ${height}`\n this.initialViewBox = `0 0 ${width} ${height}`\n }\n img.src = this.imageUrl\n }\n\n protected onWheel(event: WheelEvent) {\n event.preventDefault()\n\n const { offsetX, offsetY, deltaY } = event\n const scale = deltaY > 0 ? 1 - this.zoomFactor : 1 + this.zoomFactor\n this.zoomLevel *= scale\n\n const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)\n const mouseX = vbX + (offsetX / this.svg.clientWidth) * vbWidth\n const mouseY = vbY + (offsetY / this.svg.clientHeight) * vbHeight\n\n const newVbWidth = vbWidth / scale\n const newVbHeight = vbHeight / scale\n\n const newVbX = mouseX - (offsetX / this.svg.clientWidth) * newVbWidth\n const newVbY = mouseY - (offsetY / this.svg.clientHeight) * newVbHeight\n\n this.viewBox = `${newVbX} ${newVbY} ${newVbWidth} ${newVbHeight}`\n }\n\n protected onSvgMouseDown(event: MouseEvent) {\n const svg = this.svg\n\n this.isPanning = true\n this.panStart = { x: event.clientX, y: event.clientY }\n svg.classList.add('dragging')\n\n svg.addEventListener('mousemove', this.onPanMouseMove)\n svg.addEventListener('mouseup', this.onPanMouseUp)\n }\n\n protected onPanMouseMove = (event: MouseEvent) => {\n if (!this.isPanning) return\n\n const dx = event.clientX - this.panStart.x\n const dy = event.clientY - this.panStart.y\n\n const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)\n const scaleX = vbWidth / this.svg.clientWidth\n const scaleY = vbHeight / this.svg.clientHeight\n const scale = Math.max(scaleX, scaleY)\n\n const newVbX = vbX - dx * scale\n const newVbY = vbY - dy * scale\n\n this.viewBox = `${newVbX} ${newVbY} ${vbWidth} ${vbHeight}`\n this.panStart = { x: event.clientX, y: event.clientY }\n }\n\n protected onPanMouseUp = () => {\n const svg = this.svg\n\n this.isPanning = false\n svg.classList.remove('dragging')\n svg.removeEventListener('mousemove', this.onPanMouseMove)\n svg.removeEventListener('mouseup', this.onPanMouseUp)\n }\n\n protected onDoubleClick() {\n this.viewBox = this.initialViewBox\n this.zoomLevel = 1\n }\n\n protected renderShape(shape: Shape) {\n const commonAttrs = {\n stroke: 'red',\n 'stroke-width': 1,\n fill: 'transparent'\n }\n\n switch (shape.type) {\n case 'text':\n return svg`<text x=\"${shape.x}\" y=\"${shape.y}\" text-anchor=\"middle\" alignment-baseline=\"middle\" fill=\"black\">${shape.text}</text>`\n case 'rectangle':\n 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>`\n case 'circle':\n return svg`<circle cx=\"${shape.cx}\" cy=\"${shape.cy}\" r=\"${shape.r}\" stroke=\"${commonAttrs.stroke}\" stroke-width=\"${commonAttrs['stroke-width']}\" fill=\"${commonAttrs.fill}\"></circle>`\n case 'line':\n return svg`<line x1=\"${shape.x1}\" y1=\"${shape.y1}\" x2=\"${shape.x2}\" y2=\"${shape.y2}\" stroke=\"${commonAttrs.stroke}\" stroke-width=\"${commonAttrs['stroke-width']}\"></line>`\n case 'arrow':\n return svg`\n <defs>\n <marker id=\"arrowhead-${shape.id}\" markerWidth=\"10\" markerHeight=\"7\" refX=\"10\" refY=\"3.5\" orient=\"auto\">\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"${commonAttrs.stroke}\"></polygon>\n </marker>\n </defs>\n <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>`\n default:\n return nothing\n }\n }\n}\n"]}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"","sourcesContent":[""]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import '@material/web/icon/icon.js';
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
import { ImageMarkerMode, ShapeType } from './types';
|
|
4
|
+
export declare class OxImageMarkerMode extends LitElement {
|
|
5
|
+
static styles: import("lit").CSSResult;
|
|
6
|
+
currentMode: ImageMarkerMode;
|
|
7
|
+
currentShapeType?: ShapeType;
|
|
8
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
9
|
+
private setMode;
|
|
10
|
+
private setShape;
|
|
11
|
+
private onClickDelete;
|
|
12
|
+
private onClickSave;
|
|
13
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import '@material/web/icon/icon.js';
|
|
3
|
+
import { LitElement, html, css } from 'lit';
|
|
4
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
5
|
+
let OxImageMarkerMode = class OxImageMarkerMode extends LitElement {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.currentMode = 'select';
|
|
9
|
+
}
|
|
10
|
+
static { this.styles = css `
|
|
11
|
+
:host {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: var(--spacing-medium);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.palette {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--spacing-small);
|
|
21
|
+
justify-content: center;
|
|
22
|
+
margin: var(--spacing-medium);
|
|
23
|
+
|
|
24
|
+
--md-icon-size: 16px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.palette > * {
|
|
28
|
+
padding: var(--spacing-small);
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
background-color: #f0f0f0;
|
|
31
|
+
border: 1px solid #ccc;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
width: 12px;
|
|
34
|
+
height: 12px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.palette [selected] {
|
|
38
|
+
background-color: #d0d0d0;
|
|
39
|
+
}
|
|
40
|
+
`; }
|
|
41
|
+
render() {
|
|
42
|
+
return html `
|
|
43
|
+
<div class="palette">
|
|
44
|
+
<md-icon
|
|
45
|
+
title="Move or Select"
|
|
46
|
+
?selected=${this.currentMode !== 'draw'}
|
|
47
|
+
@click=${() => this.setMode(this.currentMode !== 'select' ? 'select' : 'move')}
|
|
48
|
+
>${this.currentMode !== 'move' ? 'select' : 'drag_pan'}</md-icon
|
|
49
|
+
>
|
|
50
|
+
|
|
51
|
+
<md-icon
|
|
52
|
+
title="Text"
|
|
53
|
+
?selected=${this.currentMode === 'draw' && this.currentShapeType === 'text'}
|
|
54
|
+
@click=${() => this.setShape('text')}
|
|
55
|
+
>match_case</md-icon
|
|
56
|
+
>
|
|
57
|
+
|
|
58
|
+
<md-icon
|
|
59
|
+
title="Rectangle"
|
|
60
|
+
?selected=${this.currentMode === 'draw' && this.currentShapeType === 'rectangle'}
|
|
61
|
+
@click=${() => this.setShape('rectangle')}
|
|
62
|
+
>rectangle</md-icon
|
|
63
|
+
>
|
|
64
|
+
|
|
65
|
+
<md-icon
|
|
66
|
+
title="Circle"
|
|
67
|
+
?selected=${this.currentMode === 'draw' && this.currentShapeType === 'circle'}
|
|
68
|
+
@click=${() => this.setShape('circle')}
|
|
69
|
+
>circle</md-icon
|
|
70
|
+
>
|
|
71
|
+
|
|
72
|
+
<md-icon
|
|
73
|
+
title="Triangle"
|
|
74
|
+
?selected=${this.currentMode === 'draw' && this.currentShapeType === 'triangle'}
|
|
75
|
+
@click=${() => this.setShape('triangle')}
|
|
76
|
+
>change_history</md-icon
|
|
77
|
+
>
|
|
78
|
+
|
|
79
|
+
<md-icon
|
|
80
|
+
title="Line"
|
|
81
|
+
?selected=${this.currentMode === 'draw' && this.currentShapeType === 'line'}
|
|
82
|
+
@click=${() => this.setShape('line')}
|
|
83
|
+
>shape_line</md-icon
|
|
84
|
+
>
|
|
85
|
+
|
|
86
|
+
<md-icon
|
|
87
|
+
title="Arrow"
|
|
88
|
+
?selected=${this.currentMode === 'draw' && this.currentShapeType === 'arrow'}
|
|
89
|
+
@click=${() => this.setShape('arrow')}
|
|
90
|
+
>line_end_arrow</md-icon
|
|
91
|
+
>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="palette">
|
|
95
|
+
<md-icon title="Delete" @click=${() => this.onClickDelete()}>delete</md-icon>
|
|
96
|
+
<md-icon title="Save" @click=${() => this.onClickSave()}>save</md-icon>
|
|
97
|
+
</div>
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
setMode(mode) {
|
|
101
|
+
this.currentMode = mode;
|
|
102
|
+
this.dispatchEvent(new CustomEvent('mode-changed', {
|
|
103
|
+
detail: { mode: this.currentMode, shapeType: this.currentShapeType },
|
|
104
|
+
bubbles: true,
|
|
105
|
+
composed: true
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
setShape(shapeType) {
|
|
109
|
+
this.currentMode = 'draw';
|
|
110
|
+
this.currentShapeType = shapeType;
|
|
111
|
+
this.dispatchEvent(new CustomEvent('mode-changed', {
|
|
112
|
+
detail: { mode: 'draw', shapeType: this.currentShapeType },
|
|
113
|
+
bubbles: true,
|
|
114
|
+
composed: true
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
onClickDelete() {
|
|
118
|
+
this.dispatchEvent(new CustomEvent('clicked-delete', {
|
|
119
|
+
bubbles: true,
|
|
120
|
+
composed: true
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
onClickSave() {
|
|
124
|
+
this.dispatchEvent(new CustomEvent('clicked-save', {
|
|
125
|
+
bubbles: true,
|
|
126
|
+
composed: true
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
__decorate([
|
|
131
|
+
property({ type: String })
|
|
132
|
+
], OxImageMarkerMode.prototype, "currentMode", void 0);
|
|
133
|
+
__decorate([
|
|
134
|
+
property({ type: String })
|
|
135
|
+
], OxImageMarkerMode.prototype, "currentShapeType", void 0);
|
|
136
|
+
OxImageMarkerMode = __decorate([
|
|
137
|
+
customElement('ox-image-marker-mode')
|
|
138
|
+
], OxImageMarkerMode);
|
|
139
|
+
export { OxImageMarkerMode };
|
|
140
|
+
//# sourceMappingURL=ox-image-marker-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-image-marker-mode.js","sourceRoot":"","sources":["../../src/ox-image-marker-mode.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { ImageMarkerMode, ShapeType } from './types'\n\n@customElement('ox-image-marker-mode')\nexport class OxImageMarkerMode extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium);\n }\n\n .palette {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-small);\n justify-content: center;\n margin: var(--spacing-medium);\n\n --md-icon-size: 16px;\n }\n\n .palette > * {\n padding: var(--spacing-small);\n cursor: pointer;\n background-color: #f0f0f0;\n border: 1px solid #ccc;\n border-radius: 4px;\n width: 12px;\n height: 12px;\n }\n\n .palette [selected] {\n background-color: #d0d0d0;\n }\n `\n\n @property({ type: String }) currentMode: ImageMarkerMode = 'select'\n @property({ type: String }) currentShapeType?: ShapeType\n\n render() {\n return html`\n <div class=\"palette\">\n <md-icon\n title=\"Move or Select\"\n ?selected=${this.currentMode !== 'draw'}\n @click=${() => this.setMode(this.currentMode !== 'select' ? 'select' : 'move')}\n >${this.currentMode !== 'move' ? 'select' : 'drag_pan'}</md-icon\n >\n\n <md-icon\n title=\"Text\"\n ?selected=${this.currentMode === 'draw' && this.currentShapeType === 'text'}\n @click=${() => this.setShape('text')}\n >match_case</md-icon\n >\n\n <md-icon\n title=\"Rectangle\"\n ?selected=${this.currentMode === 'draw' && this.currentShapeType === 'rectangle'}\n @click=${() => this.setShape('rectangle')}\n >rectangle</md-icon\n >\n\n <md-icon\n title=\"Circle\"\n ?selected=${this.currentMode === 'draw' && this.currentShapeType === 'circle'}\n @click=${() => this.setShape('circle')}\n >circle</md-icon\n >\n\n <md-icon\n title=\"Triangle\"\n ?selected=${this.currentMode === 'draw' && this.currentShapeType === 'triangle'}\n @click=${() => this.setShape('triangle')}\n >change_history</md-icon\n >\n\n <md-icon\n title=\"Line\"\n ?selected=${this.currentMode === 'draw' && this.currentShapeType === 'line'}\n @click=${() => this.setShape('line')}\n >shape_line</md-icon\n >\n\n <md-icon\n title=\"Arrow\"\n ?selected=${this.currentMode === 'draw' && this.currentShapeType === 'arrow'}\n @click=${() => this.setShape('arrow')}\n >line_end_arrow</md-icon\n >\n </div>\n\n <div class=\"palette\">\n <md-icon title=\"Delete\" @click=${() => this.onClickDelete()}>delete</md-icon>\n <md-icon title=\"Save\" @click=${() => this.onClickSave()}>save</md-icon>\n </div>\n `\n }\n\n private setMode(mode: ImageMarkerMode) {\n this.currentMode = mode\n\n this.dispatchEvent(\n new CustomEvent('mode-changed', {\n detail: { mode: this.currentMode, shapeType: this.currentShapeType },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private setShape(shapeType: ShapeType) {\n this.currentMode = 'draw'\n this.currentShapeType = shapeType\n this.dispatchEvent(\n new CustomEvent('mode-changed', {\n detail: { mode: 'draw', shapeType: this.currentShapeType },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onClickDelete() {\n this.dispatchEvent(\n new CustomEvent('clicked-delete', {\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onClickSave() {\n this.dispatchEvent(\n new CustomEvent('clicked-save', {\n bubbles: true,\n composed: true\n })\n )\n }\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { customElement } from 'lit/decorators.js';
|
|
4
|
+
import { BaseImageMarker } from './base-image-marker.js';
|
|
5
|
+
let OxImageMarkerView = class OxImageMarkerView extends BaseImageMarker {
|
|
6
|
+
render() {
|
|
7
|
+
return html `
|
|
8
|
+
<div class="container">
|
|
9
|
+
<svg
|
|
10
|
+
@wheel=${this.onWheel}
|
|
11
|
+
@mousedown=${this.onSvgMouseDown}
|
|
12
|
+
@dblclick=${this.onDoubleClick}
|
|
13
|
+
viewBox=${this.viewBox}
|
|
14
|
+
>
|
|
15
|
+
<image href=${this.imageUrl} @load=${this.onImageLoad}></image>
|
|
16
|
+
${this.shapes.map(shape => this.renderShape(shape))}
|
|
17
|
+
</svg>
|
|
18
|
+
</div>
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
OxImageMarkerView = __decorate([
|
|
23
|
+
customElement('ox-image-marker-view')
|
|
24
|
+
], OxImageMarkerView);
|
|
25
|
+
export { OxImageMarkerView };
|
|
26
|
+
//# sourceMappingURL=ox-image-marker-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-image-marker-view.js","sourceRoot":"","sources":["../../src/ox-image-marker-view.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAGjD,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,eAAe;IACpD,MAAM;QACJ,OAAO,IAAI,CAAA;;;mBAGI,IAAI,CAAC,OAAO;uBACR,IAAI,CAAC,cAAc;sBACpB,IAAI,CAAC,aAAa;oBACpB,IAAI,CAAC,OAAO;;wBAER,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,WAAW;YACnD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;;;KAGxD,CAAA;IACH,CAAC;CACF,CAAA;AAhBY,iBAAiB;IAD7B,aAAa,CAAC,sBAAsB,CAAC;GACzB,iBAAiB,CAgB7B","sourcesContent":["import { html } from 'lit'\nimport { customElement } from 'lit/decorators.js'\n\nimport { BaseImageMarker } from './base-image-marker.js'\n\n@customElement('ox-image-marker-view')\nexport class OxImageMarkerView extends BaseImageMarker {\n render() {\n return html`\n <div class=\"container\">\n <svg\n @wheel=${this.onWheel}\n @mousedown=${this.onSvgMouseDown}\n @dblclick=${this.onDoubleClick}\n viewBox=${this.viewBox}\n >\n <image href=${this.imageUrl} @load=${this.onImageLoad}></image>\n ${this.shapes.map(shape => this.renderShape(shape))}\n </svg>\n </div>\n `\n }\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import './ox-image-marker-mode';
|
|
2
|
+
import { nothing } from 'lit';
|
|
3
|
+
import { Shape } from './types';
|
|
4
|
+
import { BaseImageMarker } from './base-image-marker';
|
|
5
|
+
export declare class OxImageMarker extends BaseImageMarker {
|
|
6
|
+
static styles: import("lit").CSSResult[];
|
|
7
|
+
private currentShapeType?;
|
|
8
|
+
private currentMode;
|
|
9
|
+
private selectedShape;
|
|
10
|
+
private drawing;
|
|
11
|
+
private startX;
|
|
12
|
+
private startY;
|
|
13
|
+
private offsetX;
|
|
14
|
+
private offsetY;
|
|
15
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
16
|
+
protected onSvgMouseDown(event: MouseEvent): void;
|
|
17
|
+
private startShapeDragging;
|
|
18
|
+
private startDrawing;
|
|
19
|
+
private setMode;
|
|
20
|
+
private onMouseMove;
|
|
21
|
+
private onMouseUp;
|
|
22
|
+
private getSvgPoint;
|
|
23
|
+
protected renderShape(shape: Shape): import("lit-html").TemplateResult<2> | typeof nothing;
|
|
24
|
+
private onShapeMouseDown;
|
|
25
|
+
private onShapeMouseMove;
|
|
26
|
+
private onShapeMouseUp;
|
|
27
|
+
private getScale;
|
|
28
|
+
private renderResizeHandles;
|
|
29
|
+
private onHandleMouseDown;
|
|
30
|
+
private onHandleMouseMove;
|
|
31
|
+
private onHandleMouseUp;
|
|
32
|
+
private onTextDoubleClick;
|
|
33
|
+
}
|