@odoo/o-spreadsheet 17.4.33 → 17.4.35

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 17.4.33
6
- * @date 2025-05-02T12:30:31.263Z
7
- * @hash 3ba00e6
5
+ * @version 17.4.35
6
+ * @date 2025-05-13T17:49:44.553Z
7
+ * @hash 534c5f4
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -1914,7 +1914,7 @@ function isDateAfter(date, dateAfter) {
1914
1914
  */
1915
1915
  const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
1916
1916
  decimalSeparator = escapeRegExp(decimalSeparator);
1917
- return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
1917
+ return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
1918
1918
  });
1919
1919
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1920
1920
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
@@ -5190,6 +5190,32 @@ function moveHeaderIndexesOnHeaderDeletion(deletedHeaders, headers) {
5190
5190
  })
5191
5191
  .filter(isDefined);
5192
5192
  }
5193
+ function getNextSheetName(existingNames, baseName = "Sheet") {
5194
+ let i = 1;
5195
+ let name = `${baseName}${i}`;
5196
+ while (existingNames.includes(name)) {
5197
+ name = `${baseName}${i}`;
5198
+ i++;
5199
+ }
5200
+ return name;
5201
+ }
5202
+ function getDuplicateSheetName(nameToDuplicate, existingNames) {
5203
+ let i = 1;
5204
+ const baseName = _t("Copy of %s", nameToDuplicate);
5205
+ let name = baseName.toString();
5206
+ while (existingNames.includes(name)) {
5207
+ name = `${baseName} (${i})`;
5208
+ i++;
5209
+ }
5210
+ return name;
5211
+ }
5212
+ function isSheetNameEqual(name1, name2) {
5213
+ if (name1 === undefined || name2 === undefined) {
5214
+ return false;
5215
+ }
5216
+ return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
5217
+ getUnquotedSheetName(name2.trim().toUpperCase()));
5218
+ }
5193
5219
 
5194
5220
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
5195
5221
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
@@ -6393,6 +6419,24 @@ const monthNumberAdapter = {
6393
6419
  return `${normalizedValue}`;
6394
6420
  },
6395
6421
  };
6422
+ /**
6423
+ * normalizes month number + year
6424
+ */
6425
+ const monthAdapter = {
6426
+ normalizeFunctionValue(value) {
6427
+ const date = toNumber(value, DEFAULT_LOCALE);
6428
+ return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
6429
+ },
6430
+ toValueAndFormat(normalizedValue) {
6431
+ return {
6432
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6433
+ format: "mmmm yyyy",
6434
+ };
6435
+ },
6436
+ toFunctionValue(normalizedValue) {
6437
+ return `"${normalizedValue}"`;
6438
+ },
6439
+ };
6396
6440
  /**
6397
6441
  * normalizes quarter number
6398
6442
  */
@@ -6460,6 +6504,7 @@ pivotTimeAdapterRegistry
6460
6504
  .add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
6461
6505
  .add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
6462
6506
  .add("month_number", nullHandlerDecorator(monthNumberAdapter))
6507
+ .add("month", nullHandlerDecorator(monthAdapter))
6463
6508
  .add("quarter_number", nullHandlerDecorator(quarterNumberAdapter));
6464
6509
 
