11<script setup>
2- import { ref , computed , nextTick } from " vue" ;
2+ import { ref , computed , nextTick , onMounted } from " vue" ;
33import { opacity , createUid , createCsvContent , downloadCsv } from " ../lib" ;
44import mainConfig from " ../default_configs.json" ;
55import pdf from " ../pdf" ;
@@ -36,7 +36,9 @@ const isTooltip = ref(false);
3636const tooltipContent = ref (" " );
3737const hoveredCell = ref (undefined );
3838const step = ref (0 );
39-
39+ const tableContainer = ref (null );
40+ const isResponsive = ref (false );
41+
4042const heatmapConfig = computed (() => {
4143 return useNestedProp ({
4244 userConfig: props .config ,
@@ -49,6 +51,21 @@ const mutableConfig = ref({
4951 showTable: heatmapConfig .value .table .show
5052})
5153
54+ const breakpoint = computed (() => {
55+ return heatmapConfig .value .table .responsiveBreakpoint
56+ })
57+
58+ function observeTable () {
59+ const observer = new ResizeObserver ((entries ) => {
60+ entries .forEach (entry => {
61+ isResponsive .value = entry .contentRect .width < breakpoint .value ;
62+ })
63+ })
64+ observer .observe (tableContainer .value )
65+ }
66+
67+ onMounted (observeTable)
68+
5269const maxX = computed (() => {
5370 return Math .max (... props .dataset .flatMap (el => el .values .length ));
5471});
@@ -416,20 +433,13 @@ defineExpose({
416433 <div v-if =" heatmapConfig.style.legend.show && (!mutableConfig.inside || isPrinting)" class =" vue-ui-heatmap-legend" :style =" `background:${heatmapConfig.style.legend.backgroundColor};color:${heatmapConfig.style.legend.color};font-size:${heatmapConfig.style.legend.fontSize}px;padding-bottom:12px;font-weight:${heatmapConfig.style.legend.bold ? 'bold' : ''};display:flex; flex-direction:row;gap:3px;align-items:center;justify-content:center;font-weight:${heatmapConfig.style.legend.bold ? 'bold':'normal'}`" >
417434 <span data-cy =" heatmap-legend-min" style =" text-align :right " >{{ Number(minValue.toFixed(heatmapConfig.style.legend.roundingValue)).toLocaleString() }}</span >
418435 <svg viewBox =" 0 0 132 12" style =" width : 300px " >
419- <rect v-for =" (_,i) in 13"
420- :x =" i * 12"
421- :y =" 0"
422- :height =" 12"
423- :width =" 12"
424- :fill =" heatmapConfig.style.layout.cells.colors.underlayer"
425- />
426- <rect v-for =" (_,i) in 13"
427- :x =" i * 12"
428- :y =" 0"
429- :height =" 12"
430- :width =" 12"
431- :fill =" i < 5 ? `${heatmapConfig.style.layout.cells.colors.cold}${opacity[Math.round((1-(i / 5)) * 100)]}` : `${heatmapConfig.style.layout.cells.colors.hot}${opacity[Math.round((((i-5) / 5)) * 100)]}`"
432- />
436+ <defs >
437+ <linearGradient id =" colorScale" x1 =" 0%" y1 =" 0%" x2 =" 100%" y2 =" 0%" >
438+ <stop offset =" 0%" :stop-color =" heatmapConfig.style.layout.cells.colors.cold" />
439+ <stop offset =" 100%" :stop-color =" heatmapConfig.style.layout.cells.colors.hot" />
440+ </linearGradient >
441+ </defs >
442+ <rect x =" 0" y =" 0" height =" 12" width =" 132" fill =" url(#colorScale)" />
433443 </svg >
434444 <span data-cy =" heatmap-legend-max" style =" text-align :left " >{{ Number(maxValue.toFixed(heatmapConfig.style.legend.roundingValue)).toLocaleString() }}</span >
435445 </div >
@@ -444,40 +454,41 @@ defineExpose({
444454 />
445455
446456 <!-- DATA TABLE -->
447- <div :style =" `${isPrinting ? '' : 'max-height:400px'};overflow:auto;width:100%;margin-top:${mutableConfig.inside ? '48px' : ''}`" v-if =" mutableConfig.showTable" >
448- <table >
449- <thead >
450- <tr data-cy =" heatmap-table-title" v-if =" heatmapConfig.style.title.text" >
451- <th :colspan =" dataset.length +1" :style =" `background:${heatmapConfig.table.th.backgroundColor};color:${heatmapConfig.table.th.color};outline:${heatmapConfig.table.th.outline}`" >
452- <span >{{ heatmapConfig.style.title.text }}</span >
453- <span v-if =" heatmapConfig.style.title.subtitle.text" >
454- : {{ heatmapConfig.style.title.subtitle.text }}
455- </span >
456- </th >
457- </tr >
458- <tr >
459- <th :style =" `background:${heatmapConfig.table.th.backgroundColor};color:${heatmapConfig.table.th.color};outline:${heatmapConfig.table.th.outline};padding-right:6px`" ></th >
460- <th align =" right" :style =" `background:${heatmapConfig.table.th.backgroundColor};color:${heatmapConfig.table.th.color};outline:${heatmapConfig.table.th.outline};padding-right:6px`" v-for =" (th,i) in dataset" :data-cy =" `heatmap-table-col-name-${i}`" >
461- {{ th.name }}
462- </th >
463- </tr >
464- </thead >
465- <tbody >
466- <tr v-for =" (tr, i) in dataLabels.xLabels" :data-cy =" `heatmap-table-row-${i}`" >
467- <td :data-cy =" `heatmap-table-row-name-${i}`" :style =" `background:${heatmapConfig.table.td.backgroundColor};color:${heatmapConfig.table.td.color};outline:${heatmapConfig.table.td.outline}`" >
468- {{ tr }}
469- </td >
470- <td v-for =" (trData, j) in dataset" :style =" `background:${heatmapConfig.table.td.backgroundColor};color:${heatmapConfig.table.td.color};outline:${heatmapConfig.table.td.outline}`" >
471- {{ isNaN(trData.values[i]) ? '-' : Number(trData.values[i].toFixed(heatmapConfig.table.td.roundingValue)).toLocaleString() }}
472- </td >
473- </tr >
474- </tbody >
475- </table >
457+ <div ref =" tableContainer" class =" vue-ui-heatmap-table" >
458+ <div :style =" `${isPrinting ? '' : 'max-height:400px'};overflow:auto;width:100%;margin-top:${mutableConfig.inside ? '48px' : ''}`" v-if =" mutableConfig.showTable" :class =" {'vue-ui-responsive' : isResponsive}" >
459+ <table class =" vue-ui-data-table" >
460+ <caption :style =" `backgroundColor:${heatmapConfig.table.th.backgroundColor};color:${heatmapConfig.table.th.color};outline:${heatmapConfig.table.th.outline}`" >
461+ {{ heatmapConfig.style.title.text }} <span v-if =" heatmapConfig.style.title.subtitle.text" >{{ heatmapConfig.style.title.subtitle.text }}</span >
462+ </caption >
463+ <thead >
464+ <tr role =" row" :style =" `background:${heatmapConfig.table.th.backgroundColor};color:${heatmapConfig.table.th.color}`" >
465+ <th :style =" `outline:${heatmapConfig.table.th.outline};padding-right:6px`" ></th >
466+ <th align =" right" :style =" `outline:${heatmapConfig.table.th.outline};padding-right:6px`" v-for =" (th,i) in dataset" :data-cy =" `heatmap-table-col-name-${i}`" >
467+ {{ th.name }}
468+ </th >
469+ </tr >
470+ </thead >
471+ <tbody >
472+ <tr role =" row" v-for =" (tr, i) in dataLabels.xLabels" :class =" {'vue-ui-data-table__tbody__row' : true, 'vue-ui-data-table__tbody__row-even': i % 2 === 0, 'vue-ui-data-table__tbody__row-odd': i % 2 !== 0}" :style =" `background:${heatmapConfig.table.td.backgroundColor};color:${heatmapConfig.table.td.color}`" >
473+ <td :data-cell =" heatmapConfig.table.colNames.xAxis" class =" vue-ui-data-table__tbody__td" :data-cy =" `heatmap-table-row-name-${i}`" :style =" `outline:${heatmapConfig.table.td.outline}`" >
474+ <div style =" display : flex ; align-items :center ; gap : 5px ; justify-content :flex-end ; width :100% ; padding-right :3px ;" >
475+ {{ tr }}
476+ </div >
477+ </td >
478+ <td class =" vue-ui-data-table__tbody__td" v-for =" (trData, j) in dataset" :data-cell =" dataset[j].name" :style =" `outline:${heatmapConfig.table.td.outline}`" >
479+ <div style =" display : flex ; align-items :center ; gap : 5px ; justify-content :flex-end ; width :100% ; padding-right :3px ;" >
480+ {{ isNaN(trData.values[i]) ? '-' : Number(trData.values[i].toFixed(heatmapConfig.table.td.roundingValue)).toLocaleString() }}
481+ </div >
482+ </td >
483+ </tr >
484+ </tbody >
485+ </table >
486+ </div >
476487 </div >
477488 </div >
478489</template >
479490
480- <style scoped>
491+ <style scoped lang="scss" >
481492.vue-ui-heatmap * {
482493 transition : unset ;
483494}
@@ -522,22 +533,12 @@ defineExpose({
522533 z-index :1 ;
523534}
524535
525- .vue-ui-heatmap table {
536+ .vue-ui-heatmap- table {
526537 width : 100% ;
527- border-collapse : collapse ;
538+ max-height : 300 px ;
528539 overflow : auto ;
529- max-height : 400px ;
530- }
531- .vue-ui-heatmap table td {
532- text-align :right ;
533- padding-right : 6px ;
534- font-variant-numeric : tabular-nums ;
535- }
536- .vue-ui-heatmap table th {
537- position : sticky ;
538- top :0 ;
539- font-weight : 400 ;
540- user-select : none ;
540+ margin-top : 24px ;
541+ position : relative ;
541542}
542543.vue-data-ui-fullscreen--on {
543544 height : 80% !important ;
@@ -548,4 +549,60 @@ defineExpose({
548549.vue-data-ui-wrapper-fullscreen {
549550 overflow : auto ;
550551}
552+
553+ .vue-ui-data-table thead {
554+ position : sticky ;
555+ top :0 ;
556+ font-weight : 400 ;
557+ user-select : none ;
558+ }
559+
560+ table {
561+ width : 100% ;
562+ padding : 1rem ;
563+ border-collapse :collapse ;
564+ }
565+
566+ caption ,
567+ th ,
568+ td {
569+ padding : 0.5rem ;
570+ font-variant-numeric : tabular-nums ;
571+ }
572+
573+ caption {
574+ font-size : 1.3rem ;
575+ font-weight : 700 ;
576+ }
577+
578+ .vue-ui-responsive {
579+ th {
580+ display : none ;
581+ }
582+ td {
583+ display : grid ;
584+ gap : 0.5rem ;
585+ grid-template-columns : repeat (2 , 1fr );
586+ padding : 0.5rem 1rem ;
587+ outline : none !important ;
588+ text-align : left ;
589+ }
590+ tr {
591+ outline : v-bind (tdo );
592+ }
593+
594+ td :first-child {
595+ padding-top : 1rem ;
596+ }
597+
598+ td :last-child {
599+ padding-bottom : 1rem ;
600+ }
601+
602+ td ::before {
603+ content : attr (data-cell ) " : " ;
604+ font-weight : 700 ;
605+ text-transform : capitalize ;
606+ }
607+ }
551608 </style >
0 commit comments