@odoo/o-spreadsheet 16.2.29 → 16.2.31

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.
@@ -1695,6 +1695,9 @@ function parseFormat(formatString) {
1695
1695
  function formatValue(value, format) {
1696
1696
  switch (typeof value) {
1697
1697
  case "string":
1698
+ if (value.includes('\\"')) {
1699
+ return value.replace(/\\"/g, '"');
1700
+ }
1698
1701
  return value;
1699
1702
  case "boolean":
1700
1703
  return value ? "TRUE" : "FALSE";
@@ -6275,14 +6278,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
6275
6278
  : undefined));
6276
6279
  }
6277
6280
  }
6278
- else if (zone.left === zone.right && zone.top === zone.bottom) {
6279
- // A single cell. If it's only the title, the dataset is not added.
6280
- if (!dataSetsHaveTitle) {
6281
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
6282
- }
6283
- }
6284
6281
  else {
6285
- /* 1 row or 1 column */
6282
+ /* 1 cell, 1 row or 1 column */
6286
6283
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
6287
6284
  ? {
6288
6285
  top: zone.top,
@@ -6332,8 +6329,10 @@ function toExcelDataset(getters, ds) {
6332
6329
  }
6333
6330
  const dataRange = ds.dataRange.clone({ zone: dataZone });
6334
6331
  return {
6335
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
6336
- range: getters.getRangeString(dataRange, "forceSheetReference"),
6332
+ label: ds.labelCell
6333
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", true)
6334
+ : undefined,
6335
+ range: getters.getRangeString(dataRange, "forceSheetReference", true),
6337
6336
  };
6338
6337
  }
6339
6338
  /**
@@ -6858,6 +6857,9 @@ class BarChart extends AbstractChart {
6858
6857
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
6859
6858
  return {
6860
6859
  ...this.getDefinition(),
6860
+ labelRange: this.labelRange
6861
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
6862
+ : undefined,
6861
6863
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
6862
6864
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
6863
6865
  dataSets,
@@ -7422,6 +7424,9 @@ class LineChart extends AbstractChart {
7422
7424
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
7423
7425
  return {
7424
7426
  ...this.getDefinition(),
7427
+ labelRange: this.labelRange
7428
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
7429
+ : undefined,
7425
7430
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
7426
7431
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
7427
7432
  dataSets,
@@ -7695,6 +7700,9 @@ class PieChart extends AbstractChart {
7695
7700
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
7696
7701
  return {
7697
7702
  ...this.getDefinition(),
7703
+ labelRange: this.labelRange
7704
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
7705
+ : undefined,
7698
7706
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
7699
7707
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
7700
7708
  verticalAxisPosition: "left",
@@ -9138,6 +9146,12 @@ const INSERT_LINK = (env) => {
9138
9146
  let { col, row } = env.model.getters.getActivePosition();
9139
9147
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
9140
9148
  };
9149
+ const INSERT_LINK_NAME = (env) => {
9150
+ const sheetId = env.model.getters.getActiveSheetId();
9151
+ const { col, row } = env.model.getters.getActivePosition();
9152
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
9153
+ return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
9154
+ };
9141
9155
  //------------------------------------------------------------------------------
9142
9156
  // Filters action
9143
9157
  //------------------------------------------------------------------------------
@@ -9275,7 +9289,7 @@ cellMenuRegistry
9275
9289
  action: DELETE_CELL_SHIFT_LEFT,
9276
9290
  })
9277
9291
  .add("insert_link", {
9278
- name: _lt("Insert link"),
9292
+ name: INSERT_LINK_NAME,
9279
9293
  separator: true,
9280
9294
  sequence: 150,
9281
9295
  action: INSERT_LINK,
@@ -19961,14 +19975,13 @@ class Composer extends owl.Component {
19961
19975
  setup() {
19962
19976
  owl.onMounted(() => {
19963
19977
  const el = this.composerRef.el;
19978
+ if (this.props.isDefaultFocus) {
19979
+ this.env.focusableElement.setFocusableElement(el);
19980
+ }
19964
19981
  this.contentHelper.updateEl(el);
19965
19982
  this.processContent();
19966
19983
  this.contentHelper.scrollSelectionIntoView();
19967
19984
  });
19968
- owl.onWillUnmount(() => {
19969
- var _a, _b;
19970
- (_b = (_a = this.props).onComposerUnmounted) === null || _b === void 0 ? void 0 : _b.call(_a);
19971
- });
19972
19985
  owl.onPatched(() => {
19973
19986
  if (!this.isKeyStillDown) {
19974
19987
  this.processContent();
@@ -19979,7 +19992,8 @@ class Composer extends owl.Component {
19979
19992
  // Handlers
19980
19993
  // ---------------------------------------------------------------------------
19981
19994
  processArrowKeys(ev) {
19982
- if (this.env.model.getters.isSelectingForComposer()) {
19995
+ if (this.env.model.getters.isSelectingForComposer() ||
19996
+ this.env.model.getters.getEditionMode() === "inactive") {
19983
19997
  this.functionDescriptionState.showDescription = false;
19984
19998
  // Prevent the default content editable behavior which moves the cursor
19985
19999
  ev.preventDefault();
@@ -20020,21 +20034,23 @@ class Composer extends owl.Component {
20020
20034
  var _a;
20021
20035
  ev.preventDefault();
20022
20036
  ev.stopPropagation();
20023
- if (this.autoCompleteState.showProvider) {
20024
- const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
20025
- if (autoCompleteValue) {
20026
- this.autoComplete(autoCompleteValue);
20027
- return;
20037
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
20038
+ if (this.autoCompleteState.showProvider) {
20039
+ const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
20040
+ if (autoCompleteValue) {
20041
+ this.autoComplete(autoCompleteValue);
20042
+ return;
20043
+ }
20028
20044
  }
20029
- }
20030
- else {
20031
- // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
20032
- // we can't let the model think that it is for a ref selection.
20033
- // todo: check if this can be removed someday
20034
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20045
+ else {
20046
+ // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
20047
+ // we can't let the model think that it is for a ref selection.
20048
+ // todo: check if this can be removed someday
20049
+ this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20050
+ }
20051
+ this.env.model.dispatch("STOP_EDITION");
20035
20052
  }
20036
20053
  const direction = ev.shiftKey ? "left" : "right";
20037
- this.env.model.dispatch("STOP_EDITION");
20038
20054
  this.env.model.selection.moveAnchorCell(direction, 1);
20039
20055
  }
20040
20056
  processEnterKey(ev) {
@@ -20079,6 +20095,9 @@ class Composer extends owl.Component {
20079
20095
  this.compositionActive = false;
20080
20096
  }
20081
20097
  onKeydown(ev) {
20098
+ if (this.env.model.getters.getEditionMode() === "inactive") {
20099
+ return;
20100
+ }
20082
20101
  let handler = this.keyMapping[ev.key];
20083
20102
  if (handler) {
20084
20103
  handler.call(this, ev);
@@ -20089,22 +20108,45 @@ class Composer extends owl.Component {
20089
20108
  }
20090
20109
  }
20091
20110
  updateCursorIfNeeded() {
20092
- if (!this.env.model.getters.isSelectingForComposer()) {
20111
+ const moveCursor = !this.env.model.getters.isSelectingForComposer() &&
20112
+ !(this.env.model.getters.getEditionMode() === "inactive");
20113
+ if (moveCursor) {
20093
20114
  const { start, end } = this.contentHelper.getCurrentSelection();
20094
20115
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
20095
20116
  this.isKeyStillDown = true;
20096
20117
  }
20097
20118
  }
20119
+ onPaste(ev) {
20120
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
20121
+ ev.stopPropagation();
20122
+ }
20123
+ }
20098
20124
  /*
20099
20125
  * Triggered automatically by the content-editable between the keydown and key up
20100
20126
  * */
20101
- onInput() {
20102
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
20127
+ onInput(ev) {
20128
+ var _a, _b;
20129
+ if (!this.shouldProcessInputEvents) {
20130
+ return;
20131
+ }
20132
+ if (ev.inputType === "insertFromPaste" &&
20133
+ this.env.model.getters.getEditionMode() === "inactive") {
20103
20134
  return;
20104
20135
  }
20136
+ ev.stopPropagation();
20137
+ let content;
20138
+ if (this.env.model.getters.getEditionMode() === "inactive") {
20139
+ content = ev.data || "";
20140
+ }
20141
+ else {
20142
+ content = this.contentHelper.getText();
20143
+ }
20144
+ if (this.props.focus === "inactive") {
20145
+ return (_b = (_a = this.props).onComposerCellFocused) === null || _b === void 0 ? void 0 : _b.call(_a, content);
20146
+ }
20105
20147
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20106
20148
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
20107
- content: this.contentHelper.getText(),
20149
+ content,
20108
20150
  selection: this.contentHelper.getCurrentSelection(),
20109
20151
  });
20110
20152
  }
