@konfuzio/document-validation-ui 0.1.59-dev.1 → 0.1.59-dev.3
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 +7 -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 -43
- package/src/assets/scss/document_category.scss +8 -8
- package/src/assets/scss/document_dashboard.scss +1 -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 +4 -6
- package/src/components/DocumentAnnotations/AnnotationContent.vue +25 -52
- package/src/components/DocumentAnnotations/AnnotationRow.vue +106 -50
- package/src/components/DocumentAnnotations/DocumentAnnotations.vue +12 -6
- 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/DocumentPage/{NewAnnotation.vue → AnnotationPopup.vue} +122 -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/main.js +14 -16
- package/src/store/display.js +33 -22
- package/src/store/document.js +1 -0
- 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
|
@@ -11,26 +11,14 @@
|
|
|
11
11
|
page.number === currentPage && 'current-page',
|
|
12
12
|
]"
|
|
13
13
|
>
|
|
14
|
-
<
|
|
15
|
-
v-if="
|
|
16
|
-
:
|
|
14
|
+
<AnnotationPopup
|
|
15
|
+
v-if="!isSelecting && spanSelectionsForPage(page).length > 0"
|
|
16
|
+
:spans="spanSelectionsForPage(page)"
|
|
17
17
|
:container-width="scaledViewport.width"
|
|
18
18
|
:container-height="scaledViewport.height"
|
|
19
19
|
:page="page"
|
|
20
20
|
@close="closePopups"
|
|
21
21
|
/>
|
|
22
|
-
<EditAnnotation
|
|
23
|
-
v-if="
|
|
24
|
-
editAnnotation &&
|
|
25
|
-
editAnnotation.pageNumber &&
|
|
26
|
-
editAnnotation.pageNumber === currentPage &&
|
|
27
|
-
selection
|
|
28
|
-
"
|
|
29
|
-
:edit-annotation="editAnnotation"
|
|
30
|
-
:page="page"
|
|
31
|
-
:container-width="scaledViewport.width"
|
|
32
|
-
:container-height="scaledViewport.height"
|
|
33
|
-
/>
|
|
34
22
|
|
|
35
23
|
<div
|
|
36
24
|
v-if="showAnnotationLabel"
|
|
@@ -70,7 +58,12 @@
|
|
|
70
58
|
}"
|
|
71
59
|
></v-rect>
|
|
72
60
|
</template>
|
|
73
|
-
<v-group
|
|
61
|
+
<v-group
|
|
62
|
+
v-if="
|
|
63
|
+
!categorizeModalIsActive || !publicView || !isDocumentReviewed
|
|
64
|
+
"
|
|
65
|
+
ref="entities"
|
|
66
|
+
>
|
|
74
67
|
<v-rect
|
|
75
68
|
v-for="(entity, index) in scaledEntities(page.entities, page)"
|
|
76
69
|
:key="index"
|
|
@@ -86,7 +79,7 @@
|
|
|
86
79
|
(bbox) => bbox.page_index + 1 == page.number
|
|
87
80
|
)"
|
|
88
81
|
>
|
|
89
|
-
<v-group
|
|
82
|
+
<v-group>
|
|
90
83
|
<v-rect
|
|
91
84
|
v-if="!isAnnotationInEditMode(annotation.id)"
|
|
92
85
|
:config="annotationRect(bbox, annotation.id)"
|
|
@@ -99,7 +92,7 @@
|
|
|
99
92
|
<template
|
|
100
93
|
v-if="annotation.metadata && annotation.metadata.checkbox"
|
|
101
94
|
>
|
|
102
|
-
<v-group
|
|
95
|
+
<v-group>
|
|
103
96
|
<v-rect
|
|
104
97
|
v-if="!isAnnotationInEditMode(annotation.id)"
|
|
105
98
|
:config="
|
|
@@ -122,12 +115,22 @@
|
|
|
122
115
|
</template>
|
|
123
116
|
</template>
|
|
124
117
|
</v-layer>
|
|
125
|
-
<v-layer
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
<v-layer>
|
|
119
|
+
<template
|
|
120
|
+
v-for="(span, index) in spanSelectionsForPage(page)"
|
|
121
|
+
:key="index"
|
|
122
|
+
>
|
|
123
|
+
<span-selection :id="index" :span="span" :page="page" />
|
|
124
|
+
</template>
|
|
125
|
+
<template
|
|
126
|
+
v-for="(span, index) in placeholderSelectionForPage(page)"
|
|
127
|
+
:key="`${index}_placeholder`"
|
|
128
|
+
>
|
|
129
|
+
<placeholder-selection :span="span" :page="page" />
|
|
130
|
+
</template>
|
|
131
|
+
<template v-if="page.number === selectionPage">
|
|
132
|
+
<box-selection :page="page" />
|
|
133
|
+
</template>
|
|
131
134
|
</v-layer>
|
|
132
135
|
</v-stage>
|
|
133
136
|
<b-skeleton
|
|
@@ -143,16 +146,18 @@ import { mapState, mapGetters, mapActions } from "vuex";
|
|
|
143
146
|
import { PIXEL_RATIO } from "../../constants";
|
|
144
147
|
import api from "../../api";
|
|
145
148
|
import BoxSelection from "./BoxSelection";
|
|
146
|
-
import
|
|
147
|
-
import
|
|
149
|
+
import SpanSelection from "./SpanSelection";
|
|
150
|
+
import PlaceholderSelection from "./PlaceholderSelection";
|
|
151
|
+
import AnnotationPopup from "./AnnotationPopup";
|
|
148
152
|
import AnnSetTableOptions from "./AnnSetTableOptions";
|
|
149
153
|
|
|
150
154
|
export default {
|
|
151
155
|
name: "DocumentPage",
|
|
152
156
|
components: {
|
|
153
157
|
BoxSelection,
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
SpanSelection,
|
|
159
|
+
PlaceholderSelection,
|
|
160
|
+
AnnotationPopup,
|
|
156
161
|
AnnSetTableOptions,
|
|
157
162
|
},
|
|
158
163
|
|
|
@@ -171,12 +176,15 @@ export default {
|
|
|
171
176
|
data() {
|
|
172
177
|
return {
|
|
173
178
|
image: null,
|
|
174
|
-
newAnnotation: [],
|
|
175
179
|
};
|
|
176
180
|
},
|
|
177
181
|
|
|
178
182
|
computed: {
|
|
179
|
-
...mapState("selection", [
|
|
183
|
+
...mapState("selection", [
|
|
184
|
+
"selectedEntities",
|
|
185
|
+
"spanSelection",
|
|
186
|
+
"isSelecting",
|
|
187
|
+
]),
|
|
180
188
|
...mapState("display", [
|
|
181
189
|
"scale",
|
|
182
190
|
"categorizeModalIsActive",
|
|
@@ -187,7 +195,6 @@ export default {
|
|
|
187
195
|
...mapState("document", [
|
|
188
196
|
"documentAnnotationSelected",
|
|
189
197
|
"recalculatingAnnotations",
|
|
190
|
-
"editAnnotation",
|
|
191
198
|
"selectedDocument",
|
|
192
199
|
"publicView",
|
|
193
200
|
"annotationId",
|
|
@@ -198,7 +205,11 @@ export default {
|
|
|
198
205
|
"bboxToRect",
|
|
199
206
|
"scaledEntities",
|
|
200
207
|
]),
|
|
201
|
-
...mapGetters("selection", [
|
|
208
|
+
...mapGetters("selection", [
|
|
209
|
+
"isSelectionValid",
|
|
210
|
+
"spanSelectionsForPage",
|
|
211
|
+
"placeholderSelectionForPage",
|
|
212
|
+
]),
|
|
202
213
|
...mapGetters("document", [
|
|
203
214
|
"getAnnotationsFiltered",
|
|
204
215
|
"isAnnotationInEditMode",
|
|
@@ -303,12 +314,7 @@ export default {
|
|
|
303
314
|
scale() {
|
|
304
315
|
this.closePopups();
|
|
305
316
|
},
|
|
306
|
-
|
|
307
|
-
if (!newValue) {
|
|
308
|
-
this.$store.dispatch("selection/setSpanSelection", null);
|
|
309
|
-
this.closePopups();
|
|
310
|
-
}
|
|
311
|
-
},
|
|
317
|
+
|
|
312
318
|
page(newValue, oldValue) {
|
|
313
319
|
if (newValue.image_url !== oldValue.image_url) {
|
|
314
320
|
this.drawPage(true);
|
|
@@ -337,7 +343,6 @@ export default {
|
|
|
337
343
|
isAnnotationFocused(annotationId) {
|
|
338
344
|
return (
|
|
339
345
|
this.documentAnnotationSelected &&
|
|
340
|
-
!this.isElementSelected &&
|
|
341
346
|
annotationId === this.documentAnnotationSelected.id
|
|
342
347
|
);
|
|
343
348
|
},
|
|
@@ -353,7 +358,8 @@ export default {
|
|
|
353
358
|
|
|
354
359
|
if (
|
|
355
360
|
event.target.getParent() &&
|
|
356
|
-
event.target.getParent().className === "Transformer"
|
|
361
|
+
event.target.getParent().className === "Transformer" &&
|
|
362
|
+
!event.target.name().includes("anchor")
|
|
357
363
|
) {
|
|
358
364
|
// if we are editing a box then close popups
|
|
359
365
|
this.closePopups();
|
|
@@ -369,7 +375,10 @@ export default {
|
|
|
369
375
|
event.target.name() === "multiAnnBoxTransformer" ||
|
|
370
376
|
event.target.name() === "multiAnnButton" ||
|
|
371
377
|
event.target.name() === "boxSelection" ||
|
|
372
|
-
event.target.name() === "boxTransformer"
|
|
378
|
+
event.target.name() === "boxTransformer" ||
|
|
379
|
+
event.target.name().includes("spanSelection") ||
|
|
380
|
+
event.target.name().includes("spanTransformer") ||
|
|
381
|
+
event.target.name().includes("anchor")
|
|
373
382
|
) {
|
|
374
383
|
return;
|
|
375
384
|
}
|
|
@@ -418,104 +427,8 @@ export default {
|
|
|
418
427
|
this.closePopups();
|
|
419
428
|
},
|
|
420
429
|
|
|
421
|
-
handleCreateAnnotationsFromSelection(entities) {
|
|
422
|
-
if (
|
|
423
|
-
this.categorizeModalIsActive ||
|
|
424
|
-
this.publicView ||
|
|
425
|
-
this.isDocumentReviewed
|
|
426
|
-
)
|
|
427
|
-
return;
|
|
428
|
-
this.newAnnotation = [];
|
|
429
|
-
|
|
430
|
-
const normalizedEntities = this.scaledEntities(entities, this.page);
|
|
431
|
-
if (normalizedEntities) {
|
|
432
|
-
this.newAnnotation.push(...normalizedEntities);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (this.newAnnotation.length > 0) {
|
|
436
|
-
this.$store.dispatch(
|
|
437
|
-
"selection/setSelectedEntities",
|
|
438
|
-
this.newAnnotation
|
|
439
|
-
);
|
|
440
|
-
this.$store.dispatch(
|
|
441
|
-
"selection/getTextFromEntities",
|
|
442
|
-
this.newAnnotation
|
|
443
|
-
);
|
|
444
|
-
} else {
|
|
445
|
-
this.$store.dispatch("selection/setSelectedEntities", null);
|
|
446
|
-
}
|
|
447
|
-
},
|
|
448
|
-
|
|
449
|
-
handleEntitiesFromSelection(entities) {
|
|
450
|
-
if (
|
|
451
|
-
this.categorizeModalIsActive ||
|
|
452
|
-
this.publicView ||
|
|
453
|
-
this.isDocumentReviewed
|
|
454
|
-
)
|
|
455
|
-
return;
|
|
456
|
-
|
|
457
|
-
const normalizedEntities = this.scaledEntities(entities, this.page);
|
|
458
|
-
if (normalizedEntities.length > 0) {
|
|
459
|
-
this.$store.dispatch(
|
|
460
|
-
"selection/setSelectedEntities",
|
|
461
|
-
normalizedEntities
|
|
462
|
-
);
|
|
463
|
-
this.$store.dispatch(
|
|
464
|
-
"selection/getTextFromEntities",
|
|
465
|
-
normalizedEntities
|
|
466
|
-
);
|
|
467
|
-
} else {
|
|
468
|
-
this.$store.dispatch("selection/setSelectedEntities", null);
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
|
-
|
|
472
430
|
handleClickedEntity(entity) {
|
|
473
|
-
|
|
474
|
-
!entity ||
|
|
475
|
-
this.categorizeModalIsActive ||
|
|
476
|
-
this.publicView ||
|
|
477
|
-
this.isDocumentReviewed
|
|
478
|
-
)
|
|
479
|
-
return;
|
|
480
|
-
|
|
481
|
-
// Check if we are creating a new Annotation
|
|
482
|
-
// or if we are removing a previously selected entity
|
|
483
|
-
// or editing empty one
|
|
484
|
-
const entityToAdd = entity;
|
|
485
|
-
|
|
486
|
-
const found = this.newAnnotation.find(
|
|
487
|
-
(ann) =>
|
|
488
|
-
ann.scaled.width === entityToAdd.scaled.width &&
|
|
489
|
-
ann.original.offset_string === entityToAdd.original.offset_string
|
|
490
|
-
);
|
|
491
|
-
|
|
492
|
-
// reset the selection so that we don't have a drawn rectangle when editing based on entities
|
|
493
|
-
this.endSelection();
|
|
494
|
-
|
|
495
|
-
if (found) {
|
|
496
|
-
this.newAnnotation = [
|
|
497
|
-
...this.newAnnotation.filter(
|
|
498
|
-
(ann) =>
|
|
499
|
-
ann.scaled.width !== entityToAdd.scaled.width &&
|
|
500
|
-
ann.original.offset_string !== entityToAdd.original.offset_string
|
|
501
|
-
),
|
|
502
|
-
];
|
|
503
|
-
} else {
|
|
504
|
-
this.newAnnotation.push(entityToAdd);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (this.newAnnotation.length > 0) {
|
|
508
|
-
this.$store.dispatch(
|
|
509
|
-
"selection/setSelectedEntities",
|
|
510
|
-
this.newAnnotation
|
|
511
|
-
);
|
|
512
|
-
this.$store.dispatch(
|
|
513
|
-
"selection/getTextFromEntities",
|
|
514
|
-
this.newAnnotation
|
|
515
|
-
);
|
|
516
|
-
} else {
|
|
517
|
-
this.$store.dispatch("selection/setSelectedEntities", null);
|
|
518
|
-
}
|
|
431
|
+
this.$store.dispatch("selection/entityClick", entity);
|
|
519
432
|
},
|
|
520
433
|
|
|
521
434
|
onElementEnter(annotation = null, span = null) {
|
|
@@ -593,24 +506,11 @@ export default {
|
|
|
593
506
|
* Builds the konva config object for the entity.
|
|
594
507
|
*/
|
|
595
508
|
entityRect(entity) {
|
|
596
|
-
let entityIsSelected = false;
|
|
597
|
-
if (this.selectedEntities && this.selectedEntities.length > 0) {
|
|
598
|
-
entityIsSelected = this.selectedEntities.find((selectedEntity) => {
|
|
599
|
-
return (
|
|
600
|
-
selectedEntity.original &&
|
|
601
|
-
selectedEntity.original.offset_string ===
|
|
602
|
-
entity.original.offset_string &&
|
|
603
|
-
selectedEntity.original.x0 === entity.original.x0 &&
|
|
604
|
-
selectedEntity.original.y0 === entity.original.y0
|
|
605
|
-
);
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
|
|
609
509
|
return {
|
|
610
510
|
stroke: "#ccc",
|
|
611
511
|
strokeWidth: 1,
|
|
612
512
|
dash: [5, 2],
|
|
613
|
-
fill:
|
|
513
|
+
fill: "transparent",
|
|
614
514
|
globalCompositeOperation: "multiply",
|
|
615
515
|
transformsEnabled: "position",
|
|
616
516
|
hitStrokeWidth: 0,
|
|
@@ -676,7 +576,10 @@ export default {
|
|
|
676
576
|
}
|
|
677
577
|
},
|
|
678
578
|
closePopups() {
|
|
679
|
-
this.
|
|
579
|
+
this.$store.dispatch("selection/entitySelection", {
|
|
580
|
+
entities: [],
|
|
581
|
+
selection: null,
|
|
582
|
+
});
|
|
680
583
|
},
|
|
681
584
|
},
|
|
682
585
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-group>
|
|
3
|
+
<v-rect ref="placeholderSelection" :config="placeholderConfig" />
|
|
4
|
+
</v-group>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
import { mapGetters } from "vuex";
|
|
9
|
+
export default {
|
|
10
|
+
props: {
|
|
11
|
+
span: {
|
|
12
|
+
required: true,
|
|
13
|
+
type: Object,
|
|
14
|
+
},
|
|
15
|
+
page: {
|
|
16
|
+
required: true,
|
|
17
|
+
type: Object,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
data() {
|
|
21
|
+
return {
|
|
22
|
+
selection: {
|
|
23
|
+
start: null,
|
|
24
|
+
end: null,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
computed: {
|
|
29
|
+
placeholderConfig() {
|
|
30
|
+
const box = this.bboxToRect(this.page, this.span);
|
|
31
|
+
const primaryColor = window
|
|
32
|
+
.getComputedStyle(document.body)
|
|
33
|
+
.getPropertyValue("--primary-color");
|
|
34
|
+
return {
|
|
35
|
+
x: box.x,
|
|
36
|
+
y: box.y,
|
|
37
|
+
width: box.width,
|
|
38
|
+
height: box.height,
|
|
39
|
+
fill: "transparent",
|
|
40
|
+
stroke: primaryColor,
|
|
41
|
+
strokeWidth: 3,
|
|
42
|
+
globalCompositeOperation: "multiply",
|
|
43
|
+
shadowForStrokeEnabled: false,
|
|
44
|
+
name: "placeholderSelection",
|
|
45
|
+
draggable: false,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
...mapGetters("display", ["bboxToRect"]),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
</script>
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-group>
|
|
3
|
+
<v-rect
|
|
4
|
+
ref="spanSelection"
|
|
5
|
+
:config="config"
|
|
6
|
+
:stroke-scale-enabled="false"
|
|
7
|
+
@dragend="onChange"
|
|
8
|
+
@transformend="onChange"
|
|
9
|
+
/>
|
|
10
|
+
<v-transformer
|
|
11
|
+
v-if="!isSelectionValid"
|
|
12
|
+
ref="spanTransformer"
|
|
13
|
+
:config="transformerConfig"
|
|
14
|
+
/>
|
|
15
|
+
</v-group>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
import { mapGetters, mapState } from "vuex";
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
props: {
|
|
23
|
+
id: {
|
|
24
|
+
required: true,
|
|
25
|
+
type: Number,
|
|
26
|
+
},
|
|
27
|
+
page: {
|
|
28
|
+
required: true,
|
|
29
|
+
type: Object,
|
|
30
|
+
},
|
|
31
|
+
span: {
|
|
32
|
+
required: true,
|
|
33
|
+
type: Object,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
data() {
|
|
37
|
+
return {
|
|
38
|
+
selection: {
|
|
39
|
+
start: null,
|
|
40
|
+
end: null,
|
|
41
|
+
},
|
|
42
|
+
box: {
|
|
43
|
+
x: 0,
|
|
44
|
+
y: 0,
|
|
45
|
+
width: 0,
|
|
46
|
+
height: 0,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
computed: {
|
|
51
|
+
/**
|
|
52
|
+
* Konva options of the selection rectangle, based on the
|
|
53
|
+
* `selection` object.
|
|
54
|
+
*/
|
|
55
|
+
config() {
|
|
56
|
+
const primaryColor = window
|
|
57
|
+
.getComputedStyle(document.body)
|
|
58
|
+
.getPropertyValue("--primary-color");
|
|
59
|
+
return {
|
|
60
|
+
x: this.box.x,
|
|
61
|
+
y: this.box.y,
|
|
62
|
+
width: this.box.width,
|
|
63
|
+
height: this.box.height,
|
|
64
|
+
stroke: "#7B61FFB3",
|
|
65
|
+
fill: `${primaryColor}77`,
|
|
66
|
+
strokeWidth: 1,
|
|
67
|
+
globalCompositeOperation: "multiply",
|
|
68
|
+
shadowForStrokeEnabled: false,
|
|
69
|
+
perfectDrawEnabled: false,
|
|
70
|
+
name: `spanSelection_${this.id}`,
|
|
71
|
+
draggable: true,
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
transformerConfig() {
|
|
75
|
+
return {
|
|
76
|
+
borderEnabled: false,
|
|
77
|
+
rotateEnabled: false,
|
|
78
|
+
ignoreStroke: true,
|
|
79
|
+
keepRatio: false,
|
|
80
|
+
anchorStroke: "#7B61FF",
|
|
81
|
+
anchorSize: 6,
|
|
82
|
+
name: `spanTransformer_${this.id}`,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
...mapState("document", ["editAnnotation"]),
|
|
86
|
+
...mapGetters("display", ["clientToBbox", "bboxToRect", "scaledEntities"]),
|
|
87
|
+
...mapGetters("selection", ["entitiesOnSelection", "isSelectionValid"]),
|
|
88
|
+
},
|
|
89
|
+
watch: {
|
|
90
|
+
box() {
|
|
91
|
+
this.updateTransformer();
|
|
92
|
+
this.setSelection();
|
|
93
|
+
},
|
|
94
|
+
span() {
|
|
95
|
+
this.box = this.bboxToRect(this.page, this.span);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
mounted() {
|
|
99
|
+
this.box = this.bboxToRect(this.page, this.span);
|
|
100
|
+
this.setSelection();
|
|
101
|
+
this.$nextTick(() => {
|
|
102
|
+
this.updateTransformer();
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
methods: {
|
|
106
|
+
setSelection() {
|
|
107
|
+
this.selection = {
|
|
108
|
+
start: {
|
|
109
|
+
x: this.box.x,
|
|
110
|
+
y: this.box.y,
|
|
111
|
+
},
|
|
112
|
+
end: {
|
|
113
|
+
x: this.box.x + this.box.width,
|
|
114
|
+
y: this.box.y + this.box.height,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
startSelection(start) {
|
|
119
|
+
this.selection.start = start;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
moveSelection(points) {
|
|
123
|
+
// only apply when we have a large enough selection, otherwise we risk counting misclicks
|
|
124
|
+
const xDiff = Math.abs(this.selection.start.x - points.end.x);
|
|
125
|
+
const yDiff = Math.abs(this.selection.start.y - points.end.y);
|
|
126
|
+
if (xDiff > 5 && yDiff > 5) {
|
|
127
|
+
const { start, end } = points;
|
|
128
|
+
if (start) {
|
|
129
|
+
this.selection.start = start;
|
|
130
|
+
}
|
|
131
|
+
if (end) {
|
|
132
|
+
this.selection.end = end;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
endSelection(end) {
|
|
138
|
+
let xDiff;
|
|
139
|
+
let yDiff;
|
|
140
|
+
|
|
141
|
+
if (end) {
|
|
142
|
+
xDiff = Math.abs(this.selection.start.x - end.x);
|
|
143
|
+
yDiff = Math.abs(this.selection.start.y - end.y);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// if "end" is not provided, start and end points are the same, or if we have a selection smaller than 5x5,
|
|
147
|
+
// just reset
|
|
148
|
+
if (
|
|
149
|
+
!end ||
|
|
150
|
+
(yDiff <= 5 && xDiff <= 5) ||
|
|
151
|
+
(this.selection.start.x === end.x && this.selection.start.y == end.y)
|
|
152
|
+
) {
|
|
153
|
+
this.selection.start = null;
|
|
154
|
+
this.selection.end = null;
|
|
155
|
+
} else {
|
|
156
|
+
this.selection.start.x = this.selection.start.x - selectionPadding;
|
|
157
|
+
this.selection.start.y = this.selection.start.y - selectionPadding;
|
|
158
|
+
|
|
159
|
+
end.x = end.x + selectionPadding;
|
|
160
|
+
end.y = end.y + selectionPadding;
|
|
161
|
+
|
|
162
|
+
this.selection.end = end;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
handleSelection() {
|
|
166
|
+
this.box = {
|
|
167
|
+
x: this.selection.start.x,
|
|
168
|
+
y: this.selection.start.y,
|
|
169
|
+
width: this.selection.end.x - this.selection.start.x,
|
|
170
|
+
height: this.selection.end.y - this.selection.start.y,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const box = this.clientToBbox(
|
|
174
|
+
this.page,
|
|
175
|
+
this.selection.start,
|
|
176
|
+
this.selection.end
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
this.$store.dispatch("selection/entitySelection", {
|
|
180
|
+
entities: this.scaledEntities(
|
|
181
|
+
this.entitiesOnSelection(box, this.page),
|
|
182
|
+
this.page
|
|
183
|
+
),
|
|
184
|
+
selection: box,
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
updateTransformer() {
|
|
188
|
+
// here we need to manually attach or detach Transformer node
|
|
189
|
+
const transformer = this.$refs.spanTransformer;
|
|
190
|
+
|
|
191
|
+
// maybe we're out of sync and the transformer is not available, just return
|
|
192
|
+
if (!transformer) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const transformerNode = transformer.getNode();
|
|
197
|
+
const stage = transformerNode.getStage();
|
|
198
|
+
let selectedNode;
|
|
199
|
+
if (stage) {
|
|
200
|
+
selectedNode = stage.findOne(`.spanSelection_${this.id}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// do nothing if selected node is already attached
|
|
204
|
+
if (selectedNode === transformerNode.node()) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (selectedNode) {
|
|
209
|
+
// attach to another node
|
|
210
|
+
transformerNode.nodes([selectedNode]);
|
|
211
|
+
} else {
|
|
212
|
+
// remove transformer
|
|
213
|
+
transformerNode.nodes([]);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
transformerNode.getLayer().batchDraw();
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* This method is used for both transforms and drags since it just
|
|
221
|
+
* retrieves the rect's new attributes from the event and uses those
|
|
222
|
+
* to set the new selection state.
|
|
223
|
+
*/
|
|
224
|
+
onChange(event) {
|
|
225
|
+
const { x, y, scaleX, scaleY, skewX, width, height } = event.target.attrs;
|
|
226
|
+
const realWidth = width * scaleX;
|
|
227
|
+
const realHeight = height * scaleY;
|
|
228
|
+
let start;
|
|
229
|
+
let end;
|
|
230
|
+
|
|
231
|
+
// we need to figure out if there's skewing going on, to fix start/end points
|
|
232
|
+
// (other cases appear to fix themselves automatically)
|
|
233
|
+
if (skewX >= 0) {
|
|
234
|
+
start = { x, y };
|
|
235
|
+
end = {
|
|
236
|
+
x: start.x + realWidth,
|
|
237
|
+
y: start.y + realHeight,
|
|
238
|
+
};
|
|
239
|
+
} else {
|
|
240
|
+
end = { x, y };
|
|
241
|
+
start = { x: end.x - realWidth, y: end.y - realHeight };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this.moveSelection({ start, end });
|
|
245
|
+
|
|
246
|
+
// reset node's everything after transform (we don't want to deal with that,
|
|
247
|
+
// just with regular x/y/width/height)
|
|
248
|
+
const node = this.$refs.spanSelection.getNode();
|
|
249
|
+
node.skewX(0);
|
|
250
|
+
node.skewY(0);
|
|
251
|
+
node.rotation(0);
|
|
252
|
+
node.scaleX(1);
|
|
253
|
+
node.scaleY(1);
|
|
254
|
+
|
|
255
|
+
this.handleSelection();
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
</script>
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<b-skeleton
|
|
4
|
-
width="40px"
|
|
5
|
-
height="57px"
|
|
6
|
-
/>
|
|
2
|
+
<div class="loading-thumbnail">
|
|
3
|
+
<b-skeleton width="40px" height="57px" />
|
|
7
4
|
</div>
|
|
8
5
|
</template>
|
|
9
6
|
|
|
@@ -14,7 +11,7 @@
|
|
|
14
11
|
*/
|
|
15
12
|
|
|
16
13
|
export default {
|
|
17
|
-
name: "LoadingThumbnail"
|
|
14
|
+
name: "LoadingThumbnail",
|
|
18
15
|
};
|
|
19
16
|
</script>
|
|
20
17
|
|
|
@@ -159,7 +159,7 @@ export default {
|
|
|
159
159
|
created() {
|
|
160
160
|
window.addEventListener("resize", this.handleResize);
|
|
161
161
|
},
|
|
162
|
-
|
|
162
|
+
unmounted() {
|
|
163
163
|
window.removeEventListener("resize", this.handleResize);
|
|
164
164
|
},
|
|
165
165
|
mounted() {
|
|
@@ -174,7 +174,9 @@ export default {
|
|
|
174
174
|
this.$store.dispatch("document/setErrorMessageWidth", width);
|
|
175
175
|
},
|
|
176
176
|
handleResize() {
|
|
177
|
-
|
|
177
|
+
if (this.$refs.documentTopBar) {
|
|
178
|
+
this.setComponentWidth(this.$refs.documentTopBar.offsetWidth);
|
|
179
|
+
}
|
|
178
180
|
},
|
|
179
181
|
getPreviousAndNextDocuments(filteredDocuments) {
|
|
180
182
|
if (!filteredDocuments) return;
|
package/src/constants.js
CHANGED
|
@@ -2,10 +2,4 @@ export const PIXEL_RATIO = window.devicePixelRatio || 1;
|
|
|
2
2
|
export const VIEWPORT_RATIO = 0.98;
|
|
3
3
|
export const MINIMUM_APP_WIDTH = 600;
|
|
4
4
|
export const MINIMUM_OPTIMIZED_APP_WIDTH = 950;
|
|
5
|
-
export const TEXT_BREAKPOINT_WIDTH =
|
|
6
|
-
if (locale === "en") {
|
|
7
|
-
return 1350;
|
|
8
|
-
} else {
|
|
9
|
-
return 1800;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
5
|
+
export const TEXT_BREAKPOINT_WIDTH = 1800;
|