@rowsncolumns/charts 28.1.13 → 28.1.15

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 (99) hide show
  1. package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -1
  2. package/dist/cjs/basic-chart/basic-chart.js +308 -270
  3. package/dist/cjs/components/editors/chart-editor-capabilities.d.ts +48 -0
  4. package/dist/cjs/components/editors/chart-editor-capabilities.d.ts.map +1 -0
  5. package/dist/cjs/components/editors/chart-editor-capabilities.js +62 -0
  6. package/dist/cjs/components/editors/chart-editor.d.ts.map +1 -1
  7. package/dist/cjs/components/editors/chart-editor.js +200 -534
  8. package/dist/cjs/components/editors/fields/axis-options-fields.d.ts +16 -0
  9. package/dist/cjs/components/editors/fields/axis-options-fields.d.ts.map +1 -0
  10. package/dist/cjs/components/editors/fields/axis-options-fields.js +31 -0
  11. package/dist/cjs/components/editors/fields/chart-type-options.d.ts +25 -0
  12. package/dist/cjs/components/editors/fields/chart-type-options.d.ts.map +1 -0
  13. package/dist/cjs/components/editors/fields/chart-type-options.js +170 -0
  14. package/dist/cjs/components/editors/fields/data-labels-fields.d.ts +15 -0
  15. package/dist/cjs/components/editors/fields/data-labels-fields.d.ts.map +1 -0
  16. package/dist/cjs/components/editors/fields/data-labels-fields.js +57 -0
  17. package/dist/cjs/components/editors/fields/primitives.d.ts +54 -0
  18. package/dist/cjs/components/editors/fields/primitives.d.ts.map +1 -0
  19. package/dist/cjs/components/editors/fields/primitives.js +52 -0
  20. package/dist/cjs/components/editors/fields/series-format-fields.d.ts +17 -0
  21. package/dist/cjs/components/editors/fields/series-format-fields.d.ts.map +1 -0
  22. package/dist/cjs/components/editors/fields/series-format-fields.js +165 -0
  23. package/dist/cjs/components/editors/fields/series-name-field.d.ts +16 -0
  24. package/dist/cjs/components/editors/fields/series-name-field.d.ts.map +1 -0
  25. package/dist/cjs/components/editors/fields/series-name-field.js +36 -0
  26. package/dist/cjs/components/editors/fields/text-format-fields.d.ts +20 -0
  27. package/dist/cjs/components/editors/fields/text-format-fields.d.ts.map +1 -0
  28. package/dist/cjs/components/editors/fields/text-format-fields.js +46 -0
  29. package/dist/cjs/config.d.ts.map +1 -1
  30. package/dist/cjs/config.js +3 -0
  31. package/dist/cjs/interface/create-chart.d.ts +3 -2
  32. package/dist/cjs/interface/create-chart.d.ts.map +1 -1
  33. package/dist/cjs/interface/create-chart.js +7 -0
  34. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -1
  35. package/dist/cjs/plotly/basic-chart/basic-chart.js +255 -218
  36. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  37. package/dist/cjs/utils/chart-orientation.d.ts.map +1 -1
  38. package/dist/cjs/utils/chart-orientation.js +24 -12
  39. package/dist/cjs/utils/chart-spec-utils.d.ts.map +1 -1
  40. package/dist/cjs/utils/chart-spec-utils.js +120 -21
  41. package/dist/cjs/utils/echarts-overlays.d.ts +2 -0
  42. package/dist/cjs/utils/echarts-overlays.d.ts.map +1 -1
  43. package/dist/cjs/utils/echarts-overlays.js +25 -12
  44. package/dist/cjs/utils/series-name.d.ts +12 -0
  45. package/dist/cjs/utils/series-name.d.ts.map +1 -0
  46. package/dist/cjs/utils/series-name.js +26 -0
  47. package/dist/cjs/utils/trendline.d.ts +9 -0
  48. package/dist/cjs/utils/trendline.d.ts.map +1 -1
  49. package/dist/cjs/utils/trendline.js +16 -1
  50. package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -1
  51. package/dist/esm/basic-chart/basic-chart.js +73 -37
  52. package/dist/esm/components/editors/chart-editor-capabilities.d.ts +48 -0
  53. package/dist/esm/components/editors/chart-editor-capabilities.d.ts.map +1 -0
  54. package/dist/esm/components/editors/chart-editor-capabilities.js +58 -0
  55. package/dist/esm/components/editors/chart-editor.d.ts.map +1 -1
  56. package/dist/esm/components/editors/chart-editor.js +166 -448
  57. package/dist/esm/components/editors/fields/axis-options-fields.d.ts +16 -0
  58. package/dist/esm/components/editors/fields/axis-options-fields.d.ts.map +1 -0
  59. package/dist/esm/components/editors/fields/axis-options-fields.js +22 -0
  60. package/dist/esm/components/editors/fields/chart-type-options.d.ts +25 -0
  61. package/dist/esm/components/editors/fields/chart-type-options.d.ts.map +1 -0
  62. package/dist/esm/components/editors/fields/chart-type-options.js +151 -0
  63. package/dist/esm/components/editors/fields/data-labels-fields.d.ts +15 -0
  64. package/dist/esm/components/editors/fields/data-labels-fields.d.ts.map +1 -0
  65. package/dist/esm/components/editors/fields/data-labels-fields.js +47 -0
  66. package/dist/esm/components/editors/fields/primitives.d.ts +54 -0
  67. package/dist/esm/components/editors/fields/primitives.d.ts.map +1 -0
  68. package/dist/esm/components/editors/fields/primitives.js +26 -0
  69. package/dist/esm/components/editors/fields/series-format-fields.d.ts +17 -0
  70. package/dist/esm/components/editors/fields/series-format-fields.d.ts.map +1 -0
  71. package/dist/esm/components/editors/fields/series-format-fields.js +143 -0
  72. package/dist/esm/components/editors/fields/series-name-field.d.ts +16 -0
  73. package/dist/esm/components/editors/fields/series-name-field.d.ts.map +1 -0
  74. package/dist/esm/components/editors/fields/series-name-field.js +31 -0
  75. package/dist/esm/components/editors/fields/text-format-fields.d.ts +20 -0
  76. package/dist/esm/components/editors/fields/text-format-fields.d.ts.map +1 -0
  77. package/dist/esm/components/editors/fields/text-format-fields.js +34 -0
  78. package/dist/esm/config.d.ts.map +1 -1
  79. package/dist/esm/config.js +3 -0
  80. package/dist/esm/interface/create-chart.d.ts +3 -2
  81. package/dist/esm/interface/create-chart.d.ts.map +1 -1
  82. package/dist/esm/interface/create-chart.js +8 -1
  83. package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -1
  84. package/dist/esm/plotly/basic-chart/basic-chart.js +52 -30
  85. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  86. package/dist/esm/utils/chart-orientation.d.ts.map +1 -1
  87. package/dist/esm/utils/chart-orientation.js +20 -0
  88. package/dist/esm/utils/chart-spec-utils.d.ts.map +1 -1
  89. package/dist/esm/utils/chart-spec-utils.js +95 -8
  90. package/dist/esm/utils/echarts-overlays.d.ts +2 -0
  91. package/dist/esm/utils/echarts-overlays.d.ts.map +1 -1
  92. package/dist/esm/utils/echarts-overlays.js +15 -1
  93. package/dist/esm/utils/series-name.d.ts +12 -0
  94. package/dist/esm/utils/series-name.d.ts.map +1 -0
  95. package/dist/esm/utils/series-name.js +24 -0
  96. package/dist/esm/utils/trendline.d.ts +9 -0
  97. package/dist/esm/utils/trendline.d.ts.map +1 -1
  98. package/dist/esm/utils/trendline.js +14 -0
  99. package/package.json +3 -3
