Refactored LCPDataGrid
This commit is contained in:
@ -16,10 +16,18 @@ const LCPLegend = ({
|
||||
|
||||
// Update the container's width when it changes
|
||||
useEffect(() => {
|
||||
if (svgRef.current) {
|
||||
setContainerWidth(svgRef.current.clientWidth);
|
||||
}
|
||||
}, [svgRef.current]);
|
||||
const handleResize = () => {
|
||||
if (svgRef.current) {
|
||||
setContainerWidth(svgRef.current.clientWidth); // Measure the width of the container
|
||||
}
|
||||
};
|
||||
|
||||
handleResize(); // Initial measurement
|
||||
window.addEventListener('resize', handleResize); // Update on resize
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize); // Cleanup on component unmount
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!items.length || !svgRef.current || containerWidth === 0) return;
|
||||
@ -27,19 +35,37 @@ const LCPLegend = ({
|
||||
// Clear any existing content
|
||||
d3.select(svgRef.current).selectAll("*").remove();
|
||||
|
||||
// Variables for item dimensions and row management
|
||||
const rowHeight = itemSize + spacing;
|
||||
let currentX = 0;
|
||||
let currentY = 0;
|
||||
// Calculate item widths first
|
||||
const itemWidths = items.map(item => {
|
||||
const labelWidth = item.Label.length * fontSize * 0.6;
|
||||
return itemSize + spacing + labelWidth;
|
||||
});
|
||||
|
||||
// Calculate the maximum number of items that can fit per row
|
||||
const itemWidth = itemSize + spacing + fontSize * 0.6; // Item width (rect + label)
|
||||
const itemsPerRow = Math.floor(containerWidth / itemWidth); // Items per row based on container width
|
||||
// Calculate total width of all items
|
||||
const totalWidth = itemWidths.reduce((sum, width) => sum + width + spacing, 0);
|
||||
|
||||
// Calculate optimal number of rows
|
||||
const availableWidth = containerWidth - (startX * 2);
|
||||
const minRows = Math.ceil(totalWidth / availableWidth);
|
||||
|
||||
// Create arrays to store items for each row
|
||||
const rows = Array(minRows).fill().map(() => []);
|
||||
let rowWidths = Array(minRows).fill(0);
|
||||
|
||||
// Distribute items across rows to minimize empty space
|
||||
itemWidths.forEach((width, index) => {
|
||||
// Find the row with the most remaining space
|
||||
const rowIndex = rowWidths
|
||||
.map((rowWidth, i) => ({ width: rowWidth, index: i }))
|
||||
.sort((a, b) => a.width - b.width)[0].index;
|
||||
|
||||
rows[rowIndex].push(index);
|
||||
rowWidths[rowIndex] += width + spacing;
|
||||
});
|
||||
|
||||
// Create the SVG container
|
||||
const svg = d3.select(svgRef.current)
|
||||
.attr('width', '100%')
|
||||
.style('height', null) // Let height be dynamic
|
||||
.style('overflow', 'visible');
|
||||
|
||||
// Create the legend items group
|
||||
@ -47,45 +73,39 @@ const LCPLegend = ({
|
||||
.attr('class', 'legend-group')
|
||||
.attr('transform', `translate(${startX}, ${startY})`);
|
||||
|
||||
// Loop over items to position them
|
||||
items.forEach((item, i) => {
|
||||
// Calculate the width of each item (rect + label)
|
||||
const labelWidth = item.Label.length * fontSize * 0.6; // Approximate label width
|
||||
const itemWidth = itemSize + spacing + labelWidth; // Total width of the item (rect + label)
|
||||
// Place items in their calculated positions
|
||||
rows.forEach((rowItems, rowIndex) => {
|
||||
let currentX = 0;
|
||||
const currentY = rowIndex * (itemSize + spacing);
|
||||
|
||||
// If the item doesn't fit in the current row, move to the next row
|
||||
if (i % itemsPerRow === 0 && i !== 0) {
|
||||
currentX = 0; // Reset X position
|
||||
currentY += rowHeight; // Move down to next row
|
||||
}
|
||||
rowItems.forEach((itemIndex) => {
|
||||
const item = items[itemIndex];
|
||||
const labelWidth = item.Label.length * fontSize * 0.6;
|
||||
|
||||
// Create the rectangle
|
||||
legend.append('rect')
|
||||
.attr('x', currentX)
|
||||
.attr('y', currentY)
|
||||
.attr('width', itemSize)
|
||||
.attr('height', itemSize)
|
||||
.style('fill', item.color || '#cccccc');
|
||||
// Create the rectangle (swatch)
|
||||
legend.append('rect')
|
||||
.attr('x', currentX)
|
||||
.attr('y', currentY)
|
||||
.attr('width', itemSize)
|
||||
.attr('height', itemSize)
|
||||
.style('fill', item.color || '#cccccc');
|
||||
|
||||
// Create the label
|
||||
legend.append('text')
|
||||
.attr('x', currentX + itemSize + spacing)
|
||||
.attr('y', currentY + itemSize / 2)
|
||||
.text(item.Label || '')
|
||||
.style('font-family', fontFamily)
|
||||
.style('font-size', `${fontSize}px`)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('fill', '#333333');
|
||||
// Create the label
|
||||
legend.append('text')
|
||||
.attr('x', currentX + itemSize + spacing)
|
||||
.attr('y', currentY + itemSize / 2)
|
||||
.text(item.Label || '')
|
||||
.style('font-family', fontFamily)
|
||||
.style('font-size', `${fontSize}px`)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('fill', '#333333');
|
||||
|
||||
// Update the current X position for the next item
|
||||
currentX += itemWidth;
|
||||
currentX += itemSize + spacing + labelWidth + spacing;
|
||||
});
|
||||
});
|
||||
|
||||
// Calculate the height of the SVG element based on the number of rows
|
||||
const totalRows = Math.ceil(items.length / itemsPerRow);
|
||||
const calculatedHeight = totalRows * rowHeight + startY * 2;
|
||||
|
||||
// Set the correct height based on totalRows
|
||||
// Set the SVG height based on the number of rows
|
||||
const calculatedHeight = (minRows * (itemSize + spacing)) + (startY * 2);
|
||||
svg.attr('height', calculatedHeight);
|
||||
|
||||
}, [items, containerWidth, itemSize, spacing, startX, startY, fontFamily, fontSize]);
|
||||
|
||||
Reference in New Issue
Block a user