@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
  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';
@@ -8667,12 +8667,12 @@ const AGGREGATOR_NAMES = {
8667
8667
  avg: _t("Average"),
8668
8668
  sum: _t("Sum"),
8669
8669
  };
8670
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8670
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8671
8671
  const AGGREGATORS_BY_FIELD_TYPE = {
8672
- integer: NUMBER_CHAR_AGGREGATORS,
8673
- char: NUMBER_CHAR_AGGREGATORS,
8672
+ integer: DEFAULT_AGGREGATORS,
8673
+ char: DEFAULT_AGGREGATORS,
8674
+ datetime: DEFAULT_AGGREGATORS,
8674
8675
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8675
- datetime: ["max", "min", "count_distinct", "count"],
8676
8676
  };
8677
8677
  const AGGREGATORS = {};
8678
8678
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -18406,11 +18406,17 @@ const COLUMN = {
18406
18406
  if (isEvaluationError(cellReference?.value)) {
18407
18407
  return cellReference;
18408
18408
  }
18409
- const column = cellReference === undefined
18410
- ? this.__originCellPosition?.col
18411
- : toZone(cellReference.value).left;
18412
- assert(() => column !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18413
- return column + 1;
18409
+ if (cellReference === undefined) {
18410
+ assert(() => this.__originCellPosition?.col !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18411
+ return this.__originCellPosition.col + 1;
18412
+ }
18413
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
18414
+ if (zone.left === zone.right) {
18415
+ return zone.left + 1;
18416
+ }
18417
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
18418
+ value: zone.left + col + 1,
18419
+ }));
18414
18420
  },
18415
18421
  isExported: true,
18416
18422
  };
@@ -18629,11 +18635,17 @@ const ROW = {
18629
18635
  if (isEvaluationError(cellReference?.value)) {
18630
18636
  return cellReference;
18631
18637
  }
18632
- const row = cellReference === undefined
18633
- ? this.__originCellPosition?.row
18634
- : toZone(cellReference.value).top;
18635
- assert(() => row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18636
- return row + 1;
18638
+ if (cellReference === undefined) {
18639
+ assert(() => this.__originCellPosition?.row !== undefined, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18640
+ return this.__originCellPosition.row + 1;
18641
+ }
18642
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
18643
+ if (zone.top === zone.bottom) {
18644
+ return zone.top + 1;
18645
+ }
18646
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
18647
+ value: zone.top + row + 1,
18648
+ }));
18637
18649
  },
18638
18650
  isExported: true,
18639
18651
  };
@@ -22613,6 +22625,7 @@ const autoCompleteProviders = new Registry();
22613
22625
 