@@ -20175,9 +20217,8 @@ class Composer extends owl.Component {
20175
20217
  }
20176
20218
  const newSelection = this.contentHelper.getCurrentSelection();
20177
20219
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20178
- if (this.props.focus === "inactive") {
20179
- this.props.onComposerContentFocused(newSelection);
20180
- }
20220
+ this.props.onComposerContentFocused();
20221
+ if (this.props.focus === "inactive") ;
20181
20222
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
20182
20223
  this.processTokenAtCursor();
20183
20224
  }
@@ -20401,14 +20442,16 @@ Composer.template = "o-spreadsheet-Composer";
20401
20442
  Composer.components = { TextValueProvider, FunctionDescriptionProvider };
20402
20443
  Composer.defaultProps = {
20403
20444
  inputStyle: "",
20445
+ isDefaultFocus: false,
20404
20446
  };
20405
20447
  Composer.props = {
20406
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
20407
- onComposerContentFocused: Function,
20408
20448
  inputStyle: { type: String, optional: true },
20409
20449
  rect: { type: Object, optional: true },
20410
20450
  delimitation: { type: Object, optional: true },
20411
- onComposerUnmounted: { type: Function, optional: true },
20451
+ focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
20452
+ onComposerCellFocused: { type: Function, optional: true },
20453
+ onComposerContentFocused: Function,
20454
+ isDefaultFocus: { type: Boolean, optional: true },
20412
20455
  };
