@rowsncolumns/charts 28.1.2 → 28.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -1
  2. package/dist/cjs/basic-chart/basic-chart.js +152 -88
  3. package/dist/cjs/basic-chart/helpers.d.ts +9 -0
  4. package/dist/cjs/basic-chart/helpers.d.ts.map +1 -1
  5. package/dist/cjs/basic-chart/helpers.js +55 -1
  6. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -1
  7. package/dist/cjs/plotly/basic-chart/basic-chart.js +225 -140
  8. package/dist/cjs/plotly/plot.d.ts +12 -0
  9. package/dist/cjs/plotly/plot.d.ts.map +1 -0
  10. package/dist/cjs/plotly/plot.js +8 -0
  11. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  12. package/dist/cjs/utils/axis-number-format.d.ts +14 -0
  13. package/dist/cjs/utils/axis-number-format.d.ts.map +1 -1
  14. package/dist/cjs/utils/axis-number-format.js +41 -1
  15. package/dist/cjs/utils/empty-cells.d.ts +9 -0
  16. package/dist/cjs/utils/empty-cells.d.ts.map +1 -1
  17. package/dist/cjs/utils/empty-cells.js +26 -1
  18. package/dist/cjs/utils/legend-layout.d.ts +168 -0
  19. package/dist/cjs/utils/legend-layout.d.ts.map +1 -0
  20. package/dist/cjs/utils/legend-layout.js +338 -0
  21. package/dist/cjs/utils/trendline.d.ts +18 -5
  22. package/dist/cjs/utils/trendline.d.ts.map +1 -1
  23. package/dist/cjs/utils/trendline.js +38 -16
  24. package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -1
  25. package/dist/esm/basic-chart/basic-chart.js +118 -47
  26. package/dist/esm/basic-chart/helpers.d.ts +9 -0
  27. package/dist/esm/basic-chart/helpers.d.ts.map +1 -1
  28. package/dist/esm/basic-chart/helpers.js +30 -0
  29. package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -1
  30. package/dist/esm/plotly/basic-chart/basic-chart.js +140 -13
  31. package/dist/esm/plotly/plot.d.ts +12 -0
  32. package/dist/esm/plotly/plot.d.ts.map +1 -0
  33. package/dist/esm/plotly/plot.js +12 -0
  34. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  35. package/dist/esm/utils/axis-number-format.d.ts +14 -0
  36. package/dist/esm/utils/axis-number-format.d.ts.map +1 -1
  37. package/dist/esm/utils/axis-number-format.js +36 -0
  38. package/dist/esm/utils/empty-cells.d.ts +9 -0
  39. package/dist/esm/utils/empty-cells.d.ts.map +1 -1
  40. package/dist/esm/utils/empty-cells.js +21 -0
  41. package/dist/esm/utils/legend-layout.d.ts +168 -0
  42. package/dist/esm/utils/legend-layout.d.ts.map +1 -0
  43. package/dist/esm/utils/legend-layout.js +299 -0
  44. package/dist/esm/utils/trendline.d.ts +18 -5
  45. package/dist/esm/utils/trendline.d.ts.map +1 -1
  46. package/dist/esm/utils/trendline.js +36 -16
  47. package/package.json +9 -9
@@ -10,7 +10,7 @@ import * as echarts from "echarts/core";
10
10
  import { CanvasRenderer } from "echarts/renderers";
11
11
  import React, { useCallback, useMemo, useRef, useState } from "react";
12
12
  import { aggregateSeriesByDomain, getDomainValuesForAxis, sanitizeDomainValues, shouldAggregateSeries, } from "../utils/aggregation";
