@metamask-previews/advanced-chart-core 0.0.0-preview-e58fe0640 → 0.0.0-preview-238cb7063
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/bootstrap.cjs +185 -0
- package/dist/core/bootstrap.cjs.map +1 -0
- package/dist/core/bootstrap.d.cts +10 -0
- package/dist/core/bootstrap.d.cts.map +1 -0
- package/dist/core/bootstrap.d.mts +10 -0
- package/dist/core/bootstrap.d.mts.map +1 -0
- package/dist/core/bootstrap.mjs +181 -0
- package/dist/core/bootstrap.mjs.map +1 -0
- package/dist/core/bridge.cjs +115 -0
- package/dist/core/bridge.cjs.map +1 -0
- package/dist/core/bridge.d.cts +37 -0
- package/dist/core/bridge.d.cts.map +1 -0
- package/dist/core/bridge.d.mts +37 -0
- package/dist/core/bridge.d.mts.map +1 -0
- package/dist/core/bridge.mjs +108 -0
- package/dist/core/bridge.mjs.map +1 -0
- package/dist/core/dataLifecycle.cjs +52 -0
- package/dist/core/dataLifecycle.cjs.map +1 -0
- package/dist/core/dataLifecycle.d.cts +8 -0
- package/dist/core/dataLifecycle.d.cts.map +1 -0
- package/dist/core/dataLifecycle.d.mts +8 -0
- package/dist/core/dataLifecycle.d.mts.map +1 -0
- package/dist/core/dataLifecycle.mjs +46 -0
- package/dist/core/dataLifecycle.mjs.map +1 -0
- package/dist/core/loadLibrary.cjs +63 -0
- package/dist/core/loadLibrary.cjs.map +1 -0
- package/dist/core/loadLibrary.d.cts +16 -0
- package/dist/core/loadLibrary.d.cts.map +1 -0
- package/dist/core/loadLibrary.d.mts +16 -0
- package/dist/core/loadLibrary.d.mts.map +1 -0
- package/dist/core/loadLibrary.mjs +58 -0
- package/dist/core/loadLibrary.mjs.map +1 -0
- package/dist/core/resolution.cjs +58 -0
- package/dist/core/resolution.cjs.map +1 -0
- package/dist/core/resolution.d.cts +15 -0
- package/dist/core/resolution.d.cts.map +1 -0
- package/dist/core/resolution.d.mts +15 -0
- package/dist/core/resolution.d.mts.map +1 -0
- package/dist/core/resolution.mjs +54 -0
- package/dist/core/resolution.mjs.map +1 -0
- package/dist/core/state.cjs +343 -0
- package/dist/core/state.cjs.map +1 -0
- package/dist/core/state.d.cts +67 -0
- package/dist/core/state.d.cts.map +1 -0
- package/dist/core/state.d.mts +67 -0
- package/dist/core/state.d.mts.map +1 -0
- package/dist/core/state.mjs +283 -0
- package/dist/core/state.mjs.map +1 -0
- package/dist/core/timeUtils.cjs +72 -0
- package/dist/core/timeUtils.cjs.map +1 -0
- package/dist/core/timeUtils.d.cts +32 -0
- package/dist/core/timeUtils.d.cts.map +1 -0
- package/dist/core/timeUtils.d.mts +32 -0
- package/dist/core/timeUtils.d.mts.map +1 -0
- package/dist/core/timeUtils.mjs +66 -0
- package/dist/core/timeUtils.mjs.map +1 -0
- package/dist/core/timezone.cjs +135 -0
- package/dist/core/timezone.cjs.map +1 -0
- package/dist/core/timezone.d.cts +16 -0
- package/dist/core/timezone.d.cts.map +1 -0
- package/dist/core/timezone.d.mts +16 -0
- package/dist/core/timezone.d.mts.map +1 -0
- package/dist/core/timezone.mjs +131 -0
- package/dist/core/timezone.mjs.map +1 -0
- package/dist/core/types.cjs +20 -0
- package/dist/core/types.cjs.map +1 -0
- package/dist/core/types.d.cts +323 -0
- package/dist/core/types.d.cts.map +1 -0
- package/dist/core/types.d.mts +323 -0
- package/dist/core/types.d.mts.map +1 -0
- package/dist/core/types.mjs +17 -0
- package/dist/core/types.mjs.map +1 -0
- package/dist/features/indicators/index.cjs +183 -0
- package/dist/features/indicators/index.cjs.map +1 -0
- package/dist/features/indicators/index.d.cts +6 -0
- package/dist/features/indicators/index.d.cts.map +1 -0
- package/dist/features/indicators/index.d.mts +6 -0
- package/dist/features/indicators/index.d.mts.map +1 -0
- package/dist/features/indicators/index.mjs +177 -0
- package/dist/features/indicators/index.mjs.map +1 -0
- package/dist/features/indicators/legend.cjs +575 -0
- package/dist/features/indicators/legend.cjs.map +1 -0
- package/dist/features/indicators/legend.d.cts +40 -0
- package/dist/features/indicators/legend.d.cts.map +1 -0
- package/dist/features/indicators/legend.d.mts +40 -0
- package/dist/features/indicators/legend.d.mts.map +1 -0
- package/dist/features/indicators/legend.mjs +564 -0
- package/dist/features/indicators/legend.mjs.map +1 -0
- package/dist/features/indicators/resize.cjs +34 -0
- package/dist/features/indicators/resize.cjs.map +1 -0
- package/dist/features/indicators/resize.d.cts +2 -0
- package/dist/features/indicators/resize.d.cts.map +1 -0
- package/dist/features/indicators/resize.d.mts +2 -0
- package/dist/features/indicators/resize.d.mts.map +1 -0
- package/dist/features/indicators/resize.mjs +30 -0
- package/dist/features/indicators/resize.mjs.map +1 -0
- package/dist/features/indicators/studies.cjs +147 -0
- package/dist/features/indicators/studies.cjs.map +1 -0
- package/dist/features/indicators/studies.d.cts +36 -0
- package/dist/features/indicators/studies.d.cts.map +1 -0
- package/dist/features/indicators/studies.d.mts +36 -0
- package/dist/features/indicators/studies.d.mts.map +1 -0
- package/dist/features/indicators/studies.mjs +140 -0
- package/dist/features/indicators/studies.mjs.map +1 -0
- package/dist/features/indicators/subPane.cjs +78 -0
- package/dist/features/indicators/subPane.cjs.map +1 -0
- package/dist/features/indicators/subPane.d.cts +6 -0
- package/dist/features/indicators/subPane.d.cts.map +1 -0
- package/dist/features/indicators/subPane.d.mts +6 -0
- package/dist/features/indicators/subPane.d.mts.map +1 -0
- package/dist/features/indicators/subPane.mjs +72 -0
- package/dist/features/indicators/subPane.mjs.map +1 -0
- package/dist/features/volume/index.cjs +145 -0
- package/dist/features/volume/index.cjs.map +1 -0
- package/dist/features/volume/index.d.cts +4 -0
- package/dist/features/volume/index.d.cts.map +1 -0
- package/dist/features/volume/index.d.mts +4 -0
- package/dist/features/volume/index.d.mts.map +1 -0
- package/dist/features/volume/index.mjs +140 -0
- package/dist/features/volume/index.mjs.map +1 -0
- package/dist/index.cjs +20 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +18 -0
- package/dist/index.mjs.map +1 -0
- package/dist/interaction/crosshair.cjs +147 -0
- package/dist/interaction/crosshair.cjs.map +1 -0
- package/dist/interaction/crosshair.d.cts +21 -0
- package/dist/interaction/crosshair.d.cts.map +1 -0
- package/dist/interaction/crosshair.d.mts +21 -0
- package/dist/interaction/crosshair.d.mts.map +1 -0
- package/dist/interaction/crosshair.mjs +141 -0
- package/dist/interaction/crosshair.mjs.map +1 -0
- package/dist/interaction/visibleRange.cjs +102 -0
- package/dist/interaction/visibleRange.cjs.map +1 -0
- package/dist/interaction/visibleRange.d.cts +11 -0
- package/dist/interaction/visibleRange.d.cts.map +1 -0
- package/dist/interaction/visibleRange.d.mts +11 -0
- package/dist/interaction/visibleRange.d.mts.map +1 -0
- package/dist/interaction/visibleRange.mjs +97 -0
- package/dist/interaction/visibleRange.mjs.map +1 -0
- package/dist/messages/contract.cjs +27 -0
- package/dist/messages/contract.cjs.map +1 -0
- package/dist/messages/contract.d.cts +262 -0
- package/dist/messages/contract.d.cts.map +1 -0
- package/dist/messages/contract.d.mts +262 -0
- package/dist/messages/contract.d.mts.map +1 -0
- package/dist/messages/contract.mjs +26 -0
- package/dist/messages/contract.mjs.map +1 -0
- package/dist/messages/handler.cjs +54 -0
- package/dist/messages/handler.cjs.map +1 -0
- package/dist/messages/handler.d.cts +25 -0
- package/dist/messages/handler.d.cts.map +1 -0
- package/dist/messages/handler.d.mts +25 -0
- package/dist/messages/handler.d.mts.map +1 -0
- package/dist/messages/handler.mjs +48 -0
- package/dist/messages/handler.mjs.map +1 -0
- package/dist/overlays/focusTime/index.cjs +161 -0
- package/dist/overlays/focusTime/index.cjs.map +1 -0
- package/dist/overlays/focusTime/index.d.cts +6 -0
- package/dist/overlays/focusTime/index.d.cts.map +1 -0
- package/dist/overlays/focusTime/index.d.mts +6 -0
- package/dist/overlays/focusTime/index.d.mts.map +1 -0
- package/dist/overlays/focusTime/index.mjs +155 -0
- package/dist/overlays/focusTime/index.mjs.map +1 -0
- package/dist/overlays/positionLines/index.cjs +188 -0
- package/dist/overlays/positionLines/index.cjs.map +1 -0
- package/dist/overlays/positionLines/index.d.cts +4 -0
- package/dist/overlays/positionLines/index.d.cts.map +1 -0
- package/dist/overlays/positionLines/index.d.mts +4 -0
- package/dist/overlays/positionLines/index.d.mts.map +1 -0
- package/dist/overlays/positionLines/index.mjs +183 -0
- package/dist/overlays/positionLines/index.mjs.map +1 -0
- package/dist/overlays/positionLines/state.cjs +35 -0
- package/dist/overlays/positionLines/state.cjs.map +1 -0
- package/dist/overlays/positionLines/state.d.cts +8 -0
- package/dist/overlays/positionLines/state.d.cts.map +1 -0
- package/dist/overlays/positionLines/state.d.mts +8 -0
- package/dist/overlays/positionLines/state.d.mts.map +1 -0
- package/dist/overlays/positionLines/state.mjs +26 -0
- package/dist/overlays/positionLines/state.mjs.map +1 -0
- package/dist/overlays/socialLeaderboard/index.cjs +229 -0
- package/dist/overlays/socialLeaderboard/index.cjs.map +1 -0
- package/dist/overlays/socialLeaderboard/index.d.cts +48 -0
- package/dist/overlays/socialLeaderboard/index.d.cts.map +1 -0
- package/dist/overlays/socialLeaderboard/index.d.mts +48 -0
- package/dist/overlays/socialLeaderboard/index.d.mts.map +1 -0
- package/dist/overlays/socialLeaderboard/index.mjs +222 -0
- package/dist/overlays/socialLeaderboard/index.mjs.map +1 -0
- package/dist/overlays/tradeMarkers/animation.cjs +123 -0
- package/dist/overlays/tradeMarkers/animation.cjs.map +1 -0
- package/dist/overlays/tradeMarkers/animation.d.cts +5 -0
- package/dist/overlays/tradeMarkers/animation.d.cts.map +1 -0
- package/dist/overlays/tradeMarkers/animation.d.mts +5 -0
- package/dist/overlays/tradeMarkers/animation.d.mts.map +1 -0
- package/dist/overlays/tradeMarkers/animation.mjs +118 -0
- package/dist/overlays/tradeMarkers/animation.mjs.map +1 -0
- package/dist/overlays/tradeMarkers/index.cjs +334 -0
- package/dist/overlays/tradeMarkers/index.cjs.map +1 -0
- package/dist/overlays/tradeMarkers/index.d.cts +35 -0
- package/dist/overlays/tradeMarkers/index.d.cts.map +1 -0
- package/dist/overlays/tradeMarkers/index.d.mts +35 -0
- package/dist/overlays/tradeMarkers/index.d.mts.map +1 -0
- package/dist/overlays/tradeMarkers/index.mjs +324 -0
- package/dist/overlays/tradeMarkers/index.mjs.map +1 -0
- package/dist/overlays/tradeMarkers/markerHitTest.cjs +337 -0
- package/dist/overlays/tradeMarkers/markerHitTest.cjs.map +1 -0
- package/dist/overlays/tradeMarkers/markerHitTest.d.cts +15 -0
- package/dist/overlays/tradeMarkers/markerHitTest.d.cts.map +1 -0
- package/dist/overlays/tradeMarkers/markerHitTest.d.mts +15 -0
- package/dist/overlays/tradeMarkers/markerHitTest.d.mts.map +1 -0
- package/dist/overlays/tradeMarkers/markerHitTest.mjs +331 -0
- package/dist/overlays/tradeMarkers/markerHitTest.mjs.map +1 -0
- package/dist/overlays/tradeMarkers/state.cjs +75 -0
- package/dist/overlays/tradeMarkers/state.cjs.map +1 -0
- package/dist/overlays/tradeMarkers/state.d.cts +20 -0
- package/dist/overlays/tradeMarkers/state.d.cts.map +1 -0
- package/dist/overlays/tradeMarkers/state.d.mts +20 -0
- package/dist/overlays/tradeMarkers/state.d.mts.map +1 -0
- package/dist/overlays/tradeMarkers/state.mjs +60 -0
- package/dist/overlays/tradeMarkers/state.mjs.map +1 -0
- package/dist/pagination/priceApi.cjs +102 -0
- package/dist/pagination/priceApi.cjs.map +1 -0
- package/dist/pagination/priceApi.d.cts +43 -0
- package/dist/pagination/priceApi.d.cts.map +1 -0
- package/dist/pagination/priceApi.d.mts +43 -0
- package/dist/pagination/priceApi.d.mts.map +1 -0
- package/dist/pagination/priceApi.mjs +97 -0
- package/dist/pagination/priceApi.mjs.map +1 -0
- package/dist/pagination/rnBacked.cjs +108 -0
- package/dist/pagination/rnBacked.cjs.map +1 -0
- package/dist/pagination/rnBacked.d.cts +15 -0
- package/dist/pagination/rnBacked.d.cts.map +1 -0
- package/dist/pagination/rnBacked.d.mts +15 -0
- package/dist/pagination/rnBacked.d.mts.map +1 -0
- package/dist/pagination/rnBacked.mjs +100 -0
- package/dist/pagination/rnBacked.mjs.map +1 -0
- package/dist/widget/chartType.cjs +39 -0
- package/dist/widget/chartType.cjs.map +1 -0
- package/dist/widget/chartType.d.cts +4 -0
- package/dist/widget/chartType.d.cts.map +1 -0
- package/dist/widget/chartType.d.mts +4 -0
- package/dist/widget/chartType.d.mts.map +1 -0
- package/dist/widget/chartType.mjs +34 -0
- package/dist/widget/chartType.mjs.map +1 -0
- package/dist/widget/datafeed.cjs +228 -0
- package/dist/widget/datafeed.cjs.map +1 -0
- package/dist/widget/datafeed.d.cts +22 -0
- package/dist/widget/datafeed.d.cts.map +1 -0
- package/dist/widget/datafeed.d.mts +22 -0
- package/dist/widget/datafeed.d.mts.map +1 -0
- package/dist/widget/datafeed.mjs +223 -0
- package/dist/widget/datafeed.mjs.map +1 -0
- package/dist/widget/externalLinkBridge.cjs +134 -0
- package/dist/widget/externalLinkBridge.cjs.map +1 -0
- package/dist/widget/externalLinkBridge.d.cts +14 -0
- package/dist/widget/externalLinkBridge.d.cts.map +1 -0
- package/dist/widget/externalLinkBridge.d.mts +14 -0
- package/dist/widget/externalLinkBridge.d.mts.map +1 -0
- package/dist/widget/externalLinkBridge.mjs +127 -0
- package/dist/widget/externalLinkBridge.mjs.map +1 -0
- package/dist/widget/initChart.cjs +242 -0
- package/dist/widget/initChart.cjs.map +1 -0
- package/dist/widget/initChart.d.cts +53 -0
- package/dist/widget/initChart.d.cts.map +1 -0
- package/dist/widget/initChart.d.mts +53 -0
- package/dist/widget/initChart.d.mts.map +1 -0
- package/dist/widget/initChart.mjs +235 -0
- package/dist/widget/initChart.mjs.map +1 -0
- package/dist/widget/ohlcvIngestion.cjs +240 -0
- package/dist/widget/ohlcvIngestion.cjs.map +1 -0
- package/dist/widget/ohlcvIngestion.d.cts +16 -0
- package/dist/widget/ohlcvIngestion.d.cts.map +1 -0
- package/dist/widget/ohlcvIngestion.d.mts +16 -0
- package/dist/widget/ohlcvIngestion.d.mts.map +1 -0
- package/dist/widget/ohlcvIngestion.mjs +233 -0
- package/dist/widget/ohlcvIngestion.mjs.map +1 -0
- package/dist/widget/priceFormatter.cjs +142 -0
- package/dist/widget/priceFormatter.cjs.map +1 -0
- package/dist/widget/priceFormatter.d.cts +38 -0
- package/dist/widget/priceFormatter.d.cts.map +1 -0
- package/dist/widget/priceFormatter.d.mts +38 -0
- package/dist/widget/priceFormatter.d.mts.map +1 -0
- package/dist/widget/priceFormatter.mjs +134 -0
- package/dist/widget/priceFormatter.mjs.map +1 -0
- package/dist/widget/scaleLayout.cjs +89 -0
- package/dist/widget/scaleLayout.cjs.map +1 -0
- package/dist/widget/scaleLayout.d.cts +9 -0
- package/dist/widget/scaleLayout.d.cts.map +1 -0
- package/dist/widget/scaleLayout.d.mts +9 -0
- package/dist/widget/scaleLayout.d.mts.map +1 -0
- package/dist/widget/scaleLayout.mjs +85 -0
- package/dist/widget/scaleLayout.mjs.map +1 -0
- package/dist/widget/theme.cjs +286 -0
- package/dist/widget/theme.cjs.map +1 -0
- package/dist/widget/theme.d.cts +99 -0
- package/dist/widget/theme.d.cts.map +1 -0
- package/dist/widget/theme.d.mts +99 -0
- package/dist/widget/theme.d.mts.map +1 -0
- package/dist/widget/theme.mjs +270 -0
- package/dist/widget/theme.mjs.map +1 -0
- package/dist/widget/tvDomHelpers.cjs +65 -0
- package/dist/widget/tvDomHelpers.cjs.map +1 -0
- package/dist/widget/tvDomHelpers.d.cts +19 -0
- package/dist/widget/tvDomHelpers.d.cts.map +1 -0
- package/dist/widget/tvDomHelpers.d.mts +19 -0
- package/dist/widget/tvDomHelpers.d.mts.map +1 -0
- package/dist/widget/tvDomHelpers.mjs +60 -0
- package/dist/widget/tvDomHelpers.mjs.map +1 -0
- package/dist/widget/visualOverrides.cjs +63 -0
- package/dist/widget/visualOverrides.cjs.map +1 -0
- package/dist/widget/visualOverrides.d.cts +27 -0
- package/dist/widget/visualOverrides.d.cts.map +1 -0
- package/dist/widget/visualOverrides.d.mts +27 -0
- package/dist/widget/visualOverrides.d.mts.map +1 -0
- package/dist/widget/visualOverrides.mjs +58 -0
- package/dist/widget/visualOverrides.mjs.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// DOM legend overlay for active indicator studies.
|
|
3
|
+
//
|
|
4
|
+
// Ported from chartLogic.js: createStudyLegendOverlay (~4365),
|
|
5
|
+
// refreshStudyLegendFromExport (~4644), buildLegendHTML (~4497),
|
|
6
|
+
// updateLegendOverlayLayout (~4401), legend retry/timeout machinery
|
|
7
|
+
// (~4630-4785), getMainPriceAxisLeftRelativeTo (~1472).
|
|
8
|
+
//
|
|
9
|
+
// The overlay is a `<div id="study-legend-overlay">` injected into
|
|
10
|
+
// #tv_chart_container that holds one `.legend-pill` per active indicator.
|
|
11
|
+
// Theme-aware text colors are computed from CONFIG.theme; per-plot colors
|
|
12
|
+
// come from the legend config supplied by RN.
|
|
13
|
+
//
|
|
14
|
+
// `LEGEND_RENDERED` is posted to RN once the overlay has settled (either
|
|
15
|
+
// real values returned by chart.exportData() or after the retry timeout).
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports._resetLegendForTests = exports.updateLegendOverlayLayout = exports.removeSubPaneOverlay = exports.subscribeStudyDataLoaded = exports.scheduleLegendRefresh = exports.refreshStudyLegendFromExport = exports.attachLegendResizeListener = exports.setupLegendOverlay = void 0;
|
|
18
|
+
const bridge_js_1 = require("../../core/bridge.cjs");
|
|
19
|
+
const state_js_1 = require("../../core/state.cjs");
|
|
20
|
+
const tvDomHelpers_js_1 = require("../../widget/tvDomHelpers.cjs");
|
|
21
|
+
const OVERLAY_ID = 'study-legend-overlay';
|
|
22
|
+
const OVERLAY_LEFT_PX = 8;
|
|
23
|
+
const MAX_RETRIES = 10;
|
|
24
|
+
const RETRY_DELAY_MS = 100;
|
|
25
|
+
const RENDER_TIMEOUT_MS = 3000;
|
|
26
|
+
let exportGeneration = 0;
|
|
27
|
+
let retryCount = 0;
|
|
28
|
+
let timeoutId = null;
|
|
29
|
+
let legendOverlayEnabled = false;
|
|
30
|
+
/** Typed legend config from RN — the single source of truth for legend rendering. */
|
|
31
|
+
let legendConfig;
|
|
32
|
+
/** Sub-pane overlay elements keyed by indicator name. */
|
|
33
|
+
const subPaneOverlays = new Map();
|
|
34
|
+
// ----- Lifecycle ---------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Called once on chart-ready to set up the DOM container.
|
|
37
|
+
*
|
|
38
|
+
* @param config - The legend overlay configuration, or undefined.
|
|
39
|
+
*/
|
|
40
|
+
function setupLegendOverlay(config) {
|
|
41
|
+
legendOverlayEnabled = Boolean(config?.enabled);
|
|
42
|
+
legendConfig = config?.config;
|
|
43
|
+
if (!legendOverlayEnabled) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
createOverlayElement();
|
|
47
|
+
injectHideLegendButtonsCSS();
|
|
48
|
+
}
|
|
49
|
+
exports.setupLegendOverlay = setupLegendOverlay;
|
|
50
|
+
/**
|
|
51
|
+
* Subscribes to the widget's `panes_height_changed` event so the overlay
|
|
52
|
+
* max-width is recomputed whenever a pane resize (e.g. after adding MACD
|
|
53
|
+
* or RSI) shifts the price-axis boundary.
|
|
54
|
+
*
|
|
55
|
+
* @param widget - The TradingView widget (subset used here).
|
|
56
|
+
* @param widget.subscribe - Subscribes to the `panes_height_changed` event.
|
|
57
|
+
* @param widget.activeChart - Returns the active chart.
|
|
58
|
+
*/
|
|
59
|
+
function attachLegendResizeListener(widget) {
|
|
60
|
+
try {
|
|
61
|
+
widget.subscribe('panes_height_changed', () => {
|
|
62
|
+
const el = document.getElementById(OVERLAY_ID);
|
|
63
|
+
if (el) {
|
|
64
|
+
updateLegendOverlayLayout();
|
|
65
|
+
}
|
|
66
|
+
repositionSubPaneOverlays(widget.activeChart());
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// TV may throw if subscribe isn't ready; safe to ignore.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.attachLegendResizeListener = attachLegendResizeListener;
|
|
74
|
+
function createOverlayElement() {
|
|
75
|
+
const existing = document.getElementById(OVERLAY_ID);
|
|
76
|
+
if (existing) {
|
|
77
|
+
existing.remove();
|
|
78
|
+
}
|
|
79
|
+
const container = document.getElementById('tv_chart_container');
|
|
80
|
+
if (!container) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const div = document.createElement('div');
|
|
84
|
+
div.id = OVERLAY_ID;
|
|
85
|
+
div.style.cssText =
|
|
86
|
+
`position:absolute;top:1px;left:${OVERLAY_LEFT_PX}px;z-index:5;` +
|
|
87
|
+
`pointer-events:none;display:flex;flex-wrap:wrap;align-items:flex-start;` +
|
|
88
|
+
`column-gap:8px;row-gap:2px;`;
|
|
89
|
+
container.style.position = 'relative';
|
|
90
|
+
container.appendChild(div);
|
|
91
|
+
}
|
|
92
|
+
function injectHideLegendButtonsCSS() {
|
|
93
|
+
const styleId = 'mm-hide-legend-buttons';
|
|
94
|
+
if (document.getElementById(styleId)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let targetDoc = document;
|
|
98
|
+
(0, tvDomHelpers_js_1.eachChartDocument)((doc) => {
|
|
99
|
+
if (targetDoc === document && doc !== document) {
|
|
100
|
+
targetDoc = doc;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const style = targetDoc.createElement('style');
|
|
104
|
+
style.id = styleId;
|
|
105
|
+
style.textContent =
|
|
106
|
+
'.chart-controls-bar .apply-common-tooltip,' +
|
|
107
|
+
'.legendElement .showHide,' +
|
|
108
|
+
'.legendElement button[data-name="legend-show-hide-action"],' +
|
|
109
|
+
'.legendElement button[data-name="legend-settings-action"],' +
|
|
110
|
+
'.legendElement button[data-name="legend-delete-action"],' +
|
|
111
|
+
'.legendElement .buttons-wrapper,' +
|
|
112
|
+
'.legendElement .buttonsWrapper{display:none!important;}';
|
|
113
|
+
targetDoc.head.appendChild(style);
|
|
114
|
+
}
|
|
115
|
+
// ----- Legend rebuild ---------------------------------------------------
|
|
116
|
+
/**
|
|
117
|
+
* Returns the per-indicator legend config supplied by RN via legendOverlay.config.
|
|
118
|
+
* Consumers must pass their own config — there is no built-in fallback.
|
|
119
|
+
*
|
|
120
|
+
* @returns The per-indicator legend config map (empty when unset).
|
|
121
|
+
*/
|
|
122
|
+
function getPresetMap() {
|
|
123
|
+
return legendConfig ?? {};
|
|
124
|
+
}
|
|
125
|
+
function getLegendAltColor() {
|
|
126
|
+
const theme = (0, state_js_1.getTheme)();
|
|
127
|
+
return (theme?.legendTextColor ??
|
|
128
|
+
theme?.textAlternativeColor ??
|
|
129
|
+
theme?.textColor ??
|
|
130
|
+
'rgb(133,136,152)');
|
|
131
|
+
}
|
|
132
|
+
function isEmptyValue(value) {
|
|
133
|
+
return !value || value === '' || value === 'n/a' || value === '∅';
|
|
134
|
+
}
|
|
135
|
+
function plotValue(indicatorConfig, plotCfg, plotIndex, values) {
|
|
136
|
+
if (indicatorConfig.useIndex && plotIndex < values.length) {
|
|
137
|
+
return values[plotIndex].value;
|
|
138
|
+
}
|
|
139
|
+
const match = values.find((candidate) => candidate.title === plotCfg.tvTitle);
|
|
140
|
+
return match?.value ?? '';
|
|
141
|
+
}
|
|
142
|
+
function wrapPill(innerHtml, color) {
|
|
143
|
+
const style = color ? ` style="color:${color};"` : '';
|
|
144
|
+
return `<span class="legend-pill"${style}>${innerHtml}</span>`;
|
|
145
|
+
}
|
|
146
|
+
function buildHTML(entries) {
|
|
147
|
+
const altColor = getLegendAltColor();
|
|
148
|
+
const presets = getPresetMap();
|
|
149
|
+
const successColor = (0, state_js_1.getTheme)()?.successColor ?? 'rgb(38,166,154)';
|
|
150
|
+
const pills = [];
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
const indicatorConfig = presets[entry.name];
|
|
153
|
+
if (!indicatorConfig) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (indicatorConfig.isMA) {
|
|
157
|
+
const ma = indicatorConfig.plots[0];
|
|
158
|
+
const value = plotValue(indicatorConfig, ma, 0, entry.values);
|
|
159
|
+
if (isEmptyValue(value)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
pills.push(wrapPill(`${ma.label} ${value}`, ma.color ?? undefined));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (indicatorConfig.combineInOnePill) {
|
|
166
|
+
const labelColor = indicatorConfig.plots[0].color ?? successColor;
|
|
167
|
+
let inner = `<span style="color:${labelColor}">${indicatorConfig.title ?? indicatorConfig.plots[0].label}</span>`;
|
|
168
|
+
let hasValues = false;
|
|
169
|
+
indicatorConfig.plots.forEach((plot, idx) => {
|
|
170
|
+
const value = plotValue(indicatorConfig, plot, idx, entry.values);
|
|
171
|
+
if (isEmptyValue(value)) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
hasValues = true;
|
|
175
|
+
inner +=
|
|
176
|
+
`<span style="color:${labelColor}"> ${plot.label}</span>` +
|
|
177
|
+
`<span style="color:${altColor}"> ${value}</span>`;
|
|
178
|
+
});
|
|
179
|
+
if (hasValues) {
|
|
180
|
+
pills.push(wrapPill(inner));
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
indicatorConfig.plots.forEach((plot, idx) => {
|
|
185
|
+
const value = plotValue(indicatorConfig, plot, idx, entry.values);
|
|
186
|
+
if (isEmptyValue(value)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const color = plot.color ?? successColor;
|
|
190
|
+
const inner = `<span style="color:${color}">${plot.label}</span>` +
|
|
191
|
+
`<span style="color:${altColor}"> ${value}</span>`;
|
|
192
|
+
pills.push(wrapPill(inner));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return pills.join('');
|
|
196
|
+
}
|
|
197
|
+
// ----- Refresh from chart.exportData() ----------------------------------
|
|
198
|
+
function collectStudyIdMap() {
|
|
199
|
+
const map = {};
|
|
200
|
+
for (const [name, id] of (0, state_js_1.getActiveStudies)().entries()) {
|
|
201
|
+
map[String(id)] = name;
|
|
202
|
+
}
|
|
203
|
+
for (const [name, id] of (0, state_js_1.getMaStudies)().entries()) {
|
|
204
|
+
map[String(id)] = name;
|
|
205
|
+
}
|
|
206
|
+
const vol = (0, state_js_1.getVolumeStudyId)();
|
|
207
|
+
if (vol) {
|
|
208
|
+
map[String(vol)] = 'Volume';
|
|
209
|
+
}
|
|
210
|
+
return map;
|
|
211
|
+
}
|
|
212
|
+
function buildOrderedEntries(byStudy) {
|
|
213
|
+
const result = [];
|
|
214
|
+
for (const [name, studyId] of (0, state_js_1.getLegendStudyOrder)().entries()) {
|
|
215
|
+
const sid = String(studyId);
|
|
216
|
+
const values = byStudy[sid];
|
|
217
|
+
if (values) {
|
|
218
|
+
result.push({ name, values });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
function formatLegendValue(value) {
|
|
224
|
+
if (!Number.isFinite(value)) {
|
|
225
|
+
return '';
|
|
226
|
+
}
|
|
227
|
+
const abs = Math.abs(value);
|
|
228
|
+
if (abs >= 1e9) {
|
|
229
|
+
return `${(value / 1e9).toFixed(2)}B`;
|
|
230
|
+
}
|
|
231
|
+
if (abs >= 1e6) {
|
|
232
|
+
return `${(value / 1e6).toFixed(2)}M`;
|
|
233
|
+
}
|
|
234
|
+
if (abs >= 1e4) {
|
|
235
|
+
return `${(value / 1e3).toFixed(1)}K`;
|
|
236
|
+
}
|
|
237
|
+
if (abs >= 1000) {
|
|
238
|
+
return value.toFixed(2);
|
|
239
|
+
}
|
|
240
|
+
if (abs >= 1) {
|
|
241
|
+
return value.toFixed(2);
|
|
242
|
+
}
|
|
243
|
+
if (abs >= 0.01) {
|
|
244
|
+
return value.toFixed(4);
|
|
245
|
+
}
|
|
246
|
+
return value.toPrecision(4);
|
|
247
|
+
}
|
|
248
|
+
function hasAnyEmpty(entries) {
|
|
249
|
+
for (const entry of entries) {
|
|
250
|
+
for (const value of entry.values) {
|
|
251
|
+
if (isEmptyValue(value.value)) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
function notifyLegendRendered() {
|
|
259
|
+
requestAnimationFrame(() => {
|
|
260
|
+
requestAnimationFrame(() => {
|
|
261
|
+
(0, bridge_js_1.postToRN)('LEGEND_RENDERED', {});
|
|
262
|
+
if ((0, state_js_1.doesLegendOwnLayoutSettle)()) {
|
|
263
|
+
(0, state_js_1.setLegendOwnsLayoutSettle)(false);
|
|
264
|
+
(0, bridge_js_1.postToRN)('CHART_LAYOUT_SETTLED', {});
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function clearTimer() {
|
|
270
|
+
if (timeoutId !== null) {
|
|
271
|
+
clearTimeout(timeoutId);
|
|
272
|
+
timeoutId = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function startTimeout(gen) {
|
|
276
|
+
clearTimer();
|
|
277
|
+
timeoutId = setTimeout(() => {
|
|
278
|
+
if (gen !== exportGeneration) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
retryCount = 0;
|
|
282
|
+
clearTimer();
|
|
283
|
+
notifyLegendRendered();
|
|
284
|
+
}, RENDER_TIMEOUT_MS);
|
|
285
|
+
}
|
|
286
|
+
function scheduleRetry(gen) {
|
|
287
|
+
if (retryCount >= MAX_RETRIES) {
|
|
288
|
+
retryCount = 0;
|
|
289
|
+
clearTimer();
|
|
290
|
+
notifyLegendRendered();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
retryCount += 1;
|
|
294
|
+
setTimeout(() => {
|
|
295
|
+
if (gen === exportGeneration) {
|
|
296
|
+
refreshStudyLegendFromExport();
|
|
297
|
+
}
|
|
298
|
+
}, RETRY_DELAY_MS);
|
|
299
|
+
}
|
|
300
|
+
function renderOverlay(entries) {
|
|
301
|
+
const presets = getPresetMap();
|
|
302
|
+
const widget = (0, state_js_1.getWidget)();
|
|
303
|
+
const chart = widget?.activeChart();
|
|
304
|
+
const activeStudies = (0, state_js_1.getActiveStudies)();
|
|
305
|
+
const mainEntries = [];
|
|
306
|
+
const subPaneEntries = [];
|
|
307
|
+
for (const entry of entries) {
|
|
308
|
+
const preset = presets[entry.name];
|
|
309
|
+
if (preset?.subPaneLegend && chart) {
|
|
310
|
+
const studyId = activeStudies.get(entry.name);
|
|
311
|
+
const study = studyId ? chart.getStudyById(studyId) : null;
|
|
312
|
+
const paneIdx = study?.paneIndex?.();
|
|
313
|
+
if (paneIdx !== undefined && paneIdx > 0) {
|
|
314
|
+
subPaneEntries.push({ name: entry.name, paneIdx, entry });
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
mainEntries.push(entry);
|
|
319
|
+
}
|
|
320
|
+
const mainOverlay = document.getElementById(OVERLAY_ID);
|
|
321
|
+
if (mainOverlay) {
|
|
322
|
+
mainOverlay.innerHTML = buildHTML(mainEntries);
|
|
323
|
+
}
|
|
324
|
+
const activeNames = new Set(subPaneEntries.map((subPane) => subPane.name));
|
|
325
|
+
for (const name of subPaneOverlays.keys()) {
|
|
326
|
+
if (!activeNames.has(name)) {
|
|
327
|
+
removeSubPaneOverlay(name);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
for (const { name, paneIdx, entry } of subPaneEntries) {
|
|
331
|
+
const overlay = ensureSubPaneOverlay(name, paneIdx, chart ?? undefined);
|
|
332
|
+
if (overlay) {
|
|
333
|
+
overlay.innerHTML = buildHTML([entry]);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
updateLegendOverlayLayout();
|
|
337
|
+
if (chart) {
|
|
338
|
+
repositionSubPaneOverlays(chart);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function refreshStudyLegendFromExport() {
|
|
342
|
+
if (!legendOverlayEnabled) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const widget = (0, state_js_1.getWidget)();
|
|
346
|
+
if (!widget || !(0, state_js_1.isChartReady)()) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const overlay = document.getElementById(OVERLAY_ID);
|
|
350
|
+
if (!overlay) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const studyIdMap = collectStudyIdMap();
|
|
354
|
+
const studyIds = Object.keys(studyIdMap);
|
|
355
|
+
if (studyIds.length === 0) {
|
|
356
|
+
overlay.innerHTML = '';
|
|
357
|
+
removeAllSubPaneOverlays();
|
|
358
|
+
retryCount = 0;
|
|
359
|
+
clearTimer();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
exportGeneration += 1;
|
|
363
|
+
const gen = exportGeneration;
|
|
364
|
+
if (retryCount === 0) {
|
|
365
|
+
startTimeout(gen);
|
|
366
|
+
}
|
|
367
|
+
const chart = widget.activeChart();
|
|
368
|
+
chart
|
|
369
|
+
.exportData({
|
|
370
|
+
includeSeries: false,
|
|
371
|
+
includedStudies: studyIds,
|
|
372
|
+
})
|
|
373
|
+
.then((data) => {
|
|
374
|
+
if (gen === exportGeneration) {
|
|
375
|
+
handleExportData(data, gen);
|
|
376
|
+
}
|
|
377
|
+
return undefined;
|
|
378
|
+
})
|
|
379
|
+
.catch(() => scheduleRetry(gen));
|
|
380
|
+
}
|
|
381
|
+
exports.refreshStudyLegendFromExport = refreshStudyLegendFromExport;
|
|
382
|
+
function isValidExportData(data) {
|
|
383
|
+
return Boolean(data?.schema && data.data && data.data.length > 0);
|
|
384
|
+
}
|
|
385
|
+
function resolveDisplayValue(rawVal, colIndex, displayedData) {
|
|
386
|
+
let displayVal = rawVal !== undefined && !Number.isNaN(rawVal)
|
|
387
|
+
? formatLegendValue(rawVal)
|
|
388
|
+
: '';
|
|
389
|
+
if (displayedData && displayedData.length > 0) {
|
|
390
|
+
const dispRow = displayedData.at(-1);
|
|
391
|
+
if (dispRow?.[colIndex]) {
|
|
392
|
+
displayVal = dispRow[colIndex];
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return displayVal;
|
|
396
|
+
}
|
|
397
|
+
function buildStudyMap(data, lastRow) {
|
|
398
|
+
const byStudy = {};
|
|
399
|
+
for (let index = 0; index < data.schema.length; index++) {
|
|
400
|
+
const field = data.schema[index];
|
|
401
|
+
if (field.type === 'time' || field.type === 'userTime') {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const sid = field.sourceId ? String(field.sourceId) : '';
|
|
405
|
+
if (!sid) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (!byStudy[sid]) {
|
|
409
|
+
byStudy[sid] = [];
|
|
410
|
+
}
|
|
411
|
+
const displayVal = resolveDisplayValue(lastRow[index], index, data.displayedData);
|
|
412
|
+
byStudy[sid].push({ title: field.plotTitle ?? '', value: displayVal });
|
|
413
|
+
}
|
|
414
|
+
return byStudy;
|
|
415
|
+
}
|
|
416
|
+
function handleExportData(data, gen) {
|
|
417
|
+
if (!isValidExportData(data)) {
|
|
418
|
+
scheduleRetry(gen);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const lastRow = data.data.at(-1);
|
|
422
|
+
if (!lastRow) {
|
|
423
|
+
scheduleRetry(gen);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const byStudy = buildStudyMap(data, lastRow);
|
|
427
|
+
const entries = buildOrderedEntries(byStudy);
|
|
428
|
+
if (hasAnyEmpty(entries) && retryCount < MAX_RETRIES) {
|
|
429
|
+
scheduleRetry(gen);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
retryCount = 0;
|
|
433
|
+
renderOverlay(entries);
|
|
434
|
+
clearTimer();
|
|
435
|
+
notifyLegendRendered();
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Used by indicator handlers to request a legend rebuild after a study has
|
|
439
|
+
* been added/removed. Two rAFs to wait for TV's internal layout pass.
|
|
440
|
+
*/
|
|
441
|
+
function scheduleLegendRefresh() {
|
|
442
|
+
requestAnimationFrame(() => {
|
|
443
|
+
requestAnimationFrame(() => refreshStudyLegendFromExport());
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
exports.scheduleLegendRefresh = scheduleLegendRefresh;
|
|
447
|
+
/**
|
|
448
|
+
* Subscribes to a study's onDataLoaded event so the legend refreshes once
|
|
449
|
+
* the study's calculation finishes. Falls back to immediate refresh when
|
|
450
|
+
* the subscription API isn't available.
|
|
451
|
+
*
|
|
452
|
+
* @param chart - The active TradingView chart.
|
|
453
|
+
* @param studyId - The id of the study to subscribe to.
|
|
454
|
+
*/
|
|
455
|
+
function subscribeStudyDataLoaded(chart, studyId) {
|
|
456
|
+
try {
|
|
457
|
+
const study = chart.getStudyById(studyId);
|
|
458
|
+
if (study?.onDataLoaded) {
|
|
459
|
+
study.onDataLoaded().subscribe(null, () => {
|
|
460
|
+
scheduleLegendRefresh();
|
|
461
|
+
});
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch {
|
|
466
|
+
// Fallthrough to direct refresh.
|
|
467
|
+
}
|
|
468
|
+
scheduleLegendRefresh();
|
|
469
|
+
}
|
|
470
|
+
exports.subscribeStudyDataLoaded = subscribeStudyDataLoaded;
|
|
471
|
+
// ----- Sub-pane overlay management ----------------------------------------
|
|
472
|
+
function subPaneOverlayId(name) {
|
|
473
|
+
return `${OVERLAY_ID}-pane-${name}`;
|
|
474
|
+
}
|
|
475
|
+
function getSubPaneTopPx(paneIndex, chart) {
|
|
476
|
+
const heights = chart.getAllPanesHeight();
|
|
477
|
+
let top = 0;
|
|
478
|
+
for (let i = 0; i < paneIndex && i < heights.length; i++) {
|
|
479
|
+
top += heights[i];
|
|
480
|
+
}
|
|
481
|
+
return top + 4;
|
|
482
|
+
}
|
|
483
|
+
function ensureSubPaneOverlay(name, paneIndex, chart) {
|
|
484
|
+
const existing = subPaneOverlays.get(name);
|
|
485
|
+
if (existing && document.contains(existing)) {
|
|
486
|
+
return existing;
|
|
487
|
+
}
|
|
488
|
+
const container = document.getElementById('tv_chart_container');
|
|
489
|
+
if (!container) {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
const div = document.createElement('div');
|
|
493
|
+
div.id = subPaneOverlayId(name);
|
|
494
|
+
const topPx = chart ? getSubPaneTopPx(paneIndex, chart) : 0;
|
|
495
|
+
div.style.cssText =
|
|
496
|
+
`position:absolute;top:${topPx}px;left:${OVERLAY_LEFT_PX}px;z-index:5;` +
|
|
497
|
+
`pointer-events:none;display:flex;flex-wrap:wrap;align-items:flex-start;` +
|
|
498
|
+
`column-gap:8px;row-gap:2px;`;
|
|
499
|
+
container.appendChild(div);
|
|
500
|
+
subPaneOverlays.set(name, div);
|
|
501
|
+
return div;
|
|
502
|
+
}
|
|
503
|
+
function removeSubPaneOverlay(name) {
|
|
504
|
+
const el = subPaneOverlays.get(name);
|
|
505
|
+
if (el) {
|
|
506
|
+
el.remove();
|
|
507
|
+
subPaneOverlays.delete(name);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
exports.removeSubPaneOverlay = removeSubPaneOverlay;
|
|
511
|
+
function removeAllSubPaneOverlays() {
|
|
512
|
+
for (const el of subPaneOverlays.values()) {
|
|
513
|
+
el.remove();
|
|
514
|
+
}
|
|
515
|
+
subPaneOverlays.clear();
|
|
516
|
+
}
|
|
517
|
+
function repositionSubPaneOverlays(chart) {
|
|
518
|
+
const activeStudies = (0, state_js_1.getActiveStudies)();
|
|
519
|
+
for (const [name, el] of subPaneOverlays) {
|
|
520
|
+
const studyId = activeStudies.get(name);
|
|
521
|
+
const study = studyId ? chart.getStudyById(studyId) : null;
|
|
522
|
+
const paneIdx = study?.paneIndex?.();
|
|
523
|
+
if (paneIdx !== undefined && paneIdx > 0) {
|
|
524
|
+
el.style.top = `${getSubPaneTopPx(paneIdx, chart)}px`;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// ----- Layout ------------------------------------------------------------
|
|
529
|
+
const FALLBACK_SCALE_WIDTH = 48;
|
|
530
|
+
const SCALE_GAP = 4;
|
|
531
|
+
function getPriceScaleWidth() {
|
|
532
|
+
const widget = (0, state_js_1.getWidget)();
|
|
533
|
+
if (!widget) {
|
|
534
|
+
return FALLBACK_SCALE_WIDTH;
|
|
535
|
+
}
|
|
536
|
+
const chart = widget.activeChart();
|
|
537
|
+
const panes = chart.getPanes?.();
|
|
538
|
+
if (!panes || panes.length === 0) {
|
|
539
|
+
return FALLBACK_SCALE_WIDTH;
|
|
540
|
+
}
|
|
541
|
+
const scales = panes[0].getRightPriceScales?.();
|
|
542
|
+
const width = scales?.[0]?.width?.();
|
|
543
|
+
return width && width > 0 ? width : FALLBACK_SCALE_WIDTH;
|
|
544
|
+
}
|
|
545
|
+
function updateLegendOverlayLayout() {
|
|
546
|
+
const container = document.getElementById('tv_chart_container');
|
|
547
|
+
if (!container) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const containerWidth = container.clientWidth;
|
|
551
|
+
if (containerWidth <= 0) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const scaleWidth = getPriceScaleWidth();
|
|
555
|
+
const maxWidth = `${containerWidth - OVERLAY_LEFT_PX - scaleWidth - SCALE_GAP}px`;
|
|
556
|
+
const overlay = document.getElementById(OVERLAY_ID);
|
|
557
|
+
if (overlay) {
|
|
558
|
+
overlay.style.maxWidth = maxWidth;
|
|
559
|
+
}
|
|
560
|
+
for (const el of subPaneOverlays.values()) {
|
|
561
|
+
el.style.maxWidth = maxWidth;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
exports.updateLegendOverlayLayout = updateLegendOverlayLayout;
|
|
565
|
+
/** Test-only: clear all module-local state between cases. */
|
|
566
|
+
function _resetLegendForTests() {
|
|
567
|
+
exportGeneration = 0;
|
|
568
|
+
retryCount = 0;
|
|
569
|
+
clearTimer();
|
|
570
|
+
legendOverlayEnabled = false;
|
|
571
|
+
legendConfig = undefined;
|
|
572
|
+
removeAllSubPaneOverlays();
|
|
573
|
+
}
|
|
574
|
+
exports._resetLegendForTests = _resetLegendForTests;
|
|
575
|
+
//# sourceMappingURL=legend.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legend.cjs","sourceRoot":"","sources":["../../../src/features/indicators/legend.ts"],"names":[],"mappings":"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","sourcesContent":["// DOM legend overlay for active indicator studies.\n//\n// Ported from chartLogic.js: createStudyLegendOverlay (~4365),\n// refreshStudyLegendFromExport (~4644), buildLegendHTML (~4497),\n// updateLegendOverlayLayout (~4401), legend retry/timeout machinery\n// (~4630-4785), getMainPriceAxisLeftRelativeTo (~1472).\n//\n// The overlay is a `<div id=\"study-legend-overlay\">` injected into\n// #tv_chart_container that holds one `.legend-pill` per active indicator.\n// Theme-aware text colors are computed from CONFIG.theme; per-plot colors\n// come from the legend config supplied by RN.\n//\n// `LEGEND_RENDERED` is posted to RN once the overlay has settled (either\n// real values returned by chart.exportData() or after the retry timeout).\n\nimport { postToRN } from '../../core/bridge.js';\nimport {\n doesLegendOwnLayoutSettle,\n getActiveStudies,\n getLegendStudyOrder,\n getMaStudies,\n getTheme,\n getVolumeStudyId,\n getWidget,\n isChartReady,\n setLegendOwnsLayoutSettle,\n} from '../../core/state.js';\nimport type {\n LegendIndicatorCfg,\n LegendOverlayConfig,\n LegendPlotCfg,\n StudyId,\n TVActiveChart,\n TVExportData,\n} from '../../core/types.js';\nimport { eachChartDocument } from '../../widget/tvDomHelpers.js';\n\nconst OVERLAY_ID = 'study-legend-overlay';\nconst OVERLAY_LEFT_PX = 8;\nconst MAX_RETRIES = 10;\nconst RETRY_DELAY_MS = 100;\nconst RENDER_TIMEOUT_MS = 3000;\n\nlet exportGeneration = 0;\nlet retryCount = 0;\nlet timeoutId: ReturnType<typeof setTimeout> | null = null;\nlet legendOverlayEnabled = false;\n/** Typed legend config from RN — the single source of truth for legend rendering. */\nlet legendConfig: Record<string, LegendIndicatorCfg> | undefined;\n/** Sub-pane overlay elements keyed by indicator name. */\nconst subPaneOverlays = new Map<string, HTMLDivElement>();\n\n// ----- Lifecycle ---------------------------------------------------------\n\n/**\n * Called once on chart-ready to set up the DOM container.\n *\n * @param config - The legend overlay configuration, or undefined.\n */\nexport function setupLegendOverlay(\n config: LegendOverlayConfig | undefined,\n): void {\n legendOverlayEnabled = Boolean(config?.enabled);\n legendConfig = config?.config;\n if (!legendOverlayEnabled) {\n return;\n }\n createOverlayElement();\n injectHideLegendButtonsCSS();\n}\n\n/**\n * Subscribes to the widget's `panes_height_changed` event so the overlay\n * max-width is recomputed whenever a pane resize (e.g. after adding MACD\n * or RSI) shifts the price-axis boundary.\n *\n * @param widget - The TradingView widget (subset used here).\n * @param widget.subscribe - Subscribes to the `panes_height_changed` event.\n * @param widget.activeChart - Returns the active chart.\n */\nexport function attachLegendResizeListener(widget: {\n subscribe(event: 'panes_height_changed', handler: () => void): void;\n activeChart(): TVActiveChart;\n}): void {\n try {\n widget.subscribe('panes_height_changed', () => {\n const el = document.getElementById(OVERLAY_ID);\n if (el) {\n updateLegendOverlayLayout();\n }\n repositionSubPaneOverlays(widget.activeChart());\n });\n } catch {\n // TV may throw if subscribe isn't ready; safe to ignore.\n }\n}\n\nfunction createOverlayElement(): void {\n const existing = document.getElementById(OVERLAY_ID);\n if (existing) {\n existing.remove();\n }\n const container = document.getElementById('tv_chart_container');\n if (!container) {\n return;\n }\n const div = document.createElement('div');\n div.id = OVERLAY_ID;\n div.style.cssText =\n `position:absolute;top:1px;left:${OVERLAY_LEFT_PX}px;z-index:5;` +\n `pointer-events:none;display:flex;flex-wrap:wrap;align-items:flex-start;` +\n `column-gap:8px;row-gap:2px;`;\n container.style.position = 'relative';\n container.appendChild(div);\n}\n\nfunction injectHideLegendButtonsCSS(): void {\n const styleId = 'mm-hide-legend-buttons';\n if (document.getElementById(styleId)) {\n return;\n }\n let targetDoc: Document = document;\n eachChartDocument((doc) => {\n if (targetDoc === document && doc !== document) {\n targetDoc = doc;\n }\n });\n const style = targetDoc.createElement('style');\n style.id = styleId;\n style.textContent =\n '.chart-controls-bar .apply-common-tooltip,' +\n '.legendElement .showHide,' +\n '.legendElement button[data-name=\"legend-show-hide-action\"],' +\n '.legendElement button[data-name=\"legend-settings-action\"],' +\n '.legendElement button[data-name=\"legend-delete-action\"],' +\n '.legendElement .buttons-wrapper,' +\n '.legendElement .buttonsWrapper{display:none!important;}';\n targetDoc.head.appendChild(style);\n}\n\n// ----- Legend rebuild ---------------------------------------------------\n\n/**\n * Returns the per-indicator legend config supplied by RN via legendOverlay.config.\n * Consumers must pass their own config — there is no built-in fallback.\n *\n * @returns The per-indicator legend config map (empty when unset).\n */\nfunction getPresetMap(): Record<string, LegendIndicatorCfg> {\n return legendConfig ?? {};\n}\n\nfunction getLegendAltColor(): string {\n const theme = getTheme();\n return (\n theme?.legendTextColor ??\n theme?.textAlternativeColor ??\n theme?.textColor ??\n 'rgb(133,136,152)'\n );\n}\n\ntype StudyValueEntry = {\n title: string;\n value: string;\n};\n\ntype StudyDataEntry = {\n name: string;\n values: StudyValueEntry[];\n};\n\nfunction isEmptyValue(value: string): boolean {\n return !value || value === '' || value === 'n/a' || value === '∅';\n}\n\nfunction plotValue(\n indicatorConfig: LegendIndicatorCfg,\n plotCfg: LegendPlotCfg,\n plotIndex: number,\n values: StudyValueEntry[],\n): string {\n if (indicatorConfig.useIndex && plotIndex < values.length) {\n return values[plotIndex].value;\n }\n const match = values.find((candidate) => candidate.title === plotCfg.tvTitle);\n return match?.value ?? '';\n}\n\nfunction wrapPill(innerHtml: string, color?: string): string {\n const style = color ? ` style=\"color:${color};\"` : '';\n return `<span class=\"legend-pill\"${style}>${innerHtml}</span>`;\n}\n\nfunction buildHTML(entries: StudyDataEntry[]): string {\n const altColor = getLegendAltColor();\n const presets = getPresetMap();\n const successColor = getTheme()?.successColor ?? 'rgb(38,166,154)';\n const pills: string[] = [];\n\n for (const entry of entries) {\n const indicatorConfig = presets[entry.name];\n if (!indicatorConfig) {\n continue;\n }\n\n if (indicatorConfig.isMA) {\n const ma = indicatorConfig.plots[0];\n const value = plotValue(indicatorConfig, ma, 0, entry.values);\n if (isEmptyValue(value)) {\n continue;\n }\n pills.push(wrapPill(`${ma.label} ${value}`, ma.color ?? undefined));\n continue;\n }\n\n if (indicatorConfig.combineInOnePill) {\n const labelColor = indicatorConfig.plots[0].color ?? successColor;\n let inner = `<span style=\"color:${labelColor}\">${indicatorConfig.title ?? indicatorConfig.plots[0].label}</span>`;\n let hasValues = false;\n indicatorConfig.plots.forEach((plot, idx) => {\n const value = plotValue(indicatorConfig, plot, idx, entry.values);\n if (isEmptyValue(value)) {\n return;\n }\n hasValues = true;\n inner +=\n `<span style=\"color:${labelColor}\"> ${plot.label}</span>` +\n `<span style=\"color:${altColor}\"> ${value}</span>`;\n });\n if (hasValues) {\n pills.push(wrapPill(inner));\n }\n continue;\n }\n\n indicatorConfig.plots.forEach((plot, idx) => {\n const value = plotValue(indicatorConfig, plot, idx, entry.values);\n if (isEmptyValue(value)) {\n return;\n }\n const color = plot.color ?? successColor;\n const inner =\n `<span style=\"color:${color}\">${plot.label}</span>` +\n `<span style=\"color:${altColor}\"> ${value}</span>`;\n pills.push(wrapPill(inner));\n });\n }\n return pills.join('');\n}\n\n// ----- Refresh from chart.exportData() ----------------------------------\n\nfunction collectStudyIdMap(): Record<string, string> {\n const map: Record<string, string> = {};\n for (const [name, id] of getActiveStudies().entries()) {\n map[String(id)] = name;\n }\n for (const [name, id] of getMaStudies().entries()) {\n map[String(id)] = name;\n }\n const vol = getVolumeStudyId();\n if (vol) {\n map[String(vol)] = 'Volume';\n }\n return map;\n}\n\nfunction buildOrderedEntries(\n byStudy: Record<string, StudyValueEntry[]>,\n): StudyDataEntry[] {\n const result: StudyDataEntry[] = [];\n for (const [name, studyId] of getLegendStudyOrder().entries()) {\n const sid = String(studyId);\n const values = byStudy[sid];\n if (values) {\n result.push({ name, values });\n }\n }\n return result;\n}\n\nfunction formatLegendValue(value: number): string {\n if (!Number.isFinite(value)) {\n return '';\n }\n const abs = Math.abs(value);\n if (abs >= 1e9) {\n return `${(value / 1e9).toFixed(2)}B`;\n }\n if (abs >= 1e6) {\n return `${(value / 1e6).toFixed(2)}M`;\n }\n if (abs >= 1e4) {\n return `${(value / 1e3).toFixed(1)}K`;\n }\n if (abs >= 1000) {\n return value.toFixed(2);\n }\n if (abs >= 1) {\n return value.toFixed(2);\n }\n if (abs >= 0.01) {\n return value.toFixed(4);\n }\n return value.toPrecision(4);\n}\n\nfunction hasAnyEmpty(entries: StudyDataEntry[]): boolean {\n for (const entry of entries) {\n for (const value of entry.values) {\n if (isEmptyValue(value.value)) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction notifyLegendRendered(): void {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n postToRN('LEGEND_RENDERED', {});\n if (doesLegendOwnLayoutSettle()) {\n setLegendOwnsLayoutSettle(false);\n postToRN('CHART_LAYOUT_SETTLED', {});\n }\n });\n });\n}\n\nfunction clearTimer(): void {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n}\n\nfunction startTimeout(gen: number): void {\n clearTimer();\n timeoutId = setTimeout(() => {\n if (gen !== exportGeneration) {\n return;\n }\n retryCount = 0;\n clearTimer();\n notifyLegendRendered();\n }, RENDER_TIMEOUT_MS);\n}\n\nfunction scheduleRetry(gen: number): void {\n if (retryCount >= MAX_RETRIES) {\n retryCount = 0;\n clearTimer();\n notifyLegendRendered();\n return;\n }\n retryCount += 1;\n setTimeout(() => {\n if (gen === exportGeneration) {\n refreshStudyLegendFromExport();\n }\n }, RETRY_DELAY_MS);\n}\n\nfunction renderOverlay(entries: StudyDataEntry[]): void {\n const presets = getPresetMap();\n const widget = getWidget();\n const chart = widget?.activeChart();\n const activeStudies = getActiveStudies();\n\n const mainEntries: StudyDataEntry[] = [];\n const subPaneEntries: {\n name: string;\n paneIdx: number;\n entry: StudyDataEntry;\n }[] = [];\n\n for (const entry of entries) {\n const preset = presets[entry.name];\n if (preset?.subPaneLegend && chart) {\n const studyId = activeStudies.get(entry.name);\n const study = studyId ? chart.getStudyById(studyId) : null;\n const paneIdx = study?.paneIndex?.();\n if (paneIdx !== undefined && paneIdx > 0) {\n subPaneEntries.push({ name: entry.name, paneIdx, entry });\n continue;\n }\n }\n mainEntries.push(entry);\n }\n\n const mainOverlay = document.getElementById(OVERLAY_ID);\n if (mainOverlay) {\n mainOverlay.innerHTML = buildHTML(mainEntries);\n }\n\n const activeNames = new Set(subPaneEntries.map((subPane) => subPane.name));\n for (const name of subPaneOverlays.keys()) {\n if (!activeNames.has(name)) {\n removeSubPaneOverlay(name);\n }\n }\n\n for (const { name, paneIdx, entry } of subPaneEntries) {\n const overlay = ensureSubPaneOverlay(name, paneIdx, chart ?? undefined);\n if (overlay) {\n overlay.innerHTML = buildHTML([entry]);\n }\n }\n\n updateLegendOverlayLayout();\n if (chart) {\n repositionSubPaneOverlays(chart);\n }\n}\n\nexport function refreshStudyLegendFromExport(): void {\n if (!legendOverlayEnabled) {\n return;\n }\n const widget = getWidget();\n if (!widget || !isChartReady()) {\n return;\n }\n const overlay = document.getElementById(OVERLAY_ID);\n if (!overlay) {\n return;\n }\n\n const studyIdMap = collectStudyIdMap();\n const studyIds = Object.keys(studyIdMap);\n if (studyIds.length === 0) {\n overlay.innerHTML = '';\n removeAllSubPaneOverlays();\n retryCount = 0;\n clearTimer();\n return;\n }\n\n exportGeneration += 1;\n const gen = exportGeneration;\n if (retryCount === 0) {\n startTimeout(gen);\n }\n\n const chart = widget.activeChart();\n chart\n .exportData({\n includeSeries: false,\n includedStudies: studyIds,\n })\n .then((data) => {\n if (gen === exportGeneration) {\n handleExportData(data, gen);\n }\n return undefined;\n })\n .catch(() => scheduleRetry(gen));\n}\n\nfunction isValidExportData(data: TVExportData): boolean {\n return Boolean(data?.schema && data.data && data.data.length > 0);\n}\n\nfunction resolveDisplayValue(\n rawVal: number | undefined,\n colIndex: number,\n displayedData: TVExportData['displayedData'],\n): string {\n let displayVal =\n rawVal !== undefined && !Number.isNaN(rawVal)\n ? formatLegendValue(rawVal)\n : '';\n if (displayedData && displayedData.length > 0) {\n const dispRow = displayedData.at(-1);\n if (dispRow?.[colIndex]) {\n displayVal = dispRow[colIndex];\n }\n }\n return displayVal;\n}\n\nfunction buildStudyMap(\n data: TVExportData,\n lastRow: (number | undefined)[],\n): Record<string, StudyValueEntry[]> {\n const byStudy: Record<string, StudyValueEntry[]> = {};\n for (let index = 0; index < data.schema.length; index++) {\n const field = data.schema[index];\n if (field.type === 'time' || field.type === 'userTime') {\n continue;\n }\n const sid = field.sourceId ? String(field.sourceId) : '';\n if (!sid) {\n continue;\n }\n if (!byStudy[sid]) {\n byStudy[sid] = [];\n }\n const displayVal = resolveDisplayValue(\n lastRow[index],\n index,\n data.displayedData,\n );\n byStudy[sid].push({ title: field.plotTitle ?? '', value: displayVal });\n }\n return byStudy;\n}\n\nfunction handleExportData(data: TVExportData, gen: number): void {\n if (!isValidExportData(data)) {\n scheduleRetry(gen);\n return;\n }\n const lastRow = data.data.at(-1);\n if (!lastRow) {\n scheduleRetry(gen);\n return;\n }\n\n const byStudy = buildStudyMap(data, lastRow);\n const entries = buildOrderedEntries(byStudy);\n if (hasAnyEmpty(entries) && retryCount < MAX_RETRIES) {\n scheduleRetry(gen);\n return;\n }\n\n retryCount = 0;\n renderOverlay(entries);\n clearTimer();\n notifyLegendRendered();\n}\n\n/**\n * Used by indicator handlers to request a legend rebuild after a study has\n * been added/removed. Two rAFs to wait for TV's internal layout pass.\n */\nexport function scheduleLegendRefresh(): void {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => refreshStudyLegendFromExport());\n });\n}\n\n/**\n * Subscribes to a study's onDataLoaded event so the legend refreshes once\n * the study's calculation finishes. Falls back to immediate refresh when\n * the subscription API isn't available.\n *\n * @param chart - The active TradingView chart.\n * @param studyId - The id of the study to subscribe to.\n */\nexport function subscribeStudyDataLoaded(\n chart: TVActiveChart,\n studyId: StudyId,\n): void {\n try {\n const study = chart.getStudyById(studyId);\n if (study?.onDataLoaded) {\n study.onDataLoaded().subscribe(null, () => {\n scheduleLegendRefresh();\n });\n return;\n }\n } catch {\n // Fallthrough to direct refresh.\n }\n scheduleLegendRefresh();\n}\n\n// ----- Sub-pane overlay management ----------------------------------------\n\nfunction subPaneOverlayId(name: string): string {\n return `${OVERLAY_ID}-pane-${name}`;\n}\n\nfunction getSubPaneTopPx(paneIndex: number, chart: TVActiveChart): number {\n const heights = chart.getAllPanesHeight();\n let top = 0;\n for (let i = 0; i < paneIndex && i < heights.length; i++) {\n top += heights[i];\n }\n return top + 4;\n}\n\nfunction ensureSubPaneOverlay(\n name: string,\n paneIndex: number,\n chart?: TVActiveChart,\n): HTMLDivElement | null {\n const existing = subPaneOverlays.get(name);\n if (existing && document.contains(existing)) {\n return existing;\n }\n\n const container = document.getElementById('tv_chart_container');\n if (!container) {\n return null;\n }\n\n const div = document.createElement('div');\n div.id = subPaneOverlayId(name);\n const topPx = chart ? getSubPaneTopPx(paneIndex, chart) : 0;\n div.style.cssText =\n `position:absolute;top:${topPx}px;left:${OVERLAY_LEFT_PX}px;z-index:5;` +\n `pointer-events:none;display:flex;flex-wrap:wrap;align-items:flex-start;` +\n `column-gap:8px;row-gap:2px;`;\n container.appendChild(div);\n subPaneOverlays.set(name, div);\n return div;\n}\n\nexport function removeSubPaneOverlay(name: string): void {\n const el = subPaneOverlays.get(name);\n if (el) {\n el.remove();\n subPaneOverlays.delete(name);\n }\n}\n\nfunction removeAllSubPaneOverlays(): void {\n for (const el of subPaneOverlays.values()) {\n el.remove();\n }\n subPaneOverlays.clear();\n}\n\nfunction repositionSubPaneOverlays(chart: TVActiveChart): void {\n const activeStudies = getActiveStudies();\n for (const [name, el] of subPaneOverlays) {\n const studyId = activeStudies.get(name);\n const study = studyId ? chart.getStudyById(studyId) : null;\n const paneIdx = study?.paneIndex?.();\n if (paneIdx !== undefined && paneIdx > 0) {\n el.style.top = `${getSubPaneTopPx(paneIdx, chart)}px`;\n }\n }\n}\n\n// ----- Layout ------------------------------------------------------------\n\nconst FALLBACK_SCALE_WIDTH = 48;\nconst SCALE_GAP = 4;\n\nfunction getPriceScaleWidth(): number {\n const widget = getWidget();\n if (!widget) {\n return FALLBACK_SCALE_WIDTH;\n }\n const chart = widget.activeChart();\n const panes = chart.getPanes?.();\n if (!panes || panes.length === 0) {\n return FALLBACK_SCALE_WIDTH;\n }\n const scales = panes[0].getRightPriceScales?.();\n const width = scales?.[0]?.width?.();\n return width && width > 0 ? width : FALLBACK_SCALE_WIDTH;\n}\n\nexport function updateLegendOverlayLayout(): void {\n const container = document.getElementById('tv_chart_container');\n if (!container) {\n return;\n }\n const containerWidth = container.clientWidth;\n if (containerWidth <= 0) {\n return;\n }\n\n const scaleWidth = getPriceScaleWidth();\n const maxWidth = `${containerWidth - OVERLAY_LEFT_PX - scaleWidth - SCALE_GAP}px`;\n\n const overlay = document.getElementById(OVERLAY_ID);\n if (overlay) {\n overlay.style.maxWidth = maxWidth;\n }\n\n for (const el of subPaneOverlays.values()) {\n el.style.maxWidth = maxWidth;\n }\n}\n\n/** Test-only: clear all module-local state between cases. */\nexport function _resetLegendForTests(): void {\n exportGeneration = 0;\n retryCount = 0;\n clearTimer();\n legendOverlayEnabled = false;\n legendConfig = undefined;\n removeAllSubPaneOverlays();\n}\n"]}
|