@odoo/o-spreadsheet 17.4.0-alpha.10 → 17.4.0-alpha.12

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.0-alpha.10
6
- * @date 2024-07-02T09:04:04.564Z
7
- * @hash a68d9f2
5
+ * @version 17.4.0-alpha.12
6
+ * @date 2024-07-08T05:43:07.933Z
7
+ * @hash 7cfe14a
8
8
  */
9
9
 
10
10
  'use strict';
@@ -1805,7 +1805,7 @@ const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSepa
1805
1805
  });
1806
1806
  const getNumberRegex = memoize(function getNumberRegex(locale) {
1807
1807
  const decimalSeparator = escapeRegExp(locale.decimalSeparator);
1808
- const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
1808
+ const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
1809
1809
  const pIntegerAndDecimals = `(?:\\d+(?:${thousandsSeparator}\\d{3,})*(?:${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
1810
1810
  const pOnlyDecimals = `(?:${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
1811
1811
  const pScientificFormat = "(?:e(?:\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -1838,7 +1838,7 @@ function isNumber(value, locale) {
1838
1838
  return getNumberRegex(locale).test(value.trim());
1839
1839
  }
1840
1840
  const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
1841
- return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
1841
+ return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
1842
1842
  });
1843
1843
  /**
1844
1844
  * Convert a string into a number. It assumes that the string actually represents
@@ -5862,19 +5862,22 @@ class TokenizingChars {
5862
5862
  }
5863
5863
 
5864
5864
  function isValidLocale(locale) {
5865
- if (!(locale &&
5866
- typeof locale === "object" &&
5867
- typeof locale.name === "string" &&
5868
- typeof locale.code === "string" &&
5869
- typeof locale.thousandsSeparator === "string" &&
5870
- typeof locale.decimalSeparator === "string" &&
5871
- typeof locale.dateFormat === "string" &&
5872
- typeof locale.timeFormat === "string" &&
5873
- typeof locale.formulaArgSeparator === "string")) {
5865
+ if (!locale ||
5866
+ typeof locale !== "object" ||
5867
+ !(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
5874
5868
  return false;
5875
5869
  }
5876
- if (!Object.values(locale).every((v) => v)) {
5877
- return false;
5870
+ for (const property of [
5871
+ "code",
5872
+ "name",
5873
+ "decimalSeparator",
5874
+ "dateFormat",
5875
+ "timeFormat",
5876
+ "formulaArgSeparator",
5877
+ ]) {
5878
+ if (!locale[property] || typeof locale[property] !== "string") {
5879
+ return false;
5880
+ }
5878
5881
  }
5879
5882
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
5880
5883
  return false;
@@ -5972,7 +5975,10 @@ function canonicalizeNumberLiteral(content, locale) {
5972
5975
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
5973
5976
  return content;
5974
5977
  }
5975
- return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
5978
+ if (locale.thousandsSeparator) {
5979
+ content = content.replaceAll(locale.thousandsSeparator, "");
5980
+ }
5981
+ return content.replace(locale.decimalSeparator, ".");
5976
5982
  }
5977
5983
  /**
5978
5984
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -6324,7 +6330,7 @@ const quarterNumberAdapter = {
6324
6330
  },
6325
6331
  toValueAndFormat(normalizedValue) {
6326
6332
  return {
6327
- value: toNumber(normalizedValue, DEFAULT_LOCALE),
6333
+ value: _t("Q%(quarter_number)s", { quarter_number: normalizedValue }),
6328
6334
  format: "0",
6329
6335
  };
6330
6336
  },
@@ -6454,9 +6460,9 @@ function getMaxObjectId(o) {
6454
6460
  }
6455
6461
  const ALL_PERIODS = {
6456
6462
  year: _t("Year"),
6457
- quarter: _t("Quarter"),
6458
- month: _t("Month"),
6459
- week: _t("Week"),
6463
+ quarter: _t("Quarter & Year"),
6464
+ month: _t("Month & Year"),
6465
+ week: _t("Week & Year"),
6460
6466
  day: _t("Day"),
6461
6467
  quarter_number: _t("Quarter"),
6462
6468
  month_number: _t("Month"),
@@ -6608,7 +6614,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
6608
6614
  const pivotId = this.getters.getPivotIdFromPosition(position);
6609
6615
  const spreader = this.getters.getArrayFormulaSpreadingOn(position);
6610
6616
  if (pivotId) {
6611
- if (!deepEquals(spreader, position) || !isCopyingOneCell) {
6617
+ if (spreader && (!deepEquals(spreader, position) || !isCopyingOneCell)) {
6612
6618
  const pivotCell = this.getters.getPivotCellFromPosition(position);
6613
6619
  const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
6614
6620
  const pivotFormula = createPivotFormula(formulaPivotId, pivotCell);
@@ -7413,6 +7419,146 @@ function transformRangeData(range, executed) {
7413
7419
  return undefined;
7414
7420
  }
7415
7421
 
7422
+ var State;
7423
+ (function (State) {
7424
+ /**
7425
+ * Initial state.
7426
+ * Expecting any reference for the left part of a range
7427
+ * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7428
+ */
7429
+ State[State["LeftRef"] = 0] = "LeftRef";
7430
+ /**
7431
+ * Expecting any reference for the right part of a range
7432
+ * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7433
+ */
7434
+ State[State["RightRef"] = 1] = "RightRef";
7435
+ /**
7436
+ * Expecting the separator without any constraint on the right part
7437
+ */
7438
+ State[State["Separator"] = 2] = "Separator";
7439
+ /**
7440
+ * Expecting the separator for a full column range
7441
+ */
7442
+ State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
7443
+ /**
7444
+ * Expecting the separator for a full row range
7445
+ */
7446
+ State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
7447
+ /**
7448
+ * Expecting the right part of a full column range
7449
+ * e.g. "1", "A1"
7450
+ */
7451
+ State[State["RightColumnRef"] = 5] = "RightColumnRef";
7452
+ /**
7453
+ * Expecting the right part of a full row range
7454
+ * e.g. "A", "A1"
7455
+ */
7456
+ State[State["RightRowRef"] = 6] = "RightRowRef";
7457
+ /**
7458
+ * Final state. A range has been matched
7459
+ */
7460
+ State[State["Found"] = 7] = "Found";
7461
+ })(State || (State = {}));
7462
+ const goTo = (state, guard = () => true) => [
7463
+ {
7464
+ goTo: state,
7465
+ guard,
7466
+ },
7467
+ ];
7468
+ const goToMulti = (state, guard = () => true) => ({
7469
+ goTo: state,
7470
+ guard,
7471
+ });
7472
+ const machine = {
7473
+ [State.LeftRef]: {
7474
+ REFERENCE: goTo(State.Separator),
7475
+ NUMBER: goTo(State.FullRowSeparator),
7476
+ SYMBOL: [
7477
+ goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
7478
+ goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
7479
+ ],
7480
+ },
7481
+ [State.FullColumnSeparator]: {
7482
+ SPACE: goTo(State.FullColumnSeparator),
7483
+ OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
7484
+ },
7485
+ [State.FullRowSeparator]: {
7486
+ SPACE: goTo(State.FullRowSeparator),
7487
+ OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
7488
+ },
7489
+ [State.Separator]: {
7490
+ SPACE: goTo(State.Separator),
7491
+ OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
7492
+ },
7493
+ [State.RightRef]: {
7494
+ SPACE: goTo(State.RightRef),
7495
+ NUMBER: goTo(State.Found),
7496
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7497
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
7498
+ },
7499
+ [State.RightColumnRef]: {
7500
+ SPACE: goTo(State.RightColumnRef),
7501
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
7502
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7503
+ },
7504
+ [State.RightRowRef]: {
7505
+ SPACE: goTo(State.RightRowRef),
7506
+ NUMBER: goTo(State.Found),
7507
+ REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7508
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
7509
+ },
7510
+ [State.Found]: {},
7511
+ };
7512
+ /**
7513
+ * Check if the list of tokens starts with a sequence of tokens representing
7514
+ * a range.
7515
+ * If a range is found, the sequence is removed from the list and is returned
7516
+ * as a single token.
7517
+ */
7518
+ function matchReference(tokens) {
7519
+ let head = 0;
7520
+ let transitions = machine[State.LeftRef];
7521
+ let matchedTokens = "";
7522
+ while (transitions !== undefined) {
7523
+ const token = tokens[head++];
7524
+ if (!token) {
7525
+ return null;
7526
+ }
7527
+ const transition = transitions[token.type]?.find((transition) => transition.guard(token));
7528
+ const nextState = transition ? transition.goTo : undefined;
7529
+ switch (nextState) {
7530
+ case undefined:
7531
+ return null;
7532
+ case State.Found:
7533
+ matchedTokens += token.value;
7534
+ tokens.splice(0, head);
7535
+ return {
7536
+ type: "REFERENCE",
7537
+ value: matchedTokens,
7538
+ };
7539
+ default:
7540
+ transitions = machine[nextState];
7541
+ matchedTokens += token.value;
7542
+ break;
7543
+ }
7544
+ }
7545
+ return null;
7546
+ }
7547
+ /**
7548
+ * Take the result of the tokenizer and transform it to be usable in the
7549
+ * manipulations of range
7550
+ *
7551
+ * @param formula
7552
+ */
7553
+ function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
7554
+ const tokens = tokenize(formula, locale);
7555
+ const result = [];
7556
+ while (tokens.length) {
7557
+ result.push(matchReference(tokens) || tokens.shift());
7558
+ }
7559
+ return result;
7560
+ }
7561
+
7416
7562
  const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
