@odoo/o-spreadsheet 18.2.11 → 18.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.2.11
6
- * @date 2025-05-12T05:25:59.138Z
7
- * @hash eb87dca
5
+ * @version 18.2.12
6
+ * @date 2025-05-13T17:52:23.989Z
7
+ * @hash ba2ba9b
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -3358,7 +3358,7 @@
3358
3358
  */
3359
3359
  const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
3360
3360
  decimalSeparator = escapeRegExp(decimalSeparator);
3361
- return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3361
+ return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3362
3362
  });
3363
3363
  const getNumberRegex = memoize(function getNumberRegex(locale) {
3364
3364
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
@@ -6300,6 +6300,13 @@
6300
6300
  }
6301
6301
  return name;
6302
6302
  }
6303
+ function isSheetNameEqual(name1, name2) {
6304
+ if (name1 === undefined || name2 === undefined) {
6305
+ return false;
6306
+ }
6307
+ return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
6308
+ getUnquotedSheetName(name2.trim().toUpperCase()));
6309
+ }
6303
6310
 
6304
6311
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
6305
6312
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
@@ -8163,10 +8170,9 @@
8163
8170
  avg: _t("Average"),
8164
8171
  sum: _t("Sum"),
8165
8172
  };
8166
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8167
8173
  const AGGREGATORS_BY_FIELD_TYPE = {
8168
- integer: NUMBER_CHAR_AGGREGATORS,
8169
- char: NUMBER_CHAR_AGGREGATORS,
8174
+ integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
8175
+ char: ["count_distinct", "count"],
8170
8176
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8171
8177
  datetime: ["max", "min", "count_distinct", "count"],
8172
8178
  };
@@ -9519,7 +9525,10 @@ stores.inject(MyMetaStore, storeInstance);
9519
9525
  const functionProxy = new Proxy(value, {
9520
9526
  // trap the function call
9521
9527
  apply(target, thisArg, argArray) {
9522
- Reflect.apply(target, thisStore, argArray);
9528
+ const res = Reflect.apply(target, thisStore, argArray);
9529
+ if (res === "noStateChange") {
9530
+ return;
9531
+ }
9523
9532
  callback();
9524
9533
  },
9525
9534
  });
@@ -9541,7 +9550,7 @@ stores.inject(MyMetaStore, storeInstance);
9541
9550
  const ModelStore = createAbstractStore("Model");
9542
9551
 
