@odoo/o-spreadsheet 17.0.10 → 17.0.12

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 17.0.10
6
- * @date 2024-02-02T13:48:28.727Z
7
- * @hash be06aa9
5
+ * @version 17.0.12
6
+ * @date 2024-02-16T14:24:02.984Z
7
+ * @hash 8fa2bab
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -551,8 +551,8 @@
551
551
  * If the given item does not exist in the dictionary, it creates one with a new id.
552
552
  */
553
553
  function getItemId(item, itemsDic) {
554
- for (let [key, value] of Object.entries(itemsDic)) {
555
- if (deepEquals(value, item)) {
554
+ for (const key in itemsDic) {
555
+ if (deepEquals(itemsDic[key], item)) {
556
556
  return parseInt(key, 10);
557
557
  }
558
558
  }
@@ -651,11 +651,13 @@
651
651
  if (typeof o1 !== "object")
652
652
  return o1 === o2;
653
653
  // Objects can have different keys if the values are undefined
654
- const keys = new Set();
655
- Object.keys(o1).forEach((key) => keys.add(key));
656
- Object.keys(o2).forEach((key) => keys.add(key));
657
- for (let key of keys) {
658
- if (typeof o1[key] !== typeof o1[key])
654
+ for (const key in o2) {
655
+ if (!(key in o1) && o2[key] !== undefined) {
656
+ return false;
657
+ }
658
+ }
659
+ for (const key in o1) {
660
+ if (typeof o1[key] !== typeof o2[key])
659
661
  return false;
660
662
  if (typeof o1[key] === "object") {
661
663
  if (!deepEquals(o1[key], o2[key]))
@@ -725,18 +727,18 @@
725
727
  return true;
726
728
  }
727
729
  class JetSet extends Set {
728
- add(...iterable) {
730
+ addMany(iterable) {
729
731
  for (const element of iterable) {
730
732
  super.add(element);
731
733
  }
732
734
  return this;
733
735
  }
734
- delete(...iterable) {
735
- let deleted = false;
736
+ deleteMany(iterable) {
737
+ let wasDeleted = false;
736
738
  for (const element of iterable) {
737
- deleted ||= super.delete(element);
739
+ wasDeleted ||= super.delete(element);
738
740
  }
739
- return deleted;
741
+ return wasDeleted;
740
742
  }
741
743
  }
742
744
  /**
@@ -1995,7 +1997,7 @@
1995
1997
  * Static helper which returns a successful DispatchResult
1996
1998
  */
1997
1999
  static get Success() {
1998
- return new DispatchResult();
2000
+ return SUCCESS;
1999
2001
  }
2000
2002
  get isSuccessful() {
2001
2003
  return this.reasons.length === 0;
@@ -2008,6 +2010,7 @@
2008
2010
  return this.reasons.includes(reason);
2009
2011
  }
2010
2012
  }
2013
+ const SUCCESS = new DispatchResult();
2011
2014
  exports.CommandResult = void 0;
2012
2015
  (function (CommandResult) {
2013
2016
  CommandResult["Success"] = "Success";
@@ -2889,6 +2892,9 @@
2889
2892
  function formatValue(value, { format, locale }) {
2890
2893
  switch (typeof value) {
2891
2894
  case "string":
2895
+ if (value.includes('\\"')) {
2896
+ return value.replace(/\\"/g, '"');
2897
+ }
2892
2898
  return value;
2893
2899
  case "boolean":
2894
2900
  return value ? "TRUE" : "FALSE";
@@ -7380,7 +7386,7 @@
7380
7386
  while (!chars.isOver()) {
7381
7387
  let token = tokenizeSpace(chars) ||
7382
7388
  tokenizeArgsSeparator(chars, locale) ||
7383
- tokenizeMisc(chars) ||
7389
+ tokenizeParenthesis(chars) ||
7384
7390
  tokenizeOperator(chars) ||
7385
7391
  tokenizeString(chars) ||
7386
7392
  tokenizeDebugger(chars) ||
@@ -7401,15 +7407,14 @@
7401
7407
  }
7402
7408
  return null;
7403
7409
  }
7404
- const misc$1 = {
7405
- "(": "LEFT_PAREN",
7406
- ")": "RIGHT_PAREN",
7410
+ const parenthesis = {
7411
+ "(": { type: "LEFT_PAREN", value: "(" },
7412
+ ")": { type: "RIGHT_PAREN", value: ")" },
7407
7413
  };
7408
- function tokenizeMisc(chars) {
7409
- if (chars.current in misc$1) {
7414
+ function tokenizeParenthesis(chars) {
7415
+ if (chars.current === "(" || chars.current === ")") {
7410
7416
  const value = chars.shift();
7411
- const type = misc$1[value];
7412
- return { type, value };
7417
+ return parenthesis[value];
7413
7418
  }
7414
7419
  return null;
7415
7420
  }
@@ -7430,7 +7435,12 @@
7430
7435
  }
7431
7436
  return null;
7432
7437
  }
7438
+ const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
7433
7439
  function tokenizeNumber(chars, locale) {
7440
+ if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
7441
+ chars.current !== locale.decimalSeparator) {
7442
+ return null;
7443
+ }
7434
7444
  const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
7435
7445
  if (match) {
7436
7446
  chars.advanceBy(match[0].length);
@@ -7455,7 +7465,7 @@
7455
7465
  }
7456
7466
  return null;
7457
7467
  }
7458
- const separatorRegexp = /\w|\.|!|\$/;
7468
+ const separatorRegexp = /^[\w\.!\$]+/;
7459
7469
  /**
7460
7470
  * A "Symbol" is just basically any word-like element that can appear in a
7461
7471
  * formula, which is not a string. So:
@@ -7495,8 +7505,11 @@
7495
7505
  };
7496
7506
  }
7497
7507
  }
7498
- while (chars.current && separatorRegexp.test(chars.current)) {
7499
- result += chars.shift();
7508
+ const match = chars.remaining().match(separatorRegexp);
7509
+ if (match) {
7510
+ const value = match[0];
7511
+ result += value;
7512
+ chars.advanceBy(value.length);
7500
7513
  }
7501
7514
  if (result.length) {
7502
7515
  const value = result;
@@ -7558,7 +7571,10 @@
7558
7571
  return this.text.substring(this.currentIndex);
7559
7572
  }
7560
7573
  currentStartsWith(str) {
7561
- for (let j = 0; j < str.length; j++) {
7574
+ if (this.current !== str[0]) {
7575
+ return false;
7576
+ }
7577
+ for (let j = 1; j < str.length; j++) {
7562
7578
  if (this.text[this.currentIndex + j] !== str[j]) {
7563
7579
  return false;
7564
7580
  }
@@ -7585,6 +7601,9 @@
7585
7601
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7586
7602
  return false;
7587
7603
  }
7604
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
7605
+ return false;
7606
+ }
7588
7607
  try {
7589
7608
  formatValue(1, { locale, format: "#,##0.00" });
7590
7609
  formatValue(1, { locale, format: locale.dateFormat });
@@ -7675,7 +7694,7 @@
7675
7694
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7676
7695
  return content;
7677
7696
  }
7678
- return content.replace(locale.decimalSeparator, ".");
7697
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7679
7698
  }
7680
7699
  /**
7681
7700
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -10747,6 +10766,12 @@
10747
10766
  let { col, row } = env.model.getters.getActivePosition();
10748
10767
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
10749
10768
  };
10769
+ const INSERT_LINK_NAME = (env) => {
10770
+ const sheetId = env.model.getters.getActiveSheetId();
10771
+ const { col, row } = env.model.getters.getActivePosition();
10772
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
10773
+ return cell && cell.link ? _t("Edit link") : _t("Insert link");
10774
+ };
10750
10775
  //------------------------------------------------------------------------------
10751
10776
  // Filters action
10752
10777
  //------------------------------------------------------------------------------
@@ -19778,7 +19803,7 @@
19778
19803
  })
19779
19804
  .add("insert_link", {
19780
19805
  ...insertLink,
19781
- name: _t("Insert link"),
19806
+ name: INSERT_LINK_NAME,
19782
19807
  sequence: 150,
19783
19808
  separator: true,
19784
19809
  });
@@ -26031,6 +26056,17 @@
26031
26056
  `;
26032
26057
  class TextValueProvider extends owl.Component {
26033
26058
  static template = "o-spreadsheet-TextValueProvider";
26059
+ autoCompleteListRef = owl.useRef("autoCompleteList");
26060
+ setup() {
26061
+ owl.useEffect(() => {
26062
+ const selectedIndex = this.props.selectedIndex;
26063
+ if (selectedIndex === undefined) {
26064
+ return;
26065
+ }
26066
+ const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
26067
+ selectedElement?.scrollIntoView?.({ block: "nearest" });
26068
+ }, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
26069
+ }
26034
26070
  }
26035
26071
  TextValueProvider.props = {
26036
26072
  values: Array,
@@ -26482,6 +26518,7 @@
26482
26518
  position: absolute;
26483
26519
  margin: 1px 4px;
26484
26520
  pointer-events: none;
26521
+ overflow: auto;
26485
26522
 
26486
26523
  .o-semi-bold {
26487
26524
  /** FIXME: to remove in favor of Bootstrap
@@ -26497,6 +26534,7 @@
26497
26534
  static components = { TextValueProvider, FunctionDescriptionProvider };
26498
26535
  static defaultProps = {
26499
26536
  inputStyle: "",
26537
+ isDefaultFocus: false,
26500
26538
  };
26501
26539
  composerRef = owl.useRef("o_composer");
26502
26540
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -26527,7 +26565,10 @@
26527
26565
  if (this.props.delimitation && this.props.rect) {
26528
26566
  const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
26529
26567
  const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
26568
+ assistantStyle["max-height"] = `${remainingHeight}px`;
26530
26569
  if (cellY > remainingHeight) {
26570
+ const availableSpaceAbove = cellY;
26571
+ assistantStyle["max-height"] = `${availableSpaceAbove}px`;
26531
26572
  // render top
26532
26573
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
26533
26574
  assistantStyle.top = `-3px`;
@@ -26553,7 +26594,6 @@
26553
26594
  F2: () => console.warn("Not implemented"),
26554
26595
  F4: this.processF4Key,
26555
26596
  Tab: (ev) => this.processTabKey(ev),
26556
- " ": (ev) => this.processSpaceKey(ev),
26557
26597
  };
26558
26598
  keyCodeMapping = {
26559
26599
  NumpadDecimal: this.processNumpadDecimal,
@@ -26561,12 +26601,12 @@
26561
26601
  setup() {
26562
26602
  owl.onMounted(() => {
26563
26603
  const el = this.composerRef.el;
26604
+ if (this.props.isDefaultFocus) {
26605
+ this.env.focusableElement.setFocusableElement(el);
26606
+ }
26564
26607
  this.contentHelper.updateEl(el);
26565
26608
  this.processTokenAtCursor();
26566
26609
  });
26567
- owl.onWillUnmount(() => {
26568
- this.props.onComposerUnmounted?.();
26569
- });
26570
26610
  owl.useEffect(() => {
26571
26611
  this.processContent();
26572
26612
  });
@@ -26575,7 +26615,8 @@
26575
26615
  // Handlers
26576
26616
  // ---------------------------------------------------------------------------
26577
26617
  processArrowKeys(ev) {
26578
- if (this.env.model.getters.isSelectingForComposer()) {
26618
+ if (this.env.model.getters.isSelectingForComposer() ||
26619
+ this.env.model.getters.getEditionMode() === "inactive") {
26579
26620
  this.functionDescriptionState.showDescription = false;
26580
26621
  // Prevent the default content editable behavior which moves the cursor
26581
26622
  ev.preventDefault();
@@ -26618,26 +26659,20 @@
26618
26659
  processTabKey(ev) {
26619
26660
  ev.preventDefault();
26620
26661
  ev.stopPropagation();
26621
- const state = this.autoCompleteState;
26622
- if (state.showProvider && state.selectedIndex !== undefined) {
26623
- const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
26624
- if (autoCompleteValue) {
26625
- this.autoComplete(autoCompleteValue);
26626
- return;
26662
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
26663
+ const state = this.autoCompleteState;
26664
+ if (state.showProvider && state.selectedIndex !== undefined) {
26665
+ const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
26666
+ if (autoCompleteValue) {
26667
+ this.autoComplete(autoCompleteValue);
26668
+ return;
26669
+ }
26627
26670
  }
26671
+ interactiveStopEdition(this.env);
26628
26672
  }
26629
26673
  const direction = ev.shiftKey ? "left" : "right";
26630
- interactiveStopEdition(this.env);
26631
26674
  this.env.model.selection.moveAnchorCell(direction, 1);
26632
26675
  }
26633
- processSpaceKey(ev) {
26634
- if (ev.ctrlKey) {
26635
- ev.preventDefault();
26636
- ev.stopPropagation();
26637
- this.showFunctionAutocomplete("");
26638
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26639
- }
26640
- }
26641
26676
  processEnterKey(ev) {
26642
26677
  ev.preventDefault();
26643
26678
  ev.stopPropagation();
@@ -26690,6 +26725,9 @@
26690
26725
  this.compositionActive = false;
26691
26726
  }
26692
26727
  onKeydown(ev) {
26728
+ if (this.env.model.getters.getEditionMode() === "inactive") {
26729
+ return;
26730
+ }
26693
26731
  let handler = this.keyMapping[ev.key] || this.keyCodeMapping[ev.code];
26694
26732
  if (handler) {
26695
26733
  handler.call(this, ev);
@@ -26698,14 +26736,33 @@
26698
26736
  ev.stopPropagation();
26699
26737
  }
26700
26738
  }
26739
+ onPaste(ev) {
26740
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
26741
+ ev.stopPropagation();
26742
+ }
26743
+ }
26701
26744
  /*
26702
26745
  * Triggered automatically by the content-editable between the keydown and key up
26703
26746
  * */
26704
26747
  onInput(ev) {
26705
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
26748
+ if (!this.shouldProcessInputEvents) {
26706
26749
  return;
26707
26750
  }
26708
- let content = this.contentHelper.getText();
26751
+ if (ev.inputType === "insertFromPaste" &&
26752
+ this.env.model.getters.getEditionMode() === "inactive") {
26753
+ return;
26754
+ }
26755
+ ev.stopPropagation();
26756
+ let content;
26757
+ if (this.env.model.getters.getEditionMode() === "inactive") {
26758
+ content = ev.data || "";
26759
+ }
26760
+ else {
26761
+ content = this.contentHelper.getText();
26762
+ }
26763
+ if (this.props.focus === "inactive") {
26764
+ return this.props.onComposerCellFocused?.(content);
26765
+ }
26709
26766
  let selection = this.contentHelper.getCurrentSelection();
26710
26767
  if (ev.inputType === "insertParagraph") {
26711
26768
  const start = Math.min(selection.start, selection.end);
@@ -26787,9 +26844,8 @@
26787
26844
  }
26788
26845
  const newSelection = this.contentHelper.getCurrentSelection();
26789
26846
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26790
- if (this.props.focus === "inactive") {
26791
- this.props.onComposerContentFocused(newSelection);
26792
- }
26847
+ this.props.onComposerContentFocused();
26848
+ if (this.props.focus === "inactive") ;
26793
26849
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
26794
26850
  this.processTokenAtCursor();
26795
26851
  }
@@ -26956,7 +27012,7 @@
26956
27012
  const highlights = this.env.model.getters.getHighlights();
26957
27013
  const refSheet = sheetName
26958
27014
  ? this.env.model.getters.getSheetIdByName(sheetName)
26959
- : this.env.model.getters.getCurrentEditedCell().sheetId;
27015
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
26960
27016
  const highlight = highlights.find((highlight) => {
26961
27017
  if (highlight.sheetId !== refSheet)
26962
27018
  return false;
@@ -27049,12 +27105,13 @@
27049
27105
  }
27050
27106
  }
27051
27107
  Composer.props = {
27052
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27053
- onComposerContentFocused: Function,
27054
27108
  inputStyle: { type: String, optional: true },
27055
27109
  rect: { type: Object, optional: true },
27056
27110
  delimitation: { type: Object, optional: true },
27057
- onComposerUnmounted: { type: Function, optional: true },
27111
+ focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27112
+ onComposerCellFocused: { type: Function, optional: true },
27113
+ onComposerContentFocused: Function,
27114
+ isDefaultFocus: { type: Boolean, optional: true },
27058
27115
  };
27059
27116
 
27060
27117
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
@@ -27088,51 +27145,25 @@
27088
27145
  class GridComposer extends owl.Component {
27089
27146
  static template = "o-spreadsheet-GridComposer";
27090
27147
  static components = { Composer };
27091
- gridComposerRef;
27092
- zone;
27093
- rect;
27148
+ rect = this.defaultRect;
27149
+ isEditing = false;
27094
27150
  isCellReferenceVisible;
27095
- composerState;
27151
+ get defaultRect() {
27152
+ return { x: 0, y: 0, width: 0, height: 0 };
27153
+ }
27096
27154
  setup() {
27097
- this.gridComposerRef = owl.useRef("gridComposer");
27098
- this.composerState = owl.useState({
27099
- rect: undefined,
27100
- delimitation: undefined,
27101
- });
27102
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
27103
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
27104
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
27105
- this.isCellReferenceVisible = false;
27106
- owl.onMounted(() => {
27107
- const el = this.gridComposerRef.el;
27108
- this.composerState.rect = {
27109
- x: this.rect.x,
27110
- y: this.rect.y,
27111
- width: el.clientWidth,
27112
- height: el.clientHeight,
27113
- };
27114
- this.composerState.delimitation = {
27115
- width: this.props.gridDims.width,
27116
- height: this.props.gridDims.height,
27117
- };
27118
- });
27119
27155
  owl.onWillUpdateProps(() => {
27120
- if (this.isCellReferenceVisible) {
27121
- return;
27122
- }
27123
- const sheetId = this.env.model.getters.getActiveSheetId();
27124
- const zone = this.env.model.getters.getSelectedZone();
27125
- const rect = this.env.model.getters.getVisibleRect(zone);
27126
- if (!deepEquals(rect, this.rect) ||
27127
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27128
- this.isCellReferenceVisible = true;
27129
- }
27156
+ this.updateComponentPosition();
27157
+ this.updateCellReferenceVisibility();
27130
27158
  });
27131
27159
  }
27132
27160
  get shouldDisplayCellReference() {
27133
27161
  return this.isCellReferenceVisible;
27134
27162
  }
27135
27163
  get cellReference() {
27164
+ if (!this.env.model.getters.getCurrentEditedCell()) {
27165
+ return "";
27166
+ }
27136
27167
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27137
27168
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27138
27169
  return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
@@ -27144,7 +27175,27 @@
27144
27175
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
27145
27176
  });
27146
27177
  }
27178
+ get composerProps() {
27179
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27180
+ return {
27181
+ rect: { ...this.rect },
27182
+ delimitation: {
27183
+ width,
27184
+ height,
27185
+ },
27186
+ focus: this.props.focus,
27187
+ isDefaultFocus: true,
27188
+ onComposerContentFocused: this.props.onComposerContentFocused,
27189
+ onComposerCellFocused: this.props.onComposerCellFocused,
27190
+ };
27191
+ }
27147
27192
  get containerStyle() {
27193
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27194
+ return `
27195
+ position: absolute;
27196
+ z-index: -1000;
27197
+ `;
27198
+ }
27148
27199
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27149
27200
  const cell = this.env.model.getters.getActiveCell();
27150
27201
  const position = this.env.model.getters.getActivePosition();
@@ -27164,6 +27215,8 @@
27164
27215
  if (!isFormula) {
27165
27216
  textAlign = style.align || cell.defaultAlign;
27166
27217
  }
27218
+ const maxHeight = this.props.gridDims.height - this.rect.y;
27219
+ const maxWidth = this.props.gridDims.width - this.rect.x;
27167
27220
  /**
27168
27221
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
27169
27222
  * including all the paddings/margins of the composer
@@ -27175,6 +27228,8 @@
27175
27228
  top: `${top}px`,
27176
27229
  "min-width": `${width + 1}px`,
27177
27230
  "min-height": `${height + 1}px`,
27231
+ "max-width": `${maxWidth}px`,
27232
+ "max-height": `${maxHeight}px`,
27178
27233
  background,
27179
27234
  color,
27180
27235
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -27184,20 +27239,38 @@
27184
27239
  "text-align": textAlign,
27185
27240
  });
27186
27241
  }
27187
- get composerStyle() {
27188
- const maxHeight = this.props.gridDims.height - this.rect.y;
27189
- const maxWidth = this.props.gridDims.width - this.rect.x;
27190
- return cssPropertiesToCss({
27191
- "max-width": `${maxWidth}px`,
27192
- "max-height": `${maxHeight}px`,
27193
- });
27242
+ updateComponentPosition() {
27243
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
27244
+ if (this.isEditing !== isEditing) {
27245
+ this.isEditing = isEditing;
27246
+ if (!isEditing) {
27247
+ this.rect = this.defaultRect;
27248
+ this.env.focusableElement.focus();
27249
+ return;
27250
+ }
27251
+ const position = this.env.model.getters.getActivePosition();
27252
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
27253
+ this.rect = this.env.model.getters.getVisibleRect(zone);
27254
+ }
27255
+ }
27256
+ updateCellReferenceVisibility() {
27257
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
27258
+ return;
27259
+ }
27260
+ const sheetId = this.env.model.getters.getActiveSheetId();
27261
+ const zone = this.env.model.getters.getSelectedZone();
27262
+ const rect = this.env.model.getters.getVisibleRect(zone);
27263
+ if (!deepEquals(rect, this.rect) ||
27264
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27265
+ this.isCellReferenceVisible = true;
27266
+ }
27194
27267
  }
27195
27268
  }
27196
27269
  GridComposer.props = {
27197
27270
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27198
- onComposerUnmounted: Function,
27199
27271
  onComposerContentFocused: Function,
27200
27272
  gridDims: Object,
27273
+ onComposerCellFocused: Function,
27201
27274
  };
27202
27275
 
27203
27276
  const CSS$1 = css /* scss */ `
@@ -28288,7 +28361,6 @@
28288
28361
  this.props.onCellDoubleClicked(col, row);
28289
28362
  }
28290
28363
  onContextMenu(ev) {
28291
- ev.preventDefault();
28292
28364
  const [col, row] = this.getCartesianCoordinates(ev);
28293
28365
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
28294
28366
  }
@@ -29373,7 +29445,6 @@
29373
29445
  HEADER_WIDTH = HEADER_WIDTH;
29374
29446
  menuState;
29375
29447
  gridRef;
29376
- hiddenInput;
29377
29448
  onMouseWheel;
29378
29449
  canvasPosition;
29379
29450
  hoveredCell;
@@ -29384,17 +29455,16 @@
29384
29455
  menuItems: [],
29385
29456
  });
29386
29457
  this.gridRef = owl.useRef("grid");
29387
- this.hiddenInput = owl.useRef("hiddenInput");
29388
29458
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
29389
29459
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
29390
29460
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
29391
29461
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
29392
29462
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
29393
29463
  owl.useExternalListener(document.body, "paste", this.paste);
29394
- owl.onMounted(() => this.focus());
29395
- this.props.exposeFocus(() => this.focus());
29464
+ owl.onMounted(() => this.focusDefaultElement());
29465
+ this.props.exposeFocus(() => this.focusDefaultElement());
29396
29466
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29397
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
29467
+ owl.useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
29398
29468
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29399
29469
  this.moveCanvas(deltaX, deltaY);
29400
29470
  this.hoveredCell.col = undefined;
@@ -29417,7 +29487,7 @@
29417
29487
  if (this.env.model.getters.hasOpenedPopover()) {
29418
29488
  this.closeOpenedPopover();
29419
29489
  }
29420
- this.focus();
29490
+ this.focusDefaultElement();
29421
29491
  }
29422
29492
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
29423
29493
  // down itself
@@ -29596,10 +29666,10 @@
29596
29666
  "ALT+SHIFT+ARROWUP": () => this.processHeaderGroupingKey("up"),
29597
29667
  "ALT+SHIFT+ARROWDOWN": () => this.processHeaderGroupingKey("down"),
29598
29668
  };
29599
- focus() {
29669
+ focusDefaultElement() {
29600
29670
  if (!this.env.model.getters.getSelectedFigureId() &&
29601
29671
  this.env.model.getters.getEditionMode() === "inactive") {
29602
- this.hiddenInput.el?.focus();
29672
+ this.env.focusableElement.focus();
29603
29673
  }
29604
29674
  }
29605
29675
  get gridEl() {
@@ -29744,19 +29814,6 @@
29744
29814
  return;
29745
29815
  }
29746
29816
  }
29747
- onInput(ev) {
29748
- // the user meant to paste in the sheet, not open the composer with the pasted content
29749
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
29750
- return;
29751
- }
29752
- if (ev.data) {
29753
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
29754
- // character
29755
- ev.preventDefault();
29756
- ev.stopPropagation();
29757
- this.props.onGridComposerCellFocused(ev.data);
29758
- }
29759
- }
29760
29817
  // ---------------------------------------------------------------------------
29761
29818
  // Context Menu
29762
29819
  // ---------------------------------------------------------------------------
@@ -29871,7 +29928,7 @@
29871
29928
  }
29872
29929
  closeMenu() {
29873
29930
  this.menuState.isOpen = false;
29874
- this.focus();
29931
+ this.focusDefaultElement();
29875
29932
  }
29876
29933
  processHeaderGroupingKey(direction) {
29877
29934
  if (this.env.model.getters.getSelectedZones().length !== 1) {
@@ -33766,6 +33823,31 @@
33766
33823
  return data;
33767
33824
  },
33768
33825
  },
33826
+ {
33827
+ description: "Fix datafilter duplication",
33828
+ from: 12,
33829
+ to: 12.5,
33830
+ applyMigration(data) {
33831
+ for (let sheet of data.sheets || []) {
33832
+ let knownDataFilterZones = [];
33833
+ for (let filterTable of sheet.filterTables || []) {
33834
+ const zone = toZone(filterTable.range);
33835
+ // See commit message for the details
33836
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
33837
+ if (intersectZoneIndex !== -1) {
33838
+ knownDataFilterZones[intersectZoneIndex] = zone;
33839
+ }
33840
+ else {
33841
+ knownDataFilterZones.push(zone);
33842
+ }
33843
+ }
33844
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
33845
+ range: zoneToXc(zone),
33846
+ }));
33847
+ }
33848
+ return data;
33849
+ },
33850
+ },
33769
33851
  {
33770
33852
  description: "Change Border description structure",
33771
33853
  from: 12,
@@ -34605,22 +34687,7 @@
34605
34687
  }
34606
34688
  }
34607
34689
  export(data) {
34608
- // Borders
34609
- let borderId = 0;
34610
34690
  const borders = {};
34611
- /**
34612
- * Get the id of the given border. If the border does not exist, it creates
34613
- * one.
34614
- */
34615
- function getBorderId(border) {
34616
- for (let [key, value] of Object.entries(borders)) {
34617
- if (deepEquals(value, border)) {
34618
- return parseInt(key, 10);
34619
- }
34620
- }
34621
- borders[++borderId] = border;
34622
- return borderId;
34623
- }
34624
34691
  for (let sheet of data.sheets) {
34625
34692
  for (let col = 0; col < sheet.colNumber; col++) {
34626
34693
  for (let row = 0; row < sheet.rowNumber; row++) {
@@ -34628,7 +34695,7 @@
34628
34695
  if (border) {
34629
34696
  const xc = toXC(col, row);
34630
34697
  const cell = sheet.cells[xc];
34631
- const borderId = getBorderId(border);
34698
+ const borderId = getItemId(border, borders);
34632
34699
  if (cell) {
34633
34700
  cell.border = borderId;
34634
34701
  }
@@ -35433,9 +35500,9 @@
35433
35500
  allowDispatch(cmd) {
35434
35501
  switch (cmd.type) {
35435
35502
  case "UPDATE_CELL":
35436
- return this.checkCellOutOfSheet(cmd);
35503
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35437
35504
  case "CLEAR_CELL":
35438
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35505
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35439
35506
  default:
35440
35507
  return "Success" /* CommandResult.Success */;
35441
35508
  }
@@ -35568,9 +35635,6 @@
35568
35635
  format: cell.format ? getItemId(cell.format, formats) : undefined,
35569
35636
  content: cell.content || undefined,
35570
35637
  };
35571
- if (cell instanceof FormulaCellWithDependencies) {
35572
- cells[xc].content = cell.contentWithFixedReferences || undefined;
35573
- }
35574
35638
  }
35575
35639
  _sheet.cells = cells;
35576
35640
  }
@@ -35588,8 +35652,8 @@
35588
35652
  }
35589
35653
  removeDefaultStyleValues(style) {
35590
35654
  const cleanedStyle = { ...style };
35591
- for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
35592
- if (cleanedStyle[property] === defaultValue) {
35655
+ for (const property in DEFAULT_STYLE) {
35656
+ if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
35593
35657
  delete cleanedStyle[property];
35594
35658
  }
35595
35659
  }
@@ -35746,10 +35810,7 @@
35746
35810
  else {
35747
35811
  style = before ? before.style : undefined;
35748
35812
  }
35749
- const locale = this.getters.getLocale();
35750
- let format = ("format" in after ? after.format : before && before.format) ||
35751
- detectDateFormat(afterContent, locale) ||
35752
- detectNumberFormat(afterContent);
35813
+ const format = "format" in after ? after.format : before && before.format;
35753
35814
  /* Read the following IF as:
35754
35815
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
35755
35816
  * - the command says the new content is empty and has no border/format/style
@@ -35790,12 +35851,11 @@
35790
35851
  }
35791
35852
  createLiteralCell(id, content, format, style) {
35792
35853
  const locale = this.getters.getLocale();
35793
- content = parseLiteral(content, locale).toString();
35794
35854
  return {
35795
35855
  id,
35796
- content,
35856
+ content: parseLiteral(content, locale).toString(),
35797
35857
  style,
35798
- format,
35858
+ format: format || detectDateFormat(content, locale) || detectNumberFormat(content),
35799
35859
  isFormula: false,
35800
35860
  };
35801
35861
  }
@@ -35821,8 +35881,11 @@
35821
35881
  * being a computed property to rebuild the dependencies XC.
35822
35882
  */
35823
35883
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
35824
- const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
35825
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
35884
+ const dependencies = [];
35885
+ for (const xc of compiledFormula.dependencies) {
35886
+ dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
35887
+ }
35888
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
35826
35889
  }
35827
35890
  createErrorFormula(id, content, format, style, error) {
35828
35891
  return {
@@ -35857,6 +35920,18 @@
35857
35920
  }
35858
35921
  return "Success" /* CommandResult.Success */;
35859
35922
  }
35923
+ checkUselessUpdateCell(cmd) {
35924
+ const cell = this.getters.getCell(cmd);
35925
+ const hasContent = "content" in cmd || "formula" in cmd;
35926
+ const hasStyle = "style" in cmd;
35927
+ const hasFormat = "format" in cmd;
35928
+ if ((!hasContent || cell?.content === cmd.content) &&
35929
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
35930
+ (!hasFormat || cell?.format === cmd.format)) {
35931
+ return "NoChanges" /* CommandResult.NoChanges */;
35932
+ }
35933
+ return "Success" /* CommandResult.Success */;
35934
+ }
35860
35935
  }
35861
35936
  class FormulaCellWithDependencies {
35862
35937
  id;
@@ -35876,7 +35951,7 @@
35876
35951
  const tokens = compiledFormula.tokens.map((token) => {
35877
35952
  if (token.type === "REFERENCE") {
35878
35953
  const index = rangeIndex++;
35879
- return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
35954
+ return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
35880
35955
  }
35881
35956
  return token;
35882
35957
  });
@@ -35903,13 +35978,20 @@
35903
35978
  }
35904
35979
  }
35905
35980
  class RangeReferenceToken {
35906
- getValue;
35981
+ ranges;
35982
+ rangeIndex;
35983
+ sheetId;
35984
+ getRangeString;
35907
35985
  type = "REFERENCE";
35908
- constructor(getValue) {
35909
- this.getValue = getValue;
35986
+ constructor(ranges, rangeIndex, sheetId, getRangeString) {
35987
+ this.ranges = ranges;
35988
+ this.rangeIndex = rangeIndex;
35989
+ this.sheetId = sheetId;
35990
+ this.getRangeString = getRangeString;
35910
35991
  }
35911
35992
  get value() {
35912
- return this.getValue();
35993
+ const range = this.ranges[this.rangeIndex];
35994
+ return this.getRangeString(range, this.sheetId);
35913
35995
  }
35914
35996
  }
35915
35997
 
@@ -40645,7 +40727,15 @@
40645
40727
  if (!this.rTrees[sheetId]) {
40646
40728
  return;
40647
40729
  }
40648
- this.rTrees[sheetId].remove(item, deepEquals);
40730
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
40731
+ }
40732
+ rtreeItemComparer(left, right) {
40733
+ return (left.data == right.data &&
40734
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
40735
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
40736
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
40737
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
40738
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
40649
40739
  }
40650
40740
  }
40651
40741
  /**
@@ -40723,7 +40813,7 @@
40723
40813
  const queue = Array.from(ranges).reverse();
40724
40814
  while (queue.length > 0) {
40725
40815
  const range = queue.pop();
40726
- visited.add(...this.encoder.encodeBoundingBox(range));
40816
+ visited.addMany(this.encoder.encodeBoundingBox(range));
40727
40817
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
40728
40818
  for (const positionId of impactedPositionIds) {
40729
40819
  if (!visited.has(positionId)) {
@@ -40731,7 +40821,7 @@
40731
40821
  }
40732
40822
  }
40733
40823
  }
40734
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
40824
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
40735
40825
  return visited;
40736
40826
  }
40737
40827
  }
@@ -40869,9 +40959,9 @@
40869
40959
  const cells = positions.map((p) => this.encoder.encode(p));
40870
40960
  const cellsToCompute = new JetSet(cells);
40871
40961
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
40872
- cellsToCompute.add(...this.getCellsDependingOn(cells));
40873
- cellsToCompute.add(...arrayFormulasPositionIds);
40874
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
40962
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
40963
+ cellsToCompute.addMany(arrayFormulasPositionIds);
40964
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
40875
40965
  this.evaluate(cellsToCompute);
40876
40966
  }
40877
40967
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -40885,7 +40975,7 @@
40885
40975
  }
40886
40976
  if (!content) {
40887
40977
  // The previous content could have blocked some array formulas
40888
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
40978
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
40889
40979
  }
40890
40980
  }
40891
40981
  return impactedPositionIds;
@@ -40934,7 +41024,7 @@
40934
41024
  }
40935
41025
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
40936
41026
  const cells = new JetSet(arrayFormulas);
40937
- cells.add(...this.getCellsDependingOn(arrayFormulas));
41027
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
40938
41028
  return cells;
40939
41029
  }
40940
41030
  nextPositionsToUpdate = new JetSet();
@@ -41083,7 +41173,7 @@
41083
41173
  this.setEvaluatedCell(positionId, evaluatedCell);
41084
41174
  // check if formula dependencies present in the spread zone
41085
41175
  // if so, they need to be recomputed
41086
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
41176
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41087
41177
  };
41088
41178
  }
41089
41179
  invalidateSpreading(positionId) {
@@ -41098,8 +41188,8 @@
41098
41188
  continue;
41099
41189
  }
41100
41190
  this.evaluatedCells.delete(child);
41101
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
41102
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
41191
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
41192
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
41103
41193
  }
41104
41194
  this.spreadingRelations.removeNode(positionId);
41105
41195
  }
@@ -41448,7 +41538,13 @@
41448
41538
  const format = newFormat
41449
41539
  ? getItemId(newFormat, data.formats)
41450
41540
  : exportedCellData.format;
41451
- const content = !isExported ? newContent : exportedCellData.content;
41541
+ let content;
41542
+ if (formulaCell instanceof FormulaCellWithDependencies) {
41543
+ content = this.getters.getFormulaCellContent(exportedSheetData.id, formulaCell.compiledFormula, formulaCell.compiledFormula.dependencies, true);
41544
+ }
41545
+ else {
41546
+ content = !isExported ? newContent : exportedCellData.content;
41547
+ }
41452
41548
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
41453
41549
  }
41454
41550
  }
@@ -50764,6 +50860,16 @@
50764
50860
  }
50765
50861
  }
50766
50862
 
50863
+ class FocusableElement {
50864
+ focusableElement = undefined;
50865
+ setFocusableElement(element) {
50866
+ this.focusableElement = element;
50867
+ }
50868
+ focus() {
50869
+ this.focusableElement?.focus();
50870
+ }
50871
+ }
50872
+
50767
50873
  const RIPPLE_KEY_FRAMES = [
50768
50874
  { transform: "scale(0)" },
50769
50875
  { transform: "scale(0.8)", offset: 0.33 },
@@ -52920,6 +53026,7 @@
52920
53026
  toggleSidePanel: this.toggleSidePanel.bind(this),
52921
53027
  clipboard: this.env.clipboard || instantiateClipboard(),
52922
53028
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
53029
+ focusableElement: new FocusableElement(),
52923
53030
  });
52924
53031
  owl.useExternalListener(window, "resize", () => this.render(true));
52925
53032
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -53026,16 +53133,17 @@
53026
53133
  }
53027
53134
  this.composer.topBarFocus = "contentFocus";
53028
53135
  this.composer.gridFocusMode = "inactive";
53029
- this.setComposerContent({ selection } || {});
53136
+ this.setComposerContent({ selection });
53030
53137
  }
53031
- onGridComposerContentFocused() {
53138
+ onGridComposerContentFocused(selection) {
53032
53139
  if (this.model.getters.isReadonly()) {
53033
53140
  return;
53034
53141
  }
53035
53142
  this.composer.topBarFocus = "inactive";
53036
53143
  this.composer.gridFocusMode = "contentFocus";
53037
- this.setComposerContent({});
53144
+ this.setComposerContent({ selection });
53038
53145
  }
53146
+ // TODO: either both are defined or none of them. change those args to an object
53039
53147
  onGridComposerCellFocused(content, selection) {
53040
53148
  if (this.model.getters.isReadonly()) {
53041
53149
  return;
@@ -54461,11 +54569,10 @@
54461
54569
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
54462
54570
  */
54463
54571
  getEndOfCluster(startPosition, dim, dir) {
54464
- const sheet = this.getters.getActiveSheet();
54465
54572
  let currentPosition = startPosition;
54466
54573
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
54467
54574
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
54468
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
54575
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
54469
54576
  ? "endOfCluster"
54470
54577
  : "nextCluster";
54471
54578
  while (true) {
@@ -54475,7 +54582,7 @@
54475
54582
  currentPosition.row === nextCellPosition.row) {
54476
54583
  break;
54477
54584
  }
54478
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
54585
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
54479
54586
  if (mode === "endOfCluster" && isNextCellEmpty) {
54480
54587
  break;
54481
54588
  }
@@ -54489,10 +54596,20 @@
54489
54596
  return dim === "cols" ? currentPosition.col : currentPosition.row;
54490
54597
  }
54491
54598
  /**
54492
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
54493
- * check if the merge containing the cell is empty.
54599
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
54600
+ * the check applies to the main cell of the merge.
54601
+ */
54602
+ isCellEmpty({ col, row }) {
54603
+ const sheetId = this.getters.getActiveSheetId();
54604
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
54605
+ return !(this.getters.getCorrespondingFormulaCell(position) || this.getters.getCell(position)?.content);
54606
+ }
54607
+ /**
54608
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
54609
+ * the check applies to the main cell of the merge.
54494
54610
  */
54495
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
54611
+ isEvaluatedCellEmpty({ col, row }) {
54612
+ const sheetId = this.getters.getActiveSheetId();
54496
54613
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
54497
54614
  const cell = this.getters.getEvaluatedCell(position);
54498
54615
  return cell.type === CellValueType.empty;
@@ -56798,9 +56915,9 @@
56798
56915
  exports.tokenize = tokenize;
56799
56916
 
56800
56917
 
56801
- __info__.version = "17.0.10";
56802
- __info__.date = "2024-02-02T13:48:28.727Z";
56803
- __info__.hash = "be06aa9";
56918
+ __info__.version = "17.0.12";
56919
+ __info__.date = "2024-02-16T14:24:02.984Z";
56920
+ __info__.hash = "8fa2bab";
56804
56921
 
56805
56922
 
56806
56923
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);