7417
7563
  const UNARY_OPERATORS_PREFIX = ["-", "+"];
7418
7564
  const UNARY_OPERATORS_POSTFIX = ["%"];
@@ -7561,7 +7707,7 @@ function parseExpression(tokens, parent_priority = 0) {
7561
7707
  * Parse an expression (as a string) into an AST.
7562
7708
  */
7563
7709
  function parse(str) {
7564
- return parseTokens(tokenize(str));
7710
+ return parseTokens(rangeTokenize(str));
7565
7711
  }
7566
7712
  function parseTokens(tokens) {
7567
7713
  tokens = tokens.filter((x) => x.type !== "SPACE");
@@ -7697,146 +7843,6 @@ function rightOperandToFormula(operationAST) {
7697
7843
  return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
7698
7844
  }
7699
7845
 
7700
- var State;
7701
- (function (State) {
7702
- /**
7703
- * Initial state.
7704
- * Expecting any reference for the left part of a range
7705
- * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7706
- */
7707
- State[State["LeftRef"] = 0] = "LeftRef";
7708
- /**
7709
- * Expecting any reference for the right part of a range
7710
- * e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
7711
- */
7712
- State[State["RightRef"] = 1] = "RightRef";
7713
- /**
7714
- * Expecting the separator without any constraint on the right part
7715
- */
7716
- State[State["Separator"] = 2] = "Separator";
7717
- /**
7718
- * Expecting the separator for a full column range
7719
- */
7720
- State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
7721
- /**
7722
- * Expecting the separator for a full row range
7723
- */
7724
- State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
7725
- /**
7726
- * Expecting the right part of a full column range
7727
- * e.g. "1", "A1"
7728
- */
7729
- State[State["RightColumnRef"] = 5] = "RightColumnRef";
7730
- /**
7731
- * Expecting the right part of a full row range
7732
- * e.g. "A", "A1"
7733
- */
7734
- State[State["RightRowRef"] = 6] = "RightRowRef";
7735
- /**
7736
- * Final state. A range has been matched
7737
- */
7738
- State[State["Found"] = 7] = "Found";
7739
- })(State || (State = {}));
7740
- const goTo = (state, guard = () => true) => [
7741
- {
7742
- goTo: state,
7743
- guard,
7744
- },
7745
- ];
7746
- const goToMulti = (state, guard = () => true) => ({
7747
- goTo: state,
7748
- guard,
7749
- });
7750
- const machine = {
7751
- [State.LeftRef]: {
7752
- REFERENCE: goTo(State.Separator),
7753
- NUMBER: goTo(State.FullRowSeparator),
7754
- SYMBOL: [
7755
- goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
7756
- goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
7757
- ],
7758
- },
7759
- [State.FullColumnSeparator]: {
7760
- SPACE: goTo(State.FullColumnSeparator),
7761
- OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
7762
- },
7763
- [State.FullRowSeparator]: {
7764
- SPACE: goTo(State.FullRowSeparator),
7765
- OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
7766
- },
7767
- [State.Separator]: {
7768
- SPACE: goTo(State.Separator),
7769
- OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
7770
- },
7771
- [State.RightRef]: {
7772
- SPACE: goTo(State.RightRef),
7773
- NUMBER: goTo(State.Found),
7774
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7775
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
7776
- },
7777
- [State.RightColumnRef]: {
7778
- SPACE: goTo(State.RightColumnRef),
7779
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
7780
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7781
- },
7782
- [State.RightRowRef]: {
7783
- SPACE: goTo(State.RightRowRef),
7784
- NUMBER: goTo(State.Found),
7785
- REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
7786
- SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
7787
- },
7788
- [State.Found]: {},
7789
- };
7790
- /**
7791
- * Check if the list of tokens starts with a sequence of tokens representing
7792
- * a range.
7793
- * If a range is found, the sequence is removed from the list and is returned
7794
- * as a single token.
7795
- */
7796
- function matchReference(tokens) {
7797
- let head = 0;
7798
- let transitions = machine[State.LeftRef];
7799
- let matchedTokens = "";
7800
- while (transitions !== undefined) {
7801
- const token = tokens[head++];
7802
- if (!token) {
7803
- return null;
7804
- }
7805
- const transition = transitions[token.type]?.find((transition) => transition.guard(token));
7806
- const nextState = transition ? transition.goTo : undefined;
7807
- switch (nextState) {
7808
- case undefined:
7809
- return null;
7810
- case State.Found:
7811
- matchedTokens += token.value;
7812
- tokens.splice(0, head);
7813
- return {
7814
- type: "REFERENCE",
7815
- value: matchedTokens,
7816
- };
7817
- default:
7818
- transitions = machine[nextState];
7819
- matchedTokens += token.value;
7820
- break;
7821
- }
7822
- }
7823
- return null;
7824
- }
7825
- /**
7826
- * Take the result of the tokenizer and transform it to be usable in the
7827
- * manipulations of range
7828
- *
7829
- * @param formula
7830
- */
7831
- function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
7832
- const tokens = tokenize(formula, locale);
7833
- const result = [];
7834
- while (tokens.length) {
7835
- result.push(matchReference(tokens) || tokens.shift());
7836
- }
7837
- return result;
7838
- }
7839
-
7840
7846
  /**
7841
7847
  * Add the following information on tokens:
7842
7848
  * - length
@@ -24027,10 +24033,6 @@ function createLineOrScatterChartRuntime(chart, getters) {
24027
24033
  const colors = new ColorGenerator();
24028
24034
  const definition = chart.getDefinition();
24029
24035
  for (let [index, { label, data }] of dataSetsValues.entries()) {
24030
- if (["linear", "time"].includes(axisType)) {
24031
- // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
24032
- data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
24033
- }
24034
24036
  const color = colors.next();
24035
24037
  let backgroundRGBA = colorToRGBA(color);
24036
24038
  if (areaChart) {
@@ -24046,6 +24048,10 @@ function createLineOrScatterChartRuntime(chart, getters) {
24046
24048
  return value;
24047
24049
  });
24048
24050
  }
24051
+ if (["linear", "time"].includes(axisType)) {
24052
+ // Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
24053
+ data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
24054
+ }
24049
24055
  const backgroundColor = rgbaToHex(backgroundRGBA);
24050
24056
  const dataset = {
24051
24057
  label,
@@ -34638,6 +34644,8 @@ class FindAndReplaceStore extends SpreadsheetStore {
34638
34644
  isSearchDirty = false;
34639
34645
  initialShowFormulaState;
34640
34646
  preserveSelectedMatchIndex = false;
34647
+ irreplaceableMatchCount = 0;
34648
+ notificationStore = this.get(NotificationStore);
34641
34649
  // fixme: why do we make selectedMatchIndex on top of a selected
34642
34650
  // property in the matches?
34643
34651
  selectedMatchIndex = null;
@@ -34714,6 +34722,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
34714
34722
  for (const match of cmd.matches) {
34715
34723
  this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
34716
34724
  }
34725
+ if (this.irreplaceableMatchCount > 0) {
34726
+ this.showReplaceWarningMessage(cmd.matches.length, this.irreplaceableMatchCount);
34727
+ }
34728
+ this.irreplaceableMatchCount = 0;
34729
+ break;
34717
34730
  }
34718
34731
  }
34719
34732
  finalize() {
@@ -34895,12 +34908,36 @@ class FindAndReplaceStore extends SpreadsheetStore {
34895
34908
  searchOptions: this.searchOptions,
34896
34909
  });
34897
34910
  }
34911
+ /**
34912
+ * Show a warning message based on the number of matches replaced and irreplaceable.
34913
+ */
34914
+ showReplaceWarningMessage(totalMatches, irreplaceableMatches) {
34915
+ const replaceableMatches = totalMatches - irreplaceableMatches;
34916
+ if (replaceableMatches === 0) {
34917
+ this.notificationStore.notifyUser({
34918
+ type: "warning",
34919
+ sticky: false,
34920
+ text: _t("Match(es) cannot be replaced as they are part of a formula."),
34921
+ });
34922
+ }
34923
+ else {
34924
+ this.notificationStore.notifyUser({
34925
+ type: "warning",
34926
+ sticky: false,
34927
+ text: _t("%(replaceable_count)s match(es) replaced. %(irreplaceable_count)s match(es) cannot be replaced as they are part of a formula.", {
34928
+ replaceable_count: replaceableMatches,
34929
+ irreplaceable_count: irreplaceableMatches,
34930
+ }),
34931
+ });
34932
+ }
34933
+ }
34898
34934
  replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
34899
34935
  const cell = this.getters.getCell(selectedMatch);
34900
34936
  if (!cell?.content) {
34901
34937
  return;
34902
34938
  }
34903
34939
  if (cell?.isFormula && !searchOptions.searchFormulas) {
34940
+ this.irreplaceableMatchCount++;
34904
34941
  return;
34905
34942
  }
34906
34943
  const searchRegex = getSearchRegex(searchString, searchOptions);
@@ -36966,7 +37003,13 @@ class SettingsPanel extends owl.Component {
36966
37003
  }
36967
37004
  async loadLocales() {
36968
37005
  this.loadedLocales = (await this.env.loadLocales())
36969
- .filter(isValidLocale)
37006
+ .filter((locale) => {
37007
+ const isValid = isValidLocale(locale);
37008
+ if (!isValid) {
37009
+ console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
37010
+ }
37011
+ return isValid;
37012
+ })
36970
37013
  .sort((a, b) => a.name.localeCompare(b.name));
36971
37014
  }
36972
37015
  get numberFormatPreview() {
@@ -38025,6 +38068,9 @@ class TopBarComponentRegistry extends Registry {
38025
38068
  const component = { ...value, id: this.uuidGenerator.uuidv4() };
38026
38069
  return super.add(name, component);
38027
38070
  }
38071
+ getAllOrdered() {
38072
+ return this.getAll().sort((a, b) => a.sequence - b.sequence);
38073
+ }
38028
38074
  }
38029
38075
  const topbarComponentRegistry = new TopBarComponentRegistry();
38030
38076
 
@@ -42135,6 +42181,7 @@ class Grid extends owl.Component {
42135
42181
  return;
42136
42182
  }
42137
42183
  if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
42184
+ ev.preventDefault();
42138
42185
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
42139
42186
  const target = this.env.model.getters.getSelectedZones();
42140
42187
  const clipboardString = this.env.model.getters.getClipboardTextContent();
@@ -42401,6 +42448,8 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42401
42448
  "BITOR",
42402
42449
  "BITRSHIFT",
42403
42450
  "BITXOR",
42451
+ "BYCOL",
42452
+ "BYROW",
42404
42453
  "CEILING.MATH",
42405
42454
  "CEILING.PRECISE",
42406
42455
  "CHISQ.DIST",
@@ -42408,6 +42457,8 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42408
42457
  "CHISQ.INV",
42409
42458
  "CHISQ.INV.RT",
42410
42459
  "CHISQ.TEST",
42460
+ "CHOOSECOLS",
42461
+ "CHOOSEROWS",
42411
42462
  "COMBINA",
42412
42463
  "CONCAT",
42413
42464
  "CONFIDENCE.NORM",
@@ -42420,14 +42471,17 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42420
42471
  "CSCH",
42421
42472
  "DAYS",
42422
42473
  "DECIMAL",
42474
+ "DROP",
42423
42475
  "ERF.PRECISE",
42424
42476
  "ERFC.PRECISE",
42477
+ "EXPAND",
42425
42478
  "EXPON.DIST",
42426
42479
  "F.DIST",
42427
42480
  "F.DIST.RT",
42428
42481
  "F.INV",
42429
42482
  "F.INV.RT",
42430
42483
  "F.TEST",
42484
+ "FIELDVALUE",
42431
42485
  "FILTERXML",
42432
42486
  "FLOOR.MATH",
42433
42487
  "FLOOR.PRECISE",
@@ -42442,6 +42496,7 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42442
42496
  "GAMMA.INV",
42443
42497
  "GAMMALN.PRECISE",
42444
42498
  "GAUSS",
42499
+ "HSTACK",
42445
42500
  "HYPGEOM.DIST",
42446
42501
  "IFNA",
42447
42502
  "IFS",
@@ -42454,9 +42509,14 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42454
42509
  "IMSINH",
42455
42510
  "IMTAN",
42456
42511
  "ISFORMULA",
42512
+ "ISOMITTED",
42457
42513
  "ISOWEEKNUM",
42514
+ "LAMBDA",
42515
+ "LET",
42458
42516
  "LOGNORM.DIST",
42459
42517
  "LOGNORM.INV",
42518
+ "MAKEARRAY",
42519
+ "MAP",
42460
42520
  "MAXIFS",
42461
42521
  "MINIFS",
42462
42522
  "MODE.MULT",
@@ -42476,17 +42536,26 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42476
42536
  "PERMUTATIONA",
42477
42537
  "PHI",
42478
42538
  "POISSON.DIST",
42539
+ "PQSOURCE",
42540
+ "PYTHON_STR",
42541
+ "PYTHON_TYPE",
42542
+ "PYTHON_TYPENAME",
42479
42543
  "QUARTILE.EXC",
42480
42544
  "QUARTILE.INC",
42481
42545
  "QUERYSTRING",
42546
+ "RANDARRAY",
42482
42547
  "RANK.AVG",
42483
42548
  "RANK.EQ",
42549
+ "REDUCE",
42484
42550
  "RRI",
42551
+ "SCAN",
42485
42552
  "SEC",
42486
42553
  "SECH",
42554
+ "SEQUENCE",
42487
42555
  "SHEET",
42488
42556
  "SHEETS",
42489
42557
  "SKEW.P",
42558
+ "SORTBY",
42490
42559
  "STDEV.P",
42491
42560
  "STDEV.S",
42492
42561
  "SWITCH",
@@ -42496,13 +42565,24 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
42496
42565
  "T.INV",
42497
42566
  "T.INV.2T",
42498
42567
  "T.TEST",
42568
+ "TAKE",
42569
+ "TEXTAFTER",
42570
+ "TEXTBEFORE",
42499
42571
  "TEXTJOIN",
42572
+ "TEXTSPLIT",
42573
+ "TOCOL",
42574
+ "TOROW",
42500
42575
  "UNICHAR",
42501
42576
  "UNICODE",
42577
+ "UNIQUE",
42502
42578
  "VAR.P",
42503
42579
  "VAR.S",
42580
+ "VSTACK",
42504
42581
  "WEBSERVICE",
42505
42582
  "WEIBULL.DIST",
42583
+ "WRAPCOLS",
42584
+ "WRAPROWS",
42585
+ "XLOOKUP",
42506
42586
  "XOR",
42507
42587
  "Z.TEST",
42508
42588
  ];
