@odoo/o-spreadsheet 16.2.29 → 16.2.32

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.
@@ -1692,6 +1692,9 @@
1692
1692
  function formatValue(value, format) {
1693
1693
  switch (typeof value) {
1694
1694
  case "string":
1695
+ if (value.includes('\\"')) {
1696
+ return value.replace(/\\"/g, '"');
1697
+ }
1695
1698
  return value;
1696
1699
  case "boolean":
1697
1700
  return value ? "TRUE" : "FALSE";
@@ -6272,14 +6275,8 @@
6272
6275
  : undefined));
6273
6276
  }
6274
6277
  }
6275
- else if (zone.left === zone.right && zone.top === zone.bottom) {
6276
- // A single cell. If it's only the title, the dataset is not added.
6277
- if (!dataSetsHaveTitle) {
6278
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
6279
- }
6280
- }
6281
6278
  else {
6282
- /* 1 row or 1 column */
6279
+ /* 1 cell, 1 row or 1 column */
6283
6280
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
6284
6281
  ? {
6285
6282
  top: zone.top,
@@ -6329,8 +6326,10 @@
6329
6326
  }
6330
6327
  const dataRange = ds.dataRange.clone({ zone: dataZone });
6331
6328
  return {
6332
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
6333
- range: getters.getRangeString(dataRange, "forceSheetReference"),
6329
+ label: ds.labelCell
6330
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", true)
6331
+ : undefined,
6332
+ range: getters.getRangeString(dataRange, "forceSheetReference", true),
6334
6333
  };
6335
6334
  }