13
- import { circularTooltipFormatter, formatAxisValue, resolveSeriesNumberFormats, resolveSourceNumberFormat, resolveValueAxisNumberFormat, tooltipValueFormatter, valueAxisLabelFormatter, } from "../utils/axis-number-format";
13
+ import { circularTooltipFormatter, formatAxisValue, resolveSeriesNumberFormats, resolveSourceNumberFormat, resolveValueAxisNumberFormat, tooltipValueFormatter, valueAxisLabelFormatter, valueAxisNameGap, } from "../utils/axis-number-format";
14
14
  import { gapWidthToBarCategoryGap, overlapToBarGap, } from "../utils/bar-layout";
15
15
  import { computeBoxplotSummary } from "../utils/boxplot";
16
16
  import { createBubbleSizeScale, prepareBubbleSeriesData, } from "../utils/bubble";
@@ -18,13 +18,14 @@ import { coerceCalculatedLabel, getChartDataRanges, } from "../utils/chart-spec-
18
18
  import { canSwitchRowColumn, cellLabelGetterFromRangeReader, switchRowColumn, } from "../utils/chart-orientation";
19
19
  import { dataUrlToBlob } from "../utils/chart-image";
20
20
  import { getChartPalette } from "../utils/chart-styles";
21
- import { applyEmptyCellsPolicy, connectsAcrossEmptyCells, isLineLikeChartType, } from "../utils/empty-cells";
21
+ import { applyEmptyCellsPolicy, connectsAcrossEmptyCells, isolatedPointIndexes, isLineLikeChartType, } from "../utils/empty-cells";
22
22
  import { getEchartsGlState, loadEchartsGl, } from "../utils/echarts-gl-loader";
23
23
  import { computeErrorBars, resolveCustomErrorBarValues, } from "../utils/error-bars";
24
24
  import { echartsGroupedCategoryAxes, resolveCategoryLevels, } from "../utils/category-levels";
25
25
  import { gradientToEcharts } from "../utils/gradient";
26
26
  import { buildHierarchyTree, extractHierarchyRows, getHierarchyHeaderTitle, } from "../utils/hierarchy";
27
27
  import { binHistogram, computePareto, isParetoSpec, } from "../utils/histogram";
28
+ import { categoryAxisNameGap, legendLabelsFrom, reserveEchartsLegendBand, } from "../utils/legend-layout";
28
29
  import { clampSecondPieSize, isOfPieChartSpec, otherSliceHalfAngle, resolveOfPieType, splitOfPie, } from "../utils/of-pie";
29
30
  import { applyAxisOverrides, applyLegendOverrides, buildSeriesLabelOption, } from "../utils/overrides";
30
31
  import { buildRadarIndicators, buildRadarSeriesData } from "../utils/radar";
