@itfin/components 1.4.36 → 1.5.0

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 (204) hide show
  1. package/package.json +17 -20
  2. package/src/ITFSettings.js +6 -0
  3. package/src/assets/scss/_css_variables.scss +2 -7
  4. package/src/assets/scss/_dark-theme.scss +2 -12
  5. package/src/assets/scss/_variables.scss +34 -9
  6. package/src/assets/scss/components/_button.scss +147 -10
  7. package/src/assets/scss/components/_checkbox.scss +9 -0
  8. package/src/assets/scss/components/_datepicker.scss +3 -3
  9. package/src/assets/scss/components/_pagination.scss +4 -1
  10. package/src/assets/scss/components/_popover.scss +22 -0
  11. package/src/assets/scss/components/_segmeneted-control.scss +19 -8
  12. package/src/assets/scss/components/_select.scss +6 -8
  13. package/src/assets/scss/components/_text-field.scss +27 -11
  14. package/src/assets/scss/components/select/_dropdown-menu.scss +1 -0
  15. package/src/assets/scss/components/select/_dropdown-toggle.scss +0 -1
  16. package/src/assets/scss/directives/tooltip.scss +10 -5
  17. package/src/assets/scss/main.scss +48 -0
  18. package/src/components/alert/AlertBanner.vue +75 -0
  19. package/src/components/app/App.vue +6 -3
  20. package/src/components/button/Button.vue +3 -1
  21. package/src/components/button/NativeButton.js +4 -0
  22. package/src/components/button/index.stories.js +2 -2
  23. package/src/components/checkbox/Checkbox.vue +2 -1
  24. package/src/components/checkbox/RadioBox.vue +13 -7
  25. package/src/components/copyToClipboard/CopyToClipboard.vue +4 -1
  26. package/src/components/customize/PropertiesList.vue +0 -2
  27. package/src/components/customize/PropertiesPopupMenu.vue +1 -1
  28. package/src/components/customize/PropertyItem.vue +6 -24
  29. package/src/components/datepicker/DatePicker.vue +3 -1
  30. package/src/components/datepicker/DatePickerInline.vue +2 -2
  31. package/src/components/datepicker/DateRangePickerInline.vue +6 -1
  32. package/src/components/dropdown/Dropdown.vue +1 -1
  33. package/src/components/dropdown/DropdownMenu.vue +1 -1
  34. package/src/components/editable/EditButton.vue +1 -1
  35. package/src/components/filter/FilterBadge.vue +20 -1
  36. package/src/components/filter/FilterFacetsList.vue +67 -13
  37. package/src/components/filter/FilterPanel.vue +8 -4
  38. package/src/components/filter/NewFilter.vue +305 -0
  39. package/src/components/form/Label.vue +5 -5
  40. package/src/components/icon/components/nomi-ai-alt.vue +5 -0
  41. package/src/components/icon/components/nomi-arrow-right.vue +4 -0
  42. package/src/components/icon/components/nomi-bell.vue +5 -0
  43. package/src/components/icon/components/nomi-calendar-payment.vue +10 -0
  44. package/src/components/icon/components/nomi-card-plus.vue +1 -0
  45. package/src/components/icon/components/nomi-cash-provider.vue +9 -0
  46. package/src/components/icon/components/nomi-cash-repeat.vue +6 -0
  47. package/src/components/icon/components/nomi-category-edit.vue +5 -0
  48. package/src/components/icon/components/nomi-chavron-up.vue +4 -0
  49. package/src/components/icon/components/nomi-chavron_down.vue +4 -0
  50. package/src/components/icon/components/nomi-chavron_up.vue +4 -0
  51. package/src/components/icon/components/nomi-chevron-up.vue +4 -0
  52. package/src/components/icon/components/nomi-exit-right.vue +4 -0
  53. package/src/components/icon/components/nomi-help.vue +2 -3
  54. package/src/components/icon/components/nomi-history.vue +7 -0
  55. package/src/components/icon/components/nomi-lock.vue +1 -1
  56. package/src/components/icon/components/nomi-pen-alt.vue +4 -0
  57. package/src/components/icon/components/nomi-project.vue +2 -2
  58. package/src/components/icon/components/nomi-refresh-off.vue +4 -0
  59. package/src/components/icon/components/nomi-refresh.vue +4 -0
  60. package/src/components/icon/components/nomi-scissors.vue +1 -1
  61. package/src/components/icon/components/nomi-start.vue +28 -0
  62. package/src/components/icon/components/nomi-table-view.vue +1 -4
  63. package/src/components/icon/components/nomi-transactions-delete.vue +5 -0
  64. package/src/components/icon/components/nomi-type-array.vue +6 -0
  65. package/src/components/icon/components/nomi-type-boolean.vue +5 -0
  66. package/src/components/icon/components/nomi-type-date.vue +4 -0
  67. package/src/components/icon/components/nomi-type-null.vue +4 -0
  68. package/src/components/icon/components/nomi-type-number.vue +4 -0
  69. package/src/components/icon/components/nomi-type-object.vue +4 -0
  70. package/src/components/icon/components/nomi-type-string.vue +4 -0
  71. package/src/components/icon/components/nomi-unarchive.vue +17 -0
  72. package/src/components/icon/components/nomi-unlink.vue +10 -0
  73. package/src/components/icon/components/nomi-user.vue +3 -3
  74. package/src/components/icon/components/nomi-warning-triangle.vue +6 -0
  75. package/src/components/icon/components/nomi-warning_triangle_filled.vue +6 -0
  76. package/src/components/icon/convert-icons.js +3 -0
  77. package/src/components/icon/icons.js +390 -312
  78. package/src/components/icon/new-icons/ai-alt.svg +4 -0
  79. package/src/components/icon/new-icons/arrow-right-alt.svg +3 -0
  80. package/src/components/icon/new-icons/arrow-right.svg +3 -0
  81. package/src/components/icon/new-icons/arrow_left.svg +3 -0
  82. package/src/components/icon/new-icons/automation.svg +4 -0
  83. package/src/components/icon/new-icons/balance.svg +3 -0
  84. package/src/components/icon/new-icons/balance_turnover.svg +4 -0
  85. package/src/components/icon/new-icons/bar-horizontal.svg +6 -0
  86. package/src/components/icon/new-icons/bell.svg +4 -0
  87. package/src/components/icon/new-icons/calendar-payment.svg +9 -0
  88. package/src/components/icon/new-icons/card-plus.svg +1 -0
  89. package/src/components/icon/new-icons/cash-provider.svg +8 -0
  90. package/src/components/icon/new-icons/cash-repeat.svg +5 -0
  91. package/src/components/icon/new-icons/cash.svg +3 -0
  92. package/src/components/icon/new-icons/cashflow.svg +3 -0
  93. package/src/components/icon/new-icons/category-edit.svg +4 -0
  94. package/src/components/icon/new-icons/category.svg +4 -0
  95. package/src/components/icon/new-icons/category_alt.svg +3 -0
  96. package/src/components/icon/new-icons/chart-bars.svg +5 -0
  97. package/src/components/icon/new-icons/chart-donut.svg +3 -0
  98. package/src/components/icon/new-icons/chart-funnel.svg +5 -0
  99. package/src/components/icon/new-icons/chart-kpi.svg +7 -0
  100. package/src/components/icon/new-icons/chart-line.svg +4 -0
  101. package/src/components/icon/new-icons/chart-lines.svg +5 -0
  102. package/src/components/icon/new-icons/check-alt.svg +3 -0
  103. package/src/components/icon/new-icons/check.svg +3 -0
  104. package/src/components/icon/new-icons/chevron-down.svg +3 -0
  105. package/src/components/icon/new-icons/chevron-left.svg +3 -0
  106. package/src/components/icon/new-icons/chevron-right.svg +3 -0
  107. package/src/components/icon/new-icons/chevron-up.svg +3 -0
  108. package/src/components/icon/new-icons/collapse.svg +6 -0
  109. package/src/components/icon/new-icons/control-panel.svg +7 -0
  110. package/src/components/icon/new-icons/credit.svg +3 -0
  111. package/src/components/icon/new-icons/currencies.svg +3 -0
  112. package/src/components/icon/new-icons/debt.svg +3 -0
  113. package/src/components/icon/new-icons/demo.svg +6 -0
  114. package/src/components/icon/new-icons/dev.svg +3 -0
  115. package/src/components/icon/new-icons/dots.svg +5 -0
  116. package/src/components/icon/new-icons/duplicate.svg +4 -0
  117. package/src/components/icon/new-icons/exit-right.svg +3 -0
  118. package/src/components/icon/new-icons/export.svg +3 -0
  119. package/src/components/icon/new-icons/file.svg +3 -0
  120. package/src/components/icon/new-icons/folder.svg +3 -0
  121. package/src/components/icon/new-icons/goods-turnover.svg +3 -0
  122. package/src/components/icon/new-icons/goods.svg +4 -0
  123. package/src/components/icon/new-icons/help-alt.svg +3 -0
  124. package/src/components/icon/new-icons/help.svg +2 -3
  125. package/src/components/icon/new-icons/history.svg +6 -0
  126. package/src/components/icon/new-icons/integration.svg +3 -0
  127. package/src/components/icon/new-icons/link.svg +5 -0
  128. package/src/components/icon/new-icons/lock.svg +1 -1
  129. package/src/components/icon/new-icons/menu.svg +5 -0
  130. package/src/components/icon/new-icons/minus.svg +3 -0
  131. package/src/components/icon/new-icons/payment_calendar.svg +3 -0
  132. package/src/components/icon/new-icons/pc.svg +3 -0
  133. package/src/components/icon/new-icons/pen-alt.svg +3 -0
  134. package/src/components/icon/new-icons/planFact.svg +4 -0
  135. package/src/components/icon/new-icons/pnl.svg +7 -0
  136. package/src/components/icon/new-icons/project.svg +2 -2
  137. package/src/components/icon/new-icons/project_alt.svg +3 -0
  138. package/src/components/icon/new-icons/project_alt2.svg +3 -0
  139. package/src/components/icon/new-icons/promo.svg +3 -0
  140. package/src/components/icon/new-icons/refresh-off.svg +3 -0
  141. package/src/components/icon/new-icons/refresh.svg +3 -0
  142. package/src/components/icon/new-icons/scissors.svg +1 -1
  143. package/src/components/icon/new-icons/segment.svg +3 -0
  144. package/src/components/icon/new-icons/start.svg +27 -0
  145. package/src/components/icon/new-icons/strongbox.svg +3 -0
  146. package/src/components/icon/new-icons/subscription.svg +3 -0
  147. package/src/components/icon/new-icons/table-view.svg +1 -4
  148. package/src/components/icon/new-icons/time.svg +3 -0
  149. package/src/components/icon/new-icons/transactions_alt.svg +6 -0
  150. package/src/components/icon/new-icons/transactions_delete.svg +4 -0
  151. package/src/components/icon/new-icons/type-array.svg +5 -0
  152. package/src/components/icon/new-icons/type-boolean.svg +4 -0
  153. package/src/components/icon/new-icons/type-date.svg +3 -0
  154. package/src/components/icon/new-icons/type-null.svg +3 -0
  155. package/src/components/icon/new-icons/type-number.svg +3 -0
  156. package/src/components/icon/new-icons/type-object.svg +3 -0
  157. package/src/components/icon/new-icons/type-string.svg +3 -0
  158. package/src/components/icon/new-icons/types.svg +6 -0
  159. package/src/components/icon/new-icons/unarchive.svg +16 -0
  160. package/src/components/icon/new-icons/unlink.svg +9 -0
  161. package/src/components/icon/new-icons/user.svg +3 -3
  162. package/src/components/icon/new-icons/user_plus.svg +10 -0
  163. package/src/components/icon/new-icons/warehouse.svg +3 -0
  164. package/src/components/icon/new-icons/warning_triangle.svg +5 -0
  165. package/src/components/icon/new-icons/warning_triangle_filled.svg +5 -0
  166. package/src/components/kanban/BoardCard.vue +1 -1
  167. package/src/components/kanban/BoardCardTimer.vue +1 -1
  168. package/src/components/modal/DeleteConfirmModal.vue +10 -6
  169. package/src/components/modal/ItemEditor.vue +1 -1
  170. package/src/components/modal/Modal.vue +1 -1
  171. package/src/components/overlay/SensitiveOverlay.vue +2 -4
  172. package/src/components/panels/Panel.vue +110 -23
  173. package/src/components/panels/PanelItemEdit.vue +8 -6
  174. package/src/components/panels/PanelList.vue +163 -40
  175. package/src/components/panels/helpers.ts +29 -11
  176. package/src/components/popover/Popover.vue +105 -22
  177. package/src/components/segmented-control/SegmentedControl.vue +9 -3
  178. package/src/components/sortable/draggable.js +1 -1
  179. package/src/components/table/Table2.vue +68 -67
  180. package/src/components/table/TableBody.vue +17 -22
  181. package/src/components/table/TableGroup.vue +40 -24
  182. package/src/components/table/TableHeader.vue +86 -81
  183. package/src/components/table/TableRowToggle.vue +1 -9
  184. package/src/components/table/TableRows.vue +49 -55
  185. package/src/components/table/mobile.js +4 -0
  186. package/src/components/table/table2.scss +34 -15
  187. package/src/components/text-field/MoneyField.vue +10 -4
  188. package/src/components/text-field/TextField.vue +17 -8
  189. package/src/components/tree/TreeEditor.vue +3 -2
  190. package/src/components/view/View.vue +73 -208
  191. package/src/directives/appendToBody.js +1 -0
  192. package/src/helpers/validators.js +9 -35
  193. package/src/helpers/validators.spec.js +11 -48
  194. package/src/locales/en.js +4 -6
  195. package/src/locales/pl.js +158 -0
  196. package/src/locales/uk.js +6 -7
  197. package/src/components/icon/components/nomi-calendar-view.vue +0 -4
  198. package/src/components/icon/components/nomi-kanban-view.vue +0 -6
  199. package/src/components/icon/components/nomi-list-view.vue +0 -7
  200. package/src/components/icon/components/nomi-table-config.vue +0 -9
  201. package/src/components/icon/new-icons/calendar-view.svg +0 -3
  202. package/src/components/icon/new-icons/kanban-view.svg +0 -5
  203. package/src/components/icon/new-icons/list-view.svg +0 -6
  204. package/src/components/icon/new-icons/table-config.svg +0 -8
