@odoo/o-spreadsheet 17.3.0-alpha.0 → 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 +1427 -603
- package/dist/o-spreadsheet.d.ts +122 -72
- package/dist/o-spreadsheet.esm.js +1427 -603
- package/dist/o-spreadsheet.iife.js +1427 -603
- package/dist/o-spreadsheet.iife.min.js +391 -341
- package/dist/o_spreadsheet.xml +156 -62
- 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';
|
|
@@ -31,7 +31,6 @@ const DEFAULT_COLOR_SCALE_MIDPOINT_COLOR = 0xb6d7a8;
|
|
|
31
31
|
const LINK_COLOR = "#017E84";
|
|
32
32
|
const FILTERS_COLOR = "#188038";
|
|
33
33
|
const BACKGROUND_HEADER_FILTER_COLOR = "#E6F4EA";
|
|
34
|
-
const BACKGROUND_HEADER_SELECTED_FILTER_COLOR = "#CEEAD6";
|
|
35
34
|
const SEPARATOR_COLOR = "#E0E2E4";
|
|
36
35
|
const ICONS_COLOR = "#4A4F59";
|
|
37
36
|
const HEADER_GROUPING_BACKGROUND_COLOR = "#F5F5F5";
|
|
@@ -467,7 +466,7 @@ function getItemId(item, itemsDic) {
|
|
|
467
466
|
}
|
|
468
467
|
// Generate new Id if the item didn't exist in the dictionary
|
|
469
468
|
const ids = Object.keys(itemsDic);
|
|
470
|
-
const maxId = ids.length === 0 ? 0 :
|
|
469
|
+
const maxId = ids.length === 0 ? 0 : largeMax(ids.map((id) => parseInt(id, 10)));
|
|
471
470
|
itemsDic[maxId + 1] = item;
|
|
472
471
|
return maxId + 1;
|
|
473
472
|
}
|
|
@@ -485,7 +484,7 @@ function debounce(func, wait, immediate) {
|
|
|
485
484
|
let timeout = undefined;
|
|
486
485
|
const debounced = function () {
|
|
487
486
|
const context = this;
|
|
488
|
-
const args = arguments;
|
|
487
|
+
const args = Array.from(arguments);
|
|
489
488
|
function later() {
|
|
490
489
|
timeout = undefined;
|
|
491
490
|
if (!immediate) {
|
|
@@ -687,6 +686,34 @@ function getSearchRegex(searchStr, searchOptions) {
|
|
|
687
686
|
}
|
|
688
687
|
return RegExp(searchValue, flags);
|
|
689
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
|
+
}
|
|
690
717
|
|
|
691
718
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
692
719
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
@@ -2089,6 +2116,7 @@ exports.CommandResult = void 0;
|
|
|
2089
2116
|
CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
|
|
2090
2117
|
CommandResult["NoChanges"] = "NoChanges";
|
|
2091
2118
|
CommandResult["InvalidInputId"] = "InvalidInputId";
|
|
2119
|
+
CommandResult["SheetIsHidden"] = "SheetIsHidden";
|
|
2092
2120
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
2093
2121
|
|
|
2094
2122
|
const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
|
|
@@ -4204,6 +4232,10 @@ function organizeZone(zone) {
|
|
|
4204
4232
|
function positionToZone(position) {
|
|
4205
4233
|
return { left: position.col, right: position.col, top: position.row, bottom: position.row };
|
|
4206
4234
|
}
|
|
4235
|
+
/** Transform a zone into a zone with only its top-left position */
|
|
4236
|
+
function zoneToTopLeft(zone) {
|
|
4237
|
+
return { ...zone, right: zone.left, bottom: zone.top };
|
|
4238
|
+
}
|
|
4207
4239
|
function isFullRow(zone) {
|
|
4208
4240
|
return zone.right === undefined;
|
|
4209
4241
|
}
|
|
@@ -4243,6 +4275,16 @@ function getZonesRows(zones) {
|
|
|
4243
4275
|
}
|
|
4244
4276
|
return set;
|
|
4245
4277
|
}
|
|
4278
|
+
function unionPositionsToZone(positions) {
|
|
4279
|
+
const zone = { top: Infinity, left: Infinity, bottom: -Infinity, right: -Infinity };
|
|
4280
|
+
for (const { col, row } of positions) {
|
|
4281
|
+
zone.top = Math.min(zone.top, row);
|
|
4282
|
+
zone.left = Math.min(zone.left, col);
|
|
4283
|
+
zone.bottom = Math.max(zone.bottom, row);
|
|
4284
|
+
zone.right = Math.max(zone.right, col);
|
|
4285
|
+
}
|
|
4286
|
+
return zone;
|
|
4287
|
+
}
|
|
4246
4288
|
|
|
4247
4289
|
class RangeImpl {
|
|
4248
4290
|
getSheetSize;
|
|
@@ -4546,8 +4588,9 @@ function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
|
4546
4588
|
* Get the default height of the cell given its style.
|
|
4547
4589
|
*/
|
|
4548
4590
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4549
|
-
if (!cell || !cell.content)
|
|
4591
|
+
if (!cell || (!cell.isFormula && !cell.content)) {
|
|
4550
4592
|
return DEFAULT_CELL_HEIGHT;
|
|
4593
|
+
}
|
|
4551
4594
|
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4552
4595
|
const numberOfLines = cell.isFormula
|
|
4553
4596
|
? 1
|
|
@@ -5572,7 +5615,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
5572
5615
|
const spreader = this.getters.getArrayFormulaSpreadingOn(position);
|
|
5573
5616
|
let cell = this.getters.getCell(position);
|
|
5574
5617
|
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
5575
|
-
if (spreader) {
|
|
5618
|
+
if (spreader && !deepEquals(spreader, position)) {
|
|
5576
5619
|
const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
|
|
5577
5620
|
const content = isSpreaderCopied
|
|
5578
5621
|
? ""
|
|
@@ -6219,19 +6262,25 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6219
6262
|
tableCellsInRow.push({});
|
|
6220
6263
|
continue;
|
|
6221
6264
|
}
|
|
6265
|
+
const coreTable = this.getters.getCoreTable(position);
|
|
6266
|
+
const tableZone = coreTable?.range.zone;
|
|
6222
6267
|
// Copy whole table
|
|
6223
|
-
if (zones.some((z) => isZoneInside(
|
|
6224
|
-
copiedTablesIds.add(
|
|
6268
|
+
if (coreTable && tableZone && zones.some((z) => isZoneInside(tableZone, z))) {
|
|
6269
|
+
copiedTablesIds.add(coreTable.id);
|
|
6225
6270
|
const values = [];
|
|
6226
|
-
for (const col of range(
|
|
6227
|
-
values.push(this.getters.getFilterHiddenValues({ sheetId, col, row:
|
|
6271
|
+
for (const col of range(tableZone.left, tableZone.right + 1)) {
|
|
6272
|
+
values.push(this.getters.getFilterHiddenValues({ sheetId, col, row: tableZone.top }));
|
|
6228
6273
|
}
|
|
6229
6274
|
tableCellsInRow.push({
|
|
6230
|
-
table: {
|
|
6275
|
+
table: {
|
|
6276
|
+
range: coreTable.range,
|
|
6277
|
+
config: coreTable.config,
|
|
6278
|
+
type: coreTable.type,
|
|
6279
|
+
},
|
|
6231
6280
|
});
|
|
6232
6281
|
}
|
|
6233
6282
|
// Copy only style of cell
|
|
6234
|
-
else {
|
|
6283
|
+
else if (table) {
|
|
6235
6284
|
tableCellsInRow.push({ style: this.getTableStyleToCopy(position) });
|
|
6236
6285
|
}
|
|
6237
6286
|
}
|
|
@@ -6302,7 +6351,7 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6302
6351
|
}
|
|
6303
6352
|
pasteTableCell(sheetId, tableCell, position, options) {
|
|
6304
6353
|
if (tableCell.table && !options?.pasteOption) {
|
|
6305
|
-
const { range: tableRange
|
|
6354
|
+
const { range: tableRange } = tableCell.table;
|
|
6306
6355
|
const zoneDims = zoneToDimension(tableRange.zone);
|
|
6307
6356
|
const newTableZone = {
|
|
6308
6357
|
left: position.col,
|
|
@@ -6314,18 +6363,13 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6314
6363
|
sheetId: position.sheetId,
|
|
6315
6364
|
ranges: [this.getters.getRangeDataFromZone(sheetId, newTableZone)],
|
|
6316
6365
|
config: tableCell.table.config,
|
|
6366
|
+
tableType: tableCell.table.type,
|
|
6317
6367
|
});
|
|
6318
|
-
for (const i of range(0, filtersValues.length)) {
|
|
6319
|
-
this.dispatch("UPDATE_FILTER", {
|
|
6320
|
-
sheetId: position.sheetId,
|
|
6321
|
-
col: newTableZone.left + i,
|
|
6322
|
-
row: newTableZone.top,
|
|
6323
|
-
hiddenValues: filtersValues[i],
|
|
6324
|
-
});
|
|
6325
|
-
}
|
|
6326
6368
|
}
|
|
6327
6369
|
// Do not paste table style if we're inside another table
|
|
6328
|
-
|
|
6370
|
+
// We cannot check for dynamic tables, because at this point the paste can have changed the evaluation, and the
|
|
6371
|
+
// dynamic tables are not yet computed
|
|
6372
|
+
if (!this.getters.getCoreTable(position)) {
|
|
6329
6373
|
if (tableCell.style?.style && options?.pasteOption !== "asValue") {
|
|
6330
6374
|
this.dispatch("UPDATE_CELL", { ...position, style: tableCell.style.style });
|
|
6331
6375
|
}
|
|
@@ -6980,10 +7024,17 @@ urlRegistry.add("sheet_URL", {
|
|
|
6980
7024
|
},
|
|
6981
7025
|
open(url, env) {
|
|
6982
7026
|
const sheetId = parseSheetUrl(url);
|
|
6983
|
-
env.model.dispatch("ACTIVATE_SHEET", {
|
|
7027
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
6984
7028
|
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
6985
7029
|
sheetIdTo: sheetId,
|
|
6986
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
|
+
}
|
|
6987
7038
|
},
|
|
6988
7039
|
sequence: 0,
|
|
6989
7040
|
});
|
|
@@ -7099,7 +7150,7 @@ function textCell(value, format, formattedValue) {
|
|
|
7099
7150
|
}
|
|
7100
7151
|
function numberCell(value, format, formattedValue) {
|
|
7101
7152
|
return {
|
|
7102
|
-
value: value || 0,
|
|
7153
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
7103
7154
|
format,
|
|
7104
7155
|
formattedValue,
|
|
7105
7156
|
type: CellValueType.number,
|
|
@@ -7427,9 +7478,11 @@ const TableTerms = {
|
|
|
7427
7478
|
bandedColumns: _t("Banded columns"),
|
|
7428
7479
|
automaticAutofill: _t("Automatically autofill formulas"),
|
|
7429
7480
|
totalRow: _t("Total row"),
|
|
7481
|
+
isDynamic: _t("Auto-adjust to formula result"),
|
|
7430
7482
|
},
|
|
7431
7483
|
Tooltips: {
|
|
7432
7484
|
filterWithoutHeader: _t("Cannot have filters without a header row"),
|
|
7485
|
+
isDynamic: _t("For tables based on array formulas only"),
|
|
7433
7486
|
},
|
|
7434
7487
|
};
|
|
7435
7488
|
|
|
@@ -8005,6 +8058,9 @@ class DependencyContainer {
|
|
|
8005
8058
|
instantiate(Store, ...args) {
|
|
8006
8059
|
return this.factory.build(Store, ...args);
|
|
8007
8060
|
}
|
|
8061
|
+
resetStores() {
|
|
8062
|
+
this.dependencies.clear();
|
|
8063
|
+
}
|
|
8008
8064
|
}
|
|
8009
8065
|
class StoreFactory {
|
|
8010
8066
|
get;
|
|
@@ -9938,11 +9994,11 @@ autoCompleteProviders.add("dataValidation", {
|
|
|
9938
9994
|
}
|
|
9939
9995
|
else {
|
|
9940
9996
|
const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
|
|
9941
|
-
values = this.getters
|
|
9997
|
+
values = Array.from(new Set(this.getters
|
|
9942
9998
|
.getRangeValues(range)
|
|
9943
9999
|
.filter(isNotNull)
|
|
9944
10000
|
.map((value) => value.toString())
|
|
9945
|
-
.filter((val) => val !== "");
|
|
10001
|
+
.filter((val) => val !== "")));
|
|
9946
10002
|
}
|
|
9947
10003
|
return values.map((value) => ({ text: value }));
|
|
9948
10004
|
},
|
|
@@ -19389,10 +19445,10 @@ function aggregateDataForLabels(labels, datasets) {
|
|
|
19389
19445
|
}
|
|
19390
19446
|
}
|
|
19391
19447
|
return {
|
|
19392
|
-
labels:
|
|
19448
|
+
labels: Array.from(labelSet),
|
|
19393
19449
|
dataSetsValues: datasets.map((dataset, indexOfDataset) => ({
|
|
19394
19450
|
...dataset,
|
|
19395
|
-
data:
|
|
19451
|
+
data: Array.from(labelSet).map((label) => labelMap[label][indexOfDataset]),
|
|
19396
19452
|
})),
|
|
19397
19453
|
};
|
|
19398
19454
|
}
|
|
@@ -19411,8 +19467,8 @@ function truncateLabel(label) {
|
|
|
19411
19467
|
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
|
|
19412
19468
|
const options = {
|
|
19413
19469
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
19414
|
-
responsive: true,
|
|
19415
|
-
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
|
|
19416
19472
|
layout: {
|
|
19417
19473
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
19418
19474
|
},
|
|
@@ -19458,7 +19514,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
|
|
|
19458
19514
|
labels: labels.map(truncateLabel),
|
|
19459
19515
|
datasets: [],
|
|
19460
19516
|
},
|
|
19461
|
-
platform: undefined,
|
|
19517
|
+
platform: undefined, // This key is optional and will be set by chart.js
|
|
19462
19518
|
plugins: [],
|
|
19463
19519
|
};
|
|
19464
19520
|
}
|
|
@@ -19735,7 +19791,7 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
19735
19791
|
},
|
|
19736
19792
|
y: {
|
|
19737
19793
|
position: chart.verticalAxisPosition,
|
|
19738
|
-
beginAtZero: true,
|
|
19794
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
19739
19795
|
ticks: {
|
|
19740
19796
|
color: fontColor,
|
|
19741
19797
|
callback: (value) => {
|
|
@@ -19786,6 +19842,204 @@ function createBarChartRuntime(chart, getters) {
|
|
|
19786
19842
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
19787
19843
|
}
|
|
19788
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
|
+
|
|
19789
20043
|
function isDataRangeValid(definition) {
|
|
19790
20044
|
return definition.dataRange && !rangeReference.test(definition.dataRange)
|
|
19791
20045
|
? "InvalidGaugeDataRange" /* CommandResult.InvalidGaugeDataRange */
|
|
@@ -20124,7 +20378,7 @@ function getBestTimeUnitForScale(labels, format, locale) {
|
|
|
20124
20378
|
return undefined;
|
|
20125
20379
|
}
|
|
20126
20380
|
const labelsTimestamps = labelDates.map((date) => date.getTime());
|
|
20127
|
-
const period =
|
|
20381
|
+
const period = largeMax(labelsTimestamps) - largeMin(labelsTimestamps);
|
|
20128
20382
|
const minUnit = getFormatMinDisplayUnit(format);
|
|
20129
20383
|
if (UNIT_LENGTH.second >= UNIT_LENGTH[minUnit] && Milliseconds.inSeconds(period) < 180) {
|
|
20130
20384
|
return "second";
|
|
@@ -20256,7 +20510,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
|
|
|
20256
20510
|
},
|
|
20257
20511
|
y: {
|
|
20258
20512
|
position: chart.verticalAxisPosition,
|
|
20259
|
-
beginAtZero: true,
|
|
20513
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
20260
20514
|
ticks: {
|
|
20261
20515
|
color: fontColor,
|
|
20262
20516
|
callback: (value) => {
|
|
@@ -20342,7 +20596,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
20342
20596
|
const dataset = {
|
|
20343
20597
|
label,
|
|
20344
20598
|
data,
|
|
20345
|
-
tension: 0,
|
|
20599
|
+
tension: 0, // 0 -> render straight lines, which is much faster
|
|
20346
20600
|
borderColor: color,
|
|
20347
20601
|
backgroundColor,
|
|
20348
20602
|
pointBackgroundColor: color,
|
|
@@ -20564,7 +20818,7 @@ class PieChart extends AbstractChart {
|
|
|
20564
20818
|
...this.getDefinition(),
|
|
20565
20819
|
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
20566
20820
|
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
20567
|
-
verticalAxisPosition: "left",
|
|
20821
|
+
verticalAxisPosition: "left", //TODO ExcelChartDefinition should be adapted, but can be done later
|
|
20568
20822
|
dataSets,
|
|
20569
20823
|
labelRange,
|
|
20570
20824
|
};
|
|
@@ -20612,7 +20866,7 @@ function getPieConfiguration(chart, labels, localeFormat) {
|
|
|
20612
20866
|
}
|
|
20613
20867
|
function getPieColors(colors, dataSetsValues) {
|
|
20614
20868
|
const pieColors = [];
|
|
20615
|
-
const maxLength =
|
|
20869
|
+
const maxLength = largeMax(dataSetsValues.map((ds) => ds.data.length));
|
|
20616
20870
|
for (let i = 0; i <= maxLength; i++) {
|
|
20617
20871
|
pieColors.push(colors.next());
|
|
20618
20872
|
}
|
|
@@ -20781,7 +21035,7 @@ function createScatterChartRuntime(chart, getters) {
|
|
|
20781
21035
|
configOptions.elements = {
|
|
20782
21036
|
point: {
|
|
20783
21037
|
radius: 3,
|
|
20784
|
-
hoverRadius: 3,
|
|
21038
|
+
hoverRadius: 3, // chartJS seems bugged, the point starts with the "hoverRadius" value
|
|
20785
21039
|
hitRadius: 8,
|
|
20786
21040
|
},
|
|
20787
21041
|
};
|
|
@@ -20821,6 +21075,16 @@ chartRegistry.add("bar", {
|
|
|
20821
21075
|
name: _t("Bar"),
|
|
20822
21076
|
sequence: 10,
|
|
20823
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
|
+
});
|
|
20824
21088
|
chartRegistry.add("line", {
|
|
20825
21089
|
match: (type) => type === "line",
|
|
20826
21090
|
createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
|
|
@@ -20874,6 +21138,7 @@ chartRegistry.add("scatter", {
|
|
|
20874
21138
|
const chartComponentRegistry = new Registry();
|
|
20875
21139
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
20876
21140
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
21141
|
+
chartComponentRegistry.add("combo", ChartJsComponent);
|
|
20877
21142
|
chartComponentRegistry.add("pie", ChartJsComponent);
|
|
20878
21143
|
chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
20879
21144
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
@@ -22227,6 +22492,7 @@ class LinkDisplay extends owl.Component {
|
|
|
22227
22492
|
}
|
|
22228
22493
|
edit() {
|
|
22229
22494
|
const { col, row } = this.props.cellPosition;
|
|
22495
|
+
this.env.model.selection.selectCell(col, row);
|
|
22230
22496
|
this.cellPopovers.open({ col, row }, "LinkEditor");
|
|
22231
22497
|
}
|
|
22232
22498
|
unlink() {
|
|
@@ -23131,7 +23397,7 @@ const lightTemplateWithHeader = (colorSet) => ({
|
|
|
23131
23397
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23132
23398
|
border: { bottom: { color: colorSet.highlight, style: "thin" } },
|
|
23133
23399
|
},
|
|
23134
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23400
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23135
23401
|
firstRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23136
23402
|
secondRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23137
23403
|
});
|
|
@@ -23149,7 +23415,7 @@ const lightTemplateAllBorders = (colorSet) => ({
|
|
|
23149
23415
|
},
|
|
23150
23416
|
},
|
|
23151
23417
|
headerRow: { border: { bottom: { color: colorSet.highlight, style: "medium" } } },
|
|
23152
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23418
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23153
23419
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23154
23420
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23155
23421
|
});
|
|
@@ -23168,7 +23434,7 @@ const mediumTemplateBandedBorders = (colorSet) => ({
|
|
|
23168
23434
|
headerRow: {
|
|
23169
23435
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23170
23436
|
},
|
|
23171
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23437
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23172
23438
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23173
23439
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23174
23440
|
});
|
|
@@ -23204,7 +23470,7 @@ const mediumTemplateMinimalBorders = (colorSet) => ({
|
|
|
23204
23470
|
bottom: { color: "#000000", style: "medium" },
|
|
23205
23471
|
},
|
|
23206
23472
|
},
|
|
23207
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23473
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23208
23474
|
headerRow: {
|
|
23209
23475
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23210
23476
|
border: { bottom: { color: "#000000", style: "medium" } },
|
|
@@ -23228,7 +23494,7 @@ const mediumTemplateAllBorders = (colorSet) => ({
|
|
|
23228
23494
|
},
|
|
23229
23495
|
style: { fillColor: colorSet.light },
|
|
23230
23496
|
},
|
|
23231
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23497
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23232
23498
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23233
23499
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
23234
23500
|
});
|
|
@@ -23259,7 +23525,7 @@ const darkTemplateNoBorders = (colorSet) => ({
|
|
|
23259
23525
|
category: "dark",
|
|
23260
23526
|
colorName: colorSet.name,
|
|
23261
23527
|
wholeTable: { style: { fillColor: colorSet.light } },
|
|
23262
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23528
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23263
23529
|
headerRow: { style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" } },
|
|
23264
23530
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23265
23531
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
@@ -23343,13 +23609,20 @@ const TABLE_PRESETS = {
|
|
|
23343
23609
|
* If a single cell is selected, expand the selection to non-empty adjacent cells to create a table.
|
|
23344
23610
|
*/
|
|
23345
23611
|
function interactiveCreateTable(env, sheetId, tableConfig) {
|
|
23346
|
-
|
|
23347
|
-
|
|
23612
|
+
let target = env.model.getters.getSelectedZones();
|
|
23613
|
+
let isDynamic = env.model.getters.canCreateDynamicTableOnZones(sheetId, target);
|
|
23614
|
+
if (target.length === 1 && !isDynamic && getZoneArea(target[0]) === 1) {
|
|
23348
23615
|
env.model.selection.selectTableAroundSelection();
|
|
23616
|
+
target = env.model.getters.getSelectedZones();
|
|
23617
|
+
isDynamic = env.model.getters.canCreateDynamicTableOnZones(sheetId, target);
|
|
23349
23618
|
}
|
|
23350
|
-
const target = env.model.getters.getSelectedZones();
|
|
23351
23619
|
const ranges = target.map((zone) => env.model.getters.getRangeDataFromZone(sheetId, zone));
|
|
23352
|
-
const result = env.model.dispatch("CREATE_TABLE", {
|
|
23620
|
+
const result = env.model.dispatch("CREATE_TABLE", {
|
|
23621
|
+
ranges,
|
|
23622
|
+
sheetId,
|
|
23623
|
+
config: tableConfig,
|
|
23624
|
+
tableType: isDynamic ? "dynamic" : "static",
|
|
23625
|
+
});
|
|
23353
23626
|
if (result.isCancelledBecause("TableOverlap" /* CommandResult.TableOverlap */)) {
|
|
23354
23627
|
env.raiseError(TableTerms.Errors.TableOverlap);
|
|
23355
23628
|
}
|
|
@@ -23417,8 +23690,8 @@ const DELETE_CONTENT_ROWS_NAME = (env) => {
|
|
|
23417
23690
|
let last;
|
|
23418
23691
|
const activesRows = env.model.getters.getActiveRows();
|
|
23419
23692
|
if (activesRows.size !== 0) {
|
|
23420
|
-
first =
|
|
23421
|
-
last =
|
|
23693
|
+
first = largeMin([...activesRows]);
|
|
23694
|
+
last = largeMax([...activesRows]);
|
|
23422
23695
|
}
|
|
23423
23696
|
else {
|
|
23424
23697
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23446,8 +23719,8 @@ const DELETE_CONTENT_COLUMNS_NAME = (env) => {
|
|
|
23446
23719
|
let last;
|
|
23447
23720
|
const activeCols = env.model.getters.getActiveCols();
|
|
23448
23721
|
if (activeCols.size !== 0) {
|
|
23449
|
-
first =
|
|
23450
|
-
last =
|
|
23722
|
+
first = largeMin([...activeCols]);
|
|
23723
|
+
last = largeMax([...activeCols]);
|
|
23451
23724
|
}
|
|
23452
23725
|
else {
|
|
23453
23726
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23475,8 +23748,8 @@ const REMOVE_ROWS_NAME = (env) => {
|
|
|
23475
23748
|
let last;
|
|
23476
23749
|
const activesRows = env.model.getters.getActiveRows();
|
|
23477
23750
|
if (activesRows.size !== 0) {
|
|
23478
|
-
first =
|
|
23479
|
-
last =
|
|
23751
|
+
first = largeMin([...activesRows]);
|
|
23752
|
+
last = largeMax([...activesRows]);
|
|
23480
23753
|
}
|
|
23481
23754
|
else {
|
|
23482
23755
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23517,8 +23790,8 @@ const REMOVE_COLUMNS_NAME = (env) => {
|
|
|
23517
23790
|
let last;
|
|
23518
23791
|
const activeCols = env.model.getters.getActiveCols();
|
|
23519
23792
|
if (activeCols.size !== 0) {
|
|
23520
|
-
first =
|
|
23521
|
-
last =
|
|
23793
|
+
first = largeMin([...activeCols]);
|
|
23794
|
+
last = largeMax([...activeCols]);
|
|
23522
23795
|
}
|
|
23523
23796
|
else {
|
|
23524
23797
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23559,7 +23832,7 @@ const INSERT_ROWS_BEFORE_ACTION = (env) => {
|
|
|
23559
23832
|
let row;
|
|
23560
23833
|
let quantity;
|
|
23561
23834
|
if (activeRows.size) {
|
|
23562
|
-
row =
|
|
23835
|
+
row = largeMin([...activeRows]);
|
|
23563
23836
|
quantity = activeRows.size;
|
|
23564
23837
|
}
|
|
23565
23838
|
else {
|
|
@@ -23580,7 +23853,7 @@ const INSERT_ROWS_AFTER_ACTION = (env) => {
|
|
|
23580
23853
|
let row;
|
|
23581
23854
|
let quantity;
|
|
23582
23855
|
if (activeRows.size) {
|
|
23583
|
-
row =
|
|
23856
|
+
row = largeMax([...activeRows]);
|
|
23584
23857
|
quantity = activeRows.size;
|
|
23585
23858
|
}
|
|
23586
23859
|
else {
|
|
@@ -23601,7 +23874,7 @@ const INSERT_COLUMNS_BEFORE_ACTION = (env) => {
|
|
|
23601
23874
|
let column;
|
|
23602
23875
|
let quantity;
|
|
23603
23876
|
if (activeCols.size) {
|
|
23604
|
-
column =
|
|
23877
|
+
column = largeMin([...activeCols]);
|
|
23605
23878
|
quantity = activeCols.size;
|
|
23606
23879
|
}
|
|
23607
23880
|
else {
|
|
@@ -23622,7 +23895,7 @@ const INSERT_COLUMNS_AFTER_ACTION = (env) => {
|
|
|
23622
23895
|
let column;
|
|
23623
23896
|
let quantity;
|
|
23624
23897
|
if (activeCols.size) {
|
|
23625
|
-
column =
|
|
23898
|
+
column = largeMax([...activeCols]);
|
|
23626
23899
|
quantity = activeCols.size;
|
|
23627
23900
|
}
|
|
23628
23901
|
else {
|
|
@@ -23797,9 +24070,13 @@ const INSERT_TABLE = (env) => {
|
|
|
23797
24070
|
};
|
|
23798
24071
|
const DELETE_SELECTED_TABLE = (env) => {
|
|
23799
24072
|
const position = env.model.getters.getActivePosition();
|
|
24073
|
+
const table = env.model.getters.getTable(position);
|
|
24074
|
+
if (!table) {
|
|
24075
|
+
return;
|
|
24076
|
+
}
|
|
23800
24077
|
env.model.dispatch("REMOVE_TABLE", {
|
|
23801
24078
|
sheetId: position.sheetId,
|
|
23802
|
-
target: [
|
|
24079
|
+
target: [table.range.zone],
|
|
23803
24080
|
});
|
|
23804
24081
|
};
|
|
23805
24082
|
//------------------------------------------------------------------------------
|
|
@@ -24215,14 +24492,19 @@ const categorieFunctionAll = {
|
|
|
24215
24492
|
children: [allFunctionListMenuBuilder],
|
|
24216
24493
|
};
|
|
24217
24494
|
function allFunctionListMenuBuilder() {
|
|
24218
|
-
const fnNames = functionRegistry.getKeys();
|
|
24495
|
+
const fnNames = functionRegistry.getKeys().filter((key) => !functionRegistry.get(key).hidden);
|
|
24219
24496
|
return createFormulaFunctions(fnNames);
|
|
24220
24497
|
}
|
|
24221
24498
|
const categoriesFunctionListMenuBuilder = () => {
|
|
24222
24499
|
const functions = functionRegistry.content;
|
|
24223
|
-
const categories = [
|
|
24500
|
+
const categories = [
|
|
24501
|
+
...new Set(functionRegistry
|
|
24502
|
+
.getAll()
|
|
24503
|
+
.filter((fn) => !fn.hidden)
|
|
24504
|
+
.map((fn) => fn.category)),
|
|
24505
|
+
].filter(isDefined$1);
|
|
24224
24506
|
return categories.sort().map((category, i) => {
|
|
24225
|
-
const functionsInCategory = Object.keys(functions).filter((key) => functions[key].category === category);
|
|
24507
|
+
const functionsInCategory = Object.keys(functions).filter((key) => functions[key].category === category && !functions[key].hidden);
|
|
24226
24508
|
return {
|
|
24227
24509
|
name: category,
|
|
24228
24510
|
children: createFormulaFunctions(functionsInCategory),
|
|
@@ -27219,6 +27501,22 @@ class BarChartDesignPanel extends LineBarPieDesignPanel {
|
|
|
27219
27501
|
static template = "o-spreadsheet-BarChartDesignPanel";
|
|
27220
27502
|
}
|
|
27221
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
|
+
|
|
27222
27520
|
class GaugeChartConfigPanel extends owl.Component {
|
|
27223
27521
|
static template = "o-spreadsheet-GaugeChartConfigPanel";
|
|
27224
27522
|
static components = { ChartErrorSection, ChartDataSeries };
|
|
@@ -27576,6 +27874,10 @@ chartSidePanelComponentRegistry
|
|
|
27576
27874
|
.add("bar", {
|
|
27577
27875
|
configuration: BarConfigPanel,
|
|
27578
27876
|
design: BarChartDesignPanel,
|
|
27877
|
+
})
|
|
27878
|
+
.add("combo", {
|
|
27879
|
+
configuration: ComboChartConfigPanel,
|
|
27880
|
+
design: ComboChartDesignPanel,
|
|
27579
27881
|
})
|
|
27580
27882
|
.add("pie", {
|
|
27581
27883
|
configuration: LineBarPieConfigPanel,
|
|
@@ -28380,11 +28682,9 @@ css /* scss */ `
|
|
|
28380
28682
|
}
|
|
28381
28683
|
.o-cell-is-operator {
|
|
28382
28684
|
margin-bottom: 5px;
|
|
28383
|
-
width: 96%;
|
|
28384
28685
|
}
|
|
28385
28686
|
.o-cell-is-value {
|
|
28386
28687
|
margin-bottom: 5px;
|
|
28387
|
-
width: 96%;
|
|
28388
28688
|
}
|
|
28389
28689
|
.o-color-picker-widget .o-color-picker-button {
|
|
28390
28690
|
pointer-events: all;
|
|
@@ -30218,7 +30518,11 @@ class SplitIntoColumnsPanel extends owl.Component {
|
|
|
30218
30518
|
const composerStore = useStore(ComposerStore);
|
|
30219
30519
|
// The feature makes no sense if we are editing a cell, because then the selection isn't active
|
|
30220
30520
|
// Stop the edition when the panel is mounted, and close the panel if the user start editing a cell
|
|
30221
|
-
owl.useEffect(
|
|
30521
|
+
owl.useEffect((editionMode) => {
|
|
30522
|
+
if (editionMode !== "inactive") {
|
|
30523
|
+
this.props.onCloseSidePanel();
|
|
30524
|
+
}
|
|
30525
|
+
}, () => [composerStore.editionMode]);
|
|
30222
30526
|
owl.onMounted(() => {
|
|
30223
30527
|
composerStore.stopEdition();
|
|
30224
30528
|
});
|
|
@@ -30304,6 +30608,24 @@ function getTableContentZone(tableZone, tableConfig) {
|
|
|
30304
30608
|
const contentZone = { ...tableZone, top: tableZone.top + numberOfHeaders };
|
|
30305
30609
|
return contentZone.top <= contentZone.bottom ? contentZone : undefined;
|
|
30306
30610
|
}
|
|
30611
|
+
function getTableTopLeft(table) {
|
|
30612
|
+
const range = table.range;
|
|
30613
|
+
return { row: range.zone.top, col: range.zone.left, sheetId: range.sheetId };
|
|
30614
|
+
}
|
|
30615
|
+
function createFilter(id, range, config, createRange) {
|
|
30616
|
+
const zone = range.zone;
|
|
30617
|
+
if (zone.left !== zone.right) {
|
|
30618
|
+
throw new Error("Can only define a filter on a single column");
|
|
30619
|
+
}
|
|
30620
|
+
const filteredZone = { ...zone, top: zone.top + config.numberOfHeaders };
|
|
30621
|
+
const filteredRange = createRange(range.sheetId, filteredZone);
|
|
30622
|
+
return {
|
|
30623
|
+
id,
|
|
30624
|
+
rangeWithHeaders: range,
|
|
30625
|
+
col: zone.left,
|
|
30626
|
+
filteredRange: filteredZone.top > filteredZone.bottom ? undefined : filteredRange,
|
|
30627
|
+
};
|
|
30628
|
+
}
|
|
30307
30629
|
function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
|
|
30308
30630
|
return {
|
|
30309
30631
|
borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
|
|
@@ -30741,6 +31063,11 @@ css /* scss */ `
|
|
|
30741
31063
|
color: #ffffff;
|
|
30742
31064
|
background: #d94b4b;
|
|
30743
31065
|
}
|
|
31066
|
+
|
|
31067
|
+
.o-info-icon {
|
|
31068
|
+
width: 14px;
|
|
31069
|
+
height: 14px;
|
|
31070
|
+
}
|
|
30744
31071
|
}
|
|
30745
31072
|
`;
|
|
30746
31073
|
class TablePanel extends owl.Component {
|
|
@@ -30772,6 +31099,29 @@ class TablePanel extends owl.Component {
|
|
|
30772
31099
|
const numberOfHeaders = hasHeaders ? 1 : 0;
|
|
30773
31100
|
this.updateNumberOfHeaders(numberOfHeaders);
|
|
30774
31101
|
}
|
|
31102
|
+
updateTableIsDynamic(isDynamic) {
|
|
31103
|
+
const newTableType = isDynamic ? "dynamic" : "forceStatic";
|
|
31104
|
+
if (newTableType === this.props.table.type) {
|
|
31105
|
+
return;
|
|
31106
|
+
}
|
|
31107
|
+
const uiTable = this.env.model.getters.getTable(getTableTopLeft(this.props.table));
|
|
31108
|
+
if (!uiTable) {
|
|
31109
|
+
return;
|
|
31110
|
+
}
|
|
31111
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
31112
|
+
const result = this.env.model.dispatch("UPDATE_TABLE", {
|
|
31113
|
+
sheetId,
|
|
31114
|
+
zone: this.props.table.range.zone,
|
|
31115
|
+
newTableRange: uiTable.range.rangeData,
|
|
31116
|
+
tableType: newTableType,
|
|
31117
|
+
});
|
|
31118
|
+
const updatedTable = this.env.model.getters.getCoreTable(getTableTopLeft(this.props.table));
|
|
31119
|
+
if (result.isSuccessful && updatedTable) {
|
|
31120
|
+
const newTableRange = updatedTable.range;
|
|
31121
|
+
this.state.tableXc = this.env.model.getters.getRangeString(newTableRange, sheetId);
|
|
31122
|
+
this.state.tableZoneErrors = [];
|
|
31123
|
+
}
|
|
31124
|
+
}
|
|
30775
31125
|
onChangeNumberOfHeaders(ev) {
|
|
30776
31126
|
const input = ev.target;
|
|
30777
31127
|
const numberOfHeaders = parseInt(input.value);
|
|
@@ -30795,35 +31145,59 @@ class TablePanel extends owl.Component {
|
|
|
30795
31145
|
}
|
|
30796
31146
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
30797
31147
|
this.state.tableXc = ranges[0];
|
|
31148
|
+
const newTableRange = this.env.model.getters.getRangeFromSheetXC(sheetId, this.state.tableXc);
|
|
30798
31149
|
this.state.tableZoneErrors = this.env.model.canDispatch("UPDATE_TABLE", {
|
|
30799
31150
|
sheetId,
|
|
30800
31151
|
zone: this.props.table.range.zone,
|
|
30801
31152
|
newTableRange: this.env.model.getters.getRangeDataFromXc(sheetId, this.state.tableXc),
|
|
31153
|
+
tableType: this.getNewTableType(newTableRange.zone),
|
|
30802
31154
|
}).reasons;
|
|
30803
31155
|
}
|
|
30804
31156
|
onRangeConfirmed() {
|
|
30805
31157
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
30806
|
-
|
|
31158
|
+
let newRange = this.env.model.getters.getRangeFromSheetXC(sheetId, this.state.tableXc);
|
|
31159
|
+
if (getZoneArea(newRange.zone) === 1) {
|
|
31160
|
+
const extendedZone = this.env.model.getters.getContiguousZone(sheetId, newRange.zone);
|
|
31161
|
+
newRange = this.env.model.getters.getRangeFromZone(sheetId, extendedZone);
|
|
31162
|
+
}
|
|
30807
31163
|
const result = this.env.model.dispatch("UPDATE_TABLE", {
|
|
30808
31164
|
sheetId,
|
|
30809
31165
|
zone: this.props.table.range.zone,
|
|
30810
31166
|
newTableRange: newRange.rangeData,
|
|
31167
|
+
tableType: this.getNewTableType(newRange.zone),
|
|
30811
31168
|
});
|
|
30812
|
-
|
|
30813
|
-
|
|
31169
|
+
const position = { sheetId, col: newRange.zone.left, row: newRange.zone.top };
|
|
31170
|
+
const updatedTable = this.env.model.getters.getCoreTable(position);
|
|
31171
|
+
if (result.isSuccessful && updatedTable) {
|
|
31172
|
+
const newTopLeft = getTableTopLeft(updatedTable);
|
|
30814
31173
|
this.env.model.selection.selectZone({
|
|
30815
|
-
zone: positionToZone(
|
|
30816
|
-
cell:
|
|
31174
|
+
zone: positionToZone(newTopLeft),
|
|
31175
|
+
cell: newTopLeft,
|
|
30817
31176
|
});
|
|
31177
|
+
const newTableRange = updatedTable.range;
|
|
31178
|
+
this.state.tableXc = this.env.model.getters.getRangeString(newTableRange, sheetId);
|
|
31179
|
+
}
|
|
31180
|
+
else {
|
|
31181
|
+
const oldTableRange = this.props.table.range;
|
|
31182
|
+
this.state.tableXc = this.env.model.getters.getRangeString(oldTableRange, sheetId);
|
|
30818
31183
|
}
|
|
30819
31184
|
this.state.tableZoneErrors = [];
|
|
30820
|
-
this.state.tableXc = result.isSuccessful
|
|
30821
|
-
? this.state.tableXc
|
|
30822
|
-
: this.env.model.getters.getRangeString(this.props.table.range, sheetId);
|
|
30823
31185
|
}
|
|
30824
31186
|
deleteTable() {
|
|
30825
31187
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
30826
|
-
this.env.model.dispatch("REMOVE_TABLE", {
|
|
31188
|
+
this.env.model.dispatch("REMOVE_TABLE", {
|
|
31189
|
+
sheetId,
|
|
31190
|
+
target: [this.props.table.range.zone],
|
|
31191
|
+
});
|
|
31192
|
+
}
|
|
31193
|
+
getNewTableType(newTableZone) {
|
|
31194
|
+
if (this.props.table.type === "forceStatic") {
|
|
31195
|
+
return "forceStatic";
|
|
31196
|
+
}
|
|
31197
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
31198
|
+
return this.env.model.getters.canCreateDynamicTableOnZones(sheetId, [newTableZone])
|
|
31199
|
+
? "dynamic"
|
|
31200
|
+
: "static";
|
|
30827
31201
|
}
|
|
30828
31202
|
get tableConfig() {
|
|
30829
31203
|
return this.props.table.config;
|
|
@@ -30841,6 +31215,14 @@ class TablePanel extends owl.Component {
|
|
|
30841
31215
|
get hasFilterCheckboxTooltip() {
|
|
30842
31216
|
return this.canHaveFilters ? undefined : TableTerms.Tooltips.filterWithoutHeader;
|
|
30843
31217
|
}
|
|
31218
|
+
get canBeDynamic() {
|
|
31219
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
31220
|
+
return (this.props.table.type === "dynamic" ||
|
|
31221
|
+
this.env.model.getters.canCreateDynamicTableOnZones(sheetId, [this.props.table.range.zone]));
|
|
31222
|
+
}
|
|
31223
|
+
get dynamicTableTooltip() {
|
|
31224
|
+
return TableTerms.Tooltips.isDynamic;
|
|
31225
|
+
}
|
|
30844
31226
|
}
|
|
30845
31227
|
|
|
30846
31228
|
const sidePanelRegistry = new Registry();
|
|
@@ -30903,11 +31285,8 @@ sidePanelRegistry.add("TableSidePanel", {
|
|
|
30903
31285
|
if (!table) {
|
|
30904
31286
|
return { isOpen: false };
|
|
30905
31287
|
}
|
|
30906
|
-
|
|
30907
|
-
|
|
30908
|
-
props: { table },
|
|
30909
|
-
key: table.id,
|
|
30910
|
-
};
|
|
31288
|
+
const coreTable = getters.getCoreTable(getTableTopLeft(table));
|
|
31289
|
+
return { isOpen: true, props: { table: coreTable }, key: table.id };
|
|
30911
31290
|
},
|
|
30912
31291
|
});
|
|
30913
31292
|
|
|
@@ -31086,6 +31465,9 @@ class FigureComponent extends owl.Component {
|
|
|
31086
31465
|
el?.focus({ preventScroll: true });
|
|
31087
31466
|
}
|
|
31088
31467
|
}, () => [this.env.model.getters.getSelectedFigureId(), this.props.figure.id, this.figureRef.el]);
|
|
31468
|
+
owl.onWillUnmount(() => {
|
|
31469
|
+
this.props.onFigureDeleted();
|
|
31470
|
+
});
|
|
31089
31471
|
}
|
|
31090
31472
|
clickAnchor(dirX, dirY, ev) {
|
|
31091
31473
|
this.props.onClickAnchor(dirX, dirY, ev);
|
|
@@ -31104,6 +31486,7 @@ class FigureComponent extends owl.Component {
|
|
|
31104
31486
|
this.props.onFigureDeleted();
|
|
31105
31487
|
ev.stopPropagation();
|
|
31106
31488
|
ev.preventDefault();
|
|
31489
|
+
ev.stopPropagation();
|
|
31107
31490
|
break;
|
|
31108
31491
|
case "ArrowDown":
|
|
31109
31492
|
case "ArrowLeft":
|
|
@@ -31124,6 +31507,7 @@ class FigureComponent extends owl.Component {
|
|
|
31124
31507
|
});
|
|
31125
31508
|
ev.stopPropagation();
|
|
31126
31509
|
ev.preventDefault();
|
|
31510
|
+
ev.stopPropagation();
|
|
31127
31511
|
break;
|
|
31128
31512
|
}
|
|
31129
31513
|
}
|
|
@@ -31791,6 +32175,7 @@ function compareContentToSpanElement(content, node) {
|
|
|
31791
32175
|
// -----------------------------------------------------------------------------
|
|
31792
32176
|
css /* scss */ `
|
|
31793
32177
|
.o-formula-assistant {
|
|
32178
|
+
background: #ffffff;
|
|
31794
32179
|
.o-formula-assistant-head {
|
|
31795
32180
|
background-color: #f2f2f2;
|
|
31796
32181
|
padding: 10px;
|
|
@@ -31846,6 +32231,9 @@ class FunctionDescriptionProvider extends owl.Component {
|
|
|
31846
32231
|
this.assistantState.allowCellSelectionBehind = false;
|
|
31847
32232
|
}, 2000);
|
|
31848
32233
|
}
|
|
32234
|
+
get formulaArgSeparator() {
|
|
32235
|
+
return this.env.model.getters.getLocale().formulaArgSeparator + " ";
|
|
32236
|
+
}
|
|
31849
32237
|
}
|
|
31850
32238
|
|
|
31851
32239
|
const functions$2 = functionRegistry.content;
|
|
@@ -32005,6 +32393,12 @@ class Composer extends owl.Component {
|
|
|
32005
32393
|
owl.useEffect(() => {
|
|
32006
32394
|
this.processContent();
|
|
32007
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
|
+
});
|
|
32008
32402
|
}
|
|
32009
32403
|
// ---------------------------------------------------------------------------
|
|
32010
32404
|
// Handlers
|
|
@@ -32743,8 +33137,7 @@ class FilterIconsOverlay extends owl.Component {
|
|
|
32743
33137
|
};
|
|
32744
33138
|
getFilterHeadersPositions() {
|
|
32745
33139
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
32746
|
-
|
|
32747
|
-
return headerPositions.map((position) => ({ sheetId, ...position }));
|
|
33140
|
+
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
32748
33141
|
}
|
|
32749
33142
|
}
|
|
32750
33143
|
|
|
@@ -33985,11 +34378,6 @@ css /* scss */ `
|
|
|
33985
34378
|
height: 10000px;
|
|
33986
34379
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
33987
34380
|
}
|
|
33988
|
-
.o-unhide-buttons {
|
|
33989
|
-
width: fit-content;
|
|
33990
|
-
gap: 5px;
|
|
33991
|
-
transform: translate(-50%, 0);
|
|
33992
|
-
}
|
|
33993
34381
|
.o-unhide:hover {
|
|
33994
34382
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
33995
34383
|
background-color: lightgrey;
|
|
@@ -34151,10 +34539,6 @@ css /* scss */ `
|
|
|
34151
34539
|
height: 1px;
|
|
34152
34540
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34153
34541
|
}
|
|
34154
|
-
.o-unhide-buttons {
|
|
34155
|
-
height: fit-content;
|
|
34156
|
-
transform: translate(0, -50%);
|
|
34157
|
-
}
|
|
34158
34542
|
.o-unhide:hover {
|
|
34159
34543
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34160
34544
|
background-color: lightgrey;
|
|
@@ -34615,19 +34999,16 @@ class GridRenderer {
|
|
|
34615
34999
|
for (let col = left; col <= right; col++) {
|
|
34616
35000
|
const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
|
|
34617
35001
|
const { x, width } = this.getters.getVisibleRect(colZone);
|
|
34618
|
-
const colHasFilter = this.getters.doesZonesContainFilter(sheetId, [colZone]);
|
|
34619
35002
|
const isColActive = activeCols.has(col);
|
|
34620
35003
|
const isColSelected = selectedCols.has(col);
|
|
34621
35004
|
if (isColActive) {
|
|
34622
|
-
ctx.fillStyle =
|
|
35005
|
+
ctx.fillStyle = BACKGROUND_HEADER_ACTIVE_COLOR;
|
|
34623
35006
|
}
|
|
34624
35007
|
else if (isColSelected) {
|
|
34625
|
-
ctx.fillStyle =
|
|
34626
|
-
? BACKGROUND_HEADER_SELECTED_FILTER_COLOR
|
|
34627
|
-
: BACKGROUND_HEADER_SELECTED_COLOR;
|
|
35008
|
+
ctx.fillStyle = BACKGROUND_HEADER_SELECTED_COLOR;
|
|
34628
35009
|
}
|
|
34629
35010
|
else {
|
|
34630
|
-
ctx.fillStyle =
|
|
35011
|
+
ctx.fillStyle = BACKGROUND_HEADER_COLOR;
|
|
34631
35012
|
}
|
|
34632
35013
|
ctx.fillRect(x, 0, width, HEADER_HEIGHT);
|
|
34633
35014
|
}
|
|
@@ -34635,19 +35016,16 @@ class GridRenderer {
|
|
|
34635
35016
|
for (let row = top; row <= bottom; row++) {
|
|
34636
35017
|
const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
|
|
34637
35018
|
const { y, height } = this.getters.getVisibleRect(rowZone);
|
|
34638
|
-
const rowHasFilter = this.getters.doesZonesContainFilter(sheetId, [rowZone]);
|
|
34639
35019
|
const isRowActive = activeRows.has(row);
|
|
34640
35020
|
const isRowSelected = selectedRows.has(row);
|
|
34641
35021
|
if (isRowActive) {
|
|
34642
|
-
ctx.fillStyle =
|
|
35022
|
+
ctx.fillStyle = BACKGROUND_HEADER_ACTIVE_COLOR;
|
|
34643
35023
|
}
|
|
34644
35024
|
else if (isRowSelected) {
|
|
34645
|
-
ctx.fillStyle =
|
|
34646
|
-
? BACKGROUND_HEADER_SELECTED_FILTER_COLOR
|
|
34647
|
-
: BACKGROUND_HEADER_SELECTED_COLOR;
|
|
35025
|
+
ctx.fillStyle = BACKGROUND_HEADER_SELECTED_COLOR;
|
|
34648
35026
|
}
|
|
34649
35027
|
else {
|
|
34650
|
-
ctx.fillStyle =
|
|
35028
|
+
ctx.fillStyle = BACKGROUND_HEADER_COLOR;
|
|
34651
35029
|
}
|
|
34652
35030
|
ctx.fillRect(0, y, HEADER_WIDTH, height);
|
|
34653
35031
|
}
|
|
@@ -35368,7 +35746,7 @@ class VerticalScrollBar extends owl.Component {
|
|
|
35368
35746
|
onScroll(offset) {
|
|
35369
35747
|
const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
|
|
35370
35748
|
this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
|
|
35371
|
-
offsetX: scrollX,
|
|
35749
|
+
offsetX: scrollX, // offsetX is the same
|
|
35372
35750
|
offsetY: offset,
|
|
35373
35751
|
});
|
|
35374
35752
|
}
|
|
@@ -35656,8 +36034,8 @@ class Grid extends owl.Component {
|
|
|
35656
36034
|
"Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
|
|
35657
36035
|
"Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
|
|
35658
36036
|
"Ctrl+Shift+V": () => PASTE_AS_VALUE_ACTION(this.env),
|
|
35659
|
-
"Ctrl+Shift+<": () => this.clearFormatting(),
|
|
35660
|
-
"Ctrl+<": () => this.clearFormatting(),
|
|
36037
|
+
"Ctrl+Shift+<": () => this.clearFormatting(), // for qwerty
|
|
36038
|
+
"Ctrl+<": () => this.clearFormatting(), // for azerty
|
|
35661
36039
|
"Ctrl+Shift+ ": () => {
|
|
35662
36040
|
this.env.model.selection.selectAll();
|
|
35663
36041
|
},
|
|
@@ -36093,6 +36471,7 @@ const XLSX_CHART_TYPES = [
|
|
|
36093
36471
|
"surfaceChart",
|
|
36094
36472
|
"surface3DChart",
|
|
36095
36473
|
"bubbleChart",
|
|
36474
|
+
"comboChart",
|
|
36096
36475
|
];
|
|
36097
36476
|
|
|
36098
36477
|
/** In XLSX color format (no #) */
|
|
@@ -36424,10 +36803,10 @@ function convertCFCellIsOperator(xlsxCfOperator) {
|
|
|
36424
36803
|
const CF_TYPE_CONVERSION_MAP = {
|
|
36425
36804
|
aboveAverage: undefined,
|
|
36426
36805
|
expression: undefined,
|
|
36427
|
-
cellIs: undefined,
|
|
36428
|
-
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
|
|
36429
36808
|
dataBar: undefined,
|
|
36430
|
-
iconSet: undefined,
|
|
36809
|
+
iconSet: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36431
36810
|
top10: undefined,
|
|
36432
36811
|
uniqueValues: undefined,
|
|
36433
36812
|
duplicateValues: undefined,
|
|
@@ -36504,6 +36883,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36504
36883
|
surfaceChart: undefined,
|
|
36505
36884
|
surface3DChart: undefined,
|
|
36506
36885
|
bubbleChart: undefined,
|
|
36886
|
+
comboChart: "combo",
|
|
36507
36887
|
};
|
|
36508
36888
|
/** Conversion map for the SUBTOTAL(index, formula) function in xlsx, index <=> actual function*/
|
|
36509
36889
|
const SUBTOTAL_FUNCTION_CONVERSION_MAP = {
|
|
@@ -36662,7 +37042,7 @@ const XLSX_INDEXED_COLORS = {
|
|
|
36662
37042
|
61: "993366",
|
|
36663
37043
|
62: "333399",
|
|
36664
37044
|
63: "333333",
|
|
36665
|
-
64: "000000",
|
|
37045
|
+
64: "000000", // system foreground
|
|
36666
37046
|
65: "FFFFFF", // system background
|
|
36667
37047
|
};
|
|
36668
37048
|
const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
|
|
@@ -37854,7 +38234,7 @@ function convertHyperlink(link, cellValue, warningManager) {
|
|
|
37854
38234
|
function getSheetDims(sheet) {
|
|
37855
38235
|
const dims = [0, 0];
|
|
37856
38236
|
for (let row of sheet.rows) {
|
|
37857
|
-
dims[0] = Math.max(dims[0],
|
|
38237
|
+
dims[0] = Math.max(dims[0], largeMax(row.cells.map((cell) => toCartesian(cell.xc).col)));
|
|
37858
38238
|
dims[1] = Math.max(dims[1], row.index);
|
|
37859
38239
|
}
|
|
37860
38240
|
dims[0] = Math.max(dims[0], EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
|
|
@@ -38720,6 +39100,9 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38720
39100
|
if (!CHART_TYPE_CONVERSION_MAP[chartType]) {
|
|
38721
39101
|
throw new Error(`Unsupported chart type ${chartType}`);
|
|
38722
39102
|
}
|
|
39103
|
+
if (CHART_TYPE_CONVERSION_MAP[chartType] === "combo") {
|
|
39104
|
+
return this.extractComboChart(rootChartElement);
|
|
39105
|
+
}
|
|
38723
39106
|
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
38724
39107
|
const chartTitle = this.mapOnElements({ parent: rootChartElement, query: "c:title a:t" }, (textElement) => {
|
|
38725
39108
|
return textElement.textContent || "";
|
|
@@ -38748,6 +39131,37 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38748
39131
|
};
|
|
38749
39132
|
})[0];
|
|
38750
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
|
+
}
|
|
38751
39165
|
extractChartDatasets(chartElement) {
|
|
38752
39166
|
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
38753
39167
|
return {
|
|
@@ -38765,12 +39179,21 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38765
39179
|
if (!plotAreaElement) {
|
|
38766
39180
|
throw new Error("Missing plot area in the chart definition.");
|
|
38767
39181
|
}
|
|
39182
|
+
let globalTag = undefined;
|
|
38768
39183
|
for (let child of plotAreaElement.children) {
|
|
38769
39184
|
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
38770
39185
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
38771
|
-
|
|
39186
|
+
if (!globalTag) {
|
|
39187
|
+
globalTag = tag;
|
|
39188
|
+
}
|
|
39189
|
+
else if (globalTag !== tag) {
|
|
39190
|
+
globalTag = "comboChart";
|
|
39191
|
+
}
|
|
38772
39192
|
}
|
|
38773
39193
|
}
|
|
39194
|
+
if (globalTag) {
|
|
39195
|
+
return globalTag;
|
|
39196
|
+
}
|
|
38774
39197
|
throw new Error("Unknown chart type");
|
|
38775
39198
|
}
|
|
38776
39199
|
}
|
|
@@ -42361,6 +42784,9 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
42361
42784
|
if (newRule.criterion.type === "isBoolean") {
|
|
42362
42785
|
this.setCenterStyleToBooleanCells(newRule);
|
|
42363
42786
|
}
|
|
42787
|
+
else if (newRule.criterion.type === "isValueInList") {
|
|
42788
|
+
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
42789
|
+
}
|
|
42364
42790
|
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
42365
42791
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
42366
42792
|
if (ruleIndex !== -1) {
|
|
@@ -42757,7 +43183,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
|
|
|
42757
43183
|
if (hiddenElements.size >= elements) {
|
|
42758
43184
|
return "TooManyHiddenElements" /* CommandResult.TooManyHiddenElements */;
|
|
42759
43185
|
}
|
|
42760
|
-
else if (
|
|
43186
|
+
else if (largeMin(cmd.elements) < 0 || largeMax(cmd.elements) > elements) {
|
|
42761
43187
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
42762
43188
|
}
|
|
42763
43189
|
else {
|
|
@@ -43086,7 +43512,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43086
43512
|
"isInSameMerge",
|
|
43087
43513
|
"isMergeHidden",
|
|
43088
43514
|
"getMainCellPosition",
|
|
43089
|
-
"getBottomLeftCell",
|
|
43090
43515
|
"expandZone",
|
|
43091
43516
|
"doesIntersectMerge",
|
|
43092
43517
|
"doesColumnsHaveCommonMerges",
|
|
@@ -43268,13 +43693,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43268
43693
|
const mergeTopLeftPos = this.getMerge(position).topLeft;
|
|
43269
43694
|
return { sheetId: position.sheetId, col: mergeTopLeftPos.col, row: mergeTopLeftPos.row };
|
|
43270
43695
|
}
|
|
43271
|
-
getBottomLeftCell(position) {
|
|
43272
|
-
if (!this.isInMerge(position)) {
|
|
43273
|
-
return position;
|
|
43274
|
-
}
|
|
43275
|
-
const { bottom, left } = this.getMerge(position);
|
|
43276
|
-
return { sheetId: position.sheetId, col: left, row: bottom };
|
|
43277
|
-
}
|
|
43278
43696
|
isMergeHidden(sheetId, merge) {
|
|
43279
43697
|
const hiddenColsGroups = this.getters.getHiddenColsGroups(sheetId);
|
|
43280
43698
|
const hiddenRowsGroups = this.getters.getHiddenRowsGroups(sheetId);
|
|
@@ -43575,8 +43993,8 @@ class RangeAdapter {
|
|
|
43575
43993
|
let newRange = range;
|
|
43576
43994
|
let changeType = "NONE";
|
|
43577
43995
|
for (let group of groups) {
|
|
43578
|
-
const min =
|
|
43579
|
-
const max =
|
|
43996
|
+
const min = largeMin(group);
|
|
43997
|
+
const max = largeMax(group);
|
|
43580
43998
|
if (range.zone[start] <= min && min <= range.zone[end]) {
|
|
43581
43999
|
const toRemove = Math.min(range.zone[end], max) - min + 1;
|
|
43582
44000
|
changeType = "RESIZE";
|
|
@@ -43958,7 +44376,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
43958
44376
|
"getSheetIds",
|
|
43959
44377
|
"getVisibleSheetIds",
|
|
43960
44378
|
"isSheetVisible",
|
|
43961
|
-
"getEvaluationSheets",
|
|
43962
44379
|
"doesHeaderExist",
|
|
43963
44380
|
"doesHeadersExist",
|
|
43964
44381
|
"getCell",
|
|
@@ -44025,8 +44442,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
44025
44442
|
}
|
|
44026
44443
|
return "Success" /* CommandResult.Success */;
|
|
44027
44444
|
case "REMOVE_COLUMNS_ROWS": {
|
|
44028
|
-
const min =
|
|
44029
|
-
const max =
|
|
44445
|
+
const min = largeMin(cmd.elements);
|
|
44446
|
+
const max = largeMax(cmd.elements);
|
|
44030
44447
|
if (min < 0 || !this.doesHeaderExist(cmd.sheetId, cmd.dimension, max)) {
|
|
44031
44448
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
44032
44449
|
}
|
|
@@ -44217,9 +44634,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44217
44634
|
getVisibleSheetIds() {
|
|
44218
44635
|
return this.orderedSheetIds.filter(this.isSheetVisible.bind(this));
|
|
44219
44636
|
}
|
|
44220
|
-
getEvaluationSheets() {
|
|
44221
|
-
return this.sheets;
|
|
44222
|
-
}
|
|
44223
44637
|
doesHeaderExist(sheetId, dimension, index) {
|
|
44224
44638
|
return dimension === "COL"
|
|
44225
44639
|
? index >= 0 && index < this.getNumberCols(sheetId)
|
|
@@ -44228,13 +44642,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44228
44642
|
doesHeadersExist(sheetId, dimension, headerIndexes) {
|
|
44229
44643
|
return headerIndexes.every((index) => this.doesHeaderExist(sheetId, dimension, index));
|
|
44230
44644
|
}
|
|
44231
|
-
getRow(sheetId, index) {
|
|
44232
|
-
const row = this.getSheet(sheetId).rows[index];
|
|
44233
|
-
if (!row) {
|
|
44234
|
-
throw new Error(`Row ${row} not found.`);
|
|
44235
|
-
}
|
|
44236
|
-
return row;
|
|
44237
|
-
}
|
|
44238
44645
|
getCell({ sheetId, col, row }) {
|
|
44239
44646
|
const sheet = this.tryGetSheet(sheetId);
|
|
44240
44647
|
const cellId = sheet?.rows[row]?.cells[col];
|
|
@@ -44819,23 +45226,12 @@ class SheetPlugin extends CorePlugin {
|
|
|
44819
45226
|
}
|
|
44820
45227
|
|
|
44821
45228
|
class TablePlugin extends CorePlugin {
|
|
44822
|
-
static getters = [
|
|
44823
|
-
"doesZonesContainFilter",
|
|
44824
|
-
"getFilter",
|
|
44825
|
-
"getFilters",
|
|
44826
|
-
"getTable",
|
|
44827
|
-
"getTables",
|
|
44828
|
-
"getTablesInZone",
|
|
44829
|
-
"getTablesOverlappingZones",
|
|
44830
|
-
"getFilterId",
|
|
44831
|
-
"getFilterHeaders",
|
|
44832
|
-
"isFilterHeader",
|
|
44833
|
-
];
|
|
45229
|
+
static getters = ["getCoreTable", "getCoreTables"];
|
|
44834
45230
|
tables = {};
|
|
44835
45231
|
adaptRanges(applyChange, sheetId) {
|
|
44836
45232
|
const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
|
|
44837
45233
|
for (const sheetId of sheetIds) {
|
|
44838
|
-
for (const table of this.
|
|
45234
|
+
for (const table of this.getCoreTables(sheetId)) {
|
|
44839
45235
|
this.applyRangeChangeOnTable(sheetId, table, applyChange);
|
|
44840
45236
|
}
|
|
44841
45237
|
}
|
|
@@ -44851,15 +45247,16 @@ class TablePlugin extends CorePlugin {
|
|
|
44851
45247
|
? "TableOverlap" /* CommandResult.TableOverlap */
|
|
44852
45248
|
: "Success" /* CommandResult.Success */, (cmd) => this.checkTableConfigUpdateIsValid(cmd.config));
|
|
44853
45249
|
case "UPDATE_TABLE":
|
|
44854
|
-
const updatedTable = this.
|
|
45250
|
+
const updatedTable = this.getTableFromZone(cmd.sheetId, cmd.zone);
|
|
44855
45251
|
if (!updatedTable) {
|
|
44856
45252
|
return "TableNotFound" /* CommandResult.TableNotFound */;
|
|
44857
45253
|
}
|
|
44858
45254
|
return this.checkValidations(cmd, this.checkUpdatedTableZoneIsValid, (cmd) => this.checkTableConfigUpdateIsValid(cmd.config));
|
|
44859
45255
|
case "ADD_MERGE":
|
|
44860
|
-
for (const
|
|
44861
|
-
|
|
44862
|
-
|
|
45256
|
+
for (const table of this.getCoreTables(cmd.sheetId)) {
|
|
45257
|
+
const tableZone = table.range.zone;
|
|
45258
|
+
for (const merge of cmd.target) {
|
|
45259
|
+
if (overlap(tableZone, merge)) {
|
|
44863
45260
|
return "MergeInTable" /* CommandResult.MergeInTable */;
|
|
44864
45261
|
}
|
|
44865
45262
|
}
|
|
@@ -44881,8 +45278,11 @@ class TablePlugin extends CorePlugin {
|
|
|
44881
45278
|
}
|
|
44882
45279
|
case "DUPLICATE_SHEET": {
|
|
44883
45280
|
const newTables = {};
|
|
44884
|
-
for (const table of this.
|
|
44885
|
-
newTables[table.id] =
|
|
45281
|
+
for (const table of this.getCoreTables(cmd.sheetId)) {
|
|
45282
|
+
newTables[table.id] =
|
|
45283
|
+
table.type === "dynamic"
|
|
45284
|
+
? this.copyDynamicTableForSheet(cmd.sheetIdTo, table)
|
|
45285
|
+
: this.copyStaticTableForSheet(cmd.sheetIdTo, table);
|
|
44886
45286
|
}
|
|
44887
45287
|
this.history.update("tables", cmd.sheetIdTo, newTables);
|
|
44888
45288
|
break;
|
|
@@ -44893,14 +45293,17 @@ class TablePlugin extends CorePlugin {
|
|
|
44893
45293
|
const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
|
|
44894
45294
|
this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
|
|
44895
45295
|
const id = this.uuidGenerator.uuidv4();
|
|
44896
|
-
const
|
|
45296
|
+
const config = cmd.config || DEFAULT_TABLE_CONFIG;
|
|
45297
|
+
const newTable = cmd.tableType === "dynamic"
|
|
45298
|
+
? this.createDynamicTable(id, union, config)
|
|
45299
|
+
: this.createStaticTable(id, cmd.tableType, union, config);
|
|
44897
45300
|
this.history.update("tables", cmd.sheetId, newTable.id, newTable);
|
|
44898
45301
|
break;
|
|
44899
45302
|
}
|
|
44900
45303
|
case "REMOVE_TABLE": {
|
|
44901
45304
|
const tables = {};
|
|
44902
|
-
for (const table of this.
|
|
44903
|
-
if (cmd.target.every((zone) => !intersection(
|
|
45305
|
+
for (const table of this.getCoreTables(cmd.sheetId)) {
|
|
45306
|
+
if (cmd.target.every((zone) => !intersection(table.range.zone, zone))) {
|
|
44904
45307
|
tables[table.id] = table;
|
|
44905
45308
|
}
|
|
44906
45309
|
}
|
|
@@ -44908,23 +45311,15 @@ class TablePlugin extends CorePlugin {
|
|
|
44908
45311
|
break;
|
|
44909
45312
|
}
|
|
44910
45313
|
case "UPDATE_TABLE": {
|
|
44911
|
-
|
|
44912
|
-
if (table) {
|
|
44913
|
-
const newTableRange = cmd.newTableRange
|
|
44914
|
-
? this.getters.getRangeFromRangeData(cmd.newTableRange)
|
|
44915
|
-
: undefined;
|
|
44916
|
-
if (newTableRange) {
|
|
44917
|
-
const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, newTableRange.zone);
|
|
44918
|
-
this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
|
|
44919
|
-
}
|
|
44920
|
-
const newTable = this.updateTable(table, newTableRange, cmd.config);
|
|
44921
|
-
this.history.update("tables", cmd.sheetId, table.id, newTable);
|
|
44922
|
-
}
|
|
45314
|
+
this.updateTable(cmd);
|
|
44923
45315
|
break;
|
|
44924
45316
|
}
|
|
44925
45317
|
case "UPDATE_CELL": {
|
|
44926
45318
|
const sheetId = cmd.sheetId;
|
|
44927
|
-
for (const table of this.
|
|
45319
|
+
for (const table of this.getCoreTables(sheetId)) {
|
|
45320
|
+
if (table.type === "dynamic") {
|
|
45321
|
+
continue;
|
|
45322
|
+
}
|
|
44928
45323
|
const direction = this.canUpdateCellCmdExtendTable(cmd, table);
|
|
44929
45324
|
if (direction === "down") {
|
|
44930
45325
|
this.extendTableDown(sheetId, table);
|
|
@@ -44948,66 +45343,24 @@ class TablePlugin extends CorePlugin {
|
|
|
44948
45343
|
}
|
|
44949
45344
|
}
|
|
44950
45345
|
}
|
|
44951
|
-
|
|
44952
|
-
return this.getTables(sheetId)
|
|
44953
|
-
.filter((table) => table.config.hasFilters)
|
|
44954
|
-
.map((table) => table.filters)
|
|
44955
|
-
.flat();
|
|
44956
|
-
}
|
|
44957
|
-
getTables(sheetId) {
|
|
45346
|
+
getCoreTables(sheetId) {
|
|
44958
45347
|
return this.tables[sheetId] ? Object.values(this.tables[sheetId]).filter(isDefined$1) : [];
|
|
44959
45348
|
}
|
|
44960
|
-
|
|
44961
|
-
|
|
44962
|
-
if (!table || !table.config.hasFilters) {
|
|
44963
|
-
return undefined;
|
|
44964
|
-
}
|
|
44965
|
-
return table.filters.find((filter) => filter.col === position.col);
|
|
44966
|
-
}
|
|
44967
|
-
getFilterId(position) {
|
|
44968
|
-
return this.getFilter(position)?.id;
|
|
44969
|
-
}
|
|
44970
|
-
getTable({ sheetId, col, row }) {
|
|
44971
|
-
return this.getTables(sheetId).find((table) => isInside(col, row, table.range.zone));
|
|
44972
|
-
}
|
|
44973
|
-
/** Get the filter tables that are fully inside the given zone */
|
|
44974
|
-
getTablesInZone(sheetId, zone) {
|
|
44975
|
-
return this.getTables(sheetId).filter((table) => isZoneInside(table.range.zone, zone));
|
|
45349
|
+
getCoreTable({ sheetId, col, row }) {
|
|
45350
|
+
return this.getCoreTables(sheetId).find((table) => isInside(col, row, table.range.zone));
|
|
44976
45351
|
}
|
|
44977
45352
|
getTablesOverlappingZones(sheetId, zones) {
|
|
44978
|
-
return this.
|
|
44979
|
-
}
|
|
44980
|
-
doesZonesContainFilter(sheetId, zones) {
|
|
44981
|
-
return (this.getTablesOverlappingZones(sheetId, zones).filter((table) => table.config.hasFilters)
|
|
44982
|
-
.length > 0);
|
|
44983
|
-
}
|
|
44984
|
-
getFilterHeaders(sheetId) {
|
|
44985
|
-
const headers = [];
|
|
44986
|
-
for (const table of this.getTables(sheetId)) {
|
|
44987
|
-
if (!table.config.hasFilters) {
|
|
44988
|
-
continue;
|
|
44989
|
-
}
|
|
44990
|
-
const zone = table.range.zone;
|
|
44991
|
-
const row = zone.top;
|
|
44992
|
-
for (let col = zone.left; col <= zone.right; col++) {
|
|
44993
|
-
headers.push({ col, row });
|
|
44994
|
-
}
|
|
44995
|
-
}
|
|
44996
|
-
return headers;
|
|
44997
|
-
}
|
|
44998
|
-
isFilterHeader({ sheetId, col, row }) {
|
|
44999
|
-
const headers = this.getFilterHeaders(sheetId);
|
|
45000
|
-
return headers.some((header) => header.col === col && header.row === row);
|
|
45353
|
+
return this.getCoreTables(sheetId).filter((table) => zones.some((zone) => overlap(table.range.zone, zone)));
|
|
45001
45354
|
}
|
|
45002
45355
|
/** Extend a table down one row */
|
|
45003
45356
|
extendTableDown(sheetId, table) {
|
|
45004
45357
|
const newRange = this.getters.extendRange(table.range, "ROW", 1);
|
|
45005
|
-
this.history.update("tables", sheetId, table.id, this.
|
|
45358
|
+
this.history.update("tables", sheetId, table.id, this.updateStaticTable(table, newRange));
|
|
45006
45359
|
}
|
|
45007
45360
|
/** Extend a table right one col */
|
|
45008
45361
|
extendTableRight(sheetId, table) {
|
|
45009
45362
|
const newRange = this.getters.extendRange(table.range, "COL", 1);
|
|
45010
|
-
this.history.update("tables", sheetId, table.id, this.
|
|
45363
|
+
this.history.update("tables", sheetId, table.id, this.updateStaticTable(table, newRange));
|
|
45011
45364
|
}
|
|
45012
45365
|
/**
|
|
45013
45366
|
* Check if an UpdateCell command should cause the given table to be extended by one row or col.
|
|
@@ -45044,12 +45397,22 @@ class TablePlugin extends CorePlugin {
|
|
|
45044
45397
|
const cellContent = this.getters.getCell(cellPosition)?.content;
|
|
45045
45398
|
if (cellContent ||
|
|
45046
45399
|
this.getters.isInMerge(cellPosition) ||
|
|
45047
|
-
this.
|
|
45400
|
+
this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
|
|
45048
45401
|
return "none";
|
|
45049
45402
|
}
|
|
45050
45403
|
}
|
|
45051
45404
|
return direction;
|
|
45052
45405
|
}
|
|
45406
|
+
getTableFromZone(sheetId, zone) {
|
|
45407
|
+
for (const table of this.getCoreTables(sheetId)) {
|
|
45408
|
+
const tableZone = table.range.zone;
|
|
45409
|
+
// Only check top left to match dynamic tables
|
|
45410
|
+
if (tableZone.left === zone.left && tableZone.top === zone.top) {
|
|
45411
|
+
return table;
|
|
45412
|
+
}
|
|
45413
|
+
}
|
|
45414
|
+
return undefined;
|
|
45415
|
+
}
|
|
45053
45416
|
checkUpdatedTableZoneIsValid(cmd) {
|
|
45054
45417
|
if (!cmd.newTableRange) {
|
|
45055
45418
|
return "Success" /* CommandResult.Success */;
|
|
@@ -45059,7 +45422,11 @@ class TablePlugin extends CorePlugin {
|
|
|
45059
45422
|
if (zoneIsInSheet !== "Success" /* CommandResult.Success */) {
|
|
45060
45423
|
return zoneIsInSheet;
|
|
45061
45424
|
}
|
|
45062
|
-
const
|
|
45425
|
+
const updatedTable = this.getTableFromZone(cmd.sheetId, cmd.zone);
|
|
45426
|
+
if (!updatedTable) {
|
|
45427
|
+
return "TableNotFound" /* CommandResult.TableNotFound */;
|
|
45428
|
+
}
|
|
45429
|
+
const overlappingTables = this.getTablesOverlappingZones(cmd.sheetId, [newTableZone]).filter((table) => table.id !== updatedTable.id);
|
|
45063
45430
|
return overlappingTables.length ? "TableOverlap" /* CommandResult.TableOverlap */ : "Success" /* CommandResult.Success */;
|
|
45064
45431
|
}
|
|
45065
45432
|
checkTableConfigUpdateIsValid(config) {
|
|
@@ -45077,7 +45444,7 @@ class TablePlugin extends CorePlugin {
|
|
|
45077
45444
|
}
|
|
45078
45445
|
return "Success" /* CommandResult.Success */;
|
|
45079
45446
|
}
|
|
45080
|
-
|
|
45447
|
+
createStaticTable(id, type, tableRange, config, filters) {
|
|
45081
45448
|
const zone = tableRange.zone;
|
|
45082
45449
|
if (!filters) {
|
|
45083
45450
|
filters = [];
|
|
@@ -45092,9 +45459,51 @@ class TablePlugin extends CorePlugin {
|
|
|
45092
45459
|
range: tableRange,
|
|
45093
45460
|
filters,
|
|
45094
45461
|
config,
|
|
45462
|
+
type,
|
|
45095
45463
|
};
|
|
45096
45464
|
}
|
|
45097
|
-
|
|
45465
|
+
createDynamicTable(id, tableRange, config) {
|
|
45466
|
+
const zone = zoneToTopLeft(tableRange.zone);
|
|
45467
|
+
return {
|
|
45468
|
+
id,
|
|
45469
|
+
range: this.getters.getRangeFromZone(tableRange.sheetId, zone),
|
|
45470
|
+
config,
|
|
45471
|
+
type: "dynamic",
|
|
45472
|
+
};
|
|
45473
|
+
}
|
|
45474
|
+
updateTable(cmd) {
|
|
45475
|
+
const table = this.getTableFromZone(cmd.sheetId, cmd.zone);
|
|
45476
|
+
if (!table) {
|
|
45477
|
+
return;
|
|
45478
|
+
}
|
|
45479
|
+
const newTableRange = cmd.newTableRange
|
|
45480
|
+
? this.getters.getRangeFromRangeData(cmd.newTableRange)
|
|
45481
|
+
: undefined;
|
|
45482
|
+
if (newTableRange) {
|
|
45483
|
+
const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, newTableRange.zone);
|
|
45484
|
+
this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
|
|
45485
|
+
}
|
|
45486
|
+
const range = newTableRange || table.range;
|
|
45487
|
+
const newConfig = this.updateTableConfig(cmd.config, table.config);
|
|
45488
|
+
const newTableType = cmd.tableType ?? table.type;
|
|
45489
|
+
if ((newTableType === "dynamic" && table.type !== "dynamic") ||
|
|
45490
|
+
(newTableType !== "dynamic" && table.type === "dynamic")) {
|
|
45491
|
+
const newTable = newTableType === "dynamic"
|
|
45492
|
+
? this.createDynamicTable(table.id, range, newConfig)
|
|
45493
|
+
: this.createStaticTable(table.id, newTableType, range, newConfig);
|
|
45494
|
+
this.history.update("tables", cmd.sheetId, table.id, newTable);
|
|
45495
|
+
}
|
|
45496
|
+
else {
|
|
45497
|
+
const updatedTable = table.type === "dynamic"
|
|
45498
|
+
? this.updateDynamicTable(table, range, newConfig)
|
|
45499
|
+
: this.updateStaticTable(table, range, newConfig, newTableType);
|
|
45500
|
+
this.history.update("tables", cmd.sheetId, table.id, updatedTable);
|
|
45501
|
+
}
|
|
45502
|
+
}
|
|
45503
|
+
updateStaticTable(table, newRange, configUpdate, newTableType = table.type) {
|
|
45504
|
+
if (newTableType === "dynamic") {
|
|
45505
|
+
throw new Error("Cannot use updateStaticTable to update a dynamic table");
|
|
45506
|
+
}
|
|
45098
45507
|
const tableRange = newRange ? newRange : table.range;
|
|
45099
45508
|
const tableZone = tableRange.zone;
|
|
45100
45509
|
const newConfig = this.updateTableConfig(configUpdate, table.config);
|
|
@@ -45115,8 +45524,16 @@ class TablePlugin extends CorePlugin {
|
|
|
45115
45524
|
range: tableRange,
|
|
45116
45525
|
config,
|
|
45117
45526
|
filters: filters.length ? filters : table.filters,
|
|
45527
|
+
type: newTableType,
|
|
45118
45528
|
};
|
|
45119
45529
|
}
|
|
45530
|
+
updateDynamicTable(table, newRange, newConfig) {
|
|
45531
|
+
const range = newRange
|
|
45532
|
+
? this.getters.getRangeFromZone(newRange.sheetId, zoneToTopLeft(newRange.zone))
|
|
45533
|
+
: table.range;
|
|
45534
|
+
const config = newConfig ? newConfig : table.config;
|
|
45535
|
+
return { ...table, range, config };
|
|
45536
|
+
}
|
|
45120
45537
|
/**
|
|
45121
45538
|
* Update the old config of a table with the new partial config from an UpdateTable command.
|
|
45122
45539
|
*
|
|
@@ -45138,35 +45555,29 @@ class TablePlugin extends CorePlugin {
|
|
|
45138
45555
|
}
|
|
45139
45556
|
createFilterFromZone(id, sheetId, zone, config) {
|
|
45140
45557
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
45141
|
-
return
|
|
45142
|
-
}
|
|
45143
|
-
createFilter(id, range, config) {
|
|
45144
|
-
const zone = range.zone;
|
|
45145
|
-
if (zone.left !== zone.right) {
|
|
45146
|
-
throw new Error("Can only define a filter on a single column");
|
|
45147
|
-
}
|
|
45148
|
-
const contentZone = getTableContentZone(zone, config);
|
|
45149
|
-
const filteredRange = contentZone
|
|
45150
|
-
? this.getters.getRangeFromZone(range.sheetId, contentZone)
|
|
45151
|
-
: undefined;
|
|
45152
|
-
return {
|
|
45153
|
-
id,
|
|
45154
|
-
rangeWithHeaders: range,
|
|
45155
|
-
col: zone.left,
|
|
45156
|
-
filteredRange,
|
|
45157
|
-
};
|
|
45558
|
+
return createFilter(id, range, config, this.getters.getRangeFromZone);
|
|
45158
45559
|
}
|
|
45159
|
-
|
|
45560
|
+
copyStaticTableForSheet(sheetId, table) {
|
|
45160
45561
|
const newRange = this.getters.getRangeFromZone(sheetId, table.range.zone);
|
|
45161
45562
|
const newFilters = table.filters.map((filter) => {
|
|
45162
45563
|
const newFilterRange = this.getters.getRangeFromZone(sheetId, filter.rangeWithHeaders.zone);
|
|
45163
|
-
return
|
|
45564
|
+
return createFilter(filter.id, newFilterRange, table.config, this.getters.getRangeFromZone);
|
|
45164
45565
|
});
|
|
45165
45566
|
return {
|
|
45166
45567
|
id: table.id,
|
|
45167
45568
|
range: newRange,
|
|
45168
45569
|
filters: newFilters,
|
|
45169
45570
|
config: deepCopy(table.config),
|
|
45571
|
+
type: table.type,
|
|
45572
|
+
};
|
|
45573
|
+
}
|
|
45574
|
+
copyDynamicTableForSheet(sheetId, table) {
|
|
45575
|
+
const newRange = this.getters.getRangeFromZone(sheetId, table.range.zone);
|
|
45576
|
+
return {
|
|
45577
|
+
id: table.id,
|
|
45578
|
+
range: newRange,
|
|
45579
|
+
config: deepCopy(table.config),
|
|
45580
|
+
type: "dynamic",
|
|
45170
45581
|
};
|
|
45171
45582
|
}
|
|
45172
45583
|
applyRangeChangeOnTable(sheetId, table, applyChange) {
|
|
@@ -45181,6 +45592,11 @@ class TablePlugin extends CorePlugin {
|
|
|
45181
45592
|
default:
|
|
45182
45593
|
newTableRange = tableRangeChange.range;
|
|
45183
45594
|
}
|
|
45595
|
+
if (table.type === "dynamic") {
|
|
45596
|
+
const newTable = this.updateDynamicTable(table, newTableRange);
|
|
45597
|
+
this.history.update("tables", sheetId, table.id, newTable);
|
|
45598
|
+
return;
|
|
45599
|
+
}
|
|
45184
45600
|
const filters = [];
|
|
45185
45601
|
for (const filter of table.filters) {
|
|
45186
45602
|
const filterRangeChange = applyChange(filter.rangeWithHeaders);
|
|
@@ -45192,7 +45608,7 @@ class TablePlugin extends CorePlugin {
|
|
|
45192
45608
|
break;
|
|
45193
45609
|
default:
|
|
45194
45610
|
const newFilterRange = filterRangeChange.range;
|
|
45195
|
-
const newFilter =
|
|
45611
|
+
const newFilter = createFilter(filter.id, newFilterRange, table.config, this.getters.getRangeFromZone);
|
|
45196
45612
|
filters.push(newFilter);
|
|
45197
45613
|
}
|
|
45198
45614
|
}
|
|
@@ -45207,7 +45623,7 @@ class TablePlugin extends CorePlugin {
|
|
|
45207
45623
|
}
|
|
45208
45624
|
filters.sort((f1, f2) => f1.col - f2.col);
|
|
45209
45625
|
}
|
|
45210
|
-
const newTable = this.
|
|
45626
|
+
const newTable = this.createStaticTable(table.id, table.type, newTableRange, table.config, filters);
|
|
45211
45627
|
this.history.update("tables", sheetId, table.id, newTable);
|
|
45212
45628
|
}
|
|
45213
45629
|
// ---------------------------------------------------------------------------
|
|
@@ -45218,16 +45634,20 @@ class TablePlugin extends CorePlugin {
|
|
|
45218
45634
|
for (const tableData of sheet.tables || []) {
|
|
45219
45635
|
const uuid = this.uuidGenerator.uuidv4();
|
|
45220
45636
|
const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
|
|
45221
|
-
const
|
|
45222
|
-
const
|
|
45637
|
+
const range = this.getters.getRangeFromSheetXC(sheet.id, tableData.range);
|
|
45638
|
+
const tableType = tableData.type || "static";
|
|
45639
|
+
const table = tableType === "dynamic"
|
|
45640
|
+
? this.createDynamicTable(uuid, range, tableConfig)
|
|
45641
|
+
: this.createStaticTable(uuid, tableType, range, tableConfig);
|
|
45223
45642
|
this.history.update("tables", sheet.id, table.id, table);
|
|
45224
45643
|
}
|
|
45225
45644
|
}
|
|
45226
45645
|
}
|
|
45227
45646
|
export(data) {
|
|
45228
45647
|
for (const sheet of data.sheets) {
|
|
45229
|
-
for (const table of this.
|
|
45230
|
-
const
|
|
45648
|
+
for (const table of this.getCoreTables(sheet.id)) {
|
|
45649
|
+
const range = zoneToXc(table.range.zone);
|
|
45650
|
+
const tableData = { range, type: table.type };
|
|
45231
45651
|
if (!deepEquals(table.config, DEFAULT_TABLE_CONFIG)) {
|
|
45232
45652
|
tableData.config = table.config;
|
|
45233
45653
|
}
|
|
@@ -45790,12 +46210,6 @@ class CompilationParametersBuilder {
|
|
|
45790
46210
|
: _t("Function %s expects its parameters to be single values or single cell references, not ranges.", functionName.toString()));
|
|
45791
46211
|
}
|
|
45792
46212
|
const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
|
|
45793
|
-
return this.readCell(position);
|
|
45794
|
-
}
|
|
45795
|
-
readCell(position) {
|
|
45796
|
-
if (!this.getters.tryGetSheet(position.sheetId)) {
|
|
45797
|
-
throw new EvaluationError(_t("Invalid sheet name"));
|
|
45798
|
-
}
|
|
45799
46213
|
return this.computeCell(position);
|
|
45800
46214
|
}
|
|
45801
46215
|
/**
|
|
@@ -45831,7 +46245,7 @@ class CompilationParametersBuilder {
|
|
|
45831
46245
|
matrix[colIndex] = new Array(height);
|
|
45832
46246
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
45833
46247
|
const rowIndex = row - _zone.top;
|
|
45834
|
-
matrix[colIndex][rowIndex] = this.
|
|
46248
|
+
matrix[colIndex][rowIndex] = this.computeCell({ sheetId, col, row });
|
|
45835
46249
|
}
|
|
45836
46250
|
}
|
|
45837
46251
|
this.rangeCache[cacheKey] = matrix;
|
|
@@ -46939,18 +47353,22 @@ class Evaluator {
|
|
|
46939
47353
|
getEvaluatedCell(position) {
|
|
46940
47354
|
return this.evaluatedCells.get(position) || EMPTY_CELL;
|
|
46941
47355
|
}
|
|
46942
|
-
|
|
47356
|
+
getSpreadZone(position) {
|
|
46943
47357
|
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
46944
|
-
return
|
|
47358
|
+
return undefined;
|
|
46945
47359
|
}
|
|
46946
|
-
|
|
47360
|
+
if (this.evaluatedCells.get(position)?.type === CellValueType.error) {
|
|
47361
|
+
return positionToZone(position);
|
|
47362
|
+
}
|
|
47363
|
+
const spreadPositions = Array.from(this.spreadingRelations.getArrayResultPositions(position));
|
|
47364
|
+
return union(positionToZone(position), unionPositionsToZone(spreadPositions));
|
|
46947
47365
|
}
|
|
46948
47366
|
getEvaluatedPositions() {
|
|
46949
47367
|
return this.evaluatedCells.keys();
|
|
46950
47368
|
}
|
|
46951
47369
|
getArrayFormulaSpreadingOn(position) {
|
|
46952
47370
|
if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
|
|
46953
|
-
return undefined;
|
|
47371
|
+
return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
|
|
46954
47372
|
}
|
|
46955
47373
|
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
|
|
46956
47374
|
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
@@ -47079,6 +47497,7 @@ class Evaluator {
|
|
|
47079
47497
|
if (!this.blockedArrayFormulas.has(position)) {
|
|
47080
47498
|
this.invalidateSpreading(position);
|
|
47081
47499
|
}
|
|
47500
|
+
this.spreadingRelations.removeNode(position);
|
|
47082
47501
|
const cell = this.getters.getCell(position);
|
|
47083
47502
|
if (cell === undefined) {
|
|
47084
47503
|
return EMPTY_CELL;
|
|
@@ -47189,7 +47608,6 @@ class Evaluator {
|
|
|
47189
47608
|
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
47190
47609
|
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
|
|
47191
47610
|
}
|
|
47192
|
-
this.spreadingRelations.removeNode(position);
|
|
47193
47611
|
}
|
|
47194
47612
|
// ----------------------------------------------------------
|
|
47195
47613
|
// COMMON FUNCTIONALITY
|
|
@@ -47348,7 +47766,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47348
47766
|
"getEvaluatedCell",
|
|
47349
47767
|
"getEvaluatedCells",
|
|
47350
47768
|
"getEvaluatedCellsInZone",
|
|
47351
|
-
"
|
|
47769
|
+
"getSpreadZone",
|
|
47352
47770
|
"getArrayFormulaSpreadingOn",
|
|
47353
47771
|
"isEmpty",
|
|
47354
47772
|
];
|
|
@@ -47454,8 +47872,11 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47454
47872
|
getEvaluatedCellsInZone(sheetId, zone) {
|
|
47455
47873
|
return positions(zone).map(({ col, row }) => this.getters.getEvaluatedCell({ sheetId, col, row }));
|
|
47456
47874
|
}
|
|
47457
|
-
|
|
47458
|
-
|
|
47875
|
+
/**
|
|
47876
|
+
* Return the spread zone the position is part of, if any
|
|
47877
|
+
*/
|
|
47878
|
+
getSpreadZone(position) {
|
|
47879
|
+
return this.evaluator.getSpreadZone(position);
|
|
47459
47880
|
}
|
|
47460
47881
|
getArrayFormulaSpreadingOn(position) {
|
|
47461
47882
|
return this.evaluator.getArrayFormulaSpreadingOn(position);
|
|
@@ -47495,7 +47916,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47495
47916
|
? getItemId(newFormat, data.formats)
|
|
47496
47917
|
: exportedCellData.format;
|
|
47497
47918
|
let content;
|
|
47498
|
-
if (formulaCell instanceof FormulaCellWithDependencies) {
|
|
47919
|
+
if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
|
|
47499
47920
|
content = formulaCell.contentWithFixedReferences;
|
|
47500
47921
|
}
|
|
47501
47922
|
else {
|
|
@@ -47543,17 +47964,17 @@ function isBadExpression(tokens) {
|
|
|
47543
47964
|
*/
|
|
47544
47965
|
function sortWithClusters(colorsToSort) {
|
|
47545
47966
|
const clusters = [
|
|
47546
|
-
{ leadColor: rgba(255, 0, 0), colors: [] },
|
|
47547
|
-
{ leadColor: rgba(255, 128, 0), colors: [] },
|
|
47548
|
-
{ leadColor: rgba(128, 128, 0), colors: [] },
|
|
47549
|
-
{ leadColor: rgba(128, 255, 0), colors: [] },
|
|
47550
|
-
{ leadColor: rgba(0, 255, 0), colors: [] },
|
|
47551
|
-
{ leadColor: rgba(0, 255, 128), colors: [] },
|
|
47552
|
-
{ leadColor: rgba(0, 255, 255), colors: [] },
|
|
47553
|
-
{ leadColor: rgba(0, 127, 255), colors: [] },
|
|
47554
|
-
{ leadColor: rgba(0, 0, 255), colors: [] },
|
|
47555
|
-
{ leadColor: rgba(127, 0, 255), colors: [] },
|
|
47556
|
-
{ 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
|
|
47557
47978
|
{ leadColor: rgba(255, 0, 128), colors: [] }, // rose
|
|
47558
47979
|
];
|
|
47559
47980
|
for (const color of colorsToSort.map(colorToRGBA)) {
|
|
@@ -47944,13 +48365,13 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
47944
48365
|
.map((cell) => cell.value);
|
|
47945
48366
|
switch (threshold.type) {
|
|
47946
48367
|
case "value":
|
|
47947
|
-
const result = functionName === "max" ?
|
|
48368
|
+
const result = functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
|
|
47948
48369
|
return result;
|
|
47949
48370
|
case "number":
|
|
47950
48371
|
return Number(threshold.value);
|
|
47951
48372
|
case "percentage":
|
|
47952
|
-
const min =
|
|
47953
|
-
const max =
|
|
48373
|
+
const min = largeMin(rangeValues);
|
|
48374
|
+
const max = largeMax(rangeValues);
|
|
47954
48375
|
const delta = max - min;
|
|
47955
48376
|
return min + (delta * Number(threshold.value)) / 100;
|
|
47956
48377
|
case "percentile":
|
|
@@ -48287,6 +48708,180 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
48287
48708
|
}
|
|
48288
48709
|
}
|
|
48289
48710
|
|
|
48711
|
+
class DynamicTablesPlugin extends UIPlugin {
|
|
48712
|
+
static getters = [
|
|
48713
|
+
"canCreateDynamicTableOnZones",
|
|
48714
|
+
"doesZonesContainFilter",
|
|
48715
|
+
"getFilter",
|
|
48716
|
+
"getFilters",
|
|
48717
|
+
"getTable",
|
|
48718
|
+
"getTables",
|
|
48719
|
+
"getTablesOverlappingZones",
|
|
48720
|
+
"getFilterId",
|
|
48721
|
+
"getFilterHeaders",
|
|
48722
|
+
"isFilterHeader",
|
|
48723
|
+
];
|
|
48724
|
+
tables = {};
|
|
48725
|
+
handle(cmd) {
|
|
48726
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
48727
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
48728
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
48729
|
+
this.tables = {};
|
|
48730
|
+
return;
|
|
48731
|
+
}
|
|
48732
|
+
switch (cmd.type) {
|
|
48733
|
+
case "CREATE_TABLE":
|
|
48734
|
+
case "REMOVE_TABLE":
|
|
48735
|
+
case "UPDATE_TABLE":
|
|
48736
|
+
case "DELETE_CONTENT":
|
|
48737
|
+
this.tables = {};
|
|
48738
|
+
break;
|
|
48739
|
+
}
|
|
48740
|
+
}
|
|
48741
|
+
finalize() {
|
|
48742
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
48743
|
+
if (!this.tables[sheetId]) {
|
|
48744
|
+
this.tables[sheetId] = this.computeTables(sheetId);
|
|
48745
|
+
}
|
|
48746
|
+
}
|
|
48747
|
+
}
|
|
48748
|
+
computeTables(sheetId) {
|
|
48749
|
+
const tables = [];
|
|
48750
|
+
const coreTables = this.getters.getCoreTables(sheetId);
|
|
48751
|
+
// First we create the static tables, so we can use them to compute collision with dynamic tables
|
|
48752
|
+
for (const table of coreTables) {
|
|
48753
|
+
if (table.type === "dynamic")
|
|
48754
|
+
continue;
|
|
48755
|
+
tables.push(table);
|
|
48756
|
+
}
|
|
48757
|
+
const staticTables = [...tables];
|
|
48758
|
+
// Then we create the dynamic tables
|
|
48759
|
+
for (const coreTable of coreTables) {
|
|
48760
|
+
if (coreTable.type !== "dynamic")
|
|
48761
|
+
continue;
|
|
48762
|
+
const table = this.coreTableToTable(sheetId, coreTable);
|
|
48763
|
+
let tableZone = table.range.zone;
|
|
48764
|
+
// Reduce the zone to avoid collision with static tables. Per design, dynamic tables can't overlap with other
|
|
48765
|
+
// dynamic tables, because formulas cannot spread on the same area, so we don't need to check for that.
|
|
48766
|
+
for (const staticTable of staticTables) {
|
|
48767
|
+
if (overlap(tableZone, staticTable.range.zone)) {
|
|
48768
|
+
tableZone = { ...tableZone, right: staticTable.range.zone.left - 1 };
|
|
48769
|
+
}
|
|
48770
|
+
}
|
|
48771
|
+
tables.push({ ...table, range: this.getters.getRangeFromZone(sheetId, tableZone) });
|
|
48772
|
+
}
|
|
48773
|
+
return tables;
|
|
48774
|
+
}
|
|
48775
|
+
getFilters(sheetId) {
|
|
48776
|
+
return this.getTables(sheetId)
|
|
48777
|
+
.filter((table) => table.config.hasFilters)
|
|
48778
|
+
.map((table) => table.filters)
|
|
48779
|
+
.flat();
|
|
48780
|
+
}
|
|
48781
|
+
getTables(sheetId) {
|
|
48782
|
+
return this.tables[sheetId] || [];
|
|
48783
|
+
}
|
|
48784
|
+
getFilter(position) {
|
|
48785
|
+
const table = this.getTable(position);
|
|
48786
|
+
if (!table || !table.config.hasFilters) {
|
|
48787
|
+
return undefined;
|
|
48788
|
+
}
|
|
48789
|
+
return table.filters.find((filter) => filter.col === position.col);
|
|
48790
|
+
}
|
|
48791
|
+
getFilterId(position) {
|
|
48792
|
+
return this.getFilter(position)?.id;
|
|
48793
|
+
}
|
|
48794
|
+
getTable({ sheetId, col, row }) {
|
|
48795
|
+
return this.getTables(sheetId).find((table) => isInside(col, row, table.range.zone));
|
|
48796
|
+
}
|
|
48797
|
+
getTablesOverlappingZones(sheetId, zones) {
|
|
48798
|
+
return this.getTables(sheetId).filter((table) => zones.some((zone) => overlap(table.range.zone, zone)));
|
|
48799
|
+
}
|
|
48800
|
+
doesZonesContainFilter(sheetId, zones) {
|
|
48801
|
+
return this.getTablesOverlappingZones(sheetId, zones).some((table) => table.config.hasFilters);
|
|
48802
|
+
}
|
|
48803
|
+
getFilterHeaders(sheetId) {
|
|
48804
|
+
const headers = [];
|
|
48805
|
+
for (const table of this.getTables(sheetId)) {
|
|
48806
|
+
if (!table.config.hasFilters) {
|
|
48807
|
+
continue;
|
|
48808
|
+
}
|
|
48809
|
+
const zone = table.range.zone;
|
|
48810
|
+
const row = zone.top;
|
|
48811
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
48812
|
+
headers.push({ sheetId, col, row });
|
|
48813
|
+
}
|
|
48814
|
+
}
|
|
48815
|
+
return headers;
|
|
48816
|
+
}
|
|
48817
|
+
isFilterHeader({ sheetId, col, row }) {
|
|
48818
|
+
const headers = this.getFilterHeaders(sheetId);
|
|
48819
|
+
return headers.some((header) => header.col === col && header.row === row);
|
|
48820
|
+
}
|
|
48821
|
+
/**
|
|
48822
|
+
* Check if we can create a dynamic table on the given zones.
|
|
48823
|
+
* - The zones must be continuous
|
|
48824
|
+
* - The union of the zones must be either:
|
|
48825
|
+
* - A single cell that contains an array formula
|
|
48826
|
+
* - All the spread cells of a single array formula
|
|
48827
|
+
*/
|
|
48828
|
+
canCreateDynamicTableOnZones(sheetId, zones) {
|
|
48829
|
+
if (!areZonesContinuous(zones)) {
|
|
48830
|
+
return false;
|
|
48831
|
+
}
|
|
48832
|
+
const unionZone = union(...zones);
|
|
48833
|
+
const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
|
|
48834
|
+
const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
|
|
48835
|
+
if (!parentSpreadingCell) {
|
|
48836
|
+
return false;
|
|
48837
|
+
}
|
|
48838
|
+
else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
|
|
48839
|
+
return true;
|
|
48840
|
+
}
|
|
48841
|
+
const zone = this.getters.getSpreadZone(parentSpreadingCell);
|
|
48842
|
+
return deepEquals(unionZone, zone);
|
|
48843
|
+
}
|
|
48844
|
+
coreTableToTable(sheetId, table) {
|
|
48845
|
+
if (table.type !== "dynamic") {
|
|
48846
|
+
return table;
|
|
48847
|
+
}
|
|
48848
|
+
const tableZone = table.range.zone;
|
|
48849
|
+
const tablePosition = { sheetId, col: tableZone.left, row: tableZone.top };
|
|
48850
|
+
const zone = this.getters.getSpreadZone(tablePosition) ?? table.range.zone;
|
|
48851
|
+
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
48852
|
+
const filters = this.getDynamicTableFilters(sheetId, table, zone);
|
|
48853
|
+
return { id: table.id, range, filters, config: table.config };
|
|
48854
|
+
}
|
|
48855
|
+
getDynamicTableFilters(sheetId, table, tableZone) {
|
|
48856
|
+
const filters = [];
|
|
48857
|
+
const { top, bottom, left, right } = tableZone;
|
|
48858
|
+
for (let col = left; col <= right; col++) {
|
|
48859
|
+
const tableColIndex = col - left;
|
|
48860
|
+
const zone = { left: col, right: col, top, bottom };
|
|
48861
|
+
const filter = createFilter(this.getDynamicTableFilterId(table.id, tableColIndex), this.getters.getRangeFromZone(sheetId, zone), table.config, this.getters.getRangeFromZone);
|
|
48862
|
+
filters.push(filter);
|
|
48863
|
+
}
|
|
48864
|
+
return filters;
|
|
48865
|
+
}
|
|
48866
|
+
getDynamicTableFilterId(tableId, tableCol) {
|
|
48867
|
+
return tableId + "_" + tableCol;
|
|
48868
|
+
}
|
|
48869
|
+
exportForExcel(data) {
|
|
48870
|
+
for (const sheet of data.sheets) {
|
|
48871
|
+
for (const tableData of sheet.tables) {
|
|
48872
|
+
const zone = toZone(tableData.range);
|
|
48873
|
+
const topLeft = { sheetId: sheet.id, col: zone.left, row: zone.top };
|
|
48874
|
+
const coreTable = this.getters.getCoreTable(topLeft);
|
|
48875
|
+
const table = this.getTable(topLeft);
|
|
48876
|
+
if (coreTable?.type !== "dynamic" || !table) {
|
|
48877
|
+
continue;
|
|
48878
|
+
}
|
|
48879
|
+
tableData.range = zoneToXc(table.range.zone);
|
|
48880
|
+
}
|
|
48881
|
+
}
|
|
48882
|
+
}
|
|
48883
|
+
}
|
|
48884
|
+
|
|
48290
48885
|
class HeaderSizeUIPlugin extends UIPlugin {
|
|
48291
48886
|
static getters = ["getRowSize", "getHeaderSize"];
|
|
48292
48887
|
tallestCellInRow = {};
|
|
@@ -48702,8 +49297,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
48702
49297
|
let row = zone.bottom;
|
|
48703
49298
|
if (col > 0) {
|
|
48704
49299
|
let leftPosition = { sheetId, col: col - 1, row };
|
|
48705
|
-
while (this.getters.
|
|
48706
|
-
this.getters.getCell(leftPosition)?.content) {
|
|
49300
|
+
while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty) {
|
|
48707
49301
|
row += 1;
|
|
48708
49302
|
leftPosition = { sheetId, col: col - 1, row };
|
|
48709
49303
|
}
|
|
@@ -48712,8 +49306,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
48712
49306
|
col = zone.right;
|
|
48713
49307
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
48714
49308
|
let rightPosition = { sheetId, col: col + 1, row };
|
|
48715
|
-
while (this.getters.
|
|
48716
|
-
this.getters.getCell(rightPosition)?.content) {
|
|
49309
|
+
while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty) {
|
|
48717
49310
|
row += 1;
|
|
48718
49311
|
rightPosition = { sheetId, col: col + 1, row };
|
|
48719
49312
|
}
|
|
@@ -49023,13 +49616,13 @@ class AutomaticSumPlugin extends UIPlugin {
|
|
|
49023
49616
|
const cells = this.getters.getEvaluatedCellsInZone(sheet.id, zone);
|
|
49024
49617
|
const cellPositions = range(end, -1, -1);
|
|
49025
49618
|
const invalidCells = cellPositions.filter((position) => cells[position] && !cells[position].isAutoSummable);
|
|
49026
|
-
const maxValidPosition =
|
|
49619
|
+
const maxValidPosition = largeMax(invalidCells);
|
|
49027
49620
|
const numberSequences = groupConsecutive(cellPositions.filter((position) => this.isNumber(cells[position])));
|
|
49028
49621
|
const firstSequence = numberSequences[0] || [];
|
|
49029
|
-
if (
|
|
49622
|
+
if (largeMax(firstSequence) < maxValidPosition) {
|
|
49030
49623
|
return Infinity;
|
|
49031
49624
|
}
|
|
49032
|
-
return
|
|
49625
|
+
return largeMin(firstSequence);
|
|
49033
49626
|
}
|
|
49034
49627
|
shouldFindData(sheetId, zone) {
|
|
49035
49628
|
return this.getters.isEmpty(sheetId, zone) || this.getters.isSingleCellOrMerge(sheetId, zone);
|
|
@@ -50604,8 +51197,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50604
51197
|
static getters = [
|
|
50605
51198
|
"doesCellHaveGridIcon",
|
|
50606
51199
|
"getCellWidth",
|
|
50607
|
-
"getCellComputedBorder",
|
|
50608
|
-
"getCellComputedStyle",
|
|
50609
51200
|
"getTextWidth",
|
|
50610
51201
|
"getCellText",
|
|
50611
51202
|
"getCellMultiLineText",
|
|
@@ -50649,7 +51240,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50649
51240
|
// Getters
|
|
50650
51241
|
// ---------------------------------------------------------------------------
|
|
50651
51242
|
getCellWidth(position) {
|
|
50652
|
-
const style = this.getCellComputedStyle(position);
|
|
51243
|
+
const style = this.getters.getCellComputedStyle(position);
|
|
50653
51244
|
let contentWidth = 0;
|
|
50654
51245
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
50655
51246
|
if (content) {
|
|
@@ -50742,35 +51333,12 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50742
51333
|
*/
|
|
50743
51334
|
isCellEmpty(position) {
|
|
50744
51335
|
const mainPosition = this.getters.getMainCellPosition(position);
|
|
50745
|
-
return
|
|
50746
|
-
this.getters.getCell(mainPosition)?.content);
|
|
50747
|
-
}
|
|
50748
|
-
getCellComputedBorder(position) {
|
|
50749
|
-
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
50750
|
-
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
50751
|
-
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
50752
|
-
const border = { ...cellTableBorder, ...removeFalsyAttributes(cellBorder) };
|
|
50753
|
-
return isObjectEmptyRecursive(border) ? null : border;
|
|
50754
|
-
}
|
|
50755
|
-
getCellComputedStyle(position) {
|
|
50756
|
-
const cell = this.getters.getCell(position);
|
|
50757
|
-
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
50758
|
-
const tableStyle = this.getters.getCellTableStyle(position);
|
|
50759
|
-
const computedStyle = {
|
|
50760
|
-
...removeFalsyAttributes(tableStyle),
|
|
50761
|
-
...removeFalsyAttributes(cell?.style),
|
|
50762
|
-
...removeFalsyAttributes(cfStyle),
|
|
50763
|
-
};
|
|
50764
|
-
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
50765
|
-
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
50766
|
-
computedStyle.textColor = LINK_COLOR;
|
|
50767
|
-
}
|
|
50768
|
-
return computedStyle;
|
|
51336
|
+
return this.getters.getEvaluatedCell(mainPosition).type === CellValueType.empty;
|
|
50769
51337
|
}
|
|
50770
51338
|
getColMaxWidth(sheetId, index) {
|
|
50771
51339
|
const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
|
|
50772
51340
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
50773
|
-
return Math.max(0,
|
|
51341
|
+
return Math.max(0, largeMax(sizes));
|
|
50774
51342
|
}
|
|
50775
51343
|
/**
|
|
50776
51344
|
* Check that any "sheetId" in the command matches an existing
|
|
@@ -50799,6 +51367,236 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50799
51367
|
}
|
|
50800
51368
|
}
|
|
50801
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
|
+
|
|
50802
51600
|
const genericRepeatsTransforms = [
|
|
50803
51601
|
repeatSheetDependantCommand,
|
|
50804
51602
|
repeatTargetDependantCommand,
|
|
@@ -51334,10 +52132,11 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
51334
52132
|
handle(cmd) {
|
|
51335
52133
|
switch (cmd.type) {
|
|
51336
52134
|
case "AUTOFILL_TABLE_COLUMN":
|
|
51337
|
-
const table = this.getters.
|
|
52135
|
+
const table = this.getters.getCoreTable(cmd);
|
|
51338
52136
|
const cell = this.getters.getCell(cmd);
|
|
51339
|
-
if (!table ||
|
|
52137
|
+
if (!table?.config.automaticAutofill || table.type === "dynamic" || !cell?.isFormula) {
|
|
51340
52138
|
return;
|
|
52139
|
+
}
|
|
51341
52140
|
const { col, row } = cmd;
|
|
51342
52141
|
const tableContentZone = getTableContentZone(table.range.zone, table.config);
|
|
51343
52142
|
if (tableContentZone && isInside(col, row, tableContentZone)) {
|
|
@@ -51375,148 +52174,6 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
51375
52174
|
}
|
|
51376
52175
|
}
|
|
51377
52176
|
|
|
51378
|
-
class TableStylePlugin extends UIPlugin {
|
|
51379
|
-
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51380
|
-
tableStyles = {};
|
|
51381
|
-
handle(cmd) {
|
|
51382
|
-
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51383
|
-
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51384
|
-
cmd.type === "EVALUATE_CELLS") {
|
|
51385
|
-
this.tableStyles = {};
|
|
51386
|
-
return;
|
|
51387
|
-
}
|
|
51388
|
-
switch (cmd.type) {
|
|
51389
|
-
case "HIDE_COLUMNS_ROWS":
|
|
51390
|
-
case "UNHIDE_COLUMNS_ROWS":
|
|
51391
|
-
case "UNFOLD_HEADER_GROUP":
|
|
51392
|
-
case "FOLD_HEADER_GROUP":
|
|
51393
|
-
case "FOLD_ALL_HEADER_GROUPS":
|
|
51394
|
-
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
51395
|
-
case "UPDATE_TABLE":
|
|
51396
|
-
case "UPDATE_FILTER":
|
|
51397
|
-
delete this.tableStyles[cmd.sheetId];
|
|
51398
|
-
break;
|
|
51399
|
-
}
|
|
51400
|
-
}
|
|
51401
|
-
finalize() {
|
|
51402
|
-
for (const sheetId of this.getters.getSheetIds()) {
|
|
51403
|
-
if (!this.tableStyles[sheetId]) {
|
|
51404
|
-
this.tableStyles[sheetId] = {};
|
|
51405
|
-
}
|
|
51406
|
-
for (const table of this.getters.getTables(sheetId)) {
|
|
51407
|
-
if (!this.tableStyles[sheetId][table.id]) {
|
|
51408
|
-
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51409
|
-
}
|
|
51410
|
-
}
|
|
51411
|
-
}
|
|
51412
|
-
}
|
|
51413
|
-
getCellTableStyle(position) {
|
|
51414
|
-
const table = this.getters.getTable(position);
|
|
51415
|
-
if (!table) {
|
|
51416
|
-
return undefined;
|
|
51417
|
-
}
|
|
51418
|
-
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51419
|
-
}
|
|
51420
|
-
getCellTableBorder(position) {
|
|
51421
|
-
const table = this.getters.getTable(position);
|
|
51422
|
-
if (!table) {
|
|
51423
|
-
return undefined;
|
|
51424
|
-
}
|
|
51425
|
-
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51426
|
-
}
|
|
51427
|
-
computeTableStyle(sheetId, table) {
|
|
51428
|
-
return lazy(() => {
|
|
51429
|
-
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51430
|
-
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51431
|
-
// Return the style with sheet coordinates instead of tables coordinates
|
|
51432
|
-
const mapping = this.getTableMapping(sheetId, table);
|
|
51433
|
-
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51434
|
-
for (let col = 0; col < numberOfCols; col++) {
|
|
51435
|
-
const colInSheet = mapping.colMapping[col];
|
|
51436
|
-
absoluteTableStyle.borders[colInSheet] = {};
|
|
51437
|
-
absoluteTableStyle.styles[colInSheet] = {};
|
|
51438
|
-
for (let row = 0; row < numberOfRows; row++) {
|
|
51439
|
-
const rowInSheet = mapping.rowMapping[row];
|
|
51440
|
-
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51441
|
-
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51442
|
-
}
|
|
51443
|
-
}
|
|
51444
|
-
return absoluteTableStyle;
|
|
51445
|
-
});
|
|
51446
|
-
}
|
|
51447
|
-
/**
|
|
51448
|
-
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51449
|
-
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51450
|
-
* hidden rows from config.numberOfHeaders.
|
|
51451
|
-
*/
|
|
51452
|
-
getTableRuntimeConfig(sheetId, table) {
|
|
51453
|
-
const tableZone = table.range.zone;
|
|
51454
|
-
const config = { ...table.config };
|
|
51455
|
-
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51456
|
-
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51457
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51458
|
-
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51459
|
-
continue;
|
|
51460
|
-
}
|
|
51461
|
-
numberOfRows--;
|
|
51462
|
-
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51463
|
-
config.numberOfHeaders--;
|
|
51464
|
-
if (config.numberOfHeaders < 0) {
|
|
51465
|
-
config.numberOfHeaders = 0;
|
|
51466
|
-
}
|
|
51467
|
-
}
|
|
51468
|
-
if (row === tableZone.bottom) {
|
|
51469
|
-
config.totalRow = false;
|
|
51470
|
-
}
|
|
51471
|
-
}
|
|
51472
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51473
|
-
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51474
|
-
continue;
|
|
51475
|
-
}
|
|
51476
|
-
numberOfCols--;
|
|
51477
|
-
if (col === tableZone.left) {
|
|
51478
|
-
config.firstColumn = false;
|
|
51479
|
-
}
|
|
51480
|
-
if (col === tableZone.right) {
|
|
51481
|
-
config.lastColumn = false;
|
|
51482
|
-
}
|
|
51483
|
-
}
|
|
51484
|
-
return {
|
|
51485
|
-
config,
|
|
51486
|
-
numberOfCols,
|
|
51487
|
-
numberOfRows,
|
|
51488
|
-
};
|
|
51489
|
-
}
|
|
51490
|
-
/**
|
|
51491
|
-
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51492
|
-
*/
|
|
51493
|
-
getTableMapping(sheetId, table) {
|
|
51494
|
-
const colMapping = {};
|
|
51495
|
-
const rowMapping = {};
|
|
51496
|
-
let colOffset = 0;
|
|
51497
|
-
let rowOffset = 0;
|
|
51498
|
-
const tableZone = table.range.zone;
|
|
51499
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51500
|
-
if (this.getters.isColHidden(sheetId, col)) {
|
|
51501
|
-
continue;
|
|
51502
|
-
}
|
|
51503
|
-
colMapping[colOffset] = col;
|
|
51504
|
-
colOffset++;
|
|
51505
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51506
|
-
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51507
|
-
continue;
|
|
51508
|
-
}
|
|
51509
|
-
rowMapping[rowOffset] = row;
|
|
51510
|
-
rowOffset++;
|
|
51511
|
-
}
|
|
51512
|
-
}
|
|
51513
|
-
return {
|
|
51514
|
-
colMapping,
|
|
51515
|
-
rowMapping,
|
|
51516
|
-
};
|
|
51517
|
-
}
|
|
51518
|
-
}
|
|
51519
|
-
|
|
51520
52177
|
/**
|
|
51521
52178
|
* Clipboard Plugin
|
|
51522
52179
|
*
|
|
@@ -52066,9 +52723,6 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52066
52723
|
case "START":
|
|
52067
52724
|
for (const sheetId of this.getters.getSheetIds()) {
|
|
52068
52725
|
this.filterValues[sheetId] = {};
|
|
52069
|
-
for (const filter of this.getters.getFilters(sheetId)) {
|
|
52070
|
-
this.filterValues[sheetId][filter.id] = [];
|
|
52071
|
-
}
|
|
52072
52726
|
}
|
|
52073
52727
|
break;
|
|
52074
52728
|
case "CREATE_SHEET":
|
|
@@ -52089,16 +52743,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52089
52743
|
this.updateHiddenRows();
|
|
52090
52744
|
break;
|
|
52091
52745
|
case "DUPLICATE_SHEET":
|
|
52092
|
-
|
|
52093
|
-
for (const newFilter of this.getters.getFilters(cmd.sheetIdTo)) {
|
|
52094
|
-
const zone = newFilter.rangeWithHeaders.zone;
|
|
52095
|
-
filterValues[newFilter.id] = this.getFilterHiddenValues({
|
|
52096
|
-
sheetId: cmd.sheetId,
|
|
52097
|
-
col: zone.left,
|
|
52098
|
-
row: zone.top,
|
|
52099
|
-
});
|
|
52100
|
-
}
|
|
52101
|
-
this.filterValues[cmd.sheetIdTo] = filterValues;
|
|
52746
|
+
this.filterValues[cmd.sheetIdTo] = deepCopy(this.filterValues[cmd.sheetId]);
|
|
52102
52747
|
break;
|
|
52103
52748
|
// If we don't handle DELETE_SHEET, on one hand we will have some residual data, on the other hand we keep the data
|
|
52104
52749
|
// on DELETE_SHEET followed by undo
|
|
@@ -52238,38 +52883,6 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52238
52883
|
}
|
|
52239
52884
|
}
|
|
52240
52885
|
|
|
52241
|
-
const selectionStatisticFunctions = [
|
|
52242
|
-
{
|
|
52243
|
-
name: _t("Sum"),
|
|
52244
|
-
types: [CellValueType.number],
|
|
52245
|
-
compute: (values, locale) => sum([[values]], locale),
|
|
52246
|
-
},
|
|
52247
|
-
{
|
|
52248
|
-
name: _t("Avg"),
|
|
52249
|
-
types: [CellValueType.number],
|
|
52250
|
-
compute: (values, locale) => average([[values]], locale),
|
|
52251
|
-
},
|
|
52252
|
-
{
|
|
52253
|
-
name: _t("Min"),
|
|
52254
|
-
types: [CellValueType.number],
|
|
52255
|
-
compute: (values, locale) => min([[values]], locale),
|
|
52256
|
-
},
|
|
52257
|
-
{
|
|
52258
|
-
name: _t("Max"),
|
|
52259
|
-
types: [CellValueType.number],
|
|
52260
|
-
compute: (values, locale) => max([[values]], locale),
|
|
52261
|
-
},
|
|
52262
|
-
{
|
|
52263
|
-
name: _t("Count"),
|
|
52264
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52265
|
-
compute: (values) => countAny([[values]]),
|
|
52266
|
-
},
|
|
52267
|
-
{
|
|
52268
|
-
name: _t("Count Numbers"),
|
|
52269
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52270
|
-
compute: (values, locale) => countNumbers([[values]], locale),
|
|
52271
|
-
},
|
|
52272
|
-
];
|
|
52273
52886
|
/**
|
|
52274
52887
|
* SelectionPlugin
|
|
52275
52888
|
*/
|
|
@@ -52285,8 +52898,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52285
52898
|
"getSelectedZones",
|
|
52286
52899
|
"getSelectedZone",
|
|
52287
52900
|
"getSelectedCells",
|
|
52288
|
-
"getStatisticFnResults",
|
|
52289
|
-
"getAggregate",
|
|
52290
52901
|
"getSelectedFigureId",
|
|
52291
52902
|
"getSelection",
|
|
52292
52903
|
"getActivePosition",
|
|
@@ -52321,7 +52932,10 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52321
52932
|
switch (cmd.type) {
|
|
52322
52933
|
case "ACTIVATE_SHEET":
|
|
52323
52934
|
try {
|
|
52324
|
-
this.getters.getSheet(cmd.sheetIdTo);
|
|
52935
|
+
const sheet = this.getters.getSheet(cmd.sheetIdTo);
|
|
52936
|
+
if (!sheet.isVisible) {
|
|
52937
|
+
return "SheetIsHidden" /* CommandResult.SheetIsHidden */;
|
|
52938
|
+
}
|
|
52325
52939
|
break;
|
|
52326
52940
|
}
|
|
52327
52941
|
catch (error) {
|
|
@@ -52475,6 +53089,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52475
53089
|
this.gridSelection.zones = this.gridSelection.zones.map((z) => this.getters.expandZone(sheetId, z));
|
|
52476
53090
|
this.gridSelection.anchor.zone = this.getters.expandZone(sheetId, this.gridSelection.anchor.zone);
|
|
52477
53091
|
this.setSelectionMixin(this.gridSelection.anchor, this.gridSelection.zones);
|
|
53092
|
+
this.selectedFigureId = null;
|
|
52478
53093
|
break;
|
|
52479
53094
|
}
|
|
52480
53095
|
/** Any change to the selection has to be reflected in the selection processor. */
|
|
@@ -52569,52 +53184,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52569
53184
|
: this.getters.getNextVisibleCellPosition({ sheetId, col: 0, row: 0 });
|
|
52570
53185
|
}
|
|
52571
53186
|
}
|
|
52572
|
-
getStatisticFnResults() {
|
|
52573
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52574
|
-
const cells = new Set();
|
|
52575
|
-
for (const zone of this.gridSelection.zones) {
|
|
52576
|
-
for (const { col, row } of positions(zone)) {
|
|
52577
|
-
if (this.getters.isRowHidden(sheetId, row) || this.getters.isColHidden(sheetId, col)) {
|
|
52578
|
-
continue; // Skip hidden cells
|
|
52579
|
-
}
|
|
52580
|
-
const evaluatedCell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52581
|
-
if (evaluatedCell.type !== CellValueType.empty) {
|
|
52582
|
-
cells.add(evaluatedCell);
|
|
52583
|
-
}
|
|
52584
|
-
}
|
|
52585
|
-
}
|
|
52586
|
-
const locale = this.getters.getLocale();
|
|
52587
|
-
let statisticFnResults = {};
|
|
52588
|
-
for (let fn of selectionStatisticFunctions) {
|
|
52589
|
-
// We don't want to display statistical information when there is no interest:
|
|
52590
|
-
// We set the statistical result to undefined if the data handled by the selection
|
|
52591
|
-
// does not match the data handled by the function.
|
|
52592
|
-
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
52593
|
-
// be displayed as undefined rather than 0.
|
|
52594
|
-
let fnResult = undefined;
|
|
52595
|
-
const evaluatedCells = [...cells].filter((c) => fn.types.includes(c.type));
|
|
52596
|
-
if (evaluatedCells.length) {
|
|
52597
|
-
fnResult = fn.compute(evaluatedCells, locale);
|
|
52598
|
-
}
|
|
52599
|
-
statisticFnResults[fn.name] = fnResult;
|
|
52600
|
-
}
|
|
52601
|
-
return statisticFnResults;
|
|
52602
|
-
}
|
|
52603
|
-
getAggregate() {
|
|
52604
|
-
let aggregate = 0;
|
|
52605
|
-
let n = 0;
|
|
52606
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52607
|
-
const cellPositions = this.gridSelection.zones.map(positions).flat();
|
|
52608
|
-
for (const { col, row } of cellPositions) {
|
|
52609
|
-
const cell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52610
|
-
if (cell.type === CellValueType.number) {
|
|
52611
|
-
n++;
|
|
52612
|
-
aggregate += cell.value;
|
|
52613
|
-
}
|
|
52614
|
-
}
|
|
52615
|
-
const locale = this.getters.getLocale();
|
|
52616
|
-
return n < 2 ? null : formatValue(aggregate, { locale });
|
|
52617
|
-
}
|
|
52618
53187
|
isSelected(zone) {
|
|
52619
53188
|
return !!this.getters.getSelectedZones().find((z) => isEqual(z, zone));
|
|
52620
53189
|
}
|
|
@@ -52656,9 +53225,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52656
53225
|
// Other
|
|
52657
53226
|
// ---------------------------------------------------------------------------
|
|
52658
53227
|
activateSheet(sheetIdFrom, sheetIdTo) {
|
|
52659
|
-
if (!this.getters.isSheetVisible(sheetIdTo)) {
|
|
52660
|
-
this.dispatch("SHOW_SHEET", { sheetId: sheetIdTo });
|
|
52661
|
-
}
|
|
52662
53228
|
this.setActiveSheet(sheetIdTo);
|
|
52663
53229
|
this.sheetsData[sheetIdFrom] = {
|
|
52664
53230
|
gridSelection: deepCopy(this.gridSelection),
|
|
@@ -53665,7 +54231,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53665
54231
|
* column of the current viewport
|
|
53666
54232
|
*/
|
|
53667
54233
|
getColDimensionsInViewport(sheetId, col) {
|
|
53668
|
-
const left =
|
|
54234
|
+
const left = largeMin(this.getters.getSheetViewVisibleCols());
|
|
53669
54235
|
const start = this.getters.getColRowOffsetInViewport("COL", left, col);
|
|
53670
54236
|
const size = this.getters.getColSize(sheetId, col);
|
|
53671
54237
|
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
@@ -53680,7 +54246,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53680
54246
|
* of the current viewport
|
|
53681
54247
|
*/
|
|
53682
54248
|
getRowDimensionsInViewport(sheetId, row) {
|
|
53683
|
-
const top =
|
|
54249
|
+
const top = largeMin(this.getters.getSheetViewVisibleRows());
|
|
53684
54250
|
const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
|
|
53685
54251
|
const size = this.getters.getRowSize(sheetId, row);
|
|
53686
54252
|
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
@@ -54032,6 +54598,7 @@ const statefulUIPluginRegistry = new Registry()
|
|
|
54032
54598
|
.add("evaluation_filter", FilterEvaluationPlugin)
|
|
54033
54599
|
.add("header_visibility_ui", HeaderVisibilityUIPlugin)
|
|
54034
54600
|
.add("table_style", TableStylePlugin)
|
|
54601
|
+
.add("cell_computed_style", CellComputedStylePlugin)
|
|
54035
54602
|
.add("header_positions", HeaderPositionsUIPlugin)
|
|
54036
54603
|
.add("viewport", SheetViewPlugin)
|
|
54037
54604
|
.add("clipboard", ClipboardPlugin);
|
|
@@ -54041,8 +54608,9 @@ const coreViewsPluginRegistry = new Registry()
|
|
|
54041
54608
|
.add("evaluation_chart", EvaluationChartPlugin)
|
|
54042
54609
|
.add("evaluation_cf", EvaluationConditionalFormatPlugin)
|
|
54043
54610
|
.add("row_size", HeaderSizeUIPlugin)
|
|
54044
|
-
.add("
|
|
54045
|
-
.add("
|
|
54611
|
+
.add("data_validation_ui", EvaluationDataValidationPlugin)
|
|
54612
|
+
.add("dynamic_tables", DynamicTablesPlugin)
|
|
54613
|
+
.add("custom_colors", CustomColorsPlugin);
|
|
54046
54614
|
|
|
54047
54615
|
const clickableCellRegistry = new Registry();
|
|
54048
54616
|
clickableCellRegistry.add("link", {
|
|
@@ -54091,6 +54659,38 @@ class ImageProvider {
|
|
|
54091
54659
|
}
|
|
54092
54660
|
}
|
|
54093
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
|
+
|
|
54094
54694
|
const RIPPLE_KEY_FRAMES = [
|
|
54095
54695
|
{ transform: "scale(0)" },
|
|
54096
54696
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -54393,12 +54993,14 @@ class BottomBarSheet extends owl.Component {
|
|
|
54393
54993
|
this.editionState = "initializing";
|
|
54394
54994
|
}
|
|
54395
54995
|
stopEdition() {
|
|
54396
|
-
|
|
54996
|
+
const input = this.sheetNameRef.el;
|
|
54997
|
+
if (!this.state.isEditing || !input)
|
|
54397
54998
|
return;
|
|
54398
54999
|
this.state.isEditing = false;
|
|
54399
55000
|
this.editionState = "initializing";
|
|
54400
|
-
|
|
55001
|
+
input.blur();
|
|
54401
55002
|
const inputValue = this.getInputContent() || "";
|
|
55003
|
+
input.innerText = inputValue;
|
|
54402
55004
|
interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
|
|
54403
55005
|
}
|
|
54404
55006
|
cancelEdition() {
|
|
@@ -54442,6 +55044,115 @@ class BottomBarSheet extends owl.Component {
|
|
|
54442
55044
|
}
|
|
54443
55045
|
}
|
|
54444
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
|
+
|
|
54445
55156
|
// -----------------------------------------------------------------------------
|
|
54446
55157
|
// SpreadSheet
|
|
54447
55158
|
// -----------------------------------------------------------------------------
|
|
@@ -54457,40 +55168,38 @@ css /* scss */ `
|
|
|
54457
55168
|
}
|
|
54458
55169
|
`;
|
|
54459
55170
|
class BottomBarStatistic extends owl.Component {
|
|
54460
|
-
static template = "o-spreadsheet-
|
|
55171
|
+
static template = "o-spreadsheet-BottomBarStatistic";
|
|
54461
55172
|
static props = {
|
|
54462
55173
|
openContextMenu: Function,
|
|
54463
55174
|
closeContextMenu: Function,
|
|
54464
55175
|
};
|
|
54465
55176
|
static components = { Ripple };
|
|
54466
55177
|
selectedStatisticFn = "";
|
|
54467
|
-
|
|
55178
|
+
store;
|
|
54468
55179
|
setup() {
|
|
54469
|
-
this.
|
|
55180
|
+
this.store = useStore(AggregateStatisticsStore);
|
|
54470
55181
|
owl.onWillUpdateProps(() => {
|
|
54471
|
-
|
|
54472
|
-
if (!deepEquals(newStatisticFnResults, this.statisticFnResults)) {
|
|
55182
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54473
55183
|
this.props.closeContextMenu();
|
|
54474
55184
|
}
|
|
54475
|
-
this.statisticFnResults = newStatisticFnResults;
|
|
54476
55185
|
});
|
|
54477
55186
|
}
|
|
54478
55187
|
getSelectedStatistic() {
|
|
54479
55188
|
// don't display button if no function has a result
|
|
54480
|
-
if (Object.values(this.statisticFnResults).every((result) => result === undefined)) {
|
|
55189
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54481
55190
|
return undefined;
|
|
54482
55191
|
}
|
|
54483
55192
|
if (this.selectedStatisticFn === "") {
|
|
54484
|
-
this.selectedStatisticFn = Object.keys(this.statisticFnResults)[0];
|
|
55193
|
+
this.selectedStatisticFn = Object.keys(this.store.statisticFnResults)[0];
|
|
54485
55194
|
}
|
|
54486
|
-
return this.getComposedFnName(this.selectedStatisticFn
|
|
55195
|
+
return this.getComposedFnName(this.selectedStatisticFn);
|
|
54487
55196
|
}
|
|
54488
55197
|
listSelectionStatistics(ev) {
|
|
54489
55198
|
const registry = new MenuItemRegistry();
|
|
54490
55199
|
let i = 0;
|
|
54491
|
-
for (let [fnName
|
|
55200
|
+
for (let [fnName] of Object.entries(this.store.statisticFnResults)) {
|
|
54492
55201
|
registry.add(fnName, {
|
|
54493
|
-
name: this.getComposedFnName(fnName
|
|
55202
|
+
name: () => this.getComposedFnName(fnName),
|
|
54494
55203
|
sequence: i,
|
|
54495
55204
|
isReadonlyAllowed: true,
|
|
54496
55205
|
execute: () => {
|
|
@@ -54503,8 +55212,9 @@ class BottomBarStatistic extends owl.Component {
|
|
|
54503
55212
|
const { top, left, width } = target.getBoundingClientRect();
|
|
54504
55213
|
this.props.openContextMenu(left + width, top, registry);
|
|
54505
55214
|
}
|
|
54506
|
-
getComposedFnName(fnName
|
|
55215
|
+
getComposedFnName(fnName) {
|
|
54507
55216
|
const locale = this.env.model.getters.getLocale();
|
|
55217
|
+
const fnValue = this.store.statisticFnResults[fnName];
|
|
54508
55218
|
return fnName + ": " + (fnValue !== undefined ? formatValue(fnValue, { locale }) : "__");
|
|
54509
55219
|
}
|
|
54510
55220
|
}
|
|
@@ -54613,10 +55323,14 @@ class BottomBar extends owl.Component {
|
|
|
54613
55323
|
name: sheet.name,
|
|
54614
55324
|
sequence: i,
|
|
54615
55325
|
isReadonlyAllowed: true,
|
|
54616
|
-
textColor: sheet.isVisible ? undefined : "
|
|
55326
|
+
textColor: sheet.isVisible ? undefined : "#808080",
|
|
54617
55327
|
execute: (env) => {
|
|
55328
|
+
if (!this.env.model.getters.isSheetVisible(sheetId)) {
|
|
55329
|
+
this.env.model.dispatch("SHOW_SHEET", { sheetId });
|
|
55330
|
+
}
|
|
54618
55331
|
env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: from, sheetIdTo: sheetId });
|
|
54619
55332
|
},
|
|
55333
|
+
isEnabled: (env) => (env.model.getters.isReadonly() ? sheet.isVisible : true),
|
|
54620
55334
|
});
|
|
54621
55335
|
i++;
|
|
54622
55336
|
}
|
|
@@ -54689,7 +55403,7 @@ class BottomBar extends owl.Component {
|
|
|
54689
55403
|
this.sheetListRef.el.scrollTo({ top: 0, left: scroll, behavior: "smooth" });
|
|
54690
55404
|
}
|
|
54691
55405
|
onSheetMouseDown(sheetId, event) {
|
|
54692
|
-
if (event.button !== 0)
|
|
55406
|
+
if (event.button !== 0 || this.env.model.getters.isReadonly())
|
|
54693
55407
|
return;
|
|
54694
55408
|
this.closeMenu();
|
|
54695
55409
|
const visibleSheets = this.getVisibleSheets();
|
|
@@ -54725,7 +55439,7 @@ class BottomBar extends owl.Component {
|
|
|
54725
55439
|
.map((sheetEl) => sheetEl.getBoundingClientRect())
|
|
54726
55440
|
.map((rect) => ({
|
|
54727
55441
|
x: rect.x,
|
|
54728
|
-
width: rect.width - 1,
|
|
55442
|
+
width: rect.width - 1, // -1 to compensate negative margin
|
|
54729
55443
|
y: rect.y,
|
|
54730
55444
|
height: rect.height,
|
|
54731
55445
|
}));
|
|
@@ -54952,7 +55666,7 @@ class RowGroup extends AbstractHeaderGroup {
|
|
|
54952
55666
|
}
|
|
54953
55667
|
return cssPropertiesToCss({
|
|
54954
55668
|
top: `${groupBox.headerRect.height / 2}px`,
|
|
54955
|
-
left: `calc(50% - 1px)`,
|
|
55669
|
+
left: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
54956
55670
|
width: `30%`,
|
|
54957
55671
|
height: `calc(100% - ${groupBox.headerRect.height / 2}px)`,
|
|
54958
55672
|
"border-left": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
|
|
@@ -55004,7 +55718,7 @@ class ColGroup extends AbstractHeaderGroup {
|
|
|
55004
55718
|
return "";
|
|
55005
55719
|
}
|
|
55006
55720
|
return cssPropertiesToCss({
|
|
55007
|
-
top: `calc(50% - 1px)`,
|
|
55721
|
+
top: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55008
55722
|
left: `${groupBox.headerRect.width / 2}px`,
|
|
55009
55723
|
width: `calc(100% - ${groupBox.headerRect.width / 2}px)`,
|
|
55010
55724
|
height: `30%`,
|
|
@@ -56093,12 +56807,9 @@ css /* scss */ `
|
|
|
56093
56807
|
.text-muted {
|
|
56094
56808
|
color: grey !important;
|
|
56095
56809
|
}
|
|
56096
|
-
button {
|
|
56097
|
-
color: #333;
|
|
56098
|
-
}
|
|
56099
56810
|
.o-disabled {
|
|
56100
56811
|
opacity: 0.4;
|
|
56101
|
-
|
|
56812
|
+
cursor: default;
|
|
56102
56813
|
pointer-events: none;
|
|
56103
56814
|
}
|
|
56104
56815
|
|
|
@@ -56216,17 +56927,17 @@ css /* scss */ `
|
|
|
56216
56927
|
}
|
|
56217
56928
|
|
|
56218
56929
|
.o-button {
|
|
56219
|
-
border: 1px solid
|
|
56930
|
+
border: 1px solid;
|
|
56220
56931
|
padding: 0px 20px 0px 20px;
|
|
56221
56932
|
border-radius: 4px;
|
|
56222
56933
|
font-weight: 500;
|
|
56223
56934
|
font-size: 14px;
|
|
56224
56935
|
height: 30px;
|
|
56225
56936
|
line-height: 16px;
|
|
56226
|
-
background: white;
|
|
56227
56937
|
margin-right: 8px;
|
|
56228
|
-
|
|
56229
|
-
|
|
56938
|
+
|
|
56939
|
+
&:not(:hover) {
|
|
56940
|
+
background-color: transparent;
|
|
56230
56941
|
}
|
|
56231
56942
|
|
|
56232
56943
|
&:enabled {
|
|
@@ -56240,6 +56951,15 @@ css /* scss */ `
|
|
|
56240
56951
|
&:last-child {
|
|
56241
56952
|
margin-right: 0px;
|
|
56242
56953
|
}
|
|
56954
|
+
|
|
56955
|
+
&.o-button-grey {
|
|
56956
|
+
border-color: lightgrey;
|
|
56957
|
+
background: #ffffff;
|
|
56958
|
+
color: #333;
|
|
56959
|
+
&:hover:enabled {
|
|
56960
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
56961
|
+
}
|
|
56962
|
+
}
|
|
56243
56963
|
}
|
|
56244
56964
|
|
|
56245
56965
|
.o-input {
|
|
@@ -56255,7 +56975,7 @@ css /* scss */ `
|
|
|
56255
56975
|
|
|
56256
56976
|
.o-number-input {
|
|
56257
56977
|
/* Remove number input arrows */
|
|
56258
|
-
|
|
56978
|
+
appearance: textfield;
|
|
56259
56979
|
&::-webkit-outer-spin-button,
|
|
56260
56980
|
&::-webkit-inner-spin-button {
|
|
56261
56981
|
-webkit-appearance: none;
|
|
@@ -56298,6 +57018,7 @@ class Spreadsheet extends owl.Component {
|
|
|
56298
57018
|
this.notificationStore = useStore(NotificationStore);
|
|
56299
57019
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
56300
57020
|
this.sidePanel = useStore(SidePanelStore);
|
|
57021
|
+
useStore(ArrayFormulaHighlight);
|
|
56301
57022
|
this.keyDownMapping = {
|
|
56302
57023
|
"CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
56303
57024
|
"CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
@@ -56400,7 +57121,7 @@ class Spreadsheet extends owl.Component {
|
|
|
56400
57121
|
const gridColSize = GROUP_LAYER_WIDTH * this.rowLayers.length;
|
|
56401
57122
|
const gridRowSize = GROUP_LAYER_WIDTH * this.colLayers.length;
|
|
56402
57123
|
return cssPropertiesToCss({
|
|
56403
|
-
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`,
|
|
57124
|
+
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`, // +2: margins
|
|
56404
57125
|
"grid-template-rows": `${gridRowSize ? gridRowSize + 2 : 0}px auto`,
|
|
56405
57126
|
});
|
|
56406
57127
|
}
|
|
@@ -57132,14 +57853,6 @@ class SelectiveHistory {
|
|
|
57132
57853
|
this.revertBefore(operationId);
|
|
57133
57854
|
this.tree.drop(operationId);
|
|
57134
57855
|
}
|
|
57135
|
-
getRevertedExecution() {
|
|
57136
|
-
const data = [];
|
|
57137
|
-
const operations = this.tree.revertedExecution(this.HEAD_BRANCH);
|
|
57138
|
-
for (const { operation } of operations) {
|
|
57139
|
-
data.push(operation.data);
|
|
57140
|
-
}
|
|
57141
|
-
return data;
|
|
57142
|
-
}
|
|
57143
57856
|
/**
|
|
57144
57857
|
* Revert the state as it was *before* the given operation was executed.
|
|
57145
57858
|
*/
|
|
@@ -57928,6 +58641,9 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
57928
58641
|
case "bar":
|
|
57929
58642
|
plot = addBarChart(chart.data);
|
|
57930
58643
|
break;
|
|
58644
|
+
case "combo":
|
|
58645
|
+
plot = addComboChart(chart.data);
|
|
58646
|
+
break;
|
|
57931
58647
|
case "line":
|
|
57932
58648
|
plot = addLineChart(chart.data);
|
|
57933
58649
|
break;
|
|
@@ -58090,6 +58806,79 @@ function addBarChart(chart) {
|
|
|
58090
58806
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58091
58807
|
`;
|
|
58092
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
|
+
}
|
|
58093
58882
|
function addLineChart(chart) {
|
|
58094
58883
|
const colors = new ChartColors();
|
|
58095
58884
|
const dataSetsNodes = [];
|
|
@@ -58137,7 +58926,7 @@ function addLineChart(chart) {
|
|
|
58137
58926
|
}
|
|
58138
58927
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
58139
58928
|
const colors = new ChartColors();
|
|
58140
|
-
const maxLength =
|
|
58929
|
+
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
58141
58930
|
const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
|
|
58142
58931
|
const dataSetsNodes = [];
|
|
58143
58932
|
for (const [dsIndex, dataset] of Object.entries(chart.dataSets).reverse()) {
|
|
@@ -59015,7 +59804,7 @@ function addTableColumns(table, sheetData) {
|
|
|
59015
59804
|
const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
|
|
59016
59805
|
const colName = sheetData.cells[colHeaderXc]?.content || `col${i}`;
|
|
59017
59806
|
const colAttributes = [
|
|
59018
|
-
["id", i + 1],
|
|
59807
|
+
["id", i + 1], // id cannot be 0
|
|
59019
59808
|
["name", colName],
|
|
59020
59809
|
];
|
|
59021
59810
|
columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
|
|
@@ -59231,6 +60020,7 @@ function addSheetViews(sheet) {
|
|
|
59231
60020
|
* https://www.ecma-international.org/publications-and-standards/standards/ecma-376/
|
|
59232
60021
|
*/
|
|
59233
60022
|
function getXLSX(data) {
|
|
60023
|
+
data = fixLengthySheetNames(data);
|
|
59234
60024
|
const files = [];
|
|
59235
60025
|
const construct = getDefaultXLSXStructure();
|
|
59236
60026
|
files.push(createWorkbook(data, construct));
|
|
@@ -59478,6 +60268,40 @@ function createRelRoot() {
|
|
|
59478
60268
|
`;
|
|
59479
60269
|
return createXMLFile(parseXML(xml), "_rels/.rels");
|
|
59480
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
|
+
}
|
|
59481
60305
|
|
|
59482
60306
|
var Status;
|
|
59483
60307
|
(function (Status) {
|
|
@@ -60167,6 +60991,6 @@ exports.tokenColors = tokenColors;
|
|
|
60167
60991
|
exports.tokenize = tokenize;
|
|
60168
60992
|
|
|
60169
60993
|
|
|
60170
|
-
__info__.version = "17.3.0-alpha.
|
|
60171
|
-
__info__.date = "2024-
|
|
60172
|
-
__info__.hash = "
|
|
60994
|
+
__info__.version = "17.3.0-alpha.2";
|
|
60995
|
+
__info__.date = "2024-04-05T14:01:07.060Z";
|
|
60996
|
+
__info__.hash = "8c5a229";
|