From 6ab9dbb5dba10b4ce12d72f87381f06510035303 Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Sun, 29 Sep 2024 15:26:05 -0400 Subject: [PATCH] feat: adds uplot-solid implementation --- README.md | 73 ++++++++++-- bun.lockb | Bin 180496 -> 186912 bytes package.json | 43 ++++++- playground/App.tsx | 140 ++++++++++++++--------- playground/resource/uplot_fake_data.json | 70 ++++++++++++ src/UplotSolid.tsx | 70 ++++++++++++ src/index.tsx | 3 +- tsup.config.ts | 6 +- 8 files changed, 335 insertions(+), 70 deletions(-) create mode 100644 playground/resource/uplot_fake_data.json create mode 100644 src/UplotSolid.tsx diff --git a/README.md b/README.md index a8948b8..18011ee 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,71 @@ -# Template: SolidJS Library +# μPlot Solid -Template for [SolidJS](https://www.solidjs.com/) library package. Bundling of the library is managed by [tsup](https://tsup.egoist.dev/). +Solid wrapper around [μPlot](https://github.com/leeoniya/uPlot?tab=readme-ov-file#-%CE%BCplot) which is a small, fast and performant 2D canvas based chart for time series, lines, areas, ohlc & bars. It exposes the μPlot API in a fully typed, declarative JSX format and does the work to make the μPlot experience as reactive as possible. -Other things configured include: +Once a μPlot instance is made, the component will **not** recreate a new instance even if the `data` or `size` of the chart updates reactively. However, it will create a new chart instance if any other options are reactively updated when using it (e.g. `series`, `hooks`, `plugins`, etc.). -- Bun (for dependency management and running scripts) -- TypeScript -- ESLint / Prettier -- Solid Testing Library + Vitest (for testing) -- GitHub Actions (for all CI/CD) +## Installing + +```bash +npm install uplot uplot-solid +pnpm add uplot uplot-solid +yarn add uplot uplot-solid +bun add uplot uplot-solid +``` + +## Overview + +If you are new to μPlot, here is a quick breakdown of some things using a simple example plot: + +```tsx + +``` + +The above is the minimum of what you need to get a chart on screen given you have filled in the data. The expected format of `data` is a 2D array where the first array is the dataset for the x-series of the plot. The `series` list follows the same ordering being a list of config objects where the first object applies to the x-series and the rest to the y-series. μPlot supports a single x-series and all the y-series share the same x-series data. There are many more fields within the series struct that you can configure to tailor the display of the series data to your liking. + +Here are some other key options within the API to familiarize yourself with: + +- `hooks`: An object structure exposing key events that occur within μPlot. Every event hook accepts an **array of callbacks** allowing you to have discrete units of work that occur within a single event. +- `plugins`: A **plugin** is used to extend or modify the behavior of the chart by injecting custom functionality. You can hook into the various lifecycle events (the very same ones exposed from the `hooks` property) and add custom behavior, such as drawing on the canvas, adding custom controls, tooltips, interactions, or integrating with external libraries. + +### Why Use Plugins Instead of Hooks? + +In a nutshell, separation of concerns. Here are some points: + +- **Customization**: Add custom drawings (like annotations, lines, or custom tooltips). +- **Modularity**: Keep the core logic of the chart clean and separate from specific custom behavior. +- **Reusability**: Create reusable plugins that can be applied across multiple uPlot instances with consistent functionality. +- **Extensibility**: Augment the chart with additional features (e.g., zoom controls, data overlays). + +### Demos + +Be sure to check out the [μPlot demos](https://leeoniya.github.io/uPlot/demos/index.html) to see the many kinds of different charts you can create. The code for the demos can be found [here](https://github.com/leeoniya/uPlot/tree/master/demos). + +Over time, I will try to add example demos using this library to showcase some simple and more complex examples (e.g. creating custom tooltips either via `plugins` or using Solid directly) ## Getting Started diff --git a/bun.lockb b/bun.lockb index c25dc32ef1b33fb0265cab46d0db6d57cea3eeb4..4b022991a492183d12c8ee7d9788150fdb96a1bf 100755 GIT binary patch delta 34760 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

