Interactivity & live data

Events

All event names are lowercase (the platform convention):

html`<${Charts} type="bar" series="${series}"
    onpointclick="${(point, { seriesIndex, pointIndex }) => select(point)}"
    onlegendclick="${(key, visible) => console.log(key, visible)}"
    ondrilldown="${(key, depth) => console.log('into', key)}"
    ondrillup="${(depth) => console.log('back to', depth)}" />`

Clicking a legend entry toggles that series’ visibility, and the chart rescales to the remaining data automatically.

Drilldown

Declarative: map a category (or slice) name to the chart it opens. Descending renders a breadcrumb for the way back, with no handlers needed:

live
import Charts from '@lemonadejs/charts';

const App = () => html`<${Charts} type="bar" title="Sales by region (click a bar)"
    categories="${['Americas', 'EMEA', 'APAC']}"
    series="${[{ name: 'Sales', data: [82, 61, 47] }]}" labels
    drilldown="${{
        Americas: { categories: ['US', 'CA', 'BR', 'MX'], series: [{ name: 'Sales', data: [40, 18, 15, 9] }] },
        EMEA: { categories: ['DE', 'UK', 'FR', 'ES'], series: [{ name: 'Sales', data: [22, 17, 12, 10] }] },
        APAC: { categories: ['CN', 'JP', 'IN', 'AU'], series: [{ name: 'Sales', data: [19, 12, 10, 6] }] },
    }}" />`;

Levels nest (a drilldown target can carry its own drilldown), and each level may change type and title too.

Zoom and navigator

For dense series, zoom enables drag-select along the x-axis (with a reset button), and navigator renders an overview strip below the plot with a draggable, resizable window:

live
import Charts from '@lemonadejs/charts';

const data = Array.from({ length: 60 }, (_, i) =>
    Math.round(70 + Math.sin(i / 5) * 20 + Math.random() * 8));

const App = () => html`<${Charts} type="line"
    categories="${data.map((_, i) => 'D' + (i + 1))}"
    series="${[{ name: 'Price', data }]}"
    markers="${false}" zoom navigator legend="${false}" />`;

Live data

Props are states, so live charts are just assignment. A polling dashboard, a websocket feed, or a simulation all look the same:

live
import Charts from '@lemonadejs/charts';

const App = (props, { state, onMount }) => {
    const series = state([{ name: 'Load', data: [50] }]);
    onMount(() => {
        const timer = setInterval(() => {
            const prev = series.value[0].data;
            const next = [...prev.slice(-29),
                Math.max(5, Math.min(95, prev[prev.length - 1] + (Math.random() - 0.5) * 14))];
            series.value = [{ name: 'Load', data: next }];
        }, 800);
        return () => clearInterval(timer);   // cleanup on unmount
    });
    return html`<${Charts} type="line" series="${series}" smooth
        markers="${false}" legend="${false}" height="${220}"
        ymin="${0}" ymax="${100}" />`;
};

Remember the v6 contract: build a new array and assign. In-place push alone does not trigger (or call .touch() after mutating).

animate (on by default) gives entrance/update transitions and respects prefers-reduced-motion.

Export

toolbar shows a small toolbar with CSV download; SVG export is available for the SVG-native types. Both work entirely client-side: the chart is the document.