@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
  import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -2938,6 +2938,9 @@ function formatValue(value, { format, locale }) {
2938
2938
  }
2939
2939
  switch (typeof value) {
2940
2940
  case "string":
2941
+ if (value.includes('\\"')) {
2942
+ return value.replace(/\\"/g, '"');
2943
+ }
2941
2944
  return value;
2942
2945
  case "boolean":
2943
2946
  return value ? "TRUE" : "FALSE";
@@ -4991,14 +4994,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
4991
4994
  : undefined));
4992
4995
  }
4993
4996
  }
4994
- else if (zone.left === zone.right && zone.top === zone.bottom) {
4995
- // A single cell. If it's only the title, the dataset is not added.
4996
- if (!dataSetsHaveTitle) {
4997
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
4998
- }
4999
- }
5000
4997
  else {
5001
- /* 1 row or 1 column */
4998
+ /* 1 cell, 1 row or 1 column */
5002
4999
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
5003
5000
  ? {
5004
5001
  top: zone.top,
@@ -5047,8 +5044,10 @@ function toExcelDataset(getters, ds) {
5047
5044
  }
5048
5045
  const dataRange = ds.dataRange.clone({ zone: dataZone });
5049
5046
  return {
5050
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
5051
- range: getters.getRangeString(dataRange, "forceSheetReference"),
5047
+ label: ds.labelCell
5048
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", { useFixedReference: true })
5049
+ : undefined,
5050
+ range: getters.getRangeString(dataRange, "forceSheetReference", { useFixedReference: true }),
5052
5051
  };
5053
5052
  }
5054
5053
  function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
@@ -5061,7 +5060,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
5061
5060
  zone.top = zone.top + 1;
5062
5061
  }
5063
5062
  const range = labelRange.clone({ zone });
5064
- return getters.getRangeString(range, "forceSheetReference");
5063
+ return getters.getRangeString(range, "forceSheetReference", { useFixedReference: true });
5065
5064
  }
5066
5065
  /**
5067
5066
  * Transform a chart definition which supports dataSets (dataSets and LabelRange)
@@ -6696,6 +6695,7 @@ const FilterMenuPopoverBuilder = {
6696
6695
  };
6697
6696
 
6698
6697
  const macRegex = /Mac/i;
6698
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
6699
6699
  /**
6700
6700
  * Return true if the event was triggered from
6701
6701
  * a child element.
@@ -6747,14 +6747,14 @@ const letterRegex = /^[a-zA-Z]$/;
6747
6747
  */
