Skip to content

Commit 831772c

Browse files
committed
feat: adds onCursorMove callback
1 parent 355a3f4 commit 831772c

File tree

4 files changed

+97
-36
lines changed

4 files changed

+97
-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: 60 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 */
@@ -58,40 +60,55 @@ type OnCreateMeta = {
5860
};
5961

6062
/**
61-
* Props for the SolidUplot component
62-
*
63-
* @template T - The type of the plugin bus data structure
63+
* Events that can be passed to the SolidUplot component
6464
*/
65-
type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> & {
66-
/** Ref callback to access the chart container element */
67-
readonly ref?: (el: HTMLDivElement) => void;
65+
type SolidUplotEvents = {
6866
/** Callback fired when the uPlot instance is created */
6967
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;
68+
/** Callback fired when the cursor moves */
69+
readonly onCursorMove?: (params: OnCursorMoveParams) => void;
9370
};
9471

72+
/**
73+
* Props for the SolidUplot component
74+
*
75+
* @template T - The type of the plugin bus data structure
76+
*/
77+
type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> &
78+
SolidUplotEvents & {
79+
/** Class name for the chart container */
80+
readonly class?: string;
81+
82+
/** CSS styles for the chart container (position is managed internally) */
83+
readonly style?: Omit<JSX.CSSProperties, "position">;
84+
85+
/** Ref callback to access the chart container element */
86+
readonly ref?: Ref<HTMLDivElement>;
87+
88+
/**
89+
* Enable automatic resizing to fit container.
90+
*
91+
* When true:
92+
* - Chart uses width/height props for initial render
93+
* - Then automatically adapts to container size changes
94+
* - If no width/height provided, uses sensible defaults (600x300)
95+
*
96+
* @default false
97+
*/
98+
readonly autoResize?: boolean;
99+
100+
/**
101+
* Whether to reset scales when chart data is updated
102+
* @default true
103+
*/
104+
readonly resetScales?: boolean;
105+
/**
106+
* Where to place children components relative to the chart
107+
* @default "top"
108+
*/
109+
readonly childrenPlacement?: ChildrenPlacement;
110+
};
111+
95112
/**
96113
* A SolidJS wrapper component for uPlot charts with enhanced features
97114
*
@@ -146,8 +163,10 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
146163
const [local, options] = splitProps(_props, [
147164
"children",
148165
"childrenPlacement",
166+
"class",
149167
"autoResize",
150168
"onCreate",
169+
"onCursorMove",
151170
"style",
152171
"ref",
153172
]);
@@ -164,9 +183,16 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
164183
const size = () => ({ width: updateableOptions.width, height: updateableOptions.height });
165184

166185
const chartPlugins = createMemo(() => {
167-
return system.plugins.map((plugin) =>
186+
const plugins = system.plugins.map((plugin) =>
168187
typeof plugin === "function" ? plugin({ bus: system.pluginBus }) : plugin,
169188
);
189+
190+
// Add internal cursor move plugin if callback is provided
191+
if (local.onCursorMove) {
192+
plugins.push(createCursorMovePlugin(local.onCursorMove));
193+
}
194+
195+
return plugins;
170196
});
171197

172198
createEffect(() => {
@@ -229,9 +255,13 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
229255
});
230256
});
231257

258+
const classes = () => (local.class ? `solid-uplot ${local.class}` : "solid-uplot");
259+
232260
return (
233261
<div
234262
id="solid-uplot-root"
263+
ref={mergeRefs(local.ref, (el) => (container = el))}
264+
class={classes()}
235265
style={{
236266
display: "flex",
237267
"flex-direction": local.childrenPlacement === "top" ? "column" : "column-reverse",
@@ -244,10 +274,6 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
244274
}),
245275
...local.style,
246276
}}
247-
ref={(el) => {
248-
container = el;
249-
local.ref?.(el);
250-
}}
251277
>
252278
{local.children}
253279
</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)