Skip to content

Commit 503d2a5

Browse files
committed
fix: exports missing UplotPluginFactory type for plugin authoring
1 parent 58fa463 commit 503d2a5

File tree

8 files changed

+171
-61
lines changed

8 files changed

+171
-61
lines changed

README.md

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ Core components and plugin system:
4242

4343
```tsx
4444
import { SolidUplot, createPluginBus } from "@dschz/solid-uplot";
45-
import type { PluginFactory, SolidUplotPluginBus } from "@dschz/solid-uplot";
45+
import type {
46+
SolidUplotPluginBus,
47+
UplotPluginFactory,
48+
UplotPluginFactoryContext,
49+
} from "@dschz/solid-uplot";
4650
```
4751

4852
### `@dschz/solid-uplot/plugins`
@@ -376,7 +380,7 @@ const MyChart = () => {
376380
The plugin system is open to extension. When authoring plugins for public consumption, follow this pattern:
377381

378382
```tsx
379-
import type { PluginFactory } from "@dschz/solid-uplot";
383+
import type { UplotPluginFactory } from "@dschz/solid-uplot";
380384
import type { CursorPluginMessageBus } from "@dschz/solid-uplot/plugins";
381385

382386
// 1. Define your plugin's message type
@@ -393,7 +397,7 @@ export type MyPluginMessageBus = {
393397
// 3. Export your plugin factory
394398
export const myPlugin = (
395399
options = {},
396-
): PluginFactory<CursorPluginMessageBus & MyPluginMessageBus> => {
400+
): UplotPluginFactory<CursorPluginMessageBus & MyPluginMessageBus> => {
397401
return ({ bus }) => {
398402
if (!bus) {
399403
console.warn("[my-plugin]: A plugin bus is required");
@@ -457,73 +461,67 @@ const MyDashboard = () => {
457461
### SolidUplot Component
458462

459463
```tsx
460-
type SolidUplotProps<T extends VoidStruct = VoidStruct> = {
461-
// Chart data (required)
462-
data: uPlot.AlignedData;
464+
// Main component props (extends all uPlot.Options except plugins, width, height)
465+
type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> & {
466+
// Ref callback to access the chart container element
467+
ref?: (el: HTMLDivElement) => void;
468+
469+
// Callback fired when the uPlot instance is created
470+
onCreate?: (u: uPlot, meta: { seriesData: SeriesDatum[] }) => void;
471+
472+
// Whether to reset scales when chart data is updated (default: true)
473+
resetScales?: boolean;
463474

475+
// CSS styles for the chart container (position is managed internally)
476+
style?: Omit<JSX.CSSProperties, "position">;
477+
478+
// Where to place children components relative to the chart (default: "top")
479+
childrenPlacement?: "top" | "bottom";
480+
481+
// Enable automatic resizing to fit container (default: false)
482+
autoResize?: boolean;
483+
};
484+
485+
// Configuration options extending uPlot.Options with SolidJS enhancements
486+
type SolidUplotOptions<T extends VoidStruct = VoidStruct> = Omit<
487+
uPlot.Options,
488+
"plugins" | "width" | "height"
489+
> & {
464490
// Chart dimensions
465491
width?: number; // default: 600
466492
height?: number; // default: 300
467493

468-
// Responsive sizing
469-
autoResize?: boolean; // default: false
470-
471494
// Plugin configuration
472495
plugins?: SolidUplotPlugin<T>[];
473496
pluginBus?: SolidUplotPluginBus<T>;
474-
475-
// Chart options (all uPlot.Options except plugins, width, height)
476-
series?: uPlot.Series[];
477-
scales?: uPlot.Scales;
478-
axes?: uPlot.Axis[];
479-
// ... other uPlot options
480-
481-
// Chart behavior
482-
resetScales?: boolean; // default: true
483-
484-
// Callbacks
485-
onCreate?: (u: uPlot, meta: { seriesData: SeriesDatum[] }) => void;
486-
487-
// Container styling
488-
style?: JSX.CSSProperties;
489-
class?: string;
490-
id?: string;
491-
ref?: (el: HTMLDivElement) => void;
492-
493-
// Children placement
494-
childrenPlacement?: "top" | "bottom"; // default: "top"
495497
};
498+
499+
// Plugin type (can be standard uPlot plugin or factory function)
500+
type SolidUplotPlugin<T extends VoidStruct = VoidStruct> = uPlot.Plugin | UplotPluginFactory<T>;
496501
```
497502

498503
### Plugin Bus
499504

500505
```tsx
501-
type SolidUplotPluginBus<T extends VoidStruct = VoidStruct> = {
502-
data: T;
503-
setData: <K extends keyof T>(key: K, value: T[K]) => void;
504-
setData: <K extends keyof T, P extends keyof T[K]>(
505-
key: K,
506-
path: P,
507-
value: T[K][P]
508-
) => void;
509-
};
506+
// Plugin bus type (derived from createPluginBus return type)
507+
type SolidUplotPluginBus<T extends VoidStruct = VoidStruct> = ReturnType<typeof createPluginBus<T>>;
510508

511509
// Create a plugin bus
512-
const createPluginBus = <T extends VoidStruct = VoidStruct>(
513-
initialData?: Partial<T>
514-
): SolidUplotPluginBus<T>;
510+
const createPluginBus: <T extends VoidStruct = VoidStruct>(
511+
initialData?: T,
512+
) => SolidUplotPluginBus<T>;
515513
```
516514

517515
### Built-in Plugin Options
518516

519517
```tsx
520518
// Cursor Plugin
521-
const cursor = (): PluginFactory<CursorPluginMessageBus>;
519+
const cursor = (): UplotPluginFactory<CursorPluginMessageBus>;
522520

523521
// Focus Series Plugin
524522
const focusSeries = (options?: {
525523
pxThreshold?: number; // default: 15
526-
}): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
524+
}): UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
527525

