@iccandle/vuejs-widget
v0.2.0
Published
Vue scanner overlay for TradingView Charting Library — ICCandle pattern search, remote theming, and plugin iframe integration.
Readme
@iccandle/vuejs-widget
Vue 3 scanner overlay for an existing TradingView Charting Library widget. It adds ICCandle’s draggable scanner popup, pattern search, replay of predicted candles, and a results embed.
Published as ESM and CommonJS. Component styles are bundled and injected at runtime — no separate CSS import.
Install
npm install @iccandle/vuejs-widget
# or
pnpm add @iccandle/vuejs-widgetvue ^3.5.0 is a peer dependency. Install it in your app. axios is a runtime dependency of this package.
Prerequisites
- Vue 3.5+
- TradingView Charting Library — obtain it under your own license, host the static assets (for example
/charting_library/), and load it at runtime vialibrary_path. This package does not ship the charting library. - A results iframe (or equivalent) that loads ICCandle’s embed app. Scan/tracker actions require the user to sign in inside that embed; the widget stores the session as
iccandle_tokeninlocalStorage.
Quick start
<script setup lang="ts">
import { ref, shallowRef, watch } from "vue";
import type { IChartingLibraryWidget } from "charting_library/charting_library";
import {
WidgetIccandle,
WIDGET_RESULT_URL,
handleResultIframeLoad,
} from "@iccandle/vuejs-widget";
const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);
const language = "en" as const;
const theme = "dark" as const;
const iframeSrc = ref(`${WIDGET_RESULT_URL}/${language}?theme=${theme}&header=true`);
const iframeLoaded = ref(false);
const resultIframeRef = ref<HTMLIFrameElement | null>(null);
const resultIframeBox = { current: null as HTMLIFrameElement | null };
watch(
resultIframeRef,
(el) => {
resultIframeBox.current = el;
},
{ immediate: true },
);
function handleSubmit(src: string) {
iframeSrc.value = src;
}
function handleIframeLoad() {
iframeLoaded.value = true;
handleResultIframeLoad(resultIframeRef.value);
}
</script>
<template>
<div class="iccandle-selector-widget__container">
<div class="iccandle-selector-widget__chart-pane">
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="handleSubmit"
:theme="theme"
:language="language"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
>
<!-- Bootstrap TradingView here and assign chartWidget when ready -->
<div id="tv_chart_container" style="height: 100%" />
</WidgetIccandle>
</div>
<iframe
ref="resultIframeRef"
:src="iframeSrc"
class="iccandle-selector-widget__iframe"
title="ICCandle results"
@load="handleIframeLoad"
/>
</div>
</template>Replace the chart placeholder with your TradingView initialization and pass the IChartingLibraryWidget instance when the widget is created (typically right after new widget(options)).
Usage
1. Host the Charting Library
Copy the TradingView Charting Library build into a path your app can serve as static files (for example public/charting_library/ in Vite). Point library_path at that URL. The library is not bundled inside @iccandle/vuejs-widget.
2. Bootstrap TradingView with replay support
Wrap your datafeed with withPlayChart so the widget can inject predicted bars during replay. Register getCustomIndicators so those bars render as generated-candle studies:
import { onMounted, onBeforeUnmount, ref, shallowRef } from "vue";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import { withPlayChart, getCustomIndicators } from "@iccandle/vuejs-widget";
const LIBRARY_PATH = "/charting_library/";
const containerRef = ref<HTMLDivElement | null>(null);
const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);
onMounted(() => {
const el = containerRef.value;
if (!el) return;
const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: withPlayChart(yourDatafeed),
locale: "en",
autosize: true,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
custom_indicators_getter: () => getCustomIndicators("dark"),
};
const tv = new widget(options);
chartWidget.value = tv;
onBeforeUnmount(() => {
try {
tv.remove();
} catch {
/* no-op */
}
chartWidget.value = null;
});
});withPlayChart also accepts a factory plus its arguments: withPlayChart(createDatafeed, arg1, arg2).
You must supply a valid datafeed, symbol, interval, locale, and any other options required by your TradingView license.
3. Wrap the chart with WidgetIccandle
The component must wrap the same subtree that contains the chart container so the scanner overlay positions correctly. Pass null for chartWidget until the instance is ready.
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="handleSubmit"
theme="dark"
language="en"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
:available-intervals="['1', '5', '15', '30', '60']"
>
<div ref="containerRef" style="height: 100%; min-height: 400px" />
</WidgetIccandle>4. Handle the results iframe URL
After a successful scan, submitCallback receives a full HTTPS URL for the ICCandle embed. Typical query parameters include symbol, reference resolution, scan resolution, candle cache id (cid), time window (from / to), theme, and optional filters (fs, period, model, temp).
Split pane (recommended) — keep an iframe beside the chart and update its src:
const submitCallback = (nextSrc: string) => {
iframeSrc.value = nextSrc;
};Open in a new tab
submitCallback: (iframeSrc) => {
window.open(iframeSrc, "_blank", "noopener,noreferrer");
};On iframe load, call handleResultIframeLoad(iframe) so the embed knows the parent origin (required for sign-in to write iccandle_token) and so a Stripe return of ?payment=success can refresh the subscription without a full reload.
result-iframe-ref is a mutable { current } box, not a Vue ref. Sync it from the iframe element:
const resultIframeRef = ref<HTMLIFrameElement | null>(null);
const resultIframeBox = { current: null as HTMLIFrameElement | null };
watch(resultIframeRef, (el) => {
resultIframeBox.current = el;
}, { immediate: true });Sign-in
Scan and Pattern Tracker require localStorage.iccandle_token. The results iframe posts auth.signIn / auth.signOut; the widget listens and stores or clears the token. Until the user signs in, those actions show a login prompt.
Theme
| theme | Behavior |
| ------- | -------- |
| "light" (default) | Light scanner chrome and embed theme. |
| "dark" | Dark scanner chrome; adds .iccandle-dark on the root. |
| "system" | Follows prefers-color-scheme. |
The root uses CSS custom properties you can override: --iccandle-primary, --iccandle-border, --iccandle-text, --iccandle-background, --iccandle-primary-gradient-end, --iccandle-font.
Language
language accepts "en" | "zh" | "vi" | "th" | "ko" | "ja" | "mn" | "ru". Unknown values fall back to "en". The value is used for scanner copy and is forwarded into embed URLs.
Layout helpers
Injected CSS includes split-pane classes used in the demo:
| Class | Role |
| ----- | ---- |
| .iccandle-selector-widget__container | Flex row (stacks below 1024px). --chart-pane-size defaults to 60%. |
| .iccandle-selector-widget__chart-pane | Chart column. |
| .iccandle-selector-widget__resize-handle | Drag handle; add --stacked for the stacked layout. |
| .iccandle-selector-widget__iframe | Results pane. |
Full working example
See src/App.vue, src/tradingview/TradingviewChart.vue, and src/tradingview/TradingviewChartContainer.vue in this repository.
API
Exports
| Name | Kind | Description |
| ---- | ---- | ----------- |
| WidgetIccandle | Component | Scanner overlay around your chart subtree. |
| WidgetIccandleProps | Type | Public props of WidgetIccandle. |
| WidgetLanguage | Type | Supported UI languages. |
| withPlayChart | Function | Wrap a TradingView datafeed (or factory) so replay can inject bars and block live ticks. |
| getCustomIndicators | Function | Returns custom studies used to draw generated / predicted candles. Pass "light" or "dark". |
| GeneratedCandlesTheme | Type | "light" | "dark" for getCustomIndicators. |
| handleResultIframeLoad | Function | Post parent origin (and optional payment-success) to the results iframe. |
| WIDGET_RESULT_URL | Constant | Default embed origin: https://embed-iccandle-app.iccandle.ai. |
| OPEN_PRICING_MESSAGE_TYPE | Constant | "open-pricing" postMessage type. |
| postOpenPricingToEmbed | Function | Ask the results iframe to open pricing. Returns false if no contentWindow. |
| postOpenPricingToParent | Function | Forward pricing to window.parent when this widget is itself embedded. |
| postOpenPricingToWindow | Function | Post an open-pricing message to an arbitrary Window. |
| OpenPricingMessage | Type | { type, theme?, language? }. |
WidgetIccandle props
| Prop | Type | Required | Default | Description |
| ---- | ---- | -------- | ------- | ----------- |
| chartWidget | IChartingLibraryWidget \| null | Yes | — | Live TradingView widget (null until ready). |
| submitCallback | (iframeSrc: string) => void | Yes | — | Called with the embed URL after a scan (and for some pricing / news navigations). |
| theme | "light" \| "dark" \| "system" | No | "light" | Scanner and forwarded embed theme. |
| language | WidgetLanguage | No | "en" | Scanner UI language. |
| onCloseResult | () => void | No | — | Called when the embed posts selector.closeResult. |
| iframeLoaded | boolean | No | true | When false, Scan and Pattern Tracker are disabled. Set true after the results iframe has loaded. |
| resultIframeRef | { current: HTMLIFrameElement \| null } \| null | No | — | Results iframe box. Receives open-pricing and chart-resolution postMessages. |
| isShowScanButton | boolean | No | true | Show Scan inside the popup. |
| isShowTrackerButton | boolean | No | true | Show Pattern Tracker inside the popup. |
| isShowScannerPopup | boolean | No | true | Show the scanner popup. |
| availableIntervals | string[] | No | — | Resolutions allowed when drawing a pattern date range from embed selection. |
| onScanClick | () => void | No | — | Fired when the user clicks Scan, before login / config / scan logic. |
Default slot receives { chartRefs } (highlightBarsRef, generated-candle study ids). The same object is exposed on the component instance as chartRefs.
Behavior
- Subscribes to chart readiness, resolution, symbol, visible range, drawings, and timescale-mark clicks.
- Draws a
date_rangeshape for the scan window; bar count and exported candles drive the scanner. - Posts selected candles to ICCandle’s cache, then calls
submitCallbackwith the embed URL. - Listens for
windowmessageevents from the embed (replay, auth, news, pattern selection, navigation, loading). - Clears persisted news mark keys (
tv:selected-news-events,tv:clicked-news-event) when a scan starts.
Embed postMessage names
The widget handles JSON messages with a name (and optional data) from the results iframe:
| Name | Effect |
| ---- | ------ |
| chart.play | Replay / predicted candles on the chart. |
| chart.stop | Stop replay and restore live ticks. |
| chart.requestResolution | Re-post the current chart resolution to the iframe. |
| selector.closeResult | Invoke onCloseResult. |
| selector.loading | Toggle scanner loading state. |
| auth.signIn | Store data.idToken as iccandle_token. |
| auth.signOut | Remove iccandle_token. |
| news.eventClicked | Draw event marks / target window on the chart. |
| news.back / news.backToSimilarEvents | Clear event overlay / replay. |
| pattern.classicPatternSelected / pattern.custom_pattern_selected | Draw the compared pattern’s date range. |
| pattern.clearClassicPatternSelected / pattern.clear_custom_pattern_selected | Clear pattern overlay. |
| nav.click | Handle embed navigation (clears date range on /news routes). |
The host → embed helpers use a different shape: { type: "parent-origin" | "payment-success" | "open-pricing" | "chart-resolution", ... }.
Optional: timescale marks (news / events)
If your datafeed implements getTimescaleMarks, you can surface stored events from localStorage (tv:selected-news-events, tv:clicked-news-event) as marks on the time axis. See src/lib/data-feed.ts in this repo.
Development (this repo)
| Script | Command | Purpose |
| ------ | ------- | ------- |
| Dev demo | pnpm dev | Vite app with a local charting library. |
| Library build | pnpm build | Emits dist/ (ESM, CJS, injected CSS, declarations). |
| App build | pnpm build:app | Full demo app build. |
prepublishOnly runs build before publish.
License
MIT. TradingView Charting Library is subject to its own license from TradingView.
