microsoft / microsoft/flint-chart

ECharts: legend.left is baked to the design canvas, so chart.resize() overlaps the plot

Open
#98 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
4.2k
Forks
235
Avg merge
17h 45m
Merged PRs (30d)
11

Description

Summary

assembleECharts places the categorical legend (and the matching legend-title graphic) at an absolute left in design-canvas pixels. That is correct when the host inits ECharts at _width × _height. It breaks as soon as a host puts the chart in a fluid container and calls the official chart.resize().

This is not an ECharts resize() bug. resize() only changes the canvas / grid / axes; it does not rewrite option pixel fields. Flint is the one emitting legend.left relative to _width.

Verified against flint-chart@0.4.1. The same left: legendLeftPx path is still in main (packages/flint-js/src/echarts/instantiate-spec.ts, legend-positioning block). I did not find an existing issue for this.

Repro

Slope Chart (any multi-series cartesian chart with a right-side categorical legend works):

import { assembleECharts } from 'flint-chart/echarts';
import * as echarts from 'echarts';

const option = assembleECharts({
  data: { values: [
    { period: '2024', team: 'Alpha', nps: 32 },
    { period: '2025', team: 'Alpha', nps: 48 },
    { period: '2024', team: 'Beta', nps: 41 },
    { period: '2025', team: 'Beta', nps: 39 },
    { period: '2024', team: 'Gamma', nps: 28 },
    { period: '2025', team: 'Gamma', nps: 52 },
    { period: '2024', team: 'Delta', nps: 55 },
    { period: '2025', team: 'Delta', nps: 61 },
  ]},
  semantic_types: { period: 'Year', team: 'Name', nps: 'Score' },
  chart_spec: {
    chartType: 'Slope Chart',
    encodings: {
      x: { field: 'period' },
      y: { field: 'nps' },
      color: { field: 'team' },
    },
    baseSize: { width: 420, height: 280 },
  },
});

// Typical host: fill the reading column, then resize.
const el = document.getElementById('chart');
el.style.width = '100%';          // e.g. 800px
el.style.height = option._height + 'px';
const chart = echarts.init(el);
chart.setOption(option);
chart.resize();

Assembled bookkeeping on 0.4.1:

field value
_width / _height 534 × 377
grid.right 112 (plot ends at canvasW − 112)
legend.left 422
legend-title graphic.left 422

422 + 112 = 534. The legend’s left edge is meant to sit on the plot’s right edge only when the canvas is 534px wide.

Actual

  • Canvas 534px (Flint’s contract): legend sits in the reserved gutter. Correct.
  • Canvas 800px + resize(): plot stretches to 800 − 112 = 688, legend stays at left: 422overlaps the series.
  • Canvas 350px + resize(): legend.left: 422 is past the right edge → clipped / gone.

grid.left / grid.right as pixel insets still work at any width. Only left-origin design coordinates (legend.left, title graphic.left, and similarly series.radius: 'NNpx' on sunburst/pie) do not.

Expected

One of:

  1. Document the contract in the ECharts assemble docs: hosts must init/size the DOM to _width × _height (or CSS-scale that box). Fluid width: 100% + resize() is unsupported.
  2. Emit fluid-safe positions so official resize() keeps the legend in the gutter:
    • legend.right (and the title graphic) instead of legend.left: designW − gutter, or
    • left: 'right' / percent, with grid.right still reserving the same gutter.

Option 2 is a small change in ecApplyLayoutToSpec and would let note/editor hosts follow the ECharts handbook (width: 100% + resize()) without rewriting Flint’s option.

A host-side conversion of left → right = _width - left is wrong: ECharts right is the inset to the component’s right edge, so the legend grows left into the plot even at design size.

Why this shows up in hosts

Flint’s own MCP / gallery path paints at a fixed canvas (create_chart_view is described as fixed-size). Editors that share a reading column with prose (and iPhone/iPad widths) cannot dedicate _width forever, and resize() is the documented ECharts way to follow the container.

Related but separate: sunburst/pie radius: [\${n}px`, ...]` is also design-canvas-absolute and clips when the live box is smaller. Happy to split that if you want a narrower first fix.

Environment

  • flint-chart@0.4.1 (reproduced)
  • main as of 2026-08-15 still uses legendLeftPx = effectiveChartWidth - rightMarginPx
  • Apache ECharts 5 / 6 (behavior of legend.left vs resize() is the same)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/flint-js/src/echarts/instantiate-spec.ts, especially the legend-positioning block and ecApplyLayoutToSpec. Reproduce the supplied assembleECharts example at the design width and at wider and narrower container widths, then verify that the chosen positioning behavior keeps the legend and title graphic out of the plot after chart.resize().

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
data-visualization
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.