@odoo/o-spreadsheet 18.1.1 → 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.1.1
6
- * @date 2025-01-14T11:43:19.116Z
7
- * @hash d0fa5de
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}$/;
@@ -6027,10 +6038,10 @@ class RangeImpl {
6027
6038
  }
6028
6039
  }
6029
6040
  /**
6030
- * 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
6031
6042
  * sheetIdTo.
6032
6043
  */
6033
- function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
6044
+ function duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) {
6034
6045
  const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
6035
6046
  return range.clone({ sheetId });
6036
6047
  }
@@ -9463,6 +9474,150 @@ class ComposerFocusStore extends SpreadsheetStore {
9463
9474
  }
9464
9475
  }
9465
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
+
9466
9621
  const TREND_LINE_XAXIS_ID = "x1";
9467
9622
  /**
9468
9623
  * This file contains helpers that are common to different charts (mainly
@@ -9515,25 +9670,25 @@ function updateChartRangesWithDataSets(getters, applyChange, chartDataSets, char
9515
9670
  };
9516
9671
  }
9517
9672
  /**
9518
- * 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
9519
9674
  * sheetIdTo.
9520
9675
  */
9521
- function copyDataSetsWithNewSheetId(sheetIdFrom, sheetIdTo, dataSets) {
9676
+ function duplicateDataSetsInDuplicatedSheet(sheetIdFrom, sheetIdTo, dataSets) {
9522
9677
  return dataSets.map((ds) => {
9523
9678
  return {
9524
- dataRange: copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.dataRange),
9679
+ dataRange: duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, ds.dataRange),
9525
9680
  labelCell: ds.labelCell
9526
- ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.labelCell)
9681
+ ? duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, ds.labelCell)
9527
9682
  : undefined,
9528
9683
  };
9529
9684
  });
9530
9685
  }
9531
9686
  /**
9532
- * 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
9533
9688
  * sheetIdTo.
9534
9689
  */
9535
- function copyLabelRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
9536
- return range ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) : undefined;
9690
+ function duplicateLabelRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) {
9691
+ return range ? duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range) : undefined;
9537
9692
  }
9538
9693
  /**
9539
9694
  * Adapt a single range of a chart
@@ -9786,10 +9941,11 @@ function formatTickValue(localeFormat) {
9786
9941
  if (isNaN(value))
9787
9942
  return value;
9788
9943
  const { locale, format } = localeFormat;
9789
- return formatValue(value, {
9944
+ const formattedValue = formatValue(value, {
9790
9945
  locale,
9791
9946
  format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
9792
9947
  });
9948
+ return truncateLabel(formattedValue);
9793
9949
  };
9794
9950
  }
9795
9951
  const CHART_AXIS_CHOICES = [
@@ -9804,6 +9960,15 @@ function getPieColors(colors, dataSetsValues) {
9804
9960
  }
9805
9961
  return pieColors;
9806
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
+ }
9807
9972
 
9808
9973
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9809
9974
  const chartShowValuesPlugin = {
@@ -9999,6 +10164,18 @@ function getNextNonEmptyBar(bars, startIndex) {
9999
10164
 
10000
10165
  window.Chart?.register(waterfallLinesPlugin);
10001
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
+ `;
10002
10179
  class ChartJsComponent extends Component {
10003
10180
  static template = "o-spreadsheet-ChartJsComponent";
10004
10181
  static props = {
@@ -10237,11 +10414,11 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
10237
10414
  keyValue: keyValueZone ? zoneToXc(keyValueZone) : undefined,
10238
10415
  };
10239
10416
  }
10240
- copyForSheetId(sheetId) {
10241
- const baseline = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.baseline);
10242
- const keyValue = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.keyValue);
10243
- const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, sheetId);
10244
- 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);
10245
10422
  }
10246
10423
  copyInSheetId(sheetId) {
10247
10424
  const definition = this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue, sheetId);
@@ -22568,15 +22745,6 @@ const CHART_COMMON_OPTIONS = {
22568
22745
  },
22569
22746
  animation: false,
22570
22747
  };
22571
- function truncateLabel(label) {
22572
- if (!label) {
22573
- return "";
22574
- }
22575
- if (label.length > MAX_CHAR_LABEL) {
22576
- return label.substring(0, MAX_CHAR_LABEL) + "…";
22577
- }
22578
- return label;
22579
- }
22580
22748
  function chartToImage(runtime, figure, type) {
22581
22749
  // wrap the canvas in a div with a fixed size because chart.js would
22582
22750
  // fill the whole page otherwise
@@ -22898,150 +23066,6 @@ const CONTENT_TYPES_FILE = "[Content_Types].xml";
22898
23066
  */
22899
23067
  const iconsOnCellRegistry = new Registry();
22900
23068
 
