@odoo/o-spreadsheet 18.1.18 → 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.18
6
- * @date 2025-05-02T12:30:47.822Z
7
- * @hash 29c21c6
5
+ * @version 18.1.20
6
+ * @date 2025-05-13T17:52:28.174Z
7
+ * @hash 3e43a46
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -3347,7 +3347,7 @@
3347
3347
  */
3348
3348
  const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
3349
3349
  decimalSeparator = escapeRegExp(decimalSeparator);
3350
- return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3350
+ return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3351
3351
  });
3352
3352
  const getNumberRegex = memoize(function getNumberRegex(locale) {
3353
3353
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
@@ -6272,6 +6272,32 @@
6272
6272
  })
6273
6273
  .filter(isDefined);
6274
6274
  }
6275
+ function getNextSheetName(existingNames, baseName = "Sheet") {
6276
+ let i = 1;
6277
+ let name = `${baseName}${i}`;
6278
+ while (existingNames.includes(name)) {
6279
+ name = `${baseName}${i}`;
6280
+ i++;
6281
+ }
6282
+ return name;
6283
+ }
6284
+ function getDuplicateSheetName(nameToDuplicate, existingNames) {
6285
+ let i = 1;
6286
+ const baseName = _t("Copy of %s", nameToDuplicate);
6287
+ let name = baseName.toString();
6288
+ while (existingNames.includes(name)) {
6289
+ name = `${baseName} (${i})`;
6290
+ i++;
6291
+ }
6292
+ return name;
6293
+ }
6294
+ function isSheetNameEqual(name1, name2) {
6295
+ if (name1 === undefined || name2 === undefined) {
6296
+ return false;
6297
+ }
6298
+ return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
6299
+ getUnquotedSheetName(name2.trim().toUpperCase()));
6300
+ }
6275
6301
 
6276
6302
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
6277
6303
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
@@ -7970,6 +7996,24 @@
7970
7996
  return `${normalizedValue}`;
7971
7997
  },
7972
7998
  };
7999
+ /**
8000
+ * normalizes month number + year
8001
+ */
8002
+ const monthAdapter = {
8003
+ normalizeFunctionValue(value) {
8004
+ const date = toNumber(value, DEFAULT_LOCALE);
8005
+ return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
8006
+ },
8007
+ toValueAndFormat(normalizedValue) {
8008
+ return {
8009
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
8010
+ format: "mmmm yyyy",
8011
+ };
8012
+ },
8013
+ toFunctionValue(normalizedValue) {
8014
+ return `"${normalizedValue}"`;
8015
+ },
8016
+ };
7973
8017
  /**
7974
8018
  * normalizes quarter number
7975
8019
  */
@@ -8100,6 +8144,7 @@
8100
8144
  .add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
8101
8145
  .add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
8102
8146
  .add("month_number", nullHandlerDecorator(monthNumberAdapter))
8147
+ .add("month", nullHandlerDecorator(monthAdapter))
8103
8148
  .add("quarter_number", nullHandlerDecorator(quarterNumberAdapter))
8104
8149
  .add("day_of_week", nullHandlerDecorator(dayOfWeekAdapter))
8105
8150
  .add("hour_number", nullHandlerDecorator(hourNumberAdapter))
@@ -8116,10 +8161,9 @@
8116
8161
  avg: _t("Average"),
8117
8162
  sum: _t("Sum"),
8118
8163
  };
8119
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8120
8164
  const AGGREGATORS_BY_FIELD_TYPE = {
8121
- integer: NUMBER_CHAR_AGGREGATORS,
8122
- char: NUMBER_CHAR_AGGREGATORS,
8165
+ integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
8166
+ char: ["count_distinct", "count"],
8123
8167
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8124
8168
  };
8125
8169
  const AGGREGATORS = {};
@@ -8279,10 +8323,7 @@
8279
8323
  return normalizer(groupValueString, dimension.granularity);
8280
8324
  }
8281
8325
  function normalizeDateTime(value, granularity) {
8282
- if (!granularity) {
8283
- throw new Error("Missing granularity");
8284
- }
8285
- return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
8326
+ return pivotTimeAdapter(granularity ?? "month").normalizeFunctionValue(value);
8286
8327
  }
