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