@itfin/components 1.2.90 → 1.2.92

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itfin/components",
3
- "version": "1.2.90",
3
+ "version": "1.2.92",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -22,6 +22,7 @@
22
22
  "@egjs/vue-flicking": "^4.10.4",
23
23
  "@mdi/js": "^7.2.96",
24
24
  "@popperjs/core": "^2.11.8",
25
+ "@shopify/draggable": "^1.0.0-beta.8",
25
26
  "@vue/cli-service": "^5.0.1",
26
27
  "@vue/composition-api": "^1.7.1",
27
28
  "air-datepicker": "^3.3.5",
@@ -36,7 +37,8 @@
36
37
  "vue": "^2.6.12",
37
38
  "vue-imask": "^6.6.3",
38
39
  "vue-property-decorator": "^9.1.2",
39
- "vue-swatches": "^2.1.1"
40
+ "vue-swatches": "^2.1.1",
41
+ "vue-virtual-scroller": "^1.1.2"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@babel/eslint-parser": "^7.19.1",
@@ -0,0 +1,34 @@
1
+ // Include functions first (so you can manipulate colors, SVGs, calc, etc)
2
+ @import "~bootstrap/scss/functions.scss";
3
+
4
+ // Include remainder of required Bootstrap stylesheets
5
+ @import "~bootstrap/scss/variables.scss";
6
+ @import "~bootstrap/scss/mixins.scss";
7
+ @import "~bootstrap/scss/maps.scss";
8
+ @import "~bootstrap/scss/utilities.scss";
9
+
10
+ $custom-colors: (
11
+ "white": #fff,
12
+ "black": #1e1e1e
13
+ );
14
+ $theme-colors: map-merge($theme-colors, $custom-colors);
15
+
16
+ // Text colors
17
+ $all-colors: map-merge-multiple($theme-colors, $custom-colors, $blues, $indigos, $purples, $pinks, $reds, $oranges, $yellows, $greens, $teals, $cyans);
18
+ $utilities: map-merge(
19
+ $utilities,
20
+ (
21
+ "color": map-merge(
22
+ map-get($utilities, "color"),
23
+ (
24
+ values: map-merge(
25
+ map-get(map-get($utilities, "color"), "values"),
26
+ (
27
+ $all-colors
28
+ ),
29
+ ),
30
+ ),
31
+ ),
32
+ )
33
+ );
34
+ //@import "~bootstrap/scss/utilities/api"; // створює дублікати класів, які вже є в bootstrap
@@ -1,4 +1,5 @@
1
1
  @import './variables.scss';
2
+ @import './bootstrap.scss';
2
3
 
3
4
  $color-income: #0d935b;
4
5
  $color-outcome: #b91e1e;
@@ -1,7 +1,4 @@
1
- // 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
2
- @import "~bootstrap/scss/functions.scss";
3
-
4
- // 2. Include any default variable overrides here
1
+ // Include any default variable overrides here
5
2
  $font-family-base: 'Fira Sans', sans-serif;
6
3
  $headings-font-family: 'Fira Sans', sans-serif;
7
4
  $font-family-monospace: "Fira Mono", "Courier New", monospace;
@@ -76,38 +73,6 @@ $dark-input-focus-border: rgb(244 206 176 / 25%);
76
73
 
77
74
  $pagination-border-width: 0;
78
75
 
79
- // 3. Include remainder of required Bootstrap stylesheets
80
- @import "~bootstrap/scss/variables.scss";
81
- @import "~bootstrap/scss/mixins.scss";
82
- @import "~bootstrap/scss/maps.scss";
83
- @import "~bootstrap/scss/utilities.scss";
84
-
85
- $custom-colors: (
86
- "white": #fff,
87
- "black": #1e1e1e
88
- );
89
- $theme-colors: map-merge($theme-colors, $custom-colors);
90
-
91
- // Text colors
92
- $all-colors: map-merge-multiple($theme-colors, $custom-colors, $blues, $indigos, $purples, $pinks, $reds, $oranges, $yellows, $greens, $teals, $cyans);
93
- $utilities: map-merge(
94
- $utilities,
95
- (
96
- "color": map-merge(
97
- map-get($utilities, "color"),
98
- (
99
- values: map-merge(
100
- map-get(map-get($utilities, "color"), "values"),
101
- (
102
- $all-colors
103
- ),
104
- ),
105
- ),
106
- ),
107
- )
108
- );
109
- @import "~bootstrap/scss/utilities/api";
110
-
111
76
  // Fonts
112
77
  $baseFontSize: 16px;
113
78
  $hintFontSize: 12px;
@@ -1,5 +1,6 @@
1
1
  @import "variables.scss";
2
2
  @import "../../variables.scss";
3
+ @import "../../bootstrap.scss";
3
4
 
