138 lines
4.6 KiB
JavaScript
138 lines
4.6 KiB
JavaScript
import { useEffect, useRef, useState } from '@wordpress/element';
|
|
import * as d3 from 'd3';
|
|
import PropTypes from 'prop-types';
|
|
|
|
const LCPLegend = ({
|
|
items = [],
|
|
itemSize = 20,
|
|
spacing = 5,
|
|
startX = 20,
|
|
startY = 20,
|
|
fontFamily = 'Arial',
|
|
fontSize = 14
|
|
}) => {
|
|
const svgRef = useRef(null);
|
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
|
|
// Update the container's width when it changes
|
|
useEffect(() => {
|
|
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;
|
|
|
|
// Clear any existing content
|
|
d3.select(svgRef.current).selectAll("*").remove();
|
|
|
|
// Calculate item widths first
|
|
const itemWidths = items.map(item => {
|
|
const labelWidth = item.Label.length * fontSize * 0.6;
|
|
return itemSize + spacing + labelWidth;
|
|
});
|
|
|
|
// 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('overflow', 'visible');
|
|
|
|
// Create the legend items group
|
|
const legend = svg.append('g')
|
|
.attr('class', 'legend-group')
|
|
.attr('transform', `translate(${startX}, ${startY})`);
|
|
|
|
// Place items in their calculated positions
|
|
rows.forEach((rowItems, rowIndex) => {
|
|
let currentX = 0;
|
|
const currentY = rowIndex * (itemSize + spacing);
|
|
|
|
rowItems.forEach((itemIndex) => {
|
|
const item = items[itemIndex];
|
|
const labelWidth = item.Label.length * fontSize * 0.6;
|
|
|
|
// 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');
|
|
|
|
currentX += itemSize + spacing + labelWidth + spacing;
|
|
});
|
|
});
|
|
|
|
// 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]);
|
|
|
|
return (
|
|
<svg
|
|
ref={svgRef}
|
|
className="lcp-legend"
|
|
style={{
|
|
width: '100%' // Ensure width is 100% of the parent container
|
|
}}
|
|
/>
|
|
);
|
|
};
|
|
|
|
LCPLegend.propTypes = {
|
|
items: PropTypes.arrayOf(PropTypes.shape({
|
|
Label: PropTypes.string.isRequired,
|
|
color: PropTypes.string.isRequired
|
|
})),
|
|
itemSize: PropTypes.number,
|
|
spacing: PropTypes.number,
|
|
startX: PropTypes.number,
|
|
startY: PropTypes.number,
|
|
fontFamily: PropTypes.string,
|
|
fontSize: PropTypes.number
|
|
};
|
|
|
|
export default LCPLegend;
|