@odoo/o-spreadsheet 18.2.21 → 18.2.23

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.2.21
6
- * @date 2025-07-11T11:11:48.661Z
7
- * @hash 1c32303
5
+ * @version 18.2.23
6
+ * @date 2025-07-30T11:19:55.262Z
7
+ * @hash 4419b30
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -8365,12 +8365,12 @@ const AGGREGATOR_NAMES = {
8365
8365
  avg: _t("Average"),
8366
8366
  sum: _t("Sum"),
8367
8367
  };
8368
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8368
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8369
8369
  const AGGREGATORS_BY_FIELD_TYPE = {
8370
- integer: NUMBER_CHAR_AGGREGATORS,
8371
- char: NUMBER_CHAR_AGGREGATORS,
8370
+ integer: DEFAULT_AGGREGATORS,
8371
+ char: DEFAULT_AGGREGATORS,
8372
+ datetime: DEFAULT_AGGREGATORS,
8372
8373
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8373
- datetime: ["max", "min", "count_distinct", "count"],
8374
8374
  };
8375
8375
  const AGGREGATORS = {};
8376
8376
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -11307,6 +11307,7 @@ const autoCompleteProviders = new Registry();
11307
11307
 
11308
11308
  autoCompleteProviders.add("dataValidation", {
11309
11309
  displayAllOnInitialContent: true,
11310
+ canBeToggled: false,
11310
11311
  getProposals(tokenAtCursor, content) {
11311
11312
  if (content.startsWith("=")) {
11312
11313
  return [];
@@ -11314,31 +11315,40 @@ autoCompleteProviders.add("dataValidation", {
11314
11315
  if (!this.composer.currentEditedCell) {
11315
11316
  return [];
11316
11317
  }
11317
- const position = this.composer.currentEditedCell;
11318
- const rule = this.getters.getValidationRuleForCell(position);
11319
- if (!rule ||
11320
- (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
11321
- return [];
11322
- }
11323
- let values;
11324
- if (rule.criterion.type === "isValueInList") {
11325
- values = rule.criterion.values;
11326
- }
11327
- else {
11328
- const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
11329
- values = Array.from(new Set(this.getters
11330
- .getRangeValues(range)
11331
- .filter(isNotNull)
11332
- .map((value) => value.toString())
11333
- .filter((val) => val !== "")));
11334
- }
11335
- return values.map((value) => ({ text: value }));
11318
+ return getProposedValues(this.getters, this.composer.currentEditedCell).map((value) => ({
11319
+ text: value.value?.toString() || "",
11320
+ htmlContent: [{ value: value.label }],
11321
+ fuzzySearchKey: value.label,
11322
+ }));
11336
11323
  },
11337
11324
  selectProposal(tokenAtCursor, value) {
11338
11325
  this.composer.setCurrentContent(value);
11339
11326
  this.composer.stopEdition();
11340
11327
  },
11341
11328
  });
11329
+ function getProposedValues(getters, position) {
11330
+ const rule = getters.getValidationRuleForCell(position);
11331
+ if (!rule ||
11332
+ (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
11333
+ return [];
11334
+ }
11335
+ let values = [];
11336
+ if (rule.criterion.type === "isValueInList") {
11337
+ values = rule.criterion.values.map((value) => ({ label: value, value }));
11338
+ }
11339
+ else {
11340
+ const labelsSet = new Set();
11341
+ const range = getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
11342
+ for (const p of positions(range.zone)) {
11343
+ const cell = getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
11344
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
11345
+ labelsSet.add(cell.formattedValue);
11346
+ values.push({ label: cell.formattedValue, value: cell.value });
11347
+ }
11348
+ }
11349
+ }
11350
+ return values;
11351
+ }
11342
11352
 
11343
11353
  function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
11344
11354
  const pendingHtmlContent = [];
@@ -19109,11 +19119,17 @@ const COLUMN = {
19109
19119
  if (isEvaluationError(cellReference?.value)) {
19110
19120
  return cellReference;
19111
19121
  }
19112
- const column = cellReference === undefined
19113
- ? this.__originCellPosition?.col
19114
- : toZone(cellReference.value).left;
19115
- assert(() => column !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19116
- return column + 1;
19122
+ if (cellReference === undefined) {
19123
+ assert(() => this.__originCellPosition?.col !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19124
+ return this.__originCellPosition.col + 1;
19125
+ }
19126
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19127
+ if (zone.left === zone.right) {
19128
+ return zone.left + 1;
19129
+ }
19130
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
19131
+ value: zone.left + col + 1,
19132
+ }));
19117
19133
  },
19118
19134
  isExported: true,
19119
19135
  };
@@ -19332,11 +19348,17 @@ const ROW = {
19332
19348
  if (isEvaluationError(cellReference?.value)) {
19333
19349
  return cellReference;
19334
19350
  }
19335
- const row = cellReference === undefined
19336
- ? this.__originCellPosition?.row
19337
- : toZone(cellReference.value).top;
19338
- assert(() => row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19339
- return row + 1;
19351
+ if (cellReference === undefined) {
19352
+ assert(() => this.__originCellPosition?.row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19353
+ return this.__originCellPosition.row + 1;
19354
+ }
19355
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19356
+ if (zone.top === zone.bottom) {
19357
+ return zone.top + 1;
19358
+ }
19359
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
19360
+ value: zone.top + row + 1,
19361
+ }));
19340
19362
  },
19341
19363
  isExported: true,
19342
19364
  };
