@odoo/o-spreadsheet 17.2.0-alpha.6 → 17.2.0
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 +847 -468
- package/dist/o-spreadsheet.d.ts +157 -119
- package/dist/o-spreadsheet.esm.js +847 -468
- package/dist/o-spreadsheet.iife.js +847 -468
- package/dist/o-spreadsheet.iife.min.js +196 -194
- package/dist/o_spreadsheet.xml +22 -13
- package/package.json +2 -2
|
@@ -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.2.0
|
|
7
|
-
* @date 2024-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.2.0
|
|
7
|
+
* @date 2024-03-20T08:12:44.398Z
|
|
8
|
+
* @hash 1869c24
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
@@ -191,8 +191,8 @@ const DEFAULT_GAUGE_LOWER_COLOR = "#cc0000";
|
|
|
191
191
|
const DEFAULT_GAUGE_MIDDLE_COLOR = "#f1c232";
|
|
192
192
|
const DEFAULT_GAUGE_UPPER_COLOR = "#6aa84f";
|
|
193
193
|
const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
|
|
194
|
-
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#
|
|
195
|
-
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#
|
|
194
|
+
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#6aa84f";
|
|
195
|
+
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#E06666";
|
|
196
196
|
const LINE_FILL_TRANSPARENCY = 0.4;
|
|
197
197
|
// session
|
|
198
198
|
const DEBOUNCE_TIME = 200;
|
|
@@ -642,21 +642,6 @@ function isConsecutive(iterable) {
|
|
|
642
642
|
}
|
|
643
643
|
return true;
|
|
644
644
|
}
|
|
645
|
-
class JetSet extends Set {
|
|
646
|
-
addMany(iterable) {
|
|
647
|
-
for (const element of iterable) {
|
|
648
|
-
super.add(element);
|
|
649
|
-
}
|
|
650
|
-
return this;
|
|
651
|
-
}
|
|
652
|
-
deleteMany(iterable) {
|
|
653
|
-
let wasDeleted = false;
|
|
654
|
-
for (const element of iterable) {
|
|
655
|
-
wasDeleted ||= super.delete(element);
|
|
656
|
-
}
|
|
657
|
-
return wasDeleted;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
645
|
/**
|
|
661
646
|
* Creates a version of the function that's memoized on the value of its first
|
|
662
647
|
* argument, if any.
|
|
@@ -2146,7 +2131,6 @@ const LAYERS = {
|
|
|
2146
2131
|
Background: 0,
|
|
2147
2132
|
Highlights: 1,
|
|
2148
2133
|
Clipboard: 2,
|
|
2149
|
-
Search: 3,
|
|
2150
2134
|
Chart: 4,
|
|
2151
2135
|
Autofill: 5,
|
|
2152
2136
|
Selection: 6,
|
|
@@ -4103,14 +4087,6 @@ function positions(zone) {
|
|
|
4103
4087
|
}
|
|
4104
4088
|
return positions;
|
|
4105
4089
|
}
|
|
4106
|
-
function forEachPositionsInZone(zone, callback) {
|
|
4107
|
-
const { left, right, top, bottom } = zone;
|
|
4108
|
-
for (let col = left; col <= right; col++) {
|
|
4109
|
-
for (let row = top; row <= bottom; row++) {
|
|
4110
|
-
callback(col, row);
|
|
4111
|
-
}
|
|
4112
|
-
}
|
|
4113
|
-
}
|
|
4114
4090
|
/**
|
|
4115
4091
|
* This function returns a zone with coordinates modified according to the change
|
|
4116
4092
|
* applied to the zone. It may be possible to change the zone by resizing or moving
|
|
@@ -6318,6 +6294,11 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6318
6294
|
this.pasteTableCell(sheetId, tableCell, position, clipboardOptions);
|
|
6319
6295
|
}
|
|
6320
6296
|
}
|
|
6297
|
+
if (tableCells.length === 1) {
|
|
6298
|
+
for (let c = 0; c < tableCells[0].length; c++) {
|
|
6299
|
+
this.dispatch("AUTOFILL_TABLE_COLUMN", { col: col + c, row, sheetId });
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6321
6302
|
}
|
|
6322
6303
|
pasteTableCell(sheetId, tableCell, position, options) {
|
|
6323
6304
|
if (tableCell.table && !options?.pasteOption) {
|
|
@@ -7444,6 +7425,7 @@ const TableTerms = {
|
|
|
7444
7425
|
firstColumn: _t("First column"),
|
|
7445
7426
|
lastColumn: _t("Last column"),
|
|
7446
7427
|
bandedColumns: _t("Banded columns"),
|
|
7428
|
+
automaticAutofill: _t("Automatically autofill formulas"),
|
|
7447
7429
|
totalRow: _t("Total row"),
|
|
7448
7430
|
},
|
|
7449
7431
|
Tooltips: {
|
|
@@ -8190,11 +8172,18 @@ function drawHighlight(renderingContext, highlight, rect) {
|
|
|
8190
8172
|
}
|
|
8191
8173
|
const color = highlight.color || HIGHLIGHT_COLOR;
|
|
8192
8174
|
const { ctx } = renderingContext;
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8175
|
+
if (!highlight.noBorder) {
|
|
8176
|
+
ctx.strokeStyle = color;
|
|
8177
|
+
if (highlight.thinLine) {
|
|
8178
|
+
ctx.lineWidth = 1;
|
|
8179
|
+
ctx.strokeRect(x, y, width, height);
|
|
8180
|
+
}
|
|
8181
|
+
else {
|
|
8182
|
+
ctx.lineWidth = 2;
|
|
8183
|
+
/** + 0.5 offset to have sharp lines. See comment in {@link RendererPlugin#drawBorder} for more details */
|
|
8184
|
+
ctx.strokeRect(x + 0.5, y + 0.5, width, height);
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8198
8187
|
if (!highlight.noFill) {
|
|
8199
8188
|
ctx.fillStyle = setColorAlpha(color, highlight.fillAlpha ?? 0.12);
|
|
8200
8189
|
ctx.fillRect(x, y, width, height);
|
|
@@ -8568,6 +8557,7 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
8568
8557
|
else if (cell.link) {
|
|
8569
8558
|
content = markdownLink(content, cell.link.url);
|
|
8570
8559
|
}
|
|
8560
|
+
this.addHeadersForSpreadingFormula(content);
|
|
8571
8561
|
this.model.dispatch("UPDATE_CELL", {
|
|
8572
8562
|
sheetId: this.sheetId,
|
|
8573
8563
|
col,
|
|
@@ -8583,6 +8573,7 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
8583
8573
|
row,
|
|
8584
8574
|
});
|
|
8585
8575
|
}
|
|
8576
|
+
this.model.dispatch("AUTOFILL_TABLE_COLUMN", { col, row, sheetId: this.sheetId });
|
|
8586
8577
|
this.setContent("");
|
|
8587
8578
|
}
|
|
8588
8579
|
}
|
|
@@ -8785,6 +8776,38 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
8785
8776
|
}
|
|
8786
8777
|
this.colorIndexByRange = colorsToKeep;
|
|
8787
8778
|
}
|
|
8779
|
+
/** Add headers at the end of the sheet so the formula in the composer has enough space to spread */
|
|
8780
|
+
addHeadersForSpreadingFormula(content) {
|
|
8781
|
+
if (!content.startsWith("=")) {
|
|
8782
|
+
return;
|
|
8783
|
+
}
|
|
8784
|
+
const evaluated = this.getters.evaluateFormula(this.sheetId, content);
|
|
8785
|
+
if (!isMatrix(evaluated)) {
|
|
8786
|
+
return;
|
|
8787
|
+
}
|
|
8788
|
+
const numberOfRows = this.getters.getNumberRows(this.sheetId);
|
|
8789
|
+
const numberOfCols = this.getters.getNumberCols(this.sheetId);
|
|
8790
|
+
const missingRows = this.row + evaluated[0].length - numberOfRows;
|
|
8791
|
+
const missingCols = this.col + evaluated.length - numberOfCols;
|
|
8792
|
+
if (missingCols > 0) {
|
|
8793
|
+
this.model.dispatch("ADD_COLUMNS_ROWS", {
|
|
8794
|
+
sheetId: this.sheetId,
|
|
8795
|
+
dimension: "COL",
|
|
8796
|
+
base: numberOfCols - 1,
|
|
8797
|
+
position: "after",
|
|
8798
|
+
quantity: missingCols + 20,
|
|
8799
|
+
});
|
|
8800
|
+
}
|
|
8801
|
+
if (missingRows > 0) {
|
|
8802
|
+
this.model.dispatch("ADD_COLUMNS_ROWS", {
|
|
8803
|
+
sheetId: this.sheetId,
|
|
8804
|
+
dimension: "ROW",
|
|
8805
|
+
base: numberOfRows - 1,
|
|
8806
|
+
position: "after",
|
|
8807
|
+
quantity: missingRows + 50,
|
|
8808
|
+
});
|
|
8809
|
+
}
|
|
8810
|
+
}
|
|
8788
8811
|
/**
|
|
8789
8812
|
* Highlight all ranges that can be found in the composer content.
|
|
8790
8813
|
*/
|
|
@@ -10009,6 +10032,9 @@ function makeArg(str, description) {
|
|
|
10009
10032
|
result.default = true;
|
|
10010
10033
|
result.defaultValue = defaultValue;
|
|
10011
10034
|
}
|
|
10035
|
+
if (types.some((t) => t.startsWith("RANGE"))) {
|
|
10036
|
+
result.acceptMatrix = true;
|
|
10037
|
+
}
|
|
10012
10038
|
return result;
|
|
10013
10039
|
}
|
|
10014
10040
|
/**
|
|
@@ -17418,10 +17444,11 @@ const DEFAULT_IS_SORTED = true;
|
|
|
17418
17444
|
const DEFAULT_MATCH_MODE = 0;
|
|
17419
17445
|
const DEFAULT_SEARCH_MODE = 1;
|
|
17420
17446
|
const DEFAULT_ABSOLUTE_RELATIVE_MODE = 1;
|
|
17421
|
-
function
|
|
17422
|
-
|
|
17423
|
-
|
|
17424
|
-
|
|
17447
|
+
function valueNotAvailable(searchKey) {
|
|
17448
|
+
return {
|
|
17449
|
+
value: CellErrorType.NotAvailable,
|
|
17450
|
+
message: _t("Did not find value '%s' in [[FUNCTION_NAME]] evaluation.", toString(searchKey)),
|
|
17451
|
+
};
|
|
17425
17452
|
}
|
|
17426
17453
|
// -----------------------------------------------------------------------------
|
|
17427
17454
|
// ADDRESS
|
|
@@ -17518,7 +17545,9 @@ const HLOOKUP = {
|
|
|
17518
17545
|
? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
|
|
17519
17546
|
: linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
|
|
17520
17547
|
const col = range[colIndex];
|
|
17521
|
-
|
|
17548
|
+
if (col === undefined) {
|
|
17549
|
+
return valueNotAvailable(searchKey);
|
|
17550
|
+
}
|
|
17522
17551
|
return col[_index - 1];
|
|
17523
17552
|
},
|
|
17524
17553
|
isExported: true,
|
|
@@ -17637,11 +17666,11 @@ const LOOKUP = {
|
|
|
17637
17666
|
: (range, index) => range[index][0].value;
|
|
17638
17667
|
const rangeLength = verticalSearch ? nbRow : nbCol;
|
|
17639
17668
|
const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
|
|
17640
|
-
if (index === -1
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
17669
|
+
if (index === -1 ||
|
|
17670
|
+
(verticalSearch && searchArray[0][index] === undefined) ||
|
|
17671
|
+
(!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
|
|
17672
|
+
return valueNotAvailable(searchKey);
|
|
17673
|
+
}
|
|
17645
17674
|
if (resultRange === undefined) {
|
|
17646
17675
|
return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
|
|
17647
17676
|
}
|
|
@@ -17691,7 +17720,10 @@ const MATCH = {
|
|
|
17691
17720
|
index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
|
|
17692
17721
|
break;
|
|
17693
17722
|
}
|
|
17694
|
-
|
|
17723
|
+
if ((nbCol === 1 && range[0][index] === undefined) ||
|
|
17724
|
+
(nbCol !== 1 && range[index] === undefined)) {
|
|
17725
|
+
return valueNotAvailable(searchKey);
|
|
17726
|
+
}
|
|
17695
17727
|
return index + 1;
|
|
17696
17728
|
},
|
|
17697
17729
|
isExported: true,
|
|
@@ -17750,7 +17782,9 @@ const VLOOKUP = {
|
|
|
17750
17782
|
? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
|
|
17751
17783
|
: linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
|
|
17752
17784
|
const value = range[_index - 1][rowIndex];
|
|
17753
|
-
|
|
17785
|
+
if (value === undefined) {
|
|
17786
|
+
return valueNotAvailable(searchKey);
|
|
17787
|
+
}
|
|
17754
17788
|
return value;
|
|
17755
17789
|
},
|
|
17756
17790
|
isExported: true,
|
|
@@ -17810,7 +17844,9 @@ const XLOOKUP = {
|
|
|
17810
17844
|
? returnRange.map((col) => [col[index]])
|
|
17811
17845
|
: [returnRange[index]];
|
|
17812
17846
|
}
|
|
17813
|
-
|
|
17847
|
+
if (defaultValue === undefined) {
|
|
17848
|
+
return valueNotAvailable(searchKey);
|
|
17849
|
+
}
|
|
17814
17850
|
return [[defaultValue]];
|
|
17815
17851
|
},
|
|
17816
17852
|
isExported: true,
|
|
@@ -18569,16 +18605,31 @@ class FunctionRegistry extends Registry {
|
|
|
18569
18605
|
}
|
|
18570
18606
|
const descr = addMetaInfoFromArg(addDescr);
|
|
18571
18607
|
validateArguments(descr.args);
|
|
18572
|
-
this.mapping[name] = addErrorHandling(addResultHandling(descr
|
|
18608
|
+
this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
|
|
18573
18609
|
super.add(name, descr);
|
|
18574
18610
|
return this;
|
|
18575
18611
|
}
|
|
18576
18612
|
}
|
|
18613
|
+
function addInputHandling(descr) {
|
|
18614
|
+
function computeWithInputHandling(...args) {
|
|
18615
|
+
for (let i = 0; i < args.length; i++) {
|
|
18616
|
+
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
18617
|
+
const arg = args[i];
|
|
18618
|
+
if (isMatrix(arg) && !argDefinition.acceptMatrix) {
|
|
18619
|
+
if (arg.length !== 1 || arg[0].length !== 1) {
|
|
18620
|
+
throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be a single value or a single cell reference, not a range.", argDefinition.name));
|
|
18621
|
+
}
|
|
18622
|
+
args[i] = arg[0][0];
|
|
18623
|
+
}
|
|
18624
|
+
}
|
|
18625
|
+
return descr.compute.apply(this, args);
|
|
18626
|
+
}
|
|
18627
|
+
return computeWithInputHandling;
|
|
18628
|
+
}
|
|
18577
18629
|
function addErrorHandling(compute, functionName) {
|
|
18578
18630
|
return function (...args) {
|
|
18579
18631
|
try {
|
|
18580
|
-
|
|
18581
|
-
return computeFormula(...args);
|
|
18632
|
+
return compute.apply(this, args);
|
|
18582
18633
|
}
|
|
18583
18634
|
catch (e) {
|
|
18584
18635
|
return handleError(e, functionName);
|
|
@@ -18612,8 +18663,7 @@ function hasStringMessage(obj) {
|
|
|
18612
18663
|
}
|
|
18613
18664
|
function addResultHandling(compute) {
|
|
18614
18665
|
return function (...args) {
|
|
18615
|
-
const
|
|
18616
|
-
const result = computeResult(...args);
|
|
18666
|
+
const result = compute.apply(this, args);
|
|
18617
18667
|
if (!isMatrix(result)) {
|
|
18618
18668
|
if (typeof result === "object" && result !== null && "value" in result) {
|
|
18619
18669
|
return result;
|
|
@@ -18828,8 +18878,10 @@ function getGroup(cell, cells, filter) {
|
|
|
18828
18878
|
if (x === cell) {
|
|
18829
18879
|
found = true;
|
|
18830
18880
|
}
|
|
18831
|
-
const cellValue =
|
|
18832
|
-
|
|
18881
|
+
const cellValue = x?.isFormula
|
|
18882
|
+
? undefined
|
|
18883
|
+
: evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
|
|
18884
|
+
if (cellValue && filter(cellValue)) {
|
|
18833
18885
|
group.push(cellValue);
|
|
18834
18886
|
}
|
|
18835
18887
|
else {
|
|
@@ -19478,9 +19530,15 @@ function getChartDatasetValues(getters, dataSets) {
|
|
|
19478
19530
|
: (label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`);
|
|
19479
19531
|
}
|
|
19480
19532
|
else {
|
|
19481
|
-
label =
|
|
19533
|
+
label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`;
|
|
19482
19534
|
}
|
|
19483
19535
|
let data = ds.dataRange ? getData(getters, ds) : [];
|
|
19536
|
+
if (data.every((e) => typeof e === "string" && !isEvaluationError(e))) {
|
|
19537
|
+
// In this case, we want a chart based on the string occurrences count
|
|
19538
|
+
// This will be done by associating each string with a value of 1 and
|
|
19539
|
+
// the using the classical aggregation method to sum the values.
|
|
19540
|
+
data.fill(1);
|
|
19541
|
+
}
|
|
19484
19542
|
datasetValues.push({ data, label });
|
|
19485
19543
|
}
|
|
19486
19544
|
return datasetValues;
|
|
@@ -19579,7 +19637,7 @@ class BarChart extends AbstractChart {
|
|
|
19579
19637
|
dataSets: context.range ? context.range : [],
|
|
19580
19638
|
dataSetsHaveTitle: false,
|
|
19581
19639
|
stacked: false,
|
|
19582
|
-
aggregated: false,
|
|
19640
|
+
aggregated: context.aggregated ?? false,
|
|
19583
19641
|
legendPosition: "top",
|
|
19584
19642
|
title: context.title || "",
|
|
19585
19643
|
type: "bar",
|
|
@@ -19595,6 +19653,7 @@ class BarChart extends AbstractChart {
|
|
|
19595
19653
|
auxiliaryRange: this.labelRange
|
|
19596
19654
|
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
19597
19655
|
: undefined,
|
|
19656
|
+
aggregated: this.aggregated,
|
|
19598
19657
|
};
|
|
19599
19658
|
}
|
|
19600
19659
|
copyForSheetId(sheetId) {
|
|
@@ -20344,7 +20403,7 @@ class LineChart extends AbstractChart {
|
|
|
20344
20403
|
verticalAxisPosition: "left",
|
|
20345
20404
|
labelRange: context.auxiliaryRange || undefined,
|
|
20346
20405
|
stacked: false,
|
|
20347
|
-
aggregated: false,
|
|
20406
|
+
aggregated: context.aggregated ?? false,
|
|
20348
20407
|
cumulative: false,
|
|
20349
20408
|
};
|
|
20350
20409
|
}
|
|
@@ -20377,6 +20436,7 @@ class LineChart extends AbstractChart {
|
|
|
20377
20436
|
auxiliaryRange: this.labelRange
|
|
20378
20437
|
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
20379
20438
|
: undefined,
|
|
20439
|
+
aggregated: this.aggregated,
|
|
20380
20440
|
};
|
|
20381
20441
|
}
|
|
20382
20442
|
updateRanges(applyChange) {
|
|
@@ -20451,7 +20511,7 @@ class PieChart extends AbstractChart {
|
|
|
20451
20511
|
title: context.title || "",
|
|
20452
20512
|
type: "pie",
|
|
20453
20513
|
labelRange: context.auxiliaryRange || undefined,
|
|
20454
|
-
aggregated: false,
|
|
20514
|
+
aggregated: context.aggregated ?? false,
|
|
20455
20515
|
};
|
|
20456
20516
|
}
|
|
20457
20517
|
getDefinition() {
|
|
@@ -20465,6 +20525,7 @@ class PieChart extends AbstractChart {
|
|
|
20465
20525
|
auxiliaryRange: this.labelRange
|
|
20466
20526
|
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
20467
20527
|
: undefined,
|
|
20528
|
+
aggregated: this.aggregated,
|
|
20468
20529
|
};
|
|
20469
20530
|
}
|
|
20470
20531
|
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
@@ -20656,7 +20717,7 @@ class ScatterChart extends AbstractChart {
|
|
|
20656
20717
|
type: "scatter",
|
|
20657
20718
|
verticalAxisPosition: "left",
|
|
20658
20719
|
labelRange: context.auxiliaryRange || undefined,
|
|
20659
|
-
aggregated: false,
|
|
20720
|
+
aggregated: context.aggregated ?? false,
|
|
20660
20721
|
};
|
|
20661
20722
|
}
|
|
20662
20723
|
getDefinition() {
|
|
@@ -20686,6 +20747,7 @@ class ScatterChart extends AbstractChart {
|
|
|
20686
20747
|
auxiliaryRange: this.labelRange
|
|
20687
20748
|
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
20688
20749
|
: undefined,
|
|
20750
|
+
aggregated: this.aggregated,
|
|
20689
20751
|
};
|
|
20690
20752
|
}
|
|
20691
20753
|
updateRanges(applyChange) {
|
|
@@ -22353,6 +22415,50 @@ function isCtrlKey(ev) {
|
|
|
22353
22415
|
return isMacOS() ? ev.metaKey : ev.ctrlKey;
|
|
22354
22416
|
}
|
|
22355
22417
|
|
|
22418
|
+
/**
|
|
22419
|
+
* Repeatedly calls a callback function with a time delay between calls.
|
|
22420
|
+
*/
|
|
22421
|
+
function useInterval(callback, delay) {
|
|
22422
|
+
let intervalId;
|
|
22423
|
+
const { setInterval, clearInterval } = window;
|
|
22424
|
+
owl.useEffect(() => {
|
|
22425
|
+
intervalId = setInterval(callback, delay);
|
|
22426
|
+
return () => clearInterval(intervalId);
|
|
22427
|
+
}, () => [delay]);
|
|
22428
|
+
return {
|
|
22429
|
+
pause: () => {
|
|
22430
|
+
clearInterval(intervalId);
|
|
22431
|
+
intervalId = undefined;
|
|
22432
|
+
},
|
|
22433
|
+
resume: () => {
|
|
22434
|
+
if (intervalId === undefined) {
|
|
22435
|
+
intervalId = setInterval(callback, delay);
|
|
22436
|
+
}
|
|
22437
|
+
},
|
|
22438
|
+
};
|
|
22439
|
+
}
|
|
22440
|
+
/**
|
|
22441
|
+
* Calls a callback function with a time delay
|
|
22442
|
+
*/
|
|
22443
|
+
function useTimeOut() {
|
|
22444
|
+
let timeOutId;
|
|
22445
|
+
function clear() {
|
|
22446
|
+
if (timeOutId !== undefined) {
|
|
22447
|
+
clearTimeout(timeOutId);
|
|
22448
|
+
timeOutId = undefined;
|
|
22449
|
+
}
|
|
22450
|
+
}
|
|
22451
|
+
function schedule(callback, delay) {
|
|
22452
|
+
clear();
|
|
22453
|
+
timeOutId = setTimeout(callback, delay);
|
|
22454
|
+
}
|
|
22455
|
+
owl.onWillUnmount(clear);
|
|
22456
|
+
return {
|
|
22457
|
+
clear,
|
|
22458
|
+
schedule,
|
|
22459
|
+
};
|
|
22460
|
+
}
|
|
22461
|
+
|
|
22356
22462
|
//------------------------------------------------------------------------------
|
|
22357
22463
|
// Context Menu Component
|
|
22358
22464
|
//------------------------------------------------------------------------------
|
|
@@ -22403,6 +22509,7 @@ css /* scss */ `
|
|
|
22403
22509
|
}
|
|
22404
22510
|
}
|
|
22405
22511
|
`;
|
|
22512
|
+
const TIMEOUT_DELAY = 250;
|
|
22406
22513
|
class Menu extends owl.Component {
|
|
22407
22514
|
static template = "o-spreadsheet-Menu";
|
|
22408
22515
|
static props = {
|
|
@@ -22413,6 +22520,7 @@ class Menu extends owl.Component {
|
|
|
22413
22520
|
onClose: Function,
|
|
22414
22521
|
onMenuClicked: { type: Function, optional: true },
|
|
22415
22522
|
menuId: { type: String, optional: true },
|
|
22523
|
+
onMouseOver: { type: Function, optional: true },
|
|
22416
22524
|
};
|
|
22417
22525
|
static components = { Menu, Popover };
|
|
22418
22526
|
static defaultProps = {
|
|
@@ -22423,10 +22531,12 @@ class Menu extends owl.Component {
|
|
|
22423
22531
|
position: null,
|
|
22424
22532
|
scrollOffset: 0,
|
|
22425
22533
|
menuItems: [],
|
|
22534
|
+
isHoveringChild: false,
|
|
22426
22535
|
});
|
|
22427
22536
|
menuRef = owl.useRef("menu");
|
|
22428
22537
|
hoveredMenu = undefined;
|
|
22429
22538
|
position = useAbsoluteBoundingRect(this.menuRef);
|
|
22539
|
+
openingTimeOut = useTimeOut();
|
|
22430
22540
|
setup() {
|
|
22431
22541
|
owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
|
|
22432
22542
|
owl.useExternalListener(window, "contextmenu", this.onExternalClick, { capture: true });
|
|
@@ -22526,6 +22636,9 @@ class Menu extends owl.Component {
|
|
|
22526
22636
|
}
|
|
22527
22637
|
return false;
|
|
22528
22638
|
}
|
|
22639
|
+
isActive(menuItem) {
|
|
22640
|
+
return (this.subMenu?.isHoveringChild || false) && this.isParentMenu(this.subMenu, menuItem);
|
|
22641
|
+
}
|
|
22529
22642
|
onScroll(ev) {
|
|
22530
22643
|
this.subMenu.scrollOffset = ev.target.scrollTop;
|
|
22531
22644
|
}
|
|
@@ -22533,10 +22646,10 @@ class Menu extends owl.Component {
|
|
|
22533
22646
|
* If the given menu is not disabled, open it's submenu at the
|
|
22534
22647
|
* correct position according to available surrounding space.
|
|
22535
22648
|
*/
|
|
22536
|
-
openSubMenu(menu,
|
|
22537
|
-
|
|
22538
|
-
if (!parentMenuEl)
|
|
22649
|
+
openSubMenu(menu, parentMenuEl) {
|
|
22650
|
+
if (!parentMenuEl) {
|
|
22539
22651
|
return;
|
|
22652
|
+
}
|
|
22540
22653
|
const y = parentMenuEl.getBoundingClientRect().top;
|
|
22541
22654
|
this.subMenu.position = {
|
|
22542
22655
|
x: this.position.x + this.props.depth * MENU_WIDTH,
|
|
@@ -22550,32 +22663,50 @@ class Menu extends owl.Component {
|
|
|
22550
22663
|
return subMenu.parentMenu?.id === menuItem.id;
|
|
22551
22664
|
}
|
|
22552
22665
|
closeSubMenu() {
|
|
22666
|
+
if (this.subMenu.isHoveringChild) {
|
|
22667
|
+
return;
|
|
22668
|
+
}
|
|
22553
22669
|
this.subMenu.isOpen = false;
|
|
22554
22670
|
this.subMenu.parentMenu = undefined;
|
|
22555
22671
|
}
|
|
22556
22672
|
onClickMenu(menu, ev) {
|
|
22557
22673
|
if (this.isEnabled(menu)) {
|
|
22558
22674
|
if (this.isRoot(menu)) {
|
|
22559
|
-
this.openSubMenu(menu, ev);
|
|
22675
|
+
this.openSubMenu(menu, ev.currentTarget);
|
|
22560
22676
|
}
|
|
22561
22677
|
else {
|
|
22562
22678
|
this.activateMenu(menu);
|
|
22563
22679
|
}
|
|
22564
22680
|
}
|
|
22565
22681
|
}
|
|
22566
|
-
|
|
22567
|
-
this.hoveredMenu = menu;
|
|
22568
|
-
menu.onStartHover?.(this.env);
|
|
22682
|
+
onMouseOver(menu, ev) {
|
|
22569
22683
|
if (this.isEnabled(menu)) {
|
|
22570
|
-
if (this.
|
|
22571
|
-
this.
|
|
22684
|
+
if (this.isParentMenu(this.subMenu, menu)) {
|
|
22685
|
+
this.openingTimeOut.clear();
|
|
22686
|
+
return;
|
|
22572
22687
|
}
|
|
22573
|
-
|
|
22574
|
-
|
|
22688
|
+
const currentTarget = ev.currentTarget;
|
|
22689
|
+
if (this.isRoot(menu)) {
|
|
22690
|
+
this.openingTimeOut.schedule(() => {
|
|
22691
|
+
this.openSubMenu(menu, currentTarget);
|
|
22692
|
+
}, TIMEOUT_DELAY);
|
|
22575
22693
|
}
|
|
22576
22694
|
}
|
|
22577
22695
|
}
|
|
22696
|
+
onMouseOverMainMenu() {
|
|
22697
|
+
this.props.onMouseOver?.();
|
|
22698
|
+
this.subMenu.isHoveringChild = false;
|
|
22699
|
+
}
|
|
22700
|
+
onMouseOverChildMenu() {
|
|
22701
|
+
this.subMenu.isHoveringChild = true;
|
|
22702
|
+
this.openingTimeOut.clear();
|
|
22703
|
+
}
|
|
22704
|
+
onMouseEnter(menu, ev) {
|
|
22705
|
+
this.hoveredMenu = menu;
|
|
22706
|
+
menu.onStartHover?.(this.env);
|
|
22707
|
+
}
|
|
22578
22708
|
onMouseLeave(menu) {
|
|
22709
|
+
this.openingTimeOut.schedule(this.closeSubMenu.bind(this), TIMEOUT_DELAY);
|
|
22579
22710
|
this.hoveredMenu = undefined;
|
|
22580
22711
|
menu.onStopHover?.(this.env);
|
|
22581
22712
|
}
|
|
@@ -22827,7 +22958,8 @@ function getChartTypes() {
|
|
|
22827
22958
|
*/
|
|
22828
22959
|
function getSmartChartDefinition(zone, getters) {
|
|
22829
22960
|
let dataSetZone = zone;
|
|
22830
|
-
|
|
22961
|
+
const singleColumn = zoneToDimension(zone).numberOfCols === 1;
|
|
22962
|
+
if (!singleColumn) {
|
|
22831
22963
|
dataSetZone = { ...zone, left: zone.left + 1 };
|
|
22832
22964
|
}
|
|
22833
22965
|
const dataSets = [zoneToXc(dataSetZone)];
|
|
@@ -22861,7 +22993,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
22861
22993
|
}
|
|
22862
22994
|
}
|
|
22863
22995
|
let labelRangeXc;
|
|
22864
|
-
if (
|
|
22996
|
+
if (!singleColumn) {
|
|
22865
22997
|
labelRangeXc = zoneToXc({
|
|
22866
22998
|
...zone,
|
|
22867
22999
|
right: zone.left,
|
|
@@ -22885,6 +23017,19 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
22885
23017
|
legendPosition: newLegendPos,
|
|
22886
23018
|
};
|
|
22887
23019
|
}
|
|
23020
|
+
const _dataSets = createDataSets(getters, dataSets, sheetId, dataSetsHaveTitle);
|
|
23021
|
+
if (singleColumn &&
|
|
23022
|
+
getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
|
|
23023
|
+
return {
|
|
23024
|
+
title: "",
|
|
23025
|
+
dataSets,
|
|
23026
|
+
aggregated: true,
|
|
23027
|
+
labelRange: dataSets[0],
|
|
23028
|
+
type: "pie",
|
|
23029
|
+
legendPosition: "top",
|
|
23030
|
+
dataSetsHaveTitle: false,
|
|
23031
|
+
};
|
|
23032
|
+
}
|
|
22888
23033
|
return {
|
|
22889
23034
|
title,
|
|
22890
23035
|
dataSets,
|
|
@@ -22912,6 +23057,7 @@ const DEFAULT_TABLE_CONFIG = {
|
|
|
22912
23057
|
numberOfHeaders: 1,
|
|
22913
23058
|
bandedRows: true,
|
|
22914
23059
|
bandedColumns: false,
|
|
23060
|
+
automaticAutofill: true,
|
|
22915
23061
|
styleId: "TableStyleMedium2",
|
|
22916
23062
|
};
|
|
22917
23063
|
function generateColorSet(name, highlightColor) {
|
|
@@ -26395,7 +26541,7 @@ class LineBarPieConfigPanel extends owl.Component {
|
|
|
26395
26541
|
{
|
|
26396
26542
|
name: "aggregated",
|
|
26397
26543
|
label: _t("Aggregate"),
|
|
26398
|
-
value: this.props.definition.aggregated,
|
|
26544
|
+
value: this.props.definition.aggregated ?? false,
|
|
26399
26545
|
onChange: this.onUpdateAggregated.bind(this),
|
|
26400
26546
|
},
|
|
26401
26547
|
];
|
|
@@ -27566,15 +27712,15 @@ css /* scss */ `
|
|
|
27566
27712
|
// -----------------------------------------------------------------------------
|
|
27567
27713
|
// We need here the svg of the icons that we need to convert to images for the renderer
|
|
27568
27714
|
// -----------------------------------------------------------------------------
|
|
27569
|
-
const ARROW_DOWN = '<svg class="o-cf-icon arrow-down" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#
|
|
27570
|
-
const ARROW_UP = '<svg class="o-cf-icon arrow-up" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#
|
|
27715
|
+
const ARROW_DOWN = '<svg class="o-cf-icon arrow-down" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#E06666" d="M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z"></path></svg>';
|
|
27716
|
+
const ARROW_UP = '<svg class="o-cf-icon arrow-up" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#6AA84F" d="M34.9 289.5l-22.2-22.2c-9.4-9.4-9.4-24.6 0-33.9L207 39c9.4-9.4 24.6-9.4 33.9 0l194.3 194.3c9.4 9.4 9.4 24.6 0 33.9L413 289.4c-9.5 9.5-25 9.3-34.3-.4L264 168.6V456c0 13.3-10.7 24-24 24h-32c-13.3 0-24-10.7-24-24V168.6L69.2 289.1c-9.3 9.8-24.8 10-34.3.4z"></path></svg>';
|
|
27571
27717
|
const ARROW_RIGHT = '<svg class="o-cf-icon arrow-right" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#F0AD4E" d="M190.5 66.9l22.2-22.2c9.4-9.4 24.6-9.4 33.9 0L441 239c9.4 9.4 9.4 24.6 0 33.9L246.6 467.3c-9.4 9.4-24.6 9.4-33.9 0l-22.2-22.2c-9.5-9.5-9.3-25 .4-34.3L311.4 296H24c-13.3 0-24-10.7-24-24v-32c0-13.3 10.7-24 24-24h287.4L190.9 101.2c-9.8-9.3-10-24.8-.4-34.3z"></path></svg>';
|
|
27572
|
-
const SMILE = '<svg class="o-cf-icon smile" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#
|
|
27718
|
+
const SMILE = '<svg class="o-cf-icon smile" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#6AA84F" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z"></path></svg>';
|
|
27573
27719
|
const MEH = '<svg class="o-cf-icon meh" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#F0AD4E" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm8 144H160c-13.2 0-24 10.8-24 24s10.8 24 24 24h176c13.2 0 24-10.8 24-24s-10.8-24-24-24z"></path></svg>';
|
|
27574
|
-
const FROWN = '<svg class="o-cf-icon frown" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#
|
|
27575
|
-
const GREEN_DOT = '<svg class="o-cf-icon green-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#
|
|
27720
|
+
const FROWN = '<svg class="o-cf-icon frown" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#E06666" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm-80 128c-40.2 0-78 17.7-103.8 48.6-8.5 10.2-7.1 25.3 3.1 33.8 10.2 8.4 25.3 7.1 33.8-3.1 16.6-19.9 41-31.4 66.9-31.4s50.3 11.4 66.9 31.4c8.1 9.7 23.1 11.9 33.8 3.1 10.2-8.5 11.5-23.6 3.1-33.8C326 321.7 288.2 304 248 304z"></path></svg>';
|
|
27721
|
+
const GREEN_DOT = '<svg class="o-cf-icon green-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#6AA84F" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
|
|
27576
27722
|
const YELLOW_DOT = '<svg class="o-cf-icon yellow-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#F0AD4E" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
|
|
27577
|
-
const RED_DOT = '<svg class="o-cf-icon red-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#
|
|
27723
|
+
const RED_DOT = '<svg class="o-cf-icon red-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#E06666" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
|
|
27578
27724
|
function loadIconImage(svg) {
|
|
27579
27725
|
/** We have to add xmlns, as it's not added by owl in the canvas */
|
|
27580
27726
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" ${svg.slice(4)}`;
|
|
@@ -29440,8 +29586,7 @@ class DataValidationPanel extends owl.Component {
|
|
|
29440
29586
|
}
|
|
29441
29587
|
}
|
|
29442
29588
|
|
|
29443
|
-
const
|
|
29444
|
-
const BACKGROUND_COLOR = "#8B008B33";
|
|
29589
|
+
const FIND_AND_REPLACE_HIGHLIGHT_COLOR = "#8B008B";
|
|
29445
29590
|
var Direction;
|
|
29446
29591
|
(function (Direction) {
|
|
29447
29592
|
Direction[Direction["previous"] = -1] = "previous";
|
|
@@ -29472,14 +29617,14 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
29472
29617
|
super(get);
|
|
29473
29618
|
this.initialShowFormulaState = this.model.getters.shouldShowFormulas();
|
|
29474
29619
|
this.searchOptions.searchFormulas = this.initialShowFormulaState;
|
|
29620
|
+
const highlightStore = get(HighlightStore);
|
|
29621
|
+
highlightStore.register(owl.toRaw(this));
|
|
29475
29622
|
this.onDispose(() => {
|
|
29476
29623
|
this.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.initialShowFormulaState });
|
|
29477
29624
|
this.updateSearchContent.stopDebounce();
|
|
29625
|
+
highlightStore.unRegister(owl.toRaw(this));
|
|
29478
29626
|
});
|
|
29479
29627
|
}
|
|
29480
|
-
get renderingLayers() {
|
|
29481
|
-
return ["Search"];
|
|
29482
|
-
}
|
|
29483
29628
|
get searchMatches() {
|
|
29484
29629
|
switch (this.searchOptions.searchScope) {
|
|
29485
29630
|
case "allSheets":
|
|
@@ -29706,8 +29851,8 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
29706
29851
|
// ---------------------------------------------------------------------------
|
|
29707
29852
|
// Grid rendering
|
|
29708
29853
|
// ---------------------------------------------------------------------------
|
|
29709
|
-
|
|
29710
|
-
const
|
|
29854
|
+
get highlights() {
|
|
29855
|
+
const highlights = [];
|
|
29711
29856
|
const sheetId = this.getters.getActiveSheetId();
|
|
29712
29857
|
for (const [index, match] of this.searchMatches.entries()) {
|
|
29713
29858
|
if (match.sheetId !== sheetId) {
|
|
@@ -29715,26 +29860,31 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
29715
29860
|
}
|
|
29716
29861
|
const zone = positionToZone(match);
|
|
29717
29862
|
const zoneWithMerge = this.getters.expandZone(sheetId, zone);
|
|
29718
|
-
const {
|
|
29863
|
+
const { width, height } = this.getters.getVisibleRect(zoneWithMerge);
|
|
29719
29864
|
if (width > 0 && height > 0) {
|
|
29720
|
-
|
|
29721
|
-
|
|
29722
|
-
|
|
29723
|
-
|
|
29724
|
-
|
|
29725
|
-
|
|
29865
|
+
highlights.push({
|
|
29866
|
+
sheetId,
|
|
29867
|
+
zone: zoneWithMerge,
|
|
29868
|
+
color: FIND_AND_REPLACE_HIGHLIGHT_COLOR,
|
|
29869
|
+
noBorder: index !== this.selectedMatchIndex,
|
|
29870
|
+
thinLine: true,
|
|
29871
|
+
fillAlpha: 0.2,
|
|
29872
|
+
});
|
|
29726
29873
|
}
|
|
29727
29874
|
}
|
|
29728
|
-
// TODO: use Highlights helpers/stores when the Highlight PR is merged
|
|
29729
29875
|
if (this.searchOptions.searchScope === "specificRange") {
|
|
29730
29876
|
const range = this.searchOptions.specificRange;
|
|
29731
|
-
if (
|
|
29732
|
-
|
|
29877
|
+
if (range && range.sheetId === sheetId) {
|
|
29878
|
+
highlights.push({
|
|
29879
|
+
sheetId,
|
|
29880
|
+
zone: range.zone,
|
|
29881
|
+
color: FIND_AND_REPLACE_HIGHLIGHT_COLOR,
|
|
29882
|
+
noFill: true,
|
|
29883
|
+
thinLine: true,
|
|
29884
|
+
});
|
|
29733
29885
|
}
|
|
29734
|
-
const { x, y, width, height } = this.getters.getVisibleRect(range.zone);
|
|
29735
|
-
ctx.strokeStyle = BORDER_COLOR;
|
|
29736
|
-
ctx.strokeRect(x, y, width, height);
|
|
29737
29886
|
}
|
|
29887
|
+
return highlights;
|
|
29738
29888
|
}
|
|
29739
29889
|
}
|
|
29740
29890
|
|
|
@@ -30148,6 +30298,12 @@ const TABLE_ELEMENTS_BY_PRIORITY = [
|
|
|
30148
30298
|
"headerRow",
|
|
30149
30299
|
"totalRow",
|
|
30150
30300
|
];
|
|
30301
|
+
/** Return the content zone of the table, ie. the table zone without the headers */
|
|
30302
|
+
function getTableContentZone(tableZone, tableConfig) {
|
|
30303
|
+
const numberOfHeaders = tableConfig.numberOfHeaders;
|
|
30304
|
+
const contentZone = { ...tableZone, top: tableZone.top + numberOfHeaders };
|
|
30305
|
+
return contentZone.top <= contentZone.bottom ? contentZone : undefined;
|
|
30306
|
+
}
|
|
30151
30307
|
function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
|
|
30152
30308
|
return {
|
|
30153
30309
|
borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
|
|
@@ -30599,14 +30755,6 @@ class TablePanel extends owl.Component {
|
|
|
30599
30755
|
tableXc: this.env.model.getters.getRangeString(this.props.table.range, sheetId),
|
|
30600
30756
|
filtersEnabledIfPossible: this.props.table.config.hasFilters,
|
|
30601
30757
|
});
|
|
30602
|
-
owl.onWillUpdateProps((nextProps) => {
|
|
30603
|
-
if (this.props.table.id !== nextProps.table.id) {
|
|
30604
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
30605
|
-
this.state.tableXc = this.env.model.getters.getRangeString(nextProps.table.range, sheetId);
|
|
30606
|
-
this.state.tableZoneErrors = [];
|
|
30607
|
-
this.state.filtersEnabledIfPossible = nextProps.table.config.hasFilters;
|
|
30608
|
-
}
|
|
30609
|
-
});
|
|
30610
30758
|
}
|
|
30611
30759
|
updateHasFilters(hasFilters) {
|
|
30612
30760
|
this.state.filtersEnabledIfPossible = hasFilters;
|
|
@@ -30755,7 +30903,11 @@ sidePanelRegistry.add("TableSidePanel", {
|
|
|
30755
30903
|
if (!table) {
|
|
30756
30904
|
return { isOpen: false };
|
|
30757
30905
|
}
|
|
30758
|
-
return {
|
|
30906
|
+
return {
|
|
30907
|
+
isOpen: true,
|
|
30908
|
+
props: { table },
|
|
30909
|
+
key: table.id,
|
|
30910
|
+
};
|
|
30759
30911
|
},
|
|
30760
30912
|
});
|
|
30761
30913
|
|
|
@@ -32470,19 +32622,21 @@ class GridCellIcon extends owl.Component {
|
|
|
32470
32622
|
slots: Object,
|
|
32471
32623
|
};
|
|
32472
32624
|
get iconStyle() {
|
|
32473
|
-
const
|
|
32474
|
-
const
|
|
32625
|
+
const cellPosition = this.props.cellPosition;
|
|
32626
|
+
const merge = this.env.model.getters.getMerge(cellPosition);
|
|
32627
|
+
const zone = merge || positionToZone(cellPosition);
|
|
32628
|
+
const rect = this.env.model.getters.getVisibleRectWithoutHeaders(zone);
|
|
32629
|
+
const x = this.getIconHorizontalPosition(rect, cellPosition);
|
|
32630
|
+
const y = this.getIconVerticalPosition(rect, cellPosition);
|
|
32475
32631
|
return cssPropertiesToCss({
|
|
32476
32632
|
top: `${y + (this.props.offset?.y || 0)}px`,
|
|
32477
32633
|
left: `${x + (this.props.offset?.x || 0)}px`,
|
|
32478
32634
|
});
|
|
32479
32635
|
}
|
|
32480
|
-
getIconVerticalPosition() {
|
|
32481
|
-
const
|
|
32482
|
-
const
|
|
32483
|
-
const
|
|
32484
|
-
const end = this.env.model.getters.getRowDimensionsInViewport(sheetId, merge ? merge.bottom : row).end;
|
|
32485
|
-
const cell = this.env.model.getters.getCell(this.props.cellPosition);
|
|
32636
|
+
getIconVerticalPosition(rect, cellPosition) {
|
|
32637
|
+
const start = rect.y;
|
|
32638
|
+
const end = rect.y + rect.height;
|
|
32639
|
+
const cell = this.env.model.getters.getCell(cellPosition);
|
|
32486
32640
|
const align = this.props.verticalAlign || cell?.style?.verticalAlign || DEFAULT_VERTICAL_ALIGN;
|
|
32487
32641
|
switch (align) {
|
|
32488
32642
|
case "bottom":
|
|
@@ -32494,13 +32648,11 @@ class GridCellIcon extends owl.Component {
|
|
|
32494
32648
|
return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
|
|
32495
32649
|
}
|
|
32496
32650
|
}
|
|
32497
|
-
getIconHorizontalPosition() {
|
|
32498
|
-
const
|
|
32499
|
-
const
|
|
32500
|
-
const
|
|
32501
|
-
const
|
|
32502
|
-
const cell = this.env.model.getters.getCell(this.props.cellPosition);
|
|
32503
|
-
const evaluatedCell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
|
|
32651
|
+
getIconHorizontalPosition(rect, cellPosition) {
|
|
32652
|
+
const start = rect.x;
|
|
32653
|
+
const end = rect.x + rect.width;
|
|
32654
|
+
const cell = this.env.model.getters.getCell(cellPosition);
|
|
32655
|
+
const evaluatedCell = this.env.model.getters.getEvaluatedCell(cellPosition);
|
|
32504
32656
|
const align = this.props.horizontalAlign || cell?.style?.align || evaluatedCell.defaultAlign;
|
|
32505
32657
|
switch (align) {
|
|
32506
32658
|
case "right":
|
|
@@ -32605,6 +32757,8 @@ css /* scss */ `
|
|
|
32605
32757
|
height: ${CHECKBOX_WIDTH}px;
|
|
32606
32758
|
accent-color: #808080;
|
|
32607
32759
|
margin: ${MARGIN}px;
|
|
32760
|
+
/** required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
32761
|
+
position: absolute;
|
|
32608
32762
|
}
|
|
32609
32763
|
`;
|
|
32610
32764
|
class DataValidationCheckbox extends owl.Component {
|
|
@@ -32623,7 +32777,7 @@ class DataValidationCheckbox extends owl.Component {
|
|
|
32623
32777
|
}
|
|
32624
32778
|
get isDisabled() {
|
|
32625
32779
|
const cell = this.env.model.getters.getCell(this.props.cellPosition);
|
|
32626
|
-
return !!cell?.isFormula;
|
|
32780
|
+
return this.env.model.getters.isReadonly() || !!cell?.isFormula;
|
|
32627
32781
|
}
|
|
32628
32782
|
}
|
|
32629
32783
|
|
|
@@ -32663,12 +32817,17 @@ class DataValidationOverlay extends owl.Component {
|
|
|
32663
32817
|
static props = {};
|
|
32664
32818
|
static components = { GridCellIcon, DataValidationCheckbox, DataValidationListIcon };
|
|
32665
32819
|
get checkBoxCellPositions() {
|
|
32666
|
-
return this.env.model.getters
|
|
32820
|
+
return this.env.model.getters
|
|
32821
|
+
.getVisibleCellPositions()
|
|
32822
|
+
.filter(this.env.model.getters.isCellValidCheckbox);
|
|
32667
32823
|
}
|
|
32668
32824
|
get listIconsCellPositions() {
|
|
32669
|
-
|
|
32670
|
-
|
|
32671
|
-
|
|
32825
|
+
if (this.env.model.getters.isReadonly()) {
|
|
32826
|
+
return [];
|
|
32827
|
+
}
|
|
32828
|
+
return this.env.model.getters
|
|
32829
|
+
.getVisibleCellPositions()
|
|
32830
|
+
.filter(this.env.model.getters.cellHasListDataValidationIcon);
|
|
32672
32831
|
}
|
|
32673
32832
|
}
|
|
32674
32833
|
|
|
@@ -32713,7 +32872,7 @@ function dragFigureForMove({ x: mouseX, y: mouseY }, { x: mouseInitialX, y: mous
|
|
|
32713
32872
|
const newY = clip(initialFigure.y + deltaY, minY, maxY - initialFigure.height - scrollY);
|
|
32714
32873
|
return { ...initialFigure, x: newX, y: newY };
|
|
32715
32874
|
}
|
|
32716
|
-
function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize) {
|
|
32875
|
+
function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize, { scrollX, scrollY }) {
|
|
32717
32876
|
let { x, y, width, height } = initialFigure;
|
|
32718
32877
|
if (keepRatio && dirX != 0 && dirY != 0) {
|
|
32719
32878
|
const deltaX = Math.min(dirX * (mouseInitialX - mouseX), initialFigure.width - minFigSize);
|
|
@@ -32740,14 +32899,14 @@ function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }
|
|
|
32740
32899
|
y = initialFigure.y - deltaY;
|
|
32741
32900
|
}
|
|
32742
32901
|
}
|
|
32743
|
-
//
|
|
32744
|
-
if (x
|
|
32745
|
-
width
|
|
32746
|
-
x =
|
|
32902
|
+
// Adjusts figure dimensions to ensure it remains within header boundaries and viewport during resizing.
|
|
32903
|
+
if (x + scrollX <= 0) {
|
|
32904
|
+
width = width + x + scrollX;
|
|
32905
|
+
x = -scrollX;
|
|
32747
32906
|
}
|
|
32748
|
-
if (y
|
|
32749
|
-
height
|
|
32750
|
-
y =
|
|
32907
|
+
if (y + scrollY <= 0) {
|
|
32908
|
+
height = height + y + scrollY;
|
|
32909
|
+
y = -scrollY;
|
|
32751
32910
|
}
|
|
32752
32911
|
return { ...initialFigure, x, y, width, height };
|
|
32753
32912
|
}
|
|
@@ -33167,7 +33326,7 @@ class FiguresContainer extends owl.Component {
|
|
|
33167
33326
|
const minFigSize = figureRegistry.get(figure.tag).minFigSize || MIN_FIG_SIZE;
|
|
33168
33327
|
const onMouseMove = (ev) => {
|
|
33169
33328
|
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
33170
|
-
const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize);
|
|
33329
|
+
const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize, this.env.model.getters.getActiveSheetScrollInfo());
|
|
33171
33330
|
const otherFigures = this.getOtherFigures(figure.id);
|
|
33172
33331
|
const snapResult = snapForResize(this.env.model.getters, dirX, dirY, draggedFigure, otherFigures);
|
|
33173
33332
|
this.dnd.draggedFigure = snapResult.snappedFigure;
|
|
@@ -33343,29 +33502,6 @@ class GridAddRowsFooter extends owl.Component {
|
|
|
33343
33502
|
}
|
|
33344
33503
|
}
|
|
33345
33504
|
|
|
33346
|
-
/**
|
|
33347
|
-
* Repeatedly calls a callback function with a time delay between calls.
|
|
33348
|
-
*/
|
|
33349
|
-
function useInterval(callback, delay) {
|
|
33350
|
-
let intervalId;
|
|
33351
|
-
const { setInterval, clearInterval } = window;
|
|
33352
|
-
owl.useEffect(() => {
|
|
33353
|
-
intervalId = setInterval(callback, delay);
|
|
33354
|
-
return () => clearInterval(intervalId);
|
|
33355
|
-
}, () => [delay]);
|
|
33356
|
-
return {
|
|
33357
|
-
pause: () => {
|
|
33358
|
-
clearInterval(intervalId);
|
|
33359
|
-
intervalId = undefined;
|
|
33360
|
-
},
|
|
33361
|
-
resume: () => {
|
|
33362
|
-
if (intervalId === undefined) {
|
|
33363
|
-
intervalId = setInterval(callback, delay);
|
|
33364
|
-
}
|
|
33365
|
-
},
|
|
33366
|
-
};
|
|
33367
|
-
}
|
|
33368
|
-
|
|
33369
33505
|
const CURSOR_SVG = /*xml*/ `
|
|
33370
33506
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="16"><path d="M6.5.4c1.3-.8 2.9-.1 3.8 1.4l2.9 5.1c.2.4.9 1.6-.4 2.3l-1.6.9 1.8 3.1c.2.4.1 1-.2 1.2l-1.6 1c-.3.1-.9 0-1.1-.4l-1.8-3.1-1.6 1c-.6.4-1.7 0-2.2-.8L0 4.3"/><path fill="#fff" d="M9.1 2a1.4 1.1 60 0 0-1.7-.6L5.5 2.5l.9 1.6-1 .6-.9-1.6-.6.4 1.8 3.1-1.3.7-1.8-3.1-1 .6 3.8 6.6 6.8-3.98M3.9 8.8 10.82 5l.795 1.4-6.81 3.96"/></svg>
|
|
33371
33507
|
`;
|
|
@@ -35250,10 +35386,17 @@ class SidePanelStore extends SpreadsheetStore {
|
|
|
35250
35386
|
get panelProps() {
|
|
35251
35387
|
const state = this.computeState(this.componentTag, this.initialPanelProps);
|
|
35252
35388
|
if (state.isOpen) {
|
|
35253
|
-
return state.props;
|
|
35389
|
+
return state.props ?? {};
|
|
35254
35390
|
}
|
|
35255
35391
|
return {};
|
|
35256
35392
|
}
|
|
35393
|
+
get panelKey() {
|
|
35394
|
+
const state = this.computeState(this.componentTag, this.initialPanelProps);
|
|
35395
|
+
if (state.isOpen) {
|
|
35396
|
+
return state.key;
|
|
35397
|
+
}
|
|
35398
|
+
return undefined;
|
|
35399
|
+
}
|
|
35257
35400
|
open(componentTag, panelProps = {}) {
|
|
35258
35401
|
const state = this.computeState(componentTag, panelProps);
|
|
35259
35402
|
if (state.isOpen === false) {
|
|
@@ -35263,7 +35406,7 @@ class SidePanelStore extends SpreadsheetStore {
|
|
|
35263
35406
|
this.initialPanelProps?.onCloseSidePanel?.();
|
|
35264
35407
|
}
|
|
35265
35408
|
this.componentTag = componentTag;
|
|
35266
|
-
this.initialPanelProps = state.props;
|
|
35409
|
+
this.initialPanelProps = state.props ?? {};
|
|
35267
35410
|
}
|
|
35268
35411
|
toggle(componentTag, panelProps) {
|
|
35269
35412
|
if (this.isOpen && componentTag === this.componentTag) {
|
|
@@ -40740,10 +40883,8 @@ function compileTokens(tokens) {
|
|
|
40740
40883
|
"ref", // a function to access a certain dependency at a given index
|
|
40741
40884
|
"range", // same as above, but guarantee that the result is in the form of a range
|
|
40742
40885
|
"ctx", code.toString());
|
|
40743
|
-
|
|
40744
|
-
|
|
40745
|
-
execute: baseFunction,
|
|
40746
|
-
};
|
|
40886
|
+
// @ts-ignore
|
|
40887
|
+
functionCache[cacheKey] = baseFunction;
|
|
40747
40888
|
/**
|
|
40748
40889
|
* This function compile the function arguments. It is mostly straightforward,
|
|
40749
40890
|
* except that there is a non trivial transformation in one situation:
|
|
@@ -40850,7 +40991,7 @@ function compileTokens(tokens) {
|
|
|
40850
40991
|
}
|
|
40851
40992
|
}
|
|
40852
40993
|
const compiledFormula = {
|
|
40853
|
-
execute: functionCache[cacheKey]
|
|
40994
|
+
execute: functionCache[cacheKey],
|
|
40854
40995
|
dependencies,
|
|
40855
40996
|
constantValues,
|
|
40856
40997
|
tokens,
|
|
@@ -42335,6 +42476,15 @@ class FigurePlugin extends CorePlugin {
|
|
|
42335
42476
|
return "Success" /* CommandResult.Success */;
|
|
42336
42477
|
}
|
|
42337
42478
|
}
|
|
42479
|
+
beforeHandle(cmd) {
|
|
42480
|
+
switch (cmd.type) {
|
|
42481
|
+
case "DELETE_SHEET":
|
|
42482
|
+
this.getters.getFigures(cmd.sheetId).forEach((figure) => {
|
|
42483
|
+
this.dispatch("DELETE_FIGURE", { id: figure.id, sheetId: cmd.sheetId });
|
|
42484
|
+
});
|
|
42485
|
+
break;
|
|
42486
|
+
}
|
|
42487
|
+
}
|
|
42338
42488
|
handle(cmd) {
|
|
42339
42489
|
switch (cmd.type) {
|
|
42340
42490
|
case "CREATE_SHEET":
|
|
@@ -43821,7 +43971,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
43821
43971
|
"getNumberHeaders",
|
|
43822
43972
|
"getGridLinesVisibility",
|
|
43823
43973
|
"getNextSheetName",
|
|
43824
|
-
"isEmpty",
|
|
43825
43974
|
"getSheetSize",
|
|
43826
43975
|
"getSheetZone",
|
|
43827
43976
|
"getPaneDivisions",
|
|
@@ -44193,14 +44342,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44193
44342
|
// ---------------------------------------------------------------------------
|
|
44194
44343
|
// Row/Col manipulation
|
|
44195
44344
|
// ---------------------------------------------------------------------------
|
|
44196
|
-
/**
|
|
44197
|
-
* Check if a zone only contains empty cells
|
|
44198
|
-
*/
|
|
44199
|
-
isEmpty(sheetId, zone) {
|
|
44200
|
-
return positions(zone)
|
|
44201
|
-
.map(({ col, row }) => this.getCell({ sheetId, col, row }))
|
|
44202
|
-
.every((cell) => !cell || cell.content === "");
|
|
44203
|
-
}
|
|
44204
44345
|
getCommandZones(cmd) {
|
|
44205
44346
|
const zones = [];
|
|
44206
44347
|
if ("zone" in cmd) {
|
|
@@ -45004,13 +45145,15 @@ class TablePlugin extends CorePlugin {
|
|
|
45004
45145
|
if (zone.left !== zone.right) {
|
|
45005
45146
|
throw new Error("Can only define a filter on a single column");
|
|
45006
45147
|
}
|
|
45007
|
-
const
|
|
45008
|
-
const filteredRange =
|
|
45148
|
+
const contentZone = getTableContentZone(zone, config);
|
|
45149
|
+
const filteredRange = contentZone
|
|
45150
|
+
? this.getters.getRangeFromZone(range.sheetId, contentZone)
|
|
45151
|
+
: undefined;
|
|
45009
45152
|
return {
|
|
45010
45153
|
id,
|
|
45011
45154
|
rangeWithHeaders: range,
|
|
45012
45155
|
col: zone.left,
|
|
45013
|
-
filteredRange
|
|
45156
|
+
filteredRange,
|
|
45014
45157
|
};
|
|
45015
45158
|
}
|
|
45016
45159
|
copyTableForSheet(sheetId, table) {
|
|
@@ -45619,7 +45762,11 @@ class CompilationParametersBuilder {
|
|
|
45619
45762
|
});
|
|
45620
45763
|
}
|
|
45621
45764
|
getParameters() {
|
|
45622
|
-
return
|
|
45765
|
+
return {
|
|
45766
|
+
referenceDenormalizer: this.refFn.bind(this),
|
|
45767
|
+
ensureRange: this.range.bind(this),
|
|
45768
|
+
evalContext: this.evalContext,
|
|
45769
|
+
};
|
|
45623
45770
|
}
|
|
45624
45771
|
/**
|
|
45625
45772
|
* Returns the value of the cell(s) used in reference
|
|
@@ -45700,6 +45847,41 @@ class CompilationParametersBuilder {
|
|
|
45700
45847
|
}
|
|
45701
45848
|
}
|
|
45702
45849
|
|
|
45850
|
+
class PositionMap {
|
|
45851
|
+
map = {};
|
|
45852
|
+
set({ sheetId, col, row }, value) {
|
|
45853
|
+
const map = this.map;
|
|
45854
|
+
if (!map[sheetId]) {
|
|
45855
|
+
map[sheetId] = {};
|
|
45856
|
+
}
|
|
45857
|
+
if (!map[sheetId][col]) {
|
|
45858
|
+
map[sheetId][col] = {};
|
|
45859
|
+
}
|
|
45860
|
+
map[sheetId][col][row] = value;
|
|
45861
|
+
}
|
|
45862
|
+
get({ sheetId, col, row }) {
|
|
45863
|
+
return this.map[sheetId]?.[col]?.[row];
|
|
45864
|
+
}
|
|
45865
|
+
has({ sheetId, col, row }) {
|
|
45866
|
+
return this.map[sheetId]?.[col]?.[row] !== undefined;
|
|
45867
|
+
}
|
|
45868
|
+
delete({ sheetId, col, row }) {
|
|
45869
|
+
delete this.map[sheetId]?.[col]?.[row];
|
|
45870
|
+
}
|
|
45871
|
+
keys() {
|
|
45872
|
+
const map = this.map;
|
|
45873
|
+
const keys = [];
|
|
45874
|
+
for (const sheetId in map) {
|
|
45875
|
+
for (const col in map[sheetId]) {
|
|
45876
|
+
for (const row in map[sheetId][col]) {
|
|
45877
|
+
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
45878
|
+
}
|
|
45879
|
+
}
|
|
45880
|
+
}
|
|
45881
|
+
return keys;
|
|
45882
|
+
}
|
|
45883
|
+
}
|
|
45884
|
+
|
|
45703
45885
|
function quickselect(arr, k, left, right, compare) {
|
|
45704
45886
|
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
|
|
45705
45887
|
}
|
|
@@ -46426,26 +46608,26 @@ class ZoneRBush extends RBush {
|
|
|
46426
46608
|
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
46427
46609
|
*/
|
|
46428
46610
|
class FormulaDependencyGraph {
|
|
46429
|
-
|
|
46430
|
-
dependencies = new
|
|
46611
|
+
createEmptyPositionSet;
|
|
46612
|
+
dependencies = new PositionMap();
|
|
46431
46613
|
rTree;
|
|
46432
|
-
constructor(
|
|
46433
|
-
this.
|
|
46614
|
+
constructor(createEmptyPositionSet, data = []) {
|
|
46615
|
+
this.createEmptyPositionSet = createEmptyPositionSet;
|
|
46434
46616
|
this.rTree = new SpreadsheetRTree(data);
|
|
46435
46617
|
}
|
|
46436
|
-
removeAllDependencies(
|
|
46437
|
-
const ranges = this.dependencies.get(
|
|
46618
|
+
removeAllDependencies(formulaPosition) {
|
|
46619
|
+
const ranges = this.dependencies.get(formulaPosition);
|
|
46438
46620
|
if (!ranges) {
|
|
46439
46621
|
return;
|
|
46440
46622
|
}
|
|
46441
46623
|
for (const range of ranges) {
|
|
46442
46624
|
this.rTree.remove(range);
|
|
46443
46625
|
}
|
|
46444
|
-
this.dependencies.delete(
|
|
46626
|
+
this.dependencies.delete(formulaPosition);
|
|
46445
46627
|
}
|
|
46446
|
-
addDependencies(
|
|
46628
|
+
addDependencies(formulaPosition, dependencies) {
|
|
46447
46629
|
const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
|
|
46448
|
-
data:
|
|
46630
|
+
data: formulaPosition,
|
|
46449
46631
|
boundingBox: {
|
|
46450
46632
|
zone,
|
|
46451
46633
|
sheetId,
|
|
@@ -46454,12 +46636,12 @@ class FormulaDependencyGraph {
|
|
|
46454
46636
|
for (const item of rTreeItems) {
|
|
46455
46637
|
this.rTree.insert(item);
|
|
46456
46638
|
}
|
|
46457
|
-
const existingDependencies = this.dependencies.get(
|
|
46639
|
+
const existingDependencies = this.dependencies.get(formulaPosition);
|
|
46458
46640
|
if (existingDependencies) {
|
|
46459
46641
|
existingDependencies.push(...rTreeItems);
|
|
46460
46642
|
}
|
|
46461
46643
|
else {
|
|
46462
|
-
this.dependencies.set(
|
|
46644
|
+
this.dependencies.set(formulaPosition, rTreeItems);
|
|
46463
46645
|
}
|
|
46464
46646
|
}
|
|
46465
46647
|
/**
|
|
@@ -46468,23 +46650,194 @@ class FormulaDependencyGraph {
|
|
|
46468
46650
|
* This is called a topological ordering (excluding cycles)
|
|
46469
46651
|
*/
|
|
46470
46652
|
getCellsDependingOn(ranges) {
|
|
46471
|
-
const visited =
|
|
46653
|
+
const visited = this.createEmptyPositionSet();
|
|
46472
46654
|
const queue = Array.from(ranges).reverse();
|
|
46473
46655
|
while (queue.length > 0) {
|
|
46474
46656
|
const range = queue.pop();
|
|
46475
|
-
visited.addMany(
|
|
46476
|
-
const
|
|
46477
|
-
for (const
|
|
46478
|
-
if (!visited.has(
|
|
46479
|
-
queue.push(
|
|
46657
|
+
visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
|
|
46658
|
+
const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
|
|
46659
|
+
for (const position of impactedPositions) {
|
|
46660
|
+
if (!visited.has(position)) {
|
|
46661
|
+
queue.push({ sheetId: position.sheetId, zone: positionToZone(position) });
|
|
46480
46662
|
}
|
|
46481
46663
|
}
|
|
46482
46664
|
}
|
|
46483
|
-
visited.deleteMany(ranges.flatMap((r) =>
|
|
46665
|
+
visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
|
|
46484
46666
|
return visited;
|
|
46485
46667
|
}
|
|
46486
46668
|
}
|
|
46487
46669
|
|
|
46670
|
+
/**
|
|
46671
|
+
* Implements a fixed-sized grid or 2D matrix of bits.
|
|
46672
|
+
* based on https://github.com/zandaqo/structurae
|
|
46673
|
+
*
|
|
46674
|
+
* The grid is implemented as a 1D array of 32-bit integers, where each bit represents a cell in the grid.
|
|
46675
|
+
* It follows row-major order, with each row stored consecutively in 32-bit blocks.
|
|
46676
|
+
* Pads the number of columns to the next power of 2 to allow quick lookups with bitwise operations.
|
|
46677
|
+
*
|
|
46678
|
+
* Key terminology:
|
|
46679
|
+
* - bucket: Index of an item in the Uint32Array, a 32-bit integer.
|
|
46680
|
+
* - bitPosition: The position of a bit within the bucket 32-bit integer.
|
|
46681
|
+
*/
|
|
46682
|
+
class BinaryGrid extends Uint32Array {
|
|
46683
|
+
columnOffset = 0;
|
|
46684
|
+
cols = 0;
|
|
46685
|
+
rows = 0;
|
|
46686
|
+
/**
|
|
46687
|
+
* Creates a binary grid of specified dimensions.
|
|
46688
|
+
*/
|
|
46689
|
+
static create(rows, columns) {
|
|
46690
|
+
const columnOffset = log2Ceil(columns);
|
|
46691
|
+
const length = (rows << columnOffset) >> 5;
|
|
46692
|
+
const grid = new this(length + 1);
|
|
46693
|
+
grid.columnOffset = columnOffset;
|
|
46694
|
+
grid.cols = columns;
|
|
46695
|
+
grid.rows = rows;
|
|
46696
|
+
return grid;
|
|
46697
|
+
}
|
|
46698
|
+
/**
|
|
46699
|
+
* Returns the bit at given coordinates.
|
|
46700
|
+
*/
|
|
46701
|
+
getValue(position) {
|
|
46702
|
+
const [bucket, bitPosition] = this.getCoordinates(position);
|
|
46703
|
+
return ((this[bucket] >> bitPosition) & 1);
|
|
46704
|
+
}
|
|
46705
|
+
/**
|
|
46706
|
+
* Sets the bit at given coordinates.
|
|
46707
|
+
*/
|
|
46708
|
+
setValue(position, value) {
|
|
46709
|
+
const [bucket, bitPosition] = this.getCoordinates(position);
|
|
46710
|
+
const currentValue = (this[bucket] >> bitPosition) & 1;
|
|
46711
|
+
const hasBeenInserted = currentValue === 0 && value === 1;
|
|
46712
|
+
this[bucket] = (this[bucket] & ~(1 << bitPosition)) | (value << bitPosition);
|
|
46713
|
+
return hasBeenInserted;
|
|
46714
|
+
// Let's breakdown of the above line:
|
|
46715
|
+
// with an example with a 4-bit integer (instead of 32-bit).
|
|
46716
|
+
//
|
|
46717
|
+
// Let's say we want to set the bit at position 2 to 1 and the existing
|
|
46718
|
+
// bit sequence this[bucket] is 1001. The final bit sequence should be 1101.
|
|
46719
|
+
//
|
|
46720
|
+
// First, we clear the bit at position 2 by AND-ing this[bucket] with a
|
|
46721
|
+
// mask having all 1s except a 0 at the bit position (~ (1 << bitPosition)).
|
|
46722
|
+
// 1 << bitPosition is 0100 (shifting 0001 to the left by 2)
|
|
46723
|
+
// Inverting the bits with ~ gives the final mask ~(1 << bitPosition): 1011
|
|
46724
|
+
//
|
|
46725
|
+
// Then, we shift the value by the bit position (value << bitPosition: 0100)
|
|
46726
|
+
// and OR the result with the previous step's result:
|
|
46727
|
+
// (1001 & 1011) | 0100 = 1101
|
|
46728
|
+
}
|
|
46729
|
+
isEmpty() {
|
|
46730
|
+
return !this.some((bucket) => bucket !== 0);
|
|
46731
|
+
}
|
|
46732
|
+
fillAllPositions() {
|
|
46733
|
+
const thirtyTwoOnes = -1 >>> 0; // same as 2 ** 32 - 1, a 32-bit number with all bits set to 1
|
|
46734
|
+
this.fill(thirtyTwoOnes);
|
|
46735
|
+
}
|
|
46736
|
+
clear() {
|
|
46737
|
+
this.fill(0);
|
|
46738
|
+
}
|
|
46739
|
+
getCoordinates(position) {
|
|
46740
|
+
const { row, col } = position;
|
|
46741
|
+
const index = (row << this.columnOffset) + col;
|
|
46742
|
+
const bucket = index >> 5;
|
|
46743
|
+
return [bucket, index - (bucket << 5)];
|
|
46744
|
+
}
|
|
46745
|
+
}
|
|
46746
|
+
function log2Ceil(value) {
|
|
46747
|
+
// A faster version of Math.ceil(Math.log2(value)).
|
|
46748
|
+
if (value === 0) {
|
|
46749
|
+
return -Infinity;
|
|
46750
|
+
}
|
|
46751
|
+
else if (value < 0) {
|
|
46752
|
+
return NaN;
|
|
46753
|
+
}
|
|
46754
|
+
// --value handles the case where value is a power of 2
|
|
46755
|
+
return 32 - Math.clz32(--value);
|
|
46756
|
+
}
|
|
46757
|
+
|
|
46758
|
+
class PositionSet {
|
|
46759
|
+
sheets = {};
|
|
46760
|
+
/**
|
|
46761
|
+
* List of positions in the order they were inserted.
|
|
46762
|
+
*/
|
|
46763
|
+
insertions = [];
|
|
46764
|
+
maxSize = 0;
|
|
46765
|
+
constructor(sheetSizes) {
|
|
46766
|
+
for (const sheetId in sheetSizes) {
|
|
46767
|
+
const cols = sheetSizes[sheetId].cols;
|
|
46768
|
+
const rows = sheetSizes[sheetId].rows;
|
|
46769
|
+
this.maxSize += cols * rows;
|
|
46770
|
+
this.sheets[sheetId] = BinaryGrid.create(rows, cols);
|
|
46771
|
+
}
|
|
46772
|
+
}
|
|
46773
|
+
add(position) {
|
|
46774
|
+
const hasBeenInserted = this.sheets[position.sheetId].setValue(position, 1);
|
|
46775
|
+
if (hasBeenInserted) {
|
|
46776
|
+
this.insertions.push(position);
|
|
46777
|
+
}
|
|
46778
|
+
}
|
|
46779
|
+
addMany(positions) {
|
|
46780
|
+
for (const position of positions) {
|
|
46781
|
+
this.add(position);
|
|
46782
|
+
}
|
|
46783
|
+
}
|
|
46784
|
+
delete(position) {
|
|
46785
|
+
this.sheets[position.sheetId].setValue(position, 0);
|
|
46786
|
+
}
|
|
46787
|
+
deleteMany(positions) {
|
|
46788
|
+
for (const position of positions) {
|
|
46789
|
+
this.delete(position);
|
|
46790
|
+
}
|
|
46791
|
+
}
|
|
46792
|
+
has(position) {
|
|
46793
|
+
return this.sheets[position.sheetId].getValue(position) === 1;
|
|
46794
|
+
}
|
|
46795
|
+
clear() {
|
|
46796
|
+
const insertions = this.insertions;
|
|
46797
|
+
this.insertions = [];
|
|
46798
|
+
for (const sheetId in this.sheets) {
|
|
46799
|
+
this.sheets[sheetId].clear();
|
|
46800
|
+
}
|
|
46801
|
+
return insertions;
|
|
46802
|
+
}
|
|
46803
|
+
isEmpty() {
|
|
46804
|
+
if (this.insertions.length === 0) {
|
|
46805
|
+
return true;
|
|
46806
|
+
}
|
|
46807
|
+
for (const sheetId in this.sheets) {
|
|
46808
|
+
if (!this.sheets[sheetId].isEmpty()) {
|
|
46809
|
+
return false;
|
|
46810
|
+
}
|
|
46811
|
+
}
|
|
46812
|
+
return true;
|
|
46813
|
+
}
|
|
46814
|
+
fillAllPositions() {
|
|
46815
|
+
this.insertions = new Array(this.maxSize);
|
|
46816
|
+
let index = 0;
|
|
46817
|
+
for (const sheetId in this.sheets) {
|
|
46818
|
+
const grid = this.sheets[sheetId];
|
|
46819
|
+
grid.fillAllPositions();
|
|
46820
|
+
for (let i = 0; i < grid.rows; i++) {
|
|
46821
|
+
for (let j = 0; j < grid.cols; j++) {
|
|
46822
|
+
this.insertions[index++] = { sheetId, row: i, col: j };
|
|
46823
|
+
}
|
|
46824
|
+
}
|
|
46825
|
+
}
|
|
46826
|
+
}
|
|
46827
|
+
/**
|
|
46828
|
+
* Iterate over the positions in the order of insertion.
|
|
46829
|
+
* Note that the same position may be yielded multiple times if the value was added
|
|
46830
|
+
* to the set then removed and then added again.
|
|
46831
|
+
*/
|
|
46832
|
+
*[Symbol.iterator]() {
|
|
46833
|
+
for (const position of this.insertions) {
|
|
46834
|
+
if (this.sheets[position.sheetId].getValue(position) === 1) {
|
|
46835
|
+
yield position;
|
|
46836
|
+
}
|
|
46837
|
+
}
|
|
46838
|
+
}
|
|
46839
|
+
}
|
|
46840
|
+
|
|
46488
46841
|
/**
|
|
46489
46842
|
* Contains, for each cell, the array
|
|
46490
46843
|
* formulas that could potentially spread on it
|
|
@@ -46498,6 +46851,7 @@ class FormulaDependencyGraph {
|
|
|
46498
46851
|
*
|
|
46499
46852
|
*/
|
|
46500
46853
|
class SpreadingRelation {
|
|
46854
|
+
createEmptyPositionSet;
|
|
46501
46855
|
/**
|
|
46502
46856
|
* Internal structure:
|
|
46503
46857
|
* For something like
|
|
@@ -46526,39 +46880,42 @@ class SpreadingRelation {
|
|
|
46526
46880
|
* - (B1) --> (B2, B3, B4) meaning B1 spreads on B2, B3 and B4
|
|
46527
46881
|
*
|
|
46528
46882
|
*/
|
|
46529
|
-
resultsToArrayFormulas = new
|
|
46530
|
-
arrayFormulasToResults = new
|
|
46531
|
-
|
|
46532
|
-
|
|
46883
|
+
resultsToArrayFormulas = new PositionMap();
|
|
46884
|
+
arrayFormulasToResults = new PositionMap();
|
|
46885
|
+
constructor(createEmptyPositionSet) {
|
|
46886
|
+
this.createEmptyPositionSet = createEmptyPositionSet;
|
|
46887
|
+
}
|
|
46888
|
+
getFormulaPositionsSpreadingOn(resultPosition) {
|
|
46889
|
+
return this.resultsToArrayFormulas.get(resultPosition) || EMPTY_ARRAY;
|
|
46533
46890
|
}
|
|
46534
|
-
|
|
46535
|
-
return this.arrayFormulasToResults.get(
|
|
46891
|
+
getArrayResultPositions(formulasPosition) {
|
|
46892
|
+
return this.arrayFormulasToResults.get(formulasPosition) || EMPTY_ARRAY;
|
|
46536
46893
|
}
|
|
46537
46894
|
/**
|
|
46538
46895
|
* Remove a node, also remove it from other nodes adjacency list
|
|
46539
46896
|
*/
|
|
46540
|
-
removeNode(
|
|
46541
|
-
this.resultsToArrayFormulas.delete(
|
|
46542
|
-
this.arrayFormulasToResults.delete(
|
|
46897
|
+
removeNode(position) {
|
|
46898
|
+
this.resultsToArrayFormulas.delete(position);
|
|
46899
|
+
this.arrayFormulasToResults.delete(position);
|
|
46543
46900
|
}
|
|
46544
46901
|
/**
|
|
46545
46902
|
* Create a spreading relation between two cells
|
|
46546
46903
|
*/
|
|
46547
|
-
addRelation({
|
|
46548
|
-
if (!this.resultsToArrayFormulas.has(
|
|
46549
|
-
this.resultsToArrayFormulas.set(
|
|
46904
|
+
addRelation({ arrayFormulaPosition, resultPosition, }) {
|
|
46905
|
+
if (!this.resultsToArrayFormulas.has(resultPosition)) {
|
|
46906
|
+
this.resultsToArrayFormulas.set(resultPosition, this.createEmptyPositionSet());
|
|
46550
46907
|
}
|
|
46551
|
-
this.resultsToArrayFormulas.get(
|
|
46552
|
-
if (!this.arrayFormulasToResults.has(
|
|
46553
|
-
this.arrayFormulasToResults.set(
|
|
46908
|
+
this.resultsToArrayFormulas.get(resultPosition)?.add(arrayFormulaPosition);
|
|
46909
|
+
if (!this.arrayFormulasToResults.has(arrayFormulaPosition)) {
|
|
46910
|
+
this.arrayFormulasToResults.set(arrayFormulaPosition, this.createEmptyPositionSet());
|
|
46554
46911
|
}
|
|
46555
|
-
this.arrayFormulasToResults.get(
|
|
46912
|
+
this.arrayFormulasToResults.get(arrayFormulaPosition)?.add(resultPosition);
|
|
46556
46913
|
}
|
|
46557
|
-
hasArrayFormulaResult(
|
|
46558
|
-
return this.resultsToArrayFormulas.has(
|
|
46914
|
+
hasArrayFormulaResult(position) {
|
|
46915
|
+
return this.resultsToArrayFormulas.has(position);
|
|
46559
46916
|
}
|
|
46560
|
-
isArrayFormula(
|
|
46561
|
-
return this.arrayFormulasToResults.has(
|
|
46917
|
+
isArrayFormula(position) {
|
|
46918
|
+
return this.arrayFormulasToResults.has(position);
|
|
46562
46919
|
}
|
|
46563
46920
|
}
|
|
46564
46921
|
const EMPTY_ARRAY = [];
|
|
@@ -46566,104 +46923,103 @@ const EMPTY_ARRAY = [];
|
|
|
46566
46923
|
const MAX_ITERATION = 30;
|
|
46567
46924
|
const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
|
|
46568
46925
|
const EMPTY_CELL = createEvaluatedCell({ value: null });
|
|
46569
|
-
const EVAL_CONTEXT_INDEX = 2;
|
|
46570
46926
|
class Evaluator {
|
|
46571
46927
|
context;
|
|
46572
46928
|
getters;
|
|
46573
46929
|
compilationParams;
|
|
46574
|
-
|
|
46575
|
-
|
|
46576
|
-
|
|
46577
|
-
|
|
46578
|
-
spreadingRelations = new SpreadingRelation();
|
|
46930
|
+
evaluatedCells = new PositionMap();
|
|
46931
|
+
formulaDependencies = lazy(new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this)));
|
|
46932
|
+
blockedArrayFormulas = new PositionSet({});
|
|
46933
|
+
spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
|
|
46579
46934
|
constructor(context, getters) {
|
|
46580
46935
|
this.context = context;
|
|
46581
46936
|
this.getters = getters;
|
|
46582
46937
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
46583
46938
|
}
|
|
46584
46939
|
getEvaluatedCell(position) {
|
|
46585
|
-
return this.evaluatedCells.get(
|
|
46940
|
+
return this.evaluatedCells.get(position) || EMPTY_CELL;
|
|
46586
46941
|
}
|
|
46587
46942
|
getSpreadPositionsOf(position) {
|
|
46588
|
-
|
|
46589
|
-
if (!this.spreadingRelations.isArrayFormula(positionId)) {
|
|
46943
|
+
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
46590
46944
|
return [];
|
|
46591
46945
|
}
|
|
46592
|
-
return Array.from(this.spreadingRelations.
|
|
46593
|
-
}
|
|
46594
|
-
getArrayFormulaSpreadingOn(position) {
|
|
46595
|
-
const positionId = this.encoder.encode(position);
|
|
46596
|
-
const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
|
|
46597
|
-
return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
|
|
46946
|
+
return Array.from(this.spreadingRelations.getArrayResultPositions(position));
|
|
46598
46947
|
}
|
|
46599
46948
|
getEvaluatedPositions() {
|
|
46600
|
-
return
|
|
46949
|
+
return this.evaluatedCells.keys();
|
|
46601
46950
|
}
|
|
46602
|
-
|
|
46603
|
-
if (!this.spreadingRelations.hasArrayFormulaResult(
|
|
46951
|
+
getArrayFormulaSpreadingOn(position) {
|
|
46952
|
+
if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
|
|
46604
46953
|
return undefined;
|
|
46605
46954
|
}
|
|
46606
|
-
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(
|
|
46607
|
-
return Array.from(arrayFormulas).find((
|
|
46955
|
+
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
|
|
46956
|
+
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
46608
46957
|
}
|
|
46609
46958
|
updateDependencies(position) {
|
|
46610
|
-
|
|
46611
|
-
this.
|
|
46612
|
-
|
|
46613
|
-
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
46959
|
+
this.formulaDependencies().removeAllDependencies(position);
|
|
46960
|
+
const dependencies = this.getDirectDependencies(position);
|
|
46961
|
+
this.formulaDependencies().addDependencies(position, dependencies);
|
|
46614
46962
|
}
|
|
46615
46963
|
addDependencies(position, dependencies) {
|
|
46616
|
-
|
|
46617
|
-
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
46964
|
+
this.formulaDependencies().addDependencies(position, dependencies);
|
|
46618
46965
|
}
|
|
46619
46966
|
updateCompilationParameters() {
|
|
46620
46967
|
// rebuild the compilation parameters (with a clean cache)
|
|
46621
46968
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
46622
|
-
this.compilationParams
|
|
46623
|
-
|
|
46624
|
-
|
|
46969
|
+
this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
|
|
46970
|
+
this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
|
|
46971
|
+
}
|
|
46972
|
+
createEmptyPositionSet() {
|
|
46973
|
+
const sheetSizes = {};
|
|
46974
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
46975
|
+
sheetSizes[sheetId] = {
|
|
46976
|
+
rows: this.getters.getNumberRows(sheetId),
|
|
46977
|
+
cols: this.getters.getNumberCols(sheetId),
|
|
46978
|
+
};
|
|
46979
|
+
}
|
|
46980
|
+
return new PositionSet(sheetSizes);
|
|
46625
46981
|
}
|
|
46626
46982
|
evaluateCells(positions) {
|
|
46627
|
-
const
|
|
46628
|
-
|
|
46629
|
-
const
|
|
46630
|
-
cellsToCompute.addMany(this.getCellsDependingOn(
|
|
46631
|
-
cellsToCompute.addMany(
|
|
46632
|
-
cellsToCompute.addMany(this.getCellsDependingOn(
|
|
46983
|
+
const cellsToCompute = this.createEmptyPositionSet();
|
|
46984
|
+
cellsToCompute.addMany(positions);
|
|
46985
|
+
const arrayFormulasPositions = this.getArrayFormulasImpactedByChangesOf(positions);
|
|
46986
|
+
cellsToCompute.addMany(this.getCellsDependingOn(positions));
|
|
46987
|
+
cellsToCompute.addMany(arrayFormulasPositions);
|
|
46988
|
+
cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
|
|
46633
46989
|
this.evaluate(cellsToCompute);
|
|
46634
46990
|
}
|
|
46635
|
-
getArrayFormulasImpactedByChangesOf(
|
|
46636
|
-
const
|
|
46637
|
-
for (const
|
|
46638
|
-
const content = this.getCell(
|
|
46639
|
-
const
|
|
46640
|
-
if (
|
|
46991
|
+
getArrayFormulasImpactedByChangesOf(positions) {
|
|
46992
|
+
const impactedPositions = this.createEmptyPositionSet();
|
|
46993
|
+
for (const position of positions) {
|
|
46994
|
+
const content = this.getters.getCell(position)?.content;
|
|
46995
|
+
const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
|
|
46996
|
+
if (arrayFormulaPosition !== undefined) {
|
|
46641
46997
|
// take into account new collisions.
|
|
46642
|
-
|
|
46998
|
+
impactedPositions.add(arrayFormulaPosition);
|
|
46643
46999
|
}
|
|
46644
47000
|
if (!content) {
|
|
46645
47001
|
// The previous content could have blocked some array formulas
|
|
46646
|
-
|
|
47002
|
+
impactedPositions.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(position));
|
|
46647
47003
|
}
|
|
46648
47004
|
}
|
|
46649
|
-
return
|
|
47005
|
+
return impactedPositions;
|
|
46650
47006
|
}
|
|
46651
47007
|
buildDependencyGraph() {
|
|
46652
|
-
this.blockedArrayFormulas =
|
|
46653
|
-
this.spreadingRelations = new SpreadingRelation();
|
|
47008
|
+
this.blockedArrayFormulas = this.createEmptyPositionSet();
|
|
47009
|
+
this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
|
|
46654
47010
|
this.formulaDependencies = lazy(() => {
|
|
46655
|
-
const dependencies = [...this.getAllCells()].flatMap((
|
|
46656
|
-
data:
|
|
47011
|
+
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position).map((range) => ({
|
|
47012
|
+
data: position,
|
|
46657
47013
|
boundingBox: {
|
|
46658
47014
|
zone: range.zone,
|
|
46659
47015
|
sheetId: range.sheetId,
|
|
46660
47016
|
},
|
|
46661
47017
|
})));
|
|
46662
|
-
return new FormulaDependencyGraph(this.
|
|
47018
|
+
return new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this), dependencies);
|
|
46663
47019
|
});
|
|
46664
47020
|
}
|
|
46665
47021
|
evaluateAllCells() {
|
|
46666
|
-
this.evaluatedCells = new
|
|
47022
|
+
this.evaluatedCells = new PositionMap();
|
|
46667
47023
|
this.evaluate(this.getAllCells());
|
|
46668
47024
|
}
|
|
46669
47025
|
evaluateFormula(sheetId, formulaString) {
|
|
@@ -46680,57 +47036,50 @@ class Evaluator {
|
|
|
46680
47036
|
return result.value;
|
|
46681
47037
|
}
|
|
46682
47038
|
getAllCells() {
|
|
46683
|
-
const
|
|
46684
|
-
|
|
46685
|
-
|
|
46686
|
-
for (const cellId in cellIds) {
|
|
46687
|
-
positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
|
|
46688
|
-
}
|
|
46689
|
-
}
|
|
46690
|
-
return positionIds;
|
|
47039
|
+
const positions = this.createEmptyPositionSet();
|
|
47040
|
+
positions.fillAllPositions();
|
|
47041
|
+
return positions;
|
|
46691
47042
|
}
|
|
46692
|
-
getArrayFormulasBlockedByOrSpreadingOn(
|
|
46693
|
-
if (!this.spreadingRelations.hasArrayFormulaResult(
|
|
47043
|
+
getArrayFormulasBlockedByOrSpreadingOn(position) {
|
|
47044
|
+
if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
|
|
46694
47045
|
return [];
|
|
46695
47046
|
}
|
|
46696
|
-
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(
|
|
46697
|
-
const
|
|
46698
|
-
|
|
46699
|
-
|
|
47047
|
+
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
|
|
47048
|
+
const positions = this.createEmptyPositionSet();
|
|
47049
|
+
positions.addMany(arrayFormulas);
|
|
47050
|
+
positions.addMany(this.getCellsDependingOn(arrayFormulas));
|
|
47051
|
+
return positions;
|
|
46700
47052
|
}
|
|
46701
|
-
nextPositionsToUpdate = new
|
|
47053
|
+
nextPositionsToUpdate = new PositionSet({});
|
|
46702
47054
|
cellsBeingComputed = new Set();
|
|
46703
|
-
evaluate(
|
|
47055
|
+
evaluate(positions) {
|
|
46704
47056
|
this.cellsBeingComputed = new Set();
|
|
46705
|
-
this.nextPositionsToUpdate =
|
|
47057
|
+
this.nextPositionsToUpdate = positions;
|
|
46706
47058
|
let currentIteration = 0;
|
|
46707
|
-
while (this.nextPositionsToUpdate.
|
|
47059
|
+
while (!this.nextPositionsToUpdate.isEmpty() && currentIteration++ < MAX_ITERATION) {
|
|
46708
47060
|
this.updateCompilationParameters();
|
|
46709
|
-
const
|
|
46710
|
-
|
|
46711
|
-
|
|
46712
|
-
const cell = positionIds[i];
|
|
46713
|
-
this.evaluatedCells.delete(cell);
|
|
47061
|
+
const positions = this.nextPositionsToUpdate.clear();
|
|
47062
|
+
for (let i = 0; i < positions.length; ++i) {
|
|
47063
|
+
this.evaluatedCells.delete(positions[i]);
|
|
46714
47064
|
}
|
|
46715
|
-
for (let i = 0; i <
|
|
46716
|
-
const
|
|
46717
|
-
|
|
47065
|
+
for (let i = 0; i < positions.length; ++i) {
|
|
47066
|
+
const position = positions[i];
|
|
47067
|
+
const evaluatedCell = this.computeCell(position);
|
|
47068
|
+
if (evaluatedCell !== EMPTY_CELL) {
|
|
47069
|
+
this.evaluatedCells.set(position, evaluatedCell);
|
|
47070
|
+
}
|
|
46718
47071
|
}
|
|
46719
47072
|
}
|
|
46720
47073
|
}
|
|
46721
|
-
|
|
46722
|
-
this.evaluatedCells.
|
|
46723
|
-
}
|
|
46724
|
-
computeCell(positionId) {
|
|
46725
|
-
const evaluation = this.evaluatedCells.get(positionId);
|
|
47074
|
+
computeCell(position) {
|
|
47075
|
+
const evaluation = this.evaluatedCells.get(position);
|
|
46726
47076
|
if (evaluation) {
|
|
46727
47077
|
return evaluation; // already computed
|
|
46728
47078
|
}
|
|
46729
|
-
if (!this.blockedArrayFormulas.has(
|
|
46730
|
-
this.invalidateSpreading(
|
|
47079
|
+
if (!this.blockedArrayFormulas.has(position)) {
|
|
47080
|
+
this.invalidateSpreading(position);
|
|
46731
47081
|
}
|
|
46732
|
-
const
|
|
46733
|
-
const cell = this.getters.getCell(cellPosition);
|
|
47082
|
+
const cell = this.getters.getCell(position);
|
|
46734
47083
|
if (cell === undefined) {
|
|
46735
47084
|
return EMPTY_CELL;
|
|
46736
47085
|
}
|
|
@@ -46742,10 +47091,12 @@ class Evaluator {
|
|
|
46742
47091
|
}
|
|
46743
47092
|
this.cellsBeingComputed.add(cellId);
|
|
46744
47093
|
return cell.isFormula
|
|
46745
|
-
? this.computeFormulaCell(
|
|
47094
|
+
? this.computeFormulaCell(position.sheetId, cell)
|
|
46746
47095
|
: evaluateLiteral(cell.content, localeFormat);
|
|
46747
47096
|
}
|
|
46748
47097
|
catch (e) {
|
|
47098
|
+
e.value = e?.value || CellErrorType.GenericError;
|
|
47099
|
+
e.message = e?.message || implementationErrorMessage;
|
|
46749
47100
|
return createEvaluatedCell(e);
|
|
46750
47101
|
}
|
|
46751
47102
|
finally {
|
|
@@ -46753,10 +47104,9 @@ class Evaluator {
|
|
|
46753
47104
|
}
|
|
46754
47105
|
}
|
|
46755
47106
|
computeAndSave(position) {
|
|
46756
|
-
const
|
|
46757
|
-
|
|
46758
|
-
|
|
46759
|
-
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
47107
|
+
const evaluatedCell = this.computeCell(position);
|
|
47108
|
+
if (!this.evaluatedCells.has(position)) {
|
|
47109
|
+
this.evaluatedCells.set(position, evaluatedCell);
|
|
46760
47110
|
}
|
|
46761
47111
|
return evaluatedCell;
|
|
46762
47112
|
}
|
|
@@ -46794,24 +47144,23 @@ class Evaluator {
|
|
|
46794
47144
|
throw new EvaluationError(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
|
|
46795
47145
|
}
|
|
46796
47146
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
46797
|
-
const
|
|
47147
|
+
const arrayFormulaPosition = { sheetId, col, row };
|
|
46798
47148
|
return (i, j) => {
|
|
46799
47149
|
const position = { sheetId, col: i + col, row: j + row };
|
|
46800
|
-
|
|
46801
|
-
this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
|
|
47150
|
+
this.spreadingRelations.addRelation({ resultPosition: position, arrayFormulaPosition });
|
|
46802
47151
|
};
|
|
46803
47152
|
}
|
|
46804
|
-
checkCollision(
|
|
46805
|
-
const
|
|
47153
|
+
checkCollision(formulaPosition) {
|
|
47154
|
+
const { sheetId, col, row } = formulaPosition;
|
|
46806
47155
|
return (i, j) => {
|
|
46807
47156
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
46808
47157
|
const rawCell = this.getters.getCell(position);
|
|
46809
47158
|
if (rawCell?.content ||
|
|
46810
47159
|
this.getters.getEvaluatedCell(position).type !== CellValueType.empty) {
|
|
46811
|
-
this.blockedArrayFormulas.add(
|
|
47160
|
+
this.blockedArrayFormulas.add(formulaPosition);
|
|
46812
47161
|
throw new EvaluationError(_t("Array result was not expanded because it would overwrite data in %s.", toXC(position.col, position.row)));
|
|
46813
47162
|
}
|
|
46814
|
-
this.blockedArrayFormulas.delete(
|
|
47163
|
+
this.blockedArrayFormulas.delete(formulaPosition);
|
|
46815
47164
|
};
|
|
46816
47165
|
}
|
|
46817
47166
|
spreadValues({ sheetId, col, row }, matrixResult) {
|
|
@@ -46819,19 +47168,18 @@ class Evaluator {
|
|
|
46819
47168
|
const position = { sheetId, col: i + col, row: j + row };
|
|
46820
47169
|
const cell = this.getters.getCell(position);
|
|
46821
47170
|
const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
|
|
46822
|
-
|
|
46823
|
-
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
47171
|
+
this.evaluatedCells.set(position, evaluatedCell);
|
|
46824
47172
|
// check if formula dependencies present in the spread zone
|
|
46825
47173
|
// if so, they need to be recomputed
|
|
46826
|
-
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([
|
|
47174
|
+
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
|
|
46827
47175
|
};
|
|
46828
47176
|
}
|
|
46829
|
-
invalidateSpreading(
|
|
46830
|
-
if (!this.spreadingRelations.isArrayFormula(
|
|
47177
|
+
invalidateSpreading(position) {
|
|
47178
|
+
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
46831
47179
|
return;
|
|
46832
47180
|
}
|
|
46833
|
-
for (const child of this.spreadingRelations.
|
|
46834
|
-
const content = this.getCell(child)?.content;
|
|
47181
|
+
for (const child of this.spreadingRelations.getArrayResultPositions(position)) {
|
|
47182
|
+
const content = this.getters.getCell(child)?.content;
|
|
46835
47183
|
if (content) {
|
|
46836
47184
|
// there's no point at re-evaluating overlapping array formulas,
|
|
46837
47185
|
// there's still a collision
|
|
@@ -46841,28 +47189,25 @@ class Evaluator {
|
|
|
46841
47189
|
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
46842
47190
|
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
|
|
46843
47191
|
}
|
|
46844
|
-
this.spreadingRelations.removeNode(
|
|
47192
|
+
this.spreadingRelations.removeNode(position);
|
|
46845
47193
|
}
|
|
46846
47194
|
// ----------------------------------------------------------
|
|
46847
47195
|
// COMMON FUNCTIONALITY
|
|
46848
47196
|
// ----------------------------------------------------------
|
|
46849
|
-
getDirectDependencies(
|
|
46850
|
-
const cell = this.getCell(
|
|
47197
|
+
getDirectDependencies(position) {
|
|
47198
|
+
const cell = this.getters.getCell(position);
|
|
46851
47199
|
if (!cell?.isFormula) {
|
|
46852
47200
|
return [];
|
|
46853
47201
|
}
|
|
46854
47202
|
return cell.compiledFormula.dependencies;
|
|
46855
47203
|
}
|
|
46856
|
-
getCellsDependingOn(
|
|
47204
|
+
getCellsDependingOn(positions) {
|
|
46857
47205
|
const ranges = [];
|
|
46858
|
-
for (const
|
|
46859
|
-
ranges.push(
|
|
47206
|
+
for (const position of positions) {
|
|
47207
|
+
ranges.push({ sheetId: position.sheetId, zone: positionToZone(position) });
|
|
46860
47208
|
}
|
|
46861
47209
|
return this.formulaDependencies().getCellsDependingOn(ranges);
|
|
46862
47210
|
}
|
|
46863
|
-
getCell(positionId) {
|
|
46864
|
-
return this.getters.getCell(this.encoder.decode(positionId));
|
|
46865
|
-
}
|
|
46866
47211
|
}
|
|
46867
47212
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
46868
47213
|
for (let i = 0; i < nbColumns; ++i) {
|
|
@@ -46888,96 +47233,17 @@ function nullValueToZeroValue(fPayload) {
|
|
|
46888
47233
|
}
|
|
46889
47234
|
return fPayload;
|
|
46890
47235
|
}
|
|
46891
|
-
/**
|
|
46892
|
-
* Encode (and decode) cell positions { sheetId, col, row }
|
|
46893
|
-
* to a single integer.
|
|
46894
|
-
*
|
|
46895
|
-
* `col` and `row` values are encoded on 21 bits each (max 2^21 = 2_097_152),
|
|
46896
|
-
* An incremental integer id is assigned to each different sheet id, starting at 0.
|
|
46897
|
-
*
|
|
46898
|
-
* e.g.
|
|
46899
|
-
* Given { col: 10, row: 4, sheetId: "abcde" }
|
|
46900
|
-
* we have:
|
|
46901
|
-
* - row "4" encoded on 21 bits: 000000000000000000100
|
|
46902
|
-
* - col "10" encoded on 21 bits: 000000000000000001010
|
|
46903
|
-
* - sheetId: let's say it's the 4th sheetId met, encoded to: 11
|
|
46904
|
-
*
|
|
46905
|
-
* The final encoded value is found by concatenating the 3 bit sequences:
|
|
46906
|
-
*
|
|
46907
|
-
* sheetId: 11
|
|
46908
|
-
* col: 000000000000000001010
|
|
46909
|
-
* row: 000000000000000000100
|
|
46910
|
-
* => 11000000000000000001010000000000000000000100
|
|
46911
|
-
*
|
|
46912
|
-
* this binary sequence is the integer 13194160504836
|
|
46913
|
-
*/
|
|
46914
|
-
class PositionBitsEncoder {
|
|
46915
|
-
sheetMapping = {};
|
|
46916
|
-
inverseSheetMapping = new Map();
|
|
46917
|
-
constructor() {
|
|
46918
|
-
try {
|
|
46919
|
-
// @ts-ignore
|
|
46920
|
-
o_spreadsheet.__DEBUG__ = o_spreadsheet.__DEBUG__ || {};
|
|
46921
|
-
// @ts-ignore
|
|
46922
|
-
o_spreadsheet.__DEBUG__.decodePosition = this.decode.bind(this);
|
|
46923
|
-
// @ts-ignore
|
|
46924
|
-
o_spreadsheet.__DEBUG__.encodePosition = this.encode.bind(this);
|
|
46925
|
-
}
|
|
46926
|
-
catch (error) { }
|
|
46927
|
-
}
|
|
46928
|
-
/**
|
|
46929
|
-
* Encode a cell position to a single integer.
|
|
46930
|
-
*/
|
|
46931
|
-
encode({ sheetId, col, row }) {
|
|
46932
|
-
return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
|
|
46933
|
-
}
|
|
46934
|
-
encodeBoundingBox({ sheetId, zone }) {
|
|
46935
|
-
const positions = [];
|
|
46936
|
-
forEachPositionsInZone(zone, (col, row) => {
|
|
46937
|
-
positions.push(this.encode({ sheetId, col, row }));
|
|
46938
|
-
});
|
|
46939
|
-
return positions;
|
|
46940
|
-
}
|
|
46941
|
-
decode(id) {
|
|
46942
|
-
// keep only the last 21 bits by AND-ing the bit sequence with 21 ones
|
|
46943
|
-
const row = Number(id & 2097151n);
|
|
46944
|
-
const col = Number((id >> 21n) & 2097151n);
|
|
46945
|
-
const sheetId = this.decodeSheet(id >> 42n);
|
|
46946
|
-
return { sheetId, col, row };
|
|
46947
|
-
}
|
|
46948
|
-
decodeToBoundingBox(id) {
|
|
46949
|
-
const { sheetId, col, row } = this.decode(id);
|
|
46950
|
-
return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
|
|
46951
|
-
}
|
|
46952
|
-
encodeSheet(sheetId) {
|
|
46953
|
-
const sheetKey = this.sheetMapping[sheetId];
|
|
46954
|
-
if (sheetKey === undefined) {
|
|
46955
|
-
const newSheetKey = BigInt(Object.keys(this.sheetMapping).length);
|
|
46956
|
-
this.sheetMapping[sheetId] = newSheetKey;
|
|
46957
|
-
this.inverseSheetMapping.set(newSheetKey, sheetId);
|
|
46958
|
-
return newSheetKey;
|
|
46959
|
-
}
|
|
46960
|
-
return sheetKey;
|
|
46961
|
-
}
|
|
46962
|
-
decodeSheet(sheetKey) {
|
|
46963
|
-
const sheetId = this.inverseSheetMapping.get(sheetKey);
|
|
46964
|
-
if (sheetId === undefined) {
|
|
46965
|
-
throw new Error("Sheet id not found");
|
|
46966
|
-
}
|
|
46967
|
-
return sheetId;
|
|
46968
|
-
}
|
|
46969
|
-
}
|
|
46970
47236
|
function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
|
|
46971
|
-
compilationParams
|
|
47237
|
+
compilationParams.evalContext.__originCellXC = lazy(() => {
|
|
46972
47238
|
if (!cellId) {
|
|
46973
47239
|
return undefined;
|
|
46974
47240
|
}
|
|
46975
47241
|
// compute the value lazily for performance reasons
|
|
46976
|
-
const position = compilationParams
|
|
47242
|
+
const position = compilationParams.evalContext.getters.getCellPosition(cellId);
|
|
46977
47243
|
return toXC(position.col, position.row);
|
|
46978
47244
|
});
|
|
46979
|
-
compilationParams
|
|
46980
|
-
return compiledFormula.execute(compiledFormula.dependencies,
|
|
47245
|
+
compilationParams.evalContext.__originSheetId = sheetId;
|
|
47246
|
+
return compiledFormula.execute(compiledFormula.dependencies, compilationParams.referenceDenormalizer, compilationParams.ensureRange, compilationParams.evalContext);
|
|
46981
47247
|
}
|
|
46982
47248
|
|
|
46983
47249
|
//#region
|
|
@@ -47084,6 +47350,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47084
47350
|
"getEvaluatedCellsInZone",
|
|
47085
47351
|
"getSpreadPositionsOf",
|
|
47086
47352
|
"getArrayFormulaSpreadingOn",
|
|
47353
|
+
"isEmpty",
|
|
47087
47354
|
];
|
|
47088
47355
|
shouldRebuildDependenciesGraph = true;
|
|
47089
47356
|
evaluator;
|
|
@@ -47112,6 +47379,10 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47112
47379
|
this.evaluator.updateDependencies(cmd);
|
|
47113
47380
|
}
|
|
47114
47381
|
break;
|
|
47382
|
+
case "DUPLICATE_SHEET":
|
|
47383
|
+
case "CREATE_SHEET":
|
|
47384
|
+
this.shouldRebuildDependenciesGraph = true;
|
|
47385
|
+
break;
|
|
47115
47386
|
case "EVALUATE_CELLS":
|
|
47116
47387
|
this.evaluator.evaluateAllCells();
|
|
47117
47388
|
break;
|
|
@@ -47189,6 +47460,14 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47189
47460
|
getArrayFormulaSpreadingOn(position) {
|
|
47190
47461
|
return this.evaluator.getArrayFormulaSpreadingOn(position);
|
|
47191
47462
|
}
|
|
47463
|
+
/**
|
|
47464
|
+
* Check if a zone only contains empty cells
|
|
47465
|
+
*/
|
|
47466
|
+
isEmpty(sheetId, zone) {
|
|
47467
|
+
return positions(zone)
|
|
47468
|
+
.map(({ col, row }) => this.getEvaluatedCell({ sheetId, col, row }))
|
|
47469
|
+
.every((cell) => cell.type === CellValueType.empty);
|
|
47470
|
+
}
|
|
47192
47471
|
// ---------------------------------------------------------------------------
|
|
47193
47472
|
// Export
|
|
47194
47473
|
// ---------------------------------------------------------------------------
|
|
@@ -47306,12 +47585,13 @@ function colorDistance(color1, color2) {
|
|
|
47306
47585
|
*/
|
|
47307
47586
|
class CustomColorsPlugin extends UIPlugin {
|
|
47308
47587
|
customColors = {};
|
|
47309
|
-
|
|
47310
|
-
shouldUpdateColors = false;
|
|
47588
|
+
shouldUpdateColors = true;
|
|
47311
47589
|
static getters = ["getCustomColors"];
|
|
47312
47590
|
constructor(config) {
|
|
47313
47591
|
super(config);
|
|
47314
|
-
|
|
47592
|
+
for (const color of config.customColors ?? []) {
|
|
47593
|
+
this.tryToAddColor(color);
|
|
47594
|
+
}
|
|
47315
47595
|
}
|
|
47316
47596
|
handle(cmd) {
|
|
47317
47597
|
switch (cmd.type) {
|
|
@@ -47322,31 +47602,29 @@ class CustomColorsPlugin extends UIPlugin {
|
|
|
47322
47602
|
case "SET_BORDER":
|
|
47323
47603
|
case "SET_ZONE_BORDERS":
|
|
47324
47604
|
case "SET_FORMATTING":
|
|
47605
|
+
case "CREATE_TABLE":
|
|
47606
|
+
case "UPDATE_TABLE":
|
|
47325
47607
|
this.history.update("shouldUpdateColors", true);
|
|
47608
|
+
break;
|
|
47326
47609
|
}
|
|
47327
47610
|
}
|
|
47328
47611
|
finalize() {
|
|
47329
47612
|
if (this.shouldUpdateColors) {
|
|
47330
47613
|
this.history.update("shouldUpdateColors", false);
|
|
47331
|
-
for (const color of this.
|
|
47614
|
+
for (const color of this.computeCustomColors()) {
|
|
47332
47615
|
this.tryToAddColor(color);
|
|
47333
47616
|
}
|
|
47334
47617
|
}
|
|
47335
47618
|
}
|
|
47336
47619
|
getCustomColors() {
|
|
47337
|
-
|
|
47620
|
+
return sortWithClusters(Object.keys(this.customColors));
|
|
47621
|
+
}
|
|
47622
|
+
computeCustomColors() {
|
|
47623
|
+
let usedColors = [];
|
|
47338
47624
|
for (const sheetId of this.getters.getSheetIds()) {
|
|
47339
|
-
usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId));
|
|
47340
|
-
}
|
|
47341
|
-
return
|
|
47342
|
-
...new Set(
|
|
47343
|
-
// remove duplicates first to check validity on a reduced
|
|
47344
|
-
// set of colors, then normalize to HEX and remove duplicates
|
|
47345
|
-
// again
|
|
47346
|
-
[...new Set([...usedColors, ...Object.keys(this.customColors)])]
|
|
47347
|
-
.filter(isColorValid)
|
|
47348
|
-
.map(toHex)),
|
|
47349
|
-
]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
|
|
47625
|
+
usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId), this.getTableColors(sheetId));
|
|
47626
|
+
}
|
|
47627
|
+
return [...new Set([...usedColors])];
|
|
47350
47628
|
}
|
|
47351
47629
|
getColorsFromCells(sheetId) {
|
|
47352
47630
|
const cells = Object.values(this.getters.getCells(sheetId));
|
|
@@ -47408,7 +47686,43 @@ class CustomColorsPlugin extends UIPlugin {
|
|
|
47408
47686
|
}
|
|
47409
47687
|
return [...chartsColors];
|
|
47410
47688
|
}
|
|
47689
|
+
getTableColors(sheetId) {
|
|
47690
|
+
const tables = this.getters.getTables(sheetId);
|
|
47691
|
+
return tables.flatMap((table) => {
|
|
47692
|
+
const config = table.config;
|
|
47693
|
+
const style = TABLE_PRESETS[config.styleId];
|
|
47694
|
+
return [
|
|
47695
|
+
this.getTableStyleElementColors(style.wholeTable),
|
|
47696
|
+
config.numberOfHeaders > 0 ? this.getTableStyleElementColors(style.headerRow) : [],
|
|
47697
|
+
config.totalRow ? this.getTableStyleElementColors(style.totalRow) : [],
|
|
47698
|
+
config.bandedColumns ? this.getTableStyleElementColors(style.firstColumnStripe) : [],
|
|
47699
|
+
config.bandedColumns ? this.getTableStyleElementColors(style.secondColumnStripe) : [],
|
|
47700
|
+
config.bandedRows ? this.getTableStyleElementColors(style.firstRowStripe) : [],
|
|
47701
|
+
config.bandedRows ? this.getTableStyleElementColors(style.secondRowStripe) : [],
|
|
47702
|
+
config.firstColumn ? this.getTableStyleElementColors(style.firstColumn) : [],
|
|
47703
|
+
config.lastColumn ? this.getTableStyleElementColors(style.lastColumn) : [],
|
|
47704
|
+
].flat();
|
|
47705
|
+
});
|
|
47706
|
+
}
|
|
47707
|
+
getTableStyleElementColors(element) {
|
|
47708
|
+
if (!element) {
|
|
47709
|
+
return [];
|
|
47710
|
+
}
|
|
47711
|
+
return [
|
|
47712
|
+
element.style?.fillColor,
|
|
47713
|
+
element.style?.textColor,
|
|
47714
|
+
element.border?.bottom?.color,
|
|
47715
|
+
element.border?.top?.color,
|
|
47716
|
+
element.border?.left?.color,
|
|
47717
|
+
element.border?.right?.color,
|
|
47718
|
+
element.border?.horizontal?.color,
|
|
47719
|
+
element.border?.vertical?.color,
|
|
47720
|
+
].filter(isDefined$1);
|
|
47721
|
+
}
|
|
47411
47722
|
tryToAddColor(color) {
|
|
47723
|
+
if (!isColorValid(color)) {
|
|
47724
|
+
return;
|
|
47725
|
+
}
|
|
47412
47726
|
const formattedColor = toHex(color);
|
|
47413
47727
|
if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
|
|
47414
47728
|
this.history.update("customColors", formattedColor, true);
|
|
@@ -47832,8 +48146,6 @@ const VALID_RESULT = { isValid: true };
|
|
|
47832
48146
|
class EvaluationDataValidationPlugin extends UIPlugin {
|
|
47833
48147
|
static getters = [
|
|
47834
48148
|
"getDataValidationInvalidCriterionValueMessage",
|
|
47835
|
-
"getDataValidationCheckBoxCellPositions",
|
|
47836
|
-
"getDataValidationListCellsPositions",
|
|
47837
48149
|
"getInvalidDataValidationMessage",
|
|
47838
48150
|
"getValidationResultForCellValue",
|
|
47839
48151
|
"isCellValidCheckbox",
|
|
@@ -47911,19 +48223,6 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
47911
48223
|
const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
|
|
47912
48224
|
return error ? { error, rule, isValid: false } : VALID_RESULT;
|
|
47913
48225
|
}
|
|
47914
|
-
getDataValidationCheckBoxCellPositions() {
|
|
47915
|
-
const rules = this.getters
|
|
47916
|
-
.getDataValidationRules(this.getters.getActiveSheetId())
|
|
47917
|
-
.filter((rule) => rule.criterion.type === "isBoolean");
|
|
47918
|
-
return getCellPositionsInRanges(rules.map((rule) => rule.ranges).flat()).filter((position) => this.isCellValidCheckbox(position));
|
|
47919
|
-
}
|
|
47920
|
-
getDataValidationListCellsPositions() {
|
|
47921
|
-
const rules = this.getters
|
|
47922
|
-
.getDataValidationRules(this.getters.getActiveSheetId())
|
|
47923
|
-
.filter((rule) => (rule.criterion.type === "isValueInList" || rule.criterion.type === "isValueInRange") &&
|
|
47924
|
-
rule.criterion.displayStyle === "arrow");
|
|
47925
|
-
return getCellPositionsInRanges(rules.map((rule) => rule.ranges).flat());
|
|
47926
|
-
}
|
|
47927
48226
|
getValidationResultForCell(cellPosition) {
|
|
47928
48227
|
const { col, row, sheetId } = cellPosition;
|
|
47929
48228
|
if (!this.validationResults[sheetId]) {
|
|
@@ -48380,6 +48679,23 @@ class AutofillPlugin extends UIPlugin {
|
|
|
48380
48679
|
* autofiller
|
|
48381
48680
|
*/
|
|
48382
48681
|
autofillAuto() {
|
|
48682
|
+
const activePosition = this.getters.getActivePosition();
|
|
48683
|
+
const table = this.getters.getTable(activePosition);
|
|
48684
|
+
let autofillRow = table ? table.range.zone.bottom : this.getAutofillAutoLastRow();
|
|
48685
|
+
// Stop autofill at the next non-empty cell
|
|
48686
|
+
const selection = this.getters.getSelectedZone();
|
|
48687
|
+
for (let row = selection.bottom + 1; row <= autofillRow; row++) {
|
|
48688
|
+
if (this.getters.getEvaluatedCell({ ...activePosition, row }).type !== CellValueType.empty) {
|
|
48689
|
+
autofillRow = row - 1;
|
|
48690
|
+
break;
|
|
48691
|
+
}
|
|
48692
|
+
}
|
|
48693
|
+
if (autofillRow > selection.bottom) {
|
|
48694
|
+
this.select(activePosition.col, autofillRow);
|
|
48695
|
+
this.autofill(true);
|
|
48696
|
+
}
|
|
48697
|
+
}
|
|
48698
|
+
getAutofillAutoLastRow() {
|
|
48383
48699
|
const zone = this.getters.getSelectedZone();
|
|
48384
48700
|
const sheetId = this.getters.getActiveSheetId();
|
|
48385
48701
|
let col = zone.left;
|
|
@@ -48403,10 +48719,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
48403
48719
|
}
|
|
48404
48720
|
}
|
|
48405
48721
|
}
|
|
48406
|
-
|
|
48407
|
-
this.select(zone.left, row - 1);
|
|
48408
|
-
this.autofill(true);
|
|
48409
|
-
}
|
|
48722
|
+
return row - 1;
|
|
48410
48723
|
}
|
|
48411
48724
|
/**
|
|
48412
48725
|
* Generate the next cell
|
|
@@ -49948,7 +50261,6 @@ class DataCleanupPlugin extends UIPlugin {
|
|
|
49948
50261
|
* the search with a new value.
|
|
49949
50262
|
*/
|
|
49950
50263
|
class FindAndReplacePlugin extends UIPlugin {
|
|
49951
|
-
static layers = ["Search"];
|
|
49952
50264
|
static getters = [];
|
|
49953
50265
|
handle(cmd) {
|
|
49954
50266
|
switch (cmd.type) {
|
|
@@ -51018,6 +51330,51 @@ class SplitToColumnsPlugin extends UIPlugin {
|
|
|
51018
51330
|
}
|
|
51019
51331
|
}
|
|
51020
51332
|
|
|
51333
|
+
class TableAutofillPlugin extends UIPlugin {
|
|
51334
|
+
handle(cmd) {
|
|
51335
|
+
switch (cmd.type) {
|
|
51336
|
+
case "AUTOFILL_TABLE_COLUMN":
|
|
51337
|
+
const table = this.getters.getTable(cmd);
|
|
51338
|
+
const cell = this.getters.getCell(cmd);
|
|
51339
|
+
if (!table || !table.config.automaticAutofill || !cell?.isFormula)
|
|
51340
|
+
return;
|
|
51341
|
+
const { col, row } = cmd;
|
|
51342
|
+
const tableContentZone = getTableContentZone(table.range.zone, table.config);
|
|
51343
|
+
if (tableContentZone && isInside(col, row, tableContentZone)) {
|
|
51344
|
+
this.autofillTableZone(cmd, tableContentZone);
|
|
51345
|
+
}
|
|
51346
|
+
break;
|
|
51347
|
+
}
|
|
51348
|
+
}
|
|
51349
|
+
autofillTableZone(autofillSource, zone) {
|
|
51350
|
+
if (zone.top === zone.bottom) {
|
|
51351
|
+
return;
|
|
51352
|
+
}
|
|
51353
|
+
const { col, row, sheetId } = autofillSource;
|
|
51354
|
+
for (let r = zone.top; r <= zone.bottom; r++) {
|
|
51355
|
+
if (r === row) {
|
|
51356
|
+
continue;
|
|
51357
|
+
}
|
|
51358
|
+
if (this.getters.getEvaluatedCell({ col, row: r, sheetId }).type !== CellValueType.empty) {
|
|
51359
|
+
return;
|
|
51360
|
+
}
|
|
51361
|
+
}
|
|
51362
|
+
// TODO: seems odd that autofill a table column have side effects on the selection. Autofill commands should be
|
|
51363
|
+
// refactored to take the selection as a parameter
|
|
51364
|
+
const oldSelection = {
|
|
51365
|
+
zone: this.getters.getSelectedZone(),
|
|
51366
|
+
cell: this.getters.getActivePosition(),
|
|
51367
|
+
};
|
|
51368
|
+
this.selection.selectCell(col, row);
|
|
51369
|
+
this.dispatch("AUTOFILL_SELECT", { col, row: zone.bottom });
|
|
51370
|
+
this.dispatch("AUTOFILL");
|
|
51371
|
+
this.selection.selectCell(col, row);
|
|
51372
|
+
this.dispatch("AUTOFILL_SELECT", { col, row: zone.top });
|
|
51373
|
+
this.dispatch("AUTOFILL");
|
|
51374
|
+
this.selection.selectZone(oldSelection);
|
|
51375
|
+
}
|
|
51376
|
+
}
|
|
51377
|
+
|
|
51021
51378
|
class TableStylePlugin extends UIPlugin {
|
|
51022
51379
|
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51023
51380
|
tableStyles = {};
|
|
@@ -52885,6 +53242,8 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
52885
53242
|
"getEdgeScrollRow",
|
|
52886
53243
|
"getVisibleFigures",
|
|
52887
53244
|
"getVisibleRect",
|
|
53245
|
+
"getVisibleRectWithoutHeaders",
|
|
53246
|
+
"getVisibleCellPositions",
|
|
52888
53247
|
"getColRowOffsetInViewport",
|
|
52889
53248
|
"getMainViewportCoordinates",
|
|
52890
53249
|
"getActiveSheetScrollInfo",
|
|
@@ -53130,6 +53489,26 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53130
53489
|
const viewports = this.getSubViewports(sheetId);
|
|
53131
53490
|
return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
|
|
53132
53491
|
}
|
|
53492
|
+
/**
|
|
53493
|
+
* Get the positions of all the cells that are visible in the viewport, taking merges into account.
|
|
53494
|
+
*/
|
|
53495
|
+
getVisibleCellPositions() {
|
|
53496
|
+
const visibleCols = this.getSheetViewVisibleCols();
|
|
53497
|
+
const visibleRows = this.getSheetViewVisibleRows();
|
|
53498
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
53499
|
+
const positions = [];
|
|
53500
|
+
for (const col of visibleCols) {
|
|
53501
|
+
for (const row of visibleRows) {
|
|
53502
|
+
const position = { sheetId, col, row };
|
|
53503
|
+
const mainPosition = this.getters.getMainCellPosition(position);
|
|
53504
|
+
if (mainPosition.row !== row || mainPosition.col !== col) {
|
|
53505
|
+
continue;
|
|
53506
|
+
}
|
|
53507
|
+
positions.push(position);
|
|
53508
|
+
}
|
|
53509
|
+
}
|
|
53510
|
+
return positions;
|
|
53511
|
+
}
|
|
53133
53512
|
/**
|
|
53134
53513
|
* Return the main viewport maximum size relative to the client size.
|
|
53135
53514
|
*/
|
|
@@ -53249,6 +53628,13 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53249
53628
|
* Computes the coordinates and size to draw the zone on the canvas
|
|
53250
53629
|
*/
|
|
53251
53630
|
getVisibleRect(zone) {
|
|
53631
|
+
const rect = this.getVisibleRectWithoutHeaders(zone);
|
|
53632
|
+
return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
|
|
53633
|
+
}
|
|
53634
|
+
/**
|
|
53635
|
+
* Computes the coordinates and size to draw the zone without taking the grid offset into account
|
|
53636
|
+
*/
|
|
53637
|
+
getVisibleRectWithoutHeaders(zone) {
|
|
53252
53638
|
const sheetId = this.getters.getActiveSheetId();
|
|
53253
53639
|
const viewportRects = this.getSubViewports(sheetId)
|
|
53254
53640
|
.map((viewport) => viewport.getRect(zone))
|
|
@@ -53260,12 +53646,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53260
53646
|
const y = Math.min(...viewportRects.map((rect) => rect.y));
|
|
53261
53647
|
const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
|
|
53262
53648
|
const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
|
|
53263
|
-
return {
|
|
53264
|
-
x: x + this.gridOffsetX,
|
|
53265
|
-
y: y + this.gridOffsetY,
|
|
53266
|
-
width,
|
|
53267
|
-
height,
|
|
53268
|
-
};
|
|
53649
|
+
return { x, y, width, height };
|
|
53269
53650
|
}
|
|
53270
53651
|
/**
|
|
53271
53652
|
* Returns the position of the MainViewport relatively to the start of the grid (without headers)
|
|
@@ -53519,13 +53900,8 @@ class HeaderPositionsUIPlugin extends UIPlugin {
|
|
|
53519
53900
|
}
|
|
53520
53901
|
break;
|
|
53521
53902
|
case "UPDATE_CELL":
|
|
53522
|
-
|
|
53523
|
-
|
|
53524
|
-
this.isDirty = true;
|
|
53525
|
-
}
|
|
53526
|
-
else {
|
|
53527
|
-
this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
|
|
53528
|
-
}
|
|
53903
|
+
this.headerPositions = {};
|
|
53904
|
+
this.isDirty = true;
|
|
53529
53905
|
break;
|
|
53530
53906
|
case "UPDATE_FILTER":
|
|
53531
53907
|
case "UPDATE_TABLE":
|
|
@@ -53648,7 +54024,8 @@ const featurePluginRegistry = new Registry()
|
|
|
53648
54024
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
53649
54025
|
.add("collaborative", CollaborativePlugin)
|
|
53650
54026
|
.add("history", HistoryPlugin)
|
|
53651
|
-
.add("data_cleanup", DataCleanupPlugin)
|
|
54027
|
+
.add("data_cleanup", DataCleanupPlugin)
|
|
54028
|
+
.add("table_autofill", TableAutofillPlugin);
|
|
53652
54029
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
53653
54030
|
const statefulUIPluginRegistry = new Registry()
|
|
53654
54031
|
.add("selection", GridSelectionPlugin)
|
|
@@ -53991,6 +54368,9 @@ class BottomBarSheet extends owl.Component {
|
|
|
53991
54368
|
this.scrollToSheet();
|
|
53992
54369
|
}
|
|
53993
54370
|
onDblClick() {
|
|
54371
|
+
if (this.env.model.getters.isReadonly()) {
|
|
54372
|
+
return;
|
|
54373
|
+
}
|
|
53994
54374
|
this.startEdition();
|
|
53995
54375
|
}
|
|
53996
54376
|
onKeyDown(ev) {
|
|
@@ -55947,7 +56327,6 @@ class Spreadsheet extends owl.Component {
|
|
|
55947
56327
|
}
|
|
55948
56328
|
}, () => [this.env.model.getters.getActiveSheetId()]);
|
|
55949
56329
|
owl.useExternalListener(window, "resize", () => this.render(true));
|
|
55950
|
-
owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
|
|
55951
56330
|
this.bindModelEvents();
|
|
55952
56331
|
owl.onWillUpdateProps((nextProps) => {
|
|
55953
56332
|
if (nextProps.model !== this.props.model) {
|
|
@@ -59788,6 +60167,6 @@ exports.tokenColors = tokenColors;
|
|
|
59788
60167
|
exports.tokenize = tokenize;
|
|
59789
60168
|
|
|
59790
60169
|
|
|
59791
|
-
__info__.version = "17.2.0
|
|
59792
|
-
__info__.date = "2024-
|
|
59793
|
-
__info__.hash = "
|
|
60170
|
+
__info__.version = "17.2.0";
|
|
60171
|
+
__info__.date = "2024-03-20T08:12:44.398Z";
|
|
60172
|
+
__info__.hash = "1869c24";
|