hyperprop-charting-library 0.1.73 → 0.1.75

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.
@@ -984,6 +984,7 @@ function createChart(element, options = {}) {
984
984
  let drawingSelectHandler = null;
985
985
  let drawingHoverHandler = null;
986
986
  let lastHoveredDrawingId = null;
987
+ let selectedDrawingId = null;
987
988
  const drawingToolDefaults = /* @__PURE__ */ new Map();
988
989
  const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
989
990
  const emitDrawingsChange = () => {
@@ -2162,6 +2163,12 @@ function createChart(element, options = {}) {
2162
2163
  if (!drawing.visible) {
2163
2164
  return;
2164
2165
  }
2166
+ const isSelected = draft || drawing.id === selectedDrawingId;
2167
+ const handleAt = (hx, hy, _color) => {
2168
+ if (isSelected) {
2169
+ drawDrawingHandle(hx, hy, drawing.color);
2170
+ }
2171
+ };
2165
2172
  ctx.save();
2166
2173
  ctx.strokeStyle = drawing.color;
2167
2174
  ctx.lineWidth = drawing.width;
@@ -2180,7 +2187,7 @@ function createChart(element, options = {}) {
2180
2187
  ctx.moveTo(crisp(chartLeft), crisp(y));
2181
2188
  ctx.lineTo(crisp(chartRight), crisp(y));
2182
2189
  ctx.stroke();
2183
- drawDrawingHandle(handleX, y, drawing.color);
2190
+ handleAt(handleX, y, drawing.color);
2184
2191
  if (drawing.label) {
2185
2192
  const midX = (chartLeft + chartRight) / 2;
2186
2193
  drawDrawingLabel(drawing.label, midX, y, drawing.color);
@@ -2195,7 +2202,7 @@ function createChart(element, options = {}) {
2195
2202
  ctx.lineTo(crisp(x), crisp(chartBottom));
2196
2203
  ctx.stroke();
2197
2204
  const handleY = (chartTop + chartBottom) / 2;
2198
- drawDrawingHandle(x, handleY, drawing.color);
2205
+ handleAt(x, handleY, drawing.color);
2199
2206
  if (drawing.label) {
2200
2207
  drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
2201
2208
  }
@@ -2212,8 +2219,8 @@ function createChart(element, options = {}) {
2212
2219
  ctx.moveTo(firstX, firstY);
2213
2220
  ctx.lineTo(secondX, secondY);
2214
2221
  ctx.stroke();
2215
- drawDrawingHandle(firstX, firstY, drawing.color);
2216
- drawDrawingHandle(secondX, secondY, drawing.color);
2222
+ handleAt(firstX, firstY, drawing.color);
2223
+ handleAt(secondX, secondY, drawing.color);
2217
2224
  if (drawing.label) {
2218
2225
  const midX = (firstX + secondX) / 2;
2219
2226
  const midY = (firstY + secondY) / 2;
@@ -2242,8 +2249,8 @@ function createChart(element, options = {}) {
2242
2249
  ctx.moveTo(firstX, firstY);
2243
2250
  ctx.lineTo(endX, endY);
2244
2251
  ctx.stroke();
2245
- drawDrawingHandle(firstX, firstY, drawing.color);
2246
- drawDrawingHandle(secondX, secondY, drawing.color);
2252
+ handleAt(firstX, firstY, drawing.color);
2253
+ handleAt(secondX, secondY, drawing.color);
2247
2254
  if (drawing.label) {
2248
2255
  const midX = (firstX + secondX) / 2;
2249
2256
  const midY = (firstY + secondY) / 2;
@@ -2297,8 +2304,8 @@ function createChart(element, options = {}) {
2297
2304
  ctx.lineTo(secondX, secondY);
2298
2305
  ctx.stroke();
2299
2306
  ctx.restore();
2300
- drawDrawingHandle(firstX, firstY, drawing.color);
2301
- drawDrawingHandle(secondX, secondY, drawing.color);
2307
+ handleAt(firstX, firstY, drawing.color);
2308
+ handleAt(secondX, secondY, drawing.color);
2302
2309
  const prevFont = ctx.font;
2303
2310
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2304
2311
  levelLines.forEach((level, index) => {
@@ -2337,12 +2344,12 @@ function createChart(element, options = {}) {
2337
2344
  }
2338
2345
  ctx.stroke();
2339
2346
  ctx.restore();
2340
- drawDrawingHandle(x0, y0, drawing.color);
2341
- drawDrawingHandle(x1, y1, drawing.color);
2347
+ handleAt(x0, y0, drawing.color);
2348
+ handleAt(x1, y1, drawing.color);
2342
2349
  if (p2) {
2343
2350
  const x2 = xFromDrawingPoint(p2);
2344
2351
  const y2 = yFromPrice(p2.price);
2345
- drawDrawingHandle(x2, y2, drawing.color);
2352
+ handleAt(x2, y2, drawing.color);
2346
2353
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
2347
2354
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
2348
2355
  const move = p1.price - p0.price;
@@ -2422,61 +2429,63 @@ function createChart(element, options = {}) {
2422
2429
  ctx.fillStyle = lossFill;
2423
2430
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2424
2431
  ctx.restore();
2425
- ctx.save();
2426
- ctx.setLineDash([]);
2427
- ctx.lineWidth = Math.max(1, drawing.width);
2428
- ctx.strokeStyle = profitLine;
2429
- ctx.beginPath();
2430
- ctx.moveTo(crisp(boxX0), crisp(targetY));
2431
- ctx.lineTo(crisp(boxX1), crisp(targetY));
2432
- ctx.stroke();
2433
- ctx.strokeStyle = lossLine;
2434
- ctx.beginPath();
2435
- ctx.moveTo(crisp(boxX0), crisp(stopY));
2436
- ctx.lineTo(crisp(boxX1), crisp(stopY));
2437
- ctx.stroke();
2438
- ctx.strokeStyle = drawing.color;
2439
- ctx.beginPath();
2440
- ctx.moveTo(crisp(boxX0), crisp(entryY));
2441
- ctx.lineTo(crisp(boxX1), crisp(entryY));
2442
- ctx.stroke();
2443
- ctx.restore();
2444
- drawDrawingHandle(leftX, targetY, drawing.color);
2445
- drawDrawingHandle(leftX, entryY, drawing.color);
2446
- drawDrawingHandle(leftX, stopY, drawing.color);
2447
- drawDrawingHandle(rightX, entryY, drawing.color);
2448
- const tick = getConfiguredTickSize();
2449
- const pctOf = (price) => {
2450
- if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2451
- return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2452
- };
2453
- const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2454
- const targetDist = Math.abs(target.price - entry.price);
2455
- const stopDist = Math.abs(entry.price - stop.price);
2456
- const rr = stopDist > 0 ? targetDist / stopDist : 0;
2457
- const cx = (boxX0 + boxX1) / 2;
2458
- const drawPositionPill = (text, centerX, centerY, bg) => {
2459
- const prevFont = ctx.font;
2460
- ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2461
- const padding = 6;
2462
- const textW = ctx.measureText(text).width;
2463
- const pillW = textW + padding * 2;
2464
- const pillH = 18;
2465
- const pillX = centerX - pillW / 2;
2466
- const pillY = centerY - pillH / 2;
2467
- ctx.fillStyle = bg;
2468
- fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2469
- ctx.fillStyle = labelTextColor;
2470
- ctx.textAlign = "center";
2471
- ctx.textBaseline = "middle";
2472
- ctx.fillText(text, centerX, pillY + pillH / 2);
2473
- ctx.font = prevFont;
2474
- };
2475
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2476
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2477
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2478
- if (drawing.label) {
2479
- drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2432
+ if (isSelected) {
2433
+ ctx.save();
2434
+ ctx.setLineDash([]);
2435
+ ctx.lineWidth = Math.max(1, drawing.width);
2436
+ ctx.strokeStyle = profitLine;
2437
+ ctx.beginPath();
2438
+ ctx.moveTo(crisp(boxX0), crisp(targetY));
2439
+ ctx.lineTo(crisp(boxX1), crisp(targetY));
2440
+ ctx.stroke();
2441
+ ctx.strokeStyle = lossLine;
2442
+ ctx.beginPath();
2443
+ ctx.moveTo(crisp(boxX0), crisp(stopY));
2444
+ ctx.lineTo(crisp(boxX1), crisp(stopY));
2445
+ ctx.stroke();
2446
+ ctx.strokeStyle = drawing.color;
2447
+ ctx.beginPath();
2448
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2449
+ ctx.lineTo(crisp(boxX1), crisp(entryY));
2450
+ ctx.stroke();
2451
+ ctx.restore();
2452
+ handleAt(leftX, targetY, drawing.color);
2453
+ handleAt(leftX, entryY, drawing.color);
2454
+ handleAt(leftX, stopY, drawing.color);
2455
+ handleAt(rightX, entryY, drawing.color);
2456
+ const tick = getConfiguredTickSize();
2457
+ const pctOf = (price) => {
2458
+ if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2459
+ return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2460
+ };
2461
+ const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2462
+ const targetDist = Math.abs(target.price - entry.price);
2463
+ const stopDist = Math.abs(entry.price - stop.price);
2464
+ const rr = stopDist > 0 ? targetDist / stopDist : 0;
2465
+ const cx = (boxX0 + boxX1) / 2;
2466
+ const drawPositionPill = (text, centerX, centerY, bg) => {
2467
+ const prevFont = ctx.font;
2468
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2469
+ const padding = 6;
2470
+ const textW = ctx.measureText(text).width;
2471
+ const pillW = textW + padding * 2;
2472
+ const pillH = 18;
2473
+ const pillX = centerX - pillW / 2;
2474
+ const pillY = centerY - pillH / 2;
2475
+ ctx.fillStyle = bg;
2476
+ fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2477
+ ctx.fillStyle = labelTextColor;
2478
+ ctx.textAlign = "center";
2479
+ ctx.textBaseline = "middle";
2480
+ ctx.fillText(text, centerX, pillY + pillH / 2);
2481
+ ctx.font = prevFont;
2482
+ };
2483
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2484
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2485
+ drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2486
+ if (drawing.label) {
2487
+ drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2488
+ }
2480
2489
  }
2481
2490
  }
2482
2491
  }
@@ -3934,6 +3943,13 @@ function createChart(element, options = {}) {
3934
3943
  }
3935
3944
  drawingToolDefaults.set(tool, { ...defaults });
3936
3945
  };
3946
+ const setSelectedDrawing = (id) => {
3947
+ if (selectedDrawingId === id) {
3948
+ return;
3949
+ }
3950
+ selectedDrawingId = id;
3951
+ draw();
3952
+ };
3937
3953
  const updateDrawingDrag = (x, y) => {
3938
3954
  if (!drawingDragState) {
3939
3955
  return false;
@@ -4115,6 +4131,7 @@ function createChart(element, options = {}) {
4115
4131
  if (region === "plot" && !activeDrawingTool) {
4116
4132
  const drawingHit = getDrawingHit(point.x, point.y);
4117
4133
  if (drawingHit) {
4134
+ selectedDrawingId = drawingHit.drawing.id;
4118
4135
  drawingSelectHandler?.({
4119
4136
  drawing: serializeDrawing(drawingHit.drawing),
4120
4137
  target: drawingHit.target,
@@ -4424,6 +4441,7 @@ function createChart(element, options = {}) {
4424
4441
  emitCrosshairMove(point.x, point.y, "plot");
4425
4442
  }
4426
4443
  } else if (!pointerDownInfo.moved) {
4444
+ selectedDrawingId = null;
4427
4445
  const clickPrice = pointerDownInfo.region === "plot" ? roundToPricePrecision(priceFromCanvasY(pointerDownInfo.y)) : void 0;
4428
4446
  chartClickHandler?.({
4429
4447
  x: pointerDownInfo.x,
@@ -4846,6 +4864,7 @@ function createChart(element, options = {}) {
4846
4864
  clearDrawings,
4847
4865
  onDrawingsChange,
4848
4866
  onDrawingSelect,
4867
+ setSelectedDrawing,
4849
4868
  onDrawingHover,
4850
4869
  setDrawingDefaults,
4851
4870
  setDoubleClickEnabled,
@@ -422,6 +422,7 @@ interface ChartInstance {
422
422
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
423
423
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
424
424
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
425
+ setSelectedDrawing: (id: string | null) => void;
425
426
  setDoubleClickEnabled: (enabled: boolean) => void;
426
427
  setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
427
428
  registerIndicator: (plugin: IndicatorPlugin<any>) => void;
@@ -958,6 +958,7 @@ function createChart(element, options = {}) {
958
958
  let drawingSelectHandler = null;
959
959
  let drawingHoverHandler = null;
960
960
  let lastHoveredDrawingId = null;
961
+ let selectedDrawingId = null;
961
962
  const drawingToolDefaults = /* @__PURE__ */ new Map();
962
963
  const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
963
964
  const emitDrawingsChange = () => {
@@ -2136,6 +2137,12 @@ function createChart(element, options = {}) {
2136
2137
  if (!drawing.visible) {
2137
2138
  return;
2138
2139
  }
2140
+ const isSelected = draft || drawing.id === selectedDrawingId;
2141
+ const handleAt = (hx, hy, _color) => {
2142
+ if (isSelected) {
2143
+ drawDrawingHandle(hx, hy, drawing.color);
2144
+ }
2145
+ };
2139
2146
  ctx.save();
2140
2147
  ctx.strokeStyle = drawing.color;
2141
2148
  ctx.lineWidth = drawing.width;
@@ -2154,7 +2161,7 @@ function createChart(element, options = {}) {
2154
2161
  ctx.moveTo(crisp(chartLeft), crisp(y));
2155
2162
  ctx.lineTo(crisp(chartRight), crisp(y));
2156
2163
  ctx.stroke();
2157
- drawDrawingHandle(handleX, y, drawing.color);
2164
+ handleAt(handleX, y, drawing.color);
2158
2165
  if (drawing.label) {
2159
2166
  const midX = (chartLeft + chartRight) / 2;
2160
2167
  drawDrawingLabel(drawing.label, midX, y, drawing.color);
@@ -2169,7 +2176,7 @@ function createChart(element, options = {}) {
2169
2176
  ctx.lineTo(crisp(x), crisp(chartBottom));
2170
2177
  ctx.stroke();
2171
2178
  const handleY = (chartTop + chartBottom) / 2;
2172
- drawDrawingHandle(x, handleY, drawing.color);
2179
+ handleAt(x, handleY, drawing.color);
2173
2180
  if (drawing.label) {
2174
2181
  drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
2175
2182
  }
@@ -2186,8 +2193,8 @@ function createChart(element, options = {}) {
2186
2193
  ctx.moveTo(firstX, firstY);
2187
2194
  ctx.lineTo(secondX, secondY);
2188
2195
  ctx.stroke();
2189
- drawDrawingHandle(firstX, firstY, drawing.color);
2190
- drawDrawingHandle(secondX, secondY, drawing.color);
2196
+ handleAt(firstX, firstY, drawing.color);
2197
+ handleAt(secondX, secondY, drawing.color);
2191
2198
  if (drawing.label) {
2192
2199
  const midX = (firstX + secondX) / 2;
2193
2200
  const midY = (firstY + secondY) / 2;
@@ -2216,8 +2223,8 @@ function createChart(element, options = {}) {
2216
2223
  ctx.moveTo(firstX, firstY);
2217
2224
  ctx.lineTo(endX, endY);
2218
2225
  ctx.stroke();
2219
- drawDrawingHandle(firstX, firstY, drawing.color);
2220
- drawDrawingHandle(secondX, secondY, drawing.color);
2226
+ handleAt(firstX, firstY, drawing.color);
2227
+ handleAt(secondX, secondY, drawing.color);
2221
2228
  if (drawing.label) {
2222
2229
  const midX = (firstX + secondX) / 2;
2223
2230
  const midY = (firstY + secondY) / 2;
@@ -2271,8 +2278,8 @@ function createChart(element, options = {}) {
2271
2278
  ctx.lineTo(secondX, secondY);
2272
2279
  ctx.stroke();
2273
2280
  ctx.restore();
2274
- drawDrawingHandle(firstX, firstY, drawing.color);
2275
- drawDrawingHandle(secondX, secondY, drawing.color);
2281
+ handleAt(firstX, firstY, drawing.color);
2282
+ handleAt(secondX, secondY, drawing.color);
2276
2283
  const prevFont = ctx.font;
2277
2284
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2278
2285
  levelLines.forEach((level, index) => {
@@ -2311,12 +2318,12 @@ function createChart(element, options = {}) {
2311
2318
  }
2312
2319
  ctx.stroke();
2313
2320
  ctx.restore();
2314
- drawDrawingHandle(x0, y0, drawing.color);
2315
- drawDrawingHandle(x1, y1, drawing.color);
2321
+ handleAt(x0, y0, drawing.color);
2322
+ handleAt(x1, y1, drawing.color);
2316
2323
  if (p2) {
2317
2324
  const x2 = xFromDrawingPoint(p2);
2318
2325
  const y2 = yFromPrice(p2.price);
2319
- drawDrawingHandle(x2, y2, drawing.color);
2326
+ handleAt(x2, y2, drawing.color);
2320
2327
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
2321
2328
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
2322
2329
  const move = p1.price - p0.price;
@@ -2396,61 +2403,63 @@ function createChart(element, options = {}) {
2396
2403
  ctx.fillStyle = lossFill;
2397
2404
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2398
2405
  ctx.restore();
2399
- ctx.save();
2400
- ctx.setLineDash([]);
2401
- ctx.lineWidth = Math.max(1, drawing.width);
2402
- ctx.strokeStyle = profitLine;
2403
- ctx.beginPath();
2404
- ctx.moveTo(crisp(boxX0), crisp(targetY));
2405
- ctx.lineTo(crisp(boxX1), crisp(targetY));
2406
- ctx.stroke();
2407
- ctx.strokeStyle = lossLine;
2408
- ctx.beginPath();
2409
- ctx.moveTo(crisp(boxX0), crisp(stopY));
2410
- ctx.lineTo(crisp(boxX1), crisp(stopY));
2411
- ctx.stroke();
2412
- ctx.strokeStyle = drawing.color;
2413
- ctx.beginPath();
2414
- ctx.moveTo(crisp(boxX0), crisp(entryY));
2415
- ctx.lineTo(crisp(boxX1), crisp(entryY));
2416
- ctx.stroke();
2417
- ctx.restore();
2418
- drawDrawingHandle(leftX, targetY, drawing.color);
2419
- drawDrawingHandle(leftX, entryY, drawing.color);
2420
- drawDrawingHandle(leftX, stopY, drawing.color);
2421
- drawDrawingHandle(rightX, entryY, drawing.color);
2422
- const tick = getConfiguredTickSize();
2423
- const pctOf = (price) => {
2424
- if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2425
- return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2426
- };
2427
- const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2428
- const targetDist = Math.abs(target.price - entry.price);
2429
- const stopDist = Math.abs(entry.price - stop.price);
2430
- const rr = stopDist > 0 ? targetDist / stopDist : 0;
2431
- const cx = (boxX0 + boxX1) / 2;
2432
- const drawPositionPill = (text, centerX, centerY, bg) => {
2433
- const prevFont = ctx.font;
2434
- ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2435
- const padding = 6;
2436
- const textW = ctx.measureText(text).width;
2437
- const pillW = textW + padding * 2;
2438
- const pillH = 18;
2439
- const pillX = centerX - pillW / 2;
2440
- const pillY = centerY - pillH / 2;
2441
- ctx.fillStyle = bg;
2442
- fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2443
- ctx.fillStyle = labelTextColor;
2444
- ctx.textAlign = "center";
2445
- ctx.textBaseline = "middle";
2446
- ctx.fillText(text, centerX, pillY + pillH / 2);
2447
- ctx.font = prevFont;
2448
- };
2449
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2450
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2451
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2452
- if (drawing.label) {
2453
- drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2406
+ if (isSelected) {
2407
+ ctx.save();
2408
+ ctx.setLineDash([]);
2409
+ ctx.lineWidth = Math.max(1, drawing.width);
2410
+ ctx.strokeStyle = profitLine;
2411
+ ctx.beginPath();
2412
+ ctx.moveTo(crisp(boxX0), crisp(targetY));
2413
+ ctx.lineTo(crisp(boxX1), crisp(targetY));
2414
+ ctx.stroke();
2415
+ ctx.strokeStyle = lossLine;
2416
+ ctx.beginPath();
2417
+ ctx.moveTo(crisp(boxX0), crisp(stopY));
2418
+ ctx.lineTo(crisp(boxX1), crisp(stopY));
2419
+ ctx.stroke();
2420
+ ctx.strokeStyle = drawing.color;
2421
+ ctx.beginPath();
2422
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2423
+ ctx.lineTo(crisp(boxX1), crisp(entryY));
2424
+ ctx.stroke();
2425
+ ctx.restore();
2426
+ handleAt(leftX, targetY, drawing.color);
2427
+ handleAt(leftX, entryY, drawing.color);
2428
+ handleAt(leftX, stopY, drawing.color);
2429
+ handleAt(rightX, entryY, drawing.color);
2430
+ const tick = getConfiguredTickSize();
2431
+ const pctOf = (price) => {
2432
+ if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2433
+ return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2434
+ };
2435
+ const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2436
+ const targetDist = Math.abs(target.price - entry.price);
2437
+ const stopDist = Math.abs(entry.price - stop.price);
2438
+ const rr = stopDist > 0 ? targetDist / stopDist : 0;
2439
+ const cx = (boxX0 + boxX1) / 2;
2440
+ const drawPositionPill = (text, centerX, centerY, bg) => {
2441
+ const prevFont = ctx.font;
2442
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2443
+ const padding = 6;
2444
+ const textW = ctx.measureText(text).width;
2445
+ const pillW = textW + padding * 2;
2446
+ const pillH = 18;
2447
+ const pillX = centerX - pillW / 2;
2448
+ const pillY = centerY - pillH / 2;
2449
+ ctx.fillStyle = bg;
2450
+ fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2451
+ ctx.fillStyle = labelTextColor;
2452
+ ctx.textAlign = "center";
2453
+ ctx.textBaseline = "middle";
2454
+ ctx.fillText(text, centerX, pillY + pillH / 2);
2455
+ ctx.font = prevFont;
2456
+ };
2457
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2458
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2459
+ drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2460
+ if (drawing.label) {
2461
+ drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2462
+ }
2454
2463
  }
2455
2464
  }
2456
2465
  }
@@ -3908,6 +3917,13 @@ function createChart(element, options = {}) {
3908
3917
  }
3909
3918
  drawingToolDefaults.set(tool, { ...defaults });
3910
3919
  };
3920
+ const setSelectedDrawing = (id) => {
3921
+ if (selectedDrawingId === id) {
3922
+ return;
3923
+ }
3924
+ selectedDrawingId = id;
3925
+ draw();
3926
+ };
3911
3927
  const updateDrawingDrag = (x, y) => {
3912
3928
  if (!drawingDragState) {
3913
3929
  return false;
@@ -4089,6 +4105,7 @@ function createChart(element, options = {}) {
4089
4105
  if (region === "plot" && !activeDrawingTool) {
4090
4106
  const drawingHit = getDrawingHit(point.x, point.y);
4091
4107
  if (drawingHit) {
4108
+ selectedDrawingId = drawingHit.drawing.id;
4092
4109
  drawingSelectHandler?.({
4093
4110
  drawing: serializeDrawing(drawingHit.drawing),
4094
4111
  target: drawingHit.target,
@@ -4398,6 +4415,7 @@ function createChart(element, options = {}) {
4398
4415
  emitCrosshairMove(point.x, point.y, "plot");
4399
4416
  }
4400
4417
  } else if (!pointerDownInfo.moved) {
4418
+ selectedDrawingId = null;
4401
4419
  const clickPrice = pointerDownInfo.region === "plot" ? roundToPricePrecision(priceFromCanvasY(pointerDownInfo.y)) : void 0;
4402
4420
  chartClickHandler?.({
4403
4421
  x: pointerDownInfo.x,
@@ -4820,6 +4838,7 @@ function createChart(element, options = {}) {
4820
4838
  clearDrawings,
4821
4839
  onDrawingsChange,
4822
4840
  onDrawingSelect,
4841
+ setSelectedDrawing,
4823
4842
  onDrawingHover,
4824
4843
  setDrawingDefaults,
4825
4844
  setDoubleClickEnabled,
package/dist/index.cjs CHANGED
@@ -984,6 +984,7 @@ function createChart(element, options = {}) {
984
984
  let drawingSelectHandler = null;
985
985
  let drawingHoverHandler = null;
986
986
  let lastHoveredDrawingId = null;
987
+ let selectedDrawingId = null;
987
988
  const drawingToolDefaults = /* @__PURE__ */ new Map();
988
989
  const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
989
990
  const emitDrawingsChange = () => {
@@ -2162,6 +2163,12 @@ function createChart(element, options = {}) {
2162
2163
  if (!drawing.visible) {
2163
2164
  return;
2164
2165
  }
2166
+ const isSelected = draft || drawing.id === selectedDrawingId;
2167
+ const handleAt = (hx, hy, _color) => {
2168
+ if (isSelected) {
2169
+ drawDrawingHandle(hx, hy, drawing.color);
2170
+ }
2171
+ };
2165
2172
  ctx.save();
2166
2173
  ctx.strokeStyle = drawing.color;
2167
2174
  ctx.lineWidth = drawing.width;
@@ -2180,7 +2187,7 @@ function createChart(element, options = {}) {
2180
2187
  ctx.moveTo(crisp(chartLeft), crisp(y));
2181
2188
  ctx.lineTo(crisp(chartRight), crisp(y));
2182
2189
  ctx.stroke();
2183
- drawDrawingHandle(handleX, y, drawing.color);
2190
+ handleAt(handleX, y, drawing.color);
2184
2191
  if (drawing.label) {
2185
2192
  const midX = (chartLeft + chartRight) / 2;
2186
2193
  drawDrawingLabel(drawing.label, midX, y, drawing.color);
@@ -2195,7 +2202,7 @@ function createChart(element, options = {}) {
2195
2202
  ctx.lineTo(crisp(x), crisp(chartBottom));
2196
2203
  ctx.stroke();
2197
2204
  const handleY = (chartTop + chartBottom) / 2;
2198
- drawDrawingHandle(x, handleY, drawing.color);
2205
+ handleAt(x, handleY, drawing.color);
2199
2206
  if (drawing.label) {
2200
2207
  drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
2201
2208
  }
@@ -2212,8 +2219,8 @@ function createChart(element, options = {}) {
2212
2219
  ctx.moveTo(firstX, firstY);
2213
2220
  ctx.lineTo(secondX, secondY);
2214
2221
  ctx.stroke();
2215
- drawDrawingHandle(firstX, firstY, drawing.color);
2216
- drawDrawingHandle(secondX, secondY, drawing.color);
2222
+ handleAt(firstX, firstY, drawing.color);
2223
+ handleAt(secondX, secondY, drawing.color);
2217
2224
  if (drawing.label) {
2218
2225
  const midX = (firstX + secondX) / 2;
2219
2226
  const midY = (firstY + secondY) / 2;
@@ -2242,8 +2249,8 @@ function createChart(element, options = {}) {
2242
2249
  ctx.moveTo(firstX, firstY);
2243
2250
  ctx.lineTo(endX, endY);
2244
2251
  ctx.stroke();
2245
- drawDrawingHandle(firstX, firstY, drawing.color);
2246
- drawDrawingHandle(secondX, secondY, drawing.color);
2252
+ handleAt(firstX, firstY, drawing.color);
2253
+ handleAt(secondX, secondY, drawing.color);
2247
2254
  if (drawing.label) {
2248
2255
  const midX = (firstX + secondX) / 2;
2249
2256
  const midY = (firstY + secondY) / 2;
@@ -2297,8 +2304,8 @@ function createChart(element, options = {}) {
2297
2304
  ctx.lineTo(secondX, secondY);
2298
2305
  ctx.stroke();
2299
2306
  ctx.restore();
2300
- drawDrawingHandle(firstX, firstY, drawing.color);
2301
- drawDrawingHandle(secondX, secondY, drawing.color);
2307
+ handleAt(firstX, firstY, drawing.color);
2308
+ handleAt(secondX, secondY, drawing.color);
2302
2309
  const prevFont = ctx.font;
2303
2310
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2304
2311
  levelLines.forEach((level, index) => {
@@ -2337,12 +2344,12 @@ function createChart(element, options = {}) {
2337
2344
  }
2338
2345
  ctx.stroke();
2339
2346
  ctx.restore();
2340
- drawDrawingHandle(x0, y0, drawing.color);
2341
- drawDrawingHandle(x1, y1, drawing.color);
2347
+ handleAt(x0, y0, drawing.color);
2348
+ handleAt(x1, y1, drawing.color);
2342
2349
  if (p2) {
2343
2350
  const x2 = xFromDrawingPoint(p2);
2344
2351
  const y2 = yFromPrice(p2.price);
2345
- drawDrawingHandle(x2, y2, drawing.color);
2352
+ handleAt(x2, y2, drawing.color);
2346
2353
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
2347
2354
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
2348
2355
  const move = p1.price - p0.price;
@@ -2422,61 +2429,63 @@ function createChart(element, options = {}) {
2422
2429
  ctx.fillStyle = lossFill;
2423
2430
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2424
2431
  ctx.restore();
2425
- ctx.save();
2426
- ctx.setLineDash([]);
2427
- ctx.lineWidth = Math.max(1, drawing.width);
2428
- ctx.strokeStyle = profitLine;
2429
- ctx.beginPath();
2430
- ctx.moveTo(crisp(boxX0), crisp(targetY));
2431
- ctx.lineTo(crisp(boxX1), crisp(targetY));
2432
- ctx.stroke();
2433
- ctx.strokeStyle = lossLine;
2434
- ctx.beginPath();
2435
- ctx.moveTo(crisp(boxX0), crisp(stopY));
2436
- ctx.lineTo(crisp(boxX1), crisp(stopY));
2437
- ctx.stroke();
2438
- ctx.strokeStyle = drawing.color;
2439
- ctx.beginPath();
2440
- ctx.moveTo(crisp(boxX0), crisp(entryY));
2441
- ctx.lineTo(crisp(boxX1), crisp(entryY));
2442
- ctx.stroke();
2443
- ctx.restore();
2444
- drawDrawingHandle(leftX, targetY, drawing.color);
2445
- drawDrawingHandle(leftX, entryY, drawing.color);
2446
- drawDrawingHandle(leftX, stopY, drawing.color);
2447
- drawDrawingHandle(rightX, entryY, drawing.color);
2448
- const tick = getConfiguredTickSize();
2449
- const pctOf = (price) => {
2450
- if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2451
- return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2452
- };
2453
- const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2454
- const targetDist = Math.abs(target.price - entry.price);
2455
- const stopDist = Math.abs(entry.price - stop.price);
2456
- const rr = stopDist > 0 ? targetDist / stopDist : 0;
2457
- const cx = (boxX0 + boxX1) / 2;
2458
- const drawPositionPill = (text, centerX, centerY, bg) => {
2459
- const prevFont = ctx.font;
2460
- ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2461
- const padding = 6;
2462
- const textW = ctx.measureText(text).width;
2463
- const pillW = textW + padding * 2;
2464
- const pillH = 18;
2465
- const pillX = centerX - pillW / 2;
2466
- const pillY = centerY - pillH / 2;
2467
- ctx.fillStyle = bg;
2468
- fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2469
- ctx.fillStyle = labelTextColor;
2470
- ctx.textAlign = "center";
2471
- ctx.textBaseline = "middle";
2472
- ctx.fillText(text, centerX, pillY + pillH / 2);
2473
- ctx.font = prevFont;
2474
- };
2475
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2476
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2477
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2478
- if (drawing.label) {
2479
- drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2432
+ if (isSelected) {
2433
+ ctx.save();
2434
+ ctx.setLineDash([]);
2435
+ ctx.lineWidth = Math.max(1, drawing.width);
2436
+ ctx.strokeStyle = profitLine;
2437
+ ctx.beginPath();
2438
+ ctx.moveTo(crisp(boxX0), crisp(targetY));
2439
+ ctx.lineTo(crisp(boxX1), crisp(targetY));
2440
+ ctx.stroke();
2441
+ ctx.strokeStyle = lossLine;
2442
+ ctx.beginPath();
2443
+ ctx.moveTo(crisp(boxX0), crisp(stopY));
2444
+ ctx.lineTo(crisp(boxX1), crisp(stopY));
2445
+ ctx.stroke();
2446
+ ctx.strokeStyle = drawing.color;
2447
+ ctx.beginPath();
2448
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2449
+ ctx.lineTo(crisp(boxX1), crisp(entryY));
2450
+ ctx.stroke();
2451
+ ctx.restore();
2452
+ handleAt(leftX, targetY, drawing.color);
2453
+ handleAt(leftX, entryY, drawing.color);
2454
+ handleAt(leftX, stopY, drawing.color);
2455
+ handleAt(rightX, entryY, drawing.color);
2456
+ const tick = getConfiguredTickSize();
2457
+ const pctOf = (price) => {
2458
+ if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2459
+ return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2460
+ };
2461
+ const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2462
+ const targetDist = Math.abs(target.price - entry.price);
2463
+ const stopDist = Math.abs(entry.price - stop.price);
2464
+ const rr = stopDist > 0 ? targetDist / stopDist : 0;
2465
+ const cx = (boxX0 + boxX1) / 2;
2466
+ const drawPositionPill = (text, centerX, centerY, bg) => {
2467
+ const prevFont = ctx.font;
2468
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2469
+ const padding = 6;
2470
+ const textW = ctx.measureText(text).width;
2471
+ const pillW = textW + padding * 2;
2472
+ const pillH = 18;
2473
+ const pillX = centerX - pillW / 2;
2474
+ const pillY = centerY - pillH / 2;
2475
+ ctx.fillStyle = bg;
2476
+ fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2477
+ ctx.fillStyle = labelTextColor;
2478
+ ctx.textAlign = "center";
2479
+ ctx.textBaseline = "middle";
2480
+ ctx.fillText(text, centerX, pillY + pillH / 2);
2481
+ ctx.font = prevFont;
2482
+ };
2483
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2484
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2485
+ drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2486
+ if (drawing.label) {
2487
+ drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2488
+ }
2480
2489
  }
2481
2490
  }
2482
2491
  }
@@ -3934,6 +3943,13 @@ function createChart(element, options = {}) {
3934
3943
  }
3935
3944
  drawingToolDefaults.set(tool, { ...defaults });
3936
3945
  };
3946
+ const setSelectedDrawing = (id) => {
3947
+ if (selectedDrawingId === id) {
3948
+ return;
3949
+ }
3950
+ selectedDrawingId = id;
3951
+ draw();
3952
+ };
3937
3953
  const updateDrawingDrag = (x, y) => {
3938
3954
  if (!drawingDragState) {
3939
3955
  return false;
@@ -4115,6 +4131,7 @@ function createChart(element, options = {}) {
4115
4131
  if (region === "plot" && !activeDrawingTool) {
4116
4132
  const drawingHit = getDrawingHit(point.x, point.y);
4117
4133
  if (drawingHit) {
4134
+ selectedDrawingId = drawingHit.drawing.id;
4118
4135
  drawingSelectHandler?.({
4119
4136
  drawing: serializeDrawing(drawingHit.drawing),
4120
4137
  target: drawingHit.target,
@@ -4424,6 +4441,7 @@ function createChart(element, options = {}) {
4424
4441
  emitCrosshairMove(point.x, point.y, "plot");
4425
4442
  }
4426
4443
  } else if (!pointerDownInfo.moved) {
4444
+ selectedDrawingId = null;
4427
4445
  const clickPrice = pointerDownInfo.region === "plot" ? roundToPricePrecision(priceFromCanvasY(pointerDownInfo.y)) : void 0;
4428
4446
  chartClickHandler?.({
4429
4447
  x: pointerDownInfo.x,
@@ -4846,6 +4864,7 @@ function createChart(element, options = {}) {
4846
4864
  clearDrawings,
4847
4865
  onDrawingsChange,
4848
4866
  onDrawingSelect,
4867
+ setSelectedDrawing,
4849
4868
  onDrawingHover,
4850
4869
  setDrawingDefaults,
4851
4870
  setDoubleClickEnabled,
package/dist/index.d.cts CHANGED
@@ -422,6 +422,7 @@ interface ChartInstance {
422
422
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
423
423
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
424
424
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
425
+ setSelectedDrawing: (id: string | null) => void;
425
426
  setDoubleClickEnabled: (enabled: boolean) => void;
426
427
  setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
427
428
  registerIndicator: (plugin: IndicatorPlugin<any>) => void;
package/dist/index.d.ts CHANGED
@@ -422,6 +422,7 @@ interface ChartInstance {
422
422
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
423
423
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
424
424
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
425
+ setSelectedDrawing: (id: string | null) => void;
425
426
  setDoubleClickEnabled: (enabled: boolean) => void;
426
427
  setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
427
428
  registerIndicator: (plugin: IndicatorPlugin<any>) => void;
package/dist/index.js CHANGED
@@ -958,6 +958,7 @@ function createChart(element, options = {}) {
958
958
  let drawingSelectHandler = null;
959
959
  let drawingHoverHandler = null;
960
960
  let lastHoveredDrawingId = null;
961
+ let selectedDrawingId = null;
961
962
  const drawingToolDefaults = /* @__PURE__ */ new Map();
962
963
  const getDrawingToolDefaults = (tool) => drawingToolDefaults.get(tool) ?? {};
963
964
  const emitDrawingsChange = () => {
@@ -2136,6 +2137,12 @@ function createChart(element, options = {}) {
2136
2137
  if (!drawing.visible) {
2137
2138
  return;
2138
2139
  }
2140
+ const isSelected = draft || drawing.id === selectedDrawingId;
2141
+ const handleAt = (hx, hy, _color) => {
2142
+ if (isSelected) {
2143
+ drawDrawingHandle(hx, hy, drawing.color);
2144
+ }
2145
+ };
2139
2146
  ctx.save();
2140
2147
  ctx.strokeStyle = drawing.color;
2141
2148
  ctx.lineWidth = drawing.width;
@@ -2154,7 +2161,7 @@ function createChart(element, options = {}) {
2154
2161
  ctx.moveTo(crisp(chartLeft), crisp(y));
2155
2162
  ctx.lineTo(crisp(chartRight), crisp(y));
2156
2163
  ctx.stroke();
2157
- drawDrawingHandle(handleX, y, drawing.color);
2164
+ handleAt(handleX, y, drawing.color);
2158
2165
  if (drawing.label) {
2159
2166
  const midX = (chartLeft + chartRight) / 2;
2160
2167
  drawDrawingLabel(drawing.label, midX, y, drawing.color);
@@ -2169,7 +2176,7 @@ function createChart(element, options = {}) {
2169
2176
  ctx.lineTo(crisp(x), crisp(chartBottom));
2170
2177
  ctx.stroke();
2171
2178
  const handleY = (chartTop + chartBottom) / 2;
2172
- drawDrawingHandle(x, handleY, drawing.color);
2179
+ handleAt(x, handleY, drawing.color);
2173
2180
  if (drawing.label) {
2174
2181
  drawDrawingLabel(drawing.label, x, chartTop + 16, drawing.color);
2175
2182
  }
@@ -2186,8 +2193,8 @@ function createChart(element, options = {}) {
2186
2193
  ctx.moveTo(firstX, firstY);
2187
2194
  ctx.lineTo(secondX, secondY);
2188
2195
  ctx.stroke();
2189
- drawDrawingHandle(firstX, firstY, drawing.color);
2190
- drawDrawingHandle(secondX, secondY, drawing.color);
2196
+ handleAt(firstX, firstY, drawing.color);
2197
+ handleAt(secondX, secondY, drawing.color);
2191
2198
  if (drawing.label) {
2192
2199
  const midX = (firstX + secondX) / 2;
2193
2200
  const midY = (firstY + secondY) / 2;
@@ -2216,8 +2223,8 @@ function createChart(element, options = {}) {
2216
2223
  ctx.moveTo(firstX, firstY);
2217
2224
  ctx.lineTo(endX, endY);
2218
2225
  ctx.stroke();
2219
- drawDrawingHandle(firstX, firstY, drawing.color);
2220
- drawDrawingHandle(secondX, secondY, drawing.color);
2226
+ handleAt(firstX, firstY, drawing.color);
2227
+ handleAt(secondX, secondY, drawing.color);
2221
2228
  if (drawing.label) {
2222
2229
  const midX = (firstX + secondX) / 2;
2223
2230
  const midY = (firstY + secondY) / 2;
@@ -2271,8 +2278,8 @@ function createChart(element, options = {}) {
2271
2278
  ctx.lineTo(secondX, secondY);
2272
2279
  ctx.stroke();
2273
2280
  ctx.restore();
2274
- drawDrawingHandle(firstX, firstY, drawing.color);
2275
- drawDrawingHandle(secondX, secondY, drawing.color);
2281
+ handleAt(firstX, firstY, drawing.color);
2282
+ handleAt(secondX, secondY, drawing.color);
2276
2283
  const prevFont = ctx.font;
2277
2284
  ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2278
2285
  levelLines.forEach((level, index) => {
@@ -2311,12 +2318,12 @@ function createChart(element, options = {}) {
2311
2318
  }
2312
2319
  ctx.stroke();
2313
2320
  ctx.restore();
2314
- drawDrawingHandle(x0, y0, drawing.color);
2315
- drawDrawingHandle(x1, y1, drawing.color);
2321
+ handleAt(x0, y0, drawing.color);
2322
+ handleAt(x1, y1, drawing.color);
2316
2323
  if (p2) {
2317
2324
  const x2 = xFromDrawingPoint(p2);
2318
2325
  const y2 = yFromPrice(p2.price);
2319
- drawDrawingHandle(x2, y2, drawing.color);
2326
+ handleAt(x2, y2, drawing.color);
2320
2327
  const palette = drawing.colors.length > 0 ? drawing.colors : null;
2321
2328
  const levelColorAt = (index) => palette ? palette[index % palette.length] : drawing.color;
2322
2329
  const move = p1.price - p0.price;
@@ -2396,61 +2403,63 @@ function createChart(element, options = {}) {
2396
2403
  ctx.fillStyle = lossFill;
2397
2404
  ctx.fillRect(boxX0, Math.min(entryY, stopY), boxW, Math.abs(stopY - entryY));
2398
2405
  ctx.restore();
2399
- ctx.save();
2400
- ctx.setLineDash([]);
2401
- ctx.lineWidth = Math.max(1, drawing.width);
2402
- ctx.strokeStyle = profitLine;
2403
- ctx.beginPath();
2404
- ctx.moveTo(crisp(boxX0), crisp(targetY));
2405
- ctx.lineTo(crisp(boxX1), crisp(targetY));
2406
- ctx.stroke();
2407
- ctx.strokeStyle = lossLine;
2408
- ctx.beginPath();
2409
- ctx.moveTo(crisp(boxX0), crisp(stopY));
2410
- ctx.lineTo(crisp(boxX1), crisp(stopY));
2411
- ctx.stroke();
2412
- ctx.strokeStyle = drawing.color;
2413
- ctx.beginPath();
2414
- ctx.moveTo(crisp(boxX0), crisp(entryY));
2415
- ctx.lineTo(crisp(boxX1), crisp(entryY));
2416
- ctx.stroke();
2417
- ctx.restore();
2418
- drawDrawingHandle(leftX, targetY, drawing.color);
2419
- drawDrawingHandle(leftX, entryY, drawing.color);
2420
- drawDrawingHandle(leftX, stopY, drawing.color);
2421
- drawDrawingHandle(rightX, entryY, drawing.color);
2422
- const tick = getConfiguredTickSize();
2423
- const pctOf = (price) => {
2424
- if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2425
- return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2426
- };
2427
- const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2428
- const targetDist = Math.abs(target.price - entry.price);
2429
- const stopDist = Math.abs(entry.price - stop.price);
2430
- const rr = stopDist > 0 ? targetDist / stopDist : 0;
2431
- const cx = (boxX0 + boxX1) / 2;
2432
- const drawPositionPill = (text, centerX, centerY, bg) => {
2433
- const prevFont = ctx.font;
2434
- ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2435
- const padding = 6;
2436
- const textW = ctx.measureText(text).width;
2437
- const pillW = textW + padding * 2;
2438
- const pillH = 18;
2439
- const pillX = centerX - pillW / 2;
2440
- const pillY = centerY - pillH / 2;
2441
- ctx.fillStyle = bg;
2442
- fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2443
- ctx.fillStyle = labelTextColor;
2444
- ctx.textAlign = "center";
2445
- ctx.textBaseline = "middle";
2446
- ctx.fillText(text, centerX, pillY + pillH / 2);
2447
- ctx.font = prevFont;
2448
- };
2449
- drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2450
- drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2451
- drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2452
- if (drawing.label) {
2453
- drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2406
+ if (isSelected) {
2407
+ ctx.save();
2408
+ ctx.setLineDash([]);
2409
+ ctx.lineWidth = Math.max(1, drawing.width);
2410
+ ctx.strokeStyle = profitLine;
2411
+ ctx.beginPath();
2412
+ ctx.moveTo(crisp(boxX0), crisp(targetY));
2413
+ ctx.lineTo(crisp(boxX1), crisp(targetY));
2414
+ ctx.stroke();
2415
+ ctx.strokeStyle = lossLine;
2416
+ ctx.beginPath();
2417
+ ctx.moveTo(crisp(boxX0), crisp(stopY));
2418
+ ctx.lineTo(crisp(boxX1), crisp(stopY));
2419
+ ctx.stroke();
2420
+ ctx.strokeStyle = drawing.color;
2421
+ ctx.beginPath();
2422
+ ctx.moveTo(crisp(boxX0), crisp(entryY));
2423
+ ctx.lineTo(crisp(boxX1), crisp(entryY));
2424
+ ctx.stroke();
2425
+ ctx.restore();
2426
+ handleAt(leftX, targetY, drawing.color);
2427
+ handleAt(leftX, entryY, drawing.color);
2428
+ handleAt(leftX, stopY, drawing.color);
2429
+ handleAt(rightX, entryY, drawing.color);
2430
+ const tick = getConfiguredTickSize();
2431
+ const pctOf = (price) => {
2432
+ if (!Number.isFinite(entry.price) || entry.price === 0) return "0.00%";
2433
+ return `${((price - entry.price) / Math.abs(entry.price) * 100).toFixed(2)}%`;
2434
+ };
2435
+ const ticksOf = (price) => tick > 0 ? ` ${Math.round(Math.abs(price - entry.price) / tick)}t` : "";
2436
+ const targetDist = Math.abs(target.price - entry.price);
2437
+ const stopDist = Math.abs(entry.price - stop.price);
2438
+ const rr = stopDist > 0 ? targetDist / stopDist : 0;
2439
+ const cx = (boxX0 + boxX1) / 2;
2440
+ const drawPositionPill = (text, centerX, centerY, bg) => {
2441
+ const prevFont = ctx.font;
2442
+ ctx.font = `500 11px ${mergedOptions.fontFamily}`;
2443
+ const padding = 6;
2444
+ const textW = ctx.measureText(text).width;
2445
+ const pillW = textW + padding * 2;
2446
+ const pillH = 18;
2447
+ const pillX = centerX - pillW / 2;
2448
+ const pillY = centerY - pillH / 2;
2449
+ ctx.fillStyle = bg;
2450
+ fillRoundedRect(pillX, pillY, pillW, pillH, 4);
2451
+ ctx.fillStyle = labelTextColor;
2452
+ ctx.textAlign = "center";
2453
+ ctx.textBaseline = "middle";
2454
+ ctx.fillText(text, centerX, pillY + pillH / 2);
2455
+ ctx.font = prevFont;
2456
+ };
2457
+ drawPositionPill(`Target ${formatPrice(target.price)} (${pctOf(target.price)})${ticksOf(target.price)}`, cx, targetY, profitLine);
2458
+ drawPositionPill(`Stop ${formatPrice(stop.price)} (${pctOf(stop.price)})${ticksOf(stop.price)}`, cx, stopY, lossLine);
2459
+ drawPositionPill(`Entry ${formatPrice(entry.price)} RR ${rr.toFixed(2)}`, cx, entryY, hexToRgba(drawing.color, 0.92));
2460
+ if (drawing.label) {
2461
+ drawDrawingLabel(drawing.label, cx, Math.min(targetY, stopY) - 4, drawing.color);
2462
+ }
2454
2463
  }
2455
2464
  }
2456
2465
  }
@@ -3908,6 +3917,13 @@ function createChart(element, options = {}) {
3908
3917
  }
3909
3918
  drawingToolDefaults.set(tool, { ...defaults });
3910
3919
  };
3920
+ const setSelectedDrawing = (id) => {
3921
+ if (selectedDrawingId === id) {
3922
+ return;
3923
+ }
3924
+ selectedDrawingId = id;
3925
+ draw();
3926
+ };
3911
3927
  const updateDrawingDrag = (x, y) => {
3912
3928
  if (!drawingDragState) {
3913
3929
  return false;
@@ -4089,6 +4105,7 @@ function createChart(element, options = {}) {
4089
4105
  if (region === "plot" && !activeDrawingTool) {
4090
4106
  const drawingHit = getDrawingHit(point.x, point.y);
4091
4107
  if (drawingHit) {
4108
+ selectedDrawingId = drawingHit.drawing.id;
4092
4109
  drawingSelectHandler?.({
4093
4110
  drawing: serializeDrawing(drawingHit.drawing),
4094
4111
  target: drawingHit.target,
@@ -4398,6 +4415,7 @@ function createChart(element, options = {}) {
4398
4415
  emitCrosshairMove(point.x, point.y, "plot");
4399
4416
  }
4400
4417
  } else if (!pointerDownInfo.moved) {
4418
+ selectedDrawingId = null;
4401
4419
  const clickPrice = pointerDownInfo.region === "plot" ? roundToPricePrecision(priceFromCanvasY(pointerDownInfo.y)) : void 0;
4402
4420
  chartClickHandler?.({
4403
4421
  x: pointerDownInfo.x,
@@ -4820,6 +4838,7 @@ function createChart(element, options = {}) {
4820
4838
  clearDrawings,
4821
4839
  onDrawingsChange,
4822
4840
  onDrawingSelect,
4841
+ setSelectedDrawing,
4823
4842
  onDrawingHover,
4824
4843
  setDrawingDefaults,
4825
4844
  setDoubleClickEnabled,
package/docs/API.md CHANGED
@@ -481,6 +481,7 @@ Use `getDrawings()` / `setDrawings()` for persistence.
481
481
  - `panY(priceDelta: number): void` (positive = move viewport up)
482
482
  - `fitContent(): void` (x-only fit, keeps y zoom)
483
483
  - `resetViewport(): void` (fit x + reset y auto-scale)
484
+ - `setSelectedDrawing(id: string | null): void` (marks a drawing selected; only the selected/drafted drawing renders its handles, and position tools render their lines/labels only while selected)
484
485
  - `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
485
486
  - `getActiveDrawingTool(): DrawingToolType | null`
486
487
  - `setDrawings(drawings: DrawingObjectOptions[]): void`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",