@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
  'use strict';
@@ -8367,12 +8367,12 @@ const AGGREGATOR_NAMES = {
8367
8367
  avg: _t("Average"),
8368
8368
  sum: _t("Sum"),
8369
8369
  };
8370
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8370
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8371
8371
  const AGGREGATORS_BY_FIELD_TYPE = {
8372
- integer: NUMBER_CHAR_AGGREGATORS,
8373
- char: NUMBER_CHAR_AGGREGATORS,
8372
+ integer: DEFAULT_AGGREGATORS,
8373
+ char: DEFAULT_AGGREGATORS,
8374
+ datetime: DEFAULT_AGGREGATORS,
8374
8375
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8375
- datetime: ["max", "min", "count_distinct", "count"],
8376
8376
  };
8377
8377
  const AGGREGATORS = {};
8378
8378
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -11309,6 +11309,7 @@ const autoCompleteProviders = new Registry();
11309
11309
 
11310
11310
  autoCompleteProviders.add("dataValidation", {
11311
11311
  displayAllOnInitialContent: true,
11312
+ canBeToggled: false,
11312
11313
  getProposals(tokenAtCursor, content) {
11313
11314
  if (content.startsWith("=")) {
11314
11315
  return [];
@@ -11316,31 +11317,40 @@ autoCompleteProviders.add("dataValidation", {
11316
11317
  if (!this.composer.currentEditedCell) {
11317
11318
  return [];
11318
11319
  }
11319
- const position = this.composer.currentEditedCell;
11320
- const rule = this.getters.getValidationRuleForCell(position);
11321
- if (!rule ||
11322
- (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
11323
- return [];
11324
- }
11325
- let values;
11326
- if (rule.criterion.type === "isValueInList") {
11327
- values = rule.criterion.values;
11328
- }
11329
- else {
11330
- const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
11331
- values = Array.from(new Set(this.getters
11332
- .getRangeValues(range)
11333
- .filter(isNotNull)
11334
- .map((value) => value.toString())
11335
- .filter((val) => val !== "")));
11336
- }
11337
- return values.map((value) => ({ text: value }));
11320
+ return getProposedValues(this.getters, this.composer.currentEditedCell).map((value) => ({
11321
+ text: value.value?.toString() || "",
11322
+ htmlContent: [{ value: value.label }],
11323
+ fuzzySearchKey: value.label,
11324
+ }));
11338
11325
  },
11339
11326
  selectProposal(tokenAtCursor, value) {
11340
11327
  this.composer.setCurrentContent(value);
11341
11328
  this.composer.stopEdition();
11342
11329
  },
11343
11330
  });
11331
+ function getProposedValues(getters, position) {
11332
+ const rule = getters.getValidationRuleForCell(position);
11333
+ if (!rule ||
11334
+ (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
11335
+ return [];
11336
+ }
11337
+ let values = [];
11338
+ if (rule.criterion.type === "isValueInList") {
11339
+ values = rule.criterion.values.map((value) => ({ label: value, value }));
11340
+ }
11341
+ else {
11342
+ const labelsSet = new Set();
11343
+ const range = getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
11344
+ for (const p of positions(range.zone)) {
11345
+ const cell = getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
11346
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
11347
+ labelsSet.add(cell.formattedValue);
11348
+ values.push({ label: cell.formattedValue, value: cell.value });
11349
+ }
11350
+ }
11351
+ }
11352
+ return values;
11353
+ }
11344
11354
 
11345
11355
  function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
11346
11356
  const pendingHtmlContent = [];
@@ -19111,11 +19121,17 @@ const COLUMN = {
19111
19121
  if (isEvaluationError(cellReference?.value)) {
19112
19122
  return cellReference;
19113
19123
  }
19114
- const column = cellReference === undefined
19115
- ? this.__originCellPosition?.col
19116
- : toZone(cellReference.value).left;
19117
- assert(() => column !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19118
- return column + 1;
19124
+ if (cellReference === undefined) {
19125
+ assert(() => this.__originCellPosition?.col !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19126
+ return this.__originCellPosition.col + 1;
19127
+ }
19128
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19129
+ if (zone.left === zone.right) {
19130
+ return zone.left + 1;
19131
+ }
19132
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
19133
+ value: zone.left + col + 1,
19134
+ }));
19119
19135
  },
19120
19136
  isExported: true,
19121
19137
  };
@@ -19334,11 +19350,17 @@ const ROW = {
19334
19350
  if (isEvaluationError(cellReference?.value)) {
19335
19351
  return cellReference;
19336
19352
  }
19337
- const row = cellReference === undefined
19338
- ? this.__originCellPosition?.row
19339
- : toZone(cellReference.value).top;
19340
- assert(() => row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19341
- return row + 1;
19353
+ if (cellReference === undefined) {
19354
+ assert(() => this.__originCellPosition?.row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
19355
+ return this.__originCellPosition.row + 1;
19356
+ }
19357
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19358
+ if (zone.top === zone.bottom) {
19359
+ return zone.top + 1;
19360
+ }
19361
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
19362
+ value: zone.top + row + 1,
19363
+ }));
19342
19364
  },
19343
19365
  isExported: true,
19344
19366
  };
