@odoo/o-spreadsheet 18.2.10 → 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.10
6
- * @date 2025-05-02T12:34:39.632Z
7
- * @hash e8ff3fc
5
+ * @version 18.2.12
6
+ * @date 2025-05-13T17:52:23.989Z
7
+ * @hash ba2ba9b
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -3358,7 +3358,7 @@
3358
3358
  */
3359
3359
  const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
3360
3360
  decimalSeparator = escapeRegExp(decimalSeparator);
3361
- return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3361
+ return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
3362
3362
  });
3363
3363
  const getNumberRegex = memoize(function getNumberRegex(locale) {
3364
3364
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
@@ -6281,6 +6281,32 @@
6281
6281
  })
6282
6282
  .filter(isDefined);
6283
6283
  }
6284
+ function getNextSheetName(existingNames, baseName = "Sheet") {
6285
+ let i = 1;
6286
+ let name = `${baseName}${i}`;
6287
+ while (existingNames.includes(name)) {
6288
+ name = `${baseName}${i}`;
6289
+ i++;
6290
+ }
6291
+ return name;
6292
+ }
6293
+ function getDuplicateSheetName(nameToDuplicate, existingNames) {
6294
+ let i = 1;
6295
+ const baseName = _t("Copy of %s", nameToDuplicate);
6296
+ let name = baseName.toString();
6297
+ while (existingNames.includes(name)) {
6298
+ name = `${baseName} (${i})`;
6299
+ i++;
6300
+ }
6301
+ return name;
6302
+ }
6303
+ function isSheetNameEqual(name1, name2) {
6304
+ if (name1 === undefined || name2 === undefined) {
6305
+ return false;
6306
+ }
6307
+ return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
6308
+ getUnquotedSheetName(name2.trim().toUpperCase()));
6309
+ }
6284
6310
 
6285
6311
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
6286
6312
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
@@ -7979,6 +8005,24 @@
7979
8005
  return `${normalizedValue}`;
7980
8006
  },
7981
8007
  };
8008
+ /**
8009
+ * normalizes month number + year
8010
+ */
8011
+ const monthAdapter = {
8012
+ normalizeFunctionValue(value) {
8013
+ const date = toNumber(value, DEFAULT_LOCALE);
8014
+ return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
8015
+ },
8016
+ toValueAndFormat(normalizedValue) {
8017
+ return {
8018
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
8019
+ format: "mmmm yyyy",
8020
+ };
8021
+ },
8022
+ toFunctionValue(normalizedValue) {
8023
+ return `"${normalizedValue}"`;
8024
+ },
8025
+ };
7982
8026
  /**
7983
8027
  * normalizes quarter number
7984
8028
  */
@@ -8109,6 +8153,7 @@
8109
8153
  .add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
8110
8154
  .add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
8111
8155
  .add("month_number", nullHandlerDecorator(monthNumberAdapter))
8156
+ .add("month", nullHandlerDecorator(monthAdapter))
8112
8157
  .add("quarter_number", nullHandlerDecorator(quarterNumberAdapter))
8113
8158
  .add("day_of_week", nullHandlerDecorator(dayOfWeekAdapter))
8114
8159
  .add("hour_number", nullHandlerDecorator(hourNumberAdapter))
@@ -8125,10 +8170,9 @@
8125
8170
  avg: _t("Average"),
8126
8171
  sum: _t("Sum"),
8127
8172
  };
8128
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8129
8173
  const AGGREGATORS_BY_FIELD_TYPE = {
8130
- integer: NUMBER_CHAR_AGGREGATORS,
8131
- char: NUMBER_CHAR_AGGREGATORS,
8174
+ integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
8175
+ char: ["count_distinct", "count"],
8132
8176
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8133
8177
  datetime: ["max", "min", "count_distinct", "count"],
8134
8178
  };
@@ -8289,10 +8333,7 @@
8289
8333
  return normalizer(groupValueString, dimension.granularity);
8290
8334
  }
8291
8335
  function normalizeDateTime(value, granularity) {
8292
- if (!granularity) {
8293
- throw new Error("Missing granularity");
8294
- }
8295
- return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
8336
+ return pivotTimeAdapter(granularity ?? "month").normalizeFunctionValue(value);
8296
8337
  }
