1
1
<script setup>
2
- import { ref , computed , onMounted , watch , nextTick } from " vue" ;
2
+ import { ref , computed , onMounted , watch , nextTick , onBeforeUnmount } from " vue" ;
3
3
import { useConfig } from " ../useConfig" ;
4
4
import { useNestedProp } from " ../useNestedProp" ;
5
5
import { createCsvContent , createUid , downloadCsv , error , objectIsEmpty , opacity } from " ../lib" ;
@@ -59,8 +59,8 @@ const FINAL_CONFIG = computed(() => {
59
59
return useNestedProp ({
60
60
userConfig: props .config ,
61
61
defaultConfig: DEFAULT_CONFIG
62
- })
63
- })
62
+ });
63
+ });
64
64
65
65
const { isPrinting , isImaging , generatePdf: makePdf , generateImage } = usePrinter ({
66
66
elementId: ` carousel-table_${ uid .value } ` ,
@@ -69,7 +69,7 @@ const { isPrinting, isImaging, generatePdf: makePdf, generateImage } = usePrinte
69
69
70
70
const mutableConfig = ref ({
71
71
showAnimation: FINAL_CONFIG .value .animation .use
72
- })
72
+ });
73
73
74
74
const tableContainer = ref (null );
75
75
const chartContainer = ref (null );
@@ -80,20 +80,35 @@ const captionHeight = ref(0);
80
80
const tableRowHeight = ref (0 );
81
81
const isResponsive = ref (false );
82
82
83
+ const tbody = ref (null );
84
+ const allTr = ref (null );
85
+ const scrollIndex = ref (0 );
86
+
87
+ function setTrElements () {
88
+ if (tbody .value ) {
89
+ allTr .value = {
90
+ elements: tbody .value .getElementsByTagName (' tr' ),
91
+ heights: Array .from (tbody .value .getElementsByTagName (' tr' )).map (el => el .getBoundingClientRect ().height )
92
+ }
93
+ }
94
+ }
95
+
96
+ onMounted (setTrElements);
97
+
98
+ const maxTrHeight = computed (() => {
99
+ if (! allTr .value || ! allTr .value .heights .length ) return 0 ;
100
+ return Math .max (... allTr .value .heights ) + captionHeight .value + tableRowHeight .value ;
101
+ })
102
+
83
103
const visibleCells = computed (() => {
84
104
if (! props .dataset .body ) return 0 ;
85
105
return FINAL_CONFIG .value .tbody .tr .visible <= props .dataset .body .length ? FINAL_CONFIG .value .tbody .tr .visible : props .dataset .body .length ;
86
- })
106
+ });
87
107
88
108
const rowHeight = computed (() => {
89
109
return ((FINAL_CONFIG .value .tbody .tr .height + FINAL_CONFIG .value .tbody .tr .td .padding .top + FINAL_CONFIG .value .tbody .tr .td .padding .bottom + FINAL_CONFIG .value .tbody .tr .border .size * 2 ) * visibleCells .value + captionHeight .value + tableRowHeight .value )
90
110
});
91
111
92
- const maxHeight = computed (() => {
93
- if (! props .dataset || ! props .dataset .body || ! props .dataset .head ) return 0 ;
94
- return ((props .dataset .body .length * (isResponsive .value ? props .dataset .head .length : 1 )) - visibleCells .value ) * (FINAL_CONFIG .value .tbody .tr .height + FINAL_CONFIG .value .tbody .tr .td .padding .top + FINAL_CONFIG .value .tbody .tr .td .padding .bottom + (FINAL_CONFIG .value .tbody .tr .border .size * 2 ));
95
- })
96
-
97
112
const init = ref (0 );
98
113
const raf = ref (null );
99
114
const lastTimestamp = ref (0 );
@@ -109,7 +124,7 @@ onMounted(() => {
109
124
tableRowHeight .value = tableRow .value .getBoundingClientRect ().height ;
110
125
}
111
126
112
- if (mutableConfig .value .showAnimation ) {
127
+ if (mutableConfig .value .showAnimation && !! allTr . value ) {
113
128
startAnimation ();
114
129
}
115
130
});
@@ -125,23 +140,31 @@ function startAnimation() {
125
140
}
126
141
}
127
142
143
+ function hasReachedScrollBottom () {
144
+ if (! tableContainer .value ) return false ;
145
+ const { scrollTop , scrollHeight , clientHeight } = tableContainer .value ;
146
+ return scrollTop + clientHeight >= scrollHeight;
147
+ }
148
+
128
149
function animate (timestamp ) {
129
150
if (isPaused .value ) return ;
130
151
if (! lastTimestamp .value ) lastTimestamp .value = timestamp;
131
152
132
153
const deltaTime = timestamp - lastTimestamp .value ;
133
154
134
155
if (deltaTime >= FINAL_CONFIG .value .animation .speedMs ) {
135
- init .value += ( FINAL_CONFIG .value .tbody . tr . height + FINAL_CONFIG . value . tbody . tr . td . padding . top + FINAL_CONFIG . value . tbody . tr . td . padding . bottom + ( FINAL_CONFIG . value . tbody . tr . border . size )) ;
136
- if (init .value > maxHeight .value ) {
156
+ init .value += allTr .value .heights [ scrollIndex . value ] ;
157
+ if (hasReachedScrollBottom () || scrollIndex .value >= allTr .value . heights . length ) {
137
158
init .value = 0 ;
138
- }
159
+ scrollIndex .value = 0 ;
160
+ }
139
161
140
162
if (tableContainer .value ) {
141
163
tableContainer .value .scrollTo ({
142
164
top: init .value ,
143
165
behavior: ' smooth'
144
166
});
167
+ scrollIndex .value += 1 ;
145
168
}
146
169
147
170
lastTimestamp .value = timestamp;
@@ -156,6 +179,8 @@ function pauseAnimation() {
156
179
raf .value = null ;
157
180
}
158
181
182
+ onBeforeUnmount (pauseAnimation)
183
+
159
184
function resumeAnimation () {
160
185
if (! isPaused .value || ! mutableConfig .value .showAnimation ) return ;
161
186
isPaused .value = false ;
@@ -202,6 +227,8 @@ onMounted(() => {
202
227
})
203
228
captionHeight .value = caption .value ? caption .value .getBoundingClientRect ().height : 0 ;
204
229
tableRowHeight .value = tableRow .value ? tableRow .value .getBoundingClientRect ().height : 0 ;
230
+ scrollIndex .value = 0 ;
231
+ setTrElements ();
205
232
})
206
233
if (tableContainer .value ) {
207
234
observer .observe (tableContainer .value );
@@ -260,7 +287,7 @@ defineExpose({
260
287
ref =" tableContainer"
261
288
:id =" `carousel-table_${uid}`"
262
289
:style =" {
263
- height: isPrinting || isImaging ? 'auto' : `${rowHeight}px`,
290
+ height: isPrinting || isImaging ? 'auto' : `${Math.max( rowHeight, maxTrHeight) }px`,
264
291
containerType: 'inline-size',
265
292
position: 'relative',
266
293
overflow: 'auto',
@@ -332,18 +359,19 @@ defineExpose({
332
359
</tr >
333
360
</thead >
334
361
335
- <tbody v-if =" dataset.body && dataset.head" >
362
+ <tbody v-if =" dataset.body && dataset.head" ref = " tbody " >
336
363
<tr
337
364
v-for =" (tr, i) in dataset.body"
338
365
:style =" {
339
366
...FINAL_CONFIG.tbody.tr.style,
340
- border: `${FINAL_CONFIG.tbody.tr.border.size}px solid ${FINAL_CONFIG.tbody.tr.border.color}`
367
+ border: `${FINAL_CONFIG.tbody.tr.border.size}px solid ${FINAL_CONFIG.tbody.tr.border.color}`,
368
+ verticalAlign: 'middle'
341
369
}"
342
370
>
343
371
<td
344
372
role =" cell"
345
373
v-for =" (td, j) in tr"
346
- :data-cell =" (isResponsive && dataset.head[j].length > 50) ? dataset.head[j].slice(0, 50) + '...' : dataset.head[j] || ''"
374
+ :data-cell =" dataset.head[j] || ''"
347
375
:style =" {
348
376
...FINAL_CONFIG.tbody.tr.td.style,
349
377
border: `${FINAL_CONFIG.tbody.tr.td.border.size}px solid ${FINAL_CONFIG.tbody.tr.td.border.color}`,
@@ -352,10 +380,11 @@ defineExpose({
352
380
paddingRight: FINAL_CONFIG.tbody.tr.td.padding.right + 'px',
353
381
paddingBottom: FINAL_CONFIG.tbody.tr.td.padding.bottom + 'px',
354
382
paddingLeft: FINAL_CONFIG.tbody.tr.td.padding.left + 'px',
383
+ verticalAlign: 'middle'
355
384
}"
356
385
:height =" `${FINAL_CONFIG.tbody.tr.height}px`"
357
386
>
358
- {{ $slots.td ? '' : isResponsive ? typeof td === 'string' ? td.slice(0, 30) + '...' : td : td }}
387
+ {{ $slots.td ? '' : td }}
359
388
<slot name =" td" v-bind =" { td, rowIndex: i, colIndex: j}" />
360
389
</td >
361
390
</tr >
@@ -436,29 +465,22 @@ thead th, tbody td {
436
465
display : grid ;
437
466
gap : 0.5rem ;
438
467
grid-template-columns : repeat (2 , 1fr );
468
+ align-items : center ;
439
469
padding : 0.5rem 1rem ;
440
470
outline : none !important ;
441
471
text-align : left ;
442
- vertical-align : middle ;
472
+ height : max-content ;
443
473
}
444
474
tr {
445
- outline : v-bind (tdo );
446
- }
447
-
448
- td :first-child {
449
- padding-top : 1rem ;
450
- }
451
-
452
- td :last-child {
453
- padding-bottom : 1rem ;
475
+ height : fit-content ;
454
476
}
455
477
456
478
td ::before {
457
479
content : attr (data-cell ) " : " ;
458
480
font-weight : 700 ;
459
481
text-transform : capitalize ;
460
482
font-size : 10px ;
461
- height : 100 % ;
483
+ height : auto ;
462
484
}
463
485
}
464
486
0 commit comments