|
9 | 9 | useWebsiteEventsSeriesQuery, |
10 | 10 | } from '@/components/hooks'; |
11 | 11 | import { renderDateLabels } from '@/lib/charts'; |
| 12 | +import { hex6 } from '@/lib/colors'; |
12 | 13 | import { CHART_COLORS } from '@/lib/constants'; |
13 | 14 | import { generateTimeSeries } from '@/lib/date'; |
14 | 15 |
|
@@ -51,9 +52,36 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps) |
51 | 52 | ], |
52 | 53 | }; |
53 | 54 | } else { |
| 55 | + // Each label has a preferred palette slot derived from a hash of the |
| 56 | + // label, so the same event tends to get the same color across reloads |
| 57 | + // and date-range changes. We walk labels in hash order and, when two |
| 58 | + // labels prefer the same slot, the later one steps to the next free |
| 59 | + // slot, so the visible set of <=12 events all get distinct colors. |
| 60 | + // The right shift on hex6 sidesteps the FNV-1a low-bit bias mod 12 |
| 61 | + // (the FNV prime is close to 2^24). |
| 62 | + const colorByKey: Record<string, string> = {}; |
| 63 | + const used = new Set<string>(); |
| 64 | + const hashOf = Object.fromEntries( |
| 65 | + Object.keys(map).map(key => [key, parseInt(hex6(key), 16)]), |
| 66 | + ); |
| 67 | + const orderedKeys = [...Object.keys(map)].sort((a, b) => hashOf[a] - hashOf[b]); |
| 68 | + for (const key of orderedKeys) { |
| 69 | + const start = (hashOf[key] >>> 4) % CHART_COLORS.length; |
| 70 | + let chosen = CHART_COLORS[start]; |
| 71 | + for (let i = 0; i < CHART_COLORS.length; i++) { |
| 72 | + const candidate = CHART_COLORS[(start + i) % CHART_COLORS.length]; |
| 73 | + if (!used.has(candidate)) { |
| 74 | + chosen = candidate; |
| 75 | + break; |
| 76 | + } |
| 77 | + } |
| 78 | + used.add(chosen); |
| 79 | + colorByKey[key] = chosen; |
| 80 | + } |
| 81 | + |
54 | 82 | return { |
55 | | - datasets: Object.keys(map).map((key, index) => { |
56 | | - const color = colord(CHART_COLORS[index % CHART_COLORS.length]); |
| 83 | + datasets: Object.keys(map).map(key => { |
| 84 | + const color = colord(colorByKey[key]); |
57 | 85 | return { |
58 | 86 | label: key, |
59 | 87 | data: generateTimeSeries(map[key], startDate, endDate, unit, dateLocale), |
|
0 commit comments