@odoo/o-spreadsheet 17.1.2 → 17.1.4

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.1.2
6
- * @date 2024-01-29T13:54:42.679Z
7
- * @hash 043a0d5
5
+ * @version 17.1.4
6
+ * @date 2024-02-09T13:09:13.430Z
7
+ * @hash 16d3ece
8
8
  */
9
9
 
10
10
  'use strict';
@@ -2940,6 +2940,9 @@ function formatValue(value, { format, locale }) {
2940
2940
  }
2941
2941
  switch (typeof value) {
2942
2942
  case "string":
2943
+ if (value.includes('\\"')) {
2944
+ return value.replace(/\\"/g, '"');
2945
+ }
2943
2946
  return value;
2944
2947
  case "boolean":
2945
2948
  return value ? "TRUE" : "FALSE";
@@ -4993,14 +4996,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
4993
4996
  : undefined));
4994
4997
  }
4995
4998
  }
4996
- else if (zone.left === zone.right && zone.top === zone.bottom) {
4997
- // A single cell. If it's only the title, the dataset is not added.
4998
- if (!dataSetsHaveTitle) {
4999
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
5000
- }
5001
- }
5002
4999
  else {
5003
- /* 1 row or 1 column */
5000
+ /* 1 cell, 1 row or 1 column */
5004
5001
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
5005
5002
  ? {
5006
5003
  top: zone.top,
@@ -5049,8 +5046,10 @@ function toExcelDataset(getters, ds) {
5049
5046
  }
5050
5047
  const dataRange = ds.dataRange.clone({ zone: dataZone });
5051
5048
  return {
5052
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
5053
- range: getters.getRangeString(dataRange, "forceSheetReference"),
5049
+ label: ds.labelCell
5050
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", { useFixedReference: true })
5051
+ : undefined,
5052
+ range: getters.getRangeString(dataRange, "forceSheetReference", { useFixedReference: true }),
5054
5053
  };
5055
5054
  }
5056
5055
  function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
@@ -5063,7 +5062,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
5063
5062
  zone.top = zone.top + 1;
5064
5063
  }
5065
5064
  const range = labelRange.clone({ zone });
5066
- return getters.getRangeString(range, "forceSheetReference");
5065
+ return getters.getRangeString(range, "forceSheetReference", { useFixedReference: true });
5067
5066
  }
5068
5067
  /**
5069
5068
  * Transform a chart definition which supports dataSets (dataSets and LabelRange)
@@ -6698,6 +6697,7 @@ const FilterMenuPopoverBuilder = {
6698
6697
  };
6699
6698
 
6700
6699
  const macRegex = /Mac/i;
6700
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
6701
6701
  /**
6702
6702
  * Return true if the event was triggered from
6703
6703
  * a child element.
@@ -6749,14 +6749,14 @@ const letterRegex = /^[a-zA-Z]$/;
6749
6749
  */