8287
8328
  function toFunctionPivotValue(value, dimension) {
8288
8329
  if (value === null) {
@@ -8294,10 +8335,7 @@
8294
8335
  return pivotToFunctionValueRegistry.get(dimension.type)(value, dimension.granularity);
8295
8336
  }
8296
8337
  function toFunctionValueDateTime(value, granularity) {
8297
- if (!granularity) {
8298
- throw new Error("Missing granularity");
8299
- }
8300
- return pivotTimeAdapter(granularity).toFunctionValue(value);
8338
+ return pivotTimeAdapter(granularity ?? "month").toFunctionValue(value);
8301
8339
  }
8302
8340
  const pivotNormalizationValueRegistry = new Registry();
8303
8341
  pivotNormalizationValueRegistry
@@ -9477,7 +9515,10 @@ stores.inject(MyMetaStore, storeInstance);
9477
9515
  const functionProxy = new Proxy(value, {
9478
9516
  // trap the function call
9479
9517
  apply(target, thisArg, argArray) {
9480
- Reflect.apply(target, thisStore, argArray);
9518
+ const res = Reflect.apply(target, thisStore, argArray);
9519
+ if (res === "noStateChange") {
9520
+ return;
9521
+ }
9481
9522
  callback();
9482
9523
  },
9483
9524
  });
@@ -9499,7 +9540,7 @@ stores.inject(MyMetaStore, storeInstance);
9499
9540
  const ModelStore = createAbstractStore("Model");
9500
9541
 
9501
9542
  class RendererStore {
9502
- mutators = ["register", "unRegister"];
9543
+ mutators = ["register", "unRegister", "drawLayer"];
9503
9544
  renderers = {};
9504
9545
  register(renderer) {
9505
9546
  if (!renderer.renderingLayers.length) {
@@ -9519,14 +9560,14 @@ stores.inject(MyMetaStore, storeInstance);
9519
9560
  }
9520
9561
  drawLayer(context, layer) {
9521
9562
  const renderers = this.renderers[layer];
9522
- if (!renderers) {
9523
- return;
9524
- }
9525
- for (const renderer of renderers) {
9526
- context.ctx.save();
9527
- renderer.drawLayer(context, layer);
9528
- context.ctx.restore();
9563
+ if (renderers) {
9564
+ for (const renderer of renderers) {
9565
+ context.ctx.save();
9566
+ renderer.drawLayer(context, layer);
9567
+ context.ctx.restore();
9568
+ }
9529
9569
  }
9570
+ return "noStateChange";
9530
9571
  }
9531
9572
  }
9532
9573
 
@@ -9579,16 +9620,17 @@ stores.inject(MyMetaStore, storeInstance);
9579
9620
  focusComposer(listener, args) {
9580
9621
  this.activeComposer = listener;
9581
9622
  if (this.getters.isReadonly()) {
9582
- return;
9623
+ return "noStateChange";
9583
9624
  }
9584
9625
  this._focusMode = args.focusMode || "contentFocus";
9585
9626
  if (this._focusMode !== "inactive") {
9586
9627
  this.setComposerContent(args);
9587
9628
  }
9629
+ return;
9588
9630
  }
9589
9631
  focusActiveComposer(args) {
9590
9632
  if (this.getters.isReadonly()) {
9591
- return;
9633
+ return "noStateChange";
9592
9634
  }
9593
9635
  if (!this.activeComposer) {
9594
9636
  throw new Error("No composer is registered");
@@ -9597,6 +9639,7 @@ stores.inject(MyMetaStore, storeInstance);
9597
9639
  if (this._focusMode !== "inactive") {
9598
9640
  this.setComposerContent(args);
9599
9641
  }
9642
+ return;
9600
9643
  }
9601
9644
  /**
9602
9645
  * Start the edition or update the content if it's already started.
@@ -9612,12 +9655,24 @@ stores.inject(MyMetaStore, storeInstance);
9612
9655
  }
9613
9656
 
9614
9657
  const chartJsExtensionRegistry = new Registry();
9615
- /** Return window.Chart, making sure all our extensions are loaded in ChartJS */
9616
- function getChartJSConstructor() {
9617
- if (window.Chart && !window.Chart?.registry.plugins.get("chartShowValuesPlugin")) {
9618
- window.Chart.register(...chartJsExtensionRegistry.getAll());
9658
+ function areChartJSExtensionsLoaded() {
9659
+ return !!window.Chart.registry.plugins.get("chartShowValuesPlugin");
9660
+ }
9661
+ function registerChartJSExtensions() {
9662
+ if (!window.Chart || areChartJSExtensionsLoaded()) {
9663
+ return;
9664
+ }
9665
+ for (const registryItem of chartJsExtensionRegistry.getAll()) {
9666
+ registryItem.register(window.Chart);
9667
+ }
9668
+ }
9669
+ function unregisterChartJsExtensions() {
9670
+ if (!window.Chart) {
9671
+ return;
9672
+ }
9673
+ for (const registryItem of chartJsExtensionRegistry.getAll()) {
9674
+ registryItem.unregister(window.Chart);
9619
9675
  }
9620
- return window.Chart;
9621
9676
  }
9622
9677
 
9623
9678
  const TREND_LINE_XAXIS_ID = "x1";
@@ -9934,7 +9989,7 @@ stores.inject(MyMetaStore, storeInstance);
9934
9989
  }
9935
9990
  function formatChartDatasetValue(axisFormats, locale) {
9936
9991
  return (value, axisId) => {
9937
- const format = axisId ? axisFormats?.[axisId] : undefined;
9992
+ const format = axisFormats?.[axisId];
9938
9993
  return formatTickValue({ format, locale })(value);
9939
9994
  };
9940
9995
  }
@@ -10098,7 +10153,7 @@ stores.inject(MyMetaStore, storeInstance);
10098
10153
  const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10099
10154
  ctx.fillStyle = chartFontColor(options.background);
10100
10155
  ctx.strokeStyle = options.background || "#ffffff";
10101
- const displayValue = options.callback(value);
10156
+ const displayValue = options.callback(value, "y");
10102
10157
  drawTextWithBackground(displayValue, x, y, ctx);
10103
10158
  }
10104
10159
  }
@@ -10158,8 +10213,14 @@ stores.inject(MyMetaStore, storeInstance);
10158
10213
  return bars.find((bar, i) => i > startIndex && bar.height !== 0);
10159
10214
  }
10160
10215
 
10161
- chartJsExtensionRegistry.add("chartShowValuesPlugin", chartShowValuesPlugin);
10162
- chartJsExtensionRegistry.add("waterfallLinesPlugin", waterfallLinesPlugin);
10216
+ chartJsExtensionRegistry.add("chartShowValuesPlugin", {
10217
+ register: (Chart) => Chart.register(chartShowValuesPlugin),
10218
+ unregister: (Chart) => Chart.unregister(chartShowValuesPlugin),
10219
+ });
10220
+ chartJsExtensionRegistry.add("waterfallLinesPlugin", {
10221
+ register: (Chart) => Chart.register(waterfallLinesPlugin),
10222
+ unregister: (Chart) => Chart.unregister(waterfallLinesPlugin),
10223
+ });
10163
10224
  class ChartJsComponent extends owl.Component {
10164
10225
  static template = "o-spreadsheet-ChartJsComponent";
10165
10226
  static props = {
@@ -10211,8 +10272,7 @@ stores.inject(MyMetaStore, storeInstance);
10211
10272
  createChart(chartData) {
10212
10273
  const canvas = this.canvas.el;
10213
10274
  const ctx = canvas.getContext("2d");
10214
- const Chart = getChartJSConstructor();
10215
- this.chart = new Chart(ctx, chartData);
10275
+ this.chart = new window.Chart(ctx, chartData);
10216
10276
  }
10217
10277
  updateChartJs(chartData) {
10218
10278
  if (chartData.data && chartData.data.datasets) {
@@ -18344,7 +18404,7 @@ stores.inject(MyMetaStore, storeInstance);
18344
18404
  return { value: "" };
18345
18405
  }
18346
18406
  if (result.value === null) {
18347
- result.value = "";
18407
+ return { ...result, value: "" };
18348
18408
  }
18349
18409
  return result;
18350
18410
  },
@@ -18365,7 +18425,7 @@ stores.inject(MyMetaStore, storeInstance);
18365
18425
  return { value: "" };
18366
18426
  }
18367
18427
  if (result.value === null) {
18368
- result.value = "";
18428
+ return { ...result, value: "" };
18369
18429
  }
18370
18430
  return result;
18371
18431
  },
@@ -18386,7 +18446,7 @@ stores.inject(MyMetaStore, storeInstance);
18386
18446
  return { value: "" };
18387
18447
  }
18388
18448
  if (result.value === null) {
18389
- result.value = "";
18449
+ return { ...result, value: "" };
18390
18450
  }
18391
18451
  return result;
18392
18452
  },
@@ -18412,7 +18472,7 @@ stores.inject(MyMetaStore, storeInstance);
18412
18472
  return { value: "" };
18413
18473
  }
18414
18474
  if (result.value === null) {
18415
- result.value = "";
18475
+ return { ...result, value: "" };
18416
18476
  }
18417
18477
  return result;
18418
18478
  }
@@ -18530,6 +18590,11 @@ stores.inject(MyMetaStore, storeInstance);
18530
18590
  if (range === undefined || range.invalidXc || range.invalidSheetName) {
18531
18591
  throw new InvalidReferenceError();
18532
18592
  }
18593
+ if (evalContext.__originCellPosition &&
18594
+ range.sheetId === evalContext.__originSheetId &&
18595
+ isZoneInside(positionToZone(evalContext.__originCellPosition), zone)) {
18596
+ throw new CircularDependencyError();
18597
+ }
18533
18598
  dependencies.push(range);
18534
18599
  }
