@konfuzio/document-validation-ui 0.1.19-dev.1 → 0.1.19

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.
Files changed (145) hide show
  1. package/.eslintrc.js +10 -10
  2. package/.prettierrc.json +1 -1
  3. package/LICENSE +21 -21
  4. package/README.md +24 -24
  5. package/cypress.config.js +13 -13
  6. package/dist/css/app.css +1 -1
  7. package/dist/index.html +1 -1
  8. package/dist/js/app.js +1 -1
  9. package/dist/js/app.js.map +1 -1
  10. package/dist/js/chunk-vendors.js +1 -1
  11. package/dist/js/chunk-vendors.js.map +1 -1
  12. package/jest.config.js +4 -4
  13. package/package.json +66 -66
  14. package/src/api.js +82 -82
  15. package/src/assets/images/AcceptedCheckMark.vue +8 -8
  16. package/src/assets/images/AcceptedUser.vue +8 -8
  17. package/src/assets/images/ActionIcon.vue +60 -60
  18. package/src/assets/images/ArrowDownKey.vue +11 -11
  19. package/src/assets/images/ArrowUpKey.vue +11 -11
  20. package/src/assets/images/CategoryIconImg.vue +13 -13
  21. package/src/assets/images/CheckMark.vue +8 -8
  22. package/src/assets/images/DraggableIcon.vue +14 -14
  23. package/src/assets/images/EditDocIcon.vue +12 -12
  24. package/src/assets/images/EmptyStateImg.vue +129 -129
  25. package/src/assets/images/ErrorIcon.vue +28 -28
  26. package/src/assets/images/EyeIcon.vue +11 -11
  27. package/src/assets/images/FileNameNotSavedImage.vue +26 -26
  28. package/src/assets/images/FileNameSavedImage.vue +14 -14
  29. package/src/assets/images/FitZoomIcon.vue +16 -16
  30. package/src/assets/images/GridIcon.vue +16 -16
  31. package/src/assets/images/KeyboardIcon.vue +16 -16
  32. package/src/assets/images/MagicWandIcon.vue +16 -16
  33. package/src/assets/images/MinusIcon.vue +13 -13
  34. package/src/assets/images/NotFoundIcon.vue +16 -16
  35. package/src/assets/images/NotOptimizedIllustration.vue +651 -651
  36. package/src/assets/images/PlusIcon.vue +13 -13
  37. package/src/assets/images/QuestionMark.vue +12 -12
  38. package/src/assets/images/ServerImage.vue +73 -73
  39. package/src/assets/images/SettingsIcon.vue +14 -14
  40. package/src/assets/images/SplitLines.vue +18 -18
  41. package/src/assets/images/SplitZigZag.vue +49 -49
  42. package/src/assets/images/StarIcon.vue +16 -16
  43. package/src/assets/images/StatusImg.vue +14 -14
  44. package/src/assets/images/TranslateArrows.vue +33 -33
  45. package/src/assets/scss/ann_set_table_options.scss +26 -26
  46. package/src/assets/scss/annotation_details.scss +141 -141
  47. package/src/assets/scss/choose_label_set_modal.scss +65 -65
  48. package/src/assets/scss/document_action_bar.scss +37 -37
  49. package/src/assets/scss/document_annotations.scss +558 -537
  50. package/src/assets/scss/document_category.scss +85 -85
  51. package/src/assets/scss/document_dashboard.scss +52 -52
  52. package/src/assets/scss/document_edit.scss +410 -410
  53. package/src/assets/scss/document_error.scss +81 -81
  54. package/src/assets/scss/document_name.scss +60 -60
  55. package/src/assets/scss/document_page.scss +12 -12
  56. package/src/assets/scss/document_thumbnails.scss +41 -41
  57. package/src/assets/scss/document_toolbar.scss +111 -111
  58. package/src/assets/scss/document_top_bar.scss +171 -171
  59. package/src/assets/scss/document_viewport_modal.scss +25 -25
  60. package/src/assets/scss/documents_list.scss +141 -141
  61. package/src/assets/scss/edit_page_thumbnail.scss +53 -53
  62. package/src/assets/scss/empty_state.scss +34 -34
  63. package/src/assets/scss/extracting_data.scss +35 -35
  64. package/src/assets/scss/imports.scss +1 -1
  65. package/src/assets/scss/multi_ann_table_overlay.scss +38 -38
  66. package/src/assets/scss/multi_ann_table_popup.scss +12 -12
  67. package/src/assets/scss/new_annotation.scss +102 -102
  68. package/src/assets/scss/scrolling_document.scss +19 -19
  69. package/src/assets/scss/theme.scss +801 -801
  70. package/src/assets/scss/variables.scss +66 -66
  71. package/src/components/App.cy.js +7 -7
  72. package/src/components/App.vue +187 -187
  73. package/src/components/DocumentAnnotations/AnnotationActionButtons.vue +152 -168
  74. package/src/components/DocumentAnnotations/AnnotationContent.vue +210 -210
  75. package/src/components/DocumentAnnotations/AnnotationDetails.vue +251 -251
  76. package/src/components/DocumentAnnotations/AnnotationRow.vue +752 -752
  77. package/src/components/DocumentAnnotations/AnnotationSetActionButtons.vue +89 -89
  78. package/src/components/DocumentAnnotations/ChooseLabelSetModal.vue +186 -186
  79. package/src/components/DocumentAnnotations/DocumentAnnotations.cy.js +441 -441
  80. package/src/components/DocumentAnnotations/DocumentAnnotations.vue +534 -534
  81. package/src/components/DocumentAnnotations/DocumentLabel.vue +189 -189
  82. package/src/components/DocumentAnnotations/EmptyAnnotation.vue +193 -193
  83. package/src/components/DocumentAnnotations/EmptyState.vue +21 -21
  84. package/src/components/DocumentAnnotations/ExtractingData.vue +41 -41
  85. package/src/components/DocumentAnnotations/LoadingAnnotations.vue +43 -43
  86. package/src/components/DocumentAnnotations/LoadingLabels.vue +43 -43
  87. package/src/components/DocumentAnnotations/MultiAnnotationTableOverlay.vue +338 -338
  88. package/src/components/DocumentAnnotations/index.js +8 -8
  89. package/src/components/DocumentCategory.vue +281 -281
  90. package/src/components/DocumentDashboard.vue +170 -170
  91. package/src/components/DocumentEdit/DocumentEdit.cy.js +541 -541
  92. package/src/components/DocumentEdit/DocumentEdit.vue +503 -503
  93. package/src/components/DocumentEdit/EditConfirmationModal.vue +55 -55
  94. package/src/components/DocumentEdit/EditPageThumbnail.vue +114 -114
  95. package/src/components/DocumentEdit/EditPages.vue +161 -161
  96. package/src/components/DocumentEdit/EditSidebar.vue +154 -154
  97. package/src/components/DocumentEdit/RenameAndCategorize.vue +184 -184
  98. package/src/components/DocumentEdit/SidebarButtons.vue +53 -53
  99. package/src/components/DocumentEdit/SplitInfoBar.vue +21 -21
  100. package/src/components/DocumentEdit/index.js +4 -4
  101. package/src/components/DocumentModals/DocumentErrorModal.vue +58 -58
  102. package/src/components/DocumentModals/NotOptimizedViewportModal.vue +51 -51
  103. package/src/components/DocumentPage/ActionBar.vue +48 -48
  104. package/src/components/DocumentPage/AnnSetTableOptions.vue +111 -111
  105. package/src/components/DocumentPage/BoxSelection.vue +152 -152
  106. package/src/components/DocumentPage/DocumentPage.cy.js +92 -92
  107. package/src/components/DocumentPage/DocumentPage.vue +568 -568
  108. package/src/components/DocumentPage/DocumentToolbar.cy.js +215 -215
  109. package/src/components/DocumentPage/DocumentToolbar.vue +228 -228
  110. package/src/components/DocumentPage/DummyPage.vue +55 -55
  111. package/src/components/DocumentPage/MultiAnnSelection.vue +371 -371
  112. package/src/components/DocumentPage/NewAnnotation.vue +308 -308
  113. package/src/components/DocumentPage/ScrollingDocument.vue +149 -149
  114. package/src/components/DocumentPage/ScrollingPage.vue +179 -179
  115. package/src/components/DocumentPage/index.js +5 -5
  116. package/src/components/DocumentThumbnails/DocumentThumbnails.cy.js +67 -67
  117. package/src/components/DocumentThumbnails/DocumentThumbnails.vue +132 -132
  118. package/src/components/DocumentThumbnails/LoadingThumbnail.vue +25 -25
  119. package/src/components/DocumentThumbnails/index.js +1 -1
  120. package/src/components/DocumentTopBar/DocumentName.vue +236 -236
  121. package/src/components/DocumentTopBar/DocumentTopBar.cy.js +222 -222
  122. package/src/components/DocumentTopBar/DocumentTopBar.vue +202 -202
  123. package/src/components/DocumentTopBar/DocumentTopBarButtons.vue +183 -183
  124. package/src/components/DocumentTopBar/KeyboardActionsDescription.vue +74 -74
  125. package/src/components/DocumentTopBar/index.js +3 -3
  126. package/src/components/DocumentsList/DocumentsList.vue +121 -121
  127. package/src/components/DocumentsList/index.js +1 -1
  128. package/src/components/ErrorMessage.vue +40 -40
  129. package/src/components/index.js +1 -1
  130. package/src/constants.js +5 -5
  131. package/src/directives/scroll.js +28 -28
  132. package/src/i18n.js +22 -22
  133. package/src/icons.js +45 -45
  134. package/src/locales/de.json +148 -148
  135. package/src/locales/en.json +148 -148
  136. package/src/main.js +26 -26
  137. package/src/store/category.js +191 -191
  138. package/src/store/display.js +311 -311
  139. package/src/store/document.js +1438 -1438
  140. package/src/store/edit.js +316 -316
  141. package/src/store/index.js +21 -21
  142. package/src/store/project.js +143 -143
  143. package/src/store/selection.js +210 -210
  144. package/src/utils/utils.js +54 -54
  145. package/vue.config.js +25 -25