9543
9552
  class RendererStore {
9544
- mutators = ["register", "unRegister"];
9553
+ mutators = ["register", "unRegister", "drawLayer"];
9545
9554
  renderers = {};
9546
9555
  register(renderer) {
9547
9556
  if (!renderer.renderingLayers.length) {
@@ -9561,14 +9570,14 @@ stores.inject(MyMetaStore, storeInstance);
9561
9570
  }
9562
9571
  drawLayer(context, layer) {
9563
9572
  const renderers = this.renderers[layer];
9564
- if (!renderers) {
9565
- return;
9566
- }
9567
- for (const renderer of renderers) {
9568
- context.ctx.save();
9569
- renderer.drawLayer(context, layer);
9570
- context.ctx.restore();
9573
+ if (renderers) {
9574
+ for (const renderer of renderers) {
9575
+ context.ctx.save();
9576
+ renderer.drawLayer(context, layer);
9577
+ context.ctx.restore();
9578
+ }
9571
9579
  }
9580
+ return "noStateChange";
9572
9581
  }
9573
9582
  }
9574
9583
 
@@ -9621,16 +9630,17 @@ stores.inject(MyMetaStore, storeInstance);
9621
9630
  focusComposer(listener, args) {
9622
9631
  this.activeComposer = listener;
9623
9632
  if (this.getters.isReadonly()) {
9624
- return;
9633
+ return "noStateChange";
9625
9634
  }
9626
9635
  this._focusMode = args.focusMode || "contentFocus";
9627
9636
  if (this._focusMode !== "inactive") {
9628
9637
  this.setComposerContent(args);
9629
9638
  }
9639
+ return;
9630
9640
  }
9631
9641
  focusActiveComposer(args) {
9632
9642
  if (this.getters.isReadonly()) {
9633
- return;
9643
+ return "noStateChange";
9634
9644
  }
9635
9645
  if (!this.activeComposer) {
9636
9646
  throw new Error("No composer is registered");
@@ -9639,6 +9649,7 @@ stores.inject(MyMetaStore, storeInstance);
9639
9649
  if (this._focusMode !== "inactive") {
9640
9650
  this.setComposerContent(args);
9641
9651
  }
9652
+ return;
9642
9653
  }
9643
9654
  /**
9644
9655
  * Start the edition or update the content if it's already started.
@@ -10132,7 +10143,7 @@ stores.inject(MyMetaStore, storeInstance);
10132
10143
  }
10133
10144
  function formatChartDatasetValue(axisFormats, locale) {
10134
10145
  return (value, axisId) => {
10135
- const format = axisId ? axisFormats?.[axisId] : undefined;
10146
+ const format = axisFormats?.[axisId];
10136
10147
  return formatTickValue({ format, locale })(value);
10137
10148
  };
10138
10149
  }
@@ -10306,7 +10317,7 @@ stores.inject(MyMetaStore, storeInstance);
10306
10317
  const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10307
10318
  ctx.fillStyle = chartFontColor(options.background);
10308
10319
  ctx.strokeStyle = options.background || "#ffffff";
10309
- const displayValue = options.callback(value);
10320
+ const displayValue = options.callback(value, "y");
10310
10321
  drawTextWithBackground(displayValue, x, y, ctx);
10311
10322
  }
10312
10323
  }
@@ -19205,6 +19216,9 @@ stores.inject(MyMetaStore, storeInstance);
19205
19216
  };
19206
19217
  }
19207
19218
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19219
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19220
+ this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
19221
+ }
19208
19222
  return pivot.getPivotCellValueAndFormat(_measure, domain);
19209
19223
  },
19210
19224
  };
@@ -19236,6 +19250,9 @@ stores.inject(MyMetaStore, storeInstance);
19236
19250
  };
19237
19251
  }
19238
19252
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19253
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19254
+ this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
19255
+ }
19239
19256
  const lastNode = domain.at(-1);
19240
19257
  if (lastNode?.field === "measure") {
19241
19258
  return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
@@ -19458,6 +19475,9 @@ stores.inject(MyMetaStore, storeInstance);
19458
19475
  return data === undefined || data.value === null;
19459
19476
  }
19460
19477
  const getNeutral = { number: 0, string: "", boolean: false };
19478
+ function areAlmostEqual(value1, value2, epsilon = 2e-16) {
19479
+ return Math.abs(value1 - value2) < epsilon;
19480
+ }
19461
19481
  const EQ = {
19462
19482
  description: _t("Equal."),
19463
19483
  args: [
@@ -19479,6 +19499,9 @@ stores.inject(MyMetaStore, storeInstance);
19479
19499
  if (typeof _value2 === "string") {
19480
19500
  _value2 = _value2.toUpperCase();
19481
19501
  }
19502
+ if (typeof _value1 === "number" && typeof _value2 === "number") {
19503
+ return { value: areAlmostEqual(_value1, _value2) };
19504
+ }
19482
19505
  return { value: _value1 === _value2 };
19483
19506
  },
19484
19507
  };
@@ -19518,6 +19541,9 @@ stores.inject(MyMetaStore, storeInstance);
19518
19541
  ],
19519
19542
  compute: function (value1, value2) {
19520
19543
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19544
+ if (typeof v1 === "number" && typeof v2 === "number") {
19545
+ return !areAlmostEqual(v1, v2) && v1 > v2;
19546
+ }
19521
19547
  return v1 > v2;
19522
19548
  });
19523
19549
  },
@@ -19533,6 +19559,9 @@ stores.inject(MyMetaStore, storeInstance);
19533
19559
  ],
19534
19560
  compute: function (value1, value2) {
19535
19561
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19562
+ if (typeof v1 === "number" && typeof v2 === "number") {
19563
+ return areAlmostEqual(v1, v2) || v1 > v2;
19564
+ }
19536
19565
  return v1 >= v2;
19537
19566
  });
19538
19567
  },
@@ -21139,7 +21168,7 @@ stores.inject(MyMetaStore, storeInstance);
21139
21168
  .find((token) => {
21140
21169
  const { xc, sheetName: sheet } = splitReference(token.value);
21141
21170
  const sheetName = sheet || this.getters.getSheetName(this.sheetId);
21142
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
21171
+ if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
21143
21172
  return false;
21144
21173
  }
21145
21174
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
@@ -24601,7 +24630,7 @@ stores.inject(MyMetaStore, storeInstance);
24601
24630
  ({ xc, sheetName } = splitReference(reference));
24602
24631
  let rangeSheetIndex;
24603
24632
  if (sheetName) {
24604
- const index = data.sheets.findIndex((sheet) => sheet.name === sheetName);
24633
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
24605
24634
  if (index < 0) {
24606
24635
  throw new Error("Unable to find a sheet with the name " + sheetName);
24607
24636
  }
@@ -24942,7 +24971,7 @@ stores.inject(MyMetaStore, storeInstance);
24942
24971
  formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
24943
24972
  externalRefId = Number(externalRefId) - 1;
24944
24973
  cellRef = cellRef.replace(/\$/g, "");
24945
- const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name === sheetName);
24974
+ const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
24946
24975
  if (sheetIndex === -1) {
24947
24976
  return match;
24948
24977
  }
@@ -25593,7 +25622,7 @@ stores.inject(MyMetaStore, storeInstance);
25593
25622
  */
25594
25623
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25595
25624
  for (let tableSheet of convertedSheets) {
25596
- const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
25625
+ const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
25597
25626
  for (let table of tables) {
25598
25627
  const tabRef = table.name + "[";
25599
25628
  for (let sheet of convertedSheets) {
@@ -32585,12 +32614,20 @@ stores.inject(MyMetaStore, storeInstance);
32585
32614
  }
32586
32615
  }
32587
32616
  hover(position) {
32617
+ if (position.col === this.col && position.row === this.row) {
32618
+ return "noStateChange";
32619
+ }
32588
32620
  this.col = position.col;
32589
32621
  this.row = position.row;
32622
+ return;
32590
32623
  }
32591
32624
  clear() {
32625
+ if (this.col === undefined && this.row === undefined) {
32626
+ return "noStateChange";
32627
+ }
32592
32628
  this.col = undefined;
32593
32629
  this.row = undefined;
32630
+ return;
32594
32631
  }
32595
32632
  }
32596
32633
 
@@ -32612,7 +32649,11 @@ stores.inject(MyMetaStore, storeInstance);
32612
32649
  this.persistentPopover = { col, row, sheetId, type };
32613
32650
  }
32614
32651
  close() {
32652
+ if (!this.persistentPopover) {
32653
+ return "noStateChange";
32654
+ }
32615
32655
  this.persistentPopover = undefined;
32656
+ return;
32616
32657
  }
32617
32658
  get persistentCellPopover() {
32618
32659
  return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
@@ -40068,10 +40109,18 @@ stores.inject(MyMetaStore, storeInstance);
40068
40109
  }
40069
40110
 
40070
40111
  class DOMFocusableElementStore {
40071
- mutators = ["setFocusableElement"];
40112
+ mutators = ["setFocusableElement", "focus"];
40072
40113
  focusableElement = undefined;
40073
40114
  setFocusableElement(element) {
40074
40115
  this.focusableElement = element;
40116
+ return "noStateChange";
40117
+ }
40118
+ focus() {
40119
+ if (this.focusableElement === document.activeElement) {
40120
+ return "noStateChange";
40121
+ }
40122
+ this.focusableElement?.focus();
40123
+ return;
40075
40124
  }
40076
40125
  }
40077
40126
 
@@ -40659,7 +40708,7 @@ stores.inject(MyMetaStore, storeInstance);
40659
40708
  if (document.activeElement === this.contentHelper.el &&
40660
40709
  this.props.composerStore.editionMode === "inactive" &&
40661
40710
  !this.props.isDefaultFocus) {
40662
- this.DOMFocusableElementStore.focusableElement?.focus();
40711
+ this.DOMFocusableElementStore.focus();
40663
40712
  }
40664
40713
  });
