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