18535
18600
  for (const measure of forMeasures) {
@@ -18978,6 +19043,9 @@ stores.inject(MyMetaStore, storeInstance);
18978
19043
  };
18979
19044
  }
18980
19045
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19046
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19047
+ this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
19048
+ }
18981
19049
  return pivot.getPivotCellValueAndFormat(_measure, domain);
18982
19050
  },
18983
19051
  };
@@ -19009,6 +19077,9 @@ stores.inject(MyMetaStore, storeInstance);
19009
19077
  };
19010
19078
  }
19011
19079
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19080
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19081
+ this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
19082
+ }
19012
19083
  const lastNode = domain.at(-1);
19013
19084
  if (lastNode?.field === "measure") {
19014
19085
  return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
@@ -19231,6 +19302,9 @@ stores.inject(MyMetaStore, storeInstance);
19231
19302
  return data === undefined || data.value === null;
19232
19303
  }
19233
19304
  const getNeutral = { number: 0, string: "", boolean: false };
19305
+ function areAlmostEqual(value1, value2, epsilon = 2e-16) {
19306
+ return Math.abs(value1 - value2) < epsilon;
19307
+ }
19234
19308
  const EQ = {
19235
19309
  description: _t("Equal."),
19236
19310
  args: [
@@ -19252,6 +19326,9 @@ stores.inject(MyMetaStore, storeInstance);
19252
19326
  if (typeof _value2 === "string") {
19253
19327
  _value2 = _value2.toUpperCase();
19254
19328
  }
19329
+ if (typeof _value1 === "number" && typeof _value2 === "number") {
19330
+ return { value: areAlmostEqual(_value1, _value2) };
19331
+ }
19255
19332
  return { value: _value1 === _value2 };
19256
19333
  },
19257
19334
  };
@@ -19291,6 +19368,9 @@ stores.inject(MyMetaStore, storeInstance);
19291
19368
  ],
19292
19369
  compute: function (value1, value2) {
19293
19370
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19371
+ if (typeof v1 === "number" && typeof v2 === "number") {
19372
+ return !areAlmostEqual(v1, v2) && v1 > v2;
19373
+ }
19294
19374
  return v1 > v2;
19295
19375
  });
19296
19376
  },
@@ -19306,6 +19386,9 @@ stores.inject(MyMetaStore, storeInstance);
19306
19386
  ],
19307
19387
  compute: function (value1, value2) {
19308
19388
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19389
+ if (typeof v1 === "number" && typeof v2 === "number") {
19390
+ return areAlmostEqual(v1, v2) || v1 > v2;
19391
+ }
19309
19392
  return v1 >= v2;
19310
19393
  });
19311
19394
  },
@@ -20912,7 +20995,7 @@ stores.inject(MyMetaStore, storeInstance);
20912
20995
  .find((token) => {
20913
20996
  const { xc, sheetName: sheet } = splitReference(token.value);
20914
20997
  const sheetName = sheet || this.getters.getSheetName(this.sheetId);
20915
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
20998
+ if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
20916
20999
  return false;
20917
21000
  }
20918
21001
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
@@ -22791,6 +22874,7 @@ stores.inject(MyMetaStore, storeInstance);
22791
22874
  },
22792
22875
  },
22793
22876
  animation: false,
22877
+ events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "mouseup"],
22794
22878
  };
