@itfin/components 1.5.1 → 1.5.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.
Files changed (120) hide show
  1. package/dist/ITFComponents.common.js +87 -0
  2. package/dist/ITFComponents.common.js.map +1 -0
  3. package/dist/ITFComponents.umd.js +98 -0
  4. package/dist/ITFComponents.umd.js.map +1 -0
  5. package/dist/ITFComponents.umd.min.js +2 -0
  6. package/dist/ITFComponents.umd.min.js.map +1 -0
  7. package/dist/demo.html +1 -0
  8. package/package.json +12 -1
  9. package/src/ITFSettings.js +0 -6
  10. package/src/components/alert/AlertBanner.vue +70 -14
  11. package/src/components/button/Button.vue +1 -3
  12. package/src/components/button/NativeButton.js +0 -4
  13. package/src/components/button/index.stories.js +2 -2
  14. package/src/components/checkbox/NestedCheckboxGroup.vue +109 -0
  15. package/src/components/customize/PropertiesList.vue +2 -0
  16. package/src/components/customize/PropertiesPopupMenu.vue +1 -1
  17. package/src/components/customize/PropertyItem.vue +24 -6
  18. package/src/components/datepicker/DatePicker.vue +1 -1
  19. package/src/components/datepicker/MonthPicker.vue +21 -1
  20. package/src/components/dropdown/Dropdown.vue +1 -1
  21. package/src/components/dropdown/DropdownMenu.vue +1 -1
  22. package/src/components/editable/EditButton.vue +1 -1
  23. package/src/components/filter/FilterBadge.vue +4 -3
  24. package/src/components/filter/FilterFacetsList.vue +16 -9
  25. package/src/components/filter/FilterPanel.vue +20 -6
  26. package/src/components/icon/components/nomi-.DS_Store +0 -0
  27. package/src/components/icon/components/nomi-approval-chain.vue +5 -0
  28. package/src/components/icon/components/nomi-calendar-view.vue +4 -0
  29. package/src/components/icon/components/nomi-close-alt.vue +5 -0
  30. package/src/components/icon/components/nomi-cog-lightning.vue +5 -0
  31. package/src/components/icon/components/nomi-comment-add.vue +5 -0
  32. package/src/components/icon/components/nomi-comment.vue +4 -0
  33. package/src/components/icon/components/nomi-comments.vue +5 -0
  34. package/src/components/icon/components/nomi-copy.vue +5 -0
  35. package/src/components/icon/components/nomi-dollar.vue +4 -0
  36. package/src/components/icon/components/nomi-expense-requests.vue +5 -0
  37. package/src/components/icon/components/nomi-file-doc.vue +7 -0
  38. package/src/components/icon/components/nomi-file-excel.vue +9 -0
  39. package/src/components/icon/components/nomi-file-image.vue +6 -0
  40. package/src/components/icon/components/nomi-file-pdf.vue +5 -0
  41. package/src/components/icon/components/nomi-help.vue +3 -2
  42. package/src/components/icon/components/nomi-kanban-view.vue +6 -0
  43. package/src/components/icon/components/nomi-light-bulb.vue +4 -0
  44. package/src/components/icon/components/nomi-list-view.vue +7 -0
  45. package/src/components/icon/components/nomi-lock.vue +1 -1
  46. package/src/components/icon/components/nomi-money-alt.vue +4 -0
  47. package/src/components/icon/components/nomi-money-requests.vue +12 -0
  48. package/src/components/icon/components/nomi-pending.vue +4 -0
  49. package/src/components/icon/components/nomi-plus.vue +5 -0
  50. package/src/components/icon/components/nomi-project.vue +2 -2
  51. package/src/components/icon/components/nomi-scissors.vue +1 -1
  52. package/src/components/icon/components/nomi-secure.vue +4 -0
  53. package/src/components/icon/components/nomi-stop.vue +4 -0
  54. package/src/components/icon/components/nomi-table-config.vue +9 -0
  55. package/src/components/icon/components/nomi-table-view.vue +4 -1
  56. package/src/components/icon/components/nomi-thumbs-down.vue +4 -0
  57. package/src/components/icon/components/nomi-thumbs-up.vue +4 -0
  58. package/src/components/icon/components/nomi-undo.vue +4 -0
  59. package/src/components/icon/components/nomi-user-settings.vue +5 -0
  60. package/src/components/icon/components/nomi-user.vue +3 -3
  61. package/src/components/icon/convert-icons.js +0 -3
  62. package/src/components/icon/icons.js +403 -372
  63. package/src/components/icon/new-icons/approval-chain.svg +4 -0
  64. package/src/components/icon/new-icons/budget.svg +3 -0
  65. package/src/components/icon/new-icons/calendar-view.svg +3 -0
  66. package/src/components/icon/new-icons/close-alt.svg +4 -0
  67. package/src/components/icon/new-icons/cog-lightning.svg +4 -0
  68. package/src/components/icon/new-icons/comment-add.svg +4 -0
  69. package/src/components/icon/new-icons/comment.svg +3 -0
  70. package/src/components/icon/new-icons/comments.svg +4 -0
  71. package/src/components/icon/new-icons/copy.svg +4 -0
  72. package/src/components/icon/new-icons/dollar.svg +3 -0
  73. package/src/components/icon/new-icons/expense-requests.svg +4 -0
  74. package/src/components/icon/new-icons/file-doc.svg +6 -0
  75. package/src/components/icon/new-icons/file-excel.svg +8 -0
  76. package/src/components/icon/new-icons/file-image.svg +5 -0
  77. package/src/components/icon/new-icons/file-pdf.svg +4 -0
  78. package/src/components/icon/new-icons/help.svg +3 -2
  79. package/src/components/icon/new-icons/kanban-view.svg +5 -0
  80. package/src/components/icon/new-icons/light-bulb.svg +3 -0
  81. package/src/components/icon/new-icons/list-view.svg +6 -0
  82. package/src/components/icon/new-icons/lock.svg +1 -1
  83. package/src/components/icon/new-icons/money-alt.svg +3 -0
  84. package/src/components/icon/new-icons/money-requests.svg +11 -0
  85. package/src/components/icon/new-icons/pending.svg +3 -0
  86. package/src/components/icon/new-icons/plus.svg +4 -0
  87. package/src/components/icon/new-icons/project.svg +2 -2
  88. package/src/components/icon/new-icons/scissors.svg +1 -1
  89. package/src/components/icon/new-icons/secure.svg +3 -0
  90. package/src/components/icon/new-icons/stop.svg +3 -0
  91. package/src/components/icon/new-icons/table-config.svg +8 -0
  92. package/src/components/icon/new-icons/table-view.svg +4 -1
  93. package/src/components/icon/new-icons/thumbs-down.svg +3 -0
  94. package/src/components/icon/new-icons/thumbs-up.svg +3 -0
  95. package/src/components/icon/new-icons/undo.svg +3 -0
  96. package/src/components/icon/new-icons/user-settings.svg +4 -0
  97. package/src/components/icon/new-icons/user.svg +3 -3
  98. package/src/components/kanban/BoardCard.vue +1 -1
  99. package/src/components/kanban/BoardCardTimer.vue +1 -1
  100. package/src/components/modal/Modal.vue +6 -1
  101. package/src/components/overlay/SensitiveOverlay.vue +4 -2
  102. package/src/components/pagination/Pagination2.vue +4 -3
  103. package/src/components/panels/Panel.vue +23 -1
  104. package/src/components/panels/PanelItemEdit.vue +91 -10
  105. package/src/components/panels/PanelList.vue +19 -6
  106. package/src/components/table/Table2.vue +65 -60
  107. package/src/components/table/TableBody.vue +6 -0
  108. package/src/components/table/TableGroup.vue +13 -4
  109. package/src/components/table/TableHeader.vue +77 -76
  110. package/src/components/table/TableRowToggle.vue +9 -1
  111. package/src/components/table/TableRows.vue +54 -30
  112. package/src/components/table/table2.scss +15 -34
  113. package/src/components/text-field/TextField.vue +8 -0
  114. package/src/components/tree/TreeEditor.vue +2 -3
  115. package/src/components/view/View.vue +217 -56
  116. package/src/helpers/validators.js +35 -9
  117. package/src/helpers/validators.spec.js +48 -11
  118. package/src/locales/en.js +8 -2
  119. package/src/locales/pl.js +2 -1
  120. package/src/locales/uk.js +7 -6
