@odoo/o-spreadsheet 17.2.14 → 17.2.16

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.2.14
7
- * @date 2024-07-02T10:35:53.850Z
8
- * @hash fb0d979
6
+ * @version 17.2.16
7
+ * @date 2024-07-11T06:38:53.101Z
8
+ * @hash 9f20685
9
9
  */
10
10
 
11
11
  'use strict';
@@ -577,7 +577,7 @@ function getAddHeaderStartIndex(position, base) {
577
577
  /**
578
578
  * Compares two objects.
579
579
  */
580
- function deepEquals(o1, o2, ignoreFunctions) {
580
+ function deepEquals(o1, o2) {
581
581
  if (o1 === o2)
582
582
  return true;
583
583
  if ((o1 && !o2) || (o2 && !o1))
@@ -593,17 +593,13 @@ function deepEquals(o1, o2, ignoreFunctions) {
593
593
  }
594
594
  }
595
595
  for (const key in o1) {
596
- const typeOfO1Key = typeof o1[key];
597
- if (typeOfO1Key !== typeof o2[key])
596
+ if (typeof o1[key] !== typeof o2[key])
598
597
  return false;
599
- if (typeOfO1Key === "object") {
600
- if (!deepEquals(o1[key], o2[key], ignoreFunctions))
598
+ if (typeof o1[key] === "object") {
599
+ if (!deepEquals(o1[key], o2[key]))
601
600
  return false;
602
601
  }
603
602
  else {
604
- if (ignoreFunctions && typeOfO1Key === "function") {
605
- continue;
606
- }
607
603
  if (o1[key] !== o2[key])
608
604
  return false;
609
605
  }
@@ -1752,7 +1748,7 @@ const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSepa
1752
1748
  });
1753
1749
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1754
1750
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1755
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1751
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1756
1752
  const pIntegerAndDecimals = `(\\d+(${thousandsSeparator}\\d{3,})*(${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1757
1753
  const pOnlyDecimals = `(${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1758
1754
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1779,7 +1775,7 @@ function isNumber(value, locale) {
1779
1775
  return getNumberRegex(locale).test(value.trim());
1780
1776
  }
1781
1777
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1782
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1778
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1783
1779
  });
1784
1780
  /**
1785
1781
  * Convert a string into a number. It assumes that the string actually represents
@@ -2648,6 +2644,9 @@ function getPredicate(descr, locale) {
2648
2644
  * If the character is a special regular expression character, it is escaped with "\\".
2649
2645
  */
2650
2646
  const wildcardToRegExp = memoize(function wildcardToRegExp(operand) {
2647
+ if (operand === "*") {
2648
+ return /.+/;
2649
+ }
2651
2650
  let exp = "";
2652
2651
  let predecessor = "";
2653
2652
  for (let char of operand) {
@@ -2671,9 +2670,9 @@ const wildcardToRegExp = memoize(function wildcardToRegExp(operand) {
2671
2670
  }
2672
2671
  return new RegExp("^" + exp + "$", "i");
2673
2672
  });
2674
- function evaluatePredicate(value, criterion) {
2673
+ function evaluatePredicate(value = "", criterion) {
2675
2674
  const { operator, operand } = criterion;
2676
- if (value === undefined || operand === undefined || value === null || operand === null) {
2675
+ if (operand === undefined || value === null || operand === null) {
2677
2676
  return false;
2678
2677
  }
2679
2678
  if (typeof operand === "number" && operator === "=") {
@@ -5359,19 +5358,22 @@ class TokenizingChars {
5359
5358
  }
5360
5359
 
5361
5360
  function isValidLocale(locale) {
5362
- if (!(locale &&
5363
- typeof locale === "object" &&
5364
- typeof locale.name === "string" &&
5365
- typeof locale.code === "string" &&
5366
- typeof locale.thousandsSeparator === "string" &&
5367
- typeof locale.decimalSeparator === "string" &&
5368
- typeof locale.dateFormat === "string" &&
5369
- typeof locale.timeFormat === "string" &&
5370
- typeof locale.formulaArgSeparator === "string")) {
5361
+ if (!locale ||
5362
+ typeof locale !== "object" ||
5363
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
5371
5364
  return false;
5372
5365
  }
5373
- if (!Object.values(locale).every((v) => v)) {
5374
- return false;
5366
+ for (const property of [
5367
+ "code",
5368
+ "name",
5369
+ "decimalSeparator",
5370
+ "dateFormat",
5371
+ "timeFormat",
5372
+ "formulaArgSeparator",
5373
+ ]) {
5374
+ if (!locale[property] || typeof locale[property] !== "string") {
5375
+ return false;
5376
+ }
5375
5377
  }
5376
5378
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
5377
5379
  return false;
@@ -5469,7 +5471,10 @@ function canonicalizeNumberLiteral(content, locale) {
5469
5471
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
5470
5472
  return content;
5471
5473
  }
5472
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
5474
+ if (locale.thousandsSeparator) {
5475
+ content = content.replaceAll(locale.thousandsSeparator, "");
5476
+ }
5477
+ return content.replace(locale.decimalSeparator, ".");
5473
5478
  }
5474
5479
  /**
5475
5480
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -6453,6 +6458,146 @@ function transformRangeData(range, executed) {
6453
6458
  return undefined;
6454
6459
  }
6455
6460
 
6461
+ var State;
6462
+ (function (State) {
6463
+ /**
6464
+ * Initial state.
6465
+ * Expecting any reference for the left part of a range
6466
+ * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
6467
+ */
6468
+ State[State["LeftRef"] = 0] = "LeftRef";
6469
+ /**
6470
+ * Expecting any reference for the right part of a range
6471
+ * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
6472
+ */
6473
+ State[State["RightRef"] = 1] = "RightRef";
6474
+ /**
6475
+ * Expecting the separator without any constraint on the right part
6476
+ */
6477
+ State[State["Separator"] = 2] = "Separator";
6478
+ /**
6479
+ * Expecting the separator for a full column range
6480
+ */
6481
+ State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
6482
+ /**
6483
+ * Expecting the separator for a full row range
6484
+ */
6485
+ State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
6486
+ /**
6487
+ * Expecting the right part of a full column range
6488
+ * e.g. "1", "A1"
6489
+ */
6490
+ State[State["RightColumnRef"] = 5] = "RightColumnRef";
6491
+ /**
6492
+ * Expecting the right part of a full row range
6493
+ * e.g. "A", "A1"
6494
+ */
6495
+ State[State["RightRowRef"] = 6] = "RightRowRef";
6496
+ /**
6497
+ * Final state. A range has been matched
6498
+ */
6499
+ State[State["Found"] = 7] = "Found";
6500
+ })(State || (State = {}));
6501
+ const goTo = (state, guard = () => true) => [
6502
+ {
6503
+ goTo: state,
6504
+ guard,
6505
+ },
6506
+ ];
6507
+ const goToMulti = (state, guard = () => true) => ({
6508
+ goTo: state,
6509
+ guard,
6510
+ });
6511
+ const machine = {
6512
+ [State.LeftRef]: {
6513
+ REFERENCE: goTo(State.Separator),
6514
+ NUMBER: goTo(State.FullRowSeparator),
6515
+ SYMBOL: [
6516
+ goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
6517
+ goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
6518
+ ],
6519
+ },
6520
+ [State.FullColumnSeparator]: {
6521
+ SPACE: goTo(State.FullColumnSeparator),
6522
+ OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
6523
+ },
6524
+ [State.FullRowSeparator]: {
6525
+ SPACE: goTo(State.FullRowSeparator),
6526
+ OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
6527
+ },
6528
+ [State.Separator]: {
6529
+ SPACE: goTo(State.Separator),
6530
+ OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
6531
+ },
6532
+ [State.RightRef]: {
6533
+ SPACE: goTo(State.RightRef),
6534
+ NUMBER: goTo(State.Found),
6535
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
6536
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
6537
+ },
6538
+ [State.RightColumnRef]: {
6539
+ SPACE: goTo(State.RightColumnRef),
6540
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
6541
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
6542
+ },
6543
+ [State.RightRowRef]: {
6544
+ SPACE: goTo(State.RightRowRef),
6545
+ NUMBER: goTo(State.Found),
6546
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
6547
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
6548
+ },
6549
+ [State.Found]: {},
6550
+ };
6551
+ /**
6552
+ * Check if the list of tokens starts with a sequence of tokens representing
6553
+ * a range.
6554
+ * If a range is found, the sequence is removed from the list and is returned
6555
+ * as a single token.
6556
+ */
6557
+ function matchReference(tokens) {
6558
+ let head = 0;
6559
+ let transitions = machine[State.LeftRef];
6560
+ let matchedTokens = "";
6561
+ while (transitions !== undefined) {
6562
+ const token = tokens[head++];
6563
+ if (!token) {
6564
+ return null;
6565
+ }
6566
+ const transition = transitions[token.type]?.find((transition) => transition.guard(token));
6567
+ const nextState = transition ? transition.goTo : undefined;
6568
+ switch (nextState) {
6569
+ case undefined:
6570
+ return null;
6571
+ case State.Found:
6572
+ matchedTokens += token.value;
6573
+ tokens.splice(0, head);
6574
+ return {
6575
+ type: "REFERENCE",
6576
+ value: matchedTokens,
6577
+ };
6578
+ default:
6579
+ transitions = machine[nextState];
6580
+ matchedTokens += token.value;
6581
+ break;
6582
+ }
6583
+ }
6584
+ return null;
6585
+ }
6586
+ /**
6587
+ * Take the result of the tokenizer and transform it to be usable in the
6588
+ * manipulations of range
6589
+ *
6590
+ * @param formula
6591
+ */
6592
+ function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
6593
+ const tokens = tokenize(formula, locale);
6594
+ const result = [];
6595
+ while (tokens.length) {
6596
+ result.push(matchReference(tokens) || tokens.shift());
6597
+ }
6598
+ return result;
6599
+ }
6600
+
6456
6601
  const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
6457
6602
  const UNARY_OPERATORS_PREFIX = ["-", "+"];
6458
6603
  const UNARY_OPERATORS_POSTFIX = ["%"];
@@ -6601,7 +6746,7 @@ function parseExpression(tokens, parent_priority = 0) {
6601
6746
  * Parse an expression (as a string) into an AST.
6602
6747
  */
6603
6748
  function parse(str) {
6604
- return parseTokens(tokenize(str));
6749
+ return parseTokens(rangeTokenize(str));
6605
6750
  }
6606
6751
  function parseTokens(tokens) {
6607
6752
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -6737,146 +6882,6 @@ function rightOperandToFormula(operationAST) {
6737
6882
  return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
6738
6883
  }
6739
6884
 
6740
- var State;
6741
- (function (State) {
6742
- /**
6743
- * Initial state.
6744
- * Expecting any reference for the left part of a range
6745
- * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
6746
- */
6747
- State[State["LeftRef"] = 0] = "LeftRef";
6748
- /**
6749
- * Expecting any reference for the right part of a range
6750
- * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
6751
- */
6752
- State[State["RightRef"] = 1] = "RightRef";
6753
- /**
6754
- * Expecting the separator without any constraint on the right part
6755
- */
6756
- State[State["Separator"] = 2] = "Separator";
6757
- /**
6758
- * Expecting the separator for a full column range
6759
- */
6760
- State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
6761
- /**
6762
- * Expecting the separator for a full row range
6763
- */
6764
- State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
6765
- /**
6766
- * Expecting the right part of a full column range
6767
- * e.g. "1", "A1"
6768
- */
6769
- State[State["RightColumnRef"] = 5] = "RightColumnRef";
6770
- /**
6771
- * Expecting the right part of a full row range
6772
- * e.g. "A", "A1"
6773
- */
6774
- State[State["RightRowRef"] = 6] = "RightRowRef";
6775
- /**
6776
- * Final state. A range has been matched
6777
- */
6778
- State[State["Found"] = 7] = "Found";
6779
- })(State || (State = {}));
6780
- const goTo = (state, guard = () => true) => [
6781
- {
6782
- goTo: state,
6783
- guard,
6784
- },
6785
- ];
6786
- const goToMulti = (state, guard = () => true) => ({
6787
- goTo: state,
6788
- guard,
6789
- });
6790
- const machine = {
6791
- [State.LeftRef]: {
6792
- REFERENCE: goTo(State.Separator),
6793
- NUMBER: goTo(State.FullRowSeparator),
6794
- SYMBOL: [
6795
- goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
6796
- goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
6797
- ],
6798
- },
6799
- [State.FullColumnSeparator]: {
6800
- SPACE: goTo(State.FullColumnSeparator),
6801
- OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
6802
- },
6803
- [State.FullRowSeparator]: {
6804
- SPACE: goTo(State.FullRowSeparator),
6805
- OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
6806
- },
6807
- [State.Separator]: {
6808
- SPACE: goTo(State.Separator),
6809
- OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
6810
- },
6811
- [State.RightRef]: {
6812
- SPACE: goTo(State.RightRef),
6813
- NUMBER: goTo(State.Found),
6814
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
6815
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
6816
- },
6817
- [State.RightColumnRef]: {
6818
- SPACE: goTo(State.RightColumnRef),
6819
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
6820
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
6821
- },
6822
- [State.RightRowRef]: {
6823
- SPACE: goTo(State.RightRowRef),
6824
- NUMBER: goTo(State.Found),
6825
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
6826
- SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
6827
- },
6828
- [State.Found]: {},
6829
- };
6830
- /**
6831
- * Check if the list of tokens starts with a sequence of tokens representing
6832
- * a range.
6833
- * If a range is found, the sequence is removed from the list and is returned
6834
- * as a single token.
6835
- */
6836
- function matchReference(tokens) {
6837
- let head = 0;
6838
- let transitions = machine[State.LeftRef];
6839
- let matchedTokens = "";
6840
- while (transitions !== undefined) {
6841
- const token = tokens[head++];
6842
- if (!token) {
6843
- return null;
6844
- }
6845
- const transition = transitions[token.type]?.find((transition) => transition.guard(token));
6846
- const nextState = transition ? transition.goTo : undefined;
6847
- switch (nextState) {
6848
- case undefined:
6849
- return null;
6850
- case State.Found:
6851
- matchedTokens += token.value;
6852
- tokens.splice(0, head);
6853
- return {
6854
- type: "REFERENCE",
6855
- value: matchedTokens,
6856
- };
6857
- default:
6858
- transitions = machine[nextState];
6859
- matchedTokens += token.value;
6860
- break;
6861
- }
6862
- }
6863
- return null;
6864
- }
6865
- /**
6866
- * Take the result of the tokenizer and transform it to be usable in the
6867
- * manipulations of range
6868
- *
6869
- * @param formula
6870
- */
6871
- function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
6872
- const tokens = tokenize(formula, locale);
6873
- const result = [];
6874
- while (tokens.length) {
6875
- result.push(matchReference(tokens) || tokens.shift());
6876
- }
6877
- return result;
6878
- }
6879
-
6880
6885
  /**
6881
6886
  * Add the following information on tokens:
6882
6887
  * - length
@@ -9200,7 +9205,6 @@ class ChartJsComponent extends owl.Component {
9200
9205
  };
9201
9206
  canvas = owl.useRef("graphContainer");
9202
9207
  chart;
9203
- currentRuntime;
9204
9208
  get background() {
9205
9209
  return this.chartRuntime.background;
9206
9210
  }
@@ -9217,18 +9221,11 @@ class ChartJsComponent extends owl.Component {
9217
9221
  setup() {
9218
9222
  owl.onMounted(() => {
9219
9223
  const runtime = this.chartRuntime;
9220
- this.currentRuntime = runtime;
9221
9224
  // Note: chartJS modify the runtime in place, so it's important to give it a copy
9222
9225
  this.createChart(deepCopy(runtime.chartJsConfig));
9223
9226
  });
9224
9227
  owl.onWillUnmount(() => this.chart?.destroy());
9225
- owl.useEffect(() => {
9226
- const runtime = this.chartRuntime;
9227
- if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
9228
- this.currentRuntime = runtime;
9229
- this.updateChartJs(deepCopy(runtime));
9230
- }
9231
- });
9228
+ owl.useEffect(() => this.updateChartJs(deepCopy(this.chartRuntime)), () => [this.chartRuntime]);
9232
9229
  }
9233
9230
  createChart(chartData) {
9234
9231
  const canvas = this.canvas.el;
@@ -19630,7 +19627,7 @@ function truncateLabel(label) {
19630
19627
  /**
19631
19628
  * Get a default chart js configuration
19632
19629
  */
19633
- function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
19630
+ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels = true }) {
19634
19631
  const options = {
19635
19632
  // https://www.chartjs.org/docs/latest/general/responsive.html
19636
19633
  responsive: true,
@@ -20548,10 +20545,6 @@ function createLineOrScatterChartRuntime(chart, getters) {
20548
20545
  const cumulative = "cumulative" in chart ? chart.cumulative : false;
20549
20546
  const colors = new ChartColors();
20550
20547
  for (let [index, { label, data }] of dataSetsValues.entries()) {
20551
- if (["linear", "time"].includes(axisType)) {
20552
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
20553
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
20554
- }
20555
20548
  const color = colors.next();
20556
20549
  let backgroundRGBA = colorToRGBA(color);
20557
20550
  if (stacked) {
@@ -20567,6 +20560,10 @@ function createLineOrScatterChartRuntime(chart, getters) {
20567
20560
  return value;
20568
20561
  });
20569
20562
  }
20563
+ if (["linear", "time"].includes(axisType)) {
20564
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
20565
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
20566
+ }
20570
20567
  const backgroundColor = rgbaToHex(backgroundRGBA);
20571
20568
  const dataset = {
20572
20569
  label,
@@ -30424,7 +30421,13 @@ class SettingsPanel extends owl.Component {
30424
30421
  }
30425
30422
  async loadLocales() {
30426
30423
  this.loadedLocales = (await this.env.loadLocales())
30427
- .filter(isValidLocale)
30424
+ .filter((locale) => {
30425
+ const isValid = isValidLocale(locale);
30426
+ if (!isValid) {
30427
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
30428
+ }
30429
+ return isValid;
30430
+ })
30428
30431
  .sort((a, b) => a.name.localeCompare(b.name));
30429
30432
  }
30430
30433
  get numberFormatPreview() {
@@ -36232,6 +36235,7 @@ class Grid extends owl.Component {
36232
36235
  return;
36233
36236
  }
36234
36237
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
36238
+ ev.preventDefault();
36235
36239
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
36236
36240
  const target = this.env.model.getters.getSelectedZones();
36237
36241
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -40808,6 +40812,21 @@ class BordersPlugin extends CorePlugin {
40808
40812
  return [];
40809
40813
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
40810
40814
  }
40815
+ /**
40816
+ * Get all the rows which contains at least a border
40817
+ */
40818
+ getRowsWithBorders(sheetId) {
40819
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined$1);
40820
+ if (!sheetBorders)
40821
+ return [];
40822
+ const rowsWithBorders = new Set();
40823
+ for (const rowBorders of sheetBorders) {
40824
+ for (const rowBorder in rowBorders) {
40825
+ rowsWithBorders.add(parseInt(rowBorder, 10));
40826
+ }
40827
+ }
40828
+ return Array.from(rowsWithBorders);
40829
+ }
40811
40830
  /**
40812
40831
  * Get the range of all the rows in the sheet
40813
40832
  */
@@ -40857,7 +40876,7 @@ class BordersPlugin extends CorePlugin {
40857
40876
  destructive: false,
40858
40877
  });
40859
40878
  }
40860
- this.getRowsRange(sheetId)
40879
+ this.getRowsWithBorders(sheetId)
40861
40880
  .filter((row) => row >= start)
40862
40881
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
40863
40882
  .forEach((row) => {
@@ -47480,7 +47499,7 @@ class PositionSet {
47480
47499
  return this.sheets[position.sheetId].getValue(position) === 1;
47481
47500
  }
47482
47501
  clear() {
47483
- const insertions = this.insertions;
47502
+ const insertions = [...this];
47484
47503
  this.insertions = [];
47485
47504
  for (const sheetId in this.sheets) {
47486
47505
  this.sheets[sheetId].clear();
@@ -47798,6 +47817,7 @@ class Evaluator {
47798
47817
  }
47799
47818
  finally {
47800
47819
  this.cellsBeingComputed.delete(cellId);
47820
+ this.nextPositionsToUpdate.delete(position);
47801
47821
  }
47802
47822
  }
47803
47823
  computeAndSave(position) {
@@ -47822,8 +47842,33 @@ class Evaluator {
47822
47842
  forEachSpreadPositionInMatrix(nbColumns, nbRows,
47823
47843
  // thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
47824
47844
  this.spreadValues(formulaPosition, formulaReturn));
47845
+ this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
47825
47846
  return createEvaluatedCell(nullValueToZeroValue(formulaReturn[0][0]), this.getters.getLocale(), cellData);
47826
47847
  }
47848
+ invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
47849
+ // the result matrix is split in 2 zones to exclude the array formula position
47850
+ const top = arrayFormulaPosition.row;
47851
+ const left = arrayFormulaPosition.col;
47852
+ const bottom = top + nbRows - 1;
47853
+ const leftColumnZone = {
47854
+ top: top + 1,
47855
+ bottom,
47856
+ left,
47857
+ right: left,
47858
+ };
47859
+ const rightPartZone = {
47860
+ top,
47861
+ bottom,
47862
+ left: left + 1,
47863
+ right: left + nbColumns - 1,
47864
+ };
47865
+ const sheetId = arrayFormulaPosition.sheetId;
47866
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
47867
+ { sheetId, zone: rightPartZone },
47868
+ { sheetId, zone: leftColumnZone },
47869
+ ]);
47870
+ this.nextPositionsToUpdate.addMany(invalidatedPositions);
47871
+ }
47827
47872
  assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
47828
47873
  const numberOfCols = this.getters.getNumberCols(sheetId);
47829
47874
  const numberOfRows = this.getters.getNumberRows(sheetId);
@@ -47866,9 +47911,6 @@ class Evaluator {
47866
47911
  const cell = this.getters.getCell(position);
47867
47912
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
47868
47913
  this.evaluatedCells.set(position, evaluatedCell);
47869
- // check if formula dependencies present in the spread zone
47870
- // if so, they need to be recomputed
47871
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
47872
47914
  };
47873
47915
  }
47874
47916
  invalidateSpreading(position) {
@@ -60841,6 +60883,6 @@ exports.tokenColors = tokenColors;
60841
60883
  exports.tokenize = tokenize;
60842
60884
 
60843
60885
 
60844
- __info__.version = "17.2.14";
60845
- __info__.date = "2024-07-02T10:35:53.850Z";
60846
- __info__.hash = "fb0d979";
60886
+ __info__.version = "17.2.16";
60887
+ __info__.date = "2024-07-11T06:38:53.101Z";
60888
+ __info__.hash = "9f20685";
@@ -1545,7 +1545,7 @@ type LocaleCode = string & Alias;
1545
1545
  interface Locale {
1546
1546
  name: string;
1547
1547
  code: LocaleCode;
1548
- thousandsSeparator: string;
1548
+ thousandsSeparator?: string;
1549
1549
  decimalSeparator: string;
1550
1550
  dateFormat: string;
1551
1551
  timeFormat: string;
@@ -2276,6 +2276,10 @@ declare class BordersPlugin extends CorePlugin<BordersPluginState> implements Bo
2276
2276
  * Get all the columns which contains at least a border
2277
2277
  */
2278
2278
  private getColumnsWithBorders;
2279
+ /**
2280
+ * Get all the rows which contains at least a border
2281
+ */
2282
+ private getRowsWithBorders;
2279
2283
  /**
2280
2284
  * Get the range of all the rows in the sheet
2281
2285
  */
@@ -4941,7 +4945,7 @@ declare function lazy<T>(fn: (() => T) | T): Lazy<T>;
4941
4945
  /**
4942
4946
  * Compares two objects.
4943
4947
  */
4944
- declare function deepEquals(o1: any, o2: any, ignoreFunctions?: "ignoreFunctions"): boolean;
4948
+ declare function deepEquals(o1: any, o2: any): boolean;
4945
4949
 
4946
4950
  interface ConstructorArgs {
4947
4951
  readonly zone: Readonly<Zone | UnboundedZone>;
@@ -6866,7 +6870,6 @@ declare class ChartJsComponent extends Component<Props$y, SpreadsheetChildEnv> {
6866
6870
  };
6867
6871
  private canvas;
6868
6872
  private chart?;
6869
- private currentRuntime;
6870
6873
  get background(): string;
6871
6874
  get canvasStyle(): string;
6872
6875
  get chartRuntime(): ChartJSRuntime;