bcbDwu-iZ~L%^(Mq{oU!~{3XiK-g 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 m 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 diff --git a/package.json b/package.json index 75fa323..3e2485d 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,24 @@ { - "name": "template-solidjs-library", - "version": "0.0.0", - "description": "Template for SolidJS library using tsup for bundling. Configured with Bun, NVM, TypeScript, ESLint, Prettier, Vitest, and GHA", + "name": "uplot-solid", + "version": "1.0.0", + "description": "Solid wrapper for μPlot", "type": "module", + "author": "Daniel Sanchez ", + "license": "MIT", + "homepage": "https://github.com/thedanchez/uplot-solid#readme", + "bugs": { + "url": "https://github.com/thedanchez/uplot-solid/issues" + }, + "files": [ + "dist" + ], + "keywords": [ + "uPlot", + "Solid", + "SolidJS", + "charts", + "plot" + ], "scripts": { "build": "tsup", "build:watch": "tsup --watch", @@ -17,7 +33,6 @@ "test:cov": "vitest run --coverage", "typecheck": "tsc --noEmit" }, - "license": "MIT", "devDependencies": { "@solidjs/testing-library": "^0.8.10", "@testing-library/jest-dom": "^6.5.0", @@ -25,19 +40,35 @@ "@typescript-eslint/eslint-plugin": "^8.7.0", "@typescript-eslint/parser": "^8.7.0", "@vitest/coverage-istanbul": "^2.1.1", + "esbuild-css-modules-plugin": "^3.1.2", "eslint": "^8.57.0", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-solid": "^0.14.3", "jsdom": "^25.0.1", "prettier": "^3.3.3", + "solid-js": "^1.9.1", "tsup": "^8.3.0", "tsup-preset-solid": "^2.2.0", "typescript": "^5.6.2", + "uplot": "^1.6.31", "vite": "^5.4.8", "vite-plugin-solid": "^2.10.2", "vitest": "^2.1.1" }, "peerDependencies": { - "solid-js": ">=1.8.0" - } + "solid-js": ">=1.8.0", + "uplot": ">=1.6.31" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "browser": {}, + "exports": { + "solid": "./dist/index.jsx", + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "typesVersions": {} } diff --git a/playground/App.tsx b/playground/App.tsx index 0bbca41..4b866cf 100644 --- a/playground/App.tsx +++ b/playground/App.tsx @@ -1,61 +1,99 @@ -import { createSignal, For } from "solid-js"; -import { createStore } from "solid-js/store"; +import { createSignal } from "solid-js"; -type Todo = { id: number; text: string; completed: boolean }; +import UplotSolid from "../dist"; +import fakeData from "./resource/uplot_fake_data.json"; + +const isNil = (value: unknown): value is null | undefined => value === null || value === undefined; export const App = () => { - let input!: HTMLInputElement; + const [size, setSize] = createSignal({ width: 1000, height: 300 }); + const [position, setPosition] = createSignal({ left: 0, top: 0 }); + const [content, setContent] = createSignal(""); - const [count, setCount] = createSignal(0); - const [todos, setTodos] = createStore([]); + const alignedData = [fakeData.time, fakeData.y_series_1, fakeData.y_series_2] as uPlot.AlignedData; - const addTodo = (text: string) => { - setTodos(todos.length, { id: todos.length, text, completed: false }); - }; + return ( +

); }; + +type TooltipProps = { + readonly position: { left: number; top: number }; + readonly content: string; +}; + +function Tooltip(props: TooltipProps) { + return ( +
+ {props.content} +
+ ); +} diff --git a/playground/resource/uplot_fake_data.json b/playground/resource/uplot_fake_data.json new file mode 100644 index 0000000..3ecc76b --- /dev/null +++ b/playground/resource/uplot_fake_data.json @@ -0,0 +1,70 @@ +{ + "time": [ + 1727395200000, 1727395500000, 1727395800000, 1727396100000, 1727396400000, 1727396700000, 1727397000000, + 1727397300000, 1727397600000, 1727397900000, 1727398200000, 1727398500000, 1727398800000, 1727399100000, + 1727399400000, 1727399700000, 1727400000000, 1727400300000, 1727400600000, 1727400900000, 1727401200000, + 1727401500000, 1727401800000, 1727402100000, 1727402400000, 1727402700000, 1727403000000, 1727403300000, + 1727403600000, 1727403900000, 1727404200000, 1727404500000, 1727404800000, 1727405100000, 1727405400000, + 1727405700000, 1727406000000, 1727406300000, 1727406600000, 1727406900000, 1727407200000, 1727407500000, + 1727407800000, 1727408100000, 1727408400000, 1727408700000, 1727409000000, 1727409300000, 1727409600000, + 1727409900000, 1727410200000, 1727410500000, 1727410800000, 1727411100000, 1727411400000, 1727411700000, + 1727412000000, 1727412300000, 1727412600000, 1727412900000, 1727413200000, 1727413500000, 1727413800000, + 1727414100000, 1727414400000, 1727414700000, 1727415000000, 1727415300000, 1727415600000, 1727415900000, + 1727416200000, 1727416500000, 1727416800000, 1727417100000, 1727417400000, 1727417700000, 1727418000000, + 1727418300000, 1727418600000, 1727418900000, 1727419200000, 1727419500000, 1727419800000, 1727420100000, + 1727420400000, 1727420700000, 1727421000000, 1727421300000, 1727421600000, 1727421900000, 1727422200000, + 1727422500000, 1727422800000, 1727423100000, 1727423400000, 1727423700000, 1727424000000, 1727424300000, + 1727424600000, 1727424900000, 1727425200000, 1727425500000, 1727425800000, 1727426100000, 1727426400000, + 1727426700000, 1727427000000, 1727427300000, 1727427600000, 1727427900000, 1727428200000, 1727428500000, + 1727428800000, 1727429100000, 1727429400000, 1727429700000, 1727430000000, 1727430300000, 1727430600000, + 1727430900000, 1727431200000, 1727431500000, 1727431800000, 1727432100000, 1727432400000, 1727432700000, + 1727433000000, 1727433300000, 1727433600000, 1727433900000, 1727434200000, 1727434500000, 1727434800000, + 1727435100000, 1727435400000, 1727435700000, 1727436000000, 1727436300000, 1727436600000, 1727436900000, + 1727437200000, 1727437500000, 1727437800000, 1727438100000, 1727438400000, 1727438700000, 1727439000000, + 1727439300000, 1727439600000, 1727439900000, 1727440200000, 1727440500000, 1727440800000, 1727441100000, + 1727441400000, 1727441700000, 1727442000000, 1727442300000, 1727442600000, 1727442900000, 1727443200000, + 1727443500000, 1727443800000, 1727444100000, 1727444400000, 1727444700000, 1727445000000, 1727445300000, + 1727445600000, 1727445900000, 1727446200000, 1727446500000, 1727446800000, 1727447100000, 1727447400000, + 1727447700000, 1727448000000, 1727448300000, 1727448600000, 1727448900000, 1727449200000, 1727449500000, + 1727449800000, 1727450100000, 1727450400000, 1727450700000, 1727451000000, 1727451300000, 1727451600000, + 1727451900000, 1727452200000, 1727452500000, 1727452800000, 1727453100000, 1727453400000, 1727453700000, + 1727454000000, 1727454300000, 1727454600000, 1727454900000, 1727455200000, 1727455500000, 1727455800000, + 1727456100000, 1727456400000, 1727456700000, 1727457000000, 1727457300000, 1727457600000, 1727457900000, + 1727458200000, 1727458500000, 1727458800000, 1727459100000, 1727459400000, 1727459700000, 1727460000000, + 1727460300000, 1727460600000, 1727460900000, 1727461200000, 1727461500000, 1727461800000, 1727462100000, + 1727462400000, 1727462700000, 1727463000000, 1727463300000, 1727463600000, 1727463900000, 1727464200000, + 1727464500000, 1727464800000, 1727465100000, 1727465400000, 1727465700000, 1727466000000, 1727466300000, + 1727466600000, 1727466900000, 1727467200000, 1727467500000, 1727467800000, 1727468100000, 1727468400000, + 1727468700000, 1727469000000, 1727469300000, 1727469600000, 1727469900000, 1727470200000, 1727470500000, + 1727470800000, 1727471100000, 1727471400000, 1727471700000, 1727472000000, 1727472300000, 1727472600000, + 1727472900000, 1727473200000, 1727473500000, 1727473800000, 1727474100000, 1727474400000, 1727474700000, + 1727475000000, 1727475300000, 1727475600000, 1727475900000, 1727476200000, 1727476500000, 1727476800000, + 1727477100000, 1727477400000, 1727477700000, 1727478000000, 1727478300000, 1727478600000, 1727478900000, + 1727479200000, 1727479500000, 1727479800000, 1727480100000, 1727480400000, 1727480700000, 1727481000000, + 1727481300000 + ], + "y_series_1": [ + 73, 75, 74, 94, 90, 78, 64, 94, 50, 74, 56, 58, 73, 50, 93, 57, 73, 60, 66, 57, 84, 84, 82, 54, 91, 88, 90, 77, 56, + 58, 57, 61, 83, 82, 97, 72, 73, 86, 84, 93, 89, 71, 76, 84, 50, 84, 86, 96, 63, 52, 50, 54, 75, 63, 88, 76, 58, 64, + 64, 75, 91, 62, 81, 88, 98, 81, 53, 79, 86, 72, 88, 94, 64, 92, 78, 85, 62, 81, 56, 71, 77, 51, 91, 94, 55, 77, 77, + 93, 93, 69, 79, 60, 77, 74, 88, 82, 50, 76, 62, 90, 52, 88, 55, 57, 76, 58, 86, 82, 91, 93, 73, 64, 81, 81, 73, 90, + 98, 98, 61, 88, 51, 52, 98, 86, 98, 66, 98, 51, 51, 77, 72, 86, 81, 82, 50, 68, 51, 93, 75, 81, 55, 81, 53, 60, 66, + 87, 73, 54, 83, 55, 71, 60, 97, 65, 82, 58, 55, 65, 78, 52, 69, 85, 68, 75, 52, 68, 69, 81, 56, 90, 82, 89, 88, 67, + 89, 50, 60, 77, 74, 99, 72, 80, 79, 91, 84, 56, 65, 75, 97, 98, 51, 50, 97, 61, 54, 86, 81, 58, 90, 84, 68, 97, 65, + 52, 69, 73, 82, 73, 60, 98, 57, 85, 87, 89, 69, 84, 97, 74, 84, 74, 78, 67, 95, 67, 51, 84, 65, 90, 85, 82, 53, 82, + 63, 70, 97, 69, 57, 56, 52, 66, 82, 97, 61, 71, 71, 95, 79, 87, 87, 94, 57, 76, 76, 83, 70, 79, 82, 77, 96, 82, 54, + 97, 68, 53, 84, 98, 66, 93, 77, 79, 78, 95, 55, 84, 90, 86, 73, 78, 98, 95, 80, 84, 82, 70, 81, 72, 82, 52 + ], + "y_series_2": [ + 37, 44, 61, 50, 73, 77, 22, 59, 65, 43, 69, 51, 66, 41, 42, 21, 46, 61, 21, 45, 36, 59, 52, 28, 62, 73, 67, 58, 48, + 61, 74, 45, 54, 69, 44, 43, 32, 79, 77, 26, 76, 55, 64, 39, 20, 27, 65, 35, 33, 31, 70, 42, 34, 47, 53, 21, 51, 42, + 41, 70, 44, 77, 41, 77, 77, 41, 68, 71, 61, 25, 34, 73, 62, 79, 56, 52, 27, 72, 79, 63, 63, 24, 58, 23, 25, 64, 51, + 71, 49, 66, 54, 74, 59, 71, 35, 32, 69, 79, 61, 49, 38, 36, 75, 38, 47, 77, 74, 45, 56, 45, 72, 42, 28, 31, 72, 20, + 77, 77, 20, 66, 53, 51, 73, 67, 44, 59, 64, 72, 20, 35, 58, 24, 41, 48, 74, 22, 31, 45, 35, 70, 56, 41, 76, 48, 33, + 47, 24, 66, 68, 49, 65, 71, 24, 31, 35, 45, 45, 67, 40, 58, 55, 52, 49, 56, 42, 77, 29, 73, 24, 55, 53, 71, 50, 29, + 77, 38, 77, 51, 20, 75, 24, 64, 23, 35, 43, 35, 74, 21, 68, 47, 51, 46, 39, 43, 31, 69, 54, 79, 52, 52, 70, 62, 56, + 31, 22, 20, 52, 59, 29, 62, 63, 48, 32, 31, 50, 65, 21, 70, 69, 54, 42, 36, 45, 27, 48, 45, 29, 45, 53, 70, 60, 26, + 23, 77, 72, 69, 64, 30, 48, 75, 55, 44, 40, 76, 55, 29, 56, 28, 43, 54, 68, 54, 67, 55, 37, 68, 58, 51, 43, 42, 51, + 56, 31, 68, 74, 32, 42, 44, 54, 60, 49, 36, 68, 39, 67, 44, 41, 32, 78, 38, 68, 55, 31, 60, 38, 31, 28, 26 + ] +} diff --git a/src/UplotSolid.tsx b/src/UplotSolid.tsx new file mode 100644 index 0000000..b859e10 --- /dev/null +++ b/src/UplotSolid.tsx @@ -0,0 +1,70 @@ +import "uplot/dist/uPlot.min.css"; + +import { createEffect, type JSX, mergeProps, onCleanup, type ParentProps, splitProps, untrack } from "solid-js"; +import uPlot from "uplot"; + +type Props = uPlot.Options & { + readonly class?: string; + /** Callback when uPlot instance is created */ + readonly onCreate?: (u: uPlot, container: HTMLDivElement) => void; + /** Apply scale reset on redraw triggered by updated plot data (default: `true`) */ + readonly resetScales?: boolean; + readonly style?: JSX.CSSProperties | string; +}; + +const DEFAULT_PROPS = { + data: [] as uPlot.AlignedData, + resetScales: true, +}; + +const UplotSolid = (props: ParentProps) => { + let chartContainerRef!: HTMLDivElement; + + const mergedProps = mergeProps(DEFAULT_PROPS, props); + const [_props, options] = splitProps(mergedProps, [ + "class", + "children", + "data", + "height", + "width", + "onCreate", + "resetScales", + "style", + ]); + + const size = () => ({ width: _props.width, height: _props.height }); + + // Untrack size to avoid re-creating the chart on size changes + const uplotOptions = () => ({ ...options, ...untrack(size) }); + + createEffect(() => { + const chart = new uPlot( + uplotOptions(), + // Untrack data to avoid re-creating the chart on data changes + untrack(() => _props.data), + chartContainerRef, + ); + + _props.onCreate?.(chart, chartContainerRef); + + createEffect(() => { + chart.setSize(size()); + }); + + createEffect(() => { + chart.setData(_props.data, _props.resetScales); + }); + + onCleanup(() => { + chart.destroy(); + }); + }); + + return ( +
+ {_props.children} +
+ ); +}; + +export default UplotSolid; diff --git a/src/index.tsx b/src/index.tsx index 148fe81..960d42e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,2 +1 @@ -// Main library export site -// Use playground app (via Vite) to test and document the library +export { default } from "./UplotSolid"; diff --git a/tsup.config.ts b/tsup.config.ts index 55ecfb1..0588e1d 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,3 +1,4 @@ +import inlineCssModules from "esbuild-css-modules-plugin"; import { defineConfig } from "tsup"; import * as preset from "tsup-preset-solid"; @@ -6,12 +7,11 @@ const generateSolidPresetOptions = (watching: boolean): preset.PresetOptions => { // entries with '.tsx' extension will have `solid` export condition generated entry: "src/index.tsx", - dev_entry: false, - server_entry: true, }, ], drop_console: !watching, // remove all `console.*` calls and `debugger` statements in prod builds cjs: false, + esbuild_plugins: [inlineCssModules()], }); export default defineConfig((config) => { @@ -28,7 +28,7 @@ export default defineConfig((config) => { const tsupOptions = preset .generateTsupOptions(parsedOptions) - .map((tsupOption) => ({ name: "solid-js", ...tsupOption })); + .map((tsupOption) => ({ name: "uplot-solid", ...tsupOption })); return tsupOptions; });