@odoo/o-spreadsheet 18.1.8 → 18.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.1.8
6
- * @date 2025-02-14T08:42:08.322Z
7
- * @hash 02682f4
5
+ * @version 18.1.9
6
+ * @date 2025-02-25T05:59:45.472Z
7
+ * @hash 6789c1c
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -2218,17 +2218,7 @@ function toZoneWithoutBoundaryChanges(xc) {
2218
2218
  */
2219
2219
  function toUnboundedZone(xc) {
2220
2220
  const zone = toZoneWithoutBoundaryChanges(xc);
2221
- if (zone.right !== undefined && zone.right < zone.left) {
2222
- const tmp = zone.left;
2223
- zone.left = zone.right;
2224
- zone.right = tmp;
2225
- }
2226
- if (zone.bottom !== undefined && zone.bottom < zone.top) {
2227
- const tmp = zone.top;
2228
- zone.top = zone.bottom;
2229
- zone.bottom = tmp;
2230
- }
2231
- return zone;
2221
+ return reorderZone(zone);
2232
2222
  }
2233
2223
  /**
2234
2224
  * Convert from a cartesian reference to a Zone.
@@ -2502,11 +2492,11 @@ function positions(zone) {
2502
2492
  return positions;
2503
2493
  }
2504
2494
  function reorderZone(zone) {
2505
- if (zone.left > zone.right) {
2506
- zone = { left: zone.right, right: zone.left, top: zone.top, bottom: zone.bottom };
2495
+ if (zone.right !== undefined && zone.left > zone.right) {
2496
+ zone = { ...zone, left: zone.right, right: zone.left };
2507
2497
  }
2508
- if (zone.top > zone.bottom) {
2509
- zone = { left: zone.left, right: zone.right, top: zone.bottom, bottom: zone.top };
2498
+ if (zone.bottom !== undefined && zone.top > zone.bottom) {
2499
+ zone = { ...zone, top: zone.bottom, bottom: zone.top };
2510
2500
  }
2511
2501
  return zone;
2512
2502
  }
@@ -3411,12 +3401,12 @@ function isTargetDependent(cmd) {
3411
3401
  function isRangeDependant(cmd) {
3412
3402
  return "ranges" in cmd;
3413
3403
  }
3414
- function isZoneDependent(cmd) {
3415
- return "zone" in cmd;
3416
- }
3417
3404
  function isPositionDependent(cmd) {
3418
3405
  return "col" in cmd && "row" in cmd && "sheetId" in cmd;
3419
3406
  }
3407
+ function isZoneDependent(cmd) {
3408
+ return "sheetId" in cmd && "zone" in cmd;
3409
+ }
3420
3410
  const invalidateEvaluationCommands = new Set([
3421
3411
  "RENAME_SHEET",
3422
3412
  "DELETE_SHEET",
@@ -3428,6 +3418,7 @@ const invalidateEvaluationCommands = new Set([
3428
3418
  "REDO",
3429
3419
  "ADD_MERGE",
3430
3420
  "REMOVE_MERGE",
3421
+ "DUPLICATE_SHEET",
3431
3422
  "UPDATE_LOCALE",
3432
3423
  "ADD_PIVOT",
3433
3424
  "UPDATE_PIVOT",
@@ -3457,7 +3448,6 @@ const invalidateChartEvaluationCommands = new Set([
3457
3448
  ]);
3458
3449
  const invalidateDependenciesCommands = new Set(["MOVE_RANGES"]);
3459
3450
  const invalidateCFEvaluationCommands = new Set([
3460
- "DUPLICATE_SHEET",
3461
3451
  "EVALUATE_CELLS",
3462
3452
  "ADD_CONDITIONAL_FORMAT",
3463
3453
  "REMOVE_CONDITIONAL_FORMAT",
@@ -3627,6 +3617,7 @@ var CommandResult;
3627
3617
  CommandResult["InvalidRange"] = "InvalidRange";
3628
3618
  CommandResult["InvalidZones"] = "InvalidZones";
3629
3619
  CommandResult["InvalidSheetId"] = "InvalidSheetId";
3620
+ CommandResult["InvalidCellId"] = "InvalidCellId";
3630
3621
  CommandResult["InvalidFigureId"] = "InvalidFigureId";
3631
3622
  CommandResult["InputAlreadyFocused"] = "InputAlreadyFocused";
3632
3623
  CommandResult["MaximumRangesReached"] = "MaximumRangesReached";
@@ -4458,7 +4449,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4458
4449
  * @param reverseSearch if true, search in the array starting from the end.
4459
4450
 
4460
4451
  */
4461
- function linearSearch(data, target, mode, numberOfValues, getValueInData, reverseSearch = false) {
4452
+ function linearSearch(data, target, mode, numberOfValues, getValueInData, lookupCaches, reverseSearch = false) {
4462
4453
  if (target === undefined || target.value === null) {
4463
4454
  return -1;
4464
4455
  }
@@ -4467,17 +4458,48 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4467
4458
  }
4468
4459
  const _target = normalizeValue(target.value);
4469
4460
  const getValue = reverseSearch
4470
- ? (data, i) => getValueInData(data, numberOfValues - i - 1)
4471
- : getValueInData;
4461
+ ? (data, i) => normalizeValue(getValueInData(data, numberOfValues - i - 1))
4462
+ : (data, i) => normalizeValue(getValueInData(data, i));
4463
+ // first check if the target is in the cache
4464
+ const isNotWildcardTarget = mode !== "wildcard" ||
4465
+ typeof _target !== "string" ||
4466
+ !(_target.includes("*") || _target.includes("?"));
4467
+ if (lookupCaches && isNotWildcardTarget) {
4468
+ const searchMode = reverseSearch ? "reverseSearch" : "forwardSearch";
4469
+ let cache = lookupCaches[searchMode].get(data);
4470
+ if (cache === undefined) {
4471
+ // build the cache for all the values
4472
+ cache = new Map();
4473
+ for (let i = 0; i < numberOfValues; i++) {
4474
+ const value = getValue(data, i) ?? null;
4475
+ if (!cache.has(value)) {
4476
+ cache.set(value, i);
4477
+ }
4478
+ }
4479
+ lookupCaches[searchMode].set(data, cache);
4480
+ }
4481
+ if (cache.has(_target)) {
4482
+ const resultIndex = cache.get(_target);
4483
+ return reverseSearch ? numberOfValues - resultIndex - 1 : resultIndex;
4484
+ }
4485
+ if (mode === "strict") {
4486
+ return -1;
4487
+ }
4488
+ }
4489
+ // else perform the linear search
4490
+ const resultIndex = _linearSearch(data, _target, mode, numberOfValues, getValue);
4491
+ return reverseSearch && resultIndex !== -1 ? numberOfValues - resultIndex - 1 : resultIndex;
4492
+ }
4493
+ function _linearSearch(data, _target, mode, numberOfValues, getNormalizeValue) {
4472
4494
  let indexMatchTarget = (i) => {
4473
- return normalizeValue(getValue(data, i)) === _target;
4495
+ return getNormalizeValue(data, i) === _target;
4474
4496
  };
4475
4497
  if (mode === "wildcard" &&
4476
4498
  typeof _target === "string" &&
4477
4499
  (_target.includes("*") || _target.includes("?"))) {
4478
4500
  const regExp = wildcardToRegExp(_target);
4479
4501
  indexMatchTarget = (i) => {
4480
- const value = normalizeValue(getValue(data, i));
4502
+ const value = getNormalizeValue(data, i);
4481
4503
  if (typeof value === "string") {
4482
4504
  return regExp.test(value);
4483
4505
  }
@@ -4488,7 +4510,7 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4488
4510
  let closestMatchIndex = -1;
4489
4511
  if (mode === "nextSmaller") {
4490
4512
  indexMatchTarget = (i) => {
4491
- const value = normalizeValue(getValue(data, i));
4513
+ const value = getNormalizeValue(data, i);
4492
4514
  if ((!closestMatch && compareCellValues(_target, value) >= 0) ||
4493
4515
  (compareCellValues(_target, value) >= 0 && compareCellValues(value, closestMatch) > 0)) {
4494
4516
  closestMatch = value;
@@ -4499,7 +4521,7 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4499
4521
  }
4500
4522
  if (mode === "nextGreater") {
4501
4523
  indexMatchTarget = (i) => {
4502
- const value = normalizeValue(getValue(data, i));
4524
+ const value = getNormalizeValue(data, i);
4503
4525
  if ((!closestMatch && compareCellValues(_target, value) <= 0) ||
4504
4526
  (compareCellValues(_target, value) <= 0 && compareCellValues(value, closestMatch) < 0)) {
4505
4527
  closestMatch = value;
@@ -4510,12 +4532,10 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4510
4532
  }
4511
4533
  for (let i = 0; i < numberOfValues; i++) {
4512
4534
  if (indexMatchTarget(i)) {
4513
- return reverseSearch ? numberOfValues - i - 1 : i;
4535
+ return i;
4514
4536
  }
4515
4537
  }
4516
- return reverseSearch && closestMatchIndex !== -1
4517
- ? numberOfValues - closestMatchIndex - 1
4518
- : closestMatchIndex;
4538
+ return closestMatchIndex;
4519
4539
  }
4520
4540
  /**
4521
4541
  * Normalize a value.
@@ -6489,10 +6509,11 @@ class UuidGenerator {
6489
6509
  *
6490
6510
  */
6491
6511
  smallUuid() {
6492
- //@ts-ignore
6493
- if (window.crypto && window.crypto.getRandomValues) {
6494
- //@ts-ignore
6495
- return ([1e7] + -1e3).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6512
+ if (window.crypto) {
6513
+ return "10000000-1000".replace(/[01]/g, (c) => {
6514
+ const n = Number(c);
6515
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6516
+ });
6496
6517
  }
6497
6518
  else {
6498
6519
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -6507,10 +6528,11 @@ class UuidGenerator {
6507
6528
  * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
6508
6529
  */
6509
6530
  uuidv4() {
6510
- //@ts-ignore
6511
- if (window.crypto && window.crypto.getRandomValues) {
6512
- //@ts-ignore
6513
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6531
+ if (window.crypto) {
6532
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
6533
+ const n = Number(c);
6534
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6535
+ });
6514
6536
  }
6515
6537
  else {
6516
6538
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -12063,6 +12085,25 @@ const LN = {
12063
12085
  isExported: true,
12064
12086
  };
12065
12087
  // -----------------------------------------------------------------------------
12088
+ // LOG
12089
+ // -----------------------------------------------------------------------------
12090
+ const LOG = {
12091
+ description: _t("The logarithm of a number, for a given base."),
12092
+ args: [
12093
+ arg("value (number)", _t("The value for which to calculate the logarithm.")),
12094
+ arg("base (number, default=10)", _t("The base of the logarithm.")),
12095
+ ],
12096
+ compute: function (value, base = { value: 10 }) {
12097
+ const _value = toNumber(value, this.locale);
12098
+ const _base = toNumber(base, this.locale);
12099
+ assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
12100
+ assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
12101
+ assert(() => _base !== 1, _t("The base must be different from 1."));
12102
+ return Math.log10(_value) / Math.log10(_base);
12103
+ },
12104
+ isExported: true,
12105
+ };
12106
+ // -----------------------------------------------------------------------------
12066
12107
  // MOD
12067
12108
  // -----------------------------------------------------------------------------
12068
12109
  function mod(dividend, divisor) {
@@ -12602,6 +12643,7 @@ var math = /*#__PURE__*/Object.freeze({
12602
12643
  ISODD: ISODD,
12603
12644
  ISO_CEILING: ISO_CEILING,
12604
12645
  LN: LN,
12646
+ LOG: LOG,
12605
12647
  MOD: MOD,
12606
12648
  MUNIT: MUNIT,
12607
12649
  ODD: ODD,
@@ -18500,7 +18542,7 @@ const HLOOKUP = {
18500
18542
  const _isSorted = toBoolean(isSorted.value);
18501
18543
  const colIndex = _isSorted
18502
18544
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18503
- : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18545
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches);
18504
18546
  const col = _range[colIndex];
18505
18547
  if (col === undefined) {
18506
18548
  return valueNotAvailable(searchKey);
@@ -18655,7 +18697,7 @@ const MATCH = {
18655
18697
  index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18656
18698
  break;
18657
18699
  case 0:
18658
- index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18700
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18659
18701
  break;
18660
18702
  case -1:
18661
18703
  index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
@@ -18723,7 +18765,7 @@ const VLOOKUP = {
18723
18765
  const _isSorted = toBoolean(isSorted.value);
18724
18766
  const rowIndex = _isSorted
18725
18767
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18726
- : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18768
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches);
18727
18769
  const value = _range[_index - 1][rowIndex];
18728
18770
  if (value === undefined) {
18729
18771
  return valueNotAvailable(searchKey);
@@ -18779,7 +18821,7 @@ const XLOOKUP = {
18779
18821
  const reverseSearch = _searchMode === -1;
18780
18822
  const index = _searchMode === 2 || _searchMode === -2
18781
18823
  ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18782
- : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18824
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18783
18825
  if (index !== -1) {
18784
18826
  return lookupDirection === "col"
18785
18827
  ? _returnRange.map((col) => [col[index]])
@@ -28267,7 +28309,7 @@ function getBarChartData(definition, dataSets, labelRange, getters) {
28267
28309
  }
28268
28310
  function getPyramidChartData(definition, dataSets, labelRange, getters) {
28269
28311
  const barChartData = getBarChartData(definition, dataSets, labelRange, getters);
28270
- const barDataset = barChartData.dataSetsValues;
28312
+ const barDataset = barChartData.dataSetsValues.filter((ds) => !ds.hidden);
28271
28313
  const pyramidDatasetValues = [];
28272
28314
  if (barDataset[0]) {
28273
28315
  const pyramidData = barDataset[0].data.map((value) => (value > 0 ? value : 0));
@@ -28744,10 +28786,8 @@ function getChartDatasetFormat(getters, allDataSets, axis) {
28744
28786
  function getChartDatasetValues(getters, dataSets) {
28745
28787
  const datasetValues = [];
28746
28788
  for (const [dsIndex, ds] of Object.entries(dataSets)) {
28747
- if (getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left)) {
28748
- continue;
28749
- }
28750
28789
  let label;
28790
+ let hidden = getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left);
28751
28791
  if (ds.labelCell) {
28752
28792
  const labelRange = ds.labelCell;
28753
28793
  const cell = labelRange
@@ -28774,9 +28814,9 @@ function getChartDatasetValues(getters, dataSets) {
28774
28814
  data.fill(1);
28775
28815
  }
28776
28816
  else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28777
- continue;
28817
+ hidden = true;
28778
28818
  }
28779
- datasetValues.push({ data, label });
28819
+ datasetValues.push({ data, label, hidden });
28780
28820
  }
28781
28821
  return datasetValues;
28782
28822
  }
@@ -28787,12 +28827,13 @@ function getBarChartDatasets(definition, args) {
28787
28827
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28788
28828
  const trendDatasets = [];
28789
28829
  for (const index in dataSetsValues) {
28790
- let { label, data } = dataSetsValues[index];
28830
+ let { label, data, hidden } = dataSetsValues[index];
28791
28831
  label = definition.dataSets?.[index].label || label;
28792
28832
  const backgroundColor = colors.next();
28793
28833
  const dataset = {
28794
28834
  label,
28795
28835
  data,
28836
+ hidden,
28796
28837
  borderColor: definition.background || BACKGROUND_CHART_COLOR,
28797
28838
  borderWidth: definition.stacked ? 1 : 0,
28798
28839
  backgroundColor,
@@ -28825,6 +28866,9 @@ function getWaterfallDatasetAndLabels(definition, args) {
28825
28866
  const labelsWithSubTotals = [];
28826
28867
  let lastValue = 0;
28827
28868
  for (const dataSetsValue of dataSetsValues) {
28869
+ if (dataSetsValue.hidden) {
28870
+ continue;
28871
+ }
28828
28872
  for (let i = 0; i < dataSetsValue.data.length; i++) {
28829
28873
  const data = dataSetsValue.data[i];
28830
28874
  labelsWithSubTotals.push(labels[i]);
@@ -28860,7 +28904,7 @@ function getLineChartDatasets(definition, args) {
28860
28904
  const trendDatasets = [];
28861
28905
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28862
28906
  for (let index = 0; index < dataSetsValues.length; index++) {
28863
- let { label, data } = dataSetsValues[index];
28907
+ let { label, data, hidden } = dataSetsValues[index];
28864
28908
  label = definition.dataSets?.[index].label || label;
28865
28909
  const color = colors.next();
28866
28910
  if (axisType && ["linear", "time"].includes(axisType)) {
@@ -28870,6 +28914,7 @@ function getLineChartDatasets(definition, args) {
28870
28914
  const dataset = {
28871
28915
  label,
28872
28916
  data,
28917
+ hidden,
28873
28918
  tension: 0, // 0 -> render straight lines, which is much faster
28874
28919
  borderColor: color,
28875
28920
  backgroundColor: areaChart ? setColorAlpha(color, LINE_FILL_TRANSPARENCY) : color,
@@ -28902,11 +28947,13 @@ function getPieChartDatasets(definition, args) {
28902
28947
  const dataSets = [];
28903
28948
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
28904
28949
  const backgroundColor = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
28905
- for (const { label, data } of dataSetsValues) {
28950
+ for (const { label, data, hidden } of dataSetsValues) {
28951
+ if (hidden)
28952
+ continue;
28906
28953
  const dataset = {
28907
28954
  label,
28908
28955
  data,
28909
- borderColor: BACKGROUND_CHART_COLOR,
28956
+ borderColor: definition.background || "#FFFFFF",
28910
28957
  backgroundColor,
28911
28958
  hoverOffset: 30,
28912
28959
  };
@@ -28920,7 +28967,7 @@ function getComboChartDatasets(definition, args) {
28920
28967
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28921
28968
  const trendDatasets = [];
28922
28969
  for (let index = 0; index < dataSetsValues.length; index++) {
28923
- let { label, data } = dataSetsValues[index];
28970
+ let { label, data, hidden } = dataSetsValues[index];
28924
28971
  label = definition.dataSets?.[index].label || label;
28925
28972
  const design = definition.dataSets?.[index];
28926
28973
  const color = colors.next();
@@ -28928,6 +28975,7 @@ function getComboChartDatasets(definition, args) {
28928
28975
  const dataset = {
28929
28976
  label: label,
28930
28977
  data,
28978
+ hidden,
28931
28979
  borderColor: color,
28932
28980
  backgroundColor: color,
28933
28981
  yAxisID: definition.dataSets?.[index].yAxisId || "y",
@@ -28952,7 +29000,7 @@ function getRadarChartDatasets(definition, args) {
28952
29000
  const fill = definition.fillArea ?? false;
28953
29001
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28954
29002
  for (let i = 0; i < dataSetsValues.length; i++) {
28955
- let { label, data } = dataSetsValues[i];
29003
+ let { label, data, hidden } = dataSetsValues[i];
28956
29004
  if (definition.dataSets?.[i]?.label) {
28957
29005
  label = definition.dataSets[i].label;
28958
29006
  }
@@ -28960,6 +29008,7 @@ function getRadarChartDatasets(definition, args) {
28960
29008
  const dataset = {
28961
29009
  label,
28962
29010
  data,
29011
+ hidden,
28963
29012
  borderColor,
28964
29013
  backgroundColor: borderColor,
28965
29014
  };
@@ -29105,6 +29154,11 @@ function getPieChartLegend(definition, args) {
29105
29154
  hidden: false,
29106
29155
  lineWidth: 2,
29107
29156
  })),
29157
+ filter: (legendItem, data) => {
29158
+ return "datasetIndex" in legendItem
29159
+ ? !data.datasets[legendItem.datasetIndex].hidden
29160
+ : true;
29161
+ },
29108
29162
  },
29109
29163
  };
29110
29164
  }
@@ -29166,6 +29220,11 @@ function getWaterfallChartLegend(definition, args) {
29166
29220
  }
29167
29221
  return legendValues;
29168
29222
  },
29223
+ filter: (legendItem, data) => {
29224
+ return "datasetIndex" in legendItem
29225
+ ? !data.datasets[legendItem.datasetIndex].hidden
29226
+ : true;
29227
+ },
29169
29228
  },
29170
29229
  onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29171
29230
  };
@@ -29249,6 +29308,11 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29249
29308
  ...legendLabelConfig,
29250
29309
  };
29251
29310
  }),
29311
+ filter: (legendItem, data) => {
29312
+ return "datasetIndex" in legendItem
29313
+ ? !data.datasets[legendItem.datasetIndex].hidden
29314
+ : true;
29315
+ },
29252
29316
  },
29253
29317
  };
29254
29318
  }
@@ -33038,6 +33102,100 @@ function* iterateChildren(el) {
33038
33102
  function getOpenedMenus() {
33039
33103
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33040
33104
  }
33105
+ function getCurrentSelection(el) {
33106
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33107
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33108
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33109
+ return {
33110
+ start: startSizeBefore,
33111
+ end: endSizeBefore,
33112
+ };
33113
+ }
33114
+ function getStartAndEndSelection(el) {
33115
+ const selection = document.getSelection();
33116
+ return {
33117
+ startElement: selection.anchorNode || el,
33118
+ startSelectionOffset: selection.anchorOffset,
33119
+ endElement: selection.focusNode || el,
33120
+ endSelectionOffset: selection.focusOffset,
33121
+ };
33122
+ }
33123
+ /**
33124
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33125
+ * The selected node is either a Text node or an Element node.
33126
+ *
33127
+ * case 1 -Text node:
33128
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33129
+ * content length of all previous nodes.
33130
+ *
33131
+ * case 2 - Element node:
33132
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33133
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33134
+ *
33135
+ * See the MDN documentation for more details.
33136
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33137
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33138
+ *
33139
+ */
33140
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33141
+ let usedCharacters = 0;
33142
+ let it = iterateChildren(el);
33143
+ let current = it.next();
33144
+ let isFirstParagraph = true;
33145
+ while (!current.done && current.value !== nodeToFind) {
33146
+ if (!current.value.hasChildNodes()) {
33147
+ if (current.value.textContent) {
33148
+ usedCharacters += current.value.textContent.length;
33149
+ }
33150
+ }
33151
+ // One new paragraph = one new line character, except for the first paragraph
33152
+ if (current.value.nodeName === "P" ||
33153
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33154
+ ) {
33155
+ if (isFirstParagraph) {
33156
+ isFirstParagraph = false;
33157
+ }
33158
+ else {
33159
+ usedCharacters++;
33160
+ }
33161
+ }
33162
+ current = it.next();
33163
+ }
33164
+ if (current.value !== nodeToFind) {
33165
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33166
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33167
+ *
33168
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33169
+ */
33170
+ return 0;
33171
+ }
33172
+ else {
33173
+ if (!current.value.hasChildNodes()) {
33174
+ usedCharacters += nodeOffset;
33175
+ }
33176
+ else {
33177
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33178
+ usedCharacters += children.reduce((acc, child, index) => {
33179
+ if (child.textContent !== null) {
33180
+ // need to account for paragraph nodes that implicitly add a new line
33181
+ // except for the last paragraph
33182
+ let chars = child.textContent.length;
33183
+ if (child.nodeName === "P" && index !== children.length - 1) {
33184
+ chars++;
33185
+ }
33186
+ return acc + chars;
33187
+ }
33188
+ else {
33189
+ return acc;
33190
+ }
33191
+ }, 0);
33192
+ }
33193
+ }
33194
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33195
+ usedCharacters++;
33196
+ }
33197
+ return usedCharacters;
33198
+ }
33041
33199
  const letterRegex = /^[a-zA-Z]$/;
33042
33200
  /**
33043
33201
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -38273,7 +38431,7 @@ css /* scss */ `
38273
38431
  .o-font-size-editor {
38274
38432
  height: calc(100% - 4px);
38275
38433
  input.o-font-size {
38276
- outline-color: ${SELECTION_BORDER_COLOR};
38434
+ outline: none;
38277
38435
  height: 20px;
38278
38436
  width: 23px;
38279
38437
  }
@@ -39886,6 +40044,10 @@ class ContentEditableHelper {
39886
40044
  if (currentStart === start && currentEnd === end) {
39887
40045
  return;
39888
40046
  }
40047
+ if (selection.rangeCount === 0) {
40048
+ const range = document.createRange();
40049
+ selection.addRange(range);
40050
+ }
39889
40051
  const currentRange = selection.getRangeAt(0);
39890
40052
  let range;
39891
40053
  if (this.el.contains(currentRange.startContainer)) {
@@ -39913,8 +40075,16 @@ class ContentEditableHelper {
39913
40075
  }
39914
40076
  let startNode = this.findChildAtCharacterIndex(start);
39915
40077
  let endNode = this.findChildAtCharacterIndex(end);
39916
- range.setStart(startNode.node, startNode.offset);
39917
- range.setEnd(endNode.node, endNode.offset);
40078
+ // setEnd (setStart) will result in a collapsed range if the end point is before the start point
40079
+ // https://developer.mozilla.org/en-US/docs/Web/API/Range/setEnd
40080
+ if (start <= end) {
40081
+ range.setStart(startNode.node, startNode.offset);
40082
+ range.setEnd(endNode.node, endNode.offset);
40083
+ }
40084
+ else {
40085
+ range.setStart(endNode.node, endNode.offset);
40086
+ range.setEnd(startNode.node, startNode.offset);
40087
+ }
39918
40088
  }
39919
40089
  }
39920
40090
  /**
@@ -40048,7 +40218,7 @@ class ContentEditableHelper {
40048
40218
  if (!focusedNode || !this.el.contains(focusedNode))
40049
40219
  return;
40050
40220
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40051
- element?.scrollIntoView({ block: "nearest" });
40221
+ element?.scrollIntoView?.({ block: "nearest" });
40052
40222
  }
40053
40223
  /**
40054
40224
  * remove the current selection of the user
@@ -40068,100 +40238,7 @@ class ContentEditableHelper {
40068
40238
  * finds the indexes of the current selection.
40069
40239
  * */
40070
40240
  getCurrentSelection() {
40071
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40072
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40073
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40074
- return {
40075
- start: startSizeBefore,
40076
- end: endSizeBefore,
40077
- };
40078
- }
40079
- /**
40080
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40081
- * The selected node is either a Text node or an Element node.
40082
- *
40083
- * case 1 -Text node:
40084
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40085
- * content length of all previous nodes.
40086
- *
40087
- * case 2 - Element node:
40088
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40089
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40090
- *
40091
- * See the MDN documentation for more details.
40092
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40093
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40094
- *
40095
- */
40096
- findSelectionIndex(nodeToFind, nodeOffset) {
40097
- let usedCharacters = 0;
40098
- let it = iterateChildren(this.el);
40099
- let current = it.next();
40100
- let isFirstParagraph = true;
40101
- while (!current.done && current.value !== nodeToFind) {
40102
- if (!current.value.hasChildNodes()) {
40103
- if (current.value.textContent) {
40104
- usedCharacters += current.value.textContent.length;
40105
- }
40106
- }
40107
- // One new paragraph = one new line character, except for the first paragraph
40108
- if (current.value.nodeName === "P" ||
40109
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40110
- ) {
40111
- if (isFirstParagraph) {
40112
- isFirstParagraph = false;
40113
- }
40114
- else {
40115
- usedCharacters++;
40116
- }
40117
- }
40118
- current = it.next();
40119
- }
40120
- if (current.value !== nodeToFind) {
40121
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40122
- * In this case, we return 0 because we don't know the size of the text before the selection.
40123
- *
40124
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40125
- *
40126
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40127
- */
40128
- return 0;
40129
- }
40130
- else {
40131
- if (!current.value.hasChildNodes()) {
40132
- usedCharacters += nodeOffset;
40133
- }
40134
- else {
40135
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40136
- usedCharacters += children.reduce((acc, child, index) => {
40137
- if (child.textContent !== null) {
40138
- // need to account for paragraph nodes that implicitely add a new line
40139
- // except for the last paragraph
40140
- let chars = child.textContent.length;
40141
- if (child.nodeName === "P" && index !== children.length - 1) {
40142
- chars++;
40143
- }
40144
- return acc + chars;
40145
- }
40146
- else {
40147
- return acc;
40148
- }
40149
- }, 0);
40150
- }
40151
- }
40152
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40153
- usedCharacters++;
40154
- }
40155
- return usedCharacters;
40156
- }
40157
- getStartAndEndSelection() {
40158
- const selection = document.getSelection();
40159
- return {
40160
- startElement: selection.anchorNode || this.el,
40161
- startSelectionOffset: selection.anchorOffset,
40162
- endElement: selection.focusNode || this.el,
40163
- endSelectionOffset: selection.focusOffset,
40164
- };
40241
+ return getCurrentSelection(this.el);
40165
40242
  }
40166
40243
  getText() {
40167
40244
  let text = "";
@@ -53116,6 +53193,10 @@ class CellPlugin extends CorePlugin {
53116
53193
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
53117
53194
  case "CLEAR_CELL":
53118
53195
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
53196
+ case "UPDATE_CELL_POSITION":
53197
+ return !cmd.cellId || this.cells[cmd.sheetId]?.[cmd.cellId]
53198
+ ? "Success" /* CommandResult.Success */
53199
+ : "InvalidCellId" /* CommandResult.InvalidCellId */;
53119
53200
  default:
53120
53201
  return "Success" /* CommandResult.Success */;
53121
53202
  }
@@ -53160,6 +53241,9 @@ class CellPlugin extends CorePlugin {
53160
53241
  case "DELETE_CONTENT":
53161
53242
  this.clearZones(cmd.sheetId, cmd.target);
53162
53243
  break;
53244
+ case "DELETE_SHEET": {
53245
+ this.history.update("cells", cmd.sheetId, undefined);
53246
+ }
53163
53247
  }
53164
53248
  }
53165
53249
  clearZones(sheetId, zones) {
@@ -53950,6 +54034,9 @@ class ConditionalFormatPlugin extends CorePlugin {
53950
54034
  allowDispatch(cmd) {
53951
54035
  switch (cmd.type) {
53952
54036
  case "ADD_CONDITIONAL_FORMAT":
54037
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
54038
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54039
+ }
53953
54040
  return this.checkValidations(cmd, this.checkCFRule, this.checkEmptyRange, this.checkCFHasChanged);
53954
54041
  case "CHANGE_CONDITIONAL_FORMAT_PRIORITY":
53955
54042
  return this.checkValidPriorityChange(cmd.cfId, cmd.delta, cmd.sheetId);
@@ -54366,8 +54453,17 @@ class DataValidationPlugin extends CorePlugin {
54366
54453
  allowDispatch(cmd) {
54367
54454
  switch (cmd.type) {
54368
54455
  case "ADD_DATA_VALIDATION_RULE":
54456
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
54457
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54458
+ }
54459
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
54460
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54461
+ }
54369
54462
  return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkValidRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
54370
54463
  case "REMOVE_DATA_VALIDATION_RULE":
54464
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
54465
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54466
+ }
54371
54467
  if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
54372
54468
  return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
54373
54469
  }
@@ -54594,6 +54690,7 @@ class DataValidationPlugin extends CorePlugin {
54594
54690
  class FigurePlugin extends CorePlugin {
54595
54691
  static getters = ["getFigures", "getFigure", "getFigureSheetId"];
54596
54692
  figures = {};
54693
+ insertionOrders = []; // TODO use a list in master
54597
54694
  // ---------------------------------------------------------------------------
54598
54695
  // Command Handling
54599
54696
  // ---------------------------------------------------------------------------
@@ -54696,11 +54793,14 @@ class FigurePlugin extends CorePlugin {
54696
54793
  }
54697
54794
  addFigure(figure, sheetId) {
54698
54795
  this.history.update("figures", sheetId, figure.id, figure);
54796
+ this.history.update("insertionOrders", this.insertionOrders.length, figure.id);
54699
54797
  }
54700
54798
  deleteSheet(sheetId) {
54799
+ this.history.update("insertionOrders", this.insertionOrders.filter((id) => !this.figures[sheetId]?.[id]));
54701
54800
  this.history.update("figures", sheetId, undefined);
54702
54801
  }
54703
54802
  removeFigure(id, sheetId) {
54803
+ this.history.update("insertionOrders", this.insertionOrders.filter((figureId) => figureId !== id));
54704
54804
  this.history.update("figures", sheetId, id, undefined);
54705
54805
  }
54706
54806
  checkFigureExists(sheetId, figureId) {
@@ -54719,7 +54819,14 @@ class FigurePlugin extends CorePlugin {
54719
54819
  // Getters
54720
54820
  // ---------------------------------------------------------------------------
54721
54821
  getFigures(sheetId) {
54722
- return Object.values(this.figures[sheetId] || {}).filter(isDefined);
54822
+ const figures = [];
54823
+ for (const figureId of this.insertionOrders) {
54824
+ const figure = this.figures[sheetId]?.[figureId];
54825
+ if (figure) {
54826
+ figures.push(figure);
54827
+ }
54828
+ }
54829
+ return figures;
54723
54830
  }
54724
54831
  getFigure(sheetId, figureId) {
54725
54832
  return this.figures[sheetId]?.[figureId];
@@ -54732,11 +54839,9 @@ class FigurePlugin extends CorePlugin {
54732
54839
  // ---------------------------------------------------------------------------
54733
54840
  import(data) {
54734
54841
  for (let sheet of data.sheets) {
54735
- const figures = {};
54736
- sheet.figures.forEach((figure) => {
54737
- figures[figure.id] = figure;
54738
- });
54739
- this.figures[sheet.id] = figures;
54842
+ for (const figure of sheet.figures) {
54843
+ this.addFigure(figure, sheet.id);
54844
+ }
54740
54845
  }
54741
54846
  }
54742
54847
  export(data) {
@@ -56162,6 +56267,9 @@ class SheetPlugin extends CorePlugin {
56162
56267
  case "CREATE_SHEET": {
56163
56268
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
56164
56269
  }
56270
+ case "DUPLICATE_SHEET": {
56271
+ return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
56272
+ }
56165
56273
  case "MOVE_SHEET":
56166
56274
  try {
56167
56275
  const currentIndex = this.orderedSheetIds.findIndex((id) => id === cmd.sheetId);
@@ -56973,6 +57081,10 @@ class SheetPlugin extends CorePlugin {
56973
57081
  checkZonesAreInSheet(cmd) {
56974
57082
  if (!("sheetId" in cmd))
56975
57083
  return "Success" /* CommandResult.Success */;
57084
+ if ("ranges" in cmd &&
57085
+ cmd.ranges.some((rangeData) => rangeData._sheetId !== "" && !this.getters.tryGetSheet(rangeData._sheetId))) {
57086
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57087
+ }
56976
57088
  return this.checkZonesExistInSheet(cmd.sheetId, this.getCommandZones(cmd));
56977
57089
  }
56978
57090
  }
@@ -56981,6 +57093,7 @@ let nextTableId = 1;
56981
57093
  class TablePlugin extends CorePlugin {
56982
57094
  static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
56983
57095
  tables = {};
57096
+ insertionOrders = {};
56984
57097
  adaptRanges(applyChange, sheetId) {
56985
57098
  const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
56986
57099
  for (const sheetId of sheetIds) {
@@ -56992,6 +57105,9 @@ class TablePlugin extends CorePlugin {
56992
57105
  allowDispatch(cmd) {
56993
57106
  switch (cmd.type) {
56994
57107
  case "CREATE_TABLE":
57108
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId) || rangeData._sheetId !== cmd.sheetId)) {
57109
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57110
+ }
56995
57111
  const zones = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData).zone);
56996
57112
  if (!areZonesContinuous(zones)) {
56997
57113
  return "NonContinuousTargets" /* CommandResult.NonContinuousTargets */;
@@ -57022,11 +57138,13 @@ class TablePlugin extends CorePlugin {
57022
57138
  switch (cmd.type) {
57023
57139
  case "CREATE_SHEET":
57024
57140
  this.history.update("tables", cmd.sheetId, {});
57141
+ this.history.update("insertionOrders", cmd.sheetId, []);
57025
57142
  break;
57026
57143
  case "DELETE_SHEET": {
57027
57144
  const tables = { ...this.tables };
57028
57145
  delete tables[cmd.sheetId];
57029
57146
  this.history.update("tables", tables);
57147
+ this.history.update("insertionOrders", cmd.sheetId, undefined);
57030
57148
  break;
57031
57149
  }
57032
57150
  case "DUPLICATE_SHEET": {
@@ -57038,6 +57156,9 @@ class TablePlugin extends CorePlugin {
57038
57156
  : this.copyStaticTableForSheet(cmd.sheetIdTo, table);
57039
57157
  }
57040
57158
  this.history.update("tables", cmd.sheetIdTo, newTables);
57159
+ this.history.update("insertionOrders", cmd.sheetIdTo, [
57160
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
57161
+ ]);
57041
57162
  break;
57042
57163
  }
57043
57164
  case "CREATE_TABLE": {
@@ -57051,6 +57172,10 @@ class TablePlugin extends CorePlugin {
57051
57172
  ? this.createDynamicTable(id, union, config)
57052
57173
  : this.createStaticTable(id, cmd.tableType, union, config);
57053
57174
  this.history.update("tables", cmd.sheetId, newTable.id, newTable);
57175
+ this.history.update("insertionOrders", cmd.sheetId, [
57176
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
57177
+ newTable.id,
57178
+ ]);
57054
57179
  break;
57055
57180
  }
57056
57181
  case "REMOVE_TABLE": {
@@ -57061,6 +57186,7 @@ class TablePlugin extends CorePlugin {
57061
57186
  }
57062
57187
  }
57063
57188
  this.history.update("tables", cmd.sheetId, tables);
57189
+ this.history.update("insertionOrders", cmd.sheetId, this.insertionOrders[cmd.sheetId]?.filter((id) => id in tables));
57064
57190
  break;
57065
57191
  }
57066
57192
  case "UPDATE_TABLE": {
@@ -57096,7 +57222,14 @@ class TablePlugin extends CorePlugin {
57096
57222
  }
57097
57223
  }
57098
57224
  getCoreTables(sheetId) {
57099
- return this.tables[sheetId] ? Object.values(this.tables[sheetId]).filter(isDefined) : [];
57225
+ const tables = [];
57226
+ for (const tableId of this.insertionOrders[sheetId] || []) {
57227
+ const table = this.tables[sheetId][tableId];
57228
+ if (table) {
57229
+ tables.push(table);
57230
+ }
57231
+ }
57232
+ return tables;
57100
57233
  }
57101
57234
  getCoreTable({ sheetId, col, row }) {
57102
57235
  return this.getCoreTables(sheetId).find((table) => isInside(col, row, table.range.zone));
@@ -57379,6 +57512,7 @@ class TablePlugin extends CorePlugin {
57379
57512
  // ---------------------------------------------------------------------------
57380
57513
  import(data) {
57381
57514
  for (const sheet of data.sheets) {
57515
+ const tableIds = [];
57382
57516
  for (const tableData of sheet.tables || []) {
57383
57517
  const uuid = `${nextTableId++}`;
57384
57518
  const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
@@ -57388,7 +57522,9 @@ class TablePlugin extends CorePlugin {
57388
57522
  ? this.createDynamicTable(uuid, range, tableConfig)
57389
57523
  : this.createStaticTable(uuid, tableType, range, tableConfig);
57390
57524
  this.history.update("tables", sheet.id, table.id, table);
57525
+ tableIds.push(table.id);
57391
57526
  }
57527
+ this.history.update("insertionOrders", sheet.id, tableIds);
57392
57528
  }
57393
57529
  }
57394
57530
  export(data) {
@@ -57428,7 +57564,10 @@ class HeaderGroupingPlugin extends CorePlugin {
57428
57564
  allowDispatch(cmd) {
57429
57565
  switch (cmd.type) {
57430
57566
  case "GROUP_HEADERS": {
57431
- const { start, end } = cmd;
57567
+ const { start, end, sheetId } = cmd;
57568
+ if (!this.getters.tryGetSheet(sheetId)) {
57569
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57570
+ }
57432
57571
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
57433
57572
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
57434
57573
  }
@@ -57441,7 +57580,10 @@ class HeaderGroupingPlugin extends CorePlugin {
57441
57580
  break;
57442
57581
  }
57443
57582
  case "UNGROUP_HEADERS": {
57444
- const { start, end } = cmd;
57583
+ const { start, end, sheetId } = cmd;
57584
+ if (!this.getters.tryGetSheet(sheetId)) {
57585
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57586
+ }
57445
57587
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
57446
57588
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
57447
57589
  }
@@ -57452,6 +57594,9 @@ class HeaderGroupingPlugin extends CorePlugin {
57452
57594
  }
57453
57595
  case "UNFOLD_HEADER_GROUP":
57454
57596
  case "FOLD_HEADER_GROUP":
57597
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
57598
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57599
+ }
57455
57600
  const group = this.findGroupWithStartEnd(cmd.sheetId, cmd.dimension, cmd.start, cmd.end);
57456
57601
  if (!group) {
57457
57602
  return "UnknownHeaderGroup" /* CommandResult.UnknownHeaderGroup */;
@@ -57852,6 +57997,9 @@ class PivotCorePlugin extends CorePlugin {
57852
57997
  return this.checkDuplicatedMeasureIds(cmd.pivot);
57853
57998
  }
57854
57999
  case "UPDATE_PIVOT": {
58000
+ if (!(cmd.pivotId in this.pivots)) {
58001
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
58002
+ }
57855
58003
  if (deepEquals(cmd.pivot, this.pivots[cmd.pivotId]?.definition)) {
57856
58004
  return "NoChanges" /* CommandResult.NoChanges */;
57857
58005
  }
@@ -57868,6 +58016,8 @@ class PivotCorePlugin extends CorePlugin {
57868
58016
  return "EmptyName" /* CommandResult.EmptyName */;
57869
58017
  }
57870
58018
  break;
58019
+ case "REMOVE_PIVOT":
58020
+ case "DUPLICATE_PIVOT":
57871
58021
  case "INSERT_PIVOT": {
57872
58022
  if (!(cmd.pivotId in this.pivots)) {
57873
58023
  return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
@@ -57917,7 +58067,7 @@ class PivotCorePlugin extends CorePlugin {
57917
58067
  break;
57918
58068
  }
57919
58069
  case "UPDATE_PIVOT": {
57920
- this.history.update("pivots", cmd.pivotId, "definition", cmd.pivot);
58070
+ this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
57921
58071
  this.compileCalculatedMeasures(cmd.pivot.measures);
57922
58072
  break;
57923
58073
  }
@@ -57988,7 +58138,7 @@ class PivotCorePlugin extends CorePlugin {
57988
58138
  // Private
57989
58139
  // -------------------------------------------------------------------------
57990
58140
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
57991
- this.history.update("pivots", pivotId, { definition: pivot, formulaId });
58141
+ this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
57992
58142
  this.compileCalculatedMeasures(pivot.measures);
57993
58143
  this.history.update("formulaIds", formulaId, pivotId);
57994
58144
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
@@ -59574,6 +59724,10 @@ class Evaluator {
59574
59724
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
59575
59725
  this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
59576
59726
  this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
59727
+ this.compilationParams.evalContext.lookupCaches = {
59728
+ forwardSearch: new Map(),
59729
+ reverseSearch: new Map(),
59730
+ };
59577
59731
  }
59578
59732
  createEmptyPositionSet() {
59579
59733
  const sheetSizes = {};
@@ -62894,6 +63048,9 @@ function updateChartRangesTransformation(toTransform, executed) {
62894
63048
  };
62895
63049
  }
62896
63050
  function createSheetTransformation(toTransform, executed) {
63051
+ if (toTransform.sheetId === executed.sheetId) {
63052
+ toTransform = { ...toTransform, sheetId: `${toTransform.sheetId}~` };
63053
+ }
62897
63054
  if (toTransform.name === executed.name) {
62898
63055
  return {
62899
63056
  ...toTransform,
@@ -63537,15 +63694,6 @@ class Session extends EventBus {
63537
63694
  }
63538
63695
  this.sendPendingMessage();
63539
63696
  }
63540
- dropPendingRevision(revisionId) {
63541
- this.revisions.drop(revisionId);
63542
- const revisionIds = this.pendingMessages
63543
- .filter((message) => message.type === "REMOTE_REVISION")
63544
- .map((message) => message.nextRevisionId);
63545
- this.trigger("pending-revisions-dropped", { revisionIds });
63546
- this.waitingAck = false;
63547
- this.waitingUndoRedoAck = false;
63548
- }
63549
63697
  /**
63550
63698
  * Send the next pending message
63551
63699
  */
@@ -63554,15 +63702,14 @@ class Session extends EventBus {
63554
63702
  if (!message)
63555
63703
  return;
63556
63704
  if (message.type === "REMOTE_REVISION") {
63557
- const revision = this.revisions.get(message.nextRevisionId);
63705
+ let revision = this.revisions.get(message.nextRevisionId);
63558
63706
  if (revision.commands.length === 0) {
63559
63707
  /**
63560
- * The command is empty, we have to drop all the next local revisions
63708
+ * The command is empty, we have to rebase all the next local revisions
63561
63709
  * to avoid issues with undo/redo
63562
63710
  */
63563
- this.dropPendingRevision(revision.id);
63564
- this.pendingMessages = [];
63565
- return;
63711
+ this.revisions.rebase(revision.id);
63712
+ revision = this.revisions.get(message.nextRevisionId);
63566
63713
  }
63567
63714
  message = {
63568
63715
  ...message,
@@ -63598,18 +63745,16 @@ class Session extends EventBus {
63598
63745
  case "REVISION_UNDONE": {
63599
63746
  this.waitingAck = false;
63600
63747
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63601
- const pendingRemoteRevisions = this.pendingMessages.filter((message) => message.type === "REMOTE_REVISION");
63602
- const firstTransformedRevisionIndex = pendingRemoteRevisions.findIndex((message) => !deepEquals(message.commands, this.revisions.get(message.nextRevisionId).commands));
63603
- if (firstTransformedRevisionIndex !== -1) {
63748
+ const firstPendingRevisionId = this.pendingMessages.findIndex((message) => message.type === "REMOTE_REVISION");
63749
+ if (firstPendingRevisionId !== -1) {
63604
63750
  /**
63605
63751
  * Some revisions undergo transformations that may cause issues with
63606
63752
  * undo/redo if the transformation is destructive (we don't get back
63607
63753
  * the original command by transforming it with the inverse).
63608
- * To prevent these problems, we must discard all subsequent local
63754
+ * To prevent these problems, we must rebase all subsequent local
63609
63755
  * revisions.
63610
63756
  */
63611
- this.dropPendingRevision(this.pendingMessages[firstTransformedRevisionIndex].nextRevisionId);
63612
- this.pendingMessages = this.pendingMessages.slice(0, firstTransformedRevisionIndex);
63757
+ this.revisions.rebase(this.pendingMessages[firstPendingRevisionId].nextRevisionId);
63613
63758
  }
63614
63759
  this.serverRevisionId = message.nextRevisionId;
63615
63760
  this.processedRevisions.add(message.nextRevisionId);
@@ -64737,6 +64882,10 @@ class SheetUIPlugin extends UIPlugin {
64737
64882
  */
64738
64883
  checkZonesAreInSheet(cmd) {
64739
64884
  const sheetId = "sheetId" in cmd ? cmd.sheetId : this.getters.tryGetActiveSheetId();
64885
+ if ("ranges" in cmd &&
64886
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
64887
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
64888
+ }
64740
64889
  const zones = this.getters.getCommandZones(cmd);
64741
64890
  if (!sheetId && zones.length > 0) {
64742
64891
  return "NoActiveSheet" /* CommandResult.NoActiveSheet */;
@@ -65291,7 +65440,6 @@ class HistoryPlugin extends UIPlugin {
65291
65440
  super(config);
65292
65441
  this.session = config.session;
65293
65442
  this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
65294
- this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
65295
65443
  this.session.on("snapshot", this, () => {
65296
65444
  this.undoStack = [];
65297
65445
  this.redoStack = [];
@@ -65361,10 +65509,6 @@ class HistoryPlugin extends UIPlugin {
65361
65509
  const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
65362
65510
  return canRepeatRevision(lastNonRedoRevision);
65363
65511
  }
65364
- drop(revisionIds) {
65365
- this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
65366
- this.redoStack = [];
65367
- }
65368
65512
  onNewLocalStateUpdate({ id }) {
65369
65513
  this.undoStack.push(id);
65370
65514
  this.redoStack = [];
@@ -68068,7 +68212,9 @@ class HeaderPositionsUIPlugin extends UIPlugin {
68068
68212
  case "UNGROUP_HEADERS":
68069
68213
  case "GROUP_HEADERS":
68070
68214
  case "CREATE_SHEET":
68071
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
68215
+ if (this.getters.tryGetSheet(cmd.sheetId)) {
68216
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
68217
+ }
68072
68218
  break;
68073
68219
  case "DUPLICATE_SHEET":
68074
68220
  this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
@@ -68076,12 +68222,14 @@ class HeaderPositionsUIPlugin extends UIPlugin {
68076
68222
  }
68077
68223
  }
68078
68224
  finalize() {
68079
- if (this.isDirty) {
68080
- for (const sheetId of this.getters.getSheetIds()) {
68225
+ for (const sheetId of this.getters.getSheetIds()) {
68226
+ // sheets can be created without this plugin being aware of it
68227
+ // in concurrent situations.
68228
+ if (this.isDirty || !this.headerPositions[sheetId]) {
68081
68229
  this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
68082
68230
  }
68083
- this.isDirty = false;
68084
68231
  }
68232
+ this.isDirty = false;
68085
68233
  }
68086
68234
  /**
68087
68235
  * Returns the size, start and end coordinates of a column on an unfolded sheet
@@ -71502,9 +71650,16 @@ class SelectiveHistory {
71502
71650
  this.fastForward();
71503
71651
  this.insert(redoId, this.buildEmpty(redoId), insertAfter);
71504
71652
  }
71505
- drop(operationId) {
71653
+ rebase(operationId) {
71654
+ const operation = this.get(operationId);
71655
+ const execution = [...this.tree.execution(this.HEAD_BRANCH).startAfter(operationId)];
71506
71656
  this.revertBefore(operationId);
71657
+ const baseId = this.HEAD_OPERATION.id;
71507
71658
  this.tree.drop(operationId);
71659
+ this.insert(operationId, operation, baseId);
71660
+ for (const { operation } of execution) {
71661
+ this.insert(operation.id, operation.data, this.HEAD_OPERATION.id);
71662
+ }
71508
71663
  }
71509
71664
  /**
71510
71665
  * Revert the state as it was *before* the given operation was executed.
@@ -74615,6 +74770,11 @@ class Model extends EventBus {
74615
74770
  dispatch: (command) => {
74616
74771
  const result = this.checkDispatchAllowed(command);
74617
74772
  if (!result.isSuccessful) {
74773
+ // core views plugins need to be invalidated
74774
+ this.dispatchToHandlers(this.coreHandlers, {
74775
+ type: "UNDO",
74776
+ commands: [command],
74777
+ });
74618
74778
  return;
74619
74779
  }
74620
74780
  this.isReplayingCommand = true;
@@ -75121,6 +75281,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75121
75281
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75122
75282
 
75123
75283
 
75124
- __info__.version = "18.1.8";
75125
- __info__.date = "2025-02-14T08:42:08.322Z";
75126
- __info__.hash = "02682f4";
75284
+ __info__.version = "18.1.9";
75285
+ __info__.date = "2025-02-25T05:59:45.472Z";
75286
+ __info__.hash = "6789c1c";