@itfin/components 1.3.84 → 1.3.86

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itfin/components",
3
- "version": "1.3.84",
3
+ "version": "1.3.86",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -26,11 +26,11 @@ $success: #10834e;
26
26
  $warning: #cda277;
27
27
  $danger: #cb4839;
28
28
 
29
- $primary: #0B314F;
30
- $link-color: #0B314F;
29
+ $primary: #0B314F !default;
30
+ $link-color: #0B314F !default;
31
31
  $input-btn-focus-width: .125rem;
32
32
 
33
- $input-bg: #f3f3f3;
33
+ $input-bg: #f3f3f3 !default;
34
34
  $input-border-color: rgba(#000, .08);
35
35
  $input-border-radius: 10px;
36
36
  $input-font-size: 0.875rem;
@@ -10,9 +10,9 @@
10
10
  padding: 15px;
11
11
 
12
12
  .toast-body {
13
- word-break: break-all;
13
+ word-break: break-word;
14
14
  }
15
-
15
+
16
16
  &.is-top-left,
17
17
  &.is-top-center,
18
18
  &.is-top-right {
@@ -95,7 +95,7 @@
95
95
  margin-top: -45px;
96
96
  }
97
97
  }
98
-
99
- .itf-toast-message {
98
+ .itf-toast-message.toast {
99
+ --bs-toast-max-width: 400px;
100
100
  z-index: $zindex-toaster;
101
101
  }
@@ -1,8 +1,5 @@
1
- import Vue from 'vue';
2
1
  import ToastContainer from './ToastContainer.vue';
3
2
 
4
- const MessageConstructor = Vue.extend(ToastContainer);
5
-
6
3
  const instances = [];
7
4
  let count = 1;
8
5
  const containers = {};
@@ -39,9 +36,10 @@ const Message = function (options) {
39
36
  Message.close(id, userOnClose);
40
37
  };
41
38
  const parent = document.getElementById('itf-app');
42
- const instance = new MessageConstructor({
43
- parent: parent ? parent.__vue__ : null,
44
- el: document.createElement('div'),
39
+
40
+ const instance = new ToastContainer({
41
+ parent: $nuxt.$el.__vue__,
42
+ el: document.createElement('itf-toast-container'),
45
43
  data: options
46
44
  });
47
45
 
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <itf-form class="facets-panel" :class="{ horizontal }">
3
+ <div v-for="(item, n) of filters" :key="n" class="mb-3">
4
+ <template v-if="item.facet && ((facets[item.facet] && !horizontal) || horizontal)">
5
+ <div><strong>{{ item.label }}</strong></div>
6
+ <div class="facets-container">
7
+ <div v-if="horizontal" class="facets-search">
8
+ <input type="text" :placeholder="$t('search')" :value="query[item.facet]" @input="(e) => setQuery(e, item.facet)">
9
+ </div>
10
+ <div v-if="facets[item.facet]" class="facets-container__scroll">
11
+ <facet-item
12
+ :query="query[item.facet]"
13
+ :facet="facets[item.facet]"
14
+ :item="item"
15
+ :value="filter"
16
+ :show-all="horizontal"
17
+ @input="setFilterValue"
18
+ />
19
+ </div>
20
+ </div>
21
+ </template>
22
+ <filter-item
23
+ v-else-if="!item.facet"
24
+ :type="item.type"
25
+ :filter="item"
26
+ :value="filter"
27
+ :prepend-icon="item.id && item.id.includes('custom_') ? null : 'search'"
28
+ :labels="labels"
29
+ :loading-labels="loadingLabels"
30
+ @input="setFilterValue"
31
+ />
32
+ </div>
33
+
34
+ <div v-for="(facet, n) of customFields" :key="`cf_${n}`" class="mb-3">
35
+ <div><strong>{{ facet.Name }}</strong></div>
36
+ <div class="facets-container">
37
+ <facet-item
38
+ :facet="facet"
39
+ :item="{ id: facet.Id, title: facet.Name }"
40
+ :value="filter"
41
+ :show-all="horizontal"
42
+ @input="setFilterValue"
43
+ />
44
+ </div>
45
+ </div>
46
+ </itf-form>
47
+ </template>
48
+ <style lang="scss" scoped>
49
+ .facets-panel {
50
+ &.horizontal {
51
+ display: grid;
52
+ grid-template-columns: repeat(7, clamp(150px, 13.75%, 15%));
53
+ grid-gap: .5rem;
54
+
55
+ .facets-container {
56
+ border: 1px solid var(--bs-border-color);
57
+ max-height: 100%;
58
+ min-height: 90%;
59
+
60
+ .facets-search {
61
+ border-bottom: 1px solid var(--bs-border-color);
62
+
63
+ input {
64
+ padding: .5rem;
65
+ border: 0;
66
+ width: 100%;
67
+ }
68
+ }
69
+ &::v-deep .facet-bar {
70
+ display: none;
71
+ }
72
+ &__scroll {
73
+ max-height: 200px;
74
+ overflow: auto;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ </style>
80
+ <script>
81
+ import { Vue, Prop, Model, Component } from 'vue-property-decorator';
82
+ import itfButton from '@itfin/components/src/components/button/Button';
83
+ import itfForm from '@itfin/components/src/components/form/Form';
84
+ import FilterItem from './FilterItem.vue';
85
+ import FacetItem from './FacetItem.vue';
86
+
87
+ export default @Component({
88
+ name: 'FacetFilters',
89
+ components: {
90
+ itfForm,
91
+ itfButton,
92
+ FilterItem,
93
+ FacetItem
94
+ }
95
+ })
96
+ class FacetFilters extends Vue {
97
+ @Model('input') value;
98
+ @Prop(String) type;
99
+ @Prop() filter;
100
+ @Prop() labels;
101
+ @Prop() loadingLabels;
102
+ @Prop() facets;
103
+ @Prop(Boolean) horizontal;
104
+ @Prop(Array) filters;
105
+
106
+ query = {};
107
+
108
+ setQuery(e, facet) {
109
+ this.query = {
110
+ ...this.query,
111
+ [facet]: e.target.value
112
+ };
113
+ }
114
+
115
+ get customFields() {
116
+ if (!this.facets) {
117
+ return [];
118
+ }
119
+ const fields = [];
120
+ for (const key in this.facets) {
121
+ const item = this.facets[key];
122
+ if (item.IsCustomField) {
123
+ fields.push({
124
+ ...item,
125
+ Id: key
126
+ });
127
+ }
128
+ }
129
+ return fields;
130
+ }
131
+
132
+ setFilterValue (newFilter = {}) {
133
+ const filter = { ...newFilter };
134
+
135
+ this.$emit('update:filter', filter);
136
+ }
137
+ }
138
+ </script>
@@ -0,0 +1,162 @@
1
+ <template>
2
+ <div>
3
+ <button v-for="(val, n) of mappedValues" :key="n" class="facet-item" :class="{'active': val.isSelected}" @click="onFilterClick(val)">
4
+ <span class="facet-name text-dark">{{ val.Text }}</span>
5
+ <span class="facet-stat text-muted">
6
+ {{ val.Count }}
7
+ <span class="facet-bar"><span :style="{'--bar-width': `${getPercent(val)}%`}" class="facet-bar-progress" /></span>
8
+ </span>
9
+ </button>
10
+
11
+ <itf-button v-if="hasMore" small block @click="toggleMore">
12
+ <span v-if="showMore">{{ $t('common.hideMore', { count: facet.Values.length }) }}</span>
13
+ <span v-else>{{ $t('common.showMore', { count: facet.Values.length }) }}</span>
14
+ </itf-button>
15
+ </div>
16
+ </template>
17
+ <style lang="scss" scoped>
18
+ .facet-item {
19
+ background: transparent;
20
+ cursor: pointer;
21
+ display: inline-flex;
22
+ -webkit-box-align: center;
23
+ align-items: center;
24
+ -webkit-box-pack: justify;
25
+ justify-content: space-between;
26
+ position: relative;
27
+ box-sizing: border-box;
28
+ height: 1.75rem;
29
+ width: 100%;
30
+ padding-left: 0.25rem;
31
+ padding-right: 0.25rem;
32
+ font-size: 0.875rem;
33
+ line-height: 1.25rem;
34
+ font-weight: 400;
35
+ white-space: normal;
36
+ user-select: none;
37
+ border-radius: 0.25rem;
38
+ border-width: 1px;
39
+ border-style: solid;
40
+ border-color: transparent;
41
+ border-image: initial;
42
+ transition: none 0s ease 0s;
43
+ &.active {
44
+ background-color: rgba(var(--bs-primary-rgb), 25%);
45
+ }
46
+ &:hover, &:focus, &.active {
47
+ border-color: var(--bs-primary);
48
+ }
49
+ .facet-name {
50
+ text-align: left;
51
+ white-space: nowrap;
52
+ overflow: hidden;
53
+ text-overflow: ellipsis;
54
+ }
55
+ .facet-stat {
56
+ display: flex;
57
+ -webkit-box-align: center;
58
+ align-items: center;
59
+ margin-left: 0.25rem;
60
+ }
61
+ .facet-bar {
62
+ display: inline-block;
63
+ margin-left: 0.5rem;
64
+ width: 60px;
65
+ }
66
+ .facet-bar-progress {
67
+ display: block;
68
+ width: var(--bar-width);
69
+ min-width: 5px;
70
+ height: 10px;
71
+ background-color: rgb(197, 205, 223);
72
+ transition: width 0.3s ease 0s;
73
+ }
74
+ }
75
+ </style>
76
+ <script>
77
+ import { Vue, Prop, Model, Component } from 'vue-property-decorator';
78
+ import itfButton from '@itfin/components/src/components/button/Button';
79
+ import { DateTime } from 'luxon';
80
+
81
+ export default @Component({
82
+ name: 'FacetItem',
83
+ components: {
84
+ itfButton
85
+ }
86
+ })
87
+ class FacetItem extends Vue {
88
+ @Model('input') value;
89
+ @Prop() facet;
90
+ @Prop() item;
91
+ @Prop() query;
92
+ @Prop(Boolean) showAll;
93
+
94
+ showMore = false;
95
+
96
+ toggleMore() {
97
+ this.showMore = !this.showMore;
98
+ }
99
+
100
+ get hasMore() {
101
+ if (this.showAll) {
102
+ return false;
103
+ }
104
+ return this.facet.Values.length > 5;
105
+ }
106
+
107
+ get mappedValues() {
108
+ const { id, items = [], multiple } = this.item || {};
109
+ let list = this.facet.Values.map(val => {
110
+ const item = items.find(i => `${i.value}` === `${val.Value}`);
111
+ const isSelected = multiple
112
+ ? Array.isArray(this.value[id]) && this.value[id].map(String).includes(`${val.Value}`)
113
+ : `${this.value[id]}` === `${val.Value}`;
114
+
115
+ if (item) {
116
+ // якщо знайдено item, ЗАТРЕ Text в val і замінить його на item.title
117
+ return { ...val, Text: item.title, isSelected };
118
+ }
119
+ return { ...val, isSelected };
120
+ });
121
+
122
+ list = list.map(val => val.Text ? { ...val, Text: this.getFormattedItemTitle(val.Text) } : val);
123
+
124
+ if (this.query) {
125
+ list = list.filter((val) => val.Text.toLowerCase().includes(this.query.toLowerCase()));
126
+ }
127
+ if (!this.showMore && !this.showAll) {
128
+ return list.slice(0, 5);
129
+ }
130
+ return list;
131
+ }
132
+
133
+ getFormattedItemTitle(title) {
134
+ return this.isValueDateInStandartFormat(title) ? DateTime.fromFormat(title, 'yyyy-MM-dd', { zone: 'utc' }).toFormat('MM/dd/yyyy') : title;
135
+ }
136
+
137
+ isValueDateInStandartFormat(value) {
138
+ return typeof value === 'string' ? DateTime.fromFormat(value, 'yyyy-MM-dd', { zone: 'utc' }).isValid : false;
139
+ }
140
+
141
+ getPercent(item) {
142
+ return this.facet.Total ? Math.round((item.Count / this.facet.Total) * 100) : 0;
143
+ }
144
+
145
+ onFilterClick(val) {
146
+ const newVal = { ...this.value };
147
+ if (this.item.multiple) {
148
+ newVal[this.item.id] = [...Array.isArray(newVal[this.item.id]) ? newVal[this.item.id] : []].map((s) => s.toString());
149
+ if (newVal[this.item.id].includes(`${val.Value}`)) {
150
+ newVal[this.item.id].splice(newVal[this.item.id].indexOf(`${val.Value}`), 1);
151
+ } else {
152
+ newVal[this.item.id].push(`${val.Value}`);
153
+ }
154
+ } else if (`${newVal[this.item.id]}` === `${val.Value}`) {
155
+ newVal[this.item.id] = null;
156
+ } else {
157
+ newVal[this.item.id] = val.Value;
158
+ }
159
+ this.$emit('input', newVal);
160
+ }
161
+ }
162
+ </script>
@@ -0,0 +1,423 @@
1
+ <template>
2
+ <div>
3
+ <template v-if="type === 'string'">
4
+ <itf-text-field
5
+ delay-input="300"
6
+ :prepend-icon="prependIcon"
7
+ :clearable="filterClearable"
8
+ :placeholder="filter.label"
9
+ :value="plainValue"
10
+ @input="updateValue(filter.id, $event)"
11
+ />
12
+ </template>
13
+ <template v-if="type === 'range'">
14
+ <div class="row">
15
+ <div class="col">
16
+ <itf-text-field
17
+ delay-input="300"
18
+ clearable
19
+ :placeholder="`${filter.label} (${$t('from')})`"
20
+ :value="value[filter.fields[0]]"
21
+ @input="updateValue(filter.fields[0], $event)"
22
+ />
23
+ </div>
24
+ <div class="col">
25
+ <itf-text-field
26
+ delay-input="300"
27
+ clearable
28
+ :placeholder="`(${$t('to')})`"
29
+ :value="value[filter.fields[1]]"
30
+ @input="updateValue(filter.fields[1], $event)"
31
+ />
32
+ </div>
33
+ </div>
34
+ </template>
35
+ <template v-else-if="type === 'list'">
36
+ <itf-select
37
+ :options="filter.items"
38
+ :clearable="filterClearable"
39
+ :reduce="(item) => item.value"
40
+ :get-option-label="(item) => item.title"
41
+ :selectable="(filter.selectable || (() => true))"
42
+ :value="plainValue"
43
+ :placeholder="filter.label"
44
+ :disabled="filter.disable"
45
+ @input="updateValue(filter.id, $event)"
46
+ >
47
+ <template #option="{ option }">
48
+ <div>
49
+ <span v-if="option.icon">{{ option.icon }}</span>
50
+ {{ option.title }}
51
+ </div>
52
+ </template>
53
+ <template #selected-option="{ option }">
54
+ <div>
55
+ <span v-if="option.icon">{{ option.icon }}</span>
56
+ {{ option.title }}
57
+ </div>
58
+ </template>
59
+ </itf-select>
60
+ </template>
61
+ <template v-else-if="type === 'tree'">
62
+ <itf-select
63
+ multiple
64
+ :clearable="filterClearable"
65
+ :placeholder="filter.label"
66
+ :value="plainValue"
67
+ :options="filter.items"
68
+ :reduce="(item) => item.id"
69
+ :selectable="(item) => !(item.type && item.type === 'group')"
70
+ @input="updateValue(filter.id, $event)"
71
+ >
72
+ <template #selected-option="{ option }">
73
+ <div v-if="option && option.label" class="d-flex align-items-center">
74
+ <div>
75
+ {{ option.label }}
76
+ </div>
77
+ </div>
78
+ </template>
79
+ <template #option="{ option }">
80
+ <div v-if="option" class="d-flex align-items-start">
81
+ <div :style="{ 'padding-left': `${option.type && option.type === 'group' ? 0 : 20}px` }">
82
+ {{ option.label }}
83
+ </div>
84
+ </div>
85
+ </template>
86
+ </itf-select>
87
+ </template>
88
+ <template v-else-if="type === 'project' && hasProjectsAccess">
89
+ <project-selector
90
+ :placeholder="filter.label"
91
+ :rules="[]"
92
+ :clearable="filterClearable"
93
+ :value="{ Id: plainValue }"
94
+ @input="updateValue(filter.id, $event && $event.Id)"
95
+ />
96
+ </template>
97
+ <template v-else-if="type === 'cashflowLine'">
98
+ <cashflow-line-selector
99
+ class="mb-3"
100
+ :value="{ Id: plainValue }"
101
+ :rules="[]"
102
+ :placeholder="filter.label"
103
+ :clearable="filterClearable"
104
+ @input="updateValue(filter.id, $event && $event.Id)"
105
+ />
106
+ </template>
107
+ <template v-else-if="(type === 'client' || type === 'counterparty') && isAllowClients">
108
+ <client-selector
109
+ :type="type === 'counterparty' ? 'vendors' : null"
110
+ :placeholder="filter.label"
111
+ :rules="[]"
112
+ :clearable="filterClearable"
113
+ :value="{ Id: plainValue }"
114
+ @input="updateValue(filter.id, $event && $event.Id)"
115
+ />
116
+ </template>
117
+ <template v-else-if="(type === 'counterparty2') && isAllowClients">
118
+ <counterparty-selector
119
+ :placeholder="filter.label"
120
+ :rules="[]"
121
+ :clearable="filterClearable"
122
+ :value="{ Id: plainValue }"
123
+ @input="updateValue(filter.id, $event && $event.Id)"
124
+ />
125
+ </template>
126
+ <template v-else-if="type === 'employee' && isCanSeeEmployees">
127
+ <employee-selector
128
+ :placeholder="filter.label"
129
+ :clearable="filterClearable"
130
+ :value="{ Id: plainValue }"
131
+ @input="updateValue(filter.id, $event && $event.Id)"
132
+ />
133
+ </template>
134
+ <template v-else-if="type === 'businessUnit'">
135
+ <business-unit-selector
136
+ :placeholder="filter.label"
137
+ :clearable="filterClearable"
138
+ :value="{ Id: plainValue }"
139
+ @input="updateValue(filter.id, $event && $event.Id)"
140
+ />
141
+ </template>
142
+ <template v-else-if="type === 'currency'">
143
+ <currency-selector
144
+ :placeholder="filter.label"
145
+ :clearable="filterClearable"
146
+ :value="{ Id: plainValue }"
147
+ @input="updateValue(filter.id, $event && $event.Id)"
148
+ />
149
+ </template>
150
+ <template v-else-if="type === 'office'">
151
+ <office-selector
152
+ :placeholder="filter.label"
153
+ :clearable="filterClearable"
154
+ :value="{ Id: plainValue }"
155
+ @input="updateValue(filter.id, $event && $event.Id)"
156
+ />
157
+ </template>
158
+ <template v-else-if="type === 'account'">
159
+ <account-selector
160
+ :placeholder="filter.label"
161
+ :clearable="filterClearable"
162
+ :value="{ Id: plainValue }"
163
+ @input="updateValue(filter.id, $event && $event.Id)"
164
+ />
165
+ </template>
166
+ <template v-else-if="type === 'vacancy'">
167
+ <vacancy-selector
168
+ :placeholder="filter.label"
169
+ :clearable="filterClearable"
170
+ :value="{ Id: plainValue }"
171
+ @input="updateValue(filter.id, $event && $event.Id)"
172
+ />
173
+ </template>
174
+ <template v-else-if="type === 'country'">
175
+ <country-selector
176
+ :placeholder="filter.label"
177
+ :clearable="filterClearable"
178
+ :value="plainValue ? { Name: plainValue } : null"
179
+ @input="updateValue(filter.id, $event && $event.Name)"
180
+ />
181
+ </template>
182
+ <template v-else-if="type === 'profile' && isProfileAvailable">
183
+ <profile-selector
184
+ :placeholder="filter.label"
185
+ :clearable="filterClearable"
186
+ :value="{ Id: plainValue }"
187
+ @input="updateValue(filter.id, $event && $event.Id)"
188
+ />
189
+ </template>
190
+ <template v-else-if="type === 'skills'">
191
+ <other-skills-selector
192
+ :taggable="false"
193
+ :placeholder="filter.label"
194
+ :clearable="filterClearable"
195
+ :value="plainValue"
196
+ @input="updateValue(filter.id, $event)"
197
+ />
198
+ </template>
199
+ <template v-else-if="type === 'label'">
200
+ <itf-select
201
+ :loading="loadingLabels"
202
+ :value="plainValue"
203
+ :placeholder="filter.label"
204
+ :options="prepareLabels"
205
+ multiple
206
+ :reduce="(item) => item.Id"
207
+ :get-option-label="(item) => item.LabelText"
208
+ @input="updateValue(filter.id, $event)"
209
+ >
210
+ <div v-if="filter.conditions" slot="list-header" class="px-2 pb-2">
211
+ <itf-segmented-control
212
+ class="text-uppercase segmented-control"
213
+ :value="value[filter.conditions] || 'and'"
214
+ :items="[{Id: 'and', Name: $t('and')}, {Id: 'or', Name: $t('or')}, {Id: 'not', Name: $t('not')}]"
215
+ @input="updateValue(filter.conditions, $event)"
216
+ />
217
+ </div>
218
+ </itf-select>
219
+ </template>
220
+ <template v-else-if="type === 'button'">
221
+ <itf-button block secondary :to="{ name: filter.to }">
222
+ {{ filter.label }}
223
+ </itf-button>
224
+ </template>
225
+ <template v-else-if="type === 'switch'">
226
+ <itf-checkbox
227
+ switch
228
+ :label="filter.label"
229
+ :value="plainValue"
230
+ @input="updateValue(filter.id, $event)"
231
+ />
232
+ </template>
233
+ <template v-else-if="type === 'date'">
234
+ <itf-date-range-picker
235
+ placement="bottom-end"
236
+ :value="dateRangeValue"
237
+ :placeholder="filter.label"
238
+ :clearable="filterClearable"
239
+ :disabled="filter.disable"
240
+ value-format="yyyy-MM-dd"
241
+ @input="updateDateValue($event, filter.fields)"
242
+ />
243
+ </template>
244
+ <template v-else-if="type === 'team'">
245
+ <team-selector
246
+ :clearable="filterClearable"
247
+ :value="{ Id: plainValue }"
248
+ @input="updateValue(filter.id, ($event && $event.Id) || null)"
249
+ />
250
+ </template>
251
+ <div v-else-if="type === 'dates'" class="d-flex gap-1">
252
+ <div>
253
+ <itf-date-picker
254
+ placement="bottom-end"
255
+ :value="dateRangeValue[0]"
256
+ :placeholder="`${filter.label} (From)`"
257
+ :clearable="filterClearable"
258
+ value-format="yyyy-MM-dd"
259
+ @input="updateDateValue([$event, dateRangeValue[1]], filter.fields)"
260
+ />
261
+ </div>
262
+ <div>
263
+ <itf-date-picker
264
+ placement="bottom-end"
265
+ :value="dateRangeValue[1]"
266
+ :placeholder="`${filter.label} (To)`"
267
+ :clearable="filterClearable"
268
+ value-format="yyyy-MM-dd"
269
+ @input="updateDateValue([dateRangeValue[0], $event], filter.fields)"
270
+ />
271
+ </div>
272
+ </div>
273
+ <template v-else-if="type === 'date-period'">
274
+ <itf-period-picker
275
+ placement="bottom-end"
276
+ :value="dateRangeValue"
277
+ value-format="yyyy-MM-dd"
278
+ :placeholder="filter.label"
279
+ :clearable="filterClearable"
280
+ @input="updateDateValue($event, filter.fields)"
281
+ />
282
+ </template>
283
+ </div>
284
+ </template>
285
+ <style lang="scss" scoped>
286
+ .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {
287
+ z-index: 3 !important;
288
+ }
289
+ </style>
290
+ <script>
291
+ import { Component, Model, Prop, Vue } from 'vue-property-decorator';
292
+ import itfButton from '@itfin/components/src/components/button/Button';
293
+ import itfTextField from '@itfin/components/src/components/text-field/TextField';
294
+ import itfSelect from '@itfin/components/src/components/select/Select';
295
+ import itfDateRangePicker from '@itfin/components/src/components/datepicker/DateRangePicker';
296
+ import itfDatePicker from '@itfin/components/src/components/datepicker/DatePicker';
297
+ import itfPeriodPicker from '@itfin/components/src/components/datepicker/PeriodPicker';
298
+ import itfCheckbox from '@itfin/components/src/components/checkbox/Checkbox';
299
+ import itfSegmentedControl from '@itfin/components/src/components/segmented-control/SegmentedControl';
300
+ import ProfileSelector from '~/components/Common/Selectors/ProfileSelector';
301
+ import ClientSelector from '~/components/Common/Selectors/ClientSelector';
302
+ import CurrencySelector from '~/components/Common/Selectors/CurrencySelector.vue';
303
+ import BusinessUnitSelector from '~/components/Common/Selectors/BusinessUnitSelector.vue';
304
+ import ProjectSelector from '~/components/Common/Selectors/ProjectSelector';
305
+ import EmployeeSelector from '~/components/Common/Selectors/EmployeeSelector';
306
+ import CounterpartySelector from '~/components/Common/Selectors/CounterpartySelector.vue';
307
+ import OfficeSelector from '~/components/Common/Selectors/OfficeSelector';
308
+ import OtherSkillsSelector from '~/components/Skills/OtherSkillsSelector';
309
+ import AccountSelector from '~/components/Common/Selectors/AccountSelector';
310
+ import VacancySelector from '~/components/Common/Selectors/VacancySelector';
311
+ import CountrySelector from '~/components/Common/Selectors/CountrySelector.vue';
312
+ import TeamSelector from '~/components/Common/Selectors/TeamSelector.vue';
313
+ import CashflowLineSelector from '~/components/Common/Selectors/CashflowLineSelector.vue';
314
+
315
+ import {
316
+ CLIENTS_VIEW_CLIENTS,
317
+ CLIENTS_VIEW_OWN_CLIENTS,
318
+ EMPLOYEES_LIST,
319
+ PROFILES_CAN_CHANGE_PROFILES,
320
+ PROFILES_SEE_PROFILES,
321
+ PROJECTS_LIST
322
+ } from '~/scopes';
323
+
324
+ export default @Component({
325
+ name: 'FilterItem',
326
+ components: {
327
+ BusinessUnitSelector,
328
+ CurrencySelector,
329
+ itfButton,
330
+ itfSelect,
331
+ itfCheckbox,
332
+ itfTextField,
333
+ itfSegmentedControl,
334
+ itfDatePicker,
335
+ itfDateRangePicker,
336
+ itfPeriodPicker,
337
+ ProjectSelector,
338
+ ProfileSelector,
339
+ ClientSelector,
340
+ OfficeSelector,
341
+ EmployeeSelector,
342
+ AccountSelector,
343
+ VacancySelector,
344
+ OtherSkillsSelector,
345
+ CountrySelector,
346
+ TeamSelector,
347
+ CounterpartySelector,
348
+ CashflowLineSelector,
349
+ }
350
+ })
351
+ class FilterItem extends Vue {
352
+ @Model('input') value;
353
+ @Prop(String) type;
354
+ @Prop() filter;
355
+ @Prop() labels;
356
+ @Prop() loadingLabels;
357
+ @Prop() prependIcon;
358
+
359
+ get isCanSeeEmployees () {
360
+ return this.$scope(EMPLOYEES_LIST);
361
+ }
362
+
363
+ get isProfileAvailable () {
364
+ return this.$scope(PROFILES_CAN_CHANGE_PROFILES, PROFILES_SEE_PROFILES);
365
+ }
366
+
367
+ get isAllowClients () {
368
+ return this.$scope(CLIENTS_VIEW_CLIENTS, CLIENTS_VIEW_OWN_CLIENTS);
369
+ }
370
+
371
+ get hasProjectsAccess() {
372
+ return this.$scope(PROJECTS_LIST);
373
+ }
374
+
375
+ get plainValue () {
376
+ return this.value[this.filter.id];
377
+ }
378
+
379
+ get dateRangeValue () {
380
+ return this.isCustomFilter
381
+ ? [
382
+ this.value[this.filter.id] && this.value[this.filter.id].from,
383
+ this.value[this.filter.id] && this.value[this.filter.id].to
384
+ ]
385
+ : [
386
+ this.value[(this.filter.fields || {})[0] || 'from'],
387
+ this.value[(this.filter.fields || {})[1] || 'to']
388
+ ];
389
+ }
390
+
391
+ get prepareLabels () {
392
+ return this.labels.map(label => ({ ...label, Color: `#${label.Color}` }));
393
+ }
394
+
395
+ get filterClearable () {
396
+ return typeof this.filter.clearable !== 'undefined' ? this.filter.clearable : true;
397
+ }
398
+
399
+ get isCustomFilter () {
400
+ return this.filter.id.toLowerCase().includes('customfield_');
401
+ }
402
+
403
+ updateValue (key, value, fields = {}) {
404
+ const filter = { ...this.value };
405
+ filter[key] = value;
406
+ this.$emit('input', filter);
407
+ }
408
+
409
+ updateDateValue (value, fields = {}) {
410
+ const filter = { ...this.value };
411
+
412
+ if (this.isCustomFilter) {
413
+ const key = this.filter.id;
414
+ filter[key] = value[0] && value[1] ? { from: value[0], to: value[1] } : null;
415
+ } else {
416
+ filter[fields[0] || 'from'] = value[0];
417
+ filter[fields[1] || 'to'] = value[1];
418
+ }
419
+
420
+ this.$emit('input', filter);
421
+ }
422
+ }
423
+ </script>
@@ -1,12 +1,10 @@
1
1
  import { storiesOf } from '@storybook/vue';
2
+ import FiltersList from './FiltersList.vue';
2
3
 
3
4
  storiesOf('Common', module)
4
5
  .add('Filter', () => ({
5
6
  components: {
6
- itfRule,
7
- itfRuleGroup,
8
- itfFilterBadge,
9
- itfFilterControl
7
+ FiltersList
10
8
  },
11
9
  data() {
12
10
  return {
@@ -78,22 +76,9 @@ storiesOf('Common', module)
78
76
  </pre>
79
77
 
80
78
 
81
- <itf-filter-control url-sync v-model="filter" :filters="filterFields">
82
- <template #dropdown-item="{ item }">
83
- {{item.text}}
84
- </template>
85
- <template #item="{ item }">
86
- {{item}}
87
- </template>
88
- <template #item="{ item }">
89
- {{item}}
90
- </template>
91
- </itf-filter-control>
79
+ <filters-list />
92
80
 
93
81
  {{filter}}
94
82
 
95
- <h2>Example</h2>
96
-
97
- <itf-rule-group :options="options" first />
98
83
  </div>`,
99
84
  }));