@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,12 +2,12 @@
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
- import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
10
+ import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
11
11
 
12
12
  function createActions(menuItems) {
13
13
  return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
@@ -355,6 +355,7 @@ var ComponentsImportance;
355
355
  ComponentsImportance[ComponentsImportance["Popover"] = 35] = "Popover";
356
356
  ComponentsImportance[ComponentsImportance["FigureAnchor"] = 1000] = "FigureAnchor";
357
357
  ComponentsImportance[ComponentsImportance["FigureSnapLine"] = 1001] = "FigureSnapLine";
358
+ ComponentsImportance[ComponentsImportance["FigureTooltip"] = 1002] = "FigureTooltip";
358
359
  })(ComponentsImportance || (ComponentsImportance = {}));
359
360
  let DEFAULT_SHEETVIEW_SIZE = 0;
360
361
  function getDefaultSheetViewSize() {
@@ -974,6 +975,16 @@ function transpose2dPOJO(pojo) {
974
975
  }
975
976
  return result;
976
977
  }
978
+ function getUniqueText(text, texts, options = {}) {
979
+ const compute = options.compute ?? ((text, i) => `${text} (${i})`);
980
+ const computeFirstOne = options.computeFirstOne ?? false;
981
+ let i = options.start ?? 1;
982
+ let newText = computeFirstOne ? compute(text, i) : text;
983
+ while (texts.includes(newText)) {
984
+ newText = compute(text, i++);
985
+ }
986
+ return newText;
987
+ }
977
988
 
978
989
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
979
990
  const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
@@ -3709,6 +3720,7 @@ var CommandResult;
3709
3720
  CommandResult["SheetIsHidden"] = "SheetIsHidden";
3710
3721
  CommandResult["InvalidTableResize"] = "InvalidTableResize";
3711
3722
  CommandResult["PivotIdNotFound"] = "PivotIdNotFound";
3723
+ CommandResult["PivotInError"] = "PivotInError";
3712
3724
  CommandResult["EmptyName"] = "EmptyName";
3713
3725
  CommandResult["ValueCellIsInvalidFormula"] = "ValueCellIsInvalidFormula";
3714
3726
  CommandResult["InvalidDefinition"] = "InvalidDefinition";
@@ -6026,10 +6038,10 @@ class RangeImpl {
6026
6038
  }
6027
6039
  }
6028
6040
  /**
6029
- * Copy a range. If the range is on the sheetIdFrom, the range will target
6041
+ * Duplicate a range. If the range is on the sheetIdFrom, the range will target
6030
6042
  * sheetIdTo.
6031
6043
  */
6032
- function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
6044
+ function duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) {
6033
6045
  const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
6034
6046
  return range.clone({ sheetId });
6035
6047
  }
@@ -7477,18 +7489,18 @@ function predictLinearValues(Y, X, newX, computeIntercept) {
7477
7489
  });
7478
7490
  return newY.length === newX.length ? newY : transposeMatrix(newY);
7479
7491
  }
7480
- function getMovingAverageValues(dataset, windowSize = DEFAULT_WINDOW_SIZE) {
7492
+ function getMovingAverageValues(dataset, labels, windowSize = DEFAULT_WINDOW_SIZE) {
7481
7493
  const values = [];
7482
7494
  // Fill the starting values with null until we have a full window
7483
7495
  for (let i = 0; i < windowSize - 1; i++) {
7484
- values.push(null);
7496
+ values.push({ x: labels[i], y: NaN });
7485
7497
  }
7486
7498
  for (let i = 0; i <= dataset.length - windowSize; i++) {
7487
7499
  let sum = 0;
7488
7500
  for (let j = i; j < i + windowSize; j++) {
7489
7501
  sum += dataset[j];
7490
7502
  }
7491
- values.push(sum / windowSize);
7503
+ values.push({ x: labels[i + windowSize - 1], y: sum / windowSize });
7492
7504
  }
7493
7505
  return values;
7494
7506
  }
@@ -9462,6 +9474,150 @@ class ComposerFocusStore extends SpreadsheetStore {
9462
9474
  }
9463
9475
  }
9464
9476
 
9477
+ /**
9478
+ * This file is largely inspired by owl 1.
9479
+ * `css` tag has been removed from owl 2 without workaround to manage css.
9480
+ * So, the solution was to import the behavior of owl 1 directly in our
9481
+ * codebase, with one difference: the css is added to the sheet as soon as the
9482
+ * css tag is executed. In owl 1, the css was added as soon as a Component was
9483
+ * created for the first time.
9484
+ */
9485
+ const STYLESHEETS = {};
9486
+ let nextId = 0;
9487
+ /**
9488
+ * CSS tag helper for defining inline stylesheets. With this, one can simply define
9489
+ * an inline stylesheet with just the following code:
9490
+ * ```js
9491
+ * css`.component-a { color: red; }`;
9492
+ * ```
9493
+ */
9494
+ function css(strings, ...args) {
9495
+ const name = `__sheet__${nextId++}`;
9496
+ const value = String.raw(strings, ...args);
9497
+ registerSheet(name, value);
9498
+ activateSheet(name);
9499
+ return name;
9500
+ }
9501
+ function processSheet(str) {
9502
+ const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
9503
+ const selectorStack = [];
9504
+ const parts = [];
9505
+ let rules = [];
9506
+ function generateSelector(stackIndex, parentSelector) {
9507
+ const parts = [];
9508
+ for (const selector of selectorStack[stackIndex]) {
9509
+ let part = (parentSelector && parentSelector + " " + selector) || selector;
9510
+ if (part.includes("&")) {
9511
+ part = selector.replace(/&/g, parentSelector || "");
9512
+ }
9513
+ if (stackIndex < selectorStack.length - 1) {
9514
+ part = generateSelector(stackIndex + 1, part);
9515
+ }
9516
+ parts.push(part);
9517
+ }
9518
+ return parts.join(", ");
9519
+ }
9520
+ function generateRules() {
9521
+ if (rules.length) {
9522
+ parts.push(generateSelector(0) + " {");
9523
+ parts.push(...rules);
9524
+ parts.push("}");
9525
+ rules = [];
9526
+ }
9527
+ }
9528
+ while (tokens.length) {
9529
+ let token = tokens.shift();
9530
+ if (token === "}") {
9531
+ generateRules();
9532
+ selectorStack.pop();
9533
+ }
9534
+ else {
9535
+ if (tokens[0] === "{") {
9536
+ generateRules();
9537
+ selectorStack.push(token.split(/\s*,\s*/));
9538
+ tokens.shift();
9539
+ }
9540
+ if (tokens[0] === ";") {
9541
+ rules.push(" " + token + ";");
9542
+ }
9543
+ }
9544
+ }
9545
+ return parts.join("\n");
9546
+ }
9547
+ function registerSheet(id, css) {
9548
+ const sheet = document.createElement("style");
9549
+ sheet.textContent = processSheet(css);
9550
+ STYLESHEETS[id] = sheet;
9551
+ }
9552
+ function activateSheet(id) {
9553
+ const sheet = STYLESHEETS[id];
9554
+ sheet.setAttribute("component", id);
9555
+ document.head.appendChild(sheet);
9556
+ }
9557
+ function getTextDecoration({ strikethrough, underline, }) {
9558
+ if (!strikethrough && !underline) {
9559
+ return "none";
9560
+ }
9561
+ return `${strikethrough ? "line-through" : ""} ${underline ? "underline" : ""}`;
9562
+ }
9563
+ /**
9564
+ * Convert the cell style to CSS properties.
9565
+ */
9566
+ function cellStyleToCss(style) {
9567
+ const attributes = cellTextStyleToCss(style);
9568
+ if (!style)
9569
+ return attributes;
9570
+ if (style.fillColor) {
9571
+ attributes["background"] = style.fillColor;
9572
+ }
9573
+ return attributes;
9574
+ }
9575
+ /**
9576
+ * Convert the cell text style to CSS properties.
9577
+ */
9578
+ function cellTextStyleToCss(style) {
9579
+ const attributes = {};
9580
+ if (!style)
9581
+ return attributes;
9582
+ if (style.bold) {
9583
+ attributes["font-weight"] = "bold";
9584
+ }
9585
+ if (style.italic) {
9586
+ attributes["font-style"] = "italic";
9587
+ }
9588
+ if (style.strikethrough || style.underline) {
9589
+ let decoration = style.strikethrough ? "line-through" : "";
9590
+ decoration = style.underline ? decoration + " underline" : decoration;
9591
+ attributes["text-decoration"] = decoration;
9592
+ }
9593
+ if (style.textColor) {
9594
+ attributes["color"] = style.textColor;
9595
+ }
9596
+ return attributes;
9597
+ }
9598
+ /**
9599
+ * Transform CSS properties into a CSS string.
9600
+ */
9601
+ function cssPropertiesToCss(attributes) {
9602
+ let styleStr = "";
9603
+ for (const attName in attributes) {
9604
+ if (!attributes[attName]) {
9605
+ continue;
9606
+ }
9607
+ styleStr += `${attName}:${attributes[attName]}; `;
9608
+ }
9609
+ return styleStr;
9610
+ }
9611
+ function getElementMargins(el) {
9612
+ const style = window.getComputedStyle(el);
9613
+ return {
9614
+ top: parseInt(style.marginTop, 10) || 0,
9615
+ bottom: parseInt(style.marginBottom, 10) || 0,
9616
+ left: parseInt(style.marginLeft, 10) || 0,
9617
+ right: parseInt(style.marginRight, 10) || 0,
9618
+ };
9619
+ }
9620
+
9465
9621
  const TREND_LINE_XAXIS_ID = "x1";
9466
9622
  /**
9467
9623
  * This file contains helpers that are common to different charts (mainly
@@ -9514,25 +9670,25 @@ function updateChartRangesWithDataSets(getters, applyChange, chartDataSets, char
9514
9670
  };
9515
9671
  }
9516
9672
  /**
9517
- * Copy the dataSets given. All the ranges which are on sheetIdFrom will target
9673
+ * Duplicate the dataSets. All ranges on sheetIdFrom are adapted to target
9518
9674
  * sheetIdTo.
9519
9675
  */
9520
- function copyDataSetsWithNewSheetId(sheetIdFrom, sheetIdTo, dataSets) {
9676
+ function duplicateDataSetsInDuplicatedSheet(sheetIdFrom, sheetIdTo, dataSets) {
9521
9677
  return dataSets.map((ds) => {
9522
9678
  return {
9523
- dataRange: copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.dataRange),
9679
+ dataRange: duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, ds.dataRange),
9524
9680
  labelCell: ds.labelCell
9525
- ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.labelCell)
9681
+ ? duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, ds.labelCell)
9526
9682
  : undefined,
9527
9683
  };
9528
9684
  });
9529
9685
  }
9530
9686
  /**
9531
- * Copy a range. If the range is on the sheetIdFrom, the range will target
9687
+ * Duplicate a range. If the range is on the sheetIdFrom, the range will target
9532
9688
  * sheetIdTo.
9533
9689
  */