22795
22879
  function truncateLabel(label) {
22796
22880
  if (!label) {
@@ -22816,8 +22900,7 @@ stores.inject(MyMetaStore, storeInstance);
22816
22900
  if ("chartJsConfig" in runtime) {
22817
22901
  const config = deepCopy(runtime.chartJsConfig);
22818
22902
  config.plugins = [backgroundColorChartJSPlugin];
22819
- const Chart = getChartJSConstructor();
22820
- const chart = new Chart(canvas, config);
22903
+ const chart = new window.Chart(canvas, config);
22821
22904
  const imgContent = chart.toBase64Image();
22822
22905
  chart.destroy();
22823
22906
  div.remove();
@@ -24523,7 +24606,7 @@ stores.inject(MyMetaStore, storeInstance);
24523
24606
  ({ xc, sheetName } = splitReference(reference));
24524
24607
  let rangeSheetIndex;
24525
24608
  if (sheetName) {
24526
- const index = data.sheets.findIndex((sheet) => sheet.name === sheetName);
24609
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
24527
24610
  if (index < 0) {
24528
24611
  throw new Error("Unable to find a sheet with the name " + sheetName);
24529
24612
  }
@@ -24864,7 +24947,7 @@ stores.inject(MyMetaStore, storeInstance);
24864
24947
  formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
24865
24948
  externalRefId = Number(externalRefId) - 1;
24866
24949
  cellRef = cellRef.replace(/\$/g, "");
24867
- const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name === sheetName);
24950
+ const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
24868
24951
  if (sheetIndex === -1) {
24869
24952
  return match;
24870
24953
  }
@@ -25515,7 +25598,7 @@ stores.inject(MyMetaStore, storeInstance);
25515
25598
  */
25516
25599
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25517
25600
  for (let tableSheet of convertedSheets) {
25518
- const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
25601
+ const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
25519
25602
  for (let table of tables) {
25520
25603
  const tabRef = table.name + "[";
25521
25604
  for (let sheet of convertedSheets) {
@@ -27949,6 +28032,7 @@ stores.inject(MyMetaStore, storeInstance);
27949
28032
  initialMessages = dropCommands(initialMessages, "SORT_CELLS");
27950
28033
  initialMessages = dropCommands(initialMessages, "SET_DECIMAL");
27951
28034
  initialMessages = fixChartDefinitions(data, initialMessages);
28035
+ initialMessages = fixTranslatedDuplicateSheetName(data, initialMessages);
27952
28036
  return initialMessages;
27953
28037
  }
27954
28038
  /**
@@ -28048,6 +28132,40 @@ stores.inject(MyMetaStore, storeInstance);
28048
28132
  }
28049
28133
  return messages;
28050
28134
  }
28135
+ function fixTranslatedDuplicateSheetName(data, initialMessages) {
28136
+ const sheetNames = {};
28137
+ for (const sheet of data.sheets || []) {
28138
+ sheetNames[sheet.id] = sheet.name;
28139
+ }
28140
+ const messages = [];
28141
+ for (const message of initialMessages) {
28142
+ if (message.type === "REMOTE_REVISION") {
28143
+ const commands = [];
28144
+ for (const cmd of message.commands) {
28145
+ switch (cmd.type) {
28146
+ case "DUPLICATE_SHEET":
28147
+ cmd.sheetNameTo =
28148
+ cmd.sheetNameTo ??
28149
+ getDuplicateSheetName(sheetNames[cmd.sheetId], Object.values(sheetNames));
28150
+ break;
28151
+ case "CREATE_SHEET":
28152
+ case "RENAME_SHEET":
28153
+ sheetNames[cmd.sheetId] = cmd.name || getNextSheetName(Object.values(sheetNames));
28154
+ break;
28155
+ }
28156
+ commands.push(cmd);
28157
+ }
28158
+ messages.push({
28159
+ ...message,
28160
+ commands,
28161
+ });
28162
+ }
28163
+ else {
28164
+ messages.push(message);
28165
+ }
28166
+ }
28167
+ return initialMessages;
28168
+ }
28051
28169
  // -----------------------------------------------------------------------------
28052
28170
  // Helpers
28053
28171
  // -----------------------------------------------------------------------------
@@ -28845,12 +28963,11 @@ stores.inject(MyMetaStore, storeInstance);
28845
28963
  }
28846
28964
  let missingTimeAdapterAlreadyWarned = false;
28847
28965
  function isLuxonTimeAdapterInstalled() {
28848
- const Chart = getChartJSConstructor();
28849
- if (!Chart) {
28966
+ if (!window.Chart) {
28850
28967
  return false;
28851
28968
  }
28852
28969
  // @ts-ignore
28853
- const adapter = new Chart._adapters._date({});
28970
+ const adapter = new window.Chart._adapters._date({});
28854
28971
  const isInstalled = adapter._id === "luxon";
28855
28972
  if (!isInstalled && !missingTimeAdapterAlreadyWarned) {
28856
28973
  missingTimeAdapterAlreadyWarned = true;
@@ -29488,6 +29605,9 @@ stores.inject(MyMetaStore, storeInstance);
29488
29605
  target.style.cursor = "default";
29489
29606
  },
29490
29607
  onClick: (event, legendItem, legend) => {
29608
+ if (event.type !== "click") {
29609
+ return;
29610
+ }
29491
29611
  const index = legendItem.datasetIndex;
29492
29612
  if (!legend.legendItems || index === undefined) {
29493
29613
  return;
@@ -32313,12 +32433,20 @@ stores.inject(MyMetaStore, storeInstance);
32313
32433
  }
32314
32434
  }
32315
32435
  hover(position) {
32436
+ if (position.col === this.col && position.row === this.row) {
32437
+ return "noStateChange";
32438
+ }
32316
32439
  this.col = position.col;
32317
32440
  this.row = position.row;
32441
+ return;
32318
32442
  }
32319
32443
  clear() {
32444
+ if (this.col === undefined && this.row === undefined) {
32445
+ return "noStateChange";
32446
+ }
32320
32447
  this.col = undefined;
32321
32448
  this.row = undefined;
32449
+ return;
32322
32450
  }
32323
32451
  }
32324
32452
 
@@ -32340,7 +32468,11 @@ stores.inject(MyMetaStore, storeInstance);
32340
32468
  this.persistentPopover = { col, row, sheetId, type };
32341
32469
  }
32342
32470
  close() {
32471
+ if (!this.persistentPopover) {
32472
+ return "noStateChange";
32473
+ }
32343
32474
  this.persistentPopover = undefined;
32475
+ return;
32344
32476
  }
32345
32477
  get persistentCellPopover() {
32346
32478
  return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
@@ -33213,10 +33345,13 @@ stores.inject(MyMetaStore, storeInstance);
33213
33345
  name: _t("Duplicate"),
33214
33346
  execute: (env) => {
33215
33347
  const sheetIdFrom = env.model.getters.getActiveSheetId();
33348
+ const sheetNameFrom = env.model.getters.getSheetName(sheetIdFrom);
33216
33349
  const sheetIdTo = env.model.uuidGenerator.smallUuid();
33350
+ const sheetNameTo = env.model.getters.getDuplicateSheetName(sheetNameFrom);
33217
33351
  env.model.dispatch("DUPLICATE_SHEET", {
33218
33352
  sheetId: sheetIdFrom,
33219
33353
  sheetIdTo,
33354
+ sheetNameTo,
33220
33355
  });
33221
33356
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom, sheetIdTo });
33222
33357
  },
@@ -40169,10 +40304,18 @@ stores.inject(MyMetaStore, storeInstance);
40169
40304
  }
40170
40305
 
40171
40306
  class DOMFocusableElementStore {
40172
- mutators = ["setFocusableElement"];
40307
+ mutators = ["setFocusableElement", "focus"];
40173
40308
  focusableElement = undefined;
40174
40309
  setFocusableElement(element) {
40175
40310
  this.focusableElement = element;
40311
+ return "noStateChange";
40312
+ }
40313
+ focus() {
40314
+ if (this.focusableElement === document.activeElement) {
40315
+ return "noStateChange";
40316
+ }
40317
+ this.focusableElement?.focus();
40318
+ return;
40176
40319
  }
40177
40320
  }
40178
40321
 
@@ -40762,7 +40905,7 @@ stores.inject(MyMetaStore, storeInstance);
40762
40905
  if (document.activeElement === this.contentHelper.el &&
40763
40906
  this.props.composerStore.editionMode === "inactive" &&
40764
40907
  !this.props.isDefaultFocus) {
40765
- this.DOMFocusableElementStore.focusableElement?.focus();
40908
+ this.DOMFocusableElementStore.focus();
40766
40909
  }
40767
40910
  });
40768
40911
  owl.useEffect(() => {
@@ -41036,6 +41179,13 @@ stores.inject(MyMetaStore, storeInstance);
41036
41179
  openAssistant() {
41037
41180
  this.assistant.forcedClosed = false;
41038
41181
  }
41182
+ onWheel(event) {
41183
+ // detect if scrollbar is available
41184
+ if (this.composerRef.el &&
41185
+ this.composerRef.el.scrollHeight > this.composerRef.el.clientHeight) {
41186
+ event.stopPropagation();
41187
+ }
41188
+ }
41039
41189
  // ---------------------------------------------------------------------------
41040
41190
  // Private
41041
41191
  // ---------------------------------------------------------------------------
@@ -45973,8 +46123,8 @@ stores.inject(MyMetaStore, storeInstance);
45973
46123
 
45974
46124
  const NULL_SYMBOL = Symbol("NULL");
45975
46125
  function createDate(dimension, value, locale) {
45976
- const granularity = dimension.granularity;
45977
- if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
46126
+ const granularity = dimension.granularity || "month";
46127
+ if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
45978
46128
  throw new Error(`Unknown date granularity: ${granularity}`);
45979
46129
  }
45980
46130
  const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
@@ -45993,6 +46143,9 @@ stores.inject(MyMetaStore, storeInstance);
45993
46143
  case "month_number":
45994
46144
  number = date.getMonth() + 1;
45995
46145
  break;
46146
+ case "month":
46147
+ number = Math.floor(toNumber(value, locale));
46148
+ break;
45996
46149
  case "iso_week_number":
45997
46150
  number = date.getIsoWeek();
45998
46151
  break;
@@ -46086,6 +46239,10 @@ stores.inject(MyMetaStore, storeInstance);
46086
46239
  set: new Set(),
46087
46240
  values: {},
46088
46241
  },
46242
+ month: {
46243
+ set: new Set(),
46244
+ values: {},
46245
+ },
46089
46246
  iso_week_number: {
46090
46247
  set: new Set(),
46091
46248
  values: {},
@@ -46296,7 +46453,7 @@ stores.inject(MyMetaStore, storeInstance);
46296
46453
  const cells = this.filterDataEntriesFromDomain(this.dataEntries, domain);
46297
46454
  const finalCell = cells[0]?.[dimension.nameWithGranularity];
46298
46455
  if (dimension.type === "datetime") {
46299
- const adapter = pivotTimeAdapter(dimension.granularity);
46456
+ const adapter = pivotTimeAdapter((dimension.granularity || "month"));
46300
46457
  return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
46301
46458
  }
46302
46459
  if (!finalCell) {
@@ -46414,7 +46571,7 @@ stores.inject(MyMetaStore, storeInstance);
46414
46571
  if (nonEmptyCells.length === 0) {
46415
46572
  return "integer";
46416
46573
  }
46417
- if (nonEmptyCells.every((cell) => cell.format && isDateTimeFormat(cell.format))) {
46574
+ if (nonEmptyCells.every((cell) => cell.type === CellValueType.number && cell.format && isDateTimeFormat(cell.format))) {
46418
46575
  return "datetime";
46419
46576
  }
46420
46577
  if (nonEmptyCells.every((cell) => cell.type === CellValueType.boolean)) {
@@ -46495,7 +46652,12 @@ stores.inject(MyMetaStore, storeInstance);
46495
46652
  entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
46496
46653
  }
46497
46654
  else {
46498
- entry[field.name] = cell;
46655
+ if (field.type === "char") {
46656
+ entry[field.name] = { ...cell, value: cell.formattedValue || null };
46657
+ }
46658
+ else {
46659
+ entry[field.name] = cell;
46660
+ }
46499
46661
  }
46500
46662
  }
46501
46663
  entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
@@ -46509,7 +46671,7 @@ stores.inject(MyMetaStore, storeInstance);
46509
46671
  for (const entry of dataEntries) {
46510
46672
  for (const dimension of dateDimensions) {
46511
46673
  const value = createDate(dimension, entry[dimension.fieldName]?.value || null, this.getters.getLocale());
46512
- const adapter = pivotTimeAdapter(dimension.granularity);
46674
+ const adapter = pivotTimeAdapter((dimension.granularity || "month"));
46513
46675
  const { format, value: valueToFormat } = adapter.toValueAndFormat(value, locale);
46514
46676
  entry[dimension.nameWithGranularity] = {
46515
46677
  value,
@@ -46529,6 +46691,7 @@ stores.inject(MyMetaStore, storeInstance);
46529
46691
  "year",
46530
46692
  "quarter_number",
46531
46693
  "month_number",
46694
+ "month",
46532
46695
  "iso_week_number",
46533
46696
  "day_of_month",
46534
46697
  "day",
@@ -46776,7 +46939,7 @@ stores.inject(MyMetaStore, storeInstance);
46776
46939
  : this.datetimeGranularities);
46777
46940
  }
46778
46941
  for (const field of dateFields) {
46779
- granularitiesPerFields[field.fieldName].delete(field.granularity);
46942
+ granularitiesPerFields[field.fieldName].delete(field.granularity || "month");
46780
46943
  }
46781
46944
  return granularitiesPerFields;
46782
46945
  }
@@ -48951,6 +49114,8 @@ stores.inject(MyMetaStore, storeInstance);
48951
49114
  }
48952
49115
  get composerProps() {
48953
49116
  const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
49117
+ // Remove the wrapper border width
49118
+ const maxHeight = this.props.gridDims.height - this.rect.y - 2 * COMPOSER_BORDER_WIDTH;
48954
49119
  return {
48955
49120
  rect: { ...this.rect },
48956
49121
  delimitation: {
@@ -48968,6 +49133,7 @@ stores.inject(MyMetaStore, storeInstance);
48968
49133
  }),
48969
49134
  onInputContextMenu: this.props.onInputContextMenu,
48970
49135
  composerStore: this.composerStore,
49136
+ inputStyle: `max-height: ${maxHeight}px;`,
48971
49137
  };
48972
49138
  }
48973
49139
  get containerStyle() {
@@ -51520,10 +51686,6 @@ stores.inject(MyMetaStore, storeInstance);
51520
51686
  ctx.scale(dpr, dpr);
51521
51687
  for (const layer of OrderedLayers()) {
51522
51688
  model.drawLayer(renderingContext, layer);
51523
- // @ts-ignore 'drawLayer' is not declated as a mutator because:
51524
- // it does not mutate anything. Most importantly it's used
51525
- // during rendering. Invoking a mutator during rendering would
51526
- // trigger another rendering, ultimately resulting in an infinite loop.
51527
51689
  rendererStore.drawLayer(renderingContext, layer);
51528
51690
  }
51529
51691
  }
@@ -52213,7 +52375,7 @@ stores.inject(MyMetaStore, storeInstance);
52213
52375
  this.cellPopovers = useStore(CellPopoverStore);
52214
52376
  owl.useEffect(() => {
52215
52377
  if (!this.sidePanel.isOpen) {
52216
- this.DOMFocusableElementStore.focusableElement?.focus();
52378
+ this.DOMFocusableElementStore.focus();
52217
52379
  }
52218
52380
  }, () => [this.sidePanel.isOpen]);
52219
52381
  useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
@@ -52423,7 +52585,7 @@ stores.inject(MyMetaStore, storeInstance);
52423
52585
  focusDefaultElement() {
52424
52586
  if (!this.env.model.getters.getSelectedFigureId() &&
52425
52587
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
52426
- this.DOMFocusableElementStore.focusableElement?.focus();
52588
+ this.DOMFocusableElementStore.focus();
52427
52589
  }
52428
52590
  }
52429
52591
  get gridEl() {
@@ -52768,6 +52930,322 @@ stores.inject(MyMetaStore, storeInstance);
52768
52930
  }
52769
52931
  }
52770
52932
 
52933
+ css /* scss */ `
52934
+ .o_pivot_html_renderer {
52935
+ width: 100%;
52936
+ border-collapse: collapse;
52937
+
52938
+ &:hover {
52939
+ cursor: pointer;
52940
+ }
52941
+
52942
+ td,
52943
+ th {
52944
+ border: 1px solid #dee2e6;
52945
+ background-color: #fff;
52946
+ padding: 0.3rem;
52947
+ white-space: nowrap;
52948
+
52949
+ &:hover {
52950
+ filter: brightness(0.9);
52951
+ }
52952
+ }
52953
+
52954
+ td {
52955
+ text-align: right;
52956
+ }
52957
+
52958
+ th {
52959
+ background-color: #f5f5f5;
52960
+ font-weight: bold;
52961
+ color: black;
52962
+ }
52963
+
52964
+ .o_missing_value {
52965
+ color: #46646d;
52966
+ background: #e7f2f6;
52967
+ }
52968
+ }
52969
+ `;
52970
+ class PivotHTMLRenderer extends owl.Component {
52971
+ static template = "o_spreadsheet.PivotHTMLRenderer";
52972
+ static components = { Checkbox };
52973
+ static props = {
52974
+ pivotId: String,
52975
+ onCellClicked: Function,
52976
+ };
52977
+ pivot = this.env.model.getters.getPivot(this.props.pivotId);
52978
+ data = {
52979
+ columns: [],
52980
+ rows: [],
52981
+ values: [],
52982
+ };
52983
+ state = owl.useState({
52984
+ showMissingValuesOnly: false,
52985
+ });
52986
+ setup() {
52987
+ const table = this.pivot.getTableStructure();
52988
+ const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
52989
+ this.data = {
52990
+ columns: this._buildColHeaders(formulaId, table),
52991
+ rows: this._buildRowHeaders(formulaId, table),
52992
+ values: this._buildValues(formulaId, table),
52993
+ };
52994
+ }
52995
+ get tracker() {
52996
+ return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
52997
+ }
52998
+ // ---------------------------------------------------------------------
52999
+ // Missing values building
53000
+ // ---------------------------------------------------------------------
53001
+ /**
53002
+ * Retrieve the data to display in the Pivot Table
53003
+ * In the case when showMissingValuesOnly is false, the returned value
53004
+ * is the complete data
53005
+ * In the case when showMissingValuesOnly is true, the returned value is
53006
+ * the data which contains only missing values in the rows and cols. In
53007
+ * the rows, we also return the parent rows of rows which contains missing
53008
+ * values, to give context to the user.
53009
+ *
53010
+ */
53011
+ getTableData() {
53012
+ if (!this.state.showMissingValuesOnly) {
53013
+ return this.data;
53014
+ }
53015
+ const colIndexes = this.getColumnsIndexes();
53016
+ const rowIndexes = this.getRowsIndexes();
53017
+ const columns = this.buildColumnsMissing(colIndexes);
53018
+ const rows = this.buildRowsMissing(rowIndexes);
53019
+ const values = this.buildValuesMissing(colIndexes, rowIndexes);
53020
+ return { columns, rows, values };
53021
+ }
53022
+ /**
53023
+ * Retrieve the parents of the given row
53024
+ * ex:
53025
+ * Australia
53026
+ * January
53027
+ * February
53028
+ * The parent of "January" is "Australia"
53029
+ */
53030
+ addRecursiveRow(index) {
53031
+ const rows = this.pivot.getTableStructure().rows;
53032
+ const row = [...rows[index].values];
53033
+ if (row.length <= 1) {
53034
+ return [index];
53035
+ }
53036
+ row.pop();
53037
+ const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
53038
+ return [index].concat(this.addRecursiveRow(parentRowIndex));
53039
+ }
53040
+ /**
53041
+ * Create the columns to be used, based on the indexes of the columns in
53042
+ * which a missing value is present
53043
+ *
53044
+ */
53045
+ buildColumnsMissing(indexes) {
53046
+ // columnsMap explode the columns in an array of array of the same
53047
+ // size with the index of each column, repeated 'span' times.
53048
+ // ex:
53049
+ // | A | B |
53050
+ // | 1 | 2 | 3 |
53051
+ // => [
53052
+ // [0, 0, 1]
53053
+ // [0, 1, 2]
53054
+ // ]
53055
+ const columnsMap = [];
53056
+ for (const column of this.data.columns) {
53057
+ const columnMap = [];
53058
+ for (const index in column) {
53059
+ for (let i = 0; i < column[index].span; i++) {
53060
+ columnMap.push(parseInt(index, 10));
53061
+ }
53062
+ }
53063
+ columnsMap.push(columnMap);
53064
+ }
53065
+ // Remove the columns that are not present in indexes
53066
+ for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
53067
+ if (!indexes.includes(i)) {
53068
+ for (const columnMap of columnsMap) {
53069
+ columnMap.splice(i, 1);
53070
+ }
53071
+ }
53072
+ }
53073
+ // Build the columns
53074
+ const columns = [];
53075
+ for (const mapIndex in columnsMap) {
53076
+ const column = [];
53077
+ let index = undefined;
53078
+ let span = 1;
53079
+ for (let i = 0; i < columnsMap[mapIndex].length; i++) {
53080
+ if (index !== columnsMap[mapIndex][i]) {
53081
+ if (index !== undefined) {
53082
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53083
+ }
53084
+ index = columnsMap[mapIndex][i];
53085
+ span = 1;
53086
+ }
53087
+ else {
53088
+ span++;
53089
+ }
53090
+ }
53091
+ if (index !== undefined) {
53092
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53093
+ }
53094
+ columns.push(column);
53095
+ }
53096
+ return columns;
53097
+ }
53098
+ /**
53099
+ * Create the rows to be used, based on the indexes of the rows in
53100
+ * which a missing value is present.
53101
+ */
53102
+ buildRowsMissing(indexes) {
53103
+ return indexes.map((index) => this.data.rows[index]);
53104
+ }
53105
+ /**
53106
+ * Create the value to be used, based on the indexes of the columns and
53107
+ * rows in which a missing value is present.
53108
+ */
53109
+ buildValuesMissing(colIndexes, rowIndexes) {
53110
+ const values = colIndexes.map(() => []);
53111
+ for (const row of rowIndexes) {
53112
+ for (const col in colIndexes) {
53113
+ values[col].push(this.data.values[colIndexes[col]][row]);
53114
+ }
53115
+ }
53116
+ return values;
53117
+ }
53118
+ getColumnsIndexes() {
53119
+ const indexes = new Set();
53120
+ for (let i = 0; i < this.data.columns.length; i++) {
53121
+ const exploded = [];
53122
+ for (let y = 0; y < this.data.columns[i].length; y++) {
53123
+ for (let x = 0; x < this.data.columns[i][y].span; x++) {
53124
+ exploded.push(this.data.columns[i][y]);
53125
+ }
53126
+ }
53127
+ for (let y = 0; y < exploded.length; y++) {
53128
+ if (exploded[y].isMissing) {
53129
+ indexes.add(y);
53130
+ }
53131
+ }
53132
+ }
53133
+ for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
53134
+ const values = this.data.values[i];
53135
+ if (values.find((x) => x.isMissing)) {
53136
+ indexes.add(i);
53137
+ }
53138
+ }
53139
+ return Array.from(indexes).sort((a, b) => a - b);
53140
+ }
53141
+ getRowsIndexes() {
53142
+ const rowIndexes = new Set();
53143
+ for (let i = 0; i < this.data.rows.length; i++) {
53144
+ if (this.data.rows[i].isMissing) {
53145
+ rowIndexes.add(i);
53146
+ }
53147
+ for (const col of this.data.values) {
53148
+ if (col[i].isMissing) {
53149
+ this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
53150
+ }
53151
+ }
53152
+ }
53153
+ return Array.from(rowIndexes).sort((a, b) => a - b);
53154
+ }
53155
+ // ---------------------------------------------------------------------
53156
+ // Data table creation
53157
+ // ---------------------------------------------------------------------
53158
+ _buildColHeaders(id, table) {
53159
+ const headers = [];
53160
+ for (const row of table.columns) {
53161
+ const current = [];
53162
+ for (const cell of row) {
53163
+ const args = [];
53164
+ for (let i = 0; i < cell.fields.length; i++) {
53165
+ args.push({ value: cell.fields[i] }, { value: cell.values[i] });
53166
+ }
53167
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53168
+ const locale = this.env.model.getters.getLocale();
53169
+ if (domain.at(-1)?.field === "measure") {
53170
+ const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
53171
+ current.push({
53172
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53173
+ value: formatValue(value, { format, locale }),
53174
+ span: cell.width,
53175
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53176
+ });
53177
+ }
53178
+ else {
53179
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53180
+ current.push({
53181
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53182
+ value: formatValue(value, { format, locale }),
53183
+ span: cell.width,
53184
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53185
+ });
53186
+ }
53187
+ }
53188
+ headers.push(current);
53189
+ }
53190
+ const last = headers[headers.length - 1];
53191
+ headers[headers.length - 1] = last.map((cell) => {
53192
+ if (!cell.isMissing) {
53193
+ cell.style = "color: #756f6f;";
53194
+ }
53195
+ return cell;
53196
+ });
53197
+ return headers;
53198
+ }
53199
+ _buildRowHeaders(id, table) {
53200
+ const headers = [];
53201
+ for (const row of table.rows) {
53202
+ const args = [];
53203
+ for (let i = 0; i < row.fields.length; i++) {
53204
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53205
+ }
53206
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53207
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53208
+ const locale = this.env.model.getters.getLocale();
53209
+ const cell = {
53210
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53211
+ value: formatValue(value, { format, locale }),
53212
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53213
+ };
53214
+ if (row.indent > 1) {
53215
+ cell.style = `padding-left: ${row.indent - 1 * 10}px`;
53216
+ }
53217
+ headers.push(cell);
53218
+ }
53219
+ return headers;
53220
+ }
53221
+ _buildValues(id, table) {
53222
+ const values = [];
53223
+ for (const col of table.columns.at(-1) || []) {
53224
+ const current = [];
53225
+ const measure = toString(col.values[col.values.length - 1]);
53226
+ for (const row of table.rows) {
53227
+ const args = [];
53228
+ for (let i = 0; i < row.fields.length; i++) {
53229
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53230
+ }
53231
+ for (let i = 0; i < col.fields.length - 1; i++) {
53232
+ args.push({ value: col.fields[i] }, { value: col.values[i] });
53233
+ }
53234
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53235
+ const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
53236
+ const locale = this.env.model.getters.getLocale();
53237
+ current.push({
53238
+ formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
53239
+ value: formatValue(value, { format, locale }),
53240
+ isMissing: !this.tracker?.isValuePresent(measure, domain),
53241
+ });
53242
+ }
53243
+ values.push(current);
53244
+ }
53245
+ return values;
53246
+ }
53247
+ }
53248
+
52771
53249
  /**
52772
53250
  * BasePlugin
52773
53251
  *
@@ -54258,9 +54736,7 @@ stores.inject(MyMetaStore, storeInstance);
54258
54736
  : "Success" /* CommandResult.Success */;
54259
54737
  }
54260
54738
  checkChartExists(cmd) {
54261
- return this.getters.getFigureSheetId(cmd.id)
54262
- ? "Success" /* CommandResult.Success */
54263
- : "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
54739
+ return this.isChartDefined(cmd.id) ? "Success" /* CommandResult.Success */ : "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
54264
54740
  }
54265
54741
  }
54266
54742
 
@@ -56207,7 +56683,7 @@ stores.inject(MyMetaStore, storeInstance);
56207
56683
  if (range.sheetId === cmd.sheetId) {
56208
56684
  return { changeType: "CHANGE", range };
56209
56685
  }
56210
- if (cmd.name && range.invalidSheetName === cmd.name) {
56686
+ if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
56211
56687
  const invalidSheetName = undefined;
56212
56688
  const sheetId = cmd.sheetId;
56213
56689
  const newRange = range.clone({ sheetId, invalidSheetName });
@@ -56545,6 +57021,7 @@ stores.inject(MyMetaStore, storeInstance);
56545
57021
  "getCommandZones",
56546
57022
  "getUnboundedZone",
56547
57023
  "checkElementsIncludeAllNonFrozenHeaders",
57024
+ "getDuplicateSheetName",
56548
57025
  ];
56549
57026
  sheetIdsMapName = {};
56550
57027
  orderedSheetIds = [];
@@ -56569,7 +57046,11 @@ stores.inject(MyMetaStore, storeInstance);
56569
57046
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
56570
57047
  }
