@odoo/o-spreadsheet 17.1.21 → 17.1.23

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.1.21
6
- * @date 2024-07-02T10:35:57.801Z
7
- * @hash ec4c09b
5
+ * @version 17.1.23
6
+ * @date 2024-07-11T06:37:01.931Z
7
+ * @hash 31e8450
8
8
  */
9
9
 
10
10
  import { Component, useRef, onMounted, onWillUnmount, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -544,7 +544,7 @@ function getAddHeaderStartIndex(position, base) {
544
544
  /**
545
545
  * Compares two objects.
546
546
  */
547
- function deepEquals(o1, o2, ignoreFunctions) {
547
+ function deepEquals(o1, o2) {
548
548
  if (o1 === o2)
549
549
  return true;
550
550
  if ((o1 && !o2) || (o2 && !o1))
@@ -560,17 +560,13 @@ function deepEquals(o1, o2, ignoreFunctions) {
560
560
  }
561
561
  }
562
562
  for (const key in o1) {
563
- const typeOfO1Key = typeof o1[key];
564
- if (typeOfO1Key !== typeof o2[key])
563
+ if (typeof o1[key] !== typeof o2[key])
565
564
  return false;
566
- if (typeOfO1Key === "object") {
567
- if (!deepEquals(o1[key], o2[key], ignoreFunctions))
565
+ if (typeof o1[key] === "object") {
566
+ if (!deepEquals(o1[key], o2[key]))
568
567
  return false;
569
568
  }
570
569
  else {
571
- if (ignoreFunctions && typeOfO1Key === "function") {
572
- continue;
573
- }
574
570
  if (o1[key] !== o2[key])
575
571
  return false;
576
572
  }
@@ -641,11 +637,9 @@ class JetSet extends Set {
641
637
  return this;
642
638
  }
643
639
  deleteMany(iterable) {
644
- let wasDeleted = false;
645
640
  for (const element of iterable) {
646
- wasDeleted ||= super.delete(element);
641
+ super.delete(element);
647
642
  }
648
- return wasDeleted;
649
643
  }
650
644
  }
651
645
  /**
@@ -1702,7 +1696,7 @@ const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSepa
1702
1696
  });
1703
1697
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1704
1698
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1705
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1699
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1706
1700
  const pIntegerAndDecimals = `(\\d+(${thousandsSeparator}\\d{3,})*(${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1707
1701
  const pOnlyDecimals = `(${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1708
1702
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1729,7 +1723,7 @@ function isNumber(value, locale) {
1729
1723
  return getNumberRegex(locale).test(value.trim());
1730
1724
  }
1731
1725
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1732
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1726
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1733
1727
  });
1734
1728
  /**
1735
1729
  * Convert a string into a number. It assumes that the string actually represents
@@ -2593,6 +2587,9 @@ function getPredicate(descr, isQuery, locale) {
2593
2587
  return result;
2594
2588
  }
2595
2589
  function operandToRegExp(operand) {
2590
+ if (operand === "*") {
2591
+ return /.+/;
2592
+ }
2596
2593
  let exp = "";
2597
2594
  let predecessor = "";
2598
2595
  for (let char of operand) {
@@ -2616,9 +2613,9 @@ function operandToRegExp(operand) {
2616
2613
  }
2617
2614
  return new RegExp("^" + exp + "$", "i");
2618
2615
  }
2619
- function evaluatePredicate(value, criterion) {
2616
+ function evaluatePredicate(value = "", criterion) {
2620
2617
  const { operator, operand } = criterion;
2621
- if (value === undefined || operand === undefined || value === null || operand === null) {
2618
+ if (operand === undefined || value === null || operand === null) {
2622
2619
  return false;
2623
2620
  }
2624
2621
  if (typeof operand === "number" && operator === "=") {
@@ -4852,7 +4849,6 @@ class ChartJsComponent extends Component {
4852
4849
  };
4853
4850
  canvas = useRef("graphContainer");
4854
4851
  chart;
4855
- currentRuntime;
4856
4852
  get background() {
4857
4853
  return this.chartRuntime.background;
4858
4854
  }
@@ -4869,18 +4865,11 @@ class ChartJsComponent extends Component {
4869
4865
  setup() {
4870
4866
  onMounted(() => {
4871
4867
  const runtime = this.chartRuntime;
4872
- this.currentRuntime = runtime;
4873
4868
  // Note: chartJS modify the runtime in place, so it's important to give it a copy
4874
4869
  this.createChart(deepCopy(runtime.chartJsConfig));
4875
4870
  });
4876
4871
  onWillUnmount(() => this.chart?.destroy());
4877
- useEffect(() => {
4878
- const runtime = this.chartRuntime;
4879
- if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
4880
- this.currentRuntime = runtime;
4881
- this.updateChartJs(deepCopy(runtime));
4882
- }
4883
- });
4872
+ useEffect(() => this.updateChartJs(deepCopy(this.chartRuntime)), () => [this.chartRuntime]);
4884
4873
  }
4885
4874
  createChart(chartData) {
4886
4875
  const canvas = this.canvas.el;
@@ -7716,19 +7705,22 @@ class TokenizingChars {
7716
7705
  }
7717
7706
 
7718
7707
  function isValidLocale(locale) {
7719
- if (!(locale &&
7720
- typeof locale === "object" &&
7721
- typeof locale.name === "string" &&
7722
- typeof locale.code === "string" &&
7723
- typeof locale.thousandsSeparator === "string" &&
7724
- typeof locale.decimalSeparator === "string" &&
7725
- typeof locale.dateFormat === "string" &&
7726
- typeof locale.timeFormat === "string" &&
7727
- typeof locale.formulaArgSeparator === "string")) {
7708
+ if (!locale ||
7709
+ typeof locale !== "object" ||
7710
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
7728
7711
  return false;
7729
7712
  }
7730
- if (!Object.values(locale).every((v) => v)) {
7731
- return false;
7713
+ for (const property of [
7714
+ "code",
7715
+ "name",
7716
+ "decimalSeparator",
7717
+ "dateFormat",
7718
+ "timeFormat",
7719
+ "formulaArgSeparator",
7720
+ ]) {
7721
+ if (!locale[property] || typeof locale[property] !== "string") {
7722
+ return false;
7723
+ }
7732
7724
  }
7733
7725
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7734
7726
  return false;
@@ -7826,7 +7818,10 @@ function canonicalizeNumberLiteral(content, locale) {
7826
7818
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7827
7819
  return content;
7828
7820
  }
7829
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7821
+ if (locale.thousandsSeparator) {
7822
+ content = content.replaceAll(locale.thousandsSeparator, "");
7823
+ }
7824
+ return content.replace(locale.decimalSeparator, ".");
7830
7825
  }
7831
7826
  /**
7832
7827
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -8472,7 +8467,7 @@ function truncateLabel(label) {
8472
8467
  /**
8473
8468
  * Get a default chart js configuration
8474
8469
  */
8475
- function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
8470
+ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels = true }) {
8476
8471
  const options = {
8477
8472
  // https://www.chartjs.org/docs/latest/general/responsive.html
8478
8473
  responsive: true,
@@ -9505,15 +9500,6 @@ function createLineChartRuntime(chart, getters) {
9505
9500
  }
9506
9501
  const colors = new ChartColors();
9507
9502
  for (let [index, { label, data }] of dataSetsValues.entries()) {
9508
- if (["linear", "time"].includes(axisType)) {
9509
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9510
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9511
- }
9512
- const color = colors.next();
9513
- let backgroundRGBA = colorToRGBA(color);
9514
- if (chart.stacked) {
9515
- backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9516
- }
9517
9503
  if (chart.cumulative) {
9518
9504
  let accumulator = 0;
9519
9505
  data = data.map((value) => {
@@ -9524,6 +9510,15 @@ function createLineChartRuntime(chart, getters) {
9524
9510
  return value;
9525
9511
  });
9526
9512
  }
9513
+ if (["linear", "time"].includes(axisType)) {
9514
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9515
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9516
+ }
9517
+ const color = colors.next();
9518
+ let backgroundRGBA = colorToRGBA(color);
9519
+ if (chart.stacked) {
9520
+ backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9521
+ }
9527
9522
  const backgroundColor = rgbaToHex(backgroundRGBA);
9528
9523
  const dataset = {
9529
9524
  label,
@@ -24779,7 +24774,13 @@ class SettingsPanel extends Component {
24779
24774
  }
24780
24775
  async loadLocales() {
24781
24776
  this.loadedLocales = (await this.env.loadLocales())
24782
- .filter(isValidLocale)
24777
+ .filter((locale) => {
24778
+ const isValid = isValidLocale(locale);
24779
+ if (!isValid) {
24780
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
24781
+ }
24782
+ return isValid;
24783
+ })
24783
24784
  .sort((a, b) => a.name.localeCompare(b.name));
24784
24785
  }
24785
24786
  get numberFormatPreview() {
@@ -30535,6 +30536,7 @@ class Grid extends Component {
30535
30536
  return;
30536
30537
  }
30537
30538
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
30539
+ ev.preventDefault();
30538
30540
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
30539
30541
  const target = this.env.model.getters.getSelectedZones();
30540
30542
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -35132,6 +35134,21 @@ class BordersPlugin extends CorePlugin {
35132
35134
  return [];
35133
35135
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
35134
35136
  }
35137
+ /**
35138
+ * Get all the rows which contains at least a border
35139
+ */
35140
+ getRowsWithBorders(sheetId) {
35141
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined$1);
35142
+ if (!sheetBorders)
35143
+ return [];
35144
+ const rowsWithBorders = new Set();
35145
+ for (const rowBorders of sheetBorders) {
35146
+ for (const rowBorder in rowBorders) {
35147
+ rowsWithBorders.add(parseInt(rowBorder, 10));
35148
+ }
35149
+ }
35150
+ return Array.from(rowsWithBorders);
35151
+ }
35135
35152
  /**
35136
35153
  * Get the range of all the rows in the sheet
35137
35154
  */
@@ -35181,7 +35198,7 @@ class BordersPlugin extends CorePlugin {
35181
35198
  destructive: false,
35182
35199
  });
35183
35200
  }
35184
- this.getRowsRange(sheetId)
35201
+ this.getRowsWithBorders(sheetId)
35185
35202
  .filter((row) => row >= start)
35186
35203
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
35187
35204
  .forEach((row) => {
@@ -35636,7 +35653,7 @@ function parseExpression(tokens, parent_priority = 0) {
35636
35653
  * Parse an expression (as a string) into an AST.
35637
35654
  */
35638
35655
  function parse(str) {
35639
- return parseTokens(tokenize(str));
35656
+ return parseTokens(rangeTokenize(str));
35640
35657
  }
35641
35658
  function parseTokens(tokens) {
35642
35659
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -42130,6 +42147,7 @@ class Evaluator {
42130
42147
  }
42131
42148
  finally {
42132
42149
  this.cellsBeingComputed.delete(cellId);
42150
+ this.nextPositionsToUpdate.delete(positionId);
42133
42151
  }
42134
42152
  }
42135
42153
  computeAndSave(position) {
@@ -42158,11 +42176,36 @@ class Evaluator {
42158
42176
  forEachSpreadPositionInMatrix(nbColumns, nbRows,
42159
42177
  // thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
42160
42178
  this.spreadValues(formulaPosition, formulaReturn));
42179
+ this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
42161
42180
  return createEvaluatedCell(formulaReturn[0][0].value, {
42162
42181
  format: cellData.format || formulaReturn[0][0]?.format,
42163
42182
  locale: this.getters.getLocale(),
42164
42183
  }, formulaReturn[0][0].message);
42165
42184
  }
42185
+ invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
42186
+ // the result matrix is split in 2 zones to exclude the array formula position
42187
+ const top = arrayFormulaPosition.row;
42188
+ const left = arrayFormulaPosition.col;
42189
+ const bottom = top + nbRows - 1;
42190
+ const leftColumnZone = {
42191
+ top: top + 1,
42192
+ bottom,
42193
+ left,
42194
+ right: left,
42195
+ };
42196
+ const rightPartZone = {
42197
+ top,
42198
+ bottom,
42199
+ left: left + 1,
42200
+ right: left + nbColumns - 1,
42201
+ };
42202
+ const sheetId = arrayFormulaPosition.sheetId;
42203
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
42204
+ { sheetId, zone: rightPartZone },
42205
+ { sheetId, zone: leftColumnZone },
42206
+ ]);
42207
+ this.nextPositionsToUpdate.addMany(invalidatedPositions);
42208
+ }
42166
42209
  assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
42167
42210
  const numberOfCols = this.getters.getNumberCols(sheetId);
42168
42211
  const numberOfRows = this.getters.getNumberRows(sheetId);
@@ -42211,9 +42254,6 @@ class Evaluator {
42211
42254
  }, matrixResult[i][j].message);
42212
42255
  const positionId = this.encoder.encode(position);
42213
42256
  this.setEvaluatedCell(positionId, evaluatedCell);
42214
- // check if formula dependencies present in the spread zone
42215
- // if so, they need to be recomputed
42216
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
42217
42257
  };
42218
42258
  }
42219
42259
  invalidateSpreading(positionId) {
@@ -57801,6 +57841,6 @@ const constants = {
57801
57841
  export { AbstractChart, CellErrorType, 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 };
57802
57842
 
57803
57843
 
57804
- __info__.version = "17.1.21";
57805
- __info__.date = "2024-07-02T10:35:57.801Z";
57806
- __info__.hash = "ec4c09b";
57844
+ __info__.version = "17.1.23";
57845
+ __info__.date = "2024-07-11T06:37:01.931Z";
57846
+ __info__.hash = "31e8450";
@@ -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.1.21
6
- * @date 2024-07-02T10:35:57.801Z
7
- * @hash ec4c09b
5
+ * @version 17.1.23
6
+ * @date 2024-07-11T06:37:01.931Z
7
+ * @hash 31e8450
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -545,7 +545,7 @@
545
545
  /**
546
546
  * Compares two objects.
547
547
  */
548
- function deepEquals(o1, o2, ignoreFunctions) {
548
+ function deepEquals(o1, o2) {
549
549
  if (o1 === o2)
550
550
  return true;
551
551
  if ((o1 && !o2) || (o2 && !o1))
@@ -561,17 +561,13 @@
561
561
  }
562
562
  }
563
563
  for (const key in o1) {
564
- const typeOfO1Key = typeof o1[key];
565
- if (typeOfO1Key !== typeof o2[key])
564
+ if (typeof o1[key] !== typeof o2[key])
566
565
  return false;
567
- if (typeOfO1Key === "object") {
568
- if (!deepEquals(o1[key], o2[key], ignoreFunctions))
566
+ if (typeof o1[key] === "object") {
567
+ if (!deepEquals(o1[key], o2[key]))
569
568
  return false;
570
569
  }
571
570
  else {
572
- if (ignoreFunctions && typeOfO1Key === "function") {
573
- continue;
574
- }
575
571
  if (o1[key] !== o2[key])
576
572
  return false;
577
573
  }
@@ -642,11 +638,9 @@
642
638
  return this;
643
639
  }
644
640
  deleteMany(iterable) {
645
- let wasDeleted = false;
646
641
  for (const element of iterable) {
647
- wasDeleted ||= super.delete(element);
642
+ super.delete(element);
648
643
  }
649
- return wasDeleted;
650
644
  }
651
645
  }
652
646
  /**
@@ -1703,7 +1697,7 @@
1703
1697
  });
1704
1698
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1705
1699
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1706
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1700
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1707
1701
  const pIntegerAndDecimals = `(\\d+(${thousandsSeparator}\\d{3,})*(${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1708
1702
  const pOnlyDecimals = `(${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1709
1703
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1730,7 +1724,7 @@
1730
1724
  return getNumberRegex(locale).test(value.trim());
1731
1725
  }
1732
1726
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1733
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1727
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1734
1728
  });
1735
1729
  /**
1736
1730
  * Convert a string into a number. It assumes that the string actually represents
@@ -2594,6 +2588,9 @@
2594
2588
  return result;
2595
2589
  }
2596
2590
  function operandToRegExp(operand) {
2591
+ if (operand === "*") {
2592
+ return /.+/;
2593
+ }
2597
2594
  let exp = "";
2598
2595
  let predecessor = "";
2599
2596
  for (let char of operand) {
@@ -2617,9 +2614,9 @@
2617
2614
  }
2618
2615
  return new RegExp("^" + exp + "$", "i");
2619
2616
  }
2620
- function evaluatePredicate(value, criterion) {
2617
+ function evaluatePredicate(value = "", criterion) {
2621
2618
  const { operator, operand } = criterion;
2622
- if (value === undefined || operand === undefined || value === null || operand === null) {
2619
+ if (operand === undefined || value === null || operand === null) {
2623
2620
  return false;
2624
2621
  }
2625
2622
  if (typeof operand === "number" && operator === "=") {
@@ -4853,7 +4850,6 @@
4853
4850
  };
4854
4851
  canvas = owl.useRef("graphContainer");
4855
4852
  chart;
4856
- currentRuntime;
4857
4853
  get background() {
4858
4854
  return this.chartRuntime.background;
4859
4855
  }
@@ -4870,18 +4866,11 @@
4870
4866
  setup() {
4871
4867
  owl.onMounted(() => {
4872
4868
  const runtime = this.chartRuntime;
4873
- this.currentRuntime = runtime;
4874
4869
  // Note: chartJS modify the runtime in place, so it's important to give it a copy
4875
4870
  this.createChart(deepCopy(runtime.chartJsConfig));
4876
4871
  });
4877
4872
  owl.onWillUnmount(() => this.chart?.destroy());
4878
- owl.useEffect(() => {
4879
- const runtime = this.chartRuntime;
4880
- if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
4881
- this.currentRuntime = runtime;
4882
- this.updateChartJs(deepCopy(runtime));
4883
- }
4884
- });
4873
+ owl.useEffect(() => this.updateChartJs(deepCopy(this.chartRuntime)), () => [this.chartRuntime]);
4885
4874
  }
4886
4875
  createChart(chartData) {
4887
4876
  const canvas = this.canvas.el;
@@ -7717,19 +7706,22 @@
7717
7706
  }
7718
7707
 
7719
7708
  function isValidLocale(locale) {
7720
- if (!(locale &&
7721
- typeof locale === "object" &&
7722
- typeof locale.name === "string" &&
7723
- typeof locale.code === "string" &&
7724
- typeof locale.thousandsSeparator === "string" &&
7725
- typeof locale.decimalSeparator === "string" &&
7726
- typeof locale.dateFormat === "string" &&
7727
- typeof locale.timeFormat === "string" &&
7728
- typeof locale.formulaArgSeparator === "string")) {
7709
+ if (!locale ||
7710
+ typeof locale !== "object" ||
7711
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
7729
7712
  return false;
7730
7713
  }
7731
- if (!Object.values(locale).every((v) => v)) {
7732
- return false;
7714
+ for (const property of [
7715
+ "code",
7716
+ "name",
7717
+ "decimalSeparator",
7718
+ "dateFormat",
7719
+ "timeFormat",
7720
+ "formulaArgSeparator",
7721
+ ]) {
7722
+ if (!locale[property] || typeof locale[property] !== "string") {
7723
+ return false;
7724
+ }
7733
7725
  }
7734
7726
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7735
7727
  return false;
@@ -7827,7 +7819,10 @@
7827
7819
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7828
7820
  return content;
7829
7821
  }
7830
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7822
+ if (locale.thousandsSeparator) {
7823
+ content = content.replaceAll(locale.thousandsSeparator, "");
7824
+ }
7825
+ return content.replace(locale.decimalSeparator, ".");
7831
7826
  }
7832
7827
  /**
7833
7828
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -8473,7 +8468,7 @@
8473
8468
  /**
8474
8469
  * Get a default chart js configuration
8475
8470
  */
8476
- function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
8471
+ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels = true }) {
8477
8472
  const options = {
8478
8473
  // https://www.chartjs.org/docs/latest/general/responsive.html
8479
8474
  responsive: true,
@@ -9506,15 +9501,6 @@
9506
9501
  }
9507
9502
  const colors = new ChartColors();
9508
9503
  for (let [index, { label, data }] of dataSetsValues.entries()) {
9509
- if (["linear", "time"].includes(axisType)) {
9510
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9511
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9512
- }
9513
- const color = colors.next();
9514
- let backgroundRGBA = colorToRGBA(color);
9515
- if (chart.stacked) {
9516
- backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9517
- }
9518
9504
  if (chart.cumulative) {
9519
9505
  let accumulator = 0;
9520
9506
  data = data.map((value) => {
@@ -9525,6 +9511,15 @@
9525
9511
  return value;
9526
9512
  });
9527
9513
  }
9514
+ if (["linear", "time"].includes(axisType)) {
9515
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
9516
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
9517
+ }
9518
+ const color = colors.next();
9519
+ let backgroundRGBA = colorToRGBA(color);
9520
+ if (chart.stacked) {
9521
+ backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
9522
+ }
9528
9523
  const backgroundColor = rgbaToHex(backgroundRGBA);
9529
9524
  const dataset = {
9530
9525
  label,
@@ -24780,7 +24775,13 @@
24780
24775
  }
24781
24776
  async loadLocales() {
24782
24777
  this.loadedLocales = (await this.env.loadLocales())
24783
- .filter(isValidLocale)
24778
+ .filter((locale) => {
24779
+ const isValid = isValidLocale(locale);
24780
+ if (!isValid) {
24781
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
24782
+ }
24783
+ return isValid;
24784
+ })
24784
24785
  .sort((a, b) => a.name.localeCompare(b.name));
24785
24786
  }
24786
24787
  get numberFormatPreview() {
@@ -30536,6 +30537,7 @@
30536
30537
  return;
30537
30538
  }
30538
30539
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
30540
+ ev.preventDefault();
30539
30541
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
30540
30542
  const target = this.env.model.getters.getSelectedZones();
30541
30543
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -35133,6 +35135,21 @@
35133
35135
  return [];
35134
35136
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
35135
35137
  }
35138
+ /**
35139
+ * Get all the rows which contains at least a border
35140
+ */
35141
+ getRowsWithBorders(sheetId) {
35142
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined$1);
35143
+ if (!sheetBorders)
35144
+ return [];
35145
+ const rowsWithBorders = new Set();
35146
+ for (const rowBorders of sheetBorders) {
35147
+ for (const rowBorder in rowBorders) {
35148
+ rowsWithBorders.add(parseInt(rowBorder, 10));
35149
+ }
35150
+ }
35151
+ return Array.from(rowsWithBorders);
35152
+ }
35136
35153
  /**
35137
35154
  * Get the range of all the rows in the sheet
35138
35155
  */
@@ -35182,7 +35199,7 @@
35182
35199
  destructive: false,
35183
35200
  });
35184
35201
  }
