JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="chart-container">
<h1>Dynamic Bar Chart Report</h1>
<!-- The data-json attribute holds your chart data.
It's structured as [label, value1, tooltip1, color1, value2, tooltip2, color2, ...]
This mock data simulates the structure from your original image. -->
<div id="chart_div" class="complex-barchart"
data-chart-type="bar"
data-json='{"cols": [{"type": "string" ,"id": "PrimaryKey" ,"label": "PrimaryKey" }, {"type": "number" ,"id": "SoftwareHours" ,"label": "Software" }, {"type": "string" ,"id": "SoftwareAnnotation" ,"label": "SoftwareAnnotation" , "p": {"role" : "annotation"}}, {"type": "string" ,"id": "SoftwareToolTip" ,"label": "SoftwareToolTip" , "p": {"role" : "tooltip"}}, {"type": "string" ,"id": "SoftwareColor" ,"label": "SoftwareColor" , "p": {"role" : "style"}}, {"type": "number" ,"id": "SoftwareBudgetHours" ,"label": "SoftwareBudgetHours" }, {"type": "string" ,"id": "SoftwareBudgetAnnotation" ,"label": "SoftwareBudgetAnnotation" , "p": {"role" : "annotation"}}, {"type": "string" ,"id": "SoftwareBudgetToolTip" ,"label": "SoftwareBudgetToolTip" , "p": {"role" : "tooltip"}}, {"type": "string" ,"id": "SoftwareBudgetColor" ,"label": "SoftwareBudgetColor" , "p": {"role" : "style"}}, {"type": "number" ,"id": "ManufacturingHours" ,"label": "Manufacturing" }, {"type": "string" ,"id": "ManufacturingAnnotation" ,"label": "ManufacturingAnnotation" , "p": {"role" : "annotation"}}, {"type": "string" ,"id": "ManufacturingToolTip" ,"label": "ManufacturingToolTip" , "p": {"role" : "tooltip"}}, {"type": "string" ,"id": "ManufacturingColor" ,"label": "ManufacturingColor" , "p": {"role" : "style"}}, {"type": "number" ,"id": "ManufacturingBudgetHours" ,"label": "ManufacturingBudgetHours" }, {"type": "string" ,"id":...
CSS
body {
font-family: 'Inter', sans-serif;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
background-color: #f0f2f5;
padding: 20px;
box-sizing: border-box;
}
.chart-container {
width: 90%; /* Responsive width */
max-width: 1200px; /* Max width for larger screens */
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
box-sizing: border-box;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
/* Ensure the chart div itself can grow */
#chart_div {
width: 100%;
/* Height will be set dynamically by Google Charts */
}
JavaScript
google.charts.load('current', { packages: ['corechart', 'bar'] })
google.charts.setOnLoadCallback(GenerateComplexBarChartReports)
// Helper function to find the minimum value for hAxis, if needed.
// This is a placeholder; you'll need to implement your actual logic here.
function FindMinValue(data) {
let minValue = 0 // Default to 0, or calculate based on your data
// Example: iterate through data to find min value if needed
// for (let i = 0; i < data.getNumberOfRows(); i++) {
// for (let j = 1; j < data.getNumberOfColumns(); j += 8) { // Adjusted to jump by 8 for each department
// let actualHours = data.getValue(i, j);
// let budgetHours = data.getValue(i, j + 4); // Budget hours are 4 columns after actual
// if (typeof actualHours === "number" && actualHours < minValue) {
// minValue = actualHours;
// }
// if (typeof budgetHours === "number" && budgetHours < minValue) {
// minValue = budgetHours;
// }
// }
// }
return minValue
}
/**
* Calculates the appropriate chart height based on the number of bars
* per y-axis item to prevent overlap and ensure proper spacing.
* @param {google.visualization.DataTable} data The DataTable object for the chart.
* @returns {number} The calculated height for the chartArea.
*/
function CalculateChartHeightBasedOnDataItems(data) {
// Height needed for a single bar including its annotation.
// This is the fundamental unit of vertical space.
const heightOfOneBarAndAnnotation = 30 // Further reduced for tighter packing
// Minimal vertical space for the category label itself, if no bars are present,
// or as a base for very sparse categories.
const minHeightForCategoryLabel = 15 // Further reduced for tighter packing
// Small, consistent padding *between* different y-axis categories.
const interCategorySpacing = 5 // Further reduced for tighter packing
let...