@processmaker/screen-builder 2.89.0 → 2.91.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/README.md +5 -4
- package/dist/vue-form-builder.css +1 -1
- package/dist/vue-form-builder.es.js +7887 -7457
- package/dist/vue-form-builder.es.js.map +1 -1
- package/dist/vue-form-builder.umd.js +74 -57
- package/dist/vue-form-builder.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/DataProvider.js +28 -8
- package/src/assets/icons/Bypass.svg +5 -0
- package/src/assets/icons/Unbypass.svg +5 -0
- package/src/components/accordions.js +1 -0
- package/src/components/computed-properties.vue +211 -110
- package/src/components/index.js +2 -0
- package/src/components/renderer/form-list-table.vue +6 -1
- package/src/components/renderer/form-requests.vue +8 -2
- package/src/components/renderer/form-tasks.vue +25 -22
- package/src/components/renderer/index.js +1 -0
- package/src/components/renderer/link-button.vue +30 -0
- package/src/components/sortable/Sortable.vue +95 -13
- package/src/components/sortable/sortable.scss +5 -0
- package/src/components/sortable/sortableList/SortableList.vue +103 -36
- package/src/components/sortable/sortableList/sortableList.scss +63 -22
- package/src/components/task.vue +256 -59
- package/src/components/vue-form-builder.vue +19 -10
- package/src/components/watchers-form.vue +4 -3
- package/src/components/watchers-list.vue +46 -100
- package/src/components/watchers-popup.vue +89 -16
- package/src/customLogs.js +26 -0
- package/src/form-builder-controls.js +42 -0
- package/src/main.js +26 -1
- package/src/mixins/ScreenBase.js +1 -0
- package/src/mixins/computedFields.js +25 -7
- package/src/mixins/extensions/ComputedFields.js +26 -5
- package/src/mixins/extensions/Watchers.js +4 -0
- package/src/mixins/watchers.js +5 -2
- package/src/stories/Sortable.stories.js +58 -11
|
@@ -8,14 +8,8 @@
|
|
|
8
8
|
@table-row-mouseover="handleRowMouseover"
|
|
9
9
|
@table-row-mouseleave="handleRowMouseleave"
|
|
10
10
|
>
|
|
11
|
-
<template
|
|
12
|
-
|
|
13
|
-
v-slot:[column.field]
|
|
14
|
-
>
|
|
15
|
-
<div
|
|
16
|
-
:key="index"
|
|
17
|
-
style="display: inline-block"
|
|
18
|
-
>
|
|
11
|
+
<template v-for="(column, index) in tableHeaders" #[column.field]>
|
|
12
|
+
<div :key="index" style="display: inline-block">
|
|
19
13
|
<img
|
|
20
14
|
v-if="column.field === 'is_priority'"
|
|
21
15
|
src="../../assets/priority-header.svg"
|
|
@@ -26,11 +20,9 @@
|
|
|
26
20
|
<span v-else>{{ $t(column.label) }}</span>
|
|
27
21
|
</div>
|
|
28
22
|
</template>
|
|
29
|
-
<template
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
>
|
|
33
|
-
<td v-for="(header, colIndex) in tableHeaders"
|
|
23
|
+
<template v-for="(row, rowIndex) in tableData.data" #[`row-${rowIndex}`]>
|
|
24
|
+
<td
|
|
25
|
+
v-for="(header, colIndex) in tableHeaders"
|
|
34
26
|
:key="`${rowIndex}-${colIndex}`"
|
|
35
27
|
>
|
|
36
28
|
<template v-if="containsHTML(getNestedPropertyValue(row, header))">
|
|
@@ -39,7 +31,9 @@
|
|
|
39
31
|
:class="{ 'pm-table-truncate': header.truncate }"
|
|
40
32
|
:style="{ maxWidth: header.width + 'px' }"
|
|
41
33
|
>
|
|
42
|
-
<span
|
|
34
|
+
<span
|
|
35
|
+
v-html="sanitize(getNestedPropertyValue(row, header))"
|
|
36
|
+
></span>
|
|
43
37
|
</div>
|
|
44
38
|
<b-tooltip
|
|
45
39
|
v-if="header.truncate"
|
|
@@ -109,7 +103,7 @@
|
|
|
109
103
|
v-show="isTooltipVisible"
|
|
110
104
|
:position="rowPosition"
|
|
111
105
|
>
|
|
112
|
-
<template
|
|
106
|
+
<template #task-tooltip-body>
|
|
113
107
|
<div @mouseover="clearHideTimer" @mouseleave="hideTooltip">
|
|
114
108
|
<slot
|
|
115
109
|
name="tooltip"
|
|
@@ -185,7 +179,7 @@ export default {
|
|
|
185
179
|
},
|
|
186
180
|
mounted() {
|
|
187
181
|
this.setupColumns();
|
|
188
|
-
this.pmql = `(user_id = ${ProcessMaker.user.id})`;
|
|
182
|
+
this.pmql = `(user_id = ${ProcessMaker.user.id}) AND (status = "In Progress")`;
|
|
189
183
|
this.fetch();
|
|
190
184
|
this.$root.$on("dropdownSelectionTask", this.fetchData);
|
|
191
185
|
this.$root.$on("searchTask", this.fetchSearch);
|
|
@@ -208,7 +202,7 @@ export default {
|
|
|
208
202
|
}
|
|
209
203
|
|
|
210
204
|
if (this.pmqlSearch) {
|
|
211
|
-
pmql = pmql
|
|
205
|
+
pmql = `${pmql} AND ${this.pmqlSearch}`;
|
|
212
206
|
}
|
|
213
207
|
|
|
214
208
|
if (this.filterDropdowns !== undefined) {
|
|
@@ -336,6 +330,7 @@ export default {
|
|
|
336
330
|
default: true,
|
|
337
331
|
width: 153,
|
|
338
332
|
fixed_width: 153,
|
|
333
|
+
truncate: true,
|
|
339
334
|
resizable: false
|
|
340
335
|
},
|
|
341
336
|
{
|
|
@@ -381,6 +376,12 @@ export default {
|
|
|
381
376
|
resizable: false
|
|
382
377
|
});
|
|
383
378
|
}
|
|
379
|
+
columns.push({
|
|
380
|
+
label: "",
|
|
381
|
+
field: "options",
|
|
382
|
+
sortable: false,
|
|
383
|
+
width: 10
|
|
384
|
+
});
|
|
384
385
|
return columns;
|
|
385
386
|
},
|
|
386
387
|
setupColumns() {
|
|
@@ -476,16 +477,19 @@ export default {
|
|
|
476
477
|
},
|
|
477
478
|
fetchData(selectedOption) {
|
|
478
479
|
this.filterDropdowns = "";
|
|
479
|
-
this.pmql = `(user_id = ${ProcessMaker.user.id})
|
|
480
|
+
this.pmql = `(user_id = ${ProcessMaker.user.id})`;
|
|
480
481
|
this.advancedFilter = "";
|
|
481
482
|
if (selectedOption === "Self-service") {
|
|
482
483
|
this.pmql = "";
|
|
483
|
-
this.advancedFilter = `&advanced_filter=[${encodeURIComponent(
|
|
484
|
+
this.advancedFilter = `&advanced_filter=[${encodeURIComponent(
|
|
485
|
+
'{"subject":{"type":"Status","value":"status"},"operator":"=","value":"Self Service"}'
|
|
486
|
+
)}]`;
|
|
484
487
|
}
|
|
485
|
-
if (selectedOption === "In Progress") {
|
|
486
|
-
this.pmql
|
|
488
|
+
if (selectedOption === "In Progress" || selectedOption === "View All") {
|
|
489
|
+
this.pmql += ` AND (status = "In Progress")`;
|
|
487
490
|
}
|
|
488
491
|
if (selectedOption === "Overdue") {
|
|
492
|
+
this.pmql += ` AND (status = "In Progress")`;
|
|
489
493
|
this.filterDropdowns = "overdue=true";
|
|
490
494
|
}
|
|
491
495
|
this.fetch();
|
|
@@ -535,7 +539,6 @@ export default {
|
|
|
535
539
|
});
|
|
536
540
|
},
|
|
537
541
|
handleRowMouseover(row) {
|
|
538
|
-
debugger;
|
|
539
542
|
if (this.ellipsisShow) {
|
|
540
543
|
this.isTooltipVisible = !this.disableRuleTooltip;
|
|
541
544
|
this.clearHideTimer();
|
|
@@ -18,3 +18,4 @@ export { default as FormListTable } from "./form-list-table.vue";
|
|
|
18
18
|
export { default as FormAnalyticsChart } from "./form-analytics-chart.vue";
|
|
19
19
|
export { default as FormRequests } from "./form-requests.vue";
|
|
20
20
|
export { default as FormTasks } from "./form-tasks.vue";
|
|
21
|
+
export { default as LinkButton } from "./link-button.vue";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="form-group">
|
|
3
|
+
<a :class="classColor" :href="inputUrlLink"> {{ label }} </a>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
export default {
|
|
9
|
+
props: [
|
|
10
|
+
"variant",
|
|
11
|
+
"label",
|
|
12
|
+
"event",
|
|
13
|
+
"eventData",
|
|
14
|
+
"name",
|
|
15
|
+
"fieldValue",
|
|
16
|
+
"value",
|
|
17
|
+
"inputUrlLink"
|
|
18
|
+
],
|
|
19
|
+
computed: {
|
|
20
|
+
classColor() {
|
|
21
|
+
return `text-${this.variant}`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
</script>
|
|
26
|
+
<style scoped>
|
|
27
|
+
a {
|
|
28
|
+
text-decoration: underline;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<button
|
|
16
16
|
type="button"
|
|
17
17
|
class="btn sortable-btn-new"
|
|
18
|
+
v-bind="testActions.btnNew"
|
|
18
19
|
@click="$emit('add-page', $event)"
|
|
19
20
|
>
|
|
20
21
|
<i class="fa fa-plus"></i>
|
|
@@ -23,26 +24,47 @@
|
|
|
23
24
|
</div>
|
|
24
25
|
|
|
25
26
|
<SortableList
|
|
27
|
+
:fields="fields"
|
|
26
28
|
:items="items"
|
|
27
29
|
:filtered-items="filteredItems"
|
|
30
|
+
:inline-edit="inlineEdit"
|
|
31
|
+
:disable-key="disableKey"
|
|
32
|
+
:data-test-actions="testActions"
|
|
28
33
|
@ordered="$emit('ordered', $event)"
|
|
29
34
|
@item-edit="$emit('item-edit', $event)"
|
|
30
35
|
@item-delete="$emit('item-delete', $event)"
|
|
31
|
-
|
|
36
|
+
>
|
|
37
|
+
<template #options="{ item }">
|
|
38
|
+
<slot name="options" :item="item"></slot>
|
|
39
|
+
</template>
|
|
40
|
+
</SortableList>
|
|
32
41
|
</div>
|
|
33
42
|
</template>
|
|
34
43
|
|
|
35
44
|
<script>
|
|
36
|
-
import SortableList from './sortableList/SortableList.vue'
|
|
45
|
+
import SortableList from './sortableList/SortableList.vue';
|
|
37
46
|
|
|
38
47
|
export default {
|
|
39
48
|
name: 'Sortable',
|
|
40
49
|
components: {
|
|
41
|
-
SortableList
|
|
50
|
+
SortableList,
|
|
42
51
|
},
|
|
43
52
|
props: {
|
|
53
|
+
fields: { type: Array, required: true },
|
|
44
54
|
items: { type: Array, required: true },
|
|
45
|
-
|
|
55
|
+
disableKey: { type: String, default: null },
|
|
56
|
+
inlineEdit: { type: Boolean, default: true },
|
|
57
|
+
dataTestActions: {
|
|
58
|
+
type: Object,
|
|
59
|
+
default: () => ({}),
|
|
60
|
+
},
|
|
61
|
+
searchProperties: {
|
|
62
|
+
type: Array,
|
|
63
|
+
required: false,
|
|
64
|
+
default() {
|
|
65
|
+
return []; // Return a new instance of the array
|
|
66
|
+
},
|
|
67
|
+
},
|
|
46
68
|
},
|
|
47
69
|
data() {
|
|
48
70
|
return {
|
|
@@ -54,34 +76,94 @@ export default {
|
|
|
54
76
|
this.$set(item, "order", index + 1);
|
|
55
77
|
}
|
|
56
78
|
return item;
|
|
57
|
-
})
|
|
79
|
+
}),
|
|
80
|
+
defaultTestActions: {
|
|
81
|
+
tableBox: { 'data-test': 'sortable-table-box' },
|
|
82
|
+
btnNew: { 'data-test': 'sortable-btn-new' },
|
|
83
|
+
btnEdit: { 'data-test': 'sortable-btn-edit' },
|
|
84
|
+
btnDelete: { 'data-test': 'sortable-btn-remove' },
|
|
85
|
+
},
|
|
58
86
|
};
|
|
59
87
|
},
|
|
88
|
+
computed: {
|
|
89
|
+
testActions() {
|
|
90
|
+
return { ...this.defaultTestActions, ...this.dataTestActions };
|
|
91
|
+
},
|
|
92
|
+
},
|
|
60
93
|
watch: {
|
|
61
94
|
search(value) {
|
|
62
|
-
this.filteredItems = this.filterItems(value, this.items);
|
|
95
|
+
this.filteredItems = this.filterItems(value, this.items, this.searchProperties);
|
|
63
96
|
},
|
|
64
97
|
items: {
|
|
65
98
|
handler(newItems) {
|
|
66
99
|
this.filteredItems = [...newItems];
|
|
67
100
|
|
|
68
101
|
if (this.search.length > 0) {
|
|
69
|
-
this.filteredItems = this.filterItems(this.search, newItems);
|
|
102
|
+
this.filteredItems = this.filterItems(this.search, newItems, this.searchProperties);
|
|
70
103
|
}
|
|
71
104
|
},
|
|
72
105
|
deep: true,
|
|
73
106
|
},
|
|
74
107
|
},
|
|
75
108
|
methods: {
|
|
76
|
-
|
|
77
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Filters items by searching within specified properties, including nested properties, considering substrings.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} searchValue - The value to search for within item properties.
|
|
113
|
+
* @param {Array} items - The collection of items to filter.
|
|
114
|
+
* @param {Array} searchProperties - The properties to search within each item.
|
|
115
|
+
* @returns {Array} - The filtered items.
|
|
116
|
+
*/
|
|
117
|
+
filterItems(searchValue, items, searchProperties) {
|
|
118
|
+
const cleanSearch = this.clearSearch(searchValue).toLowerCase();
|
|
119
|
+
|
|
120
|
+
return items.filter((item) => {
|
|
121
|
+
return this.propertyMatchesSearch(item, searchProperties, cleanSearch);
|
|
122
|
+
});
|
|
78
123
|
},
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Checks if any of the specified properties of an item match the cleaned search value as a substring.
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} item - The item object to check.
|
|
129
|
+
* @param {Array} properties - The properties to search within each item.
|
|
130
|
+
* @param {string} cleanSearch - The cleaned and lowercase search value.
|
|
131
|
+
* @returns {boolean} - True if any property matches the search value, otherwise false.
|
|
132
|
+
*/
|
|
133
|
+
propertyMatchesSearch(item, properties, cleanSearch) {
|
|
134
|
+
return properties.some((property) => {
|
|
135
|
+
const value = this.getPropertyValue(item, property);
|
|
136
|
+
if (value && typeof value === 'string') {
|
|
137
|
+
const normalizedValue = value.toLowerCase();
|
|
138
|
+
return normalizedValue.includes(cleanSearch);
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Retrieves the value of a nested property within an object.
|
|
146
|
+
*
|
|
147
|
+
* @param {Object} obj - The object from which to retrieve the property value.
|
|
148
|
+
* @param {string} path - The path to the nested property, e.g., "item.title".
|
|
149
|
+
* @returns {*} - The value of the nested property if found, otherwise undefined.
|
|
150
|
+
*/
|
|
151
|
+
getPropertyValue(obj, path) {
|
|
152
|
+
const parts = path.split('.');
|
|
153
|
+
return parts.reduce((acc, curr) => acc?.[curr], obj);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Cleans the search value by removing unwanted characters and trimming spaces.
|
|
158
|
+
*
|
|
159
|
+
* @param {string} searchValue - The value to be cleaned.
|
|
160
|
+
* @returns {string} - The cleaned search value.
|
|
161
|
+
*/
|
|
162
|
+
clearSearch(searchValue) {
|
|
163
|
+
return searchValue.trim();
|
|
82
164
|
},
|
|
83
165
|
},
|
|
84
|
-
}
|
|
166
|
+
};
|
|
85
167
|
</script>
|
|
86
168
|
|
|
87
169
|
<style lang="scss" scoped src="./sortable.scss"></style>
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
.sortable {
|
|
2
2
|
&-box {
|
|
3
3
|
font-family: "Open Sans", sans-serif !important;
|
|
4
|
+
overflow-x: auto;
|
|
4
5
|
}
|
|
5
6
|
|
|
6
7
|
&-search-box {
|
|
7
8
|
display: flex;
|
|
8
9
|
align-items: center;
|
|
9
10
|
border-color: #cdddee !important;
|
|
11
|
+
|
|
12
|
+
& > input.form-control:focus {
|
|
13
|
+
border: 0 none !important;
|
|
14
|
+
}
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
&-search-icon {
|
|
@@ -1,51 +1,90 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="row mt-3">
|
|
3
|
-
<div
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<div class="sortable-
|
|
3
|
+
<div
|
|
4
|
+
class="p-0 border rounded-lg sortable-list"
|
|
5
|
+
v-bind="dataTestActions.tableBox"
|
|
6
|
+
@dragover="dragOver"
|
|
7
|
+
>
|
|
8
|
+
<div class="sortable-list-tr">
|
|
9
|
+
<div class="sortable-list-td"></div>
|
|
9
10
|
<div
|
|
10
|
-
v-for="
|
|
11
|
-
:key="
|
|
12
|
-
|
|
13
|
-
:data-test="`item-${item.order}`"
|
|
14
|
-
:title="item.name"
|
|
15
|
-
draggable="true"
|
|
16
|
-
@dragstart="(event) => dragStart(event, item.order)"
|
|
17
|
-
@dragenter="(event) => dragEnter(event, item.order)"
|
|
18
|
-
@dragend="dragEnd"
|
|
19
|
-
class="sortable-item sortable-draggable"
|
|
11
|
+
v-for="field in fields"
|
|
12
|
+
:key="field.key"
|
|
13
|
+
class="sortable-list-td"
|
|
20
14
|
>
|
|
15
|
+
<span class="sortable-list-separator"> </span>
|
|
16
|
+
<span class="sortable-list-header">{{ field.label }}</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="sortable-list-td">
|
|
19
|
+
<span class="sortable-list-separator"> </span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div
|
|
23
|
+
v-for="(item, index) in sortedItems"
|
|
24
|
+
:key="`item-${index}`"
|
|
25
|
+
:data-order="item.order"
|
|
26
|
+
:data-test="`item-${item.order}`"
|
|
27
|
+
:title="item.name"
|
|
28
|
+
:class="[
|
|
29
|
+
'sortable-list-tr',
|
|
30
|
+
'sortable-item',
|
|
31
|
+
{ 'sortable-item-disabled': isDisabled(item) },
|
|
32
|
+
]"
|
|
33
|
+
draggable="true"
|
|
34
|
+
@dragstart="(event) => dragStart(event, item.order)"
|
|
35
|
+
@dragenter="(event) => dragEnter(event, item.order)"
|
|
36
|
+
@dragend="dragEnd"
|
|
37
|
+
>
|
|
38
|
+
<div class="sortable-list-td">
|
|
21
39
|
<div class="sortable-item-icon">
|
|
22
40
|
<i class="fas fa-bars"></i>
|
|
23
41
|
</div>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
</div>
|
|
43
|
+
<div
|
|
44
|
+
v-for="field in fields"
|
|
45
|
+
:key="field.key"
|
|
46
|
+
class="sortable-list-td sortable-item-prop"
|
|
47
|
+
>
|
|
48
|
+
<b-form-input
|
|
49
|
+
v-if="editRowIndex === index"
|
|
50
|
+
v-model="newName"
|
|
51
|
+
type="text"
|
|
52
|
+
autofocus
|
|
53
|
+
required
|
|
54
|
+
:state="validateState(newName, item)"
|
|
55
|
+
:error="validateError(newName, item)"
|
|
56
|
+
@blur.stop="onBlur(newName, item)"
|
|
57
|
+
@keydown.enter.stop="onBlur(newName, item)"
|
|
58
|
+
@keydown.esc.stop="onCancel(item)"
|
|
59
|
+
@focus="onFocus(item)"
|
|
60
|
+
/>
|
|
61
|
+
<span v-else>{{ getItemValue(item, field.key, field.cb) }}</span>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="sortable-list-td">
|
|
40
65
|
<div class="border rounded-lg sortable-item-action">
|
|
41
66
|
<button v-if="editRowIndex === index" class="btn">
|
|
42
67
|
<i class="fas fa-check"></i>
|
|
43
68
|
</button>
|
|
44
|
-
<button
|
|
69
|
+
<button
|
|
70
|
+
v-else
|
|
71
|
+
v-b-tooltip="{ customClass: 'sortable-item-action-btn-tooltip' }"
|
|
72
|
+
class="btn"
|
|
73
|
+
:title="$t('Edit')"
|
|
74
|
+
v-bind="dataTestActions.btnEdit"
|
|
75
|
+
@click.stop="onClick(item, index)"
|
|
76
|
+
>
|
|
45
77
|
<i class="fas fa-edit"></i>
|
|
46
78
|
</button>
|
|
47
79
|
<div class="sortable-item-vr"></div>
|
|
48
|
-
<
|
|
80
|
+
<slot name="options" :item="item"></slot>
|
|
81
|
+
<button
|
|
82
|
+
v-b-tooltip="{ customClass: 'sortable-item-action-btn-tooltip' }"
|
|
83
|
+
class="btn"
|
|
84
|
+
:title="$t('Delete')"
|
|
85
|
+
v-bind="dataTestActions.btnDelete"
|
|
86
|
+
@click="$emit('item-delete', item)"
|
|
87
|
+
>
|
|
49
88
|
<i class="fas fa-trash-alt"></i>
|
|
50
89
|
</button>
|
|
51
90
|
</div>
|
|
@@ -59,8 +98,12 @@
|
|
|
59
98
|
export default {
|
|
60
99
|
name: 'SortableList',
|
|
61
100
|
props: {
|
|
101
|
+
fields: { type: Array, required: true },
|
|
62
102
|
items: { type: Array, required: true },
|
|
63
103
|
filteredItems: { type: Array, required: true },
|
|
104
|
+
inlineEdit: { type: Boolean, default: true },
|
|
105
|
+
disableKey: { type: String, default: null },
|
|
106
|
+
dataTestActions: { type: Object, required: true },
|
|
64
107
|
},
|
|
65
108
|
data() {
|
|
66
109
|
return {
|
|
@@ -77,9 +120,22 @@ export default {
|
|
|
77
120
|
this.refreshSort &&
|
|
78
121
|
[...this.filteredItems].sort((a, b) => a.order - b.order);
|
|
79
122
|
return sortedItems;
|
|
80
|
-
}
|
|
123
|
+
},
|
|
81
124
|
},
|
|
82
125
|
methods: {
|
|
126
|
+
/** Get the value of a nested field in an object
|
|
127
|
+
* @param {Object} item - The object to get the value from
|
|
128
|
+
* @param {String} fieldKey - The key of the field to get the value from
|
|
129
|
+
* @param {Function} cb - Callback function to apply to the value
|
|
130
|
+
*
|
|
131
|
+
* @returns {String} The value of the field
|
|
132
|
+
*/
|
|
133
|
+
getItemValue(item, fieldKey, cb = null) {
|
|
134
|
+
return fieldKey.split('.').reduce((obj, key) => {
|
|
135
|
+
if (!obj[key]) return '';
|
|
136
|
+
return cb instanceof Function ? cb(obj[key]) : obj[key];
|
|
137
|
+
}, item);
|
|
138
|
+
},
|
|
83
139
|
validateState(name, item) {
|
|
84
140
|
const isEmpty = !name?.trim();
|
|
85
141
|
const isDuplicated = this.items
|
|
@@ -120,6 +176,11 @@ export default {
|
|
|
120
176
|
this.editRowIndex = null;
|
|
121
177
|
},
|
|
122
178
|
onClick(item, index) {
|
|
179
|
+
if (!this.inlineEdit) {
|
|
180
|
+
this.$emit("item-edit", item);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
123
184
|
this.editRowIndex = index;
|
|
124
185
|
this.$emit("item-edit", item);
|
|
125
186
|
},
|
|
@@ -129,6 +190,9 @@ export default {
|
|
|
129
190
|
this.draggedItem = order;
|
|
130
191
|
// add dragging class to the element
|
|
131
192
|
event.target.classList.add('dragging');
|
|
193
|
+
if (event?.dataTransfer) {
|
|
194
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
195
|
+
}
|
|
132
196
|
},
|
|
133
197
|
dragEnter(event, order) {
|
|
134
198
|
this.draggedOverItem = order;
|
|
@@ -185,8 +249,11 @@ export default {
|
|
|
185
249
|
dragOver(event) {
|
|
186
250
|
event.preventDefault();
|
|
187
251
|
},
|
|
252
|
+
isDisabled(item) {
|
|
253
|
+
return this.disableKey ? item[this.disableKey] : false;
|
|
254
|
+
},
|
|
188
255
|
},
|
|
189
|
-
}
|
|
256
|
+
};
|
|
190
257
|
</script>
|
|
191
258
|
|
|
192
259
|
<style lang="scss" scoped src="./sortableList.scss"></style>
|
|
@@ -2,44 +2,67 @@ $border-color: #cdddee;
|
|
|
2
2
|
|
|
3
3
|
.sortable {
|
|
4
4
|
&-list {
|
|
5
|
-
display:
|
|
5
|
+
display: table;
|
|
6
6
|
flex-direction: column;
|
|
7
|
+
width: 100%;
|
|
7
8
|
border: 1px solid $border-color !important;
|
|
8
9
|
|
|
9
|
-
&-
|
|
10
|
-
display:
|
|
11
|
-
|
|
10
|
+
&-tr {
|
|
11
|
+
display: table-row;
|
|
12
|
+
|
|
13
|
+
&:last-child > .sortable-list-td {
|
|
14
|
+
border-bottom: none;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&-td {
|
|
19
|
+
display: table-cell;
|
|
12
20
|
border-bottom: 1px solid $border-color;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
|
|
23
|
+
&:first-child {
|
|
24
|
+
width: 60px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&:nth-child(2) {
|
|
28
|
+
min-width: 30%;
|
|
29
|
+
width: auto;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:not(:first-child):not(:nth-child(2)):not(:last-child) {
|
|
33
|
+
width: auto;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&:last-child {
|
|
37
|
+
width: 1%;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
}
|
|
13
40
|
}
|
|
14
41
|
|
|
15
|
-
&-
|
|
16
|
-
padding
|
|
42
|
+
&-header {
|
|
43
|
+
padding: 16px 0 16px 16px;
|
|
17
44
|
font-size: 14px;
|
|
18
|
-
font-weight:
|
|
45
|
+
font-weight: 700;
|
|
19
46
|
color: #566877;
|
|
47
|
+
text-transform: uppercase;
|
|
48
|
+
display: inline-block;
|
|
49
|
+
white-space: normal;
|
|
50
|
+
}
|
|
51
|
+
&-separator {
|
|
52
|
+
border-left: 1px solid $border-color;
|
|
53
|
+
display: inline-block;
|
|
54
|
+
padding: 0.5rem 0rem;
|
|
55
|
+
width: 1px;
|
|
20
56
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
&-container {
|
|
24
|
-
display: flex;
|
|
25
|
-
flex-direction: column;
|
|
26
|
-
width: 100%;
|
|
27
|
-
height: 340px;
|
|
28
|
-
overflow-x: auto;
|
|
29
57
|
}
|
|
30
58
|
|
|
31
59
|
&-item {
|
|
32
|
-
display: flex;
|
|
33
|
-
align-items: center;
|
|
34
|
-
height: 56px;
|
|
35
|
-
border-bottom: 1px solid $border-color;
|
|
36
60
|
cursor: move;
|
|
37
61
|
|
|
38
62
|
&-icon {
|
|
39
63
|
display: flex;
|
|
40
64
|
justify-content: center;
|
|
41
65
|
align-items: center;
|
|
42
|
-
width: 64px;
|
|
43
66
|
height: 56px;
|
|
44
67
|
}
|
|
45
68
|
|
|
@@ -47,8 +70,7 @@ $border-color: #cdddee;
|
|
|
47
70
|
color: #6A7888;
|
|
48
71
|
}
|
|
49
72
|
|
|
50
|
-
&-
|
|
51
|
-
flex-grow: 1;
|
|
73
|
+
&-prop {
|
|
52
74
|
padding: 8px 16px;
|
|
53
75
|
font-size: 15px;
|
|
54
76
|
color: #556271;
|
|
@@ -58,6 +80,18 @@ $border-color: #cdddee;
|
|
|
58
80
|
display: flex;
|
|
59
81
|
margin: 0 16px;
|
|
60
82
|
border-color: $border-color !important;
|
|
83
|
+
|
|
84
|
+
&-btn-tooltip::v-deep {
|
|
85
|
+
& .tooltip-inner {
|
|
86
|
+
background-color: #EBEEF2 !important;
|
|
87
|
+
color: #444444 !important;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
& .arrow:before {
|
|
91
|
+
border-top-color: #EBEEF2 !important;
|
|
92
|
+
border-bottom-color: #EBEEF2 !important;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
61
95
|
}
|
|
62
96
|
|
|
63
97
|
&-vr {
|
|
@@ -65,9 +99,16 @@ $border-color: #cdddee;
|
|
|
65
99
|
margin: 9px 0;
|
|
66
100
|
border-right: 1px solid $border-color;
|
|
67
101
|
}
|
|
102
|
+
|
|
103
|
+
&-disabled > .sortable-item-prop {
|
|
104
|
+
color: rgba(85, 98, 113, 0.5);
|
|
105
|
+
}
|
|
68
106
|
}
|
|
69
107
|
}
|
|
70
108
|
|
|
71
109
|
.dragging {
|
|
72
110
|
box-shadow: 0 1px 5px 0 rgba(86, 104, 119, 0.4);
|
|
111
|
+
cursor: move;
|
|
112
|
+
cursor: -webkit-grab;
|
|
113
|
+
cursor: -moz-grab;
|
|
73
114
|
}
|