6336
6335
  /**
@@ -6855,6 +6854,9 @@
6855
6854
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
6856
6855
  return {
6857
6856
  ...this.getDefinition(),
6857
+ labelRange: this.labelRange
6858
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
6859
+ : undefined,
6858
6860
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
6859
6861
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
6860
6862
  dataSets,
@@ -7419,6 +7421,9 @@
7419
7421
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
7420
7422
  return {
7421
7423
  ...this.getDefinition(),
7424
+ labelRange: this.labelRange
7425
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
7426
+ : undefined,
7422
7427
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
7423
7428
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
7424
7429
  dataSets,
@@ -7692,6 +7697,9 @@
7692
7697
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
7693
7698
  return {
7694
7699
  ...this.getDefinition(),
7700
+ labelRange: this.labelRange
7701
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
7702
+ : undefined,
7695
7703
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
7696
7704
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
7697
7705
  verticalAxisPosition: "left",
@@ -9135,6 +9143,12 @@
9135
9143
  let { col, row } = env.model.getters.getActivePosition();
9136
9144
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
9137
9145
  };
9146
+ const INSERT_LINK_NAME = (env) => {
9147
+ const sheetId = env.model.getters.getActiveSheetId();
9148
+ const { col, row } = env.model.getters.getActivePosition();
9149
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
9150
+ return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
9151
+ };
9138
9152
  //------------------------------------------------------------------------------
9139
9153
  // Filters action
9140
9154
  //------------------------------------------------------------------------------
@@ -9272,7 +9286,7 @@
9272
9286
  action: DELETE_CELL_SHIFT_LEFT,
9273
9287
  })
9274
9288
  .add("insert_link", {
9275
- name: _lt("Insert link"),
9289
+ name: INSERT_LINK_NAME,
9276
9290
  separator: true,
9277
9291
  sequence: 150,
9278
9292
  action: INSERT_LINK,
@@ -19958,14 +19972,13 @@
19958
19972
  setup() {
19959
19973
  owl.onMounted(() => {
19960
19974
  const el = this.composerRef.el;
19975
+ if (this.props.isDefaultFocus) {
19976
+ this.env.focusableElement.setFocusableElement(el);
19977
+ }
19961
19978
  this.contentHelper.updateEl(el);
19962
19979
  this.processContent();
19963
19980
  this.contentHelper.scrollSelectionIntoView();
19964
19981
  });
19965
- owl.onWillUnmount(() => {
19966
- var _a, _b;
19967
- (_b = (_a = this.props).onComposerUnmounted) === null || _b === void 0 ? void 0 : _b.call(_a);
19968
- });
19969
19982
  owl.onPatched(() => {
19970
19983
  if (!this.isKeyStillDown) {
19971
19984
  this.processContent();
@@ -19976,7 +19989,8 @@
19976
19989
  // Handlers
19977
19990
  // ---------------------------------------------------------------------------
19978
19991
  processArrowKeys(ev) {
19979
- if (this.env.model.getters.isSelectingForComposer()) {
19992
+ if (this.env.model.getters.isSelectingForComposer() ||
19993
+ this.env.model.getters.getEditionMode() === "inactive") {
19980
19994
  this.functionDescriptionState.showDescription = false;
19981
19995
  // Prevent the default content editable behavior which moves the cursor
19982
19996
  ev.preventDefault();
@@ -20017,21 +20031,23 @@
20017
20031
  var _a;
20018
20032
  ev.preventDefault();
20019
20033
  ev.stopPropagation();
20020
- if (this.autoCompleteState.showProvider) {
20021
- const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
20022
- if (autoCompleteValue) {
20023
- this.autoComplete(autoCompleteValue);
20024
- return;
20034
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
20035
+ if (this.autoCompleteState.showProvider) {
20036
+ const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
20037
+ if (autoCompleteValue) {
20038
+ this.autoComplete(autoCompleteValue);
20039
+ return;
20040
+ }
20025
20041
  }
20026
- }
20027
- else {
20028
- // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
20029
- // we can't let the model think that it is for a ref selection.
20030
- // todo: check if this can be removed someday
20031
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20042
+ else {
20043
+ // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
20044
+ // we can't let the model think that it is for a ref selection.
20045
+ // todo: check if this can be removed someday
20046
+ this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20047
+ }
20048
+ this.env.model.dispatch("STOP_EDITION");
20032
20049
  }
20033
20050
  const direction = ev.shiftKey ? "left" : "right";
20034
- this.env.model.dispatch("STOP_EDITION");
20035
20051
  this.env.model.selection.moveAnchorCell(direction, 1);
20036
20052
  }
20037
20053
  processEnterKey(ev) {
@@ -20076,6 +20092,9 @@
20076
20092
  this.compositionActive = false;
20077
20093
  }
20078
20094
  onKeydown(ev) {
20095
+ if (this.env.model.getters.getEditionMode() === "inactive") {
20096
+ return;
20097
+ }
20079
20098
  let handler = this.keyMapping[ev.key];
20080
20099
  if (handler) {
20081
20100
  handler.call(this, ev);
@@ -20086,22 +20105,45 @@
20086
20105
  }
20087
20106
  }
20088
20107
  updateCursorIfNeeded() {
20089
- if (!this.env.model.getters.isSelectingForComposer()) {
20108
+ const moveCursor = !this.env.model.getters.isSelectingForComposer() &&
20109
+ !(this.env.model.getters.getEditionMode() === "inactive");
20110
+ if (moveCursor) {
20090
20111
  const { start, end } = this.contentHelper.getCurrentSelection();
20091
20112
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
20092
20113
  this.isKeyStillDown = true;
20093
20114
  }
20094
20115
  }
20116
+ onPaste(ev) {
20117
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
20118
+ ev.stopPropagation();
20119
+ }
20120
+ }
20095
20121
  /*
20096
20122
  * Triggered automatically by the content-editable between the keydown and key up
20097
20123
  * */
20098
- onInput() {
20099
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
20124
+ onInput(ev) {
20125
+ var _a, _b;
20126
+ if (!this.shouldProcessInputEvents) {
20127
+ return;
20128
+ }
20129
+ if (ev.inputType === "insertFromPaste" &&
20130
+ this.env.model.getters.getEditionMode() === "inactive") {
20100
20131
  return;
20101
20132
  }
20133
+ ev.stopPropagation();
20134
+ let content;
20135
+ if (this.env.model.getters.getEditionMode() === "inactive") {
20136
+ content = ev.data || "";
20137
+ }
20138
+ else {
20139
+ content = this.contentHelper.getText();
20140
+ }
20141
+ if (this.props.focus === "inactive") {
20142
+ return (_b = (_a = this.props).onComposerCellFocused) === null || _b === void 0 ? void 0 : _b.call(_a, content);
20143
+ }
20102
20144
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20103
20145
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
20104
- content: this.contentHelper.getText(),
20146
+ content,
20105
20147
  selection: this.contentHelper.getCurrentSelection(),
20106
20148
  });
20107
20149
  }
@@ -20172,9 +20214,8 @@
20172
20214
  }