@@ -1,17 +1,18 @@
1
1
  <template>
2
2
 
3
- <div class="itf-text-field input-group" :class="{ 'with-addon addon-start': prependIcon, 'with-addon addon-end': clearable }">
3
+ <div class="itf-text-field input-group form-control p-0" :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }">
4
4
  <slot name="addon">
5
- <div class="addon" v-if="prependIcon">
6
- <itf-icon :name="prependIcon"/>
5
+ <div class="input-group-text prepend text-muted" v-if="prependIcon">
6
+ <itf-icon :size="small ? 18 : 20" :name="prependIcon"/>
7
7
  </div>
8
8
  </slot>
9
9
 
10
10
  <input
11
+ :name="`text-field-${_uid}`"
11
12
  ref="input"
12
13
  autocomplete="off"
13
14
  :placeholder="placeholder"
14
- :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess(), 'form-control-sm': small }"
15
+ :class="{ 'form-control-sm': small }"
15
16
  class="itf-text-field__input form-control"
16
17
  :type="type"
17
18
  :value="value"
@@ -24,12 +25,16 @@
24
25
  @blur="$emit('blur', $event)"
25
26
  @focus="$emit('focus', $event)"
26
27
  @change="$emit('change', $event.target.value)"
28
+ :maxlength="maxlength"
27
29
  :min="min"
