@odoo/o-spreadsheet 18.1.0-alpha.5 → 18.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.1.0-alpha.5
6
- * @date 2024-11-22T14:22:50.899Z
7
- * @hash e13bd67
5
+ * @version 18.1.0-alpha.7
6
+ * @date 2024-12-05T10:40:26.512Z
7
+ * @hash 7b1c39b
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -171,10 +171,13 @@
171
171
  const ALERT_INFO_BORDER = "#98DBE2";
172
172
  const ALERT_INFO_TEXT_COLOR = "#09414A";
173
173
  const BADGE_SELECTED_COLOR = "#E6F2F3";
174
- const DEFAULT_CHART_PADDING = 20;
175
- const DEFAULT_CHART_FONT_SIZE = 22;
176
- const SCORECARD_GAUGE_CHART_PADDING = 10;
177
- const SCORECARD_GAUGE_CHART_FONT_SIZE = 14;
174
+ const CHART_PADDING$1 = 20;
175
+ const CHART_PADDING_BOTTOM = 10;
176
+ const CHART_PADDING_TOP = 15;
177
+ const CHART_TITLE_FONT_SIZE = 16;
178
+ const CHART_AXIS_TITLE_FONT_SIZE = 12;
179
+ const SCORECARD_CHART_TITLE_FONT_SIZE = 14;
180
+ const PIVOT_TOKEN_COLOR = "#F28C28";
178
181
  // Color picker defaults as upper case HEX to match `toHex`helper
179
182
  const COLOR_PICKER_DEFAULTS = [
180
183
  "#000000",
@@ -334,8 +337,8 @@
334
337
  const DEBOUNCE_TIME = 200;
335
338
  const MESSAGE_VERSION = 1;
336
339
  // Sheets
337
- const FORBIDDEN_SHEET_CHARS = ["'", "*", "?", "/", "\\", "[", "]"];
338
- const FORBIDDEN_IN_EXCEL_REGEX = /'|\*|\?|\/|\\|\[|\]/;
340
+ const FORBIDDEN_SHEETNAME_CHARS = ["'", "*", "?", "/", "\\", "[", "]"];
341
+ const FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX = /'|\*|\?|\/|\\|\[|\]/;
339
342
  // Cells
340
343
  const FORMULA_REF_IDENTIFIER = "|";
341
344
  // Components
@@ -388,6 +391,7 @@
388
391
  //------------------------------------------------------------------------------
389
392
  // Miscellaneous
390
393
  //------------------------------------------------------------------------------
394
+ const sanitizeSheetNameRegex = new RegExp(FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX, "g");
391
395
  /**
392
396
  * Remove quotes from a quoted string
393
397
  * ```js
@@ -483,6 +487,10 @@
483
487
  }
484
488
  return symbolName;
485
489
  }
490
+ /** Replace the excel-excluded characters of a sheetName */
491
+ function sanitizeSheetName(sheetName, replacementChar = " ") {
492
+ return sheetName.replace(sanitizeSheetNameRegex, replacementChar);
493
+ }
486
494
  function clip(val, min, max) {
487
495
  return val < min ? min : val > max ? max : val;
488
496
  }
@@ -3497,7 +3505,9 @@
3497
3505
  CommandResult["MaxInvalidFormula"] = "MaxInvalidFormula";
3498
3506
  CommandResult["ValueUpperInvalidFormula"] = "ValueUpperInvalidFormula";
3499
3507
  CommandResult["ValueLowerInvalidFormula"] = "ValueLowerInvalidFormula";
3508
+ CommandResult["InvalidSortAnchor"] = "InvalidSortAnchor";
3500
3509
  CommandResult["InvalidSortZone"] = "InvalidSortZone";
3510
+ CommandResult["SortZoneWithArrayFormulas"] = "SortZoneWithArrayFormulas";
3501
3511
  CommandResult["WaitingSessionConfirmation"] = "WaitingSessionConfirmation";
3502
3512
  CommandResult["MergeOverlap"] = "MergeOverlap";
3503
3513
  CommandResult["TooManyHiddenElements"] = "TooManyHiddenElements";
@@ -3553,7 +3563,6 @@
3553
3563
  CommandResult["ValueCellIsInvalidFormula"] = "ValueCellIsInvalidFormula";
3554
3564
  CommandResult["InvalidDefinition"] = "InvalidDefinition";
3555
3565
  CommandResult["InvalidColor"] = "InvalidColor";
3556
- CommandResult["DataBarRangeValuesMismatch"] = "DataBarRangeValuesMismatch";
3557
3566
  })(exports.CommandResult || (exports.CommandResult = {}));
3558
3567
 
3559
3568
  const DEFAULT_LOCALES = [
@@ -9514,6 +9523,12 @@ stores.inject(MyMetaStore, storeInstance);
9514
9523
  }
9515
9524
  return relativeLuminance(backgroundColor) < 0.3 ? "#FFFFFF" : "#000000";
9516
9525
  }
9526
+ function chartMutedFontColor(backgroundColor) {
9527
+ if (!backgroundColor) {
9528
+ return "#666666";
9529
+ }
9530
+ return relativeLuminance(backgroundColor) < 0.3 ? "#C8C8C8" : "#666666";
9531
+ }
9517
9532
  function checkDataset(definition) {
9518
9533
  if (definition.dataSets) {
9519
9534
  const invalidRanges = definition.dataSets.find((range) => !rangeReference.test(range.dataRange)) !== undefined;
@@ -9649,8 +9664,8 @@ stores.inject(MyMetaStore, storeInstance);
9649
9664
  const yMin = chart.chartArea.top;
9650
9665
  const textsPositions = {};
9651
9666
  for (const dataset of chart._metasets) {
9652
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9653
- return; // ignore trend lines
9667
+ if (dataset.xAxisID === TREND_LINE_XAXIS_ID || dataset.hidden) {
9668
+ continue;
9654
9669
  }
9655
9670
  for (let i = 0; i < dataset._parsed.length; i++) {
9656
9671
  const parsedValue = dataset._parsed[i];
@@ -10097,7 +10112,7 @@ stores.inject(MyMetaStore, storeInstance);
10097
10112
  function drawScoreChart(structure, canvas) {
10098
10113
  const ctx = canvas.getContext("2d");
10099
10114
  canvas.width = structure.canvas.width;
10100
- const availableWidth = canvas.width - DEFAULT_CHART_PADDING;
10115
+ const availableWidth = canvas.width - CHART_PADDING$1 * 2;
10101
10116
  canvas.height = structure.canvas.height;
10102
10117
  ctx.fillStyle = structure.canvas.backgroundColor;
10103
10118
  ctx.fillRect(0, 0, structure.canvas.width, structure.canvas.height);
@@ -10232,10 +10247,9 @@ stores.inject(MyMetaStore, storeInstance);
10232
10247
  }
10233
10248
 
10234
10249
  /* Padding at the border of the chart */
10235
- const CHART_PADDING = SCORECARD_GAUGE_CHART_PADDING;
10250
+ const CHART_PADDING = 10;
10236
10251
  const BOTTOM_PADDING_RATIO = 0.05;
10237
10252
  /* Maximum font sizes of each element */
10238
- const CHART_TITLE_FONT_SIZE = SCORECARD_GAUGE_CHART_FONT_SIZE;
10239
10253
  const KEY_VALUE_FONT_SIZE = 32;
10240
10254
  const BASELINE_MAX_FONT_SIZE = 16;
10241
10255
  function formatBaselineDescr(baselineDescr, baseline) {
@@ -10307,7 +10321,7 @@ stores.inject(MyMetaStore, storeInstance);
10307
10321
  : this.height - (this.height - titleHeight - baselineHeight) / 2 - CHART_PADDING,
10308
10322
  },
10309
10323
  };
10310
- const minimalBaselinePosition = baselineArrowSize + DEFAULT_CHART_PADDING;
10324
+ const minimalBaselinePosition = baselineArrowSize + CHART_PADDING * 2;
10311
10325
  if (structure.baseline.position.x < minimalBaselinePosition) {
10312
10326
  structure.baseline.position.x = minimalBaselinePosition;
10313
10327
  }
@@ -10387,7 +10401,7 @@ stores.inject(MyMetaStore, storeInstance);
10387
10401
  return this.runtime.background;
10388
10402
  }
10389
10403
  get secondaryFontColor() {
10390
- return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
10404
+ return chartMutedFontColor(this.backgroundColor);
10391
10405
  }
10392
10406
  getTextDimensions(text, font) {
10393
10407
  this.context.font = font;
@@ -10413,7 +10427,7 @@ stores.inject(MyMetaStore, storeInstance);
10413
10427
  }
10414
10428
  return {
10415
10429
  title: {
10416
- font: getDefaultContextFont(CHART_TITLE_FONT_SIZE, this.runtime.title.bold, this.runtime.title.italic),
10430
+ font: getDefaultContextFont(this.runtime.title.fontSize ?? SCORECARD_CHART_TITLE_FONT_SIZE, this.runtime.title.bold, this.runtime.title.italic),
10417
10431
  color: this.runtime.title.color ?? this.secondaryFontColor,
10418
10432
  },
10419
10433
  keyValue: {
@@ -14670,7 +14684,6 @@ stores.inject(MyMetaStore, storeInstance);
14670
14684
  return cellsToSort.sort(cellsSortingCriterion(sortDirection));
14671
14685
  }
14672
14686
  function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
14673
- let result = DispatchResult.Success;
14674
14687
  //several columns => bypass the contiguity check
14675
14688
  let multiColumns = zone.right > zone.left;
14676
14689
  if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
@@ -14690,49 +14703,37 @@ stores.inject(MyMetaStore, storeInstance);
14690
14703
  }
14691
14704
  }
14692
14705
  }
