@odoo/o-spreadsheet 18.2.0-alpha.0 → 18.2.0-alpha.1

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 18.2.0-alpha.0
6
- * @date 2024-12-26T08:02:39.123Z
7
- * @hash 0f45915
5
+ * @version 18.2.0-alpha.1
6
+ * @date 2025-01-14T11:35:51.135Z
7
+ * @hash 702f816
8
8
  */
9
9
 
10
10
  'use strict';
@@ -357,6 +357,7 @@ var ComponentsImportance;
357
357
  ComponentsImportance[ComponentsImportance["Popover"] = 35] = "Popover";
358
358
  ComponentsImportance[ComponentsImportance["FigureAnchor"] = 1000] = "FigureAnchor";
359
359
  ComponentsImportance[ComponentsImportance["FigureSnapLine"] = 1001] = "FigureSnapLine";
360
+ ComponentsImportance[ComponentsImportance["FigureTooltip"] = 1002] = "FigureTooltip";
360
361
  })(ComponentsImportance || (ComponentsImportance = {}));
361
362
  let DEFAULT_SHEETVIEW_SIZE = 0;
362
363
  function getDefaultSheetViewSize() {
@@ -976,6 +977,16 @@ function transpose2dPOJO(pojo) {
976
977
  }
977
978
  return result;
978
979
  }
980
+ function getUniqueText(text, texts, options = {}) {
981
+ const compute = options.compute ?? ((text, i) => `${text} (${i})`);
982
+ const computeFirstOne = options.computeFirstOne ?? false;
983
+ let i = options.start ?? 1;
984
+ let newText = computeFirstOne ? compute(text, i) : text;
985
+ while (texts.includes(newText)) {
986
+ newText = compute(text, i++);
987
+ }
988
+ return newText;
989
+ }
979
990
 
980
991
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
981
992
  const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
@@ -3711,6 +3722,7 @@ exports.CommandResult = void 0;
3711
3722
  CommandResult["SheetIsHidden"] = "SheetIsHidden";
3712
3723
  CommandResult["InvalidTableResize"] = "InvalidTableResize";
3713
3724
  CommandResult["PivotIdNotFound"] = "PivotIdNotFound";
3725
+ CommandResult["PivotInError"] = "PivotInError";
3714
3726
  CommandResult["EmptyName"] = "EmptyName";
3715
3727
  CommandResult["ValueCellIsInvalidFormula"] = "ValueCellIsInvalidFormula";
3716
3728
  CommandResult["InvalidDefinition"] = "InvalidDefinition";
@@ -6028,10 +6040,10 @@ class RangeImpl {
6028
6040
  }
6029
6041
  }
6030
6042
  /**
6031
- * Copy a range. If the range is on the sheetIdFrom, the range will target
6043
+ * Duplicate a range. If the range is on the sheetIdFrom, the range will target
6032
6044
  * sheetIdTo.
6033
6045
  */
6034
- function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
6046
+ function duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) {
6035
6047
  const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
6036
6048
  return range.clone({ sheetId });
6037
6049
  }
@@ -7479,18 +7491,18 @@ function predictLinearValues(Y, X, newX, computeIntercept) {
7479
7491
  });
7480
7492
  return newY.length === newX.length ? newY : transposeMatrix(newY);
7481
7493
  }
7482
- function getMovingAverageValues(dataset, windowSize = DEFAULT_WINDOW_SIZE) {
7494
+ function getMovingAverageValues(dataset, labels, windowSize = DEFAULT_WINDOW_SIZE) {
7483
7495
  const values = [];
7484
7496
  // Fill the starting values with null until we have a full window
7485
7497
  for (let i = 0; i < windowSize - 1; i++) {
7486
- values.push(null);
7498
+ values.push({ x: labels[i], y: NaN });
7487
7499
  }
7488
7500
  for (let i = 0; i <= dataset.length - windowSize; i++) {
7489
7501
  let sum = 0;
7490
7502
  for (let j = i; j < i + windowSize; j++) {
7491
7503
  sum += dataset[j];
7492
7504
  }
7493
- values.push(sum / windowSize);
7505
+ values.push({ x: labels[i + windowSize - 1], y: sum / windowSize });
7494
7506
  }
7495
7507
  return values;
7496
7508
  }
@@ -9464,6 +9476,150 @@ class ComposerFocusStore extends SpreadsheetStore {
9464
9476
  }
9465
9477
  }
9466
9478
 
9479
+ /**
9480
+ * This file is largely inspired by owl 1.
9481
+ * `css` tag has been removed from owl 2 without workaround to manage css.
9482
+ * So, the solution was to import the behavior of owl 1 directly in our
9483
+ * codebase, with one difference: the css is added to the sheet as soon as the
9484
+ * css tag is executed. In owl 1, the css was added as soon as a Component was
9485
+ * created for the first time.
9486
+ */
9487
+ const STYLESHEETS = {};
9488
+ let nextId = 0;
9489
+ /**
9490
+ * CSS tag helper for defining inline stylesheets. With this, one can simply define
9491
+ * an inline stylesheet with just the following code:
9492
+ * ```js
9493
+ * css`.component-a { color: red; }`;
9494
+ * ```
9495
+ */
9496
+ function css(strings, ...args) {
9497
+ const name = `__sheet__${nextId++}`;
9498
+ const value = String.raw(strings, ...args);
9499
+ registerSheet(name, value);
9500
+ activateSheet(name);
9501
+ return name;
9502
+ }
9503
+ function processSheet(str) {
9504
+ const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
9505
+ const selectorStack = [];
9506
+ const parts = [];
9507
+ let rules = [];
9508
+ function generateSelector(stackIndex, parentSelector) {
9509
+ const parts = [];
9510
+ for (const selector of selectorStack[stackIndex]) {
9511
+ let part = (parentSelector && parentSelector + " " + selector) || selector;
9512
+ if (part.includes("&")) {
9513
+ part = selector.replace(/&/g, parentSelector || "");
9514
+ }
9515
+ if (stackIndex < selectorStack.length - 1) {
9516
+ part = generateSelector(stackIndex + 1, part);
9517
+ }
9518
+ parts.push(part);
9519
+ }
9520
+ return parts.join(", ");
9521
+ }
9522
+ function generateRules() {
9523
+ if (rules.length) {
9524
+ parts.push(generateSelector(0) + " {");
9525
+ parts.push(...rules);
9526
+ parts.push("}");
9527
+ rules = [];
9528
+ }
9529
+ }
9530
+ while (tokens.length) {
9531
+ let token = tokens.shift();
9532
+ if (token === "}") {
9533
+ generateRules();
9534
+ selectorStack.pop();
9535
+ }
9536
+ else {
9537
+ if (tokens[0] === "{") {
9538
+ generateRules();
9539
+ selectorStack.push(token.split(/\s*,\s*/));
9540
+ tokens.shift();
9541
+ }
9542
+ if (tokens[0] === ";") {
9543
+ rules.push(" " + token + ";");
9544
+ }
9545
+ }
9546
+ }
9547
+ return parts.join("\n");
9548
+ }
9549
+ function registerSheet(id, css) {
9550
+ const sheet = document.createElement("style");
9551
+ sheet.textContent = processSheet(css);
9552
+ STYLESHEETS[id] = sheet;
9553
+ }
9554
+ function activateSheet(id) {
9555
+ const sheet = STYLESHEETS[id];
9556
+ sheet.setAttribute("component", id);
9557
+ document.head.appendChild(sheet);
9558
+ }
9559
+ function getTextDecoration({ strikethrough, underline, }) {
9560
+ if (!strikethrough && !underline) {
9561
+ return "none";
9562
+ }
9563
+ return `${strikethrough ? "line-through" : ""} ${underline ? "underline" : ""}`;
9564
+ }
9565
+ /**
9566
+ * Convert the cell style to CSS properties.
9567
+ */
9568
+ function cellStyleToCss(style) {
9569
+ const attributes = cellTextStyleToCss(style);
9570
+ if (!style)
9571
+ return attributes;
9572
+ if (style.fillColor) {
9573
+ attributes["background"] = style.fillColor;
9574
+ }
9575
+ return attributes;
9576
+ }
9577
+ /**
9578
+ * Convert the cell text style to CSS properties.
9579
+ */
9580
+ function cellTextStyleToCss(style) {
9581
+ const attributes = {};
9582
+ if (!style)
9583
+ return attributes;
9584
+ if (style.bold) {
9585
+ attributes["font-weight"] = "bold";
9586
+ }
9587
+ if (style.italic) {
9588
+ attributes["font-style"] = "italic";
9589
+ }
9590
+ if (style.strikethrough || style.underline) {
9591
+ let decoration = style.strikethrough ? "line-through" : "";
9592
+ decoration = style.underline ? decoration + " underline" : decoration;
9593
+ attributes["text-decoration"] = decoration;
9594
+ }
9595
+ if (style.textColor) {
9596
+ attributes["color"] = style.textColor;
9597
+ }
9598
+ return attributes;
9599
+ }
9600
+ /**
9601
+ * Transform CSS properties into a CSS string.
9602
+ */
9603
+ function cssPropertiesToCss(attributes) {
9604
+ let styleStr = "";
9605
+ for (const attName in attributes) {
9606
+ if (!attributes[attName]) {
9607
+ continue;
9608
+ }
9609
+ styleStr += `${attName}:${attributes[attName]}; `;
9610
+ }
9611
+ return styleStr;
9612
+ }
9613
+ function getElementMargins(el) {
9614
+ const style = window.getComputedStyle(el);
9615
+ return {
9616
+ top: parseInt(style.marginTop, 10) || 0,
9617
+ bottom: parseInt(style.marginBottom, 10) || 0,
9618
+ left: parseInt(style.marginLeft, 10) || 0,
9619
+ right: parseInt(style.marginRight, 10) || 0,
9620
+ };
9621
+ }
9622
+
9467
9623
  const TREND_LINE_XAXIS_ID = "x1";
9468
9624
  /**
9469
9625
  * This file contains helpers that are common to different charts (mainly
@@ -9516,25 +9672,25 @@ function updateChartRangesWithDataSets(getters, applyChange, chartDataSets, char
9516
9672
  };
9517
9673
  }
9518
9674
  /**
9519
- * Copy the dataSets given. All the ranges which are on sheetIdFrom will target
9675
+ * Duplicate the dataSets. All ranges on sheetIdFrom are adapted to target
9520
9676
  * sheetIdTo.
9521
9677
  */
9522
- function copyDataSetsWithNewSheetId(sheetIdFrom, sheetIdTo, dataSets) {
9678
+ function duplicateDataSetsInDuplicatedSheet(sheetIdFrom, sheetIdTo, dataSets) {
9523
9679
  return dataSets.map((ds) => {
9524
9680
  return {
9525
- dataRange: copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.dataRange),
9681
+ dataRange: duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, ds.dataRange),
9526
9682
  labelCell: ds.labelCell
9527
- ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.labelCell)
9683
+ ? duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, ds.labelCell)
9528
9684
  : undefined,
9529
9685
  };
9530
9686
  });
9531
9687
  }
