hyperprop-charting-library 0.1.156 → 0.1.158

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.
@@ -5439,6 +5439,32 @@ function createChart(element, options = {}) {
5439
5439
  ctx.fill();
5440
5440
  ctx.restore();
5441
5441
  };
5442
+ const drawTrashGlyph = (x, y, size, color) => {
5443
+ const w = size * 0.8;
5444
+ const h = size * 0.9;
5445
+ ctx.save();
5446
+ ctx.strokeStyle = color;
5447
+ ctx.lineWidth = 1.2;
5448
+ ctx.lineCap = "round";
5449
+ ctx.lineJoin = "round";
5450
+ ctx.beginPath();
5451
+ ctx.moveTo(x - w / 2, y - h * 0.28);
5452
+ ctx.lineTo(x + w / 2, y - h * 0.28);
5453
+ ctx.moveTo(x - w * 0.22, y - h * 0.28);
5454
+ ctx.lineTo(x - w * 0.22, y - h * 0.46);
5455
+ ctx.lineTo(x + w * 0.22, y - h * 0.46);
5456
+ ctx.lineTo(x + w * 0.22, y - h * 0.28);
5457
+ ctx.moveTo(x - w * 0.36, y - h * 0.28);
5458
+ ctx.lineTo(x - w * 0.28, y + h * 0.48);
5459
+ ctx.lineTo(x + w * 0.28, y + h * 0.48);
5460
+ ctx.lineTo(x + w * 0.36, y - h * 0.28);
5461
+ ctx.moveTo(x - w * 0.1, y - h * 0.08);
5462
+ ctx.lineTo(x - w * 0.1, y + h * 0.3);
5463
+ ctx.moveTo(x + w * 0.1, y - h * 0.08);
5464
+ ctx.lineTo(x + w * 0.1, y + h * 0.3);
5465
+ ctx.stroke();
5466
+ ctx.restore();
5467
+ };
5442
5468
  const drawAlertLine = (alert, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
5443
5469
  const lineY = getLineY(alert.price, yFromPrice, chartTop, chartBottom, false);
5444
5470
  if (lineY === null) return;
@@ -5457,13 +5483,11 @@ function createChart(element, options = {}) {
5457
5483
  ctx.restore();
5458
5484
  const fontSize = Math.max(8, axis.fontSize);
5459
5485
  ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
5460
- const text = alert.label ? `${formatPrice(alert.price)} ${alert.label}` : formatPrice(alert.price);
5461
5486
  const paddingX = 6;
5462
5487
  const bellWidth = fontSize + 2;
5463
5488
  const labelHeight = 20;
5464
5489
  const hovered = hoveredAlertId === alert.id;
5465
- const removeWidth = hovered ? labelHeight : 0;
5466
- const contentWidth = Math.ceil(measureTextWidth(text)) + bellWidth + paddingX * 3 + removeWidth;
5490
+ const contentWidth = bellWidth + paddingX * 2;
5467
5491
  const labelWidth = getRightAxisLabelWidth(chartRight, contentWidth);
5468
5492
  const labelX = getRightAxisLabelX(chartRight);
5469
5493
  const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
@@ -5473,29 +5497,26 @@ function createChart(element, options = {}) {
5473
5497
  fillRoundedRect(Math.round(labelX), Math.round(labelY), labelWidth, labelHeight, 3);
5474
5498
  const inkColor = labelTextColorOn(color, "#101114");
5475
5499
  drawBellGlyph(labelX + paddingX + bellWidth / 2, labelY + labelHeight / 2, fontSize, inkColor);
5476
- drawText(text, labelX + paddingX * 2 + bellWidth, labelY + labelHeight / 2, "left", "middle", inkColor);
5500
+ ctx.restore();
5477
5501
  if (hovered) {
5478
- const removeX = labelX + labelWidth - removeWidth;
5479
- ctx.strokeStyle = inkColor;
5480
- ctx.lineWidth = 1.4;
5481
- const inset = 6;
5482
- ctx.beginPath();
5483
- ctx.moveTo(removeX + inset, labelY + inset);
5484
- ctx.lineTo(removeX + removeWidth - inset, labelY + labelHeight - inset);
5485
- ctx.moveTo(removeX + removeWidth - inset, labelY + inset);
5486
- ctx.lineTo(removeX + inset, labelY + labelHeight - inset);
5487
- ctx.stroke();
5502
+ const buttonSize = labelHeight;
5503
+ const buttonX = chartLeft + 10;
5504
+ const buttonY = lineY - buttonSize / 2;
5505
+ ctx.save();
5506
+ ctx.fillStyle = color;
5507
+ fillRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, 3);
5508
+ drawTrashGlyph(buttonX + buttonSize / 2, lineY, fontSize, inkColor);
5509
+ ctx.restore();
5488
5510
  alertRegions.push({
5489
5511
  id: alert.id,
5490
- x: removeX,
5491
- y: labelY,
5492
- width: removeWidth,
5493
- height: labelHeight,
5512
+ x: buttonX,
5513
+ y: buttonY,
5514
+ width: buttonSize,
5515
+ height: buttonSize,
5494
5516
  part: "remove"
5495
5517
  });
5496
5518
  }
5497
- ctx.restore();
5498
- alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth - removeWidth, height: labelHeight, part: "line" });
5519
+ alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth, height: labelHeight, part: "line" });
5499
5520
  alertRegions.push({ id: alert.id, x: chartLeft, y: lineY - 5, width: chartRight - chartLeft, height: 10, part: "line" });
5500
5521
  };
5501
5522
  const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
