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