9534
- function copyLabelRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
9535
- return range ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) : undefined;
9690
+ function duplicateLabelRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) {
9691
+ return range ? duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) : undefined;
9536
9692
  }
9537
9693
  /**
9538
9694
  * Adapt a single range of a chart
@@ -9785,10 +9941,11 @@ function formatTickValue(localeFormat) {
9785
9941
  if (isNaN(value))
9786
9942
  return value;
9787
9943
  const { locale, format } = localeFormat;
9788
- return formatValue(value, {
9944
+ const formattedValue = formatValue(value, {
9789
9945
  locale,
9790
9946
  format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
9791
9947
  });
9948
+ return truncateLabel(formattedValue);
9792
9949
  };
9793
9950
  }
9794
9951
  const CHART_AXIS_CHOICES = [
@@ -9803,6 +9960,15 @@ function getPieColors(colors, dataSetsValues) {
9803
9960
  }
9804
9961
  return pieColors;
9805
9962
  }
9963
+ function truncateLabel(label) {
9964
+ if (!label) {
9965
+ return "";
9966
+ }
9967
+ if (label.length > MAX_CHAR_LABEL) {
9968
+ return label.substring(0, MAX_CHAR_LABEL) + "…";
9969
+ }
9970
+ return label;
9971
+ }
9806
9972
 
9807
9973
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9808
9974
  const chartShowValuesPlugin = {
@@ -9998,6 +10164,18 @@ function getNextNonEmptyBar(bars, startIndex) {
9998
10164
 
9999
10165
  window.Chart?.register(waterfallLinesPlugin);
10000
10166
  window.Chart?.register(chartShowValuesPlugin);
10167
+ css /* scss */ `
10168
+ .o-spreadsheet {
10169
+ .o-chart-custom-tooltip {
10170
+ font-size: 12px;
10171
+ background-color: #fff;
10172
+ z-index: ${ComponentsImportance.FigureTooltip};
10173
+ table td span {
10174
+ box-sizing: border-box;
10175
+ }
10176
+ }
10177
+ }
10178
+ `;
10001
10179
  class ChartJsComponent extends Component {
10002
10180
  static template = "o-spreadsheet-ChartJsComponent";
10003
10181
  static props = {
@@ -10236,11 +10414,11 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
10236
10414
  keyValue: keyValueZone ? zoneToXc(keyValueZone) : undefined,
10237
10415
  };
10238
10416
  }
10239
- copyForSheetId(sheetId) {
10240
- const baseline = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.baseline);
10241
- const keyValue = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.keyValue);
10242
- const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, sheetId);
10243
- return new ScorecardChart(definition, sheetId, this.getters);
10417
+ duplicateInDuplicatedSheet(newSheetId) {
10418
+ const baseline = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.baseline);
10419
+ const keyValue = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.keyValue);
10420
+ const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, newSheetId);
10421
+ return new ScorecardChart(definition, newSheetId, this.getters);
10244
10422
  }
10245
10423
  copyInSheetId(sheetId) {
10246
10424
  const definition = this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue, sheetId);
@@ -19930,6 +20108,17 @@ const TEXT = {
19930
20108
  },
19931
20109
  isExported: true,
19932
20110
  };
20111
+ // -----------------------------------------------------------------------------
20112
+ // VALUE
20113
+ // -----------------------------------------------------------------------------
20114
+ const VALUE = {
20115
+ description: _t("Converts a string to a numeric value."),
20116
+ args: [arg("value (number)", _t("the string to be converted"))],
20117
+ compute: function (value) {
20118
+ return toNumber(value, this.locale);
20119
+ },
20120
+ isExported: true,
20121
+ };
19933
20122
 