22614
22626
  autoCompleteProviders.add("dataValidation", {
22615
22627
  displayAllOnInitialContent: true,
22628
+ canBeToggled: false,
22616
22629
  getProposals(tokenAtCursor, content) {
22617
22630
  if (isFormula(content)) {
22618
22631
  return [];
@@ -22620,31 +22633,40 @@ autoCompleteProviders.add("dataValidation", {
22620
22633
  if (!this.composer.currentEditedCell) {
22621
22634
  return [];
22622
22635
  }
22623
- const position = this.composer.currentEditedCell;
22624
- const rule = this.getters.getValidationRuleForCell(position);
22625
- if (!rule ||
22626
- (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
22627
- return [];
22628
- }
22629
- let values;
22630
- if (rule.criterion.type === "isValueInList") {
22631
- values = rule.criterion.values;
22632
- }
22633
- else {
22634
- const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
22635
- values = Array.from(new Set(this.getters
22636
- .getRangeValues(range)
22637
- .filter(isNotNull)
22638
- .map((value) => value.toString())
22639
- .filter((val) => val !== "")));
22640
- }
22641
- return values.map((value) => ({ text: value }));
22636
+ return getProposedValues(this.getters, this.composer.currentEditedCell).map((value) => ({
22637
+ text: value.value?.toString() || "",
22638
+ htmlContent: [{ value: value.label }],
22639
+ fuzzySearchKey: value.label,
22640
+ }));
22642
22641
  },
22643
22642
  selectProposal(tokenAtCursor, value) {
22644
22643
  this.composer.setCurrentContent(value);
22645
22644
  this.composer.stopEdition();
22646
22645
  },
22647
22646
  });
22647
+ function getProposedValues(getters, position) {
22648
+ const rule = getters.getValidationRuleForCell(position);
22649
+ if (!rule ||
22650
+ (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
22651
+ return [];
22652
+ }
22653
+ let values = [];
22654
+ if (rule.criterion.type === "isValueInList") {
22655
+ values = rule.criterion.values.map((value) => ({ label: value, value }));
22656
+ }
22657
+ else {
22658
+ const labelsSet = new Set();
22659
+ const range = getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
22660
+ for (const p of positions(range.zone)) {
22661
+ const cell = getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
22662
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
22663
+ labelsSet.add(cell.formattedValue);
22664
+ values.push({ label: cell.formattedValue, value: cell.value });
22665
+ }
22666
+ }
22667
+ }
22668
+ return values;
22669
+ }
22648
22670
 
22649
22671
  function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
22650
22672
  const pendingHtmlContent = [];
@@ -23509,6 +23531,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23509
23531
  proposals,
23510
23532
  selectProposal: provider.selectProposal,
23511
23533
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
23534
+ canBeToggled: provider.canBeToggled,
23512
23535
  };
23513
23536
  }
23514
23537
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -23531,6 +23554,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23531
23554
  proposals,
23532
23555
  selectProposal: provider.selectProposal,
23533
23556
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
23557
+ canBeToggled: provider.canBeToggled,
23534
23558
  };
23535
23559
  }
23536
23560
  }
@@ -43775,9 +43799,13 @@ class Composer extends Component {
43775
43799
  }
43776
43800
  }
43777
43801
  closeAssistant() {
43802
+ if (!this.canBeToggled)
43803
+ return;
43778
43804
  this.assistant.forcedClosed = true;
43779
43805
  }
43780
43806
  openAssistant() {
43807
+ if (!this.canBeToggled)
43808
+ return;
43781
43809
  this.assistant.forcedClosed = false;
43782
43810
  }
43783
43811
  onWheel(event) {
@@ -43787,6 +43815,9 @@ class Composer extends Component {
43787
43815
  event.stopPropagation();
43788
43816
  }
43789
43817
  }
43818
+ get canBeToggled() {
43819
+ return this.autoCompleteState.provider?.canBeToggled ?? true;
43820
+ }
43790
43821
  // ---------------------------------------------------------------------------
43791
43822
  // Private
43792
43823
  // ---------------------------------------------------------------------------
@@ -43974,7 +44005,7 @@ class Composer extends Component {
43974
44005
  return [...new Set(argsToFocus)];
43975
44006
  }
