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