@tradecanvas/vue 1.3.29 → 1.3.31

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
@@ -39,7 +39,7 @@ Streams live Binance data by default. Pass `:data="bars"` for static OHLC, or `:
39
39
 
40
40
  ## Props & events
41
41
 
42
- Props: `symbol`, `timeframe`, `theme`, `chartType`, `indicators` (ids, or `{ id, params, position }`), `data` (bars of your own; no stream), `adapter`, `stream` (`false`: no stream of its own), `historyLimit`, `features` (changes apply after mount too), `autoScale`, `signalMarkers`, `signalMarkerStyle`, `tradeZones`, `tradeZoneStyle`, `overrides` (style overrides by key, all of your app's), `watermarkText`.
42
+ Props: `symbol`, `timeframe`, `theme`, `chartType`, `indicators` (ids, or `{ id, params, position }`), `data` (bars of your own; no stream; bars that go on from the last, pushed or in a new array, go in as live ticks), `adapter`, `stream` (`false`: no stream of its own), `historyLimit`, `features` (changes apply after mount too), `autoScale`, `signalMarkers`, `signalMarkerStyle`, `tradeZones`, `tradeZoneStyle`, `overrides` (style overrides by key, all of your app's), `watermarkText`.
43
43
 
44
44
  Events: `@ready="(chart) => …"`, `@crosshairMove="(payload) => …"`.
45
45
 
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`vue`),t=require(`@tradecanvas/chart`);var n=(0,e.defineComponent)({__name:`TradeCanvas`,props:{symbol:{default:`BTCUSDT`},timeframe:{default:`5m`},theme:{default:`dark`},chartType:{default:`candlestick`},indicators:{default:()=>[]},data:{},adapter:{},stream:{type:Boolean,default:!0},historyLimit:{default:500},features:{},autoScale:{type:Boolean,default:!0},signalMarkers:{},signalMarkerStyle:{},tradeZones:{},tradeZoneStyle:{},overrides:{},watermarkText:{},options:{}},emits:[`ready`,`crosshairMove`],setup(n,{expose:r,emit:i}){let a=n,o=i;function s(e){return e===`dark`?t.DARK_THEME:e===`light`?t.LIGHT_THEME:e}let c=(0,e.ref)(),l=null,u=new Map,d=null,f=e=>e.map(e=>typeof e==`string`?e:{id:e.id,...e.params?{params:{...e.params}}:{},...e.position?{position:e.position}:{}}),p=JSON.stringify(a.features??null);function m(){if(!l)return;if(!a.stream){d!==null&&l.disconnectStream(),d=null;return}if(a.data)return;let e=`${a.symbol}|${a.timeframe}`;if(d===e)return;let n=d!==null;d=e,n&&l.disconnectStream();let r=a.adapter??new t.BinanceAdapter;l.connect({adapter:r,symbol:a.symbol,timeframe:a.timeframe,historyLimit:a.historyLimit}),n&&a.watermarkText&&l.setWatermark(a.watermarkText)}function h(){return l}function g(e){l?.screenshot(e)}function _(){return l?.screenshotDataURL()??null}r({getChart:h,screenshot:g,screenshotDataURL:_}),(0,e.onMounted)(()=>{c.value&&(l=new t.Chart(c.value,{rightMargin:5,crosshair:{mode:`magnet`},...a.options,chartType:a.chartType,theme:s(a.theme),autoScale:a.autoScale,...a.watermarkText?{watermark:{text:a.watermarkText,fontSize:48,color:`rgba(255,255,255,0.03)`}}:{},features:{drawings:!0,drawingMagnet:!0,drawingUndoRedo:!0,indicators:!0,trading:!0,volume:!0,legend:!0,crosshair:!0,keyboard:!0,screenshot:!0,alerts:!0,barCountdown:!0,logScale:!0,watermark:!0,...a.options?.features,...a.features}}),a.data?x(l,a.data):m(),(0,t.syncIndicators)(l,f(a.indicators),u),a.signalMarkers&&l.setSignalMarkers(a.signalMarkers),a.signalMarkerStyle&&l.setSignalMarkerStyle(a.signalMarkerStyle),a.tradeZones&&l.setTradeZones(a.tradeZones),a.tradeZoneStyle&&l.setTradeZoneStyle(a.tradeZoneStyle),a.overrides&&l.setOverrides(a.overrides),o(`ready`,l))}),(0,e.onUnmounted)(()=>{l&&=(l.disconnectStream(),l.destroy(),null)}),(0,e.watch)(()=>[a.symbol,a.timeframe,!a.data,a.stream],m);let v=null,y=e=>e?`${e.time},${e.open},${e.high},${e.low},${e.close},${e.volume}`:``,b=t=>({...(0,e.toRaw)(t)});function x(t,n){let r=(0,e.toRaw)(n),i=r.length,a=y(r[i-1]),o=v;if(v={bars:r,length:i,last:a},o&&o.bars===r&&o.length>0){if(i===o.length){a!==o.last&&t.updateLastBar(b(r[i-1]));return}if(i===o.length+1){t.updateLastBar(b(r[i-2])),t.appendBar(b(r[i-1]));return}}t.setData(r.map(b))}return(0,e.watch)(()=>a.features,e=>{let t=JSON.stringify(e??null);t!==p&&(p=t,e&&l?.setFeatures({...e}))},{deep:!0}),(0,e.watch)(()=>a.theme,e=>{l?.setTheme(s(e))}),(0,e.watch)(()=>a.chartType,e=>{l?.setChartType(e)}),(0,e.watch)(()=>[a.data,a.data?.length,y(a.data?.[a.data.length-1])],()=>{a.data&&l&&x(l,a.data)}),(0,e.watch)(()=>a.indicators,e=>{l&&(0,t.syncIndicators)(l,f(e),u)},{deep:!0}),(0,e.watch)(()=>a.signalMarkers,e=>{e&&l?.setSignalMarkers(e)},{deep:!0}),(0,e.watch)(()=>a.signalMarkerStyle,e=>{e&&l?.setSignalMarkerStyle(e)},{deep:!0}),(0,e.watch)(()=>a.tradeZones,e=>{e&&l?.setTradeZones(e)},{deep:!0}),(0,e.watch)(()=>a.tradeZoneStyle,e=>{e&&l?.setTradeZoneStyle(e)},{deep:!0}),(0,e.watch)(()=>a.overrides,e=>{l?.setOverrides({...e})},{deep:!0}),(t,n)=>((0,e.openBlock)(),(0,e.createElementBlock)(`div`,{ref_key:`containerEl`,ref:c,style:{width:`100%`,height:`100%`}},null,512))}});exports.TradeCanvas=n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`vue`),t=require(`@tradecanvas/chart`);var n=(0,e.defineComponent)({__name:`TradeCanvas`,props:{symbol:{default:`BTCUSDT`},timeframe:{default:`5m`},theme:{default:`dark`},chartType:{default:`candlestick`},indicators:{default:()=>[]},data:{},adapter:{},stream:{type:Boolean,default:!0},historyLimit:{default:500},features:{},autoScale:{type:Boolean,default:!0},signalMarkers:{},signalMarkerStyle:{},tradeZones:{},tradeZoneStyle:{},overrides:{},watermarkText:{},options:{}},emits:[`ready`,`crosshairMove`],setup(n,{expose:r,emit:i}){let a=n,o=i;function s(e){return e===`dark`?t.DARK_THEME:e===`light`?t.LIGHT_THEME:e}let c=(0,e.ref)(),l=null,u=new Map,d=null,f=e=>e.map(e=>typeof e==`string`?e:{id:e.id,...e.params?{params:{...e.params}}:{},...e.position?{position:e.position}:{}}),p=JSON.stringify(a.features??null);function m(){if(!l)return;if(!a.stream){d!==null&&l.disconnectStream(),d=null;return}if(a.data)return;let e=`${a.symbol}|${a.timeframe}`;if(d===e)return;let n=d!==null;d=e,n&&l.disconnectStream();let r=a.adapter??new t.BinanceAdapter;l.connect({adapter:r,symbol:a.symbol,timeframe:a.timeframe,historyLimit:a.historyLimit}),n&&a.watermarkText&&l.setWatermark(a.watermarkText)}function h(){return l}function g(e){l?.screenshot(e)}function _(){return l?.screenshotDataURL()??null}r({getChart:h,screenshot:g,screenshotDataURL:_}),(0,e.onMounted)(()=>{c.value&&(l=new t.Chart(c.value,{rightMargin:5,crosshair:{mode:`magnet`},...a.options,chartType:a.chartType,theme:s(a.theme),autoScale:a.autoScale,...a.watermarkText?{watermark:{text:a.watermarkText,fontSize:48,color:`rgba(255,255,255,0.03)`}}:{},features:{drawings:!0,drawingMagnet:!0,drawingUndoRedo:!0,indicators:!0,trading:!0,volume:!0,legend:!0,crosshair:!0,keyboard:!0,screenshot:!0,alerts:!0,barCountdown:!0,logScale:!0,watermark:!0,...a.options?.features,...a.features}}),a.data?b(l,a.data):m(),(0,t.syncIndicators)(l,f(a.indicators),u),a.signalMarkers&&l.setSignalMarkers(a.signalMarkers),a.signalMarkerStyle&&l.setSignalMarkerStyle(a.signalMarkerStyle),a.tradeZones&&l.setTradeZones(a.tradeZones),a.tradeZoneStyle&&l.setTradeZoneStyle(a.tradeZoneStyle),a.overrides&&l.setOverrides(a.overrides),o(`ready`,l))}),(0,e.onUnmounted)(()=>{l&&=(l.disconnectStream(),l.destroy(),null)}),(0,e.watch)(()=>[a.symbol,a.timeframe,!a.data,a.stream],m);let v=null,y=e=>e?`${e.time},${e.open},${e.high},${e.low},${e.close},${e.volume}`:``;function b(n,r){v=(0,t.feedBars)(n,(0,e.toRaw)(r),v)}return(0,e.watch)(()=>a.features,e=>{let t=JSON.stringify(e??null);t!==p&&(p=t,e&&l?.setFeatures({...e}))},{deep:!0}),(0,e.watch)(()=>a.theme,e=>{l?.setTheme(s(e))}),(0,e.watch)(()=>a.chartType,e=>{l?.setChartType(e)}),(0,e.watch)(()=>[a.data,a.data?.length,y(a.data?.[a.data.length-1])],()=>{l&&(a.data?b(l,a.data):v=null)}),(0,e.watch)(()=>a.indicators,e=>{l&&(0,t.syncIndicators)(l,f(e),u)},{deep:!0}),(0,e.watch)(()=>a.signalMarkers,e=>{e&&l?.setSignalMarkers(e)},{deep:!0}),(0,e.watch)(()=>a.signalMarkerStyle,e=>{e&&l?.setSignalMarkerStyle(e)},{deep:!0}),(0,e.watch)(()=>a.tradeZones,e=>{e&&l?.setTradeZones(e)},{deep:!0}),(0,e.watch)(()=>a.tradeZoneStyle,e=>{e&&l?.setTradeZoneStyle(e)},{deep:!0}),(0,e.watch)(()=>a.overrides,e=>{l?.setOverrides({...e})},{deep:!0}),(t,n)=>((0,e.openBlock)(),(0,e.createElementBlock)(`div`,{ref_key:`containerEl`,ref:c,style:{width:`100%`,height:`100%`}},null,512))}});exports.TradeCanvas=n;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/TradeCanvas.vue","../src/TradeCanvas.vue"],"sourcesContent":["<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. A bar pushed onto the same array, or its last bar changed,\n * goes in as a live tick (the view stays where it is); a new array is a new\n * series. An older bar changed in place needs a new array.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart: the array, how many, and the last one as text. */\nlet fed: { bars: readonly OHLCBar[]; length: number; last: string } | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\nconst plain = (b: OHLCBar): OHLCBar => ({ ...toRaw(b) });\n\n/**\n * Bars into the chart: on the same array, a bar pushed or the last one\n * changed goes in as a tick, so the view stays put and only the end is\n * recalculated; anything else is a new series. The chart gets plain bars, not\n * Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n const bars = toRaw(data);\n const n = bars.length;\n const last = barKey(bars[n - 1]);\n const prev = fed;\n fed = { bars, length: n, last };\n if (prev && prev.bars === bars && prev.length > 0) {\n if (n === prev.length) {\n if (last !== prev.last) target.updateLastBar(plain(bars[n - 1]));\n return;\n }\n if (n === prev.length + 1) {\n target.updateLastBar(plain(bars[n - 2]));\n target.appendBar(plain(bars[n - 1]));\n return;\n }\n }\n target.setData(bars.map(plain));\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => { if (props.data && chart) feed(chart, props.data); },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n","<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. A bar pushed onto the same array, or its last bar changed,\n * goes in as a live tick (the view stays where it is); a new array is a new\n * series. An older bar changed in place needs a new array.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart: the array, how many, and the last one as text. */\nlet fed: { bars: readonly OHLCBar[]; length: number; last: string } | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\nconst plain = (b: OHLCBar): OHLCBar => ({ ...toRaw(b) });\n\n/**\n * Bars into the chart: on the same array, a bar pushed or the last one\n * changed goes in as a tick, so the view stays put and only the end is\n * recalculated; anything else is a new series. The chart gets plain bars, not\n * Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n const bars = toRaw(data);\n const n = bars.length;\n const last = barKey(bars[n - 1]);\n const prev = fed;\n fed = { bars, length: n, last };\n if (prev && prev.bars === bars && prev.length > 0) {\n if (n === prev.length) {\n if (last !== prev.last) target.updateLastBar(plain(bars[n - 1]));\n return;\n }\n if (n === prev.length + 1) {\n target.updateLastBar(plain(bars[n - 2]));\n target.appendBar(plain(bars[n - 1]));\n return;\n }\n }\n target.setData(bars.map(plain));\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => { if (props.data && chart) feed(chart, props.data); },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/TradeCanvas.vue","../src/TradeCanvas.vue"],"sourcesContent":["<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n feedBars,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n FedBars,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. Bars that go on from those given before, the last one\n * changed or bars added after it, go in as live ticks (the view isn't\n * reset): pushed onto the same array, changed in place, or in a new array.\n * Any other bars are a new series, as are yours after a stream gave the\n * chart bars of its own. An older bar changed needs a new array with a new\n * bar in its place.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart (`feedBars`). */\nlet fed: FedBars | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\n\n/**\n * Bars into the chart: bars that go on from those given before (a bar pushed\n * or the last one changed, in place or in a new array) go in as ticks, so the\n * view stays put and only the end is recalculated; anything else is a new\n * series. The chart gets plain copies, not Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n fed = feedBars(target, toRaw(data), fed);\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => {\n if (!chart) return;\n // Without data, the next bars are a new series: a stream may have come in between.\n if (props.data) feed(chart, props.data);\n else fed = null;\n },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n","<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n feedBars,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n FedBars,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. Bars that go on from those given before, the last one\n * changed or bars added after it, go in as live ticks (the view isn't\n * reset): pushed onto the same array, changed in place, or in a new array.\n * Any other bars are a new series, as are yours after a stream gave the\n * chart bars of its own. An older bar changed needs a new array with a new\n * bar in its place.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart (`feedBars`). */\nlet fed: FedBars | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\n\n/**\n * Bars into the chart: bars that go on from those given before (a bar pushed\n * or the last one changed, in place or in a new array) go in as ticks, so the\n * view stays put and only the end is recalculated; anything else is a new\n * series. The chart gets plain copies, not Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n fed = feedBars(target, toRaw(data), fed);\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => {\n if (!chart) return;\n // Without data, the next bars are a new series: a stream may have come in between.\n if (props.data) feed(chart, props.data);\n else fed = null;\n },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -26,9 +26,12 @@ declare type __VLS_Props = {
26
26
  indicators?: IndicatorSpec[];
27
27
  /**
28
28
  * Bars: the chart shows these, and opens no stream of its own when it has
29
- * them at mount. A bar pushed onto the same array, or its last bar changed,
30
- * goes in as a live tick (the view stays where it is); a new array is a new
31
- * series. An older bar changed in place needs a new array.
29
+ * them at mount. Bars that go on from those given before, the last one
30
+ * changed or bars added after it, go in as live ticks (the view isn't
31
+ * reset): pushed onto the same array, changed in place, or in a new array.
32
+ * Any other bars are a new series, as are yours after a stream gave the
33
+ * chart bars of its own. An older bar changed needs a new array with a new
34
+ * bar in its place.
32
35
  */
33
36
  data?: OHLCBar[];
34
37
  adapter?: DataAdapter;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createElementBlock as e, defineComponent as t, onMounted as n, onUnmounted as r, openBlock as i, ref as a, toRaw as o, watch as s } from "vue";
2
- import { BinanceAdapter as c, Chart as l, DARK_THEME as u, LIGHT_THEME as d, syncIndicators as f } from "@tradecanvas/chart";
2
+ import { BinanceAdapter as c, Chart as l, DARK_THEME as u, LIGHT_THEME as d, feedBars as f, syncIndicators as p } from "@tradecanvas/chart";
3
3
  //#endregion
4
4
  //#region src/TradeCanvas.vue
5
- var p = /* @__PURE__ */ t({
5
+ var m = /* @__PURE__ */ t({
6
6
  __name: "TradeCanvas",
7
7
  props: {
8
8
  symbol: { default: "BTCUSDT" },
@@ -31,58 +31,58 @@ var p = /* @__PURE__ */ t({
31
31
  options: {}
32
32
  },
33
33
  emits: ["ready", "crosshairMove"],
34
- setup(t, { expose: p, emit: m }) {
35
- let h = t, g = m;
36
- function _(e) {
34
+ setup(t, { expose: m, emit: h }) {
35
+ let g = t, _ = h;
36
+ function v(e) {
37
37
  return e === "dark" ? u : e === "light" ? d : e;
38
38
  }
39
- let v = a(), y = null, b = /* @__PURE__ */ new Map(), x = null, S = (e) => e.map((e) => typeof e == "string" ? e : {
39
+ let y = a(), b = null, x = /* @__PURE__ */ new Map(), S = null, C = (e) => e.map((e) => typeof e == "string" ? e : {
40
40
  id: e.id,
41
41
  ...e.params ? { params: { ...e.params } } : {},
42
42
  ...e.position ? { position: e.position } : {}
43
- }), C = JSON.stringify(h.features ?? null);
44
- function w() {
45
- if (!y) return;
46
- if (!h.stream) {
47
- x !== null && y.disconnectStream(), x = null;
43
+ }), w = JSON.stringify(g.features ?? null);
44
+ function T() {
45
+ if (!b) return;
46
+ if (!g.stream) {
47
+ S !== null && b.disconnectStream(), S = null;
48
48
  return;
49
49
  }
50
- if (h.data) return;
51
- let e = `${h.symbol}|${h.timeframe}`;
52
- if (x === e) return;
53
- let t = x !== null;
54
- x = e, t && y.disconnectStream();
55
- let n = h.adapter ?? new c();
56
- y.connect({
50
+ if (g.data) return;
51
+ let e = `${g.symbol}|${g.timeframe}`;
52
+ if (S === e) return;
53
+ let t = S !== null;
54
+ S = e, t && b.disconnectStream();
55
+ let n = g.adapter ?? new c();
56
+ b.connect({
57
57
  adapter: n,
58
- symbol: h.symbol,
59
- timeframe: h.timeframe,
60
- historyLimit: h.historyLimit
61
- }), t && h.watermarkText && y.setWatermark(h.watermarkText);
58
+ symbol: g.symbol,
59
+ timeframe: g.timeframe,
60
+ historyLimit: g.historyLimit
61
+ }), t && g.watermarkText && b.setWatermark(g.watermarkText);
62
62
  }
63
- function T() {
64
- return y;
63
+ function E() {
64
+ return b;
65
65
  }
66
- function E(e) {
67
- y?.screenshot(e);
66
+ function D(e) {
67
+ b?.screenshot(e);
68
68
  }
69
- function D() {
70
- return y?.screenshotDataURL() ?? null;
69
+ function O() {
70
+ return b?.screenshotDataURL() ?? null;
71
71
  }
72
- p({
73
- getChart: T,
74
- screenshot: E,
75
- screenshotDataURL: D
72
+ m({
73
+ getChart: E,
74
+ screenshot: D,
75
+ screenshotDataURL: O
76
76
  }), n(() => {
77
- v.value && (y = new l(v.value, {
77
+ y.value && (b = new l(y.value, {
78
78
  rightMargin: 5,
79
79
  crosshair: { mode: "magnet" },
80
- ...h.options,
81
- chartType: h.chartType,
82
- theme: _(h.theme),
83
- autoScale: h.autoScale,
84
- ...h.watermarkText ? { watermark: {
85
- text: h.watermarkText,
80
+ ...g.options,
81
+ chartType: g.chartType,
82
+ theme: v(g.theme),
83
+ autoScale: g.autoScale,
84
+ ...g.watermarkText ? { watermark: {
85
+ text: g.watermarkText,
86
86
  fontSize: 48,
87
87
  color: "rgba(255,255,255,0.03)"
88
88
  } } : {},
@@ -101,65 +101,50 @@ var p = /* @__PURE__ */ t({
101
101
  barCountdown: !0,
102
102
  logScale: !0,
103
103
  watermark: !0,
104
- ...h.options?.features,
105
- ...h.features
104
+ ...g.options?.features,
105
+ ...g.features
106
106
  }
107
- }), h.data ? j(y, h.data) : w(), f(y, S(h.indicators), b), h.signalMarkers && y.setSignalMarkers(h.signalMarkers), h.signalMarkerStyle && y.setSignalMarkerStyle(h.signalMarkerStyle), h.tradeZones && y.setTradeZones(h.tradeZones), h.tradeZoneStyle && y.setTradeZoneStyle(h.tradeZoneStyle), h.overrides && y.setOverrides(h.overrides), g("ready", y));
107
+ }), g.data ? j(b, g.data) : T(), p(b, C(g.indicators), x), g.signalMarkers && b.setSignalMarkers(g.signalMarkers), g.signalMarkerStyle && b.setSignalMarkerStyle(g.signalMarkerStyle), g.tradeZones && b.setTradeZones(g.tradeZones), g.tradeZoneStyle && b.setTradeZoneStyle(g.tradeZoneStyle), g.overrides && b.setOverrides(g.overrides), _("ready", b));
108
108
  }), r(() => {
109
- y &&= (y.disconnectStream(), y.destroy(), null);
109
+ b &&= (b.disconnectStream(), b.destroy(), null);
110
110
  }), s(() => [
111
- h.symbol,
112
- h.timeframe,
113
- !h.data,
114
- h.stream
115
- ], w);
116
- let O = null, k = (e) => e ? `${e.time},${e.open},${e.high},${e.low},${e.close},${e.volume}` : "", A = (e) => ({ ...o(e) });
111
+ g.symbol,
112
+ g.timeframe,
113
+ !g.data,
114
+ g.stream
115
+ ], T);
116
+ let k = null, A = (e) => e ? `${e.time},${e.open},${e.high},${e.low},${e.close},${e.volume}` : "";
117
117
  function j(e, t) {
118
- let n = o(t), r = n.length, i = k(n[r - 1]), a = O;
119
- if (O = {
120
- bars: n,
121
- length: r,
122
- last: i
123
- }, a && a.bars === n && a.length > 0) {
124
- if (r === a.length) {
125
- i !== a.last && e.updateLastBar(A(n[r - 1]));
126
- return;
127
- }
128
- if (r === a.length + 1) {
129
- e.updateLastBar(A(n[r - 2])), e.appendBar(A(n[r - 1]));
130
- return;
131
- }
132
- }
133
- e.setData(n.map(A));
118
+ k = f(e, o(t), k);
134
119
  }
135
- return s(() => h.features, (e) => {
120
+ return s(() => g.features, (e) => {
136
121
  let t = JSON.stringify(e ?? null);
137
- t !== C && (C = t, e && y?.setFeatures({ ...e }));
138
- }, { deep: !0 }), s(() => h.theme, (e) => {
139
- y?.setTheme(_(e));
140
- }), s(() => h.chartType, (e) => {
141
- y?.setChartType(e);
122
+ t !== w && (w = t, e && b?.setFeatures({ ...e }));
123
+ }, { deep: !0 }), s(() => g.theme, (e) => {
124
+ b?.setTheme(v(e));
125
+ }), s(() => g.chartType, (e) => {
126
+ b?.setChartType(e);
142
127
  }), s(() => [
143
- h.data,
144
- h.data?.length,
145
- k(h.data?.[h.data.length - 1])
128
+ g.data,
129
+ g.data?.length,
130
+ A(g.data?.[g.data.length - 1])
146
131
  ], () => {
147
- h.data && y && j(y, h.data);
148
- }), s(() => h.indicators, (e) => {
149
- y && f(y, S(e), b);
150
- }, { deep: !0 }), s(() => h.signalMarkers, (e) => {
151
- e && y?.setSignalMarkers(e);
152
- }, { deep: !0 }), s(() => h.signalMarkerStyle, (e) => {
153
- e && y?.setSignalMarkerStyle(e);
154
- }, { deep: !0 }), s(() => h.tradeZones, (e) => {
155
- e && y?.setTradeZones(e);
156
- }, { deep: !0 }), s(() => h.tradeZoneStyle, (e) => {
157
- e && y?.setTradeZoneStyle(e);
158
- }, { deep: !0 }), s(() => h.overrides, (e) => {
159
- y?.setOverrides({ ...e });
132
+ b && (g.data ? j(b, g.data) : k = null);
133
+ }), s(() => g.indicators, (e) => {
134
+ b && p(b, C(e), x);
135
+ }, { deep: !0 }), s(() => g.signalMarkers, (e) => {
136
+ e && b?.setSignalMarkers(e);
137
+ }, { deep: !0 }), s(() => g.signalMarkerStyle, (e) => {
138
+ e && b?.setSignalMarkerStyle(e);
139
+ }, { deep: !0 }), s(() => g.tradeZones, (e) => {
140
+ e && b?.setTradeZones(e);
141
+ }, { deep: !0 }), s(() => g.tradeZoneStyle, (e) => {
142
+ e && b?.setTradeZoneStyle(e);
143
+ }, { deep: !0 }), s(() => g.overrides, (e) => {
144
+ b?.setOverrides({ ...e });
160
145
  }, { deep: !0 }), (t, n) => (i(), e("div", {
161
146
  ref_key: "containerEl",
162
- ref: v,
147
+ ref: y,
163
148
  style: {
164
149
  width: "100%",
165
150
  height: "100%"
@@ -168,6 +153,6 @@ var p = /* @__PURE__ */ t({
168
153
  }
169
154
  });
170
155
  //#endregion
171
- export { p as TradeCanvas };
156
+ export { m as TradeCanvas };
172
157
 
173
158
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/TradeCanvas.vue","../src/TradeCanvas.vue"],"sourcesContent":["<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. A bar pushed onto the same array, or its last bar changed,\n * goes in as a live tick (the view stays where it is); a new array is a new\n * series. An older bar changed in place needs a new array.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart: the array, how many, and the last one as text. */\nlet fed: { bars: readonly OHLCBar[]; length: number; last: string } | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\nconst plain = (b: OHLCBar): OHLCBar => ({ ...toRaw(b) });\n\n/**\n * Bars into the chart: on the same array, a bar pushed or the last one\n * changed goes in as a tick, so the view stays put and only the end is\n * recalculated; anything else is a new series. The chart gets plain bars, not\n * Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n const bars = toRaw(data);\n const n = bars.length;\n const last = barKey(bars[n - 1]);\n const prev = fed;\n fed = { bars, length: n, last };\n if (prev && prev.bars === bars && prev.length > 0) {\n if (n === prev.length) {\n if (last !== prev.last) target.updateLastBar(plain(bars[n - 1]));\n return;\n }\n if (n === prev.length + 1) {\n target.updateLastBar(plain(bars[n - 2]));\n target.appendBar(plain(bars[n - 1]));\n return;\n }\n }\n target.setData(bars.map(plain));\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => { if (props.data && chart) feed(chart, props.data); },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n","<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. A bar pushed onto the same array, or its last bar changed,\n * goes in as a live tick (the view stays where it is); a new array is a new\n * series. An older bar changed in place needs a new array.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart: the array, how many, and the last one as text. */\nlet fed: { bars: readonly OHLCBar[]; length: number; last: string } | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\nconst plain = (b: OHLCBar): OHLCBar => ({ ...toRaw(b) });\n\n/**\n * Bars into the chart: on the same array, a bar pushed or the last one\n * changed goes in as a tick, so the view stays put and only the end is\n * recalculated; anything else is a new series. The chart gets plain bars, not\n * Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n const bars = toRaw(data);\n const n = bars.length;\n const last = barKey(bars[n - 1]);\n const prev = fed;\n fed = { bars, length: n, last };\n if (prev && prev.bars === bars && prev.length > 0) {\n if (n === prev.length) {\n if (last !== prev.last) target.updateLastBar(plain(bars[n - 1]));\n return;\n }\n if (n === prev.length + 1) {\n target.updateLastBar(plain(bars[n - 2]));\n target.appendBar(plain(bars[n - 1]));\n return;\n }\n }\n target.setData(bars.map(plain));\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => { if (props.data && chart) feed(chart, props.data); },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/TradeCanvas.vue","../src/TradeCanvas.vue"],"sourcesContent":["<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n feedBars,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n FedBars,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. Bars that go on from those given before, the last one\n * changed or bars added after it, go in as live ticks (the view isn't\n * reset): pushed onto the same array, changed in place, or in a new array.\n * Any other bars are a new series, as are yours after a stream gave the\n * chart bars of its own. An older bar changed needs a new array with a new\n * bar in its place.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart (`feedBars`). */\nlet fed: FedBars | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\n\n/**\n * Bars into the chart: bars that go on from those given before (a bar pushed\n * or the last one changed, in place or in a new array) go in as ticks, so the\n * view stays put and only the end is recalculated; anything else is a new\n * series. The chart gets plain copies, not Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n fed = feedBars(target, toRaw(data), fed);\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => {\n if (!chart) return;\n // Without data, the next bars are a new series: a stream may have come in between.\n if (props.data) feed(chart, props.data);\n else fed = null;\n },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n","<template>\n <div ref=\"containerEl\" :style=\"{ width: '100%', height: '100%' }\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, toRaw } from 'vue';\nimport {\n Chart,\n BinanceAdapter,\n DARK_THEME,\n LIGHT_THEME,\n syncIndicators,\n feedBars,\n} from '@tradecanvas/chart';\nimport type {\n ChartType,\n TimeFrame,\n FeaturesConfig,\n Theme,\n OHLCBar,\n SignalMarker,\n TradeZone,\n SignalMarkerStyle,\n TradeZoneStyle,\n DataAdapter,\n ChartStyleOverrides,\n ChartOptions,\n IndicatorSpec,\n FedBars,\n} from '@tradecanvas/chart';\n\nconst props = withDefaults(\n defineProps<{\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 /**\n * Bars: the chart shows these, and opens no stream of its own when it has\n * them at mount. Bars that go on from those given before, the last one\n * changed or bars added after it, go in as live ticks (the view isn't\n * reset): pushed onto the same array, changed in place, or in a new array.\n * Any other bars are a new series, as are yours after a stream gave the\n * chart bars of its own. An older bar changed needs a new array with a new\n * bar in its place.\n */\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 }>(),\n {\n symbol: 'BTCUSDT',\n timeframe: '5m' as TimeFrame,\n theme: 'dark',\n chartType: 'candlestick' as ChartType,\n indicators: () => [],\n stream: true,\n historyLimit: 500,\n autoScale: true,\n },\n);\n\nconst emit = defineEmits<{\n ready: [chart: Chart];\n crosshairMove: [payload: unknown];\n}>();\n\nfunction resolveTheme(theme: 'dark' | 'light' | Theme): Theme {\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'light') return LIGHT_THEME;\n return theme as Theme;\n}\n\nconst containerEl = ref<HTMLDivElement>();\nlet chart: Chart | null = null;\nconst indicatorIds = new Map<string, string>();\n/** The stream open now (`symbol|timeframe`), or null. */\nlet streamKey: string | null = null;\n\n/** The specs as plain values (not Vue's proxies), for the chart to keep. */\nconst plainSpecs = (specs: readonly IndicatorSpec[]): IndicatorSpec[] =>\n specs.map((spec) => (typeof spec === 'string'\n ? spec\n : { id: spec.id, ...(spec.params ? { params: { ...spec.params } } : {}), ...(spec.position ? { position: spec.position } : {}) }));\n/** The features last applied, as text. */\nlet featuresKey = JSON.stringify(props.features ?? null);\n\n/** A stream of its own while it has no data; `stream: false` closes it. Data given later leaves it be. */\nfunction syncStream(): void {\n if (!chart) return;\n if (!props.stream) {\n if (streamKey !== null) chart.disconnectStream();\n streamKey = null;\n return;\n }\n if (props.data) return;\n const key = `${props.symbol}|${props.timeframe}`;\n if (streamKey === key) return;\n const opened = streamKey !== null;\n streamKey = key;\n if (opened) chart.disconnectStream();\n const adapter = props.adapter ?? new BinanceAdapter();\n chart.connect({ adapter, symbol: props.symbol, timeframe: props.timeframe, historyLimit: props.historyLimit });\n if (opened && props.watermarkText) chart.setWatermark(props.watermarkText);\n}\n\nfunction getChart(): Chart | null {\n return chart;\n}\n\nfunction screenshot(filename?: string): void {\n chart?.screenshot(filename);\n}\n\nfunction screenshotDataURL(): string | null {\n return chart?.screenshotDataURL() ?? null;\n}\n\ndefineExpose({ getChart, screenshot, screenshotDataURL });\n\nonMounted(() => {\n if (!containerEl.value) return;\n\n chart = new Chart(containerEl.value, {\n rightMargin: 5,\n crosshair: { mode: 'magnet' },\n ...props.options,\n chartType: props.chartType,\n theme: resolveTheme(props.theme),\n autoScale: props.autoScale,\n ...(props.watermarkText\n ? { watermark: { text: props.watermarkText, fontSize: 48, color: 'rgba(255,255,255,0.03)' } }\n : {}),\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 ...props.options?.features,\n ...props.features,\n },\n });\n\n if (props.data) feed(chart, props.data);\n else syncStream();\n\n // Apply initial reactive collections not covered by the constructor options.\n // Vue's watches aren't immediate, so without this the first render of\n // indicators / signal markers / trade zones would be dropped.\n syncIndicators(chart, plainSpecs(props.indicators), indicatorIds);\n if (props.signalMarkers) chart.setSignalMarkers(props.signalMarkers);\n if (props.signalMarkerStyle) chart.setSignalMarkerStyle(props.signalMarkerStyle);\n if (props.tradeZones) chart.setTradeZones(props.tradeZones);\n if (props.tradeZoneStyle) chart.setTradeZoneStyle(props.tradeZoneStyle);\n if (props.overrides) chart.setOverrides(props.overrides);\n\n emit('ready', chart);\n});\n\nonUnmounted(() => {\n if (chart) {\n chart.disconnectStream();\n chart.destroy();\n chart = null;\n }\n});\n\nwatch(() => [props.symbol, props.timeframe, !props.data, props.stream] as const, syncStream);\n/** The bars last given to the chart (`feedBars`). */\nlet fed: FedBars | null = null;\nconst barKey = (b: OHLCBar | undefined) => (b ? `${b.time},${b.open},${b.high},${b.low},${b.close},${b.volume}` : '');\n\n/**\n * Bars into the chart: bars that go on from those given before (a bar pushed\n * or the last one changed, in place or in a new array) go in as ticks, so the\n * view stays put and only the end is recalculated; anything else is a new\n * series. The chart gets plain copies, not Vue's proxies, to read in its frames.\n */\nfunction feed(target: Chart, data: readonly OHLCBar[]): void {\n fed = feedBars(target, toRaw(data), fed);\n}\n\nwatch(() => props.features, (f) => {\n const key = JSON.stringify(f ?? null);\n if (key === featuresKey) return;\n featuresKey = key;\n if (f) chart?.setFeatures({ ...f });\n}, { deep: true });\n\nwatch(() => props.theme, (t) => { chart?.setTheme(resolveTheme(t)); });\nwatch(() => props.chartType, (ct) => { chart?.setChartType(ct); });\n// The array, its length and the last bar's fields: a bar pushed or the\n// forming bar changed, without walking every bar as a deep watch does.\nwatch(\n () => [props.data, props.data?.length, barKey(props.data?.[props.data.length - 1])],\n () => {\n if (!chart) return;\n // Without data, the next bars are a new series: a stream may have come in between.\n if (props.data) feed(chart, props.data);\n else fed = null;\n },\n);\n\nwatch(\n () => props.indicators,\n (specs) => { if (chart) syncIndicators(chart, plainSpecs(specs), indicatorIds); },\n { deep: true },\n);\n\nwatch(() => props.signalMarkers, (m) => { if (m) chart?.setSignalMarkers(m); }, { deep: true });\nwatch(() => props.signalMarkerStyle, (s) => { if (s) chart?.setSignalMarkerStyle(s); }, { deep: true });\nwatch(() => props.tradeZones, (z) => { if (z) chart?.setTradeZones(z); }, { deep: true });\nwatch(() => props.tradeZoneStyle, (s) => { if (s) chart?.setTradeZoneStyle(s); }, { deep: true });\n// All of the host's overrides: keys left out of the prop are taken away.\nwatch(() => props.overrides, (o) => { chart?.setOverrides({ ...o }); }, { deep: true });\n</script>\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradecanvas/vue",
3
- "version": "1.3.29",
3
+ "version": "1.3.31",
4
4
  "type": "module",
5
5
  "description": "Vue 3 component for @tradecanvas/chart — reactive props, template ref, zero boilerplate.",
6
6
  "license": "MIT",
@@ -38,7 +38,7 @@
38
38
  "README.md"
39
39
  ],
40
40
  "dependencies": {
41
- "@tradecanvas/chart": "1.47.0"
41
+ "@tradecanvas/chart": "1.49.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "vue": ">=3.3.0"