@@ -14,27 +14,28 @@ import { circularTooltipFormatter, formatAxisValue, resolveSeriesNumberFormats,
14
14
  import { gapWidthToBarCategoryGap, overlapToBarGap, } from "../utils/bar-layout";
15
15
  import { computeBoxplotSummary } from "../utils/boxplot";
16
16
  import { createBubbleSizeScale, prepareBubbleSeriesData, } from "../utils/bubble";
17
- import { coerceCalculatedLabel, getChartDataRanges, } from "../utils/chart-spec-utils";
18
- import { canSwitchRowColumn, cellLabelGetterFromRangeReader, switchRowColumn, } from "../utils/chart-orientation";
17
+ import { echartsGroupedCategoryAxes, resolveCategoryLevels, } from "../utils/category-levels";
19
18
  import { dataUrlToBlob } from "../utils/chart-image";
19
+ import { canSwitchRowColumn, cellLabelGetterFromRangeReader, switchRowColumn, } from "../utils/chart-orientation";
20
+ import { coerceCalculatedLabel, getChartDataRanges, } from "../utils/chart-spec-utils";
20
21
  import { getChartPalette } from "../utils/chart-styles";
21
- import { buildEchartsOverlaySeries } from "../utils/echarts-overlays";
22
- import { applyEmptyCellsPolicy, connectsAcrossEmptyCells, isolatedPointIndexes, isLineLikeChartType, } from "../utils/empty-cells";
22
+ import { clampDoughnutHole, doughnutRings } from "../utils/doughnut";
23
23
  import { getEchartsGlState, loadEchartsGl, } from "../utils/echarts-gl-loader";
24
- import { echartsGroupedCategoryAxes, resolveCategoryLevels, } from "../utils/category-levels";
24
+ import { buildEchartsOverlaySeries } from "../utils/echarts-overlays";
25
+ import { applyEmptyCellsPolicy, connectsAcrossEmptyCells, isLineLikeChartType, isolatedPointIndexes, } from "../utils/empty-cells";
25
26
  import { gradientToEcharts } from "../utils/gradient";
26
27
  import { buildHierarchyTree, contrastTextColor, extractHierarchyRows, getHierarchyHeaderTitle, } from "../utils/hierarchy";
27
28
  import { aggregateByCategory, binHistogram, computePareto, isParetoSpec, } from "../utils/histogram";
28
29
  import { categoryAxisNameGap, legendLabelsFrom, reserveEchartsLegendBand, } from "../utils/legend-layout";
29
30
  import { clampSecondPieSize, isOfPieChartSpec, otherSliceHalfAngle, resolveOfPieType, splitOfPie, } from "../utils/of-pie";
30
- import { clampDoughnutHole, doughnutRings } from "../utils/doughnut";
31
- import { applyAxisOverrides, dataLabelFormatter, applyLegendOverrides, buildSeriesLabelOption, resolveSeriesDataLabels, } from "../utils/overrides";
31
+ import { applyAxisOverrides, applyLegendOverrides, buildSeriesLabelOption, dataLabelFormatter, resolveSeriesDataLabels, } from "../utils/overrides";
32
+ import { explosionOffsetPx, pieRadiusPx, resolveExplosion, } from "../utils/pie-explosion";
32
33
  import { buildRadarIndicators, buildRadarSeriesData } from "../utils/radar";
33
34
  import { buildRegionMapData, buildSequentialColorRamp, isRegionMapSpec, resolveGeoScope, warnOnceForUnmatchedRegions, } from "../utils/region-map";
34
35
  import { loadRegionMapGeo } from "../utils/region-map-loader";
35
- import { STOCK_ROLES, toCandlestick, toLineSeries, toVolumeSeries, } from "../utils/stock";
36
- import { explosionOffsetPx, pieRadiusPx, resolveExplosion, } from "../utils/pie-explosion";
37
36
  import { chartSeriesId, chartSeriesSpecIndex, resolveClickedDataIndex, resolveClickedSeriesIndex, } from "../utils/series-click";
37
+ import { linkedSeriesName } from "../utils/series-name";
38
+ import { STOCK_ROLES, toCandlestick, toLineSeries, toVolumeSeries, } from "../utils/stock";
38
39
  import { buildBar3DData, buildContourData, buildLine3DSeries, buildSurfaceData, getCategoryCount, getValueExtent, isContourSpec, resolve3DRenderKind, SURFACE_COLOR_RAMP, } from "../utils/three-d";
39
40
  import { computeWaterfall } from "../utils/waterfall";
40
41
  import { isWebGLAvailable } from "../utils/webgl";
@@ -518,9 +519,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
518
519
  : "rgba(0,0,0,0.06)";
519
520
  const borderColor = isDarkMode ? "rgba(255,255,255,0.3)" : "#FFFFFF";
520
521
  const regionSeriesLabel = buildSeriesLabelOption(spec.dataLabels, undefined, locale);
521
- const seriesName = seriesEntry?.dataLabel && !isAFormula(seriesEntry.dataLabel)
522
- ? seriesEntry.dataLabel
523
- : "";
522
+ const seriesName = linkedSeriesName(seriesEntry, getDomainValuesFromRange) ??
523
+ (seriesEntry?.dataLabel && !isAFormula(seriesEntry.dataLabel)
524
+ ? seriesEntry.dataLabel
525
+ : "");
524
526
  chartRef.current?.setOption({
525
527
  // The visualMap color scale is the value legend; the series
526
528
  // legend stays hidden unless an authored override turns it on.
@@ -576,6 +578,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
576
578
  previousSeriesCount.current = 1;
577
579
  return;
578
580
  }
581
+ // Box & whisker: hidden value x-axis the point overlays are plotted on.
582
+ let boxplotOverlayAxis;
579
583
  const chartDomains = [];
580
584
  const chartSeries = [];
581
585
  const legend = { ...defaultLegendConfig, data: [] };
@@ -597,8 +601,9 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
597
601
  const seriesInputs = [];
598
602
  for (let i = 0; i < spec.series.length; i++) {
599
603
  const seriesEntry = spec.series[i];
600
- let resolvedLabel = seriesEntry.dataLabel ?? "";
601
- if (resolvedLabel && isAFormula(resolvedLabel)) {
604
+ const linkedLabel = linkedSeriesName(seriesEntry, getDomainValuesFromRange);
605
+ let resolvedLabel = linkedLabel ?? seriesEntry.dataLabel ?? "";
606
+ if (linkedLabel === undefined && resolvedLabel && isAFormula(resolvedLabel)) {
602
607
  resolvedLabel = coerceCalculatedLabel(await onRequestCalculate(resolvedLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex));
603
608
  }
604
609
  const xValues = perSeriesX[i] ?? perSeriesX[0];
@@ -1044,9 +1049,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1044
1049
  const categories = flattenChartDataValues(spec.domains, getDomainValuesFromRange, spec.hiddenDimensionStrategy, false).map((v) => String(v ?? ""));
1045
1050
  const contourSeriesInputs = [];
1046
1051
  for (const seriesEntry of spec.series ?? []) {
1047
- const dataLabel = !isNil(seriesEntry.dataLabel) && isAFormula(seriesEntry.dataLabel)
1048
- ? coerceCalculatedLabel(await onRequestCalculate(seriesEntry.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
1049
- : seriesEntry.dataLabel ?? "";
1052
+ const dataLabel = linkedSeriesName(seriesEntry, getDomainValuesFromRange) ??
1053
+ (!isNil(seriesEntry.dataLabel) && isAFormula(seriesEntry.dataLabel)
1054
+ ? coerceCalculatedLabel(await onRequestCalculate(seriesEntry.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
1055
+ : seriesEntry.dataLabel ?? "");
1050
1056
  const values = flattenChartDataValues([seriesEntry], getSeriesValuesFromRange, spec.hiddenDimensionStrategy).map(sanitizeChartValue);
1051
1057
  contourSeriesInputs.push({ name: dataLabel, values });
1052
1058
  }
@@ -1163,9 +1169,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1163
1169
  const categories = flattenChartDataValues(spec.domains, getDomainValuesFromRange, spec.hiddenDimensionStrategy, false).map((v) => String(v ?? ""));
1164
1170
  const threeDSeriesInputs = [];
1165
1171
  for (const seriesEntry of spec.series ?? []) {
1166
- const dataLabel = !isNil(seriesEntry.dataLabel) && isAFormula(seriesEntry.dataLabel)
1167
- ? coerceCalculatedLabel(await onRequestCalculate(seriesEntry.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
1168
- : seriesEntry.dataLabel ?? "";
1172
+ const dataLabel = linkedSeriesName(seriesEntry, getDomainValuesFromRange) ??
1173
+ (!isNil(seriesEntry.dataLabel) && isAFormula(seriesEntry.dataLabel)
1174
+ ? coerceCalculatedLabel(await onRequestCalculate(seriesEntry.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
1175
+ : seriesEntry.dataLabel ?? "");
1169
1176
  const values = flattenChartDataValues([seriesEntry], getSeriesValuesFromRange, spec.hiddenDimensionStrategy).map(sanitizeChartValue);
1170
1177
  threeDSeriesInputs.push({ name: dataLabel, values });
1171
1178
  }
@@ -1368,9 +1375,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1368
1375
  const categories = flattenChartDataValues(spec.domains, getDomainValuesFromRange, spec.hiddenDimensionStrategy, skipHeaders);
1369
1376
  const radarSeriesInputs = [];
1370
1377
  for (const seriesEntry of spec.series ?? []) {
1371
- const label = seriesEntry.dataLabel && !isAFormula(seriesEntry.dataLabel)
1372
- ? seriesEntry.dataLabel
1373
- : "";
1378
+ const label = linkedSeriesName(seriesEntry, getDomainValuesFromRange) ??
1379
+ (seriesEntry.dataLabel && !isAFormula(seriesEntry.dataLabel)
1380
+ ? seriesEntry.dataLabel
1381
+ : "");
1374
1382
  const values = flattenChartDataValues([seriesEntry], getSeriesValuesFromRange, spec.hiddenDimensionStrategy).map((v) => (typeof v === "number" ? v : Number(v)));
1375
1383
  radarSeriesInputs.push({ name: label, values });
1376
1384
  }
@@ -1856,9 +1864,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
1856
1864
  ]);
1857
1865
  const paletteColorAt = (index) => chartPaletteColorAt(seriesPalette, index);
1858
1866
  for (const [specSeriesIndex, series] of (spec.series ?? []).entries()) {
1859
- const dataLabel = !isNil(series.dataLabel) && isAFormula(series.dataLabel)
1860
- ? coerceCalculatedLabel(await onRequestCalculate(series.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
1861
- : series.dataLabel ?? "";
1867
+ const dataLabel = linkedSeriesName(series, getDomainValuesFromRange) ??
1868
+ (!isNil(series.dataLabel) && isAFormula(series.dataLabel)
1869
+ ? coerceCalculatedLabel(await onRequestCalculate(series.dataLabel, position.sheetId ?? sheetId, position.overlayPosition.anchorCell.rowIndex, position.overlayPosition.anchorCell.columnIndex))
1870
+ : series.dataLabel ?? "");
1862
1871
  // Label
1863
1872
  if (dataLabel) {
1864
1873
  legend.data?.push(dataLabel);
@@ -2464,7 +2473,9 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2464
2473
  }
2465
2474
  }
2466
2475
  const boxSeriesSpecs = spec.series ?? [];
2467
- const slots = grouped ? groups : boxSeriesSpecs.map((s, i) => s.dataLabel && !isAFormula(s.dataLabel) ? s.dataLabel : `Series ${i + 1}`);
2476
+ // Without a category domain every series is one box and all of them
2477
+ // share a single category group, as Excel draws it (axis label "1").
2478
+ const slots = grouped ? groups : ["1"];
2468
2479
  // Excel sizes the boxes like columns: gapWidth is a percentage of the
2469
2480
  // box width (default 150). echarts' boxplot only clamps its width
2470
2481
  // (`boxWidth`), so the clamp is the box width that gap allows in the
@@ -2474,9 +2485,20 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2474
2485
  const boxBand = slots.length ? boxPlotWidth / slots.length : boxPlotWidth;
2475
2486
  const boxWidthMax = Math.max(2, Math.round(boxBand / (Math.max(1, boxSeriesSpecs.length) + boxGap / 100)));
2476
2487
  const boxCount = Math.max(1, boxSeriesSpecs.length);
2477
- // Points of series i sit at that series' sub-slot inside the band
2478
- // (echarts spreads a category's boxes over ~80% of it).
2479
- const slotX = (slotIndex, seriesIndex) => slotIndex + ((seriesIndex + 0.5) / boxCount - 0.5) * 0.8;
2488
+ // echarts lays a category's boxes out side by side (boxplotLayout:
2489
+ // 80% of the band minus 2px, a 30% gap between boxes, the clamp only
2490
+ // narrows a box around its centre). The outliers, inner points and
2491
+ // means of a series must sit on that series' box, so they are plotted
2492
+ // on a hidden value axis spanning the same bands (a category axis
2493
+ // rounds a fractional position to the band centre, #1099).
2494
+ const boxCentre = (slotIndex, seriesIndex) => {
2495
+ const band = Math.max(1, boxBand);
2496
+ const available = band * 0.8 - 2;
2497
+ const gap = (available / boxCount) * 0.3;
2498
+ const boxWidth = (available - gap * (boxCount - 1)) / boxCount;
2499
+ const offset = boxWidth / 2 - available / 2 + seriesIndex * (gap + boxWidth);
2500
+ return slotIndex + offset / band;
2501
+ };
2480
2502
  chartDomains.length = 0;
2481
2503
  chartDomains.push({
2482
2504
  type: "category",
@@ -2489,12 +2511,23 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2489
2511
  nameLocation: "middle",
2490
2512
  nameGap: 30,
2491
2513
  });
2514
+ boxplotOverlayAxis = {
2515
+ type: "value",
2516
+ min: -0.5,
2517
+ max: slots.length - 0.5,
2518
+ show: false,
2519
+ axisLine: { show: false },
2520
+ axisTick: { show: false },
2521
+ axisLabel: { show: false },
2522
+ splitLine: { show: false },
2523
+ };
2492
2524
  chartSeries.length = 0;
2493
2525
  const boxLegend = [];
2494
2526
  boxSeriesSpecs.forEach((seriesEntry, seriesIndex) => {
2495
- const label = seriesEntry.dataLabel && !isAFormula(seriesEntry.dataLabel)
2496
- ? seriesEntry.dataLabel
2497
- : "";
2527
+ const label = linkedSeriesName(seriesEntry, getDomainValuesFromRange) ??
2528
+ (seriesEntry.dataLabel && !isAFormula(seriesEntry.dataLabel)
2529
+ ? seriesEntry.dataLabel
2530
+ : "");
2498
2531
  const rawValues = flattenChartDataValues([seriesEntry], getSeriesValuesFromRange, spec.hiddenDimensionStrategy).map((v) => (typeof v === "number" ? v : Number(v)));
2499
2532
  const color = seriesEntry.color ?? paletteColorAt(seriesIndex);
2500
2533
  const boxData = [];
@@ -2504,7 +2537,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2504
2537
  const meanLine = [];
2505
2538
  const slotValues = grouped
2506
2539
  ? groups.map((g) => rawValues.filter((_, i) => boxCategories[i] === g))
2507
- : slots.map((_, i) => (i === seriesIndex ? rawValues : []));
2540
+ : [rawValues];
2508
2541
  slotValues.forEach((values, slotIndex) => {
2509
2542
  const summary = computeBoxplotSummary(values, { quartileMethod });
2510
2543
  if (!summary) {
@@ -2522,7 +2555,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2522
2555
  summary.upperWhisker,
2523
2556
  ]);
2524
2557
  meanLine.push(summary.mean);
2525
- const x = slotX(slotIndex, seriesIndex);
2558
+ const x = boxCentre(slotIndex, seriesIndex);
2526
2559
  if (showOutliers) {
2527
2560
  for (const outlier of summary.outliers)
2528
2561
  outlierPoints.push([x, outlier]);
@@ -2551,6 +2584,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2551
2584
  type: "scatter",
2552
2585
  name,
2553
2586
  data: points,
2587
+ xAxisIndex: 1,
2554
2588
  itemStyle: { color },
2555
2589
  tooltip: { show: false },
2556
2590
  ...extra,
@@ -2568,7 +2602,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2568
2602
  chartSeries.push({
2569
2603
  type: "line",
2570
2604
  name: "",
2571
- data: meanLine.map((m, slotIndex) => m === null ? null : [slotX(slotIndex, seriesIndex), m]),
2605
+ xAxisIndex: 1,
2606
+ data: meanLine.map((m, slotIndex) => m === null ? null : [boxCentre(slotIndex, seriesIndex), m]),
2572
2607
  showSymbol: false,
2573
2608
  lineStyle: { color, type: "dashed", width: 1 },
2574
2609
  itemStyle: { color },
@@ -2983,7 +3018,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
2983
3018
  nameGap: CATEGORY_AXIS_NAME_GAP,
2984
3019
  },
2985
3020
  ];
2986
- return base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions));
3021
+ const axes = base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions));
3022
+ return boxplotOverlayAxis ? [...axes, boxplotOverlayAxis] : axes;
2987
3023
  })(),
2988
3024
  yAxis: (() => {
2989
3025
  if (isOfPie) {
@@ -0,0 +1,48 @@
1
+ import type { EmbeddedChart } from "@rowsncolumns/spreadsheet";
2
+ type ChartSpec = EmbeddedChart["spec"];
3
+ /**
4
+ * Which editor controls a chart type gets — Excel's rule set, so an option
5
+ * is only offered where the chart (and the renderers) honour it
6
+ * (rowsncolumns/spreadsheet#1102): a funnel has no axes, a box & whisker
7
+ * no trendlines, a pie no gap width.
8
+ */
9
+ export type ChartEditorCapabilities = {
10
+ category: "basic" | "bubble" | "hierarchy";
11
+ /** Value axis scale / gridlines / number format / visibility / font. */
12
+ hasValueAxis: boolean;
13
+ /** Category axis gridlines / direction / visibility / font. */
14
+ hasCategoryAxis: boolean;
15
+ /** The second value axis a series can opt into. */
16
+ hasSecondaryAxis: boolean;
17
+ /** Vertical / horizontal axis titles. */
18
+ hasAxisTitles: boolean;
19
+ hasTrendlines: boolean;
20
+ hasErrorBars: boolean;
21
+ /** Gap between category clusters (bars, columns, bins, boxes, waterfall steps). */
22
+ hasGapWidth: boolean;
23
+ /** Overlap of the bars of one category. */
24
+ hasOverlap: boolean;
25
+ /** "Invert if negative" + the negative fill. */
26
+ hasInvertIfNegative: boolean;
27
+ /** Per-series marker shape and size. */
28
+ hasMarkers: boolean;
29
+ hasLegend: boolean;
30
+ hasDataLabels: boolean;
31
+ /** Pie / doughnut "percentage" label. */
32
+ hasPercentLabels: boolean;
33
+ /** Bubble "bubble size" label. */
34
+ hasBubbleSizeLabels: boolean;
35
+ /** Per-series fill options: colour, gradient, no fill, point colours. */
36
+ hasSeriesFill: boolean;
37
+ /** Per-point colours (Format Data Point → Fill). */
38
+ hasPointColors: boolean;
39
+ /** Per-series chart type (combo). */
40
+ hasSeriesChartType: boolean;
41
+ /** Multi-level category axis (several label columns). */
42
+ hasCategoryLevels: boolean;
43
+ /** Empty-cell / #N/A handling (cell-driven series). */
44
+ hasEmptyCellOptions: boolean;
45
+ };
46
+ export declare const getChartEditorCapabilities: (spec: Pick<ChartSpec, "chartType">) => ChartEditorCapabilities;
47
+ export {};
48
+ //# sourceMappingURL=chart-editor-capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-editor-capabilities.d.ts","sourceRoot":"","sources":["../../../../components/editors/chart-editor-capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,KAAK,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAGvC;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC3C,wEAAwE;IACxE,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,eAAe,EAAE,OAAO,CAAC;IACzB,mDAAmD;IACnD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yCAAyC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,mFAAmF;IACnF,WAAW,EAAE,OAAO,CAAC;IACrB,2CAA2C;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,mBAAmB,EAAE,OAAO,CAAC;IAC7B,wCAAwC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kCAAkC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,yEAAyE;IACzE,aAAa,EAAE,OAAO,CAAC;IACvB,oDAAoD;IACpD,cAAc,EAAE,OAAO,CAAC;IACxB,qCAAqC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yDAAyD;IACzD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,uDAAuD;IACvD,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AA6BF,eAAO,MAAM,0BAA0B,GACrC,MAAM,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,KACjC,uBAmCF,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { getChartCategory } from "../../utils/chart-spec-utils";
2
+ const CARTESIAN = new Set([
3
+ "bar",
4
+ "column",
5
+ "line",
6
+ "area",
7
+ "combo",
8
+ "stock",
9
+ "histogram",
10
+ "boxplot",
11
+ "waterfall",
12
+ "scatter",
13
+ "bubble",
14
+ ]);
15
+ /** Excel offers trendlines and error bars on the unstacked XY-ish families only. */
16
+ const FITTABLE = new Set([
17
+ "bar",
18
+ "column",
19
+ "line",
20
+ "area",
21
+ "combo",
22
+ "scatter",
23
+ "bubble",
24
+ ]);
25
+ const BARS = new Set(["bar", "column", "combo"]);
26
+ export const getChartEditorCapabilities = (spec) => {
27
+ const type = spec.chartType;
28
+ const category = getChartCategory(type);
29
+ const cartesian = CARTESIAN.has(type);
30
+ const fittable = FITTABLE.has(type);
31
+ const circular = type === "pie" || type === "doughnut";
32
+ const hierarchy = category === "hierarchy";
33
+ return {
34
+ category,
35
+ hasValueAxis: cartesian || type === "radar",
36
+ hasCategoryAxis: cartesian,
37
+ hasSecondaryAxis: fittable,
38
+ hasAxisTitles: cartesian,
39
+ hasTrendlines: fittable,
40
+ hasErrorBars: fittable,
41
+ hasGapWidth: BARS.has(type) ||
42
+ type === "histogram" ||
43
+ type === "boxplot" ||
44
+ type === "waterfall",
45
+ hasOverlap: BARS.has(type),
46
+ hasInvertIfNegative: BARS.has(type),
47
+ hasMarkers: type === "line" || type === "scatter" || type === "radar",
48
+ hasLegend: true,
49
+ hasDataLabels: type !== "regionMap",
50
+ hasPercentLabels: circular,
51
+ hasBubbleSizeLabels: type === "bubble",
52
+ hasSeriesFill: type !== "regionMap" && type !== "stock",
53
+ hasPointColors: !hierarchy && type !== "regionMap" && type !== "stock" && type !== "radar",
54
+ hasSeriesChartType: type === "combo",
55
+ hasCategoryLevels: BARS.has(type) || type === "line" || type === "area" || type === "stock",
56
+ hasEmptyCellOptions: category === "basic",
57
+ };
58
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"chart-editor.d.ts","sourceRoot":"","sources":["../../../../components/editors/chart-editor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,aAAa,EAGd,MAAM,2BAA2B,CAAC;AAuBnC,OAAO,KAAkB,MAAM,OAAO,CAAC;AAMvC,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,+BAA+B,CAAC;AA4CvC,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,kBAAkB,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAChD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;CAC1C,CAAC;AAuBF,eAAO,MAAM,WAAW,GAAI,sEAMzB,gBAAgB,sBAw6DlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"chart-editor.d.ts","sourceRoot":"","sources":["../../../../components/editors/chart-editor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,aAAa,EAGd,MAAM,2BAA2B,CAAC;AAwBnC,OAAO,KAAkB,MAAM,OAAO,CAAC;AAIvC,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,+BAA+B,CAAC;AAuDvC,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,kBAAkB,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAChD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;CAC1C,CAAC;AAuBF,eAAO,MAAM,WAAW,GAAI,sEAMzB,gBAAgB,sBAkhClB,CAAC;AAEF,eAAe,WAAW,CAAC"}