@odoo/o-spreadsheet 18.1.19 → 18.1.20

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.20
6
+ * @date 2025-05-13T17:52:28.174Z
7
+ * @hash 3e43a46
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';
@@ -3346,7 +3346,7 @@ function isDateAfter(date, dateAfter) {
3346
3346
  */
3347
3347
  const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
3348
3348
  decimalSeparator = escapeRegExp(decimalSeparator);
3349
- return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3349
+ return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3350
3350
  });
3351
3351
  const getNumberRegex = memoize(function getNumberRegex(locale) {
3352
3352
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
@@ -6290,6 +6290,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
6290
6290
  }
6291
6291
  return name;
6292
6292
  }
6293
+ function isSheetNameEqual(name1, name2) {
6294
+ if (name1 === undefined || name2 === undefined) {
6295
+ return false;
6296
+ }
6297
+ return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
6298
+ getUnquotedSheetName(name2.trim().toUpperCase()));
6299
+ }
6293
6300
 
6294
6301
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
6295
6302
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
@@ -8153,10 +8160,9 @@ const AGGREGATOR_NAMES = {
8153
8160
  avg: _t("Average"),
8154
8161
  sum: _t("Sum"),
8155
8162
  };
8156
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8157
8163
  const AGGREGATORS_BY_FIELD_TYPE = {
8158
- integer: NUMBER_CHAR_AGGREGATORS,
8159
- char: NUMBER_CHAR_AGGREGATORS,
8164
+ integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
8165
+ char: ["count_distinct", "count"],
8160
8166
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8161
8167
  };
8162
8168
  const AGGREGATORS = {};
