mirror of
https://github.com/umami-software/umami.git
synced 2026-05-30 06:47:25 +00:00
Merge pull request #4259 from yancat160/feat/persist-hidden-events-across-range
fix: keep hidden events hidden across date-range changes
This commit is contained in:
@@ -18,6 +18,8 @@ export interface ChartProps extends BoxProps {
|
||||
updateMode?: UpdateMode;
|
||||
animationDuration?: number;
|
||||
onTooltip?: (model: any) => void;
|
||||
hiddenLabels?: Set<string>;
|
||||
onLegendClick?: (label: string, willBeHidden: boolean) => void;
|
||||
}
|
||||
|
||||
export function Chart({
|
||||
@@ -27,6 +29,8 @@ export function Chart({
|
||||
updateMode,
|
||||
onTooltip,
|
||||
chartOptions,
|
||||
hiddenLabels,
|
||||
onLegendClick,
|
||||
...props
|
||||
}: ChartProps) {
|
||||
const canvas = useRef(null);
|
||||
@@ -61,6 +65,15 @@ export function Chart({
|
||||
}, [chartOptions]);
|
||||
|
||||
const handleLegendClick = (item: LegendItem) => {
|
||||
if (onLegendClick && type === 'bar') {
|
||||
// Controlled mode: caller owns the hidden state. We report the click
|
||||
// and let the parent push a new hiddenLabels set on the next render.
|
||||
const { datasetIndex } = item;
|
||||
const ds = chart.current.data.datasets[datasetIndex];
|
||||
onLegendClick(ds.label, !hiddenLabels?.has(ds.label));
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'bar') {
|
||||
const { datasetIndex } = item;
|
||||
const meta = chart.current.getDatasetMeta(datasetIndex);
|
||||
@@ -111,13 +124,28 @@ export function Chart({
|
||||
});
|
||||
}
|
||||
|
||||
// Re-apply caller-driven hidden flags after focusLabel handling so a
|
||||
// dataset stays hidden across data changes (e.g. date-range switches)
|
||||
// even though Chart.js regenerates dataset meta on every replace.
|
||||
if (hiddenLabels) {
|
||||
chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => {
|
||||
if (hiddenLabels.has(ds.label)) {
|
||||
ds.hidden = true;
|
||||
} else if (!chartData.focusLabel) {
|
||||
// Explicitly reset so un-hiding a label is always reflected,
|
||||
// regardless of whether the focusLabel pass ran above.
|
||||
ds.hidden = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
chart.current.options = options;
|
||||
|
||||
chart.current.update(updateMode);
|
||||
|
||||
setLegendItems(chart.current.legend.legendItems);
|
||||
}
|
||||
}, [chartData, options, updateMode]);
|
||||
}, [chartData, options, updateMode, hiddenLabels]);
|
||||
|
||||
return (
|
||||
<Column gap="6">
|
||||
|
||||
@@ -27,6 +27,16 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps)
|
||||
const { locale, dateLocale } = useLocale();
|
||||
const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId, { limit });
|
||||
const [label, setLabel] = useState<string>(focusLabel);
|
||||
const [hiddenLabels, setHiddenLabels] = useState<Set<string>>(() => new Set());
|
||||
|
||||
const handleLegendClick = useCallback((legendLabel: string, willBeHidden: boolean) => {
|
||||
setHiddenLabels(prev => {
|
||||
const next = new Set(prev);
|
||||
if (willBeHidden) next.add(legendLabel);
|
||||
else next.delete(legendLabel);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const chartData: any = useMemo(() => {
|
||||
if (!data) return;
|
||||
@@ -115,6 +125,8 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps)
|
||||
stacked={true}
|
||||
renderXLabel={renderXLabel}
|
||||
height="400px"
|
||||
hiddenLabels={hiddenLabels}
|
||||
onLegendClick={handleLegendClick}
|
||||
/>
|
||||
)}
|
||||
</LoadingPanel>
|
||||
|
||||
Reference in New Issue
Block a user