8297
8338
  function toFunctionPivotValue(value, dimension) {
8298
8339
  if (value === null) {
@@ -8304,10 +8345,7 @@
8304
8345
  return pivotToFunctionValueRegistry.get(dimension.type)(value, dimension.granularity);
8305
8346
  }
8306
8347
  function toFunctionValueDateTime(value, granularity) {
8307
- if (!granularity) {
8308
- throw new Error("Missing granularity");
8309
- }
8310
- return pivotTimeAdapter(granularity).toFunctionValue(value);
8348
+ return pivotTimeAdapter(granularity ?? "month").toFunctionValue(value);
8311
8349
  }
8312
8350
  const pivotNormalizationValueRegistry = new Registry();
8313
8351
  pivotNormalizationValueRegistry
@@ -9487,7 +9525,10 @@ stores.inject(MyMetaStore, storeInstance);
9487
9525
  const functionProxy = new Proxy(value, {
9488
9526
  // trap the function call
9489
9527
  apply(target, thisArg, argArray) {
9490
- Reflect.apply(target, thisStore, argArray);
9528
+ const res = Reflect.apply(target, thisStore, argArray);
9529
+ if (res === "noStateChange") {
9530
+ return;
9531
+ }
9491
9532
  callback();
9492
9533
  },
9493
9534
  });
@@ -9509,7 +9550,7 @@ stores.inject(MyMetaStore, storeInstance);
9509
9550
  const ModelStore = createAbstractStore("Model");
9510
9551
 
9511
9552
  class RendererStore {
9512
- mutators = ["register", "unRegister"];
9553
+ mutators = ["register", "unRegister", "drawLayer"];
9513
9554
  renderers = {};
9514
9555
  register(renderer) {
9515
9556
  if (!renderer.renderingLayers.length) {
@@ -9529,14 +9570,14 @@ stores.inject(MyMetaStore, storeInstance);
9529
9570
  }
9530
9571
  drawLayer(context, layer) {
9531
9572
  const renderers = this.renderers[layer];
9532
- if (!renderers) {
9533
- return;
9534
- }
9535
- for (const renderer of renderers) {
9536
- context.ctx.save();
9537
- renderer.drawLayer(context, layer);
9538
- context.ctx.restore();
9573
+ if (renderers) {
9574
+ for (const renderer of renderers) {
9575
+ context.ctx.save();
9576
+ renderer.drawLayer(context, layer);
9577
+ context.ctx.restore();
9578
+ }
9539
9579
  }
9580
+ return "noStateChange";
9540
9581
  }
9541
9582
  }
9542
9583
 
@@ -9589,16 +9630,17 @@ stores.inject(MyMetaStore, storeInstance);
9589
9630
  focusComposer(listener, args) {
9590
9631
  this.activeComposer = listener;
9591
9632
  if (this.getters.isReadonly()) {
9592
- return;
9633
+ return "noStateChange";
9593
9634
  }
9594
9635
  this._focusMode = args.focusMode || "contentFocus";
9595
9636
  if (this._focusMode !== "inactive") {
9596
9637
  this.setComposerContent(args);
9597
9638
  }
9639
+ return;
9598
9640
  }
9599
9641
  focusActiveComposer(args) {
9600
9642
  if (this.getters.isReadonly()) {
9601
- return;
9643
+ return "noStateChange";
9602
9644
  }
9603
9645
  if (!this.activeComposer) {
9604
9646
  throw new Error("No composer is registered");
@@ -9607,6 +9649,7 @@ stores.inject(MyMetaStore, storeInstance);
9607
9649
  if (this._focusMode !== "inactive") {
9608
9650
  this.setComposerContent(args);
9609
9651
  }
9652
+ return;
9610
9653
  }
9611
9654
  /**
9612
9655
  * Start the edition or update the content if it's already started.
@@ -9766,12 +9809,24 @@ stores.inject(MyMetaStore, storeInstance);
9766
9809
  }
9767
9810
 
9768
9811
  const chartJsExtensionRegistry = new Registry();
9769
- /** Return window.Chart, making sure all our extensions are loaded in ChartJS */
9770
- function getChartJSConstructor() {
9771
- if (window.Chart && !window.Chart?.registry.plugins.get("chartShowValuesPlugin")) {
9772
- window.Chart.register(...chartJsExtensionRegistry.getAll());
9812
+ function areChartJSExtensionsLoaded() {
9813
+ return !!window.Chart.registry.plugins.get("chartShowValuesPlugin");
9814
+ }
9815
+ function registerChartJSExtensions() {
9816
+ if (!window.Chart || areChartJSExtensionsLoaded()) {
9817
+ return;
9818
+ }
9819
+ for (const registryItem of chartJsExtensionRegistry.getAll()) {
9820
+ registryItem.register(window.Chart);
9821
+ }
9822
+ }
9823
+ function unregisterChartJsExtensions() {
9824
+ if (!window.Chart) {
9825
+ return;
9826
+ }
9827
+ for (const registryItem of chartJsExtensionRegistry.getAll()) {
9828
+ registryItem.unregister(window.Chart);
9773
9829
  }
9774
- return window.Chart;
9775
9830
  }
9776
9831
 
9777
9832
  const TREND_LINE_XAXIS_ID = "x1";
@@ -10088,7 +10143,7 @@ stores.inject(MyMetaStore, storeInstance);
10088
10143
  }
10089
10144
  function formatChartDatasetValue(axisFormats, locale) {
10090
10145
  return (value, axisId) => {
10091
- const format = axisId ? axisFormats?.[axisId] : undefined;
10146
+ const format = axisFormats?.[axisId];
10092
10147
  return formatTickValue({ format, locale })(value);
10093
10148
  };
10094
10149
  }
@@ -10262,7 +10317,7 @@ stores.inject(MyMetaStore, storeInstance);
10262
10317
  const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10263
10318
  ctx.fillStyle = chartFontColor(options.background);
10264
10319
  ctx.strokeStyle = options.background || "#ffffff";
10265
- const displayValue = options.callback(value);
10320
+ const displayValue = options.callback(value, "y");
10266
10321
  drawTextWithBackground(displayValue, x, y, ctx);
10267
10322
  }
10268
10323
  }
@@ -10331,8 +10386,14 @@ stores.inject(MyMetaStore, storeInstance);
10331
10386
  }
10332
10387
  }
10333
10388
  `;
10334
- chartJsExtensionRegistry.add("chartShowValuesPlugin", chartShowValuesPlugin);
10335
- chartJsExtensionRegistry.add("waterfallLinesPlugin", waterfallLinesPlugin);
10389
+ chartJsExtensionRegistry.add("chartShowValuesPlugin", {
10390
+ register: (Chart) => Chart.register(chartShowValuesPlugin),
10391
+ unregister: (Chart) => Chart.unregister(chartShowValuesPlugin),
10392
+ });
10393
+ chartJsExtensionRegistry.add("waterfallLinesPlugin", {
10394
+ register: (Chart) => Chart.register(waterfallLinesPlugin),
10395
+ unregister: (Chart) => Chart.unregister(waterfallLinesPlugin),
10396
+ });
10336
10397
  class ChartJsComponent extends owl.Component {
10337
10398
  static template = "o-spreadsheet-ChartJsComponent";
10338
10399
  static props = {
@@ -10384,8 +10445,7 @@ stores.inject(MyMetaStore, storeInstance);
10384
10445
  createChart(chartData) {
10385
10446
  const canvas = this.canvas.el;
10386
10447
  const ctx = canvas.getContext("2d");
10387
- const Chart = getChartJSConstructor();
10388
- this.chart = new Chart(ctx, chartData);
10448
+ this.chart = new window.Chart(ctx, chartData);
10389
10449
  }
10390
10450
  updateChartJs(chartData) {
10391
10451
  if (chartData.data && chartData.data.datasets) {
@@ -18517,7 +18577,7 @@ stores.inject(MyMetaStore, storeInstance);
18517
18577
  return { value: "" };
18518
18578
  }
18519
18579
  if (result.value === null) {
18520
- result.value = "";
18580
+ return { ...result, value: "" };
18521
18581
  }
18522
18582
  return result;
18523
18583
  },
@@ -18538,7 +18598,7 @@ stores.inject(MyMetaStore, storeInstance);
18538
18598
  return { value: "" };
18539
18599
  }
18540
18600
  if (result.value === null) {
18541
- result.value = "";
18601
+ return { ...result, value: "" };
18542
18602
  }
18543
18603
  return result;
18544
18604
  },
@@ -18559,7 +18619,7 @@ stores.inject(MyMetaStore, storeInstance);
18559
18619
  return { value: "" };
18560
18620
  }
18561
18621
  if (result.value === null) {
18562
- result.value = "";
18622
+ return { ...result, value: "" };
18563
18623
  }
18564
18624
  return result;
18565
18625
  },
@@ -18585,7 +18645,7 @@ stores.inject(MyMetaStore, storeInstance);
18585
18645
  return { value: "" };
18586
18646
  }
18587
18647
  if (result.value === null) {
18588
- result.value = "";
18648
+ return { ...result, value: "" };
18589
18649
  }
18590
18650
  return result;
18591
18651
  }
@@ -18703,6 +18763,11 @@ stores.inject(MyMetaStore, storeInstance);
18703
18763
  if (range === undefined || range.invalidXc || range.invalidSheetName) {
18704
18764
  throw new InvalidReferenceError();
18705
18765
  }
18766
+ if (evalContext.__originCellPosition &&
18767
+ range.sheetId === evalContext.__originSheetId &&
18768
+ isZoneInside(positionToZone(evalContext.__originCellPosition), zone)) {
18769
+ throw new CircularDependencyError();
18770
+ }
18706
18771
  dependencies.push(range);
18707
18772
  }
18708
18773
  for (const measure of forMeasures) {
@@ -19151,6 +19216,9 @@ stores.inject(MyMetaStore, storeInstance);
19151
19216
  };
19152
19217
  }
19153
19218
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19219
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19220
+ this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
19221
+ }
19154
19222
  return pivot.getPivotCellValueAndFormat(_measure, domain);
19155
19223
  },
19156
19224
  };
@@ -19182,6 +19250,9 @@ stores.inject(MyMetaStore, storeInstance);
19182
19250
  };
19183
19251
  }
19184
19252
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
19253
+ if (this.getters.getActiveSheetId() === this.__originSheetId) {
19254
+ this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
19255
+ }
19185
19256
  const lastNode = domain.at(-1);
19186
19257
  if (lastNode?.field === "measure") {
19187
19258
  return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
@@ -19404,6 +19475,9 @@ stores.inject(MyMetaStore, storeInstance);
19404
19475
  return data === undefined || data.value === null;
19405
19476
  }
19406
19477
  const getNeutral = { number: 0, string: "", boolean: false };
19478
+ function areAlmostEqual(value1, value2, epsilon = 2e-16) {
19479
+ return Math.abs(value1 - value2) < epsilon;
19480
+ }
19407
19481
  const EQ = {
19408
19482
  description: _t("Equal."),
19409
19483
  args: [
@@ -19425,6 +19499,9 @@ stores.inject(MyMetaStore, storeInstance);
19425
19499
  if (typeof _value2 === "string") {
19426
19500
  _value2 = _value2.toUpperCase();
19427
19501
  }
19502
+ if (typeof _value1 === "number" && typeof _value2 === "number") {
19503
+ return { value: areAlmostEqual(_value1, _value2) };
19504
+ }
19428
19505
  return { value: _value1 === _value2 };
19429
19506
  },
19430
19507
  };
@@ -19464,6 +19541,9 @@ stores.inject(MyMetaStore, storeInstance);
19464
19541
  ],
19465
19542
  compute: function (value1, value2) {
19466
19543
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19544
+ if (typeof v1 === "number" && typeof v2 === "number") {
19545
+ return !areAlmostEqual(v1, v2) && v1 > v2;
19546
+ }
19467
19547
  return v1 > v2;
19468
19548
  });
19469
19549
  },
@@ -19479,6 +19559,9 @@ stores.inject(MyMetaStore, storeInstance);
19479
19559
  ],
19480
19560
  compute: function (value1, value2) {
19481
19561
  return applyRelationalOperator(value1, value2, (v1, v2) => {
19562
+ if (typeof v1 === "number" && typeof v2 === "number") {
19563
+ return areAlmostEqual(v1, v2) || v1 > v2;
19564
+ }
19482
19565
  return v1 >= v2;
19483
19566
  });
19484
19567
  },
@@ -21085,7 +21168,7 @@ stores.inject(MyMetaStore, storeInstance);
21085
21168
  .find((token) => {
21086
21169
  const { xc, sheetName: sheet } = splitReference(token.value);
21087
21170
  const sheetName = sheet || this.getters.getSheetName(this.sheetId);
21088
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
21171
+ if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
21089
21172
  return false;
21090
21173
  }
21091
21174
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
@@ -22964,6 +23047,7 @@ stores.inject(MyMetaStore, storeInstance);
22964
23047
  },
22965
23048
  },
22966
23049
  animation: false,
23050
+ events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "mouseup"],
22967
23051
  };
22968
23052
  function chartToImage(runtime, figure, type) {
22969
23053
  // wrap the canvas in a div with a fixed size because chart.js would
@@ -22980,8 +23064,7 @@ stores.inject(MyMetaStore, storeInstance);
22980
23064
  if ("chartJsConfig" in runtime) {
22981
23065
  const config = deepCopy(runtime.chartJsConfig);
22982
23066
  config.plugins = [backgroundColorChartJSPlugin];
22983
- const Chart = getChartJSConstructor();
22984
- const chart = new Chart(canvas, config);
23067
+ const chart = new window.Chart(canvas, config);
22985
23068
  const imgContent = chart.toBase64Image();
22986
23069
  chart.destroy();
22987
23070
  div.remove();
@@ -24547,7 +24630,7 @@ stores.inject(MyMetaStore, storeInstance);
24547
24630
  ({ xc, sheetName } = splitReference(reference));
24548
24631
  let rangeSheetIndex;
24549
24632
  if (sheetName) {
24550
- const index = data.sheets.findIndex((sheet) => sheet.name === sheetName);
24633
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
24551
24634
  if (index < 0) {
24552
24635
  throw new Error("Unable to find a sheet with the name " + sheetName);
24553
24636
  }
@@ -24888,7 +24971,7 @@ stores.inject(MyMetaStore, storeInstance);
24888
24971
  formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
24889
24972
  externalRefId = Number(externalRefId) - 1;
24890
24973
  cellRef = cellRef.replace(/\$/g, "");
24891
- const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name === sheetName);
24974
+ const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
24892
24975
  if (sheetIndex === -1) {
24893
24976
  return match;
24894
24977
  }
@@ -25539,7 +25622,7 @@ stores.inject(MyMetaStore, storeInstance);
25539
25622
  */
25540
25623
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25541
25624
  for (let tableSheet of convertedSheets) {
25542
- const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
25625
+ const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
25543
25626
  for (let table of tables) {
25544
25627
  const tabRef = table.name + "[";
25545
25628
  for (let sheet of convertedSheets) {
@@ -27974,6 +28057,7 @@ stores.inject(MyMetaStore, storeInstance);
27974
28057
  initialMessages = dropCommands(initialMessages, "SORT_CELLS");
27975
28058
  initialMessages = dropCommands(initialMessages, "SET_DECIMAL");
27976
28059
  initialMessages = fixChartDefinitions(data, initialMessages);
28060
+ initialMessages = fixTranslatedDuplicateSheetName(data, initialMessages);
27977
28061
  return initialMessages;
27978
28062
  }
27979
28063
  /**
@@ -28073,6 +28157,40 @@ stores.inject(MyMetaStore, storeInstance);
28073
28157
  }
28074
28158
  return messages;
28075
28159
  }
28160
+ function fixTranslatedDuplicateSheetName(data, initialMessages) {
28161
+ const sheetNames = {};
28162
+ for (const sheet of data.sheets || []) {
28163
+ sheetNames[sheet.id] = sheet.name;
28164
+ }
28165
+ const messages = [];
28166
+ for (const message of initialMessages) {
28167
+ if (message.type === "REMOTE_REVISION") {
28168
+ const commands = [];
28169
+ for (const cmd of message.commands) {
28170
+ switch (cmd.type) {
28171
+ case "DUPLICATE_SHEET":
28172
+ cmd.sheetNameTo =
28173
+ cmd.sheetNameTo ??
28174
+ getDuplicateSheetName(sheetNames[cmd.sheetId], Object.values(sheetNames));
28175
+ break;
28176
+ case "CREATE_SHEET":
28177
+ case "RENAME_SHEET":
28178
+ sheetNames[cmd.sheetId] = cmd.name || getNextSheetName(Object.values(sheetNames));
28179
+ break;
28180
+ }
28181
+ commands.push(cmd);
28182
+ }
28183
+ messages.push({
28184
+ ...message,
28185
+ commands,
28186
+ });
28187
+ }
28188
+ else {
28189
+ messages.push(message);
28190
+ }
28191
+ }
28192
+ return initialMessages;
28193
+ }
28076
28194
  // -----------------------------------------------------------------------------
28077
28195
  // Helpers
28078
28196
  // -----------------------------------------------------------------------------
@@ -28869,12 +28987,11 @@ stores.inject(MyMetaStore, storeInstance);
28869
28987
  }
28870
28988
  let missingTimeAdapterAlreadyWarned = false;
28871
28989
  function isLuxonTimeAdapterInstalled() {
28872
- const Chart = getChartJSConstructor();
28873
- if (!Chart) {
28990
+ if (!window.Chart) {
28874
28991
  return false;
28875
28992
  }
28876
28993
  // @ts-ignore
28877
- const adapter = new Chart._adapters._date({});
28994
+ const adapter = new window.Chart._adapters._date({});
28878
28995
  const isInstalled = adapter._id === "luxon";
28879
28996
  if (!isInstalled && !missingTimeAdapterAlreadyWarned) {
28880
28997
  missingTimeAdapterAlreadyWarned = true;
@@ -29512,6 +29629,9 @@ stores.inject(MyMetaStore, storeInstance);
29512
29629
  target.style.cursor = "default";
29513
29630
  },
29514
29631
  onClick: (event, legendItem, legend) => {
29632
+ if (event.type !== "click") {
29633
+ return;
29634
+ }
29515
29635
  const index = legendItem.datasetIndex;
29516
29636
  if (!legend.legendItems || index === undefined) {
29517
29637
  return;
@@ -32494,12 +32614,20 @@ stores.inject(MyMetaStore, storeInstance);
32494
32614
  }
32495
32615
  }
32496
32616
  hover(position) {
32617
+ if (position.col === this.col && position.row === this.row) {
32618
+ return "noStateChange";
32619
+ }
32497
32620
  this.col = position.col;
32498
32621
  this.row = position.row;
32622
+ return;
32499
32623
  }
32500
32624
  clear() {
32625
+ if (this.col === undefined && this.row === undefined) {
32626
+ return "noStateChange";
32627
+ }
32501
32628
  this.col = undefined;
32502
32629
  this.row = undefined;
32630
+ return;
32503
32631
  }
32504
32632
  }
32505
32633
 
@@ -32521,7 +32649,11 @@ stores.inject(MyMetaStore, storeInstance);
32521
32649
  this.persistentPopover = { col, row, sheetId, type };
32522
32650
  }
32523
32651
  close() {
32652
+ if (!this.persistentPopover) {
32653
+ return "noStateChange";
32654
+ }
32524
32655
  this.persistentPopover = undefined;
32656
+ return;
32525
32657
  }
32526
32658
  get persistentCellPopover() {
32527
32659
  return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
@@ -33411,10 +33543,13 @@ stores.inject(MyMetaStore, storeInstance);
33411
33543
  name: _t("Duplicate"),
33412
33544
  execute: (env) => {
33413
33545
  const sheetIdFrom = env.model.getters.getActiveSheetId();
33546
+ const sheetNameFrom = env.model.getters.getSheetName(sheetIdFrom);
33414
33547
  const sheetIdTo = env.model.uuidGenerator.smallUuid();
33548
+ const sheetNameTo = env.model.getters.getDuplicateSheetName(sheetNameFrom);
33415
33549
  env.model.dispatch("DUPLICATE_SHEET", {
33416
33550
  sheetId: sheetIdFrom,
33417
33551
  sheetIdTo,
33552
+ sheetNameTo,
33418
33553
  });
33419
33554
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom, sheetIdTo });
33420
33555
  },
@@ -38617,7 +38752,7 @@ stores.inject(MyMetaStore, storeInstance);
38617
38752
  const cancelledReasons = [
38618
38753
  ...(this.state.datasetDispatchResult?.reasons || []),
38619
38754
  ...(this.state.labelsDispatchResult?.reasons || []),
38620
- ];
38755
+ ].filter((reason) => reason !== "NoChanges" /* CommandResult.NoChanges */);
38621
38756
  return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
38622
38757
  }
38623
38758
  get isDatasetInvalid() {
@@ -39974,10 +40109,18 @@ stores.inject(MyMetaStore, storeInstance);
39974
40109
  }
39975
40110
 
39976
40111
  class DOMFocusableElementStore {
39977
- mutators = ["setFocusableElement"];
40112
+ mutators = ["setFocusableElement", "focus"];
39978
40113
  focusableElement = undefined;
39979
40114
  setFocusableElement(element) {
39980
40115
  this.focusableElement = element;
40116
+ return "noStateChange";
40117
+ }
40118
+ focus() {
40119
+ if (this.focusableElement === document.activeElement) {
40120
+ return "noStateChange";
40121
+ }
40122
+ this.focusableElement?.focus();
40123
+ return;
39981
40124
  }
39982
40125
  }
39983
40126
 
@@ -40565,7 +40708,7 @@ stores.inject(MyMetaStore, storeInstance);
40565
40708
  if (document.activeElement === this.contentHelper.el &&
40566
40709
  this.props.composerStore.editionMode === "inactive" &&
40567
40710
  !this.props.isDefaultFocus) {
40568
- this.DOMFocusableElementStore.focusableElement?.focus();
40711
+ this.DOMFocusableElementStore.focus();
40569
40712
  }
40570
40713
  });
40571
40714
  owl.useEffect(() => {
@@ -40839,6 +40982,13 @@ stores.inject(MyMetaStore, storeInstance);
40839
40982
  openAssistant() {
40840
40983
  this.assistant.forcedClosed = false;
40841
40984
  }
40985
+ onWheel(event) {
40986
+ // detect if scrollbar is available
40987
+ if (this.composerRef.el &&
40988
+ this.composerRef.el.scrollHeight > this.composerRef.el.clientHeight) {
40989
+ event.stopPropagation();
40990
+ }
40991
+ }
40842
40992
  // ---------------------------------------------------------------------------
40843
40993
  // Private
40844
40994
  // ---------------------------------------------------------------------------
@@ -46315,8 +46465,8 @@ stores.inject(MyMetaStore, storeInstance);
46315
46465
 
46316
46466
  const NULL_SYMBOL = Symbol("NULL");
46317
46467
  function createDate(dimension, value, locale) {
46318
- const granularity = dimension.granularity;
46319
- if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
46468
+ const granularity = dimension.granularity || "month";
46469
+ if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
46320
46470
  throw new Error(`Unknown date granularity: ${granularity}`);
46321
46471
  }
46322
46472
  const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
@@ -46335,6 +46485,9 @@ stores.inject(MyMetaStore, storeInstance);
46335
46485
  case "month_number":
46336
46486
  number = date.getMonth() + 1;
46337
46487
  break;
46488
+ case "month":
46489
+ number = Math.floor(toNumber(value, locale));
46490
+ break;
46338
46491
  case "iso_week_number":
46339
46492
  number = date.getIsoWeek();
46340
46493
  break;
@@ -46428,6 +46581,10 @@ stores.inject(MyMetaStore, storeInstance);
46428
46581
  set: new Set(),
46429
46582
  values: {},
46430
46583
  },
46584
+ month: {
46585
+ set: new Set(),
46586
+ values: {},
46587
+ },
46431
46588
  iso_week_number: {
46432
46589
  set: new Set(),
46433
46590
  values: {},
@@ -46638,7 +46795,7 @@ stores.inject(MyMetaStore, storeInstance);
46638
46795
  const cells = this.filterDataEntriesFromDomain(this.dataEntries, domain);
46639
46796
  const finalCell = cells[0]?.[dimension.nameWithGranularity];
46640
46797
  if (dimension.type === "datetime") {
46641
- const adapter = pivotTimeAdapter(dimension.granularity);
46798
+ const adapter = pivotTimeAdapter((dimension.granularity || "month"));
46642
46799
  return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
46643
46800
  }
46644
46801
  if (!finalCell) {
@@ -46756,7 +46913,7 @@ stores.inject(MyMetaStore, storeInstance);
46756
46913
  if (nonEmptyCells.length === 0) {
46757
46914
  return "integer";
46758
46915
  }
46759
- if (nonEmptyCells.every((cell) => cell.format && isDateTimeFormat(cell.format))) {
46916
+ if (nonEmptyCells.every((cell) => cell.type === CellValueType.number && cell.format && isDateTimeFormat(cell.format))) {
46760
46917
  return "datetime";
46761
46918
  }
46762
46919
  if (nonEmptyCells.every((cell) => cell.type === CellValueType.boolean)) {
@@ -46835,7 +46992,12 @@ stores.inject(MyMetaStore, storeInstance);
46835
46992
  entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
46836
46993
  }
46837
46994
  else {
46838
- entry[field.name] = cell;
46995
+ if (field.type === "char") {
46996
+ entry[field.name] = { ...cell, value: cell.formattedValue || null };
46997
+ }
46998
+ else {
46999
+ entry[field.name] = cell;
47000
+ }
46839
47001
  }
46840
47002
  }
46841
47003
  entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
@@ -46849,7 +47011,7 @@ stores.inject(MyMetaStore, storeInstance);
46849
47011
  for (const entry of dataEntries) {
46850
47012
  for (const dimension of dateDimensions) {
46851
47013
  const value = createDate(dimension, entry[dimension.fieldName]?.value || null, this.getters.getLocale());
46852
- const adapter = pivotTimeAdapter(dimension.granularity);
47014
+ const adapter = pivotTimeAdapter((dimension.granularity || "month"));
46853
47015
  const { format, value: valueToFormat } = adapter.toValueAndFormat(value, locale);
46854
47016
  entry[dimension.nameWithGranularity] = {
46855
47017
  value,
@@ -46869,6 +47031,7 @@ stores.inject(MyMetaStore, storeInstance);
46869
47031
  "year",
46870
47032
  "quarter_number",
46871
47033
  "month_number",
47034
+ "month",
46872
47035
  "iso_week_number",
46873
47036
  "day_of_month",
46874
47037
  "day",
@@ -47119,7 +47282,7 @@ stores.inject(MyMetaStore, storeInstance);
47119
47282
  : this.datetimeGranularities);
47120
47283
  }
47121
47284
  for (const field of dateFields) {
47122
- granularitiesPerFields[field.fieldName].delete(field.granularity);
47285
+ granularitiesPerFields[field.fieldName].delete(field.granularity || "month");
47123
47286
  }
47124
47287
  return granularitiesPerFields;
47125
47288
  }
@@ -49289,6 +49452,8 @@ stores.inject(MyMetaStore, storeInstance);
49289
49452
  }
49290
49453
  get composerProps() {
49291
49454
  const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
49455
+ // Remove the wrapper border width
49456
+ const maxHeight = this.props.gridDims.height - this.rect.y - 2 * COMPOSER_BORDER_WIDTH;
49292
49457
  return {
49293
49458
  rect: { ...this.rect },
49294
49459
  delimitation: {
@@ -49306,6 +49471,7 @@ stores.inject(MyMetaStore, storeInstance);
49306
49471
  }),
49307
49472
  onInputContextMenu: this.props.onInputContextMenu,
49308
49473
  composerStore: this.composerStore,
49474
+ inputStyle: `max-height: ${maxHeight}px;`,
49309
49475
  };
49310
49476
  }
49311
49477
  get containerStyle() {
@@ -51969,10 +52135,6 @@ stores.inject(MyMetaStore, storeInstance);
51969
52135
  ctx.scale(dpr, dpr);
51970
52136
  for (const layer of OrderedLayers()) {
51971
52137
  model.drawLayer(renderingContext, layer);
51972
- // @ts-ignore 'drawLayer' is not declated as a mutator because:
51973
- // it does not mutate anything. Most importantly it's used
51974
- // during rendering. Invoking a mutator during rendering would
51975
- // trigger another rendering, ultimately resulting in an infinite loop.
51976
52138
  rendererStore.drawLayer(renderingContext, layer);
51977
52139
  }
51978
52140
  }
@@ -52663,7 +52825,7 @@ stores.inject(MyMetaStore, storeInstance);
52663
52825
  this.cellPopovers = useStore(CellPopoverStore);
52664
52826
  owl.useEffect(() => {
52665
52827
  if (!this.sidePanel.isOpen) {
52666
- this.DOMFocusableElementStore.focusableElement?.focus();
52828
+ this.DOMFocusableElementStore.focus();
52667
52829
  }
52668
52830
  }, () => [this.sidePanel.isOpen]);
52669
52831
  useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
@@ -52873,7 +53035,7 @@ stores.inject(MyMetaStore, storeInstance);
52873
53035
  focusDefaultElement() {
52874
53036
  if (!this.env.model.getters.getSelectedFigureId() &&
52875
53037
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
52876
- this.DOMFocusableElementStore.focusableElement?.focus();
53038
+ this.DOMFocusableElementStore.focus();
52877
53039
  }
52878
53040
  }
52879
53041
  get gridEl() {
@@ -53218,6 +53380,322 @@ stores.inject(MyMetaStore, storeInstance);
53218
53380
  }
53219
53381
  }
53220
53382
 
53383
+ css /* scss */ `
53384
+ .o_pivot_html_renderer {
53385
+ width: 100%;
53386
+ border-collapse: collapse;
53387
+
53388
+ &:hover {
53389
+ cursor: pointer;
53390
+ }
53391
+
53392
+ td,
53393
+ th {
53394
+ border: 1px solid #dee2e6;
53395
+ background-color: #fff;
53396
+ padding: 0.3rem;
53397
+ white-space: nowrap;
53398
+
53399
+ &:hover {
53400
+ filter: brightness(0.9);
53401
+ }
53402
+ }
53403
+
53404
+ td {
53405
+ text-align: right;
53406
+ }
53407
+
53408
+ th {
53409
+ background-color: #f5f5f5;
53410
+ font-weight: bold;
53411
+ color: black;
53412
+ }
53413
+
53414
+ .o_missing_value {
53415
+ color: #46646d;
53416
+ background: #e7f2f6;
53417
+ }
53418
+ }
53419
+ `;
53420
+ class PivotHTMLRenderer extends owl.Component {
53421
+ static template = "o_spreadsheet.PivotHTMLRenderer";
53422
+ static components = { Checkbox };
53423
+ static props = {
53424
+ pivotId: String,
53425
+ onCellClicked: Function,
53426
+ };
53427
+ pivot = this.env.model.getters.getPivot(this.props.pivotId);
53428
+ data = {
53429
+ columns: [],
53430
+ rows: [],
53431
+ values: [],
53432
+ };
53433
+ state = owl.useState({
53434
+ showMissingValuesOnly: false,
53435
+ });
53436
+ setup() {
53437
+ const table = this.pivot.getTableStructure();
53438
+ const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
53439
+ this.data = {
53440
+ columns: this._buildColHeaders(formulaId, table),
53441
+ rows: this._buildRowHeaders(formulaId, table),
53442
+ values: this._buildValues(formulaId, table),
53443
+ };
53444
+ }
53445
+ get tracker() {
53446
+ return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
53447
+ }
53448
+ // ---------------------------------------------------------------------
53449
+ // Missing values building
53450
+ // ---------------------------------------------------------------------
53451
+ /**
53452
+ * Retrieve the data to display in the Pivot Table
53453
+ * In the case when showMissingValuesOnly is false, the returned value
53454
+ * is the complete data
53455
+ * In the case when showMissingValuesOnly is true, the returned value is
53456
+ * the data which contains only missing values in the rows and cols. In
53457
+ * the rows, we also return the parent rows of rows which contains missing
53458
+ * values, to give context to the user.
53459
+ *
53460
+ */
53461
+ getTableData() {
53462
+ if (!this.state.showMissingValuesOnly) {
53463
+ return this.data;
53464
+ }
53465
+ const colIndexes = this.getColumnsIndexes();
53466
+ const rowIndexes = this.getRowsIndexes();
53467
+ const columns = this.buildColumnsMissing(colIndexes);
53468
+ const rows = this.buildRowsMissing(rowIndexes);
53469
+ const values = this.buildValuesMissing(colIndexes, rowIndexes);
53470
+ return { columns, rows, values };
53471
+ }
53472
+ /**
53473
+ * Retrieve the parents of the given row
53474
+ * ex:
53475
+ * Australia
53476
+ * January
53477
+ * February
53478
+ * The parent of "January" is "Australia"
53479
+ */
53480
+ addRecursiveRow(index) {
53481
+ const rows = this.pivot.getTableStructure().rows;
53482
+ const row = [...rows[index].values];
53483
+ if (row.length <= 1) {
53484
+ return [index];
53485
+ }
53486
+ row.pop();
53487
+ const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
53488
+ return [index].concat(this.addRecursiveRow(parentRowIndex));
53489
+ }
53490
+ /**
53491
+ * Create the columns to be used, based on the indexes of the columns in
53492
+ * which a missing value is present
53493
+ *
53494
+ */
53495
+ buildColumnsMissing(indexes) {
53496
+ // columnsMap explode the columns in an array of array of the same
53497
+ // size with the index of each column, repeated 'span' times.
53498
+ // ex:
53499
+ // | A | B |
53500
+ // | 1 | 2 | 3 |
53501
+ // => [
53502
+ // [0, 0, 1]
53503
+ // [0, 1, 2]
53504
+ // ]
53505
+ const columnsMap = [];
53506
+ for (const column of this.data.columns) {
53507
+ const columnMap = [];
53508
+ for (const index in column) {
53509
+ for (let i = 0; i < column[index].span; i++) {
53510
+ columnMap.push(parseInt(index, 10));
53511
+ }
53512
+ }
53513
+ columnsMap.push(columnMap);
53514
+ }
53515
+ // Remove the columns that are not present in indexes
53516
+ for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
53517
+ if (!indexes.includes(i)) {
53518
+ for (const columnMap of columnsMap) {
53519
+ columnMap.splice(i, 1);
53520
+ }
53521
+ }
53522
+ }
53523
+ // Build the columns
53524
+ const columns = [];
53525
+ for (const mapIndex in columnsMap) {
53526
+ const column = [];
53527
+ let index = undefined;
53528
+ let span = 1;
53529
+ for (let i = 0; i < columnsMap[mapIndex].length; i++) {
53530
+ if (index !== columnsMap[mapIndex][i]) {
53531
+ if (index !== undefined) {
53532
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53533
+ }
53534
+ index = columnsMap[mapIndex][i];
53535
+ span = 1;
53536
+ }
53537
+ else {
53538
+ span++;
53539
+ }
53540
+ }
53541
+ if (index !== undefined) {
53542
+ column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
53543
+ }
53544
+ columns.push(column);
53545
+ }
53546
+ return columns;
53547
+ }
53548
+ /**
53549
+ * Create the rows to be used, based on the indexes of the rows in
53550
+ * which a missing value is present.
53551
+ */
53552
+ buildRowsMissing(indexes) {
53553
+ return indexes.map((index) => this.data.rows[index]);
53554
+ }
53555
+ /**
53556
+ * Create the value to be used, based on the indexes of the columns and
53557
+ * rows in which a missing value is present.
53558
+ */
53559
+ buildValuesMissing(colIndexes, rowIndexes) {
53560
+ const values = colIndexes.map(() => []);
53561
+ for (const row of rowIndexes) {
53562
+ for (const col in colIndexes) {
53563
+ values[col].push(this.data.values[colIndexes[col]][row]);
53564
+ }
53565
+ }
53566
+ return values;
53567
+ }
53568
+ getColumnsIndexes() {
53569
+ const indexes = new Set();
53570
+ for (let i = 0; i < this.data.columns.length; i++) {
53571
+ const exploded = [];
53572
+ for (let y = 0; y < this.data.columns[i].length; y++) {
53573
+ for (let x = 0; x < this.data.columns[i][y].span; x++) {
53574
+ exploded.push(this.data.columns[i][y]);
53575
+ }
53576
+ }
53577
+ for (let y = 0; y < exploded.length; y++) {
53578
+ if (exploded[y].isMissing) {
53579
+ indexes.add(y);
53580
+ }
53581
+ }
53582
+ }
53583
+ for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
53584
+ const values = this.data.values[i];
53585
+ if (values.find((x) => x.isMissing)) {
53586
+ indexes.add(i);
53587
+ }
53588
+ }
53589
+ return Array.from(indexes).sort((a, b) => a - b);
53590
+ }
53591
+ getRowsIndexes() {
53592
+ const rowIndexes = new Set();
53593
+ for (let i = 0; i < this.data.rows.length; i++) {
53594
+ if (this.data.rows[i].isMissing) {
53595
+ rowIndexes.add(i);
53596
+ }
53597
+ for (const col of this.data.values) {
53598
+ if (col[i].isMissing) {
53599
+ this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
53600
+ }
53601
+ }
53602
+ }
53603
+ return Array.from(rowIndexes).sort((a, b) => a - b);
53604
+ }
53605
+ // ---------------------------------------------------------------------
53606
+ // Data table creation
53607
+ // ---------------------------------------------------------------------
53608
+ _buildColHeaders(id, table) {
53609
+ const headers = [];
53610
+ for (const row of table.columns) {
53611
+ const current = [];
53612
+ for (const cell of row) {
53613
+ const args = [];
53614
+ for (let i = 0; i < cell.fields.length; i++) {
53615
+ args.push({ value: cell.fields[i] }, { value: cell.values[i] });
53616
+ }
53617
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53618
+ const locale = this.env.model.getters.getLocale();
53619
+ if (domain.at(-1)?.field === "measure") {
53620
+ const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
53621
+ current.push({
53622
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53623
+ value: formatValue(value, { format, locale }),
53624
+ span: cell.width,
53625
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53626
+ });
53627
+ }
53628
+ else {
53629
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53630
+ current.push({
53631
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53632
+ value: formatValue(value, { format, locale }),
53633
+ span: cell.width,
53634
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53635
+ });
53636
+ }
53637
+ }
53638
+ headers.push(current);
53639
+ }
53640
+ const last = headers[headers.length - 1];
53641
+ headers[headers.length - 1] = last.map((cell) => {
53642
+ if (!cell.isMissing) {
53643
+ cell.style = "color: #756f6f;";
53644
+ }
53645
+ return cell;
53646
+ });
53647
+ return headers;
53648
+ }
53649
+ _buildRowHeaders(id, table) {
53650
+ const headers = [];
53651
+ for (const row of table.rows) {
53652
+ const args = [];
53653
+ for (let i = 0; i < row.fields.length; i++) {
53654
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53655
+ }
53656
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53657
+ const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
53658
+ const locale = this.env.model.getters.getLocale();
53659
+ const cell = {
53660
+ formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
53661
+ value: formatValue(value, { format, locale }),
53662
+ isMissing: !this.tracker?.isHeaderPresent(domain),
53663
+ };
53664
+ if (row.indent > 1) {
53665
+ cell.style = `padding-left: ${row.indent - 1 * 10}px`;
53666
+ }
53667
+ headers.push(cell);
53668
+ }
53669
+ return headers;
53670
+ }
53671
+ _buildValues(id, table) {
53672
+ const values = [];
53673
+ for (const col of table.columns.at(-1) || []) {
53674
+ const current = [];
53675
+ const measure = toString(col.values[col.values.length - 1]);
53676
+ for (const row of table.rows) {
53677
+ const args = [];
53678
+ for (let i = 0; i < row.fields.length; i++) {
53679
+ args.push({ value: row.fields[i] }, { value: row.values[i] });
53680
+ }
53681
+ for (let i = 0; i < col.fields.length - 1; i++) {
53682
+ args.push({ value: col.fields[i] }, { value: col.values[i] });
53683
+ }
53684
+ const domain = this.pivot.parseArgsToPivotDomain(args);
53685
+ const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
53686
+ const locale = this.env.model.getters.getLocale();
53687
+ current.push({
53688
+ formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
53689
+ value: formatValue(value, { format, locale }),
53690
+ isMissing: !this.tracker?.isValuePresent(measure, domain),
53691
+ });
53692
+ }
53693
+ values.push(current);
53694
+ }
53695
+ return values;
53696
+ }
53697
+ }
53698
+
53221
53699
  /**
53222
53700
  * BasePlugin
53223
53701
  *
@@ -54568,7 +55046,7 @@ stores.inject(MyMetaStore, storeInstance);
54568
55046
  case "CREATE_CHART":
54569
55047
  return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartDuplicate));
54570
55048
  case "UPDATE_CHART":
54571
- return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartExists));
55049
+ return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartExists, this.checkChartChanged));
54572
55050
  default:
54573
55051
  return "Success" /* CommandResult.Success */;
54574
55052
  }
@@ -54723,9 +55201,12 @@ stores.inject(MyMetaStore, storeInstance);
54723
55201
  : "Success" /* CommandResult.Success */;
54724
55202
  }
54725
55203
  checkChartExists(cmd) {
54726
- return this.getters.getFigureSheetId(cmd.id)
54727
- ? "Success" /* CommandResult.Success */
54728
- : "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
55204
+ return this.isChartDefined(cmd.id) ? "Success" /* CommandResult.Success */ : "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
55205
+ }
55206
+ checkChartChanged(cmd) {
55207
+ return deepEquals(this.getChartDefinition(cmd.id), cmd.definition)
55208
+ ? "NoChanges" /* CommandResult.NoChanges */
55209
+ : "Success" /* CommandResult.Success */;
54729
55210
  }
54730
55211
  }
54731
55212
 
@@ -56674,7 +57155,7 @@ stores.inject(MyMetaStore, storeInstance);
56674
57155
  if (range.sheetId === cmd.sheetId) {
56675
57156
  return { changeType: "CHANGE", range };
56676
57157
  }
56677
- if (cmd.name && range.invalidSheetName === cmd.name) {
57158
+ if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
56678
57159
  const invalidSheetName = undefined;
56679
57160
  const sheetId = cmd.sheetId;
56680
57161
  const newRange = range.clone({ sheetId, invalidSheetName });
@@ -57044,6 +57525,7 @@ stores.inject(MyMetaStore, storeInstance);
57044
57525
  "getCommandZones",
57045
57526
  "getUnboundedZone",
57046
57527
  "checkElementsIncludeAllNonFrozenHeaders",
57528
+ "getDuplicateSheetName",
57047
57529
  ];
57048
57530
  sheetIdsMapName = {};
57049
57531
  orderedSheetIds = [];
@@ -57068,7 +57550,11 @@ stores.inject(MyMetaStore, storeInstance);
57068
57550
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
57069
57551
  }
57070
57552
  case "DUPLICATE_SHEET": {
57071
- return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
57553
+ if (this.sheets[cmd.sheetIdTo])
57554
+ return "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */;
57555
+ if (this.orderedSheetIds.map(this.getSheetName.bind(this)).includes(cmd.sheetNameTo))
57556
+ return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
57557
+ return "Success" /* CommandResult.Success */;
57072
57558
  }
57073
57559
  case "MOVE_SHEET":
57074
57560
  try {
@@ -57145,7 +57631,7 @@ stores.inject(MyMetaStore, storeInstance);
57145
57631
  this.showSheet(cmd.sheetId);
57146
57632
  break;
57147
57633
  case "DUPLICATE_SHEET":
57148
- this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo);
57634
+ this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo, cmd.sheetNameTo);
57149
57635
  break;
57150
57636
  case "DELETE_SHEET":
57151
57637
  this.deleteSheet(this.sheets[cmd.sheetId]);
@@ -57286,7 +57772,7 @@ stores.inject(MyMetaStore, storeInstance);
57286
57772
  if (name) {
57287
57773
  const unquotedName = getUnquotedSheetName(name);
57288
57774
  for (const key in this.sheetIdsMapName) {
57289
- if (key.toUpperCase() === unquotedName.toUpperCase()) {
57775
+ if (isSheetNameEqual(key, unquotedName)) {
57290
57776
  return this.sheetIdsMapName[key];
57291
57777
  }
57292
57778
  }
@@ -57352,10 +57838,7 @@ stores.inject(MyMetaStore, storeInstance);
57352
57838
  }
57353
57839
  getNextSheetName(baseName = "Sheet") {
57354
57840
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
57355
- return getUniqueText(baseName, names, {
57356
- compute: (name, i) => `${name}${i}`,
57357
- computeFirstOne: true,
57358
- });
57841
+ return getNextSheetName(names, baseName);
57359
57842
  }
57360
57843
  getSheetSize(sheetId) {
57361
57844
  return {
@@ -57537,7 +58020,7 @@ stores.inject(MyMetaStore, storeInstance);
57537
58020
  }
57538
58021
  const { orderedSheetIds, sheets } = this;
57539
58022
  const name = cmd.name && cmd.name.trim().toLowerCase();
57540
- if (orderedSheetIds.find((id) => sheets[id]?.name.toLowerCase() === name && id !== cmd.sheetId)) {
58023
+ if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
57541
58024
  return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
57542
58025
  }
57543
58026
  if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
@@ -57601,9 +58084,8 @@ stores.inject(MyMetaStore, storeInstance);
57601
58084
  showSheet(sheetId) {
57602
58085
  this.history.update("sheets", sheetId, "isVisible", true);
57603
58086
  }
57604
- duplicateSheet(fromId, toId) {
58087
+ duplicateSheet(fromId, toId, toName) {
57605
58088
  const sheet = this.getSheet(fromId);
57606
- const toName = this.getDuplicateSheetName(sheet.name);
57607
58089
  const newSheet = deepCopy(sheet);
57608
58090
  newSheet.id = toId;
57609
58091
  newSheet.name = toName;
@@ -57636,8 +58118,7 @@ stores.inject(MyMetaStore, storeInstance);
57636
58118
  }
57637
58119
  getDuplicateSheetName(sheetName) {
57638
58120
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
57639
- const baseName = _t("Copy of %s", sheetName);
57640
- return getUniqueText(baseName.toString(), names);
58121
+ return getDuplicateSheetName(sheetName, names);
57641
58122
  }
57642
58123
  deleteSheet(sheet) {
57643
58124
  const name = sheet.name;
@@ -60432,8 +60913,8 @@ stores.inject(MyMetaStore, storeInstance);
60432
60913
  const EMPTY_ARRAY = [];
60433
60914
 
60434
60915
  const MAX_ITERATION = 30;
60435
- const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
60436
- const EMPTY_CELL = createEvaluatedCell({ value: null });
60916
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
60917
+ const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
60437
60918
  class Evaluator {
60438
60919
  context;
60439
60920
  getters;
@@ -66432,6 +66913,55 @@ stores.inject(MyMetaStore, storeInstance);
66432
66913
  }
66433
66914
  }
66434
66915
 
66916
+ class PivotPresenceTracker {
66917
+ trackedValues = new Set();
66918
+ domainToArray(domain) {
66919
+ return domain.flatMap((node) => [node.field, toString(node.value)]);
66920
+ }
66921
+ isValuePresent(measure, domain) {
66922
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66923
+ return this.trackedValues.has(key);
66924
+ }
66925
+ isHeaderPresent(domain) {
66926
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66927
+ return this.trackedValues.has(key);
66928
+ }
66929
+ trackValue(measure, domain) {
66930
+ const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
66931
+ this.trackedValues.add(key);
66932
+ }
66933
+ trackHeader(domain) {
66934
+ const key = JSON.stringify({ domain: this.domainToArray(domain) });
66935
+ this.trackedValues.add(key);
66936
+ }
66937
+ }
66938
+
66939
+ class PivotPresencePlugin extends UIPlugin {
66940
+ static getters = ["getPivotPresenceTracker"];
66941
+ trackPresencePivotId;
66942
+ tracker;
66943
+ handle(cmd) {
66944
+ switch (cmd.type) {
66945
+ case "PIVOT_START_PRESENCE_TRACKING":
66946
+ this.tracker = new PivotPresenceTracker();
66947
+ this.trackPresencePivotId = cmd.pivotId;
66948
+ break;
66949
+ case "PIVOT_STOP_PRESENCE_TRACKING":
66950
+ this.trackPresencePivotId = undefined;
66951
+ break;
66952
+ }
66953
+ }
66954
+ getPivotPresenceTracker(pivotId) {
66955
+ if (this.trackPresencePivotId !== pivotId) {
66956
+ return undefined;
66957
+ }
66958
+ if (!this.tracker) {
66959
+ throw new Error("Tracker not initialized");
66960
+ }
66961
+ return this.tracker;
66962
+ }
66963
+ }
66964
+
66435
66965
  class SplitToColumnsPlugin extends UIPlugin {
66436
66966
  static getters = ["getAutomaticSeparator"];
66437
66967
  allowDispatch(cmd) {
@@ -69182,6 +69712,7 @@ stores.inject(MyMetaStore, storeInstance);
69182
69712
  .add("automatic_sum", AutomaticSumPlugin)
69183
69713
  .add("format", FormatPlugin)
69184
69714
  .add("insert_pivot", InsertPivotPlugin)
69715
+ .add("pivot_presence", PivotPresencePlugin)
69185
69716
  .add("split_to_columns", SplitToColumnsPlugin)
69186
69717
  .add("collaborative", CollaborativePlugin)
69187
69718
  .add("history", HistoryPlugin)
@@ -69562,11 +70093,11 @@ stores.inject(MyMetaStore, storeInstance);
69562
70093
  if (ev.key === "Enter") {
69563
70094
  ev.preventDefault();
69564
70095
  this.stopEdition();
69565
- this.DOMFocusableElementStore.focusableElement?.focus();
70096
+ this.DOMFocusableElementStore.focus();
69566
70097
  }
69567
70098
  if (ev.key === "Escape") {
69568
70099
  this.cancelEdition();
69569
- this.DOMFocusableElementStore.focusableElement?.focus();
70100
+ this.DOMFocusableElementStore.focus();
69570
70101
  }
69571
70102
  }
69572
70103
  onMouseEventSheetName(ev) {
@@ -71738,11 +72269,13 @@ stores.inject(MyMetaStore, storeInstance);
71738
72269
  this.checkViewportSize();
71739
72270
  stores.on("store-updated", this, render);
71740
72271
  resizeObserver.observe(this.spreadsheetRef.el);
72272
+ registerChartJSExtensions();
71741
72273
  });
71742
72274
  owl.onWillUnmount(() => {
71743
72275
  this.unbindModelEvents();
71744
72276
  stores.off("store-updated", this);
71745
72277
  resizeObserver.disconnect();
72278
+ unregisterChartJsExtensions();
71746
72279
  });
71747
72280
  owl.onPatched(() => {
71748
72281
  this.checkViewportSize();
@@ -76175,6 +76708,7 @@ stores.inject(MyMetaStore, storeInstance);
76175
76708
  PivotDimensionOrder,
76176
76709
  PivotDimension,
76177
76710
  PivotLayoutConfigurator,
76711
+ PivotHTMLRenderer,
76178
76712
  PivotDeferUpdate,
76179
76713
  PivotTitleSection,
76180
76714
  CogWheelMenu,
@@ -76269,9 +76803,9 @@ stores.inject(MyMetaStore, storeInstance);
76269
76803
  exports.tokenize = tokenize;
76270
76804
 
76271
76805
 
76272
- __info__.version = "18.2.10";
76273
- __info__.date = "2025-05-02T12:34:39.632Z";
76274
- __info__.hash = "e8ff3fc";
76806
+ __info__.version = "18.2.12";
76807
+ __info__.date = "2025-05-13T17:52:23.989Z";
76808
+ __info__.hash = "ba2ba9b";
76275
76809
 
76276
76810
 
76277
76811
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);