@odoo/o-spreadsheet 18.0.15 → 18.0.17

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.0.15
6
- * @date 2025-02-10T08:59:22.993Z
7
- * @hash 5b19f88
5
+ * @version 18.0.17
6
+ * @date 2025-02-25T05:58:39.632Z
7
+ * @hash 2ee4347
8
8
  */
9
9
 
10
10
  'use strict';
@@ -2066,17 +2066,7 @@ function toZoneWithoutBoundaryChanges(xc) {
2066
2066
  */
2067
2067
  function toUnboundedZone(xc) {
2068
2068
  const zone = toZoneWithoutBoundaryChanges(xc);
2069
- if (zone.right !== undefined && zone.right < zone.left) {
2070
- const tmp = zone.left;
2071
- zone.left = zone.right;
2072
- zone.right = tmp;
2073
- }
2074
- if (zone.bottom !== undefined && zone.bottom < zone.top) {
2075
- const tmp = zone.top;
2076
- zone.top = zone.bottom;
2077
- zone.bottom = tmp;
2078
- }
2079
- return zone;
2069
+ return reorderZone(zone);
2080
2070
  }
2081
2071
  /**
2082
2072
  * Convert from a cartesian reference to a Zone.
@@ -2350,11 +2340,11 @@ function positions(zone) {
2350
2340
  return positions;
2351
2341
  }
2352
2342
  function reorderZone(zone) {
2353
- if (zone.left > zone.right) {
2354
- zone = { left: zone.right, right: zone.left, top: zone.top, bottom: zone.bottom };
2343
+ if (zone.right !== undefined && zone.left > zone.right) {
2344
+ zone = { ...zone, left: zone.right, right: zone.left };
2355
2345
  }
2356
- if (zone.top > zone.bottom) {
2357
- zone = { left: zone.left, right: zone.right, top: zone.bottom, bottom: zone.top };
2346
+ if (zone.bottom !== undefined && zone.top > zone.bottom) {
2347
+ zone = { ...zone, top: zone.bottom, bottom: zone.top };
2358
2348
  }
2359
2349
  return zone;
2360
2350
  }
@@ -3248,12 +3238,12 @@ function isTargetDependent(cmd) {
3248
3238
  function isRangeDependant(cmd) {
3249
3239
  return "ranges" in cmd;
3250
3240
  }
3251
- function isZoneDependent(cmd) {
3252
- return "zone" in cmd;
3253
- }
3254
3241
  function isPositionDependent(cmd) {
3255
3242
  return "col" in cmd && "row" in cmd && "sheetId" in cmd;
3256
3243
  }
3244
+ function isZoneDependent(cmd) {
3245
+ return "sheetId" in cmd && "zone" in cmd;
3246
+ }
3257
3247
  const invalidateEvaluationCommands = new Set([
3258
3248
  "RENAME_SHEET",
3259
3249
  "DELETE_SHEET",
@@ -3265,6 +3255,7 @@ const invalidateEvaluationCommands = new Set([
3265
3255
  "REDO",
3266
3256
  "ADD_MERGE",
3267
3257
  "REMOVE_MERGE",
3258
+ "DUPLICATE_SHEET",
3268
3259
  "UPDATE_LOCALE",
3269
3260
  "ADD_PIVOT",
3270
3261
  "UPDATE_PIVOT",
@@ -3293,7 +3284,6 @@ const invalidateChartEvaluationCommands = new Set([
3293
3284
  ]);
3294
3285
  const invalidateDependenciesCommands = new Set(["MOVE_RANGES"]);
3295
3286
  const invalidateCFEvaluationCommands = new Set([
3296
- "DUPLICATE_SHEET",
3297
3287
  "EVALUATE_CELLS",
3298
3288
  "ADD_CONDITIONAL_FORMAT",
3299
3289
  "REMOVE_CONDITIONAL_FORMAT",
@@ -3462,6 +3452,7 @@ exports.CommandResult = void 0;
3462
3452
  CommandResult["InvalidRange"] = "InvalidRange";
3463
3453
  CommandResult["InvalidZones"] = "InvalidZones";
3464
3454
  CommandResult["InvalidSheetId"] = "InvalidSheetId";
3455
+ CommandResult["InvalidCellId"] = "InvalidCellId";
3465
3456
  CommandResult["InvalidFigureId"] = "InvalidFigureId";
3466
3457
  CommandResult["InputAlreadyFocused"] = "InputAlreadyFocused";
3467
3458
  CommandResult["MaximumRangesReached"] = "MaximumRangesReached";
@@ -6347,20 +6338,53 @@ class UuidGenerator {
6347
6338
  setIsFastStrategy(isFast) {
6348
6339
  this.isFastIdStrategy = isFast;
6349
6340
  }
6341
+ /**
6342
+ * Generates a custom UUID using a simple 36^12 method (8-character alphanumeric string with lowercase letters)
6343
+ * This has a higher chance of collision than a UUIDv4, but not only faster to generate than an UUIDV4,
6344
+ * it also has a smaller size, which is preferable to alleviate the overall data size.
6345
+ *
6346
+ * This method is preferable when generating uuids for the core data (sheetId, figureId, etc)
6347
+ * as they will appear several times in the revisions and local history.
6348
+ *
6349
+ */
6350
+ smallUuid() {
6351
+ if (this.isFastIdStrategy) {
6352
+ this.fastIdStart++;
6353
+ return String(this.fastIdStart);
6354
+ }
6355
+ else if (window.crypto) {
6356
+ return "10000000-1000".replace(/[01]/g, (c) => {
6357
+ const n = Number(c);
6358
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6359
+ });
6360
+ }
6361
+ else {
6362
+ // mainly for jest and other browsers that do not have the crypto functionality
6363
+ return "xxxxxxxx-xxxx".replace(/[xy]/g, function (c) {
6364
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
6365
+ return v.toString(16);
6366
+ });
6367
+ }
6368
+ }
6369
+ /**
6370
+ * Generates an UUIDV4, has astronomically low chance of collision, but is larger in size than the smallUuid.
6371
+ * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
6372
+ */
6350
6373
  uuidv4() {
6351
6374
  if (this.isFastIdStrategy) {
6352
6375
  this.fastIdStart++;
6353
6376
  return String(this.fastIdStart);
6354
- //@ts-ignore
6355
6377
  }
6356
- else if (window.crypto && window.crypto.getRandomValues) {
6357
- //@ts-ignore
6358
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6378
+ else if (window.crypto) {
6379
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
6380
+ const n = Number(c);
6381
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6382
+ });
6359
6383
  }
6360
6384
  else {
6361
6385
  // mainly for jest and other browsers that do not have the crypto functionality
6362
6386
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
6363
- var r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
6387
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
6364
6388
  return v.toString(16);
6365
6389
  });
6366
6390
  }
@@ -8339,7 +8363,7 @@ class ChartClipboardHandler extends AbstractFigureClipboardHandler {
8339
8363
  };
8340
8364
  }
8341
8365
  getPasteTarget(sheetId, target, content, options) {
8342
- const newId = new UuidGenerator().uuidv4();
8366
+ const newId = new UuidGenerator().smallUuid();
8343
8367
  return { zones: [], figureId: newId, sheetId };
8344
8368
  }
8345
8369
  paste(target, clippedContent, options) {
@@ -8505,7 +8529,7 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
8505
8529
  if (!targetCF && queuedCfs) {
8506
8530
  targetCF = queuedCfs.find((queued) => queued.cf.stopIfTrue === originCF.stopIfTrue && deepEquals(queued.cf.rule, originCF.rule))?.cf;
8507
8531
  }
8508
- return targetCF || { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
8532
+ return targetCF || { ...originCF, id: this.uuidGenerator.smallUuid(), ranges: [] };
8509
8533
  }
8510
8534
  }
8511
8535
 
@@ -8598,7 +8622,7 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
8598
8622
  }
8599
8623
  return (targetRule || {
8600
8624
  ...originRule,
8601
- id: newId ? this.uuidGenerator.uuidv4() : originRule.id,
8625
+ id: newId ? this.uuidGenerator.smallUuid() : originRule.id,
8602
8626
  ranges: [],
8603
8627
  });
8604
8628
  }
@@ -8660,7 +8684,7 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
8660
8684
  };
