@odoo/o-spreadsheet 18.2.11 → 18.2.13
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 +610 -170
- package/dist/o-spreadsheet.d.ts +326 -200
- package/dist/o-spreadsheet.esm.js +610 -170
- package/dist/o-spreadsheet.iife.js +610 -170
- package/dist/o-spreadsheet.iife.min.js +424 -388
- package/dist/o_spreadsheet.xml +61 -13
- 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 18.2.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.2.13
|
|
6
|
+
* @date 2025-05-20T05:57:00.985Z
|
|
7
|
+
* @hash 9872529
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -1619,18 +1619,53 @@ function lettersToNumber(letters) {
|
|
|
1619
1619
|
let result = 0;
|
|
1620
1620
|
const l = letters.length;
|
|
1621
1621
|
for (let i = 0; i < l; i++) {
|
|
1622
|
-
const
|
|
1623
|
-
const colIndex = charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1622
|
+
const colIndex = charToNumber(letters[i]);
|
|
1624
1623
|
result = result * 26 + colIndex;
|
|
1625
1624
|
}
|
|
1626
1625
|
return result - 1;
|
|
1627
1626
|
}
|
|
1627
|
+
function charToNumber(char) {
|
|
1628
|
+
const charCode = char.charCodeAt(0);
|
|
1629
|
+
return charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1630
|
+
}
|
|
1628
1631
|
function isCharALetter(char) {
|
|
1629
1632
|
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
|
|
1630
1633
|
}
|
|
1631
1634
|
function isCharADigit(char) {
|
|
1632
1635
|
return char >= "0" && char <= "9";
|
|
1633
1636
|
}
|
|
1637
|
+
// we limit the max column to 3 letters and max row to 7 digits for performance reasons
|
|
1638
|
+
const MAX_COL = lettersToNumber("ZZZ");
|
|
1639
|
+
const MAX_ROW = 9999998;
|
|
1640
|
+
function consumeSpaces(chars) {
|
|
1641
|
+
while (chars.current === " ") {
|
|
1642
|
+
chars.advanceBy(1);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
function consumeLetters(chars) {
|
|
1646
|
+
if (chars.current === "$")
|
|
1647
|
+
chars.advanceBy(1);
|
|
1648
|
+
if (!chars.current || !isCharALetter(chars.current)) {
|
|
1649
|
+
return -1;
|
|
1650
|
+
}
|
|
1651
|
+
let colCoordinate = 0;
|
|
1652
|
+
while (chars.current && isCharALetter(chars.current)) {
|
|
1653
|
+
colCoordinate = colCoordinate * 26 + charToNumber(chars.shift());
|
|
1654
|
+
}
|
|
1655
|
+
return colCoordinate;
|
|
1656
|
+
}
|
|
1657
|
+
function consumeDigits(chars) {
|
|
1658
|
+
if (chars.current === "$")
|
|
1659
|
+
chars.advanceBy(1);
|
|
1660
|
+
if (!chars.current || !isCharADigit(chars.current)) {
|
|
1661
|
+
return -1;
|
|
1662
|
+
}
|
|
1663
|
+
let num = 0;
|
|
1664
|
+
while (chars.current && isCharADigit(chars.current)) {
|
|
1665
|
+
num = num * 10 + Number(chars.shift());
|
|
1666
|
+
}
|
|
1667
|
+
return num;
|
|
1668
|
+
}
|
|
1634
1669
|
/**
|
|
1635
1670
|
* Convert a "XC" coordinate to cartesian coordinates.
|
|
1636
1671
|
*
|
|
@@ -1641,33 +1676,17 @@ function isCharADigit(char) {
|
|
|
1641
1676
|
* Note: it also accepts lowercase coordinates, but not fixed references
|
|
1642
1677
|
*/
|
|
1643
1678
|
function toCartesian(xc) {
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
// Process letter part
|
|
1649
|
-
if (xc[i] === "$")
|
|
1650
|
-
i++;
|
|
1651
|
-
while (i < xc.length && isCharALetter(xc[i])) {
|
|
1652
|
-
letterPart += xc[i++];
|
|
1653
|
-
}
|
|
1654
|
-
if (letterPart.length === 0 || letterPart.length > 3) {
|
|
1655
|
-
// limit to max 3 letters for performance reasons
|
|
1679
|
+
const chars = new TokenizingChars(xc);
|
|
1680
|
+
consumeSpaces(chars);
|
|
1681
|
+
const letterPart = consumeLetters(chars);
|
|
1682
|
+
if (letterPart === -1 || !chars.current) {
|
|
1656
1683
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1657
1684
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
}
|
|
1664
|
-
if (i !== xc.length || numberPart.length === 0 || numberPart.length > 7) {
|
|
1665
|
-
// limit to max 7 numbers for performance reasons
|
|
1666
|
-
throw new Error(`Invalid cell description: ${xc}`);
|
|
1667
|
-
}
|
|
1668
|
-
const col = lettersToNumber(letterPart);
|
|
1669
|
-
const row = Number(numberPart) - 1;
|
|
1670
|
-
if (isNaN(row)) {
|
|
1685
|
+
const num = consumeDigits(chars);
|
|
1686
|
+
consumeSpaces(chars);
|
|
1687
|
+
const col = letterPart - 1;
|
|
1688
|
+
const row = num - 1;
|
|
1689
|
+
if (!chars.isOver() || col > MAX_COL || row > MAX_ROW) {
|
|
1671
1690
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1672
1691
|
}
|
|
1673
1692
|
return { col, row };
|
|
@@ -2079,67 +2098,6 @@ class LazyTranslatedString extends String {
|
|
|
2079
2098
|
}
|
|
2080
2099
|
}
|
|
2081
2100
|
|
|
2082
|
-
/** Reference of a cell (eg. A1, $B$5) */
|
|
2083
|
-
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
2084
|
-
// Same as above, but matches the exact string (nothing before or after)
|
|
2085
|
-
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
2086
|
-
/** Reference of a column header (eg. A, AB, $A) */
|
|
2087
|
-
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
2088
|
-
/** Reference of a row header (eg. 1, $1) */
|
|
2089
|
-
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
2090
|
-
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
2091
|
-
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
2092
|
-
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
2093
|
-
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
2094
|
-
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
2095
|
-
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
2096
|
-
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
2097
|
-
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
2098
|
-
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
2099
|
-
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
2100
|
-
"(" +
|
|
2101
|
-
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
2102
|
-
")" +
|
|
2103
|
-
/$/.source, "i");
|
|
2104
|
-
/**
|
|
2105
|
-
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
2106
|
-
*/
|
|
2107
|
-
function isColReference(xc) {
|
|
2108
|
-
return colReference.test(xc);
|
|
2109
|
-
}
|
|
2110
|
-
/**
|
|
2111
|
-
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
2112
|
-
*/
|
|
2113
|
-
function isRowReference(xc) {
|
|
2114
|
-
return rowReference.test(xc);
|
|
2115
|
-
}
|
|
2116
|
-
function isColHeader(str) {
|
|
2117
|
-
return colHeader.test(str);
|
|
2118
|
-
}
|
|
2119
|
-
function isRowHeader(str) {
|
|
2120
|
-
return rowHeader.test(str);
|
|
2121
|
-
}
|
|
2122
|
-
/**
|
|
2123
|
-
* Return true if the given xc is the reference of a single cell,
|
|
2124
|
-
* without any specified sheet (e.g. A1)
|
|
2125
|
-
*/
|
|
2126
|
-
function isSingleCellReference(xc) {
|
|
2127
|
-
return singleCellReference.test(xc);
|
|
2128
|
-
}
|
|
2129
|
-
function splitReference(ref) {
|
|
2130
|
-
if (!ref.includes("!")) {
|
|
2131
|
-
return { xc: ref };
|
|
2132
|
-
}
|
|
2133
|
-
const parts = ref.split("!");
|
|
2134
|
-
const xc = parts.pop();
|
|
2135
|
-
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
2136
|
-
return { sheetName, xc };
|
|
2137
|
-
}
|
|
2138
|
-
/** Return a reference SheetName!xc from the given arguments */
|
|
2139
|
-
function getFullReference(sheetName, xc) {
|
|
2140
|
-
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
2101
|
/**
|
|
2144
2102
|
* Convert from a cartesian reference to a Zone
|
|
2145
2103
|
* The range boundaries will be kept in the same order as the
|
|
@@ -2157,63 +2115,55 @@ function getFullReference(sheetName, xc) {
|
|
|
2157
2115
|
*
|
|
2158
2116
|
*/
|
|
2159
2117
|
function toZoneWithoutBoundaryChanges(xc) {
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
if (
|
|
2164
|
-
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
if (xc.includes(":")) {
|
|
2169
|
-
[firstRangePart, secondRangePart] = xc.split(":");
|
|
2170
|
-
firstRangePart = firstRangePart.trim();
|
|
2171
|
-
secondRangePart = secondRangePart.trim();
|
|
2172
|
-
}
|
|
2173
|
-
else {
|
|
2174
|
-
firstRangePart = xc.trim();
|
|
2175
|
-
}
|
|
2118
|
+
const chars = new TokenizingChars(xc);
|
|
2119
|
+
consumeSpaces(chars);
|
|
2120
|
+
const sheetSeparatorIndex = xc.indexOf("!");
|
|
2121
|
+
if (sheetSeparatorIndex !== -1) {
|
|
2122
|
+
chars.advanceBy(sheetSeparatorIndex + 1);
|
|
2123
|
+
}
|
|
2124
|
+
const leftLetters = consumeLetters(chars);
|
|
2125
|
+
const leftNumbers = consumeDigits(chars);
|
|
2176
2126
|
let top, bottom, left, right;
|
|
2177
2127
|
let fullCol = false;
|
|
2178
2128
|
let fullRow = false;
|
|
2179
2129
|
let hasHeader = false;
|
|
2180
|
-
if (
|
|
2181
|
-
left = right =
|
|
2130
|
+
if (leftNumbers === -1) {
|
|
2131
|
+
left = right = leftLetters - 1;
|
|
2182
2132
|
top = bottom = 0;
|
|
2183
2133
|
fullCol = true;
|
|
2184
2134
|
}
|
|
2185
|
-
else if (
|
|
2186
|
-
top = bottom =
|
|
2135
|
+
else if (leftLetters === -1) {
|
|
2136
|
+
top = bottom = leftNumbers - 1;
|
|
2187
2137
|
left = right = 0;
|
|
2188
2138
|
fullRow = true;
|
|
2189
2139
|
}
|
|
2190
2140
|
else {
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
top = bottom = c.row;
|
|
2141
|
+
left = right = leftLetters - 1;
|
|
2142
|
+
top = bottom = leftNumbers - 1;
|
|
2194
2143
|
hasHeader = true;
|
|
2195
2144
|
}
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2145
|
+
consumeSpaces(chars);
|
|
2146
|
+
if (chars.current === ":") {
|
|
2147
|
+
chars.advanceBy(1);
|
|
2148
|
+
consumeSpaces(chars);
|
|
2149
|
+
const rightLetters = consumeLetters(chars);
|
|
2150
|
+
const rightNumbers = consumeDigits(chars);
|
|
2151
|
+
if (rightNumbers === -1) {
|
|
2152
|
+
right = rightLetters - 1;
|
|
2199
2153
|
fullCol = true;
|
|
2200
2154
|
}
|
|
2201
|
-
else if (
|
|
2202
|
-
bottom =
|
|
2155
|
+
else if (rightLetters === -1) {
|
|
2156
|
+
bottom = rightNumbers - 1;
|
|
2203
2157
|
fullRow = true;
|
|
2204
2158
|
}
|
|
2205
2159
|
else {
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
bottom = c.row;
|
|
2160
|
+
right = rightLetters - 1;
|
|
2161
|
+
bottom = rightNumbers - 1;
|
|
2209
2162
|
top = fullCol ? bottom : top;
|
|
2210
2163
|
left = fullRow ? right : left;
|
|
2211
2164
|
hasHeader = true;
|
|
2212
2165
|
}
|
|
2213
2166
|
}
|
|
2214
|
-
if (fullCol && fullRow) {
|
|
2215
|
-
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2216
|
-
}
|
|
2217
2167
|
const zone = {
|
|
2218
2168
|
top,
|
|
2219
2169
|
left,
|
|
@@ -2242,7 +2192,16 @@ function toZoneWithoutBoundaryChanges(xc) {
|
|
|
2242
2192
|
*/
|
|
2243
2193
|
function toUnboundedZone(xc) {
|
|
2244
2194
|
const zone = toZoneWithoutBoundaryChanges(xc);
|
|
2245
|
-
|
|
2195
|
+
const orderedZone = reorderZone(zone);
|
|
2196
|
+
const bottom = orderedZone.bottom;
|
|
2197
|
+
const right = orderedZone.right;
|
|
2198
|
+
if ((bottom !== undefined && bottom > MAX_ROW) || (right !== undefined && right > MAX_COL)) {
|
|
2199
|
+
throw new Error(`Range string out of bounds: ${xc}`); // limit the size of the zone for performance
|
|
2200
|
+
}
|
|
2201
|
+
if (bottom === undefined && right === undefined) {
|
|
2202
|
+
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2203
|
+
}
|
|
2204
|
+
return orderedZone;
|
|
2246
2205
|
}
|
|
2247
2206
|
/**
|
|
2248
2207
|
* Convert from a cartesian reference to a Zone.
|
|
@@ -3359,7 +3318,7 @@ function isDateAfter(date, dateAfter) {
|
|
|
3359
3318
|
*/
|
|
3360
3319
|
const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
|
|
3361
3320
|
decimalSeparator = escapeRegExp(decimalSeparator);
|
|
3362
|
-
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e
|
|
3321
|
+
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
|
|
3363
3322
|
});
|
|
3364
3323
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
3365
3324
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
@@ -5976,6 +5935,67 @@ function scrollDelay(value) {
|
|
|
5976
5935
|
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5977
5936
|
}
|
|
5978
5937
|
|
|
5938
|
+
/** Reference of a cell (eg. A1, $B$5) */
|
|
5939
|
+
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
5940
|
+
// Same as above, but matches the exact string (nothing before or after)
|
|
5941
|
+
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
5942
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
5943
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
5944
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
5945
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
5946
|
+
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
5947
|
+
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
5948
|
+
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
5949
|
+
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
5950
|
+
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
5951
|
+
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
5952
|
+
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
5953
|
+
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
5954
|
+
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
5955
|
+
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
5956
|
+
"(" +
|
|
5957
|
+
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
5958
|
+
")" +
|
|
5959
|
+
/$/.source, "i");
|
|
5960
|
+
/**
|
|
5961
|
+
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
5962
|
+
*/
|
|
5963
|
+
function isColReference(xc) {
|
|
5964
|
+
return colReference.test(xc);
|
|
5965
|
+
}
|
|
5966
|
+
/**
|
|
5967
|
+
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
5968
|
+
*/
|
|
5969
|
+
function isRowReference(xc) {
|
|
5970
|
+
return rowReference.test(xc);
|
|
5971
|
+
}
|
|
5972
|
+
function isColHeader(str) {
|
|
5973
|
+
return colHeader.test(str);
|
|
5974
|
+
}
|
|
5975
|
+
function isRowHeader(str) {
|
|
5976
|
+
return rowHeader.test(str);
|
|
5977
|
+
}
|
|
5978
|
+
/**
|
|
5979
|
+
* Return true if the given xc is the reference of a single cell,
|
|
5980
|
+
* without any specified sheet (e.g. A1)
|
|
5981
|
+
*/
|
|
5982
|
+
function isSingleCellReference(xc) {
|
|
5983
|
+
return singleCellReference.test(xc);
|
|
5984
|
+
}
|
|
5985
|
+
function splitReference(ref) {
|
|
5986
|
+
if (!ref.includes("!")) {
|
|
5987
|
+
return { xc: ref };
|
|
5988
|
+
}
|
|
5989
|
+
const parts = ref.split("!");
|
|
5990
|
+
const xc = parts.pop();
|
|
5991
|
+
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
5992
|
+
return { sheetName, xc };
|
|
5993
|
+
}
|
|
5994
|
+
/** Return a reference SheetName!xc from the given arguments */
|
|
5995
|
+
function getFullReference(sheetName, xc) {
|
|
5996
|
+
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5979
5999
|
class RangeImpl {
|
|
5980
6000
|
getSheetSize;
|
|
5981
6001
|
_zone;
|
|
@@ -6301,6 +6321,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
|
6301
6321
|
}
|
|
6302
6322
|
return name;
|
|
6303
6323
|
}
|
|
6324
|
+
function isSheetNameEqual(name1, name2) {
|
|
6325
|
+
if (name1 === undefined || name2 === undefined) {
|
|
6326
|
+
return false;
|
|
6327
|
+
}
|
|
6328
|
+
return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
|
|
6329
|
+
getUnquotedSheetName(name2.trim().toUpperCase()));
|
|
6330
|
+
}
|
|
6304
6331
|
|
|
6305
6332
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6306
6333
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -8164,10 +8191,9 @@ const AGGREGATOR_NAMES = {
|
|
|
8164
8191
|
avg: _t("Average"),
|
|
8165
8192
|
sum: _t("Sum"),
|
|
8166
8193
|
};
|
|
8167
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8168
8194
|
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8169
|
-
integer:
|
|
8170
|
-
char:
|
|
8195
|
+
integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
|
|
8196
|
+
char: ["count_distinct", "count"],
|
|
8171
8197
|
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8172
8198
|
datetime: ["max", "min", "count_distinct", "count"],
|
|
8173
8199
|
};
|
|
@@ -9520,7 +9546,10 @@ function proxifyStoreMutation(store, callback) {
|
|
|
9520
9546
|
const functionProxy = new Proxy(value, {
|
|
9521
9547
|
// trap the function call
|
|
9522
9548
|
apply(target, thisArg, argArray) {
|
|
9523
|
-
Reflect.apply(target, thisStore, argArray);
|
|
9549
|
+
const res = Reflect.apply(target, thisStore, argArray);
|
|
9550
|
+
if (res === "noStateChange") {
|
|
9551
|
+
return;
|
|
9552
|
+
}
|
|
9524
9553
|
callback();
|
|
9525
9554
|
},
|
|
9526
9555
|
});
|
|
@@ -9542,7 +9571,7 @@ function getDependencyContainer(env) {
|
|
|
9542
9571
|
const ModelStore = createAbstractStore("Model");
|
|
9543
9572
|
|
|
9544
9573
|
class RendererStore {
|
|
9545
|
-
mutators = ["register", "unRegister"];
|
|
9574
|
+
mutators = ["register", "unRegister", "drawLayer"];
|
|
9546
9575
|
renderers = {};
|
|
9547
9576
|
register(renderer) {
|
|
9548
9577
|
if (!renderer.renderingLayers.length) {
|
|
@@ -9562,14 +9591,14 @@ class RendererStore {
|
|
|
9562
9591
|
}
|
|
9563
9592
|
drawLayer(context, layer) {
|
|
9564
9593
|
const renderers = this.renderers[layer];
|
|
9565
|
-
if (
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
context.ctx.restore();
|
|
9594
|
+
if (renderers) {
|
|
9595
|
+
for (const renderer of renderers) {
|
|
9596
|
+
context.ctx.save();
|
|
9597
|
+
renderer.drawLayer(context, layer);
|
|
9598
|
+
context.ctx.restore();
|
|
9599
|
+
}
|
|
9572
9600
|
}
|
|
9601
|
+
return "noStateChange";
|
|
9573
9602
|
}
|
|
9574
9603
|
}
|
|
9575
9604
|
|
|
@@ -9622,16 +9651,17 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9622
9651
|
focusComposer(listener, args) {
|
|
9623
9652
|
this.activeComposer = listener;
|
|
9624
9653
|
if (this.getters.isReadonly()) {
|
|
9625
|
-
return;
|
|
9654
|
+
return "noStateChange";
|
|
9626
9655
|
}
|
|
9627
9656
|
this._focusMode = args.focusMode || "contentFocus";
|
|
9628
9657
|
if (this._focusMode !== "inactive") {
|
|
9629
9658
|
this.setComposerContent(args);
|
|
9630
9659
|
}
|
|
9660
|
+
return;
|
|
9631
9661
|
}
|
|
9632
9662
|
focusActiveComposer(args) {
|
|
9633
9663
|
if (this.getters.isReadonly()) {
|
|
9634
|
-
return;
|
|
9664
|
+
return "noStateChange";
|
|
9635
9665
|
}
|
|
9636
9666
|
if (!this.activeComposer) {
|
|
9637
9667
|
throw new Error("No composer is registered");
|
|
@@ -9640,6 +9670,7 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9640
9670
|
if (this._focusMode !== "inactive") {
|
|
9641
9671
|
this.setComposerContent(args);
|
|
9642
9672
|
}
|
|
9673
|
+
return;
|
|
9643
9674
|
}
|
|
9644
9675
|
/**
|
|
9645
9676
|
* Start the edition or update the content if it's already started.
|
|
@@ -10133,7 +10164,7 @@ function getDefinedAxis(definition) {
|
|
|
10133
10164
|
}
|
|
10134
10165
|
function formatChartDatasetValue(axisFormats, locale) {
|
|
10135
10166
|
return (value, axisId) => {
|
|
10136
|
-
const format =
|
|
10167
|
+
const format = axisFormats?.[axisId];
|
|
10137
10168
|
return formatTickValue({ format, locale })(value);
|
|
10138
10169
|
};
|
|
10139
10170
|
}
|
|
@@ -10307,7 +10338,7 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
10307
10338
|
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10308
10339
|
ctx.fillStyle = chartFontColor(options.background);
|
|
10309
10340
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
10310
|
-
const displayValue = options.callback(value);
|
|
10341
|
+
const displayValue = options.callback(value, "y");
|
|
10311
10342
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
10312
10343
|
}
|
|
10313
10344
|
}
|
|
@@ -19206,6 +19237,9 @@ const PIVOT_VALUE = {
|
|
|
19206
19237
|
};
|
|
19207
19238
|
}
|
|
19208
19239
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19240
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19241
|
+
this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
|
|
19242
|
+
}
|
|
19209
19243
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19210
19244
|
},
|
|
19211
19245
|
};
|
|
@@ -19237,6 +19271,9 @@ const PIVOT_HEADER = {
|
|
|
19237
19271
|
};
|
|
19238
19272
|
}
|
|
19239
19273
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19274
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19275
|
+
this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
|
|
19276
|
+
}
|
|
19240
19277
|
const lastNode = domain.at(-1);
|
|
19241
19278
|
if (lastNode?.field === "measure") {
|
|
19242
19279
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
@@ -19459,6 +19496,9 @@ function isEmpty(data) {
|
|
|
19459
19496
|
return data === undefined || data.value === null;
|
|
19460
19497
|
}
|
|
19461
19498
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
19499
|
+
function areAlmostEqual(value1, value2, epsilon = 2e-16) {
|
|
19500
|
+
return Math.abs(value1 - value2) < epsilon;
|
|
19501
|
+
}
|
|
19462
19502
|
const EQ = {
|
|
19463
19503
|
description: _t("Equal."),
|
|
19464
19504
|
args: [
|
|
@@ -19480,6 +19520,9 @@ const EQ = {
|
|
|
19480
19520
|
if (typeof _value2 === "string") {
|
|
19481
19521
|
_value2 = _value2.toUpperCase();
|
|
19482
19522
|
}
|
|
19523
|
+
if (typeof _value1 === "number" && typeof _value2 === "number") {
|
|
19524
|
+
return { value: areAlmostEqual(_value1, _value2) };
|
|
19525
|
+
}
|
|
19483
19526
|
return { value: _value1 === _value2 };
|
|
19484
19527
|
},
|
|
19485
19528
|
};
|
|
@@ -19519,6 +19562,9 @@ const GT = {
|
|
|
19519
19562
|
],
|
|
19520
19563
|
compute: function (value1, value2) {
|
|
19521
19564
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19565
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19566
|
+
return !areAlmostEqual(v1, v2) && v1 > v2;
|
|
19567
|
+
}
|
|
19522
19568
|
return v1 > v2;
|
|
19523
19569
|
});
|
|
19524
19570
|
},
|
|
@@ -19534,6 +19580,9 @@ const GTE = {
|
|
|
19534
19580
|
],
|
|
19535
19581
|
compute: function (value1, value2) {
|
|
19536
19582
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19583
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19584
|
+
return areAlmostEqual(v1, v2) || v1 > v2;
|
|
19585
|
+
}
|
|
19537
19586
|
return v1 >= v2;
|
|
19538
19587
|
});
|
|
19539
19588
|
},
|
|
@@ -21140,7 +21189,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
21140
21189
|
.find((token) => {
|
|
21141
21190
|
const { xc, sheetName: sheet } = splitReference(token.value);
|
|
21142
21191
|
const sheetName = sheet || this.getters.getSheetName(this.sheetId);
|
|
21143
|
-
if (this.getters.getSheetName(activeSheetId)
|
|
21192
|
+
if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
|
|
21144
21193
|
return false;
|
|
21145
21194
|
}
|
|
21146
21195
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
@@ -24602,7 +24651,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
|
|
|
24602
24651
|
({ xc, sheetName } = splitReference(reference));
|
|
24603
24652
|
let rangeSheetIndex;
|
|
24604
24653
|
if (sheetName) {
|
|
24605
|
-
const index = data.sheets.findIndex((sheet) => sheet.name
|
|
24654
|
+
const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
|
|
24606
24655
|
if (index < 0) {
|
|
24607
24656
|
throw new Error("Unable to find a sheet with the name " + sheetName);
|
|
24608
24657
|
}
|
|
@@ -24943,7 +24992,7 @@ function convertFormula(formula, data) {
|
|
|
24943
24992
|
formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
|
|
24944
24993
|
externalRefId = Number(externalRefId) - 1;
|
|
24945
24994
|
cellRef = cellRef.replace(/\$/g, "");
|
|
24946
|
-
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name
|
|
24995
|
+
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
|
|
24947
24996
|
if (sheetIndex === -1) {
|
|
24948
24997
|
return match;
|
|
24949
24998
|
}
|
|
@@ -25594,7 +25643,7 @@ function convertPivotTableConfig(pivotTable) {
|
|
|
25594
25643
|
*/
|
|
25595
25644
|
function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
|
|
25596
25645
|
for (let tableSheet of convertedSheets) {
|
|
25597
|
-
const tables = xlsxSheets.find((s) => s.sheetName
|
|
25646
|
+
const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
|
|
25598
25647
|
for (let table of tables) {
|
|
25599
25648
|
const tabRef = table.name + "[";
|
|
25600
25649
|
for (let sheet of convertedSheets) {
|
|
@@ -32586,12 +32635,20 @@ class HoveredCellStore extends SpreadsheetStore {
|
|
|
32586
32635
|
}
|
|
32587
32636
|
}
|
|
32588
32637
|
hover(position) {
|
|
32638
|
+
if (position.col === this.col && position.row === this.row) {
|
|
32639
|
+
return "noStateChange";
|
|
32640
|
+
}
|
|
32589
32641
|
this.col = position.col;
|
|
32590
32642
|
this.row = position.row;
|
|
32643
|
+
return;
|
|
32591
32644
|
}
|
|
32592
32645
|
clear() {
|
|
32646
|
+
if (this.col === undefined && this.row === undefined) {
|
|
32647
|
+
return "noStateChange";
|
|
32648
|
+
}
|
|
32593
32649
|
this.col = undefined;
|
|
32594
32650
|
this.row = undefined;
|
|
32651
|
+
return;
|
|
32595
32652
|
}
|
|
32596
32653
|
}
|
|
32597
32654
|
|
|
@@ -32613,7 +32670,11 @@ class CellPopoverStore extends SpreadsheetStore {
|
|
|
32613
32670
|
this.persistentPopover = { col, row, sheetId, type };
|
|
32614
32671
|
}
|
|
32615
32672
|
close() {
|
|
32673
|
+
if (!this.persistentPopover) {
|
|
32674
|
+
return "noStateChange";
|
|
32675
|
+
}
|
|
32616
32676
|
this.persistentPopover = undefined;
|
|
32677
|
+
return;
|
|
32617
32678
|
}
|
|
32618
32679
|
get persistentCellPopover() {
|
|
32619
32680
|
return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
|
|
@@ -40069,10 +40130,18 @@ class GaugeChartConfigPanel extends owl.Component {
|
|
|
40069
40130
|
}
|
|
40070
40131
|
|
|
40071
40132
|
class DOMFocusableElementStore {
|
|
40072
|
-
mutators = ["setFocusableElement"];
|
|
40133
|
+
mutators = ["setFocusableElement", "focus"];
|
|
40073
40134
|
focusableElement = undefined;
|
|
40074
40135
|
setFocusableElement(element) {
|
|
40075
40136
|
this.focusableElement = element;
|
|
40137
|
+
return "noStateChange";
|
|
40138
|
+
}
|
|
40139
|
+
focus() {
|
|
40140
|
+
if (this.focusableElement === document.activeElement) {
|
|
40141
|
+
return "noStateChange";
|
|
40142
|
+
}
|
|
40143
|
+
this.focusableElement?.focus();
|
|
40144
|
+
return;
|
|
40076
40145
|
}
|
|
40077
40146
|
}
|
|
40078
40147
|
|
|
@@ -40660,7 +40729,7 @@ class Composer extends owl.Component {
|
|
|
40660
40729
|
if (document.activeElement === this.contentHelper.el &&
|
|
40661
40730
|
this.props.composerStore.editionMode === "inactive" &&
|
|
40662
40731
|
!this.props.isDefaultFocus) {
|
|
40663
|
-
this.DOMFocusableElementStore.
|
|
40732
|
+
this.DOMFocusableElementStore.focus();
|
|
40664
40733
|
}
|
|
40665
40734
|
});
|
|
40666
40735
|
owl.useEffect(() => {
|
|
@@ -41110,12 +41179,13 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
41110
41179
|
return res;
|
|
41111
41180
|
}
|
|
41112
41181
|
getComposerContent() {
|
|
41182
|
+
let content = this._currentContent;
|
|
41113
41183
|
if (this.editionMode === "inactive") {
|
|
41114
41184
|
// References in the content might not be linked to the current active sheet
|
|
41115
41185
|
// We here force the sheet name prefix for all references that are not in
|
|
41116
41186
|
// the current active sheet
|
|
41117
41187
|
const defaultRangeSheetId = this.args().defaultRangeSheetId;
|
|
41118
|
-
|
|
41188
|
+
content = rangeTokenize(this.args().content)
|
|
41119
41189
|
.map((token) => {
|
|
41120
41190
|
if (token.type === "REFERENCE") {
|
|
41121
41191
|
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
@@ -41125,7 +41195,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
41125
41195
|
})
|
|
41126
41196
|
.join("");
|
|
41127
41197
|
}
|
|
41128
|
-
return this.
|
|
41198
|
+
return localizeContent(content, this.getters.getLocale());
|
|
41129
41199
|
}
|
|
41130
41200
|
stopEdition() {
|
|
41131
41201
|
this._stopEdition();
|
|
@@ -45400,6 +45470,9 @@ class PivotMeasureEditor extends owl.Component {
|
|
|
45400
45470
|
}
|
|
45401
45471
|
return undefined;
|
|
45402
45472
|
}
|
|
45473
|
+
get isCalculatedMeasureInvalid() {
|
|
45474
|
+
return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
|
|
45475
|
+
}
|
|
45403
45476
|
}
|
|
45404
45477
|
|
|
45405
45478
|
css /* scss */ `
|
|
@@ -46944,7 +47017,12 @@ class SpreadsheetPivot {
|
|
|
46944
47017
|
entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
|
|
46945
47018
|
}
|
|
46946
47019
|
else {
|
|
46947
|
-
|
|
47020
|
+
if (field.type === "char") {
|
|
47021
|
+
entry[field.name] = { ...cell, value: cell.formattedValue || null };
|
|
47022
|
+
}
|
|
47023
|
+
else {
|
|
47024
|
+
entry[field.name] = cell;
|
|
47025
|
+
}
|
|
46948
47026
|
}
|
|
46949
47027
|
}
|
|
46950
47028
|
entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
|
|
@@ -52082,10 +52160,6 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
52082
52160
|
ctx.scale(dpr, dpr);
|
|
52083
52161
|
for (const layer of OrderedLayers()) {
|
|
52084
52162
|
model.drawLayer(renderingContext, layer);
|
|
52085
|
-
// @ts-ignore 'drawLayer' is not declated as a mutator because:
|
|
52086
|
-
// it does not mutate anything. Most importantly it's used
|
|
52087
|
-
// during rendering. Invoking a mutator during rendering would
|
|
52088
|
-
// trigger another rendering, ultimately resulting in an infinite loop.
|
|
52089
52163
|
rendererStore.drawLayer(renderingContext, layer);
|
|
52090
52164
|
}
|
|
52091
52165
|
}
|
|
@@ -52776,7 +52850,7 @@ class Grid extends owl.Component {
|
|
|
52776
52850
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
52777
52851
|
owl.useEffect(() => {
|
|
52778
52852
|
if (!this.sidePanel.isOpen) {
|
|
52779
|
-
this.DOMFocusableElementStore.
|
|
52853
|
+
this.DOMFocusableElementStore.focus();
|
|
52780
52854
|
}
|
|
52781
52855
|
}, () => [this.sidePanel.isOpen]);
|
|
52782
52856
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
@@ -52986,7 +53060,7 @@ class Grid extends owl.Component {
|
|
|
52986
53060
|
focusDefaultElement() {
|
|
52987
53061
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
52988
53062
|
this.composerFocusStore.activeComposer.editionMode === "inactive") {
|
|
52989
|
-
this.DOMFocusableElementStore.
|
|
53063
|
+
this.DOMFocusableElementStore.focus();
|
|
52990
53064
|
}
|
|
52991
53065
|
}
|
|
52992
53066
|
get gridEl() {
|
|
@@ -53331,6 +53405,322 @@ class Grid extends owl.Component {
|
|
|
53331
53405
|
}
|
|
53332
53406
|
}
|
|
53333
53407
|
|
|
53408
|
+
css /* scss */ `
|
|
53409
|
+
.o_pivot_html_renderer {
|
|
53410
|
+
width: 100%;
|
|
53411
|
+
border-collapse: collapse;
|
|
53412
|
+
|
|
53413
|
+
&:hover {
|
|
53414
|
+
cursor: pointer;
|
|
53415
|
+
}
|
|
53416
|
+
|
|
53417
|
+
td,
|
|
53418
|
+
th {
|
|
53419
|
+
border: 1px solid #dee2e6;
|
|
53420
|
+
background-color: #fff;
|
|
53421
|
+
padding: 0.3rem;
|
|
53422
|
+
white-space: nowrap;
|
|
53423
|
+
|
|
53424
|
+
&:hover {
|
|
53425
|
+
filter: brightness(0.9);
|
|
53426
|
+
}
|
|
53427
|
+
}
|
|
53428
|
+
|
|
53429
|
+
td {
|
|
53430
|
+
text-align: right;
|
|
53431
|
+
}
|
|
53432
|
+
|
|
53433
|
+
th {
|
|
53434
|
+
background-color: #f5f5f5;
|
|
53435
|
+
font-weight: bold;
|
|
53436
|
+
color: black;
|
|
53437
|
+
}
|
|
53438
|
+
|
|
53439
|
+
.o_missing_value {
|
|
53440
|
+
color: #46646d;
|
|
53441
|
+
background: #e7f2f6;
|
|
53442
|
+
}
|
|
53443
|
+
}
|
|
53444
|
+
`;
|
|
53445
|
+
class PivotHTMLRenderer extends owl.Component {
|
|
53446
|
+
static template = "o_spreadsheet.PivotHTMLRenderer";
|
|
53447
|
+
static components = { Checkbox };
|
|
53448
|
+
static props = {
|
|
53449
|
+
pivotId: String,
|
|
53450
|
+
onCellClicked: Function,
|
|
53451
|
+
};
|
|
53452
|
+
pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
53453
|
+
data = {
|
|
53454
|
+
columns: [],
|
|
53455
|
+
rows: [],
|
|
53456
|
+
values: [],
|
|
53457
|
+
};
|
|
53458
|
+
state = owl.useState({
|
|
53459
|
+
showMissingValuesOnly: false,
|
|
53460
|
+
});
|
|
53461
|
+
setup() {
|
|
53462
|
+
const table = this.pivot.getTableStructure();
|
|
53463
|
+
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
53464
|
+
this.data = {
|
|
53465
|
+
columns: this._buildColHeaders(formulaId, table),
|
|
53466
|
+
rows: this._buildRowHeaders(formulaId, table),
|
|
53467
|
+
values: this._buildValues(formulaId, table),
|
|
53468
|
+
};
|
|
53469
|
+
}
|
|
53470
|
+
get tracker() {
|
|
53471
|
+
return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
|
|
53472
|
+
}
|
|
53473
|
+
// ---------------------------------------------------------------------
|
|
53474
|
+
// Missing values building
|
|
53475
|
+
// ---------------------------------------------------------------------
|
|
53476
|
+
/**
|
|
53477
|
+
* Retrieve the data to display in the Pivot Table
|
|
53478
|
+
* In the case when showMissingValuesOnly is false, the returned value
|
|
53479
|
+
* is the complete data
|
|
53480
|
+
* In the case when showMissingValuesOnly is true, the returned value is
|
|
53481
|
+
* the data which contains only missing values in the rows and cols. In
|
|
53482
|
+
* the rows, we also return the parent rows of rows which contains missing
|
|
53483
|
+
* values, to give context to the user.
|
|
53484
|
+
*
|
|
53485
|
+
*/
|
|
53486
|
+
getTableData() {
|
|
53487
|
+
if (!this.state.showMissingValuesOnly) {
|
|
53488
|
+
return this.data;
|
|
53489
|
+
}
|
|
53490
|
+
const colIndexes = this.getColumnsIndexes();
|
|
53491
|
+
const rowIndexes = this.getRowsIndexes();
|
|
53492
|
+
const columns = this.buildColumnsMissing(colIndexes);
|
|
53493
|
+
const rows = this.buildRowsMissing(rowIndexes);
|
|
53494
|
+
const values = this.buildValuesMissing(colIndexes, rowIndexes);
|
|
53495
|
+
return { columns, rows, values };
|
|
53496
|
+
}
|
|
53497
|
+
/**
|
|
53498
|
+
* Retrieve the parents of the given row
|
|
53499
|
+
* ex:
|
|
53500
|
+
* Australia
|
|
53501
|
+
* January
|
|
53502
|
+
* February
|
|
53503
|
+
* The parent of "January" is "Australia"
|
|
53504
|
+
*/
|
|
53505
|
+
addRecursiveRow(index) {
|
|
53506
|
+
const rows = this.pivot.getTableStructure().rows;
|
|
53507
|
+
const row = [...rows[index].values];
|
|
53508
|
+
if (row.length <= 1) {
|
|
53509
|
+
return [index];
|
|
53510
|
+
}
|
|
53511
|
+
row.pop();
|
|
53512
|
+
const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
|
|
53513
|
+
return [index].concat(this.addRecursiveRow(parentRowIndex));
|
|
53514
|
+
}
|
|
53515
|
+
/**
|
|
53516
|
+
* Create the columns to be used, based on the indexes of the columns in
|
|
53517
|
+
* which a missing value is present
|
|
53518
|
+
*
|
|
53519
|
+
*/
|
|
53520
|
+
buildColumnsMissing(indexes) {
|
|
53521
|
+
// columnsMap explode the columns in an array of array of the same
|
|
53522
|
+
// size with the index of each column, repeated 'span' times.
|
|
53523
|
+
// ex:
|
|
53524
|
+
// | A | B |
|
|
53525
|
+
// | 1 | 2 | 3 |
|
|
53526
|
+
// => [
|
|
53527
|
+
// [0, 0, 1]
|
|
53528
|
+
// [0, 1, 2]
|
|
53529
|
+
// ]
|
|
53530
|
+
const columnsMap = [];
|
|
53531
|
+
for (const column of this.data.columns) {
|
|
53532
|
+
const columnMap = [];
|
|
53533
|
+
for (const index in column) {
|
|
53534
|
+
for (let i = 0; i < column[index].span; i++) {
|
|
53535
|
+
columnMap.push(parseInt(index, 10));
|
|
53536
|
+
}
|
|
53537
|
+
}
|
|
53538
|
+
columnsMap.push(columnMap);
|
|
53539
|
+
}
|
|
53540
|
+
// Remove the columns that are not present in indexes
|
|
53541
|
+
for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
|
|
53542
|
+
if (!indexes.includes(i)) {
|
|
53543
|
+
for (const columnMap of columnsMap) {
|
|
53544
|
+
columnMap.splice(i, 1);
|
|
53545
|
+
}
|
|
53546
|
+
}
|
|
53547
|
+
}
|
|
53548
|
+
// Build the columns
|
|
53549
|
+
const columns = [];
|
|
53550
|
+
for (const mapIndex in columnsMap) {
|
|
53551
|
+
const column = [];
|
|
53552
|
+
let index = undefined;
|
|
53553
|
+
let span = 1;
|
|
53554
|
+
for (let i = 0; i < columnsMap[mapIndex].length; i++) {
|
|
53555
|
+
if (index !== columnsMap[mapIndex][i]) {
|
|
53556
|
+
if (index !== undefined) {
|
|
53557
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53558
|
+
}
|
|
53559
|
+
index = columnsMap[mapIndex][i];
|
|
53560
|
+
span = 1;
|
|
53561
|
+
}
|
|
53562
|
+
else {
|
|
53563
|
+
span++;
|
|
53564
|
+
}
|
|
53565
|
+
}
|
|
53566
|
+
if (index !== undefined) {
|
|
53567
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53568
|
+
}
|
|
53569
|
+
columns.push(column);
|
|
53570
|
+
}
|
|
53571
|
+
return columns;
|
|
53572
|
+
}
|
|
53573
|
+
/**
|
|
53574
|
+
* Create the rows to be used, based on the indexes of the rows in
|
|
53575
|
+
* which a missing value is present.
|
|
53576
|
+
*/
|
|
53577
|
+
buildRowsMissing(indexes) {
|
|
53578
|
+
return indexes.map((index) => this.data.rows[index]);
|
|
53579
|
+
}
|
|
53580
|
+
/**
|
|
53581
|
+
* Create the value to be used, based on the indexes of the columns and
|
|
53582
|
+
* rows in which a missing value is present.
|
|
53583
|
+
*/
|
|
53584
|
+
buildValuesMissing(colIndexes, rowIndexes) {
|
|
53585
|
+
const values = colIndexes.map(() => []);
|
|
53586
|
+
for (const row of rowIndexes) {
|
|
53587
|
+
for (const col in colIndexes) {
|
|
53588
|
+
values[col].push(this.data.values[colIndexes[col]][row]);
|
|
53589
|
+
}
|
|
53590
|
+
}
|
|
53591
|
+
return values;
|
|
53592
|
+
}
|
|
53593
|
+
getColumnsIndexes() {
|
|
53594
|
+
const indexes = new Set();
|
|
53595
|
+
for (let i = 0; i < this.data.columns.length; i++) {
|
|
53596
|
+
const exploded = [];
|
|
53597
|
+
for (let y = 0; y < this.data.columns[i].length; y++) {
|
|
53598
|
+
for (let x = 0; x < this.data.columns[i][y].span; x++) {
|
|
53599
|
+
exploded.push(this.data.columns[i][y]);
|
|
53600
|
+
}
|
|
53601
|
+
}
|
|
53602
|
+
for (let y = 0; y < exploded.length; y++) {
|
|
53603
|
+
if (exploded[y].isMissing) {
|
|
53604
|
+
indexes.add(y);
|
|
53605
|
+
}
|
|
53606
|
+
}
|
|
53607
|
+
}
|
|
53608
|
+
for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
|
|
53609
|
+
const values = this.data.values[i];
|
|
53610
|
+
if (values.find((x) => x.isMissing)) {
|
|
53611
|
+
indexes.add(i);
|
|
53612
|
+
}
|
|
53613
|
+
}
|
|
53614
|
+
return Array.from(indexes).sort((a, b) => a - b);
|
|
53615
|
+
}
|
|
53616
|
+
getRowsIndexes() {
|
|
53617
|
+
const rowIndexes = new Set();
|
|
53618
|
+
for (let i = 0; i < this.data.rows.length; i++) {
|
|
53619
|
+
if (this.data.rows[i].isMissing) {
|
|
53620
|
+
rowIndexes.add(i);
|
|
53621
|
+
}
|
|
53622
|
+
for (const col of this.data.values) {
|
|
53623
|
+
if (col[i].isMissing) {
|
|
53624
|
+
this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
|
|
53625
|
+
}
|
|
53626
|
+
}
|
|
53627
|
+
}
|
|
53628
|
+
return Array.from(rowIndexes).sort((a, b) => a - b);
|
|
53629
|
+
}
|
|
53630
|
+
// ---------------------------------------------------------------------
|
|
53631
|
+
// Data table creation
|
|
53632
|
+
// ---------------------------------------------------------------------
|
|
53633
|
+
_buildColHeaders(id, table) {
|
|
53634
|
+
const headers = [];
|
|
53635
|
+
for (const row of table.columns) {
|
|
53636
|
+
const current = [];
|
|
53637
|
+
for (const cell of row) {
|
|
53638
|
+
const args = [];
|
|
53639
|
+
for (let i = 0; i < cell.fields.length; i++) {
|
|
53640
|
+
args.push({ value: cell.fields[i] }, { value: cell.values[i] });
|
|
53641
|
+
}
|
|
53642
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53643
|
+
const locale = this.env.model.getters.getLocale();
|
|
53644
|
+
if (domain.at(-1)?.field === "measure") {
|
|
53645
|
+
const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
|
|
53646
|
+
current.push({
|
|
53647
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53648
|
+
value: formatValue(value, { format, locale }),
|
|
53649
|
+
span: cell.width,
|
|
53650
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53651
|
+
});
|
|
53652
|
+
}
|
|
53653
|
+
else {
|
|
53654
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53655
|
+
current.push({
|
|
53656
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53657
|
+
value: formatValue(value, { format, locale }),
|
|
53658
|
+
span: cell.width,
|
|
53659
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53660
|
+
});
|
|
53661
|
+
}
|
|
53662
|
+
}
|
|
53663
|
+
headers.push(current);
|
|
53664
|
+
}
|
|
53665
|
+
const last = headers[headers.length - 1];
|
|
53666
|
+
headers[headers.length - 1] = last.map((cell) => {
|
|
53667
|
+
if (!cell.isMissing) {
|
|
53668
|
+
cell.style = "color: #756f6f;";
|
|
53669
|
+
}
|
|
53670
|
+
return cell;
|
|
53671
|
+
});
|
|
53672
|
+
return headers;
|
|
53673
|
+
}
|
|
53674
|
+
_buildRowHeaders(id, table) {
|
|
53675
|
+
const headers = [];
|
|
53676
|
+
for (const row of table.rows) {
|
|
53677
|
+
const args = [];
|
|
53678
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53679
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53680
|
+
}
|
|
53681
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53682
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53683
|
+
const locale = this.env.model.getters.getLocale();
|
|
53684
|
+
const cell = {
|
|
53685
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53686
|
+
value: formatValue(value, { format, locale }),
|
|
53687
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53688
|
+
};
|
|
53689
|
+
if (row.indent > 1) {
|
|
53690
|
+
cell.style = `padding-left: ${row.indent - 1 * 10}px`;
|
|
53691
|
+
}
|
|
53692
|
+
headers.push(cell);
|
|
53693
|
+
}
|
|
53694
|
+
return headers;
|
|
53695
|
+
}
|
|
53696
|
+
_buildValues(id, table) {
|
|
53697
|
+
const values = [];
|
|
53698
|
+
for (const col of table.columns.at(-1) || []) {
|
|
53699
|
+
const current = [];
|
|
53700
|
+
const measure = toString(col.values[col.values.length - 1]);
|
|
53701
|
+
for (const row of table.rows) {
|
|
53702
|
+
const args = [];
|
|
53703
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53704
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53705
|
+
}
|
|
53706
|
+
for (let i = 0; i < col.fields.length - 1; i++) {
|
|
53707
|
+
args.push({ value: col.fields[i] }, { value: col.values[i] });
|
|
53708
|
+
}
|
|
53709
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53710
|
+
const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
|
|
53711
|
+
const locale = this.env.model.getters.getLocale();
|
|
53712
|
+
current.push({
|
|
53713
|
+
formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
|
|
53714
|
+
value: formatValue(value, { format, locale }),
|
|
53715
|
+
isMissing: !this.tracker?.isValuePresent(measure, domain),
|
|
53716
|
+
});
|
|
53717
|
+
}
|
|
53718
|
+
values.push(current);
|
|
53719
|
+
}
|
|
53720
|
+
return values;
|
|
53721
|
+
}
|
|
53722
|
+
}
|
|
53723
|
+
|
|
53334
53724
|
/**
|
|
53335
53725
|
* BasePlugin
|
|
53336
53726
|
*
|
|
@@ -56790,7 +57180,7 @@ class RangeAdapter {
|
|
|
56790
57180
|
if (range.sheetId === cmd.sheetId) {
|
|
56791
57181
|
return { changeType: "CHANGE", range };
|
|
56792
57182
|
}
|
|
56793
|
-
if (
|
|
57183
|
+
if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
|
|
56794
57184
|
const invalidSheetName = undefined;
|
|
56795
57185
|
const sheetId = cmd.sheetId;
|
|
56796
57186
|
const newRange = range.clone({ sheetId, invalidSheetName });
|
|
@@ -57407,7 +57797,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57407
57797
|
if (name) {
|
|
57408
57798
|
const unquotedName = getUnquotedSheetName(name);
|
|
57409
57799
|
for (const key in this.sheetIdsMapName) {
|
|
57410
|
-
if (key
|
|
57800
|
+
if (isSheetNameEqual(key, unquotedName)) {
|
|
57411
57801
|
return this.sheetIdsMapName[key];
|
|
57412
57802
|
}
|
|
57413
57803
|
}
|
|
@@ -57655,7 +58045,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57655
58045
|
}
|
|
57656
58046
|
const { orderedSheetIds, sheets } = this;
|
|
57657
58047
|
const name = cmd.name && cmd.name.trim().toLowerCase();
|
|
57658
|
-
if (orderedSheetIds.find((id) => sheets[id]?.name
|
|
58048
|
+
if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
|
|
57659
58049
|
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
57660
58050
|
}
|
|
57661
58051
|
if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
|
|
@@ -60585,10 +60975,9 @@ class Evaluator {
|
|
|
60585
60975
|
return this.evaluatedCells.keysForSheet(sheetId);
|
|
60586
60976
|
}
|
|
60587
60977
|
getArrayFormulaSpreadingOn(position) {
|
|
60588
|
-
const
|
|
60589
|
-
|
|
60590
|
-
|
|
60591
|
-
return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
|
|
60978
|
+
const isEmpty = this.getEvaluatedCell(position).type === CellValueType.empty;
|
|
60979
|
+
if (isEmpty) {
|
|
60980
|
+
return undefined;
|
|
60592
60981
|
}
|
|
60593
60982
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
60594
60983
|
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
@@ -66548,6 +66937,55 @@ class HistoryPlugin extends UIPlugin {
|
|
|
66548
66937
|
}
|
|
66549
66938
|
}
|
|
66550
66939
|
|
|
66940
|
+
class PivotPresenceTracker {
|
|
66941
|
+
trackedValues = new Set();
|
|
66942
|
+
domainToArray(domain) {
|
|
66943
|
+
return domain.flatMap((node) => [node.field, toString(node.value)]);
|
|
66944
|
+
}
|
|
66945
|
+
isValuePresent(measure, domain) {
|
|
66946
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66947
|
+
return this.trackedValues.has(key);
|
|
66948
|
+
}
|
|
66949
|
+
isHeaderPresent(domain) {
|
|
66950
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66951
|
+
return this.trackedValues.has(key);
|
|
66952
|
+
}
|
|
66953
|
+
trackValue(measure, domain) {
|
|
66954
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66955
|
+
this.trackedValues.add(key);
|
|
66956
|
+
}
|
|
66957
|
+
trackHeader(domain) {
|
|
66958
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66959
|
+
this.trackedValues.add(key);
|
|
66960
|
+
}
|
|
66961
|
+
}
|
|
66962
|
+
|
|
66963
|
+
class PivotPresencePlugin extends UIPlugin {
|
|
66964
|
+
static getters = ["getPivotPresenceTracker"];
|
|
66965
|
+
trackPresencePivotId;
|
|
66966
|
+
tracker;
|
|
66967
|
+
handle(cmd) {
|
|
66968
|
+
switch (cmd.type) {
|
|
66969
|
+
case "PIVOT_START_PRESENCE_TRACKING":
|
|
66970
|
+
this.tracker = new PivotPresenceTracker();
|
|
66971
|
+
this.trackPresencePivotId = cmd.pivotId;
|
|
66972
|
+
break;
|
|
66973
|
+
case "PIVOT_STOP_PRESENCE_TRACKING":
|
|
66974
|
+
this.trackPresencePivotId = undefined;
|
|
66975
|
+
break;
|
|
66976
|
+
}
|
|
66977
|
+
}
|
|
66978
|
+
getPivotPresenceTracker(pivotId) {
|
|
66979
|
+
if (this.trackPresencePivotId !== pivotId) {
|
|
66980
|
+
return undefined;
|
|
66981
|
+
}
|
|
66982
|
+
if (!this.tracker) {
|
|
66983
|
+
throw new Error("Tracker not initialized");
|
|
66984
|
+
}
|
|
66985
|
+
return this.tracker;
|
|
66986
|
+
}
|
|
66987
|
+
}
|
|
66988
|
+
|
|
66551
66989
|
class SplitToColumnsPlugin extends UIPlugin {
|
|
66552
66990
|
static getters = ["getAutomaticSeparator"];
|
|
66553
66991
|
allowDispatch(cmd) {
|
|
@@ -69298,6 +69736,7 @@ const featurePluginRegistry = new Registry()
|
|
|
69298
69736
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
69299
69737
|
.add("format", FormatPlugin)
|
|
69300
69738
|
.add("insert_pivot", InsertPivotPlugin)
|
|
69739
|
+
.add("pivot_presence", PivotPresencePlugin)
|
|
69301
69740
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
69302
69741
|
.add("collaborative", CollaborativePlugin)
|
|
69303
69742
|
.add("history", HistoryPlugin)
|
|
@@ -69678,11 +70117,11 @@ class BottomBarSheet extends owl.Component {
|
|
|
69678
70117
|
if (ev.key === "Enter") {
|
|
69679
70118
|
ev.preventDefault();
|
|
69680
70119
|
this.stopEdition();
|
|
69681
|
-
this.DOMFocusableElementStore.
|
|
70120
|
+
this.DOMFocusableElementStore.focus();
|
|
69682
70121
|
}
|
|
69683
70122
|
if (ev.key === "Escape") {
|
|
69684
70123
|
this.cancelEdition();
|
|
69685
|
-
this.DOMFocusableElementStore.
|
|
70124
|
+
this.DOMFocusableElementStore.focus();
|
|
69686
70125
|
}
|
|
69687
70126
|
}
|
|
69688
70127
|
onMouseEventSheetName(ev) {
|
|
@@ -76293,6 +76732,7 @@ const components = {
|
|
|
76293
76732
|
PivotDimensionOrder,
|
|
76294
76733
|
PivotDimension,
|
|
76295
76734
|
PivotLayoutConfigurator,
|
|
76735
|
+
PivotHTMLRenderer,
|
|
76296
76736
|
PivotDeferUpdate,
|
|
76297
76737
|
PivotTitleSection,
|
|
76298
76738
|
CogWheelMenu,
|
|
@@ -76387,6 +76827,6 @@ exports.tokenColors = tokenColors;
|
|
|
76387
76827
|
exports.tokenize = tokenize;
|
|
76388
76828
|
|
|
76389
76829
|
|
|
76390
|
-
__info__.version = "18.2.
|
|
76391
|
-
__info__.date = "2025-05-
|
|
76392
|
-
__info__.hash = "
|
|
76830
|
+
__info__.version = "18.2.13";
|
|
76831
|
+
__info__.date = "2025-05-20T05:57:00.985Z";
|
|
76832
|
+
__info__.hash = "9872529";
|