hyperprop-charting-library 0.1.128 → 0.1.130
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/hyperprop-charting-library.cjs +439 -62
- package/dist/hyperprop-charting-library.d.ts +26 -1
- package/dist/hyperprop-charting-library.js +439 -62
- package/dist/index.cjs +439 -62
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +439 -62
- package/docs/API.md +10 -0
- package/package.json +1 -1
|
@@ -759,6 +759,40 @@ var fillBetweenSeries = (ctx, renderContext, upper, lower, color, opacity) => {
|
|
|
759
759
|
flushRun(renderContext.endIndex + 1);
|
|
760
760
|
ctx.restore();
|
|
761
761
|
};
|
|
762
|
+
var drawPaneHorizontalGrid = (ctx, renderContext, minValue, maxValue) => {
|
|
763
|
+
const grid = renderContext.grid;
|
|
764
|
+
if (!grid || !grid.horizontalLines) return;
|
|
765
|
+
const range = maxValue - minValue;
|
|
766
|
+
if (!(range > 0) || !Number.isFinite(range)) return;
|
|
767
|
+
const targetCount = Math.max(2, Math.round(renderContext.chartHeight / 42));
|
|
768
|
+
const targetStep = range / targetCount;
|
|
769
|
+
if (!(targetStep > 0) || !Number.isFinite(targetStep)) return;
|
|
770
|
+
const base = Math.pow(10, Math.floor(Math.log10(targetStep)));
|
|
771
|
+
const frac = targetStep / base;
|
|
772
|
+
const step = (frac <= 1 ? 1 : frac <= 2 ? 2 : frac <= 2.5 ? 2.5 : frac <= 5 ? 5 : 10) * base;
|
|
773
|
+
ctx.save();
|
|
774
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, grid.opacity));
|
|
775
|
+
ctx.strokeStyle = grid.color;
|
|
776
|
+
ctx.lineWidth = 1;
|
|
777
|
+
ctx.setLineDash([]);
|
|
778
|
+
ctx.beginPath();
|
|
779
|
+
const firstTick = Math.ceil(minValue / step) * step;
|
|
780
|
+
const edgeInset = renderContext.chartHeight * 0.04;
|
|
781
|
+
for (let i = 0; i < 64; i += 1) {
|
|
782
|
+
const value = firstTick + i * step;
|
|
783
|
+
if (value > maxValue) break;
|
|
784
|
+
const ratio = (value - minValue) / range;
|
|
785
|
+
const y = renderContext.chartBottom - ratio * renderContext.chartHeight;
|
|
786
|
+
if (y > renderContext.chartBottom - edgeInset || y < renderContext.chartBottom - renderContext.chartHeight + edgeInset) {
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const crispY = Math.round(y) + 0.5;
|
|
790
|
+
ctx.moveTo(renderContext.chartLeft, crispY);
|
|
791
|
+
ctx.lineTo(renderContext.chartRight, crispY);
|
|
792
|
+
}
|
|
793
|
+
ctx.stroke();
|
|
794
|
+
ctx.restore();
|
|
795
|
+
};
|
|
762
796
|
var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride, maxOverride, guideLines, options = {}) => {
|
|
763
797
|
const visible = [];
|
|
764
798
|
for (let index = renderContext.startIndex; index <= renderContext.endIndex; index += 1) {
|
|
@@ -770,6 +804,7 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
770
804
|
if (visible.length === 0) return void 0;
|
|
771
805
|
const minValue = minOverride ?? Math.min(...visible);
|
|
772
806
|
const maxValue = maxOverride ?? Math.max(...visible);
|
|
807
|
+
drawPaneHorizontalGrid(ctx, renderContext, minValue, maxValue);
|
|
773
808
|
const range = maxValue - minValue || 1;
|
|
774
809
|
const yFromValue = (value) => {
|
|
775
810
|
const ratio = (value - minValue) / range;
|
|
@@ -824,6 +859,19 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
824
859
|
break;
|
|
825
860
|
}
|
|
826
861
|
}
|
|
862
|
+
if (options.valueLine && latestValue !== null && latestValue >= minValue && latestValue <= maxValue) {
|
|
863
|
+
ctx.save();
|
|
864
|
+
ctx.lineWidth = 1;
|
|
865
|
+
ctx.setLineDash([3, 3]);
|
|
866
|
+
ctx.globalAlpha = 0.65;
|
|
867
|
+
ctx.strokeStyle = color;
|
|
868
|
+
const y = yFromValue(latestValue);
|
|
869
|
+
ctx.beginPath();
|
|
870
|
+
ctx.moveTo(renderContext.chartLeft, y);
|
|
871
|
+
ctx.lineTo(renderContext.chartRight, y);
|
|
872
|
+
ctx.stroke();
|
|
873
|
+
ctx.restore();
|
|
874
|
+
}
|
|
827
875
|
const decimals = options.decimals ?? 2;
|
|
828
876
|
const formatValue = (value) => value.toFixed(decimals);
|
|
829
877
|
const axisTicks = options.axisTicks ?? guideLines;
|
|
@@ -1151,7 +1199,7 @@ var BUILTIN_STDDEV_INDICATOR = {
|
|
|
1151
1199
|
name: "StdDev",
|
|
1152
1200
|
pane: "separate",
|
|
1153
1201
|
paneHeightRatio: 0.16,
|
|
1154
|
-
defaultInputs: { length: 20, source: "close", color: "#f97316", width: 2 },
|
|
1202
|
+
defaultInputs: { length: 20, source: "close", color: "#f97316", width: 2, showValueLine: true },
|
|
1155
1203
|
draw: (ctx, renderContext, inputs) => {
|
|
1156
1204
|
const length = clampIndicatorLength(inputs.length, 20);
|
|
1157
1205
|
const values = withCachedSeries(
|
|
@@ -1161,7 +1209,8 @@ var BUILTIN_STDDEV_INDICATOR = {
|
|
|
1161
1209
|
);
|
|
1162
1210
|
return drawSeparateSeries(ctx, renderContext, values, inputs.color ?? "#f97316", Number(inputs.width) || 2, void 0, void 0, void 0, {
|
|
1163
1211
|
title: `StdDev ${length}`,
|
|
1164
|
-
decimals: 2
|
|
1212
|
+
decimals: 2,
|
|
1213
|
+
valueLine: inputs.showValueLine !== false
|
|
1165
1214
|
});
|
|
1166
1215
|
}
|
|
1167
1216
|
};
|
|
@@ -1170,13 +1219,14 @@ var BUILTIN_ATR_INDICATOR = {
|
|
|
1170
1219
|
name: "ATR",
|
|
1171
1220
|
pane: "separate",
|
|
1172
1221
|
paneHeightRatio: 0.16,
|
|
1173
|
-
defaultInputs: { length: 14, color: "#eab308", width: 2 },
|
|
1222
|
+
defaultInputs: { length: 14, color: "#eab308", width: 2, showValueLine: true },
|
|
1174
1223
|
draw: (ctx, renderContext, inputs) => {
|
|
1175
1224
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
1176
1225
|
const values = withCachedSeries(`atr|${length}`, renderContext.data, () => computeAtrSeries(renderContext.data, length));
|
|
1177
1226
|
return drawSeparateSeries(ctx, renderContext, values, inputs.color ?? "#eab308", Number(inputs.width) || 2, void 0, void 0, void 0, {
|
|
1178
1227
|
title: `ATR ${length}`,
|
|
1179
|
-
decimals: 2
|
|
1228
|
+
decimals: 2,
|
|
1229
|
+
valueLine: inputs.showValueLine !== false
|
|
1180
1230
|
});
|
|
1181
1231
|
}
|
|
1182
1232
|
};
|
|
@@ -1192,7 +1242,8 @@ var BUILTIN_RSI_INDICATOR = {
|
|
|
1192
1242
|
showLegend: true,
|
|
1193
1243
|
showValueLabel: true,
|
|
1194
1244
|
showGuideLines: true,
|
|
1195
|
-
showScaleLabels: true
|
|
1245
|
+
showScaleLabels: true,
|
|
1246
|
+
showValueLine: true
|
|
1196
1247
|
},
|
|
1197
1248
|
draw: (ctx, renderContext, inputs) => {
|
|
1198
1249
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
@@ -1215,7 +1266,8 @@ var BUILTIN_RSI_INDICATOR = {
|
|
|
1215
1266
|
valueLabelColor: "#9E9E9E",
|
|
1216
1267
|
valueLabelBackgroundColor: "#9E9E9E",
|
|
1217
1268
|
valueLabelTextColor: "#0f172a",
|
|
1218
|
-
showLegend: inputs.showLegend !== false
|
|
1269
|
+
showLegend: inputs.showLegend !== false,
|
|
1270
|
+
valueLine: inputs.showValueLine !== false
|
|
1219
1271
|
}
|
|
1220
1272
|
);
|
|
1221
1273
|
}
|
|
@@ -1272,6 +1324,7 @@ var drawSeparateMultiSeries = (ctx, renderContext, seriesList, options = {}) =>
|
|
|
1272
1324
|
const ratio = (value - minValue) / range;
|
|
1273
1325
|
return renderContext.chartBottom - ratio * renderContext.chartHeight;
|
|
1274
1326
|
};
|
|
1327
|
+
drawPaneHorizontalGrid(ctx, renderContext, minValue, maxValue);
|
|
1275
1328
|
if (options.guideLines && options.guideLines.length > 0) {
|
|
1276
1329
|
ctx.save();
|
|
1277
1330
|
ctx.strokeStyle = "rgba(148,163,184,0.35)";
|
|
@@ -1351,6 +1404,24 @@ var drawSeparateMultiSeries = (ctx, renderContext, seriesList, options = {}) =>
|
|
|
1351
1404
|
}
|
|
1352
1405
|
return null;
|
|
1353
1406
|
};
|
|
1407
|
+
if (options.valueLines) {
|
|
1408
|
+
ctx.save();
|
|
1409
|
+
ctx.lineWidth = 1;
|
|
1410
|
+
ctx.setLineDash([3, 3]);
|
|
1411
|
+
ctx.globalAlpha = 0.65;
|
|
1412
|
+
for (const spec of seriesList) {
|
|
1413
|
+
if (spec.histogram) continue;
|
|
1414
|
+
const latest = latestOf(spec.values);
|
|
1415
|
+
if (latest === null || latest < minValue || latest > maxValue) continue;
|
|
1416
|
+
const y = yFromValue(latest);
|
|
1417
|
+
ctx.strokeStyle = spec.color;
|
|
1418
|
+
ctx.beginPath();
|
|
1419
|
+
ctx.moveTo(renderContext.chartLeft, y);
|
|
1420
|
+
ctx.lineTo(renderContext.chartRight, y);
|
|
1421
|
+
ctx.stroke();
|
|
1422
|
+
}
|
|
1423
|
+
ctx.restore();
|
|
1424
|
+
}
|
|
1354
1425
|
const paneInfo = {
|
|
1355
1426
|
...options.title ? { title: options.title } : {},
|
|
1356
1427
|
axis: {
|
|
@@ -1704,7 +1775,8 @@ var BUILTIN_MACD_INDICATOR = {
|
|
|
1704
1775
|
macdColor: "#2962ff",
|
|
1705
1776
|
signalColor: "#ff6d00",
|
|
1706
1777
|
histUpColor: "#26a69a",
|
|
1707
|
-
histDownColor: "#ef5350"
|
|
1778
|
+
histDownColor: "#ef5350",
|
|
1779
|
+
showValueLine: true
|
|
1708
1780
|
},
|
|
1709
1781
|
draw: (ctx, renderContext, inputs) => {
|
|
1710
1782
|
const fast = clampIndicatorLength(inputs.fast, 12);
|
|
@@ -1733,7 +1805,8 @@ var BUILTIN_MACD_INDICATOR = {
|
|
|
1733
1805
|
includeZero: true,
|
|
1734
1806
|
guideLines: [0],
|
|
1735
1807
|
decimals: 2,
|
|
1736
|
-
valueLabelSeriesIndex: 1
|
|
1808
|
+
valueLabelSeriesIndex: 1,
|
|
1809
|
+
valueLines: inputs.showValueLine !== false
|
|
1737
1810
|
}
|
|
1738
1811
|
);
|
|
1739
1812
|
}
|
|
@@ -1743,7 +1816,7 @@ var BUILTIN_STOCHASTIC_INDICATOR = {
|
|
|
1743
1816
|
name: "Stoch",
|
|
1744
1817
|
pane: "separate",
|
|
1745
1818
|
paneHeightRatio: 0.18,
|
|
1746
|
-
defaultInputs: { kLength: 14, kSmoothing: 1, dLength: 3, kColor: "#2962ff", dColor: "#ff6d00" },
|
|
1819
|
+
defaultInputs: { kLength: 14, kSmoothing: 1, dLength: 3, kColor: "#2962ff", dColor: "#ff6d00", showValueLine: true },
|
|
1747
1820
|
draw: (ctx, renderContext, inputs) => {
|
|
1748
1821
|
const kLength = clampIndicatorLength(inputs.kLength, 14);
|
|
1749
1822
|
const kSmoothing = clampIndicatorLength(inputs.kSmoothing, 1);
|
|
@@ -1766,7 +1839,8 @@ var BUILTIN_STOCHASTIC_INDICATOR = {
|
|
|
1766
1839
|
maxOverride: 100,
|
|
1767
1840
|
guideLines: [20, 80],
|
|
1768
1841
|
axisTicks: [0, 20, 50, 80, 100],
|
|
1769
|
-
decimals: 2
|
|
1842
|
+
decimals: 2,
|
|
1843
|
+
valueLines: inputs.showValueLine !== false
|
|
1770
1844
|
}
|
|
1771
1845
|
);
|
|
1772
1846
|
}
|
|
@@ -1776,7 +1850,15 @@ var BUILTIN_STOCHRSI_INDICATOR = {
|
|
|
1776
1850
|
name: "Stoch RSI",
|
|
1777
1851
|
pane: "separate",
|
|
1778
1852
|
paneHeightRatio: 0.18,
|
|
1779
|
-
defaultInputs: {
|
|
1853
|
+
defaultInputs: {
|
|
1854
|
+
rsiLength: 14,
|
|
1855
|
+
stochLength: 14,
|
|
1856
|
+
kSmoothing: 3,
|
|
1857
|
+
dSmoothing: 3,
|
|
1858
|
+
kColor: "#2962ff",
|
|
1859
|
+
dColor: "#ff6d00",
|
|
1860
|
+
showValueLine: true
|
|
1861
|
+
},
|
|
1780
1862
|
draw: (ctx, renderContext, inputs) => {
|
|
1781
1863
|
const rsiLength = clampIndicatorLength(inputs.rsiLength, 14);
|
|
1782
1864
|
const stochLength = clampIndicatorLength(inputs.stochLength, 14);
|
|
@@ -1800,7 +1882,8 @@ var BUILTIN_STOCHRSI_INDICATOR = {
|
|
|
1800
1882
|
maxOverride: 100,
|
|
1801
1883
|
guideLines: [20, 80],
|
|
1802
1884
|
axisTicks: [0, 20, 50, 80, 100],
|
|
1803
|
-
decimals: 2
|
|
1885
|
+
decimals: 2,
|
|
1886
|
+
valueLines: inputs.showValueLine !== false
|
|
1804
1887
|
}
|
|
1805
1888
|
);
|
|
1806
1889
|
}
|
|
@@ -1810,7 +1893,14 @@ var BUILTIN_ADX_INDICATOR = {
|
|
|
1810
1893
|
name: "ADX/DMI",
|
|
1811
1894
|
pane: "separate",
|
|
1812
1895
|
paneHeightRatio: 0.18,
|
|
1813
|
-
defaultInputs: {
|
|
1896
|
+
defaultInputs: {
|
|
1897
|
+
length: 14,
|
|
1898
|
+
adxColor: "#ff6d00",
|
|
1899
|
+
plusDiColor: "#26a69a",
|
|
1900
|
+
minusDiColor: "#ef5350",
|
|
1901
|
+
showDi: true,
|
|
1902
|
+
showValueLine: true
|
|
1903
|
+
},
|
|
1814
1904
|
draw: (ctx, renderContext, inputs) => {
|
|
1815
1905
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
1816
1906
|
const { adx, plusDi, minusDi } = withCachedComputation(
|
|
@@ -1831,7 +1921,8 @@ var BUILTIN_ADX_INDICATOR = {
|
|
|
1831
1921
|
title: `ADX ${length}`,
|
|
1832
1922
|
minOverride: 0,
|
|
1833
1923
|
guideLines: [20],
|
|
1834
|
-
decimals: 2
|
|
1924
|
+
decimals: 2,
|
|
1925
|
+
valueLines: inputs.showValueLine !== false
|
|
1835
1926
|
});
|
|
1836
1927
|
}
|
|
1837
1928
|
};
|
|
@@ -1840,14 +1931,14 @@ var BUILTIN_OBV_INDICATOR = {
|
|
|
1840
1931
|
name: "OBV",
|
|
1841
1932
|
pane: "separate",
|
|
1842
1933
|
paneHeightRatio: 0.16,
|
|
1843
|
-
defaultInputs: { color: "#2962ff", width: 2 },
|
|
1934
|
+
defaultInputs: { color: "#2962ff", width: 2, showValueLine: true },
|
|
1844
1935
|
draw: (ctx, renderContext, inputs) => {
|
|
1845
1936
|
const values = withCachedSeries("obv", renderContext.data, () => computeObvSeries(renderContext.data));
|
|
1846
1937
|
return drawSeparateMultiSeries(
|
|
1847
1938
|
ctx,
|
|
1848
1939
|
renderContext,
|
|
1849
1940
|
[{ values, color: inputs.color ?? "#2962ff", width: Number(inputs.width) || 2, label: "OBV" }],
|
|
1850
|
-
{ title: "OBV", format: formatCompactNumber }
|
|
1941
|
+
{ title: "OBV", format: formatCompactNumber, valueLines: inputs.showValueLine !== false }
|
|
1851
1942
|
);
|
|
1852
1943
|
}
|
|
1853
1944
|
};
|
|
@@ -1856,7 +1947,7 @@ var BUILTIN_MFI_INDICATOR = {
|
|
|
1856
1947
|
name: "MFI",
|
|
1857
1948
|
pane: "separate",
|
|
1858
1949
|
paneHeightRatio: 0.16,
|
|
1859
|
-
defaultInputs: { length: 14, color: "#7e57c2", width: 2 },
|
|
1950
|
+
defaultInputs: { length: 14, color: "#7e57c2", width: 2, showValueLine: true },
|
|
1860
1951
|
draw: (ctx, renderContext, inputs) => {
|
|
1861
1952
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
1862
1953
|
const values = withCachedSeries(
|
|
@@ -1874,7 +1965,8 @@ var BUILTIN_MFI_INDICATOR = {
|
|
|
1874
1965
|
maxOverride: 100,
|
|
1875
1966
|
guideLines: [20, 80],
|
|
1876
1967
|
axisTicks: [0, 20, 50, 80, 100],
|
|
1877
|
-
decimals: 2
|
|
1968
|
+
decimals: 2,
|
|
1969
|
+
valueLines: inputs.showValueLine !== false
|
|
1878
1970
|
}
|
|
1879
1971
|
);
|
|
1880
1972
|
}
|
|
@@ -1884,7 +1976,7 @@ var BUILTIN_CCI_INDICATOR = {
|
|
|
1884
1976
|
name: "CCI",
|
|
1885
1977
|
pane: "separate",
|
|
1886
1978
|
paneHeightRatio: 0.16,
|
|
1887
|
-
defaultInputs: { length: 20, color: "#2962ff", width: 2 },
|
|
1979
|
+
defaultInputs: { length: 20, color: "#2962ff", width: 2, showValueLine: true },
|
|
1888
1980
|
draw: (ctx, renderContext, inputs) => {
|
|
1889
1981
|
const length = clampIndicatorLength(inputs.length, 20);
|
|
1890
1982
|
const values = withCachedSeries(
|
|
@@ -1896,7 +1988,7 @@ var BUILTIN_CCI_INDICATOR = {
|
|
|
1896
1988
|
ctx,
|
|
1897
1989
|
renderContext,
|
|
1898
1990
|
[{ values, color: inputs.color ?? "#2962ff", width: Number(inputs.width) || 2 }],
|
|
1899
|
-
{ title: `CCI ${length}`, guideLines: [-100, 0, 100], decimals: 2 }
|
|
1991
|
+
{ title: `CCI ${length}`, guideLines: [-100, 0, 100], decimals: 2, valueLines: inputs.showValueLine !== false }
|
|
1900
1992
|
);
|
|
1901
1993
|
}
|
|
1902
1994
|
};
|
|
@@ -1905,7 +1997,7 @@ var BUILTIN_WILLIAMSR_INDICATOR = {
|
|
|
1905
1997
|
name: "Williams %R",
|
|
1906
1998
|
pane: "separate",
|
|
1907
1999
|
paneHeightRatio: 0.16,
|
|
1908
|
-
defaultInputs: { length: 14, color: "#7e57c2", width: 2 },
|
|
2000
|
+
defaultInputs: { length: 14, color: "#7e57c2", width: 2, showValueLine: true },
|
|
1909
2001
|
draw: (ctx, renderContext, inputs) => {
|
|
1910
2002
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
1911
2003
|
const values = withCachedSeries(
|
|
@@ -1923,7 +2015,8 @@ var BUILTIN_WILLIAMSR_INDICATOR = {
|
|
|
1923
2015
|
maxOverride: 0,
|
|
1924
2016
|
guideLines: [-80, -20],
|
|
1925
2017
|
axisTicks: [-100, -80, -50, -20, 0],
|
|
1926
|
-
decimals: 2
|
|
2018
|
+
decimals: 2,
|
|
2019
|
+
valueLines: inputs.showValueLine !== false
|
|
1927
2020
|
}
|
|
1928
2021
|
);
|
|
1929
2022
|
}
|
|
@@ -1933,7 +2026,7 @@ var BUILTIN_ROC_INDICATOR = {
|
|
|
1933
2026
|
name: "ROC",
|
|
1934
2027
|
pane: "separate",
|
|
1935
2028
|
paneHeightRatio: 0.16,
|
|
1936
|
-
defaultInputs: { length: 9, color: "#2962ff", width: 2 },
|
|
2029
|
+
defaultInputs: { length: 9, color: "#2962ff", width: 2, showValueLine: true },
|
|
1937
2030
|
draw: (ctx, renderContext, inputs) => {
|
|
1938
2031
|
const length = clampIndicatorLength(inputs.length, 9);
|
|
1939
2032
|
const values = withCachedSeries(
|
|
@@ -1945,7 +2038,7 @@ var BUILTIN_ROC_INDICATOR = {
|
|
|
1945
2038
|
ctx,
|
|
1946
2039
|
renderContext,
|
|
1947
2040
|
[{ values, color: inputs.color ?? "#2962ff", width: Number(inputs.width) || 2 }],
|
|
1948
|
-
{ title: `ROC ${length}`, includeZero: true, guideLines: [0], decimals: 2 }
|
|
2041
|
+
{ title: `ROC ${length}`, includeZero: true, guideLines: [0], decimals: 2, valueLines: inputs.showValueLine !== false }
|
|
1949
2042
|
);
|
|
1950
2043
|
}
|
|
1951
2044
|
};
|
|
@@ -1954,7 +2047,7 @@ var BUILTIN_MOMENTUM_INDICATOR = {
|
|
|
1954
2047
|
name: "Momentum",
|
|
1955
2048
|
pane: "separate",
|
|
1956
2049
|
paneHeightRatio: 0.16,
|
|
1957
|
-
defaultInputs: { length: 10, color: "#2962ff", width: 2 },
|
|
2050
|
+
defaultInputs: { length: 10, color: "#2962ff", width: 2, showValueLine: true },
|
|
1958
2051
|
draw: (ctx, renderContext, inputs) => {
|
|
1959
2052
|
const length = clampIndicatorLength(inputs.length, 10);
|
|
1960
2053
|
const values = withCachedSeries(
|
|
@@ -1966,7 +2059,7 @@ var BUILTIN_MOMENTUM_INDICATOR = {
|
|
|
1966
2059
|
ctx,
|
|
1967
2060
|
renderContext,
|
|
1968
2061
|
[{ values, color: inputs.color ?? "#2962ff", width: Number(inputs.width) || 2 }],
|
|
1969
|
-
{ title: `Mom ${length}`, includeZero: true, guideLines: [0], decimals: 2 }
|
|
2062
|
+
{ title: `Mom ${length}`, includeZero: true, guideLines: [0], decimals: 2, valueLines: inputs.showValueLine !== false }
|
|
1970
2063
|
);
|
|
1971
2064
|
}
|
|
1972
2065
|
};
|
|
@@ -2390,6 +2483,14 @@ function createChart(element, options = {}) {
|
|
|
2390
2483
|
let watermarkImageReady = false;
|
|
2391
2484
|
let drawState = null;
|
|
2392
2485
|
let plotBottomForHit = 0;
|
|
2486
|
+
let paneLayoutFullChartHeight = 0;
|
|
2487
|
+
let paneLayoutInfos = [];
|
|
2488
|
+
let paneButtonRegions = [];
|
|
2489
|
+
let hoveredPaneId = null;
|
|
2490
|
+
let hoveredPaneButton = null;
|
|
2491
|
+
let paneDividerDrag = null;
|
|
2492
|
+
let indicatorPaneActionHandler = null;
|
|
2493
|
+
let indicatorPaneHeightChangeHandler = null;
|
|
2393
2494
|
let orderDragState = null;
|
|
2394
2495
|
let actionDragState = null;
|
|
2395
2496
|
let pointerDownInfo = null;
|
|
@@ -3491,20 +3592,24 @@ function createChart(element, options = {}) {
|
|
|
3491
3592
|
const watermark = { ...DEFAULT_WATERMARK_OPTIONS, ...mergedOptions.watermark ?? {} };
|
|
3492
3593
|
const paneGap = 0;
|
|
3493
3594
|
const separatePaneSpacing = 6;
|
|
3494
|
-
const
|
|
3595
|
+
const collapsedPaneHeight = 26;
|
|
3596
|
+
const activeSeparateIndicators = indicators.map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
3495
3597
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "separate"
|
|
3496
3598
|
).sort((a, b) => a.indicator.zIndex - b.indicator.zIndex);
|
|
3497
3599
|
const overlayIndicatorsForScale = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
3498
3600
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
3499
3601
|
);
|
|
3500
3602
|
const separatePaneHeightDefaults = activeSeparateIndicators.map(({ indicator, plugin }) => {
|
|
3603
|
+
if (!indicator.visible) return collapsedPaneHeight;
|
|
3501
3604
|
const ratio = Math.min(0.45, Math.max(0.08, indicator.paneHeightRatio ?? plugin.paneHeightRatio ?? 0.22));
|
|
3502
3605
|
return Math.round(fullChartHeight * ratio);
|
|
3503
3606
|
});
|
|
3504
3607
|
const separatePaneDesiredTotal = separatePaneHeightDefaults.reduce((sum, value) => sum + value, 0) + Math.max(0, activeSeparateIndicators.length - 1) * separatePaneSpacing;
|
|
3505
3608
|
const maxSeparatePaneTotal = Math.max(0, fullChartHeight - 140);
|
|
3506
3609
|
const separatePaneScale = separatePaneDesiredTotal > 0 && separatePaneDesiredTotal > maxSeparatePaneTotal ? maxSeparatePaneTotal / separatePaneDesiredTotal : 1;
|
|
3507
|
-
const separatePaneHeights = separatePaneHeightDefaults.map(
|
|
3610
|
+
const separatePaneHeights = separatePaneHeightDefaults.map(
|
|
3611
|
+
(value, index) => activeSeparateIndicators[index].indicator.visible ? Math.max(48, Math.round(value * separatePaneScale)) : collapsedPaneHeight
|
|
3612
|
+
);
|
|
3508
3613
|
const separatePaneTotal = separatePaneHeights.reduce((sum, value) => sum + value, 0) + Math.max(0, activeSeparateIndicators.length - 1) * separatePaneSpacing;
|
|
3509
3614
|
const pricePaneGap = activeSeparateIndicators.length > 0 ? paneGap : 0;
|
|
3510
3615
|
const chartHeight = Math.max(120, fullChartHeight - separatePaneTotal - pricePaneGap);
|
|
@@ -4755,43 +4860,138 @@ function createChart(element, options = {}) {
|
|
|
4755
4860
|
ctx.restore();
|
|
4756
4861
|
}
|
|
4757
4862
|
}
|
|
4863
|
+
paneLayoutFullChartHeight = fullChartHeight;
|
|
4864
|
+
paneLayoutInfos = [];
|
|
4865
|
+
paneButtonRegions = [];
|
|
4758
4866
|
if (activeSeparateIndicators.length > 0) {
|
|
4759
4867
|
const xFromIndex = (index) => chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
4760
4868
|
let paneTopCursor = chartBottom + paneGap;
|
|
4869
|
+
const paneButtonSize = 16;
|
|
4870
|
+
const paneButtonGap = 4;
|
|
4871
|
+
const drawPaneButtons = (indicatorId, indicatorType, indicatorVisible, startX, centerY) => {
|
|
4872
|
+
const actionList = [
|
|
4873
|
+
"visibility",
|
|
4874
|
+
"settings",
|
|
4875
|
+
"source",
|
|
4876
|
+
"remove"
|
|
4877
|
+
];
|
|
4878
|
+
let buttonX = startX;
|
|
4879
|
+
for (const action of actionList) {
|
|
4880
|
+
const buttonY = centerY - paneButtonSize / 2;
|
|
4881
|
+
const hovered = hoveredPaneButton?.id === indicatorId && hoveredPaneButton.action === action;
|
|
4882
|
+
const iconColor = labels.indicatorTextColor;
|
|
4883
|
+
ctx.save();
|
|
4884
|
+
ctx.fillStyle = hovered ? "rgba(148,163,184,0.30)" : "rgba(148,163,184,0.12)";
|
|
4885
|
+
fillRoundedRect(Math.round(buttonX), Math.round(buttonY), paneButtonSize, paneButtonSize, 4);
|
|
4886
|
+
ctx.strokeStyle = iconColor;
|
|
4887
|
+
ctx.fillStyle = iconColor;
|
|
4888
|
+
ctx.globalAlpha = hovered ? 1 : 0.75;
|
|
4889
|
+
ctx.lineWidth = 1.2;
|
|
4890
|
+
ctx.setLineDash([]);
|
|
4891
|
+
const cx = buttonX + paneButtonSize / 2;
|
|
4892
|
+
const cy = centerY;
|
|
4893
|
+
if (action === "visibility") {
|
|
4894
|
+
ctx.beginPath();
|
|
4895
|
+
ctx.ellipse(cx, cy, 4.6, 3, 0, 0, Math.PI * 2);
|
|
4896
|
+
ctx.stroke();
|
|
4897
|
+
ctx.beginPath();
|
|
4898
|
+
ctx.arc(cx, cy, 1.3, 0, Math.PI * 2);
|
|
4899
|
+
ctx.fill();
|
|
4900
|
+
if (!indicatorVisible) {
|
|
4901
|
+
ctx.beginPath();
|
|
4902
|
+
ctx.moveTo(cx - 5.2, cy + 4.6);
|
|
4903
|
+
ctx.lineTo(cx + 5.2, cy - 4.6);
|
|
4904
|
+
ctx.stroke();
|
|
4905
|
+
}
|
|
4906
|
+
} else if (action === "settings") {
|
|
4907
|
+
ctx.beginPath();
|
|
4908
|
+
ctx.arc(cx, cy, 3.4, 0, Math.PI * 2);
|
|
4909
|
+
ctx.stroke();
|
|
4910
|
+
for (let toothIndex = 0; toothIndex < 8; toothIndex += 1) {
|
|
4911
|
+
const angle = Math.PI / 4 * toothIndex;
|
|
4912
|
+
ctx.beginPath();
|
|
4913
|
+
ctx.moveTo(cx + Math.cos(angle) * 3.4, cy + Math.sin(angle) * 3.4);
|
|
4914
|
+
ctx.lineTo(cx + Math.cos(angle) * 5.2, cy + Math.sin(angle) * 5.2);
|
|
4915
|
+
ctx.stroke();
|
|
4916
|
+
}
|
|
4917
|
+
ctx.beginPath();
|
|
4918
|
+
ctx.arc(cx, cy, 1.1, 0, Math.PI * 2);
|
|
4919
|
+
ctx.fill();
|
|
4920
|
+
} else if (action === "source") {
|
|
4921
|
+
const prevSourceFont = ctx.font;
|
|
4922
|
+
ctx.font = `bold 9px ${mergedOptions.fontFamily}`;
|
|
4923
|
+
drawText("{}", cx, cy + 0.5, "center", "middle", iconColor);
|
|
4924
|
+
ctx.font = prevSourceFont;
|
|
4925
|
+
} else {
|
|
4926
|
+
ctx.beginPath();
|
|
4927
|
+
ctx.moveTo(cx - 3.4, cy - 3.4);
|
|
4928
|
+
ctx.lineTo(cx + 3.4, cy + 3.4);
|
|
4929
|
+
ctx.moveTo(cx + 3.4, cy - 3.4);
|
|
4930
|
+
ctx.lineTo(cx - 3.4, cy + 3.4);
|
|
4931
|
+
ctx.stroke();
|
|
4932
|
+
}
|
|
4933
|
+
ctx.restore();
|
|
4934
|
+
paneButtonRegions.push({
|
|
4935
|
+
id: indicatorId,
|
|
4936
|
+
type: indicatorType,
|
|
4937
|
+
action,
|
|
4938
|
+
x: buttonX,
|
|
4939
|
+
y: buttonY,
|
|
4940
|
+
width: paneButtonSize,
|
|
4941
|
+
height: paneButtonSize
|
|
4942
|
+
});
|
|
4943
|
+
buttonX += paneButtonSize + paneButtonGap;
|
|
4944
|
+
}
|
|
4945
|
+
};
|
|
4761
4946
|
activeSeparateIndicators.forEach(({ indicator, plugin }, paneIndex) => {
|
|
4762
4947
|
const paneHeight = separatePaneHeights[paneIndex] ?? 80;
|
|
4763
4948
|
const paneTop = paneTopCursor;
|
|
4764
4949
|
const paneBottom = paneTop + paneHeight;
|
|
4765
4950
|
paneTopCursor = paneBottom + separatePaneSpacing;
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4951
|
+
paneLayoutInfos.push({
|
|
4952
|
+
id: indicator.id,
|
|
4953
|
+
type: indicator.type,
|
|
4954
|
+
top: paneTop,
|
|
4955
|
+
bottom: paneBottom,
|
|
4956
|
+
visible: indicator.visible
|
|
4957
|
+
});
|
|
4958
|
+
let paneInfo;
|
|
4959
|
+
if (indicator.visible) {
|
|
4960
|
+
ctx.save();
|
|
4961
|
+
ctx.beginPath();
|
|
4962
|
+
ctx.rect(chartLeft + 1, paneTop + 1, Math.max(0, chartWidth - 2), Math.max(0, paneHeight - 2));
|
|
4963
|
+
ctx.clip();
|
|
4964
|
+
paneInfo = plugin.draw(
|
|
4965
|
+
ctx,
|
|
4966
|
+
{
|
|
4967
|
+
data,
|
|
4968
|
+
startIndex,
|
|
4969
|
+
endIndex,
|
|
4970
|
+
xStart,
|
|
4971
|
+
xSpan,
|
|
4972
|
+
chartLeft,
|
|
4973
|
+
chartRight,
|
|
4974
|
+
chartTop: paneTop,
|
|
4975
|
+
chartBottom: paneBottom,
|
|
4976
|
+
chartWidth,
|
|
4977
|
+
chartHeight: paneHeight,
|
|
4978
|
+
xFromIndex,
|
|
4979
|
+
yFromPrice: null,
|
|
4980
|
+
getCandleDirectionByIndex,
|
|
4981
|
+
getVolumeByIndex,
|
|
4982
|
+
candleSpacing,
|
|
4983
|
+
upColor: mergedOptions.upColor,
|
|
4984
|
+
downColor: mergedOptions.downColor,
|
|
4985
|
+
grid: {
|
|
4986
|
+
color: gridColor,
|
|
4987
|
+
opacity: gridOpacity,
|
|
4988
|
+
horizontalLines: grid.horizontalLines !== false
|
|
4989
|
+
}
|
|
4990
|
+
},
|
|
4991
|
+
indicator.inputs
|
|
4992
|
+
) ?? void 0;
|
|
4993
|
+
ctx.restore();
|
|
4994
|
+
}
|
|
4795
4995
|
ctx.save();
|
|
4796
4996
|
ctx.strokeStyle = axis.lineColor;
|
|
4797
4997
|
ctx.lineWidth = Math.max(1, axis.lineWidth);
|
|
@@ -4800,6 +5000,27 @@ function createChart(element, options = {}) {
|
|
|
4800
5000
|
ctx.lineTo(crisp(chartRight), crisp(paneTop));
|
|
4801
5001
|
ctx.stroke();
|
|
4802
5002
|
ctx.restore();
|
|
5003
|
+
if (!indicator.visible) {
|
|
5004
|
+
if (labels.visible) {
|
|
5005
|
+
const prevFont = ctx.font;
|
|
5006
|
+
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
5007
|
+
const hiddenText = `${plugin.name} (hidden)`;
|
|
5008
|
+
const rowCenterY = (paneTop + paneBottom) / 2;
|
|
5009
|
+
ctx.save();
|
|
5010
|
+
ctx.globalAlpha = 0.55;
|
|
5011
|
+
drawText(hiddenText, chartLeft + 10, rowCenterY, "left", "middle", labels.indicatorTextColor);
|
|
5012
|
+
ctx.restore();
|
|
5013
|
+
drawPaneButtons(
|
|
5014
|
+
indicator.id,
|
|
5015
|
+
indicator.type,
|
|
5016
|
+
false,
|
|
5017
|
+
chartLeft + 10 + Math.ceil(measureTextWidth(hiddenText)) + 10,
|
|
5018
|
+
rowCenterY
|
|
5019
|
+
);
|
|
5020
|
+
ctx.font = prevFont;
|
|
5021
|
+
}
|
|
5022
|
+
return;
|
|
5023
|
+
}
|
|
4803
5024
|
const axisInfo = paneInfo?.axis;
|
|
4804
5025
|
if (axisInfo && Number.isFinite(axisInfo.min) && Number.isFinite(axisInfo.max) && axisInfo.max !== axisInfo.min) {
|
|
4805
5026
|
const paneRange = axisInfo.max - axisInfo.min;
|
|
@@ -4829,17 +5050,28 @@ function createChart(element, options = {}) {
|
|
|
4829
5050
|
ctx.restore();
|
|
4830
5051
|
if (labels.visible) {
|
|
4831
5052
|
const prevFont = ctx.font;
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
const
|
|
5053
|
+
const legendFontSize = Math.max(8, axis.fontSize);
|
|
5054
|
+
ctx.font = `${legendFontSize}px ${mergedOptions.fontFamily}`;
|
|
5055
|
+
const legendTitle = paneInfo?.title ?? plugin.name;
|
|
5056
|
+
const legendValues = paneInfo?.legendValues ?? [];
|
|
4835
5057
|
const legendParts = [
|
|
4836
5058
|
legendTitle,
|
|
4837
5059
|
...legendValues.map((value) => value.text ?? (value.value === void 0 ? "" : formatPaneValue(value.value))).filter(Boolean)
|
|
4838
5060
|
].filter(Boolean);
|
|
5061
|
+
const legendText = legendParts.join(" ");
|
|
4839
5062
|
if (legendParts.length > 0) {
|
|
4840
|
-
drawText(
|
|
5063
|
+
drawText(legendText, chartLeft + 10, paneTop + 8, "left", "top", labels.indicatorTextColor);
|
|
4841
5064
|
}
|
|
4842
|
-
|
|
5065
|
+
if (hoveredPaneId === indicator.id) {
|
|
5066
|
+
drawPaneButtons(
|
|
5067
|
+
indicator.id,
|
|
5068
|
+
indicator.type,
|
|
5069
|
+
true,
|
|
5070
|
+
chartLeft + 10 + Math.ceil(measureTextWidth(legendText)) + 10,
|
|
5071
|
+
paneTop + 8 + legendFontSize / 2
|
|
5072
|
+
);
|
|
5073
|
+
}
|
|
5074
|
+
for (const label of paneInfo?.valueLabels ?? []) {
|
|
4843
5075
|
if (!labels.showIndicatorValueLabels) {
|
|
4844
5076
|
continue;
|
|
4845
5077
|
}
|
|
@@ -6051,6 +6283,54 @@ function createChart(element, options = {}) {
|
|
|
6051
6283
|
}
|
|
6052
6284
|
return "outside";
|
|
6053
6285
|
};
|
|
6286
|
+
const getPaneButtonHit = (x, y) => {
|
|
6287
|
+
for (const region of paneButtonRegions) {
|
|
6288
|
+
if (x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height) {
|
|
6289
|
+
return { id: region.id, type: region.type, action: region.action };
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
return null;
|
|
6293
|
+
};
|
|
6294
|
+
const getPaneAt = (x, y) => {
|
|
6295
|
+
if (!drawState || x < drawState.chartLeft || x > drawState.chartRight) {
|
|
6296
|
+
return null;
|
|
6297
|
+
}
|
|
6298
|
+
for (const pane of paneLayoutInfos) {
|
|
6299
|
+
if (y >= pane.top && y <= pane.bottom) {
|
|
6300
|
+
return pane;
|
|
6301
|
+
}
|
|
6302
|
+
}
|
|
6303
|
+
return null;
|
|
6304
|
+
};
|
|
6305
|
+
const getPaneDividerHit = (x, y) => {
|
|
6306
|
+
if (!drawState || x < drawState.chartLeft || x > drawState.chartRight) {
|
|
6307
|
+
return null;
|
|
6308
|
+
}
|
|
6309
|
+
for (const pane of paneLayoutInfos) {
|
|
6310
|
+
if (pane.visible && Math.abs(y - pane.top) <= 4) {
|
|
6311
|
+
return { id: pane.id, heightPx: pane.bottom - pane.top };
|
|
6312
|
+
}
|
|
6313
|
+
}
|
|
6314
|
+
return null;
|
|
6315
|
+
};
|
|
6316
|
+
const handlePaneButtonAction = (hit) => {
|
|
6317
|
+
const indicator = indicators.find((entry) => entry.id === hit.id);
|
|
6318
|
+
if (!indicator) {
|
|
6319
|
+
return;
|
|
6320
|
+
}
|
|
6321
|
+
if (hit.action === "visibility") {
|
|
6322
|
+
indicator.visible = !indicator.visible;
|
|
6323
|
+
scheduleDraw();
|
|
6324
|
+
indicatorPaneActionHandler?.({ id: hit.id, type: hit.type, action: "visibility", visible: indicator.visible });
|
|
6325
|
+
return;
|
|
6326
|
+
}
|
|
6327
|
+
if (hit.action === "remove" && !indicatorPaneActionHandler) {
|
|
6328
|
+
indicators = indicators.filter((entry) => entry.id !== hit.id);
|
|
6329
|
+
scheduleDraw();
|
|
6330
|
+
return;
|
|
6331
|
+
}
|
|
6332
|
+
indicatorPaneActionHandler?.({ id: hit.id, type: hit.type, action: hit.action });
|
|
6333
|
+
};
|
|
6054
6334
|
const handleDrawingToolPointerDown = (x, y) => {
|
|
6055
6335
|
if (!activeDrawingTool || !drawState) {
|
|
6056
6336
|
return false;
|
|
@@ -6530,6 +6810,26 @@ function createChart(element, options = {}) {
|
|
|
6530
6810
|
setCrosshairPoint(null);
|
|
6531
6811
|
return;
|
|
6532
6812
|
}
|
|
6813
|
+
const paneButtonHit = getPaneButtonHit(point.x, point.y);
|
|
6814
|
+
if (paneButtonHit) {
|
|
6815
|
+
setCrosshairPoint(null);
|
|
6816
|
+
handlePaneButtonAction(paneButtonHit);
|
|
6817
|
+
return;
|
|
6818
|
+
}
|
|
6819
|
+
const paneDividerHit = getPaneDividerHit(point.x, point.y);
|
|
6820
|
+
if (paneDividerHit) {
|
|
6821
|
+
activePointerId = event.pointerId;
|
|
6822
|
+
paneDividerDrag = {
|
|
6823
|
+
id: paneDividerHit.id,
|
|
6824
|
+
startY: point.y,
|
|
6825
|
+
startHeightPx: paneDividerHit.heightPx,
|
|
6826
|
+
lastRatio: paneLayoutFullChartHeight > 0 ? paneDividerHit.heightPx / paneLayoutFullChartHeight : 0.2
|
|
6827
|
+
};
|
|
6828
|
+
capturePointer(event.pointerId);
|
|
6829
|
+
canvas.style.cursor = "row-resize";
|
|
6830
|
+
setCrosshairPoint(null);
|
|
6831
|
+
return;
|
|
6832
|
+
}
|
|
6533
6833
|
}
|
|
6534
6834
|
const region = getHitRegion(point.x, point.y);
|
|
6535
6835
|
if (region === "outside") {
|
|
@@ -6654,6 +6954,32 @@ function createChart(element, options = {}) {
|
|
|
6654
6954
|
return;
|
|
6655
6955
|
}
|
|
6656
6956
|
}
|
|
6957
|
+
if (paneDividerDrag) {
|
|
6958
|
+
if (activePointerId !== null && event.pointerId !== activePointerId) {
|
|
6959
|
+
return;
|
|
6960
|
+
}
|
|
6961
|
+
const fullHeight = paneLayoutFullChartHeight;
|
|
6962
|
+
if (fullHeight > 0) {
|
|
6963
|
+
const nextHeightPx = clamp(
|
|
6964
|
+
paneDividerDrag.startHeightPx + (paneDividerDrag.startY - point.y),
|
|
6965
|
+
48,
|
|
6966
|
+
Math.round(fullHeight * 0.45)
|
|
6967
|
+
);
|
|
6968
|
+
const nextRatio = nextHeightPx / fullHeight;
|
|
6969
|
+
if (Math.abs(nextRatio - paneDividerDrag.lastRatio) > 5e-4) {
|
|
6970
|
+
paneDividerDrag.lastRatio = nextRatio;
|
|
6971
|
+
const targetIndicator = indicators.find((entry) => entry.id === paneDividerDrag?.id);
|
|
6972
|
+
if (targetIndicator) {
|
|
6973
|
+
targetIndicator.paneHeightRatio = nextRatio;
|
|
6974
|
+
indicatorPaneHeightChangeHandler?.({ id: paneDividerDrag.id, paneHeightRatio: nextRatio, dragging: true });
|
|
6975
|
+
scheduleDraw();
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
canvas.style.cursor = "row-resize";
|
|
6980
|
+
setCrosshairPoint(null);
|
|
6981
|
+
return;
|
|
6982
|
+
}
|
|
6657
6983
|
if (orderDragState) {
|
|
6658
6984
|
if (activePointerId !== null && event.pointerId !== activePointerId) {
|
|
6659
6985
|
return;
|
|
@@ -6726,6 +7052,33 @@ function createChart(element, options = {}) {
|
|
|
6726
7052
|
setCrosshairPoint(null);
|
|
6727
7053
|
return;
|
|
6728
7054
|
}
|
|
7055
|
+
const hoveredPane = getPaneAt(point.x, point.y);
|
|
7056
|
+
const nextHoveredPaneId = hoveredPane?.id ?? null;
|
|
7057
|
+
if (nextHoveredPaneId !== hoveredPaneId) {
|
|
7058
|
+
hoveredPaneId = nextHoveredPaneId;
|
|
7059
|
+
scheduleDraw();
|
|
7060
|
+
}
|
|
7061
|
+
const paneButtonHover = getPaneButtonHit(point.x, point.y);
|
|
7062
|
+
const nextHoveredButton = paneButtonHover ? { id: paneButtonHover.id, action: paneButtonHover.action } : null;
|
|
7063
|
+
if ((nextHoveredButton?.id ?? null) !== (hoveredPaneButton?.id ?? null) || nextHoveredButton?.action !== hoveredPaneButton?.action) {
|
|
7064
|
+
hoveredPaneButton = nextHoveredButton;
|
|
7065
|
+
scheduleDraw();
|
|
7066
|
+
}
|
|
7067
|
+
if (paneButtonHover) {
|
|
7068
|
+
const indicatorForTitle = indicators.find((entry) => entry.id === paneButtonHover.id);
|
|
7069
|
+
canvas.title = paneButtonHover.action === "visibility" ? indicatorForTitle?.visible === false ? "Show" : "Hide" : paneButtonHover.action === "settings" ? "Settings" : paneButtonHover.action === "source" ? "View source" : "Remove";
|
|
7070
|
+
canvas.style.cursor = "pointer";
|
|
7071
|
+
setCrosshairPoint(null);
|
|
7072
|
+
return;
|
|
7073
|
+
}
|
|
7074
|
+
if (canvas.title) {
|
|
7075
|
+
canvas.title = "";
|
|
7076
|
+
}
|
|
7077
|
+
if (getPaneDividerHit(point.x, point.y)) {
|
|
7078
|
+
canvas.style.cursor = "row-resize";
|
|
7079
|
+
setCrosshairPoint(null);
|
|
7080
|
+
return;
|
|
7081
|
+
}
|
|
6729
7082
|
}
|
|
6730
7083
|
if (!activeDrawingTool && getDrawingHit(point.x, point.y)) {
|
|
6731
7084
|
const drawingHit = getDrawingHit(point.x, point.y);
|
|
@@ -6845,6 +7198,22 @@ function createChart(element, options = {}) {
|
|
|
6845
7198
|
drawingDragState = null;
|
|
6846
7199
|
emitDrawingsChange();
|
|
6847
7200
|
}
|
|
7201
|
+
if (paneDividerDrag) {
|
|
7202
|
+
indicatorPaneHeightChangeHandler?.({
|
|
7203
|
+
id: paneDividerDrag.id,
|
|
7204
|
+
paneHeightRatio: paneDividerDrag.lastRatio,
|
|
7205
|
+
dragging: false
|
|
7206
|
+
});
|
|
7207
|
+
paneDividerDrag = null;
|
|
7208
|
+
}
|
|
7209
|
+
if (event?.type === "pointerleave" && (hoveredPaneId !== null || hoveredPaneButton !== null)) {
|
|
7210
|
+
hoveredPaneId = null;
|
|
7211
|
+
hoveredPaneButton = null;
|
|
7212
|
+
if (canvas.title) {
|
|
7213
|
+
canvas.title = "";
|
|
7214
|
+
}
|
|
7215
|
+
scheduleDraw();
|
|
7216
|
+
}
|
|
6848
7217
|
const endedPlotDrag = isDragging && dragMode === "plot";
|
|
6849
7218
|
isDragging = false;
|
|
6850
7219
|
dragMode = null;
|
|
@@ -7190,6 +7559,12 @@ function createChart(element, options = {}) {
|
|
|
7190
7559
|
const onCrosshairPriceAction = (handler) => {
|
|
7191
7560
|
crosshairPriceActionHandler = handler;
|
|
7192
7561
|
};
|
|
7562
|
+
const onIndicatorPaneAction = (handler) => {
|
|
7563
|
+
indicatorPaneActionHandler = handler;
|
|
7564
|
+
};
|
|
7565
|
+
const onIndicatorPaneHeightChange = (handler) => {
|
|
7566
|
+
indicatorPaneHeightChangeHandler = handler;
|
|
7567
|
+
};
|
|
7193
7568
|
const setDoubleClickEnabled = (enabled) => {
|
|
7194
7569
|
doubleClickEnabled = enabled;
|
|
7195
7570
|
};
|
|
@@ -7395,6 +7770,8 @@ function createChart(element, options = {}) {
|
|
|
7395
7770
|
onChartClick,
|
|
7396
7771
|
onCrosshairMove,
|
|
7397
7772
|
onCrosshairPriceAction,
|
|
7773
|
+
onIndicatorPaneAction,
|
|
7774
|
+
onIndicatorPaneHeightChange,
|
|
7398
7775
|
zoomInX,
|
|
7399
7776
|
zoomOutX,
|
|
7400
7777
|
zoomInY,
|