Added basic support for stacked bars
This commit is contained in:
@ -124,6 +124,10 @@
|
||||
"gridOpacity": {
|
||||
"type": "number",
|
||||
"default": 0.5
|
||||
},
|
||||
"enableStackedBars": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1 +1 @@
|
||||
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '1180f00c82e4e13ed149');
|
||||
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '79dffd7b201fcf20bc7c');
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -124,6 +124,10 @@
|
||||
"gridOpacity": {
|
||||
"type": "number",
|
||||
"default": 0.5
|
||||
},
|
||||
"enableStackedBars": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,12 +21,40 @@ const BarGraph = ({
|
||||
customColors,
|
||||
barOpacity,
|
||||
enableHierarchicalView,
|
||||
enableStackedBars,
|
||||
selectedParentId,
|
||||
setAttributes
|
||||
}) => {
|
||||
const svgRef = useRef();
|
||||
const containerRef = useRef();
|
||||
|
||||
// Helper function to darken a color
|
||||
const darkenColor = (color, amount) => {
|
||||
const rgb = d3.color(color).rgb();
|
||||
return d3.rgb(
|
||||
Math.max(0, rgb.r - amount),
|
||||
Math.max(0, rgb.g - amount),
|
||||
Math.max(0, rgb.b - amount)
|
||||
).toString();
|
||||
};
|
||||
|
||||
// Helper function to get children of a node
|
||||
const getChildren = (nodeId) => {
|
||||
const children = [];
|
||||
Object.entries(data).forEach(([dataset, items]) => {
|
||||
items.forEach(item => {
|
||||
if (item.parent === nodeId) {
|
||||
children.push({
|
||||
...item,
|
||||
dataset,
|
||||
color: item.color || defaultBarColor
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
return children;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!data || !svgRef.current || !containerRef.current) return;
|
||||
|
||||
@ -35,7 +63,7 @@ const BarGraph = ({
|
||||
|
||||
// Filter data based on hierarchical view
|
||||
let displayData = data;
|
||||
if (enableHierarchicalView) {
|
||||
if (enableHierarchicalView && !enableStackedBars) {
|
||||
displayData = {};
|
||||
Object.entries(data).forEach(([dataset, items]) => {
|
||||
const filteredItems = items.filter(item => item.parent === selectedParentId);
|
||||
@ -49,16 +77,44 @@ const BarGraph = ({
|
||||
const chartDataArray = [];
|
||||
Object.entries(displayData).forEach(([dataset, items]) => {
|
||||
items.forEach(item => {
|
||||
chartDataArray.push({
|
||||
id: item.id,
|
||||
dataset,
|
||||
label: item.label || '',
|
||||
value: parseFloat(item.value) || 0,
|
||||
color: item.color || defaultBarColor,
|
||||
hasChildren: Object.values(data).some(dataItems =>
|
||||
dataItems.some(dataItem => dataItem.parent === item.id)
|
||||
)
|
||||
});
|
||||
if (!enableStackedBars || item.parent === null) {
|
||||
const baseColor = item.color || defaultBarColor;
|
||||
const children = getChildren(item.id);
|
||||
|
||||
// Create the main bar
|
||||
const barData = {
|
||||
id: item.id,
|
||||
dataset,
|
||||
label: item.label || '',
|
||||
value: parseFloat(item.value) || 0,
|
||||
color: baseColor,
|
||||
hasChildren: children.length > 0,
|
||||
children: []
|
||||
};
|
||||
|
||||
// If stacked bars are enabled, add children data
|
||||
if (enableStackedBars && children.length > 0) {
|
||||
let usedColors = new Set([baseColor]);
|
||||
children.forEach((child, index) => {
|
||||
let childColor = child.color || baseColor;
|
||||
|
||||
// If color is already used, darken it
|
||||
while (usedColors.has(childColor)) {
|
||||
childColor = darkenColor(childColor, 30);
|
||||
}
|
||||
usedColors.add(childColor);
|
||||
|
||||
barData.children.push({
|
||||
id: child.id,
|
||||
label: child.label,
|
||||
value: parseFloat(child.value) || 0,
|
||||
color: childColor
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
chartDataArray.push(barData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -103,7 +159,12 @@ const BarGraph = ({
|
||||
.padding(0.1);
|
||||
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(chartDataArray, d => d.value) || 100])
|
||||
.domain([0, d3.max(chartDataArray, d => {
|
||||
if (enableStackedBars) {
|
||||
return d.value + d.children.reduce((sum, child) => sum + child.value, 0);
|
||||
}
|
||||
return d.value;
|
||||
}) || 100])
|
||||
.range([innerHeight, 0]);
|
||||
|
||||
// Get color for a datapoint based on color source
|
||||
@ -146,20 +207,44 @@ const BarGraph = ({
|
||||
}
|
||||
|
||||
// Add bars
|
||||
const bars = g.selectAll('rect')
|
||||
const barGroups = g.selectAll('.bar-group')
|
||||
.data(chartDataArray)
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', d => xScale(d.label))
|
||||
.append('g')
|
||||
.attr('class', 'bar-group')
|
||||
.attr('transform', d => `translate(${xScale(d.label)},0)`);
|
||||
|
||||
// Add main bars
|
||||
barGroups.append('rect')
|
||||
.attr('class', 'main-bar')
|
||||
.attr('y', d => yScale(d.value))
|
||||
.attr('width', xScale.bandwidth())
|
||||
.attr('height', d => innerHeight - yScale(d.value))
|
||||
.attr('fill', d => getColor(d))
|
||||
.style('opacity', barOpacity)
|
||||
.style('cursor', d => d.hasChildren ? 'pointer' : 'default');
|
||||
.style('cursor', d => (d.hasChildren && !enableStackedBars) ? 'pointer' : 'default');
|
||||
|
||||
if (enableHierarchicalView) {
|
||||
bars.on('click', function(event, d) {
|
||||
// Add stacked child bars if enabled
|
||||
if (enableStackedBars) {
|
||||
barGroups.each(function(d) {
|
||||
let yOffset = d.value;
|
||||
d.children.forEach(child => {
|
||||
d3.select(this)
|
||||
.append('rect')
|
||||
.attr('class', 'child-bar')
|
||||
.attr('y', () => yScale(yOffset + child.value))
|
||||
.attr('width', xScale.bandwidth())
|
||||
.attr('height', () => innerHeight - yScale(child.value))
|
||||
.attr('fill', child.color)
|
||||
.style('opacity', barOpacity);
|
||||
yOffset += child.value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Add click handlers for hierarchical view
|
||||
if (enableHierarchicalView && !enableStackedBars) {
|
||||
barGroups.selectAll('.main-bar').on('click', function(event, d) {
|
||||
if (d.hasChildren && setAttributes) {
|
||||
setAttributes({ selectedParentId: d.id });
|
||||
}
|
||||
@ -191,15 +276,31 @@ const BarGraph = ({
|
||||
|
||||
// Add bar values
|
||||
if (showBarValues) {
|
||||
g.selectAll('.bar-value')
|
||||
.data(chartDataArray)
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('class', 'bar-value')
|
||||
.attr('x', d => xScale(d.label) + xScale.bandwidth() / 2)
|
||||
.attr('y', d => yScale(d.value) - 5)
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(d => d.value);
|
||||
barGroups.each(function(d) {
|
||||
// Add value for main bar
|
||||
d3.select(this)
|
||||
.append('text')
|
||||
.attr('class', 'bar-value')
|
||||
.attr('x', xScale.bandwidth() / 2)
|
||||
.attr('y', yScale(d.value) - 5)
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(d.value);
|
||||
|
||||
// Add values for stacked bars if enabled
|
||||
if (enableStackedBars) {
|
||||
let yOffset = d.value;
|
||||
d.children.forEach(child => {
|
||||
d3.select(this)
|
||||
.append('text')
|
||||
.attr('class', 'bar-value')
|
||||
.attr('x', xScale.bandwidth() / 2)
|
||||
.attr('y', yScale(yOffset + child.value) - 5)
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(child.value);
|
||||
yOffset += child.value;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add X axis
|
||||
@ -239,7 +340,7 @@ const BarGraph = ({
|
||||
.text(yAxisLabel);
|
||||
}
|
||||
|
||||
}, [data, width, height, title, showGridX, showGridY, xGridColor, yGridColor, xGridWidth, yGridWidth, showBarValues, xAxisLabel, yAxisLabel, colorSource, defaultBarColor, customColors, barOpacity, enableHierarchicalView, selectedParentId]);
|
||||
}, [data, width, height, title, showGridX, showGridY, xGridColor, yGridColor, xGridWidth, yGridWidth, showBarValues, xAxisLabel, yAxisLabel, colorSource, defaultBarColor, customColors, barOpacity, enableHierarchicalView, enableStackedBars, selectedParentId]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -53,6 +53,7 @@ export default function Edit({ attributes, setAttributes }) {
|
||||
chartColorSource,
|
||||
chartCustomColors,
|
||||
enableHierarchicalView,
|
||||
enableStackedBars,
|
||||
selectedParentId } = attributes;
|
||||
|
||||
const blockProps = useBlockProps();
|
||||
@ -240,17 +241,16 @@ export default function Edit({ attributes, setAttributes }) {
|
||||
onDataSourceChange={handleDataSourceChange}
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody title="Chart Settings">
|
||||
<PanelBody title={__('Chart Settings', 'lcp')} initialOpen={true}>
|
||||
<ToggleControl
|
||||
label={__('Enable Hierarchical View', 'lcp')}
|
||||
help={__('Display data hierarchically. Click bars to show their children.', 'lcp')}
|
||||
checked={attributes.enableHierarchicalView}
|
||||
onChange={(value) => {
|
||||
setAttributes({
|
||||
enableHierarchicalView: value,
|
||||
selectedParentId: null // Reset when toggling
|
||||
});
|
||||
}}
|
||||
checked={enableHierarchicalView}
|
||||
onChange={(value) => setAttributes({ enableHierarchicalView: value })}
|
||||
/>
|
||||
<ToggleControl
|
||||
label={__('Enable Stacked Bars', 'lcp')}
|
||||
checked={enableStackedBars}
|
||||
onChange={(value) => setAttributes({ enableStackedBars: value })}
|
||||
/>
|
||||
<ToggleControl
|
||||
label={__('Display Chart Title', 'lcp')}
|
||||
@ -501,6 +501,7 @@ export default function Edit({ attributes, setAttributes }) {
|
||||
colorSource={chartColorSource}
|
||||
customColors={chartCustomColors}
|
||||
enableHierarchicalView={enableHierarchicalView}
|
||||
enableStackedBars={enableStackedBars}
|
||||
selectedParentId={selectedParentId}
|
||||
setAttributes={setAttributes}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user