hyperprop-charting-library 0.1.46 → 0.1.48
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 +4 -3
- package/dist/hyperprop-charting-library.cjs +39 -20
- package/dist/hyperprop-charting-library.d.ts +1 -0
- package/dist/hyperprop-charting-library.js +39 -20
- package/dist/index.cjs +39 -20
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +39 -20
- package/docs/API.md +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,13 +65,14 @@ const chart = createChart(root, {
|
|
|
65
65
|
```ts
|
|
66
66
|
const chart = createChart(root, {
|
|
67
67
|
tickerLine: {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// showCountdownInLabel: true
|
|
68
|
+
labelSubtexts: ["MNQ", "RTH"],
|
|
69
|
+
showCountdownInLabel: true
|
|
71
70
|
}
|
|
72
71
|
});
|
|
73
72
|
```
|
|
74
73
|
|
|
74
|
+
The price label grows to fit all extra lines. `labelSubtext: "MNQ"` still works for the simple one-line case.
|
|
75
|
+
|
|
75
76
|
## Candle Color Behavior
|
|
76
77
|
|
|
77
78
|
You can control how up/down candle color is decided:
|
|
@@ -202,6 +202,7 @@ var DEFAULT_OPTIONS = {
|
|
|
202
202
|
thickness: 1,
|
|
203
203
|
labelTextColor: "#0b1220",
|
|
204
204
|
labelSubtext: "",
|
|
205
|
+
labelSubtexts: [],
|
|
205
206
|
labelSubtextColor: "#0b1220",
|
|
206
207
|
labelSubtextFontSize: 0,
|
|
207
208
|
showCountdownInLabel: false,
|
|
@@ -2141,10 +2142,14 @@ function createChart(element, options = {}) {
|
|
|
2141
2142
|
ctx.restore();
|
|
2142
2143
|
}
|
|
2143
2144
|
if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
|
|
2144
|
-
const
|
|
2145
|
+
const tickerSubtexts = [
|
|
2146
|
+
...ticker.labelSubtext ? [ticker.labelSubtext] : [],
|
|
2147
|
+
...ticker.labelSubtexts ?? [],
|
|
2148
|
+
...ticker.showCountdownInLabel ? [getCountdownText()] : []
|
|
2149
|
+
].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
|
|
2145
2150
|
addPriceAxisLabel({
|
|
2146
2151
|
text: formatPrice(tickerPrice),
|
|
2147
|
-
...
|
|
2152
|
+
...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
|
|
2148
2153
|
subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
|
|
2149
2154
|
...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
|
|
2150
2155
|
price: tickerPrice,
|
|
@@ -2230,21 +2235,22 @@ function createChart(element, options = {}) {
|
|
|
2230
2235
|
const priceLabelFontSize = Math.max(8, axis.fontSize);
|
|
2231
2236
|
ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
|
|
2232
2237
|
const positionedLabels = priceAxisLabels.map((label) => {
|
|
2233
|
-
const
|
|
2238
|
+
const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
2234
2239
|
const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
|
|
2235
|
-
const
|
|
2240
|
+
const subtextLineGap = 5;
|
|
2241
|
+
const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
|
|
2236
2242
|
const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
|
|
2237
2243
|
let subtextWidth = 0;
|
|
2238
|
-
if (
|
|
2244
|
+
if (subtexts.length > 0) {
|
|
2239
2245
|
const baseFont = ctx.font;
|
|
2240
2246
|
ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2241
|
-
subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
|
|
2247
|
+
subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
|
|
2242
2248
|
ctx.font = baseFont;
|
|
2243
2249
|
}
|
|
2244
2250
|
const labelTextWidth = Math.max(primaryWidth, subtextWidth);
|
|
2245
2251
|
return {
|
|
2246
2252
|
...label,
|
|
2247
|
-
|
|
2253
|
+
subtexts,
|
|
2248
2254
|
subtextFontSize,
|
|
2249
2255
|
height: labelHeight,
|
|
2250
2256
|
width: labelTextWidth,
|
|
@@ -2255,7 +2261,7 @@ function createChart(element, options = {}) {
|
|
|
2255
2261
|
const minY = chartTop;
|
|
2256
2262
|
let cursorY = minY;
|
|
2257
2263
|
for (const label of positionedLabels) {
|
|
2258
|
-
const maxY = chartBottom - label.height;
|
|
2264
|
+
const maxY = Math.max(minY, chartBottom - label.height);
|
|
2259
2265
|
label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
|
|
2260
2266
|
cursorY = label.y + label.height + 2;
|
|
2261
2267
|
}
|
|
@@ -2272,19 +2278,22 @@ function createChart(element, options = {}) {
|
|
|
2272
2278
|
for (const label of positionedLabels) {
|
|
2273
2279
|
ctx.fillStyle = label.backgroundColor;
|
|
2274
2280
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
2275
|
-
const
|
|
2281
|
+
const hasSubtexts = label.subtexts.length > 0;
|
|
2282
|
+
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
2276
2283
|
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
|
|
2277
|
-
if (
|
|
2284
|
+
if (hasSubtexts) {
|
|
2278
2285
|
const baseFont = ctx.font;
|
|
2279
2286
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2287
|
+
label.subtexts.forEach((subtext, index) => {
|
|
2288
|
+
drawText(
|
|
2289
|
+
subtext,
|
|
2290
|
+
labelX + labelPaddingX,
|
|
2291
|
+
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
2292
|
+
"left",
|
|
2293
|
+
"middle",
|
|
2294
|
+
label.subtextColor ?? label.textColor
|
|
2295
|
+
);
|
|
2296
|
+
});
|
|
2288
2297
|
ctx.font = baseFont;
|
|
2289
2298
|
}
|
|
2290
2299
|
}
|
|
@@ -2502,6 +2511,16 @@ function createChart(element, options = {}) {
|
|
|
2502
2511
|
emitViewportChange();
|
|
2503
2512
|
draw();
|
|
2504
2513
|
};
|
|
2514
|
+
const zoomXFromAxis = (factor) => {
|
|
2515
|
+
if (!drawState) {
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
if (followLatest) {
|
|
2519
|
+
zoomXToLatest(factor);
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
zoomX(factor, drawState.chartLeft + drawState.chartWidth / 2);
|
|
2523
|
+
};
|
|
2505
2524
|
const zoomY = (factor, anchorY) => {
|
|
2506
2525
|
if (!drawState || data.length === 0) {
|
|
2507
2526
|
return;
|
|
@@ -3025,7 +3044,7 @@ function createChart(element, options = {}) {
|
|
|
3025
3044
|
setCrosshairPoint(null);
|
|
3026
3045
|
} else if (dragMode === "x-axis") {
|
|
3027
3046
|
canvas.style.cursor = "ew-resize";
|
|
3028
|
-
|
|
3047
|
+
zoomXFromAxis(Math.exp(deltaX * 6e-3));
|
|
3029
3048
|
setCrosshairPoint(null);
|
|
3030
3049
|
} else if (dragMode === "y-axis") {
|
|
3031
3050
|
canvas.style.cursor = "ns-resize";
|
|
@@ -3136,7 +3155,7 @@ function createChart(element, options = {}) {
|
|
|
3136
3155
|
return;
|
|
3137
3156
|
}
|
|
3138
3157
|
if (region === "x-axis") {
|
|
3139
|
-
|
|
3158
|
+
zoomXFromAxis(factor);
|
|
3140
3159
|
return;
|
|
3141
3160
|
}
|
|
3142
3161
|
zoomX(factor, point.x);
|
|
@@ -261,6 +261,7 @@ interface TickerLineOptions {
|
|
|
261
261
|
labelBackgroundColor?: string;
|
|
262
262
|
labelTextColor?: string;
|
|
263
263
|
labelSubtext?: string;
|
|
264
|
+
labelSubtexts?: Array<string | number>;
|
|
264
265
|
labelSubtextColor?: string;
|
|
265
266
|
labelSubtextFontSize?: number;
|
|
266
267
|
showCountdownInLabel?: boolean;
|
|
@@ -178,6 +178,7 @@ var DEFAULT_OPTIONS = {
|
|
|
178
178
|
thickness: 1,
|
|
179
179
|
labelTextColor: "#0b1220",
|
|
180
180
|
labelSubtext: "",
|
|
181
|
+
labelSubtexts: [],
|
|
181
182
|
labelSubtextColor: "#0b1220",
|
|
182
183
|
labelSubtextFontSize: 0,
|
|
183
184
|
showCountdownInLabel: false,
|
|
@@ -2117,10 +2118,14 @@ function createChart(element, options = {}) {
|
|
|
2117
2118
|
ctx.restore();
|
|
2118
2119
|
}
|
|
2119
2120
|
if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
|
|
2120
|
-
const
|
|
2121
|
+
const tickerSubtexts = [
|
|
2122
|
+
...ticker.labelSubtext ? [ticker.labelSubtext] : [],
|
|
2123
|
+
...ticker.labelSubtexts ?? [],
|
|
2124
|
+
...ticker.showCountdownInLabel ? [getCountdownText()] : []
|
|
2125
|
+
].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
|
|
2121
2126
|
addPriceAxisLabel({
|
|
2122
2127
|
text: formatPrice(tickerPrice),
|
|
2123
|
-
...
|
|
2128
|
+
...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
|
|
2124
2129
|
subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
|
|
2125
2130
|
...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
|
|
2126
2131
|
price: tickerPrice,
|
|
@@ -2206,21 +2211,22 @@ function createChart(element, options = {}) {
|
|
|
2206
2211
|
const priceLabelFontSize = Math.max(8, axis.fontSize);
|
|
2207
2212
|
ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
|
|
2208
2213
|
const positionedLabels = priceAxisLabels.map((label) => {
|
|
2209
|
-
const
|
|
2214
|
+
const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
2210
2215
|
const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
|
|
2211
|
-
const
|
|
2216
|
+
const subtextLineGap = 5;
|
|
2217
|
+
const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
|
|
2212
2218
|
const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
|
|
2213
2219
|
let subtextWidth = 0;
|
|
2214
|
-
if (
|
|
2220
|
+
if (subtexts.length > 0) {
|
|
2215
2221
|
const baseFont = ctx.font;
|
|
2216
2222
|
ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2217
|
-
subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
|
|
2223
|
+
subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
|
|
2218
2224
|
ctx.font = baseFont;
|
|
2219
2225
|
}
|
|
2220
2226
|
const labelTextWidth = Math.max(primaryWidth, subtextWidth);
|
|
2221
2227
|
return {
|
|
2222
2228
|
...label,
|
|
2223
|
-
|
|
2229
|
+
subtexts,
|
|
2224
2230
|
subtextFontSize,
|
|
2225
2231
|
height: labelHeight,
|
|
2226
2232
|
width: labelTextWidth,
|
|
@@ -2231,7 +2237,7 @@ function createChart(element, options = {}) {
|
|
|
2231
2237
|
const minY = chartTop;
|
|
2232
2238
|
let cursorY = minY;
|
|
2233
2239
|
for (const label of positionedLabels) {
|
|
2234
|
-
const maxY = chartBottom - label.height;
|
|
2240
|
+
const maxY = Math.max(minY, chartBottom - label.height);
|
|
2235
2241
|
label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
|
|
2236
2242
|
cursorY = label.y + label.height + 2;
|
|
2237
2243
|
}
|
|
@@ -2248,19 +2254,22 @@ function createChart(element, options = {}) {
|
|
|
2248
2254
|
for (const label of positionedLabels) {
|
|
2249
2255
|
ctx.fillStyle = label.backgroundColor;
|
|
2250
2256
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
2251
|
-
const
|
|
2257
|
+
const hasSubtexts = label.subtexts.length > 0;
|
|
2258
|
+
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
2252
2259
|
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
|
|
2253
|
-
if (
|
|
2260
|
+
if (hasSubtexts) {
|
|
2254
2261
|
const baseFont = ctx.font;
|
|
2255
2262
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2263
|
+
label.subtexts.forEach((subtext, index) => {
|
|
2264
|
+
drawText(
|
|
2265
|
+
subtext,
|
|
2266
|
+
labelX + labelPaddingX,
|
|
2267
|
+
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
2268
|
+
"left",
|
|
2269
|
+
"middle",
|
|
2270
|
+
label.subtextColor ?? label.textColor
|
|
2271
|
+
);
|
|
2272
|
+
});
|
|
2264
2273
|
ctx.font = baseFont;
|
|
2265
2274
|
}
|
|
2266
2275
|
}
|
|
@@ -2478,6 +2487,16 @@ function createChart(element, options = {}) {
|
|
|
2478
2487
|
emitViewportChange();
|
|
2479
2488
|
draw();
|
|
2480
2489
|
};
|
|
2490
|
+
const zoomXFromAxis = (factor) => {
|
|
2491
|
+
if (!drawState) {
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
if (followLatest) {
|
|
2495
|
+
zoomXToLatest(factor);
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
zoomX(factor, drawState.chartLeft + drawState.chartWidth / 2);
|
|
2499
|
+
};
|
|
2481
2500
|
const zoomY = (factor, anchorY) => {
|
|
2482
2501
|
if (!drawState || data.length === 0) {
|
|
2483
2502
|
return;
|
|
@@ -3001,7 +3020,7 @@ function createChart(element, options = {}) {
|
|
|
3001
3020
|
setCrosshairPoint(null);
|
|
3002
3021
|
} else if (dragMode === "x-axis") {
|
|
3003
3022
|
canvas.style.cursor = "ew-resize";
|
|
3004
|
-
|
|
3023
|
+
zoomXFromAxis(Math.exp(deltaX * 6e-3));
|
|
3005
3024
|
setCrosshairPoint(null);
|
|
3006
3025
|
} else if (dragMode === "y-axis") {
|
|
3007
3026
|
canvas.style.cursor = "ns-resize";
|
|
@@ -3112,7 +3131,7 @@ function createChart(element, options = {}) {
|
|
|
3112
3131
|
return;
|
|
3113
3132
|
}
|
|
3114
3133
|
if (region === "x-axis") {
|
|
3115
|
-
|
|
3134
|
+
zoomXFromAxis(factor);
|
|
3116
3135
|
return;
|
|
3117
3136
|
}
|
|
3118
3137
|
zoomX(factor, point.x);
|
package/dist/index.cjs
CHANGED
|
@@ -202,6 +202,7 @@ var DEFAULT_OPTIONS = {
|
|
|
202
202
|
thickness: 1,
|
|
203
203
|
labelTextColor: "#0b1220",
|
|
204
204
|
labelSubtext: "",
|
|
205
|
+
labelSubtexts: [],
|
|
205
206
|
labelSubtextColor: "#0b1220",
|
|
206
207
|
labelSubtextFontSize: 0,
|
|
207
208
|
showCountdownInLabel: false,
|
|
@@ -2141,10 +2142,14 @@ function createChart(element, options = {}) {
|
|
|
2141
2142
|
ctx.restore();
|
|
2142
2143
|
}
|
|
2143
2144
|
if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
|
|
2144
|
-
const
|
|
2145
|
+
const tickerSubtexts = [
|
|
2146
|
+
...ticker.labelSubtext ? [ticker.labelSubtext] : [],
|
|
2147
|
+
...ticker.labelSubtexts ?? [],
|
|
2148
|
+
...ticker.showCountdownInLabel ? [getCountdownText()] : []
|
|
2149
|
+
].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
|
|
2145
2150
|
addPriceAxisLabel({
|
|
2146
2151
|
text: formatPrice(tickerPrice),
|
|
2147
|
-
...
|
|
2152
|
+
...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
|
|
2148
2153
|
subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
|
|
2149
2154
|
...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
|
|
2150
2155
|
price: tickerPrice,
|
|
@@ -2230,21 +2235,22 @@ function createChart(element, options = {}) {
|
|
|
2230
2235
|
const priceLabelFontSize = Math.max(8, axis.fontSize);
|
|
2231
2236
|
ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
|
|
2232
2237
|
const positionedLabels = priceAxisLabels.map((label) => {
|
|
2233
|
-
const
|
|
2238
|
+
const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
2234
2239
|
const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
|
|
2235
|
-
const
|
|
2240
|
+
const subtextLineGap = 5;
|
|
2241
|
+
const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
|
|
2236
2242
|
const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
|
|
2237
2243
|
let subtextWidth = 0;
|
|
2238
|
-
if (
|
|
2244
|
+
if (subtexts.length > 0) {
|
|
2239
2245
|
const baseFont = ctx.font;
|
|
2240
2246
|
ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2241
|
-
subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
|
|
2247
|
+
subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
|
|
2242
2248
|
ctx.font = baseFont;
|
|
2243
2249
|
}
|
|
2244
2250
|
const labelTextWidth = Math.max(primaryWidth, subtextWidth);
|
|
2245
2251
|
return {
|
|
2246
2252
|
...label,
|
|
2247
|
-
|
|
2253
|
+
subtexts,
|
|
2248
2254
|
subtextFontSize,
|
|
2249
2255
|
height: labelHeight,
|
|
2250
2256
|
width: labelTextWidth,
|
|
@@ -2255,7 +2261,7 @@ function createChart(element, options = {}) {
|
|
|
2255
2261
|
const minY = chartTop;
|
|
2256
2262
|
let cursorY = minY;
|
|
2257
2263
|
for (const label of positionedLabels) {
|
|
2258
|
-
const maxY = chartBottom - label.height;
|
|
2264
|
+
const maxY = Math.max(minY, chartBottom - label.height);
|
|
2259
2265
|
label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
|
|
2260
2266
|
cursorY = label.y + label.height + 2;
|
|
2261
2267
|
}
|
|
@@ -2272,19 +2278,22 @@ function createChart(element, options = {}) {
|
|
|
2272
2278
|
for (const label of positionedLabels) {
|
|
2273
2279
|
ctx.fillStyle = label.backgroundColor;
|
|
2274
2280
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
2275
|
-
const
|
|
2281
|
+
const hasSubtexts = label.subtexts.length > 0;
|
|
2282
|
+
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
2276
2283
|
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
|
|
2277
|
-
if (
|
|
2284
|
+
if (hasSubtexts) {
|
|
2278
2285
|
const baseFont = ctx.font;
|
|
2279
2286
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2287
|
+
label.subtexts.forEach((subtext, index) => {
|
|
2288
|
+
drawText(
|
|
2289
|
+
subtext,
|
|
2290
|
+
labelX + labelPaddingX,
|
|
2291
|
+
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
2292
|
+
"left",
|
|
2293
|
+
"middle",
|
|
2294
|
+
label.subtextColor ?? label.textColor
|
|
2295
|
+
);
|
|
2296
|
+
});
|
|
2288
2297
|
ctx.font = baseFont;
|
|
2289
2298
|
}
|
|
2290
2299
|
}
|
|
@@ -2502,6 +2511,16 @@ function createChart(element, options = {}) {
|
|
|
2502
2511
|
emitViewportChange();
|
|
2503
2512
|
draw();
|
|
2504
2513
|
};
|
|
2514
|
+
const zoomXFromAxis = (factor) => {
|
|
2515
|
+
if (!drawState) {
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
if (followLatest) {
|
|
2519
|
+
zoomXToLatest(factor);
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
zoomX(factor, drawState.chartLeft + drawState.chartWidth / 2);
|
|
2523
|
+
};
|
|
2505
2524
|
const zoomY = (factor, anchorY) => {
|
|
2506
2525
|
if (!drawState || data.length === 0) {
|
|
2507
2526
|
return;
|
|
@@ -3025,7 +3044,7 @@ function createChart(element, options = {}) {
|
|
|
3025
3044
|
setCrosshairPoint(null);
|
|
3026
3045
|
} else if (dragMode === "x-axis") {
|
|
3027
3046
|
canvas.style.cursor = "ew-resize";
|
|
3028
|
-
|
|
3047
|
+
zoomXFromAxis(Math.exp(deltaX * 6e-3));
|
|
3029
3048
|
setCrosshairPoint(null);
|
|
3030
3049
|
} else if (dragMode === "y-axis") {
|
|
3031
3050
|
canvas.style.cursor = "ns-resize";
|
|
@@ -3136,7 +3155,7 @@ function createChart(element, options = {}) {
|
|
|
3136
3155
|
return;
|
|
3137
3156
|
}
|
|
3138
3157
|
if (region === "x-axis") {
|
|
3139
|
-
|
|
3158
|
+
zoomXFromAxis(factor);
|
|
3140
3159
|
return;
|
|
3141
3160
|
}
|
|
3142
3161
|
zoomX(factor, point.x);
|
package/dist/index.d.cts
CHANGED
|
@@ -261,6 +261,7 @@ interface TickerLineOptions {
|
|
|
261
261
|
labelBackgroundColor?: string;
|
|
262
262
|
labelTextColor?: string;
|
|
263
263
|
labelSubtext?: string;
|
|
264
|
+
labelSubtexts?: Array<string | number>;
|
|
264
265
|
labelSubtextColor?: string;
|
|
265
266
|
labelSubtextFontSize?: number;
|
|
266
267
|
showCountdownInLabel?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -261,6 +261,7 @@ interface TickerLineOptions {
|
|
|
261
261
|
labelBackgroundColor?: string;
|
|
262
262
|
labelTextColor?: string;
|
|
263
263
|
labelSubtext?: string;
|
|
264
|
+
labelSubtexts?: Array<string | number>;
|
|
264
265
|
labelSubtextColor?: string;
|
|
265
266
|
labelSubtextFontSize?: number;
|
|
266
267
|
showCountdownInLabel?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -178,6 +178,7 @@ var DEFAULT_OPTIONS = {
|
|
|
178
178
|
thickness: 1,
|
|
179
179
|
labelTextColor: "#0b1220",
|
|
180
180
|
labelSubtext: "",
|
|
181
|
+
labelSubtexts: [],
|
|
181
182
|
labelSubtextColor: "#0b1220",
|
|
182
183
|
labelSubtextFontSize: 0,
|
|
183
184
|
showCountdownInLabel: false,
|
|
@@ -2117,10 +2118,14 @@ function createChart(element, options = {}) {
|
|
|
2117
2118
|
ctx.restore();
|
|
2118
2119
|
}
|
|
2119
2120
|
if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
|
|
2120
|
-
const
|
|
2121
|
+
const tickerSubtexts = [
|
|
2122
|
+
...ticker.labelSubtext ? [ticker.labelSubtext] : [],
|
|
2123
|
+
...ticker.labelSubtexts ?? [],
|
|
2124
|
+
...ticker.showCountdownInLabel ? [getCountdownText()] : []
|
|
2125
|
+
].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
|
|
2121
2126
|
addPriceAxisLabel({
|
|
2122
2127
|
text: formatPrice(tickerPrice),
|
|
2123
|
-
...
|
|
2128
|
+
...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
|
|
2124
2129
|
subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
|
|
2125
2130
|
...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
|
|
2126
2131
|
price: tickerPrice,
|
|
@@ -2206,21 +2211,22 @@ function createChart(element, options = {}) {
|
|
|
2206
2211
|
const priceLabelFontSize = Math.max(8, axis.fontSize);
|
|
2207
2212
|
ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
|
|
2208
2213
|
const positionedLabels = priceAxisLabels.map((label) => {
|
|
2209
|
-
const
|
|
2214
|
+
const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
2210
2215
|
const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
|
|
2211
|
-
const
|
|
2216
|
+
const subtextLineGap = 5;
|
|
2217
|
+
const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
|
|
2212
2218
|
const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
|
|
2213
2219
|
let subtextWidth = 0;
|
|
2214
|
-
if (
|
|
2220
|
+
if (subtexts.length > 0) {
|
|
2215
2221
|
const baseFont = ctx.font;
|
|
2216
2222
|
ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2217
|
-
subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
|
|
2223
|
+
subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
|
|
2218
2224
|
ctx.font = baseFont;
|
|
2219
2225
|
}
|
|
2220
2226
|
const labelTextWidth = Math.max(primaryWidth, subtextWidth);
|
|
2221
2227
|
return {
|
|
2222
2228
|
...label,
|
|
2223
|
-
|
|
2229
|
+
subtexts,
|
|
2224
2230
|
subtextFontSize,
|
|
2225
2231
|
height: labelHeight,
|
|
2226
2232
|
width: labelTextWidth,
|
|
@@ -2231,7 +2237,7 @@ function createChart(element, options = {}) {
|
|
|
2231
2237
|
const minY = chartTop;
|
|
2232
2238
|
let cursorY = minY;
|
|
2233
2239
|
for (const label of positionedLabels) {
|
|
2234
|
-
const maxY = chartBottom - label.height;
|
|
2240
|
+
const maxY = Math.max(minY, chartBottom - label.height);
|
|
2235
2241
|
label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
|
|
2236
2242
|
cursorY = label.y + label.height + 2;
|
|
2237
2243
|
}
|
|
@@ -2248,19 +2254,22 @@ function createChart(element, options = {}) {
|
|
|
2248
2254
|
for (const label of positionedLabels) {
|
|
2249
2255
|
ctx.fillStyle = label.backgroundColor;
|
|
2250
2256
|
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
|
|
2251
|
-
const
|
|
2257
|
+
const hasSubtexts = label.subtexts.length > 0;
|
|
2258
|
+
const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
|
|
2252
2259
|
drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
|
|
2253
|
-
if (
|
|
2260
|
+
if (hasSubtexts) {
|
|
2254
2261
|
const baseFont = ctx.font;
|
|
2255
2262
|
ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2263
|
+
label.subtexts.forEach((subtext, index) => {
|
|
2264
|
+
drawText(
|
|
2265
|
+
subtext,
|
|
2266
|
+
labelX + labelPaddingX,
|
|
2267
|
+
label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
|
|
2268
|
+
"left",
|
|
2269
|
+
"middle",
|
|
2270
|
+
label.subtextColor ?? label.textColor
|
|
2271
|
+
);
|
|
2272
|
+
});
|
|
2264
2273
|
ctx.font = baseFont;
|
|
2265
2274
|
}
|
|
2266
2275
|
}
|
|
@@ -2478,6 +2487,16 @@ function createChart(element, options = {}) {
|
|
|
2478
2487
|
emitViewportChange();
|
|
2479
2488
|
draw();
|
|
2480
2489
|
};
|
|
2490
|
+
const zoomXFromAxis = (factor) => {
|
|
2491
|
+
if (!drawState) {
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
if (followLatest) {
|
|
2495
|
+
zoomXToLatest(factor);
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
zoomX(factor, drawState.chartLeft + drawState.chartWidth / 2);
|
|
2499
|
+
};
|
|
2481
2500
|
const zoomY = (factor, anchorY) => {
|
|
2482
2501
|
if (!drawState || data.length === 0) {
|
|
2483
2502
|
return;
|
|
@@ -3001,7 +3020,7 @@ function createChart(element, options = {}) {
|
|
|
3001
3020
|
setCrosshairPoint(null);
|
|
3002
3021
|
} else if (dragMode === "x-axis") {
|
|
3003
3022
|
canvas.style.cursor = "ew-resize";
|
|
3004
|
-
|
|
3023
|
+
zoomXFromAxis(Math.exp(deltaX * 6e-3));
|
|
3005
3024
|
setCrosshairPoint(null);
|
|
3006
3025
|
} else if (dragMode === "y-axis") {
|
|
3007
3026
|
canvas.style.cursor = "ns-resize";
|
|
@@ -3112,7 +3131,7 @@ function createChart(element, options = {}) {
|
|
|
3112
3131
|
return;
|
|
3113
3132
|
}
|
|
3114
3133
|
if (region === "x-axis") {
|
|
3115
|
-
|
|
3134
|
+
zoomXFromAxis(factor);
|
|
3116
3135
|
return;
|
|
3117
3136
|
}
|
|
3118
3137
|
zoomX(factor, point.x);
|
package/docs/API.md
CHANGED
|
@@ -149,10 +149,11 @@ watermark: {
|
|
|
149
149
|
- `color` (default `#38bdf8`)
|
|
150
150
|
- `labelBackgroundColor` (default `#38bdf8`)
|
|
151
151
|
- `labelTextColor` (default `#0b1220`)
|
|
152
|
-
- `labelSubtext` (optional
|
|
152
|
+
- `labelSubtext` (optional single extra line inside the current-price label, for example `"MNQ"`)
|
|
153
|
+
- `labelSubtexts` (optional array of extra lines inside the current-price label, for example `["MNQ", "RTH"]`)
|
|
153
154
|
- `labelSubtextColor` (defaults to the label text color)
|
|
154
|
-
- `labelSubtextFontSize` (default `0`, meaning
|
|
155
|
-
- `showCountdownInLabel` (default `false`;
|
|
155
|
+
- `labelSubtextFontSize` (default `0`, meaning each extra line uses the same font size as the price)
|
|
156
|
+
- `showCountdownInLabel` (default `false`; appends bar-close countdown as another extra line)
|
|
156
157
|
- `labelBorderRadius` (default `3`)
|
|
157
158
|
|
|
158
159
|
### `LabelsOptions`
|