@odoo/o-spreadsheet 17.0.28 → 17.0.29

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.28
6
- * @date 2024-07-02T10:35:57.899Z
7
- * @hash 7f86a16
5
+ * @version 17.0.29
6
+ * @date 2024-07-08T05:40:27.608Z
7
+ * @hash 2d467e3
8
8
  */
9
9
 
10
10
  'use strict';
@@ -659,7 +659,7 @@ function getAddHeaderStartIndex(position, base) {
659
659
  /**
660
660
  * Compares two objects.
661
661
  */
662
- function deepEquals(o1, o2, ignoreFunctions) {
662
+ function deepEquals(o1, o2) {
663
663
  if (o1 === o2)
664
664
  return true;
665
665
  if ((o1 && !o2) || (o2 && !o1))
@@ -675,17 +675,13 @@ function deepEquals(o1, o2, ignoreFunctions) {
675
675
  }
676
676
  }
677
677
  for (const key in o1) {
678
- const typeOfO1Key = typeof o1[key];
679
- if (typeOfO1Key !== typeof o2[key])
678
+ if (typeof o1[key] !== typeof o2[key])
680
679
  return false;
681
- if (typeOfO1Key === "object") {
682
- if (!deepEquals(o1[key], o2[key], ignoreFunctions))
680
+ if (typeof o1[key] === "object") {
681
+ if (!deepEquals(o1[key], o2[key]))
683
682
  return false;
684
683
  }
685
684
  else {
686
- if (ignoreFunctions && typeOfO1Key === "function") {
687
- continue;
688
- }
689
685
  if (o1[key] !== o2[key])
690
686
  return false;
691
687
  }
@@ -756,11 +752,9 @@ class JetSet extends Set {
756
752
  return this;
757
753
  }
758
754
  deleteMany(iterable) {
759
- let wasDeleted = false;
760
755
  for (const element of iterable) {
761
- wasDeleted ||= super.delete(element);
756
+ super.delete(element);
762
757
  }
763
- return wasDeleted;
764
758
  }
765
759
  }
766
760
  /**
@@ -1817,7 +1811,7 @@ const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSepa
1817
1811
  });
1818
1812
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1819
1813
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1820
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1814
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1821
1815
  const pIntegerAndDecimals = `(\\d+(${thousandsSeparator}\\d{3,})*(${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1822
1816
  const pOnlyDecimals = `(${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1823
1817
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1844,7 +1838,7 @@ function isNumber(value, locale) {
1844
1838
  return getNumberRegex(locale).test(value.trim());
1845
1839
  }
1846
1840
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1847
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1841
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1848
1842
  });
1849
1843
  /**
1850
1844
  * Convert a string into a number. It assumes that the string actually represents
@@ -2546,6 +2540,9 @@ function getPredicate(descr, isQuery, locale) {
2546
2540
  return result;
2547
2541
  }
2548
2542
  function operandToRegExp(operand) {
2543
+ if (operand === "*") {
2544
+ return /.+/;
2545
+ }
2549
2546
  let exp = "";
2550
2547
  let predecessor = "";
2551
2548
  for (let char of operand) {
@@ -2569,9 +2566,9 @@ function operandToRegExp(operand) {
2569
2566
  }
2570
2567
  return new RegExp("^" + exp + "$", "i");
2571
2568
  }
2572
- function evaluatePredicate(value, criterion) {
2569
+ function evaluatePredicate(value = "", criterion) {
2573
2570
  const { operator, operand } = criterion;
2574
- if (value === undefined || operand === undefined || value === null || operand === null) {
2571
+ if (operand === undefined || value === null || operand === null) {
2575
2572
  return false;
2576
2573
  }
2577
2574
  if (typeof operand === "number" && operator === "=") {
@@ -4784,7 +4781,6 @@ class ChartJsComponent extends owl.Component {
4784
4781
  static template = "o-spreadsheet-ChartJsComponent";
4785
4782
  canvas = owl.useRef("graphContainer");
4786
4783
  chart;
4787
- currentRuntime;
4788
4784
  get background() {
4789
4785
  return this.chartRuntime.background;
4790
4786
  }
@@ -4801,17 +4797,10 @@ class ChartJsComponent extends owl.Component {
4801
4797
  setup() {
4802
4798
  owl.onMounted(() => {
4803
4799
  const runtime = this.chartRuntime;
4804
- this.currentRuntime = runtime;
4805
4800
  // Note: chartJS modify the runtime in place, so it's important to give it a copy
4806
4801
  this.createChart(deepCopy(runtime.chartJsConfig));
4807
4802
  });
4808
- owl.useEffect(() => {
4809
- const runtime = this.chartRuntime;
4810
- if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
4811
- this.currentRuntime = runtime;
4812
- this.updateChartJs(deepCopy(runtime));
4813
- }
4814
- });
4803
+ owl.useEffect(() => this.updateChartJs(deepCopy(this.chartRuntime)), () => [this.chartRuntime]);
4815
4804
  }
4816
4805
  createChart(chartData) {
4817
4806
  const canvas = this.canvas.el;
@@ -7679,19 +7668,22 @@ class TokenizingChars {
7679
7668
  }
7680
7669
 
7681
7670
  function isValidLocale(locale) {
7682
- if (!(locale &&
7683
- typeof locale === "object" &&
7684
- typeof locale.name === "string" &&
7685
- typeof locale.code === "string" &&
7686
- typeof locale.thousandsSeparator === "string" &&
7687
- typeof locale.decimalSeparator === "string" &&
7688
- typeof locale.dateFormat === "string" &&
7689
- typeof locale.timeFormat === "string" &&
7690
- typeof locale.formulaArgSeparator === "string")) {
7671
+ if (!locale ||
7672
+ typeof locale !== "object" ||
7673
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
7691
7674
  return false;
7692
7675
  }
7693
- if (!Object.values(locale).every((v) => v)) {
7694
- return false;
7676
+ for (const property of [
7677
+ "code",
7678
+ "name",
7679
+ "decimalSeparator",
7680
+ "dateFormat",
7681
+ "timeFormat",
7682
+ "formulaArgSeparator",
7683
+ ]) {
7684
+ if (!locale[property] || typeof locale[property] !== "string") {
7685
+ return false;
7686
+ }
7695
7687
  }
7696
7688
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7697
7689
  return false;
@@ -7789,7 +7781,10 @@ function canonicalizeNumberLiteral(content, locale) {
7789
7781
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7790
7782
  return content;
7791
7783
  }
7792
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7784
+ if (locale.thousandsSeparator) {
7785
+ content = content.replaceAll(locale.thousandsSeparator, "");
7786
+ }
7787
+ return content.replace(locale.decimalSeparator, ".");
7793
7788
  }
7794
7789
  /**
7795
7790
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -9467,15 +9462,6 @@ function createLineChartRuntime(chart, getters) {
9467
9462
  }
9468
9463
  const colors = new ChartColors();
9469
9464
  for (let [index, { label, data }] of dataSetsValues.entries()) {
9470
- if (["linear", "time"].includes(axisType)) {
9471
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9472
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9473
- }
9474
- const color = colors.next();
9475
- let backgroundRGBA = colorToRGBA(color);
9476
- if (chart.stacked) {
9477
- backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9478
- }
9479
9465
  if (chart.cumulative) {
9480
9466
  let accumulator = 0;
9481
9467
  data = data.map((value) => {
@@ -9486,6 +9472,15 @@ function createLineChartRuntime(chart, getters) {
9486
9472
  return value;
9487
9473
  });
9488
9474
  }
9475
+ if (["linear", "time"].includes(axisType)) {
9476
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9477
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9478
+ }
9479
+ const color = colors.next();
9480
+ let backgroundRGBA = colorToRGBA(color);
9481
+ if (chart.stacked) {
9482
+ backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9483
+ }
9489
9484
  const backgroundColor = rgbaToHex(backgroundRGBA);
9490
9485
  const dataset = {
9491
9486
  label,
@@ -24283,7 +24278,13 @@ class SettingsPanel extends owl.Component {
24283
24278
  }
24284
24279
  async loadLocales() {
24285
24280
  this.loadedLocales = (await this.env.loadLocales())
24286
- .filter(isValidLocale)
24281
+ .filter((locale) => {
24282
+ const isValid = isValidLocale(locale);
24283
+ if (!isValid) {
24284
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
24285
+ }
24286
+ return isValid;
24287
+ })
24287
24288
  .sort((a, b) => a.name.localeCompare(b.name));
24288
24289
  }
24289
24290
  get numberFormatPreview() {
@@ -30066,6 +30067,7 @@ class Grid extends owl.Component {
30066
30067
  return;
30067
30068
  }
30068
30069
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
30070
+ ev.preventDefault();
30069
30071
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
30070
30072
  const target = this.env.model.getters.getSelectedZones();
30071
30073
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -34641,6 +34643,21 @@ class BordersPlugin extends CorePlugin {
34641
34643
  return [];
34642
34644
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
34643
34645
  }
34646
+ /**
34647
+ * Get all the rows which contains at least a border
34648
+ */
34649
+ getRowsWithBorders(sheetId) {
34650
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined$1);
34651
+ if (!sheetBorders)
34652
+ return [];
34653
+ const rowsWithBorders = new Set();
34654
+ for (const rowBorders of sheetBorders) {
34655
+ for (const rowBorder in rowBorders) {
34656
+ rowsWithBorders.add(parseInt(rowBorder, 10));
34657
+ }
34658
+ }
34659
+ return Array.from(rowsWithBorders);
34660
+ }
34644
34661
  /**
34645
34662
  * Get the range of all the rows in the sheet
34646
34663
  */
@@ -34690,7 +34707,7 @@ class BordersPlugin extends CorePlugin {
34690
34707
  destructive: false,
34691
34708
  });
34692
34709
  }
