hyperprop-charting-library 0.1.114 → 0.1.116
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.
|
@@ -1239,7 +1239,15 @@ function createChart(element, options = {}) {
|
|
|
1239
1239
|
const minSpan = minVisibleBars;
|
|
1240
1240
|
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, count + maxPanBars * 2));
|
|
1241
1241
|
xSpan = clamp(xSpan, minSpan, maxSpan);
|
|
1242
|
-
|
|
1242
|
+
const halfSpan = xSpan / 2;
|
|
1243
|
+
const minOverlap = Math.max(1, Math.min(count, Math.ceil(xSpan * 0.1)));
|
|
1244
|
+
const lowCenter = minOverlap - halfSpan;
|
|
1245
|
+
const highCenter = count - minOverlap + halfSpan;
|
|
1246
|
+
xCenter = clamp(
|
|
1247
|
+
xCenter,
|
|
1248
|
+
Math.max(lowCenter, -maxPanBars),
|
|
1249
|
+
Math.min(highCenter, count + maxPanBars)
|
|
1250
|
+
);
|
|
1243
1251
|
};
|
|
1244
1252
|
const fitXViewport = () => {
|
|
1245
1253
|
const count = data.length;
|
|
@@ -2666,6 +2674,37 @@ function createChart(element, options = {}) {
|
|
|
2666
2674
|
}
|
|
2667
2675
|
}
|
|
2668
2676
|
}
|
|
2677
|
+
} else if (drawing.type === "rectangle") {
|
|
2678
|
+
const p0 = drawing.points[0];
|
|
2679
|
+
const p1 = drawing.points[1];
|
|
2680
|
+
if (p0 && p1) {
|
|
2681
|
+
const px0 = xFromDrawingPoint(p0);
|
|
2682
|
+
const px1 = xFromDrawingPoint(p1);
|
|
2683
|
+
const leftX = Math.min(px0, px1);
|
|
2684
|
+
const rightX = Math.max(px0, px1);
|
|
2685
|
+
const y0 = yFromPrice(p0.price);
|
|
2686
|
+
const y1 = yFromPrice(p1.price);
|
|
2687
|
+
const topY = Math.min(y0, y1);
|
|
2688
|
+
const botY = Math.max(y0, y1);
|
|
2689
|
+
const boxW = Math.max(1, rightX - leftX);
|
|
2690
|
+
const boxH = Math.max(1, botY - topY);
|
|
2691
|
+
ctx.save();
|
|
2692
|
+
ctx.globalAlpha = draft ? 0.6 : 1;
|
|
2693
|
+
ctx.fillStyle = hexToRgba(drawing.color, 0.14);
|
|
2694
|
+
ctx.fillRect(leftX, topY, boxW, boxH);
|
|
2695
|
+
ctx.lineWidth = Math.max(1, drawing.width);
|
|
2696
|
+
ctx.strokeStyle = drawing.color;
|
|
2697
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2698
|
+
ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
|
|
2699
|
+
ctx.restore();
|
|
2700
|
+
handleAt(px0, y0, drawing.color);
|
|
2701
|
+
handleAt(px1, y1, drawing.color);
|
|
2702
|
+
handleAt(px0, y1, drawing.color);
|
|
2703
|
+
handleAt(px1, y0, drawing.color);
|
|
2704
|
+
if (drawing.label) {
|
|
2705
|
+
drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2669
2708
|
} else if (drawing.type === "price-range") {
|
|
2670
2709
|
const p0 = drawing.points[0];
|
|
2671
2710
|
const p1 = drawing.points[1];
|
|
@@ -4155,6 +4194,21 @@ function createChart(element, options = {}) {
|
|
|
4155
4194
|
if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
|
|
4156
4195
|
return { drawing, target: "line" };
|
|
4157
4196
|
}
|
|
4197
|
+
} else if (drawing.type === "rectangle") {
|
|
4198
|
+
const p0 = drawing.points[0];
|
|
4199
|
+
const p1 = drawing.points[1];
|
|
4200
|
+
if (!p0 || !p1) continue;
|
|
4201
|
+
const px0 = canvasXFromDrawingPoint(p0);
|
|
4202
|
+
const px1 = canvasXFromDrawingPoint(p1);
|
|
4203
|
+
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
4204
|
+
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
4205
|
+
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
4206
|
+
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
4207
|
+
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
4208
|
+
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
4209
|
+
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
4210
|
+
return { drawing, target: "line" };
|
|
4211
|
+
}
|
|
4158
4212
|
} else if (drawing.type === "price-range") {
|
|
4159
4213
|
const p0 = drawing.points[0];
|
|
4160
4214
|
const p1 = drawing.points[1];
|
|
@@ -4411,6 +4465,30 @@ function createChart(element, options = {}) {
|
|
|
4411
4465
|
drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
|
|
4412
4466
|
return true;
|
|
4413
4467
|
}
|
|
4468
|
+
if (activeDrawingTool === "rectangle") {
|
|
4469
|
+
if (draftDrawing?.type === "rectangle") {
|
|
4470
|
+
const completed = normalizeDrawingState({
|
|
4471
|
+
...serializeDrawing(draftDrawing),
|
|
4472
|
+
points: [draftDrawing.points[0], point]
|
|
4473
|
+
});
|
|
4474
|
+
drawings.push(completed);
|
|
4475
|
+
draftDrawing = null;
|
|
4476
|
+
activeDrawingTool = null;
|
|
4477
|
+
emitDrawingsChange();
|
|
4478
|
+
scheduleDraw();
|
|
4479
|
+
return true;
|
|
4480
|
+
}
|
|
4481
|
+
const defaults = getDrawingToolDefaults("rectangle");
|
|
4482
|
+
draftDrawing = normalizeDrawingState({
|
|
4483
|
+
type: "rectangle",
|
|
4484
|
+
points: [point, point],
|
|
4485
|
+
color: defaults.color ?? "#2962ff",
|
|
4486
|
+
style: defaults.style ?? "solid",
|
|
4487
|
+
width: defaults.width ?? 1
|
|
4488
|
+
});
|
|
4489
|
+
scheduleDraw();
|
|
4490
|
+
return true;
|
|
4491
|
+
}
|
|
4414
4492
|
if (activeDrawingTool === "price-range") {
|
|
4415
4493
|
const tick = getConfiguredTickSize();
|
|
4416
4494
|
const visibleRange = drawState.yMax - drawState.yMin;
|
|
@@ -4512,6 +4590,25 @@ function createChart(element, options = {}) {
|
|
|
4512
4590
|
}
|
|
4513
4591
|
return { ...drawing, points: pts };
|
|
4514
4592
|
}
|
|
4593
|
+
if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
|
|
4594
|
+
const pts = drawing.points.map((point) => ({ ...point }));
|
|
4595
|
+
const p0 = pts[0];
|
|
4596
|
+
const p1 = pts[1];
|
|
4597
|
+
if (!p0 || !p1) return drawing;
|
|
4598
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4599
|
+
if (pointIndex === 0) {
|
|
4600
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4601
|
+
} else if (pointIndex === 1) {
|
|
4602
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4603
|
+
} else if (pointIndex === 2) {
|
|
4604
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
|
|
4605
|
+
pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
|
|
4606
|
+
} else if (pointIndex === 3) {
|
|
4607
|
+
pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
|
|
4608
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
|
|
4609
|
+
}
|
|
4610
|
+
return { ...drawing, points: pts };
|
|
4611
|
+
}
|
|
4515
4612
|
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4516
4613
|
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4517
4614
|
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
@@ -4780,7 +4877,7 @@ function createChart(element, options = {}) {
|
|
|
4780
4877
|
setCrosshairPoint(null);
|
|
4781
4878
|
return;
|
|
4782
4879
|
}
|
|
4783
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4880
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4784
4881
|
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4785
4882
|
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4786
4883
|
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
|
@@ -44,7 +44,7 @@ interface ChartOptions {
|
|
|
44
44
|
drawings?: DrawingObjectOptions[];
|
|
45
45
|
}
|
|
46
46
|
type IndicatorPane = "overlay" | "separate";
|
|
47
|
-
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
|
|
47
|
+
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note";
|
|
48
48
|
interface DrawingPoint {
|
|
49
49
|
index: number;
|
|
50
50
|
price: number;
|
|
@@ -1213,7 +1213,15 @@ function createChart(element, options = {}) {
|
|
|
1213
1213
|
const minSpan = minVisibleBars;
|
|
1214
1214
|
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, count + maxPanBars * 2));
|
|
1215
1215
|
xSpan = clamp(xSpan, minSpan, maxSpan);
|
|
1216
|
-
|
|
1216
|
+
const halfSpan = xSpan / 2;
|
|
1217
|
+
const minOverlap = Math.max(1, Math.min(count, Math.ceil(xSpan * 0.1)));
|
|
1218
|
+
const lowCenter = minOverlap - halfSpan;
|
|
1219
|
+
const highCenter = count - minOverlap + halfSpan;
|
|
1220
|
+
xCenter = clamp(
|
|
1221
|
+
xCenter,
|
|
1222
|
+
Math.max(lowCenter, -maxPanBars),
|
|
1223
|
+
Math.min(highCenter, count + maxPanBars)
|
|
1224
|
+
);
|
|
1217
1225
|
};
|
|
1218
1226
|
const fitXViewport = () => {
|
|
1219
1227
|
const count = data.length;
|
|
@@ -2640,6 +2648,37 @@ function createChart(element, options = {}) {
|
|
|
2640
2648
|
}
|
|
2641
2649
|
}
|
|
2642
2650
|
}
|
|
2651
|
+
} else if (drawing.type === "rectangle") {
|
|
2652
|
+
const p0 = drawing.points[0];
|
|
2653
|
+
const p1 = drawing.points[1];
|
|
2654
|
+
if (p0 && p1) {
|
|
2655
|
+
const px0 = xFromDrawingPoint(p0);
|
|
2656
|
+
const px1 = xFromDrawingPoint(p1);
|
|
2657
|
+
const leftX = Math.min(px0, px1);
|
|
2658
|
+
const rightX = Math.max(px0, px1);
|
|
2659
|
+
const y0 = yFromPrice(p0.price);
|
|
2660
|
+
const y1 = yFromPrice(p1.price);
|
|
2661
|
+
const topY = Math.min(y0, y1);
|
|
2662
|
+
const botY = Math.max(y0, y1);
|
|
2663
|
+
const boxW = Math.max(1, rightX - leftX);
|
|
2664
|
+
const boxH = Math.max(1, botY - topY);
|
|
2665
|
+
ctx.save();
|
|
2666
|
+
ctx.globalAlpha = draft ? 0.6 : 1;
|
|
2667
|
+
ctx.fillStyle = hexToRgba(drawing.color, 0.14);
|
|
2668
|
+
ctx.fillRect(leftX, topY, boxW, boxH);
|
|
2669
|
+
ctx.lineWidth = Math.max(1, drawing.width);
|
|
2670
|
+
ctx.strokeStyle = drawing.color;
|
|
2671
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2672
|
+
ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
|
|
2673
|
+
ctx.restore();
|
|
2674
|
+
handleAt(px0, y0, drawing.color);
|
|
2675
|
+
handleAt(px1, y1, drawing.color);
|
|
2676
|
+
handleAt(px0, y1, drawing.color);
|
|
2677
|
+
handleAt(px1, y0, drawing.color);
|
|
2678
|
+
if (drawing.label) {
|
|
2679
|
+
drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2643
2682
|
} else if (drawing.type === "price-range") {
|
|
2644
2683
|
const p0 = drawing.points[0];
|
|
2645
2684
|
const p1 = drawing.points[1];
|
|
@@ -4129,6 +4168,21 @@ function createChart(element, options = {}) {
|
|
|
4129
4168
|
if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
|
|
4130
4169
|
return { drawing, target: "line" };
|
|
4131
4170
|
}
|
|
4171
|
+
} else if (drawing.type === "rectangle") {
|
|
4172
|
+
const p0 = drawing.points[0];
|
|
4173
|
+
const p1 = drawing.points[1];
|
|
4174
|
+
if (!p0 || !p1) continue;
|
|
4175
|
+
const px0 = canvasXFromDrawingPoint(p0);
|
|
4176
|
+
const px1 = canvasXFromDrawingPoint(p1);
|
|
4177
|
+
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
4178
|
+
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
4179
|
+
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
4180
|
+
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
4181
|
+
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
4182
|
+
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
4183
|
+
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
4184
|
+
return { drawing, target: "line" };
|
|
4185
|
+
}
|
|
4132
4186
|
} else if (drawing.type === "price-range") {
|
|
4133
4187
|
const p0 = drawing.points[0];
|
|
4134
4188
|
const p1 = drawing.points[1];
|
|
@@ -4385,6 +4439,30 @@ function createChart(element, options = {}) {
|
|
|
4385
4439
|
drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
|
|
4386
4440
|
return true;
|
|
4387
4441
|
}
|
|
4442
|
+
if (activeDrawingTool === "rectangle") {
|
|
4443
|
+
if (draftDrawing?.type === "rectangle") {
|
|
4444
|
+
const completed = normalizeDrawingState({
|
|
4445
|
+
...serializeDrawing(draftDrawing),
|
|
4446
|
+
points: [draftDrawing.points[0], point]
|
|
4447
|
+
});
|
|
4448
|
+
drawings.push(completed);
|
|
4449
|
+
draftDrawing = null;
|
|
4450
|
+
activeDrawingTool = null;
|
|
4451
|
+
emitDrawingsChange();
|
|
4452
|
+
scheduleDraw();
|
|
4453
|
+
return true;
|
|
4454
|
+
}
|
|
4455
|
+
const defaults = getDrawingToolDefaults("rectangle");
|
|
4456
|
+
draftDrawing = normalizeDrawingState({
|
|
4457
|
+
type: "rectangle",
|
|
4458
|
+
points: [point, point],
|
|
4459
|
+
color: defaults.color ?? "#2962ff",
|
|
4460
|
+
style: defaults.style ?? "solid",
|
|
4461
|
+
width: defaults.width ?? 1
|
|
4462
|
+
});
|
|
4463
|
+
scheduleDraw();
|
|
4464
|
+
return true;
|
|
4465
|
+
}
|
|
4388
4466
|
if (activeDrawingTool === "price-range") {
|
|
4389
4467
|
const tick = getConfiguredTickSize();
|
|
4390
4468
|
const visibleRange = drawState.yMax - drawState.yMin;
|
|
@@ -4486,6 +4564,25 @@ function createChart(element, options = {}) {
|
|
|
4486
4564
|
}
|
|
4487
4565
|
return { ...drawing, points: pts };
|
|
4488
4566
|
}
|
|
4567
|
+
if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
|
|
4568
|
+
const pts = drawing.points.map((point) => ({ ...point }));
|
|
4569
|
+
const p0 = pts[0];
|
|
4570
|
+
const p1 = pts[1];
|
|
4571
|
+
if (!p0 || !p1) return drawing;
|
|
4572
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4573
|
+
if (pointIndex === 0) {
|
|
4574
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4575
|
+
} else if (pointIndex === 1) {
|
|
4576
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4577
|
+
} else if (pointIndex === 2) {
|
|
4578
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
|
|
4579
|
+
pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
|
|
4580
|
+
} else if (pointIndex === 3) {
|
|
4581
|
+
pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
|
|
4582
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
|
|
4583
|
+
}
|
|
4584
|
+
return { ...drawing, points: pts };
|
|
4585
|
+
}
|
|
4489
4586
|
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4490
4587
|
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4491
4588
|
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
@@ -4754,7 +4851,7 @@ function createChart(element, options = {}) {
|
|
|
4754
4851
|
setCrosshairPoint(null);
|
|
4755
4852
|
return;
|
|
4756
4853
|
}
|
|
4757
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4854
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4758
4855
|
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4759
4856
|
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4760
4857
|
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
package/dist/index.cjs
CHANGED
|
@@ -1239,7 +1239,15 @@ function createChart(element, options = {}) {
|
|
|
1239
1239
|
const minSpan = minVisibleBars;
|
|
1240
1240
|
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, count + maxPanBars * 2));
|
|
1241
1241
|
xSpan = clamp(xSpan, minSpan, maxSpan);
|
|
1242
|
-
|
|
1242
|
+
const halfSpan = xSpan / 2;
|
|
1243
|
+
const minOverlap = Math.max(1, Math.min(count, Math.ceil(xSpan * 0.1)));
|
|
1244
|
+
const lowCenter = minOverlap - halfSpan;
|
|
1245
|
+
const highCenter = count - minOverlap + halfSpan;
|
|
1246
|
+
xCenter = clamp(
|
|
1247
|
+
xCenter,
|
|
1248
|
+
Math.max(lowCenter, -maxPanBars),
|
|
1249
|
+
Math.min(highCenter, count + maxPanBars)
|
|
1250
|
+
);
|
|
1243
1251
|
};
|
|
1244
1252
|
const fitXViewport = () => {
|
|
1245
1253
|
const count = data.length;
|
|
@@ -2666,6 +2674,37 @@ function createChart(element, options = {}) {
|
|
|
2666
2674
|
}
|
|
2667
2675
|
}
|
|
2668
2676
|
}
|
|
2677
|
+
} else if (drawing.type === "rectangle") {
|
|
2678
|
+
const p0 = drawing.points[0];
|
|
2679
|
+
const p1 = drawing.points[1];
|
|
2680
|
+
if (p0 && p1) {
|
|
2681
|
+
const px0 = xFromDrawingPoint(p0);
|
|
2682
|
+
const px1 = xFromDrawingPoint(p1);
|
|
2683
|
+
const leftX = Math.min(px0, px1);
|
|
2684
|
+
const rightX = Math.max(px0, px1);
|
|
2685
|
+
const y0 = yFromPrice(p0.price);
|
|
2686
|
+
const y1 = yFromPrice(p1.price);
|
|
2687
|
+
const topY = Math.min(y0, y1);
|
|
2688
|
+
const botY = Math.max(y0, y1);
|
|
2689
|
+
const boxW = Math.max(1, rightX - leftX);
|
|
2690
|
+
const boxH = Math.max(1, botY - topY);
|
|
2691
|
+
ctx.save();
|
|
2692
|
+
ctx.globalAlpha = draft ? 0.6 : 1;
|
|
2693
|
+
ctx.fillStyle = hexToRgba(drawing.color, 0.14);
|
|
2694
|
+
ctx.fillRect(leftX, topY, boxW, boxH);
|
|
2695
|
+
ctx.lineWidth = Math.max(1, drawing.width);
|
|
2696
|
+
ctx.strokeStyle = drawing.color;
|
|
2697
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2698
|
+
ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
|
|
2699
|
+
ctx.restore();
|
|
2700
|
+
handleAt(px0, y0, drawing.color);
|
|
2701
|
+
handleAt(px1, y1, drawing.color);
|
|
2702
|
+
handleAt(px0, y1, drawing.color);
|
|
2703
|
+
handleAt(px1, y0, drawing.color);
|
|
2704
|
+
if (drawing.label) {
|
|
2705
|
+
drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2669
2708
|
} else if (drawing.type === "price-range") {
|
|
2670
2709
|
const p0 = drawing.points[0];
|
|
2671
2710
|
const p1 = drawing.points[1];
|
|
@@ -4155,6 +4194,21 @@ function createChart(element, options = {}) {
|
|
|
4155
4194
|
if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
|
|
4156
4195
|
return { drawing, target: "line" };
|
|
4157
4196
|
}
|
|
4197
|
+
} else if (drawing.type === "rectangle") {
|
|
4198
|
+
const p0 = drawing.points[0];
|
|
4199
|
+
const p1 = drawing.points[1];
|
|
4200
|
+
if (!p0 || !p1) continue;
|
|
4201
|
+
const px0 = canvasXFromDrawingPoint(p0);
|
|
4202
|
+
const px1 = canvasXFromDrawingPoint(p1);
|
|
4203
|
+
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
4204
|
+
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
4205
|
+
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
4206
|
+
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
4207
|
+
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
4208
|
+
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
4209
|
+
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
4210
|
+
return { drawing, target: "line" };
|
|
4211
|
+
}
|
|
4158
4212
|
} else if (drawing.type === "price-range") {
|
|
4159
4213
|
const p0 = drawing.points[0];
|
|
4160
4214
|
const p1 = drawing.points[1];
|
|
@@ -4411,6 +4465,30 @@ function createChart(element, options = {}) {
|
|
|
4411
4465
|
drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
|
|
4412
4466
|
return true;
|
|
4413
4467
|
}
|
|
4468
|
+
if (activeDrawingTool === "rectangle") {
|
|
4469
|
+
if (draftDrawing?.type === "rectangle") {
|
|
4470
|
+
const completed = normalizeDrawingState({
|
|
4471
|
+
...serializeDrawing(draftDrawing),
|
|
4472
|
+
points: [draftDrawing.points[0], point]
|
|
4473
|
+
});
|
|
4474
|
+
drawings.push(completed);
|
|
4475
|
+
draftDrawing = null;
|
|
4476
|
+
activeDrawingTool = null;
|
|
4477
|
+
emitDrawingsChange();
|
|
4478
|
+
scheduleDraw();
|
|
4479
|
+
return true;
|
|
4480
|
+
}
|
|
4481
|
+
const defaults = getDrawingToolDefaults("rectangle");
|
|
4482
|
+
draftDrawing = normalizeDrawingState({
|
|
4483
|
+
type: "rectangle",
|
|
4484
|
+
points: [point, point],
|
|
4485
|
+
color: defaults.color ?? "#2962ff",
|
|
4486
|
+
style: defaults.style ?? "solid",
|
|
4487
|
+
width: defaults.width ?? 1
|
|
4488
|
+
});
|
|
4489
|
+
scheduleDraw();
|
|
4490
|
+
return true;
|
|
4491
|
+
}
|
|
4414
4492
|
if (activeDrawingTool === "price-range") {
|
|
4415
4493
|
const tick = getConfiguredTickSize();
|
|
4416
4494
|
const visibleRange = drawState.yMax - drawState.yMin;
|
|
@@ -4512,6 +4590,25 @@ function createChart(element, options = {}) {
|
|
|
4512
4590
|
}
|
|
4513
4591
|
return { ...drawing, points: pts };
|
|
4514
4592
|
}
|
|
4593
|
+
if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
|
|
4594
|
+
const pts = drawing.points.map((point) => ({ ...point }));
|
|
4595
|
+
const p0 = pts[0];
|
|
4596
|
+
const p1 = pts[1];
|
|
4597
|
+
if (!p0 || !p1) return drawing;
|
|
4598
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4599
|
+
if (pointIndex === 0) {
|
|
4600
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4601
|
+
} else if (pointIndex === 1) {
|
|
4602
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4603
|
+
} else if (pointIndex === 2) {
|
|
4604
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
|
|
4605
|
+
pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
|
|
4606
|
+
} else if (pointIndex === 3) {
|
|
4607
|
+
pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
|
|
4608
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
|
|
4609
|
+
}
|
|
4610
|
+
return { ...drawing, points: pts };
|
|
4611
|
+
}
|
|
4515
4612
|
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4516
4613
|
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4517
4614
|
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
@@ -4780,7 +4877,7 @@ function createChart(element, options = {}) {
|
|
|
4780
4877
|
setCrosshairPoint(null);
|
|
4781
4878
|
return;
|
|
4782
4879
|
}
|
|
4783
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4880
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4784
4881
|
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4785
4882
|
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4786
4883
|
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
package/dist/index.d.cts
CHANGED
|
@@ -44,7 +44,7 @@ interface ChartOptions {
|
|
|
44
44
|
drawings?: DrawingObjectOptions[];
|
|
45
45
|
}
|
|
46
46
|
type IndicatorPane = "overlay" | "separate";
|
|
47
|
-
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
|
|
47
|
+
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note";
|
|
48
48
|
interface DrawingPoint {
|
|
49
49
|
index: number;
|
|
50
50
|
price: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ interface ChartOptions {
|
|
|
44
44
|
drawings?: DrawingObjectOptions[];
|
|
45
45
|
}
|
|
46
46
|
type IndicatorPane = "overlay" | "separate";
|
|
47
|
-
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
|
|
47
|
+
type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note";
|
|
48
48
|
interface DrawingPoint {
|
|
49
49
|
index: number;
|
|
50
50
|
price: number;
|
package/dist/index.js
CHANGED
|
@@ -1213,7 +1213,15 @@ function createChart(element, options = {}) {
|
|
|
1213
1213
|
const minSpan = minVisibleBars;
|
|
1214
1214
|
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, count + maxPanBars * 2));
|
|
1215
1215
|
xSpan = clamp(xSpan, minSpan, maxSpan);
|
|
1216
|
-
|
|
1216
|
+
const halfSpan = xSpan / 2;
|
|
1217
|
+
const minOverlap = Math.max(1, Math.min(count, Math.ceil(xSpan * 0.1)));
|
|
1218
|
+
const lowCenter = minOverlap - halfSpan;
|
|
1219
|
+
const highCenter = count - minOverlap + halfSpan;
|
|
1220
|
+
xCenter = clamp(
|
|
1221
|
+
xCenter,
|
|
1222
|
+
Math.max(lowCenter, -maxPanBars),
|
|
1223
|
+
Math.min(highCenter, count + maxPanBars)
|
|
1224
|
+
);
|
|
1217
1225
|
};
|
|
1218
1226
|
const fitXViewport = () => {
|
|
1219
1227
|
const count = data.length;
|
|
@@ -2640,6 +2648,37 @@ function createChart(element, options = {}) {
|
|
|
2640
2648
|
}
|
|
2641
2649
|
}
|
|
2642
2650
|
}
|
|
2651
|
+
} else if (drawing.type === "rectangle") {
|
|
2652
|
+
const p0 = drawing.points[0];
|
|
2653
|
+
const p1 = drawing.points[1];
|
|
2654
|
+
if (p0 && p1) {
|
|
2655
|
+
const px0 = xFromDrawingPoint(p0);
|
|
2656
|
+
const px1 = xFromDrawingPoint(p1);
|
|
2657
|
+
const leftX = Math.min(px0, px1);
|
|
2658
|
+
const rightX = Math.max(px0, px1);
|
|
2659
|
+
const y0 = yFromPrice(p0.price);
|
|
2660
|
+
const y1 = yFromPrice(p1.price);
|
|
2661
|
+
const topY = Math.min(y0, y1);
|
|
2662
|
+
const botY = Math.max(y0, y1);
|
|
2663
|
+
const boxW = Math.max(1, rightX - leftX);
|
|
2664
|
+
const boxH = Math.max(1, botY - topY);
|
|
2665
|
+
ctx.save();
|
|
2666
|
+
ctx.globalAlpha = draft ? 0.6 : 1;
|
|
2667
|
+
ctx.fillStyle = hexToRgba(drawing.color, 0.14);
|
|
2668
|
+
ctx.fillRect(leftX, topY, boxW, boxH);
|
|
2669
|
+
ctx.lineWidth = Math.max(1, drawing.width);
|
|
2670
|
+
ctx.strokeStyle = drawing.color;
|
|
2671
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2672
|
+
ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
|
|
2673
|
+
ctx.restore();
|
|
2674
|
+
handleAt(px0, y0, drawing.color);
|
|
2675
|
+
handleAt(px1, y1, drawing.color);
|
|
2676
|
+
handleAt(px0, y1, drawing.color);
|
|
2677
|
+
handleAt(px1, y0, drawing.color);
|
|
2678
|
+
if (drawing.label) {
|
|
2679
|
+
drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2643
2682
|
} else if (drawing.type === "price-range") {
|
|
2644
2683
|
const p0 = drawing.points[0];
|
|
2645
2684
|
const p1 = drawing.points[1];
|
|
@@ -4129,6 +4168,21 @@ function createChart(element, options = {}) {
|
|
|
4129
4168
|
if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
|
|
4130
4169
|
return { drawing, target: "line" };
|
|
4131
4170
|
}
|
|
4171
|
+
} else if (drawing.type === "rectangle") {
|
|
4172
|
+
const p0 = drawing.points[0];
|
|
4173
|
+
const p1 = drawing.points[1];
|
|
4174
|
+
if (!p0 || !p1) continue;
|
|
4175
|
+
const px0 = canvasXFromDrawingPoint(p0);
|
|
4176
|
+
const px1 = canvasXFromDrawingPoint(p1);
|
|
4177
|
+
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
4178
|
+
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
4179
|
+
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
4180
|
+
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
4181
|
+
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
4182
|
+
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
4183
|
+
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
4184
|
+
return { drawing, target: "line" };
|
|
4185
|
+
}
|
|
4132
4186
|
} else if (drawing.type === "price-range") {
|
|
4133
4187
|
const p0 = drawing.points[0];
|
|
4134
4188
|
const p1 = drawing.points[1];
|
|
@@ -4385,6 +4439,30 @@ function createChart(element, options = {}) {
|
|
|
4385
4439
|
drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
|
|
4386
4440
|
return true;
|
|
4387
4441
|
}
|
|
4442
|
+
if (activeDrawingTool === "rectangle") {
|
|
4443
|
+
if (draftDrawing?.type === "rectangle") {
|
|
4444
|
+
const completed = normalizeDrawingState({
|
|
4445
|
+
...serializeDrawing(draftDrawing),
|
|
4446
|
+
points: [draftDrawing.points[0], point]
|
|
4447
|
+
});
|
|
4448
|
+
drawings.push(completed);
|
|
4449
|
+
draftDrawing = null;
|
|
4450
|
+
activeDrawingTool = null;
|
|
4451
|
+
emitDrawingsChange();
|
|
4452
|
+
scheduleDraw();
|
|
4453
|
+
return true;
|
|
4454
|
+
}
|
|
4455
|
+
const defaults = getDrawingToolDefaults("rectangle");
|
|
4456
|
+
draftDrawing = normalizeDrawingState({
|
|
4457
|
+
type: "rectangle",
|
|
4458
|
+
points: [point, point],
|
|
4459
|
+
color: defaults.color ?? "#2962ff",
|
|
4460
|
+
style: defaults.style ?? "solid",
|
|
4461
|
+
width: defaults.width ?? 1
|
|
4462
|
+
});
|
|
4463
|
+
scheduleDraw();
|
|
4464
|
+
return true;
|
|
4465
|
+
}
|
|
4388
4466
|
if (activeDrawingTool === "price-range") {
|
|
4389
4467
|
const tick = getConfiguredTickSize();
|
|
4390
4468
|
const visibleRange = drawState.yMax - drawState.yMin;
|
|
@@ -4486,6 +4564,25 @@ function createChart(element, options = {}) {
|
|
|
4486
4564
|
}
|
|
4487
4565
|
return { ...drawing, points: pts };
|
|
4488
4566
|
}
|
|
4567
|
+
if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
|
|
4568
|
+
const pts = drawing.points.map((point) => ({ ...point }));
|
|
4569
|
+
const p0 = pts[0];
|
|
4570
|
+
const p1 = pts[1];
|
|
4571
|
+
if (!p0 || !p1) return drawing;
|
|
4572
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4573
|
+
if (pointIndex === 0) {
|
|
4574
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4575
|
+
} else if (pointIndex === 1) {
|
|
4576
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
|
|
4577
|
+
} else if (pointIndex === 2) {
|
|
4578
|
+
pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
|
|
4579
|
+
pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
|
|
4580
|
+
} else if (pointIndex === 3) {
|
|
4581
|
+
pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
|
|
4582
|
+
pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
|
|
4583
|
+
}
|
|
4584
|
+
return { ...drawing, points: pts };
|
|
4585
|
+
}
|
|
4489
4586
|
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4490
4587
|
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4491
4588
|
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
@@ -4754,7 +4851,7 @@ function createChart(element, options = {}) {
|
|
|
4754
4851
|
setCrosshairPoint(null);
|
|
4755
4852
|
return;
|
|
4756
4853
|
}
|
|
4757
|
-
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4854
|
+
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4758
4855
|
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4759
4856
|
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4760
4857
|
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|