@itfin/components 1.3.91 → 1.3.94
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 +1 -1
- package/src/components/filter/FilterAmountRange.vue +1 -0
- package/src/components/filter/FilterPanel.vue +15 -6
- package/src/components/popover/NoticePopout.vue +48 -0
- package/src/components/table/Table2.vue +20 -3
- package/src/components/table/TableBody.vue +1 -0
- package/src/components/table/TableGroup.vue +2 -1
- package/src/components/table/TableHeader.vue +14 -4
- package/src/components/table/TableRows.vue +2 -2
- package/src/components/table/table2.scss +15 -0
- package/src/components/text-field/MoneyField.vue +1 -1
- package/src/helpers/validators.js +1 -1
package/package.json
CHANGED
|
@@ -81,11 +81,12 @@ class FilterPanel extends Vue {
|
|
|
81
81
|
const filter = {};
|
|
82
82
|
const filterValue = {};
|
|
83
83
|
for (const item of this.filters) {
|
|
84
|
-
filter[item.name] = payload[item.name] ? this.formatValue(item, { value: payload[item.name] }) : { isDefault: true, ...item.options.defaultValue };
|
|
85
84
|
if (item.type === 'period') {
|
|
85
|
+
filter[item.name] = payload.from ? this.formatValue(item, { value: [payload.from, payload.to] }) : { isDefault: true, ...item.options.defaultValue };
|
|
86
86
|
filterValue.from = payload.from;
|
|
87
87
|
filterValue.to = payload.to;
|
|
88
88
|
} else {
|
|
89
|
+
filter[item.name] = payload[item.name] ? this.formatValue(item, { value: payload[item.name] }) : { isDefault: true, ...item.options.defaultValue };
|
|
89
90
|
filterValue[item.name] = payload[item.name];
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -97,6 +98,14 @@ class FilterPanel extends Vue {
|
|
|
97
98
|
this.$emit('input', this.filterValue);
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
setFilter(field, value) {
|
|
102
|
+
const facet = this.filters.find(facet => facet.name === field);
|
|
103
|
+
if (!facet) {
|
|
104
|
+
throw new Error(`Facet ${field} not found`);
|
|
105
|
+
}
|
|
106
|
+
this.onFilterChange(facet, { value });
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
onFilterChange(facet, value) {
|
|
101
110
|
this.filter[facet.name] = this.formatValue(facet, value);
|
|
102
111
|
if (facet.type === 'period') {
|
|
@@ -128,11 +137,11 @@ class FilterPanel extends Vue {
|
|
|
128
137
|
formatValue(facet, value) {
|
|
129
138
|
if (facet.type === 'period') {
|
|
130
139
|
if (value.value) {
|
|
131
|
-
let from = DateTime.
|
|
132
|
-
let to = DateTime.
|
|
140
|
+
let from = DateTime.fromFormat(value.value[0], 'yyyy-MM-dd');
|
|
141
|
+
let to = DateTime.fromFormat(value.value[1], 'yyyy-MM-dd');
|
|
133
142
|
if (!from.isValid || !to.isValid) {
|
|
134
|
-
from = DateTime.
|
|
135
|
-
to = DateTime.
|
|
143
|
+
from = DateTime.fromFormat(facet.options.defaultValue.value[0], 'yyyy-MM-dd');
|
|
144
|
+
to = DateTime.fromFormat(facet.options.defaultValue.value[1], 'yyyy-MM-dd');
|
|
136
145
|
}
|
|
137
146
|
const namedItem = this.daysList.find(item => {
|
|
138
147
|
const [start, end] = item.date();
|
|
@@ -141,7 +150,7 @@ class FilterPanel extends Vue {
|
|
|
141
150
|
if (namedItem) {
|
|
142
151
|
value.label = namedItem.title;
|
|
143
152
|
} else {
|
|
144
|
-
value.label = formatRangeDates(from, to);
|
|
153
|
+
value.label = formatRangeDates(from.toJSDate(), to.toJSDate());
|
|
145
154
|
}
|
|
146
155
|
}
|
|
147
156
|
if (facet.options.defaultValue?.value) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="notice p-2" ref="notice">
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
<style scoped lang="scss">
|
|
7
|
+
.notice {
|
|
8
|
+
position: absolute;
|
|
9
|
+
bottom: 10px;
|
|
10
|
+
left: 50%;
|
|
11
|
+
max-width: 740px;
|
|
12
|
+
z-index: 10;
|
|
13
|
+
opacity: 0;
|
|
14
|
+
transition: all 0.3s ease;
|
|
15
|
+
transform: translateX(-50%) translateY(100%) translateZ(0px);
|
|
16
|
+
background: var(--bs-body-bg);
|
|
17
|
+
box-shadow: 0 2px 10px 0 hsl(0 calc(1 * 0%) 0% /.1);
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
border-radius: 5px;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
|
|
22
|
+
&.show {
|
|
23
|
+
opacity: 1;
|
|
24
|
+
pointer-events: all;
|
|
25
|
+
transform: translate3d(-50%, 0%, 0px);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
29
|
+
<script>
|
|
30
|
+
import { Vue, Prop, Watch, Component } from 'vue-property-decorator';
|
|
31
|
+
|
|
32
|
+
export default @Component({
|
|
33
|
+
components: {
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
class NoticePopout extends Vue {
|
|
37
|
+
@Prop(Boolean) visible;
|
|
38
|
+
|
|
39
|
+
@Watch('visible')
|
|
40
|
+
toggle(show = true) {
|
|
41
|
+
if (show) {
|
|
42
|
+
this.$refs.notice.classList.add('show');
|
|
43
|
+
} else {
|
|
44
|
+
this.$refs.notice.classList.remove('show');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
|
-
<div class="itf-table2
|
|
3
|
+
<div class="itf-table2" :class="{
|
|
4
4
|
'table-striped': striped,
|
|
5
5
|
'table-absolute': absolute,
|
|
6
6
|
'table-clickable': clickable,
|
|
7
7
|
'permanent-checkboxes': selectedIds.length
|
|
8
8
|
}" :style="{ '--indicator-area-width': `${indicatorType === 'none' ? 1 : indicatorWidth}px` }">
|
|
9
|
+
<itf-notice-popout :visible="showGroupOperations" class="rounded-pill bg-dark text-light">
|
|
10
|
+
<div class="d-flex gap-3 px-3 align-items-center">
|
|
11
|
+
<div><strong>{{selectedIds.length}}</strong> selected</div>
|
|
12
|
+
<div>
|
|
13
|
+
<slot name="group-operations"></slot>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</itf-notice-popout>
|
|
17
|
+
<div class="scrollable scrollable-x">
|
|
9
18
|
<itf-checkbox-group v-model="selectedIds">
|
|
10
19
|
<template v-for="(group, index) in groups">
|
|
11
20
|
<div class="table-view-body">
|
|
@@ -58,6 +67,7 @@
|
|
|
58
67
|
</div>
|
|
59
68
|
</template>
|
|
60
69
|
</itf-checkbox-group>
|
|
70
|
+
</div>
|
|
61
71
|
</div>
|
|
62
72
|
|
|
63
73
|
</template>
|
|
@@ -68,6 +78,7 @@ import itfButton from '../button/Button.vue';
|
|
|
68
78
|
import itfIcon from '../icon/Icon.vue';
|
|
69
79
|
import itfTableGroup from './TableGroup.vue';
|
|
70
80
|
import itfTableHeader from './TableHeader.vue';
|
|
81
|
+
import itfNoticePopout from '../popover/NoticePopout.vue';
|
|
71
82
|
import './table2.scss';
|
|
72
83
|
|
|
73
84
|
export default @Component({
|
|
@@ -80,7 +91,8 @@ export default @Component({
|
|
|
80
91
|
itfTableHeader,
|
|
81
92
|
itfButton,
|
|
82
93
|
itfIcon,
|
|
83
|
-
itfTableGroup
|
|
94
|
+
itfTableGroup,
|
|
95
|
+
itfNoticePopout
|
|
84
96
|
}
|
|
85
97
|
})
|
|
86
98
|
class itfTable2 extends Vue {
|
|
@@ -97,7 +109,7 @@ class itfTable2 extends Vue {
|
|
|
97
109
|
@Prop({ type: String, default: null }) stateName; // save state to storage
|
|
98
110
|
@Prop({ type: Object, default: () => ({}) }) schema;
|
|
99
111
|
@ModelSync('value', 'input', { type: Array, default: () => ([]) }) selectedIds;
|
|
100
|
-
@PropSync('sorting'
|
|
112
|
+
@PropSync('sorting') _sorting;
|
|
101
113
|
@Prop({ type: Array, default: () => [] }) expandedIds;
|
|
102
114
|
@Prop() currency;
|
|
103
115
|
@Prop() currencies;
|
|
@@ -123,6 +135,10 @@ class itfTable2 extends Vue {
|
|
|
123
135
|
columns: []
|
|
124
136
|
};
|
|
125
137
|
|
|
138
|
+
get showGroupOperations() {
|
|
139
|
+
return !!this.$slots['group-operations'] && this.selectedIds.length > 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
126
142
|
getTableState() {
|
|
127
143
|
const list = this.schema?.properties || [];
|
|
128
144
|
let state = this.stateName ? JSON.parse(localStorage.getItem(this.stateKey) || 'null') : null;
|
|
@@ -163,6 +179,7 @@ class itfTable2 extends Vue {
|
|
|
163
179
|
}
|
|
164
180
|
|
|
165
181
|
mounted() {
|
|
182
|
+
console.info(this.$scopedSlots)
|
|
166
183
|
this.onSchemaUpdate();
|
|
167
184
|
}
|
|
168
185
|
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
:column-sorting="columnSorting"
|
|
39
39
|
:show-add-column="showAddColumn"
|
|
40
40
|
:show-actions="showActions"
|
|
41
|
+
:id-property="idProperty"
|
|
41
42
|
:rows="rows"
|
|
42
43
|
:schema="schema"
|
|
43
44
|
:editable="editable"
|
|
@@ -362,7 +363,7 @@ class itfTableGroup extends Vue {
|
|
|
362
363
|
@Prop(Boolean) stickyHeader;
|
|
363
364
|
@Prop() indicatorWidth;
|
|
364
365
|
@Prop() cssProperty;
|
|
365
|
-
@PropSync('sorting'
|
|
366
|
+
@PropSync('sorting') _sorting;
|
|
366
367
|
@Prop({ type: String, default: null }) indicatorType;
|
|
367
368
|
@Prop({type: String, default: function() { return this.$t('components.table.new'); } }) newLabel;
|
|
368
369
|
@Prop({type: Object, default: () => ({})}) schema;
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
{{getTitle(column.title)}}
|
|
39
39
|
<div v-if="column.prefix" class="itf-table2__subtitle" v-text="column.prefix" />
|
|
40
40
|
</span>
|
|
41
|
-
<itf-icon v-if="
|
|
41
|
+
<itf-icon v-if="sortColumnParams[column.property]" :name="sortColumnParams[column.property] === 'asc' ? 'arrow_up' : 'arrow_down'" :size="16" class="ms-1" />
|
|
42
42
|
</template>
|
|
43
43
|
|
|
44
44
|
<div v-if="column.sortable">
|
|
@@ -174,6 +174,7 @@ class itfTableHeader extends Vue {
|
|
|
174
174
|
@Prop(Boolean) noColumnMenu;
|
|
175
175
|
@Prop(Boolean) noSelectAll;
|
|
176
176
|
@Prop(Boolean) editable;
|
|
177
|
+
@Prop() idProperty;
|
|
177
178
|
@Prop() indicatorType;
|
|
178
179
|
|
|
179
180
|
@Watch('selectedIds')
|
|
@@ -186,6 +187,14 @@ class itfTableHeader extends Vue {
|
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
get sortColumnParams() {
|
|
191
|
+
if (typeof this._sorting === 'string'){
|
|
192
|
+
return this._sorting[0] === '-' ? {[this._sorting.substring(1)]: 'desc'} : {[this._sorting]: 'asc'};
|
|
193
|
+
} else {
|
|
194
|
+
return this._sorting;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
189
198
|
getTitle(title) {
|
|
190
199
|
if (typeof title === 'string') {
|
|
191
200
|
return title;
|
|
@@ -203,7 +212,7 @@ class itfTableHeader extends Vue {
|
|
|
203
212
|
|
|
204
213
|
set selectAll(val) {
|
|
205
214
|
if (val) {
|
|
206
|
-
this.$emit('update:selectedIds', this.rows.map(r => r.
|
|
215
|
+
this.$emit('update:selectedIds', this.rows.map(r => r[this.idProperty]));
|
|
207
216
|
} else {
|
|
208
217
|
this.$emit('update:selectedIds', []);
|
|
209
218
|
}
|
|
@@ -350,8 +359,9 @@ class itfTableHeader extends Vue {
|
|
|
350
359
|
}
|
|
351
360
|
|
|
352
361
|
sortBy(column, order) {
|
|
353
|
-
|
|
354
|
-
|
|
362
|
+
let sort = order === 'desc' ? `-${column.property}` : column.property;
|
|
363
|
+
console.info(sort);
|
|
364
|
+
this.$emit('update:sorting', sort);
|
|
355
365
|
}
|
|
356
366
|
}
|
|
357
367
|
</script>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
23
|
<div v-if="indicatorType !== 'none'" class="indicator sticky">
|
|
24
|
-
<div class="fill table-view-row-count" :class="{'on-rest':
|
|
24
|
+
<div class="fill table-view-row-count" :class="{'on-rest': indicatorType !== 'checkbox'}">
|
|
25
25
|
<span v-if="indicatorType === 'order'">{{ (n + 1) }}</span>
|
|
26
26
|
<span v-else-if="indicatorType === 'property'">{{ item[idProperty] }}</span>
|
|
27
27
|
<a href="" @click.prevent.stop="$emit('toggle', item)" v-else-if="indicatorType === 'toggle'">
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
</template>
|
|
42
42
|
</a>
|
|
43
43
|
</div>
|
|
44
|
-
<div
|
|
44
|
+
<div class="fill" :class="{'on-hover': indicatorType !== 'checkbox'}">
|
|
45
45
|
<itf-checkbox :value="item[idProperty]" />
|
|
46
46
|
</div>
|
|
47
47
|
</div>
|
|
@@ -365,4 +365,19 @@ body[data-theme="dark"] {
|
|
|
365
365
|
bottom: 0;
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
|
+
|
|
369
|
+
.table-group-operations {
|
|
370
|
+
background: red;
|
|
371
|
+
position: absolute;
|
|
372
|
+
bottom: 1rem;
|
|
373
|
+
left: 1rem;
|
|
374
|
+
right: 1rem;
|
|
375
|
+
height: 2rem;
|
|
376
|
+
transform: rotateX(90deg);
|
|
377
|
+
transition: transform 0.3s;
|
|
378
|
+
|
|
379
|
+
&.visible {
|
|
380
|
+
transform: rotateX(0deg);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
368
383
|
}
|
|
@@ -8,7 +8,7 @@ const LINKED_IN_REGEXP = /(http(s)?:\/\/)?([\w]+\.)?linkedin\.com\/(pub|in|profi
|
|
|
8
8
|
const SPECIAL_CHARS_REGEXP = /^[\w_\-+~,/\\:'"().&*|[\]?# ]+$/i;
|
|
9
9
|
const GREETINGS_REGEXP = /\b(dr|mr|mister|mrs|ms|miss|sir|hello|hi)\b/i;
|
|
10
10
|
const PHONE_REGEXP = /(\+?\(?\+?[0-9]{1,3}\)?[-. ]+([0-9]{2,4})[-. ]?([0-9]{3,5}))|\+?[0-9]{7,}/gi;
|
|
11
|
-
const DOUBLE_REGEXP = /^-?\d{0,11}(\.\d{0,
|
|
11
|
+
const DOUBLE_REGEXP = /^-?\d{0,11}(\.\d{0,8}){0,1}$/;
|
|
12
12
|
const EMAIL_REGEXP = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/g;
|
|
13
13
|
const EMAIL_LIST_REGEXP = /^(\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]{2,4}\s*?,?\s*?)+$/g;
|
|
14
14
|
const HEX_REGEXP = /[0-9A-Fa-f]{6}/;
|