@odoo/o-spreadsheet 17.4.2 → 17.4.4

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.4.2
6
- * @date 2024-07-24T10:26:00.237Z
7
- * @hash f5ede5b
5
+ * @version 17.4.4
6
+ * @date 2024-08-09T12:24:07.798Z
7
+ * @hash a43e855
8
8
  */
9
9
 
10
10
  'use strict';
@@ -2767,13 +2767,16 @@ const wildcardToRegExp = memoize(function wildcardToRegExp(operand) {
2767
2767
  }
2768
2768
  return new RegExp("^" + exp + "$", "i");
2769
2769
  });
2770
- function evaluatePredicate(value = "", criterion) {
2770
+ function evaluatePredicate(value = "", criterion, locale) {
2771
2771
  const { operator, operand } = criterion;
2772
2772
  if (operand === undefined || value === null || operand === null) {
2773
2773
  return false;
2774
2774
  }
2775
2775
  if (typeof operand === "number" && operator === "=") {
2776
- return value.toString() === operand.toString();
2776
+ if (typeof value === "string" && (isNumber(value, locale) || isDateTime(value, locale))) {
2777
+ return toNumber(value, locale) === operand;
2778
+ }
2779
+ return value === operand;
2777
2780
  }
2778
2781
  if (operator === "<>" || operator === "=") {
2779
2782
  let result;
@@ -2855,7 +2858,7 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
2855
2858
  for (let k = 0; k < countArg - 1; k += 2) {
2856
2859
  const criteriaValue = toMatrix(args[k])[i][j].value;
2857
2860
  const criterion = predicates[k / 2];
2858
- validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion);
2861
+ validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion, locale);
2859
2862
  if (!validatedPredicates) {
2860
2863
  break;
2861
2864
  }
@@ -6546,6 +6549,9 @@ function toNormalizedPivotValue(dimension, groupValue) {
6546
6549
  const groupValueString = typeof groupValue === "boolean"
6547
6550
  ? toString(groupValue).toLocaleLowerCase()
6548
6551
  : toString(groupValue);
6552
+ if (groupValueString === "null") {
6553
+ return null;
6554
+ }
6549
6555
  if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
6550
6556
  throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
6551
6557
  field: dimension.displayName,
@@ -6566,6 +6572,9 @@ function normalizeDateTime(value, granularity) {
6566
6572
  return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
6567
6573
  }
6568
6574
  function toFunctionPivotValue(value, dimension) {
6575
+ if (value === null) {
6576
+ return `"null"`;
6577
+ }
6569
6578
  if (!pivotToFunctionValueRegistry.contains(dimension.type)) {
6570
6579
  return `"${value}"`;
6571
6580
  }
@@ -10569,6 +10578,18 @@ function getDefinedAxis(definition) {
10569
10578
  useLeftAxis ||= !useRightAxis;
10570
10579
  return { useLeftAxis, useRightAxis };
10571
10580
  }
10581
+ function formatTickValue(localeFormat) {
10582
+ return (value) => {
10583
+ value = Number(value);
10584
+ if (isNaN(value))
10585
+ return value;
10586
+ const { locale, format } = localeFormat;
10587
+ return formatValue(value, {
10588
+ locale,
10589
+ format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
10590
+ });
10591
+ };
10592
+ }
10572
10593
 
10573
10594
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
10574
10595
  const chartShowValuesPlugin = {
@@ -19117,6 +19138,7 @@ const PIVOT_VALUE = {
19117
19138
  const pivot = this.getters.getPivot(pivotId);
19118
19139
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
19119
19140
  addPivotDependencies(this, coreDefinition);
19141
+ pivot.init({ reload: pivot.needsReevaluation });
19120
19142
  const error = pivot.assertIsValid({ throwOnError: false });
19121
19143
  if (error) {
19122
19144
  return error;
@@ -19145,6 +19167,7 @@ const PIVOT_HEADER = {
19145
19167
  const pivot = this.getters.getPivot(_pivotId);
19146
19168
  const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
19147
19169
  addPivotDependencies(this, coreDefinition);
19170
+ pivot.init({ reload: pivot.needsReevaluation });
19148
19171
  const error = pivot.assertIsValid({ throwOnError: false });
19149
19172
  if (error) {
19150
19173
  return error;
@@ -20362,14 +20385,6 @@ for (let category of categories) {
20362
20385
  }
20363
20386
  const notAvailableError = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
20364
20387
  function createComputeFunction(descr, functionName) {
20365
- function runtimeCompute(...args) {
20366
- try {
20367
- return vectorizedCompute.apply(this, args);
20368
- }
20369
- catch (e) {
20370
- return handleError(e, functionName);
20371
- }
20372
- }
20373
20388
  function vectorizedCompute(...args) {
20374
20389
  let countVectorizableCol = 1;
20375
20390
  let countVectorizableRow = 1;
@@ -20410,13 +20425,13 @@ function createComputeFunction(descr, functionName) {
20410
20425
  }
20411
20426
  }
20412
20427
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
20413
- throw new BadExpressionError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be reference to a cell or range.", (i + 1).toString()));
20428
+ throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", functionName, (i + 1).toString()));
20414
20429
  }
20415
20430
  }
20416
20431
  //#endregion
20417
20432
  if (countVectorizableCol === 1 && countVectorizableRow === 1) {
20418
20433
  // either this function is not vectorized or it ends up with a 1x1 dimension
20419
- return computeFunctionToObject.apply(this, args);
20434
+ return errorHandlingCompute.apply(this, args);
20420
20435
  }
20421
20436
  const getArgOffset = (i, j) => args.map((arg, index) => {
20422
20437
  switch (vectorArgsType?.[index]) {
@@ -20434,7 +20449,7 @@ function createComputeFunction(descr, functionName) {
20434
20449
  if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
20435
20450
  return notAvailableError;
20436
20451
  }
20437
- const singleCellComputeResult = computeFunctionToObject.apply(this, getArgOffset(col, row));
20452
+ const singleCellComputeResult = errorHandlingCompute.apply(this, getArgOffset(col, row));
20438
20453
  // In the case where the user tries to vectorize arguments of an array formula, we will get an
20439
20454
  // array for every combination of the vectorized arguments, which will lead to a 3D matrix and
20440
20455
  // we won't be able to return the values.
@@ -20449,6 +20464,14 @@ function createComputeFunction(descr, functionName) {
20449
20464
  : singleCellComputeResult;
20450
20465
  });
20451
20466
  }
20467
+ function errorHandlingCompute(...args) {
20468
+ try {
20469
+ return computeFunctionToObject.apply(this, args);
20470
+ }
20471
+ catch (e) {
20472
+ return handleError(e, functionName);
20473
+ }
20474
+ }
20452
20475
  function computeFunctionToObject(...args) {
20453
20476
  const result = descr.compute.apply(this, args);
20454
20477
  if (!isMatrix(result)) {
@@ -20464,7 +20487,7 @@ function createComputeFunction(descr, functionName) {
20464
20487
  }
20465
20488
  return matrixMap(result, (row) => ({ value: row }));
20466
20489
  }
20467
- return runtimeCompute;
20490
+ return vectorizedCompute;
20468
20491
  }
20469
20492
  function handleError(e, functionName) {
20470
20493
  // the error could be an user error (instance of EvaluationError)
@@ -20781,6 +20804,78 @@ function isCtrlKey(ev) {
20781
20804
  return isMacOS() ? ev.metaKey : ev.ctrlKey;
20782
20805
  }
20783
20806
 
20807
+ /**
20808
+ * Return the o-spreadsheet element position relative
20809
+ * to the browser viewport.
20810
+ */
20811
+ function useSpreadsheetRect() {
20812
+ const position = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
20813
+ let spreadsheetElement = null;
20814
+ function updatePosition() {
20815
+ if (!spreadsheetElement) {
20816
+ spreadsheetElement = document.querySelector(".o-spreadsheet");
20817
+ }
20818
+ if (spreadsheetElement) {
20819
+ const { top, left, width, height } = spreadsheetElement.getBoundingClientRect();
20820
+ position.x = left;
20821
+ position.y = top;
20822
+ position.width = width;
20823
+ position.height = height;
20824
+ }
20825
+ }
20826
+ owl.onMounted(updatePosition);
20827
+ owl.onPatched(updatePosition);
20828
+ return position;
20829
+ }
20830
+ /**
20831
+ * Return the component (or ref's component) BoundingRect, relative
20832
+ * to the upper left corner of the screen (<body> element).
20833
+ *
20834
+ * Note: when used with a <Portal/> component, it will
20835
+ * return the portal position, not the teleported position.
20836
+ */
20837
+ function useAbsoluteBoundingRect(ref) {
20838
+ const rect = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
20839
+ function updateElRect() {
20840
+ const el = ref.el;
20841
+ if (el === null) {
20842
+ return;
20843
+ }
20844
+ const { top, left, width, height } = el.getBoundingClientRect();
20845
+ rect.x = left;
20846
+ rect.y = top;
20847
+ rect.width = width;
20848
+ rect.height = height;
20849
+ }
20850
+ owl.onMounted(updateElRect);
20851
+ owl.onPatched(updateElRect);
20852
+ return rect;
20853
+ }
20854
+ /**
20855
+ * Get the rectangle inside which a popover should stay when being displayed.
20856
+ * It's the value defined in `env.getPopoverContainerRect`, or the Rect of the "o-spreadsheet"
20857
+ * element by default.
20858
+ *
20859
+ * Coordinates are expressed expressed as absolute DOM position.
20860
+ */
20861
+ function usePopoverContainer() {
20862
+ const container = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
20863
+ const component = owl.useComponent();
20864
+ const spreadsheetRect = useSpreadsheetRect();
20865
+ function updateRect() {
20866
+ const env = component.env;
20867
+ const newRect = "getPopoverContainerRect" in env ? env.getPopoverContainerRect() : spreadsheetRect;
20868
+ container.x = newRect.x;
20869
+ container.y = newRect.y;
20870
+ container.width = newRect.width;
20871
+ container.height = newRect.height;
20872
+ }
20873
+ updateRect();
20874
+ owl.onMounted(updateRect);
20875
+ owl.onPatched(updateRect);
20876
+ return container;
20877
+ }
20878
+
20784
20879
  const arrowMap = {
20785
20880
  ArrowDown: "down",
20786
20881
  ArrowLeft: "left",
@@ -21370,7 +21465,9 @@ class Composer extends owl.Component {
21370
21465
  argToFocus: 0,
21371
21466
  });
21372
21467
  compositionActive = false;
21468
+ spreadsheetRect = useSpreadsheetRect();
21373
21469
  get assistantStyle() {
21470
+ const composerRect = this.composerRef.el.getBoundingClientRect();
21374
21471
  const assistantStyle = {};
21375
21472
  assistantStyle["min-width"] = `${this.props.rect?.width || ASSISTANT_WIDTH}px`;
21376
21473
  const proposals = this.autoCompleteState.provider?.proposals;
@@ -21397,6 +21494,9 @@ class Composer extends owl.Component {
21397
21494
  }
21398
21495
  else if (this.props.delimitation) {
21399
21496
  assistantStyle["max-height"] = `${this.props.delimitation.height}px`;
21497
+ if (composerRect.left + ASSISTANT_WIDTH > this.spreadsheetRect.width) {
21498
+ assistantStyle.right = `0px`;
21499
+ }
21400
21500
  }
21401
21501
  return cssPropertiesToCss(assistantStyle);
21402
21502
  }
@@ -23608,23 +23708,13 @@ function getBarConfiguration(chart, labels, localeFormat) {
23608
23708
  padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
23609
23709
  };
23610
23710
  config.options.indexAxis = chart.horizontal ? "y" : "x";
23611
- const formatCallback = (value) => {
23612
- value = Number(value);
23613
- if (isNaN(value))
23614
- return value;
23615
- const { locale, format } = localeFormat;
23616
- return formatValue(value, {
23617
- locale,
23618
- format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
23619
- });
23620
- };
23621
23711
  config.options.scales = {};
23622
23712
  const labelsAxis = { ticks: { padding: 5, color: fontColor } };
23623
23713
  const valuesAxis = {
23624
23714
  beginAtZero: true, // the origin of the y axis is always zero
23625
23715
  ticks: {
23626
23716
  color: fontColor,
23627
- callback: formatCallback,
23717
+ callback: formatTickValue(localeFormat),
23628
23718
  },
23629
23719
  };
23630
23720
  const xAxis = chart.horizontal ? valuesAxis : labelsAxis;
@@ -23661,7 +23751,7 @@ function getBarConfiguration(chart, labels, localeFormat) {
23661
23751
  showValues: chart.showValues,
23662
23752
  background: chart.background,
23663
23753
  horizontal: chart.horizontal,
23664
- callback: formatCallback,
23754
+ callback: formatTickValue(localeFormat),
23665
23755
  };
23666
23756
  return config;
23667
23757
  }
@@ -23940,21 +24030,11 @@ function getLineOrScatterConfiguration(chart, labels, options) {
23940
24030
  title: getChartAxisTitleRuntime(chart.axesDesign?.x),
23941
24031
  },
23942
24032
  };
23943
- const formatCallback = (value) => {
23944
- value = Number(value);
23945
- if (isNaN(value))
23946
- return value;
23947
- const { locale, format } = options;
23948
- return formatValue(value, {
23949
- locale,
23950
- format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
23951
- });
23952
- };
23953
24033
  const yAxis = {
23954
24034
  beginAtZero: true, // the origin of the y axis is always zero
23955
24035
  ticks: {
23956
24036
  color: fontColor,
23957
- callback: formatCallback,
24037
+ callback: formatTickValue(options),
23958
24038
  },
23959
24039
  };
23960
24040
  const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
@@ -23985,7 +24065,7 @@ function getLineOrScatterConfiguration(chart, labels, options) {
23985
24065
  config.options.plugins.chartShowValuesPlugin = {
23986
24066
  showValues: chart.showValues,
23987
24067
  background: chart.background,
23988
- callback: formatCallback,
24068
+ callback: formatTickValue(options),
23989
24069
  };
23990
24070
  return config;
23991
24071
  }
@@ -24260,30 +24340,18 @@ function createComboChartRuntime(chart, getters) {
24260
24340
  title: getChartAxisTitleRuntime(chart.axesDesign?.x),
24261
24341
  },
24262
24342
  };
24263
- const formatCallback = (format) => {
24264
- return (value) => {
24265
- value = Number(value);
24266
- if (isNaN(value))
24267
- return value;
24268
- const { locale } = localeFormat;
24269
- return formatValue(value, {
24270
- locale,
24271
- format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
24272
- });
24273
- };
24274
- };
24275
24343
  const leftVerticalAxis = {
24276
24344
  beginAtZero: true, // the origin of the y axis is always zero
24277
24345
  ticks: {
24278
24346
  color: fontColor,
24279
- callback: formatCallback(mainDataSetFormat),
24347
+ callback: formatTickValue({ format: mainDataSetFormat, locale }),
24280
24348
  },
24281
24349
  };
24282
24350
  const rightVerticalAxis = {
24283
24351
  beginAtZero: true, // the origin of the y axis is always zero
24284
24352
  ticks: {
24285
24353
  color: fontColor,
24286
- callback: formatCallback(lineDataSetsFormat),
24354
+ callback: formatTickValue({ format: lineDataSetsFormat, locale }),
24287
24355
  },
24288
24356
  };
24289
24357
  const definition = chart.getDefinition();
@@ -24308,7 +24376,7 @@ function createComboChartRuntime(chart, getters) {
24308
24376
  config.options.plugins.chartShowValuesPlugin = {
24309
24377
  showValues: chart.showValues,
24310
24378
  background: chart.background,
24311
- callback: formatCallback(mainDataSetFormat),
24379
+ callback: formatTickValue({ format: mainDataSetFormat, locale }),
24312
24380
  };
24313
24381
  const colors = new ColorGenerator();
24314
24382
  for (let [index, { label, data }] of dataSetsValues.entries()) {
@@ -24840,7 +24908,10 @@ function getPieConfiguration(chart, labels, localeFormat) {
24840
24908
  const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
24841
24909
  return xLabel ? `${xLabel}: ${yLabelStr} (${percentage}%)` : `${yLabelStr} (${percentage}%)`;
24842
24910
  };
24843
- config.options.plugins.chartShowValuesPlugin = { showValues: chart.showValues };
24911
+ config.options.plugins.chartShowValuesPlugin = {
24912
+ showValues: chart.showValues,
24913
+ callback: formatTickValue(localeFormat),
24914
+ };
24844
24915
  return config;
24845
24916
  }
24846
24917
  function getPieColors(colors, dataSetsValues) {
@@ -25424,6 +25495,7 @@ function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat
25424
25495
  config.options.plugins.chartShowValuesPlugin = {
25425
25496
  showValues: chart.showValues,
25426
25497
  background: chart.background,
25498
+ callback: formatTickValue(localeFormat),
25427
25499
  };
25428
25500
  return config;
25429
25501
  }
@@ -26239,78 +26311,6 @@ function zoneToRect(zone) {
26239
26311
  };
26240
26312
  }
26241
26313
 
26242
- /**
26243
- * Return the o-spreadsheet element position relative
26244
- * to the browser viewport.
26245
- */
26246
- function useSpreadsheetRect() {
26247
- const position = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
26248
- let spreadsheetElement = null;
26249
- function updatePosition() {
26250
- if (!spreadsheetElement) {
26251
- spreadsheetElement = document.querySelector(".o-spreadsheet");
26252
- }
26253
- if (spreadsheetElement) {
26254
- const { top, left, width, height } = spreadsheetElement.getBoundingClientRect();
26255
- position.x = left;
26256
- position.y = top;
26257
- position.width = width;
26258
- position.height = height;
26259
- }
26260
- }
26261
- owl.onMounted(updatePosition);
26262
- owl.onPatched(updatePosition);
26263
- return position;
26264
- }
26265
- /**
26266
- * Return the component (or ref's component) BoundingRect, relative
26267
- * to the upper left corner of the screen (<body> element).
26268
- *
26269
- * Note: when used with a <Portal/> component, it will
26270
- * return the portal position, not the teleported position.
26271
- */
26272
- function useAbsoluteBoundingRect(ref) {
26273
- const rect = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
26274
- function updateElRect() {
26275
- const el = ref.el;
26276
- if (el === null) {
26277
- return;
26278
- }
26279
- const { top, left, width, height } = el.getBoundingClientRect();
26280
- rect.x = left;
26281
- rect.y = top;
26282
- rect.width = width;
26283
- rect.height = height;
26284
- }
26285
- owl.onMounted(updateElRect);
26286
- owl.onPatched(updateElRect);
26287
- return rect;
26288
- }
26289
- /**
26290
- * Get the rectangle inside which a popover should stay when being displayed.
26291
- * It's the value defined in `env.getPopoverContainerRect`, or the Rect of the "o-spreadsheet"
26292
- * element by default.
26293
- *
26294
- * Coordinates are expressed expressed as absolute DOM position.
26295
- */
26296
- function usePopoverContainer() {
26297
- const container = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
26298
- const component = owl.useComponent();
26299
- const spreadsheetRect = useSpreadsheetRect();
26300
- function updateRect() {
26301
- const env = component.env;
26302
- const newRect = "getPopoverContainerRect" in env ? env.getPopoverContainerRect() : spreadsheetRect;
26303
- container.x = newRect.x;
26304
- container.y = newRect.y;
26305
- container.width = newRect.width;
26306
- container.height = newRect.height;
26307
- }
26308
- updateRect();
26309
- owl.onMounted(updateRect);
26310
- owl.onPatched(updateRect);
26311
- return container;
26312
- }
26313
-
26314
26314
  css /* scss */ `
26315
26315
  .o-popover {
26316
26316
  position: absolute;
@@ -35809,6 +35809,14 @@ css /* scss */ `
35809
35809
  .pivot-dim-operator-label {
35810
35810
  min-width: 120px;
35811
35811
  }
35812
+
35813
+ &.pivot-dimension-invalid {
35814
+ background-color: #ffdddd;
35815
+ border-color: red !important;
35816
+ select {
35817
+ background-color: #ffdddd;
35818
+ }
35819
+ }
35812
35820
  }
35813
35821
  `;
35814
35822
  class PivotDimension extends owl.Component {
@@ -47216,12 +47224,13 @@ class BordersPlugin extends CorePlugin {
47216
47224
  this.clearBorders(cmd.sheetId, cmd.target);
47217
47225
  break;
47218
47226
  case "REMOVE_COLUMNS_ROWS":
47219
- for (let el of [...cmd.elements].sort((a, b) => b - a)) {
47227
+ const elements = [...cmd.elements].sort((a, b) => b - a);
47228
+ for (const group of groupConsecutive(elements)) {
47220
47229
  if (cmd.dimension === "COL") {
47221
- this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
47230
+ this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
47222
47231
  }
47223
47232
  else {
47224
- this.shiftBordersVertically(cmd.sheetId, el + 1, -1);
47233
+ this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
47225
47234
  }
47226
47235
  }
47227
47236
  break;
@@ -50391,7 +50400,7 @@ class RangeAdapter {
50391
50400
  * @param sheetXC the string description of a range, in the form SheetName!XC:XC
50392
50401
  */
50393
50402
  getRangeFromSheetXC(defaultSheetId, sheetXC) {
50394
- if (!rangeReference.test(sheetXC)) {
50403
+ if (!rangeReference.test(sheetXC) || !this.getters.tryGetSheet(defaultSheetId)) {
50395
50404
  return new RangeImpl({
50396
50405
  sheetId: "",
50397
50406
  zone: { left: -1, top: -1, right: -1, bottom: -1 },
@@ -51282,12 +51291,7 @@ class SheetPlugin extends CorePlugin {
51282
51291
  });
51283
51292
  }
51284
51293
  if (colIndex > deletedColumn) {
51285
- this.dispatch("UPDATE_CELL_POSITION", {
51286
- sheetId: sheet.id,
51287
- cellId: cellId,
51288
- col: colIndex - 1,
51289
- row: rowIndex,
51290
- });
51294
+ this.setNewPosition(cellId, sheet.id, colIndex - 1, rowIndex);
51291
51295
  }
51292
51296
  }
51293
51297
  }
@@ -51297,7 +51301,7 @@ class SheetPlugin extends CorePlugin {
51297
51301
  * Move the cells after a column or rows insertion
51298
51302
  */
51299
51303
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
51300
- const commands = [];
51304
+ const updates = [];
51301
51305
  for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
51302
51306
  const row = sheet.rows[rowIndex];
51303
51307
  if (dimension !== "rows" || rowIndex >= addedElement) {
@@ -51306,20 +51310,20 @@ class SheetPlugin extends CorePlugin {
51306
51310
  const cellId = row.cells[i];
51307
51311
  if (cellId) {
51308
51312
  if (dimension === "rows" || colIndex >= addedElement) {
51309
- commands.push({
51310
- type: "UPDATE_CELL_POSITION",
51313
+ updates.push({
51311
51314
  sheetId: sheet.id,
51312
51315
  cellId: cellId,
51313
51316
  col: colIndex + (dimension === "columns" ? quantity : 0),
51314
51317
  row: rowIndex + (dimension === "rows" ? quantity : 0),
51318
+ type: "UPDATE_CELL_POSITION",
51315
51319
  });
51316
51320
  }
51317
51321
  }
51318
51322
  }
51319
51323
  }
51320
51324
  }
51321
- for (let cmd of commands.reverse()) {
51322
- this.dispatch(cmd.type, cmd);
51325
+ for (let update of updates.reverse()) {
51326
+ this.updateCellPosition(update);
51323
51327
  }
51324
51328
  }
51325
51329
  /**
@@ -51352,12 +51356,7 @@ class SheetPlugin extends CorePlugin {
51352
51356
  const colIndex = Number(i);
51353
51357
  const cellId = row.cells[i];
51354
51358
  if (cellId) {
51355
- this.dispatch("UPDATE_CELL_POSITION", {
51356
- sheetId: sheet.id,
51357
- cellId: cellId,
51358
- col: colIndex,
51359
- row: rowIndex - numberRows,
51360
- });
51359
+ this.setNewPosition(cellId, sheet.id, colIndex, rowIndex - numberRows);
51361
51360
  }
51362
51361
  }
51363
51362
  }
@@ -54054,18 +54053,23 @@ class Evaluator {
54054
54053
  this.evaluate(this.getAllCells());
54055
54054
  console.info("evaluate all cells", performance.now() - start, "ms");
54056
54055
  }
54057
- evaluateFormula(sheetId, formulaString) {
54058
- const compiledFormula = compile(formulaString);
54059
- const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
54060
- this.updateCompilationParameters();
54061
- const result = updateEvalContextAndExecute({ ...compiledFormula, dependencies: ranges }, this.compilationParams, sheetId);
54062
- if (isMatrix(result)) {
54063
- return matrixMap(result, (cell) => cell.value);
54056
+ evaluateFormulaResult(sheetId, formulaString) {
54057
+ try {
54058
+ const compiledFormula = compile(formulaString);
54059
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
54060
+ this.updateCompilationParameters();
54061
+ const result = updateEvalContextAndExecute({ ...compiledFormula, dependencies: ranges }, this.compilationParams, sheetId);
54062
+ if (isMatrix(result)) {
54063
+ return result;
54064
+ }
54065
+ if (result.value === null) {
54066
+ return { value: 0, format: result.format };
54067
+ }
54068
+ return result;
54064
54069
  }
54065
- if (result.value === null) {
54066
- return 0;
54070
+ catch (error) {
54071
+ return handleError(error, "");
54067
54072
  }
54068
- return result.value;
54069
54073
  }
54070
54074
  getAllCells() {
54071
54075
  const positions = this.createEmptyPositionSet();
@@ -54123,6 +54127,9 @@ class Evaluator {
54123
54127
  if (!this.blockedArrayFormulas.has(position)) {
54124
54128
  this.invalidateSpreading(position);
54125
54129
  }
54130
+ if (this.spreadingRelations.isArrayFormula(position)) {
54131
+ this.spreadingRelations.removeNode(position);
54132
+ }
54126
54133
  const cell = this.getters.getCell(position);
54127
54134
  if (cell === undefined) {
54128
54135
  return EMPTY_CELL;
@@ -54165,7 +54172,6 @@ class Evaluator {
54165
54172
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
54166
54173
  const nbColumns = formulaReturn.length;
54167
54174
  const nbRows = formulaReturn[0].length;
54168
- this.spreadingRelations.removeNode(formulaPosition);
54169
54175
  forEachSpreadPositionInMatrix(nbColumns, nbRows, this.updateSpreadRelation(formulaPosition));
54170
54176
  this.assertNoMergedCellsInSpreadZone(formulaPosition, formulaReturn);
54171
54177
  forEachSpreadPositionInMatrix(nbColumns, nbRows, this.checkCollision(formulaPosition));
@@ -54426,6 +54432,7 @@ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId
54426
54432
  class EvaluationPlugin extends UIPlugin {
54427
54433
  static getters = [
54428
54434
  "evaluateFormula",
54435
+ "evaluateFormulaResult",
54429
54436
  "getCorrespondingFormulaCell",
54430
54437
  "getRangeFormattedValues",
54431
54438
  "getRangeValues",
@@ -54485,12 +54492,14 @@ class EvaluationPlugin extends UIPlugin {
54485
54492
  // Getters
54486
54493
  // ---------------------------------------------------------------------------
54487
54494
  evaluateFormula(sheetId, formulaString) {
54488
- try {
54489
- return this.evaluator.evaluateFormula(sheetId, formulaString);
54490
- }
54491
- catch (error) {
54492
- return error.value || CellErrorType.GenericError;
54495
+ const result = this.evaluateFormulaResult(sheetId, formulaString);
54496
+ if (isMatrix(result)) {
54497
+ return matrixMap(result, (cell) => cell.value);
54493
54498
  }
54499
+ return result.value;
54500
+ }
54501
+ evaluateFormulaResult(sheetId, formulaString) {
54502
+ return this.evaluator.evaluateFormulaResult(sheetId, formulaString);
54494
54503
  }
54495
54504
  /**
54496
54505
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -55873,28 +55882,33 @@ class PivotUIPlugin extends UIPlugin {
55873
55882
  const pivotRow = position.row - mainPosition.row;
55874
55883
  return pivotCells[pivotCol][pivotRow];
55875
55884
  }
55876
- if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
55877
- const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
55885
+ try {
55886
+ if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
55887
+ const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
55888
+ return {
55889
+ type: "MEASURE_HEADER",
55890
+ domain,
55891
+ measure: args.at(-1)?.toString() || "",
55892
+ };
55893
+ }
55894
+ else if (functionName === "PIVOT.HEADER") {
55895
+ const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
55896
+ return {
55897
+ type: "HEADER",
55898
+ domain,
55899
+ };
55900
+ }
55901
+ const [measure, ...domainArgs] = args.slice(1);
55902
+ const domain = pivot.parseArgsToPivotDomain(domainArgs.map((value) => ({ value })));
55878
55903
  return {
55879
- type: "MEASURE_HEADER",
55904
+ type: "VALUE",
55880
55905
  domain,
55881
- measure: args.at(-1)?.toString() || "",
55906
+ measure: measure?.toString() || "",
55882
55907
  };
55883
55908
  }
55884
- else if (functionName === "PIVOT.HEADER") {
55885
- const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
55886
- return {
55887
- type: "HEADER",
55888
- domain,
55889
- };
55909
+ catch (_) {
55910
+ return EMPTY_PIVOT_CELL;
55890
55911
  }
55891
- const [measure, ...domainArgs] = args.slice(1);
55892
- const domain = pivot.parseArgsToPivotDomain(domainArgs.map((value) => ({ value })));
55893
- return {
55894
- type: "VALUE",
55895
- domain,
55896
- measure: measure?.toString() || "",
55897
- };
55898
55912
  }
55899
55913
  getPivot(pivotId) {
55900
55914
  return this.pivots[pivotId];
@@ -57762,6 +57776,7 @@ class FormatPlugin extends UIPlugin {
57762
57776
  * evaluated and updated with the number type.
57763
57777
  */
57764
57778
  setDecimal(sheetId, zones, step) {
57779
+ const positionsByFormat = {};
57765
57780
  // Find the each cell with a number value and get the format
57766
57781
  for (const zone of recomputeZones(zones)) {
57767
57782
  for (const position of positions(zone)) {
@@ -57771,15 +57786,20 @@ class FormatPlugin extends UIPlugin {
57771
57786
  // of the format
57772
57787
  this.getters.getLocale();
57773
57788
  const newFormat = changeDecimalPlaces(numberFormat, step);
57774
- // Apply the new format on the whole zone
57775
- this.dispatch("SET_FORMATTING", {
57776
- sheetId,
57777
- target: [positionToZone(position)],
57778
- format: newFormat,
57779
- });
57789
+ positionsByFormat[newFormat] = positionsByFormat[newFormat] || [];
57790
+ positionsByFormat[newFormat].push(position);
57780
57791
  }
57781
57792
  }
57782
57793
  }
57794
+ // consolidate all positions with the same format in bigger zones
57795
+ for (const newFormat in positionsByFormat) {
57796
+ const zones = recomputeZones(positionsByFormat[newFormat].map((position) => positionToZone(position)));
57797
+ this.dispatch("SET_FORMATTING", {
57798
+ sheetId,
57799
+ format: newFormat,
57800
+ target: zones,
57801
+ });
57802
+ }
57783
57803
  }
57784
57804
  /**
57785
57805
  * Take a range of cells and return the format of the first cell containing a
@@ -63090,6 +63110,8 @@ css /* scss */ `
63090
63110
 
63091
63111
  .o-sidePanel-handle-container {
63092
63112
  width: 8px;
63113
+ position: fixed;
63114
+ top: 50%;
63093
63115
  }
63094
63116
  .o-sidePanel-handle {
63095
63117
  cursor: col-resize;
@@ -65709,7 +65731,7 @@ function createEmptyStructure(node) {
65709
65731
  }
65710
65732
 
65711
65733
  class StateObserver {
65712
- changes = [];
65734
+ changes;
65713
65735
  commands = [];
65714
65736
  /**
65715
65737
  * Record the changes which could happen in the given callback, save them in a
@@ -65741,7 +65763,7 @@ class StateObserver {
65741
65763
  if (value[key] === val) {
65742
65764
  return;
65743
65765
  }
65744
- this.changes.push({
65766
+ this.changes?.push({
65745
65767
  key,
65746
65768
  target: value,
65747
65769
  before: value[key],
@@ -68407,6 +68429,6 @@ exports.tokenColors = tokenColors;
68407
68429
  exports.tokenize = tokenize;
68408
68430
 
68409
68431
 
68410
- __info__.version = "17.4.2";
68411
- __info__.date = "2024-07-24T10:26:00.237Z";
68412
- __info__.hash = "f5ede5b";
68432
+ __info__.version = "17.4.4";
68433
+ __info__.date = "2024-08-09T12:24:07.798Z";
68434
+ __info__.hash = "a43e855";