@tradecanvas/react 1.3.27 → 1.3.28
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/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +29 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/TradeCanvas.tsx"],"sourcesContent":["import { useRef, useEffect, useImperativeHandle, forwardRef, type CSSProperties } from 'react';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n ChartOptions,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n StreamConfig,\n ChartStyleOverrides,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nexport interface TradeCanvasRef {\n getChart(): Chart | null;\n screenshot(filename?: string): void;\n screenshotDataURL(): string | null;\n}\n\nexport interface TradeCanvasProps {\n symbol?: string;\n timeframe?: TimeFrame;\n theme?: 'dark' | 'light' | Theme;\n chartType?: ChartType;\n /** Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`). */\n indicators?: IndicatorSpec[];\n /** Static bars: the chart shows these, and opens no stream of its own when it has them at mount. */\n data?: OHLCBar[];\n adapter?: DataAdapter;\n /** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */\n stream?: boolean;\n historyLimit?: number;\n /** What users can do on the chart; changes after mount are applied too (the keys given). */\n features?: FeaturesConfig;\n autoScale?: boolean;\n signalMarkers?: SignalMarker[];\n signalMarkerStyle?: SignalMarkerStyle;\n tradeZones?: TradeZone[];\n tradeZoneStyle?: TradeZoneStyle;\n /** Style overrides, by key: all of your app's (`chart.setOverrides`). */\n overrides?: ChartStyleOverrides;\n watermarkText?: string;\n /**\n * Chart options the props don't cover (`maxBarSpacing`, `rightMargin`,\n * `renderer`…), merged over the component's defaults at mount; later\n * changes are not applied. `chartType`, `theme` and `autoScale` always come\n * from their props.\n */\n options?: Partial<ChartOptions>;\n onReady?: (chart: Chart) => void;\n onCrosshairMove?: (payload: unknown) => void;\n className?: string;\n style?: CSSProperties;\n}\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme | undefined): Theme {\n if (!theme || theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme;\n}\n\nexport const TradeCanvas = forwardRef<TradeCanvasRef, TradeCanvasProps>(\n function TradeCanvas(props, ref) {\n const {\n symbol = 'BTCUSDT',\n timeframe = '5m' as TimeFrame,\n theme = 'dark',\n chartType = 'candlestick' as ChartType,\n indicators = [],\n data,\n adapter,\n stream = true,\n historyLimit = 500,\n features,\n autoScale = true,\n signalMarkers,\n signalMarkerStyle,\n tradeZones,\n tradeZoneStyle,\n overrides,\n watermarkText,\n options,\n onReady,\n onCrosshairMove,\n className,\n style,\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const chartRef = useRef<Chart | null>(null);\n const indicatorIdsRef = useRef<Map<string, string>>(new Map());\n /** The stream open now (`symbol|timeframe`), or null. */\n const streamKeyRef = useRef<string | null>(null);\n const featuresRef = useRef(JSON.stringify(features ?? null));\n\n useImperativeHandle(ref, () => ({\n getChart: () => chartRef.current,\n screenshot: (filename?: string) => chartRef.current?.screenshot(filename),\n screenshotDataURL: () => chartRef.current?.screenshotDataURL() ?? null,\n }));\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const opts: ChartOptions = {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...options,\n chartType,\n theme: resolveTheme(theme),\n autoScale,\n features: {\n drawings: true,\n drawingMagnet: true,\n drawingUndoRedo: true,\n indicators: true,\n trading: true,\n volume: true,\n legend: true,\n crosshair: true,\n keyboard: true,\n screenshot: true,\n alerts: true,\n barCountdown: true,\n logScale: true,\n watermark: true,\n ...options?.features,\n ...features,\n },\n };\n\n if (watermarkText) {\n opts.watermark = { text: watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' };\n }\n\n const chart = new Chart(container, opts);\n chartRef.current = chart;\n indicatorIdsRef.current = new Map();\n\n if (data) {\n chart.setData(data);\n } else if (stream) {\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n streamKeyRef.current = `${symbol}|${timeframe}`;\n }\n\n onReady?.(chart);\n\n return () => {\n chart.disconnectStream();\n chart.destroy();\n chartRef.current = null;\n indicatorIdsRef.current.clear();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // A stream of its own while it has no data; `stream={false}` closes it. Data given later leaves it be.\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n if (!stream) {\n if (streamKeyRef.current !== null) chart.disconnectStream();\n streamKeyRef.current = null;\n return;\n }\n if (data) return;\n const key = `${symbol}|${timeframe}`;\n if (streamKeyRef.current === key) return;\n streamKeyRef.current = key;\n\n chart.disconnectStream();\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n if (watermarkText !== undefined) {\n chart.setWatermark(watermarkText);\n }\n }, [symbol, timeframe, adapter, historyLimit, data, stream, watermarkText]);\n\n useEffect(() => {\n const next = JSON.stringify(features ?? null);\n if (next === featuresRef.current) return;\n featuresRef.current = next;\n if (features) chartRef.current?.setFeatures(features);\n }, [features]);\n\n useEffect(() => {\n chartRef.current?.setTheme(resolveTheme(theme));\n }, [theme]);\n\n useEffect(() => {\n chartRef.current?.setChartType(chartType);\n }, [chartType]);\n\n useEffect(() => {\n if (data) chartRef.current?.setData(data);\n }, [data]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (chart) syncIndicators(chart, indicators, indicatorIdsRef.current);\n }, [indicators]);\n\n useEffect(() => {\n if (signalMarkers) chartRef.current?.setSignalMarkers(signalMarkers);\n }, [signalMarkers]);\n\n useEffect(() => {\n if (signalMarkerStyle) chartRef.current?.setSignalMarkerStyle(signalMarkerStyle);\n }, [signalMarkerStyle]);\n\n useEffect(() => {\n if (tradeZones) chartRef.current?.setTradeZones(tradeZones);\n }, [tradeZones]);\n\n useEffect(() => {\n if (tradeZoneStyle) chartRef.current?.setTradeZoneStyle(tradeZoneStyle);\n }, [tradeZoneStyle]);\n\n // All of the host's overrides: keys left out of the prop are taken away.\n // Without the prop, overrides set through the chart itself are left alone.\n const overridesGiven = useRef(false);\n useEffect(() => {\n if (overrides === undefined && !overridesGiven.current) return;\n overridesGiven.current = overrides !== undefined;\n chartRef.current?.setOverrides(overrides ?? {});\n }, [overrides]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart || !onCrosshairMove) return;\n const handler = onCrosshairMove as (e: unknown) => void;\n chart.on('crosshairMove', handler);\n // A new handler (an inline one is new each render) replaces this one.\n return () => chart.off('crosshairMove', handler);\n }, [onCrosshairMove]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/TradeCanvas.tsx"],"sourcesContent":["import { useRef, useEffect, useImperativeHandle, forwardRef, type CSSProperties } from 'react';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n ChartOptions,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n StreamConfig,\n ChartStyleOverrides,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nexport interface TradeCanvasRef {\n getChart(): Chart | null;\n screenshot(filename?: string): void;\n screenshotDataURL(): string | null;\n}\n\n/**\n * The chart is made once, on mount. Later changes to `symbol`, `timeframe`,\n * `theme`, `chartType`, `indicators`, `data`, `stream`, `features`,\n * `overrides`, the markers and zones and `onCrosshairMove` go to it\n * (`setTheme`, `setChartType`, `setData`…). `options` and `autoScale` are\n * read at mount; `adapter`, `historyLimit` and `watermarkText` when the\n * stream connects (at mount, or for a new symbol or interval). Unmounting\n * closes the chart's stream and destroys it.\n */\nexport interface TradeCanvasProps {\n /** The stream's symbol (`'BTCUSDT'`); a new one reconnects the stream, while there is no `data`. */\n symbol?: string;\n /** The stream's interval (`'5m'`); a new one reconnects it, while there is no `data`. */\n timeframe?: TimeFrame;\n theme?: 'dark' | 'light' | Theme;\n chartType?: ChartType;\n /**\n * Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`).\n * A new list is matched to what's on the chart: added, removed, and one\n * whose inputs changed put back with them (its style edits go).\n */\n indicators?: IndicatorSpec[];\n /**\n * Static bars: the chart shows these, and opens no stream of its own when\n * it has them at mount. A new array replaces the series (`setData`): the\n * view goes back to the newest bar and every indicator is computed again.\n * For a live feed, update the chart itself from the ref\n * (`getChart()?.updateLastBar(bar)`, `appendBar`), or give it a stream.\n */\n data?: OHLCBar[];\n /** The stream's feed (Binance by default), taken when the stream connects. */\n adapter?: DataAdapter;\n /** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */\n stream?: boolean;\n historyLimit?: number;\n /** What users can do on the chart; changes after mount are applied too (the keys given). */\n features?: FeaturesConfig;\n autoScale?: boolean;\n signalMarkers?: SignalMarker[];\n signalMarkerStyle?: SignalMarkerStyle;\n tradeZones?: TradeZone[];\n tradeZoneStyle?: TradeZoneStyle;\n /** Style overrides, by key: all of your app's (`chart.setOverrides`). */\n overrides?: ChartStyleOverrides;\n watermarkText?: string;\n /**\n * Chart options the props don't cover (`maxBarSpacing`, `rightMargin`,\n * `renderer`…), merged over the component's defaults at mount; later\n * changes are not applied. `chartType`, `theme` and `autoScale` always come\n * from their props.\n */\n options?: Partial<ChartOptions>;\n /**\n * Called with the chart when it is made, on mount: once, or twice in\n * React's StrictMode in development, which mounts twice (the first chart\n * is destroyed). The ref's `getChart()` gives it too.\n */\n onReady?: (chart: Chart) => void;\n onCrosshairMove?: (payload: unknown) => void;\n className?: string;\n style?: CSSProperties;\n}\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme | undefined): Theme {\n if (!theme || theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme;\n}\n\nexport const TradeCanvas = forwardRef<TradeCanvasRef, TradeCanvasProps>(\n function TradeCanvas(props, ref) {\n const {\n symbol = 'BTCUSDT',\n timeframe = '5m' as TimeFrame,\n theme = 'dark',\n chartType = 'candlestick' as ChartType,\n indicators = [],\n data,\n adapter,\n stream = true,\n historyLimit = 500,\n features,\n autoScale = true,\n signalMarkers,\n signalMarkerStyle,\n tradeZones,\n tradeZoneStyle,\n overrides,\n watermarkText,\n options,\n onReady,\n onCrosshairMove,\n className,\n style,\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const chartRef = useRef<Chart | null>(null);\n const indicatorIdsRef = useRef<Map<string, string>>(new Map());\n /** The stream open now (`symbol|timeframe`), or null. */\n const streamKeyRef = useRef<string | null>(null);\n const featuresRef = useRef(JSON.stringify(features ?? null));\n\n useImperativeHandle(ref, () => ({\n getChart: () => chartRef.current,\n screenshot: (filename?: string) => chartRef.current?.screenshot(filename),\n screenshotDataURL: () => chartRef.current?.screenshotDataURL() ?? null,\n }));\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const opts: ChartOptions = {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...options,\n chartType,\n theme: resolveTheme(theme),\n autoScale,\n features: {\n drawings: true,\n drawingMagnet: true,\n drawingUndoRedo: true,\n indicators: true,\n trading: true,\n volume: true,\n legend: true,\n crosshair: true,\n keyboard: true,\n screenshot: true,\n alerts: true,\n barCountdown: true,\n logScale: true,\n watermark: true,\n ...options?.features,\n ...features,\n },\n };\n\n if (watermarkText) {\n opts.watermark = { text: watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' };\n }\n\n const chart = new Chart(container, opts);\n chartRef.current = chart;\n indicatorIdsRef.current = new Map();\n\n if (data) {\n chart.setData(data);\n } else if (stream) {\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n streamKeyRef.current = `${symbol}|${timeframe}`;\n }\n\n onReady?.(chart);\n\n return () => {\n chart.disconnectStream();\n chart.destroy();\n chartRef.current = null;\n indicatorIdsRef.current.clear();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // A stream of its own while it has no data; `stream={false}` closes it. Data given later leaves it be.\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n if (!stream) {\n if (streamKeyRef.current !== null) chart.disconnectStream();\n streamKeyRef.current = null;\n return;\n }\n if (data) return;\n const key = `${symbol}|${timeframe}`;\n if (streamKeyRef.current === key) return;\n streamKeyRef.current = key;\n\n chart.disconnectStream();\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n if (watermarkText !== undefined) {\n chart.setWatermark(watermarkText);\n }\n }, [symbol, timeframe, adapter, historyLimit, data, stream, watermarkText]);\n\n useEffect(() => {\n const next = JSON.stringify(features ?? null);\n if (next === featuresRef.current) return;\n featuresRef.current = next;\n if (features) chartRef.current?.setFeatures(features);\n }, [features]);\n\n useEffect(() => {\n chartRef.current?.setTheme(resolveTheme(theme));\n }, [theme]);\n\n useEffect(() => {\n chartRef.current?.setChartType(chartType);\n }, [chartType]);\n\n useEffect(() => {\n if (data) chartRef.current?.setData(data);\n }, [data]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (chart) syncIndicators(chart, indicators, indicatorIdsRef.current);\n }, [indicators]);\n\n useEffect(() => {\n if (signalMarkers) chartRef.current?.setSignalMarkers(signalMarkers);\n }, [signalMarkers]);\n\n useEffect(() => {\n if (signalMarkerStyle) chartRef.current?.setSignalMarkerStyle(signalMarkerStyle);\n }, [signalMarkerStyle]);\n\n useEffect(() => {\n if (tradeZones) chartRef.current?.setTradeZones(tradeZones);\n }, [tradeZones]);\n\n useEffect(() => {\n if (tradeZoneStyle) chartRef.current?.setTradeZoneStyle(tradeZoneStyle);\n }, [tradeZoneStyle]);\n\n // All of the host's overrides: keys left out of the prop are taken away.\n // Without the prop, overrides set through the chart itself are left alone.\n const overridesGiven = useRef(false);\n useEffect(() => {\n if (overrides === undefined && !overridesGiven.current) return;\n overridesGiven.current = overrides !== undefined;\n chartRef.current?.setOverrides(overrides ?? {});\n }, [overrides]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart || !onCrosshairMove) return;\n const handler = onCrosshairMove as (e: unknown) => void;\n chart.on('crosshairMove', handler);\n // A new handler (an inline one is new each render) replaces this one.\n return () => chart.off('crosshairMove', handler);\n }, [onCrosshairMove]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n },\n);\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -18,15 +18,37 @@ import { TradeZoneStyle } from '@tradecanvas/chart';
|
|
|
18
18
|
|
|
19
19
|
export declare const TradeCanvas: ForwardRefExoticComponent<TradeCanvasProps & RefAttributes<TradeCanvasRef>>;
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* The chart is made once, on mount. Later changes to `symbol`, `timeframe`,
|
|
23
|
+
* `theme`, `chartType`, `indicators`, `data`, `stream`, `features`,
|
|
24
|
+
* `overrides`, the markers and zones and `onCrosshairMove` go to it
|
|
25
|
+
* (`setTheme`, `setChartType`, `setData`…). `options` and `autoScale` are
|
|
26
|
+
* read at mount; `adapter`, `historyLimit` and `watermarkText` when the
|
|
27
|
+
* stream connects (at mount, or for a new symbol or interval). Unmounting
|
|
28
|
+
* closes the chart's stream and destroys it.
|
|
29
|
+
*/
|
|
21
30
|
export declare interface TradeCanvasProps {
|
|
31
|
+
/** The stream's symbol (`'BTCUSDT'`); a new one reconnects the stream, while there is no `data`. */
|
|
22
32
|
symbol?: string;
|
|
33
|
+
/** The stream's interval (`'5m'`); a new one reconnects it, while there is no `data`. */
|
|
23
34
|
timeframe?: TimeFrame;
|
|
24
35
|
theme?: 'dark' | 'light' | Theme;
|
|
25
36
|
chartType?: ChartType;
|
|
26
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`).
|
|
39
|
+
* A new list is matched to what's on the chart: added, removed, and one
|
|
40
|
+
* whose inputs changed put back with them (its style edits go).
|
|
41
|
+
*/
|
|
27
42
|
indicators?: IndicatorSpec[];
|
|
28
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Static bars: the chart shows these, and opens no stream of its own when
|
|
45
|
+
* it has them at mount. A new array replaces the series (`setData`): the
|
|
46
|
+
* view goes back to the newest bar and every indicator is computed again.
|
|
47
|
+
* For a live feed, update the chart itself from the ref
|
|
48
|
+
* (`getChart()?.updateLastBar(bar)`, `appendBar`), or give it a stream.
|
|
49
|
+
*/
|
|
29
50
|
data?: OHLCBar[];
|
|
51
|
+
/** The stream's feed (Binance by default), taken when the stream connects. */
|
|
30
52
|
adapter?: DataAdapter;
|
|
31
53
|
/** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */
|
|
32
54
|
stream?: boolean;
|
|
@@ -48,6 +70,11 @@ export declare interface TradeCanvasProps {
|
|
|
48
70
|
* from their props.
|
|
49
71
|
*/
|
|
50
72
|
options?: Partial<ChartOptions>;
|
|
73
|
+
/**
|
|
74
|
+
* Called with the chart when it is made, on mount: once, or twice in
|
|
75
|
+
* React's StrictMode in development, which mounts twice (the first chart
|
|
76
|
+
* is destroyed). The ref's `getChart()` gives it too.
|
|
77
|
+
*/
|
|
51
78
|
onReady?: (chart: Chart) => void;
|
|
52
79
|
onCrosshairMove?: (payload: unknown) => void;
|
|
53
80
|
className?: string;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/TradeCanvas.tsx"],"sourcesContent":["import { useRef, useEffect, useImperativeHandle, forwardRef, type CSSProperties } from 'react';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n ChartOptions,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n StreamConfig,\n ChartStyleOverrides,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nexport interface TradeCanvasRef {\n getChart(): Chart | null;\n screenshot(filename?: string): void;\n screenshotDataURL(): string | null;\n}\n\nexport interface TradeCanvasProps {\n symbol?: string;\n timeframe?: TimeFrame;\n theme?: 'dark' | 'light' | Theme;\n chartType?: ChartType;\n /** Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`). */\n indicators?: IndicatorSpec[];\n /** Static bars: the chart shows these, and opens no stream of its own when it has them at mount. */\n data?: OHLCBar[];\n adapter?: DataAdapter;\n /** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */\n stream?: boolean;\n historyLimit?: number;\n /** What users can do on the chart; changes after mount are applied too (the keys given). */\n features?: FeaturesConfig;\n autoScale?: boolean;\n signalMarkers?: SignalMarker[];\n signalMarkerStyle?: SignalMarkerStyle;\n tradeZones?: TradeZone[];\n tradeZoneStyle?: TradeZoneStyle;\n /** Style overrides, by key: all of your app's (`chart.setOverrides`). */\n overrides?: ChartStyleOverrides;\n watermarkText?: string;\n /**\n * Chart options the props don't cover (`maxBarSpacing`, `rightMargin`,\n * `renderer`…), merged over the component's defaults at mount; later\n * changes are not applied. `chartType`, `theme` and `autoScale` always come\n * from their props.\n */\n options?: Partial<ChartOptions>;\n onReady?: (chart: Chart) => void;\n onCrosshairMove?: (payload: unknown) => void;\n className?: string;\n style?: CSSProperties;\n}\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme | undefined): Theme {\n if (!theme || theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme;\n}\n\nexport const TradeCanvas = forwardRef<TradeCanvasRef, TradeCanvasProps>(\n function TradeCanvas(props, ref) {\n const {\n symbol = 'BTCUSDT',\n timeframe = '5m' as TimeFrame,\n theme = 'dark',\n chartType = 'candlestick' as ChartType,\n indicators = [],\n data,\n adapter,\n stream = true,\n historyLimit = 500,\n features,\n autoScale = true,\n signalMarkers,\n signalMarkerStyle,\n tradeZones,\n tradeZoneStyle,\n overrides,\n watermarkText,\n options,\n onReady,\n onCrosshairMove,\n className,\n style,\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const chartRef = useRef<Chart | null>(null);\n const indicatorIdsRef = useRef<Map<string, string>>(new Map());\n /** The stream open now (`symbol|timeframe`), or null. */\n const streamKeyRef = useRef<string | null>(null);\n const featuresRef = useRef(JSON.stringify(features ?? null));\n\n useImperativeHandle(ref, () => ({\n getChart: () => chartRef.current,\n screenshot: (filename?: string) => chartRef.current?.screenshot(filename),\n screenshotDataURL: () => chartRef.current?.screenshotDataURL() ?? null,\n }));\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const opts: ChartOptions = {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...options,\n chartType,\n theme: resolveTheme(theme),\n autoScale,\n features: {\n drawings: true,\n drawingMagnet: true,\n drawingUndoRedo: true,\n indicators: true,\n trading: true,\n volume: true,\n legend: true,\n crosshair: true,\n keyboard: true,\n screenshot: true,\n alerts: true,\n barCountdown: true,\n logScale: true,\n watermark: true,\n ...options?.features,\n ...features,\n },\n };\n\n if (watermarkText) {\n opts.watermark = { text: watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' };\n }\n\n const chart = new Chart(container, opts);\n chartRef.current = chart;\n indicatorIdsRef.current = new Map();\n\n if (data) {\n chart.setData(data);\n } else if (stream) {\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n streamKeyRef.current = `${symbol}|${timeframe}`;\n }\n\n onReady?.(chart);\n\n return () => {\n chart.disconnectStream();\n chart.destroy();\n chartRef.current = null;\n indicatorIdsRef.current.clear();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // A stream of its own while it has no data; `stream={false}` closes it. Data given later leaves it be.\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n if (!stream) {\n if (streamKeyRef.current !== null) chart.disconnectStream();\n streamKeyRef.current = null;\n return;\n }\n if (data) return;\n const key = `${symbol}|${timeframe}`;\n if (streamKeyRef.current === key) return;\n streamKeyRef.current = key;\n\n chart.disconnectStream();\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n if (watermarkText !== undefined) {\n chart.setWatermark(watermarkText);\n }\n }, [symbol, timeframe, adapter, historyLimit, data, stream, watermarkText]);\n\n useEffect(() => {\n const next = JSON.stringify(features ?? null);\n if (next === featuresRef.current) return;\n featuresRef.current = next;\n if (features) chartRef.current?.setFeatures(features);\n }, [features]);\n\n useEffect(() => {\n chartRef.current?.setTheme(resolveTheme(theme));\n }, [theme]);\n\n useEffect(() => {\n chartRef.current?.setChartType(chartType);\n }, [chartType]);\n\n useEffect(() => {\n if (data) chartRef.current?.setData(data);\n }, [data]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (chart) syncIndicators(chart, indicators, indicatorIdsRef.current);\n }, [indicators]);\n\n useEffect(() => {\n if (signalMarkers) chartRef.current?.setSignalMarkers(signalMarkers);\n }, [signalMarkers]);\n\n useEffect(() => {\n if (signalMarkerStyle) chartRef.current?.setSignalMarkerStyle(signalMarkerStyle);\n }, [signalMarkerStyle]);\n\n useEffect(() => {\n if (tradeZones) chartRef.current?.setTradeZones(tradeZones);\n }, [tradeZones]);\n\n useEffect(() => {\n if (tradeZoneStyle) chartRef.current?.setTradeZoneStyle(tradeZoneStyle);\n }, [tradeZoneStyle]);\n\n // All of the host's overrides: keys left out of the prop are taken away.\n // Without the prop, overrides set through the chart itself are left alone.\n const overridesGiven = useRef(false);\n useEffect(() => {\n if (overrides === undefined && !overridesGiven.current) return;\n overridesGiven.current = overrides !== undefined;\n chartRef.current?.setOverrides(overrides ?? {});\n }, [overrides]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart || !onCrosshairMove) return;\n const handler = onCrosshairMove as (e: unknown) => void;\n chart.on('crosshairMove', handler);\n // A new handler (an inline one is new each render) replaces this one.\n return () => chart.off('crosshairMove', handler);\n }, [onCrosshairMove]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/TradeCanvas.tsx"],"sourcesContent":["import { useRef, useEffect, useImperativeHandle, forwardRef, type CSSProperties } from 'react';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n ChartOptions,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n StreamConfig,\n ChartStyleOverrides,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nexport interface TradeCanvasRef {\n getChart(): Chart | null;\n screenshot(filename?: string): void;\n screenshotDataURL(): string | null;\n}\n\n/**\n * The chart is made once, on mount. Later changes to `symbol`, `timeframe`,\n * `theme`, `chartType`, `indicators`, `data`, `stream`, `features`,\n * `overrides`, the markers and zones and `onCrosshairMove` go to it\n * (`setTheme`, `setChartType`, `setData`…). `options` and `autoScale` are\n * read at mount; `adapter`, `historyLimit` and `watermarkText` when the\n * stream connects (at mount, or for a new symbol or interval). Unmounting\n * closes the chart's stream and destroys it.\n */\nexport interface TradeCanvasProps {\n /** The stream's symbol (`'BTCUSDT'`); a new one reconnects the stream, while there is no `data`. */\n symbol?: string;\n /** The stream's interval (`'5m'`); a new one reconnects it, while there is no `data`. */\n timeframe?: TimeFrame;\n theme?: 'dark' | 'light' | Theme;\n chartType?: ChartType;\n /**\n * Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`).\n * A new list is matched to what's on the chart: added, removed, and one\n * whose inputs changed put back with them (its style edits go).\n */\n indicators?: IndicatorSpec[];\n /**\n * Static bars: the chart shows these, and opens no stream of its own when\n * it has them at mount. A new array replaces the series (`setData`): the\n * view goes back to the newest bar and every indicator is computed again.\n * For a live feed, update the chart itself from the ref\n * (`getChart()?.updateLastBar(bar)`, `appendBar`), or give it a stream.\n */\n data?: OHLCBar[];\n /** The stream's feed (Binance by default), taken when the stream connects. */\n adapter?: DataAdapter;\n /** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */\n stream?: boolean;\n historyLimit?: number;\n /** What users can do on the chart; changes after mount are applied too (the keys given). */\n features?: FeaturesConfig;\n autoScale?: boolean;\n signalMarkers?: SignalMarker[];\n signalMarkerStyle?: SignalMarkerStyle;\n tradeZones?: TradeZone[];\n tradeZoneStyle?: TradeZoneStyle;\n /** Style overrides, by key: all of your app's (`chart.setOverrides`). */\n overrides?: ChartStyleOverrides;\n watermarkText?: string;\n /**\n * Chart options the props don't cover (`maxBarSpacing`, `rightMargin`,\n * `renderer`…), merged over the component's defaults at mount; later\n * changes are not applied. `chartType`, `theme` and `autoScale` always come\n * from their props.\n */\n options?: Partial<ChartOptions>;\n /**\n * Called with the chart when it is made, on mount: once, or twice in\n * React's StrictMode in development, which mounts twice (the first chart\n * is destroyed). The ref's `getChart()` gives it too.\n */\n onReady?: (chart: Chart) => void;\n onCrosshairMove?: (payload: unknown) => void;\n className?: string;\n style?: CSSProperties;\n}\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme | undefined): Theme {\n if (!theme || theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme;\n}\n\nexport const TradeCanvas = forwardRef<TradeCanvasRef, TradeCanvasProps>(\n function TradeCanvas(props, ref) {\n const {\n symbol = 'BTCUSDT',\n timeframe = '5m' as TimeFrame,\n theme = 'dark',\n chartType = 'candlestick' as ChartType,\n indicators = [],\n data,\n adapter,\n stream = true,\n historyLimit = 500,\n features,\n autoScale = true,\n signalMarkers,\n signalMarkerStyle,\n tradeZones,\n tradeZoneStyle,\n overrides,\n watermarkText,\n options,\n onReady,\n onCrosshairMove,\n className,\n style,\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const chartRef = useRef<Chart | null>(null);\n const indicatorIdsRef = useRef<Map<string, string>>(new Map());\n /** The stream open now (`symbol|timeframe`), or null. */\n const streamKeyRef = useRef<string | null>(null);\n const featuresRef = useRef(JSON.stringify(features ?? null));\n\n useImperativeHandle(ref, () => ({\n getChart: () => chartRef.current,\n screenshot: (filename?: string) => chartRef.current?.screenshot(filename),\n screenshotDataURL: () => chartRef.current?.screenshotDataURL() ?? null,\n }));\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const opts: ChartOptions = {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...options,\n chartType,\n theme: resolveTheme(theme),\n autoScale,\n features: {\n drawings: true,\n drawingMagnet: true,\n drawingUndoRedo: true,\n indicators: true,\n trading: true,\n volume: true,\n legend: true,\n crosshair: true,\n keyboard: true,\n screenshot: true,\n alerts: true,\n barCountdown: true,\n logScale: true,\n watermark: true,\n ...options?.features,\n ...features,\n },\n };\n\n if (watermarkText) {\n opts.watermark = { text: watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' };\n }\n\n const chart = new Chart(container, opts);\n chartRef.current = chart;\n indicatorIdsRef.current = new Map();\n\n if (data) {\n chart.setData(data);\n } else if (stream) {\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n streamKeyRef.current = `${symbol}|${timeframe}`;\n }\n\n onReady?.(chart);\n\n return () => {\n chart.disconnectStream();\n chart.destroy();\n chartRef.current = null;\n indicatorIdsRef.current.clear();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // A stream of its own while it has no data; `stream={false}` closes it. Data given later leaves it be.\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n if (!stream) {\n if (streamKeyRef.current !== null) chart.disconnectStream();\n streamKeyRef.current = null;\n return;\n }\n if (data) return;\n const key = `${symbol}|${timeframe}`;\n if (streamKeyRef.current === key) return;\n streamKeyRef.current = key;\n\n chart.disconnectStream();\n const dataAdapter = adapter ?? new BinanceAdapter();\n chart.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });\n if (watermarkText !== undefined) {\n chart.setWatermark(watermarkText);\n }\n }, [symbol, timeframe, adapter, historyLimit, data, stream, watermarkText]);\n\n useEffect(() => {\n const next = JSON.stringify(features ?? null);\n if (next === featuresRef.current) return;\n featuresRef.current = next;\n if (features) chartRef.current?.setFeatures(features);\n }, [features]);\n\n useEffect(() => {\n chartRef.current?.setTheme(resolveTheme(theme));\n }, [theme]);\n\n useEffect(() => {\n chartRef.current?.setChartType(chartType);\n }, [chartType]);\n\n useEffect(() => {\n if (data) chartRef.current?.setData(data);\n }, [data]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (chart) syncIndicators(chart, indicators, indicatorIdsRef.current);\n }, [indicators]);\n\n useEffect(() => {\n if (signalMarkers) chartRef.current?.setSignalMarkers(signalMarkers);\n }, [signalMarkers]);\n\n useEffect(() => {\n if (signalMarkerStyle) chartRef.current?.setSignalMarkerStyle(signalMarkerStyle);\n }, [signalMarkerStyle]);\n\n useEffect(() => {\n if (tradeZones) chartRef.current?.setTradeZones(tradeZones);\n }, [tradeZones]);\n\n useEffect(() => {\n if (tradeZoneStyle) chartRef.current?.setTradeZoneStyle(tradeZoneStyle);\n }, [tradeZoneStyle]);\n\n // All of the host's overrides: keys left out of the prop are taken away.\n // Without the prop, overrides set through the chart itself are left alone.\n const overridesGiven = useRef(false);\n useEffect(() => {\n if (overrides === undefined && !overridesGiven.current) return;\n overridesGiven.current = overrides !== undefined;\n chartRef.current?.setOverrides(overrides ?? {});\n }, [overrides]);\n\n useEffect(() => {\n const chart = chartRef.current;\n if (!chart || !onCrosshairMove) return;\n const handler = onCrosshairMove as (e: unknown) => void;\n chart.on('crosshairMove', handler);\n // A new handler (an inline one is new each render) replaces this one.\n return () => chart.off('crosshairMove', handler);\n }, [onCrosshairMove]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n },\n);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradecanvas/react",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React component for @tradecanvas/chart — reactive props, ref forwarding, zero boilerplate.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"README.md"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@tradecanvas/chart": "1.
|
|
40
|
+
"@tradecanvas/chart": "1.46.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"react": ">=18.0.0",
|