19934
20123
  var text = /*#__PURE__*/Object.freeze({
19935
20124
  __proto__: null,
@@ -19952,7 +20141,8 @@ var text = /*#__PURE__*/Object.freeze({
19952
20141
  TEXT: TEXT,
19953
20142
  TEXTJOIN: TEXTJOIN,
19954
20143
  TRIM: TRIM,
19955
- UPPER: UPPER
20144
+ UPPER: UPPER,
20145
+ VALUE: VALUE
19956
20146
  });
19957
20147
 
19958
20148
  // -----------------------------------------------------------------------------
@@ -22555,15 +22745,6 @@ const CHART_COMMON_OPTIONS = {
22555
22745
  },
22556
22746
  animation: false,
22557
22747
  };
22558
- function truncateLabel(label) {
22559
- if (!label) {
22560
- return "";
22561
- }
22562
- if (label.length > MAX_CHAR_LABEL) {
22563
- return label.substring(0, MAX_CHAR_LABEL) + "…";
22564
- }
22565
- return label;
22566
- }
22567
22748
  function chartToImage(runtime, figure, type) {
22568
22749
  // wrap the canvas in a div with a fixed size because chart.js would
22569
22750
  // fill the whole page otherwise
@@ -22885,150 +23066,6 @@ const CONTENT_TYPES_FILE = "[Content_Types].xml";
22885
23066
  */
22886
23067
  const iconsOnCellRegistry = new Registry();
22887
23068
 
22888
- /**
22889
- * This file is largely inspired by owl 1.
22890
- * `css` tag has been removed from owl 2 without workaround to manage css.
22891
- * So, the solution was to import the behavior of owl 1 directly in our
22892
- * codebase, with one difference: the css is added to the sheet as soon as the
22893
- * css tag is executed. In owl 1, the css was added as soon as a Component was
22894
- * created for the first time.
22895
- */
22896
- const STYLESHEETS = {};
22897
- let nextId = 0;
22898
- /**
22899
- * CSS tag helper for defining inline stylesheets. With this, one can simply define
22900
- * an inline stylesheet with just the following code:
22901
- * ```js
22902
- * css`.component-a { color: red; }`;
22903
- * ```
22904
- */
22905
- function css(strings, ...args) {
22906
- const name = `__sheet__${nextId++}`;
22907
- const value = String.raw(strings, ...args);
22908
- registerSheet(name, value);
22909
- activateSheet(name);
22910
- return name;
22911
- }
22912
- function processSheet(str) {
22913
- const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
22914
- const selectorStack = [];
22915
- const parts = [];
22916
- let rules = [];
22917
- function generateSelector(stackIndex, parentSelector) {
22918
- const parts = [];
22919
- for (const selector of selectorStack[stackIndex]) {
22920
- let part = (parentSelector && parentSelector + " " + selector) || selector;
22921
- if (part.includes("&")) {
22922
- part = selector.replace(/&/g, parentSelector || "");
22923
- }
22924
- if (stackIndex < selectorStack.length - 1) {
22925
- part = generateSelector(stackIndex + 1, part);
22926
- }
22927
- parts.push(part);
22928
- }
22929
- return parts.join(", ");
22930
- }
22931
- function generateRules() {
22932
- if (rules.length) {
22933
- parts.push(generateSelector(0) + " {");
22934
- parts.push(...rules);
22935
- parts.push("}");
22936
- rules = [];
22937
- }
22938
- }
22939
- while (tokens.length) {
22940
- let token = tokens.shift();
22941
- if (token === "}") {
22942
- generateRules();
22943
- selectorStack.pop();
22944
- }
22945
- else {
22946
- if (tokens[0] === "{") {
22947
- generateRules();
22948
- selectorStack.push(token.split(/\s*,\s*/));
22949
- tokens.shift();
22950
- }
22951
- if (tokens[0] === ";") {
22952
- rules.push(" " + token + ";");
22953
- }
22954
- }
22955
- }
22956
- return parts.join("\n");
22957
- }
22958
- function registerSheet(id, css) {
22959
- const sheet = document.createElement("style");
22960
- sheet.textContent = processSheet(css);
22961
- STYLESHEETS[id] = sheet;
22962
- }
22963
- function activateSheet(id) {
22964
- const sheet = STYLESHEETS[id];
22965
- sheet.setAttribute("component", id);
22966
- document.head.appendChild(sheet);
22967
- }
22968
- function getTextDecoration({ strikethrough, underline, }) {
22969
- if (!strikethrough && !underline) {
22970
- return "none";
22971
- }
22972
- return `${strikethrough ? "line-through" : ""} ${underline ? "underline" : ""}`;
22973
- }
22974
- /**
22975
- * Convert the cell style to CSS properties.
22976
- */
22977
- function cellStyleToCss(style) {
22978
- const attributes = cellTextStyleToCss(style);
22979
- if (!style)
22980
- return attributes;
22981
- if (style.fillColor) {
22982
- attributes["background"] = style.fillColor;
22983
- }
22984
- return attributes;
22985
- }
22986
- /**
22987
- * Convert the cell text style to CSS properties.
22988
- */
22989
- function cellTextStyleToCss(style) {
22990
- const attributes = {};
22991
- if (!style)
22992
- return attributes;
22993
- if (style.bold) {
22994
- attributes["font-weight"] = "bold";
22995
- }
22996
- if (style.italic) {
22997
- attributes["font-style"] = "italic";
22998
- }
22999
- if (style.strikethrough || style.underline) {
23000
- let decoration = style.strikethrough ? "line-through" : "";
23001
- decoration = style.underline ? decoration + " underline" : decoration;
23002
- attributes["text-decoration"] = decoration;
23003
- }
23004
- if (style.textColor) {
23005
- attributes["color"] = style.textColor;
23006
- }
23007
- return attributes;
23008
- }
23009
- /**
23010
- * Transform CSS properties into a CSS string.
23011
- */
23012
- function cssPropertiesToCss(attributes) {
23013
- let styleStr = "";
23014
- for (const attName in attributes) {
23015
- if (!attributes[attName]) {
23016
- continue;
23017
- }
23018
- styleStr += `${attName}:${attributes[attName]}; `;
23019
- }
23020
- return styleStr;
23021
- }
23022
- function getElementMargins(el) {
23023
- const style = window.getComputedStyle(el);
23024
- return {
23025
- top: parseInt(style.marginTop, 10) || 0,
23026
- bottom: parseInt(style.marginBottom, 10) || 0,
23027
- left: parseInt(style.marginLeft, 10) || 0,
23028
- right: parseInt(style.marginRight, 10) || 0,
23029
- };
23030
- }
23031
-
23032
23069
  css /* scss */ `
23033
23070
  .o-spreadsheet {
23034
23071
  .o-icon {
@@ -24140,7 +24177,7 @@ function convertWidthFromExcel(width) {
24140
24177
  return width;
24141
24178
  return Math.round((width / WIDTH_FACTOR) * 100) / 100;
24142
24179
  }
24143
- function extractStyle(data, styleId, formatId, borderId) {
24180
+ function extractStyle(data, content, styleId, formatId, borderId) {
24144
24181
  const style = styleId ? data.styles[styleId] : {};
24145
24182
  const format = formatId ? data.formats[formatId] : undefined;
24146
24183
  const styles = {
@@ -24162,7 +24199,7 @@ function extractStyle(data, styleId, formatId, borderId) {
24162
24199
  vertical: style.verticalAlign
24163
24200
  ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
24164
24201
  : undefined,
24165
- wrapText: style.wrapping === "wrap" || undefined,
24202
+ wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
24166
24203
  },
24167
24204
  };
24168
24205
  styles.font["strike"] = !!style?.strikethrough || undefined;
@@ -24393,7 +24430,7 @@ function convertFigure(figure, id, sheetData) {
24393
24430
  return undefined;
24394
24431
  }
24395
24432
  function isChartData(data) {
24396
- return "dataSets" in data;
24433
+ return "dataSets" in data && data.dataSets.length > 0;
24397
24434
  }
24398
24435
  function isImageData(data) {
24399
24436
  return "imageSrc" in data;
@@ -24739,9 +24776,8 @@ function convertRows(sheet, numberOfRows, headerGroups) {
24739
24776
  }
24740
24777
  return rows;
24741
24778
  }
24742
- /** Remove newlines (\n) in shared strings, We do not support them */
24743
24779
  function convertSharedStrings(xlsxSharedStrings) {
24744
- return xlsxSharedStrings.map((str) => str.replace(/\n/g, ""));
24780
+ return xlsxSharedStrings.map(replaceNewLines);
24745
24781
  }
24746
24782
  function convertCells(sheet, data, sheetDims, warningManager) {
24747
24783
  const cells = {};
@@ -25829,15 +25865,10 @@ class XlsxMiscExtractor extends XlsxBaseExtractor {
25829
25865
  getSharedStrings() {
25830
25866
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "si" }, (ssElement) => {
25831
25867
  // Shared string can either be a simple text, or a rich text (text with formatting, possibly in multiple parts)
25832
- if (ssElement.children[0].tagName === "t") {
25833
- return this.extractTextContent(ssElement) || "";
25834
- }
25835
25868
  // We don't support rich text formatting, we'll only extract the text
25836
- else {
25837
- return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25838
- return this.extractTextContent(textElement) || "";
25839
- }).join("");
25840
- }
25869
+ return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25870
+ return this.extractTextContent(textElement) || "";
25871
+ }).join("");
25841
25872
  });
25842
25873
  }
25843
25874
  }
@@ -27154,12 +27185,9 @@ migrationStepRegistry
27154
27185
  }
27155
27186
  const oldName = sheet.name;
27156
27187
  const escapedName = sanitizeSheetName(oldName, "_");
27157
- let i = 1;
27158
- let newName = escapedName;
27159
- while (namesTaken.includes(newName)) {
27160
- newName = `${escapedName}${i}`;
27161
- i++;
27162
- }
27188
+ const newName = getUniqueText(escapedName, namesTaken, {
27189
+ compute: (name, i) => `${name}${i}`,
27190
+ });
27163
27191
  sheet.name = newName;
27164
27192
  namesTaken.push(newName);
27165
27193
  const replaceName = (str) => {
@@ -27478,6 +27506,13 @@ migrationStepRegistry
27478
27506
  }
27479
27507
  return data;
27480
27508
  },
27509
+ })
27510
+ .add("migration_24", {
27511
+ // Empty migration to allow odoo migrate pivot custom sorting.
27512
+ versionFrom: "24",
27513
+ migrate(data) {
27514
+ return data;
27515
+ },
27481
27516
  });
27482
27517
  function fixOverlappingFilters(data) {
27483
27518
  for (let sheet of data.sheets || []) {
@@ -27505,7 +27540,7 @@ function fixOverlappingFilters(data) {
27505
27540
  * a breaking change is made in the way the state is handled, and an upgrade
27506
27541
  * function should be defined
27507
27542
  */
27508
- const CURRENT_VERSION = 24;
27543
+ const CURRENT_VERSION = 25;
27509
27544
  const INITIAL_SHEET_ID = "Sheet1";
27510
27545
  /**
27511
27546
  * This function tries to load anything that could look like a valid
@@ -28406,12 +28441,12 @@ function getTrendDatasetForLineChart(config, data, labels, axisType, locale) {
28406
28441
  }
28407
28442
  const numberOfStep = 5 * trendLabels.length;
28408
28443
  const step = (xmax - xmin) / numberOfStep;
28409
- const newLabels = range(xmin, xmax + step / 2, step);
28410
- const newValues = interpolateData(config, filteredValues, filteredLabels, newLabels);
28411
- if (!newValues.length) {
28444
+ const trendNewLabels = range(xmin, xmax + step / 2, step);
28445
+ const trendValues = interpolateData(config, filteredValues, filteredLabels, trendNewLabels);
28446
+ if (!trendValues.length) {
28412
28447
  return;
28413
28448
  }
28414
- return newValues;
28449
+ return trendValues;
28415
28450
  }
28416
28451
  function interpolateData(config, values, labels, newLabels) {
28417
28452
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
@@ -28427,13 +28462,16 @@ function interpolateData(config, values, labels, newLabels) {
28427
28462
  case "polynomial": {
28428
28463
  const order = config.order;
28429
28464
  if (!order) {
28430
- return Array.from({ length: newLabels.length }, () => NaN);
28465
+ return newLabels.map((x) => ({ x, y: NaN }));
28431
28466
  }
28432
28467
  if (order === 1) {
28433
- return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0];
28468
+ return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0].map((y, i) => ({ x: newLabels[i], y }));
28434
28469
  }
28435
28470
  const coeffs = polynomialRegression(values, normalizedLabels, order, true).flat();
28436
- return normalizedNewLabels.map((v) => evaluatePolynomial(coeffs, v, order));
28471
+ return normalizedNewLabels.map((x, i) => ({
28472
+ x: newLabels[i],
28473
+ y: evaluatePolynomial(coeffs, x, order),
28474
+ }));
28437
28475
  }
28438
28476
  case "exponential": {
28439
28477
  const positiveLogValues = [];
@@ -28445,22 +28483,22 @@ function interpolateData(config, values, labels, newLabels) {
28445
28483
  }
28446
28484
  }
28447
28485
  if (!filteredLabels.length) {
28448
- return Array.from({ length: newLabels.length }, () => NaN);
28486
+ return newLabels.map((x) => ({ x, y: NaN }));
28449
28487
  }
28450
- return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0];
28488
+ return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0].map((y, i) => ({ x: newLabels[i], y }));
28451
28489
  }
28452
28490
  case "logarithmic": {
28453
- return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0];
28491
+ return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0].map((y, i) => ({ x: newLabels[i], y }));
28454
28492
  }
28455
28493
  case "trailingMovingAverage": {
28456
- return getMovingAverageValues(values, config.window);
28494
+ return getMovingAverageValues(values, labels, config.window);
28457
28495
  }
28458
28496
  default:
28459
- return Array.from({ length: newLabels.length }, () => NaN);
28497
+ return newLabels.map((x) => ({ x, y: NaN }));
28460
28498
  }
28461
28499
  }
28462
28500
  catch (e) {
28463
- return Array.from({ length: newLabels.length }, () => NaN);
28501
+ return newLabels.map((x) => ({ x, y: NaN }));
28464
28502
  }
28465
28503
  }
28466
28504
  function getChartAxisType(chart, labelRange, getters) {
@@ -28682,7 +28720,7 @@ function getChartDatasetValues(getters, dataSets) {
28682
28720
  : undefined;
28683
28721
  label =
28684
28722
  cell && labelRange
28685
- ? truncateLabel(cell.formattedValue)
28723
+ ? cell.formattedValue
28686
28724
  : (label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`);
28687
28725
  }
28688
28726
  else {
@@ -28696,7 +28734,7 @@ function getChartDatasetValues(getters, dataSets) {
28696
28734
  // then using the classical aggregation method to sum the values.
28697
28735
  data.fill(1);
28698
28736
  }
28699
- else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), getters.getLocale()))) {
28737
+ else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28700
28738
  continue;
28701
28739
  }
28702
28740
  datasetValues.push({ data, label });
@@ -28772,7 +28810,7 @@ function getWaterfallDatasetAndLabels(definition, args) {
28772
28810
  }
28773
28811
  return {
28774
28812
  datasets: [dataset],
28775
- labels: labelsWithSubTotals.map(truncateLabel),
28813
+ labels: labelsWithSubTotals,
28776
28814
  };
28777
28815
  }
28778
28816
  function getLineChartDatasets(definition, args) {
@@ -29021,7 +29059,7 @@ function getPieChartLegend(definition, args) {
29021
29059
  generateLabels: (c) =>
29022
29060
  //@ts-ignore
29023
29061
  c.data.labels.map((label, index) => ({
29024
- text: label,
29062
+ text: truncateLabel(String(label)),
29025
29063
  strokeStyle: colors[index],
29026
29064
  fillStyle: colors[index],
29027
29065
  pointStyle: "rect",
@@ -29090,6 +29128,7 @@ function getWaterfallChartLegend(definition, args) {
29090
29128
  return legendValues;
29091
29129
  },
29092
29130
  },
29131
+ onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29093
29132
  };
29094
29133
  }
29095
29134
  function getRadarChartLegend(definition, args) {
@@ -29151,7 +29190,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29151
29190
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29152
29191
  if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29153
29192
  return {
29154
- text: dataset.label ?? "",
29193
+ text: truncateLabel(dataset.label),
29155
29194
  fontColor,
29156
29195
  strokeStyle: dataset.borderColor,
29157
29196
  hidden: !chart.isDatasetVisible(index),
@@ -29161,7 +29200,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29161
29200
  };
29162
29201
  }
29163
29202
  return {
29164
- text: dataset.label ?? "",
29203
+ text: truncateLabel(dataset.label),
29165
29204
  fontColor,
29166
29205
  strokeStyle: dataset.borderColor,
29167
29206
  fillStyle: dataset.backgroundColor,
@@ -29231,14 +29270,19 @@ function getLineChartScales(definition, args) {
29231
29270
  /* We add a second x axis here to draw the trend lines, with the labels length being
29232
29271
  * set so that the second axis points match the classical x axis
29233
29272
  */
29234
- const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset?.length || 0));
29235
29273
  scales[TREND_LINE_XAXIS_ID] = {
29236
29274
  ...scales.x,
29237
- type: "category",
29238
- labels: range(0, maxLength).map((x) => x.toString()),
29239
- offset: false,
29240
29275
  display: false,
29241
29276
  };
29277
+ if (axisType === "category" || axisType === "time") {
29278
+ /* We add a second x axis here to draw the trend lines, with the labels length being
29279
+ * set so that the second axis points match the classical x axis
29280
+ */
29281
+ const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset?.length || 0));
29282
+ scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29283
+ scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29284
+ scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29285
+ }
29242
29286
  }
29243
29287
  return scales;
29244
29288
  }
@@ -29303,7 +29347,10 @@ function getRadarChartScales(definition, args) {
29303
29347
  callback: formatTickValue({ format: axisFormats?.r, locale }),
29304
29348
  backdropColor: definition.background || "#FFFFFF",
29305
29349
  },