@@ -21614,6 +21636,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21614
21636
  proposals,
21615
21637
  selectProposal: provider.selectProposal,
21616
21638
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
21639
+ canBeToggled: provider.canBeToggled,
21617
21640
  };
21618
21641
  }
21619
21642
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -21636,6 +21659,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21636
21659
  proposals,
21637
21660
  selectProposal: provider.selectProposal,
21638
21661
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
21662
+ canBeToggled: provider.canBeToggled,
21639
21663
  };
21640
21664
  }
21641
21665
  }
@@ -41307,9 +41331,13 @@ class Composer extends owl.Component {
41307
41331
  }
41308
41332
  }
41309
41333
  closeAssistant() {
41334
+ if (!this.canBeToggled)
41335
+ return;
41310
41336
  this.assistant.forcedClosed = true;
41311
41337
  }
41312
41338
  openAssistant() {
41339
+ if (!this.canBeToggled)
41340
+ return;
41313
41341
  this.assistant.forcedClosed = false;
41314
41342
  }
41315
41343
  onWheel(event) {
@@ -41319,6 +41347,9 @@ class Composer extends owl.Component {
41319
41347
  event.stopPropagation();
41320
41348
  }
41321
41349
  }
41350
+ get canBeToggled() {
41351
+ return this.autoCompleteState.provider?.canBeToggled ?? true;
41352
+ }
41322
41353
  // ---------------------------------------------------------------------------
41323
41354
  // Private
41324
41355
  // ---------------------------------------------------------------------------
@@ -41454,7 +41485,7 @@ class Composer extends owl.Component {
41454
41485
  }
41455
41486
  }