528526
// Tooltip Plugin
529527
const tooltip = (
@@ -535,7 +533,7 @@ const tooltip = (
535533
style?: JSX.CSSProperties;
536534
zIndex?: number; // default: 20
537535
}
538-
): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
536+
): UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
539537

540538
// Legend Plugin
541539
const legend = (
@@ -548,7 +546,7 @@ const legend = (
548546
style?: JSX.CSSProperties;
549547
zIndex?: number; // default: 10
550548
}
551-
): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
549+
): UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
552550
```
553551

554552
## 📚 Examples

src/SolidUplot.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from "solid-js";
1414
import uPlot from "uplot";
1515

16-
import type { PluginFactory, SolidUplotPluginBus, VoidStruct } from "./createPluginBus";
16+
import type { SolidUplotPluginBus, UplotPluginFactory, VoidStruct } from "./createPluginBus";
1717
import { getSeriesData, type SeriesDatum } from "./utils/getSeriesData";
1818

1919
/** Placement options for children components relative to the chart */
@@ -25,7 +25,9 @@ type ChildrenPlacement = "top" | "bottom";
2525
*
2626
* @template T - The type of the plugin bus data structure
2727
*/
28-
export type SolidUplotPlugin<T extends VoidStruct = VoidStruct> = uPlot.Plugin | PluginFactory<T>;
28+
export type SolidUplotPlugin<T extends VoidStruct = VoidStruct> =
29+
| uPlot.Plugin
30+
| UplotPluginFactory<T>;
2931

3032
/**
3133
* Configuration options for the SolidUplot component, extending uPlot.Options

src/createPluginBus.tsx

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
import { createStore, type SetStoreFunction, type Store } from "solid-js/store";
22

3+
/** Base constraint for plugin bus data structures */
34
export type VoidStruct = Record<string, unknown>;
45

6+
/**
7+
* Internal type representing the plugin store structure
8+
* @internal
9+
*/
510
type PluginStore<T> = {
611
readonly data: Store<T>;
712
readonly setData: SetStoreFunction<T>;
813
};
914

1015
/**
11-
* Represents a reactive store shared across SolidUplot plugins.
16+
* Creates a reactive store shared across SolidUplot plugins.
1217
*
1318
* This store acts as a message bus, allowing plugins to publish and
1419
* subscribe to typed messages based on agreed-upon keys (e.g., `tooltip`, `highlight`).
20+
*
21+
* @template T - The type of the plugin bus data structure
22+
* @param initialData - Initial data for the plugin bus
23+
* @returns A reactive store with data and setData functions
24+
*
25+
* @example
26+
* ```tsx
27+
* type MyBusData = {
28+
* tooltip?: { x: number; y: number; visible: boolean };
29+
* highlight?: { seriesIndex: number };
30+
* };
31+
*
32+
* const bus = createPluginBus<MyBusData>();
33+
* ```
1534
*/
1635
export const createPluginBus = <T extends VoidStruct = VoidStruct>(
1736
initialData: T = {} as T,
@@ -20,14 +39,100 @@ export const createPluginBus = <T extends VoidStruct = VoidStruct>(
2039
return { data, setData };
2140
};
2241

42+
/**
43+
* Type representing a SolidUplot plugin bus instance
44+
*
45+
* @template T - The type of the plugin bus data structure
46+
*/
2347
export type SolidUplotPluginBus<T extends VoidStruct = VoidStruct> = ReturnType<
2448
typeof createPluginBus<T>
2549
>;
2650

27-
export type PluginFactoryContext<T extends VoidStruct = VoidStruct> = {
51+
/**
52+
* Context object passed to plugin factory functions
53+
*
54+
* @template T - The type of the plugin bus data structure
55+
*/
56+
export type UplotPluginFactoryContext<T extends VoidStruct = VoidStruct> = {
57+
/** The plugin communication bus for sharing data between plugins */
2858
readonly bus?: SolidUplotPluginBus<T>;
2959
};
3060

31-
export type PluginFactory<T extends VoidStruct = VoidStruct> = (
32-
ctx: PluginFactoryContext<T>,
61+
/**
62+
* Factory function type for creating uPlot plugins with access to the SolidUplot plugin bus
63+
*
64+
* This allows plugins to communicate with each other and share state reactively through
65+
* the SolidJS store system. Plugin factories receive a context object containing the
66+
* plugin bus and return a standard uPlot plugin.
67+
*
68+
* When authoring plugins for public consumption, follow this pattern:
69+
* 1. Define your plugin's message type
70+
* 2. Define your plugin's message bus type
71+
* 3. Export your plugin factory with proper type constraints
72+
* 4. Include bus validation and warning messages
73+
*
74+
* @template T - The type of the plugin bus data structure for type-safe communication
75+
*
76+
* @param ctx - Context object containing the plugin bus and other shared resources
77+
* @returns A standard uPlot plugin object with hooks
78+
*
79+
* @example
80+
* ```tsx
81+
* import type { UplotPluginFactory } from '@dschz/solid-uplot';
82+
* import type { CursorPluginMessageBus } from '@dschz/solid-uplot/plugins';
83+
*
84+
* // 1. Define your plugin's message type
85+
* export type MyPluginMessage = {
86+
* value: number;
87+
* timestamp: number;
88+
* };
89+
*
90+
* // 2. Define your plugin's message bus type
91+
* export type MyPluginMessageBus = {
92+
* myPlugin?: MyPluginMessage;
93+
* };
94+
*
95+
* // 3. Export your plugin factory with proper type constraints
96+
* export const myPlugin = (
97+
* options = {},
98+
* ): UplotPluginFactory<CursorPluginMessageBus & MyPluginMessageBus> => {
99+
* return ({ bus }) => {
100+
* // 4. Include bus validation and warning
101+
* if (!bus) {
102+
* console.warn("[my-plugin]: A plugin bus is required");
103+
* return { hooks: {} };
104+
* }
105+
*
106+
* return {
107+
* hooks: {
108+
* ready: (u) => {
109+
* // Initialize plugin state
110+
* bus.setData("myPlugin", {
111+
* value: 0,
112+
* timestamp: Date.now(),
113+
* });
114+
* },
115+
* setData: (u) => {
116+
* // Update plugin state reactively
117+
* bus.setData("myPlugin", "value", (prev) => prev + 1);
118+
* },
119+
* },
120+
* };
121+
* };
122+
* };
123+
*
124+
* // Usage with proper typing
125+
* const bus = createPluginBus<CursorPluginMessageBus & MyPluginMessageBus>();
126+
*
127+
* <SolidUplot
128+
* data={data}
129+
* pluginBus={bus}
130+
* plugins={[cursor(), myPlugin()]}
131+
* />
132+
* ```
133+
*
134+
* @see {@link https://github.com/leeoniya/uPlot/tree/master/docs#plugins} uPlot Plugin Documentation
135+
*/
136+
export type UplotPluginFactory<T extends VoidStruct = VoidStruct> = (
137+
ctx: UplotPluginFactoryContext<T>,
33138
) => uPlot.Plugin;

src/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1-
export { createPluginBus } from "./createPluginBus";
1+
export {
2+
createPluginBus,
3+
type SolidUplotPluginBus,
4+
type UplotPluginFactory,
5+
type UplotPluginFactoryContext,
6+
} from "./createPluginBus";
27
export { SolidUplot } from "./SolidUplot";

src/plugins/cursor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type uPlot from "uplot";
22

3-
import type { PluginFactory } from "../createPluginBus";
3+
import type { UplotPluginFactory } from "../createPluginBus";
44
import { type CursorData, getCursorData } from "../utils/getCursorData";
55

66
/**
@@ -75,7 +75,7 @@ export type CursorPluginMessageBus = {
7575
* ];
7676
* ```
7777
*/
78-
export const cursor = (): PluginFactory<CursorPluginMessageBus> => {
78+
export const cursor = (): UplotPluginFactory<CursorPluginMessageBus> => {
7979
return ({ bus }) => {
8080
if (!bus) {
8181
console.warn("[solid-uplot]: A plugin bus is required for the cursor plugin");

src/plugins/focusSeries.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createEffect, createRoot } from "solid-js";
22

3-
import type { PluginFactory } from "../createPluginBus";
3+
import type { UplotPluginFactory } from "../createPluginBus";
44
import type { CursorPluginMessageBus } from "./cursor";
55

66
const DEFAULT_UNFOCUSED_ALPHA = 0.1 as const;
@@ -198,7 +198,7 @@ const seriesFocusRedraw = (u: uPlot, options: SeriesFocusRedrawOptions = {}) =>
198198
*/
199199
export const focusSeries = (
200200
options: FocusSeriesPluginOptions = {},
201-
): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus> => {
201+
): UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus> => {
202202
return ({ bus }) => {
203203
if (!bus) {
204204
console.warn("[solid-uplot]: A plugin bus is required for the focusSeries plugin");

src/plugins/legend.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type Component, type JSX, mergeProps, splitProps } from "solid-js";
22
import { render } from "solid-js/web";
33
import uPlot from "uplot";
44

5-
import type { PluginFactory, SolidUplotPluginBus } from "../createPluginBus";
5+
import type { UplotPluginFactory, SolidUplotPluginBus } from "../createPluginBus";
66
import { getSeriesData, type SeriesDatum } from "../utils/getSeriesData";
77
import type { CursorPluginMessageBus } from "./cursor";
88
import type { FocusSeriesPluginMessageBus } from "./focusSeries";
@@ -84,7 +84,7 @@ type LegendPluginOptions = LegendRootProps & LegendConfigOptions;
8484
export const legend = (
8585
Component: Component<LegendProps>,
8686
options: LegendPluginOptions = {},
87-
): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus> => {
87+
): UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus> => {
8888
return ({ bus }) => {
8989
if (!bus) {
9090
console.warn("[solid-uplot]: A plugin bus is required for the Legend plugin");

src/plugins/tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type Component, type JSX, mergeProps, Show, splitProps } from "solid-js
22
import { render } from "solid-js/web";
33
import uPlot from "uplot";
44

5-
import type { PluginFactory } from "../createPluginBus";
5+
import type { UplotPluginFactory } from "../createPluginBus";
66
import type { CursorData } from "../utils/getCursorData";
77
import { getSeriesData, type SeriesDatum } from "../utils/getSeriesData";
88
import type { CursorPluginMessageBus } from "./cursor";
@@ -224,7 +224,7 @@ export type TooltipPluginOptions = TooltipRootProps & TooltipConfigOptions;
224224
export const tooltip = (
225225
Component: Component<TooltipProps>,
226226
options: TooltipPluginOptions = {},
227-
): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus> => {
227+
): UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus> => {
228228
return ({ bus }) => {
229229
if (!bus) {
230230
console.warn("[solid-uplot]: A plugin bus is required for the tooltip plugin");

0 commit comments

Comments
 (0)