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