Skip to content

Instantly share code, notes, and snippets.

@ejabu
Created September 16, 2025 19:33
Show Gist options
  • Select an option

  • Save ejabu/72f7ea92ca9c12f1c73ef156683c938f to your computer and use it in GitHub Desktop.

Select an option

Save ejabu/72f7ea92ca9c12f1c73ef156683c938f to your computer and use it in GitHub Desktop.

Revisions

  1. ejabu created this gist Sep 16, 2025.
    200 changes: 200 additions & 0 deletions bar-chart.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,200 @@
    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;