@konfuzio/document-validation-ui 0.1.12-dev.0 → 0.1.12-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/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/package.json +1 -1
- package/src/assets/scss/document_annotations.scss +1 -1
- package/src/components/DocumentAnnotations/AnnotationContent.vue +6 -23
- package/src/components/DocumentAnnotations/AnnotationRow.vue +2 -6
- package/src/components/DocumentAnnotations/DocumentAnnotations.vue +57 -55
- package/src/components/DocumentAnnotations/DocumentLabel.vue +31 -6
- package/src/components/DocumentAnnotations/EmptyAnnotation.vue +2 -1
- package/src/components/DocumentCategory.vue +27 -0
- package/src/components/DocumentModals/DocumentErrorModal.vue +6 -4
- package/src/components/DocumentPage/NewAnnotation.vue +2 -2
- package/src/store/document.js +9 -11
package/package.json
CHANGED
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
ref="contentEditable"
|
|
7
7
|
:class="[
|
|
8
8
|
'annotation-value',
|
|
9
|
+
'keyboard-nav',
|
|
9
10
|
isLoading && 'saving-changes',
|
|
10
11
|
showActionError &&
|
|
11
12
|
editAnnotation &&
|
|
12
13
|
editAnnotation.id === annotation.id &&
|
|
13
14
|
'error-editing',
|
|
14
|
-
isAnnotationBeingEdited && 'clicked',
|
|
15
|
+
isAnnotationBeingEdited && 'clicked-ann',
|
|
15
16
|
]"
|
|
16
17
|
role="textbox"
|
|
17
18
|
:contenteditable="isAnnotationBeingEdited"
|
|
@@ -84,22 +85,10 @@ export default {
|
|
|
84
85
|
},
|
|
85
86
|
|
|
86
87
|
watch: {
|
|
87
|
-
|
|
88
|
+
isAnnotationBeingEdited(newState, oldState) {
|
|
88
89
|
// verify if new annotation in edit mode is not this one and if this
|
|
89
90
|
// one was selected before so we set the state to the previous one (like a cancel)
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
!newAnnotation &&
|
|
93
|
-
oldAnnotation &&
|
|
94
|
-
oldAnnotation.id === this.annotation.id
|
|
95
|
-
) {
|
|
96
|
-
this.handleCancel(true);
|
|
97
|
-
} else if (
|
|
98
|
-
newAnnotation &&
|
|
99
|
-
oldAnnotation &&
|
|
100
|
-
newAnnotation.id === this.annotation.id &&
|
|
101
|
-
newAnnotation.id !== oldAnnotation.id
|
|
102
|
-
) {
|
|
91
|
+
if (!newState && oldState) {
|
|
103
92
|
this.handleCancel();
|
|
104
93
|
}
|
|
105
94
|
},
|
|
@@ -170,14 +159,8 @@ export default {
|
|
|
170
159
|
}
|
|
171
160
|
}
|
|
172
161
|
},
|
|
173
|
-
handleCancel(
|
|
174
|
-
|
|
175
|
-
this.setText(this.span.offset_string);
|
|
176
|
-
} else {
|
|
177
|
-
this.$store.dispatch("selection/disableSelection");
|
|
178
|
-
this.$store.dispatch("document/endLoading");
|
|
179
|
-
}
|
|
180
|
-
|
|
162
|
+
handleCancel() {
|
|
163
|
+
this.setText(this.span.offset_string);
|
|
181
164
|
this.isLoading = false;
|
|
182
165
|
if (this.$refs.contentEditable) {
|
|
183
166
|
this.$refs.contentEditable.blur();
|
|
@@ -352,12 +352,8 @@ export default {
|
|
|
352
352
|
return label.annotations;
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
-
// Check if there are no annotations
|
|
356
|
-
if (
|
|
357
|
-
annotations.length === 0 ||
|
|
358
|
-
(this.label.annotations.length > 1 && this.enableGroupingFeature)
|
|
359
|
-
)
|
|
360
|
-
return;
|
|
355
|
+
// Check if there are no annotations
|
|
356
|
+
if (annotations.length === 0) return;
|
|
361
357
|
|
|
362
358
|
const found = annotations.find(
|
|
363
359
|
(ann) => ann.id === this.annotation.id && !ann.revised
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<EmptyState />
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
|
-
<div v-else :class="['annotation-set-list']">
|
|
20
|
+
<div v-else ref="annotationList" :class="['annotation-set-list']">
|
|
21
21
|
<div
|
|
22
22
|
v-if="Object.entries(annotationSetsInTable()).length > 0"
|
|
23
23
|
class="annotation-set-group"
|
|
@@ -162,7 +162,6 @@ export default {
|
|
|
162
162
|
}
|
|
163
163
|
},
|
|
164
164
|
acceptAnnotation(newValue, oldValue) {
|
|
165
|
-
// TODO: rework this to be more generic
|
|
166
165
|
if (!newValue && oldValue) {
|
|
167
166
|
this.focusOnNextAnnotation();
|
|
168
167
|
this.jumpToNextAnnotation = false;
|
|
@@ -177,9 +176,8 @@ export default {
|
|
|
177
176
|
},
|
|
178
177
|
methods: {
|
|
179
178
|
focusOnNextAnnotation() {
|
|
180
|
-
const annotations =
|
|
181
|
-
|
|
182
|
-
);
|
|
179
|
+
const annotations = this.createArray("keyboard-nav");
|
|
180
|
+
|
|
183
181
|
if (annotations[this.count]) {
|
|
184
182
|
annotations[this.count].click();
|
|
185
183
|
} else {
|
|
@@ -221,31 +219,36 @@ export default {
|
|
|
221
219
|
}
|
|
222
220
|
},
|
|
223
221
|
|
|
222
|
+
exitEditMode() {
|
|
223
|
+
this.count = 0;
|
|
224
|
+
this.$store.dispatch("document/resetEditAnnotation");
|
|
225
|
+
this.$store.dispatch("selection/disableSelection");
|
|
226
|
+
this.$store.dispatch("document/endLoading");
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
createArray(className) {
|
|
230
|
+
return Array.from(
|
|
231
|
+
this.$refs.annotationList.getElementsByClassName(className)
|
|
232
|
+
);
|
|
233
|
+
},
|
|
234
|
+
|
|
224
235
|
keyDownHandler(event) {
|
|
225
236
|
// only allow keyboard navigation if we are not in public view mode
|
|
226
237
|
if (this.publicView || this.isDocumentReviewed) return;
|
|
227
238
|
|
|
228
|
-
//
|
|
239
|
+
// Exit edit mode and navigation
|
|
229
240
|
if (event.key === "Escape") {
|
|
230
|
-
this.
|
|
231
|
-
this.$store.dispatch("document/resetEditAnnotation");
|
|
232
|
-
this.$store.dispatch("selection/disableSelection");
|
|
233
|
-
this.$store.dispatch("document/endLoading");
|
|
241
|
+
this.exitEditMode();
|
|
234
242
|
return;
|
|
235
243
|
}
|
|
236
244
|
|
|
237
245
|
// Not allow starting edit mode with ArrowUp key
|
|
238
246
|
if (event.key === "ArrowUp" && !this.isAnnotationBeingEdited) return;
|
|
247
|
+
// Get all the annotation elements
|
|
248
|
+
let annotations = this.createArray("keyboard-nav");
|
|
239
249
|
|
|
240
|
-
// Create an array from the elements selected
|
|
241
|
-
// for easier management of data
|
|
242
|
-
const annotations = Array.from(
|
|
243
|
-
document.getElementsByClassName("annotation-value")
|
|
244
|
-
);
|
|
245
250
|
// Get clicked element to get the index
|
|
246
|
-
const clickedAnnotations =
|
|
247
|
-
document.getElementsByClassName("clicked")
|
|
248
|
-
);
|
|
251
|
+
const clickedAnnotations = this.createArray("clicked-ann");
|
|
249
252
|
|
|
250
253
|
// get index of currently active element
|
|
251
254
|
const currentAnnIndex = annotations.findIndex(
|
|
@@ -254,10 +257,10 @@ export default {
|
|
|
254
257
|
|
|
255
258
|
// navigate with the arrow up or down keys
|
|
256
259
|
if (event.key === "ArrowDown") {
|
|
260
|
+
// Check if we are focusing on the Finish Review button
|
|
257
261
|
if (this.count >= annotations.length) {
|
|
258
|
-
const finishBtn =
|
|
259
|
-
|
|
260
|
-
);
|
|
262
|
+
const finishBtn = this.createArray("finish-review-btn");
|
|
263
|
+
|
|
261
264
|
finishBtn[0].focus();
|
|
262
265
|
this.$store.dispatch("document/resetEditAnnotation");
|
|
263
266
|
this.count = 0;
|
|
@@ -275,24 +278,24 @@ export default {
|
|
|
275
278
|
this.count = currentAnnIndex + 1;
|
|
276
279
|
}
|
|
277
280
|
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
const nextElement = annotations[this.count];
|
|
282
|
+
if (nextElement.className.includes("label-group")) {
|
|
283
|
+
// open group and then click on annotation
|
|
284
|
+
// index is the same since group is removed from keyboard nav
|
|
285
|
+
nextElement.click();
|
|
286
|
+
this.$nextTick(() => {
|
|
287
|
+
annotations = this.createArray("keyboard-nav");
|
|
288
|
+
annotations[this.count].click();
|
|
289
|
+
this.scrollToFocusedAnnotationFromKeyHandler();
|
|
284
290
|
this.count++;
|
|
285
|
-
}
|
|
291
|
+
});
|
|
292
|
+
} else if (annotations[this.count]) {
|
|
293
|
+
annotations[this.count].click();
|
|
294
|
+
this.scrollToFocusedAnnotationFromKeyHandler();
|
|
295
|
+
this.count++;
|
|
286
296
|
}
|
|
287
297
|
|
|
288
|
-
if (!annotations[this.count]) return;
|
|
289
|
-
|
|
290
|
-
annotations[this.count].click();
|
|
291
|
-
|
|
292
298
|
// scroll to current annotation if not empty
|
|
293
|
-
this.scrollToFocusedAnnotationFromKeyHandler();
|
|
294
|
-
|
|
295
|
-
this.count++;
|
|
296
299
|
} else if (event.key === "ArrowUp") {
|
|
297
300
|
// Check if the event happened on the first element from the array
|
|
298
301
|
// If so, reset count to 0
|
|
@@ -309,24 +312,27 @@ export default {
|
|
|
309
312
|
this.count = currentAnnIndex - 1;
|
|
310
313
|
}
|
|
311
314
|
|
|
312
|
-
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
315
|
+
const previousElement = annotations[this.count];
|
|
316
|
+
if (previousElement.className.includes("label-group")) {
|
|
317
|
+
// open group and then click on annotation
|
|
318
|
+
// index is the same since group is removed from keyboard nav
|
|
319
|
+
previousElement.click();
|
|
320
|
+
this.$nextTick(() => {
|
|
321
|
+
annotations = this.createArray("keyboard-nav");
|
|
322
|
+
// since we are going backwards, we need to go to the last annotation of group
|
|
323
|
+
const currentAnnIndex = annotations.findIndex(
|
|
324
|
+
(el) => el === clickedAnnotations[0]
|
|
325
|
+
);
|
|
326
|
+
this.count = currentAnnIndex - 1;
|
|
327
|
+
annotations[this.count].click();
|
|
328
|
+
this.scrollToFocusedAnnotationFromKeyHandler();
|
|
318
329
|
this.count--;
|
|
319
|
-
}
|
|
330
|
+
});
|
|
331
|
+
} else if (annotations[this.count]) {
|
|
332
|
+
annotations[this.count].click();
|
|
333
|
+
this.scrollToFocusedAnnotationFromKeyHandler();
|
|
334
|
+
this.count--;
|
|
320
335
|
}
|
|
321
|
-
|
|
322
|
-
if (!annotations[this.count]) return;
|
|
323
|
-
|
|
324
|
-
annotations[this.count].click();
|
|
325
|
-
|
|
326
|
-
// scroll to current annotation if not empty
|
|
327
|
-
this.scrollToFocusedAnnotationFromKeyHandler();
|
|
328
|
-
|
|
329
|
-
this.count--;
|
|
330
336
|
} else {
|
|
331
337
|
// Check for ENTER or DELETE
|
|
332
338
|
// Accept annotation
|
|
@@ -358,10 +364,6 @@ export default {
|
|
|
358
364
|
}
|
|
359
365
|
},
|
|
360
366
|
|
|
361
|
-
focusedAnnotationIsMarkedAsMissing(annotations, index) {
|
|
362
|
-
return annotations[index].classList.value.includes("missing-annotation");
|
|
363
|
-
},
|
|
364
|
-
|
|
365
367
|
markAnnotationsAsMissing(label, labelSet, annotationSet, markAllMissing) {
|
|
366
368
|
let missing;
|
|
367
369
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="label">
|
|
3
|
-
<div v-if="enableGroupingFeature &&
|
|
4
|
-
<div
|
|
3
|
+
<div v-if="enableGroupingFeature && nonMultipleAnnotationsExtracted">
|
|
4
|
+
<div
|
|
5
|
+
:class="['label-group', !showAnnotationsGroup && 'keyboard-nav']"
|
|
6
|
+
@click.stop="toggleGroup"
|
|
7
|
+
>
|
|
5
8
|
<div class="label-group-left">
|
|
6
9
|
<b-icon
|
|
7
10
|
:icon="showAnnotationsGroup ? 'angle-up' : 'angle-down'"
|
|
@@ -36,7 +39,7 @@
|
|
|
36
39
|
/>
|
|
37
40
|
</div>
|
|
38
41
|
</div>
|
|
39
|
-
<div v-else-if="
|
|
42
|
+
<div v-else-if="hasAnnotations">
|
|
40
43
|
<AnnotationRow
|
|
41
44
|
v-for="annotation in label.annotations"
|
|
42
45
|
:key="annotation.id"
|
|
@@ -76,7 +79,7 @@ export default {
|
|
|
76
79
|
},
|
|
77
80
|
data() {
|
|
78
81
|
return {
|
|
79
|
-
|
|
82
|
+
nonMultipleAnnotationsExtracted: false,
|
|
80
83
|
acceptedAnnotationsGroupCounter: 0,
|
|
81
84
|
showAnnotationsGroup: false,
|
|
82
85
|
};
|
|
@@ -85,6 +88,7 @@ export default {
|
|
|
85
88
|
...mapState("document", [
|
|
86
89
|
"sidebarAnnotationSelected",
|
|
87
90
|
"enableGroupingFeature",
|
|
91
|
+
"hoveredAnnotationSet",
|
|
88
92
|
]),
|
|
89
93
|
...mapGetters("document", ["numberOfAcceptedAnnotationsInLabel"]),
|
|
90
94
|
singleAnnotation() {
|
|
@@ -121,6 +125,16 @@ export default {
|
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
},
|
|
128
|
+
hoveredAnnotationSet(newValue) {
|
|
129
|
+
// Check if there are some unrevised Annotations within the group
|
|
130
|
+
if (
|
|
131
|
+
newValue &&
|
|
132
|
+
newValue.type === "accept" &&
|
|
133
|
+
this.labelHasPendingAnnotations(newValue)
|
|
134
|
+
) {
|
|
135
|
+
this.showAnnotationsGroup = true;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
124
138
|
},
|
|
125
139
|
mounted() {
|
|
126
140
|
this.updateValues();
|
|
@@ -133,12 +147,23 @@ export default {
|
|
|
133
147
|
this.showAnnotationsGroup = !this.showAnnotationsGroup;
|
|
134
148
|
},
|
|
135
149
|
updateValues() {
|
|
136
|
-
|
|
137
|
-
|
|
150
|
+
// more than 1 Annotation extracted for a non multiple Label
|
|
151
|
+
this.nonMultipleAnnotationsExtracted =
|
|
152
|
+
this.label.annotations.length > 1 &&
|
|
153
|
+
!this.label.has_multiple_top_candidates;
|
|
154
|
+
|
|
155
|
+
if (this.nonMultipleAnnotationsExtracted) {
|
|
138
156
|
this.acceptedAnnotationsGroupCounter =
|
|
139
157
|
this.numberOfAcceptedAnnotationsInLabel(this.label);
|
|
140
158
|
}
|
|
141
159
|
},
|
|
160
|
+
labelHasPendingAnnotations(hoveredSet) {
|
|
161
|
+
if (!hoveredSet) return;
|
|
162
|
+
|
|
163
|
+
const found = this.label.annotations.find((ann) => !ann.revised);
|
|
164
|
+
|
|
165
|
+
return this.annotationSet.id === hoveredSet.annotationSet.id && found;
|
|
166
|
+
},
|
|
142
167
|
},
|
|
143
168
|
};
|
|
144
169
|
</script>
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
editAnnotation.id === emptyAnnotationId() &&
|
|
12
12
|
'error-editing',
|
|
13
13
|
!isEmptyAnnotationEditable() && !isMissingAnnotation && 'label-empty',
|
|
14
|
-
isAnnotationBeingEdited() && 'clicked',
|
|
14
|
+
isAnnotationBeingEdited() && 'clicked-ann',
|
|
15
15
|
isMissingAnnotation && 'missing-annotation',
|
|
16
|
+
!isMissingAnnotation && 'keyboard-nav',
|
|
16
17
|
]"
|
|
17
18
|
:contenteditable="isEmptyAnnotationEditable()"
|
|
18
19
|
@keypress.enter="saveEmptyAnnotationChanges"
|
|
@@ -104,6 +104,33 @@ export default {
|
|
|
104
104
|
if (!this.splitMode) {
|
|
105
105
|
return this.categoryName(this.selectedDocument.category);
|
|
106
106
|
} else {
|
|
107
|
+
const missingCategory = this.updatedDocument.find(
|
|
108
|
+
(item) => !item.category
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// if there is just 1 category in the project,
|
|
112
|
+
// and one or more sub-documents has no category,
|
|
113
|
+
// assign the only category by default
|
|
114
|
+
if (this.projectHasSingleCategory() && missingCategory) {
|
|
115
|
+
const updatedValuesForDocuments = this.updatedDocument.map(
|
|
116
|
+
(document) => {
|
|
117
|
+
if (!document.category && this.categories) {
|
|
118
|
+
document.category = this.categories[0].id;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return document;
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// update the store state
|
|
126
|
+
// so that if the changes are saved the data sent to the API is updated
|
|
127
|
+
// instead of only handling the category name in this component
|
|
128
|
+
this.$store.dispatch(
|
|
129
|
+
"edit/setUpdatedDocument",
|
|
130
|
+
updatedValuesForDocuments
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
107
134
|
const categoryName = this.categoryName(
|
|
108
135
|
this.updatedDocument[this.index].category
|
|
109
136
|
);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<section class="document-error-modal">
|
|
3
|
-
<b-modal
|
|
3
|
+
<b-modal
|
|
4
|
+
v-model="isModalActive"
|
|
5
|
+
:width="400"
|
|
6
|
+
can-cancel="['x']"
|
|
7
|
+
:on-cancel="closeModal"
|
|
8
|
+
>
|
|
4
9
|
<section class="modal-card-body">
|
|
5
10
|
<div class="header">
|
|
6
11
|
<div class="error-icon">
|
|
7
12
|
<ErrorIcon class="icon" />
|
|
8
13
|
</div>
|
|
9
|
-
<div class="btn-container" type="button" @click="closeModal">
|
|
10
|
-
<b-icon icon="xmark" class="close-btn" size="is-small" />
|
|
11
|
-
</div>
|
|
12
14
|
</div>
|
|
13
15
|
<div class="content">
|
|
14
16
|
<h3>{{ $t("document_error_title") }}</h3>
|
|
@@ -127,7 +127,7 @@ const margin = 12;
|
|
|
127
127
|
const widthOfPopup = 205;
|
|
128
128
|
|
|
129
129
|
import { mapGetters, mapState } from "vuex";
|
|
130
|
-
import {
|
|
130
|
+
import { MULTI_ANN_TABLE_FEATURE } from "../../constants";
|
|
131
131
|
|
|
132
132
|
export default {
|
|
133
133
|
props: {
|
|
@@ -271,7 +271,7 @@ export default {
|
|
|
271
271
|
openAnnotationSetCreation() {
|
|
272
272
|
this.$store.dispatch("display/showChooseLabelSetModal", {
|
|
273
273
|
show: true,
|
|
274
|
-
isMultipleAnnotations:
|
|
274
|
+
isMultipleAnnotations: MULTI_ANN_TABLE_FEATURE,
|
|
275
275
|
finish: this.chooseLabelSet,
|
|
276
276
|
});
|
|
277
277
|
},
|
package/src/store/document.js
CHANGED
|
@@ -34,6 +34,7 @@ const state = {
|
|
|
34
34
|
newAcceptedAnnotations: null,
|
|
35
35
|
serverError: false,
|
|
36
36
|
splittingSuggestions: null,
|
|
37
|
+
enableGroupingFeature: true,
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
const getters = {
|
|
@@ -247,14 +248,11 @@ const getters = {
|
|
|
247
248
|
const labels = [];
|
|
248
249
|
const processedAnnotationSets = annotationSets.map((annotationSet) => {
|
|
249
250
|
const annotationSetLabels = annotationSet.labels.map((label) => {
|
|
250
|
-
// filter label
|
|
251
|
-
const filteredLabel = getters.annotationsInLabelFiltered(label);
|
|
252
|
-
|
|
253
251
|
// add annotations to the document array
|
|
254
|
-
annotations.push(...
|
|
255
|
-
labels.push(
|
|
252
|
+
annotations.push(...label.annotations);
|
|
253
|
+
labels.push(label);
|
|
256
254
|
// add labels to the labels array
|
|
257
|
-
return
|
|
255
|
+
return label;
|
|
258
256
|
});
|
|
259
257
|
annotationSet.labels = annotationSetLabels;
|
|
260
258
|
return annotationSet;
|
|
@@ -475,12 +473,12 @@ const getters = {
|
|
|
475
473
|
annotationsWithPendingReview.push(annotation);
|
|
476
474
|
});
|
|
477
475
|
}
|
|
478
|
-
});
|
|
479
476
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
477
|
+
// Check if we have grouped annotations by same label
|
|
478
|
+
if (state.enableGroupingFeature && label.annotations.length < 2) {
|
|
479
|
+
return annotationsWithPendingReview.length - label.annotations.length;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
484
482
|
|
|
485
483
|
return annotationsWithPendingReview.length;
|
|
486
484
|
},
|