28
30
  :max="max"
29
31
  :step="step"
30
32
  />
33
+ <div class="input-group-text append text-muted" v-if="postfix">
34
+ {{ postfix }}
35
+ </div>
31
36
 
32
- <div class="addon-end" v-if="clearable && value">
37
+ <template v-if="clearable && value">
33
38
  <slot name="clear">
34
39
  <itf-button
35
40
  icon
@@ -39,7 +44,7 @@
39
44
  <itf-icon name="close" />
40
45
  </itf-button>
41
46
  </slot>
42
- </div>
47
+ </template>
43
48
  </div>
44
49
 
45
50
  </template>
@@ -64,10 +69,14 @@ class itfTextField extends Vue {
64
69
  @Prop() step;
65
70
  @Prop() min;
66
71
  @Prop() max;
72
+ @Prop() maxlength;
73
+ @Prop() postfix;
67
74
  @Prop(Boolean) clearable;
68
75
  @Prop(Boolean) disabled;
69
76
  @Prop(Boolean) readonly;
70
77
  @Prop(Boolean) small;
78
+ @Prop({ type: Boolean, default: undefined }) invalid;
79
+ @Prop({ type: Boolean, default: undefined }) valid;
71
80
  @Prop({ type: String, default: 'text' }) type;
72
81
  @Prop({ type: [Number, String], default: 0 }) delayInput;
73
82
 
@@ -78,11 +87,11 @@ class itfTextField extends Vue {
78
87
  }
79
88
 
80
89
  isInvalid() {
81
- return this.itemLabel && this.itemLabel.isHasError();
90
+ return typeof this.invalid !== 'undefined' ? this.invalid : (this.itemLabel && this.itemLabel.isHasError());
82
91
  }
83
92
 
84
93
  isSuccess() {
85
- return this.itemLabel && this.itemLabel.isHasSuccess();
94
+ return typeof this.valid !== 'undefined' ? this.valid : (this.itemLabel && this.itemLabel.isHasSuccess());
86
95
  }
87
96
 
88
97
  insertTextToCurrentPosition(text) {
@@ -25,10 +25,11 @@
25
25
  @keyup.enter="(e)=>{$emit('click',{event:e,ele:e.target,node,knode})}"
26
26
  @click="(e)=>{$emit('click',{event:e,ele:e.target,node,knode})}"
27
27
  >
28
- <div v-if="node.level >= toggleStartLevel" class="itf-tree-editor_node_toggle ps-2">
28
+ <div v-if="node.level >= toggleStartLevel" class="itf-tree-editor_node_toggle">
29
29
  <itf-button
30
- v-if="autoToggleTree && node.level >= toggleStartLevel && node.Children && node.Children.length > 0"
30
+ v-if="autoToggleTree && node.level >= toggleStartLevel && node[itemChildren] && node[itemChildren].length > 0"
31
31
  small
32
+ icon
32
33
  @click.stop="toggleNode($event, node, knode)"
33
34
  >
34
35
  <itf-icon v-if="isNodeToggled(node)" name="plus" size="24" />
@@ -4,25 +4,26 @@
4
4
  <itf-filter-panel
5
5
  :search-placeholder="searchPlaceholder"
6
6
  search
7
- show-filter
7
+ :visible="!noFilters"
8
+ :filters-only="filtersOnly"
9
+ ref="filters"
10
+ :mini="panel.isMultiple()"
8
11
  class="py-2 px-3"
9
- :static-filters="filters"
10
12
  :endpoint="filtersEndpoint"
11
13
  :panel="panel"
12
14
  v-model="filter"
13
15
  @loaded="onFilterSet($event, true)"
14
16
  @change="onFilterSet($event, false)"
15
- @set-table-schema="setTableSchema"
16
17
  >
17
18
  <template #after-filter-btn>
18
- <itf-dropdown v-if="$refs.table && tableSchema" shadow append-to-context :button-options="{ default: true, icon: true }" class="h-100" autoclose="outside">
19
+ <itf-dropdown v-if="$refs.table && schema" shadow append-to-context :button-options="{ default: true, icon: true }" class="h-100" autoclose="outside">
19
20
  <template #button>
20
- <itf-icon new name="table-config" />
21
+ <itf-icon new name="table-view" />
21
22
  </template>
22
23
  <div class="dropdown-header">
23
24
  {{ $t('components.table.columns') }}
24
25
  </div>
25
- <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">
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">
26
27
  <div class="d-flex align-items-center gap-1">
27
28
  <itf-icon v-if="property.icon" :size="24" new :name="property.icon" />
28
29
  {{getTitle(property.title)}}
@@ -41,101 +42,61 @@
41
42
  </div>
42
43
  </a>
43
44
  </itf-dropdown>
44
- <itf-dropdown v-if="downloadEndpoint" shadow append-to-context :button-options="{ default: true, small: true }" class="h-100" autoclose="outside">
45
- <template #button>
46
- <itf-icon name="download"/>
47
- {{ $t('export') }}
48
- </template>
49
- <a v-for="(item, n) in getDownloadLinks()" target="_blank" :key="n" :href="item.link" class="dropdown-item">
50
- {{item.title}}
51
- </a>
52
- </itf-dropdown>
53
-
54
- <slot name="before-tabs"></slot>
55
-
56
- <itf-segmented-control
57
- v-if="tabs.length > 1"
58
- class="small"
59
- v-model="currentTab"
60
- item-key="value"
61
- :items="tabs"
62
- >
63
- <template #item="{ item }">
64
- <div class="d-flex align-items-center">
65
- <itf-icon class="text-muted" new :name="item.icon" />
66
- {{item.text}}
67
- </div>
68
- </template>
69
- </itf-segmented-control>
70
45
  </template>
71
46
  </itf-filter-panel>
72
47
 
73
- <div v-if="currentTab === 'list'" class="position-relative flex-grow-1">
74
- <div class="position-absolute" style="top: 0; left: 0; right: 0; bottom: 0; overflow: auto;">
75
- <slot name="list-view" :items="items" :loading="loading"></slot>
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
+ :state-name="stateName"
61
+ id-property="id"
62
+ :rows="preparedItems"
63
+ :schema="schema"
64
+ :sorting="sorting"
65
+ :active="activeIds"
66
+ :show-actions="showActions"
67
+ v-model="selectedIds"
68
+ @row-click="$emit('open', $event)"
69
+ @update:sorting="updateSorting($event)"
70
+ >
71
+ <template v-for="(_, name) in $slots" #[name]="slotData">
72
+ <slot :name="name" v-bind="slotData || {}"/>
73
+ </template>
74
+ <template v-for="(_, name) in $scopedSlots" #[name]="slotData">
75
+ <slot :name="name" v-bind="slotData || {}"/>
76
+ </template>
77
+ </itf-table>
76
78
  </div>
77
79
  </div>
78
- <slot v-else-if="currentTab === 'board'" name="kanban-view"></slot>
79
- <slot v-else-if="currentTab === 'calendar'" name="calendar-view"></slot>
80
- <slot v-else name="table-view">
81
- <div class="flex-grow-1 px-3 d-flex flex-column">
82
- <div class="position-relative flex-grow-1">
83
- <itf-table
84
- ref="table"
85
- style="--shadow-area-width: 0px;"
86
- absolute
87
- striped
88
- clickable
89
- column-sorting
90
- column-resizing
91
- :indicator-type="indicatorType"
92
- class="permanent-checkboxes"
93
- :state-name="stateName"
94
- id-property="id"
95
- :sort-as-string="sortAsString"
96
- :rows="items"
97
- :group-by="groupBy"
98
- :schema="tableSchema"
99
- :sorting.sync="sorting"
100
- :active="activeIds"
101
- :no-select-all="noSelectAll"
102
- :show-actions="showActions"
103
- :indicator-width="indicatorWidth"
104
- v-model="selectedIds"
105
- @row-click="$emit('open', $event)"
106
- @update:sorting="updateSorting($event)"
107
- >
108
- <template v-for="(_, name) in $slots" #[name]="slotData">
109
- <slot :name="name" v-bind="slotData || {}"/>
110
- </template>
111
- <template v-for="(_, name) in $scopedSlots" #[name]="slotData">
112
- <slot :name="name" v-bind="slotData || {}"/>
113
- </template>
114
- </itf-table>
115
- </div>
116
- </div>
117
- </slot>
118
80
 
119
81
  <itf-pagination
120
82
  class="my-2 px-3"
121
- v-if="showPagination"
122
83
  show-size
123
84
  :size="size"
124
- :items="items"
85
+ :items="preparedItems"
125
86
  :pages="countPages"
126
87
  :value="page"
127
88
  @input="updatePage($event)"
128
89
  @per-page="updateSizePerPage($event)"
129
90
  >
130
91
  <template #center>
131
- <slot name="pagination-center" :totals="totals" />
92
+ <slot name="pagination-center" />
132
93
  </template>
133
94
  </itf-pagination>
134
95
  </div>
135
96
 
136
97
  </template>
137
98
  <script>
138
- import {Vue, ModelSync, Component, Prop, Inject, PropSync, Watch} from 'vue-property-decorator';
99
+ import { Vue, ModelSync, Component, Prop, Inject } from 'vue-property-decorator';
139
100
  import loading from '../../directives/loading';
140
101
  import itfTable from '../table/Table2.vue';
141
102
  import itfFilterPanel from '../filter/FilterPanel.vue';
@@ -143,16 +104,11 @@ import itfPagination from '../pagination/Pagination2.vue';
143
104
  import itfTableBody from "../table/TableBody.vue";
144
105
  import itfIcon from "../icon/Icon.vue";
145
106
  import itfDropdown from "../dropdown/Dropdown.vue";
146
- import itfSegmentedControl from '../segmented-control/SegmentedControl.vue';
147
- import itfButton from '@itfin/components/src/components/button/Button.vue';
148
107
 
149
108
  export default @Component({
150
109
  name: 'itfView',
151
110
  components: {
152
- itfButton,
153
- itfSegmentedControl,
154
- itfDropdown,
155
- itfIcon,
111
+ itfDropdown, itfIcon,
156
112
  itfPagination,
157
113
  itfFilterPanel,
158
114
  itfTableBody,
@@ -167,32 +123,24 @@ class itfView extends Vue {
167
123
  @ModelSync('value', 'input') selectedIds;
168
124
 
169
125
  @Prop({ type: Boolean }) loading;
126
+ @Prop({ type: Boolean }) filtersOnly;
127
+ @Prop({ type: Boolean }) noFilters;
170
128
  @Prop({ type: Array }) filters;
171
- @Prop({ type: Object }) schema;
129
+ @Prop({ type: Object, required: true }) schema;
130
+ // @Prop({ default: 20 }) size;
131
+ // @Prop({ default: 1 }) page;
172
132
  @Prop(String) defaultSorting;
173
133
  @Prop(String) endpoint;
174
134
  @Prop(String) filtersEndpoint;
175
135
  @Prop(String) itemsKey;
176
- @Prop({ type: String, default: null }) groupBy;
177
- @Prop({ type: String, default: null }) downloadEndpoint; // префікс апі для завантаження
178
- @Prop({ type: String, default: 'totals' }) totalsKey;
179
136
  @Prop(String) panelKey;
180
137
  @Prop(String) stateName;
181
138
  @Prop({ type: String, default: 'checkbox' }) indicatorType;
182
- @Prop({ type: String, default: 'list' }) tab;
183
139
  @Prop({ type: String, default () { return this.$t('components.table.search'); } }) searchPlaceholder;
184
140
  @Prop() panel;
141
+ @Prop() hardFilter; // встановлює жорсткий фільтр, наприклад, треба завжди показувати лише по контрагенту
185
142
  @Prop(Boolean) showActions;
186
- @Prop({ type: Boolean, default: true }) showPagination;
187
- @Prop(Boolean) listViewEnabled;
188
- @Prop(Boolean) kanbanViewEnabled;
189
- @Prop(Boolean) calendarViewEnabled;
190
- @Prop(Boolean) noSelectAll;
191
- @Prop({ type: Boolean, default: true }) tableViewEnabled;
192
- @Prop(Boolean) sortAsString;
193
- @Prop(Boolean) oldFormat;
194
- @Prop({ default: 45 }) indicatorWidth;
195
- @Prop({type: Function, default: null }) onSplitSlectedIds // якщо потрібно розділяти вибрані рядки в таблиці на дві групи
143
+ @Prop({ type: Array, default: () => [] }) disabledIds;
196
144
 
197
145
  page = 1;
198
146
  total = 0;
@@ -202,74 +150,10 @@ class itfView extends Vue {
202
150
  filter = {};
203
151
  loadingData = false;
204
152
  activeIds = [];
205
- tableColumns = null;
206
- totals = null;
207
-
208
- @Watch('selectedIds', { deep: true, immediate: true })
209
- updateSelectedIds() {
210
- if(this.onSplitSlectedIds && this.items.length && this.selectedIds.length) {
211
- this.onSplitSlectedIds(this.selectedIds, this.items);
212
- }
213
- }
214
-
215
- getDownloadLinks() {
216
- const state = this.$refs.table ? this.$refs.table.getTableState() : null;
217
- const filter = { ...this.filter };
218
- const sorting = this.sorting;
219
- const filterableColumnsNames = (state?.columns ?? []).filter(column => column.visible).map(column => column.property);
220
-
221
- const filterWithValue = Object.fromEntries(Object.entries(filter).filter(([_, value]) => typeof value !== 'undefined'));
222
- const params = {
223
- ...filterWithValue,
224
- sort: sorting
225
- };
226
- if (filterableColumnsNames.length) {
227
- params.columns = filterableColumnsNames.join(',')
228
- }
229
- const xlsQueryParams = new URLSearchParams({ ...params, format: 'xlsx' }).toString();
230
- const csvQueryParams = new URLSearchParams({ ...params, format: 'csv' }).toString();
231
- return [
232
- { title: 'Excel (xlsx)', link: `${this.downloadEndpoint}?${xlsQueryParams}` },
233
- { title: 'Plain text (csv)', link: `${this.downloadEndpoint}?${csvQueryParams}` },
234
- ];
235
- }
236
153
 
237
- get currentTab() {
238
- return this.tab;
239
- }
240
-
241
- set currentTab(val) {
242
- this.$emit('update:tab', val);
243
- this.setPanelPayload({ tab: val });
244
- }
245
-
246
- get tabs() {
247
- const views = [];
248
- if (this.listViewEnabled) {
249
- views.push({ value: 'list', text: this.$t('list'), icon: 'list-view' });
250
- }
251
- if (this.kanbanViewEnabled) {
252
- views.push({ value: 'board', text: this.$t('board'), icon: 'kanban-view' });
253
- }
254
- if (this.calendarViewEnabled) {
255
- views.push({ value: 'calendar', text: this.$t('calendar'), icon: 'calendar-view' });
256
- }
257
- if (this.tableViewEnabled) {
258
- views.push({ value: 'table', text: this.$t('table'), icon: 'table-view' });
259
- }
260
- return views;
261
- }
262
-
263
- get tableSchema() {
264
- if (this.tableColumns) {
265
- return {
266
- properties: this.tableColumns
267
- }
268
- } else if (this.schema) {
269
- return this.schema
270
- } else {
271
- return {}
272
- }
154
+ get preparedItems() {
155
+ const disabledIdsSet = new Set(this.disabledIds);
156
+ return this.items.map(item => ({ ...item, isDisabled: disabledIdsSet.has(item.id) }));
273
157
  }
274
158
 
275
159
  created() {
@@ -291,42 +175,35 @@ class itfView extends Vue {
291
175
  }
292
176
  }
293
177
 
294
- async loadData() {
178
+ async loadData(reloadFilters = false) {
295
179
  if (!this.endpoint) {
296
180
  return;
297
181
  }
182
+ if (reloadFilters) {
183
+ this.loadFilters();
184
+ }
298
185
  this.$emit('load', this.filter);
299
186
  this.loadingData = true;
300
187
  await this.$try(async () => {
301
- let filter = { ...this.filter };
302
- if (this.oldFormat) {
303
- filter = Object.keys(filter).reduce((acc, key) => {
304
- acc[`filter[${key}]`] = filter[key];
305
- return acc;
306
- }, {})
307
- }
308
- const { data, headers } = await this.$axios.get(this.endpoint, {
188
+ const res = await this.$axios.$get(this.endpoint, {
189
+ preventRaceCondition: true,
309
190
  params: {
310
- ...filter,
191
+ ...Object.assign(this.filter ?? {}, this.hardFilter ?? {}),
311
192
  page: this.page,
312
193
  size: this.size,
313
194
  sort: this.sorting
314
195
  }
315
196
  });
316
- if (this.oldFormat) {
317
- this.items = data;
318
- this.page = Number(headers['x-page'] ?? 1);
319
- this.total = Number(headers['x-count'] ?? 0);
320
- this.size = Number(headers['x-size'] ?? 20);
321
- } else {
322
- this.items = data[this.itemsKey];
323
- this.totals = data[this.totalsKey];
324
- this.page = data.meta.page;
325
- this.total = data.meta.total;
326
- this.size = data.meta.size;
327
- }
197
+ this.selectedIds = [];
198
+ this.items = res[this.itemsKey];
199
+ this.page = res.meta.page;
200
+ this.total = res.meta.total;
201
+ this.size = res.meta.size;
202
+ this.$emit('update:items', this.items);
203
+ this.loadingData = false;
204
+ }, () => {
205
+ this.loadingData = false;
328
206
  });
329
- this.loadingData = false;
330
207
  this.$emit('loaded', this.filter);
331
208
  }
332
209
 
@@ -349,16 +226,14 @@ class itfView extends Vue {
349
226
 
350
227
  updatePage (val) {
351
228
  this.page = val;
352
- this.selectedIds = [];
353
- this.setPanelPayload({ page: val });
229
+ this.setPanelPayload({ page: val === 1 ? null : val });
354
230
  this.loadData();
355
231
  }
356
232
 
357
233
  updateSizePerPage (val) {
358
234
  this.page = 1;
359
235
  this.size = val;
360
- this.selectedIds = [];
361
- this.setPanelPayload({ page: 1, size: val });
236
+ this.setPanelPayload({ page: null, size: val });
362
237
  this.loadData();
363
238
  localStorage.setItem('sizePerPage', val);
364
239
  }
@@ -394,20 +269,10 @@ class itfView extends Vue {
394
269
  this.loadData();
395
270
  }
396
271
 
397
- setTableSchema(tableSchema) {
398
- this.tableColumns = tableSchema;
399
- }
400
-
401
- getSorting() {
402
- return this.sorting;
403
- }
404
-
405
- getFilter() {
406
- return this.filter;
407
- }
408
-
409
- getTableState() {
410
- return this.$refs.table ? this.$refs.table.getTableState() : null;
272
+ loadFilters() {
273
+ if (this.filtersEndpoint) {
274
+ this.$refs.filters?.loadData();
275
+ }
411
276
  }
412
277
  }
413
278
  </script>
@@ -30,6 +30,7 @@ export default {
30
30
  width: width + 'px',
31
31
  left: left + 'px',
32
32
  top: (top + height) + 'px',
33
+ root: rootEl
33
34
  });
34
35
  }
35
36
 
@@ -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}$/;
11
12
  const DOUBLE_REGEXP = /^-?\d{0,11}(\.\d{0,8}){0,1}$/;
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;
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;
13
14
  const EMAIL_LIST_REGEXP = /^(\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]{2,4}\s*?,?\s*?)+$/g;
14
15
  const HEX_REGEXP = /[0-9A-Fa-f]{6}/;
15
- const PASSWORD_MIN_LENGTH = 8;
16
- const PASSWORD_MAX_LENGTH = 50;
16
+ const PASSWORD_REGEX = /^(?=.*\p{Lu})(?=.*\p{Ll})(?=.*\d)(?=.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]).{8,}$/u;
17
17
 
18
18
  const STANDART_DATE_FORMAT = 'yyyy-MM-dd';
19
19
 
@@ -67,6 +67,10 @@ 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
+
70
74
  export function doubleValidation (message) {
71
75
  return (v, $t = (s) => s) => !v || !!(v + '').match(DOUBLE_REGEXP) || (message || $t('components.thisFieldMustBeANumberFormatZero'));
72
76
  }
@@ -184,38 +188,8 @@ export function dateSameOrBeforeValidation (dateCompare, message) {
184
188
  };
185
189
  }
186
190
 
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) {
191
+ export function passwordValidation(message) {
206
192
  return (v, $t) => {
207
- return !isEmpty(v) && /[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/.test(v + '') || (message || $t('components.atLeastOneSpecialCharacterRequired'));
193
+ return !isEmpty(v) && PASSWORD_REGEX.test(v.trim() + '') || (message || $t('components.passwordValidation'));
208
194
  };
209
195
  }
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,10 +7,7 @@ import {
7
7
  dateAfterValidation,
8
8
  dateSameOrAfterValidation,
9
9
  dateSameOrBeforeValidation,
10
- hasUppercaseValidation,
11
- hasLowercaseValidation,
12
- hasDigitValidation,
13
- hasSpecialCharValidation,
10
+ passwordValidation,
14
11
  } from './validators';
15
12
 
16
13
  describe('Validators', () => {
@@ -86,50 +83,16 @@ describe('Validators', () => {
86
83
  expect(mediumTextValidation()('0'.repeat(16777216), $t)).toEqual('components.mediumTextLength');
87
84
  });
88
85
 
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
- });
125
86
 
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');
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);
134
97
  });
135
98
  });