From 1e1b95fdc046137ae53e730c12cca86183f76a10 Mon Sep 17 00:00:00 2001 From: caty21 Date: Sat, 27 Jun 2026 22:36:27 +0200 Subject: [PATCH] fix: TvMiniChart utilise embed-widget-mini-symbol-overview.js (pas tv.js) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MiniSymbolOverview n'existe pas dans tv.js → try/catch avalait l'erreur silencieusement → graphiques vides. Correction : embed script dédié avec config inline (script.text). Structure DOM correcte : .tradingview-widget-container__widget + script sibling. TvAdvancedChart (tv.js + TradingView.widget) inchangé. Co-Authored-By: Claude Sonnet 4.6 --- components/TvChart.tsx | 226 ++++++++++++++++++++++++----------------- 1 file changed, 131 insertions(+), 95 deletions(-) diff --git a/components/TvChart.tsx b/components/TvChart.tsx index afbed5c..63fe312 100644 --- a/components/TvChart.tsx +++ b/components/TvChart.tsx @@ -1,19 +1,18 @@ "use client"; -import { useEffect, useRef, useId } from "react"; +import { useEffect, useRef } from "react"; -// Déclaration globale TradingView (chargé via script) -declare global { - interface Window { - TradingView?: { - MiniSymbolOverview: new (config: Record) => void; - widget: new (config: Record) => void; - }; - } -} +// ── TvMiniChart ─────────────────────────────────────────────────────────────── +// Utilise le script embed TradingView dédié (embed-widget-mini-symbol-overview.js) +// et NON pas tv.js (qui n'expose pas MiniSymbolOverview). +// La structure DOM attendue par TradingView : +//
← wrapper +//
+// +//
interface TvMiniChartProps { - symbol: string; // ex: "FX:EURUSD", "TVC:DXY", "SP:SPX" + symbol: string; // ex: "SP:SPX", "TVC:DXY", "FX:EURUSD" label?: string; // titre affiché au-dessus interval?: "W" | "D" | "M"; dateRange?: string; // "1D","5D","1M","3M","6M","12M","60M","ALL","YTD" @@ -21,89 +20,122 @@ interface TvMiniChartProps { showInfo?: boolean; // afficher nom + prix sous le graphique } -// Script TradingView chargé une seule fois -let scriptLoaded = false; -let scriptLoading = false; -const onLoadCallbacks: (() => void)[] = []; +export function TvMiniChart({ + symbol, + label, + dateRange = "1M", + height = 180, + showInfo = true, +}: TvMiniChartProps) { + const wrapperRef = useRef(null); + const initialized = useRef(false); + + useEffect(() => { + if (initialized.current || !wrapperRef.current) return; + initialized.current = true; + + const wrapper = wrapperRef.current; + wrapper.innerHTML = ""; + + // Div cible du widget + const widgetDiv = document.createElement("div"); + widgetDiv.className = "tradingview-widget-container__widget"; + wrapper.appendChild(widgetDiv); + + // Script embed avec config inline (textContent lu par le script au chargement) + const script = document.createElement("script"); + script.type = "text/javascript"; + script.async = true; + script.src = "https://s3.tradingview.com/external-embedding/embed-widget-mini-symbol-overview.js"; + script.text = JSON.stringify({ + symbol, + width: "100%", + height, + locale: "fr", + dateRange, + colorTheme: "dark", + trendLineColor: "#38bdf8", + underLineColor: "rgba(56,189,248,0.08)", + underLineBottomColor: "rgba(56,189,248,0)", + isTransparent: true, + autosize: false, + largeChartUrl: "", + noTimeScale: false, + chartOnly: !showInfo, + }); + wrapper.appendChild(script); + + return () => { + wrapper.innerHTML = ""; + initialized.current = false; + }; + }, []); // eslint-disable-line + + return ( +
+ {label && ( +

+ {label} +

+ )} +
+
+ ); +} + +// ── TvAdvancedChart ─────────────────────────────────────────────────────────── +// Graphique avancé plein format via tv.js (TradingView.widget). +// Note : certains symboles affichent une popup "disponible uniquement sur TradingView". + +declare global { + interface Window { + TradingView?: { + widget: new (config: Record) => void; + }; + } +} + +let tvScriptLoaded = false; +let tvScriptLoading = false; +const tvCallbacks: (() => void)[] = []; function loadTvScript(cb: () => void) { - if (scriptLoaded) { cb(); return; } - onLoadCallbacks.push(cb); - if (scriptLoading) return; - scriptLoading = true; + if (tvScriptLoaded) { cb(); return; } + tvCallbacks.push(cb); + if (tvScriptLoading) return; + tvScriptLoading = true; const s = document.createElement("script"); s.src = "https://s3.tradingview.com/tv.js"; s.async = true; s.onload = () => { - scriptLoaded = true; - onLoadCallbacks.forEach(f => f()); - onLoadCallbacks.length = 0; + tvScriptLoaded = true; + tvCallbacks.forEach(f => f()); + tvCallbacks.length = 0; }; document.head.appendChild(s); } -export function TvMiniChart({ symbol, label, dateRange = "1M", height = 180, showInfo = true }: TvMiniChartProps) { - const uid = useId().replace(/:/g, "_"); - const id = `tv_mini_${uid}`; +interface TvAdvancedChartProps { + symbol: string; + label?: string; + interval?: string; + height?: number; +} + +export function TvAdvancedChart({ + symbol, + label, + interval = "W", + height = 250, +}: TvAdvancedChartProps) { + const uid = useRef(`tv_adv_${Math.random().toString(36).slice(2)}`); const ref = useRef(null); const init = useRef(false); - useEffect(() => { - if (init.current) return; - init.current = true; - - loadTvScript(() => { - if (!window.TradingView || !ref.current) return; - try { - new window.TradingView.MiniSymbolOverview({ - symbol, - container_id: id, - width: "100%", - height, - locale: "fr", - dateRange, - colorTheme: "dark", - trendLineColor: "#38bdf8", - underLineColor: "rgba(56,189,248,0.08)", - underLineBottomColor: "rgba(56,189,248,0)", - isTransparent: true, - autosize: false, - largeChartUrl: "", - noTimeScale: false, - chartOnly: !showInfo, - }); - } catch { /* TradingView indisponible */ } - }); - }, []); // eslint-disable-line - - return ( -
- {label &&

{label}

} -
-
- ); -} - -// ── Vue avancée plein format (pour la page graphiques) ────────────────────── - -interface TvAdvancedChartProps { - symbol: string; - label?: string; - interval?: string; - height?: number; -} - -export function TvAdvancedChart({ symbol, label, interval = "W", height = 250 }: TvAdvancedChartProps) { - const uid = useId().replace(/:/g, "_"); - const id = `tv_adv_${uid}`; - const ref = useRef(null); - const init = useRef(false); - useEffect(() => { if (init.current) return; init.current = true; @@ -112,25 +144,25 @@ export function TvAdvancedChart({ symbol, label, interval = "W", height = 250 }: if (!window.TradingView || !ref.current) return; try { new window.TradingView.widget({ - autosize: false, - width: "100%", + autosize: false, + width: "100%", height, symbol, interval, - timezone: "Europe/Paris", - theme: "dark", - style: "1", - locale: "fr", - toolbar_bg: "#0f1623", + timezone: "Europe/Paris", + theme: "dark", + style: "1", + locale: "fr", + toolbar_bg: "#0f1623", enable_publishing: false, hide_top_toolbar: true, hide_legend: false, save_image: false, - container_id: id, - backgroundColor: "rgba(8,12,20,0)", - gridColor: "rgba(30,45,61,0.5)", - hide_volume: false, - studies: [], + container_id: uid.current, + backgroundColor: "rgba(8,12,20,0)", + gridColor: "rgba(30,45,61,0.5)", + hide_volume: false, + studies: [], }); } catch { /* TradingView indisponible */ } }); @@ -138,10 +170,14 @@ export function TvAdvancedChart({ symbol, label, interval = "W", height = 250 }: return (
- {label &&

{label}

} + {label && ( +

+ {label} +

+ )}