aai agency logo
OG Components Docs

CHART · CHARTGROUP

Composable time-series analysis

Build independent line and bar charts over one synchronized time domain. Forecasts are ordinary series, derived panels resolve their inputs by stable ID, and every panel keeps its own Y-axis and controls.

Preview

Hover any panel for synchronized tooltips. Zoom the monthly production view into May 2025 to reveal the daily pressure and hourly vibration detail. Open a panel's settings to change its controls or apply presentation mode everywhere.

MONTHLYActual + forecast
DERIVEDID-based variance
DAILYTubing pressure
HOURLYPump vibration
Production — actuals & forecastsline
  • Oil actual
  • Oil forecast
  • Gas actual
  • Gas forecast
  • Water actual
Plan variance — derived from oil.actual + oil.planbar
  • Oil vs plan
Tubing pressure — dailyline
  • Tubing pressure
Pump vibration — hourlyline
  • Pump vibration
Hover a shaded annotation to read its operational context.Independent Y · shared X

Composition model

1 · Registry

Give each source a stable ID such as oil.actual or pressure.actual.

2 · Panels

Each chart selects source IDs or declares a derived series and its dependencies.

3 · Group

ChartGroup synchronizes time, cursor, and annotations—not unrelated Y scales.

Installation

bash
pnpm add @aai-agency/og-components

Usage

tsx
import {
  Chart,
  ChartGroup,
  type ChartConfig,
} from "@aai-agency/og-components/chart";

// One independently configured panel
<Chart
  kind="line"
  id="production"
  label="Production"
  series={series}
/>;

// The same series schema can render as bars
<Chart kind="bar" series={series} />;

const charts: ChartConfig[] = [
  {
    id: "production",
    label: "Production — actuals & forecasts",
    kind: "line",
    series: ["oil.actual", "oil.forecast", "gas.actual"],
    inheritAnnotations: true,
  },
  {
    id: "variance",
    label: "Plan variance",
    kind: "bar",
    symmetricY: true,
    series: [{
      id: "oil.variance",
      label: "Oil vs plan",
      sourceSeriesIds: ["oil.actual", "oil.plan"],
      derive: ({ time, getSeries }) => {
        const actual = getSeries("oil.actual");
        const plan = getSeries("oil.plan");

        return time.map((_, index) => {
          const observed = actual?.values[index];
          const expected = plan?.values[index];
          return observed == null || expected == null
            ? null
            : observed - expected;
        });
      },
      unit: "BBL/d",
    }],
  },
];

export function WellWorkbench({ series, annotations }) {
  return (
    <ChartGroup
      series={series}
      charts={charts}
      annotations={annotations}
      timeZone="America/Chicago"
    />
  );
}

Formatting and typography

Formatters receive semantic context, so one function can distinguish axes from tooltips and resolve a panel, series, unit, or left/right axis. Typography accepts both font sizes and font weights.

tsx
<ChartGroup
  series={series}
  charts={charts}
  formatXValue={(timestamp) =>
    new Intl.DateTimeFormat("en-US", {
      month: "short",
      year: "numeric",
    }).format(new Date(timestamp * 1000))
  }
  formatYValue={(value, context) => {
    const formatted = Intl.NumberFormat("en-US", {
      notation: Math.abs(value) >= 10_000 ? "compact" : "standard",
      maximumFractionDigits: context.unit === "mm/s" ? 2 : 0,
    }).format(value);

    return context.location === "tooltip" && context.unit
      ? `${formatted} ${context.unit}`
      : formatted;
  }}
  typography={{
    fontFamily: "Inter, sans-serif",
    axisTickFontSize: 11,
    axisTickFontWeight: 500,
    axisLabelFontSize: 12,
    axisLabelFontWeight: 600,
    tooltipFontSize: 10,
    tooltipFontWeight: 400,
    tooltipHeaderFontWeight: 600,
    legendFontSize: 10,
    legendFontWeight: 500,
    titleFontSize: 11,
    titleFontWeight: 600,
  }}
/>

Chart and ChartGroup props

PropTypeDefaultDescription
kind"line" | "bar"-Required on Chart. Selects the visual renderer independently from seriesType.
seriesTimeSeries[]-Canonical series registry. Use stable IDs; forecasts use seriesType="forecast".
chartsChartConfig[]-Ordered line or bar panels. Each panel selects source IDs or registers a derived series.
annotationsAnnotation[][]Operational ranges shared with panels that set inheritAnnotations.
timeZonestringUTCIANA time zone used for calendar buckets, axes, and tooltip dates.
formatXValue(timestamp: number) => string-Formats X-axis ticks and the shared tooltip header.
formatYValueChartYValueFormatter-Formats axes and tooltips with chart, series, axis, unit, and location context.
typographyChartTypography-Controls font family, size, and weight for axes, labels, tooltips, legends, and titles.