hyperprop-charting-library 0.1.114 → 0.1.115

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.
@@ -2666,6 +2666,37 @@ function createChart(element, options = {}) {
2666
2666
  }
2667
2667
  }
2668
2668
  }
2669
+ } else if (drawing.type === "rectangle") {
2670
+ const p0 = drawing.points[0];
2671
+ const p1 = drawing.points[1];
2672
+ if (p0 && p1) {
2673
+ const px0 = xFromDrawingPoint(p0);
2674
+ const px1 = xFromDrawingPoint(p1);
2675
+ const leftX = Math.min(px0, px1);
2676
+ const rightX = Math.max(px0, px1);
2677
+ const y0 = yFromPrice(p0.price);
2678
+ const y1 = yFromPrice(p1.price);
2679
+ const topY = Math.min(y0, y1);
2680
+ const botY = Math.max(y0, y1);
2681
+ const boxW = Math.max(1, rightX - leftX);
2682
+ const boxH = Math.max(1, botY - topY);
2683
+ ctx.save();
2684
+ ctx.globalAlpha = draft ? 0.6 : 1;
2685
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2686
+ ctx.fillRect(leftX, topY, boxW, boxH);
2687
+ ctx.lineWidth = Math.max(1, drawing.width);
2688
+ ctx.strokeStyle = drawing.color;
2689
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2690
+ ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
2691
+ ctx.restore();
2692
+ handleAt(px0, y0, drawing.color);
2693
+ handleAt(px1, y1, drawing.color);
2694
+ handleAt(px0, y1, drawing.color);
2695
+ handleAt(px1, y0, drawing.color);
2696
+ if (drawing.label) {
2697
+ drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
2698
+ }
2699
+ }
2669
2700
  } else if (drawing.type === "price-range") {
2670
2701
  const p0 = drawing.points[0];
2671
2702
  const p1 = drawing.points[1];
@@ -4155,6 +4186,21 @@ function createChart(element, options = {}) {
4155
4186
  if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
4156
4187
  return { drawing, target: "line" };
4157
4188
  }
4189
+ } else if (drawing.type === "rectangle") {
4190
+ const p0 = drawing.points[0];
4191
+ const p1 = drawing.points[1];
4192
+ if (!p0 || !p1) continue;
4193
+ const px0 = canvasXFromDrawingPoint(p0);
4194
+ const px1 = canvasXFromDrawingPoint(p1);
4195
+ const y0 = canvasYFromDrawingPrice(p0.price);
4196
+ const y1 = canvasYFromDrawingPrice(p1.price);
4197
+ if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
4198
+ if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
4199
+ if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
4200
+ if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
4201
+ if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
4202
+ return { drawing, target: "line" };
4203
+ }
4158
4204
  } else if (drawing.type === "price-range") {
4159
4205
  const p0 = drawing.points[0];
4160
4206
  const p1 = drawing.points[1];
@@ -4411,6 +4457,30 @@ function createChart(element, options = {}) {
4411
4457
  drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4412
4458
  return true;
4413
4459
  }
4460
+ if (activeDrawingTool === "rectangle") {
4461
+ if (draftDrawing?.type === "rectangle") {
4462
+ const completed = normalizeDrawingState({
4463
+ ...serializeDrawing(draftDrawing),
4464
+ points: [draftDrawing.points[0], point]
4465
+ });
4466
+ drawings.push(completed);
4467
+ draftDrawing = null;
4468
+ activeDrawingTool = null;
4469
+ emitDrawingsChange();
4470
+ scheduleDraw();
4471
+ return true;
4472
+ }
4473
+ const defaults = getDrawingToolDefaults("rectangle");
4474
+ draftDrawing = normalizeDrawingState({
4475
+ type: "rectangle",
4476
+ points: [point, point],
4477
+ color: defaults.color ?? "#2962ff",
4478
+ style: defaults.style ?? "solid",
4479
+ width: defaults.width ?? 1
4480
+ });
4481
+ scheduleDraw();
4482
+ return true;
4483
+ }
4414
4484
  if (activeDrawingTool === "price-range") {
4415
4485
  const tick = getConfiguredTickSize();
4416
4486
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4512,6 +4582,25 @@ function createChart(element, options = {}) {
4512
4582
  }
4513
4583
  return { ...drawing, points: pts };
4514
4584
  }
4585
+ if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
4586
+ const pts = drawing.points.map((point) => ({ ...point }));
4587
+ const p0 = pts[0];
4588
+ const p1 = pts[1];
4589
+ if (!p0 || !p1) return drawing;
4590
+ const pointIndex = drawingDragState.pointIndex ?? 0;
4591
+ if (pointIndex === 0) {
4592
+ pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4593
+ } else if (pointIndex === 1) {
4594
+ pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4595
+ } else if (pointIndex === 2) {
4596
+ pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
4597
+ pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
4598
+ } else if (pointIndex === 3) {
4599
+ pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
4600
+ pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
4601
+ }
4602
+ return { ...drawing, points: pts };
4603
+ }
4515
4604
  if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