@@ -37,7 +38,7 @@ import { buildBar3DData, buildContourData, buildLine3DSeries, buildSurfaceData,
37
38
  import { computeTrendline } from "../utils/trendline";
38
39
  import { computeWaterfall } from "../utils/waterfall";
39
40
  import { isWebGLAvailable } from "../utils/webgl";
40
- import { chartTypeToEchartsType, getEchartsSeriesStyle, mapMarkerSymbolToEcharts, showXAxis, } from "./helpers";
41
+ import { chartTypeToEchartsType, circularLegendEntries, getEchartsSeriesStyle, isCircularChartType, mapMarkerSymbolToEcharts, showXAxis, } from "./helpers";
41
42
  // Excel error values that should be treated as null in charts
42
43
  const EXCEL_ERROR_VALUES = new Set([
43
44
  "#N/A",
@@ -180,6 +181,8 @@ const defaultLegendConfig = {
180
181
  itemHeight: 4,
181
182
  top: 20,
182
183
  };
184
+ /** Category-axis title gap: clears one row of tick labels (#986). */
185
+ const CATEGORY_AXIS_NAME_GAP = categoryAxisNameGap();
183
186
  export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, readonly, getDomainValuesFromRange, getSeriesValuesFromRange, getFormattingFromRange, onDeleteChart, onUpdateChart, onRequestEdit, onRequestCalculate, isSelected, fieldButtons, }) => {
184
187
  const spec = chart.spec;
185
188
  const explicitChartTitle = chart.spec.title ?? undefined;
@@ -306,7 +309,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
306
309
  rawDomainValues.length === seriesValues.length;
307
310
  if (canAggregate) {
308
311
  const aggregatedValues = aggregateSeriesByDomain(rawDomainValues, seriesValues, aggregateType, orderedDomainValues);
309
- if (spec.chartType === "pie") {
312
+ if (isCircularChartType(spec.chartType)) {
310
313
  return orderedDomainValues.map((name, idx) => ({
311
314
  value: aggregatedValues[idx] ?? 0,
312
315
  name,
@@ -315,7 +318,9 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
315
318
  return aggregatedValues;
316
319
  }
317
320
  const numericValues = seriesValues;
318
- if (spec.chartType === "pie") {
321
+ // Pie and doughnut points are slices keyed by their category name;
322
+ // a doughnut is a pie series with a hole, so it labels the same way.
323
+ if (isCircularChartType(spec.chartType)) {
319
324
  if (options.pieLabels) {
320
325
  const { domainLabels, offset } = options.pieLabels;
321
326
  const labels = offset === undefined
@@ -1458,7 +1463,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1458
1463
  ? spec.verticalAxisTitle ?? ""
1459
1464
  : spec.horizontalAxisTitle ?? "",
1460
1465
  nameLocation: "middle",
1461
- nameGap: 30 + 24 * categoryLevels.outer.length,
1466
+ nameGap: CATEGORY_AXIS_NAME_GAP + 24 * categoryLevels.outer.length,
1462
1467
  }));
1463
1468
  }
1464
1469
  // One category axis per domain (Excel): a domain drawn from several
@@ -1485,7 +1490,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1485
1490
  splitLine: { show: !isCircularChart },
1486
1491
  name: axisTitle,
1487
1492
  nameLocation: "middle",
1488
- nameGap: 30,
1493
+ nameGap: CATEGORY_AXIS_NAME_GAP,
1489
1494
  });
1490
1495
  });
1491
1496
  if (chartDomains.length === 0 && !isCircularChart) {
@@ -1500,7 +1505,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1500
1505
  ? spec.verticalAxisTitle ?? ""
1501
1506
  : spec.horizontalAxisTitle ?? "",
1502
1507
  nameLocation: "middle",
1503
- nameGap: 30,
1508
+ nameGap: CATEGORY_AXIS_NAME_GAP,
1504
1509
  });
1505
1510
  }
1506
1511
  // Trendline + error-bar overlays are extra series appended after the
@@ -1670,6 +1675,20 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1670
1675
  seriesCount: sideBySideBarCount,
1671
1676
  overlap: isStacked ? 0 : spec.overlap,
1672
1677
  });