20173
20215
  const newSelection = this.contentHelper.getCurrentSelection();
20174
20216
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
20175
- if (this.props.focus === "inactive") {
20176
- this.props.onComposerContentFocused(newSelection);
20177
- }
20217
+ this.props.onComposerContentFocused();
20218
+ if (this.props.focus === "inactive") ;
20178
20219
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
20179
20220
  this.processTokenAtCursor();
20180
20221
  }
@@ -20398,14 +20439,16 @@
20398
20439
  Composer.components = { TextValueProvider, FunctionDescriptionProvider };
20399
20440
  Composer.defaultProps = {
20400
20441
  inputStyle: "",
20442
+ isDefaultFocus: false,
20401
20443
  };
20402
20444
  Composer.props = {
20403
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
20404
- onComposerContentFocused: Function,
20405
20445
  inputStyle: { type: String, optional: true },
20406
20446
  rect: { type: Object, optional: true },
20407
20447
  delimitation: { type: Object, optional: true },
20408
- onComposerUnmounted: { type: Function, optional: true },
20448
+ focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
20449
+ onComposerCellFocused: { type: Function, optional: true },
20450
+ onComposerContentFocused: Function,
20451
+ isDefaultFocus: { type: Boolean, optional: true },
20409
20452
  };
20410
20453
 
20411
20454
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
@@ -20437,46 +20480,28 @@
20437
20480
  * It also applies the style of the cell to the composer input.
20438
20481
  */
