@konfuzio/document-validation-ui 0.1.1

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 (131) hide show
  1. package/.eslintrc.js +11 -0
  2. package/.prettierrc.json +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +13 -0
  5. package/dist/css/app.0c8973f8.css +1 -0
  6. package/dist/css/chunk-vendors.053b6b6e.css +5 -0
  7. package/dist/favicon.ico +0 -0
  8. package/dist/index.html +1 -0
  9. package/dist/js/app.17fe48c4.js +2 -0
  10. package/dist/js/app.17fe48c4.js.map +1 -0
  11. package/dist/js/chunk-vendors.a48fca3f.js +47 -0
  12. package/dist/js/chunk-vendors.a48fca3f.js.map +1 -0
  13. package/jest.config.js +4 -0
  14. package/package.json +60 -0
  15. package/src/.DS_Store +0 -0
  16. package/src/api.js +49 -0
  17. package/src/assets/images/AcceptedCheckMark.vue +8 -0
  18. package/src/assets/images/AcceptedUser.vue +8 -0
  19. package/src/assets/images/ActionIcon.vue +60 -0
  20. package/src/assets/images/ArrowDownKey.vue +11 -0
  21. package/src/assets/images/ArrowUpKey.vue +11 -0
  22. package/src/assets/images/CategoryIconImg.vue +13 -0
  23. package/src/assets/images/CheckMark.vue +8 -0
  24. package/src/assets/images/EditDocIcon.vue +12 -0
  25. package/src/assets/images/EmptyStateImg.vue +129 -0
  26. package/src/assets/images/ErrorIcon.vue +28 -0
  27. package/src/assets/images/EyeIcon.vue +11 -0
  28. package/src/assets/images/FileNameNotSavedImage.vue +26 -0
  29. package/src/assets/images/FileNameSavedImage.vue +14 -0
  30. package/src/assets/images/FitZoomIcon.vue +16 -0
  31. package/src/assets/images/KeyboardIcon.vue +16 -0
  32. package/src/assets/images/MinusIcon.vue +13 -0
  33. package/src/assets/images/NotOptimizedIllustration.vue +651 -0
  34. package/src/assets/images/PlusIcon.vue +13 -0
  35. package/src/assets/images/QuestionMark.vue +12 -0
  36. package/src/assets/images/ServerImage.vue +63 -0
  37. package/src/assets/images/SplitLines.vue +18 -0
  38. package/src/assets/images/SplitZigZag.vue +16 -0
  39. package/src/assets/images/StatusImg.vue +14 -0
  40. package/src/assets/images/UserIcon.vue +8 -0
  41. package/src/assets/scss/annotation_details.scss +126 -0
  42. package/src/assets/scss/categorize_modal.scss +42 -0
  43. package/src/assets/scss/choose_label_set_modal.scss +62 -0
  44. package/src/assets/scss/document_action_bar.scss +37 -0
  45. package/src/assets/scss/document_annotations.scss +472 -0
  46. package/src/assets/scss/document_category.scss +80 -0
  47. package/src/assets/scss/document_dashboard.scss +47 -0
  48. package/src/assets/scss/document_dataset_status.scss +46 -0
  49. package/src/assets/scss/document_edit.scss +431 -0
  50. package/src/assets/scss/document_error.scss +81 -0
  51. package/src/assets/scss/document_handover.scss +200 -0
  52. package/src/assets/scss/document_name.scss +62 -0
  53. package/src/assets/scss/document_page.scss +8 -0
  54. package/src/assets/scss/document_thumbnails.scss +41 -0
  55. package/src/assets/scss/document_toolbar.scss +89 -0
  56. package/src/assets/scss/document_top_bar.scss +139 -0
  57. package/src/assets/scss/document_viewport_modal.scss +25 -0
  58. package/src/assets/scss/documents_list.scss +130 -0
  59. package/src/assets/scss/empty_state.scss +34 -0
  60. package/src/assets/scss/extracting_data.scss +35 -0
  61. package/src/assets/scss/imports.scss +1 -0
  62. package/src/assets/scss/main.scss +24 -0
  63. package/src/assets/scss/multi_ann_table_popup.scss +12 -0
  64. package/src/assets/scss/new_annotation.scss +86 -0
  65. package/src/assets/scss/scrolling_document.scss +19 -0
  66. package/src/assets/scss/variables.scss +696 -0
  67. package/src/components/App.vue +112 -0
  68. package/src/components/DocumentAnnotations/ActionButtons.vue +237 -0
  69. package/src/components/DocumentAnnotations/AnnotationContent.vue +249 -0
  70. package/src/components/DocumentAnnotations/AnnotationDetails.vue +292 -0
  71. package/src/components/DocumentAnnotations/AnnotationRow.vue +616 -0
  72. package/src/components/DocumentAnnotations/CategorizeModal.vue +159 -0
  73. package/src/components/DocumentAnnotations/ChooseLabelSetModal.vue +155 -0
  74. package/src/components/DocumentAnnotations/DocumentAnnotations.vue +502 -0
  75. package/src/components/DocumentAnnotations/DocumentLabel.vue +148 -0
  76. package/src/components/DocumentAnnotations/EmptyAnnotation.vue +222 -0
  77. package/src/components/DocumentAnnotations/EmptyState.vue +21 -0
  78. package/src/components/DocumentAnnotations/ExtractingData.vue +29 -0
  79. package/src/components/DocumentAnnotations/LoadingAnnotations.vue +43 -0
  80. package/src/components/DocumentAnnotations/LoadingLabels.vue +43 -0
  81. package/src/components/DocumentAnnotations/RejectedLabels.vue +96 -0
  82. package/src/components/DocumentAnnotations/index.js +8 -0
  83. package/src/components/DocumentCategory.vue +156 -0
  84. package/src/components/DocumentDashboard.vue +159 -0
  85. package/src/components/DocumentEdit/DocumentEdit.vue +279 -0
  86. package/src/components/DocumentEdit/EditPages.vue +213 -0
  87. package/src/components/DocumentEdit/EditSidebar.vue +118 -0
  88. package/src/components/DocumentEdit/SplitOverview.vue +182 -0
  89. package/src/components/DocumentEdit/index.js +4 -0
  90. package/src/components/DocumentError.vue +53 -0
  91. package/src/components/DocumentPage/ActionBar.vue +48 -0
  92. package/src/components/DocumentPage/BoxSelection.vue +149 -0
  93. package/src/components/DocumentPage/DocumentPage.vue +517 -0
  94. package/src/components/DocumentPage/DocumentToolbar.vue +145 -0
  95. package/src/components/DocumentPage/DummyPage.vue +53 -0
  96. package/src/components/DocumentPage/MultiAnnSelection.vue +302 -0
  97. package/src/components/DocumentPage/MultiAnnotationTablePopup.vue +253 -0
  98. package/src/components/DocumentPage/NewAnnotation.vue +283 -0
  99. package/src/components/DocumentPage/ScrollingDocument.vue +108 -0
  100. package/src/components/DocumentPage/ScrollingPage.vue +184 -0
  101. package/src/components/DocumentPage/index.js +5 -0
  102. package/src/components/DocumentThumbnails/DocumentThumbnails.vue +92 -0
  103. package/src/components/DocumentThumbnails/LoadingThumbnail.vue +25 -0
  104. package/src/components/DocumentThumbnails/index.js +1 -0
  105. package/src/components/DocumentTopBar/DocumentDatasetStatus.vue +103 -0
  106. package/src/components/DocumentTopBar/DocumentHandover.vue +202 -0
  107. package/src/components/DocumentTopBar/DocumentName.vue +224 -0
  108. package/src/components/DocumentTopBar/DocumentTopBar.vue +144 -0
  109. package/src/components/DocumentTopBar/DocumentTopBarButtons.vue +148 -0
  110. package/src/components/DocumentTopBar/KeyboardActionsDescription.vue +71 -0
  111. package/src/components/DocumentTopBar/index.js +5 -0
  112. package/src/components/DocumentsList/DocumentsList.vue +126 -0
  113. package/src/components/DocumentsList/index.js +1 -0
  114. package/src/components/ErrorMessage.vue +40 -0
  115. package/src/components/NotOptimizedViewportModal.vue +54 -0
  116. package/src/constants.js +4 -0
  117. package/src/directives/scroll.js +28 -0
  118. package/src/i18n.js +23 -0
  119. package/src/locales/de.json +114 -0
  120. package/src/locales/en.json +114 -0
  121. package/src/locales/es.json +113 -0
  122. package/src/main.js +87 -0
  123. package/src/store/category.js +193 -0
  124. package/src/store/display.js +238 -0
  125. package/src/store/document.js +1057 -0
  126. package/src/store/edit.js +210 -0
  127. package/src/store/index.js +22 -0
  128. package/src/store/project.js +95 -0
  129. package/src/store/selection.js +179 -0
  130. package/src/utils/utils.js +3 -0
  131. package/vue.config.js +13 -0
