Skip to content

Commit bbe1055

Browse files
authored
Merge pull request #4257 from yancat160/fix/stable-event-chart-colors
fix: stabilize event chart colors per label
2 parents c7515ff + 46e1351 commit bbe1055

1 file changed

Lines changed: 30 additions & 2 deletions

File tree

src/components/metrics/EventsChart.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
useWebsiteEventsSeriesQuery,
1010
} from '@/components/hooks';
1111
import { renderDateLabels } from '@/lib/charts';
12+
import { hex6 } from '@/lib/colors';
1213
import { CHART_COLORS } from '@/lib/constants';
1314
import { generateTimeSeries } from '@/lib/date';
1415

@@ -51,9 +52,36 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps)
5152
],
5253
};
5354
} 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+
5482
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]);
5785
return {
5886
label: key,
5987
data: generateTimeSeries(map[key], startDate, endDate, unit, dateLocale),

0 commit comments

Comments
 (0)