20439
20482
  class GridComposer extends owl.Component {
20483
+ constructor() {
20484
+ super(...arguments);
20485
+ // TODORAR see if we can keep it undefined
20486
+ this.rect = this.defaultRect;
20487
+ this.isEditing = false;
20488
+ }
20489
+ get defaultRect() {
20490
+ return { x: 0, y: 0, width: 0, height: 0 };
20491
+ }
20440
20492
  setup() {
20441
- this.gridComposerRef = owl.useRef("gridComposer");
20442
- this.composerState = owl.useState({
20443
- rect: undefined,
20444
- delimitation: undefined,
20445
- });
20446
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
20447
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
20448
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
20449
- this.isCellReferenceVisible = false;
20450
- owl.onMounted(() => {
20451
- const el = this.gridComposerRef.el;
20452
- this.composerState.rect = {
20453
- x: this.rect.x,
20454
- y: this.rect.y,
20455
- width: el.clientWidth,
20456
- height: el.clientHeight,
20457
- };
20458
- this.composerState.delimitation = {
20459
- width: el.parentElement.clientWidth,
20460
- height: el.parentElement.clientHeight,
20461
- };
20462
- });
20463
20493
  owl.onWillUpdateProps(() => {
20464
- if (this.isCellReferenceVisible) {
20465
- return;
20466
- }
20467
- const sheetId = this.env.model.getters.getActiveSheetId();
20468
- const zone = this.env.model.getters.getSelectedZone();
20469
- const rect = this.env.model.getters.getVisibleRect(zone);
20470
- if (!deepEquals(rect, this.rect) ||
20471
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
20472
- this.isCellReferenceVisible = true;
20473
- }
20494
+ this.updateComponentPosition();
20495
+ this.updateCellReferenceVisibility();
20474
20496
  });
20475
20497
  }
20476
20498
  get shouldDisplayCellReference() {
20477
20499
  return this.isCellReferenceVisible;
20478
20500
  }
20479
20501
  get cellReference() {
20502
+ if (!this.env.model.getters.getCurrentEditedCell()) {
20503
+ return "";
20504
+ }
20480
20505
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
20481
20506
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
20482
20507
  return `${prefixSheet ? getComposerSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
@@ -20488,7 +20513,27 @@
20488
20513
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
20489
20514
  });
20490
20515
  }
20516
+ get composerProps() {
20517
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
20518
+ return {
20519
+ rect: { ...this.rect },
20520
+ delimitation: {
20521
+ width,
20522
+ height,
20523
+ },
20524
+ focus: this.props.focus,
20525
+ isDefaultFocus: true,
20526
+ onComposerContentFocused: this.props.onComposerContentFocused,
20527
+ onComposerCellFocused: this.props.onComposerCellFocused,
20528
+ };
20529
+ }
20491
20530
  get containerStyle() {
20531
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
20532
+ return `
20533
+ position: absolute;
20534
+ z-index: -1000;
20535
+ `;
20536
+ }
20492
20537
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
20493
20538
  const cell = this.env.model.getters.getActiveCell();
20494
20539
  const position = this.env.model.getters.getActivePosition();
@@ -20508,6 +20553,8 @@
20508
20553
  if (!isFormula) {
20509
20554
  textAlign = style.align || cell.defaultAlign;
20510
20555
  }
20556
+ const maxHeight = this.props.gridDims.height - this.rect.y;
20557
+ const maxWidth = this.props.gridDims.width - this.rect.x;
20511
20558
  /**
20512
20559
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
20513
20560
  * including all the paddings/margins of the composer
@@ -20519,6 +20566,8 @@
20519
20566
  top: `${top}px`,
20520
20567
  "min-width": `${width + 1}px`,
20521
20568
  "min-height": `${height + 1}px`,
20569
+ "max-width": `${maxWidth}px`,
20570
+ "max-height": `${maxHeight}px`,
20522
20571
  background,
20523
20572
  color,
20524
20573
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -20528,22 +20577,40 @@
20528
20577
  "text-align": textAlign,
20529
20578
  });
20530
20579
  }
20531
- get composerStyle() {
20532
- const maxHeight = this.props.gridDims.height - this.rect.y;
20533
- const maxWidth = this.props.gridDims.width - this.rect.x;
20534
- return cssPropertiesToCss({
20535
- "max-width": `${maxWidth}px`,
20536
- "max-height": `${maxHeight}px`,
20537
- });
20580
+ updateComponentPosition() {
20581
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
20582
+ if (this.isEditing !== isEditing) {
20583
+ this.isEditing = isEditing;
20584
+ if (!isEditing) {
20585
+ this.rect = this.defaultRect;
20586
+ this.env.focusableElement.focus();
20587
+ return;
20588
+ }
20589
+ const position = this.env.model.getters.getActivePosition();
20590
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
20591
+ this.rect = this.env.model.getters.getVisibleRect(zone);
20592
+ }
20593
+ }
20594
+ updateCellReferenceVisibility() {
20595
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
20596
+ return;
20597
+ }
20598
+ const sheetId = this.env.model.getters.getActiveSheetId();
20599
+ const zone = this.env.model.getters.getSelectedZone();
20600
+ const rect = this.env.model.getters.getVisibleRect(zone);
20601
+ if (!deepEquals(rect, this.rect) ||
20602
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
20603
+ this.isCellReferenceVisible = true;
20604
+ }
20538
20605
  }
20539
20606
  }
20540
20607
  GridComposer.template = "o-spreadsheet-GridComposer";
20541
20608
  GridComposer.components = { Composer };
20542
20609
  GridComposer.props = {
20543
20610
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
20544
- onComposerUnmounted: Function,
20545
20611
  onComposerContentFocused: Function,
20546
20612
  gridDims: Object,
20613
+ onComposerCellFocused: Function,
20547
20614
  };
20548
20615
 
20549
20616
  const CSS$1 = css /* scss */ `
@@ -21134,7 +21201,6 @@
21134
21201
  this.props.onCellDoubleClicked(col, row);
21135
21202
  }
21136
21203
  onContextMenu(ev) {
21137
- ev.preventDefault();
21138
21204
  const [col, row] = this.getCartesianCoordinates(ev);
21139
21205
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
21140
21206
  }
@@ -22361,17 +22427,16 @@
22361
22427
  menuItems: [],
22362
22428
  });
22363
22429
  this.gridRef = owl.useRef("grid");
22364
- this.hiddenInput = owl.useRef("hiddenInput");
22365
22430
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
22366
22431
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
22367
22432
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
22368
22433
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
22369
22434
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
22370
22435
  owl.useExternalListener(document.body, "paste", this.paste);
22371
- owl.onMounted(() => this.focus());
22372
- this.props.exposeFocus(() => this.focus());
22436
+ owl.onMounted(() => this.focusDefaultElement());
22437
+ this.props.exposeFocus(() => this.focusDefaultElement());
22373
22438
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
22374
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
22439
+ owl.useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
22375
22440
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
22376
22441
  this.moveCanvas(deltaX, deltaY);
22377
22442
  this.hoveredCell.col = undefined;
