hyperprop-charting-library 0.1.8 → 0.1.9
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 +245 -38
- package/dist/hyperprop-charting-library.d.ts +31 -1
- package/dist/hyperprop-charting-library.js +245 -38
- package/dist/index.cjs +106 -6
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +106 -6
- package/docs/API.md +5 -0
- package/docs/EVENTS.md +33 -0
- package/package.json +1 -1
|
@@ -24,15 +24,15 @@ __export(index_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
26
|
var DEFAULT_GRID_OPTIONS = {
|
|
27
|
-
color: "#
|
|
28
|
-
opacity: 0.
|
|
27
|
+
color: "#2b2f38",
|
|
28
|
+
opacity: 0.38,
|
|
29
29
|
horizontalLines: true,
|
|
30
30
|
verticalLines: true,
|
|
31
31
|
horizontalTickCount: 5
|
|
32
32
|
};
|
|
33
33
|
var DEFAULT_AXIS_OPTIONS = {
|
|
34
|
-
lineColor: "#
|
|
35
|
-
textColor: "#
|
|
34
|
+
lineColor: "#3b3f47",
|
|
35
|
+
textColor: "#a9adb6",
|
|
36
36
|
fontSize: 12,
|
|
37
37
|
lineWidth: 1
|
|
38
38
|
};
|
|
@@ -42,24 +42,39 @@ var DEFAULT_CROSSHAIR_OPTIONS = {
|
|
|
42
42
|
width: 1,
|
|
43
43
|
style: "dotted",
|
|
44
44
|
showHorizontal: true,
|
|
45
|
-
showVertical: true
|
|
45
|
+
showVertical: true,
|
|
46
|
+
showPriceLabel: true,
|
|
47
|
+
showTimeLabel: true,
|
|
48
|
+
timeLabelFormat: "auto",
|
|
49
|
+
labelBackgroundColor: "#0b1220",
|
|
50
|
+
labelTextColor: "#cbd5e1",
|
|
51
|
+
labelBorderRadius: 3,
|
|
52
|
+
labelBorderColor: "#94a3b8",
|
|
53
|
+
labelBorderWidth: 1,
|
|
54
|
+
labelBorderStyle: "solid"
|
|
46
55
|
};
|
|
47
56
|
var DEFAULT_WATERMARK_OPTIONS = {
|
|
48
57
|
visible: false,
|
|
49
58
|
text: "",
|
|
50
|
-
color: "#
|
|
51
|
-
opacity: 0.
|
|
59
|
+
color: "#81858d",
|
|
60
|
+
opacity: 0.14,
|
|
52
61
|
fontSize: 92,
|
|
53
62
|
fontWeight: 700,
|
|
54
|
-
thickness: 0
|
|
63
|
+
thickness: 0,
|
|
64
|
+
imageSrc: "",
|
|
65
|
+
imageScale: 1,
|
|
66
|
+
imageMaxWidthRatio: 0.42,
|
|
67
|
+
imageMaxHeightRatio: 0.3,
|
|
68
|
+
imageTintColor: "",
|
|
69
|
+
imageTintOpacity: 1
|
|
55
70
|
};
|
|
56
71
|
var DEFAULT_PRICE_LINE_OPTIONS = {
|
|
57
72
|
visible: true,
|
|
58
73
|
style: "solid",
|
|
59
74
|
thickness: 1,
|
|
60
|
-
color: "#
|
|
61
|
-
labelBackgroundColor: "#
|
|
62
|
-
labelTextColor: "#
|
|
75
|
+
color: "#38bdf8",
|
|
76
|
+
labelBackgroundColor: "#0b1220",
|
|
77
|
+
labelTextColor: "#60a5fa",
|
|
63
78
|
labelBorderRadius: 3,
|
|
64
79
|
showLabel: true
|
|
65
80
|
};
|
|
@@ -68,9 +83,9 @@ var DEFAULT_ORDER_LINE_OPTIONS = {
|
|
|
68
83
|
behavior: "static",
|
|
69
84
|
style: "solid",
|
|
70
85
|
thickness: 1,
|
|
71
|
-
color: "
|
|
72
|
-
labelBackgroundColor: "#
|
|
73
|
-
labelTextColor: "#
|
|
86
|
+
color: "rgba(59,130,246,0.8)",
|
|
87
|
+
labelBackgroundColor: "#0b1220",
|
|
88
|
+
labelTextColor: "#60a5fa",
|
|
74
89
|
labelBorderRadius: 3,
|
|
75
90
|
showCloseButton: true,
|
|
76
91
|
widgetPosition: "left",
|
|
@@ -97,12 +112,17 @@ var DEFAULT_ORDER_LINE_OPTIONS = {
|
|
|
97
112
|
var DEFAULT_OPTIONS = {
|
|
98
113
|
width: 720,
|
|
99
114
|
height: 360,
|
|
100
|
-
backgroundColor: "#
|
|
101
|
-
axisColor: "#
|
|
115
|
+
backgroundColor: "#101114",
|
|
116
|
+
axisColor: "#7f8289",
|
|
102
117
|
axis: DEFAULT_AXIS_OPTIONS,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
priceDecimals: 2,
|
|
119
|
+
initialViewport: "latest",
|
|
120
|
+
initialVisibleBars: 60,
|
|
121
|
+
rightEdgePaddingBars: 2,
|
|
122
|
+
preserveViewportOnDataUpdate: true,
|
|
123
|
+
upColor: "#2fb171",
|
|
124
|
+
downColor: "#d35a5a",
|
|
125
|
+
gridColor: "#252932",
|
|
106
126
|
fontFamily: "Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif",
|
|
107
127
|
candleBodyWidthRatio: 0.7,
|
|
108
128
|
candleMinWidth: 0.5,
|
|
@@ -118,12 +138,12 @@ var DEFAULT_OPTIONS = {
|
|
|
118
138
|
orderLines: [],
|
|
119
139
|
tickerLine: {
|
|
120
140
|
visible: true,
|
|
121
|
-
style: "
|
|
141
|
+
style: "dotted",
|
|
122
142
|
thickness: 1,
|
|
123
|
-
color: "#
|
|
124
|
-
labelBackgroundColor: "#
|
|
143
|
+
color: "#38bdf8",
|
|
144
|
+
labelBackgroundColor: "#38bdf8",
|
|
125
145
|
labelTextColor: "#0b1220",
|
|
126
|
-
labelBorderRadius:
|
|
146
|
+
labelBorderRadius: 3
|
|
127
147
|
}
|
|
128
148
|
};
|
|
129
149
|
function createChart(element, options = {}) {
|
|
@@ -165,6 +185,7 @@ function createChart(element, options = {}) {
|
|
|
165
185
|
}));
|
|
166
186
|
let orderActionHandler = null;
|
|
167
187
|
let chartClickHandler = null;
|
|
188
|
+
let crosshairMoveHandler = null;
|
|
168
189
|
let orderActionRegions = [];
|
|
169
190
|
let orderDragRegions = [];
|
|
170
191
|
let generatedPriceLineId = 1;
|
|
@@ -177,6 +198,9 @@ function createChart(element, options = {}) {
|
|
|
177
198
|
let yMaxOverride = null;
|
|
178
199
|
let autoYMin = null;
|
|
179
200
|
let autoYMax = null;
|
|
201
|
+
let watermarkImageSrc = null;
|
|
202
|
+
let watermarkImage = null;
|
|
203
|
+
let watermarkImageReady = false;
|
|
180
204
|
let drawState = null;
|
|
181
205
|
let orderDragState = null;
|
|
182
206
|
let actionDragState = null;
|
|
@@ -195,13 +219,36 @@ function createChart(element, options = {}) {
|
|
|
195
219
|
element.appendChild(canvas);
|
|
196
220
|
const margin = { top: 16, right: 72, bottom: 34, left: 12 };
|
|
197
221
|
const maxPanBars = 1e6;
|
|
198
|
-
const rightEdgePaddingBars =
|
|
222
|
+
const rightEdgePaddingBars = Math.max(0, mergedOptions.rightEdgePaddingBars);
|
|
199
223
|
const getPixelRatio = () => {
|
|
200
224
|
if (typeof window === "undefined") {
|
|
201
225
|
return 1;
|
|
202
226
|
}
|
|
203
227
|
return Math.max(1, window.devicePixelRatio || 1);
|
|
204
228
|
};
|
|
229
|
+
const ensureWatermarkImage = (src) => {
|
|
230
|
+
if (src === watermarkImageSrc) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
watermarkImageSrc = src;
|
|
234
|
+
watermarkImageReady = false;
|
|
235
|
+
watermarkImage = null;
|
|
236
|
+
if (!src) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const image = new Image();
|
|
240
|
+
image.crossOrigin = "anonymous";
|
|
241
|
+
image.onload = () => {
|
|
242
|
+
watermarkImage = image;
|
|
243
|
+
watermarkImageReady = true;
|
|
244
|
+
draw();
|
|
245
|
+
};
|
|
246
|
+
image.onerror = () => {
|
|
247
|
+
watermarkImage = null;
|
|
248
|
+
watermarkImageReady = false;
|
|
249
|
+
};
|
|
250
|
+
image.src = src;
|
|
251
|
+
};
|
|
205
252
|
const crisp = (value) => Math.round(value) + 0.5;
|
|
206
253
|
const clamp = (value, min, max) => {
|
|
207
254
|
return Math.min(max, Math.max(min, value));
|
|
@@ -225,8 +272,13 @@ function createChart(element, options = {}) {
|
|
|
225
272
|
xSpan = 60;
|
|
226
273
|
return;
|
|
227
274
|
}
|
|
228
|
-
|
|
229
|
-
|
|
275
|
+
const requestedVisibleBars = Math.max(5, Math.floor(mergedOptions.initialVisibleBars));
|
|
276
|
+
xSpan = Math.min(requestedVisibleBars, Math.max(5, count));
|
|
277
|
+
if (mergedOptions.initialViewport === "center") {
|
|
278
|
+
xCenter = count / 2;
|
|
279
|
+
} else {
|
|
280
|
+
xCenter = count - xSpan / 2 + rightEdgePaddingBars;
|
|
281
|
+
}
|
|
230
282
|
clampXViewport();
|
|
231
283
|
};
|
|
232
284
|
const getYBounds = () => {
|
|
@@ -266,13 +318,8 @@ function createChart(element, options = {}) {
|
|
|
266
318
|
return { min: nextMin, max: nextMax };
|
|
267
319
|
};
|
|
268
320
|
const formatPrice = (price) => {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
if (price >= 100) {
|
|
273
|
-
return price.toFixed(1);
|
|
274
|
-
}
|
|
275
|
-
return price.toFixed(2);
|
|
321
|
+
const decimals = clamp(Math.round(mergedOptions.priceDecimals), 0, 8);
|
|
322
|
+
return price.toFixed(decimals);
|
|
276
323
|
};
|
|
277
324
|
const parseData = (nextData) => {
|
|
278
325
|
return nextData.map((point) => ({
|
|
@@ -326,6 +373,42 @@ function createChart(element, options = {}) {
|
|
|
326
373
|
const extra = index - (data.length - 1);
|
|
327
374
|
return new Date(last.time.getTime() + extra * stepMs);
|
|
328
375
|
};
|
|
376
|
+
const formatHoverTimeLabel = (time, mode) => {
|
|
377
|
+
if (mode === "time") {
|
|
378
|
+
return time.toLocaleTimeString(void 0, {
|
|
379
|
+
hour: "2-digit",
|
|
380
|
+
minute: "2-digit",
|
|
381
|
+
hour12: false
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (mode === "datetime") {
|
|
385
|
+
return time.toLocaleString(void 0, {
|
|
386
|
+
month: "short",
|
|
387
|
+
day: "numeric",
|
|
388
|
+
hour: "2-digit",
|
|
389
|
+
minute: "2-digit",
|
|
390
|
+
hour12: false
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
if (mode === "date") {
|
|
394
|
+
return time.toLocaleDateString(void 0, {
|
|
395
|
+
month: "short",
|
|
396
|
+
day: "numeric"
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
const stepMs = getTimeStepMs();
|
|
400
|
+
if (stepMs < 24 * 60 * 60 * 1e3) {
|
|
401
|
+
return time.toLocaleTimeString(void 0, {
|
|
402
|
+
hour: "2-digit",
|
|
403
|
+
minute: "2-digit",
|
|
404
|
+
hour12: false
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
return time.toLocaleDateString(void 0, {
|
|
408
|
+
month: "short",
|
|
409
|
+
day: "numeric"
|
|
410
|
+
});
|
|
411
|
+
};
|
|
329
412
|
const drawText = (text, x, y, align = "left", baseline = "alphabetic", color = mergedOptions.axis?.textColor ?? mergedOptions.axisColor) => {
|
|
330
413
|
ctx.fillStyle = color;
|
|
331
414
|
ctx.textAlign = align;
|
|
@@ -389,7 +472,7 @@ function createChart(element, options = {}) {
|
|
|
389
472
|
return;
|
|
390
473
|
}
|
|
391
474
|
const lineY = clamp(yFromPrice(renderPrice), chartTop + 1, chartBottom - 1);
|
|
392
|
-
const color =
|
|
475
|
+
const color = line.color ?? (mergedLine.type === "takeProfit" ? "rgba(45,212,191,0.86)" : mergedLine.type === "stop" ? "rgba(245,158,11,0.86)" : "rgba(59,130,246,0.8)");
|
|
393
476
|
if (Number.isFinite(mergedLine.fillToPrice)) {
|
|
394
477
|
const fillY = clamp(yFromPrice(mergedLine.fillToPrice), chartTop + 1, chartBottom - 1);
|
|
395
478
|
const topY = Math.min(lineY, fillY);
|
|
@@ -500,7 +583,7 @@ function createChart(element, options = {}) {
|
|
|
500
583
|
const borderRadius = Math.max(0, mergedLine.labelBorderRadius);
|
|
501
584
|
const widgetBackground = mergedOptions.backgroundColor;
|
|
502
585
|
const widgetBorder = color;
|
|
503
|
-
const textColor = mergedLine.labelTextColor;
|
|
586
|
+
const textColor = line.labelTextColor ?? (mergedLine.type === "takeProfit" ? "#5eead4" : mergedLine.type === "stop" ? "#fbbf24" : mergedLine.labelTextColor);
|
|
504
587
|
const mainWidgetX = leftWidgetX + actionButtonsTotalWidth + actionButtonsGap;
|
|
505
588
|
ctx.fillStyle = widgetBackground;
|
|
506
589
|
fillRoundedRect(Math.round(mainWidgetX), Math.round(labelY), mainWidgetWidth, labelHeight, borderRadius);
|
|
@@ -750,6 +833,31 @@ function createChart(element, options = {}) {
|
|
|
750
833
|
const yFromPrice = (price) => {
|
|
751
834
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
752
835
|
};
|
|
836
|
+
if (watermark.visible && watermark.imageSrc.trim().length > 0) {
|
|
837
|
+
ensureWatermarkImage(watermark.imageSrc.trim());
|
|
838
|
+
if (watermarkImageReady && watermarkImage) {
|
|
839
|
+
const maxW = chartWidth * clamp(watermark.imageMaxWidthRatio, 0.05, 1);
|
|
840
|
+
const maxH = chartHeight * clamp(watermark.imageMaxHeightRatio, 0.05, 1);
|
|
841
|
+
const naturalW = Math.max(1, watermarkImage.naturalWidth || watermarkImage.width);
|
|
842
|
+
const naturalH = Math.max(1, watermarkImage.naturalHeight || watermarkImage.height);
|
|
843
|
+
const containScale = Math.min(maxW / naturalW, maxH / naturalH);
|
|
844
|
+
const scale = Math.max(0.05, containScale * Math.max(0.05, watermark.imageScale));
|
|
845
|
+
const drawW = naturalW * scale;
|
|
846
|
+
const drawH = naturalH * scale;
|
|
847
|
+
const drawX = chartLeft + (chartWidth - drawW) / 2;
|
|
848
|
+
const drawY = chartTop + (chartHeight - drawH) / 2;
|
|
849
|
+
ctx.save();
|
|
850
|
+
ctx.globalAlpha = clamp(watermark.opacity, 0, 1);
|
|
851
|
+
ctx.drawImage(watermarkImage, drawX, drawY, drawW, drawH);
|
|
852
|
+
if (watermark.imageTintColor.trim().length > 0) {
|
|
853
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
854
|
+
ctx.globalAlpha = clamp(watermark.opacity, 0, 1) * clamp(watermark.imageTintOpacity, 0, 1);
|
|
855
|
+
ctx.fillStyle = watermark.imageTintColor;
|
|
856
|
+
ctx.fillRect(drawX, drawY, drawW, drawH);
|
|
857
|
+
}
|
|
858
|
+
ctx.restore();
|
|
859
|
+
}
|
|
860
|
+
}
|
|
753
861
|
if (watermark.visible && watermark.text.trim().length > 0) {
|
|
754
862
|
ctx.save();
|
|
755
863
|
ctx.globalAlpha = clamp(watermark.opacity, 0, 1);
|
|
@@ -947,6 +1055,61 @@ function createChart(element, options = {}) {
|
|
|
947
1055
|
});
|
|
948
1056
|
drawText(timeLabel, x, chartBottom + 8, "center", "top", axis.textColor);
|
|
949
1057
|
}
|
|
1058
|
+
if (crosshair.visible && crosshairPoint) {
|
|
1059
|
+
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
1060
|
+
const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
|
|
1061
|
+
const labelPaddingX = 8;
|
|
1062
|
+
const labelHeight = 20;
|
|
1063
|
+
const labelRadius = Math.max(0, crosshair.labelBorderRadius);
|
|
1064
|
+
const labelBackground = crosshair.labelBackgroundColor;
|
|
1065
|
+
const labelTextColor = crosshair.labelTextColor;
|
|
1066
|
+
const labelBorderColor = crosshair.labelBorderColor;
|
|
1067
|
+
const labelBorderWidth = Math.max(0, crosshair.labelBorderWidth);
|
|
1068
|
+
const labelBorderStyle = crosshair.labelBorderStyle;
|
|
1069
|
+
const strokeCrosshairLabel = (x, y, widthValue) => {
|
|
1070
|
+
if (labelBorderWidth <= 0) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
ctx.save();
|
|
1074
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1075
|
+
ctx.lineWidth = labelBorderWidth;
|
|
1076
|
+
if (labelBorderStyle === "dotted") {
|
|
1077
|
+
ctx.setLineDash([2, 3]);
|
|
1078
|
+
} else if (labelBorderStyle === "dashed") {
|
|
1079
|
+
ctx.setLineDash([6, 4]);
|
|
1080
|
+
} else {
|
|
1081
|
+
ctx.setLineDash([]);
|
|
1082
|
+
}
|
|
1083
|
+
strokeRoundedRect(Math.round(x), Math.round(y), widthValue, labelHeight, labelRadius);
|
|
1084
|
+
ctx.restore();
|
|
1085
|
+
};
|
|
1086
|
+
if (crosshair.showPriceLabel) {
|
|
1087
|
+
const hoverPrice = yMin + (chartBottom - cy) / chartHeight * yRange;
|
|
1088
|
+
const priceText = formatPrice(hoverPrice);
|
|
1089
|
+
const priceWidth = Math.ceil(ctx.measureText(priceText).width) + labelPaddingX * 2;
|
|
1090
|
+
const priceX = chartRight + 4;
|
|
1091
|
+
const priceY = clamp(cy - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
1092
|
+
ctx.fillStyle = labelBackground;
|
|
1093
|
+
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|
|
1094
|
+
strokeCrosshairLabel(priceX, priceY, priceWidth);
|
|
1095
|
+
drawText(priceText, priceX + labelPaddingX, priceY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1096
|
+
}
|
|
1097
|
+
if (crosshair.showTimeLabel) {
|
|
1098
|
+
const ratio = clamp((cx - chartLeft) / chartWidth, 0, 1);
|
|
1099
|
+
const hoverIndex = Math.round(xStart + ratio * xSpan - 0.5);
|
|
1100
|
+
const hoverTime = getTimeForIndex(hoverIndex);
|
|
1101
|
+
if (hoverTime) {
|
|
1102
|
+
const timeText = formatHoverTimeLabel(hoverTime, crosshair.timeLabelFormat);
|
|
1103
|
+
const timeWidth = Math.ceil(ctx.measureText(timeText).width) + labelPaddingX * 2;
|
|
1104
|
+
const timeX = clamp(cx - timeWidth / 2, chartLeft, chartRight - timeWidth);
|
|
1105
|
+
const timeY = chartBottom + 8;
|
|
1106
|
+
ctx.fillStyle = labelBackground;
|
|
1107
|
+
fillRoundedRect(Math.round(timeX), Math.round(timeY), timeWidth, labelHeight, labelRadius);
|
|
1108
|
+
strokeCrosshairLabel(timeX, timeY, timeWidth);
|
|
1109
|
+
drawText(timeText, timeX + labelPaddingX, timeY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
950
1113
|
};
|
|
951
1114
|
const zoomX = (factor, anchorX) => {
|
|
952
1115
|
if (!drawState || data.length === 0) {
|
|
@@ -1041,6 +1204,39 @@ function createChart(element, options = {}) {
|
|
|
1041
1204
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
1042
1205
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
1043
1206
|
};
|
|
1207
|
+
const indexFromCanvasX = (x) => {
|
|
1208
|
+
if (!drawState) {
|
|
1209
|
+
return null;
|
|
1210
|
+
}
|
|
1211
|
+
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
1212
|
+
return Math.round(drawState.xStart + ratio * drawState.xSpan - 0.5);
|
|
1213
|
+
};
|
|
1214
|
+
const emitCrosshairMove = (x, y, region) => {
|
|
1215
|
+
if (!crosshairMoveHandler) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const index = indexFromCanvasX(x);
|
|
1219
|
+
const hoverTime = index === null ? null : getTimeForIndex(index);
|
|
1220
|
+
const parsedPoint = index === null ? void 0 : data[index];
|
|
1221
|
+
const point = parsedPoint === void 0 ? void 0 : {
|
|
1222
|
+
t: parsedPoint.time.toISOString(),
|
|
1223
|
+
o: parsedPoint.o,
|
|
1224
|
+
h: parsedPoint.h,
|
|
1225
|
+
l: parsedPoint.l,
|
|
1226
|
+
c: parsedPoint.c,
|
|
1227
|
+
...parsedPoint.v === void 0 ? {} : { v: parsedPoint.v }
|
|
1228
|
+
};
|
|
1229
|
+
const payload = {
|
|
1230
|
+
x,
|
|
1231
|
+
y,
|
|
1232
|
+
region,
|
|
1233
|
+
...region === "plot" ? { price: Number(priceFromCanvasY(y).toFixed(mergedOptions.priceDecimals)) } : {},
|
|
1234
|
+
...index === null ? {} : { index },
|
|
1235
|
+
...hoverTime ? { time: hoverTime.toISOString() } : {},
|
|
1236
|
+
...point ? { point } : {}
|
|
1237
|
+
};
|
|
1238
|
+
crosshairMoveHandler(payload);
|
|
1239
|
+
};
|
|
1044
1240
|
const getHitRegion = (x, y) => {
|
|
1045
1241
|
if (!drawState) {
|
|
1046
1242
|
return "outside";
|
|
@@ -1192,14 +1388,17 @@ function createChart(element, options = {}) {
|
|
|
1192
1388
|
}
|
|
1193
1389
|
const hoverRegion = getHitRegion(point.x, point.y);
|
|
1194
1390
|
if (hoverRegion === "plot") {
|
|
1195
|
-
canvas.style.cursor = "
|
|
1391
|
+
canvas.style.cursor = "default";
|
|
1196
1392
|
setCrosshairPoint(point);
|
|
1393
|
+
emitCrosshairMove(point.x, point.y, "plot");
|
|
1197
1394
|
} else if (hoverRegion === "x-axis") {
|
|
1198
1395
|
canvas.style.cursor = "ew-resize";
|
|
1199
1396
|
setCrosshairPoint(null);
|
|
1397
|
+
emitCrosshairMove(point.x, point.y, "x-axis");
|
|
1200
1398
|
} else if (hoverRegion === "y-axis") {
|
|
1201
1399
|
canvas.style.cursor = "ns-resize";
|
|
1202
1400
|
setCrosshairPoint(null);
|
|
1401
|
+
emitCrosshairMove(point.x, point.y, "y-axis");
|
|
1203
1402
|
} else {
|
|
1204
1403
|
canvas.style.cursor = "default";
|
|
1205
1404
|
setCrosshairPoint(null);
|
|
@@ -1209,7 +1408,7 @@ function createChart(element, options = {}) {
|
|
|
1209
1408
|
const deltaX = point.x - lastPointerX;
|
|
1210
1409
|
const deltaY = point.y - lastPointerY;
|
|
1211
1410
|
if (dragMode === "plot") {
|
|
1212
|
-
canvas.style.cursor = "
|
|
1411
|
+
canvas.style.cursor = "default";
|
|
1213
1412
|
pan(deltaX, deltaY, true, true);
|
|
1214
1413
|
setCrosshairPoint(null);
|
|
1215
1414
|
} else if (dragMode === "x-axis") {
|
|
@@ -1374,7 +1573,11 @@ function createChart(element, options = {}) {
|
|
|
1374
1573
|
autoYMin = null;
|
|
1375
1574
|
autoYMax = null;
|
|
1376
1575
|
} else {
|
|
1377
|
-
|
|
1576
|
+
if (mergedOptions.preserveViewportOnDataUpdate) {
|
|
1577
|
+
clampXViewport();
|
|
1578
|
+
} else {
|
|
1579
|
+
fitXViewport();
|
|
1580
|
+
}
|
|
1378
1581
|
}
|
|
1379
1582
|
draw();
|
|
1380
1583
|
};
|
|
@@ -1435,6 +1638,9 @@ function createChart(element, options = {}) {
|
|
|
1435
1638
|
const onChartClick = (handler) => {
|
|
1436
1639
|
chartClickHandler = handler;
|
|
1437
1640
|
};
|
|
1641
|
+
const onCrosshairMove = (handler) => {
|
|
1642
|
+
crosshairMoveHandler = handler;
|
|
1643
|
+
};
|
|
1438
1644
|
const setDoubleClickEnabled = (enabled) => {
|
|
1439
1645
|
doubleClickEnabled = enabled;
|
|
1440
1646
|
};
|
|
@@ -1463,6 +1669,7 @@ function createChart(element, options = {}) {
|
|
|
1463
1669
|
removeOrderLine,
|
|
1464
1670
|
onOrderAction,
|
|
1465
1671
|
onChartClick,
|
|
1672
|
+
onCrosshairMove,
|
|
1466
1673
|
setDoubleClickEnabled,
|
|
1467
1674
|
setDoubleClickAction,
|
|
1468
1675
|
resize,
|
|
@@ -4,6 +4,11 @@ interface ChartOptions {
|
|
|
4
4
|
backgroundColor?: string;
|
|
5
5
|
axisColor?: string;
|
|
6
6
|
axis?: AxisOptions;
|
|
7
|
+
priceDecimals?: number;
|
|
8
|
+
initialViewport?: "latest" | "center";
|
|
9
|
+
initialVisibleBars?: number;
|
|
10
|
+
rightEdgePaddingBars?: number;
|
|
11
|
+
preserveViewportOnDataUpdate?: boolean;
|
|
7
12
|
upColor?: string;
|
|
8
13
|
downColor?: string;
|
|
9
14
|
gridColor?: string;
|
|
@@ -42,6 +47,15 @@ interface CrosshairOptions {
|
|
|
42
47
|
style?: "solid" | "dotted" | "dashed";
|
|
43
48
|
showHorizontal?: boolean;
|
|
44
49
|
showVertical?: boolean;
|
|
50
|
+
showPriceLabel?: boolean;
|
|
51
|
+
showTimeLabel?: boolean;
|
|
52
|
+
timeLabelFormat?: "auto" | "date" | "time" | "datetime";
|
|
53
|
+
labelBackgroundColor?: string;
|
|
54
|
+
labelTextColor?: string;
|
|
55
|
+
labelBorderRadius?: number;
|
|
56
|
+
labelBorderColor?: string;
|
|
57
|
+
labelBorderWidth?: number;
|
|
58
|
+
labelBorderStyle?: "solid" | "dotted" | "dashed";
|
|
45
59
|
}
|
|
46
60
|
interface WatermarkOptions {
|
|
47
61
|
visible?: boolean;
|
|
@@ -51,6 +65,12 @@ interface WatermarkOptions {
|
|
|
51
65
|
fontSize?: number;
|
|
52
66
|
fontWeight?: number | string;
|
|
53
67
|
thickness?: number;
|
|
68
|
+
imageSrc?: string;
|
|
69
|
+
imageScale?: number;
|
|
70
|
+
imageMaxWidthRatio?: number;
|
|
71
|
+
imageMaxHeightRatio?: number;
|
|
72
|
+
imageTintColor?: string;
|
|
73
|
+
imageTintOpacity?: number;
|
|
54
74
|
}
|
|
55
75
|
interface PriceLineOptions {
|
|
56
76
|
id?: string;
|
|
@@ -132,6 +152,15 @@ interface ChartClickEvent {
|
|
|
132
152
|
price?: number;
|
|
133
153
|
region: "plot" | "x-axis" | "y-axis";
|
|
134
154
|
}
|
|
155
|
+
interface CrosshairMoveEvent {
|
|
156
|
+
x: number;
|
|
157
|
+
y: number;
|
|
158
|
+
region: "plot" | "x-axis" | "y-axis";
|
|
159
|
+
price?: number;
|
|
160
|
+
index?: number;
|
|
161
|
+
time?: string;
|
|
162
|
+
point?: OhlcDataPoint;
|
|
163
|
+
}
|
|
135
164
|
interface TickerLineOptions {
|
|
136
165
|
visible?: boolean;
|
|
137
166
|
style?: "solid" | "dotted" | "dashed";
|
|
@@ -152,6 +181,7 @@ interface ChartInstance {
|
|
|
152
181
|
removeOrderLine: (id: string) => void;
|
|
153
182
|
onOrderAction: (handler: ((event: OrderActionEvent) => void) | null) => void;
|
|
154
183
|
onChartClick: (handler: ((event: ChartClickEvent) => void) | null) => void;
|
|
184
|
+
onCrosshairMove: (handler: ((event: CrosshairMoveEvent) => void) | null) => void;
|
|
155
185
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
156
186
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
157
187
|
resize: (width?: number, height?: number) => void;
|
|
@@ -167,4 +197,4 @@ interface OhlcDataPoint {
|
|
|
167
197
|
}
|
|
168
198
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
169
199
|
|
|
170
|
-
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
200
|
+
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|