@odoo/o-spreadsheet 17.2.3 → 17.2.5
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 +439 -68
- package/dist/o-spreadsheet.d.ts +3 -9
- package/dist/o-spreadsheet.esm.js +439 -68
- package/dist/o-spreadsheet.iife.js +439 -68
- package/dist/o-spreadsheet.iife.min.js +268 -264
- package/dist/o_spreadsheet.xml +4 -4
- 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-04-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.2.5
|
|
7
|
+
* @date 2024-04-26T07:41:13.193Z
|
|
8
|
+
* @hash a730f5c
|
|
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';
|
|
@@ -2507,6 +2507,15 @@ function matrixMap(matrix, fn) {
|
|
|
2507
2507
|
}
|
|
2508
2508
|
return generateMatrix(matrix.length, matrix[0].length, (col, row) => fn(matrix[col][row]));
|
|
2509
2509
|
}
|
|
2510
|
+
function matrixForEach(matrix, fn) {
|
|
2511
|
+
const numberOfCols = matrix.length;
|
|
2512
|
+
const numberOfRows = matrix[0]?.length ?? 0;
|
|
2513
|
+
for (let col = 0; col < numberOfCols; col++) {
|
|
2514
|
+
for (let row = 0; row < numberOfRows; row++) {
|
|
2515
|
+
fn(matrix[col][row]);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2510
2519
|
function transposeMatrix(matrix) {
|
|
2511
2520
|
if (!matrix.length) {
|
|
2512
2521
|
return [];
|
|
@@ -18634,7 +18643,7 @@ class FunctionRegistry extends Registry {
|
|
|
18634
18643
|
}
|
|
18635
18644
|
const descr = addMetaInfoFromArg(addDescr);
|
|
18636
18645
|
validateArguments(descr.args);
|
|
18637
|
-
this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
|
|
18646
|
+
this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr), name), name);
|
|
18638
18647
|
super.add(name, descr);
|
|
18639
18648
|
return this;
|
|
18640
18649
|
}
|
|
@@ -18673,9 +18682,7 @@ function handleError(e, functionName) {
|
|
|
18673
18682
|
// so we fallback to a generic error
|
|
18674
18683
|
if (hasStringValue(e) && isEvaluationError(e.value)) {
|
|
18675
18684
|
if (hasStringMessage(e)) {
|
|
18676
|
-
|
|
18677
|
-
e.message = e.message.replace("[[FUNCTION_NAME]]", functionName);
|
|
18678
|
-
}
|
|
18685
|
+
replaceFunctionNamePlaceholder(e, functionName);
|
|
18679
18686
|
}
|
|
18680
18687
|
return e;
|
|
18681
18688
|
}
|
|
@@ -18690,21 +18697,29 @@ function hasStringMessage(obj) {
|
|
|
18690
18697
|
return (obj?.message !== undefined &&
|
|
18691
18698
|
typeof obj.message === "string");
|
|
18692
18699
|
}
|
|
18693
|
-
function addResultHandling(compute) {
|
|
18694
|
-
return function (...args) {
|
|
18700
|
+
function addResultHandling(compute, functionName) {
|
|
18701
|
+
return function computeWithResultHandling(...args) {
|
|
18695
18702
|
const result = compute.apply(this, args);
|
|
18696
18703
|
if (!isMatrix(result)) {
|
|
18697
18704
|
if (typeof result === "object" && result !== null && "value" in result) {
|
|
18705
|
+
replaceFunctionNamePlaceholder(result, functionName);
|
|
18698
18706
|
return result;
|
|
18699
18707
|
}
|
|
18700
18708
|
return { value: result };
|
|
18701
18709
|
}
|
|
18702
18710
|
if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
|
|
18711
|
+
matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
|
|
18703
18712
|
return result;
|
|
18704
18713
|
}
|
|
18705
18714
|
return matrixMap(result, (row) => ({ value: row }));
|
|
18706
18715
|
};
|
|
18707
18716
|
}
|
|
18717
|
+
function replaceFunctionNamePlaceholder(fPayload, functionName) {
|
|
18718
|
+
// for performance reasons: change in place and only if needed
|
|
18719
|
+
if (fPayload.message?.includes("[[FUNCTION_NAME]]")) {
|
|
18720
|
+
fPayload.message = fPayload.message.replace("[[FUNCTION_NAME]]", functionName);
|
|
18721
|
+
}
|
|
18722
|
+
}
|
|
18708
18723
|
const functionRegistry = new FunctionRegistry();
|
|
18709
18724
|
for (let category of categories) {
|
|
18710
18725
|
const fns = category.functions;
|
|
@@ -19472,7 +19487,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
|
|
|
19472
19487
|
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
19473
19488
|
// tooltipItem.parsed.y can be an object or a number for pie charts
|
|
19474
19489
|
const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed;
|
|
19475
|
-
const toolTipFormat = !format && yLabel
|
|
19490
|
+
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
19476
19491
|
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
19477
19492
|
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
19478
19493
|
},
|
|
@@ -19772,7 +19787,10 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
19772
19787
|
if (isNaN(value))
|
|
19773
19788
|
return value;
|
|
19774
19789
|
const { locale, format } = localeFormat;
|
|
19775
|
-
return formatValue(value, {
|
|
19790
|
+
return formatValue(value, {
|
|
19791
|
+
locale,
|
|
19792
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
19793
|
+
});
|
|
19776
19794
|
},
|
|
19777
19795
|
},
|
|
19778
19796
|
},
|
|
@@ -20293,7 +20311,10 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
|
|
|
20293
20311
|
if (isNaN(value))
|
|
20294
20312
|
return value;
|
|
20295
20313
|
const { locale, format } = localeFormat;
|
|
20296
|
-
return formatValue(value, {
|
|
20314
|
+
return formatValue(value, {
|
|
20315
|
+
locale,
|
|
20316
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
20317
|
+
});
|
|
20297
20318
|
},
|
|
20298
20319
|
},
|
|
20299
20320
|
},
|
|
@@ -20633,7 +20654,7 @@ function getPieConfiguration(chart, labels, localeFormat) {
|
|
|
20633
20654
|
const percentage = calculatePercentage(data, dataIndex);
|
|
20634
20655
|
const xLabel = tooltipItem.label || tooltipItem.dataset.label;
|
|
20635
20656
|
const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed;
|
|
20636
|
-
const toolTipFormat = !format && yLabel
|
|
20657
|
+
const toolTipFormat = !format && yLabel >= 1000 ? "#,##" : format;
|
|
20637
20658
|
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
20638
20659
|
return xLabel ? `${xLabel}: ${yLabelStr} (${percentage}%)` : `${yLabelStr} (${percentage}%)`;
|
|
20639
20660
|
};
|
|
@@ -22885,6 +22906,7 @@ class LinkEditor extends Component {
|
|
|
22885
22906
|
this.save();
|
|
22886
22907
|
}
|
|
22887
22908
|
ev.stopPropagation();
|
|
22909
|
+
ev.preventDefault();
|
|
22888
22910
|
break;
|
|
22889
22911
|
case "Escape":
|
|
22890
22912
|
this.cancel();
|
|
@@ -32049,17 +32071,13 @@ class Composer extends Component {
|
|
|
32049
32071
|
this.DOMFocusableElementStore.setFocusableElement(el);
|
|
32050
32072
|
}
|
|
32051
32073
|
this.contentHelper.updateEl(el);
|
|
32052
|
-
this.processTokenAtCursor();
|
|
32053
32074
|
});
|
|
32054
32075
|
useEffect(() => {
|
|
32055
32076
|
this.processContent();
|
|
32056
32077
|
});
|
|
32057
|
-
|
|
32058
|
-
|
|
32059
|
-
|
|
32060
|
-
this.processTokenAtCursor();
|
|
32061
|
-
}
|
|
32062
|
-
});
|
|
32078
|
+
useEffect(() => {
|
|
32079
|
+
this.processTokenAtCursor();
|
|
32080
|
+
}, () => [this.composerStore.editionMode !== "inactive"]);
|
|
32063
32081
|
}
|
|
32064
32082
|
// ---------------------------------------------------------------------------
|
|
32065
32083
|
// Handlers
|
|
@@ -33161,6 +33179,10 @@ css /*SCSS*/ `
|
|
|
33161
33179
|
height: 0px;
|
|
33162
33180
|
}
|
|
33163
33181
|
}
|
|
33182
|
+
.o-figure-container {
|
|
33183
|
+
-webkit-user-select: none; // safari
|
|
33184
|
+
user-select: none;
|
|
33185
|
+
}
|
|
33164
33186
|
`;
|
|
33165
33187
|
/**
|
|
33166
33188
|
* Each figure ⭐ is positioned inside a container `div` placed and sized
|
|
@@ -40352,7 +40374,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
40352
40374
|
this.clearBorders(cmd.sheetId, cmd.target);
|
|
40353
40375
|
break;
|
|
40354
40376
|
case "REMOVE_COLUMNS_ROWS":
|
|
40355
|
-
for (let el of cmd.elements) {
|
|
40377
|
+
for (let el of [...cmd.elements].sort((a, b) => b - a)) {
|
|
40356
40378
|
if (cmd.dimension === "COL") {
|
|
40357
40379
|
this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
|
|
40358
40380
|
}
|
|
@@ -45891,6 +45913,353 @@ class CompilationParametersBuilder {
|
|
|
45891
45913
|
}
|
|
45892
45914
|
}
|
|
45893
45915
|
|
|
45916
|
+
/**
|
|
45917
|
+
* ####################################################
|
|
45918
|
+
* # INTRODUCTION
|
|
45919
|
+
* ####################################################
|
|
45920
|
+
*
|
|
45921
|
+
* This file contain the function recomputeZones.
|
|
45922
|
+
* This function try to recompute in a performant way
|
|
45923
|
+
* an ensemble of zones possibly overlapping to avoid
|
|
45924
|
+
* overlapping and to reduce the number of zones.
|
|
45925
|
+
*
|
|
45926
|
+
* It also allows to remove some zones from the ensemble.
|
|
45927
|
+
*
|
|
45928
|
+
* In the following example, 2 zones are overlapping.
|
|
45929
|
+
* Applying recomputeZones will return zones without
|
|
45930
|
+
* overlapping:
|
|
45931
|
+
*
|
|
45932
|
+
* ["B3:D4", "D2:E3"] ["B3:C4", "D2:D4", "E2:E3"]
|
|
45933
|
+
*
|
|
45934
|
+
* A B C D E A B C D E
|
|
45935
|
+
* 1 ___ 1 ___
|
|
45936
|
+
* 2 ___|_ | 2 ___| | |
|
|
45937
|
+
* 3 | |_|_| ---> 3 | | |_|
|
|
45938
|
+
* 4 |_____| 4 |___|_|
|
|
45939
|
+
* 6 6
|
|
45940
|
+
* 7 7
|
|
45941
|
+
*
|
|
45942
|
+
*
|
|
45943
|
+
* In the following example, 2 zones are contiguous.
|
|
45944
|
+
* Applying recomputeZones will return only one zone:
|
|
45945
|
+
*
|
|
45946
|
+
* ["B2:B3", "C2:D3"] ["B2:D3"]
|
|
45947
|
+
*
|
|
45948
|
+
* A B C D E A B C D E
|
|
45949
|
+
* 1 _ ___ 1 _____
|
|
45950
|
+
* 2 | | | ---> 2 | |
|
|
45951
|
+
* 3 |_|___| 3 |_____|
|
|
45952
|
+
* 4 4
|
|
45953
|
+
*
|
|
45954
|
+
*
|
|
45955
|
+
* In the following example, we want to remove a zone
|
|
45956
|
+
* from the ensemble. Applying recomputeZones will
|
|
45957
|
+
* return the ensemble without the zone to remove:
|
|
45958
|
+
*
|
|
45959
|
+
* remove ["C3:D3"] ["B2:B4", "C2:D2",
|
|
45960
|
+
* "C4:D4", "E2:E4"]
|
|
45961
|
+
*
|
|
45962
|
+
* A B C D E F A B C D E F
|
|
45963
|
+
* 1 _______ 1 _______
|
|
45964
|
+
* 2 | | ---> 2 | |___| |
|
|
45965
|
+
* 3 | xxx | 3 | |___| |
|
|
45966
|
+
* 4 |_______| 4 |_|___|_|
|
|
45967
|
+
* 5 5
|
|
45968
|
+
*
|
|
45969
|
+
*
|
|
45970
|
+
* The exercise seems simple when we have only 2 zones.
|
|
45971
|
+
* But with n zones and in a performant way, we want to
|
|
45972
|
+
* avoid comparing each zone with all the others.
|
|
45973
|
+
*
|
|
45974
|
+
*
|
|
45975
|
+
* ####################################################
|
|
45976
|
+
* # Methodological approach
|
|
45977
|
+
* ####################################################
|
|
45978
|
+
*
|
|
45979
|
+
* The methodological approach to avoid comparing each
|
|
45980
|
+
* zone with all the others is to use a data structure
|
|
45981
|
+
* that allow to quickly find which zones are
|
|
45982
|
+
* overlapping with any other given zone.
|
|
45983
|
+
*
|
|
45984
|
+
* Here the idea is to profile the zones at the columns level.
|
|
45985
|
+
*
|
|
45986
|
+
* To do that, we propose to use a data structure
|
|
45987
|
+
* composed of 2 parts:
|
|
45988
|
+
* - profilesStartingPosition: a sorted number array
|
|
45989
|
+
* indicating on which columns a new profile begins.
|
|
45990
|
+
* - profiles: a map where the key is a column
|
|
45991
|
+
* position (from profilesStartingPosition) and the
|
|
45992
|
+
* value is a sorted number array representing a
|
|
45993
|
+
* profile.
|
|
45994
|
+
*
|
|
45995
|
+
*
|
|
45996
|
+
* See the following example: here profileStartingPosition
|
|
45997
|
+
* corresponds to [A,C,E,G,K]
|
|
45998
|
+
* A B C D E F G H I J K so with number [0,2,4,6,10]
|
|
45999
|
+
* 1 ' ' ' '
|
|
46000
|
+
* 2 ' ' '_______' here profile correspond
|
|
46001
|
+
* 3 '___' |_______| for A to []
|
|
46002
|
+
* 4 | | for C to [3, 5]
|
|
46003
|
+
* 5 |___| for E to []
|
|
46004
|
+
* 6 for G to [2, 3]
|
|
46005
|
+
* 7 for K to []
|
|
46006
|
+
*
|
|
46007
|
+
*
|
|
46008
|
+
* Now we can easily find which zones are overlapping
|
|
46009
|
+
* with a given zone. Suppose we want to add a new zone
|
|
46010
|
+
* D5:H6 to the ensemble:
|
|
46011
|
+
*
|
|
46012
|
+
* With a binary search of left and right
|
|
46013
|
+
* A B C D E F G H I J K on profilesStartingPosition, we can
|
|
46014
|
+
* 1 ' ' ' ' find the indexes of the profiles on which
|
|
46015
|
+
* 2 ' ' '_______' to apply a modification.
|
|
46016
|
+
* 3 '___' |_______|
|
|
46017
|
+
* 4 | _|_______ Here we will:
|
|
46018
|
+
* 5 |_|_| | - add a new profile in D --> become [3, 6]
|
|
46019
|
+
* 6 |_________| - modify the profile in E --> become [4, 6]
|
|
46020
|
+
* 7 - modify the profile in G --> become [2, 3, 4, 6]
|
|
46021
|
+
* - add a new profile in I --> become [8, 10]
|
|
46022
|
+
*
|
|
46023
|
+
* See below the result:
|
|
46024
|
+
*
|
|
46025
|
+
* Note the particularity of the profile
|
|
46026
|
+
* A B C D E F G H I J K for G: it will correspond to [2, 3, 4, 6]
|
|
46027
|
+
* 1 ' ' ' ' ' '
|
|
46028
|
+
* 2 ' ' ' '___'___' To know how to modify the profile (add a
|
|
46029
|
+
* 3 '_'_' |___|___| zone or remove it) we do a binary
|
|
46030
|
+
* 4 | | |___ ___ search of the top and bottom value on the
|
|
46031
|
+
* 5 |_| | | | profile array. Depending on the result index
|
|
46032
|
+
* 6 |_|___|___| parity (odd or even), because zone boundaries
|
|
46033
|
+
* 7 go by pairs, we know if we are in a zone or
|
|
46034
|
+
* not and how operate.
|
|
46035
|
+
*/
|
|
46036
|
+
/**
|
|
46037
|
+
* Recompute the zone without the cells in toRemoveZones and avoid overlapping.
|
|
46038
|
+
* This compute is particularly useful because after this function:
|
|
46039
|
+
* - you will find coordinate of a cell only once among all the zones
|
|
46040
|
+
* - the number of zones will be reduced to the minimum
|
|
46041
|
+
*/
|
|
46042
|
+
function futureRecomputeZones(zones, zonesToRemove = []) {
|
|
46043
|
+
const profilesStartingPosition = [0];
|
|
46044
|
+
const profiles = new Map([[0, []]]);
|
|
46045
|
+
modifyProfiles(profilesStartingPosition, profiles, zones, false);
|
|
46046
|
+
modifyProfiles(profilesStartingPosition, profiles, zonesToRemove, true);
|
|
46047
|
+
return constructZonesFromProfiles(profilesStartingPosition, profiles);
|
|
46048
|
+
}
|
|
46049
|
+
function modifyProfiles(// export for testing only
|
|
46050
|
+
profilesStartingPosition, profiles, zones, toRemove = false) {
|
|
46051
|
+
for (const zone of zones) {
|
|
46052
|
+
const leftValue = zone.left;
|
|
46053
|
+
const rightValue = zone.right === undefined ? undefined : zone.right + 1;
|
|
46054
|
+
const leftIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, leftValue, true, 0);
|
|
46055
|
+
const rightIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, rightValue, false, leftIndex);
|
|
46056
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
46057
|
+
const profile = profiles.get(profilesStartingPosition[i]);
|
|
46058
|
+
modifyProfile(profile, zone, toRemove);
|
|
46059
|
+
}
|
|
46060
|
+
// maybe this part cost in performance, and maybe it's not necessary (depending on the use case). To be checked
|
|
46061
|
+
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
46062
|
+
}
|
|
46063
|
+
}
|
|
46064
|
+
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
46065
|
+
if (value === undefined) {
|
|
46066
|
+
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
46067
|
+
return profilesStartingPosition.length - 1;
|
|
46068
|
+
}
|
|
46069
|
+
const predecessorIndex = binaryPredecessorSearch(profilesStartingPosition, value, startIndex);
|
|
46070
|
+
if (value != profilesStartingPosition[predecessorIndex]) {
|
|
46071
|
+
// mean that the value is not ending/starting at the same position as the previous/next profile
|
|
46072
|
+
// --> it's a new profile
|
|
46073
|
+
// --> we need to add it
|
|
46074
|
+
profilesStartingPosition.splice(predecessorIndex + 1, 0, value);
|
|
46075
|
+
// suppose the we want to add the for the left value
|
|
46076
|
+
// following profile following zone: 'C', the predecessor index
|
|
46077
|
+
// for B: [1, 3] "C3:D4" correspond to 'B'.
|
|
46078
|
+
// The next line code will
|
|
46079
|
+
// A B C D A B C D copy the profile of 'B'
|
|
46080
|
+
// 1 '___' 1 '___' to 'C'. In the rest of the
|
|
46081
|
+
// 2 | | ---> 2 | _|_ process the 'modifyProfile'
|
|
46082
|
+
// 3 |___| 3 |_|_| | function will adapt the waiting
|
|
46083
|
+
// 4 4 |___| 'C' profile [1, 3] to the
|
|
46084
|
+
// correct 'C' profile [1, 4]
|
|
46085
|
+
profiles.set(value, [...profiles.get(profilesStartingPosition[predecessorIndex])]);
|
|
46086
|
+
return searchLeft ? predecessorIndex + 1 : predecessorIndex;
|
|
46087
|
+
}
|
|
46088
|
+
return searchLeft ? predecessorIndex : predecessorIndex - 1;
|
|
46089
|
+
}
|
|
46090
|
+
/**
|
|
46091
|
+
* Suppose the following Suppose we want to add We want to have the
|
|
46092
|
+
* profile: the following zone: following profile:
|
|
46093
|
+
*
|
|
46094
|
+
* A B C D E F A B C D E F A B C D E F
|
|
46095
|
+
* 1 '___' 1 ' ' 1 '___'
|
|
46096
|
+
* 2 |___| 2 '___' 2 | |
|
|
46097
|
+
* 3 ' ' 3 | | 3 | |
|
|
46098
|
+
* 4 '___' --> 4 | | --> 4 | |
|
|
46099
|
+
* 6 | | 6 |___| 6 | |
|
|
46100
|
+
* 7 |___| 7 7 |___|
|
|
46101
|
+
* 8 8 8
|
|
46102
|
+
*
|
|
46103
|
+
* the profile for 'C' the top zone correspond Here [2, 3, 5, 8] with [3, 7]
|
|
46104
|
+
* corresponds to: to 3 and the bottom zone would be merged into [2, 8]
|
|
46105
|
+
* ____ ____ correspond to 6
|
|
46106
|
+
* [2, 3, 5, 8] would be the profile: The difficulty of modify profile
|
|
46107
|
+
* ____ is to know what must be deleted
|
|
46108
|
+
* Note that the 'filled [3, 7] and what must be added to the
|
|
46109
|
+
* zone' are always between existing profile.
|
|
46110
|
+
* an even index and its
|
|
46111
|
+
* next index
|
|
46112
|
+
*
|
|
46113
|
+
*/
|
|
46114
|
+
function modifyProfile(profile, zone, toRemove = false) {
|
|
46115
|
+
const topValue = zone.top;
|
|
46116
|
+
const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
|
|
46117
|
+
const newPoints = [];
|
|
46118
|
+
// Case we want to add a zone to the profile:
|
|
46119
|
+
// - If the top predecessor index `topPredIndex` is even, it means the top of the zone is already positioned on a filled zone
|
|
46120
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the predecessor.
|
|
46121
|
+
// - If it is odd, it means the top of the zone must be the beginning of a filled zone.
|
|
46122
|
+
// so we can keep the index of the top position
|
|
46123
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
46124
|
+
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, false);
|
|
46125
|
+
if ((topPredIndex % 2 !== 0 && !toRemove) || (topPredIndex % 2 === 0 && toRemove)) {
|
|
46126
|
+
newPoints.push(topValue);
|
|
46127
|
+
}
|
|
46128
|
+
if (bottomValue === undefined) {
|
|
46129
|
+
// The following two code lines will not impact the final result,
|
|
46130
|
+
// but they will impact the intermediate profile.
|
|
46131
|
+
// We keep them for performance reason
|
|
46132
|
+
profile.splice(topPredIndex + 1);
|
|
46133
|
+
profile.push(...newPoints);
|
|
46134
|
+
return;
|
|
46135
|
+
}
|
|
46136
|
+
// Case we want to add a zone to the profile:
|
|
46137
|
+
// - If the bottom successor index `bottomSuccIndex` is even, it means the bottom of the zone must be the ending of a filled zone
|
|
46138
|
+
// so we can keep the index of the bottom position.
|
|
46139
|
+
// - If it is odd, it means the bottom of the zone is already positioned on a filled zone
|
|
46140
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the successor
|
|
46141
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
46142
|
+
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, false);
|
|
46143
|
+
if ((bottomSuccIndex % 2 === 0 && !toRemove) || (bottomSuccIndex % 2 !== 0 && toRemove)) {
|
|
46144
|
+
newPoints.push(bottomValue);
|
|
46145
|
+
}
|
|
46146
|
+
// add the top and bottom value to the profile and
|
|
46147
|
+
// remove all information between the top and bottom index
|
|
46148
|
+
profile.splice(topPredIndex + 1, bottomSuccIndex - topPredIndex - 1, ...newPoints);
|
|
46149
|
+
}
|
|
46150
|
+
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
46151
|
+
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
46152
|
+
const end = rightIndex === profilesStartingPosition.length - 1 ? rightIndex : rightIndex + 1;
|
|
46153
|
+
for (let i = end; i > start; i--) {
|
|
46154
|
+
if (deepEqualsArray(profiles.get(profilesStartingPosition[i]), profiles.get(profilesStartingPosition[i - 1]))) {
|
|
46155
|
+
profiles.delete(profilesStartingPosition[i]);
|
|
46156
|
+
profilesStartingPosition.splice(i, 1);
|
|
46157
|
+
}
|
|
46158
|
+
}
|
|
46159
|
+
}
|
|
46160
|
+
function constructZonesFromProfiles(profilesStartingPosition, profiles) {
|
|
46161
|
+
const mergedZone = [];
|
|
46162
|
+
let pendingZones = [];
|
|
46163
|
+
for (let colIndex = 0; colIndex < profilesStartingPosition.length; colIndex++) {
|
|
46164
|
+
const left = profilesStartingPosition[colIndex];
|
|
46165
|
+
const profile = profiles.get(left);
|
|
46166
|
+
if (!profile || profile.length === 0) {
|
|
46167
|
+
mergedZone.push(...pendingZones);
|
|
46168
|
+
pendingZones = [];
|
|
46169
|
+
continue;
|
|
46170
|
+
}
|
|
46171
|
+
let right = profilesStartingPosition[colIndex + 1];
|
|
46172
|
+
if (right !== undefined) {
|
|
46173
|
+
right--;
|
|
46174
|
+
}
|
|
46175
|
+
const nextPendingZones = [];
|
|
46176
|
+
for (let i = 0; i < profile.length; i += 2) {
|
|
46177
|
+
const top = profile[i];
|
|
46178
|
+
let bottom = profile[i + 1];
|
|
46179
|
+
if (bottom !== undefined) {
|
|
46180
|
+
bottom--;
|
|
46181
|
+
}
|
|
46182
|
+
const profileZone = {
|
|
46183
|
+
top,
|
|
46184
|
+
left,
|
|
46185
|
+
bottom,
|
|
46186
|
+
right,
|
|
46187
|
+
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
46188
|
+
};
|
|
46189
|
+
let findCorrespondingZone = false;
|
|
46190
|
+
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
46191
|
+
const pendingZone = pendingZones[j];
|
|
46192
|
+
if (pendingZone.top === profileZone.top && pendingZone.bottom === profileZone.bottom) {
|
|
46193
|
+
pendingZone.right = profileZone.right;
|
|
46194
|
+
pendingZones.splice(j, 1);
|
|
46195
|
+
nextPendingZones.push(pendingZone);
|
|
46196
|
+
findCorrespondingZone = true;
|
|
46197
|
+
break;
|
|
46198
|
+
}
|
|
46199
|
+
}
|
|
46200
|
+
if (!findCorrespondingZone) {
|
|
46201
|
+
nextPendingZones.push(profileZone);
|
|
46202
|
+
}
|
|
46203
|
+
}
|
|
46204
|
+
mergedZone.push(...pendingZones);
|
|
46205
|
+
pendingZones = nextPendingZones;
|
|
46206
|
+
}
|
|
46207
|
+
mergedZone.push(...pendingZones);
|
|
46208
|
+
return mergedZone;
|
|
46209
|
+
}
|
|
46210
|
+
function binaryPredecessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
46211
|
+
let end = arr.length - 1;
|
|
46212
|
+
let result = -1;
|
|
46213
|
+
while (start <= end) {
|
|
46214
|
+
const mid = Math.floor((start + end) / 2);
|
|
46215
|
+
if (arr[mid] === val && matchEqual) {
|
|
46216
|
+
return mid;
|
|
46217
|
+
}
|
|
46218
|
+
else if (arr[mid] < val) {
|
|
46219
|
+
result = mid;
|
|
46220
|
+
start = mid + 1;
|
|
46221
|
+
}
|
|
46222
|
+
else {
|
|
46223
|
+
end = mid - 1;
|
|
46224
|
+
}
|
|
46225
|
+
}
|
|
46226
|
+
return result;
|
|
46227
|
+
}
|
|
46228
|
+
function binarySuccessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
46229
|
+
let end = arr.length - 1;
|
|
46230
|
+
let result = arr.length;
|
|
46231
|
+
while (start <= end) {
|
|
46232
|
+
const mid = Math.floor((start + end) / 2);
|
|
46233
|
+
if (arr[mid] === val && matchEqual) {
|
|
46234
|
+
return mid;
|
|
46235
|
+
}
|
|
46236
|
+
else if (arr[mid] > val) {
|
|
46237
|
+
result = mid;
|
|
46238
|
+
end = mid - 1;
|
|
46239
|
+
}
|
|
46240
|
+
else {
|
|
46241
|
+
start = mid + 1;
|
|
46242
|
+
}
|
|
46243
|
+
}
|
|
46244
|
+
return result;
|
|
46245
|
+
}
|
|
46246
|
+
/**
|
|
46247
|
+
* Compares two arrays.
|
|
46248
|
+
* For performance reasons, this function is to be preferred
|
|
46249
|
+
* to 'deepEquals' in the case we know that the inputs are arrays.
|
|
46250
|
+
*/
|
|
46251
|
+
function deepEqualsArray(arr1, arr2) {
|
|
46252
|
+
if (arr1.length !== arr2.length) {
|
|
46253
|
+
return false;
|
|
46254
|
+
}
|
|
46255
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
46256
|
+
if (!deepEquals(arr1[i], arr2[i])) {
|
|
46257
|
+
return false;
|
|
46258
|
+
}
|
|
46259
|
+
}
|
|
46260
|
+
return true;
|
|
46261
|
+
}
|
|
46262
|
+
|
|
45894
46263
|
class PositionMap {
|
|
45895
46264
|
map = {};
|
|
45896
46265
|
set({ sheetId, col, row }, value) {
|
|
@@ -46689,7 +47058,7 @@ class FormulaDependencyGraph {
|
|
|
46689
47058
|
}
|
|
46690
47059
|
}
|
|
46691
47060
|
/**
|
|
46692
|
-
* Return the
|
|
47061
|
+
* Return all the cells that depend on the provided ranges,
|
|
46693
47062
|
* in the correct order they should be evaluated.
|
|
46694
47063
|
* This is called a topological ordering (excluding cycles)
|
|
46695
47064
|
*/
|
|
@@ -46700,11 +47069,19 @@ class FormulaDependencyGraph {
|
|
|
46700
47069
|
const range = queue.pop();
|
|
46701
47070
|
visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
|
|
46702
47071
|
const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
|
|
47072
|
+
const nextInQueue = {};
|
|
46703
47073
|
for (const position of impactedPositions) {
|
|
46704
47074
|
if (!visited.has(position)) {
|
|
46705
|
-
|
|
47075
|
+
if (!nextInQueue[position.sheetId]) {
|
|
47076
|
+
nextInQueue[position.sheetId] = [];
|
|
47077
|
+
}
|
|
47078
|
+
nextInQueue[position.sheetId].push(positionToZone(position));
|
|
46706
47079
|
}
|
|
46707
47080
|
}
|
|
47081
|
+
for (const sheetId in nextInQueue) {
|
|
47082
|
+
const zones = futureRecomputeZones(nextInQueue[sheetId]);
|
|
47083
|
+
queue.push(...zones.map((zone) => ({ sheetId, zone })));
|
|
47084
|
+
}
|
|
46708
47085
|
}
|
|
46709
47086
|
visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
|
|
46710
47087
|
return visited;
|
|
@@ -47043,7 +47420,7 @@ class Evaluator {
|
|
|
47043
47420
|
}
|
|
47044
47421
|
if (!content) {
|
|
47045
47422
|
// The previous content could have blocked some array formulas
|
|
47046
|
-
impactedPositions.addMany(this.
|
|
47423
|
+
impactedPositions.addMany(this.getArrayFormulasBlockedBy(position));
|
|
47047
47424
|
}
|
|
47048
47425
|
}
|
|
47049
47426
|
return impactedPositions;
|
|
@@ -47086,14 +47463,23 @@ class Evaluator {
|
|
|
47086
47463
|
positions.fillAllPositions();
|
|
47087
47464
|
return positions;
|
|
47088
47465
|
}
|
|
47089
|
-
|
|
47466
|
+
/**
|
|
47467
|
+
* Return the position of formulas blocked by the given position
|
|
47468
|
+
* as well as all their dependencies.
|
|
47469
|
+
*/
|
|
47470
|
+
getArrayFormulasBlockedBy(position) {
|
|
47090
47471
|
if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
|
|
47091
47472
|
return [];
|
|
47092
47473
|
}
|
|
47093
47474
|
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
|
|
47094
47475
|
const positions = this.createEmptyPositionSet();
|
|
47095
47476
|
positions.addMany(arrayFormulas);
|
|
47096
|
-
|
|
47477
|
+
const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
|
|
47478
|
+
if (arrayFormulaPosition) {
|
|
47479
|
+
// ignore the formula spreading on the position. Keep only the blocked ones
|
|
47480
|
+
positions.delete(arrayFormulaPosition);
|
|
47481
|
+
}
|
|
47482
|
+
positions.addMany(this.getCellsDependingOn(positions));
|
|
47097
47483
|
return positions;
|
|
47098
47484
|
}
|
|
47099
47485
|
nextPositionsToUpdate = new PositionSet({});
|
|
@@ -47233,7 +47619,7 @@ class Evaluator {
|
|
|
47233
47619
|
}
|
|
47234
47620
|
this.evaluatedCells.delete(child);
|
|
47235
47621
|
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
47236
|
-
this.nextPositionsToUpdate.addMany(this.
|
|
47622
|
+
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
|
|
47237
47623
|
}
|
|
47238
47624
|
this.spreadingRelations.removeNode(position);
|
|
47239
47625
|
}
|
|
@@ -52084,7 +52470,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52084
52470
|
"isFilterActive",
|
|
52085
52471
|
];
|
|
52086
52472
|
filterValues = {};
|
|
52087
|
-
hiddenRows =
|
|
52473
|
+
hiddenRows = {};
|
|
52088
52474
|
isEvaluationDirty = false;
|
|
52089
52475
|
allowDispatch(cmd) {
|
|
52090
52476
|
switch (cmd.type) {
|
|
@@ -52128,11 +52514,11 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52128
52514
|
case "UNFOLD_HEADER_GROUP":
|
|
52129
52515
|
case "FOLD_ALL_HEADER_GROUPS":
|
|
52130
52516
|
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
52131
|
-
this.updateHiddenRows();
|
|
52517
|
+
this.updateHiddenRows(cmd.sheetId);
|
|
52132
52518
|
break;
|
|
52133
52519
|
case "UPDATE_FILTER":
|
|
52134
52520
|
this.updateFilter(cmd);
|
|
52135
|
-
this.updateHiddenRows();
|
|
52521
|
+
this.updateHiddenRows(cmd.sheetId);
|
|
52136
52522
|
break;
|
|
52137
52523
|
case "DUPLICATE_SHEET":
|
|
52138
52524
|
const filterValues = {};
|
|
@@ -52152,15 +52538,14 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52152
52538
|
}
|
|
52153
52539
|
finalize() {
|
|
52154
52540
|
if (this.isEvaluationDirty) {
|
|
52155
|
-
this.
|
|
52541
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
52542
|
+
this.updateHiddenRows(sheetId);
|
|
52543
|
+
}
|
|
52156
52544
|
this.isEvaluationDirty = false;
|
|
52157
52545
|
}
|
|
52158
52546
|
}
|
|
52159
52547
|
isRowFiltered(sheetId, row) {
|
|
52160
|
-
|
|
52161
|
-
return false;
|
|
52162
|
-
}
|
|
52163
|
-
return this.hiddenRows.has(row);
|
|
52548
|
+
return !!this.hiddenRows[sheetId]?.has(row);
|
|
52164
52549
|
}
|
|
52165
52550
|
getFilterHiddenValues(position) {
|
|
52166
52551
|
const id = this.getters.getFilterId(position);
|
|
@@ -52187,8 +52572,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52187
52572
|
this.filterValues[sheetId] = {};
|
|
52188
52573
|
this.filterValues[sheetId][id] = hiddenValues;
|
|
52189
52574
|
}
|
|
52190
|
-
updateHiddenRows() {
|
|
52191
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52575
|
+
updateHiddenRows(sheetId) {
|
|
52192
52576
|
const filters = this.getters
|
|
52193
52577
|
.getFilters(sheetId)
|
|
52194
52578
|
.sort((filter1, filter2) => filter1.rangeWithHeaders.zone.top - filter2.rangeWithHeaders.zone.top);
|
|
@@ -52210,7 +52594,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52210
52594
|
}
|
|
52211
52595
|
}
|
|
52212
52596
|
}
|
|
52213
|
-
this.hiddenRows = hiddenRows;
|
|
52597
|
+
this.hiddenRows[sheetId] = hiddenRows;
|
|
52214
52598
|
}
|
|
52215
52599
|
getCellValueAsString(sheetId, col, row) {
|
|
52216
52600
|
const value = this.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue;
|
|
@@ -53329,13 +53713,14 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53329
53713
|
}
|
|
53330
53714
|
}
|
|
53331
53715
|
handleEvent(event) {
|
|
53716
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
53332
53717
|
if (event.options.scrollIntoView) {
|
|
53333
53718
|
let { col, row } = findCellInNewZone(event.previousAnchor.zone, event.anchor.zone);
|
|
53334
53719
|
if (event.mode === "updateAnchor") {
|
|
53335
53720
|
const oldZone = event.previousAnchor.zone;
|
|
53336
53721
|
const newZone = event.anchor.zone;
|
|
53337
53722
|
// altering a zone should not move the viewport in a dimension that wasn't changed
|
|
53338
|
-
const { top, bottom, left, right } = this.
|
|
53723
|
+
const { top, bottom, left, right } = this.getMainInternalViewport(sheetId);
|
|
53339
53724
|
if (oldZone.left === newZone.left && oldZone.right === newZone.right) {
|
|
53340
53725
|
col = left > col || col > right ? left : col;
|
|
53341
53726
|
}
|
|
@@ -53343,7 +53728,6 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53343
53728
|
row = top > row || row > bottom ? top : row;
|
|
53344
53729
|
}
|
|
53345
53730
|
}
|
|
53346
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
53347
53731
|
col = Math.min(col, this.getters.getNumberCols(sheetId) - 1);
|
|
53348
53732
|
row = Math.min(row, this.getters.getNumberRows(sheetId) - 1);
|
|
53349
53733
|
if (!this.sheetsWithDirtyViewports.has(sheetId)) {
|
|
@@ -53380,16 +53764,16 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53380
53764
|
this.setSheetViewOffset(cmd.offsetX, cmd.offsetY);
|
|
53381
53765
|
break;
|
|
53382
53766
|
case "SHIFT_VIEWPORT_DOWN":
|
|
53383
|
-
const { top } = this.getActiveMainViewport();
|
|
53384
53767
|
const sheetId = this.getters.getActiveSheetId();
|
|
53385
|
-
const
|
|
53386
|
-
this.
|
|
53768
|
+
const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
|
|
53769
|
+
const topRowDims = this.getters.getRowDimensions(sheetId, top);
|
|
53770
|
+
this.shiftVertically(topRowDims.start + viewportHeight - offsetCorrectionY);
|
|
53387
53771
|
break;
|
|
53388
53772
|
case "SHIFT_VIEWPORT_UP": {
|
|
53389
|
-
const { top } = this.getActiveMainViewport();
|
|
53390
53773
|
const sheetId = this.getters.getActiveSheetId();
|
|
53391
|
-
const
|
|
53392
|
-
this.
|
|
53774
|
+
const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
|
|
53775
|
+
const topRowDims = this.getters.getRowDimensions(sheetId, top);
|
|
53776
|
+
this.shiftVertically(topRowDims.end - offsetCorrectionY - viewportHeight);
|
|
53393
53777
|
break;
|
|
53394
53778
|
}
|
|
53395
53779
|
case "REMOVE_TABLE":
|
|
@@ -53812,17 +54196,6 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53812
54196
|
const { maxOffsetX, maxOffsetY } = this.getMaximumSheetOffset();
|
|
53813
54197
|
Object.values(this.getSubViewports(sheetId)).forEach((viewport) => viewport.setViewportOffset(clip(offsetX, 0, maxOffsetX), clip(offsetY, 0, maxOffsetY)));
|
|
53814
54198
|
}
|
|
53815
|
-
/**
|
|
53816
|
-
* Clip the vertical offset within the allowed range.
|
|
53817
|
-
* Not above the sheet, nor below the sheet.
|
|
53818
|
-
*/
|
|
53819
|
-
clipOffsetY(offsetY) {
|
|
53820
|
-
const { height } = this.getMainViewportRect();
|
|
53821
|
-
const maxOffset = height - this.sheetViewHeight;
|
|
53822
|
-
offsetY = Math.min(offsetY, maxOffset);
|
|
53823
|
-
offsetY = Math.max(offsetY, 0);
|
|
53824
|
-
return offsetY;
|
|
53825
|
-
}
|
|
53826
54199
|
getViewportOffset(sheetId) {
|
|
53827
54200
|
return {
|
|
53828
54201
|
x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0,
|
|
@@ -53878,12 +54251,15 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53878
54251
|
* viewport top.
|
|
53879
54252
|
*/
|
|
53880
54253
|
shiftVertically(offset) {
|
|
53881
|
-
const
|
|
54254
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
54255
|
+
const { top } = this.getMainInternalViewport(sheetId);
|
|
53882
54256
|
const { scrollX } = this.getActiveSheetScrollInfo();
|
|
53883
54257
|
this.setSheetViewOffset(scrollX, offset);
|
|
53884
54258
|
const { anchor } = this.getters.getSelection();
|
|
53885
|
-
|
|
53886
|
-
|
|
54259
|
+
if (anchor.cell.row >= this.getters.getPaneDivisions(sheetId).ySplit) {
|
|
54260
|
+
const deltaRow = this.getMainInternalViewport(sheetId).top - top;
|
|
54261
|
+
this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow);
|
|
54262
|
+
}
|
|
53887
54263
|
}
|
|
53888
54264
|
getVisibleFigures() {
|
|
53889
54265
|
const sheetId = this.getters.getActiveSheetId();
|
|
@@ -54802,7 +55178,6 @@ css /* scss */ `
|
|
|
54802
55178
|
cursor: pointer;
|
|
54803
55179
|
}
|
|
54804
55180
|
`;
|
|
54805
|
-
let tKey = 1;
|
|
54806
55181
|
class SpreadsheetDashboard extends Component {
|
|
54807
55182
|
static template = "o-spreadsheet-SpreadsheetDashboard";
|
|
54808
55183
|
static props = {};
|
|
@@ -54881,13 +55256,9 @@ class SpreadsheetDashboard extends Component {
|
|
|
54881
55256
|
coordinates: rect,
|
|
54882
55257
|
position: { col, row },
|
|
54883
55258
|
action,
|
|
54884
|
-
// we can't rely on position only because a row or a column could
|
|
54885
|
-
// be inserted at any time.
|
|
54886
|
-
tKey: `${tKey}-${col}-${row}`,
|
|
54887
55259
|
});
|
|
54888
55260
|
}
|
|
54889
55261
|
}
|
|
54890
|
-
tKey++;
|
|
54891
55262
|
return cells;
|
|
54892
55263
|
}
|
|
54893
55264
|
getClickableAction(position) {
|
|
@@ -60186,6 +60557,6 @@ const constants = {
|
|
|
60186
60557
|
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 };
|
|
60187
60558
|
|
|
60188
60559
|
|
|
60189
|
-
__info__.version = "17.2.
|
|
60190
|
-
__info__.date = "2024-04-
|
|
60191
|
-
__info__.hash = "
|
|
60560
|
+
__info__.version = "17.2.5";
|
|
60561
|
+
__info__.date = "2024-04-26T07:41:13.193Z";
|
|
60562
|
+
__info__.hash = "a730f5c";
|