Skip to content

Commit

Permalink
Simplified chart click event / callbacks (#322)
Browse files Browse the repository at this point in the history
* docs: Reorder some examples

* feat(Highlight): Add `onAreaClick`, `onBarClick`, and `onPointClick` callback events

* feat(AreaChart|LineChart): Add `onPointClick` callback event (useful for multi-series charts)

* feat(Bars): Add `onBarClick` callback event

* feat(BarChart): Add `onBarClick` callback event (useful for multi-series charts)

* docs: Refine comments and click examples

* feat(PieChart): Add `onArcClick` callback event

* feat: Add `onTooltipClick` callback event for all simplified charts (AreaChart, BarChart, LineChart, PieChart, and ScatterChart)
  • Loading branch information
techniq authored Jan 15, 2025
1 parent 42796f6 commit c4a2a60
Show file tree
Hide file tree
Showing 19 changed files with 409 additions and 102 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-apes-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': minor
---

feat(Highlight): Add `onAreaClick`, `onBarClick`, and `onPointClick` callback events
5 changes: 5 additions & 0 deletions .changeset/cuddly-poems-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': minor
---

feat(BarChart): Add `onBarClick` callback event (useful for multi-series charts)
5 changes: 5 additions & 0 deletions .changeset/lazy-weeks-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': minor
---

feat(AreaChart|LineChart): Add `onPointClick` callback event (useful for multi-series charts)
5 changes: 5 additions & 0 deletions .changeset/pretty-taxis-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': patch
---

feat(Bars): Add `onBarClick` callback event
5 changes: 5 additions & 0 deletions .changeset/real-shrimps-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': minor
---

feat(PieChart): Add `onArcClick` callback event
5 changes: 5 additions & 0 deletions .changeset/shaggy-cameras-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': minor
---

feat: Add `onTooltipClick` callback event for all simplified charts (AreaChart, BarChart, LineChart, PieChart, and ScatterChart)
4 changes: 4 additions & 0 deletions packages/layerchart/src/lib/components/Bars.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
export let spring: ComponentProps<Rect>['spring'] = undefined;
export let tweened: ComponentProps<Rect>['tweened'] = undefined;
/** Event dispatched when individual Bar is clicked */
export let onBarClick: (e: { data: any }) => void = () => {};
$: _data = chartDataArray(data ?? $contextData);
</script>

Expand All @@ -66,6 +69,7 @@
{inset}
{spring}
{tweened}
on:click={() => onBarClick({ data: d })}
{...$$restProps}
/>
{/each}
Expand Down
35 changes: 32 additions & 3 deletions packages/layerchart/src/lib/components/Highlight.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<script lang="ts" context="module">
export type HighlightPointData = { x: any; y: any };
</script>

<script lang="ts">
import { type ComponentProps } from 'svelte';
import { max, min } from 'd3-array';
Expand Down Expand Up @@ -62,10 +66,14 @@
/** Set to false to disable spring transitions */
export let motion = true;
export let onAreaClick: (e: { data: any }) => void = () => {};
export let onBarClick: (e: { data: any }) => void = () => {};
export let onPointClick: (e: { point: (typeof _points)[number]; data: any }) => void = () => {};
const _x = accessor(x);
const _y = accessor(y);
let _points: { x: number; y: number; fill: string }[] = [];
let _points: { x: number; y: number; fill: string; data: HighlightPointData }[] = [];
let _lines: { x1: number; y1: number; x2: number; y2: number }[] = [];
let _area = {
x: 0,
Expand Down Expand Up @@ -217,6 +225,10 @@
x: $xScale(seriesPoint.point[1]) + xOffset,
y: yCoord + yOffset,
fill: $config.c ? $cGet(seriesPoint.series) : null,
data: {
x: seriesPoint.point[1],
y: yValue,
},
};
});
}
Expand All @@ -229,6 +241,10 @@
y: yCoord + yOffset,
// TODO: is there a better way to expose the series key/value?
fill: $config.c ? $cGet({ ...highlightData, $key }) : null,
data: {
x: xValue, // TODO: use highlightData[$key]?
y: yValue,
},
};
});
}
Expand Down Expand Up @@ -256,6 +272,10 @@
x: xCoord + xOffset,
y: $yScale(seriesPoint.point[1]) + yOffset,
fill: $config.c ? $cGet(seriesPoint.series) : null,
data: {
x: xValue,
y: seriesPoint.point[1],
},
}));
}
} else {
Expand All @@ -267,6 +287,10 @@
y: yItem + yOffset,
// TODO: is there a better way to expose the series key/value?
fill: $config.c ? $cGet({ ...highlightData, $key }) : null,
data: {
x: xValue,
y: yValue, // TODO: use highlightData[$key] ?
},
};
});
}
Expand All @@ -276,6 +300,10 @@
x: xCoord + xOffset,
y: yCoord + yOffset,
fill: $config.c ? $cGet(highlightData) : null,
data: {
x: xValue,
y: yValue,
},
},
];
} else {
Expand Down Expand Up @@ -322,7 +350,7 @@
!area.fill && 'fill-surface-content/5',
typeof area === 'object' ? area.class : null
)}
on:click
on:click={() => onAreaClick({ data: highlightData })}
/>
</slot>
{/if}
Expand All @@ -343,7 +371,7 @@
!bar.fill && 'fill-primary',
typeof bar === 'object' ? bar.class : null
)}
on:click
on:click={() => onBarClick({ data: highlightData })}
/>
</slot>
{/if}
Expand Down Expand Up @@ -382,6 +410,7 @@
!point.fill && (typeof points === 'boolean' || !points.fill) && 'fill-primary',
typeof points === 'object' ? points.class : null
)}
on:click={() => onPointClick({ point, data: highlightData })}
/>
{/each}
</slot>
Expand Down
16 changes: 14 additions & 2 deletions packages/layerchart/src/lib/components/charts/AreaChart.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import Canvas from '../layout/Canvas.svelte';
import Chart from '../Chart.svelte';
import Grid from '../Grid.svelte';
import Highlight from '../Highlight.svelte';
import Highlight, { type HighlightPointData } from '../Highlight.svelte';
import Labels from '../Labels.svelte';
import Legend from '../Legend.svelte';
import Line from '../Line.svelte';
Expand Down Expand Up @@ -38,6 +38,8 @@
series?: typeof series;
seriesLayout?: typeof seriesLayout;
renderContext?: typeof renderContext;
onPointClick?: typeof onPointClick;
onTooltipClick?: typeof onTooltipClick;
}
export let data: $$Props['data'] = [];
Expand Down Expand Up @@ -69,6 +71,15 @@
export let legend: ComponentProps<Legend> | boolean = false;
export let points: ComponentProps<Points> | boolean = false;
/** Event dispatched with current tooltip data */
export let onTooltipClick: (e: { data: any }) => void = () => {};
/** Event dispatched when Highlight point is clicked (useful with multiple series) */
export let onPointClick: (e: {
data: HighlightPointData;
series: (typeof series)[number];
}) => void = () => {};
export let props: {
xAxis?: Partial<ComponentProps<Axis>>;
yAxis?: Partial<ComponentProps<Axis>>;
Expand Down Expand Up @@ -162,7 +173,7 @@
yNice
{radial}
padding={radial ? undefined : defaultChartPadding(axis, legend)}
tooltip={{ mode: 'bisect-x' }}
tooltip={{ mode: 'bisect-x', onClick: onTooltipClick }}
{...$$restProps}
let:x
let:xScale
Expand Down Expand Up @@ -262,6 +273,7 @@
y={stackSeries ? (d) => d.stackData[i][1] : (s.value ?? (s.data ? undefined : s.key))}
points={{ fill: s.color }}
lines={i == 0}
onPointClick={(e) => onPointClick({ ...e, series: s })}
{...props.highlight}
/>
{/each}
Expand Down
12 changes: 11 additions & 1 deletion packages/layerchart/src/lib/components/charts/BarChart.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
series?: typeof series;
seriesLayout?: typeof seriesLayout;
renderContext?: typeof renderContext;
onBarClick?: typeof onBarClick;
onTooltipClick?: typeof onTooltipClick;
}
export let data: $$Props['data'] = [];
Expand Down Expand Up @@ -82,6 +84,13 @@
/** Padding between group/series items when using 'seriesLayout="group"', applied to scaleBand().padding() */
export let groupPadding = 0;
/** Event dispatched with current tooltip data */
export let onTooltipClick: (e: { data: any }) => void = () => {};
// TODO: Need to find a way to have this play nice with `tooltip={{ mode: 'band' }}`
/** Event dispatched when individual Bar is clicked (useful with multiple series) */
export let onBarClick: (e: { data: any; series: (typeof series)[number] }) => void = () => {};
$: xScale = $$props.xScale ?? (isVertical ? scaleBand().padding(bandPadding) : scaleLinear());
$: xBaseline = isVertical ? undefined : 0;
Expand Down Expand Up @@ -173,6 +182,7 @@
radius: 4,
strokeWidth: 1,
fill: s.color,
onBarClick: (e) => onBarClick({ data: e.data, series: s }),
...props.bars,
...s.props,
};
Expand Down Expand Up @@ -206,7 +216,7 @@
c={isVertical ? y : x}
cRange={['hsl(var(--color-primary))']}
padding={defaultChartPadding(axis, legend)}
tooltip={{ mode: 'band' }}
tooltip={{ mode: 'band', onClick: onTooltipClick }}
{...$$restProps}
let:x
let:xScale
Expand Down
16 changes: 14 additions & 2 deletions packages/layerchart/src/lib/components/charts/LineChart.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import Canvas from '../layout/Canvas.svelte';
import Chart from '../Chart.svelte';
import Grid from '../Grid.svelte';
import Highlight from '../Highlight.svelte';
import Highlight, { type HighlightPointData } from '../Highlight.svelte';
import Labels from '../Labels.svelte';
import Legend from '../Legend.svelte';
import Points from '../Points.svelte';
Expand All @@ -34,6 +34,8 @@
rule?: typeof rule;
series?: typeof series;
renderContext?: typeof renderContext;
onPointClick?: typeof onPointClick;
onTooltipClick?: typeof onTooltipClick;
}
export let data: $$Props['data'] = [];
Expand Down Expand Up @@ -61,6 +63,15 @@
export let legend: ComponentProps<Legend> | boolean = false;
export let points: ComponentProps<Points> | boolean = false;
/** Event dispatched with current tooltip data */
export let onTooltipClick: (e: { data: any }) => void = () => {};
/** Event dispatched when Highlight point is clicked (useful with multiple series) */
export let onPointClick: (e: {
data: HighlightPointData;
series: (typeof series)[number];
}) => void = () => {};
export let props: {
xAxis?: Partial<ComponentProps<Axis>>;
yAxis?: Partial<ComponentProps<Axis>>;
Expand Down Expand Up @@ -110,7 +121,7 @@
yNice
{radial}
padding={radial ? undefined : defaultChartPadding(axis, legend)}
tooltip={{ mode: 'bisect-x' }}
tooltip={{ mode: 'bisect-x', onClick: onTooltipClick }}
{...$$restProps}
let:x
let:xScale
Expand Down Expand Up @@ -206,6 +217,7 @@
y={s.value ?? (s.data ? undefined : s.key)}
points={{ fill: s.color }}
lines={i === 0}
onPointClick={(e) => onPointClick({ ...e, series: s })}
{...props.highlight}
/>
{/each}
Expand Down
21 changes: 20 additions & 1 deletion packages/layerchart/src/lib/components/charts/PieChart.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
props?: typeof props;
range?: typeof range;
series?: typeof series;
value?: typeof label;
value?: typeof value;
renderContext?: typeof renderContext;
onArcClick?: typeof onArcClick;
onTooltipClick?: typeof onTooltipClick;
}
export let data: ChartProps['data'] = [];
Expand Down Expand Up @@ -93,6 +95,13 @@
/** Center chart. Override and use `props.group` for more control */
export let center = placement === 'center';
// TODO: Not usable with manual tooltip / arc path. Use `onArcClick`?
/** Event dispatched with current tooltip data */
export let onTooltipClick: (e: { data: any }) => void = () => {};
/** Event dispatched when individual Arc is clicked (useful with multiple series) */
export let onArcClick: (e: { data: any; series: (typeof series)[number] }) => void = () => {};
export let props: {
pie?: Partial<ComponentProps<Pie>>;
group?: Partial<ComponentProps<Group>>;
Expand Down Expand Up @@ -184,6 +193,11 @@
track={{ fill: s.color ?? cScale?.(c(d)), 'fill-opacity': 0.1 }}
{tooltip}
data={d}
on:click={() => {
onArcClick({ data: d, series: s });
// Workaround for `tooltip={{ mode: 'manual' }}
onTooltipClick({ data: d });
}}
{...props.arc}
{...s.props}
/>
Expand All @@ -209,6 +223,11 @@
fill={cScale?.(c(arc.data))}
data={arc.data}
{tooltip}
on:click={() => {
onArcClick({ data: arc.data, series: s });
// Workaround for `tooltip={{ mode: 'manual' }}
onTooltipClick({ data: arc.data });
}}
{...props.arc}
{...s.props}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
props?: typeof props;
series?: typeof series;
renderContext?: typeof renderContext;
onTooltipClick?: typeof onTooltipClick;
}
export let data: $$Props['data'] = [];
Expand All @@ -51,6 +52,9 @@
export let legend: ComponentProps<Legend> | boolean = false;
export let rule: ComponentProps<Rule> | boolean = true;
/** Event dispatched with current tooltip data */
export let onTooltipClick: (e: { data: any }) => void = () => {};
export let props: {
xAxis?: Partial<ComponentProps<Axis>>;
yAxis?: Partial<ComponentProps<Axis>>;
Expand Down Expand Up @@ -100,7 +104,7 @@
{yScale}
yNice
padding={defaultChartPadding(axis, legend)}
tooltip={{ mode: 'voronoi' }}
tooltip={{ mode: 'voronoi', onClick: onTooltipClick }}
{...$$restProps}
let:x
let:xScale
Expand Down
Loading

0 comments on commit c4a2a60

Please sign in to comment.