Interactive Components

Add drawing tools, zoom controls, and selections using the interactive package.

Overview

Interactive features live in @reincharts/interactive. They fall into two categories: UI controls and drawing tools.

UI Controls

These don't require state management:

  • ZoomButtons: UI overlay with reset/zoom controls.
  • Brush: Selection rectangle for zooming or custom logic. Uses callback functions.

Drawing Tools

These require state management and callbacks that can be set manually or handled automatically with InteractiveManager:

  • TrendLine: Draw trend lines, rays, or extended lines.
  • FibonacciRetracement: Draw Fibonacci retracement levels between two points.
  • GannFan: Draw Gann fan analysis lines.
  • EquidistantChannel: Draw parallel channel lines at equal distances.
  • StandardDeviationChannel: Draw channel lines based on standard deviation calculations.
  • InteractiveText: Add movable text annotations.
  • InteractiveYCoordinate: Add horizontal reference lines at specific Y values.
  • Drawing: Generic drawing tool for simple shapes.

Note: The examples below show simplified code snippets. For complete examples explore the interactive stories in Storybook under "Tools" sections.

UI Controls

UI Controls (ZoomButtons & Brush)

These components work independently and don't require direct state management:

UI Controls

handleBrush(brushCoords: BrushCoords, moreProps: any) {
    const { start, end } = brushCoords;
    const left = Math.min(start.xValue, end.xValue);
    const right = Math.max(start.xValue, end.xValue);

    const low = Math.min(start.yValue, end.yValue);
    const high = Math.max(start.yValue, end.yValue);

    this.setState({
        xExtents: [left, right],
        yExtents: [low, high],
    });
}

handleReset() {
    this.setState({
        suffix: this.state.suffix + 1
    });
}

<ChartCanvas xExtents={xExtents} seriesName={`MSFT_${this.state.suffix}`}>
    <Chart yExtents={yExtents}>
        <CandlestickSeries />

        <ZoomButtons onReset={this.handleReset} />
        <Brush enabled={isEnabled} onBrush={this.handleBrush} />
    </Chart>
    <CrossHairCursor />
</ChartCanvas>

When using the Brush component, the enabled prop controls whether brush is currently active for selecting an area. After a brush happens the enabled prop should probably be set to false. When using Zoom Buttons you need to set a callback for onReset. Resetting the chart can be accomplished by changing the seriesName prop passed to ChartCanvas. See the "Zoom Buttons" and "Brush" stories for complete examples.

Drawing Tools

Drawing tools for the most part have these common props for controlling their behavior:

  • enabled: boolean - Controls whether the tool is active for drawing/interaction. After an interactive object is placed this should most likely be set to false until you want to place another object.
  • onStart: (e: MouseEvent, moreProps: any) => void - Callback when drawing begins.
  • onComplete: (e: MouseEvent, newItems: any[], moreProps: any) => void - Callback when interaction completes with updated items array. This is the most important callback because newItems must be used to update the current state array that is passed to the component in order to persist updates. More info on this in the Data Props section below.
  • appearance: object - Styling configuration (strokeStyle, strokeWidth, colors, etc.). Exact values differ between components.
  • hoverText: object - Configuration for hover tooltips.

Data Props

Each component accepts an array prop containing the items to render:

  • TrendLine: trends array
  • FibonacciRetracement: retracements array
  • GannFan: fans array
  • EquidistantChannel: channels array
  • StandardDeviationChannel: channels array
  • InteractiveText: textList array
  • InteractiveYCoordinate: yCoordinateList array
  • Drawing: drawings array

These arrays should be stored in state and updated using the onComplete callback. If they are not updated the drawn objects will not change.

Drawing Tools - Manual Usage

To use drawing tools manually, you have to manage their state and callbacks

Drawing Tools
constructor() {
  this.state = {
    trends: [],
    isEnabled: true
  };
}

handleSelection(e: any, interactives: any, moreProps: any) {
  const state = toObject(interactives, (each: any) => {
    return [
      `trends`,
      each.objects,
    ];
  });
  this.setState(state);
}

onComplete(e: any, newTrends: any, moreProps: any) {
  this.setState(prev => ({
    ...prev,
    trends: newTrends,
    isEnabled: false
  }));
}

onKeyPress(e: any) {
  switch (e.which) {
    case 8:// Backspace
    case 46: { // DEL
      const trends = this.state.trends
        .filter((each: any) => !each.selected);

      this.setState({
        trends,
      });
      break;
    }
  }
}