@@ -22394,13 +22459,12 @@
22394
22459
  if (this.env.model.getters.hasOpenedPopover()) {
22395
22460
  this.closeOpenedPopover();
22396
22461
  }
22397
- this.focus();
22462
+ this.focusDefaultElement();
22398
22463
  }
22399
- focus() {
22400
- var _a;
22464
+ focusDefaultElement() {
22401
22465
  if (!this.env.model.getters.getSelectedFigureId() &&
22402
22466
  this.env.model.getters.getEditionMode() === "inactive") {
22403
- (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
22467
+ this.env.focusableElement.focus();
22404
22468
  }
22405
22469
  }
22406
22470
  get gridEl() {
@@ -22545,19 +22609,6 @@
22545
22609
  return;
22546
22610
  }
22547
22611
  }
22548
- onInput(ev) {
22549
- // the user meant to paste in the sheet, not open the composer with the pasted content
22550
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
22551
- return;
22552
- }
22553
- if (ev.data) {
22554
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
22555
- // character
22556
- ev.preventDefault();
22557
- ev.stopPropagation();
22558
- this.props.onGridComposerCellFocused(ev.data);
22559
- }
22560
- }
22561
22612
  // ---------------------------------------------------------------------------
22562
22613
  // Context Menu
22563
22614
  // ---------------------------------------------------------------------------
@@ -22654,7 +22705,7 @@
22654
22705
  }
22655
22706
  closeMenu() {
22656
22707
  this.menuState.isOpen = false;
22657
- this.focus();
22708
+ this.focusDefaultElement();
22658
22709
  }
22659
22710
  }
22660
22711
  Grid.template = "o-spreadsheet-Grid";
@@ -27006,7 +27057,7 @@
27006
27057
  * a breaking change is made in the way the state is handled, and an upgrade
27007
27058
  * function should be defined
27008
27059
  */
27009
- const CURRENT_VERSION = 12;
27060
+ const CURRENT_VERSION = 12.5;
27010
27061
  const INITIAL_SHEET_ID = "Sheet1";
27011
27062
  /**
27012
27063
  * This function tries to load anything that could look like a valid
@@ -27272,6 +27323,31 @@
27272
27323
  return data;
27273
27324
  },
27274
27325
  },
27326
+ {
27327
+ description: "Fix datafilter duplication",
27328
+ from: 12,
27329
+ to: 12.5,
27330
+ applyMigration(data) {
27331
+ for (let sheet of data.sheets || []) {
27332
+ let knownDataFilterZones = [];
27333
+ for (let filterTable of sheet.filterTables || []) {
27334
+ const zone = toZone(filterTable.range);
27335
+ // See commit message for the details
27336
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
27337
+ if (intersectZoneIndex !== -1) {
27338
+ knownDataFilterZones[intersectZoneIndex] = zone;
27339
+ }
27340
+ else {
27341
+ knownDataFilterZones.push(zone);
27342
+ }
27343
+ }
27344
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
27345
+ range: zoneToXc(zone),
27346
+ }));
27347
+ }
27348
+ return data;
27349
+ },
27350
+ },
27275
27351
  ];
27276
27352
  /**
27277
27353
  * This function is used to repair faulty data independently of the migration.
@@ -28124,9 +28200,9 @@
28124
28200
  allowDispatch(cmd) {
28125
28201
  switch (cmd.type) {
28126
28202
  case "UPDATE_CELL":
28127
- return this.checkCellOutOfSheet(cmd);
28203
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
28128
28204
  case "CLEAR_CELL":
28129
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28205
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
28130
28206
  default:
28131
28207
  return 0 /* CommandResult.Success */;
28132
28208
  }
@@ -28292,13 +28368,13 @@
28292
28368
  /*
28293
28369
  * Reconstructs the original formula string based on a normalized form and its dependencies
28294
28370
  */