@@ -9508,7 +9514,10 @@ function proxifyStoreMutation(store, callback) {
9508
9514
  const functionProxy = new Proxy(value, {
9509
9515
  // trap the function call
9510
9516
  apply(target, thisArg, argArray) {
9511
- Reflect.apply(target, thisStore, argArray);
9517
+ const res = Reflect.apply(target, thisStore, argArray);
9518
+ if (res === "noStateChange") {
9519
+ return;
9520
+ }
9512
9521
  callback();
9513
9522
  },
9514
9523
  });
@@ -9530,7 +9539,7 @@ function getDependencyContainer(env) {
9530
9539
  const ModelStore = createAbstractStore("Model");
9531
9540
 
9532
9541
  class RendererStore {
9533
- mutators = ["register", "unRegister"];
9542
+ mutators = ["register", "unRegister", "drawLayer"];
9534
9543
  renderers = {};
9535
9544
  register(renderer) {
9536
9545
  if (!renderer.renderingLayers.length) {
@@ -9550,14 +9559,14 @@ class RendererStore {
9550
9559
  }
9551
9560
  drawLayer(context, layer) {
9552
9561
  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();
9562
+ if (renderers) {
9563
+ for (const renderer of renderers) {
9564
+ context.ctx.save();
9565
+ renderer.drawLayer(context, layer);
9566
+ context.ctx.restore();
9567
+ }
9560
9568
  }
9569
+ return "noStateChange";
9561
9570
  }
9562
9571
  }
9563
9572
 
@@ -9610,16 +9619,17 @@ class ComposerFocusStore extends SpreadsheetStore {
9610
9619
  focusComposer(listener, args) {
9611
9620
  this.activeComposer = listener;
9612
9621
  if (this.getters.isReadonly()) {
9613
- return;
9622
+ return "noStateChange";
9614
9623
  }
9615
9624
  this._focusMode = args.focusMode || "contentFocus";
9616
9625
  if (this._focusMode !== "inactive") {
9617
9626
  this.setComposerContent(args);
9618
9627
  }
9628
+ return;
9619
9629
  }
9620
9630
  focusActiveComposer(args) {
9621
9631
  if (this.getters.isReadonly()) {
9622
- return;
9632
+ return "noStateChange";
9623
9633
  }
9624
9634
  if (!this.activeComposer) {
9625
9635
  throw new Error("No composer is registered");
@@ -9628,6 +9638,7 @@ class ComposerFocusStore extends SpreadsheetStore {
9628
9638
  if (this._focusMode !== "inactive") {
9629
9639
  this.setComposerContent(args);
9630
9640
  }
9641
+ return;
9631
9642
  }
9632
9643
  /**
9633
9644
  * Start the edition or update the content if it's already started.
@@ -9977,7 +9988,7 @@ function getDefinedAxis(definition) {
9977
9988
  }
9978
9989
  function formatChartDatasetValue(axisFormats, locale) {
9979
9990
  return (value, axisId) => {
9980
- const format = axisId ? axisFormats?.[axisId] : undefined;
9991
+ const format = axisFormats?.[axisId];
9981
9992
  return formatTickValue({ format, locale })(value);
9982
9993
  };
9983
9994
  }
@@ -10141,7 +10152,7 @@ function drawPieChartValues(chart, options, ctx) {
10141
10152
  const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10142
10153
  ctx.fillStyle = chartFontColor(options.background);
10143
10154
  ctx.strokeStyle = options.background || "#ffffff";
10144
- const displayValue = options.callback(value);
10155
+ const displayValue = options.callback(value, "y");
10145
10156
  drawTextWithBackground(displayValue, x, y, ctx);
10146
10157
  }
10147
10158
  }
@@ -19031,6 +19042,9 @@ const PIVOT_VALUE = {
19031
19042
  };
19032
19043
  }
19033
19044
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19045
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19046
+ this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
19047
+ }
19034
19048
  return pivot.getPivotCellValueAndFormat(_measure, domain);
19035
19049
  },
19036
19050
  };
@@ -19062,6 +19076,9 @@ const PIVOT_HEADER = {
19062
19076
  };
19063
19077
  }
19064
19078
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19079
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19080
+ this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
19081
+ }
19065
19082
  const lastNode = domain.at(-1);
19066
19083
  if (lastNode?.field === "measure") {
19067
19084
  return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
@@ -19284,6 +19301,9 @@ function isEmpty(data) {
19284
19301
  return data === undefined || data.value === null;
19285
19302
  }
19286
19303
  const getNeutral = { number: 0, string: "", boolean: false };
19304
+ function areAlmostEqual(value1, value2, epsilon = 2e-16) {
19305
+ return Math.abs(value1 - value2) < epsilon;
19306
+ }
19287
19307
  const EQ = {
19288
19308
  description: _t("Equal."),
19289
19309
  args: [
@@ -19305,6 +19325,9 @@ const EQ = {
19305
19325
  if (typeof _value2 === "string") {
19306
19326
  _value2 = _value2.toUpperCase();
19307
19327
  }
19328
+ if (typeof _value1 === "number" && typeof _value2 === "number") {
19329
+ return { value: areAlmostEqual(_value1, _value2) };
19330
+ }
19308
19331
  return { value: _value1 === _value2 };
19309
19332
  },
19310
19333
  };
@@ -19344,6 +19367,9 @@ const GT = {
19344
19367
  ],
19345
19368
  compute: function (value1, value2) {
19346
19369
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19370
+ if (typeof v1 === "number" && typeof v2 === "number") {
19371
+ return !areAlmostEqual(v1, v2) && v1 > v2;
19372
+ }
19347
19373
  return v1 > v2;
19348
19374
  });
19349
19375
  },
@@ -19359,6 +19385,9 @@ const GTE = {
19359
19385
  ],
19360
19386
  compute: function (value1, value2) {
19361
19387
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19388
+ if (typeof v1 === "number" && typeof v2 === "number") {
19389
+ return areAlmostEqual(v1, v2) || v1 > v2;
19390
+ }
19362
19391
  return v1 >= v2;
19363
19392
  });
19364
19393
  },
@@ -20965,7 +20994,7 @@ class AbstractComposerStore extends SpreadsheetStore {
20965
20994
  .find((token) => {
20966
20995
  const { xc, sheetName: sheet } = splitReference(token.value);
20967
20996
  const sheetName = sheet || this.getters.getSheetName(this.sheetId);
20968
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
20997
+ if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
20969
20998
  return false;
20970
20999
  }
20971
21000
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
@@ -24576,7 +24605,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
24576
24605
  ({ xc, sheetName } = splitReference(reference));
24577
24606
  let rangeSheetIndex;
24578
24607
  if (sheetName) {
24579
- const index = data.sheets.findIndex((sheet) => sheet.name === sheetName);
24608
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
24580
24609
  if (index < 0) {
24581
24610
  throw new Error("Unable to find a sheet with the name " + sheetName);
24582
24611
  }
@@ -24917,7 +24946,7 @@ function convertFormula(formula, data) {
24917
24946
  formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
24918
24947
  externalRefId = Number(externalRefId) - 1;
24919
24948
  cellRef = cellRef.replace(/\$/g, "");
24920
- const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name === sheetName);
24949
+ const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
24921
24950
  if (sheetIndex === -1) {
24922
24951
  return match;
24923
24952
  }
@@ -25568,7 +25597,7 @@ function convertPivotTableConfig(pivotTable) {
25568
25597
  */
25569
25598
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25570
25599
  for (let tableSheet of convertedSheets) {
25571
- const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
25600
+ const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
25572
25601
  for (let table of tables) {
25573
25602
  const tabRef = table.name + "[";
25574
25603
  for (let sheet of convertedSheets) {
@@ -32403,12 +32432,20 @@ class HoveredCellStore extends SpreadsheetStore {
32403
32432
  }
32404
32433
  }
32405
32434
  hover(position) {
32435
+ if (position.col === this.col && position.row === this.row) {
32436
+ return "noStateChange";
32437
+ }
32406
32438
  this.col = position.col;
32407
32439
  this.row = position.row;
32440
+ return;
32408
32441
  }
32409
32442
  clear() {
32443
+ if (this.col === undefined && this.row === undefined) {
32444
+ return "noStateChange";
32445
+ }
32410
32446
  this.col = undefined;
32411
32447
  this.row = undefined;
32448
+ return;
32412
32449
  }
32413
32450
  }
32414
32451
 
@@ -32430,7 +32467,11 @@ class CellPopoverStore extends SpreadsheetStore {
32430
32467
  this.persistentPopover = { col, row, sheetId, type };
32431
32468
  }
32432
32469
  close() {
32470
+ if (!this.persistentPopover) {
32471
+ return "noStateChange";
32472
+ }
32433
32473
  this.persistentPopover = undefined;
32474
+ return;
32434
32475
  }
32435
32476
  get persistentCellPopover() {
32436
32477
  return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
@@ -40262,10 +40303,18 @@ class ChartPanel extends Component {
40262
40303
  }
40263
40304
 
40264
40305
  class DOMFocusableElementStore {
40265
- mutators = ["setFocusableElement"];
40306
+ mutators = ["setFocusableElement", "focus"];
40266
40307
  focusableElement = undefined;
40267
40308
  setFocusableElement(element) {
40268
40309
  this.focusableElement = element;
40310
+ return "noStateChange";
40311
+ }
40312
+ focus() {
40313
+ if (this.focusableElement === document.activeElement) {
40314
+ return "noStateChange";
40315
+ }
40316
+ this.focusableElement?.focus();
40317
+ return;
40269
40318
  }
40270
40319
  }
40271
40320
 
@@ -40855,7 +40904,7 @@ class Composer extends Component {
40855
40904
  if (document.activeElement === this.contentHelper.el &&
40856
40905
  this.props.composerStore.editionMode === "inactive" &&
40857
40906
  !this.props.isDefaultFocus) {
40858
- this.DOMFocusableElementStore.focusableElement?.focus();
40907
+ this.DOMFocusableElementStore.focus();
40859
40908
  }
40860
40909
  });
40861
40910
  useEffect(() => {
@@ -46602,7 +46651,12 @@ class SpreadsheetPivot {
46602
46651
  entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
46603
46652
  }
46604
46653
  else {
46605
- entry[field.name] = cell;
46654
+ if (field.type === "char") {
46655
+ entry[field.name] = { ...cell, value: cell.formattedValue || null };
46656
+ }
46657
+ else {
46658
+ entry[field.name] = cell;
46659
+ }
46606
46660
  }
46607
46661
  }
46608
46662
  entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
@@ -51631,10 +51685,6 @@ function useGridDrawing(refName, model, canvasSize) {
51631
51685
  ctx.scale(dpr, dpr);
51632
51686
  for (const layer of OrderedLayers()) {
51633
51687
  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
51688
  rendererStore.drawLayer(renderingContext, layer);
51639
51689
  }
51640
51690
  }
@@ -52324,7 +52374,7 @@ class Grid extends Component {
52324
52374
  this.cellPopovers = useStore(CellPopoverStore);
52325
52375
  useEffect(() => {
52326
52376
  if (!this.sidePanel.isOpen) {
52327
- this.DOMFocusableElementStore.focusableElement?.focus();
52377
+ this.DOMFocusableElementStore.focus();
52328
52378
  }
52329
52379
  }, () => [this.sidePanel.isOpen]);
52330
52380
  useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
@@ -52534,7 +52584,7 @@ class Grid extends Component {
52534
52584
  focusDefaultElement() {
52535
52585
  if (!this.env.model.getters.getSelectedFigureId() &&
52536
52586
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
52537
- this.DOMFocusableElementStore.focusableElement?.focus();
52587
+ this.DOMFocusableElementStore.focus();
52538
52588
  }
52539
52589
  }
52540
52590
  get gridEl() {
@@ -52879,6 +52929,322 @@ class Grid extends Component {
52879
52929
  }
52880
52930
  }
52881
52931
 
52932
+ css /* scss */ `
52933
+ .o_pivot_html_renderer {
52934
+ width: 100%;
52935
+ border-collapse: collapse;
52936
+
52937
+ &:hover {
52938
+ cursor: pointer;
52939
+ }
52940
+
52941
+ td,
52942
+ th {
52943
+ border: 1px solid #dee2e6;
52944
+ background-color: #fff;
52945
+ padding: 0.3rem;
52946
+ white-space: nowrap;
52947
+
52948
+ &:hover {
52949
+ filter: brightness(0.9);
52950
+ }
52951
+ }
52952
+
52953
+ td {
52954
+ text-align: right;
52955
+ }
52956
+
52957
+ th {
52958
+ background-color: #f5f5f5;
52959
+ font-weight: bold;
52960
+ color: black;
52961
+ }
52962
+
52963
+ .o_missing_value {
52964
+ color: #46646d;
52965
+ background: #e7f2f6;
52966
+ }
52967
+ }
52968
+ `;
52969
+ class PivotHTMLRenderer extends Component {
52970
+ static template = "o_spreadsheet.PivotHTMLRenderer";
52971
+ static components = { Checkbox };
52972
+ static props = {
52973
+ pivotId: String,
52974
+ onCellClicked: Function,
52975
+ };
52976
+ pivot = this.env.model.getters.getPivot(this.props.pivotId);
52977
+ data = {
52978
+ columns: [],
52979
+ rows: [],
52980
+ values: [],
52981
+ };
52982
+ state = useState({
52983
+ showMissingValuesOnly: false,
52984
+ });
52985
+ setup() {
52986
+ const table = this.pivot.getTableStructure();
52987
+ const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
52988
+ this.data = {
52989
+ columns: this._buildColHeaders(formulaId, table),
52990
+ rows: this._buildRowHeaders(formulaId, table),
52991
+ values: this._buildValues(formulaId, table),
52992
+ };
52993
+ }
52994
+ get tracker() {
52995
+ return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
52996
+ }
52997
+ // ---------------------------------------------------------------------
52998
+ // Missing values building
52999
+ // ---------------------------------------------------------------------
53000
+ /**
53001
+ * Retrieve the data to display in the Pivot Table
53002
+ * In the case when showMissingValuesOnly is false, the returned value
53003
+ * is the complete data
53004
+ * In the case when showMissingValuesOnly is true, the returned value is
53005
+ * the data which contains only missing values in the rows and cols. In
53006
+ * the rows, we also return the parent rows of rows which contains missing
53007
+ * values, to give context to the user.
53008
+ *
53009
+ */
53010
+ getTableData() {
53011
+ if (!this.state.showMissingValuesOnly) {
53012
+ return this.data;
53013
+ }
53014
+ const colIndexes = this.getColumnsIndexes();
53015
+ const rowIndexes = this.getRowsIndexes();
53016
+ const columns = this.buildColumnsMissing(colIndexes);
53017
+ const rows = this.buildRowsMissing(rowIndexes);
53018
+ const values = this.buildValuesMissing(colIndexes, rowIndexes);
53019
+ return { columns, rows, values };
53020
+ }
53021
+ /**
53022
+ * Retrieve the parents of the given row
53023
+ * ex:
53024
+ * Australia
53025
+ * January
53026
+ * February
53027
+ * The parent of "January" is "Australia"
53028
+ */
53029
+ addRecursiveRow(index) {
53030
+ const rows = this.pivot.getTableStructure().rows;
53031
+ const row = [...rows[index].values];
53032
+ if (row.length <= 1) {
53033
+ return [index];
53034
+ }
53035
+ row.pop();
53036
+ const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
53037
+ return [index].concat(this.addRecursiveRow(parentRowIndex));
53038
+ }
53039
+ /**
53040
+ * Create the columns to be used, based on the indexes of the columns in
53041
+ * which a missing value is present
53042
+ *
53043
+ */
53044
+ buildColumnsMissing(indexes) {
53045
+ // columnsMap explode the columns in an array of array of the same
53046
+ // size with the index of each column, repeated 'span' times.
53047
+ // ex:
53048
+ // | A | B |
53049
+ // | 1 | 2 | 3 |
53050
+ // => [
53051
+ // [0, 0, 1]
53052
+ // [0, 1, 2]
53053
+ // ]
53054
+ const columnsMap = [];
53055
+ for (const column of this.data.columns) {
53056
+ const columnMap = [];
53057
+ for (const index in column) {
53058
+ for (let i = 0; i < column[index].span; i++) {
53059
+ columnMap.push(parseInt(index, 10));
53060
+ }
53061
+ }
53062
+ columnsMap.push(columnMap);
53063
+ }
53064
+ // Remove the columns that are not present in indexes
53065
+ for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
53066
+ if (!indexes.includes(i)) {
53067
+ for (const columnMap of columnsMap) {
53068
+ columnMap.splice(i, 1);
53069
+ }
53070
+ }
53071
+ }
53072
+ // Build the columns
53073
+ const columns = [];
53074
+ for (const mapIndex in columnsMap) {
53075
+ const column = [];
53076
+ let index = undefined;
53077
+ let span = 1;
53078
+ for (let i = 0; i < columnsMap[mapIndex].length; i++) {
53079
+ if (index !== columnsMap[mapIndex][i]) {
53080
+ if (index !== undefined) {
53081
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53082
+ }
53083
+ index = columnsMap[mapIndex][i];
53084
+ span = 1;
53085
+ }
53086
+ else {
53087
+ span++;
53088
+ }
53089
+ }
53090
+ if (index !== undefined) {
53091
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53092
+ }
53093
+ columns.push(column);
53094
+ }
53095
+ return columns;
53096
+ }
53097
+ /**
53098
+ * Create the rows to be used, based on the indexes of the rows in
53099
+ * which a missing value is present.
53100
+ */
53101
+ buildRowsMissing(indexes) {
53102
+ return indexes.map((index) => this.data.rows[index]);
53103
+ }
53104
+ /**
53105
+ * Create the value to be used, based on the indexes of the columns and
53106
+ * rows in which a missing value is present.
53107
+ */
53108
+ buildValuesMissing(colIndexes, rowIndexes) {
53109
+ const values = colIndexes.map(() => []);
53110
+ for (const row of rowIndexes) {
53111
+ for (const col in colIndexes) {
53112
+ values[col].push(this.data.values[colIndexes[col]][row]);
53113
+ }
53114
+ }
53115
+ return values;
53116
+ }
53117
+ getColumnsIndexes() {
53118
+ const indexes = new Set();
53119
+ for (let i = 0; i < this.data.columns.length; i++) {
53120
+ const exploded = [];
53121
+ for (let y = 0; y < this.data.columns[i].length; y++) {
53122
+ for (let x = 0; x < this.data.columns[i][y].span; x++) {
53123
+ exploded.push(this.data.columns[i][y]);
53124
+ }
53125
+ }
53126
+ for (let y = 0; y < exploded.length; y++) {
53127
+ if (exploded[y].isMissing) {
53128
+ indexes.add(y);
53129
+ }
53130
+ }
53131
+ }
53132
+ for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
53133
+ const values = this.data.values[i];
53134
+ if (values.find((x) => x.isMissing)) {
53135
+ indexes.add(i);
53136
+ }
53137
+ }
53138
+ return Array.from(indexes).sort((a, b) => a - b);
53139
+ }
53140
+ getRowsIndexes() {
53141
+ const rowIndexes = new Set();
53142
+ for (let i = 0; i < this.data.rows.length; i++) {
53143
+ if (this.data.rows[i].isMissing) {
53144
+ rowIndexes.add(i);
53145
+ }
53146
+ for (const col of this.data.values) {
53147
+ if (col[i].isMissing) {
53148
+ this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
53149
+ }
53150
+ }
53151
+ }
53152
+ return Array.from(rowIndexes).sort((a, b) => a - b);
53153
+ }
53154
+ // ---------------------------------------------------------------------
53155
+ // Data table creation
53156
+ // ---------------------------------------------------------------------
53157
+ _buildColHeaders(id, table) {
53158
+ const headers = [];
53159
+ for (const row of table.columns) {
53160
+ const current = [];
53161
+ for (const cell of row) {
53162
+ const args = [];
53163
+ for (let i = 0; i < cell.fields.length; i++) {
53164
+ args.push({ value: cell.fields[i] }, { value: cell.values[i] });
53165
+ }
53166
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53167
+ const locale = this.env.model.getters.getLocale();
53168
+ if (domain.at(-1)?.field === "measure") {
53169
+ const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
53170
+ current.push({
53171
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53172
+ value: formatValue(value, { format, locale }),
53173
+ span: cell.width,
53174
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53175
+ });
53176
+ }
53177
+ else {
53178
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53179
+ current.push({
53180
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53181
+ value: formatValue(value, { format, locale }),
53182
+ span: cell.width,
53183
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53184
+ });
53185
+ }
53186
+ }
53187
+ headers.push(current);
53188
+ }
53189
+ const last = headers[headers.length - 1];
53190
+ headers[headers.length - 1] = last.map((cell) => {
53191
+ if (!cell.isMissing) {
53192
+ cell.style = "color: #756f6f;";
53193
+ }
53194
+ return cell;
53195
+ });
53196
+ return headers;
53197
+ }
53198
+ _buildRowHeaders(id, table) {
53199
+ const headers = [];
53200
+ for (const row of table.rows) {
53201
+ const args = [];
53202
+ for (let i = 0; i < row.fields.length; i++) {
53203
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53204
+ }
53205
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53206
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53207
+ const locale = this.env.model.getters.getLocale();
53208
+ const cell = {
53209
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53210
+ value: formatValue(value, { format, locale }),
53211
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53212
+ };
53213
+ if (row.indent > 1) {
53214
+ cell.style = `padding-left: ${row.indent - 1 * 10}px`;
53215
+ }
53216
+ headers.push(cell);
53217
+ }
53218
+ return headers;
53219
+ }
53220
+ _buildValues(id, table) {
53221
+ const values = [];
53222
+ for (const col of table.columns.at(-1) || []) {
53223
+ const current = [];
53224
+ const measure = toString(col.values[col.values.length - 1]);
53225
+ for (const row of table.rows) {
53226
+ const args = [];
53227
+ for (let i = 0; i < row.fields.length; i++) {
53228
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53229
+ }
53230
+ for (let i = 0; i < col.fields.length - 1; i++) {
53231
+ args.push({ value: col.fields[i] }, { value: col.values[i] });
53232
+ }
53233
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53234
+ const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
53235
+ const locale = this.env.model.getters.getLocale();
53236
+ current.push({
53237
+ formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
53238
+ value: formatValue(value, { format, locale }),
53239
+ isMissing: !this.tracker?.isValuePresent(measure, domain),
53240
+ });
53241
+ }
53242
+ values.push(current);
53243
+ }
53244
+ return values;
53245
+ }
53246
+ }
53247
+
52882
53248
  /**
52883
53249
  * BasePlugin
52884
53250
  *
@@ -56316,7 +56682,7 @@ class RangeAdapter {
56316
56682
  if (range.sheetId === cmd.sheetId) {
56317
56683
  return { changeType: "CHANGE", range };
56318
56684
  }
56319
- if (cmd.name && range.invalidSheetName === cmd.name) {
56685
+ if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
56320
56686
  const invalidSheetName = undefined;
56321
56687
  const sheetId = cmd.sheetId;
56322
56688
  const newRange = range.clone({ sheetId, invalidSheetName });
@@ -56901,7 +57267,7 @@ class SheetPlugin extends CorePlugin {
56901
57267
  if (name) {
56902
57268
  const unquotedName = getUnquotedSheetName(name);
56903
57269
  for (const key in this.sheetIdsMapName) {
56904
- if (key.toUpperCase() === unquotedName.toUpperCase()) {
57270
+ if (isSheetNameEqual(key, unquotedName)) {
56905
57271
  return this.sheetIdsMapName[key];
56906
57272
  }
56907
57273
  }
@@ -57149,7 +57515,7 @@ class SheetPlugin extends CorePlugin {
57149
57515
  }
57150
57516
  const { orderedSheetIds, sheets } = this;
57151
57517
  const name = cmd.name && cmd.name.trim().toLowerCase();
57152
- if (orderedSheetIds.find((id) => sheets[id]?.name.toLowerCase() === name && id !== cmd.sheetId)) {
57518
+ if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
57153
57519
  return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
57154
57520
  }
57155
57521
  if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
@@ -66052,6 +66418,55 @@ class HistoryPlugin extends UIPlugin {
66052
66418
  }
66053
66419
  }
66054
66420
 
66421
+ class PivotPresenceTracker {
66422
+ trackedValues = new Set();
66423
+ domainToArray(domain) {
66424
+ return domain.flatMap((node) => [node.field, toString(node.value)]);
66425
+ }
66426
+ isValuePresent(measure, domain) {
66427
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66428
+ return this.trackedValues.has(key);
66429
+ }
66430
+ isHeaderPresent(domain) {
66431
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66432
+ return this.trackedValues.has(key);
66433
+ }
66434
+ trackValue(measure, domain) {
66435
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66436
+ this.trackedValues.add(key);
66437
+ }
66438
+ trackHeader(domain) {
66439
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66440
+ this.trackedValues.add(key);
66441
+ }
66442
+ }
66443
+
66444
+ class PivotPresencePlugin extends UIPlugin {
66445
+ static getters = ["getPivotPresenceTracker"];
66446
+ trackPresencePivotId;
66447
+ tracker;
66448
+ handle(cmd) {
66449
+ switch (cmd.type) {
66450
+ case "PIVOT_START_PRESENCE_TRACKING":
66451
+ this.tracker = new PivotPresenceTracker();
66452
+ this.trackPresencePivotId = cmd.pivotId;
66453
+ break;
66454
+ case "PIVOT_STOP_PRESENCE_TRACKING":
66455
+ this.trackPresencePivotId = undefined;
66456
+ break;
66457
+ }
66458
+ }
66459
+ getPivotPresenceTracker(pivotId) {
66460
+ if (this.trackPresencePivotId !== pivotId) {
66461
+ return undefined;
66462
+ }
66463
+ if (!this.tracker) {
66464
+ throw new Error("Tracker not initialized");
66465
+ }
66466
+ return this.tracker;
66467
+ }
66468
+ }
66469
+
66055
66470
  class SplitToColumnsPlugin extends UIPlugin {
66056
66471
  static getters = ["getAutomaticSeparator"];
66057
66472
  allowDispatch(cmd) {
@@ -68839,6 +69254,7 @@ const featurePluginRegistry = new Registry()
68839
69254
  .add("automatic_sum", AutomaticSumPlugin)
68840
69255
  .add("format", FormatPlugin)
68841
69256
  .add("insert_pivot", InsertPivotPlugin)
69257
+ .add("pivot_presence", PivotPresencePlugin)
68842
69258
  .add("split_to_columns", SplitToColumnsPlugin)
68843
69259
  .add("collaborative", CollaborativePlugin)
68844
69260
  .add("history", HistoryPlugin)
@@ -69219,11 +69635,11 @@ class BottomBarSheet extends Component {
69219
69635
  if (ev.key === "Enter") {
69220
69636
  ev.preventDefault();
69221
69637
  this.stopEdition();
69222
- this.DOMFocusableElementStore.focusableElement?.focus();
69638
+ this.DOMFocusableElementStore.focus();
69223
69639
  }
69224
69640
  if (ev.key === "Escape") {
69225
69641
  this.cancelEdition();
69226
- this.DOMFocusableElementStore.focusableElement?.focus();
69642
+ this.DOMFocusableElementStore.focus();
69227
69643
  }
69228
69644
  }
69229
69645
  onMouseEventSheetName(ev) {
@@ -75814,6 +76230,7 @@ const components = {
75814
76230
  PivotDimensionOrder,
75815
76231
  PivotDimension,
75816
76232
  PivotLayoutConfigurator,
76233
+ PivotHTMLRenderer,
75817
76234
  PivotDeferUpdate,
75818
76235
  PivotTitleSection,
75819
76236
  CogWheelMenu,
@@ -75863,6 +76280,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75863
76280
  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
76281
 
75865
76282
 
75866
- __info__.version = "18.1.19";
75867
- __info__.date = "2025-05-12T05:26:05.861Z";
75868
- __info__.hash = "44cc170";
76283
+ __info__.version = "18.1.20";
76284
+ __info__.date = "2025-05-13T17:52:28.174Z";
76285
+ __info__.hash = "3e43a46";