@itfin/components 1.3.39 → 1.3.41

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.39",
3
+ "version": "1.3.41",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -10,6 +10,7 @@ class ITFSettings {
10
10
  defaultLocale: 'en_US',
11
11
  defaultDisplayDateFormat: 'MM/dd/yyyy',
12
12
  // defaultDisplayDateFormat: 'dd.MM.yyyy',
13
+ currencies: []
13
14
  };
14
15
 
15
16
  get defaultLocale() {
@@ -20,6 +21,14 @@ class ITFSettings {
20
21
  this.options.defaultLocale = val;
21
22
  }
22
23
 
24
+ get currencies() {
25
+ return this.options.currencies;
26
+ }
27
+
28
+ set currencies(val) {
29
+ this.options.currencies = val;
30
+ }
31
+
23
32
  get supportLanguages() {
24
33
  return [
25
34
  { locale: 'en_US', name: 'English', dateFormat: 'MM/dd/yyyy' },
@@ -2,7 +2,10 @@
2
2
 
3
3
  .itf-text-field {
4
4
  &__input {
5
- border-radius: $input-border-radius !important;
5
+ .addon + & {
6
+ border-top-left-radius: $input-border-radius !important;
7
+ border-bottom-left-radius: $input-border-radius !important;
8
+ }
6
9
  -moz-appearance: textfield; // Hide Arrows From Input Number (Firefox)
7
10
 
8
11
  &::-webkit-outer-spin-button, // Hide Arrows From Input Number (Chrome, Safari, Edge, Opera)
@@ -27,7 +30,10 @@
27
30
  z-index: 10;
28
31
  }
29
32
  &__input {
30
- border-radius: $input-border-radius !important;
33
+ .addon + & {
34
+ border-top-left-radius: $input-border-radius !important;
35
+ border-bottom-left-radius: $input-border-radius !important;
36
+ }
31
37
  resize: none;
32
38
  }
33
39
  &__autogrow &__input {
@@ -16,6 +16,8 @@ $border-radius: $vs-border-radius;
16
16
  appearance: none;
17
17
  display: flex;
18
18
  white-space: normal;
19
+ height: 100%;
20
+ width: 100%;
19
21
  }
20
22
 
21
23
  .vs__selected-options {
@@ -39,6 +39,7 @@ class itfDatePickerInline extends Vue {
39
39
  @Prop({ type: Boolean, default: false }) range;
40
40
  @Prop({ type: Object, default: () => ({}) }) customDays;
41
41
  @Prop({ type: [String, Date], default: '' }) minDate;
42
+ @Prop({ type: [String, Date], default: '' }) maxDate;
42
43
  @Prop({
43
44
  type: Array,
44
45
  default: function () {
@@ -90,6 +91,7 @@ class itfDatePickerInline extends Vue {
90
91
  view: (this.valueAsLuxon && !this.minView) ? 'days' : this.startView,
91
92
  minView: this.minView,
92
93
  minDate: this.minDate,
94
+ maxDate: this.maxDate,
93
95
  selectedDates: this.valueAsLuxon ? [this.valueAsLuxon.toJSDate()] : [],
94
96
  onSelect: ({ date }) => {
95
97
  if (this.range && !this.calendar.rangeDateTo) {
@@ -6,28 +6,29 @@
6
6
  </slot>
7
7
  </div>
8
8
  <input
9
- ref="input"
10
- readonly
11
- :disabled="disabled"
12
- class="form-control text-capitalize"
13
- :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }"
14
- @focus="onFocus"
15
- @blur="onBlur"
16
- :value="displayValue"
17
- :placeholder="placeholder"
9
+ ref="input"
10
+ readonly
11
+ :disabled="disabled"
12
+ class="form-control text-capitalize"
13
+ :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }"
14
+ @focus="onFocus"
15
+ @blur="onBlur"
16
+ :value="displayValue"
17
+ :placeholder="placeholder"
18
18
  />
19
19
  <div style="display: none">
20
20
  <div ref="dropdown" class="itf-monthpicker__dropdown border rounded">
21
21
  <div>
22
22
  <itf-date-picker-inline
23
- :value="value"
24
- start-view="months"
25
- min-view="months"
26
- :min-date="minDate"
27
- only-calendar
28
- :display-format="displayFormat"
29
- :value-format="valueFormat"
30
- @input="selectInlineDate"
23
+ :value="value"
24
+ start-view="months"
25
+ min-view="months"
26
+ :min-date="minDate"
27
+ :max-date="maxDate"
28
+ only-calendar
29
+ :display-format="displayFormat"
30
+ :value-format="valueFormat"
31
+ @input="selectInlineDate"
31
32
  />
32
33
  </div>
33
34
  <slot name="after-calendar" />
@@ -57,6 +58,7 @@ class itfMonthPicker extends Vue {
57
58
  @Prop({ type: Boolean }) disabled;
58
59
  @Prop({ type: String, default: 'ISO' }) valueFormat;
59
60
  @Prop({ type: [String, Date], default: '' }) minDate;
61
+ @Prop({ type: [String, Date], default: '' }) maxDate;
60
62
  @Prop({ type: String, default: 'LLLL, yyyy' }) displayFormat;
61
63
  @Prop({ type: String, default: '' }) placeholder;
62
64
  @Prop({ type: String, default: '' }) prependIcon;
@@ -42,6 +42,8 @@ storiesOf('Common', module)
42
42
  }
43
43
  },
44
44
  template: `<div>
45
+ <itf-app ref="app">
46
+
45
47
  <p>You need wrap whole application with this tag</p>
46
48
 
47
49
  <h2>Usage</h2>
@@ -57,8 +59,6 @@ storiesOf('Common', module)
57
59
 
58
60
  <h2>Example</h2>
59
61
 
60
- <itf-app ref="app">
61
-
62
62
  <itf-form ref="form">
63
63
 
64
64
  <itf-icon name="project_tnm" size="32" />
@@ -71,6 +71,7 @@ storiesOf('Common', module)
71
71
 
72
72
  <itf-select
73
73
  :options="['tnm', 'fixed', 'nonprofit']"
74
+ append-to-body
74
75
  v-model="text">
75
76
 
76
77
  <template #selected-option="{ option }">
@@ -96,7 +97,11 @@ storiesOf('Common', module)
96
97
  <div class="col-12">
97
98
  <itf-label :rules="[$v.emptyValidation()]" :value="text" label="Text field" required>
98
99
 
99
- <itf-text-field v-model="text"></itf-text-field>
100
+ <itf-text-field v-model="text">
101
+ <template #addon>
102
+ <span class="input-group-text" id="basic-addon1">@</span>
103
+ </template>
104
+ </itf-text-field>
100
105
 
101
106
  </itf-label>
102
107
  </div>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
 
3
- <div class="scrollable scrollable-x big-scrollbar" :class="{'permanent-checkboxes': selectedIds.length}">
3
+ <div class="itf-table2 scrollable scrollable-x" :class="{'permanent-checkboxes': selectedIds.length}">
4
4
  <itf-checkbox-group v-model="selectedIds">
5
5
  <template v-for="(group, index) in groups">
6
6
  <div class="table-view-body">
@@ -21,8 +21,11 @@
21
21
  :show-add-column="showAddColumn"
22
22
  :show-header="!noHeader"
23
23
  :schema="schema"
24
+ :editable="editable"
24
25
  :no-column-menu="noColumnMenu"
25
26
  :no-select-all="noSelectAll"
27
+ :currencies="currencies"
28
+ :currency="currency"
26
29
  @new="$emit('new', $event)"
27
30
  @add-column="$emit('add-column', $event)"
28
31
  >
@@ -39,37 +42,6 @@
39
42
  </div>
40
43
 
41
44
  </template>
42
- <style lang="scss">
43
- .scrollable {
44
- --itf-table-hover-bg: #f2f2f2;
45
- --itf-table-min-width: 45px;
46
-
47
- body[data-theme="dark"] & {
48
- --itf-table-hover-bg: #393b41;
49
- }
50
-
51
- -webkit-overflow-scrolling: touch;
52
- overflow: hidden scroll;
53
-
54
- &.scrollable-x {
55
- overflow-x: scroll;
56
- padding-right: 4px;
57
- }
58
- }
59
- .last-sticky-column:after {
60
- content: '';
61
- background: linear-gradient(to right,#dfe0e2,rgba(255,0,0,0));
62
- height: 100%;
63
- width: 6px;
64
- position: absolute;
65
- bottom: 0;
66
- right: -7px;
67
-
68
- body[data-theme="dark"] & {
69
- background: linear-gradient(to right, #707070,rgba(255,0,0,0));
70
- }
71
- }
72
- </style>
73
45
  <script>
74
46
  import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
75
47
  import itfCheckboxGroup from '../checkbox/CheckboxGroup.vue';
@@ -77,6 +49,7 @@ import itfButton from '../button/Button.vue';
77
49
  import itfIcon from '../icon/Icon.vue';
78
50
  import itfTableGroup from './TableGroup.vue';
79
51
  import itfTableHeader from './TableHeader.vue';
52
+ import './table2.scss';
80
53
 
81
54
  export default @Component({
82
55
  name: 'itfTable2',
@@ -95,9 +68,11 @@ class itfTable2 extends Vue {
95
68
  // @Prop({ required: true, type: Array }) columns;
96
69
  @Prop({ required: true, type: Array }) rows;
97
70
  @Prop({ type: String, default: null }) groupBy;
98
- @Prop({ type: String, default: 'Id' }) idProperty;
71
+ @Prop({ type: String, default: null }) idProperty;
99
72
  @Prop({ type: String, default: null }) stateName; // save state to storage
100
73
  @Prop({ type: Object, default: () => ({}) }) schema;
74
+ @Prop() currency;
75
+ @Prop() currencies;
101
76
  @Prop(Boolean) addNewRows;
102
77
  @Prop(Boolean) columnSorting;
103
78
  @Prop(Boolean) columnResizing;
@@ -107,6 +82,7 @@ class itfTable2 extends Vue {
107
82
  @Prop(Boolean) noHeader;
108
83
  @Prop(Boolean) noColumnMenu;
109
84
  @Prop(Boolean) noSelectAll;
85
+ @Prop(Boolean) editable;
110
86
 
111
87
  state = {
112
88
  selectedIds: [],
@@ -166,10 +142,11 @@ class itfTable2 extends Vue {
166
142
 
167
143
  get groups() {
168
144
  const { groupBy, rows } = this;
169
- if (!groupBy) {
170
- return this.rows ? [{ name: '', rows: this.rows }] : []
145
+ const indexedRows = (rows || []).map((row, index) => ({ ...row, $index: index }));
146
+ if (!groupBy || !indexedRows.length) {
147
+ return [{ name: '', rows: indexedRows }]
171
148
  }
172
- const groups = rows.reduce((acc, row) => {
149
+ const groups = indexedRows.reduce((acc, row) => {
173
150
  const group = row[groupBy];
174
151
  if (!acc[group]) {
175
152
  acc[group] = [];
@@ -4,7 +4,11 @@
4
4
  <div
5
5
  v-for="(item, n) in rows"
6
6
  :key="n"
7
- group="items" data-test="table-item" class="table-view-item grouped draggable-item">
7
+ group="items"
8
+ data-test="table-item"
9
+ class="table-view-item grouped draggable-item"
10
+ :class="{'selected': selectedIds.includes(item[idProperty])}"
11
+ >
8
12
  <div class="table-row-template">
9
13
  <div accept-group="items" class="table-view-body-space"></div>
10
14
  <div class="shadow-area">
@@ -19,7 +23,8 @@
19
23
  </div>
20
24
  <div class="indicator sticky">
21
25
  <div class="fill table-view-row-count" :class="{'on-rest': !noSelectAll}">
22
- <span>{{ item[idProperty] }}</span>
26
+ <span v-if="idProperty">{{ item[idProperty] }}</span>
27
+ <span v-else>{{ (n + 1) }}</span>
23
28
  </div>
24
29
  <div v-if="!noSelectAll" class="fill on-hover">
25
30
  <itf-checkbox :value="item[idProperty]" />
@@ -31,28 +36,28 @@
31
36
  v-if="column.visible !== false"
32
37
  :data-column="k"
33
38
  :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
- class="table-view-item-value d-flex h-100 align-items-stretch">
39
+ :class="{'sticky': column.pinned, 'last-sticky-column': k === lastPinnedIndex, 'flex-grow-1': column.grow, 'px-2': !(column.editable && editable), 'editable': column.editable && editable}"
40
+ class="table-view-item-value d-flex h-100">
36
41
  <slot :name="`column.${column.property}`" :item="item" :column="column">
37
- <template v-if="column.editable">
42
+ <template v-if="column.editable && editable">
38
43
  <slot :name="`edit.${column.type}`" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)" :item="item" :column="column">
39
44
  <itf-text-field class="w-100" v-if="column.type === 'text'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
40
45
  <itf-hours-field
41
46
  class="w-100"
42
- placeholder="0h 0m"
47
+ placeholder="00h 00m"
43
48
  v-else-if="column.type === 'time'"
44
49
  :hours="getValue(item, column)"
45
50
  @update:hours="updateValue(item, $event, n, column)"
46
51
  />
47
52
  <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)" />
53
+ <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)" />
49
54
  </slot>
50
55
  </template>
51
- <template v-else>
52
- <slot :name="`format.${column.type}`" :value="getValue(item, column)" :item="item" :column="column">
56
+ <div v-else>
57
+ <slot :name="`format.${column.type}`" :value="getValue(item, column)" :update="(value) => updateValue(item, value, n, column)" :item="item" :column="column">
53
58
  {{getValue(item, column)}}
54
59
  </slot>
55
- </template>
60
+ </div>
56
61
  </slot>
57
62
  </div>
58
63
  </template>
@@ -92,45 +97,6 @@
92
97
  flex-direction: row;
93
98
  }
94
99
 
95
- .table-view-item-value {
96
- text-overflow: ellipsis;
97
- background-color: var(--bs-body-bg);
98
- border-right: 1px solid var(--bs-border-color);
99
- border-bottom: 1px solid var(--bs-border-color);
100
- align-items: stretch;
101
- display: flex;
102
- position: relative;
103
- min-width: var(--itf-table-min-width);
104
-
105
- &.highlight-drop-column {
106
- position: relative;
107
-
108
- &:after {
109
- content: "";
110
- height: 100%;
111
- z-index: 100;
112
- width: 2px;
113
- background: #47beff;
114
- margin-left: -1px;
115
- position: absolute;
116
- left: 0;
117
- }
118
- &.right:after {
119
- left: auto;
120
- right: 0;
121
- }
122
- }
123
- &:hover {
124
- background-color: var(--bs-tertiary-bg);
125
- }
126
-
127
- &.sticky {
128
- z-index: 4;
129
- position: -webkit-sticky;
130
- position: sticky;
131
- }
132
- }
133
-
134
100
  .table-item-inner .extra {
135
101
  flex-grow: 1;
136
102
  border-color: var(--bs-border-color);
@@ -143,9 +109,9 @@
143
109
  z-index: 4;
144
110
  position: -webkit-sticky;
145
111
  position: sticky;
146
- background-color: var(--bs-body-bg);
112
+ background-color: var(--itf-table2-row-bg);
147
113
  //border-right: 1px solid var(--bs-border-color);
148
- border-bottom: 1px solid var(--bs-border-color);
114
+ //border-bottom: 1px solid var(--bs-border-color);
149
115
  display: flex;
150
116
  align-items: center;
151
117
  justify-content: center;
@@ -201,6 +167,7 @@
201
167
 
202
168
  .table-item-inner .boundary.right {
203
169
  left: auto;
170
+ border-right: 1px solid var(--itf-table-border-color);
204
171
  right: 0;
205
172
  }
206
173
 
@@ -255,6 +222,10 @@ class itfTableBody extends Vue {
255
222
  @Prop() idProperty;
256
223
  @Prop(Boolean) showAddColumn;
257
224
  @Prop(Boolean) noSelectAll;
225
+ @Prop(Boolean) editable;
226
+ @Prop() selectedIds;
227
+ @Prop() currency;
228
+ @Prop() currencies;
258
229
 
259
230
  getValue(item, column) {
260
231
  return get(item, column.property);
@@ -22,11 +22,8 @@
22
22
  </span>
23
23
  <span class="d-flex align-items-center line-overflow group-header-value"
24
24
  data-test="group-value-group-label-value">
25
- {{ title }}
25
+ <slot name="group-title" :rows="rows" :title="rows">{{ title }}</slot>
26
26
  </span>
27
- <div data-test="table-group-item-count" class="table-group-item-count">
28
- {{ rows.length }}
29
- </div>
30
27
  </a>
31
28
  </div>
32
29
  </div>
@@ -42,6 +39,7 @@
42
39
  :show-add-column="showAddColumn"
43
40
  :rows="rows"
44
41
  :schema="schema"
42
+ :editable="editable"
45
43
  :no-column-menu="noColumnMenu"
46
44
  :no-select-all="noSelectAll"
47
45
  :selected-ids="selectedIds"
@@ -57,8 +55,12 @@
57
55
  @update="$emit('update', $event)"
58
56
  :id-property="idProperty"
59
57
  :rows="rows"
58
+ :editable="editable"
59
+ :currency="currency"
60
+ :currencies="currencies"
60
61
  :columns="visibleColumns"
61
62
  :no-select-all="noSelectAll"
63
+ :selected-ids="selectedIds"
62
64
  :show-add-column="showAddColumn">
63
65
  <template v-for="(_, name) in $slots" #[name]="slotData">
64
66
  <slot :name="name" v-bind="slotData || {}"/>
@@ -74,8 +76,8 @@
74
76
  class="table-row-template d-flex align-items-stretch">
75
77
  <div class="shadow-area"></div>
76
78
  <a href="" @click.prevent="$emit('new', title)" data-test="table-add-new-item"
77
- class="d-flex align-items-center flex-grow-1 table-add-new-item py-1 text-decoration-none">
78
- <span class="d-sticky d-flex align-items-center">
79
+ class="d-flex align-items-center flex-grow-1 table-add-new-item text-decoration-none">
80
+ <span class="d-sticky d-flex align-items-center py-1">
79
81
  <itf-icon name="plus"/>
80
82
  <span>{{ newLabel }}</span>
81
83
  </span>
@@ -89,6 +91,7 @@
89
91
  <div class="table-summary-columns d-flex tw-flex-row align-items-center">
90
92
  <span
91
93
  v-for="(column, n) in visibleColumns"
94
+ v-if="column.visible !== false"
92
95
  :key="n"
93
96
  :data-column="n"
94
97
  class="position-relative line-overflow"
@@ -203,13 +206,13 @@
203
206
  font-size: 1.25rem;
204
207
  border-left: 0;
205
208
  border-bottom: 0;
206
- border-right: 1px solid var(--bs-border-color);
209
+ //border-right: 1px solid var(--bs-border-color);
207
210
 
208
211
  &.header-additional-column {
209
212
  border-radius: 0;
210
213
  }
211
214
  &:hover {
212
- background: var(--hover-bg);
215
+ background: var(--itf-table-hover-bg);
213
216
  }
214
217
  }
215
218
 
@@ -228,7 +231,7 @@
228
231
 
229
232
  .table-group-item-count {
230
233
  padding: 0.125rem 0.5rem;
231
- background-color: rgb(250 251 252 / 1);
234
+ //background-color: rgb(250 251 252 / 1);
232
235
  border-radius: 0.1875rem;
233
236
  font-weight: normal;
234
237
  font-size: 1rem;
@@ -243,17 +246,19 @@
243
246
  }
244
247
 
245
248
  .table-add-new-item {
246
- border-right: 1px solid rgb(238 238 238 / 1);
247
- border-bottom: 1px solid rgb(238 238 238 / 1);
249
+ border-right: 1px solid var(--itf-table-border-color);
250
+ border-bottom: 1px solid var(--itf-table-border-color);
248
251
  outline: none;
249
252
 
250
253
  & > span {
251
254
  left: var(--shadow-area-width);
252
255
  position: sticky;
253
256
  padding-left: var(--shadow-area-width);
257
+ border-left: 1px solid var(--itf-table-border-color);
258
+ height: 100%;
254
259
  }
255
260
  &:hover, &:active {
256
- background: var(--hover-bg);
261
+ background: var(--itf-table-hover-bg);
257
262
  }
258
263
  }
259
264
 
@@ -272,7 +277,7 @@
272
277
  padding-right: .25rem;
273
278
  }
274
279
  .summary-text {
275
- color: var(--bs-tertiary-color);
280
+ color: var(--itf-table-summary-text);
276
281
  }
277
282
  &.table-summary-persist .table-summary-column,
278
283
  &:hover .table-summary-column {
@@ -315,6 +320,8 @@ class itfTableGroup extends Vue {
315
320
  @Prop() selectedIds;
316
321
  @Prop() title;
317
322
  @Prop() idProperty;
323
+ @Prop() currency;
324
+ @Prop() currencies;
318
325
  @Prop(Boolean) showGrouping;
319
326
  @Prop(Boolean) showSummary;
320
327
  @Prop(Boolean) addNewRows;
@@ -324,6 +331,7 @@ class itfTableGroup extends Vue {
324
331
  @Prop(Boolean) showHeader;
325
332
  @Prop(Boolean) noColumnMenu;
326
333
  @Prop(Boolean) noSelectAll;
334
+ @Prop(Boolean) editable;
327
335
  @Prop({type: String, default: function() { return this.$t('components.table.new'); } }) newLabel;
328
336
  @Prop({type: Object, default: () => ({})}) schema;
329
337
 
@@ -374,7 +382,7 @@ class itfTableGroup extends Vue {
374
382
  get calculateMethods() {
375
383
  return [
376
384
  { id: 'none', title: this.$t('components.table.calculateNone') },
377
- { id: 'total', title: this.$t('components.table.calculateTotal'), summary: 'Total', func: (rows, column) => rows.reduce((acc, row) => acc + (getNumber(row, column.property)), 0) },
385
+ { id: 'total', title: this.$t('components.table.calculateTotal'), summary: 'Total', func: (rows, column) => round(rows.reduce((acc, row) => acc + (getNumber(row, column.property)), 0)) },
378
386
  { id: 'average', title: this.$t('components.table.calculateAverage'), summary: 'Average', func: (rows, column) => {
379
387
  const sum = rows.reduce((acc, row) => acc + (getNumber(row, column.property)), 0);
380
388
  return round(sum / rows.length, 3);
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
 
3
- <div class="itf-table-header" :class="{'no-header': !visibleHeader}">
3
+ <div class="itf-table2__header" :class="{'no-header': !visibleHeader}">
4
4
  <div ref="container" class="table-row-template">
5
5
  <div accept-group="items" class="table-view-body-space" v-dropzone="{ payload: 0 }"></div>
6
6
  <div class="shadow-area"></div>
@@ -33,11 +33,12 @@
33
33
  v-draggable="{ handle: true, payload: { index: n, item: column }, mirror: {yAxis:false} }">
34
34
  <itf-dropdown text append-to-body shadow ref="dropdown" class="w-100" :disabled="noColumnMenu">
35
35
  <template #button>
36
- <span :title="column.title[locale] || column.title['en_US']">
36
+ <span class="itf-table2__header-title" :title="column.title[locale] || column.title['en_US']">
37
37
  <span v-if="column.icon" :class="column.icon"></span>
38
38
  {{column.title[locale] || column.title['en_US']}}
39
+ <div v-if="column.prefix" class="itf-table2__subtitle" v-text="column.prefix" />
39
40
  </span>
40
- <!-- <itf-icon name="arrow_up" :size="16" class="ms-1" />-->
41
+ <!-- <itf-icon name="arrow_up" :size="16" class="ms-1" />-->
41
42
  </template>
42
43
 
43
44
  <div v-if="column.sortable">
@@ -127,162 +128,6 @@
127
128
  </div>
128
129
 
129
130
  </template>
130
- <style lang="scss">
131
- .itf-table-header {
132
- position: sticky;
133
- top: 0;
134
- z-index: calc(var(--row-count) + 1);
135
-
136
- &.no-header {
137
- --table-row-height: 0px;
138
- --table-small-row-size: 0px;
139
-
140
- .table-view-header-value {
141
- border-top: 0 none;
142
- border-bottom: 0 none;
143
- }
144
- }
145
- .table-header.draggable-mirror{
146
- z-index: 100000001;
147
- background: rgba(235,237,239,0.75);
148
- }
149
- .table-row-template {
150
- display: flex;
151
- align-items: stretch;
152
- height: var(--table-small-row-size);
153
- user-select: none;
154
- }
155
- .table-view-header-value {
156
- cursor: pointer;
157
- align-items: center;
158
- justify-content: center;
159
- height: var(--table-row-height);
160
- white-space: nowrap;
161
- //border-top: 0;
162
- border-top: 1px solid transparent;
163
- border-left: 0;
164
- border-right-width: thin;
165
- border-bottom-width: thin;
166
- border-style: solid;
167
- position: relative;
168
- display: flex;
169
- min-width: var(--itf-table-min-width);
170
- border-right-color: transparent;
171
- border-bottom-color: var(--bs-border-color);
172
- background-color: var(--bs-body-bg);
173
- //background-color: rgb(238 238 238 / 1);
174
- font-weight: 500;
175
-
176
- &.last-sticky-column {
177
- border-right-color: var(--bs-border-color);
178
- }
179
- &.reserved {
180
- left: var(--shadow-area-width);
181
- width: var(--indicator-area-width);
182
- }
183
- &.sticky {
184
- position: sticky;
185
- position: -webkit-sticky;
186
- z-index: calc(var(--row-count) + 4);
187
- }
188
- }
189
- .table-header {
190
- flex: 0 0 auto;
191
- overflow: hidden;
192
- width: 100%;
193
- text-overflow: ellipsis;
194
- padding-left: 0.75rem;
195
- padding-right: 0.75rem;
196
- gap: 0.25rem;
197
- align-items: center;
198
- & > div {
199
- min-height: var(--table-row-height);
200
- align-items: center;
201
- justify-content: space-between;
202
- display: flex;
203
- }
204
-
205
- &.table-header-add-column {
206
- text-overflow: clip;
207
- }
208
- }
209
- .resize-handle {
210
- width: 4px;
211
- height: var(--table-row-height);
212
- position: absolute;
213
- right: -2px;
214
- top: 0;
215
- background: #29ACE1;
216
- border-radius: 2px;
217
- z-index: 100;
218
- opacity: 0;
219
- cursor: ew-resize;
220
- transition: .2s;
221
-
222
- &:hover {
223
- opacity: 1;
224
- }
225
- }
226
- .context-menu-toggle {
227
- flex: 1 1 auto;
228
- text-overflow: ellipsis;
229
- white-space: nowrap;
230
- overflow: hidden;
231
-
232
- span {
233
- font-size: 12px;
234
- line-height: 36px;
235
- }
236
- }
237
- .itf-sortable-list {
238
- display: flex;
239
- }
240
- .table-view-header-space {
241
- height: 100%;
242
- position: absolute;
243
- left: 0;
244
- display: none;
245
- background: transparent;
246
- z-index: 100;
247
- width: 2px;
248
- margin-left: -1px;
249
-
250
- &.right {
251
- left: auto;
252
- right: 0;
253
- top: 0;
254
- }
255
- &.active {
256
- display: block;
257
- &.over {
258
- background: #47BEFF;
259
- }
260
- }
261
- }
262
- .draggable-mirror {
263
- opacity: .5;
264
- }
265
- .draggable-container--over .table-view-header-space {
266
- display: block;
267
- }
268
- .draggable-container--over .draggable--over .table-view-header-space {
269
- background: #47BEFF;
270
- }
271
- .table-view-header-space > .table-view-header-dropzone {
272
- position: absolute;
273
- top: 0;
274
- right: -50px;
275
- bottom: 0;
276
- left: -50px;
277
- }
278
- .table-view-header-value:not(.reserved):hover {
279
- background-color: var(--bs-tertiary-bg);
280
- }
281
- .draggable-source--is-dragging {
282
- z-index: 100;
283
- }
284
- }
285
- </style>
286
131
  <script>
287
132
  import {Vue, Component, Prop, PropSync, Watch, Inject} from 'vue-property-decorator';
288
133
  import itfCheckbox from '../checkbox/Checkbox.vue';
@@ -323,6 +168,7 @@ class itfTableHeader extends Vue {
323
168
  @Prop(Boolean) visibleHeader;
324
169
  @Prop(Boolean) noColumnMenu;
325
170
  @Prop(Boolean) noSelectAll;
171
+ @Prop(Boolean) editable;
326
172
 
327
173
  @Watch('selectedIds')
328
174
  @Watch('rows')
@@ -1,8 +1,10 @@
1
1
  import { storiesOf } from '@storybook/vue';
2
+ import ITFSettings from '../../ITFSettings';
2
3
  import itfApp from '../app/App.vue';
3
4
  import itfButton from '../button/Button.vue';
4
5
  import itfTable from './Table.vue';
5
6
  import itfTable2 from './Table2.vue';
7
+ import itfSelect from '../select/Select.vue';
6
8
 
7
9
  const exampleData = [{
8
10
  Employee: 'Cool',
@@ -72,12 +74,17 @@ const exampleData = [{
72
74
  Notes: 'Test'
73
75
  }];
74
76
 
77
+ ITFSettings.currencies = [
78
+ { Id: '1', Code: 'USD', Title: 'USD', IsDefault: true, Symbol: '$' }
79
+ ];
80
+
75
81
  storiesOf('Common', module)
76
82
  .add('Simple Table', () => ({
77
83
  components: {
78
84
  itfTable,
79
85
  itfButton,
80
- itfTable2
86
+ itfTable2,
87
+ itfSelect
81
88
  },
82
89
  data() {
83
90
  return {
@@ -110,8 +117,8 @@ storiesOf('Common', module)
110
117
  {
111
118
  Id: 1,
112
119
  summary: true,
113
- Description: 'Total',
114
- EmployeeId: 'Vitalii Savchuk',
120
+ Description: 'Total asd asd dad ad ada dadad a',
121
+ EmployeeId: 1,
115
122
  Total: 100,
116
123
  FTE: 1.0,
117
124
  Position: { Title: 'Developer', Id: 1 },
@@ -131,7 +138,7 @@ storiesOf('Common', module)
131
138
  {
132
139
  "property": "Description",
133
140
  "title": { "en_US": "Description", "uk_UA": "Прізвище" },
134
- "type": "textarea",
141
+ "type": "text",
135
142
  "editable": true,
136
143
  "sortable": false,
137
144
  width: 200,
@@ -140,7 +147,8 @@ storiesOf('Common', module)
140
147
  },
141
148
  {
142
149
  "property": "EmployeeId",
143
- "title": { "en_US": "Employee", "uk_UA": "Імʼя" },
150
+ "title": { "en_US": "Employee full name", "uk_UA": "Імʼя" },
151
+ prefix: 'USD',
144
152
  "type": "employee",
145
153
  "copy": true,
146
154
  "editable": true,
@@ -331,8 +339,35 @@ storiesOf('Common', module)
331
339
  {{item.EmployeeId}}
332
340
  </template>
333
341
  <template #edit.employee="{ item }">
342
+ <itf-select
343
+ class="w-100"
344
+ v-model="item.EmployeeId"
345
+ :reduce="item => item.Id"
346
+ :get-option-label="item => item.Name"
347
+ :options="[{ Id: 1, Name: 'Vitalii Savchuk' }]" />
348
+ </template>
349
+ </itf-table2>
350
+
351
+ <itf-table2
352
+ editable
353
+ add-new-rows
354
+ @new="onAdd"
355
+ state-name="test"
356
+ :schema="schema"
357
+ :columns.sync="columns2" :rows="list2"
358
+ show-summary
359
+ column-sorting column-resizing show-add-column show-grouping @add-column="onAdd">
360
+ <template #format.employee="{ item }">
334
361
  {{item.EmployeeId}}
335
362
  </template>
363
+ <template #edit.employee="{ item }">
364
+ <itf-select
365
+ class="w-100"
366
+ v-model="item.EmployeeId"
367
+ :reduce="item => item.Id"
368
+ :get-option-label="item => item.Name"
369
+ :options="[{ Id: 1, Name: 'Vitalii Savchuk' }]" />
370
+ </template>
336
371
  </itf-table2>
337
372
 
338
373
  <br />
@@ -439,7 +474,7 @@ storiesOf('Common', module)
439
474
  list2: [
440
475
  {
441
476
  summary: true,
442
- Employee: 'Total',
477
+ Employee: 'Total asda das dasd ada dad adaddas',
443
478
  Total: 100,
444
479
  FTE: 1.0,
445
480
  Position: 'Developer',
@@ -0,0 +1,16 @@
1
+ class Table {
2
+
3
+ }
4
+
5
+ class TableGroup {
6
+ header = null;
7
+ data = null;
8
+ }
9
+
10
+ class TableHeader {
11
+
12
+ }
13
+
14
+ class TableData {
15
+
16
+ }
@@ -0,0 +1,271 @@
1
+ .itf-table2 {
2
+ --itf-table-border-color: #dbddd1;
3
+ --itf-table-header-bg: #f9faf5;
4
+ --itf-table-hover-header-bg: var(--bs-tertiary-bg);
5
+ --itf-table-hover-bg: #f2f2f2;
6
+ --itf-table-bg: var(--bs-body-bg);
7
+ --itf-table-min-width: 45px;
8
+ --itf-table-input-border-color: #aaaba6;
9
+ --itf-table-input-focus-border-color: #0028aa;
10
+ --itf-table-selected-bg: #cbd6f4;
11
+ --itf-table-header-subtitle-color: #aeafaa;
12
+ --itf-table-summary-text: var(--bs-tertiary-color);
13
+
14
+ // dark
15
+ body[data-theme="dark"] & {
16
+ --itf-table-hover-bg: #393b41;
17
+ --itf-table-header-bg: #0f0f0f;
18
+ --itf-table-hover-header-bg: #252525;
19
+ --itf-table-border-color: var(--bs-card-border-color);
20
+ --itf-table-input-focus-border-color: #252525;
21
+ --itf-table-selected-bg: #011534;
22
+ --itf-table-summary-text: #82909d80;
23
+ }
24
+
25
+ &.scrollable {
26
+ -webkit-overflow-scrolling: touch;
27
+ overflow: hidden scroll;
28
+ }
29
+ &.scrollable-x {
30
+ overflow-x: scroll;
31
+ padding-right: 4px;
32
+ }
33
+
34
+ &__header {
35
+ background-color: var(--itf-table-header-bg);
36
+ position: sticky;
37
+ top: 0;
38
+ z-index: calc(var(--row-count) + 1);
39
+
40
+ &.no-header {
41
+ --table-row-height: 0px;
42
+ --table-small-row-size: 0px;
43
+
44
+ .table-view-header-value {
45
+ border-top: 0 none;
46
+ border-bottom: 0 none;
47
+ }
48
+ }
49
+ .table-header.draggable-mirror{
50
+ z-index: 100000001;
51
+ background: rgba(235,237,239,0.75);
52
+ }
53
+ .table-row-template {
54
+ display: flex;
55
+ align-items: stretch;
56
+ height: auto;
57
+ user-select: none;
58
+ }
59
+ .table-view-header-value {
60
+ cursor: pointer;
61
+ align-items: center;
62
+ justify-content: center;
63
+ height: auto;
64
+ white-space: nowrap;
65
+ //border-top: 0;
66
+ border-top: 1px solid var(--itf-table-border-color);
67
+ border-left: 0;
68
+ border-right: 1px solid var(--itf-table-border-color);
69
+ border-bottom: 1px solid var(--itf-table-border-color);
70
+ position: relative;
71
+ display: flex;
72
+ min-width: var(--itf-table-min-width);
73
+ background-color: var(--itf-table-header-bg);
74
+ //background-color: var(--bs-body-bg);
75
+ //background-color: rgb(238 238 238 / 1);
76
+ //font-weight: 500;
77
+
78
+ &.last-sticky-column {
79
+ border-right-color: var(--bs-border-color);
80
+ }
81
+ &.reserved {
82
+ border-left: 1px solid var(--itf-table-border-color);
83
+ left: var(--shadow-area-width);
84
+ width: var(--indicator-area-width);
85
+ }
86
+ &.sticky {
87
+ position: sticky;
88
+ position: -webkit-sticky;
89
+ z-index: calc(var(--row-count) + 4);
90
+ }
91
+ }
92
+ .table-header {
93
+ flex: 0 0 auto;
94
+ overflow: hidden;
95
+ width: 100%;
96
+ text-overflow: ellipsis;
97
+ padding-left: 0.75rem;
98
+ padding-right: 0.5rem;
99
+ gap: 0.25rem;
100
+ align-items: center;
101
+ & > div {
102
+ min-height: var(--table-row-height);
103
+ align-items: center;
104
+ justify-content: space-between;
105
+ display: flex;
106
+ }
107
+
108
+ &.table-header-add-column {
109
+ text-overflow: clip;
110
+ }
111
+ }
112
+ .resize-handle {
113
+ width: 4px;
114
+ bottom: 0;
115
+ position: absolute;
116
+ right: -2px;
117
+ top: 0;
118
+ background: #29ACE1;
119
+ border-radius: 2px;
120
+ z-index: 100;
121
+ opacity: 0;
122
+ cursor: ew-resize;
123
+ transition: .2s;
124
+
125
+ &:hover {
126
+ opacity: 1;
127
+ }
128
+ }
129
+ .context-menu-toggle {
130
+ flex: 1 1 auto;
131
+ text-overflow: ellipsis;
132
+ white-space: nowrap;
133
+ overflow: hidden;
134
+
135
+ span {
136
+ font-size: 12px;
137
+ line-height: 36px;
138
+ }
139
+ }
140
+ .itf-sortable-list {
141
+ display: flex;
142
+ }
143
+ .table-view-header-space {
144
+ height: 100%;
145
+ position: absolute;
146
+ left: 0;
147
+ display: none;
148
+ background: transparent;
149
+ z-index: 100;
150
+ width: 2px;
151
+ margin-left: -1px;
152
+
153
+ &.right {
154
+ left: auto;
155
+ right: 0;
156
+ top: 0;
157
+ }
158
+ &.active {
159
+ display: block;
160
+ &.over {
161
+ background: #47BEFF;
162
+ }
163
+ }
164
+ }
165
+ .draggable-mirror {
166
+ opacity: .5;
167
+ }
168
+ .draggable-container--over .table-view-header-space {
169
+ display: block;
170
+ }
171
+ .draggable-container--over .draggable--over .table-view-header-space {
172
+ background: #47BEFF;
173
+ }
174
+ .table-view-header-space > .table-view-header-dropzone {
175
+ position: absolute;
176
+ top: 0;
177
+ right: -50px;
178
+ bottom: 0;
179
+ left: -50px;
180
+ }
181
+ .table-view-header-value:not(.reserved):hover {
182
+ background-color: var(--itf-table-hover-header-bg);
183
+ }
184
+ .draggable-source--is-dragging {
185
+ z-index: 100;
186
+ }
187
+ }
188
+
189
+ &__subtitle {
190
+ color: var(--itf-table-header-subtitle-color);
191
+ }
192
+
193
+ .table-view-item {
194
+ --itf-table2-row-bg: var(--itf-table-bg);
195
+
196
+ background-color: var(--itf-table2-row-bg);
197
+
198
+ &.selected {
199
+ --itf-table2-row-bg: var(--itf-table-selected-bg);
200
+ }
201
+ &:hover {
202
+ --itf-table2-row-bg: var(--itf-table-selected-bg);
203
+ }
204
+
205
+ .indicator {
206
+ border-left: 1px solid var(--itf-table-border-color);
207
+ }
208
+ }
209
+
210
+
211
+ &:hover {
212
+ .table-view-item-value.editable {
213
+ .form-control {
214
+ border: 1px solid var(--itf-table-input-border-color);
215
+ }
216
+ }
217
+ }
218
+ .table-view-item-value {
219
+ text-overflow: ellipsis;
220
+ align-items: stretch;
221
+ display: flex;
222
+ position: relative;
223
+ min-width: var(--itf-table-min-width);
224
+ background: var(--itf-table2-row-bg);
225
+
226
+ .itf-table2__striped & {
227
+ border-right: 1px solid var(--bs-border-color);
228
+ border-bottom: 1px solid var(--bs-border-color);
229
+ }
230
+
231
+ &.editable {
232
+ padding: 2px;
233
+
234
+ .form-control {
235
+ border-radius: 0;
236
+ border: 1px solid transparent;
237
+ background-color: transparent;
238
+ box-shadow: none;
239
+
240
+ &:focus {
241
+ border-color: var(--itf-table-input-focus-border-color);
242
+ }
243
+ }
244
+ }
245
+
246
+ &.highlight-drop-column {
247
+ position: relative;
248
+
249
+ &:after {
250
+ content: "";
251
+ height: 100%;
252
+ z-index: 100;
253
+ width: 2px;
254
+ background: #47beff;
255
+ margin-left: -1px;
256
+ position: absolute;
257
+ left: 0;
258
+ }
259
+ &.right:after {
260
+ left: auto;
261
+ right: 0;
262
+ }
263
+ }
264
+
265
+ &.sticky {
266
+ z-index: 4;
267
+ position: -webkit-sticky;
268
+ position: sticky;
269
+ }
270
+ }
271
+ }
@@ -15,9 +15,9 @@
15
15
  </div>
16
16
 
17
17
  <div v-if="currencySelect" class="itf-money-field__currency">
18
- <span>{{ selectedCurrencyCode }}<itf-icon v-if="!currencyDisabled" name="chevron_down" /></span>
18
+ <span>{{ selectedCurrencyCode }}<itf-icon v-if="!currencyDisabled && currenciesList.length > 1" name="chevron_down" /></span>
19
19
  <select v-if="!disabled && !currencyDisabled" :value="currencyId" @input="onCurrencyChanged">
20
- <option v-for="currency in currencies" :key="currency.Id" :value="currency.Id" :selected="currencyId === currency.Id">
20
+ <option v-for="currency in currenciesList" :key="currency.Id" :value="currency.Id" :selected="currencyId === currency.Id">
21
21
  {{ currency.Symbol }}, {{ currency.Code }} - {{ currency.Title }}
22
22
  </option>
23
23
  </select>
@@ -77,6 +77,7 @@ import itfSelect from '../select/Select';
77
77
  import itfLabel from '../form/Label';
78
78
  import itfIcon from '../icon/Icon';
79
79
  import {stringToNumber} from '../../helpers/formatters';
80
+ import ITFSettings from '../../ITFSettings';
80
81
 
81
82
  export default @Component({
82
83
  name: 'itfMoneyField',
@@ -101,6 +102,10 @@ class itfMoneyField extends Vue {
101
102
  @Prop({ type: Number, default: -1000000000}) minValue;
102
103
  @Prop({ type: Number, default: 1000000000}) maxValue;
103
104
 
105
+ get currenciesList() {
106
+ return this.currencies?.length ? this.currencies : ITFSettings.currencies;
107
+ }
108
+
104
109
  get mask() {
105
110
  return {
106
111
  mask: Number,
@@ -128,11 +133,11 @@ class itfMoneyField extends Vue {
128
133
  }
129
134
 
130
135
  onCurrencyChanged(e) {
131
- const currency = this.currencies.find((c) => c.Id === parseInt(e.target.value));
136
+ const currency = this.currenciesList.find((c) => c.Id === parseInt(e.target.value));
132
137
  this.$emit('update:currency', currency)
133
138
  }
134
139
 
135
- get precition() {
140
+ get precition() {
136
141
  return this.selectedCurrency ? this.selectedCurrency.Precision : 2;
137
142
  }
138
143
 
@@ -149,10 +154,10 @@ class itfMoneyField extends Vue {
149
154
  }
150
155
 
151
156
  get selectedCurrency() {
152
- if (!this.currency || !this.currencies) {
153
- return null;
157
+ if (!this.currency || !this.currenciesList) {
158
+ return this.currenciesList.find(({ IsDefault }) => IsDefault) || null;
154
159
  }
155
- return this.currencies.find(({ Id }) => this.currency.Id === Id);
160
+ return this.currenciesList.find(({ Id }) => this.currency.Id === Id);
156
161
  }
157
162
 
158
163
  setValue (val) {
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
 
3
3
  <div class="itf-text-field input-group" :class="{ 'with-addon addon-start': prependIcon, 'with-addon addon-end': clearable }">
4
- <div class="addon" v-if="prependIcon || $slots.addon">
5
- <slot name="addon">
4
+ <slot name="addon">
5
+ <div class="addon" v-if="prependIcon">
6
6
  <itf-icon :name="prependIcon"/>
7
- </slot>
8
- </div>
7
+ </div>
8
+ </slot>
9
9
 
10
10
  <input
11
11
  ref="input"
@@ -5,7 +5,7 @@
5
5
  <slot></slot>
6
6
  </div>
7
7
 
8
- <div class="position-relative input-group">
8
+ <div class="position-relative" :class="{'input-group': prependIcon}">
9
9
  <div class="addon" v-if="prependIcon">
10
10
  <slot name="addon">
11
11
  <itf-icon :name="prependIcon"/>