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