@odoo/o-spreadsheet 17.0.4 → 17.0.6
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 +970 -220
- package/dist/o-spreadsheet.d.ts +24 -21
- package/dist/o-spreadsheet.esm.js +970 -220
- package/dist/o-spreadsheet.iife.js +970 -220
- package/dist/o-spreadsheet.iife.min.js +274 -276
- package/dist/o_spreadsheet.xml +4 -4
- package/package.json +6 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.0.
|
|
6
|
-
* @date
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.6
|
|
6
|
+
* @date 2024-01-04T12:23:10.357Z
|
|
7
|
+
* @hash 310ca57
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
|
|
@@ -329,13 +329,6 @@ const FONT_SIZES = [6, 7, 8, 9, 10, 11, 12, 14, 18, 24, 36];
|
|
|
329
329
|
//------------------------------------------------------------------------------
|
|
330
330
|
// Miscellaneous
|
|
331
331
|
//------------------------------------------------------------------------------
|
|
332
|
-
/**
|
|
333
|
-
* Stringify an object, like JSON.stringify, except that the first level of keys
|
|
334
|
-
* is ordered.
|
|
335
|
-
*/
|
|
336
|
-
function stringify(obj) {
|
|
337
|
-
return JSON.stringify(obj, Object.keys(obj).sort());
|
|
338
|
-
}
|
|
339
332
|
/**
|
|
340
333
|
* Remove quotes from a quoted string
|
|
341
334
|
* ```js
|
|
@@ -558,7 +551,7 @@ function isObjectEmptyRecursive(argument) {
|
|
|
558
551
|
*/
|
|
559
552
|
function getItemId(item, itemsDic) {
|
|
560
553
|
for (let [key, value] of Object.entries(itemsDic)) {
|
|
561
|
-
if (
|
|
554
|
+
if (deepEquals(value, item)) {
|
|
562
555
|
return parseInt(key, 10);
|
|
563
556
|
}
|
|
564
557
|
}
|
|
@@ -3387,8 +3380,10 @@ function convertInternalFormatToFormat(internalFormat) {
|
|
|
3387
3380
|
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
3388
3381
|
// Same as above, but matches the exact string (nothing before or after)
|
|
3389
3382
|
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
3390
|
-
/** Reference of a column header (eg. A, AB) */
|
|
3391
|
-
const colHeader = new RegExp(
|
|
3383
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
3384
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
3385
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
3386
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
3392
3387
|
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
3393
3388
|
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
3394
3389
|
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
@@ -3418,6 +3413,9 @@ function isRowReference(xc) {
|
|
|
3418
3413
|
function isColHeader(str) {
|
|
3419
3414
|
return colHeader.test(str);
|
|
3420
3415
|
}
|
|
3416
|
+
function isRowHeader(str) {
|
|
3417
|
+
return rowHeader.test(str);
|
|
3418
|
+
}
|
|
3421
3419
|
/**
|
|
3422
3420
|
* Return true if the given xc is the reference of a single cell,
|
|
3423
3421
|
* without any specified sheet (e.g. A1)
|
|
@@ -4101,18 +4099,10 @@ class RangeImpl {
|
|
|
4101
4099
|
if (isFullCol) {
|
|
4102
4100
|
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4103
4101
|
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4104
|
-
if (zone.left === zone.right) {
|
|
4105
|
-
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4106
|
-
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4107
|
-
}
|
|
4108
4102
|
}
|
|
4109
4103
|
if (isFullRow) {
|
|
4110
4104
|
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4111
4105
|
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4112
|
-
if (zone.top === zone.bottom) {
|
|
4113
|
-
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4114
|
-
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4115
|
-
}
|
|
4116
4106
|
}
|
|
4117
4107
|
return parts;
|
|
4118
4108
|
}
|
|
@@ -4354,7 +4344,7 @@ function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
|
4354
4344
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4355
4345
|
if (!cell || !cell.content)
|
|
4356
4346
|
return DEFAULT_CELL_HEIGHT;
|
|
4357
|
-
const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4347
|
+
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4358
4348
|
const numberOfLines = cell.isFormula
|
|
4359
4349
|
? 1
|
|
4360
4350
|
: splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
|
|
@@ -5289,7 +5279,7 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
5289
5279
|
};
|
|
5290
5280
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
5291
5281
|
}
|
|
5292
|
-
const background = getters.
|
|
5282
|
+
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
5293
5283
|
const locale = getters.getLocale();
|
|
5294
5284
|
return {
|
|
5295
5285
|
title: _t(chart.title),
|
|
@@ -5298,7 +5288,7 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
5298
5288
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
5299
5289
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
5300
5290
|
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
5301
|
-
fontColor
|
|
5291
|
+
fontColor,
|
|
5302
5292
|
background,
|
|
5303
5293
|
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
5304
5294
|
? getters.getCellStyle({
|
|
@@ -6249,10 +6239,6 @@ css /* scss */ `
|
|
|
6249
6239
|
|
|
6250
6240
|
.o-error-tooltip-message {
|
|
6251
6241
|
overflow: hidden;
|
|
6252
|
-
display: -webkit-box; /* Limit to 3 lines */
|
|
6253
|
-
-webkit-line-clamp: 3;
|
|
6254
|
-
line-clamp: 3;
|
|
6255
|
-
-webkit-box-orient: vertical;
|
|
6256
6242
|
}
|
|
6257
6243
|
}
|
|
6258
6244
|
`;
|
|
@@ -6597,6 +6583,7 @@ const FilterMenuPopoverBuilder = {
|
|
|
6597
6583
|
},
|
|
6598
6584
|
};
|
|
6599
6585
|
|
|
6586
|
+
const macRegex = /Mac/i;
|
|
6600
6587
|
/**
|
|
6601
6588
|
* Return true if the event was triggered from
|
|
6602
6589
|
* a child element.
|
|
@@ -6635,6 +6622,17 @@ function* iterateChildren(el) {
|
|
|
6635
6622
|
function getOpenedMenus() {
|
|
6636
6623
|
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
6637
6624
|
}
|
|
6625
|
+
function isMacOS() {
|
|
6626
|
+
return Boolean(macRegex.test(navigator.userAgent));
|
|
6627
|
+
}
|
|
6628
|
+
/**
|
|
6629
|
+
* @param {KeyboardEvent | MouseEvent} ev
|
|
6630
|
+
* @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
|
|
6631
|
+
* On Mac, this is the "meta" or "command" key.
|
|
6632
|
+
*/
|
|
6633
|
+
function isCtrlKey(ev) {
|
|
6634
|
+
return isMacOS() ? ev.metaKey : ev.ctrlKey;
|
|
6635
|
+
}
|
|
6638
6636
|
|
|
6639
6637
|
/**
|
|
6640
6638
|
* Return the o-spreadsheet element position relative
|
|
@@ -8882,7 +8880,7 @@ function createGaugeChartRuntime(chart, getters) {
|
|
|
8882
8880
|
});
|
|
8883
8881
|
return {
|
|
8884
8882
|
chartJsConfig: config,
|
|
8885
|
-
background: getters.
|
|
8883
|
+
background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
|
|
8886
8884
|
};
|
|
8887
8885
|
}
|
|
8888
8886
|
|
|
@@ -8963,7 +8961,7 @@ function getFormatMinDisplayUnit(format) {
|
|
|
8963
8961
|
else if (format.includes("h") || format.includes("H")) {
|
|
8964
8962
|
return "hour";
|
|
8965
8963
|
}
|
|
8966
|
-
else if (format.includes("
|
|
8964
|
+
else if (format.includes("d")) {
|
|
8967
8965
|
return "day";
|
|
8968
8966
|
}
|
|
8969
8967
|
else if (format.includes("M")) {
|
|
@@ -21216,10 +21214,10 @@ const arrowMap = {
|
|
|
21216
21214
|
function updateSelectionWithArrowKeys(ev, selection) {
|
|
21217
21215
|
const direction = arrowMap[ev.key];
|
|
21218
21216
|
if (ev.shiftKey) {
|
|
21219
|
-
selection.resizeAnchorZone(direction, ev
|
|
21217
|
+
selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21220
21218
|
}
|
|
21221
21219
|
else {
|
|
21222
|
-
selection.moveAnchorCell(direction, ev
|
|
21220
|
+
selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21223
21221
|
}
|
|
21224
21222
|
}
|
|
21225
21223
|
|
|
@@ -28204,7 +28202,10 @@ class GridOverlay extends Component {
|
|
|
28204
28202
|
return;
|
|
28205
28203
|
}
|
|
28206
28204
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
28207
|
-
this.props.onCellClicked(col, row, {
|
|
28205
|
+
this.props.onCellClicked(col, row, {
|
|
28206
|
+
expandZone: ev.shiftKey,
|
|
28207
|
+
addZone: isCtrlKey(ev),
|
|
28208
|
+
});
|
|
28208
28209
|
}
|
|
28209
28210
|
onDoubleClick(ev) {
|
|
28210
28211
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
@@ -28279,6 +28280,7 @@ class AbstractResizer extends Component {
|
|
|
28279
28280
|
draggerShadowThickness: 0,
|
|
28280
28281
|
delta: 0,
|
|
28281
28282
|
base: 0,
|
|
28283
|
+
position: "before",
|
|
28282
28284
|
});
|
|
28283
28285
|
_computeHandleDisplay(ev) {
|
|
28284
28286
|
const position = this._getEvOffset(ev);
|
|
@@ -28403,11 +28405,13 @@ class AbstractResizer extends Component {
|
|
|
28403
28405
|
this.state.draggerLinePosition = dimensions.start;
|
|
28404
28406
|
this.state.draggerShadowPosition = dimensions.start;
|
|
28405
28407
|
this.state.base = elementIndex;
|
|
28408
|
+
this.state.position = "before";
|
|
28406
28409
|
}
|
|
28407
28410
|
else if (this._getSelectedZoneEnd() < elementIndex) {
|
|
28408
28411
|
this.state.draggerLinePosition = dimensions.end;
|
|
28409
28412
|
this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
|
|
28410
|
-
this.state.base = elementIndex
|
|
28413
|
+
this.state.base = elementIndex;
|
|
28414
|
+
this.state.position = "after";
|
|
28411
28415
|
}
|
|
28412
28416
|
else {
|
|
28413
28417
|
this.state.draggerLinePosition = startDimensions.start;
|
|
@@ -28431,7 +28435,7 @@ class AbstractResizer extends Component {
|
|
|
28431
28435
|
this._increaseSelection(index);
|
|
28432
28436
|
}
|
|
28433
28437
|
else {
|
|
28434
|
-
this._selectElement(index, ev
|
|
28438
|
+
this._selectElement(index, isCtrlKey(ev));
|
|
28435
28439
|
}
|
|
28436
28440
|
this.lastSelectedElementIndex = index;
|
|
28437
28441
|
const mouseMoveSelect = (col, row) => {
|
|
@@ -28589,13 +28593,14 @@ class ColResizer extends AbstractResizer {
|
|
|
28589
28593
|
dimension: "COL",
|
|
28590
28594
|
base: this.state.base,
|
|
28591
28595
|
elements,
|
|
28596
|
+
position: this.state.position,
|
|
28592
28597
|
});
|
|
28593
28598
|
if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
|
|
28594
28599
|
this.env.raiseError(MergeErrorMessage);
|
|
28595
28600
|
}
|
|
28596
28601
|
}
|
|
28597
|
-
_selectElement(index,
|
|
28598
|
-
this.env.model.selection.selectColumn(index,
|
|
28602
|
+
_selectElement(index, addDistinctHeader) {
|
|
28603
|
+
this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28599
28604
|
}
|
|
28600
28605
|
_increaseSelection(index) {
|
|
28601
28606
|
this.env.model.selection.selectColumn(index, "updateAnchor");
|
|
@@ -28762,13 +28767,14 @@ class RowResizer extends AbstractResizer {
|
|
|
28762
28767
|
dimension: "ROW",
|
|
28763
28768
|
base: this.state.base,
|
|
28764
28769
|
elements,
|
|
28770
|
+
position: this.state.position,
|
|
28765
28771
|
});
|
|
28766
28772
|
if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
|
|
28767
28773
|
this.env.raiseError(MergeErrorMessage);
|
|
28768
28774
|
}
|
|
28769
28775
|
}
|
|
28770
|
-
_selectElement(index,
|
|
28771
|
-
this.env.model.selection.selectRow(index,
|
|
28776
|
+
_selectElement(index, addDistinctHeader) {
|
|
28777
|
+
this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28772
28778
|
}
|
|
28773
28779
|
_increaseSelection(index) {
|
|
28774
28780
|
this.env.model.selection.selectRow(index, "updateAnchor");
|
|
@@ -28869,8 +28875,8 @@ function useWheelHandler(handler) {
|
|
|
28869
28875
|
return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
|
|
28870
28876
|
}
|
|
28871
28877
|
const onMouseWheel = (ev) => {
|
|
28872
|
-
const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
|
|
28873
|
-
const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
|
|
28878
|
+
const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
|
|
28879
|
+
const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
|
|
28874
28880
|
handler(deltaX, deltaY);
|
|
28875
28881
|
};
|
|
28876
28882
|
return onMouseWheel;
|
|
@@ -29570,17 +29576,17 @@ class Grid extends Component {
|
|
|
29570
29576
|
// ---------------------------------------------------------------------------
|
|
29571
29577
|
// Zone selection with mouse
|
|
29572
29578
|
// ---------------------------------------------------------------------------
|
|
29573
|
-
onCellClicked(col, row, {
|
|
29579
|
+
onCellClicked(col, row, { addZone, expandZone }) {
|
|
29574
29580
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
29575
29581
|
this.closeOpenedPopover();
|
|
29576
29582
|
}
|
|
29577
29583
|
if (this.env.model.getters.getEditionMode() === "editing") {
|
|
29578
29584
|
interactiveStopEdition(this.env);
|
|
29579
29585
|
}
|
|
29580
|
-
if (
|
|
29586
|
+
if (expandZone) {
|
|
29581
29587
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
29582
29588
|
}
|
|
29583
|
-
else if (
|
|
29589
|
+
else if (addZone) {
|
|
29584
29590
|
this.env.model.selection.addCellToSelection(col, row);
|
|
29585
29591
|
}
|
|
29586
29592
|
else {
|
|
@@ -29641,7 +29647,7 @@ class Grid extends Component {
|
|
|
29641
29647
|
onKeydown(ev) {
|
|
29642
29648
|
const eventKey = ev.key.toUpperCase();
|
|
29643
29649
|
let keyDownString = "";
|
|
29644
|
-
if (ev
|
|
29650
|
+
if (isCtrlKey(ev) && eventKey !== "CTRL")
|
|
29645
29651
|
keyDownString += "CTRL+";
|
|
29646
29652
|
if (ev.metaKey)
|
|
29647
29653
|
keyDownString += "CTRL+";
|
|
@@ -33324,7 +33330,7 @@ const machine = {
|
|
|
33324
33330
|
SPACE: goTo(State.RightRef),
|
|
33325
33331
|
NUMBER: goTo(State.Found),
|
|
33326
33332
|
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
33327
|
-
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
|
|
33333
|
+
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
|
|
33328
33334
|
},
|
|
33329
33335
|
[State.RightColumnRef]: {
|
|
33330
33336
|
SPACE: goTo(State.RightColumnRef),
|
|
@@ -33335,6 +33341,7 @@ const machine = {
|
|
|
33335
33341
|
SPACE: goTo(State.RightRowRef),
|
|
33336
33342
|
NUMBER: goTo(State.Found),
|
|
33337
33343
|
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
33344
|
+
SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
|
|
33338
33345
|
},
|
|
33339
33346
|
[State.Found]: {},
|
|
33340
33347
|
};
|
|
@@ -34531,7 +34538,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
34531
34538
|
*/
|
|
34532
34539
|
function getBorderId(border) {
|
|
34533
34540
|
for (let [key, value] of Object.entries(borders)) {
|
|
34534
|
-
if (
|
|
34541
|
+
if (deepEquals(value, border)) {
|
|
34535
34542
|
return parseInt(key, 10);
|
|
34536
34543
|
}
|
|
34537
34544
|
}
|
|
@@ -37316,7 +37323,16 @@ class HeaderVisibilityPlugin extends CorePlugin {
|
|
|
37316
37323
|
return consecutiveIndexes;
|
|
37317
37324
|
}
|
|
37318
37325
|
getAllVisibleHeaders(sheetId, dimension) {
|
|
37319
|
-
|
|
37326
|
+
const headers = range(0, this.getters.getNumberHeaders(sheetId, dimension));
|
|
37327
|
+
const foldedHeaders = [];
|
|
37328
|
+
this.getters.getHeaderGroups(sheetId, dimension).forEach((group) => {
|
|
37329
|
+
if (group.isFolded) {
|
|
37330
|
+
foldedHeaders.push(...range(group.start, group.end + 1));
|
|
37331
|
+
}
|
|
37332
|
+
});
|
|
37333
|
+
return headers.filter((i) => {
|
|
37334
|
+
return !this.hiddenHeaders[sheetId][dimension][i] && !foldedHeaders.includes(i);
|
|
37335
|
+
});
|
|
37320
37336
|
}
|
|
37321
37337
|
import(data) {
|
|
37322
37338
|
for (let sheet of data.sheets) {
|
|
@@ -39776,10 +39792,13 @@ class CompilationParametersBuilder {
|
|
|
39776
39792
|
if (evaluatedCell === undefined) {
|
|
39777
39793
|
return { value: null, format: this.getters.getCell(position)?.format };
|
|
39778
39794
|
}
|
|
39795
|
+
if (evaluatedCell.type === CellValueType.error) {
|
|
39796
|
+
throw evaluatedCell.error;
|
|
39797
|
+
}
|
|
39779
39798
|
return evaluatedCell;
|
|
39780
39799
|
}
|
|
39781
39800
|
getEvaluatedCellIfNotEmpty(position) {
|
|
39782
|
-
const evaluatedCell = this.
|
|
39801
|
+
const evaluatedCell = this.computeCell(position);
|
|
39783
39802
|
if (evaluatedCell.type === CellValueType.empty) {
|
|
39784
39803
|
const cell = this.getters.getCell(position);
|
|
39785
39804
|
if (!cell || (!cell.isFormula && cell.content === "")) {
|
|
@@ -39788,13 +39807,6 @@ class CompilationParametersBuilder {
|
|
|
39788
39807
|
}
|
|
39789
39808
|
return evaluatedCell;
|
|
39790
39809
|
}
|
|
39791
|
-
getEvaluatedCell(position) {
|
|
39792
|
-
const evaluatedCell = this.computeCell(position);
|
|
39793
|
-
if (evaluatedCell.type === CellValueType.error) {
|
|
39794
|
-
throw evaluatedCell.error;
|
|
39795
|
-
}
|
|
39796
|
-
return evaluatedCell;
|
|
39797
|
-
}
|
|
39798
39810
|
/**
|
|
39799
39811
|
* Return the values of the cell(s) used in reference, but always in the format of a range even
|
|
39800
39812
|
* if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
|
|
@@ -39817,7 +39829,11 @@ class CompilationParametersBuilder {
|
|
|
39817
39829
|
const { top, left, bottom, right } = zone;
|
|
39818
39830
|
const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
|
|
39819
39831
|
if (cacheKey in this.rangeCache) {
|
|
39820
|
-
|
|
39832
|
+
const result = this.rangeCache[cacheKey];
|
|
39833
|
+
if (result instanceof EvaluationError) {
|
|
39834
|
+
throw result;
|
|
39835
|
+
}
|
|
39836
|
+
return result;
|
|
39821
39837
|
}
|
|
39822
39838
|
const height = _zone.bottom - _zone.top + 1;
|
|
39823
39839
|
const width = _zone.right - _zone.left + 1;
|
|
@@ -39827,6 +39843,11 @@ class CompilationParametersBuilder {
|
|
|
39827
39843
|
const colIndex = col - _zone.left;
|
|
39828
39844
|
matrix[colIndex] = new Array(height);
|
|
39829
39845
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
39846
|
+
const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
|
|
39847
|
+
if (evaluatedCell?.type === CellValueType.error) {
|
|
39848
|
+
this.rangeCache[cacheKey] = evaluatedCell.error;
|
|
39849
|
+
throw evaluatedCell.error;
|
|
39850
|
+
}
|
|
39830
39851
|
const rowIndex = row - _zone.top;
|
|
39831
39852
|
matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
|
|
39832
39853
|
}
|
|
@@ -39836,56 +39857,758 @@ class CompilationParametersBuilder {
|
|
|
39836
39857
|
}
|
|
39837
39858
|
}
|
|
39838
39859
|
|
|
39860
|
+
function quickselect(arr, k, left, right, compare) {
|
|
39861
|
+
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
|
|
39862
|
+
}
|
|
39863
|
+
|
|
39864
|
+
function quickselectStep(arr, k, left, right, compare) {
|
|
39865
|
+
|
|
39866
|
+
while (right > left) {
|
|
39867
|
+
if (right - left > 600) {
|
|
39868
|
+
var n = right - left + 1;
|
|
39869
|
+
var m = k - left + 1;
|
|
39870
|
+
var z = Math.log(n);
|
|
39871
|
+
var s = 0.5 * Math.exp(2 * z / 3);
|
|
39872
|
+
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
|
|
39873
|
+
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
|
|
39874
|
+
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
|
|
39875
|
+
quickselectStep(arr, k, newLeft, newRight, compare);
|
|
39876
|
+
}
|
|
39877
|
+
|
|
39878
|
+
var t = arr[k];
|
|
39879
|
+
var i = left;
|
|
39880
|
+
var j = right;
|
|
39881
|
+
|
|
39882
|
+
swap(arr, left, k);
|
|
39883
|
+
if (compare(arr[right], t) > 0) swap(arr, left, right);
|
|
39884
|
+
|
|
39885
|
+
while (i < j) {
|
|
39886
|
+
swap(arr, i, j);
|
|
39887
|
+
i++;
|
|
39888
|
+
j--;
|
|
39889
|
+
while (compare(arr[i], t) < 0) i++;
|
|
39890
|
+
while (compare(arr[j], t) > 0) j--;
|
|
39891
|
+
}
|
|
39892
|
+
|
|
39893
|
+
if (compare(arr[left], t) === 0) swap(arr, left, j);
|
|
39894
|
+
else {
|
|
39895
|
+
j++;
|
|
39896
|
+
swap(arr, j, right);
|
|
39897
|
+
}
|
|
39898
|
+
|
|
39899
|
+
if (j <= k) left = j + 1;
|
|
39900
|
+
if (k <= j) right = j - 1;
|
|
39901
|
+
}
|
|
39902
|
+
}
|
|
39903
|
+
|
|
39904
|
+
function swap(arr, i, j) {
|
|
39905
|
+
var tmp = arr[i];
|
|
39906
|
+
arr[i] = arr[j];
|
|
39907
|
+
arr[j] = tmp;
|
|
39908
|
+
}
|
|
39909
|
+
|
|
39910
|
+
function defaultCompare(a, b) {
|
|
39911
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
39912
|
+
}
|
|
39913
|
+
|
|
39914
|
+
class RBush {
|
|
39915
|
+
constructor(maxEntries = 9) {
|
|
39916
|
+
// max entries in a node is 9 by default; min node fill is 40% for best performance
|
|
39917
|
+
this._maxEntries = Math.max(4, maxEntries);
|
|
39918
|
+
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
|
39919
|
+
this.clear();
|
|
39920
|
+
}
|
|
39921
|
+
|
|
39922
|
+
all() {
|
|
39923
|
+
return this._all(this.data, []);
|
|
39924
|
+
}
|
|
39925
|
+
|
|
39926
|
+
search(bbox) {
|
|
39927
|
+
let node = this.data;
|
|
39928
|
+
const result = [];
|
|
39929
|
+
|
|
39930
|
+
if (!intersects(bbox, node)) return result;
|
|
39931
|
+
|
|
39932
|
+
const toBBox = this.toBBox;
|
|
39933
|
+
const nodesToSearch = [];
|
|
39934
|
+
|
|
39935
|
+
while (node) {
|
|
39936
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
39937
|
+
const child = node.children[i];
|
|
39938
|
+
const childBBox = node.leaf ? toBBox(child) : child;
|
|
39939
|
+
|
|
39940
|
+
if (intersects(bbox, childBBox)) {
|
|
39941
|
+
if (node.leaf) result.push(child);
|
|
39942
|
+
else if (contains(bbox, childBBox)) this._all(child, result);
|
|
39943
|
+
else nodesToSearch.push(child);
|
|
39944
|
+
}
|
|
39945
|
+
}
|
|
39946
|
+
node = nodesToSearch.pop();
|
|
39947
|
+
}
|
|
39948
|
+
|
|
39949
|
+
return result;
|
|
39950
|
+
}
|
|
39951
|
+
|
|
39952
|
+
collides(bbox) {
|
|
39953
|
+
let node = this.data;
|
|
39954
|
+
|
|
39955
|
+
if (!intersects(bbox, node)) return false;
|
|
39956
|
+
|
|
39957
|
+
const nodesToSearch = [];
|
|
39958
|
+
while (node) {
|
|
39959
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
39960
|
+
const child = node.children[i];
|
|
39961
|
+
const childBBox = node.leaf ? this.toBBox(child) : child;
|
|
39962
|
+
|
|
39963
|
+
if (intersects(bbox, childBBox)) {
|
|
39964
|
+
if (node.leaf || contains(bbox, childBBox)) return true;
|
|
39965
|
+
nodesToSearch.push(child);
|
|
39966
|
+
}
|
|
39967
|
+
}
|
|
39968
|
+
node = nodesToSearch.pop();
|
|
39969
|
+
}
|
|
39970
|
+
|
|
39971
|
+
return false;
|
|
39972
|
+
}
|
|
39973
|
+
|
|
39974
|
+
load(data) {
|
|
39975
|
+
if (!(data && data.length)) return this;
|
|
39976
|
+
|
|
39977
|
+
if (data.length < this._minEntries) {
|
|
39978
|
+
for (let i = 0; i < data.length; i++) {
|
|
39979
|
+
this.insert(data[i]);
|
|
39980
|
+
}
|
|
39981
|
+
return this;
|
|
39982
|
+
}
|
|
39983
|
+
|
|
39984
|
+
// recursively build the tree with the given data from scratch using OMT algorithm
|
|
39985
|
+
let node = this._build(data.slice(), 0, data.length - 1, 0);
|
|
39986
|
+
|
|
39987
|
+
if (!this.data.children.length) {
|
|
39988
|
+
// save as is if tree is empty
|
|
39989
|
+
this.data = node;
|
|
39990
|
+
|
|
39991
|
+
} else if (this.data.height === node.height) {
|
|
39992
|
+
// split root if trees have the same height
|
|
39993
|
+
this._splitRoot(this.data, node);
|
|
39994
|
+
|
|
39995
|
+
} else {
|
|
39996
|
+
if (this.data.height < node.height) {
|
|
39997
|
+
// swap trees if inserted one is bigger
|
|
39998
|
+
const tmpNode = this.data;
|
|
39999
|
+
this.data = node;
|
|
40000
|
+
node = tmpNode;
|
|
40001
|
+
}
|
|
40002
|
+
|
|
40003
|
+
// insert the small tree into the large tree at appropriate level
|
|
40004
|
+
this._insert(node, this.data.height - node.height - 1, true);
|
|
40005
|
+
}
|
|
40006
|
+
|
|
40007
|
+
return this;
|
|
40008
|
+
}
|
|
40009
|
+
|
|
40010
|
+
insert(item) {
|
|
40011
|
+
if (item) this._insert(item, this.data.height - 1);
|
|
40012
|
+
return this;
|
|
40013
|
+
}
|
|
40014
|
+
|
|
40015
|
+
clear() {
|
|
40016
|
+
this.data = createNode([]);
|
|
40017
|
+
return this;
|
|
40018
|
+
}
|
|
40019
|
+
|
|
40020
|
+
remove(item, equalsFn) {
|
|
40021
|
+
if (!item) return this;
|
|
40022
|
+
|
|
40023
|
+
let node = this.data;
|
|
40024
|
+
const bbox = this.toBBox(item);
|
|
40025
|
+
const path = [];
|
|
40026
|
+
const indexes = [];
|
|
40027
|
+
let i, parent, goingUp;
|
|
40028
|
+
|
|
40029
|
+
// depth-first iterative tree traversal
|
|
40030
|
+
while (node || path.length) {
|
|
40031
|
+
|
|
40032
|
+
if (!node) { // go up
|
|
40033
|
+
node = path.pop();
|
|
40034
|
+
parent = path[path.length - 1];
|
|
40035
|
+
i = indexes.pop();
|
|
40036
|
+
goingUp = true;
|
|
40037
|
+
}
|
|
40038
|
+
|
|
40039
|
+
if (node.leaf) { // check current node
|
|
40040
|
+
const index = findItem(item, node.children, equalsFn);
|
|
40041
|
+
|
|
40042
|
+
if (index !== -1) {
|
|
40043
|
+
// item found, remove the item and condense tree upwards
|
|
40044
|
+
node.children.splice(index, 1);
|
|
40045
|
+
path.push(node);
|
|
40046
|
+
this._condense(path);
|
|
40047
|
+
return this;
|
|
40048
|
+
}
|
|
40049
|
+
}
|
|
40050
|
+
|
|
40051
|
+
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
|
|
40052
|
+
path.push(node);
|
|
40053
|
+
indexes.push(i);
|
|
40054
|
+
i = 0;
|
|
40055
|
+
parent = node;
|
|
40056
|
+
node = node.children[0];
|
|
40057
|
+
|
|
40058
|
+
} else if (parent) { // go right
|
|
40059
|
+
i++;
|
|
40060
|
+
node = parent.children[i];
|
|
40061
|
+
goingUp = false;
|
|
40062
|
+
|
|
40063
|
+
} else node = null; // nothing found
|
|
40064
|
+
}
|
|
40065
|
+
|
|
40066
|
+
return this;
|
|
40067
|
+
}
|
|
40068
|
+
|
|
40069
|
+
toBBox(item) { return item; }
|
|
40070
|
+
|
|
40071
|
+
compareMinX(a, b) { return a.minX - b.minX; }
|
|
40072
|
+
compareMinY(a, b) { return a.minY - b.minY; }
|
|
40073
|
+
|
|
40074
|
+
toJSON() { return this.data; }
|
|
40075
|
+
|
|
40076
|
+
fromJSON(data) {
|
|
40077
|
+
this.data = data;
|
|
40078
|
+
return this;
|
|
40079
|
+
}
|
|
40080
|
+
|
|
40081
|
+
_all(node, result) {
|
|
40082
|
+
const nodesToSearch = [];
|
|
40083
|
+
while (node) {
|
|
40084
|
+
if (node.leaf) result.push(...node.children);
|
|
40085
|
+
else nodesToSearch.push(...node.children);
|
|
40086
|
+
|
|
40087
|
+
node = nodesToSearch.pop();
|
|
40088
|
+
}
|
|
40089
|
+
return result;
|
|
40090
|
+
}
|
|
40091
|
+
|
|
40092
|
+
_build(items, left, right, height) {
|
|
40093
|
+
|
|
40094
|
+
const N = right - left + 1;
|
|
40095
|
+
let M = this._maxEntries;
|
|
40096
|
+
let node;
|
|
40097
|
+
|
|
40098
|
+
if (N <= M) {
|
|
40099
|
+
// reached leaf level; return leaf
|
|
40100
|
+
node = createNode(items.slice(left, right + 1));
|
|
40101
|
+
calcBBox(node, this.toBBox);
|
|
40102
|
+
return node;
|
|
40103
|
+
}
|
|
40104
|
+
|
|
40105
|
+
if (!height) {
|
|
40106
|
+
// target height of the bulk-loaded tree
|
|
40107
|
+
height = Math.ceil(Math.log(N) / Math.log(M));
|
|
40108
|
+
|
|
40109
|
+
// target number of root entries to maximize storage utilization
|
|
40110
|
+
M = Math.ceil(N / Math.pow(M, height - 1));
|
|
40111
|
+
}
|
|
40112
|
+
|
|
40113
|
+
node = createNode([]);
|
|
40114
|
+
node.leaf = false;
|
|
40115
|
+
node.height = height;
|
|
40116
|
+
|
|
40117
|
+
// split the items into M mostly square tiles
|
|
40118
|
+
|
|
40119
|
+
const N2 = Math.ceil(N / M);
|
|
40120
|
+
const N1 = N2 * Math.ceil(Math.sqrt(M));
|
|
40121
|
+
|
|
40122
|
+
multiSelect(items, left, right, N1, this.compareMinX);
|
|
40123
|
+
|
|
40124
|
+
for (let i = left; i <= right; i += N1) {
|
|
40125
|
+
|
|
40126
|
+
const right2 = Math.min(i + N1 - 1, right);
|
|
40127
|
+
|
|
40128
|
+
multiSelect(items, i, right2, N2, this.compareMinY);
|
|
40129
|
+
|
|
40130
|
+
for (let j = i; j <= right2; j += N2) {
|
|
40131
|
+
|
|
40132
|
+
const right3 = Math.min(j + N2 - 1, right2);
|
|
40133
|
+
|
|
40134
|
+
// pack each entry recursively
|
|
40135
|
+
node.children.push(this._build(items, j, right3, height - 1));
|
|
40136
|
+
}
|
|
40137
|
+
}
|
|
40138
|
+
|
|
40139
|
+
calcBBox(node, this.toBBox);
|
|
40140
|
+
|
|
40141
|
+
return node;
|
|
40142
|
+
}
|
|
40143
|
+
|
|
40144
|
+
_chooseSubtree(bbox, node, level, path) {
|
|
40145
|
+
while (true) {
|
|
40146
|
+
path.push(node);
|
|
40147
|
+
|
|
40148
|
+
if (node.leaf || path.length - 1 === level) break;
|
|
40149
|
+
|
|
40150
|
+
let minArea = Infinity;
|
|
40151
|
+
let minEnlargement = Infinity;
|
|
40152
|
+
let targetNode;
|
|
40153
|
+
|
|
40154
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
40155
|
+
const child = node.children[i];
|
|
40156
|
+
const area = bboxArea(child);
|
|
40157
|
+
const enlargement = enlargedArea(bbox, child) - area;
|
|
40158
|
+
|
|
40159
|
+
// choose entry with the least area enlargement
|
|
40160
|
+
if (enlargement < minEnlargement) {
|
|
40161
|
+
minEnlargement = enlargement;
|
|
40162
|
+
minArea = area < minArea ? area : minArea;
|
|
40163
|
+
targetNode = child;
|
|
40164
|
+
|
|
40165
|
+
} else if (enlargement === minEnlargement) {
|
|
40166
|
+
// otherwise choose one with the smallest area
|
|
40167
|
+
if (area < minArea) {
|
|
40168
|
+
minArea = area;
|
|
40169
|
+
targetNode = child;
|
|
40170
|
+
}
|
|
40171
|
+
}
|
|
40172
|
+
}
|
|
40173
|
+
|
|
40174
|
+
node = targetNode || node.children[0];
|
|
40175
|
+
}
|
|
40176
|
+
|
|
40177
|
+
return node;
|
|
40178
|
+
}
|
|
40179
|
+
|
|
40180
|
+
_insert(item, level, isNode) {
|
|
40181
|
+
const bbox = isNode ? item : this.toBBox(item);
|
|
40182
|
+
const insertPath = [];
|
|
40183
|
+
|
|
40184
|
+
// find the best node for accommodating the item, saving all nodes along the path too
|
|
40185
|
+
const node = this._chooseSubtree(bbox, this.data, level, insertPath);
|
|
40186
|
+
|
|
40187
|
+
// put the item into the node
|
|
40188
|
+
node.children.push(item);
|
|
40189
|
+
extend(node, bbox);
|
|
40190
|
+
|
|
40191
|
+
// split on node overflow; propagate upwards if necessary
|
|
40192
|
+
while (level >= 0) {
|
|
40193
|
+
if (insertPath[level].children.length > this._maxEntries) {
|
|
40194
|
+
this._split(insertPath, level);
|
|
40195
|
+
level--;
|
|
40196
|
+
} else break;
|
|
40197
|
+
}
|
|
40198
|
+
|
|
40199
|
+
// adjust bboxes along the insertion path
|
|
40200
|
+
this._adjustParentBBoxes(bbox, insertPath, level);
|
|
40201
|
+
}
|
|
40202
|
+
|
|
40203
|
+
// split overflowed node into two
|
|
40204
|
+
_split(insertPath, level) {
|
|
40205
|
+
const node = insertPath[level];
|
|
40206
|
+
const M = node.children.length;
|
|
40207
|
+
const m = this._minEntries;
|
|
40208
|
+
|
|
40209
|
+
this._chooseSplitAxis(node, m, M);
|
|
40210
|
+
|
|
40211
|
+
const splitIndex = this._chooseSplitIndex(node, m, M);
|
|
40212
|
+
|
|
40213
|
+
const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
|
|
40214
|
+
newNode.height = node.height;
|
|
40215
|
+
newNode.leaf = node.leaf;
|
|
40216
|
+
|
|
40217
|
+
calcBBox(node, this.toBBox);
|
|
40218
|
+
calcBBox(newNode, this.toBBox);
|
|
40219
|
+
|
|
40220
|
+
if (level) insertPath[level - 1].children.push(newNode);
|
|
40221
|
+
else this._splitRoot(node, newNode);
|
|
40222
|
+
}
|
|
40223
|
+
|
|
40224
|
+
_splitRoot(node, newNode) {
|
|
40225
|
+
// split root node
|
|
40226
|
+
this.data = createNode([node, newNode]);
|
|
40227
|
+
this.data.height = node.height + 1;
|
|
40228
|
+
this.data.leaf = false;
|
|
40229
|
+
calcBBox(this.data, this.toBBox);
|
|
40230
|
+
}
|
|
40231
|
+
|
|
40232
|
+
_chooseSplitIndex(node, m, M) {
|
|
40233
|
+
let index;
|
|
40234
|
+
let minOverlap = Infinity;
|
|
40235
|
+
let minArea = Infinity;
|
|
40236
|
+
|
|
40237
|
+
for (let i = m; i <= M - m; i++) {
|
|
40238
|
+
const bbox1 = distBBox(node, 0, i, this.toBBox);
|
|
40239
|
+
const bbox2 = distBBox(node, i, M, this.toBBox);
|
|
40240
|
+
|
|
40241
|
+
const overlap = intersectionArea(bbox1, bbox2);
|
|
40242
|
+
const area = bboxArea(bbox1) + bboxArea(bbox2);
|
|
40243
|
+
|
|
40244
|
+
// choose distribution with minimum overlap
|
|
40245
|
+
if (overlap < minOverlap) {
|
|
40246
|
+
minOverlap = overlap;
|
|
40247
|
+
index = i;
|
|
40248
|
+
|
|
40249
|
+
minArea = area < minArea ? area : minArea;
|
|
40250
|
+
|
|
40251
|
+
} else if (overlap === minOverlap) {
|
|
40252
|
+
// otherwise choose distribution with minimum area
|
|
40253
|
+
if (area < minArea) {
|
|
40254
|
+
minArea = area;
|
|
40255
|
+
index = i;
|
|
40256
|
+
}
|
|
40257
|
+
}
|
|
40258
|
+
}
|
|
40259
|
+
|
|
40260
|
+
return index || M - m;
|
|
40261
|
+
}
|
|
40262
|
+
|
|
40263
|
+
// sorts node children by the best axis for split
|
|
40264
|
+
_chooseSplitAxis(node, m, M) {
|
|
40265
|
+
const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
|
|
40266
|
+
const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
|
|
40267
|
+
const xMargin = this._allDistMargin(node, m, M, compareMinX);
|
|
40268
|
+
const yMargin = this._allDistMargin(node, m, M, compareMinY);
|
|
40269
|
+
|
|
40270
|
+
// if total distributions margin value is minimal for x, sort by minX,
|
|
40271
|
+
// otherwise it's already sorted by minY
|
|
40272
|
+
if (xMargin < yMargin) node.children.sort(compareMinX);
|
|
40273
|
+
}
|
|
40274
|
+
|
|
40275
|
+
// total margin of all possible split distributions where each node is at least m full
|
|
40276
|
+
_allDistMargin(node, m, M, compare) {
|
|
40277
|
+
node.children.sort(compare);
|
|
40278
|
+
|
|
40279
|
+
const toBBox = this.toBBox;
|
|
40280
|
+
const leftBBox = distBBox(node, 0, m, toBBox);
|
|
40281
|
+
const rightBBox = distBBox(node, M - m, M, toBBox);
|
|
40282
|
+
let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
|
|
40283
|
+
|
|
40284
|
+
for (let i = m; i < M - m; i++) {
|
|
40285
|
+
const child = node.children[i];
|
|
40286
|
+
extend(leftBBox, node.leaf ? toBBox(child) : child);
|
|
40287
|
+
margin += bboxMargin(leftBBox);
|
|
40288
|
+
}
|
|
40289
|
+
|
|
40290
|
+
for (let i = M - m - 1; i >= m; i--) {
|
|
40291
|
+
const child = node.children[i];
|
|
40292
|
+
extend(rightBBox, node.leaf ? toBBox(child) : child);
|
|
40293
|
+
margin += bboxMargin(rightBBox);
|
|
40294
|
+
}
|
|
40295
|
+
|
|
40296
|
+
return margin;
|
|
40297
|
+
}
|
|
40298
|
+
|
|
40299
|
+
_adjustParentBBoxes(bbox, path, level) {
|
|
40300
|
+
// adjust bboxes along the given tree path
|
|
40301
|
+
for (let i = level; i >= 0; i--) {
|
|
40302
|
+
extend(path[i], bbox);
|
|
40303
|
+
}
|
|
40304
|
+
}
|
|
40305
|
+
|
|
40306
|
+
_condense(path) {
|
|
40307
|
+
// go through the path, removing empty nodes and updating bboxes
|
|
40308
|
+
for (let i = path.length - 1, siblings; i >= 0; i--) {
|
|
40309
|
+
if (path[i].children.length === 0) {
|
|
40310
|
+
if (i > 0) {
|
|
40311
|
+
siblings = path[i - 1].children;
|
|
40312
|
+
siblings.splice(siblings.indexOf(path[i]), 1);
|
|
40313
|
+
|
|
40314
|
+
} else this.clear();
|
|
40315
|
+
|
|
40316
|
+
} else calcBBox(path[i], this.toBBox);
|
|
40317
|
+
}
|
|
40318
|
+
}
|
|
40319
|
+
}
|
|
40320
|
+
|
|
40321
|
+
function findItem(item, items, equalsFn) {
|
|
40322
|
+
if (!equalsFn) return items.indexOf(item);
|
|
40323
|
+
|
|
40324
|
+
for (let i = 0; i < items.length; i++) {
|
|
40325
|
+
if (equalsFn(item, items[i])) return i;
|
|
40326
|
+
}
|
|
40327
|
+
return -1;
|
|
40328
|
+
}
|
|
40329
|
+
|
|
40330
|
+
// calculate node's bbox from bboxes of its children
|
|
40331
|
+
function calcBBox(node, toBBox) {
|
|
40332
|
+
distBBox(node, 0, node.children.length, toBBox, node);
|
|
40333
|
+
}
|
|
40334
|
+
|
|
40335
|
+
// min bounding rectangle of node children from k to p-1
|
|
40336
|
+
function distBBox(node, k, p, toBBox, destNode) {
|
|
40337
|
+
if (!destNode) destNode = createNode(null);
|
|
40338
|
+
destNode.minX = Infinity;
|
|
40339
|
+
destNode.minY = Infinity;
|
|
40340
|
+
destNode.maxX = -Infinity;
|
|
40341
|
+
destNode.maxY = -Infinity;
|
|
40342
|
+
|
|
40343
|
+
for (let i = k; i < p; i++) {
|
|
40344
|
+
const child = node.children[i];
|
|
40345
|
+
extend(destNode, node.leaf ? toBBox(child) : child);
|
|
40346
|
+
}
|
|
40347
|
+
|
|
40348
|
+
return destNode;
|
|
40349
|
+
}
|
|
40350
|
+
|
|
40351
|
+
function extend(a, b) {
|
|
40352
|
+
a.minX = Math.min(a.minX, b.minX);
|
|
40353
|
+
a.minY = Math.min(a.minY, b.minY);
|
|
40354
|
+
a.maxX = Math.max(a.maxX, b.maxX);
|
|
40355
|
+
a.maxY = Math.max(a.maxY, b.maxY);
|
|
40356
|
+
return a;
|
|
40357
|
+
}
|
|
40358
|
+
|
|
40359
|
+
function compareNodeMinX(a, b) { return a.minX - b.minX; }
|
|
40360
|
+
function compareNodeMinY(a, b) { return a.minY - b.minY; }
|
|
40361
|
+
|
|
40362
|
+
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
|
|
40363
|
+
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
|
|
40364
|
+
|
|
40365
|
+
function enlargedArea(a, b) {
|
|
40366
|
+
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
|
|
40367
|
+
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
|
|
40368
|
+
}
|
|
40369
|
+
|
|
40370
|
+
function intersectionArea(a, b) {
|
|
40371
|
+
const minX = Math.max(a.minX, b.minX);
|
|
40372
|
+
const minY = Math.max(a.minY, b.minY);
|
|
40373
|
+
const maxX = Math.min(a.maxX, b.maxX);
|
|
40374
|
+
const maxY = Math.min(a.maxY, b.maxY);
|
|
40375
|
+
|
|
40376
|
+
return Math.max(0, maxX - minX) *
|
|
40377
|
+
Math.max(0, maxY - minY);
|
|
40378
|
+
}
|
|
40379
|
+
|
|
40380
|
+
function contains(a, b) {
|
|
40381
|
+
return a.minX <= b.minX &&
|
|
40382
|
+
a.minY <= b.minY &&
|
|
40383
|
+
b.maxX <= a.maxX &&
|
|
40384
|
+
b.maxY <= a.maxY;
|
|
40385
|
+
}
|
|
40386
|
+
|
|
40387
|
+
function intersects(a, b) {
|
|
40388
|
+
return b.minX <= a.maxX &&
|
|
40389
|
+
b.minY <= a.maxY &&
|
|
40390
|
+
b.maxX >= a.minX &&
|
|
40391
|
+
b.maxY >= a.minY;
|
|
40392
|
+
}
|
|
40393
|
+
|
|
40394
|
+
function createNode(children) {
|
|
40395
|
+
return {
|
|
40396
|
+
children,
|
|
40397
|
+
height: 1,
|
|
40398
|
+
leaf: true,
|
|
40399
|
+
minX: Infinity,
|
|
40400
|
+
minY: Infinity,
|
|
40401
|
+
maxX: -Infinity,
|
|
40402
|
+
maxY: -Infinity
|
|
40403
|
+
};
|
|
40404
|
+
}
|
|
40405
|
+
|
|
40406
|
+
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
|
|
40407
|
+
// combines selection algorithm with binary divide & conquer approach
|
|
40408
|
+
|
|
40409
|
+
function multiSelect(arr, left, right, n, compare) {
|
|
40410
|
+
const stack = [left, right];
|
|
40411
|
+
|
|
40412
|
+
while (stack.length) {
|
|
40413
|
+
right = stack.pop();
|
|
40414
|
+
left = stack.pop();
|
|
40415
|
+
|
|
40416
|
+
if (right - left <= n) continue;
|
|
40417
|
+
|
|
40418
|
+
const mid = left + Math.ceil((right - left) / n / 2) * n;
|
|
40419
|
+
quickselect(arr, mid, left, right, compare);
|
|
40420
|
+
|
|
40421
|
+
stack.push(left, mid, mid, right);
|
|
40422
|
+
}
|
|
40423
|
+
}
|
|
40424
|
+
|
|
40425
|
+
/**
|
|
40426
|
+
* R-Tree Data Structure
|
|
40427
|
+
*
|
|
40428
|
+
* R-Tree is a spatial data structure used for efficient indexing and querying
|
|
40429
|
+
* of multi-dimensional objects, particularly in geometric and spatial applications.
|
|
40430
|
+
*
|
|
40431
|
+
* It organizes objects into a tree hierarchy, grouping nearby objects together
|
|
40432
|
+
* in bounding boxes. Each node in the tree represents a bounding box that
|
|
40433
|
+
* contains its child nodes or leaf objects. This hierarchical structure allows
|
|
40434
|
+
* for faster spatial queries.
|
|
40435
|
+
*
|
|
40436
|
+
* @see https://en.wikipedia.org/wiki/R-tree
|
|
40437
|
+
*
|
|
40438
|
+
* Consider a 2D Space with four zones: A, B, C, D
|
|
40439
|
+
* +--------------------------+
|
|
40440
|
+
* | |
|
|
40441
|
+
* | +---+ +-------+ |
|
|
40442
|
+
* | | A | | B | |
|
|
40443
|
+
* | +---+ +-------+ |
|
|
40444
|
+
* | |
|
|
40445
|
+
* | |
|
|
40446
|
+
* | +---+ |
|
|
40447
|
+
* | | C | |
|
|
40448
|
+
* | +---+ |
|
|
40449
|
+
* | +-----------+ |
|
|
40450
|
+
* | | D | |
|
|
40451
|
+
* | +-----------+ |
|
|
40452
|
+
* | |
|
|
40453
|
+
* +--------------------------+
|
|
40454
|
+
*
|
|
40455
|
+
* It groups together zones that are spatially close into a minimum bounding box.
|
|
40456
|
+
* For example, A and B are grouped together in rectangle R1, and C and D are grouped
|
|
40457
|
+
* in R2.
|
|
40458
|
+
*
|
|
40459
|
+
* R0
|
|
40460
|
+
* +--------------------------+
|
|
40461
|
+
* | R1 |
|
|
40462
|
+
* | +-----------------+ |
|
|
40463
|
+
* | | A | | B | |
|
|
40464
|
+
* | +-----------------+ |
|
|
40465
|
+
* | |
|
|
40466
|
+
* | R2 |
|
|
40467
|
+
* | +---+---+---+ |
|
|
40468
|
+
* | | | C | | |
|
|
40469
|
+
* | | +---+ | |
|
|
40470
|
+
* | +-----------+ |
|
|
40471
|
+
* | | D | |
|
|
40472
|
+
* | +-----------+ |
|
|
40473
|
+
* | |
|
|
40474
|
+
* +--------------------------+
|
|
40475
|
+
*
|
|
40476
|
+
* The tree would look like this:
|
|
40477
|
+
* R0
|
|
40478
|
+
* / \
|
|
40479
|
+
* / \
|
|
40480
|
+
* R1 R2
|
|
40481
|
+
* | |
|
|
40482
|
+
* A,B C,D
|
|
40483
|
+
|
|
40484
|
+
* Choosing how to group the zones is crucial for the performance of the tree.
|
|
40485
|
+
* Key considerations include avoiding excessive empty space coverage and minimizing overlap
|
|
40486
|
+
* to reduce the number of subtrees processed during searches.
|
|
40487
|
+
*
|
|
40488
|
+
* Various heuristics exist for determining the optimal grouping strategy, such as "least enlargement"
|
|
40489
|
+
* which prioritizes grouping nodes resulting in the smallest increase in bounding box size. In cases where
|
|
40490
|
+
* the choice cannot be made based on this criterion due to the same enlargement for different groupings,
|
|
40491
|
+
* we then evaluate "least area," aiming to minimize the overall area of bounding boxes.
|
|
40492
|
+
*
|
|
40493
|
+
* This implementation is tailored for spreadsheet use, indexing objects associated
|
|
40494
|
+
* with a zone and a sheet.
|
|
40495
|
+
*
|
|
40496
|
+
* It uses the RBush library under the hood. One 2D RBush R-tree per sheet.
|
|
40497
|
+
* @see https://github.com/mourner/rbush
|
|
40498
|
+
*/
|
|
40499
|
+
class SpreadsheetRTree {
|
|
40500
|
+
/**
|
|
40501
|
+
* One 2D R-tree per sheet
|
|
40502
|
+
*/
|
|
40503
|
+
rTrees = {};
|
|
40504
|
+
/**
|
|
40505
|
+
* Bulk-inserts the given items into the tree. Bulk insertion is usually ~2-3 times
|
|
40506
|
+
* faster than inserting items one by one. After bulk loading (bulk insertion into
|
|
40507
|
+
* an empty tree), subsequent query performance is also ~20-30% better.
|
|
40508
|
+
*/
|
|
40509
|
+
constructor(items = []) {
|
|
40510
|
+
const rangesPerSheet = {};
|
|
40511
|
+
for (const item of items) {
|
|
40512
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40513
|
+
if (!rangesPerSheet[sheetId]) {
|
|
40514
|
+
rangesPerSheet[sheetId] = [];
|
|
40515
|
+
}
|
|
40516
|
+
rangesPerSheet[sheetId].push(item);
|
|
40517
|
+
}
|
|
40518
|
+
for (const sheetId in rangesPerSheet) {
|
|
40519
|
+
this.rTrees[sheetId] = new ZoneRBush();
|
|
40520
|
+
this.rTrees[sheetId].load(rangesPerSheet[sheetId]); // bulk-insert
|
|
40521
|
+
}
|
|
40522
|
+
}
|
|
40523
|
+
insert(item) {
|
|
40524
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40525
|
+
if (!this.rTrees[sheetId]) {
|
|
40526
|
+
this.rTrees[sheetId] = new ZoneRBush();
|
|
40527
|
+
}
|
|
40528
|
+
this.rTrees[sheetId].insert(item);
|
|
40529
|
+
}
|
|
40530
|
+
search({ zone, sheetId }) {
|
|
40531
|
+
if (!this.rTrees[sheetId]) {
|
|
40532
|
+
return [];
|
|
40533
|
+
}
|
|
40534
|
+
return this.rTrees[sheetId].search({
|
|
40535
|
+
minX: zone.left,
|
|
40536
|
+
minY: zone.top,
|
|
40537
|
+
maxX: zone.right,
|
|
40538
|
+
maxY: zone.bottom,
|
|
40539
|
+
});
|
|
40540
|
+
}
|
|
40541
|
+
remove(item) {
|
|
40542
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40543
|
+
if (!this.rTrees[sheetId]) {
|
|
40544
|
+
return;
|
|
40545
|
+
}
|
|
40546
|
+
this.rTrees[sheetId].remove(item, deepEquals);
|
|
40547
|
+
}
|
|
40548
|
+
}
|
|
40549
|
+
/**
|
|
40550
|
+
* RBush extension to use zones as bounding boxes
|
|
40551
|
+
*/
|
|
40552
|
+
class ZoneRBush extends RBush {
|
|
40553
|
+
toBBox({ boundingBox }) {
|
|
40554
|
+
const zone = boundingBox.zone;
|
|
40555
|
+
return {
|
|
40556
|
+
minX: zone.left,
|
|
40557
|
+
minY: zone.top,
|
|
40558
|
+
maxX: zone.right,
|
|
40559
|
+
maxY: zone.bottom,
|
|
40560
|
+
};
|
|
40561
|
+
}
|
|
40562
|
+
compareMinX(a, b) {
|
|
40563
|
+
return a.boundingBox.zone.left - b.boundingBox.zone.left;
|
|
40564
|
+
}
|
|
40565
|
+
compareMinY(a, b) {
|
|
40566
|
+
return a.boundingBox.zone.top - b.boundingBox.zone.top;
|
|
40567
|
+
}
|
|
40568
|
+
}
|
|
40569
|
+
|
|
39839
40570
|
/**
|
|
39840
|
-
*
|
|
40571
|
+
* Implementation of a dependency Graph.
|
|
39841
40572
|
* The graph is used to evaluate the cells in the correct
|
|
39842
40573
|
* order, and should be updated each time a cell's content is modified
|
|
39843
40574
|
*
|
|
40575
|
+
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
39844
40576
|
*/
|
|
39845
40577
|
class FormulaDependencyGraph {
|
|
39846
|
-
|
|
39847
|
-
* Internal structure:
|
|
39848
|
-
* - key: a cell position (encoded as an integer)
|
|
39849
|
-
* - value: a set of cell positions that depends on the key
|
|
39850
|
-
*
|
|
39851
|
-
* Given
|
|
39852
|
-
* - A1:"= B1 + SQRT(B2)"
|
|
39853
|
-
* - C1:"= B1";
|
|
39854
|
-
* - C2:"= C1"
|
|
39855
|
-
*
|
|
39856
|
-
* we will have something like:
|
|
39857
|
-
* - B1 ---> (A1, C1) meaning A1 and C1 depends on B1
|
|
39858
|
-
* - B2 ---> (A1) meaning A1 depends on B2
|
|
39859
|
-
* - C1 ---> (C2) meaning C2 depends on C1
|
|
39860
|
-
*/
|
|
39861
|
-
inverseDependencies = new Map();
|
|
40578
|
+
encoder;
|
|
39862
40579
|
dependencies = new Map();
|
|
40580
|
+
rTree;
|
|
40581
|
+
constructor(encoder, data = []) {
|
|
40582
|
+
this.encoder = encoder;
|
|
40583
|
+
this.rTree = new SpreadsheetRTree(data);
|
|
40584
|
+
}
|
|
39863
40585
|
removeAllDependencies(formulaPositionId) {
|
|
39864
|
-
const
|
|
39865
|
-
if (!
|
|
40586
|
+
const ranges = this.dependencies.get(formulaPositionId);
|
|
40587
|
+
if (!ranges) {
|
|
39866
40588
|
return;
|
|
39867
40589
|
}
|
|
39868
|
-
for (const
|
|
39869
|
-
this.
|
|
40590
|
+
for (const range of ranges) {
|
|
40591
|
+
this.rTree.remove(range);
|
|
39870
40592
|
}
|
|
39871
40593
|
this.dependencies.delete(formulaPositionId);
|
|
39872
40594
|
}
|
|
39873
40595
|
addDependencies(formulaPositionId, dependencies) {
|
|
39874
|
-
|
|
39875
|
-
|
|
39876
|
-
|
|
39877
|
-
|
|
39878
|
-
|
|
39879
|
-
|
|
39880
|
-
|
|
39881
|
-
|
|
40596
|
+
const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
|
|
40597
|
+
data: formulaPositionId,
|
|
40598
|
+
boundingBox: {
|
|
40599
|
+
zone,
|
|
40600
|
+
sheetId,
|
|
40601
|
+
},
|
|
40602
|
+
}));
|
|
40603
|
+
for (const item of rTreeItems) {
|
|
40604
|
+
this.rTree.insert(item);
|
|
39882
40605
|
}
|
|
39883
40606
|
const existingDependencies = this.dependencies.get(formulaPositionId);
|
|
39884
40607
|
if (existingDependencies) {
|
|
39885
|
-
existingDependencies.push(...
|
|
40608
|
+
existingDependencies.push(...rTreeItems);
|
|
39886
40609
|
}
|
|
39887
40610
|
else {
|
|
39888
|
-
this.dependencies.set(formulaPositionId,
|
|
40611
|
+
this.dependencies.set(formulaPositionId, rTreeItems);
|
|
39889
40612
|
}
|
|
39890
40613
|
}
|
|
39891
40614
|
/**
|
|
@@ -39893,20 +40616,20 @@ class FormulaDependencyGraph {
|
|
|
39893
40616
|
* in the correct order they should be evaluated.
|
|
39894
40617
|
* This is called a topological ordering (excluding cycles)
|
|
39895
40618
|
*/
|
|
39896
|
-
getCellsDependingOn(
|
|
40619
|
+
getCellsDependingOn(ranges) {
|
|
39897
40620
|
const visited = new JetSet();
|
|
39898
|
-
const queue = Array.from(
|
|
40621
|
+
const queue = Array.from(ranges).reverse();
|
|
39899
40622
|
while (queue.length > 0) {
|
|
39900
|
-
const
|
|
39901
|
-
visited.add(
|
|
39902
|
-
const
|
|
39903
|
-
for (const
|
|
39904
|
-
if (!visited.has(
|
|
39905
|
-
queue.push(
|
|
40623
|
+
const range = queue.pop();
|
|
40624
|
+
visited.add(...this.encoder.encodeBoundingBox(range));
|
|
40625
|
+
const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
|
|
40626
|
+
for (const positionId of impactedPositionIds) {
|
|
40627
|
+
if (!visited.has(positionId)) {
|
|
40628
|
+
queue.push(this.encoder.decodeToBoundingBox(positionId));
|
|
39906
40629
|
}
|
|
39907
40630
|
}
|
|
39908
40631
|
}
|
|
39909
|
-
visited.delete(...
|
|
40632
|
+
visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
39910
40633
|
return visited;
|
|
39911
40634
|
}
|
|
39912
40635
|
}
|
|
@@ -39994,9 +40717,9 @@ class Evaluator {
|
|
|
39994
40717
|
context;
|
|
39995
40718
|
getters;
|
|
39996
40719
|
compilationParams;
|
|
39997
|
-
|
|
40720
|
+
encoder = new PositionBitsEncoder();
|
|
39998
40721
|
evaluatedCells = new Map();
|
|
39999
|
-
formulaDependencies = lazy(new FormulaDependencyGraph());
|
|
40722
|
+
formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
|
|
40000
40723
|
blockedArrayFormulas = new Set();
|
|
40001
40724
|
spreadingRelations = new SpreadingRelation();
|
|
40002
40725
|
constructor(context, getters) {
|
|
@@ -40005,23 +40728,23 @@ class Evaluator {
|
|
|
40005
40728
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40006
40729
|
}
|
|
40007
40730
|
getEvaluatedCell(position) {
|
|
40008
|
-
return (this.evaluatedCells.get(this.
|
|
40731
|
+
return (this.evaluatedCells.get(this.encoder.encode(position)) ||
|
|
40009
40732
|
createEvaluatedCell("", { locale: this.getters.getLocale() }));
|
|
40010
40733
|
}
|
|
40011
40734
|
getSpreadPositionsOf(position) {
|
|
40012
|
-
const positionId = this.
|
|
40735
|
+
const positionId = this.encoder.encode(position);
|
|
40013
40736
|
if (!this.spreadingRelations.isArrayFormula(positionId)) {
|
|
40014
40737
|
return [];
|
|
40015
40738
|
}
|
|
40016
|
-
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.
|
|
40739
|
+
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
|
|
40017
40740
|
}
|
|
40018
40741
|
getArrayFormulaSpreadingOn(position) {
|
|
40019
|
-
const positionId = this.
|
|
40742
|
+
const positionId = this.encoder.encode(position);
|
|
40020
40743
|
const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
|
|
40021
|
-
return formulaPosition !== undefined ? this.
|
|
40744
|
+
return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
|
|
40022
40745
|
}
|
|
40023
40746
|
getEvaluatedPositions() {
|
|
40024
|
-
return [...this.evaluatedCells.keys()].map(this.
|
|
40747
|
+
return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
|
|
40025
40748
|
}
|
|
40026
40749
|
getArrayFormulaSpreadingOnId(positionId) {
|
|
40027
40750
|
if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
|
|
@@ -40031,7 +40754,7 @@ class Evaluator {
|
|
|
40031
40754
|
return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
|
|
40032
40755
|
}
|
|
40033
40756
|
updateDependencies(position) {
|
|
40034
|
-
const positionId = this.
|
|
40757
|
+
const positionId = this.encoder.encode(position);
|
|
40035
40758
|
this.formulaDependencies().removeAllDependencies(positionId);
|
|
40036
40759
|
const dependencies = this.getDirectDependencies(positionId);
|
|
40037
40760
|
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
@@ -40041,12 +40764,12 @@ class Evaluator {
|
|
|
40041
40764
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40042
40765
|
}
|
|
40043
40766
|
evaluateCells(positions) {
|
|
40044
|
-
const cells = positions.map(this.
|
|
40767
|
+
const cells = positions.map((p) => this.encoder.encode(p));
|
|
40045
40768
|
const cellsToCompute = new JetSet(cells);
|
|
40046
|
-
const
|
|
40769
|
+
const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
|
|
40047
40770
|
cellsToCompute.add(...this.getCellsDependingOn(cells));
|
|
40048
|
-
cellsToCompute.add(...
|
|
40049
|
-
cellsToCompute.add(...this.getCellsDependingOn(
|
|
40771
|
+
cellsToCompute.add(...arrayFormulasPositionIds);
|
|
40772
|
+
cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
|
|
40050
40773
|
this.evaluate(cellsToCompute);
|
|
40051
40774
|
}
|
|
40052
40775
|
getArrayFormulasImpactedByChangesOf(positionIds) {
|
|
@@ -40069,12 +40792,14 @@ class Evaluator {
|
|
|
40069
40792
|
this.blockedArrayFormulas = new Set();
|
|
40070
40793
|
this.spreadingRelations = new SpreadingRelation();
|
|
40071
40794
|
this.formulaDependencies = lazy(() => {
|
|
40072
|
-
const
|
|
40073
|
-
|
|
40074
|
-
|
|
40075
|
-
|
|
40076
|
-
|
|
40077
|
-
|
|
40795
|
+
const dependencies = [...this.getAllCells()].flatMap((positionId) => this.getDirectDependencies(positionId).map((range) => ({
|
|
40796
|
+
data: positionId,
|
|
40797
|
+
boundingBox: {
|
|
40798
|
+
zone: range.zone,
|
|
40799
|
+
sheetId: range.sheetId,
|
|
40800
|
+
},
|
|
40801
|
+
})));
|
|
40802
|
+
return new FormulaDependencyGraph(this.encoder, dependencies);
|
|
40078
40803
|
});
|
|
40079
40804
|
}
|
|
40080
40805
|
evaluateAllCells() {
|
|
@@ -40096,7 +40821,7 @@ class Evaluator {
|
|
|
40096
40821
|
for (const sheetId of this.getters.getSheetIds()) {
|
|
40097
40822
|
const cellIds = this.getters.getCells(sheetId);
|
|
40098
40823
|
for (const cellId in cellIds) {
|
|
40099
|
-
positionIds.add(this.
|
|
40824
|
+
positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
|
|
40100
40825
|
}
|
|
40101
40826
|
}
|
|
40102
40827
|
return positionIds;
|
|
@@ -40163,7 +40888,7 @@ class Evaluator {
|
|
|
40163
40888
|
}
|
|
40164
40889
|
}
|
|
40165
40890
|
computeAndSave(position) {
|
|
40166
|
-
const positionId = this.
|
|
40891
|
+
const positionId = this.encoder.encode(position);
|
|
40167
40892
|
const evaluatedCell = this.computeCell(positionId);
|
|
40168
40893
|
if (!this.evaluatedCells.has(positionId)) {
|
|
40169
40894
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
@@ -40223,15 +40948,15 @@ class Evaluator {
|
|
|
40223
40948
|
throw new Error(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
|
|
40224
40949
|
}
|
|
40225
40950
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
40226
|
-
const arrayFormulaPositionId = this.
|
|
40951
|
+
const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40227
40952
|
return (i, j) => {
|
|
40228
40953
|
const position = { sheetId, col: i + col, row: j + row };
|
|
40229
|
-
const resultPositionId = this.
|
|
40954
|
+
const resultPositionId = this.encoder.encode(position);
|
|
40230
40955
|
this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
|
|
40231
40956
|
};
|
|
40232
40957
|
}
|
|
40233
40958
|
checkCollision({ sheetId, col, row }) {
|
|
40234
|
-
const formulaPositionId = this.
|
|
40959
|
+
const formulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40235
40960
|
return (i, j) => {
|
|
40236
40961
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
40237
40962
|
const rawCell = this.getters.getCell(position);
|
|
@@ -40252,7 +40977,7 @@ class Evaluator {
|
|
|
40252
40977
|
format: format || matrixResult[i][j]?.format,
|
|
40253
40978
|
locale: this.getters.getLocale(),
|
|
40254
40979
|
});
|
|
40255
|
-
const positionId = this.
|
|
40980
|
+
const positionId = this.encoder.encode(position);
|
|
40256
40981
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
40257
40982
|
// check if formula dependencies present in the spread zone
|
|
40258
40983
|
// if so, they need to be recomputed
|
|
@@ -40284,29 +41009,17 @@ class Evaluator {
|
|
|
40284
41009
|
if (!cell?.isFormula) {
|
|
40285
41010
|
return [];
|
|
40286
41011
|
}
|
|
40287
|
-
|
|
40288
|
-
for (const range of cell.compiledFormula.dependencies) {
|
|
40289
|
-
if (range.invalidSheetName || range.invalidXc) {
|
|
40290
|
-
continue;
|
|
40291
|
-
}
|
|
40292
|
-
const sheetId = range.sheetId;
|
|
40293
|
-
forEachPositionsInZone(range.zone, (col, row) => {
|
|
40294
|
-
dependencies.push(this.encodePosition({ sheetId, col, row }));
|
|
40295
|
-
});
|
|
40296
|
-
}
|
|
40297
|
-
return dependencies;
|
|
41012
|
+
return cell.compiledFormula.dependencies;
|
|
40298
41013
|
}
|
|
40299
41014
|
getCellsDependingOn(positionIds) {
|
|
40300
|
-
|
|
41015
|
+
const ranges = [];
|
|
41016
|
+
for (const positionId of positionIds) {
|
|
41017
|
+
ranges.push(this.encoder.decodeToBoundingBox(positionId));
|
|
41018
|
+
}
|
|
41019
|
+
return this.formulaDependencies().getCellsDependingOn(ranges);
|
|
40301
41020
|
}
|
|
40302
41021
|
getCell(positionId) {
|
|
40303
|
-
return this.getters.getCell(this.
|
|
40304
|
-
}
|
|
40305
|
-
encodePosition(position) {
|
|
40306
|
-
return this.positionEncoder.encode(position);
|
|
40307
|
-
}
|
|
40308
|
-
decodePosition(positionId) {
|
|
40309
|
-
return this.positionEncoder.decode(positionId);
|
|
41022
|
+
return this.getters.getCell(this.encoder.decode(positionId));
|
|
40310
41023
|
}
|
|
40311
41024
|
}
|
|
40312
41025
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
@@ -40362,6 +41075,13 @@ class PositionBitsEncoder {
|
|
|
40362
41075
|
encode({ sheetId, col, row }) {
|
|
40363
41076
|
return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
|
|
40364
41077
|
}
|
|
41078
|
+
encodeBoundingBox({ sheetId, zone }) {
|
|
41079
|
+
const positions = [];
|
|
41080
|
+
forEachPositionsInZone(zone, (col, row) => {
|
|
41081
|
+
positions.push(this.encode({ sheetId, col, row }));
|
|
41082
|
+
});
|
|
41083
|
+
return positions;
|
|
41084
|
+
}
|
|
40365
41085
|
decode(id) {
|
|
40366
41086
|
// keep only the last 21 bits by AND-ing the bit sequence with 21 ones
|
|
40367
41087
|
const row = Number(id & 2097151n);
|
|
@@ -40369,6 +41089,10 @@ class PositionBitsEncoder {
|
|
|
40369
41089
|
const sheetId = this.decodeSheet(id >> 42n);
|
|
40370
41090
|
return { sheetId, col, row };
|
|
40371
41091
|
}
|
|
41092
|
+
decodeToBoundingBox(id) {
|
|
41093
|
+
const { sheetId, col, row } = this.decode(id);
|
|
41094
|
+
return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
|
|
41095
|
+
}
|
|
40372
41096
|
encodeSheet(sheetId) {
|
|
40373
41097
|
const sheetKey = this.sheetMapping[sheetId];
|
|
40374
41098
|
if (sheetKey === undefined) {
|
|
@@ -40539,7 +41263,12 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
40539
41263
|
// Getters
|
|
40540
41264
|
// ---------------------------------------------------------------------------
|
|
40541
41265
|
evaluateFormula(sheetId, formulaString) {
|
|
40542
|
-
|
|
41266
|
+
try {
|
|
41267
|
+
return this.evaluator.evaluateFormula(sheetId, formulaString);
|
|
41268
|
+
}
|
|
41269
|
+
catch (error) {
|
|
41270
|
+
return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
|
|
41271
|
+
}
|
|
40543
41272
|
}
|
|
40544
41273
|
/**
|
|
40545
41274
|
* Return the value of each cell in the range as they are displayed in the grid.
|
|
@@ -40808,7 +41537,7 @@ class CustomColorsPlugin extends UIPlugin {
|
|
|
40808
41537
|
}
|
|
40809
41538
|
|
|
40810
41539
|
class EvaluationChartPlugin extends UIPlugin {
|
|
40811
|
-
static getters = ["getChartRuntime", "
|
|
41540
|
+
static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
|
|
40812
41541
|
charts = {};
|
|
40813
41542
|
createRuntimeChart = chartRuntimeFactory(this.getters);
|
|
40814
41543
|
handle(cmd) {
|
|
@@ -40846,25 +41575,26 @@ class EvaluationChartPlugin extends UIPlugin {
|
|
|
40846
41575
|
return this.charts[figureId];
|
|
40847
41576
|
}
|
|
40848
41577
|
/**
|
|
40849
|
-
* Get the background
|
|
40850
|
-
* of the chart. In order of priority, it will return :
|
|
40851
|
-
*
|
|
40852
|
-
* - the chart background color if one is defined
|
|
40853
|
-
* - the fill color of the cell if one is defined
|
|
40854
|
-
* - the fill color of the cell from conditional formats if one is defined
|
|
40855
|
-
* - the default chart color if no other color is defined
|
|
41578
|
+
* Get the background and textColor of a chart based on the color of the first cell of the main range of the chart.
|
|
40856
41579
|
*/
|
|
40857
|
-
|
|
41580
|
+
getStyleOfSingleCellChart(chartBackground, mainRange) {
|
|
40858
41581
|
if (chartBackground)
|
|
40859
|
-
return chartBackground;
|
|
41582
|
+
return { background: chartBackground, fontColor: chartFontColor(chartBackground) };
|
|
40860
41583
|
if (!mainRange) {
|
|
40861
|
-
return
|
|
41584
|
+
return {
|
|
41585
|
+
background: BACKGROUND_CHART_COLOR,
|
|
41586
|
+
fontColor: chartFontColor(BACKGROUND_CHART_COLOR),
|
|
41587
|
+
};
|
|
40862
41588
|
}
|
|
40863
41589
|
const col = mainRange.zone.left;
|
|
40864
41590
|
const row = mainRange.zone.top;
|
|
40865
41591
|
const sheetId = mainRange.sheetId;
|
|
40866
41592
|
const style = this.getters.getCellComputedStyle({ sheetId, col, row });
|
|
40867
|
-
|
|
41593
|
+
const background = style.fillColor || BACKGROUND_CHART_COLOR;
|
|
41594
|
+
return {
|
|
41595
|
+
background,
|
|
41596
|
+
fontColor: style.textColor || chartFontColor(background),
|
|
41597
|
+
};
|
|
40868
41598
|
}
|
|
40869
41599
|
exportForExcel(data) {
|
|
40870
41600
|
for (const sheet of data.sheets) {
|
|
@@ -40975,45 +41705,40 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
40975
41705
|
getComputedStyles(sheetId) {
|
|
40976
41706
|
const computedStyle = {};
|
|
40977
41707
|
for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
|
|
40978
|
-
|
|
40979
|
-
|
|
40980
|
-
|
|
40981
|
-
|
|
40982
|
-
|
|
40983
|
-
|
|
40984
|
-
|
|
40985
|
-
|
|
40986
|
-
|
|
40987
|
-
|
|
40988
|
-
|
|
40989
|
-
for (let
|
|
40990
|
-
|
|
40991
|
-
|
|
40992
|
-
|
|
40993
|
-
const
|
|
40994
|
-
|
|
40995
|
-
|
|
40996
|
-
|
|
40997
|
-
|
|
40998
|
-
|
|
40999
|
-
});
|
|
41000
|
-
}
|
|
41001
|
-
return value;
|
|
41002
|
-
});
|
|
41003
|
-
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
41004
|
-
if (!computedStyle[col])
|
|
41005
|
-
computedStyle[col] = [];
|
|
41006
|
-
// we must combine all the properties of all the CF rules applied to the given cell
|
|
41007
|
-
computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
|
|
41708
|
+
switch (cf.rule.type) {
|
|
41709
|
+
case "ColorScaleRule":
|
|
41710
|
+
for (let range of cf.ranges) {
|
|
41711
|
+
this.applyColorScale(sheetId, range, cf.rule, computedStyle);
|
|
41712
|
+
}
|
|
41713
|
+
break;
|
|
41714
|
+
case "CellIsRule":
|
|
41715
|
+
const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
|
|
41716
|
+
for (let ref of cf.ranges) {
|
|
41717
|
+
const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
|
|
41718
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
41719
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
41720
|
+
const predicate = this.rulePredicate[cf.rule.type];
|
|
41721
|
+
const target = { sheetId, col, row };
|
|
41722
|
+
const values = cf.rule.values.map((value, i) => {
|
|
41723
|
+
const compiledFormula = formulas[i];
|
|
41724
|
+
if (compiledFormula) {
|
|
41725
|
+
return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
|
|
41726
|
+
...compiledFormula,
|
|
41727
|
+
dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
|
|
41728
|
+
});
|
|
41008
41729
|
}
|
|
41730
|
+
return value;
|
|
41731
|
+
});
|
|
41732
|
+
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
41733
|
+
if (!computedStyle[col])
|
|
41734
|
+
computedStyle[col] = [];
|
|
41735
|
+
// we must combine all the properties of all the CF rules applied to the given cell
|
|
41736
|
+
computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
|
|
41009
41737
|
}
|
|
41010
41738
|
}
|
|
41011
41739
|
}
|
|
41012
|
-
|
|
41013
|
-
|
|
41014
|
-
}
|
|
41015
|
-
catch (_) {
|
|
41016
|
-
// we don't care about the errors within the evaluation of a rule
|
|
41740
|
+
}
|
|
41741
|
+
break;
|
|
41017
41742
|
}
|
|
41018
41743
|
}
|
|
41019
41744
|
return computedStyle;
|
|
@@ -41784,19 +42509,21 @@ class AutofillPlugin extends UIPlugin {
|
|
|
41784
42509
|
let col = zone.left;
|
|
41785
42510
|
let row = zone.bottom;
|
|
41786
42511
|
if (col > 0) {
|
|
41787
|
-
let
|
|
41788
|
-
while (
|
|
42512
|
+
let leftPosition = { sheetId, col: col - 1, row };
|
|
42513
|
+
while (this.getters.getCorrespondingFormulaCell(leftPosition) ||
|
|
42514
|
+
this.getters.getCell(leftPosition)?.content) {
|
|
41789
42515
|
row += 1;
|
|
41790
|
-
|
|
42516
|
+
leftPosition = { sheetId, col: col - 1, row };
|
|
41791
42517
|
}
|
|
41792
42518
|
}
|
|
41793
42519
|
if (row === zone.bottom) {
|
|
41794
42520
|
col = zone.right;
|
|
41795
42521
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
41796
|
-
let
|
|
41797
|
-
while (
|
|
42522
|
+
let rightPosition = { sheetId, col: col + 1, row };
|
|
42523
|
+
while (this.getters.getCorrespondingFormulaCell(rightPosition) ||
|
|
42524
|
+
this.getters.getCell(rightPosition)?.content) {
|
|
41798
42525
|
row += 1;
|
|
41799
|
-
|
|
42526
|
+
rightPosition = { sheetId, col: col + 1, row };
|
|
41800
42527
|
}
|
|
41801
42528
|
}
|
|
41802
42529
|
}
|
|
@@ -42788,6 +43515,7 @@ class Session extends EventBus {
|
|
|
42788
43515
|
isReplayingInitialRevisions = false;
|
|
42789
43516
|
processedRevisions = new Set();
|
|
42790
43517
|
uuidGenerator = new UuidGenerator();
|
|
43518
|
+
lastLocalOperation;
|
|
42791
43519
|
/**
|
|
42792
43520
|
* Manages the collaboration between multiple users on the same spreadsheet.
|
|
42793
43521
|
* It can forward local state changes to other users to ensure they all eventually
|
|
@@ -42820,6 +43548,11 @@ class Session extends EventBus {
|
|
|
42820
43548
|
return;
|
|
42821
43549
|
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
|
|
42822
43550
|
this.revisions.append(revision.id, revision);
|
|
43551
|
+
// REQUEST_REDO just repeats the last operation, the
|
|
43552
|
+
// last operation is still the same and should not change.
|
|
43553
|
+
if (rootCommand.type !== "REQUEST_REDO") {
|
|
43554
|
+
this.lastLocalOperation = revision;
|
|
43555
|
+
}
|
|
42823
43556
|
this.trigger("new-local-state-update", { id: revision.id });
|
|
42824
43557
|
this.sendUpdateMessage({
|
|
42825
43558
|
type: "REMOTE_REVISION",
|
|
@@ -42916,19 +43649,10 @@ class Session extends EventBus {
|
|
|
42916
43649
|
return this.pendingMessages.length === 0;
|
|
42917
43650
|
}
|
|
42918
43651
|
/**
|
|
42919
|
-
* Get the last local revision
|
|
43652
|
+
* Get the last local revision
|
|
42920
43653
|
* */
|
|
42921
|
-
getLastLocalNonEmptyRevision(
|
|
42922
|
-
|
|
42923
|
-
for (const rev of revisions) {
|
|
42924
|
-
if (rev.rootCommand === "SNAPSHOT")
|
|
42925
|
-
return undefined;
|
|
42926
|
-
if (!rev.rootCommand || rev.rootCommand === "REMOTE")
|
|
42927
|
-
continue;
|
|
42928
|
-
if (!ignoredRootCommands.includes(rev.rootCommand?.type) && rev.commands.length)
|
|
42929
|
-
return rev;
|
|
42930
|
-
}
|
|
42931
|
-
return undefined;
|
|
43654
|
+
getLastLocalNonEmptyRevision() {
|
|
43655
|
+
return this.lastLocalOperation;
|
|
42932
43656
|
}
|
|
42933
43657
|
_move(position) {
|
|
42934
43658
|
// this method is debounced and might be called after the client
|
|
@@ -42988,7 +43712,7 @@ class Session extends EventBus {
|
|
|
42988
43712
|
break;
|
|
42989
43713
|
case "REMOTE_REVISION":
|
|
42990
43714
|
const { clientId, commands, timestamp } = message;
|
|
42991
|
-
const revision = new Revision(message.nextRevisionId, clientId, commands,
|
|
43715
|
+
const revision = new Revision(message.nextRevisionId, clientId, commands, undefined, undefined, timestamp);
|
|
42992
43716
|
if (revision.clientId !== this.clientId) {
|
|
42993
43717
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
42994
43718
|
const pendingCommands = this.pendingMessages
|
|
@@ -43001,10 +43725,11 @@ class Session extends EventBus {
|
|
|
43001
43725
|
}
|
|
43002
43726
|
break;
|
|
43003
43727
|
case "SNAPSHOT_CREATED": {
|
|
43004
|
-
const revision = new Revision(message.nextRevisionId, "server", [],
|
|
43728
|
+
const revision = new Revision(message.nextRevisionId, "server", [], undefined, undefined, Date.now());
|
|
43005
43729
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
43006
43730
|
this.dropPendingHistoryMessages();
|
|
43007
43731
|
this.trigger("snapshot");
|
|
43732
|
+
this.lastLocalOperation = undefined;
|
|
43008
43733
|
break;
|
|
43009
43734
|
}
|
|
43010
43735
|
}
|
|
@@ -45559,6 +46284,9 @@ class SelectionInputsManagerPlugin extends UIPlugin {
|
|
|
45559
46284
|
// Other
|
|
45560
46285
|
// ---------------------------------------------------------------------------
|
|
45561
46286
|
initInput(id, initialRanges, inputHasSingleRange = false) {
|
|
46287
|
+
if (this.inputs[id]) {
|
|
46288
|
+
this.unfocus();
|
|
46289
|
+
}
|
|
45562
46290
|
this.inputs[id] = new SelectionInputPlugin(this.config, initialRanges, inputHasSingleRange);
|
|
45563
46291
|
if (initialRanges.length === 0) {
|
|
45564
46292
|
const input = this.inputs[id];
|
|
@@ -46357,7 +47085,7 @@ class HistoryPlugin extends UIPlugin {
|
|
|
46357
47085
|
* Ignore standard undo/redo revisions (that are empty)
|
|
46358
47086
|
*/
|
|
46359
47087
|
getPossibleRevisionToRepeat() {
|
|
46360
|
-
return this.session.getLastLocalNonEmptyRevision(
|
|
47088
|
+
return this.session.getLastLocalNonEmptyRevision();
|
|
46361
47089
|
}
|
|
46362
47090
|
}
|
|
46363
47091
|
|
|
@@ -47142,6 +47870,10 @@ function setXcToReferenceType(xc, referenceType) {
|
|
|
47142
47870
|
return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
|
|
47143
47871
|
case "colrow":
|
|
47144
47872
|
indexOfNumber = xc.search(/[0-9]/);
|
|
47873
|
+
if (indexOfNumber === -1 || indexOfNumber === 0) {
|
|
47874
|
+
// no row number (eg. A) or no column (eg. 1)
|
|
47875
|
+
return "$" + xc;
|
|
47876
|
+
}
|
|
47145
47877
|
xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
|
|
47146
47878
|
return "$" + xc;
|
|
47147
47879
|
case "none":
|
|
@@ -47912,6 +48644,12 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
47912
48644
|
break;
|
|
47913
48645
|
case "HIDE_COLUMNS_ROWS":
|
|
47914
48646
|
case "UNHIDE_COLUMNS_ROWS":
|
|
48647
|
+
case "GROUP_HEADERS":
|
|
48648
|
+
case "UNGROUP_HEADERS":
|
|
48649
|
+
case "FOLD_HEADER_GROUP":
|
|
48650
|
+
case "UNFOLD_HEADER_GROUP":
|
|
48651
|
+
case "FOLD_ALL_HEADER_GROUPS":
|
|
48652
|
+
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
47915
48653
|
this.updateHiddenRows();
|
|
47916
48654
|
break;
|
|
47917
48655
|
case "UPDATE_FILTER":
|
|
@@ -48613,7 +49351,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
48613
49351
|
sheetId: cmd.sheetId,
|
|
48614
49352
|
base: cmd.base,
|
|
48615
49353
|
quantity: thickness,
|
|
48616
|
-
position:
|
|
49354
|
+
position: cmd.position,
|
|
48617
49355
|
});
|
|
48618
49356
|
const isCol = cmd.dimension === "COL";
|
|
48619
49357
|
const start = cmd.elements[0];
|
|
@@ -48630,12 +49368,13 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
48630
49368
|
},
|
|
48631
49369
|
];
|
|
48632
49370
|
const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
|
|
49371
|
+
const base = isBasedBefore ? cmd.base : cmd.base + 1;
|
|
48633
49372
|
const pasteTarget = [
|
|
48634
49373
|
{
|
|
48635
|
-
left: isCol ?
|
|
48636
|
-
right: isCol ?
|
|
48637
|
-
top: !isCol ?
|
|
48638
|
-
bottom: !isCol ?
|
|
49374
|
+
left: isCol ? base : 0,
|
|
49375
|
+
right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
49376
|
+
top: !isCol ? base : 0,
|
|
49377
|
+
bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
48639
49378
|
},
|
|
48640
49379
|
];
|
|
48641
49380
|
state.paste(pasteTarget, { selectTarget: true });
|
|
@@ -48670,6 +49409,11 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
48670
49409
|
doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
|
|
48671
49410
|
return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
|
|
48672
49411
|
}
|
|
49412
|
+
const headers = [cmd.base, ...cmd.elements];
|
|
49413
|
+
const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
|
|
49414
|
+
if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
|
|
49415
|
+
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
49416
|
+
}
|
|
48673
49417
|
return "Success" /* CommandResult.Success */;
|
|
48674
49418
|
}
|
|
48675
49419
|
//-------------------------------------------
|
|
@@ -49715,10 +50459,13 @@ class HeaderPositionsUIPlugin extends UIPlugin {
|
|
|
49715
50459
|
}
|
|
49716
50460
|
break;
|
|
49717
50461
|
case "UPDATE_CELL":
|
|
49718
|
-
if ("content" in cmd || "format" in cmd
|
|
50462
|
+
if ("content" in cmd || "format" in cmd) {
|
|
49719
50463
|
this.headerPositions = {};
|
|
49720
50464
|
this.isDirty = true;
|
|
49721
50465
|
}
|
|
50466
|
+
else {
|
|
50467
|
+
this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
|
|
50468
|
+
}
|
|
49722
50469
|
break;
|
|
49723
50470
|
case "UPDATE_FILTER":
|
|
49724
50471
|
case "REMOVE_FILTER_TABLE":
|
|
@@ -51875,6 +52622,8 @@ css /* scss */ `
|
|
|
51875
52622
|
*:before,
|
|
51876
52623
|
*:after {
|
|
51877
52624
|
box-sizing: content-box;
|
|
52625
|
+
/** rtl not supported ATM */
|
|
52626
|
+
direction: ltr;
|
|
51878
52627
|
}
|
|
51879
52628
|
.o-separator {
|
|
51880
52629
|
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
@@ -52152,7 +52901,7 @@ class Spreadsheet extends Component {
|
|
|
52152
52901
|
}
|
|
52153
52902
|
onKeydown(ev) {
|
|
52154
52903
|
let keyDownString = "";
|
|
52155
|
-
if (ev
|
|
52904
|
+
if (isCtrlKey(ev)) {
|
|
52156
52905
|
keyDownString += "CTRL+";
|
|
52157
52906
|
}
|
|
52158
52907
|
keyDownString += ev.key.toUpperCase();
|
|
@@ -55864,6 +56613,7 @@ const helpers = {
|
|
|
55864
56613
|
colorToRGBA,
|
|
55865
56614
|
positionToZone,
|
|
55866
56615
|
isDefined: isDefined$1,
|
|
56616
|
+
isMatrix,
|
|
55867
56617
|
lazy,
|
|
55868
56618
|
genericRepeat,
|
|
55869
56619
|
createAction,
|
|
@@ -55912,6 +56662,6 @@ const constants = {
|
|
|
55912
56662
|
export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
|
|
55913
56663
|
|
|
55914
56664
|
|
|
55915
|
-
__info__.version = "17.0.
|
|
55916
|
-
__info__.date = "
|
|
55917
|
-
__info__.hash = "
|
|
56665
|
+
__info__.version = "17.0.6";
|
|
56666
|
+
__info__.date = "2024-01-04T12:23:10.357Z";
|
|
56667
|
+
__info__.hash = "310ca57";
|