@odoo/o-spreadsheet 17.0.49 → 17.0.51

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 17.0.49
6
- * @date 2025-02-10T08:55:47.771Z
7
- * @hash 1049319
5
+ * @version 17.0.51
6
+ * @date 2025-02-25T05:56:10.869Z
7
+ * @hash b433912
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -1909,12 +1909,12 @@
1909
1909
  function isRangeDependant(cmd) {
1910
1910
  return "ranges" in cmd;
1911
1911
  }
1912
- function isZoneDependent(cmd) {
1913
- return "zone" in cmd;
1914
- }
1915
1912
  function isPositionDependent(cmd) {
1916
1913
  return "col" in cmd && "row" in cmd && "sheetId" in cmd;
1917
1914
  }
1915
+ function isZoneDependent(cmd) {
1916
+ return "sheetId" in cmd && "zone" in cmd;
1917
+ }
1918
1918
  const invalidateEvaluationCommands = new Set([
1919
1919
  "RENAME_SHEET",
1920
1920
  "DELETE_SHEET",
@@ -1924,6 +1924,7 @@
1924
1924
  "UNDO",
1925
1925
  "REDO",
1926
1926
  "ADD_MERGE",
1927
+ "DUPLICATE_SHEET",
1927
1928
  "UPDATE_LOCALE",
1928
1929
  ]);
1929
1930
  const invalidateDependenciesCommands = new Set([
@@ -1932,7 +1933,6 @@
1932
1933
  ]);
1933
1934
  const invalidateCFEvaluationCommands = new Set([
1934
1935
  ...invalidateEvaluationCommands,
1935
- "DUPLICATE_SHEET",
1936
1936
  "EVALUATE_CELLS",
1937
1937
  "ADD_CONDITIONAL_FORMAT",
1938
1938
  "REMOVE_CONDITIONAL_FORMAT",
@@ -2092,6 +2092,7 @@
2092
2092
  CommandResult["InvalidRange"] = "InvalidRange";
2093
2093
  CommandResult["InvalidZones"] = "InvalidZones";
2094
2094
  CommandResult["InvalidSheetId"] = "InvalidSheetId";
2095
+ CommandResult["InvalidCellId"] = "InvalidCellId";
2095
2096
  CommandResult["InvalidFigureId"] = "InvalidFigureId";
2096
2097
  CommandResult["InputAlreadyFocused"] = "InputAlreadyFocused";
2097
2098
  CommandResult["MaximumRangesReached"] = "MaximumRangesReached";
@@ -3672,17 +3673,20 @@
3672
3673
  */
3673
3674
  function toUnboundedZone(xc) {
3674
3675
  const zone = toZoneWithoutBoundaryChanges(xc);
3676
+ reorderZone(zone);
3677
+ return zone;
3678
+ }
3679
+ function reorderZone(zone) {
3675
3680
  if (zone.right !== undefined && zone.right < zone.left) {
3676
- const tmp = zone.left;
3681
+ const right = zone.left;
3677
3682
  zone.left = zone.right;
3678
- zone.right = tmp;
3683
+ zone.right = right;
3679
3684
  }
3680
3685
  if (zone.bottom !== undefined && zone.bottom < zone.top) {
3681
- const tmp = zone.top;
3686
+ const bottom = zone.top;
3682
3687
  zone.top = zone.bottom;
3683
- zone.bottom = tmp;
3688
+ zone.bottom = bottom;
3684
3689
  }
3685
- return zone;
3686
3690
  }
3687
3691
  /**
3688
3692
  * Convert from a cartesian reference to a Zone.
@@ -4715,20 +4719,53 @@
4715
4719
  setIsFastStrategy(isFast) {
4716
4720
  this.isFastIdStrategy = isFast;
4717
4721
  }
4722
+ /**
4723
+ * Generates a custom UUID using a simple 26^8 method (8-character alphanumeric string with lowercase letters)
4724
+ * This has a higher chance of collision than a UUIDv4, but not only faster to generate than an UUIDV4,
4725
+ * it also has a smaller size, which is preferable to alleviate the overall data size.
4726
+ *
4727
+ * This method is preferable when generating uuids for the core data (sheetId, figureId, etc)
4728
+ * as they will appear several times in the revisions and local history.
4729
+ *
4730
+ */
4731
+ smallUuid() {
4732
+ if (this.isFastIdStrategy) {
4733
+ this.fastIdStart++;
4734
+ return String(this.fastIdStart);
4735
+ }
4736
+ else if (window.crypto) {
4737
+ return "10000000-1000".replace(/[01]/g, (c) => {
4738
+ const n = Number(c);
4739
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
4740
+ });
4741
+ }
4742
+ else {
4743
+ // mainly for jest and other browsers that do not have the crypto functionality
4744
+ return "xxxxxxxx-xxxx".replace(/[xy]/g, function (c) {
4745
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
4746
+ return v.toString(16);
4747
+ });
4748
+ }
4749
+ }
4750
+ /**
4751
+ * Generates an UUIDV4, has astronomically low chance of collision, but is larger in size than the smallUuid.
4752
+ * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
4753
+ */
4718
4754
  uuidv4() {
4719
4755
  if (this.isFastIdStrategy) {
4720
4756
  this.fastIdStart++;
4721
4757
  return String(this.fastIdStart);
4722
- //@ts-ignore
4723
4758
  }
4724
- else if (window.crypto && window.crypto.getRandomValues) {
4725
- //@ts-ignore
4726
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
4759
+ else if (window.crypto) {
4760
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
4761
+ const n = Number(c);
4762
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
4763
+ });
4727
4764
  }
4728
4765
  else {
4729
4766
  // mainly for jest and other browsers that do not have the crypto functionality
4730
4767
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
4731
- var r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
4768
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
4732
4769
  return v.toString(16);
4733
4770
  });
4734
4771
  }
@@ -6949,10 +6986,6 @@
6949
6986
  this.currentDisplayValue = newDisplay;
6950
6987
  if (!anchor)
6951
6988
  return;
