@odoo/o-spreadsheet 18.3.12 → 18.3.14

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.3.12
6
- * @date 2025-07-11T11:11:58.998Z
7
- * @hash d14dc96
5
+ * @version 18.3.14
6
+ * @date 2025-07-30T11:18:53.973Z
7
+ * @hash 2a7178a
8
8
  */
9
9
 
10
10
  'use strict';
@@ -8669,12 +8669,12 @@ const AGGREGATOR_NAMES = {
8669
8669
  avg: _t("Average"),
8670
8670
  sum: _t("Sum"),
8671
8671
  };
8672
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8672
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8673
8673
  const AGGREGATORS_BY_FIELD_TYPE = {
8674
- integer: NUMBER_CHAR_AGGREGATORS,
8675
- char: NUMBER_CHAR_AGGREGATORS,
8674
+ integer: DEFAULT_AGGREGATORS,
8675
+ char: DEFAULT_AGGREGATORS,
8676
+ datetime: DEFAULT_AGGREGATORS,
8676
8677
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8677
- datetime: ["max", "min", "count_distinct", "count"],
8678
8678
  };
8679
8679
  const AGGREGATORS = {};
8680
8680
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -18408,11 +18408,17 @@ const COLUMN = {
18408
18408
  if (isEvaluationError(cellReference?.value)) {
18409
18409
  return cellReference;
18410
18410
  }
18411
- const column = cellReference === undefined
18412
- ? this.__originCellPosition?.col
18413
- : toZone(cellReference.value).left;
18414
- assert(() => column !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18415
- return column + 1;
18411
+ if (cellReference === undefined) {
18412
+ assert(() => this.__originCellPosition?.col !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18413
+ return this.__originCellPosition.col + 1;
18414
+ }
18415
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
18416
+ if (zone.left === zone.right) {
18417
+ return zone.left + 1;
18418
+ }
18419
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
18420
+ value: zone.left + col + 1,
18421
+ }));
18416
18422
  },
18417
18423
  isExported: true,
18418
18424
  };
@@ -18631,11 +18637,17 @@ const ROW = {
18631
18637
  if (isEvaluationError(cellReference?.value)) {
18632
18638
  return cellReference;
18633
18639
  }
18634
- const row = cellReference === undefined
18635
- ? this.__originCellPosition?.row
18636
- : toZone(cellReference.value).top;
18637
- assert(() => row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18638
- return row + 1;
18640
+ if (cellReference === undefined) {
18641
+ assert(() => this.__originCellPosition?.row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18642
+ return this.__originCellPosition.row + 1;
18643
+ }
18644
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
18645
+ if (zone.top === zone.bottom) {
18646
+ return zone.top + 1;
18647
+ }
18648
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
18649
+ value: zone.top + row + 1,
18650
+ }));
18639
18651
  },
18640
18652
  isExported: true,
18641
18653
  };
@@ -22615,6 +22627,7 @@ const autoCompleteProviders = new Registry();
22615
22627
 
22616
22628
  autoCompleteProviders.add("dataValidation", {
22617
22629
  displayAllOnInitialContent: true,
22630
+ canBeToggled: false,
22618
22631
  getProposals(tokenAtCursor, content) {
22619
22632
  if (isFormula(content)) {
22620
22633
  return [];
@@ -22622,31 +22635,40 @@ autoCompleteProviders.add("dataValidation", {
22622
22635
  if (!this.composer.currentEditedCell) {
22623
22636
  return [];
22624
22637
  }
22625
- const position = this.composer.currentEditedCell;
22626
- const rule = this.getters.getValidationRuleForCell(position);
22627
- if (!rule ||
22628
- (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
22629
- return [];
22630
- }
22631
- let values;
22632
- if (rule.criterion.type === "isValueInList") {
22633
- values = rule.criterion.values;
22634
- }
22635
- else {
22636
- const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
22637
- values = Array.from(new Set(this.getters
22638
- .getRangeValues(range)
22639
- .filter(isNotNull)
22640
- .map((value) => value.toString())
22641
- .filter((val) => val !== "")));
22642
- }
22643
- return values.map((value) => ({ text: value }));
22638
+ return getProposedValues(this.getters, this.composer.currentEditedCell).map((value) => ({
22639
+ text: value.value?.toString() || "",
22640
+ htmlContent: [{ value: value.label }],
22641
+ fuzzySearchKey: value.label,
22642
+ }));
22644
22643
  },
22645
22644
  selectProposal(tokenAtCursor, value) {
22646
22645
  this.composer.setCurrentContent(value);
22647
22646
  this.composer.stopEdition();
22648
22647
  },
22649
22648
  });
22649
+ function getProposedValues(getters, position) {
22650
+ const rule = getters.getValidationRuleForCell(position);
22651
+ if (!rule ||
22652
+ (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
22653
+ return [];
22654
+ }
22655
+ let values = [];
22656
+ if (rule.criterion.type === "isValueInList") {
22657
+ values = rule.criterion.values.map((value) => ({ label: value, value }));
22658
+ }
22659
+ else {
22660
+ const labelsSet = new Set();
22661
+ const range = getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
22662
+ for (const p of positions(range.zone)) {
22663
+ const cell = getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
22664
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
22665
+ labelsSet.add(cell.formattedValue);
22666
+ values.push({ label: cell.formattedValue, value: cell.value });
22667
+ }
22668
+ }
22669
+ }
22670
+ return values;
22671
+ }
22650
22672
 
22651
22673
  function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
22652
22674
  const pendingHtmlContent = [];
@@ -23511,6 +23533,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23511
23533
  proposals,
23512
23534
  selectProposal: provider.selectProposal,
23513
23535
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
23536
+ canBeToggled: provider.canBeToggled,
23514
23537
  };
23515
23538
  }
23516
23539
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -23533,6 +23556,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23533
23556
  proposals,
23534
23557
  selectProposal: provider.selectProposal,
23535
23558
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
23559
+ canBeToggled: provider.canBeToggled,
23536
23560
  };
23537
23561
  }