40665
40714
  owl.useEffect(() => {
@@ -46943,7 +46992,12 @@ stores.inject(MyMetaStore, storeInstance);
46943
46992
  entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
46944
46993
  }
46945
46994
  else {
46946
- entry[field.name] = cell;
46995
+ if (field.type === "char") {
46996
+ entry[field.name] = { ...cell, value: cell.formattedValue || null };
46997
+ }
46998
+ else {
46999
+ entry[field.name] = cell;
47000
+ }
46947
47001
  }
46948
47002
  }
46949
47003
  entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
@@ -52081,10 +52135,6 @@ stores.inject(MyMetaStore, storeInstance);
52081
52135
  ctx.scale(dpr, dpr);
52082
52136
  for (const layer of OrderedLayers()) {
52083
52137
  model.drawLayer(renderingContext, layer);
52084
- // @ts-ignore 'drawLayer' is not declated as a mutator because:
52085
- // it does not mutate anything. Most importantly it's used
52086
- // during rendering. Invoking a mutator during rendering would
52087
- // trigger another rendering, ultimately resulting in an infinite loop.
52088
52138
  rendererStore.drawLayer(renderingContext, layer);
52089
52139
  }
52090
52140
  }
@@ -52775,7 +52825,7 @@ stores.inject(MyMetaStore, storeInstance);
52775
52825
  this.cellPopovers = useStore(CellPopoverStore);
