@rowsncolumns/charts 28.1.8 → 28.1.10
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.
- package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/cjs/basic-chart/basic-chart.js +483 -417
- package/dist/cjs/basic-chart/helpers.d.ts.map +1 -1
- package/dist/cjs/basic-chart/helpers.js +2 -0
- package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/cjs/plotly/basic-chart/basic-chart.js +315 -184
- package/dist/cjs/plotly/basic-chart/helpers.d.ts.map +1 -1
- package/dist/cjs/plotly/basic-chart/helpers.js +2 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/utils/bubble.d.ts +34 -1
- package/dist/cjs/utils/bubble.d.ts.map +1 -1
- package/dist/cjs/utils/bubble.js +39 -13
- package/dist/cjs/utils/echarts-overlays.d.ts +46 -0
- package/dist/cjs/utils/echarts-overlays.d.ts.map +1 -0
- package/dist/cjs/utils/echarts-overlays.js +134 -0
- package/dist/cjs/utils/hierarchy.d.ts +23 -1
- package/dist/cjs/utils/hierarchy.d.ts.map +1 -1
- package/dist/cjs/utils/hierarchy.js +179 -55
- package/dist/cjs/utils/overrides.d.ts.map +1 -1
- package/dist/cjs/utils/overrides.js +9 -2
- package/dist/cjs/utils/trendline.d.ts +7 -1
- package/dist/cjs/utils/trendline.d.ts.map +1 -1
- package/dist/cjs/utils/trendline.js +82 -18
- package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/esm/basic-chart/basic-chart.js +320 -239
- package/dist/esm/basic-chart/helpers.d.ts.map +1 -1
- package/dist/esm/basic-chart/helpers.js +2 -0
- package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/esm/plotly/basic-chart/basic-chart.js +235 -58
- package/dist/esm/plotly/basic-chart/helpers.d.ts.map +1 -1
- package/dist/esm/plotly/basic-chart/helpers.js +2 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/utils/bubble.d.ts +34 -1
- package/dist/esm/utils/bubble.d.ts.map +1 -1
- package/dist/esm/utils/bubble.js +35 -11
- package/dist/esm/utils/echarts-overlays.d.ts +46 -0
- package/dist/esm/utils/echarts-overlays.d.ts.map +1 -0
- package/dist/esm/utils/echarts-overlays.js +135 -0
- package/dist/esm/utils/hierarchy.d.ts +23 -1
- package/dist/esm/utils/hierarchy.d.ts.map +1 -1
- package/dist/esm/utils/hierarchy.js +168 -50
- package/dist/esm/utils/overrides.d.ts.map +1 -1
- package/dist/esm/utils/overrides.js +9 -2
- package/dist/esm/utils/trendline.d.ts +7 -1
- package/dist/esm/utils/trendline.d.ts.map +1 -1
- package/dist/esm/utils/trendline.js +67 -14
- package/package.json +3 -3
|
@@ -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,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,
|
|
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,YAiBjE,CAAC"}
|
|
@@ -119,6 +119,8 @@ export const showXAxis = (type) => {
|
|
|
119
119
|
type === "column" ||
|
|
120
120
|
type === "bubble" ||
|
|
121
121
|
type === "histogram" ||
|
|
122
|
+
// A waterfall's steps are categories (Opening, Sales, …) (#1070).
|
|
123
|
+
type === "waterfall" ||
|
|
122
124
|
// Stock is line-derived and lives on a category x-axis. Radar is
|
|
123
125
|
// axis-less (it uses a polar `radar` indicator instead).
|
|
124
126
|
type === "stock") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"basic-chart.d.ts","sourceRoot":"","sources":["../../../../plotly/basic-chart/basic-chart.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"basic-chart.d.ts","sourceRoot":"","sources":["../../../../plotly/basic-chart/basic-chart.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AA6ZxD,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAmGnD,eAAO,MAAM,UAAU,GAAI,gPAkBxB,eAAe,sBAgkFjB,CAAC"}
|
|
@@ -18,13 +18,13 @@ import { plotlyMulticategoryX, resolveCategoryLevels, } from "../../utils/catego
|
|
|
18
18
|
import { gradientToPlotly } from "../../utils/gradient";
|
|
19
19
|
import { BAND_GAP, legendLabelsFrom, measureTextWidth, reservePlotlyLegendBand, } from "../../utils/legend-layout";
|
|
20
20
|
import { clampDoughnutHole, doughnutRings } from "../../utils/doughnut";
|
|
21
|
-
import { resolveSeriesDataLabels } from "../../utils/overrides";
|
|
21
|
+
import { dataLabelFormatter, resolveSeriesDataLabels, } from "../../utils/overrides";
|
|
22
22
|
import { plotlyTitleText } from "../../utils/plotly-title";
|
|
23
23
|
import { explosionToPlotlyPull, resolveExplosion, } from "../../utils/pie-explosion";
|
|
24
24
|
import { resolvePlotlyClickSelection, wasDragged, } from "../../utils/plotly-click";
|
|
25
25
|
import { buildPlotlySurfaceTrace } from "../../utils/plotly-surface";
|
|
26
26
|
import { normalize3DSourceType, } from "../../utils/three-d";
|
|
27
|
-
import { extractHierarchyRows, getHierarchyHeaderTitle, toPlotlyHierarchy, } from "../../utils/hierarchy";
|
|
27
|
+
import { extractHierarchyRows, getHierarchyHeaderTitle, hierarchyNodeColors, toPlotlyHierarchy, } from "../../utils/hierarchy";
|
|
28
28
|
import { binHistogram, computePareto, isParetoSpec, } from "../../utils/histogram";
|
|
29
29
|
import { clampSecondPieSize, isOfPieChartSpec, otherSliceHalfAngle, resolveOfPieType, splitOfPie, } from "../../utils/of-pie";
|
|
30
30
|
import { buildRadarIndicators } from "../../utils/radar";
|
|
@@ -339,6 +339,13 @@ const sanitizeChartValue = (value) => {
|
|
|
339
339
|
* (Excel): every range's points in sequence. A single range passes through
|
|
340
340
|
* untouched so the common path renders exactly as before.
|
|
341
341
|
*/
|
|
342
|
+
/**
|
|
343
|
+
* The plain value array of a series' data. (`"values" in seriesData` is
|
|
344
|
+
* true for an ARRAY too — `Array.prototype.values` — which handed the
|
|
345
|
+
* waterfall / funnel / histogram / boxplot branches that method instead
|
|
346
|
+
* of the numbers.)
|
|
347
|
+
*/
|
|
348
|
+
const seriesValuesOf = (seriesData) => Array.isArray(seriesData) ? seriesData : seriesData.values;
|
|
342
349
|
const mergeSeriesData = (parts) => {
|
|
343
350
|
if (parts.length === 1)
|
|
344
351
|
return parts[0];
|
|
@@ -389,6 +396,11 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
389
396
|
// The plotted `<div>` plotly hands back on init — what `Plotly.toImage`
|
|
390
397
|
// rasterises for Copy as Picture and the print preview.
|
|
391
398
|
const graphDivRef = useRef(null);
|
|
399
|
+
// Plotly zooms a treemap / sunburst into the clicked branch by default;
|
|
400
|
+
// Excel has no drill-down, so the click handler cancels it unless the
|
|
401
|
+
// spec opts in (`drillDown`).
|
|
402
|
+
const drillDownRef = useRef(false);
|
|
403
|
+
drillDownRef.current = Boolean(spec.drillDown);
|
|
392
404
|
const focusSheet = useFocusSheet();
|
|
393
405
|
// Excel selects a data series (or one data point) when it is clicked
|
|
394
406
|
// inside the chart and the grid highlights its source cells. The plot
|
|
@@ -516,8 +528,16 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
516
528
|
sizeValues: sharedSizeValues,
|
|
517
529
|
series: seriesInputs,
|
|
518
530
|
});
|
|
519
|
-
|
|
531
|
+
// Bubble diameters scale from zero (area by default, width when
|
|
532
|
+
// `sizeRepresents` says so) and the largest bubble is a fraction of
|
|
533
|
+
// the plot area, which the margins below leave of the chart (#1068).
|
|
534
|
+
const sizeScale = createBubbleSizeScale(sizeValues, {
|
|
535
|
+
sizeRepresents: spec.sizeRepresents,
|
|
536
|
+
bubbleScale: spec.bubbleScale,
|
|
537
|
+
plotMinSide: Math.min((width ?? 0) - 80, (height ?? 0) - 100),
|
|
538
|
+
});
|
|
520
539
|
const hasStringX = categories.length > 0;
|
|
540
|
+
const hasBubbleSecondaryAxis = (spec.series ?? []).some((seriesEntry) => seriesEntry?.useSecondaryAxis);
|
|
521
541
|
const palette = getChartPalette(spec, [
|
|
522
542
|
theme.themeColors.accent1,
|
|
523
543
|
theme.themeColors.accent2,
|
|
@@ -553,16 +573,56 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
553
573
|
const lineShapeFor = (style) => style === "smooth" || style === "smoothMarker"
|
|
554
574
|
? "spline"
|
|
555
575
|
: "linear";
|
|
556
|
-
const traces =
|
|
576
|
+
const traces = [];
|
|
577
|
+
preparedSeries.forEach((entry, index) => {
|
|
557
578
|
const xValues = entry.points.map((point) => hasStringX
|
|
558
579
|
? point.xLabel ??
|
|
559
580
|
categories[Math.round(point.value[0])] ??
|
|
560
581
|
point.value[0]
|
|
561
582
|
: point.value[0]);
|
|
562
583
|
const yValues = entry.points.map((point) => point.value[1]);
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
const
|
|
584
|
+
const rawSizes = entry.points.map((point) => point.value[2]);
|
|
585
|
+
const seriesSpec = spec.series[index];
|
|
586
|
+
const seriesColor = seriesSpec?.color ?? chartPaletteColorAt(palette, index);
|
|
587
|
+
const perSeriesMarker = seriesSpec?.marker ?? undefined;
|
|
588
|
+
// Per-point colours (`<c:dPt>`) key on the point's source row
|
|
589
|
+
// (`BubbleSeriesPoint.index` — points without a Y are skipped).
|
|
590
|
+
const pointColorOverrides = new Map();
|
|
591
|
+
for (const dp of seriesSpec?.dataPointColors ?? []) {
|
|
592
|
+
if (typeof dp?.idx === "number" && typeof dp?.color === "string") {
|
|
593
|
+
pointColorOverrides.set(dp.idx, dp.color);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const pointColors = (base) => pointColorOverrides.size
|
|
597
|
+
? entry.points.map((point) => pointColorOverrides.get(point.index) ?? base)
|
|
598
|
+
: base;
|
|
599
|
+
// Hover reads the bubble label and size from `customdata`, leaving
|
|
600
|
+
// `text` to the data labels.
|
|
601
|
+
const customdata = entry.points.map((point) => [point.name, point.value[2]]);
|
|
602
|
+
const onSecondary = seriesSpec?.useSecondaryAxis ? { yaxis: "y2" } : {};
|
|
603
|
+
// This series' data labels: Y value by default, the bubble's size
|
|
604
|
+
// with `showBubbleSize`, the bubble label with `showCategoryName`.
|
|
605
|
+
const labelOpts = resolveSeriesDataLabels(spec.dataLabels, seriesSpec);
|
|
606
|
+
const labelText = labelOpts?.show
|
|
607
|
+
? entry.points.map((point) => dataLabelFormatter(labelOpts, locale)({
|
|
608
|
+
value: point.value,
|
|
609
|
+
name: point.name,
|
|
610
|
+
seriesName: entry.name,
|
|
611
|
+
data: point,
|
|
612
|
+
}))
|
|
613
|
+
: undefined;
|
|
614
|
+
const labelPosition = labelOpts?.show
|
|
615
|
+
? (labelOpts.position && PLOTLY_DLBL_POS_POINT[labelOpts.position]) ??
|
|
616
|
+
"top center"
|
|
617
|
+
: undefined;
|
|
618
|
+
const withLabels = (trace) => labelText
|
|
619
|
+
? {
|
|
620
|
+
...trace,
|
|
621
|
+
mode: withTextMode(trace.mode),
|
|
622
|
+
text: labelText,
|
|
623
|
+
textposition: labelPosition,
|
|
624
|
+
}
|
|
625
|
+
: trace;
|
|
566
626
|
if (isScatter) {
|
|
567
627
|
// Per-series marker.symbol = "none" → pure line (no markers).
|
|
568
628
|
// Per-series marker.size overrides the default. Falls back to
|
|
@@ -574,14 +634,14 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
574
634
|
: plotlyModeForScatterStyle(scatterStyle);
|
|
575
635
|
const markerSize = perSeriesMarker?.size ?? 6;
|
|
576
636
|
const markerColor = perSeriesMarker?.color ?? seriesColor;
|
|
577
|
-
|
|
637
|
+
traces.push(withLabels({
|
|
578
638
|
type: "scatter",
|
|
579
639
|
mode,
|
|
580
640
|
x: xValues,
|
|
581
641
|
y: yValues,
|
|
582
|
-
|
|
642
|
+
customdata,
|
|
583
643
|
name: entry.name,
|
|
584
|
-
hovertemplate: "%{
|
|
644
|
+
hovertemplate: "%{customdata[0]}<br>X: %{x}<br>Y: %{y}<extra></extra>",
|
|
585
645
|
line: {
|
|
586
646
|
shape: lineShapeFor(scatterStyle),
|
|
587
647
|
color: seriesColor,
|
|
@@ -592,31 +652,82 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
592
652
|
: {
|
|
593
653
|
marker: {
|
|
594
654
|
size: markerSize,
|
|
595
|
-
color: markerColor,
|
|
655
|
+
color: pointColors(markerColor),
|
|
596
656
|
symbol: mapMarkerSymbolToPlotly(symbol),
|
|
597
657
|
},
|
|
598
658
|
}),
|
|
599
|
-
|
|
659
|
+
...onSecondary,
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
// Bubble — third dimension scaled into marker diameter.
|
|
664
|
+
const scaledSizes = rawSizes.map((value) => sizeScale(value));
|
|
665
|
+
traces.push(withLabels({
|
|
666
|
+
type: "scatter",
|
|
667
|
+
mode: "markers",
|
|
668
|
+
x: xValues,
|
|
669
|
+
y: yValues,
|
|
670
|
+
customdata,
|
|
671
|
+
name: entry.name,
|
|
672
|
+
hovertemplate: "%{customdata[0]}<br>X: %{x}<br>Y: %{y}<br>Size: %{customdata[1]}<extra></extra>",
|
|
673
|
+
marker: {
|
|
674
|
+
size: scaledSizes,
|
|
675
|
+
sizemode: "diameter",
|
|
676
|
+
color: pointColors(seriesColor),
|
|
677
|
+
opacity: 0.7,
|
|
678
|
+
},
|
|
679
|
+
...onSecondary,
|
|
680
|
+
}));
|
|
681
|
+
}
|
|
682
|
+
// Trendlines on the real (x, y) points — a regression of Y on X,
|
|
683
|
+
// not on the point index — and native error bars (#1067).
|
|
684
|
+
const numericX = entry.points.map((point) => point.value[0]);
|
|
685
|
+
for (const tl of seriesSpec?.trendlines ?? []) {
|
|
686
|
+
const result = computeTrendline(yValues, {
|
|
687
|
+
type: tl.type,
|
|
688
|
+
order: tl.order,
|
|
689
|
+
period: tl.period,
|
|
690
|
+
forecastForward: tl.forecastForward,
|
|
691
|
+
forecastBackward: tl.forecastBackward,
|
|
692
|
+
}, numericX);
|
|
693
|
+
if (!result || result.points.length < 2)
|
|
694
|
+
continue;
|
|
695
|
+
const tlName = tl.name ??
|
|
696
|
+
`${entry.name ? entry.name + " " : ""}${tl.type ?? "linear"} trend`;
|
|
697
|
+
traces.push({
|
|
698
|
+
type: "scatter",
|
|
699
|
+
mode: "lines",
|
|
700
|
+
name: tlName,
|
|
701
|
+
x: result.points.map((p) => hasStringX
|
|
702
|
+
? categories[Math.round(p.x)] ?? p.x
|
|
703
|
+
: p.x),
|
|
704
|
+
y: result.points.map((p) => p.y),
|
|
705
|
+
line: { color: tl.color ?? seriesColor, dash: "dash", width: 2 },
|
|
706
|
+
hoverinfo: "skip",
|
|
707
|
+
...onSecondary,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
if (seriesSpec?.errorBars) {
|
|
711
|
+
const direction = seriesSpec.errorBars.direction ?? "y";
|
|
712
|
+
// An X error bar sizes from the X values (a percentage or
|
|
713
|
+
// deviation of X).
|
|
714
|
+
const magnitudes = computeErrorBars(direction === "x" ? numericX : yValues, resolveCustomErrorBarValues(seriesSpec.errorBars, (range) => getSeriesValuesFromRange(range, spec.hiddenDimensionStrategy)));
|
|
715
|
+
if (magnitudes) {
|
|
716
|
+
const symmetric = magnitudes.plus.every((plus, i) => plus === magnitudes.minus[i]);
|
|
717
|
+
const errorConfig = {
|
|
718
|
+
type: "data",
|
|
719
|
+
symmetric,
|
|
720
|
+
array: magnitudes.plus,
|
|
721
|
+
...(symmetric ? {} : { arrayminus: magnitudes.minus }),
|
|
722
|
+
color: seriesColor,
|
|
723
|
+
...(seriesSpec.errorBars.noEndCap ? { width: 0 } : {}),
|
|
724
|
+
};
|
|
725
|
+
const target = traces[traces.length - 1 - (seriesSpec.trendlines?.length ?? 0)];
|
|
726
|
+
if (target) {
|
|
727
|
+
target[direction === "x" ? "error_x" : "error_y"] = errorConfig;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
600
730
|
}
|
|
601
|
-
// Bubble — third dimension scaled into marker diameter.
|
|
602
|
-
const rawSizes = entry.points.map((point) => point.value[2]);
|
|
603
|
-
const scaledSizes = rawSizes.map((value) => sizeScale(value));
|
|
604
|
-
return {
|
|
605
|
-
type: "scatter",
|
|
606
|
-
mode: "markers",
|
|
607
|
-
x: xValues,
|
|
608
|
-
y: yValues,
|
|
609
|
-
text: labelValuesForSeries,
|
|
610
|
-
customdata: rawSizes,
|
|
611
|
-
name: entry.name,
|
|
612
|
-
hovertemplate: "%{text}<br>X: %{x}<br>Y: %{y}<br>Size: %{customdata}<extra></extra>",
|
|
613
|
-
marker: {
|
|
614
|
-
size: scaledSizes,
|
|
615
|
-
sizemode: "diameter",
|
|
616
|
-
color: seriesColor,
|
|
617
|
-
opacity: 0.7,
|
|
618
|
-
},
|
|
619
|
-
};
|
|
620
731
|
});
|
|
621
732
|
// Resolve text formats: chart-authored values (via OOXML <c:txPr>)
|
|
622
733
|
// override the theme-derived defaults. `spec.titleTextFormat`,
|
|
@@ -636,7 +747,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
636
747
|
x: 0.02,
|
|
637
748
|
xanchor: "left",
|
|
638
749
|
},
|
|
639
|
-
|
|
750
|
+
// Axis min / max / reverse / log / visibility from the spec
|
|
751
|
+
// (`categoryAxisOptions` is the X axis, as in the echarts
|
|
752
|
+
// renderer).
|
|
753
|
+
xaxis: applyAxisOverridesPlotly({
|
|
640
754
|
title: {
|
|
641
755
|
text: spec.horizontalAxisTitle ?? "",
|
|
642
756
|
font: { color: xAxisFmt?.color ?? textColor },
|
|
@@ -648,8 +762,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
648
762
|
...(xAxisFmt?.fontSize ? { size: xAxisFmt.fontSize } : {}),
|
|
649
763
|
},
|
|
650
764
|
type: hasStringX ? "category" : "linear",
|
|
651
|
-
},
|
|
652
|
-
yaxis: {
|
|
765
|
+
}, spec.categoryAxisOptions),
|
|
766
|
+
yaxis: applyAxisOverridesPlotly({
|
|
653
767
|
title: {
|
|
654
768
|
text: spec.verticalAxisTitle ?? "",
|
|
655
769
|
font: { color: yAxisFmt?.color ?? textColor },
|
|
@@ -660,7 +774,20 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
660
774
|
...(yAxisFmt?.fontFamily ? { family: yAxisFmt.fontFamily } : {}),
|
|
661
775
|
...(yAxisFmt?.fontSize ? { size: yAxisFmt.fontSize } : {}),
|
|
662
776
|
},
|
|
663
|
-
},
|
|
777
|
+
}, spec.valueAxisOptions),
|
|
778
|
+
// Secondary value axis on the right for the series that opt in
|
|
779
|
+
// (`useSecondaryAxis`), as the category layout has (#1067).
|
|
780
|
+
...(hasBubbleSecondaryAxis
|
|
781
|
+
? {
|
|
782
|
+
yaxis2: applyAxisOverridesPlotly({
|
|
783
|
+
overlaying: "y",
|
|
784
|
+
side: "right",
|
|
785
|
+
showgrid: false,
|
|
786
|
+
automargin: true,
|
|
787
|
+
tickfont: { color: yAxisFmt?.color ?? textColor },
|
|
788
|
+
}, spec.secondaryValueAxisOptions),
|
|
789
|
+
}
|
|
790
|
+
: {}),
|
|
664
791
|
legend: {
|
|
665
792
|
font: {
|
|
666
793
|
color: legendFmt?.color ?? textColor,
|
|
@@ -671,7 +798,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
671
798
|
paper_bgcolor: bgColor,
|
|
672
799
|
plot_bgcolor: bgColor,
|
|
673
800
|
showlegend: preparedSeries.length > 1,
|
|
674
|
-
margin: { l: 60, r: 20, t: 50, b: 50 },
|
|
801
|
+
margin: { l: 60, r: hasBubbleSecondaryAxis ? 60 : 20, t: 50, b: 50 },
|
|
675
802
|
};
|
|
676
803
|
setPlotData(traces);
|
|
677
804
|
setLayout(bubbleLayout);
|
|
@@ -693,14 +820,40 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
693
820
|
const seriesName = hierarchyData.labels.length === 1
|
|
694
821
|
? hierarchyData.labels[0] ?? ""
|
|
695
822
|
: explicitChartTitle ?? headerTitle ?? "";
|
|
823
|
+
// One solid colour per top-level category, inherited by its branch
|
|
824
|
+
// (#1072); nodes reference each other by id so a leaf name may
|
|
825
|
+
// repeat under different parents.
|
|
826
|
+
const hierarchyPalette = getChartPalette(spec, [
|
|
827
|
+
theme.themeColors.accent1,
|
|
828
|
+
theme.themeColors.accent2,
|
|
829
|
+
theme.themeColors.accent3,
|
|
830
|
+
theme.themeColors.accent4,
|
|
831
|
+
theme.themeColors.accent5,
|
|
832
|
+
theme.themeColors.accent6,
|
|
833
|
+
]);
|
|
834
|
+
const nodeColors = hierarchyNodeColors(hierarchyData, hierarchyPalette);
|
|
835
|
+
const isSunburst = spec.chartType === "sunburst";
|
|
836
|
+
// Excel's sunburst leaves the centre empty — every level, the first
|
|
837
|
+
// included, is a ring (#1073). Plotly draws the top level as a ring
|
|
838
|
+
// only around a root node, so an invisible one holds the centre.
|
|
839
|
+
const SUNBURST_ROOT_ID = "__rnc_sunburst_root__";
|
|
840
|
+
const rootTotal = hierarchyData.values.reduce((sum, value, i) => (hierarchyData.parents[i] === "" ? sum + value : sum), 0);
|
|
696
841
|
const hierarchyTrace = {
|
|
697
842
|
type: chartType,
|
|
698
|
-
labels: hierarchyData.labels,
|
|
699
|
-
|
|
700
|
-
|
|
843
|
+
labels: isSunburst ? ["", ...hierarchyData.labels] : hierarchyData.labels,
|
|
844
|
+
ids: isSunburst ? [SUNBURST_ROOT_ID, ...hierarchyData.ids] : hierarchyData.ids,
|
|
845
|
+
parents: isSunburst
|
|
846
|
+
? ["", ...hierarchyData.parents.map((parent) => parent || SUNBURST_ROOT_ID)]
|
|
847
|
+
: hierarchyData.parents,
|
|
848
|
+
values: isSunburst ? [rootTotal, ...hierarchyData.values] : hierarchyData.values,
|
|
849
|
+
marker: {
|
|
850
|
+
colors: isSunburst ? ["rgba(0,0,0,0)", ...nodeColors] : nodeColors,
|
|
851
|
+
...(isSunburst ? { line: { width: 1 } } : {}),
|
|
852
|
+
},
|
|
701
853
|
name: seriesName,
|
|
702
854
|
textinfo: "label+value",
|
|
703
855
|
branchvalues: "total",
|
|
856
|
+
...(isSunburst ? {} : { textposition: "bottom left" }),
|
|
704
857
|
};
|
|
705
858
|
const layoutForHierarchy = {
|
|
706
859
|
title: {
|
|
@@ -1190,7 +1343,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1190
1343
|
// by category index, falling back to the series color for points
|
|
1191
1344
|
// without an override.
|
|
1192
1345
|
const isPie = chartTypeForStyling === "pie";
|
|
1193
|
-
|
|
1346
|
+
const isFunnel = chartTypeForStyling === "funnel";
|
|
1347
|
+
if ((isBarOrColumn || isPie || isFunnel) &&
|
|
1194
1348
|
Array.isArray(options?.dataPointColors) &&
|
|
1195
1349
|
options?.dataPointColors.length) {
|
|
1196
1350
|
const overrides = new Map();
|
|
@@ -1445,9 +1599,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1445
1599
|
: {}),
|
|
1446
1600
|
};
|
|
1447
1601
|
if (effectiveChartType === "histogram") {
|
|
1448
|
-
const rawValues =
|
|
1449
|
-
? seriesData.values
|
|
1450
|
-
: seriesData;
|
|
1602
|
+
const rawValues = seriesValuesOf(seriesData);
|
|
1451
1603
|
const binCount = spec.binCount;
|
|
1452
1604
|
if (isPareto) {
|
|
1453
1605
|
// Plotly's native histogram trace bins in axis order and
|
|
@@ -1497,9 +1649,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1497
1649
|
}
|
|
1498
1650
|
}
|
|
1499
1651
|
else if (effectiveChartType === "boxplot") {
|
|
1500
|
-
const rawValues =
|
|
1501
|
-
? seriesData.values
|
|
1502
|
-
: seriesData;
|
|
1652
|
+
const rawValues = seriesValuesOf(seriesData);
|
|
1503
1653
|
const quartileMethod = spec.quartileMethod;
|
|
1504
1654
|
const showOutliers = spec.showOutliers;
|
|
1505
1655
|
const showMeanMarker = spec.showMeanMarker === true;
|
|
@@ -1566,37 +1716,51 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1566
1716
|
traces.push(trace);
|
|
1567
1717
|
}
|
|
1568
1718
|
else if (effectiveChartType === "waterfall") {
|
|
1569
|
-
const rawValues =
|
|
1570
|
-
? seriesData.values
|
|
1571
|
-
: seriesData;
|
|
1719
|
+
const rawValues = seriesValuesOf(seriesData);
|
|
1572
1720
|
// Plotly's native waterfall draws connector lines itself and
|
|
1573
1721
|
// renders "total" measures from the zero baseline with its
|
|
1574
1722
|
// own distinct fill.
|
|
1575
1723
|
const totalIndices = new Set(spec.totalIndices ?? []);
|
|
1724
|
+
// Increases take the series colour, decreases its negative colour
|
|
1725
|
+
// and totals the theme's third accent, as the echarts renderer and
|
|
1726
|
+
// Excel's default waterfall do (plotly's own defaults are green /
|
|
1727
|
+
// red / blue). Plotly paints a waterfall by measure, not per bar, so
|
|
1728
|
+
// `dataPointColors` cannot recolour one step here.
|
|
1729
|
+
const increaseColor = series.color ?? chartPaletteColorAt(themeColorPalette, currentSeriesIndex);
|
|
1576
1730
|
const trace = {
|
|
1577
1731
|
type: "waterfall",
|
|
1578
1732
|
x: categoriesFor(series),
|
|
1579
1733
|
y: rawValues.map((v) => (v ?? null)),
|
|
1580
1734
|
measure: rawValues.map((_, i) => totalIndices.has(i) ? "total" : "relative"),
|
|
1581
1735
|
name: dataLabel,
|
|
1736
|
+
increasing: { marker: { color: increaseColor } },
|
|
1737
|
+
decreasing: {
|
|
1738
|
+
marker: {
|
|
1739
|
+
color: series.negativeColor ?? theme.themeColors.accent2 ?? increaseColor,
|
|
1740
|
+
},
|
|
1741
|
+
},
|
|
1742
|
+
totals: { marker: { color: theme.themeColors.accent3 ?? increaseColor } },
|
|
1743
|
+
connector: {
|
|
1744
|
+
line: {
|
|
1745
|
+
color: isDarkMode ? "rgba(255,255,255,0.45)" : "#999999",
|
|
1746
|
+
width: 1,
|
|
1747
|
+
},
|
|
1748
|
+
},
|
|
1582
1749
|
};
|
|
1583
|
-
applyTraceColor(trace, series.color ?? undefined, {
|
|
1584
|
-
noFill: series.noFill,
|
|
1585
|
-
dataPointColors: series.dataPointColors,
|
|
1586
|
-
seriesIndex: currentSeriesIndex,
|
|
1587
|
-
effectiveChartType,
|
|
1588
|
-
});
|
|
1589
1750
|
traces.push(trace);
|
|
1590
1751
|
}
|
|
1591
1752
|
else if (effectiveChartType === "funnel") {
|
|
1592
|
-
const rawValues =
|
|
1593
|
-
|
|
1594
|
-
|
|
1753
|
+
const rawValues = seriesValuesOf(seriesData);
|
|
1754
|
+
// Horizontal bars with the stage names on the category axis to
|
|
1755
|
+
// the left; the series colour (per-stage `<c:dPt>` overrides) and
|
|
1756
|
+
// the values inside the bars follow below (#1069).
|
|
1595
1757
|
const trace = {
|
|
1596
1758
|
type: "funnel",
|
|
1759
|
+
orientation: "h",
|
|
1597
1760
|
y: categoriesFor(series),
|
|
1598
1761
|
x: rawValues.map((v) => (v ?? null)),
|
|
1599
1762
|
name: dataLabel,
|
|
1763
|
+
textinfo: "none",
|
|
1600
1764
|
};
|
|
1601
1765
|
applyTraceColor(trace, series.color ?? undefined, {
|
|
1602
1766
|
noFill: series.noFill,
|
|
@@ -2088,6 +2252,14 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2088
2252
|
const formattedTraces = traces.map((trace) => {
|
|
2089
2253
|
const pattern = traceNumberFormat(trace);
|
|
2090
2254
|
let next = applyDataLabelsToTracePlotly(trace, traceDataLabels(trace), spec.chartType, pattern, locale);
|
|
2255
|
+
if (next.type === "funnel") {
|
|
2256
|
+
// The formatted values ride on `text`; a funnel prints its own
|
|
2257
|
+
// `value` unless told to draw the text (or nothing).
|
|
2258
|
+
next = {
|
|
2259
|
+
...next,
|
|
2260
|
+
textinfo: traceDataLabels(trace)?.show ? "text" : "none",
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2091
2263
|
if (next.type === "pie") {
|
|
2092
2264
|
// Plotly's pie prints every slice's percentage by default; Excel
|
|
2093
2265
|
// draws nothing until data labels are on (#1062).
|
|
@@ -2278,5 +2450,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2278
2450
|
React.createElement("div", { className: "flex-1 rnc-plotly-chart-container", onPointerDownCapture: handlePointerDownCapture },
|
|
2279
2451
|
React.createElement(Plot, { data: plotData, layout: plotLayout, config: config, onClick: handlePlotClick, onInitialized: (_figure, graphDiv) => {
|
|
2280
2452
|
graphDivRef.current = graphDiv;
|
|
2453
|
+
const plotDiv = graphDiv;
|
|
2454
|
+
// Returning false from these handlers keeps the clicked branch
|
|
2455
|
+
// from becoming the new root.
|
|
2456
|
+
plotDiv.on?.("plotly_treemapclick", () => drillDownRef.current);
|
|
2457
|
+
plotDiv.on?.("plotly_sunburstclick", () => drillDownRef.current);
|
|
2281
2458
|
}, style: { width: "100%", height: "100%" } }))));
|
|
2282
2459
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../plotly/basic-chart/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,eAAO,MAAM,qBAAqB,GAChC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,KACvC,MAmEF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC;;;;;;;;;CAgBzC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,QAAQ,MAAM,GAAG,SAAS,GAAG,IAAI,KAChC,MAAM,GAAG,SA0BX,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../plotly/basic-chart/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,eAAO,MAAM,qBAAqB,GAChC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,KACvC,MAmEF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC;;;;;;;;;CAgBzC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,QAAQ,MAAM,GAAG,SAAS,GAAG,IAAI,KAChC,MAAM,GAAG,SA0BX,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,YAiBjE,CAAC"}
|
|
@@ -101,6 +101,8 @@ export const showXAxis = (type) => {
|
|
|
101
101
|
type === "column" ||
|
|
102
102
|
type === "bubble" ||
|
|
103
103
|
type === "histogram" ||
|
|
104
|
+
// A waterfall's steps are categories (Opening, Sales, …) (#1070).
|
|
105
|
+
type === "waterfall" ||
|
|
104
106
|
// Stock is line-derived on a category x-axis. Radar is polar and
|
|
105
107
|
// does not use the cartesian x-axis.
|
|
106
108
|
type === "stock") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../../config.ts","../../echarts-gl.d.ts","../../geo-modules.d.ts","../../index.ts","../../plotly-dist.d.ts","../../types.ts","../../vitest.config.ts","../../atoms/editing-chart.atom.ts","../../atoms/pivot-chart.atom.ts","../../basic-chart/basic-chart.tsx","../../basic-chart/helpers.ts","../../basic-chart/index.tsx","../../components/dialogs/chart-editor-dialog.tsx","../../components/dialogs/pivot-chart-dialog.tsx","../../components/editors/chart-editor.tsx","../../components/editors/chart-range-input.tsx","../../hooks/use-chart-editor.ts","../../hooks/use-charts.ts","../../hooks/use-on-create-chart.ts","../../hooks/use-on-delete-chart.ts","../../hooks/use-on-move-chart-to-sheet.ts","../../hooks/use-on-move-chart.ts","../../hooks/use-on-resize-chart.ts","../../hooks/use-on-update-chart.ts","../../interface/create-chart.ts","../../interface/delete-chart.ts","../../interface/move-chart-to-sheet.ts","../../interface/move-chart.ts","../../interface/resize-chart.ts","../../interface/shift-chart-ranges.ts","../../interface/update-chart.ts","../../plotly/plot.ts","../../plotly/basic-chart/basic-chart.tsx","../../plotly/basic-chart/helpers.ts","../../plotly/basic-chart/index.tsx","../../utils/aggregation.ts","../../utils/axis-number-format.ts","../../utils/bar-layout.ts","../../utils/boxplot.ts","../../utils/bubble.ts","../../utils/category-levels.ts","../../utils/chart-image.ts","../../utils/chart-orientation.ts","../../utils/chart-spec-utils.ts","../../utils/chart-styles.ts","../../utils/doughnut.ts","../../utils/echarts-gl-loader.ts","../../utils/empty-cells.ts","../../utils/error-bars.ts","../../utils/gradient.ts","../../utils/hierarchy.ts","../../utils/histogram.ts","../../utils/legend-layout.ts","../../utils/of-pie.ts","../../utils/overrides.ts","../../utils/pie-explosion.ts","../../utils/plotly-click.ts","../../utils/plotly-surface.ts","../../utils/plotly-title.ts","../../utils/radar.ts","../../utils/region-map-loader.ts","../../utils/region-map.ts","../../utils/series-click.ts","../../utils/stock.ts","../../utils/three-d.ts","../../utils/trendline.ts","../../utils/waterfall.ts","../../utils/webgl.ts"],"version":"5.8.3"}
|
|
1
|
+
{"root":["../../config.ts","../../echarts-gl.d.ts","../../geo-modules.d.ts","../../index.ts","../../plotly-dist.d.ts","../../types.ts","../../vitest.config.ts","../../atoms/editing-chart.atom.ts","../../atoms/pivot-chart.atom.ts","../../basic-chart/basic-chart.tsx","../../basic-chart/helpers.ts","../../basic-chart/index.tsx","../../components/dialogs/chart-editor-dialog.tsx","../../components/dialogs/pivot-chart-dialog.tsx","../../components/editors/chart-editor.tsx","../../components/editors/chart-range-input.tsx","../../hooks/use-chart-editor.ts","../../hooks/use-charts.ts","../../hooks/use-on-create-chart.ts","../../hooks/use-on-delete-chart.ts","../../hooks/use-on-move-chart-to-sheet.ts","../../hooks/use-on-move-chart.ts","../../hooks/use-on-resize-chart.ts","../../hooks/use-on-update-chart.ts","../../interface/create-chart.ts","../../interface/delete-chart.ts","../../interface/move-chart-to-sheet.ts","../../interface/move-chart.ts","../../interface/resize-chart.ts","../../interface/shift-chart-ranges.ts","../../interface/update-chart.ts","../../plotly/plot.ts","../../plotly/basic-chart/basic-chart.tsx","../../plotly/basic-chart/helpers.ts","../../plotly/basic-chart/index.tsx","../../utils/aggregation.ts","../../utils/axis-number-format.ts","../../utils/bar-layout.ts","../../utils/boxplot.ts","../../utils/bubble.ts","../../utils/category-levels.ts","../../utils/chart-image.ts","../../utils/chart-orientation.ts","../../utils/chart-spec-utils.ts","../../utils/chart-styles.ts","../../utils/doughnut.ts","../../utils/echarts-gl-loader.ts","../../utils/echarts-overlays.ts","../../utils/empty-cells.ts","../../utils/error-bars.ts","../../utils/gradient.ts","../../utils/hierarchy.ts","../../utils/histogram.ts","../../utils/legend-layout.ts","../../utils/of-pie.ts","../../utils/overrides.ts","../../utils/pie-explosion.ts","../../utils/plotly-click.ts","../../utils/plotly-surface.ts","../../utils/plotly-title.ts","../../utils/radar.ts","../../utils/region-map-loader.ts","../../utils/region-map.ts","../../utils/series-click.ts","../../utils/stock.ts","../../utils/three-d.ts","../../utils/trendline.ts","../../utils/waterfall.ts","../../utils/webgl.ts"],"version":"5.8.3"}
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
export declare const toLabelString: (value: unknown) => string;
|
|
2
2
|
export declare const toNumericValue: (value: unknown) => number | null;
|
|
3
|
-
export
|
|
3
|
+
export type BubbleSizeOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Whether a size value sets the bubble's area (Excel's default,
|
|
6
|
+
* diameter ∝ √value) or its width (diameter ∝ value).
|
|
7
|
+
*/
|
|
8
|
+
sizeRepresents?: "area" | "width" | null;
|
|
9
|
+
/** Percent of the default size, 0-300 (Excel's `<c:bubbleScale>`). */
|
|
10
|
+
bubbleScale?: number | null;
|
|
11
|
+
/**
|
|
12
|
+
* The plot area's smaller side in pixels — the largest bubble's
|
|
13
|
+
* diameter is a fraction of it, so bubbles grow with the chart.
|
|
14
|
+
*/
|
|
15
|
+
plotMinSide?: number | null;
|
|
16
|
+
};
|
|
17
|
+
/** The largest bubble's diameter at bubbleScale 100: a quarter of the plot area's smaller side. */
|
|
18
|
+
export declare const BUBBLE_MAX_DIAMETER_FRACTION = 0.25;
|
|
19
|
+
/**
|
|
20
|
+
* Pixel diameter per size value, scaled from ZERO like Excel: the largest
|
|
21
|
+
* absolute value gets the maximum diameter and every other bubble's area
|
|
22
|
+
* (or width) is its share of that value. Scaling from the minimum value
|
|
23
|
+
* instead made the ratio between bubbles depend on the spread of the
|
|
24
|
+
* data (100 and 101 drew as the smallest and largest bubble), and a
|
|
25
|
+
* linear diameter made the perceived area grow with the square of the
|
|
26
|
+
* value (rowsncolumns/spreadsheet#1068).
|
|
27
|
+
*/
|
|
28
|
+
export declare const createBubbleSizeScale: (values: number[], options?: BubbleSizeOptions) => (value: number) => number;
|
|
29
|
+
/** The diameter of the largest bubble for these options. */
|
|
30
|
+
export declare const bubbleMaxDiameter: (options?: BubbleSizeOptions) => number;
|
|
4
31
|
export type BubbleSeriesInput = {
|
|
5
32
|
name?: string | null;
|
|
6
33
|
values: (string | number | boolean | null | undefined)[];
|
|
@@ -23,6 +50,12 @@ export type BubbleSeriesPoint = {
|
|
|
23
50
|
value: [number, number, number];
|
|
24
51
|
name: string;
|
|
25
52
|
xLabel?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The point's row in the source ranges. Points with no Y are skipped,
|
|
55
|
+
* so this is not the point's position in the series — per-point
|
|
56
|
+
* colours (`dataPointColors[].idx`) key on it.
|
|
57
|
+
*/
|
|
58
|
+
index: number;
|
|
26
59
|
};
|
|
27
60
|
export type PreparedBubbleSeries = {
|
|
28
61
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bubble.d.ts","sourceRoot":"","sources":["../../../utils/bubble.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,MACU,CAAC;AAE1D,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAYxD,CAAC;AAEF,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"bubble.d.ts","sourceRoot":"","sources":["../../../utils/bubble.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,MACU,CAAC;AAE1D,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAYxD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACzC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,mGAAmG;AACnG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAMjD;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EAAE,EAChB,UAAS,iBAAsB,MAMvB,OAAO,MAAM,WAQtB,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,GAAI,UAAS,iBAAsB,WAUhE,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IACzD;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IAC3D;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;;OAKG;IACH,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IAC1D,WAAW,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IAC9D;;OAEG;IACH,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IAC7D,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAwCF,eAAO,MAAM,uBAAuB,GAAI,+CAKrC,uBAAuB,KAAG,kBAuF5B,CAAC"}
|
package/dist/esm/utils/bubble.js
CHANGED
|
@@ -12,22 +12,45 @@ export const toNumericValue = (value) => {
|
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
14
14
|
};
|
|
15
|
-
|
|
15
|
+
/** The largest bubble's diameter at bubbleScale 100: a quarter of the plot area's smaller side. */
|
|
16
|
+
export const BUBBLE_MAX_DIAMETER_FRACTION = 0.25;
|
|
17
|
+
/** Fallback plot side when the chart has no measured size yet. */
|
|
18
|
+
const DEFAULT_PLOT_MIN_SIDE = 240;
|
|
19
|
+
/** A positive size never collapses below this diameter. */
|
|
20
|
+
const MIN_BUBBLE_DIAMETER = 2;
|
|
21
|
+
/**
|
|
22
|
+
* Pixel diameter per size value, scaled from ZERO like Excel: the largest
|
|
23
|
+
* absolute value gets the maximum diameter and every other bubble's area
|
|
24
|
+
* (or width) is its share of that value. Scaling from the minimum value
|
|
25
|
+
* instead made the ratio between bubbles depend on the spread of the
|
|
26
|
+
* data (100 and 101 drew as the smallest and largest bubble), and a
|
|
27
|
+
* linear diameter made the perceived area grow with the square of the
|
|
28
|
+
* value (rowsncolumns/spreadsheet#1068).
|
|
29
|
+
*/
|
|
30
|
+
export const createBubbleSizeScale = (values, options = {}) => {
|
|
31
|
+
const maxDiameter = bubbleMaxDiameter(options);
|
|
16
32
|
const valid = values.filter((value) => Number.isFinite(value));
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
const min = Math.min(...valid);
|
|
21
|
-
const max = Math.max(...valid);
|
|
22
|
-
const range = max - min || 1;
|
|
33
|
+
const maxAbs = valid.reduce((max, value) => Math.max(max, Math.abs(value)), 0);
|
|
34
|
+
const byWidth = options.sizeRepresents === "width";
|
|
23
35
|
return (value) => {
|
|
24
|
-
if (!Number.isFinite(value)) {
|
|
25
|
-
return
|
|
36
|
+
if (!Number.isFinite(value) || maxAbs === 0) {
|
|
37
|
+
return maxDiameter;
|
|
26
38
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
39
|
+
const share = Math.abs(value) / maxAbs;
|
|
40
|
+
const diameter = maxDiameter * (byWidth ? share : Math.sqrt(share));
|
|
41
|
+
return share > 0 ? Math.max(MIN_BUBBLE_DIAMETER, diameter) : 0;
|
|
29
42
|
};
|
|
30
43
|
};
|
|
44
|
+
/** The diameter of the largest bubble for these options. */
|
|
45
|
+
export const bubbleMaxDiameter = (options = {}) => {
|
|
46
|
+
const plotMinSide = typeof options.plotMinSide === "number" && options.plotMinSide > 0
|
|
47
|
+
? options.plotMinSide
|
|
48
|
+
: DEFAULT_PLOT_MIN_SIDE;
|
|
49
|
+
const scale = typeof options.bubbleScale === "number" && Number.isFinite(options.bubbleScale)
|
|
50
|
+
? Math.min(300, Math.max(0, options.bubbleScale)) / 100
|
|
51
|
+
: 1;
|
|
52
|
+
return plotMinSide * BUBBLE_MAX_DIAMETER_FRACTION * scale;
|
|
53
|
+
};
|
|
31
54
|
/**
|
|
32
55
|
* Build the X-coordinate index for a series. Numeric values map to
|
|
33
56
|
* their own coordinate; string values become categorical (deduped into
|
|
@@ -107,6 +130,7 @@ export const prepareBubbleSeriesData = ({ xValues, labelValues, sizeValues, seri
|
|
|
107
130
|
value: [coordinate.coordinate, y, sizeValue],
|
|
108
131
|
name: label,
|
|
109
132
|
xLabel: coordinateLabel,
|
|
133
|
+
index: i,
|
|
110
134
|
});
|
|
111
135
|
}
|
|
112
136
|
const name = serie.name && serie.name.trim()
|