hyperprop-charting-library 0.1.16 → 0.1.18
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/README.md +17 -1
- package/dist/hyperprop-charting-library.cjs +116 -18
- package/dist/hyperprop-charting-library.d.ts +14 -1
- package/dist/hyperprop-charting-library.js +116 -18
- package/dist/index.cjs +116 -18
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +116 -18
- package/docs/API.md +10 -0
- package/docs/EVENTS.md +27 -0
- package/docs/RECIPES.md +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,22 @@ const chart = createChart(root, {
|
|
|
60
60
|
});
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Crosshair "+" Button
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const chart = createChart(root, {
|
|
67
|
+
crosshair: {
|
|
68
|
+
showPriceActionButton: true,
|
|
69
|
+
priceActionButtonRounded: false // square button
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
chart.onCrosshairPriceAction((event) => {
|
|
74
|
+
// Frontend decides what to do
|
|
75
|
+
console.log(event.price);
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
63
79
|
## Full Documentation
|
|
64
80
|
|
|
65
81
|
- API reference: `docs/API.md`
|
|
@@ -74,7 +90,7 @@ const chart = createChart(root, {
|
|
|
74
90
|
- `chart.setData(data)`
|
|
75
91
|
- `chart.setPriceLines(lines)` / `chart.addPriceLine(line)` / `chart.removePriceLine(id)`
|
|
76
92
|
- `chart.setOrderLines(lines)` / `chart.addOrderLine(line)` / `chart.updateOrderLine(id, patch)` / `chart.removeOrderLine(id)`
|
|
77
|
-
- `chart.onOrderAction(handler)` / `chart.onChartClick(handler)` / `chart.onCrosshairMove(handler)`
|
|
93
|
+
- `chart.onOrderAction(handler)` / `chart.onChartClick(handler)` / `chart.onCrosshairMove(handler)` / `chart.onCrosshairPriceAction(handler)`
|
|
78
94
|
- `chart.setDoubleClickEnabled(enabled)` / `chart.setDoubleClickAction(action)`
|
|
79
95
|
- `chart.zoomInX()` / `chart.zoomOutX()` / `chart.panX(bars)` / `chart.resetViewport()`
|
|
80
96
|
- `chart.resize(width, height)` / `chart.destroy()`
|
|
@@ -51,7 +51,14 @@ var DEFAULT_CROSSHAIR_OPTIONS = {
|
|
|
51
51
|
labelBorderRadius: 3,
|
|
52
52
|
labelBorderColor: "#94a3b8",
|
|
53
53
|
labelBorderWidth: 1,
|
|
54
|
-
labelBorderStyle: "solid"
|
|
54
|
+
labelBorderStyle: "solid",
|
|
55
|
+
showPriceActionButton: false,
|
|
56
|
+
priceActionButtonIcon: "plusThin",
|
|
57
|
+
priceActionButtonText: "+",
|
|
58
|
+
priceActionButtonSize: 16,
|
|
59
|
+
priceActionButtonGap: 4,
|
|
60
|
+
priceActionButtonRounded: true,
|
|
61
|
+
priceActionButtonBorderRadius: 8
|
|
55
62
|
};
|
|
56
63
|
var DEFAULT_WATERMARK_OPTIONS = {
|
|
57
64
|
visible: false,
|
|
@@ -163,10 +170,6 @@ var DEFAULT_OPTIONS = {
|
|
|
163
170
|
},
|
|
164
171
|
dashPatterns: DEFAULT_DASH_PATTERNS
|
|
165
172
|
};
|
|
166
|
-
var BRAND_LOGO_VIEWBOX_WIDTH = 190;
|
|
167
|
-
var BRAND_LOGO_VIEWBOX_HEIGHT = 186;
|
|
168
|
-
var BRAND_LOGO_PATH_A = "M0 93.0171V45.2271H48.9851V75.0332C48.9851 84.9545 57.0416 93.0001 66.9763 93.0001H94.9957V186H49.0531V110.984C49.0531 101.063 40.9965 93.0171 31.0619 93.0171H0Z";
|
|
169
|
-
var BRAND_LOGO_PATH_B = "M190 92.9915V140.782H141.015V110.975C141.015 101.054 132.958 93.0085 123.023 93.0085H95.0039V0H140.955V75.0162C140.955 84.9374 149.012 92.9831 158.946 92.9831H190V92.9915Z";
|
|
170
173
|
function createChart(element, options = {}) {
|
|
171
174
|
const mergedOptions = {
|
|
172
175
|
...DEFAULT_OPTIONS,
|
|
@@ -211,6 +214,8 @@ function createChart(element, options = {}) {
|
|
|
211
214
|
let orderActionHandler = null;
|
|
212
215
|
let chartClickHandler = null;
|
|
213
216
|
let crosshairMoveHandler = null;
|
|
217
|
+
let crosshairPriceActionHandler = null;
|
|
218
|
+
let crosshairPriceActionRegion = null;
|
|
214
219
|
let orderActionRegions = [];
|
|
215
220
|
let orderDragRegions = [];
|
|
216
221
|
let generatedPriceLineId = 1;
|
|
@@ -223,8 +228,6 @@ function createChart(element, options = {}) {
|
|
|
223
228
|
let yMaxOverride = null;
|
|
224
229
|
let autoYMin = null;
|
|
225
230
|
let autoYMax = null;
|
|
226
|
-
const brandLogoPathA = new Path2D(BRAND_LOGO_PATH_A);
|
|
227
|
-
const brandLogoPathB = new Path2D(BRAND_LOGO_PATH_B);
|
|
228
231
|
let watermarkImageSrc = null;
|
|
229
232
|
let watermarkImage = null;
|
|
230
233
|
let watermarkImageReady = false;
|
|
@@ -802,6 +805,7 @@ function createChart(element, options = {}) {
|
|
|
802
805
|
const draw = () => {
|
|
803
806
|
orderActionRegions = [];
|
|
804
807
|
orderDragRegions = [];
|
|
808
|
+
crosshairPriceActionRegion = null;
|
|
805
809
|
const pixelRatio = getPixelRatio();
|
|
806
810
|
canvas.style.width = `${width}px`;
|
|
807
811
|
canvas.style.height = `${height}px`;
|
|
@@ -1107,17 +1111,6 @@ function createChart(element, options = {}) {
|
|
|
1107
1111
|
});
|
|
1108
1112
|
drawText(timeLabel, x, chartBottom + 8, "center", "top", axis.textColor);
|
|
1109
1113
|
}
|
|
1110
|
-
const brandLogoWidth = 34;
|
|
1111
|
-
const brandLogoHeight = BRAND_LOGO_VIEWBOX_HEIGHT / BRAND_LOGO_VIEWBOX_WIDTH * brandLogoWidth;
|
|
1112
|
-
const brandLogoX = chartLeft + 16;
|
|
1113
|
-
const brandLogoY = chartBottom - brandLogoHeight - 10;
|
|
1114
|
-
ctx.save();
|
|
1115
|
-
ctx.translate(brandLogoX, brandLogoY);
|
|
1116
|
-
ctx.scale(brandLogoWidth / BRAND_LOGO_VIEWBOX_WIDTH, brandLogoHeight / BRAND_LOGO_VIEWBOX_HEIGHT);
|
|
1117
|
-
ctx.fillStyle = "#ffffff";
|
|
1118
|
-
ctx.fill(brandLogoPathA);
|
|
1119
|
-
ctx.fill(brandLogoPathB);
|
|
1120
|
-
ctx.restore();
|
|
1121
1114
|
if (crosshair.visible && crosshairPoint) {
|
|
1122
1115
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
1123
1116
|
const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
|
|
@@ -1154,6 +1147,83 @@ function createChart(element, options = {}) {
|
|
|
1154
1147
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|
|
1155
1148
|
strokeCrosshairLabel(priceX, priceY, priceWidth);
|
|
1156
1149
|
drawText(priceText, priceX + labelPaddingX, priceY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1150
|
+
if (crosshair.showPriceActionButton) {
|
|
1151
|
+
const buttonSize = clamp(Math.round(crosshair.priceActionButtonSize), 12, Math.max(12, labelHeight - 4));
|
|
1152
|
+
const buttonGap = Math.max(0, Math.round(crosshair.priceActionButtonGap));
|
|
1153
|
+
const containerPaddingX = 5;
|
|
1154
|
+
const containerWidth = buttonSize + containerPaddingX * 2;
|
|
1155
|
+
const containerX = priceX - buttonGap - containerWidth;
|
|
1156
|
+
const containerY = priceY;
|
|
1157
|
+
const buttonX = containerX + containerPaddingX;
|
|
1158
|
+
const buttonY = priceY + (labelHeight - buttonSize) / 2;
|
|
1159
|
+
const buttonRadius = crosshair.priceActionButtonRounded ? clamp(Math.round(crosshair.priceActionButtonBorderRadius), 0, buttonSize / 2) : 0;
|
|
1160
|
+
const buttonBorderWidth = Math.max(1, Math.round(labelBorderWidth || 1));
|
|
1161
|
+
const buttonCenterX = buttonX + buttonSize / 2;
|
|
1162
|
+
const buttonCenterY = buttonY + buttonSize / 2;
|
|
1163
|
+
const containerRadius = labelRadius;
|
|
1164
|
+
ctx.fillStyle = labelBackground;
|
|
1165
|
+
fillRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1166
|
+
if (labelBorderWidth > 0) {
|
|
1167
|
+
ctx.save();
|
|
1168
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1169
|
+
ctx.lineWidth = labelBorderWidth;
|
|
1170
|
+
ctx.setLineDash([]);
|
|
1171
|
+
strokeRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1172
|
+
ctx.restore();
|
|
1173
|
+
}
|
|
1174
|
+
if (buttonBorderWidth > 0) {
|
|
1175
|
+
ctx.save();
|
|
1176
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1177
|
+
ctx.lineWidth = buttonBorderWidth;
|
|
1178
|
+
ctx.setLineDash([]);
|
|
1179
|
+
if (crosshair.priceActionButtonRounded) {
|
|
1180
|
+
ctx.beginPath();
|
|
1181
|
+
ctx.arc(
|
|
1182
|
+
buttonCenterX,
|
|
1183
|
+
buttonCenterY,
|
|
1184
|
+
Math.max(1, buttonSize / 2 - buttonBorderWidth / 2),
|
|
1185
|
+
0,
|
|
1186
|
+
Math.PI * 2
|
|
1187
|
+
);
|
|
1188
|
+
ctx.stroke();
|
|
1189
|
+
} else {
|
|
1190
|
+
strokeRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, buttonRadius);
|
|
1191
|
+
}
|
|
1192
|
+
ctx.restore();
|
|
1193
|
+
}
|
|
1194
|
+
if (crosshair.priceActionButtonIcon !== "text" && crosshair.priceActionButtonText === "+") {
|
|
1195
|
+
const plusHalf = Math.max(3, Math.round(buttonSize * 0.22));
|
|
1196
|
+
const plusThickness = crosshair.priceActionButtonIcon === "plusThin" ? Math.max(1, Math.round(buttonSize * 0.07)) : Math.max(1, Math.round(buttonSize * 0.1));
|
|
1197
|
+
ctx.save();
|
|
1198
|
+
ctx.strokeStyle = labelTextColor;
|
|
1199
|
+
ctx.lineWidth = plusThickness;
|
|
1200
|
+
ctx.lineCap = "round";
|
|
1201
|
+
ctx.setLineDash([]);
|
|
1202
|
+
ctx.beginPath();
|
|
1203
|
+
ctx.moveTo(buttonCenterX - plusHalf, buttonCenterY);
|
|
1204
|
+
ctx.lineTo(buttonCenterX + plusHalf, buttonCenterY);
|
|
1205
|
+
ctx.moveTo(buttonCenterX, buttonCenterY - plusHalf);
|
|
1206
|
+
ctx.lineTo(buttonCenterX, buttonCenterY + plusHalf);
|
|
1207
|
+
ctx.stroke();
|
|
1208
|
+
ctx.restore();
|
|
1209
|
+
} else {
|
|
1210
|
+
drawText(
|
|
1211
|
+
crosshair.priceActionButtonText,
|
|
1212
|
+
buttonCenterX,
|
|
1213
|
+
buttonCenterY,
|
|
1214
|
+
"center",
|
|
1215
|
+
"middle",
|
|
1216
|
+
labelTextColor
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
crosshairPriceActionRegion = {
|
|
1220
|
+
x: containerX,
|
|
1221
|
+
y: containerY,
|
|
1222
|
+
width: containerWidth,
|
|
1223
|
+
height: labelHeight,
|
|
1224
|
+
price: Number(hoverPrice.toFixed(mergedOptions.priceDecimals))
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1157
1227
|
}
|
|
1158
1228
|
if (crosshair.showTimeLabel) {
|
|
1159
1229
|
const ratio = clamp((cx - chartLeft) / chartWidth, 0, 1);
|
|
@@ -1316,6 +1386,16 @@ function createChart(element, options = {}) {
|
|
|
1316
1386
|
(region) => x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height
|
|
1317
1387
|
);
|
|
1318
1388
|
};
|
|
1389
|
+
const getCrosshairPriceActionRegion = (x, y) => {
|
|
1390
|
+
if (!crosshairPriceActionRegion) {
|
|
1391
|
+
return null;
|
|
1392
|
+
}
|
|
1393
|
+
const region = crosshairPriceActionRegion;
|
|
1394
|
+
if (x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height) {
|
|
1395
|
+
return region;
|
|
1396
|
+
}
|
|
1397
|
+
return null;
|
|
1398
|
+
};
|
|
1319
1399
|
const priceFromCanvasY = (y) => {
|
|
1320
1400
|
if (!drawState) {
|
|
1321
1401
|
return 0;
|
|
@@ -1381,6 +1461,15 @@ function createChart(element, options = {}) {
|
|
|
1381
1461
|
let activePointerId = null;
|
|
1382
1462
|
const onPointerDown = (event) => {
|
|
1383
1463
|
const point = getCanvasPoint(event);
|
|
1464
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1465
|
+
if (crosshairButtonRegion) {
|
|
1466
|
+
crosshairPriceActionHandler?.({
|
|
1467
|
+
x: point.x,
|
|
1468
|
+
y: point.y,
|
|
1469
|
+
price: crosshairButtonRegion.price
|
|
1470
|
+
});
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1384
1473
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1385
1474
|
if (orderRegion) {
|
|
1386
1475
|
if (orderRegion.draggable) {
|
|
@@ -1493,6 +1582,11 @@ function createChart(element, options = {}) {
|
|
|
1493
1582
|
return;
|
|
1494
1583
|
}
|
|
1495
1584
|
if (!isDragging || !dragMode) {
|
|
1585
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1586
|
+
if (crosshairButtonRegion) {
|
|
1587
|
+
canvas.style.cursor = "pointer";
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1496
1590
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1497
1591
|
if (orderRegion) {
|
|
1498
1592
|
canvas.style.cursor = orderRegion.draggable ? "ns-resize" : "pointer";
|
|
@@ -1746,6 +1840,9 @@ function createChart(element, options = {}) {
|
|
|
1746
1840
|
const onCrosshairMove = (handler) => {
|
|
1747
1841
|
crosshairMoveHandler = handler;
|
|
1748
1842
|
};
|
|
1843
|
+
const onCrosshairPriceAction = (handler) => {
|
|
1844
|
+
crosshairPriceActionHandler = handler;
|
|
1845
|
+
};
|
|
1749
1846
|
const setDoubleClickEnabled = (enabled) => {
|
|
1750
1847
|
doubleClickEnabled = enabled;
|
|
1751
1848
|
};
|
|
@@ -1775,6 +1872,7 @@ function createChart(element, options = {}) {
|
|
|
1775
1872
|
onOrderAction,
|
|
1776
1873
|
onChartClick,
|
|
1777
1874
|
onCrosshairMove,
|
|
1875
|
+
onCrosshairPriceAction,
|
|
1778
1876
|
zoomInX,
|
|
1779
1877
|
zoomOutX,
|
|
1780
1878
|
zoomInY,
|
|
@@ -71,6 +71,13 @@ interface CrosshairOptions {
|
|
|
71
71
|
labelBorderColor?: string;
|
|
72
72
|
labelBorderWidth?: number;
|
|
73
73
|
labelBorderStyle?: "solid" | "dotted" | "dashed";
|
|
74
|
+
showPriceActionButton?: boolean;
|
|
75
|
+
priceActionButtonIcon?: "plus" | "plusThin" | "text";
|
|
76
|
+
priceActionButtonText?: string;
|
|
77
|
+
priceActionButtonSize?: number;
|
|
78
|
+
priceActionButtonGap?: number;
|
|
79
|
+
priceActionButtonRounded?: boolean;
|
|
80
|
+
priceActionButtonBorderRadius?: number;
|
|
74
81
|
}
|
|
75
82
|
interface WatermarkOptions {
|
|
76
83
|
visible?: boolean;
|
|
@@ -178,6 +185,11 @@ interface CrosshairMoveEvent {
|
|
|
178
185
|
time?: string;
|
|
179
186
|
point?: OhlcDataPoint;
|
|
180
187
|
}
|
|
188
|
+
interface CrosshairPriceActionEvent {
|
|
189
|
+
x: number;
|
|
190
|
+
y: number;
|
|
191
|
+
price: number;
|
|
192
|
+
}
|
|
181
193
|
interface TickerLineOptions {
|
|
182
194
|
visible?: boolean;
|
|
183
195
|
style?: "solid" | "dotted" | "dashed";
|
|
@@ -199,6 +211,7 @@ interface ChartInstance {
|
|
|
199
211
|
onOrderAction: (handler: ((event: OrderActionEvent) => void) | null) => void;
|
|
200
212
|
onChartClick: (handler: ((event: ChartClickEvent) => void) | null) => void;
|
|
201
213
|
onCrosshairMove: (handler: ((event: CrosshairMoveEvent) => void) | null) => void;
|
|
214
|
+
onCrosshairPriceAction: (handler: ((event: CrosshairPriceActionEvent) => void) | null) => void;
|
|
202
215
|
zoomInX: (factor?: number) => void;
|
|
203
216
|
zoomOutX: (factor?: number) => void;
|
|
204
217
|
zoomInY: (factor?: number) => void;
|
|
@@ -222,4 +235,4 @@ interface OhlcDataPoint {
|
|
|
222
235
|
}
|
|
223
236
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
224
237
|
|
|
225
|
-
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type DashPatternOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
238
|
+
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
@@ -27,7 +27,14 @@ var DEFAULT_CROSSHAIR_OPTIONS = {
|
|
|
27
27
|
labelBorderRadius: 3,
|
|
28
28
|
labelBorderColor: "#94a3b8",
|
|
29
29
|
labelBorderWidth: 1,
|
|
30
|
-
labelBorderStyle: "solid"
|
|
30
|
+
labelBorderStyle: "solid",
|
|
31
|
+
showPriceActionButton: false,
|
|
32
|
+
priceActionButtonIcon: "plusThin",
|
|
33
|
+
priceActionButtonText: "+",
|
|
34
|
+
priceActionButtonSize: 16,
|
|
35
|
+
priceActionButtonGap: 4,
|
|
36
|
+
priceActionButtonRounded: true,
|
|
37
|
+
priceActionButtonBorderRadius: 8
|
|
31
38
|
};
|
|
32
39
|
var DEFAULT_WATERMARK_OPTIONS = {
|
|
33
40
|
visible: false,
|
|
@@ -139,10 +146,6 @@ var DEFAULT_OPTIONS = {
|
|
|
139
146
|
},
|
|
140
147
|
dashPatterns: DEFAULT_DASH_PATTERNS
|
|
141
148
|
};
|
|
142
|
-
var BRAND_LOGO_VIEWBOX_WIDTH = 190;
|
|
143
|
-
var BRAND_LOGO_VIEWBOX_HEIGHT = 186;
|
|
144
|
-
var BRAND_LOGO_PATH_A = "M0 93.0171V45.2271H48.9851V75.0332C48.9851 84.9545 57.0416 93.0001 66.9763 93.0001H94.9957V186H49.0531V110.984C49.0531 101.063 40.9965 93.0171 31.0619 93.0171H0Z";
|
|
145
|
-
var BRAND_LOGO_PATH_B = "M190 92.9915V140.782H141.015V110.975C141.015 101.054 132.958 93.0085 123.023 93.0085H95.0039V0H140.955V75.0162C140.955 84.9374 149.012 92.9831 158.946 92.9831H190V92.9915Z";
|
|
146
149
|
function createChart(element, options = {}) {
|
|
147
150
|
const mergedOptions = {
|
|
148
151
|
...DEFAULT_OPTIONS,
|
|
@@ -187,6 +190,8 @@ function createChart(element, options = {}) {
|
|
|
187
190
|
let orderActionHandler = null;
|
|
188
191
|
let chartClickHandler = null;
|
|
189
192
|
let crosshairMoveHandler = null;
|
|
193
|
+
let crosshairPriceActionHandler = null;
|
|
194
|
+
let crosshairPriceActionRegion = null;
|
|
190
195
|
let orderActionRegions = [];
|
|
191
196
|
let orderDragRegions = [];
|
|
192
197
|
let generatedPriceLineId = 1;
|
|
@@ -199,8 +204,6 @@ function createChart(element, options = {}) {
|
|
|
199
204
|
let yMaxOverride = null;
|
|
200
205
|
let autoYMin = null;
|
|
201
206
|
let autoYMax = null;
|
|
202
|
-
const brandLogoPathA = new Path2D(BRAND_LOGO_PATH_A);
|
|
203
|
-
const brandLogoPathB = new Path2D(BRAND_LOGO_PATH_B);
|
|
204
207
|
let watermarkImageSrc = null;
|
|
205
208
|
let watermarkImage = null;
|
|
206
209
|
let watermarkImageReady = false;
|
|
@@ -778,6 +781,7 @@ function createChart(element, options = {}) {
|
|
|
778
781
|
const draw = () => {
|
|
779
782
|
orderActionRegions = [];
|
|
780
783
|
orderDragRegions = [];
|
|
784
|
+
crosshairPriceActionRegion = null;
|
|
781
785
|
const pixelRatio = getPixelRatio();
|
|
782
786
|
canvas.style.width = `${width}px`;
|
|
783
787
|
canvas.style.height = `${height}px`;
|
|
@@ -1083,17 +1087,6 @@ function createChart(element, options = {}) {
|
|
|
1083
1087
|
});
|
|
1084
1088
|
drawText(timeLabel, x, chartBottom + 8, "center", "top", axis.textColor);
|
|
1085
1089
|
}
|
|
1086
|
-
const brandLogoWidth = 34;
|
|
1087
|
-
const brandLogoHeight = BRAND_LOGO_VIEWBOX_HEIGHT / BRAND_LOGO_VIEWBOX_WIDTH * brandLogoWidth;
|
|
1088
|
-
const brandLogoX = chartLeft + 16;
|
|
1089
|
-
const brandLogoY = chartBottom - brandLogoHeight - 10;
|
|
1090
|
-
ctx.save();
|
|
1091
|
-
ctx.translate(brandLogoX, brandLogoY);
|
|
1092
|
-
ctx.scale(brandLogoWidth / BRAND_LOGO_VIEWBOX_WIDTH, brandLogoHeight / BRAND_LOGO_VIEWBOX_HEIGHT);
|
|
1093
|
-
ctx.fillStyle = "#ffffff";
|
|
1094
|
-
ctx.fill(brandLogoPathA);
|
|
1095
|
-
ctx.fill(brandLogoPathB);
|
|
1096
|
-
ctx.restore();
|
|
1097
1090
|
if (crosshair.visible && crosshairPoint) {
|
|
1098
1091
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
1099
1092
|
const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
|
|
@@ -1130,6 +1123,83 @@ function createChart(element, options = {}) {
|
|
|
1130
1123
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|
|
1131
1124
|
strokeCrosshairLabel(priceX, priceY, priceWidth);
|
|
1132
1125
|
drawText(priceText, priceX + labelPaddingX, priceY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1126
|
+
if (crosshair.showPriceActionButton) {
|
|
1127
|
+
const buttonSize = clamp(Math.round(crosshair.priceActionButtonSize), 12, Math.max(12, labelHeight - 4));
|
|
1128
|
+
const buttonGap = Math.max(0, Math.round(crosshair.priceActionButtonGap));
|
|
1129
|
+
const containerPaddingX = 5;
|
|
1130
|
+
const containerWidth = buttonSize + containerPaddingX * 2;
|
|
1131
|
+
const containerX = priceX - buttonGap - containerWidth;
|
|
1132
|
+
const containerY = priceY;
|
|
1133
|
+
const buttonX = containerX + containerPaddingX;
|
|
1134
|
+
const buttonY = priceY + (labelHeight - buttonSize) / 2;
|
|
1135
|
+
const buttonRadius = crosshair.priceActionButtonRounded ? clamp(Math.round(crosshair.priceActionButtonBorderRadius), 0, buttonSize / 2) : 0;
|
|
1136
|
+
const buttonBorderWidth = Math.max(1, Math.round(labelBorderWidth || 1));
|
|
1137
|
+
const buttonCenterX = buttonX + buttonSize / 2;
|
|
1138
|
+
const buttonCenterY = buttonY + buttonSize / 2;
|
|
1139
|
+
const containerRadius = labelRadius;
|
|
1140
|
+
ctx.fillStyle = labelBackground;
|
|
1141
|
+
fillRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1142
|
+
if (labelBorderWidth > 0) {
|
|
1143
|
+
ctx.save();
|
|
1144
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1145
|
+
ctx.lineWidth = labelBorderWidth;
|
|
1146
|
+
ctx.setLineDash([]);
|
|
1147
|
+
strokeRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1148
|
+
ctx.restore();
|
|
1149
|
+
}
|
|
1150
|
+
if (buttonBorderWidth > 0) {
|
|
1151
|
+
ctx.save();
|
|
1152
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1153
|
+
ctx.lineWidth = buttonBorderWidth;
|
|
1154
|
+
ctx.setLineDash([]);
|
|
1155
|
+
if (crosshair.priceActionButtonRounded) {
|
|
1156
|
+
ctx.beginPath();
|
|
1157
|
+
ctx.arc(
|
|
1158
|
+
buttonCenterX,
|
|
1159
|
+
buttonCenterY,
|
|
1160
|
+
Math.max(1, buttonSize / 2 - buttonBorderWidth / 2),
|
|
1161
|
+
0,
|
|
1162
|
+
Math.PI * 2
|
|
1163
|
+
);
|
|
1164
|
+
ctx.stroke();
|
|
1165
|
+
} else {
|
|
1166
|
+
strokeRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, buttonRadius);
|
|
1167
|
+
}
|
|
1168
|
+
ctx.restore();
|
|
1169
|
+
}
|
|
1170
|
+
if (crosshair.priceActionButtonIcon !== "text" && crosshair.priceActionButtonText === "+") {
|
|
1171
|
+
const plusHalf = Math.max(3, Math.round(buttonSize * 0.22));
|
|
1172
|
+
const plusThickness = crosshair.priceActionButtonIcon === "plusThin" ? Math.max(1, Math.round(buttonSize * 0.07)) : Math.max(1, Math.round(buttonSize * 0.1));
|
|
1173
|
+
ctx.save();
|
|
1174
|
+
ctx.strokeStyle = labelTextColor;
|
|
1175
|
+
ctx.lineWidth = plusThickness;
|
|
1176
|
+
ctx.lineCap = "round";
|
|
1177
|
+
ctx.setLineDash([]);
|
|
1178
|
+
ctx.beginPath();
|
|
1179
|
+
ctx.moveTo(buttonCenterX - plusHalf, buttonCenterY);
|
|
1180
|
+
ctx.lineTo(buttonCenterX + plusHalf, buttonCenterY);
|
|
1181
|
+
ctx.moveTo(buttonCenterX, buttonCenterY - plusHalf);
|
|
1182
|
+
ctx.lineTo(buttonCenterX, buttonCenterY + plusHalf);
|
|
1183
|
+
ctx.stroke();
|
|
1184
|
+
ctx.restore();
|
|
1185
|
+
} else {
|
|
1186
|
+
drawText(
|
|
1187
|
+
crosshair.priceActionButtonText,
|
|
1188
|
+
buttonCenterX,
|
|
1189
|
+
buttonCenterY,
|
|
1190
|
+
"center",
|
|
1191
|
+
"middle",
|
|
1192
|
+
labelTextColor
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
crosshairPriceActionRegion = {
|
|
1196
|
+
x: containerX,
|
|
1197
|
+
y: containerY,
|
|
1198
|
+
width: containerWidth,
|
|
1199
|
+
height: labelHeight,
|
|
1200
|
+
price: Number(hoverPrice.toFixed(mergedOptions.priceDecimals))
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1133
1203
|
}
|
|
1134
1204
|
if (crosshair.showTimeLabel) {
|
|
1135
1205
|
const ratio = clamp((cx - chartLeft) / chartWidth, 0, 1);
|
|
@@ -1292,6 +1362,16 @@ function createChart(element, options = {}) {
|
|
|
1292
1362
|
(region) => x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height
|
|
1293
1363
|
);
|
|
1294
1364
|
};
|
|
1365
|
+
const getCrosshairPriceActionRegion = (x, y) => {
|
|
1366
|
+
if (!crosshairPriceActionRegion) {
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
const region = crosshairPriceActionRegion;
|
|
1370
|
+
if (x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height) {
|
|
1371
|
+
return region;
|
|
1372
|
+
}
|
|
1373
|
+
return null;
|
|
1374
|
+
};
|
|
1295
1375
|
const priceFromCanvasY = (y) => {
|
|
1296
1376
|
if (!drawState) {
|
|
1297
1377
|
return 0;
|
|
@@ -1357,6 +1437,15 @@ function createChart(element, options = {}) {
|
|
|
1357
1437
|
let activePointerId = null;
|
|
1358
1438
|
const onPointerDown = (event) => {
|
|
1359
1439
|
const point = getCanvasPoint(event);
|
|
1440
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1441
|
+
if (crosshairButtonRegion) {
|
|
1442
|
+
crosshairPriceActionHandler?.({
|
|
1443
|
+
x: point.x,
|
|
1444
|
+
y: point.y,
|
|
1445
|
+
price: crosshairButtonRegion.price
|
|
1446
|
+
});
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1360
1449
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1361
1450
|
if (orderRegion) {
|
|
1362
1451
|
if (orderRegion.draggable) {
|
|
@@ -1469,6 +1558,11 @@ function createChart(element, options = {}) {
|
|
|
1469
1558
|
return;
|
|
1470
1559
|
}
|
|
1471
1560
|
if (!isDragging || !dragMode) {
|
|
1561
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1562
|
+
if (crosshairButtonRegion) {
|
|
1563
|
+
canvas.style.cursor = "pointer";
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1472
1566
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1473
1567
|
if (orderRegion) {
|
|
1474
1568
|
canvas.style.cursor = orderRegion.draggable ? "ns-resize" : "pointer";
|
|
@@ -1722,6 +1816,9 @@ function createChart(element, options = {}) {
|
|
|
1722
1816
|
const onCrosshairMove = (handler) => {
|
|
1723
1817
|
crosshairMoveHandler = handler;
|
|
1724
1818
|
};
|
|
1819
|
+
const onCrosshairPriceAction = (handler) => {
|
|
1820
|
+
crosshairPriceActionHandler = handler;
|
|
1821
|
+
};
|
|
1725
1822
|
const setDoubleClickEnabled = (enabled) => {
|
|
1726
1823
|
doubleClickEnabled = enabled;
|
|
1727
1824
|
};
|
|
@@ -1751,6 +1848,7 @@ function createChart(element, options = {}) {
|
|
|
1751
1848
|
onOrderAction,
|
|
1752
1849
|
onChartClick,
|
|
1753
1850
|
onCrosshairMove,
|
|
1851
|
+
onCrosshairPriceAction,
|
|
1754
1852
|
zoomInX,
|
|
1755
1853
|
zoomOutX,
|
|
1756
1854
|
zoomInY,
|
package/dist/index.cjs
CHANGED
|
@@ -51,7 +51,14 @@ var DEFAULT_CROSSHAIR_OPTIONS = {
|
|
|
51
51
|
labelBorderRadius: 3,
|
|
52
52
|
labelBorderColor: "#94a3b8",
|
|
53
53
|
labelBorderWidth: 1,
|
|
54
|
-
labelBorderStyle: "solid"
|
|
54
|
+
labelBorderStyle: "solid",
|
|
55
|
+
showPriceActionButton: false,
|
|
56
|
+
priceActionButtonIcon: "plusThin",
|
|
57
|
+
priceActionButtonText: "+",
|
|
58
|
+
priceActionButtonSize: 16,
|
|
59
|
+
priceActionButtonGap: 4,
|
|
60
|
+
priceActionButtonRounded: true,
|
|
61
|
+
priceActionButtonBorderRadius: 8
|
|
55
62
|
};
|
|
56
63
|
var DEFAULT_WATERMARK_OPTIONS = {
|
|
57
64
|
visible: false,
|
|
@@ -163,10 +170,6 @@ var DEFAULT_OPTIONS = {
|
|
|
163
170
|
},
|
|
164
171
|
dashPatterns: DEFAULT_DASH_PATTERNS
|
|
165
172
|
};
|
|
166
|
-
var BRAND_LOGO_VIEWBOX_WIDTH = 190;
|
|
167
|
-
var BRAND_LOGO_VIEWBOX_HEIGHT = 186;
|
|
168
|
-
var BRAND_LOGO_PATH_A = "M0 93.0171V45.2271H48.9851V75.0332C48.9851 84.9545 57.0416 93.0001 66.9763 93.0001H94.9957V186H49.0531V110.984C49.0531 101.063 40.9965 93.0171 31.0619 93.0171H0Z";
|
|
169
|
-
var BRAND_LOGO_PATH_B = "M190 92.9915V140.782H141.015V110.975C141.015 101.054 132.958 93.0085 123.023 93.0085H95.0039V0H140.955V75.0162C140.955 84.9374 149.012 92.9831 158.946 92.9831H190V92.9915Z";
|
|
170
173
|
function createChart(element, options = {}) {
|
|
171
174
|
const mergedOptions = {
|
|
172
175
|
...DEFAULT_OPTIONS,
|
|
@@ -211,6 +214,8 @@ function createChart(element, options = {}) {
|
|
|
211
214
|
let orderActionHandler = null;
|
|
212
215
|
let chartClickHandler = null;
|
|
213
216
|
let crosshairMoveHandler = null;
|
|
217
|
+
let crosshairPriceActionHandler = null;
|
|
218
|
+
let crosshairPriceActionRegion = null;
|
|
214
219
|
let orderActionRegions = [];
|
|
215
220
|
let orderDragRegions = [];
|
|
216
221
|
let generatedPriceLineId = 1;
|
|
@@ -223,8 +228,6 @@ function createChart(element, options = {}) {
|
|
|
223
228
|
let yMaxOverride = null;
|
|
224
229
|
let autoYMin = null;
|
|
225
230
|
let autoYMax = null;
|
|
226
|
-
const brandLogoPathA = new Path2D(BRAND_LOGO_PATH_A);
|
|
227
|
-
const brandLogoPathB = new Path2D(BRAND_LOGO_PATH_B);
|
|
228
231
|
let watermarkImageSrc = null;
|
|
229
232
|
let watermarkImage = null;
|
|
230
233
|
let watermarkImageReady = false;
|
|
@@ -802,6 +805,7 @@ function createChart(element, options = {}) {
|
|
|
802
805
|
const draw = () => {
|
|
803
806
|
orderActionRegions = [];
|
|
804
807
|
orderDragRegions = [];
|
|
808
|
+
crosshairPriceActionRegion = null;
|
|
805
809
|
const pixelRatio = getPixelRatio();
|
|
806
810
|
canvas.style.width = `${width}px`;
|
|
807
811
|
canvas.style.height = `${height}px`;
|
|
@@ -1107,17 +1111,6 @@ function createChart(element, options = {}) {
|
|
|
1107
1111
|
});
|
|
1108
1112
|
drawText(timeLabel, x, chartBottom + 8, "center", "top", axis.textColor);
|
|
1109
1113
|
}
|
|
1110
|
-
const brandLogoWidth = 34;
|
|
1111
|
-
const brandLogoHeight = BRAND_LOGO_VIEWBOX_HEIGHT / BRAND_LOGO_VIEWBOX_WIDTH * brandLogoWidth;
|
|
1112
|
-
const brandLogoX = chartLeft + 16;
|
|
1113
|
-
const brandLogoY = chartBottom - brandLogoHeight - 10;
|
|
1114
|
-
ctx.save();
|
|
1115
|
-
ctx.translate(brandLogoX, brandLogoY);
|
|
1116
|
-
ctx.scale(brandLogoWidth / BRAND_LOGO_VIEWBOX_WIDTH, brandLogoHeight / BRAND_LOGO_VIEWBOX_HEIGHT);
|
|
1117
|
-
ctx.fillStyle = "#ffffff";
|
|
1118
|
-
ctx.fill(brandLogoPathA);
|
|
1119
|
-
ctx.fill(brandLogoPathB);
|
|
1120
|
-
ctx.restore();
|
|
1121
1114
|
if (crosshair.visible && crosshairPoint) {
|
|
1122
1115
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
1123
1116
|
const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
|
|
@@ -1154,6 +1147,83 @@ function createChart(element, options = {}) {
|
|
|
1154
1147
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|
|
1155
1148
|
strokeCrosshairLabel(priceX, priceY, priceWidth);
|
|
1156
1149
|
drawText(priceText, priceX + labelPaddingX, priceY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1150
|
+
if (crosshair.showPriceActionButton) {
|
|
1151
|
+
const buttonSize = clamp(Math.round(crosshair.priceActionButtonSize), 12, Math.max(12, labelHeight - 4));
|
|
1152
|
+
const buttonGap = Math.max(0, Math.round(crosshair.priceActionButtonGap));
|
|
1153
|
+
const containerPaddingX = 5;
|
|
1154
|
+
const containerWidth = buttonSize + containerPaddingX * 2;
|
|
1155
|
+
const containerX = priceX - buttonGap - containerWidth;
|
|
1156
|
+
const containerY = priceY;
|
|
1157
|
+
const buttonX = containerX + containerPaddingX;
|
|
1158
|
+
const buttonY = priceY + (labelHeight - buttonSize) / 2;
|
|
1159
|
+
const buttonRadius = crosshair.priceActionButtonRounded ? clamp(Math.round(crosshair.priceActionButtonBorderRadius), 0, buttonSize / 2) : 0;
|
|
1160
|
+
const buttonBorderWidth = Math.max(1, Math.round(labelBorderWidth || 1));
|
|
1161
|
+
const buttonCenterX = buttonX + buttonSize / 2;
|
|
1162
|
+
const buttonCenterY = buttonY + buttonSize / 2;
|
|
1163
|
+
const containerRadius = labelRadius;
|
|
1164
|
+
ctx.fillStyle = labelBackground;
|
|
1165
|
+
fillRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1166
|
+
if (labelBorderWidth > 0) {
|
|
1167
|
+
ctx.save();
|
|
1168
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1169
|
+
ctx.lineWidth = labelBorderWidth;
|
|
1170
|
+
ctx.setLineDash([]);
|
|
1171
|
+
strokeRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1172
|
+
ctx.restore();
|
|
1173
|
+
}
|
|
1174
|
+
if (buttonBorderWidth > 0) {
|
|
1175
|
+
ctx.save();
|
|
1176
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1177
|
+
ctx.lineWidth = buttonBorderWidth;
|
|
1178
|
+
ctx.setLineDash([]);
|
|
1179
|
+
if (crosshair.priceActionButtonRounded) {
|
|
1180
|
+
ctx.beginPath();
|
|
1181
|
+
ctx.arc(
|
|
1182
|
+
buttonCenterX,
|
|
1183
|
+
buttonCenterY,
|
|
1184
|
+
Math.max(1, buttonSize / 2 - buttonBorderWidth / 2),
|
|
1185
|
+
0,
|
|
1186
|
+
Math.PI * 2
|
|
1187
|
+
);
|
|
1188
|
+
ctx.stroke();
|
|
1189
|
+
} else {
|
|
1190
|
+
strokeRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, buttonRadius);
|
|
1191
|
+
}
|
|
1192
|
+
ctx.restore();
|
|
1193
|
+
}
|
|
1194
|
+
if (crosshair.priceActionButtonIcon !== "text" && crosshair.priceActionButtonText === "+") {
|
|
1195
|
+
const plusHalf = Math.max(3, Math.round(buttonSize * 0.22));
|
|
1196
|
+
const plusThickness = crosshair.priceActionButtonIcon === "plusThin" ? Math.max(1, Math.round(buttonSize * 0.07)) : Math.max(1, Math.round(buttonSize * 0.1));
|
|
1197
|
+
ctx.save();
|
|
1198
|
+
ctx.strokeStyle = labelTextColor;
|
|
1199
|
+
ctx.lineWidth = plusThickness;
|
|
1200
|
+
ctx.lineCap = "round";
|
|
1201
|
+
ctx.setLineDash([]);
|
|
1202
|
+
ctx.beginPath();
|
|
1203
|
+
ctx.moveTo(buttonCenterX - plusHalf, buttonCenterY);
|
|
1204
|
+
ctx.lineTo(buttonCenterX + plusHalf, buttonCenterY);
|
|
1205
|
+
ctx.moveTo(buttonCenterX, buttonCenterY - plusHalf);
|
|
1206
|
+
ctx.lineTo(buttonCenterX, buttonCenterY + plusHalf);
|
|
1207
|
+
ctx.stroke();
|
|
1208
|
+
ctx.restore();
|
|
1209
|
+
} else {
|
|
1210
|
+
drawText(
|
|
1211
|
+
crosshair.priceActionButtonText,
|
|
1212
|
+
buttonCenterX,
|
|
1213
|
+
buttonCenterY,
|
|
1214
|
+
"center",
|
|
1215
|
+
"middle",
|
|
1216
|
+
labelTextColor
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
crosshairPriceActionRegion = {
|
|
1220
|
+
x: containerX,
|
|
1221
|
+
y: containerY,
|
|
1222
|
+
width: containerWidth,
|
|
1223
|
+
height: labelHeight,
|
|
1224
|
+
price: Number(hoverPrice.toFixed(mergedOptions.priceDecimals))
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1157
1227
|
}
|
|
1158
1228
|
if (crosshair.showTimeLabel) {
|
|
1159
1229
|
const ratio = clamp((cx - chartLeft) / chartWidth, 0, 1);
|
|
@@ -1316,6 +1386,16 @@ function createChart(element, options = {}) {
|
|
|
1316
1386
|
(region) => x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height
|
|
1317
1387
|
);
|
|
1318
1388
|
};
|
|
1389
|
+
const getCrosshairPriceActionRegion = (x, y) => {
|
|
1390
|
+
if (!crosshairPriceActionRegion) {
|
|
1391
|
+
return null;
|
|
1392
|
+
}
|
|
1393
|
+
const region = crosshairPriceActionRegion;
|
|
1394
|
+
if (x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height) {
|
|
1395
|
+
return region;
|
|
1396
|
+
}
|
|
1397
|
+
return null;
|
|
1398
|
+
};
|
|
1319
1399
|
const priceFromCanvasY = (y) => {
|
|
1320
1400
|
if (!drawState) {
|
|
1321
1401
|
return 0;
|
|
@@ -1381,6 +1461,15 @@ function createChart(element, options = {}) {
|
|
|
1381
1461
|
let activePointerId = null;
|
|
1382
1462
|
const onPointerDown = (event) => {
|
|
1383
1463
|
const point = getCanvasPoint(event);
|
|
1464
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1465
|
+
if (crosshairButtonRegion) {
|
|
1466
|
+
crosshairPriceActionHandler?.({
|
|
1467
|
+
x: point.x,
|
|
1468
|
+
y: point.y,
|
|
1469
|
+
price: crosshairButtonRegion.price
|
|
1470
|
+
});
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1384
1473
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1385
1474
|
if (orderRegion) {
|
|
1386
1475
|
if (orderRegion.draggable) {
|
|
@@ -1493,6 +1582,11 @@ function createChart(element, options = {}) {
|
|
|
1493
1582
|
return;
|
|
1494
1583
|
}
|
|
1495
1584
|
if (!isDragging || !dragMode) {
|
|
1585
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1586
|
+
if (crosshairButtonRegion) {
|
|
1587
|
+
canvas.style.cursor = "pointer";
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1496
1590
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1497
1591
|
if (orderRegion) {
|
|
1498
1592
|
canvas.style.cursor = orderRegion.draggable ? "ns-resize" : "pointer";
|
|
@@ -1746,6 +1840,9 @@ function createChart(element, options = {}) {
|
|
|
1746
1840
|
const onCrosshairMove = (handler) => {
|
|
1747
1841
|
crosshairMoveHandler = handler;
|
|
1748
1842
|
};
|
|
1843
|
+
const onCrosshairPriceAction = (handler) => {
|
|
1844
|
+
crosshairPriceActionHandler = handler;
|
|
1845
|
+
};
|
|
1749
1846
|
const setDoubleClickEnabled = (enabled) => {
|
|
1750
1847
|
doubleClickEnabled = enabled;
|
|
1751
1848
|
};
|
|
@@ -1775,6 +1872,7 @@ function createChart(element, options = {}) {
|
|
|
1775
1872
|
onOrderAction,
|
|
1776
1873
|
onChartClick,
|
|
1777
1874
|
onCrosshairMove,
|
|
1875
|
+
onCrosshairPriceAction,
|
|
1778
1876
|
zoomInX,
|
|
1779
1877
|
zoomOutX,
|
|
1780
1878
|
zoomInY,
|
package/dist/index.d.cts
CHANGED
|
@@ -71,6 +71,13 @@ interface CrosshairOptions {
|
|
|
71
71
|
labelBorderColor?: string;
|
|
72
72
|
labelBorderWidth?: number;
|
|
73
73
|
labelBorderStyle?: "solid" | "dotted" | "dashed";
|
|
74
|
+
showPriceActionButton?: boolean;
|
|
75
|
+
priceActionButtonIcon?: "plus" | "plusThin" | "text";
|
|
76
|
+
priceActionButtonText?: string;
|
|
77
|
+
priceActionButtonSize?: number;
|
|
78
|
+
priceActionButtonGap?: number;
|
|
79
|
+
priceActionButtonRounded?: boolean;
|
|
80
|
+
priceActionButtonBorderRadius?: number;
|
|
74
81
|
}
|
|
75
82
|
interface WatermarkOptions {
|
|
76
83
|
visible?: boolean;
|
|
@@ -178,6 +185,11 @@ interface CrosshairMoveEvent {
|
|
|
178
185
|
time?: string;
|
|
179
186
|
point?: OhlcDataPoint;
|
|
180
187
|
}
|
|
188
|
+
interface CrosshairPriceActionEvent {
|
|
189
|
+
x: number;
|
|
190
|
+
y: number;
|
|
191
|
+
price: number;
|
|
192
|
+
}
|
|
181
193
|
interface TickerLineOptions {
|
|
182
194
|
visible?: boolean;
|
|
183
195
|
style?: "solid" | "dotted" | "dashed";
|
|
@@ -199,6 +211,7 @@ interface ChartInstance {
|
|
|
199
211
|
onOrderAction: (handler: ((event: OrderActionEvent) => void) | null) => void;
|
|
200
212
|
onChartClick: (handler: ((event: ChartClickEvent) => void) | null) => void;
|
|
201
213
|
onCrosshairMove: (handler: ((event: CrosshairMoveEvent) => void) | null) => void;
|
|
214
|
+
onCrosshairPriceAction: (handler: ((event: CrosshairPriceActionEvent) => void) | null) => void;
|
|
202
215
|
zoomInX: (factor?: number) => void;
|
|
203
216
|
zoomOutX: (factor?: number) => void;
|
|
204
217
|
zoomInY: (factor?: number) => void;
|
|
@@ -222,4 +235,4 @@ interface OhlcDataPoint {
|
|
|
222
235
|
}
|
|
223
236
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
224
237
|
|
|
225
|
-
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type DashPatternOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
238
|
+
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,13 @@ interface CrosshairOptions {
|
|
|
71
71
|
labelBorderColor?: string;
|
|
72
72
|
labelBorderWidth?: number;
|
|
73
73
|
labelBorderStyle?: "solid" | "dotted" | "dashed";
|
|
74
|
+
showPriceActionButton?: boolean;
|
|
75
|
+
priceActionButtonIcon?: "plus" | "plusThin" | "text";
|
|
76
|
+
priceActionButtonText?: string;
|
|
77
|
+
priceActionButtonSize?: number;
|
|
78
|
+
priceActionButtonGap?: number;
|
|
79
|
+
priceActionButtonRounded?: boolean;
|
|
80
|
+
priceActionButtonBorderRadius?: number;
|
|
74
81
|
}
|
|
75
82
|
interface WatermarkOptions {
|
|
76
83
|
visible?: boolean;
|
|
@@ -178,6 +185,11 @@ interface CrosshairMoveEvent {
|
|
|
178
185
|
time?: string;
|
|
179
186
|
point?: OhlcDataPoint;
|
|
180
187
|
}
|
|
188
|
+
interface CrosshairPriceActionEvent {
|
|
189
|
+
x: number;
|
|
190
|
+
y: number;
|
|
191
|
+
price: number;
|
|
192
|
+
}
|
|
181
193
|
interface TickerLineOptions {
|
|
182
194
|
visible?: boolean;
|
|
183
195
|
style?: "solid" | "dotted" | "dashed";
|
|
@@ -199,6 +211,7 @@ interface ChartInstance {
|
|
|
199
211
|
onOrderAction: (handler: ((event: OrderActionEvent) => void) | null) => void;
|
|
200
212
|
onChartClick: (handler: ((event: ChartClickEvent) => void) | null) => void;
|
|
201
213
|
onCrosshairMove: (handler: ((event: CrosshairMoveEvent) => void) | null) => void;
|
|
214
|
+
onCrosshairPriceAction: (handler: ((event: CrosshairPriceActionEvent) => void) | null) => void;
|
|
202
215
|
zoomInX: (factor?: number) => void;
|
|
203
216
|
zoomOutX: (factor?: number) => void;
|
|
204
217
|
zoomInY: (factor?: number) => void;
|
|
@@ -222,4 +235,4 @@ interface OhlcDataPoint {
|
|
|
222
235
|
}
|
|
223
236
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
224
237
|
|
|
225
|
-
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type DashPatternOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
238
|
+
export { type AxisOptions, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,14 @@ var DEFAULT_CROSSHAIR_OPTIONS = {
|
|
|
27
27
|
labelBorderRadius: 3,
|
|
28
28
|
labelBorderColor: "#94a3b8",
|
|
29
29
|
labelBorderWidth: 1,
|
|
30
|
-
labelBorderStyle: "solid"
|
|
30
|
+
labelBorderStyle: "solid",
|
|
31
|
+
showPriceActionButton: false,
|
|
32
|
+
priceActionButtonIcon: "plusThin",
|
|
33
|
+
priceActionButtonText: "+",
|
|
34
|
+
priceActionButtonSize: 16,
|
|
35
|
+
priceActionButtonGap: 4,
|
|
36
|
+
priceActionButtonRounded: true,
|
|
37
|
+
priceActionButtonBorderRadius: 8
|
|
31
38
|
};
|
|
32
39
|
var DEFAULT_WATERMARK_OPTIONS = {
|
|
33
40
|
visible: false,
|
|
@@ -139,10 +146,6 @@ var DEFAULT_OPTIONS = {
|
|
|
139
146
|
},
|
|
140
147
|
dashPatterns: DEFAULT_DASH_PATTERNS
|
|
141
148
|
};
|
|
142
|
-
var BRAND_LOGO_VIEWBOX_WIDTH = 190;
|
|
143
|
-
var BRAND_LOGO_VIEWBOX_HEIGHT = 186;
|
|
144
|
-
var BRAND_LOGO_PATH_A = "M0 93.0171V45.2271H48.9851V75.0332C48.9851 84.9545 57.0416 93.0001 66.9763 93.0001H94.9957V186H49.0531V110.984C49.0531 101.063 40.9965 93.0171 31.0619 93.0171H0Z";
|
|
145
|
-
var BRAND_LOGO_PATH_B = "M190 92.9915V140.782H141.015V110.975C141.015 101.054 132.958 93.0085 123.023 93.0085H95.0039V0H140.955V75.0162C140.955 84.9374 149.012 92.9831 158.946 92.9831H190V92.9915Z";
|
|
146
149
|
function createChart(element, options = {}) {
|
|
147
150
|
const mergedOptions = {
|
|
148
151
|
...DEFAULT_OPTIONS,
|
|
@@ -187,6 +190,8 @@ function createChart(element, options = {}) {
|
|
|
187
190
|
let orderActionHandler = null;
|
|
188
191
|
let chartClickHandler = null;
|
|
189
192
|
let crosshairMoveHandler = null;
|
|
193
|
+
let crosshairPriceActionHandler = null;
|
|
194
|
+
let crosshairPriceActionRegion = null;
|
|
190
195
|
let orderActionRegions = [];
|
|
191
196
|
let orderDragRegions = [];
|
|
192
197
|
let generatedPriceLineId = 1;
|
|
@@ -199,8 +204,6 @@ function createChart(element, options = {}) {
|
|
|
199
204
|
let yMaxOverride = null;
|
|
200
205
|
let autoYMin = null;
|
|
201
206
|
let autoYMax = null;
|
|
202
|
-
const brandLogoPathA = new Path2D(BRAND_LOGO_PATH_A);
|
|
203
|
-
const brandLogoPathB = new Path2D(BRAND_LOGO_PATH_B);
|
|
204
207
|
let watermarkImageSrc = null;
|
|
205
208
|
let watermarkImage = null;
|
|
206
209
|
let watermarkImageReady = false;
|
|
@@ -778,6 +781,7 @@ function createChart(element, options = {}) {
|
|
|
778
781
|
const draw = () => {
|
|
779
782
|
orderActionRegions = [];
|
|
780
783
|
orderDragRegions = [];
|
|
784
|
+
crosshairPriceActionRegion = null;
|
|
781
785
|
const pixelRatio = getPixelRatio();
|
|
782
786
|
canvas.style.width = `${width}px`;
|
|
783
787
|
canvas.style.height = `${height}px`;
|
|
@@ -1083,17 +1087,6 @@ function createChart(element, options = {}) {
|
|
|
1083
1087
|
});
|
|
1084
1088
|
drawText(timeLabel, x, chartBottom + 8, "center", "top", axis.textColor);
|
|
1085
1089
|
}
|
|
1086
|
-
const brandLogoWidth = 34;
|
|
1087
|
-
const brandLogoHeight = BRAND_LOGO_VIEWBOX_HEIGHT / BRAND_LOGO_VIEWBOX_WIDTH * brandLogoWidth;
|
|
1088
|
-
const brandLogoX = chartLeft + 16;
|
|
1089
|
-
const brandLogoY = chartBottom - brandLogoHeight - 10;
|
|
1090
|
-
ctx.save();
|
|
1091
|
-
ctx.translate(brandLogoX, brandLogoY);
|
|
1092
|
-
ctx.scale(brandLogoWidth / BRAND_LOGO_VIEWBOX_WIDTH, brandLogoHeight / BRAND_LOGO_VIEWBOX_HEIGHT);
|
|
1093
|
-
ctx.fillStyle = "#ffffff";
|
|
1094
|
-
ctx.fill(brandLogoPathA);
|
|
1095
|
-
ctx.fill(brandLogoPathB);
|
|
1096
|
-
ctx.restore();
|
|
1097
1090
|
if (crosshair.visible && crosshairPoint) {
|
|
1098
1091
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
1099
1092
|
const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
|
|
@@ -1130,6 +1123,83 @@ function createChart(element, options = {}) {
|
|
|
1130
1123
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|
|
1131
1124
|
strokeCrosshairLabel(priceX, priceY, priceWidth);
|
|
1132
1125
|
drawText(priceText, priceX + labelPaddingX, priceY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1126
|
+
if (crosshair.showPriceActionButton) {
|
|
1127
|
+
const buttonSize = clamp(Math.round(crosshair.priceActionButtonSize), 12, Math.max(12, labelHeight - 4));
|
|
1128
|
+
const buttonGap = Math.max(0, Math.round(crosshair.priceActionButtonGap));
|
|
1129
|
+
const containerPaddingX = 5;
|
|
1130
|
+
const containerWidth = buttonSize + containerPaddingX * 2;
|
|
1131
|
+
const containerX = priceX - buttonGap - containerWidth;
|
|
1132
|
+
const containerY = priceY;
|
|
1133
|
+
const buttonX = containerX + containerPaddingX;
|
|
1134
|
+
const buttonY = priceY + (labelHeight - buttonSize) / 2;
|
|
1135
|
+
const buttonRadius = crosshair.priceActionButtonRounded ? clamp(Math.round(crosshair.priceActionButtonBorderRadius), 0, buttonSize / 2) : 0;
|
|
1136
|
+
const buttonBorderWidth = Math.max(1, Math.round(labelBorderWidth || 1));
|
|
1137
|
+
const buttonCenterX = buttonX + buttonSize / 2;
|
|
1138
|
+
const buttonCenterY = buttonY + buttonSize / 2;
|
|
1139
|
+
const containerRadius = labelRadius;
|
|
1140
|
+
ctx.fillStyle = labelBackground;
|
|
1141
|
+
fillRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1142
|
+
if (labelBorderWidth > 0) {
|
|
1143
|
+
ctx.save();
|
|
1144
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1145
|
+
ctx.lineWidth = labelBorderWidth;
|
|
1146
|
+
ctx.setLineDash([]);
|
|
1147
|
+
strokeRoundedRect(Math.round(containerX), Math.round(containerY), containerWidth, labelHeight, containerRadius);
|
|
1148
|
+
ctx.restore();
|
|
1149
|
+
}
|
|
1150
|
+
if (buttonBorderWidth > 0) {
|
|
1151
|
+
ctx.save();
|
|
1152
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1153
|
+
ctx.lineWidth = buttonBorderWidth;
|
|
1154
|
+
ctx.setLineDash([]);
|
|
1155
|
+
if (crosshair.priceActionButtonRounded) {
|
|
1156
|
+
ctx.beginPath();
|
|
1157
|
+
ctx.arc(
|
|
1158
|
+
buttonCenterX,
|
|
1159
|
+
buttonCenterY,
|
|
1160
|
+
Math.max(1, buttonSize / 2 - buttonBorderWidth / 2),
|
|
1161
|
+
0,
|
|
1162
|
+
Math.PI * 2
|
|
1163
|
+
);
|
|
1164
|
+
ctx.stroke();
|
|
1165
|
+
} else {
|
|
1166
|
+
strokeRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, buttonRadius);
|
|
1167
|
+
}
|
|
1168
|
+
ctx.restore();
|
|
1169
|
+
}
|
|
1170
|
+
if (crosshair.priceActionButtonIcon !== "text" && crosshair.priceActionButtonText === "+") {
|
|
1171
|
+
const plusHalf = Math.max(3, Math.round(buttonSize * 0.22));
|
|
1172
|
+
const plusThickness = crosshair.priceActionButtonIcon === "plusThin" ? Math.max(1, Math.round(buttonSize * 0.07)) : Math.max(1, Math.round(buttonSize * 0.1));
|
|
1173
|
+
ctx.save();
|
|
1174
|
+
ctx.strokeStyle = labelTextColor;
|
|
1175
|
+
ctx.lineWidth = plusThickness;
|
|
1176
|
+
ctx.lineCap = "round";
|
|
1177
|
+
ctx.setLineDash([]);
|
|
1178
|
+
ctx.beginPath();
|
|
1179
|
+
ctx.moveTo(buttonCenterX - plusHalf, buttonCenterY);
|
|
1180
|
+
ctx.lineTo(buttonCenterX + plusHalf, buttonCenterY);
|
|
1181
|
+
ctx.moveTo(buttonCenterX, buttonCenterY - plusHalf);
|
|
1182
|
+
ctx.lineTo(buttonCenterX, buttonCenterY + plusHalf);
|
|
1183
|
+
ctx.stroke();
|
|
1184
|
+
ctx.restore();
|
|
1185
|
+
} else {
|
|
1186
|
+
drawText(
|
|
1187
|
+
crosshair.priceActionButtonText,
|
|
1188
|
+
buttonCenterX,
|
|
1189
|
+
buttonCenterY,
|
|
1190
|
+
"center",
|
|
1191
|
+
"middle",
|
|
1192
|
+
labelTextColor
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
crosshairPriceActionRegion = {
|
|
1196
|
+
x: containerX,
|
|
1197
|
+
y: containerY,
|
|
1198
|
+
width: containerWidth,
|
|
1199
|
+
height: labelHeight,
|
|
1200
|
+
price: Number(hoverPrice.toFixed(mergedOptions.priceDecimals))
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1133
1203
|
}
|
|
1134
1204
|
if (crosshair.showTimeLabel) {
|
|
1135
1205
|
const ratio = clamp((cx - chartLeft) / chartWidth, 0, 1);
|
|
@@ -1292,6 +1362,16 @@ function createChart(element, options = {}) {
|
|
|
1292
1362
|
(region) => x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height
|
|
1293
1363
|
);
|
|
1294
1364
|
};
|
|
1365
|
+
const getCrosshairPriceActionRegion = (x, y) => {
|
|
1366
|
+
if (!crosshairPriceActionRegion) {
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
const region = crosshairPriceActionRegion;
|
|
1370
|
+
if (x >= region.x && x <= region.x + region.width && y >= region.y && y <= region.y + region.height) {
|
|
1371
|
+
return region;
|
|
1372
|
+
}
|
|
1373
|
+
return null;
|
|
1374
|
+
};
|
|
1295
1375
|
const priceFromCanvasY = (y) => {
|
|
1296
1376
|
if (!drawState) {
|
|
1297
1377
|
return 0;
|
|
@@ -1357,6 +1437,15 @@ function createChart(element, options = {}) {
|
|
|
1357
1437
|
let activePointerId = null;
|
|
1358
1438
|
const onPointerDown = (event) => {
|
|
1359
1439
|
const point = getCanvasPoint(event);
|
|
1440
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1441
|
+
if (crosshairButtonRegion) {
|
|
1442
|
+
crosshairPriceActionHandler?.({
|
|
1443
|
+
x: point.x,
|
|
1444
|
+
y: point.y,
|
|
1445
|
+
price: crosshairButtonRegion.price
|
|
1446
|
+
});
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1360
1449
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1361
1450
|
if (orderRegion) {
|
|
1362
1451
|
if (orderRegion.draggable) {
|
|
@@ -1469,6 +1558,11 @@ function createChart(element, options = {}) {
|
|
|
1469
1558
|
return;
|
|
1470
1559
|
}
|
|
1471
1560
|
if (!isDragging || !dragMode) {
|
|
1561
|
+
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
1562
|
+
if (crosshairButtonRegion) {
|
|
1563
|
+
canvas.style.cursor = "pointer";
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1472
1566
|
const orderRegion = getOrderActionRegion(point.x, point.y);
|
|
1473
1567
|
if (orderRegion) {
|
|
1474
1568
|
canvas.style.cursor = orderRegion.draggable ? "ns-resize" : "pointer";
|
|
@@ -1722,6 +1816,9 @@ function createChart(element, options = {}) {
|
|
|
1722
1816
|
const onCrosshairMove = (handler) => {
|
|
1723
1817
|
crosshairMoveHandler = handler;
|
|
1724
1818
|
};
|
|
1819
|
+
const onCrosshairPriceAction = (handler) => {
|
|
1820
|
+
crosshairPriceActionHandler = handler;
|
|
1821
|
+
};
|
|
1725
1822
|
const setDoubleClickEnabled = (enabled) => {
|
|
1726
1823
|
doubleClickEnabled = enabled;
|
|
1727
1824
|
};
|
|
@@ -1751,6 +1848,7 @@ function createChart(element, options = {}) {
|
|
|
1751
1848
|
onOrderAction,
|
|
1752
1849
|
onChartClick,
|
|
1753
1850
|
onCrosshairMove,
|
|
1851
|
+
onCrosshairPriceAction,
|
|
1754
1852
|
zoomInX,
|
|
1755
1853
|
zoomOutX,
|
|
1756
1854
|
zoomInY,
|
package/docs/API.md
CHANGED
|
@@ -94,6 +94,15 @@ Top-level options:
|
|
|
94
94
|
- `labelBorderColor` (default `#94a3b8`)
|
|
95
95
|
- `labelBorderWidth` (default `1`)
|
|
96
96
|
- `labelBorderStyle` (`"solid" | "dotted" | "dashed"`, default `"solid"`)
|
|
97
|
+
- `showPriceActionButton` (default `false`)
|
|
98
|
+
- `priceActionButtonIcon` (`"plus" | "plusThin" | "text"`, default `"plusThin"`)
|
|
99
|
+
- `priceActionButtonText` (default `"+"`)
|
|
100
|
+
- `priceActionButtonSize` (default `16`)
|
|
101
|
+
- `priceActionButtonGap` (default `4`)
|
|
102
|
+
- `priceActionButtonRounded` (default `true`; set `false` for square corners)
|
|
103
|
+
- `priceActionButtonBorderRadius` (default `8`)
|
|
104
|
+
|
|
105
|
+
Note: the button and its container automatically inherit crosshair label colors/border for a consistent `[button-box][label]` look.
|
|
97
106
|
|
|
98
107
|
### `WatermarkOptions`
|
|
99
108
|
|
|
@@ -243,6 +252,7 @@ Connector/fill visuals:
|
|
|
243
252
|
- `onOrderAction(handler: ((event: OrderActionEvent) => void) | null): void`
|
|
244
253
|
- `onChartClick(handler: ((event: ChartClickEvent) => void) | null): void`
|
|
245
254
|
- `onCrosshairMove(handler: ((event: CrosshairMoveEvent) => void) | null): void`
|
|
255
|
+
- `onCrosshairPriceAction(handler: ((event: CrosshairPriceActionEvent) => void) | null): void`
|
|
246
256
|
- `zoomInX(factor?: number): void` (default factor `1.25`)
|
|
247
257
|
- `zoomOutX(factor?: number): void` (default factor `1.25`)
|
|
248
258
|
- `zoomInY(factor?: number): void` (default factor `1.25`)
|
package/docs/EVENTS.md
CHANGED
|
@@ -109,6 +109,33 @@ type CrosshairMoveEvent = {
|
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
112
|
+
## `onCrosshairPriceAction`
|
|
113
|
+
|
|
114
|
+
Register:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
chart.onCrosshairPriceAction((event) => {
|
|
118
|
+
// frontend action (open order ticket, quick action, etc)
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Payload type:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
type CrosshairPriceActionEvent = {
|
|
126
|
+
x: number;
|
|
127
|
+
y: number;
|
|
128
|
+
price: number;
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Notes
|
|
133
|
+
|
|
134
|
+
- Fires when the optional crosshair price action button (default text `"+"`) is clicked.
|
|
135
|
+
- Enable button rendering via `crosshair.showPriceActionButton: true`.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
112
139
|
## Double-click integration
|
|
113
140
|
|
|
114
141
|
Set behavior:
|
package/docs/RECIPES.md
CHANGED
|
@@ -63,6 +63,34 @@ const chart = createChart(root, {
|
|
|
63
63
|
});
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
## Add crosshair "+" action button
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const chart = createChart(root, {
|
|
70
|
+
crosshair: {
|
|
71
|
+
showPriceActionButton: true,
|
|
72
|
+
priceActionButtonText: "+",
|
|
73
|
+
priceActionButtonGap: 4
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
chart.onCrosshairPriceAction((event) => {
|
|
78
|
+
// Handle in app/frontend (place order modal, quick ticket, etc.)
|
|
79
|
+
console.log("crosshair + clicked at price", event.price);
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Square style example:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const chart = createChart(root, {
|
|
87
|
+
crosshair: {
|
|
88
|
+
showPriceActionButton: true,
|
|
89
|
+
priceActionButtonRounded: false
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
66
94
|
## Add a draggable pending limit line
|
|
67
95
|
|
|
68
96
|
```ts
|