@adrienhobbs/candlekit 0.2.0 → 0.2.1

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.d.ts CHANGED
@@ -166,8 +166,19 @@ interface ChartComponentProps {
166
166
  trades?: ChartTrade[];
167
167
  selectedTradeId?: string | null;
168
168
  renderTradePopup?: (trade: ChartTrade) => ReactNode;
169
+ /**
170
+ * Fixed chart height in px. When omitted, the chart fills its container's
171
+ * height (observed via ResizeObserver) instead of a fixed default — so a
172
+ * flex/percent-sized parent gets a full-height chart.
173
+ */
174
+ height?: number;
175
+ /**
176
+ * When set, recenter the time scale on this trade (by id) so it scrolls into
177
+ * view. Pairs with `selectedTradeId` to drive both selection and focus.
178
+ */
179
+ focusTradeId?: string | null;
169
180
  }
170
- declare function ChartComponent({ bars, onLoadMoreData, indicators, lines, onBarUpdate, onNewBar, onDeleteLine, onAddLine, onClearAllLines, enableBarSelection, onBarClick, trades, selectedTradeId, renderTradePopup, }: ChartComponentProps): react_jsx_runtime.JSX.Element;
181
+ declare function ChartComponent({ bars, onLoadMoreData, indicators, lines, onBarUpdate, onNewBar, onDeleteLine, onAddLine, onClearAllLines, enableBarSelection, onBarClick, trades, selectedTradeId, renderTradePopup, height, focusTradeId, }: ChartComponentProps): react_jsx_runtime.JSX.Element;
171
182
 
172
183
  interface IndicatorBrowserProps {
173
184
  isOpen: boolean;
package/dist/index.js CHANGED
@@ -299,7 +299,9 @@ function ChartComponent({
299
299
  onBarClick,
300
300
  trades = [],
301
301
  selectedTradeId = null,
302
- renderTradePopup
302
+ renderTradePopup,
303
+ height,
304
+ focusTradeId = null
303
305
  }) {
304
306
  const chartContainerRef = useRef(null);
305
307
  const chartRef = useRef(null);
@@ -323,6 +325,8 @@ function ChartComponent({
323
325
  const barsRef = useRef(bars);
324
326
  const isDraggingRef = useRef(false);
325
327
  const mouseDownPosRef = useRef(null);
328
+ const heightRef = useRef(height);
329
+ heightRef.current = height;
326
330
  useEffect(() => {
327
331
  if (!chartContainerRef.current) return;
328
332
  chartContainerRef.current.style.position = "relative";
@@ -341,7 +345,10 @@ function ChartComponent({
341
345
  horzLines: { color: "#1e293b" }
342
346
  },
343
347
  width: chartContainerRef.current.clientWidth,
344
- height: 600,
348
+ // Auto-fill the container's height unless an explicit `height` is given.
349
+ // Fall back to 600 only when the container hasn't been laid out yet (a
350
+ // ResizeObserver below corrects it on first measure).
351
+ height: height ?? (chartContainerRef.current.clientHeight || 600),
345
352
  timeScale: {
346
353
  timeVisible: true,
347
354
  secondsVisible: true,
@@ -396,9 +403,18 @@ function ChartComponent({
396
403
  });
397
404
  const handleResize = () => {
398
405
  if (chartContainerRef.current && chart) {
399
- chart.applyOptions({ width: chartContainerRef.current.clientWidth });
406
+ const nextHeight = heightRef.current ?? chartContainerRef.current.clientHeight;
407
+ chart.applyOptions({
408
+ width: chartContainerRef.current.clientWidth,
409
+ // Only drive height when auto-filling and the container has a real
410
+ // measured height; otherwise leave the current height untouched.
411
+ ...heightRef.current === void 0 && nextHeight > 0 ? { height: nextHeight } : {},
412
+ ...heightRef.current !== void 0 ? { height: heightRef.current } : {}
413
+ });
400
414
  }
401
415
  };
416
+ const resizeObserver = new ResizeObserver(handleResize);
417
+ resizeObserver.observe(chartContainerRef.current);
402
418
  const handleContextMenu = (e) => {
403
419
  e.preventDefault();
404
420
  e.stopPropagation();
@@ -490,6 +506,7 @@ function ChartComponent({
490
506
  const container = chartContainerRef.current;
491
507
  return () => {
492
508
  window.removeEventListener("resize", handleResize);
509
+ resizeObserver.disconnect();
493
510
  container.removeEventListener("contextmenu", handleContextMenu, true);
494
511
  container.removeEventListener("mousedown", handleMouseDown, true);
495
512
  container.removeEventListener("mousemove", handleMouseMove, true);
@@ -501,6 +518,31 @@ function ChartComponent({
501
518
  useEffect(() => {
502
519
  barsRef.current = bars;
503
520
  }, [bars]);
521
+ useEffect(() => {
522
+ if (chartRef.current && height !== void 0) {
523
+ chartRef.current.applyOptions({ height });
524
+ }
525
+ }, [height]);
526
+ useEffect(() => {
527
+ if (!chartRef.current || !focusTradeId) return;
528
+ const trade = trades.find((t) => t.id === focusTradeId);
529
+ 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);
535
+ const raf = requestAnimationFrame(() => {
536
+ try {
537
+ chartRef.current?.timeScale().setVisibleRange({
538
+ from: entrySec - pad,
539
+ to: exitSec + pad
540
+ });
541
+ } catch {
542
+ }
543
+ });
544
+ return () => cancelAnimationFrame(raf);
545
+ }, [focusTradeId, trades, bars]);
504
546
  useEffect(() => {
505
547
  if (!candlestickSeriesRef.current || !volumeSeriesRef.current) return;
506
548
  const sortedBars = [...bars].sort((a, b) => a.timestamp - b.timestamp);