Catalog npm
Back to packageRELEASE DETAILS

0.2.0

npm

@0k/time-chart

Framework-free linked time-series charts with local calendar guides.

calendarchartechartstime-serieszoom
Published11 Oct 2026
Direct dependencies6
Transitive dependenciesNot calculated

README

Current package documentation

@0k/time-chart

Linked, zoomable time-series charts for the browser, built on Apache ECharts. No framework required.

  • Day, month and year guides that fade in with zoom, weekends hatched, and a "now" line; all in local time, correct across DST changes.
  • Wheel zoom and drag pan on the time axis; charts sharing a link key zoom together.
  • Stacked areas, min/max bands and gaps for missing values.
  • Light and dark themes, following the OS by default.

Install

npm install @0k/time-chart

Usage

import { createTimeChart } from '@0k/time-chart';

const now = Date.now();
const chart = createTimeChart(document.getElementById('chart')!, {
  series: [{
    id: 'sensor-a',
    label: 'Sensor A',
    points: [[now - 3_600_000, 2], [now, 3]],
  }],
  formatValue: value => value.toFixed(2),
  onWindowChange: (start, end) => { /* fetch data for this window */ },
});

chart.setSeries(newSeries);   // keeps the current window and hidden series
chart.setWindow(start, end);
chart.destroy();

The element must have a nonzero size. Times are Unix milliseconds.

Series

FieldDescription
idUnique identifier
labelName shown in the legend and tooltip
points[time, value] pairs sorted by time; null value is a gap
min, maxOptional band bounds, one per point
groupSeries of a group get close colors
stackStack with the other stacked series
colorExplicit CSS color instead of the generated one
dash'solid' (default), 'dashed' or 'dotted' line style
widthFinite positive line width in pixels (default 1); highlighting doubles it, dimming halves it
fillOpacity in (0,1]; fill to the bottom of the y axis, not stacked; cannot be combined with stack: true
band{pattern, opacity} for min / max bounds; pattern is 'solid' (default) or diagonal 'hatch' in the series color; opacity is in (0,1] (default 0.18)
lineDraw the line (default true); false keeps its band/fill and legend entry but excludes it from the tooltip

Options

OptionDefaultDescription
seriesrequiredSeries to draw
windowdata extentInitial {start, end} shown
formatValuelocalized numberFormats y-axis labels and tooltip values
formatTimelocalized date/timeFormats the tooltip time
yAxis{min, max, name, interval, minorInterval}; max: 'data' fixes it at 1.2 × data maximum (default)
yAxis.intervalautomaticFinite positive major tick and label step
yAxis.minorIntervaldisabledFinite positive minor tick step with dashed minor split lines; requires interval and must divide it into an integer count from 2 to 99
tooltip'axis''axis' (all series) or 'item' (one)
legend{show, collapsed, width, maxWidth}, scrolls, on right
legend.width340Column width in pixels, or 'auto' to fit the widest label
legend.maxWidth240 for 'auto'Caps the column; when set, also caps a numeric width
linkCharts with the same key zoom, pan and hover together by time; their 'auto' legends share the widest column
onWindowChangeCalled after the user zooms or pans
onLegendToggleCalled when the user toggles a series
localebrowser localeLocale for labels and default formatters
theme'auto''auto', 'light' or 'dark'

With legend: { width: 'auto' } the legend column is as wide as its widest label: at least 80 pixels (less only if maxWidth is smaller), at most maxWidth; longer labels are ellipsized and shown in full on hover. Labels are measured again when setSeries changes them. Charts sharing a link key and an 'auto' legend all take the widest column of the group, each within its own maxWidth, so with the same maxWidth their plots line up; the group adjusts when one of them changes labels or is destroyed. A chart with a numeric width keeps it.

Charts sharing a link key are linked by time: after a user zoom or pan they all show the same time window, and hovering one puts the others' pointers at the same time, even when their data covers different ranges or different timestamps. With tooltip: 'axis' each linked chart also shows its tooltip, with the values of its own points nearest to that time; with 'item' only the pointer is linked. A chart hides its pointer when the time is outside the span of its points or outside its window. onWindowChange fires on every affected chart after 150 ms without another zoom or pan, with that chart's current window. setWindow changes only the receiving chart and never fires the callback; set it on each linked chart when choosing a range programmatically.

Methods