4516
4605
  const pointIndex = drawingDragState.pointIndex ?? 0;
4517
4606
  const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
@@ -4780,7 +4869,7 @@ function createChart(element, options = {}) {
4780
4869
  setCrosshairPoint(null);
4781
4870
  return;
4782
4871
  }
4783
- if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4872
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4784
4873
  let nextPoint = drawingPointFromCanvas(point.x, point.y);
4785
4874
  if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
4786
4875
  nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
@@ -44,7 +44,7 @@ interface ChartOptions {
44
44
  drawings?: DrawingObjectOptions[];
45
45
  }
46
46
  type IndicatorPane = "overlay" | "separate";
47
- type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
47
+ type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note";
48
48
  interface DrawingPoint {
49
49
  index: number;
50
50
  price: number;
@@ -2640,6 +2640,37 @@ function createChart(element, options = {}) {
2640
2640
  }
2641
2641
  }
2642
2642
  }
2643
+ } else if (drawing.type === "rectangle") {
2644
+ const p0 = drawing.points[0];
2645
+ const p1 = drawing.points[1];
2646
+ if (p0 && p1) {
2647
+ const px0 = xFromDrawingPoint(p0);
2648
+ const px1 = xFromDrawingPoint(p1);
2649
+ const leftX = Math.min(px0, px1);
2650
+ const rightX = Math.max(px0, px1);
2651
+ const y0 = yFromPrice(p0.price);
2652
+ const y1 = yFromPrice(p1.price);
2653
+ const topY = Math.min(y0, y1);
2654
+ const botY = Math.max(y0, y1);
2655
+ const boxW = Math.max(1, rightX - leftX);
2656
+ const boxH = Math.max(1, botY - topY);
2657
+ ctx.save();
2658
+ ctx.globalAlpha = draft ? 0.6 : 1;
2659
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2660
+ ctx.fillRect(leftX, topY, boxW, boxH);
2661
+ ctx.lineWidth = Math.max(1, drawing.width);
2662
+ ctx.strokeStyle = drawing.color;
2663
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2664
+ ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
2665
+ ctx.restore();
2666
+ handleAt(px0, y0, drawing.color);
2667
+ handleAt(px1, y1, drawing.color);
2668
+ handleAt(px0, y1, drawing.color);
2669
+ handleAt(px1, y0, drawing.color);
2670
+ if (drawing.label) {
2671
+ drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
2672
+ }
2673
+ }
2643
2674
  } else if (drawing.type === "price-range") {
2644
2675
  const p0 = drawing.points[0];
2645
2676
  const p1 = drawing.points[1];
@@ -4129,6 +4160,21 @@ function createChart(element, options = {}) {
4129
4160
  if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
4130
4161
  return { drawing, target: "line" };
4131
4162
  }
4163
+ } else if (drawing.type === "rectangle") {
4164
+ const p0 = drawing.points[0];
4165
+ const p1 = drawing.points[1];
4166
+ if (!p0 || !p1) continue;
4167
+ const px0 = canvasXFromDrawingPoint(p0);
4168
+ const px1 = canvasXFromDrawingPoint(p1);
4169
+ const y0 = canvasYFromDrawingPrice(p0.price);
4170
+ const y1 = canvasYFromDrawingPrice(p1.price);
4171
+ if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
4172
+ if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
4173
+ if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
4174
+ if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
4175
+ if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
4176
+ return { drawing, target: "line" };
4177
+ }
4132
4178
  } else if (drawing.type === "price-range") {
4133
4179
  const p0 = drawing.points[0];
4134
4180
  const p1 = drawing.points[1];
@@ -4385,6 +4431,30 @@ function createChart(element, options = {}) {
4385
4431
  drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4386
4432
  return true;
4387
4433
  }
4434
+ if (activeDrawingTool === "rectangle") {
4435
+ if (draftDrawing?.type === "rectangle") {
4436
+ const completed = normalizeDrawingState({
4437
+ ...serializeDrawing(draftDrawing),
4438
+ points: [draftDrawing.points[0], point]
4439
+ });
4440
+ drawings.push(completed);
4441
+ draftDrawing = null;
4442
+ activeDrawingTool = null;
4443
+ emitDrawingsChange();
4444
+ scheduleDraw();
4445
+ return true;
4446
+ }
4447
+ const defaults = getDrawingToolDefaults("rectangle");
4448
+ draftDrawing = normalizeDrawingState({
4449
+ type: "rectangle",
4450
+ points: [point, point],
4451
+ color: defaults.color ?? "#2962ff",
4452
+ style: defaults.style ?? "solid",
4453
+ width: defaults.width ?? 1
4454
+ });
4455
+ scheduleDraw();
4456
+ return true;
4457
+ }
4388
4458
  if (activeDrawingTool === "price-range") {
4389
4459
  const tick = getConfiguredTickSize();
4390
4460
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4486,6 +4556,25 @@ function createChart(element, options = {}) {
4486
4556
  }
4487
4557
  return { ...drawing, points: pts };
4488
4558
  }
4559
+ if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
4560
+ const pts = drawing.points.map((point) => ({ ...point }));
4561
+ const p0 = pts[0];
4562
+ const p1 = pts[1];
4563
+ if (!p0 || !p1) return drawing;
4564
+ const pointIndex = drawingDragState.pointIndex ?? 0;
4565
+ if (pointIndex === 0) {
4566
+ pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4567
+ } else if (pointIndex === 1) {
4568
+ pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4569
+ } else if (pointIndex === 2) {
4570
+ pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
4571
+ pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
4572
+ } else if (pointIndex === 3) {
4573
+ pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
4574
+ pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
4575
+ }
4576
+ return { ...drawing, points: pts };
4577
+ }
4489
4578
  if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
