@iccandle/selector 0.0.1
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 +358 -0
- package/dist/selector-widget.cjs +88 -0
- package/dist/selector-widget.d.ts +13250 -0
- package/dist/selector-widget.js +11710 -0
- package/package.json +106 -0
package/README.md
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# widget-iccandle
|
|
2
|
+
|
|
3
|
+
React component that wraps an existing [TradingView Charting Library](https://www.tradingview.com/charting-library-docs/) widget and adds ICCandle’s **scanner UI**: a draggable popup to run pattern search over a user-selected bar range, with theming loaded from ICCandle’s API.
|
|
4
|
+
|
|
5
|
+
Published as ESM and CommonJS; component styles are bundled and injected at runtime (no separate CSS import).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install widget-iccandle
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Prerequisites
|
|
14
|
+
|
|
15
|
+
- **React 18+** — `react` and `react-dom` are **peer dependencies** (install them in your app).
|
|
16
|
+
- **TradingView Charting Library** — obtain it under your own license from TradingView, host the static assets (e.g. under `/charting_library/` in your public folder), and load the library at runtime. **This package does not ship the charting library.**
|
|
17
|
+
- **ICCandle API key** — format `icc_search_` followed by 48 hex characters, issued by ICCandle for theme, candle cache, and scanner validation.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { useState } from "react";
|
|
23
|
+
import type { IChartingLibraryWidget } from "charting_library/charting_library";
|
|
24
|
+
import { SelectorWidget } from "widget-iccandle";
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
|
|
28
|
+
null,
|
|
29
|
+
);
|
|
30
|
+
const widgetKey = "icc_search_..."; // ICCandle-issued key (48 hex chars after prefix)
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<SelectorWidget
|
|
34
|
+
chartWidget={chartWidget}
|
|
35
|
+
widgetKey={widgetKey}
|
|
36
|
+
theme="system"
|
|
37
|
+
submitCallback={(iframeSrc) => {
|
|
38
|
+
// Full URL for the ICCandle plugin iframe (open in modal, new window, etc.)
|
|
39
|
+
console.log(iframeSrc);
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
{/* Your chart container + TradingView bootstrap; call setChartWidget when ready */}
|
|
43
|
+
<div id="tv_chart_container" style={{ height: "100%" }} />
|
|
44
|
+
</SelectorWidget>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Replace the chart placeholder with your TradingView initialization and pass the `IChartingLibraryWidget` instance when `onChartReady` (or equivalent) fires.
|
|
50
|
+
|
|
51
|
+
## Usage guide
|
|
52
|
+
|
|
53
|
+
### Step 1 — Install the package
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install widget-iccandle
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Ensure `react` and `react-dom` are installed and meet the peer version range.
|
|
60
|
+
|
|
61
|
+
### Step 2 — Host the Charting Library
|
|
62
|
+
|
|
63
|
+
1. Copy the TradingView Charting Library build into a path your app can serve as static files (e.g. `public/charting_library/` in Vite or Create React App).
|
|
64
|
+
2. Import the library constructor from that path in your bundler setup (see TradingView’s integration docs for your framework). The library is **not** bundled inside `widget-iccandle`; it loads at runtime via `library_path` (or equivalent) on the widget options.
|
|
65
|
+
|
|
66
|
+
### Step 3 — Bootstrap TradingView and capture the widget instance
|
|
67
|
+
|
|
68
|
+
Create a ref for the chart DOM node, instantiate the widget in `useEffect`, store the instance in React state, and remove it on cleanup:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { useEffect, useRef, useState } from "react";
|
|
72
|
+
import type {
|
|
73
|
+
ChartingLibraryWidgetOptions,
|
|
74
|
+
IChartingLibraryWidget,
|
|
75
|
+
ResolutionString,
|
|
76
|
+
} from "charting_library/charting_library";
|
|
77
|
+
import { widget } from "charting_library/charting_library";
|
|
78
|
+
|
|
79
|
+
const LIBRARY_PATH = "/charting_library/"; // must match your hosted assets
|
|
80
|
+
|
|
81
|
+
// Inside your component:
|
|
82
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
83
|
+
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
|
|
84
|
+
null,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
const el = containerRef.current;
|
|
89
|
+
if (!el) return;
|
|
90
|
+
|
|
91
|
+
const options: ChartingLibraryWidgetOptions = {
|
|
92
|
+
container: el,
|
|
93
|
+
library_path: LIBRARY_PATH,
|
|
94
|
+
symbol: "EURUSD",
|
|
95
|
+
interval: "60" as ResolutionString,
|
|
96
|
+
datafeed: yourDatafeed,
|
|
97
|
+
locale: "en",
|
|
98
|
+
autosize: true,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const tv = new widget(options);
|
|
102
|
+
setChartWidget(tv);
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
try {
|
|
106
|
+
tv.remove();
|
|
107
|
+
} catch {
|
|
108
|
+
/* no-op */
|
|
109
|
+
}
|
|
110
|
+
setChartWidget(null);
|
|
111
|
+
};
|
|
112
|
+
}, [/* library_path, datafeed identity, or other inputs that should recreate the chart */]);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
You must supply a valid `datafeed`, `symbol`, `interval`, `locale`, and any other options required by your TradingView license and app. Import paths for `widget` and types differ by setup (`charting_library/charting_library`, `./public/charting_library`, etc.—follow TradingView’s docs for your bundler). If your integration only exposes the instance after `onChartReady`, call `setChartWidget` inside that callback instead of immediately after `new widget(...)`.
|
|
116
|
+
|
|
117
|
+
### Step 4 — Wrap the chart with `SelectorWidget`
|
|
118
|
+
|
|
119
|
+
`SelectorWidget` must wrap the same subtree that contains the chart container so the scanner overlay positions correctly. Pass the live widget instance (or `null` while mounting):
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { SelectorWidget } from "widget-iccandle";
|
|
123
|
+
|
|
124
|
+
const widgetKey = "icc_search_..."; // your ICCandle key
|
|
125
|
+
|
|
126
|
+
<SelectorWidget
|
|
127
|
+
chartWidget={chartWidget}
|
|
128
|
+
widgetKey={widgetKey}
|
|
129
|
+
theme="system"
|
|
130
|
+
submitCallback={(iframeSrc) => {
|
|
131
|
+
/* see Step 5 */
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
<div ref={containerRef} style={{ height: "100%", minHeight: 400 }} />
|
|
135
|
+
</SelectorWidget>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Step 5 — Handle the plugin iframe URL
|
|
139
|
+
|
|
140
|
+
After a successful scan setup, `submitCallback` receives a **full HTTPS URL** for the ICCandle plugin iframe. The query string typically includes the bar window (timestamps / size), symbol, `candle_id`, `apiKey` (your widget key), resolved theme, and any active filters—use it as-is in an iframe `src` or deep link.
|
|
141
|
+
|
|
142
|
+
**Open in a new tab**
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
submitCallback={(iframeSrc) => {
|
|
146
|
+
window.open(iframeSrc, "_blank", "noopener,noreferrer");
|
|
147
|
+
}}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Show in a modal or side panel**
|
|
151
|
+
|
|
152
|
+
Store the URL in state and render:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
{iframeSrc ? (
|
|
156
|
+
<iframe title="ICCandle pattern search" src={iframeSrc} className="..." />
|
|
157
|
+
) : null}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**TypeScript:** import `SelectorWidgetProps` if you wrap `SelectorWidget` in your own component and want explicit prop typing. Import `IChartingLibraryWidget` from **your** Charting Library typings path (`charting_library/charting_library` or the path your project uses)—this package does not re-export TradingView types.
|
|
161
|
+
|
|
162
|
+
### Theme and remote branding
|
|
163
|
+
|
|
164
|
+
- **`theme="light"` / `"dark"`** — forces that palette for the scanner chrome and for values forwarded into the plugin URL.
|
|
165
|
+
- **`theme="system"`** (recommended when you don’t control parent theme) — follows `prefers-color-scheme` for light/dark resolution.
|
|
166
|
+
- On mount, the widget fetches your org’s tokens from ICCandle (see [Widget key and remote theming](#widget-key-and-remote-theming)) and sets CSS custom properties on the widget root, e.g. `--iccandle-primary`, `--iccandle-border`, so the in-chart scanner matches your configured light/dark branding. The same resolved theme is reflected in the iframe URL so the plugin UI stays consistent.
|
|
167
|
+
|
|
168
|
+
### Full working example
|
|
169
|
+
|
|
170
|
+
Minimal end-to-end pattern: chart container ref, widget lifecycle, `SelectorWidget`, and iframe after submit. Replace `yourDatafeed` and widget options with your real datafeed and TradingView settings.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { useEffect, useRef, useState } from "react";
|
|
174
|
+
import type {
|
|
175
|
+
ChartingLibraryWidgetOptions,
|
|
176
|
+
IChartingLibraryWidget,
|
|
177
|
+
ResolutionString,
|
|
178
|
+
} from "charting_library/charting_library";
|
|
179
|
+
import { widget } from "charting_library/charting_library";
|
|
180
|
+
import { SelectorWidget } from "widget-iccandle";
|
|
181
|
+
|
|
182
|
+
const LIBRARY_PATH = "/charting_library/";
|
|
183
|
+
const WIDGET_KEY = "icc_search_your48hexcharactershere............";
|
|
184
|
+
|
|
185
|
+
export function ChartWithIccandleScanner() {
|
|
186
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
187
|
+
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
|
|
188
|
+
null,
|
|
189
|
+
);
|
|
190
|
+
const [pluginSrc, setPluginSrc] = useState("");
|
|
191
|
+
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
const el = containerRef.current;
|
|
194
|
+
if (!el) return;
|
|
195
|
+
|
|
196
|
+
const options: ChartingLibraryWidgetOptions = {
|
|
197
|
+
container: el,
|
|
198
|
+
library_path: LIBRARY_PATH,
|
|
199
|
+
symbol: "EURUSD",
|
|
200
|
+
interval: "60" as ResolutionString,
|
|
201
|
+
datafeed: yourDatafeed,
|
|
202
|
+
locale: "en",
|
|
203
|
+
autosize: true,
|
|
204
|
+
fullscreen: false,
|
|
205
|
+
drawings_access: {
|
|
206
|
+
type: "black",
|
|
207
|
+
tools: [{ name: "Date Range" }],
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const tv = new widget(options);
|
|
212
|
+
setChartWidget(tv);
|
|
213
|
+
|
|
214
|
+
return () => {
|
|
215
|
+
try {
|
|
216
|
+
tv.remove();
|
|
217
|
+
} catch {
|
|
218
|
+
/* no-op */
|
|
219
|
+
}
|
|
220
|
+
setChartWidget(null);
|
|
221
|
+
};
|
|
222
|
+
}, []);
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
226
|
+
<div style={{ height: 500, width: "100%" }}>
|
|
227
|
+
<SelectorWidget
|
|
228
|
+
chartWidget={chartWidget}
|
|
229
|
+
widgetKey={WIDGET_KEY}
|
|
230
|
+
theme="system"
|
|
231
|
+
submitCallback={(iframeSrc) => setPluginSrc(iframeSrc)}
|
|
232
|
+
>
|
|
233
|
+
<div ref={containerRef} style={{ height: "100%", width: "100%" }} />
|
|
234
|
+
</SelectorWidget>
|
|
235
|
+
</div>
|
|
236
|
+
{pluginSrc ? (
|
|
237
|
+
<iframe
|
|
238
|
+
title="ICCandle search"
|
|
239
|
+
src={pluginSrc}
|
|
240
|
+
style={{ width: "100%", height: 600, border: "1px solid #ccc" }}
|
|
241
|
+
/>
|
|
242
|
+
) : null}
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
For a concrete in-repo reference (custom datafeed, timezone, visibility handling), see [`src/tradingview/TradingviewChart.tsx`](src/tradingview/TradingviewChart.tsx) in this repository’s dev app.
|
|
249
|
+
|
|
250
|
+
### Common patterns
|
|
251
|
+
|
|
252
|
+
| Pattern | Approach |
|
|
253
|
+
| -------- | -------- |
|
|
254
|
+
| **Modal iframe** | On `submitCallback`, set state and render `<iframe src={url} />` inside a `<dialog>`, Radix Dialog, MUI Modal, etc. |
|
|
255
|
+
| **New tab** | `window.open(iframeSrc, "_blank", "noopener,noreferrer")`. |
|
|
256
|
+
| **Split layout** | Keep the chart in one column and mount the iframe in another when `pluginSrc` is set (as in the example above). |
|
|
257
|
+
| **News / events on the time axis** | If your datafeed implements `getTimescaleMarks`, you can sync marks with `localStorage` — see [Optional: timescale marks (news/events)](#optional-timescale-marks-newsevents). |
|
|
258
|
+
|
|
259
|
+
### Troubleshooting
|
|
260
|
+
|
|
261
|
+
| Issue | What to check |
|
|
262
|
+
| ----- | --------------- |
|
|
263
|
+
| Chart area stays blank | `library_path` must point to the folder URL where TradingView static files are served; container ref must be mounted before `new widget(...)`. |
|
|
264
|
+
| `chartWidget` is always `null` | Ensure you call `setChartWidget` after the widget is created (or inside `onChartReady` if your integration requires it). |
|
|
265
|
+
| Scanner or theme looks wrong | Confirm `widgetKey` is valid; theme fetch uses `api-key: <widgetKey>`. Network or auth failures may leave defaults. |
|
|
266
|
+
| CORS / network errors on theme API | In local dev, browser calls to `api.iccandle.ai` may be blocked by CORS unless you proxy; the widget should still function with fallback styling—verify in production or behind your proxy. |
|
|
267
|
+
| Date range tool missing | Enable the Date Range drawing in `drawings_access` (see full example) so users can select the bar window the scanner uses. |
|
|
268
|
+
|
|
269
|
+
## API
|
|
270
|
+
|
|
271
|
+
### Exports
|
|
272
|
+
|
|
273
|
+
| Name | Kind | Description |
|
|
274
|
+
| -------------------- | ------ | -------------------------------------------- |
|
|
275
|
+
| `SelectorWidget` | Component | Scanner overlay around your chart subtree |
|
|
276
|
+
| `SelectorWidgetProps`| Type | Props for `SelectorWidget` |
|
|
277
|
+
|
|
278
|
+
### `SelectorWidget` props
|
|
279
|
+
|
|
280
|
+
| Prop | Type | Required | Description |
|
|
281
|
+
| ----------------- | --------------------------------------- | -------- | ----------- |
|
|
282
|
+
| `chartWidget` | `IChartingLibraryWidget \| null` | Yes | Live TradingView widget instance (`null` until ready). |
|
|
283
|
+
| `children` | `ReactNode` | Yes | Your chart UI (e.g. container + library bootstrap). |
|
|
284
|
+
| `widgetKey` | `string` | Yes | ICCandle API key (`icc_search_` + 48 hex chars). Used for theme fetch, candle cache, and scanner validation. |
|
|
285
|
+
| `submitCallback` | `(iframeSrc: string) => void` | Yes | Called after a successful scan setup with the **plugin iframe URL** (query string includes window size, timestamps, symbol, `candle_id`, `apiKey`, theme, optional filters). |
|
|
286
|
+
| `theme` | `"light" \| "dark" \| "system"` | No | Defaults to sensible behavior; `"system"` follows `prefers-color-scheme`. Affects resolved theme tokens and the plugin URL. |
|
|
287
|
+
|
|
288
|
+
### Widget key and remote theming
|
|
289
|
+
|
|
290
|
+
On mount, the component loads branding colors from ICCandle:
|
|
291
|
+
|
|
292
|
+
- **URL:** `https://api.iccandle.ai/corporate-client/v1/widgetStyle/search/user/?service_type=search`
|
|
293
|
+
- **Header:** `api-key: <widgetKey>`
|
|
294
|
+
|
|
295
|
+
CSS custom properties (`--iccandle-primary`, `--iccandle-border`, etc.) are applied on the widget root so the scanner matches your configured light/dark tokens.
|
|
296
|
+
|
|
297
|
+
**Theme prop vs. API tokens:** The `theme` prop (`"light"` \| `"dark"` \| `"system"`) chooses which variant of those tokens to apply and is also forwarded in the plugin iframe URL. `"system"` tracks `prefers-color-scheme` so the scanner and plugin stay aligned with the user’s OS preference when you don’t pass an explicit light/dark mode from your app shell.
|
|
298
|
+
|
|
299
|
+
### Behavior summary
|
|
300
|
+
|
|
301
|
+
- Subscribes to chart readiness, resolution, symbol changes, and drawing events.
|
|
302
|
+
- Manages a `date_range` multipoint drawing so the user can adjust the bar window; window size and exported candles drive the scanner.
|
|
303
|
+
- Posts candles to ICCandle’s cache endpoint before opening the plugin URL (see scanner implementation for details).
|
|
304
|
+
- Listens for `window` `message` events for chart/news integration and can clear persisted news mark selections (`localStorage` key `tv:selected-news-events`) when starting a scan.
|
|
305
|
+
|
|
306
|
+
## Optional: timescale marks (news/events)
|
|
307
|
+
|
|
308
|
+
If your data feed implements `getTimescaleMarks`, you can surface stored events (e.g. from `localStorage` under `tv:selected-news-events`) as marks on the time axis. Example shape:
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
getTimescaleMarks: async (
|
|
312
|
+
symbolInfo: LibrarySymbolInfo,
|
|
313
|
+
from: number,
|
|
314
|
+
to: number,
|
|
315
|
+
onResult: GetMarksCallback<TimescaleMark>,
|
|
316
|
+
) => {
|
|
317
|
+
const marks: TimescaleMark[] = [];
|
|
318
|
+
try {
|
|
319
|
+
const allNewsEvents = JSON.parse(
|
|
320
|
+
localStorage.getItem("tv:selected-news-events") || "[]",
|
|
321
|
+
) as NewsEventType[];
|
|
322
|
+
|
|
323
|
+
allNewsEvents?.forEach(({ id, timestamp, event_name, currency }) => {
|
|
324
|
+
if (!id || !Number.isFinite(timestamp) || timestamp <= 0) return;
|
|
325
|
+
if (marks.some((m) => String(m.id) === String(id))) return;
|
|
326
|
+
|
|
327
|
+
marks.push({
|
|
328
|
+
id,
|
|
329
|
+
time: timestamp / 1000,
|
|
330
|
+
color: "green",
|
|
331
|
+
label: event_name.slice(0, 1) || "N",
|
|
332
|
+
tooltip: [event_name],
|
|
333
|
+
...(currency ? { imageUrl: `/images/symbols/${currency}.svg` } : {}),
|
|
334
|
+
showLabelWhenImageLoaded: true,
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
} catch {
|
|
338
|
+
/* ignore */
|
|
339
|
+
}
|
|
340
|
+
onResult(marks);
|
|
341
|
+
};
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Adjust paths and types to match your app and TradingView typings.
|
|
345
|
+
|
|
346
|
+
## Development (this repo)
|
|
347
|
+
|
|
348
|
+
| Script | Command | Purpose |
|
|
349
|
+
| ------------- | ------------ | ------- |
|
|
350
|
+
| Dev demo | `npm run dev` | Vite app with local charting library (see `vite.config.ts`). |
|
|
351
|
+
| Library build | `npm run build` | Emits `dist/` (JS, CJS, bundled CSS injection, declarations). |
|
|
352
|
+
| Lint | `npm run lint` | ESLint. |
|
|
353
|
+
|
|
354
|
+
`prepublishOnly` runs `build` before publish.
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT. TradingView Charting Library is subject to its own license from TradingView.
|