react-klinecharts-ui 0.6.0 → 1.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **react-klinecharts-ui** is a headless React library for building financial trading terminals on top of [klinecharts](https://github.com/liihuu/KLineChart). It provides a state provider, a set of hooks, and overlay templates. No UI components are included — use any UI framework you prefer.
4
4
 
5
- **[Live Demo](https://nemezzizz.github.io/react-klinecharts-ui/)**
5
+ **[Live Demo](https://nemezzizz.github.io/tradedash/)** · **[Examples](https://nemezzizz.github.io/react-klinecharts-ui/examples/)**
6
6
 
7
7
  ### Acknowledgments
8
8
 
@@ -62,13 +62,24 @@ Many features in this library — including 11 TradingView-style indicators, 9 d
62
62
  ## Installation
63
63
 
64
64
  ```bash
65
- npm install react-klinecharts-ui react-klinecharts
65
+ npm install react-klinecharts-ui klinecharts
66
66
  # or with pnpm
67
- pnpm add react-klinecharts-ui react-klinecharts
67
+ pnpm add react-klinecharts-ui klinecharts
68
68
  # or with yarn
69
- yarn add react-klinecharts-ui react-klinecharts
69
+ yarn add react-klinecharts-ui klinecharts
70
70
  ```
71
71
 
72
+ > **Rendering a chart?** `react-klinecharts-ui` is headless — it does not render
73
+ > the canvas itself. The fastest path is the optional `ChartCanvas` wrapper,
74
+ > which needs `react-klinecharts`:
75
+ >
76
+ > ```bash
77
+ > npm install react-klinecharts-ui klinecharts react-klinecharts
78
+ > ```
79
+ >
80
+ > You can also initialise the chart yourself with `klinecharts.init()` and skip
81
+ > `react-klinecharts` entirely — see [Renderer-agnostic](#renderer-agnostic).
82
+
72
83
  ---
73
84
 
74
85
  ## Concept
@@ -82,6 +93,170 @@ The library follows a **headless** pattern — all UI is written by the consumer
82
93
 
83
94
  All hooks must be called inside `<KlinechartsUIProvider>`.
84
95
 
96
+ ### Renderer-agnostic
97
+
98
+ `react-klinecharts-ui` is **headless** — it owns state (symbol, period, indicators, alerts, replay, …) and drives a klinecharts `Chart` instance, but it does **not** render the canvas. The only bridge between a renderer and the provider is a single dispatch:
99
+
100
+ ```ts
101
+ dispatch({ type: "SET_CHART", chart });
102
+ ```
103
+
104
+ Once the chart instance is registered, every hook (`useIndicators`, `useAlerts`, `useReplay`, …) reads and mutates it via `state.chart.*`. You can initialise that instance **three ways**:
105
+
106
+ **1. `ChartCanvas` (fastest, optional peer `react-klinecharts`)** — the thin wrapper shipped at `react-klinecharts-ui/chart` wires the `<KLineChart>` renderer, builds the data loader, forwards symbol/period/theme from provider state, and dispatches `SET_CHART` for you:
107
+
108
+ ```tsx
109
+ import { KlinechartsUIProvider } from "react-klinecharts-ui";
110
+ import { ChartCanvas } from "react-klinecharts-ui/chart";
111
+
112
+ <KlinechartsUIProvider datafeed={datafeed} defaultSymbol={symbol} defaultTheme="dark">
113
+ <ChartCanvas className="h-[500px]" />
114
+ </KlinechartsUIProvider>;
115
+ ```
116
+
117
+ **2. `<KLineChart>` from `react-klinecharts` directly** — full control over props, at the cost of writing the `onReady` bridge yourself:
118
+
119
+ ```tsx
120
+ import { useMemo } from "react";
121
+ import { KLineChart } from "react-klinecharts";
122
+ import { useKlinechartsUI, createDataLoader } from "react-klinecharts-ui";
123
+
124
+ function ChartView() {
125
+ const { state, dispatch, datafeed } = useKlinechartsUI();
126
+ const dataLoader = useMemo(() => createDataLoader(datafeed, dispatch), [datafeed, dispatch]);
127
+ return (
128
+ <KLineChart
129
+ dataLoader={dataLoader}
130
+ symbol={state.symbol ?? undefined}
131
+ period={state.period}
132
+ styles={state.theme}
133
+ onReady={(chart) => dispatch({ type: "SET_CHART", chart })}
134
+ />
135
+ );
136
+ }
137
+ ```
138
+
139
+ **3. Direct `klinecharts.init()` (no `react-klinecharts` at all)** — for custom lifecycles, SSR/Next.js, or when you want zero extra dependencies:
140
+
141
+ ```tsx
142
+ import { useEffect, useRef } from "react";
143
+ import { init, dispose } from "klinecharts";
144
+ import { useKlinechartsUI } from "react-klinecharts-ui";
145
+
146
+ function ChartView() {
147
+ const { dispatch, datafeed } = useKlinechartsUI();
148
+ const ref = useRef<HTMLDivElement>(null);
149
+ useEffect(() => {
150
+ const chart = init(ref.current!);
151
+ dispatch({ type: "SET_CHART", chart }); // the bridge
152
+ chart!.applyNewData(/* load bars yourself */);
153
+ return () => dispose(ref.current!);
154
+ }, []);
155
+ return <div ref={ref} style={{ height: 500 }} />;
156
+ }
157
+ ```
158
+
159
+ Whatever route you pick, the hooks work the same — they only care about the `Chart` instance in the store.
160
+
161
+ ### Persistence
162
+
163
+ By default, user-facing state (alerts, chart settings, the active indicator
164
+ set) lives only in memory and is **lost on page reload**. Pass a `storage`
165
+ option to the provider to hydrate it on mount and write it back on every
166
+ change. The adapter mirrors the Web Storage API, so `localStorage` works out
167
+ of the box:
168
+
169
+ ```tsx
170
+ <KlinechartsUIProvider datafeed={datafeed} storage={{}}>
171
+ {/* alerts / settings / indicators now survive refresh */}
172
+ </KlinechartsUIProvider>
173
+ ```
174
+
175
+ `storage={{}}` uses the defaults: `localStorage` adapter, the `alerts` /
176
+ `settings` / `indicators` namespaces, and a `"rkui:"` key prefix. You can
177
+ override any of them — e.g. plug in a remote or IndexedDB-backed adapter:
178
+
179
+ ```tsx
180
+ import type { StorageAdapter } from "react-klinecharts-ui";
181
+
182
+ const remoteAdapter: StorageAdapter = {
183
+ getItem: (key) => mySyncCache.get(key) ?? null,
184
+ setItem: (key, value) => {
185
+ mySyncCache.set(key, value);
186
+ flushToServer(key, value); // async, fire-and-forget
187
+ },
188
+ removeItem: (key) => { mySyncCache.delete(key); deleteFromServer(key); },
189
+ };
190
+
191
+ <KlinechartsUIProvider datafeed={datafeed} storage={{ adapter: remoteAdapter }}>
192
+ ```
193
+
194
+ **Scope.** The adapter covers the reducer store today: `alerts`, `settings`
195
+ (`useKlinechartsUISettings`), and `indicators` (the active lists, pane ids,
196
+ axis bindings, and visibility). Per-hook `useState` values — script code
197
+ (`useScriptEditor`), compared symbols (`useCompare`), watchlist, annotations —
198
+ are not yet covered by the adapter and remain in memory. `useLayoutManager`
199
+ (named snapshot presets) is orthogonal: it serializes the whole chart
200
+ (indicators + drawings + meta) on demand to its own keys, independent of the
201
+ live `storage` adapter.
202
+
203
+ The adapter contract is **synchronous** (matching the Web Storage API). For
204
+ async backends, keep a synchronous in-memory cache and flush in the
205
+ background — the provider reads/writes through `getItem`/`setItem` only.
206
+
207
+ ---
208
+
209
+ ### Workspace & multi-chart
210
+
211
+ `KlinechartsUIProvider` owns exactly one chart. To render a **grid** of charts
212
+ that share crosshair / scroll / zoom / symbol / period, wrap several providers
213
+ in a `WorkspaceProvider` and drop a `useChartSync` bridge inside each:
214
+
215
+ ```tsx
216
+ import {
217
+ KlinechartsUIProvider,
218
+ WorkspaceProvider,
219
+ useChartSync,
220
+ useWorkspace,
221
+ } from "react-klinecharts-ui";
222
+
223
+ const cells = [
224
+ { id: "a", symbol: { ticker: "BTCUSDT" }, period: { span: 1, type: "minute", label: "1m" } },
225
+ { id: "b", symbol: { ticker: "ETHUSDT" }, period: { span: 1, type: "minute", label: "1m" } },
226
+ ];
227
+
228
+ // Rendered inside each KlinechartsUIProvider — registers its chart with the
229
+ // workspace and mirrors viewport events to siblings.
230
+ function ChartSyncBridge({ cellId }: { cellId: string }) {
231
+ useChartSync({ cellId });
232
+ return null;
233
+ }
234
+
235
+ <WorkspaceProvider defaultCells={cells}>
236
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
237
+ {cells.map((c) => (
238
+ <KlinechartsUIProvider key={c.id} datafeed={datafeed} defaultSymbol={c.symbol} defaultPeriod={c.period}>
239
+ <ChartSyncBridge cellId={c.id} />
240
+ <ChartCanvas />
241
+ </KlinechartsUIProvider>
242
+ ))}
243
+ </div>
244
+ </WorkspaceProvider>
245
+ ```
246
+
247
+ `useChartSync` mirrors crosshair / scroll / zoom between the registered charts
248
+ using only the **public** klinecharts API (`executeAction`, `scrollToTimestamp`,
249
+ `setBarSpace`) — no internal `_chartStore`. A re-entrancy guard prevents
250
+ feedback loops. Per-channel sync can be disabled via the `sync` prop
251
+ (`{ scroll: false }`).
252
+
253
+ > **Scope of this foundation.** Each cell keeps its own alerts, replay, and
254
+ > drawings (per-provider). Hoisting shared alerts/replay/drawings to the
255
+ > workspace level, plus tabbed layouts and server-persisted workspaces, is
256
+ > planned. Read the
257
+ > [multi-chart example](https://nemezzizz.github.io/react-klinecharts-ui/examples/multi-chart/)
258
+ > for a runnable 2×2 grid.
259
+
85
260
  ---
86
261
 
87
262
  ## KlinechartsUIProvider
@@ -1480,7 +1655,6 @@ import { TA } from "react-klinecharts-ui";
1480
1655
  | `TA.atr` | `(highs: number[], lows: number[], closes: number[], period: number)` | `number[]` — Average True Range |
1481
1656
  | `TA.vwap` | `(highs: number[], lows: number[], closes: number[], volumes: number[])` | `number[]` — Volume Weighted Average Price |
1482
1657
  | `TA.cci` | `(highs: number[], lows: number[], closes: number[], period: number)` | `number[]` — Commodity Channel Index |
1483
- | `TA.stoch` | `(highs: number[], lows: number[], closes: number[], kPeriod?, kSmooth?, dPeriod?)` | `{ k, d }` — each `(number \| null)[]` |
1484
1658
 
1485
1659
  ---
1486
1660
 
@@ -1692,7 +1866,9 @@ interface OrderLineExtendData {
1692
1866
  }
1693
1867
 
1694
1868
  interface OrderLineLineStyle {
1695
- style?: "solid" | "dashed" | "dotted"; // Default: "dashed"
1869
+ // klinecharts only supports "solid" and "dashed"; a dotted effect is done
1870
+ // via `dashedValue` (e.g. [2, 2]).
1871
+ style?: "solid" | "dashed"; // Default: "dashed"
1696
1872
  width?: number; // Default: 1
1697
1873
  dashedValue?: [number, number]; // Default: [4, 2]
1698
1874
  }
@@ -2083,4 +2259,8 @@ export type {
2083
2259
  DepthOverlayExtendData,
2084
2260
  DepthOverlayRow,
2085
2261
  };
2262
+
2263
+ // Chart entry (optional — peer-depends on react-klinecharts).
2264
+ // import from "react-klinecharts-ui/chart"
2265
+ export { ChartCanvas, type ChartCanvasProps };
2086
2266
  ```
package/dist/chart.cjs ADDED
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ var chunkF24D6PWY_cjs = require('./chunk-F24D6PWY.cjs');
4
+ require('./chunk-Q7SFCCGT.cjs');
5
+ var react = require('react');
6
+ var reactKlinecharts = require('react-klinecharts');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ function ChartCanvas({
10
+ className,
11
+ children,
12
+ options,
13
+ styles
14
+ }) {
15
+ const { state, dispatch, datafeed } = chunkF24D6PWY_cjs.useKlinechartsUI();
16
+ const dataLoader = react.useMemo(
17
+ () => chunkF24D6PWY_cjs.createDataLoader(datafeed, dispatch),
18
+ [datafeed, dispatch]
19
+ );
20
+ const mainIndicatorsRef = react.useRef(state.mainIndicators);
21
+ const subIndicatorsRef = react.useRef(state.subIndicators);
22
+ react.useEffect(() => {
23
+ mainIndicatorsRef.current = state.mainIndicators;
24
+ subIndicatorsRef.current = state.subIndicators;
25
+ }, [state.mainIndicators, state.subIndicators]);
26
+ const handleReady = react.useCallback(
27
+ (chart) => {
28
+ dispatch({ type: "SET_CHART", chart });
29
+ mainIndicatorsRef.current.forEach((name) => {
30
+ chart.createIndicator(
31
+ { name, id: `main_${name}` },
32
+ { isStack: true, pane: { id: "candle_pane" } }
33
+ );
34
+ });
35
+ const subUpdates = {};
36
+ Object.keys(subIndicatorsRef.current).forEach((name) => {
37
+ const id = `sub_${name}`;
38
+ chart.createIndicator({ name, id });
39
+ const ind = chart.getIndicators({ id })[0];
40
+ if (ind?.paneId) subUpdates[name] = ind.paneId;
41
+ });
42
+ if (Object.keys(subUpdates).length > 0) {
43
+ dispatch({
44
+ type: "SET_SUB_INDICATORS",
45
+ indicators: { ...subIndicatorsRef.current, ...subUpdates }
46
+ });
47
+ }
48
+ },
49
+ [dispatch]
50
+ );
51
+ return /* @__PURE__ */ jsxRuntime.jsx(
52
+ reactKlinecharts.KLineChart,
53
+ {
54
+ className,
55
+ options,
56
+ styles: styles ?? state.theme,
57
+ dataLoader,
58
+ symbol: state.symbol ?? void 0,
59
+ period: state.period,
60
+ locale: state.locale,
61
+ timezone: state.timezone,
62
+ onReady: handleReady,
63
+ children
64
+ }
65
+ );
66
+ }
67
+
68
+ exports.ChartCanvas = ChartCanvas;
69
+ //# sourceMappingURL=chart.cjs.map
70
+ //# sourceMappingURL=chart.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chart/ChartCanvas.tsx"],"names":["useKlinechartsUI","useMemo","createDataLoader","useRef","useEffect","useCallback","jsx","KLineChart"],"mappings":";;;;;;;;AAsDO,SAAS,WAAA,CAAY;AAAA,EAC1B,SAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAgC;AAC9B,EAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAU,QAAA,KAAaA,kCAAA,EAAiB;AAEvD,EAAA,MAAM,UAAA,GAAaC,aAAA;AAAA,IACjB,MAAMC,kCAAA,CAAiB,QAAA,EAAU,QAAQ,CAAA;AAAA,IACzC,CAAC,UAAU,QAAQ;AAAA,GACrB;AAKA,EAAA,MAAM,iBAAA,GAAoBC,YAAA,CAAO,KAAA,CAAM,cAAc,CAAA;AACrD,EAAA,MAAM,gBAAA,GAAmBA,YAAA,CAAO,KAAA,CAAM,aAAa,CAAA;AACnD,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,iBAAA,CAAkB,UAAU,KAAA,CAAM,cAAA;AAClC,IAAA,gBAAA,CAAiB,UAAU,KAAA,CAAM,aAAA;AAAA,EACnC,GAAG,CAAC,KAAA,CAAM,cAAA,EAAgB,KAAA,CAAM,aAAa,CAAC,CAAA;AAE9C,EAAA,MAAM,WAAA,GAAcC,iBAAA;AAAA,IAClB,CAAC,KAAA,KAAiB;AAEhB,MAAA,QAAA,CAAS,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,CAAA;AAIrC,MAAA,iBAAA,CAAkB,OAAA,CAAQ,OAAA,CAAQ,CAAC,IAAA,KAAS;AAC1C,QAAA,KAAA,CAAM,eAAA;AAAA,UACJ,EAAE,IAAA,EAAM,EAAA,EAAI,CAAA,KAAA,EAAQ,IAAI,CAAA,CAAA,EAAG;AAAA,UAC3B,EAAE,OAAA,EAAS,IAAA,EAAM,MAAM,EAAE,EAAA,EAAI,eAAc;AAAE,SAC/C;AAAA,MACF,CAAC,CAAA;AAED,MAAA,MAAM,aAAqC,EAAC;AAC5C,MAAA,MAAA,CAAO,KAAK,gBAAA,CAAiB,OAAO,CAAA,CAAE,OAAA,CAAQ,CAAC,IAAA,KAAS;AACtD,QAAA,MAAM,EAAA,GAAK,OAAO,IAAI,CAAA,CAAA;AACtB,QAAA,KAAA,CAAM,eAAA,CAAgB,EAAE,IAAA,EAAM,EAAA,EAAI,CAAA;AAClC,QAAA,MAAM,MAAM,KAAA,CAAM,aAAA,CAAc,EAAE,EAAA,EAAI,EAAE,CAAC,CAAA;AACzC,QAAA,IAAI,GAAA,EAAK,MAAA,EAAQ,UAAA,CAAW,IAAI,IAAI,GAAA,CAAI,MAAA;AAAA,MAC1C,CAAC,CAAA;AACD,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,SAAS,CAAA,EAAG;AACtC,QAAA,QAAA,CAAS;AAAA,UACP,IAAA,EAAM,oBAAA;AAAA,UACN,YAAY,EAAE,GAAG,gBAAA,CAAiB,OAAA,EAAS,GAAG,UAAA;AAAW,SAC1D,CAAA;AAAA,MACH;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAA,uBACEC,cAAA;AAAA,IAACC,2BAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,OAAA;AAAA,MACA,MAAA,EAAQ,UAAU,KAAA,CAAM,KAAA;AAAA,MACxB,UAAA;AAAA,MACA,MAAA,EAAQ,MAAM,MAAA,IAAU,MAAA;AAAA,MACxB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,OAAA,EAAS,WAAA;AAAA,MAER;AAAA;AAAA,GACH;AAEJ","file":"chart.cjs","sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type ReactNode,\n} from \"react\";\nimport { KLineChart } from \"react-klinecharts\";\nimport type { Chart, Options, DeepPartial, Styles } from \"klinecharts\";\nimport {\n useKlinechartsUI,\n} from \"../provider/ChartTerminalContext\";\nimport { createDataLoader } from \"../utils/createDataLoader\";\n\nexport interface ChartCanvasProps {\n /** className applied to the chart container div. */\n className?: string;\n /**\n * Optional children rendered inside `<KLineChart>` (use the `<Widget>`\n * component from `react-klinecharts` to portal React into chart DOM layers).\n */\n children?: ReactNode;\n /**\n * Chart initialization options, applied once on mount. Forwarded to\n * `<KLineChart>` as the `options` prop.\n */\n options?: Options;\n /**\n * Styles override applied on mount and whenever it changes. Forwarded to\n * `<KLineChart>` as the `styles` prop.\n */\n styles?: string | DeepPartial<Styles>;\n}\n\n/**\n * Thin renderer wrapper for `react-klinecharts-ui`.\n *\n * `react-klinecharts-ui` is **headless / renderer-agnostic**: it only needs a\n * klinecharts `Chart` instance to be registered via\n * `dispatch({ type: \"SET_CHART\", chart })`. This component wires the\n * `<KLineChart>` renderer from `react-klinecharts` to that bridge for you —\n * building the data loader, forwarding symbol/period/theme/locale/timezone\n * from provider state, bootstrapping the default indicators, and dispatching\n * `SET_CHART` on ready.\n *\n * It is the fastest path to a working chart. For full control (custom\n * lifecycle, a different renderer, or direct `klinecharts.init()`), render the\n * chart yourself and dispatch `SET_CHART` — see the README \"Renderer-agnostic\"\n * section.\n *\n * This entry point requires `react-klinecharts` as an optional peer\n * dependency. Install it explicitly when you import from\n * `react-klinecharts-ui/chart`.\n */\nexport function ChartCanvas({\n className,\n children,\n options,\n styles,\n}: ChartCanvasProps): ReactNode {\n const { state, dispatch, datafeed } = useKlinechartsUI();\n\n const dataLoader = useMemo(\n () => createDataLoader(datafeed, dispatch),\n [datafeed, dispatch],\n );\n\n // Keep the latest default indicator lists in refs so `handleReady` stays\n // stable (only depends on `dispatch`) yet reads current values when the\n // chart finishes initializing.\n const mainIndicatorsRef = useRef(state.mainIndicators);\n const subIndicatorsRef = useRef(state.subIndicators);\n useEffect(() => {\n mainIndicatorsRef.current = state.mainIndicators;\n subIndicatorsRef.current = state.subIndicators;\n }, [state.mainIndicators, state.subIndicators]);\n\n const handleReady = useCallback(\n (chart: Chart) => {\n // The bridge: register the chart instance so every hook can drive it.\n dispatch({ type: \"SET_CHART\", chart });\n\n // Bootstrap the provider's default indicators onto the fresh chart,\n // mirroring the canonical pattern in examples/ChartView.tsx.\n mainIndicatorsRef.current.forEach((name) => {\n chart.createIndicator(\n { name, id: `main_${name}` },\n { isStack: true, pane: { id: \"candle_pane\" } },\n );\n });\n\n const subUpdates: Record<string, string> = {};\n Object.keys(subIndicatorsRef.current).forEach((name) => {\n const id = `sub_${name}`;\n chart.createIndicator({ name, id });\n const ind = chart.getIndicators({ id })[0];\n if (ind?.paneId) subUpdates[name] = ind.paneId;\n });\n if (Object.keys(subUpdates).length > 0) {\n dispatch({\n type: \"SET_SUB_INDICATORS\",\n indicators: { ...subIndicatorsRef.current, ...subUpdates },\n });\n }\n },\n [dispatch],\n );\n\n return (\n <KLineChart\n className={className}\n options={options}\n styles={styles ?? state.theme}\n dataLoader={dataLoader}\n symbol={state.symbol ?? undefined}\n period={state.period}\n locale={state.locale}\n timezone={state.timezone}\n onReady={handleReady}\n >\n {children}\n </KLineChart>\n );\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { ReactNode } from 'react';
2
+ import { Options, DeepPartial, Styles } from 'klinecharts';
3
+
4
+ interface ChartCanvasProps {
5
+ /** className applied to the chart container div. */
6
+ className?: string;
7
+ /**
8
+ * Optional children rendered inside `<KLineChart>` (use the `<Widget>`
9
+ * component from `react-klinecharts` to portal React into chart DOM layers).
10
+ */
11
+ children?: ReactNode;
12
+ /**
13
+ * Chart initialization options, applied once on mount. Forwarded to
14
+ * `<KLineChart>` as the `options` prop.
15
+ */
16
+ options?: Options;
17
+ /**
18
+ * Styles override applied on mount and whenever it changes. Forwarded to
19
+ * `<KLineChart>` as the `styles` prop.
20
+ */
21
+ styles?: string | DeepPartial<Styles>;
22
+ }
23
+ /**
24
+ * Thin renderer wrapper for `react-klinecharts-ui`.
25
+ *
26
+ * `react-klinecharts-ui` is **headless / renderer-agnostic**: it only needs a
27
+ * klinecharts `Chart` instance to be registered via
28
+ * `dispatch({ type: "SET_CHART", chart })`. This component wires the
29
+ * `<KLineChart>` renderer from `react-klinecharts` to that bridge for you —
30
+ * building the data loader, forwarding symbol/period/theme/locale/timezone
31
+ * from provider state, bootstrapping the default indicators, and dispatching
32
+ * `SET_CHART` on ready.
33
+ *
34
+ * It is the fastest path to a working chart. For full control (custom
35
+ * lifecycle, a different renderer, or direct `klinecharts.init()`), render the
36
+ * chart yourself and dispatch `SET_CHART` — see the README "Renderer-agnostic"
37
+ * section.
38
+ *
39
+ * This entry point requires `react-klinecharts` as an optional peer
40
+ * dependency. Install it explicitly when you import from
41
+ * `react-klinecharts-ui/chart`.
42
+ */
43
+ declare function ChartCanvas({ className, children, options, styles, }: ChartCanvasProps): ReactNode;
44
+
45
+ export { ChartCanvas, type ChartCanvasProps };
@@ -0,0 +1,45 @@
1
+ import { ReactNode } from 'react';
2
+ import { Options, DeepPartial, Styles } from 'klinecharts';
3
+
4
+ interface ChartCanvasProps {
5
+ /** className applied to the chart container div. */
6
+ className?: string;
7
+ /**
8
+ * Optional children rendered inside `<KLineChart>` (use the `<Widget>`
9
+ * component from `react-klinecharts` to portal React into chart DOM layers).
10
+ */
11
+ children?: ReactNode;
12
+ /**
13
+ * Chart initialization options, applied once on mount. Forwarded to
14
+ * `<KLineChart>` as the `options` prop.
15
+ */
16
+ options?: Options;
17
+ /**
18
+ * Styles override applied on mount and whenever it changes. Forwarded to
19
+ * `<KLineChart>` as the `styles` prop.
20
+ */
21
+ styles?: string | DeepPartial<Styles>;
22
+ }
23
+ /**
24
+ * Thin renderer wrapper for `react-klinecharts-ui`.
25
+ *
26
+ * `react-klinecharts-ui` is **headless / renderer-agnostic**: it only needs a
27
+ * klinecharts `Chart` instance to be registered via
28
+ * `dispatch({ type: "SET_CHART", chart })`. This component wires the
29
+ * `<KLineChart>` renderer from `react-klinecharts` to that bridge for you —
30
+ * building the data loader, forwarding symbol/period/theme/locale/timezone
31
+ * from provider state, bootstrapping the default indicators, and dispatching
32
+ * `SET_CHART` on ready.
33
+ *
34
+ * It is the fastest path to a working chart. For full control (custom
35
+ * lifecycle, a different renderer, or direct `klinecharts.init()`), render the
36
+ * chart yourself and dispatch `SET_CHART` — see the README "Renderer-agnostic"
37
+ * section.
38
+ *
39
+ * This entry point requires `react-klinecharts` as an optional peer
40
+ * dependency. Install it explicitly when you import from
41
+ * `react-klinecharts-ui/chart`.
42
+ */
43
+ declare function ChartCanvas({ className, children, options, styles, }: ChartCanvasProps): ReactNode;
44
+
45
+ export { ChartCanvas, type ChartCanvasProps };
package/dist/chart.js ADDED
@@ -0,0 +1,68 @@
1
+ import { useKlinechartsUI, createDataLoader } from './chunk-O7E57I66.js';
2
+ import './chunk-PZ5AY32C.js';
3
+ import { useMemo, useRef, useEffect, useCallback } from 'react';
4
+ import { KLineChart } from 'react-klinecharts';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ function ChartCanvas({
8
+ className,
9
+ children,
10
+ options,
11
+ styles
12
+ }) {
13
+ const { state, dispatch, datafeed } = useKlinechartsUI();
14
+ const dataLoader = useMemo(
15
+ () => createDataLoader(datafeed, dispatch),
16
+ [datafeed, dispatch]
17
+ );
18
+ const mainIndicatorsRef = useRef(state.mainIndicators);
19
+ const subIndicatorsRef = useRef(state.subIndicators);
20
+ useEffect(() => {
21
+ mainIndicatorsRef.current = state.mainIndicators;
22
+ subIndicatorsRef.current = state.subIndicators;
23
+ }, [state.mainIndicators, state.subIndicators]);
24
+ const handleReady = useCallback(
25
+ (chart) => {
26
+ dispatch({ type: "SET_CHART", chart });
27
+ mainIndicatorsRef.current.forEach((name) => {
28
+ chart.createIndicator(
29
+ { name, id: `main_${name}` },
30
+ { isStack: true, pane: { id: "candle_pane" } }
31
+ );
32
+ });
33
+ const subUpdates = {};
34
+ Object.keys(subIndicatorsRef.current).forEach((name) => {
35
+ const id = `sub_${name}`;
36
+ chart.createIndicator({ name, id });
37
+ const ind = chart.getIndicators({ id })[0];
38
+ if (ind?.paneId) subUpdates[name] = ind.paneId;
39
+ });
40
+ if (Object.keys(subUpdates).length > 0) {
41
+ dispatch({
42
+ type: "SET_SUB_INDICATORS",
43
+ indicators: { ...subIndicatorsRef.current, ...subUpdates }
44
+ });
45
+ }
46
+ },
47
+ [dispatch]
48
+ );
49
+ return /* @__PURE__ */ jsx(
50
+ KLineChart,
51
+ {
52
+ className,
53
+ options,
54
+ styles: styles ?? state.theme,
55
+ dataLoader,
56
+ symbol: state.symbol ?? void 0,
57
+ period: state.period,
58
+ locale: state.locale,
59
+ timezone: state.timezone,
60
+ onReady: handleReady,
61
+ children
62
+ }
63
+ );
64
+ }
65
+
66
+ export { ChartCanvas };
67
+ //# sourceMappingURL=chart.js.map
68
+ //# sourceMappingURL=chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chart/ChartCanvas.tsx"],"names":[],"mappings":";;;;;;AAsDO,SAAS,WAAA,CAAY;AAAA,EAC1B,SAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAgC;AAC9B,EAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAU,QAAA,KAAa,gBAAA,EAAiB;AAEvD,EAAA,MAAM,UAAA,GAAa,OAAA;AAAA,IACjB,MAAM,gBAAA,CAAiB,QAAA,EAAU,QAAQ,CAAA;AAAA,IACzC,CAAC,UAAU,QAAQ;AAAA,GACrB;AAKA,EAAA,MAAM,iBAAA,GAAoB,MAAA,CAAO,KAAA,CAAM,cAAc,CAAA;AACrD,EAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,KAAA,CAAM,aAAa,CAAA;AACnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,iBAAA,CAAkB,UAAU,KAAA,CAAM,cAAA;AAClC,IAAA,gBAAA,CAAiB,UAAU,KAAA,CAAM,aAAA;AAAA,EACnC,GAAG,CAAC,KAAA,CAAM,cAAA,EAAgB,KAAA,CAAM,aAAa,CAAC,CAAA;AAE9C,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,KAAA,KAAiB;AAEhB,MAAA,QAAA,CAAS,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,CAAA;AAIrC,MAAA,iBAAA,CAAkB,OAAA,CAAQ,OAAA,CAAQ,CAAC,IAAA,KAAS;AAC1C,QAAA,KAAA,CAAM,eAAA;AAAA,UACJ,EAAE,IAAA,EAAM,EAAA,EAAI,CAAA,KAAA,EAAQ,IAAI,CAAA,CAAA,EAAG;AAAA,UAC3B,EAAE,OAAA,EAAS,IAAA,EAAM,MAAM,EAAE,EAAA,EAAI,eAAc;AAAE,SAC/C;AAAA,MACF,CAAC,CAAA;AAED,MAAA,MAAM,aAAqC,EAAC;AAC5C,MAAA,MAAA,CAAO,KAAK,gBAAA,CAAiB,OAAO,CAAA,CAAE,OAAA,CAAQ,CAAC,IAAA,KAAS;AACtD,QAAA,MAAM,EAAA,GAAK,OAAO,IAAI,CAAA,CAAA;AACtB,QAAA,KAAA,CAAM,eAAA,CAAgB,EAAE,IAAA,EAAM,EAAA,EAAI,CAAA;AAClC,QAAA,MAAM,MAAM,KAAA,CAAM,aAAA,CAAc,EAAE,EAAA,EAAI,EAAE,CAAC,CAAA;AACzC,QAAA,IAAI,GAAA,EAAK,MAAA,EAAQ,UAAA,CAAW,IAAI,IAAI,GAAA,CAAI,MAAA;AAAA,MAC1C,CAAC,CAAA;AACD,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,SAAS,CAAA,EAAG;AACtC,QAAA,QAAA,CAAS;AAAA,UACP,IAAA,EAAM,oBAAA;AAAA,UACN,YAAY,EAAE,GAAG,gBAAA,CAAiB,OAAA,EAAS,GAAG,UAAA;AAAW,SAC1D,CAAA;AAAA,MACH;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,OAAA;AAAA,MACA,MAAA,EAAQ,UAAU,KAAA,CAAM,KAAA;AAAA,MACxB,UAAA;AAAA,MACA,MAAA,EAAQ,MAAM,MAAA,IAAU,MAAA;AAAA,MACxB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,OAAA,EAAS,WAAA;AAAA,MAER;AAAA;AAAA,GACH;AAEJ","file":"chart.js","sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type ReactNode,\n} from \"react\";\nimport { KLineChart } from \"react-klinecharts\";\nimport type { Chart, Options, DeepPartial, Styles } from \"klinecharts\";\nimport {\n useKlinechartsUI,\n} from \"../provider/ChartTerminalContext\";\nimport { createDataLoader } from \"../utils/createDataLoader\";\n\nexport interface ChartCanvasProps {\n /** className applied to the chart container div. */\n className?: string;\n /**\n * Optional children rendered inside `<KLineChart>` (use the `<Widget>`\n * component from `react-klinecharts` to portal React into chart DOM layers).\n */\n children?: ReactNode;\n /**\n * Chart initialization options, applied once on mount. Forwarded to\n * `<KLineChart>` as the `options` prop.\n */\n options?: Options;\n /**\n * Styles override applied on mount and whenever it changes. Forwarded to\n * `<KLineChart>` as the `styles` prop.\n */\n styles?: string | DeepPartial<Styles>;\n}\n\n/**\n * Thin renderer wrapper for `react-klinecharts-ui`.\n *\n * `react-klinecharts-ui` is **headless / renderer-agnostic**: it only needs a\n * klinecharts `Chart` instance to be registered via\n * `dispatch({ type: \"SET_CHART\", chart })`. This component wires the\n * `<KLineChart>` renderer from `react-klinecharts` to that bridge for you —\n * building the data loader, forwarding symbol/period/theme/locale/timezone\n * from provider state, bootstrapping the default indicators, and dispatching\n * `SET_CHART` on ready.\n *\n * It is the fastest path to a working chart. For full control (custom\n * lifecycle, a different renderer, or direct `klinecharts.init()`), render the\n * chart yourself and dispatch `SET_CHART` — see the README \"Renderer-agnostic\"\n * section.\n *\n * This entry point requires `react-klinecharts` as an optional peer\n * dependency. Install it explicitly when you import from\n * `react-klinecharts-ui/chart`.\n */\nexport function ChartCanvas({\n className,\n children,\n options,\n styles,\n}: ChartCanvasProps): ReactNode {\n const { state, dispatch, datafeed } = useKlinechartsUI();\n\n const dataLoader = useMemo(\n () => createDataLoader(datafeed, dispatch),\n [datafeed, dispatch],\n );\n\n // Keep the latest default indicator lists in refs so `handleReady` stays\n // stable (only depends on `dispatch`) yet reads current values when the\n // chart finishes initializing.\n const mainIndicatorsRef = useRef(state.mainIndicators);\n const subIndicatorsRef = useRef(state.subIndicators);\n useEffect(() => {\n mainIndicatorsRef.current = state.mainIndicators;\n subIndicatorsRef.current = state.subIndicators;\n }, [state.mainIndicators, state.subIndicators]);\n\n const handleReady = useCallback(\n (chart: Chart) => {\n // The bridge: register the chart instance so every hook can drive it.\n dispatch({ type: \"SET_CHART\", chart });\n\n // Bootstrap the provider's default indicators onto the fresh chart,\n // mirroring the canonical pattern in examples/ChartView.tsx.\n mainIndicatorsRef.current.forEach((name) => {\n chart.createIndicator(\n { name, id: `main_${name}` },\n { isStack: true, pane: { id: \"candle_pane\" } },\n );\n });\n\n const subUpdates: Record<string, string> = {};\n Object.keys(subIndicatorsRef.current).forEach((name) => {\n const id = `sub_${name}`;\n chart.createIndicator({ name, id });\n const ind = chart.getIndicators({ id })[0];\n if (ind?.paneId) subUpdates[name] = ind.paneId;\n });\n if (Object.keys(subUpdates).length > 0) {\n dispatch({\n type: \"SET_SUB_INDICATORS\",\n indicators: { ...subIndicatorsRef.current, ...subUpdates },\n });\n }\n },\n [dispatch],\n );\n\n return (\n <KLineChart\n className={className}\n options={options}\n styles={styles ?? state.theme}\n dataLoader={dataLoader}\n symbol={state.symbol ?? undefined}\n period={state.period}\n locale={state.locale}\n timezone={state.timezone}\n onReady={handleReady}\n >\n {children}\n </KLineChart>\n );\n}\n"]}
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/provider/ChartTerminalContext.ts
6
+ var KlinechartsUIStateContext = react.createContext(null);
7
+ var KlinechartsUIDispatchContext = react.createContext(null);
8
+ function useKlinechartsUIDispatch() {
9
+ const ctx = react.useContext(KlinechartsUIDispatchContext);
10
+ if (!ctx) {
11
+ throw new Error(
12
+ "useKlinechartsUI must be used within a <KlinechartsUIProvider>"
13
+ );
14
+ }
15
+ return ctx;
16
+ }
17
+ function useKlinechartsUI() {
18
+ const state = react.useContext(KlinechartsUIStateContext);
19
+ const dispatch = react.useContext(KlinechartsUIDispatchContext);
20
+ if (!state || !dispatch) {
21
+ throw new Error(
22
+ "useKlinechartsUI must be used within a <KlinechartsUIProvider>"
23
+ );
24
+ }
25
+ return { state, ...dispatch };
26
+ }
27
+
28
+ // src/utils/createDataLoader.ts
29
+ function createDataLoader(datafeed, dispatch) {
30
+ let oldestTimestamp = null;
31
+ let currentGen = 0;
32
+ return {
33
+ getBars: async (params) => {
34
+ try {
35
+ dispatch({ type: "SET_LOADING", isLoading: true });
36
+ if (params.type === "init") {
37
+ oldestTimestamp = null;
38
+ const gen = ++currentGen;
39
+ const data = await datafeed.getHistoryKLineData(
40
+ params.symbol,
41
+ { ...params.period, label: "" },
42
+ 0,
43
+ Date.now()
44
+ );
45
+ if (gen !== currentGen) return;
46
+ if (data.length > 0) {
47
+ oldestTimestamp = data[0].timestamp;
48
+ }
49
+ params.callback(data, {
50
+ forward: data.length > 0,
51
+ backward: false
52
+ });
53
+ } else if (params.type === "forward" && oldestTimestamp !== null) {
54
+ const gen = currentGen;
55
+ const data = await datafeed.getHistoryKLineData(
56
+ params.symbol,
57
+ { ...params.period, label: "" },
58
+ 0,
59
+ oldestTimestamp - 1
60
+ );
61
+ if (gen !== currentGen) return;
62
+ if (data.length > 0) {
63
+ oldestTimestamp = data[0].timestamp;
64
+ }
65
+ params.callback(data, {
66
+ forward: data.length > 0,
67
+ backward: false
68
+ });
69
+ } else if (params.type === "backward") {
70
+ params.callback([], { forward: false, backward: false });
71
+ }
72
+ } catch (error) {
73
+ console.error("Failed to load chart data:", error);
74
+ params.callback([], { forward: false, backward: false });
75
+ } finally {
76
+ dispatch({ type: "SET_LOADING", isLoading: false });
77
+ }
78
+ },
79
+ subscribeBar: (params) => {
80
+ datafeed.subscribe(
81
+ params.symbol,
82
+ { ...params.period, label: "" },
83
+ (klineData) => params.callback(klineData)
84
+ );
85
+ },
86
+ unsubscribeBar: (params) => {
87
+ datafeed.unsubscribe(params.symbol, { ...params.period, label: "" });
88
+ }
89
+ };
90
+ }
91
+
92
+ exports.KlinechartsUIDispatchContext = KlinechartsUIDispatchContext;
93
+ exports.KlinechartsUIStateContext = KlinechartsUIStateContext;
94
+ exports.createDataLoader = createDataLoader;
95
+ exports.useKlinechartsUI = useKlinechartsUI;
96
+ exports.useKlinechartsUIDispatch = useKlinechartsUIDispatch;
97
+ //# sourceMappingURL=chunk-F24D6PWY.cjs.map
98
+ //# sourceMappingURL=chunk-F24D6PWY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider/ChartTerminalContext.ts","../src/utils/createDataLoader.ts"],"names":["createContext","useContext"],"mappings":"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","file":"chunk-F24D6PWY.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type {\n KlinechartsUIState,\n KlinechartsUIContextValue,\n KlinechartsUIDispatchValue,\n} from \"./types\";\n\nexport const KlinechartsUIStateContext =\n createContext<KlinechartsUIState | null>(null);\n\nexport const KlinechartsUIDispatchContext =\n createContext<KlinechartsUIDispatchValue | null>(null);\n\n/**\n * @internal\n * Reads only the stable dispatch slice. Hooks that use this won't re-render\n * when chart state changes (e.g. symbol, period, isLoading).\n */\nexport function useKlinechartsUIDispatch(): KlinechartsUIDispatchValue {\n const ctx = useContext(KlinechartsUIDispatchContext);\n if (!ctx) {\n throw new Error(\n \"useKlinechartsUI must be used within a <KlinechartsUIProvider>\"\n );\n }\n return ctx;\n}\n\n/**\n * Primary hook — returns the full combined context value.\n * For performance-sensitive cases, prefer the two split hooks internally.\n */\nexport function useKlinechartsUI(): KlinechartsUIContextValue {\n const state = useContext(KlinechartsUIStateContext);\n const dispatch = useContext(KlinechartsUIDispatchContext);\n if (!state || !dispatch) {\n throw new Error(\n \"useKlinechartsUI must be used within a <KlinechartsUIProvider>\"\n );\n }\n return { state, ...dispatch };\n}\n","import type { Dispatch } from \"react\";\nimport type { DataLoader, KLineData } from \"klinecharts\";\nimport type { Datafeed, KlinechartsUIAction } from \"../provider/types\";\n\n/**\n * Creates a klinecharts DataLoader from a Datafeed instance.\n * This bridges the react-klinecharts-ui Datafeed interface to the\n * klinecharts native DataLoader format used by KLineChart component.\n *\n * In klinecharts terminology:\n * - \"init\" = initial data load\n * - \"forward\" = load older data (user scrolled left into history)\n * - \"backward\" = load newer data (user scrolled right)\n */\nexport function createDataLoader(\n datafeed: Datafeed,\n dispatch: Dispatch<KlinechartsUIAction>,\n): DataLoader {\n let oldestTimestamp: number | null = null;\n // Incremented on every \"init\" request. Forward requests capture the value at\n // their start and bail out if a newer init has begun while they were in-flight.\n let currentGen = 0;\n\n return {\n getBars: async (params) => {\n try {\n dispatch({ type: \"SET_LOADING\", isLoading: true });\n\n if (params.type === \"init\") {\n oldestTimestamp = null;\n const gen = ++currentGen;\n const data = await datafeed.getHistoryKLineData(\n params.symbol,\n { ...params.period, label: \"\" },\n 0,\n Date.now(),\n );\n if (gen !== currentGen) return; // newer init started — discard stale result\n if (data.length > 0) {\n oldestTimestamp = data[0].timestamp;\n }\n params.callback(data as KLineData[], {\n forward: data.length > 0,\n backward: false,\n });\n } else if (params.type === \"forward\" && oldestTimestamp !== null) {\n const gen = currentGen;\n const data = await datafeed.getHistoryKLineData(\n params.symbol,\n { ...params.period, label: \"\" },\n 0,\n oldestTimestamp - 1,\n );\n if (gen !== currentGen) return; // init for new period started — discard\n if (data.length > 0) {\n oldestTimestamp = data[0].timestamp;\n }\n params.callback(data as KLineData[], {\n forward: data.length > 0,\n backward: false,\n });\n } else if (params.type === \"backward\") {\n // Backward pagination (loading data newer than what we have) is generally\n // not needed in typical terminal usage because we subscribe via ws for real-time.\n params.callback([], { forward: false, backward: false });\n }\n } catch (error) {\n console.error(\"Failed to load chart data:\", error);\n params.callback([], { forward: false, backward: false });\n } finally {\n dispatch({ type: \"SET_LOADING\", isLoading: false });\n }\n },\n subscribeBar: (params) => {\n datafeed.subscribe(\n params.symbol,\n { ...params.period, label: \"\" },\n (klineData) => params.callback(klineData as KLineData),\n );\n },\n unsubscribeBar: (params) => {\n datafeed.unsubscribe(params.symbol, { ...params.period, label: \"\" });\n },\n };\n}\n"]}