4490
4579
  const pointIndex = drawingDragState.pointIndex ?? 0;
4491
4580
  const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
@@ -4754,7 +4843,7 @@ function createChart(element, options = {}) {
4754
4843
  setCrosshairPoint(null);
4755
4844
  return;
4756
4845
  }
4757
- if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4846
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4758
4847
  let nextPoint = drawingPointFromCanvas(point.x, point.y);
4759
4848
  if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
4760
4849
  nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
package/dist/index.cjs CHANGED
@@ -2666,6 +2666,37 @@ function createChart(element, options = {}) {
2666
2666
  }
2667
2667
  }
2668
2668
  }
2669
+ } else if (drawing.type === "rectangle") {
2670
+ const p0 = drawing.points[0];
2671
+ const p1 = drawing.points[1];
2672
+ if (p0 && p1) {
2673
+ const px0 = xFromDrawingPoint(p0);
2674
+ const px1 = xFromDrawingPoint(p1);
2675
+ const leftX = Math.min(px0, px1);
2676
+ const rightX = Math.max(px0, px1);
2677
+ const y0 = yFromPrice(p0.price);
2678
+ const y1 = yFromPrice(p1.price);
2679
+ const topY = Math.min(y0, y1);
2680
+ const botY = Math.max(y0, y1);
2681
+ const boxW = Math.max(1, rightX - leftX);
2682
+ const boxH = Math.max(1, botY - topY);
2683
+ ctx.save();
2684
+ ctx.globalAlpha = draft ? 0.6 : 1;
2685
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2686
+ ctx.fillRect(leftX, topY, boxW, boxH);
2687
+ ctx.lineWidth = Math.max(1, drawing.width);
2688
+ ctx.strokeStyle = drawing.color;
2689
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2690
+ ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
2691
+ ctx.restore();
2692
+ handleAt(px0, y0, drawing.color);
2693
+ handleAt(px1, y1, drawing.color);
2694
+ handleAt(px0, y1, drawing.color);
2695
+ handleAt(px1, y0, drawing.color);
2696
+ if (drawing.label) {
2697
+ drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
2698
+ }
2699
+ }
2669
2700
  } else if (drawing.type === "price-range") {
2670
2701
  const p0 = drawing.points[0];
2671
2702
  const p1 = drawing.points[1];
@@ -4155,6 +4186,21 @@ function createChart(element, options = {}) {
4155
4186
  if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
4156
4187
  return { drawing, target: "line" };
4157
4188
  }
4189
+ } else if (drawing.type === "rectangle") {
4190
+ const p0 = drawing.points[0];
4191
+ const p1 = drawing.points[1];
4192
+ if (!p0 || !p1) continue;
4193
+ const px0 = canvasXFromDrawingPoint(p0);
4194
+ const px1 = canvasXFromDrawingPoint(p1);
4195
+ const y0 = canvasYFromDrawingPrice(p0.price);
4196
+ const y1 = canvasYFromDrawingPrice(p1.price);
4197
+ if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
4198
+ if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
4199
+ if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
4200
+ if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
4201
+ if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
4202
+ return { drawing, target: "line" };
4203
+ }
4158
4204
  } else if (drawing.type === "price-range") {
4159
4205
  const p0 = drawing.points[0];
4160
4206
  const p1 = drawing.points[1];
@@ -4411,6 +4457,30 @@ function createChart(element, options = {}) {
4411
4457
  drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4412
4458
  return true;
4413
4459
  }
4460
+ if (activeDrawingTool === "rectangle") {
4461
+ if (draftDrawing?.type === "rectangle") {
4462
+ const completed = normalizeDrawingState({
4463
+ ...serializeDrawing(draftDrawing),
4464
+ points: [draftDrawing.points[0], point]
4465
+ });
4466
+ drawings.push(completed);
4467
+ draftDrawing = null;
4468
+ activeDrawingTool = null;
4469
+ emitDrawingsChange();
4470
+ scheduleDraw();
4471
+ return true;
4472
+ }
4473
+ const defaults = getDrawingToolDefaults("rectangle");
4474
+ draftDrawing = normalizeDrawingState({
4475
+ type: "rectangle",
4476
+ points: [point, point],
4477
+ color: defaults.color ?? "#2962ff",
4478
+ style: defaults.style ?? "solid",
4479
+ width: defaults.width ?? 1
4480
+ });
4481
+ scheduleDraw();
4482
+ return true;
4483
+ }
4414
4484
  if (activeDrawingTool === "price-range") {
4415
4485
  const tick = getConfiguredTickSize();
4416
4486
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4512,6 +4582,25 @@ function createChart(element, options = {}) {
4512
4582
  }
4513
4583
  return { ...drawing, points: pts };
4514
4584
  }
4585
+ if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
4586
+ const pts = drawing.points.map((point) => ({ ...point }));
4587
+ const p0 = pts[0];
4588
+ const p1 = pts[1];
4589
+ if (!p0 || !p1) return drawing;
4590
+ const pointIndex = drawingDragState.pointIndex ?? 0;
4591
+ if (pointIndex === 0) {
4592
+ pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4593
+ } else if (pointIndex === 1) {
4594
+ pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4595
+ } else if (pointIndex === 2) {
4596
+ pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
4597
+ pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
4598
+ } else if (pointIndex === 3) {
4599
+ pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
4600
+ pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
4601
+ }
4602
+ return { ...drawing, points: pts };
4603
+ }
4515
4604
  if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
