hyperprop-charting-library 0.1.48 → 0.1.49
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 +58 -16
- package/dist/hyperprop-charting-library.js +58 -16
- package/dist/index.cjs +58 -16
- package/dist/index.js +58 -16
- package/docs/API.md +1 -1
- package/package.json +1 -1
|
@@ -891,6 +891,9 @@ function createChart(element, options = {}) {
|
|
|
891
891
|
let crosshairPoint = null;
|
|
892
892
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
893
893
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
894
|
+
let noOverlappingLineLabels = DEFAULT_LABELS_OPTIONS.noOverlapping;
|
|
895
|
+
let rightAxisLabelSlots = [];
|
|
896
|
+
let plotLabelSlots = [];
|
|
894
897
|
let smoothedTickerPrice = null;
|
|
895
898
|
let tickerPriceTarget = null;
|
|
896
899
|
let smoothedTickerVolume = null;
|
|
@@ -987,6 +990,33 @@ function createChart(element, options = {}) {
|
|
|
987
990
|
const clamp = (value, min, max) => {
|
|
988
991
|
return Math.min(max, Math.max(min, value));
|
|
989
992
|
};
|
|
993
|
+
const resetLabelSlots = (enabled) => {
|
|
994
|
+
noOverlappingLineLabels = enabled;
|
|
995
|
+
rightAxisLabelSlots = [];
|
|
996
|
+
plotLabelSlots = [];
|
|
997
|
+
};
|
|
998
|
+
const placeLabelSlot = (targetY, height2, minY, maxY, slots, gap = 2) => {
|
|
999
|
+
const safeMaxY = Math.max(minY, maxY);
|
|
1000
|
+
const clampedTarget = clamp(targetY, minY, safeMaxY);
|
|
1001
|
+
if (!noOverlappingLineLabels || height2 <= 0) {
|
|
1002
|
+
return clampedTarget;
|
|
1003
|
+
}
|
|
1004
|
+
const overlaps = (y) => slots.some((slot) => y < slot.y + slot.height + gap && y + height2 + gap > slot.y);
|
|
1005
|
+
const candidates = [clampedTarget];
|
|
1006
|
+
for (const slot of slots) {
|
|
1007
|
+
candidates.push(slot.y - height2 - gap, slot.y + slot.height + gap);
|
|
1008
|
+
}
|
|
1009
|
+
const placedY = candidates.map((candidate) => clamp(candidate, minY, safeMaxY)).sort((a, b) => Math.abs(a - clampedTarget) - Math.abs(b - clampedTarget)).find((candidate) => !overlaps(candidate)) ?? clampedTarget;
|
|
1010
|
+
slots.push({ y: placedY, height: height2 });
|
|
1011
|
+
slots.sort((a, b) => a.y - b.y);
|
|
1012
|
+
return placedY;
|
|
1013
|
+
};
|
|
1014
|
+
const placeRightAxisLabel = (targetY, height2, minY, maxY) => {
|
|
1015
|
+
return placeLabelSlot(targetY, height2, minY, maxY, rightAxisLabelSlots);
|
|
1016
|
+
};
|
|
1017
|
+
const placePlotLabel = (targetY, height2, minY, maxY) => {
|
|
1018
|
+
return placeLabelSlot(targetY, height2, minY, maxY, plotLabelSlots);
|
|
1019
|
+
};
|
|
990
1020
|
const dashPatterns = {
|
|
991
1021
|
dotted: mergedOptions.dashPatterns.dotted ?? DEFAULT_DASH_PATTERNS.dotted,
|
|
992
1022
|
dashed: mergedOptions.dashPatterns.dashed ?? DEFAULT_DASH_PATTERNS.dashed,
|
|
@@ -1352,7 +1382,7 @@ function createChart(element, options = {}) {
|
|
|
1352
1382
|
const labelHeight = 20;
|
|
1353
1383
|
const labelWidth = mergedLine.label === void 0 ? getPriceLabelWidth(labelText, labelPaddingX) : Math.ceil(ctx.measureText(labelText).width) + labelPaddingX * 2;
|
|
1354
1384
|
const labelX = chartRight + 4;
|
|
1355
|
-
const labelY =
|
|
1385
|
+
const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1356
1386
|
ctx.fillStyle = mergedLine.labelBackgroundColor;
|
|
1357
1387
|
fillRoundedRect(
|
|
1358
1388
|
Math.round(labelX),
|
|
@@ -1485,17 +1515,18 @@ function createChart(element, options = {}) {
|
|
|
1485
1515
|
leftWidgetX = maxWidgetX;
|
|
1486
1516
|
}
|
|
1487
1517
|
leftWidgetX = clamp(leftWidgetX, leftWidgetXBase, maxWidgetX);
|
|
1488
|
-
const
|
|
1518
|
+
const targetLabelY = clamp(lineY - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
1519
|
+
const widgetY = placePlotLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1489
1520
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
1490
1521
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
1491
1522
|
const widgetBorder = color;
|
|
1492
1523
|
const textColor = line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor);
|
|
1493
1524
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
1494
1525
|
ctx.fillStyle = widgetBackground;
|
|
1495
|
-
fillRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1526
|
+
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1496
1527
|
ctx.strokeStyle = widgetBorder;
|
|
1497
1528
|
ctx.lineWidth = 1;
|
|
1498
|
-
strokeRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1529
|
+
strokeRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1499
1530
|
let cursorX = mainWidgetX;
|
|
1500
1531
|
const separatorColor = "rgba(148,163,184,0.45)";
|
|
1501
1532
|
if (actionButtonMetrics.length > 0) {
|
|
@@ -1505,7 +1536,7 @@ function createChart(element, options = {}) {
|
|
|
1505
1536
|
const fullHeight = button.fullHeight ?? mergedLine.actionButtonFullHeight;
|
|
1506
1537
|
const actionPadding = fullHeight ? 0 : 2;
|
|
1507
1538
|
const actionX = actionCursorX + actionPadding;
|
|
1508
|
-
const actionY =
|
|
1539
|
+
const actionY = widgetY + actionPadding;
|
|
1509
1540
|
const actionH = labelHeight - actionPadding * 2;
|
|
1510
1541
|
const actionW = Math.max(8, width2 - actionPadding * 2);
|
|
1511
1542
|
const actionRadius = Math.max(1, button.borderRadius ?? mergedLine.actionButtonBorderRadius);
|
|
@@ -1545,29 +1576,29 @@ function createChart(element, options = {}) {
|
|
|
1545
1576
|
});
|
|
1546
1577
|
}
|
|
1547
1578
|
if (qtyWidth > 0) {
|
|
1548
|
-
drawText(qtyText, cursorX + segmentPaddingX,
|
|
1579
|
+
drawText(qtyText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1549
1580
|
cursorX += qtyWidth;
|
|
1550
1581
|
ctx.strokeStyle = separatorColor;
|
|
1551
1582
|
ctx.beginPath();
|
|
1552
|
-
ctx.moveTo(crisp(cursorX),
|
|
1553
|
-
ctx.lineTo(crisp(cursorX),
|
|
1583
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1584
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1554
1585
|
ctx.stroke();
|
|
1555
1586
|
}
|
|
1556
|
-
drawText(centerText, cursorX + segmentPaddingX,
|
|
1587
|
+
drawText(centerText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1557
1588
|
cursorX += centerWidth;
|
|
1558
1589
|
if (showCloseButton) {
|
|
1559
1590
|
ctx.strokeStyle = separatorColor;
|
|
1560
1591
|
ctx.beginPath();
|
|
1561
|
-
ctx.moveTo(crisp(cursorX),
|
|
1562
|
-
ctx.lineTo(crisp(cursorX),
|
|
1592
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1593
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1563
1594
|
ctx.stroke();
|
|
1564
|
-
drawText("x", cursorX + closeWidth / 2,
|
|
1595
|
+
drawText("x", cursorX + closeWidth / 2, widgetY + labelHeight / 2, "center", "middle", textColor);
|
|
1565
1596
|
if (mergedLine.id) {
|
|
1566
1597
|
orderActionRegions.push({
|
|
1567
1598
|
orderId: mergedLine.id,
|
|
1568
1599
|
action: closeAction,
|
|
1569
1600
|
x: cursorX,
|
|
1570
|
-
y:
|
|
1601
|
+
y: widgetY,
|
|
1571
1602
|
width: closeWidth,
|
|
1572
1603
|
height: labelHeight,
|
|
1573
1604
|
line: mergedLine
|
|
@@ -1593,12 +1624,13 @@ function createChart(element, options = {}) {
|
|
|
1593
1624
|
orderPriceTagWidthById.set(mergedLine.id, priceWidth);
|
|
1594
1625
|
}
|
|
1595
1626
|
const priceX = chartRight + 4;
|
|
1627
|
+
const priceY = placeRightAxisLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1596
1628
|
ctx.fillStyle = mergedLine.labelBackgroundColor ?? color;
|
|
1597
|
-
fillRoundedRect(Math.round(priceX), Math.round(
|
|
1629
|
+
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1598
1630
|
ctx.strokeStyle = widgetBorder;
|
|
1599
1631
|
ctx.lineWidth = 1;
|
|
1600
|
-
strokeRoundedRect(Math.round(priceX), Math.round(
|
|
1601
|
-
drawText(priceText, priceX + pricePaddingX,
|
|
1632
|
+
strokeRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1633
|
+
drawText(priceText, priceX + pricePaddingX, priceY + labelHeight / 2, "left", "middle", textColor);
|
|
1602
1634
|
};
|
|
1603
1635
|
const fillRoundedRect = (x, y, widthValue, heightValue, radiusValue) => {
|
|
1604
1636
|
const maxRadius = Math.min(widthValue, heightValue) / 2;
|
|
@@ -2086,6 +2118,7 @@ function createChart(element, options = {}) {
|
|
|
2086
2118
|
ctx.font = prevFont;
|
|
2087
2119
|
}
|
|
2088
2120
|
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
2121
|
+
resetLabelSlots(labels.noOverlapping);
|
|
2089
2122
|
const priceAxisLabels = [];
|
|
2090
2123
|
const addPriceAxisLabel = (label) => {
|
|
2091
2124
|
if (!labels.visible || !Number.isFinite(label.price) || label.text.length === 0) {
|
|
@@ -2274,6 +2307,15 @@ function createChart(element, options = {}) {
|
|
|
2274
2307
|
}
|
|
2275
2308
|
}
|
|
2276
2309
|
}
|
|
2310
|
+
if (labels.noOverlapping) {
|
|
2311
|
+
rightAxisLabelSlots.push(
|
|
2312
|
+
...positionedLabels.map((label) => ({
|
|
2313
|
+
y: label.y,
|
|
2314
|
+
height: label.height
|
|
2315
|
+
}))
|
|
2316
|
+
);
|
|
2317
|
+
rightAxisLabelSlots.sort((a, b) => a.y - b.y);
|
|
2318
|
+
}
|
|
2277
2319
|
const labelX = chartRight + 4;
|
|
2278
2320
|
for (const label of positionedLabels) {
|
|
2279
2321
|
ctx.fillStyle = label.backgroundColor;
|
|
@@ -867,6 +867,9 @@ function createChart(element, options = {}) {
|
|
|
867
867
|
let crosshairPoint = null;
|
|
868
868
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
869
869
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
870
|
+
let noOverlappingLineLabels = DEFAULT_LABELS_OPTIONS.noOverlapping;
|
|
871
|
+
let rightAxisLabelSlots = [];
|
|
872
|
+
let plotLabelSlots = [];
|
|
870
873
|
let smoothedTickerPrice = null;
|
|
871
874
|
let tickerPriceTarget = null;
|
|
872
875
|
let smoothedTickerVolume = null;
|
|
@@ -963,6 +966,33 @@ function createChart(element, options = {}) {
|
|
|
963
966
|
const clamp = (value, min, max) => {
|
|
964
967
|
return Math.min(max, Math.max(min, value));
|
|
965
968
|
};
|
|
969
|
+
const resetLabelSlots = (enabled) => {
|
|
970
|
+
noOverlappingLineLabels = enabled;
|
|
971
|
+
rightAxisLabelSlots = [];
|
|
972
|
+
plotLabelSlots = [];
|
|
973
|
+
};
|
|
974
|
+
const placeLabelSlot = (targetY, height2, minY, maxY, slots, gap = 2) => {
|
|
975
|
+
const safeMaxY = Math.max(minY, maxY);
|
|
976
|
+
const clampedTarget = clamp(targetY, minY, safeMaxY);
|
|
977
|
+
if (!noOverlappingLineLabels || height2 <= 0) {
|
|
978
|
+
return clampedTarget;
|
|
979
|
+
}
|
|
980
|
+
const overlaps = (y) => slots.some((slot) => y < slot.y + slot.height + gap && y + height2 + gap > slot.y);
|
|
981
|
+
const candidates = [clampedTarget];
|
|
982
|
+
for (const slot of slots) {
|
|
983
|
+
candidates.push(slot.y - height2 - gap, slot.y + slot.height + gap);
|
|
984
|
+
}
|
|
985
|
+
const placedY = candidates.map((candidate) => clamp(candidate, minY, safeMaxY)).sort((a, b) => Math.abs(a - clampedTarget) - Math.abs(b - clampedTarget)).find((candidate) => !overlaps(candidate)) ?? clampedTarget;
|
|
986
|
+
slots.push({ y: placedY, height: height2 });
|
|
987
|
+
slots.sort((a, b) => a.y - b.y);
|
|
988
|
+
return placedY;
|
|
989
|
+
};
|
|
990
|
+
const placeRightAxisLabel = (targetY, height2, minY, maxY) => {
|
|
991
|
+
return placeLabelSlot(targetY, height2, minY, maxY, rightAxisLabelSlots);
|
|
992
|
+
};
|
|
993
|
+
const placePlotLabel = (targetY, height2, minY, maxY) => {
|
|
994
|
+
return placeLabelSlot(targetY, height2, minY, maxY, plotLabelSlots);
|
|
995
|
+
};
|
|
966
996
|
const dashPatterns = {
|
|
967
997
|
dotted: mergedOptions.dashPatterns.dotted ?? DEFAULT_DASH_PATTERNS.dotted,
|
|
968
998
|
dashed: mergedOptions.dashPatterns.dashed ?? DEFAULT_DASH_PATTERNS.dashed,
|
|
@@ -1328,7 +1358,7 @@ function createChart(element, options = {}) {
|
|
|
1328
1358
|
const labelHeight = 20;
|
|
1329
1359
|
const labelWidth = mergedLine.label === void 0 ? getPriceLabelWidth(labelText, labelPaddingX) : Math.ceil(ctx.measureText(labelText).width) + labelPaddingX * 2;
|
|
1330
1360
|
const labelX = chartRight + 4;
|
|
1331
|
-
const labelY =
|
|
1361
|
+
const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1332
1362
|
ctx.fillStyle = mergedLine.labelBackgroundColor;
|
|
1333
1363
|
fillRoundedRect(
|
|
1334
1364
|
Math.round(labelX),
|
|
@@ -1461,17 +1491,18 @@ function createChart(element, options = {}) {
|
|
|
1461
1491
|
leftWidgetX = maxWidgetX;
|
|
1462
1492
|
}
|
|
1463
1493
|
leftWidgetX = clamp(leftWidgetX, leftWidgetXBase, maxWidgetX);
|
|
1464
|
-
const
|
|
1494
|
+
const targetLabelY = clamp(lineY - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
1495
|
+
const widgetY = placePlotLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1465
1496
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
1466
1497
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
1467
1498
|
const widgetBorder = color;
|
|
1468
1499
|
const textColor = line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor);
|
|
1469
1500
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
1470
1501
|
ctx.fillStyle = widgetBackground;
|
|
1471
|
-
fillRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1502
|
+
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1472
1503
|
ctx.strokeStyle = widgetBorder;
|
|
1473
1504
|
ctx.lineWidth = 1;
|
|
1474
|
-
strokeRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1505
|
+
strokeRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1475
1506
|
let cursorX = mainWidgetX;
|
|
1476
1507
|
const separatorColor = "rgba(148,163,184,0.45)";
|
|
1477
1508
|
if (actionButtonMetrics.length > 0) {
|
|
@@ -1481,7 +1512,7 @@ function createChart(element, options = {}) {
|
|
|
1481
1512
|
const fullHeight = button.fullHeight ?? mergedLine.actionButtonFullHeight;
|
|
1482
1513
|
const actionPadding = fullHeight ? 0 : 2;
|
|
1483
1514
|
const actionX = actionCursorX + actionPadding;
|
|
1484
|
-
const actionY =
|
|
1515
|
+
const actionY = widgetY + actionPadding;
|
|
1485
1516
|
const actionH = labelHeight - actionPadding * 2;
|
|
1486
1517
|
const actionW = Math.max(8, width2 - actionPadding * 2);
|
|
1487
1518
|
const actionRadius = Math.max(1, button.borderRadius ?? mergedLine.actionButtonBorderRadius);
|
|
@@ -1521,29 +1552,29 @@ function createChart(element, options = {}) {
|
|
|
1521
1552
|
});
|
|
1522
1553
|
}
|
|
1523
1554
|
if (qtyWidth > 0) {
|
|
1524
|
-
drawText(qtyText, cursorX + segmentPaddingX,
|
|
1555
|
+
drawText(qtyText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1525
1556
|
cursorX += qtyWidth;
|
|
1526
1557
|
ctx.strokeStyle = separatorColor;
|
|
1527
1558
|
ctx.beginPath();
|
|
1528
|
-
ctx.moveTo(crisp(cursorX),
|
|
1529
|
-
ctx.lineTo(crisp(cursorX),
|
|
1559
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1560
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1530
1561
|
ctx.stroke();
|
|
1531
1562
|
}
|
|
1532
|
-
drawText(centerText, cursorX + segmentPaddingX,
|
|
1563
|
+
drawText(centerText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1533
1564
|
cursorX += centerWidth;
|
|
1534
1565
|
if (showCloseButton) {
|
|
1535
1566
|
ctx.strokeStyle = separatorColor;
|
|
1536
1567
|
ctx.beginPath();
|
|
1537
|
-
ctx.moveTo(crisp(cursorX),
|
|
1538
|
-
ctx.lineTo(crisp(cursorX),
|
|
1568
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1569
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1539
1570
|
ctx.stroke();
|
|
1540
|
-
drawText("x", cursorX + closeWidth / 2,
|
|
1571
|
+
drawText("x", cursorX + closeWidth / 2, widgetY + labelHeight / 2, "center", "middle", textColor);
|
|
1541
1572
|
if (mergedLine.id) {
|
|
1542
1573
|
orderActionRegions.push({
|
|
1543
1574
|
orderId: mergedLine.id,
|
|
1544
1575
|
action: closeAction,
|
|
1545
1576
|
x: cursorX,
|
|
1546
|
-
y:
|
|
1577
|
+
y: widgetY,
|
|
1547
1578
|
width: closeWidth,
|
|
1548
1579
|
height: labelHeight,
|
|
1549
1580
|
line: mergedLine
|
|
@@ -1569,12 +1600,13 @@ function createChart(element, options = {}) {
|
|
|
1569
1600
|
orderPriceTagWidthById.set(mergedLine.id, priceWidth);
|
|
1570
1601
|
}
|
|
1571
1602
|
const priceX = chartRight + 4;
|
|
1603
|
+
const priceY = placeRightAxisLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1572
1604
|
ctx.fillStyle = mergedLine.labelBackgroundColor ?? color;
|
|
1573
|
-
fillRoundedRect(Math.round(priceX), Math.round(
|
|
1605
|
+
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1574
1606
|
ctx.strokeStyle = widgetBorder;
|
|
1575
1607
|
ctx.lineWidth = 1;
|
|
1576
|
-
strokeRoundedRect(Math.round(priceX), Math.round(
|
|
1577
|
-
drawText(priceText, priceX + pricePaddingX,
|
|
1608
|
+
strokeRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1609
|
+
drawText(priceText, priceX + pricePaddingX, priceY + labelHeight / 2, "left", "middle", textColor);
|
|
1578
1610
|
};
|
|
1579
1611
|
const fillRoundedRect = (x, y, widthValue, heightValue, radiusValue) => {
|
|
1580
1612
|
const maxRadius = Math.min(widthValue, heightValue) / 2;
|
|
@@ -2062,6 +2094,7 @@ function createChart(element, options = {}) {
|
|
|
2062
2094
|
ctx.font = prevFont;
|
|
2063
2095
|
}
|
|
2064
2096
|
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
2097
|
+
resetLabelSlots(labels.noOverlapping);
|
|
2065
2098
|
const priceAxisLabels = [];
|
|
2066
2099
|
const addPriceAxisLabel = (label) => {
|
|
2067
2100
|
if (!labels.visible || !Number.isFinite(label.price) || label.text.length === 0) {
|
|
@@ -2250,6 +2283,15 @@ function createChart(element, options = {}) {
|
|
|
2250
2283
|
}
|
|
2251
2284
|
}
|
|
2252
2285
|
}
|
|
2286
|
+
if (labels.noOverlapping) {
|
|
2287
|
+
rightAxisLabelSlots.push(
|
|
2288
|
+
...positionedLabels.map((label) => ({
|
|
2289
|
+
y: label.y,
|
|
2290
|
+
height: label.height
|
|
2291
|
+
}))
|
|
2292
|
+
);
|
|
2293
|
+
rightAxisLabelSlots.sort((a, b) => a.y - b.y);
|
|
2294
|
+
}
|
|
2253
2295
|
const labelX = chartRight + 4;
|
|
2254
2296
|
for (const label of positionedLabels) {
|
|
2255
2297
|
ctx.fillStyle = label.backgroundColor;
|
package/dist/index.cjs
CHANGED
|
@@ -891,6 +891,9 @@ function createChart(element, options = {}) {
|
|
|
891
891
|
let crosshairPoint = null;
|
|
892
892
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
893
893
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
894
|
+
let noOverlappingLineLabels = DEFAULT_LABELS_OPTIONS.noOverlapping;
|
|
895
|
+
let rightAxisLabelSlots = [];
|
|
896
|
+
let plotLabelSlots = [];
|
|
894
897
|
let smoothedTickerPrice = null;
|
|
895
898
|
let tickerPriceTarget = null;
|
|
896
899
|
let smoothedTickerVolume = null;
|
|
@@ -987,6 +990,33 @@ function createChart(element, options = {}) {
|
|
|
987
990
|
const clamp = (value, min, max) => {
|
|
988
991
|
return Math.min(max, Math.max(min, value));
|
|
989
992
|
};
|
|
993
|
+
const resetLabelSlots = (enabled) => {
|
|
994
|
+
noOverlappingLineLabels = enabled;
|
|
995
|
+
rightAxisLabelSlots = [];
|
|
996
|
+
plotLabelSlots = [];
|
|
997
|
+
};
|
|
998
|
+
const placeLabelSlot = (targetY, height2, minY, maxY, slots, gap = 2) => {
|
|
999
|
+
const safeMaxY = Math.max(minY, maxY);
|
|
1000
|
+
const clampedTarget = clamp(targetY, minY, safeMaxY);
|
|
1001
|
+
if (!noOverlappingLineLabels || height2 <= 0) {
|
|
1002
|
+
return clampedTarget;
|
|
1003
|
+
}
|
|
1004
|
+
const overlaps = (y) => slots.some((slot) => y < slot.y + slot.height + gap && y + height2 + gap > slot.y);
|
|
1005
|
+
const candidates = [clampedTarget];
|
|
1006
|
+
for (const slot of slots) {
|
|
1007
|
+
candidates.push(slot.y - height2 - gap, slot.y + slot.height + gap);
|
|
1008
|
+
}
|
|
1009
|
+
const placedY = candidates.map((candidate) => clamp(candidate, minY, safeMaxY)).sort((a, b) => Math.abs(a - clampedTarget) - Math.abs(b - clampedTarget)).find((candidate) => !overlaps(candidate)) ?? clampedTarget;
|
|
1010
|
+
slots.push({ y: placedY, height: height2 });
|
|
1011
|
+
slots.sort((a, b) => a.y - b.y);
|
|
1012
|
+
return placedY;
|
|
1013
|
+
};
|
|
1014
|
+
const placeRightAxisLabel = (targetY, height2, minY, maxY) => {
|
|
1015
|
+
return placeLabelSlot(targetY, height2, minY, maxY, rightAxisLabelSlots);
|
|
1016
|
+
};
|
|
1017
|
+
const placePlotLabel = (targetY, height2, minY, maxY) => {
|
|
1018
|
+
return placeLabelSlot(targetY, height2, minY, maxY, plotLabelSlots);
|
|
1019
|
+
};
|
|
990
1020
|
const dashPatterns = {
|
|
991
1021
|
dotted: mergedOptions.dashPatterns.dotted ?? DEFAULT_DASH_PATTERNS.dotted,
|
|
992
1022
|
dashed: mergedOptions.dashPatterns.dashed ?? DEFAULT_DASH_PATTERNS.dashed,
|
|
@@ -1352,7 +1382,7 @@ function createChart(element, options = {}) {
|
|
|
1352
1382
|
const labelHeight = 20;
|
|
1353
1383
|
const labelWidth = mergedLine.label === void 0 ? getPriceLabelWidth(labelText, labelPaddingX) : Math.ceil(ctx.measureText(labelText).width) + labelPaddingX * 2;
|
|
1354
1384
|
const labelX = chartRight + 4;
|
|
1355
|
-
const labelY =
|
|
1385
|
+
const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1356
1386
|
ctx.fillStyle = mergedLine.labelBackgroundColor;
|
|
1357
1387
|
fillRoundedRect(
|
|
1358
1388
|
Math.round(labelX),
|
|
@@ -1485,17 +1515,18 @@ function createChart(element, options = {}) {
|
|
|
1485
1515
|
leftWidgetX = maxWidgetX;
|
|
1486
1516
|
}
|
|
1487
1517
|
leftWidgetX = clamp(leftWidgetX, leftWidgetXBase, maxWidgetX);
|
|
1488
|
-
const
|
|
1518
|
+
const targetLabelY = clamp(lineY - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
1519
|
+
const widgetY = placePlotLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1489
1520
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
1490
1521
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
1491
1522
|
const widgetBorder = color;
|
|
1492
1523
|
const textColor = line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor);
|
|
1493
1524
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
1494
1525
|
ctx.fillStyle = widgetBackground;
|
|
1495
|
-
fillRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1526
|
+
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1496
1527
|
ctx.strokeStyle = widgetBorder;
|
|
1497
1528
|
ctx.lineWidth = 1;
|
|
1498
|
-
strokeRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1529
|
+
strokeRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1499
1530
|
let cursorX = mainWidgetX;
|
|
1500
1531
|
const separatorColor = "rgba(148,163,184,0.45)";
|
|
1501
1532
|
if (actionButtonMetrics.length > 0) {
|
|
@@ -1505,7 +1536,7 @@ function createChart(element, options = {}) {
|
|
|
1505
1536
|
const fullHeight = button.fullHeight ?? mergedLine.actionButtonFullHeight;
|
|
1506
1537
|
const actionPadding = fullHeight ? 0 : 2;
|
|
1507
1538
|
const actionX = actionCursorX + actionPadding;
|
|
1508
|
-
const actionY =
|
|
1539
|
+
const actionY = widgetY + actionPadding;
|
|
1509
1540
|
const actionH = labelHeight - actionPadding * 2;
|
|
1510
1541
|
const actionW = Math.max(8, width2 - actionPadding * 2);
|
|
1511
1542
|
const actionRadius = Math.max(1, button.borderRadius ?? mergedLine.actionButtonBorderRadius);
|
|
@@ -1545,29 +1576,29 @@ function createChart(element, options = {}) {
|
|
|
1545
1576
|
});
|
|
1546
1577
|
}
|
|
1547
1578
|
if (qtyWidth > 0) {
|
|
1548
|
-
drawText(qtyText, cursorX + segmentPaddingX,
|
|
1579
|
+
drawText(qtyText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1549
1580
|
cursorX += qtyWidth;
|
|
1550
1581
|
ctx.strokeStyle = separatorColor;
|
|
1551
1582
|
ctx.beginPath();
|
|
1552
|
-
ctx.moveTo(crisp(cursorX),
|
|
1553
|
-
ctx.lineTo(crisp(cursorX),
|
|
1583
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1584
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1554
1585
|
ctx.stroke();
|
|
1555
1586
|
}
|
|
1556
|
-
drawText(centerText, cursorX + segmentPaddingX,
|
|
1587
|
+
drawText(centerText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1557
1588
|
cursorX += centerWidth;
|
|
1558
1589
|
if (showCloseButton) {
|
|
1559
1590
|
ctx.strokeStyle = separatorColor;
|
|
1560
1591
|
ctx.beginPath();
|
|
1561
|
-
ctx.moveTo(crisp(cursorX),
|
|
1562
|
-
ctx.lineTo(crisp(cursorX),
|
|
1592
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1593
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1563
1594
|
ctx.stroke();
|
|
1564
|
-
drawText("x", cursorX + closeWidth / 2,
|
|
1595
|
+
drawText("x", cursorX + closeWidth / 2, widgetY + labelHeight / 2, "center", "middle", textColor);
|
|
1565
1596
|
if (mergedLine.id) {
|
|
1566
1597
|
orderActionRegions.push({
|
|
1567
1598
|
orderId: mergedLine.id,
|
|
1568
1599
|
action: closeAction,
|
|
1569
1600
|
x: cursorX,
|
|
1570
|
-
y:
|
|
1601
|
+
y: widgetY,
|
|
1571
1602
|
width: closeWidth,
|
|
1572
1603
|
height: labelHeight,
|
|
1573
1604
|
line: mergedLine
|
|
@@ -1593,12 +1624,13 @@ function createChart(element, options = {}) {
|
|
|
1593
1624
|
orderPriceTagWidthById.set(mergedLine.id, priceWidth);
|
|
1594
1625
|
}
|
|
1595
1626
|
const priceX = chartRight + 4;
|
|
1627
|
+
const priceY = placeRightAxisLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1596
1628
|
ctx.fillStyle = mergedLine.labelBackgroundColor ?? color;
|
|
1597
|
-
fillRoundedRect(Math.round(priceX), Math.round(
|
|
1629
|
+
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1598
1630
|
ctx.strokeStyle = widgetBorder;
|
|
1599
1631
|
ctx.lineWidth = 1;
|
|
1600
|
-
strokeRoundedRect(Math.round(priceX), Math.round(
|
|
1601
|
-
drawText(priceText, priceX + pricePaddingX,
|
|
1632
|
+
strokeRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1633
|
+
drawText(priceText, priceX + pricePaddingX, priceY + labelHeight / 2, "left", "middle", textColor);
|
|
1602
1634
|
};
|
|
1603
1635
|
const fillRoundedRect = (x, y, widthValue, heightValue, radiusValue) => {
|
|
1604
1636
|
const maxRadius = Math.min(widthValue, heightValue) / 2;
|
|
@@ -2086,6 +2118,7 @@ function createChart(element, options = {}) {
|
|
|
2086
2118
|
ctx.font = prevFont;
|
|
2087
2119
|
}
|
|
2088
2120
|
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
2121
|
+
resetLabelSlots(labels.noOverlapping);
|
|
2089
2122
|
const priceAxisLabels = [];
|
|
2090
2123
|
const addPriceAxisLabel = (label) => {
|
|
2091
2124
|
if (!labels.visible || !Number.isFinite(label.price) || label.text.length === 0) {
|
|
@@ -2274,6 +2307,15 @@ function createChart(element, options = {}) {
|
|
|
2274
2307
|
}
|
|
2275
2308
|
}
|
|
2276
2309
|
}
|
|
2310
|
+
if (labels.noOverlapping) {
|
|
2311
|
+
rightAxisLabelSlots.push(
|
|
2312
|
+
...positionedLabels.map((label) => ({
|
|
2313
|
+
y: label.y,
|
|
2314
|
+
height: label.height
|
|
2315
|
+
}))
|
|
2316
|
+
);
|
|
2317
|
+
rightAxisLabelSlots.sort((a, b) => a.y - b.y);
|
|
2318
|
+
}
|
|
2277
2319
|
const labelX = chartRight + 4;
|
|
2278
2320
|
for (const label of positionedLabels) {
|
|
2279
2321
|
ctx.fillStyle = label.backgroundColor;
|
package/dist/index.js
CHANGED
|
@@ -867,6 +867,9 @@ function createChart(element, options = {}) {
|
|
|
867
867
|
let crosshairPoint = null;
|
|
868
868
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
869
869
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
870
|
+
let noOverlappingLineLabels = DEFAULT_LABELS_OPTIONS.noOverlapping;
|
|
871
|
+
let rightAxisLabelSlots = [];
|
|
872
|
+
let plotLabelSlots = [];
|
|
870
873
|
let smoothedTickerPrice = null;
|
|
871
874
|
let tickerPriceTarget = null;
|
|
872
875
|
let smoothedTickerVolume = null;
|
|
@@ -963,6 +966,33 @@ function createChart(element, options = {}) {
|
|
|
963
966
|
const clamp = (value, min, max) => {
|
|
964
967
|
return Math.min(max, Math.max(min, value));
|
|
965
968
|
};
|
|
969
|
+
const resetLabelSlots = (enabled) => {
|
|
970
|
+
noOverlappingLineLabels = enabled;
|
|
971
|
+
rightAxisLabelSlots = [];
|
|
972
|
+
plotLabelSlots = [];
|
|
973
|
+
};
|
|
974
|
+
const placeLabelSlot = (targetY, height2, minY, maxY, slots, gap = 2) => {
|
|
975
|
+
const safeMaxY = Math.max(minY, maxY);
|
|
976
|
+
const clampedTarget = clamp(targetY, minY, safeMaxY);
|
|
977
|
+
if (!noOverlappingLineLabels || height2 <= 0) {
|
|
978
|
+
return clampedTarget;
|
|
979
|
+
}
|
|
980
|
+
const overlaps = (y) => slots.some((slot) => y < slot.y + slot.height + gap && y + height2 + gap > slot.y);
|
|
981
|
+
const candidates = [clampedTarget];
|
|
982
|
+
for (const slot of slots) {
|
|
983
|
+
candidates.push(slot.y - height2 - gap, slot.y + slot.height + gap);
|
|
984
|
+
}
|
|
985
|
+
const placedY = candidates.map((candidate) => clamp(candidate, minY, safeMaxY)).sort((a, b) => Math.abs(a - clampedTarget) - Math.abs(b - clampedTarget)).find((candidate) => !overlaps(candidate)) ?? clampedTarget;
|
|
986
|
+
slots.push({ y: placedY, height: height2 });
|
|
987
|
+
slots.sort((a, b) => a.y - b.y);
|
|
988
|
+
return placedY;
|
|
989
|
+
};
|
|
990
|
+
const placeRightAxisLabel = (targetY, height2, minY, maxY) => {
|
|
991
|
+
return placeLabelSlot(targetY, height2, minY, maxY, rightAxisLabelSlots);
|
|
992
|
+
};
|
|
993
|
+
const placePlotLabel = (targetY, height2, minY, maxY) => {
|
|
994
|
+
return placeLabelSlot(targetY, height2, minY, maxY, plotLabelSlots);
|
|
995
|
+
};
|
|
966
996
|
const dashPatterns = {
|
|
967
997
|
dotted: mergedOptions.dashPatterns.dotted ?? DEFAULT_DASH_PATTERNS.dotted,
|
|
968
998
|
dashed: mergedOptions.dashPatterns.dashed ?? DEFAULT_DASH_PATTERNS.dashed,
|
|
@@ -1328,7 +1358,7 @@ function createChart(element, options = {}) {
|
|
|
1328
1358
|
const labelHeight = 20;
|
|
1329
1359
|
const labelWidth = mergedLine.label === void 0 ? getPriceLabelWidth(labelText, labelPaddingX) : Math.ceil(ctx.measureText(labelText).width) + labelPaddingX * 2;
|
|
1330
1360
|
const labelX = chartRight + 4;
|
|
1331
|
-
const labelY =
|
|
1361
|
+
const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1332
1362
|
ctx.fillStyle = mergedLine.labelBackgroundColor;
|
|
1333
1363
|
fillRoundedRect(
|
|
1334
1364
|
Math.round(labelX),
|
|
@@ -1461,17 +1491,18 @@ function createChart(element, options = {}) {
|
|
|
1461
1491
|
leftWidgetX = maxWidgetX;
|
|
1462
1492
|
}
|
|
1463
1493
|
leftWidgetX = clamp(leftWidgetX, leftWidgetXBase, maxWidgetX);
|
|
1464
|
-
const
|
|
1494
|
+
const targetLabelY = clamp(lineY - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
1495
|
+
const widgetY = placePlotLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1465
1496
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
1466
1497
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
1467
1498
|
const widgetBorder = color;
|
|
1468
1499
|
const textColor = line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor);
|
|
1469
1500
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
1470
1501
|
ctx.fillStyle = widgetBackground;
|
|
1471
|
-
fillRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1502
|
+
fillRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1472
1503
|
ctx.strokeStyle = widgetBorder;
|
|
1473
1504
|
ctx.lineWidth = 1;
|
|
1474
|
-
strokeRoundedRect(Math.round(mainWidgetX), Math.round(
|
|
1505
|
+
strokeRoundedRect(Math.round(mainWidgetX), Math.round(widgetY), mainWidgetWidth, labelHeight, borderRadius);
|
|
1475
1506
|
let cursorX = mainWidgetX;
|
|
1476
1507
|
const separatorColor = "rgba(148,163,184,0.45)";
|
|
1477
1508
|
if (actionButtonMetrics.length > 0) {
|
|
@@ -1481,7 +1512,7 @@ function createChart(element, options = {}) {
|
|
|
1481
1512
|
const fullHeight = button.fullHeight ?? mergedLine.actionButtonFullHeight;
|
|
1482
1513
|
const actionPadding = fullHeight ? 0 : 2;
|
|
1483
1514
|
const actionX = actionCursorX + actionPadding;
|
|
1484
|
-
const actionY =
|
|
1515
|
+
const actionY = widgetY + actionPadding;
|
|
1485
1516
|
const actionH = labelHeight - actionPadding * 2;
|
|
1486
1517
|
const actionW = Math.max(8, width2 - actionPadding * 2);
|
|
1487
1518
|
const actionRadius = Math.max(1, button.borderRadius ?? mergedLine.actionButtonBorderRadius);
|
|
@@ -1521,29 +1552,29 @@ function createChart(element, options = {}) {
|
|
|
1521
1552
|
});
|
|
1522
1553
|
}
|
|
1523
1554
|
if (qtyWidth > 0) {
|
|
1524
|
-
drawText(qtyText, cursorX + segmentPaddingX,
|
|
1555
|
+
drawText(qtyText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1525
1556
|
cursorX += qtyWidth;
|
|
1526
1557
|
ctx.strokeStyle = separatorColor;
|
|
1527
1558
|
ctx.beginPath();
|
|
1528
|
-
ctx.moveTo(crisp(cursorX),
|
|
1529
|
-
ctx.lineTo(crisp(cursorX),
|
|
1559
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1560
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1530
1561
|
ctx.stroke();
|
|
1531
1562
|
}
|
|
1532
|
-
drawText(centerText, cursorX + segmentPaddingX,
|
|
1563
|
+
drawText(centerText, cursorX + segmentPaddingX, widgetY + labelHeight / 2, "left", "middle", textColor);
|
|
1533
1564
|
cursorX += centerWidth;
|
|
1534
1565
|
if (showCloseButton) {
|
|
1535
1566
|
ctx.strokeStyle = separatorColor;
|
|
1536
1567
|
ctx.beginPath();
|
|
1537
|
-
ctx.moveTo(crisp(cursorX),
|
|
1538
|
-
ctx.lineTo(crisp(cursorX),
|
|
1568
|
+
ctx.moveTo(crisp(cursorX), widgetY + 4);
|
|
1569
|
+
ctx.lineTo(crisp(cursorX), widgetY + labelHeight - 4);
|
|
1539
1570
|
ctx.stroke();
|
|
1540
|
-
drawText("x", cursorX + closeWidth / 2,
|
|
1571
|
+
drawText("x", cursorX + closeWidth / 2, widgetY + labelHeight / 2, "center", "middle", textColor);
|
|
1541
1572
|
if (mergedLine.id) {
|
|
1542
1573
|
orderActionRegions.push({
|
|
1543
1574
|
orderId: mergedLine.id,
|
|
1544
1575
|
action: closeAction,
|
|
1545
1576
|
x: cursorX,
|
|
1546
|
-
y:
|
|
1577
|
+
y: widgetY,
|
|
1547
1578
|
width: closeWidth,
|
|
1548
1579
|
height: labelHeight,
|
|
1549
1580
|
line: mergedLine
|
|
@@ -1569,12 +1600,13 @@ function createChart(element, options = {}) {
|
|
|
1569
1600
|
orderPriceTagWidthById.set(mergedLine.id, priceWidth);
|
|
1570
1601
|
}
|
|
1571
1602
|
const priceX = chartRight + 4;
|
|
1603
|
+
const priceY = placeRightAxisLabel(targetLabelY, labelHeight, chartTop, chartBottom - labelHeight);
|
|
1572
1604
|
ctx.fillStyle = mergedLine.labelBackgroundColor ?? color;
|
|
1573
|
-
fillRoundedRect(Math.round(priceX), Math.round(
|
|
1605
|
+
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1574
1606
|
ctx.strokeStyle = widgetBorder;
|
|
1575
1607
|
ctx.lineWidth = 1;
|
|
1576
|
-
strokeRoundedRect(Math.round(priceX), Math.round(
|
|
1577
|
-
drawText(priceText, priceX + pricePaddingX,
|
|
1608
|
+
strokeRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, borderRadius);
|
|
1609
|
+
drawText(priceText, priceX + pricePaddingX, priceY + labelHeight / 2, "left", "middle", textColor);
|
|
1578
1610
|
};
|
|
1579
1611
|
const fillRoundedRect = (x, y, widthValue, heightValue, radiusValue) => {
|
|
1580
1612
|
const maxRadius = Math.min(widthValue, heightValue) / 2;
|
|
@@ -2062,6 +2094,7 @@ function createChart(element, options = {}) {
|
|
|
2062
2094
|
ctx.font = prevFont;
|
|
2063
2095
|
}
|
|
2064
2096
|
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
2097
|
+
resetLabelSlots(labels.noOverlapping);
|
|
2065
2098
|
const priceAxisLabels = [];
|
|
2066
2099
|
const addPriceAxisLabel = (label) => {
|
|
2067
2100
|
if (!labels.visible || !Number.isFinite(label.price) || label.text.length === 0) {
|
|
@@ -2250,6 +2283,15 @@ function createChart(element, options = {}) {
|
|
|
2250
2283
|
}
|
|
2251
2284
|
}
|
|
2252
2285
|
}
|
|
2286
|
+
if (labels.noOverlapping) {
|
|
2287
|
+
rightAxisLabelSlots.push(
|
|
2288
|
+
...positionedLabels.map((label) => ({
|
|
2289
|
+
y: label.y,
|
|
2290
|
+
height: label.height
|
|
2291
|
+
}))
|
|
2292
|
+
);
|
|
2293
|
+
rightAxisLabelSlots.sort((a, b) => a.y - b.y);
|
|
2294
|
+
}
|
|
2253
2295
|
const labelX = chartRight + 4;
|
|
2254
2296
|
for (const label of positionedLabels) {
|
|
2255
2297
|
ctx.fillStyle = label.backgroundColor;
|
package/docs/API.md
CHANGED
|
@@ -172,7 +172,7 @@ TradingView-style labels can be controlled from a single top-level object:
|
|
|
172
172
|
- `showIndicatorNames` (default `false`; draws active indicator names in the chart)
|
|
173
173
|
- `showIndicatorValues` (default `false`; appends simple indicator input values)
|
|
174
174
|
- `showCountdownToBarClose` (default `false`; draws a bottom-axis countdown based on candle time spacing)
|
|
175
|
-
- `noOverlapping` (default `true`; stacks price-scale labels so they do not cover each other)
|
|
175
|
+
- `noOverlapping` (default `true`; stacks price-scale labels, price-line tags, order/position price tags, and order widgets so they do not cover each other)
|
|
176
176
|
- Style fields: `backgroundColor`, `textColor`, `mutedTextColor`, `symbolNameBackgroundColor`, `symbolNameTextColor`, `previousCloseColor`, `highLowColor`, `bidColor`, `askColor`, `indicatorTextColor`, `borderRadius`, `labelHeight`, `labelPaddingX`
|
|
177
177
|
|
|
178
178
|
Example:
|