@konfuzio/document-validation-ui 0.1.59 → 0.2.0-dev.2
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/cypress.config.js +6 -6
- 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 +66 -23
- package/dist/js/chunk-vendors.js.map +1 -1
- package/jest.config.js +22 -2
- package/package.json +32 -38
- package/src/assets/scss/ann_set_table_options.scss +4 -4
- package/src/assets/scss/annotation_action_buttons.scss +29 -7
- package/src/assets/scss/annotation_details.scss +9 -9
- package/src/assets/scss/choose_label_set_modal.scss +5 -5
- package/src/assets/scss/document_action_bar.scss +3 -3
- package/src/assets/scss/document_annotations.scss +45 -45
- package/src/assets/scss/document_category.scss +8 -8
- package/src/assets/scss/document_dashboard.scss +6 -1
- package/src/assets/scss/document_edit.scss +30 -30
- package/src/assets/scss/document_error.scss +6 -6
- package/src/assets/scss/document_name.scss +6 -6
- package/src/assets/scss/document_page.scss +3 -3
- package/src/assets/scss/document_search_bar.scss +7 -7
- package/src/assets/scss/document_set_chooser.scss +3 -3
- package/src/assets/scss/document_thumbnails.scss +7 -7
- package/src/assets/scss/document_toolbar.scss +10 -10
- package/src/assets/scss/document_top_bar.scss +11 -11
- package/src/assets/scss/document_viewport_modal.scss +3 -3
- package/src/assets/scss/documents_list.scss +11 -12
- package/src/assets/scss/edit_page_thumbnail.scss +6 -6
- package/src/assets/scss/empty_state.scss +4 -4
- package/src/assets/scss/error_page.scss +2 -2
- package/src/assets/scss/extracting_data.scss +3 -3
- package/src/assets/scss/multi_ann_table_overlay.scss +3 -3
- package/src/assets/scss/multi_ann_table_popup.scss +1 -1
- package/src/assets/scss/new_annotation.scss +25 -19
- package/src/assets/scss/scrolling_document.scss +1 -1
- package/src/assets/scss/theme.scss +64 -52
- package/src/assets/scss/variables.scss +2 -0
- package/src/components/App.vue +9 -14
- package/src/components/DocumentAnnotations/AnnotationActionButtons.vue +31 -7
- package/src/components/DocumentAnnotations/AnnotationContent.vue +25 -52
- package/src/components/DocumentAnnotations/AnnotationRow.vue +108 -51
- package/src/components/DocumentAnnotations/DocumentAnnotations.vue +12 -6
- package/src/components/DocumentAnnotations/DocumentLabel.vue +12 -122
- package/src/components/DocumentAnnotations/EmptyAnnotation.vue +31 -70
- package/src/components/DocumentDashboard.vue +12 -17
- package/src/components/DocumentEdit/EditPages.vue +51 -46
- package/src/components/DocumentEdit/EditSidebar.vue +0 -9
- package/src/components/DocumentPage/{NewAnnotation.vue → AnnotationPopup.vue} +123 -94
- package/src/components/DocumentPage/BoxSelection.vue +16 -49
- package/src/components/DocumentPage/DocumentPage.vue +56 -153
- package/src/components/DocumentPage/DocumentToolbar.vue +0 -1
- package/src/components/DocumentPage/PlaceholderSelection.vue +51 -0
- package/src/components/DocumentPage/SpanSelection.vue +259 -0
- package/src/components/DocumentThumbnails/LoadingThumbnail.vue +3 -6
- package/src/components/DocumentTopBar/DocumentTopBar.vue +4 -2
- package/src/constants.js +1 -7
- package/src/i18n.js +2 -5
- package/src/locales/de.json +2 -1
- package/src/locales/en.json +2 -1
- package/src/locales/es.json +2 -1
- package/src/main.js +14 -16
- package/src/store/display.js +33 -22
- package/src/store/document.js +131 -10
- package/src/store/index.js +5 -8
- package/src/store/selection.js +152 -76
- package/src/assets/scss/imports.scss +0 -1
- package/src/components/DocumentPage/EditAnnotation.vue +0 -372
package/src/store/selection.js
CHANGED
|
@@ -9,30 +9,24 @@ const state = {
|
|
|
9
9
|
pageNumber: null,
|
|
10
10
|
start: null,
|
|
11
11
|
end: null,
|
|
12
|
-
custom: false, // if the box was created by user in document or it comes from an annotation
|
|
13
|
-
placeholderBox: null, // show a not editable placeholder box
|
|
14
12
|
},
|
|
15
13
|
isSelecting: false,
|
|
16
|
-
spanSelection:
|
|
17
|
-
|
|
18
|
-
selectedEntities:
|
|
14
|
+
spanSelection: [],
|
|
15
|
+
placeholderSelection: [],
|
|
16
|
+
selectedEntities: [],
|
|
17
|
+
spanLoading: false,
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
const getters = {
|
|
22
|
-
isElementSelected: (state) => {
|
|
23
|
-
return state.elementSelected;
|
|
24
|
-
},
|
|
25
|
-
isSelecting: (state) => {
|
|
26
|
-
return state.isSelecting;
|
|
27
|
-
},
|
|
28
21
|
isSelectionValid: (state) => {
|
|
29
22
|
/**
|
|
30
23
|
* `endSelection` will reset everything in case of invalid selection.
|
|
31
24
|
* Check the existence of `selection.end` before requesting the
|
|
32
25
|
* content from the backend.
|
|
33
26
|
* */
|
|
34
|
-
return state.selection && state.selection.end;
|
|
27
|
+
return state.selection && state.selection.end != null;
|
|
35
28
|
},
|
|
29
|
+
|
|
36
30
|
getSelectionForPage: (state) => (pageNumber) => {
|
|
37
31
|
if (state.selection.pageNumber === pageNumber) {
|
|
38
32
|
return state.selection;
|
|
@@ -52,19 +46,24 @@ const getters = {
|
|
|
52
46
|
box.y1 >= entity.y1
|
|
53
47
|
);
|
|
54
48
|
},
|
|
49
|
+
spanSelectionsForPage: (state) => (page) => {
|
|
50
|
+
return state.spanSelection.filter(
|
|
51
|
+
(span) => page.number === span.page_index + 1
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
placeholderSelectionForPage: (state) => (page) => {
|
|
55
|
+
return state.placeholderSelection.filter(
|
|
56
|
+
(span) => page.number === span.page_index + 1
|
|
57
|
+
);
|
|
58
|
+
},
|
|
55
59
|
};
|
|
56
60
|
|
|
57
61
|
const actions = {
|
|
58
|
-
selectElement: ({ commit }, value) => {
|
|
59
|
-
commit("RESET_SELECTION");
|
|
60
|
-
commit("SET_SPAN_SELECTION", null);
|
|
61
|
-
commit("ELEMENT_SELECTED", value);
|
|
62
|
-
},
|
|
63
|
-
|
|
64
62
|
disableSelection: ({ commit }) => {
|
|
65
|
-
commit("ELEMENT_SELECTED", null);
|
|
66
63
|
commit("RESET_SELECTION");
|
|
67
|
-
commit("
|
|
64
|
+
commit("SET_SELECTED_ENTITIES", []);
|
|
65
|
+
commit("SET_SPAN_SELECTION", []);
|
|
66
|
+
commit("SET_PLACEHOLDER_SELECTION", []);
|
|
68
67
|
},
|
|
69
68
|
|
|
70
69
|
startSelection: ({ commit }, { pageNumber, start }) => {
|
|
@@ -81,8 +80,6 @@ const actions = {
|
|
|
81
80
|
if (xDiff > 5 && yDiff > 5) {
|
|
82
81
|
commit("MOVE_SELECTION", points);
|
|
83
82
|
}
|
|
84
|
-
|
|
85
|
-
commit("SET_SELECTED_ENTITIES", null);
|
|
86
83
|
},
|
|
87
84
|
|
|
88
85
|
endSelection: ({ commit, state }, end) => {
|
|
@@ -113,78 +110,138 @@ const actions = {
|
|
|
113
110
|
}
|
|
114
111
|
},
|
|
115
112
|
|
|
116
|
-
setSelection: ({ commit }, { span, selection }) => {
|
|
117
|
-
commit("SET_SELECTION", selection);
|
|
118
|
-
commit("SET_SPAN_SELECTION", span);
|
|
119
|
-
},
|
|
120
|
-
|
|
121
113
|
setSelectedEntities: ({ commit }, entities) => {
|
|
122
114
|
commit("SET_SELECTED_ENTITIES", entities);
|
|
123
115
|
},
|
|
124
116
|
|
|
125
|
-
getTextFromBboxes: ({ commit, rootState },
|
|
126
|
-
|
|
117
|
+
getTextFromBboxes: ({ commit, rootState }, span) => {
|
|
118
|
+
commit("SET_SPAN_LOADING", true);
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
HTTP.post(`documents/${rootState.document.documentId}/bbox/`, {
|
|
121
|
+
span,
|
|
122
|
+
})
|
|
123
|
+
.then((response) => {
|
|
124
|
+
if (response.data.span.length && response.data.span.length > 0) {
|
|
125
|
+
/**
|
|
126
|
+
* If we have a non-empty bboxes list, we assume there
|
|
127
|
+
* is text here on the backend, so we just set
|
|
128
|
+
* spanSelection to the response.
|
|
129
|
+
*/
|
|
130
|
+
resolve(response.data.span);
|
|
131
|
+
} else {
|
|
132
|
+
/**
|
|
133
|
+
* Otherwise, we assume the backend can't identify text
|
|
134
|
+
* on this area, so we set our bbox into spanSelection
|
|
135
|
+
* ready to be passed back to the backend when creating
|
|
136
|
+
* an annotation on this empty area, adding the offset_string
|
|
137
|
+
* attribute, ready to be filled.
|
|
138
|
+
*/
|
|
139
|
+
resolve(span);
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
.catch((error) => {
|
|
143
|
+
alert("Could not fetch the selected text from the backend");
|
|
144
|
+
reject(error);
|
|
145
|
+
})
|
|
146
|
+
.finally(() => {
|
|
147
|
+
commit("SET_SPAN_LOADING", false);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
},
|
|
127
151
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
entitySelection: ({ commit, dispatch, state }, { entities, selection }) => {
|
|
153
|
+
if (entities.length === 0) {
|
|
154
|
+
if (selection) {
|
|
155
|
+
dispatch("getTextFromBboxes", [selection]).then((spans) => {
|
|
156
|
+
commit("SET_SPAN_SELECTION", spans);
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
commit("RESET_SELECTION");
|
|
160
|
+
commit("SET_SPAN_SELECTION", []);
|
|
161
|
+
}
|
|
162
|
+
commit("SET_SELECTED_ENTITIES", []);
|
|
163
|
+
} else {
|
|
164
|
+
commit("SET_SELECTED_ENTITIES", entities);
|
|
165
|
+
|
|
166
|
+
dispatch("document/setAnnotationId", null, {
|
|
167
|
+
root: true,
|
|
168
|
+
});
|
|
169
|
+
let span;
|
|
170
|
+
if (entities) {
|
|
171
|
+
span = entities.flatMap((s) => {
|
|
172
|
+
return s.original;
|
|
173
|
+
});
|
|
174
|
+
} else {
|
|
175
|
+
span = [entities];
|
|
176
|
+
}
|
|
177
|
+
commit("SET_SPAN_SELECTION", span);
|
|
178
|
+
dispatch("getTextFromBboxes", span).then((spans) => {
|
|
179
|
+
commit("SET_SPAN_SELECTION", spans);
|
|
131
180
|
});
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
entityClick: ({ commit, dispatch, state }, entity) => {
|
|
185
|
+
// Check if we are creating a new Annotation
|
|
186
|
+
// or if we are removing a previously selected entity
|
|
187
|
+
// or editing empty one
|
|
188
|
+
const found = state.selectedEntities.find(
|
|
189
|
+
(entityToFind) =>
|
|
190
|
+
entity.scaled.width === entityToFind.scaled.width &&
|
|
191
|
+
entity.original.offset_string === entityToFind.original.offset_string
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
let entities = state.selectedEntities;
|
|
195
|
+
if (found) {
|
|
196
|
+
entities = [
|
|
197
|
+
...state.selectedEntities.filter(
|
|
198
|
+
(entityToFilter) =>
|
|
199
|
+
entityToFilter.scaled.width !== entity.scaled.width &&
|
|
200
|
+
entityToFilter.original.offset_string !==
|
|
201
|
+
entity.original.offset_string
|
|
202
|
+
),
|
|
203
|
+
];
|
|
132
204
|
} else {
|
|
133
|
-
|
|
205
|
+
entities.push(entity);
|
|
134
206
|
}
|
|
135
207
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* spanSelection to the response.
|
|
145
|
-
*/
|
|
146
|
-
commit("SET_SPAN_SELECTION", response.data.span);
|
|
147
|
-
} else {
|
|
148
|
-
/**
|
|
149
|
-
* Otherwise, we assume the backend can't identify text
|
|
150
|
-
* on this area, so we set our bbox into spanSelection
|
|
151
|
-
* ready to be passed back to the backend when creating
|
|
152
|
-
* an annotation on this empty area, adding the offset_string
|
|
153
|
-
* attribute, ready to be filled.
|
|
154
|
-
*/
|
|
155
|
-
commit("SET_SPAN_SELECTION", span);
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
.catch((error) => {
|
|
159
|
-
alert("Could not fetch the selected text from the backend");
|
|
208
|
+
if (entities.length === 0) {
|
|
209
|
+
commit("SET_SELECTED_ENTITIES", []);
|
|
210
|
+
commit("SET_SPAN_SELECTION", []);
|
|
211
|
+
} else {
|
|
212
|
+
commit("SET_SELECTED_ENTITIES", entities);
|
|
213
|
+
|
|
214
|
+
dispatch("document/setAnnotationId", null, {
|
|
215
|
+
root: true,
|
|
160
216
|
});
|
|
161
|
-
},
|
|
162
217
|
|
|
163
|
-
|
|
164
|
-
if (!selectedEntities) return;
|
|
218
|
+
let span;
|
|
165
219
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
220
|
+
if (entities) {
|
|
221
|
+
span = entities.flatMap((s) => {
|
|
222
|
+
return s.original;
|
|
223
|
+
});
|
|
224
|
+
} else {
|
|
225
|
+
span = [entities];
|
|
226
|
+
}
|
|
227
|
+
commit("SET_SPAN_SELECTION", span);
|
|
228
|
+
}
|
|
170
229
|
},
|
|
171
230
|
|
|
172
231
|
setSpanSelection: ({ commit }, span) => {
|
|
173
232
|
commit("SET_SPAN_SELECTION", span);
|
|
174
233
|
},
|
|
234
|
+
setPlaceholderSelection: ({ commit }, span) => {
|
|
235
|
+
commit("SET_PLACEHOLDER_SELECTION", span);
|
|
236
|
+
},
|
|
175
237
|
};
|
|
176
238
|
|
|
177
239
|
const mutations = {
|
|
178
|
-
ELEMENT_SELECTED: (state, value) => {
|
|
179
|
-
state.elementSelected = value;
|
|
180
|
-
},
|
|
181
240
|
START_SELECTION: (state, { pageNumber, start }) => {
|
|
182
241
|
state.selection.end = null;
|
|
183
|
-
state.isSelecting = true;
|
|
184
242
|
state.selection.pageNumber = pageNumber;
|
|
185
|
-
state.selection.custom = true;
|
|
186
243
|
state.selection.start = start;
|
|
187
|
-
state.
|
|
244
|
+
state.isSelecting = true;
|
|
188
245
|
},
|
|
189
246
|
MOVE_SELECTION: (state, points) => {
|
|
190
247
|
const { start, end } = points;
|
|
@@ -200,20 +257,39 @@ const mutations = {
|
|
|
200
257
|
state.isSelecting = false;
|
|
201
258
|
},
|
|
202
259
|
RESET_SELECTION: (state) => {
|
|
203
|
-
state.isSelecting = false;
|
|
204
260
|
state.selection.pageNumber = null;
|
|
205
261
|
state.selection.start = null;
|
|
206
262
|
state.selection.end = null;
|
|
207
|
-
state.selection.placeholderBox = null;
|
|
208
263
|
},
|
|
209
264
|
SET_SPAN_SELECTION: (state, span) => {
|
|
210
|
-
|
|
265
|
+
if (!span) {
|
|
266
|
+
state.spanSelection = [];
|
|
267
|
+
} else {
|
|
268
|
+
state.spanSelection = span;
|
|
269
|
+
}
|
|
211
270
|
},
|
|
212
|
-
|
|
213
|
-
state.
|
|
271
|
+
ADD_SPAN_SELECTION: (state, span) => {
|
|
272
|
+
state.spanSelection.push(span);
|
|
273
|
+
},
|
|
274
|
+
SET_PLACEHOLDER_SELECTION: (state, span) => {
|
|
275
|
+
if (!span) {
|
|
276
|
+
state.placeholderSelection = [];
|
|
277
|
+
} else {
|
|
278
|
+
state.placeholderSelection = span;
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
ADD_PLACEHOLDER_SELECTION: (state, span) => {
|
|
282
|
+
state.placeholderSelection.push(span);
|
|
214
283
|
},
|
|
215
284
|
SET_SELECTED_ENTITIES: (state, entities) => {
|
|
216
|
-
|
|
285
|
+
if (!entities) {
|
|
286
|
+
state.selectedEntities = [];
|
|
287
|
+
} else {
|
|
288
|
+
state.selectedEntities = entities;
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
SET_SPAN_LOADING: (state, loading) => {
|
|
292
|
+
state.spanLoading = loading;
|
|
217
293
|
},
|
|
218
294
|
};
|
|
219
295
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@import "./variables.scss";
|
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
<!-- eslint-disable vue/no-v-html -->
|
|
2
|
-
<template>
|
|
3
|
-
<div
|
|
4
|
-
v-if="annotation && !hide"
|
|
5
|
-
class="annotation-popup small"
|
|
6
|
-
:style="{ left: `${left}px`, top: `${top}px` }"
|
|
7
|
-
>
|
|
8
|
-
<b-dropdown
|
|
9
|
-
v-model="selectedSet"
|
|
10
|
-
aria-role="list"
|
|
11
|
-
:class="[
|
|
12
|
-
'annotation-dropdown',
|
|
13
|
-
'no-padding-bottom',
|
|
14
|
-
'dropdown-full-width',
|
|
15
|
-
setsList.length === 0 ? 'no-padding-top' : '',
|
|
16
|
-
]"
|
|
17
|
-
scrollable
|
|
18
|
-
>
|
|
19
|
-
<template #trigger>
|
|
20
|
-
<b-button
|
|
21
|
-
:class="[
|
|
22
|
-
'popup-input',
|
|
23
|
-
selectedSet ? '' : 'not-selected',
|
|
24
|
-
'has-right-icon',
|
|
25
|
-
]"
|
|
26
|
-
type="is-text"
|
|
27
|
-
>
|
|
28
|
-
{{
|
|
29
|
-
selectedSet
|
|
30
|
-
? `${selectedSet.label_set.name} ${
|
|
31
|
-
selectedSet.id
|
|
32
|
-
? numberOfAnnotationSetGroup(selectedSet)
|
|
33
|
-
: `${numberOfLabelSetGroup(selectedSet.label_set)} (${$t(
|
|
34
|
-
"new"
|
|
35
|
-
)})`
|
|
36
|
-
}`
|
|
37
|
-
: $t("select_annotation_set")
|
|
38
|
-
}}
|
|
39
|
-
<span class="caret-icon">
|
|
40
|
-
<b-icon icon="angle-down" size="is-small" class="caret" />
|
|
41
|
-
</span>
|
|
42
|
-
</b-button>
|
|
43
|
-
</template>
|
|
44
|
-
<b-button
|
|
45
|
-
type="is-ghost"
|
|
46
|
-
:class="['add-ann-set', 'dropdown-item', 'no-icon-margin']"
|
|
47
|
-
icon-left="plus"
|
|
48
|
-
@click="openAnnotationSetCreation"
|
|
49
|
-
>
|
|
50
|
-
{{ $t("new_ann_set_title") }}
|
|
51
|
-
</b-button>
|
|
52
|
-
<b-dropdown-item
|
|
53
|
-
v-for="(set, index) in setsList"
|
|
54
|
-
:key="`${set.label_set.id}_${index}`"
|
|
55
|
-
aria-role="listitem"
|
|
56
|
-
:value="set"
|
|
57
|
-
>
|
|
58
|
-
<span>{{
|
|
59
|
-
`${set.label_set.name} ${
|
|
60
|
-
set.id
|
|
61
|
-
? numberOfAnnotationSetGroup(set)
|
|
62
|
-
: `${numberOfLabelSetGroup(set.label_set)} (${$t("new")})`
|
|
63
|
-
}`
|
|
64
|
-
}}</span>
|
|
65
|
-
</b-dropdown-item>
|
|
66
|
-
</b-dropdown>
|
|
67
|
-
<b-tooltip
|
|
68
|
-
multilined
|
|
69
|
-
:active="selectedSet && (!labelsFiltered || labelsFiltered.length === 0)"
|
|
70
|
-
size="is-large"
|
|
71
|
-
position="is-bottom"
|
|
72
|
-
class="bottom-aligned"
|
|
73
|
-
:close-delay="5000"
|
|
74
|
-
>
|
|
75
|
-
<template #content>
|
|
76
|
-
<div
|
|
77
|
-
v-html="
|
|
78
|
-
`${$t('no_labels_available')} ${
|
|
79
|
-
showBranding ? $t('no_labels_available_link') : ''
|
|
80
|
-
}`
|
|
81
|
-
"
|
|
82
|
-
></div>
|
|
83
|
-
</template>
|
|
84
|
-
<b-dropdown
|
|
85
|
-
v-if="selectedLabel"
|
|
86
|
-
v-model="selectedLabel"
|
|
87
|
-
aria-role="list"
|
|
88
|
-
:disabled="!labelsFiltered || labelsFiltered.length === 0"
|
|
89
|
-
scrollable
|
|
90
|
-
class="label-dropdown annotation-dropdown dropdown-full-width"
|
|
91
|
-
>
|
|
92
|
-
<template #trigger>
|
|
93
|
-
<b-button
|
|
94
|
-
class="popup-input has-right-icon"
|
|
95
|
-
:disabled="!labelsFiltered"
|
|
96
|
-
type="is-text"
|
|
97
|
-
>
|
|
98
|
-
{{ selectedLabel.name }}
|
|
99
|
-
<span class="caret-icon">
|
|
100
|
-
<b-icon icon="angle-down" size="is-small" class="caret" />
|
|
101
|
-
</span>
|
|
102
|
-
</b-button>
|
|
103
|
-
</template>
|
|
104
|
-
<b-dropdown-item
|
|
105
|
-
v-for="label in labelsFiltered"
|
|
106
|
-
:key="label.id"
|
|
107
|
-
aria-role="listitem"
|
|
108
|
-
:value="label"
|
|
109
|
-
>
|
|
110
|
-
<span>{{ label.name }}</span>
|
|
111
|
-
</b-dropdown-item>
|
|
112
|
-
</b-dropdown>
|
|
113
|
-
</b-tooltip>
|
|
114
|
-
<div class="annotation-buttons">
|
|
115
|
-
<b-button
|
|
116
|
-
type="is-text"
|
|
117
|
-
class="cancel-button popup-button primary-button"
|
|
118
|
-
:label="$t('hide')"
|
|
119
|
-
:disabled="loading"
|
|
120
|
-
@click.prevent="hide = true"
|
|
121
|
-
/>
|
|
122
|
-
<b-button
|
|
123
|
-
type="is-primary"
|
|
124
|
-
class="popup-button primary-button"
|
|
125
|
-
:label="$t('save')"
|
|
126
|
-
:disabled="loading || !spanSelection || !selectedLabel || !wasChanged"
|
|
127
|
-
@click.prevent="save"
|
|
128
|
-
/>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
</template>
|
|
132
|
-
<script>
|
|
133
|
-
/**
|
|
134
|
-
* This component is used to show a popup
|
|
135
|
-
* for editing an annotation.
|
|
136
|
-
*/
|
|
137
|
-
const heightOfPopup = 142;
|
|
138
|
-
const margin = 12;
|
|
139
|
-
const widthOfPopup = 205;
|
|
140
|
-
|
|
141
|
-
import { mapGetters, mapState } from "vuex";
|
|
142
|
-
|
|
143
|
-
export default {
|
|
144
|
-
props: {
|
|
145
|
-
editAnnotation: {
|
|
146
|
-
required: true,
|
|
147
|
-
type: Object,
|
|
148
|
-
},
|
|
149
|
-
page: {
|
|
150
|
-
required: true,
|
|
151
|
-
type: Object,
|
|
152
|
-
},
|
|
153
|
-
containerWidth: {
|
|
154
|
-
type: Number,
|
|
155
|
-
required: true,
|
|
156
|
-
},
|
|
157
|
-
containerHeight: {
|
|
158
|
-
type: Number,
|
|
159
|
-
required: true,
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
data() {
|
|
163
|
-
return {
|
|
164
|
-
annotation: null,
|
|
165
|
-
selectedLabel: null,
|
|
166
|
-
selectedSet: null,
|
|
167
|
-
labelsFiltered: null,
|
|
168
|
-
loading: false,
|
|
169
|
-
isAnnSetModalShowing: false,
|
|
170
|
-
setsList: [],
|
|
171
|
-
hide: false,
|
|
172
|
-
};
|
|
173
|
-
},
|
|
174
|
-
computed: {
|
|
175
|
-
...mapState("document", [
|
|
176
|
-
"annotationSets",
|
|
177
|
-
"annotations",
|
|
178
|
-
"labels",
|
|
179
|
-
"documentId",
|
|
180
|
-
]),
|
|
181
|
-
...mapGetters("document", [
|
|
182
|
-
"numberOfAnnotationSetGroup",
|
|
183
|
-
"numberOfLabelSetGroup",
|
|
184
|
-
"labelsFilteredForAnnotationCreation",
|
|
185
|
-
]),
|
|
186
|
-
...mapState("display", ["showBranding"]),
|
|
187
|
-
...mapGetters("display", ["bboxToRect"]),
|
|
188
|
-
...mapState("selection", ["selection", "spanSelection"]),
|
|
189
|
-
top() {
|
|
190
|
-
if (this.selection && this.selection.start && this.selection.end) {
|
|
191
|
-
const top = this.selection.start.y - heightOfPopup; // subtract the height of the popup plus some margin
|
|
192
|
-
|
|
193
|
-
const height = this.selection.end.y - this.selection.start.y;
|
|
194
|
-
|
|
195
|
-
//check if the popup will not go off the container on the top
|
|
196
|
-
return this.selection.start.y > heightOfPopup
|
|
197
|
-
? top - margin
|
|
198
|
-
: this.selection.start.y + height + margin;
|
|
199
|
-
}
|
|
200
|
-
return 0;
|
|
201
|
-
},
|
|
202
|
-
left() {
|
|
203
|
-
if (this.selection && this.selection.start && this.selection.end) {
|
|
204
|
-
const width = this.selection.end.x - this.selection.start.x;
|
|
205
|
-
|
|
206
|
-
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
|
|
207
|
-
|
|
208
|
-
//check if the popup will not go off the container
|
|
209
|
-
if (left + widthOfPopup > this.containerWidth) {
|
|
210
|
-
// on the right side
|
|
211
|
-
return this.containerWidth - widthOfPopup;
|
|
212
|
-
} else {
|
|
213
|
-
// on the left side
|
|
214
|
-
return left > 0 ? left : 0;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return 0;
|
|
218
|
-
},
|
|
219
|
-
wasChanged() {
|
|
220
|
-
return (
|
|
221
|
-
this.editAnnotation.annotationSet.id !== this.selectedSet.id ||
|
|
222
|
-
this.editAnnotation.label.id !== this.selectedLabel.id
|
|
223
|
-
);
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
watch: {
|
|
227
|
-
selectedSet(newValue) {
|
|
228
|
-
this.labelsFiltered = this.labelsFilteredForAnnotationCreation(newValue);
|
|
229
|
-
},
|
|
230
|
-
editAnnotation() {
|
|
231
|
-
this.hide = false;
|
|
232
|
-
this.loadInfo();
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
mounted() {
|
|
236
|
-
this.loadInfo();
|
|
237
|
-
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
// prevent click propagation when opening the popup
|
|
240
|
-
document.body.addEventListener("click", this.clickOutside);
|
|
241
|
-
}, 200);
|
|
242
|
-
},
|
|
243
|
-
destroyed() {
|
|
244
|
-
document.body.removeEventListener("click", this.clickOutside);
|
|
245
|
-
},
|
|
246
|
-
methods: {
|
|
247
|
-
orderedSetList(setsList) {
|
|
248
|
-
setsList.sort((a, b) => {
|
|
249
|
-
const nameA = a.label_set.name;
|
|
250
|
-
const nameB = b.label_set.name;
|
|
251
|
-
if (nameA < nameB) {
|
|
252
|
-
return -1;
|
|
253
|
-
}
|
|
254
|
-
if (nameA > nameB) {
|
|
255
|
-
return 1;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// names must be equal
|
|
259
|
-
return 0;
|
|
260
|
-
});
|
|
261
|
-
return setsList;
|
|
262
|
-
},
|
|
263
|
-
loadInfo() {
|
|
264
|
-
this.setsList = this.orderedSetList([...this.annotationSets]);
|
|
265
|
-
|
|
266
|
-
this.selectedSet = this.annotationSets.find(
|
|
267
|
-
(annSet) => annSet.id === this.editAnnotation.annotationSet.id
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
this.labelsFiltered = this.labelsFilteredForAnnotationCreation(
|
|
271
|
-
this.selectedSet
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
// if existing label is not able to be chosen we add it manually
|
|
275
|
-
if (!this.labelsFiltered.includes(this.editAnnotation.label)) {
|
|
276
|
-
this.labelsFiltered.push(this.editAnnotation.label);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
this.selectedLabel = this.editAnnotation.label;
|
|
280
|
-
|
|
281
|
-
this.annotation = this.annotations.find(
|
|
282
|
-
(ann) => ann.id === this.editAnnotation.id
|
|
283
|
-
);
|
|
284
|
-
},
|
|
285
|
-
close() {
|
|
286
|
-
this.$store.dispatch("document/resetEditAnnotation");
|
|
287
|
-
this.$store.dispatch("selection/disableSelection");
|
|
288
|
-
this.$store.dispatch("selection/setSelectedEntities", null);
|
|
289
|
-
this.$emit("close");
|
|
290
|
-
},
|
|
291
|
-
async save() {
|
|
292
|
-
this.loading = true;
|
|
293
|
-
|
|
294
|
-
if (this.wasChanged) {
|
|
295
|
-
// first delete annotation, then create new one
|
|
296
|
-
await this.$store
|
|
297
|
-
.dispatch("document/deleteAnnotation", {
|
|
298
|
-
annotationId: this.annotation.id,
|
|
299
|
-
})
|
|
300
|
-
.catch((error) => {
|
|
301
|
-
this.$store.dispatch("document/createErrorMessage", {
|
|
302
|
-
error,
|
|
303
|
-
serverErrorMessage: this.$t("server_error"),
|
|
304
|
-
defaultErrorMessage: this.$t("edit_error"),
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const spans = this.annotation.span;
|
|
309
|
-
spans[this.editAnnotation.index] = this.spanSelection;
|
|
310
|
-
|
|
311
|
-
const annotationToCreate = {
|
|
312
|
-
document: this.documentId,
|
|
313
|
-
span: spans,
|
|
314
|
-
label: this.selectedLabel.id,
|
|
315
|
-
is_correct: true,
|
|
316
|
-
revised: false,
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
if (this.selectedSet.id) {
|
|
320
|
-
annotationToCreate.annotation_set = this.selectedSet.id;
|
|
321
|
-
} else {
|
|
322
|
-
annotationToCreate.label_set = this.selectedSet.label_set.id;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
this.$store
|
|
326
|
-
.dispatch("document/createAnnotation", {
|
|
327
|
-
annotation: annotationToCreate,
|
|
328
|
-
})
|
|
329
|
-
.catch((error) => {
|
|
330
|
-
this.$store.dispatch("document/createErrorMessage", {
|
|
331
|
-
error,
|
|
332
|
-
serverErrorMessage: this.$t("server_error"),
|
|
333
|
-
defaultErrorMessage: this.$t("error_creating_annotation"),
|
|
334
|
-
});
|
|
335
|
-
})
|
|
336
|
-
.finally(() => {
|
|
337
|
-
this.close();
|
|
338
|
-
this.loading = false;
|
|
339
|
-
});
|
|
340
|
-
} else {
|
|
341
|
-
this.close();
|
|
342
|
-
}
|
|
343
|
-
},
|
|
344
|
-
chooseLabelSet(labelSet) {
|
|
345
|
-
// check if there's already a new entry for that label set to be created
|
|
346
|
-
const existsIndex = this.setsList.findIndex((set) => {
|
|
347
|
-
return set.id === null && set.label_set.id === labelSet.id;
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
const newSet = {
|
|
351
|
-
label_set: labelSet,
|
|
352
|
-
labels: labelSet.labels,
|
|
353
|
-
id: null,
|
|
354
|
-
};
|
|
355
|
-
if (existsIndex >= 0) {
|
|
356
|
-
this.setsList[existsIndex] = newSet;
|
|
357
|
-
} else {
|
|
358
|
-
this.setsList.unshift(newSet);
|
|
359
|
-
}
|
|
360
|
-
this.selectedSet = newSet;
|
|
361
|
-
},
|
|
362
|
-
openAnnotationSetCreation() {
|
|
363
|
-
this.$store.dispatch("display/showChooseLabelSetModal", {
|
|
364
|
-
show: true,
|
|
365
|
-
finish: this.chooseLabelSet,
|
|
366
|
-
});
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
</script>
|
|
371
|
-
|
|
372
|
-
<style scoped lang="scss" src="../../assets/scss/new_annotation.scss"></style>
|