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