6952
- el.style.top = "";
6953
- el.style.left = "";
6954
- el.style["max-height"] = "";
6955
- el.style["max-width"] = "";
6956
6989
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
6957
6990
  let elDims = {
6958
6991
  width: el.getBoundingClientRect().width,
@@ -7941,7 +7974,7 @@
7941
7974
  const deleteSheet = {
7942
7975
  name: _t("Delete"),
7943
7976
  isVisible: (env) => {
7944
- return env.model.getters.getSheetIds().length > 1;
7977
+ return env.model.getters.getVisibleSheetIds().length > 1;
7945
7978
  },
7946
7979
  execute: (env) => env.askConfirmation(_t("Are you sure you want to delete this sheet?"), () => {
7947
7980
  env.model.dispatch("DELETE_SHEET", { sheetId: env.model.getters.getActiveSheetId() });
@@ -7951,7 +7984,7 @@
7951
7984
  name: _t("Duplicate"),
7952
7985
  execute: (env) => {
7953
7986
  const sheetIdFrom = env.model.getters.getActiveSheetId();
7954
- const sheetIdTo = env.model.uuidGenerator.uuidv4();
7987
+ const sheetIdTo = env.model.uuidGenerator.smallUuid();
7955
7988
  env.model.dispatch("DUPLICATE_SHEET", {
7956
7989
  sheetId: sheetIdFrom,
7957
7990
  sheetIdTo,
@@ -8501,17 +8534,15 @@
8501
8534
  plugins: [],
8502
8535
  };
8503
8536
  }
8504
- function getChartLabelFormat(getters, range) {
8537
+ function getChartLabelFormat(getters, range, shouldRemoveFirstLabel) {
8505
8538
  if (!range)
8506
8539
  return undefined;
8507
- const { sheetId, zone: { left, top, bottom }, } = range;
8508
- for (let row = top; row <= bottom; row++) {
8509
- const format = getters.getEvaluatedCell({ sheetId, col: left, row }).format;
8510
- if (format) {
8511
- return format;
8512
- }
8540
+ const { sheetId, zone } = range;
8541
+ const formats = positions(zone).map((position) => getters.getEvaluatedCell({ sheetId, ...position }).format);
8542
+ if (shouldRemoveFirstLabel) {
8543
+ formats.shift();
8513
8544
  }
8514
- return undefined;
8545
+ return formats.find((format) => format !== undefined);
8515
8546
  }
8516
8547
  function getChartLabelValues(getters, dataSets, labelRange) {
8517
8548
  let labels = { values: [], formattedValues: [] };
@@ -8792,9 +8823,7 @@
8792
8823
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
8793
8824
  let labels = labelValues.formattedValues;
8794
8825
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
8795
- if (chart.dataSetsHaveTitle &&
8796
- dataSetsValues[0] &&
8797
- labels.length > dataSetsValues[0].data.length) {
8826
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
8798
8827
  labels.shift();
8799
8828
  }
8800
8829
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -9338,8 +9367,8 @@
9338
9367
  }
9339
9368
  return { labels: newLabels, dataSetsValues: newDatasets };
9340
9369
  }
9341
- function canChartParseLabels(labelRange, getters) {
9342
- return canBeDateChart(labelRange, getters) || canBeLinearChart(labelRange, getters);
9370
+ function canChartParseLabels(chart, getters) {
9371
+ return canBeDateChart(chart, getters) || canBeLinearChart(chart, getters);
9343
9372
  }
9344
9373
  function getChartAxisType(chart, getters) {
9345
9374
  if (isDateChart(chart, getters) && isLuxonTimeAdapterInstalled()) {
@@ -9351,23 +9380,26 @@
9351
9380
  return "category";
9352
9381
  }
9353
9382
  function isDateChart(chart, getters) {
9354
- return !chart.labelsAsText && canBeDateChart(chart.labelRange, getters);
9383
+ return !chart.labelsAsText && canBeDateChart(chart, getters);
9355
9384
  }
9356
9385
  function isLinearChart(chart, getters) {
9357
- return !chart.labelsAsText && canBeLinearChart(chart.labelRange, getters);
9386
+ return !chart.labelsAsText && canBeLinearChart(chart, getters);
9358
9387
  }
9359
- function canBeDateChart(labelRange, getters) {
9360
- if (!labelRange || !canBeLinearChart(labelRange, getters)) {
9388
+ function canBeDateChart(chart, getters) {
9389
+ if (!chart.labelRange || !canBeLinearChart(chart, getters)) {
9361
9390
  return false;
9362
9391
  }
9363
- const labelFormat = getChartLabelFormat(getters, labelRange);
9392
+ const labelFormat = getChartLabelFormat(getters, chart.labelRange, shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle));
9364
9393
  return Boolean(labelFormat && timeFormatLuxonCompatible.test(labelFormat));
9365
9394
  }
9366
- function canBeLinearChart(labelRange, getters) {
9367
- if (!labelRange) {
9395
+ function canBeLinearChart(chart, getters) {
9396
+ if (!chart.labelRange) {
9368
9397
  return false;
9369
9398
  }
9370
- const labels = getters.getRangeValues(labelRange);
9399
+ const labels = getters.getRangeValues(chart.labelRange);
9400
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
9401
+ labels.shift();
9402
+ }
9371
9403
  if (labels.some((label) => isNaN(Number(label)) && label)) {
9372
9404
  return false;
9373
9405
  }
@@ -9441,9 +9473,8 @@
9441
9473
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
9442
9474
  let labels = axisType === "linear" ? labelValues.values : labelValues.formattedValues;
9443
9475
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
9444
- if (chart.dataSetsHaveTitle &&
9445
- dataSetsValues[0] &&
9446
- labels.length > dataSetsValues[0].data.length) {
9476
+ const removeFirstLabel = shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle);
9477
+ if (removeFirstLabel) {
9447
9478
  labels.shift();
9448
9479
  }
9449
9480
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -9458,7 +9489,7 @@
9458
9489
  const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
9459
9490
  const options = { format: dataSetFormat, locale, truncateLabels };
9460
9491
  const config = getLineConfiguration(chart, labels, options);
9461
- const labelFormat = getChartLabelFormat(getters, chart.labelRange);
9492
+ const labelFormat = getChartLabelFormat(getters, chart.labelRange, removeFirstLabel);
9462
9493
  if (axisType === "time") {
9463
9494
  const axis = {
9464
9495
  type: "time",
@@ -9680,9 +9711,7 @@
9680
9711
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
9681
9712
  let labels = labelValues.formattedValues;
9682
9713
  let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
9683
- if (chart.dataSetsHaveTitle &&
9684
- dataSetsValues[0] &&
9685
- labels.length > dataSetsValues[0].data.length) {
9714
+ if (shouldRemoveFirstLabel(chart.labelRange, chart.dataSets[0], chart.dataSetsHaveTitle)) {
9686
9715
  labels.shift();
9687
9716
  }
9688
9717
  ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
@@ -9698,7 +9727,7 @@
9698
9727
  const dataset = {
9699
9728
  label,
9700
9729
  data,
9701
- borderColor: "#FFFFFF",
9730
+ borderColor: chart.background || "#FFFFFF",
9702
9731
  backgroundColor,
9703
9732
  };
9704
9733
  config.data.datasets.push(dataset);
@@ -10203,21 +10232,22 @@
10203
10232
  }
10204
10233
  // Only display legend for several datasets.
10205
10234
  const newLegendPos = dataSetZone.right === dataSetZone.left ? "none" : "top";
10206
- const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
10207
- if (canChartParseLabels(labelRange, getters)) {
10208
- return {
10209
- title,
10210
- dataSets,
10211
- labelsAsText: false,
10212
- stacked: false,
10213
- aggregated: false,
10214
- cumulative: false,
10215
- labelRange: labelRangeXc,
10216
- type: "line",
10217
- dataSetsHaveTitle,
10218
- verticalAxisPosition: "left",
10219
- legendPosition: newLegendPos,
10220
- };
10235
+ const lineChartDefinition = {
10236
+ title,
10237
+ dataSets,
10238
+ labelsAsText: false,
10239
+ stacked: false,
10240
+ aggregated: false,
10241
+ cumulative: false,
10242
+ labelRange: labelRangeXc,
10243
+ type: "line",
10244
+ dataSetsHaveTitle,
10245
+ verticalAxisPosition: "left",
10246
+ legendPosition: newLegendPos,
10247
+ };
10248
+ const chart = new LineChart(lineChartDefinition, sheetId, getters);
10249
+ if (canChartParseLabels(chart, getters)) {
10250
+ return lineChartDefinition;
10221
10251
  }
10222
10252
  return {
10223
10253
  title,
@@ -10817,7 +10847,7 @@
10817
10847
  //------------------------------------------------------------------------------
10818
10848
  const CREATE_CHART = (env) => {
10819
10849
  const getters = env.model.getters;
10820
- const id = env.model.uuidGenerator.uuidv4();
10850
+ const id = env.model.uuidGenerator.smallUuid();
10821
10851
  const sheetId = getters.getActiveSheetId();
10822
10852
  if (getZoneArea(env.model.getters.getSelectedZone()) === 1) {
10823
10853
  env.model.selection.selectTableAroundSelection();
@@ -10851,7 +10881,7 @@
10851
10881
  const CREATE_IMAGE = async (env) => {
10852
10882
  if (env.imageProvider) {
10853
10883
  const sheetId = env.model.getters.getActiveSheetId();
10854
- const figureId = env.model.uuidGenerator.uuidv4();
10884
+ const figureId = env.model.uuidGenerator.smallUuid();
10855
10885
  const image = await requestImage(env);
10856
10886
  if (!image) {
10857
10887
  throw new Error("No image provider was given to the environment");
@@ -12572,6 +12602,26 @@
12572
12602
  isExported: true,
12573
12603
  };
12574
12604
  // -----------------------------------------------------------------------------
12605
+ // LOG
12606
+ // -----------------------------------------------------------------------------
12607
+ const LOG = {
12608
+ description: _t("The logarithm of a number, for a given base."),
12609
+ args: [
12610
+ arg("value (number)", _t("The value for which to calculate the logarithm.")),
12611
+ arg("base (number, default=10)", _t("The base of the logarithm.")),
12612
+ ],
12613
+ returns: ["NUMBER"],
12614
+ compute: function (value, base = 10) {
12615
+ const _value = toNumber(value, this.locale);
12616
+ const _base = toNumber(base, this.locale);
12617
+ assert(() => _value > 0, _t("The value (%s) must be strictly positive.", _value.toString()));
12618
+ assert(() => _base > 0, _t("The base (%s) must be strictly positive.", _base.toString()));
12619
+ assert(() => _base !== 1, _t("The base must be different from 1."));
12620
+ return Math.log10(_value) / Math.log10(_base);
12621
+ },
12622
+ isExported: true,
12623
+ };
12624
+ // -----------------------------------------------------------------------------
12575
12625
  // MOD
12576
12626
  // -----------------------------------------------------------------------------
12577
12627
  const MOD = {
@@ -13092,6 +13142,7 @@
13092
13142
  ISODD: ISODD,
13093
13143
  ISO_CEILING: ISO_CEILING,
13094
13144
  LN: LN,
13145
+ LOG: LOG,
13095
13146
  MOD: MOD,
13096
13147
  MUNIT: MUNIT,
13097
13148
  ODD: ODD,
@@ -15953,7 +16004,7 @@
15953
16004
  }
15954
16005
  }
15955
16006
  },
15956
- isExported: true,
16007
+ isExported: false,
15957
16008
  };
15958
16009
  // -----------------------------------------------------------------------------
15959
16010
  // UNIQUE
@@ -19854,7 +19905,7 @@
19854
19905
  execute: (env) => {
19855
19906
  const activeSheetId = env.model.getters.getActiveSheetId();
19856
19907
  const position = env.model.getters.getSheetIds().indexOf(activeSheetId) + 1;
19857
- const sheetId = env.model.uuidGenerator.uuidv4();
19908
+ const sheetId = env.model.uuidGenerator.smallUuid();
19858
19909
  env.model.dispatch("CREATE_SHEET", { sheetId, position });
19859
19910
  env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
19860
19911
  },
@@ -21530,7 +21581,7 @@
21530
21581
  */
21531
21582
  class SelectionInput extends owl.Component {
21532
21583
  static template = "o-spreadsheet-SelectionInput";
21533
- id = uuidGenerator$1.uuidv4();
21584
+ id = uuidGenerator$1.smallUuid();
21534
21585
  previousRanges = this.props.ranges() || [];
21535
21586
  originSheet = this.env.model.getters.getActiveSheetId();
21536
21587
  state = owl.useState({
@@ -22580,7 +22631,7 @@
22580
22631
  get canTreatLabelsAsText() {
22581
22632
  const chart = this.env.model.getters.getChart(this.props.figureId);
22582
22633
  if (chart && chart instanceof LineChart) {
22583
- return canChartParseLabels(chart.labelRange, this.env.model.getters);
22634
+ return canChartParseLabels(chart, this.env.model.getters);
22584
22635
  }
22585
22636
  return false;
22586
22637
  }
@@ -23608,7 +23659,7 @@
23608
23659
  state;
23609
23660
  setup() {
23610
23661
  const cf = this.props.editedCf || {
23611
- id: this.env.model.uuidGenerator.uuidv4(),
23662
+ id: this.env.model.uuidGenerator.smallUuid(),
23612
23663
  ranges: this.env.model.getters
23613
23664
  .getSelectedZones()
23614
23665
  .map((zone) => this.env.model.getters.zoneToXC(this.env.model.getters.getActiveSheetId(), zone)),
@@ -25557,7 +25608,7 @@
25557
25608
  .getSelectedZones()
25558
25609
  .map((zone) => zoneToXc(this.env.model.getters.getUnboundedZone(sheetId, zone)));
25559
25610
  return {
25560
- id: this.env.model.uuidGenerator.uuidv4(),
25611
+ id: this.env.model.uuidGenerator.smallUuid(),
25561
25612
  criterion: { type: "textContains", values: [""] },
25562
25613
  ranges,
25563
25614
  };
@@ -26708,25 +26759,21 @@
26708
26759
  }
26709
26760
  }
26710
26761
  }
26762
+ }
26763
+ .o-spreadsheet .o-composer-assistant {
26764
+ pointer-events: none;
26711
26765
 
26712
- .o-composer-assistant {
26713
- position: absolute;
26714
- margin: 1px 4px;
26715
- pointer-events: none;
26716
- overflow: auto;
26717
-
26718
- .o-semi-bold {
26719
- /** FIXME: to remove in favor of Bootstrap
26720
- * 'fw-semibold' when we upgrade to Bootstrap 5.2
26721
- */
26722
- font-weight: 600 !important;
26723
- }
26766
+ .o-semi-bold {
26767
+ /** FIXME: to remove in favor of Bootstrap
26768
+ * 'fw-semibold' when we upgrade to Bootstrap 5.2
26769
+ */
26770
+ font-weight: 600 !important;
26724
26771
  }
26725
26772
  }
26726
26773
  `;
26727
26774
  class Composer extends owl.Component {
26728
26775
  static template = "o-spreadsheet-Composer";
26729
- static components = { TextValueProvider, FunctionDescriptionProvider };
26776
+ static components = { TextValueProvider, FunctionDescriptionProvider, Popover };
26730
26777
  static defaultProps = {
26731
26778
  inputStyle: "",
26732
26779
  isDefaultFocus: false,
@@ -26751,35 +26798,13 @@
26751
26798
  argToFocus: 0,
26752
26799
  });
26753
26800
  compositionActive = false;
26754
- spreadsheetRect = useSpreadsheetRect();
26755
26801
  get assistantStyle() {
26756
- const composerRect = this.composerRef.el.getBoundingClientRect();
26757
26802
  const assistantStyle = {};
26758
- assistantStyle["min-width"] = `${this.props.rect?.width || ASSISTANT_WIDTH}px`;
26803
+ const minWidth = Math.min(getBoundingRectAsPOJO(this.composerRef.el).width || Infinity, ASSISTANT_WIDTH);
26804
+ assistantStyle["min-width"] = `${minWidth}px`;
26759
26805
  if (this.autoCompleteState.type === "function") {
26760
26806
  assistantStyle.width = `${ASSISTANT_WIDTH}px`;
26761
26807
  }
26762
- if (this.props.delimitation && this.props.rect) {
26763
- const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
26764
- const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
26765
- assistantStyle["max-height"] = `${remainingHeight}px`;
26766
- if (cellY > remainingHeight) {
26767
- const availableSpaceAbove = cellY;
26768
- assistantStyle["max-height"] = `${availableSpaceAbove}px`;
26769
- // render top
26770
- // We compensate 2 px of margin on the assistant style + 1px for design reasons
26771
- assistantStyle.top = `-3px`;
26772
- assistantStyle.transform = `translate(0, -100%)`;
26773
- }
26774
- if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
26775
- // render left
26776
- assistantStyle.right = `0px`;
26777
- }
26778
- }
26779
- if (this.props.focus === "contentFocus" &&
26780
- composerRect.left + ASSISTANT_WIDTH > this.spreadsheetRect.width) {
26781
- assistantStyle.right = "0px";
26782
- }
26783
26808
  return cssPropertiesToCss(assistantStyle);
26784
26809
  }
26785
26810
  // we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
@@ -27028,7 +27053,7 @@
27028
27053
  this.autoCompleteState.selectedIndex = 0;
27029
27054
  }
27030
27055
  updateAutoCompleteIndex(index) {
27031
- this.autoCompleteState.selectedIndex = clip(0, index, 10);
27056
+ this.autoCompleteState.selectedIndex = index;
27032
27057
  }
27033
27058
  /**
27034
27059
  * This is required to ensure the content helper selection is
@@ -27316,11 +27341,16 @@
27316
27341
  this.autoCompleteState.values = values.map((value) => ({ text: value, description: "" }));
27317
27342
  this.autoCompleteState.getHtmlContent = (value) => [{ value }];
27318
27343
  }
27344
+ get popoverProps() {
27345
+ return {
27346
+ anchorRect: getBoundingRectAsPOJO(this.composerRef.el),
27347
+ positioning: "BottomLeft",
27348
+ verticalOffset: 0,
27349
+ };
27350
+ }
27319
27351
  }
27320
27352
  Composer.props = {
27321
27353
  inputStyle: { type: String, optional: true },
27322
- rect: { type: Object, optional: true },
27323
- delimitation: { type: Object, optional: true },
27324
27354
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27325
27355
  onComposerCellFocused: { type: Function, optional: true },
27326
27356
  onComposerContentFocused: Function,
@@ -27390,13 +27420,7 @@
27390
27420
  });
27391
27421
  }
27392
27422
  get composerProps() {
27393
- const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27394
27423
  return {
27395
- rect: { ...this.rect },
27396
- delimitation: {
27397
- width,
27398
- height,
27399
- },
27400
27424
  focus: this.props.focus,
27401
27425
  isDefaultFocus: true,
27402
27426
  onComposerContentFocused: this.props.onComposerContentFocused,
@@ -33843,7 +33867,7 @@
33843
33867
  * a breaking change is made in the way the state is handled, and an upgrade
33844
33868
  * function should be defined
33845
33869
  */
33846
- const CURRENT_VERSION = 14;
33870
+ const CURRENT_VERSION = 14.5;
33847
33871
  const INITIAL_SHEET_ID = "Sheet1";
33848
33872
  /**
33849
33873
  * This function tries to load anything that could look like a valid
@@ -34177,7 +34201,7 @@
34177
34201
  for (const sheet of data.sheets || []) {
34178
34202
  for (const figure of sheet.figures || []) {
34179
34203
  if (figureIds.has(figure.id)) {
34180
- figure.id += uuidGenerator.uuidv4();
34204
+ figure.id += uuidGenerator.smallUuid();
34181
34205
  }
34182
34206
  figureIds.add(figure.id);
34183
34207
  }
@@ -35853,6 +35877,10 @@
35853
35877
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35854
35878
  case "CLEAR_CELL":
35855
35879
  return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35880
+ case "UPDATE_CELL_POSITION":
35881
+ return !cmd.cellId || this.cells[cmd.sheetId]?.[cmd.cellId]
35882
+ ? "Success" /* CommandResult.Success */
35883
+ : "InvalidCellId" /* CommandResult.InvalidCellId */;
35856
35884
  default:
35857
35885
  return "Success" /* CommandResult.Success */;
35858
35886
  }
@@ -35894,6 +35922,9 @@
35894
35922
  case "CLEAR_CELLS":
35895
35923
  this.clearCells(cmd.sheetId, cmd.target);
35896
35924
  break;
35925
+ case "DELETE_SHEET": {
35926
+ this.history.update("cells", cmd.sheetId, undefined);
35927
+ }
35897
35928
  }
35898
35929
  }
35899
35930
  /**
@@ -36972,8 +37003,14 @@
36972
37003
  allowDispatch(cmd) {
36973
37004
  switch (cmd.type) {
36974
37005
  case "ADD_DATA_VALIDATION_RULE":
37006
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
37007
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
37008
+ }
36975
37009
  return this.checkValidations(cmd, this.chainValidations(this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
36976
37010
  case "REMOVE_DATA_VALIDATION_RULE":
37011
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
37012
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
37013
+ }
36977
37014
  if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
36978
37015
  return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
36979
37016
  }
@@ -37147,6 +37184,7 @@
37147
37184
  class FigurePlugin extends CorePlugin {
37148
37185
  static getters = ["getFigures", "getFigure", "getFigureSheetId"];
37149
37186
  figures = {};
37187
+ insertionOrders = []; // TODO use a list in master
37150
37188
  // ---------------------------------------------------------------------------
37151
37189
  // Command Handling
37152
37190
  // ---------------------------------------------------------------------------
@@ -37249,11 +37287,14 @@
37249
37287
  }
37250
37288
  addFigure(figure, sheetId) {
37251
37289
  this.history.update("figures", sheetId, figure.id, figure);
37290
+ this.history.update("insertionOrders", this.insertionOrders.length, figure.id);
37252
37291
  }
37253
37292
  deleteSheet(sheetId) {
37293
+ this.history.update("insertionOrders", this.insertionOrders.filter((id) => !this.figures[sheetId]?.[id]));
37254
37294
  this.history.update("figures", sheetId, undefined);
37255
37295
  }
37256
37296
  removeFigure(id, sheetId) {
37297
+ this.history.update("insertionOrders", this.insertionOrders.filter((figureId) => figureId !== id));
37257
37298
  this.history.update("figures", sheetId, id, undefined);
37258
37299
  }
37259
37300
  checkFigureExists(sheetId, figureId) {
@@ -37272,7 +37313,14 @@
37272
37313
  // Getters
37273
37314
  // ---------------------------------------------------------------------------
37274
37315
  getFigures(sheetId) {
37275
- return Object.values(this.figures[sheetId] || {}).filter(isDefined$1);
37316
+ const figures = [];
37317
+ for (const figureId of this.insertionOrders) {
37318
+ const figure = this.figures[sheetId]?.[figureId];
37319
+ if (figure) {
37320
+ figures.push(figure);
37321
+ }
37322
+ }
37323
+ return figures;
37276
37324
  }
37277
37325
  getFigure(sheetId, figureId) {
37278
37326
  return this.figures[sheetId]?.[figureId];
@@ -37285,11 +37333,9 @@
37285
37333
  // ---------------------------------------------------------------------------
37286
37334
  import(data) {
37287
37335
  for (let sheet of data.sheets) {
37288
- const figures = {};
37289
- sheet.figures.forEach((figure) => {
37290
- figures[figure.id] = figure;
37291
- });
37292
- this.figures[sheet.id] = figures;
37336
+ for (const figure of sheet.figures) {
37337
+ this.addFigure(figure, sheet.id);
37338
+ }
37293
37339
  }
37294
37340
  }
37295
37341
  export(data) {
@@ -37313,10 +37359,10 @@
37313
37359
  this.filters = [];
37314
37360
  this.zone = zone;
37315
37361
  const uuid = new UuidGenerator();
37316
- this.id = uuid.uuidv4();
37362
+ this.id = uuid.smallUuid();
37317
37363
  for (const i of range(zone.left, zone.right + 1)) {
37318
37364
  const filterZone = { ...this.zone, left: i, right: i };
37319
- this.filters.push(new Filter(uuid.uuidv4(), filterZone));
37365
+ this.filters.push(new Filter(uuid.smallUuid(), filterZone));
37320
37366
  }
37321
37367
  }
37322
37368
  /** Get zone of the table without the headers */
@@ -37518,7 +37564,7 @@
37518
37564
  if (filters.length < zoneToDimension(zone).numberOfCols) {
37519
37565
  for (let col = zone.left; col <= zone.right; col++) {
37520
37566
  if (!filters.find((filter) => filter.col === col)) {
37521
- filters.push(new Filter(this.uuidGenerator.uuidv4(), { ...zone, left: col, right: col }));
37567
+ filters.push(new Filter(this.uuidGenerator.smallUuid(), { ...zone, left: col, right: col }));
37522
37568
  }
37523
37569
  }
37524
37570
  filters.sort((f1, f2) => f1.col - f2.col);
@@ -39007,6 +39053,9 @@
39007
39053
  case "CREATE_SHEET": {
39008
39054
  return this.checkValidations(cmd, this.checkSheetName, this.checkSheetPosition);
39009
39055
  }
39056
+ case "DUPLICATE_SHEET": {
39057
+ return this.sheets[cmd.sheetIdTo] ? "DuplicatedSheetId" /* CommandResult.DuplicatedSheetId */ : "Success" /* CommandResult.Success */;
39058
+ }
39010
39059
  case "MOVE_SHEET":
39011
39060
  try {
39012
39061
  const currentIndex = this.orderedSheetIds.findIndex((id) => id === cmd.sheetId);
@@ -39019,7 +39068,7 @@
39019
39068
  case "RENAME_SHEET":
39020
39069
  return this.isRenameAllowed(cmd);
39021
39070
  case "DELETE_SHEET":
39022
- return this.orderedSheetIds.length > 1
39071
+ return this.getVisibleSheetIds().length > 1
39023
39072
  ? "Success" /* CommandResult.Success */
39024
39073
  : "NotEnoughSheets" /* CommandResult.NotEnoughSheets */;
39025
39074
  case "ADD_COLUMNS_ROWS":
@@ -39832,6 +39881,10 @@
39832
39881
  checkZonesAreInSheet(cmd) {
39833
39882
  if (!("sheetId" in cmd))
39834
39883
  return "Success" /* CommandResult.Success */;
39884
+ if ("ranges" in cmd &&
39885
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
39886
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39887
+ }
39835
39888
  return this.checkZonesExistInSheet(cmd.sheetId, this.getCommandZones(cmd));
39836
39889
  }
39837
39890
  }
@@ -39851,7 +39904,10 @@
39851
39904
  allowDispatch(cmd) {
39852
39905
  switch (cmd.type) {
39853
39906
  case "GROUP_HEADERS": {
39854
- const { start, end } = cmd;
39907
+ const { start, end, sheetId } = cmd;
39908
+ if (!this.getters.tryGetSheet(sheetId)) {
39909
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39910
+ }
39855
39911
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
39856
39912
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
39857
39913
  }
@@ -39864,7 +39920,10 @@
39864
39920
  break;
39865
39921
  }
39866
39922
  case "UNGROUP_HEADERS": {
39867
- const { start, end } = cmd;
39923
+ const { start, end, sheetId } = cmd;
39924
+ if (!this.getters.tryGetSheet(sheetId)) {
39925
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39926
+ }
39868
39927
  if (!this.getters.doesHeadersExist(cmd.sheetId, cmd.dimension, [start, end])) {
39869
39928
  return "InvalidHeaderGroupStartEnd" /* CommandResult.InvalidHeaderGroupStartEnd */;
39870
39929
  }
@@ -39875,6 +39934,9 @@
39875
39934
  }
39876
39935
  case "UNFOLD_HEADER_GROUP":
39877
39936
  case "FOLD_HEADER_GROUP":
39937
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
39938
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
39939
+ }
39878
39940
  const group = this.findGroupWithStartEnd(cmd.sheetId, cmd.dimension, cmd.start, cmd.end);
39879
39941
  if (!group) {
39880
39942
  return "UnknownHeaderGroup" /* CommandResult.UnknownHeaderGroup */;
@@ -44144,6 +44206,9 @@
44144
44206
  };
44145
44207
  }
44146
44208
  function createSheetTransformation(toTransform, executed) {
44209
+ if (toTransform.sheetId === executed.sheetId) {
44210
+ toTransform = { ...toTransform, sheetId: `${toTransform.sheetId}~` };
44211
+ }
44147
44212
  if (toTransform.name === executed.name) {
44148
44213
  return {
44149
44214
  ...toTransform,
@@ -44780,21 +44845,14 @@
44780
44845
  if (!message)
44781
44846
  return;
44782
44847
  if (message.type === "REMOTE_REVISION") {
44783
- const revision = this.revisions.get(message.nextRevisionId);
44848
+ let revision = this.revisions.get(message.nextRevisionId);
44784
44849
  if (revision.commands.length === 0) {
44785
44850
  /**
44786
- * The command is empty, we have to drop all the next local revisions
44851
+ * The command is empty, we have to rebase all the next local revisions
44787
44852
  * to avoid issues with undo/redo
44788
44853
  */
44789
- this.revisions.drop(revision.id);
44790
- const revisionIds = this.pendingMessages
44791
- .filter((message) => message.type === "REMOTE_REVISION")
44792
- .map((message) => message.nextRevisionId);
44793
- this.trigger("pending-revisions-dropped", { revisionIds });
44794
- this.waitingAck = false;
44795
- this.waitingUndoRedoAck = false;
44796
- this.pendingMessages = [];
44797
- return;
44854
+ this.revisions.rebase(revision.id);
44855
+ revision = this.revisions.get(message.nextRevisionId);
44798
44856
  }
44799
44857
  message = {
44800
44858
  ...message,
@@ -44818,7 +44876,6 @@
44818
44876
  switch (message.type) {
44819
44877
  case "REMOTE_REVISION":
44820
44878
  case "REVISION_REDONE":
44821
- case "REVISION_UNDONE":
44822
44879
  case "SNAPSHOT_CREATED":
44823
44880
  this.waitingAck = false;
44824
44881
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
@@ -44826,6 +44883,25 @@
44826
44883
  this.processedRevisions.add(message.nextRevisionId);
44827
44884
  this.sendPendingMessage();
44828
44885
  break;
44886
+ case "REVISION_UNDONE": {
44887
+ this.waitingAck = false;
44888
+ this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
44889
+ const firstPendingRevisionId = this.pendingMessages.findIndex((message) => message.type === "REMOTE_REVISION");
44890
+ if (firstPendingRevisionId !== -1) {
44891
+ /**
44892
+ * Some revisions undergo transformations that may cause issues with
44893
+ * undo/redo if the transformation is destructive (we don't get back
44894
+ * the original command by transforming it with the inverse).
44895
+ * To prevent these problems, we must rebase all subsequent local
44896
+ * revisions.
44897
+ */
44898
+ this.revisions.rebase(this.pendingMessages[firstPendingRevisionId].nextRevisionId);
44899
+ }
44900
+ this.serverRevisionId = message.nextRevisionId;
44901
+ this.processedRevisions.add(message.nextRevisionId);
44902
+ this.sendPendingMessage();
44903
+ break;
44904
+ }
44829
44905
  }
44830
44906
  }
44831
44907
  isAlreadyProcessed(message) {
@@ -45530,7 +45606,9 @@
45530
45606
  const cfInTarget = this.getters
45531
45607
  .getConditionalFormats(targetSheetId)
45532
45608
  .find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
45533
- return cfInTarget ? cfInTarget : { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
45609
+ return cfInTarget
45610
+ ? cfInTarget
45611
+ : { ...originCF, id: this.uuidGenerator.smallUuid(), ranges: [] };
45534
45612
  }
45535
45613
  pasteDataValidation(origin, target) {
45536
45614
  const rule = this.getters.getValidationRuleForCell(origin);
@@ -45562,7 +45640,7 @@
45562
45640
  originRule.isBlocking === rule.isBlocking);
45563
45641
  return ruleInTargetSheet
45564
45642
  ? ruleInTargetSheet
45565
- : { ...originRule, id: this.uuidGenerator.uuidv4(), ranges: [] };
45643
+ : { ...originRule, id: this.uuidGenerator.smallUuid(), ranges: [] };
45566
45644
  }
45567
45645
  /**
45568
45646
  * Add or remove XCs to a given data validation rule.
@@ -47754,6 +47832,10 @@
47754
47832
  */
47755
47833
  checkZonesAreInSheet(cmd) {
47756
47834
  const sheetId = "sheetId" in cmd ? cmd.sheetId : this.getters.tryGetActiveSheetId();
47835
+ if ("ranges" in cmd &&
47836
+ cmd.ranges.some((rangeData) => !this.getters.tryGetSheet(rangeData._sheetId))) {
47837
+ return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
47838
+ }
47757
47839
  const zones = this.getters.getCommandZones(cmd);
47758
47840
  if (!sheetId && zones.length > 0) {
47759
47841
  return "NoActiveSheet" /* CommandResult.NoActiveSheet */;
@@ -47802,23 +47884,23 @@
47802
47884
  function repeatCreateChartCommand(getters, cmd) {
47803
47885
  return {
47804
47886
  ...repeatSheetDependantCommand(getters, cmd),
47805
- id: uuidGenerator.uuidv4(),
47887
+ id: uuidGenerator.smallUuid(),
47806
47888
  };
47807
47889
  }
47808
47890
  function repeatCreateImageCommand(getters, cmd) {
47809
47891
  return {
47810
47892
  ...repeatSheetDependantCommand(getters, cmd),
47811
- figureId: uuidGenerator.uuidv4(),
47893
+ figureId: uuidGenerator.smallUuid(),
47812
47894
  };
47813
47895
  }
47814
47896
  function repeatCreateFigureCommand(getters, cmd) {
47815
47897
  const newCmd = repeatSheetDependantCommand(getters, cmd);
47816
- newCmd.figure.id = uuidGenerator.uuidv4();
47898
+ newCmd.figure.id = uuidGenerator.smallUuid();
47817
47899
  return newCmd;
47818
47900
  }
47819
47901
  function repeatCreateSheetCommand(getters, cmd) {
47820
47902
  const newCmd = deepCopy(cmd);
47821
- newCmd.sheetId = uuidGenerator.uuidv4();
47903
+ newCmd.sheetId = uuidGenerator.smallUuid();
47822
47904
  const sheetName = cmd.name || getters.getSheet(getters.getActiveSheetId()).name;
47823
47905
  // Extract the prefix of the sheet name (everything before the number at the end of the name)
47824
47906
  const namePrefix = sheetName.match(/(.+?)\d*$/)?.[1] || sheetName;
@@ -48013,7 +48095,6 @@
48013
48095
  super(config);
48014
48096
  this.session = config.session;
48015
48097
  this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
48016
- this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
48017
48098
  this.session.on("snapshot", this, () => {
48018
48099
  this.undoStack = [];
48019
48100
  this.redoStack = [];
@@ -48083,10 +48164,6 @@
48083
48164
  const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
48084
48165
  return canRepeatRevision(lastNonRedoRevision);
48085
48166
  }
48086
- drop(revisionIds) {
48087
- this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
48088
- this.redoStack = [];
48089
- }
48090
48167
  onNewLocalStateUpdate({ id }) {
48091
48168
  this.undoStack.push(id);
48092
48169
  this.redoStack = [];
@@ -48353,7 +48430,7 @@
48353
48430
  x: maxX < width ? 0 : Math.min(targetX, maxX - width),
48354
48431
  y: maxY < height ? 0 : Math.min(targetY, maxY - height),
48355
48432
  };
48356
- const newId = new UuidGenerator().uuidv4();
48433
+ const newId = new UuidGenerator().smallUuid();
48357
48434
  this.copiedFigureContent.paste(sheetId, newId, position, { height, width });
48358
48435
  if (this.operation === "CUT") {
48359
48436
  this.dispatch("DELETE_FIGURE", {
@@ -50056,23 +50133,7 @@
50056
50133
  gridSelection: deepCopy(gridSelection),
50057
50134
  };
50058
50135
  }
50059
- if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
50060
- const currentSheetIds = this.getters.getVisibleSheetIds();
50061
- this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
50062
- if (this.activeSheet.id in this.sheetsData) {
50063
- const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
50064
- this.selectCell(anchor.cell.col, anchor.cell.row);
50065
- }
50066
- else {
50067
- this.selectCell(0, 0);
50068
- }
50069
- const { col, row } = this.gridSelection.anchor.cell;
50070
- this.moveClient({
50071
- sheetId: this.getters.getActiveSheetId(),
50072
- col,
50073
- row,
50074
- });
50075
- }
50136
+ this.fallbackToVisibleSheet();
50076
50137
  const sheetId = this.getters.getActiveSheetId();
50077
50138
  this.gridSelection.zones = this.gridSelection.zones.map((z) => this.getters.expandZone(sheetId, z));
50078
50139
  this.gridSelection.anchor.zone = this.getters.expandZone(sheetId, this.gridSelection.anchor.zone);
@@ -50082,6 +50143,7 @@
50082
50143
  }
50083
50144
  }
50084
50145
  finalize() {
50146
+ this.fallbackToVisibleSheet();
50085
50147
  /** Any change to the selection has to be reflected in the selection processor. */
50086
50148
  this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
50087
50149
  }
@@ -50433,6 +50495,25 @@
50433
50495
  }
50434
50496
  return "Success" /* CommandResult.Success */;
50435
50497
  }
50498
+ fallbackToVisibleSheet() {
50499
+ if (!this.getters.tryGetSheet(this.getters.getActiveSheetId())) {
50500
+ const currentSheetIds = this.getters.getVisibleSheetIds();
50501
+ this.activeSheet = this.getters.getSheet(currentSheetIds[0]);
50502
+ if (this.activeSheet.id in this.sheetsData) {
50503
+ const { anchor } = this.clipSelection(this.activeSheet.id, this.sheetsData[this.activeSheet.id].gridSelection);
50504
+ this.selectCell(anchor.cell.col, anchor.cell.row);
50505
+ }
50506
+ else {
50507
+ this.selectCell(0, 0);
50508
+ }
50509
+ const { col, row } = this.gridSelection.anchor.cell;
50510
+ this.moveClient({
50511
+ sheetId: this.getters.getActiveSheetId(),
50512
+ col,
50513
+ row,
50514
+ });
50515
+ }
50516
+ }
50436
50517
  //-------------------------------------------
50437
50518
  // Helpers for extensions
50438
50519
  // ------------------------------------------
@@ -51580,7 +51661,9 @@
51580
51661
  case "UNGROUP_HEADERS":
51581
51662
  case "GROUP_HEADERS":
51582
51663
  case "CREATE_SHEET":
51583
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
51664
+ if (this.getters.tryGetSheet(cmd.sheetId)) {
51665
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
51666
+ }
51584
51667
  break;
51585
51668
  case "DUPLICATE_SHEET":
51586
51669
  this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
@@ -51588,12 +51671,14 @@
51588
51671
  }
51589
51672
  }
51590
51673
  finalize() {
51591
- if (this.isDirty) {
51592
- for (const sheetId of this.getters.getSheetIds()) {
51674
+ for (const sheetId of this.getters.getSheetIds()) {
51675
+ // sheets can be created without this plugin being aware of it
51676
+ // in concurrent situations.
51677
+ if (this.isDirty || !this.headerPositions[sheetId]) {
51593
51678
  this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
51594
51679
  }
51595
- this.isDirty = false;
51596
51680
  }
51681
+ this.isDirty = false;
51597
51682
  }
51598
51683
  /**
51599
51684
  * Returns the size, start and end coordinates of a column on an unfolded sheet
@@ -52277,7 +52362,7 @@
52277
52362
  clickAddSheet(ev) {
52278
52363
  const activeSheetId = this.env.model.getters.getActiveSheetId();
52279
52364
  const position = this.env.model.getters.getSheetIds().findIndex((sheetId) => sheetId === activeSheetId) + 1;
52280
- const sheetId = this.env.model.uuidGenerator.uuidv4();
52365
+ const sheetId = this.env.model.uuidGenerator.smallUuid();
52281
52366
  const name = this.env.model.getters.getNextSheetName(_t("Sheet"));
52282
52367
  this.env.model.dispatch("CREATE_SHEET", { sheetId, position, name });
52283
52368
  this.env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: sheetId });
@@ -53339,7 +53424,7 @@
53339
53424
  .o-font-size-editor {
53340
53425
  height: calc(100% - 4px);
53341
53426
  input.o-font-size {
53342
- outline-color: ${SELECTION_BORDER_COLOR};
53427
+ outline: none;
53343
53428
  height: 20px;
53344
53429
  width: 23px;
53345
53430
  }
@@ -54514,7 +54599,7 @@
54514
54599
  }
54515
54600
  /**
54516
54601
  * Drop the operation and all following operations in every
54517
- * branch
54602
+ * branches
54518
54603
  */
54519
54604
  drop(operationId) {
54520
54605
  for (const branch of this.branches) {
@@ -54819,9 +54904,16 @@
54819
54904
  this.fastForward();
54820
54905
  this.insert(redoId, this.buildEmpty(redoId), insertAfter);
54821
54906
  }
54822
- drop(operationId) {
54907
+ rebase(operationId) {
54908
+ const operation = this.get(operationId);
54909
+ const execution = [...this.tree.execution(this.HEAD_BRANCH).startAfter(operationId)];
54823
54910
  this.revertBefore(operationId);
54911
+ const baseId = this.HEAD_OPERATION.id;
54824
54912
  this.tree.drop(operationId);
54913
+ this.insert(operationId, operation, baseId);
54914
+ for (const { operation } of execution) {
54915
+ this.insert(operation.id, operation.data, this.HEAD_OPERATION.id);
54916
+ }
54825
54917
  }
54826
54918
  getRevertedExecution() {
54827
54919
  const data = [];
@@ -57398,6 +57490,11 @@
57398
57490
  dispatch: (command) => {
57399
57491
  const result = this.checkDispatchAllowed(command);
57400
57492
  if (!result.isSuccessful) {
57493
+ // core views plugins need to be invalidated
57494
+ this.dispatchToHandlers(this.coreHandlers, {
57495
+ type: "UNDO",
57496
+ commands: [command],
57497
+ });
57401
57498
  return;
57402
57499
  }
57403
57500
  this.isReplayingCommand = true;
@@ -57426,7 +57523,7 @@
57426
57523
  }
57427
57524
  setupConfig(config) {
57428
57525
  const client = config.client || {
57429
- id: this.uuidGenerator.uuidv4(),
57526
+ id: this.uuidGenerator.smallUuid(),
57430
57527
  name: _t("Anonymous").toString(),
57431
57528
  };
57432
57529
  const transportService = config.transportService || new LocalTransportService();
@@ -57847,9 +57944,9 @@
57847
57944
  exports.tokenize = tokenize;
57848
57945
 
57849
57946
 
57850
- __info__.version = "17.0.49";
57851
- __info__.date = "2025-02-10T08:55:47.771Z";
57852
- __info__.hash = "1049319";
57947
+ __info__.version = "17.0.51";
57948
+ __info__.date = "2025-02-25T05:56:10.869Z";
57949
+ __info__.hash = "b433912";
57853
57950
 
57854
57951
 
57855
57952
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);