@konfuzio/document-validation-ui 0.1.21-dev.0 → 0.1.21
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/dist/css/app.css +1 -1
- package/dist/index.html +1 -1
- package/dist/js/app.js +1 -1
- package/dist/js/app.js.map +1 -1
- package/dist/js/chunk-vendors.js +7 -7
- package/dist/js/chunk-vendors.js.map +1 -1
- package/jest.config.js +1 -0
- package/package.json +2 -3
- package/src/api.js +1 -3
- package/src/assets/scss/new_annotation.scss +4 -0
- package/src/components/DocumentAnnotations/AnnotationContent.vue +6 -5
- package/src/components/DocumentAnnotations/DocumentAnnotations.vue +3 -3
- package/src/components/DocumentAnnotations/EmptyAnnotation.vue +3 -3
- package/src/components/DocumentPage/DocumentPage.vue +14 -0
- package/src/components/DocumentPage/EditAnnotation.vue +340 -0
- package/src/components/DocumentPage/ScrollingPage.vue +1 -1
- package/src/store/display.js +36 -33
- package/src/store/document.js +38 -31
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konfuzio/document-validation-ui",
|
|
3
|
-
"version": "0.1.21
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"repository": "git://github.com:konfuzio-ai/document-validation-ui.git",
|
|
5
5
|
"main": "dist/app.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,8 +29,7 @@
|
|
|
29
29
|
"@fortawesome/vue-fontawesome": "^2.0.10",
|
|
30
30
|
"@sentry/tracing": "^6.19.4",
|
|
31
31
|
"@sentry/vue": "^6.2.0",
|
|
32
|
-
"axios": "^
|
|
33
|
-
"axios-extensions": "^3.1.6",
|
|
32
|
+
"axios": "^1.6.0",
|
|
34
33
|
"bignumber.js": "^9.1.0",
|
|
35
34
|
"buefy": "^0.9.22",
|
|
36
35
|
"konva": "^8.3.13",
|
package/src/api.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import { cacheAdapterEnhancer } from "axios-extensions";
|
|
3
2
|
|
|
4
3
|
let HTTP, FILE_REQUEST, authToken, appLocale;
|
|
5
4
|
const DEFAULT_URL = "https://app.konfuzio.com";
|
|
@@ -15,7 +14,6 @@ HTTP = axios.create({
|
|
|
15
14
|
FILE_REQUEST = axios.create({
|
|
16
15
|
baseURL: FILE_URL || `${DEFAULT_URL}`,
|
|
17
16
|
responseType: "blob",
|
|
18
|
-
adapter: cacheAdapterEnhancer(axios.defaults.adapter),
|
|
19
17
|
});
|
|
20
18
|
|
|
21
19
|
const setAuthToken = (token) => {
|
|
@@ -78,5 +76,5 @@ export default {
|
|
|
78
76
|
setLocale,
|
|
79
77
|
FILE_REQUEST,
|
|
80
78
|
DEFAULT_URL,
|
|
81
|
-
FILE_URL
|
|
79
|
+
FILE_URL,
|
|
82
80
|
};
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
editAnnotation &&
|
|
12
12
|
editAnnotation.id === annotation.id &&
|
|
13
13
|
'error-editing',
|
|
14
|
-
isAnnotationBeingEdited && 'clicked-ann'
|
|
15
|
-
|
|
14
|
+
isAnnotationBeingEdited && 'clicked-ann',
|
|
15
|
+
]"
|
|
16
16
|
role="textbox"
|
|
17
17
|
:contenteditable="isAnnotationBeingEdited"
|
|
18
18
|
@click="handleEditAnnotation"
|
|
@@ -119,9 +119,10 @@ export default {
|
|
|
119
119
|
.dispatch("document/setEditAnnotation", {
|
|
120
120
|
id: this.annotation.id,
|
|
121
121
|
index: this.spanIndex,
|
|
122
|
-
label: this.label
|
|
123
|
-
labelSet: this.annotationSet.label_set
|
|
124
|
-
annotationSet: this.annotationSet
|
|
122
|
+
label: this.label,
|
|
123
|
+
labelSet: this.annotationSet.label_set,
|
|
124
|
+
annotationSet: this.annotationSet,
|
|
125
|
+
pageNumber: this.span.page_index + 1,
|
|
125
126
|
})
|
|
126
127
|
.then(() => {
|
|
127
128
|
this.$refs.contentEditable.focus();
|
|
@@ -420,9 +420,9 @@ export default {
|
|
|
420
420
|
missing = [
|
|
421
421
|
{
|
|
422
422
|
document: parseInt(this.documentId),
|
|
423
|
-
label: this.editAnnotation.label,
|
|
424
|
-
label_set: this.editAnnotation.labelSet,
|
|
425
|
-
annotation_set: this.editAnnotation.annotationSet,
|
|
423
|
+
label: this.editAnnotation.label.id,
|
|
424
|
+
label_set: this.editAnnotation.labelSet.id,
|
|
425
|
+
annotation_set: this.editAnnotation.annotationSet.id,
|
|
426
426
|
},
|
|
427
427
|
];
|
|
428
428
|
} else if (annotationSet && markAllMissing) {
|
|
@@ -152,9 +152,9 @@ export default {
|
|
|
152
152
|
this.$store.dispatch("document/setEditAnnotation", {
|
|
153
153
|
id: this.emptyAnnotationId(),
|
|
154
154
|
index: this.spanIndex,
|
|
155
|
-
label: this.label
|
|
156
|
-
labelSet: this.annotationSet.label_set
|
|
157
|
-
annotationSet: this.annotationSet
|
|
155
|
+
label: this.label,
|
|
156
|
+
labelSet: this.annotationSet.label_set,
|
|
157
|
+
annotationSet: this.annotationSet,
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
160
|
},
|
|
@@ -18,6 +18,17 @@
|
|
|
18
18
|
:container-height="scaledViewport.height"
|
|
19
19
|
@close="closePopups"
|
|
20
20
|
/>
|
|
21
|
+
<EditAnnotation
|
|
22
|
+
v-if="
|
|
23
|
+
editAnnotation &&
|
|
24
|
+
editAnnotation.pageNumber &&
|
|
25
|
+
editAnnotation.pageNumber === currentPage
|
|
26
|
+
"
|
|
27
|
+
:edit-annotation="editAnnotation"
|
|
28
|
+
:page="page"
|
|
29
|
+
:container-width="scaledViewport.width"
|
|
30
|
+
:container-height="scaledViewport.height"
|
|
31
|
+
/>
|
|
21
32
|
|
|
22
33
|
<AnnSetTableOptions v-if="showAnnSetTable" :page="page" />
|
|
23
34
|
|
|
@@ -127,6 +138,7 @@ import api from "../../api";
|
|
|
127
138
|
import BoxSelection from "./BoxSelection";
|
|
128
139
|
import MultiAnnSelection from "./MultiAnnSelection";
|
|
129
140
|
import NewAnnotation from "./NewAnnotation";
|
|
141
|
+
import EditAnnotation from "./EditAnnotation";
|
|
130
142
|
import AnnSetTableOptions from "./AnnSetTableOptions";
|
|
131
143
|
|
|
132
144
|
export default {
|
|
@@ -135,6 +147,7 @@ export default {
|
|
|
135
147
|
BoxSelection,
|
|
136
148
|
MultiAnnSelection,
|
|
137
149
|
NewAnnotation,
|
|
150
|
+
EditAnnotation,
|
|
138
151
|
AnnSetTableOptions,
|
|
139
152
|
},
|
|
140
153
|
|
|
@@ -498,6 +511,7 @@ export default {
|
|
|
498
511
|
if (this.newAnnotation && this.newAnnotation.length > 0) {
|
|
499
512
|
entityIsSelected = this.newAnnotation.find((selectedEntity) => {
|
|
500
513
|
return (
|
|
514
|
+
selectedEntity.original &&
|
|
501
515
|
selectedEntity.original.offset_string ===
|
|
502
516
|
entity.original.offset_string &&
|
|
503
517
|
selectedEntity.original.x0 === entity.original.x0 &&
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="annotation"
|
|
4
|
+
class="annotation-popup small"
|
|
5
|
+
:style="{ left: `${left}px`, top: `${top}px` }"
|
|
6
|
+
>
|
|
7
|
+
<b-dropdown
|
|
8
|
+
v-model="selectedSet"
|
|
9
|
+
aria-role="list"
|
|
10
|
+
:class="[
|
|
11
|
+
'annotation-dropdown',
|
|
12
|
+
'no-padding-bottom',
|
|
13
|
+
setsList.length === 0 ? 'no-padding-top' : '',
|
|
14
|
+
]"
|
|
15
|
+
scrollable
|
|
16
|
+
>
|
|
17
|
+
<template #trigger>
|
|
18
|
+
<b-button
|
|
19
|
+
:class="['popup-input', selectedSet ? '' : 'not-selected']"
|
|
20
|
+
type="is-text"
|
|
21
|
+
>
|
|
22
|
+
{{
|
|
23
|
+
selectedSet
|
|
24
|
+
? `${selectedSet.label_set.name} ${
|
|
25
|
+
selectedSet.id
|
|
26
|
+
? numberOfAnnotationSetGroup(selectedSet)
|
|
27
|
+
: `(${$t("new")})`
|
|
28
|
+
}`
|
|
29
|
+
: $t("select_annotation_set")
|
|
30
|
+
}}
|
|
31
|
+
<span class="caret-icon">
|
|
32
|
+
<b-icon icon="angle-down" size="is-small" class="caret" />
|
|
33
|
+
</span>
|
|
34
|
+
</b-button>
|
|
35
|
+
</template>
|
|
36
|
+
<b-dropdown-item
|
|
37
|
+
v-for="(set, index) in setsList"
|
|
38
|
+
:key="`${set.label_set.id}_${index}`"
|
|
39
|
+
aria-role="listitem"
|
|
40
|
+
:value="set"
|
|
41
|
+
>
|
|
42
|
+
<span>{{
|
|
43
|
+
`${set.label_set.name} ${
|
|
44
|
+
set.id ? numberOfAnnotationSetGroup(set) : `(${$t("new")})`
|
|
45
|
+
}`
|
|
46
|
+
}}</span>
|
|
47
|
+
</b-dropdown-item>
|
|
48
|
+
<b-button
|
|
49
|
+
type="is-ghost"
|
|
50
|
+
:class="[
|
|
51
|
+
'add-ann-set',
|
|
52
|
+
'dropdown-item',
|
|
53
|
+
'no-icon-margin',
|
|
54
|
+
setsList.length > 0 ? 'has-border' : '',
|
|
55
|
+
]"
|
|
56
|
+
icon-left="plus"
|
|
57
|
+
@click="openAnnotationSetCreation"
|
|
58
|
+
>
|
|
59
|
+
{{ $t("new_ann_set_title") }}
|
|
60
|
+
</b-button>
|
|
61
|
+
</b-dropdown>
|
|
62
|
+
<b-tooltip
|
|
63
|
+
multilined
|
|
64
|
+
:active="selectedSet && (!labelsFiltered || labelsFiltered.length === 0)"
|
|
65
|
+
size="is-large"
|
|
66
|
+
position="is-bottom"
|
|
67
|
+
class="bottom-aligned"
|
|
68
|
+
:close-delay="5000"
|
|
69
|
+
>
|
|
70
|
+
<b-dropdown
|
|
71
|
+
v-if="selectedLabel"
|
|
72
|
+
v-model="selectedLabel"
|
|
73
|
+
aria-role="list"
|
|
74
|
+
:disabled="!labelsFiltered || labelsFiltered.length === 0"
|
|
75
|
+
scrollable
|
|
76
|
+
class="label-dropdown annotation-dropdown"
|
|
77
|
+
>
|
|
78
|
+
<template #trigger>
|
|
79
|
+
<b-button
|
|
80
|
+
class="popup-input"
|
|
81
|
+
:disabled="!labelsFiltered"
|
|
82
|
+
type="is-text"
|
|
83
|
+
>
|
|
84
|
+
{{ selectedLabel.name }}
|
|
85
|
+
<span class="caret-icon">
|
|
86
|
+
<b-icon icon="angle-down" size="is-small" class="caret" />
|
|
87
|
+
</span>
|
|
88
|
+
</b-button>
|
|
89
|
+
</template>
|
|
90
|
+
<b-dropdown-item
|
|
91
|
+
v-for="label in labelsFiltered"
|
|
92
|
+
:key="label.id"
|
|
93
|
+
aria-role="listitem"
|
|
94
|
+
:value="label"
|
|
95
|
+
>
|
|
96
|
+
<span>{{ label.name }}</span>
|
|
97
|
+
</b-dropdown-item>
|
|
98
|
+
</b-dropdown>
|
|
99
|
+
</b-tooltip>
|
|
100
|
+
<div class="annotation-buttons">
|
|
101
|
+
<b-button
|
|
102
|
+
type="is-text"
|
|
103
|
+
class="cancel-button popup-button primary-button"
|
|
104
|
+
:label="$t('cancel')"
|
|
105
|
+
:disabled="loading"
|
|
106
|
+
@click.prevent="close"
|
|
107
|
+
/>
|
|
108
|
+
<b-button
|
|
109
|
+
type="is-primary"
|
|
110
|
+
class="popup-button primary-button"
|
|
111
|
+
:label="$t('save')"
|
|
112
|
+
:disabled="loading || !spanSelection || !selectedLabel"
|
|
113
|
+
@click.prevent="save"
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
<script>
|
|
119
|
+
/**
|
|
120
|
+
* This component is used to show a popup
|
|
121
|
+
* for creating a new annotation.
|
|
122
|
+
*/
|
|
123
|
+
const heightOfPopup = 142;
|
|
124
|
+
const margin = 12;
|
|
125
|
+
const widthOfPopup = 205;
|
|
126
|
+
|
|
127
|
+
import { mapGetters, mapState } from "vuex";
|
|
128
|
+
import { MULTI_ANN_TABLE_FEATURE } from "../../constants";
|
|
129
|
+
|
|
130
|
+
export default {
|
|
131
|
+
props: {
|
|
132
|
+
editAnnotation: {
|
|
133
|
+
required: true,
|
|
134
|
+
type: Object,
|
|
135
|
+
},
|
|
136
|
+
page: {
|
|
137
|
+
required: true,
|
|
138
|
+
type: Object,
|
|
139
|
+
},
|
|
140
|
+
containerWidth: {
|
|
141
|
+
type: Number,
|
|
142
|
+
required: true,
|
|
143
|
+
},
|
|
144
|
+
containerHeight: {
|
|
145
|
+
type: Number,
|
|
146
|
+
required: true,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
data() {
|
|
150
|
+
return {
|
|
151
|
+
annotation: null,
|
|
152
|
+
selectedLabel: null,
|
|
153
|
+
selectedSet: null,
|
|
154
|
+
labelsFiltered: null,
|
|
155
|
+
loading: false,
|
|
156
|
+
isAnnSetModalShowing: false,
|
|
157
|
+
setsList: [],
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
computed: {
|
|
161
|
+
...mapState("document", [
|
|
162
|
+
"annotationSets",
|
|
163
|
+
"annotations",
|
|
164
|
+
"labels",
|
|
165
|
+
"documentId",
|
|
166
|
+
]),
|
|
167
|
+
...mapGetters("document", [
|
|
168
|
+
"numberOfAnnotationSetGroup",
|
|
169
|
+
"labelsFilteredForAnnotationCreation",
|
|
170
|
+
"isNegative",
|
|
171
|
+
]),
|
|
172
|
+
...mapGetters("display", ["bboxToRect"]),
|
|
173
|
+
...mapState("selection", ["selection", "spanSelection"]),
|
|
174
|
+
top() {
|
|
175
|
+
const top = this.selection.start.y - heightOfPopup; // subtract the height of the popup plus some margin
|
|
176
|
+
|
|
177
|
+
const height = this.selection.end.y - this.selection.start.y;
|
|
178
|
+
|
|
179
|
+
//check if the popup will not go off the container on the top
|
|
180
|
+
return this.selection.start.y > heightOfPopup
|
|
181
|
+
? top
|
|
182
|
+
: this.selection.start.y + height + margin;
|
|
183
|
+
},
|
|
184
|
+
left() {
|
|
185
|
+
const width = this.selection.end.x - this.selection.start.x;
|
|
186
|
+
|
|
187
|
+
const left = this.selection.start.x + width / 2 - widthOfPopup / 2; // add the entity half width to be centered and then subtract half the width of the popup
|
|
188
|
+
|
|
189
|
+
//check if the popup will not go off the container
|
|
190
|
+
if (left + widthOfPopup > this.containerWidth) {
|
|
191
|
+
// on the right side
|
|
192
|
+
return this.containerWidth - widthOfPopup;
|
|
193
|
+
} else {
|
|
194
|
+
// on the left side
|
|
195
|
+
return left > 0 ? left : 0;
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
watch: {
|
|
200
|
+
selectedSet(newValue) {
|
|
201
|
+
this.labelsFiltered = this.labelsFilteredForAnnotationCreation(newValue);
|
|
202
|
+
},
|
|
203
|
+
editAnnotation() {
|
|
204
|
+
this.loadInfo();
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
mounted() {
|
|
208
|
+
this.loadInfo();
|
|
209
|
+
|
|
210
|
+
setTimeout(() => {
|
|
211
|
+
// prevent click propagation when opening the popup
|
|
212
|
+
document.body.addEventListener("click", this.clickOutside);
|
|
213
|
+
}, 200);
|
|
214
|
+
},
|
|
215
|
+
destroyed() {
|
|
216
|
+
document.body.removeEventListener("click", this.clickOutside);
|
|
217
|
+
},
|
|
218
|
+
methods: {
|
|
219
|
+
loadInfo() {
|
|
220
|
+
this.setsList = [...this.annotationSets];
|
|
221
|
+
|
|
222
|
+
this.selectedSet = this.annotationSets.find(
|
|
223
|
+
(annSet) => annSet.id === this.editAnnotation.annotationSet.id
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
this.labelsFiltered = this.labelsFilteredForAnnotationCreation(
|
|
227
|
+
this.selectedSet
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// if existing label is not able to be chosen we add it manually
|
|
231
|
+
if (!this.labelsFiltered.includes(this.editAnnotation.label)) {
|
|
232
|
+
this.labelsFiltered.push(this.editAnnotation.label);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.selectedLabel = this.editAnnotation.label;
|
|
236
|
+
|
|
237
|
+
this.annotation = this.annotations.find(
|
|
238
|
+
(ann) => ann.id === this.editAnnotation.id
|
|
239
|
+
);
|
|
240
|
+
},
|
|
241
|
+
close() {
|
|
242
|
+
this.$store.dispatch("document/resetEditAnnotation");
|
|
243
|
+
this.$store.dispatch("selection/disableSelection");
|
|
244
|
+
this.$store.dispatch("selection/setSelectedEntities", null);
|
|
245
|
+
this.$emit("close");
|
|
246
|
+
},
|
|
247
|
+
async save() {
|
|
248
|
+
this.loading = true;
|
|
249
|
+
|
|
250
|
+
if (
|
|
251
|
+
this.editAnnotation.labelSet.id !== this.selectedSet.id ||
|
|
252
|
+
this.editAnnotation.label.id !== this.selectedLabel.id
|
|
253
|
+
) {
|
|
254
|
+
// first delete annotation, then create new one
|
|
255
|
+
await this.$store
|
|
256
|
+
.dispatch("document/deleteAnnotation", {
|
|
257
|
+
annotationId: this.annotation.id,
|
|
258
|
+
})
|
|
259
|
+
.catch((error) => {
|
|
260
|
+
this.$store.dispatch("document/createErrorMessage", {
|
|
261
|
+
error,
|
|
262
|
+
serverErrorMessage: this.$t("server_error"),
|
|
263
|
+
defaultErrorMessage: this.$t("edit_error"),
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const spans = this.annotation.span;
|
|
268
|
+
spans[this.editAnnotation.index] = this.spanSelection;
|
|
269
|
+
|
|
270
|
+
const annotationToCreate = {
|
|
271
|
+
document: this.documentId,
|
|
272
|
+
span: spans,
|
|
273
|
+
label: this.selectedLabel.id,
|
|
274
|
+
is_correct: true,
|
|
275
|
+
revised: false,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
if (this.selectedSet.id) {
|
|
279
|
+
annotationToCreate.annotation_set = this.selectedSet.id;
|
|
280
|
+
} else {
|
|
281
|
+
annotationToCreate.label_set = this.selectedSet.label_set.id;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// check if the selected label already has a negative annotation
|
|
285
|
+
let negativeAnnotationId;
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
this.selectedLabel.annotations &&
|
|
289
|
+
this.selectedLabel.annotations.length > 0
|
|
290
|
+
) {
|
|
291
|
+
const negativeAnnotation = this.selectedLabel.annotations.find(
|
|
292
|
+
(annotation) => this.isNegative(annotation)
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
if (negativeAnnotation) {
|
|
296
|
+
negativeAnnotationId = negativeAnnotation.id;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
this.$store
|
|
301
|
+
.dispatch("document/createAnnotation", {
|
|
302
|
+
annotation: annotationToCreate,
|
|
303
|
+
negativeAnnotationId: negativeAnnotationId,
|
|
304
|
+
})
|
|
305
|
+
.catch((error) => {
|
|
306
|
+
this.$store.dispatch("document/createErrorMessage", {
|
|
307
|
+
error,
|
|
308
|
+
serverErrorMessage: this.$t("server_error"),
|
|
309
|
+
defaultErrorMessage: this.$t("error_creating_annotation"),
|
|
310
|
+
});
|
|
311
|
+
})
|
|
312
|
+
.finally(() => {
|
|
313
|
+
this.close();
|
|
314
|
+
this.loading = false;
|
|
315
|
+
});
|
|
316
|
+
} else {
|
|
317
|
+
this.close();
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
chooseLabelSet(labelSet) {
|
|
321
|
+
const newSet = {
|
|
322
|
+
label_set: labelSet,
|
|
323
|
+
labels: labelSet.labels,
|
|
324
|
+
id: null,
|
|
325
|
+
};
|
|
326
|
+
this.setsList.push(newSet);
|
|
327
|
+
this.selectedSet = newSet;
|
|
328
|
+
},
|
|
329
|
+
openAnnotationSetCreation() {
|
|
330
|
+
this.$store.dispatch("display/showChooseLabelSetModal", {
|
|
331
|
+
show: true,
|
|
332
|
+
isMultipleAnnotations: MULTI_ANN_TABLE_FEATURE,
|
|
333
|
+
finish: this.chooseLabelSet,
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
</script>
|
|
339
|
+
|
|
340
|
+
<style scoped lang="scss" src="../../assets/scss/new_annotation.scss"></style>
|
|
@@ -112,7 +112,7 @@ export default {
|
|
|
112
112
|
this.$nextTick(() => {
|
|
113
113
|
// Scroll to the annotation
|
|
114
114
|
this.scrollTo(
|
|
115
|
-
this.getYForBbox(this.documentAnnotationSelected.span),
|
|
115
|
+
this.getYForBbox(this.documentAnnotationSelected.span) - 100, // offset for edit annotation popup
|
|
116
116
|
this.getXForBbox(this.documentAnnotationSelected.span)
|
|
117
117
|
);
|
|
118
118
|
});
|
package/src/store/display.js
CHANGED
|
@@ -90,39 +90,42 @@ const getters = {
|
|
|
90
90
|
(state, getters) =>
|
|
91
91
|
(page, bbox, hasOffset = false) => {
|
|
92
92
|
const imageScale = getters.imageScale(page);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
93
|
+
if (bbox.x0 && bbox.y0) {
|
|
94
|
+
const { x0, x1, y0, y1 } = bbox;
|
|
95
|
+
const pageHeight = new BigNumber(page.original_size[1]);
|
|
96
|
+
const rect = {
|
|
97
|
+
// left
|
|
98
|
+
x: new BigNumber(x0)
|
|
99
|
+
.minus(hasOffset ? 1 : 0)
|
|
100
|
+
.times(state.scale)
|
|
101
|
+
.times(imageScale)
|
|
102
|
+
.div(PIXEL_RATIO)
|
|
103
|
+
.toNumber(),
|
|
104
|
+
// top
|
|
105
|
+
y: pageHeight
|
|
106
|
+
.minus(new BigNumber(y1))
|
|
107
|
+
.minus(hasOffset ? 17.1 : 0)
|
|
108
|
+
.times(state.scale)
|
|
109
|
+
.times(imageScale)
|
|
110
|
+
.div(PIXEL_RATIO)
|
|
111
|
+
.toNumber(),
|
|
112
|
+
width: new BigNumber(x1)
|
|
113
|
+
.minus(x0)
|
|
114
|
+
.abs()
|
|
115
|
+
.times(state.scale)
|
|
116
|
+
.times(imageScale)
|
|
117
|
+
.div(PIXEL_RATIO)
|
|
118
|
+
.toNumber(),
|
|
119
|
+
height: new BigNumber(y1)
|
|
120
|
+
.minus(y0)
|
|
121
|
+
.times(state.scale)
|
|
122
|
+
.times(imageScale)
|
|
123
|
+
.div(PIXEL_RATIO)
|
|
124
|
+
.toNumber(),
|
|
125
|
+
};
|
|
126
|
+
return rect;
|
|
127
|
+
}
|
|
128
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
126
129
|
},
|
|
127
130
|
clientToBbox: (state, getters) => (page, start, end) => {
|
|
128
131
|
/**
|