@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
  'use strict';
@@ -2220,17 +2220,7 @@ function toZoneWithoutBoundaryChanges(xc) {
2220
2220
  */
2221
2221
  function toUnboundedZone(xc) {
2222
2222
  const zone = toZoneWithoutBoundaryChanges(xc);
2223
- if (zone.right !== undefined && zone.right < zone.left) {
2224
- const tmp = zone.left;
2225
- zone.left = zone.right;
2226
- zone.right = tmp;
2227
- }
2228
- if (zone.bottom !== undefined && zone.bottom < zone.top) {
2229
- const tmp = zone.top;
2230
- zone.top = zone.bottom;
2231
- zone.bottom = tmp;
2232
- }
2233
- return zone;
2223
+ return reorderZone(zone);
2234
2224
  }
2235
2225
  /**
2236
2226
  * Convert from a cartesian reference to a Zone.
@@ -2504,11 +2494,11 @@ function positions(zone) {
2504
2494
  return positions;
2505
2495
  }
2506
2496
  function reorderZone(zone) {
2507
- if (zone.left > zone.right) {
2508
- zone = { left: zone.right, right: zone.left, top: zone.top, bottom: zone.bottom };
2497
+ if (zone.right !== undefined && zone.left > zone.right) {
2498
+ zone = { ...zone, left: zone.right, right: zone.left };
2509
2499
  }
2510
- if (zone.top > zone.bottom) {
2511
- zone = { left: zone.left, right: zone.right, top: zone.bottom, bottom: zone.top };
2500
+ if (zone.bottom !== undefined && zone.top > zone.bottom) {
2501
+ zone = { ...zone, top: zone.bottom, bottom: zone.top };
2512
2502
  }
2513
2503
  return zone;
2514
2504
  }
@@ -3413,12 +3403,12 @@ function isTargetDependent(cmd) {
3413
3403
  function isRangeDependant(cmd) {
3414
3404
  return "ranges" in cmd;
3415
3405
  }
3416
- function isZoneDependent(cmd) {
3417
- return "zone" in cmd;
3418
- }
3419
3406
  function isPositionDependent(cmd) {
3420
3407
  return "col" in cmd && "row" in cmd && "sheetId" in cmd;
3421
3408
  }
3409
+ function isZoneDependent(cmd) {
3410
+ return "sheetId" in cmd && "zone" in cmd;
3411
+ }
3422
3412
  const invalidateEvaluationCommands = new Set([
3423
3413
  "RENAME_SHEET",
3424
3414
  "DELETE_SHEET",
@@ -3430,6 +3420,7 @@ const invalidateEvaluationCommands = new Set([
3430
3420
  "REDO",
3431
3421
  "ADD_MERGE",
3432
3422
  "REMOVE_MERGE",
3423
+ "DUPLICATE_SHEET",
3433
3424
  "UPDATE_LOCALE",
3434
3425
  "ADD_PIVOT",
3435
3426
  "UPDATE_PIVOT",
@@ -3459,7 +3450,6 @@ const invalidateChartEvaluationCommands = new Set([
3459
3450
  ]);
3460
3451
  const invalidateDependenciesCommands = new Set(["MOVE_RANGES"]);
3461
3452
  const invalidateCFEvaluationCommands = new Set([
3462
- "DUPLICATE_SHEET",
3463
3453
  "EVALUATE_CELLS",
3464
3454
  "ADD_CONDITIONAL_FORMAT",
3465
3455
  "REMOVE_CONDITIONAL_FORMAT",
@@ -3629,6 +3619,7 @@ exports.CommandResult = void 0;
3629
3619
  CommandResult["InvalidRange"] = "InvalidRange";
3630
3620
  CommandResult["InvalidZones"] = "InvalidZones";
3631
3621
  CommandResult["InvalidSheetId"] = "InvalidSheetId";
3622
+ CommandResult["InvalidCellId"] = "InvalidCellId";
3632
3623
  CommandResult["InvalidFigureId"] = "InvalidFigureId";
3633
3624
  CommandResult["InputAlreadyFocused"] = "InputAlreadyFocused";
3634
3625
  CommandResult["MaximumRangesReached"] = "MaximumRangesReached";
@@ -4460,7 +4451,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4460
4451
  * @param reverseSearch if true, search in the array starting from the end.
4461
4452
 
4462
4453
  */
4463
- function linearSearch(data, target, mode, numberOfValues, getValueInData, reverseSearch = false) {
4454
+ function linearSearch(data, target, mode, numberOfValues, getValueInData, lookupCaches, reverseSearch = false) {
4464
4455
  if (target === undefined || target.value === null) {
4465
4456
  return -1;
4466
4457
  }
@@ -4469,17 +4460,48 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4469
4460
  }
4470
4461
  const _target = normalizeValue(target.value);
4471
4462
  const getValue = reverseSearch
4472
- ? (data, i) => getValueInData(data, numberOfValues - i - 1)
4473
- : getValueInData;
4463
+ ? (data, i) => normalizeValue(getValueInData(data, numberOfValues - i - 1))
4464
+ : (data, i) => normalizeValue(getValueInData(data, i));
4465
+ // first check if the target is in the cache
4466
+ const isNotWildcardTarget = mode !== "wildcard" ||
4467
+ typeof _target !== "string" ||
4468
+ !(_target.includes("*") || _target.includes("?"));
4469
+ if (lookupCaches && isNotWildcardTarget) {
4470
+ const searchMode = reverseSearch ? "reverseSearch" : "forwardSearch";
4471
+ let cache = lookupCaches[searchMode].get(data);
4472
+ if (cache === undefined) {
4473
+ // build the cache for all the values
4474
+ cache = new Map();
4475
+ for (let i = 0; i < numberOfValues; i++) {
4476
+ const value = getValue(data, i) ?? null;
4477
+ if (!cache.has(value)) {
4478
+ cache.set(value, i);
4479
+ }
4480
+ }
4481
+ lookupCaches[searchMode].set(data, cache);
4482
+ }
4483
+ if (cache.has(_target)) {
4484
+ const resultIndex = cache.get(_target);
4485
+ return reverseSearch ? numberOfValues - resultIndex - 1 : resultIndex;
4486
+ }
4487
+ if (mode === "strict") {
4488
+ return -1;
4489
+ }
4490
+ }
4491
+ // else perform the linear search
4492
+ const resultIndex = _linearSearch(data, _target, mode, numberOfValues, getValue);
4493
+ return reverseSearch && resultIndex !== -1 ? numberOfValues - resultIndex - 1 : resultIndex;
4494
+ }
4495
+ function _linearSearch(data, _target, mode, numberOfValues, getNormalizeValue) {
4474
4496
  let indexMatchTarget = (i) => {
4475
- return normalizeValue(getValue(data, i)) === _target;
4497
+ return getNormalizeValue(data, i) === _target;
4476
4498
  };
4477
4499
  if (mode === "wildcard" &&
4478
4500
  typeof _target === "string" &&
4479
4501
  (_target.includes("*") || _target.includes("?"))) {
4480
4502
  const regExp = wildcardToRegExp(_target);
4481
4503
  indexMatchTarget = (i) => {
4482
- const value = normalizeValue(getValue(data, i));
4504
+ const value = getNormalizeValue(data, i);
4483
4505
  if (typeof value === "string") {
4484
4506
  return regExp.test(value);
4485
4507
  }
@@ -4490,7 +4512,7 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4490
4512
  let closestMatchIndex = -1;
4491
4513
  if (mode === "nextSmaller") {
4492
4514
  indexMatchTarget = (i) => {
4493
- const value = normalizeValue(getValue(data, i));
4515
+ const value = getNormalizeValue(data, i);
4494
4516
  if ((!closestMatch && compareCellValues(_target, value) >= 0) ||
4495
4517
  (compareCellValues(_target, value) >= 0 && compareCellValues(value, closestMatch) > 0)) {
4496
4518
  closestMatch = value;
@@ -4501,7 +4523,7 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4501
4523
  }
4502
4524
  if (mode === "nextGreater") {
4503
4525
  indexMatchTarget = (i) => {
4504
- const value = normalizeValue(getValue(data, i));
4526
+ const value = getNormalizeValue(data, i);
4505
4527
  if ((!closestMatch && compareCellValues(_target, value) <= 0) ||
4506
4528
  (compareCellValues(_target, value) <= 0 && compareCellValues(value, closestMatch) < 0)) {
4507
4529
  closestMatch = value;
@@ -4512,12 +4534,10 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4512
4534
  }
4513
4535
  for (let i = 0; i < numberOfValues; i++) {
4514
4536
  if (indexMatchTarget(i)) {
4515
- return reverseSearch ? numberOfValues - i - 1 : i;
4537
+ return i;
4516
4538
  }
4517
4539
  }
4518
- return reverseSearch && closestMatchIndex !== -1
4519
- ? numberOfValues - closestMatchIndex - 1
4520
- : closestMatchIndex;
4540
+ return closestMatchIndex;
4521
4541
  }
4522
4542
  /**
4523
4543
  * Normalize a value.
@@ -6491,10 +6511,11 @@ class UuidGenerator {
6491
6511
  *
6492
6512
  */
6493
6513
  smallUuid() {
6494
- //@ts-ignore
6495
- if (window.crypto && window.crypto.getRandomValues) {
6496
- //@ts-ignore
6497
- return ([1e7] + -1e3).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6514
+ if (window.crypto) {
6515
+ return "10000000-1000".replace(/[01]/g, (c) => {
6516
+ const n = Number(c);
6517
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6518
+ });
6498
6519
  }
6499
6520
  else {
6500
6521
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -6509,10 +6530,11 @@ class UuidGenerator {
6509
6530
  * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
6510
6531
  */
6511
6532
  uuidv4() {
6512
- //@ts-ignore
6513
- if (window.crypto && window.crypto.getRandomValues) {
6514
- //@ts-ignore
6515
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6533
+ if (window.crypto) {
6534
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
6535
+ const n = Number(c);
6536
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6537
+ });
6516
6538
  }
6517
6539
  else {
6518
6540
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -12065,6 +12087,25 @@ const LN = {
12065
12087
  isExported: true,
12066
12088
  };
12067
12089
  // -----------------------------------------------------------------------------
12090
+ // LOG
12091
+ // -----------------------------------------------------------------------------
12092
+ const LOG = {
12093
+ description: _t("The logarithm of a number, for a given base."),
12094
+ args: [
12095
+ arg("value (number)", _t("The value for which to calculate the logarithm.")),
12096
+ arg("base (number, default=10)", _t("The base of the logarithm.")),
12097
+ ],
12098
+ compute: function (value, base = { value: 10 }) {
12099
+ const _value = toNumber(value, this.locale);
12100
+ const _base = toNumber(base, this.locale);
12101
+ assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
12102
+ assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
12103
+ assert(() => _base !== 1, _t("The base must be different from 1."));
12104
+ return Math.log10(_value) / Math.log10(_base);
12105
+ },
12106
+ isExported: true,
12107
+ };
12108
+ // -----------------------------------------------------------------------------
12068
12109
  // MOD
12069
12110
  // -----------------------------------------------------------------------------
12070
12111
  function mod(dividend, divisor) {
@@ -12604,6 +12645,7 @@ var math = /*#__PURE__*/Object.freeze({
12604
12645
  ISODD: ISODD,
12605
12646
  ISO_CEILING: ISO_CEILING,
12606
12647
  LN: LN,
12648
+ LOG: LOG,
12607
12649
  MOD: MOD,
12608
12650
  MUNIT: MUNIT,
12609
12651
  ODD: ODD,
@@ -18502,7 +18544,7 @@ const HLOOKUP = {
18502
18544
  const _isSorted = toBoolean(isSorted.value);
18503
18545
  const colIndex = _isSorted
18504
18546
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18505
- : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18547
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches);
18506
18548
  const col = _range[colIndex];
18507
18549
  if (col === undefined) {
18508
18550
  return valueNotAvailable(searchKey);
@@ -18657,7 +18699,7 @@ const MATCH = {
18657
18699
  index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18658
18700
  break;
18659
18701
  case 0:
18660
- index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18702
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18661
18703
  break;
18662
18704
  case -1:
18663
18705
  index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
@@ -18725,7 +18767,7 @@ const VLOOKUP = {
18725
18767
  const _isSorted = toBoolean(isSorted.value);
18726
18768
  const rowIndex = _isSorted
18727
18769
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18728
- : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18770
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches);
18729
18771
  const value = _range[_index - 1][rowIndex];
18730
18772
  if (value === undefined) {
18731
18773
  return valueNotAvailable(searchKey);
@@ -18781,7 +18823,7 @@ const XLOOKUP = {
18781
18823
  const reverseSearch = _searchMode === -1;
18782
18824
  const index = _searchMode === 2 || _searchMode === -2
18783
18825
  ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18784
- : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18826
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18785
18827
  if (index !== -1) {
18786
18828
  return lookupDirection === "col"
18787
18829
  ? _returnRange.map((col) => [col[index]])
@@ -28269,7 +28311,7 @@ function getBarChartData(definition, dataSets, labelRange, getters) {
28269
28311
  }
28270
28312
  function getPyramidChartData(definition, dataSets, labelRange, getters) {
28271
28313
  const barChartData = getBarChartData(definition, dataSets, labelRange, getters);
28272
- const barDataset = barChartData.dataSetsValues;
28314
+ const barDataset = barChartData.dataSetsValues.filter((ds) => !ds.hidden);
28273
28315
  const pyramidDatasetValues = [];
28274
28316
  if (barDataset[0]) {
28275
28317
  const pyramidData = barDataset[0].data.map((value) => (value > 0 ? value : 0));
@@ -28746,10 +28788,8 @@ function getChartDatasetFormat(getters, allDataSets, axis) {
28746
28788
  function getChartDatasetValues(getters, dataSets) {
28747
28789
  const datasetValues = [];
28748
28790
  for (const [dsIndex, ds] of Object.entries(dataSets)) {
28749
- if (getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left)) {
28750
- continue;
28751
- }
28752
28791
  let label;
28792
+ let hidden = getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left);
28753
28793
  if (ds.labelCell) {
28754
28794
  const labelRange = ds.labelCell;
28755
28795
  const cell = labelRange
@@ -28776,9 +28816,9 @@ function getChartDatasetValues(getters, dataSets) {
28776
28816
  data.fill(1);
28777
28817
  }
28778
28818
  else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28779
- continue;
28819
+ hidden = true;
28780
28820
  }
28781
- datasetValues.push({ data, label });
28821
+ datasetValues.push({ data, label, hidden });
28782
28822
  }
28783
28823
  return datasetValues;
28784
28824
  }
@@ -28789,12 +28829,13 @@ function getBarChartDatasets(definition, args) {
28789
28829
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28790
28830
  const trendDatasets = [];
28791
28831
  for (const index in dataSetsValues) {
28792
- let { label, data } = dataSetsValues[index];
28832
+ let { label, data, hidden } = dataSetsValues[index];
28793
28833
  label = definition.dataSets?.[index].label || label;
28794
28834
  const backgroundColor = colors.next();
28795
28835
  const dataset = {
28796
28836
  label,
28797
28837
  data,
28838
+ hidden,
28798
28839
  borderColor: definition.background || BACKGROUND_CHART_COLOR,
28799
28840
  borderWidth: definition.stacked ? 1 : 0,
28800
28841
  backgroundColor,
@@ -28827,6 +28868,9 @@ function getWaterfallDatasetAndLabels(definition, args) {
28827
28868
  const labelsWithSubTotals = [];
28828
28869
  let lastValue = 0;
28829
28870
  for (const dataSetsValue of dataSetsValues) {
28871
+ if (dataSetsValue.hidden) {
28872
+ continue;
28873
+ }
28830
28874
  for (let i = 0; i < dataSetsValue.data.length; i++) {
28831
28875
  const data = dataSetsValue.data[i];
28832
28876
  labelsWithSubTotals.push(labels[i]);
@@ -28862,7 +28906,7 @@ function getLineChartDatasets(definition, args) {
28862
28906
  const trendDatasets = [];
28863
28907
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28864
28908
  for (let index = 0; index < dataSetsValues.length; index++) {
28865
- let { label, data } = dataSetsValues[index];
28909
+ let { label, data, hidden } = dataSetsValues[index];
28866
28910
  label = definition.dataSets?.[index].label || label;
28867
28911
  const color = colors.next();
28868
28912
  if (axisType && ["linear", "time"].includes(axisType)) {
@@ -28872,6 +28916,7 @@ function getLineChartDatasets(definition, args) {
28872
28916
  const dataset = {
28873
28917
  label,
28874
28918
  data,
28919
+ hidden,
28875
28920
  tension: 0, // 0 -> render straight lines, which is much faster
28876
28921
  borderColor: color,
28877
28922
  backgroundColor: areaChart ? setColorAlpha(color, LINE_FILL_TRANSPARENCY) : color,
@@ -28904,11 +28949,13 @@ function getPieChartDatasets(definition, args) {
28904
28949
  const dataSets = [];
28905
28950
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
28906
28951
  const backgroundColor = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
28907
- for (const { label, data } of dataSetsValues) {
28952
+ for (const { label, data, hidden } of dataSetsValues) {
28953
+ if (hidden)
28954
+ continue;
28908
28955
  const dataset = {
28909
28956
  label,
28910
28957
  data,
28911
- borderColor: BACKGROUND_CHART_COLOR,
28958
+ borderColor: definition.background || "#FFFFFF",
28912
28959
  backgroundColor,
28913
28960
  hoverOffset: 30,
28914
28961
  };
@@ -28922,7 +28969,7 @@ function getComboChartDatasets(definition, args) {
28922
28969
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28923
28970
  const trendDatasets = [];
28924
28971
  for (let index = 0; index < dataSetsValues.length; index++) {
28925
- let { label, data } = dataSetsValues[index];
28972
+ let { label, data, hidden } = dataSetsValues[index];
28926
28973
  label = definition.dataSets?.[index].label || label;
28927
28974
  const design = definition.dataSets?.[index];
28928
28975
  const color = colors.next();
@@ -28930,6 +28977,7 @@ function getComboChartDatasets(definition, args) {
28930
28977
  const dataset = {
28931
28978
  label: label,
28932
28979
  data,
28980
+ hidden,
28933
28981
  borderColor: color,
28934
28982
  backgroundColor: color,
28935
28983
  yAxisID: definition.dataSets?.[index].yAxisId || "y",
@@ -28954,7 +29002,7 @@ function getRadarChartDatasets(definition, args) {
28954
29002
  const fill = definition.fillArea ?? false;
28955
29003
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28956
29004
  for (let i = 0; i < dataSetsValues.length; i++) {
28957
- let { label, data } = dataSetsValues[i];
29005
+ let { label, data, hidden } = dataSetsValues[i];
28958
29006
  if (definition.dataSets?.[i]?.label) {
28959
29007
  label = definition.dataSets[i].label;
28960
29008
  }
@@ -28962,6 +29010,7 @@ function getRadarChartDatasets(definition, args) {
28962
29010
  const dataset = {
28963
29011
  label,
28964
29012
  data,
29013
+ hidden,
28965
29014
  borderColor,
28966
29015
  backgroundColor: borderColor,
28967
29016
  };
@@ -29107,6 +29156,11 @@ function getPieChartLegend(definition, args) {
29107
29156
  hidden: false,
29108
29157
  lineWidth: 2,
29109
29158
  })),
29159
+ filter: (legendItem, data) => {
29160
+ return "datasetIndex" in legendItem
29161
+ ? !data.datasets[legendItem.datasetIndex].hidden
29162
+ : true;
29163
+ },
29110
29164
  },
29111
29165
  };
29112
29166
  }
@@ -29168,6 +29222,11 @@ function getWaterfallChartLegend(definition, args) {
29168
29222
  }
29169
29223
  return legendValues;
29170
29224
  },
29225
+ filter: (legendItem, data) => {
29226
+ return "datasetIndex" in legendItem
29227
+ ? !data.datasets[legendItem.datasetIndex].hidden
29228
+ : true;
29229
+ },
29171
29230
  },
29172
29231
  onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29173
29232
  };
@@ -29251,6 +29310,11 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29251
29310
  ...legendLabelConfig,
29252
29311
  };
29253
29312
  }),
29313
+ filter: (legendItem, data) => {
29314
+ return "datasetIndex" in legendItem
29315
+ ? !data.datasets[legendItem.datasetIndex].hidden
29316
+ : true;
29317
+ },
29254
29318
  },
29255
29319
  };
29256
29320
  }
@@ -33040,6 +33104,100 @@ function* iterateChildren(el) {
33040
33104
  function getOpenedMenus() {
33041
33105
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33042
33106
  }
33107
+ function getCurrentSelection(el) {
33108
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33109
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33110
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33111
+ return {
33112
+ start: startSizeBefore,
33113
+ end: endSizeBefore,
33114
+ };
33115
+ }
33116
+ function getStartAndEndSelection(el) {
33117
+ const selection = document.getSelection();
33118
+ return {
33119
+ startElement: selection.anchorNode || el,
33120
+ startSelectionOffset: selection.anchorOffset,
33121
+ endElement: selection.focusNode || el,
33122
+ endSelectionOffset: selection.focusOffset,
33123
+ };
33124
+ }
33125
+ /**
33126
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33127
+ * The selected node is either a Text node or an Element node.
33128
+ *
33129
+ * case 1 -Text node:
33130
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33131
+ * content length of all previous nodes.
33132
+ *
33133
+ * case 2 - Element node:
33134
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33135
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33136
+ *
33137
+ * See the MDN documentation for more details.
33138
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33139
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33140
+ *
33141
+ */
33142
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33143
+ let usedCharacters = 0;
33144
+ let it = iterateChildren(el);
33145
+ let current = it.next();
33146
+ let isFirstParagraph = true;
33147
+ while (!current.done && current.value !== nodeToFind) {
33148
+ if (!current.value.hasChildNodes()) {
33149
+ if (current.value.textContent) {
33150
+ usedCharacters += current.value.textContent.length;
33151
+ }
33152
+ }
33153
+ // One new paragraph = one new line character, except for the first paragraph
33154
+ if (current.value.nodeName === "P" ||
33155
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33156
+ ) {
33157
+ if (isFirstParagraph) {
33158
+ isFirstParagraph = false;
33159
+ }
33160
+ else {
33161
+ usedCharacters++;
33162
+ }
33163
+ }
33164
+ current = it.next();
33165
+ }
33166
+ if (current.value !== nodeToFind) {
33167
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33168
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33169
+ *
33170
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33171
+ */
33172
+ return 0;
33173
+ }
33174
+ else {
33175
+ if (!current.value.hasChildNodes()) {
33176
+ usedCharacters += nodeOffset;
33177
+ }
33178
+ else {
33179
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33180
+ usedCharacters += children.reduce((acc, child, index) => {
33181
+ if (child.textContent !== null) {
33182
+ // need to account for paragraph nodes that implicitly add a new line
33183
+ // except for the last paragraph
33184
+ let chars = child.textContent.length;
33185
+ if (child.nodeName === "P" && index !== children.length - 1) {
33186
+ chars++;
33187
+ }
33188
+ return acc + chars;
33189
+ }
33190
+ else {
33191
+ return acc;
33192
+ }
33193
+ }, 0);
33194
+ }
33195
+ }
33196
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33197
+ usedCharacters++;
33198
+ }
33199
+ return usedCharacters;
33200
+ }
33043
33201
  const letterRegex = /^[a-zA-Z]$/;
33044
33202
  /**
33045
33203
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -38275,7 +38433,7 @@ css /* scss */ `
38275
38433
  .o-font-size-editor {
38276
38434
  height: calc(100% - 4px);
38277
38435
  input.o-font-size {
38278
- outline-color: ${SELECTION_BORDER_COLOR};
38436
+ outline: none;
38279
38437
  height: 20px;
38280
38438
  width: 23px;
38281
38439
  }
@@ -39888,6 +40046,10 @@ class ContentEditableHelper {
39888
40046
  if (currentStart === start && currentEnd === end) {
39889
40047
  return;
39890
40048
  }
40049
+ if (selection.rangeCount === 0) {
40050
+ const range = document.createRange();
40051
+ selection.addRange(range);
40052
+ }
39891
40053
  const currentRange = selection.getRangeAt(0);
39892
40054
  let range;
39893
40055
  if (this.el.contains(currentRange.startContainer)) {
@@ -39915,8 +40077,16 @@ class ContentEditableHelper {
39915
40077
  }
39916
40078
  let startNode = this.findChildAtCharacterIndex(start);
39917
40079
  let endNode = this.findChildAtCharacterIndex(end);
39918
- range.setStart(startNode.node, startNode.offset);
39919
- range.setEnd(endNode.node, endNode.offset);
40080
+ // setEnd (setStart) will result in a collapsed range if the end point is before the start point
40081
+ // https://developer.mozilla.org/en-US/docs/Web/API/Range/setEnd
40082
+ if (start <= end) {
40083
+ range.setStart(startNode.node, startNode.offset);
40084
+ range.setEnd(endNode.node, endNode.offset);
40085
+ }
40086
+ else {
40087
+ range.setStart(endNode.node, endNode.offset);
40088
+ range.setEnd(startNode.node, startNode.offset);
40089
+ }
39920
40090
  }
39921
40091
  }
39922
40092
  /**
@@ -40050,7 +40220,7 @@ class ContentEditableHelper {
40050
40220
  if (!focusedNode || !this.el.contains(focusedNode))
40051
40221
  return;
40052
40222
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40053
- element?.scrollIntoView({ block: "nearest" });
40223
+ element?.scrollIntoView?.({ block: "nearest" });
40054
40224
  }
40055
40225
  /**
40056
40226
  * remove the current selection of the user
@@ -40070,100 +40240,7 @@ class ContentEditableHelper {
40070
40240
  * finds the indexes of the current selection.
40071
40241
  * */
40072
40242
  getCurrentSelection() {
40073
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40074
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40075
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40076
- return {
40077
- start: startSizeBefore,
40078
- end: endSizeBefore,
40079
- };
40080
- }
40081
- /**
40082
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40083
- * The selected node is either a Text node or an Element node.
40084
- *
40085
- * case 1 -Text node:
40086
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40087
- * content length of all previous nodes.
40088
- *
40089
- * case 2 - Element node:
40090
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40091
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40092
- *
40093
- * See the MDN documentation for more details.
40094
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40095
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40096
- *
40097
- */
40098
- findSelectionIndex(nodeToFind, nodeOffset) {
40099
- let usedCharacters = 0;
40100
- let it = iterateChildren(this.el);
40101
- let current = it.next();
40102
- let isFirstParagraph = true;
40103
- while (!current.done && current.value !== nodeToFind) {
40104
- if (!current.value.hasChildNodes()) {
40105
- if (current.value.textContent) {
40106
- usedCharacters += current.value.textContent.length;
40107
- }
40108
- }
40109
- // One new paragraph = one new line character, except for the first paragraph
40110
- if (current.value.nodeName === "P" ||
40111
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40112
- ) {
40113
- if (isFirstParagraph) {
40114
- isFirstParagraph = false;
40115
- }
40116
- else {
40117
- usedCharacters++;
40118
- }
40119
- }
40120
- current = it.next();
40121
- }
40122
- if (current.value !== nodeToFind) {
40123
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40124
- * In this case, we return 0 because we don't know the size of the text before the selection.
40125
- *
40126
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40127
- *
40128
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40129
- */
40130
- return 0;
40131
- }
40132
- else {
40133
- if (!current.value.hasChildNodes()) {
40134
- usedCharacters += nodeOffset;
40135
- }
40136
- else {
40137
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40138
- usedCharacters += children.reduce((acc, child, index) => {
40139
- if (child.textContent !== null) {
40140
- // need to account for paragraph nodes that implicitely add a new line
40141
- // except for the last paragraph
40142
- let chars = child.textContent.length;
40143
- if (child.nodeName === "P" && index !== children.length - 1) {
40144
- chars++;
40145
- }
40146
- return acc + chars;
40147
- }
40148
- else {
40149
- return acc;
40150
- }
40151
- }, 0);
40152
- }
40153
- }
40154
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40155
- usedCharacters++;
40156
- }
40157
- return usedCharacters;
40158
- }
40159
- getStartAndEndSelection() {
40160
- const selection = document.getSelection();
40161
- return {
40162
- startElement: selection.anchorNode || this.el,
40163
- startSelectionOffset: selection.anchorOffset,
40164
- endElement: selection.focusNode || this.el,
40165
- endSelectionOffset: selection.focusOffset,
40166
- };
40243
+ return getCurrentSelection(this.el);
40167
40244
  }
40168
40245
  getText() {
40169
40246
  let text = "";
@@ -53118,6 +53195,10 @@ class CellPlugin extends CorePlugin {
53118
53195
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
53119
53196
  case "CLEAR_CELL":
53120
53197
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
53198
+ case "UPDATE_CELL_POSITION":
53199
+ return !cmd.cellId || this.cells[cmd.sheetId]?.[cmd.cellId]
53200
+ ? "Success" /* CommandResult.Success */
53201
+ : "InvalidCellId" /* CommandResult.InvalidCellId */;
53121
53202
  default:
53122
53203
  return "Success" /* CommandResult.Success */;
53123
53204
  }
@@ -53162,6 +53243,9 @@ class CellPlugin extends CorePlugin {
53162
53243
  case "DELETE_CONTENT":
53163
53244
  this.clearZones(cmd.sheetId, cmd.target);
53164
53245
  break;
53246
+ case "DELETE_SHEET": {
53247
+ this.history.update("cells", cmd.sheetId, undefined);
53248
+ }
53165
53249
  }
53166
53250
  }
53167
53251
  clearZones(sheetId, zones) {
@@ -53952,6 +54036,9 @@ class ConditionalFormatPlugin extends CorePlugin {
53952
54036
  allowDispatch(cmd) {
53953
54037
  switch (cmd.type) {
53954
54038
  case "ADD_CONDITIONAL_FORMAT":
54039
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
54040
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54041
+ }
53955
54042
  return this.checkValidations(cmd, this.checkCFRule, this.checkEmptyRange, this.checkCFHasChanged);
53956
54043
  case "CHANGE_CONDITIONAL_FORMAT_PRIORITY":
53957
54044
  return this.checkValidPriorityChange(cmd.cfId, cmd.delta, cmd.sheetId);
@@ -54368,8 +54455,17 @@ class DataValidationPlugin extends CorePlugin {
54368
54455
  allowDispatch(cmd) {
54369
54456
  switch (cmd.type) {
54370
54457
  case "ADD_DATA_VALIDATION_RULE":
54458
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
54459
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54460
+ }
54461
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
54462
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54463
+ }
54371
54464
  return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkValidRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
54372
54465
  case "REMOVE_DATA_VALIDATION_RULE":
54466
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
54467
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54468
+ }
54373
54469
  if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
54374
54470
  return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
54375
54471
  }
@@ -54596,6 +54692,7 @@ class DataValidationPlugin extends CorePlugin {
54596
54692
  class FigurePlugin extends CorePlugin {
54597
54693
  static getters = ["getFigures", "getFigure", "getFigureSheetId"];
54598
54694
  figures = {};
54695
+ insertionOrders = []; // TODO use a list in master
54599
54696
  // ---------------------------------------------------------------------------
54600
54697
  // Command Handling
54601
54698
  // ---------------------------------------------------------------------------
@@ -54698,11 +54795,14 @@ class FigurePlugin extends CorePlugin {
54698
54795
  }
54699
54796
  addFigure(figure, sheetId) {
54700
54797
  this.history.update("figures", sheetId, figure.id, figure);
54798
+ this.history.update("insertionOrders", this.insertionOrders.length, figure.id);
54701
54799
  }
54702
54800
  deleteSheet(sheetId) {
54801
+ this.history.update("insertionOrders", this.insertionOrders.filter((id) => !this.figures[sheetId]?.[id]));
54703
54802
  this.history.update("figures", sheetId, undefined);
54704
54803
  }
54705
54804
  removeFigure(id, sheetId) {
54805
+ this.history.update("insertionOrders", this.insertionOrders.filter((figureId) => figureId !== id));
54706
54806
  this.history.update("figures", sheetId, id, undefined);
54707
54807
  }
54708
54808
  checkFigureExists(sheetId, figureId) {
@@ -54721,7 +54821,14 @@ class FigurePlugin extends CorePlugin {
54721
54821
  // Getters
54722
54822
  // ---------------------------------------------------------------------------
54723
54823
  getFigures(sheetId) {
54724
- return Object.values(this.figures[sheetId] || {}).filter(isDefined);
54824
+ const figures = [];
54825
+ for (const figureId of this.insertionOrders) {
54826
+ const figure = this.figures[sheetId]?.[figureId];
54827
+ if (figure) {
54828
+ figures.push(figure);
54829
+ }
54830
+ }
54831
+ return figures;
54725
54832
  }
54726
54833
  getFigure(sheetId, figureId) {
54727
54834
  return this.figures[sheetId]?.[figureId];
@@ -54734,11 +54841,9 @@ class FigurePlugin extends CorePlugin {
54734
54841
  // ---------------------------------------------------------------------------
54735
54842
  import(data) {
54736
54843
  for (let sheet of data.sheets) {
54737
- const figures = {};
54738
- sheet.figures.forEach((figure) => {
54739
- figures[figure.id] = figure;
54740
- });
54741
- this.figures[sheet.id] = figures;
54844
+ for (const figure of sheet.figures) {
54845
+ this.addFigure(figure, sheet.id);
54846
+ }
54742
54847
  }
54743
54848
  }
54744
54849
  export(data) {
@@ -56164,6 +56269,9 @@ class SheetPlugin extends CorePlugin {
56164
56269
  case "CREATE_SHEET": {
56165
56270
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
56166
56271
  }
56272
+ case "DUPLICATE_SHEET": {
56273
+ return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
56274
+ }
56167
56275
  case "MOVE_SHEET":
56168
56276
  try {
56169
56277
  const currentIndex = this.orderedSheetIds.findIndex((id) => id === cmd.sheetId);
@@ -56975,6 +57083,10 @@ class SheetPlugin extends CorePlugin {
56975
57083
  checkZonesAreInSheet(cmd) {
56976
57084
  if (!("sheetId" in cmd))
56977
57085
  return "Success" /* CommandResult.Success */;
57086
+ if ("ranges" in cmd &&
57087
+ cmd.ranges.some((rangeData) => rangeData._sheetId !== "" && !this.getters.tryGetSheet(rangeData._sheetId))) {
57088
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57089
+ }
56978
57090
  return this.checkZonesExistInSheet(cmd.sheetId, this.getCommandZones(cmd));
56979
57091
  }
56980
57092
  }
@@ -56983,6 +57095,7 @@ let nextTableId = 1;
56983
57095
  class TablePlugin extends CorePlugin {
56984
57096
  static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
56985
57097
  tables = {};
57098
+ insertionOrders = {};
56986
57099
  adaptRanges(applyChange, sheetId) {
56987
57100
  const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
56988
57101
  for (const sheetId of sheetIds) {
@@ -56994,6 +57107,9 @@ class TablePlugin extends CorePlugin {
56994
57107
  allowDispatch(cmd) {
56995
57108
  switch (cmd.type) {
56996
57109
  case "CREATE_TABLE":
57110
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId) || rangeData._sheetId !== cmd.sheetId)) {
57111
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57112
+ }
56997
57113
  const zones = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData).zone);
56998
57114
  if (!areZonesContinuous(zones)) {
56999
57115
  return "NonContinuousTargets" /* CommandResult.NonContinuousTargets */;
@@ -57024,11 +57140,13 @@ class TablePlugin extends CorePlugin {
57024
57140
  switch (cmd.type) {
57025
57141
  case "CREATE_SHEET":
57026
57142
  this.history.update("tables", cmd.sheetId, {});
57143
+ this.history.update("insertionOrders", cmd.sheetId, []);
57027
57144
  break;
57028
57145
  case "DELETE_SHEET": {
57029
57146
  const tables = { ...this.tables };
57030
57147
  delete tables[cmd.sheetId];
57031
57148
  this.history.update("tables", tables);
57149
+ this.history.update("insertionOrders", cmd.sheetId, undefined);
57032
57150
  break;
57033
57151
  }
57034
57152
  case "DUPLICATE_SHEET": {
@@ -57040,6 +57158,9 @@ class TablePlugin extends CorePlugin {
57040
57158
  : this.copyStaticTableForSheet(cmd.sheetIdTo, table);
57041
57159
  }
57042
57160
  this.history.update("tables", cmd.sheetIdTo, newTables);
57161
+ this.history.update("insertionOrders", cmd.sheetIdTo, [
57162
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
57163
+ ]);
57043
57164
  break;
57044
57165
  }
57045
57166
  case "CREATE_TABLE": {
@@ -57053,6 +57174,10 @@ class TablePlugin extends CorePlugin {
57053
57174
  ? this.createDynamicTable(id, union, config)
57054
57175
  : this.createStaticTable(id, cmd.tableType, union, config);
57055
57176
  this.history.update("tables", cmd.sheetId, newTable.id, newTable);
57177
+ this.history.update("insertionOrders", cmd.sheetId, [
57178
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
57179
+ newTable.id,
57180
+ ]);
57056
57181
  break;
57057
57182
  }
57058
57183
  case "REMOVE_TABLE": {
@@ -57063,6 +57188,7 @@ class TablePlugin extends CorePlugin {
57063
57188
  }
57064
57189
  }
57065
57190
  this.history.update("tables", cmd.sheetId, tables);
57191
+ this.history.update("insertionOrders", cmd.sheetId, this.insertionOrders[cmd.sheetId]?.filter((id) => id in tables));
57066
57192
  break;
57067
57193
  }
57068
57194
  case "UPDATE_TABLE": {
@@ -57098,7 +57224,14 @@ class TablePlugin extends CorePlugin {
57098
57224
  }
57099
57225
  }
57100
57226
  getCoreTables(sheetId) {
57101
- return this.tables[sheetId] ? Object.values(this.tables[sheetId]).filter(isDefined) : [];
57227
+ const tables = [];
57228
+ for (const tableId of this.insertionOrders[sheetId] || []) {
57229
+ const table = this.tables[sheetId][tableId];
57230
+ if (table) {
57231
+ tables.push(table);
57232
+ }
57233
+ }
57234
+ return tables;
57102
57235
  }
57103
57236
  getCoreTable({ sheetId, col, row }) {
57104
57237
  return this.getCoreTables(sheetId).find((table) => isInside(col, row, table.range.zone));
@@ -57381,6 +57514,7 @@ class TablePlugin extends CorePlugin {
57381
57514
  // ---------------------------------------------------------------------------
57382
57515
  import(data) {
57383
57516
  for (const sheet of data.sheets) {
57517
+ const tableIds = [];
57384
57518
  for (const tableData of sheet.tables || []) {
57385
57519
  const uuid = `${nextTableId++}`;
57386
57520
  const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
@@ -57390,7 +57524,9 @@ class TablePlugin extends CorePlugin {
57390
57524
  ? this.createDynamicTable(uuid, range, tableConfig)
57391
57525
  : this.createStaticTable(uuid, tableType, range, tableConfig);
57392
57526
  this.history.update("tables", sheet.id, table.id, table);
57527
+ tableIds.push(table.id);
57393
57528
  }
57529
+ this.history.update("insertionOrders", sheet.id, tableIds);
57394
57530
  }
57395
57531
  }
57396
57532
  export(data) {
@@ -57430,7 +57566,10 @@ class HeaderGroupingPlugin extends CorePlugin {
57430
57566
  allowDispatch(cmd) {
57431
57567
  switch (cmd.type) {
57432
57568
  case "GROUP_HEADERS": {
57433
- const { start, end } = cmd;
57569
+ const { start, end, sheetId } = cmd;
57570
+ if (!this.getters.tryGetSheet(sheetId)) {
57571
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57572
+ }
57434
57573
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
57435
57574
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
57436
57575
  }
@@ -57443,7 +57582,10 @@ class HeaderGroupingPlugin extends CorePlugin {
57443
57582
  break;
57444
57583
  }
57445
57584
  case "UNGROUP_HEADERS": {
57446
- const { start, end } = cmd;
57585
+ const { start, end, sheetId } = cmd;
57586
+ if (!this.getters.tryGetSheet(sheetId)) {
57587
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57588
+ }
57447
57589
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
57448
57590
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
57449
57591
  }
@@ -57454,6 +57596,9 @@ class HeaderGroupingPlugin extends CorePlugin {
57454
57596
  }
57455
57597
  case "UNFOLD_HEADER_GROUP":
57456
57598
  case "FOLD_HEADER_GROUP":
57599
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
57600
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57601
+ }
57457
57602
  const group = this.findGroupWithStartEnd(cmd.sheetId, cmd.dimension, cmd.start, cmd.end);
57458
57603
  if (!group) {
57459
57604
  return "UnknownHeaderGroup" /* CommandResult.UnknownHeaderGroup */;
@@ -57854,6 +57999,9 @@ class PivotCorePlugin extends CorePlugin {
57854
57999
  return this.checkDuplicatedMeasureIds(cmd.pivot);
57855
58000
  }
57856
58001
  case "UPDATE_PIVOT": {
58002
+ if (!(cmd.pivotId in this.pivots)) {
58003
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
58004
+ }
57857
58005
  if (deepEquals(cmd.pivot, this.pivots[cmd.pivotId]?.definition)) {
57858
58006
  return "NoChanges" /* CommandResult.NoChanges */;
57859
58007
  }
@@ -57870,6 +58018,8 @@ class PivotCorePlugin extends CorePlugin {
57870
58018
  return "EmptyName" /* CommandResult.EmptyName */;
57871
58019
  }
57872
58020
  break;
58021
+ case "REMOVE_PIVOT":
58022
+ case "DUPLICATE_PIVOT":
57873
58023
  case "INSERT_PIVOT": {
57874
58024
  if (!(cmd.pivotId in this.pivots)) {
57875
58025
  return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
@@ -57919,7 +58069,7 @@ class PivotCorePlugin extends CorePlugin {
57919
58069
  break;
57920
58070
  }
57921
58071
  case "UPDATE_PIVOT": {
57922
- this.history.update("pivots", cmd.pivotId, "definition", cmd.pivot);
58072
+ this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
57923
58073
  this.compileCalculatedMeasures(cmd.pivot.measures);
57924
58074
  break;
57925
58075
  }
@@ -57990,7 +58140,7 @@ class PivotCorePlugin extends CorePlugin {
57990
58140
  // Private
57991
58141
  // -------------------------------------------------------------------------
57992
58142
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
57993
- this.history.update("pivots", pivotId, { definition: pivot, formulaId });
58143
+ this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
57994
58144
  this.compileCalculatedMeasures(pivot.measures);
57995
58145
  this.history.update("formulaIds", formulaId, pivotId);
57996
58146
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
@@ -59576,6 +59726,10 @@ class Evaluator {
59576
59726
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
59577
59727
  this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
59578
59728
  this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
59729
+ this.compilationParams.evalContext.lookupCaches = {
59730
+ forwardSearch: new Map(),
59731
+ reverseSearch: new Map(),
59732
+ };
59579
59733
  }
59580
59734
  createEmptyPositionSet() {
59581
59735
  const sheetSizes = {};
@@ -62896,6 +63050,9 @@ function updateChartRangesTransformation(toTransform, executed) {
62896
63050
  };
62897
63051
  }
62898
63052
  function createSheetTransformation(toTransform, executed) {
63053
+ if (toTransform.sheetId === executed.sheetId) {
63054
+ toTransform = { ...toTransform, sheetId: `${toTransform.sheetId}~` };
63055
+ }
62899
63056
  if (toTransform.name === executed.name) {
62900
63057
  return {
62901
63058
  ...toTransform,
@@ -63539,15 +63696,6 @@ class Session extends EventBus {
63539
63696
  }
63540
63697
  this.sendPendingMessage();
63541
63698
  }
63542
- dropPendingRevision(revisionId) {
63543
- this.revisions.drop(revisionId);
63544
- const revisionIds = this.pendingMessages
63545
- .filter((message) => message.type === "REMOTE_REVISION")
63546
- .map((message) => message.nextRevisionId);
63547
- this.trigger("pending-revisions-dropped", { revisionIds });
63548
- this.waitingAck = false;
63549
- this.waitingUndoRedoAck = false;
63550
- }
63551
63699
  /**
63552
63700
  * Send the next pending message
63553
63701
  */
@@ -63556,15 +63704,14 @@ class Session extends EventBus {
63556
63704
  if (!message)
63557
63705
  return;
63558
63706
  if (message.type === "REMOTE_REVISION") {
63559
- const revision = this.revisions.get(message.nextRevisionId);
63707
+ let revision = this.revisions.get(message.nextRevisionId);
63560
63708
  if (revision.commands.length === 0) {
63561
63709
  /**
63562
- * The command is empty, we have to drop all the next local revisions
63710
+ * The command is empty, we have to rebase all the next local revisions
63563
63711
  * to avoid issues with undo/redo
63564
63712
  */
63565
- this.dropPendingRevision(revision.id);
63566
- this.pendingMessages = [];
63567
- return;
63713
+ this.revisions.rebase(revision.id);
63714
+ revision = this.revisions.get(message.nextRevisionId);
63568
63715
  }
63569
63716
  message = {
63570
63717
  ...message,
@@ -63600,18 +63747,16 @@ class Session extends EventBus {
63600
63747
  case "REVISION_UNDONE": {
63601
63748
  this.waitingAck = false;
63602
63749
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63603
- const pendingRemoteRevisions = this.pendingMessages.filter((message) => message.type === "REMOTE_REVISION");
63604
- const firstTransformedRevisionIndex = pendingRemoteRevisions.findIndex((message) => !deepEquals(message.commands, this.revisions.get(message.nextRevisionId).commands));
63605
- if (firstTransformedRevisionIndex !== -1) {
63750
+ const firstPendingRevisionId = this.pendingMessages.findIndex((message) => message.type === "REMOTE_REVISION");
63751
+ if (firstPendingRevisionId !== -1) {
63606
63752
  /**
63607
63753
  * Some revisions undergo transformations that may cause issues with
63608
63754
  * undo/redo if the transformation is destructive (we don't get back
63609
63755
  * the original command by transforming it with the inverse).
63610
- * To prevent these problems, we must discard all subsequent local
63756
+ * To prevent these problems, we must rebase all subsequent local
63611
63757
  * revisions.
63612
63758
  */
63613
- this.dropPendingRevision(this.pendingMessages[firstTransformedRevisionIndex].nextRevisionId);
63614
- this.pendingMessages = this.pendingMessages.slice(0, firstTransformedRevisionIndex);
63759
+ this.revisions.rebase(this.pendingMessages[firstPendingRevisionId].nextRevisionId);
63615
63760
  }
63616
63761
  this.serverRevisionId = message.nextRevisionId;
63617
63762
  this.processedRevisions.add(message.nextRevisionId);
@@ -64739,6 +64884,10 @@ class SheetUIPlugin extends UIPlugin {
64739
64884
  */
64740
64885
  checkZonesAreInSheet(cmd) {
64741
64886
  const sheetId = "sheetId" in cmd ? cmd.sheetId : this.getters.tryGetActiveSheetId();
64887
+ if ("ranges" in cmd &&
64888
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
64889
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
64890
+ }
64742
64891
  const zones = this.getters.getCommandZones(cmd);
64743
64892
  if (!sheetId && zones.length > 0) {
64744
64893
  return "NoActiveSheet" /* CommandResult.NoActiveSheet */;
@@ -65293,7 +65442,6 @@ class HistoryPlugin extends UIPlugin {
65293
65442
  super(config);
65294
65443
  this.session = config.session;
65295
65444
  this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
65296
- this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
65297
65445
  this.session.on("snapshot", this, () => {
65298
65446
  this.undoStack = [];
65299
65447
  this.redoStack = [];
@@ -65363,10 +65511,6 @@ class HistoryPlugin extends UIPlugin {
65363
65511
  const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
65364
65512
  return canRepeatRevision(lastNonRedoRevision);
65365
65513
  }
65366
- drop(revisionIds) {
65367
- this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
65368
- this.redoStack = [];
65369
- }
65370
65514
  onNewLocalStateUpdate({ id }) {
65371
65515
  this.undoStack.push(id);
65372
65516
  this.redoStack = [];
@@ -68070,7 +68214,9 @@ class HeaderPositionsUIPlugin extends UIPlugin {
68070
68214
  case "UNGROUP_HEADERS":
68071
68215
  case "GROUP_HEADERS":
68072
68216
  case "CREATE_SHEET":
68073
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
68217
+ if (this.getters.tryGetSheet(cmd.sheetId)) {
68218
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
68219
+ }
68074
68220
  break;
68075
68221
  case "DUPLICATE_SHEET":
68076
68222
  this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
@@ -68078,12 +68224,14 @@ class HeaderPositionsUIPlugin extends UIPlugin {
68078
68224
  }
68079
68225
  }
68080
68226
  finalize() {
68081
- if (this.isDirty) {
68082
- for (const sheetId of this.getters.getSheetIds()) {
68227
+ for (const sheetId of this.getters.getSheetIds()) {
68228
+ // sheets can be created without this plugin being aware of it
68229
+ // in concurrent situations.
68230
+ if (this.isDirty || !this.headerPositions[sheetId]) {
68083
68231
  this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
68084
68232
  }
68085
- this.isDirty = false;
68086
68233
  }
68234
+ this.isDirty = false;
68087
68235
  }
68088
68236
  /**
68089
68237
  * Returns the size, start and end coordinates of a column on an unfolded sheet
@@ -71504,9 +71652,16 @@ class SelectiveHistory {
71504
71652
  this.fastForward();
71505
71653
  this.insert(redoId, this.buildEmpty(redoId), insertAfter);
71506
71654
  }
71507
- drop(operationId) {
71655
+ rebase(operationId) {
71656
+ const operation = this.get(operationId);
71657
+ const execution = [...this.tree.execution(this.HEAD_BRANCH).startAfter(operationId)];
71508
71658
  this.revertBefore(operationId);
71659
+ const baseId = this.HEAD_OPERATION.id;
71509
71660
  this.tree.drop(operationId);
71661
+ this.insert(operationId, operation, baseId);
71662
+ for (const { operation } of execution) {
71663
+ this.insert(operation.id, operation.data, this.HEAD_OPERATION.id);
71664
+ }
71510
71665
  }
71511
71666
  /**
71512
71667
  * Revert the state as it was *before* the given operation was executed.
@@ -74617,6 +74772,11 @@ class Model extends EventBus {
74617
74772
  dispatch: (command) => {
74618
74773
  const result = this.checkDispatchAllowed(command);
74619
74774
  if (!result.isSuccessful) {
74775
+ // core views plugins need to be invalidated
74776
+ this.dispatchToHandlers(this.coreHandlers, {
74777
+ type: "UNDO",
74778
+ commands: [command],
74779
+ });
74620
74780
  return;
74621
74781
  }
74622
74782
  this.isReplayingCommand = true;
@@ -75167,6 +75327,6 @@ exports.tokenColors = tokenColors;
75167
75327
  exports.tokenize = tokenize;
75168
75328
 
75169
75329
 
75170
- __info__.version = "18.1.8";
75171
- __info__.date = "2025-02-14T08:42:08.322Z";
75172
- __info__.hash = "02682f4";
75330
+ __info__.version = "18.1.9";
75331
+ __info__.date = "2025-02-25T05:59:45.472Z";
75332
+ __info__.hash = "6789c1c";