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