@odoo/o-spreadsheet 18.2.11 → 18.2.13

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