@rowsncolumns/charts 28.1.18 → 28.1.19
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 +338 -233
- package/dist/cjs/basic-chart/helpers.d.ts +7 -3
- package/dist/cjs/basic-chart/helpers.d.ts.map +1 -1
- package/dist/cjs/basic-chart/helpers.js +30 -4
- package/dist/cjs/basic-chart/index.d.ts +7 -0
- package/dist/cjs/basic-chart/index.d.ts.map +1 -1
- package/dist/cjs/basic-chart/index.js +19 -2
- package/dist/cjs/components/editors/chart-editor.d.ts.map +1 -1
- package/dist/cjs/components/editors/chart-editor.js +21 -12
- package/dist/cjs/components/editors/fields/axis-options-fields.d.ts +6 -3
- package/dist/cjs/components/editors/fields/axis-options-fields.d.ts.map +1 -1
- package/dist/cjs/components/editors/fields/axis-options-fields.js +69 -7
- package/dist/cjs/components/editors/fields/chart-elements-fields.d.ts +15 -0
- package/dist/cjs/components/editors/fields/chart-elements-fields.d.ts.map +1 -0
- package/dist/cjs/components/editors/fields/chart-elements-fields.js +23 -0
- package/dist/cjs/components/editors/fields/series-format-fields.d.ts.map +1 -1
- package/dist/cjs/components/editors/fields/series-format-fields.js +16 -2
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/utils/chart-lines.d.ts +36 -0
- package/dist/cjs/utils/chart-lines.d.ts.map +1 -0
- package/dist/cjs/utils/chart-lines.js +111 -0
- package/dist/cjs/utils/data-table.d.ts +31 -0
- package/dist/cjs/utils/data-table.d.ts.map +1 -0
- package/dist/cjs/utils/data-table.js +61 -0
- package/dist/cjs/utils/excel-axis-scale.d.ts +35 -0
- package/dist/cjs/utils/excel-axis-scale.d.ts.map +1 -0
- package/dist/cjs/utils/excel-axis-scale.js +210 -0
- package/dist/cjs/utils/overrides.d.ts +24 -3
- package/dist/cjs/utils/overrides.d.ts.map +1 -1
- package/dist/cjs/utils/overrides.js +119 -14
- package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/esm/basic-chart/basic-chart.js +177 -38
- package/dist/esm/basic-chart/helpers.d.ts +7 -3
- package/dist/esm/basic-chart/helpers.d.ts.map +1 -1
- package/dist/esm/basic-chart/helpers.js +26 -3
- package/dist/esm/basic-chart/index.d.ts +7 -0
- package/dist/esm/basic-chart/index.d.ts.map +1 -1
- package/dist/esm/basic-chart/index.js +15 -1
- package/dist/esm/components/editors/chart-editor.d.ts.map +1 -1
- package/dist/esm/components/editors/chart-editor.js +12 -1
- package/dist/esm/components/editors/fields/axis-options-fields.d.ts +6 -3
- package/dist/esm/components/editors/fields/axis-options-fields.d.ts.map +1 -1
- package/dist/esm/components/editors/fields/axis-options-fields.js +56 -5
- package/dist/esm/components/editors/fields/chart-elements-fields.d.ts +15 -0
- package/dist/esm/components/editors/fields/chart-elements-fields.d.ts.map +1 -0
- package/dist/esm/components/editors/fields/chart-elements-fields.js +17 -0
- package/dist/esm/components/editors/fields/series-format-fields.d.ts.map +1 -1
- package/dist/esm/components/editors/fields/series-format-fields.js +9 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/utils/chart-lines.d.ts +36 -0
- package/dist/esm/utils/chart-lines.d.ts.map +1 -0
- package/dist/esm/utils/chart-lines.js +121 -0
- package/dist/esm/utils/data-table.d.ts +31 -0
- package/dist/esm/utils/data-table.d.ts.map +1 -0
- package/dist/esm/utils/data-table.js +48 -0
- package/dist/esm/utils/excel-axis-scale.d.ts +35 -0
- package/dist/esm/utils/excel-axis-scale.d.ts.map +1 -0
- package/dist/esm/utils/excel-axis-scale.js +122 -0
- package/dist/esm/utils/overrides.d.ts +24 -3
- package/dist/esm/utils/overrides.d.ts.map +1 -1
- package/dist/esm/utils/overrides.js +111 -10
- package/package.json +7 -7
|
@@ -16,20 +16,23 @@ import { computeBoxplotSummary } from "../utils/boxplot";
|
|
|
16
16
|
import { createBubbleSizeScale, prepareBubbleSeriesData, } from "../utils/bubble";
|
|
17
17
|
import { echartsGroupedCategoryAxes, resolveCategoryLevels, } from "../utils/category-levels";
|
|
18
18
|
import { dataUrlToBlob } from "../utils/chart-image";
|
|
19
|
+
import { buildChartLineSeries, CHART_LINE_SERIES_PREFIX, } from "../utils/chart-lines";
|
|
19
20
|
import { canSwitchRowColumn, cellLabelGetterFromRangeReader, switchRowColumn, } from "../utils/chart-orientation";
|
|
20
21
|
import { coerceCalculatedLabel, getChartDataRanges, } from "../utils/chart-spec-utils";
|
|
21
22
|
import { getChartPalette } from "../utils/chart-styles";
|
|
23
|
+
import { dataTableAxes, } from "../utils/data-table";
|
|
22
24
|
import { clampDoughnutHole, doughnutRings } from "../utils/doughnut";
|
|
23
25
|
import { getEchartsGlState, loadEchartsGl, markEchartsGlFailed, } from "../utils/echarts-gl-loader";
|
|
24
26
|
import { buildEchartsOverlaySeries } from "../utils/echarts-overlays";
|
|
25
27
|
import { applyEmptyCellsPolicy, connectsAcrossEmptyCells, isLineLikeChartType, isolatedPointIndexes, } from "../utils/empty-cells";
|
|
28
|
+
import { excelAutoAxisScale, valueAxisExtentValues } from "../utils/excel-axis-scale";
|
|
26
29
|
import { gradientToEcharts } from "../utils/gradient";
|
|
27
30
|
import { buildHierarchyTree, contrastTextColor, extractHierarchyRows, getHierarchyHeaderTitle, } from "../utils/hierarchy";
|
|
28
31
|
import { aggregateByCategory, binHistogram, computePareto, isParetoSpec, } from "../utils/histogram";
|
|
29
32
|
import { categoryAxisNameGap, legendLabelsFrom, reserveEchartsLegendBand, } from "../utils/legend-layout";
|
|
30
33
|
import { clampSecondPieSize, isOfPieChartSpec, otherSliceHalfAngle, resolveOfPieType, splitOfPie, } from "../utils/of-pie";
|
|
31
34
|
import { applyAxisOverrides, applyLegendOverrides, buildSeriesLabelOption, dataLabelFormatter, resolveSeriesDataLabels, } from "../utils/overrides";
|
|
32
|
-
import { themedTextStyle } from "../utils/overrides";
|
|
35
|
+
import { CHART_TEXT_PX, CHART_TITLE_PX, fontSizePx, pointsToPixels, themedTextStyle, } from "../utils/overrides";
|
|
33
36
|
import { explosionOffsetPx, pieRadiusPx, resolveExplosion, } from "../utils/pie-explosion";
|
|
34
37
|
import { buildRadarIndicators, buildRadarSeriesData } from "../utils/radar";
|
|
35
38
|
import { buildRegionMapData, buildSequentialColorRamp, isRegionMapSpec, resolveGeoScope, warnOnceForUnmatchedRegions, } from "../utils/region-map";
|
|
@@ -40,7 +43,7 @@ import { STOCK_ROLES, toCandlestick, toLineSeries, toVolumeSeries, } from "../ut
|
|
|
40
43
|
import { buildBar3DData, buildContourData, buildLine3DSeries, buildSurfaceData, getCategoryCount, getValueExtent, isContourSpec, resolve3DRenderKind, SURFACE_COLOR_RAMP, } from "../utils/three-d";
|
|
41
44
|
import { computeWaterfall } from "../utils/waterfall";
|
|
42
45
|
import { isWebGLAvailable } from "../utils/webgl";
|
|
43
|
-
import { chartTypeToEchartsType, circularLegendEntries, getEchartsSeriesStyle, isCircularChartType, mapMarkerSymbolToEcharts, showXAxis, } from "./helpers";
|
|
46
|
+
import { automaticMarkerSymbol, chartTypeToEchartsType, circularLegendEntries, getEchartsSeriesStyle, isCircularChartType, mapMarkerSymbolToEcharts, showXAxis, } from "./helpers";
|
|
44
47
|
// Excel error values that should be treated as null in charts
|
|
45
48
|
const EXCEL_ERROR_VALUES = new Set([
|
|
46
49
|
"#N/A",
|
|
@@ -248,9 +251,33 @@ const defaultLegendConfig = {
|
|
|
248
251
|
};
|
|
249
252
|
/** Category-axis title gap: clears one row of tick labels (#986). */
|
|
250
253
|
const CATEGORY_AXIS_NAME_GAP = categoryAxisNameGap();
|
|
254
|
+
/**
|
|
255
|
+
* Excel's chart text colour: Text 1 at 65% (`<a:schemeClr val="tx1"><a:lumMod
|
|
256
|
+
* val="65000"/><a:lumOff val="35000"/>`, #595959 on the light theme) — RD-24.
|
|
257
|
+
*/
|
|
258
|
+
const chartTextColor = (theme, isDarkMode) => getStringifiedColor({ theme: 1, tint: isDarkMode ? 1 : 0.35 }, theme);
|
|
259
|
+
/** The theme's body (minor) font, which Excel's chart text uses. */
|
|
260
|
+
const chartFontFamily = (theme) => theme?.primaryFontFamily ? `${theme.primaryFontFamily}, sans-serif` : "sans-serif";
|
|
261
|
+
/** Chart types whose value axes take Excel's automatic bounds and major unit. */
|
|
262
|
+
const AUTO_SCALE_CHART_TYPES = new Set([
|
|
263
|
+
"column",
|
|
264
|
+
"bar",
|
|
265
|
+
"line",
|
|
266
|
+
"area",
|
|
267
|
+
"combo",
|
|
268
|
+
]);
|
|
251
269
|
export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, readonly, getDomainValuesFromRange, getSeriesValuesFromRange, getFormattingFromRange, onDeleteChart, onUpdateChart, onRequestEdit, onRequestCalculate, isSelected, fieldButtons, locale, enable3D, }) => {
|
|
252
270
|
const spec = chart.spec;
|
|
253
|
-
|
|
271
|
+
// A title linked to a cell shows the cell's text (FT-08); `title` is its last copy.
|
|
272
|
+
const titleSource = chart.spec.titleSource;
|
|
273
|
+
const linkedTitleValue = titleSource
|
|
274
|
+
? getDomainValuesFromRange(titleSource)?.[0]
|
|
275
|
+
: undefined;
|
|
276
|
+
const explicitChartTitle = (linkedTitleValue !== undefined && linkedTitleValue !== null && linkedTitleValue !== ""
|
|
277
|
+
? String(linkedTitleValue)
|
|
278
|
+
: undefined) ??
|
|
279
|
+
chart.spec.title ??
|
|
280
|
+
undefined;
|
|
254
281
|
const hierarchyTitleRef = useRef(explicitChartTitle);
|
|
255
282
|
const title = explicitChartTitle ?? hierarchyTitleRef.current;
|
|
256
283
|
const el = useRef(null);
|
|
@@ -790,20 +817,19 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
790
817
|
visible: bubbleLegend.show !== false,
|
|
791
818
|
labels: preparedSeries.map((entry) => entry.name),
|
|
792
819
|
legendFontSize: bubbleLegend.textStyle?.fontSize ??
|
|
793
|
-
spec.legend?.textFormat?.fontSize
|
|
794
|
-
12,
|
|
820
|
+
fontSizePx(spec.legend?.textFormat?.fontSize, CHART_TEXT_PX),
|
|
795
821
|
legendItemWidth: defaultLegendConfig.itemWidth,
|
|
796
822
|
width: width ?? 0,
|
|
797
823
|
height: height ?? 0,
|
|
798
824
|
hasTitle: Boolean(explicitChartTitle),
|
|
799
|
-
titleFontSize: spec.titleTextFormat?.fontSize
|
|
825
|
+
titleFontSize: fontSizePx(spec.titleTextFormat?.fontSize, CHART_TITLE_PX),
|
|
800
826
|
hasBottomAxisTitle: Boolean(spec.horizontalAxisTitle),
|
|
801
827
|
bottomAxisNameGap: 30,
|
|
802
828
|
hasLeftAxisTitle: Boolean(spec.verticalAxisTitle),
|
|
803
829
|
});
|
|
804
830
|
chartRef.current?.setOption({
|
|
805
831
|
xAxis: [
|
|
806
|
-
applyAxisOverrides({ ...xAxisConfig }, spec.categoryAxisOptions),
|
|
832
|
+
applyAxisOverrides({ ...xAxisConfig }, spec.categoryAxisOptions, undefined, undefined, "x"),
|
|
807
833
|
],
|
|
808
834
|
yAxis: [
|
|
809
835
|
applyAxisOverrides({
|
|
@@ -936,13 +962,12 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
936
962
|
visible: legendShown && hierarchyData.length > 0,
|
|
937
963
|
labels: hierarchyData.map((node) => node.name),
|
|
938
964
|
legendFontSize: hierarchyLegend.textStyle?.fontSize ??
|
|
939
|
-
spec.legend?.textFormat?.fontSize
|
|
940
|
-
12,
|
|
965
|
+
fontSizePx(spec.legend?.textFormat?.fontSize, CHART_TEXT_PX),
|
|
941
966
|
legendItemWidth: defaultLegendConfig.itemWidth,
|
|
942
967
|
width: width ?? 0,
|
|
943
968
|
height: height ?? 0,
|
|
944
969
|
hasTitle: Boolean(explicitChartTitle ?? headerTitle),
|
|
945
|
-
titleFontSize: spec.titleTextFormat?.fontSize
|
|
970
|
+
titleFontSize: fontSizePx(spec.titleTextFormat?.fontSize, CHART_TITLE_PX),
|
|
946
971
|
hasBottomAxisTitle: false,
|
|
947
972
|
bottomAxisNameGap: 0,
|
|
948
973
|
hasLeftAxisTitle: false,
|
|
@@ -1445,13 +1470,12 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1445
1470
|
visible: radarLegend.show !== false,
|
|
1446
1471
|
labels: radarData.map((d) => d.name),
|
|
1447
1472
|
legendFontSize: radarLegend.textStyle?.fontSize ??
|
|
1448
|
-
spec.legend?.textFormat?.fontSize
|
|
1449
|
-
12,
|
|
1473
|
+
fontSizePx(spec.legend?.textFormat?.fontSize, CHART_TEXT_PX),
|
|
1450
1474
|
legendItemWidth: defaultLegendConfig.itemWidth,
|
|
1451
1475
|
width: width ?? 0,
|
|
1452
1476
|
height: height ?? 0,
|
|
1453
1477
|
hasTitle: Boolean(explicitChartTitle),
|
|
1454
|
-
titleFontSize: spec.titleTextFormat?.fontSize
|
|
1478
|
+
titleFontSize: fontSizePx(spec.titleTextFormat?.fontSize, CHART_TITLE_PX),
|
|
1455
1479
|
hasBottomAxisTitle: false,
|
|
1456
1480
|
bottomAxisNameGap: 0,
|
|
1457
1481
|
hasLeftAxisTitle: false,
|
|
@@ -1752,6 +1776,13 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1752
1776
|
return;
|
|
1753
1777
|
}
|
|
1754
1778
|
const isStacked = spec.stackedType && spec.stackedType !== "UNSTACKED";
|
|
1779
|
+
// Tick labels and axis titles in Excel's chart text style (RD-24): 9 pt
|
|
1780
|
+
// labels, 10 pt titles, Text 1 at 65%. An axis' `<c:txPr>` overrides it.
|
|
1781
|
+
const axisTextColor = chartTextColor(theme, isDarkMode);
|
|
1782
|
+
const axisTextStyle = {
|
|
1783
|
+
axisLabel: { color: axisTextColor, fontSize: CHART_TEXT_PX },
|
|
1784
|
+
nameTextStyle: { color: axisTextColor, fontSize: pointsToPixels(10) },
|
|
1785
|
+
};
|
|
1755
1786
|
const isCircularChart = spec.chartType === "pie" || spec.chartType === "doughnut";
|
|
1756
1787
|
// Pie-of-pie / bar-of-pie composite. The of-pie branch below fills
|
|
1757
1788
|
// `ofPieLayout` so the grid/axis assembly at the bottom can add the
|
|
@@ -1778,7 +1809,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1778
1809
|
show: showAxis,
|
|
1779
1810
|
axisLine: { show: true },
|
|
1780
1811
|
axisTick: { show: true },
|
|
1781
|
-
splitLine: { show:
|
|
1812
|
+
splitLine: { show: false },
|
|
1782
1813
|
name: isHorizontalBarChart
|
|
1783
1814
|
? spec.verticalAxisTitle ?? ""
|
|
1784
1815
|
: spec.horizontalAxisTitle ?? "",
|
|
@@ -1802,12 +1833,15 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1802
1833
|
: spec.horizontalAxisTitle ?? ""
|
|
1803
1834
|
: undefined;
|
|
1804
1835
|
chartDomains.push({
|
|
1836
|
+
...axisTextStyle,
|
|
1805
1837
|
type: "category",
|
|
1806
1838
|
data: axisData,
|
|
1807
1839
|
show: showAxis && !isCircularChart,
|
|
1808
1840
|
axisLine: { show: !isCircularChart },
|
|
1809
1841
|
axisTick: { show: !isCircularChart },
|
|
1810
|
-
|
|
1842
|
+
// Excel draws vertical (category) gridlines only when the axis asks
|
|
1843
|
+
// for them (`<c:catAx><c:majorGridlines/>`) — LV-05.
|
|
1844
|
+
splitLine: { show: false },
|
|
1811
1845
|
name: axisTitle,
|
|
1812
1846
|
nameLocation: "middle",
|
|
1813
1847
|
nameGap: CATEGORY_AXIS_NAME_GAP,
|
|
@@ -1815,11 +1849,12 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1815
1849
|
});
|
|
1816
1850
|
if (chartDomains.length === 0 && !isCircularChart) {
|
|
1817
1851
|
chartDomains.push({
|
|
1852
|
+
...axisTextStyle,
|
|
1818
1853
|
type: "category",
|
|
1819
1854
|
show: showAxis,
|
|
1820
1855
|
axisLine: { show: true },
|
|
1821
1856
|
axisTick: { show: true },
|
|
1822
|
-
splitLine: { show:
|
|
1857
|
+
splitLine: { show: false },
|
|
1823
1858
|
gridIndex: 0,
|
|
1824
1859
|
name: isHorizontalBarChart
|
|
1825
1860
|
? spec.verticalAxisTitle ?? ""
|
|
@@ -1989,8 +2024,9 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
1989
2024
|
seriesEntry.showSymbol = wantsMarkers || labelsWithoutMarkers;
|
|
1990
2025
|
if (wantsMarkers) {
|
|
1991
2026
|
seriesEntry.symbol =
|
|
1992
|
-
mapMarkerSymbolToEcharts(markerSymbol) ?? "circle";
|
|
1993
|
-
|
|
2027
|
+
mapMarkerSymbolToEcharts(markerSymbol ?? automaticMarkerSymbol(specSeriesIndex)) ?? "circle";
|
|
2028
|
+
// Excel's automatic marker is 5 pt (7 px); an authored size is points too.
|
|
2029
|
+
seriesEntry.symbolSize = Math.round(((seriesAny.marker?.size ?? 5) * 4) / 3);
|
|
1994
2030
|
}
|
|
1995
2031
|
else {
|
|
1996
2032
|
if (labelsWithoutMarkers) {
|
|
@@ -2719,18 +2755,41 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2719
2755
|
}
|
|
2720
2756
|
legend.data = [];
|
|
2721
2757
|
}
|
|
2758
|
+
// Add Chart Element → Lines / Up/Down Bars (FT-08): over the primary
|
|
2759
|
+
// axis' line series, in spec order.
|
|
2760
|
+
const lineElementOptions = spec;
|
|
2761
|
+
if (!isCircularChart &&
|
|
2762
|
+
(lineElementOptions.dropLines ||
|
|
2763
|
+
(spec.chartType === "line" &&
|
|
2764
|
+
(lineElementOptions.hiLowLines || lineElementOptions.upDownBars)))) {
|
|
2765
|
+
const lineData = chartSeries
|
|
2766
|
+
.filter((s) => s?.type === "line" &&
|
|
2767
|
+
s.yAxisIndex !== 1 &&
|
|
2768
|
+
chartSeriesSpecIndex(s.id) !== undefined &&
|
|
2769
|
+
Array.isArray(s.data))
|
|
2770
|
+
.map((s) => s.data);
|
|
2771
|
+
chartSeries.push(...buildChartLineSeries({
|
|
2772
|
+
dropLines: lineElementOptions.dropLines &&
|
|
2773
|
+
(spec.chartType === "line" || spec.chartType === "area"),
|
|
2774
|
+
hiLowLines: lineElementOptions.hiLowLines,
|
|
2775
|
+
upDownBars: lineElementOptions.upDownBars,
|
|
2776
|
+
}, lineData, { isDarkMode }));
|
|
2777
|
+
}
|
|
2722
2778
|
// Only redraw chart
|
|
2723
2779
|
// if chart type has changed
|
|
2724
2780
|
// if series count has changed. Use the rendered series count
|
|
2725
2781
|
// (`chartSeries.length`) rather than `spec.series.length` because
|
|
2726
2782
|
// trendline / error-bar overlays add extra series; comparing the
|
|
2727
2783
|
// raw spec count would miss those and skip the structural redraw.
|
|
2728
|
-
|
|
2784
|
+
// A data table's rows are extra x axes: turning it on or off is a
|
|
2785
|
+
// structural change too, so it shifts the count the redraw compares.
|
|
2786
|
+
const renderedSeriesCount = chartSeries.length + (spec.dataTable ? 10_000 : 0);
|
|
2729
2787
|
const replaceMerge = previousChartType.current !== spec.chartType ||
|
|
2730
2788
|
previousSeriesCount.current !== renderedSeriesCount
|
|
2731
2789
|
? ["series", "xAxis", "yAxis", "legend", "grid", "tooltip"]
|
|
2732
2790
|
: [];
|
|
2733
2791
|
const valueAxisBase = {
|
|
2792
|
+
...axisTextStyle,
|
|
2734
2793
|
type: "value",
|
|
2735
2794
|
show: !isCircularChart,
|
|
2736
2795
|
// echarts draws the value axis line at the zero of a value-type x
|
|
@@ -2810,13 +2869,12 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2810
2869
|
visible: finalLegend.show !== false,
|
|
2811
2870
|
labels: legendLabelsFrom(finalLegend.data, chartSeries.map((s) => s?.name)),
|
|
2812
2871
|
legendFontSize: finalLegend.textStyle?.fontSize ??
|
|
2813
|
-
spec.legend?.textFormat?.fontSize
|
|
2814
|
-
12,
|
|
2872
|
+
fontSizePx(spec.legend?.textFormat?.fontSize, CHART_TEXT_PX),
|
|
2815
2873
|
legendItemWidth: defaultLegendConfig.itemWidth,
|
|
2816
2874
|
width: width ?? 0,
|
|
2817
2875
|
height: height ?? 0,
|
|
2818
2876
|
hasTitle: Boolean(title),
|
|
2819
|
-
titleFontSize: spec.titleTextFormat?.fontSize
|
|
2877
|
+
titleFontSize: fontSizePx(spec.titleTextFormat?.fontSize, CHART_TITLE_PX),
|
|
2820
2878
|
hasBottomAxisTitle: !isCircularChart && Boolean(spec.horizontalAxisTitle),
|
|
2821
2879
|
bottomAxisNameGap,
|
|
2822
2880
|
hasLeftAxisTitle: !isCircularChart && Boolean(spec.verticalAxisTitle),
|
|
@@ -2847,12 +2905,20 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2847
2905
|
const seriesLabelOptionFor = (s) => {
|
|
2848
2906
|
const specIndex = chartSeriesSpecIndex(s?.id);
|
|
2849
2907
|
const dataLabelOptions = resolveSeriesDataLabels(chartDataLabels, specIndex === undefined ? undefined : spec.series?.[specIndex]);
|
|
2850
|
-
|
|
2908
|
+
const withFormat = dataLabelOptions && !dataLabelOptions.numberFormatPattern
|
|
2851
2909
|
? {
|
|
2852
2910
|
...dataLabelOptions,
|
|
2853
2911
|
numberFormatPattern: seriesNumberFormatFor(s),
|
|
2854
2912
|
}
|
|
2855
|
-
: dataLabelOptions
|
|
2913
|
+
: dataLabelOptions;
|
|
2914
|
+
// Value From Cells: the label range's cells, point by point.
|
|
2915
|
+
const cellRange = withFormat?.valueFromCells;
|
|
2916
|
+
return buildSeriesLabelOption(cellRange
|
|
2917
|
+
? {
|
|
2918
|
+
...withFormat,
|
|
2919
|
+
cellLabels: getDomainValuesFromRange(cellRange, spec.hiddenDimensionStrategy) ?? [],
|
|
2920
|
+
}
|
|
2921
|
+
: withFormat, s?.type, locale);
|
|
2856
2922
|
};
|
|
2857
2923
|
// For doughnut, surface the inner-radius hole on each pie series.
|
|
2858
2924
|
// Excel persists `holeSize` as a 0-90 percentage; we normalize to a
|
|
@@ -2918,7 +2984,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2918
2984
|
}
|
|
2919
2985
|
const seriesWithLabels = chartSeries.map((s) => {
|
|
2920
2986
|
const next = { ...s };
|
|
2921
|
-
if (DECORATION_SERIES_IDS.has(s?.id)
|
|
2987
|
+
if (DECORATION_SERIES_IDS.has(s?.id) ||
|
|
2988
|
+
String(s?.id ?? "").startsWith(CHART_LINE_SERIES_PREFIX)) {
|
|
2922
2989
|
return next;
|
|
2923
2990
|
}
|
|
2924
2991
|
const seriesLabelOption = seriesLabelOptionFor(s);
|
|
@@ -2986,6 +3053,66 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
2986
3053
|
}
|
|
2987
3054
|
return next;
|
|
2988
3055
|
});
|
|
3056
|
+
// A chart with no category range (`<c:ser>` without `<c:cat>`) labels its
|
|
3057
|
+
// points 1, 2, 3… like Excel — echarts' bare category axis would number
|
|
3058
|
+
// them from 0 (LV-05).
|
|
3059
|
+
const bareCategoryAxis = chartDomains[0];
|
|
3060
|
+
if (!isCircularChart &&
|
|
3061
|
+
!categoryLevels &&
|
|
3062
|
+
!spec.domains?.length &&
|
|
3063
|
+
bareCategoryAxis &&
|
|
3064
|
+
bareCategoryAxis.data === undefined) {
|
|
3065
|
+
const pointCount = seriesWithLabels.reduce((count, s) => (s?.type === "bar" || s?.type === "line") && Array.isArray(s.data)
|
|
3066
|
+
? Math.max(count, s.data.length)
|
|
3067
|
+
: count, 0);
|
|
3068
|
+
bareCategoryAxis.data = Array.from({ length: pointCount }, (_, i) => String(i + 1));
|
|
3069
|
+
}
|
|
3070
|
+
// Add Chart Element → Data Table (FT-08): a row per plotted series under
|
|
3071
|
+
// the category axis, values in the series' own number format.
|
|
3072
|
+
const dataTableOptions = spec.dataTable;
|
|
3073
|
+
const dataTableRows = dataTableOptions && !isCircularChart && !isHorizontalBarChart && !isOfPie
|
|
3074
|
+
? seriesWithLabels
|
|
3075
|
+
.filter((s) => (s?.type === "line" || s?.type === "bar") &&
|
|
3076
|
+
chartSeriesSpecIndex(s?.id) !== undefined &&
|
|
3077
|
+
Array.isArray(s.data))
|
|
3078
|
+
.map((s) => {
|
|
3079
|
+
const specIndex = chartSeriesSpecIndex(s.id);
|
|
3080
|
+
const pattern = seriesNumberFormatFor(s);
|
|
3081
|
+
return {
|
|
3082
|
+
name: String(s.name ?? ""),
|
|
3083
|
+
color: typeof s.itemStyle?.color === "string"
|
|
3084
|
+
? s.itemStyle.color
|
|
3085
|
+
: spec.series?.[specIndex]?.color ?? paletteColorAt(specIndex),
|
|
3086
|
+
values: s.data.map((d) => {
|
|
3087
|
+
const v = d !== null && typeof d === "object"
|
|
3088
|
+
? d.originalValue ?? d.value
|
|
3089
|
+
: d;
|
|
3090
|
+
return v === null || v === undefined ? "" : formatAxisValue(pattern, v, locale);
|
|
3091
|
+
}),
|
|
3092
|
+
};
|
|
3093
|
+
})
|
|
3094
|
+
: [];
|
|
3095
|
+
const dataTableAxisList = dataTableRows.length
|
|
3096
|
+
? dataTableAxes(dataTableRows, dataTableOptions ?? {}, {
|
|
3097
|
+
textColor: axisTextColor,
|
|
3098
|
+
fontSize: CHART_TEXT_PX,
|
|
3099
|
+
borderColor: isDarkMode ? "rgba(255,255,255,0.3)" : "#D9D9D9",
|
|
3100
|
+
})
|
|
3101
|
+
: [];
|
|
3102
|
+
// Excel's automatic bounds and major unit on the cartesian value axes
|
|
3103
|
+
// (LV-05: echarts' own nicing drew steps of 3 where Excel draws 2).
|
|
3104
|
+
const autoScaleEligible = !isCircularChart &&
|
|
3105
|
+
!isOfPie &&
|
|
3106
|
+
AUTO_SCALE_CHART_TYPES.has(spec.chartType) &&
|
|
3107
|
+
!spec.sourceChartType &&
|
|
3108
|
+
spec.stackedType !== "PERCENT_STACKED";
|
|
3109
|
+
const withExcelAutoScale = (axis, opts, axisIndex) => {
|
|
3110
|
+
if (!autoScaleEligible || axis?.type === "log" || opts?.logarithmic) {
|
|
3111
|
+
return axis;
|
|
3112
|
+
}
|
|
3113
|
+
const scale = excelAutoAxisScale(valueAxisExtentValues(seriesWithLabels, axisIndex), { min: opts?.min, max: opts?.max, majorUnit: opts?.majorUnit });
|
|
3114
|
+
return scale ? { ...axis, ...scale } : axis;
|
|
3115
|
+
};
|
|
2989
3116
|
// Update charts
|
|
2990
3117
|
chartRef.current?.setOption({
|
|
2991
3118
|
xAxis: (() => {
|
|
@@ -3030,10 +3157,10 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3030
3157
|
}
|
|
3031
3158
|
if (isHorizontalBarChart) {
|
|
3032
3159
|
return [
|
|
3033
|
-
applyAxisOverrides({
|
|
3160
|
+
withExcelAutoScale(applyAxisOverrides({
|
|
3034
3161
|
...valueAxisBase,
|
|
3035
3162
|
name: spec.horizontalAxisTitle ?? "",
|
|
3036
|
-
}, spec.valueAxisOptions, primaryAxisNumberFormat, locale),
|
|
3163
|
+
}, spec.valueAxisOptions, primaryAxisNumberFormat, locale, "x"), spec.valueAxisOptions, 0),
|
|
3037
3164
|
];
|
|
3038
3165
|
}
|
|
3039
3166
|
const base = chartDomains.length > 0
|
|
@@ -3044,14 +3171,18 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3044
3171
|
show: showAxis,
|
|
3045
3172
|
axisLine: { show: true },
|
|
3046
3173
|
axisTick: { show: true },
|
|
3047
|
-
splitLine: { show:
|
|
3174
|
+
splitLine: { show: false },
|
|
3048
3175
|
name: spec.horizontalAxisTitle ?? "",
|
|
3049
3176
|
nameLocation: "middle",
|
|
3050
3177
|
nameGap: CATEGORY_AXIS_NAME_GAP,
|
|
3051
3178
|
},
|
|
3052
3179
|
];
|
|
3053
3180
|
const axes = base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions));
|
|
3054
|
-
return
|
|
3181
|
+
return [
|
|
3182
|
+
...axes,
|
|
3183
|
+
...(boxplotOverlayAxis ? [boxplotOverlayAxis] : []),
|
|
3184
|
+
...dataTableAxisList,
|
|
3185
|
+
];
|
|
3055
3186
|
})(),
|
|
3056
3187
|
yAxis: (() => {
|
|
3057
3188
|
if (isOfPie) {
|
|
@@ -3100,13 +3231,13 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3100
3231
|
show: showAxis,
|
|
3101
3232
|
axisLine: { show: true },
|
|
3102
3233
|
axisTick: { show: true },
|
|
3103
|
-
splitLine: { show:
|
|
3234
|
+
splitLine: { show: false },
|
|
3104
3235
|
name: spec.verticalAxisTitle ?? "",
|
|
3105
3236
|
nameLocation: "middle",
|
|
3106
3237
|
nameGap: CATEGORY_AXIS_NAME_GAP,
|
|
3107
3238
|
},
|
|
3108
3239
|
];
|
|
3109
|
-
return base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions));
|
|
3240
|
+
return base.map((axis) => applyAxisOverrides(axis, spec.categoryAxisOptions, undefined, undefined, "y"));
|
|
3110
3241
|
}
|
|
3111
3242
|
const axes = [
|
|
3112
3243
|
applyAxisOverrides({
|
|
@@ -3125,6 +3256,7 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3125
3256
|
}),
|
|
3126
3257
|
}, spec.valueAxisOptions, primaryAxisNumberFormat, locale),
|
|
3127
3258
|
];
|
|
3259
|
+
axes[0] = withExcelAutoScale(axes[0], spec.valueAxisOptions, 0);
|
|
3128
3260
|
if (hasSecondaryAxis) {
|
|
3129
3261
|
axes.push(applyAxisOverrides({
|
|
3130
3262
|
...valueAxisBase,
|
|
@@ -3139,6 +3271,9 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3139
3271
|
}
|
|
3140
3272
|
: {}),
|
|
3141
3273
|
}, spec.secondaryValueAxisOptions, secondaryAxisNumberFormat, locale));
|
|
3274
|
+
if (!isPareto) {
|
|
3275
|
+
axes[1] = withExcelAutoScale(axes[1], spec.secondaryValueAxisOptions, 1);
|
|
3276
|
+
}
|
|
3142
3277
|
}
|
|
3143
3278
|
return axes;
|
|
3144
3279
|
})(),
|
|
@@ -3185,10 +3320,8 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3185
3320
|
// re-apply these.
|
|
3186
3321
|
const applyThemeOptions = () => {
|
|
3187
3322
|
if (chartRef.current) {
|
|
3188
|
-
const textColor =
|
|
3189
|
-
|
|
3190
|
-
tint: isDarkMode ? 1 : 0,
|
|
3191
|
-
}, theme);
|
|
3323
|
+
const textColor = chartTextColor(theme, isDarkMode);
|
|
3324
|
+
const fontFamily = chartFontFamily(theme);
|
|
3192
3325
|
const bgColor = getStringifiedColor({ theme: 0, tint: isDarkMode ? -1 : 1 }, theme);
|
|
3193
3326
|
const showAxis = showXAxis(chart.spec.chartType);
|
|
3194
3327
|
// Authored chart-level text formats override the theme-derived
|
|
@@ -3204,18 +3337,24 @@ export const BasicChart = ({ width, height, chart, theme, isDarkMode, sheetId, r
|
|
|
3204
3337
|
// Every key is written (echarts merges setOption calls, so a key
|
|
3205
3338
|
// left out keeps its previous value — turning bold off used to
|
|
3206
3339
|
// leave the title bold, rowsncolumns/spreadsheet#1105).
|
|
3340
|
+
left: "center",
|
|
3207
3341
|
textStyle: themedTextStyle(authoredTitleFmt, {
|
|
3208
3342
|
color: textColor,
|
|
3209
|
-
fontSize:
|
|
3210
|
-
|
|
3343
|
+
fontSize: CHART_TITLE_PX,
|
|
3344
|
+
fontFamily,
|
|
3345
|
+
fontWeight: "normal",
|
|
3211
3346
|
}),
|
|
3212
3347
|
},
|
|
3213
3348
|
legend: {
|
|
3214
3349
|
textStyle: themedTextStyle(authoredLegendFmt, {
|
|
3215
3350
|
color: textColor,
|
|
3216
|
-
fontSize:
|
|
3351
|
+
fontSize: CHART_TEXT_PX,
|
|
3352
|
+
fontFamily,
|
|
3217
3353
|
}),
|
|
3218
3354
|
},
|
|
3355
|
+
// Every other chart text (tick labels, axis titles, data labels)
|
|
3356
|
+
// inherits the theme body font.
|
|
3357
|
+
textStyle: { fontFamily },
|
|
3219
3358
|
axisLabel: {
|
|
3220
3359
|
color: authoredDataLabelFmt?.color ?? textColor,
|
|
3221
3360
|
},
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { EmbeddedChart } from "@rowsncolumns/spreadsheet";
|
|
2
2
|
export declare const chartTypeToEchartsType: (type: EmbeddedChart["spec"]["chartType"]) => "bar" | "line" | "pie" | "radar" | "boxplot" | "funnel" | "treemap" | "sunburst" | "scatter" | "map";
|
|
3
|
+
export declare const mapMarkerSymbolToEcharts: (symbol: string | undefined | null) => string | undefined;
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* The marker Excel draws for a "Line with Markers" series that names no
|
|
6
|
+
* symbol of its own (`<c:lineChart><c:marker val="1"/>` with no
|
|
7
|
+
* `<c:ser><c:marker>`): the automatic cycle by series index — diamond,
|
|
8
|
+
* square, triangle, x, star, circle, plus (LV-05).
|
|
6
9
|
*/
|
|
7
|
-
export declare const
|
|
10
|
+
export declare const EXCEL_AUTOMATIC_MARKERS: readonly ["diamond", "square", "triangle", "x", "star", "circle", "plus"];
|
|
11
|
+
export declare const automaticMarkerSymbol: (seriesIndex: number) => "x" | "plus" | "circle" | "square" | "diamond" | "triangle" | "star";
|
|
8
12
|
/** Pie and doughnut — the pie-family types whose points are slices. */
|
|
9
13
|
export declare const isCircularChartType: (type: EmbeddedChart["spec"]["chartType"] | undefined) => boolean;
|
|
10
14
|
/**
|
|
@@ -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;
|
|
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;AAcF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,GAAG,SAAS,GAAG,IAAI,KAChC,MAAM,GAAG,SAqBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,2EAQ1B,CAAC;AAEX,eAAO,MAAM,qBAAqB,GAAI,aAAa,MAAM,yEAKtD,CAAC;AAEJ,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,YAmBjE,CAAC"}
|
|
@@ -50,6 +50,13 @@ export const chartTypeToEchartsType = (type) => {
|
|
|
50
50
|
* Map OOXML `<c:marker><c:symbol>` values to echarts symbol names.
|
|
51
51
|
* Returns undefined for "auto" / unknown — caller defaults to "circle".
|
|
52
52
|
*/
|
|
53
|
+
/** Excel's x / plus / asterisk / dash markers as echarts path symbols (filled outlines). */
|
|
54
|
+
const MARKER_PATHS = {
|
|
55
|
+
x: "path://M2,0L5,3L8,0L10,2L7,5L10,8L8,10L5,7L2,10L0,8L3,5L0,2Z",
|
|
56
|
+
plus: "path://M4,0H6V4H10V6H6V10H4V6H0V4H4Z",
|
|
57
|
+
star: "path://M4.3,0H5.7V3.8L9,1.9L9.7,3.1L6.4,5L9.7,6.9L9,8.1L5.7,6.2V10H4.3V6.2L1,8.1L0.3,6.9L3.6,5L0.3,3.1L1,1.9L4.3,3.8Z",
|
|
58
|
+
dash: "path://M0,4H10V6H0Z",
|
|
59
|
+
};
|
|
53
60
|
export const mapMarkerSymbolToEcharts = (symbol) => {
|
|
54
61
|
switch (symbol) {
|
|
55
62
|
case "circle":
|
|
@@ -63,17 +70,33 @@ export const mapMarkerSymbolToEcharts = (symbol) => {
|
|
|
63
70
|
return "triangle";
|
|
64
71
|
case "x":
|
|
65
72
|
case "plus":
|
|
66
|
-
return "pin";
|
|
67
73
|
case "star":
|
|
68
|
-
return "diamond";
|
|
69
74
|
case "dash":
|
|
70
|
-
return
|
|
75
|
+
return MARKER_PATHS[symbol];
|
|
71
76
|
case "none":
|
|
72
77
|
return "none";
|
|
73
78
|
default:
|
|
74
79
|
return undefined;
|
|
75
80
|
}
|
|
76
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* The marker Excel draws for a "Line with Markers" series that names no
|
|
84
|
+
* symbol of its own (`<c:lineChart><c:marker val="1"/>` with no
|
|
85
|
+
* `<c:ser><c:marker>`): the automatic cycle by series index — diamond,
|
|
86
|
+
* square, triangle, x, star, circle, plus (LV-05).
|
|
87
|
+
*/
|
|
88
|
+
export const EXCEL_AUTOMATIC_MARKERS = [
|
|
89
|
+
"diamond",
|
|
90
|
+
"square",
|
|
91
|
+
"triangle",
|
|
92
|
+
"x",
|
|
93
|
+
"star",
|
|
94
|
+
"circle",
|
|
95
|
+
"plus",
|
|
96
|
+
];
|
|
97
|
+
export const automaticMarkerSymbol = (seriesIndex) => EXCEL_AUTOMATIC_MARKERS[((seriesIndex % EXCEL_AUTOMATIC_MARKERS.length) +
|
|
98
|
+
EXCEL_AUTOMATIC_MARKERS.length) %
|
|
99
|
+
EXCEL_AUTOMATIC_MARKERS.length];
|
|
77
100
|
/** Pie and doughnut — the pie-family types whose points are slices. */
|
|
78
101
|
export const isCircularChartType = (type) => type === "pie" || type === "doughnut";
|
|
79
102
|
/**
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { EChartComponentProps } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Excel's chart area outline: a thin Text 1 at 15% line (#D9D9D9), no drop
|
|
5
|
+
* shadow (LV-05). A chart with its own Format Chart Area border
|
|
6
|
+
* (`EmbeddedChart.borderColor`) is outlined by the grid's embed frame
|
|
7
|
+
* instead, so it gets none here.
|
|
8
|
+
*/
|
|
9
|
+
export declare const chartFrameBorder: (chart: EChartComponentProps["chart"], isDarkMode?: boolean) => string | undefined;
|
|
3
10
|
export declare const ChartComponent: (props: EChartComponentProps) => React.JSX.Element;
|
|
4
11
|
export default ChartComponent;
|
|
5
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../basic-chart/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAGrD,eAAO,MAAM,cAAc,GAAI,OAAO,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../basic-chart/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAGrD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,OAAO,oBAAoB,CAAC,OAAO,CAAC,EACpC,aAAa,OAAO,KACnB,MAAM,GAAG,SAGyD,CAAC;AAEtE,eAAO,MAAM,cAAc,GAAI,OAAO,oBAAoB,sBAazD,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { BasicChart } from "./basic-chart";
|
|
3
|
+
/**
|
|
4
|
+
* Excel's chart area outline: a thin Text 1 at 15% line (#D9D9D9), no drop
|
|
5
|
+
* shadow (LV-05). A chart with its own Format Chart Area border
|
|
6
|
+
* (`EmbeddedChart.borderColor`) is outlined by the grid's embed frame
|
|
7
|
+
* instead, so it gets none here.
|
|
8
|
+
*/
|
|
9
|
+
export const chartFrameBorder = (chart, isDarkMode) => chart?.borderColor
|
|
10
|
+
? undefined
|
|
11
|
+
: `1px solid ${isDarkMode ? "rgba(255,255,255,0.2)" : "#D9D9D9"}`;
|
|
3
12
|
export const ChartComponent = (props) => {
|
|
4
|
-
return (React.createElement("div", { className: "
|
|
13
|
+
return (React.createElement("div", { className: "bg-rnc-background flex-1 flex break-words flex-col",
|
|
14
|
+
// An inset outline: drawn over the plot's edge without shrinking it.
|
|
15
|
+
style: {
|
|
16
|
+
outline: chartFrameBorder(props.chart, props.isDarkMode),
|
|
17
|
+
outlineOffset: -1,
|
|
18
|
+
} },
|
|
5
19
|
React.createElement(BasicChart, { ...props })));
|
|
6
20
|
};
|
|
7
21
|
export default ChartComponent;
|
|
@@ -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;AAwBnC,OAAO,KAAkB,MAAM,OAAO,CAAC;AAIvC,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,+BAA+B,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;AAwDvC,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;IACzC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAuBF,eAAO,MAAM,WAAW,GAAI,gFAOzB,gBAAgB,sBAujClB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -14,6 +14,7 @@ import { applyChartColorPalette, applyChartQuickLayout, applyChartStyle, CHART_C
|
|
|
14
14
|
import { getChartEditorCapabilities } from "./chart-editor-capabilities";
|
|
15
15
|
import { ChartRangeInput } from "./chart-range-input";
|
|
16
16
|
import { AxisOptionsFields } from "./fields/axis-options-fields";
|
|
17
|
+
import { ChartElementsFields } from "./fields/chart-elements-fields";
|
|
17
18
|
import { chartTypeHasOptions, ChartTypeOptionsFields, } from "./fields/chart-type-options";
|
|
18
19
|
import { DataLabelsFields } from "./fields/data-labels-fields";
|
|
19
20
|
import { SeriesFormatFields } from "./fields/series-format-fields";
|
|
@@ -322,7 +323,14 @@ export const ChartEditor = ({ chart, sheetId, onSubmit, onRequestMoveChart, getF
|
|
|
322
323
|
React.createElement(Section, { title: "Chart style", defaultOpen: true },
|
|
323
324
|
React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
|
|
324
325
|
React.createElement("label", { className: "font-system text-sm" }, "Chart title"),
|
|
325
|
-
React.createElement(Input, { value: values?.spec.title ?? "", type: "text", onChange: (e) => setFieldValue(`spec.title`, e.target.value), className: "text-xs", placeholder: "Enter chart title" })
|
|
326
|
+
React.createElement(Input, { value: values?.spec.title ?? "", type: "text", onChange: (e) => setFieldValue(`spec.title`, e.target.value), className: "text-xs", placeholder: "Enter chart title" }),
|
|
327
|
+
React.createElement("label", { className: "font-system text-xs" }, "Link title to cell"),
|
|
328
|
+
React.createElement(ChartRangeInput, { sheetId: sheetId, range: values.spec.titleSource ?? undefined, onChangeRange: (titleSource) => setFieldValue("spec.titleSource", {
|
|
329
|
+
...titleSource,
|
|
330
|
+
endRowIndex: titleSource.startRowIndex,
|
|
331
|
+
endColumnIndex: titleSource.startColumnIndex,
|
|
332
|
+
}), placeholder: "Select a cell", className: "mb-0" }),
|
|
333
|
+
values.spec.titleSource ? (React.createElement("button", { type: "button", className: "font-system text-xs text-rnc-muted-foreground underline self-start", onClick: () => setFieldValue("spec.titleSource", undefined) }, "Unlink title")) : null),
|
|
326
334
|
React.createElement(Stack, { direction: "vertical", className: "gap-1 pb-2" },
|
|
327
335
|
React.createElement("label", { className: "font-system text-sm" }, "Subtitle"),
|
|
328
336
|
React.createElement(Input, { value: values.spec.subtitle ?? "", type: "text", onChange: (e) => setFieldValue("spec.subtitle", e.target.value || undefined), className: "text-xs", placeholder: "Enter chart subtitle", "aria-label": "Chart subtitle" })),
|
|
@@ -400,6 +408,9 @@ export const ChartEditor = ({ chart, sheetId, onSubmit, onRequestMoveChart, getF
|
|
|
400
408
|
React.createElement("label", { className: "font-system text-sm" }, "Horizontal axis title"),
|
|
401
409
|
React.createElement(Input, { value: values?.spec.horizontalAxisTitle ?? "", type: "text", onChange: (e) => setFieldValue(`spec.horizontalAxisTitle`, e.target.value), className: "text-xs", placeholder: "Horizontal axis title" }))) : null,
|
|
402
410
|
caps.hasCategoryAxis ? (React.createElement(AxisOptionsFields, { label: "Category axis", kind: caps.category === "bubble" ? "value" : "category", value: values.spec.categoryAxisOptions, gridlinesDefault: false, onChange: (next) => setFieldValue("spec.categoryAxisOptions", next) })) : null)) : null,
|
|
411
|
+
caps.hasValueAxis &&
|
|
412
|
+
["line", "area", "column", "combo"].includes(values.spec.chartType) ? (React.createElement(Section, { title: "Chart elements" },
|
|
413
|
+
React.createElement(ChartElementsFields, { chartType: values.spec.chartType, spec: values.spec, onChange: (key, value) => setFieldValue(`spec.${key}`, value) }))) : null,
|
|
403
414
|
caps.hasDataLabels ? (React.createElement(Section, { title: "Data labels" },
|
|
404
415
|
React.createElement(DataLabelsFields, { value: values.spec.dataLabels, capabilities: caps, onChange: (dataLabels) => setFieldValue("spec.dataLabels", dataLabels) }))) : null,
|
|
405
416
|
hasTypeOptions ? (React.createElement(Section, { title: "Chart options" },
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ChartAxisOptions } from "@rowsncolumns/spreadsheet";
|
|
2
2
|
import React from "react";
|
|
3
3
|
/**
|
|
4
|
-
* One axis' options (`ChartAxisOptions`)
|
|
5
|
-
* (
|
|
6
|
-
* and
|
|
4
|
+
* One axis' options (`ChartAxisOptions`) — Excel's Format Axis pane
|
|
5
|
+
* (demo-app-monorepo#49969 FT-08): a value axis has its bounds, log scale
|
|
6
|
+
* and base, major / minor units, display units and number format; a
|
|
7
|
+
* category axis its label / tick-mark intervals; both have where the other
|
|
8
|
+
* axis crosses, direction, major / minor gridlines, tick marks, visibility
|
|
9
|
+
* and tick-label font.
|
|
7
10
|
*/
|
|
8
11
|
export declare const AxisOptionsFields: ({ label, kind, value, onChange, gridlinesDefault, }: {
|
|
9
12
|
label: string;
|