@odoo/o-spreadsheet 17.2.5 → 17.2.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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.2.5
7
- * @date 2024-04-26T07:41:13.193Z
8
- * @hash a730f5c
6
+ * @version 17.2.7
7
+ * @date 2024-05-15T09:20:44.429Z
8
+ * @hash 57e89fa
9
9
  */
10
10
 
11
11
  import { reactive, useEnv, useSubEnv, useState, onWillUnmount, markRaw, toRaw, Component, useRef, onMounted, useEffect, onPatched, useComponent, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv } from '@odoo/owl';
@@ -552,7 +552,7 @@ function getAddHeaderStartIndex(position, base) {
552
552
  /**
553
553
  * Compares two objects.
554
554
  */
555
- function deepEquals(o1, o2) {
555
+ function deepEquals(o1, o2, ignoreFunctions) {
556
556
  if (o1 === o2)
557
557
  return true;
558
558
  if ((o1 && !o2) || (o2 && !o1))
@@ -568,13 +568,16 @@ function deepEquals(o1, o2) {
568
568
  }
569
569
  }
570
570
  for (const key in o1) {
571
- if (typeof o1[key] !== typeof o2[key])
571
+ const typeOfO1Key = typeof o1[key];
572
+ if (typeOfO1Key !== typeof o2[key])
572
573
  return false;
573
- if (typeof o1[key] === "object") {
574
- if (!deepEquals(o1[key], o2[key]))
574
+ if (typeOfO1Key === "object") {
575
+ if (!deepEquals(o1[key], o2[key], ignoreFunctions))
575
576
  return false;
576
577
  }
577
578
  else {
579
+ if (ignoreFunctions && typeOfO1Key === "function")
580
+ return true;
578
581
  if (o1[key] !== o2[key])
579
582
  return false;
580
583
  }
@@ -3682,21 +3685,22 @@ function toZoneWithoutBoundaryChanges(xc) {
3682
3685
  xc = xc.split("!").at(-1);
3683
3686
  }
3684
3687
  if (xc.includes("$")) {
3685
- xc = xc.replace(/\$/g, "");
3688
+ xc = xc.replaceAll("$", "");
3686
3689
  }
3687
- let ranges;
3690
+ let firstRangePart = "";
3691
+ let secondRangePart;
3688
3692
  if (xc.includes(":")) {
3689
- ranges = xc.split(":").map((x) => x.trim());
3693
+ [firstRangePart, secondRangePart] = xc.split(":");
3694
+ firstRangePart = firstRangePart.trim();
3695
+ secondRangePart = secondRangePart.trim();
3690
3696
  }
3691
3697
  else {
3692
- ranges = [xc.trim()];
3698
+ firstRangePart = xc.trim();
3693
3699
  }
3694
3700
  let top, bottom, left, right;
3695
3701
  let fullCol = false;
3696
3702
  let fullRow = false;
3697
3703
  let hasHeader = false;
3698
- const firstRangePart = ranges[0];
3699
- const secondRangePart = ranges[1] && ranges[1];
3700
3704
  if (isColReference(firstRangePart)) {
3701
3705
  left = right = lettersToNumber(firstRangePart);
3702
3706
  top = bottom = 0;
@@ -3713,7 +3717,7 @@ function toZoneWithoutBoundaryChanges(xc) {
3713
3717
  top = bottom = c.row;
3714
3718
  hasHeader = true;
3715
3719
  }
3716
- if (ranges.length === 2) {
3720
+ if (secondRangePart) {
3717
3721
  if (isColReference(secondRangePart)) {
3718
3722
  right = lettersToNumber(secondRangePart);
3719
3723
  fullCol = true;
@@ -5197,7 +5201,7 @@ function tokenizeString(chars) {
5197
5201
  }
5198
5202
  return null;
5199
5203
  }
5200
- const separatorRegexp = /^[\w\.!\$]+/;
5204
+ const SYMBOL_CHARS = new Set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.!$");
5201
5205
  /**
5202
5206
  * A "Symbol" is just basically any word-like element that can appear in a
5203
5207
  * formula, which is not a string. So:
@@ -5237,11 +5241,8 @@ function tokenizeSymbol(chars) {
5237
5241
  };
5238
5242
  }
5239
5243
  }
5240
- const match = chars.remaining().match(separatorRegexp);
5241
- if (match) {
5242
- const value = match[0];
5243
- result += value;
5244
- chars.advanceBy(value.length);
5244
+ while (chars.current && SYMBOL_CHARS.has(chars.current)) {
5245
+ result += chars.shift();
5245
5246
  }
5246
5247
  if (result.length) {
5247
5248
  const value = result;
@@ -6793,7 +6794,7 @@ const machine = {
6793
6794
  function matchReference(tokens) {
6794
6795
  let head = 0;
6795
6796
  let transitions = machine[State.LeftRef];
6796
- const matchedTokens = [];
6797
+ let matchedTokens = "";
6797
6798
  while (transitions !== undefined) {
6798
6799
  const token = tokens[head++];
6799
6800
  if (!token) {
@@ -6805,15 +6806,15 @@ function matchReference(tokens) {
6805
6806
  case undefined:
6806
6807
  return null;
6807
6808
  case State.Found:
6808
- matchedTokens.push(token);
6809
+ matchedTokens += token.value;
6809
6810
  tokens.splice(0, head);
6810
6811
  return {
6811
6812
  type: "REFERENCE",
6812
- value: concat(matchedTokens.map((token) => token.value)),
6813
+ value: matchedTokens,
6813
6814
  };
6814
6815
  default:
6815
6816
  transitions = machine[nextState];
6816
- matchedTokens.push(token);
6817
+ matchedTokens += token.value;
6817
6818
  break;
6818
6819
  }
6819
6820
  }
@@ -8904,8 +8905,7 @@ class ComposerStore extends SpreadsheetStore {
8904
8905
  const exactMatch = proposals?.find((p) => p.text === tokenAtCursor.value);
8905
8906
  // remove tokens that are likely to be other parts of the formula that slipped in the token if it's a string
8906
8907
  const searchTerm = tokenAtCursor.value.replace(/[ ,\(\)]/g, "");
8907
- const initialContent = this.initialContent;
8908
- if (exactMatch && exactMatch.text !== initialContent) {
8908
+ if (exactMatch && this._currentContent !== this.initialContent) {
8909
8909
  // this means the user has chosen a proposal
8910
8910
  return;
8911
8911
  }
@@ -8913,7 +8913,7 @@ class ComposerStore extends SpreadsheetStore {
8913
8913
  proposals &&
8914
8914
  !["ARG_SEPARATOR", "LEFT_PAREN"].includes(tokenAtCursor.type)) {
8915
8915
  const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
8916
- if (!exactMatch) {
8916
+ if (!exactMatch || filteredProposals.length > 1) {
8917
8917
  proposals = filteredProposals;
8918
8918
  }
8919
8919
  }
@@ -9048,6 +9048,7 @@ class ChartJsComponent extends Component {
9048
9048
  };
9049
9049
  canvas = useRef("graphContainer");
9050
9050
  chart;
9051
+ currentRuntime;
9051
9052
  get background() {
9052
9053
  return this.chartRuntime.background;
9053
9054
  }
@@ -9064,9 +9065,18 @@ class ChartJsComponent extends Component {
9064
9065
  setup() {
9065
9066
  onMounted(() => {
9066
9067
  const runtime = this.chartRuntime;
9067
- this.createChart(runtime.chartJsConfig);
9068
+ this.currentRuntime = runtime;
9069
+ // Note: chartJS modify the runtime in place, so it's important to give it a copy
9070
+ this.createChart(deepCopy(runtime.chartJsConfig));
9071
+ });
9072
+ onWillUnmount(() => this.chart?.destroy());
9073
+ useEffect(() => {
9074
+ const runtime = this.chartRuntime;
9075
+ if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
9076
+ this.currentRuntime = runtime;
9077
+ this.updateChartJs(deepCopy(runtime));
9078
+ }
9068
9079
  });
9069
- useEffect(() => this.updateChartJs(this.chartRuntime), () => [this.chartRuntime]);
9070
9080
  }
9071
9081
  createChart(chartData) {
9072
9082
  const canvas = this.canvas.el;
@@ -9088,8 +9098,7 @@ class ChartJsComponent extends Component {
9088
9098
  this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip;
9089
9099
  this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
9090
9100
  this.chart.config.options.scales = chartData.options?.scales;
9091
- // ?
9092
- this.chart.update("active");
9101
+ this.chart.update();
9093
9102
  }
9094
9103
  }
9095
9104
 
@@ -19452,7 +19461,7 @@ function truncateLabel(label) {
19452
19461
  /**
19453
19462
  * Get a default chart js configuration
19454
19463
  */
19455
- function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
19464
+ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
19456
19465
  const options = {
19457
19466
  // https://www.chartjs.org/docs/latest/general/responsive.html
19458
19467
  responsive: true,
@@ -19499,7 +19508,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
19499
19508
  type: chart.type,
19500
19509
  options,
19501
19510
  data: {
19502
- labels: labels.map(truncateLabel),
19511
+ labels: truncateLabels ? labels.map(truncateLabel) : labels,
19503
19512
  datasets: [],
19504
19513
  },
19505
19514
  platform: undefined,
@@ -20265,9 +20274,9 @@ function isLuxonTimeAdapterInstalled() {
20265
20274
  }
20266
20275
  return isInstalled;
20267
20276
  }
20268
- function getLineOrScatterConfiguration(chart, labels, localeFormat) {
20277
+ function getLineOrScatterConfiguration(chart, labels, options) {
20269
20278
  const fontColor = chartFontColor(chart.background);
20270
- const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
20279
+ const config = getDefaultChartJsRuntime(chart, labels, fontColor, options);
20271
20280
  const legend = {
20272
20281
  labels: {
20273
20282
  color: fontColor,
@@ -20310,7 +20319,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
20310
20319
  value = Number(value);
20311
20320
  if (isNaN(value))
20312
20321
  return value;
20313
- const { locale, format } = localeFormat;
20322
+ const { locale, format } = options;
20314
20323
  return formatValue(value, {
20315
20324
  locale,
20316
20325
  format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
@@ -20343,9 +20352,10 @@ function createLineOrScatterChartRuntime(chart, getters) {
20343
20352
  ({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
20344
20353
  }
20345
20354
  const locale = getters.getLocale();
20355
+ const truncateLabels = axisType === "category";
20346
20356
  const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
20347
- const localeFormat = { format: dataSetFormat, locale };
20348
- const config = getLineOrScatterConfiguration(chart, labels, localeFormat);
20357
+ const options = { format: dataSetFormat, locale, truncateLabels };
20358
+ const config = getLineOrScatterConfiguration(chart, labels, options);
20349
20359
  const labelFormat = getChartLabelFormat(getters, chart.labelRange);
20350
20360
  if (axisType === "time") {
20351
20361
  const axis = {
@@ -32899,7 +32909,8 @@ class DataValidationOverlay extends Component {
32899
32909
  get checkBoxCellPositions() {
32900
32910
  return this.env.model.getters
32901
32911
  .getVisibleCellPositions()
32902
- .filter(this.env.model.getters.isCellValidCheckbox);
32912
+ .filter((position) => this.env.model.getters.isCellValidCheckbox(position) &&
32913
+ !this.env.model.getters.isFilterHeader(position));
32903
32914
  }
32904
32915
  get listIconsCellPositions() {
32905
32916
  if (this.env.model.getters.isReadonly()) {
@@ -32907,7 +32918,8 @@ class DataValidationOverlay extends Component {
32907
32918
  }
32908
32919
  return this.env.model.getters
32909
32920
  .getVisibleCellPositions()
32910
- .filter(this.env.model.getters.cellHasListDataValidationIcon);
32921
+ .filter((position) => this.env.model.getters.cellHasListDataValidationIcon(position) &&
32922
+ !this.env.model.getters.isFilterHeader(position));
32911
32923
  }
32912
32924
  }
32913
32925
 
@@ -47420,9 +47432,10 @@ class Evaluator {
47420
47432
  }
47421
47433
  if (!content) {
47422
47434
  // The previous content could have blocked some array formulas
47423
- impactedPositions.addMany(this.getArrayFormulasBlockedBy(position));
47435
+ impactedPositions.add(position);
47424
47436
  }
47425
47437
  }
47438
+ impactedPositions.addMany(this.getArrayFormulasBlockedBy(impactedPositions));
47426
47439
  return impactedPositions;
47427
47440
  }
47428
47441
  buildDependencyGraph() {
@@ -47464,23 +47477,25 @@ class Evaluator {
47464
47477
  return positions;
47465
47478
  }
47466
47479
  /**
47467
- * Return the position of formulas blocked by the given position
47480
+ * Return the position of formulas blocked by the given positions
47468
47481
  * as well as all their dependencies.
47469
47482
  */
47470
- getArrayFormulasBlockedBy(position) {
47471
- if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
47472
- return [];
47473
- }
47474
- const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
47475
- const positions = this.createEmptyPositionSet();
47476
- positions.addMany(arrayFormulas);
47477
- const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
47478
- if (arrayFormulaPosition) {
47479
- // ignore the formula spreading on the position. Keep only the blocked ones
47480
- positions.delete(arrayFormulaPosition);
47483
+ getArrayFormulasBlockedBy(positions) {
47484
+ const arrayFormulaPositions = this.createEmptyPositionSet();
47485
+ for (const position of positions) {
47486
+ if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
47487
+ continue;
47488
+ }
47489
+ const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
47490
+ arrayFormulaPositions.addMany(arrayFormulas);
47491
+ const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
47492
+ if (arrayFormulaPosition) {
47493
+ // ignore the formula spreading on the position. Keep only the blocked ones
47494
+ arrayFormulaPositions.delete(arrayFormulaPosition);
47495
+ }
47481
47496
  }
47482
- positions.addMany(this.getCellsDependingOn(positions));
47483
- return positions;
47497
+ arrayFormulaPositions.addMany(this.getCellsDependingOn(arrayFormulaPositions));
47498
+ return arrayFormulaPositions;
47484
47499
  }
47485
47500
  nextPositionsToUpdate = new PositionSet({});
47486
47501
  cellsBeingComputed = new Set();
@@ -47610,6 +47625,7 @@ class Evaluator {
47610
47625
  if (!this.spreadingRelations.isArrayFormula(position)) {
47611
47626
  return;
47612
47627
  }
47628
+ const invalidated = this.createEmptyPositionSet();
47613
47629
  for (const child of this.spreadingRelations.getArrayResultPositions(position)) {
47614
47630
  const content = this.getters.getCell(child)?.content;
47615
47631
  if (content) {
@@ -47617,10 +47633,11 @@ class Evaluator {
47617
47633
  // there's still a collision
47618
47634
  continue;
47619
47635
  }
47636
+ invalidated.add(child);
47620
47637
  this.evaluatedCells.delete(child);
47621
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
47622
- this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
47623
47638
  }
47639
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn(invalidated));
47640
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(invalidated));
47624
47641
  this.spreadingRelations.removeNode(position);
47625
47642
  }
47626
47643
  // ----------------------------------------------------------
@@ -47927,7 +47944,7 @@ class EvaluationPlugin extends UIPlugin {
47927
47944
  ? getItemId(newFormat, data.formats)
47928
47945
  : exportedCellData.format;
47929
47946
  let content;
47930
- if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
47947
+ if (isExported && isFormula && formulaCell instanceof FormulaCellWithDependencies) {
47931
47948
  content = formulaCell.contentWithFixedReferences;
47932
47949
  }
47933
47950
  else {
@@ -60557,6 +60574,6 @@ const constants = {
60557
60574
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, 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 };
60558
60575
 
60559
60576
 
60560
- __info__.version = "17.2.5";
60561
- __info__.date = "2024-04-26T07:41:13.193Z";
60562
- __info__.hash = "a730f5c";
60577
+ __info__.version = "17.2.7";
60578
+ __info__.date = "2024-05-15T09:20:44.429Z";
60579
+ __info__.hash = "57e89fa";