20413
20456
 
20414
20457
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
@@ -20440,46 +20483,28 @@ css /* scss */ `
20440
20483
  * It also applies the style of the cell to the composer input.
20441
20484
  */
20442
20485
  class GridComposer extends owl.Component {
20486
+ constructor() {
20487
+ super(...arguments);
20488
+ // TODORAR see if we can keep it undefined
20489
+ this.rect = this.defaultRect;
20490
+ this.isEditing = false;
20491
+ }
20492
+ get defaultRect() {
20493
+ return { x: 0, y: 0, width: 0, height: 0 };
20494
+ }
20443
20495
  setup() {
20444
- this.gridComposerRef = owl.useRef("gridComposer");
20445
- this.composerState = owl.useState({
20446
- rect: undefined,
20447
- delimitation: undefined,
20448
- });
20449
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
20450
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
20451
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
20452
- this.isCellReferenceVisible = false;
20453
- owl.onMounted(() => {
20454
- const el = this.gridComposerRef.el;
20455
- this.composerState.rect = {
20456
- x: this.rect.x,
20457
- y: this.rect.y,
20458
- width: el.clientWidth,
20459
- height: el.clientHeight,
20460
- };
20461
- this.composerState.delimitation = {
20462
- width: el.parentElement.clientWidth,
20463
- height: el.parentElement.clientHeight,
20464
- };
20465
- });
20466
20496
  owl.onWillUpdateProps(() => {
20467
- if (this.isCellReferenceVisible) {
20468
- return;
20469
- }
20470
- const sheetId = this.env.model.getters.getActiveSheetId();
20471
- const zone = this.env.model.getters.getSelectedZone();
20472
- const rect = this.env.model.getters.getVisibleRect(zone);
20473
- if (!deepEquals(rect, this.rect) ||
20474
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
20475
- this.isCellReferenceVisible = true;
20476
- }
20497
+ this.updateComponentPosition();
20498
+ this.updateCellReferenceVisibility();
20477
20499
  });
20478
20500
  }
20479
20501
  get shouldDisplayCellReference() {
20480
20502
  return this.isCellReferenceVisible;
20481
20503
  }
20482
20504
  get cellReference() {
20505
+ if (!this.env.model.getters.getCurrentEditedCell()) {
20506
+ return "";
20507
+ }
20483
20508
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
20484
20509
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
20485
20510
  return `${prefixSheet ? getComposerSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
@@ -20491,7 +20516,27 @@ class GridComposer extends owl.Component {
20491
20516
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
20492
20517
  });
20493
20518
  }