@@ -21612,6 +21634,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21612
21634
  proposals,
21613
21635
  selectProposal: provider.selectProposal,
21614
21636
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
21637
+ canBeToggled: provider.canBeToggled,
21615
21638
  };
21616
21639
  }
21617
21640
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -21634,6 +21657,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21634
21657
  proposals,
21635
21658
  selectProposal: provider.selectProposal,
21636
21659
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
21660
+ canBeToggled: provider.canBeToggled,
21637
21661
  };
21638
21662
  }
21639
21663
  }
@@ -41305,9 +41329,13 @@ class Composer extends Component {
41305
41329
  }
41306
41330
  }
41307
41331
  closeAssistant() {
41332
+ if (!this.canBeToggled)
41333
+ return;
41308
41334
  this.assistant.forcedClosed = true;
41309
41335
  }
41310
41336
  openAssistant() {
41337
+ if (!this.canBeToggled)
41338
+ return;
41311
41339
  this.assistant.forcedClosed = false;
41312
41340
  }
41313
41341
  onWheel(event) {
@@ -41317,6 +41345,9 @@ class Composer extends Component {
41317
41345
  event.stopPropagation();
41318
41346
  }
41319
41347
  }
41348
+ get canBeToggled() {
41349
+ return this.autoCompleteState.provider?.canBeToggled ?? true;
41350
+ }
41320
41351
  // ---------------------------------------------------------------------------
41321
41352
  // Private
41322
41353
  // ---------------------------------------------------------------------------
@@ -41452,7 +41483,7 @@ class Composer extends Component {
41452
41483
  }
41453
41484
  }