@@ -46765,6 +46845,21 @@ class BordersPlugin extends CorePlugin {
46765
46845
  return [];
46766
46846
  return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
46767
46847
  }
46848
+ /**
46849
+ * Get all the rows which contains at least a border
46850
+ */
46851
+ getRowsWithBorders(sheetId) {
46852
+ const sheetBorders = this.borders[sheetId]?.filter(isDefined);
46853
+ if (!sheetBorders)
46854
+ return [];
46855
+ const rowsWithBorders = new Set();
46856
+ for (const rowBorders of sheetBorders) {
46857
+ for (const rowBorder in rowBorders) {
46858
+ rowsWithBorders.add(parseInt(rowBorder, 10));
46859
+ }
46860
+ }
46861
+ return Array.from(rowsWithBorders);
46862
+ }
46768
46863
  /**
46769
46864
  * Get the range of all the rows in the sheet
46770
46865
  */
@@ -46814,7 +46909,7 @@ class BordersPlugin extends CorePlugin {
46814
46909
  destructive: false,
46815
46910
  });
46816
46911
  }
46817
- this.getRowsRange(sheetId)
46912
+ this.getRowsWithBorders(sheetId)
46818
46913
  .filter((row) => row >= start)
46819
46914
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
46820
46915
  .forEach((row) => {
@@ -63107,7 +63202,7 @@ class TopBar extends owl.Component {
63107
63202
  }
63108
63203
  get topbarComponents() {
63109
63204
  return topbarComponentRegistry
63110
- .getAll()
63205
+ .getAllOrdered()
63111
63206
  .filter((item) => !item.isVisible || item.isVisible(this.env));
63112
63207
  }
63113
63208
  onExternalClick(ev) {
@@ -67738,6 +67833,6 @@ exports.tokenColors = tokenColors;
67738
67833
  exports.tokenize = tokenize;
67739
67834
 
67740
67835
 
67741
- __info__.version = "17.4.0-alpha.10";
67742
- __info__.date = "2024-07-02T09:04:04.564Z";
67743
- __info__.hash = "a68d9f2";
67836
+ __info__.version = "17.4.0-alpha.12";
67837
+ __info__.date = "2024-07-08T05:43:07.933Z";
67838
+ __info__.hash = "7cfe14a";