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