4
5
  .pdf-app {
5
6
  --pdf-app-background-color: #{$pdf-app-background-color};
@@ -1,4 +1,5 @@
1
1
  @import '../variables';
2
+ @import '../bootstrap';
2
3
 
3
4
  $tooltip-color: #fff9ae;
4
5
  $dark-tooltip-color: #7b52eb;
@@ -7,7 +7,7 @@
7
7
  >
8
8
  <slot name="content">
9
9
  <div class="modal-content itf-append-context" ref="content" @click.stop>
10
- <div v-if="title" class="modal-header">
10
+ <div v-if="title || $slots.title" class="modal-header">
11
11
  <slot name="title">
12
12
  <h5 class="modal-title" :id="modalId">{{title}}</h5>
13
13
  </slot>
@@ -1,5 +1,6 @@
1
1
  @import "variables.scss";
2
2
  @import "../../../assets/scss/variables.scss";
3
+ @import "../../../assets/scss/bootstrap.scss";
3
4
 
4
5
  .pdf-app {
5
6
  --pdf-app-background-color: #{$pdf-app-background-color};
@@ -0,0 +1,162 @@
1
+ <template>
2
+
3
+ <recycle-scroller
4
+ class="scroller"
5
+ page-mode
6
+ :items="rows"
7
+ :item-size="36"
8
+ key-field="Id"
9
+ v-slot="{ item }"
10
+ direction="vertical"
11
+ >
12
+ <div group="items" data-test="table-item" class="table-view-item grouped draggable-item">
13
+ <div class="table-row-template">
14
+ <div accept-group="items" class="table-view-body-space"></div>
15
+ <div class="shadow-area">
16
+ <div class="toggler-wrapper"></div>
17
+ <div class="handle-wrapper hover-only">
18
+ <a href="" class="context-menu-toggle table-item-options-menu">
19
+ <div class="v-popper--has-tooltip drag-handle">
20
+ <i data-test="table-item-options" class="ic-drag"></i>
21
+ </div>
22
+ </a>
23
+ </div>
24
+ </div>
25
+ <div class="indicator sticky tw-border-gray dark:tw-border-gray-invert tw-border-r tw-bg-white dark:tw-bg-white-invert tw-border-b ">
26
+ <div class="fill on-rest table-view-row-count">
27
+ <span>{{ item.Id }}</span>
28
+ </div>
29
+ <div class="fill on-hover">
30
+ <a href="" data-test="table-item-expand">
31
+ <i class="ic-expand"></i>
32
+ </a>
33
+ <div class="">
34
+ <a data-test="table-row-generator" href="">
35
+ <i class="ic-plus"></i>
36
+ </a>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ <div accept-group="items" class="hbox table-item-inner">
41
+ <div v-for="(column, n) in columns" :data-column="n" :style="`width: ${column.width}px`">
42
+ <div
43
+ class="table-view-item-value tw-flex tw-relative tw-h-full tw-border-b tw-border-r tw-border-gray tw-bg-white dark:tw-bg-white-invert dark:tw-border-gray-invert hover:tw-bg-light dark:hover:tw-bg-gray-invert tw-items-stretch">
44
+ {{ item.Name }}
45
+ </div>
46
+ </div>
47
+
48
+ <div class="table-view-item-value extra"></div>
49
+ <div class="boundary top"></div>
50
+ <div class="boundary right"></div>
51
+ <div class="boundary bottom"></div>
52
+ <div class="boundary left"></div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </recycle-scroller>
57
+ </template>
58
+ <style lang="scss">
59
+ .table-row-template {
60
+ display: flex;
61
+ height: 100%;
62
+ align-items: stretch;
63
+ }
64
+
65
+ .table-item-inner {
66
+ height: 100%;
67
+ flex-grow: 1;
68
+ position: relative;
69
+ display: flex;
70
+ flex-direction: row;
71
+ }
72
+
73
+ .table-view-item-value {
74
+ text-overflow: ellipsis;
75
+ background-color: rgb(255 255 255 / 1);
76
+ border-right: 1px solid rgb(238 238 238 / 1);
77
+ border-bottom: 1px solid rgb(238 238 238 / 1);
78
+ align-items: stretch;
79
+ height: 100%;
80
+ display: flex;
81
+ position: relative;
82
+
83
+ &:hover {
84
+ background-color: rgb(250 251 252 / 1);
85
+ }
86
+ }
87
+
88
+ .table-item-inner .extra {
89
+ height: 100%;
90
+ flex-grow: 1;
91
+ border-color: rgb(238 238 238 / 1);
92
+ }
93
+
94
+ .indicator {
95
+ height: 100%;
96
+ left: var(--shadow-area-width);
97
+ width: var(--indicator-area-width);
98
+ z-index: 4;
99
+ position: -webkit-sticky;
100
+ position: sticky;
101
+ background-color: rgb(255 255 255 / 1);
102
+ border-right: 1px solid rgb(238 238 238 / 1);
103
+ border-bottom: 1px solid rgb(238 238 238 / 1);
104
+ }
105
+
106
+ .table-item-inner .boundary {
107
+ z-index: 3;
108
+ position: absolute;
109
+ }
110
+
111
+ .table-item-inner .boundary.top, .table-item-inner .boundary.bottom {
112
+ width: 100%;
113
+ border-top: thin solid #0000;
114
+ top: 0;
115
+ }
116
+
117
+ .table-item-inner .boundary {
118
+ z-index: 3;
119
+ position: absolute;
120
+ }
121
+
122
+ .table-item-inner .boundary.left, .table-item-inner .boundary.right {
123
+ height: 100%;
124
+ border-left: thin solid #0000;
125
+ left: 0;
126
+ }
127
+
128
+ .table-item-inner .boundary.right {
129
+ left: auto;
130
+ right: 0;
131
+ }
132
+
133
+ .table-item-inner .boundary.bottom {
134
+ top: auto;
135
+ bottom: 0;
136
+ }
137
+ .table-small-row .table-view-item {
138
+ height: 36px;
139
+ }
140
+ .vue-recycle-scroller {
141
+ position: relative;
142
+ }
143
+ </style>
144
+
145
+ <script>
146
+ import { Vue, Component, Prop } from 'vue-property-decorator';
147
+ import { RecycleScroller } from 'vue-virtual-scroller'
148
+
149
+ export default @Component({
150
+ name: 'itfTableBody',
151
+ components: {
152
+ RecycleScroller
153
+ }
154
+ })
155
+ class itfTableBody extends Vue {
156
+ @Prop() columns;
157
+ @Prop() rows;
158
+
159
+ mounted() {
160
+ }
161
+ }
162
+ </script>
@@ -0,0 +1,226 @@
1
+ <template>
2
+
3
+ <div class="itf-table-group table-small-row">
4
+ <!-- Тут показується лінія при драг н дропі -->
5
+ <div accept-group="tablegroups" class="preline over">
6
+ <div class="line"></div>
7
+ </div>
8
+
9
+
10
+ <div data-test="table-group-wrapper" class="table-group-wrapper" :style="`--row-count: ${isShowTable ? rows.length : 0}`">
11
+ <div class="">
12
+ <div data-test="table-group" class="position-relative me-4">
13
+ <div group="tablegroups" class="draggable-item" data-draggable-mirror="{&quot;xAxis&quot;:false}">
14
+ <div class="table-row-template d-flex align-items-stretch" style="height: var(--group-title-height)">
15
+ <div class="shadow-area"></div>
16
+ <div class="header-wrapper drag-handle">
17
+ <div class="header-content tw-sticky tw-flex tw-items-center tw-gap-5 tw-px-4">
18
+ <a href="" class="collapse-arrow" @click.prevent="toggleGroup">
19
+ <itf-icon :name="isShowTable ? 'chevron_down' : 'chevron_right'" />
20
+ </a>
21
+ <a href="" class="d-flex align-items-center line-overflow group-header-value" data-test="group-value-group-label-value">
22
+ <span title="To Do" class="badge text-decoration-none" style="background-color: #FFA2A2; color: #4C4E69;">To Do</span>
23
+ </a>
24
+ <div data-test="table-group-item-count" class="table-group-item-count">
25
+ 16
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+ <!-- Сама таблиця -->
33
+ <div v-if="isShowTable" class="table-view-body">
34
+ <itf-table-header :columns="columns" />
35
+ <itf-table-body :rows="rows" :columns="columns" />
36
+ </div>
37
+
38
+ <!-- Лінія додати нову -->
39
+ <div v-if="isShowTable" data-v-54c5481c="" data-v-1fb25116=""
40
+ class="table-row-template tw-flex tw-items-stretch tw-h-[var(--table-small-row-size)]">
41
+ <div data-v-54c5481c="" class="tw-bg-light dark:tw-bg-light-invert shadow-area"></div>
42
+ <a data-v-54c5481c="" href="" data-test="table-add-new-item"
43
+ class="tw-flex tw-items-center tw-flex-grow tw-rounded-b tw-border-b-1 tw-border-r-1 tw-border-gray dark:tw-border-gray-invert tw-bg-white dark:tw-bg-white-invert hover:tw-bg-gray dark:hover:tw-bg-gray-invert tw-text-blue"><span
44
+ data-v-54c5481c=""
45
+ class="tw-sticky tw-left-[var(--shadow-area-width)] tw-flex tw-items-center tw-gap-3 tw-px-4"><i
46
+ data-v-54c5481c="" class="ic-plus"></i> <span
47
+ data-v-54c5481c="">Add new row</span></span></a></div>
48
+ <!-- Групування -->
49
+ <div v-if="isShowTable" data-v-54c5481c="" data-v-1fb25116=""
50
+ class="table-row-template tw-flex tw-items-stretch tw-h-[var(--table-small-row-size)] table-summary">
51
+ <div data-v-54c5481c="" class="tw-bg-light dark:tw-bg-light-invert shadow-area"></div>
52
+ <div data-v-54c5481c=""
53
+ class="tw-flex tw-flex-row tw-items-center tw-ml-[var(--indicator-area-width)]"><span
54
+ data-v-54c5481c="" data-column="0" class="tw-relative line-overflow"
55
+ style="width: 400px;"><a href=""
56
+ class="context-menu-toggle tw-w-full tw-text-gray dark:tw-text-gray-invert tw-text-sm tw-flex tw-pr-3 tw-flex tw-items-stretch tw-justify-end"
57
+ data-v-54c5481c=""><span data-v-016abbf7=""
58
+ data-test="summary-column"
59
+ data-attribute-id="621b5429-3de2-4731-979f-ef0e0c974a6e"
60
+ class="invisible-summary tw-flex tw-items-center tw-justify-end tw-flex-auto"><span
61
+ data-v-016abbf7="" class="summary-placeholder hbox tw-items-center tw-justify-center">
62
+ Summary
63
+ <i data-v-016abbf7=""
64
+ class="tw-ml-2 tw-text-gray dark:tw-text-gray-invert ic-arrow-down"></i></span></span>
65
+ <!----></a></span><span data-v-54c5481c="" data-column="1"
66
+ class="tw-relative line-overflow" style="width: 240px;"><a
67
+ href=""
68
+ class="context-menu-toggle tw-w-full tw-text-gray dark:tw-text-gray-invert tw-text-sm tw-flex tw-pr-3 tw-flex tw-items-stretch tw-justify-end"
69
+ data-v-54c5481c=""><span data-v-016abbf7="" data-test="summary-column"
70
+ data-attribute-id="afaaafd3-6f27-470c-9f85-4f0312eaefc0"
71
+ class="invisible-summary tw-flex tw-items-center tw-justify-end tw-flex-auto visible-summary"><span
72
+ data-v-016abbf7=""><span data-v-016abbf7="" class="summary-placeholder tw-mr-2">
73
+ Earliest:
74
+ </span> <span data-v-016abbf7="">
75
+ Jun 28, 2023
76
+ </span></span></span> <!----></a></span><span data-v-54c5481c="" data-column="2"
77
+ class="tw-relative line-overflow"
78
+ style="width: 240px;"><a href=""
79
+ class="context-menu-toggle tw-w-full tw-text-gray dark:tw-text-gray-invert tw-text-sm tw-flex tw-pr-3 tw-flex tw-items-stretch tw-justify-end"
80
+ data-v-54c5481c=""><span
81
+ data-v-016abbf7="" data-test="summary-column"
82
+ data-attribute-id="0c545783-9fb4-4105-9558-9b105415bb07"
83
+ class="invisible-summary tw-flex tw-items-center tw-justify-end tw-flex-auto"><span
84
+ data-v-016abbf7="" class="summary-placeholder hbox tw-items-center tw-justify-center">
85
+ Summary
86
+ <i data-v-016abbf7=""
87
+ class="tw-ml-2 tw-text-gray dark:tw-text-gray-invert ic-arrow-down"></i></span></span>
88
+ <!----></a></span><span data-v-54c5481c="" data-column="3"
89
+ class="tw-relative line-overflow" style="width: 120px;"><a
90
+ href=""
91
+ class="context-menu-toggle tw-w-full tw-text-gray dark:tw-text-gray-invert tw-text-sm tw-flex tw-pr-3 tw-flex tw-items-stretch tw-justify-end"
92
+ data-v-54c5481c=""><span data-v-016abbf7="" data-test="summary-column"
93
+ data-attribute-id="b3935a97-9dc6-49bc-ae6b-767f3d88b86e"
94
+ class="invisible-summary tw-flex tw-items-center tw-justify-end tw-flex-auto"><span
95
+ data-v-016abbf7="" class="summary-placeholder hbox tw-items-center tw-justify-center">
96
+ Summary
97
+ <i data-v-016abbf7=""
98
+ class="tw-ml-2 tw-text-gray dark:tw-text-gray-invert ic-arrow-down"></i></span></span>
99
+ <!----></a></span></div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ </template>
107
+ <style lang="scss">
108
+ .itf-table-group {
109
+ --group-title-height: 40px;
110
+ --table-small-row-size: 36px;
111
+ --table-row-height: 36px;
112
+ --shadow-area-width: 45px;
113
+ --indicator-area-width: 38px;
114
+
115
+ .preline {
116
+ display: flex;
117
+ align-items: center;
118
+ height: 32px;
119
+
120
+ &.over .line {
121
+ width: 100%;
122
+ border-bottom: 4px solid rgb(71 190 255 / 1);
123
+ }
124
+ }
125
+
126
+ .table-group-wrapper {
127
+ display: flex;
128
+ height: calc(var(--group-title-height) + var(--table-small-row-size)*3 + var(--row-count)*var(--table-row-height));
129
+ }
130
+
131
+ .shadow-area {
132
+ z-index: 4;
133
+ height: 100%;
134
+ width: var(--shadow-area-width);
135
+ justify-content: center;
136
+ align-items: center;
137
+ gap: 4px;
138
+ display: flex;
139
+ position: -webkit-sticky;
140
+ position: sticky;
141
+ left: 0;
142
+ background: #fff;
143
+ }
144
+ .header-wrapper:not(.collapsed *) {
145
+ flex-grow: 1;
146
+ border-bottom-right-radius: 0!important;
147
+ border-bottom-left-radius: 0!important;
148
+ }
149
+ .header-wrapper {
150
+ background-color: rgb(218 218 218 / 1);
151
+ left: var(--shadow-area-width);
152
+ align-items: center;
153
+ display: flex;
154
+ position: -webkit-sticky;
155
+ position: sticky;
156
+ border-radius: 0.1875rem;
157
+ border-bottom-width: 1px;
158
+ border-color: rgb(238 238 238 / 1);
159
+ }
160
+ .header-content:not(.draggable-mirror *) {
161
+ left: var(--shadow-area-width);
162
+ padding-left: 0.75rem;
163
+ padding-right: 0.75rem;
164
+ gap: 1rem;
165
+ align-items: center;
166
+ position: sticky;
167
+ display: flex;
168
+ }
169
+ .table-group-item-count {
170
+ padding-top: 0.125rem;
171
+ padding-bottom: 0.125rem;
172
+ padding-left: 0.5rem;
173
+ padding-right: 0.5rem;
174
+ background-color: rgb(250 251 252 / 1);
175
+ border-radius: 0.1875rem;
176
+ }
177
+ .vue-recycle-scroller__item-wrapper {
178
+ -webkit-box-flex: 1;
179
+ -ms-flex: 1;
180
+ flex: 1;
181
+ -webkit-box-sizing: border-box;
182
+ box-sizing: border-box;
183
+ overflow: hidden;
184
+ position: relative;
185
+ }
186
+ .vue-recycle-scroller.direction-vertical .vue-recycle-scroller__item-wrapper {
187
+ width: 100%;
188
+ }
189
+ .table-view-wrapper .vue-recycle-scroller__item-wrapper {
190
+ overflow: visible;
191
+ }
192
+ .vue-recycle-scroller.ready .vue-recycle-scroller__item-view {
193
+ position: absolute;
194
+ top: 0;
195
+ left: 0;
196
+ will-change: transform;
197
+ }
198
+ }
199
+ </style>
200
+ <script>
201
+ import {Vue, Component, Prop, PropSync} from 'vue-property-decorator';
202
+ import itfButton from '../button/Button.vue';
203
+ import itfIcon from '../icon/Icon.vue';
204
+ import itfTableBody from './TableBody.vue';
205
+ import itfTableHeader from './TableHeader.vue';
206
+
207
+ export default @Component({
208
+ name: 'itfTableGroup',
209
+ components: {
210
+ itfButton,
211
+ itfIcon,
212
+ itfTableBody,
213
+ itfTableHeader
214
+ }
215
+ })
216
+ class itfTableGroup extends Vue {
217
+ @Prop() columns;
218
+ @Prop() rows;
219
+
220
+ isShowTable = true;
221
+
222
+ toggleGroup() {
223
+ this.isShowTable = !this.isShowTable;
224
+ }
225
+ }
226
+ </script>
@@ -0,0 +1,223 @@
1
+ <template>
2
+
3
+ <div class="itf-table-header">
4
+ <div ref="container" class="table-row-template">
5
+ <div accept-group="items" class="table-view-body-space"></div>
6
+ <div class="tw-bg-light dark:tw-bg-light-invert shadow-area"></div>
7
+ <div class="table-view-header-value reserved sticky"></div>
8
+
9
+ <div v-for="(column, n) in sortedColumns" :key="n" data-test="table-header-column" :data-column="n" :data-id="column.Id"
10
+ class="table-view-header-value" style="width: 100px; left: 0px;">
11
+ <div accept-group="tablecolumns" class="table-view-header-space">
12
+ <div class="table-view-header-dropzone"></div>
13
+ </div>
14
+ <div group="tablecolumns" class="table-header draggable-item drag-handle" data-draggable-mirror="{&quot;yAxis&quot;:false}">
15
+ <a href="" class="context-menu-toggle tw-flex-auto line-overflow">
16
+ <span title="Name">
17
+ <itf-icon name="type_select" :size="16" /> {{n}}
18
+ </span>
19
+ </a>
20
+ </div>
21
+ <div ref="resizeHandle" class="resize-handle"></div>
22
+ </div>
23
+
24
+ <div class="table-view-header-value">
25
+ <div data-v-14c77b30="" class="attribute-creator" data-v-54c5481c="">
26
+ <div data-v-fd8e8e6a="" data-test="dropdown"
27
+ class="dropdown-body tw-relative tw-cursor-pointer tw-w-full"><!---->
28
+ <div data-v-fd8e8e6a="" tabindex="0" data-test="dropdown-toggle">
29
+ <div data-v-fd8e8e6a=""
30
+ class="hbox tw-flex-auto tw-items-center tw-justify-center"><a
31
+ data-v-14c77b30="" data-v-fd8e8e6a="" href=""
32
+ data-test="table-header-add-column" class="tw-text-blue tw-mx-4"><span
33
+ data-v-14c77b30="" data-v-fd8e8e6a="" class="ic-plus"></span>
34
+ Add column
35
+ </a></div>
36
+ </div>
37
+ <span data-v-b4d986ec="" data-v-fd8e8e6a="" style="display: none;"> <div
38
+ data-v-b4d986ec="" class="context-modal-backdrop"></div><div
39
+ data-v-b4d986ec="" class="context-modal tw-min-w-[280px]"><!----> <!---->
40
+ <!----></div></span></div> <!----></div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ </template>
46
+ <style lang="scss">
47
+ .itf-table-header {
48
+ position: sticky;
49
+ top: 0;
50
+ z-index: calc(var(--row-count) + 1);
51
+
52
+ .table-row-template {
53
+ display: flex;
54
+ align-items: stretch;
55
+ height: var(--table-small-row-size);
56
+ }
57
+ .table-view-header-value {
58
+ align-items: center;
59
+ height: 36px;
60
+ white-space: nowrap;
61
+ border-top: 0;
62
+ border-left: 0;
63
+ border-right-width: thin;
64
+ border-bottom-width: thin;
65
+ border-style: solid;
66
+ position: relative;
67
+ display: flex;
68
+ border-right-color: rgb(218 218 218 / 1);
69
+ border-bottom-color: rgb(238 238 238 / 1);
70
+ background-color: rgb(238 238 238 / 1);
71
+
72
+ &.reserved {
73
+ left: var(--shadow-area-width);
74
+ width: var(--indicator-area-width);
75
+ }
76
+ &.sticky {
77
+ position: sticky;
78
+ position: -webkit-sticky;
79
+ z-index: calc(var(--row-count) + 4);
80
+ }
81
+ }
82
+ .table-header {
83
+ flex: 0 0 auto;
84
+ overflow: hidden;
85
+ width: 100%;
86
+ text-overflow: ellipsis;
87
+ margin-right: -5px;
88
+ padding-left: 0.75rem;
89
+ padding-right: 0.75rem;
90
+ gap: 0.25rem;
91
+ align-items: center;
92
+ display: flex;
93
+ }
94
+ .resize-handle {
95
+ width: 4px;
96
+ height: 36px;
97
+ position: absolute;
98
+ right: -2px;
99
+ top: 0;
100
+ background: #29ACE1;
101
+ border-radius: 2px;
102
+ z-index: 100;
103
+ opacity: 0;
104
+ cursor: ew-resize;
105
+ transition: .2s;
106
+
107
+ &:hover {
108
+ opacity: 1;
109
+ }
110
+ }
111
+ .context-menu-toggle {
112
+ display: flex;
113
+ flex: 1 1 auto;
114
+ text-overflow: ellipsis;
115
+ white-space: nowrap;
116
+ overflow: hidden;
117
+
118
+ span {
119
+ font-size: 12px;
120
+ line-height: 36px;
121
+ }
122
+ }
123
+ .itf-sortable-list {
124
+ display: flex;
125
+ }
126
+ .table-view-header-space {
127
+ height: 100%;
128
+ position: absolute;
129
+ left: 0;
130
+ display: none;
131
+ background: transparent;
132
+ z-index: 100;
133
+ width: 2px;
134
+ margin-left: -1px;
135
+ }
136
+ .draggable-mirror {
137
+ opacity: .5;
138
+ }
139
+ .draggable-container--over .table-view-header-space {
140
+ display: block;
141
+ }
142
+ .draggable-container--over .draggable--over .table-view-header-space {
143
+ background: #47BEFF;
144
+ }
145
+ .table-view-header-space > .table-view-header-dropzone {
146
+ position: absolute;
147
+ top: 0;
148
+ right: -50px;
149
+ bottom: 0;
150
+ left: -50px;
151
+ }
152
+ .table-view-header-value:not(.reserved):hover {
153
+ background-color: rgb(218 218 218 / 1);
154
+ }
155
+ .draggable-source--is-dragging {
156
+ z-index: 100;
157
+ }
158
+ }
159
+ </style>
160
+ <script>
161
+ import { Vue, Component, Prop, PropSync } from 'vue-property-decorator';
162
+ import itfButton from '../button/Button.vue';
163
+ import itfIcon from '../icon/Icon.vue';
164
+ import { Draggable, Droppable } from '@shopify/draggable';
165
+
166
+ export default @Component({
167
+ name: 'itfTableHeader',
168
+ components: {
169
+ itfButton,
170
+ itfIcon
171
+ }
172
+ })
173
+ class itfTable extends Vue {
174
+ @PropSync('columns', { type: Array, default: () => ([]) }) sortedColumns;
175
+
176
+ mounted() {
177
+ const draggable = new Draggable(this.$refs.container, {
178
+ draggable: '[group="tablecolumns"]',
179
+ mirror: {
180
+ yAxis: false,
181
+ appendTo: this.$refs.container
182
+ }
183
+ });
184
+ const droppable = new Droppable(this.$refs.container, {
185
+ draggable: '[group="tablecolumns"]',
186
+ dropzone: '[accept-group="tablecolumns"]'
187
+ });
188
+ draggable.on('drag:stop', (event) => {
189
+ console.info('dropped', event);
190
+ });
191
+
192
+ // resize
193
+ const resizeHandle = this.$refs.resizeHandle;
194
+ resizeHandle.forEach((handle) => {
195
+ handle.addEventListener('mousedown', (event) => {
196
+ const column = event.target.closest('.table-view-header-value');
197
+ const body = event.target.closest('.table-view-body');
198
+ const columnWidth = column.offsetWidth;
199
+ const startX = event.pageX;
200
+ const index = column.getAttribute('data-column');
201
+ const columns = body.querySelectorAll(`[data-column="${index}"]`);
202
+ const mouseMoveHandler = (event) => {
203
+ const delta = event.pageX - startX;
204
+ columns.forEach((column) => {
205
+ column.style.width = `${columnWidth + delta}px`;
206
+ });
207
+ this.changeColumn(index, { width: columnWidth + delta });
208
+ };
209
+ const mouseUpHandler = () => {
210
+ document.removeEventListener('mousemove', mouseMoveHandler);
211
+ document.removeEventListener('mouseup', mouseUpHandler);
212
+ };
213
+ document.addEventListener('mousemove', mouseMoveHandler);
214
+ document.addEventListener('mouseup', mouseUpHandler);
215
+ });
216
+ });
217
+ }
218
+
219
+ changeColumn(index, params) {
220
+ console.info(index, params);
221
+ }
222
+ }
223
+ </script>
@@ -0,0 +1,222 @@
1
+ <template>
2
+
3
+ <div
4
+ class="table-row-template flex items-stretch" view-settings="[object Object]">
5
+ <div accept-group="items" class="table-view-body-space"
6
+ ></div>
7
+ <div class="bg-light dark:bg-light-invert shadow-area">
8
+ <div class="toggler-wrapper"><!----></div>
9
+ <div class="handle-wrapper hover-only"><a
10
+ href="" class="context-menu-toggle table-item-options-menu"
11
+ >
12
+ <div class="v-popper--has-tooltip drag-handle"><i
13
+ data-test="table-item-options"
14
+ class="ic-drag"></i>
15
+ </div> <!----></a></div>
16
+ </div>
17
+ <div
18
+ class="indicator sticky border-gray dark:border-gray-invert border-r bg-white dark:bg-white-invert border-b ">
19
+ <div class="fill on-rest table-view-row-count"><span
20
+ >1</span></div>
21
+ <div class="fill on-hover"><a
22
+ href=""
23
+ data-test="table-item-expand"><i
24
+ class="ic-expand"></i></a>
25
+ <div class="top"><a
26
+ data-test="table-row-generator" href=""><i
27
+ class="ic-plus"></i></a>
28
+ </div>
29
+ <div class=""><a
30
+ data-test="table-row-generator"
31
+ href=""><i
32
+ class="ic-plus"></i></a></div>
33
+ </div>
34
+ </div>
35
+ <div accept-group="items" class="hbox table-item-inner">
36
+ <div data-column="0" class="sticky last-sticky-column"
37
+ style="width: 240px; left: 83px;">
38
+ <div
39
+ class="table-view-item-value flex relative h-full border-b border-r border-gray bg-white dark:bg-white-invert dark:border-gray-invert hover:bg-light dark:hover:bg-gray-invert items-stretch"
40
+ >
41
+ <div
42
+ class="flex-auto flex gap-2 items-start p-3 show-hidden-hover overflow-hidden">
43
+ <a href=""
44
+ class="context-menu-toggle h-full line-overflow cursor-pointer flex-auto"><span>
45
+ Jun 30th
46
+ </span> <!----></a> <!---->
47
+ <div class="flex items-center justify-end hover-show"><a href="" tabindex="0"
48
+ class="text-sm p-2 ic-exit"></a>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ <div data-column="1" class="" style="width: 400px;">
54
+ <div
55
+ class="table-view-item-value flex relative h-full border-b border-r border-gray bg-white dark:bg-white-invert dark:border-gray-invert hover:bg-light dark:hover:bg-gray-invert items-stretch"
56
+ ><textarea
57
+ data-test="table-text-value" rows="2" type="text"
58
+ wrap="hard"
59
+ class="h-full px-3 input-inline text-value w-full hide-scrollbar !leading-[35px] whitespace-nowrap"></textarea>
60
+ </div>
61
+ </div>
62
+ <div data-column="2" class="" style="width: 240px;">
63
+ <div
64
+ class="table-view-item-value flex relative h-full border-b border-r border-gray bg-white dark:bg-white-invert dark:border-gray-invert hover:bg-light dark:hover:bg-gray-invert items-stretch"
65
+ >
66
+ <div
67
+ class="flex-auto flex gap-2 items-start p-3 show-hidden-hover overflow-hidden">
68
+ <a href=""
69
+ class="context-menu-toggle h-full line-overflow cursor-pointer flex-auto"><span>
70
+ Jul 13th
71
+ </span> <!----></a> <!---->
72
+ <div class="flex items-center justify-end hover-show"><a href="" tabindex="0"
73
+ class="text-sm p-2 ic-exit"></a>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ <div data-column="3" class="" style="width: 120px;">
79
+ <div
80
+ class="table-view-item-value flex relative h-full border-b border-r border-gray bg-white dark:bg-white-invert dark:border-gray-invert hover:bg-light dark:hover:bg-gray-invert items-stretch"
81
+ ><a href=""
82
+ class="context-menu-toggle table-value-label flex flex-auto p-3 w-full"><span
83
+ class="table-value-label-selected w-full h-full vbox"><span
84
+ class="flex gap-2 hide-scrollbar overflow-auto"><span
85
+ title="Upcoming"
86
+ class="label-value"
87
+ style="--label-color: #A7E6FF; --label-text: #4C4E69;">
88
+ Upcoming
89
+ <!----></span></span></span> <!----></a></div>
90
+ </div>
91
+ <div
92
+ class="extra border-b border-r border-gray dark:border-gray-invert"></div>
93
+ <div class="boundary top"></div>
94
+ <div class="boundary right"></div>
95
+ <div class="boundary bottom"></div>
96
+ <div class="boundary left"></div> <!----></div>
97
+ </div>
98
+
99
+ </template>
100
+ <script>
101
+ import { Vue, Component, Prop, PropSync } from 'vue-property-decorator';
102
+ import itfButton from '../button/Button.vue';
103
+ import itfIcon from '../icon/Icon.vue';
104
+
105
+ export default @Component({
106
+ name: 'itfTable',
107
+ components: {
108
+ itfButton,
109
+ itfIcon,
110
+ }
111
+ })
112
+ class itfTable extends Vue {
113
+ @PropSync('sorting', { type: Object, default: () => ({}) }) sortedColumns;
114
+ @Prop({ required: true, type: Array }) columns;
115
+ @Prop({ required: true, type: Array }) rows;
116
+ @Prop({ default: false, type: Boolean }) stickyHeader;
117
+ @Prop({ default: false, type: Boolean }) stickyColumn;
118
+ @Prop({ default: false, type: Boolean }) stickyLastColumn;
119
+ @Prop({ default: false, type: Boolean }) loading;
120
+ @Prop({ default: false, type: Boolean }) striped;
121
+ @Prop({ default: false, type: Boolean }) hoverable;
122
+ @Prop({ default: false, type: Boolean }) small;
123
+
124
+ sortDirection = 'asc';
125
+ scrolledX = false;
126
+ scrolledEnd = false;
127
+ scrolledY = false;
128
+ stickyObserver = null;
129
+
130
+ scrollFunc = null;
131
+
132
+ mounted() {
133
+ if (this.$refs.scrollContainer && this.$refs.scrollContainer2) {
134
+ this.scrollFunc = initSyncScroll(this.$refs.scrollContainer, this.$refs.scrollContainer2, (isEnd) => {
135
+ this.scrolledX = true;
136
+ this.scrolledEnd = isEnd;
137
+ }, () => {
138
+ this.scrolledX = false;
139
+ });
140
+ } else {
141
+ setTimeout(() => {
142
+ this.scrollFunc = initSyncScroll(this.$refs.scrollContainer, this.$refs.scrollContainer2, (isEnd) => {
143
+ this.scrolledX = true;
144
+ this.scrolledEnd = isEnd;
145
+ }, () => {
146
+ this.scrolledX = false;
147
+ });
148
+ }, 1000);
149
+ }
150
+
151
+ if (this.$refs.stickyHeader) {
152
+ this.stickyObserver = new IntersectionObserver(([e]) => {
153
+ return this.scrolledY = e.intersectionRatio < 1;
154
+ }, {
155
+ rootMargin: `-${this.$refs.stickyHeader.offsetHeight + 5}px 0px 0px 0px`,
156
+ threshold: [1]
157
+ });
158
+ this.stickyObserver.observe(this.$refs.stickyHeader);
159
+ }
160
+ function initSyncScroll(el1, el2, stuck, unstuck) {
161
+ function isScrolledEnd(el) {
162
+ return el.scrollWidth === el.getBoundingClientRect().width // якщо контейнер менший ширини екрану
163
+ || el.scrollLeft === el.scrollWidth - el.getBoundingClientRect().width; // якщо контейнер більший ширини екрану
164
+ }
165
+
166
+ function func1() {
167
+ el2.scrollLeft = el1.scrollLeft;
168
+ if (el1.scrollLeft > 0) {
169
+ stuck(isScrolledEnd(el1));
170
+ } else {
171
+ unstuck();
172
+ }
173
+ }
174
+
175
+ function func2() {
176
+ el1.scrollLeft = el2.scrollLeft;
177
+ if (el2.scrollLeft > 0) {
178
+ stuck(isScrolledEnd(el2));
179
+ } else {
180
+ unstuck();
181
+ }
182
+ }
183
+
184
+ if (isScrolledEnd(el1)) {
185
+ stuck(true);
186
+ unstuck();
187
+ }
188
+
189
+ el1.addEventListener('scroll', func1);
190
+ el2.addEventListener('scroll', func2);
191
+
192
+ return [func1, func2];
193
+ }
194
+ }
195
+
196
+ beforeDestroy() {
197
+ if (this.scrollFunc) {
198
+ const [func1, func2] = this.scrollFunc;
199
+ this.$refs.scrollContainer.removeEventListener('scroll', func1);
200
+ this.$refs.scrollContainer2.removeEventListener('scroll', func2);
201
+ }
202
+ }
203
+
204
+ get columnsGridStyle() {
205
+ return this.columns.map((column) => {
206
+ if (column.min) {
207
+ return `minmax(${column.min}px, ${column.max ? `${column.max}px` : 'auto'})`;
208
+ }
209
+ return `${column.fraction || 1}fr`;
210
+ }).join(' ');
211
+ }
212
+
213
+ sortBy(column, index) {
214
+ if (!column.sortable) {
215
+ return;
216
+ }
217
+ const { name } = this.columns[index];
218
+ const direction = this.sortedColumns[name] === 'asc' ? 'desc' : 'asc';
219
+ this.sortedColumns = { [name]: direction };
220
+ }
221
+ }
222
+ </script>
@@ -2,6 +2,7 @@ import { storiesOf } from '@storybook/vue';
2
2
  import itfApp from '../app/App.vue';
3
3
  import itfButton from '../button/Button.vue';
4
4
  import itfTable from './Table.vue';
5
+ import itfTableGroup from './TableGroup.vue';
5
6
 
6
7
  const exampleData = [{
7
8
  Employee: 'Cool',
@@ -75,69 +76,84 @@ storiesOf('Common', module)
75
76
  .add('Simple Table', () => ({
76
77
  components: {
77
78
  itfTable,
78
- itfButton
79
+ itfButton,
80
+ itfTableGroup
79
81
  },
80
82
  data() {
81
83
  return {
84
+ list: Array.from({length: 30}).map((_, i) => ({
85
+ Id: i,
86
+ Name: `Item #${i}`
87
+ })),
82
88
  columns: [{
83
89
  text: 'Employee',
84
90
  name: 'Employee',
91
+ width: 200,
85
92
  min: 250,
86
93
  max: 250
87
94
  }, {
88
95
  text: 'Total',
89
96
  name: 'Total',
97
+ width: 200,
90
98
  min: 150,
91
99
  max: 150
92
100
  }, {
93
101
  text: 'FTE',
94
102
  name: 'FTE',
103
+ width: 200,
95
104
  min: 100,
96
105
  max: 100
97
106
  }, {
98
107
  text: 'Position',
99
108
  name: 'Position',
109
+ width: 200,
100
110
  min: 200,
101
111
  max: 200
102
112
  }, {
103
113
  text: 'Office',
104
114
  name: 'Office',
115
+ width: 200,
105
116
  min: 150,
106
117
  max: 150
107
118
  }, {
108
119
  text: 'Internal',
109
120
  name: 'MinutesInternal',
121
+ width: 200,
110
122
  min: 150,
111
123
  max: 150,
112
124
  sortable: true
113
125
  }, {
114
126
  text: 'External',
115
127
  name: 'MinutesExternal',
128
+ width: 200,
116
129
  min: 150,
117
130
  max: 150,
118
131
  sortable: true
119
132
  }, {
120
133
  text: 'Compensation (Internal)',
121
134
  name: 'AmountInternal',
135
+ width: 200,
122
136
  min: 200,
123
137
  max: 200
124
138
  }, {
125
139
  text: 'Compensation (External)',
126
140
  name: 'AmountExternal',
141
+ width: 200,
127
142
  min: 150,
128
143
  max: 150
129
144
  }, {
130
145
  text: 'Bonus/Commission',
131
146
  name: 'AmountShare',
147
+ width: 200,
132
148
  min: 150,
133
149
  max: 150
134
150
  }, {
135
151
  text: 'Actions',
136
152
  name: 'Notes',
153
+ width: 200,
137
154
  min: 80,
138
155
  max: 80
139
- }],
140
- list: exampleData
156
+ }]
141
157
  }
142
158
  },
143
159
  template: `<div>
@@ -156,14 +172,18 @@ storiesOf('Common', module)
156
172
 
157
173
  <h3>Example</h3>
158
174
 
159
- <itf-table
175
+ <itf-table-group :columns="columns" :rows="list" />
176
+
177
+ <itf-table-group :columns="columns" :rows="list" />
178
+
179
+ <!--itf-table
160
180
  :columns="columns"
161
181
  :rows="list"
162
182
  >
163
183
  <template #column.Employee="{ item }">
164
184
  {{item.Employee}}
165
185
  </template>
166
- </itf-table>
186
+ </itf-table-->
167
187
 
168
188
  </div>`,
169
189
  }))