Components/TimeseriesChart

TimeseriesChart

Time-series lines and stacked bars: clustered markers, threshold lines, range brushing, full tooltips.

VerifiedSince 0.1.0

Usage

data is an array of series; each series' data holds time-ordered [timestamp_ms, value] tuples. color is optional — series fall onto the categorical palette by position, and positions are stable, so hiding one series never recolors the rest. Hovering opens the tooltip: rows sort by value descending, duplicate names dedupe, and values resolve by binary-searching the nearest sample.

Two-series lines

Lines + axis formatting

xAxisTickFormat / tooltipValueFormat customize tick and value text.

Loading demo...

yAxisMinInterval drops ticks closer together than the interval, which suits discrete counts; tooltipFooter appends one line of text below the tooltip rows.

Integer ticks + tooltip footer

yAxisMinInterval 1 keeps integer ticks only; tooltipFooter adds a footer row.

Loading demo...

API Reference

TimeseriesChart Props

PropTypeDefaultDescription
dataTimeseriesData[]—{ name, data: [ts, value][], color? }; missing color falls onto the categorical palette by position.
type'line' | 'bar''line'Bars stack automatically.
markersTimeseriesMarker[]—{ timestamp, label?, description?, color?, lineStyle? }.
thresholdsTimeseriesThreshold[]—{ value, label?, color }; the y domain extends to cover them.
xAxisName / yAxisNamestring—Axis names.
xAxisTickCount / yAxisTickCountnumber5Suggested tick counts.
xAxisTickFormat / yAxisTickFormat(v: number) => string—Tick formatters.
yAxisMinIntervalnumber—Minimum interval between y-axis ticks (ECharts yAxis.minInterval): closer ticks drop, the lowest and highest always stay, and integer ticks stop printing as 1.0 when set to 1.
tooltipValueFormat(v: number) => stringraw valueTooltip value formatter.
tooltipMode'all' | 'single''all'single shows only the series closest to the cursor's y value.
tooltipMaxItemsnumber10Overflow folds into "+N more".
tooltipFollowCursor'both' | 'x''both'x pins the vertical position to avoid jitter.
tooltipFooterstring—Extra line of text appended below the tooltip rows.
tooltipBoundaryTimeseriesTooltipBoundary'clipping-ancestors'Tooltip collision boundary; by default it walks clipping ancestors (offsetParents whose overflow is not visible) and intersects them with the viewport before flipping. The TimeseriesTooltipBoundary type is exported from the @talex-touch/tuffex/charts entry.
incomplete{ before?, after? }—Incomplete periods (line type only).
gradientbooleanfalseGradient fill under lines.
loadingbooleanfalseSkeleton state.
highlightedSeriesstring | null—Emphasized series; the rest dim to 10% opacity.
heightnumber350Pixel height.
widthnumbermeasuredExplicit width (SSR/tests).
ariaDescriptionstring—Accessible description (svg role="img").
clusterLabel(n: number) => stringn => `${n} changes`Cluster label wording.
timestampFormat(ts: number) => stringcompact localeTooltip timestamp format.

TimeseriesChart Events

EventPayloadDescription
time-range-change(from: number, to: number)Brush finished. Attaching it enables brushing.
update:hiddenSeries(names: string[])v-model:hidden-series write-back.

Markers & Thresholds

markers draw vertical dashed lines on the time axis; markers that crowd together cluster into one line based on the visible span, the label becomes "N changes" (clusterLabel overrides the wording), and hovering the line lists each marker's label and description. thresholds draw horizontal lines on the value axis, and the y domain extends automatically to cover them.

Deploy markers + SLO line

Markers + Thresholds

The last two markers sit close together and cluster.

Loading demo...

Gradient & Incomplete Data

gradient adds a vertical fill under each line (series color at 40% → transparent). incomplete declares data outside [before, after] as incomplete: edge segments render dashed and overlap the solid segment by one sample so the line stays connected.

Gradient + dashed edges

Gradient + Incomplete

The first and last three hours are incomplete periods.

Loading demo...

Stacked Bars