28295
- buildFormulaContent(sheetId, cell, dependencies) {
28371
+ buildFormulaContent(sheetId, cell, dependencies, useFixedZone = false) {
28296
28372
  const ranges = dependencies || cell.dependencies;
28297
28373
  let rangeIndex = 0;
28298
28374
  return concat(cell.compiledFormula.tokens.map((token) => {
28299
28375
  if (token.type === "REFERENCE") {
28300
28376
  const range = ranges[rangeIndex++];
28301
- return this.getters.getRangeString(range, sheetId);
28377
+ return this.getters.getRangeString(range, sheetId, useFixedZone);
28302
28378
  }
28303
28379
  return token.value;
28304
28380
  }));
@@ -28421,7 +28497,7 @@
28421
28497
  else {
28422
28498
  style = before ? before.style : undefined;
28423
28499
  }
28424
- let format = ("format" in after ? after.format : before && before.format) || detectFormat(afterContent);
28500
+ const format = "format" in after ? after.format : before && before.format;
28425
28501
  /* Read the following IF as:
28426
28502
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
28427
28503
  * - the command says the new content is empty and has no border/format/style
@@ -28465,7 +28541,7 @@
28465
28541
  id,
28466
28542
  content,
28467
28543
  style,
28468
- format,
28544
+ format: format || detectFormat(content),
28469
28545
  isFormula: false,
28470
28546
  };
28471
28547
  }
@@ -28542,6 +28618,18 @@
28542
28618
  }
28543
28619
  return 0 /* CommandResult.Success */;
28544
28620
  }
28621
+ checkUselessUpdateCell(cmd) {
28622
+ const cell = this.getters.getCell(cmd);
28623
+ const hasContent = "content" in cmd || "formula" in cmd;
28624
+ const hasStyle = "style" in cmd;
28625
+ const hasFormat = "format" in cmd;
28626
+ if ((!hasContent || (cell === null || cell === void 0 ? void 0 : cell.content) === cmd.content) &&
28627
+ (!hasStyle || deepEquals(cell === null || cell === void 0 ? void 0 : cell.style, cmd.style)) &&
28628
+ (!hasFormat || (cell === null || cell === void 0 ? void 0 : cell.format) === cmd.format)) {
28629
+ return 89 /* CommandResult.NoChanges */;
28630
+ }
28631
+ return 0 /* CommandResult.Success */;
28632
+ }
28545
28633
  }
28546
28634
  CellPlugin.getters = [
28547
28635
  "zoneToXC",
@@ -28868,7 +28956,13 @@
28868
28956
  }
28869
28957
  }
28870
28958
  exportForExcel(data) {
28871
- this.export(data);
28959
+ if (data.sheets) {
28960
+ for (let sheet of data.sheets) {
28961
+ if (this.cfRules[sheet.id]) {
28962
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
28963
+ }
28964
+ }
28965
+ }
28872
28966
  }
28873
28967
  // ---------------------------------------------------------------------------
28874
28968
  // Getters
@@ -28918,11 +29012,11 @@
28918
29012
  // ---------------------------------------------------------------------------
28919
29013
  // Private
28920
29014
  // ---------------------------------------------------------------------------