35185
- this.getRowsRange(sheetId)
35202
+ this.getRowsWithBorders(sheetId)
35186
35203
  .filter((row) => row >= start)
35187
35204
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
35188
35205
  .forEach((row) => {
@@ -35637,7 +35654,7 @@
35637
35654
  * Parse an expression (as a string) into an AST.
35638
35655
  */
35639
35656
  function parse(str) {
35640
- return parseTokens(tokenize(str));
35657
+ return parseTokens(rangeTokenize(str));
35641
35658
  }
35642
35659
  function parseTokens(tokens) {
35643
35660
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -42131,6 +42148,7 @@
42131
42148
  }
42132
42149
  finally {
42133
42150
  this.cellsBeingComputed.delete(cellId);
42151
+ this.nextPositionsToUpdate.delete(positionId);
42134
42152
  }
42135
42153
  }
42136
42154
  computeAndSave(position) {
@@ -42159,11 +42177,36 @@
42159
42177
  forEachSpreadPositionInMatrix(nbColumns, nbRows,
42160
42178
  // thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
42161
42179
  this.spreadValues(formulaPosition, formulaReturn));
42180
+ this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
42162
42181
  return createEvaluatedCell(formulaReturn[0][0].value, {
42163
42182
  format: cellData.format || formulaReturn[0][0]?.format,
42164
42183
  locale: this.getters.getLocale(),
42165
42184
  }, formulaReturn[0][0].message);
42166
42185
  }
42186
+ invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
42187
+ // the result matrix is split in 2 zones to exclude the array formula position
42188
+ const top = arrayFormulaPosition.row;
42189
+ const left = arrayFormulaPosition.col;
42190
+ const bottom = top + nbRows - 1;
42191
+ const leftColumnZone = {
42192
+ top: top + 1,
42193
+ bottom,
42194
+ left,
42195
+ right: left,
42196
+ };
42197
+ const rightPartZone = {
42198
+ top,
42199
+ bottom,
42200
+ left: left + 1,
42201
+ right: left + nbColumns - 1,
42202
+ };
42203
+ const sheetId = arrayFormulaPosition.sheetId;
42204
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
42205
+ { sheetId, zone: rightPartZone },
42206
+ { sheetId, zone: leftColumnZone },
42207
+ ]);
42208
+ this.nextPositionsToUpdate.addMany(invalidatedPositions);
42209
+ }
42167
42210
  assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
42168
42211
  const numberOfCols = this.getters.getNumberCols(sheetId);
42169
42212
  const numberOfRows = this.getters.getNumberRows(sheetId);
@@ -42212,9 +42255,6 @@
42212
42255
  }, matrixResult[i][j].message);
42213
42256
  const positionId = this.encoder.encode(position);
42214
42257
  this.setEvaluatedCell(positionId, evaluatedCell);
42215
- // check if formula dependencies present in the spread zone
42216
- // if so, they need to be recomputed
42217
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
42218
42258
  };
42219
42259
  }
42220
42260
  invalidateSpreading(positionId) {
@@ -57838,9 +57878,9 @@
57838
57878
  exports.tokenize = tokenize;
57839
57879
 
57840
57880
 
57841
- __info__.version = "17.1.21";
57842
- __info__.date = "2024-07-02T10:35:57.801Z";
57843
- __info__.hash = "ec4c09b";
57881
+ __info__.version = "17.1.23";
57882
+ __info__.date = "2024-07-11T06:37:01.931Z";
57883
+ __info__.hash = "31e8450";
57844
57884
 
57845
57885
 
57846
57886
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);