hyperprop-charting-library 0.1.49 → 0.1.51
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 +205 -18
- package/dist/hyperprop-charting-library.d.ts +39 -2
- package/dist/hyperprop-charting-library.js +205 -18
- package/dist/index.cjs +205 -18
- package/dist/index.d.cts +39 -2
- package/dist/index.d.ts +39 -2
- package/dist/index.js +205 -18
- package/docs/API.md +46 -2
- package/docs/RECIPES.md +112 -0
- package/package.json +1 -1
|
@@ -74,6 +74,10 @@ var DEFAULT_LABELS_OPTIONS = {
|
|
|
74
74
|
askPrice: Number.NaN,
|
|
75
75
|
showIndicatorNames: false,
|
|
76
76
|
showIndicatorValues: false,
|
|
77
|
+
showIndicatorValueLabels: true,
|
|
78
|
+
indicatorLegendPosition: "top-left",
|
|
79
|
+
indicatorLegendOffsetX: 10,
|
|
80
|
+
indicatorLegendOffsetY: 10,
|
|
77
81
|
showCountdownToBarClose: false,
|
|
78
82
|
noOverlapping: true,
|
|
79
83
|
backgroundColor: "#0b1220",
|
|
@@ -507,7 +511,7 @@ var drawOverlaySeries = (ctx, renderContext, values, color, width) => {
|
|
|
507
511
|
}
|
|
508
512
|
ctx.restore();
|
|
509
513
|
};
|
|
510
|
-
var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride, maxOverride, guideLines) => {
|
|
514
|
+
var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride, maxOverride, guideLines, options = {}) => {
|
|
511
515
|
const visible = [];
|
|
512
516
|
for (let index = renderContext.startIndex; index <= renderContext.endIndex; index += 1) {
|
|
513
517
|
const value = values[index];
|
|
@@ -515,7 +519,7 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
515
519
|
visible.push(value);
|
|
516
520
|
}
|
|
517
521
|
}
|
|
518
|
-
if (visible.length === 0) return;
|
|
522
|
+
if (visible.length === 0) return void 0;
|
|
519
523
|
const minValue = minOverride ?? Math.min(...visible);
|
|
520
524
|
const maxValue = maxOverride ?? Math.max(...visible);
|
|
521
525
|
const range = maxValue - minValue || 1;
|
|
@@ -564,6 +568,51 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
564
568
|
}
|
|
565
569
|
}
|
|
566
570
|
ctx.restore();
|
|
571
|
+
let latestValue = null;
|
|
572
|
+
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
573
|
+
const value = values[index];
|
|
574
|
+
if (Number.isFinite(value ?? Number.NaN)) {
|
|
575
|
+
latestValue = value;
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const decimals = options.decimals ?? 2;
|
|
580
|
+
const formatValue = (value) => value.toFixed(decimals);
|
|
581
|
+
const axisTicks = options.axisTicks ?? guideLines;
|
|
582
|
+
const paneInfo = {
|
|
583
|
+
...options.title ? { title: options.title } : {},
|
|
584
|
+
axis: {
|
|
585
|
+
min: minValue,
|
|
586
|
+
max: maxValue,
|
|
587
|
+
...axisTicks ? { ticks: axisTicks } : {},
|
|
588
|
+
decimals
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
if (guideLines) {
|
|
592
|
+
paneInfo.guideLines = guideLines.map((value) => ({ value, label: formatValue(value), style: "dotted" }));
|
|
593
|
+
}
|
|
594
|
+
if (latestValue !== null) {
|
|
595
|
+
paneInfo.legendValues = [
|
|
596
|
+
{
|
|
597
|
+
...options.legendLabel ? { label: options.legendLabel } : {},
|
|
598
|
+
value: latestValue,
|
|
599
|
+
text: formatValue(latestValue),
|
|
600
|
+
color
|
|
601
|
+
}
|
|
602
|
+
];
|
|
603
|
+
}
|
|
604
|
+
if (options.valueLabel !== false && latestValue !== null) {
|
|
605
|
+
paneInfo.valueLabels = [
|
|
606
|
+
{
|
|
607
|
+
value: latestValue,
|
|
608
|
+
text: formatValue(latestValue),
|
|
609
|
+
color: options.valueLabelColor ?? color,
|
|
610
|
+
backgroundColor: options.valueLabelBackgroundColor ?? options.valueLabelColor ?? color,
|
|
611
|
+
textColor: options.valueLabelTextColor ?? "#0f172a"
|
|
612
|
+
}
|
|
613
|
+
];
|
|
614
|
+
}
|
|
615
|
+
return paneInfo;
|
|
567
616
|
};
|
|
568
617
|
var getPercentileValue = (values, percentile) => {
|
|
569
618
|
if (values.length === 0) {
|
|
@@ -736,7 +785,10 @@ var BUILTIN_STDDEV_INDICATOR = {
|
|
|
736
785
|
renderContext.data,
|
|
737
786
|
() => computeStdDevSeries(renderContext.data, length, inputs.source ?? "close")
|
|
738
787
|
);
|
|
739
|
-
drawSeparateSeries(ctx, renderContext, values, inputs.color ?? "#f97316", Number(inputs.width) || 2
|
|
788
|
+
return drawSeparateSeries(ctx, renderContext, values, inputs.color ?? "#f97316", Number(inputs.width) || 2, void 0, void 0, void 0, {
|
|
789
|
+
title: `StdDev ${length}`,
|
|
790
|
+
decimals: 2
|
|
791
|
+
});
|
|
740
792
|
}
|
|
741
793
|
};
|
|
742
794
|
var BUILTIN_ATR_INDICATOR = {
|
|
@@ -748,7 +800,10 @@ var BUILTIN_ATR_INDICATOR = {
|
|
|
748
800
|
draw: (ctx, renderContext, inputs) => {
|
|
749
801
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
750
802
|
const values = withCachedSeries(`atr|${length}`, renderContext.data, () => computeAtrSeries(renderContext.data, length));
|
|
751
|
-
drawSeparateSeries(ctx, renderContext, values, inputs.color ?? "#eab308", Number(inputs.width) || 2
|
|
803
|
+
return drawSeparateSeries(ctx, renderContext, values, inputs.color ?? "#eab308", Number(inputs.width) || 2, void 0, void 0, void 0, {
|
|
804
|
+
title: `ATR ${length}`,
|
|
805
|
+
decimals: 2
|
|
806
|
+
});
|
|
752
807
|
}
|
|
753
808
|
};
|
|
754
809
|
var BUILTIN_RSI_INDICATOR = {
|
|
@@ -760,7 +815,7 @@ var BUILTIN_RSI_INDICATOR = {
|
|
|
760
815
|
draw: (ctx, renderContext, inputs) => {
|
|
761
816
|
const length = clampIndicatorLength(inputs.length, 14);
|
|
762
817
|
const values = withCachedSeries(`rsi|${length}`, renderContext.data, () => computeRsiSeries(renderContext.data, length));
|
|
763
|
-
drawSeparateSeries(
|
|
818
|
+
return drawSeparateSeries(
|
|
764
819
|
ctx,
|
|
765
820
|
renderContext,
|
|
766
821
|
values,
|
|
@@ -768,7 +823,15 @@ var BUILTIN_RSI_INDICATOR = {
|
|
|
768
823
|
Number(inputs.width) || 2,
|
|
769
824
|
0,
|
|
770
825
|
100,
|
|
771
|
-
[30, 50, 70]
|
|
826
|
+
[30, 50, 70],
|
|
827
|
+
{
|
|
828
|
+
title: `RSI ${length}`,
|
|
829
|
+
axisTicks: [0, 30, 50, 70, 100],
|
|
830
|
+
decimals: 2,
|
|
831
|
+
valueLabelColor: "#9E9E9E",
|
|
832
|
+
valueLabelBackgroundColor: "#9E9E9E",
|
|
833
|
+
valueLabelTextColor: "#0f172a"
|
|
834
|
+
}
|
|
772
835
|
);
|
|
773
836
|
}
|
|
774
837
|
};
|
|
@@ -1332,6 +1395,11 @@ function createChart(element, options = {}) {
|
|
|
1332
1395
|
const pad = (value) => String(value).padStart(2, "0");
|
|
1333
1396
|
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
1334
1397
|
};
|
|
1398
|
+
const getRightAxisLabelX = (chartRight) => chartRight + 1;
|
|
1399
|
+
const getRightAxisLabelWidth = (chartRight, contentWidth) => {
|
|
1400
|
+
const scaleWidth = Math.max(0, Math.floor(width - getRightAxisLabelX(chartRight)));
|
|
1401
|
+
return Math.max(contentWidth, scaleWidth);
|
|
1402
|
+
};
|
|
1335
1403
|
const drawPriceLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
1336
1404
|
const mergedLine = { ...DEFAULT_PRICE_LINE_OPTIONS, ...line };
|
|
1337
1405
|
if (!mergedLine.visible || !Number.isFinite(mergedLine.price)) {
|
|
@@ -1356,8 +1424,9 @@ function createChart(element, options = {}) {
|
|
|
1356
1424
|
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
1357
1425
|
const labelPaddingX = 8;
|
|
1358
1426
|
const labelHeight = 20;
|
|
1359
|
-
const
|
|
1360
|
-
const
|
|
1427
|
+
const measuredLabelWidth = mergedLine.label === void 0 ? getPriceLabelWidth(labelText, labelPaddingX) : Math.ceil(ctx.measureText(labelText).width) + labelPaddingX * 2;
|
|
1428
|
+
const labelWidth = getRightAxisLabelWidth(chartRight, measuredLabelWidth);
|
|
1429
|
+
const labelX = getRightAxisLabelX(chartRight);
|
|
1361
1430
|
const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1362
1431
|
ctx.fillStyle = mergedLine.labelBackgroundColor;
|
|
1363
1432
|
fillRoundedRect(
|
|
@@ -1595,11 +1664,11 @@ function createChart(element, options = {}) {
|
|
|
1595
1664
|
const priceText = formatPrice(renderPrice);
|
|
1596
1665
|
const pricePaddingX = 8;
|
|
1597
1666
|
const measuredPriceWidth = getPriceLabelWidth(priceText, pricePaddingX);
|
|
1598
|
-
const priceWidth = mergedLine.id === void 0 ? measuredPriceWidth : Math.max(measuredPriceWidth, orderPriceTagWidthById.get(mergedLine.id) ?? 0);
|
|
1667
|
+
const priceWidth = mergedLine.id === void 0 ? getRightAxisLabelWidth(chartRight, measuredPriceWidth) : getRightAxisLabelWidth(chartRight, Math.max(measuredPriceWidth, orderPriceTagWidthById.get(mergedLine.id) ?? 0));
|
|
1599
1668
|
if (mergedLine.id) {
|
|
1600
1669
|
orderPriceTagWidthById.set(mergedLine.id, priceWidth);
|
|
1601
1670
|
}
|
|
1602
|
-
const priceX = chartRight
|
|
1671
|
+
const priceX = getRightAxisLabelX(chartRight);
|
|
1603
1672
|
const priceY = placeRightAxisLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1604
1673
|
ctx.fillStyle = mergedLine.labelBackgroundColor ?? color;
|
|
1605
1674
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
@@ -1674,9 +1743,63 @@ function createChart(element, options = {}) {
|
|
|
1674
1743
|
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
1675
1744
|
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
1676
1745
|
ctx.fillRect(0, 0, width, height);
|
|
1746
|
+
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
1747
|
+
const estimateRightMargin = () => {
|
|
1748
|
+
const paddingX = Math.max(4, labels.labelPaddingX);
|
|
1749
|
+
const measure = (text) => Math.ceil(ctx.measureText(text).width) + paddingX * 2;
|
|
1750
|
+
let required = margin.right - 1;
|
|
1751
|
+
const include = (text) => {
|
|
1752
|
+
const normalized = text?.trim();
|
|
1753
|
+
if (normalized) {
|
|
1754
|
+
required = Math.max(required, measure(normalized));
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
const ticker2 = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
1758
|
+
const lastPoint2 = data[data.length - 1];
|
|
1759
|
+
if ((ticker2.visible ?? true) && labels.showLastPrice && lastPoint2) {
|
|
1760
|
+
include(formatPrice(lastPoint2.c));
|
|
1761
|
+
if (ticker2.labelSubtext) include(String(ticker2.labelSubtext));
|
|
1762
|
+
for (const subtext of ticker2.labelSubtexts ?? []) {
|
|
1763
|
+
include(String(subtext));
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
if (labels.showSymbolName) {
|
|
1767
|
+
include(labels.symbolName);
|
|
1768
|
+
}
|
|
1769
|
+
if (labels.showPreviousClose) {
|
|
1770
|
+
const previousCloseCandidate = Number.isFinite(labels.previousClosePrice) ? labels.previousClosePrice : data.length > 1 ? data[data.length - 2]?.c : Number.NaN;
|
|
1771
|
+
if (previousCloseCandidate !== void 0 && Number.isFinite(previousCloseCandidate)) {
|
|
1772
|
+
include(`PDC ${formatPrice(previousCloseCandidate)}`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
if (labels.showHighLow && data.length > 0) {
|
|
1776
|
+
include(`H ${formatPrice(Math.max(...data.map((point) => point.h)))}`);
|
|
1777
|
+
include(`L ${formatPrice(Math.min(...data.map((point) => point.l)))}`);
|
|
1778
|
+
}
|
|
1779
|
+
if (labels.showBidAsk) {
|
|
1780
|
+
if (Number.isFinite(labels.bidPrice)) include(`B ${formatPrice(labels.bidPrice)}`);
|
|
1781
|
+
if (Number.isFinite(labels.askPrice)) include(`A ${formatPrice(labels.askPrice)}`);
|
|
1782
|
+
}
|
|
1783
|
+
for (const line of priceLines) {
|
|
1784
|
+
const mergedLine = { ...DEFAULT_PRICE_LINE_OPTIONS, ...line };
|
|
1785
|
+
if (mergedLine.visible && Number.isFinite(mergedLine.price)) {
|
|
1786
|
+
include(mergedLine.label ?? formatPrice(mergedLine.price));
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
for (const line of orderLines) {
|
|
1790
|
+
const mergedLine = { ...DEFAULT_ORDER_LINE_OPTIONS, ...line };
|
|
1791
|
+
const renderPrice = mergedLine.behavior === "follow" && Number.isFinite(mergedLine.followPrice) ? mergedLine.followPrice : mergedLine.price;
|
|
1792
|
+
if (mergedLine.visible && Number.isFinite(renderPrice)) {
|
|
1793
|
+
include(formatPrice(renderPrice));
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
const maxRightMargin = Math.max(margin.right, width - margin.left - 160);
|
|
1797
|
+
return Math.min(maxRightMargin, Math.max(margin.right, Math.ceil(required + 1)));
|
|
1798
|
+
};
|
|
1799
|
+
const rightMargin = estimateRightMargin();
|
|
1677
1800
|
const chartLeft = margin.left;
|
|
1678
1801
|
const chartTop = margin.top;
|
|
1679
|
-
const chartWidth = width - margin.left -
|
|
1802
|
+
const chartWidth = width - margin.left - rightMargin;
|
|
1680
1803
|
const fullChartHeight = height - margin.top - margin.bottom;
|
|
1681
1804
|
const fullChartBottom = chartTop + fullChartHeight;
|
|
1682
1805
|
const chartRight = chartLeft + chartWidth;
|
|
@@ -2029,7 +2152,7 @@ function createChart(element, options = {}) {
|
|
|
2029
2152
|
ctx.beginPath();
|
|
2030
2153
|
ctx.rect(chartLeft + 1, paneTop + 1, Math.max(0, chartWidth - 2), Math.max(0, paneHeight - 2));
|
|
2031
2154
|
ctx.clip();
|
|
2032
|
-
plugin.draw(
|
|
2155
|
+
const paneInfo = plugin.draw(
|
|
2033
2156
|
ctx,
|
|
2034
2157
|
{
|
|
2035
2158
|
data,
|
|
@@ -2062,6 +2185,63 @@ function createChart(element, options = {}) {
|
|
|
2062
2185
|
ctx.lineTo(crisp(chartRight), crisp(paneTop));
|
|
2063
2186
|
ctx.stroke();
|
|
2064
2187
|
ctx.restore();
|
|
2188
|
+
const axisInfo = paneInfo?.axis;
|
|
2189
|
+
if (axisInfo && Number.isFinite(axisInfo.min) && Number.isFinite(axisInfo.max) && axisInfo.max !== axisInfo.min) {
|
|
2190
|
+
const paneRange = axisInfo.max - axisInfo.min;
|
|
2191
|
+
const yFromPaneValue = (value) => {
|
|
2192
|
+
const ratio = (value - axisInfo.min) / paneRange;
|
|
2193
|
+
return paneBottom - ratio * paneHeight;
|
|
2194
|
+
};
|
|
2195
|
+
const formatPaneValue = (value) => {
|
|
2196
|
+
if (axisInfo.format) {
|
|
2197
|
+
return axisInfo.format(value);
|
|
2198
|
+
}
|
|
2199
|
+
const decimals = axisInfo.decimals ?? (Math.abs(paneRange) <= 2 ? 2 : Math.abs(paneRange) <= 20 ? 1 : 0);
|
|
2200
|
+
return value.toFixed(Math.max(0, Math.min(8, Math.round(decimals))));
|
|
2201
|
+
};
|
|
2202
|
+
const axisTicks = axisInfo.ticks && axisInfo.ticks.length > 0 ? axisInfo.ticks : [axisInfo.min, axisInfo.min + paneRange / 2, axisInfo.max];
|
|
2203
|
+
const uniqueTicks = Array.from(new Set(axisTicks.filter((tick) => Number.isFinite(tick))));
|
|
2204
|
+
ctx.save();
|
|
2205
|
+
ctx.font = `${yAxisFontSize}px ${mergedOptions.fontFamily}`;
|
|
2206
|
+
for (const tick of uniqueTicks) {
|
|
2207
|
+
if (tick < axisInfo.min || tick > axisInfo.max) {
|
|
2208
|
+
continue;
|
|
2209
|
+
}
|
|
2210
|
+
drawText(formatPaneValue(tick), chartRight + 6, yFromPaneValue(tick), "left", "middle", yAxis.textColor);
|
|
2211
|
+
}
|
|
2212
|
+
ctx.restore();
|
|
2213
|
+
if (labels.visible) {
|
|
2214
|
+
const prevFont = ctx.font;
|
|
2215
|
+
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
2216
|
+
const legendTitle = paneInfo.title ?? plugin.name;
|
|
2217
|
+
const legendValues = paneInfo.legendValues ?? [];
|
|
2218
|
+
const legendParts = [
|
|
2219
|
+
legendTitle,
|
|
2220
|
+
...legendValues.map((value) => value.text ?? (value.value === void 0 ? "" : formatPaneValue(value.value))).filter(Boolean)
|
|
2221
|
+
].filter(Boolean);
|
|
2222
|
+
if (legendParts.length > 0) {
|
|
2223
|
+
drawText(legendParts.join(" "), chartLeft + 10, paneTop + 8, "left", "top", labels.indicatorTextColor);
|
|
2224
|
+
}
|
|
2225
|
+
for (const label of paneInfo.valueLabels ?? []) {
|
|
2226
|
+
if (!labels.showIndicatorValueLabels) {
|
|
2227
|
+
continue;
|
|
2228
|
+
}
|
|
2229
|
+
if (!Number.isFinite(label.value) || label.value < axisInfo.min || label.value > axisInfo.max) {
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
const text = label.text ?? formatPaneValue(label.value);
|
|
2233
|
+
const labelPaddingX = 7;
|
|
2234
|
+
const labelHeight = Math.max(16, yAxisFontSize + 8);
|
|
2235
|
+
const labelWidth = getRightAxisLabelWidth(chartRight, Math.ceil(ctx.measureText(text).width) + labelPaddingX * 2);
|
|
2236
|
+
const labelX = getRightAxisLabelX(chartRight);
|
|
2237
|
+
const labelY = clamp(yFromPaneValue(label.value) - labelHeight / 2, paneTop + 2, paneBottom - labelHeight - 2);
|
|
2238
|
+
ctx.fillStyle = label.backgroundColor ?? label.color ?? labels.backgroundColor;
|
|
2239
|
+
fillRoundedRect(Math.round(labelX), Math.round(labelY), labelWidth, labelHeight, Math.max(0, labels.borderRadius));
|
|
2240
|
+
drawText(text, labelX + labelPaddingX, labelY + labelHeight / 2, "left", "middle", label.textColor ?? labels.textColor);
|
|
2241
|
+
}
|
|
2242
|
+
ctx.font = prevFont;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2065
2245
|
});
|
|
2066
2246
|
}
|
|
2067
2247
|
if (crosshair.visible && crosshairPoint && crosshair.showVertical) {
|
|
@@ -2093,7 +2273,6 @@ function createChart(element, options = {}) {
|
|
|
2093
2273
|
drawText(formatPrice(price), chartRight + 6, y, "left", "middle", yAxis.textColor);
|
|
2094
2274
|
ctx.font = prevFont;
|
|
2095
2275
|
}
|
|
2096
|
-
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
2097
2276
|
resetLabelSlots(labels.noOverlapping);
|
|
2098
2277
|
const priceAxisLabels = [];
|
|
2099
2278
|
const addPriceAxisLabel = (label) => {
|
|
@@ -2257,12 +2436,13 @@ function createChart(element, options = {}) {
|
|
|
2257
2436
|
ctx.font = baseFont;
|
|
2258
2437
|
}
|
|
2259
2438
|
const labelTextWidth = Math.max(primaryWidth, subtextWidth);
|
|
2439
|
+
const labelWidth = getRightAxisLabelWidth(chartRight, labelTextWidth);
|
|
2260
2440
|
return {
|
|
2261
2441
|
...label,
|
|
2262
2442
|
subtexts,
|
|
2263
2443
|
subtextFontSize,
|
|
2264
2444
|
height: labelHeight,
|
|
2265
|
-
width:
|
|
2445
|
+
width: labelWidth,
|
|
2266
2446
|
targetY: clamp(yFromPrice(label.price), chartTop + 1, chartBottom - 1) - labelHeight / 2,
|
|
2267
2447
|
y: 0
|
|
2268
2448
|
};
|
|
@@ -2292,7 +2472,7 @@ function createChart(element, options = {}) {
|
|
|
2292
2472
|
);
|
|
2293
2473
|
rightAxisLabelSlots.sort((a, b) => a.y - b.y);
|
|
2294
2474
|
}
|
|
2295
|
-
const labelX = chartRight
|
|
2475
|
+
const labelX = getRightAxisLabelX(chartRight);
|
|
2296
2476
|
for (const label of positionedLabels) {
|
|
2297
2477
|
ctx.fillStyle = label.backgroundColor;
|
|
2298
2478
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
@@ -2337,7 +2517,14 @@ function createChart(element, options = {}) {
|
|
|
2337
2517
|
const prevFont = ctx.font;
|
|
2338
2518
|
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
2339
2519
|
const legendText = labelEntries.join(" ");
|
|
2340
|
-
|
|
2520
|
+
const offsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
|
|
2521
|
+
const offsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
|
|
2522
|
+
const position = labels.indicatorLegendPosition;
|
|
2523
|
+
const isRight = position === "top-right" || position === "bottom-right";
|
|
2524
|
+
const isBottom = position === "bottom-left" || position === "bottom-right";
|
|
2525
|
+
const legendX = isRight ? chartRight - offsetX : chartLeft + offsetX;
|
|
2526
|
+
const legendY = isBottom ? chartBottom - offsetY : chartTop + offsetY;
|
|
2527
|
+
drawText(legendText, legendX, legendY, isRight ? "right" : "left", isBottom ? "bottom" : "top", labels.indicatorTextColor);
|
|
2341
2528
|
ctx.font = prevFont;
|
|
2342
2529
|
}
|
|
2343
2530
|
}
|
|
@@ -2395,8 +2582,8 @@ function createChart(element, options = {}) {
|
|
|
2395
2582
|
if (crosshair.showPriceLabel) {
|
|
2396
2583
|
const hoverPrice = yMin + (chartBottom - cy) / chartHeight * yRange;
|
|
2397
2584
|
const priceText = formatPrice(hoverPrice);
|
|
2398
|
-
const priceWidth = getPriceLabelWidth(priceText, labelPaddingX);
|
|
2399
|
-
const priceX = chartRight
|
|
2585
|
+
const priceWidth = getRightAxisLabelWidth(chartRight, getPriceLabelWidth(priceText, labelPaddingX));
|
|
2586
|
+
const priceX = getRightAxisLabelX(chartRight);
|
|
2400
2587
|
const priceY = clamp(cy - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
2401
2588
|
ctx.fillStyle = labelBackground;
|
|
2402
2589
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|