@adrienhobbs/candlekit 0.2.1 → 0.2.3

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.
package/dist/index.js CHANGED
@@ -327,6 +327,8 @@ function ChartComponent({
327
327
  const mouseDownPosRef = useRef(null);
328
328
  const heightRef = useRef(height);
329
329
  heightRef.current = height;
330
+ const lineEditEnabledRef = useRef(false);
331
+ lineEditEnabledRef.current = Boolean(onAddLine || onClearAllLines);
330
332
  useEffect(() => {
331
333
  if (!chartContainerRef.current) return;
332
334
  chartContainerRef.current.style.position = "relative";
@@ -344,6 +346,12 @@ function ChartComponent({
344
346
  vertLines: { color: "#1e293b" },
345
347
  horzLines: { color: "#1e293b" }
346
348
  },
349
+ // Render axis ticks + crosshair in the viewer's LOCAL timezone (lightweight-
350
+ // charts otherwise renders numeric times as UTC, which mismatches any
351
+ // local-time table/labels alongside the chart).
352
+ localization: {
353
+ timeFormatter: localCrosshairTimeFormatter
354
+ },
347
355
  width: chartContainerRef.current.clientWidth,
348
356
  // Auto-fill the container's height unless an explicit `height` is given.
349
357
  // Fall back to 600 only when the container hasn't been laid out yet (a
@@ -352,7 +360,8 @@ function ChartComponent({
352
360
  timeScale: {
353
361
  timeVisible: true,
354
362
  secondsVisible: true,
355
- borderColor: "#334155"
363
+ borderColor: "#334155",
364
+ tickMarkFormatter: localTickMarkFormatter
356
365
  },
357
366
  rightPriceScale: {
358
367
  borderColor: "#334155"
@@ -416,6 +425,7 @@ function ChartComponent({
416
425
  const resizeObserver = new ResizeObserver(handleResize);
417
426
  resizeObserver.observe(chartContainerRef.current);
418
427
  const handleContextMenu = (e) => {
428
+ if (!lineEditEnabledRef.current) return;
419
429
  e.preventDefault();
420
430
  e.stopPropagation();
421
431
  if (!candlestickSeriesRef.current) {
@@ -527,16 +537,37 @@ function ChartComponent({
527
537
  if (!chartRef.current || !focusTradeId) return;
528
538
  const trade = trades.find((t) => t.id === focusTradeId);
529
539
  if (!trade) return;
530
- const entrySec = trade.entryTime / 1e3;
531
- const exitSec = trade.exitTime / 1e3;
532
- const sorted = [...bars].sort((a, b) => a.timestamp - b.timestamp);
533
- const stepSec = sorted.length > 1 ? (sorted[sorted.length - 1].timestamp - sorted[0].timestamp) / 1e3 / (sorted.length - 1) : 300;
534
- const pad = Math.max(exitSec - entrySec, stepSec * 12);
540
+ const seen = /* @__PURE__ */ new Set();
541
+ const series = [];
542
+ for (const b of [...bars].sort((a, b2) => a.timestamp - b2.timestamp)) {
543
+ if (seen.has(b.timestamp)) continue;
544
+ seen.add(b.timestamp);
545
+ series.push(b.timestamp);
546
+ }
547
+ if (series.length === 0) return;
548
+ const nearestIdx = (ms) => {
549
+ let lo = 0;
550
+ let hi = series.length - 1;
551
+ let idx = series.length - 1;
552
+ while (lo <= hi) {
553
+ const mid = lo + hi >> 1;
554
+ if (series[mid] >= ms) {
555
+ idx = mid;
556
+ hi = mid - 1;
557
+ } else {
558
+ lo = mid + 1;
559
+ }
560
+ }
561
+ return idx;
562
+ };
563
+ const entryIdx = nearestIdx(trade.entryTime);
564
+ const exitIdx = Math.max(entryIdx, nearestIdx(trade.exitTime));
565
+ const PAD = 15;
535
566
  const raf = requestAnimationFrame(() => {
536
567
  try {
537
- chartRef.current?.timeScale().setVisibleRange({
538
- from: entrySec - pad,
539
- to: exitSec + pad
568
+ chartRef.current?.timeScale().setVisibleLogicalRange({
569
+ from: entryIdx - PAD,
570
+ to: exitIdx + PAD
540
571
  });
541
572
  } catch {
542
573
  }
@@ -878,7 +909,7 @@ function ChartComponent({
878
909
  }
879
910
  )
880
911
  ] }),
881
- chartContainerRef.current && lines.map((line) => {
912
+ onDeleteLine && chartContainerRef.current && lines.map((line) => {
882
913
  const pos = linePositions.get(line.id);
883
914
  if (!pos) return null;
884
915
  return createPortal(
@@ -983,6 +1014,31 @@ function getLineStyle(style) {
983
1014
  return LineStyle.Solid;
984
1015
  }
985
1016
  }
1017
+ var pad2 = (n) => String(n).padStart(2, "0");
1018
+ function localTickMarkFormatter(time, tickMarkType) {
1019
+ const d = new Date(time * 1e3);
1020
+ switch (tickMarkType) {
1021
+ case 0:
1022
+ return String(d.getFullYear());
1023
+ case 1:
1024
+ return d.toLocaleString(void 0, { month: "short" });
1025
+ case 2:
1026
+ return d.toLocaleString(void 0, { month: "short", day: "numeric" });
1027
+ case 4:
1028
+ return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
1029
+ default:
1030
+ return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
1031
+ }
1032
+ }
1033
+ function localCrosshairTimeFormatter(time) {
1034
+ return new Date(time * 1e3).toLocaleString(void 0, {
1035
+ month: "short",
1036
+ day: "2-digit",
1037
+ hour: "2-digit",
1038
+ minute: "2-digit",
1039
+ hour12: false
1040
+ });
1041
+ }
986
1042
  var IndicatorCategory = /* @__PURE__ */ ((IndicatorCategory2) => {
987
1043
  IndicatorCategory2["TREND"] = "Trend";
988
1044
  IndicatorCategory2["MOMENTUM"] = "Momentum";