23538
23562
  }
@@ -43777,9 +43801,13 @@ class Composer extends owl.Component {
43777
43801
  }
43778
43802
  }
43779
43803
  closeAssistant() {
43804
+ if (!this.canBeToggled)
43805
+ return;
43780
43806
  this.assistant.forcedClosed = true;
43781
43807
  }
43782
43808
  openAssistant() {
43809
+ if (!this.canBeToggled)
43810
+ return;
43783
43811
  this.assistant.forcedClosed = false;
43784
43812
  }
43785
43813
  onWheel(event) {
@@ -43789,6 +43817,9 @@ class Composer extends owl.Component {
43789
43817
  event.stopPropagation();
43790
43818
  }
43791
43819
  }
43820
+ get canBeToggled() {
43821
+ return this.autoCompleteState.provider?.canBeToggled ?? true;
43822
+ }
43792
43823
  // ---------------------------------------------------------------------------
43793
43824
  // Private
43794
43825
  // ---------------------------------------------------------------------------
@@ -43976,7 +44007,7 @@ class Composer extends owl.Component {
43976
44007
  return [...new Set(argsToFocus)];
43977
44008
  }
43978
44009
  autoComplete(value) {
43979
- if (!value || this.assistant.forcedClosed) {
44010
+ if (!value || (this.assistant.forcedClosed && this.canBeToggled)) {
43980
44011
  return;
43981
44012
  }
43982
44013
  this.autoCompleteState.provider?.selectProposal(value);
@@ -45504,7 +45535,8 @@ class ConditionalFormattingEditor extends owl.Component {
45504
45535
  static props = {
45505
45536
  editedCf: Object,
45506
45537
  onCancel: Function,
45507
- onSave: Function,
45538
+ onExit: Function,
45539
+ isNewCf: Boolean,
45508
45540
  };
45509
45541
  static components = {
45510
45542
  SelectionInput,
@@ -45523,6 +45555,7 @@ class ConditionalFormattingEditor extends owl.Component {
45523
45555
  getTextDecoration = getTextDecoration;
45524
45556
  colorNumberString = colorNumberString;
45525
45557
  state;
45558
+ hasEditedCf = this.props.isNewCf;
45526
45559
  setup() {
45527
45560
  this.state = owl.useState({
45528
45561
  errors: [],
@@ -45580,6 +45613,9 @@ class ConditionalFormattingEditor extends owl.Component {
45580
45613
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
45581
45614
  sheetId,
45582
45615
  });
45616
+ if (result.isSuccessful) {
45617
+ this.hasEditedCf = true;
45618
+ }
45583
45619
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
45584
45620
  if (!newCf.suppressErrors) {
45585
45621
  this.state.errors = reasons;
@@ -45601,7 +45637,15 @@ class ConditionalFormattingEditor extends owl.Component {
45601
45637
  onSave() {
45602
45638
  const result = this.updateConditionalFormat({});
45603
45639
  if (result.length === 0) {
45604
- this.props.onSave();
45640
+ this.props.onExit();
45641
+ }
45642
+ }
45643
+ onCancel() {
45644
+ if (this.hasEditedCf) {
45645
+ this.props.onCancel();
45646
+ }
45647
+ else {
45648
+ this.props.onExit();
45605
45649
  }
45606
45650
  }
45607
45651
  getDefaultRules() {
@@ -49955,9 +49999,15 @@ class SpreadsheetPivot {
49955
49999
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
49956
50000
  }
49957
50001
  filterDataEntriesFromDomainNode(dataEntries, domain) {
49958
- const { field, value } = domain;
50002
+ const { field, value, type } = domain;
49959
50003
  const { nameWithGranularity } = this.getDimension(field);
49960
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
50004
+ return dataEntries.filter((entry) => {
50005
+ const cellValue = entry[nameWithGranularity]?.value;
50006
+ if (type === "char") {
50007
+ return String(cellValue) === String(value);
50008
+ }
50009
+ return cellValue === value;
50010
+ });
49961
50011
  }
49962
50012
  getDimension(nameWithGranularity) {
49963
50013
  return this.definition.getDimension(nameWithGranularity);
@@ -52623,6 +52673,11 @@ class GridComposer extends owl.Component {
52623
52673
  rect = this.defaultRect;
52624
52674
  isEditing = false;
52625
52675
  isCellReferenceVisible = false;
52676
+ currentEditedCell = {
52677
+ col: 0,
52678
+ row: 0,
52679
+ sheetId: this.env.model.getters.getActiveSheetId(),
52680
+ };
52626
52681
  composerStore;
52627
52682
  composerFocusStore;
52628
52683
  composerInterface;
@@ -52652,7 +52707,7 @@ class GridComposer extends owl.Component {
52652
52707
  return this.isCellReferenceVisible;
52653
52708
  }
52654
52709
  get cellReference() {
52655
- const { col, row, sheetId } = this.composerStore.currentEditedCell;
52710
+ const { col, row, sheetId } = this.currentEditedCell;
52656
52711
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
52657
52712
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
52658
52713
  }
@@ -52744,12 +52799,17 @@ class GridComposer extends owl.Component {
52744
52799
  if (!isEditing && this.composerFocusStore.activeComposer !== this.composerInterface) {
52745
52800
  this.composerFocusStore.focusComposer(this.composerInterface, { focusMode: "inactive" });
52746
52801
  }
52802
+ let shouldRecomputeRect = isEditing && !deepEquals(this.currentEditedCell, this.composerStore.currentEditedCell);
52747
52803
  if (this.isEditing !== isEditing) {
52748
52804
  this.isEditing = isEditing;
52749
52805
  if (!isEditing) {
52750
52806
  this.rect = this.defaultRect;
52751
52807
  return;
52752
52808
  }
52809
+ this.currentEditedCell = this.composerStore.currentEditedCell;
52810
+ shouldRecomputeRect = true;
52811
+ }
52812
+ if (shouldRecomputeRect) {
52753
52813
  const position = this.env.model.getters.getActivePosition();
52754
52814
  const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
52755
52815
  this.rect = this.env.model.getters.getVisibleRect(zone);
@@ -65876,6 +65936,23 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
65876
65936
  static getters = ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
65877
65937
  tallestCellInRow = {};
65878
65938
  ctx = document.createElement("canvas").getContext("2d");
65939
+ beforeHandle(cmd) {
65940
+ switch (cmd.type) {
65941
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
65942
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
65943
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
65944
+ // size updates may apply to incorrect (pre-insert) rows.
65945
+ case "ADD_COLUMNS_ROWS":
65946
+ if (cmd.dimension === "COL") {
65947
+ return;
65948
+ }
65949
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
65950
+ const newCells = Array(cmd.quantity).fill(undefined);
65951
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
65952
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
65953
+ break;
65954
+ }
65955
+ }
65879
65956
  handle(cmd) {
65880
65957
  switch (cmd.type) {
65881
65958
  case "START":
@@ -65905,16 +65982,6 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
65905
65982
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
65906
65983
  break;
65907
65984
  }
65908
- case "ADD_COLUMNS_ROWS": {
65909
- if (cmd.dimension === "COL") {
65910
- return;
65911
- }
65912
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
65913
- const newCells = Array(cmd.quantity).fill(undefined);
65914
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
65915
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
65916
- break;
65917
- }
65918
65985
  case "RESIZE_COLUMNS_ROWS":
65919
65986
  {
65920
65987
  const sheetId = cmd.sheetId;
@@ -71915,6 +71982,14 @@ class GridSelectionPlugin extends UIPlugin {
71915
71982
  const isBasedBefore = cmd.base < start;
71916
71983
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
71917
71984
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
71985
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
71986
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
71987
+ const size = isCol
71988
+ ? this.getters.getColSize(cmd.sheetId, element)
71989
+ : this.getters.getUserRowSize(cmd.sheetId, element);
71990
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
71991
+ return [element, isDefaultCol ? undefined : size];
71992
+ }));
71918
71993
  const target = [
71919
71994
  {
71920
71995
  left: isCol ? start + deltaCol : 0,
@@ -71945,13 +72020,12 @@ class GridSelectionPlugin extends UIPlugin {
71945
72020
  const col = selection.left;
71946
72021
  const row = selection.top;
71947
72022
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
71948
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
71949
72023
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
71950
72024
  const resizingGroups = {};
71951
72025
  for (const element of toRemove) {
71952
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
72026
+ const size = originalSize[element];
71953
72027
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
71954
- if (size != currentSize) {
72028
+ if (size && size != currentSize) {
71955
72029
  resizingGroups[size] ??= [];
71956
72030
  resizingGroups[size].push(currentIndex);
71957
72031
  currentIndex += 1;
@@ -73745,14 +73819,12 @@ class BottomBarSheet extends owl.Component {
73745
73819
  this.editionState = "initializing";
73746
73820
  }
73747
73821
  stopEdition() {
73748
- const input = this.sheetNameRef.el;
73749
- if (!this.state.isEditing || !input)
73822
+ if (!this.state.isEditing || !this.sheetNameRef.el)
73750
73823
  return;
73751
73824
  this.state.isEditing = false;
73752
73825
  this.editionState = "initializing";
73753
- input.blur();
73826
+ this.sheetNameRef.el.blur();
73754
73827
  const inputValue = this.getInputContent() || "";
73755
- input.innerText = inputValue;
73756
73828
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
73757
73829
  }
73758
73830
  cancelEdition() {
@@ -80850,6 +80922,6 @@ exports.tokenColors = tokenColors;
80850
80922
  exports.tokenize = tokenize;
80851
80923
 
80852
80924
 
80853
- __info__.version = "18.3.12";
80854
- __info__.date = "2025-07-11T11:11:58.998Z";
80855
- __info__.hash = "d14dc96";
80925
+ __info__.version = "18.3.14";
80926
+ __info__.date = "2025-07-30T11:18:53.973Z";
80927
+ __info__.hash = "2a7178a";
@@ -5546,6 +5546,7 @@ declare class HeaderSizeUIPlugin extends CoreViewPlugin<HeaderSizeState> impleme
5546
5546
  static getters: readonly ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
5547
5547
  readonly tallestCellInRow: Immutable<Record<UID, Array<CellWithSize | undefined>>>;
5548
5548
  private ctx;
5549
+ beforeHandle(cmd: Command): void;
5549
5550
  handle(cmd: Command): void;
5550
5551
  getRowSize(sheetId: UID, row: HeaderIndex): Pixel;
5551
5552
  getMaxAnchorOffset(sheetId: UID, height: Pixel, width: Pixel): AnchorOffset;
@@ -8540,6 +8541,7 @@ declare class Composer extends Component<CellComposerProps, SpreadsheetChildEnv>
8540
8541
  closeAssistant(): void;
8541
8542
  openAssistant(): void;
8542
8543
  onWheel(event: WheelEvent): void;
8544
+ get canBeToggled(): boolean;
8543
8545
  private processContent;
8544
8546
  /**
8545
8547
  * Get the HTML content corresponding to the current composer token, divided by lines.
@@ -8592,6 +8594,7 @@ interface AutoCompleteProvider {
8592
8594
  proposals: AutoCompleteProposal[];
8593
8595
  selectProposal(text: string): void;
8594
8596
  autoSelectFirstProposal: boolean;
8597
+ canBeToggled?: boolean;
8595
8598
  }
8596
8599
  interface ComposerStoreInterface {
8597
8600
  currentEditedCell?: CellPosition;
@@ -8611,6 +8614,7 @@ interface ComposerStoreInterface {
8611
8614
  interface AutoCompleteProviderDefinition {
8612
8615
  sequence?: number;
8613
8616
  autoSelectFirstProposal?: boolean;
8617
+ canBeToggled?: boolean;
8614
8618
  displayAllOnInitialContent?: boolean;
8615
8619
  maxDisplayedProposals?: number;
8616
8620
  getProposals(this: {
@@ -9232,6 +9236,7 @@ declare class GridComposer extends Component<Props$I, SpreadsheetChildEnv> {
9232
9236
  private rect;
9233
9237
  private isEditing;
9234
9238
  private isCellReferenceVisible;
9239
+ private currentEditedCell;
9235
9240
  private composerStore;
9236
9241
  composerFocusStore: Store<ComposerFocusStore>;
9237
9242
  private composerInterface;