@konfuzio/document-validation-ui 0.1.59-dev.3 → 0.1.60-dev.0
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 +23 -66
- package/dist/js/chunk-vendors.js.map +1 -1
- package/jest.config.js +2 -22
- package/package.json +38 -32
- package/src/api.js +12 -0
- 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 +43 -45
- 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 +12 -11
- 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/imports.scss +1 -0
- 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 +19 -25
- package/src/assets/scss/scrolling_document.scss +1 -1
- package/src/assets/scss/theme.scss +52 -64
- package/src/assets/scss/variables.scss +0 -2
- package/src/components/App.vue +14 -9
- package/src/components/DocumentAnnotations/AnnotationActionButtons.vue +6 -4
- package/src/components/DocumentAnnotations/AnnotationContent.vue +52 -25
- package/src/components/DocumentAnnotations/AnnotationRow.vue +50 -106
- package/src/components/DocumentAnnotations/DocumentAnnotations.vue +6 -12
- package/src/components/DocumentAnnotations/EmptyAnnotation.vue +70 -31
- package/src/components/DocumentDashboard.vue +17 -12
- package/src/components/DocumentEdit/EditPages.vue +46 -51
- package/src/components/DocumentPage/BoxSelection.vue +49 -16
- package/src/components/DocumentPage/DocumentPage.vue +153 -56
- package/src/components/DocumentPage/DocumentToolbar.vue +15 -5
- package/src/components/DocumentPage/EditAnnotation.vue +372 -0
- package/src/components/DocumentPage/{AnnotationPopup.vue → NewAnnotation.vue} +94 -122
- package/src/components/DocumentPage/ScrollingPage.vue +10 -2
- package/src/components/DocumentThumbnails/LoadingThumbnail.vue +6 -3
- package/src/components/DocumentTopBar/DocumentTopBar.vue +2 -4
- package/src/constants.js +7 -1
- package/src/i18n.js +5 -2
- package/src/main.js +16 -14
- package/src/store/display.js +24 -38
- package/src/store/document.js +6 -1
- package/src/store/index.js +8 -5
- package/src/store/selection.js +76 -152
- package/src/components/DocumentPage/PlaceholderSelection.vue +0 -51
- package/src/components/DocumentPage/SpanSelection.vue +0 -259
package/src/store/selection.js
CHANGED
|
@@ -9,24 +9,30 @@ const state = {
|
|
|
9
9
|
pageNumber: null,
|
|
10
10
|
start: null,
|
|
11
11
|
end: null,
|
|
12
|
+
custom: false, // if the box was created by user in document or it comes from an annotation
|
|
13
|
+
placeholderBox: null, // show a not editable placeholder box
|
|
12
14
|
},
|
|
13
15
|
isSelecting: false,
|
|
14
|
-
spanSelection:
|
|
15
|
-
|
|
16
|
-
selectedEntities:
|
|
17
|
-
spanLoading: false,
|
|
16
|
+
spanSelection: null,
|
|
17
|
+
elementSelected: null, // selected element id
|
|
18
|
+
selectedEntities: null,
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const getters = {
|
|
22
|
+
isElementSelected: (state) => {
|
|
23
|
+
return state.elementSelected;
|
|
24
|
+
},
|
|
25
|
+
isSelecting: (state) => {
|
|
26
|
+
return state.isSelecting;
|
|
27
|
+
},
|
|
21
28
|
isSelectionValid: (state) => {
|
|
22
29
|
/**
|
|
23
30
|
* `endSelection` will reset everything in case of invalid selection.
|
|
24
31
|
* Check the existence of `selection.end` before requesting the
|
|
25
32
|
* content from the backend.
|
|
26
33
|
* */
|
|
27
|
-
return state.selection && state.selection.end
|
|
34
|
+
return state.selection && state.selection.end;
|
|
28
35
|
},
|
|
29
|
-
|
|
30
36
|
getSelectionForPage: (state) => (pageNumber) => {
|
|
31
37
|
if (state.selection.pageNumber === pageNumber) {
|
|
32
38
|
return state.selection;
|
|
@@ -46,24 +52,19 @@ const getters = {
|
|
|
46
52
|
box.y1 >= entity.y1
|
|
47
53
|
);
|
|
48
54
|
},
|
|
49
|
-
spanSelectionsForPage: (state) => (page) => {
|
|
50
|
-
return state.spanSelection.filter(
|
|
51
|
-
(span) => page.number === span.page_index + 1
|
|
52
|
-
);
|
|
53
|
-
},
|
|
54
|
-
placeholderSelectionForPage: (state) => (page) => {
|
|
55
|
-
return state.placeholderSelection.filter(
|
|
56
|
-
(span) => page.number === span.page_index + 1
|
|
57
|
-
);
|
|
58
|
-
},
|
|
59
55
|
};
|
|
60
56
|
|
|
61
57
|
const actions = {
|
|
58
|
+
selectElement: ({ commit }, value) => {
|
|
59
|
+
commit("RESET_SELECTION");
|
|
60
|
+
commit("SET_SPAN_SELECTION", null);
|
|
61
|
+
commit("ELEMENT_SELECTED", value);
|
|
62
|
+
},
|
|
63
|
+
|
|
62
64
|
disableSelection: ({ commit }) => {
|
|
65
|
+
commit("ELEMENT_SELECTED", null);
|
|
63
66
|
commit("RESET_SELECTION");
|
|
64
|
-
commit("
|
|
65
|
-
commit("SET_SPAN_SELECTION", []);
|
|
66
|
-
commit("SET_PLACEHOLDER_SELECTION", []);
|
|
67
|
+
commit("SET_SPAN_SELECTION", null);
|
|
67
68
|
},
|
|
68
69
|
|
|
69
70
|
startSelection: ({ commit }, { pageNumber, start }) => {
|
|
@@ -80,6 +81,8 @@ const actions = {
|
|
|
80
81
|
if (xDiff > 5 && yDiff > 5) {
|
|
81
82
|
commit("MOVE_SELECTION", points);
|
|
82
83
|
}
|
|
84
|
+
|
|
85
|
+
commit("SET_SELECTED_ENTITIES", null);
|
|
83
86
|
},
|
|
84
87
|
|
|
85
88
|
endSelection: ({ commit, state }, end) => {
|
|
@@ -110,138 +113,78 @@ const actions = {
|
|
|
110
113
|
}
|
|
111
114
|
},
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
commit("
|
|
116
|
+
setSelection: ({ commit }, { span, selection }) => {
|
|
117
|
+
commit("SET_SELECTION", selection);
|
|
118
|
+
commit("SET_SPAN_SELECTION", span);
|
|
115
119
|
},
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
commit("
|
|
119
|
-
return new Promise((resolve, reject) => {
|
|
120
|
-
HTTP.post(`documents/${rootState.document.documentId}/bbox/`, {
|
|
121
|
-
span,
|
|
122
|
-
})
|
|
123
|
-
.then((response) => {
|
|
124
|
-
if (response.data.span.length && response.data.span.length > 0) {
|
|
125
|
-
/**
|
|
126
|
-
* If we have a non-empty bboxes list, we assume there
|
|
127
|
-
* is text here on the backend, so we just set
|
|
128
|
-
* spanSelection to the response.
|
|
129
|
-
*/
|
|
130
|
-
resolve(response.data.span);
|
|
131
|
-
} else {
|
|
132
|
-
/**
|
|
133
|
-
* Otherwise, we assume the backend can't identify text
|
|
134
|
-
* on this area, so we set our bbox into spanSelection
|
|
135
|
-
* ready to be passed back to the backend when creating
|
|
136
|
-
* an annotation on this empty area, adding the offset_string
|
|
137
|
-
* attribute, ready to be filled.
|
|
138
|
-
*/
|
|
139
|
-
resolve(span);
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
.catch((error) => {
|
|
143
|
-
alert("Could not fetch the selected text from the backend");
|
|
144
|
-
reject(error);
|
|
145
|
-
})
|
|
146
|
-
.finally(() => {
|
|
147
|
-
commit("SET_SPAN_LOADING", false);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
121
|
+
setSelectedEntities: ({ commit }, entities) => {
|
|
122
|
+
commit("SET_SELECTED_ENTITIES", entities);
|
|
150
123
|
},
|
|
151
124
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (selection) {
|
|
155
|
-
dispatch("getTextFromBboxes", [selection]).then((spans) => {
|
|
156
|
-
commit("SET_SPAN_SELECTION", spans);
|
|
157
|
-
});
|
|
158
|
-
} else {
|
|
159
|
-
commit("RESET_SELECTION");
|
|
160
|
-
commit("SET_SPAN_SELECTION", []);
|
|
161
|
-
}
|
|
162
|
-
commit("SET_SELECTED_ENTITIES", []);
|
|
163
|
-
} else {
|
|
164
|
-
commit("SET_SELECTED_ENTITIES", entities);
|
|
125
|
+
getTextFromBboxes: ({ commit, rootState }, { box, entities }) => {
|
|
126
|
+
let span;
|
|
165
127
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
let span;
|
|
170
|
-
if (entities) {
|
|
171
|
-
span = entities.flatMap((s) => {
|
|
172
|
-
return s.original;
|
|
173
|
-
});
|
|
174
|
-
} else {
|
|
175
|
-
span = [entities];
|
|
176
|
-
}
|
|
177
|
-
commit("SET_SPAN_SELECTION", span);
|
|
178
|
-
dispatch("getTextFromBboxes", span).then((spans) => {
|
|
179
|
-
commit("SET_SPAN_SELECTION", spans);
|
|
128
|
+
if (entities) {
|
|
129
|
+
span = box.flatMap((s) => {
|
|
130
|
+
return s.original;
|
|
180
131
|
});
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
entityClick: ({ commit, dispatch, state }, entity) => {
|
|
185
|
-
// Check if we are creating a new Annotation
|
|
186
|
-
// or if we are removing a previously selected entity
|
|
187
|
-
// or editing empty one
|
|
188
|
-
const found = state.selectedEntities.find(
|
|
189
|
-
(entityToFind) =>
|
|
190
|
-
entity.scaled.width === entityToFind.scaled.width &&
|
|
191
|
-
entity.original.offset_string === entityToFind.original.offset_string
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
let entities = state.selectedEntities;
|
|
195
|
-
if (found) {
|
|
196
|
-
entities = [
|
|
197
|
-
...state.selectedEntities.filter(
|
|
198
|
-
(entityToFilter) =>
|
|
199
|
-
entityToFilter.scaled.width !== entity.scaled.width &&
|
|
200
|
-
entityToFilter.original.offset_string !==
|
|
201
|
-
entity.original.offset_string
|
|
202
|
-
),
|
|
203
|
-
];
|
|
204
132
|
} else {
|
|
205
|
-
|
|
133
|
+
span = [box];
|
|
206
134
|
}
|
|
207
135
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
136
|
+
return HTTP.post(`documents/${rootState.document.documentId}/bbox/`, {
|
|
137
|
+
span,
|
|
138
|
+
})
|
|
139
|
+
.then((response) => {
|
|
140
|
+
if (response.data.span.length && response.data.span.length > 0) {
|
|
141
|
+
/**
|
|
142
|
+
* If we have a non-empty bboxes list, we assume there
|
|
143
|
+
* is text here on the backend, so we just set
|
|
144
|
+
* spanSelection to the response.
|
|
145
|
+
*/
|
|
146
|
+
commit("SET_SPAN_SELECTION", response.data.span);
|
|
147
|
+
} else {
|
|
148
|
+
/**
|
|
149
|
+
* Otherwise, we assume the backend can't identify text
|
|
150
|
+
* on this area, so we set our bbox into spanSelection
|
|
151
|
+
* ready to be passed back to the backend when creating
|
|
152
|
+
* an annotation on this empty area, adding the offset_string
|
|
153
|
+
* attribute, ready to be filled.
|
|
154
|
+
*/
|
|
155
|
+
commit("SET_SPAN_SELECTION", span);
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
.catch((error) => {
|
|
159
|
+
alert("Could not fetch the selected text from the backend");
|
|
216
160
|
});
|
|
161
|
+
},
|
|
217
162
|
|
|
218
|
-
|
|
163
|
+
getTextFromEntities: ({ commit, dispatch }, selectedEntities) => {
|
|
164
|
+
if (!selectedEntities) return;
|
|
219
165
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
} else {
|
|
225
|
-
span = [entities];
|
|
226
|
-
}
|
|
227
|
-
commit("SET_SPAN_SELECTION", span);
|
|
228
|
-
}
|
|
166
|
+
return dispatch("getTextFromBboxes", {
|
|
167
|
+
box: selectedEntities,
|
|
168
|
+
entities: true,
|
|
169
|
+
});
|
|
229
170
|
},
|
|
230
171
|
|
|
231
172
|
setSpanSelection: ({ commit }, span) => {
|
|
232
173
|
commit("SET_SPAN_SELECTION", span);
|
|
233
174
|
},
|
|
234
|
-
setPlaceholderSelection: ({ commit }, span) => {
|
|
235
|
-
commit("SET_PLACEHOLDER_SELECTION", span);
|
|
236
|
-
},
|
|
237
175
|
};
|
|
238
176
|
|
|
239
177
|
const mutations = {
|
|
178
|
+
ELEMENT_SELECTED: (state, value) => {
|
|
179
|
+
state.elementSelected = value;
|
|
180
|
+
},
|
|
240
181
|
START_SELECTION: (state, { pageNumber, start }) => {
|
|
241
182
|
state.selection.end = null;
|
|
183
|
+
state.isSelecting = true;
|
|
242
184
|
state.selection.pageNumber = pageNumber;
|
|
185
|
+
state.selection.custom = true;
|
|
243
186
|
state.selection.start = start;
|
|
244
|
-
state.
|
|
187
|
+
state.selection.placeholderBox = null;
|
|
245
188
|
},
|
|
246
189
|
MOVE_SELECTION: (state, points) => {
|
|
247
190
|
const { start, end } = points;
|
|
@@ -257,39 +200,20 @@ const mutations = {
|
|
|
257
200
|
state.isSelecting = false;
|
|
258
201
|
},
|
|
259
202
|
RESET_SELECTION: (state) => {
|
|
203
|
+
state.isSelecting = false;
|
|
260
204
|
state.selection.pageNumber = null;
|
|
261
205
|
state.selection.start = null;
|
|
262
206
|
state.selection.end = null;
|
|
207
|
+
state.selection.placeholderBox = null;
|
|
263
208
|
},
|
|
264
209
|
SET_SPAN_SELECTION: (state, span) => {
|
|
265
|
-
|
|
266
|
-
state.spanSelection = [];
|
|
267
|
-
} else {
|
|
268
|
-
state.spanSelection = span;
|
|
269
|
-
}
|
|
210
|
+
state.spanSelection = span;
|
|
270
211
|
},
|
|
271
|
-
|
|
272
|
-
state.
|
|
273
|
-
},
|
|
274
|
-
SET_PLACEHOLDER_SELECTION: (state, span) => {
|
|
275
|
-
if (!span) {
|
|
276
|
-
state.placeholderSelection = [];
|
|
277
|
-
} else {
|
|
278
|
-
state.placeholderSelection = span;
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
ADD_PLACEHOLDER_SELECTION: (state, span) => {
|
|
282
|
-
state.placeholderSelection.push(span);
|
|
212
|
+
SET_SELECTION: (state, selection) => {
|
|
213
|
+
state.selection = selection;
|
|
283
214
|
},
|
|
284
215
|
SET_SELECTED_ENTITIES: (state, entities) => {
|
|
285
|
-
|
|
286
|
-
state.selectedEntities = [];
|
|
287
|
-
} else {
|
|
288
|
-
state.selectedEntities = entities;
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
SET_SPAN_LOADING: (state, loading) => {
|
|
292
|
-
state.spanLoading = loading;
|
|
216
|
+
state.selectedEntities = entities;
|
|
293
217
|
},
|
|
294
218
|
};
|
|
295
219
|
|
|
@@ -1,51 +0,0 @@
|
|
|
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>
|
|
@@ -1,259 +0,0 @@
|
|
|
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>
|