@odoo/o-spreadsheet 17.3.0-alpha.1 → 17.3.0-alpha.2
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/o-spreadsheet.cjs.js +941 -414
- package/dist/o-spreadsheet.d.ts +66 -55
- package/dist/o-spreadsheet.esm.js +941 -414
- package/dist/o-spreadsheet.iife.js +941 -414
- package/dist/o-spreadsheet.iife.min.js +315 -275
- package/dist/o_spreadsheet.xml +107 -44
- package/package.json +5 -5
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
5
5
|
* @see https://github.com/odoo/o-spreadsheet
|
|
6
|
-
* @version 17.3.0-alpha.
|
|
7
|
-
* @date 2024-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.3.0-alpha.2
|
|
7
|
+
* @date 2024-04-05T14:01:07.060Z
|
|
8
|
+
* @hash 8c5a229
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { reactive, useEnv, useSubEnv, useState, onWillUnmount, markRaw, toRaw, Component, useRef, onMounted, useEffect, onPatched, useComponent, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv } from '@odoo/owl';
|
|
@@ -464,7 +464,7 @@ function getItemId(item, itemsDic) {
|
|
|
464
464
|
}
|
|
465
465
|
// Generate new Id if the item didn't exist in the dictionary
|
|
466
466
|
const ids = Object.keys(itemsDic);
|
|
467
|
-
const maxId = ids.length === 0 ? 0 :
|
|
467
|
+
const maxId = ids.length === 0 ? 0 : largeMax(ids.map((id) => parseInt(id, 10)));
|
|
468
468
|
itemsDic[maxId + 1] = item;
|
|
469
469
|
return maxId + 1;
|
|
470
470
|
}
|
|
@@ -482,7 +482,7 @@ function debounce(func, wait, immediate) {
|
|
|
482
482
|
let timeout = undefined;
|
|
483
483
|
const debounced = function () {
|
|
484
484
|
const context = this;
|
|
485
|
-
const args = arguments;
|
|
485
|
+
const args = Array.from(arguments);
|
|
486
486
|
function later() {
|
|
487
487
|
timeout = undefined;
|
|
488
488
|
if (!immediate) {
|
|
@@ -684,6 +684,34 @@ function getSearchRegex(searchStr, searchOptions) {
|
|
|
684
684
|
}
|
|
685
685
|
return RegExp(searchValue, flags);
|
|
686
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Alternative to Math.max that works with large arrays.
|
|
689
|
+
* Typically useful for arrays bigger than 100k elements.
|
|
690
|
+
*/
|
|
691
|
+
function largeMax(array) {
|
|
692
|
+
let len = array.length;
|
|
693
|
+
if (len < 100_000)
|
|
694
|
+
return Math.max(...array);
|
|
695
|
+
let max = -Infinity;
|
|
696
|
+
while (len--) {
|
|
697
|
+
max = array[len] > max ? array[len] : max;
|
|
698
|
+
}
|
|
699
|
+
return max;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Alternative to Math.min that works with large arrays.
|
|
703
|
+
* Typically useful for arrays bigger than 100k elements.
|
|
704
|
+
*/
|
|
705
|
+
function largeMin(array) {
|
|
706
|
+
let len = array.length;
|
|
707
|
+
if (len < 100_000)
|
|
708
|
+
return Math.min(...array);
|
|
709
|
+
let min = +Infinity;
|
|
710
|
+
while (len--) {
|
|
711
|
+
min = array[len] < min ? array[len] : min;
|
|
712
|
+
}
|
|
713
|
+
return min;
|
|
714
|
+
}
|
|
687
715
|
|
|
688
716
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
689
717
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
@@ -2086,6 +2114,7 @@ var CommandResult;
|
|
|
2086
2114
|
CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
|
|
2087
2115
|
CommandResult["NoChanges"] = "NoChanges";
|
|
2088
2116
|
CommandResult["InvalidInputId"] = "InvalidInputId";
|
|
2117
|
+
CommandResult["SheetIsHidden"] = "SheetIsHidden";
|
|
2089
2118
|
})(CommandResult || (CommandResult = {}));
|
|
2090
2119
|
|
|
2091
2120
|
const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
|
|
@@ -4557,8 +4586,9 @@ function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
|
4557
4586
|
* Get the default height of the cell given its style.
|
|
4558
4587
|
*/
|
|
4559
4588
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4560
|
-
if (!cell || !cell.content)
|
|
4589
|
+
if (!cell || (!cell.isFormula && !cell.content)) {
|
|
4561
4590
|
return DEFAULT_CELL_HEIGHT;
|
|
4591
|
+
}
|
|
4562
4592
|
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4563
4593
|
const numberOfLines = cell.isFormula
|
|
4564
4594
|
? 1
|
|
@@ -6992,10 +7022,17 @@ urlRegistry.add("sheet_URL", {
|
|
|
6992
7022
|
},
|
|
6993
7023
|
open(url, env) {
|
|
6994
7024
|
const sheetId = parseSheetUrl(url);
|
|
6995
|
-
env.model.dispatch("ACTIVATE_SHEET", {
|
|
7025
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
6996
7026
|
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
6997
7027
|
sheetIdTo: sheetId,
|
|
6998
7028
|
});
|
|
7029
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
7030
|
+
env.notifyUser({
|
|
7031
|
+
type: "warning",
|
|
7032
|
+
sticky: false,
|
|
7033
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
7034
|
+
});
|
|
7035
|
+
}
|
|
6999
7036
|
},
|
|
7000
7037
|
sequence: 0,
|
|
7001
7038
|
});
|
|
@@ -7111,7 +7148,7 @@ function textCell(value, format, formattedValue) {
|
|
|
7111
7148
|
}
|
|
7112
7149
|
function numberCell(value, format, formattedValue) {
|
|
7113
7150
|
return {
|
|
7114
|
-
value: value || 0,
|
|
7151
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
7115
7152
|
format,
|
|
7116
7153
|
formattedValue,
|
|
7117
7154
|
type: CellValueType.number,
|
|
@@ -9955,11 +9992,11 @@ autoCompleteProviders.add("dataValidation", {
|
|
|
9955
9992
|
}
|
|
9956
9993
|
else {
|
|
9957
9994
|
const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
|
|
9958
|
-
values = this.getters
|
|
9995
|
+
values = Array.from(new Set(this.getters
|
|
9959
9996
|
.getRangeValues(range)
|
|
9960
9997
|
.filter(isNotNull)
|
|
9961
9998
|
.map((value) => value.toString())
|
|
9962
|
-
.filter((val) => val !== "");
|
|
9999
|
+
.filter((val) => val !== "")));
|
|
9963
10000
|
}
|
|
9964
10001
|
return values.map((value) => ({ text: value }));
|
|
9965
10002
|
},
|
|
@@ -19406,10 +19443,10 @@ function aggregateDataForLabels(labels, datasets) {
|
|
|
19406
19443
|
}
|
|
19407
19444
|
}
|
|
19408
19445
|
return {
|
|
19409
|
-
labels:
|
|
19446
|
+
labels: Array.from(labelSet),
|
|
19410
19447
|
dataSetsValues: datasets.map((dataset, indexOfDataset) => ({
|
|
19411
19448
|
...dataset,
|
|
19412
|
-
data:
|
|
19449
|
+
data: Array.from(labelSet).map((label) => labelMap[label][indexOfDataset]),
|
|
19413
19450
|
})),
|
|
19414
19451
|
};
|
|
19415
19452
|
}
|
|
@@ -19428,8 +19465,8 @@ function truncateLabel(label) {
|
|
|
19428
19465
|
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
|
|
19429
19466
|
const options = {
|
|
19430
19467
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
19431
|
-
responsive: true,
|
|
19432
|
-
maintainAspectRatio: false,
|
|
19468
|
+
responsive: true, // will resize when its container is resized
|
|
19469
|
+
maintainAspectRatio: false, // doesn't maintain the aspect ration (width/height =2 by default) so the user has the choice of the exact layout
|
|
19433
19470
|
layout: {
|
|
19434
19471
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
19435
19472
|
},
|
|
@@ -19475,7 +19512,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
|
|
|
19475
19512
|
labels: labels.map(truncateLabel),
|
|
19476
19513
|
datasets: [],
|
|
19477
19514
|
},
|
|
19478
|
-
platform: undefined,
|
|
19515
|
+
platform: undefined, // This key is optional and will be set by chart.js
|
|
19479
19516
|
plugins: [],
|
|
19480
19517
|
};
|
|
19481
19518
|
}
|
|
@@ -19752,7 +19789,7 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
19752
19789
|
},
|
|
19753
19790
|
y: {
|
|
19754
19791
|
position: chart.verticalAxisPosition,
|
|
19755
|
-
beginAtZero: true,
|
|
19792
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
19756
19793
|
ticks: {
|
|
19757
19794
|
color: fontColor,
|
|
19758
19795
|
callback: (value) => {
|
|
@@ -19803,6 +19840,204 @@ function createBarChartRuntime(chart, getters) {
|
|
|
19803
19840
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
19804
19841
|
}
|
|
19805
19842
|
|
|
19843
|
+
class ComboChart extends AbstractChart {
|
|
19844
|
+
useBothYAxis;
|
|
19845
|
+
dataSets;
|
|
19846
|
+
labelRange;
|
|
19847
|
+
background;
|
|
19848
|
+
verticalAxisPosition;
|
|
19849
|
+
legendPosition;
|
|
19850
|
+
aggregated;
|
|
19851
|
+
dataSetsHaveTitle;
|
|
19852
|
+
type = "combo";
|
|
19853
|
+
constructor(definition, sheetId, getters) {
|
|
19854
|
+
super(definition, sheetId, getters);
|
|
19855
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
19856
|
+
this.labelRange = createRange(getters, sheetId, definition.labelRange);
|
|
19857
|
+
this.background = definition.background;
|
|
19858
|
+
this.verticalAxisPosition = definition.verticalAxisPosition;
|
|
19859
|
+
this.legendPosition = definition.legendPosition;
|
|
19860
|
+
this.aggregated = definition.aggregated;
|
|
19861
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
19862
|
+
this.useBothYAxis = definition.useBothYAxis;
|
|
19863
|
+
}
|
|
19864
|
+
static transformDefinition(definition, executed) {
|
|
19865
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
19866
|
+
}
|
|
19867
|
+
static validateChartDefinition(validator, definition) {
|
|
19868
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
19869
|
+
}
|
|
19870
|
+
getContextCreation() {
|
|
19871
|
+
return {
|
|
19872
|
+
background: this.background,
|
|
19873
|
+
title: this.title,
|
|
19874
|
+
range: this.dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, this.sheetId)),
|
|
19875
|
+
auxiliaryRange: this.labelRange
|
|
19876
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
19877
|
+
: undefined,
|
|
19878
|
+
aggregated: this.aggregated,
|
|
19879
|
+
};
|
|
19880
|
+
}
|
|
19881
|
+
getDefinition() {
|
|
19882
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
19883
|
+
}
|
|
19884
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
19885
|
+
return {
|
|
19886
|
+
type: "combo",
|
|
19887
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
19888
|
+
background: this.background,
|
|
19889
|
+
dataSets: dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, targetSheetId || this.sheetId)),
|
|
19890
|
+
legendPosition: this.legendPosition,
|
|
19891
|
+
verticalAxisPosition: this.verticalAxisPosition,
|
|
19892
|
+
labelRange: labelRange
|
|
19893
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
19894
|
+
: undefined,
|
|
19895
|
+
title: this.title,
|
|
19896
|
+
aggregated: this.aggregated,
|
|
19897
|
+
useBothYAxis: this.useBothYAxis,
|
|
19898
|
+
};
|
|
19899
|
+
}
|
|
19900
|
+
getDefinitionForExcel() {
|
|
19901
|
+
// Excel does not support aggregating labels
|
|
19902
|
+
if (this.aggregated) {
|
|
19903
|
+
return undefined;
|
|
19904
|
+
}
|
|
19905
|
+
const dataSets = this.dataSets
|
|
19906
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
19907
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
19908
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
19909
|
+
return {
|
|
19910
|
+
...this.getDefinition(),
|
|
19911
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
19912
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
19913
|
+
dataSets,
|
|
19914
|
+
labelRange,
|
|
19915
|
+
};
|
|
19916
|
+
}
|
|
19917
|
+
updateRanges(applyChange) {
|
|
19918
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
19919
|
+
if (!isStale) {
|
|
19920
|
+
return this;
|
|
19921
|
+
}
|
|
19922
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
19923
|
+
return new ComboChart(definition, this.sheetId, this.getters);
|
|
19924
|
+
}
|
|
19925
|
+
static getDefinitionFromContextCreation(context) {
|
|
19926
|
+
return {
|
|
19927
|
+
background: context.background,
|
|
19928
|
+
dataSets: context.range ? context.range : [],
|
|
19929
|
+
dataSetsHaveTitle: false,
|
|
19930
|
+
aggregated: context.aggregated,
|
|
19931
|
+
legendPosition: "top",
|
|
19932
|
+
title: context.title || "",
|
|
19933
|
+
verticalAxisPosition: "left",
|
|
19934
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
19935
|
+
type: "combo",
|
|
19936
|
+
useBothYAxis: false,
|
|
19937
|
+
};
|
|
19938
|
+
}
|
|
19939
|
+
copyForSheetId(sheetId) {
|
|
19940
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
19941
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
19942
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
19943
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
19944
|
+
}
|
|
19945
|
+
copyInSheetId(sheetId) {
|
|
19946
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
19947
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
19948
|
+
}
|
|
19949
|
+
}
|
|
19950
|
+
function createComboChartRuntime(chart, getters) {
|
|
19951
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
19952
|
+
const locale = getters.getLocale();
|
|
19953
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
19954
|
+
let labels = labelValues.formattedValues;
|
|
19955
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
19956
|
+
if (chart.dataSetsHaveTitle &&
|
|
19957
|
+
dataSetsValues[0] &&
|
|
19958
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
19959
|
+
labels.shift();
|
|
19960
|
+
}
|
|
19961
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
19962
|
+
if (chart.aggregated) {
|
|
19963
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
19964
|
+
}
|
|
19965
|
+
const localeFormat = { format: dataSetFormat, locale };
|
|
19966
|
+
const fontColor = chartFontColor(chart.background);
|
|
19967
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
|
|
19968
|
+
const legend = {
|
|
19969
|
+
labels: { color: fontColor },
|
|
19970
|
+
};
|
|
19971
|
+
if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
|
|
19972
|
+
legend.display = false;
|
|
19973
|
+
}
|
|
19974
|
+
else {
|
|
19975
|
+
legend.position = chart.legendPosition;
|
|
19976
|
+
}
|
|
19977
|
+
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
19978
|
+
config.options.layout = {
|
|
19979
|
+
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
19980
|
+
};
|
|
19981
|
+
config.options.scales = {
|
|
19982
|
+
x: {
|
|
19983
|
+
ticks: {
|
|
19984
|
+
padding: 5,
|
|
19985
|
+
color: fontColor,
|
|
19986
|
+
},
|
|
19987
|
+
},
|
|
19988
|
+
};
|
|
19989
|
+
const verticalAxis = {
|
|
19990
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
19991
|
+
ticks: {
|
|
19992
|
+
color: fontColor,
|
|
19993
|
+
callback: (value) => {
|
|
19994
|
+
value = Number(value);
|
|
19995
|
+
if (isNaN(value))
|
|
19996
|
+
return value;
|
|
19997
|
+
const { locale, format } = localeFormat;
|
|
19998
|
+
return formatValue(value, {
|
|
19999
|
+
locale,
|
|
20000
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
20001
|
+
});
|
|
20002
|
+
},
|
|
20003
|
+
},
|
|
20004
|
+
};
|
|
20005
|
+
if (chart.useBothYAxis) {
|
|
20006
|
+
config.options.scales.y = {
|
|
20007
|
+
...verticalAxis,
|
|
20008
|
+
position: "left",
|
|
20009
|
+
};
|
|
20010
|
+
config.options.scales.y1 = {
|
|
20011
|
+
...verticalAxis,
|
|
20012
|
+
position: "right",
|
|
20013
|
+
grid: {
|
|
20014
|
+
display: false,
|
|
20015
|
+
},
|
|
20016
|
+
};
|
|
20017
|
+
}
|
|
20018
|
+
else {
|
|
20019
|
+
config.options.scales.y = {
|
|
20020
|
+
...verticalAxis,
|
|
20021
|
+
position: chart.verticalAxisPosition,
|
|
20022
|
+
};
|
|
20023
|
+
}
|
|
20024
|
+
const colors = new ChartColors();
|
|
20025
|
+
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
20026
|
+
const color = colors.next();
|
|
20027
|
+
const dataset = {
|
|
20028
|
+
label,
|
|
20029
|
+
data,
|
|
20030
|
+
borderColor: color,
|
|
20031
|
+
backgroundColor: color,
|
|
20032
|
+
yAxisID: index > 0 && chart.useBothYAxis ? "y1" : "y",
|
|
20033
|
+
type: index === 0 ? "bar" : "line",
|
|
20034
|
+
order: -index,
|
|
20035
|
+
};
|
|
20036
|
+
config.data.datasets.push(dataset);
|
|
20037
|
+
}
|
|
20038
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
20039
|
+
}
|
|
20040
|
+
|
|
19806
20041
|
function isDataRangeValid(definition) {
|
|
19807
20042
|
return definition.dataRange && !rangeReference.test(definition.dataRange)
|
|
19808
20043
|
? "InvalidGaugeDataRange" /* CommandResult.InvalidGaugeDataRange */
|
|
@@ -20141,7 +20376,7 @@ function getBestTimeUnitForScale(labels, format, locale) {
|
|
|
20141
20376
|
return undefined;
|
|
20142
20377
|
}
|
|
20143
20378
|
const labelsTimestamps = labelDates.map((date) => date.getTime());
|
|
20144
|
-
const period =
|
|
20379
|
+
const period = largeMax(labelsTimestamps) - largeMin(labelsTimestamps);
|
|
20145
20380
|
const minUnit = getFormatMinDisplayUnit(format);
|
|
20146
20381
|
if (UNIT_LENGTH.second >= UNIT_LENGTH[minUnit] && Milliseconds.inSeconds(period) < 180) {
|
|
20147
20382
|
return "second";
|
|
@@ -20273,7 +20508,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
|
|
|
20273
20508
|
},
|
|
20274
20509
|
y: {
|
|
20275
20510
|
position: chart.verticalAxisPosition,
|
|
20276
|
-
beginAtZero: true,
|
|
20511
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
20277
20512
|
ticks: {
|
|
20278
20513
|
color: fontColor,
|
|
20279
20514
|
callback: (value) => {
|
|
@@ -20359,7 +20594,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
20359
20594
|
const dataset = {
|
|
20360
20595
|
label,
|
|
20361
20596
|
data,
|
|
20362
|
-
tension: 0,
|
|
20597
|
+
tension: 0, // 0 -> render straight lines, which is much faster
|
|
20363
20598
|
borderColor: color,
|
|
20364
20599
|
backgroundColor,
|
|
20365
20600
|
pointBackgroundColor: color,
|
|
@@ -20581,7 +20816,7 @@ class PieChart extends AbstractChart {
|
|
|
20581
20816
|
...this.getDefinition(),
|
|
20582
20817
|
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
20583
20818
|
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
20584
|
-
verticalAxisPosition: "left",
|
|
20819
|
+
verticalAxisPosition: "left", //TODO ExcelChartDefinition should be adapted, but can be done later
|
|
20585
20820
|
dataSets,
|
|
20586
20821
|
labelRange,
|
|
20587
20822
|
};
|
|
@@ -20629,7 +20864,7 @@ function getPieConfiguration(chart, labels, localeFormat) {
|
|
|
20629
20864
|
}
|
|
20630
20865
|
function getPieColors(colors, dataSetsValues) {
|
|
20631
20866
|
const pieColors = [];
|
|
20632
|
-
const maxLength =
|
|
20867
|
+
const maxLength = largeMax(dataSetsValues.map((ds) => ds.data.length));
|
|
20633
20868
|
for (let i = 0; i <= maxLength; i++) {
|
|
20634
20869
|
pieColors.push(colors.next());
|
|
20635
20870
|
}
|
|
@@ -20798,7 +21033,7 @@ function createScatterChartRuntime(chart, getters) {
|
|
|
20798
21033
|
configOptions.elements = {
|
|
20799
21034
|
point: {
|
|
20800
21035
|
radius: 3,
|
|
20801
|
-
hoverRadius: 3,
|
|
21036
|
+
hoverRadius: 3, // chartJS seems bugged, the point starts with the "hoverRadius" value
|
|
20802
21037
|
hitRadius: 8,
|
|
20803
21038
|
},
|
|
20804
21039
|
};
|
|
@@ -20838,6 +21073,16 @@ chartRegistry.add("bar", {
|
|
|
20838
21073
|
name: _t("Bar"),
|
|
20839
21074
|
sequence: 10,
|
|
20840
21075
|
});
|
|
21076
|
+
chartRegistry.add("combo", {
|
|
21077
|
+
match: (type) => type === "combo",
|
|
21078
|
+
createChart: (definition, sheetId, getters) => new ComboChart(definition, sheetId, getters),
|
|
21079
|
+
getChartRuntime: createComboChartRuntime,
|
|
21080
|
+
validateChartDefinition: (validator, definition) => ComboChart.validateChartDefinition(validator, definition),
|
|
21081
|
+
transformDefinition: (definition, executed) => ComboChart.transformDefinition(definition, executed),
|
|
21082
|
+
getChartDefinitionFromContextCreation: (context) => ComboChart.getDefinitionFromContextCreation(context),
|
|
21083
|
+
name: _t("Combo"),
|
|
21084
|
+
sequence: 15,
|
|
21085
|
+
});
|
|
20841
21086
|
chartRegistry.add("line", {
|
|
20842
21087
|
match: (type) => type === "line",
|
|
20843
21088
|
createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
|
|
@@ -20891,6 +21136,7 @@ chartRegistry.add("scatter", {
|
|
|
20891
21136
|
const chartComponentRegistry = new Registry();
|
|
20892
21137
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
20893
21138
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
21139
|
+
chartComponentRegistry.add("combo", ChartJsComponent);
|
|
20894
21140
|
chartComponentRegistry.add("pie", ChartJsComponent);
|
|
20895
21141
|
chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
20896
21142
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
@@ -23149,7 +23395,7 @@ const lightTemplateWithHeader = (colorSet) => ({
|
|
|
23149
23395
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23150
23396
|
border: { bottom: { color: colorSet.highlight, style: "thin" } },
|
|
23151
23397
|
},
|
|
23152
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23398
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23153
23399
|
firstRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23154
23400
|
secondRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23155
23401
|
});
|
|
@@ -23167,7 +23413,7 @@ const lightTemplateAllBorders = (colorSet) => ({
|
|
|
23167
23413
|
},
|
|
23168
23414
|
},
|
|
23169
23415
|
headerRow: { border: { bottom: { color: colorSet.highlight, style: "medium" } } },
|
|
23170
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23416
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23171
23417
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23172
23418
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23173
23419
|
});
|
|
@@ -23186,7 +23432,7 @@ const mediumTemplateBandedBorders = (colorSet) => ({
|
|
|
23186
23432
|
headerRow: {
|
|
23187
23433
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23188
23434
|
},
|
|
23189
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23435
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23190
23436
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23191
23437
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23192
23438
|
});
|
|
@@ -23222,7 +23468,7 @@ const mediumTemplateMinimalBorders = (colorSet) => ({
|
|
|
23222
23468
|
bottom: { color: "#000000", style: "medium" },
|
|
23223
23469
|
},
|
|
23224
23470
|
},
|
|
23225
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23471
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23226
23472
|
headerRow: {
|
|
23227
23473
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23228
23474
|
border: { bottom: { color: "#000000", style: "medium" } },
|
|
@@ -23246,7 +23492,7 @@ const mediumTemplateAllBorders = (colorSet) => ({
|
|
|
23246
23492
|
},
|
|
23247
23493
|
style: { fillColor: colorSet.light },
|
|
23248
23494
|
},
|
|
23249
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23495
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23250
23496
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23251
23497
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
23252
23498
|
});
|
|
@@ -23277,7 +23523,7 @@ const darkTemplateNoBorders = (colorSet) => ({
|
|
|
23277
23523
|
category: "dark",
|
|
23278
23524
|
colorName: colorSet.name,
|
|
23279
23525
|
wholeTable: { style: { fillColor: colorSet.light } },
|
|
23280
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23526
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23281
23527
|
headerRow: { style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" } },
|
|
23282
23528
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23283
23529
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
@@ -23442,8 +23688,8 @@ const DELETE_CONTENT_ROWS_NAME = (env) => {
|
|
|
23442
23688
|
let last;
|
|
23443
23689
|
const activesRows = env.model.getters.getActiveRows();
|
|
23444
23690
|
if (activesRows.size !== 0) {
|
|
23445
|
-
first =
|
|
23446
|
-
last =
|
|
23691
|
+
first = largeMin([...activesRows]);
|
|
23692
|
+
last = largeMax([...activesRows]);
|
|
23447
23693
|
}
|
|
23448
23694
|
else {
|
|
23449
23695
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23471,8 +23717,8 @@ const DELETE_CONTENT_COLUMNS_NAME = (env) => {
|
|
|
23471
23717
|
let last;
|
|
23472
23718
|
const activeCols = env.model.getters.getActiveCols();
|
|
23473
23719
|
if (activeCols.size !== 0) {
|
|
23474
|
-
first =
|
|
23475
|
-
last =
|
|
23720
|
+
first = largeMin([...activeCols]);
|
|
23721
|
+
last = largeMax([...activeCols]);
|
|
23476
23722
|
}
|
|
23477
23723
|
else {
|
|
23478
23724
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23500,8 +23746,8 @@ const REMOVE_ROWS_NAME = (env) => {
|
|
|
23500
23746
|
let last;
|
|
23501
23747
|
const activesRows = env.model.getters.getActiveRows();
|
|
23502
23748
|
if (activesRows.size !== 0) {
|
|
23503
|
-
first =
|
|
23504
|
-
last =
|
|
23749
|
+
first = largeMin([...activesRows]);
|
|
23750
|
+
last = largeMax([...activesRows]);
|
|
23505
23751
|
}
|
|
23506
23752
|
else {
|
|
23507
23753
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23542,8 +23788,8 @@ const REMOVE_COLUMNS_NAME = (env) => {
|
|
|
23542
23788
|
let last;
|
|
23543
23789
|
const activeCols = env.model.getters.getActiveCols();
|
|
23544
23790
|
if (activeCols.size !== 0) {
|
|
23545
|
-
first =
|
|
23546
|
-
last =
|
|
23791
|
+
first = largeMin([...activeCols]);
|
|
23792
|
+
last = largeMax([...activeCols]);
|
|
23547
23793
|
}
|
|
23548
23794
|
else {
|
|
23549
23795
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23584,7 +23830,7 @@ const INSERT_ROWS_BEFORE_ACTION = (env) => {
|
|
|
23584
23830
|
let row;
|
|
23585
23831
|
let quantity;
|
|
23586
23832
|
if (activeRows.size) {
|
|
23587
|
-
row =
|
|
23833
|
+
row = largeMin([...activeRows]);
|
|
23588
23834
|
quantity = activeRows.size;
|
|
23589
23835
|
}
|
|
23590
23836
|
else {
|
|
@@ -23605,7 +23851,7 @@ const INSERT_ROWS_AFTER_ACTION = (env) => {
|
|
|
23605
23851
|
let row;
|
|
23606
23852
|
let quantity;
|
|
23607
23853
|
if (activeRows.size) {
|
|
23608
|
-
row =
|
|
23854
|
+
row = largeMax([...activeRows]);
|
|
23609
23855
|
quantity = activeRows.size;
|
|
23610
23856
|
}
|
|
23611
23857
|
else {
|
|
@@ -23626,7 +23872,7 @@ const INSERT_COLUMNS_BEFORE_ACTION = (env) => {
|
|
|
23626
23872
|
let column;
|
|
23627
23873
|
let quantity;
|
|
23628
23874
|
if (activeCols.size) {
|
|
23629
|
-
column =
|
|
23875
|
+
column = largeMin([...activeCols]);
|
|
23630
23876
|
quantity = activeCols.size;
|
|
23631
23877
|
}
|
|
23632
23878
|
else {
|
|
@@ -23647,7 +23893,7 @@ const INSERT_COLUMNS_AFTER_ACTION = (env) => {
|
|
|
23647
23893
|
let column;
|
|
23648
23894
|
let quantity;
|
|
23649
23895
|
if (activeCols.size) {
|
|
23650
|
-
column =
|
|
23896
|
+
column = largeMax([...activeCols]);
|
|
23651
23897
|
quantity = activeCols.size;
|
|
23652
23898
|
}
|
|
23653
23899
|
else {
|
|
@@ -27253,6 +27499,22 @@ class BarChartDesignPanel extends LineBarPieDesignPanel {
|
|
|
27253
27499
|
static template = "o-spreadsheet-BarChartDesignPanel";
|
|
27254
27500
|
}
|
|
27255
27501
|
|
|
27502
|
+
class ComboChartConfigPanel extends LineBarPieConfigPanel {
|
|
27503
|
+
static template = "o-spreadsheet-ComboChartConfigPanel";
|
|
27504
|
+
get shouldUseRightAxis() {
|
|
27505
|
+
return _t("Use right axis for line series");
|
|
27506
|
+
}
|
|
27507
|
+
onUpdateUseRightAxis(useBothYAxis) {
|
|
27508
|
+
this.props.updateChart(this.props.figureId, {
|
|
27509
|
+
useBothYAxis,
|
|
27510
|
+
});
|
|
27511
|
+
}
|
|
27512
|
+
}
|
|
27513
|
+
|
|
27514
|
+
class ComboChartDesignPanel extends LineBarPieDesignPanel {
|
|
27515
|
+
static template = "o-spreadsheet-ComboChartDesignPanel";
|
|
27516
|
+
}
|
|
27517
|
+
|
|
27256
27518
|
class GaugeChartConfigPanel extends Component {
|
|
27257
27519
|
static template = "o-spreadsheet-GaugeChartConfigPanel";
|
|
27258
27520
|
static components = { ChartErrorSection, ChartDataSeries };
|
|
@@ -27610,6 +27872,10 @@ chartSidePanelComponentRegistry
|
|
|
27610
27872
|
.add("bar", {
|
|
27611
27873
|
configuration: BarConfigPanel,
|
|
27612
27874
|
design: BarChartDesignPanel,
|
|
27875
|
+
})
|
|
27876
|
+
.add("combo", {
|
|
27877
|
+
configuration: ComboChartConfigPanel,
|
|
27878
|
+
design: ComboChartDesignPanel,
|
|
27613
27879
|
})
|
|
27614
27880
|
.add("pie", {
|
|
27615
27881
|
configuration: LineBarPieConfigPanel,
|
|
@@ -30250,7 +30516,11 @@ class SplitIntoColumnsPanel extends Component {
|
|
|
30250
30516
|
const composerStore = useStore(ComposerStore);
|
|
30251
30517
|
// The feature makes no sense if we are editing a cell, because then the selection isn't active
|
|
30252
30518
|
// Stop the edition when the panel is mounted, and close the panel if the user start editing a cell
|
|
30253
|
-
useEffect(
|
|
30519
|
+
useEffect((editionMode) => {
|
|
30520
|
+
if (editionMode !== "inactive") {
|
|
30521
|
+
this.props.onCloseSidePanel();
|
|
30522
|
+
}
|
|
30523
|
+
}, () => [composerStore.editionMode]);
|
|
30254
30524
|
onMounted(() => {
|
|
30255
30525
|
composerStore.stopEdition();
|
|
30256
30526
|
});
|
|
@@ -31959,6 +32229,9 @@ class FunctionDescriptionProvider extends Component {
|
|
|
31959
32229
|
this.assistantState.allowCellSelectionBehind = false;
|
|
31960
32230
|
}, 2000);
|
|
31961
32231
|
}
|
|
32232
|
+
get formulaArgSeparator() {
|
|
32233
|
+
return this.env.model.getters.getLocale().formulaArgSeparator + " ";
|
|
32234
|
+
}
|
|
31962
32235
|
}
|
|
31963
32236
|
|
|
31964
32237
|
const functions$2 = functionRegistry.content;
|
|
@@ -32118,6 +32391,12 @@ class Composer extends Component {
|
|
|
32118
32391
|
useEffect(() => {
|
|
32119
32392
|
this.processContent();
|
|
32120
32393
|
});
|
|
32394
|
+
onPatched(() => {
|
|
32395
|
+
// Required because typing '=SUM' and double-clicking another cell leaves ShowProvider/ShowDescription true
|
|
32396
|
+
if (this.composerStore.editionMode === "inactive") {
|
|
32397
|
+
this.processTokenAtCursor();
|
|
32398
|
+
}
|
|
32399
|
+
});
|
|
32121
32400
|
}
|
|
32122
32401
|
// ---------------------------------------------------------------------------
|
|
32123
32402
|
// Handlers
|
|
@@ -34097,11 +34376,6 @@ css /* scss */ `
|
|
|
34097
34376
|
height: 10000px;
|
|
34098
34377
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34099
34378
|
}
|
|
34100
|
-
.o-unhide-buttons {
|
|
34101
|
-
width: fit-content;
|
|
34102
|
-
gap: 5px;
|
|
34103
|
-
transform: translate(-50%, 0);
|
|
34104
|
-
}
|
|
34105
34379
|
.o-unhide:hover {
|
|
34106
34380
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34107
34381
|
background-color: lightgrey;
|
|
@@ -34263,10 +34537,6 @@ css /* scss */ `
|
|
|
34263
34537
|
height: 1px;
|
|
34264
34538
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34265
34539
|
}
|
|
34266
|
-
.o-unhide-buttons {
|
|
34267
|
-
height: fit-content;
|
|
34268
|
-
transform: translate(0, -50%);
|
|
34269
|
-
}
|
|
34270
34540
|
.o-unhide:hover {
|
|
34271
34541
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34272
34542
|
background-color: lightgrey;
|
|
@@ -35474,7 +35744,7 @@ class VerticalScrollBar extends Component {
|
|
|
35474
35744
|
onScroll(offset) {
|
|
35475
35745
|
const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
|
|
35476
35746
|
this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
|
|
35477
|
-
offsetX: scrollX,
|
|
35747
|
+
offsetX: scrollX, // offsetX is the same
|
|
35478
35748
|
offsetY: offset,
|
|
35479
35749
|
});
|
|
35480
35750
|
}
|
|
@@ -35762,8 +36032,8 @@ class Grid extends Component {
|
|
|
35762
36032
|
"Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
|
|
35763
36033
|
"Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
|
|
35764
36034
|
"Ctrl+Shift+V": () => PASTE_AS_VALUE_ACTION(this.env),
|
|
35765
|
-
"Ctrl+Shift+<": () => this.clearFormatting(),
|
|
35766
|
-
"Ctrl+<": () => this.clearFormatting(),
|
|
36035
|
+
"Ctrl+Shift+<": () => this.clearFormatting(), // for qwerty
|
|
36036
|
+
"Ctrl+<": () => this.clearFormatting(), // for azerty
|
|
35767
36037
|
"Ctrl+Shift+ ": () => {
|
|
35768
36038
|
this.env.model.selection.selectAll();
|
|
35769
36039
|
},
|
|
@@ -36199,6 +36469,7 @@ const XLSX_CHART_TYPES = [
|
|
|
36199
36469
|
"surfaceChart",
|
|
36200
36470
|
"surface3DChart",
|
|
36201
36471
|
"bubbleChart",
|
|
36472
|
+
"comboChart",
|
|
36202
36473
|
];
|
|
36203
36474
|
|
|
36204
36475
|
/** In XLSX color format (no #) */
|
|
@@ -36530,10 +36801,10 @@ function convertCFCellIsOperator(xlsxCfOperator) {
|
|
|
36530
36801
|
const CF_TYPE_CONVERSION_MAP = {
|
|
36531
36802
|
aboveAverage: undefined,
|
|
36532
36803
|
expression: undefined,
|
|
36533
|
-
cellIs: undefined,
|
|
36534
|
-
colorScale: undefined,
|
|
36804
|
+
cellIs: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36805
|
+
colorScale: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36535
36806
|
dataBar: undefined,
|
|
36536
|
-
iconSet: undefined,
|
|
36807
|
+
iconSet: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36537
36808
|
top10: undefined,
|
|
36538
36809
|
uniqueValues: undefined,
|
|
36539
36810
|
duplicateValues: undefined,
|
|
@@ -36610,6 +36881,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36610
36881
|
surfaceChart: undefined,
|
|
36611
36882
|
surface3DChart: undefined,
|
|
36612
36883
|
bubbleChart: undefined,
|
|
36884
|
+
comboChart: "combo",
|
|
36613
36885
|
};
|
|
36614
36886
|
/** Conversion map for the SUBTOTAL(index, formula) function in xlsx, index <=> actual function*/
|
|
36615
36887
|
const SUBTOTAL_FUNCTION_CONVERSION_MAP = {
|
|
@@ -36768,7 +37040,7 @@ const XLSX_INDEXED_COLORS = {
|
|
|
36768
37040
|
61: "993366",
|
|
36769
37041
|
62: "333399",
|
|
36770
37042
|
63: "333333",
|
|
36771
|
-
64: "000000",
|
|
37043
|
+
64: "000000", // system foreground
|
|
36772
37044
|
65: "FFFFFF", // system background
|
|
36773
37045
|
};
|
|
36774
37046
|
const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
|
|
@@ -37960,7 +38232,7 @@ function convertHyperlink(link, cellValue, warningManager) {
|
|
|
37960
38232
|
function getSheetDims(sheet) {
|
|
37961
38233
|
const dims = [0, 0];
|
|
37962
38234
|
for (let row of sheet.rows) {
|
|
37963
|
-
dims[0] = Math.max(dims[0],
|
|
38235
|
+
dims[0] = Math.max(dims[0], largeMax(row.cells.map((cell) => toCartesian(cell.xc).col)));
|
|
37964
38236
|
dims[1] = Math.max(dims[1], row.index);
|
|
37965
38237
|
}
|
|
37966
38238
|
dims[0] = Math.max(dims[0], EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
|
|
@@ -38826,6 +39098,9 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38826
39098
|
if (!CHART_TYPE_CONVERSION_MAP[chartType]) {
|
|
38827
39099
|
throw new Error(`Unsupported chart type ${chartType}`);
|
|
38828
39100
|
}
|
|
39101
|
+
if (CHART_TYPE_CONVERSION_MAP[chartType] === "combo") {
|
|
39102
|
+
return this.extractComboChart(rootChartElement);
|
|
39103
|
+
}
|
|
38829
39104
|
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
38830
39105
|
const chartTitle = this.mapOnElements({ parent: rootChartElement, query: "c:title a:t" }, (textElement) => {
|
|
38831
39106
|
return textElement.textContent || "";
|
|
@@ -38854,6 +39129,37 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38854
39129
|
};
|
|
38855
39130
|
})[0];
|
|
38856
39131
|
}
|
|
39132
|
+
extractComboChart(chartElement) {
|
|
39133
|
+
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
39134
|
+
const chartTitle = this.mapOnElements({ parent: chartElement, query: "c:title a:t" }, (textElement) => {
|
|
39135
|
+
return textElement.textContent || "";
|
|
39136
|
+
}).join("");
|
|
39137
|
+
const barChartGrouping = this.extractChildAttr(chartElement, "c:grouping", "val", {
|
|
39138
|
+
default: "clustered",
|
|
39139
|
+
}).asString();
|
|
39140
|
+
return {
|
|
39141
|
+
title: chartTitle,
|
|
39142
|
+
type: "combo",
|
|
39143
|
+
dataSets: [
|
|
39144
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`)),
|
|
39145
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`)),
|
|
39146
|
+
],
|
|
39147
|
+
labelRange: this.extractChildTextContent(chartElement, "c:ser c:cat c:f"),
|
|
39148
|
+
backgroundColor: this.extractChildAttr(chartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
39149
|
+
default: "ffffff",
|
|
39150
|
+
}).asString(),
|
|
39151
|
+
verticalAxisPosition: this.extractChildAttr(chartElement, "c:valAx > c:axPos", "val", {
|
|
39152
|
+
default: "l",
|
|
39153
|
+
}).asString() === "r"
|
|
39154
|
+
? "right"
|
|
39155
|
+
: "left",
|
|
39156
|
+
legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
|
|
39157
|
+
default: "b",
|
|
39158
|
+
}).asString()],
|
|
39159
|
+
stacked: barChartGrouping === "stacked",
|
|
39160
|
+
fontColor: "000000",
|
|
39161
|
+
};
|
|
39162
|
+
}
|
|
38857
39163
|
extractChartDatasets(chartElement) {
|
|
38858
39164
|
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
38859
39165
|
return {
|
|
@@ -38871,12 +39177,21 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38871
39177
|
if (!plotAreaElement) {
|
|
38872
39178
|
throw new Error("Missing plot area in the chart definition.");
|
|
38873
39179
|
}
|
|
39180
|
+
let globalTag = undefined;
|
|
38874
39181
|
for (let child of plotAreaElement.children) {
|
|
38875
39182
|
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
38876
39183
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
38877
|
-
|
|
39184
|
+
if (!globalTag) {
|
|
39185
|
+
globalTag = tag;
|
|
39186
|
+
}
|
|
39187
|
+
else if (globalTag !== tag) {
|
|
39188
|
+
globalTag = "comboChart";
|
|
39189
|
+
}
|
|
38878
39190
|
}
|
|
38879
39191
|
}
|
|
39192
|
+
if (globalTag) {
|
|
39193
|
+
return globalTag;
|
|
39194
|
+
}
|
|
38880
39195
|
throw new Error("Unknown chart type");
|
|
38881
39196
|
}
|
|
38882
39197
|
}
|
|
@@ -42467,6 +42782,9 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
42467
42782
|
if (newRule.criterion.type === "isBoolean") {
|
|
42468
42783
|
this.setCenterStyleToBooleanCells(newRule);
|
|
42469
42784
|
}
|
|
42785
|
+
else if (newRule.criterion.type === "isValueInList") {
|
|
42786
|
+
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
42787
|
+
}
|
|
42470
42788
|
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
42471
42789
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
42472
42790
|
if (ruleIndex !== -1) {
|
|
@@ -42863,7 +43181,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
|
|
|
42863
43181
|
if (hiddenElements.size >= elements) {
|
|
42864
43182
|
return "TooManyHiddenElements" /* CommandResult.TooManyHiddenElements */;
|
|
42865
43183
|
}
|
|
42866
|
-
else if (
|
|
43184
|
+
else if (largeMin(cmd.elements) < 0 || largeMax(cmd.elements) > elements) {
|
|
42867
43185
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
42868
43186
|
}
|
|
42869
43187
|
else {
|
|
@@ -43192,7 +43510,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43192
43510
|
"isInSameMerge",
|
|
43193
43511
|
"isMergeHidden",
|
|
43194
43512
|
"getMainCellPosition",
|
|
43195
|
-
"getBottomLeftCell",
|
|
43196
43513
|
"expandZone",
|
|
43197
43514
|
"doesIntersectMerge",
|
|
43198
43515
|
"doesColumnsHaveCommonMerges",
|
|
@@ -43374,13 +43691,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43374
43691
|
const mergeTopLeftPos = this.getMerge(position).topLeft;
|
|
43375
43692
|
return { sheetId: position.sheetId, col: mergeTopLeftPos.col, row: mergeTopLeftPos.row };
|
|
43376
43693
|
}
|
|
43377
|
-
getBottomLeftCell(position) {
|
|
43378
|
-
if (!this.isInMerge(position)) {
|
|
43379
|
-
return position;
|
|
43380
|
-
}
|
|
43381
|
-
const { bottom, left } = this.getMerge(position);
|
|
43382
|
-
return { sheetId: position.sheetId, col: left, row: bottom };
|
|
43383
|
-
}
|
|
43384
43694
|
isMergeHidden(sheetId, merge) {
|
|
43385
43695
|
const hiddenColsGroups = this.getters.getHiddenColsGroups(sheetId);
|
|
43386
43696
|
const hiddenRowsGroups = this.getters.getHiddenRowsGroups(sheetId);
|
|
@@ -43681,8 +43991,8 @@ class RangeAdapter {
|
|
|
43681
43991
|
let newRange = range;
|
|
43682
43992
|
let changeType = "NONE";
|
|
43683
43993
|
for (let group of groups) {
|
|
43684
|
-
const min =
|
|
43685
|
-
const max =
|
|
43994
|
+
const min = largeMin(group);
|
|
43995
|
+
const max = largeMax(group);
|
|
43686
43996
|
if (range.zone[start] <= min && min <= range.zone[end]) {
|
|
43687
43997
|
const toRemove = Math.min(range.zone[end], max) - min + 1;
|
|
43688
43998
|
changeType = "RESIZE";
|
|
@@ -44064,7 +44374,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44064
44374
|
"getSheetIds",
|
|
44065
44375
|
"getVisibleSheetIds",
|
|
44066
44376
|
"isSheetVisible",
|
|
44067
|
-
"getEvaluationSheets",
|
|
44068
44377
|
"doesHeaderExist",
|
|
44069
44378
|
"doesHeadersExist",
|
|
44070
44379
|
"getCell",
|
|
@@ -44131,8 +44440,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
44131
44440
|
}
|
|
44132
44441
|
return "Success" /* CommandResult.Success */;
|
|
44133
44442
|
case "REMOVE_COLUMNS_ROWS": {
|
|
44134
|
-
const min =
|
|
44135
|
-
const max =
|
|
44443
|
+
const min = largeMin(cmd.elements);
|
|
44444
|
+
const max = largeMax(cmd.elements);
|
|
44136
44445
|
if (min < 0 || !this.doesHeaderExist(cmd.sheetId, cmd.dimension, max)) {
|
|
44137
44446
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
44138
44447
|
}
|
|
@@ -44323,9 +44632,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44323
44632
|
getVisibleSheetIds() {
|
|
44324
44633
|
return this.orderedSheetIds.filter(this.isSheetVisible.bind(this));
|
|
44325
44634
|
}
|
|
44326
|
-
getEvaluationSheets() {
|
|
44327
|
-
return this.sheets;
|
|
44328
|
-
}
|
|
44329
44635
|
doesHeaderExist(sheetId, dimension, index) {
|
|
44330
44636
|
return dimension === "COL"
|
|
44331
44637
|
? index >= 0 && index < this.getNumberCols(sheetId)
|
|
@@ -44334,13 +44640,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44334
44640
|
doesHeadersExist(sheetId, dimension, headerIndexes) {
|
|
44335
44641
|
return headerIndexes.every((index) => this.doesHeaderExist(sheetId, dimension, index));
|
|
44336
44642
|
}
|
|
44337
|
-
getRow(sheetId, index) {
|
|
44338
|
-
const row = this.getSheet(sheetId).rows[index];
|
|
44339
|
-
if (!row) {
|
|
44340
|
-
throw new Error(`Row ${row} not found.`);
|
|
44341
|
-
}
|
|
44342
|
-
return row;
|
|
44343
|
-
}
|
|
44344
44643
|
getCell({ sheetId, col, row }) {
|
|
44345
44644
|
const sheet = this.tryGetSheet(sheetId);
|
|
44346
44645
|
const cellId = sheet?.rows[row]?.cells[col];
|
|
@@ -45909,12 +46208,6 @@ class CompilationParametersBuilder {
|
|
|
45909
46208
|
: _t("Function %s expects its parameters to be single values or single cell references, not ranges.", functionName.toString()));
|
|
45910
46209
|
}
|
|
45911
46210
|
const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
|
|
45912
|
-
return this.readCell(position);
|
|
45913
|
-
}
|
|
45914
|
-
readCell(position) {
|
|
45915
|
-
if (!this.getters.tryGetSheet(position.sheetId)) {
|
|
45916
|
-
throw new EvaluationError(_t("Invalid sheet name"));
|
|
45917
|
-
}
|
|
45918
46211
|
return this.computeCell(position);
|
|
45919
46212
|
}
|
|
45920
46213
|
/**
|
|
@@ -45950,7 +46243,7 @@ class CompilationParametersBuilder {
|
|
|
45950
46243
|
matrix[colIndex] = new Array(height);
|
|
45951
46244
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
45952
46245
|
const rowIndex = row - _zone.top;
|
|
45953
|
-
matrix[colIndex][rowIndex] = this.
|
|
46246
|
+
matrix[colIndex][rowIndex] = this.computeCell({ sheetId, col, row });
|
|
45954
46247
|
}
|
|
45955
46248
|
}
|
|
45956
46249
|
this.rangeCache[cacheKey] = matrix;
|
|
@@ -47058,15 +47351,15 @@ class Evaluator {
|
|
|
47058
47351
|
getEvaluatedCell(position) {
|
|
47059
47352
|
return this.evaluatedCells.get(position) || EMPTY_CELL;
|
|
47060
47353
|
}
|
|
47061
|
-
|
|
47354
|
+
getSpreadZone(position) {
|
|
47062
47355
|
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
47063
|
-
return
|
|
47356
|
+
return undefined;
|
|
47064
47357
|
}
|
|
47065
47358
|
if (this.evaluatedCells.get(position)?.type === CellValueType.error) {
|
|
47066
|
-
return
|
|
47359
|
+
return positionToZone(position);
|
|
47067
47360
|
}
|
|
47068
47361
|
const spreadPositions = Array.from(this.spreadingRelations.getArrayResultPositions(position));
|
|
47069
|
-
return
|
|
47362
|
+
return union(positionToZone(position), unionPositionsToZone(spreadPositions));
|
|
47070
47363
|
}
|
|
47071
47364
|
getEvaluatedPositions() {
|
|
47072
47365
|
return this.evaluatedCells.keys();
|
|
@@ -47471,7 +47764,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47471
47764
|
"getEvaluatedCell",
|
|
47472
47765
|
"getEvaluatedCells",
|
|
47473
47766
|
"getEvaluatedCellsInZone",
|
|
47474
|
-
"
|
|
47767
|
+
"getSpreadZone",
|
|
47475
47768
|
"getArrayFormulaSpreadingOn",
|
|
47476
47769
|
"isEmpty",
|
|
47477
47770
|
];
|
|
@@ -47577,8 +47870,11 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47577
47870
|
getEvaluatedCellsInZone(sheetId, zone) {
|
|
47578
47871
|
return positions(zone).map(({ col, row }) => this.getters.getEvaluatedCell({ sheetId, col, row }));
|
|
47579
47872
|
}
|
|
47580
|
-
|
|
47581
|
-
|
|
47873
|
+
/**
|
|
47874
|
+
* Return the spread zone the position is part of, if any
|
|
47875
|
+
*/
|
|
47876
|
+
getSpreadZone(position) {
|
|
47877
|
+
return this.evaluator.getSpreadZone(position);
|
|
47582
47878
|
}
|
|
47583
47879
|
getArrayFormulaSpreadingOn(position) {
|
|
47584
47880
|
return this.evaluator.getArrayFormulaSpreadingOn(position);
|
|
@@ -47618,7 +47914,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47618
47914
|
? getItemId(newFormat, data.formats)
|
|
47619
47915
|
: exportedCellData.format;
|
|
47620
47916
|
let content;
|
|
47621
|
-
if (formulaCell instanceof FormulaCellWithDependencies) {
|
|
47917
|
+
if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
|
|
47622
47918
|
content = formulaCell.contentWithFixedReferences;
|
|
47623
47919
|
}
|
|
47624
47920
|
else {
|
|
@@ -47666,17 +47962,17 @@ function isBadExpression(tokens) {
|
|
|
47666
47962
|
*/
|
|
47667
47963
|
function sortWithClusters(colorsToSort) {
|
|
47668
47964
|
const clusters = [
|
|
47669
|
-
{ leadColor: rgba(255, 0, 0), colors: [] },
|
|
47670
|
-
{ leadColor: rgba(255, 128, 0), colors: [] },
|
|
47671
|
-
{ leadColor: rgba(128, 128, 0), colors: [] },
|
|
47672
|
-
{ leadColor: rgba(128, 255, 0), colors: [] },
|
|
47673
|
-
{ leadColor: rgba(0, 255, 0), colors: [] },
|
|
47674
|
-
{ leadColor: rgba(0, 255, 128), colors: [] },
|
|
47675
|
-
{ leadColor: rgba(0, 255, 255), colors: [] },
|
|
47676
|
-
{ leadColor: rgba(0, 127, 255), colors: [] },
|
|
47677
|
-
{ leadColor: rgba(0, 0, 255), colors: [] },
|
|
47678
|
-
{ leadColor: rgba(127, 0, 255), colors: [] },
|
|
47679
|
-
{ leadColor: rgba(128, 0, 128), colors: [] },
|
|
47965
|
+
{ leadColor: rgba(255, 0, 0), colors: [] }, // red
|
|
47966
|
+
{ leadColor: rgba(255, 128, 0), colors: [] }, // orange
|
|
47967
|
+
{ leadColor: rgba(128, 128, 0), colors: [] }, // yellow
|
|
47968
|
+
{ leadColor: rgba(128, 255, 0), colors: [] }, // chartreuse
|
|
47969
|
+
{ leadColor: rgba(0, 255, 0), colors: [] }, // green
|
|
47970
|
+
{ leadColor: rgba(0, 255, 128), colors: [] }, // spring green
|
|
47971
|
+
{ leadColor: rgba(0, 255, 255), colors: [] }, // cyan
|
|
47972
|
+
{ leadColor: rgba(0, 127, 255), colors: [] }, // azure
|
|
47973
|
+
{ leadColor: rgba(0, 0, 255), colors: [] }, // blue
|
|
47974
|
+
{ leadColor: rgba(127, 0, 255), colors: [] }, // violet
|
|
47975
|
+
{ leadColor: rgba(128, 0, 128), colors: [] }, // magenta
|
|
47680
47976
|
{ leadColor: rgba(255, 0, 128), colors: [] }, // rose
|
|
47681
47977
|
];
|
|
47682
47978
|
for (const color of colorsToSort.map(colorToRGBA)) {
|
|
@@ -48067,13 +48363,13 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
48067
48363
|
.map((cell) => cell.value);
|
|
48068
48364
|
switch (threshold.type) {
|
|
48069
48365
|
case "value":
|
|
48070
|
-
const result = functionName === "max" ?
|
|
48366
|
+
const result = functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
|
|
48071
48367
|
return result;
|
|
48072
48368
|
case "number":
|
|
48073
48369
|
return Number(threshold.value);
|
|
48074
48370
|
case "percentage":
|
|
48075
|
-
const min =
|
|
48076
|
-
const max =
|
|
48371
|
+
const min = largeMin(rangeValues);
|
|
48372
|
+
const max = largeMax(rangeValues);
|
|
48077
48373
|
const delta = max - min;
|
|
48078
48374
|
return min + (delta * Number(threshold.value)) / 100;
|
|
48079
48375
|
case "percentile":
|
|
@@ -48540,8 +48836,8 @@ class DynamicTablesPlugin extends UIPlugin {
|
|
|
48540
48836
|
else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
|
|
48541
48837
|
return true;
|
|
48542
48838
|
}
|
|
48543
|
-
const
|
|
48544
|
-
return deepEquals(unionZone,
|
|
48839
|
+
const zone = this.getters.getSpreadZone(parentSpreadingCell);
|
|
48840
|
+
return deepEquals(unionZone, zone);
|
|
48545
48841
|
}
|
|
48546
48842
|
coreTableToTable(sheetId, table) {
|
|
48547
48843
|
if (table.type !== "dynamic") {
|
|
@@ -48549,8 +48845,7 @@ class DynamicTablesPlugin extends UIPlugin {
|
|
|
48549
48845
|
}
|
|
48550
48846
|
const tableZone = table.range.zone;
|
|
48551
48847
|
const tablePosition = { sheetId, col: tableZone.left, row: tableZone.top };
|
|
48552
|
-
const
|
|
48553
|
-
const zone = spreadPositions.length ? unionPositionsToZone(spreadPositions) : table.range.zone;
|
|
48848
|
+
const zone = this.getters.getSpreadZone(tablePosition) ?? table.range.zone;
|
|
48554
48849
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
48555
48850
|
const filters = this.getDynamicTableFilters(sheetId, table, zone);
|
|
48556
48851
|
return { id: table.id, range, filters, config: table.config };
|
|
@@ -49000,8 +49295,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
49000
49295
|
let row = zone.bottom;
|
|
49001
49296
|
if (col > 0) {
|
|
49002
49297
|
let leftPosition = { sheetId, col: col - 1, row };
|
|
49003
|
-
while (this.getters.
|
|
49004
|
-
this.getters.getCell(leftPosition)?.content) {
|
|
49298
|
+
while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty) {
|
|
49005
49299
|
row += 1;
|
|
49006
49300
|
leftPosition = { sheetId, col: col - 1, row };
|
|
49007
49301
|
}
|
|
@@ -49010,8 +49304,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
49010
49304
|
col = zone.right;
|
|
49011
49305
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
49012
49306
|
let rightPosition = { sheetId, col: col + 1, row };
|
|
49013
|
-
while (this.getters.
|
|
49014
|
-
this.getters.getCell(rightPosition)?.content) {
|
|
49307
|
+
while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty) {
|
|
49015
49308
|
row += 1;
|
|
49016
49309
|
rightPosition = { sheetId, col: col + 1, row };
|
|
49017
49310
|
}
|
|
@@ -49321,13 +49614,13 @@ class AutomaticSumPlugin extends UIPlugin {
|
|
|
49321
49614
|
const cells = this.getters.getEvaluatedCellsInZone(sheet.id, zone);
|
|
49322
49615
|
const cellPositions = range(end, -1, -1);
|
|
49323
49616
|
const invalidCells = cellPositions.filter((position) => cells[position] && !cells[position].isAutoSummable);
|
|
49324
|
-
const maxValidPosition =
|
|
49617
|
+
const maxValidPosition = largeMax(invalidCells);
|
|
49325
49618
|
const numberSequences = groupConsecutive(cellPositions.filter((position) => this.isNumber(cells[position])));
|
|
49326
49619
|
const firstSequence = numberSequences[0] || [];
|
|
49327
|
-
if (
|
|
49620
|
+
if (largeMax(firstSequence) < maxValidPosition) {
|
|
49328
49621
|
return Infinity;
|
|
49329
49622
|
}
|
|
49330
|
-
return
|
|
49623
|
+
return largeMin(firstSequence);
|
|
49331
49624
|
}
|
|
49332
49625
|
shouldFindData(sheetId, zone) {
|
|
49333
49626
|
return this.getters.isEmpty(sheetId, zone) || this.getters.isSingleCellOrMerge(sheetId, zone);
|
|
@@ -50902,8 +51195,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50902
51195
|
static getters = [
|
|
50903
51196
|
"doesCellHaveGridIcon",
|
|
50904
51197
|
"getCellWidth",
|
|
50905
|
-
"getCellComputedBorder",
|
|
50906
|
-
"getCellComputedStyle",
|
|
50907
51198
|
"getTextWidth",
|
|
50908
51199
|
"getCellText",
|
|
50909
51200
|
"getCellMultiLineText",
|
|
@@ -50947,7 +51238,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50947
51238
|
// Getters
|
|
50948
51239
|
// ---------------------------------------------------------------------------
|
|
50949
51240
|
getCellWidth(position) {
|
|
50950
|
-
const style = this.getCellComputedStyle(position);
|
|
51241
|
+
const style = this.getters.getCellComputedStyle(position);
|
|
50951
51242
|
let contentWidth = 0;
|
|
50952
51243
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
50953
51244
|
if (content) {
|
|
@@ -51040,35 +51331,12 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
51040
51331
|
*/
|
|
51041
51332
|
isCellEmpty(position) {
|
|
51042
51333
|
const mainPosition = this.getters.getMainCellPosition(position);
|
|
51043
|
-
return
|
|
51044
|
-
this.getters.getCell(mainPosition)?.content);
|
|
51045
|
-
}
|
|
51046
|
-
getCellComputedBorder(position) {
|
|
51047
|
-
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
51048
|
-
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
51049
|
-
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
51050
|
-
const border = { ...cellTableBorder, ...removeFalsyAttributes(cellBorder) };
|
|
51051
|
-
return isObjectEmptyRecursive(border) ? null : border;
|
|
51052
|
-
}
|
|
51053
|
-
getCellComputedStyle(position) {
|
|
51054
|
-
const cell = this.getters.getCell(position);
|
|
51055
|
-
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
51056
|
-
const tableStyle = this.getters.getCellTableStyle(position);
|
|
51057
|
-
const computedStyle = {
|
|
51058
|
-
...removeFalsyAttributes(tableStyle),
|
|
51059
|
-
...removeFalsyAttributes(cell?.style),
|
|
51060
|
-
...removeFalsyAttributes(cfStyle),
|
|
51061
|
-
};
|
|
51062
|
-
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
51063
|
-
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
51064
|
-
computedStyle.textColor = LINK_COLOR;
|
|
51065
|
-
}
|
|
51066
|
-
return computedStyle;
|
|
51334
|
+
return this.getters.getEvaluatedCell(mainPosition).type === CellValueType.empty;
|
|
51067
51335
|
}
|
|
51068
51336
|
getColMaxWidth(sheetId, index) {
|
|
51069
51337
|
const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
|
|
51070
51338
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
51071
|
-
return Math.max(0,
|
|
51339
|
+
return Math.max(0, largeMax(sizes));
|
|
51072
51340
|
}
|
|
51073
51341
|
/**
|
|
51074
51342
|
* Check that any "sheetId" in the command matches an existing
|
|
@@ -51097,6 +51365,236 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
51097
51365
|
}
|
|
51098
51366
|
}
|
|
51099
51367
|
|
|
51368
|
+
class TableStylePlugin extends UIPlugin {
|
|
51369
|
+
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51370
|
+
tableStyles = {};
|
|
51371
|
+
handle(cmd) {
|
|
51372
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51373
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51374
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
51375
|
+
this.tableStyles = {};
|
|
51376
|
+
return;
|
|
51377
|
+
}
|
|
51378
|
+
if (doesCommandInvalidatesTableStyle(cmd)) {
|
|
51379
|
+
delete this.tableStyles[cmd.sheetId];
|
|
51380
|
+
return;
|
|
51381
|
+
}
|
|
51382
|
+
}
|
|
51383
|
+
finalize() {
|
|
51384
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
51385
|
+
if (!this.tableStyles[sheetId]) {
|
|
51386
|
+
this.tableStyles[sheetId] = {};
|
|
51387
|
+
}
|
|
51388
|
+
for (const table of this.getters.getTables(sheetId)) {
|
|
51389
|
+
if (!this.tableStyles[sheetId][table.id]) {
|
|
51390
|
+
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51391
|
+
}
|
|
51392
|
+
}
|
|
51393
|
+
}
|
|
51394
|
+
}
|
|
51395
|
+
getCellTableStyle(position) {
|
|
51396
|
+
const table = this.getters.getTable(position);
|
|
51397
|
+
if (!table) {
|
|
51398
|
+
return undefined;
|
|
51399
|
+
}
|
|
51400
|
+
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51401
|
+
}
|
|
51402
|
+
getCellTableBorder(position) {
|
|
51403
|
+
const table = this.getters.getTable(position);
|
|
51404
|
+
if (!table) {
|
|
51405
|
+
return undefined;
|
|
51406
|
+
}
|
|
51407
|
+
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51408
|
+
}
|
|
51409
|
+
computeTableStyle(sheetId, table) {
|
|
51410
|
+
return lazy(() => {
|
|
51411
|
+
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51412
|
+
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51413
|
+
// Return the style with sheet coordinates instead of tables coordinates
|
|
51414
|
+
const mapping = this.getTableMapping(sheetId, table);
|
|
51415
|
+
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51416
|
+
for (let col = 0; col < numberOfCols; col++) {
|
|
51417
|
+
const colInSheet = mapping.colMapping[col];
|
|
51418
|
+
absoluteTableStyle.borders[colInSheet] = {};
|
|
51419
|
+
absoluteTableStyle.styles[colInSheet] = {};
|
|
51420
|
+
for (let row = 0; row < numberOfRows; row++) {
|
|
51421
|
+
const rowInSheet = mapping.rowMapping[row];
|
|
51422
|
+
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51423
|
+
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51424
|
+
}
|
|
51425
|
+
}
|
|
51426
|
+
return absoluteTableStyle;
|
|
51427
|
+
});
|
|
51428
|
+
}
|
|
51429
|
+
/**
|
|
51430
|
+
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51431
|
+
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51432
|
+
* hidden rows from config.numberOfHeaders.
|
|
51433
|
+
*/
|
|
51434
|
+
getTableRuntimeConfig(sheetId, table) {
|
|
51435
|
+
const tableZone = table.range.zone;
|
|
51436
|
+
const config = { ...table.config };
|
|
51437
|
+
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51438
|
+
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51439
|
+
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51440
|
+
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51441
|
+
continue;
|
|
51442
|
+
}
|
|
51443
|
+
numberOfRows--;
|
|
51444
|
+
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51445
|
+
config.numberOfHeaders--;
|
|
51446
|
+
if (config.numberOfHeaders < 0) {
|
|
51447
|
+
config.numberOfHeaders = 0;
|
|
51448
|
+
}
|
|
51449
|
+
}
|
|
51450
|
+
if (row === tableZone.bottom) {
|
|
51451
|
+
config.totalRow = false;
|
|
51452
|
+
}
|
|
51453
|
+
}
|
|
51454
|
+
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51455
|
+
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51456
|
+
continue;
|
|
51457
|
+
}
|
|
51458
|
+
numberOfCols--;
|
|
51459
|
+
if (col === tableZone.left) {
|
|
51460
|
+
config.firstColumn = false;
|
|
51461
|
+
}
|
|
51462
|
+
if (col === tableZone.right) {
|
|
51463
|
+
config.lastColumn = false;
|
|
51464
|
+
}
|
|
51465
|
+
}
|
|
51466
|
+
return {
|
|
51467
|
+
config,
|
|
51468
|
+
numberOfCols,
|
|
51469
|
+
numberOfRows,
|
|
51470
|
+
};
|
|
51471
|
+
}
|
|
51472
|
+
/**
|
|
51473
|
+
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51474
|
+
*/
|
|
51475
|
+
getTableMapping(sheetId, table) {
|
|
51476
|
+
const colMapping = {};
|
|
51477
|
+
const rowMapping = {};
|
|
51478
|
+
let colOffset = 0;
|
|
51479
|
+
let rowOffset = 0;
|
|
51480
|
+
const tableZone = table.range.zone;
|
|
51481
|
+
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51482
|
+
if (this.getters.isColHidden(sheetId, col)) {
|
|
51483
|
+
continue;
|
|
51484
|
+
}
|
|
51485
|
+
colMapping[colOffset] = col;
|
|
51486
|
+
colOffset++;
|
|
51487
|
+
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51488
|
+
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51489
|
+
continue;
|
|
51490
|
+
}
|
|
51491
|
+
rowMapping[rowOffset] = row;
|
|
51492
|
+
rowOffset++;
|
|
51493
|
+
}
|
|
51494
|
+
}
|
|
51495
|
+
return {
|
|
51496
|
+
colMapping,
|
|
51497
|
+
rowMapping,
|
|
51498
|
+
};
|
|
51499
|
+
}
|
|
51500
|
+
}
|
|
51501
|
+
const invalidateTableStyleCommands = [
|
|
51502
|
+
"HIDE_COLUMNS_ROWS",
|
|
51503
|
+
"UNHIDE_COLUMNS_ROWS",
|
|
51504
|
+
"UNFOLD_HEADER_GROUP",
|
|
51505
|
+
"FOLD_HEADER_GROUP",
|
|
51506
|
+
"FOLD_ALL_HEADER_GROUPS",
|
|
51507
|
+
"UNFOLD_ALL_HEADER_GROUPS",
|
|
51508
|
+
"CREATE_TABLE",
|
|
51509
|
+
"UPDATE_TABLE",
|
|
51510
|
+
"UPDATE_FILTER",
|
|
51511
|
+
];
|
|
51512
|
+
const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
|
|
51513
|
+
function doesCommandInvalidatesTableStyle(cmd) {
|
|
51514
|
+
return invalidateTableStyleCommandsSet.has(cmd.type);
|
|
51515
|
+
}
|
|
51516
|
+
|
|
51517
|
+
class CellComputedStylePlugin extends UIPlugin {
|
|
51518
|
+
static getters = ["getCellComputedBorder", "getCellComputedStyle"];
|
|
51519
|
+
styles = {};
|
|
51520
|
+
borders = {};
|
|
51521
|
+
handle(cmd) {
|
|
51522
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51523
|
+
cmd.type === "UPDATE_CELL" ||
|
|
51524
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
51525
|
+
this.styles = {};
|
|
51526
|
+
this.borders = {};
|
|
51527
|
+
return;
|
|
51528
|
+
}
|
|
51529
|
+
if (doesCommandInvalidatesTableStyle(cmd)) {
|
|
51530
|
+
delete this.styles[cmd.sheetId];
|
|
51531
|
+
delete this.borders[cmd.sheetId];
|
|
51532
|
+
return;
|
|
51533
|
+
}
|
|
51534
|
+
if (invalidateCFEvaluationCommands.has(cmd.type)) {
|
|
51535
|
+
this.styles = {};
|
|
51536
|
+
return;
|
|
51537
|
+
}
|
|
51538
|
+
}
|
|
51539
|
+
getCellComputedBorder(position) {
|
|
51540
|
+
const { sheetId, row, col } = position;
|
|
51541
|
+
if (this.borders[sheetId]?.[row]?.[col] !== undefined) {
|
|
51542
|
+
return this.borders[sheetId][row][col];
|
|
51543
|
+
}
|
|
51544
|
+
if (!this.borders[sheetId]) {
|
|
51545
|
+
this.borders[sheetId] = {};
|
|
51546
|
+
}
|
|
51547
|
+
if (!this.borders[sheetId][row]) {
|
|
51548
|
+
this.borders[sheetId][row] = {};
|
|
51549
|
+
}
|
|
51550
|
+
if (!this.borders[sheetId][row][col]) {
|
|
51551
|
+
this.borders[sheetId][row][col] = this.computeCellBorder(position);
|
|
51552
|
+
}
|
|
51553
|
+
return this.borders[sheetId][row][col];
|
|
51554
|
+
}
|
|
51555
|
+
getCellComputedStyle(position) {
|
|
51556
|
+
const { sheetId, row, col } = position;
|
|
51557
|
+
if (this.styles[sheetId]?.[row]?.[col] !== undefined) {
|
|
51558
|
+
return this.styles[sheetId][row][col];
|
|
51559
|
+
}
|
|
51560
|
+
if (!this.styles[sheetId]) {
|
|
51561
|
+
this.styles[sheetId] = {};
|
|
51562
|
+
}
|
|
51563
|
+
if (!this.styles[sheetId][row]) {
|
|
51564
|
+
this.styles[sheetId][row] = {};
|
|
51565
|
+
}
|
|
51566
|
+
if (!this.styles[sheetId][row][col]) {
|
|
51567
|
+
this.styles[sheetId][row][col] = this.computeCellStyle(position);
|
|
51568
|
+
}
|
|
51569
|
+
return this.styles[sheetId][row][col];
|
|
51570
|
+
}
|
|
51571
|
+
computeCellBorder(position) {
|
|
51572
|
+
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
51573
|
+
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
51574
|
+
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
51575
|
+
const border = {
|
|
51576
|
+
...removeFalsyAttributes(cellTableBorder),
|
|
51577
|
+
...removeFalsyAttributes(cellBorder),
|
|
51578
|
+
};
|
|
51579
|
+
return isObjectEmptyRecursive(border) ? null : border;
|
|
51580
|
+
}
|
|
51581
|
+
computeCellStyle(position) {
|
|
51582
|
+
const cell = this.getters.getCell(position);
|
|
51583
|
+
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
51584
|
+
const tableStyle = this.getters.getCellTableStyle(position);
|
|
51585
|
+
const computedStyle = {
|
|
51586
|
+
...removeFalsyAttributes(tableStyle),
|
|
51587
|
+
...removeFalsyAttributes(cell?.style),
|
|
51588
|
+
...removeFalsyAttributes(cfStyle),
|
|
51589
|
+
};
|
|
51590
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
51591
|
+
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
51592
|
+
computedStyle.textColor = LINK_COLOR;
|
|
51593
|
+
}
|
|
51594
|
+
return computedStyle;
|
|
51595
|
+
}
|
|
51596
|
+
}
|
|
51597
|
+
|
|
51100
51598
|
const genericRepeatsTransforms = [
|
|
51101
51599
|
repeatSheetDependantCommand,
|
|
51102
51600
|
repeatTargetDependantCommand,
|
|
@@ -51674,148 +52172,6 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
51674
52172
|
}
|
|
51675
52173
|
}
|
|
51676
52174
|
|
|
51677
|
-
class TableStylePlugin extends UIPlugin {
|
|
51678
|
-
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51679
|
-
tableStyles = {};
|
|
51680
|
-
handle(cmd) {
|
|
51681
|
-
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51682
|
-
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51683
|
-
cmd.type === "EVALUATE_CELLS") {
|
|
51684
|
-
this.tableStyles = {};
|
|
51685
|
-
return;
|
|
51686
|
-
}
|
|
51687
|
-
switch (cmd.type) {
|
|
51688
|
-
case "HIDE_COLUMNS_ROWS":
|
|
51689
|
-
case "UNHIDE_COLUMNS_ROWS":
|
|
51690
|
-
case "UNFOLD_HEADER_GROUP":
|
|
51691
|
-
case "FOLD_HEADER_GROUP":
|
|
51692
|
-
case "FOLD_ALL_HEADER_GROUPS":
|
|
51693
|
-
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
51694
|
-
case "UPDATE_TABLE":
|
|
51695
|
-
case "UPDATE_FILTER":
|
|
51696
|
-
delete this.tableStyles[cmd.sheetId];
|
|
51697
|
-
break;
|
|
51698
|
-
}
|
|
51699
|
-
}
|
|
51700
|
-
finalize() {
|
|
51701
|
-
for (const sheetId of this.getters.getSheetIds()) {
|
|
51702
|
-
if (!this.tableStyles[sheetId]) {
|
|
51703
|
-
this.tableStyles[sheetId] = {};
|
|
51704
|
-
}
|
|
51705
|
-
for (const table of this.getters.getTables(sheetId)) {
|
|
51706
|
-
if (!this.tableStyles[sheetId][table.id]) {
|
|
51707
|
-
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51708
|
-
}
|
|
51709
|
-
}
|
|
51710
|
-
}
|
|
51711
|
-
}
|
|
51712
|
-
getCellTableStyle(position) {
|
|
51713
|
-
const table = this.getters.getTable(position);
|
|
51714
|
-
if (!table) {
|
|
51715
|
-
return undefined;
|
|
51716
|
-
}
|
|
51717
|
-
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51718
|
-
}
|
|
51719
|
-
getCellTableBorder(position) {
|
|
51720
|
-
const table = this.getters.getTable(position);
|
|
51721
|
-
if (!table) {
|
|
51722
|
-
return undefined;
|
|
51723
|
-
}
|
|
51724
|
-
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51725
|
-
}
|
|
51726
|
-
computeTableStyle(sheetId, table) {
|
|
51727
|
-
return lazy(() => {
|
|
51728
|
-
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51729
|
-
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51730
|
-
// Return the style with sheet coordinates instead of tables coordinates
|
|
51731
|
-
const mapping = this.getTableMapping(sheetId, table);
|
|
51732
|
-
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51733
|
-
for (let col = 0; col < numberOfCols; col++) {
|
|
51734
|
-
const colInSheet = mapping.colMapping[col];
|
|
51735
|
-
absoluteTableStyle.borders[colInSheet] = {};
|
|
51736
|
-
absoluteTableStyle.styles[colInSheet] = {};
|
|
51737
|
-
for (let row = 0; row < numberOfRows; row++) {
|
|
51738
|
-
const rowInSheet = mapping.rowMapping[row];
|
|
51739
|
-
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51740
|
-
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51741
|
-
}
|
|
51742
|
-
}
|
|
51743
|
-
return absoluteTableStyle;
|
|
51744
|
-
});
|
|
51745
|
-
}
|
|
51746
|
-
/**
|
|
51747
|
-
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51748
|
-
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51749
|
-
* hidden rows from config.numberOfHeaders.
|
|
51750
|
-
*/
|
|
51751
|
-
getTableRuntimeConfig(sheetId, table) {
|
|
51752
|
-
const tableZone = table.range.zone;
|
|
51753
|
-
const config = { ...table.config };
|
|
51754
|
-
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51755
|
-
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51756
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51757
|
-
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51758
|
-
continue;
|
|
51759
|
-
}
|
|
51760
|
-
numberOfRows--;
|
|
51761
|
-
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51762
|
-
config.numberOfHeaders--;
|
|
51763
|
-
if (config.numberOfHeaders < 0) {
|
|
51764
|
-
config.numberOfHeaders = 0;
|
|
51765
|
-
}
|
|
51766
|
-
}
|
|
51767
|
-
if (row === tableZone.bottom) {
|
|
51768
|
-
config.totalRow = false;
|
|
51769
|
-
}
|
|
51770
|
-
}
|
|
51771
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51772
|
-
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51773
|
-
continue;
|
|
51774
|
-
}
|
|
51775
|
-
numberOfCols--;
|
|
51776
|
-
if (col === tableZone.left) {
|
|
51777
|
-
config.firstColumn = false;
|
|
51778
|
-
}
|
|
51779
|
-
if (col === tableZone.right) {
|
|
51780
|
-
config.lastColumn = false;
|
|
51781
|
-
}
|
|
51782
|
-
}
|
|
51783
|
-
return {
|
|
51784
|
-
config,
|
|
51785
|
-
numberOfCols,
|
|
51786
|
-
numberOfRows,
|
|
51787
|
-
};
|
|
51788
|
-
}
|
|
51789
|
-
/**
|
|
51790
|
-
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51791
|
-
*/
|
|
51792
|
-
getTableMapping(sheetId, table) {
|
|
51793
|
-
const colMapping = {};
|
|
51794
|
-
const rowMapping = {};
|
|
51795
|
-
let colOffset = 0;
|
|
51796
|
-
let rowOffset = 0;
|
|
51797
|
-
const tableZone = table.range.zone;
|
|
51798
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51799
|
-
if (this.getters.isColHidden(sheetId, col)) {
|
|
51800
|
-
continue;
|
|
51801
|
-
}
|
|
51802
|
-
colMapping[colOffset] = col;
|
|
51803
|
-
colOffset++;
|
|
51804
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51805
|
-
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51806
|
-
continue;
|
|
51807
|
-
}
|
|
51808
|
-
rowMapping[rowOffset] = row;
|
|
51809
|
-
rowOffset++;
|
|
51810
|
-
}
|
|
51811
|
-
}
|
|
51812
|
-
return {
|
|
51813
|
-
colMapping,
|
|
51814
|
-
rowMapping,
|
|
51815
|
-
};
|
|
51816
|
-
}
|
|
51817
|
-
}
|
|
51818
|
-
|
|
51819
52175
|
/**
|
|
51820
52176
|
* Clipboard Plugin
|
|
51821
52177
|
*
|
|
@@ -52525,38 +52881,6 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52525
52881
|
}
|
|
52526
52882
|
}
|
|
52527
52883
|
|
|
52528
|
-
const selectionStatisticFunctions = [
|
|
52529
|
-
{
|
|
52530
|
-
name: _t("Sum"),
|
|
52531
|
-
types: [CellValueType.number],
|
|
52532
|
-
compute: (values, locale) => sum([[values]], locale),
|
|
52533
|
-
},
|
|
52534
|
-
{
|
|
52535
|
-
name: _t("Avg"),
|
|
52536
|
-
types: [CellValueType.number],
|
|
52537
|
-
compute: (values, locale) => average([[values]], locale),
|
|
52538
|
-
},
|
|
52539
|
-
{
|
|
52540
|
-
name: _t("Min"),
|
|
52541
|
-
types: [CellValueType.number],
|
|
52542
|
-
compute: (values, locale) => min([[values]], locale),
|
|
52543
|
-
},
|
|
52544
|
-
{
|
|
52545
|
-
name: _t("Max"),
|
|
52546
|
-
types: [CellValueType.number],
|
|
52547
|
-
compute: (values, locale) => max([[values]], locale),
|
|
52548
|
-
},
|
|
52549
|
-
{
|
|
52550
|
-
name: _t("Count"),
|
|
52551
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52552
|
-
compute: (values) => countAny([[values]]),
|
|
52553
|
-
},
|
|
52554
|
-
{
|
|
52555
|
-
name: _t("Count Numbers"),
|
|
52556
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52557
|
-
compute: (values, locale) => countNumbers([[values]], locale),
|
|
52558
|
-
},
|
|
52559
|
-
];
|
|
52560
52884
|
/**
|
|
52561
52885
|
* SelectionPlugin
|
|
52562
52886
|
*/
|
|
@@ -52572,8 +52896,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52572
52896
|
"getSelectedZones",
|
|
52573
52897
|
"getSelectedZone",
|
|
52574
52898
|
"getSelectedCells",
|
|
52575
|
-
"getStatisticFnResults",
|
|
52576
|
-
"getAggregate",
|
|
52577
52899
|
"getSelectedFigureId",
|
|
52578
52900
|
"getSelection",
|
|
52579
52901
|
"getActivePosition",
|
|
@@ -52608,7 +52930,10 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52608
52930
|
switch (cmd.type) {
|
|
52609
52931
|
case "ACTIVATE_SHEET":
|
|
52610
52932
|
try {
|
|
52611
|
-
this.getters.getSheet(cmd.sheetIdTo);
|
|
52933
|
+
const sheet = this.getters.getSheet(cmd.sheetIdTo);
|
|
52934
|
+
if (!sheet.isVisible) {
|
|
52935
|
+
return "SheetIsHidden" /* CommandResult.SheetIsHidden */;
|
|
52936
|
+
}
|
|
52612
52937
|
break;
|
|
52613
52938
|
}
|
|
52614
52939
|
catch (error) {
|
|
@@ -52857,52 +53182,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52857
53182
|
: this.getters.getNextVisibleCellPosition({ sheetId, col: 0, row: 0 });
|
|
52858
53183
|
}
|
|
52859
53184
|
}
|
|
52860
|
-
getStatisticFnResults() {
|
|
52861
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52862
|
-
const cells = new Set();
|
|
52863
|
-
for (const zone of this.gridSelection.zones) {
|
|
52864
|
-
for (const { col, row } of positions(zone)) {
|
|
52865
|
-
if (this.getters.isRowHidden(sheetId, row) || this.getters.isColHidden(sheetId, col)) {
|
|
52866
|
-
continue; // Skip hidden cells
|
|
52867
|
-
}
|
|
52868
|
-
const evaluatedCell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52869
|
-
if (evaluatedCell.type !== CellValueType.empty) {
|
|
52870
|
-
cells.add(evaluatedCell);
|
|
52871
|
-
}
|
|
52872
|
-
}
|
|
52873
|
-
}
|
|
52874
|
-
const locale = this.getters.getLocale();
|
|
52875
|
-
let statisticFnResults = {};
|
|
52876
|
-
for (let fn of selectionStatisticFunctions) {
|
|
52877
|
-
// We don't want to display statistical information when there is no interest:
|
|
52878
|
-
// We set the statistical result to undefined if the data handled by the selection
|
|
52879
|
-
// does not match the data handled by the function.
|
|
52880
|
-
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
52881
|
-
// be displayed as undefined rather than 0.
|
|
52882
|
-
let fnResult = undefined;
|
|
52883
|
-
const evaluatedCells = [...cells].filter((c) => fn.types.includes(c.type));
|
|
52884
|
-
if (evaluatedCells.length) {
|
|
52885
|
-
fnResult = fn.compute(evaluatedCells, locale);
|
|
52886
|
-
}
|
|
52887
|
-
statisticFnResults[fn.name] = fnResult;
|
|
52888
|
-
}
|
|
52889
|
-
return statisticFnResults;
|
|
52890
|
-
}
|
|
52891
|
-
getAggregate() {
|
|
52892
|
-
let aggregate = 0;
|
|
52893
|
-
let n = 0;
|
|
52894
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52895
|
-
const cellPositions = this.gridSelection.zones.map(positions).flat();
|
|
52896
|
-
for (const { col, row } of cellPositions) {
|
|
52897
|
-
const cell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52898
|
-
if (cell.type === CellValueType.number) {
|
|
52899
|
-
n++;
|
|
52900
|
-
aggregate += cell.value;
|
|
52901
|
-
}
|
|
52902
|
-
}
|
|
52903
|
-
const locale = this.getters.getLocale();
|
|
52904
|
-
return n < 2 ? null : formatValue(aggregate, { locale });
|
|
52905
|
-
}
|
|
52906
53185
|
isSelected(zone) {
|
|
52907
53186
|
return !!this.getters.getSelectedZones().find((z) => isEqual(z, zone));
|
|
52908
53187
|
}
|
|
@@ -52944,9 +53223,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52944
53223
|
// Other
|
|
52945
53224
|
// ---------------------------------------------------------------------------
|
|
52946
53225
|
activateSheet(sheetIdFrom, sheetIdTo) {
|
|
52947
|
-
if (!this.getters.isSheetVisible(sheetIdTo)) {
|
|
52948
|
-
this.dispatch("SHOW_SHEET", { sheetId: sheetIdTo });
|
|
52949
|
-
}
|
|
52950
53226
|
this.setActiveSheet(sheetIdTo);
|
|
52951
53227
|
this.sheetsData[sheetIdFrom] = {
|
|
52952
53228
|
gridSelection: deepCopy(this.gridSelection),
|
|
@@ -53953,7 +54229,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53953
54229
|
* column of the current viewport
|
|
53954
54230
|
*/
|
|
53955
54231
|
getColDimensionsInViewport(sheetId, col) {
|
|
53956
|
-
const left =
|
|
54232
|
+
const left = largeMin(this.getters.getSheetViewVisibleCols());
|
|
53957
54233
|
const start = this.getters.getColRowOffsetInViewport("COL", left, col);
|
|
53958
54234
|
const size = this.getters.getColSize(sheetId, col);
|
|
53959
54235
|
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
@@ -53968,7 +54244,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53968
54244
|
* of the current viewport
|
|
53969
54245
|
*/
|
|
53970
54246
|
getRowDimensionsInViewport(sheetId, row) {
|
|
53971
|
-
const top =
|
|
54247
|
+
const top = largeMin(this.getters.getSheetViewVisibleRows());
|
|
53972
54248
|
const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
|
|
53973
54249
|
const size = this.getters.getRowSize(sheetId, row);
|
|
53974
54250
|
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
@@ -54320,6 +54596,7 @@ const statefulUIPluginRegistry = new Registry()
|
|
|
54320
54596
|
.add("evaluation_filter", FilterEvaluationPlugin)
|
|
54321
54597
|
.add("header_visibility_ui", HeaderVisibilityUIPlugin)
|
|
54322
54598
|
.add("table_style", TableStylePlugin)
|
|
54599
|
+
.add("cell_computed_style", CellComputedStylePlugin)
|
|
54323
54600
|
.add("header_positions", HeaderPositionsUIPlugin)
|
|
54324
54601
|
.add("viewport", SheetViewPlugin)
|
|
54325
54602
|
.add("clipboard", ClipboardPlugin);
|
|
@@ -54380,6 +54657,38 @@ class ImageProvider {
|
|
|
54380
54657
|
}
|
|
54381
54658
|
}
|
|
54382
54659
|
|
|
54660
|
+
class ArrayFormulaHighlight extends SpreadsheetStore {
|
|
54661
|
+
highlightStore = this.get(HighlightStore);
|
|
54662
|
+
constructor(get) {
|
|
54663
|
+
super(get);
|
|
54664
|
+
this.highlightStore.register(this);
|
|
54665
|
+
}
|
|
54666
|
+
get highlights() {
|
|
54667
|
+
const zone = this.getHighlightZone();
|
|
54668
|
+
if (!zone) {
|
|
54669
|
+
return [];
|
|
54670
|
+
}
|
|
54671
|
+
const sheetId = this.model.getters.getActiveSheetId();
|
|
54672
|
+
return [
|
|
54673
|
+
{
|
|
54674
|
+
sheetId,
|
|
54675
|
+
zone,
|
|
54676
|
+
color: "#17A2B8",
|
|
54677
|
+
noFill: true,
|
|
54678
|
+
thinLine: true,
|
|
54679
|
+
},
|
|
54680
|
+
];
|
|
54681
|
+
}
|
|
54682
|
+
getHighlightZone() {
|
|
54683
|
+
const position = this.model.getters.getActivePosition();
|
|
54684
|
+
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
|
|
54685
|
+
const spreadZone = spreader
|
|
54686
|
+
? this.model.getters.getSpreadZone(spreader)
|
|
54687
|
+
: this.model.getters.getSpreadZone(position);
|
|
54688
|
+
return spreadZone;
|
|
54689
|
+
}
|
|
54690
|
+
}
|
|
54691
|
+
|
|
54383
54692
|
const RIPPLE_KEY_FRAMES = [
|
|
54384
54693
|
{ transform: "scale(0)" },
|
|
54385
54694
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -54682,12 +54991,14 @@ class BottomBarSheet extends Component {
|
|
|
54682
54991
|
this.editionState = "initializing";
|
|
54683
54992
|
}
|
|
54684
54993
|
stopEdition() {
|
|
54685
|
-
|
|
54994
|
+
const input = this.sheetNameRef.el;
|
|
54995
|
+
if (!this.state.isEditing || !input)
|
|
54686
54996
|
return;
|
|
54687
54997
|
this.state.isEditing = false;
|
|
54688
54998
|
this.editionState = "initializing";
|
|
54689
|
-
|
|
54999
|
+
input.blur();
|
|
54690
55000
|
const inputValue = this.getInputContent() || "";
|
|
55001
|
+
input.innerText = inputValue;
|
|
54691
55002
|
interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
|
|
54692
55003
|
}
|
|
54693
55004
|
cancelEdition() {
|
|
@@ -54731,6 +55042,115 @@ class BottomBarSheet extends Component {
|
|
|
54731
55042
|
}
|
|
54732
55043
|
}
|
|
54733
55044
|
|
|
55045
|
+
const selectionStatisticFunctions = [
|
|
55046
|
+
{
|
|
55047
|
+
name: _t("Sum"),
|
|
55048
|
+
types: [CellValueType.number],
|
|
55049
|
+
compute: (values, locale) => sum([[values]], locale),
|
|
55050
|
+
},
|
|
55051
|
+
{
|
|
55052
|
+
name: _t("Avg"),
|
|
55053
|
+
types: [CellValueType.number],
|
|
55054
|
+
compute: (values, locale) => average([[values]], locale),
|
|
55055
|
+
},
|
|
55056
|
+
{
|
|
55057
|
+
name: _t("Min"),
|
|
55058
|
+
types: [CellValueType.number],
|
|
55059
|
+
compute: (values, locale) => min([[values]], locale),
|
|
55060
|
+
},
|
|
55061
|
+
{
|
|
55062
|
+
name: _t("Max"),
|
|
55063
|
+
types: [CellValueType.number],
|
|
55064
|
+
compute: (values, locale) => max([[values]], locale),
|
|
55065
|
+
},
|
|
55066
|
+
{
|
|
55067
|
+
name: _t("Count"),
|
|
55068
|
+
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
55069
|
+
compute: (values) => countAny([[values]]),
|
|
55070
|
+
},
|
|
55071
|
+
{
|
|
55072
|
+
name: _t("Count Numbers"),
|
|
55073
|
+
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
55074
|
+
compute: (values, locale) => countNumbers([[values]], locale),
|
|
55075
|
+
},
|
|
55076
|
+
];
|
|
55077
|
+
class AggregateStatisticsStore extends SpreadsheetStore {
|
|
55078
|
+
statisticFnResults = this._computeStatisticFnResults();
|
|
55079
|
+
isDirty = false;
|
|
55080
|
+
constructor(get) {
|
|
55081
|
+
super(get);
|
|
55082
|
+
this.model.selection.observe(this, {
|
|
55083
|
+
handleEvent: this.handleEvent.bind(this),
|
|
55084
|
+
});
|
|
55085
|
+
}
|
|
55086
|
+
handle(cmd) {
|
|
55087
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
55088
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd)) {
|
|
55089
|
+
this.isDirty = true;
|
|
55090
|
+
}
|
|
55091
|
+
switch (cmd.type) {
|
|
55092
|
+
case "HIDE_COLUMNS_ROWS":
|
|
55093
|
+
case "UNHIDE_COLUMNS_ROWS":
|
|
55094
|
+
case "GROUP_HEADERS":
|
|
55095
|
+
case "UNGROUP_HEADERS":
|
|
55096
|
+
case "ACTIVATE_SHEET":
|
|
55097
|
+
case "ACTIVATE_NEXT_SHEET":
|
|
55098
|
+
case "ACTIVATE_PREVIOUS_SHEET":
|
|
55099
|
+
case "EVALUATE_CELLS":
|
|
55100
|
+
case "UNDO":
|
|
55101
|
+
case "REDO":
|
|
55102
|
+
this.isDirty = true;
|
|
55103
|
+
}
|
|
55104
|
+
}
|
|
55105
|
+
finalize() {
|
|
55106
|
+
if (this.isDirty) {
|
|
55107
|
+
this.isDirty = false;
|
|
55108
|
+
this.statisticFnResults = this._computeStatisticFnResults();
|
|
55109
|
+
}
|
|
55110
|
+
}
|
|
55111
|
+
handleEvent() {
|
|
55112
|
+
if (this.getters.isGridSelectionActive()) {
|
|
55113
|
+
this.statisticFnResults = this._computeStatisticFnResults();
|
|
55114
|
+
}
|
|
55115
|
+
}
|
|
55116
|
+
_computeStatisticFnResults() {
|
|
55117
|
+
const getters = this.getters;
|
|
55118
|
+
const sheetId = getters.getActiveSheetId();
|
|
55119
|
+
const cells = new Set();
|
|
55120
|
+
const zones = getters.getSelectedZones();
|
|
55121
|
+
for (const zone of zones) {
|
|
55122
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
55123
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
55124
|
+
if (getters.isRowHidden(sheetId, row) || getters.isColHidden(sheetId, col)) {
|
|
55125
|
+
continue; // Skip hidden cells
|
|
55126
|
+
}
|
|
55127
|
+
const evaluatedCell = getters.getEvaluatedCell({ sheetId, col, row });
|
|
55128
|
+
if (evaluatedCell.type !== CellValueType.empty) {
|
|
55129
|
+
cells.add(evaluatedCell);
|
|
55130
|
+
}
|
|
55131
|
+
}
|
|
55132
|
+
}
|
|
55133
|
+
}
|
|
55134
|
+
const locale = getters.getLocale();
|
|
55135
|
+
let statisticFnResults = {};
|
|
55136
|
+
const cellsArray = [...cells];
|
|
55137
|
+
for (let fn of selectionStatisticFunctions) {
|
|
55138
|
+
// We don't want to display statistical information when there is no interest:
|
|
55139
|
+
// We set the statistical result to undefined if the data handled by the selection
|
|
55140
|
+
// does not match the data handled by the function.
|
|
55141
|
+
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
55142
|
+
// be displayed as undefined rather than 0.
|
|
55143
|
+
let fnResult = undefined;
|
|
55144
|
+
const evaluatedCells = cellsArray.filter((c) => fn.types.includes(c.type));
|
|
55145
|
+
if (evaluatedCells.length) {
|
|
55146
|
+
fnResult = fn.compute(evaluatedCells, locale);
|
|
55147
|
+
}
|
|
55148
|
+
statisticFnResults[fn.name] = fnResult;
|
|
55149
|
+
}
|
|
55150
|
+
return statisticFnResults;
|
|
55151
|
+
}
|
|
55152
|
+
}
|
|
55153
|
+
|
|
54734
55154
|
// -----------------------------------------------------------------------------
|
|
54735
55155
|
// SpreadSheet
|
|
54736
55156
|
// -----------------------------------------------------------------------------
|
|
@@ -54746,40 +55166,38 @@ css /* scss */ `
|
|
|
54746
55166
|
}
|
|
54747
55167
|
`;
|
|
54748
55168
|
class BottomBarStatistic extends Component {
|
|
54749
|
-
static template = "o-spreadsheet-
|
|
55169
|
+
static template = "o-spreadsheet-BottomBarStatistic";
|
|
54750
55170
|
static props = {
|
|
54751
55171
|
openContextMenu: Function,
|
|
54752
55172
|
closeContextMenu: Function,
|
|
54753
55173
|
};
|
|
54754
55174
|
static components = { Ripple };
|
|
54755
55175
|
selectedStatisticFn = "";
|
|
54756
|
-
|
|
55176
|
+
store;
|
|
54757
55177
|
setup() {
|
|
54758
|
-
this.
|
|
55178
|
+
this.store = useStore(AggregateStatisticsStore);
|
|
54759
55179
|
onWillUpdateProps(() => {
|
|
54760
|
-
|
|
54761
|
-
if (!deepEquals(newStatisticFnResults, this.statisticFnResults)) {
|
|
55180
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54762
55181
|
this.props.closeContextMenu();
|
|
54763
55182
|
}
|
|
54764
|
-
this.statisticFnResults = newStatisticFnResults;
|
|
54765
55183
|
});
|
|
54766
55184
|
}
|
|
54767
55185
|
getSelectedStatistic() {
|
|
54768
55186
|
// don't display button if no function has a result
|
|
54769
|
-
if (Object.values(this.statisticFnResults).every((result) => result === undefined)) {
|
|
55187
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54770
55188
|
return undefined;
|
|
54771
55189
|
}
|
|
54772
55190
|
if (this.selectedStatisticFn === "") {
|
|
54773
|
-
this.selectedStatisticFn = Object.keys(this.statisticFnResults)[0];
|
|
55191
|
+
this.selectedStatisticFn = Object.keys(this.store.statisticFnResults)[0];
|
|
54774
55192
|
}
|
|
54775
|
-
return this.getComposedFnName(this.selectedStatisticFn
|
|
55193
|
+
return this.getComposedFnName(this.selectedStatisticFn);
|
|
54776
55194
|
}
|
|
54777
55195
|
listSelectionStatistics(ev) {
|
|
54778
55196
|
const registry = new MenuItemRegistry();
|
|
54779
55197
|
let i = 0;
|
|
54780
|
-
for (let [fnName
|
|
55198
|
+
for (let [fnName] of Object.entries(this.store.statisticFnResults)) {
|
|
54781
55199
|
registry.add(fnName, {
|
|
54782
|
-
name: this.getComposedFnName(fnName
|
|
55200
|
+
name: () => this.getComposedFnName(fnName),
|
|
54783
55201
|
sequence: i,
|
|
54784
55202
|
isReadonlyAllowed: true,
|
|
54785
55203
|
execute: () => {
|
|
@@ -54792,8 +55210,9 @@ class BottomBarStatistic extends Component {
|
|
|
54792
55210
|
const { top, left, width } = target.getBoundingClientRect();
|
|
54793
55211
|
this.props.openContextMenu(left + width, top, registry);
|
|
54794
55212
|
}
|
|
54795
|
-
getComposedFnName(fnName
|
|
55213
|
+
getComposedFnName(fnName) {
|
|
54796
55214
|
const locale = this.env.model.getters.getLocale();
|
|
55215
|
+
const fnValue = this.store.statisticFnResults[fnName];
|
|
54797
55216
|
return fnName + ": " + (fnValue !== undefined ? formatValue(fnValue, { locale }) : "__");
|
|
54798
55217
|
}
|
|
54799
55218
|
}
|
|
@@ -54902,10 +55321,14 @@ class BottomBar extends Component {
|
|
|
54902
55321
|
name: sheet.name,
|
|
54903
55322
|
sequence: i,
|
|
54904
55323
|
isReadonlyAllowed: true,
|
|
54905
|
-
textColor: sheet.isVisible ? undefined : "
|
|
55324
|
+
textColor: sheet.isVisible ? undefined : "#808080",
|
|
54906
55325
|
execute: (env) => {
|
|
55326
|
+
if (!this.env.model.getters.isSheetVisible(sheetId)) {
|
|
55327
|
+
this.env.model.dispatch("SHOW_SHEET", { sheetId });
|
|
55328
|
+
}
|
|
54907
55329
|
env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: from, sheetIdTo: sheetId });
|
|
54908
55330
|
},
|
|
55331
|
+
isEnabled: (env) => (env.model.getters.isReadonly() ? sheet.isVisible : true),
|
|
54909
55332
|
});
|
|
54910
55333
|
i++;
|
|
54911
55334
|
}
|
|
@@ -54978,7 +55401,7 @@ class BottomBar extends Component {
|
|
|
54978
55401
|
this.sheetListRef.el.scrollTo({ top: 0, left: scroll, behavior: "smooth" });
|
|
54979
55402
|
}
|
|
54980
55403
|
onSheetMouseDown(sheetId, event) {
|
|
54981
|
-
if (event.button !== 0)
|
|
55404
|
+
if (event.button !== 0 || this.env.model.getters.isReadonly())
|
|
54982
55405
|
return;
|
|
54983
55406
|
this.closeMenu();
|
|
54984
55407
|
const visibleSheets = this.getVisibleSheets();
|
|
@@ -55014,7 +55437,7 @@ class BottomBar extends Component {
|
|
|
55014
55437
|
.map((sheetEl) => sheetEl.getBoundingClientRect())
|
|
55015
55438
|
.map((rect) => ({
|
|
55016
55439
|
x: rect.x,
|
|
55017
|
-
width: rect.width - 1,
|
|
55440
|
+
width: rect.width - 1, // -1 to compensate negative margin
|
|
55018
55441
|
y: rect.y,
|
|
55019
55442
|
height: rect.height,
|
|
55020
55443
|
}));
|
|
@@ -55241,7 +55664,7 @@ class RowGroup extends AbstractHeaderGroup {
|
|
|
55241
55664
|
}
|
|
55242
55665
|
return cssPropertiesToCss({
|
|
55243
55666
|
top: `${groupBox.headerRect.height / 2}px`,
|
|
55244
|
-
left: `calc(50% - 1px)`,
|
|
55667
|
+
left: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55245
55668
|
width: `30%`,
|
|
55246
55669
|
height: `calc(100% - ${groupBox.headerRect.height / 2}px)`,
|
|
55247
55670
|
"border-left": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
|
|
@@ -55293,7 +55716,7 @@ class ColGroup extends AbstractHeaderGroup {
|
|
|
55293
55716
|
return "";
|
|
55294
55717
|
}
|
|
55295
55718
|
return cssPropertiesToCss({
|
|
55296
|
-
top: `calc(50% - 1px)`,
|
|
55719
|
+
top: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55297
55720
|
left: `${groupBox.headerRect.width / 2}px`,
|
|
55298
55721
|
width: `calc(100% - ${groupBox.headerRect.width / 2}px)`,
|
|
55299
55722
|
height: `30%`,
|
|
@@ -56384,7 +56807,7 @@ css /* scss */ `
|
|
|
56384
56807
|
}
|
|
56385
56808
|
.o-disabled {
|
|
56386
56809
|
opacity: 0.4;
|
|
56387
|
-
|
|
56810
|
+
cursor: default;
|
|
56388
56811
|
pointer-events: none;
|
|
56389
56812
|
}
|
|
56390
56813
|
|
|
@@ -56550,7 +56973,7 @@ css /* scss */ `
|
|
|
56550
56973
|
|
|
56551
56974
|
.o-number-input {
|
|
56552
56975
|
/* Remove number input arrows */
|
|
56553
|
-
|
|
56976
|
+
appearance: textfield;
|
|
56554
56977
|
&::-webkit-outer-spin-button,
|
|
56555
56978
|
&::-webkit-inner-spin-button {
|
|
56556
56979
|
-webkit-appearance: none;
|
|
@@ -56593,6 +57016,7 @@ class Spreadsheet extends Component {
|
|
|
56593
57016
|
this.notificationStore = useStore(NotificationStore);
|
|
56594
57017
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
56595
57018
|
this.sidePanel = useStore(SidePanelStore);
|
|
57019
|
+
useStore(ArrayFormulaHighlight);
|
|
56596
57020
|
this.keyDownMapping = {
|
|
56597
57021
|
"CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
56598
57022
|
"CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
@@ -56695,7 +57119,7 @@ class Spreadsheet extends Component {
|
|
|
56695
57119
|
const gridColSize = GROUP_LAYER_WIDTH * this.rowLayers.length;
|
|
56696
57120
|
const gridRowSize = GROUP_LAYER_WIDTH * this.colLayers.length;
|
|
56697
57121
|
return cssPropertiesToCss({
|
|
56698
|
-
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`,
|
|
57122
|
+
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`, // +2: margins
|
|
56699
57123
|
"grid-template-rows": `${gridRowSize ? gridRowSize + 2 : 0}px auto`,
|
|
56700
57124
|
});
|
|
56701
57125
|
}
|
|
@@ -57427,14 +57851,6 @@ class SelectiveHistory {
|
|
|
57427
57851
|
this.revertBefore(operationId);
|
|
57428
57852
|
this.tree.drop(operationId);
|
|
57429
57853
|
}
|
|
57430
|
-
getRevertedExecution() {
|
|
57431
|
-
const data = [];
|
|
57432
|
-
const operations = this.tree.revertedExecution(this.HEAD_BRANCH);
|
|
57433
|
-
for (const { operation } of operations) {
|
|
57434
|
-
data.push(operation.data);
|
|
57435
|
-
}
|
|
57436
|
-
return data;
|
|
57437
|
-
}
|
|
57438
57854
|
/**
|
|
57439
57855
|
* Revert the state as it was *before* the given operation was executed.
|
|
57440
57856
|
*/
|
|
@@ -58223,6 +58639,9 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
58223
58639
|
case "bar":
|
|
58224
58640
|
plot = addBarChart(chart.data);
|
|
58225
58641
|
break;
|
|
58642
|
+
case "combo":
|
|
58643
|
+
plot = addComboChart(chart.data);
|
|
58644
|
+
break;
|
|
58226
58645
|
case "line":
|
|
58227
58646
|
plot = addLineChart(chart.data);
|
|
58228
58647
|
break;
|
|
@@ -58385,6 +58804,79 @@ function addBarChart(chart) {
|
|
|
58385
58804
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58386
58805
|
`;
|
|
58387
58806
|
}
|
|
58807
|
+
function addComboChart(chart) {
|
|
58808
|
+
// gapWitdh and overlap that define the space between clusters (in %) and the overlap between datasets (from -100: completely scattered to 100, completely overlapped)
|
|
58809
|
+
// see gapWidth : https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_gapWidth_topic_ID0EFVEQB.html#topic_ID0EFVEQB
|
|
58810
|
+
// see overlap : https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_overlap_topic_ID0ELYQQB.html#topic_ID0ELYQQB
|
|
58811
|
+
//
|
|
58812
|
+
// overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
|
|
58813
|
+
// See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
|
|
58814
|
+
const colors = new ChartColors();
|
|
58815
|
+
const dataSetsNodes = [];
|
|
58816
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
58817
|
+
const color = toXlsxHexColor(colors.next());
|
|
58818
|
+
const dataShapeProperty = shapeProperty({
|
|
58819
|
+
backgroundColor: color,
|
|
58820
|
+
line: { color },
|
|
58821
|
+
});
|
|
58822
|
+
dataSetsNodes.push(dsIndex === "0"
|
|
58823
|
+
? escapeXml /*xml*/ `
|
|
58824
|
+
<c:ser>
|
|
58825
|
+
<c:idx val="${dsIndex}"/>
|
|
58826
|
+
<c:order val="${dsIndex}"/>
|
|
58827
|
+
${dataset.label ? escapeXml /*xml*/ `<c:tx>${stringRef(dataset.label)}</c:tx>` : ""}
|
|
58828
|
+
${dataShapeProperty}
|
|
58829
|
+
${chart.labelRange ? escapeXml /*xml*/ `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
58830
|
+
<c:val> <!-- x-coordinate values -->
|
|
58831
|
+
${numberRef(dataset.range)}
|
|
58832
|
+
</c:val>
|
|
58833
|
+
</c:ser>
|
|
58834
|
+
`
|
|
58835
|
+
: escapeXml /*xml*/ `
|
|
58836
|
+
<c:ser>
|
|
58837
|
+
<c:idx val="${dsIndex}"/>
|
|
58838
|
+
<c:order val="${dsIndex}"/>
|
|
58839
|
+
<c:smooth val="0"/>
|
|
58840
|
+
<c:marker>
|
|
58841
|
+
<c:symbol val="circle" />
|
|
58842
|
+
<c:size val="5"/>
|
|
58843
|
+
</c:marker>
|
|
58844
|
+
${dataset.label ? escapeXml `<c:tx>${stringRef(dataset.label)}</c:tx>` : ""}
|
|
58845
|
+
${dataShapeProperty}
|
|
58846
|
+
${chart.labelRange ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
58847
|
+
<c:val> <!-- x-coordinate values -->
|
|
58848
|
+
${numberRef(dataset.range)}
|
|
58849
|
+
</c:val>
|
|
58850
|
+
</c:ser>
|
|
58851
|
+
`);
|
|
58852
|
+
}
|
|
58853
|
+
// Excel does not support this feature
|
|
58854
|
+
const axisPos = chart.verticalAxisPosition === "left" ? "l" : "r";
|
|
58855
|
+
const overlap = chart.stacked ? 100 : -20;
|
|
58856
|
+
return escapeXml /*xml*/ `
|
|
58857
|
+
<c:barChart>
|
|
58858
|
+
<c:barDir val="col"/>
|
|
58859
|
+
<c:grouping val="clustered"/>
|
|
58860
|
+
<c:overlap val="${overlap}"/>
|
|
58861
|
+
<c:gapWidth val="70"/>
|
|
58862
|
+
<!-- each data marker in the series does not have a different color -->
|
|
58863
|
+
<c:varyColors val="0"/>
|
|
58864
|
+
${dataSetsNodes[0]}
|
|
58865
|
+
<c:axId val="${catAxId}" />
|
|
58866
|
+
<c:axId val="${valAxId}" />
|
|
58867
|
+
</c:barChart>
|
|
58868
|
+
<c:lineChart>
|
|
58869
|
+
<c:grouping val="standard"/>
|
|
58870
|
+
<!-- each data marker in the series does not have a different color -->
|
|
58871
|
+
<c:varyColors val="0"/>
|
|
58872
|
+
${joinXmlNodes(dataSetsNodes.slice(1))}
|
|
58873
|
+
<c:axId val="${catAxId}" />
|
|
58874
|
+
<c:axId val="${valAxId}" />
|
|
58875
|
+
</c:lineChart>
|
|
58876
|
+
${addAx("b", "c:catAx", catAxId, valAxId, { fontColor: chart.fontColor })}
|
|
58877
|
+
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58878
|
+
`;
|
|
58879
|
+
}
|
|
58388
58880
|
function addLineChart(chart) {
|
|
58389
58881
|
const colors = new ChartColors();
|
|
58390
58882
|
const dataSetsNodes = [];
|
|
@@ -58432,7 +58924,7 @@ function addLineChart(chart) {
|
|
|
58432
58924
|
}
|
|
58433
58925
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
58434
58926
|
const colors = new ChartColors();
|
|
58435
|
-
const maxLength =
|
|
58927
|
+
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
58436
58928
|
const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
|
|
58437
58929
|
const dataSetsNodes = [];
|
|
58438
58930
|
for (const [dsIndex, dataset] of Object.entries(chart.dataSets).reverse()) {
|
|
@@ -59310,7 +59802,7 @@ function addTableColumns(table, sheetData) {
|
|
|
59310
59802
|
const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
|
|
59311
59803
|
const colName = sheetData.cells[colHeaderXc]?.content || `col${i}`;
|
|
59312
59804
|
const colAttributes = [
|
|
59313
|
-
["id", i + 1],
|
|
59805
|
+
["id", i + 1], // id cannot be 0
|
|
59314
59806
|
["name", colName],
|
|
59315
59807
|
];
|
|
59316
59808
|
columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
|
|
@@ -59526,6 +60018,7 @@ function addSheetViews(sheet) {
|
|
|
59526
60018
|
* https://www.ecma-international.org/publications-and-standards/standards/ecma-376/
|
|
59527
60019
|
*/
|
|
59528
60020
|
function getXLSX(data) {
|
|
60021
|
+
data = fixLengthySheetNames(data);
|
|
59529
60022
|
const files = [];
|
|
59530
60023
|
const construct = getDefaultXLSXStructure();
|
|
59531
60024
|
files.push(createWorkbook(data, construct));
|
|
@@ -59773,6 +60266,40 @@ function createRelRoot() {
|
|
|
59773
60266
|
`;
|
|
59774
60267
|
return createXMLFile(parseXML(xml), "_rels/.rels");
|
|
59775
60268
|
}
|
|
60269
|
+
/**
|
|
60270
|
+
* Excel sheet names are maximum 31 characters while o-spreadsheet do not have this limit.
|
|
60271
|
+
* This method converts the sheet names to be within the 31 characters limit.
|
|
60272
|
+
* The cells/charts referencing this sheet will be updated accordingly.
|
|
60273
|
+
*/
|
|
60274
|
+
function fixLengthySheetNames(data) {
|
|
60275
|
+
const nameMapping = {};
|
|
60276
|
+
const newNames = new Set();
|
|
60277
|
+
for (const sheet of data.sheets) {
|
|
60278
|
+
let newName = sheet.name.slice(0, 31);
|
|
60279
|
+
let i = 1;
|
|
60280
|
+
while (newNames.has(newName)) {
|
|
60281
|
+
newName = newName.slice(0, 31 - String(i).length) + i++;
|
|
60282
|
+
}
|
|
60283
|
+
newNames.add(newName);
|
|
60284
|
+
if (newName !== sheet.name) {
|
|
60285
|
+
nameMapping[sheet.name] = newName;
|
|
60286
|
+
sheet.name = newName;
|
|
60287
|
+
}
|
|
60288
|
+
}
|
|
60289
|
+
if (!Object.keys(nameMapping).length) {
|
|
60290
|
+
return data;
|
|
60291
|
+
}
|
|
60292
|
+
const sheetWithNewNames = Object.keys(nameMapping).sort((a, b) => b.length - a.length);
|
|
60293
|
+
let stringifiedData = JSON.stringify(data);
|
|
60294
|
+
for (const sheetName of sheetWithNewNames) {
|
|
60295
|
+
const regex = new RegExp(`'?${escapeRegExp(sheetName)}'?!`, "g");
|
|
60296
|
+
stringifiedData = stringifiedData.replaceAll(regex, (match) => {
|
|
60297
|
+
const newName = nameMapping[sheetName];
|
|
60298
|
+
return match.replace(sheetName, newName);
|
|
60299
|
+
});
|
|
60300
|
+
}
|
|
60301
|
+
return JSON.parse(stringifiedData);
|
|
60302
|
+
}
|
|
59776
60303
|
|
|
59777
60304
|
var Status;
|
|
59778
60305
|
(function (Status) {
|
|
@@ -60421,6 +60948,6 @@ const constants = {
|
|
|
60421
60948
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
60422
60949
|
|
|
60423
60950
|
|
|
60424
|
-
__info__.version = "17.3.0-alpha.
|
|
60425
|
-
__info__.date = "2024-
|
|
60426
|
-
__info__.hash = "
|
|
60951
|
+
__info__.version = "17.3.0-alpha.2";
|
|
60952
|
+
__info__.date = "2024-04-05T14:01:07.060Z";
|
|
60953
|
+
__info__.hash = "8c5a229";
|