@odoo/o-spreadsheet 17.2.4 → 17.2.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 +423 -48
- package/dist/o-spreadsheet.d.ts +5 -2
- package/dist/o-spreadsheet.esm.js +423 -48
- package/dist/o-spreadsheet.iife.js +423 -48
- package/dist/o-spreadsheet.iife.min.js +100 -100
- package/dist/o_spreadsheet.xml +3 -3
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
5
5
|
* @see https://github.com/odoo/o-spreadsheet
|
|
6
|
-
* @version 17.2.
|
|
7
|
-
* @date 2024-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.2.6
|
|
7
|
+
* @date 2024-05-07T10:41:20.332Z
|
|
8
|
+
* @hash a4f800c
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { reactive, useEnv, useSubEnv, useState, onWillUnmount, markRaw, toRaw, Component, useRef, onMounted, useEffect, onPatched, useComponent, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv } from '@odoo/owl';
|
|
@@ -552,7 +552,7 @@ function getAddHeaderStartIndex(position, base) {
|
|
|
552
552
|
/**
|
|
553
553
|
* Compares two objects.
|
|
554
554
|
*/
|
|
555
|
-
function deepEquals(o1, o2) {
|
|
555
|
+
function deepEquals(o1, o2, ignoreFunctions) {
|
|
556
556
|
if (o1 === o2)
|
|
557
557
|
return true;
|
|
558
558
|
if ((o1 && !o2) || (o2 && !o1))
|
|
@@ -568,13 +568,16 @@ function deepEquals(o1, o2) {
|
|
|
568
568
|
}
|
|
569
569
|
}
|
|
570
570
|
for (const key in o1) {
|
|
571
|
-
|
|
571
|
+
const typeOfO1Key = typeof o1[key];
|
|
572
|
+
if (typeOfO1Key !== typeof o2[key])
|
|
572
573
|
return false;
|
|
573
|
-
if (
|
|
574
|
-
if (!deepEquals(o1[key], o2[key]))
|
|
574
|
+
if (typeOfO1Key === "object") {
|
|
575
|
+
if (!deepEquals(o1[key], o2[key], ignoreFunctions))
|
|
575
576
|
return false;
|
|
576
577
|
}
|
|
577
578
|
else {
|
|
579
|
+
if (ignoreFunctions && typeOfO1Key === "function")
|
|
580
|
+
return true;
|
|
578
581
|
if (o1[key] !== o2[key])
|
|
579
582
|
return false;
|
|
580
583
|
}
|
|
@@ -3682,21 +3685,22 @@ function toZoneWithoutBoundaryChanges(xc) {
|
|
|
3682
3685
|
xc = xc.split("!").at(-1);
|
|
3683
3686
|
}
|
|
3684
3687
|
if (xc.includes("$")) {
|
|
3685
|
-
xc = xc.
|
|
3688
|
+
xc = xc.replaceAll("$", "");
|
|
3686
3689
|
}
|
|
3687
|
-
let
|
|
3690
|
+
let firstRangePart = "";
|
|
3691
|
+
let secondRangePart;
|
|
3688
3692
|
if (xc.includes(":")) {
|
|
3689
|
-
|
|
3693
|
+
[firstRangePart, secondRangePart] = xc.split(":");
|
|
3694
|
+
firstRangePart = firstRangePart.trim();
|
|
3695
|
+
secondRangePart = secondRangePart.trim();
|
|
3690
3696
|
}
|
|
3691
3697
|
else {
|
|
3692
|
-
|
|
3698
|
+
firstRangePart = xc.trim();
|
|
3693
3699
|
}
|
|
3694
3700
|
let top, bottom, left, right;
|
|
3695
3701
|
let fullCol = false;
|
|
3696
3702
|
let fullRow = false;
|
|
3697
3703
|
let hasHeader = false;
|
|
3698
|
-
const firstRangePart = ranges[0];
|
|
3699
|
-
const secondRangePart = ranges[1] && ranges[1];
|
|
3700
3704
|
if (isColReference(firstRangePart)) {
|
|
3701
3705
|
left = right = lettersToNumber(firstRangePart);
|
|
3702
3706
|
top = bottom = 0;
|
|
@@ -3713,7 +3717,7 @@ function toZoneWithoutBoundaryChanges(xc) {
|
|
|
3713
3717
|
top = bottom = c.row;
|
|
3714
3718
|
hasHeader = true;
|
|
3715
3719
|
}
|
|
3716
|
-
if (
|
|
3720
|
+
if (secondRangePart) {
|
|
3717
3721
|
if (isColReference(secondRangePart)) {
|
|
3718
3722
|
right = lettersToNumber(secondRangePart);
|
|
3719
3723
|
fullCol = true;
|
|
@@ -8904,8 +8908,7 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
8904
8908
|
const exactMatch = proposals?.find((p) => p.text === tokenAtCursor.value);
|
|
8905
8909
|
// remove tokens that are likely to be other parts of the formula that slipped in the token if it's a string
|
|
8906
8910
|
const searchTerm = tokenAtCursor.value.replace(/[ ,\(\)]/g, "");
|
|
8907
|
-
|
|
8908
|
-
if (exactMatch && exactMatch.text !== initialContent) {
|
|
8911
|
+
if (exactMatch && this._currentContent !== this.initialContent) {
|
|
8909
8912
|
// this means the user has chosen a proposal
|
|
8910
8913
|
return;
|
|
8911
8914
|
}
|
|
@@ -8913,7 +8916,7 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
8913
8916
|
proposals &&
|
|
8914
8917
|
!["ARG_SEPARATOR", "LEFT_PAREN"].includes(tokenAtCursor.type)) {
|
|
8915
8918
|
const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
|
|
8916
|
-
if (!exactMatch) {
|
|
8919
|
+
if (!exactMatch || filteredProposals.length > 1) {
|
|
8917
8920
|
proposals = filteredProposals;
|
|
8918
8921
|
}
|
|
8919
8922
|
}
|
|
@@ -9048,6 +9051,7 @@ class ChartJsComponent extends Component {
|
|
|
9048
9051
|
};
|
|
9049
9052
|
canvas = useRef("graphContainer");
|
|
9050
9053
|
chart;
|
|
9054
|
+
currentRuntime;
|
|
9051
9055
|
get background() {
|
|
9052
9056
|
return this.chartRuntime.background;
|
|
9053
9057
|
}
|
|
@@ -9064,9 +9068,18 @@ class ChartJsComponent extends Component {
|
|
|
9064
9068
|
setup() {
|
|
9065
9069
|
onMounted(() => {
|
|
9066
9070
|
const runtime = this.chartRuntime;
|
|
9067
|
-
this.
|
|
9071
|
+
this.currentRuntime = runtime;
|
|
9072
|
+
// Note: chartJS modify the runtime in place, so it's important to give it a copy
|
|
9073
|
+
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
9074
|
+
});
|
|
9075
|
+
onWillUnmount(() => this.chart?.destroy());
|
|
9076
|
+
useEffect(() => {
|
|
9077
|
+
const runtime = this.chartRuntime;
|
|
9078
|
+
if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
|
|
9079
|
+
this.currentRuntime = runtime;
|
|
9080
|
+
this.updateChartJs(deepCopy(runtime));
|
|
9081
|
+
}
|
|
9068
9082
|
});
|
|
9069
|
-
useEffect(() => this.updateChartJs(this.chartRuntime), () => [this.chartRuntime]);
|
|
9070
9083
|
}
|
|
9071
9084
|
createChart(chartData) {
|
|
9072
9085
|
const canvas = this.canvas.el;
|
|
@@ -9088,8 +9101,7 @@ class ChartJsComponent extends Component {
|
|
|
9088
9101
|
this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip;
|
|
9089
9102
|
this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
|
|
9090
9103
|
this.chart.config.options.scales = chartData.options?.scales;
|
|
9091
|
-
|
|
9092
|
-
this.chart.update("active");
|
|
9104
|
+
this.chart.update();
|
|
9093
9105
|
}
|
|
9094
9106
|
}
|
|
9095
9107
|
|
|
@@ -19452,7 +19464,7 @@ function truncateLabel(label) {
|
|
|
19452
19464
|
/**
|
|
19453
19465
|
* Get a default chart js configuration
|
|
19454
19466
|
*/
|
|
19455
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
|
|
19467
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
|
|
19456
19468
|
const options = {
|
|
19457
19469
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
19458
19470
|
responsive: true,
|
|
@@ -19499,7 +19511,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
|
|
|
19499
19511
|
type: chart.type,
|
|
19500
19512
|
options,
|
|
19501
19513
|
data: {
|
|
19502
|
-
labels: labels.map(truncateLabel),
|
|
19514
|
+
labels: truncateLabels ? labels.map(truncateLabel) : labels,
|
|
19503
19515
|
datasets: [],
|
|
19504
19516
|
},
|
|
19505
19517
|
platform: undefined,
|
|
@@ -20265,9 +20277,9 @@ function isLuxonTimeAdapterInstalled() {
|
|
|
20265
20277
|
}
|
|
20266
20278
|
return isInstalled;
|
|
20267
20279
|
}
|
|
20268
|
-
function getLineOrScatterConfiguration(chart, labels,
|
|
20280
|
+
function getLineOrScatterConfiguration(chart, labels, options) {
|
|
20269
20281
|
const fontColor = chartFontColor(chart.background);
|
|
20270
|
-
const config = getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
20282
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, options);
|
|
20271
20283
|
const legend = {
|
|
20272
20284
|
labels: {
|
|
20273
20285
|
color: fontColor,
|
|
@@ -20310,7 +20322,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
|
|
|
20310
20322
|
value = Number(value);
|
|
20311
20323
|
if (isNaN(value))
|
|
20312
20324
|
return value;
|
|
20313
|
-
const { locale, format } =
|
|
20325
|
+
const { locale, format } = options;
|
|
20314
20326
|
return formatValue(value, {
|
|
20315
20327
|
locale,
|
|
20316
20328
|
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
@@ -20343,9 +20355,10 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
20343
20355
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
20344
20356
|
}
|
|
20345
20357
|
const locale = getters.getLocale();
|
|
20358
|
+
const truncateLabels = axisType === "category";
|
|
20346
20359
|
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
20347
|
-
const
|
|
20348
|
-
const config = getLineOrScatterConfiguration(chart, labels,
|
|
20360
|
+
const options = { format: dataSetFormat, locale, truncateLabels };
|
|
20361
|
+
const config = getLineOrScatterConfiguration(chart, labels, options);
|
|
20349
20362
|
const labelFormat = getChartLabelFormat(getters, chart.labelRange);
|
|
20350
20363
|
if (axisType === "time") {
|
|
20351
20364
|
const axis = {
|
|
@@ -32071,17 +32084,13 @@ class Composer extends Component {
|
|
|
32071
32084
|
this.DOMFocusableElementStore.setFocusableElement(el);
|
|
32072
32085
|
}
|
|
32073
32086
|
this.contentHelper.updateEl(el);
|
|
32074
|
-
this.processTokenAtCursor();
|
|
32075
32087
|
});
|
|
32076
32088
|
useEffect(() => {
|
|
32077
32089
|
this.processContent();
|
|
32078
32090
|
});
|
|
32079
|
-
|
|
32080
|
-
|
|
32081
|
-
|
|
32082
|
-
this.processTokenAtCursor();
|
|
32083
|
-
}
|
|
32084
|
-
});
|
|
32091
|
+
useEffect(() => {
|
|
32092
|
+
this.processTokenAtCursor();
|
|
32093
|
+
}, () => [this.composerStore.editionMode !== "inactive"]);
|
|
32085
32094
|
}
|
|
32086
32095
|
// ---------------------------------------------------------------------------
|
|
32087
32096
|
// Handlers
|
|
@@ -32903,7 +32912,8 @@ class DataValidationOverlay extends Component {
|
|
|
32903
32912
|
get checkBoxCellPositions() {
|
|
32904
32913
|
return this.env.model.getters
|
|
32905
32914
|
.getVisibleCellPositions()
|
|
32906
|
-
.filter(this.env.model.getters.isCellValidCheckbox)
|
|
32915
|
+
.filter((position) => this.env.model.getters.isCellValidCheckbox(position) &&
|
|
32916
|
+
!this.env.model.getters.isFilterHeader(position));
|
|
32907
32917
|
}
|
|
32908
32918
|
get listIconsCellPositions() {
|
|
32909
32919
|
if (this.env.model.getters.isReadonly()) {
|
|
@@ -32911,7 +32921,8 @@ class DataValidationOverlay extends Component {
|
|
|
32911
32921
|
}
|
|
32912
32922
|
return this.env.model.getters
|
|
32913
32923
|
.getVisibleCellPositions()
|
|
32914
|
-
.filter(this.env.model.getters.cellHasListDataValidationIcon)
|
|
32924
|
+
.filter((position) => this.env.model.getters.cellHasListDataValidationIcon(position) &&
|
|
32925
|
+
!this.env.model.getters.isFilterHeader(position));
|
|
32915
32926
|
}
|
|
32916
32927
|
}
|
|
32917
32928
|
|
|
@@ -40378,7 +40389,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
40378
40389
|
this.clearBorders(cmd.sheetId, cmd.target);
|
|
40379
40390
|
break;
|
|
40380
40391
|
case "REMOVE_COLUMNS_ROWS":
|
|
40381
|
-
for (let el of cmd.elements) {
|
|
40392
|
+
for (let el of [...cmd.elements].sort((a, b) => b - a)) {
|
|
40382
40393
|
if (cmd.dimension === "COL") {
|
|
40383
40394
|
this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
|
|
40384
40395
|
}
|
|
@@ -45917,6 +45928,353 @@ class CompilationParametersBuilder {
|
|
|
45917
45928
|
}
|
|
45918
45929
|
}
|
|
45919
45930
|
|
|
45931
|
+
/**
|
|
45932
|
+
* ####################################################
|
|
45933
|
+
* # INTRODUCTION
|
|
45934
|
+
* ####################################################
|
|
45935
|
+
*
|
|
45936
|
+
* This file contain the function recomputeZones.
|
|
45937
|
+
* This function try to recompute in a performant way
|
|
45938
|
+
* an ensemble of zones possibly overlapping to avoid
|
|
45939
|
+
* overlapping and to reduce the number of zones.
|
|
45940
|
+
*
|
|
45941
|
+
* It also allows to remove some zones from the ensemble.
|
|
45942
|
+
*
|
|
45943
|
+
* In the following example, 2 zones are overlapping.
|
|
45944
|
+
* Applying recomputeZones will return zones without
|
|
45945
|
+
* overlapping:
|
|
45946
|
+
*
|
|
45947
|
+
* ["B3:D4", "D2:E3"] ["B3:C4", "D2:D4", "E2:E3"]
|
|
45948
|
+
*
|
|
45949
|
+
* A B C D E A B C D E
|
|
45950
|
+
* 1 ___ 1 ___
|
|
45951
|
+
* 2 ___|_ | 2 ___| | |
|
|
45952
|
+
* 3 | |_|_| ---> 3 | | |_|
|
|
45953
|
+
* 4 |_____| 4 |___|_|
|
|
45954
|
+
* 6 6
|
|
45955
|
+
* 7 7
|
|
45956
|
+
*
|
|
45957
|
+
*
|
|
45958
|
+
* In the following example, 2 zones are contiguous.
|
|
45959
|
+
* Applying recomputeZones will return only one zone:
|
|
45960
|
+
*
|
|
45961
|
+
* ["B2:B3", "C2:D3"] ["B2:D3"]
|
|
45962
|
+
*
|
|
45963
|
+
* A B C D E A B C D E
|
|
45964
|
+
* 1 _ ___ 1 _____
|
|
45965
|
+
* 2 | | | ---> 2 | |
|
|
45966
|
+
* 3 |_|___| 3 |_____|
|
|
45967
|
+
* 4 4
|
|
45968
|
+
*
|
|
45969
|
+
*
|
|
45970
|
+
* In the following example, we want to remove a zone
|
|
45971
|
+
* from the ensemble. Applying recomputeZones will
|
|
45972
|
+
* return the ensemble without the zone to remove:
|
|
45973
|
+
*
|
|
45974
|
+
* remove ["C3:D3"] ["B2:B4", "C2:D2",
|
|
45975
|
+
* "C4:D4", "E2:E4"]
|
|
45976
|
+
*
|
|
45977
|
+
* A B C D E F A B C D E F
|
|
45978
|
+
* 1 _______ 1 _______
|
|
45979
|
+
* 2 | | ---> 2 | |___| |
|
|
45980
|
+
* 3 | xxx | 3 | |___| |
|
|
45981
|
+
* 4 |_______| 4 |_|___|_|
|
|
45982
|
+
* 5 5
|
|
45983
|
+
*
|
|
45984
|
+
*
|
|
45985
|
+
* The exercise seems simple when we have only 2 zones.
|
|
45986
|
+
* But with n zones and in a performant way, we want to
|
|
45987
|
+
* avoid comparing each zone with all the others.
|
|
45988
|
+
*
|
|
45989
|
+
*
|
|
45990
|
+
* ####################################################
|
|
45991
|
+
* # Methodological approach
|
|
45992
|
+
* ####################################################
|
|
45993
|
+
*
|
|
45994
|
+
* The methodological approach to avoid comparing each
|
|
45995
|
+
* zone with all the others is to use a data structure
|
|
45996
|
+
* that allow to quickly find which zones are
|
|
45997
|
+
* overlapping with any other given zone.
|
|
45998
|
+
*
|
|
45999
|
+
* Here the idea is to profile the zones at the columns level.
|
|
46000
|
+
*
|
|
46001
|
+
* To do that, we propose to use a data structure
|
|
46002
|
+
* composed of 2 parts:
|
|
46003
|
+
* - profilesStartingPosition: a sorted number array
|
|
46004
|
+
* indicating on which columns a new profile begins.
|
|
46005
|
+
* - profiles: a map where the key is a column
|
|
46006
|
+
* position (from profilesStartingPosition) and the
|
|
46007
|
+
* value is a sorted number array representing a
|
|
46008
|
+
* profile.
|
|
46009
|
+
*
|
|
46010
|
+
*
|
|
46011
|
+
* See the following example: here profileStartingPosition
|
|
46012
|
+
* corresponds to [A,C,E,G,K]
|
|
46013
|
+
* A B C D E F G H I J K so with number [0,2,4,6,10]
|
|
46014
|
+
* 1 ' ' ' '
|
|
46015
|
+
* 2 ' ' '_______' here profile correspond
|
|
46016
|
+
* 3 '___' |_______| for A to []
|
|
46017
|
+
* 4 | | for C to [3, 5]
|
|
46018
|
+
* 5 |___| for E to []
|
|
46019
|
+
* 6 for G to [2, 3]
|
|
46020
|
+
* 7 for K to []
|
|
46021
|
+
*
|
|
46022
|
+
*
|
|
46023
|
+
* Now we can easily find which zones are overlapping
|
|
46024
|
+
* with a given zone. Suppose we want to add a new zone
|
|
46025
|
+
* D5:H6 to the ensemble:
|
|
46026
|
+
*
|
|
46027
|
+
* With a binary search of left and right
|
|
46028
|
+
* A B C D E F G H I J K on profilesStartingPosition, we can
|
|
46029
|
+
* 1 ' ' ' ' find the indexes of the profiles on which
|
|
46030
|
+
* 2 ' ' '_______' to apply a modification.
|
|
46031
|
+
* 3 '___' |_______|
|
|
46032
|
+
* 4 | _|_______ Here we will:
|
|
46033
|
+
* 5 |_|_| | - add a new profile in D --> become [3, 6]
|
|
46034
|
+
* 6 |_________| - modify the profile in E --> become [4, 6]
|
|
46035
|
+
* 7 - modify the profile in G --> become [2, 3, 4, 6]
|
|
46036
|
+
* - add a new profile in I --> become [8, 10]
|
|
46037
|
+
*
|
|
46038
|
+
* See below the result:
|
|
46039
|
+
*
|
|
46040
|
+
* Note the particularity of the profile
|
|
46041
|
+
* A B C D E F G H I J K for G: it will correspond to [2, 3, 4, 6]
|
|
46042
|
+
* 1 ' ' ' ' ' '
|
|
46043
|
+
* 2 ' ' ' '___'___' To know how to modify the profile (add a
|
|
46044
|
+
* 3 '_'_' |___|___| zone or remove it) we do a binary
|
|
46045
|
+
* 4 | | |___ ___ search of the top and bottom value on the
|
|
46046
|
+
* 5 |_| | | | profile array. Depending on the result index
|
|
46047
|
+
* 6 |_|___|___| parity (odd or even), because zone boundaries
|
|
46048
|
+
* 7 go by pairs, we know if we are in a zone or
|
|
46049
|
+
* not and how operate.
|
|
46050
|
+
*/
|
|
46051
|
+
/**
|
|
46052
|
+
* Recompute the zone without the cells in toRemoveZones and avoid overlapping.
|
|
46053
|
+
* This compute is particularly useful because after this function:
|
|
46054
|
+
* - you will find coordinate of a cell only once among all the zones
|
|
46055
|
+
* - the number of zones will be reduced to the minimum
|
|
46056
|
+
*/
|
|
46057
|
+
function futureRecomputeZones(zones, zonesToRemove = []) {
|
|
46058
|
+
const profilesStartingPosition = [0];
|
|
46059
|
+
const profiles = new Map([[0, []]]);
|
|
46060
|
+
modifyProfiles(profilesStartingPosition, profiles, zones, false);
|
|
46061
|
+
modifyProfiles(profilesStartingPosition, profiles, zonesToRemove, true);
|
|
46062
|
+
return constructZonesFromProfiles(profilesStartingPosition, profiles);
|
|
46063
|
+
}
|
|
46064
|
+
function modifyProfiles(// export for testing only
|
|
46065
|
+
profilesStartingPosition, profiles, zones, toRemove = false) {
|
|
46066
|
+
for (const zone of zones) {
|
|
46067
|
+
const leftValue = zone.left;
|
|
46068
|
+
const rightValue = zone.right === undefined ? undefined : zone.right + 1;
|
|
46069
|
+
const leftIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, leftValue, true, 0);
|
|
46070
|
+
const rightIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, rightValue, false, leftIndex);
|
|
46071
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
46072
|
+
const profile = profiles.get(profilesStartingPosition[i]);
|
|
46073
|
+
modifyProfile(profile, zone, toRemove);
|
|
46074
|
+
}
|
|
46075
|
+
// maybe this part cost in performance, and maybe it's not necessary (depending on the use case). To be checked
|
|
46076
|
+
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
46077
|
+
}
|
|
46078
|
+
}
|
|
46079
|
+
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
46080
|
+
if (value === undefined) {
|
|
46081
|
+
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
46082
|
+
return profilesStartingPosition.length - 1;
|
|
46083
|
+
}
|
|
46084
|
+
const predecessorIndex = binaryPredecessorSearch(profilesStartingPosition, value, startIndex);
|
|
46085
|
+
if (value != profilesStartingPosition[predecessorIndex]) {
|
|
46086
|
+
// mean that the value is not ending/starting at the same position as the previous/next profile
|
|
46087
|
+
// --> it's a new profile
|
|
46088
|
+
// --> we need to add it
|
|
46089
|
+
profilesStartingPosition.splice(predecessorIndex + 1, 0, value);
|
|
46090
|
+
// suppose the we want to add the for the left value
|
|
46091
|
+
// following profile following zone: 'C', the predecessor index
|
|
46092
|
+
// for B: [1, 3] "C3:D4" correspond to 'B'.
|
|
46093
|
+
// The next line code will
|
|
46094
|
+
// A B C D A B C D copy the profile of 'B'
|
|
46095
|
+
// 1 '___' 1 '___' to 'C'. In the rest of the
|
|
46096
|
+
// 2 | | ---> 2 | _|_ process the 'modifyProfile'
|
|
46097
|
+
// 3 |___| 3 |_|_| | function will adapt the waiting
|
|
46098
|
+
// 4 4 |___| 'C' profile [1, 3] to the
|
|
46099
|
+
// correct 'C' profile [1, 4]
|
|
46100
|
+
profiles.set(value, [...profiles.get(profilesStartingPosition[predecessorIndex])]);
|
|
46101
|
+
return searchLeft ? predecessorIndex + 1 : predecessorIndex;
|
|
46102
|
+
}
|
|
46103
|
+
return searchLeft ? predecessorIndex : predecessorIndex - 1;
|
|
46104
|
+
}
|
|
46105
|
+
/**
|
|
46106
|
+
* Suppose the following Suppose we want to add We want to have the
|
|
46107
|
+
* profile: the following zone: following profile:
|
|
46108
|
+
*
|
|
46109
|
+
* A B C D E F A B C D E F A B C D E F
|
|
46110
|
+
* 1 '___' 1 ' ' 1 '___'
|
|
46111
|
+
* 2 |___| 2 '___' 2 | |
|
|
46112
|
+
* 3 ' ' 3 | | 3 | |
|
|
46113
|
+
* 4 '___' --> 4 | | --> 4 | |
|
|
46114
|
+
* 6 | | 6 |___| 6 | |
|
|
46115
|
+
* 7 |___| 7 7 |___|
|
|
46116
|
+
* 8 8 8
|
|
46117
|
+
*
|
|
46118
|
+
* the profile for 'C' the top zone correspond Here [2, 3, 5, 8] with [3, 7]
|
|
46119
|
+
* corresponds to: to 3 and the bottom zone would be merged into [2, 8]
|
|
46120
|
+
* ____ ____ correspond to 6
|
|
46121
|
+
* [2, 3, 5, 8] would be the profile: The difficulty of modify profile
|
|
46122
|
+
* ____ is to know what must be deleted
|
|
46123
|
+
* Note that the 'filled [3, 7] and what must be added to the
|
|
46124
|
+
* zone' are always between existing profile.
|
|
46125
|
+
* an even index and its
|
|
46126
|
+
* next index
|
|
46127
|
+
*
|
|
46128
|
+
*/
|
|
46129
|
+
function modifyProfile(profile, zone, toRemove = false) {
|
|
46130
|
+
const topValue = zone.top;
|
|
46131
|
+
const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
|
|
46132
|
+
const newPoints = [];
|
|
46133
|
+
// Case we want to add a zone to the profile:
|
|
46134
|
+
// - If the top predecessor index `topPredIndex` is even, it means the top of the zone is already positioned on a filled zone
|
|
46135
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the predecessor.
|
|
46136
|
+
// - If it is odd, it means the top of the zone must be the beginning of a filled zone.
|
|
46137
|
+
// so we can keep the index of the top position
|
|
46138
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
46139
|
+
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, false);
|
|
46140
|
+
if ((topPredIndex % 2 !== 0 && !toRemove) || (topPredIndex % 2 === 0 && toRemove)) {
|
|
46141
|
+
newPoints.push(topValue);
|
|
46142
|
+
}
|
|
46143
|
+
if (bottomValue === undefined) {
|
|
46144
|
+
// The following two code lines will not impact the final result,
|
|
46145
|
+
// but they will impact the intermediate profile.
|
|
46146
|
+
// We keep them for performance reason
|
|
46147
|
+
profile.splice(topPredIndex + 1);
|
|
46148
|
+
profile.push(...newPoints);
|
|
46149
|
+
return;
|
|
46150
|
+
}
|
|
46151
|
+
// Case we want to add a zone to the profile:
|
|
46152
|
+
// - If the bottom successor index `bottomSuccIndex` is even, it means the bottom of the zone must be the ending of a filled zone
|
|
46153
|
+
// so we can keep the index of the bottom position.
|
|
46154
|
+
// - If it is odd, it means the bottom of the zone is already positioned on a filled zone
|
|
46155
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the successor
|
|
46156
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
46157
|
+
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, false);
|
|
46158
|
+
if ((bottomSuccIndex % 2 === 0 && !toRemove) || (bottomSuccIndex % 2 !== 0 && toRemove)) {
|
|
46159
|
+
newPoints.push(bottomValue);
|
|
46160
|
+
}
|
|
46161
|
+
// add the top and bottom value to the profile and
|
|
46162
|
+
// remove all information between the top and bottom index
|
|
46163
|
+
profile.splice(topPredIndex + 1, bottomSuccIndex - topPredIndex - 1, ...newPoints);
|
|
46164
|
+
}
|
|
46165
|
+
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
46166
|
+
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
46167
|
+
const end = rightIndex === profilesStartingPosition.length - 1 ? rightIndex : rightIndex + 1;
|
|
46168
|
+
for (let i = end; i > start; i--) {
|
|
46169
|
+
if (deepEqualsArray(profiles.get(profilesStartingPosition[i]), profiles.get(profilesStartingPosition[i - 1]))) {
|
|
46170
|
+
profiles.delete(profilesStartingPosition[i]);
|
|
46171
|
+
profilesStartingPosition.splice(i, 1);
|
|
46172
|
+
}
|
|
46173
|
+
}
|
|
46174
|
+
}
|
|
46175
|
+
function constructZonesFromProfiles(profilesStartingPosition, profiles) {
|
|
46176
|
+
const mergedZone = [];
|
|
46177
|
+
let pendingZones = [];
|
|
46178
|
+
for (let colIndex = 0; colIndex < profilesStartingPosition.length; colIndex++) {
|
|
46179
|
+
const left = profilesStartingPosition[colIndex];
|
|
46180
|
+
const profile = profiles.get(left);
|
|
46181
|
+
if (!profile || profile.length === 0) {
|
|
46182
|
+
mergedZone.push(...pendingZones);
|
|
46183
|
+
pendingZones = [];
|
|
46184
|
+
continue;
|
|
46185
|
+
}
|
|
46186
|
+
let right = profilesStartingPosition[colIndex + 1];
|
|
46187
|
+
if (right !== undefined) {
|
|
46188
|
+
right--;
|
|
46189
|
+
}
|
|
46190
|
+
const nextPendingZones = [];
|
|
46191
|
+
for (let i = 0; i < profile.length; i += 2) {
|
|
46192
|
+
const top = profile[i];
|
|
46193
|
+
let bottom = profile[i + 1];
|
|
46194
|
+
if (bottom !== undefined) {
|
|
46195
|
+
bottom--;
|
|
46196
|
+
}
|
|
46197
|
+
const profileZone = {
|
|
46198
|
+
top,
|
|
46199
|
+
left,
|
|
46200
|
+
bottom,
|
|
46201
|
+
right,
|
|
46202
|
+
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
46203
|
+
};
|
|
46204
|
+
let findCorrespondingZone = false;
|
|
46205
|
+
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
46206
|
+
const pendingZone = pendingZones[j];
|
|
46207
|
+
if (pendingZone.top === profileZone.top && pendingZone.bottom === profileZone.bottom) {
|
|
46208
|
+
pendingZone.right = profileZone.right;
|
|
46209
|
+
pendingZones.splice(j, 1);
|
|
46210
|
+
nextPendingZones.push(pendingZone);
|
|
46211
|
+
findCorrespondingZone = true;
|
|
46212
|
+
break;
|
|
46213
|
+
}
|
|
46214
|
+
}
|
|
46215
|
+
if (!findCorrespondingZone) {
|
|
46216
|
+
nextPendingZones.push(profileZone);
|
|
46217
|
+
}
|
|
46218
|
+
}
|
|
46219
|
+
mergedZone.push(...pendingZones);
|
|
46220
|
+
pendingZones = nextPendingZones;
|
|
46221
|
+
}
|
|
46222
|
+
mergedZone.push(...pendingZones);
|
|
46223
|
+
return mergedZone;
|
|
46224
|
+
}
|
|
46225
|
+
function binaryPredecessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
46226
|
+
let end = arr.length - 1;
|
|
46227
|
+
let result = -1;
|
|
46228
|
+
while (start <= end) {
|
|
46229
|
+
const mid = Math.floor((start + end) / 2);
|
|
46230
|
+
if (arr[mid] === val && matchEqual) {
|
|
46231
|
+
return mid;
|
|
46232
|
+
}
|
|
46233
|
+
else if (arr[mid] < val) {
|
|
46234
|
+
result = mid;
|
|
46235
|
+
start = mid + 1;
|
|
46236
|
+
}
|
|
46237
|
+
else {
|
|
46238
|
+
end = mid - 1;
|
|
46239
|
+
}
|
|
46240
|
+
}
|
|
46241
|
+
return result;
|
|
46242
|
+
}
|
|
46243
|
+
function binarySuccessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
46244
|
+
let end = arr.length - 1;
|
|
46245
|
+
let result = arr.length;
|
|
46246
|
+
while (start <= end) {
|
|
46247
|
+
const mid = Math.floor((start + end) / 2);
|
|
46248
|
+
if (arr[mid] === val && matchEqual) {
|
|
46249
|
+
return mid;
|
|
46250
|
+
}
|
|
46251
|
+
else if (arr[mid] > val) {
|
|
46252
|
+
result = mid;
|
|
46253
|
+
end = mid - 1;
|
|
46254
|
+
}
|
|
46255
|
+
else {
|
|
46256
|
+
start = mid + 1;
|
|
46257
|
+
}
|
|
46258
|
+
}
|
|
46259
|
+
return result;
|
|
46260
|
+
}
|
|
46261
|
+
/**
|
|
46262
|
+
* Compares two arrays.
|
|
46263
|
+
* For performance reasons, this function is to be preferred
|
|
46264
|
+
* to 'deepEquals' in the case we know that the inputs are arrays.
|
|
46265
|
+
*/
|
|
46266
|
+
function deepEqualsArray(arr1, arr2) {
|
|
46267
|
+
if (arr1.length !== arr2.length) {
|
|
46268
|
+
return false;
|
|
46269
|
+
}
|
|
46270
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
46271
|
+
if (!deepEquals(arr1[i], arr2[i])) {
|
|
46272
|
+
return false;
|
|
46273
|
+
}
|
|
46274
|
+
}
|
|
46275
|
+
return true;
|
|
46276
|
+
}
|
|
46277
|
+
|
|
45920
46278
|
class PositionMap {
|
|
45921
46279
|
map = {};
|
|
45922
46280
|
set({ sheetId, col, row }, value) {
|
|
@@ -46715,7 +47073,7 @@ class FormulaDependencyGraph {
|
|
|
46715
47073
|
}
|
|
46716
47074
|
}
|
|
46717
47075
|
/**
|
|
46718
|
-
* Return the
|
|
47076
|
+
* Return all the cells that depend on the provided ranges,
|
|
46719
47077
|
* in the correct order they should be evaluated.
|
|
46720
47078
|
* This is called a topological ordering (excluding cycles)
|
|
46721
47079
|
*/
|
|
@@ -46726,11 +47084,19 @@ class FormulaDependencyGraph {
|
|
|
46726
47084
|
const range = queue.pop();
|
|
46727
47085
|
visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
|
|
46728
47086
|
const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
|
|
47087
|
+
const nextInQueue = {};
|
|
46729
47088
|
for (const position of impactedPositions) {
|
|
46730
47089
|
if (!visited.has(position)) {
|
|
46731
|
-
|
|
47090
|
+
if (!nextInQueue[position.sheetId]) {
|
|
47091
|
+
nextInQueue[position.sheetId] = [];
|
|
47092
|
+
}
|
|
47093
|
+
nextInQueue[position.sheetId].push(positionToZone(position));
|
|
46732
47094
|
}
|
|
46733
47095
|
}
|
|
47096
|
+
for (const sheetId in nextInQueue) {
|
|
47097
|
+
const zones = futureRecomputeZones(nextInQueue[sheetId]);
|
|
47098
|
+
queue.push(...zones.map((zone) => ({ sheetId, zone })));
|
|
47099
|
+
}
|
|
46734
47100
|
}
|
|
46735
47101
|
visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
|
|
46736
47102
|
return visited;
|
|
@@ -47069,7 +47435,7 @@ class Evaluator {
|
|
|
47069
47435
|
}
|
|
47070
47436
|
if (!content) {
|
|
47071
47437
|
// The previous content could have blocked some array formulas
|
|
47072
|
-
impactedPositions.addMany(this.
|
|
47438
|
+
impactedPositions.addMany(this.getArrayFormulasBlockedBy(position));
|
|
47073
47439
|
}
|
|
47074
47440
|
}
|
|
47075
47441
|
return impactedPositions;
|
|
@@ -47112,14 +47478,23 @@ class Evaluator {
|
|
|
47112
47478
|
positions.fillAllPositions();
|
|
47113
47479
|
return positions;
|
|
47114
47480
|
}
|
|
47115
|
-
|
|
47481
|
+
/**
|
|
47482
|
+
* Return the position of formulas blocked by the given position
|
|
47483
|
+
* as well as all their dependencies.
|
|
47484
|
+
*/
|
|
47485
|
+
getArrayFormulasBlockedBy(position) {
|
|
47116
47486
|
if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
|
|
47117
47487
|
return [];
|
|
47118
47488
|
}
|
|
47119
47489
|
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
|
|
47120
47490
|
const positions = this.createEmptyPositionSet();
|
|
47121
47491
|
positions.addMany(arrayFormulas);
|
|
47122
|
-
|
|
47492
|
+
const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
|
|
47493
|
+
if (arrayFormulaPosition) {
|
|
47494
|
+
// ignore the formula spreading on the position. Keep only the blocked ones
|
|
47495
|
+
positions.delete(arrayFormulaPosition);
|
|
47496
|
+
}
|
|
47497
|
+
positions.addMany(this.getCellsDependingOn(positions));
|
|
47123
47498
|
return positions;
|
|
47124
47499
|
}
|
|
47125
47500
|
nextPositionsToUpdate = new PositionSet({});
|
|
@@ -47259,7 +47634,7 @@ class Evaluator {
|
|
|
47259
47634
|
}
|
|
47260
47635
|
this.evaluatedCells.delete(child);
|
|
47261
47636
|
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
47262
|
-
this.nextPositionsToUpdate.addMany(this.
|
|
47637
|
+
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
|
|
47263
47638
|
}
|
|
47264
47639
|
this.spreadingRelations.removeNode(position);
|
|
47265
47640
|
}
|
|
@@ -47567,7 +47942,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47567
47942
|
? getItemId(newFormat, data.formats)
|
|
47568
47943
|
: exportedCellData.format;
|
|
47569
47944
|
let content;
|
|
47570
|
-
if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
|
|
47945
|
+
if (isExported && isFormula && formulaCell instanceof FormulaCellWithDependencies) {
|
|
47571
47946
|
content = formulaCell.contentWithFixedReferences;
|
|
47572
47947
|
}
|
|
47573
47948
|
else {
|
|
@@ -60197,6 +60572,6 @@ const constants = {
|
|
|
60197
60572
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
60198
60573
|
|
|
60199
60574
|
|
|
60200
|
-
__info__.version = "17.2.
|
|
60201
|
-
__info__.date = "2024-
|
|
60202
|
-
__info__.hash = "
|
|
60575
|
+
__info__.version = "17.2.6";
|
|
60576
|
+
__info__.date = "2024-05-07T10:41:20.332Z";
|
|
60577
|
+
__info__.hash = "a4f800c";
|