9532
9688
  /**
9533
- * Copy a range. If the range is on the sheetIdFrom, the range will target
9689
+ * Duplicate a range. If the range is on the sheetIdFrom, the range will target
9534
9690
  * sheetIdTo.
9535
9691
  */
9536
- function copyLabelRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
9537
- return range ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) : undefined;
9692
+ function duplicateLabelRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) {
9693
+ return range ? duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) : undefined;
9538
9694
  }
9539
9695
  /**
9540
9696
  * Adapt a single range of a chart
@@ -9787,10 +9943,11 @@ function formatTickValue(localeFormat) {
9787
9943
  if (isNaN(value))
9788
9944
  return value;
9789
9945
  const { locale, format } = localeFormat;
9790
- return formatValue(value, {
9946
+ const formattedValue = formatValue(value, {
9791
9947
  locale,
9792
9948
  format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
9793
9949
  });
9950
+ return truncateLabel(formattedValue);
9794
9951
  };
9795
9952
  }
9796
9953
  const CHART_AXIS_CHOICES = [
@@ -9805,6 +9962,15 @@ function getPieColors(colors, dataSetsValues) {
9805
9962
  }
9806
9963
  return pieColors;
9807
9964
  }
9965
+ function truncateLabel(label) {
9966
+ if (!label) {
9967
+ return "";
9968
+ }
9969
+ if (label.length > MAX_CHAR_LABEL) {
9970
+ return label.substring(0, MAX_CHAR_LABEL) + "…";
9971
+ }
9972
+ return label;
9973
+ }
9808
9974
 
9809
9975
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9810
9976
  const chartShowValuesPlugin = {
@@ -10000,6 +10166,18 @@ function getNextNonEmptyBar(bars, startIndex) {
10000
10166
 
10001
10167
  window.Chart?.register(waterfallLinesPlugin);
10002
10168
  window.Chart?.register(chartShowValuesPlugin);
10169
+ css /* scss */ `
10170
+ .o-spreadsheet {
10171
+ .o-chart-custom-tooltip {
10172
+ font-size: 12px;
10173
+ background-color: #fff;
10174
+ z-index: ${ComponentsImportance.FigureTooltip};
10175
+ table td span {
10176
+ box-sizing: border-box;
10177
+ }
10178
+ }
10179
+ }
10180
+ `;
10003
10181
  class ChartJsComponent extends owl.Component {
10004
10182
  static template = "o-spreadsheet-ChartJsComponent";
10005
10183
  static props = {
@@ -10238,11 +10416,11 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
10238
10416
  keyValue: keyValueZone ? zoneToXc(keyValueZone) : undefined,
10239
10417
  };
10240
10418
  }
10241
- copyForSheetId(sheetId) {
10242
- const baseline = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.baseline);
10243
- const keyValue = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.keyValue);
10244
- const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, sheetId);
10245
- return new ScorecardChart(definition, sheetId, this.getters);
10419
+ duplicateInDuplicatedSheet(newSheetId) {
10420
+ const baseline = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.baseline);
10421
+ const keyValue = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.keyValue);
10422
+ const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, newSheetId);
10423
+ return new ScorecardChart(definition, newSheetId, this.getters);
10246
10424
  }
10247
10425
  copyInSheetId(sheetId) {
10248
10426
  const definition = this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue, sheetId);
@@ -19932,6 +20110,17 @@ const TEXT = {
19932
20110
  },
19933
20111
  isExported: true,
19934
20112
  };
20113
+ // -----------------------------------------------------------------------------
20114
+ // VALUE
20115
+ // -----------------------------------------------------------------------------
20116
+ const VALUE = {
20117
+ description: _t("Converts a string to a numeric value."),
20118
+ args: [arg("value (number)", _t("the string to be converted"))],
20119
+ compute: function (value) {
20120
+ return toNumber(value, this.locale);
20121
+ },
20122
+ isExported: true,
20123
+ };
19935
20124
 
