Skip to content

Commit 4f0826e

Browse files
committed
feat: adds onCursorMove callback
1 parent 355a3f4 commit 4f0826e

File tree

4 files changed

+94
-36
lines changed

4 files changed

+94
-36
lines changed

bun.lock

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,7 @@
146146
"solid-js": ">=1.6.0",
147147
"uplot": ">=1.6.32"
148148
},
149-
"dependencies": {}
149+
"dependencies": {
150+
"@solid-primitives/refs": "^1.1.1"
151+
}
150152
}

src/SolidUplot.tsx

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "uplot/dist/uPlot.min.css";
22

3+
import { mergeRefs, Ref } from "@solid-primitives/refs";
34
import {
45
createEffect,
56
createMemo,
@@ -14,6 +15,7 @@ import {
1415
import uPlot from "uplot";
1516

1617
import type { SolidUplotPluginBus, UplotPluginFactory, VoidStruct } from "./createPluginBus";
18+
import { createCursorMovePlugin, type OnCursorMoveParams } from "./eventPlugins";
1719
import { getSeriesData, type SeriesDatum } from "./utils/getSeriesData";
1820

1921
/** Placement options for children components relative to the chart */
@@ -57,40 +59,52 @@ type OnCreateMeta = {
5759
readonly seriesData: SeriesDatum[];
5860
};
5961

62+
type SolidUplotEvents = {
63+
/** Callback fired when the uPlot instance is created */
64+
readonly onCreate?: (u: uPlot, meta: OnCreateMeta) => void;
65+
/** Callback fired when the cursor moves */
66+
readonly onCursorMove?: (params: OnCursorMoveParams) => void;
67+
};
68+
6069
/**
6170
* Props for the SolidUplot component
6271
*
6372
* @template T - The type of the plugin bus data structure
6473
*/
65-
type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> & {
66-
/** Ref callback to access the chart container element */
67-
readonly ref?: (el: HTMLDivElement) => void;
68-
/** Callback fired when the uPlot instance is created */
69-
readonly onCreate?: (u: uPlot, meta: OnCreateMeta) => void;
70-
/**
71-
* Whether to reset scales when chart data is updated
72-
* @default true
73-
*/
74-
readonly resetScales?: boolean;
75-
/** CSS styles for the chart container (position is managed internally) */
76-
readonly style?: Omit<JSX.CSSProperties, "position">;
77-
/**
78-
* Where to place children components relative to the chart
79-
* @default "top"
80-
*/
81-
readonly childrenPlacement?: ChildrenPlacement;
82-
/**
83-
* Enable automatic resizing to fit container.
84-
*
85-
* When true:
86-
* - Chart uses width/height props for initial render
87-
* - Then automatically adapts to container size changes
88-
* - If no width/height provided, uses sensible defaults (600x300)
89-
*
90-
* @default false
91-
*/
92-
readonly autoResize?: boolean;
93-
};
74+
type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> &
75+
SolidUplotEvents & {
76+
/** Class name for the chart container */
77+
readonly class?: string;
78+
79+
/** CSS styles for the chart container (position is managed internally) */
80+
readonly style?: Omit<JSX.CSSProperties, "position">;
81+
82+
/** Ref callback to access the chart container element */
83+
readonly ref?: Ref<HTMLDivElement>;
84+
85+
/**
86+
* Enable automatic resizing to fit container.
87+
*
88+
* When true:
89+
* - Chart uses width/height props for initial render
90+
* - Then automatically adapts to container size changes
91+
* - If no width/height provided, uses sensible defaults (600x300)
92+
*
93+
* @default false
94+
*/
95+
readonly autoResize?: boolean;
96+
97+
/**
98+
* Whether to reset scales when chart data is updated
99+
* @default true
100+
*/
101+
readonly resetScales?: boolean;
102+
/**
103+
* Where to place children components relative to the chart
104+
* @default "top"
105+
*/
106+
readonly childrenPlacement?: ChildrenPlacement;
107+
};
94108

95109
/**
96110
* A SolidJS wrapper component for uPlot charts with enhanced features
@@ -146,8 +160,10 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
146160
const [local, options] = splitProps(_props, [
147161
"children",
148162
"childrenPlacement",
163+
"class",
149164
"autoResize",
150165
"onCreate",
166+
"onCursorMove",
151167
"style",
152168
"ref",
153169
]);
@@ -164,9 +180,16 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
164180
const size = () => ({ width: updateableOptions.width, height: updateableOptions.height });
165181

166182
const chartPlugins = createMemo(() => {
167-
return system.plugins.map((plugin) =>
183+
const plugins = system.plugins.map((plugin) =>
168184
typeof plugin === "function" ? plugin({ bus: system.pluginBus }) : plugin,
169185
);
186+
187+
// Add internal cursor move plugin if callback is provided
188+
if (local.onCursorMove) {
189+
plugins.push(createCursorMovePlugin(local.onCursorMove));
190+
}
191+
192+
return plugins;
170193
});
171194

172195
createEffect(() => {
@@ -229,9 +252,13 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
229252
});
230253
});
231254

255+
const classes = () => (local.class ? `solid-uplot ${local.class}` : "solid-uplot");
256+
232257
return (
233258
<div
234259
id="solid-uplot-root"
260+
ref={mergeRefs(local.ref, (el) => (container = el))}
261+
class={classes()}
235262
style={{
236263
display: "flex",
237264
"flex-direction": local.childrenPlacement === "top" ? "column" : "column-reverse",
@@ -244,10 +271,6 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
244271
}),
245272
...local.style,
246273
}}
247-
ref={(el) => {
248-
container = el;
249-
local.ref?.(el);
250-
}}
251274
>
252275
{local.children}
253276
</div>

src/eventPlugins.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { type CursorData, getCursorData, getSeriesData, type SeriesDatum } from "./utils";
2+
3+
export type OnCursorMoveParams = {
4+
/** The uPlot instance */
5+
readonly u: uPlot;
6+
/** The cursor data */
7+
readonly cursor: CursorData;
8+
/** Array of series data extracted from the chart configuration */
9+
readonly seriesData: SeriesDatum[];
10+
};
11+
12+
/**
13+
* Internal plugin factory for cursor move detection
14+
* @internal
15+
*/
16+
export const createCursorMovePlugin = (
17+
onCursorMove: (params: OnCursorMoveParams) => void,
18+
): uPlot.Plugin => {
19+
return {
20+
hooks: {
21+
setCursor: (u: uPlot) => {
22+
const cursor = getCursorData(u);
23+
if (!cursor) return;
24+
onCursorMove?.({ u, cursor, seriesData: getSeriesData(u) });
25+
},
26+
},
27+
};
28+
};

0 commit comments

Comments
 (0)