29306
- pointLabels: { color: chartFontColor(definition.background) },
29350
+ pointLabels: {
29351
+ color: chartFontColor(definition.background),
29352
+ callback: truncateLabel,
29353
+ },
29307
29354
  suggestedMin: minValue < 0 ? minValue - 1 : 0,
29308
29355
  },
29309
29356
  };
@@ -29400,6 +29447,11 @@ function getChartAxis(definition, position, type, options) {
29400
29447
  ticks: {
29401
29448
  padding: 5,
29402
29449
  color: fontColor,
29450
+ callback: function (tickValue) {
29451
+ // Category axis callback's internal tick value is the index of the label
29452
+ // https://www.chartjs.org/docs/latest/axes/labelling.html#creating-custom-tick-formats
29453
+ return truncateLabel(this.getLabelForValue(tickValue));
29454
+ },
29403
29455
  },
29404
29456
  grid: {
29405
29457
  display: false,
@@ -29479,8 +29531,70 @@ function getChartTitle(definition) {
29479
29531
  };
29480
29532
  }
29481
29533
 
29534
+ /**
29535
+ * Custom tooltip for the charts. Mostly copied from Odoo's custom tooltip, with some slight changes to make it work
29536
+ * with o-spreadsheet chart data and CSS.
29537
+ *
29538
+ * https://github.com/odoo/odoo/blob/18.0/addons/web/static/src/views/graph/graph_renderer.xml
29539
+ */
29540
+ const templates = /* xml */ `
29541
+ <templates>
29542
+ <t t-name="o-spreadsheet-CustomTooltip">
29543
+ <div
29544
+ class="o-chart-custom-tooltip border rounded px-2 py-1 pe-none mw-100 position-absolute text-nowrap shadow opacity-100">
29545
+ <table class="overflow-hidden m-0">
29546
+ <thead>
29547
+ <tr>
29548
+ <th class="o-tooltip-title align-baseline border-0 text-truncate" t-esc="title" t-attf-style="max-width: {{ labelsMaxWidth }}"/>
29549
+ </tr>
29550
+ </thead>
29551
+ <tbody>
29552
+ <tr t-foreach="tooltipItems" t-as="tooltipItem" t-key="tooltipItem_index">
29553
+ <td>
29554
+ <span
29555
+ class="badge ps-2 py-2 rounded-0 align-middle"
29556
+ t-attf-style="background-color: {{ tooltipItem.boxColor }}"
29557
+ > </span>
29558
+ <small
29559
+ t-if="tooltipItem.label"
29560
+ class="o-tooltip-label d-inline-block text-truncate align-middle smaller ms-2"
29561
+ t-esc="tooltipItem.label"
29562
+ t-attf-style="max-width: {{ labelsMaxWidth }}"
29563
+ />
29564
+ </td>
29565
+ <td class="o-tooltip-value ps-2 fw-bolder text-end">
29566
+ <small class="smaller d-inline-block text-truncate align-middle" t-attf-style="max-width: {{ valuesMaxWidth }}">
29567
+ <t t-esc="tooltipItem.value"/>
29568
+ <t t-if="tooltipItem.percentage">
29569
+ (
29570
+ <t t-esc="tooltipItem.percentage"/>
29571
+ %)
29572
+ </t>
29573
+ </small>
29574
+ </td>
29575
+ </tr>
29576
+ </tbody>
29577
+ </table>
29578
+ </div>
29579
+ </t>
29580
+ </templates>
29581
+ `;
29582
+ const app = new App(Component, { templates, translateFn: _t });
29583
+ function renderToString(templateName, context = {}) {
29584
+ return render(templateName, context).innerHTML;
29585
+ }
29586
+ function render(templateName, context = {}) {
29587
+ const templateFn = app.getTemplate(templateName);
29588
+ const bdom = templateFn(context, {});
29589
+ const div = document.createElement("div");
29590
+ blockDom.mount(bdom, div);
29591
+ return div;
29592
+ }
29593
+
29482
29594
  function getBarChartTooltip(definition, args) {
29483
29595
  return {
29596
+ enabled: false,
29597
+ external: customTooltipHandler,
29484
29598
  callbacks: {
29485
29599
  title: function (tooltipItems) {
29486
29600
  return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
@@ -29504,11 +29618,17 @@ function getBarChartTooltip(definition, args) {
29504
29618
  function getLineChartTooltip(definition, args) {
29505
29619
  const { axisType, locale, axisFormats } = args;
29506
29620
  const labelFormat = axisFormats?.x;
29507
- const tooltip = { callbacks: {} };
29621
+ const tooltip = {
29622
+ enabled: false,
29623
+ external: customTooltipHandler,
29624
+ callbacks: {},
29625
+ };
29508
29626
  if (axisType === "linear") {
29509
29627
  tooltip.callbacks.label = (tooltipItem) => {
29510
29628
  const dataSetPoint = tooltipItem.parsed.y;
29511
- let label = tooltipItem.parsed.x;
29629
+ let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29630
+ ? ""
29631
+ : tooltipItem.parsed.x;
29512
29632
  if (typeof label === "string" && isNumber(label, locale)) {
29513
29633
  label = toNumber(label, locale);
29514
29634
  }
@@ -29541,6 +29661,8 @@ function getPieChartTooltip(definition, args) {
29541
29661
  const { locale, axisFormats } = args;
29542
29662
  const format = axisFormats?.y || axisFormats?.y1;
29543
29663
  return {
29664
+ enabled: false,
29665
+ external: customTooltipHandler,
29544
29666
  callbacks: {
29545
29667
  title: function (tooltipItems) {
29546
29668
  return tooltipItems[0].dataset.label;
@@ -29565,6 +29687,8 @@ function getWaterfallChartTooltip(definition, args) {
29565
29687
  const format = axisFormats?.y || axisFormats?.y1;
29566
29688
  const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
29567
29689
  return {
29690
+ enabled: false,
29691
+ external: customTooltipHandler,
29568
29692
  callbacks: {
29569
29693
  label: function (tooltipItem) {
29570
29694
  const [lastValue, currentValue] = tooltipItem.raw;
@@ -29596,6 +29720,8 @@ function getPyramidChartTooltip(definition, args) {
29596
29720
  function getRadarChartTooltip(definition, args) {
29597
29721
  const { locale, axisFormats } = args;
29598
29722
  return {
29723
+ enabled: false,
29724
+ external: customTooltipHandler,
29599
29725
  callbacks: {
29600
29726
  label: function (tooltipItem) {
29601
29727
  const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
@@ -29634,6 +29760,48 @@ function calculatePercentage(dataset, dataIndex) {
29634
29760
  const percentage = (dataset[dataIndex] / total) * 100;
29635
29761
  return percentage.toFixed(2);
29636
29762
  }
29763
+ function customTooltipHandler({ chart, tooltip }) {
29764
+ chart.canvas.parentNode.querySelector("div.o-chart-custom-tooltip")?.remove();
29765
+ if (tooltip.opacity === 0 || tooltip.dataPoints.length === 0) {
29766
+ return;
29767
+ }
29768
+ const tooltipItems = tooltip.body.map((body, index) => {
29769
+ let [label, value] = body.lines[0].split(":").map((str) => str.trim());
29770
+ if (!value) {
29771
+ value = label;
29772
+ label = "";
29773
+ }
29774
+ const color = tooltip.labelColors[index].backgroundColor;
29775
+ return {
29776
+ label,
29777
+ value,
29778
+ boxColor: typeof color === "string" ? setColorAlpha(color, 1) : color,
29779
+ };
29780
+ });
29781
+ const innerHTML = renderToString("o-spreadsheet-CustomTooltip", {
29782
+ labelsMaxWidth: Math.floor(chart.canvas.clientWidth * 0.5) + "px",
29783
+ valuesMaxWidth: Math.floor(chart.canvas.clientWidth * 0.25) + "px",
29784
+ title: tooltip.title[0],
29785
+ tooltipItems,
29786
+ });
29787
+ const template = Object.assign(document.createElement("template"), { innerHTML });
29788
+ const newTooltipEl = template.content.firstChild;
29789
+ chart.canvas.parentNode?.appendChild(newTooltipEl);
29790
+ Object.assign(newTooltipEl.style, {
29791
+ left: getTooltipLeftPosition(chart, tooltip, newTooltipEl.clientWidth) + "px",
29792
+ top: Math.floor(tooltip.caretY - newTooltipEl.clientHeight / 2) + "px",
29793
+ });
29794
+ }
29795
+ /**
29796
+ * Get the left position for the tooltip, making sure it doesn't go out of the chart area.
29797
+ */
29798
+ function getTooltipLeftPosition(chart, tooltip, tooltipWidth) {
29799
+ const x = tooltip.caretX;
29800
+ if (x + tooltipWidth > chart.chartArea.right) {
29801
+ return Math.max(0, x - tooltipWidth);
29802
+ }
29803
+ return x;
29804
+ }
29637
29805
 
29638
29806
  var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
29639
29807
  __proto__: null,
@@ -29747,11 +29915,11 @@ class BarChart extends AbstractChart {
29747
29915
  : undefined,
29748
29916
  };
29749
29917
  }
29750
- copyForSheetId(sheetId) {
29751
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
29752
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
29753
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
29754
- return new BarChart(definition, sheetId, this.getters);
29918
+ duplicateInDuplicatedSheet(newSheetId) {
29919
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
29920
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
29921
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
29922
+ return new BarChart(definition, newSheetId, this.getters);
29755
29923
  }
29756
29924
  copyInSheetId(sheetId) {
29757
29925
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -29818,7 +29986,7 @@ function createBarChartRuntime(chart, getters) {
29818
29986
  const config = {
29819
29987
  type: "bar",
29820
29988
  data: {
29821
- labels: chartData.labels.map(truncateLabel),
29989
+ labels: chartData.labels,
29822
29990
  datasets: getBarChartDatasets(definition, chartData),
29823
29991
  },
29824
29992
  options: {
@@ -29954,11 +30122,11 @@ class ComboChart extends AbstractChart {
29954
30122
  showValues: context.showValues,
29955
30123
  };
29956
30124
  }
29957
- copyForSheetId(sheetId) {
29958
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
29959
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
29960
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
29961
- return new ComboChart(definition, sheetId, this.getters);
30125
+ duplicateInDuplicatedSheet(newSheetId) {
30126
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30127
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30128
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30129
+ return new ComboChart(definition, newSheetId, this.getters);
29962
30130
  }
29963
30131
  copyInSheetId(sheetId) {
29964
30132
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -29971,7 +30139,7 @@ function createComboChartRuntime(chart, getters) {
29971
30139
  const config = {
29972
30140
  type: "bar",
29973
30141
  data: {
29974
- labels: chartData.labels.map(truncateLabel),
30142
+ labels: chartData.labels,
29975
30143
  datasets: getComboChartDatasets(definition, chartData),
29976
30144
  },
29977
30145
  options: {
@@ -30105,10 +30273,10 @@ class GaugeChart extends AbstractChart {
30105
30273
  },
30106
30274
  };
30107
30275
  }
30108
- copyForSheetId(sheetId) {
30109
- const dataRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.dataRange);
30110
- const definition = this.getDefinitionWithSpecificRanges(dataRange, sheetId);
30111
- return new GaugeChart(definition, sheetId, this.getters);
30276
+ duplicateInDuplicatedSheet(newSheetId) {
30277
+ const dataRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.dataRange);
30278
+ const definition = this.getDefinitionWithSpecificRanges(dataRange, newSheetId);
30279
+ return new GaugeChart(definition, newSheetId, this.getters);
30112
30280
  }
30113
30281
  copyInSheetId(sheetId) {
30114
30282
  const definition = this.getDefinitionWithSpecificRanges(this.dataRange, sheetId);
@@ -30201,7 +30369,11 @@ function createGaugeChartRuntime(chart, getters) {
30201
30369
  colors.push(chartColors.upperColor);
30202
30370
  return {
30203
30371
  background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
30204
- title: chart.title ?? { text: "" },
30372
+ title: {
30373
+ ...chart.title,
30374
+ // chart titles are extracted from .json files and they are translated at runtime here
30375
+ text: _t(chart.title.text ?? ""),
30376
+ },
30205
30377
  minValue: {
30206
30378
  value: minValue,
30207
30379
  label: formatValue(minValue, { locale, format }),
@@ -30285,11 +30457,11 @@ class GeoChart extends AbstractChart {
30285
30457
  : undefined,
30286
30458
  };
30287
30459
  }
30288
- copyForSheetId(sheetId) {
30289
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30290
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30291
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30292
- return new GeoChart(definition, sheetId, this.getters);
30460
+ duplicateInDuplicatedSheet(newSheetId) {
30461
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30462
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30463
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30464
+ return new GeoChart(definition, newSheetId, this.getters);
30293
30465
  }
30294
30466
  copyInSheetId(sheetId) {
30295
30467
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30482,11 +30654,11 @@ class LineChart extends AbstractChart {
30482
30654
  verticalAxis: getDefinedAxis(definition),
30483
30655
  };
30484
30656
  }
30485
- copyForSheetId(sheetId) {
30486
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30487
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30488
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30489
- return new LineChart(definition, sheetId, this.getters);
30657
+ duplicateInDuplicatedSheet(newSheetId) {
30658
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30659
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30660
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30661
+ return new LineChart(definition, newSheetId, this.getters);
30490
30662
  }
30491
30663
  copyInSheetId(sheetId) {
30492
30664
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30499,7 +30671,7 @@ function createLineChartRuntime(chart, getters) {
30499
30671
  const config = {
30500
30672
  type: "line",
30501
30673
  data: {
30502
- labels: chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
30674
+ labels: chartData.labels,
30503
30675
  datasets: getLineChartDatasets(definition, chartData),
30504
30676
  },
30505
30677
  options: {
@@ -30593,11 +30765,11 @@ class PieChart extends AbstractChart {
30593
30765
  showValues: this.showValues,
30594
30766
  };
30595
30767
  }
30596
- copyForSheetId(sheetId) {
30597
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30598
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30599
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30600
- return new PieChart(definition, sheetId, this.getters);
30768
+ duplicateInDuplicatedSheet(newSheetId) {
30769
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30770
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30771
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30772
+ return new PieChart(definition, newSheetId, this.getters);
30601
30773
  }
30602
30774
  copyInSheetId(sheetId) {
30603
30775
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30634,7 +30806,7 @@ function createPieChartRuntime(chart, getters) {
30634
30806
  const config = {
30635
30807
  type: chart.isDoughnut ? "doughnut" : "pie",
30636
30808
  data: {
30637
- labels: chartData.labels.map(truncateLabel),
30809
+ labels: chartData.labels,
30638
30810
  datasets: getPieChartDatasets(definition, chartData),
30639
30811
  },
30640
30812
  options: {
@@ -30714,11 +30886,11 @@ class PyramidChart extends AbstractChart {
30714
30886
  : undefined,
30715
30887
  };
30716
30888
  }
30717
- copyForSheetId(sheetId) {
30718
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30719
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30720
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30721
- return new PyramidChart(definition, sheetId, this.getters);
30889
+ duplicateInDuplicatedSheet(newSheetId) {
30890
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
30891
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
30892
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
30893
+ return new PyramidChart(definition, newSheetId, this.getters);
30722
30894
  }
30723
30895
  copyInSheetId(sheetId) {
30724
30896
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30770,7 +30942,7 @@ function createPyramidChartRuntime(chart, getters) {
30770
30942
  const config = {
30771
30943
  type: "bar",
30772
30944
  data: {
30773
- labels: chartData.labels.map(truncateLabel),
30945
+ labels: chartData.labels,
30774
30946
  datasets: getBarChartDatasets(definition, chartData),
30775
30947
  },
30776
30948
  options: {
@@ -30851,11 +31023,11 @@ class RadarChart extends AbstractChart {
30851
31023
  : undefined,
30852
31024
  };
30853
31025
  }
30854
- copyForSheetId(sheetId) {
30855
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30856
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30857
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30858
- return new RadarChart(definition, sheetId, this.getters);
31026
+ duplicateInDuplicatedSheet(newSheetId) {
31027
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
31028
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
31029
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
31030
+ return new RadarChart(definition, newSheetId, this.getters);
30859
31031
  }
30860
31032
  copyInSheetId(sheetId) {
30861
31033
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30920,7 +31092,7 @@ function createRadarChartRuntime(chart, getters) {
30920
31092
  const config = {
30921
31093
  type: "radar",
30922
31094
  data: {
30923
- labels: chartData.labels.map(truncateLabel),
31095
+ labels: chartData.labels,
30924
31096
  datasets: getRadarChartDatasets(definition, chartData),
30925
31097
  },
30926
31098
  options: {
@@ -31054,11 +31226,11 @@ class ScatterChart extends AbstractChart {
31054
31226
  verticalAxis: getDefinedAxis(definition),
31055
31227
  };
31056
31228
  }
31057
- copyForSheetId(sheetId) {
31058
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
31059
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
31060
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
31061
- return new ScatterChart(definition, sheetId, this.getters);
31229
+ duplicateInDuplicatedSheet(newSheetId) {
31230
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
31231
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
31232
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
31233
+ return new ScatterChart(definition, newSheetId, this.getters);
31062
31234
  }
31063
31235
  copyInSheetId(sheetId) {
31064
31236
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -31073,7 +31245,7 @@ function createScatterChartRuntime(chart, getters) {
31073
31245
  // have less options than the line chart (it only works with linear labels)
31074
31246
  type: "line",
31075
31247
  data: {
31076
- labels: chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
31248
+ labels: chartData.labels,
31077
31249
  datasets: getScatterChartDatasets(definition, chartData),
31078
31250
  },
31079
31251
  options: {
@@ -31171,11 +31343,11 @@ class WaterfallChart extends AbstractChart {
31171
31343
  : undefined,
31172
31344
  };
31173
31345
  }
31174
- copyForSheetId(sheetId) {
31175
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
31176
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
31177
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
31178
- return new WaterfallChart(definition, sheetId, this.getters);
31346
+ duplicateInDuplicatedSheet(newSheetId) {
31347
+ const dataSets = duplicateDataSetsInDuplicatedSheet(this.sheetId, newSheetId, this.dataSets);
31348
+ const labelRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.labelRange);
31349
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, newSheetId);
31350
+ return new WaterfallChart(definition, newSheetId, this.getters);
31179
31351
  }
31180
31352
  copyInSheetId(sheetId) {
31181
31353
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -33602,8 +33774,6 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33602
33774
  chartToImage: chartToImage,
33603
33775
  checkDataset: checkDataset,
33604
33776
  checkLabelRange: checkLabelRange,
33605
- copyDataSetsWithNewSheetId: copyDataSetsWithNewSheetId,
33606
- copyLabelRangeWithNewSheetId: copyLabelRangeWithNewSheetId,
33607
33777
  createBarChartRuntime: createBarChartRuntime,
33608
33778
  createDataSets: createDataSets,
33609
33779
  createGaugeChartRuntime: createGaugeChartRuntime,
@@ -33612,6 +33782,8 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33612
33782
  createScorecardChartRuntime: createScorecardChartRuntime,
33613
33783
  createWaterfallChartRuntime: createWaterfallChartRuntime,
33614
33784
  drawScoreChart: drawScoreChart,
33785
+ duplicateDataSetsInDuplicatedSheet: duplicateDataSetsInDuplicatedSheet,
33786
+ duplicateLabelRangeInDuplicatedSheet: duplicateLabelRangeInDuplicatedSheet,
33615
33787
  formatChartDatasetValue: formatChartDatasetValue,
33616
33788
  formatTickValue: formatTickValue,
33617
33789
  getChartPositionAtCenterOfViewport: getChartPositionAtCenterOfViewport,
@@ -35332,12 +35504,20 @@ function fontSizeMenuBuilder() {
35332
35504
  });
35333
35505
  }
35334
35506
  function isAutomaticFormatSelected(env) {
35335
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
35336
- return !activeCell || !activeCell.format;
35507
+ const activePosition = env.model.getters.getActivePosition();
35508
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
35509
+ if (pivotCell.type === "VALUE") {
35510
+ return !env.model.getters.getEvaluatedCell(activePosition).format;
35511
+ }
35512
+ return !env.model.getters.getCell(activePosition)?.format;
35337
35513
  }
35338
35514
  function isFormatSelected(env, format) {
35339
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
35340
- return activeCell?.format === format;
35515
+ const activePosition = env.model.getters.getActivePosition();
35516
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
35517
+ if (pivotCell.type === "VALUE") {
35518
+ return env.model.getters.getEvaluatedCell(activePosition).format === format;
35519
+ }
35520
+ return env.model.getters.getCell(activePosition)?.format === format;
35341
35521
  }
35342
35522
  function isFontSizeSelected(env, fontSize) {
35343
35523
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
@@ -39676,14 +39856,11 @@ class ChartPanel extends Component {
39676
39856
  }
39677
39857
 
39678
39858
  class DOMFocusableElementStore {
39679
- mutators = ["setFocusableElement", "focus"];
39859
+ mutators = ["setFocusableElement"];
39680
39860
  focusableElement = undefined;
39681
39861
  setFocusableElement(element) {
39682
39862
  this.focusableElement = element;
39683
39863
  }
39684
- focus() {
39685
- this.focusableElement?.focus();
39686
- }
39687
39864
  }
39688
39865
 
39689
39866
  css /* scss */ `
@@ -40331,7 +40508,7 @@ class Composer extends Component {
40331
40508
  if (document.activeElement === this.contentHelper.el &&
40332
40509
  this.props.composerStore.editionMode === "inactive" &&
40333
40510
  !this.props.isDefaultFocus) {
40334
- this.DOMFocusableElementStore.focus();
40511
+ this.DOMFocusableElementStore.focusableElement?.focus();
40335
40512
  }
40336
40513
  });
40337
40514
  useEffect(() => {
@@ -44312,16 +44489,21 @@ class TextInput extends Component {
44312
44489
  }
44313
44490
  this.inputRef.el?.blur();
44314
44491
  }
44315
- focusInputAndSelectContent() {
44316
- const inputEl = this.inputRef.el;
44317
- if (!inputEl)
44318
- return;
44319
- // The onFocus event selects all text in the input.
44320
- // The subsequent mouseup event can deselect this text,
44321
- // so t-on-mouseup.prevent.stop is used to prevent this
44322
- // default behavior and preserve the selection.
44323
- inputEl.focus();
44324
- inputEl.select();
44492
+ onMouseDown(ev) {
44493
+ // Stop the event if the input is not focused, we handle everything in onMouseUp
44494
+ if (ev.target !== document.activeElement) {
44495
+ ev.preventDefault();
44496
+ ev.stopPropagation();
44497
+ }
44498
+ }
44499
+ onMouseUp(ev) {
44500
+ const target = ev.target;
44501
+ if (target !== document.activeElement) {
44502
+ target.focus();
44503
+ target.select();
44504
+ ev.preventDefault();
44505
+ ev.stopPropagation();
44506
+ }
44325
44507
  }
44326
44508
  }
44327
44509
 
@@ -44874,7 +45056,16 @@ class PivotTitleSection extends Component {
44874
45056
  newPivotId,
44875
45057
  newSheetId,
44876
45058
  });
44877
- const text = result.isSuccessful ? _t("Pivot duplicated.") : _t("Pivot duplication failed");
45059
+ let text;
45060
+ if (result.isSuccessful) {
45061
+ text = _t("Pivot duplicated.");
45062
+ }
45063
+ else if (result.isCancelledBecause("PivotInError" /* CommandResult.PivotInError */)) {
45064
+ text = _t("Cannot duplicate a pivot in error.");
45065
+ }
45066
+ else {
45067
+ text = _t("Pivot duplication failed.");
45068
+ }
44878
45069
  const type = result.isSuccessful ? "success" : "danger";
44879
45070
  this.env.notifyUser({
44880
45071
  text,
@@ -46025,12 +46216,10 @@ class SpreadsheetPivot {
46025
46216
  * Take cares of double names
46026
46217
  */
46027
46218
  findName(name, fields) {
46028
- let increment = 1;
46029
- const initialName = name;
46030
- while (name in fields) {
46031
- name = `${initialName}${++increment}`;
46032
- }
46033
- return name;
46219
+ return getUniqueText(name, Object.keys(fields), {
46220
+ compute: (name, i) => `${name}${i}`,
46221
+ start: 2,
46222
+ });
46034
46223
  }
46035
46224
  extractDataEntriesFromRange(range) {
46036
46225
  const dataEntries = [];
@@ -46213,7 +46402,9 @@ class PivotSidePanelStore extends SpreadsheetStore {
46213
46402
  pivot: this.draft,
46214
46403
  });
46215
46404
  this.draft = null;
46216
- if (!this.alreadyNotified && !this.isDynamicPivotInViewport()) {
46405
+ if (!this.alreadyNotified &&
46406
+ !this.isDynamicPivotInViewport() &&
46407
+ this.isStaticPivotInViewport()) {
46217
46408
  const formulaId = this.getters.getPivotFormulaId(this.pivotId);
46218
46409
  const pivotExample = `=PIVOT(${formulaId})`;
46219
46410
  this.alreadyNotified = true;
@@ -46270,11 +46461,20 @@ class PivotSidePanelStore extends SpreadsheetStore {
46270
46461
  }
46271
46462
  }
46272
46463
  isDynamicPivotInViewport() {
46273
- const sheetId = this.getters.getActiveSheetId();
46274
- for (const col of this.getters.getSheetViewVisibleCols()) {
46275
- for (const row of this.getters.getSheetViewVisibleRows()) {
46276
- const isDynamicPivot = this.getters.isSpillPivotFormula({ sheetId, col, row });
46277
- if (isDynamicPivot) {
46464
+ for (const position of this.getters.getVisibleCellPositions()) {
46465
+ const isDynamicPivot = this.getters.isSpillPivotFormula(position);
46466
+ if (isDynamicPivot) {
46467
+ return true;
46468
+ }
46469
+ }
46470
+ return false;
46471
+ }
46472
+ isStaticPivotInViewport() {
46473
+ for (const position of this.getters.getVisibleCellPositions()) {
46474
+ const cell = this.getters.getCell(position);
46475
+ if (cell?.isFormula) {
46476
+ const pivotFunction = getFirstPivotFunction(cell.compiledFormula.tokens);
46477
+ if (pivotFunction && pivotFunction.functionName !== "PIVOT") {
46278
46478
  return true;
46279
46479
  }
46280
46480
  }
@@ -51693,7 +51893,7 @@ class Grid extends Component {
51693
51893
  this.cellPopovers = useStore(CellPopoverStore);
51694
51894
  useEffect(() => {
51695
51895
  if (!this.sidePanel.isOpen) {
51696
- this.DOMFocusableElementStore.focus();
51896
+ this.DOMFocusableElementStore.focusableElement?.focus();
51697
51897
  }
51698
51898
  }, () => [this.sidePanel.isOpen]);
51699
51899
  }
@@ -51899,7 +52099,7 @@ class Grid extends Component {
51899
52099
  focusDefaultElement() {
51900
52100
  if (!this.env.model.getters.getSelectedFigureId() &&
51901
52101
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
51902
- this.DOMFocusableElementStore.focus();
52102
+ this.DOMFocusableElementStore.focusableElement?.focus();
51903
52103
  }
51904
52104
  }
51905
52105
  get gridEl() {
@@ -52258,15 +52458,11 @@ class Grid extends Component {
52258
52458
  class BasePlugin {
52259
52459
  static getters = [];
52260
52460
  history;
52261
- dispatch;
52262
- canDispatch;
52263
- constructor(stateObserver, dispatch, canDispatch) {
52461
+ constructor(stateObserver) {
52264
52462
  this.history = Object.assign(Object.create(stateObserver), {
52265
52463
  update: stateObserver.addChange.bind(stateObserver, this),
52266
52464
  selectCell: () => { },
52267
52465
  });
52268
- this.dispatch = dispatch;
52269
- this.canDispatch = canDispatch;
52270
52466
  }
52271
52467
  /**
52272
52468
  * Export for excel should be available for all plugins, even for the UI.
@@ -52344,10 +52540,14 @@ class BasePlugin {
52344
52540
  */
52345
52541
  class CorePlugin extends BasePlugin {
52346
52542
  getters;
52543
+ dispatch;
52544
+ canDispatch;
52347
52545
  constructor({ getters, stateObserver, range, dispatch, canDispatch }) {
52348
- super(stateObserver, dispatch, canDispatch);
52546
+ super(stateObserver);
52349
52547
  range.addRangeProvider(this.adaptRanges.bind(this));
52350
52548
  this.getters = getters;
52549
+ this.dispatch = dispatch;
52550
+ this.canDispatch = canDispatch;
52351
52551
  }
52352
52552
  // ---------------------------------------------------------------------------
52353
52553
  // Import/Export
@@ -52436,10 +52636,34 @@ class BordersPlugin extends CorePlugin {
52436
52636
  const elements = [...cmd.elements].sort((a, b) => b - a);
52437
52637
  for (const group of groupConsecutive(elements)) {
52438
52638
  if (cmd.dimension === "COL") {
52439
- this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
52639
+ if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52640
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52641
+ this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52642
+ }
52643
+ }
52644
+ if (group[group.length - 1] === 0) {
52645
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52646
+ this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52647
+ }
52648
+ }
52649
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52650
+ this.clearInsideBorders(cmd.sheetId, [zone]);
52651
+ this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52440
52652
  }
52441
52653
  else {
52442
- this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
52654
+ if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52655
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52656
+ this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52657
+ }
52658
+ }
52659
+ if (group[group.length - 1] === 0) {
52660
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52661
+ this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52662
+ }
52663
+ }
52664
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52665
+ this.clearInsideBorders(cmd.sheetId, [zone]);
52666
+ this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52443
52667
  }
52444
52668
  }
52445
52669
  break;
@@ -52746,6 +52970,18 @@ class BordersPlugin extends CorePlugin {
52746
52970
  }
52747
52971
  }
52748
52972
  }
52973
+ /**
52974
+ * Remove the borders inside of a zone
52975
+ */
52976
+ clearInsideBorders(sheetId, zones) {
52977
+ for (let zone of zones) {
52978
+ for (let row = zone.top; row <= zone.bottom; row++) {
52979
+ for (let col = zone.left; col <= zone.right; col++) {
52980
+ this.history.update("borders", sheetId, col, row, undefined);
52981
+ }
52982
+ }
52983
+ }
52984
+ }
52749
52985
  /**
52750
52986
  * Add a border to the existing one to a cell
52751
52987
  */
@@ -53548,7 +53784,7 @@ class ChartPlugin extends CorePlugin {
53548
53784
  if (fig.tag === "chart") {
53549
53785
  const figureIdBase = fig.id.split(FIGURE_ID_SPLITTER).pop();
53550
53786
  const duplicatedFigureId = `${cmd.sheetIdTo}${FIGURE_ID_SPLITTER}${figureIdBase}`;
53551
- const chart = this.charts[fig.id]?.copyForSheetId(cmd.sheetIdTo);
53787
+ const chart = this.charts[fig.id]?.duplicateInDuplicatedSheet(cmd.sheetIdTo);
53552
53788
  if (chart) {
53553
53789
  this.dispatch("CREATE_CHART", {
53554
53790
  id: duplicatedFigureId,
@@ -54192,7 +54428,7 @@ class DataValidationPlugin extends CorePlugin {
54192
54428
  case "DUPLICATE_SHEET": {
54193
54429
  const rules = deepCopy(this.rules[cmd.sheetId]).map((rule) => ({
54194
54430
  ...rule,
54195
- ranges: rule.ranges.map((range) => copyRangeWithNewSheetId(cmd.sheetId, cmd.sheetIdTo, range)),
54431
+ ranges: rule.ranges.map((range) => duplicateRangeInDuplicatedSheet(cmd.sheetId, cmd.sheetIdTo, range)),
54196
54432
  }));
54197
54433
  this.history.update("rules", cmd.sheetIdTo, rules);
54198
54434
  break;
@@ -56241,14 +56477,11 @@ class SheetPlugin extends CorePlugin {
56241
56477
  return dimension === "COL" ? this.getNumberCols(sheetId) : this.getNumberRows(sheetId);
56242
56478
  }
56243
56479
  getNextSheetName(baseName = "Sheet") {
56244
- let i = 1;
56245
56480
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56246
- let name = `${baseName}${i}`;
56247
- while (names.includes(name)) {
56248
- name = `${baseName}${i}`;
56249
- i++;
56250
- }
56251
- return name;
56481
+ return getUniqueText(baseName, names, {
56482
+ compute: (name, i) => `${name}${i}`,
56483
+ computeFirstOne: true,
56484
+ });
56252
56485
  }
56253
56486
  getSheetSize(sheetId) {
56254
56487
  return {
@@ -56528,15 +56761,9 @@ class SheetPlugin extends CorePlugin {
56528
56761
  this.history.update("sheetIdsMapName", sheetIdsMapName);
56529
56762
  }
56530
56763
  getDuplicateSheetName(sheetName) {
56531
- let i = 1;
56532
56764
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56533
56765
  const baseName = _t("Copy of %s", sheetName);
56534
- let name = baseName.toString();
56535
- while (names.includes(name)) {
56536
- name = `${baseName} (${i})`;
56537
- i++;
56538
- }
56539
- return name;
56766
+ return getUniqueText(baseName.toString(), names);
56540
56767
  }
56541
56768
  deleteSheet(sheet) {
56542
56769
  const name = sheet.name;
@@ -58066,15 +58293,8 @@ class TableStylePlugin extends CorePlugin {
58066
58293
  }
58067
58294
  getNewCustomTableStyleName() {
58068
58295
  let name = _t("Custom Table Style");
58069
- const styleNames = new Set(Object.values(this.styles).map((style) => style.displayName));
58070
- if (!styleNames.has(name)) {
58071
- return name;
58072
- }
58073
- let i = 2;
58074
- while (styleNames.has(`${name} ${i}`)) {
58075
- i++;
58076
- }
58077
- return `${name} ${i}`;
58296
+ const styleNames = Object.values(this.styles).map((style) => style.displayName);
58297
+ return getUniqueText(name, styleNames, { compute: (name, i) => `${name} ${i}`, start: 2 });
58078
58298
  }
58079
58299
  isTableStyleEditable(styleId) {
58080
58300
  return !TABLE_PRESETS[styleId];
@@ -58104,24 +58324,15 @@ class TableStylePlugin extends CorePlugin {
58104
58324
  }
58105
58325
 
58106
58326
  /**
58107
- * UI plugins handle any transient data required to display a spreadsheet.
58108
- * They can draw on the grid canvas.
58327
+ * Core view plugins handle any data derived from core date (i.e. evaluation).
58328
+ * They cannot impact the model data (i.e. cannot dispatch commands).
58109
58329
  */
58110
- class UIPlugin extends BasePlugin {
58111
- static layers = [];
58330
+ class CoreViewPlugin extends BasePlugin {
58112
58331
  getters;
58113
- ui;
58114
- selection;
58115
- constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
58116
- super(stateObserver, dispatch, canDispatch);
58332
+ constructor({ getters, stateObserver }) {
58333
+ super(stateObserver);
58117
58334
  this.getters = getters;
58118
- this.ui = uiActions;
58119
- this.selection = selection;
58120
58335
  }
58121
- // ---------------------------------------------------------------------------
58122
- // Grid rendering
58123
- // ---------------------------------------------------------------------------
58124
- drawLayer(ctx, layer) { }
58125
58336
  }
58126
58337
 
58127
58338
  /**
@@ -59816,7 +60027,7 @@ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId
59816
60027
  // as necessary in several iterations, where evaluated cells can trigger the evaluation
59817
60028
  // of other cells depending on it, at the next iteration.
59818
60029
  //#endregion
59819
- class EvaluationPlugin extends UIPlugin {
60030
+ class EvaluationPlugin extends CoreViewPlugin {
59820
60031
  static getters = [
59821
60032
  "evaluateFormula",
59822
60033
  "evaluateFormulaResult",
@@ -60083,7 +60294,7 @@ function colorDistance(color1, color2) {
60083
60294
  * This plugins aims to compute and keep to custom colors used in the
60084
60295
  * current spreadsheet
60085
60296
  */
60086
- class CustomColorsPlugin extends UIPlugin {
60297
+ class CustomColorsPlugin extends CoreViewPlugin {
60087
60298
  customColors = {};
60088
60299
  shouldUpdateColors = true;
60089
60300
  static getters = ["getCustomColors"];
@@ -60219,7 +60430,7 @@ class CustomColorsPlugin extends UIPlugin {
60219
60430
  }
60220
60431
  }
60221
60432
 
60222
- class EvaluationChartPlugin extends UIPlugin {
60433
+ class EvaluationChartPlugin extends CoreViewPlugin {
60223
60434
  static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
60224
60435
  charts = {};
60225
60436
  createRuntimeChart = chartRuntimeFactory(this.getters);
@@ -60323,7 +60534,7 @@ class EvaluationChartPlugin extends UIPlugin {
60323
60534
  }
60324
60535
  }
60325
60536
 
60326
- class EvaluationConditionalFormatPlugin extends UIPlugin {
60537
+ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
60327
60538
  static getters = [
60328
60539
  "getConditionalIcon",
60329
60540
  "getCellConditionalFormatStyle",
@@ -60641,7 +60852,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
60641
60852
  }
60642
60853
 
60643
60854
  const VALID_RESULT = { isValid: true };
60644
- class EvaluationDataValidationPlugin extends UIPlugin {
60855
+ class EvaluationDataValidationPlugin extends CoreViewPlugin {
60645
60856
  static getters = [
60646
60857
  "getDataValidationInvalidCriterionValueMessage",
60647
60858
  "getInvalidDataValidationMessage",
@@ -60772,7 +60983,7 @@ class EvaluationDataValidationPlugin extends UIPlugin {
60772
60983
  }
60773
60984
  }
60774
60985
 
60775
- class DynamicTablesPlugin extends UIPlugin {
60986
+ class DynamicTablesPlugin extends CoreViewPlugin {
60776
60987
  static getters = [
60777
60988
  "canCreateDynamicTableOnZones",
60778
60989
  "doesZonesContainFilter",
@@ -60946,7 +61157,7 @@ class DynamicTablesPlugin extends UIPlugin {
60946
61157
  }
60947
61158
  }
60948
61159
 
60949
- class HeaderSizeUIPlugin extends UIPlugin {
61160
+ class HeaderSizeUIPlugin extends CoreViewPlugin {
60950
61161
  static getters = ["getRowSize", "getHeaderSize"];
60951
61162
  tallestCellInRow = {};
60952
61163
  ctx = document.createElement("canvas").getContext("2d");
@@ -61652,7 +61863,7 @@ const UNDO_REDO_PIVOT_COMMANDS = ["ADD_PIVOT", "UPDATE_PIVOT"];
61652
61863
  function isPivotCommand(cmd) {
61653
61864
  return UNDO_REDO_PIVOT_COMMANDS.includes(cmd.type);
61654
61865
  }
61655
- class PivotUIPlugin extends UIPlugin {
61866
+ class PivotUIPlugin extends CoreViewPlugin {
61656
61867
  static getters = [
61657
61868
  "getPivot",
61658
61869
  "getFirstPivotFunction",
@@ -61856,13 +62067,9 @@ class PivotUIPlugin extends UIPlugin {
61856
62067
  }
61857
62068
  generateNewCalculatedMeasureName(measures) {
61858
62069
  const existingMeasures = measures.map((m) => m.fieldName);
61859
- let i = 1;
61860
- let name = _t("Calculated measure %s", i);
61861
- while (existingMeasures.includes(name)) {
61862
- i++;
61863
- name = _t("Calculated measure %s", i);
61864
- }
61865
- return name;
62070
+ return getUniqueText(_t("Calculated measure 1"), existingMeasures, {
62071
+ compute: (name, i) => _t("Calculated measure %s", i),
62072
+ });
61866
62073
  }
61867
62074
  getPivot(pivotId) {
61868
62075
  if (!this.getters.isExistingPivot(pivotId)) {
@@ -61916,6 +62123,31 @@ class PivotUIPlugin extends UIPlugin {
61916
62123
  }
61917
62124
  }
61918
62125
 
62126
+ /**
62127
+ * UI plugins handle any transient data required to display a spreadsheet.
62128
+ * They can draw on the grid canvas.
62129
+ */
62130
+ class UIPlugin extends BasePlugin {
62131
+ static layers = [];
62132
+ getters;
62133
+ ui;
62134
+ selection;
62135
+ dispatch;
62136
+ canDispatch;
62137
+ constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
62138
+ super(stateObserver);
62139
+ this.getters = getters;
62140
+ this.ui = uiActions;
62141
+ this.selection = selection;
62142
+ this.dispatch = dispatch;
62143
+ this.canDispatch = canDispatch;
62144
+ }
62145
+ // ---------------------------------------------------------------------------
62146
+ // Grid rendering
62147
+ // ---------------------------------------------------------------------------
62148
+ drawLayer(ctx, layer) { }
62149
+ }
62150
+
61919
62151
  /**
61920
62152
  * This plugin manage the autofill.
61921
62153
  *
@@ -63292,6 +63524,9 @@ class Session extends EventBus {
63292
63524
  }
63293
63525
  }
63294
63526
  this.acknowledge(message);
63527
+ if (message.type === "REMOTE_REVISION" && message.clientId === this.clientId) {
63528
+ return;
63529
+ }
63295
63530
  this.trigger("collaborative-event-received");
63296
63531
  }
63297
63532
  onClientMoved(message) {
@@ -63942,6 +64177,19 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
63942
64177
 
63943
64178
  class InsertPivotPlugin extends UIPlugin {
63944
64179
  static getters = [];
64180
+ allowDispatch(cmd) {
64181
+ switch (cmd.type) {
64182
+ case "DUPLICATE_PIVOT_IN_NEW_SHEET":
64183
+ if (!this.getters.isExistingPivot(cmd.pivotId)) {
64184
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
64185
+ }
64186
+ if (!this.getters.getPivot(cmd.pivotId).isValid()) {
64187
+ return "PivotInError" /* CommandResult.PivotInError */;
64188
+ }
64189
+ break;
64190
+ }
64191
+ return "Success" /* CommandResult.Success */;
64192
+ }
63945
64193
  handle(cmd) {
63946
64194
  switch (cmd.type) {
63947
64195
  case "INSERT_NEW_PIVOT":
@@ -64015,15 +64263,9 @@ class InsertPivotPlugin extends UIPlugin {
64015
64263
  }
64016
64264
  }
64017
64265
  getPivotDuplicateSheetName(pivotName) {
64018
- let i = 1;
64019
64266
  const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
64020
64267
  const sanitizedName = sanitizeSheetName(pivotName);
64021
- let name = sanitizedName;
64022
- while (names.includes(name)) {
64023
- name = `${sanitizedName} (${i})`;
64024
- i++;
64025
- }
64026
- return name;
64268
+ return getUniqueText(sanitizedName, names);
64027
64269
  }
64028
64270
  insertPivotWithTable(sheetId, col, row, pivotId, table, mode) {
64029
64271
  const { cols, rows, measures, fieldsType } = table;
@@ -66130,13 +66372,10 @@ class FilterEvaluationPlugin extends UIPlugin {
66130
66372
  if (!colName) {
66131
66373
  colName = `Column${colIndex}`;
66132
66374
  }
66133
- let currentColName = colName;
66134
- let i = 2;
66135
- while (usedColNames.includes(currentColName)) {
66136
- currentColName = colName + String(i);
66137
- i++;
66138
- }
66139
- return currentColName;
66375
+ return getUniqueText(colName, usedColNames, {
66376
+ compute: (name, i) => colName + String(i),
66377
+ start: 2,
66378
+ });
66140
66379
  }
66141
66380
  }
66142
66381
 
@@ -68178,11 +68417,6 @@ class BottomBarSheet extends Component {
68178
68417
  editionState = "initializing";
68179
68418
  DOMFocusableElementStore;
68180
68419
  setup() {
68181
- onMounted(() => {
68182
- if (this.isSheetActive) {
68183
- this.scrollToSheet();
68184
- }
68185
- });
68186
68420
  onPatched(() => {
68187
68421
  if (this.sheetNameRef.el && this.state.isEditing && this.editionState === "initializing") {
68188
68422
  this.editionState = "editing";
@@ -68191,6 +68425,11 @@ class BottomBarSheet extends Component {
68191
68425
  });
68192
68426
  this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
68193
68427
  useExternalListener(window, "click", () => (this.state.pickerOpened = false));
68428
+ useEffect((sheetId) => {
68429
+ if (this.props.sheetId === sheetId) {
68430
+ this.scrollToSheet();
68431
+ }
68432
+ }, () => [this.env.model.getters.getActiveSheetId()]);
68194
68433
  }
68195
68434
  focusInputAndSelectContent() {
68196
68435
  if (!this.state.isEditing || !this.sheetNameRef.el)
@@ -68202,7 +68441,10 @@ class BottomBarSheet extends Component {
68202
68441
  }
68203
68442
  }
68204
68443
  scrollToSheet() {
68205
- this.sheetDivRef.el?.scrollIntoView?.();
68444
+ this.sheetDivRef.el?.scrollIntoView?.({
68445
+ behavior: "smooth",
68446
+ inline: "nearest",
68447
+ });
68206
68448
  }
68207
68449
  onFocusOut() {
68208
68450
  if (this.state.isEditing && this.editionState !== "initializing") {
@@ -68232,11 +68474,11 @@ class BottomBarSheet extends Component {
68232
68474
  if (ev.key === "Enter") {
68233
68475
  ev.preventDefault();
68234
68476
  this.stopEdition();
68235
- this.DOMFocusableElementStore.focus();
68477
+ this.DOMFocusableElementStore.focusableElement?.focus();
68236
68478
  }
68237
68479
  if (ev.key === "Escape") {
68238
68480
  this.cancelEdition();
68239
- this.DOMFocusableElementStore.focus();
68481
+ this.DOMFocusableElementStore.focusableElement?.focus();
68240
68482
  }
68241
68483
  }
68242
68484
  onMouseEventSheetName(ev) {
@@ -68785,23 +69027,17 @@ class ClickableCellsStore extends SpreadsheetStore {
68785
69027
  const cells = [];
68786
69028
  const getters = this.getters;
68787
69029
  const sheetId = getters.getActiveSheetId();
68788
- for (const col of getters.getSheetViewVisibleCols()) {
68789
- for (const row of getters.getSheetViewVisibleRows()) {
68790
- const position = { sheetId, col, row };
68791
- if (!getters.isMainCellPosition(position)) {
68792
- continue;
68793
- }
68794
- const action = this.getClickableAction(position);
68795
- if (!action) {
68796
- continue;
68797
- }
68798
- const zone = getters.expandZone(sheetId, positionToZone(position));
68799
- cells.push({
68800
- coordinates: getters.getVisibleRect(zone),
68801
- position,
68802
- action,
68803
- });
69030
+ for (const position of this.getters.getVisibleCellPositions()) {
69031
+ const action = this.getClickableAction(position);
69032
+ if (!action) {
69033
+ continue;
68804
69034
  }
69035
+ const zone = getters.expandZone(sheetId, positionToZone(position));
69036
+ cells.push({
69037
+ coordinates: getters.getVisibleRect(zone),
69038
+ position,
69039
+ action,
69040
+ });
68805
69041
  }
68806
69042
  return cells;
68807
69043
  }
@@ -73612,7 +73848,7 @@ function addRows(construct, data, sheet) {
73612
73848
  if (content || styleId || formatId || borderId || value !== undefined) {
73613
73849
  const attributes = [["r", xc]];
73614
73850
  // style
73615
- const id = normalizeStyle(construct, extractStyle(data, styleId, formatId, borderId));
73851
+ const id = normalizeStyle(construct, extractStyle(data, content, styleId, formatId, borderId));
73616
73852
  // don't add style if default
73617
73853
  if (id) {
73618
73854
  attributes.push(["s", id]);
@@ -73966,7 +74202,12 @@ function createSharedStrings(strings) {
73966
74202
  ["count", strings.length],
73967
74203
  ["uniqueCount", strings.length],
73968
74204
  ];
73969
- const stringNodes = strings.map((string) => escapeXml /*xml*/ `<si><t>${string}</t></si>`);
74205
+ const stringNodes = strings.map((string) => {
74206
+ if (string.trim() !== string) {
74207
+ return escapeXml /*xml*/ `<si><t xml:space="preserve">${string}</t></si>`;
74208
+ }
74209
+ return escapeXml /*xml*/ `<si><t>${string}</t></si>`;
74210
+ });
73970
74211
  const xml = escapeXml /*xml*/ `
73971
74212
  <sst ${formatAttributes(namespaces)}>
73972
74213
  ${joinXmlNodes(stringNodes)}
@@ -74047,14 +74288,13 @@ function createRelRoot() {
74047
74288
  */
74048
74289
  function fixLengthySheetNames(data) {
74049
74290
  const nameMapping = {};
74050
- const newNames = new Set();
74291
+ const newNames = [];
74051
74292
  for (const sheet of data.sheets) {
74052
74293
  let newName = sheet.name.slice(0, 31);
74053
- let i = 1;
74054
- while (newNames.has(newName)) {
74055
- newName = newName.slice(0, 31 - String(i).length) + i++;
74056
- }
74057
- newNames.add(newName);
74294
+ newName = getUniqueText(newName, newNames, {
74295
+ compute: (name, i) => name.slice(0, 31 - String(i).length) + i,
74296
+ });
74297
+ newNames.push(newName);
74058
74298
  if (newName !== sheet.name) {
74059
74299
  nameMapping[sheet.name] = newName;
74060
74300
  sheet.name = newName;
@@ -74119,6 +74359,7 @@ class Model extends EventBus {
74119
74359
  */
74120
74360
  config;
74121
74361
  corePluginConfig;
74362
+ coreViewPluginConfig;
74122
74363
  uiPluginConfig;
74123
74364
  state;
74124
74365
  selection;
@@ -74171,6 +74412,7 @@ class Model extends EventBus {
74171
74412
  this.coreHandlers.push(this.range);
74172
74413
  this.handlers.push(this.range);
74173
74414
  this.corePluginConfig = this.setupCorePluginConfig();
74415
+ this.coreViewPluginConfig = this.setupCoreViewPluginConfig();
74174
74416
  this.uiPluginConfig = this.setupUiPluginConfig();
74175
74417
  // registering plugins
74176
74418
  for (let Plugin of corePluginRegistry.getAll()) {
@@ -74179,7 +74421,7 @@ class Model extends EventBus {
74179
74421
  Object.assign(this.getters, this.coreGetters);
74180
74422
  this.session.loadInitialMessages(stateUpdateMessages);
74181
74423
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
74182
- const plugin = this.setupUiPlugin(Plugin);
74424
+ const plugin = this.setupCoreViewPlugin(Plugin);
74183
74425
  this.handlers.push(plugin);
74184
74426
  this.uiHandlers.push(plugin);
74185
74427
  this.coreHandlers.push(plugin);
@@ -74206,7 +74448,7 @@ class Model extends EventBus {
74206
74448
  // events
74207
74449
  this.setupSessionEvents();
74208
74450
  this.joinSession();
74209
- if (config.snapshotRequested) {
74451
+ if (config.snapshotRequested || (data["[Content_Types].xml"] && !this.getters.isReadonly())) {
74210
74452
  const startSnapshot = performance.now();
74211
74453
  console.debug("Snapshot requested");
74212
74454
  this.session.snapshot(this.exportData());
@@ -74245,6 +74487,19 @@ class Model extends EventBus {
74245
74487
  }
74246
74488
  return plugin;
74247
74489
  }
74490
+ setupCoreViewPlugin(Plugin) {
74491
+ const plugin = new Plugin(this.coreViewPluginConfig);
74492
+ for (let name of Plugin.getters) {
74493
+ if (!(name in plugin)) {
74494
+ throw new Error(`Invalid getter name: ${name} for plugin ${plugin.constructor}`);
74495
+ }
74496
+ if (name in this.getters) {
74497
+ throw new Error(`Getter "${name}" is already defined.`);
74498
+ }
74499
+ this.getters[name] = plugin[name].bind(plugin);
74500
+ }
74501
+ return plugin;
74502
+ }
74248
74503
  /**
74249
74504
  * Initialize and properly configure a plugin.
74250
74505
  *
@@ -74346,6 +74601,20 @@ class Model extends EventBus {
74346
74601
  external: this.config.external,
74347
74602
  };
74348
74603
  }
74604
+ setupCoreViewPluginConfig() {
74605
+ return {
74606
+ getters: this.getters,
74607
+ stateObserver: this.state,
74608
+ selection: this.selection,
74609
+ moveClient: this.session.move.bind(this.session),
74610
+ custom: this.config.custom,
74611
+ uiActions: this.config,
74612
+ session: this.session,
74613
+ defaultCurrency: this.config.defaultCurrency,
74614
+ customColors: this.config.customColors || [],
74615
+ external: this.config.external,
74616
+ };
74617
+ }
74349
74618
  setupUiPluginConfig() {
74350
74619
  return {
74351
74620
  getters: this.getters,
@@ -74693,6 +74962,9 @@ const helpers = {
74693
74962
  areDomainArgsFieldsValid,
74694
74963
  splitReference,
74695
74964
  sanitizeSheetName,
74965
+ getUniqueText,
74966
+ isNumber,
74967
+ isDateTime,
74696
74968
  };
74697
74969
  const links = {
74698
74970
  isMarkdownLink,
@@ -74784,9 +75056,9 @@ const constants = {
74784
75056
  };
74785
75057
  const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
74786
75058
 
74787
- export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, 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 };
75059
+ export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, 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 };
74788
75060
 
74789
75061
 
74790
- __info__.version = "18.2.0-alpha.0";
74791
- __info__.date = "2024-12-26T08:02:39.123Z";
74792
- __info__.hash = "0f45915";
75062
+ __info__.version = "18.2.0-alpha.1";
75063
+ __info__.date = "2025-01-14T11:35:51.135Z";
75064
+ __info__.hash = "702f816";