hyperprop-charting-library 0.1.123 → 0.1.125
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 +75 -7
- package/dist/hyperprop-charting-library.js +75 -7
- package/dist/index.cjs +75 -7
- package/dist/index.js +75 -7
- package/package.json +1 -1
|
@@ -303,6 +303,16 @@ var getIndicatorSourceValue = (point, source) => {
|
|
|
303
303
|
var clampIndicatorLength = (value, fallback) => {
|
|
304
304
|
return Math.max(1, Math.round(Number(value) || fallback));
|
|
305
305
|
};
|
|
306
|
+
var isFullyWhiteColor = (value) => {
|
|
307
|
+
if (!value) return false;
|
|
308
|
+
const normalized = value.trim().toLowerCase();
|
|
309
|
+
if (normalized === "white" || normalized === "#fff" || normalized === "#ffffff") return true;
|
|
310
|
+
const rgb = /^rgba?\(\s*255\s*,\s*255\s*,\s*255\s*(?:,\s*[\d.]+\s*)?\)$/.exec(normalized);
|
|
311
|
+
return rgb !== null;
|
|
312
|
+
};
|
|
313
|
+
var labelTextColorOn = (background, textColor) => {
|
|
314
|
+
return isFullyWhiteColor(background) ? "#000000" : textColor;
|
|
315
|
+
};
|
|
306
316
|
var computeSmaSeries = (data, length, source) => {
|
|
307
317
|
const result = new Array(data.length).fill(null);
|
|
308
318
|
let sum = 0;
|
|
@@ -1364,7 +1374,7 @@ function createChart(element, options = {}) {
|
|
|
1364
1374
|
return;
|
|
1365
1375
|
}
|
|
1366
1376
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1367
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1377
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1368
1378
|
const dt = 1 / 60;
|
|
1369
1379
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1370
1380
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1801,6 +1811,56 @@ function createChart(element, options = {}) {
|
|
|
1801
1811
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1802
1812
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1803
1813
|
};
|
|
1814
|
+
const indexForTimeMs = (timeMs) => {
|
|
1815
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1816
|
+
return null;
|
|
1817
|
+
}
|
|
1818
|
+
const firstMs = data[0].time.getTime();
|
|
1819
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1820
|
+
if (timeMs < firstMs) {
|
|
1821
|
+
const stepMs = getTimeStepMs();
|
|
1822
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1823
|
+
}
|
|
1824
|
+
if (timeMs > lastMs) {
|
|
1825
|
+
const stepMs = getTimeStepMs();
|
|
1826
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1827
|
+
}
|
|
1828
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1829
|
+
};
|
|
1830
|
+
const reanchorDrawingPoint = (point) => {
|
|
1831
|
+
if (!point.time) {
|
|
1832
|
+
return point;
|
|
1833
|
+
}
|
|
1834
|
+
const timeMs = Date.parse(point.time);
|
|
1835
|
+
if (!Number.isFinite(timeMs)) {
|
|
1836
|
+
return point;
|
|
1837
|
+
}
|
|
1838
|
+
const rounded = Math.round(point.index);
|
|
1839
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1840
|
+
return point;
|
|
1841
|
+
}
|
|
1842
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1843
|
+
if (mapped === null) {
|
|
1844
|
+
return point;
|
|
1845
|
+
}
|
|
1846
|
+
const fraction = point.index - rounded;
|
|
1847
|
+
return { ...point, index: mapped + fraction };
|
|
1848
|
+
};
|
|
1849
|
+
const reanchorDrawingsToData = () => {
|
|
1850
|
+
if (data.length === 0) {
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
drawings = drawings.map((drawing) => ({
|
|
1854
|
+
...drawing,
|
|
1855
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1856
|
+
}));
|
|
1857
|
+
if (draftDrawing) {
|
|
1858
|
+
draftDrawing = {
|
|
1859
|
+
...draftDrawing,
|
|
1860
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
};
|
|
1804
1864
|
const getCandleDirectionByIndex = (index) => {
|
|
1805
1865
|
const point = data[index];
|
|
1806
1866
|
if (!point) {
|
|
@@ -1933,7 +1993,7 @@ function createChart(element, options = {}) {
|
|
|
1933
1993
|
labelY + labelHeight / 2,
|
|
1934
1994
|
"left",
|
|
1935
1995
|
"middle",
|
|
1936
|
-
mergedLine.labelTextColor
|
|
1996
|
+
labelTextColorOn(mergedLine.labelBackgroundColor, mergedLine.labelTextColor)
|
|
1937
1997
|
);
|
|
1938
1998
|
};
|
|
1939
1999
|
const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
@@ -2073,7 +2133,10 @@ function createChart(element, options = {}) {
|
|
|
2073
2133
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
2074
2134
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
2075
2135
|
const widgetBorder = color;
|
|
2076
|
-
const textColor =
|
|
2136
|
+
const textColor = labelTextColorOn(
|
|
2137
|
+
widgetBackground,
|
|
2138
|
+
line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor)
|
|
2139
|
+
);
|
|
2077
2140
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
2078
2141
|
ctx.fillStyle = widgetBackground;
|
|
2079
2142
|
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
@@ -3825,7 +3888,8 @@ function createChart(element, options = {}) {
|
|
|
3825
3888
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
3826
3889
|
const hasSubtexts = label.subtexts.length > 0;
|
|
3827
3890
|
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
3828
|
-
|
|
3891
|
+
const effectiveTextColor = labelTextColorOn(label.backgroundColor, label.textColor);
|
|
3892
|
+
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", effectiveTextColor);
|
|
3829
3893
|
if (hasSubtexts) {
|
|
3830
3894
|
const baseFont = ctx.font;
|
|
3831
3895
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -3836,7 +3900,7 @@ function createChart(element, options = {}) {
|
|
|
3836
3900
|
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
3837
3901
|
"left",
|
|
3838
3902
|
"middle",
|
|
3839
|
-
label.subtextColor ?? label.textColor
|
|
3903
|
+
labelTextColorOn(label.backgroundColor, label.subtextColor ?? label.textColor)
|
|
3840
3904
|
);
|
|
3841
3905
|
});
|
|
3842
3906
|
ctx.font = baseFont;
|
|
@@ -3993,7 +4057,7 @@ function createChart(element, options = {}) {
|
|
|
3993
4057
|
const labelHeight = 20;
|
|
3994
4058
|
const labelRadius = Math.max(0, crosshair.labelBorderRadius);
|
|
3995
4059
|
const labelBackground = crosshair.labelBackgroundColor;
|
|
3996
|
-
const labelTextColor = crosshair.labelTextColor;
|
|
4060
|
+
const labelTextColor = labelTextColorOn(labelBackground, crosshair.labelTextColor);
|
|
3997
4061
|
const labelBorderColor = crosshair.labelBorderColor;
|
|
3998
4062
|
const labelBorderWidth = Math.max(0, crosshair.labelBorderWidth);
|
|
3999
4063
|
const labelBorderStyle = crosshair.labelBorderStyle;
|
|
@@ -5785,6 +5849,7 @@ function createChart(element, options = {}) {
|
|
|
5785
5849
|
fitXViewport();
|
|
5786
5850
|
}
|
|
5787
5851
|
}
|
|
5852
|
+
reanchorDrawingsToData();
|
|
5788
5853
|
const lastPoint = data[data.length - 1];
|
|
5789
5854
|
if (lastPoint) {
|
|
5790
5855
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5831,6 +5896,7 @@ function createChart(element, options = {}) {
|
|
|
5831
5896
|
}
|
|
5832
5897
|
merged.set(timeMs, parsed);
|
|
5833
5898
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5899
|
+
reanchorDrawingsToData();
|
|
5834
5900
|
}
|
|
5835
5901
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5836
5902
|
scheduleDraw();
|
|
@@ -6013,11 +6079,13 @@ function createChart(element, options = {}) {
|
|
|
6013
6079
|
const setDrawings = (nextDrawings) => {
|
|
6014
6080
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
6015
6081
|
draftDrawing = null;
|
|
6082
|
+
reanchorDrawingsToData();
|
|
6016
6083
|
emitDrawingsChange();
|
|
6017
6084
|
scheduleDraw();
|
|
6018
6085
|
};
|
|
6019
6086
|
const addDrawing = (drawing) => {
|
|
6020
6087
|
const next = normalizeDrawingState(drawing);
|
|
6088
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
6021
6089
|
drawings.push(next);
|
|
6022
6090
|
emitDrawingsChange();
|
|
6023
6091
|
scheduleDraw();
|
|
@@ -6029,7 +6097,7 @@ function createChart(element, options = {}) {
|
|
|
6029
6097
|
...serializeDrawing(drawing),
|
|
6030
6098
|
...patch,
|
|
6031
6099
|
id,
|
|
6032
|
-
points: patch.points ?? drawing.points
|
|
6100
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6033
6101
|
}) : drawing
|
|
6034
6102
|
);
|
|
6035
6103
|
emitDrawingsChange();
|
|
@@ -277,6 +277,16 @@ var getIndicatorSourceValue = (point, source) => {
|
|
|
277
277
|
var clampIndicatorLength = (value, fallback) => {
|
|
278
278
|
return Math.max(1, Math.round(Number(value) || fallback));
|
|
279
279
|
};
|
|
280
|
+
var isFullyWhiteColor = (value) => {
|
|
281
|
+
if (!value) return false;
|
|
282
|
+
const normalized = value.trim().toLowerCase();
|
|
283
|
+
if (normalized === "white" || normalized === "#fff" || normalized === "#ffffff") return true;
|
|
284
|
+
const rgb = /^rgba?\(\s*255\s*,\s*255\s*,\s*255\s*(?:,\s*[\d.]+\s*)?\)$/.exec(normalized);
|
|
285
|
+
return rgb !== null;
|
|
286
|
+
};
|
|
287
|
+
var labelTextColorOn = (background, textColor) => {
|
|
288
|
+
return isFullyWhiteColor(background) ? "#000000" : textColor;
|
|
289
|
+
};
|
|
280
290
|
var computeSmaSeries = (data, length, source) => {
|
|
281
291
|
const result = new Array(data.length).fill(null);
|
|
282
292
|
let sum = 0;
|
|
@@ -1338,7 +1348,7 @@ function createChart(element, options = {}) {
|
|
|
1338
1348
|
return;
|
|
1339
1349
|
}
|
|
1340
1350
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1341
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1351
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1342
1352
|
const dt = 1 / 60;
|
|
1343
1353
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1344
1354
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1775,6 +1785,56 @@ function createChart(element, options = {}) {
|
|
|
1775
1785
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1776
1786
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1777
1787
|
};
|
|
1788
|
+
const indexForTimeMs = (timeMs) => {
|
|
1789
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1790
|
+
return null;
|
|
1791
|
+
}
|
|
1792
|
+
const firstMs = data[0].time.getTime();
|
|
1793
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1794
|
+
if (timeMs < firstMs) {
|
|
1795
|
+
const stepMs = getTimeStepMs();
|
|
1796
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1797
|
+
}
|
|
1798
|
+
if (timeMs > lastMs) {
|
|
1799
|
+
const stepMs = getTimeStepMs();
|
|
1800
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1801
|
+
}
|
|
1802
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1803
|
+
};
|
|
1804
|
+
const reanchorDrawingPoint = (point) => {
|
|
1805
|
+
if (!point.time) {
|
|
1806
|
+
return point;
|
|
1807
|
+
}
|
|
1808
|
+
const timeMs = Date.parse(point.time);
|
|
1809
|
+
if (!Number.isFinite(timeMs)) {
|
|
1810
|
+
return point;
|
|
1811
|
+
}
|
|
1812
|
+
const rounded = Math.round(point.index);
|
|
1813
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1814
|
+
return point;
|
|
1815
|
+
}
|
|
1816
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1817
|
+
if (mapped === null) {
|
|
1818
|
+
return point;
|
|
1819
|
+
}
|
|
1820
|
+
const fraction = point.index - rounded;
|
|
1821
|
+
return { ...point, index: mapped + fraction };
|
|
1822
|
+
};
|
|
1823
|
+
const reanchorDrawingsToData = () => {
|
|
1824
|
+
if (data.length === 0) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
drawings = drawings.map((drawing) => ({
|
|
1828
|
+
...drawing,
|
|
1829
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1830
|
+
}));
|
|
1831
|
+
if (draftDrawing) {
|
|
1832
|
+
draftDrawing = {
|
|
1833
|
+
...draftDrawing,
|
|
1834
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1778
1838
|
const getCandleDirectionByIndex = (index) => {
|
|
1779
1839
|
const point = data[index];
|
|
1780
1840
|
if (!point) {
|
|
@@ -1907,7 +1967,7 @@ function createChart(element, options = {}) {
|
|
|
1907
1967
|
labelY + labelHeight / 2,
|
|
1908
1968
|
"left",
|
|
1909
1969
|
"middle",
|
|
1910
|
-
mergedLine.labelTextColor
|
|
1970
|
+
labelTextColorOn(mergedLine.labelBackgroundColor, mergedLine.labelTextColor)
|
|
1911
1971
|
);
|
|
1912
1972
|
};
|
|
1913
1973
|
const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
@@ -2047,7 +2107,10 @@ function createChart(element, options = {}) {
|
|
|
2047
2107
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
2048
2108
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
2049
2109
|
const widgetBorder = color;
|
|
2050
|
-
const textColor =
|
|
2110
|
+
const textColor = labelTextColorOn(
|
|
2111
|
+
widgetBackground,
|
|
2112
|
+
line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor)
|
|
2113
|
+
);
|
|
2051
2114
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
2052
2115
|
ctx.fillStyle = widgetBackground;
|
|
2053
2116
|
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
@@ -3799,7 +3862,8 @@ function createChart(element, options = {}) {
|
|
|
3799
3862
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
3800
3863
|
const hasSubtexts = label.subtexts.length > 0;
|
|
3801
3864
|
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
3802
|
-
|
|
3865
|
+
const effectiveTextColor = labelTextColorOn(label.backgroundColor, label.textColor);
|
|
3866
|
+
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", effectiveTextColor);
|
|
3803
3867
|
if (hasSubtexts) {
|
|
3804
3868
|
const baseFont = ctx.font;
|
|
3805
3869
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -3810,7 +3874,7 @@ function createChart(element, options = {}) {
|
|
|
3810
3874
|
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
3811
3875
|
"left",
|
|
3812
3876
|
"middle",
|
|
3813
|
-
label.subtextColor ?? label.textColor
|
|
3877
|
+
labelTextColorOn(label.backgroundColor, label.subtextColor ?? label.textColor)
|
|
3814
3878
|
);
|
|
3815
3879
|
});
|
|
3816
3880
|
ctx.font = baseFont;
|
|
@@ -3967,7 +4031,7 @@ function createChart(element, options = {}) {
|
|
|
3967
4031
|
const labelHeight = 20;
|
|
3968
4032
|
const labelRadius = Math.max(0, crosshair.labelBorderRadius);
|
|
3969
4033
|
const labelBackground = crosshair.labelBackgroundColor;
|
|
3970
|
-
const labelTextColor = crosshair.labelTextColor;
|
|
4034
|
+
const labelTextColor = labelTextColorOn(labelBackground, crosshair.labelTextColor);
|
|
3971
4035
|
const labelBorderColor = crosshair.labelBorderColor;
|
|
3972
4036
|
const labelBorderWidth = Math.max(0, crosshair.labelBorderWidth);
|
|
3973
4037
|
const labelBorderStyle = crosshair.labelBorderStyle;
|
|
@@ -5759,6 +5823,7 @@ function createChart(element, options = {}) {
|
|
|
5759
5823
|
fitXViewport();
|
|
5760
5824
|
}
|
|
5761
5825
|
}
|
|
5826
|
+
reanchorDrawingsToData();
|
|
5762
5827
|
const lastPoint = data[data.length - 1];
|
|
5763
5828
|
if (lastPoint) {
|
|
5764
5829
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5805,6 +5870,7 @@ function createChart(element, options = {}) {
|
|
|
5805
5870
|
}
|
|
5806
5871
|
merged.set(timeMs, parsed);
|
|
5807
5872
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5873
|
+
reanchorDrawingsToData();
|
|
5808
5874
|
}
|
|
5809
5875
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5810
5876
|
scheduleDraw();
|
|
@@ -5987,11 +6053,13 @@ function createChart(element, options = {}) {
|
|
|
5987
6053
|
const setDrawings = (nextDrawings) => {
|
|
5988
6054
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
5989
6055
|
draftDrawing = null;
|
|
6056
|
+
reanchorDrawingsToData();
|
|
5990
6057
|
emitDrawingsChange();
|
|
5991
6058
|
scheduleDraw();
|
|
5992
6059
|
};
|
|
5993
6060
|
const addDrawing = (drawing) => {
|
|
5994
6061
|
const next = normalizeDrawingState(drawing);
|
|
6062
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
5995
6063
|
drawings.push(next);
|
|
5996
6064
|
emitDrawingsChange();
|
|
5997
6065
|
scheduleDraw();
|
|
@@ -6003,7 +6071,7 @@ function createChart(element, options = {}) {
|
|
|
6003
6071
|
...serializeDrawing(drawing),
|
|
6004
6072
|
...patch,
|
|
6005
6073
|
id,
|
|
6006
|
-
points: patch.points ?? drawing.points
|
|
6074
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6007
6075
|
}) : drawing
|
|
6008
6076
|
);
|
|
6009
6077
|
emitDrawingsChange();
|
package/dist/index.cjs
CHANGED
|
@@ -303,6 +303,16 @@ var getIndicatorSourceValue = (point, source) => {
|
|
|
303
303
|
var clampIndicatorLength = (value, fallback) => {
|
|
304
304
|
return Math.max(1, Math.round(Number(value) || fallback));
|
|
305
305
|
};
|
|
306
|
+
var isFullyWhiteColor = (value) => {
|
|
307
|
+
if (!value) return false;
|
|
308
|
+
const normalized = value.trim().toLowerCase();
|
|
309
|
+
if (normalized === "white" || normalized === "#fff" || normalized === "#ffffff") return true;
|
|
310
|
+
const rgb = /^rgba?\(\s*255\s*,\s*255\s*,\s*255\s*(?:,\s*[\d.]+\s*)?\)$/.exec(normalized);
|
|
311
|
+
return rgb !== null;
|
|
312
|
+
};
|
|
313
|
+
var labelTextColorOn = (background, textColor) => {
|
|
314
|
+
return isFullyWhiteColor(background) ? "#000000" : textColor;
|
|
315
|
+
};
|
|
306
316
|
var computeSmaSeries = (data, length, source) => {
|
|
307
317
|
const result = new Array(data.length).fill(null);
|
|
308
318
|
let sum = 0;
|
|
@@ -1364,7 +1374,7 @@ function createChart(element, options = {}) {
|
|
|
1364
1374
|
return;
|
|
1365
1375
|
}
|
|
1366
1376
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1367
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1377
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1368
1378
|
const dt = 1 / 60;
|
|
1369
1379
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1370
1380
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1801,6 +1811,56 @@ function createChart(element, options = {}) {
|
|
|
1801
1811
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1802
1812
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1803
1813
|
};
|
|
1814
|
+
const indexForTimeMs = (timeMs) => {
|
|
1815
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1816
|
+
return null;
|
|
1817
|
+
}
|
|
1818
|
+
const firstMs = data[0].time.getTime();
|
|
1819
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1820
|
+
if (timeMs < firstMs) {
|
|
1821
|
+
const stepMs = getTimeStepMs();
|
|
1822
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1823
|
+
}
|
|
1824
|
+
if (timeMs > lastMs) {
|
|
1825
|
+
const stepMs = getTimeStepMs();
|
|
1826
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1827
|
+
}
|
|
1828
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1829
|
+
};
|
|
1830
|
+
const reanchorDrawingPoint = (point) => {
|
|
1831
|
+
if (!point.time) {
|
|
1832
|
+
return point;
|
|
1833
|
+
}
|
|
1834
|
+
const timeMs = Date.parse(point.time);
|
|
1835
|
+
if (!Number.isFinite(timeMs)) {
|
|
1836
|
+
return point;
|
|
1837
|
+
}
|
|
1838
|
+
const rounded = Math.round(point.index);
|
|
1839
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1840
|
+
return point;
|
|
1841
|
+
}
|
|
1842
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1843
|
+
if (mapped === null) {
|
|
1844
|
+
return point;
|
|
1845
|
+
}
|
|
1846
|
+
const fraction = point.index - rounded;
|
|
1847
|
+
return { ...point, index: mapped + fraction };
|
|
1848
|
+
};
|
|
1849
|
+
const reanchorDrawingsToData = () => {
|
|
1850
|
+
if (data.length === 0) {
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
drawings = drawings.map((drawing) => ({
|
|
1854
|
+
...drawing,
|
|
1855
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1856
|
+
}));
|
|
1857
|
+
if (draftDrawing) {
|
|
1858
|
+
draftDrawing = {
|
|
1859
|
+
...draftDrawing,
|
|
1860
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
};
|
|
1804
1864
|
const getCandleDirectionByIndex = (index) => {
|
|
1805
1865
|
const point = data[index];
|
|
1806
1866
|
if (!point) {
|
|
@@ -1933,7 +1993,7 @@ function createChart(element, options = {}) {
|
|
|
1933
1993
|
labelY + labelHeight / 2,
|
|
1934
1994
|
"left",
|
|
1935
1995
|
"middle",
|
|
1936
|
-
mergedLine.labelTextColor
|
|
1996
|
+
labelTextColorOn(mergedLine.labelBackgroundColor, mergedLine.labelTextColor)
|
|
1937
1997
|
);
|
|
1938
1998
|
};
|
|
1939
1999
|
const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
@@ -2073,7 +2133,10 @@ function createChart(element, options = {}) {
|
|
|
2073
2133
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
2074
2134
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
2075
2135
|
const widgetBorder = color;
|
|
2076
|
-
const textColor =
|
|
2136
|
+
const textColor = labelTextColorOn(
|
|
2137
|
+
widgetBackground,
|
|
2138
|
+
line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor)
|
|
2139
|
+
);
|
|
2077
2140
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
2078
2141
|
ctx.fillStyle = widgetBackground;
|
|
2079
2142
|
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
@@ -3825,7 +3888,8 @@ function createChart(element, options = {}) {
|
|
|
3825
3888
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
3826
3889
|
const hasSubtexts = label.subtexts.length > 0;
|
|
3827
3890
|
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
3828
|
-
|
|
3891
|
+
const effectiveTextColor = labelTextColorOn(label.backgroundColor, label.textColor);
|
|
3892
|
+
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", effectiveTextColor);
|
|
3829
3893
|
if (hasSubtexts) {
|
|
3830
3894
|
const baseFont = ctx.font;
|
|
3831
3895
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -3836,7 +3900,7 @@ function createChart(element, options = {}) {
|
|
|
3836
3900
|
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
3837
3901
|
"left",
|
|
3838
3902
|
"middle",
|
|
3839
|
-
label.subtextColor ?? label.textColor
|
|
3903
|
+
labelTextColorOn(label.backgroundColor, label.subtextColor ?? label.textColor)
|
|
3840
3904
|
);
|
|
3841
3905
|
});
|
|
3842
3906
|
ctx.font = baseFont;
|
|
@@ -3993,7 +4057,7 @@ function createChart(element, options = {}) {
|
|
|
3993
4057
|
const labelHeight = 20;
|
|
3994
4058
|
const labelRadius = Math.max(0, crosshair.labelBorderRadius);
|
|
3995
4059
|
const labelBackground = crosshair.labelBackgroundColor;
|
|
3996
|
-
const labelTextColor = crosshair.labelTextColor;
|
|
4060
|
+
const labelTextColor = labelTextColorOn(labelBackground, crosshair.labelTextColor);
|
|
3997
4061
|
const labelBorderColor = crosshair.labelBorderColor;
|
|
3998
4062
|
const labelBorderWidth = Math.max(0, crosshair.labelBorderWidth);
|
|
3999
4063
|
const labelBorderStyle = crosshair.labelBorderStyle;
|
|
@@ -5785,6 +5849,7 @@ function createChart(element, options = {}) {
|
|
|
5785
5849
|
fitXViewport();
|
|
5786
5850
|
}
|
|
5787
5851
|
}
|
|
5852
|
+
reanchorDrawingsToData();
|
|
5788
5853
|
const lastPoint = data[data.length - 1];
|
|
5789
5854
|
if (lastPoint) {
|
|
5790
5855
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5831,6 +5896,7 @@ function createChart(element, options = {}) {
|
|
|
5831
5896
|
}
|
|
5832
5897
|
merged.set(timeMs, parsed);
|
|
5833
5898
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5899
|
+
reanchorDrawingsToData();
|
|
5834
5900
|
}
|
|
5835
5901
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5836
5902
|
scheduleDraw();
|
|
@@ -6013,11 +6079,13 @@ function createChart(element, options = {}) {
|
|
|
6013
6079
|
const setDrawings = (nextDrawings) => {
|
|
6014
6080
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
6015
6081
|
draftDrawing = null;
|
|
6082
|
+
reanchorDrawingsToData();
|
|
6016
6083
|
emitDrawingsChange();
|
|
6017
6084
|
scheduleDraw();
|
|
6018
6085
|
};
|
|
6019
6086
|
const addDrawing = (drawing) => {
|
|
6020
6087
|
const next = normalizeDrawingState(drawing);
|
|
6088
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
6021
6089
|
drawings.push(next);
|
|
6022
6090
|
emitDrawingsChange();
|
|
6023
6091
|
scheduleDraw();
|
|
@@ -6029,7 +6097,7 @@ function createChart(element, options = {}) {
|
|
|
6029
6097
|
...serializeDrawing(drawing),
|
|
6030
6098
|
...patch,
|
|
6031
6099
|
id,
|
|
6032
|
-
points: patch.points ?? drawing.points
|
|
6100
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6033
6101
|
}) : drawing
|
|
6034
6102
|
);
|
|
6035
6103
|
emitDrawingsChange();
|
package/dist/index.js
CHANGED
|
@@ -277,6 +277,16 @@ var getIndicatorSourceValue = (point, source) => {
|
|
|
277
277
|
var clampIndicatorLength = (value, fallback) => {
|
|
278
278
|
return Math.max(1, Math.round(Number(value) || fallback));
|
|
279
279
|
};
|
|
280
|
+
var isFullyWhiteColor = (value) => {
|
|
281
|
+
if (!value) return false;
|
|
282
|
+
const normalized = value.trim().toLowerCase();
|
|
283
|
+
if (normalized === "white" || normalized === "#fff" || normalized === "#ffffff") return true;
|
|
284
|
+
const rgb = /^rgba?\(\s*255\s*,\s*255\s*,\s*255\s*(?:,\s*[\d.]+\s*)?\)$/.exec(normalized);
|
|
285
|
+
return rgb !== null;
|
|
286
|
+
};
|
|
287
|
+
var labelTextColorOn = (background, textColor) => {
|
|
288
|
+
return isFullyWhiteColor(background) ? "#000000" : textColor;
|
|
289
|
+
};
|
|
280
290
|
var computeSmaSeries = (data, length, source) => {
|
|
281
291
|
const result = new Array(data.length).fill(null);
|
|
282
292
|
let sum = 0;
|
|
@@ -1338,7 +1348,7 @@ function createChart(element, options = {}) {
|
|
|
1338
1348
|
return;
|
|
1339
1349
|
}
|
|
1340
1350
|
const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1341
|
-
const speed = clamp(tickerOpts.smoothingSpeed ??
|
|
1351
|
+
const speed = clamp(tickerOpts.smoothingSpeed ?? 4, 1, 60);
|
|
1342
1352
|
const dt = 1 / 60;
|
|
1343
1353
|
const lerp = 1 - Math.exp(-speed * dt);
|
|
1344
1354
|
smoothedTickerPrice += priceDiff * lerp;
|
|
@@ -1775,6 +1785,56 @@ function createChart(element, options = {}) {
|
|
|
1775
1785
|
const upperDelta = Math.abs(upperPoint.time.getTime() - timeMs);
|
|
1776
1786
|
return lowerDelta <= upperDelta ? lower : upper;
|
|
1777
1787
|
};
|
|
1788
|
+
const indexForTimeMs = (timeMs) => {
|
|
1789
|
+
if (!Number.isFinite(timeMs) || data.length === 0) {
|
|
1790
|
+
return null;
|
|
1791
|
+
}
|
|
1792
|
+
const firstMs = data[0].time.getTime();
|
|
1793
|
+
const lastMs = data[data.length - 1].time.getTime();
|
|
1794
|
+
if (timeMs < firstMs) {
|
|
1795
|
+
const stepMs = getTimeStepMs();
|
|
1796
|
+
return stepMs > 0 ? (timeMs - firstMs) / stepMs : 0;
|
|
1797
|
+
}
|
|
1798
|
+
if (timeMs > lastMs) {
|
|
1799
|
+
const stepMs = getTimeStepMs();
|
|
1800
|
+
return data.length - 1 + (stepMs > 0 ? (timeMs - lastMs) / stepMs : 0);
|
|
1801
|
+
}
|
|
1802
|
+
return findNearestIndexForTimeMs(timeMs);
|
|
1803
|
+
};
|
|
1804
|
+
const reanchorDrawingPoint = (point) => {
|
|
1805
|
+
if (!point.time) {
|
|
1806
|
+
return point;
|
|
1807
|
+
}
|
|
1808
|
+
const timeMs = Date.parse(point.time);
|
|
1809
|
+
if (!Number.isFinite(timeMs)) {
|
|
1810
|
+
return point;
|
|
1811
|
+
}
|
|
1812
|
+
const rounded = Math.round(point.index);
|
|
1813
|
+
if (data[rounded]?.time.getTime() === timeMs) {
|
|
1814
|
+
return point;
|
|
1815
|
+
}
|
|
1816
|
+
const mapped = indexForTimeMs(timeMs);
|
|
1817
|
+
if (mapped === null) {
|
|
1818
|
+
return point;
|
|
1819
|
+
}
|
|
1820
|
+
const fraction = point.index - rounded;
|
|
1821
|
+
return { ...point, index: mapped + fraction };
|
|
1822
|
+
};
|
|
1823
|
+
const reanchorDrawingsToData = () => {
|
|
1824
|
+
if (data.length === 0) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
drawings = drawings.map((drawing) => ({
|
|
1828
|
+
...drawing,
|
|
1829
|
+
points: drawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1830
|
+
}));
|
|
1831
|
+
if (draftDrawing) {
|
|
1832
|
+
draftDrawing = {
|
|
1833
|
+
...draftDrawing,
|
|
1834
|
+
points: draftDrawing.points.map((point) => reanchorDrawingPoint(point))
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1778
1838
|
const getCandleDirectionByIndex = (index) => {
|
|
1779
1839
|
const point = data[index];
|
|
1780
1840
|
if (!point) {
|
|
@@ -1907,7 +1967,7 @@ function createChart(element, options = {}) {
|
|
|
1907
1967
|
labelY + labelHeight / 2,
|
|
1908
1968
|
"left",
|
|
1909
1969
|
"middle",
|
|
1910
|
-
mergedLine.labelTextColor
|
|
1970
|
+
labelTextColorOn(mergedLine.labelBackgroundColor, mergedLine.labelTextColor)
|
|
1911
1971
|
);
|
|
1912
1972
|
};
|
|
1913
1973
|
const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
@@ -2047,7 +2107,10 @@ function createChart(element, options = {}) {
|
|
|
2047
2107
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
2048
2108
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
2049
2109
|
const widgetBorder = color;
|
|
2050
|
-
const textColor =
|
|
2110
|
+
const textColor = labelTextColorOn(
|
|
2111
|
+
widgetBackground,
|
|
2112
|
+
line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor)
|
|
2113
|
+
);
|
|
2051
2114
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
2052
2115
|
ctx.fillStyle = widgetBackground;
|
|
2053
2116
|
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
@@ -3799,7 +3862,8 @@ function createChart(element, options = {}) {
|
|
|
3799
3862
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
3800
3863
|
const hasSubtexts = label.subtexts.length > 0;
|
|
3801
3864
|
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
3802
|
-
|
|
3865
|
+
const effectiveTextColor = labelTextColorOn(label.backgroundColor, label.textColor);
|
|
3866
|
+
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", effectiveTextColor);
|
|
3803
3867
|
if (hasSubtexts) {
|
|
3804
3868
|
const baseFont = ctx.font;
|
|
3805
3869
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -3810,7 +3874,7 @@ function createChart(element, options = {}) {
|
|
|
3810
3874
|
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
3811
3875
|
"left",
|
|
3812
3876
|
"middle",
|
|
3813
|
-
label.subtextColor ?? label.textColor
|
|
3877
|
+
labelTextColorOn(label.backgroundColor, label.subtextColor ?? label.textColor)
|
|
3814
3878
|
);
|
|
3815
3879
|
});
|
|
3816
3880
|
ctx.font = baseFont;
|
|
@@ -3967,7 +4031,7 @@ function createChart(element, options = {}) {
|
|
|
3967
4031
|
const labelHeight = 20;
|
|
3968
4032
|
const labelRadius = Math.max(0, crosshair.labelBorderRadius);
|
|
3969
4033
|
const labelBackground = crosshair.labelBackgroundColor;
|
|
3970
|
-
const labelTextColor = crosshair.labelTextColor;
|
|
4034
|
+
const labelTextColor = labelTextColorOn(labelBackground, crosshair.labelTextColor);
|
|
3971
4035
|
const labelBorderColor = crosshair.labelBorderColor;
|
|
3972
4036
|
const labelBorderWidth = Math.max(0, crosshair.labelBorderWidth);
|
|
3973
4037
|
const labelBorderStyle = crosshair.labelBorderStyle;
|
|
@@ -5759,6 +5823,7 @@ function createChart(element, options = {}) {
|
|
|
5759
5823
|
fitXViewport();
|
|
5760
5824
|
}
|
|
5761
5825
|
}
|
|
5826
|
+
reanchorDrawingsToData();
|
|
5762
5827
|
const lastPoint = data[data.length - 1];
|
|
5763
5828
|
if (lastPoint) {
|
|
5764
5829
|
pushSmoothedTicker(lastPoint.c, lastPoint.v);
|
|
@@ -5805,6 +5870,7 @@ function createChart(element, options = {}) {
|
|
|
5805
5870
|
}
|
|
5806
5871
|
merged.set(timeMs, parsed);
|
|
5807
5872
|
data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
5873
|
+
reanchorDrawingsToData();
|
|
5808
5874
|
}
|
|
5809
5875
|
pushSmoothedTicker(parsed.c, parsed.v);
|
|
5810
5876
|
scheduleDraw();
|
|
@@ -5987,11 +6053,13 @@ function createChart(element, options = {}) {
|
|
|
5987
6053
|
const setDrawings = (nextDrawings) => {
|
|
5988
6054
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
5989
6055
|
draftDrawing = null;
|
|
6056
|
+
reanchorDrawingsToData();
|
|
5990
6057
|
emitDrawingsChange();
|
|
5991
6058
|
scheduleDraw();
|
|
5992
6059
|
};
|
|
5993
6060
|
const addDrawing = (drawing) => {
|
|
5994
6061
|
const next = normalizeDrawingState(drawing);
|
|
6062
|
+
next.points = next.points.map((point) => reanchorDrawingPoint(point));
|
|
5995
6063
|
drawings.push(next);
|
|
5996
6064
|
emitDrawingsChange();
|
|
5997
6065
|
scheduleDraw();
|
|
@@ -6003,7 +6071,7 @@ function createChart(element, options = {}) {
|
|
|
6003
6071
|
...serializeDrawing(drawing),
|
|
6004
6072
|
...patch,
|
|
6005
6073
|
id,
|
|
6006
|
-
points: patch.points ?? drawing.points
|
|
6074
|
+
points: (patch.points ?? drawing.points).map((point) => reanchorDrawingPoint(point))
|
|
6007
6075
|
}) : drawing
|
|
6008
6076
|
);
|
|
6009
6077
|
emitDrawingsChange();
|