56571
57048
  case "DUPLICATE_SHEET": {
56572
- return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
57049
+ if (this.sheets[cmd.sheetIdTo])
57050
+ return "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */;
57051
+ if (this.orderedSheetIds.map(this.getSheetName.bind(this)).includes(cmd.sheetNameTo))
57052
+ return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
57053
+ return "Success" /* CommandResult.Success */;
56573
57054
  }
56574
57055
  case "MOVE_SHEET":
56575
57056
  try {
@@ -56646,7 +57127,7 @@ stores.inject(MyMetaStore, storeInstance);
56646
57127
  this.showSheet(cmd.sheetId);
56647
57128
  break;
56648
57129
  case "DUPLICATE_SHEET":
56649
- this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo);
57130
+ this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo, cmd.sheetNameTo);
56650
57131
  break;
56651
57132
  case "DELETE_SHEET":
56652
57133
  this.deleteSheet(this.sheets[cmd.sheetId]);
@@ -56787,7 +57268,7 @@ stores.inject(MyMetaStore, storeInstance);
56787
57268
  if (name) {
56788
57269
  const unquotedName = getUnquotedSheetName(name);
56789
57270
  for (const key in this.sheetIdsMapName) {
56790
- if (key.toUpperCase() === unquotedName.toUpperCase()) {
57271
+ if (isSheetNameEqual(key, unquotedName)) {
56791
57272
  return this.sheetIdsMapName[key];
56792
57273
  }
56793
57274
  }
@@ -56852,14 +57333,8 @@ stores.inject(MyMetaStore, storeInstance);
56852
57333
  return dimension === "COL" ? this.getNumberCols(sheetId) : this.getNumberRows(sheetId);
56853
57334
  }
56854
57335
  getNextSheetName(baseName = "Sheet") {
56855
- let i = 1;
56856
57336
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56857
- let name = `${baseName}${i}`;
56858
- while (names.includes(name)) {
56859
- name = `${baseName}${i}`;
56860
- i++;
56861
- }
56862
- return name;
57337
+ return getNextSheetName(names, baseName);
56863
57338
  }
56864
57339
  getSheetSize(sheetId) {
56865
57340
  return {
@@ -57041,7 +57516,7 @@ stores.inject(MyMetaStore, storeInstance);
57041
57516
  }
57042
57517
  const { orderedSheetIds, sheets } = this;
57043
57518
  const name = cmd.name && cmd.name.trim().toLowerCase();
57044
- if (orderedSheetIds.find((id) => sheets[id]?.name.toLowerCase() === name && id !== cmd.sheetId)) {
57519
+ if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
57045
57520
  return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
57046
57521
  }
57047
57522
  if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
@@ -57105,9 +57580,8 @@ stores.inject(MyMetaStore, storeInstance);
57105
57580
  showSheet(sheetId) {
57106
57581
  this.history.update("sheets", sheetId, "isVisible", true);
57107
57582
  }
57108
- duplicateSheet(fromId, toId) {
57583
+ duplicateSheet(fromId, toId, toName) {
57109
57584
  const sheet = this.getSheet(fromId);
57110
- const toName = this.getDuplicateSheetName(sheet.name);
57111
57585
  const newSheet = deepCopy(sheet);
57112
57586
  newSheet.id = toId;
57113
57587
  newSheet.name = toName;
@@ -57139,15 +57613,8 @@ stores.inject(MyMetaStore, storeInstance);
57139
57613
  this.history.update("sheetIdsMapName", sheetIdsMapName);
57140
57614
  }
57141
57615
  getDuplicateSheetName(sheetName) {
57142
- let i = 1;
57143
57616
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
57144
- const baseName = _t("Copy of %s", sheetName);
57145
- let name = baseName.toString();
57146
- while (names.includes(name)) {
57147
- name = `${baseName} (${i})`;
57148
- i++;
57149
- }
57150
- return name;
57617
+ return getDuplicateSheetName(sheetName, names);
57151
57618
  }
57152
57619
  deleteSheet(sheet) {
57153
57620
  const name = sheet.name;
@@ -59974,8 +60441,8 @@ stores.inject(MyMetaStore, storeInstance);
59974
60441
  const EMPTY_ARRAY = [];
59975
60442
 
59976
60443
  const MAX_ITERATION = 30;
59977
- const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
59978
- const EMPTY_CELL = createEvaluatedCell({ value: null });
60444
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
60445
+ const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
59979
60446
  class Evaluator {
59980
60447
  context;
59981
60448
  getters;
@@ -65952,6 +66419,55 @@ stores.inject(MyMetaStore, storeInstance);
65952
66419
  }
65953
66420
  }
65954
66421
 
66422
+ class PivotPresenceTracker {
66423
+ trackedValues = new Set();
66424
+ domainToArray(domain) {
66425
+ return domain.flatMap((node) => [node.field, toString(node.value)]);
66426
+ }
66427
+ isValuePresent(measure, domain) {
66428
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66429
+ return this.trackedValues.has(key);
66430
+ }
66431
+ isHeaderPresent(domain) {
66432
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66433
+ return this.trackedValues.has(key);
66434
+ }
66435
+ trackValue(measure, domain) {
66436
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66437
+ this.trackedValues.add(key);
66438
+ }
66439
+ trackHeader(domain) {
66440
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66441
+ this.trackedValues.add(key);
66442
+ }
66443
+ }
66444
+
66445
+ class PivotPresencePlugin extends UIPlugin {
66446
+ static getters = ["getPivotPresenceTracker"];
66447
+ trackPresencePivotId;
66448
+ tracker;
66449
+ handle(cmd) {
66450
+ switch (cmd.type) {
66451
+ case "PIVOT_START_PRESENCE_TRACKING":
66452
+ this.tracker = new PivotPresenceTracker();
66453
+ this.trackPresencePivotId = cmd.pivotId;
66454
+ break;
66455
+ case "PIVOT_STOP_PRESENCE_TRACKING":
66456
+ this.trackPresencePivotId = undefined;
66457
+ break;
66458
+ }
66459
+ }
66460
+ getPivotPresenceTracker(pivotId) {
66461
+ if (this.trackPresencePivotId !== pivotId) {
66462
+ return undefined;
66463
+ }
66464
+ if (!this.tracker) {
66465
+ throw new Error("Tracker not initialized");
66466
+ }
66467
+ return this.tracker;
66468
+ }
66469
+ }
66470
+
65955
66471
  class SplitToColumnsPlugin extends UIPlugin {
65956
66472
  static getters = ["getAutomaticSeparator"];
65957
66473
  allowDispatch(cmd) {
@@ -68739,6 +69255,7 @@ stores.inject(MyMetaStore, storeInstance);
68739
69255
  .add("automatic_sum", AutomaticSumPlugin)
68740
69256
  .add("format", FormatPlugin)
68741
69257
  .add("insert_pivot", InsertPivotPlugin)
69258
+ .add("pivot_presence", PivotPresencePlugin)
68742
69259
  .add("split_to_columns", SplitToColumnsPlugin)
68743
69260
  .add("collaborative", CollaborativePlugin)
68744
69261
  .add("history", HistoryPlugin)
@@ -69119,11 +69636,11 @@ stores.inject(MyMetaStore, storeInstance);
69119
69636
  if (ev.key === "Enter") {
69120
69637
  ev.preventDefault();
69121
69638
  this.stopEdition();
69122
- this.DOMFocusableElementStore.focusableElement?.focus();
69639
+ this.DOMFocusableElementStore.focus();
69123
69640
  }
69124
69641
  if (ev.key === "Escape") {
69125
69642
  this.cancelEdition();
69126
- this.DOMFocusableElementStore.focusableElement?.focus();
69643
+ this.DOMFocusableElementStore.focus();
69127
69644
  }
69128
69645
  }
69129
69646
  onMouseEventSheetName(ev) {
@@ -71307,11 +71824,13 @@ stores.inject(MyMetaStore, storeInstance);
71307
71824
  this.checkViewportSize();
71308
71825
  stores.on("store-updated", this, render);
71309
71826
  resizeObserver.observe(this.spreadsheetRef.el);
71827
+ registerChartJSExtensions();
71310
71828
  });
71311
71829
  owl.onWillUnmount(() => {
71312
71830
  this.unbindModelEvents();
71313
71831
  stores.off("store-updated", this);
71314
71832
  resizeObserver.disconnect();
71833
+ unregisterChartJsExtensions();
71315
71834
  });
71316
71835
  owl.onPatched(() => {
71317
71836
  this.checkViewportSize();
@@ -75712,6 +76231,7 @@ stores.inject(MyMetaStore, storeInstance);
75712
76231
  PivotDimensionOrder,
75713
76232
  PivotDimension,
75714
76233
  PivotLayoutConfigurator,
76234
+ PivotHTMLRenderer,
75715
76235
  PivotDeferUpdate,
75716
76236
  PivotTitleSection,
75717
76237
  CogWheelMenu,
@@ -75805,9 +76325,9 @@ stores.inject(MyMetaStore, storeInstance);
75805
76325
  exports.tokenize = tokenize;
75806
76326
 
75807
76327
 
75808
- __info__.version = "18.1.18";
75809
- __info__.date = "2025-05-02T12:30:47.822Z";
75810
- __info__.hash = "29c21c6";
76328
+ __info__.version = "18.1.20";
76329
+ __info__.date = "2025-05-13T17:52:28.174Z";
76330
+ __info__.hash = "3e43a46";
75811
76331
 
75812
76332
 
75813
76333
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);