@@ -8480,30 +8501,42 @@ function createChart(element, options = {}) {
8480
8501
  ctx.fill();
8481
8502
  cursorX += dotRadius * 2 + 6;
8482
8503
  }
8483
- const headline = [dataLine.symbol, dataLine.interval, dataLine.exchange].filter((part) => part && part.length > 0).join(" \xB7 ");
8504
+ const headline = [
8505
+ dataLine.symbol,
8506
+ dataLine.description,
8507
+ dataLine.interval,
8508
+ dataLine.exchange,
8509
+ dataLine.chartTypeLabel
8510
+ ].filter((part) => part && part.length > 0).join(" \xB7 ");
8484
8511
  if (headline) {
8485
8512
  ctx.fillStyle = symbolColor;
8486
8513
  ctx.fillText(headline, cursorX, rowY);
8487
- cursorX += measureTextWidth(headline) + 10;
8514
+ cursorX += measureTextWidth(headline) + 12;
8488
8515
  }
8489
8516
  if (dataLine.showOhlc !== false) {
8490
- const ohlc = `O${formatPrice(bar.o)} H${formatPrice(bar.h)} L${formatPrice(bar.l)} C${formatPrice(bar.c)}`;
8491
- ctx.fillStyle = changeColor;
8492
- ctx.fillText(ohlc, cursorX, rowY);
8493
- cursorX += measureTextWidth(ohlc) + 8;
8517
+ const ohlcLabelColor = dataLine.ohlcLabelColor || textColor;
8518
+ const pairs = [
8519
+ ["O", formatPrice(bar.o)],
8520
+ ["H", formatPrice(bar.h)],
8521
+ ["L", formatPrice(bar.l)],
8522
+ ["C", formatPrice(bar.c)]
8523
+ ];
8524
+ for (const [label, value] of pairs) {
8525
+ ctx.fillStyle = ohlcLabelColor;
8526
+ ctx.fillText(label, cursorX, rowY);
8527
+ cursorX += measureTextWidth(label) + 2;
8528
+ ctx.fillStyle = changeColor;
8529
+ ctx.fillText(value, cursorX, rowY);
8530
+ cursorX += measureTextWidth(value) + 9;
8531
+ }
8532
+ cursorX += 3;
8494
8533
  }
8495
8534
  if (dataLine.showChange !== false) {
8496
8535
  const pct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
8497
8536
  const changeText = `${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${pct.toFixed(2)}%)`;
8498
8537
  ctx.fillStyle = changeColor;
8499
8538
  ctx.fillText(changeText, cursorX, rowY);
8500
- cursorX += measureTextWidth(changeText) + 10;
8501
- }
8502
- if (dataLine.showVolume && bar.v !== void 0) {
8503
- const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8504
- ctx.fillStyle = textColor;
8505
- ctx.fillText(volumeText, cursorX, rowY);
8506
- cursorX += measureTextWidth(volumeText) + 10;
8539
+ cursorX += measureTextWidth(changeText) + 12;
8507
8540
  }
8508
8541
  for (const detail of dataLine.details ?? []) {
8509
8542
  const text = detail.label ? `${detail.label} ${detail.value}` : detail.value;
@@ -8517,7 +8550,14 @@ function createChart(element, options = {}) {
8517
8550
  ctx.fillText(text, cursorX, rowY);
8518
8551
  cursorX += width2 + 12;
8519
8552
  }
8520
- dataLineBottom = rowY + fontSize + 6;
8553
+ let lastRowY = rowY;
8554
+ if (dataLine.showVolume && bar.v !== void 0) {
8555
+ const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8556
+ lastRowY = rowY + fontSize + 4;
8557
+ ctx.fillStyle = textColor;
8558
+ ctx.fillText(volumeText, chartLeft + legendInsetX, lastRowY);
8559
+ }
8560
+ dataLineBottom = lastRowY + fontSize + 6;
8521
8561
  ctx.font = prevFont;
8522
8562
  }
8523
8563
  }
@@ -697,15 +697,26 @@ interface DataLineOptions {
697
697
  visible?: boolean;
698
698
  /** e.g. "NQU6" — drawn first, in the emphasis color. */
699
699
  symbol?: string;
700
+ /**
701
+ * Long instrument name, e.g. "NASDAQ 100 E-mini Futures". Drawn after the
702
+ * symbol in the emphasis color, the way other charting platforms label a
703
+ * pane. Omit to keep the headline to the code alone.
704
+ */
705
+ description?: string;
700
706
  /** e.g. "1h". */
701
707
  interval?: string;
702
708
  /** e.g. "CME". */
703
709
  exchange?: string;
710
+ /**
711
+ * Chart style, e.g. "Candles" or "Renko [ATR(14), 1000]" — the last part of
712
+ * the headline, so a non-default style is never a surprise.
713
+ */
714
+ chartTypeLabel?: string;
704
715
  /** O/H/L/C values for the hovered (or latest) bar. Default true. */
705
716
  showOhlc?: boolean;
706
717
  /** Absolute and percent change of that bar, colored by direction. Default true. */
707
718
  showChange?: boolean;
708
- /** Bar volume. Default false. */
719
+ /** Bar volume. Drawn on its own row beneath the values. Default false. */
709
720
  showVolume?: boolean;
710
721
  /**
711
722
  * Extra chips after the values — session, currency, a "Closed" badge. Each
@@ -724,6 +735,12 @@ interface DataLineOptions {
724
735
  symbolColor?: string;
725
736
  /** Everything after the symbol. Defaults to the indicator legend color. */
726
737
  textColor?: string;
738
+ /**
739
+ * Color of the O/H/L/C letters. Values stay colored by direction, so dimming
740
+ * the letters is what makes the numbers readable at a glance rather than one
741
+ * run of same-colored text. Defaults to `textColor`.
742
+ */
743
+ ohlcLabelColor?: string;
727
744
  }
