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