@itfin/components 2.0.8 → 2.0.10

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.
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5.08422 11.626C4.97192 11.8628 4.97193 12.1375 5.08423 12.3743C6.303 14.9438 8.94045 16.7231 11.9977 16.7231C15.055 16.7231 17.6925 14.9437 18.9112 12.3741C19.0235 12.1373 19.0235 11.8625 18.9112 11.6258C17.6924 9.0562 15.055 7.27698 11.9978 7.27698C8.94046 7.27698 6.30294 9.05631 5.08422 11.626ZM15 12C15 13.6569 13.6569 15 12 15C10.3432 15 9.00003 13.6569 9.00003 12C9.00003 10.3432 10.3432 9.00002 12 9.00002C12.3075 9.00002 12.6042 9.04627 12.8834 9.1322C12.3625 9.36738 12 9.89137 12 10.5C12 11.3284 12.6716 12 13.5 12C14.1087 12 14.6327 11.6375 14.8678 11.1166C14.9538 11.3959 15 11.6926 15 12Z" fill="currentColor"/>
3
+ </svg>
@@ -16,7 +16,7 @@
16
16
 
17
17
  <div v-if="page.type === 'page'" class="d-flex flex-nowrap text-nowrap align-items-center gap-1 text-muted">
18
18
  <itf-text-field :invalid="!isValid" type="number" small :min="1" :max="9999" :value="value" @change="onPageChange" />
19
- / {{pages}}
19
+ / {{pagesCount}}
20
20
  </div>
21
21
  <a v-if="page.type === 'next'" href="" class="page-link" :aria-label="$t('components.pagination.previous')" @click.prevent="onPage(page.number)">
22
22
  <span class="visually-hidden">{{$t('components.pagination.next')}}</span>
