@itfin/components 2.0.17 → 2.0.18
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/datepicker/DateGranularityPicker.vue +2 -1
- package/src/components/datepicker/PeriodPicker.vue +39 -182
- package/src/components/datepicker/PeriodPickerInline.vue +187 -0
- package/src/components/datepicker/index.stories.js +4 -0
- package/src/components/filter/FilterBadge.vue +22 -3
- package/src/components/filter/FilterFacetsList.vue +14 -5
- package/src/components/filter/FilterPanel.vue +155 -41
package/package.json
CHANGED
|
@@ -129,7 +129,7 @@ class itfDateGranularityPicker extends Vue {
|
|
|
129
129
|
@Prop({ type: [String, Date], default: ''}) maxDate;
|
|
130
130
|
@Prop(Boolean) disabled;
|
|
131
131
|
|
|
132
|
-
granularity = '
|
|
132
|
+
granularity = 'monthly';
|
|
133
133
|
|
|
134
134
|
get granularities() {
|
|
135
135
|
return [
|
|
@@ -137,6 +137,7 @@ class itfDateGranularityPicker extends Vue {
|
|
|
137
137
|
{ title: 'Quarterly', value: 'quarterly' },
|
|
138
138
|
{ title: 'Monthly', value: 'monthly' },
|
|
139
139
|
{ title: 'Weekly', value: 'weekly' },
|
|
140
|
+
{ title: 'Daily', value: 'daily' },
|
|
140
141
|
{ title: 'Custom', value: 'manual' },
|
|
141
142
|
];
|
|
142
143
|
}
|
|
@@ -11,85 +11,27 @@
|
|
|
11
11
|
:placeholder="placeholder"
|
|
12
12
|
/>
|
|
13
13
|
<div style="display: none">
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<div class="col">
|
|
22
|
-
<itf-button block @click="setYear(prevYear)">
|
|
23
|
-
{{prevYear}}
|
|
24
|
-
</itf-button>
|
|
25
|
-
</div>
|
|
26
|
-
<div class="col">
|
|
27
|
-
<itf-button color="outline-primary" block>
|
|
28
|
-
{{year}}
|
|
29
|
-
</itf-button>
|
|
30
|
-
</div>
|
|
31
|
-
<div class="col">
|
|
32
|
-
<itf-button block @click="setYear(nextYear)">
|
|
33
|
-
{{nextYear}}
|
|
34
|
-
</itf-button>
|
|
35
|
-
</div>
|
|
36
|
-
<div class="col text-end">
|
|
37
|
-
<itf-button @click="setYear(nextYear)">
|
|
38
|
-
<itf-icon name="chevron_right" />
|
|
39
|
-
</itf-button>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<itf-button block class="mb-3" :class="{'btn-whole': !isCurrentYear()}" :primary="isCurrentYear()" @click="onYearSelect(year)">
|
|
44
|
-
{{ $t('components.wholeYear') }}
|
|
45
|
-
</itf-button>
|
|
46
|
-
|
|
47
|
-
<div class="itf-periodpicker__quarters">
|
|
48
|
-
<div
|
|
49
|
-
v-for="quarter of quarters"
|
|
50
|
-
:key="quarter.Number"
|
|
51
|
-
class="itf-periodpicker__quarter"
|
|
52
|
-
:class="{ 'active': isOnlyQuarter(quarter.Number), 'active-inside': isCurrentQuarter(quarter.Number) }">
|
|
53
|
-
<div class="px-3 pt-2" @click="onQuarterSelect([quarter.Months[0], quarter.Months[2]])">
|
|
54
|
-
<small><b>QUARTER</b></small><br>
|
|
55
|
-
<span class="quarter-number">{{ quarter.Number }}</span>
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<div class="itf-periodpicker__months">
|
|
59
|
-
<itf-button
|
|
60
|
-
class="itf-periodpicker__month px-1"
|
|
61
|
-
v-for="month of quarter.Months"
|
|
62
|
-
:key="month"
|
|
63
|
-
:primary="isCurrentMonth(month)"
|
|
64
|
-
@click="onMonthSelect(month)"
|
|
65
|
-
>
|
|
66
|
-
{{ month | formatMonth }}
|
|
67
|
-
</itf-button>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
14
|
+
<itf-period-picker-inline
|
|
15
|
+
ref="dropdown"
|
|
16
|
+
class="itf-periodpicker__dropdown"
|
|
17
|
+
v-model="value"
|
|
18
|
+
:value-format="valueFormat"
|
|
19
|
+
@input="onInput"
|
|
20
|
+
/>
|
|
72
21
|
</div>
|
|
73
22
|
</div>
|
|
74
23
|
</template>
|
|
75
24
|
<script>
|
|
76
25
|
import { Vue, Component, Prop, Inject } from 'vue-property-decorator';
|
|
77
|
-
import { DateTime } from 'luxon';
|
|
78
26
|
import tippy from 'tippy.js';
|
|
79
|
-
import
|
|
80
|
-
import
|
|
27
|
+
import itfPeriodPickerInline from "./PeriodPickerInline.vue";
|
|
28
|
+
import {DateTime} from "luxon";
|
|
81
29
|
import ITFSettings from '../../ITFSettings';
|
|
82
30
|
|
|
83
31
|
export default @Component({
|
|
84
32
|
name: 'itfPeriodPicker',
|
|
85
33
|
components: {
|
|
86
|
-
|
|
87
|
-
itfButton
|
|
88
|
-
},
|
|
89
|
-
filters: {
|
|
90
|
-
formatMonth(month) {
|
|
91
|
-
return DateTime.local().set({ month }).toFormat('MMM');
|
|
92
|
-
}
|
|
34
|
+
itfPeriodPickerInline
|
|
93
35
|
}
|
|
94
36
|
})
|
|
95
37
|
class itfPeriodPicker extends Vue {
|
|
@@ -97,51 +39,12 @@ class itfPeriodPicker extends Vue {
|
|
|
97
39
|
|
|
98
40
|
@Prop({ type: Array }) value;
|
|
99
41
|
@Prop({ type: String, default: 'ISO' }) valueFormat;
|
|
100
|
-
@Prop({ type: String }) displayFormat;
|
|
101
42
|
@Prop({ type: String, default: 'bottom-start' }) placement;
|
|
102
|
-
@Prop({ type: String, default: 'days', validator: (value) => ['days', 'months', 'years'].includes(value) }) startView;
|
|
103
|
-
@Prop({ type: Boolean, default: false }) onlyCalendar;
|
|
104
43
|
@Prop({ type: String, default: '' }) placeholder;
|
|
105
|
-
|
|
106
|
-
year = null;
|
|
44
|
+
@Prop({ type: String }) displayFormat;
|
|
107
45
|
|
|
108
46
|
focused = false;
|
|
109
47
|
|
|
110
|
-
tooltip = null;
|
|
111
|
-
|
|
112
|
-
get quarters() {
|
|
113
|
-
return [
|
|
114
|
-
{ Name: `${this.$t('components.quarter')} 1`, Months: [1, 2, 3], Number: 1 },
|
|
115
|
-
{ Name: `${this.$t('components.quarter')} 2`, Months: [4, 5, 6], Number: 2 },
|
|
116
|
-
{ Name: `${this.$t('components.quarter')} 3`, Months: [7, 8, 9], Number: 3 },
|
|
117
|
-
{ Name: `${this.$t('components.quarter')} 4`, Months: [10, 11, 12], Number: 4 },
|
|
118
|
-
];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
get dateFormat() {
|
|
122
|
-
return this.displayFormat || ITFSettings.defaultDisplayDateFormat;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
get nextYear() {
|
|
126
|
-
return this.year + 1;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
get prevYear() {
|
|
130
|
-
return this.year - 1;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
setYear(year) {
|
|
134
|
-
this.year = year;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
isInvalid() {
|
|
138
|
-
return this.itemLabel && this.itemLabel.isHasError();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
isSuccess() {
|
|
142
|
-
return this.itemLabel && this.itemLabel.isHasSuccess();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
48
|
mounted() {
|
|
146
49
|
const context = this.$el.closest('.itf-append-context') || document.body;
|
|
147
50
|
this.tooltip = tippy(this.$refs.input, {
|
|
@@ -149,30 +52,17 @@ class itfPeriodPicker extends Vue {
|
|
|
149
52
|
interactiveDebounce: 75,
|
|
150
53
|
animation: 'scale',
|
|
151
54
|
arrow: true,
|
|
152
|
-
content: this.$refs.dropdown,
|
|
55
|
+
content: this.$refs.dropdown.$el,
|
|
153
56
|
allowHTML: true,
|
|
154
57
|
trigger: 'click',
|
|
155
58
|
interactive: true,
|
|
156
59
|
placement: this.placement,
|
|
157
60
|
appendTo: context,
|
|
158
61
|
});
|
|
159
|
-
this.year = (this.value && this.value[0]) ? this.valueAsLuxon[0].year : DateTime.local().year;
|
|
160
62
|
}
|
|
161
63
|
|
|
162
|
-
get
|
|
163
|
-
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
if (this.valueFormat === 'ISO') {
|
|
167
|
-
return [
|
|
168
|
-
DateTime.fromISO(this.value[0]),
|
|
169
|
-
DateTime.fromISO(this.value[1]),
|
|
170
|
-
];
|
|
171
|
-
}
|
|
172
|
-
return [
|
|
173
|
-
DateTime.fromFormat(this.value[0], this.valueFormat),
|
|
174
|
-
DateTime.fromFormat(this.value[1], this.valueFormat),
|
|
175
|
-
];
|
|
64
|
+
get dateFormat() {
|
|
65
|
+
return this.displayFormat || ITFSettings.defaultDisplayDateFormat;
|
|
176
66
|
}
|
|
177
67
|
|
|
178
68
|
get displayText() {
|
|
@@ -183,12 +73,29 @@ class itfPeriodPicker extends Vue {
|
|
|
183
73
|
if (!this.valueAsLuxon || this.valueAsLuxon.length < 2) {
|
|
184
74
|
return [];
|
|
185
75
|
}
|
|
76
|
+
console.info(this.valueAsLuxon);
|
|
186
77
|
return [
|
|
187
78
|
this.valueAsLuxon[0].toFormat(this.dateFormat),
|
|
188
79
|
this.valueAsLuxon[1].toFormat(this.dateFormat)
|
|
189
80
|
];
|
|
190
81
|
}
|
|
191
82
|
|
|
83
|
+
get valueAsLuxon() {
|
|
84
|
+
if (!this.value || this.value.length < 2) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (this.valueFormat === 'ISO') {
|
|
88
|
+
return [
|
|
89
|
+
DateTime.fromISO(this.value[0]),
|
|
90
|
+
DateTime.fromISO(this.value[1]),
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
return [
|
|
94
|
+
DateTime.fromFormat(this.value[0], this.valueFormat),
|
|
95
|
+
DateTime.fromFormat(this.value[1], this.valueFormat),
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
|
|
192
99
|
onFocus() {
|
|
193
100
|
this.focused = true;
|
|
194
101
|
}
|
|
@@ -197,71 +104,21 @@ class itfPeriodPicker extends Vue {
|
|
|
197
104
|
this.focused = false;
|
|
198
105
|
}
|
|
199
106
|
|
|
200
|
-
|
|
201
|
-
this
|
|
202
|
-
this.tooltip.hide();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
isCurrentMonth(month) {
|
|
206
|
-
if (!this.valueAsLuxon) {
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
return Number(this.valueAsLuxon[0].toFormat('M')) === month && Number(this.valueAsLuxon[1].toFormat('M')) === month;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
isOnlyQuarter(quarter) {
|
|
213
|
-
if (!this.valueAsLuxon) {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
return this.isCurrentQuarter(quarter) && this.valueAsLuxon[0].month !== this.valueAsLuxon[1].month;
|
|
107
|
+
isInvalid() {
|
|
108
|
+
return this.itemLabel && this.itemLabel.isHasError();
|
|
217
109
|
}
|
|
218
110
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
return this.valueAsLuxon[0].quarter === quarter && this.valueAsLuxon[1].quarter === quarter;
|
|
111
|
+
isSuccess() {
|
|
112
|
+
return this.itemLabel && this.itemLabel.isHasSuccess();
|
|
224
113
|
}
|
|
225
114
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
return this.valueAsLuxon[0].hasSame(this.valueAsLuxon[0].startOf('year'), 'day')
|
|
231
|
-
&& this.valueAsLuxon[1].hasSame(this.valueAsLuxon[0].endOf('year'), 'day');
|
|
115
|
+
selectInlineDate(date) {
|
|
116
|
+
this.$emit('input', date);
|
|
232
117
|
}
|
|
233
118
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.$emit('input', []);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
this.$emit('input', [
|
|
240
|
-
(this.valueFormat === 'ISO') ? start.toISO() : start.toFormat(this.valueFormat),
|
|
241
|
-
(this.valueFormat === 'ISO') ? end.toISO() : end.toFormat(this.valueFormat),
|
|
242
|
-
]);
|
|
119
|
+
onInput(val) {
|
|
120
|
+
this.$emit('input', val);
|
|
243
121
|
this.tooltip.hide();
|
|
244
122
|
}
|
|
245
|
-
|
|
246
|
-
onYearSelect(year) {
|
|
247
|
-
this.updateValue(
|
|
248
|
-
DateTime.local().set({ year }).startOf('year'),
|
|
249
|
-
DateTime.local().set({ year }).endOf('year'),
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
onMonthSelect(month) {
|
|
254
|
-
this.updateValue(
|
|
255
|
-
DateTime.local().set({ month, year: this.year }).startOf('month'),
|
|
256
|
-
DateTime.local().set({ month, year: this.year }).endOf('month'),
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
onQuarterSelect([startMonth, endMonth]) {
|
|
261
|
-
this.updateValue(
|
|
262
|
-
DateTime.local().set({ month: startMonth, year: this.year }).startOf('quarter'),
|
|
263
|
-
DateTime.local().set({ month: endMonth, year: this.year }).endOf('quarter'),
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
123
|
}
|
|
267
124
|
</script>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div class="row mb-3">
|
|
4
|
+
<div class="col">
|
|
5
|
+
<itf-button icon @click="setYear(prevYear)">
|
|
6
|
+
<itf-icon name="chevron_left" />
|
|
7
|
+
</itf-button>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="col">
|
|
10
|
+
<itf-button block @click="setYear(prevYear)">
|
|
11
|
+
{{prevYear}}
|
|
12
|
+
</itf-button>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="col">
|
|
15
|
+
<itf-button color="outline-primary" block>
|
|
16
|
+
{{year}}
|
|
17
|
+
</itf-button>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="col">
|
|
20
|
+
<itf-button block @click="setYear(nextYear)">
|
|
21
|
+
{{nextYear}}
|
|
22
|
+
</itf-button>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="col text-end">
|
|
25
|
+
<itf-button icon @click="setYear(nextYear)">
|
|
26
|
+
<itf-icon name="chevron_right" />
|
|
27
|
+
</itf-button>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<itf-button block class="mb-3" :class="{'btn-whole': !isCurrentYear()}" :primary="isCurrentYear()" @click="onYearSelect(year)">
|
|
32
|
+
{{ $t('components.wholeYear') }}
|
|
33
|
+
</itf-button>
|
|
34
|
+
|
|
35
|
+
<div class="itf-periodpicker__quarters">
|
|
36
|
+
<div
|
|
37
|
+
v-for="quarter of quarters"
|
|
38
|
+
:key="quarter.Number"
|
|
39
|
+
class="itf-periodpicker__quarter"
|
|
40
|
+
:class="{ 'active': isOnlyQuarter(quarter.Number), 'active-inside': isCurrentQuarter(quarter.Number) }">
|
|
41
|
+
<div class="px-3 pt-2" @click="onQuarterSelect([quarter.Months[0], quarter.Months[2]])">
|
|
42
|
+
<small><b>{{ $t('components.quarter') }}</b></small><br>
|
|
43
|
+
<span class="quarter-number">{{ quarter.Number }}</span>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="itf-periodpicker__months">
|
|
47
|
+
<itf-button
|
|
48
|
+
class="itf-periodpicker__month px-1"
|
|
49
|
+
v-for="month of quarter.Months"
|
|
50
|
+
:key="month"
|
|
51
|
+
:primary="isCurrentMonth(month)"
|
|
52
|
+
@click="onMonthSelect(month)"
|
|
53
|
+
>
|
|
54
|
+
{{ month | formatMonth }}
|
|
55
|
+
</itf-button>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
<script>
|
|
62
|
+
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
63
|
+
import { DateTime } from 'luxon';
|
|
64
|
+
import itfIcon from '../icon/Icon';
|
|
65
|
+
import itfButton from '../button/Button';
|
|
66
|
+
|
|
67
|
+
export default @Component({
|
|
68
|
+
name: 'itfPeriodPickerInline',
|
|
69
|
+
components: {
|
|
70
|
+
itfIcon,
|
|
71
|
+
itfButton
|
|
72
|
+
},
|
|
73
|
+
filters: {
|
|
74
|
+
formatMonth(month) {
|
|
75
|
+
return DateTime.local().set({ month }).toFormat('MMM');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
class itfPeriodPickerInline extends Vue {
|
|
80
|
+
@Prop({ type: Array }) value;
|
|
81
|
+
@Prop({ type: String, default: 'ISO' }) valueFormat;
|
|
82
|
+
|
|
83
|
+
year = null;
|
|
84
|
+
|
|
85
|
+
get quarters() {
|
|
86
|
+
return [
|
|
87
|
+
{ Name: `${this.$t('components.quarter')} 1`, Months: [1, 2, 3], Number: 1 },
|
|
88
|
+
{ Name: `${this.$t('components.quarter')} 2`, Months: [4, 5, 6], Number: 2 },
|
|
89
|
+
{ Name: `${this.$t('components.quarter')} 3`, Months: [7, 8, 9], Number: 3 },
|
|
90
|
+
{ Name: `${this.$t('components.quarter')} 4`, Months: [10, 11, 12], Number: 4 },
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get nextYear() {
|
|
95
|
+
return this.year + 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get prevYear() {
|
|
99
|
+
return this.year - 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setYear(year) {
|
|
103
|
+
this.year = year;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
mounted() {
|
|
107
|
+
this.year = (this.value && this.value[0]) ? this.valueAsLuxon[0].year : DateTime.local().year;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get valueAsLuxon() {
|
|
111
|
+
if (!this.value || this.value.length < 2) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (this.valueFormat === 'ISO') {
|
|
115
|
+
return [
|
|
116
|
+
DateTime.fromISO(this.value[0]),
|
|
117
|
+
DateTime.fromISO(this.value[1]),
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
return [
|
|
121
|
+
DateTime.fromFormat(this.value[0], this.valueFormat),
|
|
122
|
+
DateTime.fromFormat(this.value[1], this.valueFormat),
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isCurrentMonth(month) {
|
|
127
|
+
if (!this.valueAsLuxon) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return Number(this.valueAsLuxon[0].toFormat('M')) === month && Number(this.valueAsLuxon[1].toFormat('M')) === month;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
isOnlyQuarter(quarter) {
|
|
134
|
+
if (!this.valueAsLuxon) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return this.isCurrentQuarter(quarter) && this.valueAsLuxon[0].month !== this.valueAsLuxon[1].month;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
isCurrentQuarter(quarter) {
|
|
141
|
+
if (!this.valueAsLuxon) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return this.valueAsLuxon[0].quarter === quarter && this.valueAsLuxon[1].quarter === quarter;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
isCurrentYear() {
|
|
148
|
+
if (!this.valueAsLuxon) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return this.valueAsLuxon[0].hasSame(this.valueAsLuxon[0].startOf('year'), 'day')
|
|
152
|
+
&& this.valueAsLuxon[1].hasSame(this.valueAsLuxon[0].endOf('year'), 'day');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
updateValue(start, end) {
|
|
156
|
+
if (!start) {
|
|
157
|
+
this.$emit('input', []);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.$emit('input', [
|
|
161
|
+
(this.valueFormat === 'ISO') ? start.toISO() : start.toFormat(this.valueFormat),
|
|
162
|
+
(this.valueFormat === 'ISO') ? end.toISO() : end.toFormat(this.valueFormat),
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
onYearSelect(year) {
|
|
167
|
+
this.updateValue(
|
|
168
|
+
DateTime.local().set({ year }).startOf('year'),
|
|
169
|
+
DateTime.local().set({ year }).endOf('year'),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
onMonthSelect(month) {
|
|
174
|
+
this.updateValue(
|
|
175
|
+
DateTime.local().set({ month, year: this.year }).startOf('month'),
|
|
176
|
+
DateTime.local().set({ month, year: this.year }).endOf('month'),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
onQuarterSelect([startMonth, endMonth]) {
|
|
181
|
+
this.updateValue(
|
|
182
|
+
DateTime.local().set({ month: startMonth, year: this.year }).startOf('quarter'),
|
|
183
|
+
DateTime.local().set({ month: endMonth, year: this.year }).endOf('quarter'),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
</script>
|
|
@@ -2,6 +2,7 @@ import { storiesOf } from '@storybook/vue';
|
|
|
2
2
|
import itfDatePicker from './DatePicker.vue';
|
|
3
3
|
import itfMonthPicker from './MonthPicker.vue';
|
|
4
4
|
import itfPeriodPicker from './PeriodPicker';
|
|
5
|
+
import itfPeriodPickerInline from './PeriodPickerInline';
|
|
5
6
|
import itfDatePickerInline from './DatePickerInline.vue';
|
|
6
7
|
import itfDateRangePicker from './DateRangePicker.vue';
|
|
7
8
|
import itfDateRangePickerInline from './DateRangePickerInline.vue';
|
|
@@ -13,6 +14,7 @@ storiesOf('Common', module)
|
|
|
13
14
|
itfApp,
|
|
14
15
|
itfDatePicker,
|
|
15
16
|
itfPeriodPicker,
|
|
17
|
+
itfPeriodPickerInline,
|
|
16
18
|
itfMonthPicker,
|
|
17
19
|
itfDateRangePicker,
|
|
18
20
|
itfDatePickerInline,
|
|
@@ -86,6 +88,8 @@ storiesOf('Common', module)
|
|
|
86
88
|
<h2>Period</h2>
|
|
87
89
|
|
|
88
90
|
<itf-period-picker :value="dateRange" v-model.lazy="dateRange"></itf-period-picker>
|
|
91
|
+
|
|
92
|
+
<itf-period-picker-inline :value="dateRange" v-model.lazy="dateRange"></itf-period-picker-inline>
|
|
89
93
|
</itf-app>
|
|
90
94
|
</div>`,
|
|
91
95
|
}));
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
</template>
|
|
14
14
|
|
|
15
|
-
<div>
|
|
15
|
+
<div class="px-2">
|
|
16
16
|
<template v-if="type === 'list'">
|
|
17
17
|
<a
|
|
18
18
|
v-for="(item, n) in options.items"
|
|
@@ -29,6 +29,13 @@
|
|
|
29
29
|
@input="onFilterChange({ value: $event })"
|
|
30
30
|
/>
|
|
31
31
|
</template>
|
|
32
|
+
<template v-else-if="type === 'timeframe'">
|
|
33
|
+
<itf-period-picker-inline
|
|
34
|
+
:value="value.value"
|
|
35
|
+
value-format="yyyy-MM-dd"
|
|
36
|
+
@input="onFilterChange({ value: $event })"
|
|
37
|
+
/>
|
|
38
|
+
</template>
|
|
32
39
|
<template v-else-if="type === 'date'">
|
|
33
40
|
<itf-date-picker-inline
|
|
34
41
|
style="margin: -.5rem"
|
|
@@ -61,6 +68,7 @@
|
|
|
61
68
|
<style lang="scss">
|
|
62
69
|
:root {
|
|
63
70
|
--filter-badge__default-color: #475266;
|
|
71
|
+
--filter-badge__icon-color: #A7AFBB;
|
|
64
72
|
--filter-badge__default-border-color: #0000001A;
|
|
65
73
|
--filter-badge__default-bg-color: transparent;
|
|
66
74
|
--filter-badge__default-bg-color-hover: #1A4A970D;
|
|
@@ -75,6 +83,10 @@
|
|
|
75
83
|
--filter-badge__padding-y: .5rem;
|
|
76
84
|
--filter-badge__padding-x: .75rem;
|
|
77
85
|
}
|
|
86
|
+
body[data-theme="dark"] {
|
|
87
|
+
--filter-badge__default-border-color: #FFFFFF1A;
|
|
88
|
+
--filter-badge__selected-color: #efd877;
|
|
89
|
+
}
|
|
78
90
|
.filter-pill {
|
|
79
91
|
align-items: center;
|
|
80
92
|
font-size: 14px;
|
|
@@ -87,6 +99,7 @@
|
|
|
87
99
|
|
|
88
100
|
.icon {
|
|
89
101
|
margin: -2px;
|
|
102
|
+
color: var(--filter-badge__icon-color);
|
|
90
103
|
}
|
|
91
104
|
&:hover {
|
|
92
105
|
background-color: var(--filter-badge__default-bg-color-hover);
|
|
@@ -95,16 +108,20 @@
|
|
|
95
108
|
&.filter-not-default-pill {
|
|
96
109
|
background-color: var(--filter-badge__selected-bg-color);
|
|
97
110
|
outline: 1px solid var(--filter-badge__selected-color);
|
|
111
|
+
|
|
112
|
+
.icon { color: var(--filter-badge__selected-color) }
|
|
98
113
|
}
|
|
99
114
|
&.filter-invalid-pill {
|
|
100
115
|
background-color: var(--filter-badge__invalid-bg-color);
|
|
116
|
+
|
|
117
|
+
.icon { color: var(--filter-badge__invalid-color) }
|
|
101
118
|
}
|
|
102
119
|
&.filter-pill__default-value {
|
|
103
120
|
padding: var(--filter-badge__padding-y) var(--filter-badge__padding-x);
|
|
104
121
|
}
|
|
105
122
|
}
|
|
106
123
|
.filter-pill__label {
|
|
107
|
-
color: var(--filter-badge__default-color);
|
|
124
|
+
//color: var(--filter-badge__default-color);
|
|
108
125
|
padding: var(--filter-badge__padding-y) 0 var(--filter-badge__padding-y) var(--filter-badge__padding-x);
|
|
109
126
|
max-width: 330px;
|
|
110
127
|
text-overflow: ellipsis;
|
|
@@ -129,7 +146,7 @@
|
|
|
129
146
|
padding: 0 calc(var(--filter-badge__padding-x) / 2) 0 calc(var(--filter-badge__padding-x) / 4);
|
|
130
147
|
|
|
131
148
|
svg {
|
|
132
|
-
color: var(--filter-badge__default-color);
|
|
149
|
+
//color: var(--filter-badge__default-color);
|
|
133
150
|
}
|
|
134
151
|
&.filter-pill__icon-invalid svg {
|
|
135
152
|
color: var(--filter-badge__invalid-color);
|
|
@@ -146,6 +163,7 @@ import itfButton from '../button/Button';
|
|
|
146
163
|
import itfDropdown from '../dropdown/Dropdown.vue';
|
|
147
164
|
import itfDatePickerInline from '../datepicker/DatePickerInline.vue';
|
|
148
165
|
import itfDateRangePickerInline from '../datepicker/DateRangePickerInline.vue';
|
|
166
|
+
import itfPeriodPickerInline from '../datepicker/PeriodPickerInline.vue'
|
|
149
167
|
import itfTextField from '../text-field/TextField.vue';
|
|
150
168
|
import FilterFacetsList from './FilterFacetsList';
|
|
151
169
|
import FilterAmountRange from './FilterAmountRange.vue';
|
|
@@ -157,6 +175,7 @@ export default @Component({
|
|
|
157
175
|
itfDropdown,
|
|
158
176
|
itfDatePickerInline,
|
|
159
177
|
itfDateRangePickerInline,
|
|
178
|
+
itfPeriodPickerInline,
|
|
160
179
|
itfTextField,
|
|
161
180
|
FilterFacetsList,
|
|
162
181
|
FilterAmountRange
|
|
@@ -19,16 +19,18 @@
|
|
|
19
19
|
<div class="text-muted text-center py-4">{{ $t('components.filter.noResults') }}</div>
|
|
20
20
|
</div>
|
|
21
21
|
</div>
|
|
22
|
-
<div
|
|
22
|
+
<div class="facets-list">
|
|
23
|
+
<div v-for="(val, n) of mappedValues" :key="n" class="dropdown-item px-2" :class="{'active': val.isSelected}" @click="onFilterClick(val)">
|
|
23
24
|
<span class="facet-name text-dark d-flex align-items-center">
|
|
24
25
|
<itf-checkbox ungrouped :value="val.isSelected" class="m-0" />
|
|
25
|
-
<div class="w-100 text-truncate">{{ val.label }}</div>
|
|
26
|
+
<div class="w-100 text-truncate">{{ val.label }} <span v-if="val.description" class="small"><br/>{{ val.description }}</span></div>
|
|
26
27
|
</span>
|
|
27
|
-
<span class="facet-stat
|
|
28
|
+
<span v-if="val.count" class="facet-stat">
|
|
28
29
|
{{ val.count }}
|
|
29
30
|
<span class="facet-bar"><span :style="{'--bar-width': `${getPercent(val)}%`}" class="facet-bar-progress" /></span>
|
|
30
31
|
</span>
|
|
31
32
|
</div>
|
|
33
|
+
</div>
|
|
32
34
|
|
|
33
35
|
<itf-button default class="mt-1" v-if="hasMore" small block @click="toggleMore">
|
|
34
36
|
<span v-if="showMore">{{ $t('components.filter.hideMore', { count: visibleList.length }) }}</span>
|
|
@@ -43,7 +45,13 @@
|
|
|
43
45
|
padding: 0 0.75rem .5rem;
|
|
44
46
|
margin: 0 -.75rem .75rem;
|
|
45
47
|
}
|
|
48
|
+
.facets-list {
|
|
49
|
+
max-height: 50vh;
|
|
50
|
+
overflow: auto;
|
|
51
|
+
}
|
|
46
52
|
.dropdown-item {
|
|
53
|
+
--bs-dropdown-link-active-bg: rgba(var(--bs-primary-rgb), .25);
|
|
54
|
+
|
|
47
55
|
cursor: pointer;
|
|
48
56
|
display: inline-flex;
|
|
49
57
|
-webkit-box-align: center;
|
|
@@ -52,7 +60,7 @@
|
|
|
52
60
|
justify-content: space-between;
|
|
53
61
|
position: relative;
|
|
54
62
|
box-sizing: border-box;
|
|
55
|
-
height: 1.75rem;
|
|
63
|
+
min-height: 1.75rem;
|
|
56
64
|
width: 100%;
|
|
57
65
|
font-size: 0.875rem;
|
|
58
66
|
line-height: 1.25rem;
|
|
@@ -74,10 +82,11 @@
|
|
|
74
82
|
.facet-name {
|
|
75
83
|
min-width: 0;
|
|
76
84
|
text-align: left;
|
|
85
|
+
line-height: 100%;
|
|
77
86
|
white-space: nowrap;
|
|
78
87
|
|
|
79
88
|
.itf-checkbox {
|
|
80
|
-
min-height:
|
|
89
|
+
min-height: 1.25rem;
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
.facet-stat {
|
|
@@ -1,41 +1,56 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="itf-filter-panel d-flex flex-column gap-3 align-items-start">
|
|
3
|
-
<div v-if="
|
|
3
|
+
<div v-if="!filtersOnly" class="d-flex gap-2 justify-content-between w-100">
|
|
4
4
|
<slot name="search">
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
<div>
|
|
6
|
+
<itf-text-field
|
|
7
|
+
v-if="search"
|
|
8
|
+
style="width: 300px"
|
|
9
|
+
small
|
|
10
|
+
:placeholder="searchPlaceholder"
|
|
11
|
+
prepend-icon="search"
|
|
12
|
+
:delay-input="250"
|
|
13
|
+
clearable
|
|
14
|
+
:value="filterValueQuery"
|
|
15
|
+
@input="(e) => onFilterChange({ type: 'text', name: 'query' }, { value: e })"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
15
18
|
</slot>
|
|
16
19
|
<div class="d-flex gap-2">
|
|
17
|
-
|
|
20
|
+
<!--itf-button v-if="showFilter" default icon class="position-relative" @click="toggleFilters" :class="{'active': showFilters}">
|
|
18
21
|
<itf-icon new name="filter" />
|
|
19
22
|
<span v-if="activeFiltersCount" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary">
|
|
20
23
|
{{activeFiltersCount}}
|
|
21
24
|
<span class="visually-hidden">active filters</span>
|
|
22
25
|
</span>
|
|
23
|
-
</itf-button
|
|
26
|
+
</itf-button-->
|
|
24
27
|
<slot name="after-filter-btn"></slot>
|
|
25
28
|
</div>
|
|
26
29
|
</div>
|
|
27
|
-
<div
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
<div class="d-flex align-items-start justify-content-between w-100">
|
|
31
|
+
<div v-show="showFilters && showFilter" class="gap-2 filters-row" ref="container" :class="{'expanded': isFilterExpanded}">
|
|
32
|
+
<filter-badge
|
|
33
|
+
v-for="(facet, n) in visibleFilters"
|
|
34
|
+
:key="n"
|
|
35
|
+
class="itf-filter-panel__badge"
|
|
36
|
+
:ref="'item-' + n"
|
|
37
|
+
v-model="filter[facet.name]"
|
|
38
|
+
:is-default="filter[facet.name].isDefault"
|
|
39
|
+
:text="filter[facet.name].label"
|
|
40
|
+
:type="facet.type"
|
|
41
|
+
:icon="facet.icon"
|
|
42
|
+
:options="facet.options"
|
|
43
|
+
:class="{ hidden: !visibleItems.has(n) && !isFilterExpanded }"
|
|
44
|
+
@change="onFilterChange(facet, $event)"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
<div v-if="showFilters && showFilter && (visibleItems.size < visibleFilters.length || (isFilterExpanded && visibleItems.size === visibleFilters.length))">
|
|
48
|
+
<itf-button icon default small class="itf-filter-panel__filters" @click="toggleExpandFilter">
|
|
49
|
+
<itf-icon v-if="isFilterExpanded" name="minus" />
|
|
50
|
+
<itf-icon v-else name="plus" />
|
|
51
|
+
</itf-button>
|
|
52
|
+
</div>
|
|
53
|
+
<slot name="after-filters"></slot>
|
|
39
54
|
</div>
|
|
40
55
|
<div v-if="loading">
|
|
41
56
|
<span class="itf-spinner"></span>
|
|
@@ -45,6 +60,18 @@
|
|
|
45
60
|
</template>
|
|
46
61
|
<style lang="scss">
|
|
47
62
|
.itf-filter-panel {
|
|
63
|
+
&__badge {
|
|
64
|
+
transition: opacity 0.3s ease-in-out;
|
|
65
|
+
|
|
66
|
+
&.hidden {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
pointer-events: none;
|
|
69
|
+
visibility: hidden;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
&__filters {
|
|
73
|
+
outline: 1px solid var(--filter-badge__default-border-color);
|
|
74
|
+
}
|
|
48
75
|
.itf-text-field:not(.is-valid):not(.is-invalid) .itf-icon {
|
|
49
76
|
color: #8E97A5;
|
|
50
77
|
}
|
|
@@ -63,18 +90,28 @@
|
|
|
63
90
|
}
|
|
64
91
|
|
|
65
92
|
.filters-row {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
93
|
+
display: flex;
|
|
94
|
+
overflow: hidden;
|
|
95
|
+
width: 100%;
|
|
96
|
+
padding: 2px;
|
|
97
|
+
margin: -2px;
|
|
98
|
+
flex-wrap: nowrap;
|
|
99
|
+
|
|
100
|
+
&.expanded {
|
|
101
|
+
flex-wrap: wrap;
|
|
102
|
+
|
|
103
|
+
.itf-filter-panel__badge.hidden {
|
|
104
|
+
opacity: 1;
|
|
105
|
+
visibility: visible;
|
|
106
|
+
pointer-events: all;
|
|
107
|
+
}
|
|
71
108
|
}
|
|
72
109
|
}
|
|
73
110
|
}
|
|
74
111
|
</style>
|
|
75
112
|
<script>
|
|
76
113
|
import { DateTime } from 'luxon';
|
|
77
|
-
import { Vue, Model, Prop, Component } from 'vue-property-decorator';
|
|
114
|
+
import { Vue, Watch, Model, Prop, Component } from 'vue-property-decorator';
|
|
78
115
|
import tooltip from '../../directives/tooltip';
|
|
79
116
|
import itfIcon from '../icon/Icon';
|
|
80
117
|
import itfButton from '../button/Button';
|
|
@@ -103,18 +140,75 @@ class FilterPanel extends Vue {
|
|
|
103
140
|
@Prop() panel;
|
|
104
141
|
@Prop(String) stateName;
|
|
105
142
|
@Prop(Boolean) search;
|
|
143
|
+
@Prop({ type: Boolean, default: true }) showFilter;
|
|
144
|
+
@Prop({ type: Boolean, default: false }) filtersOnly;
|
|
106
145
|
@Prop(Boolean) mini;
|
|
107
146
|
@Prop({ type: String, default: function() { return this.$t('components.filter.search'); } }) searchPlaceholder;
|
|
108
147
|
|
|
109
148
|
filter = {};
|
|
110
|
-
filterValue =
|
|
149
|
+
filterValue = null;
|
|
111
150
|
filters = [];
|
|
112
151
|
loading = false;
|
|
113
152
|
showFilters = true;
|
|
153
|
+
isFilterExpanded = false;
|
|
154
|
+
observer = null;
|
|
155
|
+
|
|
156
|
+
periodFilters = ['period', 'timeframe'];
|
|
157
|
+
visibleItems = new Set();
|
|
158
|
+
|
|
159
|
+
toggleExpandFilter() {
|
|
160
|
+
this.isFilterExpanded = !this.isFilterExpanded;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
beforeDestroy() {
|
|
164
|
+
if (this.observer) {
|
|
165
|
+
this.observer.disconnect();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
initObserver() {
|
|
170
|
+
if (this.observer) {
|
|
171
|
+
this.observer.disconnect();
|
|
172
|
+
}
|
|
173
|
+
if (!this.$refs.container) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.observer = new IntersectionObserver(
|
|
177
|
+
(entries) => {
|
|
178
|
+
entries.forEach(entry => {
|
|
179
|
+
const index = parseInt(entry.target.dataset.index);
|
|
180
|
+
const filter = this.filters[index];
|
|
181
|
+
const value = this.filter[filter.name];
|
|
182
|
+
if (entry.isIntersecting) {
|
|
183
|
+
this.visibleItems.add(index); // Додаємо, якщо елемент у полі зору
|
|
184
|
+
} else {
|
|
185
|
+
this.visibleItems.delete(index); // Видаляємо, якщо вийшов за межі
|
|
186
|
+
}
|
|
187
|
+
this.$forceUpdate(); // Оновлюємо Vue, бо Set не є реактивним
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
{ root: this.$refs.container, threshold: 1.0 }
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Спостерігаємо за кожним елементом
|
|
194
|
+
this.$nextTick(() => {
|
|
195
|
+
for (const index in this.visibleFilters) {
|
|
196
|
+
const item = this.$refs[`item-${index}`][0];
|
|
197
|
+
if (item) {
|
|
198
|
+
item.$el.dataset.index = index; // Зберігаємо індекс у dataset
|
|
199
|
+
this.observer.observe(item.$el);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
get filterValueQuery() {
|
|
206
|
+
return this.filterValue?.query ?? '';
|
|
207
|
+
}
|
|
114
208
|
|
|
115
209
|
get visibleFilters() {
|
|
116
210
|
if (this.mini) {
|
|
117
|
-
return sortBy(this.filters, (f) => this.filter[f.name].isDefault).filter(f => !f.options?.hidden)
|
|
211
|
+
return sortBy(this.filters, (f) => this.filter[f.name].isDefault).filter(f => !f.options?.hidden);
|
|
118
212
|
}
|
|
119
213
|
return this.filters.filter(f => !f.options?.hidden);
|
|
120
214
|
}
|
|
@@ -127,6 +221,12 @@ class FilterPanel extends Vue {
|
|
|
127
221
|
return `filter-panel-${this.stateName}-filters`;
|
|
128
222
|
}
|
|
129
223
|
|
|
224
|
+
@Watch('staticFilters', { deep: true })
|
|
225
|
+
onStaticFiltersUpdate() {
|
|
226
|
+
this.filters = this.staticFilters ?? [];
|
|
227
|
+
this.loadFiltersValue();
|
|
228
|
+
}
|
|
229
|
+
|
|
130
230
|
async mounted() {
|
|
131
231
|
if (this.stateName) {
|
|
132
232
|
const item = localStorage.getItem(this.localstorageKey);
|
|
@@ -137,8 +237,10 @@ class FilterPanel extends Vue {
|
|
|
137
237
|
if (this.endpoint) {
|
|
138
238
|
this.loading = true;
|
|
139
239
|
await this.$try(async () => {
|
|
140
|
-
const
|
|
240
|
+
const payload = this.panel ? this.panel.getPayload() : {};
|
|
241
|
+
const {filters, tableSchema} = await this.$axios.$get(this.endpoint, { params: payload });
|
|
141
242
|
this.filters = filters;
|
|
243
|
+
this.$emit('set-table-schema', tableSchema);
|
|
142
244
|
this.loadFiltersValue();
|
|
143
245
|
});
|
|
144
246
|
this.loading = false;
|
|
@@ -163,7 +265,7 @@ class FilterPanel extends Vue {
|
|
|
163
265
|
const filterValue = {};
|
|
164
266
|
if (this.filters) {
|
|
165
267
|
for (const item of this.filters) {
|
|
166
|
-
if (item.type
|
|
268
|
+
if (this.periodFilters.includes(item.type)) {
|
|
167
269
|
filter[item.name] = payload.from ? this.formatValue(item, { value: [payload.from, payload.to] }) : { isDefault: true, ...item.options.defaultValue };
|
|
168
270
|
filterValue.from = payload.from;
|
|
169
271
|
filterValue.to = payload.to;
|
|
@@ -176,13 +278,25 @@ class FilterPanel extends Vue {
|
|
|
176
278
|
if (this.search) {
|
|
177
279
|
filterValue.query = payload.query;
|
|
178
280
|
}
|
|
179
|
-
const prevFilter = JSON.stringify(this.
|
|
180
|
-
const newFilter = JSON.stringify(
|
|
181
|
-
if (prevFilter !== newFilter) {
|
|
281
|
+
const prevFilter = JSON.stringify(this.getVisibleFilters(this.filterValue));//.concat(JSON.stringify(this.filterValue));
|
|
282
|
+
const newFilter = JSON.stringify(this.getVisibleFilters(filterValue));//.concat(JSON.stringify(filterValue));
|
|
283
|
+
if (prevFilter !== newFilter || !this.filterValue) {
|
|
182
284
|
this.filter = filter;
|
|
183
285
|
this.filterValue = filterValue;
|
|
184
286
|
this.$emit('input', this.filterValue);
|
|
287
|
+
this.initObserver();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getVisibleFilters(filter) {
|
|
292
|
+
const result = [];
|
|
293
|
+
const facets = Object.values(this.filter);
|
|
294
|
+
for (const facet of facets) {
|
|
295
|
+
if (!facet.isDefault && !facet.hidden) {
|
|
296
|
+
result.push(filter[facet.name]);
|
|
297
|
+
}
|
|
185
298
|
}
|
|
299
|
+
return result.filter(Boolean);
|
|
186
300
|
}
|
|
187
301
|
|
|
188
302
|
setFilter(field, value) {
|
|
@@ -195,7 +309,7 @@ class FilterPanel extends Vue {
|
|
|
195
309
|
|
|
196
310
|
onFilterChange(facet, value) {
|
|
197
311
|
this.filter[facet.name] = this.formatValue(facet, value);
|
|
198
|
-
if (facet.type
|
|
312
|
+
if (this.periodFilters.includes(facet.type)) {
|
|
199
313
|
this.filterValue.from = this.filter[facet.name].isDefault ? undefined : value.value[0];
|
|
200
314
|
this.filterValue.to = this.filter[facet.name].isDefault ? undefined : value.value[1];
|
|
201
315
|
} else {
|
|
@@ -226,7 +340,7 @@ class FilterPanel extends Vue {
|
|
|
226
340
|
}
|
|
227
341
|
|
|
228
342
|
formatValue(facet, value) {
|
|
229
|
-
if (facet.type
|
|
343
|
+
if (this.periodFilters.includes(facet.type)) {
|
|
230
344
|
if (value.value) {
|
|
231
345
|
let from = DateTime.fromFormat(value.value[0], 'yyyy-MM-dd');
|
|
232
346
|
let to = DateTime.fromFormat(value.value[1], 'yyyy-MM-dd');
|
|
@@ -285,7 +399,7 @@ class FilterPanel extends Vue {
|
|
|
285
399
|
value.isDefault = facet.options.defaultValue ? JSON.stringify(value.value) === JSON.stringify(facet.options.defaultValue.value) : false;
|
|
286
400
|
value.label = item ? item.label : facet.options.defaultValue.label;
|
|
287
401
|
} else if (facet.type === 'text') {
|
|
288
|
-
value.value = value.value.length ? value.value : undefined;
|
|
402
|
+
value.value = value.value.length ? value.value : (facet.options?.defaultValue ?? undefined);
|
|
289
403
|
value.isDefault = !value.value;
|
|
290
404
|
}
|
|
291
405
|
value.hidden = facet.options?.hidden ?? false;
|