1678
+ // The colour a series without its own fill gets: the chart palette entry
1679
+ // for its position, the same cycle the option-level `color` gives echarts'
1680
+ // default path. Excel keeps that colour whatever else is authored on the
1681
+ // series — `<c:dPt>` overrides recolour only their points and
1682
+ // `invertIfNegative` only the negative bars (rowsncolumns/spreadsheet#1030).
1683
+ const seriesPalette = getChartPalette(spec, [
1684
+ theme.themeColors.accent1,
1685
+ theme.themeColors.accent2,
1686
+ theme.themeColors.accent3,
1687
+ theme.themeColors.accent4,
1688
+ theme.themeColors.accent5,
1689
+ theme.themeColors.accent6,
1690
+ ]);
1691
+ const paletteColorAt = (index) => seriesPalette[index % seriesPalette.length];
1673
1692
  for (const [specSeriesIndex, series] of (spec.series ?? []).entries()) {
1674
1693
  const dataLabel = !isNil(series.dataLabel) && isAFormula(series.dataLabel)
1675
1694
  ? coerceCalculatedLabel(await onRequestCalculate(series.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
@@ -1689,7 +1708,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1689
1708
  // domain range's labels in sequence, each series range taking its own
1690
1709
  // window — not the primary domain range alone (C-78).
1691
1710
  const domainSources = spec.domains?.[0]?.sources ?? [];
1692
- const pieDomainLabels = spec.chartType === "pie" &&
1711
+ const pieDomainLabels = isCircularChart &&
1693
1712
  (domainSources.length > 1 || series.sources.length > 1)
1694
1713
  ? domainSources.flatMap((sheetRange) => getDomainValuesFromRange(sheetRange, spec.hiddenDimensionStrategy) ?? [])
1695
1714
  : undefined;
@@ -1766,6 +1785,22 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1766
1785
  mapMarkerSymbolToEcharts(markerSymbol) ?? "circle";
1767
1786
  seriesEntry.symbolSize = seriesAny.marker?.size ?? 6;
1768
1787
  }
1788
+ else if (Array.isArray(data)) {
1789
+ // A marker-less line draws nothing for a point with a gap on both
1790
+ // sides (a data row followed by a blank row), so that row vanishes
1791
+ // from the chart. Excel plots an isolated marker there: keep the
1792
+ // series symbol-less and mark only those points.
1793
+ const isolated = isolatedPointIndexes(data, connectsAcrossEmptyCells(spec, lineLike));
1794
+ if (isolated.length) {
1795
+ seriesEntry.showSymbol = true;
1796
+ seriesEntry.symbol = "none";
1797
+ const marked = [...data];
1798
+ for (const i of isolated) {
1799
+ marked[i] = { value: marked[i], symbol: "circle", symbolSize: 6 };
1800
+ }
1801
+ seriesEntry.data = marked;
1802
+ }
1803
+ }
1769
1804
  }
1770
1805
  // Gradient fill — overrides solid color when present. echarts
1771
1806
  // paints it natively via a LinearGradient on itemStyle.color
@@ -1801,14 +1836,16 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1801
1836
  // Per-data-point color overrides — Gantt-style charts colour
1802
1837
  // each task differently via `<c:dPt>`. Echarts accepts an
1803
1838
  // `itemStyle.color` function that receives the data index;
1804
- // we look up the override or fall back to the series color.
1839
+ // we look up the override or fall back to the series colour —
1840
+ // its own, else its palette slot (never a fixed accent1, which
1841
+ // painted every such series alike).
1805
1842
  const overrides = new Map();
1806
1843
  for (const dp of seriesAny.dataPointColors) {
1807
1844
  if (typeof dp.idx === "number" && typeof dp.color === "string") {
1808
1845
  overrides.set(dp.idx, dp.color);
1809
1846
  }
1810
1847
  }
1811
- const fallback = series.color ?? theme.themeColors.accent1;
1848
+ const fallback = series.color ?? paletteColorAt(specSeriesIndex);
1812
1849
  seriesEntry.itemStyle = {
1813
1850
  ...(seriesEntry.itemStyle ?? {}),
1814
1851
  color: (params) => overrides.get(params.dataIndex) ?? fallback,
@@ -1826,25 +1863,18 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1826
1863
  overrides.set(dp.idx, dp.color);
1827
1864
  }
1828
1865
  }
1829
- const slicePalette = getChartPalette(spec, [
1830
- theme.themeColors.accent1,
1831
- theme.themeColors.accent2,
1832
- theme.themeColors.accent3,
1833
- theme.themeColors.accent4,
1834
- theme.themeColors.accent5,
1835
- theme.themeColors.accent6,
1836
- ]);
1837
1866
  seriesEntry.itemStyle = {
1838
1867
  ...(seriesEntry.itemStyle ?? {}),
1839
1868
  color: (params) => overrides.get(params.dataIndex) ??
1840
1869
  series.color ??
1841
- slicePalette[params.dataIndex % slicePalette.length],
1870
+ paletteColorAt(params.dataIndex),
1842
1871
  };
1843
1872
  }