43976
44007
  autoComplete(value) {
43977
- if (!value || this.assistant.forcedClosed) {
44008
+ if (!value || (this.assistant.forcedClosed && this.canBeToggled)) {
43978
44009
  return;
43979
44010
  }
43980
44011
  this.autoCompleteState.provider?.selectProposal(value);
@@ -45502,7 +45533,8 @@ class ConditionalFormattingEditor extends Component {
45502
45533
  static props = {
45503
45534
  editedCf: Object,
45504
45535
  onCancel: Function,
45505
- onSave: Function,
45536
+ onExit: Function,
45537
+ isNewCf: Boolean,
45506
45538
  };
45507
45539
  static components = {
45508
45540
  SelectionInput,
@@ -45521,6 +45553,7 @@ class ConditionalFormattingEditor extends Component {
45521
45553
  getTextDecoration = getTextDecoration;
45522
45554
  colorNumberString = colorNumberString;
45523
45555
  state;
45556
+ hasEditedCf = this.props.isNewCf;
45524
45557
  setup() {
45525
45558
  this.state = useState({
45526
45559
  errors: [],
@@ -45578,6 +45611,9 @@ class ConditionalFormattingEditor extends Component {
45578
45611
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
45579
45612
  sheetId,
45580
45613
  });
45614
+ if (result.isSuccessful) {
45615
+ this.hasEditedCf = true;
45616
+ }
45581
45617
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
45582
45618
  if (!newCf.suppressErrors) {
45583
45619
  this.state.errors = reasons;
@@ -45599,7 +45635,15 @@ class ConditionalFormattingEditor extends Component {
45599
45635
  onSave() {
45600
45636
  const result = this.updateConditionalFormat({});
45601
45637
  if (result.length === 0) {
45602
- this.props.onSave();
45638
+ this.props.onExit();
45639
+ }
45640
+ }
45641
+ onCancel() {
45642
+ if (this.hasEditedCf) {
45643
+ this.props.onCancel();
45644
+ }
45645
+ else {
45646
+ this.props.onExit();
45603
45647
  }
45604
45648
  }
45605
45649
  getDefaultRules() {
@@ -49953,9 +49997,15 @@ class SpreadsheetPivot {
49953
49997
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
49954
49998
  }
49955
49999
  filterDataEntriesFromDomainNode(dataEntries, domain) {
49956
- const { field, value } = domain;
50000
+ const { field, value, type } = domain;
49957
50001
  const { nameWithGranularity } = this.getDimension(field);
49958
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
50002
+ return dataEntries.filter((entry) => {
50003
+ const cellValue = entry[nameWithGranularity]?.value;
50004
+ if (type === "char") {
50005
+ return String(cellValue) === String(value);
50006
+ }
50007
+ return cellValue === value;
50008
+ });
49959
50009
  }
49960
50010
  getDimension(nameWithGranularity) {
49961
50011
  return this.definition.getDimension(nameWithGranularity);
@@ -52621,6 +52671,11 @@ class GridComposer extends Component {
52621
52671
  rect = this.defaultRect;
52622
52672
  isEditing = false;
52623
52673
  isCellReferenceVisible = false;
52674
+ currentEditedCell = {
52675
+ col: 0,
52676
+ row: 0,
52677
+ sheetId: this.env.model.getters.getActiveSheetId(),
52678
+ };
52624
52679
  composerStore;
52625
52680
  composerFocusStore;
52626
52681
  composerInterface;
@@ -52650,7 +52705,7 @@ class GridComposer extends Component {
52650
52705
  return this.isCellReferenceVisible;
52651
52706
  }
52652
52707
  get cellReference() {
52653
- const { col, row, sheetId } = this.composerStore.currentEditedCell;
52708
+ const { col, row, sheetId } = this.currentEditedCell;
52654
52709
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
52655
52710
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
52656
52711
  }
@@ -52742,12 +52797,17 @@ class GridComposer extends Component {
52742
52797
  if (!isEditing && this.composerFocusStore.activeComposer !== this.composerInterface) {
52743
52798
  this.composerFocusStore.focusComposer(this.composerInterface, { focusMode: "inactive" });
52744
52799
  }
52800
+ let shouldRecomputeRect = isEditing && !deepEquals(this.currentEditedCell, this.composerStore.currentEditedCell);
52745
52801
  if (this.isEditing !== isEditing) {
52746
52802
  this.isEditing = isEditing;
52747
52803
  if (!isEditing) {
52748
52804
  this.rect = this.defaultRect;
52749
52805
  return;
52750
52806
  }
52807
+ this.currentEditedCell = this.composerStore.currentEditedCell;
52808
+ shouldRecomputeRect = true;
52809
+ }
52810
+ if (shouldRecomputeRect) {
52751
52811
  const position = this.env.model.getters.getActivePosition();
52752
52812
  const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
52753
52813
  this.rect = this.env.model.getters.getVisibleRect(zone);
@@ -65874,6 +65934,23 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
65874
65934
  static getters = ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
65875
65935
  tallestCellInRow = {};
65876
65936
  ctx = document.createElement("canvas").getContext("2d");
65937
+ beforeHandle(cmd) {
65938
+ switch (cmd.type) {
65939
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
65940
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
65941
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
65942
+ // size updates may apply to incorrect (pre-insert) rows.
65943
+ case "ADD_COLUMNS_ROWS":
65944
+ if (cmd.dimension === "COL") {
65945
+ return;
65946
+ }
65947
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
65948
+ const newCells = Array(cmd.quantity).fill(undefined);
65949
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
65950
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
65951
+ break;
65952
+ }
65953
+ }
65877
65954
  handle(cmd) {
65878
65955
  switch (cmd.type) {
65879
65956
  case "START":
@@ -65903,16 +65980,6 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
65903
65980
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
65904
65981
  break;
65905
65982
  }
65906
- case "ADD_COLUMNS_ROWS": {
65907
- if (cmd.dimension === "COL") {
65908
- return;
65909
- }
65910
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
65911
- const newCells = Array(cmd.quantity).fill(undefined);
65912
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
65913
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
65914
- break;
65915
- }
65916
65983
  case "RESIZE_COLUMNS_ROWS":
65917
65984
  {
65918
65985
  const sheetId = cmd.sheetId;
@@ -71913,6 +71980,14 @@ class GridSelectionPlugin extends UIPlugin {
71913
71980
  const isBasedBefore = cmd.base < start;
71914
71981
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
71915
71982
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
71983
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
71984
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
71985
+ const size = isCol
71986
+ ? this.getters.getColSize(cmd.sheetId, element)
71987
+ : this.getters.getUserRowSize(cmd.sheetId, element);
71988
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
71989
+ return [element, isDefaultCol ? undefined : size];
71990
+ }));
71916
71991
  const target = [
71917
71992
  {
71918
71993
  left: isCol ? start + deltaCol : 0,
@@ -71943,13 +72018,12 @@ class GridSelectionPlugin extends UIPlugin {
71943
72018
  const col = selection.left;
71944
72019
  const row = selection.top;
71945
72020
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
71946
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
71947
72021
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
71948
72022
  const resizingGroups = {};
71949
72023
  for (const element of toRemove) {
71950
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
72024
+ const size = originalSize[element];
71951
72025
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
71952
- if (size != currentSize) {
72026
+ if (size && size != currentSize) {
71953
72027
  resizingGroups[size] ??= [];
71954
72028
  resizingGroups[size].push(currentIndex);
71955
72029
  currentIndex += 1;
@@ -73743,14 +73817,12 @@ class BottomBarSheet extends Component {
73743
73817
  this.editionState = "initializing";
73744
73818
  }
73745
73819
  stopEdition() {
73746
- const input = this.sheetNameRef.el;
73747
- if (!this.state.isEditing || !input)
73820
+ if (!this.state.isEditing || !this.sheetNameRef.el)
73748
73821
  return;
73749
73822
  this.state.isEditing = false;
73750
73823
  this.editionState = "initializing";
73751
- input.blur();
73824
+ this.sheetNameRef.el.blur();
73752
73825
  const inputValue = this.getInputContent() || "";
73753
- input.innerText = inputValue;
73754
73826
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
73755
73827
  }
73756
73828
  cancelEdition() {
@@ -80802,6 +80874,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
80802
80874
  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, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
80803
80875
 
80804
80876
 
80805
- __info__.version = "18.3.12";
80806
- __info__.date = "2025-07-11T11:11:58.998Z";
80807
- __info__.hash = "d14dc96";
80877
+ __info__.version = "18.3.14";
80878
+ __info__.date = "2025-07-30T11:18:53.973Z";
80879
+ __info__.hash = "2a7178a";