11<script setup>
2- import { ref , computed , onMounted , watch , nextTick } from " vue" ;
2+ import { ref , computed , onMounted , watch , nextTick , onBeforeUnmount } from " vue" ;
33import { useConfig } from " ../useConfig" ;
44import { useNestedProp } from " ../useNestedProp" ;
55import { createCsvContent , createUid , downloadCsv , error , objectIsEmpty , opacity } from " ../lib" ;
@@ -59,8 +59,8 @@ const FINAL_CONFIG = computed(() => {
5959 return useNestedProp ({
6060 userConfig: props .config ,
6161 defaultConfig: DEFAULT_CONFIG
62- })
63- })
62+ });
63+ });
6464
6565const { isPrinting , isImaging , generatePdf: makePdf , generateImage } = usePrinter ({
6666 elementId: ` carousel-table_${ uid .value } ` ,
@@ -69,7 +69,7 @@ const { isPrinting, isImaging, generatePdf: makePdf, generateImage } = usePrinte
6969
7070const mutableConfig = ref ({
7171 showAnimation: FINAL_CONFIG .value .animation .use
72- })
72+ });
7373
7474const tableContainer = ref (null );
7575const chartContainer = ref (null );
@@ -80,20 +80,35 @@ const captionHeight = ref(0);
8080const tableRowHeight = ref (0 );
8181const isResponsive = ref (false );
8282
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+
83103const visibleCells = computed (() => {
84104 if (! props .dataset .body ) return 0 ;
85105 return FINAL_CONFIG .value .tbody .tr .visible <= props .dataset .body .length ? FINAL_CONFIG .value .tbody .tr .visible : props .dataset .body .length ;
86- })
106+ });
87107
88108const rowHeight = computed (() => {
89109 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 )
90110});
91111
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-
97112const init = ref (0 );
98113const raf = ref (null );
99114const lastTimestamp = ref (0 );
@@ -109,7 +124,7 @@ onMounted(() => {
109124 tableRowHeight .value = tableRow .value .getBoundingClientRect ().height ;
110125 }
111126
112- if (mutableConfig .value .showAnimation ) {
127+ if (mutableConfig .value .showAnimation && !! allTr . value ) {
113128 startAnimation ();
114129 }
115130});
@@ -125,23 +140,31 @@ function startAnimation() {
125140 }
126141}
127142
143+ function hasReachedScrollBottom () {
144+ if (! tableContainer .value ) return false ;
145+ const { scrollTop , scrollHeight , clientHeight } = tableContainer .value ;
146+ return scrollTop + clientHeight >= scrollHeight;
147+ }
148+
128149function animate (timestamp ) {
129150 if (isPaused .value ) return ;
130151 if (! lastTimestamp .value ) lastTimestamp .value = timestamp;
131152
132153 const deltaTime = timestamp - lastTimestamp .value ;
133154
134155 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 ) {
137158 init .value = 0 ;
138- }
159+ scrollIndex .value = 0 ;
160+ }
139161
140162 if (tableContainer .value ) {
141163 tableContainer .value .scrollTo ({
142164 top: init .value ,
143165 behavior: ' smooth'
144166 });
167+ scrollIndex .value += 1 ;
145168 }
146169
147170 lastTimestamp .value = timestamp;
@@ -156,6 +179,8 @@ function pauseAnimation() {
156179 raf .value = null ;
157180}
158181
182+ onBeforeUnmount (pauseAnimation)
183+
159184function resumeAnimation () {
160185 if (! isPaused .value || ! mutableConfig .value .showAnimation ) return ;
161186 isPaused .value = false ;
@@ -202,6 +227,8 @@ onMounted(() => {
202227 })
203228 captionHeight .value = caption .value ? caption .value .getBoundingClientRect ().height : 0 ;
204229 tableRowHeight .value = tableRow .value ? tableRow .value .getBoundingClientRect ().height : 0 ;
230+ scrollIndex .value = 0 ;
231+ setTrElements ();
205232 })
206233 if (tableContainer .value ) {
207234 observer .observe (tableContainer .value );
@@ -260,7 +287,7 @@ defineExpose({
260287 ref =" tableContainer"
261288 :id =" `carousel-table_${uid}`"
262289 :style =" {
263- height: isPrinting || isImaging ? 'auto' : `${rowHeight}px`,
290+ height: isPrinting || isImaging ? 'auto' : `${Math.max( rowHeight, maxTrHeight) }px`,
264291 containerType: 'inline-size',
265292 position: 'relative',
266293 overflow: 'auto',
@@ -332,18 +359,19 @@ defineExpose({
332359 </tr >
333360 </thead >
334361
335- <tbody v-if =" dataset.body && dataset.head" >
362+ <tbody v-if =" dataset.body && dataset.head" ref = " tbody " >
336363 <tr
337364 v-for =" (tr, i) in dataset.body"
338365 :style =" {
339366 ...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'
341369 }"
342370 >
343371 <td
344372 role =" cell"
345373 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] || ''"
347375 :style =" {
348376 ...FINAL_CONFIG.tbody.tr.td.style,
349377 border: `${FINAL_CONFIG.tbody.tr.td.border.size}px solid ${FINAL_CONFIG.tbody.tr.td.border.color}`,
@@ -352,10 +380,11 @@ defineExpose({
352380 paddingRight: FINAL_CONFIG.tbody.tr.td.padding.right + 'px',
353381 paddingBottom: FINAL_CONFIG.tbody.tr.td.padding.bottom + 'px',
354382 paddingLeft: FINAL_CONFIG.tbody.tr.td.padding.left + 'px',
383+ verticalAlign: 'middle'
355384 }"
356385 :height =" `${FINAL_CONFIG.tbody.tr.height}px`"
357386 >
358- {{ $slots.td ? '' : isResponsive ? typeof td === 'string' ? td.slice(0, 30) + '...' : td : td }}
387+ {{ $slots.td ? '' : td }}
359388 <slot name =" td" v-bind =" { td, rowIndex: i, colIndex: j}" />
360389 </td >
361390 </tr >
@@ -436,29 +465,22 @@ thead th, tbody td {
436465 display : grid ;
437466 gap : 0.5rem ;
438467 grid-template-columns : repeat (2 , 1fr );
468+ align-items : center ;
439469 padding : 0.5rem 1rem ;
440470 outline : none !important ;
441471 text-align : left ;
442- vertical-align : middle ;
472+ height : max-content ;
443473 }
444474 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 ;
454476 }
455477
456478 td ::before {
457479 content : attr (data-cell ) " : " ;
458480 font-weight : 700 ;
459481 text-transform : capitalize ;
460482 font-size : 10px ;
461- height : 100 % ;
483+ height : auto ;
462484 }
463485}
464486
0 commit comments