1844
1873
  else if (isBarOrColumn && series.invertIfNegative) {
1845
- const positiveColor = series.color ?? theme.themeColors.accent1;
1874
+ // Positive bars keep the series colour (own, else palette slot);
1875
+ // only negative bars take the inverted colour.
1876
+ const positiveColor = series.color ?? paletteColorAt(specSeriesIndex);
1846
1877
  const negativeColor = series.negativeColor ?? "#FF0000";
1847
- // Use a color function that returns different colors based on value
1848
1878
  seriesEntry.itemStyle = {
1849
1879
  ...(seriesEntry.itemStyle ?? {}),
1850
1880
  color: (params) => {
@@ -1876,6 +1906,12 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1876
1906
  chartSeries.push(seriesEntry);
1877
1907
  pushSeriesOverlays(series, data, dataLabel);
1878
1908
  }
1909
+ if (isCircularChart && !isOfPie) {
1910
+ // Excel's pie / doughnut legend lists the categories, one entry per
1911
+ // slice — not the series name (the loop above collected those). The
1912
+ // of-pie branch below builds its own category legend.
1913
+ legend.data = circularLegendEntries(chartSeries);
1914
+ }
1879
1915
  if (isOfPie) {
1880
1916
  // Pie-of-pie / bar-of-pie — Excel splits the minor slices into a
1881
1917
  // secondary pie or stacked bar beside the main pie, replaces them
@@ -2298,27 +2334,6 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2298
2334
  previousSeriesCount.current !== renderedSeriesCount
2299
2335
  ? ["series", "xAxis", "yAxis", "legend", "grid", "tooltip"]
2300
2336
  : [];
2301
- // Of-pie uses a full-container grid (for the connector overlay's
2302
- // hidden 0-1 axes) plus, for bar-of-pie, a second grid hosting the
2303
- // stacked secondary bar.
2304
- const gridConfig = isOfPie
2305
- ? [
2306
- { left: 0, right: 0, top: 0, bottom: 0 },
2307
- ...(ofPieLayout?.barGrid ? [ofPieLayout.barGrid] : []),
2308
- ]
2309
- : isCircularChart
2310
- ? {
2311
- left: 0,
2312
- right: 0,
2313
- top: 0,
2314
- bottom: 0,
2315
- }
2316
- : {
2317
- left: "3%",
2318
- right: "4%",
2319
- bottom: "3%",
2320
- containLabel: true,
2321
- };
2322
2337
  const valueAxisBase = {
2323
2338
  type: "value",
2324
2339
  show: !isCircularChart,
@@ -2380,6 +2395,49 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2380
2395
  };
2381
2396
  // Phase 5.1: apply legend / axis / dataLabels / doughnut overrides.
2382
2397
  const finalLegend = applyLegendOverrides(legend, spec.legend);
2398
+ // Reserve a band for the legend outside the axis-title band, outside
2399
+ // the tick labels — Excel shrinks the plot instead of painting the
2400
+ // legend over the category labels / axis title (#986). The bottom
2401
+ // axis is the value axis on a horizontal bar chart, else the (first)
2402
+ // category axis with whatever nameGap its levels needed.
2403
+ const bottomAxisNameGap = isHorizontalBarChart
2404
+ ? valueAxisBase.nameGap
2405
+ : Number(chartDomains[0]?.nameGap ?? CATEGORY_AXIS_NAME_GAP);
2406
+ const legendBand = reserveEchartsLegendBand({
2407
+ position: spec.legend?.position,
2408
+ visible: finalLegend.show !== false,
2409
+ labels: legendLabelsFrom(finalLegend.data, chartSeries.map((s) => s?.name)),
2410
+ legendFontSize: finalLegend.textStyle?.fontSize ??
2411
+ spec.legend?.textFormat?.fontSize ??
2412
+ 12,
2413
+ legendItemWidth: defaultLegendConfig.itemWidth,
2414
+ width: width ?? 0,
2415
+ height: height ?? 0,
2416
+ hasTitle: Boolean(title),
2417
+ titleFontSize: spec.titleTextFormat?.fontSize ?? 16,
2418
+ hasBottomAxisTitle: !isCircularChart && Boolean(spec.horizontalAxisTitle),
2419
+ bottomAxisNameGap,
2420
+ hasLeftAxisTitle: !isCircularChart && Boolean(spec.verticalAxisTitle),
2421
+ });
2422
+ const positionedLegend = isCircularChart
2423
+ ? finalLegend
2424
+ : { ...finalLegend, ...legendBand.legend };
2425
+ // Of-pie uses a full-container grid (for the connector overlay's
2426
+ // hidden 0-1 axes) plus, for bar-of-pie, a second grid hosting the
2427
+ // stacked secondary bar.
2428
+ const gridConfig = isOfPie
2429
+ ? [
2430
+ { left: 0, right: 0, top: 0, bottom: 0 },
2431
+ ...(ofPieLayout?.barGrid ? [ofPieLayout.barGrid] : []),
2432
+ ]
2433
+ : isCircularChart
2434
+ ? {
2435
+ left: 0,
2436
+ right: 0,
2437
+ top: 0,
2438
+ bottom: 0,
2439
+ }
2440
+ : legendBand.grid;
2383
2441
  // Data labels fall back to the series' axis format when the spec has
2384
2442
  // no explicit `<c:dLbls><c:numFmt>` — Excel's "Linked to source".
2385
2443
  const dataLabelOptions = spec.dataLabels;
@@ -2540,7 +2598,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2540
2598
  splitLine: { show: true },
2541
2599
  name: spec.horizontalAxisTitle ?? "",
2542
2600
  nameLocation: "middle",
2543
- nameGap: 30,
2601
+ nameGap: CATEGORY_AXIS_NAME_GAP,
2544
2602
  },
2545
2603
  ];
2546
2604
  return base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions));
