@itfin/components 2.0.8 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/filter/FilterPanel.vue +15 -3
- package/src/components/icon/components/nomi-eye.vue +4 -0
- package/src/components/icon/icons.js +307 -306
- package/src/components/icon/new-icons/eye.svg +3 -0
- package/src/components/pagination/Pagination2.vue +5 -1
- package/src/components/panels/PanelLink.vue +20 -6
- package/src/components/panels/PanelList.vue +14 -25
- package/src/components/panels/helpers.spec.ts +27 -0
- package/src/components/panels/helpers.ts +37 -0
- package/src/components/table/Table2.vue +5 -1
- package/src/components/table/TableBody.vue +3 -16
- package/src/components/table/TableGroup.vue +5 -2
- package/src/components/table/TableHeader.vue +20 -8
- package/src/components/table/TableRowToggle.vue +50 -0
- package/src/components/table/TableRows.vue +13 -24
- package/src/components/table/table2.scss +13 -4
- package/src/components/view/View.vue +42 -4
- package/src/locales/en.js +1 -0
- package/src/locales/uk.js +3 -0
|
@@ -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
|
-
/ {{
|
|
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
|
-
|
|
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.
|
|
50
|
+
let stack = this.activeList?.getCurrentStack() ?? [];
|
|
41
51
|
if (!this.append) {
|
|
42
|
-
stack = stack.splice(0, this.currentPanel
|
|
52
|
+
stack = stack.splice(0, this.currentPanel?.index + 1);
|
|
43
53
|
}
|
|
44
|
-
const hash =
|
|
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
|
|
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.
|
|
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
|
-
|
|
142
|
-
opacity: 0;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
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 =
|
|
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
|
|
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="
|
|
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 =
|
|
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,
|
|
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="
|
|
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-
|
|
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
|
|
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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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,50 @@
|
|
|
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
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
<script>
|
|
36
|
+
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
37
|
+
|
|
38
|
+
export default @Component({
|
|
39
|
+
components: {
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
class itfTableRowToggle extends Vue {
|
|
43
|
+
@Prop(Boolean) expanded;
|
|
44
|
+
@Prop(Boolean) visible;
|
|
45
|
+
|
|
46
|
+
toggle() {
|
|
47
|
+
this.$emit('toggle');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
@@ -20,29 +20,11 @@
|
|
|
20
20
|
</a>
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
|
-
<div
|
|
24
|
-
<div class="
|
|
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
|
-
<
|
|
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="
|
|
54
|
-
:class="{'justify-content-end': column.align === 'end', 'sticky': column.pinned, 'last-sticky-column': k === lastPinnedIndex, '
|
|
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
|
|
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: #
|
|
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;
|
|
@@ -211,8 +211,8 @@ body[data-theme="dark"] {
|
|
|
211
211
|
background: var(--itf-table-bg);
|
|
212
212
|
min-height: var(--itf-table-header-height);
|
|
213
213
|
|
|
214
|
-
&:not(.draggable-container--is-dragging) .table-view-header-value {
|
|
215
|
-
z-index:
|
|
214
|
+
&:not(.draggable-container--is-dragging):hover .table-view-header-value {
|
|
215
|
+
z-index: 39;
|
|
216
216
|
}
|
|
217
217
|
&:after {
|
|
218
218
|
content: "";
|
|
@@ -258,9 +258,11 @@ body[data-theme="dark"] {
|
|
|
258
258
|
border-left: var(--itf-table-border-base-width) solid var(--itf-table-border-base-color);
|
|
259
259
|
left: var(--shadow-area-width);
|
|
260
260
|
width: var(--indicator-area-width);
|
|
261
|
+
min-width: 1rem;
|
|
261
262
|
|
|
262
263
|
&.sticky {
|
|
263
|
-
|
|
264
|
+
border-top-left-radius: var(--itf-table-table-border-radius);
|
|
265
|
+
z-index: 13;
|
|
264
266
|
}
|
|
265
267
|
}
|
|
266
268
|
&.sticky {
|
|
@@ -422,6 +424,13 @@ body[data-theme="dark"] {
|
|
|
422
424
|
}
|
|
423
425
|
}
|
|
424
426
|
|
|
427
|
+
&__row-divider {
|
|
428
|
+
background-color: #F7F8FA;
|
|
429
|
+
height: 5px;
|
|
430
|
+
padding: 0;
|
|
431
|
+
border-top: 1px solid rgba(238, 238, 238, 1);
|
|
432
|
+
border-bottom: 1px solid rgba(238, 238, 238, 1);
|
|
433
|
+
}
|
|
425
434
|
//&:hover, &.permanent-editable-border {
|
|
426
435
|
// .table-view-item-value.editable {
|
|
427
436
|
// .form-control {
|