hyperprop-charting-library 0.1.151 → 0.1.153

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",
@@ -6287,6 +6289,8 @@ function createChart(element, options = {}) {
6287
6289
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
6288
6290
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
6289
6291
  const move = p1.price - p0.price;
6292
+ const lineLeft = Math.min(x0, x1, x2);
6293
+ const lineRight = Math.max(x0, x1, x2);
6290
6294
  const levelLines = FIB_EXT_RATIOS.map((ratio) => {
6291
6295
  const price = p2.price + move * ratio;
6292
6296
  return { ratio, price, y: yFromPrice(price) };
@@ -6299,7 +6303,7 @@ function createChart(element, options = {}) {
6299
6303
  ctx.save();
6300
6304
  ctx.fillStyle = hexToRgba(levelColorAt(index), 0.1);
6301
6305
  ctx.globalAlpha = draft ? 0.5 : 1;
6302
- ctx.fillRect(chartLeft, bandTop, chartRight - chartLeft, bandBottom - bandTop);
6306
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
6303
6307
  ctx.restore();
6304
6308
  }
6305
6309
  ctx.save();
@@ -6308,8 +6312,8 @@ function createChart(element, options = {}) {
6308
6312
  levelLines.forEach((level, index) => {
6309
6313
  ctx.strokeStyle = levelColorAt(index);
6310
6314
  ctx.beginPath();
6311
- ctx.moveTo(crisp(chartLeft), crisp(level.y));
6312
- ctx.lineTo(crisp(chartRight), crisp(level.y));
6315
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
6316
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
6313
6317
  ctx.stroke();
6314
6318
  });
6315
6319
  ctx.restore();
@@ -6319,7 +6323,7 @@ function createChart(element, options = {}) {
6319
6323
  const labelText = `${level.ratio} (${formatPrice(level.price)})`;
6320
6324
  const textWidth = measureTextWidth(labelText);
6321
6325
  const padding = 4;
6322
- const bgX = chartLeft + 4;
6326
+ const bgX = Math.max(chartLeft + 2, lineLeft - textWidth - padding * 2 - 4);
6323
6327
  const bgY = level.y - 9;
6324
6328
  ctx.fillStyle = mergedOptions.backgroundColor;
6325
6329
  fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
@@ -8264,6 +8268,8 @@ function createChart(element, options = {}) {
8264
8268
  for (const orderLine of orderLines) {
8265
8269
  drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
8266
8270
  }
8271
+ const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8272
+ const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
8267
8273
  let dataLineBottom = chartTop;
8268
8274
  const dataLine = mergedOptions.dataLine;
8269
8275
  if (dataLine?.visible && data.length > 0) {
@@ -8280,8 +8286,8 @@ function createChart(element, options = {}) {
8280
8286
  const textColor = dataLine.textColor || labels.indicatorTextColor;
8281
8287
  const changeAbs = bar.c - bar.o;
8282
8288
  const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
8283
- const rowY = chartTop + 6;
8284
- let cursorX = chartLeft + 10;
8289
+ const rowY = chartTop + legendInsetY;
8290
+ let cursorX = chartLeft + legendInsetX;
8285
8291
  if (dataLine.statusColor) {
8286
8292
  const dotRadius = Math.max(2.5, fontSize / 4);
8287
8293
  ctx.fillStyle = dataLine.statusColor;
@@ -8327,7 +8333,7 @@ function createChart(element, options = {}) {
8327
8333
  ctx.fillText(text, cursorX, rowY);
8328
8334
  cursorX += width2 + 12;
8329
8335
  }
8330
- dataLineBottom = rowY + fontSize + 4;
8336
+ dataLineBottom = rowY + fontSize + 6;
8331
8337
  ctx.font = prevFont;
8332
8338
  }
8333
8339
  }
@@ -8357,8 +8363,8 @@ function createChart(element, options = {}) {
8357
8363
  const prevFont = ctx.font;
8358
8364
  const legendFontSize = Math.max(8, axis.fontSize);
8359
8365
  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);
8366
+ const offsetX = legendInsetX;
8367
+ const offsetY = legendInsetY;
8362
8368
  const position = labels.indicatorLegendPosition;
8363
8369
  const isRight = position === "top-right" || position === "bottom-right";
8364
8370
  const isBottom = position === "bottom-left" || position === "bottom-right";
@@ -9383,12 +9389,16 @@ function createChart(element, options = {}) {
9383
9389
  if (Math.hypot(x - x2, y - y2) <= 9 * hitTolerance) {
9384
9390
  return { drawing, target: "handle", pointIndex: 1 };
9385
9391
  }
9386
- const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9387
- for (const ratio of fibRatios) {
9388
- const price = first.price + (second.price - first.price) * (1 - ratio);
9389
- const lineY = canvasYFromDrawingPrice(price);
9390
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9391
- return { drawing, target: "line" };
9392
+ const fibLeft = Math.min(x1, x2) - 5 * hitTolerance;
9393
+ const fibRight = Math.max(x1, x2) + 5 * hitTolerance;
9394
+ if (x >= fibLeft && x <= fibRight) {
9395
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9396
+ for (const ratio of fibRatios) {
9397
+ const price = first.price + (second.price - first.price) * (1 - ratio);
9398
+ const lineY = canvasYFromDrawingPrice(price);
9399
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9400
+ return { drawing, target: "line" };
9401
+ }
9392
9402
  }
9393
9403
  }
9394
9404
  } else if (drawing.type === "fib-extension") {
@@ -9413,11 +9423,15 @@ function createChart(element, options = {}) {
9413
9423
  return { drawing, target: "handle", pointIndex: 2 };
9414
9424
  }
9415
9425
  const move = p1.price - p0.price;
9416
- const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9417
- for (const ratio of fibExtRatios) {
9418
- const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9419
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9420
- return { drawing, target: "line" };
9426
+ const extLeft = Math.min(x0, x1, x2) - 5 * hitTolerance;
9427
+ const extRight = Math.max(x0, x1, x2) + 5 * hitTolerance;
9428
+ if (x >= extLeft && x <= extRight) {
9429
+ const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9430
+ for (const ratio of fibExtRatios) {
9431
+ const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9432
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9433
+ return { drawing, target: "line" };
9434
+ }
9421
9435
  }
9422
9436
  }
9423
9437
  if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
@@ -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;
@@ -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",
@@ -6251,6 +6253,8 @@ function createChart(element, options = {}) {
6251
6253
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
6252
6254
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
6253
6255
  const move = p1.price - p0.price;
6256
+ const lineLeft = Math.min(x0, x1, x2);
6257
+ const lineRight = Math.max(x0, x1, x2);
6254
6258
  const levelLines = FIB_EXT_RATIOS.map((ratio) => {
6255
6259
  const price = p2.price + move * ratio;
6256
6260
  return { ratio, price, y: yFromPrice(price) };
@@ -6263,7 +6267,7 @@ function createChart(element, options = {}) {
6263
6267
  ctx.save();
6264
6268
  ctx.fillStyle = hexToRgba(levelColorAt(index), 0.1);
6265
6269
  ctx.globalAlpha = draft ? 0.5 : 1;
6266
- ctx.fillRect(chartLeft, bandTop, chartRight - chartLeft, bandBottom - bandTop);
6270
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
6267
6271
  ctx.restore();
6268
6272
  }
6269
6273
  ctx.save();
@@ -6272,8 +6276,8 @@ function createChart(element, options = {}) {
6272
6276
  levelLines.forEach((level, index) => {
6273
6277
  ctx.strokeStyle = levelColorAt(index);
6274
6278
  ctx.beginPath();
6275
- ctx.moveTo(crisp(chartLeft), crisp(level.y));
6276
- ctx.lineTo(crisp(chartRight), crisp(level.y));
6279
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
6280
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
6277
6281
  ctx.stroke();
6278
6282
  });
6279
6283
  ctx.restore();
@@ -6283,7 +6287,7 @@ function createChart(element, options = {}) {
6283
6287
  const labelText = `${level.ratio} (${formatPrice(level.price)})`;
6284
6288
  const textWidth = measureTextWidth(labelText);
6285
6289
  const padding = 4;
6286
- const bgX = chartLeft + 4;
6290
+ const bgX = Math.max(chartLeft + 2, lineLeft - textWidth - padding * 2 - 4);
6287
6291
  const bgY = level.y - 9;
6288
6292
  ctx.fillStyle = mergedOptions.backgroundColor;
6289
6293
  fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
@@ -8228,6 +8232,8 @@ function createChart(element, options = {}) {
8228
8232
  for (const orderLine of orderLines) {
8229
8233
  drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
8230
8234
  }
8235
+ const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8236
+ const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
8231
8237
  let dataLineBottom = chartTop;
8232
8238
  const dataLine = mergedOptions.dataLine;
8233
8239
  if (dataLine?.visible && data.length > 0) {
@@ -8244,8 +8250,8 @@ function createChart(element, options = {}) {
8244
8250
  const textColor = dataLine.textColor || labels.indicatorTextColor;
8245
8251
  const changeAbs = bar.c - bar.o;
8246
8252
  const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
8247
- const rowY = chartTop + 6;
8248
- let cursorX = chartLeft + 10;
8253
+ const rowY = chartTop + legendInsetY;
8254
+ let cursorX = chartLeft + legendInsetX;
8249
8255
  if (dataLine.statusColor) {
8250
8256
  const dotRadius = Math.max(2.5, fontSize / 4);
8251
8257
  ctx.fillStyle = dataLine.statusColor;
@@ -8291,7 +8297,7 @@ function createChart(element, options = {}) {
8291
8297
  ctx.fillText(text, cursorX, rowY);
8292
8298
  cursorX += width2 + 12;
8293
8299
  }
8294
- dataLineBottom = rowY + fontSize + 4;
8300
+ dataLineBottom = rowY + fontSize + 6;
8295
8301
  ctx.font = prevFont;
8296
8302
  }
8297
8303
  }
@@ -8321,8 +8327,8 @@ function createChart(element, options = {}) {
8321
8327
  const prevFont = ctx.font;
8322
8328
  const legendFontSize = Math.max(8, axis.fontSize);
8323
8329
  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);
8330
+ const offsetX = legendInsetX;
8331
+ const offsetY = legendInsetY;
8326
8332
  const position = labels.indicatorLegendPosition;
8327
8333
  const isRight = position === "top-right" || position === "bottom-right";
8328
8334
  const isBottom = position === "bottom-left" || position === "bottom-right";
@@ -9347,12 +9353,16 @@ function createChart(element, options = {}) {
9347
9353
  if (Math.hypot(x - x2, y - y2) <= 9 * hitTolerance) {
9348
9354
  return { drawing, target: "handle", pointIndex: 1 };
9349
9355
  }
9350
- const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9351
- for (const ratio of fibRatios) {
9352
- const price = first.price + (second.price - first.price) * (1 - ratio);
9353
- const lineY = canvasYFromDrawingPrice(price);
9354
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9355
- return { drawing, target: "line" };
9356
+ const fibLeft = Math.min(x1, x2) - 5 * hitTolerance;
9357
+ const fibRight = Math.max(x1, x2) + 5 * hitTolerance;
9358
+ if (x >= fibLeft && x <= fibRight) {
9359
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9360
+ for (const ratio of fibRatios) {
9361
+ const price = first.price + (second.price - first.price) * (1 - ratio);
9362
+ const lineY = canvasYFromDrawingPrice(price);
9363
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9364
+ return { drawing, target: "line" };
9365
+ }
9356
9366
  }
9357
9367
  }
9358
9368
  } else if (drawing.type === "fib-extension") {
@@ -9377,11 +9387,15 @@ function createChart(element, options = {}) {
9377
9387
  return { drawing, target: "handle", pointIndex: 2 };
9378
9388
  }
9379
9389
  const move = p1.price - p0.price;
9380
- const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9381
- for (const ratio of fibExtRatios) {
9382
- const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9383
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9384
- return { drawing, target: "line" };
9390
+ const extLeft = Math.min(x0, x1, x2) - 5 * hitTolerance;
9391
+ const extRight = Math.max(x0, x1, x2) + 5 * hitTolerance;
9392
+ if (x >= extLeft && x <= extRight) {
9393
+ const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9394
+ for (const ratio of fibExtRatios) {
9395
+ const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9396
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9397
+ return { drawing, target: "line" };
9398
+ }
9385
9399
  }
9386
9400
  }
9387
9401
  if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
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",
@@ -6287,6 +6289,8 @@ function createChart(element, options = {}) {
6287
6289
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
6288
6290
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
6289
6291
  const move = p1.price - p0.price;
6292
+ const lineLeft = Math.min(x0, x1, x2);
6293
+ const lineRight = Math.max(x0, x1, x2);
6290
6294
  const levelLines = FIB_EXT_RATIOS.map((ratio) => {
6291
6295
  const price = p2.price + move * ratio;
6292
6296
  return { ratio, price, y: yFromPrice(price) };
@@ -6299,7 +6303,7 @@ function createChart(element, options = {}) {
6299
6303
  ctx.save();
6300
6304
  ctx.fillStyle = hexToRgba(levelColorAt(index), 0.1);
6301
6305
  ctx.globalAlpha = draft ? 0.5 : 1;
6302
- ctx.fillRect(chartLeft, bandTop, chartRight - chartLeft, bandBottom - bandTop);
6306
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
6303
6307
  ctx.restore();
6304
6308
  }
6305
6309
  ctx.save();
@@ -6308,8 +6312,8 @@ function createChart(element, options = {}) {
6308
6312
  levelLines.forEach((level, index) => {
6309
6313
  ctx.strokeStyle = levelColorAt(index);
6310
6314
  ctx.beginPath();
6311
- ctx.moveTo(crisp(chartLeft), crisp(level.y));
6312
- ctx.lineTo(crisp(chartRight), crisp(level.y));
6315
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
6316
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
6313
6317
  ctx.stroke();
6314
6318
  });
6315
6319
  ctx.restore();
@@ -6319,7 +6323,7 @@ function createChart(element, options = {}) {
6319
6323
  const labelText = `${level.ratio} (${formatPrice(level.price)})`;
6320
6324
  const textWidth = measureTextWidth(labelText);
6321
6325
  const padding = 4;
6322
- const bgX = chartLeft + 4;
6326
+ const bgX = Math.max(chartLeft + 2, lineLeft - textWidth - padding * 2 - 4);
6323
6327
  const bgY = level.y - 9;
6324
6328
  ctx.fillStyle = mergedOptions.backgroundColor;
6325
6329
  fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
@@ -8264,6 +8268,8 @@ function createChart(element, options = {}) {
8264
8268
  for (const orderLine of orderLines) {
8265
8269
  drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
8266
8270
  }
8271
+ const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8272
+ const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
8267
8273
  let dataLineBottom = chartTop;
8268
8274
  const dataLine = mergedOptions.dataLine;
8269
8275
  if (dataLine?.visible && data.length > 0) {
@@ -8280,8 +8286,8 @@ function createChart(element, options = {}) {
8280
8286
  const textColor = dataLine.textColor || labels.indicatorTextColor;
8281
8287
  const changeAbs = bar.c - bar.o;
8282
8288
  const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
8283
- const rowY = chartTop + 6;
8284
- let cursorX = chartLeft + 10;
8289
+ const rowY = chartTop + legendInsetY;
8290
+ let cursorX = chartLeft + legendInsetX;
8285
8291
  if (dataLine.statusColor) {
8286
8292
  const dotRadius = Math.max(2.5, fontSize / 4);
8287
8293
  ctx.fillStyle = dataLine.statusColor;
@@ -8327,7 +8333,7 @@ function createChart(element, options = {}) {
8327
8333
  ctx.fillText(text, cursorX, rowY);
8328
8334
  cursorX += width2 + 12;
8329
8335
  }
8330
- dataLineBottom = rowY + fontSize + 4;
8336
+ dataLineBottom = rowY + fontSize + 6;
8331
8337
  ctx.font = prevFont;
8332
8338
  }
8333
8339
  }
@@ -8357,8 +8363,8 @@ function createChart(element, options = {}) {
8357
8363
  const prevFont = ctx.font;
8358
8364
  const legendFontSize = Math.max(8, axis.fontSize);
8359
8365
  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);
8366
+ const offsetX = legendInsetX;
8367
+ const offsetY = legendInsetY;
8362
8368
  const position = labels.indicatorLegendPosition;
8363
8369
  const isRight = position === "top-right" || position === "bottom-right";
8364
8370
  const isBottom = position === "bottom-left" || position === "bottom-right";
@@ -9383,12 +9389,16 @@ function createChart(element, options = {}) {
9383
9389
  if (Math.hypot(x - x2, y - y2) <= 9 * hitTolerance) {
9384
9390
  return { drawing, target: "handle", pointIndex: 1 };
9385
9391
  }
9386
- const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9387
- for (const ratio of fibRatios) {
9388
- const price = first.price + (second.price - first.price) * (1 - ratio);
9389
- const lineY = canvasYFromDrawingPrice(price);
9390
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9391
- return { drawing, target: "line" };
9392
+ const fibLeft = Math.min(x1, x2) - 5 * hitTolerance;
9393
+ const fibRight = Math.max(x1, x2) + 5 * hitTolerance;
9394
+ if (x >= fibLeft && x <= fibRight) {
9395
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9396
+ for (const ratio of fibRatios) {
9397
+ const price = first.price + (second.price - first.price) * (1 - ratio);
9398
+ const lineY = canvasYFromDrawingPrice(price);
9399
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9400
+ return { drawing, target: "line" };
9401
+ }
9392
9402
  }
9393
9403
  }
9394
9404
  } else if (drawing.type === "fib-extension") {
@@ -9413,11 +9423,15 @@ function createChart(element, options = {}) {
9413
9423
  return { drawing, target: "handle", pointIndex: 2 };
9414
9424
  }
9415
9425
  const move = p1.price - p0.price;
9416
- const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9417
- for (const ratio of fibExtRatios) {
9418
- const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9419
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9420
- return { drawing, target: "line" };
9426
+ const extLeft = Math.min(x0, x1, x2) - 5 * hitTolerance;
9427
+ const extRight = Math.max(x0, x1, x2) + 5 * hitTolerance;
9428
+ if (x >= extLeft && x <= extRight) {
9429
+ const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9430
+ for (const ratio of fibExtRatios) {
9431
+ const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9432
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9433
+ return { drawing, target: "line" };
9434
+ }
9421
9435
  }
9422
9436
  }
9423
9437
  if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
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;
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;
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",
@@ -6251,6 +6253,8 @@ function createChart(element, options = {}) {
6251
6253
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
6252
6254
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
6253
6255
  const move = p1.price - p0.price;
6256
+ const lineLeft = Math.min(x0, x1, x2);
6257
+ const lineRight = Math.max(x0, x1, x2);
6254
6258
  const levelLines = FIB_EXT_RATIOS.map((ratio) => {
6255
6259
  const price = p2.price + move * ratio;
6256
6260
  return { ratio, price, y: yFromPrice(price) };
@@ -6263,7 +6267,7 @@ function createChart(element, options = {}) {
6263
6267
  ctx.save();
6264
6268
  ctx.fillStyle = hexToRgba(levelColorAt(index), 0.1);
6265
6269
  ctx.globalAlpha = draft ? 0.5 : 1;
6266
- ctx.fillRect(chartLeft, bandTop, chartRight - chartLeft, bandBottom - bandTop);
6270
+ ctx.fillRect(lineLeft, bandTop, lineRight - lineLeft, bandBottom - bandTop);
6267
6271
  ctx.restore();
6268
6272
  }
6269
6273
  ctx.save();
@@ -6272,8 +6276,8 @@ function createChart(element, options = {}) {
6272
6276
  levelLines.forEach((level, index) => {
6273
6277
  ctx.strokeStyle = levelColorAt(index);
6274
6278
  ctx.beginPath();
6275
- ctx.moveTo(crisp(chartLeft), crisp(level.y));
6276
- ctx.lineTo(crisp(chartRight), crisp(level.y));
6279
+ ctx.moveTo(crisp(lineLeft), crisp(level.y));
6280
+ ctx.lineTo(crisp(lineRight), crisp(level.y));
6277
6281
  ctx.stroke();
6278
6282
  });
6279
6283
  ctx.restore();
@@ -6283,7 +6287,7 @@ function createChart(element, options = {}) {
6283
6287
  const labelText = `${level.ratio} (${formatPrice(level.price)})`;
6284
6288
  const textWidth = measureTextWidth(labelText);
6285
6289
  const padding = 4;
6286
- const bgX = chartLeft + 4;
6290
+ const bgX = Math.max(chartLeft + 2, lineLeft - textWidth - padding * 2 - 4);
6287
6291
  const bgY = level.y - 9;
6288
6292
  ctx.fillStyle = mergedOptions.backgroundColor;
6289
6293
  fillRoundedRect(bgX, bgY, textWidth + padding * 2, 16, 3);
@@ -8228,6 +8232,8 @@ function createChart(element, options = {}) {
8228
8232
  for (const orderLine of orderLines) {
8229
8233
  drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
8230
8234
  }
8235
+ const legendInsetX = Math.max(0, Number(labels.indicatorLegendOffsetX) || 0);
8236
+ const legendInsetY = Math.max(0, Number(labels.indicatorLegendOffsetY) || 0);
8231
8237
  let dataLineBottom = chartTop;
8232
8238
  const dataLine = mergedOptions.dataLine;
8233
8239
  if (dataLine?.visible && data.length > 0) {
@@ -8244,8 +8250,8 @@ function createChart(element, options = {}) {
8244
8250
  const textColor = dataLine.textColor || labels.indicatorTextColor;
8245
8251
  const changeAbs = bar.c - bar.o;
8246
8252
  const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
8247
- const rowY = chartTop + 6;
8248
- let cursorX = chartLeft + 10;
8253
+ const rowY = chartTop + legendInsetY;
8254
+ let cursorX = chartLeft + legendInsetX;
8249
8255
  if (dataLine.statusColor) {
8250
8256
  const dotRadius = Math.max(2.5, fontSize / 4);
8251
8257
  ctx.fillStyle = dataLine.statusColor;
@@ -8291,7 +8297,7 @@ function createChart(element, options = {}) {
8291
8297
  ctx.fillText(text, cursorX, rowY);
8292
8298
  cursorX += width2 + 12;
8293
8299
  }
8294
- dataLineBottom = rowY + fontSize + 4;
8300
+ dataLineBottom = rowY + fontSize + 6;
8295
8301
  ctx.font = prevFont;
8296
8302
  }
8297
8303
  }
@@ -8321,8 +8327,8 @@ function createChart(element, options = {}) {
8321
8327
  const prevFont = ctx.font;
8322
8328
  const legendFontSize = Math.max(8, axis.fontSize);
8323
8329
  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);
8330
+ const offsetX = legendInsetX;
8331
+ const offsetY = legendInsetY;
8326
8332
  const position = labels.indicatorLegendPosition;
8327
8333
  const isRight = position === "top-right" || position === "bottom-right";
8328
8334
  const isBottom = position === "bottom-left" || position === "bottom-right";
@@ -9347,12 +9353,16 @@ function createChart(element, options = {}) {
9347
9353
  if (Math.hypot(x - x2, y - y2) <= 9 * hitTolerance) {
9348
9354
  return { drawing, target: "handle", pointIndex: 1 };
9349
9355
  }
9350
- const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9351
- for (const ratio of fibRatios) {
9352
- const price = first.price + (second.price - first.price) * (1 - ratio);
9353
- const lineY = canvasYFromDrawingPrice(price);
9354
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9355
- return { drawing, target: "line" };
9356
+ const fibLeft = Math.min(x1, x2) - 5 * hitTolerance;
9357
+ const fibRight = Math.max(x1, x2) + 5 * hitTolerance;
9358
+ if (x >= fibLeft && x <= fibRight) {
9359
+ const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
9360
+ for (const ratio of fibRatios) {
9361
+ const price = first.price + (second.price - first.price) * (1 - ratio);
9362
+ const lineY = canvasYFromDrawingPrice(price);
9363
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9364
+ return { drawing, target: "line" };
9365
+ }
9356
9366
  }
9357
9367
  }
9358
9368
  } else if (drawing.type === "fib-extension") {
@@ -9377,11 +9387,15 @@ function createChart(element, options = {}) {
9377
9387
  return { drawing, target: "handle", pointIndex: 2 };
9378
9388
  }
9379
9389
  const move = p1.price - p0.price;
9380
- const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9381
- for (const ratio of fibExtRatios) {
9382
- const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9383
- if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9384
- return { drawing, target: "line" };
9390
+ const extLeft = Math.min(x0, x1, x2) - 5 * hitTolerance;
9391
+ const extRight = Math.max(x0, x1, x2) + 5 * hitTolerance;
9392
+ if (x >= extLeft && x <= extRight) {
9393
+ const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
9394
+ for (const ratio of fibExtRatios) {
9395
+ const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
9396
+ if (Math.abs(y - lineY) <= 5 * hitTolerance) {
9397
+ return { drawing, target: "line" };
9398
+ }
9385
9399
  }
9386
9400
  }
9387
9401
  if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.151",
3
+ "version": "0.1.153",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",