52776
52826
  owl.useEffect(() => {
52777
52827
  if (!this.sidePanel.isOpen) {
52778
- this.DOMFocusableElementStore.focusableElement?.focus();
52828
+ this.DOMFocusableElementStore.focus();
52779
52829
  }
52780
52830
  }, () => [this.sidePanel.isOpen]);
52781
52831
  useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
@@ -52985,7 +53035,7 @@ stores.inject(MyMetaStore, storeInstance);
52985
53035
  focusDefaultElement() {
52986
53036
  if (!this.env.model.getters.getSelectedFigureId() &&
52987
53037
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
52988
- this.DOMFocusableElementStore.focusableElement?.focus();
53038
+ this.DOMFocusableElementStore.focus();
52989
53039
  }
52990
53040
  }
52991
53041
  get gridEl() {
@@ -53330,6 +53380,322 @@ stores.inject(MyMetaStore, storeInstance);
53330
53380
  }
53331
53381
  }
53332
53382
 
53383
+ css /* scss */ `
53384
+ .o_pivot_html_renderer {
53385
+ width: 100%;
53386
+ border-collapse: collapse;
53387
+
53388
+ &:hover {
53389
+ cursor: pointer;
53390
+ }
53391
+
53392
+ td,
53393
+ th {
53394
+ border: 1px solid #dee2e6;
53395
+ background-color: #fff;
53396
+ padding: 0.3rem;
53397
+ white-space: nowrap;
53398
+
53399
+ &:hover {
53400
+ filter: brightness(0.9);
53401
+ }
53402
+ }
53403
+
53404
+ td {
53405
+ text-align: right;
53406
+ }
53407
+
53408
+ th {
53409
+ background-color: #f5f5f5;
53410
+ font-weight: bold;
53411
+ color: black;
53412
+ }
53413
+
53414
+ .o_missing_value {
53415
+ color: #46646d;
53416
+ background: #e7f2f6;
53417
+ }
53418
+ }
53419
+ `;
53420
+ class PivotHTMLRenderer extends owl.Component {
53421
+ static template = "o_spreadsheet.PivotHTMLRenderer";
53422
+ static components = { Checkbox };
53423
+ static props = {
53424
+ pivotId: String,
53425
+ onCellClicked: Function,
53426
+ };
53427
+ pivot = this.env.model.getters.getPivot(this.props.pivotId);
53428
+ data = {
53429
+ columns: [],
53430
+ rows: [],
53431
+ values: [],
53432
+ };
53433
+ state = owl.useState({
53434
+ showMissingValuesOnly: false,
53435
+ });
53436
+ setup() {
53437
+ const table = this.pivot.getTableStructure();
53438
+ const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
53439
+ this.data = {
53440
+ columns: this._buildColHeaders(formulaId, table),
53441
+ rows: this._buildRowHeaders(formulaId, table),
53442
+ values: this._buildValues(formulaId, table),
53443
+ };
53444
+ }
53445
+ get tracker() {
53446
+ return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
53447
+ }
53448
+ // ---------------------------------------------------------------------
53449
+ // Missing values building
53450
+ // ---------------------------------------------------------------------
53451
+ /**
53452
+ * Retrieve the data to display in the Pivot Table
53453
+ * In the case when showMissingValuesOnly is false, the returned value
53454
+ * is the complete data
53455
+ * In the case when showMissingValuesOnly is true, the returned value is
53456
+ * the data which contains only missing values in the rows and cols. In
53457
+ * the rows, we also return the parent rows of rows which contains missing
53458
+ * values, to give context to the user.
53459
+ *
53460
+ */
53461
+ getTableData() {
53462
+ if (!this.state.showMissingValuesOnly) {
53463
+ return this.data;
53464
+ }
53465
+ const colIndexes = this.getColumnsIndexes();
53466
+ const rowIndexes = this.getRowsIndexes();
53467
+ const columns = this.buildColumnsMissing(colIndexes);
53468
+ const rows = this.buildRowsMissing(rowIndexes);
53469
+ const values = this.buildValuesMissing(colIndexes, rowIndexes);
53470
+ return { columns, rows, values };
53471
+ }
53472
+ /**
53473
+ * Retrieve the parents of the given row
53474
+ * ex:
53475
+ * Australia
53476
+ * January
53477
+ * February
53478
+ * The parent of "January" is "Australia"
53479
+ */
53480
+ addRecursiveRow(index) {
53481
+ const rows = this.pivot.getTableStructure().rows;
53482
+ const row = [...rows[index].values];
53483
+ if (row.length <= 1) {
53484
+ return [index];
53485
+ }
53486
+ row.pop();
53487
+ const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
53488
+ return [index].concat(this.addRecursiveRow(parentRowIndex));
53489
+ }
53490
+ /**
53491
+ * Create the columns to be used, based on the indexes of the columns in
53492
+ * which a missing value is present
53493
+ *
53494
+ */
53495
+ buildColumnsMissing(indexes) {
53496
+ // columnsMap explode the columns in an array of array of the same
53497
+ // size with the index of each column, repeated 'span' times.
53498
+ // ex:
53499
+ // | A | B |
53500
+ // | 1 | 2 | 3 |
53501
+ // => [
53502
+ // [0, 0, 1]
53503
+ // [0, 1, 2]
53504
+ // ]
53505
+ const columnsMap = [];
53506
+ for (const column of this.data.columns) {
53507
+ const columnMap = [];
53508
+ for (const index in column) {
53509
+ for (let i = 0; i < column[index].span; i++) {
53510
+ columnMap.push(parseInt(index, 10));
53511
+ }
53512
+ }
53513
+ columnsMap.push(columnMap);
53514
+ }
53515
+ // Remove the columns that are not present in indexes
53516
+ for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
53517
+ if (!indexes.includes(i)) {
53518
+ for (const columnMap of columnsMap) {
53519
+ columnMap.splice(i, 1);
53520
+ }
53521
+ }
53522
+ }
53523
+ // Build the columns
53524
+ const columns = [];
53525
+ for (const mapIndex in columnsMap) {
53526
+ const column = [];
53527
+ let index = undefined;
53528
+ let span = 1;
53529
+ for (let i = 0; i < columnsMap[mapIndex].length; i++) {
53530
+ if (index !== columnsMap[mapIndex][i]) {
53531
+ if (index !== undefined) {
53532
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53533
+ }
53534
+ index = columnsMap[mapIndex][i];
53535
+ span = 1;
53536
+ }
53537
+ else {
53538
+ span++;
53539
+ }
53540
+ }
53541
+ if (index !== undefined) {
53542
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53543
+ }
53544
+ columns.push(column);
53545
+ }
53546
+ return columns;
53547
+ }
53548
+ /**
53549
+ * Create the rows to be used, based on the indexes of the rows in
53550
+ * which a missing value is present.
53551
+ */
53552
+ buildRowsMissing(indexes) {
53553
+ return indexes.map((index) => this.data.rows[index]);
53554
+ }
53555
+ /**
53556
+ * Create the value to be used, based on the indexes of the columns and
53557
+ * rows in which a missing value is present.
53558
+ */
53559
+ buildValuesMissing(colIndexes, rowIndexes) {
53560
+ const values = colIndexes.map(() => []);
53561
+ for (const row of rowIndexes) {
53562
+ for (const col in colIndexes) {
53563
+ values[col].push(this.data.values[colIndexes[col]][row]);
53564
+ }
53565
+ }
53566
+ return values;
53567
+ }
53568
+ getColumnsIndexes() {
53569
+ const indexes = new Set();
53570
+ for (let i = 0; i < this.data.columns.length; i++) {
53571
+ const exploded = [];
53572
+ for (let y = 0; y < this.data.columns[i].length; y++) {
53573
+ for (let x = 0; x < this.data.columns[i][y].span; x++) {
53574
+ exploded.push(this.data.columns[i][y]);
53575
+ }
53576
+ }
53577
+ for (let y = 0; y < exploded.length; y++) {
53578
+ if (exploded[y].isMissing) {
53579
+ indexes.add(y);
53580
+ }
53581
+ }
53582
+ }
53583
+ for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
53584
+ const values = this.data.values[i];
53585
+ if (values.find((x) => x.isMissing)) {
53586
+ indexes.add(i);
53587
+ }
53588
+ }
53589
+ return Array.from(indexes).sort((a, b) => a - b);
53590
+ }
53591
+ getRowsIndexes() {
53592
+ const rowIndexes = new Set();
53593
+ for (let i = 0; i < this.data.rows.length; i++) {
53594
+ if (this.data.rows[i].isMissing) {
53595
+ rowIndexes.add(i);
53596
+ }
53597
+ for (const col of this.data.values) {
53598
+ if (col[i].isMissing) {
53599
+ this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
53600
+ }
53601
+ }
53602
+ }
53603
+ return Array.from(rowIndexes).sort((a, b) => a - b);
53604
+ }
53605
+ // ---------------------------------------------------------------------
53606
+ // Data table creation
53607
+ // ---------------------------------------------------------------------
53608
+ _buildColHeaders(id, table) {
53609
+ const headers = [];
53610
+ for (const row of table.columns) {
53611
+ const current = [];
53612
+ for (const cell of row) {
53613
+ const args = [];
53614
+ for (let i = 0; i < cell.fields.length; i++) {
53615
+ args.push({ value: cell.fields[i] }, { value: cell.values[i] });
53616
+ }
53617
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53618
+ const locale = this.env.model.getters.getLocale();
53619
+ if (domain.at(-1)?.field === "measure") {
53620
+ const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
53621
+ current.push({
53622
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53623
+ value: formatValue(value, { format, locale }),
53624
+ span: cell.width,
53625
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53626
+ });
53627
+ }
53628
+ else {
53629
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53630
+ current.push({
53631
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53632
+ value: formatValue(value, { format, locale }),
53633
+ span: cell.width,
53634
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53635
+ });
53636
+ }
53637
+ }
53638
+ headers.push(current);
53639
+ }
53640
+ const last = headers[headers.length - 1];
53641
+ headers[headers.length - 1] = last.map((cell) => {
53642
+ if (!cell.isMissing) {
53643
+ cell.style = "color: #756f6f;";
53644
+ }
53645
+ return cell;
53646
+ });
53647
+ return headers;
53648
+ }
53649
+ _buildRowHeaders(id, table) {
53650
+ const headers = [];
53651
+ for (const row of table.rows) {
53652
+ const args = [];
53653
+ for (let i = 0; i < row.fields.length; i++) {
53654
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53655
+ }
53656
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53657
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53658
+ const locale = this.env.model.getters.getLocale();
53659
+ const cell = {
53660
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53661
+ value: formatValue(value, { format, locale }),
53662
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53663
+ };
53664
+ if (row.indent > 1) {
53665
+ cell.style = `padding-left: ${row.indent - 1 * 10}px`;
53666
+ }
53667
+ headers.push(cell);
53668
+ }
53669
+ return headers;
53670
+ }
53671
+ _buildValues(id, table) {
53672
+ const values = [];
53673
+ for (const col of table.columns.at(-1) || []) {
53674
+ const current = [];
53675
+ const measure = toString(col.values[col.values.length - 1]);
53676
+ for (const row of table.rows) {
53677
+ const args = [];
53678
+ for (let i = 0; i < row.fields.length; i++) {
53679
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53680
+ }
53681
+ for (let i = 0; i < col.fields.length - 1; i++) {
53682
+ args.push({ value: col.fields[i] }, { value: col.values[i] });
53683
+ }
53684
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53685
+ const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
53686
+ const locale = this.env.model.getters.getLocale();
53687
+ current.push({
53688
+ formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
53689
+ value: formatValue(value, { format, locale }),
53690
+ isMissing: !this.tracker?.isValuePresent(measure, domain),
53691
+ });
53692
+ }
53693
+ values.push(current);
53694
+ }
53695
+ return values;
53696
+ }
53697
+ }
53698
+
53333
53699
  /**
53334
53700
  * BasePlugin
53335
53701
  *
@@ -56789,7 +57155,7 @@ stores.inject(MyMetaStore, storeInstance);
56789
57155
  if (range.sheetId === cmd.sheetId) {
56790
57156
  return { changeType: "CHANGE", range };
56791
57157
  }
56792
- if (cmd.name && range.invalidSheetName === cmd.name) {
57158
+ if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
56793
57159
  const invalidSheetName = undefined;
56794
57160
  const sheetId = cmd.sheetId;
56795
57161
  const newRange = range.clone({ sheetId, invalidSheetName });
@@ -57406,7 +57772,7 @@ stores.inject(MyMetaStore, storeInstance);
57406
57772
  if (name) {
57407
57773
  const unquotedName = getUnquotedSheetName(name);
57408
57774
  for (const key in this.sheetIdsMapName) {
57409
- if (key.toUpperCase() === unquotedName.toUpperCase()) {
57775
+ if (isSheetNameEqual(key, unquotedName)) {
57410
57776
  return this.sheetIdsMapName[key];
57411
57777
  }
57412
57778
  }
@@ -57654,7 +58020,7 @@ stores.inject(MyMetaStore, storeInstance);
57654
58020
  }
57655
58021
  const { orderedSheetIds, sheets } = this;
57656
58022
  const name = cmd.name && cmd.name.trim().toLowerCase();
57657
- if (orderedSheetIds.find((id) => sheets[id]?.name.toLowerCase() === name && id !== cmd.sheetId)) {
58023
+ if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
57658
58024
  return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
57659
58025
  }
57660
58026
  if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
@@ -66547,6 +66913,55 @@ stores.inject(MyMetaStore, storeInstance);
66547
66913
  }
66548
66914
  }
66549
66915
 
66916
+ class PivotPresenceTracker {
66917
+ trackedValues = new Set();
66918
+ domainToArray(domain) {
66919
+ return domain.flatMap((node) => [node.field, toString(node.value)]);
66920
+ }
66921
+ isValuePresent(measure, domain) {
66922
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66923
+ return this.trackedValues.has(key);
66924
+ }
66925
+ isHeaderPresent(domain) {
66926
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66927
+ return this.trackedValues.has(key);
66928
+ }
66929
+ trackValue(measure, domain) {
66930
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66931
+ this.trackedValues.add(key);
66932
+ }
66933
+ trackHeader(domain) {
66934
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66935
+ this.trackedValues.add(key);
66936
+ }
66937
+ }
66938
+
66939
+ class PivotPresencePlugin extends UIPlugin {
66940
+ static getters = ["getPivotPresenceTracker"];
66941
+ trackPresencePivotId;
66942
+ tracker;
66943
+ handle(cmd) {
66944
+ switch (cmd.type) {
66945
+ case "PIVOT_START_PRESENCE_TRACKING":
66946
+ this.tracker = new PivotPresenceTracker();
66947
+ this.trackPresencePivotId = cmd.pivotId;
66948
+ break;
66949
+ case "PIVOT_STOP_PRESENCE_TRACKING":
66950
+ this.trackPresencePivotId = undefined;
66951
+ break;
66952
+ }
66953
+ }
66954
+ getPivotPresenceTracker(pivotId) {
66955
+ if (this.trackPresencePivotId !== pivotId) {
66956
+ return undefined;
66957
+ }
66958
+ if (!this.tracker) {
66959
+ throw new Error("Tracker not initialized");
66960
+ }
66961
+ return this.tracker;
66962
+ }
66963
+ }
66964
+
66550
66965
  class SplitToColumnsPlugin extends UIPlugin {
66551
66966
  static getters = ["getAutomaticSeparator"];
66552
66967
  allowDispatch(cmd) {
@@ -69297,6 +69712,7 @@ stores.inject(MyMetaStore, storeInstance);
69297
69712
  .add("automatic_sum", AutomaticSumPlugin)
69298
69713
  .add("format", FormatPlugin)
69299
69714
  .add("insert_pivot", InsertPivotPlugin)
69715
+ .add("pivot_presence", PivotPresencePlugin)
69300
69716
  .add("split_to_columns", SplitToColumnsPlugin)
69301
69717
  .add("collaborative", CollaborativePlugin)
69302
69718
  .add("history", HistoryPlugin)
@@ -69677,11 +70093,11 @@ stores.inject(MyMetaStore, storeInstance);
69677
70093
  if (ev.key === "Enter") {
69678
70094
  ev.preventDefault();
69679
70095
  this.stopEdition();
69680
- this.DOMFocusableElementStore.focusableElement?.focus();
70096
+ this.DOMFocusableElementStore.focus();
69681
70097
  }
69682
70098
  if (ev.key === "Escape") {
69683
70099
  this.cancelEdition();
69684
- this.DOMFocusableElementStore.focusableElement?.focus();
70100
+ this.DOMFocusableElementStore.focus();
69685
70101
  }
69686
70102
  }
69687
70103
  onMouseEventSheetName(ev) {
@@ -76292,6 +76708,7 @@ stores.inject(MyMetaStore, storeInstance);
76292
76708
  PivotDimensionOrder,
76293
76709
  PivotDimension,
76294
76710
  PivotLayoutConfigurator,
76711
+ PivotHTMLRenderer,
76295
76712
  PivotDeferUpdate,
76296
76713
  PivotTitleSection,
76297
76714
  CogWheelMenu,
@@ -76386,9 +76803,9 @@ stores.inject(MyMetaStore, storeInstance);
76386
76803
  exports.tokenize = tokenize;
76387
76804
 
76388
76805
 
76389
- __info__.version = "18.2.11";
76390
- __info__.date = "2025-05-12T05:25:59.138Z";
76391
- __info__.hash = "eb87dca";
76806
+ __info__.version = "18.2.12";
76807
+ __info__.date = "2025-05-13T17:52:23.989Z";
76808
+ __info__.hash = "ba2ba9b";
76392
76809
 
76393
76810
 
76394
76811
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);