@itfin/components 1.2.103 → 1.2.104
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/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="itf-board">
|
|
3
3
|
|
|
4
4
|
<div ref="container" class="itf-board-titles">
|
|
5
|
-
<div v-for="(column, index) of
|
|
5
|
+
<div v-for="(column, index) of orderColumnsInBoard"
|
|
6
6
|
:data-column="index"
|
|
7
7
|
@mouseover="columnHighlight(index, 'enter')"
|
|
8
8
|
@mouseout="columnHighlight(index, 'leave')"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
accept-group="board-columns"
|
|
19
19
|
@enter="columnHighlight(index, 'enter')"
|
|
20
20
|
@leave="columnHighlight(index, 'leave')"
|
|
21
|
+
@click="$emit('update:columnSettings', column)"
|
|
21
22
|
v-dropzone="{ payload: { index, column } }"
|
|
22
23
|
class="d-flex align-items-center">
|
|
23
24
|
<div
|
|
@@ -25,8 +26,8 @@
|
|
|
25
26
|
@drop="reorderColumns"
|
|
26
27
|
v-draggable="{ handle: true, payload: { index, column }, mirror: { yAxis:false } }"
|
|
27
28
|
class="flex-grow-1 d-flex align-items-center itf-board-column__header justify-content-between">
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
<div><slot name="header" :column="column">{{ column[columnNameKey] }}</slot></div>
|
|
30
|
+
<div class="text-muted me-1"><slot name="header-addon" :column="column">{{ (groupedItems[column.Id] || []).length }}</slot></div>
|
|
30
31
|
</div>
|
|
31
32
|
</itf-edit-button>
|
|
32
33
|
<div v-if="index === columns.length - 1"
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
</div>
|
|
40
41
|
</div>
|
|
41
42
|
<div class="align-items-end d-flex pb-1">
|
|
42
|
-
<slot name="add-column">
|
|
43
|
+
<slot name="add-column" @click="$emit('addColumn')">
|
|
43
44
|
<itf-button icon small>
|
|
44
45
|
<itf-icon name="plus" />
|
|
45
46
|
</itf-button>
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
</div>
|
|
49
50
|
|
|
50
51
|
<div class="itf-board-columns flex-grow-1">
|
|
51
|
-
<div v-for="(column, index) of
|
|
52
|
+
<div v-for="(column, index) of orderColumnsInBoard"
|
|
52
53
|
:key="`column-${index}`"
|
|
53
54
|
:data-column="index"
|
|
54
55
|
class="itf-board-column px-2 pb-2 pt-2" :class="{'empty': !groupedItems[column.Id]}">
|
|
@@ -66,6 +67,7 @@ import loading from '../../directives/loading';
|
|
|
66
67
|
import itfForm from '../form/Form';
|
|
67
68
|
import itfEditButton from '../editable/EditButton.vue';
|
|
68
69
|
import createDraggable from '../sortable/draggable';
|
|
70
|
+
import { debounce } from '../../helpers/debounce';
|
|
69
71
|
|
|
70
72
|
const { Node, ...draggableDirectives } = createDraggable();
|
|
71
73
|
|
|
@@ -87,6 +89,7 @@ export default @Component({
|
|
|
87
89
|
class Board extends Vue {
|
|
88
90
|
@Prop(Array) columns;
|
|
89
91
|
@Prop(Array) items;
|
|
92
|
+
@Prop(Array) boardColumnsOrder
|
|
90
93
|
@Prop(Array) columnOrders;
|
|
91
94
|
@Prop({ type: Function, default: (item) => item.Status?.Id }) groupFunc;
|
|
92
95
|
@Prop({ type: Function, default: (item, value) => { item.Status = value; } }) updateFunc;
|
|
@@ -100,6 +103,7 @@ class Board extends Vue {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
mounted() {
|
|
106
|
+
this.itemStatusChanged = debounce(this.emitItemStatusChanged, 10);
|
|
103
107
|
if (this.columnSorting) {
|
|
104
108
|
Node.addContainer(this.$refs.container);
|
|
105
109
|
}
|
|
@@ -113,12 +117,21 @@ class Board extends Vue {
|
|
|
113
117
|
return grouped;
|
|
114
118
|
}
|
|
115
119
|
|
|
120
|
+
get orderColumnsInBoard() {
|
|
121
|
+
return this.sortByOrdering(this.columns, this.boardColumnsOrder || []);
|
|
122
|
+
}
|
|
123
|
+
|
|
116
124
|
orderItemsInColumn(column) {
|
|
117
|
-
const columnOrdering = this.columnOrders.find((c) => c.Id === column.Id) || { ItemIds: [] };
|
|
125
|
+
const columnOrdering = (this.columnOrders.find((c) => c.Id === column.Id) || { ItemIds: [] }).ItemIds;
|
|
118
126
|
let items = this.items.filter((item) => column.Id === this.groupFunc(item));
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
return this.sortByOrdering(items, columnOrdering);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
sortByOrdering (arr, ordering) {
|
|
131
|
+
const arrCopy = arr.slice();
|
|
132
|
+
return arrCopy.sort((a, b) => {
|
|
133
|
+
const aIndex = ordering.findIndex((id) => id === a.Id);
|
|
134
|
+
const bIndex = ordering.findIndex((id) => id === b.Id);
|
|
122
135
|
if (aIndex === -1 && bIndex === -1) {
|
|
123
136
|
return 0;
|
|
124
137
|
}
|
|
@@ -130,7 +143,6 @@ class Board extends Vue {
|
|
|
130
143
|
}
|
|
131
144
|
return aIndex - bIndex;
|
|
132
145
|
});
|
|
133
|
-
return items;
|
|
134
146
|
}
|
|
135
147
|
|
|
136
148
|
columnHighlight(index, state, className = 'over') {
|
|
@@ -153,7 +165,7 @@ class Board extends Vue {
|
|
|
153
165
|
reorderColumns({ detail }) {
|
|
154
166
|
const { index: fromIndex } = detail.draggablePayload;
|
|
155
167
|
const { index: toIndex, last } = detail.dropzonePayload;
|
|
156
|
-
const newValue = [...this.
|
|
168
|
+
const newValue = [...this.orderColumnsInBoard];
|
|
157
169
|
const [removed] = newValue.splice(fromIndex, 1);
|
|
158
170
|
|
|
159
171
|
if (last) {
|
|
@@ -186,36 +198,41 @@ class Board extends Vue {
|
|
|
186
198
|
|
|
187
199
|
items[itemIndex] = newItem;
|
|
188
200
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
newOrders.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
newOrders.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
placeIndex =
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
201
|
+
this.lastUpdatedIndex = typeof this.lastUpdatedIndex === 'undefined' ? toIndex : this.lastUpdatedIndex; // запамятовуємо індекс, бо виклики може бути два, коли колонка і карточка обробляє
|
|
202
|
+
this.itemStatusChanged(items[itemIndex], column, items, () => {
|
|
203
|
+
const toIndex = this.lastUpdatedIndex; // беремо індекс з пам'яті, бо викликів може бути два, але індекс тільки в картці, якщо нема, то це колонка
|
|
204
|
+
this.lastUpdatedIndex = undefined; // обнуляємо, щоб при наступному перетягувані вже не брало його
|
|
205
|
+
const newOrders = [...this.columnOrders];
|
|
206
|
+
let sorting = newOrders.find((c) => c.Id === column.Id);
|
|
207
|
+
if (!sorting) {
|
|
208
|
+
sorting = { Id: column.Id, ItemIds: [] };
|
|
209
|
+
newOrders.push(sorting)
|
|
210
|
+
}
|
|
211
|
+
let fromSorting = newOrders.find((c) => c.Id === fromColumn.Id);
|
|
212
|
+
if (!fromSorting) {
|
|
213
|
+
fromSorting = { Id: fromColumn.Id, ItemIds: [] };
|
|
214
|
+
newOrders.push(fromSorting)
|
|
215
|
+
}
|
|
216
|
+
// update from
|
|
217
|
+
fromSorting.ItemIds = this.groupedItems[fromColumn.Id].map((i) => i.Id).filter((t) => t !== newItem.Id);
|
|
218
|
+
// update to
|
|
219
|
+
const items = [...this.groupedItems[column.Id]];
|
|
220
|
+
let placeIndex = last ? items.length : toIndex;
|
|
221
|
+
if (typeof placeIndex === 'undefined') { // ідекса може не бути, якщо на колонку переносять, тоді в кінець
|
|
222
|
+
placeIndex = items.length;
|
|
223
|
+
}
|
|
224
|
+
const itemsInColumn = items.map((t) => t.Id).filter((t) => t !== newItem.Id);
|
|
225
|
+
itemsInColumn.splice(placeIndex, 0, newItem.Id);
|
|
226
|
+
sorting.ItemIds = itemsInColumn;
|
|
227
|
+
this.$emit('update:columnOrders', newOrders);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
212
230
|
|
|
213
|
-
|
|
231
|
+
emitItemStatusChanged(item, column, items, updateOrder) {
|
|
232
|
+
updateOrder();
|
|
214
233
|
|
|
215
234
|
this.$emit('update:item', [item, column]);
|
|
216
235
|
this.$emit('update:items', items);
|
|
217
|
-
|
|
218
|
-
this.$emit('update:columnOrders', newOrders);
|
|
219
236
|
}
|
|
220
237
|
}
|
|
221
238
|
</script>
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<div class="itf-board-card shadow-sm">
|
|
3
3
|
<slot name="header"></slot>
|
|
4
4
|
|
|
5
|
-
<div class="itf-board-card-inner d-flex">
|
|
6
|
-
<div class="flex-grow-1">
|
|
5
|
+
<div class="itf-board-card-inner d-flex" @click="$emit('click')">
|
|
6
|
+
<div class="flex-grow-1 itf-board-card-inner-content">
|
|
7
7
|
<slot>{{text}}</slot>
|
|
8
8
|
</div>
|
|
9
9
|
<div>
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
<
|
|
16
|
+
<div class="itf-board-card-footer d-flex">
|
|
17
|
+
<slot name="footer"></slot>
|
|
18
|
+
</div>
|
|
17
19
|
</div>
|
|
18
20
|
</template>
|
|
19
21
|
<script>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
--itf-board-column-hover-color:
|
|
2
|
+
--itf-board-column-hover-color: #f5f9fe;
|
|
3
3
|
--itf-board-column-width: 300px;
|
|
4
4
|
--itf-board-placeholder-color: #47BEFF;
|
|
5
5
|
--itf-board-placeholder-border-color: #0567eb;
|
|
6
6
|
--itf-board-placeholder-bg-color: #ebeffe;
|
|
7
7
|
--itf-board-card-bg-color: #fff;
|
|
8
|
+
--itf-board-card-border-color: #ccc;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
[data-theme="dark"] {
|
|
@@ -13,10 +14,7 @@
|
|
|
13
14
|
--itf-board-placeholder-border-color: #FFCC00;
|
|
14
15
|
--itf-board-placeholder-bg-color: #383b41;
|
|
15
16
|
--itf-board-card-bg-color: #313337;
|
|
16
|
-
|
|
17
|
-
.itf-board {
|
|
18
|
-
--bs-border-color: #424448;
|
|
19
|
-
}
|
|
17
|
+
--itf-board-card-border-color: #424448;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
.itf-board {
|
|
@@ -108,7 +106,6 @@
|
|
|
108
106
|
.itf-board-column {
|
|
109
107
|
position: relative;
|
|
110
108
|
width: var(--itf-board-column-width);
|
|
111
|
-
min-width: var(--itf-board-column-width);
|
|
112
109
|
border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
|
|
113
110
|
|
|
114
111
|
&__title {
|
|
@@ -152,7 +149,7 @@
|
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
.itf-board-titles {
|
|
155
|
-
border-bottom: 1px solid var(--
|
|
152
|
+
border-bottom: 1px solid var(--itf-board-card-border-color);
|
|
156
153
|
}
|
|
157
154
|
|
|
158
155
|
.itf-board-columns {
|
|
@@ -212,18 +209,27 @@
|
|
|
212
209
|
.itf-board-card {
|
|
213
210
|
background-color: var(--itf-board-card-bg-color);
|
|
214
211
|
border-radius: var(--bs-border-radius);
|
|
215
|
-
border: 1px solid var(--
|
|
212
|
+
border: 1px solid var(--itf-board-card-border-color);
|
|
216
213
|
margin-bottom: .75rem;
|
|
217
214
|
overflow: hidden;
|
|
218
215
|
user-select: none;
|
|
216
|
+
padding: .5rem;
|
|
219
217
|
|
|
220
218
|
.itf-board-card-inner {
|
|
221
|
-
padding: .5rem;
|
|
222
219
|
font-size: .875rem;
|
|
223
220
|
word-wrap: break-word;
|
|
224
221
|
-webkit-line-clamp: unset;
|
|
225
|
-
overflow
|
|
222
|
+
overflow: hidden;
|
|
226
223
|
word-break: break-word;
|
|
224
|
+
|
|
225
|
+
.itf-board-card-inner-content {
|
|
226
|
+
padding-right: 0.2rem;
|
|
227
|
+
overflow-x: hidden;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.itf-board-card-footer {
|
|
232
|
+
overflow-x: hidden;
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
|
|
@@ -232,6 +238,7 @@
|
|
|
232
238
|
flex-direction: column;
|
|
233
239
|
max-height: 100%;
|
|
234
240
|
flex-grow: 1;
|
|
241
|
+
max-width: 100%;
|
|
235
242
|
}
|
|
236
243
|
|
|
237
244
|
.itf-board-cards-wrapper {
|
|
@@ -243,7 +250,7 @@
|
|
|
243
250
|
.draggable-source--is-dragging {
|
|
244
251
|
position: relative !important;
|
|
245
252
|
.itf-board-card {
|
|
246
|
-
background-color: var(--
|
|
253
|
+
background-color: var(--itf-board-card-border-color);
|
|
247
254
|
|
|
248
255
|
& > div {
|
|
249
256
|
opacity: 0;
|