function createColoredTable(width, height, headerHeight, attributes, data, colorSchemes) {
const columnData = attributes.map(key => data.map(row => row[key]));
const rowData = data.map(row => Object.keys(row).filter(key => attributes.includes(key)).map(key => row[key]))
const svg = d3.select(DOM.svg(width, height));
const columnScale = d3.scaleBand()
.domain(d3.range(columnData.length))
.range([0, width])
.padding(0.1);
const rowScale = d3.scaleBand()
.domain(d3.range(rowData.length))
.range([headerHeight, height])
.padding(0);
const colorScales = attributes.map((_, i) => {
return d3.scaleSequential()
.domain(d3.extent(columnData[i]))
.interpolator(colorSchemes[i % colorSchemes.length]);
});
const rows = svg.selectAll('.row')
.data(rowData)
.join('g')
.attr('class', 'row');
rows.each(function(rowData, rowIndex) {
d3.select(this) // Select the row group.
.selectAll('rect')
.data(rowData)
.join('rect')
.attr('x', (_, i) => columnScale(i))
.attr('y', rowScale(rowIndex))
.attr('height', rowScale.bandwidth())
.attr('width', columnScale.bandwidth())
.style('fill', (d, i) => isNaN(d) ? '#f0f' : colorScales[i](d));
});
// Create a group for each column.
const columns = svg.selectAll('.column')
.data(attributes)
.join('g')
.attr('class', 'column');
// Append a rectangle to each column to make columns clickable.
// The rectangles also serve as an outline for the columns.
columns.append('rect')
.attr('x', (_, i) => columnScale(i))
.attr('y', rowScale(0))
.attr('height', rowScale(rowData.length - 1) - rowScale(0))
.attr('width', columnScale.bandwidth())
.style('fill', '#0000') // Set fill color to transparent.
.style('stroke', 'black')
.on('click', (_, d) => {
// If a column is clicked, sort the row data based on the values in the column.
const index = attributes.indexOf(d);
rowData.sort((a, b) => isNaN(a[index]) ? -1 : isNaN(b[index]) ? 1 : a[index] - b[index]);
// Update the rows with the sorted data.
rows.data(rowData).each(function(cellData) {
// Update the color of the rectangles in each row.
d3.select(this)
.selectAll('rect')
.data(cellData)
.join('rect')
.style('fill', (d, i) => isNaN(d) ? '#f0f' : colorScales[i](d));
});
})
.on('pointerenter', (e) => {
// Highlight the column outline on pointer enter.
d3.select(e.target) // Select the column rectangle.
.style('stroke-width', 2);
})
.on('pointerleave', (e) => {
// De-highlight the column outline on pointer enter.
d3.select(e.target) // Select the column rectangle.
.style('stroke-width', 1);
});
// Create the column header text.
columns.append('text')
.attr('x', (_, i) => columnScale(i) + columnScale.bandwidth() * 0.5)
.attr('y', headerHeight * 0.5)
.attr('font-size', headerHeight * 0.5)
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.style('cursor', 'default')
.style('user-select', 'none')
.text(d => d);
// Return the HTML element of the SVG image.
return svg.node();
}