41456
41487
  autoComplete(value) {
41457
- if (!value || this.assistant.forcedClosed) {
41488
+ if (!value || (this.assistant.forcedClosed && this.canBeToggled)) {
41458
41489
  return;
41459
41490
  }
41460
41491
  this.autoCompleteState.provider?.selectProposal(value);
@@ -42790,7 +42821,8 @@ class ConditionalFormattingEditor extends owl.Component {
42790
42821
  static props = {
42791
42822
  editedCf: Object,
42792
42823
  onCancel: Function,
42793
- onSave: Function,
42824
+ onExit: Function,
42825
+ isNewCf: Boolean,
42794
42826
  };
42795
42827
  static components = {
42796
42828
  SelectionInput,
@@ -42809,6 +42841,7 @@ class ConditionalFormattingEditor extends owl.Component {
42809
42841
  getTextDecoration = getTextDecoration;
42810
42842
  colorNumberString = colorNumberString;
42811
42843
  state;
42844
+ hasEditedCf = this.props.isNewCf;
42812
42845
  setup() {
42813
42846
  this.state = owl.useState({
42814
42847
  errors: [],
@@ -42866,6 +42899,9 @@ class ConditionalFormattingEditor extends owl.Component {
42866
42899
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
42867
42900
  sheetId,
42868
42901
  });
42902
+ if (result.isSuccessful) {
42903
+ this.hasEditedCf = true;
42904
+ }
42869
42905
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
42870
42906
  if (!newCf.suppressErrors) {
42871
42907
  this.state.errors = reasons;
@@ -42887,7 +42923,15 @@ class ConditionalFormattingEditor extends owl.Component {
42887
42923
  onSave() {
42888
42924
  const result = this.updateConditionalFormat({});
42889
42925
  if (result.length === 0) {
42890
- this.props.onSave();
42926
+ this.props.onExit();
42927
+ }
42928
+ }
42929
+ onCancel() {
42930
+ if (this.hasEditedCf) {
42931
+ this.props.onCancel();
42932
+ }
42933
+ else {
42934
+ this.props.onExit();
42891
42935
  }
42892
42936
  }
42893
42937
  getDefaultRules() {
@@ -47238,9 +47282,15 @@ class SpreadsheetPivot {
47238
47282
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
47239
47283
  }
47240
47284
  filterDataEntriesFromDomainNode(dataEntries, domain) {
47241
- const { field, value } = domain;
47285
+ const { field, value, type } = domain;
47242
47286
  const { nameWithGranularity } = this.getDimension(field);
47243
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
47287
+ return dataEntries.filter((entry) => {
47288
+ const cellValue = entry[nameWithGranularity]?.value;
47289
+ if (type === "char") {
47290
+ return String(cellValue) === String(value);
47291
+ }
47292
+ return cellValue === value;
47293
+ });
47244
47294
  }
47245
47295
  getDimension(nameWithGranularity) {
47246
47296
  return this.definition.getDimension(nameWithGranularity);
@@ -49736,6 +49786,11 @@ class GridComposer extends owl.Component {
49736
49786
  rect = this.defaultRect;
49737
49787
  isEditing = false;
49738
49788
  isCellReferenceVisible = false;
49789
+ currentEditedCell = {
49790
+ col: 0,
49791
+ row: 0,
49792
+ sheetId: this.env.model.getters.getActiveSheetId(),
49793
+ };
49739
49794
  composerStore;
49740
49795
  composerFocusStore;
49741
49796
  composerInterface;
@@ -49765,7 +49820,7 @@ class GridComposer extends owl.Component {
49765
49820
  return this.isCellReferenceVisible;
49766
49821
  }
49767
49822
  get cellReference() {
49768
- const { col, row, sheetId } = this.composerStore.currentEditedCell;
49823
+ const { col, row, sheetId } = this.currentEditedCell;
49769
49824
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
49770
49825
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
49771
49826
  }
@@ -49857,12 +49912,17 @@ class GridComposer extends owl.Component {
49857
49912
  if (!isEditing && this.composerFocusStore.activeComposer !== this.composerInterface) {
49858
49913
  this.composerFocusStore.focusComposer(this.composerInterface, { focusMode: "inactive" });
49859
49914
  }
49915
+ let shouldRecomputeRect = isEditing && !deepEquals(this.currentEditedCell, this.composerStore.currentEditedCell);
49860
49916
  if (this.isEditing !== isEditing) {
49861
49917
  this.isEditing = isEditing;
49862
49918
  if (!isEditing) {
49863
49919
  this.rect = this.defaultRect;
49864
49920
  return;
49865
49921
  }
49922
+ this.currentEditedCell = this.composerStore.currentEditedCell;
49923
+ shouldRecomputeRect = true;
49924
+ }
49925
+ if (shouldRecomputeRect) {
49866
49926
  const position = this.env.model.getters.getActivePosition();
49867
49927
  const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
49868
49928
  this.rect = this.env.model.getters.getVisibleRect(zone);
@@ -62971,6 +63031,23 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
62971
63031
  static getters = ["getRowSize", "getHeaderSize"];
62972
63032
  tallestCellInRow = {};
62973
63033
  ctx = document.createElement("canvas").getContext("2d");
63034
+ beforeHandle(cmd) {
63035
+ switch (cmd.type) {
63036
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
63037
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
63038
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
63039
+ // size updates may apply to incorrect (pre-insert) rows.
63040
+ case "ADD_COLUMNS_ROWS":
63041
+ if (cmd.dimension === "COL") {
63042
+ return;
63043
+ }
63044
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
63045
+ const newCells = Array(cmd.quantity).fill(undefined);
63046
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
63047
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
63048
+ break;
63049
+ }
63050
+ }
62974
63051
  handle(cmd) {
62975
63052
  switch (cmd.type) {
62976
63053
  case "START":
@@ -63000,16 +63077,6 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
63000
63077
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
63001
63078
  break;
63002
63079
  }
63003
- case "ADD_COLUMNS_ROWS": {
63004
- if (cmd.dimension === "COL") {
63005
- return;
63006
- }
63007
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
63008
- const newCells = Array(cmd.quantity).fill(undefined);
63009
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
63010
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
63011
- break;
63012
- }
63013
63080
  case "RESIZE_COLUMNS_ROWS":
63014
63081
  {
63015
63082
  const sheetId = cmd.sheetId;
@@ -68762,6 +68829,14 @@ class GridSelectionPlugin extends UIPlugin {
68762
68829
  const isBasedBefore = cmd.base < start;
68763
68830
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
68764
68831
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
68832
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
68833
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
68834
+ const size = isCol
68835
+ ? this.getters.getColSize(cmd.sheetId, element)
68836
+ : this.getters.getUserRowSize(cmd.sheetId, element);
68837
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
68838
+ return [element, isDefaultCol ? undefined : size];
68839
+ }));
68765
68840
  const target = [
68766
68841
  {
68767
68842
  left: isCol ? start + deltaCol : 0,
@@ -68792,13 +68867,12 @@ class GridSelectionPlugin extends UIPlugin {
68792
68867
  const col = selection.left;
68793
68868
  const row = selection.top;
68794
68869
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
68795
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
68796
68870
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
68797
68871
  const resizingGroups = {};
68798
68872
  for (const element of toRemove) {
68799
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
68873
+ const size = originalSize[element];
68800
68874
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
68801
- if (size != currentSize) {
68875
+ if (size && size != currentSize) {
68802
68876
  resizingGroups[size] ??= [];
68803
68877
  resizingGroups[size].push(currentIndex);
68804
68878
  currentIndex += 1;
@@ -70483,14 +70557,12 @@ class BottomBarSheet extends owl.Component {
70483
70557
  this.editionState = "initializing";
70484
70558
  }
70485
70559
  stopEdition() {
70486
- const input = this.sheetNameRef.el;
70487
- if (!this.state.isEditing || !input)
70560
+ if (!this.state.isEditing || !this.sheetNameRef.el)
70488
70561
  return;
70489
70562
  this.state.isEditing = false;
70490
70563
  this.editionState = "initializing";
70491
- input.blur();
70564
+ this.sheetNameRef.el.blur();
70492
70565
  const inputValue = this.getInputContent() || "";
70493
- input.innerText = inputValue;
70494
70566
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
70495
70567
  }
70496
70568
  cancelEdition() {
@@ -77187,6 +77259,6 @@ exports.tokenColors = tokenColors;
77187
77259
  exports.tokenize = tokenize;
77188
77260
 
77189
77261
 
77190
- __info__.version = "18.2.21";
77191
- __info__.date = "2025-07-11T11:11:48.661Z";
77192
- __info__.hash = "1c32303";
77262
+ __info__.version = "18.2.23";
77263
+ __info__.date = "2025-07-30T11:19:55.262Z";
77264
+ __info__.hash = "4419b30";
@@ -5316,6 +5316,7 @@ declare class HeaderSizeUIPlugin extends CoreViewPlugin<HeaderSizeState> impleme
5316
5316
  static getters: readonly ["getRowSize", "getHeaderSize"];
5317
5317
  readonly tallestCellInRow: Immutable<Record<UID, Array<CellWithSize | undefined>>>;
5318
5318
  private ctx;
5319
+ beforeHandle(cmd: Command): void;
5319
5320
  handle(cmd: Command): void;
5320
5321
  getRowSize(sheetId: UID, row: HeaderIndex): Pixel;
5321
5322
  getHeaderSize(sheetId: UID, dimension: Dimension, index: HeaderIndex): Pixel;
@@ -8313,6 +8314,7 @@ declare class Composer extends Component<CellComposerProps, SpreadsheetChildEnv>
8313
8314
  closeAssistant(): void;
8314
8315
  openAssistant(): void;
8315
8316
  onWheel(event: WheelEvent): void;
8317
+ get canBeToggled(): boolean;
8316
8318
  private processContent;
8317
8319
  /**
8318
8320
  * Get the HTML content corresponding to the current composer token, divided by lines.
@@ -8354,6 +8356,7 @@ interface AutoCompleteProvider {
8354
8356
  proposals: AutoCompleteProposal[];
8355
8357
  selectProposal(text: string): void;
8356
8358
  autoSelectFirstProposal: boolean;
8359
+ canBeToggled?: boolean;
8357
8360
  }
8358
8361
  interface ComposerStoreInterface {
8359
8362
  currentEditedCell?: CellPosition;
@@ -8373,6 +8376,7 @@ interface ComposerStoreInterface {
8373
8376
  interface AutoCompleteProviderDefinition {
8374
8377
  sequence?: number;
8375
8378
  autoSelectFirstProposal?: boolean;
8379
+ canBeToggled?: boolean;
8376
8380
  displayAllOnInitialContent?: boolean;
8377
8381
  maxDisplayedProposals?: number;
8378
8382
  getProposals(this: {
@@ -8972,6 +8976,7 @@ declare class GridComposer extends Component<Props$J, SpreadsheetChildEnv> {
8972
8976
  private rect;
8973
8977
  private isEditing;
8974
8978
  private isCellReferenceVisible;
8979
+ private currentEditedCell;
8975
8980
  private composerStore;
8976
8981
  composerFocusStore: Store<ComposerFocusStore>;
8977
8982
  private composerInterface;