hyperprop-charting-library 0.1.150 → 0.1.152
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 +12 -8
- package/dist/hyperprop-charting-library.d.ts +11 -1
- package/dist/hyperprop-charting-library.js +12 -8
- package/dist/index.cjs +12 -8
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +12 -8
- package/docs/API.md +24 -0
- package/package.json +1 -1
|
@@ -3587,8 +3587,10 @@ var DEFAULT_LABELS_OPTIONS = {
|
|
|
3587
3587
|
showIndicatorValues: false,
|
|
3588
3588
|
showIndicatorValueLabels: true,
|
|
3589
3589
|
indicatorLegendPosition: "top-left",
|
|
3590
|
-
|
|
3591
|
-
|
|
3590
|
+
// One inset for the whole top-left block (data line + legend), so the gap
|
|
3591
|
+
// from the left edge matches the gap from the top.
|
|
3592
|
+
indicatorLegendOffsetX: 12,
|
|
3593
|
+
indicatorLegendOffsetY: 12,
|
|
3592
3594
|
showCountdownToBarClose: false,
|
|
3593
3595
|
noOverlapping: true,
|
|
3594
3596
|
backgroundColor: "#0b1220",
|
|
@@ -7156,7 +7158,7 @@ function createChart(element, options = {}) {
|
|
|
7156
7158
|
ctx.stroke();
|
|
7157
7159
|
ctx.restore();
|
|
7158
7160
|
}
|
|
7159
|
-
const sessionSeparatorsOn = grid.sessionSeparators
|
|
7161
|
+
const sessionSeparatorsOn = grid.sessionSeparators && sessionOptions.separators !== false;
|
|
7160
7162
|
const intradaySeries = (() => {
|
|
7161
7163
|
if (data.length < 2) return false;
|
|
7162
7164
|
const probe = Math.min(Math.max(1, startIndex), data.length - 1);
|
|
@@ -8264,6 +8266,8 @@ function createChart(element, options = {}) {
|
|
|
8264
8266
|
for (const orderLine of orderLines) {
|
|
8265
8267
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
8266
8268
|
}
|
|
8269
|
+
const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
|
|
8270
|
+
const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
|
|
8267
8271
|
let dataLineBottom = chartTop;
|
|
8268
8272
|
const dataLine = mergedOptions.dataLine;
|
|
8269
8273
|
if (dataLine?.visible && data.length > 0) {
|
|
@@ -8280,8 +8284,8 @@ function createChart(element, options = {}) {
|
|
|
8280
8284
|
const textColor = dataLine.textColor || labels.indicatorTextColor;
|
|
8281
8285
|
const changeAbs = bar.c - bar.o;
|
|
8282
8286
|
const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
|
|
8283
|
-
const rowY = chartTop +
|
|
8284
|
-
let cursorX = chartLeft +
|
|
8287
|
+
const rowY = chartTop + legendInsetY;
|
|
8288
|
+
let cursorX = chartLeft + legendInsetX;
|
|
8285
8289
|
if (dataLine.statusColor) {
|
|
8286
8290
|
const dotRadius = Math.max(2.5, fontSize / 4);
|
|
8287
8291
|
ctx.fillStyle = dataLine.statusColor;
|
|
@@ -8327,7 +8331,7 @@ function createChart(element, options = {}) {
|
|
|
8327
8331
|
ctx.fillText(text, cursorX, rowY);
|
|
8328
8332
|
cursorX += width2 + 12;
|
|
8329
8333
|
}
|
|
8330
|
-
dataLineBottom = rowY + fontSize +
|
|
8334
|
+
dataLineBottom = rowY + fontSize + 6;
|
|
8331
8335
|
ctx.font = prevFont;
|
|
8332
8336
|
}
|
|
8333
8337
|
}
|
|
@@ -8357,8 +8361,8 @@ function createChart(element, options = {}) {
|
|
|
8357
8361
|
const prevFont = ctx.font;
|
|
8358
8362
|
const legendFontSize = Math.max(8, axis.fontSize);
|
|
8359
8363
|
ctx.font = `${legendFontSize}px ${mergedOptions.fontFamily}`;
|
|
8360
|
-
const offsetX =
|
|
8361
|
-
const offsetY =
|
|
8364
|
+
const offsetX = legendInsetX;
|
|
8365
|
+
const offsetY = legendInsetY;
|
|
8362
8366
|
const position = labels.indicatorLegendPosition;
|
|
8363
8367
|
const isRight = position === "top-right" || position === "bottom-right";
|
|
8364
8368
|
const isBottom = position === "bottom-left" || position === "bottom-right";
|
|
@@ -844,6 +844,11 @@ interface LabelsOptions {
|
|
|
844
844
|
showIndicatorValues?: boolean;
|
|
845
845
|
showIndicatorValueLabels?: boolean;
|
|
846
846
|
indicatorLegendPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
847
|
+
/**
|
|
848
|
+
* Inset of the whole top-left block — the data line and the indicator
|
|
849
|
+
* legend stacked under it — from the plot edges. Both axes use the same
|
|
850
|
+
* pair so the block never sits closer to the top than to the side.
|
|
851
|
+
*/
|
|
847
852
|
indicatorLegendOffsetX?: number;
|
|
848
853
|
indicatorLegendOffsetY?: number;
|
|
849
854
|
showCountdownToBarClose?: boolean;
|
|
@@ -1192,7 +1197,12 @@ type SessionPresetName = "cme-futures" | "cme-rth" | "us-equities" | "us-equitie
|
|
|
1192
1197
|
interface SessionOptions {
|
|
1193
1198
|
/** A preset name or your own spec. null disables session handling. */
|
|
1194
1199
|
spec?: SessionSpec | SessionPresetName | null;
|
|
1195
|
-
/**
|
|
1200
|
+
/**
|
|
1201
|
+
* Opt out of session-aware separators while keeping the spec for everything
|
|
1202
|
+
* else. Setting a spec never turns separators *on* by itself — that switch
|
|
1203
|
+
* is `grid.sessionSeparators`; the spec only moves the boundary from
|
|
1204
|
+
* calendar midnight to the real session open.
|
|
1205
|
+
*/
|
|
1196
1206
|
separators?: boolean;
|
|
1197
1207
|
/**
|
|
1198
1208
|
* Tint bars that fall outside the session (overnight/extended hours), the
|
|
@@ -3551,8 +3551,10 @@ var DEFAULT_LABELS_OPTIONS = {
|
|
|
3551
3551
|
showIndicatorValues: false,
|
|
3552
3552
|
showIndicatorValueLabels: true,
|
|
3553
3553
|
indicatorLegendPosition: "top-left",
|
|
3554
|
-
|
|
3555
|
-
|
|
3554
|
+
// One inset for the whole top-left block (data line + legend), so the gap
|
|
3555
|
+
// from the left edge matches the gap from the top.
|
|
3556
|
+
indicatorLegendOffsetX: 12,
|
|
3557
|
+
indicatorLegendOffsetY: 12,
|
|
3556
3558
|
showCountdownToBarClose: false,
|
|
3557
3559
|
noOverlapping: true,
|
|
3558
3560
|
backgroundColor: "#0b1220",
|
|
@@ -7120,7 +7122,7 @@ function createChart(element, options = {}) {
|
|
|
7120
7122
|
ctx.stroke();
|
|
7121
7123
|
ctx.restore();
|
|
7122
7124
|
}
|
|
7123
|
-
const sessionSeparatorsOn = grid.sessionSeparators
|
|
7125
|
+
const sessionSeparatorsOn = grid.sessionSeparators && sessionOptions.separators !== false;
|
|
7124
7126
|
const intradaySeries = (() => {
|
|
7125
7127
|
if (data.length < 2) return false;
|
|
7126
7128
|
const probe = Math.min(Math.max(1, startIndex), data.length - 1);
|
|
@@ -8228,6 +8230,8 @@ function createChart(element, options = {}) {
|
|
|
8228
8230
|
for (const orderLine of orderLines) {
|
|
8229
8231
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
8230
8232
|
}
|
|
8233
|
+
const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
|
|
8234
|
+
const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
|
|
8231
8235
|
let dataLineBottom = chartTop;
|
|
8232
8236
|
const dataLine = mergedOptions.dataLine;
|
|
8233
8237
|
if (dataLine?.visible && data.length > 0) {
|
|
@@ -8244,8 +8248,8 @@ function createChart(element, options = {}) {
|
|
|
8244
8248
|
const textColor = dataLine.textColor || labels.indicatorTextColor;
|
|
8245
8249
|
const changeAbs = bar.c - bar.o;
|
|
8246
8250
|
const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
|
|
8247
|
-
const rowY = chartTop +
|
|
8248
|
-
let cursorX = chartLeft +
|
|
8251
|
+
const rowY = chartTop + legendInsetY;
|
|
8252
|
+
let cursorX = chartLeft + legendInsetX;
|
|
8249
8253
|
if (dataLine.statusColor) {
|
|
8250
8254
|
const dotRadius = Math.max(2.5, fontSize / 4);
|
|
8251
8255
|
ctx.fillStyle = dataLine.statusColor;
|
|
@@ -8291,7 +8295,7 @@ function createChart(element, options = {}) {
|
|
|
8291
8295
|
ctx.fillText(text, cursorX, rowY);
|
|
8292
8296
|
cursorX += width2 + 12;
|
|
8293
8297
|
}
|
|
8294
|
-
dataLineBottom = rowY + fontSize +
|
|
8298
|
+
dataLineBottom = rowY + fontSize + 6;
|
|
8295
8299
|
ctx.font = prevFont;
|
|
8296
8300
|
}
|
|
8297
8301
|
}
|
|
@@ -8321,8 +8325,8 @@ function createChart(element, options = {}) {
|
|
|
8321
8325
|
const prevFont = ctx.font;
|
|
8322
8326
|
const legendFontSize = Math.max(8, axis.fontSize);
|
|
8323
8327
|
ctx.font = `${legendFontSize}px ${mergedOptions.fontFamily}`;
|
|
8324
|
-
const offsetX =
|
|
8325
|
-
const offsetY =
|
|
8328
|
+
const offsetX = legendInsetX;
|
|
8329
|
+
const offsetY = legendInsetY;
|
|
8326
8330
|
const position = labels.indicatorLegendPosition;
|
|
8327
8331
|
const isRight = position === "top-right" || position === "bottom-right";
|
|
8328
8332
|
const isBottom = position === "bottom-left" || position === "bottom-right";
|
package/dist/index.cjs
CHANGED
|
@@ -3587,8 +3587,10 @@ var DEFAULT_LABELS_OPTIONS = {
|
|
|
3587
3587
|
showIndicatorValues: false,
|
|
3588
3588
|
showIndicatorValueLabels: true,
|
|
3589
3589
|
indicatorLegendPosition: "top-left",
|
|
3590
|
-
|
|
3591
|
-
|
|
3590
|
+
// One inset for the whole top-left block (data line + legend), so the gap
|
|
3591
|
+
// from the left edge matches the gap from the top.
|
|
3592
|
+
indicatorLegendOffsetX: 12,
|
|
3593
|
+
indicatorLegendOffsetY: 12,
|
|
3592
3594
|
showCountdownToBarClose: false,
|
|
3593
3595
|
noOverlapping: true,
|
|
3594
3596
|
backgroundColor: "#0b1220",
|
|
@@ -7156,7 +7158,7 @@ function createChart(element, options = {}) {
|
|
|
7156
7158
|
ctx.stroke();
|
|
7157
7159
|
ctx.restore();
|
|
7158
7160
|
}
|
|
7159
|
-
const sessionSeparatorsOn = grid.sessionSeparators
|
|
7161
|
+
const sessionSeparatorsOn = grid.sessionSeparators && sessionOptions.separators !== false;
|
|
7160
7162
|
const intradaySeries = (() => {
|
|
7161
7163
|
if (data.length < 2) return false;
|
|
7162
7164
|
const probe = Math.min(Math.max(1, startIndex), data.length - 1);
|
|
@@ -8264,6 +8266,8 @@ function createChart(element, options = {}) {
|
|
|
8264
8266
|
for (const orderLine of orderLines) {
|
|
8265
8267
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
8266
8268
|
}
|
|
8269
|
+
const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
|
|
8270
|
+
const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
|
|
8267
8271
|
let dataLineBottom = chartTop;
|
|
8268
8272
|
const dataLine = mergedOptions.dataLine;
|
|
8269
8273
|
if (dataLine?.visible && data.length > 0) {
|
|
@@ -8280,8 +8284,8 @@ function createChart(element, options = {}) {
|
|
|
8280
8284
|
const textColor = dataLine.textColor || labels.indicatorTextColor;
|
|
8281
8285
|
const changeAbs = bar.c - bar.o;
|
|
8282
8286
|
const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
|
|
8283
|
-
const rowY = chartTop +
|
|
8284
|
-
let cursorX = chartLeft +
|
|
8287
|
+
const rowY = chartTop + legendInsetY;
|
|
8288
|
+
let cursorX = chartLeft + legendInsetX;
|
|
8285
8289
|
if (dataLine.statusColor) {
|
|
8286
8290
|
const dotRadius = Math.max(2.5, fontSize / 4);
|
|
8287
8291
|
ctx.fillStyle = dataLine.statusColor;
|
|
@@ -8327,7 +8331,7 @@ function createChart(element, options = {}) {
|
|
|
8327
8331
|
ctx.fillText(text, cursorX, rowY);
|
|
8328
8332
|
cursorX += width2 + 12;
|
|
8329
8333
|
}
|
|
8330
|
-
dataLineBottom = rowY + fontSize +
|
|
8334
|
+
dataLineBottom = rowY + fontSize + 6;
|
|
8331
8335
|
ctx.font = prevFont;
|
|
8332
8336
|
}
|
|
8333
8337
|
}
|
|
@@ -8357,8 +8361,8 @@ function createChart(element, options = {}) {
|
|
|
8357
8361
|
const prevFont = ctx.font;
|
|
8358
8362
|
const legendFontSize = Math.max(8, axis.fontSize);
|
|
8359
8363
|
ctx.font = `${legendFontSize}px ${mergedOptions.fontFamily}`;
|
|
8360
|
-
const offsetX =
|
|
8361
|
-
const offsetY =
|
|
8364
|
+
const offsetX = legendInsetX;
|
|
8365
|
+
const offsetY = legendInsetY;
|
|
8362
8366
|
const position = labels.indicatorLegendPosition;
|
|
8363
8367
|
const isRight = position === "top-right" || position === "bottom-right";
|
|
8364
8368
|
const isBottom = position === "bottom-left" || position === "bottom-right";
|
package/dist/index.d.cts
CHANGED
|
@@ -844,6 +844,11 @@ interface LabelsOptions {
|
|
|
844
844
|
showIndicatorValues?: boolean;
|
|
845
845
|
showIndicatorValueLabels?: boolean;
|
|
846
846
|
indicatorLegendPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
847
|
+
/**
|
|
848
|
+
* Inset of the whole top-left block — the data line and the indicator
|
|
849
|
+
* legend stacked under it — from the plot edges. Both axes use the same
|
|
850
|
+
* pair so the block never sits closer to the top than to the side.
|
|
851
|
+
*/
|
|
847
852
|
indicatorLegendOffsetX?: number;
|
|
848
853
|
indicatorLegendOffsetY?: number;
|
|
849
854
|
showCountdownToBarClose?: boolean;
|
|
@@ -1192,7 +1197,12 @@ type SessionPresetName = "cme-futures" | "cme-rth" | "us-equities" | "us-equitie
|
|
|
1192
1197
|
interface SessionOptions {
|
|
1193
1198
|
/** A preset name or your own spec. null disables session handling. */
|
|
1194
1199
|
spec?: SessionSpec | SessionPresetName | null;
|
|
1195
|
-
/**
|
|
1200
|
+
/**
|
|
1201
|
+
* Opt out of session-aware separators while keeping the spec for everything
|
|
1202
|
+
* else. Setting a spec never turns separators *on* by itself — that switch
|
|
1203
|
+
* is `grid.sessionSeparators`; the spec only moves the boundary from
|
|
1204
|
+
* calendar midnight to the real session open.
|
|
1205
|
+
*/
|
|
1196
1206
|
separators?: boolean;
|
|
1197
1207
|
/**
|
|
1198
1208
|
* Tint bars that fall outside the session (overnight/extended hours), the
|
package/dist/index.d.ts
CHANGED
|
@@ -844,6 +844,11 @@ interface LabelsOptions {
|
|
|
844
844
|
showIndicatorValues?: boolean;
|
|
845
845
|
showIndicatorValueLabels?: boolean;
|
|
846
846
|
indicatorLegendPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
847
|
+
/**
|
|
848
|
+
* Inset of the whole top-left block — the data line and the indicator
|
|
849
|
+
* legend stacked under it — from the plot edges. Both axes use the same
|
|
850
|
+
* pair so the block never sits closer to the top than to the side.
|
|
851
|
+
*/
|
|
847
852
|
indicatorLegendOffsetX?: number;
|
|
848
853
|
indicatorLegendOffsetY?: number;
|
|
849
854
|
showCountdownToBarClose?: boolean;
|
|
@@ -1192,7 +1197,12 @@ type SessionPresetName = "cme-futures" | "cme-rth" | "us-equities" | "us-equitie
|
|
|
1192
1197
|
interface SessionOptions {
|
|
1193
1198
|
/** A preset name or your own spec. null disables session handling. */
|
|
1194
1199
|
spec?: SessionSpec | SessionPresetName | null;
|
|
1195
|
-
/**
|
|
1200
|
+
/**
|
|
1201
|
+
* Opt out of session-aware separators while keeping the spec for everything
|
|
1202
|
+
* else. Setting a spec never turns separators *on* by itself — that switch
|
|
1203
|
+
* is `grid.sessionSeparators`; the spec only moves the boundary from
|
|
1204
|
+
* calendar midnight to the real session open.
|
|
1205
|
+
*/
|
|
1196
1206
|
separators?: boolean;
|
|
1197
1207
|
/**
|
|
1198
1208
|
* Tint bars that fall outside the session (overnight/extended hours), the
|
package/dist/index.js
CHANGED
|
@@ -3551,8 +3551,10 @@ var DEFAULT_LABELS_OPTIONS = {
|
|
|
3551
3551
|
showIndicatorValues: false,
|
|
3552
3552
|
showIndicatorValueLabels: true,
|
|
3553
3553
|
indicatorLegendPosition: "top-left",
|
|
3554
|
-
|
|
3555
|
-
|
|
3554
|
+
// One inset for the whole top-left block (data line + legend), so the gap
|
|
3555
|
+
// from the left edge matches the gap from the top.
|
|
3556
|
+
indicatorLegendOffsetX: 12,
|
|
3557
|
+
indicatorLegendOffsetY: 12,
|
|
3556
3558
|
showCountdownToBarClose: false,
|
|
3557
3559
|
noOverlapping: true,
|
|
3558
3560
|
backgroundColor: "#0b1220",
|
|
@@ -7120,7 +7122,7 @@ function createChart(element, options = {}) {
|
|
|
7120
7122
|
ctx.stroke();
|
|
7121
7123
|
ctx.restore();
|
|
7122
7124
|
}
|
|
7123
|
-
const sessionSeparatorsOn = grid.sessionSeparators
|
|
7125
|
+
const sessionSeparatorsOn = grid.sessionSeparators && sessionOptions.separators !== false;
|
|
7124
7126
|
const intradaySeries = (() => {
|
|
7125
7127
|
if (data.length < 2) return false;
|
|
7126
7128
|
const probe = Math.min(Math.max(1, startIndex), data.length - 1);
|
|
@@ -8228,6 +8230,8 @@ function createChart(element, options = {}) {
|
|
|
8228
8230
|
for (const orderLine of orderLines) {
|
|
8229
8231
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
8230
8232
|
}
|
|
8233
|
+
const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
|
|
8234
|
+
const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
|
|
8231
8235
|
let dataLineBottom = chartTop;
|
|
8232
8236
|
const dataLine = mergedOptions.dataLine;
|
|
8233
8237
|
if (dataLine?.visible && data.length > 0) {
|
|
@@ -8244,8 +8248,8 @@ function createChart(element, options = {}) {
|
|
|
8244
8248
|
const textColor = dataLine.textColor || labels.indicatorTextColor;
|
|
8245
8249
|
const changeAbs = bar.c - bar.o;
|
|
8246
8250
|
const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
|
|
8247
|
-
const rowY = chartTop +
|
|
8248
|
-
let cursorX = chartLeft +
|
|
8251
|
+
const rowY = chartTop + legendInsetY;
|
|
8252
|
+
let cursorX = chartLeft + legendInsetX;
|
|
8249
8253
|
if (dataLine.statusColor) {
|
|
8250
8254
|
const dotRadius = Math.max(2.5, fontSize / 4);
|
|
8251
8255
|
ctx.fillStyle = dataLine.statusColor;
|
|
@@ -8291,7 +8295,7 @@ function createChart(element, options = {}) {
|
|
|
8291
8295
|
ctx.fillText(text, cursorX, rowY);
|
|
8292
8296
|
cursorX += width2 + 12;
|
|
8293
8297
|
}
|
|
8294
|
-
dataLineBottom = rowY + fontSize +
|
|
8298
|
+
dataLineBottom = rowY + fontSize + 6;
|
|
8295
8299
|
ctx.font = prevFont;
|
|
8296
8300
|
}
|
|
8297
8301
|
}
|
|
@@ -8321,8 +8325,8 @@ function createChart(element, options = {}) {
|
|
|
8321
8325
|
const prevFont = ctx.font;
|
|
8322
8326
|
const legendFontSize = Math.max(8, axis.fontSize);
|
|
8323
8327
|
ctx.font = `${legendFontSize}px ${mergedOptions.fontFamily}`;
|
|
8324
|
-
const offsetX =
|
|
8325
|
-
const offsetY =
|
|
8328
|
+
const offsetX = legendInsetX;
|
|
8329
|
+
const offsetY = legendInsetY;
|
|
8326
8330
|
const position = labels.indicatorLegendPosition;
|
|
8327
8331
|
const isRight = position === "top-right" || position === "bottom-right";
|
|
8328
8332
|
const isBottom = position === "bottom-left" || position === "bottom-right";
|
package/docs/API.md
CHANGED
|
@@ -881,6 +881,30 @@ they are not part of `saveState()`.
|
|
|
881
881
|
|
|
882
882
|
---
|
|
883
883
|
|
|
884
|
+
## Multi-chart layouts
|
|
885
|
+
|
|
886
|
+
Several charts are just several `createChart` calls; what the library adds is
|
|
887
|
+
the piece hosts can't do themselves — mirroring one chart's crosshair onto the
|
|
888
|
+
others:
|
|
889
|
+
|
|
890
|
+
```ts
|
|
891
|
+
primary.onCrosshairMove((event) => {
|
|
892
|
+
const ms = event.time ? Date.parse(event.time) : NaN;
|
|
893
|
+
const at = event.region === "plot" && Number.isFinite(ms) ? { timeMs: ms } : null;
|
|
894
|
+
for (const other of siblings) other.setExternalCrosshair(at);
|
|
895
|
+
});
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
Mirrored crosshairs match by **timestamp**, not pixels, so charts on different
|
|
899
|
+
intervals — or different instruments — still line up, and the ghost line is
|
|
900
|
+
drawn dimmer than the chart's own. It is suppressed while that chart is the one
|
|
901
|
+
being hovered, so the two never fight.
|
|
902
|
+
|
|
903
|
+
Time-range sync needs nothing new: feed `onViewportChange` into the siblings'
|
|
904
|
+
`setViewport({ centerTimeMs, xSpan })`.
|
|
905
|
+
|
|
906
|
+
---
|
|
907
|
+
|
|
884
908
|
## Overlay indicator legend
|
|
885
909
|
|
|
886
910
|
With `labels.showIndicatorNames` / `showIndicatorValues`, overlay indicators
|