hyperprop-charting-library 0.1.148 → 0.1.150

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.
@@ -4306,6 +4306,7 @@ function createChart(element, options = {}) {
4306
4306
  let actionDragState = null;
4307
4307
  let pointerDownInfo = null;
4308
4308
  let crosshairPoint = null;
4309
+ let externalCrosshair = null;
4309
4310
  let doubleClickEnabled = mergedOptions.doubleClickEnabled;
4310
4311
  let doubleClickAction = mergedOptions.doubleClickAction;
4311
4312
  setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
@@ -8508,7 +8509,26 @@ function createChart(element, options = {}) {
8508
8509
  crosshairPriceActionRegion = null;
8509
8510
  const layout = overlayLayout;
8510
8511
  const crosshair = resolvedCrosshair;
8511
- if (!layout || !crosshair.visible || !crosshairPoint) {
8512
+ if (!layout) return;
8513
+ if (!crosshairPoint && externalCrosshair !== null && crosshair.visible) {
8514
+ const index = findNearestIndexForTimeMs(externalCrosshair.timeMs);
8515
+ if (index !== null) {
8516
+ const x = layout.chartLeft + (index + 0.5 - layout.xStart) / layout.xSpan * layout.chartWidth;
8517
+ if (x >= layout.chartLeft && x <= layout.chartRight) {
8518
+ ctx.save();
8519
+ ctx.globalAlpha = 0.55;
8520
+ ctx.strokeStyle = crosshair.color;
8521
+ ctx.lineWidth = Math.max(1, crosshair.width);
8522
+ applyDashPattern(crosshair.style, dashPatterns.dotted, dashPatterns.dashed);
8523
+ ctx.beginPath();
8524
+ ctx.moveTo(crisp(x), crisp(layout.chartTop));
8525
+ ctx.lineTo(crisp(x), crisp(layout.fullChartBottom));
8526
+ ctx.stroke();
8527
+ ctx.restore();
8528
+ }
8529
+ }
8530
+ }
8531
+ if (!crosshair.visible || !crosshairPoint) {
8512
8532
  return;
8513
8533
  }
8514
8534
  const { chartLeft, chartTop, chartRight, chartBottom, chartWidth, chartHeight, fullChartBottom, yMin, yRange, xStart, xSpan: xSpan2 } = layout;
@@ -11990,6 +12010,12 @@ function createChart(element, options = {}) {
11990
12010
  const onAlertAction = (handler) => {
11991
12011
  alertActionHandler = handler;
11992
12012
  };
12013
+ const setExternalCrosshair = (point) => {
12014
+ const nextMs = point && Number.isFinite(point.timeMs) ? point.timeMs : null;
12015
+ if (nextMs === (externalCrosshair?.timeMs ?? null)) return;
12016
+ externalCrosshair = nextMs === null ? null : { timeMs: nextMs };
12017
+ scheduleDraw({ overlayOnly: true });
12018
+ };
11993
12019
  const setTimezone = (timezone) => {
11994
12020
  timezoneSetting = timezone || "local";
11995
12021
  zoneClock = createZoneClock(timezoneSetting);
@@ -12300,6 +12326,7 @@ function createChart(element, options = {}) {
12300
12326
  saveState,
12301
12327
  loadState,
12302
12328
  takeScreenshot,
12329
+ setExternalCrosshair,
12303
12330
  setTimezone,
12304
12331
  getTimezone,
12305
12332
  setTimeFormat,
@@ -1030,6 +1030,14 @@ interface ChartInstance {
1030
1030
  * the crosshair tag and day/session boundaries move. Unknown ids fall back
1031
1031
  * to local time rather than throwing.
1032
1032
  */
1033
+ /**
1034
+ * Mirror a sibling chart's crosshair in a multi-chart layout. Matching is
1035
+ * by timestamp, so charts on different intervals or instruments still line
1036
+ * up. Pass null to clear.
1037
+ */
1038
+ setExternalCrosshair: (point: {
1039
+ timeMs: number;
1040
+ } | null) => void;
1033
1041
  setTimezone: (timezone: ChartTimezone) => void;
1034
1042
  getTimezone: () => ChartTimezone;
1035
1043
  /** Switch rendered clock times between 24-hour and 12-hour. */
@@ -4270,6 +4270,7 @@ function createChart(element, options = {}) {
4270
4270
  let actionDragState = null;
4271
4271
  let pointerDownInfo = null;
4272
4272
  let crosshairPoint = null;
4273
+ let externalCrosshair = null;
4273
4274
  let doubleClickEnabled = mergedOptions.doubleClickEnabled;
4274
4275
  let doubleClickAction = mergedOptions.doubleClickAction;
4275
4276
  setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
@@ -8472,7 +8473,26 @@ function createChart(element, options = {}) {
8472
8473
  crosshairPriceActionRegion = null;
8473
8474
  const layout = overlayLayout;
8474
8475
  const crosshair = resolvedCrosshair;
8475
- if (!layout || !crosshair.visible || !crosshairPoint) {
8476
+ if (!layout) return;
8477
+ if (!crosshairPoint && externalCrosshair !== null && crosshair.visible) {
8478
+ const index = findNearestIndexForTimeMs(externalCrosshair.timeMs);
8479
+ if (index !== null) {
8480
+ const x = layout.chartLeft + (index + 0.5 - layout.xStart) / layout.xSpan * layout.chartWidth;
8481
+ if (x >= layout.chartLeft && x <= layout.chartRight) {
8482
+ ctx.save();
8483
+ ctx.globalAlpha = 0.55;
8484
+ ctx.strokeStyle = crosshair.color;
8485
+ ctx.lineWidth = Math.max(1, crosshair.width);
8486
+ applyDashPattern(crosshair.style, dashPatterns.dotted, dashPatterns.dashed);
8487
+ ctx.beginPath();
8488
+ ctx.moveTo(crisp(x), crisp(layout.chartTop));
8489
+ ctx.lineTo(crisp(x), crisp(layout.fullChartBottom));
8490
+ ctx.stroke();
8491
+ ctx.restore();
8492
+ }
8493
+ }
8494
+ }
8495
+ if (!crosshair.visible || !crosshairPoint) {
8476
8496
  return;
8477
8497
  }
8478
8498
  const { chartLeft, chartTop, chartRight, chartBottom, chartWidth, chartHeight, fullChartBottom, yMin, yRange, xStart, xSpan: xSpan2 } = layout;
@@ -11954,6 +11974,12 @@ function createChart(element, options = {}) {
11954
11974
  const onAlertAction = (handler) => {
11955
11975
  alertActionHandler = handler;
11956
11976
  };
11977
+ const setExternalCrosshair = (point) => {
11978
+ const nextMs = point && Number.isFinite(point.timeMs) ? point.timeMs : null;
11979
+ if (nextMs === (externalCrosshair?.timeMs ?? null)) return;
11980
+ externalCrosshair = nextMs === null ? null : { timeMs: nextMs };
11981
+ scheduleDraw({ overlayOnly: true });
11982
+ };
11957
11983
  const setTimezone = (timezone) => {
11958
11984
  timezoneSetting = timezone || "local";
11959
11985
  zoneClock = createZoneClock(timezoneSetting);
@@ -12264,6 +12290,7 @@ function createChart(element, options = {}) {
12264
12290
  saveState,
12265
12291
  loadState,
12266
12292
  takeScreenshot,
12293
+ setExternalCrosshair,
12267
12294
  setTimezone,
12268
12295
  getTimezone,
12269
12296
  setTimeFormat,
package/dist/index.cjs CHANGED
@@ -4306,6 +4306,7 @@ function createChart(element, options = {}) {
4306
4306
  let actionDragState = null;
4307
4307
  let pointerDownInfo = null;
4308
4308
  let crosshairPoint = null;
4309
+ let externalCrosshair = null;
4309
4310
  let doubleClickEnabled = mergedOptions.doubleClickEnabled;
4310
4311
  let doubleClickAction = mergedOptions.doubleClickAction;
4311
4312
  setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
@@ -8508,7 +8509,26 @@ function createChart(element, options = {}) {
8508
8509
  crosshairPriceActionRegion = null;
8509
8510
  const layout = overlayLayout;
8510
8511
  const crosshair = resolvedCrosshair;
8511
- if (!layout || !crosshair.visible || !crosshairPoint) {
8512
+ if (!layout) return;
8513
+ if (!crosshairPoint && externalCrosshair !== null && crosshair.visible) {
8514
+ const index = findNearestIndexForTimeMs(externalCrosshair.timeMs);
8515
+ if (index !== null) {
8516
+ const x = layout.chartLeft + (index + 0.5 - layout.xStart) / layout.xSpan * layout.chartWidth;
8517
+ if (x >= layout.chartLeft && x <= layout.chartRight) {
8518
+ ctx.save();
8519
+ ctx.globalAlpha = 0.55;
8520
+ ctx.strokeStyle = crosshair.color;
8521
+ ctx.lineWidth = Math.max(1, crosshair.width);
8522
+ applyDashPattern(crosshair.style, dashPatterns.dotted, dashPatterns.dashed);
8523
+ ctx.beginPath();
8524
+ ctx.moveTo(crisp(x), crisp(layout.chartTop));
8525
+ ctx.lineTo(crisp(x), crisp(layout.fullChartBottom));
8526
+ ctx.stroke();
8527
+ ctx.restore();
8528
+ }
8529
+ }
8530
+ }
8531
+ if (!crosshair.visible || !crosshairPoint) {
8512
8532
  return;
8513
8533
  }
8514
8534
  const { chartLeft, chartTop, chartRight, chartBottom, chartWidth, chartHeight, fullChartBottom, yMin, yRange, xStart, xSpan: xSpan2 } = layout;
@@ -11990,6 +12010,12 @@ function createChart(element, options = {}) {
11990
12010
  const onAlertAction = (handler) => {
11991
12011
  alertActionHandler = handler;
11992
12012
  };
12013
+ const setExternalCrosshair = (point) => {
12014
+ const nextMs = point && Number.isFinite(point.timeMs) ? point.timeMs : null;
12015
+ if (nextMs === (externalCrosshair?.timeMs ?? null)) return;
12016
+ externalCrosshair = nextMs === null ? null : { timeMs: nextMs };
12017
+ scheduleDraw({ overlayOnly: true });
12018
+ };
11993
12019
  const setTimezone = (timezone) => {
11994
12020
  timezoneSetting = timezone || "local";
11995
12021
  zoneClock = createZoneClock(timezoneSetting);
@@ -12300,6 +12326,7 @@ function createChart(element, options = {}) {
12300
12326
  saveState,
12301
12327
  loadState,
12302
12328
  takeScreenshot,
12329
+ setExternalCrosshair,
12303
12330
  setTimezone,
12304
12331
  getTimezone,
12305
12332
  setTimeFormat,
package/dist/index.d.cts CHANGED
@@ -1030,6 +1030,14 @@ interface ChartInstance {
1030
1030
  * the crosshair tag and day/session boundaries move. Unknown ids fall back
1031
1031
  * to local time rather than throwing.
1032
1032
  */
1033
+ /**
1034
+ * Mirror a sibling chart's crosshair in a multi-chart layout. Matching is
1035
+ * by timestamp, so charts on different intervals or instruments still line
1036
+ * up. Pass null to clear.
1037
+ */
1038
+ setExternalCrosshair: (point: {
1039
+ timeMs: number;
1040
+ } | null) => void;
1033
1041
  setTimezone: (timezone: ChartTimezone) => void;
1034
1042
  getTimezone: () => ChartTimezone;
1035
1043
  /** Switch rendered clock times between 24-hour and 12-hour. */
package/dist/index.d.ts CHANGED
@@ -1030,6 +1030,14 @@ interface ChartInstance {
1030
1030
  * the crosshair tag and day/session boundaries move. Unknown ids fall back
1031
1031
  * to local time rather than throwing.
1032
1032
  */
1033
+ /**
1034
+ * Mirror a sibling chart's crosshair in a multi-chart layout. Matching is
1035
+ * by timestamp, so charts on different intervals or instruments still line
1036
+ * up. Pass null to clear.
1037
+ */
1038
+ setExternalCrosshair: (point: {
1039
+ timeMs: number;
1040
+ } | null) => void;
1033
1041
  setTimezone: (timezone: ChartTimezone) => void;
1034
1042
  getTimezone: () => ChartTimezone;
1035
1043
  /** Switch rendered clock times between 24-hour and 12-hour. */
package/dist/index.js CHANGED
@@ -4270,6 +4270,7 @@ function createChart(element, options = {}) {
4270
4270
  let actionDragState = null;
4271
4271
  let pointerDownInfo = null;
4272
4272
  let crosshairPoint = null;
4273
+ let externalCrosshair = null;
4273
4274
  let doubleClickEnabled = mergedOptions.doubleClickEnabled;
4274
4275
  let doubleClickAction = mergedOptions.doubleClickAction;
4275
4276
  setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
@@ -8472,7 +8473,26 @@ function createChart(element, options = {}) {
8472
8473
  crosshairPriceActionRegion = null;
8473
8474
  const layout = overlayLayout;
8474
8475
  const crosshair = resolvedCrosshair;
8475
- if (!layout || !crosshair.visible || !crosshairPoint) {
8476
+ if (!layout) return;
8477
+ if (!crosshairPoint && externalCrosshair !== null && crosshair.visible) {
8478
+ const index = findNearestIndexForTimeMs(externalCrosshair.timeMs);
8479
+ if (index !== null) {
8480
+ const x = layout.chartLeft + (index + 0.5 - layout.xStart) / layout.xSpan * layout.chartWidth;
8481
+ if (x >= layout.chartLeft && x <= layout.chartRight) {
8482
+ ctx.save();
8483
+ ctx.globalAlpha = 0.55;
8484
+ ctx.strokeStyle = crosshair.color;
8485
+ ctx.lineWidth = Math.max(1, crosshair.width);
8486
+ applyDashPattern(crosshair.style, dashPatterns.dotted, dashPatterns.dashed);
8487
+ ctx.beginPath();
8488
+ ctx.moveTo(crisp(x), crisp(layout.chartTop));
8489
+ ctx.lineTo(crisp(x), crisp(layout.fullChartBottom));
8490
+ ctx.stroke();
8491
+ ctx.restore();
8492
+ }
8493
+ }
8494
+ }
8495
+ if (!crosshair.visible || !crosshairPoint) {
8476
8496
  return;
8477
8497
  }
8478
8498
  const { chartLeft, chartTop, chartRight, chartBottom, chartWidth, chartHeight, fullChartBottom, yMin, yRange, xStart, xSpan: xSpan2 } = layout;
@@ -11954,6 +11974,12 @@ function createChart(element, options = {}) {
11954
11974
  const onAlertAction = (handler) => {
11955
11975
  alertActionHandler = handler;
11956
11976
  };
11977
+ const setExternalCrosshair = (point) => {
11978
+ const nextMs = point && Number.isFinite(point.timeMs) ? point.timeMs : null;
11979
+ if (nextMs === (externalCrosshair?.timeMs ?? null)) return;
11980
+ externalCrosshair = nextMs === null ? null : { timeMs: nextMs };
11981
+ scheduleDraw({ overlayOnly: true });
11982
+ };
11957
11983
  const setTimezone = (timezone) => {
11958
11984
  timezoneSetting = timezone || "local";
11959
11985
  zoneClock = createZoneClock(timezoneSetting);
@@ -12264,6 +12290,7 @@ function createChart(element, options = {}) {
12264
12290
  saveState,
12265
12291
  loadState,
12266
12292
  takeScreenshot,
12293
+ setExternalCrosshair,
12267
12294
  setTimezone,
12268
12295
  getTimezone,
12269
12296
  setTimeFormat,
package/docs/API.md CHANGED
@@ -881,6 +881,28 @@ they are not part of `saveState()`.
881
881
 
882
882
  ---
883
883
 
884
+ ## Overlay indicator legend
885
+
886
+ With `labels.showIndicatorNames` / `showIndicatorValues`, overlay indicators
887
+ are listed top-left, one row each. Hovering a row reveals the same four
888
+ controls the separate panes have — eye, gear, `{}`, X — and they emit the same
889
+ `onIndicatorPaneAction` event, so a host that already handles pane actions gets
890
+ the overlay legend for free:
891
+
892
+ ```ts
893
+ chart.updateOptions({ labels: { showIndicatorNames: true, showIndicatorValues: true } });
894
+ chart.onIndicatorPaneAction(({ id, action }) => {
895
+ if (action === "remove") store.removeIndicator(id);
896
+ if (action === "settings") store.openSettings(id);
897
+ });
898
+ ```
899
+
900
+ The eye also toggles visibility internally so it works without a host, and
901
+ hidden indicators render their row dimmed. Position and offsets come from
902
+ `labels.indicatorLegendPosition` / `indicatorLegendOffsetX` / `OffsetY`.
903
+
904
+ ---
905
+
884
906
  ## In-chart data line
885
907
 
886
908
  The symbol/OHLC line TradingView draws in the top-left of the plot. Hosts have
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.148",
3
+ "version": "0.1.150",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",