hyperprop-charting-library 0.1.89 → 0.1.91
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 +46 -4
- package/dist/hyperprop-charting-library.d.ts +3 -0
- package/dist/hyperprop-charting-library.js +46 -4
- package/dist/index.cjs +46 -4
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +46 -4
- package/docs/API.md +2 -0
- package/package.json +1 -1
|
@@ -1000,6 +1000,9 @@ function createChart(element, options = {}) {
|
|
|
1000
1000
|
let drawingSelectHandler = null;
|
|
1001
1001
|
let drawingDoubleClickHandler = null;
|
|
1002
1002
|
let drawingEditTextHandler = null;
|
|
1003
|
+
let magnetMode = "none";
|
|
1004
|
+
let magnetModifierActive = false;
|
|
1005
|
+
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
1003
1006
|
let drawingHoverHandler = null;
|
|
1004
1007
|
let lastHoveredDrawingId = null;
|
|
1005
1008
|
let selectedDrawingId = null;
|
|
@@ -3724,17 +3727,39 @@ function createChart(element, options = {}) {
|
|
|
3724
3727
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
3725
3728
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
3726
3729
|
};
|
|
3730
|
+
const applyMagnet = (y, index, price) => {
|
|
3731
|
+
const effective = magnetModifierActive ? "strong" : magnetMode;
|
|
3732
|
+
if (effective === "none") return { index, price };
|
|
3733
|
+
const barIndex = Math.round(index);
|
|
3734
|
+
const bar = data[barIndex];
|
|
3735
|
+
if (!bar) return { index, price };
|
|
3736
|
+
const candidates = [bar.o, bar.h, bar.l, bar.c];
|
|
3737
|
+
let nearest = bar.c;
|
|
3738
|
+
let bestDiff = Infinity;
|
|
3739
|
+
for (const candidate of candidates) {
|
|
3740
|
+
const diff = Math.abs(candidate - price);
|
|
3741
|
+
if (diff < bestDiff) {
|
|
3742
|
+
bestDiff = diff;
|
|
3743
|
+
nearest = candidate;
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
|
|
3747
|
+
return { index, price };
|
|
3748
|
+
}
|
|
3749
|
+
return { index: barIndex, price: nearest };
|
|
3750
|
+
};
|
|
3727
3751
|
const drawingPointFromCanvas = (x, y) => {
|
|
3728
3752
|
if (!drawState) {
|
|
3729
3753
|
return null;
|
|
3730
3754
|
}
|
|
3731
3755
|
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
3732
|
-
const
|
|
3733
|
-
const
|
|
3756
|
+
const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
|
|
3757
|
+
const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
|
|
3758
|
+
const nearestIndex = Math.round(snapped.index);
|
|
3734
3759
|
const time = getTimeForIndex(nearestIndex);
|
|
3735
3760
|
return {
|
|
3736
|
-
index,
|
|
3737
|
-
price: roundToPricePrecision(
|
|
3761
|
+
index: snapped.index,
|
|
3762
|
+
price: roundToPricePrecision(snapped.price),
|
|
3738
3763
|
...time ? { time: time.toISOString() } : {}
|
|
3739
3764
|
};
|
|
3740
3765
|
};
|
|
@@ -4351,6 +4376,7 @@ function createChart(element, options = {}) {
|
|
|
4351
4376
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4352
4377
|
event.preventDefault();
|
|
4353
4378
|
}
|
|
4379
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4354
4380
|
const point = getCanvasPoint(event);
|
|
4355
4381
|
if (event.pointerType === "touch") {
|
|
4356
4382
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4473,6 +4499,7 @@ function createChart(element, options = {}) {
|
|
|
4473
4499
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4474
4500
|
event.preventDefault();
|
|
4475
4501
|
}
|
|
4502
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4476
4503
|
const point = getCanvasPoint(event);
|
|
4477
4504
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4478
4505
|
touchPointers.set(event.pointerId, point);
|
|
@@ -5067,6 +5094,18 @@ function createChart(element, options = {}) {
|
|
|
5067
5094
|
draw();
|
|
5068
5095
|
};
|
|
5069
5096
|
const getActiveDrawingTool = () => activeDrawingTool;
|
|
5097
|
+
const cancelDrawing = () => {
|
|
5098
|
+
if (draftDrawing) {
|
|
5099
|
+
draftDrawing = null;
|
|
5100
|
+
draw();
|
|
5101
|
+
return true;
|
|
5102
|
+
}
|
|
5103
|
+
return false;
|
|
5104
|
+
};
|
|
5105
|
+
const setMagnetMode = (mode) => {
|
|
5106
|
+
magnetMode = mode;
|
|
5107
|
+
};
|
|
5108
|
+
const getMagnetMode = () => magnetMode;
|
|
5070
5109
|
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
5071
5110
|
const setDrawings = (nextDrawings) => {
|
|
5072
5111
|
drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
|
|
@@ -5175,6 +5214,9 @@ function createChart(element, options = {}) {
|
|
|
5175
5214
|
onDrawingSelect,
|
|
5176
5215
|
onDrawingDoubleClick,
|
|
5177
5216
|
onDrawingEditText,
|
|
5217
|
+
setMagnetMode,
|
|
5218
|
+
getMagnetMode,
|
|
5219
|
+
cancelDrawing,
|
|
5178
5220
|
setSelectedDrawing,
|
|
5179
5221
|
onDrawingHover,
|
|
5180
5222
|
setDrawingDefaults,
|
|
@@ -430,6 +430,9 @@ interface ChartInstance {
|
|
|
430
430
|
onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
431
431
|
onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
432
432
|
onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
433
|
+
setMagnetMode: (mode: "none" | "weak" | "strong") => void;
|
|
434
|
+
getMagnetMode: () => "none" | "weak" | "strong";
|
|
435
|
+
cancelDrawing: () => boolean;
|
|
433
436
|
onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
|
|
434
437
|
setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
|
|
435
438
|
setSelectedDrawing: (id: string | null) => void;
|
|
@@ -974,6 +974,9 @@ function createChart(element, options = {}) {
|
|
|
974
974
|
let drawingSelectHandler = null;
|
|
975
975
|
let drawingDoubleClickHandler = null;
|
|
976
976
|
let drawingEditTextHandler = null;
|
|
977
|
+
let magnetMode = "none";
|
|
978
|
+
let magnetModifierActive = false;
|
|
979
|
+
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
977
980
|
let drawingHoverHandler = null;
|
|
978
981
|
let lastHoveredDrawingId = null;
|
|
979
982
|
let selectedDrawingId = null;
|
|
@@ -3698,17 +3701,39 @@ function createChart(element, options = {}) {
|
|
|
3698
3701
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
3699
3702
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
3700
3703
|
};
|
|
3704
|
+
const applyMagnet = (y, index, price) => {
|
|
3705
|
+
const effective = magnetModifierActive ? "strong" : magnetMode;
|
|
3706
|
+
if (effective === "none") return { index, price };
|
|
3707
|
+
const barIndex = Math.round(index);
|
|
3708
|
+
const bar = data[barIndex];
|
|
3709
|
+
if (!bar) return { index, price };
|
|
3710
|
+
const candidates = [bar.o, bar.h, bar.l, bar.c];
|
|
3711
|
+
let nearest = bar.c;
|
|
3712
|
+
let bestDiff = Infinity;
|
|
3713
|
+
for (const candidate of candidates) {
|
|
3714
|
+
const diff = Math.abs(candidate - price);
|
|
3715
|
+
if (diff < bestDiff) {
|
|
3716
|
+
bestDiff = diff;
|
|
3717
|
+
nearest = candidate;
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
|
|
3721
|
+
return { index, price };
|
|
3722
|
+
}
|
|
3723
|
+
return { index: barIndex, price: nearest };
|
|
3724
|
+
};
|
|
3701
3725
|
const drawingPointFromCanvas = (x, y) => {
|
|
3702
3726
|
if (!drawState) {
|
|
3703
3727
|
return null;
|
|
3704
3728
|
}
|
|
3705
3729
|
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
3706
|
-
const
|
|
3707
|
-
const
|
|
3730
|
+
const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
|
|
3731
|
+
const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
|
|
3732
|
+
const nearestIndex = Math.round(snapped.index);
|
|
3708
3733
|
const time = getTimeForIndex(nearestIndex);
|
|
3709
3734
|
return {
|
|
3710
|
-
index,
|
|
3711
|
-
price: roundToPricePrecision(
|
|
3735
|
+
index: snapped.index,
|
|
3736
|
+
price: roundToPricePrecision(snapped.price),
|
|
3712
3737
|
...time ? { time: time.toISOString() } : {}
|
|
3713
3738
|
};
|
|
3714
3739
|
};
|
|
@@ -4325,6 +4350,7 @@ function createChart(element, options = {}) {
|
|
|
4325
4350
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4326
4351
|
event.preventDefault();
|
|
4327
4352
|
}
|
|
4353
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4328
4354
|
const point = getCanvasPoint(event);
|
|
4329
4355
|
if (event.pointerType === "touch") {
|
|
4330
4356
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4447,6 +4473,7 @@ function createChart(element, options = {}) {
|
|
|
4447
4473
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4448
4474
|
event.preventDefault();
|
|
4449
4475
|
}
|
|
4476
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4450
4477
|
const point = getCanvasPoint(event);
|
|
4451
4478
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4452
4479
|
touchPointers.set(event.pointerId, point);
|
|
@@ -5041,6 +5068,18 @@ function createChart(element, options = {}) {
|
|
|
5041
5068
|
draw();
|
|
5042
5069
|
};
|
|
5043
5070
|
const getActiveDrawingTool = () => activeDrawingTool;
|
|
5071
|
+
const cancelDrawing = () => {
|
|
5072
|
+
if (draftDrawing) {
|
|
5073
|
+
draftDrawing = null;
|
|
5074
|
+
draw();
|
|
5075
|
+
return true;
|
|
5076
|
+
}
|
|
5077
|
+
return false;
|
|
5078
|
+
};
|
|
5079
|
+
const setMagnetMode = (mode) => {
|
|
5080
|
+
magnetMode = mode;
|
|
5081
|
+
};
|
|
5082
|
+
const getMagnetMode = () => magnetMode;
|
|
5044
5083
|
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
5045
5084
|
const setDrawings = (nextDrawings) => {
|
|
5046
5085
|
drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
|
|
@@ -5149,6 +5188,9 @@ function createChart(element, options = {}) {
|
|
|
5149
5188
|
onDrawingSelect,
|
|
5150
5189
|
onDrawingDoubleClick,
|
|
5151
5190
|
onDrawingEditText,
|
|
5191
|
+
setMagnetMode,
|
|
5192
|
+
getMagnetMode,
|
|
5193
|
+
cancelDrawing,
|
|
5152
5194
|
setSelectedDrawing,
|
|
5153
5195
|
onDrawingHover,
|
|
5154
5196
|
setDrawingDefaults,
|
package/dist/index.cjs
CHANGED
|
@@ -1000,6 +1000,9 @@ function createChart(element, options = {}) {
|
|
|
1000
1000
|
let drawingSelectHandler = null;
|
|
1001
1001
|
let drawingDoubleClickHandler = null;
|
|
1002
1002
|
let drawingEditTextHandler = null;
|
|
1003
|
+
let magnetMode = "none";
|
|
1004
|
+
let magnetModifierActive = false;
|
|
1005
|
+
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
1003
1006
|
let drawingHoverHandler = null;
|
|
1004
1007
|
let lastHoveredDrawingId = null;
|
|
1005
1008
|
let selectedDrawingId = null;
|
|
@@ -3724,17 +3727,39 @@ function createChart(element, options = {}) {
|
|
|
3724
3727
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
3725
3728
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
3726
3729
|
};
|
|
3730
|
+
const applyMagnet = (y, index, price) => {
|
|
3731
|
+
const effective = magnetModifierActive ? "strong" : magnetMode;
|
|
3732
|
+
if (effective === "none") return { index, price };
|
|
3733
|
+
const barIndex = Math.round(index);
|
|
3734
|
+
const bar = data[barIndex];
|
|
3735
|
+
if (!bar) return { index, price };
|
|
3736
|
+
const candidates = [bar.o, bar.h, bar.l, bar.c];
|
|
3737
|
+
let nearest = bar.c;
|
|
3738
|
+
let bestDiff = Infinity;
|
|
3739
|
+
for (const candidate of candidates) {
|
|
3740
|
+
const diff = Math.abs(candidate - price);
|
|
3741
|
+
if (diff < bestDiff) {
|
|
3742
|
+
bestDiff = diff;
|
|
3743
|
+
nearest = candidate;
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
|
|
3747
|
+
return { index, price };
|
|
3748
|
+
}
|
|
3749
|
+
return { index: barIndex, price: nearest };
|
|
3750
|
+
};
|
|
3727
3751
|
const drawingPointFromCanvas = (x, y) => {
|
|
3728
3752
|
if (!drawState) {
|
|
3729
3753
|
return null;
|
|
3730
3754
|
}
|
|
3731
3755
|
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
3732
|
-
const
|
|
3733
|
-
const
|
|
3756
|
+
const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
|
|
3757
|
+
const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
|
|
3758
|
+
const nearestIndex = Math.round(snapped.index);
|
|
3734
3759
|
const time = getTimeForIndex(nearestIndex);
|
|
3735
3760
|
return {
|
|
3736
|
-
index,
|
|
3737
|
-
price: roundToPricePrecision(
|
|
3761
|
+
index: snapped.index,
|
|
3762
|
+
price: roundToPricePrecision(snapped.price),
|
|
3738
3763
|
...time ? { time: time.toISOString() } : {}
|
|
3739
3764
|
};
|
|
3740
3765
|
};
|
|
@@ -4351,6 +4376,7 @@ function createChart(element, options = {}) {
|
|
|
4351
4376
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4352
4377
|
event.preventDefault();
|
|
4353
4378
|
}
|
|
4379
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4354
4380
|
const point = getCanvasPoint(event);
|
|
4355
4381
|
if (event.pointerType === "touch") {
|
|
4356
4382
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4473,6 +4499,7 @@ function createChart(element, options = {}) {
|
|
|
4473
4499
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4474
4500
|
event.preventDefault();
|
|
4475
4501
|
}
|
|
4502
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4476
4503
|
const point = getCanvasPoint(event);
|
|
4477
4504
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4478
4505
|
touchPointers.set(event.pointerId, point);
|
|
@@ -5067,6 +5094,18 @@ function createChart(element, options = {}) {
|
|
|
5067
5094
|
draw();
|
|
5068
5095
|
};
|
|
5069
5096
|
const getActiveDrawingTool = () => activeDrawingTool;
|
|
5097
|
+
const cancelDrawing = () => {
|
|
5098
|
+
if (draftDrawing) {
|
|
5099
|
+
draftDrawing = null;
|
|
5100
|
+
draw();
|
|
5101
|
+
return true;
|
|
5102
|
+
}
|
|
5103
|
+
return false;
|
|
5104
|
+
};
|
|
5105
|
+
const setMagnetMode = (mode) => {
|
|
5106
|
+
magnetMode = mode;
|
|
5107
|
+
};
|
|
5108
|
+
const getMagnetMode = () => magnetMode;
|
|
5070
5109
|
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
5071
5110
|
const setDrawings = (nextDrawings) => {
|
|
5072
5111
|
drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
|
|
@@ -5175,6 +5214,9 @@ function createChart(element, options = {}) {
|
|
|
5175
5214
|
onDrawingSelect,
|
|
5176
5215
|
onDrawingDoubleClick,
|
|
5177
5216
|
onDrawingEditText,
|
|
5217
|
+
setMagnetMode,
|
|
5218
|
+
getMagnetMode,
|
|
5219
|
+
cancelDrawing,
|
|
5178
5220
|
setSelectedDrawing,
|
|
5179
5221
|
onDrawingHover,
|
|
5180
5222
|
setDrawingDefaults,
|
package/dist/index.d.cts
CHANGED
|
@@ -430,6 +430,9 @@ interface ChartInstance {
|
|
|
430
430
|
onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
431
431
|
onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
432
432
|
onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
433
|
+
setMagnetMode: (mode: "none" | "weak" | "strong") => void;
|
|
434
|
+
getMagnetMode: () => "none" | "weak" | "strong";
|
|
435
|
+
cancelDrawing: () => boolean;
|
|
433
436
|
onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
|
|
434
437
|
setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
|
|
435
438
|
setSelectedDrawing: (id: string | null) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -430,6 +430,9 @@ interface ChartInstance {
|
|
|
430
430
|
onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
431
431
|
onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
432
432
|
onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
433
|
+
setMagnetMode: (mode: "none" | "weak" | "strong") => void;
|
|
434
|
+
getMagnetMode: () => "none" | "weak" | "strong";
|
|
435
|
+
cancelDrawing: () => boolean;
|
|
433
436
|
onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
|
|
434
437
|
setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
|
|
435
438
|
setSelectedDrawing: (id: string | null) => void;
|
package/dist/index.js
CHANGED
|
@@ -974,6 +974,9 @@ function createChart(element, options = {}) {
|
|
|
974
974
|
let drawingSelectHandler = null;
|
|
975
975
|
let drawingDoubleClickHandler = null;
|
|
976
976
|
let drawingEditTextHandler = null;
|
|
977
|
+
let magnetMode = "none";
|
|
978
|
+
let magnetModifierActive = false;
|
|
979
|
+
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
977
980
|
let drawingHoverHandler = null;
|
|
978
981
|
let lastHoveredDrawingId = null;
|
|
979
982
|
let selectedDrawingId = null;
|
|
@@ -3698,17 +3701,39 @@ function createChart(element, options = {}) {
|
|
|
3698
3701
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
3699
3702
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
3700
3703
|
};
|
|
3704
|
+
const applyMagnet = (y, index, price) => {
|
|
3705
|
+
const effective = magnetModifierActive ? "strong" : magnetMode;
|
|
3706
|
+
if (effective === "none") return { index, price };
|
|
3707
|
+
const barIndex = Math.round(index);
|
|
3708
|
+
const bar = data[barIndex];
|
|
3709
|
+
if (!bar) return { index, price };
|
|
3710
|
+
const candidates = [bar.o, bar.h, bar.l, bar.c];
|
|
3711
|
+
let nearest = bar.c;
|
|
3712
|
+
let bestDiff = Infinity;
|
|
3713
|
+
for (const candidate of candidates) {
|
|
3714
|
+
const diff = Math.abs(candidate - price);
|
|
3715
|
+
if (diff < bestDiff) {
|
|
3716
|
+
bestDiff = diff;
|
|
3717
|
+
nearest = candidate;
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
|
|
3721
|
+
return { index, price };
|
|
3722
|
+
}
|
|
3723
|
+
return { index: barIndex, price: nearest };
|
|
3724
|
+
};
|
|
3701
3725
|
const drawingPointFromCanvas = (x, y) => {
|
|
3702
3726
|
if (!drawState) {
|
|
3703
3727
|
return null;
|
|
3704
3728
|
}
|
|
3705
3729
|
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
3706
|
-
const
|
|
3707
|
-
const
|
|
3730
|
+
const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
|
|
3731
|
+
const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
|
|
3732
|
+
const nearestIndex = Math.round(snapped.index);
|
|
3708
3733
|
const time = getTimeForIndex(nearestIndex);
|
|
3709
3734
|
return {
|
|
3710
|
-
index,
|
|
3711
|
-
price: roundToPricePrecision(
|
|
3735
|
+
index: snapped.index,
|
|
3736
|
+
price: roundToPricePrecision(snapped.price),
|
|
3712
3737
|
...time ? { time: time.toISOString() } : {}
|
|
3713
3738
|
};
|
|
3714
3739
|
};
|
|
@@ -4325,6 +4350,7 @@ function createChart(element, options = {}) {
|
|
|
4325
4350
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4326
4351
|
event.preventDefault();
|
|
4327
4352
|
}
|
|
4353
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4328
4354
|
const point = getCanvasPoint(event);
|
|
4329
4355
|
if (event.pointerType === "touch") {
|
|
4330
4356
|
touchPointers.set(event.pointerId, point);
|
|
@@ -4447,6 +4473,7 @@ function createChart(element, options = {}) {
|
|
|
4447
4473
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
4448
4474
|
event.preventDefault();
|
|
4449
4475
|
}
|
|
4476
|
+
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
4450
4477
|
const point = getCanvasPoint(event);
|
|
4451
4478
|
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
4452
4479
|
touchPointers.set(event.pointerId, point);
|
|
@@ -5041,6 +5068,18 @@ function createChart(element, options = {}) {
|
|
|
5041
5068
|
draw();
|
|
5042
5069
|
};
|
|
5043
5070
|
const getActiveDrawingTool = () => activeDrawingTool;
|
|
5071
|
+
const cancelDrawing = () => {
|
|
5072
|
+
if (draftDrawing) {
|
|
5073
|
+
draftDrawing = null;
|
|
5074
|
+
draw();
|
|
5075
|
+
return true;
|
|
5076
|
+
}
|
|
5077
|
+
return false;
|
|
5078
|
+
};
|
|
5079
|
+
const setMagnetMode = (mode) => {
|
|
5080
|
+
magnetMode = mode;
|
|
5081
|
+
};
|
|
5082
|
+
const getMagnetMode = () => magnetMode;
|
|
5044
5083
|
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
5045
5084
|
const setDrawings = (nextDrawings) => {
|
|
5046
5085
|
drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
|
|
@@ -5149,6 +5188,9 @@ function createChart(element, options = {}) {
|
|
|
5149
5188
|
onDrawingSelect,
|
|
5150
5189
|
onDrawingDoubleClick,
|
|
5151
5190
|
onDrawingEditText,
|
|
5191
|
+
setMagnetMode,
|
|
5192
|
+
getMagnetMode,
|
|
5193
|
+
cancelDrawing,
|
|
5152
5194
|
setSelectedDrawing,
|
|
5153
5195
|
onDrawingHover,
|
|
5154
5196
|
setDrawingDefaults,
|
package/docs/API.md
CHANGED
|
@@ -484,6 +484,8 @@ Use `getDrawings()` / `setDrawings()` for persistence.
|
|
|
484
484
|
- `setSelectedDrawing(id: string | null): void` (marks a drawing selected; only the selected/drafted drawing renders its handles, and position tools render their lines/labels only while selected)
|
|
485
485
|
- `onDrawingDoubleClick(handler): void` (fires when a drawing is double-clicked; use it to open a settings dialog, e.g. for position tools)
|
|
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
|
+
- `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.
|
|
488
|
+
- `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.
|
|
487
489
|
- `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
|
|
488
490
|
- `getActiveDrawingTool(): DrawingToolType | null`
|
|
489
491
|
- `setDrawings(drawings: DrawingObjectOptions[]): void`
|