@konfuzio/document-validation-ui 0.1.22-dev.0 → 0.1.22-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 +9 -0
- package/src/components/DocumentAnnotations/AnnotationRow.vue +9 -19
- package/src/components/DocumentAnnotations/DocumentAnnotations.vue +128 -29
- package/src/components/DocumentPage/DocumentPage.vue +20 -14
- package/src/store/document.js +44 -0
package/package.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
.annotation-set-group {
|
|
53
53
|
.label-set-header {
|
|
54
|
+
cursor: pointer;
|
|
54
55
|
display: flex;
|
|
55
56
|
align-items: center;
|
|
56
57
|
justify-content: space-between;
|
|
@@ -66,6 +67,14 @@
|
|
|
66
67
|
font-size: 14px;
|
|
67
68
|
line-height: 20px;
|
|
68
69
|
color: $text;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 8px;
|
|
73
|
+
|
|
74
|
+
.icon {
|
|
75
|
+
width: 12px;
|
|
76
|
+
height: 12px;
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
}
|
|
71
80
|
|
|
@@ -300,32 +300,22 @@ export default {
|
|
|
300
300
|
) {
|
|
301
301
|
clearTimeout(this.annotationAnimationTimeout);
|
|
302
302
|
|
|
303
|
-
let timeout;
|
|
304
|
-
|
|
305
|
-
if (!newSidebarAnnotationSelected.trigger) {
|
|
306
|
-
timeout = 600;
|
|
307
|
-
} else {
|
|
308
|
-
timeout = 1200;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
303
|
this.isSelected = true;
|
|
312
304
|
// remove annotation selection after some time
|
|
313
305
|
this.annotationAnimationTimeout = setTimeout(() => {
|
|
314
306
|
this.$store.dispatch("document/setSidebarAnnotationSelected", null);
|
|
315
307
|
this.isSelected = false;
|
|
316
|
-
},
|
|
308
|
+
}, 1200);
|
|
317
309
|
|
|
318
310
|
// Check if sidebarAnnotationSelected changed from a click or hover
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
runAnimation();
|
|
328
|
-
}
|
|
311
|
+
const runAnimation = () => {
|
|
312
|
+
this.$el.scrollIntoView({
|
|
313
|
+
behavior: "smooth",
|
|
314
|
+
block: "center",
|
|
315
|
+
inline: "nearest",
|
|
316
|
+
});
|
|
317
|
+
};
|
|
318
|
+
runAnimation();
|
|
329
319
|
}
|
|
330
320
|
},
|
|
331
321
|
editAnnotation(newValue) {
|
|
@@ -49,12 +49,22 @@
|
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
51
|
<div
|
|
52
|
-
v-for="(annotationSet, indexGroup) in
|
|
52
|
+
v-for="(annotationSet, indexGroup) in annotationSets"
|
|
53
53
|
:key="indexGroup"
|
|
54
|
-
class="
|
|
54
|
+
:class="[
|
|
55
|
+
'annotation-set-group',
|
|
56
|
+
!annotationSetsAccordion[indexGroup] === true &&
|
|
57
|
+
'annotation-set-collapsed',
|
|
58
|
+
]"
|
|
55
59
|
>
|
|
56
|
-
<div class="label-set-header">
|
|
60
|
+
<div class="label-set-header" @click="toggleAccordion(indexGroup)">
|
|
57
61
|
<div class="label-set-name">
|
|
62
|
+
<b-icon
|
|
63
|
+
:icon="
|
|
64
|
+
annotationSetsAccordion[indexGroup] ? 'angle-up' : 'angle-down'
|
|
65
|
+
"
|
|
66
|
+
>
|
|
67
|
+
</b-icon>
|
|
58
68
|
{{
|
|
59
69
|
`${annotationSet.label_set.name} ${numberOfAnnotationSetGroup(
|
|
60
70
|
annotationSet
|
|
@@ -66,6 +76,7 @@
|
|
|
66
76
|
class="labelset-action-buttons"
|
|
67
77
|
>
|
|
68
78
|
<AnnotationSetActionButtons
|
|
79
|
+
v-if="annotationSetsAccordion[indexGroup] === true"
|
|
69
80
|
:number-of-empty-labels-in-annotation-set="
|
|
70
81
|
emptyLabels(annotationSet).length
|
|
71
82
|
"
|
|
@@ -92,34 +103,38 @@
|
|
|
92
103
|
</div>
|
|
93
104
|
</div>
|
|
94
105
|
|
|
95
|
-
<
|
|
96
|
-
<div v-
|
|
97
|
-
<div
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
<b-collapse :open="annotationSetsAccordion[indexGroup] === true">
|
|
107
|
+
<div v-if="annotationSet.labels.length > 0">
|
|
108
|
+
<div v-for="label in annotationSet.labels" :key="label.id">
|
|
109
|
+
<div
|
|
110
|
+
v-if="!(label.annotations.length === 0 && publicView)"
|
|
111
|
+
class="labels"
|
|
112
|
+
>
|
|
113
|
+
<DocumentLabel
|
|
114
|
+
:label="label"
|
|
115
|
+
:annotation-set="annotationSet"
|
|
116
|
+
:index-group="indexGroup"
|
|
117
|
+
@handle-missing-annotation="markAnnotationsAsMissing"
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
107
120
|
</div>
|
|
108
121
|
</div>
|
|
109
|
-
</div>
|
|
110
122
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
<div v-if="annotationSet.labels.length === 0" class="no-labels">
|
|
124
|
+
<span> {{ $t("no_labels_in_set") }}</span>
|
|
125
|
+
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
126
|
+
<span v-if="isDocumentEditable" v-html="$t('link_to_add_labels')" />
|
|
127
|
+
</div>
|
|
116
128
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
<div
|
|
130
|
+
v-else-if="
|
|
131
|
+
!annotationSetHasAnnotations(annotationSet) && publicView
|
|
132
|
+
"
|
|
133
|
+
class="no-labels"
|
|
134
|
+
>
|
|
135
|
+
<span> {{ $t("no_annotations_in_annotation_set") }}</span>
|
|
136
|
+
</div>
|
|
137
|
+
</b-collapse>
|
|
123
138
|
</div>
|
|
124
139
|
</div>
|
|
125
140
|
</div>
|
|
@@ -150,6 +165,7 @@ export default {
|
|
|
150
165
|
count: 0,
|
|
151
166
|
jumpToNextAnnotation: false,
|
|
152
167
|
numberOfLoadingAnnotations: 3,
|
|
168
|
+
annotationSetsAccordion: [],
|
|
153
169
|
};
|
|
154
170
|
},
|
|
155
171
|
computed: {
|
|
@@ -166,15 +182,16 @@ export default {
|
|
|
166
182
|
"labels",
|
|
167
183
|
"selectedDocument",
|
|
168
184
|
"splittingSuggestions",
|
|
185
|
+
"sidebarAnnotationSelected",
|
|
169
186
|
]),
|
|
170
187
|
...mapGetters("category", ["category"]),
|
|
171
188
|
...mapGetters("document", [
|
|
172
189
|
"numberOfAnnotationSetGroup",
|
|
173
190
|
"emptyLabels",
|
|
174
191
|
"notCorrectAnnotations",
|
|
175
|
-
"annotationSetsToShowInList",
|
|
176
192
|
"annotationSetsInTable",
|
|
177
193
|
"isDocumentReviewed",
|
|
194
|
+
"annotationSetOfAnnotation",
|
|
178
195
|
]),
|
|
179
196
|
isAnnotationBeingEdited() {
|
|
180
197
|
return this.editAnnotation && this.editAnnotation.id;
|
|
@@ -195,6 +212,70 @@ export default {
|
|
|
195
212
|
this.jumpToNextAnnotation = false;
|
|
196
213
|
}
|
|
197
214
|
},
|
|
215
|
+
annotationSets(newAnnotationSets, oldAnnotationSets) {
|
|
216
|
+
if (newAnnotationSets) {
|
|
217
|
+
const newAnnotationSetsAccordion = [];
|
|
218
|
+
const annotationSetsOpened = [];
|
|
219
|
+
const annotationSetsCreated = [];
|
|
220
|
+
if (oldAnnotationSets) {
|
|
221
|
+
// when annotation sets changed, restore old state
|
|
222
|
+
// and check if new ones were created to be open by default
|
|
223
|
+
|
|
224
|
+
this.annotationSetsAccordion.forEach((isOpen, index) => {
|
|
225
|
+
if (isOpen) {
|
|
226
|
+
annotationSetsOpened.push(oldAnnotationSets[index]);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
newAnnotationSets.forEach((newAnnotationSet) => {
|
|
231
|
+
const existed = oldAnnotationSets.find(
|
|
232
|
+
(oldAnnotationSet) =>
|
|
233
|
+
oldAnnotationSet.id &&
|
|
234
|
+
newAnnotationSet.id &&
|
|
235
|
+
oldAnnotationSet.id === newAnnotationSet.id
|
|
236
|
+
);
|
|
237
|
+
if (!existed && newAnnotationSet.id !== null) {
|
|
238
|
+
annotationSetsCreated.push(newAnnotationSet);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
newAnnotationSets.forEach((newAnnotationSet, index) => {
|
|
244
|
+
const wasOpen = annotationSetsOpened.find(
|
|
245
|
+
(annotationSetOpened) =>
|
|
246
|
+
annotationSetOpened.id &&
|
|
247
|
+
newAnnotationSet.id &&
|
|
248
|
+
newAnnotationSet.id === annotationSetOpened.id
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (wasOpen) {
|
|
252
|
+
newAnnotationSetsAccordion[index] = wasOpen !== undefined;
|
|
253
|
+
} else {
|
|
254
|
+
const wasCreated = annotationSetsCreated.find(
|
|
255
|
+
(annotationSetCreated) =>
|
|
256
|
+
annotationSetCreated.id &&
|
|
257
|
+
newAnnotationSet.id &&
|
|
258
|
+
newAnnotationSet.id === annotationSetCreated.id
|
|
259
|
+
);
|
|
260
|
+
newAnnotationSetsAccordion[index] = wasCreated !== undefined;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
this.annotationSetsAccordion = newAnnotationSetsAccordion;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
sidebarAnnotationSelected(annotation) {
|
|
267
|
+
if (annotation) {
|
|
268
|
+
const annotationSet = this.annotationSetOfAnnotation(annotation);
|
|
269
|
+
if (annotationSet) {
|
|
270
|
+
const index = this.annotationSets.findIndex(
|
|
271
|
+
(annotationSetToFind) => annotationSetToFind.id === annotationSet.id
|
|
272
|
+
);
|
|
273
|
+
const newAnnotationSetsAccordion = [...this.annotationSetsAccordion];
|
|
274
|
+
newAnnotationSetsAccordion[index] = true;
|
|
275
|
+
this.annotationSetsAccordion = newAnnotationSetsAccordion;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
198
279
|
},
|
|
199
280
|
created() {
|
|
200
281
|
window.addEventListener("keydown", this.keyDownHandler);
|
|
@@ -203,6 +284,18 @@ export default {
|
|
|
203
284
|
window.removeEventListener("keydown", this.keyDownHandler);
|
|
204
285
|
},
|
|
205
286
|
methods: {
|
|
287
|
+
toggleAccordion(index) {
|
|
288
|
+
const newAnnotationSetsAccordion = [...this.annotationSetsAccordion];
|
|
289
|
+
newAnnotationSetsAccordion[index] = !newAnnotationSetsAccordion[index];
|
|
290
|
+
this.annotationSetsAccordion = newAnnotationSetsAccordion;
|
|
291
|
+
},
|
|
292
|
+
openAllAccordions() {
|
|
293
|
+
const newAnnotationSetsAccordion = [...this.annotationSetsAccordion];
|
|
294
|
+
newAnnotationSetsAccordion.forEach((_, index) => {
|
|
295
|
+
newAnnotationSetsAccordion[index] = true;
|
|
296
|
+
});
|
|
297
|
+
this.annotationSetsAccordion = newAnnotationSetsAccordion;
|
|
298
|
+
},
|
|
206
299
|
annotationSetHasAnnotations(annotationSet) {
|
|
207
300
|
const found = annotationSet.labels.find(
|
|
208
301
|
(label) => label.annotations.length > 0
|
|
@@ -280,6 +373,10 @@ export default {
|
|
|
280
373
|
|
|
281
374
|
// Not allow starting edit mode with ArrowUp key
|
|
282
375
|
if (event.key === "ArrowUp" && !this.isAnnotationBeingEdited) return;
|
|
376
|
+
|
|
377
|
+
// open accordions
|
|
378
|
+
this.openAllAccordions();
|
|
379
|
+
|
|
283
380
|
// Get all the annotation elements
|
|
284
381
|
let annotations = this.createArray("keyboard-nav");
|
|
285
382
|
|
|
@@ -297,7 +394,9 @@ export default {
|
|
|
297
394
|
if (this.count >= annotations.length) {
|
|
298
395
|
const finishBtn = this.createArray("finish-review-btn");
|
|
299
396
|
|
|
300
|
-
finishBtn[0]
|
|
397
|
+
if (finishBtn && finishBtn[0]) {
|
|
398
|
+
finishBtn[0].focus();
|
|
399
|
+
}
|
|
301
400
|
this.$store.dispatch("document/resetEditAnnotation");
|
|
302
401
|
this.count = 0;
|
|
303
402
|
if (event.key === "Enter" && !finishBtn.disabled) {
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
v-if="!isAnnotationInEditMode(annotation.id)"
|
|
72
72
|
:key="'ann' + annotation.id + '-' + index"
|
|
73
73
|
:config="annotationRect(bbox, annotation.id)"
|
|
74
|
-
@click="handleFocusedAnnotation(annotation
|
|
75
|
-
@mouseenter="
|
|
74
|
+
@click="handleFocusedAnnotation(annotation)"
|
|
75
|
+
@mouseenter="onElementEnter(annotation, bbox)"
|
|
76
76
|
@mouseleave="onElementLeave"
|
|
77
77
|
/>
|
|
78
78
|
</template>
|
|
@@ -284,6 +284,7 @@ export default {
|
|
|
284
284
|
"isDocumentReadyToBeReviewed",
|
|
285
285
|
"entitiesOnSelection",
|
|
286
286
|
"isDocumentReviewed",
|
|
287
|
+
"labelOfAnnotation",
|
|
287
288
|
]),
|
|
288
289
|
},
|
|
289
290
|
watch: {
|
|
@@ -396,17 +397,9 @@ export default {
|
|
|
396
397
|
});
|
|
397
398
|
},
|
|
398
399
|
|
|
399
|
-
handleFocusedAnnotation(annotation
|
|
400
|
-
this.$store.dispatch("document/setSidebarAnnotationSelected",
|
|
401
|
-
|
|
402
|
-
trigger,
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
if (trigger && trigger === "click") {
|
|
406
|
-
this.closePopups(true);
|
|
407
|
-
} else {
|
|
408
|
-
this.onElementEnter();
|
|
409
|
-
}
|
|
400
|
+
handleFocusedAnnotation(annotation) {
|
|
401
|
+
this.$store.dispatch("document/setSidebarAnnotationSelected", annotation);
|
|
402
|
+
this.closePopups(true);
|
|
410
403
|
},
|
|
411
404
|
|
|
412
405
|
handleClickedEntity(entity) {
|
|
@@ -454,7 +447,7 @@ export default {
|
|
|
454
447
|
}
|
|
455
448
|
},
|
|
456
449
|
|
|
457
|
-
onElementEnter() {
|
|
450
|
+
onElementEnter(annotation = null, span = null) {
|
|
458
451
|
if (
|
|
459
452
|
!this.categorizeModalIsActive &&
|
|
460
453
|
!this.publicView &&
|
|
@@ -463,10 +456,23 @@ export default {
|
|
|
463
456
|
) {
|
|
464
457
|
this.$refs.stage.$el.style.cursor = "pointer";
|
|
465
458
|
}
|
|
459
|
+
|
|
460
|
+
if (annotation) {
|
|
461
|
+
const label = this.labelOfAnnotation(annotation);
|
|
462
|
+
if (label) {
|
|
463
|
+
this.$store.dispatch("document/setDocumentAnnotationSelected", {
|
|
464
|
+
annotation,
|
|
465
|
+
label,
|
|
466
|
+
span,
|
|
467
|
+
scrollTo: false,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
466
471
|
},
|
|
467
472
|
|
|
468
473
|
onElementLeave() {
|
|
469
474
|
this.$refs.stage.$el.style.cursor = "inherit";
|
|
475
|
+
this.$store.dispatch("document/disableDocumentAnnotationSelected");
|
|
470
476
|
},
|
|
471
477
|
|
|
472
478
|
/**
|
package/src/store/document.js
CHANGED
|
@@ -247,6 +247,50 @@ const getters = {
|
|
|
247
247
|
return annotations;
|
|
248
248
|
},
|
|
249
249
|
|
|
250
|
+
/* Get annotation set for a given annotation */
|
|
251
|
+
annotationSetOfAnnotation: (state) => (annotationToFind) => {
|
|
252
|
+
let foundAnnotationSet = null;
|
|
253
|
+
state.annotationSets.forEach((annotationSet) => {
|
|
254
|
+
annotationSet.labels.forEach((label) => {
|
|
255
|
+
label.annotations.forEach((annotation) => {
|
|
256
|
+
if (annotation.id === annotationToFind.id) {
|
|
257
|
+
foundAnnotationSet = annotationSet;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
if (foundAnnotationSet) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
if (foundAnnotationSet) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return foundAnnotationSet;
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
/* Get label for a given annotation */
|
|
273
|
+
labelOfAnnotation: (state) => (annotationToFind) => {
|
|
274
|
+
let foundLabel = null;
|
|
275
|
+
state.annotationSets.forEach((annotationSet) => {
|
|
276
|
+
annotationSet.labels.forEach((label) => {
|
|
277
|
+
label.annotations.forEach((annotation) => {
|
|
278
|
+
if (annotation.id === annotationToFind.id) {
|
|
279
|
+
foundLabel = label;
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
if (foundLabel) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (foundLabel) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
return foundLabel;
|
|
292
|
+
},
|
|
293
|
+
|
|
250
294
|
/* Process annotations and extract labels and sets */
|
|
251
295
|
processAnnotationSets: (state, getters) => (annotationSets) => {
|
|
252
296
|
// group annotations for sidebar
|