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