@odoo/o-spreadsheet 18.1.19 → 18.1.21
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 +318 -192
- package/dist/o-spreadsheet.esm.js +610 -170
- package/dist/o-spreadsheet.iife.js +610 -170
- package/dist/o-spreadsheet.iife.min.js +423 -387
- 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.1.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.21
|
|
6
|
+
* @date 2025-05-20T05:54:45.398Z
|
|
7
|
+
* @hash 89ed6a9
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -1608,18 +1608,53 @@ function lettersToNumber(letters) {
|
|
|
1608
1608
|
let result = 0;
|
|
1609
1609
|
const l = letters.length;
|
|
1610
1610
|
for (let i = 0; i < l; i++) {
|
|
1611
|
-
const
|
|
1612
|
-
const colIndex = charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1611
|
+
const colIndex = charToNumber(letters[i]);
|
|
1613
1612
|
result = result * 26 + colIndex;
|
|
1614
1613
|
}
|
|
1615
1614
|
return result - 1;
|
|
1616
1615
|
}
|
|
1616
|
+
function charToNumber(char) {
|
|
1617
|
+
const charCode = char.charCodeAt(0);
|
|
1618
|
+
return charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1619
|
+
}
|
|
1617
1620
|
function isCharALetter(char) {
|
|
1618
1621
|
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
|
|
1619
1622
|
}
|
|
1620
1623
|
function isCharADigit(char) {
|
|
1621
1624
|
return char >= "0" && char <= "9";
|
|
1622
1625
|
}
|
|
1626
|
+
// we limit the max column to 3 letters and max row to 7 digits for performance reasons
|
|
1627
|
+
const MAX_COL = lettersToNumber("ZZZ");
|
|
1628
|
+
const MAX_ROW = 9999998;
|
|
1629
|
+
function consumeSpaces(chars) {
|
|
1630
|
+
while (chars.current === " ") {
|
|
1631
|
+
chars.advanceBy(1);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function consumeLetters(chars) {
|
|
1635
|
+
if (chars.current === "$")
|
|
1636
|
+
chars.advanceBy(1);
|
|
1637
|
+
if (!chars.current || !isCharALetter(chars.current)) {
|
|
1638
|
+
return -1;
|
|
1639
|
+
}
|
|
1640
|
+
let colCoordinate = 0;
|
|
1641
|
+
while (chars.current && isCharALetter(chars.current)) {
|
|
1642
|
+
colCoordinate = colCoordinate * 26 + charToNumber(chars.shift());
|
|
1643
|
+
}
|
|
1644
|
+
return colCoordinate;
|
|
1645
|
+
}
|
|
1646
|
+
function consumeDigits(chars) {
|
|
1647
|
+
if (chars.current === "$")
|
|
1648
|
+
chars.advanceBy(1);
|
|
1649
|
+
if (!chars.current || !isCharADigit(chars.current)) {
|
|
1650
|
+
return -1;
|
|
1651
|
+
}
|
|
1652
|
+
let num = 0;
|
|
1653
|
+
while (chars.current && isCharADigit(chars.current)) {
|
|
1654
|
+
num = num * 10 + Number(chars.shift());
|
|
1655
|
+
}
|
|
1656
|
+
return num;
|
|
1657
|
+
}
|
|
1623
1658
|
/**
|
|
1624
1659
|
* Convert a "XC" coordinate to cartesian coordinates.
|
|
1625
1660
|
*
|
|
@@ -1630,33 +1665,17 @@ function isCharADigit(char) {
|
|
|
1630
1665
|
* Note: it also accepts lowercase coordinates, but not fixed references
|
|
1631
1666
|
*/
|
|
1632
1667
|
function toCartesian(xc) {
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
// Process letter part
|
|
1638
|
-
if (xc[i] === "$")
|
|
1639
|
-
i++;
|
|
1640
|
-
while (i < xc.length && isCharALetter(xc[i])) {
|
|
1641
|
-
letterPart += xc[i++];
|
|
1642
|
-
}
|
|
1643
|
-
if (letterPart.length === 0 || letterPart.length > 3) {
|
|
1644
|
-
// limit to max 3 letters for performance reasons
|
|
1668
|
+
const chars = new TokenizingChars(xc);
|
|
1669
|
+
consumeSpaces(chars);
|
|
1670
|
+
const letterPart = consumeLetters(chars);
|
|
1671
|
+
if (letterPart === -1 || !chars.current) {
|
|
1645
1672
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1646
1673
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
}
|
|
1653
|
-
if (i !== xc.length || numberPart.length === 0 || numberPart.length > 7) {
|
|
1654
|
-
// limit to max 7 numbers for performance reasons
|
|
1655
|
-
throw new Error(`Invalid cell description: ${xc}`);
|
|
1656
|
-
}
|
|
1657
|
-
const col = lettersToNumber(letterPart);
|
|
1658
|
-
const row = Number(numberPart) - 1;
|
|
1659
|
-
if (isNaN(row)) {
|
|
1674
|
+
const num = consumeDigits(chars);
|
|
1675
|
+
consumeSpaces(chars);
|
|
1676
|
+
const col = letterPart - 1;
|
|
1677
|
+
const row = num - 1;
|
|
1678
|
+
if (!chars.isOver() || col > MAX_COL || row > MAX_ROW) {
|
|
1660
1679
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1661
1680
|
}
|
|
1662
1681
|
return { col, row };
|
|
@@ -2068,67 +2087,6 @@ class LazyTranslatedString extends String {
|
|
|
2068
2087
|
}
|
|
2069
2088
|
}
|
|
2070
2089
|
|
|
2071
|
-
/** Reference of a cell (eg. A1, $B$5) */
|
|
2072
|
-
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
2073
|
-
// Same as above, but matches the exact string (nothing before or after)
|
|
2074
|
-
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
2075
|
-
/** Reference of a column header (eg. A, AB, $A) */
|
|
2076
|
-
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
2077
|
-
/** Reference of a row header (eg. 1, $1) */
|
|
2078
|
-
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
2079
|
-
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
2080
|
-
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
2081
|
-
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
2082
|
-
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
2083
|
-
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
2084
|
-
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
2085
|
-
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
2086
|
-
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
2087
|
-
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
2088
|
-
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
2089
|
-
"(" +
|
|
2090
|
-
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
2091
|
-
")" +
|
|
2092
|
-
/$/.source, "i");
|
|
2093
|
-
/**
|
|
2094
|
-
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
2095
|
-
*/
|
|
2096
|
-
function isColReference(xc) {
|
|
2097
|
-
return colReference.test(xc);
|
|
2098
|
-
}
|
|
2099
|
-
/**
|
|
2100
|
-
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
2101
|
-
*/
|
|
2102
|
-
function isRowReference(xc) {
|
|
2103
|
-
return rowReference.test(xc);
|
|
2104
|
-
}
|
|
2105
|
-
function isColHeader(str) {
|
|
2106
|
-
return colHeader.test(str);
|
|
2107
|
-
}
|
|
2108
|
-
function isRowHeader(str) {
|
|
2109
|
-
return rowHeader.test(str);
|
|
2110
|
-
}
|
|
2111
|
-
/**
|
|
2112
|
-
* Return true if the given xc is the reference of a single cell,
|
|
2113
|
-
* without any specified sheet (e.g. A1)
|
|
2114
|
-
*/
|
|
2115
|
-
function isSingleCellReference(xc) {
|
|
2116
|
-
return singleCellReference.test(xc);
|
|
2117
|
-
}
|
|
2118
|
-
function splitReference(ref) {
|
|
2119
|
-
if (!ref.includes("!")) {
|
|
2120
|
-
return { xc: ref };
|
|
2121
|
-
}
|
|
2122
|
-
const parts = ref.split("!");
|
|
2123
|
-
const xc = parts.pop();
|
|
2124
|
-
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
2125
|
-
return { sheetName, xc };
|
|
2126
|
-
}
|
|
2127
|
-
/** Return a reference SheetName!xc from the given arguments */
|
|
2128
|
-
function getFullReference(sheetName, xc) {
|
|
2129
|
-
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
2090
|
/**
|
|
2133
2091
|
* Convert from a cartesian reference to a Zone
|
|
2134
2092
|
* The range boundaries will be kept in the same order as the
|
|
@@ -2146,63 +2104,55 @@ function getFullReference(sheetName, xc) {
|
|
|
2146
2104
|
*
|
|
2147
2105
|
*/
|
|
2148
2106
|
function toZoneWithoutBoundaryChanges(xc) {
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
if (
|
|
2153
|
-
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
if (xc.includes(":")) {
|
|
2158
|
-
[firstRangePart, secondRangePart] = xc.split(":");
|
|
2159
|
-
firstRangePart = firstRangePart.trim();
|
|
2160
|
-
secondRangePart = secondRangePart.trim();
|
|
2161
|
-
}
|
|
2162
|
-
else {
|
|
2163
|
-
firstRangePart = xc.trim();
|
|
2164
|
-
}
|
|
2107
|
+
const chars = new TokenizingChars(xc);
|
|
2108
|
+
consumeSpaces(chars);
|
|
2109
|
+
const sheetSeparatorIndex = xc.indexOf("!");
|
|
2110
|
+
if (sheetSeparatorIndex !== -1) {
|
|
2111
|
+
chars.advanceBy(sheetSeparatorIndex + 1);
|
|
2112
|
+
}
|
|
2113
|
+
const leftLetters = consumeLetters(chars);
|
|
2114
|
+
const leftNumbers = consumeDigits(chars);
|
|
2165
2115
|
let top, bottom, left, right;
|
|
2166
2116
|
let fullCol = false;
|
|
2167
2117
|
let fullRow = false;
|
|
2168
2118
|
let hasHeader = false;
|
|
2169
|
-
if (
|
|
2170
|
-
left = right =
|
|
2119
|
+
if (leftNumbers === -1) {
|
|
2120
|
+
left = right = leftLetters - 1;
|
|
2171
2121
|
top = bottom = 0;
|
|
2172
2122
|
fullCol = true;
|
|
2173
2123
|
}
|
|
2174
|
-
else if (
|
|
2175
|
-
top = bottom =
|
|
2124
|
+
else if (leftLetters === -1) {
|
|
2125
|
+
top = bottom = leftNumbers - 1;
|
|
2176
2126
|
left = right = 0;
|
|
2177
2127
|
fullRow = true;
|
|
2178
2128
|
}
|
|
2179
2129
|
else {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
top = bottom = c.row;
|
|
2130
|
+
left = right = leftLetters - 1;
|
|
2131
|
+
top = bottom = leftNumbers - 1;
|
|
2183
2132
|
hasHeader = true;
|
|
2184
2133
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2134
|
+
consumeSpaces(chars);
|
|
2135
|
+
if (chars.current === ":") {
|
|
2136
|
+
chars.advanceBy(1);
|
|
2137
|
+
consumeSpaces(chars);
|
|
2138
|
+
const rightLetters = consumeLetters(chars);
|
|
2139
|
+
const rightNumbers = consumeDigits(chars);
|
|
2140
|
+
if (rightNumbers === -1) {
|
|
2141
|
+
right = rightLetters - 1;
|
|
2188
2142
|
fullCol = true;
|
|
2189
2143
|
}
|
|
2190
|
-
else if (
|
|
2191
|
-
bottom =
|
|
2144
|
+
else if (rightLetters === -1) {
|
|
2145
|
+
bottom = rightNumbers - 1;
|
|
2192
2146
|
fullRow = true;
|
|
2193
2147
|
}
|
|
2194
2148
|
else {
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
bottom = c.row;
|
|
2149
|
+
right = rightLetters - 1;
|
|
2150
|
+
bottom = rightNumbers - 1;
|
|
2198
2151
|
top = fullCol ? bottom : top;
|
|
2199
2152
|
left = fullRow ? right : left;
|
|
2200
2153
|
hasHeader = true;
|
|
2201
2154
|
}
|
|
2202
2155
|
}
|
|
2203
|
-
if (fullCol && fullRow) {
|
|
2204
|
-
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2205
|
-
}
|
|
2206
2156
|
const zone = {
|
|
2207
2157
|
top,
|
|
2208
2158
|
left,
|
|
@@ -2231,7 +2181,16 @@ function toZoneWithoutBoundaryChanges(xc) {
|
|
|
2231
2181
|
*/
|
|
2232
2182
|
function toUnboundedZone(xc) {
|
|
2233
2183
|
const zone = toZoneWithoutBoundaryChanges(xc);
|
|
2234
|
-
|
|
2184
|
+
const orderedZone = reorderZone(zone);
|
|
2185
|
+
const bottom = orderedZone.bottom;
|
|
2186
|
+
const right = orderedZone.right;
|
|
2187
|
+
if ((bottom !== undefined && bottom > MAX_ROW) || (right !== undefined && right > MAX_COL)) {
|
|
2188
|
+
throw new Error(`Range string out of bounds: ${xc}`); // limit the size of the zone for performance
|
|
2189
|
+
}
|
|
2190
|
+
if (bottom === undefined && right === undefined) {
|
|
2191
|
+
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
2192
|
+
}
|
|
2193
|
+
return orderedZone;
|
|
2235
2194
|
}
|
|
2236
2195
|
/**
|
|
2237
2196
|
* Convert from a cartesian reference to a Zone.
|
|
@@ -3348,7 +3307,7 @@ function isDateAfter(date, dateAfter) {
|
|
|
3348
3307
|
*/
|
|
3349
3308
|
const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
|
|
3350
3309
|
decimalSeparator = escapeRegExp(decimalSeparator);
|
|
3351
|
-
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e
|
|
3310
|
+
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
|
|
3352
3311
|
});
|
|
3353
3312
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
3354
3313
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
@@ -5967,6 +5926,67 @@ function scrollDelay(value) {
|
|
|
5967
5926
|
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5968
5927
|
}
|
|
5969
5928
|
|
|
5929
|
+
/** Reference of a cell (eg. A1, $B$5) */
|
|
5930
|
+
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
5931
|
+
// Same as above, but matches the exact string (nothing before or after)
|
|
5932
|
+
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
5933
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
5934
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
5935
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
5936
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
5937
|
+
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
5938
|
+
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
5939
|
+
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
5940
|
+
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
5941
|
+
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
5942
|
+
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
5943
|
+
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
5944
|
+
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
5945
|
+
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
5946
|
+
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
5947
|
+
"(" +
|
|
5948
|
+
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
5949
|
+
")" +
|
|
5950
|
+
/$/.source, "i");
|
|
5951
|
+
/**
|
|
5952
|
+
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
5953
|
+
*/
|
|
5954
|
+
function isColReference(xc) {
|
|
5955
|
+
return colReference.test(xc);
|
|
5956
|
+
}
|
|
5957
|
+
/**
|
|
5958
|
+
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
5959
|
+
*/
|
|
5960
|
+
function isRowReference(xc) {
|
|
5961
|
+
return rowReference.test(xc);
|
|
5962
|
+
}
|
|
5963
|
+
function isColHeader(str) {
|
|
5964
|
+
return colHeader.test(str);
|
|
5965
|
+
}
|
|
5966
|
+
function isRowHeader(str) {
|
|
5967
|
+
return rowHeader.test(str);
|
|
5968
|
+
}
|
|
5969
|
+
/**
|
|
5970
|
+
* Return true if the given xc is the reference of a single cell,
|
|
5971
|
+
* without any specified sheet (e.g. A1)
|
|
5972
|
+
*/
|
|
5973
|
+
function isSingleCellReference(xc) {
|
|
5974
|
+
return singleCellReference.test(xc);
|
|
5975
|
+
}
|
|
5976
|
+
function splitReference(ref) {
|
|
5977
|
+
if (!ref.includes("!")) {
|
|
5978
|
+
return { xc: ref };
|
|
5979
|
+
}
|
|
5980
|
+
const parts = ref.split("!");
|
|
5981
|
+
const xc = parts.pop();
|
|
5982
|
+
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
5983
|
+
return { sheetName, xc };
|
|
5984
|
+
}
|
|
5985
|
+
/** Return a reference SheetName!xc from the given arguments */
|
|
5986
|
+
function getFullReference(sheetName, xc) {
|
|
5987
|
+
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
5988
|
+
}
|
|
5989
|
+
|
|
5970
5990
|
class RangeImpl {
|
|
5971
5991
|
getSheetSize;
|
|
5972
5992
|
_zone;
|
|
@@ -6292,6 +6312,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
|
6292
6312
|
}
|
|
6293
6313
|
return name;
|
|
6294
6314
|
}
|
|
6315
|
+
function isSheetNameEqual(name1, name2) {
|
|
6316
|
+
if (name1 === undefined || name2 === undefined) {
|
|
6317
|
+
return false;
|
|
6318
|
+
}
|
|
6319
|
+
return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
|
|
6320
|
+
getUnquotedSheetName(name2.trim().toUpperCase()));
|
|
6321
|
+
}
|
|
6295
6322
|
|
|
6296
6323
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6297
6324
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -8155,10 +8182,9 @@ const AGGREGATOR_NAMES = {
|
|
|
8155
8182
|
avg: _t("Average"),
|
|
8156
8183
|
sum: _t("Sum"),
|
|
8157
8184
|
};
|
|
8158
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8159
8185
|
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8160
|
-
integer:
|
|
8161
|
-
char:
|
|
8186
|
+
integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
|
|
8187
|
+
char: ["count_distinct", "count"],
|
|
8162
8188
|
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8163
8189
|
};
|
|
8164
8190
|
const AGGREGATORS = {};
|
|
@@ -9510,7 +9536,10 @@ function proxifyStoreMutation(store, callback) {
|
|
|
9510
9536
|
const functionProxy = new Proxy(value, {
|
|
9511
9537
|
// trap the function call
|
|
9512
9538
|
apply(target, thisArg, argArray) {
|
|
9513
|
-
Reflect.apply(target, thisStore, argArray);
|
|
9539
|
+
const res = Reflect.apply(target, thisStore, argArray);
|
|
9540
|
+
if (res === "noStateChange") {
|
|
9541
|
+
return;
|
|
9542
|
+
}
|
|
9514
9543
|
callback();
|
|
9515
9544
|
},
|
|
9516
9545
|
});
|
|
@@ -9532,7 +9561,7 @@ function getDependencyContainer(env) {
|
|
|
9532
9561
|
const ModelStore = createAbstractStore("Model");
|
|
9533
9562
|
|
|
9534
9563
|
class RendererStore {
|
|
9535
|
-
mutators = ["register", "unRegister"];
|
|
9564
|
+
mutators = ["register", "unRegister", "drawLayer"];
|
|
9536
9565
|
renderers = {};
|
|
9537
9566
|
register(renderer) {
|
|
9538
9567
|
if (!renderer.renderingLayers.length) {
|
|
@@ -9552,14 +9581,14 @@ class RendererStore {
|
|
|
9552
9581
|
}
|
|
9553
9582
|
drawLayer(context, layer) {
|
|
9554
9583
|
const renderers = this.renderers[layer];
|
|
9555
|
-
if (
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
context.ctx.restore();
|
|
9584
|
+
if (renderers) {
|
|
9585
|
+
for (const renderer of renderers) {
|
|
9586
|
+
context.ctx.save();
|
|
9587
|
+
renderer.drawLayer(context, layer);
|
|
9588
|
+
context.ctx.restore();
|
|
9589
|
+
}
|
|
9562
9590
|
}
|
|
9591
|
+
return "noStateChange";
|
|
9563
9592
|
}
|
|
9564
9593
|
}
|
|
9565
9594
|
|
|
@@ -9612,16 +9641,17 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9612
9641
|
focusComposer(listener, args) {
|
|
9613
9642
|
this.activeComposer = listener;
|
|
9614
9643
|
if (this.getters.isReadonly()) {
|
|
9615
|
-
return;
|
|
9644
|
+
return "noStateChange";
|
|
9616
9645
|
}
|
|
9617
9646
|
this._focusMode = args.focusMode || "contentFocus";
|
|
9618
9647
|
if (this._focusMode !== "inactive") {
|
|
9619
9648
|
this.setComposerContent(args);
|
|
9620
9649
|
}
|
|
9650
|
+
return;
|
|
9621
9651
|
}
|
|
9622
9652
|
focusActiveComposer(args) {
|
|
9623
9653
|
if (this.getters.isReadonly()) {
|
|
9624
|
-
return;
|
|
9654
|
+
return "noStateChange";
|
|
9625
9655
|
}
|
|
9626
9656
|
if (!this.activeComposer) {
|
|
9627
9657
|
throw new Error("No composer is registered");
|
|
@@ -9630,6 +9660,7 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9630
9660
|
if (this._focusMode !== "inactive") {
|
|
9631
9661
|
this.setComposerContent(args);
|
|
9632
9662
|
}
|
|
9663
|
+
return;
|
|
9633
9664
|
}
|
|
9634
9665
|
/**
|
|
9635
9666
|
* Start the edition or update the content if it's already started.
|
|
@@ -9979,7 +10010,7 @@ function getDefinedAxis(definition) {
|
|
|
9979
10010
|
}
|
|
9980
10011
|
function formatChartDatasetValue(axisFormats, locale) {
|
|
9981
10012
|
return (value, axisId) => {
|
|
9982
|
-
const format =
|
|
10013
|
+
const format = axisFormats?.[axisId];
|
|
9983
10014
|
return formatTickValue({ format, locale })(value);
|
|
9984
10015
|
};
|
|
9985
10016
|
}
|
|
@@ -10143,7 +10174,7 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
10143
10174
|
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10144
10175
|
ctx.fillStyle = chartFontColor(options.background);
|
|
10145
10176
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
10146
|
-
const displayValue = options.callback(value);
|
|
10177
|
+
const displayValue = options.callback(value, "y");
|
|
10147
10178
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
10148
10179
|
}
|
|
10149
10180
|
}
|
|
@@ -19033,6 +19064,9 @@ const PIVOT_VALUE = {
|
|
|
19033
19064
|
};
|
|
19034
19065
|
}
|
|
19035
19066
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19067
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19068
|
+
this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
|
|
19069
|
+
}
|
|
19036
19070
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19037
19071
|
},
|
|
19038
19072
|
};
|
|
@@ -19064,6 +19098,9 @@ const PIVOT_HEADER = {
|
|
|
19064
19098
|
};
|
|
19065
19099
|
}
|
|
19066
19100
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19101
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19102
|
+
this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
|
|
19103
|
+
}
|
|
19067
19104
|
const lastNode = domain.at(-1);
|
|
19068
19105
|
if (lastNode?.field === "measure") {
|
|
19069
19106
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
@@ -19286,6 +19323,9 @@ function isEmpty(data) {
|
|
|
19286
19323
|
return data === undefined || data.value === null;
|
|
19287
19324
|
}
|
|
19288
19325
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
19326
|
+
function areAlmostEqual(value1, value2, epsilon = 2e-16) {
|
|
19327
|
+
return Math.abs(value1 - value2) < epsilon;
|
|
19328
|
+
}
|
|
19289
19329
|
const EQ = {
|
|
19290
19330
|
description: _t("Equal."),
|
|
19291
19331
|
args: [
|
|
@@ -19307,6 +19347,9 @@ const EQ = {
|
|
|
19307
19347
|
if (typeof _value2 === "string") {
|
|
19308
19348
|
_value2 = _value2.toUpperCase();
|
|
19309
19349
|
}
|
|
19350
|
+
if (typeof _value1 === "number" && typeof _value2 === "number") {
|
|
19351
|
+
return { value: areAlmostEqual(_value1, _value2) };
|
|
19352
|
+
}
|
|
19310
19353
|
return { value: _value1 === _value2 };
|
|
19311
19354
|
},
|
|
19312
19355
|
};
|
|
@@ -19346,6 +19389,9 @@ const GT = {
|
|
|
19346
19389
|
],
|
|
19347
19390
|
compute: function (value1, value2) {
|
|
19348
19391
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19392
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19393
|
+
return !areAlmostEqual(v1, v2) && v1 > v2;
|
|
19394
|
+
}
|
|
19349
19395
|
return v1 > v2;
|
|
19350
19396
|
});
|
|
19351
19397
|
},
|
|
@@ -19361,6 +19407,9 @@ const GTE = {
|
|
|
19361
19407
|
],
|
|
19362
19408
|
compute: function (value1, value2) {
|
|
19363
19409
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19410
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19411
|
+
return areAlmostEqual(v1, v2) || v1 > v2;
|
|
19412
|
+
}
|
|
19364
19413
|
return v1 >= v2;
|
|
19365
19414
|
});
|
|
19366
19415
|
},
|
|
@@ -20967,7 +21016,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20967
21016
|
.find((token) => {
|
|
20968
21017
|
const { xc, sheetName: sheet } = splitReference(token.value);
|
|
20969
21018
|
const sheetName = sheet || this.getters.getSheetName(this.sheetId);
|
|
20970
|
-
if (this.getters.getSheetName(activeSheetId)
|
|
21019
|
+
if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
|
|
20971
21020
|
return false;
|
|
20972
21021
|
}
|
|
20973
21022
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
@@ -24578,7 +24627,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
|
|
|
24578
24627
|
({ xc, sheetName } = splitReference(reference));
|
|
24579
24628
|
let rangeSheetIndex;
|
|
24580
24629
|
if (sheetName) {
|
|
24581
|
-
const index = data.sheets.findIndex((sheet) => sheet.name
|
|
24630
|
+
const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
|
|
24582
24631
|
if (index < 0) {
|
|
24583
24632
|
throw new Error("Unable to find a sheet with the name " + sheetName);
|
|
24584
24633
|
}
|
|
@@ -24919,7 +24968,7 @@ function convertFormula(formula, data) {
|
|
|
24919
24968
|
formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
|
|
24920
24969
|
externalRefId = Number(externalRefId) - 1;
|
|
24921
24970
|
cellRef = cellRef.replace(/\$/g, "");
|
|
24922
|
-
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name
|
|
24971
|
+
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
|
|
24923
24972
|
if (sheetIndex === -1) {
|
|
24924
24973
|
return match;
|
|
24925
24974
|
}
|
|
@@ -25570,7 +25619,7 @@ function convertPivotTableConfig(pivotTable) {
|
|
|
25570
25619
|
*/
|
|
25571
25620
|
function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
|
|
25572
25621
|
for (let tableSheet of convertedSheets) {
|
|
25573
|
-
const tables = xlsxSheets.find((s) => s.sheetName
|
|
25622
|
+
const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
|
|
25574
25623
|
for (let table of tables) {
|
|
25575
25624
|
const tabRef = table.name + "[";
|
|
25576
25625
|
for (let sheet of convertedSheets) {
|
|
@@ -32405,12 +32454,20 @@ class HoveredCellStore extends SpreadsheetStore {
|
|
|
32405
32454
|
}
|
|
32406
32455
|
}
|
|
32407
32456
|
hover(position) {
|
|
32457
|
+
if (position.col === this.col && position.row === this.row) {
|
|
32458
|
+
return "noStateChange";
|
|
32459
|
+
}
|
|
32408
32460
|
this.col = position.col;
|
|
32409
32461
|
this.row = position.row;
|
|
32462
|
+
return;
|
|
32410
32463
|
}
|
|
32411
32464
|
clear() {
|
|
32465
|
+
if (this.col === undefined && this.row === undefined) {
|
|
32466
|
+
return "noStateChange";
|
|
32467
|
+
}
|
|
32412
32468
|
this.col = undefined;
|
|
32413
32469
|
this.row = undefined;
|
|
32470
|
+
return;
|
|
32414
32471
|
}
|
|
32415
32472
|
}
|
|
32416
32473
|
|
|
@@ -32432,7 +32489,11 @@ class CellPopoverStore extends SpreadsheetStore {
|
|
|
32432
32489
|
this.persistentPopover = { col, row, sheetId, type };
|
|
32433
32490
|
}
|
|
32434
32491
|
close() {
|
|
32492
|
+
if (!this.persistentPopover) {
|
|
32493
|
+
return "noStateChange";
|
|
32494
|
+
}
|
|
32435
32495
|
this.persistentPopover = undefined;
|
|
32496
|
+
return;
|
|
32436
32497
|
}
|
|
32437
32498
|
get persistentCellPopover() {
|
|
32438
32499
|
return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
|
|
@@ -40264,10 +40325,18 @@ class ChartPanel extends owl.Component {
|
|
|
40264
40325
|
}
|
|
40265
40326
|
|
|
40266
40327
|
class DOMFocusableElementStore {
|
|
40267
|
-
mutators = ["setFocusableElement"];
|
|
40328
|
+
mutators = ["setFocusableElement", "focus"];
|
|
40268
40329
|
focusableElement = undefined;
|
|
40269
40330
|
setFocusableElement(element) {
|
|
40270
40331
|
this.focusableElement = element;
|
|
40332
|
+
return "noStateChange";
|
|
40333
|
+
}
|
|
40334
|
+
focus() {
|
|
40335
|
+
if (this.focusableElement === document.activeElement) {
|
|
40336
|
+
return "noStateChange";
|
|
40337
|
+
}
|
|
40338
|
+
this.focusableElement?.focus();
|
|
40339
|
+
return;
|
|
40271
40340
|
}
|
|
40272
40341
|
}
|
|
40273
40342
|
|
|
@@ -40857,7 +40926,7 @@ class Composer extends owl.Component {
|
|
|
40857
40926
|
if (document.activeElement === this.contentHelper.el &&
|
|
40858
40927
|
this.props.composerStore.editionMode === "inactive" &&
|
|
40859
40928
|
!this.props.isDefaultFocus) {
|
|
40860
|
-
this.DOMFocusableElementStore.
|
|
40929
|
+
this.DOMFocusableElementStore.focus();
|
|
40861
40930
|
}
|
|
40862
40931
|
});
|
|
40863
40932
|
owl.useEffect(() => {
|
|
@@ -41297,12 +41366,13 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
41297
41366
|
return providersDefinitions;
|
|
41298
41367
|
}
|
|
41299
41368
|
getComposerContent() {
|
|
41369
|
+
let content = this._currentContent;
|
|
41300
41370
|
if (this.editionMode === "inactive") {
|
|
41301
41371
|
// References in the content might not be linked to the current active sheet
|
|
41302
41372
|
// We here force the sheet name prefix for all references that are not in
|
|
41303
41373
|
// the current active sheet
|
|
41304
41374
|
const defaultRangeSheetId = this.args().defaultRangeSheetId;
|
|
41305
|
-
|
|
41375
|
+
content = rangeTokenize(this.args().content)
|
|
41306
41376
|
.map((token) => {
|
|
41307
41377
|
if (token.type === "REFERENCE") {
|
|
41308
41378
|
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
@@ -41312,7 +41382,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
|
|
|
41312
41382
|
})
|
|
41313
41383
|
.join("");
|
|
41314
41384
|
}
|
|
41315
|
-
return this.
|
|
41385
|
+
return localizeContent(content, this.getters.getLocale());
|
|
41316
41386
|
}
|
|
41317
41387
|
stopEdition() {
|
|
41318
41388
|
this._stopEdition();
|
|
@@ -45058,6 +45128,9 @@ class PivotMeasureEditor extends owl.Component {
|
|
|
45058
45128
|
}
|
|
45059
45129
|
return undefined;
|
|
45060
45130
|
}
|
|
45131
|
+
get isCalculatedMeasureInvalid() {
|
|
45132
|
+
return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
|
|
45133
|
+
}
|
|
45061
45134
|
}
|
|
45062
45135
|
|
|
45063
45136
|
css /* scss */ `
|
|
@@ -46604,7 +46677,12 @@ class SpreadsheetPivot {
|
|
|
46604
46677
|
entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
|
|
46605
46678
|
}
|
|
46606
46679
|
else {
|
|
46607
|
-
|
|
46680
|
+
if (field.type === "char") {
|
|
46681
|
+
entry[field.name] = { ...cell, value: cell.formattedValue || null };
|
|
46682
|
+
}
|
|
46683
|
+
else {
|
|
46684
|
+
entry[field.name] = cell;
|
|
46685
|
+
}
|
|
46608
46686
|
}
|
|
46609
46687
|
}
|
|
46610
46688
|
entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
|
|
@@ -51633,10 +51711,6 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
51633
51711
|
ctx.scale(dpr, dpr);
|
|
51634
51712
|
for (const layer of OrderedLayers()) {
|
|
51635
51713
|
model.drawLayer(renderingContext, layer);
|
|
51636
|
-
// @ts-ignore 'drawLayer' is not declated as a mutator because:
|
|
51637
|
-
// it does not mutate anything. Most importantly it's used
|
|
51638
|
-
// during rendering. Invoking a mutator during rendering would
|
|
51639
|
-
// trigger another rendering, ultimately resulting in an infinite loop.
|
|
51640
51714
|
rendererStore.drawLayer(renderingContext, layer);
|
|
51641
51715
|
}
|
|
51642
51716
|
}
|
|
@@ -52326,7 +52400,7 @@ class Grid extends owl.Component {
|
|
|
52326
52400
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
52327
52401
|
owl.useEffect(() => {
|
|
52328
52402
|
if (!this.sidePanel.isOpen) {
|
|
52329
|
-
this.DOMFocusableElementStore.
|
|
52403
|
+
this.DOMFocusableElementStore.focus();
|
|
52330
52404
|
}
|
|
52331
52405
|
}, () => [this.sidePanel.isOpen]);
|
|
52332
52406
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
@@ -52536,7 +52610,7 @@ class Grid extends owl.Component {
|
|
|
52536
52610
|
focusDefaultElement() {
|
|
52537
52611
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
52538
52612
|
this.composerFocusStore.activeComposer.editionMode === "inactive") {
|
|
52539
|
-
this.DOMFocusableElementStore.
|
|
52613
|
+
this.DOMFocusableElementStore.focus();
|
|
52540
52614
|
}
|
|
52541
52615
|
}
|
|
52542
52616
|
get gridEl() {
|
|
@@ -52881,6 +52955,322 @@ class Grid extends owl.Component {
|
|
|
52881
52955
|
}
|
|
52882
52956
|
}
|
|
52883
52957
|
|
|
52958
|
+
css /* scss */ `
|
|
52959
|
+
.o_pivot_html_renderer {
|
|
52960
|
+
width: 100%;
|
|
52961
|
+
border-collapse: collapse;
|
|
52962
|
+
|
|
52963
|
+
&:hover {
|
|
52964
|
+
cursor: pointer;
|
|
52965
|
+
}
|
|
52966
|
+
|
|
52967
|
+
td,
|
|
52968
|
+
th {
|
|
52969
|
+
border: 1px solid #dee2e6;
|
|
52970
|
+
background-color: #fff;
|
|
52971
|
+
padding: 0.3rem;
|
|
52972
|
+
white-space: nowrap;
|
|
52973
|
+
|
|
52974
|
+
&:hover {
|
|
52975
|
+
filter: brightness(0.9);
|
|
52976
|
+
}
|
|
52977
|
+
}
|
|
52978
|
+
|
|
52979
|
+
td {
|
|
52980
|
+
text-align: right;
|
|
52981
|
+
}
|
|
52982
|
+
|
|
52983
|
+
th {
|
|
52984
|
+
background-color: #f5f5f5;
|
|
52985
|
+
font-weight: bold;
|
|
52986
|
+
color: black;
|
|
52987
|
+
}
|
|
52988
|
+
|
|
52989
|
+
.o_missing_value {
|
|
52990
|
+
color: #46646d;
|
|
52991
|
+
background: #e7f2f6;
|
|
52992
|
+
}
|
|
52993
|
+
}
|
|
52994
|
+
`;
|
|
52995
|
+
class PivotHTMLRenderer extends owl.Component {
|
|
52996
|
+
static template = "o_spreadsheet.PivotHTMLRenderer";
|
|
52997
|
+
static components = { Checkbox };
|
|
52998
|
+
static props = {
|
|
52999
|
+
pivotId: String,
|
|
53000
|
+
onCellClicked: Function,
|
|
53001
|
+
};
|
|
53002
|
+
pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
53003
|
+
data = {
|
|
53004
|
+
columns: [],
|
|
53005
|
+
rows: [],
|
|
53006
|
+
values: [],
|
|
53007
|
+
};
|
|
53008
|
+
state = owl.useState({
|
|
53009
|
+
showMissingValuesOnly: false,
|
|
53010
|
+
});
|
|
53011
|
+
setup() {
|
|
53012
|
+
const table = this.pivot.getTableStructure();
|
|
53013
|
+
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
53014
|
+
this.data = {
|
|
53015
|
+
columns: this._buildColHeaders(formulaId, table),
|
|
53016
|
+
rows: this._buildRowHeaders(formulaId, table),
|
|
53017
|
+
values: this._buildValues(formulaId, table),
|
|
53018
|
+
};
|
|
53019
|
+
}
|
|
53020
|
+
get tracker() {
|
|
53021
|
+
return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
|
|
53022
|
+
}
|
|
53023
|
+
// ---------------------------------------------------------------------
|
|
53024
|
+
// Missing values building
|
|
53025
|
+
// ---------------------------------------------------------------------
|
|
53026
|
+
/**
|
|
53027
|
+
* Retrieve the data to display in the Pivot Table
|
|
53028
|
+
* In the case when showMissingValuesOnly is false, the returned value
|
|
53029
|
+
* is the complete data
|
|
53030
|
+
* In the case when showMissingValuesOnly is true, the returned value is
|
|
53031
|
+
* the data which contains only missing values in the rows and cols. In
|
|
53032
|
+
* the rows, we also return the parent rows of rows which contains missing
|
|
53033
|
+
* values, to give context to the user.
|
|
53034
|
+
*
|
|
53035
|
+
*/
|
|
53036
|
+
getTableData() {
|
|
53037
|
+
if (!this.state.showMissingValuesOnly) {
|
|
53038
|
+
return this.data;
|
|
53039
|
+
}
|
|
53040
|
+
const colIndexes = this.getColumnsIndexes();
|
|
53041
|
+
const rowIndexes = this.getRowsIndexes();
|
|
53042
|
+
const columns = this.buildColumnsMissing(colIndexes);
|
|
53043
|
+
const rows = this.buildRowsMissing(rowIndexes);
|
|
53044
|
+
const values = this.buildValuesMissing(colIndexes, rowIndexes);
|
|
53045
|
+
return { columns, rows, values };
|
|
53046
|
+
}
|
|
53047
|
+
/**
|
|
53048
|
+
* Retrieve the parents of the given row
|
|
53049
|
+
* ex:
|
|
53050
|
+
* Australia
|
|
53051
|
+
* January
|
|
53052
|
+
* February
|
|
53053
|
+
* The parent of "January" is "Australia"
|
|
53054
|
+
*/
|
|
53055
|
+
addRecursiveRow(index) {
|
|
53056
|
+
const rows = this.pivot.getTableStructure().rows;
|
|
53057
|
+
const row = [...rows[index].values];
|
|
53058
|
+
if (row.length <= 1) {
|
|
53059
|
+
return [index];
|
|
53060
|
+
}
|
|
53061
|
+
row.pop();
|
|
53062
|
+
const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
|
|
53063
|
+
return [index].concat(this.addRecursiveRow(parentRowIndex));
|
|
53064
|
+
}
|
|
53065
|
+
/**
|
|
53066
|
+
* Create the columns to be used, based on the indexes of the columns in
|
|
53067
|
+
* which a missing value is present
|
|
53068
|
+
*
|
|
53069
|
+
*/
|
|
53070
|
+
buildColumnsMissing(indexes) {
|
|
53071
|
+
// columnsMap explode the columns in an array of array of the same
|
|
53072
|
+
// size with the index of each column, repeated 'span' times.
|
|
53073
|
+
// ex:
|
|
53074
|
+
// | A | B |
|
|
53075
|
+
// | 1 | 2 | 3 |
|
|
53076
|
+
// => [
|
|
53077
|
+
// [0, 0, 1]
|
|
53078
|
+
// [0, 1, 2]
|
|
53079
|
+
// ]
|
|
53080
|
+
const columnsMap = [];
|
|
53081
|
+
for (const column of this.data.columns) {
|
|
53082
|
+
const columnMap = [];
|
|
53083
|
+
for (const index in column) {
|
|
53084
|
+
for (let i = 0; i < column[index].span; i++) {
|
|
53085
|
+
columnMap.push(parseInt(index, 10));
|
|
53086
|
+
}
|
|
53087
|
+
}
|
|
53088
|
+
columnsMap.push(columnMap);
|
|
53089
|
+
}
|
|
53090
|
+
// Remove the columns that are not present in indexes
|
|
53091
|
+
for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
|
|
53092
|
+
if (!indexes.includes(i)) {
|
|
53093
|
+
for (const columnMap of columnsMap) {
|
|
53094
|
+
columnMap.splice(i, 1);
|
|
53095
|
+
}
|
|
53096
|
+
}
|
|
53097
|
+
}
|
|
53098
|
+
// Build the columns
|
|
53099
|
+
const columns = [];
|
|
53100
|
+
for (const mapIndex in columnsMap) {
|
|
53101
|
+
const column = [];
|
|
53102
|
+
let index = undefined;
|
|
53103
|
+
let span = 1;
|
|
53104
|
+
for (let i = 0; i < columnsMap[mapIndex].length; i++) {
|
|
53105
|
+
if (index !== columnsMap[mapIndex][i]) {
|
|
53106
|
+
if (index !== undefined) {
|
|
53107
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53108
|
+
}
|
|
53109
|
+
index = columnsMap[mapIndex][i];
|
|
53110
|
+
span = 1;
|
|
53111
|
+
}
|
|
53112
|
+
else {
|
|
53113
|
+
span++;
|
|
53114
|
+
}
|
|
53115
|
+
}
|
|
53116
|
+
if (index !== undefined) {
|
|
53117
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53118
|
+
}
|
|
53119
|
+
columns.push(column);
|
|
53120
|
+
}
|
|
53121
|
+
return columns;
|
|
53122
|
+
}
|
|
53123
|
+
/**
|
|
53124
|
+
* Create the rows to be used, based on the indexes of the rows in
|
|
53125
|
+
* which a missing value is present.
|
|
53126
|
+
*/
|
|
53127
|
+
buildRowsMissing(indexes) {
|
|
53128
|
+
return indexes.map((index) => this.data.rows[index]);
|
|
53129
|
+
}
|
|
53130
|
+
/**
|
|
53131
|
+
* Create the value to be used, based on the indexes of the columns and
|
|
53132
|
+
* rows in which a missing value is present.
|
|
53133
|
+
*/
|
|
53134
|
+
buildValuesMissing(colIndexes, rowIndexes) {
|
|
53135
|
+
const values = colIndexes.map(() => []);
|
|
53136
|
+
for (const row of rowIndexes) {
|
|
53137
|
+
for (const col in colIndexes) {
|
|
53138
|
+
values[col].push(this.data.values[colIndexes[col]][row]);
|
|
53139
|
+
}
|
|
53140
|
+
}
|
|
53141
|
+
return values;
|
|
53142
|
+
}
|
|
53143
|
+
getColumnsIndexes() {
|
|
53144
|
+
const indexes = new Set();
|
|
53145
|
+
for (let i = 0; i < this.data.columns.length; i++) {
|
|
53146
|
+
const exploded = [];
|
|
53147
|
+
for (let y = 0; y < this.data.columns[i].length; y++) {
|
|
53148
|
+
for (let x = 0; x < this.data.columns[i][y].span; x++) {
|
|
53149
|
+
exploded.push(this.data.columns[i][y]);
|
|
53150
|
+
}
|
|
53151
|
+
}
|
|
53152
|
+
for (let y = 0; y < exploded.length; y++) {
|
|
53153
|
+
if (exploded[y].isMissing) {
|
|
53154
|
+
indexes.add(y);
|
|
53155
|
+
}
|
|
53156
|
+
}
|
|
53157
|
+
}
|
|
53158
|
+
for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
|
|
53159
|
+
const values = this.data.values[i];
|
|
53160
|
+
if (values.find((x) => x.isMissing)) {
|
|
53161
|
+
indexes.add(i);
|
|
53162
|
+
}
|
|
53163
|
+
}
|
|
53164
|
+
return Array.from(indexes).sort((a, b) => a - b);
|
|
53165
|
+
}
|
|
53166
|
+
getRowsIndexes() {
|
|
53167
|
+
const rowIndexes = new Set();
|
|
53168
|
+
for (let i = 0; i < this.data.rows.length; i++) {
|
|
53169
|
+
if (this.data.rows[i].isMissing) {
|
|
53170
|
+
rowIndexes.add(i);
|
|
53171
|
+
}
|
|
53172
|
+
for (const col of this.data.values) {
|
|
53173
|
+
if (col[i].isMissing) {
|
|
53174
|
+
this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
|
|
53175
|
+
}
|
|
53176
|
+
}
|
|
53177
|
+
}
|
|
53178
|
+
return Array.from(rowIndexes).sort((a, b) => a - b);
|
|
53179
|
+
}
|
|
53180
|
+
// ---------------------------------------------------------------------
|
|
53181
|
+
// Data table creation
|
|
53182
|
+
// ---------------------------------------------------------------------
|
|
53183
|
+
_buildColHeaders(id, table) {
|
|
53184
|
+
const headers = [];
|
|
53185
|
+
for (const row of table.columns) {
|
|
53186
|
+
const current = [];
|
|
53187
|
+
for (const cell of row) {
|
|
53188
|
+
const args = [];
|
|
53189
|
+
for (let i = 0; i < cell.fields.length; i++) {
|
|
53190
|
+
args.push({ value: cell.fields[i] }, { value: cell.values[i] });
|
|
53191
|
+
}
|
|
53192
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53193
|
+
const locale = this.env.model.getters.getLocale();
|
|
53194
|
+
if (domain.at(-1)?.field === "measure") {
|
|
53195
|
+
const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
|
|
53196
|
+
current.push({
|
|
53197
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53198
|
+
value: formatValue(value, { format, locale }),
|
|
53199
|
+
span: cell.width,
|
|
53200
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53201
|
+
});
|
|
53202
|
+
}
|
|
53203
|
+
else {
|
|
53204
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53205
|
+
current.push({
|
|
53206
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53207
|
+
value: formatValue(value, { format, locale }),
|
|
53208
|
+
span: cell.width,
|
|
53209
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53210
|
+
});
|
|
53211
|
+
}
|
|
53212
|
+
}
|
|
53213
|
+
headers.push(current);
|
|
53214
|
+
}
|
|
53215
|
+
const last = headers[headers.length - 1];
|
|
53216
|
+
headers[headers.length - 1] = last.map((cell) => {
|
|
53217
|
+
if (!cell.isMissing) {
|
|
53218
|
+
cell.style = "color: #756f6f;";
|
|
53219
|
+
}
|
|
53220
|
+
return cell;
|
|
53221
|
+
});
|
|
53222
|
+
return headers;
|
|
53223
|
+
}
|
|
53224
|
+
_buildRowHeaders(id, table) {
|
|
53225
|
+
const headers = [];
|
|
53226
|
+
for (const row of table.rows) {
|
|
53227
|
+
const args = [];
|
|
53228
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53229
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53230
|
+
}
|
|
53231
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53232
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53233
|
+
const locale = this.env.model.getters.getLocale();
|
|
53234
|
+
const cell = {
|
|
53235
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53236
|
+
value: formatValue(value, { format, locale }),
|
|
53237
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53238
|
+
};
|
|
53239
|
+
if (row.indent > 1) {
|
|
53240
|
+
cell.style = `padding-left: ${row.indent - 1 * 10}px`;
|
|
53241
|
+
}
|
|
53242
|
+
headers.push(cell);
|
|
53243
|
+
}
|
|
53244
|
+
return headers;
|
|
53245
|
+
}
|
|
53246
|
+
_buildValues(id, table) {
|
|
53247
|
+
const values = [];
|
|
53248
|
+
for (const col of table.columns.at(-1) || []) {
|
|
53249
|
+
const current = [];
|
|
53250
|
+
const measure = toString(col.values[col.values.length - 1]);
|
|
53251
|
+
for (const row of table.rows) {
|
|
53252
|
+
const args = [];
|
|
53253
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53254
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53255
|
+
}
|
|
53256
|
+
for (let i = 0; i < col.fields.length - 1; i++) {
|
|
53257
|
+
args.push({ value: col.fields[i] }, { value: col.values[i] });
|
|
53258
|
+
}
|
|
53259
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53260
|
+
const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
|
|
53261
|
+
const locale = this.env.model.getters.getLocale();
|
|
53262
|
+
current.push({
|
|
53263
|
+
formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
|
|
53264
|
+
value: formatValue(value, { format, locale }),
|
|
53265
|
+
isMissing: !this.tracker?.isValuePresent(measure, domain),
|
|
53266
|
+
});
|
|
53267
|
+
}
|
|
53268
|
+
values.push(current);
|
|
53269
|
+
}
|
|
53270
|
+
return values;
|
|
53271
|
+
}
|
|
53272
|
+
}
|
|
53273
|
+
|
|
52884
53274
|
/**
|
|
52885
53275
|
* BasePlugin
|
|
52886
53276
|
*
|
|
@@ -56318,7 +56708,7 @@ class RangeAdapter {
|
|
|
56318
56708
|
if (range.sheetId === cmd.sheetId) {
|
|
56319
56709
|
return { changeType: "CHANGE", range };
|
|
56320
56710
|
}
|
|
56321
|
-
if (
|
|
56711
|
+
if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
|
|
56322
56712
|
const invalidSheetName = undefined;
|
|
56323
56713
|
const sheetId = cmd.sheetId;
|
|
56324
56714
|
const newRange = range.clone({ sheetId, invalidSheetName });
|
|
@@ -56903,7 +57293,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
56903
57293
|
if (name) {
|
|
56904
57294
|
const unquotedName = getUnquotedSheetName(name);
|
|
56905
57295
|
for (const key in this.sheetIdsMapName) {
|
|
56906
|
-
if (key
|
|
57296
|
+
if (isSheetNameEqual(key, unquotedName)) {
|
|
56907
57297
|
return this.sheetIdsMapName[key];
|
|
56908
57298
|
}
|
|
56909
57299
|
}
|
|
@@ -57151,7 +57541,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57151
57541
|
}
|
|
57152
57542
|
const { orderedSheetIds, sheets } = this;
|
|
57153
57543
|
const name = cmd.name && cmd.name.trim().toLowerCase();
|
|
57154
|
-
if (orderedSheetIds.find((id) => sheets[id]?.name
|
|
57544
|
+
if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
|
|
57155
57545
|
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
57156
57546
|
}
|
|
57157
57547
|
if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
|
|
@@ -60113,10 +60503,9 @@ class Evaluator {
|
|
|
60113
60503
|
return this.evaluatedCells.keysForSheet(sheetId);
|
|
60114
60504
|
}
|
|
60115
60505
|
getArrayFormulaSpreadingOn(position) {
|
|
60116
|
-
const
|
|
60117
|
-
|
|
60118
|
-
|
|
60119
|
-
return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
|
|
60506
|
+
const isEmpty = this.getEvaluatedCell(position).type === CellValueType.empty;
|
|
60507
|
+
if (isEmpty) {
|
|
60508
|
+
return undefined;
|
|
60120
60509
|
}
|
|
60121
60510
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
60122
60511
|
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
@@ -66054,6 +66443,55 @@ class HistoryPlugin extends UIPlugin {
|
|
|
66054
66443
|
}
|
|
66055
66444
|
}
|
|
66056
66445
|
|
|
66446
|
+
class PivotPresenceTracker {
|
|
66447
|
+
trackedValues = new Set();
|
|
66448
|
+
domainToArray(domain) {
|
|
66449
|
+
return domain.flatMap((node) => [node.field, toString(node.value)]);
|
|
66450
|
+
}
|
|
66451
|
+
isValuePresent(measure, domain) {
|
|
66452
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66453
|
+
return this.trackedValues.has(key);
|
|
66454
|
+
}
|
|
66455
|
+
isHeaderPresent(domain) {
|
|
66456
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66457
|
+
return this.trackedValues.has(key);
|
|
66458
|
+
}
|
|
66459
|
+
trackValue(measure, domain) {
|
|
66460
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66461
|
+
this.trackedValues.add(key);
|
|
66462
|
+
}
|
|
66463
|
+
trackHeader(domain) {
|
|
66464
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66465
|
+
this.trackedValues.add(key);
|
|
66466
|
+
}
|
|
66467
|
+
}
|
|
66468
|
+
|
|
66469
|
+
class PivotPresencePlugin extends UIPlugin {
|
|
66470
|
+
static getters = ["getPivotPresenceTracker"];
|
|
66471
|
+
trackPresencePivotId;
|
|
66472
|
+
tracker;
|
|
66473
|
+
handle(cmd) {
|
|
66474
|
+
switch (cmd.type) {
|
|
66475
|
+
case "PIVOT_START_PRESENCE_TRACKING":
|
|
66476
|
+
this.tracker = new PivotPresenceTracker();
|
|
66477
|
+
this.trackPresencePivotId = cmd.pivotId;
|
|
66478
|
+
break;
|
|
66479
|
+
case "PIVOT_STOP_PRESENCE_TRACKING":
|
|
66480
|
+
this.trackPresencePivotId = undefined;
|
|
66481
|
+
break;
|
|
66482
|
+
}
|
|
66483
|
+
}
|
|
66484
|
+
getPivotPresenceTracker(pivotId) {
|
|
66485
|
+
if (this.trackPresencePivotId !== pivotId) {
|
|
66486
|
+
return undefined;
|
|
66487
|
+
}
|
|
66488
|
+
if (!this.tracker) {
|
|
66489
|
+
throw new Error("Tracker not initialized");
|
|
66490
|
+
}
|
|
66491
|
+
return this.tracker;
|
|
66492
|
+
}
|
|
66493
|
+
}
|
|
66494
|
+
|
|
66057
66495
|
class SplitToColumnsPlugin extends UIPlugin {
|
|
66058
66496
|
static getters = ["getAutomaticSeparator"];
|
|
66059
66497
|
allowDispatch(cmd) {
|
|
@@ -68841,6 +69279,7 @@ const featurePluginRegistry = new Registry()
|
|
|
68841
69279
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
68842
69280
|
.add("format", FormatPlugin)
|
|
68843
69281
|
.add("insert_pivot", InsertPivotPlugin)
|
|
69282
|
+
.add("pivot_presence", PivotPresencePlugin)
|
|
68844
69283
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
68845
69284
|
.add("collaborative", CollaborativePlugin)
|
|
68846
69285
|
.add("history", HistoryPlugin)
|
|
@@ -69221,11 +69660,11 @@ class BottomBarSheet extends owl.Component {
|
|
|
69221
69660
|
if (ev.key === "Enter") {
|
|
69222
69661
|
ev.preventDefault();
|
|
69223
69662
|
this.stopEdition();
|
|
69224
|
-
this.DOMFocusableElementStore.
|
|
69663
|
+
this.DOMFocusableElementStore.focus();
|
|
69225
69664
|
}
|
|
69226
69665
|
if (ev.key === "Escape") {
|
|
69227
69666
|
this.cancelEdition();
|
|
69228
|
-
this.DOMFocusableElementStore.
|
|
69667
|
+
this.DOMFocusableElementStore.focus();
|
|
69229
69668
|
}
|
|
69230
69669
|
}
|
|
69231
69670
|
onMouseEventSheetName(ev) {
|
|
@@ -75816,6 +76255,7 @@ const components = {
|
|
|
75816
76255
|
PivotDimensionOrder,
|
|
75817
76256
|
PivotDimension,
|
|
75818
76257
|
PivotLayoutConfigurator,
|
|
76258
|
+
PivotHTMLRenderer,
|
|
75819
76259
|
PivotDeferUpdate,
|
|
75820
76260
|
PivotTitleSection,
|
|
75821
76261
|
CogWheelMenu,
|
|
@@ -75909,6 +76349,6 @@ exports.tokenColors = tokenColors;
|
|
|
75909
76349
|
exports.tokenize = tokenize;
|
|
75910
76350
|
|
|
75911
76351
|
|
|
75912
|
-
__info__.version = "18.1.
|
|
75913
|
-
__info__.date = "2025-05-
|
|
75914
|
-
__info__.hash = "
|
|
76352
|
+
__info__.version = "18.1.21";
|
|
76353
|
+
__info__.date = "2025-05-20T05:54:45.398Z";
|
|
76354
|
+
__info__.hash = "89ed6a9";
|