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
package/dist/index.d.ts
CHANGED
|
@@ -49,9 +49,13 @@ interface CrosshairOptions {
|
|
|
49
49
|
showVertical?: boolean;
|
|
50
50
|
showPriceLabel?: boolean;
|
|
51
51
|
showTimeLabel?: boolean;
|
|
52
|
+
timeLabelFormat?: "auto" | "date" | "time" | "datetime";
|
|
52
53
|
labelBackgroundColor?: string;
|
|
53
54
|
labelTextColor?: string;
|
|
54
55
|
labelBorderRadius?: number;
|
|
56
|
+
labelBorderColor?: string;
|
|
57
|
+
labelBorderWidth?: number;
|
|
58
|
+
labelBorderStyle?: "solid" | "dotted" | "dashed";
|
|
55
59
|
}
|
|
56
60
|
interface WatermarkOptions {
|
|
57
61
|
visible?: boolean;
|
|
@@ -148,6 +152,15 @@ interface ChartClickEvent {
|
|
|
148
152
|
price?: number;
|
|
149
153
|
region: "plot" | "x-axis" | "y-axis";
|
|
150
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
|
+
}
|
|
151
164
|
interface TickerLineOptions {
|
|
152
165
|
visible?: boolean;
|
|
153
166
|
style?: "solid" | "dotted" | "dashed";
|
|
@@ -168,6 +181,7 @@ interface ChartInstance {
|
|
|
168
181
|
removeOrderLine: (id: string) => void;
|
|
169
182
|
onOrderAction: (handler: ((event: OrderActionEvent) => void) | null) => void;
|
|
170
183
|
onChartClick: (handler: ((event: ChartClickEvent) => void) | null) => void;
|
|
184
|
+
onCrosshairMove: (handler: ((event: CrosshairMoveEvent) => void) | null) => void;
|
|
171
185
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
172
186
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
173
187
|
resize: (width?: number, height?: number) => void;
|
|
@@ -183,4 +197,4 @@ interface OhlcDataPoint {
|
|
|
183
197
|
}
|
|
184
198
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
185
199
|
|
|
186
|
-
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 };
|
package/dist/index.js
CHANGED
|
@@ -21,9 +21,13 @@ var DEFAULT_CROSSHAIR_OPTIONS = {
|
|
|
21
21
|
showVertical: true,
|
|
22
22
|
showPriceLabel: true,
|
|
23
23
|
showTimeLabel: true,
|
|
24
|
+
timeLabelFormat: "auto",
|
|
24
25
|
labelBackgroundColor: "#0b1220",
|
|
25
26
|
labelTextColor: "#cbd5e1",
|
|
26
|
-
labelBorderRadius: 3
|
|
27
|
+
labelBorderRadius: 3,
|
|
28
|
+
labelBorderColor: "#94a3b8",
|
|
29
|
+
labelBorderWidth: 1,
|
|
30
|
+
labelBorderStyle: "solid"
|
|
27
31
|
};
|
|
28
32
|
var DEFAULT_WATERMARK_OPTIONS = {
|
|
29
33
|
visible: false,
|
|
@@ -157,6 +161,7 @@ function createChart(element, options = {}) {
|
|
|
157
161
|
}));
|
|
158
162
|
let orderActionHandler = null;
|
|
159
163
|
let chartClickHandler = null;
|
|
164
|
+
let crosshairMoveHandler = null;
|
|
160
165
|
let orderActionRegions = [];
|
|
161
166
|
let orderDragRegions = [];
|
|
162
167
|
let generatedPriceLineId = 1;
|
|
@@ -344,6 +349,42 @@ function createChart(element, options = {}) {
|
|
|
344
349
|
const extra = index - (data.length - 1);
|
|
345
350
|
return new Date(last.time.getTime() + extra * stepMs);
|
|
346
351
|
};
|
|
352
|
+
const formatHoverTimeLabel = (time, mode) => {
|
|
353
|
+
if (mode === "time") {
|
|
354
|
+
return time.toLocaleTimeString(void 0, {
|
|
355
|
+
hour: "2-digit",
|
|
356
|
+
minute: "2-digit",
|
|
357
|
+
hour12: false
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
if (mode === "datetime") {
|
|
361
|
+
return time.toLocaleString(void 0, {
|
|
362
|
+
month: "short",
|
|
363
|
+
day: "numeric",
|
|
364
|
+
hour: "2-digit",
|
|
365
|
+
minute: "2-digit",
|
|
366
|
+
hour12: false
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
if (mode === "date") {
|
|
370
|
+
return time.toLocaleDateString(void 0, {
|
|
371
|
+
month: "short",
|
|
372
|
+
day: "numeric"
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const stepMs = getTimeStepMs();
|
|
376
|
+
if (stepMs < 24 * 60 * 60 * 1e3) {
|
|
377
|
+
return time.toLocaleTimeString(void 0, {
|
|
378
|
+
hour: "2-digit",
|
|
379
|
+
minute: "2-digit",
|
|
380
|
+
hour12: false
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
return time.toLocaleDateString(void 0, {
|
|
384
|
+
month: "short",
|
|
385
|
+
day: "numeric"
|
|
386
|
+
});
|
|
387
|
+
};
|
|
347
388
|
const drawText = (text, x, y, align = "left", baseline = "alphabetic", color = mergedOptions.axis?.textColor ?? mergedOptions.axisColor) => {
|
|
348
389
|
ctx.fillStyle = color;
|
|
349
390
|
ctx.textAlign = align;
|
|
@@ -998,6 +1039,26 @@ function createChart(element, options = {}) {
|
|
|
998
1039
|
const labelRadius = Math.max(0, crosshair.labelBorderRadius);
|
|
999
1040
|
const labelBackground = crosshair.labelBackgroundColor;
|
|
1000
1041
|
const labelTextColor = crosshair.labelTextColor;
|
|
1042
|
+
const labelBorderColor = crosshair.labelBorderColor;
|
|
1043
|
+
const labelBorderWidth = Math.max(0, crosshair.labelBorderWidth);
|
|
1044
|
+
const labelBorderStyle = crosshair.labelBorderStyle;
|
|
1045
|
+
const strokeCrosshairLabel = (x, y, widthValue) => {
|
|
1046
|
+
if (labelBorderWidth <= 0) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
ctx.save();
|
|
1050
|
+
ctx.strokeStyle = labelBorderColor;
|
|
1051
|
+
ctx.lineWidth = labelBorderWidth;
|
|
1052
|
+
if (labelBorderStyle === "dotted") {
|
|
1053
|
+
ctx.setLineDash([2, 3]);
|
|
1054
|
+
} else if (labelBorderStyle === "dashed") {
|
|
1055
|
+
ctx.setLineDash([6, 4]);
|
|
1056
|
+
} else {
|
|
1057
|
+
ctx.setLineDash([]);
|
|
1058
|
+
}
|
|
1059
|
+
strokeRoundedRect(Math.round(x), Math.round(y), widthValue, labelHeight, labelRadius);
|
|
1060
|
+
ctx.restore();
|
|
1061
|
+
};
|
|
1001
1062
|
if (crosshair.showPriceLabel) {
|
|
1002
1063
|
const hoverPrice = yMin + (chartBottom - cy) / chartHeight * yRange;
|
|
1003
1064
|
const priceText = formatPrice(hoverPrice);
|
|
@@ -1006,6 +1067,7 @@ function createChart(element, options = {}) {
|
|
|
1006
1067
|
const priceY = clamp(cy - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
1007
1068
|
ctx.fillStyle = labelBackground;
|
|
1008
1069
|
fillRoundedRect(Math.round(priceX), Math.round(priceY), priceWidth, labelHeight, labelRadius);
|
|
1070
|
+
strokeCrosshairLabel(priceX, priceY, priceWidth);
|
|
1009
1071
|
drawText(priceText, priceX + labelPaddingX, priceY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1010
1072
|
}
|
|
1011
1073
|
if (crosshair.showTimeLabel) {
|
|
@@ -1013,15 +1075,13 @@ function createChart(element, options = {}) {
|
|
|
1013
1075
|
const hoverIndex = Math.round(xStart + ratio * xSpan - 0.5);
|
|
1014
1076
|
const hoverTime = getTimeForIndex(hoverIndex);
|
|
1015
1077
|
if (hoverTime) {
|
|
1016
|
-
const timeText = hoverTime
|
|
1017
|
-
month: "short",
|
|
1018
|
-
day: "numeric"
|
|
1019
|
-
});
|
|
1078
|
+
const timeText = formatHoverTimeLabel(hoverTime, crosshair.timeLabelFormat);
|
|
1020
1079
|
const timeWidth = Math.ceil(ctx.measureText(timeText).width) + labelPaddingX * 2;
|
|
1021
1080
|
const timeX = clamp(cx - timeWidth / 2, chartLeft, chartRight - timeWidth);
|
|
1022
1081
|
const timeY = chartBottom + 8;
|
|
1023
1082
|
ctx.fillStyle = labelBackground;
|
|
1024
1083
|
fillRoundedRect(Math.round(timeX), Math.round(timeY), timeWidth, labelHeight, labelRadius);
|
|
1084
|
+
strokeCrosshairLabel(timeX, timeY, timeWidth);
|
|
1025
1085
|
drawText(timeText, timeX + labelPaddingX, timeY + labelHeight / 2, "left", "middle", labelTextColor);
|
|
1026
1086
|
}
|
|
1027
1087
|
}
|
|
@@ -1120,6 +1180,39 @@ function createChart(element, options = {}) {
|
|
|
1120
1180
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
1121
1181
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
1122
1182
|
};
|
|
1183
|
+
const indexFromCanvasX = (x) => {
|
|
1184
|
+
if (!drawState) {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
1188
|
+
return Math.round(drawState.xStart + ratio * drawState.xSpan - 0.5);
|
|
1189
|
+
};
|
|
1190
|
+
const emitCrosshairMove = (x, y, region) => {
|
|
1191
|
+
if (!crosshairMoveHandler) {
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
const index = indexFromCanvasX(x);
|
|
1195
|
+
const hoverTime = index === null ? null : getTimeForIndex(index);
|
|
1196
|
+
const parsedPoint = index === null ? void 0 : data[index];
|
|
1197
|
+
const point = parsedPoint === void 0 ? void 0 : {
|
|
1198
|
+
t: parsedPoint.time.toISOString(),
|
|
1199
|
+
o: parsedPoint.o,
|
|
1200
|
+
h: parsedPoint.h,
|
|
1201
|
+
l: parsedPoint.l,
|
|
1202
|
+
c: parsedPoint.c,
|
|
1203
|
+
...parsedPoint.v === void 0 ? {} : { v: parsedPoint.v }
|
|
1204
|
+
};
|
|
1205
|
+
const payload = {
|
|
1206
|
+
x,
|
|
1207
|
+
y,
|
|
1208
|
+
region,
|
|
1209
|
+
...region === "plot" ? { price: Number(priceFromCanvasY(y).toFixed(mergedOptions.priceDecimals)) } : {},
|
|
1210
|
+
...index === null ? {} : { index },
|
|
1211
|
+
...hoverTime ? { time: hoverTime.toISOString() } : {},
|
|
1212
|
+
...point ? { point } : {}
|
|
1213
|
+
};
|
|
1214
|
+
crosshairMoveHandler(payload);
|
|
1215
|
+
};
|
|
1123
1216
|
const getHitRegion = (x, y) => {
|
|
1124
1217
|
if (!drawState) {
|
|
1125
1218
|
return "outside";
|
|
@@ -1273,12 +1366,15 @@ function createChart(element, options = {}) {
|
|
|
1273
1366
|
if (hoverRegion === "plot") {
|
|
1274
1367
|
canvas.style.cursor = "default";
|
|
1275
1368
|
setCrosshairPoint(point);
|
|
1369
|
+
emitCrosshairMove(point.x, point.y, "plot");
|
|
1276
1370
|
} else if (hoverRegion === "x-axis") {
|
|
1277
1371
|
canvas.style.cursor = "ew-resize";
|
|
1278
1372
|
setCrosshairPoint(null);
|
|
1373
|
+
emitCrosshairMove(point.x, point.y, "x-axis");
|
|
1279
1374
|
} else if (hoverRegion === "y-axis") {
|
|
1280
1375
|
canvas.style.cursor = "ns-resize";
|
|
1281
1376
|
setCrosshairPoint(null);
|
|
1377
|
+
emitCrosshairMove(point.x, point.y, "y-axis");
|
|
1282
1378
|
} else {
|
|
1283
1379
|
canvas.style.cursor = "default";
|
|
1284
1380
|
setCrosshairPoint(null);
|
|
@@ -1288,7 +1384,7 @@ function createChart(element, options = {}) {
|
|
|
1288
1384
|
const deltaX = point.x - lastPointerX;
|
|
1289
1385
|
const deltaY = point.y - lastPointerY;
|
|
1290
1386
|
if (dragMode === "plot") {
|
|
1291
|
-
canvas.style.cursor = "
|
|
1387
|
+
canvas.style.cursor = "default";
|
|
1292
1388
|
pan(deltaX, deltaY, true, true);
|
|
1293
1389
|
setCrosshairPoint(null);
|
|
1294
1390
|
} else if (dragMode === "x-axis") {
|
|
@@ -1518,6 +1614,9 @@ function createChart(element, options = {}) {
|
|
|
1518
1614
|
const onChartClick = (handler) => {
|
|
1519
1615
|
chartClickHandler = handler;
|
|
1520
1616
|
};
|
|
1617
|
+
const onCrosshairMove = (handler) => {
|
|
1618
|
+
crosshairMoveHandler = handler;
|
|
1619
|
+
};
|
|
1521
1620
|
const setDoubleClickEnabled = (enabled) => {
|
|
1522
1621
|
doubleClickEnabled = enabled;
|
|
1523
1622
|
};
|
|
@@ -1546,6 +1645,7 @@ function createChart(element, options = {}) {
|
|
|
1546
1645
|
removeOrderLine,
|
|
1547
1646
|
onOrderAction,
|
|
1548
1647
|
onChartClick,
|
|
1648
|
+
onCrosshairMove,
|
|
1549
1649
|
setDoubleClickEnabled,
|
|
1550
1650
|
setDoubleClickAction,
|
|
1551
1651
|
resize,
|
package/docs/API.md
CHANGED
|
@@ -80,9 +80,13 @@ Top-level options:
|
|
|
80
80
|
- `showVertical` (default `true`)
|
|
81
81
|
- `showPriceLabel` (default `true`)
|
|
82
82
|
- `showTimeLabel` (default `true`)
|
|
83
|
+
- `timeLabelFormat` (`"auto" | "date" | "time" | "datetime"`, default `"auto"`)
|
|
83
84
|
- `labelBackgroundColor` (default `#0b1220`)
|
|
84
85
|
- `labelTextColor` (default `#cbd5e1`)
|
|
85
86
|
- `labelBorderRadius` (default `3`)
|
|
87
|
+
- `labelBorderColor` (default `#94a3b8`)
|
|
88
|
+
- `labelBorderWidth` (default `1`)
|
|
89
|
+
- `labelBorderStyle` (`"solid" | "dotted" | "dashed"`, default `"solid"`)
|
|
86
90
|
|
|
87
91
|
### `WatermarkOptions`
|
|
88
92
|
|
|
@@ -208,6 +212,7 @@ Connector/fill visuals:
|
|
|
208
212
|
- `removeOrderLine(id: string): void`
|
|
209
213
|
- `onOrderAction(handler: ((event: OrderActionEvent) => void) | null): void`
|
|
210
214
|
- `onChartClick(handler: ((event: ChartClickEvent) => void) | null): void`
|
|
215
|
+
- `onCrosshairMove(handler: ((event: CrosshairMoveEvent) => void) | null): void`
|
|
211
216
|
- `setDoubleClickEnabled(enabled: boolean): void`
|
|
212
217
|
- `setDoubleClickAction(action: "reset" | "placeLimitOrder"): void`
|
|
213
218
|
- `resize(width?: number, height?: number): void`
|
package/docs/EVENTS.md
CHANGED
|
@@ -76,6 +76,39 @@ type ChartClickEvent = {
|
|
|
76
76
|
|
|
77
77
|
---
|
|
78
78
|
|
|
79
|
+
## `onCrosshairMove`
|
|
80
|
+
|
|
81
|
+
Register:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
chart.onCrosshairMove((event) => {
|
|
85
|
+
// update OHLC header, hover widgets, etc
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Payload type:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
type CrosshairMoveEvent = {
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
region: "plot" | "x-axis" | "y-axis";
|
|
96
|
+
price?: number;
|
|
97
|
+
index?: number;
|
|
98
|
+
time?: string;
|
|
99
|
+
point?: OhlcDataPoint;
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Notes
|
|
104
|
+
|
|
105
|
+
- Fires while hovering in chart regions.
|
|
106
|
+
- For plot hover, `price` is included.
|
|
107
|
+
- `point` is included when hover maps to an existing candle index.
|
|
108
|
+
- Use this to implement TradingView-style dynamic OHLC readout in your app header.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
79
112
|
## Double-click integration
|
|
80
113
|
|
|
81
114
|
Set behavior:
|