@@ -0,0 +1,222 @@
1
+ <template>
2
+ <div class="empty-annotation">
3
+ <span
4
+ v-if="!publicView"
5
+ :id="emptyAnnotationId()"
6
+ ref="emptyAnnotation"
7
+ :class="[
8
+ 'annotation-value',
9
+ showActionError &&
10
+ editAnnotation &&
11
+ editAnnotation.id === emptyAnnotationId() &&
12
+ 'error-editing',
13
+ isEmptyAnnotationEditable() ? '' : 'label-empty',
14
+ isAnnotationBeingEdited() && 'clicked',
15
+ ]"
16
+ :contenteditable="isEmptyAnnotationEditable()"
17
+ @keypress.enter="saveEmptyAnnotationChanges"
18
+ @click="handleEditEmptyAnnotation"
19
+ @focus="handleEditEmptyAnnotation"
20
+ >
21
+ <span v-if="span && span.offset_string && isEmptyAnnotationEditable()">
22
+ {{ span.offset_string }}
23
+ </span>
24
+ <span v-else>
25
+ {{ $t("no_data_found") }}
26
+ </span>
27
+ </span>
28
+ </div>
29
+ </template>
30
+ <script>
31
+ import { mapState, mapGetters } from "vuex";
32
+
33
+ /**
34
+ * This component is responsible for managing empty annotations (labels with no annotations).
35
+ */
36
+ export default {
37
+ name: "EmptyAnnotation",
38
+ props: {
39
+ label: {
40
+ required: true,
41
+ },
42
+ annotationSet: {
43
+ required: true,
44
+ },
45
+
46
+ span: {
47
+ required: false,
48
+ },
49
+ spanIndex: {
50
+ required: false,
51
+ },
52
+ saveChanges: {
53
+ type: Boolean,
54
+ required: false,
55
+ },
56
+ },
57
+ data() {
58
+ return {
59
+ isLoading: false,
60
+ };
61
+ },
62
+ computed: {
63
+ ...mapGetters("document", [
64
+ "isAnnotationInEditMode",
65
+ "getTextFromEntities",
66
+ ]),
67
+ ...mapGetters("selection", ["isValueArray"]),
68
+ ...mapState("selection", ["spanSelection", "elementSelected"]),
69
+ ...mapState("document", [
70
+ "editAnnotation",
71
+ "publicView",
72
+ "selectedEntities",
73
+ "showActionError",
74
+ ]),
75
+ },
76
+ watch: {
77
+ span(newValue) {
78
+ if (this.elementSelected === this.emptyAnnotationId() && newValue) {
79
+ if (this.isValueArray(newValue))
80
+ newValue.map((span) => {
81
+ if (span.offset_string) {
82
+ span.offset_string =
83
+ this.$refs.emptyAnnotation.textContent.trim();
84
+ span.offset_string_original =
85
+ this.$refs.emptyAnnotation.textContent.trim();
86
+
87
+ this.setText(span.offset_string);
88
+ }
89
+ });
90
+ }
91
+ },
92
+ editAnnotation(newAnnotation, oldAnnotation) {
93
+ // verify if new annotation in edit mode is not this one and if this
94
+ // one was selected before so we set the state to the previous one (like a cancel)
95
+ if (oldAnnotation && oldAnnotation.id === this.emptyAnnotationId()) {
96
+ this.cancelEmptyAnnotation(true);
97
+ }
98
+ },
99
+ selectedEntities(newValue) {
100
+ if (!newValue && this.isAnnotationBeingEdited(this.emptyAnnotationId())) {
101
+ this.setText(
102
+ this.$t("draw_box_document", { label_name: this.label.name })
103
+ );
104
+ return;
105
+ }
106
+
107
+ if (
108
+ newValue &&
109
+ this.editAnnotation &&
110
+ this.emptyAnnotationId() === this.editAnnotation.id
111
+ ) {
112
+ const text = this.getTextFromEntities();
113
+ this.setText(text);
114
+ }
115
+ },
116
+ spanSelection(newValue) {
117
+ if (!newValue) return;
118
+
119
+ const isSpanArray = Array.isArray(newValue);
120
+
121
+ // Check if the bbox is empty
122
+ if (
123
+ (isSpanArray && !newValue[0].offset_string) ||
124
+ (!isSpanArray && !newValue.offset_string)
125
+ ) {
126
+ this.$store.dispatch("document/resetEditAnnotation");
127
+ this.$store.dispatch("selection/disableSelection");
128
+ }
129
+ },
130
+ },
131
+ methods: {
132
+ emptyAnnotationId() {
133
+ if (!this.annotationSet || !this.label) return;
134
+
135
+ if (this.annotationSet.id) {
136
+ return `${this.annotationSet.id}_${this.label.id}`;
137
+ } else {
138
+ return `${this.annotationSet.label_set.id}_${this.label.id}`;
139
+ }
140
+ },
141
+ isAnnotationBeingEdited() {
142
+ return this.isAnnotationInEditMode(this.emptyAnnotationId());
143
+ },
144
+ handleEditEmptyAnnotation() {
145
+ if (this.publicView) return;
146
+
147
+ if (
148
+ !this.publicView &&
149
+ !this.isLoading &&
150
+ this.elementSelected !== this.emptyAnnotationId()
151
+ ) {
152
+ this.setText(
153
+ this.$t("draw_box_document", { label_name: this.label.name })
154
+ );
155
+ this.$store.dispatch(
156
+ "selection/selectElement",
157
+ this.emptyAnnotationId()
158
+ );
159
+ this.$store.dispatch("document/setEditAnnotation", {
160
+ id: this.emptyAnnotationId(),
161
+ index: this.spanIndex,
162
+ label: this.label.id,
163
+ labelSet: this.annotationSet.label_set.id,
164
+ annotationSet: this.annotationSet.id,
165
+ });
166
+ }
167
+ },
168
+ cancelEmptyAnnotation(wasOutsideClick = false) {
169
+ if (wasOutsideClick) {
170
+ this.setText(this.$t("no_data_found"));
171
+ } else {
172
+ this.$store.dispatch("selection/disableSelection");
173
+ }
174
+
175
+ this.isLoading = false;
176
+ this.$store.dispatch("document/setSelectedEntities", null);
177
+
178
+ if (this.$refs.emptyAnnotation) {
179
+ this.$refs.emptyAnnotation.blur();
180
+ }
181
+ },
182
+ isEmptyAnnotationEditable() {
183
+ if (this.selectedEntities && this.selectedEntities.length > 0) {
184
+ return (
185
+ this.elementSelected === this.emptyAnnotationId() && !this.isLoading
186
+ );
187
+ } else if (
188
+ this.spanSelection &&
189
+ this.spanSelection[this.spanIndex] === 0
190
+ ) {
191
+ return false;
192
+ } else {
193
+ return (
194
+ this.elementSelected === this.emptyAnnotationId() &&
195
+ this.spanSelection &&
196
+ this.spanSelection[this.spanIndex] &&
197
+ this.spanSelection[this.spanIndex].offset_string != null &&
198
+ !this.isLoading
199
+ );
200
+ }
201
+ },
202
+ saveEmptyAnnotationChanges(event) {
203
+ if (this.publicView) return;
204
+
205
+ if (event) {
206
+ event.preventDefault();
207
+ }
208
+
209
+ // API call handled in parent component - AnnotationRow
210
+ this.$emit("save-empty-annotation-changes");
211
+ },
212
+ setText(text) {
213
+ this.$refs.emptyAnnotation.innerHTML = text;
214
+ },
215
+ },
216
+ };
217
+ </script>
218
+ <style
219
+ scoped
220
+ lang="scss"
221
+ src="../../assets/scss/document_annotations.scss"
222
+ ></style>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <div class="empty-container">
3
+ <EmptyStateImg />
4
+ <div class="empty-text">
5
+ <p class="title">
6
+ {{ $t("no_label_sets_found") }}
7
+ </p>
8
+ <p class="description">
9
+ {{ $t("no_label_sets_found_description") }}
10
+ </p>
11
+ </div>
12
+ </div>
13
+ </template>
14
+ <script>
15
+ import EmptyStateImg from "../../assets/images/EmptyStateImg";
16
+ export default {
17
+ name: "EmptyState",
18
+ components: { EmptyStateImg }
19
+ };
20
+ </script>
21
+ <style scoped lang="scss" src="../../assets/scss/empty_state.scss"></style>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <div class="data-extraction-container">
3
+ <div class="loading-container">
4
+ <ActionButtons
5
+ :is-loading="true"
6
+ :save-btn="false"
7
+ :cancel-btn="false"
8
+ />
9
+ </div>
10
+ <div class="data-extraction-text">
11
+ <p class="title">
12
+ {{ $t("data_being_extracted") }}
13
+ </p>
14
+ <div class="description">
15
+ <p>{{ $t("analysing_document") }}</p>
16
+ <p>{{ $t("few_minutes") }}</p>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </template>
21
+ <script>
22
+ import ActionButtons from "./ActionButtons";
23
+
24
+ export default {
25
+ name: "ExtractingData",
26
+ components: { ActionButtons }
27
+ };
28
+ </script>
29
+ <style scoped lang="scss" src="../../assets/scss/extracting_data.scss"></style>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="annotations-skeleton">
3
+ <div class="label-set-name-skeleton">
4
+ <b-skeleton
5
+ width="20%"
6
+ height="8px"
7
+ />
8
+ </div>
9
+
10
+ <!-- Label rows -->
11
+ <div
12
+ v-for="n in numberOfLabelRows"
13
+ :key="n"
14
+ >
15
+ <LoadingLabels />
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ import LoadingLabels from "./LoadingLabels";
22
+
23
+ /**
24
+ * This component shows a skeleton instead of the label sets
25
+ * while some document data is still loading
26
+ */
27
+
28
+ export default {
29
+ name: "LoadingAnnotations",
30
+ components: { LoadingLabels },
31
+ data() {
32
+ return {
33
+ numberOfLabelRows: 7
34
+ };
35
+ }
36
+ };
37
+ </script>
38
+
39
+ <style
40
+ scoped
41
+ lang="scss"
42
+ src="../../assets/scss/document_annotations.scss"
43
+ ></style>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="label-properties-skeleton">
3
+ <div class="loading-left">
4
+ <div class="circle">
5
+ <b-skeleton
6
+ circle
7
+ width="16px"
8
+ height="16px"
9
+ />
10
+ </div>
11
+ <div class="label-skeleton">
12
+ <b-skeleton
13
+ width="70%"
14
+ height="8px"
15
+ />
16
+ </div>
17
+ </div>
18
+
19
+ <div class="loading-right">
20
+ <b-skeleton
21
+ width="80%"
22
+ height="8px"
23
+ />
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script>
29
+ /**
30
+ * This component shows a skeleton instead of the labels and annotations
31
+ * while some document data is still loading
32
+ */
33
+
34
+ export default {
35
+ name: "LoadingLabels"
36
+ };
37
+ </script>
38
+
39
+ <style
40
+ scoped
41
+ lang="scss"
42
+ src="../../assets/scss/document_annotations.scss"
43
+ ></style>
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <div class="rejected-label-container">
3
+ <p class="title">
4
+ {{ `${$t("rejected")} (${missingAnnotations.length})` }}
5
+ </p>
6
+ <section class="rejected-tag-container">
7
+ <b-taglist
8
+ v-for="missingAnnotation in missingAnnotations"
9
+ :key="missingAnnotation.id"
10
+ >
11
+ <b-tag
12
+ attached
13
+ closable
14
+ aria-close-label="Close tag"
15
+ :class="[
16
+ isLoading && closedTag === missingAnnotation.id && 'loading-active',
17
+ ]"
18
+ @close="removeRejectedLabel(missingAnnotation.id)"
19
+ >
20
+ <span
21
+ :class="[
22
+ 'label-name',
23
+ isLoading && closedTag === missingAnnotation.id && 'loading',
24
+ ]"
25
+ >
26
+ {{ getLabelName(missingAnnotation.label) }}
27
+ </span>
28
+ </b-tag>
29
+ <div class="tag-loading-container">
30
+ <ActionButtons
31
+ :is-loading="isLoading && closedTag === missingAnnotation.id"
32
+ />
33
+ </div>
34
+ </b-taglist>
35
+ </section>
36
+ </div>
37
+ </template>
38
+
39
+ <script>
40
+ import { mapState } from "vuex";
41
+ import ActionButtons from "./ActionButtons.vue";
42
+
43
+ export default {
44
+ name: "RejectedLabels",
45
+ components: { ActionButtons },
46
+ props: {
47
+ missingAnnotations: {
48
+ default: null,
49
+ type: Array,
50
+ },
51
+ },
52
+
53
+ data() {
54
+ return {
55
+ isLoading: false,
56
+ closedTag: null,
57
+ };
58
+ },
59
+ computed: {
60
+ ...mapState("document", ["labels"]),
61
+ },
62
+ methods: {
63
+ removeRejectedLabel(id) {
64
+ this.isLoading = true;
65
+ this.closedTag = id;
66
+
67
+ this.$store
68
+ .dispatch("document/deleteMissingAnnotation", id)
69
+ .catch((error) => {
70
+ this.$store.dispatch("document/createErrorMessage", {
71
+ error,
72
+ serverErrorMessage: this.$t("server_error"),
73
+ defaultErrorMessage: this.$t("edit_error"),
74
+ });
75
+ })
76
+ .finally(() => {
77
+ this.isLoading = false;
78
+ this.closedTag = null;
79
+ });
80
+ },
81
+ getLabelName(label) {
82
+ if (!this.labels) return;
83
+ const found = this.labels.find((l) => l.id === label);
84
+ if (found) {
85
+ return found.name;
86
+ }
87
+ },
88
+ },
89
+ };
90
+ </script>
91
+
92
+ <style
93
+ scoped
94
+ lang="scss"
95
+ src="../../assets/scss/document_annotations.scss"
96
+ ></style>
@@ -0,0 +1,8 @@
1
+ export { default as DocumentAnnotations } from "./DocumentAnnotations";
2
+ export { default as AnnotationContent } from "./AnnotationContent";
3
+ export { default as EmptyAnnotation } from "./EmptyAnnotation";
4
+ export { default as DocumentLabel } from "./DocumentLabel";
5
+ export { default as AnnotationDetails } from "./AnnotationDetails";
6
+ export { default as RejectedLabels } from "./RejectedLabels";
7
+ export { default as ChooseLabelSetModal } from "./ChooseLabelSetModal";
8
+ export { default as AnnotationRow } from "./AnnotationRow";
@@ -0,0 +1,156 @@
1
+ <template>
2
+ <b-dropdown
3
+ :class="[
4
+ 'category-chooser',
5
+ splitMode && 'split-mode',
6
+ selectedDocument.is_reviewed && 'disabled',
7
+ ]"
8
+ aria-role="list"
9
+ :disabled="selectedDocument.is_reviewed"
10
+ >
11
+ <template #trigger>
12
+ <div class="category-drop-down">
13
+ <div class="icon">
14
+ <CategoryIcon />
15
+ </div>
16
+ <div class="category-info">
17
+ <p v-if="!splitMode" class="category-title">
18
+ {{ $t("category") }}
19
+ </p>
20
+ <div class="category-name">
21
+ {{
22
+ !splitMode
23
+ ? categoryName(selectedDocument.category)
24
+ : categoryName(updatedDocument[index].category)
25
+ }}
26
+ </div>
27
+ </div>
28
+ <div :class="[!splitMode && 'caret-section']">
29
+ <b-icon
30
+ icon="angle-down"
31
+ size="is-small"
32
+ :class="['caret', splitMode && 'split-mode-caret']"
33
+ />
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <b-dropdown-item
39
+ v-for="category in currentProjectCategories"
40
+ :key="category.id"
41
+ aria-role="listitem"
42
+ :disabled="handleOptionInDropdownDisabled(category)"
43
+ @click="handleChangeCategory(category)"
44
+ >
45
+ <span>{{ category.name }}</span>
46
+ </b-dropdown-item>
47
+ </b-dropdown>
48
+ </template>
49
+
50
+ <script>
51
+ import { mapGetters, mapState } from "vuex";
52
+ import CategoryIcon from "../assets/images/CategoryIconImg";
53
+
54
+ export default {
55
+ name: "DocumentCategory",
56
+ components: {
57
+ CategoryIcon,
58
+ },
59
+ props: {
60
+ splitMode: {
61
+ type: Boolean,
62
+ },
63
+ page: {
64
+ type: Object,
65
+ },
66
+ index: {
67
+ type: Number,
68
+ },
69
+ },
70
+ data() {
71
+ return {
72
+ currentProjectCategories: [],
73
+ categoryError: false,
74
+ };
75
+ },
76
+ computed: {
77
+ ...mapGetters("category", {
78
+ categoryName: "categoryName",
79
+ }),
80
+ ...mapState("document", ["selectedDocument"]),
81
+ ...mapState("category", ["categories"]),
82
+ ...mapState("edit", ["updatedDocument"]),
83
+ },
84
+ watch: {
85
+ categories(newValue) {
86
+ newValue.map((category) => {
87
+ if (category.project === this.selectedDocument.project) {
88
+ const found = this.currentProjectCategories.find(
89
+ (cat) => cat.id === category.id
90
+ );
91
+ if (found) return;
92
+
93
+ this.currentProjectCategories.push(category);
94
+ }
95
+ });
96
+ },
97
+ },
98
+ mounted() {
99
+ if (this.categories) {
100
+ this.categories.map((category) => {
101
+ if (category.project === this.selectedDocument.project) {
102
+ const found = this.currentProjectCategories.find(
103
+ (cat) => cat.id === category.id
104
+ );
105
+ if (found) return;
106
+
107
+ this.currentProjectCategories.push(category);
108
+ }
109
+ });
110
+ }
111
+ },
112
+ methods: {
113
+ // The current category name will change
114
+ // depending on if we are on edit mode or not
115
+ handleOptionInDropdownDisabled(category) {
116
+ if (!this.splitMode)
117
+ return category.id === this.selectedDocument.category;
118
+
119
+ return category.id === this.updatedDocument[this.index].category;
120
+ },
121
+
122
+ handleChangeCategory(category) {
123
+ // handling the category change will be different based on
124
+ // the dropdown being on the topbar or the split overview
125
+ const updatedCategory = {
126
+ category: category.id,
127
+ };
128
+
129
+ if (!this.splitMode) {
130
+ this.$store.dispatch("document/startRecalculatingAnnotations");
131
+
132
+ this.$store
133
+ .dispatch("document/updateDocument", updatedCategory)
134
+ .catch((error) => {
135
+ this.$store.dispatch("document/createErrorMessage", {
136
+ error,
137
+ serverErrorMessage: this.$t("server_error"),
138
+ defaultErrorMessage: this.$t("edit_error"),
139
+ });
140
+ })
141
+ .finally(() => {
142
+ this.$store.dispatch("document/endRecalculatingAnnotations");
143
+ });
144
+
145
+ return;
146
+ }
147
+
148
+ // Send the category ID to the split overview
149
+ // to update the new document category
150
+ this.$emit("category-change", this.page, category.id);
151
+ },
152
+ },
153
+ };
154
+ </script>
155
+
156
+ <style scoped lang="scss" src="../assets/scss/document_category.scss"></style>