4516
4605
  const pointIndex = drawingDragState.pointIndex ?? 0;
4517
4606
  const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
@@ -4780,7 +4869,7 @@ function createChart(element, options = {}) {
4780
4869
  setCrosshairPoint(null);
4781
4870
  return;
4782
4871
  }
4783
- if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4872
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4784
4873
  let nextPoint = drawingPointFromCanvas(point.x, point.y);
4785
4874
  if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
4786
4875
  nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
package/dist/index.d.cts CHANGED
@@ -44,7 +44,7 @@ interface ChartOptions {
44
44
  drawings?: DrawingObjectOptions[];
45
45
  }
46
46
  type IndicatorPane = "overlay" | "separate";
47
- type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
47
+ type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note";
48
48
  interface DrawingPoint {
49
49
  index: number;
50
50
  price: number;
package/dist/index.d.ts CHANGED
@@ -44,7 +44,7 @@ interface ChartOptions {
44
44
  drawings?: DrawingObjectOptions[];
45
45
  }
46
46
  type IndicatorPane = "overlay" | "separate";
47
- type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
47
+ type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note";
48
48
  interface DrawingPoint {
49
49
  index: number;
50
50
  price: number;
package/dist/index.js CHANGED
@@ -2640,6 +2640,37 @@ function createChart(element, options = {}) {
2640
2640
  }
2641
2641
  }
2642
2642
  }