@@ -2595,13 +2653,26 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2595
2653
  splitLine: { show: true },
2596
2654
  name: spec.verticalAxisTitle ?? "",
2597
2655
  nameLocation: "middle",
2598
- nameGap: 30,
2656
+ nameGap: CATEGORY_AXIS_NAME_GAP,
2599
2657
  },
2600
2658
  ];
2601
2659
  return base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions));
2602
2660
  }
2603
2661
  const axes = [
2604
- applyAxisOverrides({ ...valueAxisBase, name: spec.verticalAxisTitle ?? "" }, spec.valueAxisOptions, primaryAxisNumberFormat),
2662
+ applyAxisOverrides({
2663
+ ...valueAxisBase,
2664
+ name: spec.verticalAxisTitle ?? "",
2665
+ // Clear the tick labels: "$2,000.00" is wider than the 50px
2666
+ // default gap, and the title painted over its own labels.
2667
+ nameGap: valueAxisNameGap({
2668
+ values: chartSeries
2669
+ .filter((s) => s?.yAxisIndex !== 1)
2670
+ .flatMap((s) => Array.isArray(s?.data)
2671
+ ? s.data.map((d) => d !== null && typeof d === "object" ? d.value : d)
2672
+ : []),
2673
+ pattern: primaryAxisNumberFormat,
2674
+ }),
2675
+ }, spec.valueAxisOptions, primaryAxisNumberFormat),
2605
2676
  ];
2606
2677
  if (hasSecondaryAxis) {
2607
2678
  axes.push(applyAxisOverrides({
@@ -2621,7 +2692,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2621
2692
  return axes;
2622
2693
  })(),
2623
2694
  series: seriesWithLabels,
2624
- legend: finalLegend,
2695
+ legend: positionedLegend,
2625
2696
  grid: gridConfig,
2626
2697
  tooltip: tooltipConfig,
2627
2698
  }, { replaceMerge, lazyUpdate: true });
