@odoo/o-spreadsheet 17.1.0-alpha.4 → 17.1.0-alpha.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 +1056 -284
- package/dist/o-spreadsheet.d.ts +37 -44
- package/dist/o-spreadsheet.esm.js +1056 -284
- package/dist/o-spreadsheet.iife.js +1056 -284
- package/dist/o-spreadsheet.iife.min.js +277 -273
- 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.1.0-alpha.
|
|
6
|
-
* @date
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.1.0-alpha.6
|
|
6
|
+
* @date 2024-01-04T12:22:45.921Z
|
|
7
|
+
* @hash 56dbdc9
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -329,13 +329,6 @@
|
|
|
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 @@
|
|
|
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
|
}
|
|
@@ -2061,6 +2054,8 @@
|
|
|
2061
2054
|
CommandResult["NoChanges"] = "NoChanges";
|
|
2062
2055
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
2063
2056
|
|
|
2057
|
+
const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
|
|
2058
|
+
|
|
2064
2059
|
const DEFAULT_LOCALES = [
|
|
2065
2060
|
{
|
|
2066
2061
|
name: "English (US)",
|
|
@@ -2824,6 +2819,9 @@
|
|
|
2824
2819
|
* Formats a cell value with its format.
|
|
2825
2820
|
*/
|
|
2826
2821
|
function formatValue(value, { format, locale }) {
|
|
2822
|
+
if (format === PLAIN_TEXT_FORMAT) {
|
|
2823
|
+
return toString(value) || "";
|
|
2824
|
+
}
|
|
2827
2825
|
switch (typeof value) {
|
|
2828
2826
|
case "string":
|
|
2829
2827
|
return value;
|
|
@@ -3387,8 +3385,10 @@
|
|
|
3387
3385
|
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
3388
3386
|
// Same as above, but matches the exact string (nothing before or after)
|
|
3389
3387
|
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(
|
|
3388
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
3389
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
3390
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
3391
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
3392
3392
|
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
3393
3393
|
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
3394
3394
|
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
@@ -3418,6 +3418,9 @@
|
|
|
3418
3418
|
function isColHeader(str) {
|
|
3419
3419
|
return colHeader.test(str);
|
|
3420
3420
|
}
|
|
3421
|
+
function isRowHeader(str) {
|
|
3422
|
+
return rowHeader.test(str);
|
|
3423
|
+
}
|
|
3421
3424
|
/**
|
|
3422
3425
|
* Return true if the given xc is the reference of a single cell,
|
|
3423
3426
|
* without any specified sheet (e.g. A1)
|
|
@@ -4108,18 +4111,10 @@
|
|
|
4108
4111
|
if (isFullCol) {
|
|
4109
4112
|
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4110
4113
|
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4111
|
-
if (zone.left === zone.right) {
|
|
4112
|
-
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4113
|
-
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4114
|
-
}
|
|
4115
4114
|
}
|
|
4116
4115
|
if (isFullRow) {
|
|
4117
4116
|
parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4118
4117
|
parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
|
|
4119
|
-
if (zone.top === zone.bottom) {
|
|
4120
|
-
parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4121
|
-
parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
|
|
4122
|
-
}
|
|
4123
4118
|
}
|
|
4124
4119
|
return parts;
|
|
4125
4120
|
}
|
|
@@ -4361,7 +4356,7 @@
|
|
|
4361
4356
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4362
4357
|
if (!cell || !cell.content)
|
|
4363
4358
|
return DEFAULT_CELL_HEIGHT;
|
|
4364
|
-
const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4359
|
+
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4365
4360
|
const numberOfLines = cell.isFormula
|
|
4366
4361
|
? 1
|
|
4367
4362
|
: splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
|
|
@@ -5298,7 +5293,7 @@
|
|
|
5298
5293
|
};
|
|
5299
5294
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
5300
5295
|
}
|
|
5301
|
-
const background = getters.
|
|
5296
|
+
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
5302
5297
|
const locale = getters.getLocale();
|
|
5303
5298
|
return {
|
|
5304
5299
|
title: _t(chart.title),
|
|
@@ -5307,7 +5302,7 @@
|
|
|
5307
5302
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
5308
5303
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
5309
5304
|
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
5310
|
-
fontColor
|
|
5305
|
+
fontColor,
|
|
5311
5306
|
background,
|
|
5312
5307
|
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
5313
5308
|
? getters.getCellStyle({
|
|
@@ -5733,6 +5728,9 @@
|
|
|
5733
5728
|
}
|
|
5734
5729
|
|
|
5735
5730
|
function evaluateLiteral(content, localeFormat) {
|
|
5731
|
+
if (localeFormat.format === PLAIN_TEXT_FORMAT) {
|
|
5732
|
+
return textCell(content || "", localeFormat);
|
|
5733
|
+
}
|
|
5736
5734
|
return createEvaluatedCell(parseLiteral(content || "", localeFormat.locale), localeFormat);
|
|
5737
5735
|
}
|
|
5738
5736
|
function parseLiteral(content, locale) {
|
|
@@ -5767,6 +5765,9 @@
|
|
|
5767
5765
|
}
|
|
5768
5766
|
function _createEvaluatedCell(value, localeFormat) {
|
|
5769
5767
|
try {
|
|
5768
|
+
if (localeFormat.format === PLAIN_TEXT_FORMAT) {
|
|
5769
|
+
return textCell(toString(value), localeFormat);
|
|
5770
|
+
}
|
|
5770
5771
|
for (const builder of builders) {
|
|
5771
5772
|
const evaluateCell = builder(value, localeFormat);
|
|
5772
5773
|
if (evaluateCell) {
|
|
@@ -6258,10 +6259,6 @@
|
|
|
6258
6259
|
|
|
6259
6260
|
.o-error-tooltip-message {
|
|
6260
6261
|
overflow: hidden;
|
|
6261
|
-
display: -webkit-box; /* Limit to 3 lines */
|
|
6262
|
-
-webkit-line-clamp: 3;
|
|
6263
|
-
line-clamp: 3;
|
|
6264
|
-
-webkit-box-orient: vertical;
|
|
6265
6262
|
}
|
|
6266
6263
|
}
|
|
6267
6264
|
`;
|
|
@@ -6606,6 +6603,7 @@
|
|
|
6606
6603
|
},
|
|
6607
6604
|
};
|
|
6608
6605
|
|
|
6606
|
+
const macRegex = /Mac/i;
|
|
6609
6607
|
/**
|
|
6610
6608
|
* Return true if the event was triggered from
|
|
6611
6609
|
* a child element.
|
|
@@ -6657,7 +6655,7 @@
|
|
|
6657
6655
|
*/
|
|
6658
6656
|
function keyboardEventToShortcutString(ev, mode = "key") {
|
|
6659
6657
|
let keyDownString = "";
|
|
6660
|
-
if (ev
|
|
6658
|
+
if (isCtrlKey(ev) && ev.key !== "Ctrl")
|
|
6661
6659
|
keyDownString += "Ctrl+";
|
|
6662
6660
|
if (ev.metaKey)
|
|
6663
6661
|
keyDownString += "Ctrl+";
|
|
@@ -6669,6 +6667,17 @@
|
|
|
6669
6667
|
keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
|
|
6670
6668
|
return keyDownString;
|
|
6671
6669
|
}
|
|
6670
|
+
function isMacOS() {
|
|
6671
|
+
return Boolean(macRegex.test(navigator.userAgent));
|
|
6672
|
+
}
|
|
6673
|
+
/**
|
|
6674
|
+
* @param {KeyboardEvent | MouseEvent} ev
|
|
6675
|
+
* @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
|
|
6676
|
+
* On Mac, this is the "meta" or "command" key.
|
|
6677
|
+
*/
|
|
6678
|
+
function isCtrlKey(ev) {
|
|
6679
|
+
return isMacOS() ? ev.metaKey : ev.ctrlKey;
|
|
6680
|
+
}
|
|
6672
6681
|
|
|
6673
6682
|
/**
|
|
6674
6683
|
* Return the o-spreadsheet element position relative
|
|
@@ -8906,7 +8915,7 @@
|
|
|
8906
8915
|
});
|
|
8907
8916
|
return {
|
|
8908
8917
|
chartJsConfig: config,
|
|
8909
|
-
background: getters.
|
|
8918
|
+
background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
|
|
8910
8919
|
};
|
|
8911
8920
|
}
|
|
8912
8921
|
|
|
@@ -8987,7 +8996,7 @@
|
|
|
8987
8996
|
else if (format.includes("h") || format.includes("H")) {
|
|
8988
8997
|
return "hour";
|
|
8989
8998
|
}
|
|
8990
|
-
else if (format.includes("
|
|
8999
|
+
else if (format.includes("d")) {
|
|
8991
9000
|
return "day";
|
|
8992
9001
|
}
|
|
8993
9002
|
else if (format.includes("M")) {
|
|
@@ -9499,6 +9508,27 @@
|
|
|
9499
9508
|
const percentage = (dataset[dataIndex] / total) * 100;
|
|
9500
9509
|
return percentage.toFixed(2);
|
|
9501
9510
|
}
|
|
9511
|
+
function filterNegativeValues(labels, datasets) {
|
|
9512
|
+
const dataPointsIndexes = labels.reduce((indexes, label, i) => {
|
|
9513
|
+
const shouldKeep = datasets.some((dataset) => {
|
|
9514
|
+
const dataPoint = dataset.data[i];
|
|
9515
|
+
return typeof dataPoint !== "number" || dataPoint >= 0;
|
|
9516
|
+
});
|
|
9517
|
+
if (shouldKeep) {
|
|
9518
|
+
indexes.push(i);
|
|
9519
|
+
}
|
|
9520
|
+
return indexes;
|
|
9521
|
+
}, []);
|
|
9522
|
+
const filteredLabels = dataPointsIndexes.map((i) => labels[i] || "");
|
|
9523
|
+
const filteredDatasets = datasets.map((dataset) => ({
|
|
9524
|
+
...dataset,
|
|
9525
|
+
data: dataPointsIndexes.map((i) => {
|
|
9526
|
+
const dataPoint = dataset.data[i];
|
|
9527
|
+
return typeof dataPoint !== "number" || dataPoint >= 0 ? dataPoint : 0;
|
|
9528
|
+
}),
|
|
9529
|
+
}));
|
|
9530
|
+
return { labels: filteredLabels, dataSetsValues: filteredDatasets };
|
|
9531
|
+
}
|
|
9502
9532
|
function createPieChartRuntime(chart, getters) {
|
|
9503
9533
|
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
9504
9534
|
let labels = labelValues.formattedValues;
|
|
@@ -9512,6 +9542,7 @@
|
|
|
9512
9542
|
if (chart.aggregated) {
|
|
9513
9543
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
9514
9544
|
}
|
|
9545
|
+
({ dataSetsValues, labels } = filterNegativeValues(labels, dataSetsValues));
|
|
9515
9546
|
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
9516
9547
|
const locale = getters.getLocale();
|
|
9517
9548
|
const config = getPieConfiguration(chart, labels, { format: dataSetFormat, locale });
|
|
@@ -10349,7 +10380,7 @@
|
|
|
10349
10380
|
// Simple actions
|
|
10350
10381
|
//------------------------------------------------------------------------------
|
|
10351
10382
|
const PASTE_ACTION = async (env) => paste$1(env);
|
|
10352
|
-
const
|
|
10383
|
+
const PASTE_AS_VALUE_ACTION = async (env) => paste$1(env, "asValue");
|
|
10353
10384
|
async function paste$1(env, pasteOption) {
|
|
10354
10385
|
const spreadsheetClipboard = env.model.getters.getClipboardTextContent();
|
|
10355
10386
|
const osClipboard = await env.clipboard.readText();
|
|
@@ -10362,7 +10393,7 @@
|
|
|
10362
10393
|
else {
|
|
10363
10394
|
interactivePaste(env, target, pasteOption);
|
|
10364
10395
|
}
|
|
10365
|
-
if (env.model.getters.isCutOperation() && pasteOption !== "
|
|
10396
|
+
if (env.model.getters.isCutOperation() && pasteOption !== "asValue") {
|
|
10366
10397
|
await env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
|
|
10367
10398
|
}
|
|
10368
10399
|
break;
|
|
@@ -10777,9 +10808,9 @@
|
|
|
10777
10808
|
icon: "o-spreadsheet-Icon.PASTE",
|
|
10778
10809
|
};
|
|
10779
10810
|
const pasteSpecialValue = {
|
|
10780
|
-
name: _t("Paste value
|
|
10811
|
+
name: _t("Paste as value"),
|
|
10781
10812
|
description: "Ctrl+Shift+V",
|
|
10782
|
-
execute:
|
|
10813
|
+
execute: PASTE_AS_VALUE_ACTION,
|
|
10783
10814
|
};
|
|
10784
10815
|
const pasteSpecialFormat = {
|
|
10785
10816
|
name: _t("Paste format only"),
|
|
@@ -17948,6 +17979,10 @@
|
|
|
17948
17979
|
return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
|
|
17949
17980
|
case "colrow":
|
|
17950
17981
|
indexOfNumber = xc.search(/[0-9]/);
|
|
17982
|
+
if (indexOfNumber === -1 || indexOfNumber === 0) {
|
|
17983
|
+
// no row number (eg. A) or no column (eg. 1)
|
|
17984
|
+
return "$" + xc;
|
|
17985
|
+
}
|
|
17951
17986
|
xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
|
|
17952
17987
|
return "$" + xc;
|
|
17953
17988
|
case "none":
|
|
@@ -20014,6 +20049,11 @@
|
|
|
20014
20049
|
execute: (env) => setFormatter(env, ""),
|
|
20015
20050
|
isActive: (env) => isAutomaticFormatSelected(env),
|
|
20016
20051
|
};
|
|
20052
|
+
const formatNumberPlainText = {
|
|
20053
|
+
name: _t("Plain text"),
|
|
20054
|
+
execute: (env) => setFormatter(env, PLAIN_TEXT_FORMAT),
|
|
20055
|
+
isActive: (env) => isFormatSelected(env, PLAIN_TEXT_FORMAT),
|
|
20056
|
+
};
|
|
20017
20057
|
const formatNumberNumber = createFormatActionSpec({
|
|
20018
20058
|
name: _t("Number"),
|
|
20019
20059
|
descriptionValue: 1000.12,
|
|
@@ -20373,6 +20413,7 @@
|
|
|
20373
20413
|
formatNumberFullWeekDayAndMonth: formatNumberFullWeekDayAndMonth,
|
|
20374
20414
|
formatNumberNumber: formatNumberNumber,
|
|
20375
20415
|
formatNumberPercent: formatNumberPercent,
|
|
20416
|
+
formatNumberPlainText: formatNumberPlainText,
|
|
20376
20417
|
formatNumberShortMonth: formatNumberShortMonth,
|
|
20377
20418
|
formatNumberShortWeekDay: formatNumberShortWeekDay,
|
|
20378
20419
|
formatNumberTime: formatNumberTime,
|
|
@@ -20803,6 +20844,10 @@
|
|
|
20803
20844
|
.add("format_number_automatic", {
|
|
20804
20845
|
...formatNumberAutomatic,
|
|
20805
20846
|
sequence: 10,
|
|
20847
|
+
})
|
|
20848
|
+
.add("format_number_plain_text", {
|
|
20849
|
+
...formatNumberPlainText,
|
|
20850
|
+
sequence: 15,
|
|
20806
20851
|
separator: true,
|
|
20807
20852
|
})
|
|
20808
20853
|
.add("format_number_number", {
|
|
@@ -21401,10 +21446,10 @@
|
|
|
21401
21446
|
function updateSelectionWithArrowKeys(ev, selection) {
|
|
21402
21447
|
const direction = arrowMap[ev.key];
|
|
21403
21448
|
if (ev.shiftKey) {
|
|
21404
|
-
selection.resizeAnchorZone(direction, ev
|
|
21449
|
+
selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21405
21450
|
}
|
|
21406
21451
|
else {
|
|
21407
|
-
selection.moveAnchorCell(direction, ev
|
|
21452
|
+
selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
|
|
21408
21453
|
}
|
|
21409
21454
|
}
|
|
21410
21455
|
|
|
@@ -24013,6 +24058,12 @@
|
|
|
24013
24058
|
padding: 4px 0 4px 4px;
|
|
24014
24059
|
}
|
|
24015
24060
|
}
|
|
24061
|
+
|
|
24062
|
+
.o-matches-count div {
|
|
24063
|
+
text-overflow: ellipsis;
|
|
24064
|
+
overflow: hidden;
|
|
24065
|
+
white-space: nowrap;
|
|
24066
|
+
}
|
|
24016
24067
|
}
|
|
24017
24068
|
`;
|
|
24018
24069
|
class FindAndReplacePanel extends owl.Component {
|
|
@@ -28443,7 +28494,10 @@
|
|
|
28443
28494
|
return;
|
|
28444
28495
|
}
|
|
28445
28496
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
28446
|
-
this.props.onCellClicked(col, row, {
|
|
28497
|
+
this.props.onCellClicked(col, row, {
|
|
28498
|
+
expandZone: ev.shiftKey,
|
|
28499
|
+
addZone: isCtrlKey(ev),
|
|
28500
|
+
});
|
|
28447
28501
|
}
|
|
28448
28502
|
onDoubleClick(ev) {
|
|
28449
28503
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
@@ -28518,6 +28572,7 @@
|
|
|
28518
28572
|
draggerShadowThickness: 0,
|
|
28519
28573
|
delta: 0,
|
|
28520
28574
|
base: 0,
|
|
28575
|
+
position: "before",
|
|
28521
28576
|
});
|
|
28522
28577
|
_computeHandleDisplay(ev) {
|
|
28523
28578
|
const position = this._getEvOffset(ev);
|
|
@@ -28642,11 +28697,13 @@
|
|
|
28642
28697
|
this.state.draggerLinePosition = dimensions.start;
|
|
28643
28698
|
this.state.draggerShadowPosition = dimensions.start;
|
|
28644
28699
|
this.state.base = elementIndex;
|
|
28700
|
+
this.state.position = "before";
|
|
28645
28701
|
}
|
|
28646
28702
|
else if (this._getSelectedZoneEnd() < elementIndex) {
|
|
28647
28703
|
this.state.draggerLinePosition = dimensions.end;
|
|
28648
28704
|
this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
|
|
28649
|
-
this.state.base = elementIndex
|
|
28705
|
+
this.state.base = elementIndex;
|
|
28706
|
+
this.state.position = "after";
|
|
28650
28707
|
}
|
|
28651
28708
|
else {
|
|
28652
28709
|
this.state.draggerLinePosition = startDimensions.start;
|
|
@@ -28670,7 +28727,7 @@
|
|
|
28670
28727
|
this._increaseSelection(index);
|
|
28671
28728
|
}
|
|
28672
28729
|
else {
|
|
28673
|
-
this._selectElement(index, ev
|
|
28730
|
+
this._selectElement(index, isCtrlKey(ev));
|
|
28674
28731
|
}
|
|
28675
28732
|
this.lastSelectedElementIndex = index;
|
|
28676
28733
|
const mouseMoveSelect = (col, row) => {
|
|
@@ -28821,13 +28878,14 @@
|
|
|
28821
28878
|
dimension: "COL",
|
|
28822
28879
|
base: this.state.base,
|
|
28823
28880
|
elements,
|
|
28881
|
+
position: this.state.position,
|
|
28824
28882
|
});
|
|
28825
28883
|
if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
|
|
28826
28884
|
this.env.raiseError(MergeErrorMessage);
|
|
28827
28885
|
}
|
|
28828
28886
|
}
|
|
28829
|
-
_selectElement(index,
|
|
28830
|
-
this.env.model.selection.selectColumn(index,
|
|
28887
|
+
_selectElement(index, addDistinctHeader) {
|
|
28888
|
+
this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28831
28889
|
}
|
|
28832
28890
|
_increaseSelection(index) {
|
|
28833
28891
|
this.env.model.selection.selectColumn(index, "updateAnchor");
|
|
@@ -28985,13 +29043,14 @@
|
|
|
28985
29043
|
dimension: "ROW",
|
|
28986
29044
|
base: this.state.base,
|
|
28987
29045
|
elements,
|
|
29046
|
+
position: this.state.position,
|
|
28988
29047
|
});
|
|
28989
29048
|
if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
|
|
28990
29049
|
this.env.raiseError(MergeErrorMessage);
|
|
28991
29050
|
}
|
|
28992
29051
|
}
|
|
28993
|
-
_selectElement(index,
|
|
28994
|
-
this.env.model.selection.selectRow(index,
|
|
29052
|
+
_selectElement(index, addDistinctHeader) {
|
|
29053
|
+
this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
|
|
28995
29054
|
}
|
|
28996
29055
|
_increaseSelection(index) {
|
|
28997
29056
|
this.env.model.selection.selectRow(index, "updateAnchor");
|
|
@@ -29092,8 +29151,8 @@
|
|
|
29092
29151
|
return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
|
|
29093
29152
|
}
|
|
29094
29153
|
const onMouseWheel = (ev) => {
|
|
29095
|
-
const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
|
|
29096
|
-
const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
|
|
29154
|
+
const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
|
|
29155
|
+
const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
|
|
29097
29156
|
handler(deltaX, deltaY);
|
|
29098
29157
|
};
|
|
29099
29158
|
return onMouseWheel;
|
|
@@ -29684,7 +29743,7 @@
|
|
|
29684
29743
|
"Ctrl+Shift+E": () => this.setHorizontalAlign("center"),
|
|
29685
29744
|
"Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
|
|
29686
29745
|
"Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
|
|
29687
|
-
"Ctrl+Shift+V": () =>
|
|
29746
|
+
"Ctrl+Shift+V": () => PASTE_AS_VALUE_ACTION(this.env),
|
|
29688
29747
|
"Ctrl+Shift+<": () => this.clearFormatting(),
|
|
29689
29748
|
"Ctrl+<": () => this.clearFormatting(),
|
|
29690
29749
|
"Ctrl+Shift+ ": () => {
|
|
@@ -29792,17 +29851,17 @@
|
|
|
29792
29851
|
// ---------------------------------------------------------------------------
|
|
29793
29852
|
// Zone selection with mouse
|
|
29794
29853
|
// ---------------------------------------------------------------------------
|
|
29795
|
-
onCellClicked(col, row, {
|
|
29854
|
+
onCellClicked(col, row, { addZone, expandZone }) {
|
|
29796
29855
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
29797
29856
|
this.closeOpenedPopover();
|
|
29798
29857
|
}
|
|
29799
29858
|
if (this.env.model.getters.getEditionMode() === "editing") {
|
|
29800
29859
|
interactiveStopEdition(this.env);
|
|
29801
29860
|
}
|
|
29802
|
-
if (
|
|
29861
|
+
if (expandZone) {
|
|
29803
29862
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
29804
29863
|
}
|
|
29805
|
-
else if (
|
|
29864
|
+
else if (addZone) {
|
|
29806
29865
|
this.env.model.selection.addCellToSelection(col, row);
|
|
29807
29866
|
}
|
|
29808
29867
|
else {
|
|
@@ -30605,7 +30664,7 @@
|
|
|
30605
30664
|
46: "hhhh:mm:ss",
|
|
30606
30665
|
47: "hhhh:mm:ss",
|
|
30607
30666
|
48: undefined,
|
|
30608
|
-
49:
|
|
30667
|
+
49: PLAIN_TEXT_FORMAT,
|
|
30609
30668
|
};
|
|
30610
30669
|
/**
|
|
30611
30670
|
* Mapping format index to format defined by default
|
|
@@ -33530,7 +33589,7 @@
|
|
|
33530
33589
|
SPACE: goTo(State.RightRef),
|
|
33531
33590
|
NUMBER: goTo(State.Found),
|
|
33532
33591
|
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
33533
|
-
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
|
|
33592
|
+
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
|
|
33534
33593
|
},
|
|
33535
33594
|
[State.RightColumnRef]: {
|
|
33536
33595
|
SPACE: goTo(State.RightColumnRef),
|
|
@@ -33541,6 +33600,7 @@
|
|
|
33541
33600
|
SPACE: goTo(State.RightRowRef),
|
|
33542
33601
|
NUMBER: goTo(State.Found),
|
|
33543
33602
|
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
33603
|
+
SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
|
|
33544
33604
|
},
|
|
33545
33605
|
[State.Found]: {},
|
|
33546
33606
|
};
|
|
@@ -34737,7 +34797,7 @@
|
|
|
34737
34797
|
*/
|
|
34738
34798
|
function getBorderId(border) {
|
|
34739
34799
|
for (let [key, value] of Object.entries(borders)) {
|
|
34740
|
-
if (
|
|
34800
|
+
if (deepEquals(value, border)) {
|
|
34741
34801
|
return parseInt(key, 10);
|
|
34742
34802
|
}
|
|
34743
34803
|
}
|
|
@@ -35908,7 +35968,9 @@
|
|
|
35908
35968
|
}
|
|
35909
35969
|
createLiteralCell(id, content, format, style) {
|
|
35910
35970
|
const locale = this.getters.getLocale();
|
|
35911
|
-
|
|
35971
|
+
if (format !== PLAIN_TEXT_FORMAT) {
|
|
35972
|
+
content = toString(parseLiteral(content, locale));
|
|
35973
|
+
}
|
|
35912
35974
|
return {
|
|
35913
35975
|
id,
|
|
35914
35976
|
content,
|
|
@@ -37520,7 +37582,16 @@
|
|
|
37520
37582
|
return consecutiveIndexes;
|
|
37521
37583
|
}
|
|
37522
37584
|
getAllVisibleHeaders(sheetId, dimension) {
|
|
37523
|
-
|
|
37585
|
+
const headers = range(0, this.getters.getNumberHeaders(sheetId, dimension));
|
|
37586
|
+
const foldedHeaders = [];
|
|
37587
|
+
this.getters.getHeaderGroups(sheetId, dimension).forEach((group) => {
|
|
37588
|
+
if (group.isFolded) {
|
|
37589
|
+
foldedHeaders.push(...range(group.start, group.end + 1));
|
|
37590
|
+
}
|
|
37591
|
+
});
|
|
37592
|
+
return headers.filter((i) => {
|
|
37593
|
+
return !this.hiddenHeaders[sheetId][dimension][i] && !foldedHeaders.includes(i);
|
|
37594
|
+
});
|
|
37524
37595
|
}
|
|
37525
37596
|
import(data) {
|
|
37526
37597
|
for (let sheet of data.sheets) {
|
|
@@ -39981,10 +40052,13 @@
|
|
|
39981
40052
|
if (evaluatedCell === undefined) {
|
|
39982
40053
|
return { value: null, format: this.getters.getCell(position)?.format };
|
|
39983
40054
|
}
|
|
40055
|
+
if (evaluatedCell.type === CellValueType.error) {
|
|
40056
|
+
throw evaluatedCell.error;
|
|
40057
|
+
}
|
|
39984
40058
|
return evaluatedCell;
|
|
39985
40059
|
}
|
|
39986
40060
|
getEvaluatedCellIfNotEmpty(position) {
|
|
39987
|
-
const evaluatedCell = this.
|
|
40061
|
+
const evaluatedCell = this.computeCell(position);
|
|
39988
40062
|
if (evaluatedCell.type === CellValueType.empty) {
|
|
39989
40063
|
const cell = this.getters.getCell(position);
|
|
39990
40064
|
if (!cell || (!cell.isFormula && cell.content === "")) {
|
|
@@ -39993,13 +40067,6 @@
|
|
|
39993
40067
|
}
|
|
39994
40068
|
return evaluatedCell;
|
|
39995
40069
|
}
|
|
39996
|
-
getEvaluatedCell(position) {
|
|
39997
|
-
const evaluatedCell = this.computeCell(position);
|
|
39998
|
-
if (evaluatedCell.type === CellValueType.error) {
|
|
39999
|
-
throw evaluatedCell.error;
|
|
40000
|
-
}
|
|
40001
|
-
return evaluatedCell;
|
|
40002
|
-
}
|
|
40003
40070
|
/**
|
|
40004
40071
|
* Return the values of the cell(s) used in reference, but always in the format of a range even
|
|
40005
40072
|
* if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
|
|
@@ -40022,7 +40089,11 @@
|
|
|
40022
40089
|
const { top, left, bottom, right } = zone;
|
|
40023
40090
|
const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
|
|
40024
40091
|
if (cacheKey in this.rangeCache) {
|
|
40025
|
-
|
|
40092
|
+
const result = this.rangeCache[cacheKey];
|
|
40093
|
+
if (result instanceof EvaluationError) {
|
|
40094
|
+
throw result;
|
|
40095
|
+
}
|
|
40096
|
+
return result;
|
|
40026
40097
|
}
|
|
40027
40098
|
const height = _zone.bottom - _zone.top + 1;
|
|
40028
40099
|
const width = _zone.right - _zone.left + 1;
|
|
@@ -40032,6 +40103,11 @@
|
|
|
40032
40103
|
const colIndex = col - _zone.left;
|
|
40033
40104
|
matrix[colIndex] = new Array(height);
|
|
40034
40105
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
40106
|
+
const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
|
|
40107
|
+
if (evaluatedCell?.type === CellValueType.error) {
|
|
40108
|
+
this.rangeCache[cacheKey] = evaluatedCell.error;
|
|
40109
|
+
throw evaluatedCell.error;
|
|
40110
|
+
}
|
|
40035
40111
|
const rowIndex = row - _zone.top;
|
|
40036
40112
|
matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
|
|
40037
40113
|
}
|
|
@@ -40041,56 +40117,758 @@
|
|
|
40041
40117
|
}
|
|
40042
40118
|
}
|
|
40043
40119
|
|
|
40120
|
+
function quickselect(arr, k, left, right, compare) {
|
|
40121
|
+
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
|
|
40122
|
+
}
|
|
40123
|
+
|
|
40124
|
+
function quickselectStep(arr, k, left, right, compare) {
|
|
40125
|
+
|
|
40126
|
+
while (right > left) {
|
|
40127
|
+
if (right - left > 600) {
|
|
40128
|
+
var n = right - left + 1;
|
|
40129
|
+
var m = k - left + 1;
|
|
40130
|
+
var z = Math.log(n);
|
|
40131
|
+
var s = 0.5 * Math.exp(2 * z / 3);
|
|
40132
|
+
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
|
|
40133
|
+
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
|
|
40134
|
+
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
|
|
40135
|
+
quickselectStep(arr, k, newLeft, newRight, compare);
|
|
40136
|
+
}
|
|
40137
|
+
|
|
40138
|
+
var t = arr[k];
|
|
40139
|
+
var i = left;
|
|
40140
|
+
var j = right;
|
|
40141
|
+
|
|
40142
|
+
swap(arr, left, k);
|
|
40143
|
+
if (compare(arr[right], t) > 0) swap(arr, left, right);
|
|
40144
|
+
|
|
40145
|
+
while (i < j) {
|
|
40146
|
+
swap(arr, i, j);
|
|
40147
|
+
i++;
|
|
40148
|
+
j--;
|
|
40149
|
+
while (compare(arr[i], t) < 0) i++;
|
|
40150
|
+
while (compare(arr[j], t) > 0) j--;
|
|
40151
|
+
}
|
|
40152
|
+
|
|
40153
|
+
if (compare(arr[left], t) === 0) swap(arr, left, j);
|
|
40154
|
+
else {
|
|
40155
|
+
j++;
|
|
40156
|
+
swap(arr, j, right);
|
|
40157
|
+
}
|
|
40158
|
+
|
|
40159
|
+
if (j <= k) left = j + 1;
|
|
40160
|
+
if (k <= j) right = j - 1;
|
|
40161
|
+
}
|
|
40162
|
+
}
|
|
40163
|
+
|
|
40164
|
+
function swap(arr, i, j) {
|
|
40165
|
+
var tmp = arr[i];
|
|
40166
|
+
arr[i] = arr[j];
|
|
40167
|
+
arr[j] = tmp;
|
|
40168
|
+
}
|
|
40169
|
+
|
|
40170
|
+
function defaultCompare(a, b) {
|
|
40171
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
40172
|
+
}
|
|
40173
|
+
|
|
40174
|
+
class RBush {
|
|
40175
|
+
constructor(maxEntries = 9) {
|
|
40176
|
+
// max entries in a node is 9 by default; min node fill is 40% for best performance
|
|
40177
|
+
this._maxEntries = Math.max(4, maxEntries);
|
|
40178
|
+
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
|
40179
|
+
this.clear();
|
|
40180
|
+
}
|
|
40181
|
+
|
|
40182
|
+
all() {
|
|
40183
|
+
return this._all(this.data, []);
|
|
40184
|
+
}
|
|
40185
|
+
|
|
40186
|
+
search(bbox) {
|
|
40187
|
+
let node = this.data;
|
|
40188
|
+
const result = [];
|
|
40189
|
+
|
|
40190
|
+
if (!intersects(bbox, node)) return result;
|
|
40191
|
+
|
|
40192
|
+
const toBBox = this.toBBox;
|
|
40193
|
+
const nodesToSearch = [];
|
|
40194
|
+
|
|
40195
|
+
while (node) {
|
|
40196
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
40197
|
+
const child = node.children[i];
|
|
40198
|
+
const childBBox = node.leaf ? toBBox(child) : child;
|
|
40199
|
+
|
|
40200
|
+
if (intersects(bbox, childBBox)) {
|
|
40201
|
+
if (node.leaf) result.push(child);
|
|
40202
|
+
else if (contains(bbox, childBBox)) this._all(child, result);
|
|
40203
|
+
else nodesToSearch.push(child);
|
|
40204
|
+
}
|
|
40205
|
+
}
|
|
40206
|
+
node = nodesToSearch.pop();
|
|
40207
|
+
}
|
|
40208
|
+
|
|
40209
|
+
return result;
|
|
40210
|
+
}
|
|
40211
|
+
|
|
40212
|
+
collides(bbox) {
|
|
40213
|
+
let node = this.data;
|
|
40214
|
+
|
|
40215
|
+
if (!intersects(bbox, node)) return false;
|
|
40216
|
+
|
|
40217
|
+
const nodesToSearch = [];
|
|
40218
|
+
while (node) {
|
|
40219
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
40220
|
+
const child = node.children[i];
|
|
40221
|
+
const childBBox = node.leaf ? this.toBBox(child) : child;
|
|
40222
|
+
|
|
40223
|
+
if (intersects(bbox, childBBox)) {
|
|
40224
|
+
if (node.leaf || contains(bbox, childBBox)) return true;
|
|
40225
|
+
nodesToSearch.push(child);
|
|
40226
|
+
}
|
|
40227
|
+
}
|
|
40228
|
+
node = nodesToSearch.pop();
|
|
40229
|
+
}
|
|
40230
|
+
|
|
40231
|
+
return false;
|
|
40232
|
+
}
|
|
40233
|
+
|
|
40234
|
+
load(data) {
|
|
40235
|
+
if (!(data && data.length)) return this;
|
|
40236
|
+
|
|
40237
|
+
if (data.length < this._minEntries) {
|
|
40238
|
+
for (let i = 0; i < data.length; i++) {
|
|
40239
|
+
this.insert(data[i]);
|
|
40240
|
+
}
|
|
40241
|
+
return this;
|
|
40242
|
+
}
|
|
40243
|
+
|
|
40244
|
+
// recursively build the tree with the given data from scratch using OMT algorithm
|
|
40245
|
+
let node = this._build(data.slice(), 0, data.length - 1, 0);
|
|
40246
|
+
|
|
40247
|
+
if (!this.data.children.length) {
|
|
40248
|
+
// save as is if tree is empty
|
|
40249
|
+
this.data = node;
|
|
40250
|
+
|
|
40251
|
+
} else if (this.data.height === node.height) {
|
|
40252
|
+
// split root if trees have the same height
|
|
40253
|
+
this._splitRoot(this.data, node);
|
|
40254
|
+
|
|
40255
|
+
} else {
|
|
40256
|
+
if (this.data.height < node.height) {
|
|
40257
|
+
// swap trees if inserted one is bigger
|
|
40258
|
+
const tmpNode = this.data;
|
|
40259
|
+
this.data = node;
|
|
40260
|
+
node = tmpNode;
|
|
40261
|
+
}
|
|
40262
|
+
|
|
40263
|
+
// insert the small tree into the large tree at appropriate level
|
|
40264
|
+
this._insert(node, this.data.height - node.height - 1, true);
|
|
40265
|
+
}
|
|
40266
|
+
|
|
40267
|
+
return this;
|
|
40268
|
+
}
|
|
40269
|
+
|
|
40270
|
+
insert(item) {
|
|
40271
|
+
if (item) this._insert(item, this.data.height - 1);
|
|
40272
|
+
return this;
|
|
40273
|
+
}
|
|
40274
|
+
|
|
40275
|
+
clear() {
|
|
40276
|
+
this.data = createNode([]);
|
|
40277
|
+
return this;
|
|
40278
|
+
}
|
|
40279
|
+
|
|
40280
|
+
remove(item, equalsFn) {
|
|
40281
|
+
if (!item) return this;
|
|
40282
|
+
|
|
40283
|
+
let node = this.data;
|
|
40284
|
+
const bbox = this.toBBox(item);
|
|
40285
|
+
const path = [];
|
|
40286
|
+
const indexes = [];
|
|
40287
|
+
let i, parent, goingUp;
|
|
40288
|
+
|
|
40289
|
+
// depth-first iterative tree traversal
|
|
40290
|
+
while (node || path.length) {
|
|
40291
|
+
|
|
40292
|
+
if (!node) { // go up
|
|
40293
|
+
node = path.pop();
|
|
40294
|
+
parent = path[path.length - 1];
|
|
40295
|
+
i = indexes.pop();
|
|
40296
|
+
goingUp = true;
|
|
40297
|
+
}
|
|
40298
|
+
|
|
40299
|
+
if (node.leaf) { // check current node
|
|
40300
|
+
const index = findItem(item, node.children, equalsFn);
|
|
40301
|
+
|
|
40302
|
+
if (index !== -1) {
|
|
40303
|
+
// item found, remove the item and condense tree upwards
|
|
40304
|
+
node.children.splice(index, 1);
|
|
40305
|
+
path.push(node);
|
|
40306
|
+
this._condense(path);
|
|
40307
|
+
return this;
|
|
40308
|
+
}
|
|
40309
|
+
}
|
|
40310
|
+
|
|
40311
|
+
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
|
|
40312
|
+
path.push(node);
|
|
40313
|
+
indexes.push(i);
|
|
40314
|
+
i = 0;
|
|
40315
|
+
parent = node;
|
|
40316
|
+
node = node.children[0];
|
|
40317
|
+
|
|
40318
|
+
} else if (parent) { // go right
|
|
40319
|
+
i++;
|
|
40320
|
+
node = parent.children[i];
|
|
40321
|
+
goingUp = false;
|
|
40322
|
+
|
|
40323
|
+
} else node = null; // nothing found
|
|
40324
|
+
}
|
|
40325
|
+
|
|
40326
|
+
return this;
|
|
40327
|
+
}
|
|
40328
|
+
|
|
40329
|
+
toBBox(item) { return item; }
|
|
40330
|
+
|
|
40331
|
+
compareMinX(a, b) { return a.minX - b.minX; }
|
|
40332
|
+
compareMinY(a, b) { return a.minY - b.minY; }
|
|
40333
|
+
|
|
40334
|
+
toJSON() { return this.data; }
|
|
40335
|
+
|
|
40336
|
+
fromJSON(data) {
|
|
40337
|
+
this.data = data;
|
|
40338
|
+
return this;
|
|
40339
|
+
}
|
|
40340
|
+
|
|
40341
|
+
_all(node, result) {
|
|
40342
|
+
const nodesToSearch = [];
|
|
40343
|
+
while (node) {
|
|
40344
|
+
if (node.leaf) result.push(...node.children);
|
|
40345
|
+
else nodesToSearch.push(...node.children);
|
|
40346
|
+
|
|
40347
|
+
node = nodesToSearch.pop();
|
|
40348
|
+
}
|
|
40349
|
+
return result;
|
|
40350
|
+
}
|
|
40351
|
+
|
|
40352
|
+
_build(items, left, right, height) {
|
|
40353
|
+
|
|
40354
|
+
const N = right - left + 1;
|
|
40355
|
+
let M = this._maxEntries;
|
|
40356
|
+
let node;
|
|
40357
|
+
|
|
40358
|
+
if (N <= M) {
|
|
40359
|
+
// reached leaf level; return leaf
|
|
40360
|
+
node = createNode(items.slice(left, right + 1));
|
|
40361
|
+
calcBBox(node, this.toBBox);
|
|
40362
|
+
return node;
|
|
40363
|
+
}
|
|
40364
|
+
|
|
40365
|
+
if (!height) {
|
|
40366
|
+
// target height of the bulk-loaded tree
|
|
40367
|
+
height = Math.ceil(Math.log(N) / Math.log(M));
|
|
40368
|
+
|
|
40369
|
+
// target number of root entries to maximize storage utilization
|
|
40370
|
+
M = Math.ceil(N / Math.pow(M, height - 1));
|
|
40371
|
+
}
|
|
40372
|
+
|
|
40373
|
+
node = createNode([]);
|
|
40374
|
+
node.leaf = false;
|
|
40375
|
+
node.height = height;
|
|
40376
|
+
|
|
40377
|
+
// split the items into M mostly square tiles
|
|
40378
|
+
|
|
40379
|
+
const N2 = Math.ceil(N / M);
|
|
40380
|
+
const N1 = N2 * Math.ceil(Math.sqrt(M));
|
|
40381
|
+
|
|
40382
|
+
multiSelect(items, left, right, N1, this.compareMinX);
|
|
40383
|
+
|
|
40384
|
+
for (let i = left; i <= right; i += N1) {
|
|
40385
|
+
|
|
40386
|
+
const right2 = Math.min(i + N1 - 1, right);
|
|
40387
|
+
|
|
40388
|
+
multiSelect(items, i, right2, N2, this.compareMinY);
|
|
40389
|
+
|
|
40390
|
+
for (let j = i; j <= right2; j += N2) {
|
|
40391
|
+
|
|
40392
|
+
const right3 = Math.min(j + N2 - 1, right2);
|
|
40393
|
+
|
|
40394
|
+
// pack each entry recursively
|
|
40395
|
+
node.children.push(this._build(items, j, right3, height - 1));
|
|
40396
|
+
}
|
|
40397
|
+
}
|
|
40398
|
+
|
|
40399
|
+
calcBBox(node, this.toBBox);
|
|
40400
|
+
|
|
40401
|
+
return node;
|
|
40402
|
+
}
|
|
40403
|
+
|
|
40404
|
+
_chooseSubtree(bbox, node, level, path) {
|
|
40405
|
+
while (true) {
|
|
40406
|
+
path.push(node);
|
|
40407
|
+
|
|
40408
|
+
if (node.leaf || path.length - 1 === level) break;
|
|
40409
|
+
|
|
40410
|
+
let minArea = Infinity;
|
|
40411
|
+
let minEnlargement = Infinity;
|
|
40412
|
+
let targetNode;
|
|
40413
|
+
|
|
40414
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
40415
|
+
const child = node.children[i];
|
|
40416
|
+
const area = bboxArea(child);
|
|
40417
|
+
const enlargement = enlargedArea(bbox, child) - area;
|
|
40418
|
+
|
|
40419
|
+
// choose entry with the least area enlargement
|
|
40420
|
+
if (enlargement < minEnlargement) {
|
|
40421
|
+
minEnlargement = enlargement;
|
|
40422
|
+
minArea = area < minArea ? area : minArea;
|
|
40423
|
+
targetNode = child;
|
|
40424
|
+
|
|
40425
|
+
} else if (enlargement === minEnlargement) {
|
|
40426
|
+
// otherwise choose one with the smallest area
|
|
40427
|
+
if (area < minArea) {
|
|
40428
|
+
minArea = area;
|
|
40429
|
+
targetNode = child;
|
|
40430
|
+
}
|
|
40431
|
+
}
|
|
40432
|
+
}
|
|
40433
|
+
|
|
40434
|
+
node = targetNode || node.children[0];
|
|
40435
|
+
}
|
|
40436
|
+
|
|
40437
|
+
return node;
|
|
40438
|
+
}
|
|
40439
|
+
|
|
40440
|
+
_insert(item, level, isNode) {
|
|
40441
|
+
const bbox = isNode ? item : this.toBBox(item);
|
|
40442
|
+
const insertPath = [];
|
|
40443
|
+
|
|
40444
|
+
// find the best node for accommodating the item, saving all nodes along the path too
|
|
40445
|
+
const node = this._chooseSubtree(bbox, this.data, level, insertPath);
|
|
40446
|
+
|
|
40447
|
+
// put the item into the node
|
|
40448
|
+
node.children.push(item);
|
|
40449
|
+
extend(node, bbox);
|
|
40450
|
+
|
|
40451
|
+
// split on node overflow; propagate upwards if necessary
|
|
40452
|
+
while (level >= 0) {
|
|
40453
|
+
if (insertPath[level].children.length > this._maxEntries) {
|
|
40454
|
+
this._split(insertPath, level);
|
|
40455
|
+
level--;
|
|
40456
|
+
} else break;
|
|
40457
|
+
}
|
|
40458
|
+
|
|
40459
|
+
// adjust bboxes along the insertion path
|
|
40460
|
+
this._adjustParentBBoxes(bbox, insertPath, level);
|
|
40461
|
+
}
|
|
40462
|
+
|
|
40463
|
+
// split overflowed node into two
|
|
40464
|
+
_split(insertPath, level) {
|
|
40465
|
+
const node = insertPath[level];
|
|
40466
|
+
const M = node.children.length;
|
|
40467
|
+
const m = this._minEntries;
|
|
40468
|
+
|
|
40469
|
+
this._chooseSplitAxis(node, m, M);
|
|
40470
|
+
|
|
40471
|
+
const splitIndex = this._chooseSplitIndex(node, m, M);
|
|
40472
|
+
|
|
40473
|
+
const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
|
|
40474
|
+
newNode.height = node.height;
|
|
40475
|
+
newNode.leaf = node.leaf;
|
|
40476
|
+
|
|
40477
|
+
calcBBox(node, this.toBBox);
|
|
40478
|
+
calcBBox(newNode, this.toBBox);
|
|
40479
|
+
|
|
40480
|
+
if (level) insertPath[level - 1].children.push(newNode);
|
|
40481
|
+
else this._splitRoot(node, newNode);
|
|
40482
|
+
}
|
|
40483
|
+
|
|
40484
|
+
_splitRoot(node, newNode) {
|
|
40485
|
+
// split root node
|
|
40486
|
+
this.data = createNode([node, newNode]);
|
|
40487
|
+
this.data.height = node.height + 1;
|
|
40488
|
+
this.data.leaf = false;
|
|
40489
|
+
calcBBox(this.data, this.toBBox);
|
|
40490
|
+
}
|
|
40491
|
+
|
|
40492
|
+
_chooseSplitIndex(node, m, M) {
|
|
40493
|
+
let index;
|
|
40494
|
+
let minOverlap = Infinity;
|
|
40495
|
+
let minArea = Infinity;
|
|
40496
|
+
|
|
40497
|
+
for (let i = m; i <= M - m; i++) {
|
|
40498
|
+
const bbox1 = distBBox(node, 0, i, this.toBBox);
|
|
40499
|
+
const bbox2 = distBBox(node, i, M, this.toBBox);
|
|
40500
|
+
|
|
40501
|
+
const overlap = intersectionArea(bbox1, bbox2);
|
|
40502
|
+
const area = bboxArea(bbox1) + bboxArea(bbox2);
|
|
40503
|
+
|
|
40504
|
+
// choose distribution with minimum overlap
|
|
40505
|
+
if (overlap < minOverlap) {
|
|
40506
|
+
minOverlap = overlap;
|
|
40507
|
+
index = i;
|
|
40508
|
+
|
|
40509
|
+
minArea = area < minArea ? area : minArea;
|
|
40510
|
+
|
|
40511
|
+
} else if (overlap === minOverlap) {
|
|
40512
|
+
// otherwise choose distribution with minimum area
|
|
40513
|
+
if (area < minArea) {
|
|
40514
|
+
minArea = area;
|
|
40515
|
+
index = i;
|
|
40516
|
+
}
|
|
40517
|
+
}
|
|
40518
|
+
}
|
|
40519
|
+
|
|
40520
|
+
return index || M - m;
|
|
40521
|
+
}
|
|
40522
|
+
|
|
40523
|
+
// sorts node children by the best axis for split
|
|
40524
|
+
_chooseSplitAxis(node, m, M) {
|
|
40525
|
+
const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
|
|
40526
|
+
const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
|
|
40527
|
+
const xMargin = this._allDistMargin(node, m, M, compareMinX);
|
|
40528
|
+
const yMargin = this._allDistMargin(node, m, M, compareMinY);
|
|
40529
|
+
|
|
40530
|
+
// if total distributions margin value is minimal for x, sort by minX,
|
|
40531
|
+
// otherwise it's already sorted by minY
|
|
40532
|
+
if (xMargin < yMargin) node.children.sort(compareMinX);
|
|
40533
|
+
}
|
|
40534
|
+
|
|
40535
|
+
// total margin of all possible split distributions where each node is at least m full
|
|
40536
|
+
_allDistMargin(node, m, M, compare) {
|
|
40537
|
+
node.children.sort(compare);
|
|
40538
|
+
|
|
40539
|
+
const toBBox = this.toBBox;
|
|
40540
|
+
const leftBBox = distBBox(node, 0, m, toBBox);
|
|
40541
|
+
const rightBBox = distBBox(node, M - m, M, toBBox);
|
|
40542
|
+
let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
|
|
40543
|
+
|
|
40544
|
+
for (let i = m; i < M - m; i++) {
|
|
40545
|
+
const child = node.children[i];
|
|
40546
|
+
extend(leftBBox, node.leaf ? toBBox(child) : child);
|
|
40547
|
+
margin += bboxMargin(leftBBox);
|
|
40548
|
+
}
|
|
40549
|
+
|
|
40550
|
+
for (let i = M - m - 1; i >= m; i--) {
|
|
40551
|
+
const child = node.children[i];
|
|
40552
|
+
extend(rightBBox, node.leaf ? toBBox(child) : child);
|
|
40553
|
+
margin += bboxMargin(rightBBox);
|
|
40554
|
+
}
|
|
40555
|
+
|
|
40556
|
+
return margin;
|
|
40557
|
+
}
|
|
40558
|
+
|
|
40559
|
+
_adjustParentBBoxes(bbox, path, level) {
|
|
40560
|
+
// adjust bboxes along the given tree path
|
|
40561
|
+
for (let i = level; i >= 0; i--) {
|
|
40562
|
+
extend(path[i], bbox);
|
|
40563
|
+
}
|
|
40564
|
+
}
|
|
40565
|
+
|
|
40566
|
+
_condense(path) {
|
|
40567
|
+
// go through the path, removing empty nodes and updating bboxes
|
|
40568
|
+
for (let i = path.length - 1, siblings; i >= 0; i--) {
|
|
40569
|
+
if (path[i].children.length === 0) {
|
|
40570
|
+
if (i > 0) {
|
|
40571
|
+
siblings = path[i - 1].children;
|
|
40572
|
+
siblings.splice(siblings.indexOf(path[i]), 1);
|
|
40573
|
+
|
|
40574
|
+
} else this.clear();
|
|
40575
|
+
|
|
40576
|
+
} else calcBBox(path[i], this.toBBox);
|
|
40577
|
+
}
|
|
40578
|
+
}
|
|
40579
|
+
}
|
|
40580
|
+
|
|
40581
|
+
function findItem(item, items, equalsFn) {
|
|
40582
|
+
if (!equalsFn) return items.indexOf(item);
|
|
40583
|
+
|
|
40584
|
+
for (let i = 0; i < items.length; i++) {
|
|
40585
|
+
if (equalsFn(item, items[i])) return i;
|
|
40586
|
+
}
|
|
40587
|
+
return -1;
|
|
40588
|
+
}
|
|
40589
|
+
|
|
40590
|
+
// calculate node's bbox from bboxes of its children
|
|
40591
|
+
function calcBBox(node, toBBox) {
|
|
40592
|
+
distBBox(node, 0, node.children.length, toBBox, node);
|
|
40593
|
+
}
|
|
40594
|
+
|
|
40595
|
+
// min bounding rectangle of node children from k to p-1
|
|
40596
|
+
function distBBox(node, k, p, toBBox, destNode) {
|
|
40597
|
+
if (!destNode) destNode = createNode(null);
|
|
40598
|
+
destNode.minX = Infinity;
|
|
40599
|
+
destNode.minY = Infinity;
|
|
40600
|
+
destNode.maxX = -Infinity;
|
|
40601
|
+
destNode.maxY = -Infinity;
|
|
40602
|
+
|
|
40603
|
+
for (let i = k; i < p; i++) {
|
|
40604
|
+
const child = node.children[i];
|
|
40605
|
+
extend(destNode, node.leaf ? toBBox(child) : child);
|
|
40606
|
+
}
|
|
40607
|
+
|
|
40608
|
+
return destNode;
|
|
40609
|
+
}
|
|
40610
|
+
|
|
40611
|
+
function extend(a, b) {
|
|
40612
|
+
a.minX = Math.min(a.minX, b.minX);
|
|
40613
|
+
a.minY = Math.min(a.minY, b.minY);
|
|
40614
|
+
a.maxX = Math.max(a.maxX, b.maxX);
|
|
40615
|
+
a.maxY = Math.max(a.maxY, b.maxY);
|
|
40616
|
+
return a;
|
|
40617
|
+
}
|
|
40618
|
+
|
|
40619
|
+
function compareNodeMinX(a, b) { return a.minX - b.minX; }
|
|
40620
|
+
function compareNodeMinY(a, b) { return a.minY - b.minY; }
|
|
40621
|
+
|
|
40622
|
+
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
|
|
40623
|
+
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
|
|
40624
|
+
|
|
40625
|
+
function enlargedArea(a, b) {
|
|
40626
|
+
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
|
|
40627
|
+
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
|
|
40628
|
+
}
|
|
40629
|
+
|
|
40630
|
+
function intersectionArea(a, b) {
|
|
40631
|
+
const minX = Math.max(a.minX, b.minX);
|
|
40632
|
+
const minY = Math.max(a.minY, b.minY);
|
|
40633
|
+
const maxX = Math.min(a.maxX, b.maxX);
|
|
40634
|
+
const maxY = Math.min(a.maxY, b.maxY);
|
|
40635
|
+
|
|
40636
|
+
return Math.max(0, maxX - minX) *
|
|
40637
|
+
Math.max(0, maxY - minY);
|
|
40638
|
+
}
|
|
40639
|
+
|
|
40640
|
+
function contains(a, b) {
|
|
40641
|
+
return a.minX <= b.minX &&
|
|
40642
|
+
a.minY <= b.minY &&
|
|
40643
|
+
b.maxX <= a.maxX &&
|
|
40644
|
+
b.maxY <= a.maxY;
|
|
40645
|
+
}
|
|
40646
|
+
|
|
40647
|
+
function intersects(a, b) {
|
|
40648
|
+
return b.minX <= a.maxX &&
|
|
40649
|
+
b.minY <= a.maxY &&
|
|
40650
|
+
b.maxX >= a.minX &&
|
|
40651
|
+
b.maxY >= a.minY;
|
|
40652
|
+
}
|
|
40653
|
+
|
|
40654
|
+
function createNode(children) {
|
|
40655
|
+
return {
|
|
40656
|
+
children,
|
|
40657
|
+
height: 1,
|
|
40658
|
+
leaf: true,
|
|
40659
|
+
minX: Infinity,
|
|
40660
|
+
minY: Infinity,
|
|
40661
|
+
maxX: -Infinity,
|
|
40662
|
+
maxY: -Infinity
|
|
40663
|
+
};
|
|
40664
|
+
}
|
|
40665
|
+
|
|
40666
|
+
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
|
|
40667
|
+
// combines selection algorithm with binary divide & conquer approach
|
|
40668
|
+
|
|
40669
|
+
function multiSelect(arr, left, right, n, compare) {
|
|
40670
|
+
const stack = [left, right];
|
|
40671
|
+
|
|
40672
|
+
while (stack.length) {
|
|
40673
|
+
right = stack.pop();
|
|
40674
|
+
left = stack.pop();
|
|
40675
|
+
|
|
40676
|
+
if (right - left <= n) continue;
|
|
40677
|
+
|
|
40678
|
+
const mid = left + Math.ceil((right - left) / n / 2) * n;
|
|
40679
|
+
quickselect(arr, mid, left, right, compare);
|
|
40680
|
+
|
|
40681
|
+
stack.push(left, mid, mid, right);
|
|
40682
|
+
}
|
|
40683
|
+
}
|
|
40684
|
+
|
|
40044
40685
|
/**
|
|
40045
|
-
*
|
|
40686
|
+
* R-Tree Data Structure
|
|
40687
|
+
*
|
|
40688
|
+
* R-Tree is a spatial data structure used for efficient indexing and querying
|
|
40689
|
+
* of multi-dimensional objects, particularly in geometric and spatial applications.
|
|
40690
|
+
*
|
|
40691
|
+
* It organizes objects into a tree hierarchy, grouping nearby objects together
|
|
40692
|
+
* in bounding boxes. Each node in the tree represents a bounding box that
|
|
40693
|
+
* contains its child nodes or leaf objects. This hierarchical structure allows
|
|
40694
|
+
* for faster spatial queries.
|
|
40695
|
+
*
|
|
40696
|
+
* @see https://en.wikipedia.org/wiki/R-tree
|
|
40697
|
+
*
|
|
40698
|
+
* Consider a 2D Space with four zones: A, B, C, D
|
|
40699
|
+
* +--------------------------+
|
|
40700
|
+
* | |
|
|
40701
|
+
* | +---+ +-------+ |
|
|
40702
|
+
* | | A | | B | |
|
|
40703
|
+
* | +---+ +-------+ |
|
|
40704
|
+
* | |
|
|
40705
|
+
* | |
|
|
40706
|
+
* | +---+ |
|
|
40707
|
+
* | | C | |
|
|
40708
|
+
* | +---+ |
|
|
40709
|
+
* | +-----------+ |
|
|
40710
|
+
* | | D | |
|
|
40711
|
+
* | +-----------+ |
|
|
40712
|
+
* | |
|
|
40713
|
+
* +--------------------------+
|
|
40714
|
+
*
|
|
40715
|
+
* It groups together zones that are spatially close into a minimum bounding box.
|
|
40716
|
+
* For example, A and B are grouped together in rectangle R1, and C and D are grouped
|
|
40717
|
+
* in R2.
|
|
40718
|
+
*
|
|
40719
|
+
* R0
|
|
40720
|
+
* +--------------------------+
|
|
40721
|
+
* | R1 |
|
|
40722
|
+
* | +-----------------+ |
|
|
40723
|
+
* | | A | | B | |
|
|
40724
|
+
* | +-----------------+ |
|
|
40725
|
+
* | |
|
|
40726
|
+
* | R2 |
|
|
40727
|
+
* | +---+---+---+ |
|
|
40728
|
+
* | | | C | | |
|
|
40729
|
+
* | | +---+ | |
|
|
40730
|
+
* | +-----------+ |
|
|
40731
|
+
* | | D | |
|
|
40732
|
+
* | +-----------+ |
|
|
40733
|
+
* | |
|
|
40734
|
+
* +--------------------------+
|
|
40735
|
+
*
|
|
40736
|
+
* The tree would look like this:
|
|
40737
|
+
* R0
|
|
40738
|
+
* / \
|
|
40739
|
+
* / \
|
|
40740
|
+
* R1 R2
|
|
40741
|
+
* | |
|
|
40742
|
+
* A,B C,D
|
|
40743
|
+
|
|
40744
|
+
* Choosing how to group the zones is crucial for the performance of the tree.
|
|
40745
|
+
* Key considerations include avoiding excessive empty space coverage and minimizing overlap
|
|
40746
|
+
* to reduce the number of subtrees processed during searches.
|
|
40747
|
+
*
|
|
40748
|
+
* Various heuristics exist for determining the optimal grouping strategy, such as "least enlargement"
|
|
40749
|
+
* which prioritizes grouping nodes resulting in the smallest increase in bounding box size. In cases where
|
|
40750
|
+
* the choice cannot be made based on this criterion due to the same enlargement for different groupings,
|
|
40751
|
+
* we then evaluate "least area," aiming to minimize the overall area of bounding boxes.
|
|
40752
|
+
*
|
|
40753
|
+
* This implementation is tailored for spreadsheet use, indexing objects associated
|
|
40754
|
+
* with a zone and a sheet.
|
|
40755
|
+
*
|
|
40756
|
+
* It uses the RBush library under the hood. One 2D RBush R-tree per sheet.
|
|
40757
|
+
* @see https://github.com/mourner/rbush
|
|
40758
|
+
*/
|
|
40759
|
+
class SpreadsheetRTree {
|
|
40760
|
+
/**
|
|
40761
|
+
* One 2D R-tree per sheet
|
|
40762
|
+
*/
|
|
40763
|
+
rTrees = {};
|
|
40764
|
+
/**
|
|
40765
|
+
* Bulk-inserts the given items into the tree. Bulk insertion is usually ~2-3 times
|
|
40766
|
+
* faster than inserting items one by one. After bulk loading (bulk insertion into
|
|
40767
|
+
* an empty tree), subsequent query performance is also ~20-30% better.
|
|
40768
|
+
*/
|
|
40769
|
+
constructor(items = []) {
|
|
40770
|
+
const rangesPerSheet = {};
|
|
40771
|
+
for (const item of items) {
|
|
40772
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40773
|
+
if (!rangesPerSheet[sheetId]) {
|
|
40774
|
+
rangesPerSheet[sheetId] = [];
|
|
40775
|
+
}
|
|
40776
|
+
rangesPerSheet[sheetId].push(item);
|
|
40777
|
+
}
|
|
40778
|
+
for (const sheetId in rangesPerSheet) {
|
|
40779
|
+
this.rTrees[sheetId] = new ZoneRBush();
|
|
40780
|
+
this.rTrees[sheetId].load(rangesPerSheet[sheetId]); // bulk-insert
|
|
40781
|
+
}
|
|
40782
|
+
}
|
|
40783
|
+
insert(item) {
|
|
40784
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40785
|
+
if (!this.rTrees[sheetId]) {
|
|
40786
|
+
this.rTrees[sheetId] = new ZoneRBush();
|
|
40787
|
+
}
|
|
40788
|
+
this.rTrees[sheetId].insert(item);
|
|
40789
|
+
}
|
|
40790
|
+
search({ zone, sheetId }) {
|
|
40791
|
+
if (!this.rTrees[sheetId]) {
|
|
40792
|
+
return [];
|
|
40793
|
+
}
|
|
40794
|
+
return this.rTrees[sheetId].search({
|
|
40795
|
+
minX: zone.left,
|
|
40796
|
+
minY: zone.top,
|
|
40797
|
+
maxX: zone.right,
|
|
40798
|
+
maxY: zone.bottom,
|
|
40799
|
+
});
|
|
40800
|
+
}
|
|
40801
|
+
remove(item) {
|
|
40802
|
+
const sheetId = item.boundingBox.sheetId;
|
|
40803
|
+
if (!this.rTrees[sheetId]) {
|
|
40804
|
+
return;
|
|
40805
|
+
}
|
|
40806
|
+
this.rTrees[sheetId].remove(item, deepEquals);
|
|
40807
|
+
}
|
|
40808
|
+
}
|
|
40809
|
+
/**
|
|
40810
|
+
* RBush extension to use zones as bounding boxes
|
|
40811
|
+
*/
|
|
40812
|
+
class ZoneRBush extends RBush {
|
|
40813
|
+
toBBox({ boundingBox }) {
|
|
40814
|
+
const zone = boundingBox.zone;
|
|
40815
|
+
return {
|
|
40816
|
+
minX: zone.left,
|
|
40817
|
+
minY: zone.top,
|
|
40818
|
+
maxX: zone.right,
|
|
40819
|
+
maxY: zone.bottom,
|
|
40820
|
+
};
|
|
40821
|
+
}
|
|
40822
|
+
compareMinX(a, b) {
|
|
40823
|
+
return a.boundingBox.zone.left - b.boundingBox.zone.left;
|
|
40824
|
+
}
|
|
40825
|
+
compareMinY(a, b) {
|
|
40826
|
+
return a.boundingBox.zone.top - b.boundingBox.zone.top;
|
|
40827
|
+
}
|
|
40828
|
+
}
|
|
40829
|
+
|
|
40830
|
+
/**
|
|
40831
|
+
* Implementation of a dependency Graph.
|
|
40046
40832
|
* The graph is used to evaluate the cells in the correct
|
|
40047
40833
|
* order, and should be updated each time a cell's content is modified
|
|
40048
40834
|
*
|
|
40835
|
+
* It uses an R-Tree data structure to efficiently find dependent cells.
|
|
40049
40836
|
*/
|
|
40050
40837
|
class FormulaDependencyGraph {
|
|
40051
|
-
|
|
40052
|
-
* Internal structure:
|
|
40053
|
-
* - key: a cell position (encoded as an integer)
|
|
40054
|
-
* - value: a set of cell positions that depends on the key
|
|
40055
|
-
*
|
|
40056
|
-
* Given
|
|
40057
|
-
* - A1:"= B1 + SQRT(B2)"
|
|
40058
|
-
* - C1:"= B1";
|
|
40059
|
-
* - C2:"= C1"
|
|
40060
|
-
*
|
|
40061
|
-
* we will have something like:
|
|
40062
|
-
* - B1 ---> (A1, C1) meaning A1 and C1 depends on B1
|
|
40063
|
-
* - B2 ---> (A1) meaning A1 depends on B2
|
|
40064
|
-
* - C1 ---> (C2) meaning C2 depends on C1
|
|
40065
|
-
*/
|
|
40066
|
-
inverseDependencies = new Map();
|
|
40838
|
+
encoder;
|
|
40067
40839
|
dependencies = new Map();
|
|
40840
|
+
rTree;
|
|
40841
|
+
constructor(encoder, data = []) {
|
|
40842
|
+
this.encoder = encoder;
|
|
40843
|
+
this.rTree = new SpreadsheetRTree(data);
|
|
40844
|
+
}
|
|
40068
40845
|
removeAllDependencies(formulaPositionId) {
|
|
40069
|
-
const
|
|
40070
|
-
if (!
|
|
40846
|
+
const ranges = this.dependencies.get(formulaPositionId);
|
|
40847
|
+
if (!ranges) {
|
|
40071
40848
|
return;
|
|
40072
40849
|
}
|
|
40073
|
-
for (const
|
|
40074
|
-
this.
|
|
40850
|
+
for (const range of ranges) {
|
|
40851
|
+
this.rTree.remove(range);
|
|
40075
40852
|
}
|
|
40076
40853
|
this.dependencies.delete(formulaPositionId);
|
|
40077
40854
|
}
|
|
40078
40855
|
addDependencies(formulaPositionId, dependencies) {
|
|
40079
|
-
|
|
40080
|
-
|
|
40081
|
-
|
|
40082
|
-
|
|
40083
|
-
|
|
40084
|
-
|
|
40085
|
-
|
|
40086
|
-
|
|
40856
|
+
const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
|
|
40857
|
+
data: formulaPositionId,
|
|
40858
|
+
boundingBox: {
|
|
40859
|
+
zone,
|
|
40860
|
+
sheetId,
|
|
40861
|
+
},
|
|
40862
|
+
}));
|
|
40863
|
+
for (const item of rTreeItems) {
|
|
40864
|
+
this.rTree.insert(item);
|
|
40087
40865
|
}
|
|
40088
40866
|
const existingDependencies = this.dependencies.get(formulaPositionId);
|
|
40089
40867
|
if (existingDependencies) {
|
|
40090
|
-
existingDependencies.push(...
|
|
40868
|
+
existingDependencies.push(...rTreeItems);
|
|
40091
40869
|
}
|
|
40092
40870
|
else {
|
|
40093
|
-
this.dependencies.set(formulaPositionId,
|
|
40871
|
+
this.dependencies.set(formulaPositionId, rTreeItems);
|
|
40094
40872
|
}
|
|
40095
40873
|
}
|
|
40096
40874
|
/**
|
|
@@ -40098,20 +40876,20 @@
|
|
|
40098
40876
|
* in the correct order they should be evaluated.
|
|
40099
40877
|
* This is called a topological ordering (excluding cycles)
|
|
40100
40878
|
*/
|
|
40101
|
-
getCellsDependingOn(
|
|
40879
|
+
getCellsDependingOn(ranges) {
|
|
40102
40880
|
const visited = new JetSet();
|
|
40103
|
-
const queue = Array.from(
|
|
40881
|
+
const queue = Array.from(ranges).reverse();
|
|
40104
40882
|
while (queue.length > 0) {
|
|
40105
|
-
const
|
|
40106
|
-
visited.add(
|
|
40107
|
-
const
|
|
40108
|
-
for (const
|
|
40109
|
-
if (!visited.has(
|
|
40110
|
-
queue.push(
|
|
40883
|
+
const range = queue.pop();
|
|
40884
|
+
visited.add(...this.encoder.encodeBoundingBox(range));
|
|
40885
|
+
const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
|
|
40886
|
+
for (const positionId of impactedPositionIds) {
|
|
40887
|
+
if (!visited.has(positionId)) {
|
|
40888
|
+
queue.push(this.encoder.decodeToBoundingBox(positionId));
|
|
40111
40889
|
}
|
|
40112
40890
|
}
|
|
40113
40891
|
}
|
|
40114
|
-
visited.delete(...
|
|
40892
|
+
visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
40115
40893
|
return visited;
|
|
40116
40894
|
}
|
|
40117
40895
|
}
|
|
@@ -40199,9 +40977,9 @@
|
|
|
40199
40977
|
context;
|
|
40200
40978
|
getters;
|
|
40201
40979
|
compilationParams;
|
|
40202
|
-
|
|
40980
|
+
encoder = new PositionBitsEncoder();
|
|
40203
40981
|
evaluatedCells = new Map();
|
|
40204
|
-
formulaDependencies = lazy(new FormulaDependencyGraph());
|
|
40982
|
+
formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
|
|
40205
40983
|
blockedArrayFormulas = new Set();
|
|
40206
40984
|
spreadingRelations = new SpreadingRelation();
|
|
40207
40985
|
constructor(context, getters) {
|
|
@@ -40210,23 +40988,23 @@
|
|
|
40210
40988
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40211
40989
|
}
|
|
40212
40990
|
getEvaluatedCell(position) {
|
|
40213
|
-
return (this.evaluatedCells.get(this.
|
|
40991
|
+
return (this.evaluatedCells.get(this.encoder.encode(position)) ||
|
|
40214
40992
|
createEvaluatedCell("", { locale: this.getters.getLocale() }));
|
|
40215
40993
|
}
|
|
40216
40994
|
getSpreadPositionsOf(position) {
|
|
40217
|
-
const positionId = this.
|
|
40995
|
+
const positionId = this.encoder.encode(position);
|
|
40218
40996
|
if (!this.spreadingRelations.isArrayFormula(positionId)) {
|
|
40219
40997
|
return [];
|
|
40220
40998
|
}
|
|
40221
|
-
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.
|
|
40999
|
+
return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
|
|
40222
41000
|
}
|
|
40223
41001
|
getArrayFormulaSpreadingOn(position) {
|
|
40224
|
-
const positionId = this.
|
|
41002
|
+
const positionId = this.encoder.encode(position);
|
|
40225
41003
|
const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
|
|
40226
|
-
return formulaPosition !== undefined ? this.
|
|
41004
|
+
return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
|
|
40227
41005
|
}
|
|
40228
41006
|
getEvaluatedPositions() {
|
|
40229
|
-
return [...this.evaluatedCells.keys()].map(this.
|
|
41007
|
+
return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
|
|
40230
41008
|
}
|
|
40231
41009
|
getArrayFormulaSpreadingOnId(positionId) {
|
|
40232
41010
|
if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
|
|
@@ -40236,7 +41014,7 @@
|
|
|
40236
41014
|
return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
|
|
40237
41015
|
}
|
|
40238
41016
|
updateDependencies(position) {
|
|
40239
|
-
const positionId = this.
|
|
41017
|
+
const positionId = this.encoder.encode(position);
|
|
40240
41018
|
this.formulaDependencies().removeAllDependencies(positionId);
|
|
40241
41019
|
const dependencies = this.getDirectDependencies(positionId);
|
|
40242
41020
|
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
@@ -40246,12 +41024,12 @@
|
|
|
40246
41024
|
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
40247
41025
|
}
|
|
40248
41026
|
evaluateCells(positions) {
|
|
40249
|
-
const cells = positions.map(this.
|
|
41027
|
+
const cells = positions.map((p) => this.encoder.encode(p));
|
|
40250
41028
|
const cellsToCompute = new JetSet(cells);
|
|
40251
|
-
const
|
|
41029
|
+
const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
|
|
40252
41030
|
cellsToCompute.add(...this.getCellsDependingOn(cells));
|
|
40253
|
-
cellsToCompute.add(...
|
|
40254
|
-
cellsToCompute.add(...this.getCellsDependingOn(
|
|
41031
|
+
cellsToCompute.add(...arrayFormulasPositionIds);
|
|
41032
|
+
cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
|
|
40255
41033
|
this.evaluate(cellsToCompute);
|
|
40256
41034
|
}
|
|
40257
41035
|
getArrayFormulasImpactedByChangesOf(positionIds) {
|
|
@@ -40274,12 +41052,14 @@
|
|
|
40274
41052
|
this.blockedArrayFormulas = new Set();
|
|
40275
41053
|
this.spreadingRelations = new SpreadingRelation();
|
|
40276
41054
|
this.formulaDependencies = lazy(() => {
|
|
40277
|
-
const
|
|
40278
|
-
|
|
40279
|
-
|
|
40280
|
-
|
|
40281
|
-
|
|
40282
|
-
|
|
41055
|
+
const dependencies = [...this.getAllCells()].flatMap((positionId) => this.getDirectDependencies(positionId).map((range) => ({
|
|
41056
|
+
data: positionId,
|
|
41057
|
+
boundingBox: {
|
|
41058
|
+
zone: range.zone,
|
|
41059
|
+
sheetId: range.sheetId,
|
|
41060
|
+
},
|
|
41061
|
+
})));
|
|
41062
|
+
return new FormulaDependencyGraph(this.encoder, dependencies);
|
|
40283
41063
|
});
|
|
40284
41064
|
}
|
|
40285
41065
|
evaluateAllCells() {
|
|
@@ -40301,7 +41081,7 @@
|
|
|
40301
41081
|
for (const sheetId of this.getters.getSheetIds()) {
|
|
40302
41082
|
const cellIds = this.getters.getCells(sheetId);
|
|
40303
41083
|
for (const cellId in cellIds) {
|
|
40304
|
-
positionIds.add(this.
|
|
41084
|
+
positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
|
|
40305
41085
|
}
|
|
40306
41086
|
}
|
|
40307
41087
|
return positionIds;
|
|
@@ -40346,7 +41126,7 @@
|
|
|
40346
41126
|
if (!this.blockedArrayFormulas.has(positionId)) {
|
|
40347
41127
|
this.invalidateSpreading(positionId);
|
|
40348
41128
|
}
|
|
40349
|
-
const cellPosition = this.
|
|
41129
|
+
const cellPosition = this.encoder.decode(positionId);
|
|
40350
41130
|
const cell = this.getters.getCell(cellPosition);
|
|
40351
41131
|
if (cell === undefined) {
|
|
40352
41132
|
return createEvaluatedCell("", { locale: this.getters.getLocale() });
|
|
@@ -40369,7 +41149,7 @@
|
|
|
40369
41149
|
}
|
|
40370
41150
|
}
|
|
40371
41151
|
computeAndSave(position) {
|
|
40372
|
-
const positionId = this.
|
|
41152
|
+
const positionId = this.encoder.encode(position);
|
|
40373
41153
|
const evaluatedCell = this.computeCell(positionId);
|
|
40374
41154
|
if (!this.evaluatedCells.has(positionId)) {
|
|
40375
41155
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
@@ -40424,15 +41204,15 @@
|
|
|
40424
41204
|
throw new Error(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
|
|
40425
41205
|
}
|
|
40426
41206
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
40427
|
-
const arrayFormulaPositionId = this.
|
|
41207
|
+
const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40428
41208
|
return (i, j) => {
|
|
40429
41209
|
const position = { sheetId, col: i + col, row: j + row };
|
|
40430
|
-
const resultPositionId = this.
|
|
41210
|
+
const resultPositionId = this.encoder.encode(position);
|
|
40431
41211
|
this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
|
|
40432
41212
|
};
|
|
40433
41213
|
}
|
|
40434
41214
|
checkCollision({ sheetId, col, row }) {
|
|
40435
|
-
const formulaPositionId = this.
|
|
41215
|
+
const formulaPositionId = this.encoder.encode({ sheetId, col, row });
|
|
40436
41216
|
return (i, j) => {
|
|
40437
41217
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
40438
41218
|
const rawCell = this.getters.getCell(position);
|
|
@@ -40453,7 +41233,7 @@
|
|
|
40453
41233
|
format: format || matrixResult[i][j]?.format,
|
|
40454
41234
|
locale: this.getters.getLocale(),
|
|
40455
41235
|
});
|
|
40456
|
-
const positionId = this.
|
|
41236
|
+
const positionId = this.encoder.encode(position);
|
|
40457
41237
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
40458
41238
|
// check if formula dependencies present in the spread zone
|
|
40459
41239
|
// if so, they need to be recomputed
|
|
@@ -40485,29 +41265,17 @@
|
|
|
40485
41265
|
if (!cell?.isFormula) {
|
|
40486
41266
|
return [];
|
|
40487
41267
|
}
|
|
40488
|
-
|
|
40489
|
-
for (const range of cell.compiledFormula.dependencies) {
|
|
40490
|
-
if (range.invalidSheetName || range.invalidXc) {
|
|
40491
|
-
continue;
|
|
40492
|
-
}
|
|
40493
|
-
const sheetId = range.sheetId;
|
|
40494
|
-
forEachPositionsInZone(range.zone, (col, row) => {
|
|
40495
|
-
dependencies.push(this.encodePosition({ sheetId, col, row }));
|
|
40496
|
-
});
|
|
40497
|
-
}
|
|
40498
|
-
return dependencies;
|
|
41268
|
+
return cell.compiledFormula.dependencies;
|
|
40499
41269
|
}
|
|
40500
41270
|
getCellsDependingOn(positionIds) {
|
|
40501
|
-
|
|
41271
|
+
const ranges = [];
|
|
41272
|
+
for (const positionId of positionIds) {
|
|
41273
|
+
ranges.push(this.encoder.decodeToBoundingBox(positionId));
|
|
41274
|
+
}
|
|
41275
|
+
return this.formulaDependencies().getCellsDependingOn(ranges);
|
|
40502
41276
|
}
|
|
40503
41277
|
getCell(positionId) {
|
|
40504
|
-
return this.getters.getCell(this.
|
|
40505
|
-
}
|
|
40506
|
-
encodePosition(position) {
|
|
40507
|
-
return this.positionEncoder.encode(position);
|
|
40508
|
-
}
|
|
40509
|
-
decodePosition(positionId) {
|
|
40510
|
-
return this.positionEncoder.decode(positionId);
|
|
41278
|
+
return this.getters.getCell(this.encoder.decode(positionId));
|
|
40511
41279
|
}
|
|
40512
41280
|
}
|
|
40513
41281
|
function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
@@ -40563,6 +41331,13 @@
|
|
|
40563
41331
|
encode({ sheetId, col, row }) {
|
|
40564
41332
|
return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
|
|
40565
41333
|
}
|
|
41334
|
+
encodeBoundingBox({ sheetId, zone }) {
|
|
41335
|
+
const positions = [];
|
|
41336
|
+
forEachPositionsInZone(zone, (col, row) => {
|
|
41337
|
+
positions.push(this.encode({ sheetId, col, row }));
|
|
41338
|
+
});
|
|
41339
|
+
return positions;
|
|
41340
|
+
}
|
|
40566
41341
|
decode(id) {
|
|
40567
41342
|
// keep only the last 21 bits by AND-ing the bit sequence with 21 ones
|
|
40568
41343
|
const row = Number(id & 2097151n);
|
|
@@ -40570,6 +41345,10 @@
|
|
|
40570
41345
|
const sheetId = this.decodeSheet(id >> 42n);
|
|
40571
41346
|
return { sheetId, col, row };
|
|
40572
41347
|
}
|
|
41348
|
+
decodeToBoundingBox(id) {
|
|
41349
|
+
const { sheetId, col, row } = this.decode(id);
|
|
41350
|
+
return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
|
|
41351
|
+
}
|
|
40573
41352
|
encodeSheet(sheetId) {
|
|
40574
41353
|
const sheetKey = this.sheetMapping[sheetId];
|
|
40575
41354
|
if (sheetKey === undefined) {
|
|
@@ -40752,7 +41531,12 @@
|
|
|
40752
41531
|
// Getters
|
|
40753
41532
|
// ---------------------------------------------------------------------------
|
|
40754
41533
|
evaluateFormula(sheetId, formulaString) {
|
|
40755
|
-
|
|
41534
|
+
try {
|
|
41535
|
+
return this.evaluator.evaluateFormula(sheetId, formulaString);
|
|
41536
|
+
}
|
|
41537
|
+
catch (error) {
|
|
41538
|
+
return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
|
|
41539
|
+
}
|
|
40756
41540
|
}
|
|
40757
41541
|
/**
|
|
40758
41542
|
* Return the value of each cell in the range as they are displayed in the grid.
|
|
@@ -41021,7 +41805,7 @@
|
|
|
41021
41805
|
}
|
|
41022
41806
|
|
|
41023
41807
|
class EvaluationChartPlugin extends UIPlugin {
|
|
41024
|
-
static getters = ["getChartRuntime", "
|
|
41808
|
+
static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
|
|
41025
41809
|
charts = {};
|
|
41026
41810
|
createRuntimeChart = chartRuntimeFactory(this.getters);
|
|
41027
41811
|
handle(cmd) {
|
|
@@ -41059,25 +41843,26 @@
|
|
|
41059
41843
|
return this.charts[figureId];
|
|
41060
41844
|
}
|
|
41061
41845
|
/**
|
|
41062
|
-
* Get the background
|
|
41063
|
-
* of the chart. In order of priority, it will return :
|
|
41064
|
-
*
|
|
41065
|
-
* - the chart background color if one is defined
|
|
41066
|
-
* - the fill color of the cell if one is defined
|
|
41067
|
-
* - the fill color of the cell from conditional formats if one is defined
|
|
41068
|
-
* - the default chart color if no other color is defined
|
|
41846
|
+
* Get the background and textColor of a chart based on the color of the first cell of the main range of the chart.
|
|
41069
41847
|
*/
|
|
41070
|
-
|
|
41848
|
+
getStyleOfSingleCellChart(chartBackground, mainRange) {
|
|
41071
41849
|
if (chartBackground)
|
|
41072
|
-
return chartBackground;
|
|
41850
|
+
return { background: chartBackground, fontColor: chartFontColor(chartBackground) };
|
|
41073
41851
|
if (!mainRange) {
|
|
41074
|
-
return
|
|
41852
|
+
return {
|
|
41853
|
+
background: BACKGROUND_CHART_COLOR,
|
|
41854
|
+
fontColor: chartFontColor(BACKGROUND_CHART_COLOR),
|
|
41855
|
+
};
|
|
41075
41856
|
}
|
|
41076
41857
|
const col = mainRange.zone.left;
|
|
41077
41858
|
const row = mainRange.zone.top;
|
|
41078
41859
|
const sheetId = mainRange.sheetId;
|
|
41079
41860
|
const style = this.getters.getCellComputedStyle({ sheetId, col, row });
|
|
41080
|
-
|
|
41861
|
+
const background = style.fillColor || BACKGROUND_CHART_COLOR;
|
|
41862
|
+
return {
|
|
41863
|
+
background,
|
|
41864
|
+
fontColor: style.textColor || chartFontColor(background),
|
|
41865
|
+
};
|
|
41081
41866
|
}
|
|
41082
41867
|
exportForExcel(data) {
|
|
41083
41868
|
for (const sheet of data.sheets) {
|
|
@@ -41133,7 +41918,7 @@
|
|
|
41133
41918
|
// ---------------------------------------------------------------------------
|
|
41134
41919
|
handle(cmd) {
|
|
41135
41920
|
if (invalidateCFEvaluationCommands.has(cmd.type) ||
|
|
41136
|
-
(cmd.type === "UPDATE_CELL" && "content" in cmd)) {
|
|
41921
|
+
(cmd.type === "UPDATE_CELL" && ("content" in cmd || "format" in cmd))) {
|
|
41137
41922
|
this.isStale = true;
|
|
41138
41923
|
}
|
|
41139
41924
|
}
|
|
@@ -41188,45 +41973,40 @@
|
|
|
41188
41973
|
getComputedStyles(sheetId) {
|
|
41189
41974
|
const computedStyle = {};
|
|
41190
41975
|
for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
|
|
41191
|
-
|
|
41192
|
-
|
|
41193
|
-
|
|
41194
|
-
|
|
41195
|
-
|
|
41196
|
-
|
|
41197
|
-
|
|
41198
|
-
|
|
41199
|
-
|
|
41200
|
-
|
|
41201
|
-
|
|
41202
|
-
for (let
|
|
41203
|
-
|
|
41204
|
-
|
|
41205
|
-
|
|
41206
|
-
const
|
|
41207
|
-
|
|
41208
|
-
|
|
41209
|
-
|
|
41210
|
-
|
|
41211
|
-
|
|
41212
|
-
});
|
|
41213
|
-
}
|
|
41214
|
-
return value;
|
|
41215
|
-
});
|
|
41216
|
-
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
41217
|
-
if (!computedStyle[col])
|
|
41218
|
-
computedStyle[col] = [];
|
|
41219
|
-
// we must combine all the properties of all the CF rules applied to the given cell
|
|
41220
|
-
computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
|
|
41976
|
+
switch (cf.rule.type) {
|
|
41977
|
+
case "ColorScaleRule":
|
|
41978
|
+
for (let range of cf.ranges) {
|
|
41979
|
+
this.applyColorScale(sheetId, range, cf.rule, computedStyle);
|
|
41980
|
+
}
|
|
41981
|
+
break;
|
|
41982
|
+
case "CellIsRule":
|
|
41983
|
+
const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
|
|
41984
|
+
for (let ref of cf.ranges) {
|
|
41985
|
+
const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
|
|
41986
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
41987
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
41988
|
+
const predicate = this.rulePredicate[cf.rule.type];
|
|
41989
|
+
const target = { sheetId, col, row };
|
|
41990
|
+
const values = cf.rule.values.map((value, i) => {
|
|
41991
|
+
const compiledFormula = formulas[i];
|
|
41992
|
+
if (compiledFormula) {
|
|
41993
|
+
return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
|
|
41994
|
+
...compiledFormula,
|
|
41995
|
+
dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
|
|
41996
|
+
});
|
|
41221
41997
|
}
|
|
41998
|
+
return value;
|
|
41999
|
+
});
|
|
42000
|
+
if (predicate && predicate(target, { ...cf.rule, values })) {
|
|
42001
|
+
if (!computedStyle[col])
|
|
42002
|
+
computedStyle[col] = [];
|
|
42003
|
+
// we must combine all the properties of all the CF rules applied to the given cell
|
|
42004
|
+
computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
|
|
41222
42005
|
}
|
|
41223
42006
|
}
|
|
41224
42007
|
}
|
|
41225
|
-
|
|
41226
|
-
|
|
41227
|
-
}
|
|
41228
|
-
catch (_) {
|
|
41229
|
-
// we don't care about the errors within the evaluation of a rule
|
|
42008
|
+
}
|
|
42009
|
+
break;
|
|
41230
42010
|
}
|
|
41231
42011
|
}
|
|
41232
42012
|
return computedStyle;
|
|
@@ -41459,7 +42239,7 @@
|
|
|
41459
42239
|
handle(cmd) {
|
|
41460
42240
|
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
41461
42241
|
cmd.type === "EVALUATE_CELLS" ||
|
|
41462
|
-
(cmd.type === "UPDATE_CELL" && "content" in cmd)) {
|
|
42242
|
+
(cmd.type === "UPDATE_CELL" && ("content" in cmd || "format" in cmd))) {
|
|
41463
42243
|
this.validationResults = {};
|
|
41464
42244
|
return;
|
|
41465
42245
|
}
|
|
@@ -41837,11 +42617,6 @@
|
|
|
41837
42617
|
return "Success" /* CommandResult.Success */;
|
|
41838
42618
|
}
|
|
41839
42619
|
return "InvalidAutofillSelection" /* CommandResult.InvalidAutofillSelection */;
|
|
41840
|
-
case "AUTOFILL_AUTO":
|
|
41841
|
-
const zone = this.getters.getSelectedZone();
|
|
41842
|
-
return zone.top === zone.bottom
|
|
41843
|
-
? "Success" /* CommandResult.Success */
|
|
41844
|
-
: "CancelledForUnknownReason" /* CommandResult.CancelledForUnknownReason */;
|
|
41845
42620
|
}
|
|
41846
42621
|
return "Success" /* CommandResult.Success */;
|
|
41847
42622
|
}
|
|
@@ -41997,19 +42772,21 @@
|
|
|
41997
42772
|
let col = zone.left;
|
|
41998
42773
|
let row = zone.bottom;
|
|
41999
42774
|
if (col > 0) {
|
|
42000
|
-
let
|
|
42001
|
-
while (
|
|
42775
|
+
let leftPosition = { sheetId, col: col - 1, row };
|
|
42776
|
+
while (this.getters.getCorrespondingFormulaCell(leftPosition) ||
|
|
42777
|
+
this.getters.getCell(leftPosition)?.content) {
|
|
42002
42778
|
row += 1;
|
|
42003
|
-
|
|
42779
|
+
leftPosition = { sheetId, col: col - 1, row };
|
|
42004
42780
|
}
|
|
42005
42781
|
}
|
|
42006
42782
|
if (row === zone.bottom) {
|
|
42007
42783
|
col = zone.right;
|
|
42008
42784
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
42009
|
-
let
|
|
42010
|
-
while (
|
|
42785
|
+
let rightPosition = { sheetId, col: col + 1, row };
|
|
42786
|
+
while (this.getters.getCorrespondingFormulaCell(rightPosition) ||
|
|
42787
|
+
this.getters.getCell(rightPosition)?.content) {
|
|
42011
42788
|
row += 1;
|
|
42012
|
-
|
|
42789
|
+
rightPosition = { sheetId, col: col + 1, row };
|
|
42013
42790
|
}
|
|
42014
42791
|
}
|
|
42015
42792
|
}
|
|
@@ -43001,6 +43778,7 @@
|
|
|
43001
43778
|
isReplayingInitialRevisions = false;
|
|
43002
43779
|
processedRevisions = new Set();
|
|
43003
43780
|
uuidGenerator = new UuidGenerator();
|
|
43781
|
+
lastLocalOperation;
|
|
43004
43782
|
/**
|
|
43005
43783
|
* Manages the collaboration between multiple users on the same spreadsheet.
|
|
43006
43784
|
* It can forward local state changes to other users to ensure they all eventually
|
|
@@ -43033,6 +43811,11 @@
|
|
|
43033
43811
|
return;
|
|
43034
43812
|
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
|
|
43035
43813
|
this.revisions.append(revision.id, revision);
|
|
43814
|
+
// REQUEST_REDO just repeats the last operation, the
|
|
43815
|
+
// last operation is still the same and should not change.
|
|
43816
|
+
if (rootCommand.type !== "REQUEST_REDO") {
|
|
43817
|
+
this.lastLocalOperation = revision;
|
|
43818
|
+
}
|
|
43036
43819
|
this.trigger("new-local-state-update", { id: revision.id });
|
|
43037
43820
|
this.sendUpdateMessage({
|
|
43038
43821
|
type: "REMOTE_REVISION",
|
|
@@ -43129,19 +43912,10 @@
|
|
|
43129
43912
|
return this.pendingMessages.length === 0;
|
|
43130
43913
|
}
|
|
43131
43914
|
/**
|
|
43132
|
-
* Get the last local revision
|
|
43915
|
+
* Get the last local revision
|
|
43133
43916
|
* */
|
|
43134
|
-
getLastLocalNonEmptyRevision(
|
|
43135
|
-
|
|
43136
|
-
for (const rev of revisions) {
|
|
43137
|
-
if (rev.rootCommand === "SNAPSHOT")
|
|
43138
|
-
return undefined;
|
|
43139
|
-
if (!rev.rootCommand || rev.rootCommand === "REMOTE")
|
|
43140
|
-
continue;
|
|
43141
|
-
if (!ignoredRootCommands.includes(rev.rootCommand?.type) && rev.commands.length)
|
|
43142
|
-
return rev;
|
|
43143
|
-
}
|
|
43144
|
-
return undefined;
|
|
43917
|
+
getLastLocalNonEmptyRevision() {
|
|
43918
|
+
return this.lastLocalOperation;
|
|
43145
43919
|
}
|
|
43146
43920
|
_move(position) {
|
|
43147
43921
|
// this method is debounced and might be called after the client
|
|
@@ -43201,7 +43975,7 @@
|
|
|
43201
43975
|
break;
|
|
43202
43976
|
case "REMOTE_REVISION":
|
|
43203
43977
|
const { clientId, commands, timestamp } = message;
|
|
43204
|
-
const revision = new Revision(message.nextRevisionId, clientId, commands,
|
|
43978
|
+
const revision = new Revision(message.nextRevisionId, clientId, commands, undefined, undefined, timestamp);
|
|
43205
43979
|
if (revision.clientId !== this.clientId) {
|
|
43206
43980
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
43207
43981
|
const pendingCommands = this.pendingMessages
|
|
@@ -43214,10 +43988,11 @@
|
|
|
43214
43988
|
}
|
|
43215
43989
|
break;
|
|
43216
43990
|
case "SNAPSHOT_CREATED": {
|
|
43217
|
-
const revision = new Revision(message.nextRevisionId, "server", [],
|
|
43991
|
+
const revision = new Revision(message.nextRevisionId, "server", [], undefined, undefined, Date.now());
|
|
43218
43992
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
43219
43993
|
this.dropPendingHistoryMessages();
|
|
43220
43994
|
this.trigger("snapshot");
|
|
43995
|
+
this.lastLocalOperation = undefined;
|
|
43221
43996
|
break;
|
|
43222
43997
|
}
|
|
43223
43998
|
}
|
|
@@ -43603,7 +44378,7 @@
|
|
|
43603
44378
|
isPasteAllowed(target, clipboardOption) {
|
|
43604
44379
|
const sheetId = this.getters.getActiveSheetId();
|
|
43605
44380
|
if (this.operation === "CUT" && clipboardOption?.pasteOption !== undefined) {
|
|
43606
|
-
// cannot paste only format or
|
|
44381
|
+
// cannot paste only format or as value if the previous operation is a CUT
|
|
43607
44382
|
return "WrongPasteOption" /* CommandResult.WrongPasteOption */;
|
|
43608
44383
|
}
|
|
43609
44384
|
if (target.length > 1) {
|
|
@@ -43785,7 +44560,7 @@
|
|
|
43785
44560
|
// This condition is used to determine if we have to paste the CF or not.
|
|
43786
44561
|
// We have to do it when the command handled is "PASTE", not "INSERT_CELL"
|
|
43787
44562
|
// or "DELETE_CELL". So, the state should be the local state
|
|
43788
|
-
const shouldPasteCF = clipboardOptions?.pasteOption !== "
|
|
44563
|
+
const shouldPasteCF = clipboardOptions?.pasteOption !== "asValue" && clipboardOptions?.shouldPasteCF;
|
|
43789
44564
|
const shouldPasteDV = !clipboardOptions?.pasteOption;
|
|
43790
44565
|
const sheetId = this.getters.getActiveSheetId();
|
|
43791
44566
|
// first, add missing cols/rows if needed
|
|
@@ -43821,10 +44596,11 @@
|
|
|
43821
44596
|
pasteCell(origin, target, operation, clipboardOption) {
|
|
43822
44597
|
const { sheetId, col, row } = target;
|
|
43823
44598
|
const targetCell = this.getters.getEvaluatedCell(target);
|
|
43824
|
-
|
|
44599
|
+
const originFormat = origin.cell?.format ?? origin.evaluatedCell.format;
|
|
44600
|
+
if (clipboardOption?.pasteOption === "asValue") {
|
|
43825
44601
|
const locale = this.getters.getLocale();
|
|
43826
44602
|
const content = formatValue(origin.evaluatedCell.value, { locale });
|
|
43827
|
-
this.dispatch("UPDATE_CELL", { ...target, content });
|
|
44603
|
+
this.dispatch("UPDATE_CELL", { ...target, content, format: originFormat });
|
|
43828
44604
|
return;
|
|
43829
44605
|
}
|
|
43830
44606
|
const targetBorders = this.getters.getCellBorder(target);
|
|
@@ -43840,7 +44616,7 @@
|
|
|
43840
44616
|
this.dispatch("UPDATE_CELL", {
|
|
43841
44617
|
...target,
|
|
43842
44618
|
style: origin.cell?.style ?? null,
|
|
43843
|
-
format:
|
|
44619
|
+
format: originFormat ?? targetCell.format,
|
|
43844
44620
|
});
|
|
43845
44621
|
return;
|
|
43846
44622
|
}
|
|
@@ -45862,6 +46638,9 @@
|
|
|
45862
46638
|
// Other
|
|
45863
46639
|
// ---------------------------------------------------------------------------
|
|
45864
46640
|
initInput(id, initialRanges, inputHasSingleRange = false) {
|
|
46641
|
+
if (this.inputs[id]) {
|
|
46642
|
+
this.unfocus();
|
|
46643
|
+
}
|
|
45865
46644
|
this.inputs[id] = new SelectionInputPlugin(this.config, initialRanges, inputHasSingleRange);
|
|
45866
46645
|
if (initialRanges.length === 0) {
|
|
45867
46646
|
const input = this.inputs[id];
|
|
@@ -46570,7 +47349,7 @@
|
|
|
46570
47349
|
* Ignore standard undo/redo revisions (that are empty)
|
|
46571
47350
|
*/
|
|
46572
47351
|
getPossibleRevisionToRepeat() {
|
|
46573
|
-
return this.session.getLastLocalNonEmptyRevision(
|
|
47352
|
+
return this.session.getLastLocalNonEmptyRevision();
|
|
46574
47353
|
}
|
|
46575
47354
|
}
|
|
46576
47355
|
|
|
@@ -48041,6 +48820,12 @@
|
|
|
48041
48820
|
break;
|
|
48042
48821
|
case "HIDE_COLUMNS_ROWS":
|
|
48043
48822
|
case "UNHIDE_COLUMNS_ROWS":
|
|
48823
|
+
case "GROUP_HEADERS":
|
|
48824
|
+
case "UNGROUP_HEADERS":
|
|
48825
|
+
case "FOLD_HEADER_GROUP":
|
|
48826
|
+
case "UNFOLD_HEADER_GROUP":
|
|
48827
|
+
case "FOLD_ALL_HEADER_GROUPS":
|
|
48828
|
+
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
48044
48829
|
this.updateHiddenRows();
|
|
48045
48830
|
break;
|
|
48046
48831
|
case "UPDATE_FILTER":
|
|
@@ -48742,7 +49527,7 @@
|
|
|
48742
49527
|
sheetId: cmd.sheetId,
|
|
48743
49528
|
base: cmd.base,
|
|
48744
49529
|
quantity: thickness,
|
|
48745
|
-
position:
|
|
49530
|
+
position: cmd.position,
|
|
48746
49531
|
});
|
|
48747
49532
|
const isCol = cmd.dimension === "COL";
|
|
48748
49533
|
const start = cmd.elements[0];
|
|
@@ -48759,12 +49544,13 @@
|
|
|
48759
49544
|
},
|
|
48760
49545
|
];
|
|
48761
49546
|
const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
|
|
49547
|
+
const base = isBasedBefore ? cmd.base : cmd.base + 1;
|
|
48762
49548
|
const pasteTarget = [
|
|
48763
49549
|
{
|
|
48764
|
-
left: isCol ?
|
|
48765
|
-
right: isCol ?
|
|
48766
|
-
top: !isCol ?
|
|
48767
|
-
bottom: !isCol ?
|
|
49550
|
+
left: isCol ? base : 0,
|
|
49551
|
+
right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
49552
|
+
top: !isCol ? base : 0,
|
|
49553
|
+
bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
48768
49554
|
},
|
|
48769
49555
|
];
|
|
48770
49556
|
state.paste(pasteTarget, { selectTarget: true });
|
|
@@ -48799,6 +49585,11 @@
|
|
|
48799
49585
|
doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
|
|
48800
49586
|
return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
|
|
48801
49587
|
}
|
|
49588
|
+
const headers = [cmd.base, ...cmd.elements];
|
|
49589
|
+
const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
|
|
49590
|
+
if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
|
|
49591
|
+
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
49592
|
+
}
|
|
48802
49593
|
return "Success" /* CommandResult.Success */;
|
|
48803
49594
|
}
|
|
48804
49595
|
//-------------------------------------------
|
|
@@ -48976,9 +49767,6 @@
|
|
|
48976
49767
|
*/
|
|
48977
49768
|
adjustPosition(position) {
|
|
48978
49769
|
const sheetId = this.sheetId;
|
|
48979
|
-
if (!position) {
|
|
48980
|
-
position = this.getters.getSheetPosition(sheetId);
|
|
48981
|
-
}
|
|
48982
49770
|
const mainCellPosition = this.getters.getMainCellPosition({ sheetId, ...position });
|
|
48983
49771
|
const { col, row } = this.getters.getNextVisibleCellPosition(mainCellPosition);
|
|
48984
49772
|
if (isInside(col, this.boundaries.top, this.boundaries)) {
|
|
@@ -48991,56 +49779,50 @@
|
|
|
48991
49779
|
adjustPositionX(targetCol) {
|
|
48992
49780
|
const sheetId = this.sheetId;
|
|
48993
49781
|
const { end } = this.getters.getColDimensions(sheetId, targetCol);
|
|
48994
|
-
const maxCol = this.getters.getNumberCols(sheetId);
|
|
48995
49782
|
if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
|
|
49783
|
+
const maxCol = this.getters.getNumberCols(sheetId);
|
|
48996
49784
|
let finalTarget = targetCol;
|
|
48997
|
-
while (this.getters.isColHidden(sheetId, finalTarget) &&
|
|
49785
|
+
while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget < maxCol) {
|
|
48998
49786
|
finalTarget++;
|
|
48999
49787
|
}
|
|
49000
49788
|
const finalTargetEnd = this.getters.getColDimensions(sheetId, finalTarget).end;
|
|
49001
49789
|
const startIndex = this.searchHeaderIndex("COL", finalTargetEnd - this.viewportWidth - this.offsetCorrectionX, this.boundaries.left);
|
|
49002
|
-
this.
|
|
49790
|
+
this.offsetScrollbarX =
|
|
49003
49791
|
this.getters.getColDimensions(sheetId, startIndex).end - this.offsetCorrectionX;
|
|
49004
|
-
this.offsetScrollbarX = this.offsetX;
|
|
49005
|
-
this.adjustViewportZoneX();
|
|
49006
49792
|
}
|
|
49007
49793
|
else if (this.left > targetCol) {
|
|
49008
49794
|
let finalTarget = targetCol;
|
|
49009
|
-
while (this.getters.isColHidden(sheetId, finalTarget) &&
|
|
49795
|
+
while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget > 0) {
|
|
49010
49796
|
finalTarget--;
|
|
49011
49797
|
}
|
|
49012
|
-
this.
|
|
49798
|
+
this.offsetScrollbarX =
|
|
49013
49799
|
this.getters.getColDimensions(sheetId, finalTarget).start - this.offsetCorrectionX;
|
|
49014
|
-
this.offsetScrollbarX = this.offsetX;
|
|
49015
|
-
this.adjustViewportZoneX();
|
|
49016
49800
|
}
|
|
49801
|
+
this.adjustViewportZoneX();
|
|
49017
49802
|
}
|
|
49018
49803
|
adjustPositionY(targetRow) {
|
|
49019
49804
|
const sheetId = this.sheetId;
|
|
49020
49805
|
const { end } = this.getters.getRowDimensions(sheetId, targetRow);
|
|
49021
|
-
const maxRow = this.getters.getNumberRows(sheetId);
|
|
49022
49806
|
if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
|
|
49807
|
+
const maxRow = this.getters.getNumberRows(sheetId);
|
|
49023
49808
|
let finalTarget = targetRow;
|
|
49024
|
-
while (this.getters.isRowHidden(sheetId, finalTarget) &&
|
|
49809
|
+
while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget < maxRow) {
|
|
49025
49810
|
finalTarget++;
|
|
49026
49811
|
}
|
|
49027
49812
|
const finalTargetEnd = this.getters.getRowDimensions(sheetId, finalTarget).end;
|
|
49028
49813
|
const startIndex = this.searchHeaderIndex("ROW", finalTargetEnd - this.viewportHeight - this.offsetCorrectionY, this.boundaries.top);
|
|
49029
|
-
this.
|
|
49814
|
+
this.offsetScrollbarY =
|
|
49030
49815
|
this.getters.getRowDimensions(sheetId, startIndex).end - this.offsetCorrectionY;
|
|
49031
|
-
this.offsetScrollbarY = this.offsetY;
|
|
49032
|
-
this.adjustViewportZoneY();
|
|
49033
49816
|
}
|
|
49034
49817
|
else if (this.top > targetRow) {
|
|
49035
49818
|
let finalTarget = targetRow;
|
|
49036
|
-
while (this.getters.isRowHidden(sheetId, finalTarget) &&
|
|
49819
|
+
while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget > 0) {
|
|
49037
49820
|
finalTarget--;
|
|
49038
49821
|
}
|
|
49039
|
-
this.
|
|
49822
|
+
this.offsetScrollbarY =
|
|
49040
49823
|
this.getters.getRowDimensions(sheetId, finalTarget).start - this.offsetCorrectionY;
|
|
49041
|
-
this.offsetScrollbarY = this.offsetY;
|
|
49042
|
-
this.adjustViewportZoneY();
|
|
49043
49824
|
}
|
|
49825
|
+
this.adjustViewportZoneY();
|
|
49044
49826
|
}
|
|
49045
49827
|
setViewportOffset(offsetX, offsetY) {
|
|
49046
49828
|
this.setViewportOffsetX(offsetX);
|
|
@@ -49056,11 +49838,10 @@
|
|
|
49056
49838
|
* @returns Computes the absolute coordinate of a given zone inside the viewport
|
|
49057
49839
|
*/
|
|
49058
49840
|
getRect(zone) {
|
|
49059
|
-
const targetZone = intersection(zone, this
|
|
49841
|
+
const targetZone = intersection(zone, this);
|
|
49060
49842
|
if (targetZone) {
|
|
49061
|
-
const x = this.getters.getColRowOffset("COL", this.
|
|
49062
|
-
|
|
49063
|
-
const y = this.getters.getColRowOffset("ROW", this.zone.top, targetZone.top) + this.offsetCorrectionY;
|
|
49843
|
+
const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
|
|
49844
|
+
const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
|
|
49064
49845
|
const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
|
|
49065
49846
|
const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
|
|
49066
49847
|
return {
|
|
@@ -49070,9 +49851,7 @@
|
|
|
49070
49851
|
height,
|
|
49071
49852
|
};
|
|
49072
49853
|
}
|
|
49073
|
-
|
|
49074
|
-
return undefined;
|
|
49075
|
-
}
|
|
49854
|
+
return undefined;
|
|
49076
49855
|
}
|
|
49077
49856
|
isVisible(col, row) {
|
|
49078
49857
|
const isInside = row <= this.bottom && row >= this.top && col >= this.left && col <= this.right;
|
|
@@ -49080,7 +49859,6 @@
|
|
|
49080
49859
|
!this.getters.isColHidden(this.sheetId, col) &&
|
|
49081
49860
|
!this.getters.isRowHidden(this.sheetId, row));
|
|
49082
49861
|
}
|
|
49083
|
-
// PRIVATE
|
|
49084
49862
|
searchHeaderIndex(dimension, position, startIndex = 0) {
|
|
49085
49863
|
const sheetId = this.sheetId;
|
|
49086
49864
|
const headers = this.getters.getNumberHeaders(sheetId, dimension);
|
|
@@ -49103,9 +49881,6 @@
|
|
|
49103
49881
|
}
|
|
49104
49882
|
return -1;
|
|
49105
49883
|
}
|
|
49106
|
-
get zone() {
|
|
49107
|
-
return { left: this.left, right: this.right, top: this.top, bottom: this.bottom };
|
|
49108
|
-
}
|
|
49109
49884
|
setViewportOffsetX(offsetX) {
|
|
49110
49885
|
if (!this.canScrollHorizontally) {
|
|
49111
49886
|
return;
|
|
@@ -49130,11 +49905,6 @@
|
|
|
49130
49905
|
this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
|
|
49131
49906
|
}
|
|
49132
49907
|
}
|
|
49133
|
-
this.left = this.getColIndex(this.offsetScrollbarX);
|
|
49134
|
-
this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth);
|
|
49135
|
-
if (this.right === -1) {
|
|
49136
|
-
this.right = this.boundaries.right;
|
|
49137
|
-
}
|
|
49138
49908
|
this.adjustViewportZoneX();
|
|
49139
49909
|
}
|
|
49140
49910
|
/** Corrects the viewport's vertical offset based on the current structure
|
|
@@ -49147,11 +49917,6 @@
|
|
|
49147
49917
|
this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
|
|
49148
49918
|
}
|
|
49149
49919
|
}
|
|
49150
|
-
this.top = this.getRowIndex(this.offsetScrollbarY);
|
|
49151
|
-
this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportHeight);
|
|
49152
|
-
if (this.bottom === -1) {
|
|
49153
|
-
this.bottom = this.boundaries.bottom;
|
|
49154
|
-
}
|
|
49155
49920
|
this.adjustViewportZoneY();
|
|
49156
49921
|
}
|
|
49157
49922
|
/** Updates the pane zone and snapped offset based on its horizontal
|
|
@@ -49529,7 +50294,7 @@
|
|
|
49529
50294
|
isVisibleInViewport({ sheetId, col, row }) {
|
|
49530
50295
|
return this.getSubViewports(sheetId).some((pane) => pane.isVisible(col, row));
|
|
49531
50296
|
}
|
|
49532
|
-
// =>
|
|
50297
|
+
// => returns the new offset
|
|
49533
50298
|
getEdgeScrollCol(x, previousX, startingX) {
|
|
49534
50299
|
let canEdgeScroll = false;
|
|
49535
50300
|
let direction = 0;
|
|
@@ -49844,10 +50609,13 @@
|
|
|
49844
50609
|
}
|
|
49845
50610
|
break;
|
|
49846
50611
|
case "UPDATE_CELL":
|
|
49847
|
-
if ("content" in cmd || "format" in cmd
|
|
50612
|
+
if ("content" in cmd || "format" in cmd) {
|
|
49848
50613
|
this.headerPositions = {};
|
|
49849
50614
|
this.isDirty = true;
|
|
49850
50615
|
}
|
|
50616
|
+
else {
|
|
50617
|
+
this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
|
|
50618
|
+
}
|
|
49851
50619
|
break;
|
|
49852
50620
|
case "UPDATE_FILTER":
|
|
49853
50621
|
case "REMOVE_FILTER_TABLE":
|
|
@@ -52004,6 +52772,8 @@
|
|
|
52004
52772
|
*:before,
|
|
52005
52773
|
*:after {
|
|
52006
52774
|
box-sizing: content-box;
|
|
52775
|
+
/** rtl not supported ATM */
|
|
52776
|
+
direction: ltr;
|
|
52007
52777
|
}
|
|
52008
52778
|
.o-separator {
|
|
52009
52779
|
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
@@ -52294,7 +53064,7 @@
|
|
|
52294
53064
|
}
|
|
52295
53065
|
onKeydown(ev) {
|
|
52296
53066
|
let keyDownString = "";
|
|
52297
|
-
if (ev
|
|
53067
|
+
if (isCtrlKey(ev)) {
|
|
52298
53068
|
keyDownString += "CTRL+";
|
|
52299
53069
|
}
|
|
52300
53070
|
keyDownString += ev.key.toUpperCase();
|
|
@@ -55031,7 +55801,8 @@
|
|
|
55031
55801
|
}
|
|
55032
55802
|
else if (cell.content && cell.content !== "") {
|
|
55033
55803
|
const isTableHeader = isCellTableHeader(c, r, sheet);
|
|
55034
|
-
|
|
55804
|
+
const isPlainText = !!(cell.format && data.formats[cell.format] === PLAIN_TEXT_FORMAT);
|
|
55805
|
+
({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
|
|
55035
55806
|
}
|
|
55036
55807
|
attributes.push(...additionalAttrs);
|
|
55037
55808
|
cellNodes.push(escapeXml /*xml*/ `
|
|
@@ -55960,6 +56731,7 @@
|
|
|
55960
56731
|
colorToRGBA,
|
|
55961
56732
|
positionToZone,
|
|
55962
56733
|
isDefined: isDefined$1,
|
|
56734
|
+
isMatrix,
|
|
55963
56735
|
lazy,
|
|
55964
56736
|
genericRepeat,
|
|
55965
56737
|
createAction,
|
|
@@ -56044,9 +56816,9 @@
|
|
|
56044
56816
|
exports.tokenize = tokenize;
|
|
56045
56817
|
|
|
56046
56818
|
|
|
56047
|
-
__info__.version = "17.1.0-alpha.
|
|
56048
|
-
__info__.date = "
|
|
56049
|
-
__info__.hash = "
|
|
56819
|
+
__info__.version = "17.1.0-alpha.6";
|
|
56820
|
+
__info__.date = "2024-01-04T12:22:45.921Z";
|
|
56821
|
+
__info__.hash = "56dbdc9";
|
|
56050
56822
|
|
|
56051
56823
|
|
|
56052
56824
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|