2643
+ } else if (drawing.type === "rectangle") {
2644
+ const p0 = drawing.points[0];
2645
+ const p1 = drawing.points[1];
2646
+ if (p0 && p1) {
2647
+ const px0 = xFromDrawingPoint(p0);
2648
+ const px1 = xFromDrawingPoint(p1);
2649
+ const leftX = Math.min(px0, px1);
2650
+ const rightX = Math.max(px0, px1);
2651
+ const y0 = yFromPrice(p0.price);
2652
+ const y1 = yFromPrice(p1.price);
2653
+ const topY = Math.min(y0, y1);
2654
+ const botY = Math.max(y0, y1);
2655
+ const boxW = Math.max(1, rightX - leftX);
2656
+ const boxH = Math.max(1, botY - topY);
2657
+ ctx.save();
2658
+ ctx.globalAlpha = draft ? 0.6 : 1;
2659
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2660
+ ctx.fillRect(leftX, topY, boxW, boxH);
2661
+ ctx.lineWidth = Math.max(1, drawing.width);
2662
+ ctx.strokeStyle = drawing.color;
2663
+ applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
2664
+ ctx.strokeRect(crisp(leftX), crisp(topY), crisp(rightX) - crisp(leftX), crisp(botY) - crisp(topY));
2665
+ ctx.restore();
2666
+ handleAt(px0, y0, drawing.color);
2667
+ handleAt(px1, y1, drawing.color);
2668
+ handleAt(px0, y1, drawing.color);
2669
+ handleAt(px1, y0, drawing.color);
2670
+ if (drawing.label) {
2671
+ drawDrawingLabel(drawing.label, (leftX + rightX) / 2, topY - 4, drawing.color);
2672
+ }
2673
+ }
2643
2674
  } else if (drawing.type === "price-range") {
2644
2675
  const p0 = drawing.points[0];
2645
2676
  const p1 = drawing.points[1];
@@ -4129,6 +4160,21 @@ function createChart(element, options = {}) {
4129
4160
  if (x >= x0 && x <= x1 && y >= yTop && y <= yBot) {
4130
4161
  return { drawing, target: "line" };
4131
4162
  }
4163
+ } else if (drawing.type === "rectangle") {
4164
+ const p0 = drawing.points[0];
4165
+ const p1 = drawing.points[1];
4166
+ if (!p0 || !p1) continue;
4167
+ const px0 = canvasXFromDrawingPoint(p0);
4168
+ const px1 = canvasXFromDrawingPoint(p1);
4169
+ const y0 = canvasYFromDrawingPrice(p0.price);
4170
+ const y1 = canvasYFromDrawingPrice(p1.price);
4171
+ if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
4172
+ if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
4173
+ if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
4174
+ if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
4175
+ if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
4176
+ return { drawing, target: "line" };
4177
+ }
4132
4178
  } else if (drawing.type === "price-range") {
4133
4179
  const p0 = drawing.points[0];
4134
4180
  const p1 = drawing.points[1];
@@ -4385,6 +4431,30 @@ function createChart(element, options = {}) {
4385
4431
  drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4386
4432
  return true;
4387
4433
  }
4434
+ if (activeDrawingTool === "rectangle") {
4435
+ if (draftDrawing?.type === "rectangle") {
4436
+ const completed = normalizeDrawingState({
4437
+ ...serializeDrawing(draftDrawing),
4438
+ points: [draftDrawing.points[0], point]
4439
+ });
4440
+ drawings.push(completed);
4441
+ draftDrawing = null;
4442
+ activeDrawingTool = null;
4443
+ emitDrawingsChange();
4444
+ scheduleDraw();
4445
+ return true;
4446
+ }
4447
+ const defaults = getDrawingToolDefaults("rectangle");
4448
+ draftDrawing = normalizeDrawingState({
4449
+ type: "rectangle",
4450
+ points: [point, point],
4451
+ color: defaults.color ?? "#2962ff",
4452
+ style: defaults.style ?? "solid",
4453
+ width: defaults.width ?? 1
4454
+ });
4455
+ scheduleDraw();
4456
+ return true;
4457
+ }
4388
4458
  if (activeDrawingTool === "price-range") {
4389
4459
  const tick = getConfiguredTickSize();
4390
4460
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4486,6 +4556,25 @@ function createChart(element, options = {}) {
4486
4556
  }
4487
4557
  return { ...drawing, points: pts };
4488
4558
  }
4559
+ if (drawingDragState.target === "handle" && drawing.type === "rectangle") {
4560
+ const pts = drawing.points.map((point) => ({ ...point }));
4561
+ const p0 = pts[0];
4562
+ const p1 = pts[1];
4563
+ if (!p0 || !p1) return drawing;
4564
+ const pointIndex = drawingDragState.pointIndex ?? 0;
4565
+ if (pointIndex === 0) {
4566
+ pts[0] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4567
+ } else if (pointIndex === 1) {
4568
+ pts[1] = normalizeDrawingPoint(currentPoint.index, currentPoint.price);
4569
+ } else if (pointIndex === 2) {
4570
+ pts[0] = normalizeDrawingPoint(currentPoint.index, p0.price);
4571
+ pts[1] = normalizeDrawingPoint(p1.index, currentPoint.price);
4572
+ } else if (pointIndex === 3) {
4573
+ pts[0] = normalizeDrawingPoint(p0.index, currentPoint.price);
4574
+ pts[1] = normalizeDrawingPoint(currentPoint.index, p1.price);
4575
+ }
4576
+ return { ...drawing, points: pts };
4577
+ }
4489
4578
  if (drawingDragState.target === "handle" && shiftKeyActive && (drawing.type === "trendline" || drawing.type === "ray")) {
4490
4579
  const pointIndex = drawingDragState.pointIndex ?? 0;
4491
4580
  const anchor = drawing.points[pointIndex === 0 ? 1 : 0];
@@ -4754,7 +4843,7 @@ function createChart(element, options = {}) {
4754
4843
  setCrosshairPoint(null);
4755
4844
  return;
4756
4845
  }
4757
- if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4846
+ if (draftDrawing && (activeDrawingTool === "trendline" || activeDrawingTool === "ray" || activeDrawingTool === "rectangle" || activeDrawingTool === "fib-retracement" || activeDrawingTool === "fib-extension")) {
4758
4847
  let nextPoint = drawingPointFromCanvas(point.x, point.y);
4759
4848
  if (nextPoint && shiftKeyActive && (draftDrawing.type === "trendline" || draftDrawing.type === "ray") && draftDrawing.points[0]) {
4760
4849
  nextPoint = constrainAngleFromAnchor(draftDrawing.points[0], point.x, point.y) ?? nextPoint;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.114",
3
+ "version": "0.1.115",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",