@dschz/solid-uplot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,771 @@
1
+ <p align="center">
2
+ <img src="https://assets.solidjs.com/banner?project=solid-uplot&type=Ecosystem&background=tiles" alt="@dschz/solid-uplot banner" />
3
+ </p>
4
+
5
+ # @dschz/solid-uplot
6
+
7
+ [![SolidJS](https://img.shields.io/badge/SolidJS-≥1.6.0-2c4f7c?logo=solid&logoColor=c8c9cb)](https://www.solidjs.com)
8
+ [![uPlot](https://img.shields.io/badge/uPlot-%3E%3D1.6.32-orange)](https://github.com/leeoniya/uPlot)
9
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.8+-blue)](https://www.typescriptlang.org)
11
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@dschz/solid-uplot)](https://bundlephobia.com/package/@dschz/solid-uplot)
12
+ [![npm](https://img.shields.io/npm/v/@dschz/solid-uplot)](https://www.npmjs.com/package/@dschz/solid-uplot)
13
+
14
+ > 💹 SolidJS wrapper for [uPlot](https://github.com/leeoniya/uPlot) — an ultra-fast, small footprint charting library for time-series data.
15
+
16
+ ## ✨ Features
17
+
18
+ - ✅ Fully reactive SolidJS wrapper around uPlot
19
+ - 🔌 Plugin system support with inter-plugin communication
20
+ - 🎯 Fine-grained control over chart lifecycle
21
+ - 💡 Lightweight and fast
22
+ - 💻 TypeScript support out of the box
23
+ - 🎨 Built-in plugins for tooltips, legends, cursor tracking, and series focusing
24
+ - 📱 Responsive sizing support with auto-resize capabilities
25
+
26
+ ## 📦 Installation
27
+
28
+ ```bash
29
+ npm install solid-js uplot @dschz/solid-uplot
30
+ pnpm install solid-js uplot @dschz/solid-uplot
31
+ yarn install solid-js uplot @dschz/solid-uplot
32
+ bun install solid-js uplot @dschz/solid-uplot
33
+ ```
34
+
35
+ ## 📁 Package Structure
36
+
37
+ This package provides three main export paths for different functionality:
38
+
39
+ ### `@dschz/solid-uplot`
40
+
41
+ Core components and plugin system:
42
+
43
+ ```tsx
44
+ import { SolidUplot, createPluginBus } from "@dschz/solid-uplot";
45
+ import type { PluginFactory, SolidUplotPluginBus } from "@dschz/solid-uplot";
46
+ ```
47
+
48
+ ### `@dschz/solid-uplot/plugins`
49
+
50
+ This export path provides four plugins (three of which can be considered primitives).
51
+
52
+ - `cursor`: transmits cursor position data
53
+ - `focusSeries`: transmits which series are visually emphasized
54
+ - `tooltip`: plugin that allows you to present a custom JSX tooltip around the cursor
55
+ - `legend`: plugin that allows you to present a custom JSX component as your legend over the canvas drawing area.
56
+
57
+ ```tsx
58
+ import { cursor, tooltip, legend, focusSeries } from "@dschz/solid-uplot/plugins";
59
+ import type {
60
+ CursorPluginMessageBus,
61
+ FocusSeriesPluginMessageBus,
62
+ TooltipProps,
63
+ LegendProps,
64
+ } from "@dschz/solid-uplot/plugins";
65
+ ```
66
+
67
+ ### `@dschz/solid-uplot/utils`
68
+
69
+ Some convenience utility functions for getting certain bits of data from a `uPlot` instance (except for `getColorString` which translates a series' `stroke` or `fill` into a color value).
70
+
71
+ ```tsx
72
+ import {
73
+ getSeriesData,
74
+ getCursorData,
75
+ getColorString,
76
+ getNewCalendarDayIndices,
77
+ } from "@dschz/solid-uplot/utils";
78
+ import type { SeriesDatum, CursorData } from "@dschz/solid-uplot/utils";
79
+ ```
80
+
81
+ ## 🚀 Quick Start
82
+
83
+ ```tsx
84
+ import { SolidUplot, createPluginBus } from "@dschz/solid-uplot";
85
+ import { cursor, tooltip, legend } from "@dschz/solid-uplot/plugins";
86
+ import type { CursorPluginMessageBus, TooltipProps, LegendProps } from "@dschz/solid-uplot/plugins";
87
+
88
+ // Create a tooltip component
89
+ const MyTooltip = (props: TooltipProps) => (
90
+ <div style={{ background: "white", padding: "8px", border: "1px solid #ccc" }}>
91
+ <div>X: {props.cursor.xValue}</div>
92
+ <For each={props.seriesData}>
93
+ {(series) => {
94
+ const value = props.u.data[series.seriesIdx]?.[props.cursor.idx];
95
+ return (
96
+ <div>
97
+ {series.label}: {value}
98
+ </div>
99
+ );
100
+ }}
101
+ </For>
102
+ </div>
103
+ );
104
+
105
+ // Create a legend component
106
+ const MyLegend = (props: LegendProps) => (
107
+ <div style={{ background: "rgba(255,255,255,0.9)", padding: "8px" }}>
108
+ <For each={props.seriesData}>
109
+ {(series) => (
110
+ <div style={{ display: "flex", "align-items": "center", gap: "4px" }}>
111
+ <div
112
+ style={{
113
+ width: "12px",
114
+ height: "12px",
115
+ background: series.stroke,
116
+ }}
117
+ />
118
+ <span>{series.label}</span>
119
+ </div>
120
+ )}
121
+ </For>
122
+ </div>
123
+ );
124
+
125
+ const MyChart = () => {
126
+ const bus = createPluginBus<CursorPluginMessageBus>();
127
+
128
+ return (
129
+ <SolidUplot
130
+ data={[
131
+ [0, 1, 2, 3], // x values
132
+ [10, 20, 30, 40], // y values for series 1
133
+ [15, 25, 35, 45], // y values for series 2
134
+ ]}
135
+ width={600}
136
+ height={400}
137
+ series={[
138
+ {},
139
+ { label: "Series 1", stroke: "#1f77b4" },
140
+ { label: "Series 2", stroke: "#ff7f0e" },
141
+ ]}
142
+ plugins={[
143
+ cursor(),
144
+ tooltip(MyTooltip),
145
+ legend(MyLegend, { placement: "top-right", pxOffset: 12 }),
146
+ ]}
147
+ pluginBus={bus}
148
+ />
149
+ );
150
+ };
151
+ ```
152
+
153
+ ## 📏 Responsive Sizing
154
+
155
+ For responsive charts that automatically adapt to container size changes, use the `autoResize` prop:
156
+
157
+ ```tsx
158
+ <div style={{ width: "100%", height: "400px" }}>
159
+ <SolidUplot
160
+ autoResize={true}
161
+ data={data}
162
+ series={series}
163
+ // Chart will automatically resize to fill the container
164
+ />
165
+ </div>
166
+ ```
167
+
168
+ For more advanced responsive patterns, you can pair this library with [@dschz/solid-auto-sizer](https://github.com/dsnchz/solid-auto-sizer):
169
+
170
+ ```bash
171
+ npm install @dschz/solid-auto-sizer
172
+ pnpm install @dschz/solid-auto-sizer
173
+ yarn install @dschz/solid-auto-sizer
174
+ bun install @dschz/solid-auto-sizer
175
+ ```
176
+
177
+ ```tsx
178
+ import { AutoSizer } from "@dschz/solid-auto-sizer";
179
+
180
+ <AutoSizer>
181
+ {({ width, height }) => <SolidUplot width={width} height={height} data={data} />}
182
+ </AutoSizer>;
183
+ ```
184
+
185
+ ## 🔌 Enhanced Plugin System
186
+
187
+ The cornerstone feature of `SolidUplot` is its refined plugin system that enables extensible functionality and inter-plugin communication through a reactive message bus.
188
+
189
+ ### Plugin Bus Architecture
190
+
191
+ The Plugin Bus System enables plugins to communicate with each other and external components through a reactive store. This architecture provides:
192
+
193
+ - **Type-safe communication**: All plugin messages are fully typed
194
+ - **Reactive updates**: Changes in plugin state automatically trigger updates
195
+ - **Decoupled components**: Plugins can interact without direct dependencies
196
+ - **Extensible**: Easy to add new plugins that integrate with existing ones
197
+
198
+ ### Built-in Plugins
199
+
200
+ #### Cursor Plugin
201
+
202
+ Tracks cursor position and interaction state across charts:
203
+
204
+ ```tsx
205
+ import { cursor } from "@dschz/solid-uplot/plugins";
206
+ import type { CursorPluginMessageBus } from "@dschz/solid-uplot/plugins";
207
+
208
+ const cursorPlugin = cursor();
209
+ ```
210
+
211
+ The cursor plugin provides cursor position data that other plugins can consume through the bus.
212
+
213
+ #### Focus Series Plugin
214
+
215
+ Highlights series based on cursor proximity:
216
+
217
+ ```tsx
218
+ import { focusSeries } from "@dschz/solid-uplot/plugins";
219
+ import type { FocusSeriesPluginMessageBus } from "@dschz/solid-uplot/plugins";
220
+
221
+ const focusPlugin = focusSeries({
222
+ pxThreshold: 15, // Distance threshold for focusing (default: 15)
223
+ });
224
+ ```
225
+
226
+ #### Tooltip Plugin
227
+
228
+ Renders custom tooltips with automatic positioning and overflow handling:
229
+
230
+ ```tsx
231
+ import { tooltip } from "@dschz/solid-uplot/plugins";
232
+ import type { TooltipProps } from "@dschz/solid-uplot/plugins";
233
+
234
+ const MyTooltip: Component<TooltipProps> = (props) => {
235
+ return (
236
+ <div
237
+ style={{
238
+ background: "white",
239
+ border: "1px solid #ccc",
240
+ padding: "8px",
241
+ "border-radius": "4px",
242
+ "box-shadow": "0 2px 4px rgba(0,0,0,0.1)",
243
+ }}
244
+ >
245
+ <div style={{ "font-weight": "bold", "margin-bottom": "8px" }}>X: {props.cursor.xValue}</div>
246
+ <For each={props.seriesData}>
247
+ {(series) => {
248
+ const value = () => props.u.data[series.seriesIdx]?.[props.cursor.idx];
249
+ return (
250
+ <Show when={series.visible}>
251
+ <div style={{ display: "flex", "align-items": "center", "margin-bottom": "4px" }}>
252
+ <div
253
+ style={{
254
+ width: "10px",
255
+ height: "10px",
256
+ "border-radius": "50%",
257
+ "background-color": series.stroke,
258
+ "margin-right": "6px",
259
+ }}
260
+ />
261
+ <span style={{ color: series.stroke }}>
262
+ {series.label}: {value()?.toFixed(2)}
263
+ </span>
264
+ </div>
265
+ </Show>
266
+ );
267
+ }}
268
+ </For>
269
+ </div>
270
+ );
271
+ };
272
+
273
+ const tooltipPlugin = tooltip(MyTooltip, {
274
+ placement: "top-left", // "top-left" | "top-right" | "bottom-left" | "bottom-right"
275
+ zIndex: 20,
276
+ });
277
+ ```
278
+
279
+ #### Legend Plugin
280
+
281
+ Adds customizable legends with smart positioning and interactive features:
282
+
283
+ ```tsx
284
+ import { legend } from "@dschz/solid-uplot/plugins";
285
+ import type { LegendProps } from "@dschz/solid-uplot/plugins";
286
+
287
+ const MyLegend: Component<LegendProps> = (props) => {
288
+ // Access cursor data for interactive features
289
+ const cursorVisible = () => props.bus.data.cursor?.state[props.u.root.id]?.visible;
290
+
291
+ return (
292
+ <div
293
+ style={{
294
+ background: "white",
295
+ border: "1px solid #ddd",
296
+ "border-radius": "4px",
297
+ padding: "8px",
298
+ "box-shadow": "0 2px 4px rgba(0,0,0,0.1)",
299
+ // Dim when tooltip is active
300
+ opacity: cursorVisible() ? 0.6 : 1,
301
+ transition: "opacity 200ms",
302
+ }}
303
+ >
304
+ <div style={{ "font-weight": "bold", "margin-bottom": "8px" }}>Legend</div>
305
+ <For each={props.seriesData}>
306
+ {(series) => (
307
+ <Show when={series.visible}>
308
+ <div
309
+ style={{
310
+ display: "flex",
311
+ "align-items": "center",
312
+ gap: "6px",
313
+ "margin-bottom": "4px",
314
+ }}
315
+ >
316
+ <div
317
+ style={{
318
+ width: "12px",
319
+ height: "12px",
320
+ "background-color": series.stroke,
321
+ "border-radius": "2px",
322
+ }}
323
+ />
324
+ <span style={{ "font-size": "14px" }}>{series.label}</span>
325
+ </div>
326
+ </Show>
327
+ )}
328
+ </For>
329
+ </div>
330
+ );
331
+ };
332
+
333
+ const legendPlugin = legend(MyLegend, {
334
+ placement: "top-left", // "top-left" | "top-right"
335
+ pxOffset: 8, // Distance from chart edges (default: 8)
336
+ zIndex: 10,
337
+ });
338
+ ```
339
+
340
+ **Legend Plugin Features:**
341
+
342
+ - **Simple positioning**: Only top-left or top-right corners to avoid axis conflicts
343
+ - **Size-constrained**: Legend cannot exceed chart drawing area dimensions
344
+ - **Layout-agnostic**: You control internal layout and styling
345
+ - **Non-interfering**: Designed to work harmoniously with chart interactions
346
+ - **Plugin bus integration**: Access cursor and focus data for smart interactions
347
+ - **Automatic cleanup**: Proper memory management and DOM cleanup
348
+
349
+ ### Plugin Bus Type Safety
350
+
351
+ When using multiple plugins, ensure type safety by properly typing the plugin bus:
352
+
353
+ ```tsx
354
+ import { createPluginBus } from "@dschz/solid-uplot";
355
+ import type {
356
+ CursorPluginMessageBus,
357
+ FocusSeriesPluginMessageBus,
358
+ } from "@dschz/solid-uplot/plugins";
359
+
360
+ // Create a bus that includes all plugin message types
361
+ const bus = createPluginBus<CursorPluginMessageBus & FocusSeriesPluginMessageBus>();
362
+
363
+ const MyChart = () => {
364
+ return (
365
+ <SolidUplot
366
+ data={data}
367
+ pluginBus={bus}
368
+ plugins={[cursor(), focusSeries(), tooltip(MyTooltip), legend(MyLegend)]}
369
+ />
370
+ );
371
+ };
372
+ ```
373
+
374
+ ### Creating Custom Plugins
375
+
376
+ The plugin system is open to extension. When authoring plugins for public consumption, follow this pattern:
377
+
378
+ ```tsx
379
+ import type { PluginFactory } from "@dschz/solid-uplot";
380
+ import type { CursorPluginMessageBus } from "@dschz/solid-uplot/plugins";
381
+
382
+ // 1. Define your plugin's message type
383
+ export type MyPluginMessage = {
384
+ value: number;
385
+ timestamp: number;
386
+ };
387
+
388
+ // 2. Define your plugin's message bus
389
+ export type MyPluginMessageBus = {
390
+ myPlugin?: MyPluginMessage;
391
+ };
392
+
393
+ // 3. Export your plugin factory
394
+ export const myPlugin = (
395
+ options = {},
396
+ ): PluginFactory<CursorPluginMessageBus & MyPluginMessageBus> => {
397
+ return ({ bus }) => {
398
+ if (!bus) {
399
+ console.warn("[my-plugin]: A plugin bus is required");
400
+ return { hooks: {} };
401
+ }
402
+
403
+ return {
404
+ hooks: {
405
+ ready: (u) => {
406
+ // Initialize plugin state
407
+ bus.setData("myPlugin", {
408
+ value: 0,
409
+ timestamp: Date.now(),
410
+ });
411
+ },
412
+ setData: (u) => {
413
+ // Update plugin state
414
+ bus.setData("myPlugin", "value", (prev) => prev + 1);
415
+ },
416
+ },
417
+ };
418
+ };
419
+ };
420
+ ```
421
+
422
+ ### External Component Integration
423
+
424
+ The plugin bus enables powerful integrations between charts and external components:
425
+
426
+ ```tsx
427
+ import { createPluginBus } from "@dschz/solid-uplot";
428
+ import type { FocusSeriesPluginMessageBus } from "@dschz/solid-uplot/plugins";
429
+
430
+ const bus = createPluginBus<FocusSeriesPluginMessageBus>();
431
+
432
+ // External component that can trigger series focus
433
+ const DataGrid = (props: { bus: typeof bus }) => {
434
+ const handleRowHover = (seriesLabel: string) => {
435
+ props.bus.setData("focusSeries", {
436
+ sourceId: "data-grid",
437
+ targets: [{ label: seriesLabel }],
438
+ });
439
+ };
440
+
441
+ return <table>{/* Grid implementation */}</table>;
442
+ };
443
+
444
+ // Chart and grid interact through shared bus
445
+ const MyDashboard = () => {
446
+ return (
447
+ <div>
448
+ <DataGrid bus={bus} />
449
+ <SolidUplot data={data} pluginBus={bus} plugins={[focusSeries()]} />
450
+ </div>
451
+ );
452
+ };
453
+ ```
454
+
455
+ ## 🔧 API Reference
456
+
457
+ ### SolidUplot Component
458
+
459
+ ```tsx
460
+ type SolidUplotProps<T extends VoidStruct = VoidStruct> = {
461
+ // Chart data (required)
462
+ data: uPlot.AlignedData;
463
+
464
+ // Chart dimensions
465
+ width?: number; // default: 600
466
+ height?: number; // default: 300
467
+
468
+ // Responsive sizing
469
+ autoResize?: boolean; // default: false
470
+
471
+ // Plugin configuration
472
+ plugins?: SolidUplotPlugin<T>[];
473
+ 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"
495
+ };
496
+ ```
497
+
498
+ ### Plugin Bus
499
+
500
+ ```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
+ };
510
+
511
+ // Create a plugin bus
512
+ const createPluginBus = <T extends VoidStruct = VoidStruct>(
513
+ initialData?: Partial<T>
514
+ ): SolidUplotPluginBus<T>;
515
+ ```
516
+
517
+ ### Built-in Plugin Options
518
+
519
+ ```tsx
520
+ // Cursor Plugin
521
+ const cursor = (): PluginFactory<CursorPluginMessageBus>;
522
+
523
+ // Focus Series Plugin
524
+ const focusSeries = (options?: {
525
+ pxThreshold?: number; // default: 15
526
+ }): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
527
+
528
+ // Tooltip Plugin
529
+ const tooltip = (
530
+ Component: Component<TooltipProps>,
531
+ options?: {
532
+ placement?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
533
+ id?: string;
534
+ class?: string;
535
+ style?: JSX.CSSProperties;
536
+ zIndex?: number; // default: 20
537
+ }
538
+ ): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
539
+
540
+ // Legend Plugin
541
+ const legend = (
542
+ Component: Component<LegendProps>,
543
+ options?: {
544
+ placement?: "top-left" | "top-right"; // default: "top-left"
545
+ pxOffset?: number; // default: 8
546
+ id?: string;
547
+ class?: string;
548
+ style?: JSX.CSSProperties;
549
+ zIndex?: number; // default: 10
550
+ }
551
+ ): PluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
552
+ ```
553
+
554
+ ## 📚 Examples
555
+
556
+ ### Basic Chart
557
+
558
+ ```tsx
559
+ import { SolidUplot } from "@dschz/solid-uplot";
560
+
561
+ const BasicChart = () => {
562
+ return (
563
+ <SolidUplot
564
+ data={[
565
+ [0, 1, 2, 3],
566
+ [10, 20, 30, 40],
567
+ [15, 25, 35, 45],
568
+ ]}
569
+ width={600}
570
+ height={400}
571
+ scales={{
572
+ x: { time: false },
573
+ }}
574
+ series={[
575
+ {},
576
+ { label: "Series 1", stroke: "#1f77b4" },
577
+ { label: "Series 2", stroke: "#ff7f0e" },
578
+ ]}
579
+ />
580
+ );
581
+ };
582
+ ```
583
+
584
+ ### Chart with All Plugins
585
+
586
+ ```tsx
587
+ import { SolidUplot, createPluginBus } from "@dschz/solid-uplot";
588
+ import { cursor, tooltip, legend, focusSeries } from "@dschz/solid-uplot/plugins";
589
+ import type {
590
+ CursorPluginMessageBus,
591
+ FocusSeriesPluginMessageBus,
592
+ TooltipProps,
593
+ LegendProps,
594
+ } from "@dschz/solid-uplot/plugins";
595
+
596
+ const MyTooltip: Component<TooltipProps> = (props) => (
597
+ <div style={{ background: "white", padding: "8px", border: "1px solid #ccc" }}>
598
+ <div>Time: {new Date(props.cursor.xValue * 1000).toLocaleTimeString()}</div>
599
+ <For each={props.seriesData}>
600
+ {(series) => {
601
+ const value = props.u.data[series.seriesIdx]?.[props.cursor.idx];
602
+ return (
603
+ <div style={{ color: series.stroke }}>
604
+ {series.label}: {value?.toFixed(2)}
605
+ </div>
606
+ );
607
+ }}
608
+ </For>
609
+ </div>
610
+ );
611
+
612
+ const MyLegend: Component<LegendProps> = (props) => {
613
+ const cursorVisible = () => props.bus.data.cursor?.state[props.u.root.id]?.visible;
614
+
615
+ return (
616
+ <div
617
+ style={{
618
+ background: "white",
619
+ border: "1px solid #ddd",
620
+ padding: "8px",
621
+ opacity: cursorVisible() ? 0.6 : 1,
622
+ transition: "opacity 200ms",
623
+ }}
624
+ >
625
+ <For each={props.seriesData}>
626
+ {(series) => (
627
+ <div style={{ display: "flex", "align-items": "center", gap: "6px" }}>
628
+ <div
629
+ style={{
630
+ width: "12px",
631
+ height: "12px",
632
+ background: series.stroke,
633
+ }}
634
+ />
635
+ <span>{series.label}</span>
636
+ </div>
637
+ )}
638
+ </For>
639
+ </div>
640
+ );
641
+ };
642
+
643
+ const FullFeaturedChart = () => {
644
+ const bus = createPluginBus<CursorPluginMessageBus & FocusSeriesPluginMessageBus>();
645
+
646
+ return (
647
+ <SolidUplot
648
+ data={[
649
+ [0, 1, 2, 3, 4, 5],
650
+ [10, 20, 30, 40, 50, 60],
651
+ [15, 25, 35, 45, 55, 65],
652
+ [5, 15, 25, 35, 45, 55],
653
+ ]}
654
+ width={800}
655
+ height={500}
656
+ series={[
657
+ {},
658
+ { label: "Revenue", stroke: "#1f77b4" },
659
+ { label: "Profit", stroke: "#ff7f0e" },
660
+ { label: "Expenses", stroke: "#2ca02c" },
661
+ ]}
662
+ plugins={[
663
+ cursor(),
664
+ focusSeries({ pxThreshold: 20 }),
665
+ tooltip(MyTooltip, { placement: "top-right" }),
666
+ legend(MyLegend, { placement: "top-left", pxOffset: 12 }),
667
+ ]}
668
+ pluginBus={bus}
669
+ />
670
+ );
671
+ };
672
+ ```
673
+
674
+ ### Responsive Chart
675
+
676
+ ```tsx
677
+ const ResponsiveChart = () => {
678
+ return (
679
+ <div style={{ width: "100%", height: "400px", border: "1px solid #ccc" }}>
680
+ <SolidUplot
681
+ autoResize={true}
682
+ data={data}
683
+ series={series}
684
+ plugins={[cursor(), tooltip(MyTooltip)]}
685
+ />
686
+ </div>
687
+ );
688
+ };
689
+ ```
690
+
691
+ ### External Integration
692
+
693
+ ```tsx
694
+ const Dashboard = () => {
695
+ const bus = createPluginBus<FocusSeriesPluginMessageBus>();
696
+
697
+ const handleSeriesToggle = (seriesLabel: string) => {
698
+ bus.setData("focusSeries", {
699
+ sourceId: "external-control",
700
+ targets: [{ label: seriesLabel }],
701
+ });
702
+ };
703
+
704
+ return (
705
+ <div>
706
+ <div>
707
+ <button onClick={() => handleSeriesToggle("Series 1")}>Focus Series 1</button>
708
+ <button onClick={() => handleSeriesToggle("Series 2")}>Focus Series 2</button>
709
+ </div>
710
+ <SolidUplot data={data} plugins={[focusSeries()]} pluginBus={bus} />
711
+ </div>
712
+ );
713
+ };
714
+ ```
715
+
716
+ ## 🎮 Interactive Playground
717
+
718
+ This library includes a comprehensive playground application that demonstrates all features and provides interactive examples. The playground showcases:
719
+
720
+ - **Basic Charts**: Simple line charts with different configurations
721
+ - **Plugin Examples**: All built-in plugins working together
722
+ - **Legend Showcase**: Various legend patterns and interactions
723
+ - **Responsive Sizing**: Auto-resize and manual sizing examples
724
+ - **Custom Plugins**: Examples of creating your own plugins
725
+ - **External Integration**: Charts interacting with external components
726
+
727
+ ### Running the Playground Locally
728
+
729
+ To explore the playground and see the library in action:
730
+
731
+ ```bash
732
+ # Clone the repository
733
+ git clone https://github.com/dsnchz/solid-uplot.git
734
+ cd solid-uplot
735
+
736
+ # Install dependencies
737
+ npm install
738
+ # or
739
+ pnpm install
740
+ # or
741
+ yarn install
742
+ # or
743
+ bun install
744
+
745
+ # Start the playground development server
746
+ npm run start
747
+ # or
748
+ pnpm start
749
+ # or
750
+ yarn start
751
+ # or
752
+ bun start
753
+ ```
754
+
755
+ The playground will be available at `http://localhost:3000` and includes:
756
+
757
+ - **Live code examples** with syntax highlighting
758
+ - **Interactive demos** you can modify in real-time
759
+ - **Performance comparisons** between different configurations
760
+ - **Best practices** and common patterns
761
+ - **Plugin development examples** with step-by-step guides
762
+
763
+ The playground source code also serves as a comprehensive reference for implementing various chart patterns and plugin combinations.
764
+
765
+ ## 🤝 Contributing
766
+
767
+ Contributions are welcome! Please feel free to submit a Pull Request.
768
+
769
+ ## 📄 License
770
+
771
+ MIT