@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
|
'use strict';
|
|
@@ -466,7 +466,7 @@ function getItemId(item, itemsDic) {
|
|
|
466
466
|
}
|
|
467
467
|
// Generate new Id if the item didn't exist in the dictionary
|
|
468
468
|
const ids = Object.keys(itemsDic);
|
|
469
|
-
const maxId = ids.length === 0 ? 0 :
|
|
469
|
+
const maxId = ids.length === 0 ? 0 : largeMax(ids.map((id) => parseInt(id, 10)));
|
|
470
470
|
itemsDic[maxId + 1] = item;
|
|
471
471
|
return maxId + 1;
|
|
472
472
|
}
|
|
@@ -484,7 +484,7 @@ function debounce(func, wait, immediate) {
|
|
|
484
484
|
let timeout = undefined;
|
|
485
485
|
const debounced = function () {
|
|
486
486
|
const context = this;
|
|
487
|
-
const args = arguments;
|
|
487
|
+
const args = Array.from(arguments);
|
|
488
488
|
function later() {
|
|
489
489
|
timeout = undefined;
|
|
490
490
|
if (!immediate) {
|
|
@@ -686,6 +686,34 @@ function getSearchRegex(searchStr, searchOptions) {
|
|
|
686
686
|
}
|
|
687
687
|
return RegExp(searchValue, flags);
|
|
688
688
|
}
|
|
689
|
+
/**
|
|
690
|
+
* Alternative to Math.max that works with large arrays.
|
|
691
|
+
* Typically useful for arrays bigger than 100k elements.
|
|
692
|
+
*/
|
|
693
|
+
function largeMax(array) {
|
|
694
|
+
let len = array.length;
|
|
695
|
+
if (len < 100_000)
|
|
696
|
+
return Math.max(...array);
|
|
697
|
+
let max = -Infinity;
|
|
698
|
+
while (len--) {
|
|
699
|
+
max = array[len] > max ? array[len] : max;
|
|
700
|
+
}
|
|
701
|
+
return max;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Alternative to Math.min that works with large arrays.
|
|
705
|
+
* Typically useful for arrays bigger than 100k elements.
|
|
706
|
+
*/
|
|
707
|
+
function largeMin(array) {
|
|
708
|
+
let len = array.length;
|
|
709
|
+
if (len < 100_000)
|
|
710
|
+
return Math.min(...array);
|
|
711
|
+
let min = +Infinity;
|
|
712
|
+
while (len--) {
|
|
713
|
+
min = array[len] < min ? array[len] : min;
|
|
714
|
+
}
|
|
715
|
+
return min;
|
|
716
|
+
}
|
|
689
717
|
|
|
690
718
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
691
719
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
@@ -2088,6 +2116,7 @@ exports.CommandResult = void 0;
|
|
|
2088
2116
|
CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
|
|
2089
2117
|
CommandResult["NoChanges"] = "NoChanges";
|
|
2090
2118
|
CommandResult["InvalidInputId"] = "InvalidInputId";
|
|
2119
|
+
CommandResult["SheetIsHidden"] = "SheetIsHidden";
|
|
2091
2120
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
2092
2121
|
|
|
2093
2122
|
const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
|
|
@@ -4559,8 +4588,9 @@ function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
|
4559
4588
|
* Get the default height of the cell given its style.
|
|
4560
4589
|
*/
|
|
4561
4590
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4562
|
-
if (!cell || !cell.content)
|
|
4591
|
+
if (!cell || (!cell.isFormula && !cell.content)) {
|
|
4563
4592
|
return DEFAULT_CELL_HEIGHT;
|
|
4593
|
+
}
|
|
4564
4594
|
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4565
4595
|
const numberOfLines = cell.isFormula
|
|
4566
4596
|
? 1
|
|
@@ -6994,10 +7024,17 @@ urlRegistry.add("sheet_URL", {
|
|
|
6994
7024
|
},
|
|
6995
7025
|
open(url, env) {
|
|
6996
7026
|
const sheetId = parseSheetUrl(url);
|
|
6997
|
-
env.model.dispatch("ACTIVATE_SHEET", {
|
|
7027
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
6998
7028
|
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
6999
7029
|
sheetIdTo: sheetId,
|
|
7000
7030
|
});
|
|
7031
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
7032
|
+
env.notifyUser({
|
|
7033
|
+
type: "warning",
|
|
7034
|
+
sticky: false,
|
|
7035
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
7036
|
+
});
|
|
7037
|
+
}
|
|
7001
7038
|
},
|
|
7002
7039
|
sequence: 0,
|
|
7003
7040
|
});
|
|
@@ -7113,7 +7150,7 @@ function textCell(value, format, formattedValue) {
|
|
|
7113
7150
|
}
|
|
7114
7151
|
function numberCell(value, format, formattedValue) {
|
|
7115
7152
|
return {
|
|
7116
|
-
value: value || 0,
|
|
7153
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
7117
7154
|
format,
|
|
7118
7155
|
formattedValue,
|
|
7119
7156
|
type: CellValueType.number,
|
|
@@ -9957,11 +9994,11 @@ autoCompleteProviders.add("dataValidation", {
|
|
|
9957
9994
|
}
|
|
9958
9995
|
else {
|
|
9959
9996
|
const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
|
|
9960
|
-
values = this.getters
|
|
9997
|
+
values = Array.from(new Set(this.getters
|
|
9961
9998
|
.getRangeValues(range)
|
|
9962
9999
|
.filter(isNotNull)
|
|
9963
10000
|
.map((value) => value.toString())
|
|
9964
|
-
.filter((val) => val !== "");
|
|
10001
|
+
.filter((val) => val !== "")));
|
|
9965
10002
|
}
|
|
9966
10003
|
return values.map((value) => ({ text: value }));
|
|
9967
10004
|
},
|
|
@@ -19408,10 +19445,10 @@ function aggregateDataForLabels(labels, datasets) {
|
|
|
19408
19445
|
}
|
|
19409
19446
|
}
|
|
19410
19447
|
return {
|
|
19411
|
-
labels:
|
|
19448
|
+
labels: Array.from(labelSet),
|
|
19412
19449
|
dataSetsValues: datasets.map((dataset, indexOfDataset) => ({
|
|
19413
19450
|
...dataset,
|
|
19414
|
-
data:
|
|
19451
|
+
data: Array.from(labelSet).map((label) => labelMap[label][indexOfDataset]),
|
|
19415
19452
|
})),
|
|
19416
19453
|
};
|
|
19417
19454
|
}
|
|
@@ -19430,8 +19467,8 @@ function truncateLabel(label) {
|
|
|
19430
19467
|
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
|
|
19431
19468
|
const options = {
|
|
19432
19469
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
19433
|
-
responsive: true,
|
|
19434
|
-
maintainAspectRatio: false,
|
|
19470
|
+
responsive: true, // will resize when its container is resized
|
|
19471
|
+
maintainAspectRatio: false, // doesn't maintain the aspect ration (width/height =2 by default) so the user has the choice of the exact layout
|
|
19435
19472
|
layout: {
|
|
19436
19473
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
19437
19474
|
},
|
|
@@ -19477,7 +19514,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
|
|
|
19477
19514
|
labels: labels.map(truncateLabel),
|
|
19478
19515
|
datasets: [],
|
|
19479
19516
|
},
|
|
19480
|
-
platform: undefined,
|
|
19517
|
+
platform: undefined, // This key is optional and will be set by chart.js
|
|
19481
19518
|
plugins: [],
|
|
19482
19519
|
};
|
|
19483
19520
|
}
|
|
@@ -19754,7 +19791,7 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
19754
19791
|
},
|
|
19755
19792
|
y: {
|
|
19756
19793
|
position: chart.verticalAxisPosition,
|
|
19757
|
-
beginAtZero: true,
|
|
19794
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
19758
19795
|
ticks: {
|
|
19759
19796
|
color: fontColor,
|
|
19760
19797
|
callback: (value) => {
|
|
@@ -19805,6 +19842,204 @@ function createBarChartRuntime(chart, getters) {
|
|
|
19805
19842
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
19806
19843
|
}
|
|
19807
19844
|
|
|
19845
|
+
class ComboChart extends AbstractChart {
|
|
19846
|
+
useBothYAxis;
|
|
19847
|
+
dataSets;
|
|
19848
|
+
labelRange;
|
|
19849
|
+
background;
|
|
19850
|
+
verticalAxisPosition;
|
|
19851
|
+
legendPosition;
|
|
19852
|
+
aggregated;
|
|
19853
|
+
dataSetsHaveTitle;
|
|
19854
|
+
type = "combo";
|
|
19855
|
+
constructor(definition, sheetId, getters) {
|
|
19856
|
+
super(definition, sheetId, getters);
|
|
19857
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
19858
|
+
this.labelRange = createRange(getters, sheetId, definition.labelRange);
|
|
19859
|
+
this.background = definition.background;
|
|
19860
|
+
this.verticalAxisPosition = definition.verticalAxisPosition;
|
|
19861
|
+
this.legendPosition = definition.legendPosition;
|
|
19862
|
+
this.aggregated = definition.aggregated;
|
|
19863
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
19864
|
+
this.useBothYAxis = definition.useBothYAxis;
|
|
19865
|
+
}
|
|
19866
|
+
static transformDefinition(definition, executed) {
|
|
19867
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
19868
|
+
}
|
|
19869
|
+
static validateChartDefinition(validator, definition) {
|
|
19870
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
19871
|
+
}
|
|
19872
|
+
getContextCreation() {
|
|
19873
|
+
return {
|
|
19874
|
+
background: this.background,
|
|
19875
|
+
title: this.title,
|
|
19876
|
+
range: this.dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, this.sheetId)),
|
|
19877
|
+
auxiliaryRange: this.labelRange
|
|
19878
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
19879
|
+
: undefined,
|
|
19880
|
+
aggregated: this.aggregated,
|
|
19881
|
+
};
|
|
19882
|
+
}
|
|
19883
|
+
getDefinition() {
|
|
19884
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
19885
|
+
}
|
|
19886
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
19887
|
+
return {
|
|
19888
|
+
type: "combo",
|
|
19889
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
19890
|
+
background: this.background,
|
|
19891
|
+
dataSets: dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, targetSheetId || this.sheetId)),
|
|
19892
|
+
legendPosition: this.legendPosition,
|
|
19893
|
+
verticalAxisPosition: this.verticalAxisPosition,
|
|
19894
|
+
labelRange: labelRange
|
|
19895
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
19896
|
+
: undefined,
|
|
19897
|
+
title: this.title,
|
|
19898
|
+
aggregated: this.aggregated,
|
|
19899
|
+
useBothYAxis: this.useBothYAxis,
|
|
19900
|
+
};
|
|
19901
|
+
}
|
|
19902
|
+
getDefinitionForExcel() {
|
|
19903
|
+
// Excel does not support aggregating labels
|
|
19904
|
+
if (this.aggregated) {
|
|
19905
|
+
return undefined;
|
|
19906
|
+
}
|
|
19907
|
+
const dataSets = this.dataSets
|
|
19908
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
19909
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
19910
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
19911
|
+
return {
|
|
19912
|
+
...this.getDefinition(),
|
|
19913
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
19914
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
19915
|
+
dataSets,
|
|
19916
|
+
labelRange,
|
|
19917
|
+
};
|
|
19918
|
+
}
|
|
19919
|
+
updateRanges(applyChange) {
|
|
19920
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
19921
|
+
if (!isStale) {
|
|
19922
|
+
return this;
|
|
19923
|
+
}
|
|
19924
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
19925
|
+
return new ComboChart(definition, this.sheetId, this.getters);
|
|
19926
|
+
}
|
|
19927
|
+
static getDefinitionFromContextCreation(context) {
|
|
19928
|
+
return {
|
|
19929
|
+
background: context.background,
|
|
19930
|
+
dataSets: context.range ? context.range : [],
|
|
19931
|
+
dataSetsHaveTitle: false,
|
|
19932
|
+
aggregated: context.aggregated,
|
|
19933
|
+
legendPosition: "top",
|
|
19934
|
+
title: context.title || "",
|
|
19935
|
+
verticalAxisPosition: "left",
|
|
19936
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
19937
|
+
type: "combo",
|
|
19938
|
+
useBothYAxis: false,
|
|
19939
|
+
};
|
|
19940
|
+
}
|
|
19941
|
+
copyForSheetId(sheetId) {
|
|
19942
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
19943
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
19944
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
19945
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
19946
|
+
}
|
|
19947
|
+
copyInSheetId(sheetId) {
|
|
19948
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
19949
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
19950
|
+
}
|
|
19951
|
+
}
|
|
19952
|
+
function createComboChartRuntime(chart, getters) {
|
|
19953
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
19954
|
+
const locale = getters.getLocale();
|
|
19955
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
19956
|
+
let labels = labelValues.formattedValues;
|
|
19957
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
19958
|
+
if (chart.dataSetsHaveTitle &&
|
|
19959
|
+
dataSetsValues[0] &&
|
|
19960
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
19961
|
+
labels.shift();
|
|
19962
|
+
}
|
|
19963
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
19964
|
+
if (chart.aggregated) {
|
|
19965
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
19966
|
+
}
|
|
19967
|
+
const localeFormat = { format: dataSetFormat, locale };
|
|
19968
|
+
const fontColor = chartFontColor(chart.background);
|
|
19969
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
|
|
19970
|
+
const legend = {
|
|
19971
|
+
labels: { color: fontColor },
|
|
19972
|
+
};
|
|
19973
|
+
if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
|
|
19974
|
+
legend.display = false;
|
|
19975
|
+
}
|
|
19976
|
+
else {
|
|
19977
|
+
legend.position = chart.legendPosition;
|
|
19978
|
+
}
|
|
19979
|
+
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
19980
|
+
config.options.layout = {
|
|
19981
|
+
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
19982
|
+
};
|
|
19983
|
+
config.options.scales = {
|
|
19984
|
+
x: {
|
|
19985
|
+
ticks: {
|
|
19986
|
+
padding: 5,
|
|
19987
|
+
color: fontColor,
|
|
19988
|
+
},
|
|
19989
|
+
},
|
|
19990
|
+
};
|
|
19991
|
+
const verticalAxis = {
|
|
19992
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
19993
|
+
ticks: {
|
|
19994
|
+
color: fontColor,
|
|
19995
|
+
callback: (value) => {
|
|
19996
|
+
value = Number(value);
|
|
19997
|
+
if (isNaN(value))
|
|
19998
|
+
return value;
|
|
19999
|
+
const { locale, format } = localeFormat;
|
|
20000
|
+
return formatValue(value, {
|
|
20001
|
+
locale,
|
|
20002
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
20003
|
+
});
|
|
20004
|
+
},
|
|
20005
|
+
},
|
|
20006
|
+
};
|
|
20007
|
+
if (chart.useBothYAxis) {
|
|
20008
|
+
config.options.scales.y = {
|
|
20009
|
+
...verticalAxis,
|
|
20010
|
+
position: "left",
|
|
20011
|
+
};
|
|
20012
|
+
config.options.scales.y1 = {
|
|
20013
|
+
...verticalAxis,
|
|
20014
|
+
position: "right",
|
|
20015
|
+
grid: {
|
|
20016
|
+
display: false,
|
|
20017
|
+
},
|
|
20018
|
+
};
|
|
20019
|
+
}
|
|
20020
|
+
else {
|
|
20021
|
+
config.options.scales.y = {
|
|
20022
|
+
...verticalAxis,
|
|
20023
|
+
position: chart.verticalAxisPosition,
|
|
20024
|
+
};
|
|
20025
|
+
}
|
|
20026
|
+
const colors = new ChartColors();
|
|
20027
|
+
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
20028
|
+
const color = colors.next();
|
|
20029
|
+
const dataset = {
|
|
20030
|
+
label,
|
|
20031
|
+
data,
|
|
20032
|
+
borderColor: color,
|
|
20033
|
+
backgroundColor: color,
|
|
20034
|
+
yAxisID: index > 0 && chart.useBothYAxis ? "y1" : "y",
|
|
20035
|
+
type: index === 0 ? "bar" : "line",
|
|
20036
|
+
order: -index,
|
|
20037
|
+
};
|
|
20038
|
+
config.data.datasets.push(dataset);
|
|
20039
|
+
}
|
|
20040
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
20041
|
+
}
|
|
20042
|
+
|
|
19808
20043
|
function isDataRangeValid(definition) {
|
|
19809
20044
|
return definition.dataRange && !rangeReference.test(definition.dataRange)
|
|
19810
20045
|
? "InvalidGaugeDataRange" /* CommandResult.InvalidGaugeDataRange */
|
|
@@ -20143,7 +20378,7 @@ function getBestTimeUnitForScale(labels, format, locale) {
|
|
|
20143
20378
|
return undefined;
|
|
20144
20379
|
}
|
|
20145
20380
|
const labelsTimestamps = labelDates.map((date) => date.getTime());
|
|
20146
|
-
const period =
|
|
20381
|
+
const period = largeMax(labelsTimestamps) - largeMin(labelsTimestamps);
|
|
20147
20382
|
const minUnit = getFormatMinDisplayUnit(format);
|
|
20148
20383
|
if (UNIT_LENGTH.second >= UNIT_LENGTH[minUnit] && Milliseconds.inSeconds(period) < 180) {
|
|
20149
20384
|
return "second";
|
|
@@ -20275,7 +20510,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
|
|
|
20275
20510
|
},
|
|
20276
20511
|
y: {
|
|
20277
20512
|
position: chart.verticalAxisPosition,
|
|
20278
|
-
beginAtZero: true,
|
|
20513
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
20279
20514
|
ticks: {
|
|
20280
20515
|
color: fontColor,
|
|
20281
20516
|
callback: (value) => {
|
|
@@ -20361,7 +20596,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
20361
20596
|
const dataset = {
|
|
20362
20597
|
label,
|
|
20363
20598
|
data,
|
|
20364
|
-
tension: 0,
|
|
20599
|
+
tension: 0, // 0 -> render straight lines, which is much faster
|
|
20365
20600
|
borderColor: color,
|
|
20366
20601
|
backgroundColor,
|
|
20367
20602
|
pointBackgroundColor: color,
|
|
@@ -20583,7 +20818,7 @@ class PieChart extends AbstractChart {
|
|
|
20583
20818
|
...this.getDefinition(),
|
|
20584
20819
|
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
20585
20820
|
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
20586
|
-
verticalAxisPosition: "left",
|
|
20821
|
+
verticalAxisPosition: "left", //TODO ExcelChartDefinition should be adapted, but can be done later
|
|
20587
20822
|
dataSets,
|
|
20588
20823
|
labelRange,
|
|
20589
20824
|
};
|
|
@@ -20631,7 +20866,7 @@ function getPieConfiguration(chart, labels, localeFormat) {
|
|
|
20631
20866
|
}
|
|
20632
20867
|
function getPieColors(colors, dataSetsValues) {
|
|
20633
20868
|
const pieColors = [];
|
|
20634
|
-
const maxLength =
|
|
20869
|
+
const maxLength = largeMax(dataSetsValues.map((ds) => ds.data.length));
|
|
20635
20870
|
for (let i = 0; i <= maxLength; i++) {
|
|
20636
20871
|
pieColors.push(colors.next());
|
|
20637
20872
|
}
|
|
@@ -20800,7 +21035,7 @@ function createScatterChartRuntime(chart, getters) {
|
|
|
20800
21035
|
configOptions.elements = {
|
|
20801
21036
|
point: {
|
|
20802
21037
|
radius: 3,
|
|
20803
|
-
hoverRadius: 3,
|
|
21038
|
+
hoverRadius: 3, // chartJS seems bugged, the point starts with the "hoverRadius" value
|
|
20804
21039
|
hitRadius: 8,
|
|
20805
21040
|
},
|
|
20806
21041
|
};
|
|
@@ -20840,6 +21075,16 @@ chartRegistry.add("bar", {
|
|
|
20840
21075
|
name: _t("Bar"),
|
|
20841
21076
|
sequence: 10,
|
|
20842
21077
|
});
|
|
21078
|
+
chartRegistry.add("combo", {
|
|
21079
|
+
match: (type) => type === "combo",
|
|
21080
|
+
createChart: (definition, sheetId, getters) => new ComboChart(definition, sheetId, getters),
|
|
21081
|
+
getChartRuntime: createComboChartRuntime,
|
|
21082
|
+
validateChartDefinition: (validator, definition) => ComboChart.validateChartDefinition(validator, definition),
|
|
21083
|
+
transformDefinition: (definition, executed) => ComboChart.transformDefinition(definition, executed),
|
|
21084
|
+
getChartDefinitionFromContextCreation: (context) => ComboChart.getDefinitionFromContextCreation(context),
|
|
21085
|
+
name: _t("Combo"),
|
|
21086
|
+
sequence: 15,
|
|
21087
|
+
});
|
|
20843
21088
|
chartRegistry.add("line", {
|
|
20844
21089
|
match: (type) => type === "line",
|
|
20845
21090
|
createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
|
|
@@ -20893,6 +21138,7 @@ chartRegistry.add("scatter", {
|
|
|
20893
21138
|
const chartComponentRegistry = new Registry();
|
|
20894
21139
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
20895
21140
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
21141
|
+
chartComponentRegistry.add("combo", ChartJsComponent);
|
|
20896
21142
|
chartComponentRegistry.add("pie", ChartJsComponent);
|
|
20897
21143
|
chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
20898
21144
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
@@ -23151,7 +23397,7 @@ const lightTemplateWithHeader = (colorSet) => ({
|
|
|
23151
23397
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23152
23398
|
border: { bottom: { color: colorSet.highlight, style: "thin" } },
|
|
23153
23399
|
},
|
|
23154
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23400
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23155
23401
|
firstRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23156
23402
|
secondRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23157
23403
|
});
|
|
@@ -23169,7 +23415,7 @@ const lightTemplateAllBorders = (colorSet) => ({
|
|
|
23169
23415
|
},
|
|
23170
23416
|
},
|
|
23171
23417
|
headerRow: { border: { bottom: { color: colorSet.highlight, style: "medium" } } },
|
|
23172
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23418
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23173
23419
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23174
23420
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23175
23421
|
});
|
|
@@ -23188,7 +23434,7 @@ const mediumTemplateBandedBorders = (colorSet) => ({
|
|
|
23188
23434
|
headerRow: {
|
|
23189
23435
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23190
23436
|
},
|
|
23191
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23437
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23192
23438
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23193
23439
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23194
23440
|
});
|
|
@@ -23224,7 +23470,7 @@ const mediumTemplateMinimalBorders = (colorSet) => ({
|
|
|
23224
23470
|
bottom: { color: "#000000", style: "medium" },
|
|
23225
23471
|
},
|
|
23226
23472
|
},
|
|
23227
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23473
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23228
23474
|
headerRow: {
|
|
23229
23475
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23230
23476
|
border: { bottom: { color: "#000000", style: "medium" } },
|
|
@@ -23248,7 +23494,7 @@ const mediumTemplateAllBorders = (colorSet) => ({
|
|
|
23248
23494
|
},
|
|
23249
23495
|
style: { fillColor: colorSet.light },
|
|
23250
23496
|
},
|
|
23251
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23497
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23252
23498
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23253
23499
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
23254
23500
|
});
|
|
@@ -23279,7 +23525,7 @@ const darkTemplateNoBorders = (colorSet) => ({
|
|
|
23279
23525
|
category: "dark",
|
|
23280
23526
|
colorName: colorSet.name,
|
|
23281
23527
|
wholeTable: { style: { fillColor: colorSet.light } },
|
|
23282
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23528
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23283
23529
|
headerRow: { style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" } },
|
|
23284
23530
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23285
23531
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
@@ -23444,8 +23690,8 @@ const DELETE_CONTENT_ROWS_NAME = (env) => {
|
|
|
23444
23690
|
let last;
|
|
23445
23691
|
const activesRows = env.model.getters.getActiveRows();
|
|
23446
23692
|
if (activesRows.size !== 0) {
|
|
23447
|
-
first =
|
|
23448
|
-
last =
|
|
23693
|
+
first = largeMin([...activesRows]);
|
|
23694
|
+
last = largeMax([...activesRows]);
|
|
23449
23695
|
}
|
|
23450
23696
|
else {
|
|
23451
23697
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23473,8 +23719,8 @@ const DELETE_CONTENT_COLUMNS_NAME = (env) => {
|
|
|
23473
23719
|
let last;
|
|
23474
23720
|
const activeCols = env.model.getters.getActiveCols();
|
|
23475
23721
|
if (activeCols.size !== 0) {
|
|
23476
|
-
first =
|
|
23477
|
-
last =
|
|
23722
|
+
first = largeMin([...activeCols]);
|
|
23723
|
+
last = largeMax([...activeCols]);
|
|
23478
23724
|
}
|
|
23479
23725
|
else {
|
|
23480
23726
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23502,8 +23748,8 @@ const REMOVE_ROWS_NAME = (env) => {
|
|
|
23502
23748
|
let last;
|
|
23503
23749
|
const activesRows = env.model.getters.getActiveRows();
|
|
23504
23750
|
if (activesRows.size !== 0) {
|
|
23505
|
-
first =
|
|
23506
|
-
last =
|
|
23751
|
+
first = largeMin([...activesRows]);
|
|
23752
|
+
last = largeMax([...activesRows]);
|
|
23507
23753
|
}
|
|
23508
23754
|
else {
|
|
23509
23755
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23544,8 +23790,8 @@ const REMOVE_COLUMNS_NAME = (env) => {
|
|
|
23544
23790
|
let last;
|
|
23545
23791
|
const activeCols = env.model.getters.getActiveCols();
|
|
23546
23792
|
if (activeCols.size !== 0) {
|
|
23547
|
-
first =
|
|
23548
|
-
last =
|
|
23793
|
+
first = largeMin([...activeCols]);
|
|
23794
|
+
last = largeMax([...activeCols]);
|
|
23549
23795
|
}
|
|
23550
23796
|
else {
|
|
23551
23797
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23586,7 +23832,7 @@ const INSERT_ROWS_BEFORE_ACTION = (env) => {
|
|
|
23586
23832
|
let row;
|
|
23587
23833
|
let quantity;
|
|
23588
23834
|
if (activeRows.size) {
|
|
23589
|
-
row =
|
|
23835
|
+
row = largeMin([...activeRows]);
|
|
23590
23836
|
quantity = activeRows.size;
|
|
23591
23837
|
}
|
|
23592
23838
|
else {
|
|
@@ -23607,7 +23853,7 @@ const INSERT_ROWS_AFTER_ACTION = (env) => {
|
|
|
23607
23853
|
let row;
|
|
23608
23854
|
let quantity;
|
|
23609
23855
|
if (activeRows.size) {
|
|
23610
|
-
row =
|
|
23856
|
+
row = largeMax([...activeRows]);
|
|
23611
23857
|
quantity = activeRows.size;
|
|
23612
23858
|
}
|
|
23613
23859
|
else {
|
|
@@ -23628,7 +23874,7 @@ const INSERT_COLUMNS_BEFORE_ACTION = (env) => {
|
|
|
23628
23874
|
let column;
|
|
23629
23875
|
let quantity;
|
|
23630
23876
|
if (activeCols.size) {
|
|
23631
|
-
column =
|
|
23877
|
+
column = largeMin([...activeCols]);
|
|
23632
23878
|
quantity = activeCols.size;
|
|
23633
23879
|
}
|
|
23634
23880
|
else {
|
|
@@ -23649,7 +23895,7 @@ const INSERT_COLUMNS_AFTER_ACTION = (env) => {
|
|
|
23649
23895
|
let column;
|
|
23650
23896
|
let quantity;
|
|
23651
23897
|
if (activeCols.size) {
|
|
23652
|
-
column =
|
|
23898
|
+
column = largeMax([...activeCols]);
|
|
23653
23899
|
quantity = activeCols.size;
|
|
23654
23900
|
}
|
|
23655
23901
|
else {
|
|
@@ -27255,6 +27501,22 @@ class BarChartDesignPanel extends LineBarPieDesignPanel {
|
|
|
27255
27501
|
static template = "o-spreadsheet-BarChartDesignPanel";
|
|
27256
27502
|
}
|
|
27257
27503
|
|
|
27504
|
+
class ComboChartConfigPanel extends LineBarPieConfigPanel {
|
|
27505
|
+
static template = "o-spreadsheet-ComboChartConfigPanel";
|
|
27506
|
+
get shouldUseRightAxis() {
|
|
27507
|
+
return _t("Use right axis for line series");
|
|
27508
|
+
}
|
|
27509
|
+
onUpdateUseRightAxis(useBothYAxis) {
|
|
27510
|
+
this.props.updateChart(this.props.figureId, {
|
|
27511
|
+
useBothYAxis,
|
|
27512
|
+
});
|
|
27513
|
+
}
|
|
27514
|
+
}
|
|
27515
|
+
|
|
27516
|
+
class ComboChartDesignPanel extends LineBarPieDesignPanel {
|
|
27517
|
+
static template = "o-spreadsheet-ComboChartDesignPanel";
|
|
27518
|
+
}
|
|
27519
|
+
|
|
27258
27520
|
class GaugeChartConfigPanel extends owl.Component {
|
|
27259
27521
|
static template = "o-spreadsheet-GaugeChartConfigPanel";
|
|
27260
27522
|
static components = { ChartErrorSection, ChartDataSeries };
|
|
@@ -27612,6 +27874,10 @@ chartSidePanelComponentRegistry
|
|
|
27612
27874
|
.add("bar", {
|
|
27613
27875
|
configuration: BarConfigPanel,
|
|
27614
27876
|
design: BarChartDesignPanel,
|
|
27877
|
+
})
|
|
27878
|
+
.add("combo", {
|
|
27879
|
+
configuration: ComboChartConfigPanel,
|
|
27880
|
+
design: ComboChartDesignPanel,
|
|
27615
27881
|
})
|
|
27616
27882
|
.add("pie", {
|
|
27617
27883
|
configuration: LineBarPieConfigPanel,
|
|
@@ -30252,7 +30518,11 @@ class SplitIntoColumnsPanel extends owl.Component {
|
|
|
30252
30518
|
const composerStore = useStore(ComposerStore);
|
|
30253
30519
|
// The feature makes no sense if we are editing a cell, because then the selection isn't active
|
|
30254
30520
|
// Stop the edition when the panel is mounted, and close the panel if the user start editing a cell
|
|
30255
|
-
owl.useEffect(
|
|
30521
|
+
owl.useEffect((editionMode) => {
|
|
30522
|
+
if (editionMode !== "inactive") {
|
|
30523
|
+
this.props.onCloseSidePanel();
|
|
30524
|
+
}
|
|
30525
|
+
}, () => [composerStore.editionMode]);
|
|
30256
30526
|
owl.onMounted(() => {
|
|
30257
30527
|
composerStore.stopEdition();
|
|
30258
30528
|
});
|
|
@@ -31961,6 +32231,9 @@ class FunctionDescriptionProvider extends owl.Component {
|
|
|
31961
32231
|
this.assistantState.allowCellSelectionBehind = false;
|
|
31962
32232
|
}, 2000);
|
|
31963
32233
|
}
|
|
32234
|
+
get formulaArgSeparator() {
|
|
32235
|
+
return this.env.model.getters.getLocale().formulaArgSeparator + " ";
|
|
32236
|
+
}
|
|
31964
32237
|
}
|
|
31965
32238
|
|
|
31966
32239
|
const functions$2 = functionRegistry.content;
|
|
@@ -32120,6 +32393,12 @@ class Composer extends owl.Component {
|
|
|
32120
32393
|
owl.useEffect(() => {
|
|
32121
32394
|
this.processContent();
|
|
32122
32395
|
});
|
|
32396
|
+
owl.onPatched(() => {
|
|
32397
|
+
// Required because typing '=SUM' and double-clicking another cell leaves ShowProvider/ShowDescription true
|
|
32398
|
+
if (this.composerStore.editionMode === "inactive") {
|
|
32399
|
+
this.processTokenAtCursor();
|
|
32400
|
+
}
|
|
32401
|
+
});
|
|
32123
32402
|
}
|
|
32124
32403
|
// ---------------------------------------------------------------------------
|
|
32125
32404
|
// Handlers
|
|
@@ -34099,11 +34378,6 @@ css /* scss */ `
|
|
|
34099
34378
|
height: 10000px;
|
|
34100
34379
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34101
34380
|
}
|
|
34102
|
-
.o-unhide-buttons {
|
|
34103
|
-
width: fit-content;
|
|
34104
|
-
gap: 5px;
|
|
34105
|
-
transform: translate(-50%, 0);
|
|
34106
|
-
}
|
|
34107
34381
|
.o-unhide:hover {
|
|
34108
34382
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34109
34383
|
background-color: lightgrey;
|
|
@@ -34265,10 +34539,6 @@ css /* scss */ `
|
|
|
34265
34539
|
height: 1px;
|
|
34266
34540
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34267
34541
|
}
|
|
34268
|
-
.o-unhide-buttons {
|
|
34269
|
-
height: fit-content;
|
|
34270
|
-
transform: translate(0, -50%);
|
|
34271
|
-
}
|
|
34272
34542
|
.o-unhide:hover {
|
|
34273
34543
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34274
34544
|
background-color: lightgrey;
|
|
@@ -35476,7 +35746,7 @@ class VerticalScrollBar extends owl.Component {
|
|
|
35476
35746
|
onScroll(offset) {
|
|
35477
35747
|
const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
|
|
35478
35748
|
this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
|
|
35479
|
-
offsetX: scrollX,
|
|
35749
|
+
offsetX: scrollX, // offsetX is the same
|
|
35480
35750
|
offsetY: offset,
|
|
35481
35751
|
});
|
|
35482
35752
|
}
|
|
@@ -35764,8 +36034,8 @@ class Grid extends owl.Component {
|
|
|
35764
36034
|
"Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
|
|
35765
36035
|
"Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
|
|
35766
36036
|
"Ctrl+Shift+V": () => PASTE_AS_VALUE_ACTION(this.env),
|
|
35767
|
-
"Ctrl+Shift+<": () => this.clearFormatting(),
|
|
35768
|
-
"Ctrl+<": () => this.clearFormatting(),
|
|
36037
|
+
"Ctrl+Shift+<": () => this.clearFormatting(), // for qwerty
|
|
36038
|
+
"Ctrl+<": () => this.clearFormatting(), // for azerty
|
|
35769
36039
|
"Ctrl+Shift+ ": () => {
|
|
35770
36040
|
this.env.model.selection.selectAll();
|
|
35771
36041
|
},
|
|
@@ -36201,6 +36471,7 @@ const XLSX_CHART_TYPES = [
|
|
|
36201
36471
|
"surfaceChart",
|
|
36202
36472
|
"surface3DChart",
|
|
36203
36473
|
"bubbleChart",
|
|
36474
|
+
"comboChart",
|
|
36204
36475
|
];
|
|
36205
36476
|
|
|
36206
36477
|
/** In XLSX color format (no #) */
|
|
@@ -36532,10 +36803,10 @@ function convertCFCellIsOperator(xlsxCfOperator) {
|
|
|
36532
36803
|
const CF_TYPE_CONVERSION_MAP = {
|
|
36533
36804
|
aboveAverage: undefined,
|
|
36534
36805
|
expression: undefined,
|
|
36535
|
-
cellIs: undefined,
|
|
36536
|
-
colorScale: undefined,
|
|
36806
|
+
cellIs: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36807
|
+
colorScale: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36537
36808
|
dataBar: undefined,
|
|
36538
|
-
iconSet: undefined,
|
|
36809
|
+
iconSet: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36539
36810
|
top10: undefined,
|
|
36540
36811
|
uniqueValues: undefined,
|
|
36541
36812
|
duplicateValues: undefined,
|
|
@@ -36612,6 +36883,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36612
36883
|
surfaceChart: undefined,
|
|
36613
36884
|
surface3DChart: undefined,
|
|
36614
36885
|
bubbleChart: undefined,
|
|
36886
|
+
comboChart: "combo",
|
|
36615
36887
|
};
|
|
36616
36888
|
/** Conversion map for the SUBTOTAL(index, formula) function in xlsx, index <=> actual function*/
|
|
36617
36889
|
const SUBTOTAL_FUNCTION_CONVERSION_MAP = {
|
|
@@ -36770,7 +37042,7 @@ const XLSX_INDEXED_COLORS = {
|
|
|
36770
37042
|
61: "993366",
|
|
36771
37043
|
62: "333399",
|
|
36772
37044
|
63: "333333",
|
|
36773
|
-
64: "000000",
|
|
37045
|
+
64: "000000", // system foreground
|
|
36774
37046
|
65: "FFFFFF", // system background
|
|
36775
37047
|
};
|
|
36776
37048
|
const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
|
|
@@ -37962,7 +38234,7 @@ function convertHyperlink(link, cellValue, warningManager) {
|
|
|
37962
38234
|
function getSheetDims(sheet) {
|
|
37963
38235
|
const dims = [0, 0];
|
|
37964
38236
|
for (let row of sheet.rows) {
|
|
37965
|
-
dims[0] = Math.max(dims[0],
|
|
38237
|
+
dims[0] = Math.max(dims[0], largeMax(row.cells.map((cell) => toCartesian(cell.xc).col)));
|
|
37966
38238
|
dims[1] = Math.max(dims[1], row.index);
|
|
37967
38239
|
}
|
|
37968
38240
|
dims[0] = Math.max(dims[0], EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
|
|
@@ -38828,6 +39100,9 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38828
39100
|
if (!CHART_TYPE_CONVERSION_MAP[chartType]) {
|
|
38829
39101
|
throw new Error(`Unsupported chart type ${chartType}`);
|
|
38830
39102
|
}
|
|
39103
|
+
if (CHART_TYPE_CONVERSION_MAP[chartType] === "combo") {
|
|
39104
|
+
return this.extractComboChart(rootChartElement);
|
|
39105
|
+
}
|
|
38831
39106
|
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
38832
39107
|
const chartTitle = this.mapOnElements({ parent: rootChartElement, query: "c:title a:t" }, (textElement) => {
|
|
38833
39108
|
return textElement.textContent || "";
|
|
@@ -38856,6 +39131,37 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38856
39131
|
};
|
|
38857
39132
|
})[0];
|
|
38858
39133
|
}
|
|
39134
|
+
extractComboChart(chartElement) {
|
|
39135
|
+
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
39136
|
+
const chartTitle = this.mapOnElements({ parent: chartElement, query: "c:title a:t" }, (textElement) => {
|
|
39137
|
+
return textElement.textContent || "";
|
|
39138
|
+
}).join("");
|
|
39139
|
+
const barChartGrouping = this.extractChildAttr(chartElement, "c:grouping", "val", {
|
|
39140
|
+
default: "clustered",
|
|
39141
|
+
}).asString();
|
|
39142
|
+
return {
|
|
39143
|
+
title: chartTitle,
|
|
39144
|
+
type: "combo",
|
|
39145
|
+
dataSets: [
|
|
39146
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`)),
|
|
39147
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`)),
|
|
39148
|
+
],
|
|
39149
|
+
labelRange: this.extractChildTextContent(chartElement, "c:ser c:cat c:f"),
|
|
39150
|
+
backgroundColor: this.extractChildAttr(chartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
39151
|
+
default: "ffffff",
|
|
39152
|
+
}).asString(),
|
|
39153
|
+
verticalAxisPosition: this.extractChildAttr(chartElement, "c:valAx > c:axPos", "val", {
|
|
39154
|
+
default: "l",
|
|
39155
|
+
}).asString() === "r"
|
|
39156
|
+
? "right"
|
|
39157
|
+
: "left",
|
|
39158
|
+
legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
|
|
39159
|
+
default: "b",
|
|
39160
|
+
}).asString()],
|
|
39161
|
+
stacked: barChartGrouping === "stacked",
|
|
39162
|
+
fontColor: "000000",
|
|
39163
|
+
};
|
|
39164
|
+
}
|
|
38859
39165
|
extractChartDatasets(chartElement) {
|
|
38860
39166
|
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
38861
39167
|
return {
|
|
@@ -38873,12 +39179,21 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38873
39179
|
if (!plotAreaElement) {
|
|
38874
39180
|
throw new Error("Missing plot area in the chart definition.");
|
|
38875
39181
|
}
|
|
39182
|
+
let globalTag = undefined;
|
|
38876
39183
|
for (let child of plotAreaElement.children) {
|
|
38877
39184
|
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
38878
39185
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
38879
|
-
|
|
39186
|
+
if (!globalTag) {
|
|
39187
|
+
globalTag = tag;
|
|
39188
|
+
}
|
|
39189
|
+
else if (globalTag !== tag) {
|
|
39190
|
+
globalTag = "comboChart";
|
|
39191
|
+
}
|
|
38880
39192
|
}
|
|
38881
39193
|
}
|
|
39194
|
+
if (globalTag) {
|
|
39195
|
+
return globalTag;
|
|
39196
|
+
}
|
|
38882
39197
|
throw new Error("Unknown chart type");
|
|
38883
39198
|
}
|
|
38884
39199
|
}
|
|
@@ -42469,6 +42784,9 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
42469
42784
|
if (newRule.criterion.type === "isBoolean") {
|
|
42470
42785
|
this.setCenterStyleToBooleanCells(newRule);
|
|
42471
42786
|
}
|
|
42787
|
+
else if (newRule.criterion.type === "isValueInList") {
|
|
42788
|
+
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
42789
|
+
}
|
|
42472
42790
|
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
42473
42791
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
42474
42792
|
if (ruleIndex !== -1) {
|
|
@@ -42865,7 +43183,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
|
|
|
42865
43183
|
if (hiddenElements.size >= elements) {
|
|
42866
43184
|
return "TooManyHiddenElements" /* CommandResult.TooManyHiddenElements */;
|
|
42867
43185
|
}
|
|
42868
|
-
else if (
|
|
43186
|
+
else if (largeMin(cmd.elements) < 0 || largeMax(cmd.elements) > elements) {
|
|
42869
43187
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
42870
43188
|
}
|
|
42871
43189
|
else {
|
|
@@ -43194,7 +43512,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43194
43512
|
"isInSameMerge",
|
|
43195
43513
|
"isMergeHidden",
|
|
43196
43514
|
"getMainCellPosition",
|
|
43197
|
-
"getBottomLeftCell",
|
|
43198
43515
|
"expandZone",
|
|
43199
43516
|
"doesIntersectMerge",
|
|
43200
43517
|
"doesColumnsHaveCommonMerges",
|
|
@@ -43376,13 +43693,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43376
43693
|
const mergeTopLeftPos = this.getMerge(position).topLeft;
|
|
43377
43694
|
return { sheetId: position.sheetId, col: mergeTopLeftPos.col, row: mergeTopLeftPos.row };
|
|
43378
43695
|
}
|
|
43379
|
-
getBottomLeftCell(position) {
|
|
43380
|
-
if (!this.isInMerge(position)) {
|
|
43381
|
-
return position;
|
|
43382
|
-
}
|
|
43383
|
-
const { bottom, left } = this.getMerge(position);
|
|
43384
|
-
return { sheetId: position.sheetId, col: left, row: bottom };
|
|
43385
|
-
}
|
|
43386
43696
|
isMergeHidden(sheetId, merge) {
|
|
43387
43697
|
const hiddenColsGroups = this.getters.getHiddenColsGroups(sheetId);
|
|
43388
43698
|
const hiddenRowsGroups = this.getters.getHiddenRowsGroups(sheetId);
|
|
@@ -43683,8 +43993,8 @@ class RangeAdapter {
|
|
|
43683
43993
|
let newRange = range;
|
|
43684
43994
|
let changeType = "NONE";
|
|
43685
43995
|
for (let group of groups) {
|
|
43686
|
-
const min =
|
|
43687
|
-
const max =
|
|
43996
|
+
const min = largeMin(group);
|
|
43997
|
+
const max = largeMax(group);
|
|
43688
43998
|
if (range.zone[start] <= min && min <= range.zone[end]) {
|
|
43689
43999
|
const toRemove = Math.min(range.zone[end], max) - min + 1;
|
|
43690
44000
|
changeType = "RESIZE";
|
|
@@ -44066,7 +44376,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44066
44376
|
"getSheetIds",
|
|
44067
44377
|
"getVisibleSheetIds",
|
|
44068
44378
|
"isSheetVisible",
|
|
44069
|
-
"getEvaluationSheets",
|
|
44070
44379
|
"doesHeaderExist",
|
|
44071
44380
|
"doesHeadersExist",
|
|
44072
44381
|
"getCell",
|
|
@@ -44133,8 +44442,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
44133
44442
|
}
|
|
44134
44443
|
return "Success" /* CommandResult.Success */;
|
|
44135
44444
|
case "REMOVE_COLUMNS_ROWS": {
|
|
44136
|
-
const min =
|
|
44137
|
-
const max =
|
|
44445
|
+
const min = largeMin(cmd.elements);
|
|
44446
|
+
const max = largeMax(cmd.elements);
|
|
44138
44447
|
if (min < 0 || !this.doesHeaderExist(cmd.sheetId, cmd.dimension, max)) {
|
|
44139
44448
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
44140
44449
|
}
|
|
@@ -44325,9 +44634,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44325
44634
|
getVisibleSheetIds() {
|
|
44326
44635
|
return this.orderedSheetIds.filter(this.isSheetVisible.bind(this));
|
|
44327
44636
|
}
|
|
44328
|
-
getEvaluationSheets() {
|
|
44329
|
-
return this.sheets;
|
|
44330
|
-
}
|
|
44331
44637
|
doesHeaderExist(sheetId, dimension, index) {
|
|
44332
44638
|
return dimension === "COL"
|
|
44333
44639
|
? index >= 0 && index < this.getNumberCols(sheetId)
|
|
@@ -44336,13 +44642,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44336
44642
|
doesHeadersExist(sheetId, dimension, headerIndexes) {
|
|
44337
44643
|
return headerIndexes.every((index) => this.doesHeaderExist(sheetId, dimension, index));
|
|
44338
44644
|
}
|
|
44339
|
-
getRow(sheetId, index) {
|
|
44340
|
-
const row = this.getSheet(sheetId).rows[index];
|
|
44341
|
-
if (!row) {
|
|
44342
|
-
throw new Error(`Row ${row} not found.`);
|
|
44343
|
-
}
|
|
44344
|
-
return row;
|
|
44345
|
-
}
|
|
44346
44645
|
getCell({ sheetId, col, row }) {
|
|
44347
44646
|
const sheet = this.tryGetSheet(sheetId);
|
|
44348
44647
|
const cellId = sheet?.rows[row]?.cells[col];
|
|
@@ -45911,12 +46210,6 @@ class CompilationParametersBuilder {
|
|
|
45911
46210
|
: _t("Function %s expects its parameters to be single values or single cell references, not ranges.", functionName.toString()));
|
|
45912
46211
|
}
|
|
45913
46212
|
const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
|
|
45914
|
-
return this.readCell(position);
|
|
45915
|
-
}
|
|
45916
|
-
readCell(position) {
|
|
45917
|
-
if (!this.getters.tryGetSheet(position.sheetId)) {
|
|
45918
|
-
throw new EvaluationError(_t("Invalid sheet name"));
|
|
45919
|
-
}
|
|
45920
46213
|
return this.computeCell(position);
|
|
45921
46214
|
}
|
|
45922
46215
|
/**
|
|
@@ -45952,7 +46245,7 @@ class CompilationParametersBuilder {
|
|
|
45952
46245
|
matrix[colIndex] = new Array(height);
|
|
45953
46246
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
45954
46247
|
const rowIndex = row - _zone.top;
|
|
45955
|
-
matrix[colIndex][rowIndex] = this.
|
|
46248
|
+
matrix[colIndex][rowIndex] = this.computeCell({ sheetId, col, row });
|
|
45956
46249
|
}
|
|
45957
46250
|
}
|
|
45958
46251
|
this.rangeCache[cacheKey] = matrix;
|
|
@@ -47060,15 +47353,15 @@ class Evaluator {
|
|
|
47060
47353
|
getEvaluatedCell(position) {
|
|
47061
47354
|
return this.evaluatedCells.get(position) || EMPTY_CELL;
|
|
47062
47355
|
}
|
|
47063
|
-
|
|
47356
|
+
getSpreadZone(position) {
|
|
47064
47357
|
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
47065
|
-
return
|
|
47358
|
+
return undefined;
|
|
47066
47359
|
}
|
|
47067
47360
|
if (this.evaluatedCells.get(position)?.type === CellValueType.error) {
|
|
47068
|
-
return
|
|
47361
|
+
return positionToZone(position);
|
|
47069
47362
|
}
|
|
47070
47363
|
const spreadPositions = Array.from(this.spreadingRelations.getArrayResultPositions(position));
|
|
47071
|
-
return
|
|
47364
|
+
return union(positionToZone(position), unionPositionsToZone(spreadPositions));
|
|
47072
47365
|
}
|
|
47073
47366
|
getEvaluatedPositions() {
|
|
47074
47367
|
return this.evaluatedCells.keys();
|
|
@@ -47473,7 +47766,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47473
47766
|
"getEvaluatedCell",
|
|
47474
47767
|
"getEvaluatedCells",
|
|
47475
47768
|
"getEvaluatedCellsInZone",
|
|
47476
|
-
"
|
|
47769
|
+
"getSpreadZone",
|
|
47477
47770
|
"getArrayFormulaSpreadingOn",
|
|
47478
47771
|
"isEmpty",
|
|
47479
47772
|
];
|
|
@@ -47579,8 +47872,11 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47579
47872
|
getEvaluatedCellsInZone(sheetId, zone) {
|
|
47580
47873
|
return positions(zone).map(({ col, row }) => this.getters.getEvaluatedCell({ sheetId, col, row }));
|
|
47581
47874
|
}
|
|
47582
|
-
|
|
47583
|
-
|
|
47875
|
+
/**
|
|
47876
|
+
* Return the spread zone the position is part of, if any
|
|
47877
|
+
*/
|
|
47878
|
+
getSpreadZone(position) {
|
|
47879
|
+
return this.evaluator.getSpreadZone(position);
|
|
47584
47880
|
}
|
|
47585
47881
|
getArrayFormulaSpreadingOn(position) {
|
|
47586
47882
|
return this.evaluator.getArrayFormulaSpreadingOn(position);
|
|
@@ -47620,7 +47916,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47620
47916
|
? getItemId(newFormat, data.formats)
|
|
47621
47917
|
: exportedCellData.format;
|
|
47622
47918
|
let content;
|
|
47623
|
-
if (formulaCell instanceof FormulaCellWithDependencies) {
|
|
47919
|
+
if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
|
|
47624
47920
|
content = formulaCell.contentWithFixedReferences;
|
|
47625
47921
|
}
|
|
47626
47922
|
else {
|
|
@@ -47668,17 +47964,17 @@ function isBadExpression(tokens) {
|
|
|
47668
47964
|
*/
|
|
47669
47965
|
function sortWithClusters(colorsToSort) {
|
|
47670
47966
|
const clusters = [
|
|
47671
|
-
{ leadColor: rgba(255, 0, 0), colors: [] },
|
|
47672
|
-
{ leadColor: rgba(255, 128, 0), colors: [] },
|
|
47673
|
-
{ leadColor: rgba(128, 128, 0), colors: [] },
|
|
47674
|
-
{ leadColor: rgba(128, 255, 0), colors: [] },
|
|
47675
|
-
{ leadColor: rgba(0, 255, 0), colors: [] },
|
|
47676
|
-
{ leadColor: rgba(0, 255, 128), colors: [] },
|
|
47677
|
-
{ leadColor: rgba(0, 255, 255), colors: [] },
|
|
47678
|
-
{ leadColor: rgba(0, 127, 255), colors: [] },
|
|
47679
|
-
{ leadColor: rgba(0, 0, 255), colors: [] },
|
|
47680
|
-
{ leadColor: rgba(127, 0, 255), colors: [] },
|
|
47681
|
-
{ leadColor: rgba(128, 0, 128), colors: [] },
|
|
47967
|
+
{ leadColor: rgba(255, 0, 0), colors: [] }, // red
|
|
47968
|
+
{ leadColor: rgba(255, 128, 0), colors: [] }, // orange
|
|
47969
|
+
{ leadColor: rgba(128, 128, 0), colors: [] }, // yellow
|
|
47970
|
+
{ leadColor: rgba(128, 255, 0), colors: [] }, // chartreuse
|
|
47971
|
+
{ leadColor: rgba(0, 255, 0), colors: [] }, // green
|
|
47972
|
+
{ leadColor: rgba(0, 255, 128), colors: [] }, // spring green
|
|
47973
|
+
{ leadColor: rgba(0, 255, 255), colors: [] }, // cyan
|
|
47974
|
+
{ leadColor: rgba(0, 127, 255), colors: [] }, // azure
|
|
47975
|
+
{ leadColor: rgba(0, 0, 255), colors: [] }, // blue
|
|
47976
|
+
{ leadColor: rgba(127, 0, 255), colors: [] }, // violet
|
|
47977
|
+
{ leadColor: rgba(128, 0, 128), colors: [] }, // magenta
|
|
47682
47978
|
{ leadColor: rgba(255, 0, 128), colors: [] }, // rose
|
|
47683
47979
|
];
|
|
47684
47980
|
for (const color of colorsToSort.map(colorToRGBA)) {
|
|
@@ -48069,13 +48365,13 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
48069
48365
|
.map((cell) => cell.value);
|
|
48070
48366
|
switch (threshold.type) {
|
|
48071
48367
|
case "value":
|
|
48072
|
-
const result = functionName === "max" ?
|
|
48368
|
+
const result = functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
|
|
48073
48369
|
return result;
|
|
48074
48370
|
case "number":
|
|
48075
48371
|
return Number(threshold.value);
|
|
48076
48372
|
case "percentage":
|
|
48077
|
-
const min =
|
|
48078
|
-
const max =
|
|
48373
|
+
const min = largeMin(rangeValues);
|
|
48374
|
+
const max = largeMax(rangeValues);
|
|
48079
48375
|
const delta = max - min;
|
|
48080
48376
|
return min + (delta * Number(threshold.value)) / 100;
|
|
48081
48377
|
case "percentile":
|
|
@@ -48542,8 +48838,8 @@ class DynamicTablesPlugin extends UIPlugin {
|
|
|
48542
48838
|
else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
|
|
48543
48839
|
return true;
|
|
48544
48840
|
}
|
|
48545
|
-
const
|
|
48546
|
-
return deepEquals(unionZone,
|
|
48841
|
+
const zone = this.getters.getSpreadZone(parentSpreadingCell);
|
|
48842
|
+
return deepEquals(unionZone, zone);
|
|
48547
48843
|
}
|
|
48548
48844
|
coreTableToTable(sheetId, table) {
|
|
48549
48845
|
if (table.type !== "dynamic") {
|
|
@@ -48551,8 +48847,7 @@ class DynamicTablesPlugin extends UIPlugin {
|
|
|
48551
48847
|
}
|
|
48552
48848
|
const tableZone = table.range.zone;
|
|
48553
48849
|
const tablePosition = { sheetId, col: tableZone.left, row: tableZone.top };
|
|
48554
|
-
const
|
|
48555
|
-
const zone = spreadPositions.length ? unionPositionsToZone(spreadPositions) : table.range.zone;
|
|
48850
|
+
const zone = this.getters.getSpreadZone(tablePosition) ?? table.range.zone;
|
|
48556
48851
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
48557
48852
|
const filters = this.getDynamicTableFilters(sheetId, table, zone);
|
|
48558
48853
|
return { id: table.id, range, filters, config: table.config };
|
|
@@ -49002,8 +49297,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
49002
49297
|
let row = zone.bottom;
|
|
49003
49298
|
if (col > 0) {
|
|
49004
49299
|
let leftPosition = { sheetId, col: col - 1, row };
|
|
49005
|
-
while (this.getters.
|
|
49006
|
-
this.getters.getCell(leftPosition)?.content) {
|
|
49300
|
+
while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty) {
|
|
49007
49301
|
row += 1;
|
|
49008
49302
|
leftPosition = { sheetId, col: col - 1, row };
|
|
49009
49303
|
}
|
|
@@ -49012,8 +49306,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
49012
49306
|
col = zone.right;
|
|
49013
49307
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
49014
49308
|
let rightPosition = { sheetId, col: col + 1, row };
|
|
49015
|
-
while (this.getters.
|
|
49016
|
-
this.getters.getCell(rightPosition)?.content) {
|
|
49309
|
+
while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty) {
|
|
49017
49310
|
row += 1;
|
|
49018
49311
|
rightPosition = { sheetId, col: col + 1, row };
|
|
49019
49312
|
}
|
|
@@ -49323,13 +49616,13 @@ class AutomaticSumPlugin extends UIPlugin {
|
|
|
49323
49616
|
const cells = this.getters.getEvaluatedCellsInZone(sheet.id, zone);
|
|
49324
49617
|
const cellPositions = range(end, -1, -1);
|
|
49325
49618
|
const invalidCells = cellPositions.filter((position) => cells[position] && !cells[position].isAutoSummable);
|
|
49326
|
-
const maxValidPosition =
|
|
49619
|
+
const maxValidPosition = largeMax(invalidCells);
|
|
49327
49620
|
const numberSequences = groupConsecutive(cellPositions.filter((position) => this.isNumber(cells[position])));
|
|
49328
49621
|
const firstSequence = numberSequences[0] || [];
|
|
49329
|
-
if (
|
|
49622
|
+
if (largeMax(firstSequence) < maxValidPosition) {
|
|
49330
49623
|
return Infinity;
|
|
49331
49624
|
}
|
|
49332
|
-
return
|
|
49625
|
+
return largeMin(firstSequence);
|
|
49333
49626
|
}
|
|
49334
49627
|
shouldFindData(sheetId, zone) {
|
|
49335
49628
|
return this.getters.isEmpty(sheetId, zone) || this.getters.isSingleCellOrMerge(sheetId, zone);
|
|
@@ -50904,8 +51197,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50904
51197
|
static getters = [
|
|
50905
51198
|
"doesCellHaveGridIcon",
|
|
50906
51199
|
"getCellWidth",
|
|
50907
|
-
"getCellComputedBorder",
|
|
50908
|
-
"getCellComputedStyle",
|
|
50909
51200
|
"getTextWidth",
|
|
50910
51201
|
"getCellText",
|
|
50911
51202
|
"getCellMultiLineText",
|
|
@@ -50949,7 +51240,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50949
51240
|
// Getters
|
|
50950
51241
|
// ---------------------------------------------------------------------------
|
|
50951
51242
|
getCellWidth(position) {
|
|
50952
|
-
const style = this.getCellComputedStyle(position);
|
|
51243
|
+
const style = this.getters.getCellComputedStyle(position);
|
|
50953
51244
|
let contentWidth = 0;
|
|
50954
51245
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
50955
51246
|
if (content) {
|
|
@@ -51042,35 +51333,12 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
51042
51333
|
*/
|
|
51043
51334
|
isCellEmpty(position) {
|
|
51044
51335
|
const mainPosition = this.getters.getMainCellPosition(position);
|
|
51045
|
-
return
|
|
51046
|
-
this.getters.getCell(mainPosition)?.content);
|
|
51047
|
-
}
|
|
51048
|
-
getCellComputedBorder(position) {
|
|
51049
|
-
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
51050
|
-
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
51051
|
-
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
51052
|
-
const border = { ...cellTableBorder, ...removeFalsyAttributes(cellBorder) };
|
|
51053
|
-
return isObjectEmptyRecursive(border) ? null : border;
|
|
51054
|
-
}
|
|
51055
|
-
getCellComputedStyle(position) {
|
|
51056
|
-
const cell = this.getters.getCell(position);
|
|
51057
|
-
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
51058
|
-
const tableStyle = this.getters.getCellTableStyle(position);
|
|
51059
|
-
const computedStyle = {
|
|
51060
|
-
...removeFalsyAttributes(tableStyle),
|
|
51061
|
-
...removeFalsyAttributes(cell?.style),
|
|
51062
|
-
...removeFalsyAttributes(cfStyle),
|
|
51063
|
-
};
|
|
51064
|
-
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
51065
|
-
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
51066
|
-
computedStyle.textColor = LINK_COLOR;
|
|
51067
|
-
}
|
|
51068
|
-
return computedStyle;
|
|
51336
|
+
return this.getters.getEvaluatedCell(mainPosition).type === CellValueType.empty;
|
|
51069
51337
|
}
|
|
51070
51338
|
getColMaxWidth(sheetId, index) {
|
|
51071
51339
|
const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
|
|
51072
51340
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
51073
|
-
return Math.max(0,
|
|
51341
|
+
return Math.max(0, largeMax(sizes));
|
|
51074
51342
|
}
|
|
51075
51343
|
/**
|
|
51076
51344
|
* Check that any "sheetId" in the command matches an existing
|
|
@@ -51099,6 +51367,236 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
51099
51367
|
}
|
|
51100
51368
|
}
|
|
51101
51369
|
|
|
51370
|
+
class TableStylePlugin extends UIPlugin {
|
|
51371
|
+
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51372
|
+
tableStyles = {};
|
|
51373
|
+
handle(cmd) {
|
|
51374
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51375
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51376
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
51377
|
+
this.tableStyles = {};
|
|
51378
|
+
return;
|
|
51379
|
+
}
|
|
51380
|
+
if (doesCommandInvalidatesTableStyle(cmd)) {
|
|
51381
|
+
delete this.tableStyles[cmd.sheetId];
|
|
51382
|
+
return;
|
|
51383
|
+
}
|
|
51384
|
+
}
|
|
51385
|
+
finalize() {
|
|
51386
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
51387
|
+
if (!this.tableStyles[sheetId]) {
|
|
51388
|
+
this.tableStyles[sheetId] = {};
|
|
51389
|
+
}
|
|
51390
|
+
for (const table of this.getters.getTables(sheetId)) {
|
|
51391
|
+
if (!this.tableStyles[sheetId][table.id]) {
|
|
51392
|
+
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51393
|
+
}
|
|
51394
|
+
}
|
|
51395
|
+
}
|
|
51396
|
+
}
|
|
51397
|
+
getCellTableStyle(position) {
|
|
51398
|
+
const table = this.getters.getTable(position);
|
|
51399
|
+
if (!table) {
|
|
51400
|
+
return undefined;
|
|
51401
|
+
}
|
|
51402
|
+
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51403
|
+
}
|
|
51404
|
+
getCellTableBorder(position) {
|
|
51405
|
+
const table = this.getters.getTable(position);
|
|
51406
|
+
if (!table) {
|
|
51407
|
+
return undefined;
|
|
51408
|
+
}
|
|
51409
|
+
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51410
|
+
}
|
|
51411
|
+
computeTableStyle(sheetId, table) {
|
|
51412
|
+
return lazy(() => {
|
|
51413
|
+
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51414
|
+
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51415
|
+
// Return the style with sheet coordinates instead of tables coordinates
|
|
51416
|
+
const mapping = this.getTableMapping(sheetId, table);
|
|
51417
|
+
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51418
|
+
for (let col = 0; col < numberOfCols; col++) {
|
|
51419
|
+
const colInSheet = mapping.colMapping[col];
|
|
51420
|
+
absoluteTableStyle.borders[colInSheet] = {};
|
|
51421
|
+
absoluteTableStyle.styles[colInSheet] = {};
|
|
51422
|
+
for (let row = 0; row < numberOfRows; row++) {
|
|
51423
|
+
const rowInSheet = mapping.rowMapping[row];
|
|
51424
|
+
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51425
|
+
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51426
|
+
}
|
|
51427
|
+
}
|
|
51428
|
+
return absoluteTableStyle;
|
|
51429
|
+
});
|
|
51430
|
+
}
|
|
51431
|
+
/**
|
|
51432
|
+
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51433
|
+
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51434
|
+
* hidden rows from config.numberOfHeaders.
|
|
51435
|
+
*/
|
|
51436
|
+
getTableRuntimeConfig(sheetId, table) {
|
|
51437
|
+
const tableZone = table.range.zone;
|
|
51438
|
+
const config = { ...table.config };
|
|
51439
|
+
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51440
|
+
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51441
|
+
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51442
|
+
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51443
|
+
continue;
|
|
51444
|
+
}
|
|
51445
|
+
numberOfRows--;
|
|
51446
|
+
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51447
|
+
config.numberOfHeaders--;
|
|
51448
|
+
if (config.numberOfHeaders < 0) {
|
|
51449
|
+
config.numberOfHeaders = 0;
|
|
51450
|
+
}
|
|
51451
|
+
}
|
|
51452
|
+
if (row === tableZone.bottom) {
|
|
51453
|
+
config.totalRow = false;
|
|
51454
|
+
}
|
|
51455
|
+
}
|
|
51456
|
+
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51457
|
+
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51458
|
+
continue;
|
|
51459
|
+
}
|
|
51460
|
+
numberOfCols--;
|
|
51461
|
+
if (col === tableZone.left) {
|
|
51462
|
+
config.firstColumn = false;
|
|
51463
|
+
}
|
|
51464
|
+
if (col === tableZone.right) {
|
|
51465
|
+
config.lastColumn = false;
|
|
51466
|
+
}
|
|
51467
|
+
}
|
|
51468
|
+
return {
|
|
51469
|
+
config,
|
|
51470
|
+
numberOfCols,
|
|
51471
|
+
numberOfRows,
|
|
51472
|
+
};
|
|
51473
|
+
}
|
|
51474
|
+
/**
|
|
51475
|
+
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51476
|
+
*/
|
|
51477
|
+
getTableMapping(sheetId, table) {
|
|
51478
|
+
const colMapping = {};
|
|
51479
|
+
const rowMapping = {};
|
|
51480
|
+
let colOffset = 0;
|
|
51481
|
+
let rowOffset = 0;
|
|
51482
|
+
const tableZone = table.range.zone;
|
|
51483
|
+
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51484
|
+
if (this.getters.isColHidden(sheetId, col)) {
|
|
51485
|
+
continue;
|
|
51486
|
+
}
|
|
51487
|
+
colMapping[colOffset] = col;
|
|
51488
|
+
colOffset++;
|
|
51489
|
+
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51490
|
+
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51491
|
+
continue;
|
|
51492
|
+
}
|
|
51493
|
+
rowMapping[rowOffset] = row;
|
|
51494
|
+
rowOffset++;
|
|
51495
|
+
}
|
|
51496
|
+
}
|
|
51497
|
+
return {
|
|
51498
|
+
colMapping,
|
|
51499
|
+
rowMapping,
|
|
51500
|
+
};
|
|
51501
|
+
}
|
|
51502
|
+
}
|
|
51503
|
+
const invalidateTableStyleCommands = [
|
|
51504
|
+
"HIDE_COLUMNS_ROWS",
|
|
51505
|
+
"UNHIDE_COLUMNS_ROWS",
|
|
51506
|
+
"UNFOLD_HEADER_GROUP",
|
|
51507
|
+
"FOLD_HEADER_GROUP",
|
|
51508
|
+
"FOLD_ALL_HEADER_GROUPS",
|
|
51509
|
+
"UNFOLD_ALL_HEADER_GROUPS",
|
|
51510
|
+
"CREATE_TABLE",
|
|
51511
|
+
"UPDATE_TABLE",
|
|
51512
|
+
"UPDATE_FILTER",
|
|
51513
|
+
];
|
|
51514
|
+
const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
|
|
51515
|
+
function doesCommandInvalidatesTableStyle(cmd) {
|
|
51516
|
+
return invalidateTableStyleCommandsSet.has(cmd.type);
|
|
51517
|
+
}
|
|
51518
|
+
|
|
51519
|
+
class CellComputedStylePlugin extends UIPlugin {
|
|
51520
|
+
static getters = ["getCellComputedBorder", "getCellComputedStyle"];
|
|
51521
|
+
styles = {};
|
|
51522
|
+
borders = {};
|
|
51523
|
+
handle(cmd) {
|
|
51524
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51525
|
+
cmd.type === "UPDATE_CELL" ||
|
|
51526
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
51527
|
+
this.styles = {};
|
|
51528
|
+
this.borders = {};
|
|
51529
|
+
return;
|
|
51530
|
+
}
|
|
51531
|
+
if (doesCommandInvalidatesTableStyle(cmd)) {
|
|
51532
|
+
delete this.styles[cmd.sheetId];
|
|
51533
|
+
delete this.borders[cmd.sheetId];
|
|
51534
|
+
return;
|
|
51535
|
+
}
|
|
51536
|
+
if (invalidateCFEvaluationCommands.has(cmd.type)) {
|
|
51537
|
+
this.styles = {};
|
|
51538
|
+
return;
|
|
51539
|
+
}
|
|
51540
|
+
}
|
|
51541
|
+
getCellComputedBorder(position) {
|
|
51542
|
+
const { sheetId, row, col } = position;
|
|
51543
|
+
if (this.borders[sheetId]?.[row]?.[col] !== undefined) {
|
|
51544
|
+
return this.borders[sheetId][row][col];
|
|
51545
|
+
}
|
|
51546
|
+
if (!this.borders[sheetId]) {
|
|
51547
|
+
this.borders[sheetId] = {};
|
|
51548
|
+
}
|
|
51549
|
+
if (!this.borders[sheetId][row]) {
|
|
51550
|
+
this.borders[sheetId][row] = {};
|
|
51551
|
+
}
|
|
51552
|
+
if (!this.borders[sheetId][row][col]) {
|
|
51553
|
+
this.borders[sheetId][row][col] = this.computeCellBorder(position);
|
|
51554
|
+
}
|
|
51555
|
+
return this.borders[sheetId][row][col];
|
|
51556
|
+
}
|
|
51557
|
+
getCellComputedStyle(position) {
|
|
51558
|
+
const { sheetId, row, col } = position;
|
|
51559
|
+
if (this.styles[sheetId]?.[row]?.[col] !== undefined) {
|
|
51560
|
+
return this.styles[sheetId][row][col];
|
|
51561
|
+
}
|
|
51562
|
+
if (!this.styles[sheetId]) {
|
|
51563
|
+
this.styles[sheetId] = {};
|
|
51564
|
+
}
|
|
51565
|
+
if (!this.styles[sheetId][row]) {
|
|
51566
|
+
this.styles[sheetId][row] = {};
|
|
51567
|
+
}
|
|
51568
|
+
if (!this.styles[sheetId][row][col]) {
|
|
51569
|
+
this.styles[sheetId][row][col] = this.computeCellStyle(position);
|
|
51570
|
+
}
|
|
51571
|
+
return this.styles[sheetId][row][col];
|
|
51572
|
+
}
|
|
51573
|
+
computeCellBorder(position) {
|
|
51574
|
+
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
51575
|
+
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
51576
|
+
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
51577
|
+
const border = {
|
|
51578
|
+
...removeFalsyAttributes(cellTableBorder),
|
|
51579
|
+
...removeFalsyAttributes(cellBorder),
|
|
51580
|
+
};
|
|
51581
|
+
return isObjectEmptyRecursive(border) ? null : border;
|
|
51582
|
+
}
|
|
51583
|
+
computeCellStyle(position) {
|
|
51584
|
+
const cell = this.getters.getCell(position);
|
|
51585
|
+
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
51586
|
+
const tableStyle = this.getters.getCellTableStyle(position);
|
|
51587
|
+
const computedStyle = {
|
|
51588
|
+
...removeFalsyAttributes(tableStyle),
|
|
51589
|
+
...removeFalsyAttributes(cell?.style),
|
|
51590
|
+
...removeFalsyAttributes(cfStyle),
|
|
51591
|
+
};
|
|
51592
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
51593
|
+
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
51594
|
+
computedStyle.textColor = LINK_COLOR;
|
|
51595
|
+
}
|
|
51596
|
+
return computedStyle;
|
|
51597
|
+
}
|
|
51598
|
+
}
|
|
51599
|
+
|
|
51102
51600
|
const genericRepeatsTransforms = [
|
|
51103
51601
|
repeatSheetDependantCommand,
|
|
51104
51602
|
repeatTargetDependantCommand,
|
|
@@ -51676,148 +52174,6 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
51676
52174
|
}
|
|
51677
52175
|
}
|
|
51678
52176
|
|
|
51679
|
-
class TableStylePlugin extends UIPlugin {
|
|
51680
|
-
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51681
|
-
tableStyles = {};
|
|
51682
|
-
handle(cmd) {
|
|
51683
|
-
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51684
|
-
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51685
|
-
cmd.type === "EVALUATE_CELLS") {
|
|
51686
|
-
this.tableStyles = {};
|
|
51687
|
-
return;
|
|
51688
|
-
}
|
|
51689
|
-
switch (cmd.type) {
|
|
51690
|
-
case "HIDE_COLUMNS_ROWS":
|
|
51691
|
-
case "UNHIDE_COLUMNS_ROWS":
|
|
51692
|
-
case "UNFOLD_HEADER_GROUP":
|
|
51693
|
-
case "FOLD_HEADER_GROUP":
|
|
51694
|
-
case "FOLD_ALL_HEADER_GROUPS":
|
|
51695
|
-
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
51696
|
-
case "UPDATE_TABLE":
|
|
51697
|
-
case "UPDATE_FILTER":
|
|
51698
|
-
delete this.tableStyles[cmd.sheetId];
|
|
51699
|
-
break;
|
|
51700
|
-
}
|
|
51701
|
-
}
|
|
51702
|
-
finalize() {
|
|
51703
|
-
for (const sheetId of this.getters.getSheetIds()) {
|
|
51704
|
-
if (!this.tableStyles[sheetId]) {
|
|
51705
|
-
this.tableStyles[sheetId] = {};
|
|
51706
|
-
}
|
|
51707
|
-
for (const table of this.getters.getTables(sheetId)) {
|
|
51708
|
-
if (!this.tableStyles[sheetId][table.id]) {
|
|
51709
|
-
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51710
|
-
}
|
|
51711
|
-
}
|
|
51712
|
-
}
|
|
51713
|
-
}
|
|
51714
|
-
getCellTableStyle(position) {
|
|
51715
|
-
const table = this.getters.getTable(position);
|
|
51716
|
-
if (!table) {
|
|
51717
|
-
return undefined;
|
|
51718
|
-
}
|
|
51719
|
-
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51720
|
-
}
|
|
51721
|
-
getCellTableBorder(position) {
|
|
51722
|
-
const table = this.getters.getTable(position);
|
|
51723
|
-
if (!table) {
|
|
51724
|
-
return undefined;
|
|
51725
|
-
}
|
|
51726
|
-
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51727
|
-
}
|
|
51728
|
-
computeTableStyle(sheetId, table) {
|
|
51729
|
-
return lazy(() => {
|
|
51730
|
-
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51731
|
-
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51732
|
-
// Return the style with sheet coordinates instead of tables coordinates
|
|
51733
|
-
const mapping = this.getTableMapping(sheetId, table);
|
|
51734
|
-
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51735
|
-
for (let col = 0; col < numberOfCols; col++) {
|
|
51736
|
-
const colInSheet = mapping.colMapping[col];
|
|
51737
|
-
absoluteTableStyle.borders[colInSheet] = {};
|
|
51738
|
-
absoluteTableStyle.styles[colInSheet] = {};
|
|
51739
|
-
for (let row = 0; row < numberOfRows; row++) {
|
|
51740
|
-
const rowInSheet = mapping.rowMapping[row];
|
|
51741
|
-
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51742
|
-
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51743
|
-
}
|
|
51744
|
-
}
|
|
51745
|
-
return absoluteTableStyle;
|
|
51746
|
-
});
|
|
51747
|
-
}
|
|
51748
|
-
/**
|
|
51749
|
-
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51750
|
-
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51751
|
-
* hidden rows from config.numberOfHeaders.
|
|
51752
|
-
*/
|
|
51753
|
-
getTableRuntimeConfig(sheetId, table) {
|
|
51754
|
-
const tableZone = table.range.zone;
|
|
51755
|
-
const config = { ...table.config };
|
|
51756
|
-
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51757
|
-
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51758
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51759
|
-
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51760
|
-
continue;
|
|
51761
|
-
}
|
|
51762
|
-
numberOfRows--;
|
|
51763
|
-
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51764
|
-
config.numberOfHeaders--;
|
|
51765
|
-
if (config.numberOfHeaders < 0) {
|
|
51766
|
-
config.numberOfHeaders = 0;
|
|
51767
|
-
}
|
|
51768
|
-
}
|
|
51769
|
-
if (row === tableZone.bottom) {
|
|
51770
|
-
config.totalRow = false;
|
|
51771
|
-
}
|
|
51772
|
-
}
|
|
51773
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51774
|
-
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51775
|
-
continue;
|
|
51776
|
-
}
|
|
51777
|
-
numberOfCols--;
|
|
51778
|
-
if (col === tableZone.left) {
|
|
51779
|
-
config.firstColumn = false;
|
|
51780
|
-
}
|
|
51781
|
-
if (col === tableZone.right) {
|
|
51782
|
-
config.lastColumn = false;
|
|
51783
|
-
}
|
|
51784
|
-
}
|
|
51785
|
-
return {
|
|
51786
|
-
config,
|
|
51787
|
-
numberOfCols,
|
|
51788
|
-
numberOfRows,
|
|
51789
|
-
};
|
|
51790
|
-
}
|
|
51791
|
-
/**
|
|
51792
|
-
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51793
|
-
*/
|
|
51794
|
-
getTableMapping(sheetId, table) {
|
|
51795
|
-
const colMapping = {};
|
|
51796
|
-
const rowMapping = {};
|
|
51797
|
-
let colOffset = 0;
|
|
51798
|
-
let rowOffset = 0;
|
|
51799
|
-
const tableZone = table.range.zone;
|
|
51800
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51801
|
-
if (this.getters.isColHidden(sheetId, col)) {
|
|
51802
|
-
continue;
|
|
51803
|
-
}
|
|
51804
|
-
colMapping[colOffset] = col;
|
|
51805
|
-
colOffset++;
|
|
51806
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51807
|
-
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51808
|
-
continue;
|
|
51809
|
-
}
|
|
51810
|
-
rowMapping[rowOffset] = row;
|
|
51811
|
-
rowOffset++;
|
|
51812
|
-
}
|
|
51813
|
-
}
|
|
51814
|
-
return {
|
|
51815
|
-
colMapping,
|
|
51816
|
-
rowMapping,
|
|
51817
|
-
};
|
|
51818
|
-
}
|
|
51819
|
-
}
|
|
51820
|
-
|
|
51821
52177
|
/**
|
|
51822
52178
|
* Clipboard Plugin
|
|
51823
52179
|
*
|
|
@@ -52527,38 +52883,6 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52527
52883
|
}
|
|
52528
52884
|
}
|
|
52529
52885
|
|
|
52530
|
-
const selectionStatisticFunctions = [
|
|
52531
|
-
{
|
|
52532
|
-
name: _t("Sum"),
|
|
52533
|
-
types: [CellValueType.number],
|
|
52534
|
-
compute: (values, locale) => sum([[values]], locale),
|
|
52535
|
-
},
|
|
52536
|
-
{
|
|
52537
|
-
name: _t("Avg"),
|
|
52538
|
-
types: [CellValueType.number],
|
|
52539
|
-
compute: (values, locale) => average([[values]], locale),
|
|
52540
|
-
},
|
|
52541
|
-
{
|
|
52542
|
-
name: _t("Min"),
|
|
52543
|
-
types: [CellValueType.number],
|
|
52544
|
-
compute: (values, locale) => min([[values]], locale),
|
|
52545
|
-
},
|
|
52546
|
-
{
|
|
52547
|
-
name: _t("Max"),
|
|
52548
|
-
types: [CellValueType.number],
|
|
52549
|
-
compute: (values, locale) => max([[values]], locale),
|
|
52550
|
-
},
|
|
52551
|
-
{
|
|
52552
|
-
name: _t("Count"),
|
|
52553
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52554
|
-
compute: (values) => countAny([[values]]),
|
|
52555
|
-
},
|
|
52556
|
-
{
|
|
52557
|
-
name: _t("Count Numbers"),
|
|
52558
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52559
|
-
compute: (values, locale) => countNumbers([[values]], locale),
|
|
52560
|
-
},
|
|
52561
|
-
];
|
|
52562
52886
|
/**
|
|
52563
52887
|
* SelectionPlugin
|
|
52564
52888
|
*/
|
|
@@ -52574,8 +52898,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52574
52898
|
"getSelectedZones",
|
|
52575
52899
|
"getSelectedZone",
|
|
52576
52900
|
"getSelectedCells",
|
|
52577
|
-
"getStatisticFnResults",
|
|
52578
|
-
"getAggregate",
|
|
52579
52901
|
"getSelectedFigureId",
|
|
52580
52902
|
"getSelection",
|
|
52581
52903
|
"getActivePosition",
|
|
@@ -52610,7 +52932,10 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52610
52932
|
switch (cmd.type) {
|
|
52611
52933
|
case "ACTIVATE_SHEET":
|
|
52612
52934
|
try {
|
|
52613
|
-
this.getters.getSheet(cmd.sheetIdTo);
|
|
52935
|
+
const sheet = this.getters.getSheet(cmd.sheetIdTo);
|
|
52936
|
+
if (!sheet.isVisible) {
|
|
52937
|
+
return "SheetIsHidden" /* CommandResult.SheetIsHidden */;
|
|
52938
|
+
}
|
|
52614
52939
|
break;
|
|
52615
52940
|
}
|
|
52616
52941
|
catch (error) {
|
|
@@ -52859,52 +53184,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52859
53184
|
: this.getters.getNextVisibleCellPosition({ sheetId, col: 0, row: 0 });
|
|
52860
53185
|
}
|
|
52861
53186
|
}
|
|
52862
|
-
getStatisticFnResults() {
|
|
52863
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52864
|
-
const cells = new Set();
|
|
52865
|
-
for (const zone of this.gridSelection.zones) {
|
|
52866
|
-
for (const { col, row } of positions(zone)) {
|
|
52867
|
-
if (this.getters.isRowHidden(sheetId, row) || this.getters.isColHidden(sheetId, col)) {
|
|
52868
|
-
continue; // Skip hidden cells
|
|
52869
|
-
}
|
|
52870
|
-
const evaluatedCell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52871
|
-
if (evaluatedCell.type !== CellValueType.empty) {
|
|
52872
|
-
cells.add(evaluatedCell);
|
|
52873
|
-
}
|
|
52874
|
-
}
|
|
52875
|
-
}
|
|
52876
|
-
const locale = this.getters.getLocale();
|
|
52877
|
-
let statisticFnResults = {};
|
|
52878
|
-
for (let fn of selectionStatisticFunctions) {
|
|
52879
|
-
// We don't want to display statistical information when there is no interest:
|
|
52880
|
-
// We set the statistical result to undefined if the data handled by the selection
|
|
52881
|
-
// does not match the data handled by the function.
|
|
52882
|
-
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
52883
|
-
// be displayed as undefined rather than 0.
|
|
52884
|
-
let fnResult = undefined;
|
|
52885
|
-
const evaluatedCells = [...cells].filter((c) => fn.types.includes(c.type));
|
|
52886
|
-
if (evaluatedCells.length) {
|
|
52887
|
-
fnResult = fn.compute(evaluatedCells, locale);
|
|
52888
|
-
}
|
|
52889
|
-
statisticFnResults[fn.name] = fnResult;
|
|
52890
|
-
}
|
|
52891
|
-
return statisticFnResults;
|
|
52892
|
-
}
|
|
52893
|
-
getAggregate() {
|
|
52894
|
-
let aggregate = 0;
|
|
52895
|
-
let n = 0;
|
|
52896
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52897
|
-
const cellPositions = this.gridSelection.zones.map(positions).flat();
|
|
52898
|
-
for (const { col, row } of cellPositions) {
|
|
52899
|
-
const cell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52900
|
-
if (cell.type === CellValueType.number) {
|
|
52901
|
-
n++;
|
|
52902
|
-
aggregate += cell.value;
|
|
52903
|
-
}
|
|
52904
|
-
}
|
|
52905
|
-
const locale = this.getters.getLocale();
|
|
52906
|
-
return n < 2 ? null : formatValue(aggregate, { locale });
|
|
52907
|
-
}
|
|
52908
53187
|
isSelected(zone) {
|
|
52909
53188
|
return !!this.getters.getSelectedZones().find((z) => isEqual(z, zone));
|
|
52910
53189
|
}
|
|
@@ -52946,9 +53225,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52946
53225
|
// Other
|
|
52947
53226
|
// ---------------------------------------------------------------------------
|
|
52948
53227
|
activateSheet(sheetIdFrom, sheetIdTo) {
|
|
52949
|
-
if (!this.getters.isSheetVisible(sheetIdTo)) {
|
|
52950
|
-
this.dispatch("SHOW_SHEET", { sheetId: sheetIdTo });
|
|
52951
|
-
}
|
|
52952
53228
|
this.setActiveSheet(sheetIdTo);
|
|
52953
53229
|
this.sheetsData[sheetIdFrom] = {
|
|
52954
53230
|
gridSelection: deepCopy(this.gridSelection),
|
|
@@ -53955,7 +54231,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53955
54231
|
* column of the current viewport
|
|
53956
54232
|
*/
|
|
53957
54233
|
getColDimensionsInViewport(sheetId, col) {
|
|
53958
|
-
const left =
|
|
54234
|
+
const left = largeMin(this.getters.getSheetViewVisibleCols());
|
|
53959
54235
|
const start = this.getters.getColRowOffsetInViewport("COL", left, col);
|
|
53960
54236
|
const size = this.getters.getColSize(sheetId, col);
|
|
53961
54237
|
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
@@ -53970,7 +54246,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53970
54246
|
* of the current viewport
|
|
53971
54247
|
*/
|
|
53972
54248
|
getRowDimensionsInViewport(sheetId, row) {
|
|
53973
|
-
const top =
|
|
54249
|
+
const top = largeMin(this.getters.getSheetViewVisibleRows());
|
|
53974
54250
|
const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
|
|
53975
54251
|
const size = this.getters.getRowSize(sheetId, row);
|
|
53976
54252
|
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
@@ -54322,6 +54598,7 @@ const statefulUIPluginRegistry = new Registry()
|
|
|
54322
54598
|
.add("evaluation_filter", FilterEvaluationPlugin)
|
|
54323
54599
|
.add("header_visibility_ui", HeaderVisibilityUIPlugin)
|
|
54324
54600
|
.add("table_style", TableStylePlugin)
|
|
54601
|
+
.add("cell_computed_style", CellComputedStylePlugin)
|
|
54325
54602
|
.add("header_positions", HeaderPositionsUIPlugin)
|
|
54326
54603
|
.add("viewport", SheetViewPlugin)
|
|
54327
54604
|
.add("clipboard", ClipboardPlugin);
|
|
@@ -54382,6 +54659,38 @@ class ImageProvider {
|
|
|
54382
54659
|
}
|
|
54383
54660
|
}
|
|
54384
54661
|
|
|
54662
|
+
class ArrayFormulaHighlight extends SpreadsheetStore {
|
|
54663
|
+
highlightStore = this.get(HighlightStore);
|
|
54664
|
+
constructor(get) {
|
|
54665
|
+
super(get);
|
|
54666
|
+
this.highlightStore.register(this);
|
|
54667
|
+
}
|
|
54668
|
+
get highlights() {
|
|
54669
|
+
const zone = this.getHighlightZone();
|
|
54670
|
+
if (!zone) {
|
|
54671
|
+
return [];
|
|
54672
|
+
}
|
|
54673
|
+
const sheetId = this.model.getters.getActiveSheetId();
|
|
54674
|
+
return [
|
|
54675
|
+
{
|
|
54676
|
+
sheetId,
|
|
54677
|
+
zone,
|
|
54678
|
+
color: "#17A2B8",
|
|
54679
|
+
noFill: true,
|
|
54680
|
+
thinLine: true,
|
|
54681
|
+
},
|
|
54682
|
+
];
|
|
54683
|
+
}
|
|
54684
|
+
getHighlightZone() {
|
|
54685
|
+
const position = this.model.getters.getActivePosition();
|
|
54686
|
+
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
|
|
54687
|
+
const spreadZone = spreader
|
|
54688
|
+
? this.model.getters.getSpreadZone(spreader)
|
|
54689
|
+
: this.model.getters.getSpreadZone(position);
|
|
54690
|
+
return spreadZone;
|
|
54691
|
+
}
|
|
54692
|
+
}
|
|
54693
|
+
|
|
54385
54694
|
const RIPPLE_KEY_FRAMES = [
|
|
54386
54695
|
{ transform: "scale(0)" },
|
|
54387
54696
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -54684,12 +54993,14 @@ class BottomBarSheet extends owl.Component {
|
|
|
54684
54993
|
this.editionState = "initializing";
|
|
54685
54994
|
}
|
|
54686
54995
|
stopEdition() {
|
|
54687
|
-
|
|
54996
|
+
const input = this.sheetNameRef.el;
|
|
54997
|
+
if (!this.state.isEditing || !input)
|
|
54688
54998
|
return;
|
|
54689
54999
|
this.state.isEditing = false;
|
|
54690
55000
|
this.editionState = "initializing";
|
|
54691
|
-
|
|
55001
|
+
input.blur();
|
|
54692
55002
|
const inputValue = this.getInputContent() || "";
|
|
55003
|
+
input.innerText = inputValue;
|
|
54693
55004
|
interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
|
|
54694
55005
|
}
|
|
54695
55006
|
cancelEdition() {
|
|
@@ -54733,6 +55044,115 @@ class BottomBarSheet extends owl.Component {
|
|
|
54733
55044
|
}
|
|
54734
55045
|
}
|
|
54735
55046
|
|
|
55047
|
+
const selectionStatisticFunctions = [
|
|
55048
|
+
{
|
|
55049
|
+
name: _t("Sum"),
|
|
55050
|
+
types: [CellValueType.number],
|
|
55051
|
+
compute: (values, locale) => sum([[values]], locale),
|
|
55052
|
+
},
|
|
55053
|
+
{
|
|
55054
|
+
name: _t("Avg"),
|
|
55055
|
+
types: [CellValueType.number],
|
|
55056
|
+
compute: (values, locale) => average([[values]], locale),
|
|
55057
|
+
},
|
|
55058
|
+
{
|
|
55059
|
+
name: _t("Min"),
|
|
55060
|
+
types: [CellValueType.number],
|
|
55061
|
+
compute: (values, locale) => min([[values]], locale),
|
|
55062
|
+
},
|
|
55063
|
+
{
|
|
55064
|
+
name: _t("Max"),
|
|
55065
|
+
types: [CellValueType.number],
|
|
55066
|
+
compute: (values, locale) => max([[values]], locale),
|
|
55067
|
+
},
|
|
55068
|
+
{
|
|
55069
|
+
name: _t("Count"),
|
|
55070
|
+
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
55071
|
+
compute: (values) => countAny([[values]]),
|
|
55072
|
+
},
|
|
55073
|
+
{
|
|
55074
|
+
name: _t("Count Numbers"),
|
|
55075
|
+
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
55076
|
+
compute: (values, locale) => countNumbers([[values]], locale),
|
|
55077
|
+
},
|
|
55078
|
+
];
|
|
55079
|
+
class AggregateStatisticsStore extends SpreadsheetStore {
|
|
55080
|
+
statisticFnResults = this._computeStatisticFnResults();
|
|
55081
|
+
isDirty = false;
|
|
55082
|
+
constructor(get) {
|
|
55083
|
+
super(get);
|
|
55084
|
+
this.model.selection.observe(this, {
|
|
55085
|
+
handleEvent: this.handleEvent.bind(this),
|
|
55086
|
+
});
|
|
55087
|
+
}
|
|
55088
|
+
handle(cmd) {
|
|
55089
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
55090
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd)) {
|
|
55091
|
+
this.isDirty = true;
|
|
55092
|
+
}
|
|
55093
|
+
switch (cmd.type) {
|
|
55094
|
+
case "HIDE_COLUMNS_ROWS":
|
|
55095
|
+
case "UNHIDE_COLUMNS_ROWS":
|
|
55096
|
+
case "GROUP_HEADERS":
|
|
55097
|
+
case "UNGROUP_HEADERS":
|
|
55098
|
+
case "ACTIVATE_SHEET":
|
|
55099
|
+
case "ACTIVATE_NEXT_SHEET":
|
|
55100
|
+
case "ACTIVATE_PREVIOUS_SHEET":
|
|
55101
|
+
case "EVALUATE_CELLS":
|
|
55102
|
+
case "UNDO":
|
|
55103
|
+
case "REDO":
|
|
55104
|
+
this.isDirty = true;
|
|
55105
|
+
}
|
|
55106
|
+
}
|
|
55107
|
+
finalize() {
|
|
55108
|
+
if (this.isDirty) {
|
|
55109
|
+
this.isDirty = false;
|
|
55110
|
+
this.statisticFnResults = this._computeStatisticFnResults();
|
|
55111
|
+
}
|
|
55112
|
+
}
|
|
55113
|
+
handleEvent() {
|
|
55114
|
+
if (this.getters.isGridSelectionActive()) {
|
|
55115
|
+
this.statisticFnResults = this._computeStatisticFnResults();
|
|
55116
|
+
}
|
|
55117
|
+
}
|
|
55118
|
+
_computeStatisticFnResults() {
|
|
55119
|
+
const getters = this.getters;
|
|
55120
|
+
const sheetId = getters.getActiveSheetId();
|
|
55121
|
+
const cells = new Set();
|
|
55122
|
+
const zones = getters.getSelectedZones();
|
|
55123
|
+
for (const zone of zones) {
|
|
55124
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
55125
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
55126
|
+
if (getters.isRowHidden(sheetId, row) || getters.isColHidden(sheetId, col)) {
|
|
55127
|
+
continue; // Skip hidden cells
|
|
55128
|
+
}
|
|
55129
|
+
const evaluatedCell = getters.getEvaluatedCell({ sheetId, col, row });
|
|
55130
|
+
if (evaluatedCell.type !== CellValueType.empty) {
|
|
55131
|
+
cells.add(evaluatedCell);
|
|
55132
|
+
}
|
|
55133
|
+
}
|
|
55134
|
+
}
|
|
55135
|
+
}
|
|
55136
|
+
const locale = getters.getLocale();
|
|
55137
|
+
let statisticFnResults = {};
|
|
55138
|
+
const cellsArray = [...cells];
|
|
55139
|
+
for (let fn of selectionStatisticFunctions) {
|
|
55140
|
+
// We don't want to display statistical information when there is no interest:
|
|
55141
|
+
// We set the statistical result to undefined if the data handled by the selection
|
|
55142
|
+
// does not match the data handled by the function.
|
|
55143
|
+
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
55144
|
+
// be displayed as undefined rather than 0.
|
|
55145
|
+
let fnResult = undefined;
|
|
55146
|
+
const evaluatedCells = cellsArray.filter((c) => fn.types.includes(c.type));
|
|
55147
|
+
if (evaluatedCells.length) {
|
|
55148
|
+
fnResult = fn.compute(evaluatedCells, locale);
|
|
55149
|
+
}
|
|
55150
|
+
statisticFnResults[fn.name] = fnResult;
|
|
55151
|
+
}
|
|
55152
|
+
return statisticFnResults;
|
|
55153
|
+
}
|
|
55154
|
+
}
|
|
55155
|
+
|
|
54736
55156
|
// -----------------------------------------------------------------------------
|
|
54737
55157
|
// SpreadSheet
|
|
54738
55158
|
// -----------------------------------------------------------------------------
|
|
@@ -54748,40 +55168,38 @@ css /* scss */ `
|
|
|
54748
55168
|
}
|
|
54749
55169
|
`;
|
|
54750
55170
|
class BottomBarStatistic extends owl.Component {
|
|
54751
|
-
static template = "o-spreadsheet-
|
|
55171
|
+
static template = "o-spreadsheet-BottomBarStatistic";
|
|
54752
55172
|
static props = {
|
|
54753
55173
|
openContextMenu: Function,
|
|
54754
55174
|
closeContextMenu: Function,
|
|
54755
55175
|
};
|
|
54756
55176
|
static components = { Ripple };
|
|
54757
55177
|
selectedStatisticFn = "";
|
|
54758
|
-
|
|
55178
|
+
store;
|
|
54759
55179
|
setup() {
|
|
54760
|
-
this.
|
|
55180
|
+
this.store = useStore(AggregateStatisticsStore);
|
|
54761
55181
|
owl.onWillUpdateProps(() => {
|
|
54762
|
-
|
|
54763
|
-
if (!deepEquals(newStatisticFnResults, this.statisticFnResults)) {
|
|
55182
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54764
55183
|
this.props.closeContextMenu();
|
|
54765
55184
|
}
|
|
54766
|
-
this.statisticFnResults = newStatisticFnResults;
|
|
54767
55185
|
});
|
|
54768
55186
|
}
|
|
54769
55187
|
getSelectedStatistic() {
|
|
54770
55188
|
// don't display button if no function has a result
|
|
54771
|
-
if (Object.values(this.statisticFnResults).every((result) => result === undefined)) {
|
|
55189
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54772
55190
|
return undefined;
|
|
54773
55191
|
}
|
|
54774
55192
|
if (this.selectedStatisticFn === "") {
|
|
54775
|
-
this.selectedStatisticFn = Object.keys(this.statisticFnResults)[0];
|
|
55193
|
+
this.selectedStatisticFn = Object.keys(this.store.statisticFnResults)[0];
|
|
54776
55194
|
}
|
|
54777
|
-
return this.getComposedFnName(this.selectedStatisticFn
|
|
55195
|
+
return this.getComposedFnName(this.selectedStatisticFn);
|
|
54778
55196
|
}
|
|
54779
55197
|
listSelectionStatistics(ev) {
|
|
54780
55198
|
const registry = new MenuItemRegistry();
|
|
54781
55199
|
let i = 0;
|
|
54782
|
-
for (let [fnName
|
|
55200
|
+
for (let [fnName] of Object.entries(this.store.statisticFnResults)) {
|
|
54783
55201
|
registry.add(fnName, {
|
|
54784
|
-
name: this.getComposedFnName(fnName
|
|
55202
|
+
name: () => this.getComposedFnName(fnName),
|
|
54785
55203
|
sequence: i,
|
|
54786
55204
|
isReadonlyAllowed: true,
|
|
54787
55205
|
execute: () => {
|
|
@@ -54794,8 +55212,9 @@ class BottomBarStatistic extends owl.Component {
|
|
|
54794
55212
|
const { top, left, width } = target.getBoundingClientRect();
|
|
54795
55213
|
this.props.openContextMenu(left + width, top, registry);
|
|
54796
55214
|
}
|
|
54797
|
-
getComposedFnName(fnName
|
|
55215
|
+
getComposedFnName(fnName) {
|
|
54798
55216
|
const locale = this.env.model.getters.getLocale();
|
|
55217
|
+
const fnValue = this.store.statisticFnResults[fnName];
|
|
54799
55218
|
return fnName + ": " + (fnValue !== undefined ? formatValue(fnValue, { locale }) : "__");
|
|
54800
55219
|
}
|
|
54801
55220
|
}
|
|
@@ -54904,10 +55323,14 @@ class BottomBar extends owl.Component {
|
|
|
54904
55323
|
name: sheet.name,
|
|
54905
55324
|
sequence: i,
|
|
54906
55325
|
isReadonlyAllowed: true,
|
|
54907
|
-
textColor: sheet.isVisible ? undefined : "
|
|
55326
|
+
textColor: sheet.isVisible ? undefined : "#808080",
|
|
54908
55327
|
execute: (env) => {
|
|
55328
|
+
if (!this.env.model.getters.isSheetVisible(sheetId)) {
|
|
55329
|
+
this.env.model.dispatch("SHOW_SHEET", { sheetId });
|
|
55330
|
+
}
|
|
54909
55331
|
env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: from, sheetIdTo: sheetId });
|
|
54910
55332
|
},
|
|
55333
|
+
isEnabled: (env) => (env.model.getters.isReadonly() ? sheet.isVisible : true),
|
|
54911
55334
|
});
|
|
54912
55335
|
i++;
|
|
54913
55336
|
}
|
|
@@ -54980,7 +55403,7 @@ class BottomBar extends owl.Component {
|
|
|
54980
55403
|
this.sheetListRef.el.scrollTo({ top: 0, left: scroll, behavior: "smooth" });
|
|
54981
55404
|
}
|
|
54982
55405
|
onSheetMouseDown(sheetId, event) {
|
|
54983
|
-
if (event.button !== 0)
|
|
55406
|
+
if (event.button !== 0 || this.env.model.getters.isReadonly())
|
|
54984
55407
|
return;
|
|
54985
55408
|
this.closeMenu();
|
|
54986
55409
|
const visibleSheets = this.getVisibleSheets();
|
|
@@ -55016,7 +55439,7 @@ class BottomBar extends owl.Component {
|
|
|
55016
55439
|
.map((sheetEl) => sheetEl.getBoundingClientRect())
|
|
55017
55440
|
.map((rect) => ({
|
|
55018
55441
|
x: rect.x,
|
|
55019
|
-
width: rect.width - 1,
|
|
55442
|
+
width: rect.width - 1, // -1 to compensate negative margin
|
|
55020
55443
|
y: rect.y,
|
|
55021
55444
|
height: rect.height,
|
|
55022
55445
|
}));
|
|
@@ -55243,7 +55666,7 @@ class RowGroup extends AbstractHeaderGroup {
|
|
|
55243
55666
|
}
|
|
55244
55667
|
return cssPropertiesToCss({
|
|
55245
55668
|
top: `${groupBox.headerRect.height / 2}px`,
|
|
55246
|
-
left: `calc(50% - 1px)`,
|
|
55669
|
+
left: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55247
55670
|
width: `30%`,
|
|
55248
55671
|
height: `calc(100% - ${groupBox.headerRect.height / 2}px)`,
|
|
55249
55672
|
"border-left": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
|
|
@@ -55295,7 +55718,7 @@ class ColGroup extends AbstractHeaderGroup {
|
|
|
55295
55718
|
return "";
|
|
55296
55719
|
}
|
|
55297
55720
|
return cssPropertiesToCss({
|
|
55298
|
-
top: `calc(50% - 1px)`,
|
|
55721
|
+
top: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55299
55722
|
left: `${groupBox.headerRect.width / 2}px`,
|
|
55300
55723
|
width: `calc(100% - ${groupBox.headerRect.width / 2}px)`,
|
|
55301
55724
|
height: `30%`,
|
|
@@ -56386,7 +56809,7 @@ css /* scss */ `
|
|
|
56386
56809
|
}
|
|
56387
56810
|
.o-disabled {
|
|
56388
56811
|
opacity: 0.4;
|
|
56389
|
-
|
|
56812
|
+
cursor: default;
|
|
56390
56813
|
pointer-events: none;
|
|
56391
56814
|
}
|
|
56392
56815
|
|
|
@@ -56552,7 +56975,7 @@ css /* scss */ `
|
|
|
56552
56975
|
|
|
56553
56976
|
.o-number-input {
|
|
56554
56977
|
/* Remove number input arrows */
|
|
56555
|
-
|
|
56978
|
+
appearance: textfield;
|
|
56556
56979
|
&::-webkit-outer-spin-button,
|
|
56557
56980
|
&::-webkit-inner-spin-button {
|
|
56558
56981
|
-webkit-appearance: none;
|
|
@@ -56595,6 +57018,7 @@ class Spreadsheet extends owl.Component {
|
|
|
56595
57018
|
this.notificationStore = useStore(NotificationStore);
|
|
56596
57019
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
56597
57020
|
this.sidePanel = useStore(SidePanelStore);
|
|
57021
|
+
useStore(ArrayFormulaHighlight);
|
|
56598
57022
|
this.keyDownMapping = {
|
|
56599
57023
|
"CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
56600
57024
|
"CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
@@ -56697,7 +57121,7 @@ class Spreadsheet extends owl.Component {
|
|
|
56697
57121
|
const gridColSize = GROUP_LAYER_WIDTH * this.rowLayers.length;
|
|
56698
57122
|
const gridRowSize = GROUP_LAYER_WIDTH * this.colLayers.length;
|
|
56699
57123
|
return cssPropertiesToCss({
|
|
56700
|
-
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`,
|
|
57124
|
+
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`, // +2: margins
|
|
56701
57125
|
"grid-template-rows": `${gridRowSize ? gridRowSize + 2 : 0}px auto`,
|
|
56702
57126
|
});
|
|
56703
57127
|
}
|
|
@@ -57429,14 +57853,6 @@ class SelectiveHistory {
|
|
|
57429
57853
|
this.revertBefore(operationId);
|
|
57430
57854
|
this.tree.drop(operationId);
|
|
57431
57855
|
}
|
|
57432
|
-
getRevertedExecution() {
|
|
57433
|
-
const data = [];
|
|
57434
|
-
const operations = this.tree.revertedExecution(this.HEAD_BRANCH);
|
|
57435
|
-
for (const { operation } of operations) {
|
|
57436
|
-
data.push(operation.data);
|
|
57437
|
-
}
|
|
57438
|
-
return data;
|
|
57439
|
-
}
|
|
57440
57856
|
/**
|
|
57441
57857
|
* Revert the state as it was *before* the given operation was executed.
|
|
57442
57858
|
*/
|
|
@@ -58225,6 +58641,9 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
58225
58641
|
case "bar":
|
|
58226
58642
|
plot = addBarChart(chart.data);
|
|
58227
58643
|
break;
|
|
58644
|
+
case "combo":
|
|
58645
|
+
plot = addComboChart(chart.data);
|
|
58646
|
+
break;
|
|
58228
58647
|
case "line":
|
|
58229
58648
|
plot = addLineChart(chart.data);
|
|
58230
58649
|
break;
|
|
@@ -58387,6 +58806,79 @@ function addBarChart(chart) {
|
|
|
58387
58806
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58388
58807
|
`;
|
|
58389
58808
|
}
|
|
58809
|
+
function addComboChart(chart) {
|
|
58810
|
+
// gapWitdh and overlap that define the space between clusters (in %) and the overlap between datasets (from -100: completely scattered to 100, completely overlapped)
|
|
58811
|
+
// see gapWidth : https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_gapWidth_topic_ID0EFVEQB.html#topic_ID0EFVEQB
|
|
58812
|
+
// see overlap : https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_overlap_topic_ID0ELYQQB.html#topic_ID0ELYQQB
|
|
58813
|
+
//
|
|
58814
|
+
// overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
|
|
58815
|
+
// See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
|
|
58816
|
+
const colors = new ChartColors();
|
|
58817
|
+
const dataSetsNodes = [];
|
|
58818
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
58819
|
+
const color = toXlsxHexColor(colors.next());
|
|
58820
|
+
const dataShapeProperty = shapeProperty({
|
|
58821
|
+
backgroundColor: color,
|
|
58822
|
+
line: { color },
|
|
58823
|
+
});
|
|
58824
|
+
dataSetsNodes.push(dsIndex === "0"
|
|
58825
|
+
? escapeXml /*xml*/ `
|
|
58826
|
+
<c:ser>
|
|
58827
|
+
<c:idx val="${dsIndex}"/>
|
|
58828
|
+
<c:order val="${dsIndex}"/>
|
|
58829
|
+
${dataset.label ? escapeXml /*xml*/ `<c:tx>${stringRef(dataset.label)}</c:tx>` : ""}
|
|
58830
|
+
${dataShapeProperty}
|
|
58831
|
+
${chart.labelRange ? escapeXml /*xml*/ `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
58832
|
+
<c:val> <!-- x-coordinate values -->
|
|
58833
|
+
${numberRef(dataset.range)}
|
|
58834
|
+
</c:val>
|
|
58835
|
+
</c:ser>
|
|
58836
|
+
`
|
|
58837
|
+
: escapeXml /*xml*/ `
|
|
58838
|
+
<c:ser>
|
|
58839
|
+
<c:idx val="${dsIndex}"/>
|
|
58840
|
+
<c:order val="${dsIndex}"/>
|
|
58841
|
+
<c:smooth val="0"/>
|
|
58842
|
+
<c:marker>
|
|
58843
|
+
<c:symbol val="circle" />
|
|
58844
|
+
<c:size val="5"/>
|
|
58845
|
+
</c:marker>
|
|
58846
|
+
${dataset.label ? escapeXml `<c:tx>${stringRef(dataset.label)}</c:tx>` : ""}
|
|
58847
|
+
${dataShapeProperty}
|
|
58848
|
+
${chart.labelRange ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
58849
|
+
<c:val> <!-- x-coordinate values -->
|
|
58850
|
+
${numberRef(dataset.range)}
|
|
58851
|
+
</c:val>
|
|
58852
|
+
</c:ser>
|
|
58853
|
+
`);
|
|
58854
|
+
}
|
|
58855
|
+
// Excel does not support this feature
|
|
58856
|
+
const axisPos = chart.verticalAxisPosition === "left" ? "l" : "r";
|
|
58857
|
+
const overlap = chart.stacked ? 100 : -20;
|
|
58858
|
+
return escapeXml /*xml*/ `
|
|
58859
|
+
<c:barChart>
|
|
58860
|
+
<c:barDir val="col"/>
|
|
58861
|
+
<c:grouping val="clustered"/>
|
|
58862
|
+
<c:overlap val="${overlap}"/>
|
|
58863
|
+
<c:gapWidth val="70"/>
|
|
58864
|
+
<!-- each data marker in the series does not have a different color -->
|
|
58865
|
+
<c:varyColors val="0"/>
|
|
58866
|
+
${dataSetsNodes[0]}
|
|
58867
|
+
<c:axId val="${catAxId}" />
|
|
58868
|
+
<c:axId val="${valAxId}" />
|
|
58869
|
+
</c:barChart>
|
|
58870
|
+
<c:lineChart>
|
|
58871
|
+
<c:grouping val="standard"/>
|
|
58872
|
+
<!-- each data marker in the series does not have a different color -->
|
|
58873
|
+
<c:varyColors val="0"/>
|
|
58874
|
+
${joinXmlNodes(dataSetsNodes.slice(1))}
|
|
58875
|
+
<c:axId val="${catAxId}" />
|
|
58876
|
+
<c:axId val="${valAxId}" />
|
|
58877
|
+
</c:lineChart>
|
|
58878
|
+
${addAx("b", "c:catAx", catAxId, valAxId, { fontColor: chart.fontColor })}
|
|
58879
|
+
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58880
|
+
`;
|
|
58881
|
+
}
|
|
58390
58882
|
function addLineChart(chart) {
|
|
58391
58883
|
const colors = new ChartColors();
|
|
58392
58884
|
const dataSetsNodes = [];
|
|
@@ -58434,7 +58926,7 @@ function addLineChart(chart) {
|
|
|
58434
58926
|
}
|
|
58435
58927
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
58436
58928
|
const colors = new ChartColors();
|
|
58437
|
-
const maxLength =
|
|
58929
|
+
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
58438
58930
|
const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
|
|
58439
58931
|
const dataSetsNodes = [];
|
|
58440
58932
|
for (const [dsIndex, dataset] of Object.entries(chart.dataSets).reverse()) {
|
|
@@ -59312,7 +59804,7 @@ function addTableColumns(table, sheetData) {
|
|
|
59312
59804
|
const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
|
|
59313
59805
|
const colName = sheetData.cells[colHeaderXc]?.content || `col${i}`;
|
|
59314
59806
|
const colAttributes = [
|
|
59315
|
-
["id", i + 1],
|
|
59807
|
+
["id", i + 1], // id cannot be 0
|
|
59316
59808
|
["name", colName],
|
|
59317
59809
|
];
|
|
59318
59810
|
columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
|
|
@@ -59528,6 +60020,7 @@ function addSheetViews(sheet) {
|
|
|
59528
60020
|
* https://www.ecma-international.org/publications-and-standards/standards/ecma-376/
|
|
59529
60021
|
*/
|
|
59530
60022
|
function getXLSX(data) {
|
|
60023
|
+
data = fixLengthySheetNames(data);
|
|
59531
60024
|
const files = [];
|
|
59532
60025
|
const construct = getDefaultXLSXStructure();
|
|
59533
60026
|
files.push(createWorkbook(data, construct));
|
|
@@ -59775,6 +60268,40 @@ function createRelRoot() {
|
|
|
59775
60268
|
`;
|
|
59776
60269
|
return createXMLFile(parseXML(xml), "_rels/.rels");
|
|
59777
60270
|
}
|
|
60271
|
+
/**
|
|
60272
|
+
* Excel sheet names are maximum 31 characters while o-spreadsheet do not have this limit.
|
|
60273
|
+
* This method converts the sheet names to be within the 31 characters limit.
|
|
60274
|
+
* The cells/charts referencing this sheet will be updated accordingly.
|
|
60275
|
+
*/
|
|
60276
|
+
function fixLengthySheetNames(data) {
|
|
60277
|
+
const nameMapping = {};
|
|
60278
|
+
const newNames = new Set();
|
|
60279
|
+
for (const sheet of data.sheets) {
|
|
60280
|
+
let newName = sheet.name.slice(0, 31);
|
|
60281
|
+
let i = 1;
|
|
60282
|
+
while (newNames.has(newName)) {
|
|
60283
|
+
newName = newName.slice(0, 31 - String(i).length) + i++;
|
|
60284
|
+
}
|
|
60285
|
+
newNames.add(newName);
|
|
60286
|
+
if (newName !== sheet.name) {
|
|
60287
|
+
nameMapping[sheet.name] = newName;
|
|
60288
|
+
sheet.name = newName;
|
|
60289
|
+
}
|
|
60290
|
+
}
|
|
60291
|
+
if (!Object.keys(nameMapping).length) {
|
|
60292
|
+
return data;
|
|
60293
|
+
}
|
|
60294
|
+
const sheetWithNewNames = Object.keys(nameMapping).sort((a, b) => b.length - a.length);
|
|
60295
|
+
let stringifiedData = JSON.stringify(data);
|
|
60296
|
+
for (const sheetName of sheetWithNewNames) {
|
|
60297
|
+
const regex = new RegExp(`'?${escapeRegExp(sheetName)}'?!`, "g");
|
|
60298
|
+
stringifiedData = stringifiedData.replaceAll(regex, (match) => {
|
|
60299
|
+
const newName = nameMapping[sheetName];
|
|
60300
|
+
return match.replace(sheetName, newName);
|
|
60301
|
+
});
|
|
60302
|
+
}
|
|
60303
|
+
return JSON.parse(stringifiedData);
|
|
60304
|
+
}
|
|
59778
60305
|
|
|
59779
60306
|
var Status;
|
|
59780
60307
|
(function (Status) {
|
|
@@ -60464,6 +60991,6 @@ exports.tokenColors = tokenColors;
|
|
|
60464
60991
|
exports.tokenize = tokenize;
|
|
60465
60992
|
|
|
60466
60993
|
|
|
60467
|
-
__info__.version = "17.3.0-alpha.
|
|
60468
|
-
__info__.date = "2024-
|
|
60469
|
-
__info__.hash = "
|
|
60994
|
+
__info__.version = "17.3.0-alpha.2";
|
|
60995
|
+
__info__.date = "2024-04-05T14:01:07.060Z";
|
|
60996
|
+
__info__.hash = "8c5a229";
|