MFColumnGradient / beta / js / mf-column-gradient.js file last updated : 2023-09-05 |
---|
/*! MFColumnGradient.js
*
* Version: beta
* Licence: MIT
*/
class MFColumnGradient {
constructor(tableId, lowColor, highColor) {
if ( lowColor==undefined ) console.error('No MFColumnGradient low color given');
if ( highColor==undefined ) console.error('No MFColumnGradient high color given');
this.table = document.getElementById(tableId);
this.lowColor = lowColor;
this.highColor = highColor;
this.doAutoForeground = false;
this.lightBgColFg = 'white';
this.darkBgColFg = 'black';
this.dpChr = '.';
this.trace = true;
}
_getValue(inputStr, dpChr) {
// Replace the specified character with a period
const pattern = new RegExp(`[^0-9${this.dpChr}-]`, 'g');
// Replace all characters that are not 0-9, dpChr, or '-' with an empty string
const cleanedStr = inputStr.replace(pattern, '').replace(this.dpChr, '.');
// Parse the cleaned string as a float (assuming it represents a decimal number)
const numericValue = parseFloat(cleanedStr);
// Check if the numericValue is a valid number, if not, return NaN
if (isNaN(numericValue)) {
return NaN;
}
// Return the numeric value
return numericValue;
}
// Function to change cell color based on value and thresholds
_changeCellColor(cell, lowThreshold, highThreshold, colorScale) {
let value;
if ( cell ) {
if ( cell.getAttribute('data-cgvalue') ) value=parseFloat(cell.getAttribute('data-cgvalue'));
else value = parseFloat(this._getValue(cell.textContent, this.dpChr));
}
if ( cell && this.trace ) console.log(cell.textContent + ' = '+value);
if (!isNaN(value)) {
let adjustedValue = value;
if (adjustedValue < lowThreshold) {
adjustedValue = lowThreshold;
} else if (adjustedValue > highThreshold) {
adjustedValue = highThreshold;
}
const ratio = (adjustedValue - lowThreshold) / (highThreshold - lowThreshold);
const cellColor = this._interpolateColor(this.lowColor, this.highColor, ratio, colorScale);
if ( this.trace ) console.log('adjusted color for '+value+' / '+adjustedValue+' = '+ratio);
cell.style.backgroundColor = cellColor;
cell.title=cellColor;
if ( this.doAutoForeground ) cell.style.color = this._getTextColorBasedOnBackground(cellColor);
}
}
// Function to interpolate between two colors
_interpolateColor(color1, color2, ratio, colorScale) {
const index = Math.round(ratio * (colorScale.length - 1));
return colorScale[index];
}
// Public method to set column gradient
setColumnGradient(colN, minThreshold, maxThreshold) {
let minValue = Infinity;
let maxValue = -Infinity;
// Iterate through rows to find the minimum and maximum values
for (let r = 0; r < this.table.rows.length; r++) {
const row = this.table.rows[r];
const cell = row.cells[colN];
if (cell) {
const value = parseFloat(this._getValue(cell.textContent, this.dpChr));
if (!isNaN(value)) {
minValue = Math.min(minValue, value);
maxValue = Math.max(maxValue, value);
}
}
}
let lowThreshold, highThreshold;
if ( minThreshold && minValue<minThreshold ) lowThreshold=minThreshold;
else lowThreshold=minValue;
if ( maxThreshold && maxValue>maxThreshold ) highThreshold=maxThreshold;
else highThreshold=maxValue;
const colorScale = this._generateColorScale(this.lowColor, this.highColor, 100);
if ( this.trace ) console.log('Gradients from '+lowThreshold+' to '+highThreshold+' ('+this.lowColor+' to '+this.highColor+')');
for (let r = 0; r < this.table.rows.length; r++) {
const row = this.table.rows[r];
this._changeCellColor(row.cells[colN], lowThreshold, highThreshold, colorScale);
}
}
setDecimalPlaceChar(dpChr) {
this.dpChr=dpChr;
}
setAutoForeground(lightBgCol, darkBgCol) {
this.doAutoForeground = true;
this.lightBgColFg=lightBgCol;
this.darkBgColFg=darkBgCol;
}
_getTextColorBasedOnBackground(backgroundColor) {
// Calculate the relative luminance of the background color
const r = parseInt(backgroundColor.slice(1, 3), 16) / 255;
const g = parseInt(backgroundColor.slice(3, 5), 16) / 255;
const b = parseInt(backgroundColor.slice(5, 7), 16) / 255;
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
// Define a brightness threshold (you can adjust this value as needed)
const brightnessThreshold = 0.5;
// Determine the text color based on the luminance
if (luminance > brightnessThreshold) {
return this.lightBgColFg; // Use black text on light backgrounds
} else {
return this.darkBgColFg; // Use white text on dark backgrounds
}
}
_generateColorScale(color1, color2, numSteps) {
// Parse the input colors
const r1 = parseInt(color1.slice(1, 3), 16);
const g1 = parseInt(color1.slice(3, 5), 16);
const b1 = parseInt(color1.slice(5, 7), 16);
const r2 = parseInt(color2.slice(1, 3), 16);
const g2 = parseInt(color2.slice(3, 5), 16);
const b2 = parseInt(color2.slice(5, 7), 16);
// Calculate step values for each color component
const stepR = (r2 - r1) / numSteps;
const stepG = (g2 - g1) / numSteps;
const stepB = (b2 - b1) / numSteps;
// Generate the color scale
const colorScale = [];
for (let i = 0; i <= numSteps; i++) {
const r = Math.round(r1 + stepR * i);
const g = Math.round(g1 + stepG * i);
const b = Math.round(b1 + stepB * i);
// Convert to hexadecimal format and push to the scale
const color = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
colorScale.push(color);
}
return colorScale;
}
}
Also see
articles/Implementing-a-Monaco-Editorsquarearticles/iConnectionTestsquarearticles/javascript-camerasquarearticles/list-editsquarearticles/minifierssquarearticles/opayosquarearticles/table-drag-sortersquarearticles/typewatchsquareprojects/MFCalendarPopupsquareprojects/MFChartColumnsquareprojects/MFColorPickersquareprojects/MFColorPickerBasicsquareprojects/MFColumnGradientsquareprojects/MFFloatawayMsgsquareprojects/MFPanelssquareprojects/MFSelectorsquare
Comments
New Comment