@@ -1,371 +1,371 @@
1
- <template>
2
- <v-group>
3
- <v-rect
4
- ref="multiAnnBoxSelection"
5
- :config="tableConfig"
6
- :stroke-scale-enabled="false"
7
- @dragstart="changeStart"
8
- @dragend="onChange"
9
- @transformstart="changeStart"
10
- @transformend="onChange"
11
- />
12
- <v-transformer ref="multiAnnBoxTransformer" :config="transformerConfig" />
13
- <v-label
14
- v-if="showButton"
15
- :config="buttonConfig"
16
- @mouseenter="onButtonEnter"
17
- @mouseleave="onButtonLeave"
18
- >
19
- <v-tag
20
- :config="{
21
- fill: '#7B61FF',
22
- cornerRadius: 4,
23
- name: 'tag',
24
- }"
25
- />
26
- <v-text
27
- :config="{
28
- align: 'right',
29
- width: buttonWidth,
30
- ellipsis: true,
31
- wrap: 'none',
32
- padding: 8,
33
- text: $t('new_multi_ann_title'),
34
- fill: 'white',
35
- fontSize: 14,
36
- cursor: 'pointer',
37
- name: 'multiAnnButton',
38
- }"
39
- @click="openMultiAnnotationModal"
40
- />
41
- </v-label>
42
- </v-group>
43
- </template>
44
-
45
- <script>
46
- import { mapGetters, mapState, mapActions } from "vuex";
47
- import { table_reference_api } from "../../store/document";
48
-
49
- export default {
50
- props: {
51
- page: {
52
- required: true,
53
- type: Object,
54
- },
55
- },
56
- data() {
57
- const BUTTON_HEIGHT = 36;
58
- const BUTTON_WIDTH = this.$i18n.locale === "de" ? 238 : 187;
59
- return {
60
- buttonWidth: BUTTON_WIDTH,
61
- buttonHeight: BUTTON_HEIGHT,
62
- isEditing: false,
63
- showTable: false,
64
- entities: [],
65
- };
66
- },
67
- computed: {
68
- showButton() {
69
- return !this.isSelecting && !this.isEditing;
70
- },
71
- transformerConfig() {
72
- return {
73
- borderEnabled: false,
74
- rotateEnabled: false,
75
- ignoreStroke: true,
76
- keepRatio: false,
77
- anchorStroke: "#7B61FF",
78
- anchorSize: 6,
79
- };
80
- },
81
- tableConfig() {
82
- return {
83
- x: this.selection.start.x,
84
- y: this.selection.start.y,
85
- width: this.selection.end.x - this.selection.start.x,
86
- height: this.selection.end.y - this.selection.start.y,
87
- fill: this.isSelecting ? "#7B61FFB3" : "#7B61FF33",
88
- stroke: this.isSelecting ? "transparent" : "#7B61FFB3",
89
- strokeWidth: 1,
90
- globalCompositeOperation: "multiply",
91
- shadowForStrokeEnabled: false,
92
- name: "multiAnnBoxSelection",
93
- draggable: true,
94
- };
95
- },
96
- buttonConfig() {
97
- let x = this.selection.end.x;
98
- let y = this.selection.end.y;
99
- const marginTop = 4;
100
-
101
- // check if selection was made from right to left
102
- if (x < this.selection.start.x) {
103
- x = this.selection.start.x;
104
- }
105
- if (y < this.selection.start.y) {
106
- y = this.selection.start.y;
107
- }
108
- x = x - this.buttonWidth;
109
- y = y + marginTop;
110
-
111
- return {
112
- x: x > 0 ? x : 0,
113
- y: y > 0 ? y : 0,
114
- height: this.buttonHeight,
115
- width: this.buttonWidth,
116
- };
117
- },
118
- ...mapState("selection", ["selection", "isSelecting"]),
119
- ...mapState("document", ["documentId"]),
120
- ...mapGetters("display", ["clientToBbox"]),
121
- ...mapGetters("document", ["entitiesOnSelection"]),
122
- },
123
- watch: {
124
- isSelecting(newValue, oldValue) {
125
- // if it ends selection
126
- if (!newValue && oldValue) {
127
- this.calculateEntities();
128
- this.updateTransformer();
129
- }
130
- },
131
- },
132
- methods: {
133
- openMultiAnnotationModal() {
134
- this.$store.dispatch("display/showChooseLabelSetModal", {
135
- show: true,
136
- isMultipleAnnotations: true,
137
- finish: this.submitAnnotations,
138
- });
139
- },
140
-
141
- async submitAnnotations(labelSet) {
142
- const columns = labelSet.labels.map((label) => {
143
- return {
144
- field: `${label.id}`,
145
- label: label.name,
146
- centered: true,
147
- };
148
- });
149
-
150
- const orderedEntities = this.processRows(columns);
151
-
152
- const annotations = [];
153
-
154
- orderedEntities.forEach((orderedEntity) => {
155
- annotations.push({
156
- document: this.documentId,
157
- span: orderedEntity.spans,
158
- label: orderedEntity.label_id,
159
- is_correct: true,
160
- revised: false,
161
- label_set: labelSet.id,
162
- set_reference: orderedEntity.row_index,
163
- origin: table_reference_api,
164
- });
165
- });
166
-
167
- this.$store
168
- .dispatch("document/createAnnotation", annotations)
169
- .then(() => {
170
- this.$store.dispatch("selection/disableSelection");
171
- this.$emit("finished");
172
- })
173
- .catch((error) => {
174
- this.$store.dispatch("document/createErrorMessage", {
175
- error,
176
- serverErrorMessage: this.$t("server_error"),
177
- defaultErrorMessage: this.$t("error_creating_multi_ann"),
178
- });
179
- this.$store.dispatch("selection/disableSelection");
180
- this.$emit("finished");
181
- });
182
- },
183
-
184
- onButtonEnter() {
185
- this.$emit("buttonEnter");
186
- },
187
-
188
- onButtonLeave() {
189
- this.$emit("buttonLeave");
190
- },
191
- changeStart() {
192
- this.isEditing = true;
193
- },
194
- /**
195
- * This method is used for both transforms and drags since it just
196
- * retrieves the rect's new attributes from the event and uses those
197
- * to set the new selection state.
198
- */
199
- onChange(event) {
200
- this.isEditing = false;
201
-
202
- const { x, y, scaleX, scaleY, skewX, width, height } = event.target.attrs;
203
- const realWidth = width * scaleX;
204
- const realHeight = height * scaleY;
205
- let start;
206
- let end;
207
-
208
- // we need to figure out if there's skewing going on, to fix start/end points
209
- // (other cases appear to fix themselves automatically)
210
- if (skewX >= 0) {
211
- start = { x, y };
212
- end = {
213
- x: start.x + realWidth,
214
- y: start.y + realHeight,
215
- };
216
- } else {
217
- end = { x, y };
218
- start = { x: end.x - realWidth, y: end.y - realHeight };
219
- }
220
-
221
- this.moveSelection({ start, end });
222
-
223
- // reset node's everything after transform (we don't want to deal with that,
224
- // just with regular x/y/width/height)
225
- const node = this.$refs.multiAnnBoxSelection.getNode();
226
- node.skewX(0);
227
- node.skewY(0);
228
- node.rotation(0);
229
- node.scaleX(1);
230
- node.scaleY(1);
231
-
232
- this.calculateEntities();
233
- this.updateTransformer();
234
- },
235
-
236
- updateTransformer() {
237
- // here we need to manually attach or detach Transformer node
238
- const transformer = this.$refs.multiAnnBoxTransformer;
239
-
240
- // maybe we're out of sync and the transformer is not available, just return
241
- if (!transformer) {
242
- return;
243
- }
244
-
245
- const transformerNode = transformer.getNode();
246
- const stage = transformerNode.getStage();
247
- const selectedNode = stage.findOne(".multiAnnBoxSelection");
248
-
249
- // do nothing if selected node is already attached
250
- if (selectedNode === transformerNode.node()) {
251
- return;
252
- }
253
-
254
- if (selectedNode) {
255
- // attach to another node
256
- transformerNode.nodes([selectedNode]);
257
- } else {
258
- // remove transformer
259
- transformerNode.nodes([]);
260
- }
261
-
262
- transformerNode.getLayer().batchDraw();
263
- },
264
-
265
- calculateEntities() {
266
- const box = this.clientToBbox(
267
- this.page,
268
- this.selection.start,
269
- this.selection.end
270
- );
271
- const entities = this.entitiesOnSelection(box, this.page);
272
-
273
- const offset = 4;
274
- if (entities.length === 0) {
275
- return;
276
- }
277
-
278
- const rows = [...new Set(entities.map((o) => o["top"]))];
279
- rows.sort();
280
-
281
- let joinRow = 0;
282
- const jointRows = [];
283
-
284
- // group entities that are near each other on Y axis
285
- rows.forEach((row) => {
286
- if (row - joinRow > offset) {
287
- joinRow = row;
288
- jointRows.push(row);
289
- }
290
- });
291
-
292
- let cols = {};
293
- jointRows.forEach((row) => {
294
- const entityRow = [];
295
- entities.forEach((item) => {
296
- if (item.top === row || Math.abs(item.top - row) <= offset) {
297
- entityRow.push(item);
298
- }
299
- });
300
- entityRow.sort((a, b) => a.x0 - b.x0);
301
-
302
- const finalRow = {};
303
- let previousEntity = null;
304
-
305
- // group entities that are near each other on X axis
306
- entityRow.forEach((entity) => {
307
- let xGroup = entity.x0;
308
- if (previousEntity && previousEntity.x1 + offset > entity.x0) {
309
- // compare to previous one
310
- finalRow[previousEntity.xGroup].push(entity);
311
- xGroup = previousEntity.xGroup;
312
- } else {
313
- finalRow[entity.x0] = [entity];
314
- }
315
- previousEntity = entity;
316
- previousEntity.xGroup = xGroup;
317
- });
318
-
319
- cols[row] = finalRow;
320
- });
321
-
322
- this.entities = cols;
323
- },
324
-
325
- processRows(columns) {
326
- const orderedEntities = []; // this will match the order of entities in the table so we have a way of tracking them once we submit
327
- let rowIndex = 0;
328
-
329
- Object.entries(this.entities).forEach(([key, groupedEntity]) => {
330
- let row = null;
331
- columns.forEach((column, index) => {
332
- let spans = [];
333
- if (
334
- Object.entries(groupedEntity)[index] &&
335
- Object.entries(groupedEntity)[index].length > 0
336
- ) {
337
- spans = Object.entries(groupedEntity)[index][1];
338
- }
339
- const entityExists = spans.length > 0;
340
-
341
- let textContent = "";
342
-
343
- spans.forEach((entity) => {
344
- textContent = `${textContent} ${entity.offset_string}`;
345
- });
346
-
347
- row = {
348
- ...row,
349
- [column.field]: textContent,
350
- };
351
- if (entityExists) {
352
- const customEntity = {
353
- spans: [...spans],
354
- label_id: column.field,
355
- row_index: rowIndex,
356
- };
357
-
358
- orderedEntities.push(customEntity);
359
- }
360
- });
361
- if (row !== null) {
362
- rowIndex++;
363
- }
364
- });
365
- return orderedEntities;
366
- },
367
-
368
- ...mapActions("selection", ["moveSelection"]),
369
- },
370
- };
371
- </script>
1
+ <template>
2
+ <v-group>
3
+ <v-rect
4
+ ref="multiAnnBoxSelection"
5
+ :config="tableConfig"
6
+ :stroke-scale-enabled="false"
7
+ @dragstart="changeStart"
8
+ @dragend="onChange"
9
+ @transformstart="changeStart"
10
+ @transformend="onChange"
11
+ />
12
+ <v-transformer ref="multiAnnBoxTransformer" :config="transformerConfig" />
13
+ <v-label
14
+ v-if="showButton"
15
+ :config="buttonConfig"
16
+ @mouseenter="onButtonEnter"
17
+ @mouseleave="onButtonLeave"
18
+ >
19
+ <v-tag
20
+ :config="{
21
+ fill: '#7B61FF',
22
+ cornerRadius: 4,
23
+ name: 'tag',
24
+ }"
25
+ />
26
+ <v-text
27
+ :config="{
28
+ align: 'right',
29
+ width: buttonWidth,
30
+ ellipsis: true,
31
+ wrap: 'none',
32
+ padding: 8,
33
+ text: $t('new_multi_ann_title'),
34
+ fill: 'white',
35
+ fontSize: 14,
36
+ cursor: 'pointer',
37
+ name: 'multiAnnButton',
38
+ }"
39
+ @click="openMultiAnnotationModal"
40
+ />
41
+ </v-label>
42
+ </v-group>
43
+ </template>
44
+
45
+ <script>
46
+ import { mapGetters, mapState, mapActions } from "vuex";
47
+ import { table_reference_api } from "../../store/document";
48
+
49
+ export default {
50
+ props: {
51
+ page: {
52
+ required: true,
53
+ type: Object,
54
+ },
55
+ },
56
+ data() {
57
+ const BUTTON_HEIGHT = 36;
58
+ const BUTTON_WIDTH = this.$i18n.locale === "de" ? 238 : 187;
59
+ return {
60
+ buttonWidth: BUTTON_WIDTH,
61
+ buttonHeight: BUTTON_HEIGHT,
62
+ isEditing: false,
63
+ showTable: false,
64
+ entities: [],
65
+ };
66
+ },
67
+ computed: {
68
+ showButton() {
69
+ return !this.isSelecting && !this.isEditing;
70
+ },
71
+ transformerConfig() {
72
+ return {
73
+ borderEnabled: false,
74
+ rotateEnabled: false,
75
+ ignoreStroke: true,
76
+ keepRatio: false,
77
+ anchorStroke: "#7B61FF",
78
+ anchorSize: 6,
79
+ };
80
+ },
81
+ tableConfig() {
82
+ return {
83
+ x: this.selection.start.x,
84
+ y: this.selection.start.y,
85
+ width: this.selection.end.x - this.selection.start.x,
86
+ height: this.selection.end.y - this.selection.start.y,
87
+ fill: this.isSelecting ? "#7B61FFB3" : "#7B61FF33",
88
+ stroke: this.isSelecting ? "transparent" : "#7B61FFB3",
89
+ strokeWidth: 1,
90
+ globalCompositeOperation: "multiply",
91
+ shadowForStrokeEnabled: false,
92
+ name: "multiAnnBoxSelection",
93
+ draggable: true,
94
+ };
95
+ },
96
+ buttonConfig() {
97
+ let x = this.selection.end.x;
98
+ let y = this.selection.end.y;
99
+ const marginTop = 4;
100
+
101
+ // check if selection was made from right to left
102
+ if (x < this.selection.start.x) {
103
+ x = this.selection.start.x;
104
+ }
105
+ if (y < this.selection.start.y) {
106
+ y = this.selection.start.y;
107
+ }
108
+ x = x - this.buttonWidth;
109
+ y = y + marginTop;
110
+
111
+ return {
112
+ x: x > 0 ? x : 0,
113
+ y: y > 0 ? y : 0,
114
+ height: this.buttonHeight,
115
+ width: this.buttonWidth,
116
+ };
117
+ },
118
+ ...mapState("selection", ["selection", "isSelecting"]),
119
+ ...mapState("document", ["documentId"]),
120
+ ...mapGetters("display", ["clientToBbox"]),
121
+ ...mapGetters("document", ["entitiesOnSelection"]),
122
+ },
123
+ watch: {
124
+ isSelecting(newValue, oldValue) {
125
+ // if it ends selection
126
+ if (!newValue && oldValue) {
127
+ this.calculateEntities();
128
+ this.updateTransformer();
129
+ }
130
+ },
131
+ },
132
+ methods: {
133
+ openMultiAnnotationModal() {
134
+ this.$store.dispatch("display/showChooseLabelSetModal", {
135
+ show: true,
136
+ isMultipleAnnotations: true,
137
+ finish: this.submitAnnotations,
138
+ });
139
+ },
140
+
141
+ async submitAnnotations(labelSet) {
142
+ const columns = labelSet.labels.map((label) => {
143
+ return {
144
+ field: `${label.id}`,
145
+ label: label.name,
146
+ centered: true,
147
+ };
148
+ });
149
+
150
+ const orderedEntities = this.processRows(columns);
151
+
152
+ const annotations = [];
153
+
154
+ orderedEntities.forEach((orderedEntity) => {
155
+ annotations.push({
156
+ document: this.documentId,
157
+ span: orderedEntity.spans,
158
+ label: orderedEntity.label_id,
159
+ is_correct: true,
160
+ revised: false,
161
+ label_set: labelSet.id,
162
+ set_reference: orderedEntity.row_index,
163
+ origin: table_reference_api,
164
+ });
165
+ });
166
+
167
+ this.$store
168
+ .dispatch("document/createAnnotation", annotations)
169
+ .then(() => {
170
+ this.$store.dispatch("selection/disableSelection");
171
+ this.$emit("finished");
172
+ })
173
+ .catch((error) => {
174
+ this.$store.dispatch("document/createErrorMessage", {
175
+ error,
176
+ serverErrorMessage: this.$t("server_error"),
177
+ defaultErrorMessage: this.$t("error_creating_multi_ann"),
178
+ });
179
+ this.$store.dispatch("selection/disableSelection");
180
+ this.$emit("finished");
181
+ });
182
+ },
183
+
184
+ onButtonEnter() {
185
+ this.$emit("buttonEnter");
186
+ },
187
+
188
+ onButtonLeave() {
189
+ this.$emit("buttonLeave");
190
+ },
191
+ changeStart() {
192
+ this.isEditing = true;
193
+ },
194
+ /**
195
+ * This method is used for both transforms and drags since it just
196
+ * retrieves the rect's new attributes from the event and uses those
197
+ * to set the new selection state.
198
+ */
199
+ onChange(event) {
200
+ this.isEditing = false;
201
+
202
+ const { x, y, scaleX, scaleY, skewX, width, height } = event.target.attrs;
203
+ const realWidth = width * scaleX;
204
+ const realHeight = height * scaleY;
205
+ let start;
206
+ let end;
207
+
208
+ // we need to figure out if there's skewing going on, to fix start/end points
209
+ // (other cases appear to fix themselves automatically)
210
+ if (skewX >= 0) {
211
+ start = { x, y };
212
+ end = {
213
+ x: start.x + realWidth,
214
+ y: start.y + realHeight,
215
+ };
216
+ } else {
217
+ end = { x, y };
218
+ start = { x: end.x - realWidth, y: end.y - realHeight };
219
+ }
220
+
221
+ this.moveSelection({ start, end });
222
+
223
+ // reset node's everything after transform (we don't want to deal with that,
224
+ // just with regular x/y/width/height)
225
+ const node = this.$refs.multiAnnBoxSelection.getNode();
226
+ node.skewX(0);
227
+ node.skewY(0);
228
+ node.rotation(0);
229
+ node.scaleX(1);
230
+ node.scaleY(1);
231
+
232
+ this.calculateEntities();
233
+ this.updateTransformer();
234
+ },
235
+
236
+ updateTransformer() {
237
+ // here we need to manually attach or detach Transformer node
238
+ const transformer = this.$refs.multiAnnBoxTransformer;
239
+
240
+ // maybe we're out of sync and the transformer is not available, just return
241
+ if (!transformer) {
242
+ return;
243
+ }
244
+
245
+ const transformerNode = transformer.getNode();
246
+ const stage = transformerNode.getStage();
247
+ const selectedNode = stage.findOne(".multiAnnBoxSelection");
248
+
249
+ // do nothing if selected node is already attached
250
+ if (selectedNode === transformerNode.node()) {
251
+ return;
252
+ }
253
+
254
+ if (selectedNode) {
255
+ // attach to another node
256
+ transformerNode.nodes([selectedNode]);
257
+ } else {
258
+ // remove transformer
259
+ transformerNode.nodes([]);
260
+ }
261
+
262
+ transformerNode.getLayer().batchDraw();
263
+ },
264
+
265
+ calculateEntities() {
266
+ const box = this.clientToBbox(
267
+ this.page,
268
+ this.selection.start,
269
+ this.selection.end
270
+ );
271
+ const entities = this.entitiesOnSelection(box, this.page);
272
+
273
+ const offset = 4;
274
+ if (entities.length === 0) {
275
+ return;
276
+ }
277
+
278
+ const rows = [...new Set(entities.map((o) => o["top"]))];
279
+ rows.sort();
280
+
281
+ let joinRow = 0;
282
+ const jointRows = [];
283
+
284
+ // group entities that are near each other on Y axis
285
+ rows.forEach((row) => {
286
+ if (row - joinRow > offset) {
287
+ joinRow = row;
288
+ jointRows.push(row);
289
+ }
290
+ });
291
+
292
+ let cols = {};
293
+ jointRows.forEach((row) => {
294
+ const entityRow = [];
295
+ entities.forEach((item) => {
296
+ if (item.top === row || Math.abs(item.top - row) <= offset) {
297
+ entityRow.push(item);
298
+ }
299
+ });
300
+ entityRow.sort((a, b) => a.x0 - b.x0);
301
+
302
+ const finalRow = {};
303
+ let previousEntity = null;
304
+
305
+ // group entities that are near each other on X axis
306
+ entityRow.forEach((entity) => {
307
+ let xGroup = entity.x0;
308
+ if (previousEntity && previousEntity.x1 + offset > entity.x0) {
309
+ // compare to previous one
310
+ finalRow[previousEntity.xGroup].push(entity);
311
+ xGroup = previousEntity.xGroup;
312
+ } else {
313
+ finalRow[entity.x0] = [entity];
314
+ }
315
+ previousEntity = entity;
316
+ previousEntity.xGroup = xGroup;
317
+ });
318
+
319
+ cols[row] = finalRow;
320
+ });
321
+
322
+ this.entities = cols;
323
+ },
324
+
325
+ processRows(columns) {
326
+ const orderedEntities = []; // this will match the order of entities in the table so we have a way of tracking them once we submit
327
+ let rowIndex = 0;
328
+
329
+ Object.entries(this.entities).forEach(([key, groupedEntity]) => {
330
+ let row = null;
331
+ columns.forEach((column, index) => {
332
+ let spans = [];
333
+ if (
334
+ Object.entries(groupedEntity)[index] &&
335
+ Object.entries(groupedEntity)[index].length > 0
336
+ ) {
337
+ spans = Object.entries(groupedEntity)[index][1];
338
+ }
339
+ const entityExists = spans.length > 0;
340
+
341
+ let textContent = "";
342
+
343
+ spans.forEach((entity) => {
344
+ textContent = `${textContent} ${entity.offset_string}`;
345
+ });
346
+
347
+ row = {
348
+ ...row,
349
+ [column.field]: textContent,
350
+ };
351
+ if (entityExists) {
352
+ const customEntity = {
353
+ spans: [...spans],
354
+ label_id: column.field,
355
+ row_index: rowIndex,
356
+ };
357
+
358
+ orderedEntities.push(customEntity);
359
+ }
360
+ });
361
+ if (row !== null) {
362
+ rowIndex++;
363
+ }
364
+ });
365
+ return orderedEntities;
366
+ },
367
+
368
+ ...mapActions("selection", ["moveSelection"]),
369
+ },
370
+ };
371
+ </script>