14693
- const { col, row } = anchor;
14694
14706
  if (multiColumns) {
14695
- result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
14707
+ interactiveSort(env, sheetId, anchor, zone, sortDirection);
14708
+ return;
14709
+ }
14710
+ const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
14711
+ if (isEqual(contiguousZone, zone)) {
14712
+ interactiveSort(env, sheetId, anchor, zone, sortDirection);
14696
14713
  }
14697
14714
  else {
14698
- // check contiguity
14699
- const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
14700
- if (isEqual(contiguousZone, zone)) {
14701
- // merge as it is
14702
- result = env.model.dispatch("SORT_CELLS", {
14703
- sheetId,
14704
- col,
14705
- row,
14706
- zone,
14707
- sortDirection,
14708
- });
14709
- }
14710
- else {
14711
- env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
14712
- zone = contiguousZone;
14713
- result = env.model.dispatch("SORT_CELLS", {
14714
- sheetId,
14715
- col,
14716
- row,
14717
- zone,
14718
- sortDirection,
14719
- });
14720
- }, () => {
14721
- result = env.model.dispatch("SORT_CELLS", {
14722
- sheetId,
14723
- col,
14724
- row,
14725
- zone,
14726
- sortDirection,
14727
- });
14728
- });
14729
- }
14715
+ env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => interactiveSort(env, sheetId, anchor, contiguousZone, sortDirection), () => interactiveSort(env, sheetId, anchor, zone, sortDirection));
14730
14716
  }
14717
+ }
14718
+ function interactiveSort(env, sheetId, anchor, zone, sortDirection, sortOptions) {
14719
+ const result = env.model.dispatch("SORT_CELLS", {
14720
+ sheetId,
14721
+ col: anchor.col,
14722
+ row: anchor.row,
14723
+ zone,
14724
+ sortDirection,
14725
+ sortOptions,
14726
+ });
14731
14727
  if (result.isCancelledBecause("InvalidSortZone" /* CommandResult.InvalidSortZone */)) {
14732
14728
  const { col, row } = anchor;
14733
14729
  env.model.selection.selectZone({ cell: { col, row }, zone });
14734
14730
  env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
14735
14731
  }
14732
+ if (result.isCancelledBecause("SortZoneWithArrayFormulas" /* CommandResult.SortZoneWithArrayFormulas */)) {
14733
+ const { col, row } = anchor;
14734
+ env.model.selection.selectZone({ cell: { col, row }, zone });
14735
+ env.raiseError(_t("Cannot sort a zone with array formulas."));
14736
+ }
14736
14737
  }
14737
14738
 