@@ -4,26 +4,29 @@
4
4
  <itf-filter-panel
5
5
  :search-placeholder="searchPlaceholder"
6
6
  search
7
+ show-filter
7
8
  :visible="!noFilters"
8
9
  :filters-only="filtersOnly"
9
10
  ref="filters"
10
11
  :mini="panel.isMultiple()"
11
12
  class="py-2 px-3"
13
+ :static-filters="filters"
12
14
  :endpoint="filtersEndpoint"
13
15
  :panel="panel"
14
16
  v-model="filter"
15
17
  @loaded="onFilterSet($event, true)"
16
18
  @change="onFilterSet($event, false)"
19
+ @set-table-schema="setTableSchema"
17
20
  >
18
21
  <template #after-filter-btn>
19
- <itf-dropdown v-if="$refs.table && schema" shadow append-to-context :button-options="{ default: true, icon: true }" class="h-100" autoclose="outside">
22
+ <itf-dropdown v-if="$refs.table && tableSchema" shadow append-to-context :button-options="{ default: true, icon: true }" class="h-100" autoclose="outside">
20
23
  <template #button>
21
- <itf-icon new name="table-view" />
24
+ <itf-icon new name="table-config" />
22
25
  </template>
23
26
  <div class="dropdown-header">
24
27
  {{ $t('components.table.columns') }}
25
28
  </div>
26
- <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">
29
+ <a v-for="(property, n) of tableSchema.properties" :key="n" href="" @click.stop.prevent="$refs.table.toggleVisibility(property.property)" class="dropdown-item justify-content-between d-flex gap-3">
27
30
  <div class="d-flex align-items-center gap-1">
28
31
  <itf-icon v-if="property.icon" :size="24" new :name="property.icon" />
29
32
  {{getTitle(property.title)}}
@@ -42,64 +45,105 @@
42
45
  </div>
