@itfin/components 1.4.35 → 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 (202) 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 +1 -1
  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 +1 -2
  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 +39 -14
  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 +1 -1
  174. package/src/components/panels/PanelList.vue +65 -11
  175. package/src/components/popover/Popover.vue +105 -22
  176. package/src/components/segmented-control/SegmentedControl.vue +9 -3
  177. package/src/components/sortable/draggable.js +1 -1
  178. package/src/components/table/Table2.vue +8 -3
  179. package/src/components/table/TableBody.vue +17 -16
  180. package/src/components/table/TableGroup.vue +38 -12
  181. package/src/components/table/TableHeader.vue +83 -73
  182. package/src/components/table/TableRows.vue +15 -9
  183. package/src/components/table/mobile.js +4 -0
  184. package/src/components/table/table2.scss +9 -0
  185. package/src/components/text-field/MoneyField.vue +10 -4
  186. package/src/components/text-field/TextField.vue +17 -8
  187. package/src/components/tree/TreeEditor.vue +3 -2
  188. package/src/components/view/View.vue +86 -214
  189. package/src/directives/appendToBody.js +1 -0
  190. package/src/helpers/validators.js +9 -35
  191. package/src/helpers/validators.spec.js +11 -48
  192. package/src/locales/en.js +4 -6
  193. package/src/locales/pl.js +158 -0
  194. package/src/locales/uk.js +6 -7
  195. package/src/components/icon/components/nomi-calendar-view.vue +0 -4
  196. package/src/components/icon/components/nomi-kanban-view.vue +0 -6
  197. package/src/components/icon/components/nomi-list-view.vue +0 -7
  198. package/src/components/icon/components/nomi-table-config.vue +0 -9
  199. package/src/components/icon/new-icons/calendar-view.svg +0 -3
  200. package/src/components/icon/new-icons/kanban-view.svg +0 -5
  201. package/src/components/icon/new-icons/list-view.svg +0 -6
  202. package/src/components/icon/new-icons/table-config.svg +0 -8
@@ -10,6 +10,7 @@
10
10
  <template v-for="(panel, n) of panelsStack">
11
11
  <panel
12
12
  :key="n"
13
+ :ref="`panel-${panel.id}`"
13
14
  :index="n"
14
15
  :panel="panel"
15
16
  :title="panel.title"
@@ -27,6 +28,8 @@
27
28
  @collapse="collapsePanel(panel)"
28
29
  @close="closePanel(panel)"
29
30
  @open-menu="$emit('open-menu', panel.type, panel.payload)"
31
+ @onboarding-progress="$emit('onboarding-progress', $event)"
32
+ @onboarding-step-viewed="$emit('onboarding-step-viewed', $event)"
30
33
  >
31
34
  <template #before-header>
32
35
  <slot name="before-header" :panel="panel" :index="n" :payload="panel.payload"></slot>
@@ -163,7 +166,7 @@ $double-an-time: $an-time * 2;
163
166
  //}
164
167
  </style>
165
168
  <script lang="ts">
166
- import { Vue, Component, Prop } from 'vue-property-decorator';
169
+ import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
167
170
  import itfIcon from '../icon/Icon.vue';
168
171
  import Panel from './Panel.vue';
169
172
  import {hashToStack, stackToHash} from "@itfin/components/src/components/panels/helpers";