@@ -93,6 +93,10 @@ class itfPagination extends Vue {
93
93
 
94
94
  isValid = true;
95
95
 
96
+ get pagesCount() {
97
+ return this.pages || 1;
98
+ }
99
+
96
100
  onPage(page) {
97
101
  this.$emit('input', page);
98
102
  }
@@ -4,6 +4,7 @@
4
4
  <script lang="ts">
5
5
  import { Vue, Component, Inject, Prop } from 'vue-property-decorator';
6
6
  import { IPanel } from './PanelList.vue';
7
+ import {stackToHash} from "@itfin/components/src/components/panels/helpers";
7
8
 
8
9
  @Component({
9
10
  components: {
@@ -17,9 +18,11 @@ export default class PanelLink extends Vue {
17
18
  @Inject({ default: null }) panelList;
18
19
  @Inject({ default: null }) currentPanel;
19
20
 
21
+ @Prop(Boolean) global: boolean;
20
22
  @Prop(String) panel: string;
21
23
  @Prop() payload: payload;
22
24
  @Prop() target: string;
25
+ @Prop() list;
23
26
  @Prop({ type: String, default: 'active' }) activeClass: string;
24
27
  @Prop(Boolean) append: boolean;
25
28
 
@@ -31,30 +34,41 @@ export default class PanelLink extends Vue {
31
34
  return handlers;
32
35
  }
33
36
 
37
+ get activeList() {
38
+ return this.list ?? this.panelList;
39
+ }
40
+
34
41
  get isActive() {
35
- let stack = this.panelList.getCurrentStack();
42
+ if (!this.activeList) {
43
+ return false;
44
+ }
45
+ let stack = this.activeList.getCurrentStack();
36
46
  return stack.find(s => s.type === this.panel);
37
47
  }
38
48
 
39
49
  get link() {
40
- let stack = this.panelList.getCurrentStack();
50
+ let stack = this.activeList?.getCurrentStack() ?? [];
41
51
  if (!this.append) {
42
- stack = stack.splice(0, this.currentPanel.index + 1);
52
+ stack = stack.splice(0, this.currentPanel?.index + 1);
43
53
  }
44
- const hash = this.panelList.getLink([
54
+ const hash = stackToHash([
45
55
  ...stack,
46
56
  {
47
57
  type: this.panel,
48
58
  payload: this.payload || {}
49
59
  }
50
60
  ]);
51
- return `#${hash}`;
61
+ return hash;
52
62
  }
53
63
 
54
64
  onClick(e) {
65
+ console.info(this.activeList);
66
+ if (!this.activeList) {
67
+ return;
68
+ }
55
69
  e.preventDefault();
56
70
  e.stopPropagation();
57
- this.panelList.openPanel(this.panel, this.payload || {}, this.append ? undefined : this.currentPanel.index + 1);
71
+ this.activeList.openPanel(this.panel, this.payload || {}, this.append ? undefined : this.currentPanel?.index + 1);
58
72
  }
59
73
  }
60
74
  </script>
@@ -134,21 +134,22 @@ $double-an-time: $an-time * 2;
134
134
  transition: min-width $an-time linear, flex-grow $an-time linear;
135
135
 
136
136
  > div {
137
- transition: opacity $an-time linear;
137
+ //transition: opacity $an-time linear;
138
138
  }
139
139
  }
140
140
 
141
- .slide-enter-active > div {
142
- opacity: 0;
143
- }
144
-
145
- .opacity-null > div {
146
- opacity: 0 !important;
147
- }
141
+ //.slide-enter-active > div {
142
+ // opacity: 0;
143
+ //}
144
+ //
145
+ //.opacity-null > div {
146
+ // opacity: 0 !important;
147
+ //}
148
148
  </style>
149
149
  <script lang="ts">
150
150
  import { Vue, Component, Prop } from 'vue-property-decorator';
151
151
  import Panel from './Panel.vue';
152
+ import {hashToStack, stackToHash} from "@itfin/components/src/components/panels/helpers";
152
153
 
153
154
  interface VisualOptions {
154
155
  title: string;
@@ -200,7 +201,6 @@ export default class PanelList extends Vue {
200
201
 
201
202
  nextId:number = 0;
202
203
 
203
-
204
204
  created() {
205
205
  if (this.firstPanel) {
206
206
  this.internalOpenPanel(this.firstPanel.type, this.firstPanel.payload);
@@ -276,6 +276,8 @@ export default class PanelList extends Vue {
276
276
  if (!this.panelsStack.length || openIndex === 0) {
277
277
  newPanel.isCloseable = false;
278
278
  }
279
+ console.info(newPanel, this.panelsStack)
280
+ console.trace();
279
281
  let newStack = [...this.panelsStack];
280
282
  if (this.panels[type].permanentExpanded && newStack.length) {
281
283
  for (const panel of newStack) {
@@ -389,30 +391,17 @@ export default class PanelList extends Vue {
389
391
  return [...this.panelsStack];
390
392
  }
391
393
 
392
- getLink(stack: IPanel[]) {
393
- return stack.map(panel => {
394
- return `${panel.type}${panel.isCollapsed ? '' : '!'}=${JSON.stringify(panel.payload || {})}`;
395
- }).join('&');
396
- }
397
-
398
394
  setPanelHash() {
399
- const hash = this.getLink(this.panelsStack);
395
+ const hash = stackToHash(this.panelsStack).replace(/^#/, '');
400
396
  this.$router.push({ hash });
401
397
  }
402
398
 
403
399
  async parsePanelHash() {
404
400
  const {hash} = location;
405
401
  if (hash) {
406
- const panels = hash.slice(1).split('&').map(item => {
407
- const [type, payload] = item.split('=');
408
- const isCollapsed = !item.includes('!');
409
- return {
410
- type: type.replace('!', ''),
411
- isCollapsed,
412
- payload: JSON.parse(decodeURIComponent(payload))
413
- };
414
- });
402
+ const panels = hashToStack(hash);
415
403
  const newStack = [];
404
+ this.panelsStack = [];
416
405
  for (const panelIndex in panels) {
417
406
  const panel = panels[panelIndex];
418
407
  if (this.panelsStack[panelIndex] && this.panelsStack[panelIndex].type === panel.type) {
@@ -0,0 +1,27 @@
1
+ import {stackToHash, hashToStack} from "./helpers";
2
+
3
+ describe('panel helpers', () => {
4
+ test('stackToHash', () => {
5
+ const stack = [
6
+ { type: 'a', payload: { a: 1 }, isCollapsed: false },
7
+ { type: 'b', payload: { b: 2 }, isCollapsed: true },
8
+ { type: 'c', payload: { c: 3 }, isCollapsed: false }
9
+ ];
10
+ expect(stackToHash(stack)).toBe('a!={"a":1}&b={"b":2}&c!={"c":3}');
11
+ });
12
+ test('hashToStack', () => {
13
+ const stack = [
14
+ { type: 'a', payload: { a: 1 }, isCollapsed: false },
15
+ { type: 'b', payload: { b: 2 }, isCollapsed: true },
16
+ { type: 'c', payload: { c: 3 }, isCollapsed: false }
17
+ ];
18
+ expect(hashToStack('a!={"a":1}&b={"b":2}&c!={"c":3}')).toEqual(stack);
19
+ expect(hashToStack('a!={"a')).toEqual([
20
+ {
21
+ "isCollapsed": false,
22
+ "payload": {},
23
+ "type": "a"
24
+ }
25
+ ]);
26
+ });
27
+ });
@@ -0,0 +1,37 @@
1
+ export interface IPanel {
2
+ type: string;
3
+ payload?: any;
4
+ isCollapsed?: boolean;
5
+ }
6
+
7
+ export function stackToHash(stack: IPanel[]) {
8
+ const hash = stack.map(panel => {
9
+ return `${panel.type}${panel.isCollapsed ? '' : '!'}=${JSON.stringify(panel.payload || {})}`;
10
+ }).join('&');
11
+ return `#${hash}`;
12
+ }
13
+
14
+
15
+ export function hashToStack(hash: string|undefined): IPanel[] {
16
+ let stack:IPanel[] = [];
17
+ if (hash) {
18
+ const str = hash.replace(/^#/, '');
19
+
20
+ stack = str.split('&').map(item => {
21
+ const [type, payload] = item.split('=');
22
+ const isCollapsed = !type.includes('!');
23
+ let payloadObj:any = {};
24
+ try {
25
+ payloadObj = JSON.parse(decodeURIComponent(payload));
26
+ } catch (e) {
27
+ // ignore
28
+ }
29
+ return {
30
+ type: type.replace('!', ''),
31
+ isCollapsed,
32
+ payload: payloadObj
33
+ };
34
+ });
35
+ }
36
+ return stack;
37
+ }
@@ -5,7 +5,7 @@
5
5
  'table-absolute': absolute,
6
6
  'table-clickable': clickable,
7
7
  'permanent-checkboxes': selectedIds.length
8
- }" :style="{ '--indicator-area-width': `${indicatorType === 'none' ? 1 : indicatorWidth}px` }">
8
+ }" :style="{ '--indicator-area-width': `${indicatorType === 'none' ? 1 : indicatorWidth}px`, '--shadow-area-width': `${shadowWidth}px` }">
9
9
  <itf-notice-popout :visible="showGroupOperations" class="rounded-pill bg-dark text-light">
10
10
  <div class="d-flex gap-3 px-3 align-items-center">
11
11
  <div><strong>{{selectedIds.length}}</strong> selected</div>
@@ -29,6 +29,7 @@
29
29
  :title="group.name"
30
30
  :selected-ids.sync="selectedIds"
31
31
  :add-new-rows="addNewRows"
32
+ :shadow-width="shadowWidth"
32
33
  :column-sorting="columnSorting"
33
34
  :column-resizing="columnResizing"
34
35
  :show-grouping="showGrouping"
@@ -43,6 +44,7 @@
43
44
  :currencies="currencies"
44
45
  :currency="currency"
45
46
  :subrows-property="subrowsProperty"
47
+ :divider-property="dividerProperty"
46
48
  :indicator-type="indicatorType"
47
49
  :expanded-all="expandedAll"
48
50
  :indicatorWidth="indicatorWidth"
@@ -103,9 +105,11 @@ class itfTable2 extends Vue {
103
105
  @Prop({ type: String, default: null }) idProperty;
104
106
  @Prop({ type: String, default: null }) cssProperty;
105
107
  @Prop({ type: String, default: null }) subrowsProperty;
108
+ @Prop({ type: String, default: null }) dividerProperty;
106
109
  @Prop({ type: String, default: null }) editableProperty;
107
110
  @Prop({ default: null }) active;
108
111
  @Prop({ default: 45 }) indicatorWidth;
112
+ @Prop({ default: 0 }) shadowWidth;
109
113
  @Prop({ type: String, default: null, validator: (val) => ['order', 'checkbox', 'toggle', 'property', 'none'].includes(val) }) indicatorType;
110
114
  @Prop({ type: String, default: null }) stateName; // save state to storage
111
115
  @Prop({ type: Object, default: () => ({}) }) schema;
@@ -7,6 +7,7 @@
7
7
  :columns="columns"
8
8
  :id-property="idProperty"
9
9
  :subrows-property="subrowsProperty"
10
+ :divider-property="dividerProperty"
10
11
  :show-add-column="showAddColumn"
11
12
  :show-actions="showActions"
12
13
  :no-select-all="noSelectAll"
@@ -91,7 +92,7 @@
91
92
  display: flex;
92
93
  align-items: center;
93
94
  justify-content: center;
94
- min-width: var(--itf-table-min-width);
95
+ min-width: 1rem;//var(--itf-table-min-width);
95
96
  }
96
97
 
97
98
  .table-row-template, .table-view-header-value {
@@ -109,21 +110,6 @@
109
110
  }
110
111
  }
111
112
  }
112
- .table-row-template {
113
- .on-hover {
114
- display: none;
115
- }
116
- &:hover, .permanent-checkboxes & {
117
- .on-rest {
118
- display: none;
119
- }
120
-
121
- .on-hover {
122
- opacity: 1;
123
- display: block;
124
- }
125
- }
126
- }
127
113
 
128
114
  .table-small-row .table-view-item {
129
115
  // height: var(--table-row-height);
@@ -158,6 +144,7 @@ class itfTableBody extends Vue {
158
144
  @Prop() rows;
159
145
  @Prop() idProperty;
160
146
  @Prop() subrowsProperty;
147
+ @Prop() dividerProperty;
161
148
  @Prop() active;
162
149
  @Prop(Boolean) showAddColumn;
163
150
  @Prop(Boolean) showActions;
@@ -61,6 +61,7 @@
61
61
  @row-click="$emit('row-click', $event)"
62
62
  :id-property="idProperty"
63
63
  :subrows-property="subrowsProperty"
64
+ :divider-property="dividerProperty"
64
65
  :rows="rows"
65
66
  :editable="editable"
66
67
  :currency="currency"
@@ -112,7 +113,7 @@
112
113
  :key="n"
113
114
  :data-column="n"
114
115
  class="position-relative line-overflow px-1"
115
- :style="column.grow ? `min-width: ${column.minWidth}px; flex-grow: 1;` : `width: ${column.width}px; max-width: ${column.width}px;`">
116
+ :style="`width: ${column.width}px; max-width: ${column.width}px;`">
116
117
  <itf-dropdown append-to-context text right @open="persistSummary = true" @close="persistSummary = false" autoclose="outside">
117
118
  <template #button>
118
119
  <span data-test="summary-column" class="invisible-summary d-flex align-items-center justify-content-end flex-auto">
@@ -358,6 +359,7 @@ class itfTableGroup extends Vue {
358
359
  @Prop() title;
359
360
  @Prop() idProperty;
360
361
  @Prop() subrowsProperty;
362
+ @Prop() dividerProperty;
361
363
  @Prop() currency;
362
364
  @Prop() currencies;
363
365
  @Prop() active;
@@ -377,6 +379,7 @@ class itfTableGroup extends Vue {
377
379
  @Prop(Boolean) striped;
378
380
  @Prop(Boolean) stickyHeader;
379
381
  @Prop() indicatorWidth;
382
+ @Prop() shadowWidth;
380
383
  @Prop() cssProperty;
381
384
  @PropSync('sorting') _sorting;
382
385
  @Prop({ type: String, default: null }) indicatorType;
@@ -390,7 +393,7 @@ class itfTableGroup extends Vue {
390
393
  get visibleColumns() {
391
394
  let list = this.columns;
392
395
  list = sortBy(list, (column) => column.index);
393
- let left = 12 + (this.indicatorType === 'none' ? 1 : Number(this.indicatorWidth)); // sum of first 2 cells
396
+ let left = Number(this.shadowWidth) + (this.indicatorType === 'none' ? 1 : Math.max(Number(this.indicatorWidth), 16)); // sum of first 2 cells
394
397
  const pinned = list.filter((column) => column.pinned && column.visible !== false);
395
398
  list = list.map((column, index) => {
396
399
  const item = {...column};
@@ -15,9 +15,9 @@
15
15
  data-test="table-header-column"
16
16
  :data-column="n"
17
17
  :data-id="column.Id"
18
- :class="{'sticky': column.pinned, 'active': sortColumnParams[column.property], 'last-sticky-column': n === lastPinnedIndex, 'flex-grow-1': column.grow, [`justify-content-${column.align || 'start'}`]: true}"
18
+ :class="{'sticky': column.pinned, 'active': sortColumnParams[column.property], 'last-sticky-column': n === lastPinnedIndex, [`justify-content-${column.align || 'start'}`]: true}"
19
19
  class="table-view-header-value"
20
- :style="column.grow ? `left: ${column.left}px; flex-grow: 1` : `width: ${column.width}px; max-width: ${column.width}px; left: ${column.left}px;`">
20
+ :style="`width: ${column.width}px; max-width: ${column.width}px; left: ${column.left}px;`">
21
21
  <!-- Не треба видаляти колонки, бо вони потрібні для збереження ширини -->
22
22
  <div v-if="visibleHeader" accept-group="tablecolumns"
23
23
  class="table-view-header-space"
@@ -32,9 +32,9 @@
32
32
  v-draggable="{ handle: true, payload: { index: n, item: column }, mirror: {yAxis:false} }">
33
33
  <itf-dropdown text append-to-body shadow ref="dropdown" class="w-100" :disabled="noColumnMenu">
34
34
  <template #button>
35
- <div class="itf-table2__header-title d-flex w-100 align-items-start" :title="getTitle(column.title)" :class="{'ms-auto': column.align === 'end'}">
35
+ <div class="itf-table2__header-title d-flex w-100 align-items-center" :title="getTitle(column.title)">
36
36
  <itf-icon class="itf-table2__header-icon" new v-if="column.icon" :name="column.icon"></itf-icon>
37
- <div class="flex-grow-1 w-100 itf-table2__title-container">
37
+ <div class="flex-grow-1 w-100 itf-table2__title-container d-flex align-items-center" :class="{'justify-content-end': column.align === 'end'}">
38
38
  <div class="itf-table2__title text-truncate">{{getTitle(column.title)}}</div>
39
39
  <div v-if="column.prefix" class="itf-table2__subtitle text-truncate" v-text="column.prefix" />
40
40
  </div>
@@ -139,7 +139,7 @@
139
139
  v-dropzone="{payload:{ last: true }}">
140
140
  <div class="table-view-header-dropzone"></div>
141
141
  </div>
142
- <div v-if="visibleHeader && columnResizing && !column.grow" ref="resizeHandle" class="resize-handle"></div>
142
+ <div v-if="visibleHeader && columnResizing" ref="resizeHandle" class="resize-handle"></div>
143
143
  </div>
144
144
  </template>
145
145
  <div class="table-view-item-value extra flex-grow-1"></div>
@@ -341,15 +341,27 @@ class itfTableHeader extends Vue {
341
341
  const delta = event.pageX - startX;
342
342
  newWidth = Math.max(columnWidth + delta, 50);
343
343
  columns.forEach((column) => {
344
- if (!this.sortedColumns[index].grow) {
345
- column.style.width = `${newWidth}px`;
346
- column.style['max-width'] = `${newWidth}px`;
344
+ const { maxWidth, minWidth } = this.sortedColumns[index];
345
+ if (minWidth && newWidth < minWidth) {
346
+ newWidth = minWidth;
347
347
  }
348
+ if (maxWidth && newWidth > maxWidth) {
349
+ newWidth = maxWidth;
350
+ }
351
+ column.style.width = `${newWidth}px`;
352
+ column.style['max-width'] = `${newWidth}px`;
348
353
  });
349
354
  };
350
355
  const mouseUpHandler = () => {
351
356
  document.removeEventListener('mousemove', mouseMoveHandler);
352
357
  document.removeEventListener('mouseup', mouseUpHandler);
358
+ const { minWidth, maxWidth } = this.sortedColumns[index];
359
+ if (minWidth && newWidth < minWidth) {
360
+ newWidth = minWidth;
361
+ }
362
+ if (maxWidth && newWidth > maxWidth) {
363
+ newWidth = maxWidth;
364
+ }
353
365
  this.changeColumn(index, { width: newWidth });
354
366
  };
355
367
  document.addEventListener('mousemove', mouseMoveHandler);
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <div>
3
+ <div @click.prevent.stop="toggle" class="d-flex align-items-center flex-nowrap" :class="{'active-toggle': visible}">
4
+ <div class="item-toggle text-muted">
5
+ <template v-if="visible && expanded">
6
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
7
+ width="16" height="16" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
8
+ <path d="M184.7,413.1l2.1-1.8l156.5-136c5.3-4.6,8.6-11.5,8.6-19.2c0-7.7-3.4-14.6-8.6-19.2L187.1,101l-2.6-2.3
9
+ C182,97,179,96,175.8,96c-8.7,0-15.8,7.4-15.8,16.6h0v286.8h0c0,9.2,7.1,16.6,15.8,16.6C179.1,416,182.2,414.9,184.7,413.1z" fill="currentColor" />
10
+ </svg>
11
+ </template>
12
+ <template v-else-if="visible">
13
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
14
+ width="16" height="16" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
15
+ <path d="M98.9,184.7l1.8,2.1l136,156.5c4.6,5.3,11.5,8.6,19.2,8.6c7.7,0,14.6-3.4,19.2-8.6L411,187.1l2.3-2.6
16
+ c1.7-2.5,2.7-5.5,2.7-8.7c0-8.7-7.4-15.8-16.6-15.8v0H112.6v0c-9.2,0-16.6,7.1-16.6,15.8C96,179.1,97.1,182.2,98.9,184.7z" fill="currentColor" />
17
+ </svg>
18
+ </template>
19
+ </div>
20
+
21
+ <slot></slot>
22
+ </div>
23
+ </div>
24
+ </template>
25
+ <style lang="scss" scoped>
26
+ .active-toggle {
27
+ cursor: pointer;
28
+ }
29
+ .item-toggle {
30
+ width: 1.5rem;
31
+ min-width: 1.5rem;
32
+ display: flex;
33
+ align-items: center;
34
+ }
35
+ </style>
36
+ <script>
37
+ import { Vue, Component, Prop } from 'vue-property-decorator';
38
+
39
+ export default @Component({
40
+ components: {
41
+ }
42
+ })
43
+ class itfTableRowToggle extends Vue {
44
+ @Prop(Boolean) expanded;
45
+ @Prop(Boolean) visible;
46
+
47
+ toggle() {
48
+ this.$emit('toggle');
49
+ }
50
+ }
51
+ </script>
@@ -20,29 +20,11 @@
20
20
  </a>
21
21
  </div>
22
22
  </div>
23
- <div v-if="indicatorType !== 'none'" class="indicator sticky">
24
- <div class="fill table-view-row-count" :class="{'on-rest': indicatorType !== 'checkbox' && indicatorType !== 'toggle'}">
23
+ <div class="indicator sticky">
24
+ <div v-if="indicatorType !== 'none'" class="table-view-row-count">
25
25
  <span v-if="indicatorType === 'order'">{{ (n + 1) }}</span>
26
26
  <span v-else-if="indicatorType === 'property'">{{ item[idProperty] }}</span>
27
- <a href="" @click.prevent.stop="$emit('toggle', item)" v-else-if="indicatorType === 'toggle'">
28
- <template v-if="subrowsProperty && item[subrowsProperty] && item[subrowsProperty].length">
29
- <template v-if="item[subrowsProperty] && item[subrowsProperty].length && !isExpanded(item)">
30
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square" viewBox="0 0 16 16">
31
- <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
32
- <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4"/>
33
- </svg>
34
- </template>
35
- <template v-else>
36
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dash-square" viewBox="0 0 16 16">
37
- <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
38
- <path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8"/>
39
- </svg>
40
- </template>
41
- </template>
42
- </a>
43
- </div>
44
- <div v-if="indicatorType !== 'toggle'" class="fill" :class="{'on-hover': indicatorType !== 'checkbox'}">
45
- <itf-checkbox :value="item[idProperty]" />
27
+ <span v-else-if="indicatorType === 'checkbox'"><itf-checkbox :value="item[idProperty]" /></span>
46
28
  </div>
47
29
  </div>
48
30
  <div accept-group="items" class="table-item-inner" @click="$emit('row-click', item)">
@@ -50,11 +32,16 @@
50
32
  <div
51
33
  v-if="column.visible !== false"
52
34
  :data-column="k"
53
- :style="column.grow ? `left: ${column.left}px; flex-grow: 1` : `width: ${column.width}px; max-width: ${column.width}px; left: ${column.left}px;`"
54
- :class="{'justify-content-end': column.align === 'end', 'sticky': column.pinned, 'last-sticky-column': k === lastPinnedIndex, 'flex-grow-1': column.grow, 'editable': column.editable && editable}"
35
+ :style="`width: ${column.width}px; max-width: ${column.width}px; left: ${column.left}px;`"
36
+ :class="{'justify-content-end': column.align === 'end', 'sticky': column.pinned, 'last-sticky-column': k === lastPinnedIndex, 'editable': column.editable && editable}"
55
37
  class="table-view-item-value d-flex h-100">
56
38
  <div class="table-view-item-value-content" :class="{'px-2': !(column.editable && editable)}">
57
- <slot :name="`column.${column.property}`" :toggle="() => $emit('toggle', item)" :level="level" :editable="column.editable && editable" :item="item" :column="column" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)">
39
+ <slot
40
+ :name="`column.${column.property}`"
41
+ :toggle="() => $emit('toggle', item)"
42
+ :hasSubitems="!!(subrowsProperty && item[subrowsProperty] && item[subrowsProperty].length)"
43
+ :isExpanded="!!(item[subrowsProperty] && item[subrowsProperty].length && !isExpanded(item))"
44
+ :level="level" :editable="column.editable && editable" :item="item" :column="column" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)">
58
45
  <template v-if="column.editable && editable && (!editableProperty || item[editableProperty])">
59
46
  <slot :name="`edit.${column.type}`" :level="level" :toggle="() => $emit('toggle', item)" :update="(val) => updateValue(item, val, n, column)" :value="getValue(item, column)" :item="item" :column="column">
60
47
  <itf-text-field class="w-100 h-100" v-if="column.type === 'text'" :value="getValue(item, column)" @input="updateValue(item, $event, n, column)" />
@@ -122,6 +109,7 @@
122
109
  </template>
123
110
  </itf-table-rows>
124
111
  </template>
112
+ <div v-if="dividerProperty && item[dividerProperty]" :key="`divider-${n}`" class="itf-table2__row-divider"></div>
125
113
  </template>
126
114
  </div>
127
115
  </template>
@@ -157,6 +145,7 @@ class itfTableRows extends Vue {
157
145
  @Prop() rows;
158
146
  @Prop() idProperty;
159
147
  @Prop() subrowsProperty;
148
+ @Prop() dividerProperty;
160
149
  @Prop() active;
161
150
  @Prop(Boolean) showAddColumn;
162
151
  @Prop(Boolean) noSelectAll;
@@ -13,7 +13,7 @@
13
13
  --itf-table-header-border-color: #8E97A533;
14
14
  --itf-table-border-base-color: var(--itf-table-header-bg); // кольори границь таблиці без внутрішніх рядків
15
15
  --itf-table-border-base-width: 2px;
16
- --itf-table-hover-header-bg: #1A4A971A;
16
+ --itf-table-hover-header-bg: #dfe5ef;
17
17
  --itf-table-hover-bg: #e9edf5;
18
18
  --itf-table-bg: var(--bs-body-bg);
19
19
  --itf-table-min-width: 45px;
@@ -130,6 +130,7 @@ body[data-theme="dark"] {
130
130
  }
131
131
  .last-sticky-column {
132
132
  position: relative;
133
+ border-right: 0 none !important;
133
134
  &:after {
134
135
  content: "";
135
136
  position: absolute;
@@ -211,8 +212,8 @@ body[data-theme="dark"] {
211
212
  background: var(--itf-table-bg);
212
213
  min-height: var(--itf-table-header-height);
213
214
 
214
- &:not(.draggable-container--is-dragging) .table-view-header-value {
215
- z-index: 9;
215
+ &:not(.draggable-container--is-dragging):hover .table-view-header-value {
216
+ z-index: 39;
216
217
  }
217
218
  &:after {
218
219
  content: "";
@@ -258,9 +259,11 @@ body[data-theme="dark"] {
258
259
  border-left: var(--itf-table-border-base-width) solid var(--itf-table-border-base-color);
259
260
  left: var(--shadow-area-width);
260
261
  width: var(--indicator-area-width);
262
+ min-width: 1rem;
261
263
 
262
264
  &.sticky {
263
- border-top-left-radius: var(--itf-table-table-border-radius);
265
+ border-top-left-radius: var(--itf-table-table-border-radius);
266
+ z-index: 13;
264
267
  }
265
268
  }
266
269
  &.sticky {
@@ -422,6 +425,13 @@ body[data-theme="dark"] {
422
425
  }
423
426
  }
424
427
 
428
+ &__row-divider {
429
+ background-color: #F7F8FA;
430
+ height: 5px;
431
+ padding: 0;
432
+ border-top: 1px solid rgba(238, 238, 238, 1);
433
+ border-bottom: 1px solid rgba(238, 238, 238, 1);
434
+ }
425
435
  //&:hover, &.permanent-editable-border {
426
436
  // .table-view-item-value.editable {
427
437
  // .form-control {