@odoo/o-spreadsheet 17.0.48 → 17.0.50

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.0.48
6
- * @date 2025-02-05T07:18:39.331Z
7
- * @hash aea9101
5
+ * @version 17.0.50
6
+ * @date 2025-02-14T08:37:34.289Z
7
+ * @hash cc1037d
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -687,9 +687,16 @@
687
687
  }
688
688
  return true;
689
689
  }
690
- /** Check if the given array contains all the values of the other array. */
690
+ /**
691
+ * Check if the given array contains all the values of the other array.
692
+ * It makes the assumption that both array do not contain duplicates.
693
+ */
691
694
  function includesAll(arr, values) {
692
- return values.every((value) => arr.includes(value));
695
+ if (arr.length < values.length) {
696
+ return false;
697
+ }
698
+ const set = new Set(arr);
699
+ return values.every((value) => set.has(value));
693
700
  }
694
701
  /**
695
702
  * Return an object with all the keys in the object that have a falsy value removed.
@@ -1902,12 +1909,12 @@
1902
1909
  function isRangeDependant(cmd) {
1903
1910
  return "ranges" in cmd;
1904
1911
  }
1905
- function isZoneDependent(cmd) {
1906
- return "zone" in cmd;
1907
- }
1908
1912
  function isPositionDependent(cmd) {
1909
1913
  return "col" in cmd && "row" in cmd && "sheetId" in cmd;
1910
1914
  }
1915
+ function isZoneDependent(cmd) {
1916
+ return "sheetId" in cmd && "zone" in cmd;
1917
+ }
1911
1918
  const invalidateEvaluationCommands = new Set([
1912
1919
  "RENAME_SHEET",
1913
1920
  "DELETE_SHEET",
@@ -1917,6 +1924,7 @@
1917
1924
  "UNDO",
1918
1925
  "REDO",
1919
1926
  "ADD_MERGE",
1927
+ "DUPLICATE_SHEET",
1920
1928
  "UPDATE_LOCALE",
1921
1929
  ]);
1922
1930
  const invalidateDependenciesCommands = new Set([
@@ -1925,7 +1933,6 @@
1925
1933
  ]);
1926
1934
  const invalidateCFEvaluationCommands = new Set([
1927
1935
  ...invalidateEvaluationCommands,
1928
- "DUPLICATE_SHEET",
1929
1936
  "EVALUATE_CELLS",
1930
1937
  "ADD_CONDITIONAL_FORMAT",
1931
1938
  "REMOVE_CONDITIONAL_FORMAT",
@@ -2085,6 +2092,7 @@
2085
2092
  CommandResult["InvalidRange"] = "InvalidRange";
2086
2093
  CommandResult["InvalidZones"] = "InvalidZones";
2087
2094
  CommandResult["InvalidSheetId"] = "InvalidSheetId";
2095
+ CommandResult["InvalidCellId"] = "InvalidCellId";
2088
2096
  CommandResult["InvalidFigureId"] = "InvalidFigureId";
2089
2097
  CommandResult["InputAlreadyFocused"] = "InputAlreadyFocused";
2090
2098
  CommandResult["MaximumRangesReached"] = "MaximumRangesReached";
@@ -3665,17 +3673,20 @@
3665
3673
  */
3666
3674
  function toUnboundedZone(xc) {
3667
3675
  const zone = toZoneWithoutBoundaryChanges(xc);
3676
+ reorderZone(zone);
3677
+ return zone;
3678
+ }
3679
+ function reorderZone(zone) {
3668
3680
  if (zone.right !== undefined && zone.right < zone.left) {
3669
- const tmp = zone.left;
3681
+ const right = zone.left;
3670
3682
  zone.left = zone.right;
3671
- zone.right = tmp;
3683
+ zone.right = right;
3672
3684
  }
3673
3685
  if (zone.bottom !== undefined && zone.bottom < zone.top) {
3674
- const tmp = zone.top;
3686
+ const bottom = zone.top;
3675
3687
  zone.top = zone.bottom;
3676
- zone.bottom = tmp;
3688
+ zone.bottom = bottom;
3677
3689
  }
3678
- return zone;
3679
3690
  }
3680
3691
  /**
3681
3692
  * Convert from a cartesian reference to a Zone.
@@ -4708,6 +4719,37 @@
4708
4719
  setIsFastStrategy(isFast) {
4709
4720
  this.isFastIdStrategy = isFast;
4710
4721
  }
4722
+ /**
4723
+ * Generates a custom UUID using a simple 26^8 method (8-character alphanumeric string with lowercase letters)
4724
+ * This has a higher chance of collision than a UUIDv4, but not only faster to generate than an UUIDV4,
4725
+ * it also has a smaller size, which is preferable to alleviate the overall data size.
4726
+ *
4727
+ * This method is preferable when generating uuids for the core data (sheetId, figureId, etc)
4728
+ * as they will appear several times in the revisions and local history.
4729
+ *
4730
+ */
4731
+ smallUuid() {
4732
+ if (this.isFastIdStrategy) {
4733
+ this.fastIdStart++;
4734
+ return String(this.fastIdStart);
4735
+ //@ts-ignore
4736
+ }
4737
+ else if (window.crypto && window.crypto.getRandomValues) {
4738
+ //@ts-ignore
4739
+ return ([1e7] + -1e3).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
4740
+ }
4741
+ else {
4742
+ // mainly for jest and other browsers that do not have the crypto functionality
4743
+ return "xxxxxxxx-xxxx".replace(/[xy]/g, function (c) {
4744
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
4745
+ return v.toString(16);
4746
+ });
4747
+ }
4748
+ }
4749
+ /**
4750
+ * Generates an UUIDV4, has astronomically low chance of collision, but is larger in size than the smallUuid.
4751
+ * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
4752
+ */
4711
4753
  uuidv4() {
4712
4754
  if (this.isFastIdStrategy) {
4713
4755
  this.fastIdStart++;
@@ -4721,7 +4763,7 @@
4721
4763
  else {
4722
4764
  // mainly for jest and other browsers that do not have the crypto functionality
4723
4765
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
4724
- var r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
4766
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
4725
4767
  return v.toString(16);
4726
4768
  });
4727
4769
  }
