JSFiddle - React, Tailwind, and code Playground

by neonDog

HTML

<div class="test">

</div>

SCSS

body, html { padding:0; margin: 0; background: #222; }

.neon-heatmap {
  --color-dark-blue: #1E2537;
  --color-light-gray: #ECECEC;
  --color-light-green: rgb(225,229,162);
  --color-dark-green: rgb(57,132,162);
  //font-family: -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  font-size: 11px;
  
  &__row {
    display: flex;
    flex-wrap: wrap;
    height: 50px;
  }
  
  &__cell {
    display: flex;
    justify-content: center;
    width: 100%;
    flex: 1;
    justify-items: center;
    align-items: center;
    color: rgba(255,255,255,0.8);
    font-weight: 900;
    //background: red;
    overflow: hidden;
    white-space: nowrap;
    border:1px #44444444 solid;
    &:hover {
      opacity: 0.8;
      cursor: pointer;
    }
  }
  
  &__header {
    .neon-heatmap__cell {
      color: rgb(153,153,153);
      background: var(--color-light-gray);
      font-weight: 500;
      border-color: transparent;
    }
  }
  
  &__left-header {
    display: flex;
    width: 100px;
    align-items: center;
    justify-content: center;
    background: var(--color-dark-blue);
    color: white;
    font-weight: 600;
    &:hover {
      opacity: 0.6;
      cursor: pointer;
    }
  }

}

JavaScript

const heatMapColorforValue = function(value, hue=0, invert=false) {
	//Invert?
  let l = invert ? 100 - value : value;
  return `hsl(${hue}, 100%, ${l}%)`;
}

const normalizeArray = function(min, max) {
    const delta = max - min;
    return function (val) {
        return (val - min) / delta;
    };
}

//console.log([0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 25].map(normalizeArray(0, 25)));
const normalize = function(val, max, min) { return (val - min) / (max - min); }

class NeonHeatmap {

	constructor(container, settings={}) {
  	
    const defaults = {
    	x: 24,
    	y: ['S','M','T','W','T','F','S'],
      render: {
      	y: (value) => value,
        x: (value) => value + ':00',
        cell: (value) => value
        //columnRender:(value) => value
      },
      invert: false,
      hue:-90,
      scaleOffset: 50
    };
    
    this.options = Object.assign({}, defaults, settings);
    
    this.container = container;
    
    this.cells = []; //Flat
    this.cellsMulti = []; //Multidimensional
    this.generate();
    
  }
  
  outputFlat(data, max=255, min=0) {
  	
    const renderFn = this.options.render.cell;
    const hue = this.options.hue;
    const invert = this.options.invert;
    const scaleOffset = this.options.scaleOffset;
    
  	for (let i=0, l=data.length; i < l; i++) {
    	const d = data[i];
    	const cell = this.cells[i];
      const isValue = typeof d === 'number';
    	//Todo: normalize the data
      const n = normalize(isValue ? d : d.value, max, min);
      
    	cell.style.background = heatMapColorforValue(n * 255 * scaleOffset, hue, invert);
      cell.innerHTML = renderFn(d, cell);
    }
  }
  
  generate() {
  	const table = document.createElement('div');
    table.className = 'neon-heatmap';
    table.append(this.generateRows());
    this.container.appendChild(table);
  }
  
  generateRows() {
  	
    const axis = this.options.y;
    const isArray = Array.isArray(axis);
    const render = this.options.render.y;
    
    const axis2...