34693
- this.getRowsRange(sheetId)
34710
+ this.getRowsWithBorders(sheetId)
34694
34711
  .filter((row) => row >= start)
34695
34712
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
34696
34713
  .forEach((row) => {
@@ -35154,7 +35171,7 @@ function parseExpression(tokens, parent_priority = 0) {
35154
35171
  * Parse an expression (as a string) into an AST.
35155
35172
  */
35156
35173
  function parse(str) {
35157
- return parseTokens(tokenize(str));
35174
+ return parseTokens(rangeTokenize(str));
35158
35175
  }
35159
35176
  function parseTokens(tokens) {
35160
35177
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -41672,6 +41689,7 @@ class Evaluator {
41672
41689
  }
41673
41690
  finally {
41674
41691
  this.cellsBeingComputed.delete(cellId);
41692
+ this.nextPositionsToUpdate.delete(positionId);
41675
41693
  }
41676
41694
  }
41677
41695
  computeAndSave(position) {
@@ -41713,11 +41731,36 @@ class Evaluator {
41713
41731
  forEachSpreadPositionInMatrix(nbColumns, nbRows,
41714
41732
  // thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
41715
41733
  this.spreadValues(formulaPosition, formulaReturn));
41734
+ this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
41716
41735
  return createEvaluatedCell(formulaReturn[0][0].value, {
41717
41736
  format: cellData.format || formulaReturn[0][0]?.format,
41718
41737
  locale: this.getters.getLocale(),
41719
41738
  });
41720
41739
  }
41740
+ invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
41741
+ // the result matrix is split in 2 zones to exclude the array formula position
41742
+ const top = arrayFormulaPosition.row;
41743
+ const left = arrayFormulaPosition.col;
41744
+ const bottom = top + nbRows - 1;
41745
+ const leftColumnZone = {
41746
+ top: top + 1,
41747
+ bottom,
41748
+ left,
41749
+ right: left,
41750
+ };
41751
+ const rightPartZone = {
41752
+ top,
41753
+ bottom,
41754
+ left: left + 1,
41755
+ right: left + nbColumns - 1,
41756
+ };
41757
+ const sheetId = arrayFormulaPosition.sheetId;
41758
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
41759
+ { sheetId, zone: rightPartZone },
41760
+ { sheetId, zone: leftColumnZone },
41761
+ ]);
41762
+ this.nextPositionsToUpdate.addMany(invalidatedPositions);
41763
+ }
41721
41764
  assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
41722
41765
  const numberOfCols = this.getters.getNumberCols(sheetId);
41723
41766
  const numberOfRows = this.getters.getNumberRows(sheetId);
@@ -41766,9 +41809,6 @@ class Evaluator {
41766
41809
  });
