@itfin/components 2.0.1 → 2.0.3

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.
@@ -0,0 +1,3 @@
1
+ <svg style="transform: rotate(-90deg);" xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="-1 -1 19 19">
2
+ <path d="M3.5 3.5c-.614-.884-.074-1.962.858-2.5L8 7.226 11.642 1c.932.538 1.472 1.616.858 2.5L8.81 8.61l1.556 2.661a2.5 2.5 0 1 1-.794.637L8 9.73l-1.572 2.177a2.5 2.5 0 1 1-.794-.637L7.19 8.61zm2.5 10a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0m7 0a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0"/>
3
+ </svg>
@@ -27,7 +27,6 @@
27
27
  @click="$emit('cancel')"
28
28
  >
29
29
  <span v-html="$t('components.popover.noKeepIt')"></span>
30
- <kbd>Esc</kbd>
31
30
  </button>
32
31
  <button
33
32
  type="button"
@@ -36,7 +35,6 @@
36
35
  @click="onConfirm"
37
36
  >
38
37
  <span v-html="$t('components.popover.yesDelete')"></span>
39
- <kbd>↵</kbd>
40
38
  </button>
41
39
  </div>
42
40
  </div>
@@ -43,15 +43,13 @@
43
43
 
44
44
  <itf-button
45
45
  v-if="!readonly"
46
- squircle
47
46
  data-test="item-editor-cancel"
48
47
  :disabled="loading"
49
- secondary
48
+ default
50
49
  class="me-2"
51
50
  @click="$emit('input', false)"
52
51
  >
53
52
  <span class="me-1">{{ $t('components.modal.cancel') }}</span>
54
- <kbd>Esc</kbd>
55
53
  </itf-button>
56
54
 
57
55
  <slot
@@ -60,14 +58,12 @@
60
58
  >
61
59
  <itf-button
62
60
  primary
63
- squircle
64
61
  data-test="item-editor-save"
65
62
  :disabled="loading"
66
63
  :loading="loading"
67
64
  @click="submitSaveItem()"
68
65
  >
69
66
  <span class="me-1">{{ saveBtnCaption }}</span>
70
- <kbd>Shift + ↵</kbd>
71
67
  </itf-button>
72
68
  </slot>
73
69
  </slot>
@@ -14,7 +14,7 @@
14
14
  <slot name="header">
15
15
  <div>
16
16
  <slot name="title">
17
- <div class="b-panel__title ps-1 fw-bold h5" v-text="title"></div>
17
+ <div class="b-panel__title ps-1 fw-bold h2" v-text="title"></div>
18
18
  </slot>
19
19
  </div>
20
20
  <div class="d-flex gap-1">
@@ -5,7 +5,7 @@
5
5
  </template>
6
6
  <style scoped lang="scss">
7
7
  .notice {
8
- position: absolute;
8
+ position: fixed;
9
9
  bottom: 10px;
10
10
  left: 50%;
11
11
  max-width: 740px;
@@ -17,10 +17,10 @@
17
17
  <div class="shadow-area"></div>
18
18
  <div class="header-wrapper" :class="{'header-additional-column': showAddColumn}" @click.prevent="toggleGroup">
19
19
  <a class="header-content position-sticky d-flex align-items-center">
20
- <span class="collapse-arrow">
20
+ <itf-button squircle icon small secondary class="collapse-arrow">
21
21
  <itf-icon :name="isShowTable ? 'chevron_down' : 'chevron_right'"/>
22
- </span>
23
- <span class="d-flex align-items-center line-overflow group-header-value"
22
+ </itf-button>
23
+ <span class="d-flex align-items-center line-overflow group-header-value text-primary"
24
24
  data-test="group-value-group-label-value">
25
25
  <slot name="group-title" :rows="rows" :title="title">{{ title }}</slot>
26
26
  </span>
@@ -230,7 +230,7 @@
230
230
  border-radius: 0;
231
231
  }
