@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
  'use strict';
@@ -554,7 +554,7 @@ function getAddHeaderStartIndex(position, base) {
554
554
  /**
555
555
  * Compares two objects.
556
556
  */
557
- function deepEquals(o1, o2) {
557
+ function deepEquals(o1, o2, ignoreFunctions) {
558
558
  if (o1 === o2)
559
559
  return true;
560
560
  if ((o1 && !o2) || (o2 && !o1))
@@ -570,13 +570,16 @@ function deepEquals(o1, o2) {
570
570
  }
571
571
  }
572
572
  for (const key in o1) {
573
- if (typeof o1[key] !== typeof o2[key])
573
+ const typeOfO1Key = typeof o1[key];
574
+ if (typeOfO1Key !== typeof o2[key])
574
575
  return false;
575
- if (typeof o1[key] === "object") {
576
- if (!deepEquals(o1[key], o2[key]))
576
+ if (typeOfO1Key === "object") {
577
+ if (!deepEquals(o1[key], o2[key], ignoreFunctions))
577
578
  return false;
578
579
  }
579
580
  else {
581
+ if (ignoreFunctions && typeOfO1Key === "function")
582
+ return true;
580
583
  if (o1[key] !== o2[key])
581
584
  return false;
582
585
  }
@@ -3684,21 +3687,22 @@ function toZoneWithoutBoundaryChanges(xc) {
3684
3687
  xc = xc.split("!").at(-1);
3685
3688
  }
3686
3689
  if (xc.includes("$")) {
3687
- xc = xc.replace(/\$/g, "");
3690
+ xc = xc.replaceAll("$", "");
3688
3691
  }
3689
- let ranges;
3692
+ let firstRangePart = "";
3693
+ let secondRangePart;
3690
3694
  if (xc.includes(":")) {
3691
- ranges = xc.split(":").map((x) => x.trim());
3695
+ [firstRangePart, secondRangePart] = xc.split(":");
3696
+ firstRangePart = firstRangePart.trim();
3697
+ secondRangePart = secondRangePart.trim();
3692
3698
  }
3693
3699
  else {
3694
- ranges = [xc.trim()];
3700
+ firstRangePart = xc.trim();
3695
3701
  }
3696
3702
  let top, bottom, left, right;
3697
3703
  let fullCol = false;
3698
3704
  let fullRow = false;
3699
3705
  let hasHeader = false;
3700
- const firstRangePart = ranges[0];
3701
- const secondRangePart = ranges[1] && ranges[1];
3702
3706
  if (isColReference(firstRangePart)) {
3703
3707
  left = right = lettersToNumber(firstRangePart);
3704
3708
  top = bottom = 0;
@@ -3715,7 +3719,7 @@ function toZoneWithoutBoundaryChanges(xc) {
3715
3719
  top = bottom = c.row;
3716
3720
  hasHeader = true;
3717
3721
  }
3718
- if (ranges.length === 2) {
3722
+ if (secondRangePart) {
3719
3723
  if (isColReference(secondRangePart)) {
3720
3724
  right = lettersToNumber(secondRangePart);
3721
3725
  fullCol = true;
@@ -5199,7 +5203,7 @@ function tokenizeString(chars) {
5199
5203
  }
5200
5204
  return null;
5201
5205
  }
5202
- const separatorRegexp = /^[\w\.!\$]+/;
5206
+ const SYMBOL_CHARS = new Set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.!$");
5203
5207
  /**
5204
5208
  * A "Symbol" is just basically any word-like element that can appear in a
5205
5209
  * formula, which is not a string. So:
@@ -5239,11 +5243,8 @@ function tokenizeSymbol(chars) {
5239
5243
  };
5240
5244
  }
5241
5245
  }
5242
- const match = chars.remaining().match(separatorRegexp);
5243
- if (match) {
5244
- const value = match[0];
5245
- result += value;
5246
- chars.advanceBy(value.length);
5246
+ while (chars.current && SYMBOL_CHARS.has(chars.current)) {
5247
+ result += chars.shift();
5247
5248
  }
5248
5249
  if (result.length) {
5249
5250
  const value = result;
@@ -6795,7 +6796,7 @@ const machine = {
6795
6796
  function matchReference(tokens) {
6796
6797
  let head = 0;
6797
6798
  let transitions = machine[State.LeftRef];
6798
- const matchedTokens = [];
6799
+ let matchedTokens = "";
6799
6800
  while (transitions !== undefined) {
6800
6801
  const token = tokens[head++];
6801
6802
  if (!token) {
@@ -6807,15 +6808,15 @@ function matchReference(tokens) {
6807
6808
  case undefined:
6808
6809
  return null;
6809
6810
  case State.Found:
6810
- matchedTokens.push(token);
6811
+ matchedTokens += token.value;
6811
6812
  tokens.splice(0, head);
6812
6813
  return {
6813
6814
  type: "REFERENCE",
6814
- value: concat(matchedTokens.map((token) => token.value)),
6815
+ value: matchedTokens,
6815
6816
  };
6816
6817
  default:
6817
6818
  transitions = machine[nextState];
6818
- matchedTokens.push(token);
6819
+ matchedTokens += token.value;
6819
6820
  break;
6820
6821
  }
6821
6822
  }
@@ -8906,8 +8907,7 @@ class ComposerStore extends SpreadsheetStore {
8906
8907
  const exactMatch = proposals?.find((p) => p.text === tokenAtCursor.value);
8907
8908
  // remove tokens that are likely to be other parts of the formula that slipped in the token if it's a string
8908
8909
  const searchTerm = tokenAtCursor.value.replace(/[ ,\(\)]/g, "");
8909
- const initialContent = this.initialContent;
8910
- if (exactMatch && exactMatch.text !== initialContent) {
8910
+ if (exactMatch && this._currentContent !== this.initialContent) {
8911
8911
  // this means the user has chosen a proposal
8912
8912
  return;
8913
8913
  }
@@ -8915,7 +8915,7 @@ class ComposerStore extends SpreadsheetStore {
8915
8915
  proposals &&
8916
8916
  !["ARG_SEPARATOR", "LEFT_PAREN"].includes(tokenAtCursor.type)) {
8917
8917
  const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
8918
- if (!exactMatch) {
8918
+ if (!exactMatch || filteredProposals.length > 1) {
8919
8919
  proposals = filteredProposals;
8920
8920
  }
8921
8921
  }
@@ -9050,6 +9050,7 @@ class ChartJsComponent extends owl.Component {
9050
9050
  };
9051
9051
  canvas = owl.useRef("graphContainer");
9052
9052
  chart;
9053
+ currentRuntime;
9053
9054
  get background() {
9054
9055
  return this.chartRuntime.background;
9055
9056
  }
@@ -9066,9 +9067,18 @@ class ChartJsComponent extends owl.Component {
9066
9067
  setup() {
9067
9068
  owl.onMounted(() => {
9068
9069
  const runtime = this.chartRuntime;
9069
- this.createChart(runtime.chartJsConfig);
9070
+ this.currentRuntime = runtime;
9071
+ // Note: chartJS modify the runtime in place, so it's important to give it a copy
9072
+ this.createChart(deepCopy(runtime.chartJsConfig));
9073
+ });
9074
+ owl.onWillUnmount(() => this.chart?.destroy());
9075
+ owl.useEffect(() => {
9076
+ const runtime = this.chartRuntime;
9077
+ if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
9078
+ this.currentRuntime = runtime;
9079
+ this.updateChartJs(deepCopy(runtime));
9080
+ }
9070
9081
  });
9071
- owl.useEffect(() => this.updateChartJs(this.chartRuntime), () => [this.chartRuntime]);
9072
9082
  }
9073
9083
  createChart(chartData) {
9074
9084
  const canvas = this.canvas.el;
@@ -9090,8 +9100,7 @@ class ChartJsComponent extends owl.Component {
9090
9100
  this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip;
9091
9101
  this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
9092
9102
  this.chart.config.options.scales = chartData.options?.scales;
9093
- // ?
9094
- this.chart.update("active");
9103
+ this.chart.update();
9095
9104
  }
9096
9105
  }
9097
9106
 
@@ -19454,7 +19463,7 @@ function truncateLabel(label) {
19454
19463
  /**
19455
19464
  * Get a default chart js configuration
19456
19465
  */
19457
- function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
19466
+ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels }) {
19458
19467
  const options = {
19459
19468
  // https://www.chartjs.org/docs/latest/general/responsive.html
19460
19469
  responsive: true,
@@ -19501,7 +19510,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
19501
19510
  type: chart.type,
19502
19511
  options,
19503
19512
  data: {
19504
- labels: labels.map(truncateLabel),
19513
+ labels: truncateLabels ? labels.map(truncateLabel) : labels,
19505
19514
  datasets: [],
19506
19515
  },
19507
19516
  platform: undefined,
@@ -20267,9 +20276,9 @@ function isLuxonTimeAdapterInstalled() {
20267
20276
  }
20268
20277
  return isInstalled;
20269
20278
  }
20270
- function getLineOrScatterConfiguration(chart, labels, localeFormat) {
20279
+ function getLineOrScatterConfiguration(chart, labels, options) {
20271
20280
  const fontColor = chartFontColor(chart.background);
20272
- const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
20281
+ const config = getDefaultChartJsRuntime(chart, labels, fontColor, options);
20273
20282
  const legend = {
20274
20283
  labels: {
20275
20284
  color: fontColor,
@@ -20312,7 +20321,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
20312
20321
  value = Number(value);
20313
20322
  if (isNaN(value))
20314
20323
  return value;
20315
- const { locale, format } = localeFormat;
20324
+ const { locale, format } = options;
20316
20325
  return formatValue(value, {
20317
20326
  locale,
20318
20327
  format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
@@ -20345,9 +20354,10 @@ function createLineOrScatterChartRuntime(chart, getters) {
20345
20354
  ({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
20346
20355
  }
20347
20356
  const locale = getters.getLocale();
20357
+ const truncateLabels = axisType === "category";
20348
20358
  const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
20349
- const localeFormat = { format: dataSetFormat, locale };
20350
- const config = getLineOrScatterConfiguration(chart, labels, localeFormat);
20359
+ const options = { format: dataSetFormat, locale, truncateLabels };
20360
+ const config = getLineOrScatterConfiguration(chart, labels, options);
20351
20361
  const labelFormat = getChartLabelFormat(getters, chart.labelRange);
20352
20362
  if (axisType === "time") {
20353
20363
  const axis = {
@@ -32901,7 +32911,8 @@ class DataValidationOverlay extends owl.Component {
32901
32911
  get checkBoxCellPositions() {
32902
32912
  return this.env.model.getters
32903
32913
  .getVisibleCellPositions()
32904
- .filter(this.env.model.getters.isCellValidCheckbox);
32914
+ .filter((position) => this.env.model.getters.isCellValidCheckbox(position) &&
32915
+ !this.env.model.getters.isFilterHeader(position));
32905
32916
  }
32906
32917
  get listIconsCellPositions() {
32907
32918
  if (this.env.model.getters.isReadonly()) {
@@ -32909,7 +32920,8 @@ class DataValidationOverlay extends owl.Component {
32909
32920
  }
32910
32921
  return this.env.model.getters
32911
32922
  .getVisibleCellPositions()
32912
- .filter(this.env.model.getters.cellHasListDataValidationIcon);
32923
+ .filter((position) => this.env.model.getters.cellHasListDataValidationIcon(position) &&
32924
+ !this.env.model.getters.isFilterHeader(position));
32913
32925
  }
32914
32926
  }
32915
32927
 
@@ -47422,9 +47434,10 @@ class Evaluator {
47422
47434
  }
47423
47435
  if (!content) {
47424
47436
  // The previous content could have blocked some array formulas
47425
- impactedPositions.addMany(this.getArrayFormulasBlockedBy(position));
47437
+ impactedPositions.add(position);
47426
47438
  }
47427
47439
  }
47440
+ impactedPositions.addMany(this.getArrayFormulasBlockedBy(impactedPositions));
47428
47441
  return impactedPositions;
47429
47442
  }
47430
47443
  buildDependencyGraph() {
@@ -47466,23 +47479,25 @@ class Evaluator {
47466
47479
  return positions;
47467
47480
  }
47468
47481
  /**
47469
- * Return the position of formulas blocked by the given position
47482
+ * Return the position of formulas blocked by the given positions
47470
47483
  * as well as all their dependencies.
47471
47484
  */
47472
- getArrayFormulasBlockedBy(position) {
47473
- if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
47474
- return [];
47475
- }
47476
- const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
47477
- const positions = this.createEmptyPositionSet();
47478
- positions.addMany(arrayFormulas);
47479
- const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
47480
- if (arrayFormulaPosition) {
47481
- // ignore the formula spreading on the position. Keep only the blocked ones
47482
- positions.delete(arrayFormulaPosition);
47485
+ getArrayFormulasBlockedBy(positions) {
47486
+ const arrayFormulaPositions = this.createEmptyPositionSet();
47487
+ for (const position of positions) {
47488
+ if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
47489
+ continue;
47490
+ }
47491
+ const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
47492
+ arrayFormulaPositions.addMany(arrayFormulas);
47493
+ const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
47494
+ if (arrayFormulaPosition) {
47495
+ // ignore the formula spreading on the position. Keep only the blocked ones
47496
+ arrayFormulaPositions.delete(arrayFormulaPosition);
47497
+ }
47483
47498
  }
47484
- positions.addMany(this.getCellsDependingOn(positions));
47485
- return positions;
47499
+ arrayFormulaPositions.addMany(this.getCellsDependingOn(arrayFormulaPositions));
47500
+ return arrayFormulaPositions;
47486
47501
  }
47487
47502
  nextPositionsToUpdate = new PositionSet({});
47488
47503
  cellsBeingComputed = new Set();
@@ -47612,6 +47627,7 @@ class Evaluator {
47612
47627
  if (!this.spreadingRelations.isArrayFormula(position)) {
47613
47628
  return;
47614
47629
  }
47630
+ const invalidated = this.createEmptyPositionSet();
47615
47631
  for (const child of this.spreadingRelations.getArrayResultPositions(position)) {
47616
47632
  const content = this.getters.getCell(child)?.content;
47617
47633
  if (content) {
@@ -47619,10 +47635,11 @@ class Evaluator {
47619
47635
  // there's still a collision
47620
47636
  continue;
47621
47637
  }
47638
+ invalidated.add(child);
47622
47639
  this.evaluatedCells.delete(child);
47623
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
47624
- this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
47625
47640
  }
47641
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn(invalidated));
47642
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(invalidated));
47626
47643
  this.spreadingRelations.removeNode(position);
47627
47644
  }
47628
47645
  // ----------------------------------------------------------
@@ -47929,7 +47946,7 @@ class EvaluationPlugin extends UIPlugin {
47929
47946
  ? getItemId(newFormat, data.formats)
47930
47947
  : exportedCellData.format;
47931
47948
  let content;
47932
- if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
47949
+ if (isExported && isFormula && formulaCell instanceof FormulaCellWithDependencies) {
47933
47950
  content = formulaCell.contentWithFixedReferences;
47934
47951
  }
47935
47952
  else {
@@ -60600,6 +60617,6 @@ exports.tokenColors = tokenColors;
60600
60617
  exports.tokenize = tokenize;
60601
60618
 
60602
60619
 
60603
- __info__.version = "17.2.5";
60604
- __info__.date = "2024-04-26T07:41:13.193Z";
60605
- __info__.hash = "a730f5c";
60620
+ __info__.version = "17.2.7";
60621
+ __info__.date = "2024-05-15T09:20:44.429Z";
60622
+ __info__.hash = "57e89fa";
@@ -4937,7 +4937,7 @@ declare function lazy<T>(fn: (() => T) | T): Lazy<T>;
4937
4937
  /**
4938
4938
  * Compares two objects.
4939
4939
  */
4940
- declare function deepEquals(o1: any, o2: any): boolean;
4940
+ declare function deepEquals(o1: any, o2: any, ignoreFunctions?: "ignoreFunctions"): boolean;
4941
4941
 
4942
4942
  interface ConstructorArgs {
4943
4943
  readonly zone: Readonly<Zone | UnboundedZone>;
@@ -6858,6 +6858,7 @@ declare class ChartJsComponent extends Component<Props$y, SpreadsheetChildEnv> {
6858
6858
  };
6859
6859
  private canvas;
6860
6860
  private chart?;
6861
+ private currentRuntime;
6861
6862
  get background(): string;
6862
6863
  get canvasStyle(): string;
6863
6864
  get chartRuntime(): ChartJSRuntime;
@@ -8028,7 +8029,9 @@ declare function chartFontColor(backgroundColor: Color | undefined): Color;
8028
8029
  /**
8029
8030
  * Get a default chart js configuration
8030
8031
  */
8031
- declare function getDefaultChartJsRuntime(chart: AbstractChart, labels: string[], fontColor: Color, { format, locale }: LocaleFormat): Required<ChartConfiguration>;
8032
+ declare function getDefaultChartJsRuntime(chart: AbstractChart, labels: string[], fontColor: Color, { format, locale, truncateLabels }: LocaleFormat & {
8033
+ truncateLabels?: boolean;
8034
+ }): Required<ChartConfiguration>;
8032
8035
  /** See https://www.chartjs.org/docs/latest/charts/area.html#filling-modes */
8033
8036
  declare function getFillingMode(index: number): "origin" | number;
8034
8037