@itfin/components 1.3.35 → 1.3.37

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.35",
3
+ "version": "1.3.37",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -8,5 +8,9 @@
8
8
  .page-link {
9
9
  border-radius: .5rem;
10
10
  }
11
+
12
+ &.disabled .page-link {
13
+ background-color: transparent;
14
+ }
11
15
  }
12
16
  }
@@ -1,6 +1,6 @@
1
1
 
2
2
  $bootstrap-icons-font: "bootstrap-icons" !default;
3
- $bootstrap-icons-font-dir: "../fonts" !default;
3
+ $bootstrap-icons-font-dir: "/fonts" !default;
4
4
  $bootstrap-icons-font-file: "#{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font}" !default;
5
5
  $bootstrap-icons-font-hash: "24e3eb84d0bcaf83d77f904c78ac1f47" !default;
6
6
  $bootstrap-icons-font-src: url("#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}") format("woff2"),
@@ -24,6 +24,7 @@ class itfDropdown extends Vue {
24
24
  @Prop({ type: Boolean }) disabled;
25
25
  @Prop({ type: Boolean }) text;
26
26
  @Prop({ type: Boolean }) appendToBody;
27
+ @Prop({ type: Boolean }) appendToContext;
27
28
  @Prop({ validator: (value) => [true, false, 'inside', 'outside'].includes(value), default: true }) autoclose;
28
29
  @Prop({ type: Object, default: () => ({}) }) buttonOptions;
29
30
  @Prop({ type: Object, default: () => ({}) }) menuOptions;
@@ -31,7 +32,7 @@ class itfDropdown extends Vue {
31
32
  modalId = '';
32
33
 
33
34
  render (createElement, context) {
34
- const { props, slots, data } = context;
35
+ const { props, slots, data, listeners } = context;
35
36
  const modalId = `dropdownId${globalModalIndex++}`;
36
37
  const { buttonOptions, toggle, text, disabled, label, appendToBody } = props;
37
38
  const { button, default: defaultSlot } = slots();
@@ -51,6 +52,18 @@ class itfDropdown extends Vue {
51
52
  {
52
53
  ref: data.ref,
53
54
  props: { ...props, toggleId: modalId },
55
+ on: {
56
+ open: () => {
57
+ if (listeners.open) {
58
+ listeners.open();
59
+ }
60
+ },
61
+ close: () => {
62
+ if (listeners.close) {
63
+ listeners.close();
64
+ }
65
+ }
66
+ }
54
67
  },
55
68
  defaultSlot
56
69
  )
@@ -22,6 +22,7 @@ class itfDropdownMenu extends Vue {
22
22
  @Prop({ type: Boolean }) disabled;
23
23
  @Prop({ type: Boolean }) text;
24
24
  @Prop({ type: Boolean }) appendToBody;
25
+ @Prop({ type: Boolean }) appendToContext;
25
26
  @Prop({ type: String }) toggleId;
26
27
  @Prop({ validator: (value) => [true, false, 'inside', 'outside'].includes(value), default: true }) autoclose;
27
28
  @Prop({ type: Object, default: () => ({}) }) buttonOptions;
@@ -39,32 +40,39 @@ class itfDropdownMenu extends Vue {
39
40
  const { default: Dropdown } = await import('bootstrap/js/src/dropdown.js');
40
41
  let context = document.body;
41
42
  this.modalEl = new Dropdown(toggle, {
42
- reference: 'toggle',
43
+ reference: 'parent',
43
44
  autoClose: this.autoclose
44
45
  });
45
- if (this.appendToBody && this.$el instanceof Node && this.$el.parentNode) {
46
+ if (this.appendToContext && this.$el instanceof Node && this.$el.parentNode) {
47
+ context = this.$el.closest('.itf-append-context') || document.body;
48
+ this.$el.parentNode.removeChild(this.$el);
49
+ context.appendChild(this.$el); // should append only to body
50
+ } else if (this.appendToBody && this.$el instanceof Node && this.$el.parentNode) {
46
51
  this.$el.parentNode.removeChild(this.$el);
47
52
  context.appendChild(this.$el); // should append only to body
48
53
  }
49
54
 
50
- this.$el.addEventListener('shown.bs.dropdown', () => {
51
- setTimeout(() => {
52
- this.$emit('open');
53
- }, 500);
55
+ toggle.addEventListener('shown.bs.dropdown', () => {
56
+ this.$emit('open');
54
57
  });
55
- this.$el.addEventListener('hidden.bs.dropdown', () => {
58
+ toggle.addEventListener('hidden.bs.dropdown', () => {
56
59
  this.$emit('close');
57
60
  });
58
61
  }
59
62
 
60
63
  beforeDestroy() {
61
- if (this.modalEl) {
62
- this.modalEl.hide();
63
- this.modalEl.dispose();
64
+ try {
65
+ if (this.modalEl) {
66
+ this.modalEl.hide();
67
+ this.modalEl.dispose();
68
+ }
69
+ } catch (err) {
70
+ // ignore
64
71
  }
65
72
  }
66
73
 
67
74
  show() {
75
+ console.info('p[en')
68
76
  if (this.modalEl) {
69
77
  this.modalEl.show();
70
78
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
 
3
3
  <nav aria-label="Page navigation example">
4
- <ul class="pagination itf-pagination">
4
+ <ul class="pagination itf-pagination px-3">
5
5
  <li
6
6
  v-for="(page, n) in pagesArr"
7
7
  :key="n"
@@ -6,6 +6,7 @@
6
6
  <div class="table-view-body">
7
7
  <itf-table-group
8
8
  :key="index"
9
+ @update="$emit('update', { ...$event, group, groupIndex: index })"
9
10
  :id-property="idProperty"
10
11
  :columns="columns"
11
12
  @update:columns="onColumnsUpdate"
@@ -20,6 +21,8 @@
20
21
  :show-add-column="showAddColumn"
21
22
  :show-header="!noHeader"
22
23
  :schema="schema"
24
+ :no-column-menu="noColumnMenu"
25
+ :no-select-all="noSelectAll"
23
26
  @new="$emit('new', $event)"
24
27
  @add-column="$emit('add-column', $event)"
25
28
  >
@@ -40,7 +43,6 @@
40
43
  .scrollable {
41
44
  --itf-table-hover-bg: #f2f2f2;
42
45
  --itf-table-min-width: 45px;
43
- --itf-table-line-height: 35px;
44
46
 
45
47
  body[data-theme="dark"] & {
46
48
  --itf-table-hover-bg: #393b41;
@@ -103,6 +105,8 @@ class itfTable2 extends Vue {
103
105
  @Prop(Boolean) showGrouping;
104
106
  @Prop(Boolean) showSummary;
105
107
  @Prop(Boolean) noHeader;
108
+ @Prop(Boolean) noColumnMenu;
109
+ @Prop(Boolean) noSelectAll;
106
110
 
107
111
  state = {
108
112
  selectedIds: [],
@@ -123,7 +127,10 @@ class itfTable2 extends Vue {
123
127
  for (const column of list) {
124
128
  const stateColumn = state.columns.find(i => i.property === column.property);
125
129
  if (stateColumn) {
126
- Object.assign(stateColumn, column);
130
+ Object.assign(stateColumn, {
131
+ ...column,
132
+ width: stateColumn.width
133
+ });
127
134
  } else {
128
135
  state.columns.push(column);
129
136
  }
@@ -18,30 +18,39 @@
18
18
  </div>
19
19
  </div>
20
20
  <div class="indicator sticky">
21
- <div class="fill on-rest table-view-row-count">
22
- <!-- <span>{{ item[idProperty] }}</span>-->
21
+ <div class="fill table-view-row-count" :class="{'on-rest': !noSelectAll}">
22
+ <span>{{ item[idProperty] }}</span>
23
23
  </div>
24
- <div class="fill on-hover">
24
+ <div v-if="!noSelectAll" class="fill on-hover">
25
25
  <itf-checkbox :value="item[idProperty]" />
26
26
  </div>
27
27
  </div>
28
28
  <div accept-group="items" class="table-item-inner">
29
- <template v-for="(column, n) in visibleAttributes">
29
+ <template v-for="(column, k) in visibleAttributes">
30
30
  <div
31
31
  v-if="column.visible !== false"
32
- :data-column="n"
33
- :style="`width: ${column.width}px; left: ${column.left}px;`"
34
- :class="{'sticky': column.pinned, 'last-sticky-column': n === lastPinnedIndex, 'flex-grow-1': column.grow}"
32
+ :data-column="k"
33
+ :style="`width: ${column.width}px; max-width: ${column.width}px; left: ${column.left}px;`"
34
+ :class="{'sticky': column.pinned, 'last-sticky-column': k === lastPinnedIndex, 'flex-grow-1': column.grow, 'px-2': !column.editable, 'p-1': column.editable}"
35
35
  class="table-view-item-value d-flex h-100 align-items-stretch">
36
- <slot :name="`column.${column.name}`" :item="item" :column="column">
36
+ <slot :name="`column.${column.property}`" :item="item" :column="column">
37
37
  <template v-if="column.editable">
38
- <slot :name="`edit.${column.type}`" :value="getValue(item, column)" :item="item" :column="column">
39
- <div class="px-1 py-1 w-100"><itf-text-field :value="getValue(item, column)" /></div>
38
+ <slot :name="`edit.${column.type}`" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)" :item="item" :column="column">
39
+ <itf-text-field class="w-100" v-if="column.type === 'text'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
40
+ <itf-hours-field
41
+ class="w-100"
42
+ placeholder="0h 0m"
43
+ v-else-if="column.type === 'time'"
44
+ :hours="getValue(item, column)"
45
+ @update:hours="updateValue(item, $event, n, column)"
46
+ />
47
+ <itf-textarea class="w-100" :rows="2" autogrow v-else-if="column.type === 'textarea'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
48
+ <itf-money-field class="w-100" v-else-if="column.type === 'money'" no-currency-sign currency-disabled :currency-select="false" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
40
49
  </slot>
41
50
  </template>
42
51
  <template v-else>
43
52
  <slot :name="`format.${column.type}`" :value="getValue(item, column)" :item="item" :column="column">
44
- <div class="px-2 w-100">{{getValue(item, column)}}</div>
53
+ {{getValue(item, column)}}
45
54
  </slot>
46
55
  </template>
47
56
  </slot>
@@ -55,6 +64,19 @@
55
64
  </div>
56
65
  </div>
57
66
  </div>
67
+
68
+ <div v-if="!rows.length" data-test="table-no-results" class="table-view-item">
69
+ <div class="table-row-template">
70
+ <div accept-group="items" class="table-view-body-space"></div>
71
+ <div class="shadow-area"></div>
72
+ <div class="indicator sticky"></div>
73
+ <div class="table-item-inner">
74
+ <div class="table-view-item-value w-100 align-items-center p-3">
75
+ {{$t('components.table.noResults')}}
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
58
80
  </div>
59
81
  </template>
60
82
  <style lang="scss">
@@ -78,7 +100,6 @@
78
100
  align-items: stretch;
79
101
  display: flex;
80
102
  position: relative;
81
- line-height: var(--itf-table-line-height);
82
103
  min-width: var(--itf-table-min-width);
83
104
 
84
105
  &.highlight-drop-column {
@@ -208,18 +229,24 @@
208
229
  <script>
209
230
  import { Vue, Component, Prop } from 'vue-property-decorator';
210
231
  import get from 'lodash/get';
211
- import { RecycleScroller } from 'vue-virtual-scroller'
232
+ import set from 'lodash/set';
233
+ // import { RecycleScroller } from 'vue-virtual-scroller'
212
234
  import itfCheckbox from '../checkbox/Checkbox.vue';
213
235
  import itfTextField from '../text-field/TextField.vue';
214
- import { INLINE_TYPES } from '../customize/constants';
215
- import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
236
+ import itfMoneyField from '../text-field/MoneyField.vue';
237
+ import itfTextarea from '../text-field/Textarea.vue';
238
+ import itfHoursField from '../text-field/HoursField.vue';
239
+ // import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
216
240
 
217
241
  export default @Component({
218
242
  name: 'itfTableBody',
219
243
  components: {
220
244
  itfCheckbox,
245
+ itfMoneyField,
246
+ itfHoursField,
247
+ itfTextarea,
221
248
  itfTextField,
222
- RecycleScroller
249
+ // RecycleScroller
223
250
  }
224
251
  })
225
252
  class itfTableBody extends Vue {
@@ -227,8 +254,7 @@ class itfTableBody extends Vue {
227
254
  @Prop() rows;
228
255
  @Prop() idProperty;
229
256
  @Prop(Boolean) showAddColumn;
230
-
231
- editTypes = {};
257
+ @Prop(Boolean) noSelectAll;
232
258
 
233
259
  getValue(item, column) {
234
260
  return get(item, column.property);
@@ -242,10 +268,10 @@ class itfTableBody extends Vue {
242
268
  return this.columns.findIndex((column) => column.lastPinned);
243
269
  }
244
270
 
245
- mounted() {
246
- for (const { Type } of INLINE_TYPES) {
247
- this.editTypes[Type] = true;
248
- }
271
+ updateValue(item, value, index, column) {
272
+ const newItem = { ...item };
273
+ set(newItem, column.property, value);
274
+ this.$emit('update', { index, item, value: newItem });
249
275
  }
250
276
  }
251
277
  </script>
@@ -42,6 +42,8 @@
42
42
  :show-add-column="showAddColumn"
43
43
  :rows="rows"
44
44
  :schema="schema"
45
+ :no-column-menu="noColumnMenu"
46
+ :no-select-all="noSelectAll"
45
47
  :selected-ids="selectedIds"
46
48
  @update:selectedIds="$emit('update:selectedIds', $event)"
47
49
  @update:columns="$emit('update:columns', $event)"
@@ -52,9 +54,11 @@
52
54
  <!-- Сама таблиця -->
53
55
  <div v-if="isShowTable">
54
56
  <itf-table-body
57
+ @update="$emit('update', $event)"
55
58
  :id-property="idProperty"
56
59
  :rows="rows"
57
60
  :columns="visibleColumns"
61
+ :no-select-all="noSelectAll"
58
62
  :show-add-column="showAddColumn">
59
63
  <template v-for="(_, name) in $slots" #[name]="slotData">
60
64
  <slot :name="name" v-bind="slotData || {}"/>
@@ -79,7 +83,7 @@
79
83
  </div>
80
84
 
81
85
  <!-- Групування -->
82
- <div v-if="isShowTable && showSummary" class="table-row-template d-flex align-items-stretch table-summary">
86
+ <div v-if="isShowTable && showSummary" class="table-row-template d-flex align-items-stretch table-summary" :class="{'table-summary-persist': persistSummary}">
83
87
  <div class="shadow-area"></div>
84
88
 
85
89
  <div class="table-summary-columns d-flex tw-flex-row align-items-center">
@@ -87,51 +91,27 @@
87
91
  v-for="(column, n) in visibleColumns"
88
92
  :key="n"
89
93
  :data-column="n"
90
- class="table-summary-column position-relative line-overflow"
91
- :style="`width: ${column.width}px;`">
92
- <itf-dropdown text>
94
+ class="position-relative line-overflow"
95
+ :style="`width: ${column.width}px; max-width: ${column.width}px;`">
96
+ <itf-dropdown append-to-context text right @open="persistSummary = true" @close="persistSummary = false">
93
97
  <template #button>
94
98
  <span data-test="summary-column" class="invisible-summary d-flex align-items-center justify-content-end flex-auto">
95
- <span class="summary-placeholder align-items-center justify-content-center">
96
- Summary
99
+ <span v-if="column.calculate === 'none' || !column.calculate" class="table-summary-column summary-placeholder align-items-center justify-content-center">
100
+ {{$t('components.table.calculate')}}
97
101
  <itf-icon name="chevron_down" />
98
102
  </span>
103
+ <span v-else>
104
+ <span class="summary-text text-uppercase pe-2">{{getCalculateMethodTitle(column.calculate)}}</span>
105
+ <span>{{getCalculateMethodValue(column.calculate, column)}}</span>
106
+ </span>
99
107
  </span>
100
108
  </template>
101
109
 
102
- <ul class="dropdown-menu show ps-0" aria-labelledby="dropdownMenuOffset">
103
- <li v-if="column.sortable">
104
- <a class="dropdown-item d-flex align-items-center" href="javascript:;">
105
- <itf-icon name="arrow_up" :size="16" class="me-1" />
106
- Sort By Asc
107
- </a>
108
- </li>
109
- <li v-if="column.sortable">
110
- <a class="dropdown-item d-flex align-items-center" href="javascript:;">
111
- <itf-icon name="arrow_down" :size="16" class="me-1" />
112
- Sort By Desc
113
- </a>
114
- </li>
115
- <li v-if="column.groupable">
116
- <a class="dropdown-item d-flex align-items-center" href="javascript:;">
117
- <itf-icon name="episodes" :size="16" class="me-1" />
118
- Group By
119
- </a>
120
- </li>
121
- <li>
122
- <a class="dropdown-item d-flex align-items-center" href="javascript:;" @click="togglePinned(n)">
123
- <itf-icon :name="column.pinned ? 'checkbox_checked' : 'checkbox_empty'" :size="16" class="me-1" />
124
- <span v-if="column.pinned">Unpin Column</span>
125
- <span v-else>Pin Column</span>
126
- </a>
127
- </li>
128
- <li>
129
- <a class="dropdown-item d-flex align-items-center" href="javascript:;" @click="hideColumn(n)">
130
- <itf-icon name="eye_no" :size="16" class="me-1" />
131
- Hide
132
- </a>
133
- </li>
134
- </ul>
110
+ <div v-for="(method, m) in calculateMethods" :key="method.id">
111
+ <a class="dropdown-item d-flex align-items-center" href="javascript:;" @click="selectSummary(method, n)">
112
+ {{method.title}}
113
+ </a>
114
+ </div>
135
115
  </itf-dropdown>
136
116
  </span>
137
117
  </div>
@@ -289,23 +269,34 @@
289
269
  opacity: 0.05;
290
270
  font-size: .85rem;
291
271
  padding-right: .25rem;
292
-
293
- &:hover {
294
- opacity: 1;
295
- }
272
+ }
273
+ .summary-text {
274
+ color: var(--bs-tertiary-color);
275
+ }
276
+ &.table-summary-persist .table-summary-column,
277
+ &:hover .table-summary-column {
278
+ opacity: 1;
296
279
  }
297
280
  }
298
281
  }
299
282
  </style>
300
283
  <script>
284
+ import get from 'lodash/get';
301
285
  import sortBy from 'lodash/sortBy';
286
+ import uniq from "lodash/uniq";
287
+ import round from "lodash/round";
302
288
  import {Vue, Component, Prop, Watch} from 'vue-property-decorator';
303
289
  import itfDropdown from '../dropdown/Dropdown.vue';
304
290
  import itfButton from '../button/Button.vue';
305
291
  import itfIcon from '../icon/Icon.vue';
306
292
  import itfTableBody from './TableBody.vue';
307
293
  import itfTableHeader from './TableHeader.vue';
308
- import Sticky from "@/components/table/sticky";
294
+ import Sticky from "./sticky";
295
+
296
+ function getNumber(item, prop) {
297
+ const num = Number(get(item, prop));
298
+ return Number.isNaN(num) ? 0 : num;
299
+ }
309
300
 
310
301
  export default @Component({
311
302
  name: 'itfTableGroup',
@@ -330,10 +321,13 @@ class itfTableGroup extends Vue {
330
321
  @Prop(Boolean) columnResizing;
331
322
  @Prop(Boolean) showAddColumn;
332
323
  @Prop(Boolean) showHeader;
324
+ @Prop(Boolean) noColumnMenu;
325
+ @Prop(Boolean) noSelectAll;
333
326
  @Prop({type: String, default: function() { return this.$t('components.new'); } }) newLabel;
334
327
  @Prop({type: Object, default: () => ({})}) schema;
335
328
 
336
329
  isShowTable = true;
330
+ persistSummary = false;
337
331
 
338
332
  get visibleColumns() {
339
333
  let list = this.columns;
@@ -368,7 +362,47 @@ class itfTableGroup extends Vue {
368
362
  wrap: true,
369
363
  stickyClass: 'sticky',
370
364
  });
371
- console.info(this.sticky);
365
+ }
366
+
367
+ selectSummary(method, index) {
368
+ const columns = [...this.columns];
369
+ columns[index].calculate = method.id;
370
+ this.$emit('update:columns', columns);
371
+ }
372
+
373
+ get calculateMethods() {
374
+ return [
375
+ { id: 'none', title: this.$t('components.table.calculateNone') },
376
+ { id: 'total', title: this.$t('components.table.calculateTotal'), summary: 'Total', func: (rows, column) => rows.reduce((acc, row) => acc + (getNumber(row, column.property)), 0) },
377
+ { id: 'average', title: this.$t('components.table.calculateAverage'), summary: 'Average', func: (rows, column) => {
378
+ const sum = rows.reduce((acc, row) => acc + (getNumber(row, column.property)), 0);
379
+ return round(sum / rows.length, 3);
380
+ } },
381
+ { id: 'min', title: this.$t('components.table.calculateMin'), summary: 'Min', func: (rows, column) => Math.min(...rows.map(row => getNumber(row, column.property))) },
382
+ { id: 'max', title: this.$t('components.table.calculateMax'), summary: 'Max', func: (rows, column) => Math.max(...rows.map(row => getNumber(row, column.property))) },
383
+ { id: 'countAll', title: this.$t('components.table.calculateCountAll'), summary: 'Count', func: (rows) => rows.length },
384
+ { id: 'countValues', title: this.$t('components.table.calculateCountValues'), summary: 'Values', func: (rows, column) => rows.filter(row => !!get(row, column.property)).length },
385
+ { id: 'countUniqueValues', title: this.$t('components.table.calculateCountUniqueValues'), summary: 'Unique', func: (rows, column) => uniq(rows.filter(row => !!get(row, column.property))).length },
386
+ { id: 'countEmpty', title: this.$t('components.table.calculateCountEmpty'), summary: 'Empty', func: (rows, column) => rows.filter(row => !get(row, column.property)).length },
387
+ { id: 'countNotEmpty', title: this.$t('components.table.calculateCountNotEmpty'), summary: 'Not Empty', func: (rows, column) => rows.filter(row => !!get(row, column.property)).length },
388
+ { id: 'percentEmpty', title: this.$t('components.table.calculatePercentEmpty'), summary: 'Empty', func: (rows, column) => {
389
+ const empty = rows.filter(row => !get(row, column.property)).length;
390
+ return round(empty / rows.length * 100, 3) + '%';
391
+ } },
392
+ { id: 'percentNotEmpty', title: this.$t('components.table.calculatePercentNotEmpty'), summary: 'Not Empty', func: (rows, column) => {
393
+ const notEmpty = rows.filter(row => !!get(row, column.property)).length;
394
+ return round(notEmpty / rows.length * 100, 3) + '%';
395
+ } },
396
+ ];
397
+ }
398
+
399
+ getCalculateMethodTitle(methodName) {
400
+ return this.calculateMethods.find(method => method.id === methodName)?.summary;
401
+ }
402
+
403
+ getCalculateMethodValue(methodName, column) {
404
+ const method = this.calculateMethods.find(method => method.id === methodName);
405
+ return method?.func ? method.func(this.rows, column) : '';
372
406
  }
373
407
  }
374
408
  </script>
@@ -5,7 +5,7 @@
5
5
  <div accept-group="items" class="table-view-body-space" v-dropzone="{ payload: 0 }"></div>
6
6
  <div class="shadow-area"></div>
7
7
  <div class="table-view-header-value reserved sticky">
8
- <itf-checkbox v-if="visibleHeader" ungrouped value="all" v-model="selectAll" ref="selectAll" />
8
+ <itf-checkbox v-if="visibleHeader && !noSelectAll" ungrouped value="all" v-model="selectAll" ref="selectAll" />
9
9
  </div>
10
10
 
11
11
  <template v-for="(column, n) in visibleAttributes">
@@ -17,7 +17,7 @@
17
17
  :data-id="column.Id"
18
18
  :class="{'sticky': column.pinned, 'last-sticky-column': n === lastPinnedIndex, 'flex-grow-1': column.grow, [`justify-content-${column.align || 'start'}`]: true}"
19
19
  class="table-view-header-value"
20
- :style="`width: ${column.width}px; left: ${column.left}px;`">
20
+ :style="`width: ${column.width}px; max-width: ${column.width}px; left: ${column.left}px;`">
21
21
  <!-- Не треба видаляти колонки, бо вони потрібні для збереження ширини -->
22
22
  <div v-if="visibleHeader" accept-group="tablecolumns"
23
23
  class="table-view-header-space"
@@ -31,7 +31,7 @@
31
31
  @drop="reorderColumns"
32
32
  v-drag-handle
33
33
  v-draggable="{ handle: true, payload: { index: n, item: column }, mirror: {yAxis:false} }">
34
- <itf-dropdown text append-to-body shadow ref="dropdown" class="w-100">
34
+ <itf-dropdown text append-to-body shadow ref="dropdown" class="w-100" :disabled="noColumnMenu">
35
35
  <template #button>
36
36
  <span :title="column.title[locale] || column.title['en_US']">
37
37
  <span v-if="column.icon" :class="column.icon"></span>
@@ -43,32 +43,32 @@
43
43
  <div v-if="column.sortable">
44
44
  <a class="dropdown-item d-flex align-items-center" href="javascript:;">
45
45
  <itf-icon name="arrow_up" :size="16" class="me-1" />
46
- Sort ascending
46
+ {{$t('components.table.sortAscending')}}
47
47
  </a>
48
48
  </div>
49
49
  <div v-if="column.sortable">
50
50
  <a class="dropdown-item d-flex align-items-center" href="javascript:;">
51
51
  <itf-icon name="arrow_down" :size="16" class="me-1" />
52
- Sort descending
52
+ {{$t('components.table.sortAscending')}}
53
53
  </a>
54
54
  </div>
55
55
  <div v-if="column.groupable">
56
56
  <a class="dropdown-item d-flex align-items-center" href="javascript:;">
57
57
  <itf-icon name="episodes" :size="16" class="me-1" />
58
- Group By
58
+ {{$t('components.table.groupBy')}}
59
59
  </a>
60
60
  </div>
61
61
  <div>
62
62
  <a class="dropdown-item d-flex align-items-center" href="javascript:;" @click="togglePinned(n)">
63
63
  <itf-icon :name="column.pinned ? 'checkbox_checked' : 'checkbox_empty'" :size="16" class="me-1" />
64
- <span v-if="column.pinned">Unfreeze column</span>
65
- <span v-else>Freeze up to column</span>
64
+ <span v-if="column.pinned">{{$t('components.table.unfreezeColumn')}}</span>
65
+ <span v-else>{{$t('components.table.freezeColumn')}}</span>
66
66
  </a>
67
67
  </div>
68
- <div>
68
+ <div v-if="showAddColumn">
69
69
  <a class="dropdown-item d-flex align-items-center" href="javascript:;" @click="hideColumn(n)">
70
70
  <itf-icon name="eye_no" :size="16" class="me-1" />
71
- Hide in view
71
+ {{$t('components.table.hideColumn')}}
72
72
  </a>
73
73
  </div>
74
74
  </itf-dropdown>
@@ -85,14 +85,14 @@
85
85
  </div>
86
86
  </template>
87
87
  <div v-if="showAddColumn" class="table-view-header-value flex-grow-1 justify-content-start">
88
- <itf-dropdown v-if="visibleHeader" ref="newDd" text append-to-body shadow autoclose="outside" class="table-header table-header-add-column" data-test="table-header-add-column">
88
+ <itf-dropdown v-if="visibleHeader" ref="newDd" text append-to-context shadow autoclose="outside" class="table-header table-header-add-column" data-test="table-header-add-column">
89
89
  <template #button>
90
90
  <span class="nom-layout-three-columns"></span>
91
91
  </template>
92
92
 
93
93
  <itf-sortable :value="sortedColumns" @input="onSortColumns">
94
94
  <template v-for="(column, k) in sortedColumns">
95
- <div :key="`column${k}`" class="d-flex align-items-center justify-content-between px-2 py-1">
95
+ <div :key="`column${k}`" class="d-flex align-items-center justify-content-between" :class="{'px-2 py-1': column.visible !== false}">
96
96
  <template v-if="column.visible !== false">
97
97
  <div class="d-flex justify-content-between flex-grow-1">
98
98
  <div>
@@ -111,7 +111,7 @@
111
111
  </itf-sortable>
112
112
 
113
113
  <div v-if="invisibleColumns.length">
114
- <div class="dropdown-header">Add column</div>
114
+ <div class="dropdown-header">{{$t('components.table.addColumn')}}</div>
115
115
  <template v-for="(column, k) in invisibleColumns">
116
116
  <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">
117
117
  <div>
@@ -321,6 +321,8 @@ class itfTableHeader extends Vue {
321
321
  @Prop(Boolean) columnResizing;
322
322
  @Prop(Boolean) showAddColumn;
323
323
  @Prop(Boolean) visibleHeader;
324
+ @Prop(Boolean) noColumnMenu;
325
+ @Prop(Boolean) noSelectAll;
324
326
 
325
327
  @Watch('selectedIds')
326
328
  @Watch('rows')
@@ -433,6 +435,7 @@ class itfTableHeader extends Vue {
433
435
  newWidth = Math.max(columnWidth + delta, 100);
434
436
  columns.forEach((column) => {
435
437
  column.style.width = `${newWidth}px`;
438
+ column.style['max-width'] = `${newWidth}px`;
436
439
  });
437
440
  };
438
441
  const mouseUpHandler = () => {
@@ -131,7 +131,7 @@ storiesOf('Common', module)
131
131
  {
132
132
  "property": "Description",
133
133
  "title": { "en_US": "Description", "uk_UA": "Прізвище" },
134
- "type": "text",
134
+ "type": "textarea",
135
135
  "editable": true,
136
136
  "sortable": false,
137
137
  width: 200,
@@ -324,7 +324,9 @@ storiesOf('Common', module)
324
324
  @new="onAdd"
325
325
  state-name="test"
326
326
  :schema="schema"
327
- :columns.sync="columns2" :rows="list2" column-sorting column-resizing show-add-column show-grouping @add-column="onAdd">
327
+ :columns.sync="columns2" :rows="list2"
328
+ show-summary
329
+ column-sorting column-resizing show-add-column show-grouping @add-column="onAdd">
328
330
  <template #format.employee="{ item }">
329
331
  {{item.EmployeeId}}
330
332
  </template>
@@ -1,16 +1,16 @@
1
1
  <template>
2
2
  <div class="itf-money-field" :class="{'currency-arrow': !currencyDisabled, 'currency-select': currencySelect}">
3
- <div :style="`--itf-money-field-padding-left: ${selectedCurrencySymbol.length * 0.6 + 1}rem`">
4
- <span class="itf-money-field__prepend">{{ 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="itf-money-field__prepend" v-if="!noCurrencySign">{{ selectedCurrencySymbol }}</span>
5
5
  <i-mask-component
6
- ref="input"
7
- v-bind="mask"
8
- class="form-control"
9
- :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }"
10
- @input="setValue"
11
- :value="maskedValue"
12
- :unmask="false"
13
- :disabled="disabled"
6
+ ref="input"
7
+ v-bind="mask"
8
+ class="form-control"
9
+ :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }"
10
+ @input="setValue"
11
+ :value="maskedValue"
12
+ :unmask="false"
13
+ :disabled="disabled"
14
14
  />
15
15
  </div>
16
16
 
@@ -93,6 +93,7 @@ class itfMoneyField extends Vue {
93
93
 
94
94
  @Model('input', { default: '' }) value;
95
95
  @Prop({ type: Object, default: null }) currency;
96
+ @Prop({ type: Boolean, default: false }) noCurrencySign;
96
97
  @Prop({ type: Boolean, default: false }) disabled;
97
98
  @Prop({ type: Boolean, default: true }) currencySelect;
98
99
  @Prop({ type: Array, default: () => ([]) }) currencies;
package/src/locales/en.js CHANGED
@@ -96,5 +96,29 @@ module.exports = {
96
96
  },
97
97
  copyToClipboard: {
98
98
  copyingToClipboardWasSuccessful: 'Copying to clipboard was successful',
99
+ },
100
+ table: {
101
+ new: 'New',
102
+ noResults: 'No items',
103
+ sortAscending: 'Sort ascending',
104
+ sortDescending: 'Sort descending',
105
+ groupBy: 'Group by',
106
+ hideColumn: 'Hide in view',
107
+ freezeColumn: 'Freeze up to column',
108
+ unfreezeColumn: 'Unfreeze column',
109
+ addColumn: 'Add column',
110
+ calculate: 'Calculate',
111
+ calculateNone: 'None',
112
+ calculateTotal: 'Total',
113
+ calculateCountAll: 'Count all',
114
+ calculateAverage: 'Average',
115
+ calculateMin: 'Min',
116
+ calculateMax: 'Max',
117
+ calculateCountValues: 'Count values',
118
+ calculateCountUniqueValues: 'Count unique values',
119
+ calculateCountEmpty: 'Count empty',
120
+ calculateCountNotEmpty: 'Count not empty',
121
+ calculatePercentEmpty: 'Percent empty',
122
+ calculatePercentNotEmpty: 'Percent not empty',
99
123
  }
100
124
  };
package/src/locales/uk.js CHANGED
@@ -96,5 +96,29 @@ module.exports = {
96
96
  },
97
97
  copyToClipboard: {
98
98
  copyingToClipboardWasSuccessful: 'Скопійовано в буфер',
99
+ },
100
+ table: {
101
+ new: 'Додати',
102
+ noResults: 'Немає записів',
103
+ sortAscending: 'Сортувати за зростанням',
104
+ sortDescending: 'Сортувати за спаданням',
105
+ groupBy: 'Групувати',
106
+ hideColumn: 'Приховати колонку',
107
+ freezeColumn: 'Закріпити колонку',
108
+ unfreezeColumn: 'Відкріпити колонку',
109
+ addColumn: 'Додати колонку',
110
+ calculate: 'Обчислення',
111
+ calculateNone: 'Немає',
112
+ calculateTotal: 'Всього',
113
+ calculateCountAll: 'Кількість',
114
+ calculateAverage: 'Середнє',
115
+ calculateMin: 'Мінімум',
116
+ calculateMax: 'Максимум',
117
+ calculateCountValues: 'Кількість значень',
118
+ calculateCountUniqueValues: 'Кількість унікальних значень',
119
+ calculateCountEmpty: 'Кількість порожніх',
120
+ calculateCountNotEmpty: 'Кількість не порожніх',
121
+ calculatePercentEmpty: 'Відсоток порожніх',
122
+ calculatePercentNotEmpty: 'Відсоток не порожніх',
99
123
  }
100
124
  };
@@ -1,30 +0,0 @@
1
- <div class="notion-overlay-container notion-default-overlay-container"
2
- style="position: fixed; inset: 0px; z-index: 999; pointer-events: none; overflow: hidden;">
3
- <div style="position: relative; z-index: 0;"></div>
4
- <div data-overlay="true" style="pointer-events: auto; position: relative; z-index: 0;">
5
- <div>
6
- <div style="position: fixed; top: 0px; left: 0px; width: 100vw; height: 100vh;"></div>
7
- <div style="position: fixed; left: 347px; top: 463.398px; pointer-events: none;">
8
- <div style="width: 280px; height: 0px;"></div>
9
- <div style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start;">
10
- <div style="position: relative; top: 100%; pointer-events: auto;">
11
- <div style="display: flex; align-items: center; position: relative; flex-direction: column-reverse; transform-origin: 0% top; left: 0px; top: 0px;">
12
- <div role="dialog"
13
- style="border-radius: 6px; background: white; backdrop-filter: none; position: relative; max-width: calc(-24px + 100vw); box-shadow: rgba(15, 15, 15, 0.05) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 3px 6px, rgba(15, 15, 15, 0.2) 0px 9px 24px; overflow: visible; width: 280px; min-height: 34px; max-height: 634px; display: flex; flex-direction: column;">
14
- <div style="display: flex; flex-direction: column; overflow-y: auto; flex-grow: 1; height: 100%;">
15
- <div style="padding: 6px 9px; font-size: 14px; min-height: 34px; display: flex; height: 100%; flex-direction: column; justify-content: space-between; flex-grow: 1; font-weight: 500;">
16
- <div class="notranslate" spellcheck="true" placeholder=" "
17
- data-content-editable-leaf="true" contenteditable="true"
18
- style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-color: rgb(55, 53, 47);">
19
- d
20
- </div>
21
- </div>
22
- </div>
23
- </div>
24
- </div>
25
- </div>
26
- </div>
27
- </div>
28
- </div>
29
- </div>
30
- </div>