hyperprop-charting-library 0.1.54 → 0.1.56
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 +376 -2
- package/dist/hyperprop-charting-library.d.ts +36 -1
- package/dist/hyperprop-charting-library.js +376 -2
- package/dist/index.cjs +376 -2
- package/dist/index.d.cts +36 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.js +376 -2
- package/docs/API.md +43 -0
- package/docs/RECIPES.md +28 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -214,7 +214,8 @@ var DEFAULT_OPTIONS = {
|
|
|
214
214
|
},
|
|
215
215
|
labels: DEFAULT_LABELS_OPTIONS,
|
|
216
216
|
dashPatterns: DEFAULT_DASH_PATTERNS,
|
|
217
|
-
indicators: []
|
|
217
|
+
indicators: [],
|
|
218
|
+
drawings: []
|
|
218
219
|
};
|
|
219
220
|
var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
220
221
|
...baseOptions,
|
|
@@ -905,6 +906,7 @@ function createChart(element, options = {}) {
|
|
|
905
906
|
let generatedPriceLineId = 1;
|
|
906
907
|
let generatedOrderLineId = 1;
|
|
907
908
|
let generatedIndicatorId = 1;
|
|
909
|
+
let generatedDrawingId = 1;
|
|
908
910
|
const indicatorRegistry = /* @__PURE__ */ new Map();
|
|
909
911
|
for (const indicator of BUILTIN_INDICATORS) {
|
|
910
912
|
indicatorRegistry.set(indicator.id, indicator);
|
|
@@ -927,7 +929,42 @@ function createChart(element, options = {}) {
|
|
|
927
929
|
}
|
|
928
930
|
};
|
|
929
931
|
};
|
|
932
|
+
const normalizeDrawingState = (drawing) => ({
|
|
933
|
+
id: drawing.id ?? `drawing-${generatedDrawingId++}`,
|
|
934
|
+
type: drawing.type,
|
|
935
|
+
points: drawing.points.map((point) => ({
|
|
936
|
+
index: Number(point.index) || 0,
|
|
937
|
+
price: Number(point.price) || 0,
|
|
938
|
+
...point.time ? { time: point.time } : {}
|
|
939
|
+
})),
|
|
940
|
+
visible: drawing.visible ?? true,
|
|
941
|
+
color: drawing.color ?? "#94a3b8",
|
|
942
|
+
style: drawing.style ?? "dotted",
|
|
943
|
+
width: Math.max(1, Number(drawing.width) || 1),
|
|
944
|
+
locked: drawing.locked ?? false,
|
|
945
|
+
...drawing.label === void 0 ? {} : { label: drawing.label }
|
|
946
|
+
});
|
|
947
|
+
const serializeDrawing = (drawing) => ({
|
|
948
|
+
id: drawing.id,
|
|
949
|
+
type: drawing.type,
|
|
950
|
+
points: drawing.points.map((point) => ({ ...point })),
|
|
951
|
+
visible: drawing.visible,
|
|
952
|
+
color: drawing.color,
|
|
953
|
+
style: drawing.style,
|
|
954
|
+
width: drawing.width,
|
|
955
|
+
locked: drawing.locked,
|
|
956
|
+
...drawing.label === void 0 ? {} : { label: drawing.label }
|
|
957
|
+
});
|
|
930
958
|
let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
|
|
959
|
+
let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
|
|
960
|
+
let activeDrawingTool = null;
|
|
961
|
+
let draftDrawing = null;
|
|
962
|
+
let drawingDragState = null;
|
|
963
|
+
let drawingsChangeHandler = null;
|
|
964
|
+
let drawingSelectHandler = null;
|
|
965
|
+
const emitDrawingsChange = () => {
|
|
966
|
+
drawingsChangeHandler?.(drawings.map((drawing) => serializeDrawing(drawing)));
|
|
967
|
+
};
|
|
931
968
|
const orderWidgetWidthById = /* @__PURE__ */ new Map();
|
|
932
969
|
const orderPriceTagWidthById = /* @__PURE__ */ new Map();
|
|
933
970
|
let xCenter = 0;
|
|
@@ -1981,6 +2018,57 @@ function createChart(element, options = {}) {
|
|
|
1981
2018
|
const yFromPrice = (price) => {
|
|
1982
2019
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
1983
2020
|
};
|
|
2021
|
+
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
2022
|
+
const drawDrawingHandle = (x, y, color) => {
|
|
2023
|
+
ctx.save();
|
|
2024
|
+
ctx.setLineDash([]);
|
|
2025
|
+
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
2026
|
+
ctx.strokeStyle = color;
|
|
2027
|
+
ctx.lineWidth = 2;
|
|
2028
|
+
ctx.beginPath();
|
|
2029
|
+
ctx.arc(x, y, 5, 0, Math.PI * 2);
|
|
2030
|
+
ctx.fill();
|
|
2031
|
+
ctx.stroke();
|
|
2032
|
+
ctx.restore();
|
|
2033
|
+
};
|
|
2034
|
+
const drawDrawing = (drawing, draft = false) => {
|
|
2035
|
+
if (!drawing.visible) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
ctx.save();
|
|
2039
|
+
ctx.strokeStyle = drawing.color;
|
|
2040
|
+
ctx.lineWidth = drawing.width;
|
|
2041
|
+
ctx.globalAlpha = draft ? 0.72 : 1;
|
|
2042
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2043
|
+
if (drawing.type === "horizontal-line") {
|
|
2044
|
+
const point = drawing.points[0];
|
|
2045
|
+
if (point) {
|
|
2046
|
+
const y = clamp(yFromPrice(point.price), chartTop + 1, chartBottom - 1);
|
|
2047
|
+
const handleX = chartRight - 14;
|
|
2048
|
+
ctx.beginPath();
|
|
2049
|
+
ctx.moveTo(crisp(chartLeft), crisp(y));
|
|
2050
|
+
ctx.lineTo(crisp(chartRight), crisp(y));
|
|
2051
|
+
ctx.stroke();
|
|
2052
|
+
drawDrawingHandle(handleX, y, drawing.color);
|
|
2053
|
+
}
|
|
2054
|
+
} else if (drawing.type === "trendline") {
|
|
2055
|
+
const first = drawing.points[0];
|
|
2056
|
+
const second = drawing.points[1];
|
|
2057
|
+
if (first && second) {
|
|
2058
|
+
const firstX = xFromDrawingPoint(first);
|
|
2059
|
+
const firstY = yFromPrice(first.price);
|
|
2060
|
+
const secondX = xFromDrawingPoint(second);
|
|
2061
|
+
const secondY = yFromPrice(second.price);
|
|
2062
|
+
ctx.beginPath();
|
|
2063
|
+
ctx.moveTo(firstX, firstY);
|
|
2064
|
+
ctx.lineTo(secondX, secondY);
|
|
2065
|
+
ctx.stroke();
|
|
2066
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2067
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
ctx.restore();
|
|
2071
|
+
};
|
|
1984
2072
|
if (watermark.visible && watermark.imageSrc.trim().length > 0) {
|
|
1985
2073
|
ensureWatermarkImage(watermark.imageSrc.trim());
|
|
1986
2074
|
if (watermarkImageReady && watermarkImage) {
|
|
@@ -2152,6 +2240,10 @@ function createChart(element, options = {}) {
|
|
|
2152
2240
|
);
|
|
2153
2241
|
});
|
|
2154
2242
|
}
|
|
2243
|
+
drawings.forEach((drawing) => drawDrawing(drawing));
|
|
2244
|
+
if (draftDrawing) {
|
|
2245
|
+
drawDrawing(draftDrawing, true);
|
|
2246
|
+
}
|
|
2155
2247
|
const crosshair = { ...DEFAULT_CROSSHAIR_OPTIONS, ...mergedOptions.crosshair ?? {} };
|
|
2156
2248
|
if (crosshair.visible && crosshairPoint) {
|
|
2157
2249
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
@@ -2988,6 +3080,82 @@ function createChart(element, options = {}) {
|
|
|
2988
3080
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
2989
3081
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
2990
3082
|
};
|
|
3083
|
+
const drawingPointFromCanvas = (x, y) => {
|
|
3084
|
+
if (!drawState) {
|
|
3085
|
+
return null;
|
|
3086
|
+
}
|
|
3087
|
+
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
3088
|
+
const index = drawState.xStart + ratio * drawState.xSpan - 0.5;
|
|
3089
|
+
const nearestIndex = Math.round(index);
|
|
3090
|
+
const time = getTimeForIndex(nearestIndex);
|
|
3091
|
+
return {
|
|
3092
|
+
index,
|
|
3093
|
+
price: roundToPricePrecision(priceFromCanvasY(y)),
|
|
3094
|
+
...time ? { time: time.toISOString() } : {}
|
|
3095
|
+
};
|
|
3096
|
+
};
|
|
3097
|
+
const normalizeDrawingPoint = (index, price) => {
|
|
3098
|
+
const roundedIndex = Math.round(index);
|
|
3099
|
+
const time = getTimeForIndex(roundedIndex);
|
|
3100
|
+
return {
|
|
3101
|
+
index,
|
|
3102
|
+
price: roundToPricePrecision(price),
|
|
3103
|
+
...time ? { time: time.toISOString() } : {}
|
|
3104
|
+
};
|
|
3105
|
+
};
|
|
3106
|
+
const canvasXFromDrawingPoint = (point) => {
|
|
3107
|
+
if (!drawState) return 0;
|
|
3108
|
+
return drawState.chartLeft + (point.index + 0.5 - drawState.xStart) / drawState.xSpan * drawState.chartWidth;
|
|
3109
|
+
};
|
|
3110
|
+
const canvasYFromDrawingPrice = (price) => {
|
|
3111
|
+
if (!drawState) return 0;
|
|
3112
|
+
const range = drawState.yMax - drawState.yMin || 1;
|
|
3113
|
+
return drawState.chartBottom - (price - drawState.yMin) / range * drawState.chartHeight;
|
|
3114
|
+
};
|
|
3115
|
+
const distanceToSegment = (x, y, x1, y1, x2, y2) => {
|
|
3116
|
+
const dx = x2 - x1;
|
|
3117
|
+
const dy = y2 - y1;
|
|
3118
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3119
|
+
if (lengthSq === 0) return Math.hypot(x - x1, y - y1);
|
|
3120
|
+
const t = clamp(((x - x1) * dx + (y - y1) * dy) / lengthSq, 0, 1);
|
|
3121
|
+
return Math.hypot(x - (x1 + t * dx), y - (y1 + t * dy));
|
|
3122
|
+
};
|
|
3123
|
+
const getDrawingHit = (x, y) => {
|
|
3124
|
+
for (let index = drawings.length - 1; index >= 0; index -= 1) {
|
|
3125
|
+
const drawing = drawings[index];
|
|
3126
|
+
if (!drawing?.visible) continue;
|
|
3127
|
+
if (drawing.type === "horizontal-line") {
|
|
3128
|
+
const point = drawing.points[0];
|
|
3129
|
+
if (!point) continue;
|
|
3130
|
+
const lineY = canvasYFromDrawingPrice(point.price);
|
|
3131
|
+
const handleX = drawState ? drawState.chartRight - 14 : 0;
|
|
3132
|
+
if (Math.hypot(x - handleX, y - lineY) <= 8) {
|
|
3133
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3134
|
+
}
|
|
3135
|
+
if (Math.abs(y - lineY) <= 7) {
|
|
3136
|
+
return { drawing, target: "line" };
|
|
3137
|
+
}
|
|
3138
|
+
} else if (drawing.type === "trendline") {
|
|
3139
|
+
const first = drawing.points[0];
|
|
3140
|
+
const second = drawing.points[1];
|
|
3141
|
+
if (!first || !second) continue;
|
|
3142
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3143
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3144
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3145
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3146
|
+
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
3147
|
+
return { drawing, target: "handle", pointIndex: 0 };
|
|
3148
|
+
}
|
|
3149
|
+
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
3150
|
+
return { drawing, target: "handle", pointIndex: 1 };
|
|
3151
|
+
}
|
|
3152
|
+
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3153
|
+
return { drawing, target: "line" };
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
return null;
|
|
3158
|
+
};
|
|
2991
3159
|
const indexFromCanvasX = (x) => {
|
|
2992
3160
|
if (!drawState) {
|
|
2993
3161
|
return null;
|
|
@@ -3040,6 +3208,86 @@ function createChart(element, options = {}) {
|
|
|
3040
3208
|
}
|
|
3041
3209
|
return "outside";
|
|
3042
3210
|
};
|
|
3211
|
+
const handleDrawingToolPointerDown = (x, y) => {
|
|
3212
|
+
if (!activeDrawingTool || !drawState) {
|
|
3213
|
+
return false;
|
|
3214
|
+
}
|
|
3215
|
+
const point = drawingPointFromCanvas(x, y);
|
|
3216
|
+
if (!point) {
|
|
3217
|
+
return false;
|
|
3218
|
+
}
|
|
3219
|
+
if (activeDrawingTool === "horizontal-line") {
|
|
3220
|
+
drawings.push(
|
|
3221
|
+
normalizeDrawingState({
|
|
3222
|
+
type: "horizontal-line",
|
|
3223
|
+
points: [point],
|
|
3224
|
+
color: "#38bdf8",
|
|
3225
|
+
style: "dotted",
|
|
3226
|
+
width: 1
|
|
3227
|
+
})
|
|
3228
|
+
);
|
|
3229
|
+
emitDrawingsChange();
|
|
3230
|
+
draw();
|
|
3231
|
+
return true;
|
|
3232
|
+
}
|
|
3233
|
+
if (activeDrawingTool === "trendline") {
|
|
3234
|
+
if (draftDrawing?.type === "trendline") {
|
|
3235
|
+
const completed = normalizeDrawingState({
|
|
3236
|
+
...serializeDrawing(draftDrawing),
|
|
3237
|
+
points: [draftDrawing.points[0], point]
|
|
3238
|
+
});
|
|
3239
|
+
drawings.push(completed);
|
|
3240
|
+
draftDrawing = null;
|
|
3241
|
+
activeDrawingTool = null;
|
|
3242
|
+
emitDrawingsChange();
|
|
3243
|
+
draw();
|
|
3244
|
+
return true;
|
|
3245
|
+
}
|
|
3246
|
+
draftDrawing = normalizeDrawingState({
|
|
3247
|
+
type: "trendline",
|
|
3248
|
+
points: [point, point],
|
|
3249
|
+
color: "#2563eb",
|
|
3250
|
+
style: "solid",
|
|
3251
|
+
width: 2
|
|
3252
|
+
});
|
|
3253
|
+
draw();
|
|
3254
|
+
return true;
|
|
3255
|
+
}
|
|
3256
|
+
return false;
|
|
3257
|
+
};
|
|
3258
|
+
const updateDrawingDrag = (x, y) => {
|
|
3259
|
+
if (!drawingDragState) {
|
|
3260
|
+
return false;
|
|
3261
|
+
}
|
|
3262
|
+
const currentPoint = drawingPointFromCanvas(x, y);
|
|
3263
|
+
if (!currentPoint) {
|
|
3264
|
+
return true;
|
|
3265
|
+
}
|
|
3266
|
+
const deltaIndex = currentPoint.index - drawingDragState.startCanvasPoint.index;
|
|
3267
|
+
const deltaPrice = currentPoint.price - drawingDragState.startCanvasPoint.price;
|
|
3268
|
+
drawings = drawings.map((drawing) => {
|
|
3269
|
+
if (drawing.id !== drawingDragState?.drawingId || drawing.locked) {
|
|
3270
|
+
return drawing;
|
|
3271
|
+
}
|
|
3272
|
+
if (drawingDragState.target === "handle") {
|
|
3273
|
+
return {
|
|
3274
|
+
...drawing,
|
|
3275
|
+
points: drawing.points.map(
|
|
3276
|
+
(point, index) => index === (drawingDragState?.pointIndex ?? 0) ? normalizeDrawingPoint(currentPoint.index, currentPoint.price) : point
|
|
3277
|
+
)
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
return {
|
|
3281
|
+
...drawing,
|
|
3282
|
+
points: drawingDragState.startPoints.map(
|
|
3283
|
+
(point) => normalizeDrawingPoint(point.index + deltaIndex, point.price + deltaPrice)
|
|
3284
|
+
)
|
|
3285
|
+
};
|
|
3286
|
+
});
|
|
3287
|
+
emitDrawingsChange();
|
|
3288
|
+
draw();
|
|
3289
|
+
return true;
|
|
3290
|
+
};
|
|
3043
3291
|
let isDragging = false;
|
|
3044
3292
|
let dragMode = null;
|
|
3045
3293
|
let lastPointerX = 0;
|
|
@@ -3079,6 +3327,7 @@ function createChart(element, options = {}) {
|
|
|
3079
3327
|
pointerDownInfo = null;
|
|
3080
3328
|
orderDragState = null;
|
|
3081
3329
|
actionDragState = null;
|
|
3330
|
+
drawingDragState = null;
|
|
3082
3331
|
canvas.style.cursor = "default";
|
|
3083
3332
|
setCrosshairPoint(null);
|
|
3084
3333
|
};
|
|
@@ -3165,6 +3414,40 @@ function createChart(element, options = {}) {
|
|
|
3165
3414
|
if (region === "outside") {
|
|
3166
3415
|
return;
|
|
3167
3416
|
}
|
|
3417
|
+
if (region === "plot" && !activeDrawingTool) {
|
|
3418
|
+
const drawingHit = getDrawingHit(point.x, point.y);
|
|
3419
|
+
if (drawingHit) {
|
|
3420
|
+
drawingSelectHandler?.({
|
|
3421
|
+
drawing: serializeDrawing(drawingHit.drawing),
|
|
3422
|
+
target: drawingHit.target,
|
|
3423
|
+
...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
|
|
3424
|
+
x: point.x,
|
|
3425
|
+
y: point.y
|
|
3426
|
+
});
|
|
3427
|
+
if (!drawingHit.drawing.locked) {
|
|
3428
|
+
const startCanvasPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3429
|
+
if (startCanvasPoint) {
|
|
3430
|
+
drawingDragState = {
|
|
3431
|
+
drawingId: drawingHit.drawing.id,
|
|
3432
|
+
target: drawingHit.target,
|
|
3433
|
+
...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
|
|
3434
|
+
startCanvasPoint,
|
|
3435
|
+
startPoints: drawingHit.drawing.points.map((drawingPoint) => ({ ...drawingPoint }))
|
|
3436
|
+
};
|
|
3437
|
+
activePointerId = event.pointerId;
|
|
3438
|
+
canvas.setPointerCapture(event.pointerId);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
setCrosshairPoint(null);
|
|
3442
|
+
canvas.style.cursor = drawingHit.drawing.locked ? "pointer" : drawingHit.target === "handle" ? "grab" : "move";
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
if (region === "plot" && handleDrawingToolPointerDown(point.x, point.y)) {
|
|
3447
|
+
setCrosshairPoint(null);
|
|
3448
|
+
canvas.style.cursor = "crosshair";
|
|
3449
|
+
return;
|
|
3450
|
+
}
|
|
3168
3451
|
isDragging = true;
|
|
3169
3452
|
dragMode = region;
|
|
3170
3453
|
activePointerId = event.pointerId;
|
|
@@ -3196,6 +3479,27 @@ function createChart(element, options = {}) {
|
|
|
3196
3479
|
pointerDownInfo.moved = true;
|
|
3197
3480
|
}
|
|
3198
3481
|
}
|
|
3482
|
+
if (drawingDragState) {
|
|
3483
|
+
if (activePointerId !== null && event.pointerId !== activePointerId) {
|
|
3484
|
+
return;
|
|
3485
|
+
}
|
|
3486
|
+
updateDrawingDrag(point.x, point.y);
|
|
3487
|
+
canvas.style.cursor = drawingDragState.target === "handle" ? "grabbing" : "move";
|
|
3488
|
+
setCrosshairPoint(null);
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3491
|
+
if (draftDrawing && activeDrawingTool === "trendline") {
|
|
3492
|
+
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3493
|
+
if (nextPoint) {
|
|
3494
|
+
draftDrawing = {
|
|
3495
|
+
...draftDrawing,
|
|
3496
|
+
points: [draftDrawing.points[0], nextPoint]
|
|
3497
|
+
};
|
|
3498
|
+
canvas.style.cursor = "crosshair";
|
|
3499
|
+
draw();
|
|
3500
|
+
return;
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3199
3503
|
if (orderDragState) {
|
|
3200
3504
|
if (activePointerId !== null && event.pointerId !== activePointerId) {
|
|
3201
3505
|
return;
|
|
@@ -3266,9 +3570,15 @@ function createChart(element, options = {}) {
|
|
|
3266
3570
|
setCrosshairPoint(null);
|
|
3267
3571
|
return;
|
|
3268
3572
|
}
|
|
3573
|
+
if (!activeDrawingTool && getDrawingHit(point.x, point.y)) {
|
|
3574
|
+
const drawingHit = getDrawingHit(point.x, point.y);
|
|
3575
|
+
canvas.style.cursor = drawingHit?.drawing.locked ? "pointer" : drawingHit?.target === "handle" ? "grab" : "move";
|
|
3576
|
+
setCrosshairPoint(null);
|
|
3577
|
+
return;
|
|
3578
|
+
}
|
|
3269
3579
|
const hoverRegion = getHitRegion(point.x, point.y);
|
|
3270
3580
|
if (hoverRegion === "plot") {
|
|
3271
|
-
canvas.style.cursor = doubleClickEnabled ? "default" : "crosshair";
|
|
3581
|
+
canvas.style.cursor = activeDrawingTool ? "crosshair" : doubleClickEnabled ? "default" : "crosshair";
|
|
3272
3582
|
setCrosshairPoint(point);
|
|
3273
3583
|
emitCrosshairMove(point.x, point.y, "plot");
|
|
3274
3584
|
} else if (hoverRegion === "x-axis") {
|
|
@@ -3358,6 +3668,10 @@ function createChart(element, options = {}) {
|
|
|
3358
3668
|
}
|
|
3359
3669
|
actionDragState = null;
|
|
3360
3670
|
}
|
|
3671
|
+
if (drawingDragState) {
|
|
3672
|
+
drawingDragState = null;
|
|
3673
|
+
emitDrawingsChange();
|
|
3674
|
+
}
|
|
3361
3675
|
isDragging = false;
|
|
3362
3676
|
dragMode = null;
|
|
3363
3677
|
activePointerId = null;
|
|
@@ -3667,6 +3981,56 @@ function createChart(element, options = {}) {
|
|
|
3667
3981
|
indicators = indicators.filter((indicator) => indicator.id !== id);
|
|
3668
3982
|
draw();
|
|
3669
3983
|
};
|
|
3984
|
+
const setActiveDrawingTool = (tool) => {
|
|
3985
|
+
activeDrawingTool = tool;
|
|
3986
|
+
draftDrawing = null;
|
|
3987
|
+
canvas.style.cursor = tool ? "crosshair" : "default";
|
|
3988
|
+
draw();
|
|
3989
|
+
};
|
|
3990
|
+
const getActiveDrawingTool = () => activeDrawingTool;
|
|
3991
|
+
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
3992
|
+
const setDrawings = (nextDrawings) => {
|
|
3993
|
+
drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
|
|
3994
|
+
draftDrawing = null;
|
|
3995
|
+
emitDrawingsChange();
|
|
3996
|
+
draw();
|
|
3997
|
+
};
|
|
3998
|
+
const addDrawing = (drawing) => {
|
|
3999
|
+
const next = normalizeDrawingState(drawing);
|
|
4000
|
+
drawings.push(next);
|
|
4001
|
+
emitDrawingsChange();
|
|
4002
|
+
draw();
|
|
4003
|
+
return next.id;
|
|
4004
|
+
};
|
|
4005
|
+
const updateDrawing = (id, patch) => {
|
|
4006
|
+
drawings = drawings.map(
|
|
4007
|
+
(drawing) => drawing.id === id ? normalizeDrawingState({
|
|
4008
|
+
...serializeDrawing(drawing),
|
|
4009
|
+
...patch,
|
|
4010
|
+
id,
|
|
4011
|
+
points: patch.points ?? drawing.points
|
|
4012
|
+
}) : drawing
|
|
4013
|
+
);
|
|
4014
|
+
emitDrawingsChange();
|
|
4015
|
+
draw();
|
|
4016
|
+
};
|
|
4017
|
+
const removeDrawing = (id) => {
|
|
4018
|
+
drawings = drawings.filter((drawing) => drawing.id !== id);
|
|
4019
|
+
emitDrawingsChange();
|
|
4020
|
+
draw();
|
|
4021
|
+
};
|
|
4022
|
+
const clearDrawings = () => {
|
|
4023
|
+
drawings = [];
|
|
4024
|
+
draftDrawing = null;
|
|
4025
|
+
emitDrawingsChange();
|
|
4026
|
+
draw();
|
|
4027
|
+
};
|
|
4028
|
+
const onDrawingsChange = (handler) => {
|
|
4029
|
+
drawingsChangeHandler = handler;
|
|
4030
|
+
};
|
|
4031
|
+
const onDrawingSelect = (handler) => {
|
|
4032
|
+
drawingSelectHandler = handler;
|
|
4033
|
+
};
|
|
3670
4034
|
const destroy = () => {
|
|
3671
4035
|
if (smoothingRafId !== null) {
|
|
3672
4036
|
cancelAnimationFrame(smoothingRafId);
|
|
@@ -3710,6 +4074,16 @@ function createChart(element, options = {}) {
|
|
|
3710
4074
|
getViewport,
|
|
3711
4075
|
setViewport,
|
|
3712
4076
|
onViewportChange,
|
|
4077
|
+
setActiveDrawingTool,
|
|
4078
|
+
getActiveDrawingTool,
|
|
4079
|
+
setDrawings,
|
|
4080
|
+
getDrawings,
|
|
4081
|
+
addDrawing,
|
|
4082
|
+
updateDrawing,
|
|
4083
|
+
removeDrawing,
|
|
4084
|
+
clearDrawings,
|
|
4085
|
+
onDrawingsChange,
|
|
4086
|
+
onDrawingSelect,
|
|
3713
4087
|
setDoubleClickEnabled,
|
|
3714
4088
|
setDoubleClickAction,
|
|
3715
4089
|
registerIndicator,
|
package/dist/index.d.cts
CHANGED
|
@@ -40,8 +40,33 @@ interface ChartOptions {
|
|
|
40
40
|
labels?: LabelsOptions;
|
|
41
41
|
dashPatterns?: Partial<DashPatternOptions>;
|
|
42
42
|
indicators?: IndicatorInstanceOptions[];
|
|
43
|
+
drawings?: DrawingObjectOptions[];
|
|
43
44
|
}
|
|
44
45
|
type IndicatorPane = "overlay" | "separate";
|
|
46
|
+
type DrawingToolType = "horizontal-line" | "trendline";
|
|
47
|
+
interface DrawingPoint {
|
|
48
|
+
index: number;
|
|
49
|
+
price: number;
|
|
50
|
+
time?: string;
|
|
51
|
+
}
|
|
52
|
+
interface DrawingObjectOptions {
|
|
53
|
+
id?: string;
|
|
54
|
+
type: DrawingToolType;
|
|
55
|
+
points: DrawingPoint[];
|
|
56
|
+
visible?: boolean;
|
|
57
|
+
color?: string;
|
|
58
|
+
style?: "solid" | "dotted" | "dashed";
|
|
59
|
+
width?: number;
|
|
60
|
+
label?: string;
|
|
61
|
+
locked?: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface DrawingSelectEvent {
|
|
64
|
+
drawing: DrawingObjectOptions;
|
|
65
|
+
target: "line" | "handle";
|
|
66
|
+
pointIndex?: number;
|
|
67
|
+
x: number;
|
|
68
|
+
y: number;
|
|
69
|
+
}
|
|
45
70
|
interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
|
|
46
71
|
id?: string;
|
|
47
72
|
type: string;
|
|
@@ -363,6 +388,16 @@ interface ChartInstance {
|
|
|
363
388
|
getViewport: () => ViewportState;
|
|
364
389
|
setViewport: (viewport: Partial<ViewportState>) => void;
|
|
365
390
|
onViewportChange: (handler: ((viewport: ViewportState) => void) | null) => void;
|
|
391
|
+
setActiveDrawingTool: (tool: DrawingToolType | null) => void;
|
|
392
|
+
getActiveDrawingTool: () => DrawingToolType | null;
|
|
393
|
+
setDrawings: (nextDrawings: DrawingObjectOptions[]) => void;
|
|
394
|
+
getDrawings: () => DrawingObjectOptions[];
|
|
395
|
+
addDrawing: (drawing: DrawingObjectOptions) => string;
|
|
396
|
+
updateDrawing: (id: string, patch: Partial<DrawingObjectOptions>) => void;
|
|
397
|
+
removeDrawing: (id: string) => void;
|
|
398
|
+
clearDrawings: () => void;
|
|
399
|
+
onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
|
|
400
|
+
onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
366
401
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
367
402
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
368
403
|
registerIndicator: (plugin: IndicatorPlugin<any>) => void;
|
|
@@ -398,4 +433,4 @@ interface ViewportState {
|
|
|
398
433
|
}
|
|
399
434
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
400
435
|
|
|
401
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|
|
436
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|
package/dist/index.d.ts
CHANGED
|
@@ -40,8 +40,33 @@ interface ChartOptions {
|
|
|
40
40
|
labels?: LabelsOptions;
|
|
41
41
|
dashPatterns?: Partial<DashPatternOptions>;
|
|
42
42
|
indicators?: IndicatorInstanceOptions[];
|
|
43
|
+
drawings?: DrawingObjectOptions[];
|
|
43
44
|
}
|
|
44
45
|
type IndicatorPane = "overlay" | "separate";
|
|
46
|
+
type DrawingToolType = "horizontal-line" | "trendline";
|
|
47
|
+
interface DrawingPoint {
|
|
48
|
+
index: number;
|
|
49
|
+
price: number;
|
|
50
|
+
time?: string;
|
|
51
|
+
}
|
|
52
|
+
interface DrawingObjectOptions {
|
|
53
|
+
id?: string;
|
|
54
|
+
type: DrawingToolType;
|
|
55
|
+
points: DrawingPoint[];
|
|
56
|
+
visible?: boolean;
|
|
57
|
+
color?: string;
|
|
58
|
+
style?: "solid" | "dotted" | "dashed";
|
|
59
|
+
width?: number;
|
|
60
|
+
label?: string;
|
|
61
|
+
locked?: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface DrawingSelectEvent {
|
|
64
|
+
drawing: DrawingObjectOptions;
|
|
65
|
+
target: "line" | "handle";
|
|
66
|
+
pointIndex?: number;
|
|
67
|
+
x: number;
|
|
68
|
+
y: number;
|
|
69
|
+
}
|
|
45
70
|
interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
|
|
46
71
|
id?: string;
|
|
47
72
|
type: string;
|
|
@@ -363,6 +388,16 @@ interface ChartInstance {
|
|
|
363
388
|
getViewport: () => ViewportState;
|
|
364
389
|
setViewport: (viewport: Partial<ViewportState>) => void;
|
|
365
390
|
onViewportChange: (handler: ((viewport: ViewportState) => void) | null) => void;
|
|
391
|
+
setActiveDrawingTool: (tool: DrawingToolType | null) => void;
|
|
392
|
+
getActiveDrawingTool: () => DrawingToolType | null;
|
|
393
|
+
setDrawings: (nextDrawings: DrawingObjectOptions[]) => void;
|
|
394
|
+
getDrawings: () => DrawingObjectOptions[];
|
|
395
|
+
addDrawing: (drawing: DrawingObjectOptions) => string;
|
|
396
|
+
updateDrawing: (id: string, patch: Partial<DrawingObjectOptions>) => void;
|
|
397
|
+
removeDrawing: (id: string) => void;
|
|
398
|
+
clearDrawings: () => void;
|
|
399
|
+
onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
|
|
400
|
+
onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
366
401
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
367
402
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
368
403
|
registerIndicator: (plugin: IndicatorPlugin<any>) => void;
|
|
@@ -398,4 +433,4 @@ interface ViewportState {
|
|
|
398
433
|
}
|
|
399
434
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
400
435
|
|
|
401
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|
|
436
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|