43
46
  </a>
44
47
  </itf-dropdown>
45
- </template>
46
- </itf-filter-panel>
48
+ <itf-dropdown v-if="downloadEndpoint" shadow append-to-context :button-options="{ default: true, small: true }" class="h-100" autoclose="outside">
49
+ <template #button>
50
+ <itf-icon name="download"/>
51
+ {{ $t('export') }}
52
+ </template>
53
+ <a v-for="(item, n) in getDownloadLinks()" target="_blank" :key="n" :href="item.link" class="dropdown-item">
54
+ {{item.title}}
55
+ </a>
56
+ </itf-dropdown>
57
+
58
+ <slot name="before-tabs"></slot>
47
59
 
48
- <div class="flex-grow-1 px-3 d-flex flex-column">
49
- <div class="position-relative flex-grow-1">
50
- <itf-table
51
- ref="table"
52
- style="--shadow-area-width: 0px;"
53
- absolute
54
- striped
55
- clickable
56
- column-sorting
57
- column-resizing
58
- :indicator-type="indicatorType"
59
- class="permanent-checkboxes"
60
- divider-property="hasDivider"
61
- subrows-property="children"
62
- :state-name="stateName"
63
- id-property="id"
64
- :rows="preparedItems"
65
- :schema="schema"
66
- :sorting="sorting"
67
- :active="activeIds"
68
- :show-actions="showActions"
69
- :expanded-ids.sync="expandedIds"
70
- v-model="selectedIds"
71
- @row-click="$emit('open', $event)"
72
- @update:sorting="updateSorting($event)"
60
+ <itf-segmented-control
61
+ v-if="tabs.length > 1"
62
+ class="small"
63
+ v-model="currentTab"
64
+ item-key="value"
65
+ :items="tabs"
73
66
  >
74
- <template v-for="(_, name) in $slots" #[name]="slotData">
75
- <slot :name="name" v-bind="slotData || {}"/>
76
- </template>
77
- <template v-for="(_, name) in $scopedSlots" #[name]="slotData">
78
- <slot :name="name" v-bind="slotData || {}"/>
67
+ <template #item="{ item }">
68
+ <div class="d-flex align-items-center">
69
+ <itf-icon class="text-muted" new :name="item.icon" />
70
+ {{item.text}}
71
+ </div>
79
72
  </template>
80
- </itf-table>
73
+ </itf-segmented-control>
74
+ </template>
75
+ </itf-filter-panel>
76
+
77
+ <div v-if="currentTab === 'list'" class="position-relative flex-grow-1">
78
+ <div class="position-absolute" style="top: 0; left: 0; right: 0; bottom: 0; overflow: auto;">
79
+ <slot name="list-view" :items="items" :loading="loading"></slot>
81
80
  </div>
82
81
  </div>
82
+ <slot v-else-if="currentTab === 'board'" name="kanban-view"></slot>
83
+ <slot v-else-if="currentTab === 'calendar'" name="calendar-view"></slot>
84
+ <slot v-else name="table-view">
85
+ <div class="flex-grow-1 px-3 d-flex flex-column">
86
+ <div class="position-relative flex-grow-1">
87
+ <itf-table
88
+ ref="table"
89
+ style="--shadow-area-width: 0px;"
90
+ absolute
91
+ striped
92
+ clickable
93
+ column-sorting
94
+ column-resizing
95
+ :indicator-type="indicatorType"
96
+ class="permanent-checkboxes"
97
+ :state-name="stateName"
98
+ id-property="id"
99
+ divider-property="hasDivider"
100
+ subrows-property="children"
101
+ :sort-as-string="sortAsString"
102
+ :rows="preparedItems"
103
+ :group-by="groupBy"
104
+ :schema="tableSchema"
105
+ :sorting="sorting"
106
+ :active="activeIds"
107
+ :expanded-ids.sync="expandedIds"
108
+ :no-select-all="noSelectAll"
109
+ :show-actions="showActions"
110
+ :indicator-width="indicatorWidth"
111
+ v-model="selectedIds"
112
+ @row-click="$emit('open', $event)"
113
+ @update:sorting="updateSorting($event)"
114
+ >
115
+ <template v-for="(_, name) in $slots" #[name]="slotData">
116
+ <slot :name="name" v-bind="slotData || {}"/>
117
+ </template>
118
+ <template v-for="(_, name) in $scopedSlots" #[name]="slotData">
119
+ <slot :name="name" v-bind="slotData || {}"/>
120
+ </template>
121
+ </itf-table>
122
+ </div>
123
+ </div>
124
+ </slot>
83
125
 
84
126
  <itf-pagination
85
127
  class="my-2 px-3"
128
+ v-if="showPagination"
86
129
  show-size
87
130
  :size="size"
88
131
  :items="preparedItems"
89
132
  :pages="countPages"
90
133
  :value="page"
134
+ :totalCount="showTotalCount ? total : null"
91
135
  @input="updatePage($event)"
92
136
  @per-page="updateSizePerPage($event)"
93
137
  >
94
138
  <template #center>
95
- <slot name="pagination-center" />
139
+ <slot name="pagination-center" :totals="totals" />
96
140
  </template>