28921
- mapToConditionalFormat(sheetId, cf) {
29015
+ mapToConditionalFormat(sheetId, cf, options = { useFixedReference: false }) {
28922
29016
  return {
28923
29017
  ...cf,
28924
29018
  ranges: cf.ranges.map((range) => {
28925
- return this.getters.getRangeString(range, sheetId);
29019
+ return this.getters.getRangeString(range, sheetId, options.useFixedReference);
28926
29020
  }),
28927
29021
  };
28928
29022
  }
@@ -30851,8 +30945,9 @@
30851
30945
  *
30852
30946
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
30853
30947
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
30948
+ * @param [useFixedZone=false] if true, the range will be returned with fixed row and column
30854
30949
  */
30855
- getRangeString(range, forSheetId) {
30950
+ getRangeString(range, forSheetId, useFixedZone = false) {
30856
30951
  if (!range) {
30857
30952
  return INCORRECT_RANGE_STRING;
30858
30953
  }
@@ -30879,7 +30974,7 @@
30879
30974
  if (prefixSheet && !sheetName) {
30880
30975
  return INCORRECT_RANGE_STRING;
30881
30976
  }
30882
- let rangeString = this.getRangePartString(rangeImpl, 0);
30977
+ let rangeString = this.getRangePartString(rangeImpl, 0, useFixedZone);
30883
30978
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
30884
30979
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
30885
30980
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -30889,7 +30984,7 @@
30889
30984
  rangeImpl.parts[1].rowFixed ||
30890
30985
  rangeImpl.parts[1].colFixed) {
30891
30986
  rangeString += ":";
30892
- rangeString += this.getRangePartString(rangeImpl, 1);
30987
+ rangeString += this.getRangePartString(rangeImpl, 1, useFixedZone);
30893
30988
  }
30894
30989
  }
30895
30990
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -30919,14 +31014,14 @@
30919
31014
  /**
30920
31015
  * Get a Xc string that represent a part of a range
30921
31016
  */
30922
- getRangePartString(range, part) {
31017
+ getRangePartString(range, part, useFixedZone = false) {
30923
31018
  var _a, _b;
30924
31019
  const colFixed = range.parts && ((_a = range.parts[part]) === null || _a === void 0 ? void 0 : _a.colFixed) ? "$" : "";
30925
31020
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
30926
31021
  const rowFixed = range.parts && ((_b = range.parts[part]) === null || _b === void 0 ? void 0 : _b.rowFixed) ? "$" : "";
30927
31022
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
30928
31023
  let str = "";
30929
- if (range.isFullCol) {
31024
+ if (range.isFullCol && !useFixedZone) {
30930
31025
  if (part === 0 && range.unboundedZone.hasHeader) {
30931
31026
  str = colFixed + col + rowFixed + row;
30932
31027
  }
@@ -30934,7 +31029,7 @@
30934
31029
  str = colFixed + col;
30935
31030
  }
30936
31031
  }
30937
- else if (range.isFullRow) {
31032
+ else if (range.isFullRow && !useFixedZone) {
30938
31033
  if (part === 0 && range.unboundedZone.hasHeader) {
30939
31034
  str = colFixed + col + rowFixed + row;
30940
31035
  }
@@ -32323,6 +32418,10 @@
32323
32418
  if (cell.format !== evaluatedCell.format) {
32324
32419
  exportedCellData.computedFormat = evaluatedCell.format;
32325
32420
  }
32421
+ // if there is a formula but no dependencies (maybe because the cell is in error), no need to recompute the formula text
32422
+ if (cell.isFormula && cell.dependencies.length) {
32423
+ exportedCellData.content = this.getters.buildFormulaContent(sheet.id, cell, cell.dependencies, true);
32424
+ }
32326
32425
  }
32327
32426
  }
32328
32427
  }
@@ -34185,8 +34284,7 @@
34185
34284
  let row = zone.bottom;
34186
34285
  if (col > 0) {
34187
34286
  let leftPosition = { sheetId, col: col - 1, row };
34188
- while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
34189
- ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content)) {
34287
+ while ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content) {
34190
34288
  row += 1;
34191
34289
  leftPosition = { sheetId, col: col - 1, row };
34192
34290
  }
@@ -34195,8 +34293,7 @@
34195
34293
  col = zone.right;
34196
34294
  if (col <= this.getters.getNumberCols(sheetId)) {
34197
34295
  let rightPosition = { sheetId, col: col + 1, row };
34198
- while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
34199
- ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content)) {
34296
+ while ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content) {
34200
34297
  row += 1;
34201
34298
  rightPosition = { sheetId, col: col + 1, row };
34202
34299
  }
@@ -39914,6 +40011,19 @@
39914
40011
  }
39915
40012
  }
39916
40013
 
40014
+ class FocusableElement {
40015
+ constructor() {
40016
+ this.focusableElement = undefined;
40017
+ }
40018
+ setFocusableElement(element) {
40019
+ this.focusableElement = element;
40020
+ }
40021
+ focus() {
40022
+ var _a;
40023
+ (_a = this.focusableElement) === null || _a === void 0 ? void 0 : _a.focus();
40024
+ }
40025
+ }
40026
+
39917
40027
  const RIPPLE_KEY_FRAMES = [
39918
40028
  { transform: "scale(0)" },
39919
40029
  { transform: "scale(0.8)", offset: 0.33 },
@@ -41889,6 +41999,7 @@
41889
41999
  _t: Spreadsheet._t,
41890
42000
  clipboard: this.env.clipboard || instantiateClipboard(),
41891
42001
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
42002
+ focusableElement: new FocusableElement(),
41892
42003
  });
41893
42004
  owl.useExternalListener(window, "resize", () => this.render(true));
41894
42005
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -41987,17 +42098,18 @@
41987
42098
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
41988
42099
  this.composer.topBarFocus = "contentFocus";
41989
42100
  this.composer.gridFocusMode = "inactive";
41990
- this.setComposerContent({ selection } || {});
42101
+ this.setComposerContent({ selection });
41991
42102
  }
