Library/Components/Tables/MatrixTable

MatrixTable

react
InvoiceTableTablesTableExpandable
MatrixTable.tsx — preview
reactlive
MatrixTable.tsx
'use client'

import React, { useState, useMemo } from 'react'

interface MatrixData {
  rows: string[]
  columns: string[]
  values: number[][]
}

interface TooltipState {
  visible: boolean
  x: number
  y: number
  content: string
}

const MatrixTable: React.FC = () => {
  const [tooltipState, setTooltipState] = useState<TooltipState>({
    visible: false,
    x: 0,
    y: 0,
    content: '',
  })

  // Demo data: Sales by Region and Quarter
  const data: MatrixData = {
    rows: ['North Region', 'South Region', 'East Region', 'West Region', 'Central Region'],
    columns: ['Q1 2024', 'Q2 2024', 'Q3 2024', 'Q4 2024'],
    values: [
      [45000, 52000, 48000, 61000],
      [38000, 41000, 39000, 52000],
      [62000, 58000, 65000, 72000],
      [51000, 54000, 49000, 58000],
      [33000, 36000, 41000, 47000],
    ],
  }

  // Calculate row and column totals
  const { rowTotals, columnTotals, maxValue, minValue } = useMemo(() => {
    const rowTotals = data.values.map((row) =>
      row.reduce((sum, val) => sum + val, 0)
    )
    const columnTotals = data.columns.map((_, colIndex) =>
      data.values.reduce((sum, row) => sum + row[colIndex], 0)
    )

    const allValues = data.values.flat()
    return {
      rowTotals,
      columnTotals,
      maxValue: Math.max(...allValues),
      minValue: Math.min(...allValues),
    }
  }, [])

  // Function to calculate color intensity based on value
  const getHeatmapColor = (value: number): string => {
    if (maxValue === minValue) {
      return 'bg-blue-500'
    }

    const normalized = (value - minValue) / (maxValue - minValue)

    // Blue intensity scale: light blue to dark blue
    if (normalized < 0.25) {
      return 'bg-blue-200'
    } else if (normalized < 0.5) {
      return 'bg-blue-400'
    } else if (normalized < 0.75) {
      return 'bg-blue-600'
    } else {
      return 'bg-blue-800'
    }
  }

  // Format number with thousand separator
  const formatNumber = (num: number): string => {
    return num.toLocaleString('en-US')
  }

  const handleCellHover = (
    e: React.MouseEvent<HTMLDivElement>,
    rowLabel: string,
    colLabel: string,
    value: number
  ) => {
    const rect = e.currentTarget.getBoundingClientRect()
    setTooltipState({
      visible: true,
      x: rect.left + rect.width / 2,
      y: rect.top - 10,
      content: `${rowLabel} - ${colLabel}: ${formatNumber(value)}`,
    })
  }

  const handleMouseLeave = () => {
    setTooltipState({ ...tooltipState, visible: false })
  }

  return (
    <div className="w-full bg-[#0A0A0A] text-[#F5F5F0] p-8 rounded-lg">
      <div className="mb-6">
        <h2 className="text-2xl font-bold mb-2 text-[#C9A84C]">Matrix Table</h2>
        <p className="text-sm text-gray-400">Sales Performance by Region and Quarter</p>
      </div>

      <div className="overflow-x-auto">
        <table className="w-full border-collapse">
          <thead>
            <tr>
              <th className="bg-gray-900 border border-[#C9A84C] p-3 text-left text-[#C9A84C] font-semibold w-32">
                Region
              </th>
              {data.columns.map((col, idx) => (
                <th
                  key={idx}
                  className="bg-gray-900 border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-semibold min-w-24"
                >
                  {col}
                </th>
              ))}
              <th className="bg-gray-900 border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-semibold min-w-24">
                Total
              </th>
            </tr>
          </thead>
          <tbody>
            {data.rows.map((row, rowIdx) => (
              <tr key={rowIdx}>
                <td className="bg-gray-900 border border-gray-700 p-3 text-[#C9A84C] font-semibold">
                  {row}
                </td>
                {data.values[rowIdx].map((value, colIdx) => (
                  <td
                    key={`${rowIdx}-${colIdx}`}
                    className={`border border-gray-700 p-3 text-center text-white cursor-pointer transition-opacity hover:opacity-80 ${getHeatmapColor(value)}`}
                    onMouseEnter={(e) => handleCellHover(e, row, data.columns[colIdx], value)}
                    onMouseLeave={handleMouseLeave}
                  >
                    <span className="font-medium">{formatNumber(value)}</span>
                  </td>
                ))}
                <td className="bg-gray-900 border border-gray-700 p-3 text-center text-[#C9A84C] font-bold">
                  {formatNumber(rowTotals[rowIdx])}
                </td>
              </tr>
            ))}
            <tr className="bg-gray-900">
              <td className="border border-[#C9A84C] p-3 text-[#C9A84C] font-bold">Total</td>
              {columnTotals.map((total, idx) => (
                <td
                  key={idx}
                  className="border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-bold"
                >
                  {formatNumber(total)}
                </td>
              ))}
              <td className="border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-bold">
                {formatNumber(rowTotals.reduce((a, b) => a + b, 0))}
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      {/* Heatmap legend */}
      <div className="mt-6 flex items-center gap-4">
        <span className="text-sm font-semibold text-[#C9A84C]">Intensity Scale:</span>
        <div className="flex gap-2 items-center">
          <div className="w-6 h-6 bg-blue-200 border border-gray-600"></div>
          <span className="text-xs text-gray-400">Low</span>
          <div className="w-6 h-6 bg-blue-400 border border-gray-600"></div>
          <div className="w-6 h-6 bg-blue-600 border border-gray-600"></div>
          <div className="w-6 h-6 bg-blue-800 border border-gray-600"></div>
          <span className="text-xs text-gray-400">High</span>
        </div>
      </div>

      {/* Tooltip */}
      {tooltipState.visible && (
        <div
          className="fixed bg-gray-800 text-white px-3 py-2 rounded text-sm border border-[#C9A84C] pointer-events-none z-50"
          style={{
            left: `${tooltipState.x}px`,
            top: `${tooltipState.y}px`,
            transform: 'translate(-50%, -100%)',
          }}
        >
          {tooltipState.content}
        </div>
      )}
    </div>
  )
}

export default MatrixTable
Same category

Similar components

MatrixTable
MatrixTable
DataTable
DataTable
AnalyticsTable
AnalyticsTable
DataTable
DataTable