MethodDescription
setSeries(series)Replace the data
setWindow(start, end)Show this time range (no onWindowChange)
getWindow()Current {start, end}
setHidden(ids)Hide these series
highlight(ids)Emphasize these series; null resets
setLegendCollapsed(bool)Hide or show the legend
resize()Redraw after a size change
destroy()Release the chart; safe to call twice

calendarGuides(start, end, widthPx, now, locale?) is also exported: it returns the guide lines and weekend bands for a time range, without drawing anything.

Development

npm install
npm run check   # typecheck, tests, build
npm run demo    # http://127.0.0.1:5187/demo/

To use a working copy from another project without changing its committed files:

npm run watch                                # here
npm install --no-save /path/to/time-chart    # in the other project

License

MIT

Changelog

0.2.0 (2026-10-11)

New

  • Fit the legend to its labels, keeping linked charts aligned. [Valentin Lab]

    With legend: { width: 'auto' } the legend column fits its widest label, measured in the legend's font, between 80 pixels and legend.maxWidth (240 by default; an explicit maxWidth below 80 wins); longer labels are ellipsized. Charts sharing a link key with auto legends all take the group's widest column, each within its own maxWidth, so with the same maxWidth their plots line up; they adjust when one of them changes labels or is destroyed.

    maxWidth also caps a numeric width; an invalid value for either now throws. The demo charts switch to auto legends.

  • Choose the step between y-axis labels, with dashed minor lines. [Valentin Lab]

    yAxis.interval sets the step between y-axis labels, and yAxis.minorInterval subdivides it with dashed minor lines. Invalid values throw: minorInterval requires interval and must divide it into an integer count from 2 to 99 (ECharts ignores more). Minor ticks are drawn in the palette's axis color, so they follow the light and dark themes.

  • Draw dashed, dotted, thick, filled, hatched and line-less series. [Valentin Lab]

    Each series can now set its line style with dash ('solid', 'dashed' or 'dotted') and width in pixels, which highlighting doubles and dimming halves. fill shades the area down to the bottom of the y axis at the given opacity, without stacking. band: {pattern, opacity} draws the min/max band solid or hatched diagonally in the series color, at a chosen opacity. line: false draws only a series' fill or band: it keeps its legend entry but is left out of the tooltip.

    Invalid widths and opacities throw, as do fill with stack: true and band on a series without min and max.

Changes

  • Depend on ECharts 6, which npm audit no longer flags. [Valentin Lab]

    ECharts 5 has a security advisory (GHSA-fgmj-fm8m-jvvx, an XSS in the tooltip of lines series) that only 6.1.0 fixes, and 5.x gets no more fixes. The charts drawn here never used a lines series, but every consumer's npm audit reported it, with no way to resolve it.

    Two ECharts 6 defaults are pinned back so the charts look as before:

    • grid.outerBoundsMode: "none": ECharts 6 shrinks the plot to fit wide axis labels, which would move the plot away from the calendar guides, the legend and the alignment of linked charts.
    • yAxis.nameMoveOverlap: false: ECharts 6 pushes the y-axis name past its labels, off the canvas; it stays 46 px from the axis line.

    AGENTS.md now names ~/dev/ts/rrd-graph as the consumer that uses this working copy.

    Contract:

    • echarts is ^6.1.0; the public API and options are unchanged.
    • The plot fills exactly the grid insets, whatever the label widths.
    • The y-axis name is drawn 46 px left of the axis line.
    • Tests: test/option.test.ts; demo/verify.py passes in Chrome, and the demo renders as with ECharts 5.6 apart from ECharts' own time-axis labelling.

