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