41454
41485
  autoComplete(value) {
41455
- if (!value || this.assistant.forcedClosed) {
41486
+ if (!value || (this.assistant.forcedClosed && this.canBeToggled)) {
41456
41487
  return;
41457
41488
  }
41458
41489
  this.autoCompleteState.provider?.selectProposal(value);
@@ -42788,7 +42819,8 @@ class ConditionalFormattingEditor extends Component {
42788
42819
  static props = {
42789
42820
  editedCf: Object,
42790
42821
  onCancel: Function,
42791
- onSave: Function,
42822
+ onExit: Function,
42823
+ isNewCf: Boolean,
42792
42824
  };
42793
42825
  static components = {
42794
42826
  SelectionInput,
@@ -42807,6 +42839,7 @@ class ConditionalFormattingEditor extends Component {
42807
42839
  getTextDecoration = getTextDecoration;
42808
42840
  colorNumberString = colorNumberString;
42809
42841
  state;
42842
+ hasEditedCf = this.props.isNewCf;
42810
42843
  setup() {
42811
42844
  this.state = useState({
42812
42845
  errors: [],
@@ -42864,6 +42897,9 @@ class ConditionalFormattingEditor extends Component {
42864
42897
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
42865
42898
  sheetId,
42866
42899
  });
42900
+ if (result.isSuccessful) {
42901
+ this.hasEditedCf = true;
42902
+ }
42867
42903
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
42868
42904
  if (!newCf.suppressErrors) {
42869
42905
  this.state.errors = reasons;
@@ -42885,7 +42921,15 @@ class ConditionalFormattingEditor extends Component {
42885
42921
  onSave() {
42886
42922
  const result = this.updateConditionalFormat({});
42887
42923
  if (result.length === 0) {
42888
- this.props.onSave();
42924
+ this.props.onExit();
42925
+ }
42926
+ }
42927
+ onCancel() {
42928
+ if (this.hasEditedCf) {
42929
+ this.props.onCancel();
42930
+ }
42931
+ else {
42932
+ this.props.onExit();
42889
42933
  }
42890
42934
  }
42891
42935
  getDefaultRules() {
@@ -47236,9 +47280,15 @@ class SpreadsheetPivot {
47236
47280
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
47237
47281
  }
47238
47282
  filterDataEntriesFromDomainNode(dataEntries, domain) {
47239
- const { field, value } = domain;
47283
+ const { field, value, type } = domain;
47240
47284
  const { nameWithGranularity } = this.getDimension(field);
47241
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
47285
+ return dataEntries.filter((entry) => {
47286
+ const cellValue = entry[nameWithGranularity]?.value;
47287
+ if (type === "char") {
47288
+ return String(cellValue) === String(value);
47289
+ }
47290
+ return cellValue === value;
47291
+ });
47242
47292
  }
47243
47293
  getDimension(nameWithGranularity) {
47244
47294
  return this.definition.getDimension(nameWithGranularity);
@@ -49734,6 +49784,11 @@ class GridComposer extends Component {
49734
49784
  rect = this.defaultRect;
49735
49785
  isEditing = false;
49736
49786
  isCellReferenceVisible = false;
49787
+ currentEditedCell = {
49788
+ col: 0,
49789
+ row: 0,
49790
+ sheetId: this.env.model.getters.getActiveSheetId(),
49791
+ };
49737
49792
  composerStore;
49738
49793
  composerFocusStore;
49739
49794
  composerInterface;
@@ -49763,7 +49818,7 @@ class GridComposer extends Component {
49763
49818
  return this.isCellReferenceVisible;
49764
49819
  }
49765
49820
  get cellReference() {
49766
- const { col, row, sheetId } = this.composerStore.currentEditedCell;
49821
+ const { col, row, sheetId } = this.currentEditedCell;
49767
49822
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
49768
49823
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
49769
49824
  }
@@ -49855,12 +49910,17 @@ class GridComposer extends Component {
49855
49910
  if (!isEditing && this.composerFocusStore.activeComposer !== this.composerInterface) {
49856
49911
  this.composerFocusStore.focusComposer(this.composerInterface, { focusMode: "inactive" });
49857
49912
  }
49913
+ let shouldRecomputeRect = isEditing && !deepEquals(this.currentEditedCell, this.composerStore.currentEditedCell);
49858
49914
  if (this.isEditing !== isEditing) {
49859
49915
  this.isEditing = isEditing;
49860
49916
  if (!isEditing) {
49861
49917
  this.rect = this.defaultRect;
49862
49918
  return;
49863
49919
  }
49920
+ this.currentEditedCell = this.composerStore.currentEditedCell;
49921
+ shouldRecomputeRect = true;
49922
+ }
49923
+ if (shouldRecomputeRect) {
49864
49924
  const position = this.env.model.getters.getActivePosition();
49865
49925
  const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
49866
49926
  this.rect = this.env.model.getters.getVisibleRect(zone);
@@ -62969,6 +63029,23 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
62969
63029
  static getters = ["getRowSize", "getHeaderSize"];
62970
63030
  tallestCellInRow = {};
62971
63031
  ctx = document.createElement("canvas").getContext("2d");
63032
+ beforeHandle(cmd) {
63033
+ switch (cmd.type) {
63034
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
63035
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
63036
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
63037
+ // size updates may apply to incorrect (pre-insert) rows.
63038
+ case "ADD_COLUMNS_ROWS":
63039
+ if (cmd.dimension === "COL") {
63040
+ return;
63041
+ }
63042
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
63043
+ const newCells = Array(cmd.quantity).fill(undefined);
63044
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
63045
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
63046
+ break;
63047
+ }
63048
+ }
62972
63049
  handle(cmd) {
62973
63050
  switch (cmd.type) {
62974
63051
  case "START":
@@ -62998,16 +63075,6 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
62998
63075
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
62999
63076
  break;
63000
63077
  }
63001
- case "ADD_COLUMNS_ROWS": {
63002
- if (cmd.dimension === "COL") {
63003
- return;
63004
- }
63005
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
63006
- const newCells = Array(cmd.quantity).fill(undefined);
63007
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
63008
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
63009
- break;
63010
- }
63011
63078
  case "RESIZE_COLUMNS_ROWS":
63012
63079
  {
63013
63080
  const sheetId = cmd.sheetId;
@@ -68760,6 +68827,14 @@ class GridSelectionPlugin extends UIPlugin {
68760
68827
  const isBasedBefore = cmd.base < start;
68761
68828
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
68762
68829
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
68830
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
68831
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
68832
+ const size = isCol
68833
+ ? this.getters.getColSize(cmd.sheetId, element)
68834
+ : this.getters.getUserRowSize(cmd.sheetId, element);
68835
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
68836
+ return [element, isDefaultCol ? undefined : size];
68837
+ }));
68763
68838
  const target = [
68764
68839
  {
68765
68840
  left: isCol ? start + deltaCol : 0,
@@ -68790,13 +68865,12 @@ class GridSelectionPlugin extends UIPlugin {
68790
68865
  const col = selection.left;
68791
68866
  const row = selection.top;
68792
68867
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
68793
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
68794
68868
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
68795
68869
  const resizingGroups = {};
68796
68870
  for (const element of toRemove) {
68797
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
68871
+ const size = originalSize[element];
68798
68872
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
68799
- if (size != currentSize) {
68873
+ if (size && size != currentSize) {
68800
68874
  resizingGroups[size] ??= [];
68801
68875
  resizingGroups[size].push(currentIndex);
68802
68876
  currentIndex += 1;
@@ -70481,14 +70555,12 @@ class BottomBarSheet extends Component {
70481
70555
  this.editionState = "initializing";
70482
70556
  }
70483
70557
  stopEdition() {
70484
- const input = this.sheetNameRef.el;
70485
- if (!this.state.isEditing || !input)
70558
+ if (!this.state.isEditing || !this.sheetNameRef.el)
70486
70559
  return;
70487
70560
  this.state.isEditing = false;
70488
70561
  this.editionState = "initializing";
70489
- input.blur();
70562
+ this.sheetNameRef.el.blur();
70490
70563
  const inputValue = this.getInputContent() || "";
70491
- input.innerText = inputValue;
70492
70564
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
70493
70565
  }
70494
70566
  cancelEdition() {
@@ -77140,6 +77212,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
77140
77212
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
77141
77213
 
77142
77214
 
77143
- __info__.version = "18.2.21";
77144
- __info__.date = "2025-07-11T11:11:48.661Z";
77145
- __info__.hash = "1c32303";
77215
+ __info__.version = "18.2.23";
77216
+ __info__.date = "2025-07-30T11:19:55.262Z";
77217
+ __info__.hash = "4419b30";