Created
September 16, 2025 19:33
-
-
Save ejabu/72f7ea92ca9c12f1c73ef156683c938f to your computer and use it in GitHub Desktop.
Recharts Drill Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)} | |
| > | |
| </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