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