@odoo/o-spreadsheet 18.1.19 → 18.1.21

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