@@ -5,6 +5,15 @@ export declare const chartTypeToEchartsType: (type: EmbeddedChart["spec"]["chart
5
5
  * Returns undefined for "auto" / unknown — caller defaults to "circle".
6
6
  */
7
7
  export declare const mapMarkerSymbolToEcharts: (symbol: string | undefined | null) => string | undefined;
8
+ /** Pie and doughnut — the pie-family types whose points are slices. */
9
+ export declare const isCircularChartType: (type: EmbeddedChart["spec"]["chartType"] | undefined) => boolean;
10
+ /**
11
+ * Legend entries of a pie / doughnut: the slice names (the categories), in
12
+ * order, each once across every pie series — Excel's pie legend lists the
13
+ * categories, not the series name. echarts keys pie legend items by data
14
+ * item name, so an entry toggles that slice in every series.
15
+ */
16
+ export declare const circularLegendEntries: (series: ReadonlyArray<unknown>) => string[];
8
17
  export declare const getEchartsSeriesStyle: (type: EmbeddedChart["spec"]["chartType"]) => {
9
18
  areaStyle: {};
10
19
  } | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../basic-chart/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,eAAO,MAAM,sBAAsB,GACjC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,yGA6DzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,GAAG,SAAS,GAAG,IAAI,KAChC,MAAM,GAAG,SAuBX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC;;aASzC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,YAejE,CAAC"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../basic-chart/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,eAAO,MAAM,sBAAsB,GACjC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,yGA6DzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,GAAG,SAAS,GAAG,IAAI,KAChC,MAAM,GAAG,SAuBX,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,GAC9B,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,GAAG,SAAS,KACnD,OAAgD,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,aAAa,CAAC,OAAO,CAAC,KAC7B,MAAM,EAmBR,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC;;aASzC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,YAejE,CAAC"}
@@ -74,6 +74,36 @@ export const mapMarkerSymbolToEcharts = (symbol) => {
74
74
  return undefined;
75
75
  }
76
76
  };
77
+ /** Pie and doughnut — the pie-family types whose points are slices. */
78
+ export const isCircularChartType = (type) => type === "pie" || type === "doughnut";
79
+ /**
80
+ * Legend entries of a pie / doughnut: the slice names (the categories), in
81
+ * order, each once across every pie series — Excel's pie legend lists the
82
+ * categories, not the series name. echarts keys pie legend items by data
83
+ * item name, so an entry toggles that slice in every series.
84
+ */
85
+ export const circularLegendEntries = (series) => {
86
+ const seen = new Set();
87
+ const entries = [];
88
+ for (const raw of series) {
89
+ const entry = raw;
90
+ if (entry?.type !== "pie" || !Array.isArray(entry.data))
91
+ continue;
92
+ for (const item of entry.data) {
93
+ const name = item !== null && typeof item === "object" && "name" in item
94
+ ? item.name
95
+ : undefined;
96
+ if (name === undefined || name === null)
97
+ continue;
98
+ const label = String(name);
99
+ if (label === "" || seen.has(label))
100
+ continue;
101
+ seen.add(label);
102
+ entries.push(label);
103
+ }
104
+ }
105
+ return entries;
106
+ };
77
107
  export const getEchartsSeriesStyle = (type) => {
78
108
  switch (type) {
79
109
  case "area":
@@ -1 +1 @@
1
- {"version":3,"file":"basic-chart.d.ts","sourceRoot":"","sources":["../../../../plotly/basic-chart/basic-chart.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAGtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AA4VxD,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AA0FnD,eAAO,MAAM,UAAU,GAAI,wOAiBxB,eAAe,sBAmrEjB,CAAC"}
1
+ {"version":3,"file":"basic-chart.d.ts","sourceRoot":"","sources":["../../../../plotly/basic-chart/basic-chart.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAgXxD,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AA0FnD,eAAO,MAAM,UAAU,GAAI,wOAiBxB,eAAe,sBA4zEjB,CAAC"}