20519
+ get composerProps() {
20520
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
20521
+ return {
20522
+ rect: { ...this.rect },
20523
+ delimitation: {
20524
+ width,
20525
+ height,
20526
+ },
20527
+ focus: this.props.focus,
20528
+ isDefaultFocus: true,
20529
+ onComposerContentFocused: this.props.onComposerContentFocused,
20530
+ onComposerCellFocused: this.props.onComposerCellFocused,
20531
+ };
20532
+ }
20494
20533
  get containerStyle() {
20534
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
20535
+ return `
20536
+ position: absolute;
20537
+ z-index: -1000;
20538
+ `;
20539
+ }
20495
20540
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
20496
20541
  const cell = this.env.model.getters.getActiveCell();
20497
20542
  const position = this.env.model.getters.getActivePosition();
@@ -20511,6 +20556,8 @@ class GridComposer extends owl.Component {
20511
20556
  if (!isFormula) {
20512
20557
  textAlign = style.align || cell.defaultAlign;
20513
20558
  }
20559
+ const maxHeight = this.props.gridDims.height - this.rect.y;
20560
+ const maxWidth = this.props.gridDims.width - this.rect.x;
20514
20561
  /**
20515
20562
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
20516
20563
  * including all the paddings/margins of the composer
@@ -20522,6 +20569,8 @@ class GridComposer extends owl.Component {
20522
20569
  top: `${top}px`,
20523
20570
  "min-width": `${width + 1}px`,
20524
20571
  "min-height": `${height + 1}px`,
20572
+ "max-width": `${maxWidth}px`,
20573
+ "max-height": `${maxHeight}px`,
20525
20574
  background,
20526
20575
  color,
20527
20576
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -20531,22 +20580,40 @@ class GridComposer extends owl.Component {
20531
20580
  "text-align": textAlign,
20532
20581
  });
20533
20582
  }
20534
- get composerStyle() {
20535
- const maxHeight = this.props.gridDims.height - this.rect.y;
20536
- const maxWidth = this.props.gridDims.width - this.rect.x;
20537
- return cssPropertiesToCss({
20538
- "max-width": `${maxWidth}px`,
20539
- "max-height": `${maxHeight}px`,
20540
- });
20583
+ updateComponentPosition() {
20584
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
20585
+ if (this.isEditing !== isEditing) {
20586
+ this.isEditing = isEditing;
20587
+ if (!isEditing) {
20588
+ this.rect = this.defaultRect;
20589
+ this.env.focusableElement.focus();
20590
+ return;
20591
+ }
20592
+ const position = this.env.model.getters.getActivePosition();
20593
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
20594
+ this.rect = this.env.model.getters.getVisibleRect(zone);
20595
+ }
20596
+ }
20597
+ updateCellReferenceVisibility() {
20598
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
20599
+ return;
20600
+ }
20601
+ const sheetId = this.env.model.getters.getActiveSheetId();
20602
+ const zone = this.env.model.getters.getSelectedZone();
20603
+ const rect = this.env.model.getters.getVisibleRect(zone);
20604
+ if (!deepEquals(rect, this.rect) ||
20605
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
20606
+ this.isCellReferenceVisible = true;
20607
+ }
20541
20608
  }
20542
20609
  }
20543
20610
  GridComposer.template = "o-spreadsheet-GridComposer";
20544
20611
  GridComposer.components = { Composer };
20545
20612
  GridComposer.props = {
20546
20613
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
20547
- onComposerUnmounted: Function,
20548
20614
  onComposerContentFocused: Function,
20549
20615
  gridDims: Object,
20616
+ onComposerCellFocused: Function,
20550
20617
  };
20551
20618
 
20552
20619
  const CSS$1 = css /* scss */ `
@@ -21137,7 +21204,6 @@ class GridOverlay extends owl.Component {
21137
21204
  this.props.onCellDoubleClicked(col, row);
21138
21205
  }
21139
21206
  onContextMenu(ev) {
21140
- ev.preventDefault();
21141
21207
  const [col, row] = this.getCartesianCoordinates(ev);
21142
21208
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
21143
21209
  }
@@ -22364,17 +22430,16 @@ class Grid extends owl.Component {
22364
22430
  menuItems: [],
22365
22431
  });
22366
22432
  this.gridRef = owl.useRef("grid");
22367
- this.hiddenInput = owl.useRef("hiddenInput");
22368
22433
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
22369
22434
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
22370
22435
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
22371
22436
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
22372
22437
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
22373
22438
  owl.useExternalListener(document.body, "paste", this.paste);
22374
- owl.onMounted(() => this.focus());
22375
- this.props.exposeFocus(() => this.focus());
22439
+ owl.onMounted(() => this.focusDefaultElement());
22440
+ this.props.exposeFocus(() => this.focusDefaultElement());
22376
22441
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
22377
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
22442
+ owl.useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
22378
22443
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
22379
22444
  this.moveCanvas(deltaX, deltaY);
22380
22445
  this.hoveredCell.col = undefined;
@@ -22397,13 +22462,12 @@ class Grid extends owl.Component {
22397
22462
  if (this.env.model.getters.hasOpenedPopover()) {
22398
22463
  this.closeOpenedPopover();
22399
22464
  }
22400
- this.focus();
22465
+ this.focusDefaultElement();
22401
22466
  }
22402
- focus() {
22403
- var _a;
22467
+ focusDefaultElement() {
22404
22468
  if (!this.env.model.getters.getSelectedFigureId() &&
22405
22469
  this.env.model.getters.getEditionMode() === "inactive") {
22406
- (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
22470
+ this.env.focusableElement.focus();
22407
22471
  }
22408
22472
  }
22409
22473
  get gridEl() {
@@ -22548,19 +22612,6 @@ class Grid extends owl.Component {
22548
22612
  return;
22549
22613
  }
22550
22614
  }
22551
- onInput(ev) {
22552
- // the user meant to paste in the sheet, not open the composer with the pasted content
22553
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
22554
- return;
22555
- }
22556
- if (ev.data) {
22557
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
22558
- // character
22559
- ev.preventDefault();
22560
- ev.stopPropagation();
22561
- this.props.onGridComposerCellFocused(ev.data);
22562
- }
22563
- }
22564
22615
  // ---------------------------------------------------------------------------
22565
22616
  // Context Menu
22566
22617
  // ---------------------------------------------------------------------------
@@ -22657,7 +22708,7 @@ class Grid extends owl.Component {
22657
22708
  }
22658
22709
  closeMenu() {
22659
22710
  this.menuState.isOpen = false;
22660
- this.focus();
22711
+ this.focusDefaultElement();
22661
22712
  }
22662
22713
  }
22663
22714
  Grid.template = "o-spreadsheet-Grid";
@@ -27009,7 +27060,7 @@ function normalizeV9(formula) {
27009
27060
  * a breaking change is made in the way the state is handled, and an upgrade
27010
27061
  * function should be defined
27011
27062
  */
27012
- const CURRENT_VERSION = 12;
27063
+ const CURRENT_VERSION = 12.5;
27013
27064
  const INITIAL_SHEET_ID = "Sheet1";
27014
27065
  /**
27015
27066
  * This function tries to load anything that could look like a valid
@@ -27275,6 +27326,31 @@ const MIGRATIONS = [
27275
27326
  return data;
27276
27327
  },
27277
27328
  },
27329
+ {
27330
+ description: "Fix datafilter duplication",
27331
+ from: 12,
27332
+ to: 12.5,
27333
+ applyMigration(data) {
27334
+ for (let sheet of data.sheets || []) {
27335
+ let knownDataFilterZones = [];
27336
+ for (let filterTable of sheet.filterTables || []) {
27337
+ const zone = toZone(filterTable.range);
27338
+ // See commit message for the details
27339
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
27340
+ if (intersectZoneIndex !== -1) {
27341
+ knownDataFilterZones[intersectZoneIndex] = zone;
27342
+ }
27343
+ else {
27344
+ knownDataFilterZones.push(zone);
27345
+ }
27346
+ }
27347
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
27348
+ range: zoneToXc(zone),
27349
+ }));
27350
+ }
27351
+ return data;
27352
+ },
27353
+ },
27278
27354
  ];
27279
27355
  /**
27280
27356
  * This function is used to repair faulty data independently of the migration.
@@ -28295,13 +28371,13 @@ class CellPlugin extends CorePlugin {
28295
28371
  /*
28296
28372
  * Reconstructs the original formula string based on a normalized form and its dependencies
28297
28373
  */
28298
- buildFormulaContent(sheetId, cell, dependencies) {
28374
+ buildFormulaContent(sheetId, cell, dependencies, useFixedZone = false) {
28299
28375
  const ranges = dependencies || cell.dependencies;
28300
28376
  let rangeIndex = 0;
28301
28377
  return concat(cell.compiledFormula.tokens.map((token) => {
28302
28378
  if (token.type === "REFERENCE") {
28303
28379
  const range = ranges[rangeIndex++];
28304
- return this.getters.getRangeString(range, sheetId);
28380
+ return this.getters.getRangeString(range, sheetId, useFixedZone);
28305
28381
  }
28306
28382
  return token.value;
28307
28383
  }));
@@ -28871,7 +28947,13 @@ class ConditionalFormatPlugin extends CorePlugin {
28871
28947
  }
28872
28948
  }
28873
28949
  exportForExcel(data) {
28874
- this.export(data);
28950
+ if (data.sheets) {
28951
+ for (let sheet of data.sheets) {
28952
+ if (this.cfRules[sheet.id]) {
28953
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
28954
+ }
28955
+ }
28956
+ }
28875
28957
  }
28876
28958
  // ---------------------------------------------------------------------------
28877
28959
  // Getters
@@ -28921,11 +29003,11 @@ class ConditionalFormatPlugin extends CorePlugin {
28921
29003
  // ---------------------------------------------------------------------------
28922
29004
  // Private
28923
29005
  // ---------------------------------------------------------------------------
28924
- mapToConditionalFormat(sheetId, cf) {
29006
+ mapToConditionalFormat(sheetId, cf, options = { useFixedReference: false }) {
28925
29007
  return {
28926
29008
  ...cf,
28927
29009
  ranges: cf.ranges.map((range) => {
28928
- return this.getters.getRangeString(range, sheetId);
29010
+ return this.getters.getRangeString(range, sheetId, options.useFixedReference);
28929
29011
  }),
28930
29012
  };
28931
29013
  }
@@ -30854,8 +30936,9 @@ class RangeAdapter {
30854
30936
  *
30855
30937
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
30856
30938
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
30939
+ * @param [useFixedZone=false] if true, the range will be returned with fixed row and column
30857
30940
  */
30858
- getRangeString(range, forSheetId) {
30941
+ getRangeString(range, forSheetId, useFixedZone = false) {
30859
30942
  if (!range) {
30860
30943
  return INCORRECT_RANGE_STRING;
30861
30944
  }
@@ -30882,7 +30965,7 @@ class RangeAdapter {
30882
30965
  if (prefixSheet && !sheetName) {
30883
30966
  return INCORRECT_RANGE_STRING;
30884
30967
  }
30885
- let rangeString = this.getRangePartString(rangeImpl, 0);
30968
+ let rangeString = this.getRangePartString(rangeImpl, 0, useFixedZone);
30886
30969
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
30887
30970
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
30888
30971
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -30892,7 +30975,7 @@ class RangeAdapter {
30892
30975
  rangeImpl.parts[1].rowFixed ||
30893
30976
  rangeImpl.parts[1].colFixed) {
30894
30977
  rangeString += ":";
30895
- rangeString += this.getRangePartString(rangeImpl, 1);
30978
+ rangeString += this.getRangePartString(rangeImpl, 1, useFixedZone);
30896
30979
  }
30897
30980
  }
30898
30981
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -30922,14 +31005,14 @@ class RangeAdapter {
30922
31005
  /**
30923
31006
  * Get a Xc string that represent a part of a range
30924
31007
  */
30925
- getRangePartString(range, part) {
31008
+ getRangePartString(range, part, useFixedZone = false) {
30926
31009
  var _a, _b;
30927
31010
  const colFixed = range.parts && ((_a = range.parts[part]) === null || _a === void 0 ? void 0 : _a.colFixed) ? "$" : "";
30928
31011
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
30929
31012
  const rowFixed = range.parts && ((_b = range.parts[part]) === null || _b === void 0 ? void 0 : _b.rowFixed) ? "$" : "";
30930
31013
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
30931
31014
  let str = "";
30932
- if (range.isFullCol) {
31015
+ if (range.isFullCol && !useFixedZone) {
30933
31016
  if (part === 0 && range.unboundedZone.hasHeader) {
30934
31017
  str = colFixed + col + rowFixed + row;
30935
31018
  }
@@ -30937,7 +31020,7 @@ class RangeAdapter {
30937
31020
  str = colFixed + col;
30938
31021
  }
30939
31022
  }
30940
- else if (range.isFullRow) {
31023
+ else if (range.isFullRow && !useFixedZone) {
30941
31024
  if (part === 0 && range.unboundedZone.hasHeader) {
30942
31025
  str = colFixed + col + rowFixed + row;
30943
31026
  }
@@ -32326,6 +32409,10 @@ class EvaluationPlugin extends UIPlugin {
32326
32409
  if (cell.format !== evaluatedCell.format) {
32327
32410
  exportedCellData.computedFormat = evaluatedCell.format;
32328
32411
  }
32412
+ // if there is a formula but no dependencies (maybe because the cell is in error), no need to recompute the formula text
32413
+ if (cell.isFormula && cell.dependencies.length) {
32414
+ exportedCellData.content = this.getters.buildFormulaContent(sheet.id, cell, cell.dependencies, true);
32415
+ }
32329
32416
  }
32330
32417
  }
32331
32418
  }
@@ -39917,6 +40004,19 @@ class ImageProvider {
39917
40004
  }
39918
40005
  }
39919
40006
 
40007
+ class FocusableElement {
40008
+ constructor() {
40009
+ this.focusableElement = undefined;
40010
+ }
40011
+ setFocusableElement(element) {
40012
+ this.focusableElement = element;
40013
+ }
40014
+ focus() {
40015
+ var _a;
40016
+ (_a = this.focusableElement) === null || _a === void 0 ? void 0 : _a.focus();
40017
+ }
40018
+ }
40019
+
39920
40020
  const RIPPLE_KEY_FRAMES = [
39921
40021
  { transform: "scale(0)" },
39922
40022
  { transform: "scale(0.8)", offset: 0.33 },
@@ -41892,6 +41992,7 @@ class Spreadsheet extends owl.Component {
41892
41992
  _t: Spreadsheet._t,
41893
41993
  clipboard: this.env.clipboard || instantiateClipboard(),
41894
41994
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
41995
+ focusableElement: new FocusableElement(),
41895
41996
  });
41896
41997
  owl.useExternalListener(window, "resize", () => this.render(true));
41897
41998
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -41990,17 +42091,18 @@ class Spreadsheet extends owl.Component {
41990
42091
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
41991
42092
  this.composer.topBarFocus = "contentFocus";
41992
42093
  this.composer.gridFocusMode = "inactive";
41993
- this.setComposerContent({ selection } || {});
42094
+ this.setComposerContent({ selection });
41994
42095
  }
41995
- onGridComposerContentFocused() {
42096
+ onGridComposerContentFocused(selection) {
41996
42097
  if (this.model.getters.isReadonly()) {
41997
42098
  return;
41998
42099
  }
41999
42100
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
42000
42101
  this.composer.topBarFocus = "inactive";
42001
42102
  this.composer.gridFocusMode = "contentFocus";
42002
- this.setComposerContent({});
42103
+ this.setComposerContent({ selection });
42003
42104
  }
42105
+ // TODO: either both are defined or none of them. change those args to an object
42004
42106
  onGridComposerCellFocused(content, selection) {
42005
42107
  if (this.model.getters.isReadonly()) {
42006
42108
  return;
@@ -43976,12 +44078,7 @@ function addFormula(cell) {
43976
44078
  attrs.push(["t", "str"]);
43977
44079
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
43978
44080
  }
43979
- node = escapeXml /*xml*/ `
43980
- <f>
43981
- ${XlsxFormula}
43982
- </f>
43983
- ${cycle}
43984
- `;
44081
+ node = escapeXml /*xml*/ `<f> ${XlsxFormula} </f> ${cycle}`;
43985
44082
  return { attrs, node };
43986
44083
  }
43987
44084
  else {
@@ -44777,11 +44874,10 @@ function addRows(construct, data, sheet) {
44777
44874
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
44778
44875
  }
44779
44876
  attributes.push(...additionalAttrs);
44780
- cellNodes.push(escapeXml /*xml*/ `
44781
- <c ${formatAttributes(attributes)}>
44782
- ${cellNode}
44783
- </c>
44784
- `);
44877
+ // prettier-ignore
44878
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
44879
+ ${cellNode}
44880
+ </c>`);
44785
44881
  }
44786
44882
  }
44787
44883
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -45675,6 +45771,6 @@ exports.setTranslationMethod = setTranslationMethod;
45675
45771
  exports.tokenize = tokenize;
45676
45772
 
45677
45773
 
45678
- __info__.version = '16.2.29';
45679
- __info__.date = '2024-01-29T13:49:36.240Z';
45680
- __info__.hash = '27b2ec1';
45774
+ __info__.version = '16.2.31';
45775
+ __info__.date = '2024-02-09T13:05:40.161Z';
45776
+ __info__.hash = '2e76bdf';