<ChartCanvas>
  <Chart id={1}>
    <CandlestickSeries />

    <TrendLine
      enabled={this.state.isEnabled}
      ref={this.saveInteractiveNodes("TrendLine", 1)} // See stories for implementation of this
      onComplete={this.onComplete}
      trends={this.state.trends}
    />
  </Chart>
  <InteractiveObjectSelector
    enabled
    getInteractiveNodes={this.getInteractiveNodes} // See stories for implementation of this
    onSelect={this.handleSelection}
  />
</ChartCanvas>

For complete examples, see individual tool stories like "Trend Line", "Fibonacci Retracement", "Equidistant Channel", etc.

Key concepts for manual usage

  • Enable Control: Use a boolean prop to control if the tool is active and able create new objects.
  • State Management: Store interactive objects in component state and pass them to the data array prop then update them using onComplete.
  • Ref: Ref must be set in order for InteractiveObjectSelector to work correctly. More on this in the InteractiveObjectSelector section below.

InteractiveObjectSelector

To enable selection and modification of drawn objects, you must include InteractiveObjectSelector. This is also required for the onKeyPress function in the example above to delete the currently selected object.

  • Multi-tool and chart support: Handles selection across different drawing tools in different charts when configured accordingly with getInteractiveNodes/onSelect.
  • Reference tracking system: The selector uses getInteractiveNodes() to access stored tool refs, then calls each tool's getSelectionState() method to determine what objects are currently selected or interactive.
  • getInteractiveNodes: Requires a function that returns references to interactive components. This function provides access to all interactive tool instances so the selector can query their selection state. View an example of how this works in any of the tool stories with saveInteractiveNodes and getInteractiveNodes.

InteractiveManager - Automatic State and Callback Handling

The InteractiveManager automatically controls the state and callbacks of drawing tools (not UI controls like ZoomButtons/Brush) and provides a built-in toolbar:

Interactive Manager
constructor() {
    this.state = {
        interactiveStates: {},
    };
}

const handleInteractiveToggle = (type: any, enabled: any) => {
    console.log(`${type} tool ${enabled ? 'enabled' : 'disabled'}`);
};

<InteractiveManager
    onInteractiveToggle={handleInteractiveToggle}
    height={600}
    width={width}
    onSave={(data) => { console.log("Save Data: ", data); }}
    initialStates={this.state.interactiveStates}
>
    <ChartCanvas>
        <Chart id={1}>
            <CandlestickSeries />
            <Drawing />
            <GannFan />
            <FibonacciRetracement />
            <TrendLine />
            <InteractiveYCoordinate isDraggable />
        </Chart>
        <Chart id={2}>
            <LineSeries />
            <Drawing />
            <EquidistantChannel />
            <StandardDeviationChannel />
            <InteractiveText />
        </Chart>
        <CrossHairCursor />
    </ChartCanvas>
</InteractiveManager>

InteractiveManager features

  • Drawing tools only: Manages TrendLine, FibonacciRetracement, etc. Not ZoomButtons/Brush that work independently.
  • Auto-detection: Automatically finds drawing components in children and creates toolbar buttons.
  • Granular: Can add a specific set tools to each charts and the manager will only let those tools be drawn on that chart.
  • Single-tool activation: Ensures only one drawing tool is active at a time.
  • Built-in toolbar: Provides sidebar with toggle buttons for each detected tool.
  • Keyboard shortcuts: Delete/Backspace removes selected items, Escape deactivates tools.
  • Layout management: Automatically adjusts chart dimensions to account for sidebar space.
  • State persistence: Supports initialStates prop and onSave callback for data persistence.
  • If you don't like the appearance or behavior you can use the source as a base and create a new component with your own custom styling/handling.

InteractiveManager props

  • sidebarPosition: "left" | "right" | "top" | "bottom" - Position of the toolbar.
  • showSidebar: boolean - Whether to display the toolbar.
  • height: number - Height of the chart.
  • width: number - Width of the chart.
  • size: number - Width/height of the sidebar (default: 54px).
  • onInteractiveToggle: (type: string, enabled: boolean) => void - Callback when tools are toggled.
  • onSave: (data: string) => void - Callback for save button with serialized state.
  • initialStates: Record<string, Array<any>> - Initial state for tools.
  • interactiveIcons: Record<string, React.ElementType> - Custom icons for toolbar buttons.

See the "Interactive Manager" story for a complete working example with multiple tools.

Next Steps

Explore the interactive stories in storybook under "Tools" for complete, running examples of all tools: