@odoo/o-spreadsheet 17.0.55 → 17.0.57
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 +622 -478
- package/dist/o-spreadsheet.d.ts +19 -10
- package/dist/o-spreadsheet.esm.js +622 -478
- package/dist/o-spreadsheet.iife.js +622 -478
- package/dist/o-spreadsheet.iife.min.js +256 -256
- package/dist/o_spreadsheet.xml +3 -3
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.0.
|
|
6
|
-
* @date 2025-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.57
|
|
6
|
+
* @date 2025-04-14T17:13:56.677Z
|
|
7
|
+
* @hash 6ac8bb7
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
|
|
@@ -710,8 +710,7 @@ function removeFalsyAttributes(obj) {
|
|
|
710
710
|
*
|
|
711
711
|
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
|
|
712
712
|
*/
|
|
713
|
-
const
|
|
714
|
-
" ",
|
|
713
|
+
const specialWhiteSpaceSpecialCharacters = [
|
|
715
714
|
"\t",
|
|
716
715
|
"\f",
|
|
717
716
|
"\v",
|
|
@@ -726,7 +725,7 @@ const whiteSpaceSpecialCharacters = [
|
|
|
726
725
|
String.fromCharCode(parseInt("3000", 16)),
|
|
727
726
|
String.fromCharCode(parseInt("feff", 16)),
|
|
728
727
|
];
|
|
729
|
-
const
|
|
728
|
+
const specialWhiteSpaceRegexp = new RegExp(specialWhiteSpaceSpecialCharacters.join("|"), "g");
|
|
730
729
|
const newLineRegexp = /(\r\n|\r)/g;
|
|
731
730
|
/**
|
|
732
731
|
* Replace all different newlines characters by \n
|
|
@@ -2002,6 +2001,7 @@ const coreTypes = new Set([
|
|
|
2002
2001
|
"CLEAR_FORMATTING",
|
|
2003
2002
|
"SET_BORDER",
|
|
2004
2003
|
"SET_ZONE_BORDERS",
|
|
2004
|
+
"SET_BORDERS_ON_TARGET",
|
|
2005
2005
|
/** CHART */
|
|
2006
2006
|
"CREATE_CHART",
|
|
2007
2007
|
"UPDATE_CHART",
|
|
@@ -7531,8 +7531,12 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
|
7531
7531
|
str = replaceNewLines(str);
|
|
7532
7532
|
const chars = new TokenizingChars(str);
|
|
7533
7533
|
const result = [];
|
|
7534
|
+
const tokenizeSpace = specialWhiteSpaceRegexp.test(str)
|
|
7535
|
+
? tokenizeSpecialCharacterSpace
|
|
7536
|
+
: tokenizeSimpleSpace;
|
|
7534
7537
|
while (!chars.isOver()) {
|
|
7535
|
-
let token =
|
|
7538
|
+
let token = tokenizeNewLine(chars) ||
|
|
7539
|
+
tokenizeSpace(chars) ||
|
|
7536
7540
|
tokenizeArgsSeparator(chars, locale) ||
|
|
7537
7541
|
tokenizeParenthesis(chars) ||
|
|
7538
7542
|
tokenizeOperator(chars) ||
|
|
@@ -7666,17 +7670,19 @@ function tokenizeSymbol(chars) {
|
|
|
7666
7670
|
}
|
|
7667
7671
|
return null;
|
|
7668
7672
|
}
|
|
7669
|
-
function
|
|
7670
|
-
let
|
|
7671
|
-
while (chars.current ===
|
|
7672
|
-
|
|
7673
|
-
chars.shift();
|
|
7673
|
+
function tokenizeSpecialCharacterSpace(chars) {
|
|
7674
|
+
let spaces = "";
|
|
7675
|
+
while (chars.current === " " || (chars.current && chars.current.match(specialWhiteSpaceRegexp))) {
|
|
7676
|
+
spaces += chars.shift();
|
|
7674
7677
|
}
|
|
7675
|
-
if (
|
|
7676
|
-
return { type: "SPACE", value:
|
|
7678
|
+
if (spaces) {
|
|
7679
|
+
return { type: "SPACE", value: spaces };
|
|
7677
7680
|
}
|
|
7681
|
+
return null;
|
|
7682
|
+
}
|
|
7683
|
+
function tokenizeSimpleSpace(chars) {
|
|
7678
7684
|
let spaces = "";
|
|
7679
|
-
while (chars.current
|
|
7685
|
+
while (chars.current === " ") {
|
|
7680
7686
|
spaces += chars.shift();
|
|
7681
7687
|
}
|
|
7682
7688
|
if (spaces) {
|
|
@@ -7684,6 +7690,17 @@ function tokenizeSpace(chars) {
|
|
|
7684
7690
|
}
|
|
7685
7691
|
return null;
|
|
7686
7692
|
}
|
|
7693
|
+
function tokenizeNewLine(chars) {
|
|
7694
|
+
let length = 0;
|
|
7695
|
+
while (chars.current === NEWLINE) {
|
|
7696
|
+
length++;
|
|
7697
|
+
chars.shift();
|
|
7698
|
+
}
|
|
7699
|
+
if (length) {
|
|
7700
|
+
return { type: "SPACE", value: NEWLINE.repeat(length) };
|
|
7701
|
+
}
|
|
7702
|
+
return null;
|
|
7703
|
+
}
|
|
7687
7704
|
function tokenizeInvalidRange(chars) {
|
|
7688
7705
|
if (chars.currentStartsWith(INCORRECT_RANGE_STRING)) {
|
|
7689
7706
|
chars.advanceBy(INCORRECT_RANGE_STRING.length);
|
|
@@ -34667,6 +34684,15 @@ class BordersPlugin extends CorePlugin {
|
|
|
34667
34684
|
case "SET_BORDER":
|
|
34668
34685
|
this.setBorder(cmd.sheetId, cmd.col, cmd.row, cmd.border);
|
|
34669
34686
|
break;
|
|
34687
|
+
case "SET_BORDERS_ON_TARGET":
|
|
34688
|
+
for (const zone of cmd.target) {
|
|
34689
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
34690
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
34691
|
+
this.setBorder(cmd.sheetId, col, row, cmd.border);
|
|
34692
|
+
}
|
|
34693
|
+
}
|
|
34694
|
+
}
|
|
34695
|
+
break;
|
|
34670
34696
|
case "SET_ZONE_BORDERS":
|
|
34671
34697
|
if (cmd.border) {
|
|
34672
34698
|
const target = cmd.target.map((zone) => this.getters.expandZone(cmd.sheetId, zone));
|
|
@@ -36673,6 +36699,353 @@ class ChartPlugin extends CorePlugin {
|
|
|
36673
36699
|
}
|
|
36674
36700
|
}
|
|
36675
36701
|
|
|
36702
|
+
/**
|
|
36703
|
+
* ####################################################
|
|
36704
|
+
* # INTRODUCTION
|
|
36705
|
+
* ####################################################
|
|
36706
|
+
*
|
|
36707
|
+
* This file contain the function recomputeZones.
|
|
36708
|
+
* This function try to recompute in a performant way
|
|
36709
|
+
* an ensemble of zones possibly overlapping to avoid
|
|
36710
|
+
* overlapping and to reduce the number of zones.
|
|
36711
|
+
*
|
|
36712
|
+
* It also allows to remove some zones from the ensemble.
|
|
36713
|
+
*
|
|
36714
|
+
* In the following example, 2 zones are overlapping.
|
|
36715
|
+
* Applying recomputeZones will return zones without
|
|
36716
|
+
* overlapping:
|
|
36717
|
+
*
|
|
36718
|
+
* ["B3:D4", "D2:E3"] ["B3:C4", "D2:D4", "E2:E3"]
|
|
36719
|
+
*
|
|
36720
|
+
* A B C D E A B C D E
|
|
36721
|
+
* 1 ___ 1 ___
|
|
36722
|
+
* 2 ___|_ | 2 ___| | |
|
|
36723
|
+
* 3 | |_|_| ---> 3 | | |_|
|
|
36724
|
+
* 4 |_____| 4 |___|_|
|
|
36725
|
+
* 6 6
|
|
36726
|
+
* 7 7
|
|
36727
|
+
*
|
|
36728
|
+
*
|
|
36729
|
+
* In the following example, 2 zones are contiguous.
|
|
36730
|
+
* Applying recomputeZones will return only one zone:
|
|
36731
|
+
*
|
|
36732
|
+
* ["B2:B3", "C2:D3"] ["B2:D3"]
|
|
36733
|
+
*
|
|
36734
|
+
* A B C D E A B C D E
|
|
36735
|
+
* 1 _ ___ 1 _____
|
|
36736
|
+
* 2 | | | ---> 2 | |
|
|
36737
|
+
* 3 |_|___| 3 |_____|
|
|
36738
|
+
* 4 4
|
|
36739
|
+
*
|
|
36740
|
+
*
|
|
36741
|
+
* In the following example, we want to remove a zone
|
|
36742
|
+
* from the ensemble. Applying recomputeZones will
|
|
36743
|
+
* return the ensemble without the zone to remove:
|
|
36744
|
+
*
|
|
36745
|
+
* remove ["C3:D3"] ["B2:B4", "C2:D2",
|
|
36746
|
+
* "C4:D4", "E2:E4"]
|
|
36747
|
+
*
|
|
36748
|
+
* A B C D E F A B C D E F
|
|
36749
|
+
* 1 _______ 1 _______
|
|
36750
|
+
* 2 | | ---> 2 | |___| |
|
|
36751
|
+
* 3 | xxx | 3 | |___| |
|
|
36752
|
+
* 4 |_______| 4 |_|___|_|
|
|
36753
|
+
* 5 5
|
|
36754
|
+
*
|
|
36755
|
+
*
|
|
36756
|
+
* The exercise seems simple when we have only 2 zones.
|
|
36757
|
+
* But with n zones and in a performant way, we want to
|
|
36758
|
+
* avoid comparing each zone with all the others.
|
|
36759
|
+
*
|
|
36760
|
+
*
|
|
36761
|
+
* ####################################################
|
|
36762
|
+
* # Methodological approach
|
|
36763
|
+
* ####################################################
|
|
36764
|
+
*
|
|
36765
|
+
* The methodological approach to avoid comparing each
|
|
36766
|
+
* zone with all the others is to use a data structure
|
|
36767
|
+
* that allow to quickly find which zones are
|
|
36768
|
+
* overlapping with any other given zone.
|
|
36769
|
+
*
|
|
36770
|
+
* Here the idea is to profile the zones at the columns level.
|
|
36771
|
+
*
|
|
36772
|
+
* To do that, we propose to use a data structure
|
|
36773
|
+
* composed of 2 parts:
|
|
36774
|
+
* - profilesStartingPosition: a sorted number array
|
|
36775
|
+
* indicating on which columns a new profile begins.
|
|
36776
|
+
* - profiles: a map where the key is a column
|
|
36777
|
+
* position (from profilesStartingPosition) and the
|
|
36778
|
+
* value is a sorted number array representing a
|
|
36779
|
+
* profile.
|
|
36780
|
+
*
|
|
36781
|
+
*
|
|
36782
|
+
* See the following example: here profileStartingPosition
|
|
36783
|
+
* corresponds to [A,C,E,G,K]
|
|
36784
|
+
* A B C D E F G H I J K so with number [0,2,4,6,10]
|
|
36785
|
+
* 1 ' ' ' '
|
|
36786
|
+
* 2 ' ' '_______' here profile correspond
|
|
36787
|
+
* 3 '___' |_______| for A to []
|
|
36788
|
+
* 4 | | for C to [3, 5]
|
|
36789
|
+
* 5 |___| for E to []
|
|
36790
|
+
* 6 for G to [2, 3]
|
|
36791
|
+
* 7 for K to []
|
|
36792
|
+
*
|
|
36793
|
+
*
|
|
36794
|
+
* Now we can easily find which zones are overlapping
|
|
36795
|
+
* with a given zone. Suppose we want to add a new zone
|
|
36796
|
+
* D5:H6 to the ensemble:
|
|
36797
|
+
*
|
|
36798
|
+
* With a binary search of left and right
|
|
36799
|
+
* A B C D E F G H I J K on profilesStartingPosition, we can
|
|
36800
|
+
* 1 ' ' ' ' find the indexes of the profiles on which
|
|
36801
|
+
* 2 ' ' '_______' to apply a modification.
|
|
36802
|
+
* 3 '___' |_______|
|
|
36803
|
+
* 4 | _|_______ Here we will:
|
|
36804
|
+
* 5 |_|_| | - add a new profile in D --> become [3, 6]
|
|
36805
|
+
* 6 |_________| - modify the profile in E --> become [4, 6]
|
|
36806
|
+
* 7 - modify the profile in G --> become [2, 3, 4, 6]
|
|
36807
|
+
* - add a new profile in I --> become [8, 10]
|
|
36808
|
+
*
|
|
36809
|
+
* See below the result:
|
|
36810
|
+
*
|
|
36811
|
+
* Note the particularity of the profile
|
|
36812
|
+
* A B C D E F G H I J K for G: it will correspond to [2, 3, 4, 6]
|
|
36813
|
+
* 1 ' ' ' ' ' '
|
|
36814
|
+
* 2 ' ' ' '___'___' To know how to modify the profile (add a
|
|
36815
|
+
* 3 '_'_' |___|___| zone or remove it) we do a binary
|
|
36816
|
+
* 4 | | |___ ___ search of the top and bottom value on the
|
|
36817
|
+
* 5 |_| | | | profile array. Depending on the result index
|
|
36818
|
+
* 6 |_|___|___| parity (odd or even), because zone boundaries
|
|
36819
|
+
* 7 go by pairs, we know if we are in a zone or
|
|
36820
|
+
* not and how operate.
|
|
36821
|
+
*/
|
|
36822
|
+
/**
|
|
36823
|
+
* Recompute the zone without the cells in toRemoveZones and avoid overlapping.
|
|
36824
|
+
* This compute is particularly useful because after this function:
|
|
36825
|
+
* - you will find coordinate of a cell only once among all the zones
|
|
36826
|
+
* - the number of zones will be reduced to the minimum
|
|
36827
|
+
*/
|
|
36828
|
+
function futureRecomputeZones(zones, zonesToRemove = []) {
|
|
36829
|
+
const profilesStartingPosition = [0];
|
|
36830
|
+
const profiles = new Map([[0, []]]);
|
|
36831
|
+
modifyProfiles(profilesStartingPosition, profiles, zones, false);
|
|
36832
|
+
modifyProfiles(profilesStartingPosition, profiles, zonesToRemove, true);
|
|
36833
|
+
return constructZonesFromProfiles(profilesStartingPosition, profiles);
|
|
36834
|
+
}
|
|
36835
|
+
function modifyProfiles(// export for testing only
|
|
36836
|
+
profilesStartingPosition, profiles, zones, toRemove = false) {
|
|
36837
|
+
for (const zone of zones) {
|
|
36838
|
+
const leftValue = zone.left;
|
|
36839
|
+
const rightValue = zone.right === undefined ? undefined : zone.right + 1;
|
|
36840
|
+
const leftIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, leftValue, true, 0);
|
|
36841
|
+
const rightIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, rightValue, false, leftIndex);
|
|
36842
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
36843
|
+
const profile = profiles.get(profilesStartingPosition[i]);
|
|
36844
|
+
modifyProfile(profile, zone, toRemove);
|
|
36845
|
+
}
|
|
36846
|
+
// maybe this part cost in performance, and maybe it's not necessary (depending on the use case). To be checked
|
|
36847
|
+
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
36848
|
+
}
|
|
36849
|
+
}
|
|
36850
|
+
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
36851
|
+
if (value === undefined) {
|
|
36852
|
+
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
36853
|
+
return profilesStartingPosition.length - 1;
|
|
36854
|
+
}
|
|
36855
|
+
const predecessorIndex = binaryPredecessorSearch(profilesStartingPosition, value, startIndex);
|
|
36856
|
+
if (value != profilesStartingPosition[predecessorIndex]) {
|
|
36857
|
+
// mean that the value is not ending/starting at the same position as the previous/next profile
|
|
36858
|
+
// --> it's a new profile
|
|
36859
|
+
// --> we need to add it
|
|
36860
|
+
profilesStartingPosition.splice(predecessorIndex + 1, 0, value);
|
|
36861
|
+
// suppose the we want to add the for the left value
|
|
36862
|
+
// following profile following zone: 'C', the predecessor index
|
|
36863
|
+
// for B: [1, 3] "C3:D4" correspond to 'B'.
|
|
36864
|
+
// The next line code will
|
|
36865
|
+
// A B C D A B C D copy the profile of 'B'
|
|
36866
|
+
// 1 '___' 1 '___' to 'C'. In the rest of the
|
|
36867
|
+
// 2 | | ---> 2 | _|_ process the 'modifyProfile'
|
|
36868
|
+
// 3 |___| 3 |_|_| | function will adapt the waiting
|
|
36869
|
+
// 4 4 |___| 'C' profile [1, 3] to the
|
|
36870
|
+
// correct 'C' profile [1, 4]
|
|
36871
|
+
profiles.set(value, [...profiles.get(profilesStartingPosition[predecessorIndex])]);
|
|
36872
|
+
return searchLeft ? predecessorIndex + 1 : predecessorIndex;
|
|
36873
|
+
}
|
|
36874
|
+
return searchLeft ? predecessorIndex : predecessorIndex - 1;
|
|
36875
|
+
}
|
|
36876
|
+
/**
|
|
36877
|
+
* Suppose the following Suppose we want to add We want to have the
|
|
36878
|
+
* profile: the following zone: following profile:
|
|
36879
|
+
*
|
|
36880
|
+
* A B C D E F A B C D E F A B C D E F
|
|
36881
|
+
* 1 '___' 1 ' ' 1 '___'
|
|
36882
|
+
* 2 |___| 2 '___' 2 | |
|
|
36883
|
+
* 3 ' ' 3 | | 3 | |
|
|
36884
|
+
* 4 '___' --> 4 | | --> 4 | |
|
|
36885
|
+
* 6 | | 6 |___| 6 | |
|
|
36886
|
+
* 7 |___| 7 7 |___|
|
|
36887
|
+
* 8 8 8
|
|
36888
|
+
*
|
|
36889
|
+
* the profile for 'C' the top zone correspond Here [2, 3, 5, 8] with [3, 7]
|
|
36890
|
+
* corresponds to: to 3 and the bottom zone would be merged into [2, 8]
|
|
36891
|
+
* ____ ____ correspond to 6
|
|
36892
|
+
* [2, 3, 5, 8] would be the profile: The difficulty of modify profile
|
|
36893
|
+
* ____ is to know what must be deleted
|
|
36894
|
+
* Note that the 'filled [3, 7] and what must be added to the
|
|
36895
|
+
* zone' are always between existing profile.
|
|
36896
|
+
* an even index and its
|
|
36897
|
+
* next index
|
|
36898
|
+
*
|
|
36899
|
+
*/
|
|
36900
|
+
function modifyProfile(profile, zone, toRemove = false) {
|
|
36901
|
+
const topValue = zone.top;
|
|
36902
|
+
const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
|
|
36903
|
+
const newPoints = [];
|
|
36904
|
+
// Case we want to add a zone to the profile:
|
|
36905
|
+
// - If the top predecessor index `topPredIndex` is even, it means the top of the zone is already positioned on a filled zone
|
|
36906
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the predecessor.
|
|
36907
|
+
// - If it is odd, it means the top of the zone must be the beginning of a filled zone.
|
|
36908
|
+
// so we can keep the index of the top position
|
|
36909
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
36910
|
+
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, false);
|
|
36911
|
+
if ((topPredIndex % 2 !== 0 && !toRemove) || (topPredIndex % 2 === 0 && toRemove)) {
|
|
36912
|
+
newPoints.push(topValue);
|
|
36913
|
+
}
|
|
36914
|
+
if (bottomValue === undefined) {
|
|
36915
|
+
// The following two code lines will not impact the final result,
|
|
36916
|
+
// but they will impact the intermediate profile.
|
|
36917
|
+
// We keep them for performance reason
|
|
36918
|
+
profile.splice(topPredIndex + 1);
|
|
36919
|
+
profile.push(...newPoints);
|
|
36920
|
+
return;
|
|
36921
|
+
}
|
|
36922
|
+
// Case we want to add a zone to the profile:
|
|
36923
|
+
// - If the bottom successor index `bottomSuccIndex` is even, it means the bottom of the zone must be the ending of a filled zone
|
|
36924
|
+
// so we can keep the index of the bottom position.
|
|
36925
|
+
// - If it is odd, it means the bottom of the zone is already positioned on a filled zone
|
|
36926
|
+
// so we don't need to add it to the profile. we can keep in reference the index of the successor
|
|
36927
|
+
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
36928
|
+
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, false);
|
|
36929
|
+
if ((bottomSuccIndex % 2 === 0 && !toRemove) || (bottomSuccIndex % 2 !== 0 && toRemove)) {
|
|
36930
|
+
newPoints.push(bottomValue);
|
|
36931
|
+
}
|
|
36932
|
+
// add the top and bottom value to the profile and
|
|
36933
|
+
// remove all information between the top and bottom index
|
|
36934
|
+
profile.splice(topPredIndex + 1, bottomSuccIndex - topPredIndex - 1, ...newPoints);
|
|
36935
|
+
}
|
|
36936
|
+
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
36937
|
+
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
36938
|
+
const end = rightIndex === profilesStartingPosition.length - 1 ? rightIndex : rightIndex + 1;
|
|
36939
|
+
for (let i = end; i > start; i--) {
|
|
36940
|
+
if (deepEqualsArray(profiles.get(profilesStartingPosition[i]), profiles.get(profilesStartingPosition[i - 1]))) {
|
|
36941
|
+
profiles.delete(profilesStartingPosition[i]);
|
|
36942
|
+
profilesStartingPosition.splice(i, 1);
|
|
36943
|
+
}
|
|
36944
|
+
}
|
|
36945
|
+
}
|
|
36946
|
+
function constructZonesFromProfiles(profilesStartingPosition, profiles) {
|
|
36947
|
+
const mergedZone = [];
|
|
36948
|
+
let pendingZones = [];
|
|
36949
|
+
for (let colIndex = 0; colIndex < profilesStartingPosition.length; colIndex++) {
|
|
36950
|
+
const left = profilesStartingPosition[colIndex];
|
|
36951
|
+
const profile = profiles.get(left);
|
|
36952
|
+
if (!profile || profile.length === 0) {
|
|
36953
|
+
mergedZone.push(...pendingZones);
|
|
36954
|
+
pendingZones = [];
|
|
36955
|
+
continue;
|
|
36956
|
+
}
|
|
36957
|
+
let right = profilesStartingPosition[colIndex + 1];
|
|
36958
|
+
if (right !== undefined) {
|
|
36959
|
+
right--;
|
|
36960
|
+
}
|
|
36961
|
+
const nextPendingZones = [];
|
|
36962
|
+
for (let i = 0; i < profile.length; i += 2) {
|
|
36963
|
+
const top = profile[i];
|
|
36964
|
+
let bottom = profile[i + 1];
|
|
36965
|
+
if (bottom !== undefined) {
|
|
36966
|
+
bottom--;
|
|
36967
|
+
}
|
|
36968
|
+
const profileZone = {
|
|
36969
|
+
top,
|
|
36970
|
+
left,
|
|
36971
|
+
bottom,
|
|
36972
|
+
right,
|
|
36973
|
+
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
36974
|
+
};
|
|
36975
|
+
let findCorrespondingZone = false;
|
|
36976
|
+
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
36977
|
+
const pendingZone = pendingZones[j];
|
|
36978
|
+
if (pendingZone.top === profileZone.top && pendingZone.bottom === profileZone.bottom) {
|
|
36979
|
+
pendingZone.right = profileZone.right;
|
|
36980
|
+
pendingZones.splice(j, 1);
|
|
36981
|
+
nextPendingZones.push(pendingZone);
|
|
36982
|
+
findCorrespondingZone = true;
|
|
36983
|
+
break;
|
|
36984
|
+
}
|
|
36985
|
+
}
|
|
36986
|
+
if (!findCorrespondingZone) {
|
|
36987
|
+
nextPendingZones.push(profileZone);
|
|
36988
|
+
}
|
|
36989
|
+
}
|
|
36990
|
+
mergedZone.push(...pendingZones);
|
|
36991
|
+
pendingZones = nextPendingZones;
|
|
36992
|
+
}
|
|
36993
|
+
mergedZone.push(...pendingZones);
|
|
36994
|
+
return mergedZone;
|
|
36995
|
+
}
|
|
36996
|
+
function binaryPredecessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
36997
|
+
let end = arr.length - 1;
|
|
36998
|
+
let result = -1;
|
|
36999
|
+
while (start <= end) {
|
|
37000
|
+
const mid = Math.floor((start + end) / 2);
|
|
37001
|
+
if (arr[mid] === val && matchEqual) {
|
|
37002
|
+
return mid;
|
|
37003
|
+
}
|
|
37004
|
+
else if (arr[mid] < val) {
|
|
37005
|
+
result = mid;
|
|
37006
|
+
start = mid + 1;
|
|
37007
|
+
}
|
|
37008
|
+
else {
|
|
37009
|
+
end = mid - 1;
|
|
37010
|
+
}
|
|
37011
|
+
}
|
|
37012
|
+
return result;
|
|
37013
|
+
}
|
|
37014
|
+
function binarySuccessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
37015
|
+
let end = arr.length - 1;
|
|
37016
|
+
let result = arr.length;
|
|
37017
|
+
while (start <= end) {
|
|
37018
|
+
const mid = Math.floor((start + end) / 2);
|
|
37019
|
+
if (arr[mid] === val && matchEqual) {
|
|
37020
|
+
return mid;
|
|
37021
|
+
}
|
|
37022
|
+
else if (arr[mid] > val) {
|
|
37023
|
+
result = mid;
|
|
37024
|
+
end = mid - 1;
|
|
37025
|
+
}
|
|
37026
|
+
else {
|
|
37027
|
+
start = mid + 1;
|
|
37028
|
+
}
|
|
37029
|
+
}
|
|
37030
|
+
return result;
|
|
37031
|
+
}
|
|
37032
|
+
/**
|
|
37033
|
+
* Compares two arrays.
|
|
37034
|
+
* For performance reasons, this function is to be preferred
|
|
37035
|
+
* to 'deepEquals' in the case we know that the inputs are arrays.
|
|
37036
|
+
*/
|
|
37037
|
+
function deepEqualsArray(arr1, arr2) {
|
|
37038
|
+
if (arr1.length !== arr2.length) {
|
|
37039
|
+
return false;
|
|
37040
|
+
}
|
|
37041
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
37042
|
+
if (!deepEquals(arr1[i], arr2[i])) {
|
|
37043
|
+
return false;
|
|
37044
|
+
}
|
|
37045
|
+
}
|
|
37046
|
+
return true;
|
|
37047
|
+
}
|
|
37048
|
+
|
|
36676
37049
|
// -----------------------------------------------------------------------------
|
|
36677
37050
|
// Constants
|
|
36678
37051
|
// -----------------------------------------------------------------------------
|
|
@@ -36842,10 +37215,12 @@ class ConditionalFormatPlugin extends CorePlugin {
|
|
|
36842
37215
|
const replaceIndex = rules.findIndex((c) => c.id === cf.id);
|
|
36843
37216
|
let currentRanges = [];
|
|
36844
37217
|
if (replaceIndex > -1) {
|
|
36845
|
-
currentRanges = rules[replaceIndex].ranges;
|
|
37218
|
+
currentRanges = rules[replaceIndex].ranges.map(toUnboundedZone);
|
|
36846
37219
|
}
|
|
36847
37220
|
currentRanges = currentRanges.concat(toAdd);
|
|
36848
|
-
|
|
37221
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
37222
|
+
const withRemovedZones = futureRecomputeZones(currentRanges, toRemove);
|
|
37223
|
+
return futureRecomputeZones([...toAdd, ...withRemovedZones], []);
|
|
36849
37224
|
}
|
|
36850
37225
|
// ---------------------------------------------------------------------------
|
|
36851
37226
|
// Private
|
|
@@ -40580,353 +40955,6 @@ class CompilationParametersBuilder {
|
|
|
40580
40955
|
}
|
|
40581
40956
|
}
|
|
40582
40957
|
|
|
40583
|
-
/**
|
|
40584
|
-
* ####################################################
|
|
40585
|
-
* # INTRODUCTION
|
|
40586
|
-
* ####################################################
|
|
40587
|
-
*
|
|
40588
|
-
* This file contain the function recomputeZones.
|
|
40589
|
-
* This function try to recompute in a performant way
|
|
40590
|
-
* an ensemble of zones possibly overlapping to avoid
|
|
40591
|
-
* overlapping and to reduce the number of zones.
|
|
40592
|
-
*
|
|
40593
|
-
* It also allows to remove some zones from the ensemble.
|
|
40594
|
-
*
|
|
40595
|
-
* In the following example, 2 zones are overlapping.
|
|
40596
|
-
* Applying recomputeZones will return zones without
|
|
40597
|
-
* overlapping:
|
|
40598
|
-
*
|
|
40599
|
-
* ["B3:D4", "D2:E3"] ["B3:C4", "D2:D4", "E2:E3"]
|
|
40600
|
-
*
|
|
40601
|
-
* A B C D E A B C D E
|
|
40602
|
-
* 1 ___ 1 ___
|
|
40603
|
-
* 2 ___|_ | 2 ___| | |
|
|
40604
|
-
* 3 | |_|_| ---> 3 | | |_|
|
|
40605
|
-
* 4 |_____| 4 |___|_|
|
|
40606
|
-
* 6 6
|
|
40607
|
-
* 7 7
|
|
40608
|
-
*
|
|
40609
|
-
*
|
|
40610
|
-
* In the following example, 2 zones are contiguous.
|
|
40611
|
-
* Applying recomputeZones will return only one zone:
|
|
40612
|
-
*
|
|
40613
|
-
* ["B2:B3", "C2:D3"] ["B2:D3"]
|
|
40614
|
-
*
|
|
40615
|
-
* A B C D E A B C D E
|
|
40616
|
-
* 1 _ ___ 1 _____
|
|
40617
|
-
* 2 | | | ---> 2 | |
|
|
40618
|
-
* 3 |_|___| 3 |_____|
|
|
40619
|
-
* 4 4
|
|
40620
|
-
*
|
|
40621
|
-
*
|
|
40622
|
-
* In the following example, we want to remove a zone
|
|
40623
|
-
* from the ensemble. Applying recomputeZones will
|
|
40624
|
-
* return the ensemble without the zone to remove:
|
|
40625
|
-
*
|
|
40626
|
-
* remove ["C3:D3"] ["B2:B4", "C2:D2",
|
|
40627
|
-
* "C4:D4", "E2:E4"]
|
|
40628
|
-
*
|
|
40629
|
-
* A B C D E F A B C D E F
|
|
40630
|
-
* 1 _______ 1 _______
|
|
40631
|
-
* 2 | | ---> 2 | |___| |
|
|
40632
|
-
* 3 | xxx | 3 | |___| |
|
|
40633
|
-
* 4 |_______| 4 |_|___|_|
|
|
40634
|
-
* 5 5
|
|
40635
|
-
*
|
|
40636
|
-
*
|
|
40637
|
-
* The exercise seems simple when we have only 2 zones.
|
|
40638
|
-
* But with n zones and in a performant way, we want to
|
|
40639
|
-
* avoid comparing each zone with all the others.
|
|
40640
|
-
*
|
|
40641
|
-
*
|
|
40642
|
-
* ####################################################
|
|
40643
|
-
* # Methodological approach
|
|
40644
|
-
* ####################################################
|
|
40645
|
-
*
|
|
40646
|
-
* The methodological approach to avoid comparing each
|
|
40647
|
-
* zone with all the others is to use a data structure
|
|
40648
|
-
* that allow to quickly find which zones are
|
|
40649
|
-
* overlapping with any other given zone.
|
|
40650
|
-
*
|
|
40651
|
-
* Here the idea is to profile the zones at the columns level.
|
|
40652
|
-
*
|
|
40653
|
-
* To do that, we propose to use a data structure
|
|
40654
|
-
* composed of 2 parts:
|
|
40655
|
-
* - profilesStartingPosition: a sorted number array
|
|
40656
|
-
* indicating on which columns a new profile begins.
|
|
40657
|
-
* - profiles: a map where the key is a column
|
|
40658
|
-
* position (from profilesStartingPosition) and the
|
|
40659
|
-
* value is a sorted number array representing a
|
|
40660
|
-
* profile.
|
|
40661
|
-
*
|
|
40662
|
-
*
|
|
40663
|
-
* See the following example: here profileStartingPosition
|
|
40664
|
-
* corresponds to [A,C,E,G,K]
|
|
40665
|
-
* A B C D E F G H I J K so with number [0,2,4,6,10]
|
|
40666
|
-
* 1 ' ' ' '
|
|
40667
|
-
* 2 ' ' '_______' here profile correspond
|
|
40668
|
-
* 3 '___' |_______| for A to []
|
|
40669
|
-
* 4 | | for C to [3, 5]
|
|
40670
|
-
* 5 |___| for E to []
|
|
40671
|
-
* 6 for G to [2, 3]
|
|
40672
|
-
* 7 for K to []
|
|
40673
|
-
*
|
|
40674
|
-
*
|
|
40675
|
-
* Now we can easily find which zones are overlapping
|
|
40676
|
-
* with a given zone. Suppose we want to add a new zone
|
|
40677
|
-
* D5:H6 to the ensemble:
|
|
40678
|
-
*
|
|
40679
|
-
* With a binary search of left and right
|
|
40680
|
-
* A B C D E F G H I J K on profilesStartingPosition, we can
|
|
40681
|
-
* 1 ' ' ' ' find the indexes of the profiles on which
|
|
40682
|
-
* 2 ' ' '_______' to apply a modification.
|
|
40683
|
-
* 3 '___' |_______|
|
|
40684
|
-
* 4 | _|_______ Here we will:
|
|
40685
|
-
* 5 |_|_| | - add a new profile in D --> become [3, 6]
|
|
40686
|
-
* 6 |_________| - modify the profile in E --> become [4, 6]
|
|
40687
|
-
* 7 - modify the profile in G --> become [2, 3, 4, 6]
|
|
40688
|
-
* - add a new profile in I --> become [8, 10]
|
|
40689
|
-
*
|
|
40690
|
-
* See below the result:
|
|
40691
|
-
*
|
|
40692
|
-
* Note the particularity of the profile
|
|
40693
|
-
* A B C D E F G H I J K for G: it will correspond to [2, 3, 4, 6]
|
|
40694
|
-
* 1 ' ' ' ' ' '
|
|
40695
|
-
* 2 ' ' ' '___'___' To know how to modify the profile (add a
|
|
40696
|
-
* 3 '_'_' |___|___| zone or remove it) we do a binary
|
|
40697
|
-
* 4 | | |___ ___ search of the top and bottom value on the
|
|
40698
|
-
* 5 |_| | | | profile array. Depending on the result index
|
|
40699
|
-
* 6 |_|___|___| parity (odd or even), because zone boundaries
|
|
40700
|
-
* 7 go by pairs, we know if we are in a zone or
|
|
40701
|
-
* not and how operate.
|
|
40702
|
-
*/
|
|
40703
|
-
/**
|
|
40704
|
-
* Recompute the zone without the cells in toRemoveZones and avoid overlapping.
|
|
40705
|
-
* This compute is particularly useful because after this function:
|
|
40706
|
-
* - you will find coordinate of a cell only once among all the zones
|
|
40707
|
-
* - the number of zones will be reduced to the minimum
|
|
40708
|
-
*/
|
|
40709
|
-
function futureRecomputeZones(zones, zonesToRemove = []) {
|
|
40710
|
-
const profilesStartingPosition = [0];
|
|
40711
|
-
const profiles = new Map([[0, []]]);
|
|
40712
|
-
modifyProfiles(profilesStartingPosition, profiles, zones, false);
|
|
40713
|
-
modifyProfiles(profilesStartingPosition, profiles, zonesToRemove, true);
|
|
40714
|
-
return constructZonesFromProfiles(profilesStartingPosition, profiles);
|
|
40715
|
-
}
|
|
40716
|
-
function modifyProfiles(// export for testing only
|
|
40717
|
-
profilesStartingPosition, profiles, zones, toRemove = false) {
|
|
40718
|
-
for (const zone of zones) {
|
|
40719
|
-
const leftValue = zone.left;
|
|
40720
|
-
const rightValue = zone.right === undefined ? undefined : zone.right + 1;
|
|
40721
|
-
const leftIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, leftValue, true, 0);
|
|
40722
|
-
const rightIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, rightValue, false, leftIndex);
|
|
40723
|
-
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
40724
|
-
const profile = profiles.get(profilesStartingPosition[i]);
|
|
40725
|
-
modifyProfile(profile, zone, toRemove);
|
|
40726
|
-
}
|
|
40727
|
-
// maybe this part cost in performance, and maybe it's not necessary (depending on the use case). To be checked
|
|
40728
|
-
removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
|
|
40729
|
-
}
|
|
40730
|
-
}
|
|
40731
|
-
function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
|
|
40732
|
-
if (value === undefined) {
|
|
40733
|
-
// this is only the case when the value correspond to a bottom value that could be undefined
|
|
40734
|
-
return profilesStartingPosition.length - 1;
|
|
40735
|
-
}
|
|
40736
|
-
const predecessorIndex = binaryPredecessorSearch(profilesStartingPosition, value, startIndex);
|
|
40737
|
-
if (value != profilesStartingPosition[predecessorIndex]) {
|
|
40738
|
-
// mean that the value is not ending/starting at the same position as the previous/next profile
|
|
40739
|
-
// --> it's a new profile
|
|
40740
|
-
// --> we need to add it
|
|
40741
|
-
profilesStartingPosition.splice(predecessorIndex + 1, 0, value);
|
|
40742
|
-
// suppose the we want to add the for the left value
|
|
40743
|
-
// following profile following zone: 'C', the predecessor index
|
|
40744
|
-
// for B: [1, 3] "C3:D4" correspond to 'B'.
|
|
40745
|
-
// The next line code will
|
|
40746
|
-
// A B C D A B C D copy the profile of 'B'
|
|
40747
|
-
// 1 '___' 1 '___' to 'C'. In the rest of the
|
|
40748
|
-
// 2 | | ---> 2 | _|_ process the 'modifyProfile'
|
|
40749
|
-
// 3 |___| 3 |_|_| | function will adapt the waiting
|
|
40750
|
-
// 4 4 |___| 'C' profile [1, 3] to the
|
|
40751
|
-
// correct 'C' profile [1, 4]
|
|
40752
|
-
profiles.set(value, [...profiles.get(profilesStartingPosition[predecessorIndex])]);
|
|
40753
|
-
return searchLeft ? predecessorIndex + 1 : predecessorIndex;
|
|
40754
|
-
}
|
|
40755
|
-
return searchLeft ? predecessorIndex : predecessorIndex - 1;
|
|
40756
|
-
}
|
|
40757
|
-
/**
|
|
40758
|
-
* Suppose the following Suppose we want to add We want to have the
|
|
40759
|
-
* profile: the following zone: following profile:
|
|
40760
|
-
*
|
|
40761
|
-
* A B C D E F A B C D E F A B C D E F
|
|
40762
|
-
* 1 '___' 1 ' ' 1 '___'
|
|
40763
|
-
* 2 |___| 2 '___' 2 | |
|
|
40764
|
-
* 3 ' ' 3 | | 3 | |
|
|
40765
|
-
* 4 '___' --> 4 | | --> 4 | |
|
|
40766
|
-
* 6 | | 6 |___| 6 | |
|
|
40767
|
-
* 7 |___| 7 7 |___|
|
|
40768
|
-
* 8 8 8
|
|
40769
|
-
*
|
|
40770
|
-
* the profile for 'C' the top zone correspond Here [2, 3, 5, 8] with [3, 7]
|
|
40771
|
-
* corresponds to: to 3 and the bottom zone would be merged into [2, 8]
|
|
40772
|
-
* ____ ____ correspond to 6
|
|
40773
|
-
* [2, 3, 5, 8] would be the profile: The difficulty of modify profile
|
|
40774
|
-
* ____ is to know what must be deleted
|
|
40775
|
-
* Note that the 'filled [3, 7] and what must be added to the
|
|
40776
|
-
* zone' are always between existing profile.
|
|
40777
|
-
* an even index and its
|
|
40778
|
-
* next index
|
|
40779
|
-
*
|
|
40780
|
-
*/
|
|
40781
|
-
function modifyProfile(profile, zone, toRemove = false) {
|
|
40782
|
-
const topValue = zone.top;
|
|
40783
|
-
const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
|
|
40784
|
-
const newPoints = [];
|
|
40785
|
-
// Case we want to add a zone to the profile:
|
|
40786
|
-
// - If the top predecessor index `topPredIndex` is even, it means the top of the zone is already positioned on a filled zone
|
|
40787
|
-
// so we don't need to add it to the profile. we can keep in reference the index of the predecessor.
|
|
40788
|
-
// - If it is odd, it means the top of the zone must be the beginning of a filled zone.
|
|
40789
|
-
// so we can keep the index of the top position
|
|
40790
|
-
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
40791
|
-
const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, false);
|
|
40792
|
-
if ((topPredIndex % 2 !== 0 && !toRemove) || (topPredIndex % 2 === 0 && toRemove)) {
|
|
40793
|
-
newPoints.push(topValue);
|
|
40794
|
-
}
|
|
40795
|
-
if (bottomValue === undefined) {
|
|
40796
|
-
// The following two code lines will not impact the final result,
|
|
40797
|
-
// but they will impact the intermediate profile.
|
|
40798
|
-
// We keep them for performance reason
|
|
40799
|
-
profile.splice(topPredIndex + 1);
|
|
40800
|
-
profile.push(...newPoints);
|
|
40801
|
-
return;
|
|
40802
|
-
}
|
|
40803
|
-
// Case we want to add a zone to the profile:
|
|
40804
|
-
// - If the bottom successor index `bottomSuccIndex` is even, it means the bottom of the zone must be the ending of a filled zone
|
|
40805
|
-
// so we can keep the index of the bottom position.
|
|
40806
|
-
// - If it is odd, it means the bottom of the zone is already positioned on a filled zone
|
|
40807
|
-
// so we don't need to add it to the profile. we can keep in reference the index of the successor
|
|
40808
|
-
// Case we want to remove a zone from the profile: it's the opposite of the previous case
|
|
40809
|
-
const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, false);
|
|
40810
|
-
if ((bottomSuccIndex % 2 === 0 && !toRemove) || (bottomSuccIndex % 2 !== 0 && toRemove)) {
|
|
40811
|
-
newPoints.push(bottomValue);
|
|
40812
|
-
}
|
|
40813
|
-
// add the top and bottom value to the profile and
|
|
40814
|
-
// remove all information between the top and bottom index
|
|
40815
|
-
profile.splice(topPredIndex + 1, bottomSuccIndex - topPredIndex - 1, ...newPoints);
|
|
40816
|
-
}
|
|
40817
|
-
function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
|
|
40818
|
-
const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
|
|
40819
|
-
const end = rightIndex === profilesStartingPosition.length - 1 ? rightIndex : rightIndex + 1;
|
|
40820
|
-
for (let i = end; i > start; i--) {
|
|
40821
|
-
if (deepEqualsArray(profiles.get(profilesStartingPosition[i]), profiles.get(profilesStartingPosition[i - 1]))) {
|
|
40822
|
-
profiles.delete(profilesStartingPosition[i]);
|
|
40823
|
-
profilesStartingPosition.splice(i, 1);
|
|
40824
|
-
}
|
|
40825
|
-
}
|
|
40826
|
-
}
|
|
40827
|
-
function constructZonesFromProfiles(profilesStartingPosition, profiles) {
|
|
40828
|
-
const mergedZone = [];
|
|
40829
|
-
let pendingZones = [];
|
|
40830
|
-
for (let colIndex = 0; colIndex < profilesStartingPosition.length; colIndex++) {
|
|
40831
|
-
const left = profilesStartingPosition[colIndex];
|
|
40832
|
-
const profile = profiles.get(left);
|
|
40833
|
-
if (!profile || profile.length === 0) {
|
|
40834
|
-
mergedZone.push(...pendingZones);
|
|
40835
|
-
pendingZones = [];
|
|
40836
|
-
continue;
|
|
40837
|
-
}
|
|
40838
|
-
let right = profilesStartingPosition[colIndex + 1];
|
|
40839
|
-
if (right !== undefined) {
|
|
40840
|
-
right--;
|
|
40841
|
-
}
|
|
40842
|
-
const nextPendingZones = [];
|
|
40843
|
-
for (let i = 0; i < profile.length; i += 2) {
|
|
40844
|
-
const top = profile[i];
|
|
40845
|
-
let bottom = profile[i + 1];
|
|
40846
|
-
if (bottom !== undefined) {
|
|
40847
|
-
bottom--;
|
|
40848
|
-
}
|
|
40849
|
-
const profileZone = {
|
|
40850
|
-
top,
|
|
40851
|
-
left,
|
|
40852
|
-
bottom,
|
|
40853
|
-
right,
|
|
40854
|
-
hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
|
|
40855
|
-
};
|
|
40856
|
-
let findCorrespondingZone = false;
|
|
40857
|
-
for (let j = pendingZones.length - 1; j >= 0; j--) {
|
|
40858
|
-
const pendingZone = pendingZones[j];
|
|
40859
|
-
if (pendingZone.top === profileZone.top && pendingZone.bottom === profileZone.bottom) {
|
|
40860
|
-
pendingZone.right = profileZone.right;
|
|
40861
|
-
pendingZones.splice(j, 1);
|
|
40862
|
-
nextPendingZones.push(pendingZone);
|
|
40863
|
-
findCorrespondingZone = true;
|
|
40864
|
-
break;
|
|
40865
|
-
}
|
|
40866
|
-
}
|
|
40867
|
-
if (!findCorrespondingZone) {
|
|
40868
|
-
nextPendingZones.push(profileZone);
|
|
40869
|
-
}
|
|
40870
|
-
}
|
|
40871
|
-
mergedZone.push(...pendingZones);
|
|
40872
|
-
pendingZones = nextPendingZones;
|
|
40873
|
-
}
|
|
40874
|
-
mergedZone.push(...pendingZones);
|
|
40875
|
-
return mergedZone;
|
|
40876
|
-
}
|
|
40877
|
-
function binaryPredecessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
40878
|
-
let end = arr.length - 1;
|
|
40879
|
-
let result = -1;
|
|
40880
|
-
while (start <= end) {
|
|
40881
|
-
const mid = Math.floor((start + end) / 2);
|
|
40882
|
-
if (arr[mid] === val && matchEqual) {
|
|
40883
|
-
return mid;
|
|
40884
|
-
}
|
|
40885
|
-
else if (arr[mid] < val) {
|
|
40886
|
-
result = mid;
|
|
40887
|
-
start = mid + 1;
|
|
40888
|
-
}
|
|
40889
|
-
else {
|
|
40890
|
-
end = mid - 1;
|
|
40891
|
-
}
|
|
40892
|
-
}
|
|
40893
|
-
return result;
|
|
40894
|
-
}
|
|
40895
|
-
function binarySuccessorSearch(arr, val, start = 0, matchEqual = true) {
|
|
40896
|
-
let end = arr.length - 1;
|
|
40897
|
-
let result = arr.length;
|
|
40898
|
-
while (start <= end) {
|
|
40899
|
-
const mid = Math.floor((start + end) / 2);
|
|
40900
|
-
if (arr[mid] === val && matchEqual) {
|
|
40901
|
-
return mid;
|
|
40902
|
-
}
|
|
40903
|
-
else if (arr[mid] > val) {
|
|
40904
|
-
result = mid;
|
|
40905
|
-
end = mid - 1;
|
|
40906
|
-
}
|
|
40907
|
-
else {
|
|
40908
|
-
start = mid + 1;
|
|
40909
|
-
}
|
|
40910
|
-
}
|
|
40911
|
-
return result;
|
|
40912
|
-
}
|
|
40913
|
-
/**
|
|
40914
|
-
* Compares two arrays.
|
|
40915
|
-
* For performance reasons, this function is to be preferred
|
|
40916
|
-
* to 'deepEquals' in the case we know that the inputs are arrays.
|
|
40917
|
-
*/
|
|
40918
|
-
function deepEqualsArray(arr1, arr2) {
|
|
40919
|
-
if (arr1.length !== arr2.length) {
|
|
40920
|
-
return false;
|
|
40921
|
-
}
|
|
40922
|
-
for (let i = 0; i < arr1.length; i++) {
|
|
40923
|
-
if (!deepEquals(arr1[i], arr2[i])) {
|
|
40924
|
-
return false;
|
|
40925
|
-
}
|
|
40926
|
-
}
|
|
40927
|
-
return true;
|
|
40928
|
-
}
|
|
40929
|
-
|
|
40930
40958
|
function quickselect(arr, k, left, right, compare) {
|
|
40931
40959
|
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
|
|
40932
40960
|
}
|
|
@@ -43523,25 +43551,6 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43523
43551
|
case "AUTOFILL_AUTO":
|
|
43524
43552
|
this.autofillAuto();
|
|
43525
43553
|
break;
|
|
43526
|
-
case "AUTOFILL_CELL":
|
|
43527
|
-
this.autoFillMerge(cmd.originCol, cmd.originRow, cmd.col, cmd.row);
|
|
43528
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
43529
|
-
this.dispatch("UPDATE_CELL", {
|
|
43530
|
-
sheetId,
|
|
43531
|
-
col: cmd.col,
|
|
43532
|
-
row: cmd.row,
|
|
43533
|
-
style: cmd.style || null,
|
|
43534
|
-
content: cmd.content || "",
|
|
43535
|
-
format: cmd.format || "",
|
|
43536
|
-
});
|
|
43537
|
-
this.dispatch("SET_BORDER", {
|
|
43538
|
-
sheetId,
|
|
43539
|
-
col: cmd.col,
|
|
43540
|
-
row: cmd.row,
|
|
43541
|
-
border: cmd.border,
|
|
43542
|
-
});
|
|
43543
|
-
this.autofillCF(cmd.originCol, cmd.originRow, cmd.col, cmd.row);
|
|
43544
|
-
this.autofillDV(cmd.originCol, cmd.originRow, cmd.col, cmd.row);
|
|
43545
43554
|
}
|
|
43546
43555
|
}
|
|
43547
43556
|
// ---------------------------------------------------------------------------
|
|
@@ -43565,6 +43574,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43565
43574
|
}
|
|
43566
43575
|
const source = this.getters.getSelectedZone();
|
|
43567
43576
|
const target = this.autofillZone;
|
|
43577
|
+
const autofillCellsData = [];
|
|
43568
43578
|
switch (this.direction) {
|
|
43569
43579
|
case "down" /* DIRECTION.DOWN */:
|
|
43570
43580
|
for (let col = source.left; col <= source.right; col++) {
|
|
@@ -43574,7 +43584,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43574
43584
|
}
|
|
43575
43585
|
const generator = this.createGenerator(xcs);
|
|
43576
43586
|
for (let row = target.top; row <= target.bottom; row++) {
|
|
43577
|
-
this.computeNewCell(generator, col, row
|
|
43587
|
+
autofillCellsData.push(this.computeNewCell(generator, col, row));
|
|
43578
43588
|
}
|
|
43579
43589
|
}
|
|
43580
43590
|
break;
|
|
@@ -43586,7 +43596,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43586
43596
|
}
|
|
43587
43597
|
const generator = this.createGenerator(xcs);
|
|
43588
43598
|
for (let row = target.bottom; row >= target.top; row--) {
|
|
43589
|
-
this.computeNewCell(generator, col, row
|
|
43599
|
+
autofillCellsData.push(this.computeNewCell(generator, col, row));
|
|
43590
43600
|
}
|
|
43591
43601
|
}
|
|
43592
43602
|
break;
|
|
@@ -43598,7 +43608,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43598
43608
|
}
|
|
43599
43609
|
const generator = this.createGenerator(xcs);
|
|
43600
43610
|
for (let col = target.right; col >= target.left; col--) {
|
|
43601
|
-
this.computeNewCell(generator, col, row
|
|
43611
|
+
autofillCellsData.push(this.computeNewCell(generator, col, row));
|
|
43602
43612
|
}
|
|
43603
43613
|
}
|
|
43604
43614
|
break;
|
|
@@ -43610,12 +43620,26 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43610
43620
|
}
|
|
43611
43621
|
const generator = this.createGenerator(xcs);
|
|
43612
43622
|
for (let col = target.left; col <= target.right; col++) {
|
|
43613
|
-
this.computeNewCell(generator, col, row
|
|
43623
|
+
autofillCellsData.push(this.computeNewCell(generator, col, row));
|
|
43614
43624
|
}
|
|
43615
43625
|
}
|
|
43616
43626
|
break;
|
|
43617
43627
|
}
|
|
43618
43628
|
if (apply) {
|
|
43629
|
+
const bordersZones = {};
|
|
43630
|
+
const cfNewRanges = {};
|
|
43631
|
+
const dvNewZones = {};
|
|
43632
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
43633
|
+
for (const data of autofillCellsData) {
|
|
43634
|
+
this.collectBordersData(data, bordersZones);
|
|
43635
|
+
this.autofillMerge(sheetId, data);
|
|
43636
|
+
this.autofillCell(sheetId, data);
|
|
43637
|
+
this.collectConditionalFormatsData(sheetId, data, cfNewRanges);
|
|
43638
|
+
this.collectDataValidationsData(sheetId, data, dvNewZones);
|
|
43639
|
+
}
|
|
43640
|
+
this.autofillBorders(sheetId, bordersZones);
|
|
43641
|
+
this.autofillConditionalFormats(sheetId, cfNewRanges);
|
|
43642
|
+
this.autofillDataValidations(sheetId, dvNewZones);
|
|
43619
43643
|
this.autofillZone = undefined;
|
|
43620
43644
|
this.selection.resizeAnchorZone(this.direction, this.steps);
|
|
43621
43645
|
this.lastCellSelected = {};
|
|
@@ -43624,6 +43648,95 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43624
43648
|
this.tooltip = undefined;
|
|
43625
43649
|
}
|
|
43626
43650
|
}
|
|
43651
|
+
collectBordersData(data, bordersPositions) {
|
|
43652
|
+
const key = JSON.stringify(data.border);
|
|
43653
|
+
if (!(key in bordersPositions)) {
|
|
43654
|
+
bordersPositions[key] = [];
|
|
43655
|
+
}
|
|
43656
|
+
bordersPositions[key].push(positionToZone({ col: data.col, row: data.row }));
|
|
43657
|
+
}
|
|
43658
|
+
collectConditionalFormatsData(sheetId, data, cfNewRanges) {
|
|
43659
|
+
const { originCol, originRow, col, row } = data;
|
|
43660
|
+
const cfsAtOrigin = this.getters.getRulesByCell(sheetId, originCol, originRow);
|
|
43661
|
+
const xc = toXC(col, row);
|
|
43662
|
+
for (const cf of cfsAtOrigin) {
|
|
43663
|
+
if (!(cf.id in cfNewRanges)) {
|
|
43664
|
+
cfNewRanges[cf.id] = [];
|
|
43665
|
+
}
|
|
43666
|
+
cfNewRanges[cf.id].push(xc);
|
|
43667
|
+
}
|
|
43668
|
+
}
|
|
43669
|
+
collectDataValidationsData(sheetId, data, dvNewZones) {
|
|
43670
|
+
const { originCol, originRow, col, row } = data;
|
|
43671
|
+
const cellPosition = { sheetId, col: originCol, row: originRow };
|
|
43672
|
+
const dvsAtOrigin = this.getters.getValidationRuleForCell(cellPosition);
|
|
43673
|
+
if (!dvsAtOrigin) {
|
|
43674
|
+
return;
|
|
43675
|
+
}
|
|
43676
|
+
if (!(dvsAtOrigin.id in dvNewZones)) {
|
|
43677
|
+
dvNewZones[dvsAtOrigin.id] = [];
|
|
43678
|
+
}
|
|
43679
|
+
dvNewZones[dvsAtOrigin.id].push(positionToZone({ col, row }));
|
|
43680
|
+
}
|
|
43681
|
+
autofillCell(sheetId, data) {
|
|
43682
|
+
this.dispatch("UPDATE_CELL", {
|
|
43683
|
+
sheetId,
|
|
43684
|
+
col: data.col,
|
|
43685
|
+
row: data.row,
|
|
43686
|
+
content: data.content || "",
|
|
43687
|
+
style: data.style || null,
|
|
43688
|
+
format: data.format || "",
|
|
43689
|
+
});
|
|
43690
|
+
// Still usefull in odoo ATM to autofill field sync
|
|
43691
|
+
this.dispatch("AUTOFILL_CELL", data);
|
|
43692
|
+
}
|
|
43693
|
+
autofillBorders(sheetId, bordersPositions) {
|
|
43694
|
+
for (const stringifiedBorder in bordersPositions) {
|
|
43695
|
+
const border = stringifiedBorder === "undefined" ? undefined : JSON.parse(stringifiedBorder);
|
|
43696
|
+
this.dispatch("SET_BORDERS_ON_TARGET", {
|
|
43697
|
+
sheetId,
|
|
43698
|
+
border,
|
|
43699
|
+
target: futureRecomputeZones(bordersPositions[stringifiedBorder]),
|
|
43700
|
+
});
|
|
43701
|
+
}
|
|
43702
|
+
}
|
|
43703
|
+
autofillConditionalFormats(sheetId, cfNewRanges) {
|
|
43704
|
+
for (const cfId in cfNewRanges) {
|
|
43705
|
+
const changes = cfNewRanges[cfId];
|
|
43706
|
+
const cf = this.getters.getConditionalFormats(sheetId).find((cf) => cf.id === cfId);
|
|
43707
|
+
if (!cf) {
|
|
43708
|
+
continue;
|
|
43709
|
+
}
|
|
43710
|
+
const newCfZones = this.getters.getAdaptedCfRanges(sheetId, cf, changes.map(toZone), []);
|
|
43711
|
+
if (newCfZones) {
|
|
43712
|
+
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
43713
|
+
cf: {
|
|
43714
|
+
id: cf.id,
|
|
43715
|
+
rule: cf.rule,
|
|
43716
|
+
stopIfTrue: cf.stopIfTrue,
|
|
43717
|
+
},
|
|
43718
|
+
ranges: newCfZones.map((xc) => this.getters.getRangeDataFromZone(sheetId, xc)),
|
|
43719
|
+
sheetId,
|
|
43720
|
+
});
|
|
43721
|
+
}
|
|
43722
|
+
}
|
|
43723
|
+
}
|
|
43724
|
+
autofillDataValidations(sheetId, dvNewZones) {
|
|
43725
|
+
for (const dvId in dvNewZones) {
|
|
43726
|
+
const changes = dvNewZones[dvId];
|
|
43727
|
+
const dvOrigin = this.getters.getDataValidationRule(sheetId, dvId);
|
|
43728
|
+
if (!dvOrigin) {
|
|
43729
|
+
continue;
|
|
43730
|
+
}
|
|
43731
|
+
const dvRangesXcs = dvOrigin.ranges.map((range) => range.zone);
|
|
43732
|
+
const newDvRanges = futureRecomputeZones(dvRangesXcs.concat(changes), []);
|
|
43733
|
+
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
43734
|
+
rule: dvOrigin,
|
|
43735
|
+
ranges: newDvRanges.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
43736
|
+
sheetId,
|
|
43737
|
+
});
|
|
43738
|
+
}
|
|
43739
|
+
}
|
|
43627
43740
|
/**
|
|
43628
43741
|
* Select a cell which becomes the last cell of the autofillZone
|
|
43629
43742
|
*/
|
|
@@ -43690,22 +43803,20 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43690
43803
|
/**
|
|
43691
43804
|
* Generate the next cell
|
|
43692
43805
|
*/
|
|
43693
|
-
computeNewCell(generator, col, row
|
|
43806
|
+
computeNewCell(generator, col, row) {
|
|
43694
43807
|
const { cellData, tooltip, origin } = generator.next();
|
|
43695
43808
|
const { content, style, border, format } = cellData;
|
|
43696
43809
|
this.tooltip = tooltip;
|
|
43697
|
-
|
|
43698
|
-
|
|
43699
|
-
|
|
43700
|
-
|
|
43701
|
-
|
|
43702
|
-
|
|
43703
|
-
|
|
43704
|
-
|
|
43705
|
-
|
|
43706
|
-
|
|
43707
|
-
});
|
|
43708
|
-
}
|
|
43810
|
+
return {
|
|
43811
|
+
originCol: origin.col,
|
|
43812
|
+
originRow: origin.row,
|
|
43813
|
+
col,
|
|
43814
|
+
row,
|
|
43815
|
+
content,
|
|
43816
|
+
style,
|
|
43817
|
+
border,
|
|
43818
|
+
format,
|
|
43819
|
+
};
|
|
43709
43820
|
}
|
|
43710
43821
|
/**
|
|
43711
43822
|
* Get the rule associated to the current cell
|
|
@@ -43773,8 +43884,8 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43773
43884
|
? position[first].value
|
|
43774
43885
|
: position[second].value;
|
|
43775
43886
|
}
|
|
43776
|
-
|
|
43777
|
-
const
|
|
43887
|
+
autofillMerge(sheetId, data) {
|
|
43888
|
+
const { originCol, originRow, col, row } = data;
|
|
43778
43889
|
const position = { sheetId, col, row };
|
|
43779
43890
|
const originPosition = { sheetId, col: originCol, row: originRow };
|
|
43780
43891
|
if (this.getters.isInMerge(position) && !this.getters.isInMerge(originPosition)) {
|
|
@@ -43801,35 +43912,6 @@ class AutofillPlugin extends UIPlugin {
|
|
|
43801
43912
|
});
|
|
43802
43913
|
}
|
|
43803
43914
|
}
|
|
43804
|
-
autofillCF(originCol, originRow, col, row) {
|
|
43805
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
43806
|
-
const cfOrigin = this.getters.getRulesByCell(sheetId, originCol, originRow);
|
|
43807
|
-
for (const cf of cfOrigin) {
|
|
43808
|
-
const newCfRanges = this.getters.getAdaptedCfRanges(sheetId, cf, [toXC(col, row)], []);
|
|
43809
|
-
if (newCfRanges) {
|
|
43810
|
-
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
43811
|
-
cf: deepCopy(cf),
|
|
43812
|
-
ranges: newCfRanges.map((xc) => this.getters.getRangeDataFromXc(sheetId, xc)),
|
|
43813
|
-
sheetId,
|
|
43814
|
-
});
|
|
43815
|
-
}
|
|
43816
|
-
}
|
|
43817
|
-
}
|
|
43818
|
-
autofillDV(originCol, originRow, col, row) {
|
|
43819
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
43820
|
-
const cellPosition = { sheetId, col: originCol, row: originRow };
|
|
43821
|
-
const dvOrigin = this.getters.getValidationRuleForCell(cellPosition);
|
|
43822
|
-
if (!dvOrigin) {
|
|
43823
|
-
return;
|
|
43824
|
-
}
|
|
43825
|
-
const dvRangesXcs = dvOrigin.ranges.map((range) => this.getters.getRangeString(range, sheetId));
|
|
43826
|
-
const newDvRanges = recomputeZones(dvRangesXcs.concat(toXC(col, row)), []);
|
|
43827
|
-
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
43828
|
-
rule: dvOrigin,
|
|
43829
|
-
ranges: newDvRanges.map((xc) => this.getters.getRangeDataFromXc(sheetId, xc)),
|
|
43830
|
-
sheetId,
|
|
43831
|
-
});
|
|
43832
|
-
}
|
|
43833
43915
|
// ---------------------------------------------------------------------------
|
|
43834
43916
|
// Grid rendering
|
|
43835
43917
|
// ---------------------------------------------------------------------------
|
|
@@ -45216,6 +45298,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45216
45298
|
copiedTables;
|
|
45217
45299
|
zones;
|
|
45218
45300
|
uuidGenerator = new UuidGenerator();
|
|
45301
|
+
queuedBordersToAdd = {};
|
|
45302
|
+
queuedCFChanges = {};
|
|
45303
|
+
queuedDVChanges = {};
|
|
45219
45304
|
constructor(zones, operation, getters, dispatch, selection) {
|
|
45220
45305
|
super(operation, getters, dispatch, selection);
|
|
45221
45306
|
if (!zones.length) {
|
|
@@ -45325,6 +45410,8 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45325
45410
|
* Paste the clipboard content in the given target
|
|
45326
45411
|
*/
|
|
45327
45412
|
paste(target, options) {
|
|
45413
|
+
this.queuedCFChanges = {};
|
|
45414
|
+
this.queuedDVChanges = {};
|
|
45328
45415
|
if (this.operation === "COPY") {
|
|
45329
45416
|
this.pasteFromCopy(target, options);
|
|
45330
45417
|
}
|
|
@@ -45337,6 +45424,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45337
45424
|
if (options?.selectTarget) {
|
|
45338
45425
|
this.selectPastedZone(width, height, isCutOperation, target);
|
|
45339
45426
|
}
|
|
45427
|
+
this.executeQueuedChanges(this.getters.getActiveSheetId());
|
|
45340
45428
|
}
|
|
45341
45429
|
pasteFromCopy(target, options) {
|
|
45342
45430
|
if (target.length === 1) {
|
|
@@ -45511,7 +45599,11 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45511
45599
|
left: targetBorders?.left || originBorders?.left,
|
|
45512
45600
|
right: targetBorders?.right || originBorders?.right,
|
|
45513
45601
|
};
|
|
45514
|
-
|
|
45602
|
+
const borderKey = JSON.stringify(border);
|
|
45603
|
+
if (!this.queuedBordersToAdd[borderKey]) {
|
|
45604
|
+
this.queuedBordersToAdd[borderKey] = [];
|
|
45605
|
+
}
|
|
45606
|
+
this.queuedBordersToAdd[borderKey].push(positionToZone(target));
|
|
45515
45607
|
if (clipboardOption?.pasteOption === "onlyFormat") {
|
|
45516
45608
|
this.dispatch("UPDATE_CELL", {
|
|
45517
45609
|
...target,
|
|
@@ -45657,7 +45749,8 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45657
45749
|
}
|
|
45658
45750
|
}
|
|
45659
45751
|
pasteCf(origin, target) {
|
|
45660
|
-
const
|
|
45752
|
+
const zone = positionToZone(target);
|
|
45753
|
+
const originZone = positionToZone(origin);
|
|
45661
45754
|
for (let rule of this.getters.getConditionalFormats(origin.sheetId)) {
|
|
45662
45755
|
for (let range of rule.ranges) {
|
|
45663
45756
|
if (isInside(origin.col, origin.row, this.getters.getRangeFromSheetXC(origin.sheetId, range).zone)) {
|
|
@@ -45665,15 +45758,15 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45665
45758
|
const toRemoveRange = [];
|
|
45666
45759
|
if (this.operation === "CUT") {
|
|
45667
45760
|
//remove from current rule
|
|
45668
|
-
toRemoveRange.push(
|
|
45761
|
+
toRemoveRange.push(originZone);
|
|
45669
45762
|
}
|
|
45670
45763
|
if (origin.sheetId === target.sheetId) {
|
|
45671
|
-
this.adaptCFRules(origin.sheetId, cf, [
|
|
45764
|
+
this.adaptCFRules(origin.sheetId, cf, [zone], toRemoveRange);
|
|
45672
45765
|
}
|
|
45673
45766
|
else {
|
|
45674
45767
|
this.adaptCFRules(origin.sheetId, cf, [], toRemoveRange);
|
|
45675
45768
|
const cfToCopyTo = this.getCFToCopyTo(target.sheetId, cf);
|
|
45676
|
-
this.adaptCFRules(target.sheetId, cfToCopyTo, [
|
|
45769
|
+
this.adaptCFRules(target.sheetId, cfToCopyTo, [zone], []);
|
|
45677
45770
|
}
|
|
45678
45771
|
}
|
|
45679
45772
|
}
|
|
@@ -45683,79 +45776,130 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
45683
45776
|
* Add or remove cells to a given conditional formatting rule.
|
|
45684
45777
|
*/
|
|
45685
45778
|
adaptCFRules(sheetId, cf, toAdd, toRemove) {
|
|
45686
|
-
|
|
45687
|
-
|
|
45688
|
-
return;
|
|
45779
|
+
if (!this.queuedCFChanges[sheetId]) {
|
|
45780
|
+
this.queuedCFChanges[sheetId] = [];
|
|
45689
45781
|
}
|
|
45690
|
-
|
|
45691
|
-
|
|
45692
|
-
|
|
45782
|
+
const queuedChange = this.queuedCFChanges[sheetId].find((queued) => queued.cf.id === cf.id);
|
|
45783
|
+
if (!queuedChange) {
|
|
45784
|
+
this.queuedCFChanges[sheetId].push({ toAdd, toRemove, cf });
|
|
45785
|
+
}
|
|
45786
|
+
else {
|
|
45787
|
+
queuedChange.toAdd.push(...toAdd);
|
|
45788
|
+
queuedChange.toRemove.push(...toRemove);
|
|
45693
45789
|
}
|
|
45694
|
-
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
45695
|
-
cf: {
|
|
45696
|
-
id: cf.id,
|
|
45697
|
-
rule: cf.rule,
|
|
45698
|
-
stopIfTrue: cf.stopIfTrue,
|
|
45699
|
-
},
|
|
45700
|
-
ranges: newRangesXC.map((xc) => this.getters.getRangeDataFromXc(sheetId, xc)),
|
|
45701
|
-
sheetId,
|
|
45702
|
-
});
|
|
45703
45790
|
}
|
|
45704
45791
|
getCFToCopyTo(targetSheetId, originCF) {
|
|
45705
|
-
|
|
45792
|
+
let targetCF = this.getters
|
|
45706
45793
|
.getConditionalFormats(targetSheetId)
|
|
45707
45794
|
.find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
|
|
45708
|
-
|
|
45709
|
-
|
|
45710
|
-
|
|
45795
|
+
const queuedCfs = this.queuedCFChanges[targetSheetId];
|
|
45796
|
+
if (!targetCF && queuedCfs) {
|
|
45797
|
+
targetCF = queuedCfs.find((queued) => queued.cf.stopIfTrue === originCF.stopIfTrue && deepEquals(queued.cf.rule, originCF.rule))?.cf;
|
|
45798
|
+
}
|
|
45799
|
+
return targetCF ? targetCF : { ...originCF, id: this.uuidGenerator.smallUuid(), ranges: [] };
|
|
45711
45800
|
}
|
|
45712
45801
|
pasteDataValidation(origin, target) {
|
|
45713
45802
|
const rule = this.getters.getValidationRuleForCell(origin);
|
|
45803
|
+
const originZone = positionToZone(origin);
|
|
45714
45804
|
if (!rule) {
|
|
45715
45805
|
return;
|
|
45716
45806
|
}
|
|
45717
|
-
const
|
|
45807
|
+
const zone = positionToZone(target);
|
|
45718
45808
|
for (const range of rule.ranges) {
|
|
45719
45809
|
if (isInside(origin.col, origin.row, range.zone)) {
|
|
45720
45810
|
const toRemoveRange = [];
|
|
45721
45811
|
if (this.operation === "CUT") {
|
|
45722
|
-
toRemoveRange.push(
|
|
45812
|
+
toRemoveRange.push(originZone);
|
|
45723
45813
|
}
|
|
45724
45814
|
if (origin.sheetId === target.sheetId) {
|
|
45725
|
-
this.
|
|
45815
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule);
|
|
45816
|
+
this.adaptDataValidationRule(origin.sheetId, copyToRule, [zone], toRemoveRange);
|
|
45726
45817
|
}
|
|
45727
45818
|
else {
|
|
45728
|
-
this.
|
|
45729
|
-
|
|
45730
|
-
|
|
45819
|
+
const originRule = this.getters.getValidationRuleForCell(origin);
|
|
45820
|
+
if (originRule) {
|
|
45821
|
+
this.adaptDataValidationRule(origin.sheetId, originRule, [], toRemoveRange);
|
|
45822
|
+
}
|
|
45823
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule);
|
|
45824
|
+
this.adaptDataValidationRule(target.sheetId, copyToRule, [zone], []);
|
|
45731
45825
|
}
|
|
45732
45826
|
}
|
|
45733
45827
|
}
|
|
45734
45828
|
}
|
|
45735
45829
|
getDataValidationRuleToCopyTo(targetSheetId, originRule) {
|
|
45736
|
-
|
|
45830
|
+
let targetRule = this.getters
|
|
45737
45831
|
.getDataValidationRules(targetSheetId)
|
|
45738
45832
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
45739
45833
|
originRule.isBlocking === rule.isBlocking);
|
|
45740
|
-
|
|
45741
|
-
|
|
45742
|
-
|
|
45834
|
+
const queuedRules = this.queuedDVChanges[targetSheetId];
|
|
45835
|
+
if (!targetRule && queuedRules) {
|
|
45836
|
+
targetRule = queuedRules.find((queued) => deepEquals(originRule.criterion, queued.rule.criterion) &&
|
|
45837
|
+
originRule.isBlocking === queued.rule.isBlocking)?.rule;
|
|
45838
|
+
}
|
|
45839
|
+
return targetRule || { ...originRule, id: this.uuidGenerator.smallUuid(), ranges: [] };
|
|
45743
45840
|
}
|
|
45744
45841
|
/**
|
|
45745
45842
|
* Add or remove XCs to a given data validation rule.
|
|
45746
45843
|
*/
|
|
45747
45844
|
adaptDataValidationRule(sheetId, rule, toAdd, toRemove) {
|
|
45748
|
-
|
|
45749
|
-
|
|
45750
|
-
if (newRangesXC.length === 0) {
|
|
45751
|
-
this.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: rule.id });
|
|
45752
|
-
return;
|
|
45845
|
+
if (!this.queuedDVChanges[sheetId]) {
|
|
45846
|
+
this.queuedDVChanges[sheetId] = [];
|
|
45753
45847
|
}
|
|
45754
|
-
this.
|
|
45755
|
-
|
|
45756
|
-
|
|
45757
|
-
|
|
45758
|
-
|
|
45848
|
+
const queuedChange = this.queuedDVChanges[sheetId].find((queued) => queued.rule.id === rule.id);
|
|
45849
|
+
if (!queuedChange) {
|
|
45850
|
+
this.queuedDVChanges[sheetId].push({ toAdd, toRemove, rule: deepCopy(rule) });
|
|
45851
|
+
}
|
|
45852
|
+
else {
|
|
45853
|
+
queuedChange.toAdd.push(...toAdd);
|
|
45854
|
+
queuedChange.toRemove.push(...toRemove);
|
|
45855
|
+
}
|
|
45856
|
+
}
|
|
45857
|
+
executeQueuedChanges(pasteSheetTarget) {
|
|
45858
|
+
for (const sheetId in this.queuedCFChanges) {
|
|
45859
|
+
for (const { toAdd, toRemove, cf } of this.queuedCFChanges[sheetId]) {
|
|
45860
|
+
const newRangesXc = this.getters.getAdaptedCfRanges(sheetId, cf, toAdd, toRemove);
|
|
45861
|
+
if (!newRangesXc) {
|
|
45862
|
+
continue;
|
|
45863
|
+
}
|
|
45864
|
+
if (newRangesXc.length === 0) {
|
|
45865
|
+
this.dispatch("REMOVE_CONDITIONAL_FORMAT", { id: cf.id, sheetId });
|
|
45866
|
+
continue;
|
|
45867
|
+
}
|
|
45868
|
+
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
45869
|
+
cf: {
|
|
45870
|
+
id: cf.id,
|
|
45871
|
+
rule: cf.rule,
|
|
45872
|
+
stopIfTrue: cf.stopIfTrue,
|
|
45873
|
+
},
|
|
45874
|
+
ranges: newRangesXc.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
45875
|
+
sheetId,
|
|
45876
|
+
});
|
|
45877
|
+
}
|
|
45878
|
+
}
|
|
45879
|
+
for (const sheetId in this.queuedDVChanges) {
|
|
45880
|
+
for (const { toAdd, toRemove, rule: dv } of this.queuedDVChanges[sheetId]) {
|
|
45881
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
45882
|
+
const dvZones = dv.ranges.map((range) => range.zone);
|
|
45883
|
+
const withRemovedZones = futureRecomputeZones(dvZones, toRemove);
|
|
45884
|
+
const newDvZones = futureRecomputeZones([...withRemovedZones, ...toAdd], []);
|
|
45885
|
+
if (newDvZones.length === 0) {
|
|
45886
|
+
this.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: dv.id });
|
|
45887
|
+
continue;
|
|
45888
|
+
}
|
|
45889
|
+
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
45890
|
+
rule: { id: dv.id, criterion: dv.criterion, isBlocking: dv.isBlocking },
|
|
45891
|
+
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
45892
|
+
sheetId,
|
|
45893
|
+
});
|
|
45894
|
+
}
|
|
45895
|
+
}
|
|
45896
|
+
for (const borderKey in this.queuedBordersToAdd) {
|
|
45897
|
+
const zones = this.queuedBordersToAdd[borderKey];
|
|
45898
|
+
const border = JSON.parse(borderKey);
|
|
45899
|
+
const target = futureRecomputeZones(zones, []);
|
|
45900
|
+
this.dispatch("SET_BORDERS_ON_TARGET", { sheetId: pasteSheetTarget, target, border });
|
|
45901
|
+
}
|
|
45902
|
+
this.queuedBordersToAdd = {};
|
|
45759
45903
|
}
|
|
45760
45904
|
}
|
|
45761
45905
|
|
|
@@ -58068,6 +58212,6 @@ const constants = {
|
|
|
58068
58212
|
export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
|
|
58069
58213
|
|
|
58070
58214
|
|
|
58071
|
-
__info__.version = "17.0.
|
|
58072
|
-
__info__.date = "2025-
|
|
58073
|
-
__info__.hash = "
|
|
58215
|
+
__info__.version = "17.0.57";
|
|
58216
|
+
__info__.date = "2025-04-14T17:13:56.677Z";
|
|
58217
|
+
__info__.hash = "6ac8bb7";
|