97
141
  </itf-pagination>
98
142
  </div>
99
143
 
100
144
  </template>
101
145
  <script>
102
- import { Vue, ModelSync, Component, Prop, Inject } from 'vue-property-decorator';
146
+ import {Vue, ModelSync, Component, Prop, Inject, PropSync, Watch} from 'vue-property-decorator';
103
147
  import loading from '../../directives/loading';
104
148
  import itfTable from '../table/Table2.vue';
105
149
  import itfFilterPanel from '../filter/FilterPanel.vue';
@@ -107,11 +151,16 @@ import itfPagination from '../pagination/Pagination2.vue';
107
151
  import itfTableBody from "../table/TableBody.vue";
108
152
  import itfIcon from "../icon/Icon.vue";
109
153
  import itfDropdown from "../dropdown/Dropdown.vue";
154
+ import itfSegmentedControl from '../segmented-control/SegmentedControl.vue';
155
+ import itfButton from '@itfin/components/src/components/button/Button.vue';
110
156
 
111
157
  export default @Component({
112
158
  name: 'itfView',
113
159
  components: {
114
- itfDropdown, itfIcon,
160
+ itfButton,
161
+ itfSegmentedControl,
162
+ itfDropdown,
163
+ itfIcon,
115
164
  itfPagination,
116
165
  itfFilterPanel,
117
166
  itfTableBody,
@@ -129,21 +178,34 @@ class itfView extends Vue {
129
178
  @Prop({ type: Boolean }) filtersOnly;
130
179
  @Prop({ type: Boolean }) noFilters;
131
180
  @Prop({ type: Array }) filters;
132
- @Prop({ type: Object, required: true }) schema;
133
- // @Prop({ default: 20 }) size;
134
- // @Prop({ default: 1 }) page;
181
+ @Prop({ type: Object }) schema;
135
182
  @Prop(String) defaultSorting;
136
183
  @Prop(String) endpoint;
137
184
  @Prop(String) filtersEndpoint;
138
185
  @Prop(String) itemsKey;
186
+ @Prop({ type: String, default: null }) groupBy;
187
+ @Prop({ type: String, default: null }) downloadEndpoint; // префікс апі для завантаження
188
+ @Prop({ type: String, default: 'totals' }) totalsKey;
139
189
  @Prop(String) panelKey;
140
190
  @Prop(String) stateName;
141
191
  @Prop({ type: String, default: 'checkbox' }) indicatorType;
192
+ @Prop({ type: String, default: 'list' }) tab;
142
193
  @Prop({ type: String, default () { return this.$t('components.table.search'); } }) searchPlaceholder;
143
194
  @Prop() panel;
144
195
  @Prop() hardFilter; // встановлює жорсткий фільтр, наприклад, треба завжди показувати лише по контрагенту
145
196
  @Prop(Boolean) showActions;
197
+ @Prop({ type: Boolean, default: true }) showPagination;
198
+ @Prop(Boolean) listViewEnabled;
199
+ @Prop(Boolean) kanbanViewEnabled;
200
+ @Prop(Boolean) calendarViewEnabled;
201
+ @Prop(Boolean) noSelectAll;
202
+ @Prop({ type: Boolean, default: true }) tableViewEnabled;
203
+ @Prop(Boolean) sortAsString;
204
+ @Prop(Boolean) oldFormat;
146
205
  @Prop({ type: Array, default: () => [] }) disabledIds;
206
+ @Prop({ default: 45 }) indicatorWidth;
207
+ @Prop({type: Function, default: null }) onSplitSlectedIds // якщо потрібно розділяти вибрані рядки в таблиці на дві групи
208
+ @Prop({ type: Boolean, default: false }) showTotalCount;
147
209
 
148
210
  page = 1;
149
211
  total = 0;
@@ -153,6 +215,8 @@ class itfView extends Vue {
153
215
  filter = {};
154
216
  loadingData = false;
155
217
  activeIds = [];
218
+ tableColumns = null;
219
+ totals = null;
156
220
  expandedIds = [];
157
221
 
158
222
  get preparedItems() {
@@ -160,6 +224,73 @@ class itfView extends Vue {
160
224
  return this.items.map(item => ({ ...item, isDisabled: disabledIdsSet.has(item.id) }));
161
225
  }
162
226
 
227
+ @Watch('selectedIds', { deep: true, immediate: true })
228
+ updateSelectedIds() {
229
+ if(this.onSplitSlectedIds && this.items.length && this.selectedIds.length) {
230
+ this.onSplitSlectedIds(this.selectedIds, this.items);
231
+ }
232
+ }
233
+
234
+ getDownloadLinks() {
235
+ const state = this.$refs.table ? this.$refs.table.getTableState() : null;
236
+ const filter = { ...this.filter };
237
+ const sorting = this.sorting;
238
+ const filterableColumnsNames = (state?.columns ?? []).filter(column => column.visible).map(column => column.property);
239
+
240
+ const filterWithValue = Object.fromEntries(Object.entries(filter).filter(([_, value]) => typeof value !== 'undefined'));
241
+ const params = {
242
+ ...filterWithValue,
243
+ sort: sorting
244
+ };
245
+ if (filterableColumnsNames.length) {
246
+ params.columns = filterableColumnsNames.join(',')
247
+ }
248
+ const xlsQueryParams = new URLSearchParams({ ...params, format: 'xlsx' }).toString();
249
+ const csvQueryParams = new URLSearchParams({ ...params, format: 'csv' }).toString();
250
+ return [
251
+ { title: 'Excel (xlsx)', link: `${this.downloadEndpoint}?${xlsQueryParams}` },
252
+ { title: 'Plain text (csv)', link: `${this.downloadEndpoint}?${csvQueryParams}` },
253
+ ];
254
+ }
255
+
256
+ get currentTab() {
257
+ return this.tab;
258
+ }
259
+
260
+ set currentTab(val) {
261
+ this.$emit('update:tab', val);
262
+ this.setPanelPayload({ tab: val });
263
+ }
264
+
265
+ get tabs() {
266
+ const views = [];
267
+ if (this.listViewEnabled) {
268
+ views.push({ value: 'list', text: this.$t('list'), icon: 'list-view' });
269
+ }
270
+ if (this.kanbanViewEnabled) {
271
+ views.push({ value: 'board', text: this.$t('board'), icon: 'kanban-view' });
272
+ }
273
+ if (this.calendarViewEnabled) {
274
+ views.push({ value: 'calendar', text: this.$t('calendar'), icon: 'calendar-view' });
275
+ }
276
+ if (this.tableViewEnabled) {
277
+ views.push({ value: 'table', text: this.$t('table'), icon: 'table-view' });
278
+ }
279
+ return views;
280
+ }
281
+
282
+ get tableSchema() {
283
+ if (this.tableColumns) {
284
+ return {
285
+ properties: this.tableColumns
286
+ }
287
+ } else if (this.schema) {
288
+ return this.schema
289
+ } else {
290
+ return {}
291
+ }
292
+ }
293
+
163
294
  created() {
164
295
  const defaultSize = localStorage.getItem('sizePerPage') ?? 20;
165
296
 
@@ -189,29 +320,42 @@ class itfView extends Vue {
189
320
  this.$emit('load', this.filter);
190
321
  this.loadingData = true;
191
322
  await this.$try(async () => {
192
- const res = await this.$axios.$get(this.endpoint, {
323
+ let filter = { ...this.filter };
324
+ if (this.oldFormat) {
325
+ filter = Object.keys(filter).reduce((acc, key) => {
326
+ acc[`filter[${key}]`] = filter[key];
327
+ return acc;
328
+ }, {})
329
+ }
330
+ this.selectedIds = [];
331
+ const { data, headers } = await this.$axios.get(this.endpoint, {
193
332
  preventRaceCondition: true,
194
333
  params: {
195
- ...Object.assign(this.filter ?? {}, this.hardFilter ?? {}),
334
+ ...filter,
196
335
  page: this.page,
197
336
  size: this.size,
198
337
  sort: this.sorting
199
338
  }
200
339
  });
201
- this.selectedIds = [];
202
- this.items = res[this.itemsKey];
203
- this.page = res.meta.page;
204
- this.total = res.meta.total;
205
- this.size = res.meta.size;
206
- if (res.columns) {
207
- this.$emit('columns-loaded', res.columns);
340
+ if (this.oldFormat) {
341
+ this.items = data;
342
+ this.page = Number(headers['x-page'] ?? 1);
343
+ this.total = Number(headers['x-count'] ?? 0);
344
+ this.size = Number(headers['x-size'] ?? 20);
345
+ } else {
346
+ this.items = data[this.itemsKey];
347
+ this.totals = data[this.totalsKey];
348
+ this.page = data.meta.page;
349
+ this.total = data.meta.total;
350
+ this.size = data.meta.size;
208
351
  }
209
-
210
- this.$emit('update:items', this.items);
211
- this.loadingData = false;
212
- }, () => {
213
- this.loadingData = false;
214
352
  });
353
+ if (res.columns) {
354
+ this.$emit('columns-loaded', res.columns);
355
+ }
356
+
357
+ this.$emit('update:items', this.items);
358
+ this.loadingData = false;
215
359
  this.$emit('loaded', this.filter);
216
360
  }
217
361
 
@@ -241,6 +385,7 @@ class itfView extends Vue {
241
385
  updateSizePerPage (val) {
242
386
  this.page = 1;
243
387
  this.size = val;
388
+ this.selectedIds = [];
244
389
  this.setPanelPayload({ page: null, size: val });
245
390
  this.loadData();
246
391
  localStorage.setItem('sizePerPage', val);
@@ -277,10 +422,26 @@ class itfView extends Vue {
277
422
  this.loadData();
278
423
  }
279
424
 
425
+ setTableSchema(tableSchema) {
426
+ this.tableColumns = tableSchema;
427
+ }
428
+
429
+ getSorting() {
430
+ return this.sorting;
431
+ }
432
+
280
433
  loadFilters() {
281
434
  if (this.filtersEndpoint) {
282
435
  this.$refs.filters?.loadData();
283
436
  }
284
437
  }
438
+
439
+ getFilter() {
440
+ return this.filter;
441
+ }
442
+
443
+ getTableState() {
444
+ return this.$refs.table ? this.$refs.table.getTableState() : null;
445
+ }
285
446
  }
286
447
  </script>
@@ -8,12 +8,12 @@ const LINKED_IN_REGEXP = /(http(s)?:\/\/)?([\w]+\.)?linkedin\.com\/(pub|in|profi
8
8
  const SPECIAL_CHARS_REGEXP = /^[\w_\-+~,/\\:'"().&*|[\]?# ]+$/i;
9
9
  const GREETINGS_REGEXP = /\b(dr|mr|mister|mrs|ms|miss|sir|hello|hi)\b/i;
10
10
  const PHONE_REGEXP = /(\+?\(?\+?[0-9]{1,3}\)?[-. ]+([0-9]{2,4})[-. ]?([0-9]{3,5}))|\+?[0-9]{7,}/gi;
11
- const INTEGER_REGEXP = /^-?\d{0,11}$/;
12
11
  const DOUBLE_REGEXP = /^-?\d{0,11}(\.\d{0,8}){0,1}$/;
13
- const EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
12
+ const EMAIL_REGEXP = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/g;
14
13
  const EMAIL_LIST_REGEXP = /^(\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]{2,4}\s*?,?\s*?)+$/g;
15
14
  const HEX_REGEXP = /[0-9A-Fa-f]{6}/;
16
- const PASSWORD_REGEX = /^(?=.*\p{Lu})(?=.*\p{Ll})(?=.*\d)(?=.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]).{8,}$/u;
15
+ const PASSWORD_MIN_LENGTH = 8;
16
+ const PASSWORD_MAX_LENGTH = 50;
17
17
 
18
18
  const STANDART_DATE_FORMAT = 'yyyy-MM-dd';
19
19
 
@@ -67,10 +67,6 @@ export function emptyArrayValidation (message) {
67
67
  return (v, $t = (s) => s) => (Array.isArray(v) && v.length > 0) || (message || $t('components.thisFieldIsRequired'));
68
68
  }
69
69
 
70
- export function integerValidation (message) {
71
- return (v, $t = (s) => s) => !v || !!(v + '').match(INTEGER_REGEXP) || (message || $t('components.thisFieldMustBeANumberFormatZero'));
72
- }
73
-
74
70
  export function doubleValidation (message) {
75
71
  return (v, $t = (s) => s) => !v || !!(v + '').match(DOUBLE_REGEXP) || (message || $t('components.thisFieldMustBeANumberFormatZero'));
76
72
  }
@@ -188,8 +184,38 @@ export function dateSameOrBeforeValidation (dateCompare, message) {
188
184
  };
189
185
  }
190
186
 
191
- export function passwordValidation(message) {
187
+ export function hasUppercaseValidation(message) {
188
+ return (v, $t) => {
189
+ return !isEmpty(v) && /\p{Lu}/u.test(v + '') || (message || $t('components.atLeastOneUppercaseLetterRequired'));
190
+ };
191
+ }
192
+
193
+ export function hasLowercaseValidation(message) {
194
+ return (v, $t) => {
195
+ return !isEmpty(v) && /\p{Ll}/u.test(v + '') || (message || $t('components.atLeastOneLowercaseLetterRequired'));
196
+ };
197
+ }
198
+
199
+ export function hasDigitValidation(message) {
200
+ return (v, $t) => {
201
+ return !isEmpty(v) && /\d/.test(v + '') || (message || $t('components.atLeastOneNumberRequired'));
202
+ };
203
+ }
204
+
205
+ export function hasSpecialCharValidation(message) {
192
206
  return (v, $t) => {
193
- return !isEmpty(v) && PASSWORD_REGEX.test(v.trim() + '') || (message || $t('components.passwordValidation'));
207
+ return !isEmpty(v) && /[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/.test(v + '') || (message || $t('components.atLeastOneSpecialCharacterRequired'));
194
208
  };
195
209
  }
210
+
211
+ export function accountPasswordValidation(message) {
212
+ return [
213
+ emptyValidation(message),
214
+ minLengthValidation(PASSWORD_MIN_LENGTH, message),
215
+ lengthValidation(PASSWORD_MAX_LENGTH, message),
216
+ hasUppercaseValidation(message),
217
+ hasLowercaseValidation(message),
218
+ hasDigitValidation(message),
219
+ hasSpecialCharValidation(message),
220
+ ];
221
+ }
@@ -7,7 +7,10 @@ import {
7
7
  dateAfterValidation,
8
8
  dateSameOrAfterValidation,
9
9
  dateSameOrBeforeValidation,
10
- passwordValidation,
10
+ hasUppercaseValidation,
11
+ hasLowercaseValidation,
12
+ hasDigitValidation,
13
+ hasSpecialCharValidation,
11
14
  } from './validators';
12
15
 
13
16
  describe('Validators', () => {
@@ -83,16 +86,50 @@ describe('Validators', () => {
83
86
  expect(mediumTextValidation()('0'.repeat(16777216), $t)).toEqual('components.mediumTextLength');
84
87
  });
85
88
 
89
+ test('hasUppercaseValidation', () => {
90
+ expect(hasUppercaseValidation()('', $t)).toEqual('components.atLeastOneUppercaseLetterRequired');
91
+ expect(hasUppercaseValidation()(' ', $t)).toEqual('components.atLeastOneUppercaseLetterRequired');
92
+ expect(hasUppercaseValidation()(null, $t)).toEqual('components.atLeastOneUppercaseLetterRequired');
93
+ expect(hasUppercaseValidation()(undefined, $t)).toEqual('components.atLeastOneUppercaseLetterRequired');
94
+ expect(hasUppercaseValidation()('A', $t)).toEqual(true);
95
+ expect(hasUppercaseValidation()(' Test ', $t)).toEqual(true);
96
+ expect(hasUppercaseValidation()(' test ', $t)).toEqual('components.atLeastOneUppercaseLetterRequired');
97
+ expect(hasUppercaseValidation()(' Тест ', $t)).toEqual(true);
98
+ expect(hasUppercaseValidation()(' тест ', $t)).toEqual('components.atLeastOneUppercaseLetterRequired');
99
+ expect(hasUppercaseValidation()(' ТЕСТ ', $t)).toEqual(true);
100
+ });
101
+
102
+ test('hasLowercaseValidation', () => {
103
+ expect(hasLowercaseValidation()('', $t)).toEqual('components.atLeastOneLowercaseLetterRequired');
104
+ expect(hasLowercaseValidation()(' ', $t)).toEqual('components.atLeastOneLowercaseLetterRequired');
105
+ expect(hasLowercaseValidation()(null, $t)).toEqual('components.atLeastOneLowercaseLetterRequired');
106
+ expect(hasLowercaseValidation()(undefined, $t)).toEqual('components.atLeastOneLowercaseLetterRequired');
107
+ expect(hasLowercaseValidation()('a', $t)).toEqual(true);
108
+ expect(hasLowercaseValidation()(' test ', $t)).toEqual(true);
109
+ expect(hasLowercaseValidation()('A', $t)).toEqual('components.atLeastOneLowercaseLetterRequired');
110
+ expect(hasLowercaseValidation()(' Test ', $t)).toEqual(true);
111
+ expect(hasLowercaseValidation()(' Тест ', $t)).toEqual(true);
112
+ expect(hasLowercaseValidation()(' тест ', $t)).toEqual(true);
113
+ expect(hasLowercaseValidation()(' ТЕСТ ', $t)).toEqual('components.atLeastOneLowercaseLetterRequired');
114
+ })
115
+
116
+ test('hasDigitValidation', () => {
117
+ expect(hasDigitValidation()('', $t)).toEqual('components.atLeastOneNumberRequired');
118
+ expect(hasDigitValidation()(' ', $t)).toEqual('components.atLeastOneNumberRequired');
119
+ expect(hasDigitValidation()(null, $t)).toEqual('components.atLeastOneNumberRequired');
120
+ expect(hasDigitValidation()(undefined, $t)).toEqual('components.atLeastOneNumberRequired');
121
+ expect(hasDigitValidation()('1', $t)).toEqual(true);
122
+ expect(hasDigitValidation()(' 1 ', $t)).toEqual(true);
123
+ expect(hasDigitValidation()('a', $t)).toEqual('components.atLeastOneNumberRequired');
124
+ });
86
125
 
87
- test('passwordValidation', () => {
88
- expect(passwordValidation()('', $t)).toEqual('components.passwordValidation');
89
- expect(passwordValidation()(' ', $t)).toEqual('components.passwordValidation');
90
- expect(passwordValidation()(null, $t)).toEqual('components.passwordValidation');
91
- expect(passwordValidation()(undefined, $t)).toEqual('components.passwordValidation');
92
- expect(passwordValidation()(' тесттест ', $t)).toEqual('components.passwordValidation');
93
- expect(passwordValidation()(' ТЕСТТЕСТ ', $t)).toEqual('components.passwordValidation');
94
- expect(passwordValidation()(' ТестТест ', $t)).toEqual('components.passwordValidation');
95
- expect(passwordValidation()(' ТестТест1 ', $t)).toEqual('components.passwordValidation');
96
- expect(passwordValidation()(' ТестТест1@ ', $t)).toEqual(true);
126
+ test('hasSpecialCharValidation', () => {
127
+ expect(hasSpecialCharValidation()('', $t)).toEqual('components.atLeastOneSpecialCharacterRequired');
128
+ expect(hasSpecialCharValidation()(' ', $t)).toEqual('components.atLeastOneSpecialCharacterRequired');
129
+ expect(hasSpecialCharValidation()(null, $t)).toEqual('components.atLeastOneSpecialCharacterRequired');
130
+ expect(hasSpecialCharValidation()(undefined, $t)).toEqual('components.atLeastOneSpecialCharacterRequired');
131
+ expect(hasSpecialCharValidation()('@', $t)).toEqual(true);
132
+ expect(hasSpecialCharValidation()(' @ ', $t)).toEqual(true);
133
+ expect(hasSpecialCharValidation()('a', $t)).toEqual('components.atLeastOneSpecialCharacterRequired');
97
134
  });
98
135
  });
package/src/locales/en.js CHANGED
@@ -56,6 +56,11 @@ module.exports = {
56
56
  addMore: 'Add more',
57
57
  close: 'Close',
58
58
  wholeYear: 'Whole year',
59
+ atLeastOneUppercaseLetterRequired: 'At least one uppercase letter is required',
60
+ atLeastOneLowercaseLetterRequired: 'At least one lowercase letter is required',
61
+ atLeastOneNumberRequired: 'At least one number is required',
62
+ atLeastOneSpecialCharacterRequired: 'At least one special character is required (e.g. @,$,!,%,*,?,&)',
63
+ dropzoneText: 'Click or drag file to upload',
59
64
 
60
65
  select: {
61
66
  loading: 'Loading...',
@@ -99,7 +104,7 @@ module.exports = {
99
104
  copyingToClipboardWasSuccessful: 'Copying to clipboard was successful',
100
105
  },
101
106
  table: {
102
- new: 'New',
107
+ new: 'New row',
103
108
  noResults: 'No items',
104
109
  sortAscending: 'Sort ascending',
105
110
  sortDescending: 'Sort descending',
@@ -133,7 +138,7 @@ module.exports = {
133
138
  cancelSelected: 'Cancel',
134
139
  },
135
140
  pagination: {
136
- itemsPerPage: 'Items per page',
141
+ itemsPerPage: 'On the page',
137
142
  previous: 'Previous',
138
143
  next: 'Next'
139
144
  },
@@ -155,4 +160,5 @@ module.exports = {
155
160
  },
156
161
  passwordValidation: 'Password should contain at least 8 characters, including uppercase letters, lowercase letters, numbers, and special characters (e.g. @, $, !, %, *, ?, &)',
157
162
  iveGotIt: 'I\'ve got it',
163
+ totalItems: 'Total',
158
164
  };
package/src/locales/pl.js CHANGED
@@ -133,7 +133,7 @@ module.exports = {
133
133
  cancelSelected: 'Anuluj zaznaczenie',
134
134
  },
135
135
  pagination: {
136
- itemsPerPage: 'Elementów na stronę',
136
+ itemsPerPage: 'Na stronie',
137
137
  previous: 'Poprzednia',
138
138
  next: 'Następna',
139
139
  },
@@ -155,4 +155,5 @@ module.exports = {
155
155
  },
156
156
  passwordValidation: 'Hasło powinno zawierać co najmniej 8 znaków, w tym wielkie i małe litery, cyfry oraz znaki specjalne (np. @, $, !, %, *, ?, &)',
157
157
  iveGotIt: 'Rozumiem',
158
+ totalItems: 'Całkowity',
158
159
  };
package/src/locales/uk.js CHANGED
@@ -28,8 +28,7 @@ module.exports = {
28
28
  pleaseEnterValidURL: 'Будь ласка, введіть дійсний URL',
29
29
  pleaseEnteraValidHex: 'Будь ласка, введіть дійсний код кольору',
30
30
  passwordsDontMatch: 'Паролі не збігаються',
31
- thisFieldMustBeAIntegerFormatZero: 'Це поле має містити число',
32
- thisFieldMustBeANumberFormatZero: 'Це поле має містити число (формат 0.00)',
31
+ thisFieldMustBeANumberFormatZero: 'Це поле має містити числом (формат 0.00)',
33
32
  theValueMustBeGreaterThanOrEqualToMinAndMax: 'Значення має бути більше або дорівнювати {min} і менше або дорівнювати {max}',
34
33
  theValueMustBeGreaterThanOrEqualToMin: 'Значення має бути більше або дорівнювати {min}',
35
34
  mustBeLessThanMax: 'Має бути менше ніж {max}',
@@ -57,6 +56,10 @@ module.exports = {
57
56
  addMore: 'Додати ще',
58
57
  close: 'Закрити',
59
58
  wholeYear: 'Весь рік',
59
+ atLeastOneUppercaseLetterRequired: 'Має бути принаймні одна велика літера',
60
+ atLeastOneLowercaseLetterRequired: 'Має бути принаймні одна мала літера',
61
+ atLeastOneNumberRequired: 'Має бути принаймні одна цифра',
62
+ atLeastOneSpecialCharacterRequired: 'Має бути принаймні один спеціальний символ (@,$,!,%,*,?,&)',
60
63
 
61
64
  select: {
62
65
  loading: 'Завантаження...',
@@ -105,7 +108,7 @@ module.exports = {
105
108
  next: 'Наступна',
106
109
  },
107
110
  table: {
108
- new: 'Додати',
111
+ new: 'Додати рядок',
109
112
  noResults: 'Немає записів',
110
113
  sortAscending: 'Сортувати за зростанням',
111
114
  sortDescending: 'Сортувати за спаданням',
@@ -153,7 +156,5 @@ module.exports = {
153
156
  from: 'Від',
154
157
  to: 'До',
155
158
  value: 'Значення',
156
- },
157
- passwordValidation: 'Пароль має містити принаймні 8 символів, включаючи великі літери, малі літери, цифри та спеціальні символи (@, $, !, %, *, ?, &)',
158
- iveGotIt: 'Зрозуміло',
159
+ }
159
160
  };