41767
41810
  const positionId = this.encoder.encode(position);
41768
41811
  this.setEvaluatedCell(positionId, evaluatedCell);
41769
- // check if formula dependencies present in the spread zone
41770
- // if so, they need to be recomputed
41771
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41772
41812
  };
41773
41813
  }
41774
41814
  invalidateSpreading(positionId) {
@@ -57538,6 +57578,6 @@ exports.setTranslationMethod = setTranslationMethod;
57538
57578
  exports.tokenize = tokenize;
57539
57579
 
57540
57580
 
57541
- __info__.version = "17.0.28";
57542
- __info__.date = "2024-07-02T10:35:57.899Z";
57543
- __info__.hash = "7f86a16";
57581
+ __info__.version = "17.0.29";
57582
+ __info__.date = "2024-07-08T05:40:27.608Z";
57583
+ __info__.hash = "2d467e3";
@@ -996,7 +996,7 @@ declare function lazy<T>(fn: (() => T) | T): Lazy<T>;
996
996
  /**
997
997
  * Compares two objects.
998
998
  */
999
- declare function deepEquals(o1: any, o2: any, ignoreFunctions?: "ignoreFunctions"): boolean;
999
+ declare function deepEquals(o1: any, o2: any): boolean;
1000
1000
 
1001
1001
  interface ConstructorArgs {
1002
1002
  readonly zone: Readonly<Zone | UnboundedZone>;
@@ -2819,7 +2819,7 @@ type LocaleCode = string & Alias;
2819
2819
  interface Locale {
2820
2820
  name: string;
2821
2821
  code: LocaleCode;
2822
- thousandsSeparator: string;
2822
+ thousandsSeparator?: string;
2823
2823
  decimalSeparator: string;
2824
2824
  dateFormat: string;
2825
2825
  timeFormat: string;
@@ -3317,6 +3317,10 @@ declare class BordersPlugin extends CorePlugin<BordersPluginState> implements Bo
3317
3317
  * Get all the columns which contains at least a border
3318
3318
  */
3319
3319
  private getColumnsWithBorders;
3320
+ /**
3321
+ * Get all the rows which contains at least a border
3322
+ */
3323
+ private getRowsWithBorders;
3320
3324
  /**
3321
3325
  * Get the range of all the rows in the sheet
3322
3326
  */
@@ -5956,7 +5960,6 @@ declare class ChartJsComponent extends Component<Props$B, SpreadsheetChildEnv> {
5956
5960
  static template: string;
5957
5961
  private canvas;
5958
5962
  private chart?;
5959
- private currentRuntime;
5960
5963
  get background(): string;
5961
5964
  get canvasStyle(): string;
5962
5965
  get chartRuntime(): ChartJSRuntime;
@@ -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.28
6
- * @date 2024-07-02T10:35:57.899Z
7
- * @hash 7f86a16
5
+ * @version 17.0.29
6
+ * @date 2024-07-08T05:40:27.608Z
7
+ * @hash 2d467e3
8
8
  */
9
9
 
10
10
  import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -657,7 +657,7 @@ function getAddHeaderStartIndex(position, base) {
657
657
  /**
658
658
  * Compares two objects.
659
659
  */
660
- function deepEquals(o1, o2, ignoreFunctions) {
660
+ function deepEquals(o1, o2) {
661
661
  if (o1 === o2)
662
662
  return true;
663
663
  if ((o1 && !o2) || (o2 && !o1))
@@ -673,17 +673,13 @@ function deepEquals(o1, o2, ignoreFunctions) {
673
673
  }
674
674
  }
675
675
  for (const key in o1) {
676
- const typeOfO1Key = typeof o1[key];
677
- if (typeOfO1Key !== typeof o2[key])
676
+ if (typeof o1[key] !== typeof o2[key])
678
677
  return false;
679
- if (typeOfO1Key === "object") {
680
- if (!deepEquals(o1[key], o2[key], ignoreFunctions))
678
+ if (typeof o1[key] === "object") {
679
+ if (!deepEquals(o1[key], o2[key]))
681
680
  return false;
682
681
  }
683
682
  else {
684
- if (ignoreFunctions && typeOfO1Key === "function") {
685
- continue;
686
- }
687
683
  if (o1[key] !== o2[key])
688
684
  return false;
689
685
  }
@@ -754,11 +750,9 @@ class JetSet extends Set {
754
750
  return this;
755
751
  }
756
752
  deleteMany(iterable) {
757
- let wasDeleted = false;
758
753
  for (const element of iterable) {
759
- wasDeleted ||= super.delete(element);
754
+ super.delete(element);
760
755
  }
761
- return wasDeleted;
762
756
  }
763
757
  }
764
758
  /**
@@ -1815,7 +1809,7 @@ const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSepa
1815
1809
  });
1816
1810
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1817
1811
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1818
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1812
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1819
1813
  const pIntegerAndDecimals = `(\\d+(${thousandsSeparator}\\d{3,})*(${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1820
1814
  const pOnlyDecimals = `(${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1821
1815
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1842,7 +1836,7 @@ function isNumber(value, locale) {
1842
1836
  return getNumberRegex(locale).test(value.trim());
1843
1837
  }
1844
1838
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1845
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1839
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1846
1840
  });
1847
1841
  /**
1848
1842
  * Convert a string into a number. It assumes that the string actually represents
@@ -2544,6 +2538,9 @@ function getPredicate(descr, isQuery, locale) {
2544
2538
  return result;
2545
2539
  }
2546
2540
  function operandToRegExp(operand) {
2541
+ if (operand === "*") {
2542
+ return /.+/;
2543
+ }
2547
2544
  let exp = "";
2548
2545
  let predecessor = "";
2549
2546
  for (let char of operand) {
@@ -2567,9 +2564,9 @@ function operandToRegExp(operand) {
2567
2564
  }
2568
2565
  return new RegExp("^" + exp + "$", "i");
2569
2566
  }
2570
- function evaluatePredicate(value, criterion) {
2567
+ function evaluatePredicate(value = "", criterion) {
2571
2568
  const { operator, operand } = criterion;
2572
- if (value === undefined || operand === undefined || value === null || operand === null) {
2569
+ if (operand === undefined || value === null || operand === null) {
2573
2570
  return false;
2574
2571
  }
2575
2572
  if (typeof operand === "number" && operator === "=") {
@@ -4782,7 +4779,6 @@ class ChartJsComponent extends Component {
4782
4779
  static template = "o-spreadsheet-ChartJsComponent";
4783
4780
  canvas = useRef("graphContainer");
4784
4781
  chart;
4785
- currentRuntime;
4786
4782
  get background() {
4787
4783
  return this.chartRuntime.background;
4788
4784
  }
@@ -4799,17 +4795,10 @@ class ChartJsComponent extends Component {
4799
4795
  setup() {
4800
4796
  onMounted(() => {
4801
4797
  const runtime = this.chartRuntime;
4802
- this.currentRuntime = runtime;
4803
4798
  // Note: chartJS modify the runtime in place, so it's important to give it a copy
4804
4799
  this.createChart(deepCopy(runtime.chartJsConfig));
4805
4800
  });
4806
- useEffect(() => {
4807
- const runtime = this.chartRuntime;
4808
- if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
4809
- this.currentRuntime = runtime;
4810
- this.updateChartJs(deepCopy(runtime));
4811
- }
4812
- });
4801
+ useEffect(() => this.updateChartJs(deepCopy(this.chartRuntime)), () => [this.chartRuntime]);
4813
4802
  }
4814
4803
  createChart(chartData) {
4815
4804
  const canvas = this.canvas.el;
@@ -7677,19 +7666,22 @@ class TokenizingChars {
7677
7666
  }
7678
7667
 
7679
7668
  function isValidLocale(locale) {
7680
- if (!(locale &&
7681
- typeof locale === "object" &&
7682
- typeof locale.name === "string" &&
7683
- typeof locale.code === "string" &&
7684
- typeof locale.thousandsSeparator === "string" &&
7685
- typeof locale.decimalSeparator === "string" &&
7686
- typeof locale.dateFormat === "string" &&
7687
- typeof locale.timeFormat === "string" &&
7688
- typeof locale.formulaArgSeparator === "string")) {
7669
+ if (!locale ||
7670
+ typeof locale !== "object" ||
7671
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
7689
7672
  return false;
7690
7673
  }
7691
- if (!Object.values(locale).every((v) => v)) {
7692
- return false;
7674
+ for (const property of [
7675
+ "code",
7676
+ "name",
7677
+ "decimalSeparator",
7678
+ "dateFormat",
7679
+ "timeFormat",
7680
+ "formulaArgSeparator",
7681
+ ]) {
7682
+ if (!locale[property] || typeof locale[property] !== "string") {
7683
+ return false;
7684
+ }
7693
7685
  }
7694
7686
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7695
7687
  return false;
@@ -7787,7 +7779,10 @@ function canonicalizeNumberLiteral(content, locale) {
7787
7779
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7788
7780
  return content;
7789
7781
  }
7790
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7782
+ if (locale.thousandsSeparator) {
7783
+ content = content.replaceAll(locale.thousandsSeparator, "");
7784
+ }
7785
+ return content.replace(locale.decimalSeparator, ".");
7791
7786
  }
7792
7787
  /**
7793
7788
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -9465,15 +9460,6 @@ function createLineChartRuntime(chart, getters) {
9465
9460
  }
9466
9461
  const colors = new ChartColors();
9467
9462
  for (let [index, { label, data }] of dataSetsValues.entries()) {
9468
- if (["linear", "time"].includes(axisType)) {
9469
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9470
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9471
- }
9472
- const color = colors.next();
9473
- let backgroundRGBA = colorToRGBA(color);
9474
- if (chart.stacked) {
9475
- backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9476
- }
9477
9463
  if (chart.cumulative) {
9478
9464
  let accumulator = 0;
9479
9465
  data = data.map((value) => {
@@ -9484,6 +9470,15 @@ function createLineChartRuntime(chart, getters) {
9484
9470
  return value;
9485
9471
  });
9486
9472
  }
9473
+ if (["linear", "time"].includes(axisType)) {
9474
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9475
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9476
+ }
9477
+ const color = colors.next();
9478
+ let backgroundRGBA = colorToRGBA(color);
9479
+ if (chart.stacked) {
9480
+ backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9481
+ }
9487
9482
  const backgroundColor = rgbaToHex(backgroundRGBA);
9488
9483
  const dataset = {
9489
9484
  label,
@@ -24281,7 +24276,13 @@ class SettingsPanel extends Component {
24281
24276
  }
24282
24277
  async loadLocales() {
24283
24278
  this.loadedLocales = (await this.env.loadLocales())
24284
- .filter(isValidLocale)
24279
+ .filter((locale) => {
24280
+ const isValid = isValidLocale(locale);
24281
+ if (!isValid) {
24282
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
24283
+ }
24284
+ return isValid;
24285
+ })
24285
24286
  .sort((a, b) => a.name.localeCompare(b.name));
24286
24287
  }
24287
24288
  get numberFormatPreview() {
@@ -30064,6 +30065,7 @@ class Grid extends Component {
30064
30065
  return;
30065
30066
  }
30066
30067
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
30068
+ ev.preventDefault();
30067
30069
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
30068
30070
  const target = this.env.model.getters.getSelectedZones();
30069
30071
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -34639,6 +34641,21 @@ class BordersPlugin extends CorePlugin {
34639
34641
  return [];
34640
34642
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
34641
34643
  }
34644
+ /**
34645
+ * Get all the rows which contains at least a border
34646
+ */
34647
+ getRowsWithBorders(sheetId) {
34648
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined$1);
34649
+ if (!sheetBorders)
34650
+ return [];
34651
+ const rowsWithBorders = new Set();
34652
+ for (const rowBorders of sheetBorders) {
34653
+ for (const rowBorder in rowBorders) {
34654
+ rowsWithBorders.add(parseInt(rowBorder, 10));
34655
+ }
34656
+ }
34657
+ return Array.from(rowsWithBorders);
34658
+ }
34642
34659
  /**
34643
34660
  * Get the range of all the rows in the sheet
34644
34661
  */
@@ -34688,7 +34705,7 @@ class BordersPlugin extends CorePlugin {
34688
34705
  destructive: false,
34689
34706
  });
34690
34707
  }
34691
- this.getRowsRange(sheetId)
34708
+ this.getRowsWithBorders(sheetId)
34692
34709
  .filter((row) => row >= start)
34693
34710
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
34694
34711
  .forEach((row) => {
@@ -35152,7 +35169,7 @@ function parseExpression(tokens, parent_priority = 0) {
35152
35169
  * Parse an expression (as a string) into an AST.
35153
35170
  */
35154
35171
  function parse(str) {
35155
- return parseTokens(tokenize(str));
35172
+ return parseTokens(rangeTokenize(str));
35156
35173
  }
35157
35174
  function parseTokens(tokens) {
35158
35175
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -41670,6 +41687,7 @@ class Evaluator {
41670
41687
  }
41671
41688
  finally {
41672
41689
  this.cellsBeingComputed.delete(cellId);
41690
+ this.nextPositionsToUpdate.delete(positionId);
41673
41691
  }
41674
41692
  }
41675
41693
  computeAndSave(position) {
@@ -41711,11 +41729,36 @@ class Evaluator {
41711
41729
  forEachSpreadPositionInMatrix(nbColumns, nbRows,
41712
41730
  // thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
41713
41731
  this.spreadValues(formulaPosition, formulaReturn));
41732
+ this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
41714
41733
  return createEvaluatedCell(formulaReturn[0][0].value, {
41715
41734
  format: cellData.format || formulaReturn[0][0]?.format,
41716
41735
  locale: this.getters.getLocale(),
41717
41736
  });
41718
41737
  }
41738
+ invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
41739
+ // the result matrix is split in 2 zones to exclude the array formula position
41740
+ const top = arrayFormulaPosition.row;
41741
+ const left = arrayFormulaPosition.col;
41742
+ const bottom = top + nbRows - 1;
41743
+ const leftColumnZone = {
41744
+ top: top + 1,
41745
+ bottom,
41746
+ left,
41747
+ right: left,
41748
+ };
41749
+ const rightPartZone = {
41750
+ top,
41751
+ bottom,
41752
+ left: left + 1,
41753
+ right: left + nbColumns - 1,
41754
+ };
41755
+ const sheetId = arrayFormulaPosition.sheetId;
41756
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
41757
+ { sheetId, zone: rightPartZone },
41758
+ { sheetId, zone: leftColumnZone },
41759
+ ]);
41760
+ this.nextPositionsToUpdate.addMany(invalidatedPositions);
41761
+ }
41719
41762
  assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
41720
41763
  const numberOfCols = this.getters.getNumberCols(sheetId);
41721
41764
  const numberOfRows = this.getters.getNumberRows(sheetId);
@@ -41764,9 +41807,6 @@ class Evaluator {
41764
41807
  });
41765
41808
  const positionId = this.encoder.encode(position);
41766
41809
  this.setEvaluatedCell(positionId, evaluatedCell);
41767
- // check if formula dependencies present in the spread zone
41768
- // if so, they need to be recomputed
41769
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41770
41810
  };
41771
41811
  }
41772
41812
  invalidateSpreading(positionId) {
@@ -57501,6 +57541,6 @@ const constants = {
57501
57541
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
57502
57542
 
57503
57543
 
57504
- __info__.version = "17.0.28";
57505
- __info__.date = "2024-07-02T10:35:57.899Z";
57506
- __info__.hash = "7f86a16";
57544
+ __info__.version = "17.0.29";
57545
+ __info__.date = "2024-07-08T05:40:27.608Z";
57546
+ __info__.hash = "2d467e3";