hyperprop-charting-library 0.1.105 → 0.1.107
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 +47 -8
- package/dist/hyperprop-charting-library.js +47 -8
- package/dist/index.cjs +47 -8
- package/dist/index.js +47 -8
- package/docs/API.md +1 -0
- package/package.json +1 -1
|
@@ -1007,6 +1007,7 @@ function createChart(element, options = {}) {
|
|
|
1007
1007
|
let drawingEditTextHandler = null;
|
|
1008
1008
|
let magnetMode = "none";
|
|
1009
1009
|
let magnetModifierActive = false;
|
|
1010
|
+
let shiftKeyActive = false;
|
|
1010
1011
|
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
1011
1012
|
let drawingHoverHandler = null;
|
|
1012
1013
|
let lastHoveredDrawingId = null;
|
|
@@ -3918,6 +3919,23 @@ function createChart(element, options = {}) {
|
|
|
3918
3919
|
...time ? { time: time.toISOString() } : {}
|
|
3919
3920
|
};
|
|
3920
3921
|
};
|
|
3922
|
+
const constrainAngleFromAnchor = (anchor, x, y) => {
|
|
3923
|
+
const ax = canvasXFromDrawingPoint(anchor);
|
|
3924
|
+
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
3925
|
+
const dx = x - ax;
|
|
3926
|
+
const dy = y - ay;
|
|
3927
|
+
if (dx === 0 && dy === 0) return drawingPointFromCanvas(x, y);
|
|
3928
|
+
const step = Math.PI / 4;
|
|
3929
|
+
const snappedAngle = Math.round(Math.atan2(dy, dx) / step) * step;
|
|
3930
|
+
const length = Math.hypot(dx, dy);
|
|
3931
|
+
const sx = ax + Math.cos(snappedAngle) * length;
|
|
3932
|
+
const sy = ay + Math.sin(snappedAngle) * length;
|
|
3933
|
+
const resolved = drawingPointFromCanvas(sx, sy);
|
|
3934
|
+
if (!resolved) return resolved;
|
|
3935
|
+
if (Math.abs(Math.sin(snappedAngle)) < 1e-6) return { ...resolved, price: anchor.price };
|
|
3936
|
+
if (Math.abs(Math.cos(snappedAngle)) < 1e-6) return { ...resolved, index: anchor.index };
|
|
3937
|
+
return resolved;
|
|
3938
|
+
};
|
|
3921
3939
|
const normalizeDrawingPoint = (index, price) => {
|
|
3922
3940
|
const roundedIndex = Math.round(index);
|
|
3923
3941
|
const time = getTimeForIndex(roundedIndex);
|
|
@@ -4221,9 +4239,11 @@ function createChart(element, options = {}) {
|
|
|
4221
4239
|
}
|
|
4222
4240
|
if (activeDrawingTool === "trendline") {
|
|
4223
4241
|
if (draftDrawing?.type === "trendline") {
|
|
4242
|
+
const anchor = draftDrawing.points[0];
|
|
4243
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4224
4244
|
const completed = normalizeDrawingState({
|
|
4225
4245
|
...serializeDrawing(draftDrawing),
|
|
4226
|
-
points: [
|
|
4246
|
+
points: [anchor, endPoint]
|
|
4227
4247
|
});
|
|
4228
4248
|
drawings.push(completed);
|
|
4229
4249
|
draftDrawing = null;
|
|
@@ -4245,9 +4265,11 @@ function createChart(element, options = {}) {
|
|
|
4245
4265
|
}
|
|
4246
4266
|
if (activeDrawingTool === "ray") {
|
|
4247
4267
|
if (draftDrawing?.type === "ray") {
|
|
4268
|
+
const anchor = draftDrawing.points[0];
|
|
4269
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4248
4270
|
const completed = normalizeDrawingState({
|
|
4249
4271
|
...serializeDrawing(draftDrawing),
|
|
4250
|
-
points: [
|
|
4272
|
+
points: [anchor, endPoint]
|
|
4251
4273
|
});
|
|
4252
4274
|
drawings.push(completed);
|
|
4253
4275
|
draftDrawing = null;
|
|
@@ -4444,6 +4466,17 @@ function createChart(element, options = {}) {
|
|
|
4444
4466
|
}
|
|
4445
4467
|
return { ...drawing, points: pts };
|
|
4446
4468
|
}
|
|
4469
|
+
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4470
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4471
|
+
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
4472
|
+
const constrained = anchor ? constrainAngleFromAnchor(anchor, x, y) : null;
|
|
4473
|
+
if (constrained) {
|
|
4474
|
+
return {
|
|
4475
|
+
...drawing,
|
|
4476
|
+
points: drawing.points.map((point, index) => index === pointIndex ? constrained : point)
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4447
4480
|
if (drawingDragState.target === "handle") {
|
|
4448
4481
|
return {
|
|
4449
4482
|
...drawing,
|
|
@@ -4532,6 +4565,7 @@ function createChart(element, options = {}) {
|
|
|
4532
4565
|
event.preventDefault();
|
|
4533
4566
|
}
|
|
4534
4567
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4568
|
+
shiftKeyActive = event.shiftKey;
|
|
4535
4569
|
const point = getCanvasPoint(event);
|
|
4536
4570
|
if (event.pointerType === "touch") {
|
|
4537
4571
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4631,7 +4665,7 @@ function createChart(element, options = {}) {
|
|
|
4631
4665
|
isDragging = true;
|
|
4632
4666
|
dragMode = region;
|
|
4633
4667
|
activePointerId = event.pointerId;
|
|
4634
|
-
const crosshairDrag =
|
|
4668
|
+
const crosshairDrag = false;
|
|
4635
4669
|
pointerDownInfo = {
|
|
4636
4670
|
pointerId: event.pointerId,
|
|
4637
4671
|
pointerType: event.pointerType,
|
|
@@ -4655,6 +4689,7 @@ function createChart(element, options = {}) {
|
|
|
4655
4689
|
event.preventDefault();
|
|
4656
4690
|
}
|
|
4657
4691
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4692
|
+
shiftKeyActive = event.shiftKey;
|
|
4658
4693
|
const point = getCanvasPoint(event);
|
|
4659
4694
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4660
4695
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4697,7 +4732,10 @@ function createChart(element, options = {}) {
|
|
|
4697
4732
|
return;
|
|
4698
4733
|
}
|
|
4699
4734
|
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4700
|
-
|
|
4735
|
+
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4736
|
+
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4737
|
+
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
|
4738
|
+
}
|
|
4701
4739
|
if (nextPoint) {
|
|
4702
4740
|
draftDrawing = {
|
|
4703
4741
|
...draftDrawing,
|
|
@@ -5012,10 +5050,11 @@ function createChart(element, options = {}) {
|
|
|
5012
5050
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
5013
5051
|
const onModifierKeyChange = (event) => {
|
|
5014
5052
|
const active = event.metaKey || event.ctrlKey;
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5053
|
+
const shift = event.shiftKey;
|
|
5054
|
+
const changed = active !== magnetModifierActive || shift !== shiftKeyActive;
|
|
5055
|
+
magnetModifierActive = active;
|
|
5056
|
+
shiftKeyActive = shift;
|
|
5057
|
+
if (changed && draftDrawing) draw();
|
|
5019
5058
|
};
|
|
5020
5059
|
const onWindowBlurMagnet = () => {
|
|
5021
5060
|
magnetModifierActive = false;
|
|
@@ -981,6 +981,7 @@ function createChart(element, options = {}) {
|
|
|
981
981
|
let drawingEditTextHandler = null;
|
|
982
982
|
let magnetMode = "none";
|
|
983
983
|
let magnetModifierActive = false;
|
|
984
|
+
let shiftKeyActive = false;
|
|
984
985
|
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
985
986
|
let drawingHoverHandler = null;
|
|
986
987
|
let lastHoveredDrawingId = null;
|
|
@@ -3892,6 +3893,23 @@ function createChart(element, options = {}) {
|
|
|
3892
3893
|
...time ? { time: time.toISOString() } : {}
|
|
3893
3894
|
};
|
|
3894
3895
|
};
|
|
3896
|
+
const constrainAngleFromAnchor = (anchor, x, y) => {
|
|
3897
|
+
const ax = canvasXFromDrawingPoint(anchor);
|
|
3898
|
+
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
3899
|
+
const dx = x - ax;
|
|
3900
|
+
const dy = y - ay;
|
|
3901
|
+
if (dx === 0 && dy === 0) return drawingPointFromCanvas(x, y);
|
|
3902
|
+
const step = Math.PI / 4;
|
|
3903
|
+
const snappedAngle = Math.round(Math.atan2(dy, dx) / step) * step;
|
|
3904
|
+
const length = Math.hypot(dx, dy);
|
|
3905
|
+
const sx = ax + Math.cos(snappedAngle) * length;
|
|
3906
|
+
const sy = ay + Math.sin(snappedAngle) * length;
|
|
3907
|
+
const resolved = drawingPointFromCanvas(sx, sy);
|
|
3908
|
+
if (!resolved) return resolved;
|
|
3909
|
+
if (Math.abs(Math.sin(snappedAngle)) < 1e-6) return { ...resolved, price: anchor.price };
|
|
3910
|
+
if (Math.abs(Math.cos(snappedAngle)) < 1e-6) return { ...resolved, index: anchor.index };
|
|
3911
|
+
return resolved;
|
|
3912
|
+
};
|
|
3895
3913
|
const normalizeDrawingPoint = (index, price) => {
|
|
3896
3914
|
const roundedIndex = Math.round(index);
|
|
3897
3915
|
const time = getTimeForIndex(roundedIndex);
|
|
@@ -4195,9 +4213,11 @@ function createChart(element, options = {}) {
|
|
|
4195
4213
|
}
|
|
4196
4214
|
if (activeDrawingTool === "trendline") {
|
|
4197
4215
|
if (draftDrawing?.type === "trendline") {
|
|
4216
|
+
const anchor = draftDrawing.points[0];
|
|
4217
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4198
4218
|
const completed = normalizeDrawingState({
|
|
4199
4219
|
...serializeDrawing(draftDrawing),
|
|
4200
|
-
points: [
|
|
4220
|
+
points: [anchor, endPoint]
|
|
4201
4221
|
});
|
|
4202
4222
|
drawings.push(completed);
|
|
4203
4223
|
draftDrawing = null;
|
|
@@ -4219,9 +4239,11 @@ function createChart(element, options = {}) {
|
|
|
4219
4239
|
}
|
|
4220
4240
|
if (activeDrawingTool === "ray") {
|
|
4221
4241
|
if (draftDrawing?.type === "ray") {
|
|
4242
|
+
const anchor = draftDrawing.points[0];
|
|
4243
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4222
4244
|
const completed = normalizeDrawingState({
|
|
4223
4245
|
...serializeDrawing(draftDrawing),
|
|
4224
|
-
points: [
|
|
4246
|
+
points: [anchor, endPoint]
|
|
4225
4247
|
});
|
|
4226
4248
|
drawings.push(completed);
|
|
4227
4249
|
draftDrawing = null;
|
|
@@ -4418,6 +4440,17 @@ function createChart(element, options = {}) {
|
|
|
4418
4440
|
}
|
|
4419
4441
|
return { ...drawing, points: pts };
|
|
4420
4442
|
}
|
|
4443
|
+
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4444
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4445
|
+
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
4446
|
+
const constrained = anchor ? constrainAngleFromAnchor(anchor, x, y) : null;
|
|
4447
|
+
if (constrained) {
|
|
4448
|
+
return {
|
|
4449
|
+
...drawing,
|
|
4450
|
+
points: drawing.points.map((point, index) => index === pointIndex ? constrained : point)
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4421
4454
|
if (drawingDragState.target === "handle") {
|
|
4422
4455
|
return {
|
|
4423
4456
|
...drawing,
|
|
@@ -4506,6 +4539,7 @@ function createChart(element, options = {}) {
|
|
|
4506
4539
|
event.preventDefault();
|
|
4507
4540
|
}
|
|
4508
4541
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4542
|
+
shiftKeyActive = event.shiftKey;
|
|
4509
4543
|
const point = getCanvasPoint(event);
|
|
4510
4544
|
if (event.pointerType === "touch") {
|
|
4511
4545
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4605,7 +4639,7 @@ function createChart(element, options = {}) {
|
|
|
4605
4639
|
isDragging = true;
|
|
4606
4640
|
dragMode = region;
|
|
4607
4641
|
activePointerId = event.pointerId;
|
|
4608
|
-
const crosshairDrag =
|
|
4642
|
+
const crosshairDrag = false;
|
|
4609
4643
|
pointerDownInfo = {
|
|
4610
4644
|
pointerId: event.pointerId,
|
|
4611
4645
|
pointerType: event.pointerType,
|
|
@@ -4629,6 +4663,7 @@ function createChart(element, options = {}) {
|
|
|
4629
4663
|
event.preventDefault();
|
|
4630
4664
|
}
|
|
4631
4665
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4666
|
+
shiftKeyActive = event.shiftKey;
|
|
4632
4667
|
const point = getCanvasPoint(event);
|
|
4633
4668
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4634
4669
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4671,7 +4706,10 @@ function createChart(element, options = {}) {
|
|
|
4671
4706
|
return;
|
|
4672
4707
|
}
|
|
4673
4708
|
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4674
|
-
|
|
4709
|
+
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4710
|
+
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4711
|
+
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
|
4712
|
+
}
|
|
4675
4713
|
if (nextPoint) {
|
|
4676
4714
|
draftDrawing = {
|
|
4677
4715
|
...draftDrawing,
|
|
@@ -4986,10 +5024,11 @@ function createChart(element, options = {}) {
|
|
|
4986
5024
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
4987
5025
|
const onModifierKeyChange = (event) => {
|
|
4988
5026
|
const active = event.metaKey || event.ctrlKey;
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
5027
|
+
const shift = event.shiftKey;
|
|
5028
|
+
const changed = active !== magnetModifierActive || shift !== shiftKeyActive;
|
|
5029
|
+
magnetModifierActive = active;
|
|
5030
|
+
shiftKeyActive = shift;
|
|
5031
|
+
if (changed && draftDrawing) draw();
|
|
4993
5032
|
};
|
|
4994
5033
|
const onWindowBlurMagnet = () => {
|
|
4995
5034
|
magnetModifierActive = false;
|
package/dist/index.cjs
CHANGED
|
@@ -1007,6 +1007,7 @@ function createChart(element, options = {}) {
|
|
|
1007
1007
|
let drawingEditTextHandler = null;
|
|
1008
1008
|
let magnetMode = "none";
|
|
1009
1009
|
let magnetModifierActive = false;
|
|
1010
|
+
let shiftKeyActive = false;
|
|
1010
1011
|
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
1011
1012
|
let drawingHoverHandler = null;
|
|
1012
1013
|
let lastHoveredDrawingId = null;
|
|
@@ -3918,6 +3919,23 @@ function createChart(element, options = {}) {
|
|
|
3918
3919
|
...time ? { time: time.toISOString() } : {}
|
|
3919
3920
|
};
|
|
3920
3921
|
};
|
|
3922
|
+
const constrainAngleFromAnchor = (anchor, x, y) => {
|
|
3923
|
+
const ax = canvasXFromDrawingPoint(anchor);
|
|
3924
|
+
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
3925
|
+
const dx = x - ax;
|
|
3926
|
+
const dy = y - ay;
|
|
3927
|
+
if (dx === 0 && dy === 0) return drawingPointFromCanvas(x, y);
|
|
3928
|
+
const step = Math.PI / 4;
|
|
3929
|
+
const snappedAngle = Math.round(Math.atan2(dy, dx) / step) * step;
|
|
3930
|
+
const length = Math.hypot(dx, dy);
|
|
3931
|
+
const sx = ax + Math.cos(snappedAngle) * length;
|
|
3932
|
+
const sy = ay + Math.sin(snappedAngle) * length;
|
|
3933
|
+
const resolved = drawingPointFromCanvas(sx, sy);
|
|
3934
|
+
if (!resolved) return resolved;
|
|
3935
|
+
if (Math.abs(Math.sin(snappedAngle)) < 1e-6) return { ...resolved, price: anchor.price };
|
|
3936
|
+
if (Math.abs(Math.cos(snappedAngle)) < 1e-6) return { ...resolved, index: anchor.index };
|
|
3937
|
+
return resolved;
|
|
3938
|
+
};
|
|
3921
3939
|
const normalizeDrawingPoint = (index, price) => {
|
|
3922
3940
|
const roundedIndex = Math.round(index);
|
|
3923
3941
|
const time = getTimeForIndex(roundedIndex);
|
|
@@ -4221,9 +4239,11 @@ function createChart(element, options = {}) {
|
|
|
4221
4239
|
}
|
|
4222
4240
|
if (activeDrawingTool === "trendline") {
|
|
4223
4241
|
if (draftDrawing?.type === "trendline") {
|
|
4242
|
+
const anchor = draftDrawing.points[0];
|
|
4243
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4224
4244
|
const completed = normalizeDrawingState({
|
|
4225
4245
|
...serializeDrawing(draftDrawing),
|
|
4226
|
-
points: [
|
|
4246
|
+
points: [anchor, endPoint]
|
|
4227
4247
|
});
|
|
4228
4248
|
drawings.push(completed);
|
|
4229
4249
|
draftDrawing = null;
|
|
@@ -4245,9 +4265,11 @@ function createChart(element, options = {}) {
|
|
|
4245
4265
|
}
|
|
4246
4266
|
if (activeDrawingTool === "ray") {
|
|
4247
4267
|
if (draftDrawing?.type === "ray") {
|
|
4268
|
+
const anchor = draftDrawing.points[0];
|
|
4269
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4248
4270
|
const completed = normalizeDrawingState({
|
|
4249
4271
|
...serializeDrawing(draftDrawing),
|
|
4250
|
-
points: [
|
|
4272
|
+
points: [anchor, endPoint]
|
|
4251
4273
|
});
|
|
4252
4274
|
drawings.push(completed);
|
|
4253
4275
|
draftDrawing = null;
|
|
@@ -4444,6 +4466,17 @@ function createChart(element, options = {}) {
|
|
|
4444
4466
|
}
|
|
4445
4467
|
return { ...drawing, points: pts };
|
|
4446
4468
|
}
|
|
4469
|
+
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4470
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4471
|
+
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
4472
|
+
const constrained = anchor ? constrainAngleFromAnchor(anchor, x, y) : null;
|
|
4473
|
+
if (constrained) {
|
|
4474
|
+
return {
|
|
4475
|
+
...drawing,
|
|
4476
|
+
points: drawing.points.map((point, index) => index === pointIndex ? constrained : point)
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4447
4480
|
if (drawingDragState.target === "handle") {
|
|
4448
4481
|
return {
|
|
4449
4482
|
...drawing,
|
|
@@ -4532,6 +4565,7 @@ function createChart(element, options = {}) {
|
|
|
4532
4565
|
event.preventDefault();
|
|
4533
4566
|
}
|
|
4534
4567
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4568
|
+
shiftKeyActive = event.shiftKey;
|
|
4535
4569
|
const point = getCanvasPoint(event);
|
|
4536
4570
|
if (event.pointerType === "touch") {
|
|
4537
4571
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4631,7 +4665,7 @@ function createChart(element, options = {}) {
|
|
|
4631
4665
|
isDragging = true;
|
|
4632
4666
|
dragMode = region;
|
|
4633
4667
|
activePointerId = event.pointerId;
|
|
4634
|
-
const crosshairDrag =
|
|
4668
|
+
const crosshairDrag = false;
|
|
4635
4669
|
pointerDownInfo = {
|
|
4636
4670
|
pointerId: event.pointerId,
|
|
4637
4671
|
pointerType: event.pointerType,
|
|
@@ -4655,6 +4689,7 @@ function createChart(element, options = {}) {
|
|
|
4655
4689
|
event.preventDefault();
|
|
4656
4690
|
}
|
|
4657
4691
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4692
|
+
shiftKeyActive = event.shiftKey;
|
|
4658
4693
|
const point = getCanvasPoint(event);
|
|
4659
4694
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4660
4695
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4697,7 +4732,10 @@ function createChart(element, options = {}) {
|
|
|
4697
4732
|
return;
|
|
4698
4733
|
}
|
|
4699
4734
|
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4700
|
-
|
|
4735
|
+
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4736
|
+
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4737
|
+
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
|
4738
|
+
}
|
|
4701
4739
|
if (nextPoint) {
|
|
4702
4740
|
draftDrawing = {
|
|
4703
4741
|
...draftDrawing,
|
|
@@ -5012,10 +5050,11 @@ function createChart(element, options = {}) {
|
|
|
5012
5050
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
5013
5051
|
const onModifierKeyChange = (event) => {
|
|
5014
5052
|
const active = event.metaKey || event.ctrlKey;
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5053
|
+
const shift = event.shiftKey;
|
|
5054
|
+
const changed = active !== magnetModifierActive || shift !== shiftKeyActive;
|
|
5055
|
+
magnetModifierActive = active;
|
|
5056
|
+
shiftKeyActive = shift;
|
|
5057
|
+
if (changed && draftDrawing) draw();
|
|
5019
5058
|
};
|
|
5020
5059
|
const onWindowBlurMagnet = () => {
|
|
5021
5060
|
magnetModifierActive = false;
|
package/dist/index.js
CHANGED
|
@@ -981,6 +981,7 @@ function createChart(element, options = {}) {
|
|
|
981
981
|
let drawingEditTextHandler = null;
|
|
982
982
|
let magnetMode = "none";
|
|
983
983
|
let magnetModifierActive = false;
|
|
984
|
+
let shiftKeyActive = false;
|
|
984
985
|
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
985
986
|
let drawingHoverHandler = null;
|
|
986
987
|
let lastHoveredDrawingId = null;
|
|
@@ -3892,6 +3893,23 @@ function createChart(element, options = {}) {
|
|
|
3892
3893
|
...time ? { time: time.toISOString() } : {}
|
|
3893
3894
|
};
|
|
3894
3895
|
};
|
|
3896
|
+
const constrainAngleFromAnchor = (anchor, x, y) => {
|
|
3897
|
+
const ax = canvasXFromDrawingPoint(anchor);
|
|
3898
|
+
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
3899
|
+
const dx = x - ax;
|
|
3900
|
+
const dy = y - ay;
|
|
3901
|
+
if (dx === 0 && dy === 0) return drawingPointFromCanvas(x, y);
|
|
3902
|
+
const step = Math.PI / 4;
|
|
3903
|
+
const snappedAngle = Math.round(Math.atan2(dy, dx) / step) * step;
|
|
3904
|
+
const length = Math.hypot(dx, dy);
|
|
3905
|
+
const sx = ax + Math.cos(snappedAngle) * length;
|
|
3906
|
+
const sy = ay + Math.sin(snappedAngle) * length;
|
|
3907
|
+
const resolved = drawingPointFromCanvas(sx, sy);
|
|
3908
|
+
if (!resolved) return resolved;
|
|
3909
|
+
if (Math.abs(Math.sin(snappedAngle)) < 1e-6) return { ...resolved, price: anchor.price };
|
|
3910
|
+
if (Math.abs(Math.cos(snappedAngle)) < 1e-6) return { ...resolved, index: anchor.index };
|
|
3911
|
+
return resolved;
|
|
3912
|
+
};
|
|
3895
3913
|
const normalizeDrawingPoint = (index, price) => {
|
|
3896
3914
|
const roundedIndex = Math.round(index);
|
|
3897
3915
|
const time = getTimeForIndex(roundedIndex);
|
|
@@ -4195,9 +4213,11 @@ function createChart(element, options = {}) {
|
|
|
4195
4213
|
}
|
|
4196
4214
|
if (activeDrawingTool === "trendline") {
|
|
4197
4215
|
if (draftDrawing?.type === "trendline") {
|
|
4216
|
+
const anchor = draftDrawing.points[0];
|
|
4217
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4198
4218
|
const completed = normalizeDrawingState({
|
|
4199
4219
|
...serializeDrawing(draftDrawing),
|
|
4200
|
-
points: [
|
|
4220
|
+
points: [anchor, endPoint]
|
|
4201
4221
|
});
|
|
4202
4222
|
drawings.push(completed);
|
|
4203
4223
|
draftDrawing = null;
|
|
@@ -4219,9 +4239,11 @@ function createChart(element, options = {}) {
|
|
|
4219
4239
|
}
|
|
4220
4240
|
if (activeDrawingTool === "ray") {
|
|
4221
4241
|
if (draftDrawing?.type === "ray") {
|
|
4242
|
+
const anchor = draftDrawing.points[0];
|
|
4243
|
+
const endPoint = shiftKeyActive ? constrainAngleFromAnchor(anchor, x, y) ?? point : point;
|
|
4222
4244
|
const completed = normalizeDrawingState({
|
|
4223
4245
|
...serializeDrawing(draftDrawing),
|
|
4224
|
-
points: [
|
|
4246
|
+
points: [anchor, endPoint]
|
|
4225
4247
|
});
|
|
4226
4248
|
drawings.push(completed);
|
|
4227
4249
|
draftDrawing = null;
|
|
@@ -4418,6 +4440,17 @@ function createChart(element, options = {}) {
|
|
|
4418
4440
|
}
|
|
4419
4441
|
return { ...drawing, points: pts };
|
|
4420
4442
|
}
|
|
4443
|
+
if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
|
|
4444
|
+
const pointIndex = drawingDragState.pointIndex ?? 0;
|
|
4445
|
+
const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
|
|
4446
|
+
const constrained = anchor ? constrainAngleFromAnchor(anchor, x, y) : null;
|
|
4447
|
+
if (constrained) {
|
|
4448
|
+
return {
|
|
4449
|
+
...drawing,
|
|
4450
|
+
points: drawing.points.map((point, index) => index === pointIndex ? constrained : point)
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4421
4454
|
if (drawingDragState.target === "handle") {
|
|
4422
4455
|
return {
|
|
4423
4456
|
...drawing,
|
|
@@ -4506,6 +4539,7 @@ function createChart(element, options = {}) {
|
|
|
4506
4539
|
event.preventDefault();
|
|
4507
4540
|
}
|
|
4508
4541
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4542
|
+
shiftKeyActive = event.shiftKey;
|
|
4509
4543
|
const point = getCanvasPoint(event);
|
|
4510
4544
|
if (event.pointerType === "touch") {
|
|
4511
4545
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4605,7 +4639,7 @@ function createChart(element, options = {}) {
|
|
|
4605
4639
|
isDragging = true;
|
|
4606
4640
|
dragMode = region;
|
|
4607
4641
|
activePointerId = event.pointerId;
|
|
4608
|
-
const crosshairDrag =
|
|
4642
|
+
const crosshairDrag = false;
|
|
4609
4643
|
pointerDownInfo = {
|
|
4610
4644
|
pointerId: event.pointerId,
|
|
4611
4645
|
pointerType: event.pointerType,
|
|
@@ -4629,6 +4663,7 @@ function createChart(element, options = {}) {
|
|
|
4629
4663
|
event.preventDefault();
|
|
4630
4664
|
}
|
|
4631
4665
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4666
|
+
shiftKeyActive = event.shiftKey;
|
|
4632
4667
|
const point = getCanvasPoint(event);
|
|
4633
4668
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4634
4669
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4671,7 +4706,10 @@ function createChart(element, options = {}) {
|
|
|
4671
4706
|
return;
|
|
4672
4707
|
}
|
|
4673
4708
|
if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
|
|
4674
|
-
|
|
4709
|
+
let nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
4710
|
+
if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
|
|
4711
|
+
nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
|
|
4712
|
+
}
|
|
4675
4713
|
if (nextPoint) {
|
|
4676
4714
|
draftDrawing = {
|
|
4677
4715
|
...draftDrawing,
|
|
@@ -4986,10 +5024,11 @@ function createChart(element, options = {}) {
|
|
|
4986
5024
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
4987
5025
|
const onModifierKeyChange = (event) => {
|
|
4988
5026
|
const active = event.metaKey || event.ctrlKey;
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
5027
|
+
const shift = event.shiftKey;
|
|
5028
|
+
const changed = active !== magnetModifierActive || shift !== shiftKeyActive;
|
|
5029
|
+
magnetModifierActive = active;
|
|
5030
|
+
shiftKeyActive = shift;
|
|
5031
|
+
if (changed && draftDrawing) draw();
|
|
4993
5032
|
};
|
|
4994
5033
|
const onWindowBlurMagnet = () => {
|
|
4995
5034
|
magnetModifierActive = false;
|
package/docs/API.md
CHANGED
|
@@ -486,6 +486,7 @@ Use `getDrawings()` / `setDrawings()` for persistence.
|
|
|
486
486
|
- `onDrawingEditText(handler): void` (fires when a `text`/`note` tool is placed or double-clicked; use it to show an inline text editor at `{x, y}` and write the result back via `updateDrawing(id, { label })`). `text`/`note` drawings store their content in `label` and size in `fontSize`.
|
|
487
487
|
- `setTradeMarkers(markers: TradeMarkerOptions[]): void` — draw trade execution markers (arrow + `qty @ price` label) on the candle of each fill's `time`. Buy = arrow below the low pointing up; sell = arrow above the high pointing down. Each marker: `{ time (ms|ISO), price, side: "buy"|"sell", qty?, text?, color?, textColor? }` (defaults: buy `#2962ff`, sell `#f23645`, text `#d1d4dc`). Markers resolve to bars by time, so they persist across timeframe switches and scrolling. Pass `[]` to clear.
|
|
488
488
|
- `cancelDrawing(): boolean` — abort an in-progress multi-click drawing (between the first click and the final point, e.g. a half-drawn trendline/ray/fib). Returns `true` if a draft was cancelled. Wire it to Escape.
|
|
489
|
+
- Hold **Shift** while drawing or dragging an endpoint of a `trendline`/`ray` to constrain it to the nearest 0/45/90° angle (e.g. a perfectly horizontal line).
|
|
489
490
|
- `setMagnetMode(mode): void` / `getMagnetMode()` — magnet/snap for all drawing tools. `"none"` off, `"weak"` snaps to a candle's OHLC only when the cursor is near a value, `"strong"` always snaps to the nearest OHLC of the bar under the cursor. Holding Cmd/Ctrl while drawing/dragging forces strong snapping regardless of the mode.
|
|
490
491
|
- `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
|
|
491
492
|
- `getActiveDrawingTool(): DrawingToolType | null`
|