6748
6748
  function keyboardEventToShortcutString(ev, mode = "key") {
6749
6749
  let keyDownString = "";
6750
- if (isCtrlKey(ev) && ev.key !== "Ctrl")
6751
- keyDownString += "Ctrl+";
6752
- if (ev.metaKey)
6753
- keyDownString += "Ctrl+";
6754
- if (ev.altKey && ev.key !== "Alt")
6755
- keyDownString += "Alt+";
6756
- if (ev.shiftKey && ev.key !== "Shift")
6757
- keyDownString += "Shift+";
6750
+ if (!MODIFIER_KEYS.includes(ev.key)) {
6751
+ if (isCtrlKey(ev))
6752
+ keyDownString += "Ctrl+";
6753
+ if (ev.altKey)
6754
+ keyDownString += "Alt+";
6755
+ if (ev.shiftKey)
6756
+ keyDownString += "Shift+";
6757
+ }
6758
6758
  const key = mode === "key" ? ev.key : ev.code;
6759
6759
  keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
6760
6760
  return keyDownString;
@@ -7438,7 +7438,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7438
7438
  while (!chars.isOver()) {
7439
7439
  let token = tokenizeSpace(chars) ||
7440
7440
  tokenizeArgsSeparator(chars, locale) ||
7441
- tokenizeMisc(chars) ||
7441
+ tokenizeParenthesis(chars) ||
7442
7442
  tokenizeOperator(chars) ||
7443
7443
  tokenizeString(chars) ||
7444
7444
  tokenizeDebugger(chars) ||
@@ -7459,15 +7459,14 @@ function tokenizeDebugger(chars) {
7459
7459
  }
7460
7460
  return null;
7461
7461
  }
7462
- const misc$1 = {
7463
- "(": "LEFT_PAREN",
7464
- ")": "RIGHT_PAREN",
7462
+ const parenthesis = {
7463
+ "(": { type: "LEFT_PAREN", value: "(" },
7464
+ ")": { type: "RIGHT_PAREN", value: ")" },
7465
7465
  };
7466
- function tokenizeMisc(chars) {
7467
- if (chars.current in misc$1) {
7466
+ function tokenizeParenthesis(chars) {
7467
+ if (chars.current === "(" || chars.current === ")") {
7468
7468
  const value = chars.shift();
7469
- const type = misc$1[value];
7470
- return { type, value };
7469
+ return parenthesis[value];
7471
7470
  }
7472
7471
  return null;
7473
7472
  }
@@ -7488,7 +7487,12 @@ function tokenizeOperator(chars) {
7488
7487
  }
7489
7488
  return null;
7490
7489
  }
7490
+ const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
7491
7491
  function tokenizeNumber(chars, locale) {
7492
+ if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
7493
+ chars.current !== locale.decimalSeparator) {
7494
+ return null;
7495
+ }
7492
7496
  const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
7493
7497
  if (match) {
7494
7498
  chars.advanceBy(match[0].length);
@@ -7513,7 +7517,7 @@ function tokenizeString(chars) {
7513
7517
  }
7514
7518
  return null;
7515
7519
  }
7516
- const separatorRegexp = /\w|\.|!|\$/;
7520
+ const separatorRegexp = /^[\w\.!\$]+/;
7517
7521
  /**
7518
7522
  * A "Symbol" is just basically any word-like element that can appear in a
7519
7523
  * formula, which is not a string. So:
@@ -7553,8 +7557,11 @@ function tokenizeSymbol(chars) {
7553
7557
  };
7554
7558
  }
7555
7559
  }
7556
- while (chars.current && separatorRegexp.test(chars.current)) {
7557
- result += chars.shift();
7560
+ const match = chars.remaining().match(separatorRegexp);
7561
+ if (match) {
7562
+ const value = match[0];
7563
+ result += value;
7564
+ chars.advanceBy(value.length);
7558
7565
  }
7559
7566
  if (result.length) {
7560
7567
  const value = result;
@@ -7616,7 +7623,10 @@ class TokenizingChars {
7616
7623
  return this.text.substring(this.currentIndex);
7617
7624
  }
7618
7625
  currentStartsWith(str) {
7619
- for (let j = 0; j < str.length; j++) {
7626
+ if (this.current !== str[0]) {
7627
+ return false;
7628
+ }
7629
+ for (let j = 1; j < str.length; j++) {
7620
7630
  if (this.text[this.currentIndex + j] !== str[j]) {
7621
7631
  return false;
7622
7632
  }
@@ -8648,7 +8658,7 @@ class BarChart extends AbstractChart {
8648
8658
  return undefined;
8649
8659
  const dataSets = this.dataSets
8650
8660
  .map((ds) => toExcelDataset(this.getters, ds))
8651
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
8661
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
8652
8662
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
8653
8663
  return {
8654
8664
  ...this.getDefinition(),
@@ -9226,7 +9236,7 @@ class LineChart extends AbstractChart {
9226
9236
  return undefined;
9227
9237
  const dataSets = this.dataSets
9228
9238
  .map((ds) => toExcelDataset(this.getters, ds))
9229
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
9239
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
9230
9240
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
9231
9241
  return {
9232
9242
  ...this.getDefinition(),
@@ -9532,7 +9542,7 @@ class PieChart extends AbstractChart {
9532
9542
  return undefined;
9533
9543
  const dataSets = this.dataSets
9534
9544
  .map((ds) => toExcelDataset(this.getters, ds))
9535
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
9545
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
9536
9546
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
9537
9547
  return {
9538
9548
  ...this.getDefinition(),
@@ -18679,6 +18689,9 @@ const HLOOKUP = {
18679
18689
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18680
18690
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18681
18691
  assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18692
+ if (searchKey && isEvaluationError(searchKey.value)) {
18693
+ return searchKey;
18694
+ }
18682
18695
  const getValueFromRange = (range, index) => range[index][0].value;
18683
18696
  const _isSorted = toBoolean(isSorted.value);
18684
18697
  const colIndex = _isSorted
@@ -18846,6 +18859,9 @@ const VLOOKUP = {
18846
18859
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18847
18860
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18848
18861
  assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18862
+ if (searchKey && isEvaluationError(searchKey.value)) {
18863
+ return searchKey;
18864
+ }
18849
18865
  const getValueFromRange = (range, index) => range[0][index].value;
18850
18866
  const _isSorted = toBoolean(isSorted.value);
18851
18867
  const rowIndex = _isSorted
@@ -18885,6 +18901,9 @@ const XLOOKUP = {
18885
18901
  assert(() => lookupDirection === "col"
18886
18902
  ? returnRange[0].length === lookupRange[0].length
18887
18903
  : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18904
+ if (searchKey && isEvaluationError(searchKey.value)) {
18905
+ return [[searchKey]];
18906
+ }
18888
18907
  const getElement = lookupDirection === "col"
18889
18908
  ? (range, index) => range[0][index].value
18890
18909
  : (range, index) => range[index][0].value;
@@ -20206,19 +20225,20 @@ const formatNumberPercent = createFormatActionSpec({
20206
20225
  const formatNumberCurrency = createFormatActionSpec({
20207
20226
  name: _t("Currency"),
20208
20227
  descriptionValue: 1000.12,
20209
- format: (env) => env.model.config.defaultCurrencyFormat,
20228
+ format: (env) => env.model.config.defaultCurrencyFormat ?? DEFAULT_CURRENCY_FORMAT,
20210
20229
  });
20211
20230
  const formatNumberCurrencyRounded = {
20212
20231
  ...createFormatActionSpec({
20213
20232
  name: _t("Currency rounded"),
20214
20233
  descriptionValue: 1000,
20215
- format: (env) => roundFormat(env.model.config.defaultCurrencyFormat),
20234
+ format: (env) => roundFormat(env.model.config.defaultCurrencyFormat ?? DEFAULT_CURRENCY_FORMAT),
20216
20235
  }),
20217
20236
  isVisible: (env) => {
20218
20237
  const currencyFormat = env.model.config.defaultCurrencyFormat;
20219
- return currencyFormat !== roundFormat(currencyFormat);
20238
+ return currencyFormat !== roundFormat(currencyFormat ?? DEFAULT_CURRENCY_FORMAT);
20220
20239
  },
20221
20240
  };
20241
+ const DEFAULT_CURRENCY_FORMAT = "[$$]#,##0.00";
20222
20242
  const EXAMPLE_DATE = parseLiteral("2023/09/26 10:43:00 PM", DEFAULT_LOCALE);
20223
20243
  const formatCustomCurrency = {
20224
20244
  name: _t("Custom currency"),
@@ -26965,15 +26985,17 @@ class Composer extends Component {
26965
26985
  focus: {
26966
26986
  validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
26967
26987
  },
26968
- onComposerContentFocused: Function,
26969
26988
  inputStyle: { type: String, optional: true },
26970
26989
  rect: { type: Object, optional: true },
26971
26990
  delimitation: { type: Object, optional: true },
26972
- onComposerUnmounted: { type: Function, optional: true },
26991
+ onComposerCellFocused: { type: Function, optional: true },
26992
+ onComposerContentFocused: Function,
26993
+ isDefaultFocus: { type: Boolean, optional: true },
26973
26994
  };
26974
26995
  static components = { TextValueProvider, FunctionDescriptionProvider };
26975
26996
  static defaultProps = {
26976
26997
  inputStyle: "",
26998
+ isDefaultFocus: false,
26977
26999
  };
26978
27000
  composerRef = useRef("o_composer");
26979
27001
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -27030,7 +27052,6 @@ class Composer extends Component {
27030
27052
  F4: this.processF4Key,
27031
27053
  Tab: (ev) => this.processTabKey(ev, "right"),
27032
27054
  "Shift+Tab": (ev) => this.processTabKey(ev, "left"),
27033
- "Ctrl+ ": this.processSpaceKey,
27034
27055
  };
27035
27056
  keyCodeMapping = {
27036
27057
  NumpadDecimal: this.processNumpadDecimal,
@@ -27038,12 +27059,12 @@ class Composer extends Component {
27038
27059
  setup() {
27039
27060
  onMounted(() => {
27040
27061
  const el = this.composerRef.el;
27062
+ if (this.props.isDefaultFocus) {
27063
+ this.env.focusableElement.setFocusableElement(el);
27064
+ }
27041
27065
  this.contentHelper.updateEl(el);
27042
27066
  this.processTokenAtCursor();
27043
27067
  });
27044
- onWillUnmount(() => {
27045
- this.props.onComposerUnmounted?.();
27046
- });
27047
27068
  useEffect(() => {
27048
27069
  this.processContent();
27049
27070
  });
@@ -27052,7 +27073,8 @@ class Composer extends Component {
27052
27073
  // Handlers
27053
27074
  // ---------------------------------------------------------------------------
27054
27075
  processArrowKeys(ev) {
27055
- if (this.env.model.getters.isSelectingForComposer()) {
27076
+ if (this.env.model.getters.isSelectingForComposer() ||
27077
+ this.env.model.getters.getEditionMode() === "inactive") {
27056
27078
  this.functionDescriptionState.showDescription = false;
27057
27079
  // Prevent the default content editable behavior which moves the cursor
27058
27080
  ev.preventDefault();
@@ -27095,23 +27117,19 @@ class Composer extends Component {
27095
27117
  processTabKey(ev, direction) {
27096
27118
  ev.preventDefault();
27097
27119
  ev.stopPropagation();
27098
- const state = this.autoCompleteState;
27099
- if (state.showProvider && state.selectedIndex !== undefined) {
27100
- const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
27101
- if (autoCompleteValue) {
27102
- this.autoComplete(autoCompleteValue);
27103
- return;
27120
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
27121
+ const state = this.autoCompleteState;
27122
+ if (state.showProvider && state.selectedIndex !== undefined) {
27123
+ const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
27124
+ if (autoCompleteValue) {
27125
+ this.autoComplete(autoCompleteValue);
27126
+ return;
27127
+ }
27104
27128
  }
27129
+ interactiveStopEdition(this.env);
27105
27130
  }
27106
- interactiveStopEdition(this.env);
27107
27131
  this.env.model.selection.moveAnchorCell(direction, 1);
27108
27132
  }
27109
- processSpaceKey(ev) {
27110
- ev.preventDefault();
27111
- ev.stopPropagation();
27112
- this.showFunctionAutocomplete("");
27113
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27114
- }
27115
27133
  processEnterKey(ev, direction) {
27116
27134
  ev.preventDefault();
27117
27135
  ev.stopPropagation();
@@ -27173,6 +27191,9 @@ class Composer extends Component {
27173
27191
  this.compositionActive = false;
27174
27192
  }
27175
27193
  onKeydown(ev) {
27194
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27195
+ return;
27196
+ }
27176
27197
  if (ev.key.startsWith("Arrow")) {
27177
27198
  this.processArrowKeys(ev);
27178
27199
  return;
@@ -27186,14 +27207,33 @@ class Composer extends Component {
27186
27207
  ev.stopPropagation();
27187
27208
  }
27188
27209
  }
27210
+ onPaste(ev) {
27211
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
27212
+ ev.stopPropagation();
27213
+ }
27214
+ }
27189
27215
  /*
27190
27216
  * Triggered automatically by the content-editable between the keydown and key up
27191
27217
  * */
27192
27218
  onInput(ev) {
27193
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
27219
+ if (!this.shouldProcessInputEvents) {
27220
+ return;
27221
+ }
27222
+ if (ev.inputType === "insertFromPaste" &&
27223
+ this.env.model.getters.getEditionMode() === "inactive") {
27194
27224
  return;
27195
27225
  }
27196
- let content = this.contentHelper.getText();
27226
+ ev.stopPropagation();
27227
+ let content;
27228
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27229
+ content = ev.data || "";
27230
+ }
27231
+ else {
27232
+ content = this.contentHelper.getText();
27233
+ }
27234
+ if (this.props.focus === "inactive") {
27235
+ return this.props.onComposerCellFocused?.(content);
27236
+ }
27197
27237
  let selection = this.contentHelper.getCurrentSelection();
27198
27238
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27199
27239
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
@@ -27269,9 +27309,8 @@ class Composer extends Component {
27269
27309
  }
27270
27310
  const newSelection = this.contentHelper.getCurrentSelection();
27271
27311
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27272
- if (this.props.focus === "inactive") {
27273
- this.props.onComposerContentFocused(newSelection);
27274
- }
27312
+ this.props.onComposerContentFocused();
27313
+ if (this.props.focus === "inactive") ;
27275
27314
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
27276
27315
  this.processTokenAtCursor();
27277
27316
  }
@@ -27438,7 +27477,7 @@ class Composer extends Component {
27438
27477
  const highlights = this.env.model.getters.getHighlights();
27439
27478
  const refSheet = sheetName
27440
27479
  ? this.env.model.getters.getSheetIdByName(sheetName)
27441
- : this.env.model.getters.getCurrentEditedCell().sheetId;
27480
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
27442
27481
  const highlight = highlights.find((highlight) => {
27443
27482
  if (highlight.sheetId !== refSheet)
27444
27483
  return false;
@@ -27565,56 +27604,30 @@ class GridComposer extends Component {
27565
27604
  focus: {
27566
27605
  validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
27567
27606
  },
27568
- onComposerUnmounted: Function,
27607
+ onComposerCellFocused: Function,
27569
27608
  onComposerContentFocused: Function,
27570
27609
  gridDims: Object,
27571
27610
  };
27572
27611
  static components = { Composer };
27573
- gridComposerRef;
27574
- zone;
27575
- rect;
27612
+ rect = this.defaultRect;
27613
+ isEditing = false;
27576
27614
  isCellReferenceVisible;
27577
- composerState;
27615
+ get defaultRect() {
27616
+ return { x: 0, y: 0, width: 0, height: 0 };
27617
+ }
27578
27618
  setup() {
27579
- this.gridComposerRef = useRef("gridComposer");
27580
- this.composerState = useState({
27581
- rect: undefined,
27582
- delimitation: undefined,
27583
- });
27584
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
27585
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
27586
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
27587
- this.isCellReferenceVisible = false;
27588
- onMounted(() => {
27589
- const el = this.gridComposerRef.el;
27590
- this.composerState.rect = {
27591
- x: this.rect.x,
27592
- y: this.rect.y,
27593
- width: el.clientWidth,
27594
- height: el.clientHeight,
27595
- };
27596
- this.composerState.delimitation = {
27597
- width: this.props.gridDims.width,
27598
- height: this.props.gridDims.height,
27599
- };
27600
- });
27601
27619
  onWillUpdateProps(() => {
27602
- if (this.isCellReferenceVisible) {
27603
- return;
27604
- }
27605
- const sheetId = this.env.model.getters.getActiveSheetId();
27606
- const zone = this.env.model.getters.getSelectedZone();
27607
- const rect = this.env.model.getters.getVisibleRect(zone);
27608
- if (!deepEquals(rect, this.rect) ||
27609
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27610
- this.isCellReferenceVisible = true;
27611
- }
27620
+ this.updateComponentPosition();
27621
+ this.updateCellReferenceVisibility();
27612
27622
  });
27613
27623
  }
27614
27624
  get shouldDisplayCellReference() {
27615
27625
  return this.isCellReferenceVisible;
27616
27626
  }
27617
27627
  get cellReference() {
27628
+ if (!this.env.model.getters.getCurrentEditedCell()) {
27629
+ return "";
27630
+ }
27618
27631
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27619
27632
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27620
27633
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
@@ -27626,7 +27639,27 @@ class GridComposer extends Component {
27626
27639
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
27627
27640
  });
27628
27641
  }
27642
+ get composerProps() {
27643
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27644
+ return {
27645
+ rect: { ...this.rect },
27646
+ delimitation: {
27647
+ width,
27648
+ height,
27649
+ },
27650
+ focus: this.props.focus,
27651
+ isDefaultFocus: true,
27652
+ onComposerContentFocused: this.props.onComposerContentFocused,
27653
+ onComposerCellFocused: this.props.onComposerCellFocused,
27654
+ };
27655
+ }
27629
27656
  get containerStyle() {
27657
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27658
+ return `
27659
+ position: absolute;
27660
+ z-index: -1000;
27661
+ `;
27662
+ }
27630
27663
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27631
27664
  const cell = this.env.model.getters.getActiveCell();
27632
27665
  const position = this.env.model.getters.getActivePosition();
@@ -27646,6 +27679,8 @@ class GridComposer extends Component {
27646
27679
  if (!isFormula) {
27647
27680
  textAlign = style.align || cell.defaultAlign;
27648
27681
  }
27682
+ const maxHeight = this.props.gridDims.height - this.rect.y;
27683
+ const maxWidth = this.props.gridDims.width - this.rect.x;
27649
27684
  /**
27650
27685
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
27651
27686
  * including all the paddings/margins of the composer
@@ -27657,6 +27692,8 @@ class GridComposer extends Component {
27657
27692
  top: `${top}px`,
27658
27693
  "min-width": `${width + 1}px`,
27659
27694
  "min-height": `${height + 1}px`,
27695
+ "max-width": `${maxWidth}px`,
27696
+ "max-height": `${maxHeight}px`,
27660
27697
  background,
27661
27698
  color,
27662
27699
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -27666,13 +27703,31 @@ class GridComposer extends Component {
27666
27703
  "text-align": textAlign,
27667
27704
  });
27668
27705
  }
27669
- get composerStyle() {
27670
- const maxHeight = this.props.gridDims.height - this.rect.y;
27671
- const maxWidth = this.props.gridDims.width - this.rect.x;
27672
- return cssPropertiesToCss({
27673
- "max-width": `${maxWidth}px`,
27674
- "max-height": `${maxHeight}px`,
27675
- });
27706
+ updateComponentPosition() {
27707
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
27708
+ if (this.isEditing !== isEditing) {
27709
+ this.isEditing = isEditing;
27710
+ if (!isEditing) {
27711
+ this.rect = this.defaultRect;
27712
+ this.env.focusableElement.focus();
27713
+ return;
27714
+ }
27715
+ const position = this.env.model.getters.getActivePosition();
27716
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
27717
+ this.rect = this.env.model.getters.getVisibleRect(zone);
27718
+ }
27719
+ }
27720
+ updateCellReferenceVisibility() {
27721
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
27722
+ return;
27723
+ }
27724
+ const sheetId = this.env.model.getters.getActiveSheetId();
27725
+ const zone = this.env.model.getters.getSelectedZone();
27726
+ const rect = this.env.model.getters.getVisibleRect(zone);
27727
+ if (!deepEquals(rect, this.rect) ||
27728
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27729
+ this.isCellReferenceVisible = true;
27730
+ }
27676
27731
  }
27677
27732
  }
27678
27733
 
@@ -28772,7 +28827,6 @@ class GridOverlay extends Component {
28772
28827
  this.props.onCellDoubleClicked(col, row);
28773
28828
  }
28774
28829
  onContextMenu(ev) {
28775
- ev.preventDefault();
28776
28830
  const [col, row] = this.getCartesianCoordinates(ev);
28777
28831
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
28778
28832
  }
@@ -29837,7 +29891,6 @@ class Grid extends Component {
29837
29891
  HEADER_WIDTH = HEADER_WIDTH;
29838
29892
  menuState;
29839
29893
  gridRef;
29840
- hiddenInput;
29841
29894
  onMouseWheel;
29842
29895
  canvasPosition;
29843
29896
  hoveredCell;
@@ -29848,15 +29901,14 @@ class Grid extends Component {
29848
29901
  menuItems: [],
29849
29902
  });
29850
29903
  this.gridRef = useRef("grid");
29851
- this.hiddenInput = useRef("hiddenInput");
29852
29904
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
29853
29905
  this.hoveredCell = useState({ col: undefined, row: undefined });
29854
29906
  useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
29855
29907
  useExternalListener(document.body, "cut", this.copy.bind(this, true));
29856
29908
  useExternalListener(document.body, "copy", this.copy.bind(this, false));
29857
29909
  useExternalListener(document.body, "paste", this.paste);
29858
- onMounted(() => this.focus());
29859
- this.props.exposeFocus(() => this.focus());
29910
+ onMounted(() => this.focusDefaultElement());
29911
+ this.props.exposeFocus(() => this.focusDefaultElement());
29860
29912
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29861
29913
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29862
29914
  this.moveCanvas(deltaX, deltaY);
@@ -29880,7 +29932,7 @@ class Grid extends Component {
29880
29932
  if (this.env.model.getters.hasOpenedPopover()) {
29881
29933
  this.closeOpenedPopover();
29882
29934
  }
29883
- this.focus();
29935
+ this.focusDefaultElement();
29884
29936
  }
29885
29937
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
29886
29938
  // down itself
@@ -30059,10 +30111,10 @@ class Grid extends Component {
30059
30111
  "Alt+Shift+ArrowUp": () => this.processHeaderGroupingKey("up"),
30060
30112
  "Alt+Shift+ArrowDown": () => this.processHeaderGroupingKey("down"),
30061
30113
  };
30062
- focus() {
30114
+ focusDefaultElement() {
30063
30115
  if (!this.env.model.getters.getSelectedFigureId() &&
30064
30116
  this.env.model.getters.getEditionMode() === "inactive") {
30065
- this.hiddenInput.el?.focus();
30117
+ this.env.focusableElement.focus();
30066
30118
  }
30067
30119
  }
30068
30120
  get gridEl() {
@@ -30198,19 +30250,6 @@ class Grid extends Component {
30198
30250
  return;
30199
30251
  }
30200
30252
  }
30201
- onInput(ev) {
30202
- // the user meant to paste in the sheet, not open the composer with the pasted content
30203
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
30204
- return;
30205
- }
30206
- if (ev.data) {
30207
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
30208
- // character
30209
- ev.preventDefault();
30210
- ev.stopPropagation();
30211
- this.props.onGridComposerCellFocused(ev.data);
30212
- }
30213
- }
30214
30253
  // ---------------------------------------------------------------------------
30215
30254
  // Context Menu
30216
30255
  // ---------------------------------------------------------------------------
@@ -30325,7 +30364,7 @@ class Grid extends Component {
30325
30364
  }
30326
30365
  closeMenu() {
30327
30366
  this.menuState.isOpen = false;
30328
- this.focus();
30367
+ this.focusDefaultElement();
30329
30368
  }
30330
30369
  processHeaderGroupingKey(direction) {
30331
30370
  if (this.env.model.getters.getSelectedZones().length !== 1) {
@@ -34242,6 +34281,31 @@ const MIGRATIONS = [
34242
34281
  return data;
34243
34282
  },
34244
34283
  },
34284
+ {
34285
+ description: "Fix datafilter duplication",
34286
+ from: 12,
34287
+ to: 12.5,
34288
+ applyMigration(data) {
34289
+ for (let sheet of data.sheets || []) {
34290
+ let knownDataFilterZones = [];
34291
+ for (let filterTable of sheet.filterTables || []) {
34292
+ const zone = toZone(filterTable.range);
34293
+ // See commit message for the details
34294
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
34295
+ if (intersectZoneIndex !== -1) {
34296
+ knownDataFilterZones[intersectZoneIndex] = zone;
34297
+ }
34298
+ else {
34299
+ knownDataFilterZones.push(zone);
34300
+ }
34301
+ }
34302
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
34303
+ range: zoneToXc(zone),
34304
+ }));
34305
+ }
34306
+ return data;
34307
+ },
34308
+ },
34245
34309
  {
34246
34310
  description: "Change Border description structure",
34247
34311
  from: 12,
@@ -36071,12 +36135,12 @@ class CellPlugin extends CorePlugin {
36071
36135
  /*
36072
36136
  * Reconstructs the original formula string based on a normalized form and its dependencies
36073
36137
  */
36074
- getFormulaCellContent(sheetId, compiledFormula, dependencies) {
36138
+ getFormulaCellContent(sheetId, compiledFormula, dependencies, useFixedReference = false) {
36075
36139
  let rangeIndex = 0;
36076
36140
  return concat(compiledFormula.tokens.map((token) => {
36077
36141
  if (token.type === "REFERENCE") {
36078
36142
  const range = dependencies[rangeIndex++];
36079
- return this.getters.getRangeString(range, sheetId);
36143
+ return this.getters.getRangeString(range, sheetId, { useFixedReference });
36080
36144
  }
36081
36145
  return token.value;
36082
36146
  }));
@@ -36279,8 +36343,11 @@ class CellPlugin extends CorePlugin {
36279
36343
  * being a computed property to rebuild the dependencies XC.
36280
36344
  */
36281
36345
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
36282
- const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
36283
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
36346
+ const dependencies = [];
36347
+ for (const xc of compiledFormula.dependencies) {
36348
+ dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36349
+ }
36350
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
36284
36351
  }
36285
36352
  createErrorFormula(id, content, format, style, error) {
36286
36353
  return {
@@ -36334,7 +36401,7 @@ class FormulaCellWithDependencies {
36334
36401
  const tokens = compiledFormula.tokens.map((token) => {
36335
36402
  if (token.type === "REFERENCE") {
36336
36403
  const index = rangeIndex++;
36337
- return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
36404
+ return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
36338
36405
  }
36339
36406
  return token;
36340
36407
  });
@@ -36347,15 +36414,34 @@ class FormulaCellWithDependencies {
36347
36414
  get content() {
36348
36415
  return concat(this.compiledFormula.tokens.map((token) => token.value));
36349
36416
  }
36417
+ get contentWithFixedReferences() {
36418
+ let rangeIndex = 0;
36419
+ return concat(this.compiledFormula.tokens.map((token) => {
36420
+ if (token.type === "REFERENCE") {
36421
+ const index = rangeIndex++;
36422
+ return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
36423
+ useFixedReference: true,
36424
+ });
36425
+ }
36426
+ return token.value;
36427
+ }));
36428
+ }
36350
36429
  }
36351
36430
  class RangeReferenceToken {
36352
- getValue;
36431
+ ranges;
36432
+ rangeIndex;
36433
+ sheetId;
36434
+ getRangeString;
36353
36435
  type = "REFERENCE";
36354
- constructor(getValue) {
36355
- this.getValue = getValue;
36436
+ constructor(ranges, rangeIndex, sheetId, getRangeString) {
36437
+ this.ranges = ranges;
36438
+ this.rangeIndex = rangeIndex;
36439
+ this.sheetId = sheetId;
36440
+ this.getRangeString = getRangeString;
36356
36441
  }
36357
36442
  get value() {
36358
- return this.getValue();
36443
+ const range = this.ranges[this.rangeIndex];
36444
+ return this.getRangeString(range, this.sheetId);
36359
36445
  }
36360
36446
  }
36361
36447
 
@@ -36651,7 +36737,13 @@ class ConditionalFormatPlugin extends CorePlugin {
36651
36737
  }
36652
36738
  }
36653
36739
  exportForExcel(data) {
36654
- this.export(data);
36740
+ if (data.sheets) {
36741
+ for (let sheet of data.sheets) {
36742
+ if (this.cfRules[sheet.id]) {
36743
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
36744
+ }
36745
+ }
36746
+ }
36655
36747
  }
36656
36748
  // ---------------------------------------------------------------------------
36657
36749
  // Getters
@@ -36716,11 +36808,11 @@ class ConditionalFormatPlugin extends CorePlugin {
36716
36808
  // ---------------------------------------------------------------------------
36717
36809
  // Private
36718
36810
  // ---------------------------------------------------------------------------
36719
- mapToConditionalFormat(sheetId, cf) {
36811
+ mapToConditionalFormat(sheetId, cf, { useFixedReference } = { useFixedReference: false }) {
36720
36812
  return {
36721
36813
  ...cf,
36722
36814
  ranges: cf.ranges.map((range) => {
36723
- return this.getters.getRangeString(range, sheetId);
36815
+ return this.getters.getRangeString(range, sheetId, { useFixedReference });
36724
36816
  }),
36725
36817
  };
36726
36818
  }
@@ -38788,8 +38880,10 @@ class RangeAdapter {
38788
38880
  *
38789
38881
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
38790
38882
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
38883
+ * @param options
38884
+ * @param options.useFixedReference if true, the range will be returned with fixed row and column
38791
38885
  */
38792
- getRangeString(range, forSheetId) {
38886
+ getRangeString(range, forSheetId, options = { useFixedReference: false }) {
38793
38887
  if (!range) {
38794
38888
  return CellErrorType.InvalidReference;
38795
38889
  }
@@ -38816,7 +38910,7 @@ class RangeAdapter {
38816
38910
  if (prefixSheet && !sheetName) {
38817
38911
  return CellErrorType.InvalidReference;
38818
38912
  }
38819
- let rangeString = this.getRangePartString(rangeImpl, 0);
38913
+ let rangeString = this.getRangePartString(rangeImpl, 0, options);
38820
38914
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
38821
38915
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
38822
38916
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -38826,7 +38920,7 @@ class RangeAdapter {
38826
38920
  rangeImpl.parts[1].rowFixed ||
38827
38921
  rangeImpl.parts[1].colFixed) {
38828
38922
  rangeString += ":";
38829
- rangeString += this.getRangePartString(rangeImpl, 1);
38923
+ rangeString += this.getRangePartString(rangeImpl, 1, options);
38830
38924
  }
38831
38925
  }
38832
38926
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -38867,13 +38961,13 @@ class RangeAdapter {
38867
38961
  /**
38868
38962
  * Get a Xc string that represent a part of a range
38869
38963
  */
38870
- getRangePartString(range, part) {
38964
+ getRangePartString(range, part, options = { useFixedReference: false }) {
38871
38965
  const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
38872
38966
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
38873
38967
  const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
38874
38968
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
38875
38969
  let str = "";
38876
- if (range.isFullCol) {
38970
+ if (range.isFullCol && !options.useFixedReference) {
38877
38971
  if (part === 0 && range.unboundedZone.hasHeader) {
38878
38972
  str = colFixed + col + rowFixed + row;
38879
38973
  }
@@ -38881,7 +38975,7 @@ class RangeAdapter {
38881
38975
  str = colFixed + col;
38882
38976
  }
38883
38977
  }
38884
- else if (range.isFullRow) {
38978
+ else if (range.isFullRow && !options.useFixedReference) {
38885
38979
  if (part === 0 && range.unboundedZone.hasHeader) {
38886
38980
  str = colFixed + col + rowFixed + row;
38887
38981
  }
@@ -41882,7 +41976,13 @@ class EvaluationPlugin extends UIPlugin {
41882
41976
  const format = newFormat
41883
41977
  ? getItemId(newFormat, data.formats)
41884
41978
  : exportedCellData.format;
41885
- const content = !isExported ? newContent : exportedCellData.content;
41979
+ let content;
41980
+ if (formulaCell instanceof FormulaCellWithDependencies) {
41981
+ content = formulaCell.contentWithFixedReferences;
41982
+ }
41983
+ else {
41984
+ content = !isExported ? newContent : exportedCellData.content;
41985
+ }
41886
41986
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
41887
41987
  }
41888
41988
  }
@@ -51066,6 +51166,16 @@ class ImageProvider {
51066
51166
  }
51067
51167
  }
51068
51168
 
51169
+ class FocusableElement {
51170
+ focusableElement = undefined;
51171
+ setFocusableElement(element) {
51172
+ this.focusableElement = element;
51173
+ }
51174
+ focus() {
51175
+ this.focusableElement?.focus();
51176
+ }
51177
+ }
51178
+
51069
51179
  const RIPPLE_KEY_FRAMES = [
51070
51180
  { transform: "scale(0)" },
51071
51181
  { transform: "scale(0.8)", offset: 0.33 },
@@ -53220,6 +53330,7 @@ class Spreadsheet extends Component {
53220
53330
  toggleSidePanel: this.toggleSidePanel.bind(this),
53221
53331
  clipboard: this.env.clipboard || instantiateClipboard(),
53222
53332
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
53333
+ focusableElement: new FocusableElement(),
53223
53334
  });
53224
53335
  useEffect(() => {
53225
53336
  /**
@@ -53338,16 +53449,17 @@ class Spreadsheet extends Component {
53338
53449
  }
53339
53450
  this.composer.topBarFocus = "contentFocus";
53340
53451
  this.composer.gridFocusMode = "inactive";
53341
- this.setComposerContent({ selection } || {});
53452
+ this.setComposerContent({ selection });
53342
53453
  }
53343
- onGridComposerContentFocused() {
53454
+ onGridComposerContentFocused(selection) {
53344
53455
  if (this.model.getters.isReadonly()) {
53345
53456
  return;
53346
53457
  }
53347
53458
  this.composer.topBarFocus = "inactive";
53348
53459
  this.composer.gridFocusMode = "contentFocus";
53349
- this.setComposerContent({});
53460
+ this.setComposerContent({ selection });
53350
53461
  }
53462
+ // TODO: either both are defined or none of them. change those args to an object
53351
53463
  onGridComposerCellFocused(content, selection) {
53352
53464
  if (this.model.getters.isReadonly()) {
53353
53465
  return;
@@ -55233,12 +55345,7 @@ function addFormula(cell) {
55233
55345
  attrs.push(["t", "str"]);
55234
55346
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
55235
55347
  }
55236
- node = escapeXml /*xml*/ `
55237
- <f>
55238
- ${XlsxFormula}
55239
- </f>
55240
- ${cycle}
55241
- `;
55348
+ node = escapeXml /*xml*/ `<f>${XlsxFormula}</f>${cycle}`;
55242
55349
  return { attrs, node };
55243
55350
  }
55244
55351
  function addContent(content, sharedStrings, forceString = false) {
@@ -56058,11 +56165,10 @@ function addRows(construct, data, sheet) {
56058
56165
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
56059
56166
  }
56060
56167
  attributes.push(...additionalAttrs);
56061
- cellNodes.push(escapeXml /*xml*/ `
56062
- <c ${formatAttributes(attributes)}>
56063
- ${cellNode}
56064
- </c>
56065
- `);
56168
+ // prettier-ignore
56169
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
56170
+ ${cellNode}
56171
+ </c>`);
56066
56172
  }
56067
56173
  }
56068
56174
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -56658,7 +56764,6 @@ class Model extends EventBus {
56658
56764
  ...config,
56659
56765
  mode: config.mode || "normal",
56660
56766
  custom: config.custom || {},
56661
- defaultCurrencyFormat: config.defaultCurrencyFormat || "[$$]#,##0.00",
56662
56767
  external: this.setupExternalConfig(config.external || {}),
56663
56768
  transportService,
56664
56769
  client,
@@ -56696,6 +56801,7 @@ class Model extends EventBus {
56696
56801
  custom: this.config.custom,
56697
56802
  uiActions: this.config,
56698
56803
  session: this.session,
56804
+ defaultCurrencyFormat: this.config.defaultCurrencyFormat,
56699
56805
  };
56700
56806
  }
56701
56807
  // ---------------------------------------------------------------------------
@@ -57041,6 +57147,6 @@ const constants = {
57041
57147
  export { AbstractChart, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
57042
57148
 
57043
57149
 
57044
- __info__.version = "17.1.2";
57045
- __info__.date = "2024-01-29T13:54:42.679Z";
57046
- __info__.hash = "043a0d5";
57150
+ __info__.version = "17.1.4";
57151
+ __info__.date = "2024-02-09T13:09:13.430Z";
57152
+ __info__.hash = "16d3ece";