19936
20125
  var text = /*#__PURE__*/Object.freeze({
19937
20126
  __proto__: null,
@@ -19954,7 +20143,8 @@ var text = /*#__PURE__*/Object.freeze({
19954
20143
  TEXT: TEXT,
19955
20144
  TEXTJOIN: TEXTJOIN,
19956
20145
  TRIM: TRIM,
19957
- UPPER: UPPER
20146
+ UPPER: UPPER,
20147
+ VALUE: VALUE
19958
20148
  });
19959
20149
 
19960
20150
  // -----------------------------------------------------------------------------
@@ -22557,15 +22747,6 @@ const CHART_COMMON_OPTIONS = {
22557
22747
  },
22558
22748
  animation: false,
22559
22749
  };
22560
- function truncateLabel(label) {
22561
- if (!label) {
22562
- return "";
22563
- }
22564
- if (label.length > MAX_CHAR_LABEL) {
22565
- return label.substring(0, MAX_CHAR_LABEL) + "…";
22566
- }
22567
- return label;
22568
- }
22569
22750
  function chartToImage(runtime, figure, type) {
22570
22751
  // wrap the canvas in a div with a fixed size because chart.js would
22571
22752
  // fill the whole page otherwise
@@ -22887,150 +23068,6 @@ const CONTENT_TYPES_FILE = "[Content_Types].xml";
22887
23068
  */
22888
23069
  const iconsOnCellRegistry = new Registry();
22889
23070
 
22890
- /**
22891
- * This file is largely inspired by owl 1.
22892
- * `css` tag has been removed from owl 2 without workaround to manage css.
22893
- * So, the solution was to import the behavior of owl 1 directly in our
22894
- * codebase, with one difference: the css is added to the sheet as soon as the
22895
- * css tag is executed. In owl 1, the css was added as soon as a Component was
22896
- * created for the first time.
22897
- */
22898
- const STYLESHEETS = {};
22899
- let nextId = 0;
22900
- /**
22901
- * CSS tag helper for defining inline stylesheets. With this, one can simply define
22902
- * an inline stylesheet with just the following code:
22903
- * ```js
22904
- * css`.component-a { color: red; }`;
22905
- * ```
22906
- */
22907
- function css(strings, ...args) {
22908
- const name = `__sheet__${nextId++}`;
22909
- const value = String.raw(strings, ...args);
22910
- registerSheet(name, value);
22911
- activateSheet(name);
22912
- return name;
22913
- }
22914
- function processSheet(str) {
22915
- const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
22916
- const selectorStack = [];
22917
- const parts = [];
22918
- let rules = [];
22919
- function generateSelector(stackIndex, parentSelector) {
22920
- const parts = [];
22921
- for (const selector of selectorStack[stackIndex]) {
22922
- let part = (parentSelector && parentSelector + " " + selector) || selector;
22923
- if (part.includes("&")) {
22924
- part = selector.replace(/&/g, parentSelector || "");
22925
- }
22926
- if (stackIndex < selectorStack.length - 1) {
22927
- part = generateSelector(stackIndex + 1, part);
22928
- }
22929
- parts.push(part);
22930
- }
22931
- return parts.join(", ");
22932
- }
22933
- function generateRules() {
22934
- if (rules.length) {
22935
- parts.push(generateSelector(0) + " {");
22936
- parts.push(...rules);
22937
- parts.push("}");
22938
- rules = [];
22939
- }
22940
- }
22941
- while (tokens.length) {
22942
- let token = tokens.shift();
22943
- if (token === "}") {
22944
- generateRules();
22945
- selectorStack.pop();
22946
- }
22947
- else {
22948
- if (tokens[0] === "{") {
22949
- generateRules();
22950
- selectorStack.push(token.split(/\s*,\s*/));
22951
- tokens.shift();
22952
- }
22953
- if (tokens[0] === ";") {
22954
- rules.push(" " + token + ";");
22955
- }
22956
- }
22957
- }
22958
- return parts.join("\n");
22959
- }
22960
- function registerSheet(id, css) {
22961
- const sheet = document.createElement("style");
22962
- sheet.textContent = processSheet(css);
22963
- STYLESHEETS[id] = sheet;
22964
- }
22965
- function activateSheet(id) {
22966
- const sheet = STYLESHEETS[id];
22967
- sheet.setAttribute("component", id);
22968
- document.head.appendChild(sheet);
22969
- }
22970
- function getTextDecoration({ strikethrough, underline, }) {
22971
- if (!strikethrough && !underline) {
22972
- return "none";
22973
- }
22974
- return `${strikethrough ? "line-through" : ""} ${underline ? "underline" : ""}`;
22975
- }
22976
- /**
22977
- * Convert the cell style to CSS properties.
22978
- */
22979
- function cellStyleToCss(style) {
22980
- const attributes = cellTextStyleToCss(style);
22981
- if (!style)
22982
- return attributes;
22983
- if (style.fillColor) {
22984
- attributes["background"] = style.fillColor;
22985
- }
22986
- return attributes;
22987
- }
22988
- /**
22989
- * Convert the cell text style to CSS properties.
22990
- */
22991
- function cellTextStyleToCss(style) {
22992
- const attributes = {};
22993
- if (!style)
22994
- return attributes;
22995
- if (style.bold) {
22996
- attributes["font-weight"] = "bold";
22997
- }
22998
- if (style.italic) {
22999
- attributes["font-style"] = "italic";
23000
- }
23001
- if (style.strikethrough || style.underline) {
23002
- let decoration = style.strikethrough ? "line-through" : "";
23003
- decoration = style.underline ? decoration + " underline" : decoration;
23004
- attributes["text-decoration"] = decoration;
23005
- }
23006
- if (style.textColor) {
23007
- attributes["color"] = style.textColor;
23008
- }
23009
- return attributes;
23010
- }
23011
- /**
23012
- * Transform CSS properties into a CSS string.
23013
- */
23014
- function cssPropertiesToCss(attributes) {
23015
- let styleStr = "";
23016
- for (const attName in attributes) {
23017
- if (!attributes[attName]) {
23018
- continue;
23019
- }
23020
- styleStr += `${attName}:${attributes[attName]}; `;
23021
- }
23022
- return styleStr;
23023
- }
23024
- function getElementMargins(el) {
23025
- const style = window.getComputedStyle(el);
23026
- return {
23027
- top: parseInt(style.marginTop, 10) || 0,
23028
- bottom: parseInt(style.marginBottom, 10) || 0,
23029
- left: parseInt(style.marginLeft, 10) || 0,
23030
- right: parseInt(style.marginRight, 10) || 0,
23031
- };
23032
- }
23033
-
23034
23071
  css /* scss */ `
23035
23072
  .o-spreadsheet {
23036
23073
  .o-icon {
@@ -24142,7 +24179,7 @@ function convertWidthFromExcel(width) {
24142
24179
  return width;
24143
24180
  return Math.round((width / WIDTH_FACTOR) * 100) / 100;
24144
24181
  }
24145
- function extractStyle(data, styleId, formatId, borderId) {
24182
+ function extractStyle(data, content, styleId, formatId, borderId) {
24146
24183
  const style = styleId ? data.styles[styleId] : {};
24147
24184
  const format = formatId ? data.formats[formatId] : undefined;
24148
24185
  const styles = {
@@ -24164,7 +24201,7 @@ function extractStyle(data, styleId, formatId, borderId) {
24164
24201
  vertical: style.verticalAlign
24165
24202
  ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
24166
24203
  : undefined,
24167
- wrapText: style.wrapping === "wrap" || undefined,
24204
+ wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
24168
24205
  },
24169
24206
  };
24170
24207
  styles.font["strike"] = !!style?.strikethrough || undefined;
@@ -24395,7 +24432,7 @@ function convertFigure(figure, id, sheetData) {
24395
24432
  return undefined;
24396
24433
  }
24397
24434
  function isChartData(data) {
24398
- return "dataSets" in data;
24435
+ return "dataSets" in data && data.dataSets.length > 0;
24399
24436
  }
24400
24437
  function isImageData(data) {
24401
24438
  return "imageSrc" in data;
@@ -24741,9 +24778,8 @@ function convertRows(sheet, numberOfRows, headerGroups) {
24741
24778
  }
24742
24779
  return rows;
24743
24780
  }
24744
- /** Remove newlines (\n) in shared strings, We do not support them */
24745
24781
  function convertSharedStrings(xlsxSharedStrings) {
24746
- return xlsxSharedStrings.map((str) => str.replace(/\n/g, ""));
24782
+ return xlsxSharedStrings.map(replaceNewLines);
24747
24783
  }
24748
24784
  function convertCells(sheet, data, sheetDims, warningManager) {
24749
24785
  const cells = {};
@@ -25831,15 +25867,10 @@ class XlsxMiscExtractor extends XlsxBaseExtractor {
25831
25867
  getSharedStrings() {
25832
25868
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "si" }, (ssElement) => {
25833
25869
  // Shared string can either be a simple text, or a rich text (text with formatting, possibly in multiple parts)
25834
- if (ssElement.children[0].tagName === "t") {
25835
- return this.extractTextContent(ssElement) || "";
25836
- }
25837
25870
  // We don't support rich text formatting, we'll only extract the text
25838
- else {
25839
- return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25840
- return this.extractTextContent(textElement) || "";
25841
- }).join("");
25842
- }
25871
+ return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25872
+ return this.extractTextContent(textElement) || "";
25873
+ }).join("");
25843
25874
  });
25844
25875
  }
25845
25876
  }
@@ -27156,12 +27187,9 @@ migrationStepRegistry
27156
27187
  }
27157
27188
  const oldName = sheet.name;
27158
27189
  const escapedName = sanitizeSheetName(oldName, "_");
27159
- let i = 1;
27160
- let newName = escapedName;
27161
- while (namesTaken.includes(newName)) {
27162
- newName = `${escapedName}${i}`;
27163
- i++;
27164
- }
27190
+ const newName = getUniqueText(escapedName, namesTaken, {
27191
+ compute: (name, i) => `${name}${i}`,
27192
+ });
27165
27193
  sheet.name = newName;
27166
27194
  namesTaken.push(newName);
27167
27195
  const replaceName = (str) => {
@@ -27480,6 +27508,13 @@ migrationStepRegistry
27480
27508
  }
27481
27509
  return data;
27482
27510
  },
27511
+ })
27512
+ .add("migration_24", {
27513
+ // Empty migration to allow odoo migrate pivot custom sorting.
27514
+ versionFrom: "24",
27515
+ migrate(data) {
27516
+ return data;
27517
+ },
27483
27518
  });
27484
27519
  function fixOverlappingFilters(data) {
27485
27520
  for (let sheet of data.sheets || []) {
@@ -27507,7 +27542,7 @@ function fixOverlappingFilters(data) {
27507
27542
  * a breaking change is made in the way the state is handled, and an upgrade
27508
27543
  * function should be defined
27509
27544
  */
27510
- const CURRENT_VERSION = 24;
27545
+ const CURRENT_VERSION = 25;
27511
27546
  const INITIAL_SHEET_ID = "Sheet1";
27512
27547
  /**
27513
27548
  * This function tries to load anything that could look like a valid
@@ -28408,12 +28443,12 @@ function getTrendDatasetForLineChart(config, data, labels, axisType, locale) {
28408
28443
  }
28409
28444
  const numberOfStep = 5 * trendLabels.length;
28410
28445
  const step = (xmax - xmin) / numberOfStep;
28411
- const newLabels = range(xmin, xmax + step / 2, step);
28412
- const newValues = interpolateData(config, filteredValues, filteredLabels, newLabels);
28413
- if (!newValues.length) {
28446
+ const trendNewLabels = range(xmin, xmax + step / 2, step);
28447
+ const trendValues = interpolateData(config, filteredValues, filteredLabels, trendNewLabels);
28448
+ if (!trendValues.length) {
28414
28449
  return;
28415
28450
  }
28416
- return newValues;
28451
+ return trendValues;
28417
28452
  }
28418
28453
  function interpolateData(config, values, labels, newLabels) {
28419
28454
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
@@ -28429,13 +28464,16 @@ function interpolateData(config, values, labels, newLabels) {
28429
28464
  case "polynomial": {
28430
28465
  const order = config.order;
28431
28466
  if (!order) {
28432
- return Array.from({ length: newLabels.length }, () => NaN);
28467
+ return newLabels.map((x) => ({ x, y: NaN }));
28433
28468
  }
28434
28469
  if (order === 1) {
28435
- return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0];
28470
+ return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0].map((y, i) => ({ x: newLabels[i], y }));
28436
28471
  }
28437
28472
  const coeffs = polynomialRegression(values, normalizedLabels, order, true).flat();
28438
- return normalizedNewLabels.map((v) => evaluatePolynomial(coeffs, v, order));
28473
+ return normalizedNewLabels.map((x, i) => ({
28474
+ x: newLabels[i],
28475
+ y: evaluatePolynomial(coeffs, x, order),
28476
+ }));
28439
28477
  }
28440
28478
  case "exponential": {
28441
28479
  const positiveLogValues = [];
@@ -28447,22 +28485,22 @@ function interpolateData(config, values, labels, newLabels) {
28447
28485
  }
28448
28486
  }
28449
28487
  if (!filteredLabels.length) {
28450
- return Array.from({ length: newLabels.length }, () => NaN);
28488
+ return newLabels.map((x) => ({ x, y: NaN }));
28451
28489
  }
28452
- return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0];
28490
+ return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0].map((y, i) => ({ x: newLabels[i], y }));
28453
28491
  }
28454
28492
  case "logarithmic": {
28455
- return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0];
28493
+ return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0].map((y, i) => ({ x: newLabels[i], y }));
28456
28494
  }
28457
28495
  case "trailingMovingAverage": {
28458
- return getMovingAverageValues(values, config.window);
28496
+ return getMovingAverageValues(values, labels, config.window);
28459
28497
  }
28460
28498
  default:
28461
- return Array.from({ length: newLabels.length }, () => NaN);
28499
+ return newLabels.map((x) => ({ x, y: NaN }));
28462
28500
  }
28463
28501
  }
28464
28502
  catch (e) {
28465
- return Array.from({ length: newLabels.length }, () => NaN);
28503
+ return newLabels.map((x) => ({ x, y: NaN }));
28466
28504
  }
28467
28505
  }
28468
28506
  function getChartAxisType(chart, labelRange, getters) {
@@ -28684,7 +28722,7 @@ function getChartDatasetValues(getters, dataSets) {
28684
28722
  : undefined;
28685
28723
  label =
28686
28724
  cell && labelRange
28687
- ? truncateLabel(cell.formattedValue)
28725
+ ? cell.formattedValue
28688
28726
  : (label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`);
28689
28727
  }
28690
28728
  else {
@@ -28698,7 +28736,7 @@ function getChartDatasetValues(getters, dataSets) {
28698
28736
  // then using the classical aggregation method to sum the values.
28699
28737
  data.fill(1);
28700
28738
  }
28701
- else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), getters.getLocale()))) {
28739
+ else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28702
28740
  continue;
28703
28741
  }
28704
28742
  datasetValues.push({ data, label });
@@ -28774,7 +28812,7 @@ function getWaterfallDatasetAndLabels(definition, args) {
28774
28812
  }
28775
28813
  return {
28776
28814
  datasets: [dataset],
28777
- labels: labelsWithSubTotals.map(truncateLabel),
28815
+ labels: labelsWithSubTotals,
28778
28816
  };
28779
28817
  }
28780
28818
  function getLineChartDatasets(definition, args) {
@@ -29023,7 +29061,7 @@ function getPieChartLegend(definition, args) {
29023
29061
  generateLabels: (c) =>
29024
29062
  //@ts-ignore
29025
29063
  c.data.labels.map((label, index) => ({
29026
- text: label,
29064
+ text: truncateLabel(String(label)),
29027
29065
  strokeStyle: colors[index],
29028
29066
  fillStyle: colors[index],
29029
29067
  pointStyle: "rect",
@@ -29092,6 +29130,7 @@ function getWaterfallChartLegend(definition, args) {
29092
29130
  return legendValues;
29093
29131
  },
29094
29132
  },
29133
+ onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29095
29134
  };
29096
29135
  }
29097
29136
  function getRadarChartLegend(definition, args) {
@@ -29153,7 +29192,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29153
29192
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29154
29193
  if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29155
29194
  return {
29156
- text: dataset.label ?? "",
29195
+ text: truncateLabel(dataset.label),
29157
29196
  fontColor,
29158
29197
  strokeStyle: dataset.borderColor,
29159
29198
  hidden: !chart.isDatasetVisible(index),
@@ -29163,7 +29202,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29163
29202
  };
29164
29203
  }
29165
29204
  return {
29166
- text: dataset.label ?? "",
29205
+ text: truncateLabel(dataset.label),
29167
29206
  fontColor,
29168
29207
  strokeStyle: dataset.borderColor,
29169
29208
  fillStyle: dataset.backgroundColor,
@@ -29233,14 +29272,19 @@ function getLineChartScales(definition, args) {
29233
29272
  /* We add a second x axis here to draw the trend lines, with the labels length being
29234
29273
  * set so that the second axis points match the classical x axis
29235
29274
  */
29236
- const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset?.length || 0));
29237
29275
  scales[TREND_LINE_XAXIS_ID] = {
29238
29276
  ...scales.x,
29239
- type: "category",
29240
- labels: range(0, maxLength).map((x) => x.toString()),
29241
- offset: false,
29242
29277
  display: false,
29243
29278
  };
29279
+ if (axisType === "category" || axisType === "time") {
29280
+ /* We add a second x axis here to draw the trend lines, with the labels length being
29281
+ * set so that the second axis points match the classical x axis
29282
+ */
29283
+ const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset?.length || 0));
29284
+ scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29285
+ scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29286
+ scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29287
+ }
29244
29288
  }
29245
29289
  return scales;
29246
29290
  }
@@ -29305,7 +29349,10 @@ function getRadarChartScales(definition, args) {
29305
29349
  callback: formatTickValue({ format: axisFormats?.r, locale }),
29306
29350
  backdropColor: definition.background || "#FFFFFF",
29307
29351
  },
