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.
@@ -3587,8 +3587,10 @@ var DEFAULT_LABELS_OPTIONS = {
3587
3587
  showIndicatorValues: false,
3588
3588
  showIndicatorValueLabels: true,
3589
3589
  indicatorLegendPosition: "top-left",
3590
- indicatorLegendOffsetX: 10,
3591
- indicatorLegendOffsetY: 10,
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 || compiledSession !== null && sessionOptions.separators !== false;
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 + 6;
8284
- let cursorX = chartLeft + 10;
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 + 4;
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 = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8361
- const offsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
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
- /** Vertical rule at each session start. Default true when a spec is set. */
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
- indicatorLegendOffsetX: 10,
3555
- indicatorLegendOffsetY: 10,
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 || compiledSession !== null && sessionOptions.separators !== false;
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 + 6;
8248
- let cursorX = chartLeft + 10;
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 + 4;
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 = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8325
- const offsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
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
- indicatorLegendOffsetX: 10,
3591
- indicatorLegendOffsetY: 10,
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 || compiledSession !== null && sessionOptions.separators !== false;
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 + 6;
8284
- let cursorX = chartLeft + 10;
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 + 4;
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 = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8361
- const offsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
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
- /** Vertical rule at each session start. Default true when a spec is set. */
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
- /** Vertical rule at each session start. Default true when a spec is set. */
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
- indicatorLegendOffsetX: 10,
3555
- indicatorLegendOffsetY: 10,
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 || compiledSession !== null && sessionOptions.separators !== false;
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 + 6;
8248
- let cursorX = chartLeft + 10;
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 + 4;
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 = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8325
- const offsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.150",
3
+ "version": "0.1.152",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",