@@ -7934,7 +7976,7 @@
7934
7976
  const deleteSheet = {
7935
7977
  name: _t("Delete"),
7936
7978
  isVisible: (env) => {
7937
- return env.model.getters.getSheetIds().length > 1;
7979
+ return env.model.getters.getVisibleSheetIds().length > 1;
7938
7980
  },
7939
7981
  execute: (env) => env.askConfirmation(_t("Are you sure you want to delete this sheet?"), () => {
7940
7982
  env.model.dispatch("DELETE_SHEET", { sheetId: env.model.getters.getActiveSheetId() });
@@ -7944,7 +7986,7 @@
7944
7986
  name: _t("Duplicate"),
7945
7987
  execute: (env) => {
7946
7988
  const sheetIdFrom = env.model.getters.getActiveSheetId();
7947
- const sheetIdTo = env.model.uuidGenerator.uuidv4();
7989
+ const sheetIdTo = env.model.uuidGenerator.smallUuid();
7948
7990
  env.model.dispatch("DUPLICATE_SHEET", {
7949
7991
  sheetId: sheetIdFrom,
7950
7992
  sheetIdTo,
@@ -8494,17 +8536,15 @@
8494
8536
  plugins: [],
8495
8537
  };
8496
8538
  }
8497
- function getChartLabelFormat(getters, range) {
8539
+ function getChartLabelFormat(getters, range, shouldRemoveFirstLabel) {
8498
8540
  if (!range)
8499
8541
  return undefined;
8500
- const { sheetId, zone: { left, top, bottom }, } = range;
8501
- for (let row = top; row <= bottom; row++) {
8502
- const format = getters.getEvaluatedCell({ sheetId, col: left, row }).format;
8503
- if (format) {
8504
- return format;
8505
- }
8542
+ const { sheetId, zone } = range;
8543
+ const formats = positions(zone).map((position) => getters.getEvaluatedCell({ sheetId, ...position }).format);
8544
+ if (shouldRemoveFirstLabel) {
8545
+ formats.shift();
8506
8546
  }
8507
- return undefined;
8547
+ return formats.find((format) => format !== undefined);
8508
8548
  }
8509
8549
  function getChartLabelValues(getters, dataSets, labelRange) {
8510
8550
  let labels = { values: [], formattedValues: [] };
@@ -8785,9 +8825,7 @@
8785
8825
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
8786
8826
  let labels = labelValues.formattedValues;
8787
8827
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
8788
- if (chart.dataSetsHaveTitle &&
8789
- dataSetsValues[0] &&
8790
- labels.length > dataSetsValues[0].data.length) {
8828
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
8791
8829
  labels.shift();
8792
8830
  }
8793
8831
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -9331,8 +9369,8 @@
9331
9369
  }
9332
9370
  return { labels: newLabels, dataSetsValues: newDatasets };
9333
9371
  }
9334
- function canChartParseLabels(labelRange, getters) {
9335
- return canBeDateChart(labelRange, getters) || canBeLinearChart(labelRange, getters);
9372
+ function canChartParseLabels(chart, getters) {
9373
+ return canBeDateChart(chart, getters) || canBeLinearChart(chart, getters);
9336
9374
  }
9337
9375
  function getChartAxisType(chart, getters) {
9338
9376
  if (isDateChart(chart, getters) && isLuxonTimeAdapterInstalled()) {
@@ -9344,23 +9382,26 @@
9344
9382
  return "category";
9345
9383
  }
9346
9384
  function isDateChart(chart, getters) {
9347
- return !chart.labelsAsText && canBeDateChart(chart.labelRange, getters);
9385
+ return !chart.labelsAsText && canBeDateChart(chart, getters);
9348
9386
  }
9349
9387
  function isLinearChart(chart, getters) {
9350
- return !chart.labelsAsText && canBeLinearChart(chart.labelRange, getters);
9388
+ return !chart.labelsAsText && canBeLinearChart(chart, getters);
9351
9389
  }
9352
- function canBeDateChart(labelRange, getters) {
9353
- if (!labelRange || !canBeLinearChart(labelRange, getters)) {
9390
+ function canBeDateChart(chart, getters) {
9391
+ if (!chart.labelRange || !canBeLinearChart(chart, getters)) {
9354
9392
  return false;
9355
9393
  }
9356
- const labelFormat = getChartLabelFormat(getters, labelRange);
9394
+ const labelFormat = getChartLabelFormat(getters, chart.labelRange, shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle));
9357
9395
  return Boolean(labelFormat && timeFormatLuxonCompatible.test(labelFormat));
9358
9396
  }
9359
- function canBeLinearChart(labelRange, getters) {
9360
- if (!labelRange) {
9397
+ function canBeLinearChart(chart, getters) {
9398
+ if (!chart.labelRange) {
9361
9399
  return false;
9362
9400
  }
9363
- const labels = getters.getRangeValues(labelRange);
9401
+ const labels = getters.getRangeValues(chart.labelRange);
9402
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
9403
+ labels.shift();
9404
+ }
9364
9405
  if (labels.some((label) => isNaN(Number(label)) && label)) {
9365
9406
  return false;
9366
9407
  }
@@ -9434,9 +9475,8 @@
9434
9475
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
9435
9476
  let labels = axisType === "linear" ? labelValues.values : labelValues.formattedValues;
9436
9477
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
9437
- if (chart.dataSetsHaveTitle &&
9438
- dataSetsValues[0] &&
9439
- labels.length > dataSetsValues[0].data.length) {
9478
+ const removeFirstLabel = shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle);
9479
+ if (removeFirstLabel) {
9440
9480
  labels.shift();
9441
9481
  }
9442
9482
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -9451,7 +9491,7 @@
9451
9491
  const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
9452
9492
  const options = { format: dataSetFormat, locale, truncateLabels };
9453
9493
  const config = getLineConfiguration(chart, labels, options);
9454
- const labelFormat = getChartLabelFormat(getters, chart.labelRange);
9494
+ const labelFormat = getChartLabelFormat(getters, chart.labelRange, removeFirstLabel);
9455
9495
  if (axisType === "time") {
9456
9496
  const axis = {
9457
9497
  type: "time",
@@ -9673,9 +9713,7 @@
9673
9713
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
9674
9714
  let labels = labelValues.formattedValues;
9675
9715
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
9676
- if (chart.dataSetsHaveTitle &&
9677
- dataSetsValues[0] &&
9678
- labels.length > dataSetsValues[0].data.length) {
9716
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
9679
9717
  labels.shift();
9680
9718
  }
9681
9719
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -10196,21 +10234,22 @@
10196
10234
  }
10197
10235
  // Only display legend for several datasets.
10198
10236
  const newLegendPos = dataSetZone.right === dataSetZone.left ? "none" : "top";
10199
- const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
10200
- if (canChartParseLabels(labelRange, getters)) {
10201
- return {
10202
- title,
10203
- dataSets,
10204
- labelsAsText: false,
10205
- stacked: false,
10206
- aggregated: false,
10207
- cumulative: false,
10208
- labelRange: labelRangeXc,
10209
- type: "line",
10210
- dataSetsHaveTitle,
10211
- verticalAxisPosition: "left",
10212
- legendPosition: newLegendPos,
10213
- };
10237
+ const lineChartDefinition = {
10238
+ title,
10239
+ dataSets,
10240
+ labelsAsText: false,
10241
+ stacked: false,
10242
+ aggregated: false,
10243
+ cumulative: false,
10244
+ labelRange: labelRangeXc,
10245
+ type: "line",
10246
+ dataSetsHaveTitle,
10247
+ verticalAxisPosition: "left",
10248
+ legendPosition: newLegendPos,
10249
+ };
10250
+ const chart = new LineChart(lineChartDefinition, sheetId, getters);
10251
+ if (canChartParseLabels(chart, getters)) {
10252
+ return lineChartDefinition;
10214
10253
  }
10215
10254
  return {
10216
10255
  title,
@@ -10810,7 +10849,7 @@
10810
10849
  //------------------------------------------------------------------------------
10811
10850
  const CREATE_CHART = (env) => {
10812
10851
  const getters = env.model.getters;
10813
- const id = env.model.uuidGenerator.uuidv4();
10852
+ const id = env.model.uuidGenerator.smallUuid();
10814
10853
  const sheetId = getters.getActiveSheetId();
10815
10854
  if (getZoneArea(env.model.getters.getSelectedZone()) === 1) {
10816
10855
  env.model.selection.selectTableAroundSelection();
@@ -10844,7 +10883,7 @@
10844
10883
  const CREATE_IMAGE = async (env) => {
10845
10884
  if (env.imageProvider) {
10846
10885
  const sheetId = env.model.getters.getActiveSheetId();
10847
- const figureId = env.model.uuidGenerator.uuidv4();
10886
+ const figureId = env.model.uuidGenerator.smallUuid();
10848
10887
  const image = await requestImage(env);
10849
10888
  if (!image) {
10850
10889
  throw new Error("No image provider was given to the environment");
@@ -12565,6 +12604,26 @@
12565
12604
  isExported: true,
12566
12605
  };
12567
12606
  // -----------------------------------------------------------------------------
12607
+ // LOG
12608
+ // -----------------------------------------------------------------------------
12609
+ const LOG = {
12610
+ description: _t("The logarithm of a number, for a given base."),
12611
+ args: [
12612
+ arg("value (number)", _t("The value for which to calculate the logarithm.")),
12613
+ arg("base (number, default=10)", _t("The base of the logarithm.")),
12614
+ ],
12615
+ returns: ["NUMBER"],
12616
+ compute: function (value, base = 10) {
12617
+ const _value = toNumber(value, this.locale);
12618
+ const _base = toNumber(base, this.locale);
12619
+ assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
12620
+ assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
12621
+ assert(() => _base !== 1, _t("The base must be different from 1."));
12622
+ return Math.log10(_value) / Math.log10(_base);
12623
+ },
12624
+ isExported: true,
12625
+ };
12626
+ // -----------------------------------------------------------------------------
12568
12627
  // MOD
12569
12628
  // -----------------------------------------------------------------------------
12570
12629
  const MOD = {
@@ -13085,6 +13144,7 @@
13085
13144
  ISODD: ISODD,
13086
13145
  ISO_CEILING: ISO_CEILING,
13087
13146
  LN: LN,
13147
+ LOG: LOG,
13088
13148
  MOD: MOD,
13089
13149
  MUNIT: MUNIT,
13090
13150
  ODD: ODD,
@@ -15946,7 +16006,7 @@
15946
16006
  }
15947
16007
  }
15948
16008
  },
15949
- isExported: true,
16009
+ isExported: false,
15950
16010
  };
15951
16011
  // -----------------------------------------------------------------------------
15952
16012
  // UNIQUE
@@ -19847,7 +19907,7 @@
19847
19907
  execute: (env) => {
19848
19908
  const activeSheetId = env.model.getters.getActiveSheetId();
19849
19909
  const position = env.model.getters.getSheetIds().indexOf(activeSheetId) + 1;
19850
- const sheetId = env.model.uuidGenerator.uuidv4();
19910
+ const sheetId = env.model.uuidGenerator.smallUuid();
19851
19911
  env.model.dispatch("CREATE_SHEET", { sheetId, position });
19852
19912
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
19853
19913
  },
@@ -21523,7 +21583,7 @@
21523
21583
  */
21524
21584
  class SelectionInput extends owl.Component {
21525
21585
  static template = "o-spreadsheet-SelectionInput";
21526
- id = uuidGenerator$1.uuidv4();
21586
+ id = uuidGenerator$1.smallUuid();
21527
21587
  previousRanges = this.props.ranges() || [];
21528
21588
  originSheet = this.env.model.getters.getActiveSheetId();
21529
21589
  state = owl.useState({
@@ -22573,7 +22633,7 @@
22573
22633
  get canTreatLabelsAsText() {
22574
22634
  const chart = this.env.model.getters.getChart(this.props.figureId);
22575
22635
  if (chart && chart instanceof LineChart) {
22576
- return canChartParseLabels(chart.labelRange, this.env.model.getters);
22636
+ return canChartParseLabels(chart, this.env.model.getters);
22577
22637
  }
22578
22638
  return false;
22579
22639
  }
@@ -23601,7 +23661,7 @@
23601
23661
  state;
23602
23662
  setup() {
23603
23663
  const cf = this.props.editedCf || {
23604
- id: this.env.model.uuidGenerator.uuidv4(),
23664
+ id: this.env.model.uuidGenerator.smallUuid(),
23605
23665
  ranges: this.env.model.getters
23606
23666
  .getSelectedZones()
23607
23667
  .map((zone) => this.env.model.getters.zoneToXC(this.env.model.getters.getActiveSheetId(), zone)),
@@ -25550,7 +25610,7 @@
25550
25610
  .getSelectedZones()
25551
25611
  .map((zone) => zoneToXc(this.env.model.getters.getUnboundedZone(sheetId, zone)));
25552
25612
  return {
25553
- id: this.env.model.uuidGenerator.uuidv4(),
25613
+ id: this.env.model.uuidGenerator.smallUuid(),
25554
25614
  criterion: { type: "textContains", values: [""] },
25555
25615
  ranges,
25556
25616
  };
@@ -34170,7 +34230,7 @@
34170
34230
  for (const sheet of data.sheets || []) {
34171
34231
  for (const figure of sheet.figures || []) {
34172
34232
  if (figureIds.has(figure.id)) {
34173
- figure.id += uuidGenerator.uuidv4();
34233
+ figure.id += uuidGenerator.smallUuid();
34174
34234
  }
34175
34235
  figureIds.add(figure.id);
34176
34236
  }
@@ -35846,6 +35906,10 @@
35846
35906
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35847
35907
  case "CLEAR_CELL":
35848
35908
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35909
+ case "UPDATE_CELL_POSITION":
35910
+ return !cmd.cellId || this.cells[cmd.sheetId]?.[cmd.cellId]
35911
+ ? "Success" /* CommandResult.Success */
35912
+ : "InvalidCellId" /* CommandResult.InvalidCellId */;
35849
35913
  default:
35850
35914
  return "Success" /* CommandResult.Success */;
35851
35915
  }
@@ -35887,6 +35951,9 @@
35887
35951
  case "CLEAR_CELLS":
35888
35952
  this.clearCells(cmd.sheetId, cmd.target);
35889
35953
  break;
35954
+ case "DELETE_SHEET": {
35955
+ this.history.update("cells", cmd.sheetId, undefined);
35956
+ }
35890
35957
  }
35891
35958
  }
35892
35959
  /**
@@ -36965,8 +37032,14 @@
36965
37032
  allowDispatch(cmd) {
36966
37033
  switch (cmd.type) {
36967
37034
  case "ADD_DATA_VALIDATION_RULE":
37035
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
37036
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
37037
+ }
36968
37038
  return this.checkValidations(cmd, this.chainValidations(this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
36969
37039
  case "REMOVE_DATA_VALIDATION_RULE":
37040
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
37041
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
37042
+ }
36970
37043
  if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
36971
37044
  return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
36972
37045
  }
@@ -37140,6 +37213,7 @@
37140
37213
  class FigurePlugin extends CorePlugin {
37141
37214
  static getters = ["getFigures", "getFigure", "getFigureSheetId"];
37142
37215
  figures = {};
37216
+ insertionOrders = []; // TODO use a list in master
37143
37217
  // ---------------------------------------------------------------------------
37144
37218
  // Command Handling
37145
37219
  // ---------------------------------------------------------------------------
@@ -37242,11 +37316,14 @@
37242
37316
  }
37243
37317
  addFigure(figure, sheetId) {
37244
37318
  this.history.update("figures", sheetId, figure.id, figure);
37319
+ this.history.update("insertionOrders", this.insertionOrders.length, figure.id);
37245
37320
  }
37246
37321
  deleteSheet(sheetId) {
37322
+ this.history.update("insertionOrders", this.insertionOrders.filter((id) => !this.figures[sheetId]?.[id]));
37247
37323
  this.history.update("figures", sheetId, undefined);
37248
37324
  }
37249
37325
  removeFigure(id, sheetId) {
37326
+ this.history.update("insertionOrders", this.insertionOrders.filter((figureId) => figureId !== id));
37250
37327
  this.history.update("figures", sheetId, id, undefined);
37251
37328
  }
37252
37329
  checkFigureExists(sheetId, figureId) {
@@ -37265,7 +37342,14 @@
37265
37342
  // Getters
37266
37343
  // ---------------------------------------------------------------------------
37267
37344
  getFigures(sheetId) {
37268
- return Object.values(this.figures[sheetId] || {}).filter(isDefined$1);
37345
+ const figures = [];
37346
+ for (const figureId of this.insertionOrders) {
37347
+ const figure = this.figures[sheetId]?.[figureId];
37348
+ if (figure) {
37349
+ figures.push(figure);
37350
+ }
37351
+ }
37352
+ return figures;
37269
37353
  }
37270
37354
  getFigure(sheetId, figureId) {
37271
37355
  return this.figures[sheetId]?.[figureId];
@@ -37278,11 +37362,9 @@
37278
37362
  // ---------------------------------------------------------------------------
37279
37363
  import(data) {
37280
37364
  for (let sheet of data.sheets) {
37281
- const figures = {};
37282
- sheet.figures.forEach((figure) => {
37283
- figures[figure.id] = figure;
37284
- });
37285
- this.figures[sheet.id] = figures;
37365
+ for (const figure of sheet.figures) {
37366
+ this.addFigure(figure, sheet.id);
37367
+ }
37286
37368
  }
37287
37369
  }
37288
37370
  export(data) {
@@ -37306,10 +37388,10 @@
37306
37388
  this.filters = [];
37307
37389
  this.zone = zone;
37308
37390
  const uuid = new UuidGenerator();
37309
- this.id = uuid.uuidv4();
37391
+ this.id = uuid.smallUuid();
37310
37392
  for (const i of range(zone.left, zone.right + 1)) {
37311
37393
  const filterZone = { ...this.zone, left: i, right: i };
37312
- this.filters.push(new Filter(uuid.uuidv4(), filterZone));
37394
+ this.filters.push(new Filter(uuid.smallUuid(), filterZone));
37313
37395
  }
37314
37396
  }
37315
37397
  /** Get zone of the table without the headers */
@@ -37511,7 +37593,7 @@
37511
37593
  if (filters.length < zoneToDimension(zone).numberOfCols) {
37512
37594
  for (let col = zone.left; col <= zone.right; col++) {
37513
37595
  if (!filters.find((filter) => filter.col === col)) {
37514
- filters.push(new Filter(this.uuidGenerator.uuidv4(), { ...zone, left: col, right: col }));
37596
+ filters.push(new Filter(this.uuidGenerator.smallUuid(), { ...zone, left: col, right: col }));
37515
37597
  }
37516
37598
  }
37517
37599
  filters.sort((f1, f2) => f1.col - f2.col);
@@ -39000,6 +39082,9 @@
39000
39082
  case "CREATE_SHEET": {
39001
39083
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
39002
39084
  }
39085
+ case "DUPLICATE_SHEET": {
39086
+ return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
39087
+ }
39003
39088
  case "MOVE_SHEET":
39004
39089
  try {
39005
39090
  const currentIndex = this.orderedSheetIds.findIndex((id) => id === cmd.sheetId);
@@ -39012,7 +39097,7 @@
39012
39097
  case "RENAME_SHEET":
39013
39098
  return this.isRenameAllowed(cmd);
39014
39099
  case "DELETE_SHEET":
39015
- return this.orderedSheetIds.length > 1
39100
+ return this.getVisibleSheetIds().length > 1
39016
39101
  ? "Success" /* CommandResult.Success */
39017
39102
  : "NotEnoughSheets" /* CommandResult.NotEnoughSheets */;
39018
39103
  case "ADD_COLUMNS_ROWS":
@@ -39825,6 +39910,10 @@
39825
39910
  checkZonesAreInSheet(cmd) {
39826
39911
  if (!("sheetId" in cmd))
39827
39912
  return "Success" /* CommandResult.Success */;
39913
+ if ("ranges" in cmd &&
39914
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
39915
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39916
+ }
39828
39917
  return this.checkZonesExistInSheet(cmd.sheetId, this.getCommandZones(cmd));
39829
39918
  }
39830
39919
  }
@@ -39844,7 +39933,10 @@
39844
39933
  allowDispatch(cmd) {
39845
39934
  switch (cmd.type) {
39846
39935
  case "GROUP_HEADERS": {
39847
- const { start, end } = cmd;
39936
+ const { start, end, sheetId } = cmd;
39937
+ if (!this.getters.tryGetSheet(sheetId)) {
39938
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39939
+ }
39848
39940
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
39849
39941
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
39850
39942
  }
@@ -39857,7 +39949,10 @@
39857
39949
  break;
39858
39950
  }
39859
39951
  case "UNGROUP_HEADERS": {
39860
- const { start, end } = cmd;
39952
+ const { start, end, sheetId } = cmd;
39953
+ if (!this.getters.tryGetSheet(sheetId)) {
39954
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39955
+ }
39861
39956
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
39862
39957
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
39863
39958
  }
@@ -39868,6 +39963,9 @@
39868
39963
  }
39869
39964
  case "UNFOLD_HEADER_GROUP":
39870
39965
  case "FOLD_HEADER_GROUP":
39966
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
39967
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39968
+ }
39871
39969
  const group = this.findGroupWithStartEnd(cmd.sheetId, cmd.dimension, cmd.start, cmd.end);
39872
39970
  if (!group) {
39873
39971
  return "UnknownHeaderGroup" /* CommandResult.UnknownHeaderGroup */;
@@ -44137,6 +44235,9 @@
44137
44235
  };
44138
44236
  }
44139
44237
  function createSheetTransformation(toTransform, executed) {
44238
+ if (toTransform.sheetId === executed.sheetId) {
44239
+ toTransform = { ...toTransform, sheetId: `${toTransform.sheetId}~` };
44240
+ }
44140
44241
  if (toTransform.name === executed.name) {
44141
44242
  return {
44142
44243
  ...toTransform,
@@ -44773,21 +44874,14 @@
44773
44874
  if (!message)
44774
44875
  return;
44775
44876
  if (message.type === "REMOTE_REVISION") {
44776
- const revision = this.revisions.get(message.nextRevisionId);
44877
+ let revision = this.revisions.get(message.nextRevisionId);
44777
44878
  if (revision.commands.length === 0) {
44778
44879
  /**
44779
- * The command is empty, we have to drop all the next local revisions
44880
+ * The command is empty, we have to rebase all the next local revisions
44780
44881
  * to avoid issues with undo/redo
44781
44882
  */
44782
- this.revisions.drop(revision.id);
44783
- const revisionIds = this.pendingMessages
44784
- .filter((message) => message.type === "REMOTE_REVISION")
44785
- .map((message) => message.nextRevisionId);
44786
- this.trigger("pending-revisions-dropped", { revisionIds });
44787
- this.waitingAck = false;
44788
- this.waitingUndoRedoAck = false;
44789
- this.pendingMessages = [];
44790
- return;
44883
+ this.revisions.rebase(revision.id);
44884
+ revision = this.revisions.get(message.nextRevisionId);
44791
44885
  }
44792
44886
  message = {
44793
44887
  ...message,
@@ -44811,7 +44905,6 @@
44811
44905
  switch (message.type) {
44812
44906
  case "REMOTE_REVISION":
44813
44907
  case "REVISION_REDONE":
44814
- case "REVISION_UNDONE":
44815
44908
  case "SNAPSHOT_CREATED":
44816
44909
  this.waitingAck = false;
44817
44910
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
@@ -44819,6 +44912,25 @@
44819
44912
  this.processedRevisions.add(message.nextRevisionId);
44820
44913
  this.sendPendingMessage();
44821
44914
  break;
44915
+ case "REVISION_UNDONE": {
44916
+ this.waitingAck = false;
44917
+ this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
44918
+ const firstPendingRevisionId = this.pendingMessages.findIndex((message) => message.type === "REMOTE_REVISION");
44919
+ if (firstPendingRevisionId !== -1) {
44920
+ /**
44921
+ * Some revisions undergo transformations that may cause issues with
44922
+ * undo/redo if the transformation is destructive (we don't get back
44923
+ * the original command by transforming it with the inverse).
44924
+ * To prevent these problems, we must rebase all subsequent local
44925
+ * revisions.
44926
+ */
44927
+ this.revisions.rebase(this.pendingMessages[firstPendingRevisionId].nextRevisionId);
44928
+ }
44929
+ this.serverRevisionId = message.nextRevisionId;
44930
+ this.processedRevisions.add(message.nextRevisionId);
44931
+ this.sendPendingMessage();
44932
+ break;
44933
+ }
44822
44934
  }
44823
44935
  }
44824
44936
  isAlreadyProcessed(message) {
@@ -45523,7 +45635,9 @@
45523
45635
  const cfInTarget = this.getters
45524
45636
  .getConditionalFormats(targetSheetId)
45525
45637
  .find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
45526
- return cfInTarget ? cfInTarget : { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
45638
+ return cfInTarget
45639
+ ? cfInTarget
45640
+ : { ...originCF, id: this.uuidGenerator.smallUuid(), ranges: [] };
45527
45641
  }
45528
45642
  pasteDataValidation(origin, target) {
45529
45643
  const rule = this.getters.getValidationRuleForCell(origin);
@@ -45555,7 +45669,7 @@
45555
45669
  originRule.isBlocking === rule.isBlocking);
45556
45670
  return ruleInTargetSheet
45557
45671
  ? ruleInTargetSheet
45558
- : { ...originRule, id: this.uuidGenerator.uuidv4(), ranges: [] };
45672
+ : { ...originRule, id: this.uuidGenerator.smallUuid(), ranges: [] };
45559
45673
  }
45560
45674
  /**
45561
45675
  * Add or remove XCs to a given data validation rule.
@@ -47747,6 +47861,10 @@
47747
47861
  */
47748
47862
  checkZonesAreInSheet(cmd) {
47749
47863
  const sheetId = "sheetId" in cmd ? cmd.sheetId : this.getters.tryGetActiveSheetId();
47864
+ if ("ranges" in cmd &&
47865
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
47866
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
47867
+ }
47750
47868
  const zones = this.getters.getCommandZones(cmd);
47751
47869
  if (!sheetId && zones.length > 0) {
47752
47870
  return "NoActiveSheet" /* CommandResult.NoActiveSheet */;
@@ -47795,23 +47913,23 @@
47795
47913
  function repeatCreateChartCommand(getters, cmd) {
47796
47914
  return {
47797
47915
  ...repeatSheetDependantCommand(getters, cmd),
47798
- id: uuidGenerator.uuidv4(),
47916
+ id: uuidGenerator.smallUuid(),
47799
47917
  };
47800
47918
  }
47801
47919
  function repeatCreateImageCommand(getters, cmd) {
47802
47920
  return {
47803
47921
  ...repeatSheetDependantCommand(getters, cmd),
47804
- figureId: uuidGenerator.uuidv4(),
47922
+ figureId: uuidGenerator.smallUuid(),
47805
47923
  };
47806
47924
  }
47807
47925
  function repeatCreateFigureCommand(getters, cmd) {
47808
47926
  const newCmd = repeatSheetDependantCommand(getters, cmd);
47809
- newCmd.figure.id = uuidGenerator.uuidv4();
47927
+ newCmd.figure.id = uuidGenerator.smallUuid();
47810
47928
  return newCmd;
47811
47929
  }
47812
47930
  function repeatCreateSheetCommand(getters, cmd) {
47813
47931
  const newCmd = deepCopy(cmd);
47814
- newCmd.sheetId = uuidGenerator.uuidv4();
47932
+ newCmd.sheetId = uuidGenerator.smallUuid();
47815
47933
  const sheetName = cmd.name || getters.getSheet(getters.getActiveSheetId()).name;
47816
47934
  // Extract the prefix of the sheet name (everything before the number at the end of the name)
47817
47935
  const namePrefix = sheetName.match(/(.+?)\d*$/)?.[1] || sheetName;
@@ -48006,7 +48124,6 @@
48006
48124
  super(config);
48007
48125
  this.session = config.session;
48008
48126
  this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
48009
- this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
48010
48127
  this.session.on("snapshot", this, () => {
48011
48128
  this.undoStack = [];
48012
48129
  this.redoStack = [];
@@ -48076,10 +48193,6 @@
48076
48193
  const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
48077
48194
  return canRepeatRevision(lastNonRedoRevision);
48078
48195
  }
48079
- drop(revisionIds) {
48080
- this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
48081
- this.redoStack = [];
48082
- }
48083
48196
  onNewLocalStateUpdate({ id }) {
48084
48197
  this.undoStack.push(id);
48085
48198
  this.redoStack = [];
@@ -48346,7 +48459,7 @@
48346
48459
  x: maxX < width ? 0 : Math.min(targetX, maxX - width),
48347
48460
  y: maxY < height ? 0 : Math.min(targetY, maxY - height),
48348
48461
  };
48349
- const newId = new UuidGenerator().uuidv4();
48462
+ const newId = new UuidGenerator().smallUuid();
48350
48463
  this.copiedFigureContent.paste(sheetId, newId, position, { height, width });
48351
48464
  if (this.operation === "CUT") {
48352
48465
  this.dispatch("DELETE_FIGURE", {
@@ -50049,23 +50162,7 @@
50049
50162
  gridSelection: deepCopy(gridSelection),
50050
50163
  };
50051
50164
  }
50052
- if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
50053
- const currentSheetIds = this.getters.getVisibleSheetIds();
50054
- this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
50055
- if (this.activeSheet.id in this.sheetsData) {
50056
- const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
50057
- this.selectCell(anchor.cell.col, anchor.cell.row);
50058
- }
50059
- else {
50060
- this.selectCell(0, 0);
50061
- }
50062
- const { col, row } = this.gridSelection.anchor.cell;
50063
- this.moveClient({
50064
- sheetId: this.getters.getActiveSheetId(),
50065
- col,
50066
- row,
50067
- });
50068
- }
50165
+ this.fallbackToVisibleSheet();
50069
50166
  const sheetId = this.getters.getActiveSheetId();
50070
50167
  this.gridSelection.zones = this.gridSelection.zones.map((z) => this.getters.expandZone(sheetId, z));
50071
50168
  this.gridSelection.anchor.zone = this.getters.expandZone(sheetId, this.gridSelection.anchor.zone);
@@ -50075,6 +50172,7 @@
50075
50172
  }
50076
50173
  }
50077
50174
  finalize() {
50175
+ this.fallbackToVisibleSheet();
50078
50176
  /** Any change to the selection has to be reflected in the selection processor. */
50079
50177
  this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
50080
50178
  }
@@ -50426,6 +50524,25 @@
50426
50524
  }
50427
50525
  return "Success" /* CommandResult.Success */;
50428
50526
  }
50527
+ fallbackToVisibleSheet() {
50528
+ if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
50529
+ const currentSheetIds = this.getters.getVisibleSheetIds();
50530
+ this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
50531
+ if (this.activeSheet.id in this.sheetsData) {
50532
+ const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
50533
+ this.selectCell(anchor.cell.col, anchor.cell.row);
50534
+ }
50535
+ else {
50536
+ this.selectCell(0, 0);
50537
+ }
50538
+ const { col, row } = this.gridSelection.anchor.cell;
50539
+ this.moveClient({
50540
+ sheetId: this.getters.getActiveSheetId(),
50541
+ col,
50542
+ row,
50543
+ });
50544
+ }
50545
+ }
50429
50546
  //-------------------------------------------
50430
50547
  // Helpers for extensions
50431
50548
  // ------------------------------------------
@@ -51573,7 +51690,9 @@
51573
51690
  case "UNGROUP_HEADERS":
51574
51691
  case "GROUP_HEADERS":
51575
51692
  case "CREATE_SHEET":
51576
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
51693
+ if (this.getters.tryGetSheet(cmd.sheetId)) {
51694
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
51695
+ }
51577
51696
  break;
51578
51697
  case "DUPLICATE_SHEET":
51579
51698
  this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
@@ -51581,12 +51700,14 @@
51581
51700
  }
51582
51701
  }
51583
51702
  finalize() {
51584
- if (this.isDirty) {
51585
- for (const sheetId of this.getters.getSheetIds()) {
51703
+ for (const sheetId of this.getters.getSheetIds()) {
51704
+ // sheets can be created without this plugin being aware of it
51705
+ // in concurrent situations.
51706
+ if (this.isDirty || !this.headerPositions[sheetId]) {
51586
51707
  this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
51587
51708
  }
51588
- this.isDirty = false;
51589
51709
  }
51710
+ this.isDirty = false;
51590
51711
  }
51591
51712
  /**
51592
51713
  * Returns the size, start and end coordinates of a column on an unfolded sheet
@@ -52270,7 +52391,7 @@
52270
52391
  clickAddSheet(ev) {
52271
52392
  const activeSheetId = this.env.model.getters.getActiveSheetId();
52272
52393
  const position = this.env.model.getters.getSheetIds().findIndex((sheetId) => sheetId === activeSheetId) + 1;
52273
- const sheetId = this.env.model.uuidGenerator.uuidv4();
52394
+ const sheetId = this.env.model.uuidGenerator.smallUuid();
52274
52395
  const name = this.env.model.getters.getNextSheetName(_t("Sheet"));
52275
52396
  this.env.model.dispatch("CREATE_SHEET", { sheetId, position, name });
52276
52397
  this.env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
@@ -53332,7 +53453,7 @@
53332
53453
  .o-font-size-editor {
53333
53454
  height: calc(100% - 4px);
53334
53455
  input.o-font-size {
53335
- outline-color: ${SELECTION_BORDER_COLOR};
53456
+ outline: none;
53336
53457
  height: 20px;
53337
53458
  width: 23px;
53338
53459
  }
@@ -54507,7 +54628,7 @@
54507
54628
  }
54508
54629
  /**
54509
54630
  * Drop the operation and all following operations in every
54510
- * branch
54631
+ * branches
54511
54632
  */
54512
54633
  drop(operationId) {
54513
54634
  for (const branch of this.branches) {
@@ -54812,9 +54933,16 @@
54812
54933
  this.fastForward();
54813
54934
  this.insert(redoId, this.buildEmpty(redoId), insertAfter);
54814
54935
  }
54815
- drop(operationId) {
54936
+ rebase(operationId) {
54937
+ const operation = this.get(operationId);
54938
+ const execution = [...this.tree.execution(this.HEAD_BRANCH).startAfter(operationId)];
54816
54939
  this.revertBefore(operationId);
54940
+ const baseId = this.HEAD_OPERATION.id;
54817
54941
  this.tree.drop(operationId);
54942
+ this.insert(operationId, operation, baseId);
54943
+ for (const { operation } of execution) {
54944
+ this.insert(operation.id, operation.data, this.HEAD_OPERATION.id);
54945
+ }
54818
54946
  }
54819
54947
  getRevertedExecution() {
54820
54948
  const data = [];
@@ -57391,6 +57519,11 @@
57391
57519
  dispatch: (command) => {
57392
57520
  const result = this.checkDispatchAllowed(command);
57393
57521
  if (!result.isSuccessful) {
57522
+ // core views plugins need to be invalidated
57523
+ this.dispatchToHandlers(this.coreHandlers, {
57524
+ type: "UNDO",
57525
+ commands: [command],
57526
+ });
57394
57527
  return;
57395
57528
  }
57396
57529
  this.isReplayingCommand = true;
@@ -57419,7 +57552,7 @@
57419
57552
  }
57420
57553
  setupConfig(config) {
57421
57554
  const client = config.client || {
57422
- id: this.uuidGenerator.uuidv4(),
57555
+ id: this.uuidGenerator.smallUuid(),
57423
57556
  name: _t("Anonymous").toString(),
57424
57557
  };
57425
57558
  const transportService = config.transportService || new LocalTransportService();
@@ -57840,9 +57973,9 @@
57840
57973
  exports.tokenize = tokenize;
57841
57974
 
57842
57975
 
57843
- __info__.version = "17.0.48";
57844
- __info__.date = "2025-02-05T07:18:39.331Z";
57845
- __info__.hash = "aea9101";
57976
+ __info__.version = "17.0.50";
57977
+ __info__.date = "2025-02-14T08:37:34.289Z";
57978
+ __info__.hash = "cc1037d";
57846
57979
 
57847
57980
 
57848
57981
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);