22901
- /**
22902
- * This file is largely inspired by owl 1.
22903
- * `css` tag has been removed from owl 2 without workaround to manage css.
22904
- * So, the solution was to import the behavior of owl 1 directly in our
22905
- * codebase, with one difference: the css is added to the sheet as soon as the
22906
- * css tag is executed. In owl 1, the css was added as soon as a Component was
22907
- * created for the first time.
22908
- */
22909
- const STYLESHEETS = {};
22910
- let nextId = 0;
22911
- /**
22912
- * CSS tag helper for defining inline stylesheets. With this, one can simply define
22913
- * an inline stylesheet with just the following code:
22914
- * ```js
22915
- * css`.component-a { color: red; }`;
22916
- * ```
22917
- */
22918
- function css(strings, ...args) {
22919
- const name = `__sheet__${nextId++}`;
22920
- const value = String.raw(strings, ...args);
22921
- registerSheet(name, value);
22922
- activateSheet(name);
22923
- return name;
22924
- }
22925
- function processSheet(str) {
22926
- const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
22927
- const selectorStack = [];
22928
- const parts = [];
22929
- let rules = [];
22930
- function generateSelector(stackIndex, parentSelector) {
22931
- const parts = [];
22932
- for (const selector of selectorStack[stackIndex]) {
22933
- let part = (parentSelector && parentSelector + " " + selector) || selector;
22934
- if (part.includes("&")) {
22935
- part = selector.replace(/&/g, parentSelector || "");
22936
- }
22937
- if (stackIndex < selectorStack.length - 1) {
22938
- part = generateSelector(stackIndex + 1, part);
22939
- }
22940
- parts.push(part);
22941
- }
22942
- return parts.join(", ");
22943
- }
22944
- function generateRules() {
22945
- if (rules.length) {
22946
- parts.push(generateSelector(0) + " {");
22947
- parts.push(...rules);
22948
- parts.push("}");
22949
- rules = [];
22950
- }
22951
- }
22952
- while (tokens.length) {
22953
- let token = tokens.shift();
22954
- if (token === "}") {
22955
- generateRules();
22956
- selectorStack.pop();
22957
- }
22958
- else {
22959
- if (tokens[0] === "{") {
22960
- generateRules();
22961
- selectorStack.push(token.split(/\s*,\s*/));
22962
- tokens.shift();
22963
- }
22964
- if (tokens[0] === ";") {
22965
- rules.push(" " + token + ";");
22966
- }
22967
- }
22968
- }
22969
- return parts.join("\n");
22970
- }
22971
- function registerSheet(id, css) {
22972
- const sheet = document.createElement("style");
22973
- sheet.textContent = processSheet(css);
22974
- STYLESHEETS[id] = sheet;
22975
- }
22976
- function activateSheet(id) {
22977
- const sheet = STYLESHEETS[id];
22978
- sheet.setAttribute("component", id);
22979
- document.head.appendChild(sheet);
22980
- }
22981
- function getTextDecoration({ strikethrough, underline, }) {
22982
- if (!strikethrough && !underline) {
22983
- return "none";
22984
- }
22985
- return `${strikethrough ? "line-through" : ""} ${underline ? "underline" : ""}`;
22986
- }
22987
- /**
22988
- * Convert the cell style to CSS properties.
22989
- */
22990
- function cellStyleToCss(style) {
22991
- const attributes = cellTextStyleToCss(style);
22992
- if (!style)
22993
- return attributes;
22994
- if (style.fillColor) {
22995
- attributes["background"] = style.fillColor;
22996
- }
22997
- return attributes;
22998
- }
22999
- /**
23000
- * Convert the cell text style to CSS properties.
23001
- */
23002
- function cellTextStyleToCss(style) {
23003
- const attributes = {};
23004
- if (!style)
23005
- return attributes;
23006
- if (style.bold) {
23007
- attributes["font-weight"] = "bold";
23008
- }
23009
- if (style.italic) {
23010
- attributes["font-style"] = "italic";
23011
- }
23012
- if (style.strikethrough || style.underline) {
23013
- let decoration = style.strikethrough ? "line-through" : "";
23014
- decoration = style.underline ? decoration + " underline" : decoration;
23015
- attributes["text-decoration"] = decoration;
23016
- }
23017
- if (style.textColor) {
23018
- attributes["color"] = style.textColor;
23019
- }
23020
- return attributes;
23021
- }
23022
- /**
23023
- * Transform CSS properties into a CSS string.
23024
- */
23025
- function cssPropertiesToCss(attributes) {
23026
- let styleStr = "";
23027
- for (const attName in attributes) {
23028
- if (!attributes[attName]) {
23029
- continue;
23030
- }
23031
- styleStr += `${attName}:${attributes[attName]}; `;
23032
- }
23033
- return styleStr;
23034
- }
23035
- function getElementMargins(el) {
23036
- const style = window.getComputedStyle(el);
23037
- return {
23038
- top: parseInt(style.marginTop, 10) || 0,
23039
- bottom: parseInt(style.marginBottom, 10) || 0,
23040
- left: parseInt(style.marginLeft, 10) || 0,
23041
- right: parseInt(style.marginRight, 10) || 0,
23042
- };
23043
- }
23044
-
23045
23069
  css /* scss */ `
23046
23070
  .o-spreadsheet {
23047
23071
  .o-icon {
@@ -27161,12 +27185,9 @@ migrationStepRegistry
27161
27185
  }
27162
27186
  const oldName = sheet.name;
27163
27187
  const escapedName = sanitizeSheetName(oldName, "_");
27164
- let i = 1;
27165
- let newName = escapedName;
27166
- while (namesTaken.includes(newName)) {
27167
- newName = `${escapedName}${i}`;
27168
- i++;
27169
- }
27188
+ const newName = getUniqueText(escapedName, namesTaken, {
27189
+ compute: (name, i) => `${name}${i}`,
27190
+ });
27170
27191
  sheet.name = newName;
27171
27192
  namesTaken.push(newName);
27172
27193
  const replaceName = (str) => {
@@ -28699,7 +28720,7 @@ function getChartDatasetValues(getters, dataSets) {
28699
28720
  : undefined;
28700
28721
  label =
28701
28722
  cell && labelRange
28702
- ? truncateLabel(cell.formattedValue)
28723
+ ? cell.formattedValue
28703
28724
  : (label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`);