8661
8685
  }
8662
8686
  getPasteTarget(sheetId, target, content, options) {
8663
- const newId = new UuidGenerator().uuidv4();
8687
+ const newId = new UuidGenerator().smallUuid();
8664
8688
  return { sheetId, zones: [], figureId: newId };
8665
8689
  }
8666
8690
  paste(target, clippedContent, options) {
@@ -9639,6 +9663,9 @@ function getTrendDatasetForBarChart(config, dataset) {
9639
9663
  const filteredValues = [];
9640
9664
  const filteredLabels = [];
9641
9665
  const labels = [];
9666
+ if (dataset.hidden) {
9667
+ return;
9668
+ }
9642
9669
  for (let i = 0; i < dataset.data.length; i++) {
9643
9670
  if (typeof dataset.data[i] === "number") {
9644
9671
  filteredValues.push(dataset.data[i]);
@@ -11974,6 +12001,25 @@ const LN = {
11974
12001
  isExported: true,
11975
12002
  };
11976
12003
  // -----------------------------------------------------------------------------
12004
+ // LOG
12005
+ // -----------------------------------------------------------------------------
12006
+ const LOG = {
12007
+ description: _t("The logarithm of a number, for a given base."),
12008
+ args: [
12009
+ arg("value (number)", _t("The value for which to calculate the logarithm.")),
12010
+ arg("base (number, default=10)", _t("The base of the logarithm.")),
12011
+ ],
12012
+ compute: function (value, base = { value: 10 }) {
12013
+ const _value = toNumber(value, this.locale);
12014
+ const _base = toNumber(base, this.locale);
12015
+ assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
12016
+ assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
12017
+ assert(() => _base !== 1, _t("The base must be different from 1."));
12018
+ return Math.log10(_value) / Math.log10(_base);
12019
+ },
12020
+ isExported: true,
12021
+ };
12022
+ // -----------------------------------------------------------------------------
11977
12023
  // MOD
11978
12024
  // -----------------------------------------------------------------------------
11979
12025
  function mod(dividend, divisor) {
@@ -12513,6 +12559,7 @@ var math = /*#__PURE__*/Object.freeze({
12513
12559
  ISODD: ISODD,
12514
12560
  ISO_CEILING: ISO_CEILING,
12515
12561
  LN: LN,
12562
+ LOG: LOG,
12516
12563
  MOD: MOD,
12517
12564
  MUNIT: MUNIT,
12518
12565
  ODD: ODD,
@@ -15062,7 +15109,7 @@ const SORTN = {
15062
15109
  }
15063
15110
  }
15064
15111
  },
15065
- isExported: true,
15112
+ isExported: false,
15066
15113
  };
15067
15114
  // -----------------------------------------------------------------------------
15068
15115
  // UNIQUE
@@ -28516,17 +28563,15 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
28516
28563
  plugins: [],
28517
28564
  };
28518
28565
  }
28519
- function getChartLabelFormat(getters, range) {
28566
+ function getChartLabelFormat(getters, range, shouldRemoveFirstLabel) {
28520
28567
  if (!range)
28521
28568
  return undefined;
28522
- const { sheetId, zone: { left, top, bottom }, } = range;
28523
- for (let row = top; row <= bottom; row++) {
28524
- const format = getters.getEvaluatedCell({ sheetId, col: left, row }).format;
28525
- if (format) {
28526
- return format;
28527
- }
28569
+ const { sheetId, zone } = range;
28570
+ const formats = positions(zone).map((position) => getters.getEvaluatedCell({ sheetId, ...position }).format);
28571
+ if (shouldRemoveFirstLabel) {
28572
+ formats.shift();
28528
28573
  }
28529
- return undefined;
28574
+ return formats.find((format) => format !== undefined);
28530
28575
  }
28531
28576
  function getChartLabelValues(getters, dataSets, labelRange) {
28532
28577
  let labels = { values: [], formattedValues: [] };
@@ -28581,10 +28626,8 @@ function getChartDatasetFormat(getters, dataSets) {
28581
28626
  function getChartDatasetValues(getters, dataSets) {
28582
28627
  const datasetValues = [];
28583
28628
  for (const [dsIndex, ds] of Object.entries(dataSets)) {
28584
- if (getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left)) {
28585
- continue;
28586
- }
28587
28629
  let label;
28630
+ let hidden = getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left);
28588
28631
  if (ds.labelCell) {
28589
28632
  const labelRange = ds.labelCell;
28590
28633
  const cell = labelRange
@@ -28611,9 +28654,9 @@ function getChartDatasetValues(getters, dataSets) {
28611
28654
  data.fill(1);
28612
28655
  }
28613
28656
  else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28614
- continue;
28657
+ hidden = true;
28615
28658
  }
28616
- datasetValues.push({ data, label });
28659
+ datasetValues.push({ data, label, hidden });
28617
28660
  }
28618
28661
  return datasetValues;
28619
28662
  }
@@ -28680,6 +28723,20 @@ const backgroundColorChartJSPlugin = {
28680
28723
  ctx.restore();
28681
28724
  },
28682
28725
  };
28726
+ function getChartJsLegend(fontColor, legend = {}) {
28727
+ return {
28728
+ ...legend,
28729
+ labels: {
28730
+ color: fontColor,
28731
+ filter: (legendItem, data) => {
28732
+ return "datasetIndex" in legendItem
28733
+ ? !data.datasets[legendItem.datasetIndex].hidden
28734
+ : true;
28735
+ },
28736
+ ...legend.labels,
28737
+ },
28738
+ };
28739
+ }
28683
28740
 
28684
28741
  class BarChart extends AbstractChart {
28685
28742
  dataSets;
@@ -28814,9 +28871,7 @@ function createBarChartRuntime(chart, getters) {
28814
28871
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
28815
28872
  let labels = labelValues.formattedValues;
28816
28873
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
28817
- if (chart.dataSetsHaveTitle &&
28818
- dataSetsValues[0] &&
28819
- labels.length > dataSetsValues[0].data.length) {
28874
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
28820
28875
  labels.shift();
28821
28876
  }
28822
28877
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -28831,9 +28886,7 @@ function createBarChartRuntime(chart, getters) {
28831
28886
  ...localeFormat,
28832
28887
  horizontalChart: chart.horizontal,
28833
28888
  });
28834
- const legend = {
28835
- labels: { color: fontColor },
28836
- };
28889
+ const legend = getChartJsLegend(fontColor);
28837
28890
  if (chart.legendPosition === "none") {
28838
28891
  legend.display = false;
28839
28892
  }
@@ -28897,11 +28950,12 @@ function createBarChartRuntime(chart, getters) {
28897
28950
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28898
28951
  const trendDatasets = [];
28899
28952
  for (const index in dataSetsValues) {
28900
- const { label, data } = dataSetsValues[index];
28953
+ const { label, data, hidden } = dataSetsValues[index];
28901
28954
  const color = colors.next();
28902
28955
  const dataset = {
28903
28956
  label,
28904
28957
  data,
28958
+ hidden,
28905
28959
  borderColor: definition.background || BACKGROUND_CHART_COLOR,
28906
28960
  borderWidth: definition.stacked ? 1 : 0,
28907
28961
  backgroundColor: color,
@@ -29085,8 +29139,8 @@ function fixEmptyLabelsForDateCharts(labels, dataSetsValues) {
29085
29139
  }
29086
29140
  return { labels: newLabels, dataSetsValues: newDatasets };
29087
29141
  }
29088
- function canChartParseLabels(labelRange, getters) {
29089
- return canBeDateChart(labelRange, getters) || canBeLinearChart(labelRange, getters);
29142
+ function canChartParseLabels(chart, getters) {
29143
+ return canBeDateChart(chart, getters) || canBeLinearChart(chart, getters);
29090
29144
  }
29091
29145
  function getChartAxisType(chart, getters) {
29092
29146
  if (isDateChart(chart, getters) && isLuxonTimeAdapterInstalled()) {
@@ -29098,23 +29152,26 @@ function getChartAxisType(chart, getters) {
29098
29152
  return "category";
29099
29153
  }
29100
29154
  function isDateChart(chart, getters) {
29101
- return !chart.labelsAsText && canBeDateChart(chart.labelRange, getters);
29155
+ return !chart.labelsAsText && canBeDateChart(chart, getters);
29102
29156
  }
29103
29157
  function isLinearChart(chart, getters) {
29104
- return !chart.labelsAsText && canBeLinearChart(chart.labelRange, getters);
29158
+ return !chart.labelsAsText && canBeLinearChart(chart, getters);
29105
29159
  }
29106
- function canBeDateChart(labelRange, getters) {
29107
- if (!labelRange || !canBeLinearChart(labelRange, getters)) {
29160
+ function canBeDateChart(chart, getters) {
29161
+ if (!chart.labelRange || !canBeLinearChart(chart, getters)) {
29108
29162
  return false;
29109
29163
  }
29110
- const labelFormat = getChartLabelFormat(getters, labelRange);
29164
+ const labelFormat = getChartLabelFormat(getters, chart.labelRange, shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle));
29111
29165
  return Boolean(labelFormat && timeFormatLuxonCompatible.test(labelFormat));
29112
29166
  }
29113
- function canBeLinearChart(labelRange, getters) {
29114
- if (!labelRange) {
29167
+ function canBeLinearChart(chart, getters) {
29168
+ if (!chart.labelRange) {
29115
29169
  return false;
29116
29170
  }
29117
- const labels = getters.getRangeValues(labelRange);
29171
+ const labels = getters.getRangeValues(chart.labelRange);
29172
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
29173
+ labels.shift();
29174
+ }
29118
29175
  if (labels.some((label) => isNaN(Number(label)) && label)) {
29119
29176
  return false;
29120
29177
  }
@@ -29142,6 +29199,9 @@ function getTrendDatasetForLineChart(config, dataset, axisType, locale) {
29142
29199
  const filteredLabels = [];
29143
29200
  const labels = [];
29144
29201
  const datasetLength = dataset.data.length;
29202
+ if (dataset.hidden) {
29203
+ return;
29204
+ }
29145
29205
  if (datasetLength < 2) {
29146
29206
  return;
29147
29207
  }
@@ -29198,9 +29258,8 @@ function createLineOrScatterChartRuntime(chart, getters) {
29198
29258
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
29199
29259
  let labels = axisType === "linear" ? labelValues.values : labelValues.formattedValues;
29200
29260
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
29201
- if (chart.dataSetsHaveTitle &&
29202
- dataSetsValues[0] &&
29203
- labels.length > dataSetsValues[0].data.length) {
29261
+ const removeFirstLabel = shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle);
29262
+ if (removeFirstLabel) {
29204
29263
  labels.shift();
29205
29264
  }
29206
29265
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -29216,9 +29275,8 @@ function createLineOrScatterChartRuntime(chart, getters) {
29216
29275
  const options = { format: dataSetFormat, locale, truncateLabels };
29217
29276
  const fontColor = chartFontColor(chart.background);
29218
29277
  const config = getDefaultChartJsRuntime(chart, labels, fontColor, options);
29219
- const legend = {
29278
+ const legend = getChartJsLegend(fontColor, {
29220
29279
  labels: {
29221
- color: fontColor,
29222
29280
  generateLabels(chart) {
29223
29281
  // color the legend labels with the dataset color, without any transparency
29224
29282
  const { data } = chart;
@@ -29229,7 +29287,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
29229
29287
  return labels;
29230
29288
  },
29231
29289
  },
29232
- };
29290
+ });
29233
29291
  if (chart.legendPosition === "none") {
29234
29292
  legend.display = false;
29235
29293
  }
@@ -29290,12 +29348,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
29290
29348
  background: chart.background,
29291
29349
  callback: formatTickValue(options),
29292
29350
  };
29293
- if (chart.dataSetsHaveTitle &&
29294
- dataSetsValues[0] &&
29295
- labels.length > dataSetsValues[0].data.length) {
29296
- labels.shift();
29297
- }
29298
- const labelFormat = getChartLabelFormat(getters, chart.labelRange);
29351
+ const labelFormat = getChartLabelFormat(getters, chart.labelRange, removeFirstLabel);
29299
29352
  if (axisType === "time") {
29300
29353
  const axis = {
29301
29354
  type: "time",
@@ -29334,7 +29387,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
29334
29387
  const cumulative = "cumulative" in chart ? chart.cumulative : false;
29335
29388
  const definition = chart.getDefinition();
29336
29389
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
29337
- for (let [index, { label, data }] of dataSetsValues.entries()) {
29390
+ for (let [index, { label, data, hidden }] of dataSetsValues.entries()) {
29338
29391
  const color = colors.next();
29339
29392
  let backgroundRGBA = colorToRGBA(color);
29340
29393
  if (areaChart) {
@@ -29358,6 +29411,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
29358
29411
  const dataset = {
29359
29412
  label,
29360
29413
  data,
29414
+ hidden,
29361
29415
  tension: 0, // 0 -> render straight lines, which is much faster
29362
29416
  borderColor: color,
29363
29417
  backgroundColor,
@@ -29553,9 +29607,7 @@ function createComboChartRuntime(chart, getters) {
29553
29607
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
29554
29608
  let labels = labelValues.formattedValues;
29555
29609
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
29556
- if (chart.dataSetsHaveTitle &&
29557
- dataSetsValues[0] &&
29558
- labels.length > dataSetsValues[0].data.length) {
29610
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
29559
29611
  labels.shift();
29560
29612
  }
29561
29613
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -29565,9 +29617,7 @@ function createComboChartRuntime(chart, getters) {
29565
29617
  const localeFormat = { format: mainDataSetFormat, locale };
29566
29618
  const fontColor = chartFontColor(chart.background);
29567
29619
  const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
29568
- const legend = {
29569
- labels: { color: fontColor },
29570
- };
29620
+ const legend = getChartJsLegend(fontColor);
29571
29621
  if (chart.legendPosition === "none") {
29572
29622
  legend.display = false;
29573
29623
  }
@@ -29631,13 +29681,14 @@ function createComboChartRuntime(chart, getters) {
29631
29681
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
29632
29682
  let maxLength = 0;
29633
29683
  const trendDatasets = [];
29634
- for (let [index, { label, data }] of dataSetsValues.entries()) {
29684
+ for (let [index, { label, data, hidden }] of dataSetsValues.entries()) {
29635
29685
  const design = definition.dataSets[index];
29636
29686
  const color = colors.next();
29637
29687
  const type = design?.type ?? "line";
29638
29688
  const dataset = {
29639
29689
  label: design?.label ?? label,
29640
29690
  data,
29691
+ hidden,
29641
29692
  borderColor: color,
29642
29693
  backgroundColor: color,
29643
29694
  yAxisID: design?.yAxisId ?? "y",
@@ -30245,10 +30296,8 @@ function filterNegativeValues(labels, datasets) {
30245
30296
  function createPieChartRuntime(chart, getters) {
30246
30297
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
30247
30298
  let labels = labelValues.formattedValues;
30248
- let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
30249
- if (chart.dataSetsHaveTitle &&
30250
- dataSetsValues[0] &&
30251
- labels.length > dataSetsValues[0].data.length) {
30299
+ let dataSetsValues = getChartDatasetValues(getters, chart.dataSets).filter((dataSet) => !dataSet.hidden);
30300
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
30252
30301
  labels.shift();
30253
30302
  }
30254
30303
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -30265,7 +30314,7 @@ function createPieChartRuntime(chart, getters) {
30265
30314
  const dataset = {
30266
30315
  label,
30267
30316
  data,
30268
- borderColor: BACKGROUND_CHART_COLOR,
30317
+ borderColor: chart.background || "#FFFFFF",
30269
30318
  backgroundColor,
30270
30319
  hoverOffset: 30,
30271
30320
  };
@@ -30394,8 +30443,9 @@ function createPyramidChartRuntime(chart, getters) {
30394
30443
  const barDef = { ...chart.getDefinition(), type: "bar" };
30395
30444
  const barChart = new BarChart(barDef, chart.sheetId, getters);
30396
30445
  const barRuntime = createBarChartRuntime(barChart, getters);
30446
+ // align design with filtered datasets
30397
30447
  const config = barRuntime.chartJsConfig;
30398
- let datasets = config.data?.datasets;
30448
+ let datasets = config.data?.datasets.filter((dataSet) => !dataSet.hidden);
30399
30449
  if (datasets && datasets[0]) {
30400
30450
  datasets[0].data = datasets[0].data.map((value) => (value > 0 ? value : 0));
30401
30451
  }
@@ -30792,10 +30842,8 @@ function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat
30792
30842
  function createWaterfallChartRuntime(chart, getters) {
30793
30843
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
30794
30844
  let labels = labelValues.formattedValues;
30795
- let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
30796
- if (chart.dataSetsHaveTitle &&
30797
- dataSetsValues[0] &&
30798
- labels.length > dataSetsValues[0].data.length) {
30845
+ let dataSetsValues = getChartDatasetValues(getters, chart.dataSets).filter((ds) => !ds.hidden);
30846
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
30799
30847
  labels.shift();
30800
30848
  }
30801
30849
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -32291,7 +32339,7 @@ const linkSheet = {
32291
32339
  const deleteSheet = {
32292
32340
  name: _t("Delete"),
32293
32341
  isVisible: (env) => {
32294
- return env.model.getters.getSheetIds().length > 1;
32342
+ return env.model.getters.getVisibleSheetIds().length > 1;
32295
32343
  },
32296
32344
  execute: (env) => env.askConfirmation(_t("Are you sure you want to delete this sheet?"), () => {
32297
32345
  env.model.dispatch("DELETE_SHEET", { sheetId: env.model.getters.getActiveSheetId() });
@@ -32302,7 +32350,7 @@ const duplicateSheet = {
32302
32350
  name: _t("Duplicate"),
32303
32351
  execute: (env) => {
32304
32352
  const sheetIdFrom = env.model.getters.getActiveSheetId();
32305
- const sheetIdTo = env.model.uuidGenerator.uuidv4();
32353
+ const sheetIdTo = env.model.uuidGenerator.smallUuid();
32306
32354
  env.model.dispatch("DUPLICATE_SHEET", {
32307
32355
  sheetId: sheetIdFrom,
32308
32356
  sheetIdTo,
@@ -32929,20 +32977,21 @@ function getSmartChartDefinition(zone, getters) {
32929
32977
  }
32930
32978
  // Only display legend for several datasets.
32931
32979
  const newLegendPos = dataSetZone.right === dataSetZone.left ? "none" : "top";
32932
- const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
32933
- if (canChartParseLabels(labelRange, getters)) {
32934
- return {
32935
- title: {},
32936
- dataSets,
32937
- labelsAsText: false,
32938
- stacked: false,
32939
- aggregated: false,
32940
- cumulative: false,
32941
- labelRange: labelRangeXc,
32942
- type: "line",
32943
- dataSetsHaveTitle,
32944
- legendPosition: newLegendPos,
32945
- };
32980
+ const lineChartDefinition = {
32981
+ title: {},
32982
+ dataSets,
32983
+ labelsAsText: false,
32984
+ stacked: false,
32985
+ aggregated: false,
32986
+ cumulative: false,
32987
+ labelRange: labelRangeXc,
32988
+ type: "line",
32989
+ dataSetsHaveTitle,
32990
+ legendPosition: newLegendPos,
32991
+ };
32992
+ const chart = new LineChart(lineChartDefinition, sheetId, getters);
32993
+ if (canChartParseLabels(chart, getters)) {
32994
+ return lineChartDefinition;
32946
32995
  }
32947
32996
  const _dataSets = createDataSets(getters, dataSets, sheetId, dataSetsHaveTitle);
32948
32997
  if (singleColumn &&
@@ -33310,7 +33359,7 @@ const HIDE_ROWS_NAME = (env) => {
33310
33359
  //------------------------------------------------------------------------------
33311
33360
  const CREATE_CHART = (env) => {
33312
33361
  const getters = env.model.getters;
33313
- const id = env.model.uuidGenerator.uuidv4();
33362
+ const id = env.model.uuidGenerator.smallUuid();
33314
33363
  const sheetId = getters.getActiveSheetId();
33315
33364
  if (getZoneArea(env.model.getters.getSelectedZone()) === 1) {
33316
33365
  env.model.selection.selectTableAroundSelection();
@@ -33333,8 +33382,8 @@ const CREATE_CHART = (env) => {
33333
33382
  // Pivots
33334
33383
  //------------------------------------------------------------------------------
33335
33384
  const CREATE_PIVOT = (env) => {
33336
- const pivotId = env.model.uuidGenerator.uuidv4();
33337
- const newSheetId = env.model.uuidGenerator.uuidv4();
33385
+ const pivotId = env.model.uuidGenerator.smallUuid();
33386
+ const newSheetId = env.model.uuidGenerator.smallUuid();
33338
33387
  const result = env.model.dispatch("INSERT_NEW_PIVOT", { pivotId, newSheetId });
33339
33388
  if (result.isSuccessful) {
33340
33389
  env.openSidePanel("PivotSidePanel", { pivotId });
@@ -33393,7 +33442,7 @@ async function requestImage(env) {
33393
33442
  const CREATE_IMAGE = async (env) => {
33394
33443
  if (env.imageProvider) {
33395
33444
  const sheetId = env.model.getters.getActiveSheetId();
33396
- const figureId = env.model.uuidGenerator.uuidv4();
33445
+ const figureId = env.model.uuidGenerator.smallUuid();
33397
33446
  const image = await requestImage(env);
33398
33447
  if (!image) {
33399
33448
  throw new Error("No image provider was given to the environment");
@@ -33946,7 +33995,7 @@ const insertCheckbox = {
33946
33995
  ranges,
33947
33996
  sheetId,
33948
33997
  rule: {
33949
- id: env.model.uuidGenerator.uuidv4(),
33998
+ id: env.model.uuidGenerator.smallUuid(),
33950
33999
  criterion: {
33951
34000
  type: "isBoolean",
33952
34001
  values: [],
@@ -33962,7 +34011,7 @@ const insertDropdown = {
33962
34011
  const zones = env.model.getters.getSelectedZones();
33963
34012
  const sheetId = env.model.getters.getActiveSheetId();
33964
34013
  const ranges = zones.map((zone) => env.model.getters.getRangeDataFromZone(sheetId, zone));
33965
- const ruleID = env.model.uuidGenerator.uuidv4();
34014
+ const ruleID = env.model.uuidGenerator.smallUuid();
33966
34015
  env.model.dispatch("ADD_DATA_VALIDATION_RULE", {
33967
34016
  ranges,
33968
34017
  sheetId,
@@ -33993,7 +34042,7 @@ const insertSheet = {
33993
34042
  execute: (env) => {
33994
34043
  const activeSheetId = env.model.getters.getActiveSheetId();
33995
34044
  const position = env.model.getters.getSheetIds().indexOf(activeSheetId) + 1;
33996
- const sheetId = env.model.uuidGenerator.uuidv4();
34045
+ const sheetId = env.model.uuidGenerator.smallUuid();
33997
34046
  env.model.dispatch("CREATE_SHEET", { sheetId, position });
33998
34047
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
33999
34048
  },
@@ -37988,7 +38037,7 @@ class LineConfigPanel extends GenericChartConfigPanel {
37988
38037
  get canTreatLabelsAsText() {
37989
38038
  const chart = this.env.model.getters.getChart(this.props.figureId);
37990
38039
  if (chart && chart instanceof LineChart) {
37991
- return canChartParseLabels(chart.labelRange, this.env.model.getters);
38040
+ return canChartParseLabels(chart, this.env.model.getters);
37992
38041
  }
37993
38042
  return false;
37994
38043
  }
@@ -38057,7 +38106,7 @@ class ScatterConfigPanel extends GenericChartConfigPanel {
38057
38106
  get canTreatLabelsAsText() {
38058
38107
  const chart = this.env.model.getters.getChart(this.props.figureId);
38059
38108
  if (chart && chart instanceof ScatterChart) {
38060
- return canChartParseLabels(chart.labelRange, this.env.model.getters);
38109
+ return canChartParseLabels(chart, this.env.model.getters);
38061
38110
  }
38062
38111
  return false;
38063
38112
  }
@@ -39872,7 +39921,7 @@ class ConditionalFormattingEditor extends owl.Component {
39872
39921
  state;
39873
39922
  setup() {
39874
39923
  const cf = this.props.editedCf || {
39875
- id: this.env.model.uuidGenerator.uuidv4(),
39924
+ id: this.env.model.uuidGenerator.smallUuid(),
39876
39925
  ranges: this.env.model.getters
39877
39926
  .getSelectedZones()
39878
39927
  .map((zone) => this.env.model.getters.zoneToXC(this.env.model.getters.getActiveSheetId(), zone)),
@@ -41468,7 +41517,7 @@ class DataValidationEditor extends owl.Component {
41468
41517
  .getSelectedZones()
41469
41518
  .map((zone) => zoneToXc(this.env.model.getters.getUnboundedZone(sheetId, zone)));
41470
41519
  return {
41471
- id: this.env.model.uuidGenerator.uuidv4(),
41520
+ id: this.env.model.uuidGenerator.smallUuid(),
41472
41521
  criterion: { type: "textContains", values: [""] },
41473
41522
  ranges,
41474
41523
  };
@@ -42987,8 +43036,8 @@ class PivotTitleSection extends owl.Component {
42987
43036
  return this.env.model.getters.getPivotDisplayName(this.props.pivotId);
42988
43037
  }
42989
43038
  duplicatePivot() {
42990
- const newPivotId = this.env.model.uuidGenerator.uuidv4();
42991
- const newSheetId = this.env.model.uuidGenerator.uuidv4();
43039
+ const newPivotId = this.env.model.uuidGenerator.smallUuid();
43040
+ const newSheetId = this.env.model.uuidGenerator.smallUuid();
42992
43041
  const result = this.env.model.dispatch("DUPLICATE_PIVOT_IN_NEW_SHEET", {
42993
43042
  pivotId: this.props.pivotId,
42994
43043
  newPivotId,
@@ -45553,7 +45602,7 @@ class TableStyleEditorPanel extends owl.Component {
45553
45602
  this.state.selectedTemplateName = templateName;
45554
45603
  }
45555
45604
  onConfirm() {
45556
- const tableStyleId = this.props.styleId || this.env.model.uuidGenerator.uuidv4();
45605
+ const tableStyleId = this.props.styleId || this.env.model.uuidGenerator.smallUuid();
45557
45606
  this.env.model.dispatch("CREATE_TABLE_STYLE", {
45558
45607
  tableStyleId,
45559
45608
  tableStyleName: this.state.styleName,
@@ -51149,6 +51198,10 @@ class CellPlugin extends CorePlugin {
51149
51198
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
51150
51199
  case "CLEAR_CELL":
51151
51200
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
51201
+ case "UPDATE_CELL_POSITION":
51202
+ return !cmd.cellId || this.cells[cmd.sheetId]?.[cmd.cellId]
51203
+ ? "Success" /* CommandResult.Success */
51204
+ : "InvalidCellId" /* CommandResult.InvalidCellId */;
51152
51205
  default:
51153
51206
  return "Success" /* CommandResult.Success */;
51154
51207
  }
@@ -51193,6 +51246,9 @@ class CellPlugin extends CorePlugin {
51193
51246
  case "DELETE_CONTENT":
51194
51247
  this.clearZones(cmd.sheetId, cmd.target);
51195
51248
  break;
51249
+ case "DELETE_SHEET": {
51250
+ this.history.update("cells", cmd.sheetId, undefined);
51251
+ }
51196
51252
  }
51197
51253
  }
51198
51254
  clearZones(sheetId, zones) {
@@ -52414,8 +52470,14 @@ class DataValidationPlugin extends CorePlugin {
52414
52470
  allowDispatch(cmd) {
52415
52471
  switch (cmd.type) {
52416
52472
  case "ADD_DATA_VALIDATION_RULE":
52473
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
52474
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
52475
+ }
52417
52476
  return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
52418
52477
  case "REMOVE_DATA_VALIDATION_RULE":
52478
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
52479
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
52480
+ }
52419
52481
  if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
52420
52482
  return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
52421
52483
  }
@@ -52635,6 +52697,7 @@ class DataValidationPlugin extends CorePlugin {
52635
52697
  class FigurePlugin extends CorePlugin {
52636
52698
  static getters = ["getFigures", "getFigure", "getFigureSheetId"];
52637
52699
  figures = {};
52700
+ insertionOrders = []; // TODO use a list in master
52638
52701
  // ---------------------------------------------------------------------------
52639
52702
  // Command Handling
52640
52703
  // ---------------------------------------------------------------------------
@@ -52737,11 +52800,14 @@ class FigurePlugin extends CorePlugin {
52737
52800
  }
52738
52801
  addFigure(figure, sheetId) {
52739
52802
  this.history.update("figures", sheetId, figure.id, figure);
52803
+ this.history.update("insertionOrders", this.insertionOrders.length, figure.id);
52740
52804
  }
52741
52805
  deleteSheet(sheetId) {
52806
+ this.history.update("insertionOrders", this.insertionOrders.filter((id) => !this.figures[sheetId]?.[id]));
52742
52807
  this.history.update("figures", sheetId, undefined);
52743
52808
  }
52744
52809
  removeFigure(id, sheetId) {
52810
+ this.history.update("insertionOrders", this.insertionOrders.filter((figureId) => figureId !== id));
52745
52811
  this.history.update("figures", sheetId, id, undefined);
52746
52812
  }
52747
52813
  checkFigureExists(sheetId, figureId) {
@@ -52760,7 +52826,14 @@ class FigurePlugin extends CorePlugin {
52760
52826
  // Getters
52761
52827
  // ---------------------------------------------------------------------------
52762
52828
  getFigures(sheetId) {
52763
- return Object.values(this.figures[sheetId] || {}).filter(isDefined);
52829
+ const figures = [];
52830
+ for (const figureId of this.insertionOrders) {
52831
+ const figure = this.figures[sheetId]?.[figureId];
52832
+ if (figure) {
52833
+ figures.push(figure);
52834
+ }
52835
+ }
52836
+ return figures;
52764
52837
  }
52765
52838
  getFigure(sheetId, figureId) {
52766
52839
  return this.figures[sheetId]?.[figureId];
@@ -52773,11 +52846,9 @@ class FigurePlugin extends CorePlugin {
52773
52846
  // ---------------------------------------------------------------------------
52774
52847
  import(data) {
52775
52848
  for (let sheet of data.sheets) {
52776
- const figures = {};
52777
- sheet.figures.forEach((figure) => {
52778
- figures[figure.id] = figure;
52779
- });
52780
- this.figures[sheet.id] = figures;
52849
+ for (const figure of sheet.figures) {
52850
+ this.addFigure(figure, sheet.id);
52851
+ }
52781
52852
  }
52782
52853
  }
52783
52854
  export(data) {
@@ -54203,6 +54274,9 @@ class SheetPlugin extends CorePlugin {
54203
54274
  case "CREATE_SHEET": {
54204
54275
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
54205
54276
  }
54277
+ case "DUPLICATE_SHEET": {
54278
+ return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
54279
+ }
54206
54280
  case "MOVE_SHEET":
54207
54281
  try {
54208
54282
  const currentIndex = this.orderedSheetIds.findIndex((id) => id === cmd.sheetId);
@@ -54219,7 +54293,7 @@ class SheetPlugin extends CorePlugin {
54219
54293
  ? "Success" /* CommandResult.Success */
54220
54294
  : "InvalidColor" /* CommandResult.InvalidColor */;
54221
54295
  case "DELETE_SHEET":
54222
- return this.orderedSheetIds.length > 1
54296
+ return this.getVisibleSheetIds().length > 1
54223
54297
  ? "Success" /* CommandResult.Success */
54224
54298
  : "NotEnoughSheets" /* CommandResult.NotEnoughSheets */;
54225
54299
  case "ADD_COLUMNS_ROWS":
@@ -55014,6 +55088,10 @@ class SheetPlugin extends CorePlugin {
55014
55088
  checkZonesAreInSheet(cmd) {
55015
55089
  if (!("sheetId" in cmd))
55016
55090
  return "Success" /* CommandResult.Success */;
55091
+ if ("ranges" in cmd &&
55092
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
55093
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
55094
+ }
55017
55095
  return this.checkZonesExistInSheet(cmd.sheetId, this.getCommandZones(cmd));
55018
55096
  }
55019
55097
  }
@@ -55021,6 +55099,7 @@ class SheetPlugin extends CorePlugin {
55021
55099
  class TablePlugin extends CorePlugin {
55022
55100
  static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
55023
55101
  tables = {};
55102
+ insertionOrders = {};
55024
55103
  adaptRanges(applyChange, sheetId) {
55025
55104
  const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
55026
55105
  for (const sheetId of sheetIds) {
@@ -55032,6 +55111,9 @@ class TablePlugin extends CorePlugin {
55032
55111
  allowDispatch(cmd) {
55033
55112
  switch (cmd.type) {
55034
55113
  case "CREATE_TABLE":
55114
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId) || rangeData._sheetId !== cmd.sheetId)) {
55115
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
55116
+ }
55035
55117
  const zones = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData).zone);
55036
55118
  if (!areZonesContinuous(zones)) {
55037
55119
  return "NonContinuousTargets" /* CommandResult.NonContinuousTargets */;
@@ -55062,11 +55144,13 @@ class TablePlugin extends CorePlugin {
55062
55144
  switch (cmd.type) {
55063
55145
  case "CREATE_SHEET":
55064
55146
  this.history.update("tables", cmd.sheetId, {});
55147
+ this.history.update("insertionOrders", cmd.sheetId, []);
55065
55148
  break;
55066
55149
  case "DELETE_SHEET": {
55067
55150
  const tables = { ...this.tables };
55068
55151
  delete tables[cmd.sheetId];
55069
55152
  this.history.update("tables", tables);
55153
+ this.history.update("insertionOrders", cmd.sheetId, undefined);
55070
55154
  break;
55071
55155
  }
55072
55156
  case "DUPLICATE_SHEET": {
@@ -55078,6 +55162,9 @@ class TablePlugin extends CorePlugin {
55078
55162
  : this.copyStaticTableForSheet(cmd.sheetIdTo, table);
55079
55163
  }
55080
55164
  this.history.update("tables", cmd.sheetIdTo, newTables);
55165
+ this.history.update("insertionOrders", cmd.sheetIdTo, [
55166
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
55167
+ ]);
55081
55168
  break;
55082
55169
  }
55083
55170
  case "CREATE_TABLE": {
@@ -55085,12 +55172,16 @@ class TablePlugin extends CorePlugin {
55085
55172
  const union = this.getters.getRangesUnion(ranges);
55086
55173
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
55087
55174
  this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
55088
- const id = this.uuidGenerator.uuidv4();
55175
+ const id = this.uuidGenerator.smallUuid();
55089
55176
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
55090
55177
  const newTable = cmd.tableType === "dynamic"
55091
55178
  ? this.createDynamicTable(id, union, config)
55092
55179
  : this.createStaticTable(id, cmd.tableType, union, config);
55093
55180
  this.history.update("tables", cmd.sheetId, newTable.id, newTable);
55181
+ this.history.update("insertionOrders", cmd.sheetId, [
55182
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
55183
+ newTable.id,
55184
+ ]);
55094
55185
  break;
55095
55186
  }
55096
55187
  case "REMOVE_TABLE": {
@@ -55101,6 +55192,7 @@ class TablePlugin extends CorePlugin {
55101
55192
  }
55102
55193
  }
55103
55194
  this.history.update("tables", cmd.sheetId, tables);
55195
+ this.history.update("insertionOrders", cmd.sheetId, this.insertionOrders[cmd.sheetId]?.filter((id) => id in tables));
55104
55196
  break;
55105
55197
  }
55106
55198
  case "UPDATE_TABLE": {
@@ -55136,7 +55228,14 @@ class TablePlugin extends CorePlugin {
55136
55228
  }
55137
55229
  }
55138
55230
  getCoreTables(sheetId) {
55139
- return this.tables[sheetId] ? Object.values(this.tables[sheetId]).filter(isDefined) : [];
55231
+ const tables = [];
55232
+ for (const tableId of this.insertionOrders[sheetId] || []) {
55233
+ const table = this.tables[sheetId][tableId];
55234
+ if (table) {
55235
+ tables.push(table);
55236
+ }
55237
+ }
55238
+ return tables;
55140
55239
  }
55141
55240
  getCoreTable({ sheetId, col, row }) {
55142
55241
  return this.getCoreTables(sheetId).find((table) => isInside(col, row, table.range.zone));
@@ -55238,7 +55337,7 @@ class TablePlugin extends CorePlugin {
55238
55337
  filters = [];
55239
55338
  for (const i of range(zone.left, zone.right + 1)) {
55240
55339
  const filterZone = { ...zone, left: i, right: i };
55241
- const uid = this.uuidGenerator.uuidv4();
55340
+ const uid = this.uuidGenerator.smallUuid();
55242
55341
  filters.push(this.createFilterFromZone(uid, tableRange.sheetId, filterZone, config));
55243
55342
  }
55244
55343
  }
@@ -55303,7 +55402,7 @@ class TablePlugin extends CorePlugin {
55303
55402
  ? table.filters.find((f) => f.col === i)
55304
55403
  : undefined;
55305
55404
  const filterZone = { ...tableZone, left: i, right: i };
55306
- const filterId = oldFilter?.id || this.uuidGenerator.uuidv4();
55405
+ const filterId = oldFilter?.id || this.uuidGenerator.smallUuid();
55307
55406
  filters.push(this.createFilterFromZone(filterId, tableRange.sheetId, filterZone, config));
55308
55407
  }
55309
55408
  }
@@ -55404,7 +55503,7 @@ class TablePlugin extends CorePlugin {
55404
55503
  if (filters.length < zoneToDimension(tableZone).numberOfCols) {
55405
55504
  for (let col = tableZone.left; col <= tableZone.right; col++) {
55406
55505
  if (!filters.find((filter) => filter.col === col)) {
55407
- const uid = this.uuidGenerator.uuidv4();
55506
+ const uid = this.uuidGenerator.smallUuid();
55408
55507
  const filterZone = { ...tableZone, left: col, right: col };
55409
55508
  filters.push(this.createFilterFromZone(uid, sheetId, filterZone, table.config));
55410
55509
  }
@@ -55419,6 +55518,7 @@ class TablePlugin extends CorePlugin {
55419
55518
  // ---------------------------------------------------------------------------
55420
55519
  import(data) {
55421
55520
  for (const sheet of data.sheets) {
55521
+ const tableIds = [];
55422
55522
  for (const tableData of sheet.tables || []) {
55423
55523
  const uuid = this.uuidGenerator.uuidv4();
55424
55524
  const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
@@ -55428,7 +55528,9 @@ class TablePlugin extends CorePlugin {
55428
55528
  ? this.createDynamicTable(uuid, range, tableConfig)
55429
55529
  : this.createStaticTable(uuid, tableType, range, tableConfig);
55430
55530
  this.history.update("tables", sheet.id, table.id, table);
55531
+ tableIds.push(table.id);
55431
55532
  }
55533
+ this.history.update("insertionOrders", sheet.id, tableIds);
55432
55534
  }
55433
55535
  }
55434
55536
  export(data) {
@@ -55468,7 +55570,10 @@ class HeaderGroupingPlugin extends CorePlugin {
55468
55570
  allowDispatch(cmd) {
55469
55571
  switch (cmd.type) {
55470
55572
  case "GROUP_HEADERS": {
55471
- const { start, end } = cmd;
55573
+ const { start, end, sheetId } = cmd;
55574
+ if (!this.getters.tryGetSheet(sheetId)) {
55575
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
55576
+ }
55472
55577
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
55473
55578
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
55474
55579
  }
@@ -55481,7 +55586,10 @@ class HeaderGroupingPlugin extends CorePlugin {
55481
55586
  break;
55482
55587
  }
55483
55588
  case "UNGROUP_HEADERS": {
55484
- const { start, end } = cmd;
55589
+ const { start, end, sheetId } = cmd;
55590
+ if (!this.getters.tryGetSheet(sheetId)) {
55591
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
55592
+ }
55485
55593
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
55486
55594
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
55487
55595
  }
@@ -55492,6 +55600,9 @@ class HeaderGroupingPlugin extends CorePlugin {
55492
55600
  }
55493
55601
  case "UNFOLD_HEADER_GROUP":
55494
55602
  case "FOLD_HEADER_GROUP":
55603
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
55604
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
55605
+ }
55495
55606
  const group = this.findGroupWithStartEnd(cmd.sheetId, cmd.dimension, cmd.start, cmd.end);
55496
55607
  if (!group) {
55497
55608
  return "UnknownHeaderGroup" /* CommandResult.UnknownHeaderGroup */;
@@ -55892,6 +56003,9 @@ class PivotCorePlugin extends CorePlugin {
55892
56003
  return this.checkDuplicatedMeasureIds(cmd.pivot);
55893
56004
  }
55894
56005
  case "UPDATE_PIVOT": {
56006
+ if (!(cmd.pivotId in this.pivots)) {
56007
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
56008
+ }
55895
56009
  if (deepEquals(cmd.pivot, this.pivots[cmd.pivotId]?.definition)) {
55896
56010
  return "NoChanges" /* CommandResult.NoChanges */;
55897
56011
  }
@@ -55908,6 +56022,8 @@ class PivotCorePlugin extends CorePlugin {
55908
56022
  return "EmptyName" /* CommandResult.EmptyName */;
55909
56023
  }
55910
56024
  break;
56025
+ case "REMOVE_PIVOT":
56026
+ case "DUPLICATE_PIVOT":
55911
56027
  case "INSERT_PIVOT": {
55912
56028
  if (!(cmd.pivotId in this.pivots)) {
55913
56029
  return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
@@ -55957,7 +56073,7 @@ class PivotCorePlugin extends CorePlugin {
55957
56073
  break;
55958
56074
  }
55959
56075
  case "UPDATE_PIVOT": {
55960
- this.history.update("pivots", cmd.pivotId, "definition", cmd.pivot);
56076
+ this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
55961
56077
  this.compileCalculatedMeasures(cmd.pivot.measures);
55962
56078
  break;
55963
56079
  }
@@ -56028,7 +56144,7 @@ class PivotCorePlugin extends CorePlugin {
56028
56144
  // Private
56029
56145
  // -------------------------------------------------------------------------
56030
56146
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
56031
- this.history.update("pivots", pivotId, { definition: pivot, formulaId });
56147
+ this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
56032
56148
  this.compileCalculatedMeasures(pivot.measures);
56033
56149
  this.history.update("formulaIds", formulaId, pivotId);
56034
56150
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
@@ -60960,6 +61076,9 @@ function updateChartRangesTransformation(toTransform, executed) {
60960
61076
  };
60961
61077
  }
60962
61078
  function createSheetTransformation(toTransform, executed) {
61079
+ if (toTransform.sheetId === executed.sheetId) {
61080
+ toTransform = { ...toTransform, sheetId: `${toTransform.sheetId}~` };
61081
+ }
60963
61082
  if (toTransform.name === executed.name) {
60964
61083
  return {
60965
61084
  ...toTransform,
@@ -61612,21 +61731,14 @@ class Session extends EventBus {
61612
61731
  if (!message)
61613
61732
  return;
61614
61733
  if (message.type === "REMOTE_REVISION") {
61615
- const revision = this.revisions.get(message.nextRevisionId);
61734
+ let revision = this.revisions.get(message.nextRevisionId);
61616
61735
  if (revision.commands.length === 0) {
61617
61736
  /**
61618
- * The command is empty, we have to drop all the next local revisions
61737
+ * The command is empty, we have to rebase all the next local revisions
61619
61738
  * to avoid issues with undo/redo
61620
61739
  */
61621
- this.revisions.drop(revision.id);
61622
- const revisionIds = this.pendingMessages
61623
- .filter((message) => message.type === "REMOTE_REVISION")
61624
- .map((message) => message.nextRevisionId);
61625
- this.trigger("pending-revisions-dropped", { revisionIds });
61626
- this.waitingAck = false;
61627
- this.waitingUndoRedoAck = false;
61628
- this.pendingMessages = [];
61629
- return;
61740
+ this.revisions.rebase(revision.id);
61741
+ revision = this.revisions.get(message.nextRevisionId);
61630
61742
  }
61631
61743
  message = {
61632
61744
  ...message,
@@ -61650,7 +61762,6 @@ class Session extends EventBus {
61650
61762
  switch (message.type) {
61651
61763
  case "REMOTE_REVISION":
61652
61764
  case "REVISION_REDONE":
61653
- case "REVISION_UNDONE":
61654
61765
  case "SNAPSHOT_CREATED":
61655
61766
  this.waitingAck = false;
61656
61767
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
@@ -61659,6 +61770,25 @@ class Session extends EventBus {
61659
61770
  this.lastRevisionMessage = message;
61660
61771
  this.sendPendingMessage();
61661
61772
  break;
61773
+ case "REVISION_UNDONE": {
61774
+ this.waitingAck = false;
61775
+ this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
61776
+ const firstPendingRevisionId = this.pendingMessages.findIndex((message) => message.type === "REMOTE_REVISION");
61777
+ if (firstPendingRevisionId !== -1) {
61778
+ /**
61779
+ * Some revisions undergo transformations that may cause issues with
61780
+ * undo/redo if the transformation is destructive (we don't get back
61781
+ * the original command by transforming it with the inverse).
61782
+ * To prevent these problems, we must rebase all subsequent local
61783
+ * revisions.
61784
+ */
61785
+ this.revisions.rebase(this.pendingMessages[firstPendingRevisionId].nextRevisionId);
61786
+ }
61787
+ this.serverRevisionId = message.nextRevisionId;
61788
+ this.processedRevisions.add(message.nextRevisionId);
61789
+ this.sendPendingMessage();
61790
+ break;
61791
+ }
61662
61792
  }
61663
61793
  }
61664
61794
  isAlreadyProcessed(message) {
@@ -62741,6 +62871,10 @@ class SheetUIPlugin extends UIPlugin {
62741
62871
  */
62742
62872
  checkZonesAreInSheet(cmd) {
62743
62873
  const sheetId = "sheetId" in cmd ? cmd.sheetId : this.getters.tryGetActiveSheetId();
62874
+ if ("ranges" in cmd &&
62875
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
62876
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
62877
+ }
62744
62878
  const zones = this.getters.getCommandZones(cmd);
62745
62879
  if (!sheetId && zones.length > 0) {
62746
62880
  return "NoActiveSheet" /* CommandResult.NoActiveSheet */;
@@ -63090,23 +63224,23 @@ const uuidGenerator = new UuidGenerator();
63090
63224
  function repeatCreateChartCommand(getters, cmd) {
63091
63225
  return {
63092
63226
  ...repeatSheetDependantCommand(getters, cmd),
63093
- id: uuidGenerator.uuidv4(),
63227
+ id: uuidGenerator.smallUuid(),
63094
63228
  };
63095
63229
  }
63096
63230
  function repeatCreateImageCommand(getters, cmd) {
63097
63231
  return {
63098
63232
  ...repeatSheetDependantCommand(getters, cmd),
63099
- figureId: uuidGenerator.uuidv4(),
63233
+ figureId: uuidGenerator.smallUuid(),
63100
63234
  };
63101
63235
  }
63102
63236
  function repeatCreateFigureCommand(getters, cmd) {
63103
63237
  const newCmd = repeatSheetDependantCommand(getters, cmd);
63104
- newCmd.figure.id = uuidGenerator.uuidv4();
63238
+ newCmd.figure.id = uuidGenerator.smallUuid();
63105
63239
  return newCmd;
63106
63240
  }
63107
63241
  function repeatCreateSheetCommand(getters, cmd) {
63108
63242
  const newCmd = deepCopy(cmd);
63109
- newCmd.sheetId = uuidGenerator.uuidv4();
63243
+ newCmd.sheetId = uuidGenerator.smallUuid();
63110
63244
  const sheetName = cmd.name || getters.getSheet(getters.getActiveSheetId()).name;
63111
63245
  // Extract the prefix of the sheet name (everything before the number at the end of the name)
63112
63246
  const namePrefix = sheetName.match(/(.+?)\d*$/)?.[1] || sheetName;
@@ -63295,7 +63429,6 @@ class HistoryPlugin extends UIPlugin {
63295
63429
  super(config);
63296
63430
  this.session = config.session;
63297
63431
  this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
63298
- this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
63299
63432
  this.session.on("snapshot", this, () => {
63300
63433
  this.undoStack = [];
63301
63434
  this.redoStack = [];
@@ -63365,10 +63498,6 @@ class HistoryPlugin extends UIPlugin {
63365
63498
  const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
63366
63499
  return canRepeatRevision(lastNonRedoRevision);
63367
63500
  }
63368
- drop(revisionIds) {
63369
- this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
63370
- this.redoStack = [];
63371
- }
63372
63501
  onNewLocalStateUpdate({ id }) {
63373
63502
  this.undoStack.push(id);
63374
63503
  this.redoStack = [];
@@ -64569,23 +64698,7 @@ class GridSelectionPlugin extends UIPlugin {
64569
64698
  gridSelection: deepCopy(gridSelection),
64570
64699
  };
64571
64700
  }
64572
- if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
64573
- const currentSheetIds = this.getters.getVisibleSheetIds();
64574
- this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
64575
- if (this.activeSheet.id in this.sheetsData) {
64576
- const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
64577
- this.selectCell(anchor.cell.col, anchor.cell.row);
64578
- }
64579
- else {
64580
- this.selectCell(0, 0);
64581
- }
64582
- const { col, row } = this.gridSelection.anchor.cell;
64583
- this.moveClient({
64584
- sheetId: this.getters.getActiveSheetId(),
64585
- col,
64586
- row,
64587
- });
64588
- }
64701
+ this.fallbackToVisibleSheet();
64589
64702
  const sheetId = this.getters.getActiveSheetId();
64590
64703
  this.gridSelection.zones = this.gridSelection.zones.map((z) => this.getters.expandZone(sheetId, z));
64591
64704
  this.gridSelection.anchor.zone = this.getters.expandZone(sheetId, this.gridSelection.anchor.zone);
@@ -64595,6 +64708,7 @@ class GridSelectionPlugin extends UIPlugin {
64595
64708
  }
64596
64709
  }
64597
64710
  finalize() {
64711
+ this.fallbackToVisibleSheet();
64598
64712
  /** Any change to the selection has to be reflected in the selection processor. */
64599
64713
  this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
64600
64714
  }
@@ -64898,6 +65012,25 @@ class GridSelectionPlugin extends UIPlugin {
64898
65012
  }
64899
65013
  return "Success" /* CommandResult.Success */;
64900
65014
  }
65015
+ fallbackToVisibleSheet() {
65016
+ if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
65017
+ const currentSheetIds = this.getters.getVisibleSheetIds();
65018
+ this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
65019
+ if (this.activeSheet.id in this.sheetsData) {
65020
+ const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
65021
+ this.selectCell(anchor.cell.col, anchor.cell.row);
65022
+ }
65023
+ else {
65024
+ this.selectCell(0, 0);
65025
+ }
65026
+ const { col, row } = this.gridSelection.anchor.cell;
65027
+ this.moveClient({
65028
+ sheetId: this.getters.getActiveSheetId(),
65029
+ col,
65030
+ row,
65031
+ });
65032
+ }
65033
+ }
64901
65034
  //-------------------------------------------
64902
65035
  // Helpers for extensions
64903
65036
  // ------------------------------------------
@@ -66064,7 +66197,9 @@ class HeaderPositionsUIPlugin extends UIPlugin {
66064
66197
  case "UNGROUP_HEADERS":
66065
66198
  case "GROUP_HEADERS":
66066
66199
  case "CREATE_SHEET":
66067
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
66200
+ if (this.getters.tryGetSheet(cmd.sheetId)) {
66201
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
66202
+ }
66068
66203
  break;
66069
66204
  case "DUPLICATE_SHEET":
66070
66205
  this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
@@ -66072,12 +66207,14 @@ class HeaderPositionsUIPlugin extends UIPlugin {
66072
66207
  }
66073
66208
  }
66074
66209
  finalize() {
66075
- if (this.isDirty) {
66076
- for (const sheetId of this.getters.getSheetIds()) {
66210
+ for (const sheetId of this.getters.getSheetIds()) {
66211
+ // sheets can be created without this plugin being aware of it
66212
+ // in concurrent situations.
66213
+ if (this.isDirty || !this.headerPositions[sheetId]) {
66077
66214
  this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
66078
66215
  }
66079
- this.isDirty = false;
66080
66216
  }
66217
+ this.isDirty = false;
66081
66218
  }
66082
66219
  /**
66083
66220
  * Returns the size, start and end coordinates of a column on an unfolded sheet
@@ -66900,7 +67037,7 @@ class BottomBar extends owl.Component {
66900
67037
  clickAddSheet(ev) {
66901
67038
  const activeSheetId = this.env.model.getters.getActiveSheetId();
66902
67039
  const position = this.env.model.getters.getSheetIds().findIndex((sheetId) => sheetId === activeSheetId) + 1;
66903
- const sheetId = this.env.model.uuidGenerator.uuidv4();
67040
+ const sheetId = this.env.model.uuidGenerator.smallUuid();
66904
67041
  const name = this.env.model.getters.getNextSheetName(_t("Sheet"));
66905
67042
  this.env.model.dispatch("CREATE_SHEET", { sheetId, position, name });
66906
67043
  this.env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
@@ -67988,7 +68125,7 @@ css /* scss */ `
67988
68125
  .o-font-size-editor {
67989
68126
  height: calc(100% - 4px);
67990
68127
  input.o-font-size {
67991
- outline-color: ${SELECTION_BORDER_COLOR};
68128
+ outline: none;
67992
68129
  height: 20px;
67993
68130
  width: 23px;
67994
68131
  }
@@ -69275,7 +69412,7 @@ class Tree {
69275
69412
  }
69276
69413
  /**
69277
69414
  * Drop the operation and all following operations in every
69278
- * branch
69415
+ * branches
69279
69416
  */
69280
69417
  drop(operationId) {
69281
69418
  for (const branch of this.branches) {
@@ -69580,9 +69717,16 @@ class SelectiveHistory {
69580
69717
  this.fastForward();
69581
69718
  this.insert(redoId, this.buildEmpty(redoId), insertAfter);
69582
69719
  }
69583
- drop(operationId) {
69720
+ rebase(operationId) {
69721
+ const operation = this.get(operationId);
69722
+ const execution = [...this.tree.execution(this.HEAD_BRANCH).startAfter(operationId)];
69584
69723
  this.revertBefore(operationId);
69724
+ const baseId = this.HEAD_OPERATION.id;
69585
69725
  this.tree.drop(operationId);
69726
+ this.insert(operationId, operation, baseId);
69727
+ for (const { operation } of execution) {
69728
+ this.insert(operation.id, operation.data, this.HEAD_OPERATION.id);
69729
+ }
69586
69730
  }
69587
69731
  /**
69588
69732
  * Revert the state as it was *before* the given operation was executed.
@@ -72624,6 +72768,11 @@ class Model extends EventBus {
72624
72768
  dispatch: (command) => {
72625
72769
  const result = this.checkDispatchAllowed(command);
72626
72770
  if (!result.isSuccessful) {
72771
+ // core views plugins need to be invalidated
72772
+ this.dispatchToHandlers(this.coreHandlers, {
72773
+ type: "UNDO",
72774
+ commands: [command],
72775
+ });
72627
72776
  return;
72628
72777
  }
72629
72778
  this.isReplayingCommand = true;
@@ -72652,7 +72801,7 @@ class Model extends EventBus {
72652
72801
  }
72653
72802
  setupConfig(config) {
72654
72803
  const client = config.client || {
72655
- id: this.uuidGenerator.uuidv4(),
72804
+ id: this.uuidGenerator.smallUuid(),
72656
72805
  name: _t("Anonymous").toString(),
72657
72806
  };
72658
72807
  const transportService = config.transportService || new LocalTransportService();
@@ -73182,6 +73331,6 @@ exports.tokenColors = tokenColors;
73182
73331
  exports.tokenize = tokenize;
73183
73332
 
73184
73333
 
73185
- __info__.version = "18.0.15";
73186
- __info__.date = "2025-02-10T08:59:22.993Z";
73187
- __info__.hash = "5b19f88";
73334
+ __info__.version = "18.0.17";
73335
+ __info__.date = "2025-02-25T05:58:39.632Z";
73336
+ __info__.hash = "2ee4347";