728
745
  interface IndicatorUpdateOptions {
729
746
  /**
@@ -5400,6 +5400,32 @@ function createChart(element, options = {}) {
5400
5400
  ctx.fill();
5401
5401
  ctx.restore();
5402
5402
  };
5403
+ const drawTrashGlyph = (x, y, size, color) => {
5404
+ const w = size * 0.8;
5405
+ const h = size * 0.9;
5406
+ ctx.save();
5407
+ ctx.strokeStyle = color;
5408
+ ctx.lineWidth = 1.2;
5409
+ ctx.lineCap = "round";
5410
+ ctx.lineJoin = "round";
5411
+ ctx.beginPath();
5412
+ ctx.moveTo(x - w / 2, y - h * 0.28);
5413
+ ctx.lineTo(x + w / 2, y - h * 0.28);
5414
+ ctx.moveTo(x - w * 0.22, y - h * 0.28);
5415
+ ctx.lineTo(x - w * 0.22, y - h * 0.46);
5416
+ ctx.lineTo(x + w * 0.22, y - h * 0.46);
5417
+ ctx.lineTo(x + w * 0.22, y - h * 0.28);
5418
+ ctx.moveTo(x - w * 0.36, y - h * 0.28);
5419
+ ctx.lineTo(x - w * 0.28, y + h * 0.48);
5420
+ ctx.lineTo(x + w * 0.28, y + h * 0.48);
5421
+ ctx.lineTo(x + w * 0.36, y - h * 0.28);
5422
+ ctx.moveTo(x - w * 0.1, y - h * 0.08);
5423
+ ctx.lineTo(x - w * 0.1, y + h * 0.3);
5424
+ ctx.moveTo(x + w * 0.1, y - h * 0.08);
5425
+ ctx.lineTo(x + w * 0.1, y + h * 0.3);
5426
+ ctx.stroke();
5427
+ ctx.restore();
5428
+ };
5403
5429
  const drawAlertLine = (alert, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
5404
5430
  const lineY = getLineY(alert.price, yFromPrice, chartTop, chartBottom, false);
5405
5431
  if (lineY === null) return;
@@ -5418,13 +5444,11 @@ function createChart(element, options = {}) {
5418
5444
  ctx.restore();
5419
5445
  const fontSize = Math.max(8, axis.fontSize);
5420
5446
  ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
5421
- const text = alert.label ? `${formatPrice(alert.price)} ${alert.label}` : formatPrice(alert.price);
5422
5447
  const paddingX = 6;
5423
5448
  const bellWidth = fontSize + 2;
5424
5449
  const labelHeight = 20;
5425
5450
  const hovered = hoveredAlertId === alert.id;
5426
- const removeWidth = hovered ? labelHeight : 0;
5427
- const contentWidth = Math.ceil(measureTextWidth(text)) + bellWidth + paddingX * 3 + removeWidth;
5451
+ const contentWidth = bellWidth + paddingX * 2;
5428
5452
  const labelWidth = getRightAxisLabelWidth(chartRight, contentWidth);
5429
5453
  const labelX = getRightAxisLabelX(chartRight);
5430
5454
  const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
@@ -5434,29 +5458,26 @@ function createChart(element, options = {}) {
5434
5458
  fillRoundedRect(Math.round(labelX), Math.round(labelY), labelWidth, labelHeight, 3);
5435
5459
  const inkColor = labelTextColorOn(color, "#101114");
5436
5460
  drawBellGlyph(labelX + paddingX + bellWidth / 2, labelY + labelHeight / 2, fontSize, inkColor);
5437
- drawText(text, labelX + paddingX * 2 + bellWidth, labelY + labelHeight / 2, "left", "middle", inkColor);
5461
+ ctx.restore();
5438
5462
  if (hovered) {
5439
- const removeX = labelX + labelWidth - removeWidth;
5440
- ctx.strokeStyle = inkColor;
5441
- ctx.lineWidth = 1.4;
5442
- const inset = 6;
5443
- ctx.beginPath();
5444
- ctx.moveTo(removeX + inset, labelY + inset);
5445
- ctx.lineTo(removeX + removeWidth - inset, labelY + labelHeight - inset);
5446
- ctx.moveTo(removeX + removeWidth - inset, labelY + inset);
5447
- ctx.lineTo(removeX + inset, labelY + labelHeight - inset);
5448
- ctx.stroke();
5463
+ const buttonSize = labelHeight;
5464
+ const buttonX = chartLeft + 10;
5465
+ const buttonY = lineY - buttonSize / 2;
5466
+ ctx.save();
5467
+ ctx.fillStyle = color;
5468
+ fillRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, 3);
5469
+ drawTrashGlyph(buttonX + buttonSize / 2, lineY, fontSize, inkColor);
5470
+ ctx.restore();
5449
5471
  alertRegions.push({
5450
5472
  id: alert.id,
5451
- x: removeX,
5452
- y: labelY,
5453
- width: removeWidth,
5454
- height: labelHeight,
5473
+ x: buttonX,
5474
+ y: buttonY,
5475
+ width: buttonSize,
5476
+ height: buttonSize,
5455
5477
  part: "remove"
5456
5478
  });
5457
5479
  }
5458
- ctx.restore();
5459
- alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth - removeWidth, height: labelHeight, part: "line" });
5480
+ alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth, height: labelHeight, part: "line" });
5460
5481
  alertRegions.push({ id: alert.id, x: chartLeft, y: lineY - 5, width: chartRight - chartLeft, height: 10, part: "line" });
5461
5482
  };
5462
5483
  const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
@@ -8441,30 +8462,42 @@ function createChart(element, options = {}) {
8441
8462
  ctx.fill();
8442
8463
  cursorX += dotRadius * 2 + 6;
8443
8464
  }
8444
- const headline = [dataLine.symbol, dataLine.interval, dataLine.exchange].filter((part) => part && part.length > 0).join(" \xB7 ");
8465
+ const headline = [
8466
+ dataLine.symbol,
8467
+ dataLine.description,
8468
+ dataLine.interval,
8469
+ dataLine.exchange,
8470
+ dataLine.chartTypeLabel
8471
+ ].filter((part) => part && part.length > 0).join(" \xB7 ");
8445
8472
  if (headline) {
8446
8473
  ctx.fillStyle = symbolColor;
8447
8474
  ctx.fillText(headline, cursorX, rowY);
8448
- cursorX += measureTextWidth(headline) + 10;
8475
+ cursorX += measureTextWidth(headline) + 12;
8449
8476
  }
8450
8477
  if (dataLine.showOhlc !== false) {
8451
- const ohlc = `O${formatPrice(bar.o)} H${formatPrice(bar.h)} L${formatPrice(bar.l)} C${formatPrice(bar.c)}`;
8452
- ctx.fillStyle = changeColor;
8453
- ctx.fillText(ohlc, cursorX, rowY);
8454
- cursorX += measureTextWidth(ohlc) + 8;
8478
+ const ohlcLabelColor = dataLine.ohlcLabelColor || textColor;
8479
+ const pairs = [
8480
+ ["O", formatPrice(bar.o)],
8481
+ ["H", formatPrice(bar.h)],
8482
+ ["L", formatPrice(bar.l)],
8483
+ ["C", formatPrice(bar.c)]
8484
+ ];
8485
+ for (const [label, value] of pairs) {
8486
+ ctx.fillStyle = ohlcLabelColor;
8487
+ ctx.fillText(label, cursorX, rowY);
8488
+ cursorX += measureTextWidth(label) + 2;
8489
+ ctx.fillStyle = changeColor;
8490
+ ctx.fillText(value, cursorX, rowY);
8491
+ cursorX += measureTextWidth(value) + 9;
8492
+ }
8493
+ cursorX += 3;
8455
8494
  }
8456
8495
  if (dataLine.showChange !== false) {
8457
8496
  const pct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
8458
8497
  const changeText = `${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${pct.toFixed(2)}%)`;
8459
8498
  ctx.fillStyle = changeColor;
8460
8499
  ctx.fillText(changeText, cursorX, rowY);
8461
- cursorX += measureTextWidth(changeText) + 10;
8462
- }
8463
- if (dataLine.showVolume && bar.v !== void 0) {
8464
- const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8465
- ctx.fillStyle = textColor;
8466
- ctx.fillText(volumeText, cursorX, rowY);
8467
- cursorX += measureTextWidth(volumeText) + 10;
8500
+ cursorX += measureTextWidth(changeText) + 12;
8468
8501
  }
8469
8502
  for (const detail of dataLine.details ?? []) {
8470
8503
  const text = detail.label ? `${detail.label} ${detail.value}` : detail.value;
@@ -8478,7 +8511,14 @@ function createChart(element, options = {}) {
8478
8511
  ctx.fillText(text, cursorX, rowY);
8479
8512
  cursorX += width2 + 12;
8480
8513
  }
8481
- dataLineBottom = rowY + fontSize + 6;
8514
+ let lastRowY = rowY;
8515
+ if (dataLine.showVolume && bar.v !== void 0) {
8516
+ const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8517
+ lastRowY = rowY + fontSize + 4;
8518
+ ctx.fillStyle = textColor;
8519
+ ctx.fillText(volumeText, chartLeft + legendInsetX, lastRowY);
8520
+ }
8521
+ dataLineBottom = lastRowY + fontSize + 6;
8482
8522
  ctx.font = prevFont;
8483
8523
  }
8484
8524
  }
package/dist/index.cjs CHANGED
@@ -5439,6 +5439,32 @@ function createChart(element, options = {}) {
5439
5439
  ctx.fill();
5440
5440
  ctx.restore();
5441
5441
  };
5442
+ const drawTrashGlyph = (x, y, size, color) => {
5443
+ const w = size * 0.8;
5444
+ const h = size * 0.9;
5445
+ ctx.save();
5446
+ ctx.strokeStyle = color;
5447
+ ctx.lineWidth = 1.2;
5448
+ ctx.lineCap = "round";
5449
+ ctx.lineJoin = "round";
5450
+ ctx.beginPath();
5451
+ ctx.moveTo(x - w / 2, y - h * 0.28);
5452
+ ctx.lineTo(x + w / 2, y - h * 0.28);
5453
+ ctx.moveTo(x - w * 0.22, y - h * 0.28);
5454
+ ctx.lineTo(x - w * 0.22, y - h * 0.46);
5455
+ ctx.lineTo(x + w * 0.22, y - h * 0.46);
5456
+ ctx.lineTo(x + w * 0.22, y - h * 0.28);
5457
+ ctx.moveTo(x - w * 0.36, y - h * 0.28);
5458
+ ctx.lineTo(x - w * 0.28, y + h * 0.48);
5459
+ ctx.lineTo(x + w * 0.28, y + h * 0.48);
5460
+ ctx.lineTo(x + w * 0.36, y - h * 0.28);
5461
+ ctx.moveTo(x - w * 0.1, y - h * 0.08);
5462
+ ctx.lineTo(x - w * 0.1, y + h * 0.3);
5463
+ ctx.moveTo(x + w * 0.1, y - h * 0.08);
5464
+ ctx.lineTo(x + w * 0.1, y + h * 0.3);
5465
+ ctx.stroke();
5466
+ ctx.restore();
5467
+ };
5442
5468
  const drawAlertLine = (alert, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
5443
5469
  const lineY = getLineY(alert.price, yFromPrice, chartTop, chartBottom, false);
5444
5470
  if (lineY === null) return;
@@ -5457,13 +5483,11 @@ function createChart(element, options = {}) {
5457
5483
  ctx.restore();
5458
5484
  const fontSize = Math.max(8, axis.fontSize);
5459
5485
  ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
5460
- const text = alert.label ? `${formatPrice(alert.price)} ${alert.label}` : formatPrice(alert.price);
5461
5486
  const paddingX = 6;
5462
5487
  const bellWidth = fontSize + 2;
5463
5488
  const labelHeight = 20;
5464
5489
  const hovered = hoveredAlertId === alert.id;
5465
- const removeWidth = hovered ? labelHeight : 0;
5466
- const contentWidth = Math.ceil(measureTextWidth(text)) + bellWidth + paddingX * 3 + removeWidth;
5490
+ const contentWidth = bellWidth + paddingX * 2;
5467
5491
  const labelWidth = getRightAxisLabelWidth(chartRight, contentWidth);
5468
5492
  const labelX = getRightAxisLabelX(chartRight);
5469
5493
  const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
@@ -5473,29 +5497,26 @@ function createChart(element, options = {}) {
5473
5497
  fillRoundedRect(Math.round(labelX), Math.round(labelY), labelWidth, labelHeight, 3);
5474
5498
  const inkColor = labelTextColorOn(color, "#101114");
5475
5499
  drawBellGlyph(labelX + paddingX + bellWidth / 2, labelY + labelHeight / 2, fontSize, inkColor);
5476
- drawText(text, labelX + paddingX * 2 + bellWidth, labelY + labelHeight / 2, "left", "middle", inkColor);
5500
+ ctx.restore();
5477
5501
  if (hovered) {
5478
- const removeX = labelX + labelWidth - removeWidth;
5479
- ctx.strokeStyle = inkColor;
5480
- ctx.lineWidth = 1.4;
5481
- const inset = 6;
5482
- ctx.beginPath();
5483
- ctx.moveTo(removeX + inset, labelY + inset);
5484
- ctx.lineTo(removeX + removeWidth - inset, labelY + labelHeight - inset);
5485
- ctx.moveTo(removeX + removeWidth - inset, labelY + inset);
5486
- ctx.lineTo(removeX + inset, labelY + labelHeight - inset);
5487
- ctx.stroke();
5502
+ const buttonSize = labelHeight;
5503
+ const buttonX = chartLeft + 10;
5504
+ const buttonY = lineY - buttonSize / 2;
5505
+ ctx.save();
5506
+ ctx.fillStyle = color;
5507
+ fillRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, 3);
5508
+ drawTrashGlyph(buttonX + buttonSize / 2, lineY, fontSize, inkColor);
5509
+ ctx.restore();
5488
5510
  alertRegions.push({
5489
5511
  id: alert.id,
5490
- x: removeX,
5491
- y: labelY,
5492
- width: removeWidth,
5493
- height: labelHeight,
5512
+ x: buttonX,
5513
+ y: buttonY,
5514
+ width: buttonSize,
5515
+ height: buttonSize,
5494
5516
  part: "remove"
5495
5517
  });
5496
5518
  }
5497
- ctx.restore();
5498
- alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth - removeWidth, height: labelHeight, part: "line" });
5519
+ alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth, height: labelHeight, part: "line" });
5499
5520
  alertRegions.push({ id: alert.id, x: chartLeft, y: lineY - 5, width: chartRight - chartLeft, height: 10, part: "line" });
5500
5521
  };
5501
5522
  const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
@@ -8480,30 +8501,42 @@ function createChart(element, options = {}) {
8480
8501
  ctx.fill();
8481
8502
  cursorX += dotRadius * 2 + 6;
8482
8503
  }
8483
- const headline = [dataLine.symbol, dataLine.interval, dataLine.exchange].filter((part) => part && part.length > 0).join(" \xB7 ");
8504
+ const headline = [
8505
+ dataLine.symbol,
8506
+ dataLine.description,
8507
+ dataLine.interval,
8508
+ dataLine.exchange,
8509
+ dataLine.chartTypeLabel
8510
+ ].filter((part) => part && part.length > 0).join(" \xB7 ");
8484
8511
  if (headline) {
8485
8512
  ctx.fillStyle = symbolColor;
8486
8513
  ctx.fillText(headline, cursorX, rowY);
8487
- cursorX += measureTextWidth(headline) + 10;
8514
+ cursorX += measureTextWidth(headline) + 12;
8488
8515
  }
8489
8516
  if (dataLine.showOhlc !== false) {
8490
- const ohlc = `O${formatPrice(bar.o)} H${formatPrice(bar.h)} L${formatPrice(bar.l)} C${formatPrice(bar.c)}`;
8491
- ctx.fillStyle = changeColor;
8492
- ctx.fillText(ohlc, cursorX, rowY);
8493
- cursorX += measureTextWidth(ohlc) + 8;
8517
+ const ohlcLabelColor = dataLine.ohlcLabelColor || textColor;
8518
+ const pairs = [
8519
+ ["O", formatPrice(bar.o)],
8520
+ ["H", formatPrice(bar.h)],
8521
+ ["L", formatPrice(bar.l)],
8522
+ ["C", formatPrice(bar.c)]
8523
+ ];
8524
+ for (const [label, value] of pairs) {
8525
+ ctx.fillStyle = ohlcLabelColor;
8526
+ ctx.fillText(label, cursorX, rowY);
8527
+ cursorX += measureTextWidth(label) + 2;
8528
+ ctx.fillStyle = changeColor;
8529
+ ctx.fillText(value, cursorX, rowY);
8530
+ cursorX += measureTextWidth(value) + 9;
8531
+ }
8532
+ cursorX += 3;
8494
8533
  }
8495
8534
  if (dataLine.showChange !== false) {
8496
8535
  const pct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
8497
8536
  const changeText = `${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${pct.toFixed(2)}%)`;
8498
8537
  ctx.fillStyle = changeColor;
8499
8538
  ctx.fillText(changeText, cursorX, rowY);
8500
- cursorX += measureTextWidth(changeText) + 10;
8501
- }
8502
- if (dataLine.showVolume && bar.v !== void 0) {
8503
- const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8504
- ctx.fillStyle = textColor;
8505
- ctx.fillText(volumeText, cursorX, rowY);
8506
- cursorX += measureTextWidth(volumeText) + 10;
8539
+ cursorX += measureTextWidth(changeText) + 12;
8507
8540
  }
8508
8541
  for (const detail of dataLine.details ?? []) {
8509
8542
  const text = detail.label ? `${detail.label} ${detail.value}` : detail.value;
@@ -8517,7 +8550,14 @@ function createChart(element, options = {}) {
8517
8550
  ctx.fillText(text, cursorX, rowY);
8518
8551
  cursorX += width2 + 12;
8519
8552
  }
8520
- dataLineBottom = rowY + fontSize + 6;
8553
+ let lastRowY = rowY;
8554
+ if (dataLine.showVolume && bar.v !== void 0) {
8555
+ const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8556
+ lastRowY = rowY + fontSize + 4;
8557
+ ctx.fillStyle = textColor;
8558
+ ctx.fillText(volumeText, chartLeft + legendInsetX, lastRowY);
8559
+ }
8560
+ dataLineBottom = lastRowY + fontSize + 6;
8521
8561
  ctx.font = prevFont;
8522
8562
  }
8523
8563
  }
package/dist/index.d.cts CHANGED
@@ -697,15 +697,26 @@ interface DataLineOptions {
697
697
  visible?: boolean;
698
698
  /** e.g. "NQU6" — drawn first, in the emphasis color. */
699
699
  symbol?: string;
700
+ /**
701
+ * Long instrument name, e.g. "NASDAQ 100 E-mini Futures". Drawn after the
702
+ * symbol in the emphasis color, the way other charting platforms label a
703
+ * pane. Omit to keep the headline to the code alone.
704
+ */
705
+ description?: string;
700
706
  /** e.g. "1h". */
701
707
  interval?: string;
702
708
  /** e.g. "CME". */
703
709
  exchange?: string;
710
+ /**
711
+ * Chart style, e.g. "Candles" or "Renko [ATR(14), 1000]" — the last part of
712
+ * the headline, so a non-default style is never a surprise.
713
+ */
714
+ chartTypeLabel?: string;
704
715
  /** O/H/L/C values for the hovered (or latest) bar. Default true. */
705
716
  showOhlc?: boolean;
706
717
  /** Absolute and percent change of that bar, colored by direction. Default true. */
707
718
  showChange?: boolean;
708
- /** Bar volume. Default false. */
719
+ /** Bar volume. Drawn on its own row beneath the values. Default false. */
709
720
  showVolume?: boolean;
710
721
  /**
711
722
  * Extra chips after the values — session, currency, a "Closed" badge. Each
@@ -724,6 +735,12 @@ interface DataLineOptions {
724
735
  symbolColor?: string;
725
736
  /** Everything after the symbol. Defaults to the indicator legend color. */
726
737
  textColor?: string;
738
+ /**
739
+ * Color of the O/H/L/C letters. Values stay colored by direction, so dimming
740
+ * the letters is what makes the numbers readable at a glance rather than one
741
+ * run of same-colored text. Defaults to `textColor`.
742
+ */
743
+ ohlcLabelColor?: string;
727
744
  }
