File size: 3,146 Bytes
d35e9ec
75959de
d35e9ec
d26f541
 
 
d35e9ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26f541
 
5b91336
d26f541
 
 
 
 
 
 
 
 
 
d35e9ec
d26f541
 
 
 
 
d35e9ec
d26f541
d35e9ec
d26f541
 
 
 
 
4207527
b9d7d54
4207527
b9d7d54
d26f541
 
5b91336
 
d35e9ec
5b91336
d26f541
 
 
 
 
 
 
 
 
 
d35e9ec
d26f541
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { useMemo } from 'react';
import type { HeatmapProps } from '../types';
import { categorySort} from '../utils/chartUtils';


const Heatmap: React.FC<HeatmapProps> = ({ matrix, judge1, judge2, onCellClick }) => {
  const { judge1Cat, judge2Cat, maxValue } = useMemo(() => {
    const j1Categories = Object.keys(matrix).sort(categorySort);

    let newMaxValue = 1;
    const allSubCats = new Set<string>();

    j1Categories.forEach(cat => {
      Object.keys(matrix[cat] || {}).forEach(subCat => {
        allSubCats.add(subCat);
        const value = matrix[cat][subCat];
        if (value > newMaxValue) newMaxValue = value;
      });
    });

    // Convert Set to Array and sort
    const j2Categories = Array.from(allSubCats).sort(categorySort);

    return {
      judge1Cat: j1Categories,
      judge2Cat: j2Categories,
      maxValue: newMaxValue
    };
  }, [matrix]);
  

  const getOpacity = (value: number) => {
    return value === 0 ? 0.05 : 0.05 + (value / maxValue) * 0.95;
  };

  return (
    <div className="heatmap-container">
      <h3 className="chart-title">Transition Matrix</h3>
      <div className="heatmap">
        <div className="heatmap-main">
            <div className="heatmap-y-axis">
                <div className="y-axis-label">{judge1.split('/')[1] || judge1}</div>
                <div className="y-ticks">
                    {judge1Cat.map(cat => (
                    <div key={cat} className="y-tick">{cat}</div>
                    ))}
                </div>
            </div>
            <div className="heatmap-grid">
                {judge1Cat.map(fromCat => (
                <div key={fromCat} className="heatmap-row">
                    {judge2Cat.map(toCat => {
                    const value = matrix[fromCat]?.[toCat] || 0;
                    return (
                        <div
                        key={`${fromCat}-${toCat}`}
                        className="heatmap-cell"
                        onClick={() => value < 100000 
                          ? onCellClick(fromCat, toCat)
                          : alert("Details only available for values < 100000. Please refine your selection.")
                        }
                        title={`${fromCat} → ${toCat}: ${value}`}
                        >
                          <div  
                            className='heatmap-cell-bg' 
                            style={{ backgroundColor: `var(--${toCat.toLowerCase()}, #fff)`, opacity: getOpacity(value) }}>
                          </div>
                            {value > 0 && <span className="cell-value">{value}</span>}
                        </div>
                    );
                    })}
                </div>
                ))}
            </div>
        </div>
        <div className="heatmap-x-axis">
            <div className="x-ticks">
              {judge2Cat.map(cat => (
                <div key={cat} className="x-tick">{cat}</div>
              ))}
            </div>
            <div className="x-axis-label">{judge2.split('/')[1] || judge2}</div>
        </div>
      </div>
    </div>
  );
};

export default Heatmap;