28704
28725
  }
28705
28726
  else {
@@ -28789,7 +28810,7 @@ function getWaterfallDatasetAndLabels(definition, args) {
28789
28810
  }
28790
28811
  return {
28791
28812
  datasets: [dataset],
28792
- labels: labelsWithSubTotals.map(truncateLabel),
28813
+ labels: labelsWithSubTotals,
28793
28814
  };
28794
28815
  }
28795
28816
  function getLineChartDatasets(definition, args) {
@@ -29038,7 +29059,7 @@ function getPieChartLegend(definition, args) {
29038
29059
  generateLabels: (c) =>
29039
29060
  //@ts-ignore
29040
29061
  c.data.labels.map((label, index) => ({
29041
- text: label,
29062
+ text: truncateLabel(String(label)),
29042
29063
  strokeStyle: colors[index],
29043
29064
  fillStyle: colors[index],
29044
29065
  pointStyle: "rect",
@@ -29169,7 +29190,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29169
29190
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29170
29191
  if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29171
29192
  return {
29172
- text: dataset.label ?? "",
29193
+ text: truncateLabel(dataset.label),
29173
29194
  fontColor,
29174
29195
  strokeStyle: dataset.borderColor,
29175
29196
  hidden: !chart.isDatasetVisible(index),
@@ -29179,7 +29200,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29179
29200
  };
29180
29201
  }
29181
29202
  return {
29182
- text: dataset.label ?? "",
29203
+ text: truncateLabel(dataset.label),
29183
29204
  fontColor,
29184
29205
  strokeStyle: dataset.borderColor,
29185
29206
  fillStyle: dataset.backgroundColor,
@@ -29326,7 +29347,10 @@ function getRadarChartScales(definition, args) {
29326
29347
  callback: formatTickValue({ format: axisFormats?.r, locale }),
29327
29348
  backdropColor: definition.background || "#FFFFFF",
29328
29349
  },
29329
- pointLabels: { color: chartFontColor(definition.background) },
29350
+ pointLabels: {
29351
+ color: chartFontColor(definition.background),
29352
+ callback: truncateLabel,
29353
+ },
29330
29354
  suggestedMin: minValue < 0 ? minValue - 1 : 0,
29331
29355
  },
29332
29356
  };
@@ -29423,6 +29447,11 @@ function getChartAxis(definition, position, type, options) {
29423
29447
  ticks: {
29424
29448
  padding: 5,
29425
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
+ },
29426
29455
  },
29427
29456
  grid: {
29428
29457
  display: false,
@@ -29502,8 +29531,70 @@ function getChartTitle(definition) {
29502
29531
  };
29503
29532
  }
29504
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
+
29505
29594
  function getBarChartTooltip(definition, args) {
29506
29595
  return {
29596
+ enabled: false,
29597
+ external: customTooltipHandler,
29507
29598
  callbacks: {
29508
29599
  title: function (tooltipItems) {
29509
29600
  return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
@@ -29527,7 +29618,11 @@ function getBarChartTooltip(definition, args) {
29527
29618
  function getLineChartTooltip(definition, args) {
29528
29619
  const { axisType, locale, axisFormats } = args;
29529
29620
  const labelFormat = axisFormats?.x;
29530
- const tooltip = { callbacks: {} };
29621
+ const tooltip = {
29622
+ enabled: false,
29623
+ external: customTooltipHandler,
29624
+ callbacks: {},
29625
+ };
29531
29626
  if (axisType === "linear") {
29532
29627
  tooltip.callbacks.label = (tooltipItem) => {
29533
29628
  const dataSetPoint = tooltipItem.parsed.y;
@@ -29566,6 +29661,8 @@ function getPieChartTooltip(definition, args) {
29566
29661
  const { locale, axisFormats } = args;
29567
29662
  const format = axisFormats?.y || axisFormats?.y1;
29568
29663
  return {
29664
+ enabled: false,
29665
+ external: customTooltipHandler,
29569
29666
  callbacks: {
29570
29667
  title: function (tooltipItems) {
29571
29668
  return tooltipItems[0].dataset.label;
@@ -29590,6 +29687,8 @@ function getWaterfallChartTooltip(definition, args) {
29590
29687
  const format = axisFormats?.y || axisFormats?.y1;
29591
29688
  const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
29592
29689
  return {
29690
+ enabled: false,
29691
+ external: customTooltipHandler,
29593
29692
  callbacks: {
29594
29693
  label: function (tooltipItem) {
29595
29694
  const [lastValue, currentValue] = tooltipItem.raw;
@@ -29621,6 +29720,8 @@ function getPyramidChartTooltip(definition, args) {
29621
29720
  function getRadarChartTooltip(definition, args) {
29622
29721
  const { locale, axisFormats } = args;
29623
29722
  return {
29723
+ enabled: false,
29724
+ external: customTooltipHandler,
29624
29725
  callbacks: {
29625
29726
  label: function (tooltipItem) {
29626
29727
  const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
@@ -29659,6 +29760,48 @@ function calculatePercentage(dataset, dataIndex) {
29659
29760
  const percentage = (dataset[dataIndex] / total) * 100;
29660
29761
  return percentage.toFixed(2);
29661
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
+ }
29662
29805
 
29663
29806
  var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
29664
29807
  __proto__: null,
@@ -29772,11 +29915,11 @@ class BarChart extends AbstractChart {
29772
29915
  : undefined,
29773
29916
  };
29774
29917
  }
29775
- copyForSheetId(sheetId) {
29776
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
29777
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
29778
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
29779
- 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);
29780
29923
  }
29781
29924
  copyInSheetId(sheetId) {
29782
29925
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -29843,7 +29986,7 @@ function createBarChartRuntime(chart, getters) {
29843
29986
  const config = {
29844
29987
  type: "bar",
29845
29988
  data: {
29846
- labels: chartData.labels.map(truncateLabel),
29989
+ labels: chartData.labels,
29847
29990
  datasets: getBarChartDatasets(definition, chartData),
29848
29991
  },
29849
29992
  options: {
@@ -29979,11 +30122,11 @@ class ComboChart extends AbstractChart {
29979
30122
  showValues: context.showValues,
29980
30123
  };
29981
30124
  }
29982
- copyForSheetId(sheetId) {
29983
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
29984
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
29985
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
29986
- 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);
29987
30130
  }
29988
30131
  copyInSheetId(sheetId) {
29989
30132
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -29996,7 +30139,7 @@ function createComboChartRuntime(chart, getters) {
29996
30139
  const config = {
29997
30140
  type: "bar",
29998
30141
  data: {
29999
- labels: chartData.labels.map(truncateLabel),
30142
+ labels: chartData.labels,
30000
30143
  datasets: getComboChartDatasets(definition, chartData),
30001
30144
  },
30002
30145
  options: {
@@ -30130,10 +30273,10 @@ class GaugeChart extends AbstractChart {
30130
30273
  },
30131
30274
  };
30132
30275
  }
30133
- copyForSheetId(sheetId) {
30134
- const dataRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.dataRange);
30135
- const definition = this.getDefinitionWithSpecificRanges(dataRange, sheetId);
30136
- 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);
30137
30280
  }
30138
30281
  copyInSheetId(sheetId) {
30139
30282
  const definition = this.getDefinitionWithSpecificRanges(this.dataRange, sheetId);
@@ -30314,11 +30457,11 @@ class GeoChart extends AbstractChart {
30314
30457
  : undefined,
30315
30458
  };
30316
30459
  }
30317
- copyForSheetId(sheetId) {
30318
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30319
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30320
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30321
- 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);
30322
30465
  }
30323
30466
  copyInSheetId(sheetId) {
30324
30467
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30511,11 +30654,11 @@ class LineChart extends AbstractChart {
30511
30654
  verticalAxis: getDefinedAxis(definition),
30512
30655
  };
30513
30656
  }
30514
- copyForSheetId(sheetId) {
30515
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30516
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30517
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30518
- 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);
30519
30662
  }
30520
30663
  copyInSheetId(sheetId) {
30521
30664
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30528,7 +30671,7 @@ function createLineChartRuntime(chart, getters) {
30528
30671
  const config = {
30529
30672
  type: "line",
30530
30673
  data: {
30531
- labels: chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
30674
+ labels: chartData.labels,
30532
30675
  datasets: getLineChartDatasets(definition, chartData),
30533
30676
  },
30534
30677
  options: {
@@ -30622,11 +30765,11 @@ class PieChart extends AbstractChart {
30622
30765
  showValues: this.showValues,
30623
30766
  };
30624
30767
  }
30625
- copyForSheetId(sheetId) {
30626
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30627
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30628
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30629
- 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);
30630
30773
  }
30631
30774
  copyInSheetId(sheetId) {
30632
30775
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30663,7 +30806,7 @@ function createPieChartRuntime(chart, getters) {
30663
30806
  const config = {
30664
30807
  type: chart.isDoughnut ? "doughnut" : "pie",
30665
30808
  data: {
30666
- labels: chartData.labels.map(truncateLabel),
30809
+ labels: chartData.labels,
30667
30810
  datasets: getPieChartDatasets(definition, chartData),
30668
30811
  },
30669
30812
  options: {
@@ -30743,11 +30886,11 @@ class PyramidChart extends AbstractChart {
30743
30886
  : undefined,
30744
30887
  };
30745
30888
  }
30746
- copyForSheetId(sheetId) {
30747
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30748
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30749
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30750
- 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);
30751
30894
  }
30752
30895
  copyInSheetId(sheetId) {
30753
30896
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30799,7 +30942,7 @@ function createPyramidChartRuntime(chart, getters) {
30799
30942
  const config = {
30800
30943
  type: "bar",
30801
30944
  data: {
30802
- labels: chartData.labels.map(truncateLabel),
30945
+ labels: chartData.labels,
30803
30946
  datasets: getBarChartDatasets(definition, chartData),
30804
30947
  },
30805
30948
  options: {
@@ -30880,11 +31023,11 @@ class RadarChart extends AbstractChart {
30880
31023
  : undefined,
30881
31024
  };
30882
31025
  }
30883
- copyForSheetId(sheetId) {
30884
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30885
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30886
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30887
- 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);
30888
31031
  }
30889
31032
  copyInSheetId(sheetId) {
30890
31033
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -30949,7 +31092,7 @@ function createRadarChartRuntime(chart, getters) {
30949
31092
  const config = {
30950
31093
  type: "radar",
30951
31094
  data: {
30952
- labels: chartData.labels.map(truncateLabel),
31095
+ labels: chartData.labels,
30953
31096
  datasets: getRadarChartDatasets(definition, chartData),
30954
31097
  },
30955
31098
  options: {
@@ -31083,11 +31226,11 @@ class ScatterChart extends AbstractChart {
31083
31226
  verticalAxis: getDefinedAxis(definition),
31084
31227
  };
31085
31228
  }
31086
- copyForSheetId(sheetId) {
31087
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
31088
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
31089
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
31090
- 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);
31091
31234
  }
31092
31235
  copyInSheetId(sheetId) {
31093
31236
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -31102,7 +31245,7 @@ function createScatterChartRuntime(chart, getters) {
31102
31245
  // have less options than the line chart (it only works with linear labels)
31103
31246
  type: "line",
31104
31247
  data: {
31105
- labels: chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
31248
+ labels: chartData.labels,
31106
31249
  datasets: getScatterChartDatasets(definition, chartData),
31107
31250
  },
31108
31251
  options: {
@@ -31200,11 +31343,11 @@ class WaterfallChart extends AbstractChart {
31200
31343
  : undefined,
31201
31344
  };
31202
31345
  }
31203
- copyForSheetId(sheetId) {
31204
- const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
31205
- const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
31206
- const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
31207
- 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);
31208
31351
  }
31209
31352
  copyInSheetId(sheetId) {
31210
31353
  const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
@@ -33631,8 +33774,6 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33631
33774
  chartToImage: chartToImage,
33632
33775
  checkDataset: checkDataset,
33633
33776
  checkLabelRange: checkLabelRange,
33634
- copyDataSetsWithNewSheetId: copyDataSetsWithNewSheetId,
33635
- copyLabelRangeWithNewSheetId: copyLabelRangeWithNewSheetId,
33636
33777
  createBarChartRuntime: createBarChartRuntime,
33637
33778
  createDataSets: createDataSets,
33638
33779
  createGaugeChartRuntime: createGaugeChartRuntime,
@@ -33641,6 +33782,8 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33641
33782
  createScorecardChartRuntime: createScorecardChartRuntime,
33642
33783
  createWaterfallChartRuntime: createWaterfallChartRuntime,
33643
33784
  drawScoreChart: drawScoreChart,
33785
+ duplicateDataSetsInDuplicatedSheet: duplicateDataSetsInDuplicatedSheet,
33786
+ duplicateLabelRangeInDuplicatedSheet: duplicateLabelRangeInDuplicatedSheet,
33644
33787
  formatChartDatasetValue: formatChartDatasetValue,
33645
33788
  formatTickValue: formatTickValue,
33646
33789
  getChartPositionAtCenterOfViewport: getChartPositionAtCenterOfViewport,
@@ -46073,12 +46216,10 @@ class SpreadsheetPivot {
46073
46216
  * Take cares of double names
46074
46217
  */
46075
46218
  findName(name, fields) {
46076
- let increment = 1;
46077
- const initialName = name;
46078
- while (name in fields) {
46079
- name = `${initialName}${++increment}`;
46080
- }
46081
- return name;
46219
+ return getUniqueText(name, Object.keys(fields), {
46220
+ compute: (name, i) => `${name}${i}`,
46221
+ start: 2,
46222
+ });
46082
46223
  }
46083
46224
  extractDataEntriesFromRange(range) {
46084
46225
  const dataEntries = [];
@@ -46320,13 +46461,10 @@ class PivotSidePanelStore extends SpreadsheetStore {
46320
46461
  }
46321
46462
  }
46322
46463
  isDynamicPivotInViewport() {
46323
- const sheetId = this.getters.getActiveSheetId();
46324
- for (const col of this.getters.getSheetViewVisibleCols()) {
46325
- for (const row of this.getters.getSheetViewVisibleRows()) {
46326
- const isDynamicPivot = this.getters.isSpillPivotFormula({ sheetId, col, row });
46327
- if (isDynamicPivot) {
46328
- return true;
46329
- }
46464
+ for (const position of this.getters.getVisibleCellPositions()) {
46465
+ const isDynamicPivot = this.getters.isSpillPivotFormula(position);
46466
+ if (isDynamicPivot) {
46467
+ return true;
46330
46468
  }
46331
46469
  }
46332
46470
  return false;
@@ -52320,15 +52458,11 @@ class Grid extends Component {
52320
52458
  class BasePlugin {
52321
52459
  static getters = [];
52322
52460
  history;
52323
- dispatch;
52324
- canDispatch;
52325
- constructor(stateObserver, dispatch, canDispatch) {
52461
+ constructor(stateObserver) {
52326
52462
  this.history = Object.assign(Object.create(stateObserver), {
52327
52463
  update: stateObserver.addChange.bind(stateObserver, this),
52328
52464
  selectCell: () => { },
52329
52465
  });
52330
- this.dispatch = dispatch;
52331
- this.canDispatch = canDispatch;
52332
52466
  }
52333
52467
  /**
52334
52468
  * Export for excel should be available for all plugins, even for the UI.
@@ -52406,10 +52540,14 @@ class BasePlugin {
52406
52540
  */
52407
52541
  class CorePlugin extends BasePlugin {
52408
52542
  getters;
52543
+ dispatch;
52544
+ canDispatch;
52409
52545
  constructor({ getters, stateObserver, range, dispatch, canDispatch }) {
52410
- super(stateObserver, dispatch, canDispatch);
52546
+ super(stateObserver);
52411
52547
  range.addRangeProvider(this.adaptRanges.bind(this));
52412
52548
  this.getters = getters;
52549
+ this.dispatch = dispatch;
52550
+ this.canDispatch = canDispatch;
52413
52551
  }
52414
52552
  // ---------------------------------------------------------------------------
52415
52553
  // Import/Export
@@ -53646,7 +53784,7 @@ class ChartPlugin extends CorePlugin {
53646
53784
  if (fig.tag === "chart") {
53647
53785
  const figureIdBase = fig.id.split(FIGURE_ID_SPLITTER).pop();
53648
53786
  const duplicatedFigureId = `${cmd.sheetIdTo}${FIGURE_ID_SPLITTER}${figureIdBase}`;
53649
- const chart = this.charts[fig.id]?.copyForSheetId(cmd.sheetIdTo);
53787
+ const chart = this.charts[fig.id]?.duplicateInDuplicatedSheet(cmd.sheetIdTo);
53650
53788
  if (chart) {
53651
53789
  this.dispatch("CREATE_CHART", {
53652
53790
  id: duplicatedFigureId,
@@ -54290,7 +54428,7 @@ class DataValidationPlugin extends CorePlugin {
54290
54428
  case "DUPLICATE_SHEET": {
54291
54429
  const rules = deepCopy(this.rules[cmd.sheetId]).map((rule) => ({
54292
54430
  ...rule,
54293
- ranges: rule.ranges.map((range) => copyRangeWithNewSheetId(cmd.sheetId, cmd.sheetIdTo, range)),
54431
+ ranges: rule.ranges.map((range) => duplicateRangeInDuplicatedSheet(cmd.sheetId, cmd.sheetIdTo, range)),
54294
54432
  }));
54295
54433
  this.history.update("rules", cmd.sheetIdTo, rules);
54296
54434
  break;
@@ -56339,14 +56477,11 @@ class SheetPlugin extends CorePlugin {
56339
56477
  return dimension === "COL" ? this.getNumberCols(sheetId) : this.getNumberRows(sheetId);
56340
56478
  }
56341
56479
  getNextSheetName(baseName = "Sheet") {
56342
- let i = 1;
56343
56480
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56344
- let name = `${baseName}${i}`;
56345
- while (names.includes(name)) {
56346
- name = `${baseName}${i}`;
56347
- i++;
56348
- }
56349
- return name;
56481
+ return getUniqueText(baseName, names, {
56482
+ compute: (name, i) => `${name}${i}`,
56483
+ computeFirstOne: true,
56484
+ });
56350
56485
  }
56351
56486
  getSheetSize(sheetId) {
56352
56487
  return {
@@ -56626,15 +56761,9 @@ class SheetPlugin extends CorePlugin {
56626
56761
  this.history.update("sheetIdsMapName", sheetIdsMapName);
56627
56762
  }
56628
56763
  getDuplicateSheetName(sheetName) {
56629
- let i = 1;
56630
56764
  const names = this.orderedSheetIds.map(this.getSheetName.bind(this));
56631
56765
  const baseName = _t("Copy of %s", sheetName);
56632
- let name = baseName.toString();
56633
- while (names.includes(name)) {
56634
- name = `${baseName} (${i})`;
56635
- i++;
56636
- }
56637
- return name;
56766
+ return getUniqueText(baseName.toString(), names);
56638
56767
  }
56639
56768
  deleteSheet(sheet) {
56640
56769
  const name = sheet.name;
@@ -58164,15 +58293,8 @@ class TableStylePlugin extends CorePlugin {
58164
58293
  }
58165
58294
  getNewCustomTableStyleName() {
58166
58295
  let name = _t("Custom Table Style");
58167
- const styleNames = new Set(Object.values(this.styles).map((style) => style.displayName));
58168
- if (!styleNames.has(name)) {
58169
- return name;
58170
- }
58171
- let i = 2;
58172
- while (styleNames.has(`${name} ${i}`)) {
58173
- i++;
58174
- }
58175
- 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 });
58176
58298
  }
58177
58299
  isTableStyleEditable(styleId) {
58178
58300
  return !TABLE_PRESETS[styleId];
@@ -58202,24 +58324,15 @@ class TableStylePlugin extends CorePlugin {
58202
58324
  }
58203
58325
 
58204
58326
  /**
58205
- * UI plugins handle any transient data required to display a spreadsheet.
58206
- * 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).
58207
58329
  */
58208
- class UIPlugin extends BasePlugin {
58209
- static layers = [];
58330
+ class CoreViewPlugin extends BasePlugin {
58210
58331
  getters;
58211
- ui;
58212
- selection;
58213
- constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
58214
- super(stateObserver, dispatch, canDispatch);
58332
+ constructor({ getters, stateObserver }) {
58333
+ super(stateObserver);
58215
58334
  this.getters = getters;
58216
- this.ui = uiActions;
58217
- this.selection = selection;
58218
58335
  }
58219
- // ---------------------------------------------------------------------------
58220
- // Grid rendering
58221
- // ---------------------------------------------------------------------------
58222
- drawLayer(ctx, layer) { }
58223
58336
  }
58224
58337
 
58225
58338
  /**
@@ -59914,7 +60027,7 @@ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId
59914
60027
  // as necessary in several iterations, where evaluated cells can trigger the evaluation
59915
60028
  // of other cells depending on it, at the next iteration.
59916
60029
  //#endregion
59917
- class EvaluationPlugin extends UIPlugin {
60030
+ class EvaluationPlugin extends CoreViewPlugin {
59918
60031
  static getters = [
59919
60032
  "evaluateFormula",
59920
60033
  "evaluateFormulaResult",
@@ -60181,7 +60294,7 @@ function colorDistance(color1, color2) {
60181
60294
  * This plugins aims to compute and keep to custom colors used in the
60182
60295
  * current spreadsheet
60183
60296
  */
60184
- class CustomColorsPlugin extends UIPlugin {
60297
+ class CustomColorsPlugin extends CoreViewPlugin {
60185
60298
  customColors = {};
60186
60299
  shouldUpdateColors = true;
60187
60300
  static getters = ["getCustomColors"];
@@ -60317,7 +60430,7 @@ class CustomColorsPlugin extends UIPlugin {
60317
60430
  }
60318
60431
  }
60319
60432
 
60320
- class EvaluationChartPlugin extends UIPlugin {
60433
+ class EvaluationChartPlugin extends CoreViewPlugin {
60321
60434
  static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
60322
60435
  charts = {};
60323
60436
  createRuntimeChart = chartRuntimeFactory(this.getters);
@@ -60421,7 +60534,7 @@ class EvaluationChartPlugin extends UIPlugin {
60421
60534
  }
60422
60535
  }
60423
60536
 
60424
- class EvaluationConditionalFormatPlugin extends UIPlugin {
60537
+ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
60425
60538
  static getters = [
60426
60539
  "getConditionalIcon",
60427
60540
  "getCellConditionalFormatStyle",
@@ -60739,7 +60852,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
60739
60852
  }
60740
60853
 
60741
60854
  const VALID_RESULT = { isValid: true };
60742
- class EvaluationDataValidationPlugin extends UIPlugin {
60855
+ class EvaluationDataValidationPlugin extends CoreViewPlugin {
60743
60856
  static getters = [
60744
60857
  "getDataValidationInvalidCriterionValueMessage",
60745
60858
  "getInvalidDataValidationMessage",
@@ -60870,7 +60983,7 @@ class EvaluationDataValidationPlugin extends UIPlugin {
60870
60983
  }
60871
60984
  }
60872
60985
 
60873
- class DynamicTablesPlugin extends UIPlugin {
60986
+ class DynamicTablesPlugin extends CoreViewPlugin {
60874
60987
  static getters = [
60875
60988
  "canCreateDynamicTableOnZones",
60876
60989
  "doesZonesContainFilter",
@@ -61044,7 +61157,7 @@ class DynamicTablesPlugin extends UIPlugin {
61044
61157
  }
61045
61158
  }
61046
61159
 
61047
- class HeaderSizeUIPlugin extends UIPlugin {
61160
+ class HeaderSizeUIPlugin extends CoreViewPlugin {
61048
61161
  static getters = ["getRowSize", "getHeaderSize"];
61049
61162
  tallestCellInRow = {};
61050
61163
  ctx = document.createElement("canvas").getContext("2d");
@@ -61750,7 +61863,7 @@ const UNDO_REDO_PIVOT_COMMANDS = ["ADD_PIVOT", "UPDATE_PIVOT"];
61750
61863
  function isPivotCommand(cmd) {
61751
61864
  return UNDO_REDO_PIVOT_COMMANDS.includes(cmd.type);
61752
61865
  }
61753
- class PivotUIPlugin extends UIPlugin {
61866
+ class PivotUIPlugin extends CoreViewPlugin {
61754
61867
  static getters = [
61755
61868
  "getPivot",
61756
61869
  "getFirstPivotFunction",
@@ -61954,13 +62067,9 @@ class PivotUIPlugin extends UIPlugin {
61954
62067
  }
61955
62068
  generateNewCalculatedMeasureName(measures) {
61956
62069
  const existingMeasures = measures.map((m) => m.fieldName);
61957
- let i = 1;
61958
- let name = _t("Calculated measure %s", i);
61959
- while (existingMeasures.includes(name)) {
61960
- i++;
61961
- name = _t("Calculated measure %s", i);
61962
- }
61963
- return name;
62070
+ return getUniqueText(_t("Calculated measure 1"), existingMeasures, {
62071
+ compute: (name, i) => _t("Calculated measure %s", i),
62072
+ });
61964
62073
  }
61965
62074
  getPivot(pivotId) {
61966
62075
  if (!this.getters.isExistingPivot(pivotId)) {
@@ -62014,6 +62123,31 @@ class PivotUIPlugin extends UIPlugin {
62014
62123
  }
62015
62124
  }
62016
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
+
62017
62151
  /**
62018
62152
  * This plugin manage the autofill.
62019
62153
  *
@@ -64129,15 +64263,9 @@ class InsertPivotPlugin extends UIPlugin {
64129
64263
  }
64130
64264
  }
64131
64265
  getPivotDuplicateSheetName(pivotName) {
64132
- let i = 1;
64133
64266
  const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
64134
64267
  const sanitizedName = sanitizeSheetName(pivotName);
64135
- let name = sanitizedName;
64136
- while (names.includes(name)) {
64137
- name = `${sanitizedName} (${i})`;
64138
- i++;
64139
- }
64140
- return name;
64268
+ return getUniqueText(sanitizedName, names);
64141
64269
  }
64142
64270
  insertPivotWithTable(sheetId, col, row, pivotId, table, mode) {
64143
64271
  const { cols, rows, measures, fieldsType } = table;
@@ -66244,13 +66372,10 @@ class FilterEvaluationPlugin extends UIPlugin {
66244
66372
  if (!colName) {
66245
66373
  colName = `Column${colIndex}`;
66246
66374
  }
66247
- let currentColName = colName;
66248
- let i = 2;
66249
- while (usedColNames.includes(currentColName)) {
66250
- currentColName = colName + String(i);
66251
- i++;
66252
- }
66253
- return currentColName;
66375
+ return getUniqueText(colName, usedColNames, {
66376
+ compute: (name, i) => colName + String(i),
66377
+ start: 2,
66378
+ });
66254
66379
  }
66255
66380
  }
66256
66381
 
@@ -68902,23 +69027,17 @@ class ClickableCellsStore extends SpreadsheetStore {
68902
69027
  const cells = [];
68903
69028
  const getters = this.getters;
68904
69029
  const sheetId = getters.getActiveSheetId();
68905
- for (const col of getters.getSheetViewVisibleCols()) {
68906
- for (const row of getters.getSheetViewVisibleRows()) {
68907
- const position = { sheetId, col, row };
68908
- if (!getters.isMainCellPosition(position)) {
68909
- continue;
68910
- }
68911
- const action = this.getClickableAction(position);
68912
- if (!action) {
68913
- continue;
68914
- }
68915
- const zone = getters.expandZone(sheetId, positionToZone(position));
68916
- cells.push({
68917
- coordinates: getters.getVisibleRect(zone),
68918
- position,
68919
- action,
68920
- });
69030
+ for (const position of this.getters.getVisibleCellPositions()) {
69031
+ const action = this.getClickableAction(position);
69032
+ if (!action) {
69033
+ continue;
68921
69034
  }
69035
+ const zone = getters.expandZone(sheetId, positionToZone(position));
69036
+ cells.push({
69037
+ coordinates: getters.getVisibleRect(zone),
69038
+ position,
69039
+ action,
69040
+ });
68922
69041
  }
68923
69042
  return cells;
68924
69043
  }
@@ -74169,14 +74288,13 @@ function createRelRoot() {
74169
74288
  */
74170
74289
  function fixLengthySheetNames(data) {
74171
74290
  const nameMapping = {};
74172
- const newNames = new Set();
74291
+ const newNames = [];
74173
74292
  for (const sheet of data.sheets) {
74174
74293
  let newName = sheet.name.slice(0, 31);
74175
- let i = 1;
74176
- while (newNames.has(newName)) {
74177
- newName = newName.slice(0, 31 - String(i).length) + i++;
74178
- }
74179
- 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);
74180
74298
  if (newName !== sheet.name) {
74181
74299
  nameMapping[sheet.name] = newName;
74182
74300
  sheet.name = newName;
@@ -74241,6 +74359,7 @@ class Model extends EventBus {
74241
74359
  */
74242
74360
  config;
74243
74361
  corePluginConfig;
74362
+ coreViewPluginConfig;
74244
74363
  uiPluginConfig;
74245
74364
  state;
74246
74365
  selection;
@@ -74293,6 +74412,7 @@ class Model extends EventBus {
74293
74412
  this.coreHandlers.push(this.range);
74294
74413
  this.handlers.push(this.range);
74295
74414
  this.corePluginConfig = this.setupCorePluginConfig();
74415
+ this.coreViewPluginConfig = this.setupCoreViewPluginConfig();
74296
74416
  this.uiPluginConfig = this.setupUiPluginConfig();
74297
74417
  // registering plugins
74298
74418
  for (let Plugin of corePluginRegistry.getAll()) {
@@ -74301,7 +74421,7 @@ class Model extends EventBus {
74301
74421
  Object.assign(this.getters, this.coreGetters);
74302
74422
  this.session.loadInitialMessages(stateUpdateMessages);
74303
74423
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
74304
- const plugin = this.setupUiPlugin(Plugin);
74424
+ const plugin = this.setupCoreViewPlugin(Plugin);
74305
74425
  this.handlers.push(plugin);
74306
74426
  this.uiHandlers.push(plugin);
74307
74427
  this.coreHandlers.push(plugin);
@@ -74367,6 +74487,19 @@ class Model extends EventBus {
74367
74487
  }
74368
74488
  return plugin;
74369
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
+ }
74370
74503
  /**
74371
74504
  * Initialize and properly configure a plugin.
74372
74505
  *
@@ -74468,6 +74601,20 @@ class Model extends EventBus {
74468
74601
  external: this.config.external,
74469
74602
  };
74470
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
+ }
74471
74618
  setupUiPluginConfig() {
74472
74619
  return {
74473
74620
  getters: this.getters,
@@ -74815,6 +74962,7 @@ const helpers = {
74815
74962
  areDomainArgsFieldsValid,
74816
74963
  splitReference,
74817
74964
  sanitizeSheetName,
74965
+ getUniqueText,
74818
74966
  isNumber,
74819
74967
  isDateTime,
74820
74968
  };
@@ -74908,9 +75056,9 @@ const constants = {
74908
75056
  };
74909
75057
  const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
74910
75058
 
74911
- 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 };
74912
75060
 
74913
75061
 
74914
- __info__.version = "18.1.1";
74915
- __info__.date = "2025-01-14T11:43:19.116Z";
74916
- __info__.hash = "d0fa5de";
75062
+ __info__.version = "18.2.0-alpha.1";
75063
+ __info__.date = "2025-01-14T11:35:51.135Z";
75064
+ __info__.hash = "702f816";