728
745
  interface IndicatorUpdateOptions {
729
746
  /**
package/dist/index.d.ts CHANGED
@@ -697,15 +697,26 @@ interface DataLineOptions {
697
697
  visible?: boolean;
698
698
  /** e.g. "NQU6" — drawn first, in the emphasis color. */
699
699
  symbol?: string;
700
+ /**
701
+ * Long instrument name, e.g. "NASDAQ 100 E-mini Futures". Drawn after the
702
+ * symbol in the emphasis color, the way other charting platforms label a
703
+ * pane. Omit to keep the headline to the code alone.
704
+ */
705
+ description?: string;
700
706
  /** e.g. "1h". */
701
707
  interval?: string;
702
708
  /** e.g. "CME". */
703
709
  exchange?: string;
710
+ /**
711
+ * Chart style, e.g. "Candles" or "Renko [ATR(14), 1000]" — the last part of
712
+ * the headline, so a non-default style is never a surprise.
713
+ */
714
+ chartTypeLabel?: string;
704
715
  /** O/H/L/C values for the hovered (or latest) bar. Default true. */
705
716
  showOhlc?: boolean;
706
717
  /** Absolute and percent change of that bar, colored by direction. Default true. */
707
718
  showChange?: boolean;
708
- /** Bar volume. Default false. */
719
+ /** Bar volume. Drawn on its own row beneath the values. Default false. */
709
720
  showVolume?: boolean;
710
721
  /**
711
722
  * Extra chips after the values — session, currency, a "Closed" badge. Each
@@ -724,6 +735,12 @@ interface DataLineOptions {
724
735
  symbolColor?: string;
725
736
  /** Everything after the symbol. Defaults to the indicator legend color. */
726
737
  textColor?: string;
738
+ /**
739
+ * Color of the O/H/L/C letters. Values stay colored by direction, so dimming
740
+ * the letters is what makes the numbers readable at a glance rather than one
741
+ * run of same-colored text. Defaults to `textColor`.
742
+ */
743
+ ohlcLabelColor?: string;
727
744
  }