6465
6510
  const AGGREGATOR_NAMES = {
@@ -6472,10 +6517,9 @@ const AGGREGATOR_NAMES = {
6472
6517
  avg: _t("Average"),
6473
6518
  sum: _t("Sum"),
6474
6519
  };
6475
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
6476
6520
  const AGGREGATORS_BY_FIELD_TYPE = {
6477
- integer: NUMBER_CHAR_AGGREGATORS,
6478
- char: NUMBER_CHAR_AGGREGATORS,
6521
+ integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
6522
+ char: ["count_distinct", "count"],
6479
6523
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
6480
6524
  };
6481
6525
  const AGGREGATORS = {};
@@ -6640,10 +6684,7 @@ function toNormalizedPivotValue(dimension, groupValue) {
6640
6684
  return normalizer(groupValueString, dimension.granularity);
6641
6685
  }
6642
6686
  function normalizeDateTime(value, granularity) {
6643
- if (!granularity) {
6644
- throw new Error("Missing granularity");
6645
- }
6646
- return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
6687
+ return pivotTimeAdapter(granularity ?? "month").normalizeFunctionValue(value);
6647
6688
  }
6648
6689
  function toFunctionPivotValue(value, dimension) {
6649
6690
  if (value === null) {
@@ -6655,10 +6696,7 @@ function toFunctionPivotValue(value, dimension) {
6655
6696
  return pivotToFunctionValueRegistry.get(dimension.type)(value, dimension.granularity);
6656
6697
  }
6657
6698
  function toFunctionValueDateTime(value, granularity) {
6658
- if (!granularity) {
6659
- throw new Error("Missing granularity");
6660
- }
6661
- return pivotTimeAdapter(granularity).toFunctionValue(value);
6699
+ return pivotTimeAdapter(granularity ?? "month").toFunctionValue(value);
6662
6700
  }
6663
6701
  const pivotNormalizationValueRegistry = new Registry();
6664
6702
  pivotNormalizationValueRegistry
@@ -6695,8 +6733,11 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
6695
6733
  const evaluatedCell = this.getters.getEvaluatedCell(position);
6696
6734
  const pivotId = this.getters.getPivotIdFromPosition(position);
6697
6735
  const spreader = this.getters.getArrayFormulaSpreadingOn(position);
6698
- if (pivotId) {
6699
- if (spreader && (!deepEquals(spreader, position) || !isCopyingOneCell)) {
6736
+ if (pivotId && spreader) {
6737
+ const pivotZone = this.getters.getSpreadZone(spreader);
6738
+ if ((!deepEquals(spreader, position) || !isCopyingOneCell) &&
6739
+ pivotZone &&
6740
+ !data.zones.some((z) => isZoneInside(pivotZone, z))) {
6700
6741
  const pivotCell = this.getters.getPivotCellFromPosition(position);
6701
6742
  const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
6702
6743
  const pivotFormula = createPivotFormula(formulaPivotId, pivotCell);
@@ -9418,7 +9459,10 @@ function proxifyStoreMutation(store, callback) {
9418
9459
  const functionProxy = new Proxy(value, {
9419
9460
  // trap the function call
9420
9461
  apply(target, thisArg, argArray) {
9421
- Reflect.apply(target, thisStore, argArray);
9462
+ const res = Reflect.apply(target, thisStore, argArray);
9463
+ if (res === "noStateChange") {
9464
+ return;
9465
+ }
9422
9466
  callback();
9423
9467
  },
9424
9468
  });
@@ -9440,7 +9484,7 @@ function getDependencyContainer(env) {
9440
9484
  const ModelStore = createAbstractStore("Model");
9441
9485
 
9442
9486
  class RendererStore {
9443
- mutators = ["register", "unRegister"];
9487
+ mutators = ["register", "unRegister", "drawLayer"];
9444
9488
  renderers = {};
9445
9489
  register(renderer) {
9446
9490
  if (!renderer.renderingLayers.length) {
@@ -9460,14 +9504,14 @@ class RendererStore {
9460
9504
  }
9461
9505
  drawLayer(context, layer) {
9462
9506
  const renderers = this.renderers[layer];
9463
- if (!renderers) {
9464
- return;
9465
- }
9466
- for (const renderer of renderers) {
9467
- context.ctx.save();
9468
- renderer.drawLayer(context, layer);
9469
- context.ctx.restore();
9507
+ if (renderers) {
9508
+ for (const renderer of renderers) {
9509
+ context.ctx.save();
9510
+ renderer.drawLayer(context, layer);
9511
+ context.ctx.restore();
9512
+ }
9470
9513
  }
9514
+ return "noStateChange";
9471
9515
  }
9472
9516
  }
9473
9517
 
@@ -10080,7 +10124,7 @@ class ComposerStore extends SpreadsheetStore {
10080
10124
  .find((token) => {
10081
10125
  const { xc, sheetName: sheet } = splitReference(token.value);
10082
10126
  const sheetName = sheet || this.getters.getSheetName(this.sheetId);
10083
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
10127
+ if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
10084
10128
  return false;
10085
10129
  }
10086
10130
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
@@ -10385,27 +10429,30 @@ class ComposerFocusStore extends SpreadsheetStore {
10385
10429
  }
10386
10430
  focusTopBarComposer(selection) {
10387
10431
  if (this.getters.isReadonly()) {
10388
- return;
10432
+ return "noStateChange";
10389
10433
  }
10390
10434
  this.topBarFocus = "contentFocus";
10391
10435
  this.gridFocusMode = "inactive";
10392
- this.setComposerContent({ selection } || {});
10436
+ this.setComposerContent({ selection });
10437
+ return;
10393
10438
  }
10394
10439
  focusGridComposerContent() {
10395
10440
  if (this.getters.isReadonly()) {
10396
- return;
10441
+ return "noStateChange";
10397
10442
  }
10398
10443
  this.topBarFocus = "inactive";
10399
10444
  this.gridFocusMode = "contentFocus";
10400
10445
  this.setComposerContent({});
10446
+ return;
10401
10447
  }
10402
10448
  focusGridComposerCell(content, selection) {
10403
10449
  if (this.getters.isReadonly()) {
10404
- return;
10450
+ return "noStateChange";
10405
10451
  }
10406
10452
  this.topBarFocus = "inactive";
10407
10453
  this.gridFocusMode = "cellFocus";
10408
- this.setComposerContent({ content, selection } || {});
10454
+ this.setComposerContent({ content, selection });
10455
+ return;
10409
10456
  }
10410
10457
  /**
10411
10458
  * Start the edition or update the content if it's already started.
@@ -19382,7 +19429,7 @@ const IF = {
19382
19429
  return { value: "" };
19383
19430
  }
19384
19431
  if (result.value === null) {
19385
- result.value = "";
19432
+ return { ...result, value: "" };
19386
19433
  }
19387
19434
  return result;
19388
19435
  },
@@ -19403,7 +19450,7 @@ const IFERROR = {
19403
19450
  return { value: "" };
19404
19451
  }
19405
19452
  if (result.value === null) {
19406
- result.value = "";
19453
+ return { ...result, value: "" };
19407
19454
  }
19408
19455
  return result;
19409
19456
  },
@@ -19424,7 +19471,7 @@ const IFNA = {
19424
19471
  return { value: "" };
19425
19472
  }
19426
19473
  if (result.value === null) {
19427
- result.value = "";
19474
+ return { ...result, value: "" };
19428
19475
  }
19429
19476
  return result;
19430
19477
  },
@@ -19450,7 +19497,7 @@ const IFS = {
19450
19497
  return { value: "" };
19451
19498
  }
19452
19499
  if (result.value === null) {
19453
- result.value = "";
19500
+ return { ...result, value: "" };
19454
19501
  }
19455
19502
  return result;
19456
19503
  }
@@ -20290,6 +20337,9 @@ function isEmpty(data) {
20290
20337
  return data === undefined || data.value === null;
20291
20338
  }
20292
20339
  const getNeutral = { number: 0, string: "", boolean: false };
20340
+ function areAlmostEqual(value1, value2, epsilon = 2e-16) {
20341
+ return Math.abs(value1 - value2) < epsilon;
20342
+ }
20293
20343
  const EQ = {
20294
20344
  description: _t("Equal."),
20295
20345
  args: [
@@ -20311,6 +20361,9 @@ const EQ = {
20311
20361
  if (isEvaluationError(_value2)) {
20312
20362
  throw value2;
20313
20363
  }
20364
+ if (typeof _value1 === "number" && typeof _value2 === "number") {
20365
+ return areAlmostEqual(_value1, _value2);
20366
+ }
20314
20367
  return _value1 === _value2;
20315
20368
  },
20316
20369
  };
@@ -20350,6 +20403,9 @@ const GT = {
20350
20403
  ],
20351
20404
  compute: function (value1, value2) {
20352
20405
  return applyRelationalOperator(value1, value2, (v1, v2) => {
20406
+ if (typeof v1 === "number" && typeof v2 === "number") {
20407
+ return !areAlmostEqual(v1, v2) && v1 > v2;
20408
+ }
20353
20409
  return v1 > v2;
20354
20410
  });
20355
20411
  },
@@ -20365,6 +20421,9 @@ const GTE = {
20365
20421
  ],
20366
20422
  compute: function (value1, value2) {
20367
20423
  return applyRelationalOperator(value1, value2, (v1, v2) => {
20424
+ if (typeof v1 === "number" && typeof v2 === "number") {
20425
+ return areAlmostEqual(v1, v2) || v1 > v2;
20426
+ }
20368
20427
  return v1 >= v2;
20369
20428
  });
20370
20429
  },
@@ -21465,10 +21524,18 @@ autoCompleteProviders.add("functions", {
21465
21524
  });
21466
21525
 
21467
21526
  class DOMFocusableElementStore {
21468
- mutators = ["setFocusableElement"];
21527
+ mutators = ["setFocusableElement", "focus"];
21469
21528
  focusableElement = undefined;
21470
21529
  setFocusableElement(element) {
21471
21530
  this.focusableElement = element;
21531
+ return "noStateChange";
21532
+ }
21533
+ focus() {
21534
+ if (this.focusableElement === document.activeElement) {
21535
+ return "noStateChange";
21536
+ }
21537
+ this.focusableElement?.focus();
21538
+ return;
21472
21539
  }
21473
21540
  }
21474
21541
 
@@ -21725,12 +21792,20 @@ class HoveredCellStore extends SpreadsheetStore {
21725
21792
  }
21726
21793
  }
21727
21794
  hover(position) {
21795
+ if (position.col === this.col && position.row === this.row) {
21796
+ return "noStateChange";
21797
+ }
21728
21798
  this.col = position.col;
21729
21799
  this.row = position.row;
21800
+ return;
21730
21801
  }
21731
21802
  clear() {
21803
+ if (this.col === undefined && this.row === undefined) {
21804
+ return "noStateChange";
21805
+ }
21732
21806
  this.col = undefined;
21733
21807
  this.row = undefined;
21808
+ return;
21734
21809
  }
21735
21810
  }
21736
21811
 
@@ -21752,7 +21827,11 @@ class CellPopoverStore extends SpreadsheetStore {
21752
21827
  this.persistentPopover = { col, row, sheetId, type };
21753
21828
  }
21754
21829
  close() {
21830
+ if (!this.persistentPopover) {
21831
+ return "noStateChange";
21832
+ }
21755
21833
  this.persistentPopover = undefined;
21834
+ return;
21756
21835
  }
21757
21836
  get persistentCellPopover() {
21758
21837
  return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
@@ -22621,10 +22700,13 @@ const duplicateSheet = {
22621
22700
  name: _t("Duplicate"),
22622
22701
  execute: (env) => {
22623
22702
  const sheetIdFrom = env.model.getters.getActiveSheetId();
22703
+ const sheetNameFrom = env.model.getters.getSheetName(sheetIdFrom);
22624
22704
  const sheetIdTo = env.model.uuidGenerator.smallUuid();
22705
+ const sheetNameTo = env.model.getters.getDuplicateSheetName(sheetNameFrom);
22625
22706
  env.model.dispatch("DUPLICATE_SHEET", {
22626
22707
  sheetId: sheetIdFrom,
22627
22708
  sheetIdTo,
22709
+ sheetNameTo,
22628
22710
  });
22629
22711
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom, sheetIdTo });
22630
22712
  },
@@ -24069,6 +24151,13 @@ class Composer extends Component {
24069
24151
  this.props.onInputContextMenu?.(ev);
24070
24152
  }
24071
24153
  }
24154
+ onWheel(event) {
24155
+ // detect if scrollbar is available
24156
+ if (this.composerRef.el &&
24157
+ this.composerRef.el.scrollHeight > this.composerRef.el.clientHeight) {
24158
+ event.stopPropagation();
24159
+ }
24160
+ }
24072
24161
  // ---------------------------------------------------------------------------
24073
24162
  // Private
24074
24163
  // ---------------------------------------------------------------------------
@@ -29357,7 +29446,7 @@ const FIX_FORMULAS = {
29357
29446
  if (!pivot.isValid()) {
29358
29447
  return;
29359
29448
  }
29360
- env.model.dispatch("INSERT_PIVOT", {
29449
+ env.model.dispatch("SPLIT_PIVOT_FORMULA", {
29361
29450
  sheetId,
29362
29451
  col,
29363
29452
  row,
@@ -36991,8 +37080,8 @@ function compareDimensionValues(dimension, a, b) {
36991
37080
 
36992
37081
  const NULL_SYMBOL = Symbol("NULL");
36993
37082
  function createDate(dimension, value, locale) {
36994
- const granularity = dimension.granularity;
36995
- if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
37083
+ const granularity = dimension.granularity || "month";
37084
+ if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
36996
37085
  throw new Error(`Unknown date granularity: ${granularity}`);
36997
37086
  }
36998
37087
  const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
@@ -37011,6 +37100,9 @@ function createDate(dimension, value, locale) {
37011
37100
  case "month_number":
37012
37101
  number = date.getMonth() + 1;
37013
37102
  break;
37103
+ case "month":
37104
+ number = Math.floor(toNumber(value, locale));
37105
+ break;
37014
37106
  case "iso_week_number":
37015
37107
  number = date.getIsoWeek();
37016
37108
  break;
@@ -37069,6 +37161,10 @@ const MAP_VALUE_DIMENSION_DATE = {
37069
37161
  set: new Set(),
37070
37162
  values: {},
37071
37163
  },
37164
+ month: {
37165
+ set: new Set(),
37166
+ values: {},
37167
+ },
37072
37168
  iso_week_number: {
37073
37169
  set: new Set(),
37074
37170
  values: {},
@@ -37245,7 +37341,7 @@ class SpreadsheetPivot {
37245
37341
  const cells = this.filterDataEntriesFromDomain(this.dataEntries, domain);
37246
37342
  const finalCell = cells[0]?.[dimension.nameWithGranularity];
37247
37343
  if (dimension.type === "date") {
37248
- const adapter = pivotTimeAdapter(dimension.granularity);
37344
+ const adapter = pivotTimeAdapter((dimension.granularity || "month"));
37249
37345
  return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
37250
37346
  }
37251
37347
  if (!finalCell) {
@@ -37330,7 +37426,7 @@ class SpreadsheetPivot {
37330
37426
  if (nonEmptyCells.length === 0) {
37331
37427
  return "integer";
37332
37428
  }
37333
- if (nonEmptyCells.every((cell) => cell.format && isDateTimeFormat(cell.format))) {
37429
+ if (nonEmptyCells.every((cell) => cell.type === CellValueType.number && cell.format && isDateTimeFormat(cell.format))) {
37334
37430
  return "date";
37335
37431
  }
37336
37432
  if (nonEmptyCells.every((cell) => cell.type === CellValueType.boolean)) {
@@ -37411,7 +37507,12 @@ class SpreadsheetPivot {
37411
37507
  entry[field.name] = { value: null, type: CellValueType.empty };
37412
37508
  }
37413
37509
  else {
37414
- entry[field.name] = cell;
37510
+ if (field.type === "char") {
37511
+ entry[field.name] = { ...cell, value: cell.formattedValue || null };
37512
+ }
37513
+ else {
37514
+ entry[field.name] = cell;
37515
+ }
37415
37516
  }
37416
37517
  }
37417
37518
  entry["__count"] = { value: 1, type: CellValueType.number };
@@ -37445,6 +37546,7 @@ pivotRegistry.add("SPREADSHEET", {
37445
37546
  "year",
37446
37547
  "quarter_number",
37447
37548
  "month_number",
37549
+ "month",
37448
37550
  "iso_week_number",
37449
37551
  "day_of_month",
37450
37552
  "day",
@@ -37654,7 +37756,7 @@ class PivotSidePanelStore extends SpreadsheetStore {
37654
37756
  granularitiesPerFields[field.name] = new Set(granularities);
37655
37757
  }
37656
37758
  for (const field of dateFields) {
37657
- granularitiesPerFields[field.name].delete(field.granularity);
37759
+ granularitiesPerFields[field.name].delete(field.granularity || "month");
37658
37760
  }
37659
37761
  return granularitiesPerFields;
37660
37762
  }
@@ -39541,12 +39643,15 @@ class GridComposer extends Component {
39541
39643
  });
39542
39644
  }
39543
39645
  get composerProps() {
39646
+ // Remove the wrapper border width
39647
+ const maxHeight = this.props.gridDims.height - this.rect.y - 2 * COMPOSER_BORDER_WIDTH;
39544
39648
  return {
39545
39649
  focus: this.composerFocusStore.gridComposerFocus,
39546
39650
  isDefaultFocus: true,
39547
39651
  onComposerContentFocused: () => this.composerFocusStore.focusGridComposerContent(),
39548
39652
  onComposerCellFocused: (content) => this.composerFocusStore.focusGridComposerCell(content),
39549
39653
  onInputContextMenu: this.props.onInputContextMenu,
39654
+ inputStyle: `max-height: ${maxHeight}px;`,
39550
39655
  };
39551
39656
  }
39552
39657
  get containerStyle() {
@@ -39602,7 +39707,7 @@ class GridComposer extends Component {
39602
39707
  this.isEditing = isEditing;
39603
39708
  if (!isEditing) {
39604
39709
  this.rect = this.defaultRect;
39605
- this.DOMFocusableElementStore.focusableElement?.focus();
39710
+ this.DOMFocusableElementStore.focus();
39606
39711
  return;
39607
39712
  }
39608
39713
  const position = this.env.model.getters.getActivePosition();
@@ -42038,10 +42143,6 @@ function useGridDrawing(refName, model, canvasSize) {
42038
42143
  ctx.scale(dpr, dpr);
42039
42144
  for (const layer of OrderedLayers()) {
42040
42145
  model.drawLayer(renderingContext, layer);
42041
- // @ts-ignore 'drawLayer' is not declated as a mutator because:
42042
- // it does not mutate anything. Most importantly it's used
42043
- // during rendering. Invoking a mutator during rendering would
42044
- // trigger another rendering, ultimately resulting in an infinite loop.
42045
42146
  rendererStore.drawLayer(renderingContext, layer);
42046
42147
  }
42047
42148
  }
@@ -42664,7 +42765,7 @@ class Grid extends Component {
42664
42765
  this.cellPopovers = useStore(CellPopoverStore);
42665
42766
  useEffect(() => {
42666
42767
  if (!this.sidePanel.isOpen) {
42667
- this.DOMFocusableElementStore.focusableElement?.focus();
42768
+ this.DOMFocusableElementStore.focus();
42668
42769
  }
42669
42770
  }, () => [this.sidePanel.isOpen]);
42670
42771
  }
@@ -42868,7 +42969,7 @@ class Grid extends Component {
42868
42969
  focusDefaultElement() {
42869
42970
  if (!this.env.model.getters.getSelectedFigureId() &&
42870
42971
  this.composerStore.editionMode === "inactive") {
42871
- this.DOMFocusableElementStore.focusableElement?.focus();
42972
+ this.DOMFocusableElementStore.focus();
42872
42973
  }
42873
42974
  }
42874
42975
  get gridEl() {
@@ -44605,7 +44706,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
44605
44706
  ({ xc, sheetName } = splitReference(reference));
44606
44707
  let rangeSheetIndex;
44607
44708
  if (sheetName) {
44608
- const index = data.sheets.findIndex((sheet) => sheet.name === sheetName);
44709
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
44609
44710
  if (index < 0) {
44610
44711
  throw new Error("Unable to find a sheet with the name " + sheetName);
44611
44712
  }
@@ -44826,7 +44927,7 @@ function convertFormula(formula, data) {
44826
44927
  formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
44827
44928
  externalRefId = Number(externalRefId) - 1;
44828
44929
  cellRef = cellRef.replace(/\$/g, "");
44829
- const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name === sheetName);
44930
+ const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
44830
44931
  if (sheetIndex === -1) {
44831
44932
  return match;
44832
44933
  }
@@ -45155,7 +45256,7 @@ function convertPivotTableConfig(pivotTable) {
45155
45256
  */
45156
45257
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
45157
45258
  for (let tableSheet of convertedSheets) {
45158
- const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
45259
+ const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
45159
45260
  for (let table of tables) {
45160
45261
  const tabRef = table.name + "[";
45161
45262
  for (let sheet of convertedSheets) {
@@ -47303,6 +47404,7 @@ function repairInitialMessages(data, initialMessages) {
47303
47404
  initialMessages = dropCommands(initialMessages, "SORT_CELLS");
47304
47405
  initialMessages = dropCommands(initialMessages, "SET_DECIMAL");
47305
47406
  initialMessages = fixChartDefinitions(data, initialMessages);
47407
+ initialMessages = fixTranslatedDuplicateSheetName(data, initialMessages);
47306
47408
  return initialMessages;
47307
47409
  }
47308
47410
  /**
@@ -47422,6 +47524,40 @@ function fixOverlappingFilters(data) {
47422
47524
  }
47423
47525
  return data;
47424
47526
  }
47527
+ function fixTranslatedDuplicateSheetName(data, initialMessages) {
47528
+ const sheetNames = {};
47529
+ for (const sheet of data.sheets || []) {
47530
+ sheetNames[sheet.id] = sheet.name;
47531
+ }
47532
+ const messages = [];
47533
+ for (const message of initialMessages) {
47534
+ if (message.type === "REMOTE_REVISION") {
47535
+ const commands = [];
47536
+ for (const cmd of message.commands) {
47537
+ switch (cmd.type) {
47538
+ case "DUPLICATE_SHEET":
47539
+ cmd.sheetNameTo =
47540
+ cmd.sheetNameTo ??
47541
+ getDuplicateSheetName(sheetNames[cmd.sheetId], Object.values(sheetNames));
47542
+ break;
47543
+ case "CREATE_SHEET":
47544
+ case "RENAME_SHEET":
47545
+ sheetNames[cmd.sheetId] = cmd.name || getNextSheetName(Object.values(sheetNames));
47546
+ break;
47547
+ }
47548
+ commands.push(cmd);
47549
+ }
47550
+ messages.push({
47551
+ ...message,
47552
+ commands,
47553
+ });
47554
+ }
47555
+ else {
47556
+ messages.push(message);
47557
+ }
47558
+ }
47559
+ return initialMessages;
47560
+ }
47425
47561
  // -----------------------------------------------------------------------------
47426
47562
  // Helpers
47427
47563
  // -----------------------------------------------------------------------------
@@ -48957,9 +49093,7 @@ class ChartPlugin extends CorePlugin {
48957
49093
  : "Success" /* CommandResult.Success */;
48958
49094
  }
48959
49095
  checkChartExists(cmd) {
48960
- return this.getters.getFigureSheetId(cmd.id)
48961
- ? "Success" /* CommandResult.Success */
48962
- : "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
49096
+ return this.isChartDefined(cmd.id) ? "Success" /* CommandResult.Success */ : "ChartDoesNotExist" /* CommandResult.ChartDoesNotExist */;
48963
49097
  }
48964
49098
  }
48965
49099
 
@@ -50799,7 +50933,7 @@ class RangeAdapter {
50799
50933
  if (range.sheetId === cmd.sheetId) {
50800
50934
  return { changeType: "CHANGE", range };
50801
50935
  }
50802
- if (cmd.name && range.invalidSheetName === cmd.name) {
50936
+ if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
50803
50937
  const invalidSheetName = undefined;
50804
50938
  const sheetId = cmd.sheetId;
50805
50939
  const newRange = range.clone({ sheetId, invalidSheetName });
@@ -51136,6 +51270,7 @@ class SheetPlugin extends CorePlugin {
51136
51270
  "getCommandZones",
51137
51271
  "getUnboundedZone",
51138
51272
  "checkElementsIncludeAllNonFrozenHeaders",
51273
+ "getDuplicateSheetName",
51139
51274
  ];
51140
51275
  sheetIdsMapName = {};
51141
51276
  orderedSheetIds = [];
@@ -51160,7 +51295,11 @@ class SheetPlugin extends CorePlugin {
51160
51295
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
51161
51296
  }
51162
51297
  case "DUPLICATE_SHEET": {
51163
- return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
51298
+ if (this.sheets[cmd.sheetIdTo])
51299
+ return "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */;
51300
+ if (this.orderedSheetIds.map(this.getSheetName.bind(this)).includes(cmd.sheetNameTo))
51301
+ return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
51302
+ return "Success" /* CommandResult.Success */;
51164
51303
  }
51165
51304
  case "MOVE_SHEET":
51166
51305
  try {
@@ -51230,7 +51369,7 @@ class SheetPlugin extends CorePlugin {
51230
51369
  this.showSheet(cmd.sheetId);
51231
51370
  break;
51232
51371
  case "DUPLICATE_SHEET":
51233
- this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo);
51372
+ this.duplicateSheet(cmd.sheetId, cmd.sheetIdTo, cmd.sheetNameTo);
51234
51373
  break;
51235
51374
  case "DELETE_SHEET":
51236
51375
  this.deleteSheet(this.sheets[cmd.sheetId]);
@@ -51365,7 +51504,7 @@ class SheetPlugin extends CorePlugin {
51365
51504
  if (name) {
51366
51505
  const unquotedName = getUnquotedSheetName(name);
51367
51506
  for (const key in this.sheetIdsMapName) {
51368
- if (key.toUpperCase() === unquotedName.toUpperCase()) {
51507
+ if (isSheetNameEqual(key, unquotedName)) {
51369
51508
  return this.sheetIdsMapName[key];
51370
51509
  }
51371
51510
  }
@@ -51430,14 +51569,8 @@ class SheetPlugin extends CorePlugin {
51430
51569
  return dimension === "COL" ? this.getNumberCols(sheetId) : this.getNumberRows(sheetId);
51431
51570
  }
51432
51571
  getNextSheetName(baseName = "Sheet") {
51433
- let i = 1;
51434
51572
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
51435
- let name = `${baseName}${i}`;
51436
- while (names.includes(name)) {
51437
- name = `${baseName}${i}`;
51438
- i++;
51439
- }
51440
- return name;
51573
+ return getNextSheetName(names, baseName);
51441
51574
  }
51442
51575
  getSheetSize(sheetId) {
51443
51576
  return {
@@ -51619,7 +51752,7 @@ class SheetPlugin extends CorePlugin {
51619
51752
  }
51620
51753
  const { orderedSheetIds, sheets } = this;
51621
51754
  const name = cmd.name && cmd.name.trim().toLowerCase();
51622
- if (orderedSheetIds.find((id) => sheets[id]?.name.toLowerCase() === name && id !== cmd.sheetId)) {
51755
+ if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
51623
51756
  return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
51624
51757
  }
51625
51758
  if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
@@ -51683,9 +51816,8 @@ class SheetPlugin extends CorePlugin {
51683
51816
  showSheet(sheetId) {
51684
51817
  this.history.update("sheets", sheetId, "isVisible", true);
51685
51818
  }
51686
- duplicateSheet(fromId, toId) {
51819
+ duplicateSheet(fromId, toId, toName) {
51687
51820
  const sheet = this.getSheet(fromId);
51688
- const toName = this.getDuplicateSheetName(sheet.name);
51689
51821
  const newSheet = deepCopy(sheet);
51690
51822
  newSheet.id = toId;
51691
51823
  newSheet.name = toName;
@@ -51717,15 +51849,8 @@ class SheetPlugin extends CorePlugin {
51717
51849
  this.history.update("sheetIdsMapName", sheetIdsMapName);
51718
51850
  }
51719
51851
  getDuplicateSheetName(sheetName) {
51720
- let i = 1;
51721
51852
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
51722
- const baseName = _t("Copy of %s", sheetName);
51723
- let name = baseName.toString();
51724
- while (names.includes(name)) {
51725
- name = `${baseName} (${i})`;
51726
- i++;
51727
- }
51728
- return name;
51853
+ return getDuplicateSheetName(sheetName, names);
51729
51854
  }
51730
51855
  deleteSheet(sheet) {
51731
51856
  const name = sheet.name;
@@ -54524,8 +54649,8 @@ class SpreadingRelation {
54524
54649
  const EMPTY_ARRAY = [];
54525
54650
 
54526
54651
  const MAX_ITERATION = 30;
54527
- const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
54528
- const EMPTY_CELL = createEvaluatedCell({ value: null });
54652
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
54653
+ const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
54529
54654
  class Evaluator {
54530
54655
  context;
54531
54656
  getters;
@@ -58587,6 +58712,8 @@ class InsertPivotPlugin extends UIPlugin {
58587
58712
  case "INSERT_PIVOT_WITH_TABLE":
58588
58713
  this.insertPivotWithTable(cmd.sheetId, cmd.col, cmd.row, cmd.pivotId, cmd.table, cmd.pivotMode);
58589
58714
  break;
58715
+ case "SPLIT_PIVOT_FORMULA":
58716
+ this.splitPivotFormula(cmd.sheetId, cmd.col, cmd.row, cmd.pivotId, cmd.table);
58590
58717
  }
58591
58718
  }
58592
58719
  insertNewPivot(pivotId, sheetId) {
@@ -58734,6 +58861,36 @@ class InsertPivotPlugin extends UIPlugin {
58734
58861
  });
58735
58862
  }
58736
58863
  }
58864
+ splitPivotFormula(sheetId, col, row, pivotId, pivotTableData) {
58865
+ this.dispatch("INSERT_PIVOT", {
58866
+ sheetId,
58867
+ col,
58868
+ row,
58869
+ pivotId,
58870
+ table: pivotTableData,
58871
+ });
58872
+ const table = this.getters.getCoreTable({ sheetId, col, row });
58873
+ if (table?.type === "dynamic") {
58874
+ const zone = positionToZone({ col, row });
58875
+ const { cols, rows, measures, fieldsType } = pivotTableData;
58876
+ const pivotTable = new SpreadsheetPivotTable(cols, rows, measures, fieldsType || {});
58877
+ const colNumber = pivotTable.getNumberOfDataColumns() + 1;
58878
+ const rowNumber = pivotTable.columns.length + pivotTable.rows.length;
58879
+ const tableZone = {
58880
+ left: col,
58881
+ top: row,
58882
+ right: col + colNumber - 1,
58883
+ bottom: row + rowNumber - 1,
58884
+ };
58885
+ const rangeData = this.getters.getRangeDataFromZone(sheetId, tableZone);
58886
+ this.dispatch("UPDATE_TABLE", {
58887
+ sheetId,
58888
+ zone,
58889
+ newTableRange: rangeData,
58890
+ tableType: "static",
58891
+ });
58892
+ }
58893
+ }
58737
58894
  }
58738
58895
 
58739
58896
  class SortPlugin extends UIPlugin {
@@ -62927,11 +63084,11 @@ class BottomBarSheet extends Component {
62927
63084
  if (ev.key === "Enter") {
62928
63085
  ev.preventDefault();
62929
63086
  this.stopEdition();
62930
- this.DOMFocusableElementStore.focusableElement?.focus();
63087
+ this.DOMFocusableElementStore.focus();
62931
63088
  }
62932
63089
  if (ev.key === "Escape") {
62933
63090
  this.cancelEdition();
62934
- this.DOMFocusableElementStore.focusableElement?.focus();
63091
+ this.DOMFocusableElementStore.focus();
62935
63092
  }
62936
63093
  }
62937
63094
  onMouseEventSheetName(ev) {
@@ -69361,6 +69518,6 @@ const constants = {
69361
69518
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
69362
69519
 
69363
69520
 
69364
- __info__.version = "17.4.33";
69365
- __info__.date = "2025-05-02T12:30:31.263Z";
69366
- __info__.hash = "3ba00e6";
69521
+ __info__.version = "17.4.35";
69522
+ __info__.date = "2025-05-13T17:49:44.553Z";
69523
+ __info__.hash = "534c5f4";