29308
- pointLabels: { color: chartFontColor(definition.background) },
29352
+ pointLabels: {
29353
+ color: chartFontColor(definition.background),
29354
+ callback: truncateLabel,
29355
+ },
29309
29356
  suggestedMin: minValue < 0 ? minValue - 1 : 0,
29310
29357
  },
29311
29358
  };
@@ -29402,6 +29449,11 @@ function getChartAxis(definition, position, type, options) {
29402
29449
  ticks: {
29403
29450
  padding: 5,
29404
29451
  color: fontColor,
29452
+ callback: function (tickValue) {
29453
+ // Category axis callback's internal tick value is the index of the label
29454
+ // https://www.chartjs.org/docs/latest/axes/labelling.html#creating-custom-tick-formats
29455
+ return truncateLabel(this.getLabelForValue(tickValue));
29456
+ },
29405
29457
  },
29406
29458
  grid: {
29407
29459
  display: false,
@@ -29481,8 +29533,70 @@ function getChartTitle(definition) {
29481
29533
  };
29482
29534
  }
29483
29535
 
29536
+ /**
29537
+ * Custom tooltip for the charts. Mostly copied from Odoo's custom tooltip, with some slight changes to make it work
29538
+ * with o-spreadsheet chart data and CSS.
29539
+ *
29540
+ * https://github.com/odoo/odoo/blob/18.0/addons/web/static/src/views/graph/graph_renderer.xml
29541
+ */
29542
+ const templates = /* xml */ `
29543
+ <templates>
29544
+ <t t-name="o-spreadsheet-CustomTooltip">
29545
+ <div
29546
+ class="o-chart-custom-tooltip border rounded px-2 py-1 pe-none mw-100 position-absolute text-nowrap shadow opacity-100">
29547
+ <table class="overflow-hidden m-0">
29548
+ <thead>
29549
+ <tr>
29550
+ <th class="o-tooltip-title align-baseline border-0 text-truncate" t-esc="title" t-attf-style="max-width: {{ labelsMaxWidth }}"/>
29551
+ </tr>
29552
+ </thead>
29553
+ <tbody>
29554
+ <tr t-foreach="tooltipItems" t-as="tooltipItem" t-key="tooltipItem_index">
29555
+ <td>
29556
+ <span
29557
+ class="badge ps-2 py-2 rounded-0 align-middle"
29558
+ t-attf-style="background-color: {{ tooltipItem.boxColor }}"
29559
+ > </span>
29560
+ <small
29561
+ t-if="tooltipItem.label"
29562
+ class="o-tooltip-label d-inline-block text-truncate align-middle smaller ms-2"
29563
+ t-esc="tooltipItem.label"
29564
+ t-attf-style="max-width: {{ labelsMaxWidth }}"
29565
+ />
29566
+ </td>
29567
+ <td class="o-tooltip-value ps-2 fw-bolder text-end">
29568
+ <small class="smaller d-inline-block text-truncate align-middle" t-attf-style="max-width: {{ valuesMaxWidth }}">
29569
+ <t t-esc="tooltipItem.value"/>
29570
+ <t t-if="tooltipItem.percentage">
29571
+ (
29572
+ <t t-esc="tooltipItem.percentage"/>
29573
+ %)
29574
+ </t>
29575
+ </small>
29576
+ </td>
29577
+ </tr>
29578
+ </tbody>
29579
+ </table>
29580
+ </div>
29581
+ </t>
29582
+ </templates>
29583
+ `;
29584
+ const app = new owl.App(owl.Component, { templates, translateFn: _t });
29585
+ function renderToString(templateName, context = {}) {
29586
+ return render(templateName, context).innerHTML;
29587
+ }
29588
+ function render(templateName, context = {}) {
29589
+ const templateFn = app.getTemplate(templateName);
29590
+ const bdom = templateFn(context, {});
29591
+ const div = document.createElement("div");
29592
+ owl.blockDom.mount(bdom, div);
29593
+ return div;
29594
+ }
29595
+
29484
29596
  function getBarChartTooltip(definition, args) {
29485
29597
  return {
29598
+ enabled: false,
29599
+ external: customTooltipHandler,
29486
29600
  callbacks: {
29487
29601
  title: function (tooltipItems) {
29488
29602
  return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
@@ -29506,11 +29620,17 @@ function getBarChartTooltip(definition, args) {
29506
29620
  function getLineChartTooltip(definition, args) {
29507
29621
  const { axisType, locale, axisFormats } = args;
29508
29622
  const labelFormat = axisFormats?.x;
29509
- const tooltip = { callbacks: {} };
29623
+ const tooltip = {
29624
+ enabled: false,
29625
+ external: customTooltipHandler,
29626
+ callbacks: {},
29627
+ };
29510
29628
  if (axisType === "linear") {
29511
29629
  tooltip.callbacks.label = (tooltipItem) => {
29512
29630
  const dataSetPoint = tooltipItem.parsed.y;
29513
- let label = tooltipItem.parsed.x;
29631
+ let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29632
+ ? ""
29633
+ : tooltipItem.parsed.x;
29514
29634
  if (typeof label === "string" && isNumber(label, locale)) {
29515
29635
  label = toNumber(label, locale);
29516
29636
  }
@@ -29543,6 +29663,8 @@ function getPieChartTooltip(definition, args) {
29543
29663
  const { locale, axisFormats } = args;
29544
29664
  const format = axisFormats?.y || axisFormats?.y1;
29545
29665
  return {
29666
+ enabled: false,
29667
+ external: customTooltipHandler,
29546
29668
  callbacks: {
29547
29669
  title: function (tooltipItems) {
29548
29670
  return tooltipItems[0].dataset.label;
@@ -29567,6 +29689,8 @@ function getWaterfallChartTooltip(definition, args) {
29567
29689
  const format = axisFormats?.y || axisFormats?.y1;
29568
29690
  const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
29569
29691
  return {
29692
+ enabled: false,
29693
+ external: customTooltipHandler,
29570
29694
  callbacks: {
29571
29695
  label: function (tooltipItem) {
29572
29696
  const [lastValue, currentValue] = tooltipItem.raw;
@@ -29598,6 +29722,8 @@ function getPyramidChartTooltip(definition, args) {
29598
29722
  function getRadarChartTooltip(definition, args) {
29599
29723
  const { locale, axisFormats } = args;
29600
29724
  return {
29725
+ enabled: false,
29726
+ external: customTooltipHandler,
29601
29727
  callbacks: {
29602
29728
  label: function (tooltipItem) {
29603
29729
  const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
@@ -29636,6 +29762,48 @@ function calculatePercentage(dataset, dataIndex) {
29636
29762
  const percentage = (dataset[dataIndex] / total) * 100;
29637
29763
  return percentage.toFixed(2);
29638
29764
  }
29765
+ function customTooltipHandler({ chart, tooltip }) {
29766
+ chart.canvas.parentNode.querySelector("div.o-chart-custom-tooltip")?.remove();
29767
+ if (tooltip.opacity === 0 || tooltip.dataPoints.length === 0) {
29768
+ return;
29769
+ }
29770
+ const tooltipItems = tooltip.body.map((body, index) => {
29771
+ let [label, value] = body.lines[0].split(":").map((str) => str.trim());
29772
+ if (!value) {
29773
+ value = label;
29774
+ label = "";
29775
+ }
29776
+ const color = tooltip.labelColors[index].backgroundColor;
29777
+ return {
29778
+ label,
29779
+ value,
29780
+ boxColor: typeof color === "string" ? setColorAlpha(color, 1) : color,
29781
+ };
29782
+ });
29783
+ const innerHTML = renderToString("o-spreadsheet-CustomTooltip", {
29784
+ labelsMaxWidth: Math.floor(chart.canvas.clientWidth * 0.5) + "px",
29785
+ valuesMaxWidth: Math.floor(chart.canvas.clientWidth * 0.25) + "px",
29786
+ title: tooltip.title[0],
29787
+ tooltipItems,
29788
+ });
29789
+ const template = Object.assign(document.createElement("template"), { innerHTML });
29790
+ const newTooltipEl = template.content.firstChild;
29791
+ chart.canvas.parentNode?.appendChild(newTooltipEl);
29792
+ Object.assign(newTooltipEl.style, {
29793
+ left: getTooltipLeftPosition(chart, tooltip, newTooltipEl.clientWidth) + "px",
29794
+ top: Math.floor(tooltip.caretY - newTooltipEl.clientHeight / 2) + "px",
29795
+ });
29796
+ }
29797
+ /**
29798
+ * Get the left position for the tooltip, making sure it doesn't go out of the chart area.
29799
+ */
29800
+ function getTooltipLeftPosition(chart, tooltip, tooltipWidth) {
29801
+ const x = tooltip.caretX;
29802
+ if (x + tooltipWidth > chart.chartArea.right) {
29803
+ return Math.max(0, x - tooltipWidth);
29804
+ }
29805
+ return x;
29806
+ }
29639
29807
 
29640
29808
  var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
29641
29809
  __proto__: null,
@@ -29749,11 +29917,11 @@ class BarChart extends AbstractChart {
29749
29917
  : undefined,
29750
29918
  };
29751
29919
  }
29752
- copyForSheetId(sheetId) {
29753
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
29754
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
29755
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
29756
- return new BarChart(definition, sheetId, this.getters);
29920
+ duplicateInDuplicatedSheet(newSheetId) {
29921
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
29922
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
29923
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
29924
+ return new BarChart(definition, newSheetId, this.getters);
29757
29925
  }
29758
29926
  copyInSheetId(sheetId) {
29759
29927
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -29820,7 +29988,7 @@ function createBarChartRuntime(chart, getters) {
29820
29988
  const config = {
29821
29989
  type: "bar",
29822
29990
  data: {
29823
- labels: chartData.labels.map(truncateLabel),
29991
+ labels: chartData.labels,
29824
29992
  datasets: getBarChartDatasets(definition, chartData),
29825
29993
  },
29826
29994
  options: {
@@ -29956,11 +30124,11 @@ class ComboChart extends AbstractChart {
29956
30124
  showValues: context.showValues,
29957
30125
  };
29958
30126
  }
29959
- copyForSheetId(sheetId) {
29960
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
29961
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
29962
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
29963
- return new ComboChart(definition, sheetId, this.getters);
30127
+ duplicateInDuplicatedSheet(newSheetId) {
30128
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30129
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30130
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30131
+ return new ComboChart(definition, newSheetId, this.getters);
29964
30132
  }
29965
30133
  copyInSheetId(sheetId) {
29966
30134
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -29973,7 +30141,7 @@ function createComboChartRuntime(chart, getters) {
29973
30141
  const config = {
29974
30142
  type: "bar",
29975
30143
  data: {
29976
- labels: chartData.labels.map(truncateLabel),
30144
+ labels: chartData.labels,
29977
30145
  datasets: getComboChartDatasets(definition, chartData),
29978
30146
  },
29979
30147
  options: {
@@ -30107,10 +30275,10 @@ class GaugeChart extends AbstractChart {
30107
30275
  },
30108
30276
  };
30109
30277
  }
30110
- copyForSheetId(sheetId) {
30111
- const dataRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.dataRange);
30112
- const definition = this.getDefinitionWithSpecificRanges(dataRange, sheetId);
30113
- return new GaugeChart(definition, sheetId, this.getters);
30278
+ duplicateInDuplicatedSheet(newSheetId) {
30279
+ const dataRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.dataRange);
30280
+ const definition = this.getDefinitionWithSpecificRanges(dataRange, newSheetId);
30281
+ return new GaugeChart(definition, newSheetId, this.getters);
30114
30282
  }
30115
30283
  copyInSheetId(sheetId) {
30116
30284
  const definition = this.getDefinitionWithSpecificRanges(this.dataRange, sheetId);
@@ -30203,7 +30371,11 @@ function createGaugeChartRuntime(chart, getters) {
30203
30371
  colors.push(chartColors.upperColor);
30204
30372
  return {
30205
30373
  background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
30206
- title: chart.title ?? { text: "" },
30374
+ title: {
30375
+ ...chart.title,
30376
+ // chart titles are extracted from .json files and they are translated at runtime here
30377
+ text: _t(chart.title.text ?? ""),
30378
+ },
30207
30379
  minValue: {
30208
30380
  value: minValue,
30209
30381
  label: formatValue(minValue, { locale, format }),
@@ -30287,11 +30459,11 @@ class GeoChart extends AbstractChart {
30287
30459
  : undefined,
30288
30460
  };
30289
30461
  }
30290
- copyForSheetId(sheetId) {
30291
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30292
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30293
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30294
- return new GeoChart(definition, sheetId, this.getters);
30462
+ duplicateInDuplicatedSheet(newSheetId) {
30463
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30464
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30465
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30466
+ return new GeoChart(definition, newSheetId, this.getters);
30295
30467
  }
30296
30468
  copyInSheetId(sheetId) {
30297
30469
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30484,11 +30656,11 @@ class LineChart extends AbstractChart {
30484
30656
  verticalAxis: getDefinedAxis(definition),
30485
30657
  };
30486
30658
  }
30487
- copyForSheetId(sheetId) {
30488
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30489
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30490
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30491
- return new LineChart(definition, sheetId, this.getters);
30659
+ duplicateInDuplicatedSheet(newSheetId) {
30660
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30661
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30662
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30663
+ return new LineChart(definition, newSheetId, this.getters);
30492
30664
  }
30493
30665
  copyInSheetId(sheetId) {
30494
30666
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30501,7 +30673,7 @@ function createLineChartRuntime(chart, getters) {
30501
30673
  const config = {
30502
30674
  type: "line",
30503
30675
  data: {
30504
- labels: chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
30676
+ labels: chartData.labels,
30505
30677
  datasets: getLineChartDatasets(definition, chartData),
30506
30678
  },
30507
30679
  options: {
@@ -30595,11 +30767,11 @@ class PieChart extends AbstractChart {
30595
30767
  showValues: this.showValues,
30596
30768
  };
30597
30769
  }
30598
- copyForSheetId(sheetId) {
30599
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30600
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30601
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30602
- return new PieChart(definition, sheetId, this.getters);
30770
+ duplicateInDuplicatedSheet(newSheetId) {
30771
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30772
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30773
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30774
+ return new PieChart(definition, newSheetId, this.getters);
30603
30775
  }
30604
30776
  copyInSheetId(sheetId) {
30605
30777
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30636,7 +30808,7 @@ function createPieChartRuntime(chart, getters) {
30636
30808
  const config = {
30637
30809
  type: chart.isDoughnut ? "doughnut" : "pie",
30638
30810
  data: {
30639
- labels: chartData.labels.map(truncateLabel),
30811
+ labels: chartData.labels,
30640
30812
  datasets: getPieChartDatasets(definition, chartData),
30641
30813
  },
30642
30814
  options: {
@@ -30716,11 +30888,11 @@ class PyramidChart extends AbstractChart {
30716
30888
  : undefined,
30717
30889
  };
30718
30890
  }
30719
- copyForSheetId(sheetId) {
30720
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30721
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30722
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30723
- return new PyramidChart(definition, sheetId, this.getters);
30891
+ duplicateInDuplicatedSheet(newSheetId) {
30892
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30893
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30894
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30895
+ return new PyramidChart(definition, newSheetId, this.getters);
30724
30896
  }
30725
30897
  copyInSheetId(sheetId) {
30726
30898
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30772,7 +30944,7 @@ function createPyramidChartRuntime(chart, getters) {
30772
30944
  const config = {
30773
30945
  type: "bar",
30774
30946
  data: {
30775
- labels: chartData.labels.map(truncateLabel),
30947
+ labels: chartData.labels,
30776
30948
  datasets: getBarChartDatasets(definition, chartData),
30777
30949
  },
30778
30950
  options: {
@@ -30853,11 +31025,11 @@ class RadarChart extends AbstractChart {
30853
31025
  : undefined,
30854
31026
  };
30855
31027
  }
30856
- copyForSheetId(sheetId) {
30857
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30858
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30859
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30860
- return new RadarChart(definition, sheetId, this.getters);
31028
+ duplicateInDuplicatedSheet(newSheetId) {
31029
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
31030
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
31031
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
31032
+ return new RadarChart(definition, newSheetId, this.getters);
30861
31033
  }
30862
31034
  copyInSheetId(sheetId) {
30863
31035
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30922,7 +31094,7 @@ function createRadarChartRuntime(chart, getters) {
30922
31094
  const config = {
30923
31095
  type: "radar",
30924
31096
  data: {
30925
- labels: chartData.labels.map(truncateLabel),
31097
+ labels: chartData.labels,
30926
31098
  datasets: getRadarChartDatasets(definition, chartData),
30927
31099
  },
30928
31100
  options: {
@@ -31056,11 +31228,11 @@ class ScatterChart extends AbstractChart {
31056
31228
  verticalAxis: getDefinedAxis(definition),
31057
31229
  };
31058
31230
  }
31059
- copyForSheetId(sheetId) {
31060
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
31061
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
31062
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
31063
- return new ScatterChart(definition, sheetId, this.getters);
31231
+ duplicateInDuplicatedSheet(newSheetId) {
31232
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
31233
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
31234
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
31235
+ return new ScatterChart(definition, newSheetId, this.getters);
31064
31236
  }
31065
31237
  copyInSheetId(sheetId) {
31066
31238
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -31075,7 +31247,7 @@ function createScatterChartRuntime(chart, getters) {
31075
31247
  // have less options than the line chart (it only works with linear labels)
31076
31248
  type: "line",
31077
31249
  data: {
31078
- labels: chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
31250
+ labels: chartData.labels,
31079
31251
  datasets: getScatterChartDatasets(definition, chartData),
31080
31252
  },
31081
31253
  options: {
@@ -31173,11 +31345,11 @@ class WaterfallChart extends AbstractChart {
31173
31345
  : undefined,
31174
31346
  };
31175
31347
  }
31176
- copyForSheetId(sheetId) {
31177
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
31178
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
31179
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
31180
- return new WaterfallChart(definition, sheetId, this.getters);
31348
+ duplicateInDuplicatedSheet(newSheetId) {
31349
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
31350
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
31351
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
31352
+ return new WaterfallChart(definition, newSheetId, this.getters);
31181
31353
  }
31182
31354
  copyInSheetId(sheetId) {
31183
31355
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -33604,8 +33776,6 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33604
33776
  chartToImage: chartToImage,
33605
33777
  checkDataset: checkDataset,
33606
33778
  checkLabelRange: checkLabelRange,
33607
- copyDataSetsWithNewSheetId: copyDataSetsWithNewSheetId,
33608
- copyLabelRangeWithNewSheetId: copyLabelRangeWithNewSheetId,
33609
33779
  createBarChartRuntime: createBarChartRuntime,
33610
33780
  createDataSets: createDataSets,
33611
33781
  createGaugeChartRuntime: createGaugeChartRuntime,
@@ -33614,6 +33784,8 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33614
33784
  createScorecardChartRuntime: createScorecardChartRuntime,
33615
33785
  createWaterfallChartRuntime: createWaterfallChartRuntime,
33616
33786
  drawScoreChart: drawScoreChart,
33787
+ duplicateDataSetsInDuplicatedSheet: duplicateDataSetsInDuplicatedSheet,
33788
+ duplicateLabelRangeInDuplicatedSheet: duplicateLabelRangeInDuplicatedSheet,
33617
33789
  formatChartDatasetValue: formatChartDatasetValue,
33618
33790
  formatTickValue: formatTickValue,
33619
33791
  getChartPositionAtCenterOfViewport: getChartPositionAtCenterOfViewport,
@@ -35334,12 +35506,20 @@ function fontSizeMenuBuilder() {
35334
35506
  });
35335
35507
  }
35336
35508
  function isAutomaticFormatSelected(env) {
35337
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
35338
- return !activeCell || !activeCell.format;
35509
+ const activePosition = env.model.getters.getActivePosition();
35510
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
35511
+ if (pivotCell.type === "VALUE") {
35512
+ return !env.model.getters.getEvaluatedCell(activePosition).format;
35513
+ }
35514
+ return !env.model.getters.getCell(activePosition)?.format;
35339
35515
  }
35340
35516
  function isFormatSelected(env, format) {
35341
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
35342
- return activeCell?.format === format;
35517
+ const activePosition = env.model.getters.getActivePosition();
35518
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
35519
+ if (pivotCell.type === "VALUE") {
35520
+ return env.model.getters.getEvaluatedCell(activePosition).format === format;
35521
+ }
35522
+ return env.model.getters.getCell(activePosition)?.format === format;
35343
35523
  }
35344
35524
  function isFontSizeSelected(env, fontSize) {
35345
35525
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
@@ -39678,14 +39858,11 @@ class ChartPanel extends owl.Component {
39678
39858
  }
39679
39859
 
39680
39860
  class DOMFocusableElementStore {
39681
- mutators = ["setFocusableElement", "focus"];
39861
+ mutators = ["setFocusableElement"];
39682
39862
  focusableElement = undefined;
39683
39863
  setFocusableElement(element) {
39684
39864
  this.focusableElement = element;
39685
39865
  }
39686
- focus() {
39687
- this.focusableElement?.focus();
39688
- }
39689
39866
  }
39690
39867
 
39691
39868
  css /* scss */ `
@@ -40333,7 +40510,7 @@ class Composer extends owl.Component {
40333
40510
  if (document.activeElement === this.contentHelper.el &&
40334
40511
  this.props.composerStore.editionMode === "inactive" &&
40335
40512
  !this.props.isDefaultFocus) {
40336
- this.DOMFocusableElementStore.focus();
40513
+ this.DOMFocusableElementStore.focusableElement?.focus();
40337
40514
  }
40338
40515
  });
40339
40516
  owl.useEffect(() => {
@@ -44314,16 +44491,21 @@ class TextInput extends owl.Component {
44314
44491
  }
44315
44492
  this.inputRef.el?.blur();
44316
44493
  }
44317
- focusInputAndSelectContent() {
44318
- const inputEl = this.inputRef.el;
44319
- if (!inputEl)
44320
- return;
44321
- // The onFocus event selects all text in the input.
44322
- // The subsequent mouseup event can deselect this text,
44323
- // so t-on-mouseup.prevent.stop is used to prevent this
44324
- // default behavior and preserve the selection.
44325
- inputEl.focus();
44326
- inputEl.select();
44494
+ onMouseDown(ev) {
44495
+ // Stop the event if the input is not focused, we handle everything in onMouseUp
44496
+ if (ev.target !== document.activeElement) {
44497
+ ev.preventDefault();
44498
+ ev.stopPropagation();
44499
+ }
44500
+ }
44501
+ onMouseUp(ev) {
44502
+ const target = ev.target;
44503
+ if (target !== document.activeElement) {
44504
+ target.focus();
44505
+ target.select();
44506
+ ev.preventDefault();
44507
+ ev.stopPropagation();
44508
+ }
44327
44509
  }
44328
44510
  }
44329
44511
 
@@ -44876,7 +45058,16 @@ class PivotTitleSection extends owl.Component {
44876
45058
  newPivotId,
44877
45059
  newSheetId,
44878
45060
  });
44879
- const text = result.isSuccessful ? _t("Pivot duplicated.") : _t("Pivot duplication failed");
45061
+ let text;
45062
+ if (result.isSuccessful) {
45063
+ text = _t("Pivot duplicated.");
45064
+ }
45065
+ else if (result.isCancelledBecause("PivotInError" /* CommandResult.PivotInError */)) {
45066
+ text = _t("Cannot duplicate a pivot in error.");
45067
+ }
45068
+ else {
45069
+ text = _t("Pivot duplication failed.");
45070
+ }
44880
45071
  const type = result.isSuccessful ? "success" : "danger";
44881
45072
  this.env.notifyUser({
44882
45073
  text,
@@ -46027,12 +46218,10 @@ class SpreadsheetPivot {
46027
46218
  * Take cares of double names
46028
46219
  */
46029
46220
  findName(name, fields) {
46030
- let increment = 1;
46031
- const initialName = name;
46032
- while (name in fields) {
46033
- name = `${initialName}${++increment}`;
46034
- }
46035
- return name;
46221
+ return getUniqueText(name, Object.keys(fields), {
46222
+ compute: (name, i) => `${name}${i}`,
46223
+ start: 2,
46224
+ });
46036
46225
  }
46037
46226
  extractDataEntriesFromRange(range) {
46038
46227
  const dataEntries = [];
@@ -46215,7 +46404,9 @@ class PivotSidePanelStore extends SpreadsheetStore {
46215
46404
  pivot: this.draft,
46216
46405
  });
46217
46406
  this.draft = null;
46218
- if (!this.alreadyNotified && !this.isDynamicPivotInViewport()) {
46407
+ if (!this.alreadyNotified &&
46408
+ !this.isDynamicPivotInViewport() &&
46409
+ this.isStaticPivotInViewport()) {
46219
46410
  const formulaId = this.getters.getPivotFormulaId(this.pivotId);
46220
46411
  const pivotExample = `=PIVOT(${formulaId})`;
46221
46412
  this.alreadyNotified = true;
@@ -46272,11 +46463,20 @@ class PivotSidePanelStore extends SpreadsheetStore {
46272
46463
  }
46273
46464
  }
46274
46465
  isDynamicPivotInViewport() {
46275
- const sheetId = this.getters.getActiveSheetId();
46276
- for (const col of this.getters.getSheetViewVisibleCols()) {
46277
- for (const row of this.getters.getSheetViewVisibleRows()) {
46278
- const isDynamicPivot = this.getters.isSpillPivotFormula({ sheetId, col, row });
46279
- if (isDynamicPivot) {
46466
+ for (const position of this.getters.getVisibleCellPositions()) {
46467
+ const isDynamicPivot = this.getters.isSpillPivotFormula(position);
46468
+ if (isDynamicPivot) {
46469
+ return true;
46470
+ }
46471
+ }
46472
+ return false;
46473
+ }
46474
+ isStaticPivotInViewport() {
46475
+ for (const position of this.getters.getVisibleCellPositions()) {
46476
+ const cell = this.getters.getCell(position);
46477
+ if (cell?.isFormula) {
46478
+ const pivotFunction = getFirstPivotFunction(cell.compiledFormula.tokens);
46479
+ if (pivotFunction && pivotFunction.functionName !== "PIVOT") {
46280
46480
  return true;
46281
46481
  }
46282
46482
  }
@@ -51695,7 +51895,7 @@ class Grid extends owl.Component {
51695
51895
  this.cellPopovers = useStore(CellPopoverStore);
51696
51896
  owl.useEffect(() => {
51697
51897
  if (!this.sidePanel.isOpen) {
51698
- this.DOMFocusableElementStore.focus();
51898
+ this.DOMFocusableElementStore.focusableElement?.focus();
51699
51899
  }
51700
51900
  }, () => [this.sidePanel.isOpen]);
51701
51901
  }
@@ -51901,7 +52101,7 @@ class Grid extends owl.Component {
51901
52101
  focusDefaultElement() {
51902
52102
  if (!this.env.model.getters.getSelectedFigureId() &&
51903
52103
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
51904
- this.DOMFocusableElementStore.focus();
52104
+ this.DOMFocusableElementStore.focusableElement?.focus();
51905
52105
  }
51906
52106
  }
51907
52107
  get gridEl() {
@@ -52260,15 +52460,11 @@ class Grid extends owl.Component {
52260
52460
  class BasePlugin {
52261
52461
  static getters = [];
52262
52462
  history;
52263
- dispatch;
52264
- canDispatch;
52265
- constructor(stateObserver, dispatch, canDispatch) {
52463
+ constructor(stateObserver) {
52266
52464
  this.history = Object.assign(Object.create(stateObserver), {
52267
52465
  update: stateObserver.addChange.bind(stateObserver, this),
52268
52466
  selectCell: () => { },
52269
52467
  });
52270
- this.dispatch = dispatch;
52271
- this.canDispatch = canDispatch;
52272
52468
  }
52273
52469
  /**
52274
52470
  * Export for excel should be available for all plugins, even for the UI.
@@ -52346,10 +52542,14 @@ class BasePlugin {
52346
52542
  */
52347
52543
  class CorePlugin extends BasePlugin {
52348
52544
  getters;
52545
+ dispatch;
52546
+ canDispatch;
52349
52547
  constructor({ getters, stateObserver, range, dispatch, canDispatch }) {
52350
- super(stateObserver, dispatch, canDispatch);
52548
+ super(stateObserver);
52351
52549
  range.addRangeProvider(this.adaptRanges.bind(this));
52352
52550
  this.getters = getters;
52551
+ this.dispatch = dispatch;
52552
+ this.canDispatch = canDispatch;
52353
52553
  }
52354
52554
  // ---------------------------------------------------------------------------
52355
52555
  // Import/Export
@@ -52438,10 +52638,34 @@ class BordersPlugin extends CorePlugin {
52438
52638
  const elements = [...cmd.elements].sort((a, b) => b - a);
52439
52639
  for (const group of groupConsecutive(elements)) {
52440
52640
  if (cmd.dimension === "COL") {
52441
- this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
52641
+ if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52642
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52643
+ this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52644
+ }
52645
+ }
52646
+ if (group[group.length - 1] === 0) {
52647
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52648
+ this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52649
+ }
52650
+ }
52651
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52652
+ this.clearInsideBorders(cmd.sheetId, [zone]);
52653
+ this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52442
52654
  }
52443
52655
  else {
52444
- this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
52656
+ if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52657
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52658
+ this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52659
+ }
52660
+ }
52661
+ if (group[group.length - 1] === 0) {
52662
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52663
+ this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52664
+ }
52665
+ }
52666
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52667
+ this.clearInsideBorders(cmd.sheetId, [zone]);
52668
+ this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52445
52669
  }
52446
52670
  }
52447
52671
  break;
@@ -52748,6 +52972,18 @@ class BordersPlugin extends CorePlugin {
52748
52972
  }
52749
52973
  }
52750
52974
  }
52975
+ /**
52976
+ * Remove the borders inside of a zone
52977
+ */
52978
+ clearInsideBorders(sheetId, zones) {
52979
+ for (let zone of zones) {
52980
+ for (let row = zone.top; row <= zone.bottom; row++) {
52981
+ for (let col = zone.left; col <= zone.right; col++) {
52982
+ this.history.update("borders", sheetId, col, row, undefined);
52983
+ }
52984
+ }
52985
+ }
52986
+ }
52751
52987
  /**
52752
52988
  * Add a border to the existing one to a cell
52753
52989
  */
@@ -53550,7 +53786,7 @@ class ChartPlugin extends CorePlugin {
53550
53786
  if (fig.tag === "chart") {
53551
53787
  const figureIdBase = fig.id.split(FIGURE_ID_SPLITTER).pop();
53552
53788
  const duplicatedFigureId = `${cmd.sheetIdTo}${FIGURE_ID_SPLITTER}${figureIdBase}`;
53553
- const chart = this.charts[fig.id]?.copyForSheetId(cmd.sheetIdTo);
53789
+ const chart = this.charts[fig.id]?.duplicateInDuplicatedSheet(cmd.sheetIdTo);
53554
53790
  if (chart) {
53555
53791
  this.dispatch("CREATE_CHART", {
53556
53792
  id: duplicatedFigureId,
@@ -54194,7 +54430,7 @@ class DataValidationPlugin extends CorePlugin {
54194
54430
  case "DUPLICATE_SHEET": {
54195
54431
  const rules = deepCopy(this.rules[cmd.sheetId]).map((rule) => ({
54196
54432
  ...rule,
54197
- ranges: rule.ranges.map((range) => copyRangeWithNewSheetId(cmd.sheetId, cmd.sheetIdTo, range)),
54433
+ ranges: rule.ranges.map((range) => duplicateRangeInDuplicatedSheet(cmd.sheetId, cmd.sheetIdTo, range)),
54198
54434
  }));
54199
54435
  this.history.update("rules", cmd.sheetIdTo, rules);
54200
54436
  break;
@@ -56243,14 +56479,11 @@ class SheetPlugin extends CorePlugin {
56243
56479
  return dimension === "COL" ? this.getNumberCols(sheetId) : this.getNumberRows(sheetId);
56244
56480
  }
56245
56481
  getNextSheetName(baseName = "Sheet") {
56246
- let i = 1;
56247
56482
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56248
- let name = `${baseName}${i}`;
56249
- while (names.includes(name)) {
56250
- name = `${baseName}${i}`;
56251
- i++;
56252
- }
56253
- return name;
56483
+ return getUniqueText(baseName, names, {
56484
+ compute: (name, i) => `${name}${i}`,
56485
+ computeFirstOne: true,
56486
+ });
56254
56487
  }
56255
56488
  getSheetSize(sheetId) {
56256
56489
  return {
@@ -56530,15 +56763,9 @@ class SheetPlugin extends CorePlugin {
56530
56763
  this.history.update("sheetIdsMapName", sheetIdsMapName);
56531
56764
  }
56532
56765
  getDuplicateSheetName(sheetName) {
56533
- let i = 1;
56534
56766
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56535
56767
  const baseName = _t("Copy of %s", sheetName);
56536
- let name = baseName.toString();
56537
- while (names.includes(name)) {
56538
- name = `${baseName} (${i})`;
56539
- i++;
56540
- }
56541
- return name;
56768
+ return getUniqueText(baseName.toString(), names);
56542
56769
  }
56543
56770
  deleteSheet(sheet) {
56544
56771
  const name = sheet.name;
@@ -58068,15 +58295,8 @@ class TableStylePlugin extends CorePlugin {
58068
58295
  }
58069
58296
  getNewCustomTableStyleName() {
58070
58297
  let name = _t("Custom Table Style");
58071
- const styleNames = new Set(Object.values(this.styles).map((style) => style.displayName));
58072
- if (!styleNames.has(name)) {
58073
- return name;
58074
- }
58075
- let i = 2;
58076
- while (styleNames.has(`${name} ${i}`)) {
58077
- i++;
58078
- }
58079
- return `${name} ${i}`;
58298
+ const styleNames = Object.values(this.styles).map((style) => style.displayName);
58299
+ return getUniqueText(name, styleNames, { compute: (name, i) => `${name} ${i}`, start: 2 });
58080
58300
  }
58081
58301
  isTableStyleEditable(styleId) {
58082
58302
  return !TABLE_PRESETS[styleId];
@@ -58106,24 +58326,15 @@ class TableStylePlugin extends CorePlugin {
58106
58326
  }
58107
58327
 
58108
58328
  /**
58109
- * UI plugins handle any transient data required to display a spreadsheet.
58110
- * They can draw on the grid canvas.
58329
+ * Core view plugins handle any data derived from core date (i.e. evaluation).
58330
+ * They cannot impact the model data (i.e. cannot dispatch commands).
58111
58331
  */
58112
- class UIPlugin extends BasePlugin {
58113
- static layers = [];
58332
+ class CoreViewPlugin extends BasePlugin {
58114
58333
  getters;
58115
- ui;
58116
- selection;
58117
- constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
58118
- super(stateObserver, dispatch, canDispatch);
58334
+ constructor({ getters, stateObserver }) {
58335
+ super(stateObserver);
58119
58336
  this.getters = getters;
58120
- this.ui = uiActions;
58121
- this.selection = selection;
58122
58337
  }
58123
- // ---------------------------------------------------------------------------
58124
- // Grid rendering
58125
- // ---------------------------------------------------------------------------
58126
- drawLayer(ctx, layer) { }
58127
58338
  }
58128
58339
 
58129
58340
  /**
@@ -59818,7 +60029,7 @@ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId
59818
60029
  // as necessary in several iterations, where evaluated cells can trigger the evaluation
59819
60030
  // of other cells depending on it, at the next iteration.
59820
60031
  //#endregion
59821
- class EvaluationPlugin extends UIPlugin {
60032
+ class EvaluationPlugin extends CoreViewPlugin {
59822
60033
  static getters = [
59823
60034
  "evaluateFormula",
59824
60035
  "evaluateFormulaResult",
@@ -60085,7 +60296,7 @@ function colorDistance(color1, color2) {
60085
60296
  * This plugins aims to compute and keep to custom colors used in the
60086
60297
  * current spreadsheet
60087
60298
  */
60088
- class CustomColorsPlugin extends UIPlugin {
60299
+ class CustomColorsPlugin extends CoreViewPlugin {
60089
60300
  customColors = {};
60090
60301
  shouldUpdateColors = true;
60091
60302
  static getters = ["getCustomColors"];
@@ -60221,7 +60432,7 @@ class CustomColorsPlugin extends UIPlugin {
60221
60432
  }
60222
60433
  }
60223
60434
 
60224
- class EvaluationChartPlugin extends UIPlugin {
60435
+ class EvaluationChartPlugin extends CoreViewPlugin {
60225
60436
  static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
60226
60437
  charts = {};
60227
60438
  createRuntimeChart = chartRuntimeFactory(this.getters);
@@ -60325,7 +60536,7 @@ class EvaluationChartPlugin extends UIPlugin {
60325
60536
  }
60326
60537
  }
60327
60538
 
60328
- class EvaluationConditionalFormatPlugin extends UIPlugin {
60539
+ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
60329
60540
  static getters = [
60330
60541
  "getConditionalIcon",
60331
60542
  "getCellConditionalFormatStyle",
@@ -60643,7 +60854,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
60643
60854
  }
60644
60855
 
60645
60856
  const VALID_RESULT = { isValid: true };
60646
- class EvaluationDataValidationPlugin extends UIPlugin {
60857
+ class EvaluationDataValidationPlugin extends CoreViewPlugin {
60647
60858
  static getters = [
60648
60859
  "getDataValidationInvalidCriterionValueMessage",
60649
60860
  "getInvalidDataValidationMessage",
@@ -60774,7 +60985,7 @@ class EvaluationDataValidationPlugin extends UIPlugin {
60774
60985
  }
60775
60986
  }
60776
60987
 
60777
- class DynamicTablesPlugin extends UIPlugin {
60988
+ class DynamicTablesPlugin extends CoreViewPlugin {
60778
60989
  static getters = [
60779
60990
  "canCreateDynamicTableOnZones",
60780
60991
  "doesZonesContainFilter",
@@ -60948,7 +61159,7 @@ class DynamicTablesPlugin extends UIPlugin {
60948
61159
  }
60949
61160
  }
60950
61161
 
60951
- class HeaderSizeUIPlugin extends UIPlugin {
61162
+ class HeaderSizeUIPlugin extends CoreViewPlugin {
60952
61163
  static getters = ["getRowSize", "getHeaderSize"];
60953
61164
  tallestCellInRow = {};
60954
61165
  ctx = document.createElement("canvas").getContext("2d");
@@ -61654,7 +61865,7 @@ const UNDO_REDO_PIVOT_COMMANDS = ["ADD_PIVOT", "UPDATE_PIVOT"];
61654
61865
  function isPivotCommand(cmd) {
61655
61866
  return UNDO_REDO_PIVOT_COMMANDS.includes(cmd.type);
61656
61867
  }
61657
- class PivotUIPlugin extends UIPlugin {
61868
+ class PivotUIPlugin extends CoreViewPlugin {
61658
61869
  static getters = [
61659
61870
  "getPivot",
61660
61871
  "getFirstPivotFunction",
@@ -61858,13 +62069,9 @@ class PivotUIPlugin extends UIPlugin {
61858
62069
  }
61859
62070
  generateNewCalculatedMeasureName(measures) {
61860
62071
  const existingMeasures = measures.map((m) => m.fieldName);
61861
- let i = 1;
61862
- let name = _t("Calculated measure %s", i);
61863
- while (existingMeasures.includes(name)) {
61864
- i++;
61865
- name = _t("Calculated measure %s", i);
61866
- }
61867
- return name;
62072
+ return getUniqueText(_t("Calculated measure 1"), existingMeasures, {
62073
+ compute: (name, i) => _t("Calculated measure %s", i),
62074
+ });
61868
62075
  }
61869
62076
  getPivot(pivotId) {
61870
62077
  if (!this.getters.isExistingPivot(pivotId)) {
@@ -61918,6 +62125,31 @@ class PivotUIPlugin extends UIPlugin {
61918
62125
  }
61919
62126
  }
61920
62127
 
62128
+ /**
62129
+ * UI plugins handle any transient data required to display a spreadsheet.
62130
+ * They can draw on the grid canvas.
62131
+ */
62132
+ class UIPlugin extends BasePlugin {
62133
+ static layers = [];
62134
+ getters;
62135
+ ui;
62136
+ selection;
62137
+ dispatch;
62138
+ canDispatch;
62139
+ constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
62140
+ super(stateObserver);
62141
+ this.getters = getters;
62142
+ this.ui = uiActions;
62143
+ this.selection = selection;
62144
+ this.dispatch = dispatch;
62145
+ this.canDispatch = canDispatch;
62146
+ }
62147
+ // ---------------------------------------------------------------------------
62148
+ // Grid rendering
62149
+ // ---------------------------------------------------------------------------
62150
+ drawLayer(ctx, layer) { }
62151
+ }
62152
+
61921
62153
  /**
61922
62154
  * This plugin manage the autofill.
61923
62155
  *
@@ -63294,6 +63526,9 @@ class Session extends EventBus {
63294
63526
  }
63295
63527
  }
63296
63528
  this.acknowledge(message);
63529
+ if (message.type === "REMOTE_REVISION" && message.clientId === this.clientId) {
63530
+ return;
63531
+ }
63297
63532
  this.trigger("collaborative-event-received");
63298
63533
  }
63299
63534
  onClientMoved(message) {
@@ -63944,6 +64179,19 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
63944
64179
 
63945
64180
  class InsertPivotPlugin extends UIPlugin {
63946
64181
  static getters = [];
64182
+ allowDispatch(cmd) {
64183
+ switch (cmd.type) {
64184
+ case "DUPLICATE_PIVOT_IN_NEW_SHEET":
64185
+ if (!this.getters.isExistingPivot(cmd.pivotId)) {
64186
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
64187
+ }
64188
+ if (!this.getters.getPivot(cmd.pivotId).isValid()) {
64189
+ return "PivotInError" /* CommandResult.PivotInError */;
64190
+ }
64191
+ break;
64192
+ }
64193
+ return "Success" /* CommandResult.Success */;
64194
+ }
63947
64195
  handle(cmd) {
63948
64196
  switch (cmd.type) {
63949
64197
  case "INSERT_NEW_PIVOT":
@@ -64017,15 +64265,9 @@ class InsertPivotPlugin extends UIPlugin {
64017
64265
  }
64018
64266
  }
64019
64267
  getPivotDuplicateSheetName(pivotName) {
64020
- let i = 1;
64021
64268
  const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
64022
64269
  const sanitizedName = sanitizeSheetName(pivotName);
64023
- let name = sanitizedName;
64024
- while (names.includes(name)) {
64025
- name = `${sanitizedName} (${i})`;
64026
- i++;
64027
- }
64028
- return name;
64270
+ return getUniqueText(sanitizedName, names);
64029
64271
  }
64030
64272
  insertPivotWithTable(sheetId, col, row, pivotId, table, mode) {
64031
64273
  const { cols, rows, measures, fieldsType } = table;
@@ -66132,13 +66374,10 @@ class FilterEvaluationPlugin extends UIPlugin {
66132
66374
  if (!colName) {
66133
66375
  colName = `Column${colIndex}`;
66134
66376
  }
66135
- let currentColName = colName;
66136
- let i = 2;
66137
- while (usedColNames.includes(currentColName)) {
66138
- currentColName = colName + String(i);
66139
- i++;
66140
- }
66141
- return currentColName;
66377
+ return getUniqueText(colName, usedColNames, {
66378
+ compute: (name, i) => colName + String(i),
66379
+ start: 2,
66380
+ });
66142
66381
  }
66143
66382
  }
66144
66383
 
@@ -68180,11 +68419,6 @@ class BottomBarSheet extends owl.Component {
68180
68419
  editionState = "initializing";
68181
68420
  DOMFocusableElementStore;
68182
68421
  setup() {
68183
- owl.onMounted(() => {
68184
- if (this.isSheetActive) {
68185
- this.scrollToSheet();
68186
- }
68187
- });
68188
68422
  owl.onPatched(() => {
68189
68423
  if (this.sheetNameRef.el && this.state.isEditing && this.editionState === "initializing") {
68190
68424
  this.editionState = "editing";
@@ -68193,6 +68427,11 @@ class BottomBarSheet extends owl.Component {
68193
68427
  });
68194
68428
  this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
68195
68429
  owl.useExternalListener(window, "click", () => (this.state.pickerOpened = false));
68430
+ owl.useEffect((sheetId) => {
68431
+ if (this.props.sheetId === sheetId) {
68432
+ this.scrollToSheet();
68433
+ }
68434
+ }, () => [this.env.model.getters.getActiveSheetId()]);
68196
68435
  }
68197
68436
  focusInputAndSelectContent() {
68198
68437
  if (!this.state.isEditing || !this.sheetNameRef.el)
@@ -68204,7 +68443,10 @@ class BottomBarSheet extends owl.Component {
68204
68443
  }
68205
68444
  }
68206
68445
  scrollToSheet() {
68207
- this.sheetDivRef.el?.scrollIntoView?.();
68446
+ this.sheetDivRef.el?.scrollIntoView?.({
68447
+ behavior: "smooth",
68448
+ inline: "nearest",
68449
+ });
68208
68450
  }
68209
68451
  onFocusOut() {
68210
68452
  if (this.state.isEditing && this.editionState !== "initializing") {
@@ -68234,11 +68476,11 @@ class BottomBarSheet extends owl.Component {
68234
68476
  if (ev.key === "Enter") {
68235
68477
  ev.preventDefault();
68236
68478
  this.stopEdition();
68237
- this.DOMFocusableElementStore.focus();
68479
+ this.DOMFocusableElementStore.focusableElement?.focus();
68238
68480
  }
68239
68481
  if (ev.key === "Escape") {
68240
68482
  this.cancelEdition();
68241
- this.DOMFocusableElementStore.focus();
68483
+ this.DOMFocusableElementStore.focusableElement?.focus();
68242
68484
  }
68243
68485
  }
68244
68486
  onMouseEventSheetName(ev) {
@@ -68787,23 +69029,17 @@ class ClickableCellsStore extends SpreadsheetStore {
68787
69029
  const cells = [];
68788
69030
  const getters = this.getters;
68789
69031
  const sheetId = getters.getActiveSheetId();
68790
- for (const col of getters.getSheetViewVisibleCols()) {
68791
- for (const row of getters.getSheetViewVisibleRows()) {
68792
- const position = { sheetId, col, row };
68793
- if (!getters.isMainCellPosition(position)) {
68794
- continue;
68795
- }
68796
- const action = this.getClickableAction(position);
68797
- if (!action) {
68798
- continue;
68799
- }
68800
- const zone = getters.expandZone(sheetId, positionToZone(position));
68801
- cells.push({
68802
- coordinates: getters.getVisibleRect(zone),
68803
- position,
68804
- action,
68805
- });
69032
+ for (const position of this.getters.getVisibleCellPositions()) {
69033
+ const action = this.getClickableAction(position);
69034
+ if (!action) {
69035
+ continue;
68806
69036
  }
69037
+ const zone = getters.expandZone(sheetId, positionToZone(position));
69038
+ cells.push({
69039
+ coordinates: getters.getVisibleRect(zone),
69040
+ position,
69041
+ action,
69042
+ });
68807
69043
  }
68808
69044
  return cells;
68809
69045
  }
@@ -73614,7 +73850,7 @@ function addRows(construct, data, sheet) {
73614
73850
  if (content || styleId || formatId || borderId || value !== undefined) {
73615
73851
  const attributes = [["r", xc]];
73616
73852
  // style
73617
- const id = normalizeStyle(construct, extractStyle(data, styleId, formatId, borderId));
73853
+ const id = normalizeStyle(construct, extractStyle(data, content, styleId, formatId, borderId));
73618
73854
  // don't add style if default
73619
73855
  if (id) {
73620
73856
  attributes.push(["s", id]);
@@ -73968,7 +74204,12 @@ function createSharedStrings(strings) {
73968
74204
  ["count", strings.length],
73969
74205
  ["uniqueCount", strings.length],
73970
74206
  ];
73971
- const stringNodes = strings.map((string) => escapeXml /*xml*/ `<si><t>${string}</t></si>`);
74207
+ const stringNodes = strings.map((string) => {
74208
+ if (string.trim() !== string) {
74209
+ return escapeXml /*xml*/ `<si><t xml:space="preserve">${string}</t></si>`;
74210
+ }
74211
+ return escapeXml /*xml*/ `<si><t>${string}</t></si>`;
74212
+ });
73972
74213
  const xml = escapeXml /*xml*/ `
73973
74214
  <sst ${formatAttributes(namespaces)}>
73974
74215
  ${joinXmlNodes(stringNodes)}
@@ -74049,14 +74290,13 @@ function createRelRoot() {
74049
74290
  */
74050
74291
  function fixLengthySheetNames(data) {
74051
74292
  const nameMapping = {};
74052
- const newNames = new Set();
74293
+ const newNames = [];
74053
74294
  for (const sheet of data.sheets) {
74054
74295
  let newName = sheet.name.slice(0, 31);
74055
- let i = 1;
74056
- while (newNames.has(newName)) {
74057
- newName = newName.slice(0, 31 - String(i).length) + i++;
74058
- }
74059
- newNames.add(newName);
74296
+ newName = getUniqueText(newName, newNames, {
74297
+ compute: (name, i) => name.slice(0, 31 - String(i).length) + i,
74298
+ });
74299
+ newNames.push(newName);
74060
74300
  if (newName !== sheet.name) {
74061
74301
  nameMapping[sheet.name] = newName;
74062
74302
  sheet.name = newName;
@@ -74121,6 +74361,7 @@ class Model extends EventBus {
74121
74361
  */
74122
74362
  config;
74123
74363
  corePluginConfig;
74364
+ coreViewPluginConfig;
74124
74365
  uiPluginConfig;
74125
74366
  state;
74126
74367
  selection;
@@ -74173,6 +74414,7 @@ class Model extends EventBus {
74173
74414
  this.coreHandlers.push(this.range);
74174
74415
  this.handlers.push(this.range);
74175
74416
  this.corePluginConfig = this.setupCorePluginConfig();
74417
+ this.coreViewPluginConfig = this.setupCoreViewPluginConfig();
74176
74418
  this.uiPluginConfig = this.setupUiPluginConfig();
74177
74419
  // registering plugins
74178
74420
  for (let Plugin of corePluginRegistry.getAll()) {
@@ -74181,7 +74423,7 @@ class Model extends EventBus {
74181
74423
  Object.assign(this.getters, this.coreGetters);
74182
74424
  this.session.loadInitialMessages(stateUpdateMessages);
74183
74425
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
74184
- const plugin = this.setupUiPlugin(Plugin);
74426
+ const plugin = this.setupCoreViewPlugin(Plugin);
74185
74427
  this.handlers.push(plugin);
74186
74428
  this.uiHandlers.push(plugin);
74187
74429
  this.coreHandlers.push(plugin);
@@ -74208,7 +74450,7 @@ class Model extends EventBus {
74208
74450
  // events
74209
74451
  this.setupSessionEvents();
74210
74452
  this.joinSession();
74211
- if (config.snapshotRequested) {
74453
+ if (config.snapshotRequested || (data["[Content_Types].xml"] && !this.getters.isReadonly())) {
74212
74454
  const startSnapshot = performance.now();
74213
74455
  console.debug("Snapshot requested");
74214
74456
  this.session.snapshot(this.exportData());
@@ -74247,6 +74489,19 @@ class Model extends EventBus {
74247
74489
  }
74248
74490
  return plugin;
74249
74491
  }
74492
+ setupCoreViewPlugin(Plugin) {
74493
+ const plugin = new Plugin(this.coreViewPluginConfig);
74494
+ for (let name of Plugin.getters) {
74495
+ if (!(name in plugin)) {
74496
+ throw new Error(`Invalid getter name: ${name} for plugin ${plugin.constructor}`);
74497
+ }
74498
+ if (name in this.getters) {
74499
+ throw new Error(`Getter "${name}" is already defined.`);
74500
+ }
74501
+ this.getters[name] = plugin[name].bind(plugin);
74502
+ }
74503
+ return plugin;
74504
+ }
74250
74505
  /**
74251
74506
  * Initialize and properly configure a plugin.
74252
74507
  *
@@ -74348,6 +74603,20 @@ class Model extends EventBus {
74348
74603
  external: this.config.external,
74349
74604
  };
74350
74605
  }
74606
+ setupCoreViewPluginConfig() {
74607
+ return {
74608
+ getters: this.getters,
74609
+ stateObserver: this.state,
74610
+ selection: this.selection,
74611
+ moveClient: this.session.move.bind(this.session),
74612
+ custom: this.config.custom,
74613
+ uiActions: this.config,
74614
+ session: this.session,
74615
+ defaultCurrency: this.config.defaultCurrency,
74616
+ customColors: this.config.customColors || [],
74617
+ external: this.config.external,
74618
+ };
74619
+ }
74351
74620
  setupUiPluginConfig() {
74352
74621
  return {
74353
74622
  getters: this.getters,
@@ -74695,6 +74964,9 @@ const helpers = {
74695
74964
  areDomainArgsFieldsValid,
74696
74965
  splitReference,
74697
74966
  sanitizeSheetName,
74967
+ getUniqueText,
74968
+ isNumber,
74969
+ isDateTime,
74698
74970
  };
74699
74971
  const links = {
74700
74972
  isMarkdownLink,
@@ -74791,6 +75063,7 @@ exports.AbstractChart = AbstractChart;
74791
75063
  exports.AbstractFigureClipboardHandler = AbstractFigureClipboardHandler;
74792
75064
  exports.CellErrorType = CellErrorType;
74793
75065
  exports.CorePlugin = CorePlugin;
75066
+ exports.CoreViewPlugin = CoreViewPlugin;
74794
75067
  exports.DispatchResult = DispatchResult;
74795
75068
  exports.EvaluationError = EvaluationError;
74796
75069
  exports.Model = Model;
@@ -74833,6 +75106,6 @@ exports.tokenColors = tokenColors;
74833
75106
  exports.tokenize = tokenize;
74834
75107
 
74835
75108
 
74836
- __info__.version = "18.2.0-alpha.0";
74837
- __info__.date = "2024-12-26T08:02:39.123Z";
74838
- __info__.hash = "0f45915";
75109
+ __info__.version = "18.2.0-alpha.1";
75110
+ __info__.date = "2025-01-14T11:35:51.135Z";
75111
+ __info__.hash = "702f816";