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(() => { if (svgRef.current) { setContainerWidth(svgRef.current.clientWidth); } }, [svgRef.current]); useEffect(() => { if (!items.length || !svgRef.current || containerWidth === 0) return; // 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 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 // 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 const legend = svg.append('g') .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) // 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 } // 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 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; }); // 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 svg.attr('height', calculatedHeight); }, [items, containerWidth, itemSize, spacing, startX, startY, fontFamily, fontSize]); return ( ); }; 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;