@@ -182,6 +185,7 @@ export interface IPanel {
182
185
  payload: any;
183
186
  nocard?: boolean;
184
187
  isCollapsed: boolean;
188
+ isExpanded: boolean;
185
189
  isCloseable: boolean;
186
190
  isAnimate: boolean;
187
191
  open: (type: string, visOptions: VisualOptions, payload: any) => void;
@@ -223,6 +227,17 @@ export default class PanelList extends Vue {
223
227
 
224
228
  nextId:number = 0;
225
229
 
230
+ @Watch('panels', { deep: true })
231
+ onPanelsPropChanged(newPanels: Record<string, Component>) {
232
+ this.panelsStack.forEach(stackedPanel => {
233
+ const newPanel = newPanels[stackedPanel.type];
234
+ if (newPanel && typeof newPanel.inAppOnboarding === 'function') {
235
+ const updatedOnboarding = newPanel.inAppOnboarding(this.$t.bind(this));
236
+ stackedPanel.inAppOnboarding = updatedOnboarding;
237
+ }
238
+ });
239
+ }
240
+
226
241
  created() {
227
242
  setRootPanelList(this);
228
243
  if (this.firstPanel) {
@@ -244,6 +259,7 @@ export default class PanelList extends Vue {
244
259
  const newStack = [...this.panelsStack];
245
260
  const index = newStack.findIndex(p => p.id === panel.id);
246
261
  newStack[index].isCollapsed = false;
262
+ newStack[index].isExpanded = true;
247
263
  this.panelsStack = newStack;
248
264
  this.ensureOnlyTwoOpenPanels(panel.id);
249
265
  this.setPanelHash();
@@ -265,10 +281,12 @@ export default class PanelList extends Vue {
265
281
  keepOpenIds.push(panel2.id);
266
282
  }
267
283
  if (keepOpenIds.length === 1) {
268
- if (newStack.length - 1 === indexKeep) {
269
- keepOpenIds.push(newStack[indexKeep - 1].id);
270
- } else {
271
- keepOpenIds.push(newStack[indexKeep + 1].id);
284
+ if (!openPanels.find(p => p.id === keepOpenId).isExpanded) {
285
+ if (newStack.length - 1 === indexKeep) {
286
+ keepOpenIds.push(newStack[indexKeep - 1].id);
287
+ } else {
288
+ keepOpenIds.push(newStack[indexKeep + 1].id);
289
+ }
272
290
  }
273
291
  }
274
292
  for (const panel of newStack) {
@@ -278,7 +296,7 @@ export default class PanelList extends Vue {
278
296
  this.panelsStack = newStack;
279
297
  }
280
298
 
281
- async internalOpenPanel(type: string, payload: any = {}, openIndex?: number, noEvents = false) {
299
+ async internalOpenPanel(type: string, payload: any = {}, openIndex?: number, noEvents = false, noExpand = false) {
282
300
  let panel = this.panels[type];
283
301
  if (!panel) {
284
302
  panel = await this.searchPanel(type, this.panels);
@@ -307,6 +325,7 @@ export default class PanelList extends Vue {
307
325
  isCollapsed: false,
308
326
  isCloseable: true,
309
327
  __events: {},
328
+ inAppOnboarding: panel.inAppOnboarding ? panel.inAppOnboarding(this.$t.bind(this)) : null,
310
329
  };
311
330
  if (!this.panelsStack.length || openIndex === 0) {
312
331
  newPanel.isCloseable = false;
@@ -322,7 +341,7 @@ export default class PanelList extends Vue {
322
341
  isAnimation = newStack.length === openIndex;
323
342
  newStack = newStack.slice(0, openIndex);
324
343
  }
325
- if (newStack.length > 0 && !newStack.find(p => !p.isCollapsed)) {
344
+ if (newStack.length > 0 && !newStack.find(p => !p.isCollapsed) && !noExpand) {
326
345
  // якщо немає відкритих панелей, то перша панель має бути розгорнута
327
346
  newStack[0].isCollapsed = false;
328
347
  }
@@ -369,6 +388,10 @@ export default class PanelList extends Vue {
369
388
  newPanel.payload = value;
370
389
  this.setPanelHash();
371
390
  }
391
+ newPanel.rebuildAllOnboardingPopovers = (refreshDelay?: number) => this.rebuildAllOnboardingPopovers(refreshDelay);
392
+ newPanel.rebuildPanelOnboardingPopovers = (panelId: string | number, refreshDelay?: number) => {
393
+ this.rebuildPanelOnboardingPopovers(panelId, refreshDelay);
394
+ }
372
395
  newStack.push(newPanel);
373
396
  this.panelsStack = newStack;
374
397
  this.ensureOnlyTwoOpenPanels(newPanel.id);
@@ -390,6 +413,7 @@ export default class PanelList extends Vue {
390
413
  async openPanel(type: string, payload: any, openIndex?: number) {
391
414
  await this.internalOpenPanel(type, payload, openIndex);
392
415
  this.setPanelHash();
416
+ if(openIndex) this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
393
417
  }
394
418
 
395
419
  emitEvent(event: string, ...args: any[]) {
@@ -411,13 +435,14 @@ export default class PanelList extends Vue {
411
435
  openPanel.isCollapsed = false;
412
436
  }
413
437
  const openPanelIndex = this.panelsStack.findIndex(p => p === openPanel);
414
- if (openPanelIndex > 0 && !openPanel?.permanentExpanded) {
438
+ if (openPanelIndex > 0 && !openPanel?.permanentExpanded && !openPanel.isExpanded) {
415
439
  this.panelsStack[openPanelIndex - 1].isCollapsed = false;
416
440
  }
417
441
  this.ensureOnlyTwoOpenPanels(openPanel.id);
418
442
  this.setPanelHash();
419
443
  this.emitEvent('panels.closed', panel);
420
444
  this.emitEvent('panels.changed', this.panelsStack);
445
+ this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
421
446
  }
422
447
 
423
448
  fullsizePanel(panel: IPanel) {
@@ -425,9 +450,13 @@ export default class PanelList extends Vue {
425
450
  for (const p of newStack) {
426
451
  p.isLastOpened = !p.isCollapsed && p !== panel;
427
452
  p.isCollapsed = p !== panel;
453
+ if (!p.isCollapsed) {
454
+ p.isExpanded = true;
455
+ }
428
456
  }
429
457
  this.panelsStack = newStack;
430
458
  this.setPanelHash();
459
+ this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
431
460
  }
432
461
 
433
462
  get isFullSize() {
@@ -438,9 +467,13 @@ export default class PanelList extends Vue {
438
467
  const newStack = [...this.panelsStack];
439
468
  const index = newStack.findIndex(p => p.id === panel.id);
440
469
  newStack[index].isCollapsed = false;
470
+ for (const p of newStack) {
471
+ p.isExpanded = false;
472
+ }
441
473
  this.panelsStack = newStack;
442
474
  this.ensureOnlyTwoOpenPanels(panel.id);
443
475
  this.setPanelHash();
476
+ this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
444
477
  }
445
478
 
446
479
  collapsePanel(panel: IPanel) {
@@ -448,13 +481,17 @@ export default class PanelList extends Vue {
448
481
  const currenctIndex = newStack.findIndex(p => p.id === panel.id);
449
482
  const lastOpenedIndex = newStack.findIndex(p => p.isLastOpened);
450
483
  if (lastOpenedIndex !== -1) { // якщо зебрежена остання відкрита панель
451
- newStack[lastOpenedIndex].isCollapsed = false
484
+ newStack[lastOpenedIndex].isCollapsed = false;
452
485
  } else if (newStack[currenctIndex-1]) { // якщо після оновлення сторінки відсутнє значення "остання відкрита", то відкриваємо ту, що зліва
453
486
  newStack[currenctIndex-1].isCollapsed = false;
454
487
  }
488
+ for (const p of newStack) {
489
+ p.isExpanded = false;
490
+ }
455
491
  this.panelsStack = newStack;
456
492
  this.ensureOnlyTwoOpenPanels(panel.id);
457
493
  this.setPanelHash();
494
+ this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
458
495
  }
459
496
 
460
497
  getPanels(type) {
@@ -468,7 +505,7 @@ export default class PanelList extends Vue {
468
505
  setPanelHash() {
469
506
  const hash = stackToHash(this.panelsStack, this.routeType === 'path').replace(/^#/, '');
470
507
  if (this.routeType === 'path') {
471
- this.$router.push({ path: `/${hash}` });
508
+ this.$router.push({ path: `${hash}` });
472
509
  } else {
473
510
  this.$router.push({ hash });
474
511
  }
@@ -487,16 +524,22 @@ export default class PanelList extends Vue {
487
524
  // reuse panel
488
525
  this.panelsStack[panelIndex].payload = panel.payload;
489
526
  this.panelsStack[panelIndex].isCollapsed = panel.isCollapsed;
527
+ this.panelsStack[panelIndex].isExpanded = false;
490
528
  newStack.push(this.panelsStack[panelIndex]);
491
529
  } else {
492
- const resPanel = await this.internalOpenPanel(panel.type, panel.payload, undefined, true);
530
+ const resPanel = await this.internalOpenPanel(panel.type, panel.payload, undefined, true, true);
493
531
  if (resPanel) {
494
532
  resPanel.isCollapsed = panel.isCollapsed;
533
+ resPanel.isExpanded = false;
495
534
  resPanel.isAnimate = false;
496
535
  newStack.push(resPanel);
497
536
  }
498
537
  }
499
538
  }
539
+ const hasExpanded = newStack.length > 1 && newStack.filter(p => !p.isCollapsed).length === 1;
540
+ if (hasExpanded) {
541
+ newStack[newStack.findIndex(p => !p.isCollapsed)].isExpanded = true;
542
+ }
500
543
  this.panelsStack = newStack;
501
544
  this.emitEvent('panels.changed', this.panelsStack);
502
545
  this.updateTitle();
@@ -521,5 +564,16 @@ export default class PanelList extends Vue {
521
564
  element.classList.add('animate');
522
565
  }
523
566
  }
567
+
568
+ rebuildPanelOnboardingPopovers(panelId: string | number, refreshDelay?: number) {
569
+ const panelRef = this.$refs[`panel-${panelId}`];
570
+ if (panelRef?.[0]) panelRef[0].rebuildOnboardingPopovers(refreshDelay);
571
+ }
572
+
573
+ rebuildAllOnboardingPopovers(refreshDelay?: number) {
574
+ this.panelsStack.forEach((panel) => {
575
+ this.rebuildPanelOnboardingPopovers(panel.id, refreshDelay);
576
+ });
577
+ }
524
578
  }
525
579
  </script>
@@ -24,20 +24,26 @@ export default @Component({
24
24
  class itfPopover extends Vue {
25
25
  @PropSync('visible') value;
26
26
 
27
- @Prop({ type: String, default: 'bottom', validator: (value) => ['bottom', 'left', 'right', 'top'].includes(value) }) placement;
27
+ @Prop({ type: String, default: 'bottom', validator: (value) => ['bottom', 'left', 'right', 'top', 'auto'].includes(value) }) placement;
28
28
 
29
29
  @Prop({ type: String, default: 'click', validator: (value) => ['click', 'focus', 'hover', 'manual'].includes(value) }) trigger;
30
30
 
31
31
  @Prop({ type: String, default: '' }) customClass;
32
+ @Prop({ type: Boolean }) appendToBody;
33
+ @Prop({ type: Boolean }) appendToContext;
34
+ @Prop({ type: String }) locator;
35
+ @Prop({ type: Boolean, default: false }) isActivatorHighlighted;
32
36
 
33
37
  modalId = '';
34
-
35
38
  modalEl = null;
39
+ targetEl = null;
36
40
 
37
41
  ignoreUpdate = false; // потрібно щоб не оновлювалось visible декілька разів, бо буде баг коли вікно відкривається і одразу закривається
38
42
 
43
+ initTimeout = null;
44
+
39
45
  beforeDestroy() {
40
- this.hide();
46
+ this.destroy();
41
47
  }
42
48
 
43
49
  @Watch('value')
@@ -46,9 +52,26 @@ class itfPopover extends Vue {
46
52
  return;
47
53
  }
48
54
  if (newValue) {
49
- this.modalEl.show();
55
+ this.modalEl?.show();
56
+ } else {
57
+ this.modalEl?.hide();
58
+ }
59
+ }
60
+
61
+ @Watch('locator')
62
+ onLocatorChanged(newValue, oldValue) {
63
+ if (newValue !== oldValue && newValue && this.modalEl) {
64
+ this.initPopover();
65
+ }
66
+ }
67
+
68
+ @Watch('isActivatorHighlighted')
69
+ onIsActivatorHighlightedChanged(newValue, oldValue) {
70
+ if (!this.targetEl) return;
71
+ if (newValue) {
72
+ this.targetEl.classList.add('pulsing');
50
73
  } else {
51
- this.modalEl.hide();
74
+ this.targetEl.classList.remove('pulsing');
52
75
  }
53
76
  }
54
77
 
@@ -58,14 +81,62 @@ class itfPopover extends Vue {
58
81
  }
59
82
 
60
83
  async mounted() {
84
+ this.refresh(100);
85
+ }
86
+
87
+ destroy() {
88
+ if (this.initTimeout) {
89
+ clearTimeout(this.initTimeout);
90
+ this.initTimeout = null;
91
+ }
92
+ if (this.modalEl) {
93
+ this.modalEl.dispose();
94
+ this.modalEl = null;
95
+ }
96
+ if (this.targetEl) this.resetTargetEl();
97
+ }
98
+
99
+ refresh(delay = 300) {
100
+ this.destroy();
101
+
102
+ if (this.initTimeout) clearTimeout(this.initTimeout);
103
+ this.initTimeout = setTimeout(() => this.initPopover(), delay);
104
+ }
105
+
106
+ async initPopover() {
61
107
  if (typeof window === 'undefined') {
62
108
  return;
63
109
  }
64
- const el = this.$refs.popover;
110
+
111
+ // Remove listeners from the previous target element before finding the new one
112
+ if (this.modalEl) this.destroy();
113
+
114
+ // Determine target
115
+ if (this.locator) {
116
+ this.targetEl = document.querySelector(this.locator);
117
+ if (!this.targetEl) return;
118
+ } else {
119
+ this.targetEl = this.$el;
120
+ }
121
+
122
+ if (this.isActivatorHighlighted) this.targetEl.classList.add('pulsing');
123
+
124
+ const el = this.$refs.popover || "";
65
125
  const { default: Popover } = await import('bootstrap/js/src/popover');
66
126
 
67
- const context = this.$el.closest('.itf-append-context') || document.body;
68
- this.modalEl = new Popover(this.$el, {
127
+ if (this._isDestroyed) return;
128
+
129
+ let context = document.body;
130
+ if (this.appendToContext && this.targetEl instanceof Node && this.targetEl.parentNode) {
131
+ context = this.targetEl.closest('.itf-append-context') || document.body;
132
+ this.targetEl.parentNode.removeChild(this.targetEl);
133
+ context.appendChild(this.targetEl); // should append only to body
134
+ } else if (this.appendToBody && this.targetEl instanceof Node && this.targetEl.parentNode) {
135
+ this.targetEl.parentNode.removeChild(this.targetEl);
136
+ context.appendChild(this.targetEl); // should append only to body
137
+ }
138
+
139
+ this.modalEl = new Popover(this.targetEl, {
69
140
  content: el,
70
141
  container: context,
71
142
  sanitize: false,
@@ -74,26 +145,38 @@ class itfPopover extends Vue {
74
145
  placement: this.placement,
75
146
  trigger: this.trigger,
76
147
  });
148
+
77
149
  this.onVisibleChanged(this.value);
78
- this.$el.addEventListener('shown.bs.popover', () => {
79
- this.ignoreUpdate = true;
80
- this.value = true;
81
- document.body.addEventListener('click', this.clickOutside);
82
- this.$nextTick(() => this.ignoreUpdate = false);
83
- });
84
- this.$el.addEventListener('hidden.bs.popover', () => {
85
- this.ignoreUpdate = true;
86
- this.value = false;
87
- document.body.removeEventListener('click', this.clickOutside);
88
- this.$nextTick(() => this.ignoreUpdate = false);
89
- });
150
+ this.targetEl.addEventListener('shown.bs.popover', this.handleShown);
151
+ this.targetEl.addEventListener('hidden.bs.popover', this.handleHidden);
152
+ }
153
+
154
+ resetTargetEl() {
155
+ this.targetEl.removeEventListener('shown.bs.popover', this.handleShown);
156
+ this.targetEl.removeEventListener('hidden.bs.popover', this.handleHidden);
157
+ this.targetEl.classList.remove('pulsing');
158
+ this.targetEl = null;
159
+ }
160
+
161
+ handleShown() {
162
+ this.ignoreUpdate = true;
163
+ this.value = true;
164
+ document.body.addEventListener('click', this.clickOutside);
165
+ this.$nextTick(() => this.ignoreUpdate = false);
166
+ }
167
+
168
+ handleHidden() {
169
+ this.ignoreUpdate = true;
170
+ this.value = false;
171
+ document.body.removeEventListener('click', this.clickOutside);
172
+ this.$nextTick(() => this.ignoreUpdate = false);
90
173
  }
91
174
 
92
175
  clickOutside(e) {
93
176
  if (this.trigger !== 'click' || !e.target) {
94
177
  return;
95
178
  }
96
- if (this.$el !== e.target && e.target.closest('.itf-popover') !== this.$refs.popover) {
179
+ if (this.targetEl !== e.target && e.target.closest('.itf-popover') !== this.$refs.popover) {
97
180
  this.hide();
98
181
  }
99
182
  }
@@ -107,7 +190,7 @@ class itfPopover extends Vue {
107
190
  hide() {
108
191
  if (this.modalEl) {
109
192
  // треба затримка щоб попав не показався знову через отримання фокусу
110
- setTimeout(() => this.modalEl.hide(), 10);
193
+ setTimeout(() => this.modalEl?.hide(), 10);
111
194
  }
112
195
  }
113
196
  }
@@ -1,17 +1,17 @@
1
1
  <template>
2
2
  <div>
3
- <div class="itf-segmeneted-control d-none d-lg-grid">
3
+ <div class="itf-segmeneted-control d-none d-lg-grid" :class="{'small': small}">
4
4
  <span v-if="!isUndefined" ref="slider" class="selection" :class="{'elevation-1': !disabled}"></span>
5
5
 
6
6
  <div class="option" v-for="(item, n) in itemsWithNames" :key="n">
7
- <label>
7
+ <label v-tooltip="tooltipCallback(item)" :key="n" :class="{'disabled': disabled || item[itemDisabled]}">
8
8
  <slot name="itemMarkup" :item="item" />
9
9
  <input
10
10
  ref="input"
11
11
  type="radio"
12
12
  :name="name"
13
13
  :value="n"
14
- v-tooltip="tooltipCallback(item)"
14
+ :disabled="disabled || item[itemDisabled]"
15
15
  :checked="isChecked(item)"
16
16
  @change="onItemChanged(item)" />
17
17
  <span>
@@ -24,6 +24,7 @@
24
24
  <itf-select
25
25
  :value="value"
26
26
  @input="$emit('input', $event)"
27
+ :disabled="disabled"
27
28
  :options="itemsWithNames"
28
29
  :reduce="(item) => item[itemKey]"
29
30
  :get-option-label="(item) => item[itemText]"
@@ -50,8 +51,10 @@ class itfSegmentedControl extends Vue {
50
51
  @Prop({ type: Array, required: true }) items;
51
52
  @Prop({ type: String, default: 'Id' }) itemKey;
52
53
  @Prop({ type: String, default: 'Name' }) itemText;
54
+ @Prop({ type: String, default: 'Disabled' }) itemDisabled;
53
55
  @Prop({ type: Boolean, default: false }) returnObject;
54
56
  @Prop({ type: Boolean, default: false }) disabled;
57
+ @Prop({ type: Boolean, default: false }) small;
55
58
  @Prop({ type: Function, default: () => null }) tooltipCallback;
56
59
 
57
60
  timers = [];
@@ -127,6 +130,9 @@ class itfSegmentedControl extends Vue {
127
130
  }
128
131
 
129
132
  onItemChanged (item) {
133
+ if (this.disabled) {
134
+ return;
135
+ }
130
136
  this.$emit('input', this.returnObject ? item : item[this.itemKey]);
131
137
  }
132
138
  }
@@ -7,7 +7,7 @@ const SORTABLE_ATTRIBUTES = ['drag-ignore-handle', 'scrollable'];
7
7
 
8
8
  const DEFAULT_OPTIONS = {
9
9
  draggableClass: DRAGGABLE_CLASS,
10
- dragHandleClass: DRAG_HANDLE_CLASS,
10
+ // dragHandleClass: DRAG_HANDLE_CLASS,
11
11
  delay: 200,
12
12
  tresholdDistance: 2,
13
13
  draggable: `.${DRAGGABLE_CLASS}`,
@@ -19,7 +19,7 @@
19
19
  </div>
20
20
  </itf-notice-popout>
21
21
  <div class="scrollable scrollable-x">
22
- <itf-checkbox-group v-model="selectedIds">
22
+ <itf-checkbox-group v-model="selectedIds" class="d-flex flex-column" style="min-width: 100%; width: max-content">
23
23
  <template v-for="(group, index) in groups">
24
24
  <div class="table-view-body">
25
25
  <itf-table-group
@@ -59,7 +59,10 @@
59
59
  :editable-property="editableProperty"
60
60
  :sorting.sync="_sorting"
61
61
  :active="active"
62
+ :new-label="newLabel"
63
+ :collapsed-group-ids="collapsedGroupIds"
62
64
  @update:expanded-ids="$emit('update:expanded-ids', $event)"
65
+ @update:collapsed-group-ids="$emit('update:collapsed-group-ids', $event)"
63
66
  @new="$emit('new', $event)"
64
67
  @filter="$emit('filter', $event)"
65
68
  @add-column="$emit('add-column', $event)"
@@ -120,6 +123,7 @@ class itfTable2 extends Vue {
120
123
  @ModelSync('value', 'input', { type: Array, default: () => ([]) }) selectedIds;
121
124
  @PropSync('sorting') _sorting;
122
125
  @Prop({ type: Array, default: () => [] }) expandedIds;
126
+ @Prop({ type: Array }) collapsedGroupIds;
123
127
  @Prop() currency;
124
128
  @Prop() currencies;
125
129
  @Prop(Boolean) stickyHeader;
@@ -138,6 +142,7 @@ class itfTable2 extends Vue {
138
142
  @Prop(Boolean) striped;
139
143
  @Prop(Boolean) absolute;
140
144
  @Prop(Boolean) clickable;
145
+ @Prop({type: String, default: function() { return this.$t('components.table.new'); } }) newLabel;
141
146
 
142
147
  state = {
143
148
  selectedIds: [],
@@ -186,7 +191,7 @@ class itfTable2 extends Vue {
186
191
  if (stateColumnIndex === -1) {
187
192
  state.columns.push(column);
188
193
  } else {
189
- state.columns[stateColumnIndex] = { ...column, width: state.columns[stateColumnIndex].width };
194
+ state.columns[stateColumnIndex] = { ...column, width: state.columns[stateColumnIndex].width, visible: state.columns[stateColumnIndex].visible };
190
195
  }
191
196
  }
192
197
  for (const column of state.columns) {
@@ -206,7 +211,7 @@ class itfTable2 extends Vue {
206
211
  if (!this.stateName) {
207
212
  return;
208
213
  }
209
- const columns = this.state.columns.map((s) => ({ property: s.property, width: s.width, index: s.index }));
214
+ const columns = this.state.columns.map((s) => ({ property: s.property, width: s.width, index: s.index, visible: s.visible }));
210
215
  localStorage.setItem(this.stateKey, JSON.stringify({ columns }));
211
216
  }
212
217
 
@@ -1,6 +1,22 @@
1
1
  <template>
2
2
  <div>
3
+ <div v-if="!rows.length" data-test="table-no-results" class="scroller">
4
+ <div class="table-view-item">
5
+ <div class="table-row-template">
6
+ <div accept-group="items" class="table-view-body-space"></div>
7
+ <div class="shadow-area"></div>
8
+ <div class="indicator sticky"></div>
9
+ <div class="table-item-inner">
10
+ <div class="table-view-item-value w-100 align-items-center p-3 no-results">
11
+ {{$t('components.table.noResults')}}
12
+ </div>
13
+ <div class="boundary right"></div>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ </div>
3
18
  <itf-table-rows
19
+ v-else
4
20
  class="scroller"
5
21
  :rows="rows"
6
22
  :selected-ids="selectedIds"
@@ -33,21 +49,6 @@
33
49
  <slot :name="name" v-bind="slotData || {}"/>
34
50
  </template>
35
51
  </itf-table-rows>
36
- <div v-if="!rows.length" data-test="table-no-results" class="scroller">
37
- <div class="table-view-item">
38
- <div class="table-row-template">
39
- <div accept-group="items" class="table-view-body-space"></div>
40
- <div class="shadow-area"></div>
41
- <div class="indicator sticky"></div>
42
- <div class="table-item-inner">
43
- <div class="table-view-item-value w-100 align-items-center p-3 no-results">
44
- {{$t('components.table.noResults')}}
45
- </div>
46
- <div class="boundary right"></div>
47
- </div>
48
- </div>
49
- </div>
50
- </div>
51
52
  </div>
52
53
  </template>
53
54
  <style lang="scss">
@@ -92,7 +93,7 @@
92
93
  display: flex;
93
94
  align-items: center;
94
95
  justify-content: center;
95
- min-width: 1rem;//var(--itf-table-min-width);
96
+ min-width: 1rem; // важливо для скруглення //var(--itf-table-min-width);
96
97
  }
97
98
 
98
99
  .table-row-template, .table-view-header-value {