Fix

  • Link charts by time, so zoom, pan and hover line up when their data differs. [Valentin Lab]

    echarts.connect relays a zoom as a percentage of each chart's own time axis, and a hover as a data index of the hovered chart. Linked charts whose data covered different ranges therefore showed different windows, and hovering one put the other charts' pointers and tooltips at other times, sometimes 600 px away, or nowhere.

    The link registry now relays by time instead of using connect:

    • a user zoom or pan sends its time window to every linked chart, which moves only its zoom (its axis grows if the window falls outside it);
    • a hover sends the hovered time; each linked chart draws its pointer there, with its axis tooltip showing its own nearest values, or nothing when the time is outside the span of its points or its window. The pointer does not snap to the nearest point (that would differ between charts), and it also links with tooltip: "item", without a tooltip.

    Contract:

    • After a user zoom or pan, every chart of a link group shows the same window and fires onWindowChange once, after 150 ms; no echo.
    • setWindow and setSeries never move another chart's window or pointer; only a change of the shared auto-legend width redraws them.
    • A hover points every linked chart exactly at the hovered time, or hides its pointer outside its data's span or window; leaving hides them all.
    • Tests: test/link.test.ts (real ECharts), test/chart.test.ts, demo/verify.py in Chrome.
  • Let a long legend scroll, aligned with the top of the plot. [Valentin Lab]

    The legend was the canvas legend of echarts, which can only page with small arrow buttons when its entries do not fit: the mouse wheel over it zoomed the chart instead, and its first entry sat a few pixels below the plot's top edge.

    The legend is now an HTML list over the chart's right margin. It spans exactly the plot's height, scrolls with the wheel without zooming the chart or scrolling the page, and keeps its scroll position across redraws. Long labels are truncated clear of overlay scrollbars and show their full text on hover. Clicking an entry, or pressing Space or Enter on it, still hides or shows its series and calls onLegendToggle; hidden series are greyed out. The options and API are unchanged.

    A host element with position: static is switched to relative so the legend can be placed inside it.

0.1.2 (2026-10-08)

New

  • Draw linked, zoomable time-series charts with calendar guides. [Valentin Lab]

    Framework-free TypeScript library over Apache ECharts: day, month and year guides fading with zoom density, hatched weekends, a "now" line, time-axis wheel zoom and drag pan past the data, a y-axis fixed from the full data, stacked areas, minimum and maximum bands, light and dark palettes following the OS, and charts sharing a link key zooming together.

Fix

  • Draw the hatching of a weekend already in progress at the window start. [Valentin Lab]

    A window starting on a Saturday or Sunday drew no hatching for that weekend, because bands were only emitted for Saturday midnights inside the window. The band of a weekend overlapping the window start is now drawn too.

  • Keep day guides on local midnight after a DST jump at midnight. [Valentin Lab]

    In zones whose clocks jump at midnight (Chile, Azores, Cuba), the jump day starts at 01:00. Stepping one Date forward with setDate carried that 01:00 onto every following day guide and weekend band, until the end of the window. Each day is now built from its date parts, so only the jump day itself starts at 01:00.

  • Draw legend icons as solid swatches readable in dark mode. [Valentin Lab]

    ECharts' default legend icon is an empty circle filled white, a glaring white dot on a dark background.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 6

Native requirements
6 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
echarts
Environment
{
  "local_name": "echarts",
  "alias": false,
  "peer_metadata": null
}
^6.1.0runtime
vite
Environment
{
  "local_name": "vite",
  "alias": false,
  "peer_metadata": null
}
6.4.4development
jsdom
Environment
{
  "local_name": "jsdom",
  "alias": false,
  "peer_metadata": null
}
25.0.1development
vitest
Environment
{
  "local_name": "vitest",
  "alias": false,
  "peer_metadata": null
}
5.0.3development
typescript
Environment
{
  "local_name": "typescript",
  "alias": false,
  "peer_metadata": null
}
5.7.3development
@types/node
Environment
{
  "local_name": "@types/node",
  "alias": false,
  "peer_metadata": null
}
22.10.5development

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "shasum": "ce0b530a7e5a7e0327c3479bd39170bc90acba14",
  "tarball": "https://registry.npmjs.org/@0k/time-chart/-/time-chart-0.2.0.tgz",
  "integrity": "sha512-u0X5znDqNxBzAox9rMsIhWHV/DZY7rQlFgLQTz7630F4MKZjPh1KJPAUCAgqP2uU6MpeSSDk52GTMLNv2VjKrQ==",
  "fileCount": 20,
  "unpackedSize": 78341,
  "signatures": [
    {
      "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
      "sig": "MEUCIQDCr+Xoq3pTFoFuadZVm1QV/hkH+9EmJIBGX0BRh62NxgIgJ0y1yy7sVGnDk/mX+s9HxdH7vb12MTKODpm6Qy58bF0="
    }
  ]
}

Repository

PENDING
0k/time-chart

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 22:12 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:e00c960c91b3277a5710d130bfda2e09cd8af05056caaf46db14413f50e21741

Other collected versions

0.1.28 Oct0.0.0-stage8 OctView all collected versions →