728
745
  interface IndicatorUpdateOptions {
729
746
  /**
package/dist/index.js CHANGED
@@ -5400,6 +5400,32 @@ function createChart(element, options = {}) {
5400
5400
  ctx.fill();
5401
5401
  ctx.restore();
5402
5402
  };
5403
+ const drawTrashGlyph = (x, y, size, color) => {
5404
+ const w = size * 0.8;
5405
+ const h = size * 0.9;
5406
+ ctx.save();
5407
+ ctx.strokeStyle = color;
5408
+ ctx.lineWidth = 1.2;
5409
+ ctx.lineCap = "round";
5410
+ ctx.lineJoin = "round";
5411
+ ctx.beginPath();
5412
+ ctx.moveTo(x - w / 2, y - h * 0.28);
5413
+ ctx.lineTo(x + w / 2, y - h * 0.28);
5414
+ ctx.moveTo(x - w * 0.22, y - h * 0.28);
5415
+ ctx.lineTo(x - w * 0.22, y - h * 0.46);
5416
+ ctx.lineTo(x + w * 0.22, y - h * 0.46);
5417
+ ctx.lineTo(x + w * 0.22, y - h * 0.28);
5418
+ ctx.moveTo(x - w * 0.36, y - h * 0.28);
5419
+ ctx.lineTo(x - w * 0.28, y + h * 0.48);
5420
+ ctx.lineTo(x + w * 0.28, y + h * 0.48);
5421
+ ctx.lineTo(x + w * 0.36, y - h * 0.28);
5422
+ ctx.moveTo(x - w * 0.1, y - h * 0.08);
5423
+ ctx.lineTo(x - w * 0.1, y + h * 0.3);
5424
+ ctx.moveTo(x + w * 0.1, y - h * 0.08);
5425
+ ctx.lineTo(x + w * 0.1, y + h * 0.3);
5426
+ ctx.stroke();
5427
+ ctx.restore();
5428
+ };
5403
5429
  const drawAlertLine = (alert, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
5404
5430
  const lineY = getLineY(alert.price, yFromPrice, chartTop, chartBottom, false);
5405
5431
  if (lineY === null) return;
@@ -5418,13 +5444,11 @@ function createChart(element, options = {}) {
5418
5444
  ctx.restore();
5419
5445
  const fontSize = Math.max(8, axis.fontSize);
5420
5446
  ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
5421
- const text = alert.label ? `${formatPrice(alert.price)} ${alert.label}` : formatPrice(alert.price);
5422
5447
  const paddingX = 6;
5423
5448
  const bellWidth = fontSize + 2;
5424
5449
  const labelHeight = 20;
5425
5450
  const hovered = hoveredAlertId === alert.id;
5426
- const removeWidth = hovered ? labelHeight : 0;
5427
- const contentWidth = Math.ceil(measureTextWidth(text)) + bellWidth + paddingX * 3 + removeWidth;
5451
+ const contentWidth = bellWidth + paddingX * 2;
5428
5452
  const labelWidth = getRightAxisLabelWidth(chartRight, contentWidth);
5429
5453
  const labelX = getRightAxisLabelX(chartRight);
5430
5454
  const labelY = placeRightAxisLabel(lineY - labelHeight / 2, labelHeight, chartTop, chartBottom - labelHeight);
@@ -5434,29 +5458,26 @@ function createChart(element, options = {}) {
5434
5458
  fillRoundedRect(Math.round(labelX), Math.round(labelY), labelWidth, labelHeight, 3);
5435
5459
  const inkColor = labelTextColorOn(color, "#101114");
5436
5460
  drawBellGlyph(labelX + paddingX + bellWidth / 2, labelY + labelHeight / 2, fontSize, inkColor);
5437
- drawText(text, labelX + paddingX * 2 + bellWidth, labelY + labelHeight / 2, "left", "middle", inkColor);
5461
+ ctx.restore();
5438
5462
  if (hovered) {
5439
- const removeX = labelX + labelWidth - removeWidth;
5440
- ctx.strokeStyle = inkColor;
5441
- ctx.lineWidth = 1.4;
5442
- const inset = 6;
5443
- ctx.beginPath();
5444
- ctx.moveTo(removeX + inset, labelY + inset);
5445
- ctx.lineTo(removeX + removeWidth - inset, labelY + labelHeight - inset);
5446
- ctx.moveTo(removeX + removeWidth - inset, labelY + inset);
5447
- ctx.lineTo(removeX + inset, labelY + labelHeight - inset);
5448
- ctx.stroke();
5463
+ const buttonSize = labelHeight;
5464
+ const buttonX = chartLeft + 10;
5465
+ const buttonY = lineY - buttonSize / 2;
5466
+ ctx.save();
5467
+ ctx.fillStyle = color;
5468
+ fillRoundedRect(Math.round(buttonX), Math.round(buttonY), buttonSize, buttonSize, 3);
5469
+ drawTrashGlyph(buttonX + buttonSize / 2, lineY, fontSize, inkColor);
5470
+ ctx.restore();
5449
5471
  alertRegions.push({
5450
5472
  id: alert.id,
5451
- x: removeX,
5452
- y: labelY,
5453
- width: removeWidth,
5454
- height: labelHeight,
5473
+ x: buttonX,
5474
+ y: buttonY,
5475
+ width: buttonSize,
5476
+ height: buttonSize,
5455
5477
  part: "remove"
5456
5478
  });
5457
5479
  }
5458
- ctx.restore();
5459
- alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth - removeWidth, height: labelHeight, part: "line" });
5480
+ alertRegions.push({ id: alert.id, x: labelX, y: labelY, width: labelWidth, height: labelHeight, part: "line" });
5460
5481
  alertRegions.push({ id: alert.id, x: chartLeft, y: lineY - 5, width: chartRight - chartLeft, height: 10, part: "line" });
5461
5482
  };
5462
5483
  const drawOrderLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
@@ -8441,30 +8462,42 @@ function createChart(element, options = {}) {
8441
8462
  ctx.fill();
8442
8463
  cursorX += dotRadius * 2 + 6;
8443
8464
  }
8444
- const headline = [dataLine.symbol, dataLine.interval, dataLine.exchange].filter((part) => part && part.length > 0).join(" \xB7 ");
8465
+ const headline = [
8466
+ dataLine.symbol,
8467
+ dataLine.description,
8468
+ dataLine.interval,
8469
+ dataLine.exchange,
8470
+ dataLine.chartTypeLabel
8471
+ ].filter((part) => part && part.length > 0).join(" \xB7 ");
8445
8472
  if (headline) {
8446
8473
  ctx.fillStyle = symbolColor;
8447
8474
  ctx.fillText(headline, cursorX, rowY);
8448
- cursorX += measureTextWidth(headline) + 10;
8475
+ cursorX += measureTextWidth(headline) + 12;
8449
8476
  }
8450
8477
  if (dataLine.showOhlc !== false) {
8451
- const ohlc = `O${formatPrice(bar.o)} H${formatPrice(bar.h)} L${formatPrice(bar.l)} C${formatPrice(bar.c)}`;
8452
- ctx.fillStyle = changeColor;
8453
- ctx.fillText(ohlc, cursorX, rowY);
8454
- cursorX += measureTextWidth(ohlc) + 8;
8478
+ const ohlcLabelColor = dataLine.ohlcLabelColor || textColor;
8479
+ const pairs = [
8480
+ ["O", formatPrice(bar.o)],
8481
+ ["H", formatPrice(bar.h)],
8482
+ ["L", formatPrice(bar.l)],
8483
+ ["C", formatPrice(bar.c)]
8484
+ ];
8485
+ for (const [label, value] of pairs) {
8486
+ ctx.fillStyle = ohlcLabelColor;
8487
+ ctx.fillText(label, cursorX, rowY);
8488
+ cursorX += measureTextWidth(label) + 2;
8489
+ ctx.fillStyle = changeColor;
8490
+ ctx.fillText(value, cursorX, rowY);
8491
+ cursorX += measureTextWidth(value) + 9;
8492
+ }
8493
+ cursorX += 3;
8455
8494
  }
8456
8495
  if (dataLine.showChange !== false) {
8457
8496
  const pct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
8458
8497
  const changeText = `${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${pct.toFixed(2)}%)`;
8459
8498
  ctx.fillStyle = changeColor;
8460
8499
  ctx.fillText(changeText, cursorX, rowY);
8461
- cursorX += measureTextWidth(changeText) + 10;
8462
- }
8463
- if (dataLine.showVolume && bar.v !== void 0) {
8464
- const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8465
- ctx.fillStyle = textColor;
8466
- ctx.fillText(volumeText, cursorX, rowY);
8467
- cursorX += measureTextWidth(volumeText) + 10;
8500
+ cursorX += measureTextWidth(changeText) + 12;
8468
8501
  }
8469
8502
  for (const detail of dataLine.details ?? []) {
8470
8503
  const text = detail.label ? `${detail.label} ${detail.value}` : detail.value;
@@ -8478,7 +8511,14 @@ function createChart(element, options = {}) {
8478
8511
  ctx.fillText(text, cursorX, rowY);
8479
8512
  cursorX += width2 + 12;
8480
8513
  }
8481
- dataLineBottom = rowY + fontSize + 6;
8514
+ let lastRowY = rowY;
8515
+ if (dataLine.showVolume && bar.v !== void 0) {
8516
+ const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
8517
+ lastRowY = rowY + fontSize + 4;
8518
+ ctx.fillStyle = textColor;
8519
+ ctx.fillText(volumeText, chartLeft + legendInsetX, lastRowY);
8520
+ }
8521
+ dataLineBottom = lastRowY + fontSize + 6;
8482
8522
  ctx.font = prevFont;
8483
8523
  }
8484
8524
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.156",
3
+ "version": "0.1.158",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",