41992
- onGridComposerContentFocused() {
42103
+ onGridComposerContentFocused(selection) {
41993
42104
  if (this.model.getters.isReadonly()) {
41994
42105
  return;
41995
42106
  }
41996
42107
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
41997
42108
  this.composer.topBarFocus = "inactive";
41998
42109
  this.composer.gridFocusMode = "contentFocus";
41999
- this.setComposerContent({});
42110
+ this.setComposerContent({ selection });
42000
42111
  }
42112
+ // TODO: either both are defined or none of them. change those args to an object
42001
42113
  onGridComposerCellFocused(content, selection) {
42002
42114
  if (this.model.getters.isReadonly()) {
42003
42115
  return;
@@ -43459,11 +43571,10 @@
43459
43571
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
43460
43572
  */
43461
43573
  getEndOfCluster(startPosition, dim, dir) {
43462
- const sheet = this.getters.getActiveSheet();
43463
43574
  let currentPosition = startPosition;
43464
43575
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
43465
43576
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
43466
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
43577
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
43467
43578
  ? "endOfCluster"
43468
43579
  : "nextCluster";
43469
43580
  while (true) {
@@ -43473,7 +43584,7 @@
43473
43584
  currentPosition.row === nextCellPosition.row) {
43474
43585
  break;
43475
43586
  }
43476
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
43587
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
43477
43588
  if (mode === "endOfCluster" && isNextCellEmpty) {
43478
43589
  break;
43479
43590
  }
@@ -43487,14 +43598,25 @@
43487
43598
  return dim === "cols" ? currentPosition.col : currentPosition.row;
43488
43599
  }
43489
43600
  /**
43490
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
43491
- * check if the merge containing the cell is empty.
43601
+ * Check if a cell evaluated value is empty. If the cell is part of a merge,
43602
+ * the check applies to the main cell of the merge.
43492
43603
  */
43493
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
43604
+ isEvaluatedCellEmpty({ col, row }) {
43605
+ const sheetId = this.getters.getActiveSheetId();
43494
43606
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
43495
43607
  const cell = this.getters.getEvaluatedCell(position);
43496
43608
  return cell.type === CellValueType.empty;
43497
43609
  }
43610
+ /**
43611
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
43612
+ * the check applies to the main cell of the merge.
43613
+ */
43614
+ isCellEmpty({ col, row }) {
43615
+ var _a;
43616
+ const sheetId = this.getters.getActiveSheetId();
43617
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
43618
+ return !((_a = this.getters.getCell(position)) === null || _a === void 0 ? void 0 : _a.content);
43619
+ }
43498
43620
  /** Computes the next cell position in the given direction by crossing through merges and skipping hidden cells.
43499
43621
  *
43500
43622
  * This has the same behaviour as getNextAvailablePosition() for certain arguments, but use this method instead
@@ -43973,12 +44095,7 @@
43973
44095
  attrs.push(["t", "str"]);
43974
44096
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
43975
44097
  }
43976
- node = escapeXml /*xml*/ `
43977
- <f>
43978
- ${XlsxFormula}
43979
- </f>
43980
- ${cycle}
43981
- `;
44098
+ node = escapeXml /*xml*/ `<f> ${XlsxFormula} </f> ${cycle}`;
43982
44099
  return { attrs, node };
43983
44100
  }
43984
44101
  else {
@@ -44774,11 +44891,10 @@
44774
44891
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
44775
44892
  }
44776
44893
  attributes.push(...additionalAttrs);
44777
- cellNodes.push(escapeXml /*xml*/ `
44778
- <c ${formatAttributes(attributes)}>
44779
- ${cellNode}
44780
- </c>
44781
- `);
44894
+ // prettier-ignore
44895
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
44896
+ ${cellNode}
44897
+ </c>`);
44782
44898
  }
44783
44899
  }
44784
44900
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -45674,9 +45790,9 @@
45674
45790
  Object.defineProperty(exports, '__esModule', { value: true });
45675
45791
 
45676
45792
 
45677
- __info__.version = '16.2.29';
45678
- __info__.date = '2024-01-29T13:49:36.240Z';
45679
- __info__.hash = '27b2ec1';
45793
+ __info__.version = '16.2.32';
45794
+ __info__.date = '2024-02-16T14:17:18.471Z';
45795
+ __info__.hash = 'd5e57e6';
45680
45796
 
45681
45797
 
45682
45798
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);