@livo-build/charts 0.2.1 → 0.2.2
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 +107 -15
- package/dist/core/chart.d.ts +61 -2
- package/dist/core/chart.js +363 -32
- package/dist/core/format.d.ts +13 -1
- package/dist/core/format.js +38 -3
- package/dist/core/indicators.d.ts +14 -0
- package/dist/core/indicators.js +62 -0
- package/dist/core/renderer.d.ts +81 -1
- package/dist/core/renderer.js +344 -57
- package/dist/core/theme.d.ts +5 -1
- package/dist/core/theme.js +33 -12
- package/dist/core/types.d.ts +78 -2
- package/dist/index.d.ts +6 -6
- package/dist/index.js +4 -4
- package/dist/react/HyperliquidChart.d.ts +28 -2
- package/dist/react/HyperliquidChart.js +39 -17
- package/dist/react/PriceChart.d.ts +39 -4
- package/dist/react/PriceChart.js +52 -25
- package/dist/react/ui.d.ts +13 -0
- package/dist/react/ui.js +29 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @livo-build/charts (livo-charts)
|
|
2
2
|
|
|
3
|
+
> **Live demos + docs: [charts.livo.build](https://charts.livo.build)** — interactive
|
|
4
|
+
> examples with copy-paste code, plus agent-friendly [llms.txt](https://charts.livo.build/llms.txt)
|
|
5
|
+
> / [llms-full.txt](https://charts.livo.build/llms-full.txt).
|
|
6
|
+
|
|
3
7
|
A lightweight, **dependency-free** canvas charting library. The core renders to a
|
|
4
8
|
`<canvas>` with zero runtime dependencies; a thin, self-styled React wrapper is
|
|
5
9
|
shipped under a separate entry so non-React consumers never pull React into their
|
|
@@ -7,8 +11,8 @@ bundle.
|
|
|
7
11
|
|
|
8
12
|
It ships a **TradingView-style trading chart**: candlesticks **or** a line series, a
|
|
9
13
|
**volume panel**, a **crosshair with price + time axis labels**, an **OHLCV legend**,
|
|
10
|
-
independent **X and Y zoom** (+ pan), and a toolbar for **intervals,
|
|
11
|
-
price/market-cap and fullscreen**. The architecture (a framework-agnostic `Chart`
|
|
14
|
+
independent **X and Y zoom** (+ pan, plus touch/pinch), and a toolbar for **intervals,
|
|
15
|
+
USD/ETH, price/market-cap, log scale and fullscreen**. The architecture (a framework-agnostic `Chart`
|
|
12
16
|
controller + a pure `draw()` pass) is built to keep growing — indicators, overlays,
|
|
13
17
|
multiple panes — without touching consumers.
|
|
14
18
|
|
|
@@ -28,8 +32,7 @@ import { PriceChart } from "@livo-build/charts/react";
|
|
|
28
32
|
<PriceChart
|
|
29
33
|
swaps={trades} // [{ t: unixSeconds, p: priceUsd, v: usdVolume }, …]
|
|
30
34
|
ethUsd={1711.81} // enables the USD↔ETH toggle
|
|
31
|
-
|
|
32
|
-
decimals={18}
|
|
35
|
+
supply={1_000_000_000} // enables the Price↔MCap toggle (or tokenAddress to read it)
|
|
33
36
|
symbol="PEPE"
|
|
34
37
|
quote="WETH"
|
|
35
38
|
height={420}
|
|
@@ -37,14 +40,15 @@ import { PriceChart } from "@livo-build/charts/react";
|
|
|
37
40
|
```
|
|
38
41
|
|
|
39
42
|
`swaps` are aggregated into OHLC+volume candles client-side. The toolbar
|
|
40
|
-
(`1s 1m 5m 15m 1h 4h 1d`, candle/line, Price/MCap, USD/ETH, flip, fullscreen) and the
|
|
43
|
+
(`1s 1m 5m 15m 1h 4h 1d`, candle/line, Price/MCap, USD/ETH, flip, log, fullscreen) and the
|
|
41
44
|
OHLCV legend are built in. The canvas is long-lived, so zoom/pan survive prop updates
|
|
42
45
|
(streaming trades won't reset the user's view). The wrapper is styled inline — no CSS
|
|
43
46
|
framework required.
|
|
44
47
|
|
|
45
|
-
Market cap = `price ×
|
|
46
|
-
`https://cloudflare-eth.com
|
|
47
|
-
is
|
|
48
|
+
Market cap = `price × supply`. Pass `supply` directly, or pass `tokenAddress` (+`decimals`)
|
|
49
|
+
to read `totalSupply()` once via `rpcUrl` (default `https://cloudflare-eth.com`, cached).
|
|
50
|
+
The **Price/MCap toggle only appears when one of those is set** — otherwise it's hidden
|
|
51
|
+
(no dead button), and it falls back to price if the RPC read fails.
|
|
48
52
|
|
|
49
53
|
### Live Hyperliquid chart
|
|
50
54
|
|
|
@@ -71,12 +75,82 @@ API key. Zoom/pan survive every update.
|
|
|
71
75
|
### Indicators
|
|
72
76
|
|
|
73
77
|
`PriceChart` and `HyperliquidChart` accept `indicators` — overlays drawn on the price
|
|
74
|
-
pane. Each is `{ type: "sma" | "ema" | "wma" | "vwap", period, color?, source? }`
|
|
75
|
-
(`source` defaults to `close`; `vwap` is cumulative
|
|
78
|
+
pane. Each is `{ type: "sma" | "ema" | "wma" | "vwap" | "bollinger", period, color?, source?, mult? }`
|
|
79
|
+
(`source` defaults to `close`; `vwap` is cumulative; `bollinger` draws a 3-line band at
|
|
80
|
+
`mult` std-devs, default 2).
|
|
76
81
|
Colors fall back to a built-in palette by position. The pure
|
|
77
82
|
`sma`/`ema`/`wma`/`vwap`/`bollingerBands`/`computeIndicator` helpers are exported from
|
|
78
83
|
the core (e.g. feed `bollingerBands(values).{upper,mid,lower}` as three overlays).
|
|
79
84
|
|
|
85
|
+
**Oscillators (RSI / MACD)** render in their own **stacked sub-panes** below the volume
|
|
86
|
+
panel — pass `oscillators`:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<PriceChart
|
|
90
|
+
swaps={trades}
|
|
91
|
+
indicators={[{ type: "ema", period: 21 }]} // on the price pane
|
|
92
|
+
oscillators={[
|
|
93
|
+
{ type: "rsi", period: 14 }, // 0–100 band with 70/30 rails
|
|
94
|
+
{ type: "macd", fast: 12, slow: 26, signal: 9 }, // line + signal + histogram
|
|
95
|
+
]}
|
|
96
|
+
/>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Each pane defaults to 84px (`height` to override). The pure `rsi(values, period)` and
|
|
100
|
+
`macd(values, fast, slow, signal) → { macd, signal, hist }` helpers are exported too.
|
|
101
|
+
|
|
102
|
+
### Fitting the container & axis styling
|
|
103
|
+
|
|
104
|
+
By default `PriceChart` / `HyperliquidChart` **fit the container** (`fitContent`) — candles
|
|
105
|
+
spread across the full plot width instead of clustering at a capped slot. Set
|
|
106
|
+
`fitContent={false}` for the tight, right-anchored look (the low-level `Chart` defaults to
|
|
107
|
+
tight; the React wrappers default to fill).
|
|
108
|
+
|
|
109
|
+
The ticks are easy to style — every axis knob is a prop (and a `chart.setAxis({…})` call):
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<PriceChart
|
|
113
|
+
swaps={trades}
|
|
114
|
+
priceFormat={(v) => `$${v.toLocaleString()}`} // y-axis labels
|
|
115
|
+
timeFormat={(t) => new Date(t * 1000).toLocaleTimeString()}
|
|
116
|
+
priceTicks={6} // horizontal gridlines (default 5)
|
|
117
|
+
timeTicks={8} // x-axis labels (default 7)
|
|
118
|
+
axisFont="11px Inter, sans-serif" // label font (color = theme.axis)
|
|
119
|
+
/>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The **default** price formatter is range-aware: it keeps just enough decimals to tell
|
|
123
|
+
adjacent ticks apart, so a narrow high-value axis (e.g. WETH 1.71K–1.75K) renders
|
|
124
|
+
`1.7350K / 1.7430K` instead of duplicate `1.73K`s. Tick text color comes from `theme.axis`.
|
|
125
|
+
|
|
126
|
+
### Log scale & themes
|
|
127
|
+
|
|
128
|
+
The price axis can be **logarithmic** — equal vertical distance = equal % move, the
|
|
129
|
+
right default for assets that span orders of magnitude. `PriceChart`/`HyperliquidChart`
|
|
130
|
+
expose a **Log** toolbar toggle (or pass `options={{ logScale: true }}`); the core has
|
|
131
|
+
`chart.setLogScale(true)`. Log mode auto-falls back to linear if any visible low is ≤ 0.
|
|
132
|
+
|
|
133
|
+
Two built-in themes ship as `DEFAULT_THEME` (dark) and `LIGHT_THEME`, also addressable
|
|
134
|
+
via `THEME_PRESETS.dark` / `THEME_PRESETS.light`. Pass either (or a partial override) as
|
|
135
|
+
`theme`, or call `chart.setTheme(LIGHT_THEME)`.
|
|
136
|
+
|
|
137
|
+
### Drawing tools (trendlines & horizontal lines)
|
|
138
|
+
|
|
139
|
+
The toolbar's **↗** (trendline) and **—** (horizontal line) buttons arm a one-shot draw:
|
|
140
|
+
drag for a trendline, click for a horizontal price line. Drawings are anchored in **data
|
|
141
|
+
space**, so they stay pinned to their price/time across pan, zoom and live updates. In the
|
|
142
|
+
default cursor mode, click a line to select it, drag to move it, and **double-click a line
|
|
143
|
+
to delete it**; **⌫** clears all.
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<PriceChart swaps={trades} onDrawingsChange={(d) => save(d)} drawings={restored} />
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Drive it from the core too: `chart.setDrawMode("trendline" | "hline" | "none")`,
|
|
150
|
+
`getDrawings()` / `setDrawings()` / `removeDrawing(id)` / `clearDrawings()`, and the
|
|
151
|
+
`onDrawingsChange` callback. Pass `hideDrawingTools` to drop the buttons. The
|
|
152
|
+
`computeProjection(input)` helper exposes the same pixel↔data mapping for custom tools.
|
|
153
|
+
|
|
80
154
|
### Realtime feeds + lazy history (any data source)
|
|
81
155
|
|
|
82
156
|
Connect a chart to a **feed** — a source that serves paged OHLCV history *and* live
|
|
@@ -133,7 +207,13 @@ chart.destroy();
|
|
|
133
207
|
| `setCandles(candles)` | Replace the series (each candle has `vol`) and redraw. |
|
|
134
208
|
| `setInterval(seconds)` | Bucket interval — drives time-axis label granularity. |
|
|
135
209
|
| `setChartType("candle" \| "line")` | Switch series style. |
|
|
210
|
+
| `setShowVolume(on)` | Show / hide the volume panel (the price pane reclaims the space). |
|
|
211
|
+
| `setLogScale(on)` | Toggle the logarithmic price axis (auto-falls back to linear if any low ≤ 0). |
|
|
212
|
+
| `setAxis({ fitContent, priceFormat, timeFormat, priceTicks, timeTicks, axisFont })` | Style the axes — only the provided keys change. |
|
|
136
213
|
| `setIndicators(indicators)` | Set the moving-average overlays and redraw. |
|
|
214
|
+
| `setOscillators(oscillators)` | Set the RSI / MACD sub-panes and redraw. |
|
|
215
|
+
| `setDrawMode(mode)` | Arm a drawing tool (`"trendline"` / `"hline"`) or `"none"`. |
|
|
216
|
+
| `setDrawings` / `getDrawings` / `removeDrawing(id)` / `clearDrawings()` | Manage drawings. |
|
|
137
217
|
| `setNeedHistory(cb)` | Callback fired when the view nears the start of loaded data (lazy history). |
|
|
138
218
|
| `setHeight(px)` / `setTheme(partial)` | Resize / merge theme overrides. |
|
|
139
219
|
| `resetView()` | Reset zoom/pan (same as double-click). |
|
|
@@ -142,15 +222,17 @@ chart.destroy();
|
|
|
142
222
|
`ChartOptions`: `height`, `theme`, `initialBars` (120), `minBars` (20),
|
|
143
223
|
`maxBarWidth` (18 — caps candle spacing so sparse series stay tight, right-anchored),
|
|
144
224
|
`volumeRatio` (0.18), `rightPad`/`bottomPad`/`topPad`, `onCrosshair`, `indicators`,
|
|
145
|
-
|
|
225
|
+
`oscillators`, `drawings`, `showVolume` (true), `logScale` (false), `fitContent` (false),
|
|
226
|
+
`maxBarWidth` (18) / `maxBodyWidth` (40), `priceFormat`/`timeFormat`, `priceTicks` (5) /
|
|
227
|
+
`timeTicks` (7), `axisFont`, and `onNeedHistory`.
|
|
146
228
|
|
|
147
229
|
### Helpers
|
|
148
230
|
|
|
149
231
|
- `buildOHLC(points, interval, transform?)` — bucket priced trades into OHLC+volume
|
|
150
232
|
candles. `transform` = `{ denom, ethUsd, flip, supply }` (`supply` → market cap).
|
|
151
233
|
- `transformPrice(p, transform?)` — apply the transform to one price.
|
|
152
|
-
- `sma` / `ema` / `wma` / `vwap` / `bollingerBands` / `
|
|
153
|
-
indicator math (aligned to input, null until the window fills).
|
|
234
|
+
- `sma` / `ema` / `wma` / `vwap` / `bollingerBands` / `rsi` / `macd` / `computeIndicator`
|
|
235
|
+
— pure indicator math (aligned to input, null until the window fills).
|
|
154
236
|
- `connectFeed(chart, feed, opts)` — wire a `ChartFeed` (paged history + live stream)
|
|
155
237
|
to a chart: latest page, lazy older history, live merge. Returns `{ disconnect }`.
|
|
156
238
|
- `hyperliquidFeed({ coin, interval, testnet })` — a ready `ChartFeed` (REST history +
|
|
@@ -158,14 +240,24 @@ and `onNeedHistory`.
|
|
|
158
240
|
`HL_INTERVAL_SECONDS` are exported for custom fetching.
|
|
159
241
|
- `draw(input)` — the pure render pass (exported for custom hosts/tests); returns the
|
|
160
242
|
crosshair candle.
|
|
161
|
-
- `DEFAULT_THEME
|
|
243
|
+
- `DEFAULT_THEME` / `LIGHT_THEME` / `THEME_PRESETS`, `formatValue`, `formatAxisValue`
|
|
244
|
+
(range-aware tick labels), `formatVolume`, `formatTime`.
|
|
245
|
+
- `slotWidth(plotW, count, maxBarWidth, fitContent?)` — candle slot-width math (exported
|
|
246
|
+
for custom hosts).
|
|
247
|
+
- `computeProjection(input)` / `priceScale` / `timeScale` / `windowOf` — the pixel↔data
|
|
248
|
+
mapping the renderer and drawing tools share (returns `{ xOfTime, timeOfX, yOfPrice,
|
|
249
|
+
priceOfY, … }`).
|
|
162
250
|
|
|
163
251
|
## Interaction
|
|
164
252
|
|
|
165
253
|
- **Scroll** over the plot → zoom time; over the price axis → zoom price.
|
|
166
254
|
- **Drag** the plot → pan; drag the time axis → zoom X; drag the price axis → zoom Y.
|
|
167
255
|
- **Hover** → crosshair + price/time axis labels + the OHLCV legend.
|
|
168
|
-
- **Double-click** → reset zoom/pan.
|
|
256
|
+
- **Double-click** → reset zoom/pan (or delete a drawing when one is under the cursor).
|
|
257
|
+
- **Touch** → one finger pans (or zooms an axis from its gutter); two-finger pinch
|
|
258
|
+
zooms time (horizontal spread) and price (vertical spread) together.
|
|
259
|
+
- **Drawing tools** → arm trendline/h-line from the toolbar; select + drag to move,
|
|
260
|
+
double-click to delete.
|
|
169
261
|
|
|
170
262
|
## Design notes
|
|
171
263
|
|
package/dist/core/chart.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Candle, ChartOptions, ChartTheme, ChartType, Indicator } from "./types";
|
|
1
|
+
import type { AxisOptions, Candle, ChartOptions, ChartTheme, ChartType, DrawMode, Drawing, Indicator, Oscillator } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* Framework-agnostic candlestick/line chart with a volume panel. Creates and owns a
|
|
4
4
|
* `<canvas>` inside the given container and wires the interactions:
|
|
@@ -23,17 +23,36 @@ export declare class Chart {
|
|
|
23
23
|
private readonly ro;
|
|
24
24
|
private readonly pads;
|
|
25
25
|
private readonly minBars;
|
|
26
|
-
private
|
|
26
|
+
private maxBarWidth;
|
|
27
|
+
private maxBodyWidth?;
|
|
27
28
|
private readonly volumeRatio;
|
|
28
29
|
private readonly onCrosshair?;
|
|
29
30
|
private theme;
|
|
30
31
|
private height;
|
|
31
32
|
private candles;
|
|
32
33
|
private indicators;
|
|
34
|
+
private oscillators;
|
|
35
|
+
private drawings;
|
|
36
|
+
private drawMode;
|
|
37
|
+
private selectedDrawing;
|
|
38
|
+
/** in-progress trendline (drag from anchor A to B). */
|
|
39
|
+
private draftDrawing;
|
|
40
|
+
/** active move of an existing drawing: its id + the data-space grab anchor + originals. */
|
|
41
|
+
private drawingDrag;
|
|
42
|
+
private onDrawingsChange?;
|
|
43
|
+
private onDrawModeChange?;
|
|
33
44
|
/** Indicator values precomputed on data/indicator change (not per frame). */
|
|
34
45
|
private overlays;
|
|
35
46
|
private interval;
|
|
36
47
|
private type;
|
|
48
|
+
private showVolume;
|
|
49
|
+
private logScale;
|
|
50
|
+
private fitContent;
|
|
51
|
+
private priceFormat?;
|
|
52
|
+
private timeFormat?;
|
|
53
|
+
private priceTicks?;
|
|
54
|
+
private timeTicks?;
|
|
55
|
+
private axisFont?;
|
|
37
56
|
/** requestAnimationFrame handle coalescing high-frequency redraws (0 = none queued). */
|
|
38
57
|
private raf;
|
|
39
58
|
private onNeedHistory?;
|
|
@@ -44,12 +63,38 @@ export declare class Chart {
|
|
|
44
63
|
private yZoom;
|
|
45
64
|
private hover;
|
|
46
65
|
private drag;
|
|
66
|
+
/** two-finger pinch baseline: finger spreads + view at gesture start. */
|
|
67
|
+
private pinch;
|
|
47
68
|
private lastActive;
|
|
48
69
|
constructor(container: HTMLElement, opts?: ChartOptions);
|
|
49
70
|
setCandles(candles: Candle[]): this;
|
|
50
71
|
setInterval(interval: number): this;
|
|
51
72
|
setChartType(type: ChartType): this;
|
|
73
|
+
/** Show or hide the volume panel (the price pane reclaims the space when hidden). */
|
|
74
|
+
setShowVolume(on: boolean): this;
|
|
75
|
+
/** Toggle the logarithmic price axis (equal pixels = equal % move). */
|
|
76
|
+
setLogScale(on: boolean): this;
|
|
77
|
+
/** Style the axes: fill-vs-tight candle spacing, custom price/time label formatters,
|
|
78
|
+
* tick counts, and the label font. Only the provided keys change. */
|
|
79
|
+
setAxis(opts: AxisOptions): this;
|
|
52
80
|
setIndicators(indicators: Indicator[]): this;
|
|
81
|
+
/** Set the oscillator sub-panes (RSI / MACD) drawn below the volume panel. */
|
|
82
|
+
setOscillators(oscillators: Oscillator[]): this;
|
|
83
|
+
/** Arm a drawing tool ("trendline" / "hline"), or "none" for pan/zoom + select/move.
|
|
84
|
+
* Drawing tools are one-shot: after one drawing the mode auto-resets to "none". */
|
|
85
|
+
setDrawMode(mode: DrawMode): this;
|
|
86
|
+
/** Replace all drawings (does not fire onDrawingsChange). */
|
|
87
|
+
setDrawings(drawings: Drawing[]): this;
|
|
88
|
+
/** Current drawings (a copy). */
|
|
89
|
+
getDrawings(): Drawing[];
|
|
90
|
+
/** Remove a drawing by id. */
|
|
91
|
+
removeDrawing(id: string): this;
|
|
92
|
+
/** Remove the currently selected drawing, if any. */
|
|
93
|
+
deleteSelected(): this;
|
|
94
|
+
/** Remove all drawings. */
|
|
95
|
+
clearDrawings(): this;
|
|
96
|
+
private emitDrawings;
|
|
97
|
+
private exitDrawMode;
|
|
53
98
|
/** Register a callback fired when the user pans/zooms near the start of loaded data
|
|
54
99
|
* (so a feed can lazily load older candles). See connectFeed. */
|
|
55
100
|
setNeedHistory(cb: (() => void) | undefined): this;
|
|
@@ -61,6 +106,10 @@ export declare class Chart {
|
|
|
61
106
|
private get plotW();
|
|
62
107
|
private measure;
|
|
63
108
|
private clampView;
|
|
109
|
+
/** Assemble the {@link RenderInput} from current state — shared by render() and projection(). */
|
|
110
|
+
private buildInput;
|
|
111
|
+
/** Pixel↔data projection for the current frame (null when there are no candles). */
|
|
112
|
+
private projection;
|
|
64
113
|
private render;
|
|
65
114
|
/** Coalesce high-frequency redraws (drag/hover/wheel) into one per animation frame. */
|
|
66
115
|
private scheduleRender;
|
|
@@ -73,9 +122,19 @@ export declare class Chart {
|
|
|
73
122
|
private readonly onWheel;
|
|
74
123
|
/** Zoom the time axis by factor `f`, keeping the candle under `cursorX` anchored. */
|
|
75
124
|
private zoomTimeAt;
|
|
125
|
+
/** Topmost drawing under the pointer (within tolerance), or null. */
|
|
126
|
+
private hitTest;
|
|
76
127
|
private readonly onDown;
|
|
77
128
|
private readonly onMove;
|
|
129
|
+
/** Translate the dragged drawing by the pointer's data-space delta from the grab anchor. */
|
|
130
|
+
private applyDrawingDrag;
|
|
131
|
+
/** Apply an in-progress drag (pan or axis-zoom) from the current pointer position.
|
|
132
|
+
* Shared by mouse-move and single-finger touch-move. Returns true if a drag is active. */
|
|
133
|
+
private applyDrag;
|
|
78
134
|
private readonly onUp;
|
|
79
135
|
private readonly onLeave;
|
|
80
136
|
private readonly onDouble;
|
|
137
|
+
private readonly onTouchStart;
|
|
138
|
+
private readonly onTouchMove;
|
|
139
|
+
private readonly onTouchEnd;
|
|
81
140
|
}
|