With type="bar" every series stacks automatically (kumo's stack: 'total'), and the y domain covers each timestamp's stacked total.

Daily stacks

Stacked bars

Three series share one bar per day.

Loading demo...

Time Range Selection

Attaching a time-range-change listener enables horizontal brushing: drag on the plot to draw a selection; releasing emits (from, to) in milliseconds and clears the selection. Drags under 3px count as clicks and do nothing. The selection rectangle fills with rgba(120, 140, 180, 0.3) and strokes 1px rgba(120, 140, 180, 0.8). While dragging, the plot outside the selection dims too: one luminance <mask> spanning the plot (a white plot rect plus two #4D4D4D strips) presses everything outside the selection to 30%, so each series stays mounted exactly once — equivalent to ECharts outOfBrush.colorAlpha: 0.3.

Drag to select

Brush selection

The readout below shows the selected range.

Loading demo...

Legend Interplay

v-model:hidden-series two-way binds the hidden series names: hidden series neither render nor appear in tooltips. highlighted-series emphasizes one series and dims the rest to 10% opacity (ECharts blur's fromState.opacity * 0.1). Combine both with TxChartLegendItem for a clickable, hover-highlighting legend.

Click to hide + hover to highlight

Legend interplay

Click legend items to toggle; hover to highlight.

Loading demo...

Loading Skeleton

loading swaps the chart for a harmonic-wave skeleton; the line and bar variants share one silhouette, and the shimmer respects prefers-reduced-motion.

Skeleton loop

Loading skeleton

Loading toggles on a loop, alternating the line and bar variants.

Loading demo...

Animation & Interaction

The chart reuses ECharts' default timings (kumo pins echarts ^6.0.0):

  • First render: lines/areas reveal left to right through a clip, 1000ms easings.linear; bars grow out of the base axis (y lerps from baseY to the target, height 0 → target) and scatter symbols scale 0 → radius while fading in, all 1000ms cubicInOut.
  • Data updates: geometry morphs over 500ms cubicInOut.
  • State switches (emphasis / blur): 300ms cubicOut.
  • animationThreshold 2000: a series with more points than that renders its final geometry with no animation.
  • Tooltip: closing waits 100ms, then fades out over 200ms and removes the DOM, so brief pointer excursions do not flicker; the position travels via transform: translate3d() and lets the browser interpolate the 400ms move (cubic-bezier(0.23, 1, 0.32, 1)); pointer tracking is throttled to 50ms, taking one leading and one trailing sample.
  • The plot outside the brush selection dims while dragging and restores on release — see the single <mask> above.
  • Hovering the plot dims every series except the one nearest the cursor's value at that timestamp to 10% opacity; this is a documented approximation of ECharts emphasis.focus: 'series', which only emphasizes over an actual item.
  • Under prefers-reduced-motion: reduce every tween lands on its final frame immediately, and all CSS transitions sit inside @media (prefers-reduced-motion: no-preference).

The @talex-touch/tuffex/charts entry also exports the animation primitives, so bespoke charts can reuse the same timings: easings (linear | cubicIn | cubicOut | cubicInOut), cubicBezier(x1, y1, x2, y2), tween({ duration, easing, delay, onUpdate, onComplete }), useEnterProgress(options), useTweenedNumbers(source, options), prefersReducedMotion(), plus the constants ENTER_DURATION 1000, UPDATE_DURATION 500, STATE_DURATION 300 and ANIMATION_THRESHOLD 2000. The tooltip timing constants (TOOLTIP_FADE_DURATION 200, TOOLTIP_MOVE_DURATION 400, TOOLTIP_HIDE_DELAY 100, TOOLTIP_TRACK_THROTTLE 50, tooltipMoveEasing) live in the charts entry's core/animate.

Differences from kumo

  • enableLegendSelection + imperative echarts actions → declarative v-model:hidden-series; highlighting via highlighted-series.
  • tooltipBoundary performs the real clipping-ancestors collision walk: it intersects clipping ancestors (offsetParents whose overflow is not visible) with the viewport, flips on overflow, and defaults to 'clipping-ancestors'.
  • The brush no longer just draws the rectangle during a drag: one luminance <mask> presses everything outside the selection to 30% while each series stays mounted exactly once.
  • Geometry animation matches ECharts: the pie grows angularly via animationType: 'expansion' and highlights displace a slice by scaleSize 5px; sankey and maps have no geometry animation (SankeyView has no initProps/updateProps, maps set animationDurationUpdate: 0) — parity, not a gap.
  • Series-level hover emphasis is an approximation: ECharts blurs the other series only over an actual item, while this picks the nearest series at the cursor's timestamp.
  • Keep each series under ~5k points: SVG rendering degrades in the tens of thousands.