{"id":31900798,"url":"https://github.com/dsnchz/solid-uplot","last_synced_at":"2025-10-13T12:28:04.899Z","repository":{"id":295858895,"uuid":"982918254","full_name":"dsnchz/solid-uplot","owner":"dsnchz","description":"SolidJS wrapper for uPlot — an ultra-fast, tiny time-series \u0026 charting library with a SolidJS enhanced plugin system","archived":false,"fork":false,"pushed_at":"2025-06-17T15:17:52.000Z","size":273,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-10T22:14:27.242Z","etag":null,"topics":["analysis","canvas","canvas-chart","canvas-chart-library","charting-library","charts","data-analysis","data-analytics","data-visualization","solidjs","trading","visualization"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dsnchz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-13T15:41:45.000Z","updated_at":"2025-09-24T15:57:52.000Z","dependencies_parsed_at":"2025-05-27T18:51:33.667Z","dependency_job_id":null,"html_url":"https://github.com/dsnchz/solid-uplot","commit_stats":null,"previous_names":["dsnchz/solid-uplot"],"tags_count":0,"template":false,"template_full_name":"thedanchez/template-solidjs-library","purl":"pkg:github/dsnchz/solid-uplot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsnchz%2Fsolid-uplot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsnchz%2Fsolid-uplot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsnchz%2Fsolid-uplot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsnchz%2Fsolid-uplot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dsnchz","download_url":"https://codeload.github.com/dsnchz/solid-uplot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsnchz%2Fsolid-uplot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279015061,"owners_count":26085643,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analysis","canvas","canvas-chart","canvas-chart-library","charting-library","charts","data-analysis","data-analytics","data-visualization","solidjs","trading","visualization"],"created_at":"2025-10-13T12:28:00.797Z","updated_at":"2025-10-13T12:28:04.888Z","avatar_url":"https://github.com/dsnchz.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://assets.solidjs.com/banner?project=solid-uplot\u0026type=Ecosystem\u0026background=tiles\" alt=\"@dschz/solid-uplot banner\" /\u003e\n\u003c/p\u003e\n\n# @dschz/solid-uplot\n\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![uPlot](https://img.shields.io/badge/uPlot-%3E%3D1.6.32-orange)](https://github.com/leeoniya/uPlot)\n[![npm](https://img.shields.io/npm/v/@dschz/solid-uplot?color=blue)](https://www.npmjs.com/package/@dschz/solid-uplot)\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@dschz/solid-uplot)](https://bundlephobia.com/package/@dschz/solid-uplot)\n[![JSR](https://jsr.io/badges/@dschz/solid-uplot/score)](https://jsr.io/@dschz/solid-uplot)\n[![CI](https://github.com/dsnchz/solid-uplot/actions/workflows/ci.yaml/badge.svg)](https://github.com/dsnchz/solid-uplot/actions/workflows/ci.yaml)\n[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord\u0026logoColor=white)](https://discord.gg/6wkpXQCNn6)\n\n\u003e 💹 SolidJS wrapper for [uPlot](https://github.com/leeoniya/uPlot) — an ultra-fast, small footprint charting library for time-series data.\n\n## ✨ Features\n\n- ✅ Fully reactive SolidJS wrapper around uPlot\n- 🔌 Plugin system support with inter-plugin communication\n- 🎯 Fine-grained control over chart lifecycle\n- 💡 Lightweight and fast\n- 💻 TypeScript support out of the box\n- 🎨 Built-in plugins for tooltips, legends, cursor tracking, and series focusing\n- 📱 Responsive sizing support with auto-resize capabilities\n\n## 📦 Installation\n\n```bash\nnpm install solid-js uplot @dschz/solid-uplot\npnpm install solid-js uplot @dschz/solid-uplot\nyarn install solid-js uplot @dschz/solid-uplot\nbun install solid-js uplot @dschz/solid-uplot\n```\n\n## 📁 Package Structure\n\nThis package provides three main export paths for different functionality:\n\n### `@dschz/solid-uplot`\n\nCore components and plugin system:\n\n```tsx\nimport { SolidUplot, createPluginBus } from \"@dschz/solid-uplot\";\nimport type {\n  SolidUplotPluginBus,\n  UplotPluginFactory,\n  UplotPluginFactoryContext,\n} from \"@dschz/solid-uplot\";\n```\n\n### `@dschz/solid-uplot/plugins`\n\nThis export path provides four plugins (three of which can be considered primitives).\n\n- `cursor`: transmits cursor position data\n- `focusSeries`: transmits which series are visually emphasized\n- `tooltip`: plugin that allows you to present a custom JSX tooltip around the cursor\n- `legend`: plugin that allows you to present a custom JSX component as your legend over the canvas drawing area.\n\n```tsx\nimport { cursor, tooltip, legend, focusSeries } from \"@dschz/solid-uplot/plugins\";\nimport type {\n  CursorPluginMessageBus,\n  FocusSeriesPluginMessageBus,\n  TooltipProps,\n  LegendProps,\n} from \"@dschz/solid-uplot/plugins\";\n```\n\n### `@dschz/solid-uplot/utils`\n\nSome 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).\n\n```tsx\nimport {\n  getSeriesData,\n  getCursorData,\n  getColorString,\n  getNewCalendarDayIndices,\n} from \"@dschz/solid-uplot/utils\";\nimport type { SeriesDatum, CursorData } from \"@dschz/solid-uplot/utils\";\n```\n\n## 🚀 Quick Start\n\n```tsx\nimport { SolidUplot, createPluginBus } from \"@dschz/solid-uplot\";\nimport { cursor, tooltip, legend } from \"@dschz/solid-uplot/plugins\";\nimport type { CursorPluginMessageBus, TooltipProps, LegendProps } from \"@dschz/solid-uplot/plugins\";\n\n// Create a tooltip component\nconst MyTooltip = (props: TooltipProps) =\u003e (\n  \u003cdiv style={{ background: \"white\", padding: \"8px\", border: \"1px solid #ccc\" }}\u003e\n    \u003cdiv\u003eX: {props.cursor.xValue}\u003c/div\u003e\n    \u003cFor each={props.seriesData}\u003e\n      {(series) =\u003e {\n        const value = props.u.data[series.seriesIdx]?.[props.cursor.idx];\n        return (\n          \u003cdiv\u003e\n            {series.label}: {value}\n          \u003c/div\u003e\n        );\n      }}\n    \u003c/For\u003e\n  \u003c/div\u003e\n);\n\n// Create a legend component\nconst MyLegend = (props: LegendProps) =\u003e (\n  \u003cdiv style={{ background: \"rgba(255,255,255,0.9)\", padding: \"8px\" }}\u003e\n    \u003cFor each={props.seriesData}\u003e\n      {(series) =\u003e (\n        \u003cdiv style={{ display: \"flex\", \"align-items\": \"center\", gap: \"4px\" }}\u003e\n          \u003cdiv\n            style={{\n              width: \"12px\",\n              height: \"12px\",\n              background: series.stroke,\n            }}\n          /\u003e\n          \u003cspan\u003e{series.label}\u003c/span\u003e\n        \u003c/div\u003e\n      )}\n    \u003c/For\u003e\n  \u003c/div\u003e\n);\n\nconst MyChart = () =\u003e {\n  const bus = createPluginBus\u003cCursorPluginMessageBus\u003e();\n\n  return (\n    \u003cSolidUplot\n      data={[\n        [0, 1, 2, 3], // x values\n        [10, 20, 30, 40], // y values for series 1\n        [15, 25, 35, 45], // y values for series 2\n      ]}\n      width={600}\n      height={400}\n      series={[\n        {},\n        { label: \"Series 1\", stroke: \"#1f77b4\" },\n        { label: \"Series 2\", stroke: \"#ff7f0e\" },\n      ]}\n      plugins={[\n        cursor(),\n        tooltip(MyTooltip),\n        legend(MyLegend, { placement: \"top-right\", pxOffset: 12 }),\n      ]}\n      pluginBus={bus}\n    /\u003e\n  );\n};\n```\n\n## 📏 Responsive Sizing\n\nFor responsive charts that automatically adapt to container size changes, use the `autoResize` prop:\n\n```tsx\n\u003cdiv style={{ width: \"100%\", height: \"400px\" }}\u003e\n  \u003cSolidUplot\n    autoResize={true}\n    data={data}\n    series={series}\n    // Chart will automatically resize to fill the container\n  /\u003e\n\u003c/div\u003e\n```\n\nFor more advanced responsive patterns, you can pair this library with [@dschz/solid-auto-sizer](https://github.com/dsnchz/solid-auto-sizer):\n\n```bash\nnpm install @dschz/solid-auto-sizer\npnpm install @dschz/solid-auto-sizer\nyarn install @dschz/solid-auto-sizer\nbun install @dschz/solid-auto-sizer\n```\n\n```tsx\nimport { AutoSizer } from \"@dschz/solid-auto-sizer\";\n\n\u003cAutoSizer\u003e\n  {({ width, height }) =\u003e \u003cSolidUplot width={width} height={height} data={data} /\u003e}\n\u003c/AutoSizer\u003e;\n```\n\n## 🔌 Enhanced Plugin System\n\nThe cornerstone feature of `SolidUplot` is its refined plugin system that enables extensible functionality and inter-plugin communication through a reactive message bus.\n\n### Plugin Bus Architecture\n\nThe Plugin Bus System enables plugins to communicate with each other and external components through a reactive store. This architecture provides:\n\n- **Type-safe communication**: All plugin messages are fully typed\n- **Reactive updates**: Changes in plugin state automatically trigger updates\n- **Decoupled components**: Plugins can interact without direct dependencies\n- **Extensible**: Easy to add new plugins that integrate with existing ones\n\n### Built-in Plugins\n\n#### Cursor Plugin\n\nTracks cursor position and interaction state across charts:\n\n```tsx\nimport { cursor } from \"@dschz/solid-uplot/plugins\";\nimport type { CursorPluginMessageBus } from \"@dschz/solid-uplot/plugins\";\n\nconst cursorPlugin = cursor();\n```\n\nThe cursor plugin provides cursor position data that other plugins can consume through the bus.\n\n#### Focus Series Plugin\n\nHighlights series based on cursor proximity:\n\n```tsx\nimport { focusSeries } from \"@dschz/solid-uplot/plugins\";\nimport type { FocusSeriesPluginMessageBus } from \"@dschz/solid-uplot/plugins\";\n\nconst focusPlugin = focusSeries({\n  pxThreshold: 15, // Distance threshold for focusing (default: 15)\n});\n```\n\n#### Tooltip Plugin\n\nRenders custom tooltips with automatic positioning and overflow handling:\n\n```tsx\nimport { tooltip } from \"@dschz/solid-uplot/plugins\";\nimport type { TooltipProps } from \"@dschz/solid-uplot/plugins\";\n\nconst MyTooltip: Component\u003cTooltipProps\u003e = (props) =\u003e {\n  return (\n    \u003cdiv\n      style={{\n        background: \"white\",\n        border: \"1px solid #ccc\",\n        padding: \"8px\",\n        \"border-radius\": \"4px\",\n        \"box-shadow\": \"0 2px 4px rgba(0,0,0,0.1)\",\n      }}\n    \u003e\n      \u003cdiv style={{ \"font-weight\": \"bold\", \"margin-bottom\": \"8px\" }}\u003eX: {props.cursor.xValue}\u003c/div\u003e\n      \u003cFor each={props.seriesData}\u003e\n        {(series) =\u003e {\n          const value = () =\u003e props.u.data[series.seriesIdx]?.[props.cursor.idx];\n          return (\n            \u003cShow when={series.visible}\u003e\n              \u003cdiv style={{ display: \"flex\", \"align-items\": \"center\", \"margin-bottom\": \"4px\" }}\u003e\n                \u003cdiv\n                  style={{\n                    width: \"10px\",\n                    height: \"10px\",\n                    \"border-radius\": \"50%\",\n                    \"background-color\": series.stroke,\n                    \"margin-right\": \"6px\",\n                  }}\n                /\u003e\n                \u003cspan style={{ color: series.stroke }}\u003e\n                  {series.label}: {value()?.toFixed(2)}\n                \u003c/span\u003e\n              \u003c/div\u003e\n            \u003c/Show\u003e\n          );\n        }}\n      \u003c/For\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst tooltipPlugin = tooltip(MyTooltip, {\n  placement: \"top-left\", // \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\"\n  zIndex: 20,\n});\n```\n\n#### Legend Plugin\n\nAdds customizable legends with smart positioning and interactive features:\n\n```tsx\nimport { legend } from \"@dschz/solid-uplot/plugins\";\nimport type { LegendProps } from \"@dschz/solid-uplot/plugins\";\n\nconst MyLegend: Component\u003cLegendProps\u003e = (props) =\u003e {\n  // Access cursor data for interactive features\n  const cursorVisible = () =\u003e props.bus.data.cursor?.state[props.u.root.id]?.visible;\n\n  return (\n    \u003cdiv\n      style={{\n        background: \"white\",\n        border: \"1px solid #ddd\",\n        \"border-radius\": \"4px\",\n        padding: \"8px\",\n        \"box-shadow\": \"0 2px 4px rgba(0,0,0,0.1)\",\n        // Dim when tooltip is active\n        opacity: cursorVisible() ? 0.6 : 1,\n        transition: \"opacity 200ms\",\n      }}\n    \u003e\n      \u003cdiv style={{ \"font-weight\": \"bold\", \"margin-bottom\": \"8px\" }}\u003eLegend\u003c/div\u003e\n      \u003cFor each={props.seriesData}\u003e\n        {(series) =\u003e (\n          \u003cShow when={series.visible}\u003e\n            \u003cdiv\n              style={{\n                display: \"flex\",\n                \"align-items\": \"center\",\n                gap: \"6px\",\n                \"margin-bottom\": \"4px\",\n              }}\n            \u003e\n              \u003cdiv\n                style={{\n                  width: \"12px\",\n                  height: \"12px\",\n                  \"background-color\": series.stroke,\n                  \"border-radius\": \"2px\",\n                }}\n              /\u003e\n              \u003cspan style={{ \"font-size\": \"14px\" }}\u003e{series.label}\u003c/span\u003e\n            \u003c/div\u003e\n          \u003c/Show\u003e\n        )}\n      \u003c/For\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst legendPlugin = legend(MyLegend, {\n  placement: \"top-left\", // \"top-left\" | \"top-right\"\n  pxOffset: 8, // Distance from chart edges (default: 8)\n  zIndex: 10,\n});\n```\n\n**Legend Plugin Features:**\n\n- **Simple positioning**: Only top-left or top-right corners to avoid axis conflicts\n- **Size-constrained**: Legend cannot exceed chart drawing area dimensions\n- **Layout-agnostic**: You control internal layout and styling\n- **Non-interfering**: Designed to work harmoniously with chart interactions\n- **Plugin bus integration**: Access cursor and focus data for smart interactions\n- **Automatic cleanup**: Proper memory management and DOM cleanup\n\n### Plugin Bus Type Safety\n\nWhen using multiple plugins, ensure type safety by properly typing the plugin bus:\n\n```tsx\nimport { createPluginBus } from \"@dschz/solid-uplot\";\nimport type {\n  CursorPluginMessageBus,\n  FocusSeriesPluginMessageBus,\n} from \"@dschz/solid-uplot/plugins\";\n\n// Create a bus that includes all plugin message types\nconst bus = createPluginBus\u003cCursorPluginMessageBus \u0026 FocusSeriesPluginMessageBus\u003e();\n\nconst MyChart = () =\u003e {\n  return (\n    \u003cSolidUplot\n      data={data}\n      pluginBus={bus}\n      plugins={[cursor(), focusSeries(), tooltip(MyTooltip), legend(MyLegend)]}\n    /\u003e\n  );\n};\n```\n\n### Creating Custom Plugins\n\nThe plugin system is open to extension. When authoring plugins for public consumption, follow this pattern:\n\n```tsx\nimport type { UplotPluginFactory } from \"@dschz/solid-uplot\";\nimport type { CursorPluginMessageBus } from \"@dschz/solid-uplot/plugins\";\n\n// 1. Define your plugin's message type\nexport type MyPluginMessage = {\n  value: number;\n  timestamp: number;\n};\n\n// 2. Define your plugin's message bus\nexport type MyPluginMessageBus = {\n  myPlugin?: MyPluginMessage;\n};\n\n// 3. Export your plugin factory\nexport const myPlugin = (\n  options = {},\n): UplotPluginFactory\u003cCursorPluginMessageBus \u0026 MyPluginMessageBus\u003e =\u003e {\n  return ({ bus }) =\u003e {\n    if (!bus) {\n      console.warn(\"[my-plugin]: A plugin bus is required\");\n      return { hooks: {} };\n    }\n\n    return {\n      hooks: {\n        ready: (u) =\u003e {\n          // Initialize plugin state\n          bus.setData(\"myPlugin\", {\n            value: 0,\n            timestamp: Date.now(),\n          });\n        },\n        setData: (u) =\u003e {\n          // Update plugin state\n          bus.setData(\"myPlugin\", \"value\", (prev) =\u003e prev + 1);\n        },\n      },\n    };\n  };\n};\n```\n\n### External Component Integration\n\nThe plugin bus enables powerful integrations between charts and external components:\n\n```tsx\nimport { createPluginBus } from \"@dschz/solid-uplot\";\nimport type { FocusSeriesPluginMessageBus } from \"@dschz/solid-uplot/plugins\";\n\nconst bus = createPluginBus\u003cFocusSeriesPluginMessageBus\u003e();\n\n// External component that can trigger series focus\nconst DataGrid = (props: { bus: typeof bus }) =\u003e {\n  const handleRowHover = (seriesLabel: string) =\u003e {\n    props.bus.setData(\"focusSeries\", {\n      sourceId: \"data-grid\",\n      targets: [{ label: seriesLabel }],\n    });\n  };\n\n  return \u003ctable\u003e{/* Grid implementation */}\u003c/table\u003e;\n};\n\n// Chart and grid interact through shared bus\nconst MyDashboard = () =\u003e {\n  return (\n    \u003cdiv\u003e\n      \u003cDataGrid bus={bus} /\u003e\n      \u003cSolidUplot data={data} pluginBus={bus} plugins={[focusSeries()]} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## 🔧 API Reference\n\n### SolidUplot Component\n\n```tsx\ntype SolidUplotEvents = {\n  /** Callback fired when the uPlot instance is created */\n  readonly onCreate?: (u: uPlot, meta: OnCreateMeta) =\u003e void;\n  /** Callback fired when the cursor moves */\n  readonly onCursorMove?: (params: OnCursorMoveParams) =\u003e void;\n};\n\n// Main component props (extends all uPlot.Options except plugins, width, height)\ntype SolidUplotProps\u003cT extends VoidStruct = VoidStruct\u003e = SolidUplotOptions\u003cT\u003e \u0026\n  SolidUplotEvents \u0026 {\n    // Ref callback to access the chart container element\n    ref?: Ref\u003cHTMLDivElement\u003e;\n\n    // CSS class name for the chart container (default: \"solid-uplot\")\n    // Additional classes will be appended to the default class\n    class?: string;\n\n    // CSS styles for the chart container (position is managed internally)\n    style?: Omit\u003cJSX.CSSProperties, \"position\"\u003e;\n\n    // Enable automatic resizing to fit container (default: false)\n    autoResize?: boolean;\n\n    // Whether to reset scales when chart data is updated (default: true)\n    resetScales?: boolean;\n\n    // Where to place children components relative to the chart (default: \"top\")\n    childrenPlacement?: \"top\" | \"bottom\";\n  };\n\n// Configuration options extending uPlot.Options with SolidJS enhancements\ntype SolidUplotOptions\u003cT extends VoidStruct = VoidStruct\u003e = Omit\u003c\n  uPlot.Options,\n  \"plugins\" | \"width\" | \"height\"\n\u003e \u0026 {\n  // Chart dimensions\n  width?: number; // default: 600\n  height?: number; // default: 300\n\n  // Plugin configuration\n  plugins?: SolidUplotPlugin\u003cT\u003e[];\n  pluginBus?: SolidUplotPluginBus\u003cT\u003e;\n};\n\n// Plugin type (can be standard uPlot plugin or factory function)\ntype SolidUplotPlugin\u003cT extends VoidStruct = VoidStruct\u003e = uPlot.Plugin | UplotPluginFactory\u003cT\u003e;\n```\n\n### Plugin Bus\n\n```tsx\n// Plugin bus type (derived from createPluginBus return type)\ntype SolidUplotPluginBus\u003cT extends VoidStruct = VoidStruct\u003e = ReturnType\u003ctypeof createPluginBus\u003cT\u003e\u003e;\n\n// Create a plugin bus\nconst createPluginBus: \u003cT extends VoidStruct = VoidStruct\u003e(\n  initialData?: T,\n) =\u003e SolidUplotPluginBus\u003cT\u003e;\n```\n\n### Built-in Plugin Options\n\n```tsx\n// Cursor Plugin\nconst cursor = (): UplotPluginFactory\u003cCursorPluginMessageBus\u003e;\n\n// Focus Series Plugin\nconst focusSeries = (options?: {\n  pxThreshold?: number; // default: 15\n}): UplotPluginFactory\u003cCursorPluginMessageBus \u0026 FocusSeriesPluginMessageBus\u003e;\n\n// Tooltip Plugin\nconst tooltip = (\n  Component: Component\u003cTooltipProps\u003e,\n  options?: {\n    placement?: \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n    id?: string;\n    class?: string;\n    style?: JSX.CSSProperties;\n    zIndex?: number; // default: 20\n  }\n): UplotPluginFactory\u003cCursorPluginMessageBus \u0026 FocusSeriesPluginMessageBus\u003e;\n\n// Legend Plugin\nconst legend = (\n  Component: Component\u003cLegendProps\u003e,\n  options?: {\n    placement?: \"top-left\" | \"top-right\"; // default: \"top-left\"\n    pxOffset?: number; // default: 8\n    id?: string;\n    class?: string;\n    style?: JSX.CSSProperties;\n    zIndex?: number; // default: 10\n  }\n): UplotPluginFactory\u003cCursorPluginMessageBus \u0026 FocusSeriesPluginMessageBus\u003e;\n```\n\n## 📚 Examples\n\n### Basic Chart\n\n```tsx\nimport { SolidUplot } from \"@dschz/solid-uplot\";\n\nconst BasicChart = () =\u003e {\n  return (\n    \u003cSolidUplot\n      data={[\n        [0, 1, 2, 3],\n        [10, 20, 30, 40],\n        [15, 25, 35, 45],\n      ]}\n      width={600}\n      height={400}\n      scales={{\n        x: { time: false },\n      }}\n      series={[\n        {},\n        { label: \"Series 1\", stroke: \"#1f77b4\" },\n        { label: \"Series 2\", stroke: \"#ff7f0e\" },\n      ]}\n    /\u003e\n  );\n};\n```\n\n### Chart with All Plugins\n\n```tsx\nimport { SolidUplot, createPluginBus } from \"@dschz/solid-uplot\";\nimport { cursor, tooltip, legend, focusSeries } from \"@dschz/solid-uplot/plugins\";\nimport type {\n  CursorPluginMessageBus,\n  FocusSeriesPluginMessageBus,\n  TooltipProps,\n  LegendProps,\n} from \"@dschz/solid-uplot/plugins\";\n\nconst MyTooltip: Component\u003cTooltipProps\u003e = (props) =\u003e (\n  \u003cdiv style={{ background: \"white\", padding: \"8px\", border: \"1px solid #ccc\" }}\u003e\n    \u003cdiv\u003eTime: {new Date(props.cursor.xValue * 1000).toLocaleTimeString()}\u003c/div\u003e\n    \u003cFor each={props.seriesData}\u003e\n      {(series) =\u003e {\n        const value = props.u.data[series.seriesIdx]?.[props.cursor.idx];\n        return (\n          \u003cdiv style={{ color: series.stroke }}\u003e\n            {series.label}: {value?.toFixed(2)}\n          \u003c/div\u003e\n        );\n      }}\n    \u003c/For\u003e\n  \u003c/div\u003e\n);\n\nconst MyLegend: Component\u003cLegendProps\u003e = (props) =\u003e {\n  const cursorVisible = () =\u003e props.bus.data.cursor?.state[props.u.root.id]?.visible;\n\n  return (\n    \u003cdiv\n      style={{\n        background: \"white\",\n        border: \"1px solid #ddd\",\n        padding: \"8px\",\n        opacity: cursorVisible() ? 0.6 : 1,\n        transition: \"opacity 200ms\",\n      }}\n    \u003e\n      \u003cFor each={props.seriesData}\u003e\n        {(series) =\u003e (\n          \u003cdiv style={{ display: \"flex\", \"align-items\": \"center\", gap: \"6px\" }}\u003e\n            \u003cdiv\n              style={{\n                width: \"12px\",\n                height: \"12px\",\n                background: series.stroke,\n              }}\n            /\u003e\n            \u003cspan\u003e{series.label}\u003c/span\u003e\n          \u003c/div\u003e\n        )}\n      \u003c/For\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst FullFeaturedChart = () =\u003e {\n  const bus = createPluginBus\u003cCursorPluginMessageBus \u0026 FocusSeriesPluginMessageBus\u003e();\n\n  return (\n    \u003cSolidUplot\n      data={[\n        [0, 1, 2, 3, 4, 5],\n        [10, 20, 30, 40, 50, 60],\n        [15, 25, 35, 45, 55, 65],\n        [5, 15, 25, 35, 45, 55],\n      ]}\n      width={800}\n      height={500}\n      series={[\n        {},\n        { label: \"Revenue\", stroke: \"#1f77b4\" },\n        { label: \"Profit\", stroke: \"#ff7f0e\" },\n        { label: \"Expenses\", stroke: \"#2ca02c\" },\n      ]}\n      plugins={[\n        cursor(),\n        focusSeries({ pxThreshold: 20 }),\n        tooltip(MyTooltip, { placement: \"top-right\" }),\n        legend(MyLegend, { placement: \"top-left\", pxOffset: 12 }),\n      ]}\n      pluginBus={bus}\n    /\u003e\n  );\n};\n```\n\n### Responsive Chart\n\n```tsx\nconst ResponsiveChart = () =\u003e {\n  return (\n    \u003cdiv style={{ width: \"100%\", height: \"400px\", border: \"1px solid #ccc\" }}\u003e\n      \u003cSolidUplot\n        autoResize={true}\n        data={data}\n        series={series}\n        plugins={[cursor(), tooltip(MyTooltip)]}\n      /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### External Integration\n\n```tsx\nconst Dashboard = () =\u003e {\n  const bus = createPluginBus\u003cFocusSeriesPluginMessageBus\u003e();\n\n  const handleSeriesToggle = (seriesLabel: string) =\u003e {\n    bus.setData(\"focusSeries\", {\n      sourceId: \"external-control\",\n      targets: [{ label: seriesLabel }],\n    });\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv\u003e\n        \u003cbutton onClick={() =\u003e handleSeriesToggle(\"Series 1\")}\u003eFocus Series 1\u003c/button\u003e\n        \u003cbutton onClick={() =\u003e handleSeriesToggle(\"Series 2\")}\u003eFocus Series 2\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cSolidUplot data={data} plugins={[focusSeries()]} pluginBus={bus} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## 🎮 Interactive Playground\n\nThis library includes a comprehensive playground application that demonstrates all features and provides interactive examples. The playground showcases:\n\n- **Basic Charts**: Simple line charts with different configurations\n- **Plugin Examples**: All built-in plugins working together\n- **Legend Showcase**: Various legend patterns and interactions\n- **Responsive Sizing**: Auto-resize and manual sizing examples\n- **Custom Plugins**: Examples of creating your own plugins\n- **External Integration**: Charts interacting with external components\n\n### Running the Playground Locally\n\nTo explore the playground and see the library in action:\n\n```bash\n# Clone the repository\ngit clone https://github.com/dsnchz/solid-uplot.git\ncd solid-uplot\n\n# Install dependencies\nnpm install\n# or\npnpm install\n# or\nyarn install\n# or\nbun install\n\n# Start the playground development server\nnpm run start\n# or\npnpm start\n# or\nyarn start\n# or\nbun start\n```\n\nThe playground will be available at `http://localhost:3000` and includes:\n\n- **Live code examples** with syntax highlighting\n- **Interactive demos** you can modify in real-time\n- **Performance comparisons** between different configurations\n- **Best practices** and common patterns\n- **Plugin development examples** with step-by-step guides\n\nThe playground source code also serves as a comprehensive reference for implementing various chart patterns and plugin combinations.\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\nAlso check out the [Discord](https://discord.gg/6wkpXQCNn6) community.\n\n## 📄 License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsnchz%2Fsolid-uplot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdsnchz%2Fsolid-uplot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsnchz%2Fsolid-uplot/lists"}