Skip to content

Instantly share code, notes, and snippets.

@ejabu
Created September 16, 2025 19:33
Show Gist options
  • Save ejabu/72f7ea92ca9c12f1c73ef156683c938f to your computer and use it in GitHub Desktop.
Save ejabu/72f7ea92ca9c12f1c73ef156683c938f to your computer and use it in GitHub Desktop.
Recharts Drill Down
import React, { useState } from 'react';
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell } from 'recharts';
const data = [
{ name: 'Jan', value: 400, details: 'Q1 Performance: Strong growth in new markets' },
{ name: 'Feb', value: 300, details: 'Q1 Performance: Seasonal adjustment period' },
{ name: 'Mar', value: 300, details: 'Q1 Performance: Recovery after February dip' },
{ name: 'Apr', value: 200, details: 'Q2 Performance: Market consolidation phase' },
{ name: 'May', value: 278, details: 'Q2 Performance: Mid-quarter improvement' },
{ name: 'Jun', value: 189, details: 'Q2 Performance: End of quarter challenges' },
{ name: 'Jul', value: 349, details: 'Q3 Performance: Summer campaign success' },
{ name: 'Aug', value: 400, details: 'Q3 Performance: Peak season results' },
];
const BarChartWithContextMenu = () => {
const [contextMenu, setContextMenu] = useState(null);
const [hoveredIndex, setHoveredIndex] = useState(null);
const [activeSubmenu, setActiveSubmenu] = useState(null);
const handleBarClick = (event, data, index) => {
event.stopPropagation();
setContextMenu({
x: event.clientX,
y: event.clientY,
data: data,
index: index
});
};
const handleContextMenuAction = (action, data) => {
console.log(`${action} clicked for:`, data);
alert(`${action} selected for ${data.name}: ${data.details}`);
setContextMenu(null);
setActiveSubmenu(null);
};
const handleClickOutside = () => {
setContextMenu(null);
setActiveSubmenu(null);
};
const getBarColor = (index) => {
return hoveredIndex === index ? '#ff6b6b' : '#8884d8';
};
return (
<div
className="w-full h-96 p-4 bg-white relative"
onClick={handleClickOutside}
>
<h2 className="text-2xl font-bold mb-4 text-gray-800">Monthly Performance</h2>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
<XAxis
dataKey="name"
tick={{ fontSize: 12 }}
axisLine={{ stroke: '#e0e0e0' }}
/>
<YAxis
tick={{ fontSize: 12 }}
axisLine={{ stroke: '#e0e0e0' }}
gridline={{ stroke: '#f0f0f0' }}
/>
<Bar
dataKey="value"
radius={[4, 4, 0, 0]}
onMouseEnter={(data, index) => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
>
{data.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={getBarColor(index)}
onClick={(event) => handleBarClick(event, entry, index)}
style={{ cursor: 'pointer' }}
/>
))}
</Bar>
</BarChart>
</ResponsiveContainer>
{contextMenu && (
<div
className="absolute bg-white border border-gray-300 rounded-md shadow-lg z-10 min-w-48"
style={{
left: contextMenu.x,
top: contextMenu.y,
}}
onClick={(e) => e.stopPropagation()}
>
<div className="py-1">
<div className="px-4 py-2 text-sm font-medium text-gray-700 border-b border-gray-200">
{contextMenu.data.name} - {contextMenu.data.value}
</div>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('View Details', contextMenu.data)}
>
View Details
</button>
{/* Drill Down with submenu */}
<div
className="relative"
onMouseEnter={() => setActiveSubmenu('drilldown')}
onMouseLeave={() => setActiveSubmenu(null)}
>
<button className="flex items-center justify-between w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100">
<span>Drill Down</span>
<span className="text-xs">▶</span>
</button>
{activeSubmenu === 'drilldown' && (
<div className="absolute left-full top-0 ml-1 bg-white border border-gray-300 rounded-md shadow-lg min-w-40">
<div className="py-1">
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Daily Breakdown', contextMenu.data)}
>
Daily Breakdown
</button>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Weekly Analysis', contextMenu.data)}
>
Weekly Analysis
</button>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Product Categories', contextMenu.data)}
>
Product Categories
</button>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Regional Data', contextMenu.data)}
>
Regional Data
</button>
</div>
</div>
)}
</div>
{/* Export Data with submenu */}
<div
className="relative"
onMouseEnter={() => setActiveSubmenu('export')}
onMouseLeave={() => setActiveSubmenu(null)}
>
<button className="flex items-center justify-between w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100">
<span>Export Data</span>
<span className="text-xs">▶</span>
</button>
{activeSubmenu === 'export' && (
<div className="absolute left-full top-0 ml-1 bg-white border border-gray-300 rounded-md shadow-lg min-w-32">
<div className="py-1">
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Export as CSV', contextMenu.data)}
>
CSV
</button>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Export as PDF', contextMenu.data)}
>
PDF
</button>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Export as Excel', contextMenu.data)}
>
Excel
</button>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Export as JSON', contextMenu.data)}
>
JSON
</button>
</div>
</div>
)}
</div>
<button
className="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100"
onClick={() => handleContextMenuAction('Compare', contextMenu.data)}
>
Compare
</button>
</div>
</div>
)}
</div>
);
};
export default BarChartWithContextMenu;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment