@odoo/o-spreadsheet 18.1.7 → 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.7
6
- * @date 2025-02-10T09:00:28.556Z
7
- * @hash 338d8a1
5
+ * @version 18.1.9
6
+ * @date 2025-02-25T05:59:45.472Z
7
+ * @hash 6789c1c
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -2219,17 +2219,7 @@
2219
2219
  */
2220
2220
  function toUnboundedZone(xc) {
2221
2221
  const zone = toZoneWithoutBoundaryChanges(xc);
2222
- if (zone.right !== undefined && zone.right < zone.left) {
2223
- const tmp = zone.left;
2224
- zone.left = zone.right;
2225
- zone.right = tmp;
2226
- }
2227
- if (zone.bottom !== undefined && zone.bottom < zone.top) {
2228
- const tmp = zone.top;
2229
- zone.top = zone.bottom;
2230
- zone.bottom = tmp;
2231
- }
2232
- return zone;
2222
+ return reorderZone(zone);
2233
2223
  }
2234
2224
  /**
2235
2225
  * Convert from a cartesian reference to a Zone.
@@ -2503,11 +2493,11 @@
2503
2493
  return positions;
2504
2494
  }
2505
2495
  function reorderZone(zone) {
2506
- if (zone.left > zone.right) {
2507
- zone = { left: zone.right, right: zone.left, top: zone.top, bottom: zone.bottom };
2496
+ if (zone.right !== undefined && zone.left > zone.right) {
2497
+ zone = { ...zone, left: zone.right, right: zone.left };
2508
2498
  }
2509
- if (zone.top > zone.bottom) {
2510
- zone = { left: zone.left, right: zone.right, top: zone.bottom, bottom: zone.top };
2499
+ if (zone.bottom !== undefined && zone.top > zone.bottom) {
2500
+ zone = { ...zone, top: zone.bottom, bottom: zone.top };
2511
2501
  }
2512
2502
  return zone;
2513
2503
  }
@@ -3412,12 +3402,12 @@
3412
3402
  function isRangeDependant(cmd) {
3413
3403
  return "ranges" in cmd;
3414
3404
  }
3415
- function isZoneDependent(cmd) {
3416
- return "zone" in cmd;
3417
- }
3418
3405
  function isPositionDependent(cmd) {
3419
3406
  return "col" in cmd && "row" in cmd && "sheetId" in cmd;
3420
3407
  }
3408
+ function isZoneDependent(cmd) {
3409
+ return "sheetId" in cmd && "zone" in cmd;
3410
+ }
3421
3411
  const invalidateEvaluationCommands = new Set([
3422
3412
  "RENAME_SHEET",
3423
3413
  "DELETE_SHEET",
@@ -3429,6 +3419,7 @@
3429
3419
  "REDO",
3430
3420
  "ADD_MERGE",
3431
3421
  "REMOVE_MERGE",
3422
+ "DUPLICATE_SHEET",
3432
3423
  "UPDATE_LOCALE",
3433
3424
  "ADD_PIVOT",
3434
3425
  "UPDATE_PIVOT",
@@ -3458,7 +3449,6 @@
3458
3449
  ]);
3459
3450
  const invalidateDependenciesCommands = new Set(["MOVE_RANGES"]);
3460
3451
  const invalidateCFEvaluationCommands = new Set([
3461
- "DUPLICATE_SHEET",
3462
3452
  "EVALUATE_CELLS",
3463
3453
  "ADD_CONDITIONAL_FORMAT",
3464
3454
  "REMOVE_CONDITIONAL_FORMAT",
@@ -3628,6 +3618,7 @@
3628
3618
  CommandResult["InvalidRange"] = "InvalidRange";
3629
3619
  CommandResult["InvalidZones"] = "InvalidZones";
3630
3620
  CommandResult["InvalidSheetId"] = "InvalidSheetId";
3621
+ CommandResult["InvalidCellId"] = "InvalidCellId";
3631
3622
  CommandResult["InvalidFigureId"] = "InvalidFigureId";
3632
3623
  CommandResult["InputAlreadyFocused"] = "InputAlreadyFocused";
3633
3624
  CommandResult["MaximumRangesReached"] = "MaximumRangesReached";
@@ -4459,7 +4450,7 @@
4459
4450
  * @param reverseSearch if true, search in the array starting from the end.
4460
4451
 
4461
4452
  */
4462
- function linearSearch(data, target, mode, numberOfValues, getValueInData, reverseSearch = false) {
4453
+ function linearSearch(data, target, mode, numberOfValues, getValueInData, lookupCaches, reverseSearch = false) {
4463
4454
  if (target === undefined || target.value === null) {
4464
4455
  return -1;
4465
4456
  }
@@ -4468,17 +4459,48 @@
4468
4459
  }
4469
4460
  const _target = normalizeValue(target.value);
4470
4461
  const getValue = reverseSearch
4471
- ? (data, i) => getValueInData(data, numberOfValues - i - 1)
4472
- : getValueInData;
4462
+ ? (data, i) => normalizeValue(getValueInData(data, numberOfValues - i - 1))
4463
+ : (data, i) => normalizeValue(getValueInData(data, i));
4464
+ // first check if the target is in the cache
4465
+ const isNotWildcardTarget = mode !== "wildcard" ||
4466
+ typeof _target !== "string" ||
4467
+ !(_target.includes("*") || _target.includes("?"));
4468
+ if (lookupCaches && isNotWildcardTarget) {
4469
+ const searchMode = reverseSearch ? "reverseSearch" : "forwardSearch";
4470
+ let cache = lookupCaches[searchMode].get(data);
4471
+ if (cache === undefined) {
4472
+ // build the cache for all the values
4473
+ cache = new Map();
4474
+ for (let i = 0; i < numberOfValues; i++) {
4475
+ const value = getValue(data, i) ?? null;
4476
+ if (!cache.has(value)) {
4477
+ cache.set(value, i);
4478
+ }
4479
+ }
4480
+ lookupCaches[searchMode].set(data, cache);
4481
+ }
4482
+ if (cache.has(_target)) {
4483
+ const resultIndex = cache.get(_target);
4484
+ return reverseSearch ? numberOfValues - resultIndex - 1 : resultIndex;
4485
+ }
4486
+ if (mode === "strict") {
4487
+ return -1;
4488
+ }
4489
+ }
4490
+ // else perform the linear search
4491
+ const resultIndex = _linearSearch(data, _target, mode, numberOfValues, getValue);
4492
+ return reverseSearch && resultIndex !== -1 ? numberOfValues - resultIndex - 1 : resultIndex;
4493
+ }
4494
+ function _linearSearch(data, _target, mode, numberOfValues, getNormalizeValue) {
4473
4495
  let indexMatchTarget = (i) => {
4474
- return normalizeValue(getValue(data, i)) === _target;
4496
+ return getNormalizeValue(data, i) === _target;
4475
4497
  };
4476
4498
  if (mode === "wildcard" &&
4477
4499
  typeof _target === "string" &&
4478
4500
  (_target.includes("*") || _target.includes("?"))) {
4479
4501
  const regExp = wildcardToRegExp(_target);
4480
4502
  indexMatchTarget = (i) => {
4481
- const value = normalizeValue(getValue(data, i));
4503
+ const value = getNormalizeValue(data, i);
4482
4504
  if (typeof value === "string") {
4483
4505
  return regExp.test(value);
4484
4506
  }
@@ -4489,7 +4511,7 @@
4489
4511
  let closestMatchIndex = -1;
4490
4512
  if (mode === "nextSmaller") {
4491
4513
  indexMatchTarget = (i) => {
4492
- const value = normalizeValue(getValue(data, i));
4514
+ const value = getNormalizeValue(data, i);
4493
4515
  if ((!closestMatch && compareCellValues(_target, value) >= 0) ||
4494
4516
  (compareCellValues(_target, value) >= 0 && compareCellValues(value, closestMatch) > 0)) {
4495
4517
  closestMatch = value;
@@ -4500,7 +4522,7 @@
4500
4522
  }
4501
4523
  if (mode === "nextGreater") {
4502
4524
  indexMatchTarget = (i) => {
4503
- const value = normalizeValue(getValue(data, i));
4525
+ const value = getNormalizeValue(data, i);
4504
4526
  if ((!closestMatch && compareCellValues(_target, value) <= 0) ||
4505
4527
  (compareCellValues(_target, value) <= 0 && compareCellValues(value, closestMatch) < 0)) {
4506
4528
  closestMatch = value;
@@ -4511,12 +4533,10 @@
4511
4533
  }
4512
4534
  for (let i = 0; i < numberOfValues; i++) {
4513
4535
  if (indexMatchTarget(i)) {
4514
- return reverseSearch ? numberOfValues - i - 1 : i;
4536
+ return i;
4515
4537
  }
4516
4538
  }
4517
- return reverseSearch && closestMatchIndex !== -1
4518
- ? numberOfValues - closestMatchIndex - 1
4519
- : closestMatchIndex;
4539
+ return closestMatchIndex;
4520
4540
  }
4521
4541
  /**
4522
4542
  * Normalize a value.
@@ -6480,11 +6500,40 @@
6480
6500
  * https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
6481
6501
  * */
6482
6502
  class UuidGenerator {
6503
+ /**
6504
+ * Generates a custom UUID using a simple 36^12 method (8-character alphanumeric string with lowercase letters)
6505
+ * This has a higher chance of collision than a UUIDv4, but not only faster to generate than an UUIDV4,
6506
+ * it also has a smaller size, which is preferable to alleviate the overall data size.
6507
+ *
6508
+ * This method is preferable when generating uuids for the core data (sheetId, figureId, etc)
6509
+ * as they will appear several times in the revisions and local history.
6510
+ *
6511
+ */
6512
+ smallUuid() {
6513
+ if (window.crypto) {
6514
+ return "10000000-1000".replace(/[01]/g, (c) => {
6515
+ const n = Number(c);
6516
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6517
+ });
6518
+ }
6519
+ else {
6520
+ // mainly for jest and other browsers that do not have the crypto functionality
6521
+ return "xxxxxxxx-xxxx".replace(/[xy]/g, function (c) {
6522
+ const r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
6523
+ return v.toString(16);
6524
+ });
6525
+ }
6526
+ }
6527
+ /**
6528
+ * Generates an UUIDV4, has astronomically low chance of collision, but is larger in size than the smallUuid.
6529
+ * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
6530
+ */
6483
6531
  uuidv4() {
6484
- //@ts-ignore
6485
- if (window.crypto && window.crypto.getRandomValues) {
6486
- //@ts-ignore
6487
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6532
+ if (window.crypto) {
6533
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
6534
+ const n = Number(c);
6535
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6536
+ });
6488
6537
  }
6489
6538
  else {
6490
6539
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -8514,7 +8563,7 @@
8514
8563
  };
8515
8564
  }
8516
8565
  getPasteTarget(sheetId, target, content, options) {
8517
- const newId = new UuidGenerator().uuidv4();
8566
+ const newId = new UuidGenerator().smallUuid();
8518
8567
  return { zones: [], figureId: newId, sheetId };
8519
8568
  }
8520
8569
  paste(target, clippedContent, options) {
@@ -8680,7 +8729,7 @@
8680
8729
  if (!targetCF && queuedCfs) {
8681
8730
  targetCF = queuedCfs.find((queued) => queued.cf.stopIfTrue === originCF.stopIfTrue && deepEquals(queued.cf.rule, originCF.rule))?.cf;
8682
8731
  }
8683
- return targetCF || { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
8732
+ return targetCF || { ...originCF, id: this.uuidGenerator.smallUuid(), ranges: [] };
8684
8733
  }
8685
8734
  }
8686
8735
 
@@ -8773,7 +8822,7 @@
8773
8822
  }
8774
8823
  return (targetRule || {
8775
8824
  ...originRule,
8776
- id: newId ? this.uuidGenerator.uuidv4() : originRule.id,
8825
+ id: newId ? this.uuidGenerator.smallUuid() : originRule.id,
8777
8826
  ranges: [],
8778
8827
  });
8779
8828
  }
@@ -8835,7 +8884,7 @@
8835
8884
  };
8836
8885
  }
8837
8886
  getPasteTarget(sheetId, target, content, options) {
8838
- const newId = new UuidGenerator().uuidv4();
8887
+ const newId = new UuidGenerator().smallUuid();
8839
8888
  return { sheetId, zones: [], figureId: newId };
8840
8889
  }
8841
8890
  paste(target, clippedContent, options) {
@@ -12037,6 +12086,25 @@ stores.inject(MyMetaStore, storeInstance);
12037
12086
  isExported: true,
12038
12087
  };
12039
12088
  // -----------------------------------------------------------------------------
12089
+ // LOG
12090
+ // -----------------------------------------------------------------------------
12091
+ const LOG = {
12092
+ description: _t("The logarithm of a number, for a given base."),
12093
+ args: [
12094
+ arg("value (number)", _t("The value for which to calculate the logarithm.")),
12095
+ arg("base (number, default=10)", _t("The base of the logarithm.")),
12096
+ ],
12097
+ compute: function (value, base = { value: 10 }) {
12098
+ const _value = toNumber(value, this.locale);
12099
+ const _base = toNumber(base, this.locale);
12100
+ assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
12101
+ assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
12102
+ assert(() => _base !== 1, _t("The base must be different from 1."));
12103
+ return Math.log10(_value) / Math.log10(_base);
12104
+ },
12105
+ isExported: true,
12106
+ };
12107
+ // -----------------------------------------------------------------------------
12040
12108
  // MOD
12041
12109
  // -----------------------------------------------------------------------------
12042
12110
  function mod(dividend, divisor) {
@@ -12576,6 +12644,7 @@ stores.inject(MyMetaStore, storeInstance);
12576
12644
  ISODD: ISODD,
12577
12645
  ISO_CEILING: ISO_CEILING,
12578
12646
  LN: LN,
12647
+ LOG: LOG,
12579
12648
  MOD: MOD,
12580
12649
  MUNIT: MUNIT,
12581
12650
  ODD: ODD,
@@ -15110,7 +15179,7 @@ stores.inject(MyMetaStore, storeInstance);
15110
15179
  }
15111
15180
  }
15112
15181
  },
15113
- isExported: true,
15182
+ isExported: false,
15114
15183
  };
15115
15184
  // -----------------------------------------------------------------------------
15116
15185
  // UNIQUE
@@ -18474,7 +18543,7 @@ stores.inject(MyMetaStore, storeInstance);
18474
18543
  const _isSorted = toBoolean(isSorted.value);
18475
18544
  const colIndex = _isSorted
18476
18545
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18477
- : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18546
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches);
18478
18547
  const col = _range[colIndex];
18479
18548
  if (col === undefined) {
18480
18549
  return valueNotAvailable(searchKey);
@@ -18629,7 +18698,7 @@ stores.inject(MyMetaStore, storeInstance);
18629
18698
  index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18630
18699
  break;
18631
18700
  case 0:
18632
- index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18701
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18633
18702
  break;
18634
18703
  case -1:
18635
18704
  index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
@@ -18697,7 +18766,7 @@ stores.inject(MyMetaStore, storeInstance);
18697
18766
  const _isSorted = toBoolean(isSorted.value);
18698
18767
  const rowIndex = _isSorted
18699
18768
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18700
- : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18769
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches);
18701
18770
  const value = _range[_index - 1][rowIndex];
18702
18771
  if (value === undefined) {
18703
18772
  return valueNotAvailable(searchKey);
@@ -18753,7 +18822,7 @@ stores.inject(MyMetaStore, storeInstance);
18753
18822
  const reverseSearch = _searchMode === -1;
18754
18823
  const index = _searchMode === 2 || _searchMode === -2
18755
18824
  ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18756
- : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18825
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18757
18826
  if (index !== -1) {
18758
18827
  return lookupDirection === "col"
18759
18828
  ? _returnRange.map((col) => [col[index]])
@@ -27623,7 +27692,7 @@ stores.inject(MyMetaStore, storeInstance);
27623
27692
  for (const sheet of data.sheets || []) {
27624
27693
  for (const figure of sheet.figures || []) {
27625
27694
  if (figureIds.has(figure.id)) {
27626
- figure.id += uuidGenerator.uuidv4();
27695
+ figure.id += uuidGenerator.smallUuid();
27627
27696
  }
27628
27697
  figureIds.add(figure.id);
27629
27698
  }
@@ -28208,9 +28277,7 @@ stores.inject(MyMetaStore, storeInstance);
28208
28277
  const labelValues = getChartLabelValues(getters, dataSets, labelRange);
28209
28278
  let labels = labelValues.formattedValues;
28210
28279
  let dataSetsValues = getChartDatasetValues(getters, dataSets);
28211
- if (definition.dataSetsHaveTitle &&
28212
- dataSetsValues[0] &&
28213
- labels.length > dataSetsValues[0].data.length) {
28280
+ if (shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false)) {
28214
28281
  labels.shift();
28215
28282
  }
28216
28283
  ({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
@@ -28243,7 +28310,7 @@ stores.inject(MyMetaStore, storeInstance);
28243
28310
  }
28244
28311
  function getPyramidChartData(definition, dataSets, labelRange, getters) {
28245
28312
  const barChartData = getBarChartData(definition, dataSets, labelRange, getters);
28246
- const barDataset = barChartData.dataSetsValues;
28313
+ const barDataset = barChartData.dataSetsValues.filter((ds) => !ds.hidden);
28247
28314
  const pyramidDatasetValues = [];
28248
28315
  if (barDataset[0]) {
28249
28316
  const pyramidData = barDataset[0].data.map((value) => (value > 0 ? value : 0));
@@ -28259,13 +28326,12 @@ stores.inject(MyMetaStore, storeInstance);
28259
28326
  };
28260
28327
  }
28261
28328
  function getLineChartData(definition, dataSets, labelRange, getters) {
28262
- const axisType = getChartAxisType(definition, labelRange, getters);
28329
+ const axisType = getChartAxisType(definition, dataSets, labelRange, getters);
28263
28330
  const labelValues = getChartLabelValues(getters, dataSets, labelRange);
28264
28331
  let labels = axisType === "linear" ? labelValues.values : labelValues.formattedValues;
28265
28332
  let dataSetsValues = getChartDatasetValues(getters, dataSets);
28266
- if (definition.dataSetsHaveTitle &&
28267
- dataSetsValues[0] &&
28268
- labels.length > dataSetsValues[0].data.length) {
28333
+ const removeFirstLabel = shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false);
28334
+ if (removeFirstLabel) {
28269
28335
  labels.shift();
28270
28336
  }
28271
28337
  ({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
@@ -28277,7 +28343,7 @@ stores.inject(MyMetaStore, storeInstance);
28277
28343
  }
28278
28344
  const leftAxisFormat = getChartDatasetFormat(getters, dataSets, "left");
28279
28345
  const rightAxisFormat = getChartDatasetFormat(getters, dataSets, "right");
28280
- const labelsFormat = getChartLabelFormat(getters, labelRange);
28346
+ const labelsFormat = getChartLabelFormat(getters, labelRange, removeFirstLabel);
28281
28347
  const axisFormats = { y: leftAxisFormat, y1: rightAxisFormat, x: labelsFormat };
28282
28348
  const trendDataSetsValues = [];
28283
28349
  for (const index in dataSetsValues) {
@@ -28313,9 +28379,7 @@ stores.inject(MyMetaStore, storeInstance);
28313
28379
  const labelValues = getChartLabelValues(getters, dataSets, labelRange);
28314
28380
  let labels = labelValues.formattedValues;
28315
28381
  let dataSetsValues = getChartDatasetValues(getters, dataSets);
28316
- if (definition.dataSetsHaveTitle &&
28317
- dataSetsValues[0] &&
28318
- labels.length > dataSetsValues[0].data.length) {
28382
+ if (shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false)) {
28319
28383
  labels.shift();
28320
28384
  }
28321
28385
  ({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
@@ -28335,9 +28399,7 @@ stores.inject(MyMetaStore, storeInstance);
28335
28399
  const labelValues = getChartLabelValues(getters, dataSets, labelRange);
28336
28400
  let labels = labelValues.formattedValues;
28337
28401
  let dataSetsValues = getChartDatasetValues(getters, dataSets);
28338
- if (definition.dataSetsHaveTitle &&
28339
- dataSetsValues[0] &&
28340
- labels.length > dataSetsValues[0].data.length) {
28402
+ if (shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false)) {
28341
28403
  labels.shift();
28342
28404
  }
28343
28405
  ({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
@@ -28357,7 +28419,7 @@ stores.inject(MyMetaStore, storeInstance);
28357
28419
  function getGeoChartData(definition, dataSets, labelRange, getters) {
28358
28420
  const labelValues = getChartLabelValues(getters, dataSets, labelRange);
28359
28421
  let labels = labelValues.formattedValues;
28360
- if (definition.dataSetsHaveTitle) {
28422
+ if (shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false)) {
28361
28423
  labels.shift();
28362
28424
  }
28363
28425
  let dataSetsValues = getChartDatasetValues(getters, dataSets);
@@ -28518,36 +28580,41 @@ stores.inject(MyMetaStore, storeInstance);
28518
28580
  }
28519
28581
  return { normalizedLabels, normalizedNewLabels };
28520
28582
  }
28521
- function getChartAxisType(chart, labelRange, getters) {
28522
- if (isDateChart(chart, labelRange, getters) && isLuxonTimeAdapterInstalled()) {
28583
+ function getChartAxisType(definition, dataSets, labelRange, getters) {
28584
+ if (isDateChart(definition, dataSets, labelRange, getters) && isLuxonTimeAdapterInstalled()) {
28523
28585
  return "time";
28524
28586
  }
28525
- if (isLinearChart(chart, labelRange, getters)) {
28587
+ if (isLinearChart(definition, dataSets, labelRange, getters)) {
28526
28588
  return "linear";
28527
28589
  }
28528
28590
  return "category";
28529
28591
  }
28530
- function isDateChart(definition, labelRange, getters) {
28531
- return !definition.labelsAsText && canBeDateChart(labelRange, getters);
28592
+ function isDateChart(definition, dataSets, labelRange, getters) {
28593
+ return !definition.labelsAsText && canBeDateChart(definition, dataSets, labelRange, getters);
28532
28594
  }
28533
- function isLinearChart(definition, labelRange, getters) {
28534
- return !definition.labelsAsText && canBeLinearChart(labelRange, getters);
28595
+ function isLinearChart(definition, dataSets, labelRange, getters) {
28596
+ return !definition.labelsAsText && canBeLinearChart(definition, dataSets, labelRange, getters);
28535
28597
  }
28536
- function canChartParseLabels(labelRange, getters) {
28537
- return canBeDateChart(labelRange, getters) || canBeLinearChart(labelRange, getters);
28598
+ function canChartParseLabels(definition, dataSets, labelRange, getters) {
28599
+ return (canBeDateChart(definition, dataSets, labelRange, getters) ||
28600
+ canBeLinearChart(definition, dataSets, labelRange, getters));
28538
28601
  }
28539
- function canBeDateChart(labelRange, getters) {
28540
- if (!labelRange || !canBeLinearChart(labelRange, getters)) {
28602
+ function canBeDateChart(definition, dataSets, labelRange, getters) {
28603
+ if (!labelRange || !canBeLinearChart(definition, dataSets, labelRange, getters)) {
28541
28604
  return false;
28542
28605
  }
28543
- const labelFormat = getChartLabelFormat(getters, labelRange);
28606
+ const removeFirstLabel = shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false);
28607
+ const labelFormat = getChartLabelFormat(getters, labelRange, removeFirstLabel);
28544
28608
  return Boolean(labelFormat && timeFormatLuxonCompatible.test(labelFormat));
28545
28609
  }
28546
- function canBeLinearChart(labelRange, getters) {
28610
+ function canBeLinearChart(definition, dataSets, labelRange, getters) {
28547
28611
  if (!labelRange) {
28548
28612
  return false;
28549
28613
  }
28550
28614
  const labels = getters.getRangeValues(labelRange);
28615
+ if (shouldRemoveFirstLabel(labelRange, dataSets[0], definition.dataSetsHaveTitle || false)) {
28616
+ labels.shift();
28617
+ }
28551
28618
  if (labels.some((label) => isNaN(Number(label)) && label)) {
28552
28619
  return false;
28553
28620
  }
@@ -28656,17 +28723,15 @@ stores.inject(MyMetaStore, storeInstance);
28656
28723
  })),
28657
28724
  };
28658
28725
  }
28659
- function getChartLabelFormat(getters, range) {
28726
+ function getChartLabelFormat(getters, range, shouldRemoveFirstLabel) {
28660
28727
  if (!range)
28661
28728
  return undefined;
28662
- const { sheetId, zone: { left, top, bottom }, } = range;
28663
- for (let row = top; row <= bottom; row++) {
28664
- const format = getters.getEvaluatedCell({ sheetId, col: left, row }).format;
28665
- if (format) {
28666
- return format;
28667
- }
28729
+ const { sheetId, zone } = range;
28730
+ const formats = positions(zone).map((position) => getters.getEvaluatedCell({ sheetId, ...position }).format);
28731
+ if (shouldRemoveFirstLabel) {
28732
+ formats.shift();
28668
28733
  }
28669
- return undefined;
28734
+ return formats.find((format) => format !== undefined);
28670
28735
  }
28671
28736
  function getChartLabelValues(getters, dataSets, labelRange) {
28672
28737
  let labels = { values: [], formattedValues: [] };
@@ -28722,10 +28787,8 @@ stores.inject(MyMetaStore, storeInstance);
28722
28787
  function getChartDatasetValues(getters, dataSets) {
28723
28788
  const datasetValues = [];
28724
28789
  for (const [dsIndex, ds] of Object.entries(dataSets)) {
28725
- if (getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left)) {
28726
- continue;
28727
- }
28728
28790
  let label;
28791
+ let hidden = getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left);
28729
28792
  if (ds.labelCell) {
28730
28793
  const labelRange = ds.labelCell;
28731
28794
  const cell = labelRange
@@ -28752,9 +28815,9 @@ stores.inject(MyMetaStore, storeInstance);
28752
28815
  data.fill(1);
28753
28816
  }
28754
28817
  else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28755
- continue;
28818
+ hidden = true;
28756
28819
  }
28757
- datasetValues.push({ data, label });
28820
+ datasetValues.push({ data, label, hidden });
28758
28821
  }
28759
28822
  return datasetValues;
28760
28823
  }
@@ -28765,12 +28828,13 @@ stores.inject(MyMetaStore, storeInstance);
28765
28828
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28766
28829
  const trendDatasets = [];
28767
28830
  for (const index in dataSetsValues) {
28768
- let { label, data } = dataSetsValues[index];
28831
+ let { label, data, hidden } = dataSetsValues[index];
28769
28832
  label = definition.dataSets?.[index].label || label;
28770
28833
  const backgroundColor = colors.next();
28771
28834
  const dataset = {
28772
28835
  label,
28773
28836
  data,
28837
+ hidden,
28774
28838
  borderColor: definition.background || BACKGROUND_CHART_COLOR,
28775
28839
  borderWidth: definition.stacked ? 1 : 0,
28776
28840
  backgroundColor,
@@ -28803,6 +28867,9 @@ stores.inject(MyMetaStore, storeInstance);
28803
28867
  const labelsWithSubTotals = [];
28804
28868
  let lastValue = 0;
28805
28869
  for (const dataSetsValue of dataSetsValues) {
28870
+ if (dataSetsValue.hidden) {
28871
+ continue;
28872
+ }
28806
28873
  for (let i = 0; i < dataSetsValue.data.length; i++) {
28807
28874
  const data = dataSetsValue.data[i];
28808
28875
  labelsWithSubTotals.push(labels[i]);
@@ -28838,7 +28905,7 @@ stores.inject(MyMetaStore, storeInstance);
28838
28905
  const trendDatasets = [];
28839
28906
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28840
28907
  for (let index = 0; index < dataSetsValues.length; index++) {
28841
- let { label, data } = dataSetsValues[index];
28908
+ let { label, data, hidden } = dataSetsValues[index];
28842
28909
  label = definition.dataSets?.[index].label || label;
28843
28910
  const color = colors.next();
28844
28911
  if (axisType && ["linear", "time"].includes(axisType)) {
@@ -28848,6 +28915,7 @@ stores.inject(MyMetaStore, storeInstance);
28848
28915
  const dataset = {
28849
28916
  label,
28850
28917
  data,
28918
+ hidden,
28851
28919
  tension: 0, // 0 -> render straight lines, which is much faster
28852
28920
  borderColor: color,
28853
28921
  backgroundColor: areaChart ? setColorAlpha(color, LINE_FILL_TRANSPARENCY) : color,
@@ -28880,11 +28948,13 @@ stores.inject(MyMetaStore, storeInstance);
28880
28948
  const dataSets = [];
28881
28949
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
28882
28950
  const backgroundColor = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
28883
- for (const { label, data } of dataSetsValues) {
28951
+ for (const { label, data, hidden } of dataSetsValues) {
28952
+ if (hidden)
28953
+ continue;
28884
28954
  const dataset = {
28885
28955
  label,
28886
28956
  data,
28887
- borderColor: BACKGROUND_CHART_COLOR,
28957
+ borderColor: definition.background || "#FFFFFF",
28888
28958
  backgroundColor,
28889
28959
  hoverOffset: 30,
28890
28960
  };
@@ -28898,7 +28968,7 @@ stores.inject(MyMetaStore, storeInstance);
28898
28968
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28899
28969
  const trendDatasets = [];
28900
28970
  for (let index = 0; index < dataSetsValues.length; index++) {
28901
- let { label, data } = dataSetsValues[index];
28971
+ let { label, data, hidden } = dataSetsValues[index];
28902
28972
  label = definition.dataSets?.[index].label || label;
28903
28973
  const design = definition.dataSets?.[index];
28904
28974
  const color = colors.next();
@@ -28906,6 +28976,7 @@ stores.inject(MyMetaStore, storeInstance);
28906
28976
  const dataset = {
28907
28977
  label: label,
28908
28978
  data,
28979
+ hidden,
28909
28980
  borderColor: color,
28910
28981
  backgroundColor: color,
28911
28982
  yAxisID: definition.dataSets?.[index].yAxisId || "y",
@@ -28930,7 +29001,7 @@ stores.inject(MyMetaStore, storeInstance);
28930
29001
  const fill = definition.fillArea ?? false;
28931
29002
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28932
29003
  for (let i = 0; i < dataSetsValues.length; i++) {
28933
- let { label, data } = dataSetsValues[i];
29004
+ let { label, data, hidden } = dataSetsValues[i];
28934
29005
  if (definition.dataSets?.[i]?.label) {
28935
29006
  label = definition.dataSets[i].label;
28936
29007
  }
@@ -28938,6 +29009,7 @@ stores.inject(MyMetaStore, storeInstance);
28938
29009
  const dataset = {
28939
29010
  label,
28940
29011
  data,
29012
+ hidden,
28941
29013
  borderColor,
28942
29014
  backgroundColor: borderColor,
28943
29015
  };
@@ -29083,6 +29155,11 @@ stores.inject(MyMetaStore, storeInstance);
29083
29155
  hidden: false,
29084
29156
  lineWidth: 2,
29085
29157
  })),
29158
+ filter: (legendItem, data) => {
29159
+ return "datasetIndex" in legendItem
29160
+ ? !data.datasets[legendItem.datasetIndex].hidden
29161
+ : true;
29162
+ },
29086
29163
  },
29087
29164
  };
29088
29165
  }
@@ -29144,6 +29221,11 @@ stores.inject(MyMetaStore, storeInstance);
29144
29221
  }
29145
29222
  return legendValues;
29146
29223
  },
29224
+ filter: (legendItem, data) => {
29225
+ return "datasetIndex" in legendItem
29226
+ ? !data.datasets[legendItem.datasetIndex].hidden
29227
+ : true;
29228
+ },
29147
29229
  },
29148
29230
  onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29149
29231
  };
@@ -29227,6 +29309,11 @@ stores.inject(MyMetaStore, storeInstance);
29227
29309
  ...legendLabelConfig,
29228
29310
  };
29229
29311
  }),
29312
+ filter: (legendItem, data) => {
29313
+ return "datasetIndex" in legendItem
29314
+ ? !data.datasets[legendItem.datasetIndex].hidden
29315
+ : true;
29316
+ },
29230
29317
  },
29231
29318
  };
29232
29319
  }
@@ -32899,7 +32986,7 @@ stores.inject(MyMetaStore, storeInstance);
32899
32986
  const deleteSheet = {
32900
32987
  name: _t("Delete"),
32901
32988
  isVisible: (env) => {
32902
- return env.model.getters.getSheetIds().length > 1;
32989
+ return env.model.getters.getVisibleSheetIds().length > 1;
32903
32990
  },
32904
32991
  execute: (env) => env.askConfirmation(_t("Are you sure you want to delete this sheet?"), () => {
32905
32992
  env.model.dispatch("DELETE_SHEET", { sheetId: env.model.getters.getActiveSheetId() });
@@ -32910,7 +32997,7 @@ stores.inject(MyMetaStore, storeInstance);
32910
32997
  name: _t("Duplicate"),
32911
32998
  execute: (env) => {
32912
32999
  const sheetIdFrom = env.model.getters.getActiveSheetId();
32913
- const sheetIdTo = env.model.uuidGenerator.uuidv4();
33000
+ const sheetIdTo = env.model.uuidGenerator.smallUuid();
32914
33001
  env.model.dispatch("DUPLICATE_SHEET", {
32915
33002
  sheetId: sheetIdFrom,
32916
33003
  sheetIdTo,
@@ -33016,6 +33103,100 @@ stores.inject(MyMetaStore, storeInstance);
33016
33103
  function getOpenedMenus() {
33017
33104
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33018
33105
  }
33106
+ function getCurrentSelection(el) {
33107
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33108
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33109
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33110
+ return {
33111
+ start: startSizeBefore,
33112
+ end: endSizeBefore,
33113
+ };
33114
+ }
33115
+ function getStartAndEndSelection(el) {
33116
+ const selection = document.getSelection();
33117
+ return {
33118
+ startElement: selection.anchorNode || el,
33119
+ startSelectionOffset: selection.anchorOffset,
33120
+ endElement: selection.focusNode || el,
33121
+ endSelectionOffset: selection.focusOffset,
33122
+ };
33123
+ }
33124
+ /**
33125
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33126
+ * The selected node is either a Text node or an Element node.
33127
+ *
33128
+ * case 1 -Text node:
33129
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33130
+ * content length of all previous nodes.
33131
+ *
33132
+ * case 2 - Element node:
33133
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33134
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33135
+ *
33136
+ * See the MDN documentation for more details.
33137
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33138
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33139
+ *
33140
+ */
33141
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33142
+ let usedCharacters = 0;
33143
+ let it = iterateChildren(el);
33144
+ let current = it.next();
33145
+ let isFirstParagraph = true;
33146
+ while (!current.done && current.value !== nodeToFind) {
33147
+ if (!current.value.hasChildNodes()) {
33148
+ if (current.value.textContent) {
33149
+ usedCharacters += current.value.textContent.length;
33150
+ }
33151
+ }
33152
+ // One new paragraph = one new line character, except for the first paragraph
33153
+ if (current.value.nodeName === "P" ||
33154
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33155
+ ) {
33156
+ if (isFirstParagraph) {
33157
+ isFirstParagraph = false;
33158
+ }
33159
+ else {
33160
+ usedCharacters++;
33161
+ }
33162
+ }
33163
+ current = it.next();
33164
+ }
33165
+ if (current.value !== nodeToFind) {
33166
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33167
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33168
+ *
33169
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33170
+ */
33171
+ return 0;
33172
+ }
33173
+ else {
33174
+ if (!current.value.hasChildNodes()) {
33175
+ usedCharacters += nodeOffset;
33176
+ }
33177
+ else {
33178
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33179
+ usedCharacters += children.reduce((acc, child, index) => {
33180
+ if (child.textContent !== null) {
33181
+ // need to account for paragraph nodes that implicitly add a new line
33182
+ // except for the last paragraph
33183
+ let chars = child.textContent.length;
33184
+ if (child.nodeName === "P" && index !== children.length - 1) {
33185
+ chars++;
33186
+ }
33187
+ return acc + chars;
33188
+ }
33189
+ else {
33190
+ return acc;
33191
+ }
33192
+ }, 0);
33193
+ }
33194
+ }
33195
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33196
+ usedCharacters++;
33197
+ }
33198
+ return usedCharacters;
33199
+ }
33019
33200
  const letterRegex = /^[a-zA-Z]$/;
33020
33201
  /**
33021
33202
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -33613,20 +33794,21 @@ stores.inject(MyMetaStore, storeInstance);
33613
33794
  }
33614
33795
  // Only display legend for several datasets.
33615
33796
  const newLegendPos = dataSetZone.right === dataSetZone.left ? "none" : "top";
33616
- const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
33617
- if (canChartParseLabels(labelRange, getters)) {
33618
- return {
33619
- title: {},
33620
- dataSets,
33621
- labelsAsText: false,
33622
- stacked: false,
33623
- aggregated: false,
33624
- cumulative: false,
33625
- labelRange: labelRangeXc,
33626
- type: "line",
33627
- dataSetsHaveTitle,
33628
- legendPosition: newLegendPos,
33629
- };
33797
+ const lineChartDefinition = {
33798
+ title: {},
33799
+ dataSets,
33800
+ labelsAsText: false,
33801
+ stacked: false,
33802
+ aggregated: false,
33803
+ cumulative: false,
33804
+ labelRange: labelRangeXc,
33805
+ type: "line",
33806
+ dataSetsHaveTitle,
33807
+ legendPosition: newLegendPos,
33808
+ };
33809
+ const chart = new LineChart(lineChartDefinition, sheetId, getters);
33810
+ if (canChartParseLabels(lineChartDefinition, chart.dataSets, chart.labelRange, getters)) {
33811
+ return lineChartDefinition;
33630
33812
  }
33631
33813
  const _dataSets = createDataSets(getters, dataSets, sheetId, dataSetsHaveTitle);
33632
33814
  if (singleColumn &&
@@ -34040,7 +34222,7 @@ stores.inject(MyMetaStore, storeInstance);
34040
34222
  //------------------------------------------------------------------------------
34041
34223
  const CREATE_CHART = (env) => {
34042
34224
  const getters = env.model.getters;
34043
- const id = env.model.uuidGenerator.uuidv4();
34225
+ const id = env.model.uuidGenerator.smallUuid();
34044
34226
  const sheetId = getters.getActiveSheetId();
34045
34227
  if (getZoneArea(env.model.getters.getSelectedZone()) === 1) {
34046
34228
  env.model.selection.selectTableAroundSelection();
@@ -34063,8 +34245,8 @@ stores.inject(MyMetaStore, storeInstance);
34063
34245
  // Pivots
34064
34246
  //------------------------------------------------------------------------------
34065
34247
  const CREATE_PIVOT = (env) => {
34066
- const pivotId = env.model.uuidGenerator.uuidv4();
34067
- const newSheetId = env.model.uuidGenerator.uuidv4();
34248
+ const pivotId = env.model.uuidGenerator.smallUuid();
34249
+ const newSheetId = env.model.uuidGenerator.smallUuid();
34068
34250
  const result = env.model.dispatch("INSERT_NEW_PIVOT", { pivotId, newSheetId });
34069
34251
  if (result.isSuccessful) {
34070
34252
  env.openSidePanel("PivotSidePanel", { pivotId });
@@ -34123,7 +34305,7 @@ stores.inject(MyMetaStore, storeInstance);
34123
34305
  const CREATE_IMAGE = async (env) => {
34124
34306
  if (env.imageProvider) {
34125
34307
  const sheetId = env.model.getters.getActiveSheetId();
34126
- const figureId = env.model.uuidGenerator.uuidv4();
34308
+ const figureId = env.model.uuidGenerator.smallUuid();
34127
34309
  const image = await requestImage(env);
34128
34310
  if (!image) {
34129
34311
  throw new Error("No image provider was given to the environment");
@@ -34676,7 +34858,7 @@ stores.inject(MyMetaStore, storeInstance);
34676
34858
  ranges,
34677
34859
  sheetId,
34678
34860
  rule: {
34679
- id: env.model.uuidGenerator.uuidv4(),
34861
+ id: env.model.uuidGenerator.smallUuid(),
34680
34862
  criterion: {
34681
34863
  type: "isBoolean",
34682
34864
  values: [],
@@ -34692,7 +34874,7 @@ stores.inject(MyMetaStore, storeInstance);
34692
34874
  const zones = env.model.getters.getSelectedZones();
34693
34875
  const sheetId = env.model.getters.getActiveSheetId();
34694
34876
  const ranges = zones.map((zone) => env.model.getters.getRangeDataFromZone(sheetId, zone));
34695
- const ruleID = env.model.uuidGenerator.uuidv4();
34877
+ const ruleID = env.model.uuidGenerator.smallUuid();
34696
34878
  env.model.dispatch("ADD_DATA_VALIDATION_RULE", {
34697
34879
  ranges,
34698
34880
  sheetId,
@@ -34723,7 +34905,7 @@ stores.inject(MyMetaStore, storeInstance);
34723
34905
  execute: (env) => {
34724
34906
  const activeSheetId = env.model.getters.getActiveSheetId();
34725
34907
  const position = env.model.getters.getSheetIds().indexOf(activeSheetId) + 1;
34726
- const sheetId = env.model.uuidGenerator.uuidv4();
34908
+ const sheetId = env.model.uuidGenerator.smallUuid();
34727
34909
  env.model.dispatch("CREATE_SHEET", { sheetId, position });
34728
34910
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
34729
34911
  },
@@ -38250,7 +38432,7 @@ stores.inject(MyMetaStore, storeInstance);
38250
38432
  .o-font-size-editor {
38251
38433
  height: calc(100% - 4px);
38252
38434
  input.o-font-size {
38253
- outline-color: ${SELECTION_BORDER_COLOR};
38435
+ outline: none;
38254
38436
  height: 20px;
38255
38437
  width: 23px;
38256
38438
  }
@@ -39212,7 +39394,7 @@ stores.inject(MyMetaStore, storeInstance);
39212
39394
  get canTreatLabelsAsText() {
39213
39395
  const chart = this.env.model.getters.getChart(this.props.figureId);
39214
39396
  if (chart && chart instanceof LineChart) {
39215
- return canChartParseLabels(chart.labelRange, this.env.model.getters);
39397
+ return canChartParseLabels(chart.getDefinition(), chart.dataSets, chart.labelRange, this.env.model.getters);
39216
39398
  }
39217
39399
  return false;
39218
39400
  }
@@ -39289,7 +39471,7 @@ stores.inject(MyMetaStore, storeInstance);
39289
39471
  get canTreatLabelsAsText() {
39290
39472
  const chart = this.env.model.getters.getChart(this.props.figureId);
39291
39473
  if (chart && chart instanceof ScatterChart) {
39292
- return canChartParseLabels(chart.labelRange, this.env.model.getters);
39474
+ return canChartParseLabels(chart.getDefinition(), chart.dataSets, chart.labelRange, this.env.model.getters);
39293
39475
  }
39294
39476
  return false;
39295
39477
  }
@@ -39863,6 +40045,10 @@ stores.inject(MyMetaStore, storeInstance);
39863
40045
  if (currentStart === start && currentEnd === end) {
39864
40046
  return;
39865
40047
  }
40048
+ if (selection.rangeCount === 0) {
40049
+ const range = document.createRange();
40050
+ selection.addRange(range);
40051
+ }
39866
40052
  const currentRange = selection.getRangeAt(0);
39867
40053
  let range;
39868
40054
  if (this.el.contains(currentRange.startContainer)) {
@@ -39890,8 +40076,16 @@ stores.inject(MyMetaStore, storeInstance);
39890
40076
  }
39891
40077
  let startNode = this.findChildAtCharacterIndex(start);
39892
40078
  let endNode = this.findChildAtCharacterIndex(end);
39893
- range.setStart(startNode.node, startNode.offset);
39894
- range.setEnd(endNode.node, endNode.offset);
40079
+ // setEnd (setStart) will result in a collapsed range if the end point is before the start point
40080
+ // https://developer.mozilla.org/en-US/docs/Web/API/Range/setEnd
40081
+ if (start <= end) {
40082
+ range.setStart(startNode.node, startNode.offset);
40083
+ range.setEnd(endNode.node, endNode.offset);
40084
+ }
40085
+ else {
40086
+ range.setStart(endNode.node, endNode.offset);
40087
+ range.setEnd(startNode.node, startNode.offset);
40088
+ }
39895
40089
  }
39896
40090
  }
39897
40091
  /**
@@ -40025,7 +40219,7 @@ stores.inject(MyMetaStore, storeInstance);
40025
40219
  if (!focusedNode || !this.el.contains(focusedNode))
40026
40220
  return;
40027
40221
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40028
- element?.scrollIntoView({ block: "nearest" });
40222
+ element?.scrollIntoView?.({ block: "nearest" });
40029
40223
  }
40030
40224
  /**
40031
40225
  * remove the current selection of the user
@@ -40045,100 +40239,7 @@ stores.inject(MyMetaStore, storeInstance);
40045
40239
  * finds the indexes of the current selection.
40046
40240
  * */
40047
40241
  getCurrentSelection() {
40048
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40049
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40050
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40051
- return {
40052
- start: startSizeBefore,
40053
- end: endSizeBefore,
40054
- };
40055
- }
40056
- /**
40057
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40058
- * The selected node is either a Text node or an Element node.
40059
- *
40060
- * case 1 -Text node:
40061
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40062
- * content length of all previous nodes.
40063
- *
40064
- * case 2 - Element node:
40065
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40066
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40067
- *
40068
- * See the MDN documentation for more details.
40069
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40070
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40071
- *
40072
- */
40073
- findSelectionIndex(nodeToFind, nodeOffset) {
40074
- let usedCharacters = 0;
40075
- let it = iterateChildren(this.el);
40076
- let current = it.next();
40077
- let isFirstParagraph = true;
40078
- while (!current.done && current.value !== nodeToFind) {
40079
- if (!current.value.hasChildNodes()) {
40080
- if (current.value.textContent) {
40081
- usedCharacters += current.value.textContent.length;
40082
- }
40083
- }
40084
- // One new paragraph = one new line character, except for the first paragraph
40085
- if (current.value.nodeName === "P" ||
40086
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40087
- ) {
40088
- if (isFirstParagraph) {
40089
- isFirstParagraph = false;
40090
- }
40091
- else {
40092
- usedCharacters++;
40093
- }
40094
- }
40095
- current = it.next();
40096
- }
40097
- if (current.value !== nodeToFind) {
40098
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40099
- * In this case, we return 0 because we don't know the size of the text before the selection.
40100
- *
40101
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40102
- *
40103
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40104
- */
40105
- return 0;
40106
- }
40107
- else {
40108
- if (!current.value.hasChildNodes()) {
40109
- usedCharacters += nodeOffset;
40110
- }
40111
- else {
40112
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40113
- usedCharacters += children.reduce((acc, child, index) => {
40114
- if (child.textContent !== null) {
40115
- // need to account for paragraph nodes that implicitely add a new line
40116
- // except for the last paragraph
40117
- let chars = child.textContent.length;
40118
- if (child.nodeName === "P" && index !== children.length - 1) {
40119
- chars++;
40120
- }
40121
- return acc + chars;
40122
- }
40123
- else {
40124
- return acc;
40125
- }
40126
- }, 0);
40127
- }
40128
- }
40129
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40130
- usedCharacters++;
40131
- }
40132
- return usedCharacters;
40133
- }
40134
- getStartAndEndSelection() {
40135
- const selection = document.getSelection();
40136
- return {
40137
- startElement: selection.anchorNode || this.el,
40138
- startSelectionOffset: selection.anchorOffset,
40139
- endElement: selection.focusNode || this.el,
40140
- endSelectionOffset: selection.focusOffset,
40141
- };
40242
+ return getCurrentSelection(this.el);
40142
40243
  }
40143
40244
  getText() {
40144
40245
  let text = "";
@@ -42067,7 +42168,7 @@ stores.inject(MyMetaStore, storeInstance);
42067
42168
  this.originalEditedCf = undefined;
42068
42169
  }
42069
42170
  addConditionalFormat() {
42070
- const cfId = this.env.model.uuidGenerator.uuidv4();
42171
+ const cfId = this.env.model.uuidGenerator.smallUuid();
42071
42172
  this.env.model.dispatch("ADD_CONDITIONAL_FORMAT", {
42072
42173
  sheetId: this.activeSheetId,
42073
42174
  ranges: this.env.model.getters
@@ -43337,7 +43438,7 @@ stores.inject(MyMetaStore, storeInstance);
43337
43438
  .getSelectedZones()
43338
43439
  .map((zone) => zoneToXc(this.env.model.getters.getUnboundedZone(sheetId, zone)));
43339
43440
  return {
43340
- id: this.env.model.uuidGenerator.uuidv4(),
43441
+ id: this.env.model.uuidGenerator.smallUuid(),
43341
43442
  criterion: { type: "textContains", values: [""] },
43342
43443
  ranges,
43343
43444
  };
@@ -44955,8 +45056,8 @@ stores.inject(MyMetaStore, storeInstance);
44955
45056
  return this.env.model.getters.getPivotDisplayName(this.props.pivotId);
44956
45057
  }
44957
45058
  duplicatePivot() {
44958
- const newPivotId = this.env.model.uuidGenerator.uuidv4();
44959
- const newSheetId = this.env.model.uuidGenerator.uuidv4();
45059
+ const newPivotId = this.env.model.uuidGenerator.smallUuid();
45060
+ const newSheetId = this.env.model.uuidGenerator.smallUuid();
44960
45061
  const result = this.env.model.dispatch("DUPLICATE_PIVOT_IN_NEW_SHEET", {
44961
45062
  pivotId: this.props.pivotId,
44962
45063
  newPivotId,
@@ -47581,7 +47682,7 @@ stores.inject(MyMetaStore, storeInstance);
47581
47682
  this.state.selectedTemplateName = templateName;
47582
47683
  }
47583
47684
  onConfirm() {
47584
- const tableStyleId = this.props.styleId || this.env.model.uuidGenerator.uuidv4();
47685
+ const tableStyleId = this.props.styleId || this.env.model.uuidGenerator.smallUuid();
47585
47686
  this.env.model.dispatch("CREATE_TABLE_STYLE", {
47586
47687
  tableStyleId,
47587
47688
  tableStyleName: this.state.styleName,
@@ -53093,6 +53194,10 @@ stores.inject(MyMetaStore, storeInstance);
53093
53194
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
53094
53195
  case "CLEAR_CELL":
53095
53196
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
53197
+ case "UPDATE_CELL_POSITION":
53198
+ return !cmd.cellId || this.cells[cmd.sheetId]?.[cmd.cellId]
53199
+ ? "Success" /* CommandResult.Success */
53200
+ : "InvalidCellId" /* CommandResult.InvalidCellId */;
53096
53201
  default:
53097
53202
  return "Success" /* CommandResult.Success */;
53098
53203
  }
@@ -53137,6 +53242,9 @@ stores.inject(MyMetaStore, storeInstance);
53137
53242
  case "DELETE_CONTENT":
53138
53243
  this.clearZones(cmd.sheetId, cmd.target);
53139
53244
  break;
53245
+ case "DELETE_SHEET": {
53246
+ this.history.update("cells", cmd.sheetId, undefined);
53247
+ }
53140
53248
  }
53141
53249
  }
53142
53250
  clearZones(sheetId, zones) {
@@ -53927,6 +54035,9 @@ stores.inject(MyMetaStore, storeInstance);
53927
54035
  allowDispatch(cmd) {
53928
54036
  switch (cmd.type) {
53929
54037
  case "ADD_CONDITIONAL_FORMAT":
54038
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
54039
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54040
+ }
53930
54041
  return this.checkValidations(cmd, this.checkCFRule, this.checkEmptyRange, this.checkCFHasChanged);
53931
54042
  case "CHANGE_CONDITIONAL_FORMAT_PRIORITY":
53932
54043
  return this.checkValidPriorityChange(cmd.cfId, cmd.delta, cmd.sheetId);
@@ -54343,8 +54454,17 @@ stores.inject(MyMetaStore, storeInstance);
54343
54454
  allowDispatch(cmd) {
54344
54455
  switch (cmd.type) {
54345
54456
  case "ADD_DATA_VALIDATION_RULE":
54457
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
54458
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54459
+ }
54460
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
54461
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54462
+ }
54346
54463
  return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkValidRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
54347
54464
  case "REMOVE_DATA_VALIDATION_RULE":
54465
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
54466
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
54467
+ }
54348
54468
  if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
54349
54469
  return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
54350
54470
  }
@@ -54571,6 +54691,7 @@ stores.inject(MyMetaStore, storeInstance);
54571
54691
  class FigurePlugin extends CorePlugin {
54572
54692
  static getters = ["getFigures", "getFigure", "getFigureSheetId"];
54573
54693
  figures = {};
54694
+ insertionOrders = []; // TODO use a list in master
54574
54695
  // ---------------------------------------------------------------------------
54575
54696
  // Command Handling
54576
54697
  // ---------------------------------------------------------------------------
@@ -54673,11 +54794,14 @@ stores.inject(MyMetaStore, storeInstance);
54673
54794
  }
54674
54795
  addFigure(figure, sheetId) {
54675
54796
  this.history.update("figures", sheetId, figure.id, figure);
54797
+ this.history.update("insertionOrders", this.insertionOrders.length, figure.id);
54676
54798
  }
54677
54799
  deleteSheet(sheetId) {
54800
+ this.history.update("insertionOrders", this.insertionOrders.filter((id) => !this.figures[sheetId]?.[id]));
54678
54801
  this.history.update("figures", sheetId, undefined);
54679
54802
  }
54680
54803
  removeFigure(id, sheetId) {
54804
+ this.history.update("insertionOrders", this.insertionOrders.filter((figureId) => figureId !== id));
54681
54805
  this.history.update("figures", sheetId, id, undefined);
54682
54806
  }
54683
54807
  checkFigureExists(sheetId, figureId) {
@@ -54696,7 +54820,14 @@ stores.inject(MyMetaStore, storeInstance);
54696
54820
  // Getters
54697
54821
  // ---------------------------------------------------------------------------
54698
54822
  getFigures(sheetId) {
54699
- return Object.values(this.figures[sheetId] || {}).filter(isDefined);
54823
+ const figures = [];
54824
+ for (const figureId of this.insertionOrders) {
54825
+ const figure = this.figures[sheetId]?.[figureId];
54826
+ if (figure) {
54827
+ figures.push(figure);
54828
+ }
54829
+ }
54830
+ return figures;
54700
54831
  }
54701
54832
  getFigure(sheetId, figureId) {
54702
54833
  return this.figures[sheetId]?.[figureId];
@@ -54709,11 +54840,9 @@ stores.inject(MyMetaStore, storeInstance);
54709
54840
  // ---------------------------------------------------------------------------
54710
54841
  import(data) {
54711
54842
  for (let sheet of data.sheets) {
54712
- const figures = {};
54713
- sheet.figures.forEach((figure) => {
54714
- figures[figure.id] = figure;
54715
- });
54716
- this.figures[sheet.id] = figures;
54843
+ for (const figure of sheet.figures) {
54844
+ this.addFigure(figure, sheet.id);
54845
+ }
54717
54846
  }
54718
54847
  }
54719
54848
  export(data) {
@@ -56139,6 +56268,9 @@ stores.inject(MyMetaStore, storeInstance);
56139
56268
  case "CREATE_SHEET": {
56140
56269
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
56141
56270
  }
56271
+ case "DUPLICATE_SHEET": {
56272
+ return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
56273
+ }
56142
56274
  case "MOVE_SHEET":
56143
56275
  try {
56144
56276
  const currentIndex = this.orderedSheetIds.findIndex((id) => id === cmd.sheetId);
@@ -56155,7 +56287,7 @@ stores.inject(MyMetaStore, storeInstance);
56155
56287
  ? "Success" /* CommandResult.Success */
56156
56288
  : "InvalidColor" /* CommandResult.InvalidColor */;
56157
56289
  case "DELETE_SHEET":
56158
- return this.orderedSheetIds.length > 1
56290
+ return this.getVisibleSheetIds().length > 1
56159
56291
  ? "Success" /* CommandResult.Success */
56160
56292
  : "NotEnoughSheets" /* CommandResult.NotEnoughSheets */;
56161
56293
  case "ADD_COLUMNS_ROWS":
@@ -56950,6 +57082,10 @@ stores.inject(MyMetaStore, storeInstance);
56950
57082
  checkZonesAreInSheet(cmd) {
56951
57083
  if (!("sheetId" in cmd))
56952
57084
  return "Success" /* CommandResult.Success */;
57085
+ if ("ranges" in cmd &&
57086
+ cmd.ranges.some((rangeData) => rangeData._sheetId !== "" && !this.getters.tryGetSheet(rangeData._sheetId))) {
57087
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57088
+ }
56953
57089
  return this.checkZonesExistInSheet(cmd.sheetId, this.getCommandZones(cmd));
56954
57090
  }
56955
57091
  }
@@ -56958,6 +57094,7 @@ stores.inject(MyMetaStore, storeInstance);
56958
57094
  class TablePlugin extends CorePlugin {
56959
57095
  static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
56960
57096
  tables = {};
57097
+ insertionOrders = {};
56961
57098
  adaptRanges(applyChange, sheetId) {
56962
57099
  const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
56963
57100
  for (const sheetId of sheetIds) {
@@ -56969,6 +57106,9 @@ stores.inject(MyMetaStore, storeInstance);
56969
57106
  allowDispatch(cmd) {
56970
57107
  switch (cmd.type) {
56971
57108
  case "CREATE_TABLE":
57109
+ if (cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId) || rangeData._sheetId !== cmd.sheetId)) {
57110
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57111
+ }
56972
57112
  const zones = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData).zone);
56973
57113
  if (!areZonesContinuous(zones)) {
56974
57114
  return "NonContinuousTargets" /* CommandResult.NonContinuousTargets */;
@@ -56999,11 +57139,13 @@ stores.inject(MyMetaStore, storeInstance);
56999
57139
  switch (cmd.type) {
57000
57140
  case "CREATE_SHEET":
57001
57141
  this.history.update("tables", cmd.sheetId, {});
57142
+ this.history.update("insertionOrders", cmd.sheetId, []);
57002
57143
  break;
57003
57144
  case "DELETE_SHEET": {
57004
57145
  const tables = { ...this.tables };
57005
57146
  delete tables[cmd.sheetId];
57006
57147
  this.history.update("tables", tables);
57148
+ this.history.update("insertionOrders", cmd.sheetId, undefined);
57007
57149
  break;
57008
57150
  }
57009
57151
  case "DUPLICATE_SHEET": {
@@ -57015,6 +57157,9 @@ stores.inject(MyMetaStore, storeInstance);
57015
57157
  : this.copyStaticTableForSheet(cmd.sheetIdTo, table);
57016
57158
  }
57017
57159
  this.history.update("tables", cmd.sheetIdTo, newTables);
57160
+ this.history.update("insertionOrders", cmd.sheetIdTo, [
57161
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
57162
+ ]);
57018
57163
  break;
57019
57164
  }
57020
57165
  case "CREATE_TABLE": {
@@ -57028,6 +57173,10 @@ stores.inject(MyMetaStore, storeInstance);
57028
57173
  ? this.createDynamicTable(id, union, config)
57029
57174
  : this.createStaticTable(id, cmd.tableType, union, config);
57030
57175
  this.history.update("tables", cmd.sheetId, newTable.id, newTable);
57176
+ this.history.update("insertionOrders", cmd.sheetId, [
57177
+ ...(this.insertionOrders[cmd.sheetId] ?? []),
57178
+ newTable.id,
57179
+ ]);
57031
57180
  break;
57032
57181
  }
57033
57182
  case "REMOVE_TABLE": {
@@ -57038,6 +57187,7 @@ stores.inject(MyMetaStore, storeInstance);
57038
57187
  }
57039
57188
  }
57040
57189
  this.history.update("tables", cmd.sheetId, tables);
57190
+ this.history.update("insertionOrders", cmd.sheetId, this.insertionOrders[cmd.sheetId]?.filter((id) => id in tables));
57041
57191
  break;
57042
57192
  }
57043
57193
  case "UPDATE_TABLE": {
@@ -57073,7 +57223,14 @@ stores.inject(MyMetaStore, storeInstance);
57073
57223
  }
57074
57224
  }
57075
57225
  getCoreTables(sheetId) {
57076
- return this.tables[sheetId] ? Object.values(this.tables[sheetId]).filter(isDefined) : [];
57226
+ const tables = [];
57227
+ for (const tableId of this.insertionOrders[sheetId] || []) {
57228
+ const table = this.tables[sheetId][tableId];
57229
+ if (table) {
57230
+ tables.push(table);
57231
+ }
57232
+ }
57233
+ return tables;
57077
57234
  }
57078
57235
  getCoreTable({ sheetId, col, row }) {
57079
57236
  return this.getCoreTables(sheetId).find((table) => isInside(col, row, table.range.zone));
@@ -57356,6 +57513,7 @@ stores.inject(MyMetaStore, storeInstance);
57356
57513
  // ---------------------------------------------------------------------------
57357
57514
  import(data) {
57358
57515
  for (const sheet of data.sheets) {
57516
+ const tableIds = [];
57359
57517
  for (const tableData of sheet.tables || []) {
57360
57518
  const uuid = `${nextTableId++}`;
57361
57519
  const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
@@ -57365,7 +57523,9 @@ stores.inject(MyMetaStore, storeInstance);
57365
57523
  ? this.createDynamicTable(uuid, range, tableConfig)
57366
57524
  : this.createStaticTable(uuid, tableType, range, tableConfig);
57367
57525
  this.history.update("tables", sheet.id, table.id, table);
57526
+ tableIds.push(table.id);
57368
57527
  }
57528
+ this.history.update("insertionOrders", sheet.id, tableIds);
57369
57529
  }
57370
57530
  }
57371
57531
  export(data) {
@@ -57405,7 +57565,10 @@ stores.inject(MyMetaStore, storeInstance);
57405
57565
  allowDispatch(cmd) {
57406
57566
  switch (cmd.type) {
57407
57567
  case "GROUP_HEADERS": {
57408
- const { start, end } = cmd;
57568
+ const { start, end, sheetId } = cmd;
57569
+ if (!this.getters.tryGetSheet(sheetId)) {
57570
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57571
+ }
57409
57572
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
57410
57573
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
57411
57574
  }
@@ -57418,7 +57581,10 @@ stores.inject(MyMetaStore, storeInstance);
57418
57581
  break;
57419
57582
  }
57420
57583
  case "UNGROUP_HEADERS": {
57421
- const { start, end } = cmd;
57584
+ const { start, end, sheetId } = cmd;
57585
+ if (!this.getters.tryGetSheet(sheetId)) {
57586
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57587
+ }
57422
57588
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
57423
57589
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
57424
57590
  }
@@ -57429,6 +57595,9 @@ stores.inject(MyMetaStore, storeInstance);
57429
57595
  }
57430
57596
  case "UNFOLD_HEADER_GROUP":
57431
57597
  case "FOLD_HEADER_GROUP":
57598
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
57599
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
57600
+ }
57432
57601
  const group = this.findGroupWithStartEnd(cmd.sheetId, cmd.dimension, cmd.start, cmd.end);
57433
57602
  if (!group) {
57434
57603
  return "UnknownHeaderGroup" /* CommandResult.UnknownHeaderGroup */;
@@ -57829,6 +57998,9 @@ stores.inject(MyMetaStore, storeInstance);
57829
57998
  return this.checkDuplicatedMeasureIds(cmd.pivot);
57830
57999
  }
57831
58000
  case "UPDATE_PIVOT": {
58001
+ if (!(cmd.pivotId in this.pivots)) {
58002
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
58003
+ }
57832
58004
  if (deepEquals(cmd.pivot, this.pivots[cmd.pivotId]?.definition)) {
57833
58005
  return "NoChanges" /* CommandResult.NoChanges */;
57834
58006
  }
@@ -57845,6 +58017,8 @@ stores.inject(MyMetaStore, storeInstance);
57845
58017
  return "EmptyName" /* CommandResult.EmptyName */;
57846
58018
  }
57847
58019
  break;
58020
+ case "REMOVE_PIVOT":
58021
+ case "DUPLICATE_PIVOT":
57848
58022
  case "INSERT_PIVOT": {
57849
58023
  if (!(cmd.pivotId in this.pivots)) {
57850
58024
  return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
@@ -57894,7 +58068,7 @@ stores.inject(MyMetaStore, storeInstance);
57894
58068
  break;
57895
58069
  }
57896
58070
  case "UPDATE_PIVOT": {
57897
- this.history.update("pivots", cmd.pivotId, "definition", cmd.pivot);
58071
+ this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
57898
58072
  this.compileCalculatedMeasures(cmd.pivot.measures);
57899
58073
  break;
57900
58074
  }
@@ -57965,7 +58139,7 @@ stores.inject(MyMetaStore, storeInstance);
57965
58139
  // Private
57966
58140
  // -------------------------------------------------------------------------
57967
58141
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
57968
- this.history.update("pivots", pivotId, { definition: pivot, formulaId });
58142
+ this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
57969
58143
  this.compileCalculatedMeasures(pivot.measures);
57970
58144
  this.history.update("formulaIds", formulaId, pivotId);
57971
58145
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
@@ -59551,6 +59725,10 @@ stores.inject(MyMetaStore, storeInstance);
59551
59725
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
59552
59726
  this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
59553
59727
  this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
59728
+ this.compilationParams.evalContext.lookupCaches = {
59729
+ forwardSearch: new Map(),
59730
+ reverseSearch: new Map(),
59731
+ };
59554
59732
  }
59555
59733
  createEmptyPositionSet() {
59556
59734
  const sheetSizes = {};
@@ -62871,6 +63049,9 @@ stores.inject(MyMetaStore, storeInstance);
62871
63049
  };
62872
63050
  }
62873
63051
  function createSheetTransformation(toTransform, executed) {
63052
+ if (toTransform.sheetId === executed.sheetId) {
63053
+ toTransform = { ...toTransform, sheetId: `${toTransform.sheetId}~` };
63054
+ }
62874
63055
  if (toTransform.name === executed.name) {
62875
63056
  return {
62876
63057
  ...toTransform,
@@ -63522,21 +63703,14 @@ stores.inject(MyMetaStore, storeInstance);
63522
63703
  if (!message)
63523
63704
  return;
63524
63705
  if (message.type === "REMOTE_REVISION") {
63525
- const revision = this.revisions.get(message.nextRevisionId);
63706
+ let revision = this.revisions.get(message.nextRevisionId);
63526
63707
  if (revision.commands.length === 0) {
63527
63708
  /**
63528
- * The command is empty, we have to drop all the next local revisions
63709
+ * The command is empty, we have to rebase all the next local revisions
63529
63710
  * to avoid issues with undo/redo
63530
63711
  */
63531
- this.revisions.drop(revision.id);
63532
- const revisionIds = this.pendingMessages
63533
- .filter((message) => message.type === "REMOTE_REVISION")
63534
- .map((message) => message.nextRevisionId);
63535
- this.trigger("pending-revisions-dropped", { revisionIds });
63536
- this.waitingAck = false;
63537
- this.waitingUndoRedoAck = false;
63538
- this.pendingMessages = [];
63539
- return;
63712
+ this.revisions.rebase(revision.id);
63713
+ revision = this.revisions.get(message.nextRevisionId);
63540
63714
  }
63541
63715
  message = {
63542
63716
  ...message,
@@ -63561,7 +63735,6 @@ stores.inject(MyMetaStore, storeInstance);
63561
63735
  switch (message.type) {
63562
63736
  case "REMOTE_REVISION":
63563
63737
  case "REVISION_REDONE":
63564
- case "REVISION_UNDONE":
63565
63738
  case "SNAPSHOT_CREATED":
63566
63739
  this.waitingAck = false;
63567
63740
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
@@ -63570,6 +63743,25 @@ stores.inject(MyMetaStore, storeInstance);
63570
63743
  this.lastRevisionMessage = message;
63571
63744
  this.sendPendingMessage();
63572
63745
  break;
63746
+ case "REVISION_UNDONE": {
63747
+ this.waitingAck = false;
63748
+ this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63749
+ const firstPendingRevisionId = this.pendingMessages.findIndex((message) => message.type === "REMOTE_REVISION");
63750
+ if (firstPendingRevisionId !== -1) {
63751
+ /**
63752
+ * Some revisions undergo transformations that may cause issues with
63753
+ * undo/redo if the transformation is destructive (we don't get back
63754
+ * the original command by transforming it with the inverse).
63755
+ * To prevent these problems, we must rebase all subsequent local
63756
+ * revisions.
63757
+ */
63758
+ this.revisions.rebase(this.pendingMessages[firstPendingRevisionId].nextRevisionId);
63759
+ }
63760
+ this.serverRevisionId = message.nextRevisionId;
63761
+ this.processedRevisions.add(message.nextRevisionId);
63762
+ this.sendPendingMessage();
63763
+ break;
63764
+ }
63573
63765
  }
63574
63766
  }
63575
63767
  isAlreadyProcessed(message) {
@@ -64691,6 +64883,10 @@ stores.inject(MyMetaStore, storeInstance);
64691
64883
  */
64692
64884
  checkZonesAreInSheet(cmd) {
64693
64885
  const sheetId = "sheetId" in cmd ? cmd.sheetId : this.getters.tryGetActiveSheetId();
64886
+ if ("ranges" in cmd &&
64887
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
64888
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
64889
+ }
64694
64890
  const zones = this.getters.getCommandZones(cmd);
64695
64891
  if (!sheetId && zones.length > 0) {
64696
64892
  return "NoActiveSheet" /* CommandResult.NoActiveSheet */;
@@ -65040,23 +65236,23 @@ stores.inject(MyMetaStore, storeInstance);
65040
65236
  function repeatCreateChartCommand(getters, cmd) {
65041
65237
  return {
65042
65238
  ...repeatSheetDependantCommand(getters, cmd),
65043
- id: uuidGenerator.uuidv4(),
65239
+ id: uuidGenerator.smallUuid(),
65044
65240
  };
65045
65241
  }
65046
65242
  function repeatCreateImageCommand(getters, cmd) {
65047
65243
  return {
65048
65244
  ...repeatSheetDependantCommand(getters, cmd),
65049
- figureId: uuidGenerator.uuidv4(),
65245
+ figureId: uuidGenerator.smallUuid(),
65050
65246
  };
65051
65247
  }
65052
65248
  function repeatCreateFigureCommand(getters, cmd) {
65053
65249
  const newCmd = repeatSheetDependantCommand(getters, cmd);
65054
- newCmd.figure.id = uuidGenerator.uuidv4();
65250
+ newCmd.figure.id = uuidGenerator.smallUuid();
65055
65251
  return newCmd;
65056
65252
  }
65057
65253
  function repeatCreateSheetCommand(getters, cmd) {
65058
65254
  const newCmd = deepCopy(cmd);
65059
- newCmd.sheetId = uuidGenerator.uuidv4();
65255
+ newCmd.sheetId = uuidGenerator.smallUuid();
65060
65256
  const sheetName = cmd.name || getters.getSheet(getters.getActiveSheetId()).name;
65061
65257
  // Extract the prefix of the sheet name (everything before the number at the end of the name)
65062
65258
  const namePrefix = sheetName.match(/(.+?)\d*$/)?.[1] || sheetName;
@@ -65245,7 +65441,6 @@ stores.inject(MyMetaStore, storeInstance);
65245
65441
  super(config);
65246
65442
  this.session = config.session;
65247
65443
  this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
65248
- this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
65249
65444
  this.session.on("snapshot", this, () => {
65250
65445
  this.undoStack = [];
65251
65446
  this.redoStack = [];
@@ -65315,10 +65510,6 @@ stores.inject(MyMetaStore, storeInstance);
65315
65510
  const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
65316
65511
  return canRepeatRevision(lastNonRedoRevision);
65317
65512
  }
65318
- drop(revisionIds) {
65319
- this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
65320
- this.redoStack = [];
65321
- }
65322
65513
  onNewLocalStateUpdate({ id }) {
65323
65514
  this.undoStack.push(id);
65324
65515
  this.redoStack = [];
@@ -66519,23 +66710,7 @@ stores.inject(MyMetaStore, storeInstance);
66519
66710
  gridSelection: deepCopy(gridSelection),
66520
66711
  };
66521
66712
  }
66522
- if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
66523
- const currentSheetIds = this.getters.getVisibleSheetIds();
66524
- this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
66525
- if (this.activeSheet.id in this.sheetsData) {
66526
- const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
66527
- this.selectCell(anchor.cell.col, anchor.cell.row);
66528
- }
66529
- else {
66530
- this.selectCell(0, 0);
66531
- }
66532
- const { col, row } = this.gridSelection.anchor.cell;
66533
- this.moveClient({
66534
- sheetId: this.getters.getActiveSheetId(),
66535
- col,
66536
- row,
66537
- });
66538
- }
66713
+ this.fallbackToVisibleSheet();
66539
66714
  const sheetId = this.getters.getActiveSheetId();
66540
66715
  this.gridSelection.zones = this.gridSelection.zones.map((z) => this.getters.expandZone(sheetId, z));
66541
66716
  this.gridSelection.anchor.zone = this.getters.expandZone(sheetId, this.gridSelection.anchor.zone);
@@ -66545,6 +66720,7 @@ stores.inject(MyMetaStore, storeInstance);
66545
66720
  }
66546
66721
  }
66547
66722
  finalize() {
66723
+ this.fallbackToVisibleSheet();
66548
66724
  /** Any change to the selection has to be reflected in the selection processor. */
66549
66725
  this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
66550
66726
  }
@@ -66855,6 +67031,25 @@ stores.inject(MyMetaStore, storeInstance);
66855
67031
  }
66856
67032
  return "Success" /* CommandResult.Success */;
66857
67033
  }
67034
+ fallbackToVisibleSheet() {
67035
+ if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
67036
+ const currentSheetIds = this.getters.getVisibleSheetIds();
67037
+ this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
67038
+ if (this.activeSheet.id in this.sheetsData) {
67039
+ const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
67040
+ this.selectCell(anchor.cell.col, anchor.cell.row);
67041
+ }
67042
+ else {
67043
+ this.selectCell(0, 0);
67044
+ }
67045
+ const { col, row } = this.gridSelection.anchor.cell;
67046
+ this.moveClient({
67047
+ sheetId: this.getters.getActiveSheetId(),
67048
+ col,
67049
+ row,
67050
+ });
67051
+ }
67052
+ }
66858
67053
  //-------------------------------------------
66859
67054
  // Helpers for extensions
66860
67055
  // ------------------------------------------
@@ -68018,7 +68213,9 @@ stores.inject(MyMetaStore, storeInstance);
68018
68213
  case "UNGROUP_HEADERS":
68019
68214
  case "GROUP_HEADERS":
68020
68215
  case "CREATE_SHEET":
68021
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
68216
+ if (this.getters.tryGetSheet(cmd.sheetId)) {
68217
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
68218
+ }
68022
68219
  break;
68023
68220
  case "DUPLICATE_SHEET":
68024
68221
  this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
@@ -68026,12 +68223,14 @@ stores.inject(MyMetaStore, storeInstance);
68026
68223
  }
68027
68224
  }
68028
68225
  finalize() {
68029
- if (this.isDirty) {
68030
- for (const sheetId of this.getters.getSheetIds()) {
68226
+ for (const sheetId of this.getters.getSheetIds()) {
68227
+ // sheets can be created without this plugin being aware of it
68228
+ // in concurrent situations.
68229
+ if (this.isDirty || !this.headerPositions[sheetId]) {
68031
68230
  this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
68032
68231
  }
68033
- this.isDirty = false;
68034
68232
  }
68233
+ this.isDirty = false;
68035
68234
  }
68036
68235
  /**
68037
68236
  * Returns the size, start and end coordinates of a column on an unfolded sheet
@@ -68855,7 +69054,7 @@ stores.inject(MyMetaStore, storeInstance);
68855
69054
  clickAddSheet(ev) {
68856
69055
  const activeSheetId = this.env.model.getters.getActiveSheetId();
68857
69056
  const position = this.env.model.getters.getSheetIds().findIndex((sheetId) => sheetId === activeSheetId) + 1;
68858
- const sheetId = this.env.model.uuidGenerator.uuidv4();
69057
+ const sheetId = this.env.model.uuidGenerator.smallUuid();
68859
69058
  const name = this.env.model.getters.getNextSheetName(_t("Sheet"));
68860
69059
  this.env.model.dispatch("CREATE_SHEET", { sheetId, position, name });
68861
69060
  this.env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
@@ -69870,6 +70069,10 @@ stores.inject(MyMetaStore, storeInstance);
69870
70069
  </svg>
69871
70070
  `;
69872
70071
  css /* scss */ `
70072
+ .o-topbar-composer-container {
70073
+ height: ${TOPBAR_TOOLBAR_HEIGHT}px;
70074
+ }
70075
+
69873
70076
  .o-topbar-composer {
69874
70077
  height: fit-content;
69875
70078
  margin-top: -1px;
@@ -71143,7 +71346,7 @@ stores.inject(MyMetaStore, storeInstance);
71143
71346
  }
71144
71347
  /**
71145
71348
  * Drop the operation and all following operations in every
71146
- * branch
71349
+ * branches
71147
71350
  */
71148
71351
  drop(operationId) {
71149
71352
  for (const branch of this.branches) {
@@ -71448,9 +71651,16 @@ stores.inject(MyMetaStore, storeInstance);
71448
71651
  this.fastForward();
71449
71652
  this.insert(redoId, this.buildEmpty(redoId), insertAfter);
71450
71653
  }
71451
- drop(operationId) {
71654
+ rebase(operationId) {
71655
+ const operation = this.get(operationId);
71656
+ const execution = [...this.tree.execution(this.HEAD_BRANCH).startAfter(operationId)];
71452
71657
  this.revertBefore(operationId);
71658
+ const baseId = this.HEAD_OPERATION.id;
71453
71659
  this.tree.drop(operationId);
71660
+ this.insert(operationId, operation, baseId);
71661
+ for (const { operation } of execution) {
71662
+ this.insert(operation.id, operation.data, this.HEAD_OPERATION.id);
71663
+ }
71454
71664
  }
71455
71665
  /**
71456
71666
  * Revert the state as it was *before* the given operation was executed.
@@ -74561,6 +74771,11 @@ stores.inject(MyMetaStore, storeInstance);
74561
74771
  dispatch: (command) => {
74562
74772
  const result = this.checkDispatchAllowed(command);
74563
74773
  if (!result.isSuccessful) {
74774
+ // core views plugins need to be invalidated
74775
+ this.dispatchToHandlers(this.coreHandlers, {
74776
+ type: "UNDO",
74777
+ commands: [command],
74778
+ });
74564
74779
  return;
74565
74780
  }
74566
74781
  this.isReplayingCommand = true;
@@ -74588,7 +74803,7 @@ stores.inject(MyMetaStore, storeInstance);
74588
74803
  }
74589
74804
  setupConfig(config) {
74590
74805
  const client = config.client || {
74591
- id: this.uuidGenerator.uuidv4(),
74806
+ id: this.uuidGenerator.smallUuid(),
74592
74807
  name: _t("Anonymous").toString(),
74593
74808
  };
74594
74809
  const transportService = config.transportService || new LocalTransportService();
@@ -75111,9 +75326,9 @@ stores.inject(MyMetaStore, storeInstance);
75111
75326
  exports.tokenize = tokenize;
75112
75327
 
75113
75328
 
75114
- __info__.version = "18.1.7";
75115
- __info__.date = "2025-02-10T09:00:28.556Z";
75116
- __info__.hash = "338d8a1";
75329
+ __info__.version = "18.1.9";
75330
+ __info__.date = "2025-02-25T05:59:45.472Z";
75331
+ __info__.hash = "6789c1c";
75117
75332
 
75118
75333
 
75119
75334
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);