6750
6750
  function keyboardEventToShortcutString(ev, mode = "key") {
6751
6751
  let keyDownString = "";
6752
- if (isCtrlKey(ev) && ev.key !== "Ctrl")
6753
- keyDownString += "Ctrl+";
6754
- if (ev.metaKey)
6755
- keyDownString += "Ctrl+";
6756
- if (ev.altKey && ev.key !== "Alt")
6757
- keyDownString += "Alt+";
6758
- if (ev.shiftKey && ev.key !== "Shift")
6759
- keyDownString += "Shift+";
6752
+ if (!MODIFIER_KEYS.includes(ev.key)) {
6753
+ if (isCtrlKey(ev))
6754
+ keyDownString += "Ctrl+";
6755
+ if (ev.altKey)
6756
+ keyDownString += "Alt+";
6757
+ if (ev.shiftKey)
6758
+ keyDownString += "Shift+";
6759
+ }
6760
6760
  const key = mode === "key" ? ev.key : ev.code;
6761
6761
  keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
6762
6762
  return keyDownString;
@@ -7440,7 +7440,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7440
7440
  while (!chars.isOver()) {
7441
7441
  let token = tokenizeSpace(chars) ||
7442
7442
  tokenizeArgsSeparator(chars, locale) ||
7443
- tokenizeMisc(chars) ||
7443
+ tokenizeParenthesis(chars) ||
7444
7444
  tokenizeOperator(chars) ||
7445
7445
  tokenizeString(chars) ||
7446
7446
  tokenizeDebugger(chars) ||
@@ -7461,15 +7461,14 @@ function tokenizeDebugger(chars) {
7461
7461
  }
7462
7462
  return null;
7463
7463
  }
7464
- const misc$1 = {
7465
- "(": "LEFT_PAREN",
7466
- ")": "RIGHT_PAREN",
7464
+ const parenthesis = {
7465
+ "(": { type: "LEFT_PAREN", value: "(" },
7466
+ ")": { type: "RIGHT_PAREN", value: ")" },
7467
7467
  };
7468
- function tokenizeMisc(chars) {
7469
- if (chars.current in misc$1) {
7468
+ function tokenizeParenthesis(chars) {
7469
+ if (chars.current === "(" || chars.current === ")") {
7470
7470
  const value = chars.shift();
7471
- const type = misc$1[value];
7472
- return { type, value };
7471
+ return parenthesis[value];
7473
7472
  }
7474
7473
  return null;
7475
7474
  }
@@ -7490,7 +7489,12 @@ function tokenizeOperator(chars) {
7490
7489
  }
7491
7490
  return null;
7492
7491
  }
7492
+ const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
7493
7493
  function tokenizeNumber(chars, locale) {
7494
+ if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
7495
+ chars.current !== locale.decimalSeparator) {
7496
+ return null;
7497
+ }
7494
7498
  const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
7495
7499
  if (match) {
7496
7500
  chars.advanceBy(match[0].length);
@@ -7515,7 +7519,7 @@ function tokenizeString(chars) {
7515
7519
  }
7516
7520
  return null;
7517
7521
  }
7518
- const separatorRegexp = /\w|\.|!|\$/;
7522
+ const separatorRegexp = /^[\w\.!\$]+/;
7519
7523
  /**
7520
7524
  * A "Symbol" is just basically any word-like element that can appear in a
7521
7525
  * formula, which is not a string. So:
@@ -7555,8 +7559,11 @@ function tokenizeSymbol(chars) {
7555
7559
  };
7556
7560
  }
7557
7561
  }
7558
- while (chars.current && separatorRegexp.test(chars.current)) {
7559
- result += chars.shift();
7562
+ const match = chars.remaining().match(separatorRegexp);
7563
+ if (match) {
7564
+ const value = match[0];
7565
+ result += value;
7566
+ chars.advanceBy(value.length);
7560
7567
  }
7561
7568
  if (result.length) {
7562
7569
  const value = result;
@@ -7618,7 +7625,10 @@ class TokenizingChars {
7618
7625
  return this.text.substring(this.currentIndex);
7619
7626
  }
7620
7627
  currentStartsWith(str) {
7621
- for (let j = 0; j < str.length; j++) {
7628
+ if (this.current !== str[0]) {
7629
+ return false;
7630
+ }
7631
+ for (let j = 1; j < str.length; j++) {
7622
7632
  if (this.text[this.currentIndex + j] !== str[j]) {
7623
7633
  return false;
7624
7634
  }
@@ -8650,7 +8660,7 @@ class BarChart extends AbstractChart {
8650
8660
  return undefined;
8651
8661
  const dataSets = this.dataSets
8652
8662
  .map((ds) => toExcelDataset(this.getters, ds))
8653
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
8663
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
8654
8664
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
8655
8665
  return {
8656
8666
  ...this.getDefinition(),
@@ -9228,7 +9238,7 @@ class LineChart extends AbstractChart {
9228
9238
  return undefined;
9229
9239
  const dataSets = this.dataSets
9230
9240
  .map((ds) => toExcelDataset(this.getters, ds))
9231
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
9241
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
9232
9242
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
9233
9243
  return {
9234
9244
  ...this.getDefinition(),
@@ -9534,7 +9544,7 @@ class PieChart extends AbstractChart {
9534
9544
  return undefined;
9535
9545
  const dataSets = this.dataSets
9536
9546
  .map((ds) => toExcelDataset(this.getters, ds))
9537
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
9547
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
9538
9548
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
9539
9549
  return {
9540
9550
  ...this.getDefinition(),
@@ -18681,6 +18691,9 @@ const HLOOKUP = {
18681
18691
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18682
18692
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18683
18693
  assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18694
+ if (searchKey && isEvaluationError(searchKey.value)) {
18695
+ return searchKey;
18696
+ }
18684
18697
  const getValueFromRange = (range, index) => range[index][0].value;
18685
18698
  const _isSorted = toBoolean(isSorted.value);
18686
18699
  const colIndex = _isSorted
@@ -18848,6 +18861,9 @@ const VLOOKUP = {
18848
18861
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18849
18862
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18850
18863
  assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18864
+ if (searchKey && isEvaluationError(searchKey.value)) {
18865
+ return searchKey;
18866
+ }
18851
18867
  const getValueFromRange = (range, index) => range[0][index].value;
18852
18868
  const _isSorted = toBoolean(isSorted.value);
18853
18869
  const rowIndex = _isSorted
@@ -18887,6 +18903,9 @@ const XLOOKUP = {
18887
18903
  assert(() => lookupDirection === "col"
18888
18904
  ? returnRange[0].length === lookupRange[0].length
18889
18905
  : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18906
+ if (searchKey && isEvaluationError(searchKey.value)) {
18907
+ return [[searchKey]];
18908
+ }
18890
18909
  const getElement = lookupDirection === "col"
18891
18910
  ? (range, index) => range[0][index].value
18892
18911
  : (range, index) => range[index][0].value;
@@ -20208,19 +20227,20 @@ const formatNumberPercent = createFormatActionSpec({
20208
20227
  const formatNumberCurrency = createFormatActionSpec({
20209
20228
  name: _t("Currency"),
20210
20229
  descriptionValue: 1000.12,
20211
- format: (env) => env.model.config.defaultCurrencyFormat,
20230
+ format: (env) => env.model.config.defaultCurrencyFormat ?? DEFAULT_CURRENCY_FORMAT,
20212
20231
  });
20213
20232
  const formatNumberCurrencyRounded = {
20214
20233
  ...createFormatActionSpec({
20215
20234
  name: _t("Currency rounded"),
20216
20235
  descriptionValue: 1000,
20217
- format: (env) => roundFormat(env.model.config.defaultCurrencyFormat),
20236
+ format: (env) => roundFormat(env.model.config.defaultCurrencyFormat ?? DEFAULT_CURRENCY_FORMAT),
20218
20237
  }),
20219
20238
  isVisible: (env) => {
20220
20239
  const currencyFormat = env.model.config.defaultCurrencyFormat;
20221
- return currencyFormat !== roundFormat(currencyFormat);
20240
+ return currencyFormat !== roundFormat(currencyFormat ?? DEFAULT_CURRENCY_FORMAT);
20222
20241
  },
20223
20242
  };
20243
+ const DEFAULT_CURRENCY_FORMAT = "[$$]#,##0.00";
20224
20244
  const EXAMPLE_DATE = parseLiteral("2023/09/26 10:43:00 PM", DEFAULT_LOCALE);
20225
20245
  const formatCustomCurrency = {
20226
20246
  name: _t("Custom currency"),
@@ -26967,15 +26987,17 @@ class Composer extends owl.Component {
26967
26987
  focus: {
26968
26988
  validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
26969
26989
  },
26970
- onComposerContentFocused: Function,
26971
26990
  inputStyle: { type: String, optional: true },
26972
26991
  rect: { type: Object, optional: true },
26973
26992
  delimitation: { type: Object, optional: true },
26974
- onComposerUnmounted: { type: Function, optional: true },
26993
+ onComposerCellFocused: { type: Function, optional: true },
26994
+ onComposerContentFocused: Function,
26995
+ isDefaultFocus: { type: Boolean, optional: true },
26975
26996
  };
26976
26997
  static components = { TextValueProvider, FunctionDescriptionProvider };
26977
26998
  static defaultProps = {
26978
26999
  inputStyle: "",
27000
+ isDefaultFocus: false,
26979
27001
  };
26980
27002
  composerRef = owl.useRef("o_composer");
26981
27003
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -27032,7 +27054,6 @@ class Composer extends owl.Component {
27032
27054
  F4: this.processF4Key,
27033
27055
  Tab: (ev) => this.processTabKey(ev, "right"),
27034
27056
  "Shift+Tab": (ev) => this.processTabKey(ev, "left"),
27035
- "Ctrl+ ": this.processSpaceKey,
27036
27057
  };
27037
27058
  keyCodeMapping = {
27038
27059
  NumpadDecimal: this.processNumpadDecimal,
@@ -27040,12 +27061,12 @@ class Composer extends owl.Component {
27040
27061
  setup() {
27041
27062
  owl.onMounted(() => {
27042
27063
  const el = this.composerRef.el;
27064
+ if (this.props.isDefaultFocus) {
27065
+ this.env.focusableElement.setFocusableElement(el);
27066
+ }
27043
27067
  this.contentHelper.updateEl(el);
27044
27068
  this.processTokenAtCursor();
27045
27069
  });
27046
- owl.onWillUnmount(() => {
27047
- this.props.onComposerUnmounted?.();
27048
- });
27049
27070
  owl.useEffect(() => {
27050
27071
  this.processContent();
27051
27072
  });
@@ -27054,7 +27075,8 @@ class Composer extends owl.Component {
27054
27075
  // Handlers
27055
27076
  // ---------------------------------------------------------------------------
27056
27077
  processArrowKeys(ev) {
27057
- if (this.env.model.getters.isSelectingForComposer()) {
27078
+ if (this.env.model.getters.isSelectingForComposer() ||
27079
+ this.env.model.getters.getEditionMode() === "inactive") {
27058
27080
  this.functionDescriptionState.showDescription = false;
27059
27081
  // Prevent the default content editable behavior which moves the cursor
27060
27082
  ev.preventDefault();
@@ -27097,23 +27119,19 @@ class Composer extends owl.Component {
27097
27119
  processTabKey(ev, direction) {
27098
27120
  ev.preventDefault();
27099
27121
  ev.stopPropagation();
27100
- const state = this.autoCompleteState;
27101
- if (state.showProvider && state.selectedIndex !== undefined) {
27102
- const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
27103
- if (autoCompleteValue) {
27104
- this.autoComplete(autoCompleteValue);
27105
- return;
27122
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
27123
+ const state = this.autoCompleteState;
27124
+ if (state.showProvider && state.selectedIndex !== undefined) {
27125
+ const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
27126
+ if (autoCompleteValue) {
27127
+ this.autoComplete(autoCompleteValue);
27128
+ return;
27129
+ }
27106
27130
  }
27131
+ interactiveStopEdition(this.env);
27107
27132
  }
27108
- interactiveStopEdition(this.env);
27109
27133
  this.env.model.selection.moveAnchorCell(direction, 1);
27110
27134
  }
27111
- processSpaceKey(ev) {
27112
- ev.preventDefault();
27113
- ev.stopPropagation();
27114
- this.showFunctionAutocomplete("");
27115
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27116
- }
27117
27135
  processEnterKey(ev, direction) {
27118
27136
  ev.preventDefault();
27119
27137
  ev.stopPropagation();
@@ -27175,6 +27193,9 @@ class Composer extends owl.Component {
27175
27193
  this.compositionActive = false;
27176
27194
  }
27177
27195
  onKeydown(ev) {
27196
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27197
+ return;
27198
+ }
27178
27199
  if (ev.key.startsWith("Arrow")) {
27179
27200
  this.processArrowKeys(ev);
27180
27201
  return;
@@ -27188,14 +27209,33 @@ class Composer extends owl.Component {
27188
27209
  ev.stopPropagation();
27189
27210
  }
27190
27211
  }
27212
+ onPaste(ev) {
27213
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
27214
+ ev.stopPropagation();
27215
+ }
27216
+ }
27191
27217
  /*
27192
27218
  * Triggered automatically by the content-editable between the keydown and key up
27193
27219
  * */
27194
27220
  onInput(ev) {
27195
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
27221
+ if (!this.shouldProcessInputEvents) {
27222
+ return;
27223
+ }
27224
+ if (ev.inputType === "insertFromPaste" &&
27225
+ this.env.model.getters.getEditionMode() === "inactive") {
27196
27226
  return;
27197
27227
  }
27198
- let content = this.contentHelper.getText();
27228
+ ev.stopPropagation();
27229
+ let content;
27230
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27231
+ content = ev.data || "";
27232
+ }
27233
+ else {
27234
+ content = this.contentHelper.getText();
27235
+ }
27236
+ if (this.props.focus === "inactive") {
27237
+ return this.props.onComposerCellFocused?.(content);
27238
+ }
27199
27239
  let selection = this.contentHelper.getCurrentSelection();
27200
27240
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27201
27241
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
@@ -27271,9 +27311,8 @@ class Composer extends owl.Component {
27271
27311
  }
27272
27312
  const newSelection = this.contentHelper.getCurrentSelection();
27273
27313
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27274
- if (this.props.focus === "inactive") {
27275
- this.props.onComposerContentFocused(newSelection);
27276
- }
27314
+ this.props.onComposerContentFocused();
27315
+ if (this.props.focus === "inactive") ;
27277
27316
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
27278
27317
  this.processTokenAtCursor();
27279
27318
  }
@@ -27440,7 +27479,7 @@ class Composer extends owl.Component {
27440
27479
  const highlights = this.env.model.getters.getHighlights();
27441
27480
  const refSheet = sheetName
27442
27481
  ? this.env.model.getters.getSheetIdByName(sheetName)
27443
- : this.env.model.getters.getCurrentEditedCell().sheetId;
27482
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
27444
27483
  const highlight = highlights.find((highlight) => {
27445
27484
  if (highlight.sheetId !== refSheet)
27446
27485
  return false;
@@ -27567,56 +27606,30 @@ class GridComposer extends owl.Component {
27567
27606
  focus: {
27568
27607
  validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
27569
27608
  },
27570
- onComposerUnmounted: Function,
27609
+ onComposerCellFocused: Function,
27571
27610
  onComposerContentFocused: Function,
27572
27611
  gridDims: Object,
27573
27612
  };
27574
27613
  static components = { Composer };
27575
- gridComposerRef;
27576
- zone;
27577
- rect;
27614
+ rect = this.defaultRect;
27615
+ isEditing = false;
27578
27616
  isCellReferenceVisible;
27579
- composerState;
27617
+ get defaultRect() {
27618
+ return { x: 0, y: 0, width: 0, height: 0 };
27619
+ }
27580
27620
  setup() {
27581
- this.gridComposerRef = owl.useRef("gridComposer");
27582
- this.composerState = owl.useState({
27583
- rect: undefined,
27584
- delimitation: undefined,
27585
- });
27586
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
27587
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
27588
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
27589
- this.isCellReferenceVisible = false;
27590
- owl.onMounted(() => {
27591
- const el = this.gridComposerRef.el;
27592
- this.composerState.rect = {
27593
- x: this.rect.x,
27594
- y: this.rect.y,
27595
- width: el.clientWidth,
27596
- height: el.clientHeight,
27597
- };
27598
- this.composerState.delimitation = {
27599
- width: this.props.gridDims.width,
27600
- height: this.props.gridDims.height,
27601
- };
27602
- });
27603
27621
  owl.onWillUpdateProps(() => {
27604
- if (this.isCellReferenceVisible) {
27605
- return;
27606
- }
27607
- const sheetId = this.env.model.getters.getActiveSheetId();
27608
- const zone = this.env.model.getters.getSelectedZone();
27609
- const rect = this.env.model.getters.getVisibleRect(zone);
27610
- if (!deepEquals(rect, this.rect) ||
27611
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27612
- this.isCellReferenceVisible = true;
27613
- }
27622
+ this.updateComponentPosition();
27623
+ this.updateCellReferenceVisibility();
27614
27624
  });
27615
27625
  }
27616
27626
  get shouldDisplayCellReference() {
27617
27627
  return this.isCellReferenceVisible;
27618
27628
  }
27619
27629
  get cellReference() {
27630
+ if (!this.env.model.getters.getCurrentEditedCell()) {
27631
+ return "";
27632
+ }
27620
27633
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27621
27634
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27622
27635
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
@@ -27628,7 +27641,27 @@ class GridComposer extends owl.Component {
27628
27641
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
27629
27642
  });
27630
27643
  }
27644
+ get composerProps() {
27645
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27646
+ return {
27647
+ rect: { ...this.rect },
27648
+ delimitation: {
27649
+ width,
27650
+ height,
27651
+ },
27652
+ focus: this.props.focus,
27653
+ isDefaultFocus: true,
27654
+ onComposerContentFocused: this.props.onComposerContentFocused,
27655
+ onComposerCellFocused: this.props.onComposerCellFocused,
27656
+ };
27657
+ }
27631
27658
  get containerStyle() {
27659
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27660
+ return `
27661
+ position: absolute;
27662
+ z-index: -1000;
27663
+ `;
27664
+ }
27632
27665
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27633
27666
  const cell = this.env.model.getters.getActiveCell();
27634
27667
  const position = this.env.model.getters.getActivePosition();
@@ -27648,6 +27681,8 @@ class GridComposer extends owl.Component {
27648
27681
  if (!isFormula) {
27649
27682
  textAlign = style.align || cell.defaultAlign;
27650
27683
  }
27684
+ const maxHeight = this.props.gridDims.height - this.rect.y;
27685
+ const maxWidth = this.props.gridDims.width - this.rect.x;
27651
27686
  /**
27652
27687
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
27653
27688
  * including all the paddings/margins of the composer
@@ -27659,6 +27694,8 @@ class GridComposer extends owl.Component {
27659
27694
  top: `${top}px`,
27660
27695
  "min-width": `${width + 1}px`,
27661
27696
  "min-height": `${height + 1}px`,
27697
+ "max-width": `${maxWidth}px`,
27698
+ "max-height": `${maxHeight}px`,
27662
27699
  background,
27663
27700
  color,
27664
27701
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -27668,13 +27705,31 @@ class GridComposer extends owl.Component {
27668
27705
  "text-align": textAlign,
27669
27706
  });
27670
27707
  }
27671
- get composerStyle() {
27672
- const maxHeight = this.props.gridDims.height - this.rect.y;
27673
- const maxWidth = this.props.gridDims.width - this.rect.x;
27674
- return cssPropertiesToCss({
27675
- "max-width": `${maxWidth}px`,
27676
- "max-height": `${maxHeight}px`,
27677
- });
27708
+ updateComponentPosition() {
27709
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
27710
+ if (this.isEditing !== isEditing) {
27711
+ this.isEditing = isEditing;
27712
+ if (!isEditing) {
27713
+ this.rect = this.defaultRect;
27714
+ this.env.focusableElement.focus();
27715
+ return;
27716
+ }
27717
+ const position = this.env.model.getters.getActivePosition();
27718
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
27719
+ this.rect = this.env.model.getters.getVisibleRect(zone);
27720
+ }
27721
+ }
27722
+ updateCellReferenceVisibility() {
27723
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
27724
+ return;
27725
+ }
27726
+ const sheetId = this.env.model.getters.getActiveSheetId();
27727
+ const zone = this.env.model.getters.getSelectedZone();
27728
+ const rect = this.env.model.getters.getVisibleRect(zone);
27729
+ if (!deepEquals(rect, this.rect) ||
27730
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27731
+ this.isCellReferenceVisible = true;
27732
+ }
27678
27733
  }
27679
27734
  }
27680
27735
 
@@ -28774,7 +28829,6 @@ class GridOverlay extends owl.Component {
28774
28829
  this.props.onCellDoubleClicked(col, row);
28775
28830
  }
28776
28831
  onContextMenu(ev) {
28777
- ev.preventDefault();
28778
28832
  const [col, row] = this.getCartesianCoordinates(ev);
28779
28833
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
28780
28834
  }
@@ -29839,7 +29893,6 @@ class Grid extends owl.Component {
29839
29893
  HEADER_WIDTH = HEADER_WIDTH;
29840
29894
  menuState;
29841
29895
  gridRef;
29842
- hiddenInput;
29843
29896
  onMouseWheel;
29844
29897
  canvasPosition;
29845
29898
  hoveredCell;
@@ -29850,15 +29903,14 @@ class Grid extends owl.Component {
29850
29903
  menuItems: [],
29851
29904
  });
29852
29905
  this.gridRef = owl.useRef("grid");
29853
- this.hiddenInput = owl.useRef("hiddenInput");
29854
29906
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
29855
29907
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
29856
29908
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
29857
29909
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
29858
29910
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
29859
29911
  owl.useExternalListener(document.body, "paste", this.paste);
29860
- owl.onMounted(() => this.focus());
29861
- this.props.exposeFocus(() => this.focus());
29912
+ owl.onMounted(() => this.focusDefaultElement());
29913
+ this.props.exposeFocus(() => this.focusDefaultElement());
29862
29914
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29863
29915
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29864
29916
  this.moveCanvas(deltaX, deltaY);
@@ -29882,7 +29934,7 @@ class Grid extends owl.Component {
29882
29934
  if (this.env.model.getters.hasOpenedPopover()) {
29883
29935
  this.closeOpenedPopover();
29884
29936
  }
29885
- this.focus();
29937
+ this.focusDefaultElement();
29886
29938
  }
29887
29939
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
29888
29940
  // down itself
@@ -30061,10 +30113,10 @@ class Grid extends owl.Component {
30061
30113
  "Alt+Shift+ArrowUp": () => this.processHeaderGroupingKey("up"),
30062
30114
  "Alt+Shift+ArrowDown": () => this.processHeaderGroupingKey("down"),
30063
30115
  };
30064
- focus() {
30116
+ focusDefaultElement() {
30065
30117
  if (!this.env.model.getters.getSelectedFigureId() &&
30066
30118
  this.env.model.getters.getEditionMode() === "inactive") {
30067
- this.hiddenInput.el?.focus();
30119
+ this.env.focusableElement.focus();
30068
30120
  }
30069
30121
  }
30070
30122
  get gridEl() {
@@ -30200,19 +30252,6 @@ class Grid extends owl.Component {
30200
30252
  return;
30201
30253
  }
30202
30254
  }
30203
- onInput(ev) {
30204
- // the user meant to paste in the sheet, not open the composer with the pasted content
30205
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
30206
- return;
30207
- }
30208
- if (ev.data) {
30209
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
30210
- // character
30211
- ev.preventDefault();
30212
- ev.stopPropagation();
30213
- this.props.onGridComposerCellFocused(ev.data);
30214
- }
30215
- }
30216
30255
  // ---------------------------------------------------------------------------
30217
30256
  // Context Menu
30218
30257
  // ---------------------------------------------------------------------------
@@ -30327,7 +30366,7 @@ class Grid extends owl.Component {
30327
30366
  }
30328
30367
  closeMenu() {
30329
30368
  this.menuState.isOpen = false;
30330
- this.focus();
30369
+ this.focusDefaultElement();
30331
30370
  }
30332
30371
  processHeaderGroupingKey(direction) {
30333
30372
  if (this.env.model.getters.getSelectedZones().length !== 1) {
@@ -34244,6 +34283,31 @@ const MIGRATIONS = [
34244
34283
  return data;
34245
34284
  },
34246
34285
  },
34286
+ {
34287
+ description: "Fix datafilter duplication",
34288
+ from: 12,
34289
+ to: 12.5,
34290
+ applyMigration(data) {
34291
+ for (let sheet of data.sheets || []) {
34292
+ let knownDataFilterZones = [];
34293
+ for (let filterTable of sheet.filterTables || []) {
34294
+ const zone = toZone(filterTable.range);
34295
+ // See commit message for the details
34296
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
34297
+ if (intersectZoneIndex !== -1) {
34298
+ knownDataFilterZones[intersectZoneIndex] = zone;
34299
+ }
34300
+ else {
34301
+ knownDataFilterZones.push(zone);
34302
+ }
34303
+ }
34304
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
34305
+ range: zoneToXc(zone),
34306
+ }));
34307
+ }
34308
+ return data;
34309
+ },
34310
+ },
34247
34311
  {
34248
34312
  description: "Change Border description structure",
34249
34313
  from: 12,
@@ -36073,12 +36137,12 @@ class CellPlugin extends CorePlugin {
36073
36137
  /*
36074
36138
  * Reconstructs the original formula string based on a normalized form and its dependencies
36075
36139
  */
36076
- getFormulaCellContent(sheetId, compiledFormula, dependencies) {
36140
+ getFormulaCellContent(sheetId, compiledFormula, dependencies, useFixedReference = false) {
36077
36141
  let rangeIndex = 0;
36078
36142
  return concat(compiledFormula.tokens.map((token) => {
36079
36143
  if (token.type === "REFERENCE") {
36080
36144
  const range = dependencies[rangeIndex++];
36081
- return this.getters.getRangeString(range, sheetId);
36145
+ return this.getters.getRangeString(range, sheetId, { useFixedReference });
36082
36146
  }
36083
36147
  return token.value;
36084
36148
  }));
@@ -36281,8 +36345,11 @@ class CellPlugin extends CorePlugin {
36281
36345
  * being a computed property to rebuild the dependencies XC.
36282
36346
  */
36283
36347
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
36284
- const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
36285
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
36348
+ const dependencies = [];
36349
+ for (const xc of compiledFormula.dependencies) {
36350
+ dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36351
+ }
36352
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
36286
36353
  }
36287
36354
  createErrorFormula(id, content, format, style, error) {
36288
36355
  return {
@@ -36336,7 +36403,7 @@ class FormulaCellWithDependencies {
36336
36403
  const tokens = compiledFormula.tokens.map((token) => {
36337
36404
  if (token.type === "REFERENCE") {
36338
36405
  const index = rangeIndex++;
36339
- return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
36406
+ return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
36340
36407
  }
36341
36408
  return token;
36342
36409
  });
@@ -36349,15 +36416,34 @@ class FormulaCellWithDependencies {
36349
36416
  get content() {
36350
36417
  return concat(this.compiledFormula.tokens.map((token) => token.value));
36351
36418
  }
36419
+ get contentWithFixedReferences() {
36420
+ let rangeIndex = 0;
36421
+ return concat(this.compiledFormula.tokens.map((token) => {
36422
+ if (token.type === "REFERENCE") {
36423
+ const index = rangeIndex++;
36424
+ return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
36425
+ useFixedReference: true,
36426
+ });
36427
+ }
36428
+ return token.value;
36429
+ }));
36430
+ }
36352
36431
  }
36353
36432
  class RangeReferenceToken {
36354
- getValue;
36433
+ ranges;
36434
+ rangeIndex;
36435
+ sheetId;
36436
+ getRangeString;
36355
36437
  type = "REFERENCE";
36356
- constructor(getValue) {
36357
- this.getValue = getValue;
36438
+ constructor(ranges, rangeIndex, sheetId, getRangeString) {
36439
+ this.ranges = ranges;
36440
+ this.rangeIndex = rangeIndex;
36441
+ this.sheetId = sheetId;
36442
+ this.getRangeString = getRangeString;
36358
36443
  }
36359
36444
  get value() {
36360
- return this.getValue();
36445
+ const range = this.ranges[this.rangeIndex];
36446
+ return this.getRangeString(range, this.sheetId);
36361
36447
  }
36362
36448
  }
36363
36449
 
@@ -36653,7 +36739,13 @@ class ConditionalFormatPlugin extends CorePlugin {
36653
36739
  }
36654
36740
  }
36655
36741
  exportForExcel(data) {
36656
- this.export(data);
36742
+ if (data.sheets) {
36743
+ for (let sheet of data.sheets) {
36744
+ if (this.cfRules[sheet.id]) {
36745
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
36746
+ }
36747
+ }
36748
+ }
36657
36749
  }
36658
36750
  // ---------------------------------------------------------------------------
36659
36751
  // Getters
@@ -36718,11 +36810,11 @@ class ConditionalFormatPlugin extends CorePlugin {
36718
36810
  // ---------------------------------------------------------------------------
36719
36811
  // Private
36720
36812
  // ---------------------------------------------------------------------------
36721
- mapToConditionalFormat(sheetId, cf) {
36813
+ mapToConditionalFormat(sheetId, cf, { useFixedReference } = { useFixedReference: false }) {
36722
36814
  return {
36723
36815
  ...cf,
36724
36816
  ranges: cf.ranges.map((range) => {
36725
- return this.getters.getRangeString(range, sheetId);
36817
+ return this.getters.getRangeString(range, sheetId, { useFixedReference });
36726
36818
  }),
36727
36819
  };
36728
36820
  }
@@ -38790,8 +38882,10 @@ class RangeAdapter {
38790
38882
  *
38791
38883
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
38792
38884
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
38885
+ * @param options
38886
+ * @param options.useFixedReference if true, the range will be returned with fixed row and column
38793
38887
  */
38794
- getRangeString(range, forSheetId) {
38888
+ getRangeString(range, forSheetId, options = { useFixedReference: false }) {
38795
38889
  if (!range) {
38796
38890
  return CellErrorType.InvalidReference;
38797
38891
  }
@@ -38818,7 +38912,7 @@ class RangeAdapter {
38818
38912
  if (prefixSheet && !sheetName) {
38819
38913
  return CellErrorType.InvalidReference;
38820
38914
  }
38821
- let rangeString = this.getRangePartString(rangeImpl, 0);
38915
+ let rangeString = this.getRangePartString(rangeImpl, 0, options);
38822
38916
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
38823
38917
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
38824
38918
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -38828,7 +38922,7 @@ class RangeAdapter {
38828
38922
  rangeImpl.parts[1].rowFixed ||
38829
38923
  rangeImpl.parts[1].colFixed) {
38830
38924
  rangeString += ":";
38831
- rangeString += this.getRangePartString(rangeImpl, 1);
38925
+ rangeString += this.getRangePartString(rangeImpl, 1, options);
38832
38926
  }
38833
38927
  }
38834
38928
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -38869,13 +38963,13 @@ class RangeAdapter {
38869
38963
  /**
38870
38964
  * Get a Xc string that represent a part of a range
38871
38965
  */
38872
- getRangePartString(range, part) {
38966
+ getRangePartString(range, part, options = { useFixedReference: false }) {
38873
38967
  const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
38874
38968
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
38875
38969
  const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
38876
38970
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
38877
38971
  let str = "";
38878
- if (range.isFullCol) {
38972
+ if (range.isFullCol && !options.useFixedReference) {
38879
38973
  if (part === 0 && range.unboundedZone.hasHeader) {
38880
38974
  str = colFixed + col + rowFixed + row;
38881
38975
  }
@@ -38883,7 +38977,7 @@ class RangeAdapter {
38883
38977
  str = colFixed + col;
38884
38978
  }
38885
38979
  }
38886
- else if (range.isFullRow) {
38980
+ else if (range.isFullRow && !options.useFixedReference) {
38887
38981
  if (part === 0 && range.unboundedZone.hasHeader) {
38888
38982
  str = colFixed + col + rowFixed + row;
38889
38983
  }
@@ -41884,7 +41978,13 @@ class EvaluationPlugin extends UIPlugin {
41884
41978
  const format = newFormat
41885
41979
  ? getItemId(newFormat, data.formats)
41886
41980
  : exportedCellData.format;
41887
- const content = !isExported ? newContent : exportedCellData.content;
41981
+ let content;
41982
+ if (formulaCell instanceof FormulaCellWithDependencies) {
41983
+ content = formulaCell.contentWithFixedReferences;
41984
+ }
41985
+ else {
41986
+ content = !isExported ? newContent : exportedCellData.content;
41987
+ }
41888
41988
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
41889
41989
  }
41890
41990
  }
@@ -51068,6 +51168,16 @@ class ImageProvider {
51068
51168
  }
51069
51169
  }
51070
51170
 
51171
+ class FocusableElement {
51172
+ focusableElement = undefined;
51173
+ setFocusableElement(element) {
51174
+ this.focusableElement = element;
51175
+ }
51176
+ focus() {
51177
+ this.focusableElement?.focus();
51178
+ }
51179
+ }
51180
+
51071
51181
  const RIPPLE_KEY_FRAMES = [
51072
51182
  { transform: "scale(0)" },
51073
51183
  { transform: "scale(0.8)", offset: 0.33 },
@@ -53222,6 +53332,7 @@ class Spreadsheet extends owl.Component {
53222
53332
  toggleSidePanel: this.toggleSidePanel.bind(this),
53223
53333
  clipboard: this.env.clipboard || instantiateClipboard(),
53224
53334
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
53335
+ focusableElement: new FocusableElement(),
53225
53336
  });
53226
53337
  owl.useEffect(() => {
53227
53338
  /**
@@ -53340,16 +53451,17 @@ class Spreadsheet extends owl.Component {
53340
53451
  }
53341
53452
  this.composer.topBarFocus = "contentFocus";
53342
53453
  this.composer.gridFocusMode = "inactive";
53343
- this.setComposerContent({ selection } || {});
53454
+ this.setComposerContent({ selection });
53344
53455
  }
53345
- onGridComposerContentFocused() {
53456
+ onGridComposerContentFocused(selection) {
53346
53457
  if (this.model.getters.isReadonly()) {
53347
53458
  return;
53348
53459
  }
53349
53460
  this.composer.topBarFocus = "inactive";
53350
53461
  this.composer.gridFocusMode = "contentFocus";
53351
- this.setComposerContent({});
53462
+ this.setComposerContent({ selection });
53352
53463
  }
53464
+ // TODO: either both are defined or none of them. change those args to an object
53353
53465
  onGridComposerCellFocused(content, selection) {
53354
53466
  if (this.model.getters.isReadonly()) {
53355
53467
  return;
@@ -55235,12 +55347,7 @@ function addFormula(cell) {
55235
55347
  attrs.push(["t", "str"]);
55236
55348
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
55237
55349
  }
55238
- node = escapeXml /*xml*/ `
55239
- <f>
55240
- ${XlsxFormula}
55241
- </f>
55242
- ${cycle}
55243
- `;
55350
+ node = escapeXml /*xml*/ `<f>${XlsxFormula}</f>${cycle}`;
55244
55351
  return { attrs, node };
55245
55352
  }
55246
55353
  function addContent(content, sharedStrings, forceString = false) {
@@ -56060,11 +56167,10 @@ function addRows(construct, data, sheet) {
56060
56167
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
56061
56168
  }
56062
56169
  attributes.push(...additionalAttrs);
56063
- cellNodes.push(escapeXml /*xml*/ `
56064
- <c ${formatAttributes(attributes)}>
56065
- ${cellNode}
56066
- </c>
56067
- `);
56170
+ // prettier-ignore
56171
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
56172
+ ${cellNode}
56173
+ </c>`);
56068
56174
  }
56069
56175
  }
56070
56176
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -56660,7 +56766,6 @@ class Model extends EventBus {
56660
56766
  ...config,
56661
56767
  mode: config.mode || "normal",
56662
56768
  custom: config.custom || {},
56663
- defaultCurrencyFormat: config.defaultCurrencyFormat || "[$$]#,##0.00",
56664
56769
  external: this.setupExternalConfig(config.external || {}),
56665
56770
  transportService,
56666
56771
  client,
@@ -56698,6 +56803,7 @@ class Model extends EventBus {
56698
56803
  custom: this.config.custom,
56699
56804
  uiActions: this.config,
56700
56805
  session: this.session,
56806
+ defaultCurrencyFormat: this.config.defaultCurrencyFormat,
56701
56807
  };
56702
56808
  }
56703
56809
  // ---------------------------------------------------------------------------
@@ -57079,6 +57185,6 @@ exports.setTranslationMethod = setTranslationMethod;
57079
57185
  exports.tokenize = tokenize;
57080
57186
 
57081
57187
 
57082
- __info__.version = "17.1.2";
57083
- __info__.date = "2024-01-29T13:54:42.679Z";
57084
- __info__.hash = "043a0d5";
57188
+ __info__.version = "17.1.4";
57189
+ __info__.date = "2024-02-09T13:09:13.430Z";
57190
+ __info__.hash = "16d3ece";