14738
14739
  function sortMatrix(matrix, locale, ...criteria) {
@@ -21247,7 +21248,7 @@ stores.inject(MyMetaStore, storeInstance);
21247
21248
 
21248
21249
  const PIVOT_FUNCTIONS = ["PIVOT.VALUE", "PIVOT.HEADER", "PIVOT"];
21249
21250
  /**
21250
- * Create a proposal entry for the compose autowcomplete
21251
+ * Create a proposal entry for the composer autocomplete
21251
21252
  * to insert a field name string in a formula.
21252
21253
  */
21253
21254
  function makeFieldProposal(field, granularity) {
@@ -22030,14 +22031,8 @@ stores.inject(MyMetaStore, storeInstance);
22030
22031
  const GAUGE_LABELS_FONT_SIZE = 12;
22031
22032
  const GAUGE_DEFAULT_VALUE_FONT_SIZE = 80;
22032
22033
  const GAUGE_BACKGROUND_COLOR = "#F3F2F1";
22033
- const GAUGE_TEXT_COLOR = "#666666";
22034
- const GAUGE_TEXT_COLOR_HIGH_CONTRAST = "#C8C8C8";
22035
- const GAUGE_INFLECTION_MARKER_COLOR = "#666666aa";
22036
22034
  const GAUGE_INFLECTION_LABEL_BOTTOM_MARGIN = 6;
22037
22035
  const GAUGE_TITLE_SECTION_HEIGHT = 25;
22038
- const GAUGE_TITLE_FONT_SIZE = SCORECARD_GAUGE_CHART_FONT_SIZE;
22039
- const GAUGE_TITLE_PADDING_LEFT = SCORECARD_GAUGE_CHART_PADDING;
22040
- const GAUGE_TITLE_PADDING_TOP = SCORECARD_GAUGE_CHART_PADDING;
22041
22036
  function drawGaugeChart(canvas, runtime) {
22042
22037
  const canvasBoundingRect = canvas.getBoundingClientRect();
22043
22038
  canvas.width = canvasBoundingRect.width;
@@ -22096,7 +22091,7 @@ stores.inject(MyMetaStore, storeInstance);
22096
22091
  ctx.translate(rectX + width / 2 - 0.5, rectY + height - 0.5); // -0.5 for sharper lines. see RendererPlugin.drawBorders comment
22097
22092
  ctx.rotate(Math.PI / 2 - inflectionValue.rotation);
22098
22093
  ctx.lineWidth = 2;
22099
- ctx.strokeStyle = GAUGE_INFLECTION_MARKER_COLOR;
22094
+ ctx.strokeStyle = chartMutedFontColor(config.backgroundColor) + "aa";
22100
22095
  ctx.beginPath();
22101
22096
  ctx.moveTo(0, -(height - config.gauge.arcWidth));
22102
22097
  ctx.lineTo(0, -height - 3);
@@ -22150,22 +22145,22 @@ stores.inject(MyMetaStore, storeInstance);
22150
22145
  x: gaugeRect.x + gaugeRect.width - gaugeArcWidth / 2,
22151
22146
  y: gaugeRect.y + gaugeRect.height + GAUGE_LABELS_FONT_SIZE,
22152
22147
  };
22153
- const textColor = getContrastedTextColor(runtime.background);
22148
+ const textColor = chartMutedFontColor(runtime.background);
22154
22149
  const inflectionValues = getInflectionValues(runtime, gaugeRect, textColor, ctx);
22155
22150
  let x = 0, titleWidth = 0, titleHeight = 0;
22156
22151
  if (runtime.title.text) {
22157
- ({ width: titleWidth, height: titleHeight } = computeTextDimension(ctx, runtime.title.text, { ...runtime.title, fontSize: GAUGE_TITLE_FONT_SIZE }, "px"));
22152
+ ({ width: titleWidth, height: titleHeight } = computeTextDimension(ctx, runtime.title.text, { fontSize: CHART_TITLE_FONT_SIZE, ...runtime.title }, "px"));
22158
22153
  }
22159
22154
  switch (runtime.title.align) {
22160
22155
  case "right":
22161
- x = boundingRect.width - titleWidth - GAUGE_TITLE_PADDING_LEFT;
22156
+ x = boundingRect.width - titleWidth - CHART_PADDING$1;
22162
22157
  break;
22163
22158
  case "center":
22164
22159
  x = (boundingRect.width - titleWidth) / 2;
22165
22160
  break;
22166
22161
  case "left":
22167
22162
  default:
22168
- x = GAUGE_TITLE_PADDING_LEFT;
22163
+ x = CHART_PADDING$1;
22169
22164
  break;
22170
22165
  }
22171
22166
  return {
@@ -22173,10 +22168,10 @@ stores.inject(MyMetaStore, storeInstance);
22173
22168
  height: boundingRect.height,
22174
22169
  title: {
22175
22170
  label: runtime.title.text ?? "",
22176
- fontSize: GAUGE_TITLE_FONT_SIZE,
22171
+ fontSize: runtime.title.fontSize ?? CHART_TITLE_FONT_SIZE,
22177
22172
  textPosition: {
22178
22173
  x,
22179
- y: GAUGE_TITLE_PADDING_TOP + titleHeight / 2,
22174
+ y: CHART_PADDING_TOP + titleHeight / 2,
22180
22175
  },
22181
22176
  color: runtime.title.color ?? textColor,
22182
22177
  bold: runtime.title.bold,
@@ -22293,11 +22288,6 @@ stores.inject(MyMetaStore, storeInstance);
22293
22288
  }
22294
22289
  return runtime.colors.at(-1);
22295
22290
  }
22296
- function getContrastedTextColor(backgroundColor) {
22297
- return relativeLuminance(backgroundColor) > 0.3
22298
- ? GAUGE_TEXT_COLOR
22299
- : GAUGE_TEXT_COLOR_HIGH_CONTRAST;
22300
- }
22301
22291
  function getSegmentsOfRectangle(rectangle) {
22302
22292
  return [
22303
22293
  { start: rectangle.topLeft, end: rectangle.topRight },
@@ -27011,13 +27001,12 @@ stores.inject(MyMetaStore, storeInstance);
27011
27001
  versionFrom: "7",
27012
27002
  migrate(data) {
27013
27003
  const namesTaken = [];
27014
- const globalForbiddenInExcel = new RegExp(FORBIDDEN_IN_EXCEL_REGEX, "g");
27015
27004
  for (let sheet of data.sheets || []) {
27016
27005
  if (!sheet.name) {
27017
27006
  continue;
27018
27007
  }
27019
27008
  const oldName = sheet.name;
27020
- const escapedName = oldName.replace(globalForbiddenInExcel, "_");
27009
+ const escapedName = sanitizeSheetName(oldName, "_");
27021
27010
  let i = 1;
27022
27011
  let newName = escapedName;
27023
27012
  while (namesTaken.includes(newName)) {
@@ -27720,7 +27709,6 @@ stores.inject(MyMetaStore, storeInstance);
27720
27709
  ["ValueLowerInvalidFormula" /* CommandResult.ValueLowerInvalidFormula */]: _t("Invalid lower inflection point formula"),
27721
27710
  ["EmptyRange" /* CommandResult.EmptyRange */]: _t("A range needs to be defined"),
27722
27711
  ["ValueCellIsInvalidFormula" /* CommandResult.ValueCellIsInvalidFormula */]: _t("At least one of the provided values is an invalid formula"),
27723
- ["DataBarRangeValuesMismatch" /* CommandResult.DataBarRangeValuesMismatch */]: _t("All the ranges and the range values must have the same size"),
27724
27712
  Unexpected: _t("The rule is invalid for an unknown reason"),
27725
27713
  },
27726
27714
  ColorScale: _t("Color scale"),
@@ -28254,37 +28242,45 @@ stores.inject(MyMetaStore, storeInstance);
28254
28242
  const labelRange = labelMax - labelMin;
28255
28243
  const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
28256
28244
  const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
28257
- switch (config.type) {
28258
- case "polynomial": {
28259
- const order = config.order ?? 2;
28260
- if (order === 1) {
28261
- return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0];
28262
- }
28263
- const coeffs = polynomialRegression(values, normalizedLabels, order, true).flat();
28264
- return normalizedNewLabels.map((v) => evaluatePolynomial(coeffs, v, order));
28265
- }
28266
- case "exponential": {
28267
- const positiveLogValues = [];
28268
- const filteredLabels = [];
28269
- for (let i = 0; i < values.length; i++) {
28270
- if (values[i] > 0) {
28271
- positiveLogValues.push(Math.log(values[i]));
28272
- filteredLabels.push(normalizedLabels[i]);
28245
+ try {
28246
+ switch (config.type) {
28247
+ case "polynomial": {
28248
+ const order = config.order;
28249
+ if (!order) {
28250
+ return Array.from({ length: newLabels.length }, () => NaN);
28251
+ }
28252
+ if (order === 1) {
28253
+ return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0];
28254
+ }
28255
+ const coeffs = polynomialRegression(values, normalizedLabels, order, true).flat();
28256
+ return normalizedNewLabels.map((v) => evaluatePolynomial(coeffs, v, order));
28257
+ }
28258
+ case "exponential": {
28259
+ const positiveLogValues = [];
28260
+ const filteredLabels = [];
28261
+ for (let i = 0; i < values.length; i++) {
28262
+ if (values[i] > 0) {
28263
+ positiveLogValues.push(Math.log(values[i]));
28264
+ filteredLabels.push(normalizedLabels[i]);
28265
+ }
28266
+ }
28267
+ if (!filteredLabels.length) {
28268
+ return Array.from({ length: newLabels.length }, () => NaN);
28273
28269
  }
28270
+ return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0];
28274
28271
  }
28275
- if (!filteredLabels.length) {
28276
- return [];
28272
+ case "logarithmic": {
28273
+ return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0];
28277
28274
  }
28278
- return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0];
28279
- }
28280
- case "logarithmic": {
28281
- return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0];
28282
- }
28283
- case "trailingMovingAverage": {
28284
- return getMovingAverageValues(values, config.window);
28275
+ case "trailingMovingAverage": {
28276
+ return getMovingAverageValues(values, config.window);
28277
+ }
28278
+ default:
28279
+ return Array.from({ length: newLabels.length }, () => NaN);
28285
28280
  }
28286
- default:
28287
- return [];
28281
+ }
28282
+ catch (e) {
28283
+ return Array.from({ length: newLabels.length }, () => NaN);
28288
28284
  }
28289
28285
  }
28290
28286
  function getChartAxisType(chart, labelRange, getters) {
@@ -28755,54 +28751,16 @@ stores.inject(MyMetaStore, storeInstance);
28755
28751
  return new ColorGenerator(dataSetsSize, definition.dataSets?.map((ds) => ds.backgroundColor) || []);
28756
28752
  }
28757
28753
 
28758
- function getCommonChartLayout(definition) {
28759
- // TODO FIXME: this is unused ATM. All the charts should probably use this instead oh whatever padding they are using now
28760
- // also look into how DEFAULT_CHART_PADDING is used in scorecards, it look strange
28754
+ function getChartLayout(definition) {
28761
28755
  return {
28762
28756
  padding: {
28763
- left: DEFAULT_CHART_PADDING,
28764
- right: DEFAULT_CHART_PADDING,
28765
- top: definition.title?.text ? DEFAULT_CHART_PADDING / 2 : DEFAULT_CHART_PADDING + 5,
28766
- bottom: DEFAULT_CHART_PADDING,
28757
+ left: CHART_PADDING$1,
28758
+ right: CHART_PADDING$1,
28759
+ top: CHART_PADDING_TOP,
28760
+ bottom: CHART_PADDING_BOTTOM,
28767
28761
  },
28768
28762
  };
28769
28763
  }
28770
- function getBarChartLayout(definition) {
28771
- return {
28772
- padding: computeChartPadding({
28773
- displayTitle: !!definition.title?.text,
28774
- displayLegend: definition.legendPosition === "top",
28775
- }),
28776
- };
28777
- }
28778
- function getLineChartLayout(definition) {
28779
- return {
28780
- padding: computeChartPadding({
28781
- displayTitle: !!definition.title?.text,
28782
- displayLegend: definition.legendPosition === "top",
28783
- }),
28784
- };
28785
- }
28786
- function getPieChartLayout(definition) {
28787
- return {
28788
- padding: { left: 20, right: 20, top: definition.title ? 10 : 25, bottom: 10 },
28789
- };
28790
- }
28791
- function getWaterfallChartLayout(definition) {
28792
- return {
28793
- padding: { left: 20, right: 20, top: definition.title ? 10 : 25, bottom: 10 },
28794
- };
28795
- }
28796
- function computeChartPadding({ displayTitle, displayLegend, }) {
28797
- let top = 25;
28798
- if (displayTitle) {
28799
- top = 0;
28800
- }
28801
- else if (displayLegend) {
28802
- top = 10;
28803
- }
28804
- return { left: 20, right: 20, top, bottom: 10 };
28805
- }
28806
28764
 
28807
28765
  function getLegendDisplayOptions(definition, args) {
28808
28766
  return {
@@ -28860,11 +28818,12 @@ stores.inject(MyMetaStore, storeInstance);
28860
28818
  return {
28861
28819
  ...INTERACTIVE_LEGEND_CONFIG,
28862
28820
  ...getLegendDisplayOptions(definition),
28863
- labels: {
28864
- color: chartFontColor(definition.background),
28865
- boxHeight: 6,
28866
- usePointStyle: true,
28867
- },
28821
+ ...getCustomLegendLabels(chartFontColor(definition.background), {
28822
+ pointStyle: "circle",
28823
+ // the stroke is the border around the circle, so increasing its size with the chart's color reduce the size of the circle
28824
+ strokeStyle: definition.background || "#ffffff",
28825
+ lineWidth: 8,
28826
+ }),
28868
28827
  };
28869
28828
  }
28870
28829
  function getComboChartLegend(definition, args) {
@@ -28953,10 +28912,10 @@ stores.inject(MyMetaStore, storeInstance);
28953
28912
  target.style.cursor = "default";
28954
28913
  },
28955
28914
  onClick: (event, legendItem, legend) => {
28956
- if (!legend.legendItems) {
28915
+ const index = legendItem.datasetIndex;
28916
+ if (!legend.legendItems || index === undefined) {
28957
28917
  return;
28958
28918
  }
28959
- const index = legend.legendItems.indexOf(legendItem);
28960
28919
  if (legend.chart.isDatasetVisible(index)) {
28961
28920
  legend.chart.hide(index);
28962
28921
  }
@@ -28972,15 +28931,29 @@ stores.inject(MyMetaStore, storeInstance);
28972
28931
  labels: {
28973
28932
  color: fontColor,
28974
28933
  usePointStyle: true,
28975
- generateLabels: (chart) => chart.data.datasets.map((dataset, index) => ({
28976
- text: dataset.label ?? "",
28977
- fontColor,
28978
- strokeStyle: dataset.borderColor,
28979
- fillStyle: dataset.backgroundColor,
28980
- hidden: !chart.isDatasetVisible(index),
28981
- pointStyle: dataset.type === "line" ? "line" : "rect",
28982
- ...legendLabelConfig,
28983
- })),
28934
+ generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
28935
+ if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
28936
+ return {
28937
+ text: dataset.label ?? "",
28938
+ fontColor,
28939
+ strokeStyle: dataset.borderColor,
28940
+ hidden: !chart.isDatasetVisible(index),
28941
+ pointStyle: "line",
28942
+ datasetIndex: index,
28943
+ lineWidth: 3,
28944
+ };
28945
+ }
28946
+ return {
28947
+ text: dataset.label ?? "",
28948
+ fontColor,
28949
+ strokeStyle: dataset.borderColor,
28950
+ fillStyle: dataset.backgroundColor,
28951
+ hidden: !chart.isDatasetVisible(index),
28952
+ pointStyle: dataset.type === "line" ? "line" : "rect",
28953
+ datasetIndex: index,
28954
+ ...legendLabelConfig,
28955
+ };
28956
+ }),
28984
28957
  },
28985
28958
  };
28986
28959
  }
@@ -29094,20 +29067,27 @@ stores.inject(MyMetaStore, storeInstance);
29094
29067
  return scales;
29095
29068
  }
29096
29069
  function getPyramidChartScales(definition, args) {
29070
+ const { dataSetsValues } = args;
29097
29071
  const scales = getBarChartScales(definition, args);
29098
29072
  const scalesXCallback = scales.x.ticks.callback;
29099
29073
  scales.x.ticks.callback = (value) => scalesXCallback(Math.abs(value));
29074
+ const maxValue = Math.max(...dataSetsValues.map((dataSet) => Math.max(...dataSet.data.map(Math.abs))));
29075
+ scales.x.suggestedMin = -maxValue;
29076
+ scales.x.suggestedMax = maxValue;
29100
29077
  return scales;
29101
29078
  }
29102
29079
  function getRadarChartScales(definition, args) {
29103
- const { locale, axisFormats } = args;
29080
+ const { locale, axisFormats, dataSetsValues } = args;
29081
+ const minValue = Math.min(...dataSetsValues.map((ds) => Math.min(...ds.data.filter((x) => !isNaN(x)))));
29104
29082
  return {
29105
29083
  r: {
29084
+ beginAtZero: true,
29106
29085
  ticks: {
29107
29086
  callback: formatTickValue({ format: axisFormats?.r, locale }),
29108
29087
  backdropColor: definition.background || "#FFFFFF",
29109
29088
  },
29110
29089
  pointLabels: { color: chartFontColor(definition.background) },
29090
+ suggestedMin: minValue < 0 ? minValue - 1 : 0,
29111
29091
  },
29112
29092
  };
29113
29093
  }
@@ -29121,6 +29101,7 @@ stores.inject(MyMetaStore, storeInstance);
29121
29101
  font: {
29122
29102
  style: italic ? "italic" : "normal",
29123
29103
  weight: bold ? "bold" : "normal",
29104
+ size: design.title.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE,
29124
29105
  },
29125
29106
  align: align === "left" ? "start" : align === "right" ? "end" : "center",
29126
29107
  };
@@ -29186,17 +29167,22 @@ stores.inject(MyMetaStore, storeInstance);
29186
29167
 
29187
29168
  function getChartTitle(definition) {
29188
29169
  const chartTitle = definition.title;
29189
- const fontColor = chartFontColor(definition.background);
29170
+ const fontColor = chartMutedFontColor(definition.background);
29190
29171
  return {
29191
29172
  display: !!chartTitle.text,
29192
29173
  text: _t(chartTitle.text),
29193
29174
  color: chartTitle?.color ?? fontColor,
29194
29175
  align: chartTitle.align === "center" ? "center" : chartTitle.align === "right" ? "end" : "start",
29195
29176
  font: {
29196
- size: DEFAULT_CHART_FONT_SIZE,
29177
+ size: definition.title.fontSize ?? CHART_TITLE_FONT_SIZE,
29197
29178
  weight: chartTitle.bold ? "bold" : "normal",
29198
29179
  style: chartTitle.italic ? "italic" : "normal",
29199
29180
  },
29181
+ padding: {
29182
+ // Disable title top/left/right padding to use the chart padding instead.
29183
+ // The legend already has a top padding, so bottom padding is useless for the title there.
29184
+ bottom: definition.legendPosition === "top" ? 0 : CHART_PADDING$1,
29185
+ },
29200
29186
  };
29201
29187
  }
29202
29188
 
@@ -29343,26 +29329,23 @@ stores.inject(MyMetaStore, storeInstance);
29343
29329
  canChartParseLabels: canChartParseLabels,
29344
29330
  getBarChartData: getBarChartData,
29345
29331
  getBarChartDatasets: getBarChartDatasets,
29346
- getBarChartLayout: getBarChartLayout,
29347
29332
  getBarChartLegend: getBarChartLegend,
29348
29333
  getBarChartScales: getBarChartScales,
29349
29334
  getBarChartTooltip: getBarChartTooltip,
29350
29335
  getChartLabelFormat: getChartLabelFormat,
29336
+ getChartLayout: getChartLayout,
29351
29337
  getChartShowValues: getChartShowValues,
29352
29338
  getChartTitle: getChartTitle,
29353
29339
  getComboChartDatasets: getComboChartDatasets,
29354
29340
  getComboChartLegend: getComboChartLegend,
29355
- getCommonChartLayout: getCommonChartLayout,
29356
29341
  getData: getData,
29357
29342
  getLineChartData: getLineChartData,
29358
29343
  getLineChartDatasets: getLineChartDatasets,
29359
- getLineChartLayout: getLineChartLayout,
29360
29344
  getLineChartLegend: getLineChartLegend,
29361
29345
  getLineChartScales: getLineChartScales,
29362
29346
  getLineChartTooltip: getLineChartTooltip,
29363
29347
  getPieChartData: getPieChartData,
29364
29348
  getPieChartDatasets: getPieChartDatasets,
29365
- getPieChartLayout: getPieChartLayout,
29366
29349
  getPieChartLegend: getPieChartLegend,
29367
29350
  getPieChartTooltip: getPieChartTooltip,
29368
29351
  getPyramidChartData: getPyramidChartData,
@@ -29378,7 +29361,6 @@ stores.inject(MyMetaStore, storeInstance);
29378
29361
  getScatterChartScales: getScatterChartScales,
29379
29362
  getTrendDatasetForBarChart: getTrendDatasetForBarChart,
29380
29363
  getTrendDatasetForLineChart: getTrendDatasetForLineChart,
29381
- getWaterfallChartLayout: getWaterfallChartLayout,
29382
29364
  getWaterfallChartLegend: getWaterfallChartLegend,
29383
29365
  getWaterfallChartScales: getWaterfallChartScales,
29384
29366
  getWaterfallChartTooltip: getWaterfallChartTooltip,
@@ -29526,7 +29508,7 @@ stores.inject(MyMetaStore, storeInstance);
29526
29508
  options: {
29527
29509
  ...CHART_COMMON_OPTIONS,
29528
29510
  indexAxis: chart.horizontal ? "y" : "x",
29529
- layout: getBarChartLayout(definition),
29511
+ layout: getChartLayout(),
29530
29512
  scales: getBarChartScales(definition, chartData),
29531
29513
  plugins: {
29532
29514
  title: getChartTitle(definition),
@@ -29678,7 +29660,7 @@ stores.inject(MyMetaStore, storeInstance);
29678
29660
  },
29679
29661
  options: {
29680
29662
  ...CHART_COMMON_OPTIONS,
29681
- layout: getBarChartLayout(definition),
29663
+ layout: getChartLayout(),
29682
29664
  scales: getBarChartScales(definition, chartData),
29683
29665
  plugins: {
29684
29666
  title: getChartTitle(definition),
@@ -30079,7 +30061,7 @@ stores.inject(MyMetaStore, storeInstance);
30079
30061
  },
30080
30062
  options: {
30081
30063
  ...CHART_COMMON_OPTIONS,
30082
- layout: getLineChartLayout(definition),
30064
+ layout: getChartLayout(),
30083
30065
  scales: getLineChartScales(definition, chartData),
30084
30066
  plugins: {
30085
30067
  title: getChartTitle(definition),
@@ -30214,7 +30196,7 @@ stores.inject(MyMetaStore, storeInstance);
30214
30196
  },
30215
30197
  options: {
30216
30198
  ...CHART_COMMON_OPTIONS,
30217
- layout: getPieChartLayout(definition),
30199
+ layout: getChartLayout(),
30218
30200
  plugins: {
30219
30201
  title: getChartTitle(definition),
30220
30202
  legend: getPieChartLegend(definition, chartData),
@@ -30351,7 +30333,7 @@ stores.inject(MyMetaStore, storeInstance);
30351
30333
  options: {
30352
30334
  ...CHART_COMMON_OPTIONS,
30353
30335
  indexAxis: "y",
30354
- layout: getBarChartLayout(definition),
30336
+ layout: getChartLayout(),
30355
30337
  scales: getPyramidChartScales(definition, chartData),
30356
30338
  plugins: {
30357
30339
  title: getChartTitle(definition),
@@ -30500,7 +30482,7 @@ stores.inject(MyMetaStore, storeInstance);
30500
30482
  },
30501
30483
  options: {
30502
30484
  ...CHART_COMMON_OPTIONS,
30503
- layout: getBarChartLayout(definition),
30485
+ layout: getChartLayout(),
30504
30486
  scales: getRadarChartScales(definition, chartData),
30505
30487
  plugins: {
30506
30488
  title: getChartTitle(definition),
@@ -30653,7 +30635,7 @@ stores.inject(MyMetaStore, storeInstance);
30653
30635
  },
30654
30636
  options: {
30655
30637
  ...CHART_COMMON_OPTIONS,
30656
- layout: getLineChartLayout(definition),
30638
+ layout: getChartLayout(),
30657
30639
  scales: getScatterChartScales(definition, chartData),
30658
30640
  plugins: {
30659
30641
  title: getChartTitle(definition),
@@ -30814,7 +30796,7 @@ stores.inject(MyMetaStore, storeInstance);
30814
30796
  },
30815
30797
  options: {
30816
30798
  ...CHART_COMMON_OPTIONS,
30817
- layout: getWaterfallChartLayout(definition),
30799
+ layout: getChartLayout(),
30818
30800
  scales: getWaterfallChartScales(definition, chartData),
30819
30801
  plugins: {
30820
30802
  title: getChartTitle(definition),
@@ -32208,14 +32190,9 @@ stores.inject(MyMetaStore, storeInstance);
32208
32190
  }
32209
32191
  const sheetId = this.env.model.getters.getActiveSheetId();
32210
32192
  const contentZone = { ...tableZone, top: tableZone.top + 1 };
32211
- this.env.model.dispatch("SORT_CELLS", {
32212
- sheetId,
32213
- col: filterPosition.col,
32214
- row: contentZone.top,
32215
- zone: contentZone,
32216
- sortDirection,
32217
- sortOptions: { emptyCellAsZero: true, sortHeaders: true },
32218
- });
32193
+ const sortAnchor = { col: filterPosition.col, row: contentZone.top };
32194
+ const sortOptions = { emptyCellAsZero: true, sortHeaders: true };
32195
+ interactiveSort(this.env, sheetId, sortAnchor, contentZone, sortDirection, sortOptions);
32219
32196
  this.props.onClosed?.();
32220
32197
  }
32221
32198
  }
@@ -33129,6 +33106,7 @@ stores.inject(MyMetaStore, storeInstance);
33129
33106
  adaptChartRange: adaptChartRange,
33130
33107
  chartFactory: chartFactory,
33131
33108
  chartFontColor: chartFontColor,
33109
+ chartMutedFontColor: chartMutedFontColor,
33132
33110
  chartRuntimeFactory: chartRuntimeFactory,
33133
33111
  chartToImage: chartToImage,
33134
33112
  checkDataset: checkDataset,
@@ -34231,6 +34209,7 @@ stores.inject(MyMetaStore, storeInstance);
34231
34209
  const pivotId = env.model.getters.getPivotIdFromPosition(position);
34232
34210
  return (pivotId && env.model.getters.isExistingPivot(pivotId)) || false;
34233
34211
  },
34212
+ isReadonlyAllowed: true,
34234
34213
  icon: "o-spreadsheet-Icon.PIVOT",
34235
34214
  };
34236
34215
  const FIX_FORMULAS = {
@@ -35948,6 +35927,7 @@ stores.inject(MyMetaStore, storeInstance);
35948
35927
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
35949
35928
  name: env.model.getters.getPivotDisplayName(pivotId),
35950
35929
  sequence: sequence + index,
35930
+ isReadonlyAllowed: true,
35951
35931
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
35952
35932
  onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
35953
35933
  onStopHover: (env) => env.getStore(HighlightStore).unRegister(highlightProvider),
@@ -37419,6 +37399,95 @@ stores.inject(MyMetaStore, storeInstance);
37419
37399
  }
37420
37400
  }
37421
37401
 
37402
+ css /* scss */ `
37403
+ .o-font-size-editor {
37404
+ height: calc(100% - 4px);
37405
+ input.o-font-size {
37406
+ outline-color: ${SELECTION_BORDER_COLOR};
37407
+ height: 20px;
37408
+ width: 23px;
37409
+ }
37410
+ }
37411
+ .o-text-options > div {
37412
+ cursor: pointer;
37413
+ line-height: 26px;
37414
+ padding: 3px 12px;
37415
+ &:hover {
37416
+ background-color: rgba(0, 0, 0, 0.08);
37417
+ }
37418
+ }
37419
+ `;
37420
+ class FontSizeEditor extends owl.Component {
37421
+ static template = "o-spreadsheet-FontSizeEditor";
37422
+ static props = {
37423
+ currentFontSize: Number,
37424
+ onFontSizeChanged: Function,
37425
+ onToggle: { type: Function, optional: true },
37426
+ class: String,
37427
+ };
37428
+ static components = { Popover };
37429
+ fontSizes = FONT_SIZES;
37430
+ dropdown = owl.useState({ isOpen: false });
37431
+ inputRef = owl.useRef("inputFontSize");
37432
+ rootEditorRef = owl.useRef("FontSizeEditor");
37433
+ fontSizeListRef = owl.useRef("fontSizeList");
37434
+ setup() {
37435
+ owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
37436
+ }
37437
+ get popoverProps() {
37438
+ const { x, y, width, height } = this.rootEditorRef.el.getBoundingClientRect();
37439
+ return {
37440
+ anchorRect: { x, y, width, height },
37441
+ positioning: "BottomLeft",
37442
+ verticalOffset: 0,
37443
+ };
37444
+ }
37445
+ onExternalClick(ev) {
37446
+ if (!isChildEvent(this.fontSizeListRef.el, ev) && !isChildEvent(this.rootEditorRef.el, ev)) {
37447
+ this.closeFontList();
37448
+ }
37449
+ }
37450
+ toggleFontList() {
37451
+ const isOpen = this.dropdown.isOpen;
37452
+ if (!isOpen) {
37453
+ this.props.onToggle?.();
37454
+ this.inputRef.el.focus();
37455
+ }
37456
+ else {
37457
+ this.closeFontList();
37458
+ }
37459
+ }
37460
+ closeFontList() {
37461
+ this.dropdown.isOpen = false;
37462
+ }
37463
+ setSize(fontSizeStr) {
37464
+ const fontSize = clip(Math.floor(parseFloat(fontSizeStr)), 1, 400);
37465
+ this.props.onFontSizeChanged(fontSize);
37466
+ this.closeFontList();
37467
+ }
37468
+ setSizeFromInput(ev) {
37469
+ this.setSize(ev.target.value);
37470
+ }
37471
+ setSizeFromList(fontSizeStr) {
37472
+ this.setSize(fontSizeStr);
37473
+ }
37474
+ onInputFocused(ev) {
37475
+ this.dropdown.isOpen = true;
37476
+ ev.target.select();
37477
+ }
37478
+ onInputKeydown(ev) {
37479
+ if (ev.key === "Enter" || ev.key === "Escape") {
37480
+ this.closeFontList();
37481
+ const target = ev.target;
37482
+ // In the case of a ESCAPE key, we get the previous font size back
37483
+ if (ev.key === "Escape") {
37484
+ target.value = `${this.props.currentFontSize}`;
37485
+ }
37486
+ this.props.onToggle?.();
37487
+ }
37488
+ }
37489
+ }
37490
+
37422
37491
  css /* scss */ `
37423
37492
  .o-chart-title-designer {
37424
37493
  > span {
@@ -37453,7 +37522,7 @@ stores.inject(MyMetaStore, storeInstance);
37453
37522
  `;
37454
37523
  class ChartTitle extends owl.Component {
37455
37524
  static template = "o-spreadsheet.ChartTitle";
37456
- static components = { Section, ColorPickerWidget };
37525
+ static components = { Section, ColorPickerWidget, FontSizeEditor };
37457
37526
  static props = {
37458
37527
  title: { type: String, optional: true },
37459
37528
  updateTitle: Function,
@@ -37462,7 +37531,8 @@ stores.inject(MyMetaStore, storeInstance);
37462
37531
  toggleBold: { type: Function, optional: true },
37463
37532
  updateAlignment: { type: Function, optional: true },
37464
37533
  updateColor: { type: Function, optional: true },
37465
- style: { type: Object, optional: true },
37534
+ style: Object,
37535
+ onFontSizeChanged: Function,
37466
37536
  };
37467
37537
  static defaultProps = {
37468
37538
  title: "",
@@ -37477,6 +37547,9 @@ stores.inject(MyMetaStore, storeInstance);
37477
37547
  updateTitle(ev) {
37478
37548
  this.props.updateTitle(ev.target.value);
37479
37549
  }
37550
+ updateFontSize(fontSize) {
37551
+ this.props.onFontSizeChanged(fontSize);
37552
+ }
37480
37553
  toggleDropdownTool(tool, ev) {
37481
37554
  const isOpen = this.state.activeTool === tool;
37482
37555
  this.closeMenus();
@@ -37519,6 +37592,7 @@ stores.inject(MyMetaStore, storeInstance);
37519
37592
  return {
37520
37593
  color: "",
37521
37594
  align: "center",
37595
+ fontSize: CHART_AXIS_TITLE_FONT_SIZE,
37522
37596
  ...axisDesign.title,
37523
37597
  };
37524
37598
  }
@@ -37536,6 +37610,17 @@ stores.inject(MyMetaStore, storeInstance);
37536
37610
  };
37537
37611
  this.props.updateChart(this.props.figureId, { axesDesign });
37538
37612
  }
37613
+ updateAxisTitleFontSize(fontSize) {
37614
+ const axesDesign = this.props.definition.axesDesign ?? {};
37615
+ axesDesign[this.state.currentAxis] = {
37616
+ ...axesDesign[this.state.currentAxis],
37617
+ title: {
37618
+ ...(axesDesign[this.state.currentAxis]?.title ?? {}),
37619
+ fontSize,
37620
+ },
37621
+ };
37622
+ this.props.updateChart(this.props.figureId, { axesDesign });
37623
+ }
37539
37624
  toggleBoldAxisTitle() {
37540
37625
  const axesDesign = this.props.definition.axesDesign ?? {};
37541
37626
  const title = axesDesign[this.state.currentAxis]?.title ?? {};
@@ -37655,8 +37740,12 @@ stores.inject(MyMetaStore, storeInstance);
37655
37740
  figureId: String,
37656
37741
  definition: Object,
37657
37742
  updateChart: Function,
37743
+ defaultChartTitleFontSize: { type: Number, optional: true },
37658
37744
  slots: { type: Object, optional: true },
37659
37745
  };
37746
+ static defaultProps = {
37747
+ defaultChartTitleFontSize: CHART_TITLE_FONT_SIZE,
37748
+ };
37660
37749
  state;
37661
37750
  setup() {
37662
37751
  this.state = owl.useState({
@@ -37682,6 +37771,7 @@ stores.inject(MyMetaStore, storeInstance);
37682
37771
  get titleStyle() {
37683
37772
  return {
37684
37773
  align: "left",
37774
+ fontSize: this.props.defaultChartTitleFontSize,
37685
37775
  ...this.title,
37686
37776
  };
37687
37777
  }
@@ -37690,6 +37780,10 @@ stores.inject(MyMetaStore, storeInstance);
37690
37780
  this.props.updateChart(this.props.figureId, { title });
37691
37781
  this.state.activeTool = "";
37692
37782
  }
37783
+ updateChartTitleFontSize(fontSize) {
37784
+ const title = { ...this.title, fontSize };
37785
+ this.props.updateChart(this.props.figureId, { title });
37786
+ }
37693
37787
  toggleBoldChartTitle() {
37694
37788
  let title = this.title;
37695
37789
  title = { ...title, bold: !title.bold };
@@ -37897,7 +37991,7 @@ stores.inject(MyMetaStore, storeInstance);
37897
37991
  case "polynomial":
37898
37992
  config = {
37899
37993
  type: "polynomial",
37900
- order: type === "linear" ? 1 : 2,
37994
+ order: type === "linear" ? 1 : this.getMaxPolynomialDegree(index),
37901
37995
  };
37902
37996
  break;
37903
37997
  case "exponential":
@@ -38357,6 +38451,9 @@ stores.inject(MyMetaStore, storeInstance);
38357
38451
  get humanizeNumbersLabel() {
38358
38452
  return _t("Humanize numbers");
38359
38453
  }
38454
+ get defaultScorecardTitleFontSize() {
38455
+ return SCORECARD_CHART_TITLE_FONT_SIZE;
38456
+ }
38360
38457
  updateHumanizeNumbers(humanize) {
38361
38458
  this.props.updateChart(this.props.figureId, { humanize });
38362
38459
  }
@@ -39820,6 +39917,15 @@ stores.inject(MyMetaStore, storeInstance);
39820
39917
  confirmEdition(content) {
39821
39918
  this.args().onConfirm(content);
39822
39919
  }
39920
+ getTokenColor(token) {
39921
+ if (token.type === "SYMBOL") {
39922
+ const matchedColor = this.args().getContextualColoredSymbolToken?.(token);
39923
+ if (matchedColor) {
39924
+ return matchedColor;
39925
+ }
39926
+ }
39927
+ return super.getTokenColor(token);
39928
+ }
39823
39929
  }
39824
39930
 
39825
39931
  css /* scss */ `
@@ -39857,6 +39963,7 @@ stores.inject(MyMetaStore, storeInstance);
39857
39963
  placeholder: { type: String, optional: true },
39858
39964
  class: { type: String, optional: true },
39859
39965
  invalid: { type: Boolean, optional: true },
39966
+ getContextualColoredSymbolToken: { type: Function, optional: true },
39860
39967
  };
39861
39968
  static components = { Composer };
39862
39969
  static defaultProps = {
@@ -39873,6 +39980,7 @@ stores.inject(MyMetaStore, storeInstance);
39873
39980
  content: this.props.composerContent,
39874
39981
  contextualAutocomplete: this.props.contextualAutocomplete,
39875
39982
  defaultRangeSheetId: this.props.defaultRangeSheetId,
39983
+ getContextualColoredSymbolToken: this.props.getContextualColoredSymbolToken,
39876
39984
  }));
39877
39985
  this.standaloneComposerStore = standaloneComposerStore;
39878
39986
  this.composerInterface = {
@@ -43433,7 +43541,7 @@ stores.inject(MyMetaStore, storeInstance);
43433
43541
  return {
43434
43542
  text: text,
43435
43543
  description: measure.displayName,
43436
- htmlContent: [{ value: text, color: tokenColors.FUNCTION }],
43544
+ htmlContent: [{ value: text, color: PIVOT_TOKEN_COLOR }],
43437
43545
  fuzzySearchKey: measure.displayName + text + measure.fieldName,
43438
43546
  };
43439
43547
  });
@@ -43442,7 +43550,7 @@ stores.inject(MyMetaStore, storeInstance);
43442
43550
  return {
43443
43551
  text: text,
43444
43552
  description: dimension.displayName,
43445
- htmlContent: [{ value: text, color: tokenColors.FUNCTION }],
43553
+ htmlContent: [{ value: text, color: PIVOT_TOKEN_COLOR }],
43446
43554
  fuzzySearchKey: dimension.displayName + text + dimension.fieldName,
43447
43555
  };
43448
43556
  });
@@ -43522,6 +43630,18 @@ stores.inject(MyMetaStore, storeInstance);
43522
43630
  measure: this.props.measure,
43523
43631
  });
43524
43632
  }
43633
+ getColoredSymbolToken(token) {
43634
+ if (token.type !== "SYMBOL") {
43635
+ return undefined;
43636
+ }
43637
+ const tokenValue = unquote(token.value, "'");
43638
+ if (this.props.definition.columns.some((col) => col.nameWithGranularity === tokenValue) ||
43639
+ this.props.definition.rows.some((row) => row.nameWithGranularity === tokenValue) ||
43640
+ this.props.definition.measures.some((measure) => measure.id === tokenValue && measure.id !== this.props.measure.id)) {
43641
+ return PIVOT_TOKEN_COLOR;
43642
+ }
43643
+ return undefined;
43644
+ }
43525
43645
  }
43526
43646
 
43527
43647
  css /* scss */ `
@@ -51896,7 +52016,7 @@ stores.inject(MyMetaStore, storeInstance);
51896
52016
  for (let col = zone.left; col <= zone.right; col++) {
51897
52017
  for (let row = zone.top; row <= zone.bottom; row++) {
51898
52018
  const cell = this.getters.getCell({ sheetId, col, row });
51899
- if (cell) {
52019
+ if (cell?.isFormula || cell?.content) {
51900
52020
  this.dispatch("UPDATE_CELL", {
51901
52021
  sheetId: sheetId,
51902
52022
  content: "",
@@ -51932,7 +52052,6 @@ stores.inject(MyMetaStore, storeInstance);
51932
52052
  for (let zone of recomputeZones(zones)) {
51933
52053
  for (let col = zone.left; col <= zone.right; col++) {
51934
52054
  for (let row = zone.top; row <= zone.bottom; row++) {
51935
- // commandHelpers.updateCell(sheetId, col, row, { style: undefined});
51936
52055
  this.dispatch("UPDATE_CELL", {
51937
52056
  sheetId,
51938
52057
  col,
@@ -52903,8 +53022,6 @@ stores.inject(MyMetaStore, storeInstance);
52903
53022
  case "IconSetRule": {
52904
53023
  return this.checkValidations(rule, this.chainValidations(this.checkInflectionPoints(this.checkNaN), this.checkLowerBiggerThanUpper), this.chainValidations(this.checkInflectionPoints(this.checkFormulaCompilation)));
52905
53024
  }
52906
- case "DataBarRule":
52907
- return this.checkDataBarRangeValues(rule, cmd.ranges, cmd.sheetId);
52908
53025
  }
52909
53026
  return "Success" /* CommandResult.Success */;
52910
53027
  }
@@ -53035,18 +53152,6 @@ stores.inject(MyMetaStore, storeInstance);
53035
53152
  }
53036
53153
  return "Success" /* CommandResult.Success */;
53037
53154
  }
53038
- checkDataBarRangeValues(rule, ranges, sheetId) {
53039
- if (rule.rangeValues) {
53040
- const { numberOfCols, numberOfRows } = zoneToDimension(this.getters.getRangeFromSheetXC(sheetId, rule.rangeValues).zone);
53041
- for (const range of ranges) {
53042
- const dimensions = zoneToDimension(this.getters.getRangeFromRangeData(range).zone);
53043
- if (numberOfCols !== dimensions.numberOfCols || numberOfRows !== dimensions.numberOfRows) {
53044
- return "DataBarRangeValuesMismatch" /* CommandResult.DataBarRangeValuesMismatch */;
53045
- }
53046
- }
53047
- }
53048
- return "Success" /* CommandResult.Success */;
53049
- }
53050
53155
  removeConditionalFormatting(id, sheet) {
53051
53156
  const cfIndex = this.cfRules[sheet].findIndex((s) => s.id === id);
53052
53157
  if (cfIndex !== -1) {
@@ -55365,7 +55470,7 @@ stores.inject(MyMetaStore, storeInstance);
55365
55470
  if (orderedSheetIds.find((id) => sheets[id]?.name.toLowerCase() === name && id !== cmd.sheetId)) {
55366
55471
  return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
55367
55472
  }
55368
- if (FORBIDDEN_IN_EXCEL_REGEX.test(name)) {
55473
+ if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
55369
55474
  return "ForbiddenCharactersInSheetName" /* CommandResult.ForbiddenCharactersInSheetName */;
55370
55475
  }
55371
55476
  return "Success" /* CommandResult.Success */;
@@ -59451,8 +59556,12 @@ stores.inject(MyMetaStore, storeInstance);
59451
59556
  const zoneOfValues = rangeValues.zone;
59452
59557
  for (let row = zone.top; row <= zone.bottom; row++) {
59453
59558
  for (let col = zone.left; col <= zone.right; col++) {
59454
- const cell = this.getEvaluatedCellInZone(sheetId, zone, col, row, zoneOfValues);
59455
- if (cell.type !== CellValueType.number || cell.value <= 0) {
59559
+ const targetCol = col - zone.left + zoneOfValues.left;
59560
+ const targetRow = row - zone.top + zoneOfValues.top;
59561
+ const cell = this.getters.getEvaluatedCell({ sheetId, col: targetCol, row: targetRow });
59562
+ if (!isInside(targetCol, targetRow, zoneOfValues) ||
59563
+ cell.type !== CellValueType.number ||
59564
+ cell.value <= 0) {
59456
59565
  // values negatives or 0 are ignored
59457
59566
  continue;
59458
59567
  }
@@ -59465,11 +59574,6 @@ stores.inject(MyMetaStore, storeInstance);
59465
59574
  }
59466
59575
  }
59467
59576
  }
59468
- getEvaluatedCellInZone(sheetId, zone, col, row, targetZone) {
59469
- const targetCol = col - zone.left + targetZone.left;
59470
- const targetRow = row - zone.top + targetZone.top;
59471
- return this.getters.getEvaluatedCell({ sheetId, col: targetCol, row: targetRow });
59472
- }
59473
59577
  /** Compute the color scale for the given range and CF rule, and apply in in the given computedStyle object */
59474
59578
  applyColorScale(sheetId, range, rule, computedStyle) {
59475
59579
  const minValue = this.parsePoint(sheetId, range, rule.minimum, "min");
@@ -60774,11 +60878,13 @@ stores.inject(MyMetaStore, storeInstance);
60774
60878
  return EMPTY_PIVOT_CELL;
60775
60879
  }
60776
60880
  if (functionName === "PIVOT") {
60777
- const includeTotal = args[2] === false ? false : undefined;
60778
- const includeColumnHeaders = args[3] === false ? false : undefined;
60881
+ const includeTotal = toScalar(args[2]);
60882
+ const shouldIncludeTotal = includeTotal === undefined ? true : toBoolean(includeTotal);
60883
+ const includeColumnHeaders = toScalar(args[3]);
60884
+ const shouldIncludeColumnHeaders = includeColumnHeaders === undefined ? true : toBoolean(includeColumnHeaders);
60779
60885
  const pivotCells = pivot
60780
60886
  .getTableStructure()
60781
- .getPivotCells(includeTotal, includeColumnHeaders);
60887
+ .getPivotCells(shouldIncludeTotal, shouldIncludeColumnHeaders);
60782
60888
  const pivotCol = position.col - mainPosition.col;
60783
60889
  const pivotRow = position.row - mainPosition.row;
60784
60890
  return pivotCells[pivotCol][pivotRow];
@@ -62932,7 +63038,7 @@ stores.inject(MyMetaStore, storeInstance);
62932
63038
  getPivotDuplicateSheetName(pivotName) {
62933
63039
  let i = 1;
62934
63040
  const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
62935
- const sanitizedName = pivotName.replace(new RegExp(FORBIDDEN_IN_EXCEL_REGEX, "g"), " ");
63041
+ const sanitizedName = sanitizeSheetName(pivotName);
62936
63042
  let name = sanitizedName;
62937
63043
  while (names.includes(name)) {
62938
63044
  name = `${sanitizedName} (${i})`;
@@ -63047,9 +63153,9 @@ stores.inject(MyMetaStore, storeInstance);
63047
63153
  switch (cmd.type) {
63048
63154
  case "SORT_CELLS":
63049
63155
  if (!isInside(cmd.col, cmd.row, cmd.zone)) {
63050
- throw new Error(_t("The anchor must be part of the provided zone"));
63156
+ return "InvalidSortAnchor" /* CommandResult.InvalidSortAnchor */;
63051
63157
  }
63052
- return this.checkValidations(cmd, this.checkMerge, this.checkMergeSizes);
63158
+ return this.checkValidations(cmd, this.checkMerge, this.checkMergeSizes, this.checkArrayFormulaInSortZone);
63053
63159
  }
63054
63160
  return "Success" /* CommandResult.Success */;
63055
63161
  }
@@ -63090,6 +63196,10 @@ stores.inject(MyMetaStore, storeInstance);
63090
63196
  }
63091
63197
  return "Success" /* CommandResult.Success */;
63092
63198
  }
63199
+ checkArrayFormulaInSortZone({ sheetId, zone }) {
63200
+ const arrayFormulaInZone = positions(zone).some(({ col, row }) => this.getters.getArrayFormulaSpreadingOn({ sheetId, col, row }));
63201
+ return arrayFormulaInZone ? "SortZoneWithArrayFormulas" /* CommandResult.SortZoneWithArrayFormulas */ : "Success" /* CommandResult.Success */;
63202
+ }
63093
63203
  /**
63094
63204
  * This function evaluates if the top row of a provided zone can be considered as a `header`
63095
63205
  * by checking the following criteria:
@@ -67004,7 +67114,7 @@ stores.inject(MyMetaStore, storeInstance);
67004
67114
  env.raiseError(_t("A sheet with the name %s already exists. Please select another name.", name), errorCallback);
67005
67115
  }
67006
67116
  else if (result.reasons.includes("ForbiddenCharactersInSheetName" /* CommandResult.ForbiddenCharactersInSheetName */)) {
67007
- env.raiseError(_t("Some used characters are not allowed in a sheet name (Forbidden characters are %s).", FORBIDDEN_SHEET_CHARS.join(" ")), errorCallback);
67117
+ env.raiseError(_t("Some used characters are not allowed in a sheet name (Forbidden characters are %s).", FORBIDDEN_SHEETNAME_CHARS.join(" ")), errorCallback);
67008
67118
  }
67009
67119
  }
67010
67120
 
@@ -68234,7 +68344,7 @@ stores.inject(MyMetaStore, storeInstance);
68234
68344
  setup() {
68235
68345
  owl.onWillUpdateProps((nextProps) => {
68236
68346
  if (nextProps.action !== this.props.action) {
68237
- this.actionButton = createAction(this.props.action);
68347
+ this.actionButton = createAction(nextProps.action);
68238
68348
  }
68239
68349
  });
68240
68350
  }
@@ -68570,88 +68680,6 @@ stores.inject(MyMetaStore, storeInstance);
68570
68680
  }
68571
68681
  }
68572
68682
 
68573
- css /* scss */ `
68574
- .o-font-size-editor {
68575
- height: calc(100% - 4px);
68576
- input.o-font-size {
68577
- outline-color: ${SELECTION_BORDER_COLOR};
68578
- height: 20px;
68579
- width: 23px;
68580
- }
68581
- }
68582
- .o-text-options > div {
68583
- cursor: pointer;
68584
- line-height: 26px;
68585
- padding: 3px 12px;
68586
- &:hover {
68587
- background-color: rgba(0, 0, 0, 0.08);
68588
- }
68589
- }
68590
- `;
68591
- class FontSizeEditor extends owl.Component {
68592
- static template = "o-spreadsheet-FontSizeEditor";
68593
- static props = {
68594
- onToggle: Function,
68595
- dropdownStyle: String,
68596
- class: String,
68597
- };
68598
- static components = {};
68599
- fontSizes = FONT_SIZES;
68600
- dropdown = owl.useState({ isOpen: false });
68601
- inputRef = owl.useRef("inputFontSize");
68602
- rootEditorRef = owl.useRef("FontSizeEditor");
68603
- setup() {
68604
- owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
68605
- }
68606
- onExternalClick(ev) {
68607
- if (!isChildEvent(this.rootEditorRef.el, ev)) {
68608
- this.closeFontList();
68609
- }
68610
- }
68611
- get currentFontSize() {
68612
- return this.env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
68613
- }
68614
- toggleFontList() {
68615
- const isOpen = this.dropdown.isOpen;
68616
- if (!isOpen) {
68617
- this.props.onToggle();
68618
- this.inputRef.el.focus();
68619
- }
68620
- else {
68621
- this.closeFontList();
68622
- }
68623
- }
68624
- closeFontList() {
68625
- this.dropdown.isOpen = false;
68626
- }
68627
- setSize(fontSizeStr) {
68628
- const fontSize = clip(Math.floor(parseFloat(fontSizeStr)), 1, 400);
68629
- setStyle(this.env, { fontSize });
68630
- this.closeFontList();
68631
- }
68632
- setSizeFromInput(ev) {
68633
- this.setSize(ev.target.value);
68634
- }
68635
- setSizeFromList(fontSizeStr) {
68636
- this.setSize(fontSizeStr);
68637
- }
68638
- onInputFocused(ev) {
68639
- this.dropdown.isOpen = true;
68640
- ev.target.select();
68641
- }
68642
- onInputKeydown(ev) {
68643
- if (ev.key === "Enter" || ev.key === "Escape") {
68644
- this.closeFontList();
68645
- const target = ev.target;
68646
- // In the case of a ESCAPE key, we get the previous font size back
68647
- if (ev.key === "Escape") {
68648
- target.value = `${this.currentFontSize}`;
68649
- }
68650
- this.props.onToggle();
68651
- }
68652
- }
68653
- }
68654
-
68655
68683
  class TableDropdownButton extends owl.Component {
68656
68684
  static template = "o-spreadsheet-TableDropdownButton";
68657
68685
  static components = { TableStylesPopover, ActionButton };
@@ -68820,9 +68848,6 @@ stores.inject(MyMetaStore, storeInstance);
68820
68848
  onClick: Function,
68821
68849
  dropdownMaxHeight: Number,
68822
68850
  };
68823
- get dropdownStyle() {
68824
- return `max-height:${this.props.dropdownMaxHeight}px`;
68825
- }
68826
68851
  static components = {
68827
68852
  ColorPickerWidget,
68828
68853
  ColorPicker,
@@ -68860,6 +68885,9 @@ stores.inject(MyMetaStore, storeInstance);
68860
68885
  .getAllOrdered()
68861
68886
  .filter((item) => !item.isVisible || item.isVisible(this.env));
68862
68887
  }
68888
+ get currentFontSize() {
68889
+ return this.env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
68890
+ }
68863
68891
  onExternalClick(ev) {
68864
68892
  // TODO : manage click events better. We need this piece of code
68865
68893
  // otherwise the event opening the menu would close it on the same frame.
@@ -68938,6 +68966,9 @@ stores.inject(MyMetaStore, storeInstance);
68938
68966
  setStyle(this.env, { [target]: color });
68939
68967
  this.onClick();
68940
68968
  }
68969
+ setFontSize(fontSize) {
68970
+ setStyle(this.env, { fontSize });
68971
+ }
68941
68972
  }
68942
68973
 
68943
68974
  function instantiateClipboard() {
@@ -70938,12 +70969,11 @@ stores.inject(MyMetaStore, storeInstance);
70938
70969
  // <manualLayout/> to manually position the chart in the figure container
70939
70970
  let title = escapeXml ``;
70940
70971
  if (chart.data.title?.text) {
70941
- const color = chart.data.title.color
70942
- ? toXlsxHexColor(chart.data.title.color)
70943
- : chart.data.fontColor;
70972
+ const titleColor = toXlsxHexColor(chartMutedFontColor(chart.data.backgroundColor));
70973
+ const fontSize = chart.data.title.fontSize ?? CHART_TITLE_FONT_SIZE;
70944
70974
  title = escapeXml /*xml*/ `
70945
70975
  <c:title>
70946
- ${insertText(chart.data.title.text, color, DEFAULT_CHART_FONT_SIZE, chart.data.title)}
70976
+ ${insertText(chart.data.title.text, titleColor, fontSize, chart.data.title)}
70947
70977
  <c:overlay val="0" />
70948
70978
  </c:title>
70949
70979
  `;
@@ -71033,7 +71063,7 @@ stores.inject(MyMetaStore, storeInstance);
71033
71063
  </a:ln>
71034
71064
  `;
71035
71065
  }
71036
- function insertText(text, fontColor = "000000", fontsize = DEFAULT_CHART_FONT_SIZE, style = {}) {
71066
+ function insertText(text, fontColor = "000000", fontsize = CHART_TITLE_FONT_SIZE, style = {}) {
71037
71067
  return escapeXml /*xml*/ `
71038
71068
  <c:tx>
71039
71069
  <c:rich>
@@ -71534,6 +71564,7 @@ stores.inject(MyMetaStore, storeInstance);
71534
71564
  // Each Axis present inside a graph needs to be identified by an unsigned integer in order to be referenced by its crossAxis.
71535
71565
  // I.e. x-axis, will reference y-axis and vice-versa.
71536
71566
  const color = title?.color ? toXlsxHexColor(title.color) : defaultFontColor;
71567
+ const fontSize = title?.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE;
71537
71568
  return escapeXml /*xml*/ `
71538
71569
  <${axisName}>
71539
71570
  <c:axId val="${axId}"/>
@@ -71549,7 +71580,7 @@ stores.inject(MyMetaStore, storeInstance);
71549
71580
  <c:minorTickMark val="none" />
71550
71581
  <c:numFmt formatCode="General" sourceLinked="1" />
71551
71582
  <c:title>
71552
- ${insertText(title?.text ?? "", color, 10, title)}
71583
+ ${insertText(title?.text ?? "", color, fontSize, title)}
71553
71584
  </c:title>
71554
71585
  ${insertTextProperties(10, defaultFontColor)}
71555
71586
  </${axisName}>
@@ -73660,6 +73691,7 @@ stores.inject(MyMetaStore, storeInstance);
73660
73691
  createPivotFormula,
73661
73692
  areDomainArgsFieldsValid,
73662
73693
  splitReference,
73694
+ sanitizeSheetName,
73663
73695
  };
73664
73696
  const links = {
73665
73697
  isMarkdownLink,
@@ -73691,6 +73723,9 @@ stores.inject(MyMetaStore, storeInstance);
73691
73723
  GaugeChartDesignPanel,
73692
73724
  ScorecardChartConfigPanel,
73693
73725
  ScorecardChartDesignPanel,
73726
+ RadarChartDesignPanel,
73727
+ WaterfallChartDesignPanel,
73728
+ ComboChartDesignPanel,
73694
73729
  ChartTypePicker,
73695
73730
  FigureComponent,
73696
73731
  Menu,
@@ -73744,6 +73779,7 @@ stores.inject(MyMetaStore, storeInstance);
73744
73779
  DEFAULT_LOCALE,
73745
73780
  HIGHLIGHT_COLOR,
73746
73781
  PIVOT_TABLE_CONFIG,
73782
+ ChartTerms,
73747
73783
  };
73748
73784
  const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
73749
73785
 
@@ -73794,9 +73830,9 @@ stores.inject(MyMetaStore, storeInstance);
73794
73830
  exports.tokenize = tokenize;
73795
73831
 
73796
73832
 
73797
- __info__.version = "18.1.0-alpha.5";
73798
- __info__.date = "2024-11-22T14:22:50.899Z";
73799
- __info__.hash = "e13bd67";
73833
+ __info__.version = "18.1.0-alpha.7";
73834
+ __info__.date = "2024-12-05T10:40:26.512Z";
73835
+ __info__.hash = "7b1c39b";
73800
73836
 
73801
73837
 
73802
73838
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);