232
232
  &:hover {
233
- background: var(--itf-table-hover-bg);
233
+ //background: var(--itf-table-hover-bg); // видно, що група не на всю ширину коли є нижній скролінг
234
234
  }
235
235
  }
236
236
 
@@ -239,7 +239,7 @@
239
239
  }
240
240
  .header-content:not(.draggable-mirror *) {
241
241
  left: var(--shadow-area-width);
242
- padding-left: 0.75rem;
242
+ //padding-left: 0.75rem;
243
243
  padding-right: 0.75rem;
244
244
  gap: 1rem;
245
245
  align-items: center;
@@ -284,9 +284,20 @@
284
284
  }
285
285
 
286
286
  .sticky-group {
287
+ top: 0px;
287
288
  position: sticky;
288
- top: 0;
289
289
  z-index: 10;
290
+
291
+ & > .draggable-item {
292
+ margin-bottom: 8px;
293
+ }
294
+ .collapse-arrow {
295
+ padding: 0;
296
+
297
+ &:after {
298
+ display: none;
299
+ }
300
+ }
290
301
  }
291
302
  .table-summary {
292
303
  .shadow-area {
@@ -143,43 +143,6 @@
143
143
  </div>
144
144
  </template>
145
145
  <div class="table-view-item-value extra flex-grow-1"></div>
146
- <itf-dropdown v-if="showAddColumn && visibleHeader" ref="newDd" text append-to-context shadow autoclose="outside" class="table-header-add-column table-view-item-actions" data-test="table-header-add-column">
147
- <template #button>
148
- <itf-button icon small><span class="nom-gear"></span></itf-button>
149
- </template>
150
-
151
- <itf-sortable :value="sortedColumns" @input="onSortColumns">
152
- <template v-for="(column, k) in sortedColumns">
153
- <div :key="`column${k}`" class="d-flex align-items-center justify-content-between" :class="{'px-2 py-1': column.visible !== false}">
154
- <template v-if="column.visible !== false">
155
- <div class="d-flex justify-content-between flex-grow-1">
156
- <div>
157
- <span class="nom-grip-vertical"></span>
158
- <span v-if="column.icon" :class="column.icon"></span>
159
- {{getTitle(column.title)}}
160
- </div>
161
- <span v-if="column.pinned" class="nom-pin-fill"></span>
162
- </div>
163
- <a sortable-skip href="" class="text-decoration-none" @click.prevent="hideColumn(k)">
164
- <span class="nom-trash"></span>
165
- </a>
166
- </template>
167
- </div>
168
- </template>
169
- </itf-sortable>
170
-
171
- <div v-if="invisibleColumns.length">
172
- <div class="dropdown-header">{{$t('components.table.addColumn')}}</div>
173
- <template v-for="(column, k) in invisibleColumns">
174
- <a href="" @click.prevent="addColumn(column)" :key="`inv-column${k}`" class="dropdown-item d-flex align-items-center justify-content-between px-2 py-1">
175
- <div>
176
- <span class="nom-plus"></span>
177
- {{getTitle(column.title)}}
178
- </div>
179
- </a>
180
- </template>
181
- </div>
182
- </itf-dropdown>
183
146
 
184
147
  <div class="boundary top"></div>
185
148
  <div class="boundary right"></div>
@@ -220,7 +183,7 @@ class itfTableHeader extends Vue {
220
183
  @Inject() tableEl;
221
184
 
222
185
  @PropSync('columns', { type: Array, default: () => ([]) }) sortedColumns;
223
- @PropSync('sorting', { type: Object, default: () => ({}) }) _sorting;
186
+ @PropSync('sorting') _sorting;
224
187
  @Prop({ type: Array, default: () => ([]) }) rows;
225
188
  @Prop({ type: Array, default: () => ([]) }) selectedIds;
226
189
  @Prop({ type: Object, default: () => ({}) }) schema;
@@ -57,18 +57,18 @@
57
57
  <slot :name="`column.${column.property}`" :toggle="() => $emit('toggle', item)" :level="level" :editable="column.editable && editable" :item="item" :column="column" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)">
58
58
  <template v-if="column.editable && editable && (!editableProperty || item[editableProperty])">
59
59
  <slot :name="`edit.${column.type}`" :level="level" :toggle="() => $emit('toggle', item)" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)" :item="item" :column="column">
60
- <itf-text-field class="w-100" v-if="column.type === 'text'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
61
- <itf-text-field class="w-100" v-if="column.type === 'number'" type="number" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
60
+ <itf-text-field class="w-100 h-100" v-if="column.type === 'text'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
61
+ <itf-text-field class="w-100 h-100" v-if="column.type === 'number'" type="number" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
62
62
  <itf-hours-field
63
- class="w-100"
63
+ class="w-100 h-100"
64
64
  placeholder="00h 00m"
65
65
  v-else-if="column.type === 'time'"
66
66
  :hours="getValue(item, column)"
67
67
  @update:hours="updateValue(item, $event, n, column)"
68
68
  />
69
- <itf-textarea class="w-100" :rows="1" autogrow v-else-if="column.type === 'textarea'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
70
- <itf-money-field class="w-100" currency-disabled :currency="currency" :currencies="currencies" v-else-if="column.type === 'money'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
71
- <itf-select class="w-100" v-else-if="column.type === 'select'" :reduce="(item) => item.value" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" :options="column.options"></itf-select>
69
+ <itf-textarea class="w-100 h-100" :rows="1" autogrow v-else-if="column.type === 'textarea'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
70
+ <itf-money-field class="w-100 h-100" currency-disabled :currency="currency" :currencies="currencies" v-else-if="column.type === 'money'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
71
+ <itf-select class="w-100 h-100" v-else-if="column.type === 'select'" :reduce="(item) => item.value" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" :options="column.options"></itf-select>
72
72
  </slot>
73
73
  </template>
74
74
  <div v-else class="h-100 align-items-center d-flex w-100">
@@ -1,7 +1,7 @@
1
1
  :root {
2
2
  --itf-primary: #1A4A97;
3
3
  --itf-table-header-font-size: 12px;
4
- --itf-table-content-font-size: 14px;
4
+ --itf-table-content-font-size: 16px;
5
5
  --itf-table-selected-bg: #f9fafc;
6
6
  --itf-table-active-bg: #1A4A971A;
7
7
  --itf-table-alt-bg: #F9FAFB;
@@ -48,11 +48,20 @@ body[data-theme="dark"] {
48
48
  font-size: var(--itf-table-content-font-size, var(--itf-table-font-size));
49
49
  position: relative;
50
50
 
51
+ .itf-cell-center {
52
+ width: 100%;
53
+ height: 100%;
54
+ display: flex;
55
+ align-items: center;
56
+ }
51
57
  .scrollable {
52
58
  -webkit-overflow-scrolling: touch;
53
59
  overflow: hidden scroll;
54
60
  height: 100%;
55
61
  }
62
+ .scroller {
63
+ margin-bottom: .5rem;
64
+ }
56
65
  .scrollable-x {
57
66
  overflow-x: scroll;
58
67
  padding-right: 4px;
@@ -194,10 +203,10 @@ body[data-theme="dark"] {
194
203
  content: "";
195
204
  position: absolute;
196
205
  display: block;
197
- height: 20px;
206
+ height: 12px;
198
207
  left: 0;
199
208
  right: 0;
200
- top: -20px;
209
+ top: -12px;
201
210
  z-index: 4;
202
211
  background: var(--itf-table-bg);
203
212
  }
@@ -398,13 +407,13 @@ body[data-theme="dark"] {
398
407
  }
399
408
  }
400
409
 
401
- &:hover, &.permanent-editable-border {
402
- .table-view-item-value.editable {
403
- .form-control {
404
- border: 1px solid var(--itf-table-input-border-color);
405
- }
406
- }
407
- }
410
+ //&:hover, &.permanent-editable-border {
411
+ // .table-view-item-value.editable {
412
+ // .form-control {
413
+ // border: 1px solid var(--itf-table-input-border-color);
414
+ // }
415
+ // }
416
+ //}
408
417
  .table-view-item-value-content {
409
418
  position: relative;
410
419
  z-index: 2;
@@ -427,16 +436,16 @@ body[data-theme="dark"] {
427
436
  &.editable {
428
437
  padding: 2px;
429
438
 
430
- .form-control {
431
- border-radius: 0;
432
- border: 1px solid transparent;
433
- background-color: transparent;
434
- box-shadow: none;
435
-
436
- &:focus {
437
- border-color: var(--itf-table-input-focus-border-color);
438
- }
439
- }
439
+ //.form-control {
440
+ // border-radius: 0;
441
+ // border: 1px solid transparent;
442
+ // background-color: transparent;
443
+ // box-shadow: none;
444
+ //
445
+ // &:focus {
446
+ // border-color: var(--itf-table-input-focus-border-color);
447
+ // }
448
+ //}
440
449
  }
441
450
 
442
451
  &.highlight-drop-column {
@@ -1,26 +1,24 @@
1
1
  <template>
2
2
  <div class="itf-money-field ph-no-capture" :class="{'currency-arrow': !currencyDisabled, 'currency-select': currencySelect}">
3
- <div class="h-100" :class="{'input-group h-100': noCurrencySign}" :style="`--itf-money-field-padding-left: ${noCurrencySign ? 1 : selectedCurrencySymbol.length * 0.6 + 1}rem`">
4
- <span class="itf-money-field__prepend" v-if="!noCurrencySign">{{ selectedCurrencySymbol }}</span>
3
+ <div class="input-group h-100" :style="`--itf-money-field-padding-left: ${noCurrencySign ? 1 : selectedCurrencySymbol.length * 0.6 + 1}rem`">
4
+ <span class="input-group-text pe-1" v-if="!noCurrencySign">{{ selectedCurrencySymbol }}</span>
5
5
  <i-mask-component
6
6
  ref="input"
7
7
  v-bind="mask"
8
- class="form-control h-100"
8
+ class="form-control"
9
9
  :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }"
10
10
  @input="setValue"
11
11
  :value="maskedValue"
12
12
  :unmask="false"
13
13
  :disabled="disabled"
14
14
  />
15
- </div>
16
-
17
- <div v-if="currencySelect" class="itf-money-field__currency">
18
- <span>{{ selectedCurrencyCode }}<itf-icon v-if="!currencyDisabled && currenciesList.length > 1" name="chevron_down" /></span>
19
- <select v-if="!disabled && !currencyDisabled" :value="currencyId" @input="onCurrencyChanged">
15
+ <span v-if="currencySelect" class="input-group-text ps-1">{{ selectedCurrencyCode }}<itf-icon v-if="!currencyDisabled && currenciesList.length > 1" name="chevron_down" /></span>
16
+ <select v-if="currencySelect && !disabled && !currencyDisabled" :value="currencyId" @input="onCurrencyChanged">
20
17
  <option v-for="currency in currenciesList" :key="currency.Id" :value="currency.Id" :selected="currencyId === currency.Id">
21
18
  {{ currency.Symbol }}, {{ currency.Code }} - {{ currency.Title }}
22
19
  </option>
23
20
  </select>
21
+ <slot name="postfix"></slot>
24
22
  </div>
25
23
  </div>
26
24
  </template>
@@ -28,50 +26,9 @@
28
26
  .itf-money-field {
29
27
  position: relative;
30
28
 
31
- input {
32
- padding-left: var(--itf-money-field-padding-left, 1.5rem);
33
-
34
- &.is-invalid, &.is-valid {
35
- padding-right: 4rem;
36
- }
37
- }
38
- &.currency-select {
39
- input {
40
- padding-right: 3rem;
41
- }
42
- &.currency-arrow {
43
- input {
44
- padding-right: 4.5rem;
45
- }
46
- }
47
- }
48
-
49
- &__prepend {
50
- position: absolute;
51
- left: .75rem;
52
- top: .5rem;
53
- bottom: .5rem;
54
- display: flex;
55
- align-items: center;
56
- }
57
- &__currency {
58
- position: absolute;
59
- right: .75rem;
60
- top: 0;
61
- bottom: 0;
62
- display: flex;
63
- align-items: center;
64
- .is-invalid &, .is-valid & {
65
- right: 1.75rem;
66
- }
67
-
68
- select {
69
- position: absolute;
70
- height: 100%;
71
- right: 0;
72
- opacity: 0;
73
- left: 0;
74
- }
29
+ .form-control {
30
+ padding-left: 0;
31
+ padding-right: 0;
75
32
  }
76
33
  }
77
34
  </style>
@@ -1,17 +1,16 @@
1
1
  <template>
2
2
 
3
- <div v-loading="loading" class="flex-grow-1 w-100 d-flex flex-column">
4
- {{filter}}
3
+ <div v-loading="loading || loadingData" class="flex-grow-1 w-100 d-flex flex-column">
5
4
  <itf-filter-panel
6
5
  search-placeholder="Search by text"
7
6
  search
8
7
  class="py-2 px-3"
9
- :static-filters="filters"
8
+ :endpoint="filtersEndpoint"
10
9
  v-model="filter"
11
- @input="$emit('load')"
10
+ @input="loadData"
12
11
  >
13
12
  <template #after-filter-btn>
14
- <itf-dropdown v-if="$refs.table" shadow :button-options="{ default: true, icon: true }" autoclose="outside">
13
+ <itf-dropdown v-if="$refs.table && schema" shadow append-to-context :button-options="{ default: true, icon: true }" class="h-100" autoclose="outside">
15
14
  <template #button>
16
15
  <itf-icon new name="table-view" />
17
16
  </template>
@@ -21,7 +20,7 @@
21
20
  <a v-for="(property, n) of schema.properties" :key="n" href="" @click.stop.prevent="$refs.table.toggleVisibility(property.property)" class="dropdown-item justify-content-between d-flex gap-3">
22
21
  <div class="d-flex align-items-center gap-1">
23
22
  <itf-icon v-if="property.icon" :size="24" new :name="property.icon" />
24
- {{property.title.en_US}}
23
+ {{getTitle(property.title)}}
25
24
  </div>
26
25
 
27
26
  <itf-icon v-if="$refs.table.getColumnVisibility(property.property)" new :size="24" name="eye-open" />
@@ -47,10 +46,18 @@
47
46
  style="--shadow-area-width: 0px;"
48
47
  absolute
49
48
  striped
49
+ clickable
50
50
  column-sorting
51
51
  column-resizing
52
+ class="permanent-checkboxes"
53
+ id-property="id"
52
54
  :rows="items"
53
55
  :schema="schema"
56
+ :sorting="sorting"
57
+ :active="activeIds"
58
+ v-model="selectedIds"
59
+ @row-click="$emit('open', $event)"
60
+ @update:sorting="updateSorting($event)"
54
61
  >
55
62
  <template v-for="(_, name) in $slots" #[name]="slotData">
56
63
  <slot :name="name" v-bind="slotData || {}"/>
@@ -69,7 +76,8 @@
69
76
  :items="items"
70
77
  :pages="countPages"
71
78
  :value="page"
72
- @input="$emit('update:page', $event)"
79
+ @input="updatePage($event)"
80
+ @per-page="updateSizePerPage($event)"
73
81
  >
74
82
  <template #center>
75
83
  <slot name="pagination-center" />
@@ -79,14 +87,14 @@
79
87
 
80
88
  </template>
81
89
  <script>
82
- import { Vue, Component, Prop } from 'vue-property-decorator';
90
+ import { Vue, Component, Prop, Inject } from 'vue-property-decorator';
83
91
  import loading from '../../directives/loading';
84
92
  import itfTable from '../table/Table2.vue';
85
93
  import itfFilterPanel from '../filter/FilterPanel.vue';
86
94
  import itfPagination from '../pagination/Pagination2.vue';
87
- import itfTableBody from "@/components/table/TableBody.vue";
88
- import itfIcon from "@/components/icon/Icon.vue";
89
- import itfDropdown from "@/components/dropdown/Dropdown.vue";
95
+ import itfTableBody from "../table/TableBody.vue";
96
+ import itfIcon from "../icon/Icon.vue";
97
+ import itfDropdown from "../dropdown/Dropdown.vue";
90
98
 
91
99
  export default @Component({
92
100
  name: 'itfView',
@@ -102,18 +110,91 @@ export default @Component({
102
110
  }
103
111
  })
104
112
  class itfView extends Vue {
113
+ @Inject({ default: null }) panelList;
114
+
105
115
  @Prop({ type: Boolean }) loading;
106
- @Prop({ type: Array, required: true }) items;
107
116
  @Prop({ type: Array }) filters;
108
117
  @Prop({ type: Object, required: true }) schema;
109
- @Prop({ default: 20 }) size;
110
- @Prop({ default: 100 }) total;
111
- @Prop({ default: 1 }) page;
118
+ // @Prop({ default: 20 }) size;
119
+ // @Prop({ default: 1 }) page;
120
+ @Prop(String) defaultSorting;
121
+ @Prop(String) endpoint;
122
+ @Prop(String) filtersEndpoint;
123
+ @Prop(String) itemsKey;
124
+ @Prop(String) panelKey;
112
125
 
126
+ page = 1;
127
+ total = 0;
128
+ size = 20;
129
+ sorting = 'id';
130
+ items = [];
113
131
  filter = {};
132
+ loadingData = false;
133
+ activeIds = [];
134
+ selectedIds = [];
135
+
136
+ created() {
137
+ this.sorting = this.defaultSorting;
138
+ }
139
+
140
+ mounted() {
141
+ if (this.panelList && this.panelList.panelsStack && this.panelList.panelsStack.length && this.panelKey) {
142
+ const updateIds = () => {
143
+ this.activeIds = this.panelList.panelsStack.filter(p => p.type === this.panelKey).map(p => p.payload.id);
144
+ };
145
+ this.panelList.panelsStack[0].on('panels.changed', updateIds);
146
+ updateIds();
147
+ }
148
+ }
149
+
150
+ async loadData() {
151
+ if (!this.endpoint) {
152
+ return;
153
+ }
154
+ this.$emit('load', this.filter);
155
+ this.loadingData = true;
156
+ await this.$try(async () => {
157
+ const res = await this.$axios.$get(this.endpoint, {
158
+ params: {
159
+ ...this.filter,
160
+ page: this.page,
161
+ size: this.size,
162
+ sort: this.sorting
163
+ }
164
+ });
165
+ this.items = res[this.itemsKey];
166
+ this.page = res.meta.page;
167
+ this.total = res.meta.total;
168
+ this.size = res.meta.size;
169
+ });
170
+ this.loadingData = false;
171
+ this.$emit('loaded', this.filter);
172
+ }
173
+
174
+ getTitle(title) {
175
+ if (typeof title === 'string') {
176
+ return title;
177
+ }
178
+ return title[this.locale] || title['en_US'];
179
+ }
114
180
 
115
181
  get countPages() {
116
182
  return Math.ceil(this.total / this.size);
117
183
  }
184
+
185
+ updateSorting (val) {
186
+ this.sorting = val;
187
+ this.loadData();
188
+ }
189
+
190
+ updatePage (val) {
191
+ this.page = val;
192
+ this.loadData();
193
+ }
194
+
195
+ updateSizePerPage (val) {
196
+ this.size = val;
197
+ this.loadData();
198
+ }
118
199
  }
119
200
  </script>