@odoo/o-spreadsheet 16.1.33 → 16.1.36

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.
@@ -207,7 +207,7 @@ const TOPBAR_HEIGHT = 63;
207
207
  const BOTTOMBAR_HEIGHT = 36;
208
208
  const DEFAULT_CELL_WIDTH = 96;
209
209
  const DEFAULT_CELL_HEIGHT = 23;
210
- const SCROLLBAR_WIDTH$1 = 15;
210
+ const SCROLLBAR_WIDTH = 15;
211
211
  const AUTOFILL_EDGE_LENGTH = 8;
212
212
  const ICON_EDGE_LENGTH = 18;
213
213
  const UNHIDE_ICON_EDGE_LENGTH = 14;
@@ -1676,6 +1676,9 @@ function parseFormat(formatString) {
1676
1676
  function formatValue(value, format) {
1677
1677
  switch (typeof value) {
1678
1678
  case "string":
1679
+ if (value.includes('\\"')) {
1680
+ return value.replace(/\\"/g, '"');
1681
+ }
1679
1682
  return value;
1680
1683
  case "boolean":
1681
1684
  return value ? "TRUE" : "FALSE";
@@ -3878,14 +3881,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
3878
3881
  : undefined));
3879
3882
  }
3880
3883
  }
3881
- else if (zone.left === zone.right && zone.top === zone.bottom) {
3882
- // A single cell. If it's only the title, the dataset is not added.
3883
- if (!dataSetsHaveTitle) {
3884
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
3885
- }
3886
- }
3887
3884
  else {
3888
- /* 1 row or 1 column */
3885
+ /* 1 cell, 1 row or 1 column */
3889
3886
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
3890
3887
  ? {
3891
3888
  top: zone.top,
@@ -3935,8 +3932,10 @@ function toExcelDataset(getters, ds) {
3935
3932
  }
3936
3933
  const dataRange = ds.dataRange.clone({ zone: dataZone });
3937
3934
  return {
3938
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
3939
- range: getters.getRangeString(dataRange, "forceSheetReference"),
3935
+ label: ds.labelCell
3936
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", true)
3937
+ : undefined,
3938
+ range: getters.getRangeString(dataRange, "forceSheetReference", true),
3940
3939
  };
3941
3940
  }
3942
3941
  /**
@@ -4433,6 +4432,9 @@ class BarChart extends AbstractChart {
4433
4432
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
4434
4433
  return {
4435
4434
  ...this.getDefinition(),
4435
+ labelRange: this.labelRange
4436
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
4437
+ : undefined,
4436
4438
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
4437
4439
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
4438
4440
  dataSets,
@@ -4991,6 +4993,9 @@ class LineChart extends AbstractChart {
4991
4993
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
4992
4994
  return {
4993
4995
  ...this.getDefinition(),
4996
+ labelRange: this.labelRange
4997
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
4998
+ : undefined,
4994
4999
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
4995
5000
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
4996
5001
  dataSets,
@@ -5254,6 +5259,9 @@ class PieChart extends AbstractChart {
5254
5259
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
5255
5260
  return {
5256
5261
  ...this.getDefinition(),
5262
+ labelRange: this.labelRange
5263
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
5264
+ : undefined,
5257
5265
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
5258
5266
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
5259
5267
  verticalAxisPosition: "left",
@@ -7259,6 +7267,12 @@ const INSERT_LINK = (env) => {
7259
7267
  let { col, row } = env.model.getters.getActivePosition();
7260
7268
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
7261
7269
  };
7270
+ const INSERT_LINK_NAME = (env) => {
7271
+ const sheetId = env.model.getters.getActiveSheetId();
7272
+ const { col, row } = env.model.getters.getActivePosition();
7273
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
7274
+ return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
7275
+ };
7262
7276
  //------------------------------------------------------------------------------
7263
7277
  // Filters action
7264
7278
  //------------------------------------------------------------------------------
@@ -7396,7 +7410,7 @@ cellMenuRegistry
7396
7410
  action: DELETE_CELL_SHIFT_LEFT,
7397
7411
  })
7398
7412
  .add("insert_link", {
7399
- name: _lt("Insert link"),
7413
+ name: INSERT_LINK_NAME,
7400
7414
  separator: true,
7401
7415
  sequence: 150,
7402
7416
  action: INSERT_LINK,
@@ -16555,9 +16569,6 @@ class EditionPlugin extends UIPlugin {
16555
16569
  break;
16556
16570
  }
16557
16571
  }
16558
- unsubscribe() {
16559
- this.mode = "inactive";
16560
- }
16561
16572
  // ---------------------------------------------------------------------------
16562
16573
  // Getters
16563
16574
  // ---------------------------------------------------------------------------
@@ -17450,13 +17461,12 @@ class Composer extends owl.Component {
17450
17461
  setup() {
17451
17462
  owl.onMounted(() => {
17452
17463
  const el = this.composerRef.el;
17464
+ if (this.props.isDefaultFocus) {
17465
+ this.env.focusableElement.setFocusableElement(el);
17466
+ }
17453
17467
  this.contentHelper.updateEl(el);
17454
17468
  this.processContent();
17455
17469
  });
17456
- owl.onWillUnmount(() => {
17457
- var _a, _b;
17458
- (_b = (_a = this.props).onComposerUnmounted) === null || _b === void 0 ? void 0 : _b.call(_a);
17459
- });
17460
17470
  owl.onPatched(() => {
17461
17471
  if (!this.isKeyStillDown) {
17462
17472
  this.processContent();
@@ -17467,7 +17477,8 @@ class Composer extends owl.Component {
17467
17477
  // Handlers
17468
17478
  // ---------------------------------------------------------------------------
17469
17479
  processArrowKeys(ev) {
17470
- if (this.env.model.getters.isSelectingForComposer()) {
17480
+ if (this.env.model.getters.isSelectingForComposer() ||
17481
+ this.env.model.getters.getEditionMode() === "inactive") {
17471
17482
  this.functionDescriptionState.showDescription = false;
17472
17483
  // Prevent the default content editable behavior which moves the cursor
17473
17484
  ev.preventDefault();
@@ -17508,21 +17519,23 @@ class Composer extends owl.Component {
17508
17519
  var _a;
17509
17520
  ev.preventDefault();
17510
17521
  ev.stopPropagation();
17511
- if (this.autoCompleteState.showProvider) {
17512
- const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
17513
- if (autoCompleteValue) {
17514
- this.autoComplete(autoCompleteValue);
17515
- return;
17522
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
17523
+ if (this.autoCompleteState.showProvider) {
17524
+ const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
17525
+ if (autoCompleteValue) {
17526
+ this.autoComplete(autoCompleteValue);
17527
+ return;
17528
+ }
17516
17529
  }
17517
- }
17518
- else {
17519
- // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
17520
- // we can't let the model think that it is for a ref selection.
17521
- // todo: check if this can be removed someday
17522
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17530
+ else {
17531
+ // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
17532
+ // we can't let the model think that it is for a ref selection.
17533
+ // todo: check if this can be removed someday
17534
+ this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17535
+ }
17536
+ this.env.model.dispatch("STOP_EDITION");
17523
17537
  }
17524
17538
  const direction = ev.shiftKey ? "left" : "right";
17525
- this.env.model.dispatch("STOP_EDITION");
17526
17539
  this.env.model.selection.moveAnchorCell(direction, 1);
17527
17540
  }
17528
17541
  processEnterKey(ev) {
@@ -17555,6 +17568,9 @@ class Composer extends owl.Component {
17555
17568
  this.compositionActive = false;
17556
17569
  }
17557
17570
  onKeydown(ev) {
17571
+ if (this.env.model.getters.getEditionMode() === "inactive") {
17572
+ return;
17573
+ }
17558
17574
  let handler = this.keyMapping[ev.key];
17559
17575
  if (handler) {
17560
17576
  handler.call(this, ev);
@@ -17565,23 +17581,46 @@ class Composer extends owl.Component {
17565
17581
  }
17566
17582
  }
17567
17583
  updateCursorIfNeeded() {
17568
- if (!this.env.model.getters.isSelectingForComposer()) {
17584
+ const moveCursor = !this.env.model.getters.isSelectingForComposer() &&
17585
+ !(this.env.model.getters.getEditionMode() === "inactive");
17586
+ if (moveCursor) {
17569
17587
  const { start, end } = this.contentHelper.getCurrentSelection();
17570
17588
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
17571
17589
  this.isKeyStillDown = true;
17572
17590
  }
17573
17591
  }
17592
+ onPaste(ev) {
17593
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
17594
+ ev.stopPropagation();
17595
+ }
17596
+ }
17574
17597
  /*
17575
17598
  * Triggered automatically by the content-editable between the keydown and key up
17576
17599
  * */
17577
- onInput() {
17578
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
17600
+ onInput(ev) {
17601
+ var _a, _b;
17602
+ if (!this.shouldProcessInputEvents) {
17579
17603
  return;
17580
17604
  }
17605
+ if (ev.inputType === "insertFromPaste" &&
17606
+ this.env.model.getters.getEditionMode() === "inactive") {
17607
+ return;
17608
+ }
17609
+ ev.stopPropagation();
17610
+ let content;
17611
+ if (this.env.model.getters.getEditionMode() === "inactive") {
17612
+ content = ev.data || "";
17613
+ }
17614
+ else {
17615
+ const el = this.composerRef.el;
17616
+ content = el.childNodes.length ? el.textContent : "";
17617
+ }
17618
+ if (this.props.focus === "inactive") {
17619
+ return (_b = (_a = this.props).onComposerCellFocused) === null || _b === void 0 ? void 0 : _b.call(_a, content);
17620
+ }
17581
17621
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17582
- const el = this.composerRef.el;
17583
17622
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
17584
- content: el.childNodes.length ? el.textContent : "",
17623
+ content,
17585
17624
  selection: this.contentHelper.getCurrentSelection(),
17586
17625
  });
17587
17626
  }
@@ -17641,9 +17680,8 @@ class Composer extends owl.Component {
17641
17680
  }
17642
17681
  const newSelection = this.contentHelper.getCurrentSelection();
17643
17682
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17644
- if (this.props.focus === "inactive") {
17645
- this.props.onComposerContentFocused(newSelection);
17646
- }
17683
+ this.props.onComposerContentFocused();
17684
+ if (this.props.focus === "inactive") ;
17647
17685
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
17648
17686
  this.processTokenAtCursor();
17649
17687
  }
@@ -17660,6 +17698,7 @@ class Composer extends owl.Component {
17660
17698
  this.contentHelper.removeAll(); // removes the content of the composer, to be added just after
17661
17699
  this.shouldProcessInputEvents = false;
17662
17700
  if (this.props.focus !== "inactive") {
17701
+ this.contentHelper.el.focus();
17663
17702
  this.contentHelper.selectRange(0, 0); // move the cursor inside the composer at 0 0.
17664
17703
  }
17665
17704
  const content = this.getContent();
@@ -17826,18 +17865,18 @@ Composer.template = "o-spreadsheet-Composer";
17826
17865
  Composer.components = { TextValueProvider, FunctionDescriptionProvider };
17827
17866
  Composer.defaultProps = {
17828
17867
  inputStyle: "",
17868
+ isDefaultFocus: false,
17829
17869
  };
17830
17870
  Composer.props = {
17831
17871
  inputStyle: { type: String, optional: true },
17832
17872
  rect: { type: Object, optional: true },
17833
17873
  delimitation: { type: Object, optional: true },
17834
17874
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
17835
- onComposerUnmounted: { type: Function, optional: true },
17875
+ onComposerCellFocused: { type: Function, optional: true },
17836
17876
  onComposerContentFocused: Function,
17877
+ isDefaultFocus: { type: Boolean, optional: true },
17837
17878
  };
17838
17879
 
17839
- const SCROLLBAR_WIDTH = 14;
17840
- const SCROLLBAR_HIGHT = 15;
17841
17880
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
17842
17881
  css /* scss */ `
17843
17882
  div.o-grid-composer {
@@ -17852,35 +17891,52 @@ css /* scss */ `
17852
17891
  * It also applies the style of the cell to the composer input.
17853
17892
  */
17854
17893
  class GridComposer extends owl.Component {
17894
+ constructor() {
17895
+ super(...arguments);
17896
+ this.rect = undefined;
17897
+ this.isEditing = false;
17898
+ }
17899
+ get defaultRect() {
17900
+ return { x: 0, y: 0, width: 0, height: 0 };
17901
+ }
17855
17902
  setup() {
17856
- this.gridComposerRef = owl.useRef("gridComposer");
17857
- this.composerState = owl.useState({
17858
- rect: undefined,
17859
- delimitation: undefined,
17860
- });
17861
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
17862
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
17863
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
17864
- owl.onMounted(() => {
17865
- const el = this.gridComposerRef.el;
17866
- //TODO Should be more correct to have a props that give the parent's clientHeight and clientWidth
17867
- const maxHeight = el.parentElement.clientHeight - this.rect.y - SCROLLBAR_HIGHT;
17868
- el.style.maxHeight = (maxHeight + "px");
17869
- const maxWidth = el.parentElement.clientWidth - this.rect.x - SCROLLBAR_WIDTH;
17870
- el.style.maxWidth = (maxWidth + "px");
17871
- this.composerState.rect = {
17872
- x: this.rect.x,
17873
- y: this.rect.y,
17874
- width: el.clientWidth,
17875
- height: el.clientHeight,
17876
- };
17877
- this.composerState.delimitation = {
17878
- width: el.parentElement.clientWidth,
17879
- height: el.parentElement.clientHeight,
17880
- };
17903
+ owl.onWillUpdateProps(() => {
17904
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
17905
+ if (this.isEditing !== isEditing) {
17906
+ this.isEditing = isEditing;
17907
+ if (!isEditing) {
17908
+ this.rect = undefined;
17909
+ this.env.focusableElement.focus();
17910
+ return;
17911
+ }
17912
+ const position = this.env.model.getters.getActivePosition();
17913
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
17914
+ this.rect = this.env.model.getters.getVisibleRect(zone);
17915
+ }
17881
17916
  });
17882
17917
  }
17918
+ get composerProps() {
17919
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
17920
+ return {
17921
+ rect: this.rect && { ...this.rect },
17922
+ delimitation: {
17923
+ width,
17924
+ height,
17925
+ },
17926
+ inputStyle: this.composerStyle,
17927
+ focus: this.props.focus,
17928
+ isDefaultFocus: true,
17929
+ onComposerContentFocused: this.props.onComposerContentFocused,
17930
+ onComposerCellFocused: this.props.onComposerCellFocused,
17931
+ };
17932
+ }
17883
17933
  get containerStyle() {
17934
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
17935
+ return `
17936
+ position: absolute;
17937
+ z-index: -1000;
17938
+ `;
17939
+ }
17884
17940
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
17885
17941
  const cell = this.env.model.getters.getActiveCell();
17886
17942
  const position = this.env.model.getters.getActivePosition();
@@ -17900,12 +17956,18 @@ class GridComposer extends owl.Component {
17900
17956
  if (!isFormula) {
17901
17957
  textAlign = style.align || cell.defaultAlign;
17902
17958
  }
17959
+ const sheetDimensions = this.env.model.getters.getSheetViewDimensionWithHeaders();
17960
+ const maxWidth = sheetDimensions.width - this.rect.x;
17961
+ const maxHeight = sheetDimensions.height - this.rect.y;
17903
17962
  return `
17904
17963
  left: ${left - 1}px;
17905
17964
  top: ${top}px;
17906
17965
  min-width: ${width + 2}px;
17907
17966
  min-height: ${height + 1}px;
17908
17967
 
17968
+ max-width: ${maxWidth}px;
17969
+ max-height: ${maxHeight}px;
17970
+
17909
17971
  background: ${background};
17910
17972
  color: ${color};
17911
17973
 
@@ -17929,8 +17991,8 @@ GridComposer.template = "o-spreadsheet-GridComposer";
17929
17991
  GridComposer.components = { Composer };
17930
17992
  GridComposer.props = {
17931
17993
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
17932
- onComposerUnmounted: Function,
17933
17994
  onComposerContentFocused: Function,
17995
+ onComposerCellFocused: Function,
17934
17996
  };
17935
17997
 
17936
17998
  const CSS$2 = css /* scss */ `
@@ -22692,7 +22754,6 @@ class GridOverlay extends owl.Component {
22692
22754
  this.props.onCellDoubleClicked(col, row);
22693
22755
  }
22694
22756
  onContextMenu(ev) {
22695
- ev.preventDefault();
22696
22757
  const [col, row] = this.getCartesianCoordinates(ev);
22697
22758
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
22698
22759
  }
@@ -23626,8 +23687,8 @@ css /* scss */ `
23626
23687
  &.corner {
23627
23688
  right: 0px;
23628
23689
  bottom: 0px;
23629
- height: ${SCROLLBAR_WIDTH$1}px;
23630
- width: ${SCROLLBAR_WIDTH$1}px;
23690
+ height: ${SCROLLBAR_WIDTH}px;
23691
+ width: ${SCROLLBAR_WIDTH}px;
23631
23692
  border-top: 1px solid #e2e3e3;
23632
23693
  border-left: 1px solid #e2e3e3;
23633
23694
  }
@@ -23700,7 +23761,7 @@ class HorizontalScrollBar extends owl.Component {
23700
23761
  return {
23701
23762
  left: `${this.props.leftOffset + x}px`,
23702
23763
  bottom: "0px",
23703
- height: `${SCROLLBAR_WIDTH$1}px`,
23764
+ height: `${SCROLLBAR_WIDTH}px`,
23704
23765
  right: `0px`,
23705
23766
  };
23706
23767
  }
@@ -23745,7 +23806,7 @@ class VerticalScrollBar extends owl.Component {
23745
23806
  return {
23746
23807
  top: `${this.props.topOffset + y}px`,
23747
23808
  right: "0px",
23748
- width: `${SCROLLBAR_WIDTH$1}px`,
23809
+ width: `${SCROLLBAR_WIDTH}px`,
23749
23810
  bottom: `0px`,
23750
23811
  };
23751
23812
  }
@@ -23919,17 +23980,16 @@ class Grid extends owl.Component {
23919
23980
  menuItems: [],
23920
23981
  });
23921
23982
  this.gridRef = owl.useRef("grid");
23922
- this.hiddenInput = owl.useRef("hiddenInput");
23923
23983
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
23924
23984
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
23925
23985
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
23926
23986
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
23927
23987
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
23928
23988
  owl.useExternalListener(document.body, "paste", this.paste);
23929
- owl.onMounted(() => this.focus());
23930
- this.props.exposeFocus(() => this.focus());
23989
+ owl.onMounted(() => this.focusDefaultElement());
23990
+ this.props.exposeFocus(() => this.focusDefaultElement());
23931
23991
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
23932
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
23992
+ owl.useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
23933
23993
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
23934
23994
  this.moveCanvas(deltaX, deltaY);
23935
23995
  this.hoveredCell.col = undefined;
@@ -23944,21 +24004,20 @@ class Grid extends owl.Component {
23944
24004
  return `
23945
24005
  top: ${HEADER_HEIGHT}px;
23946
24006
  left: ${HEADER_WIDTH}px;
23947
- height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH$1}px);
23948
- width: calc(100% - ${HEADER_WIDTH + SCROLLBAR_WIDTH$1}px);
24007
+ height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH}px);
24008
+ width: calc(100% - ${HEADER_WIDTH + SCROLLBAR_WIDTH}px);
23949
24009
  `;
23950
24010
  }
23951
24011
  onClosePopover() {
23952
24012
  if (this.env.model.getters.hasOpenedPopover()) {
23953
24013
  this.closeOpenedPopover();
23954
24014
  }
23955
- this.focus();
24015
+ this.focusDefaultElement();
23956
24016
  }
23957
- focus() {
23958
- var _a;
24017
+ focusDefaultElement() {
23959
24018
  if (!this.env.model.getters.getSelectedFigureId() &&
23960
24019
  this.env.model.getters.getEditionMode() === "inactive") {
23961
- (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
24020
+ this.env.focusableElement.focus();
23962
24021
  }
23963
24022
  }
23964
24023
  get gridEl() {
@@ -24103,19 +24162,6 @@ class Grid extends owl.Component {
24103
24162
  return;
24104
24163
  }
24105
24164
  }
24106
- onInput(ev) {
24107
- // the user meant to paste in the sheet, not open the composer with the pasted content
24108
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
24109
- return;
24110
- }
24111
- if (ev.data) {
24112
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
24113
- // character
24114
- ev.preventDefault();
24115
- ev.stopPropagation();
24116
- this.props.onGridComposerCellFocused(ev.data);
24117
- }
24118
- }
24119
24165
  // ---------------------------------------------------------------------------
24120
24166
  // Context Menu
24121
24167
  // ---------------------------------------------------------------------------
@@ -24212,7 +24258,7 @@ class Grid extends owl.Component {
24212
24258
  }
24213
24259
  closeMenu() {
24214
24260
  this.menuState.isOpen = false;
24215
- this.focus();
24261
+ this.focusDefaultElement();
24216
24262
  }
24217
24263
  }
24218
24264
  Grid.template = "o-spreadsheet-Grid";
@@ -27525,7 +27571,7 @@ function normalizeV9(formula) {
27525
27571
  * a breaking change is made in the way the state is handled, and an upgrade
27526
27572
  * function should be defined
27527
27573
  */
27528
- const CURRENT_VERSION = 12;
27574
+ const CURRENT_VERSION = 12.5;
27529
27575
  const INITIAL_SHEET_ID = "Sheet1";
27530
27576
  /**
27531
27577
  * This function tries to load anything that could look like a valid
@@ -27791,6 +27837,31 @@ const MIGRATIONS = [
27791
27837
  return data;
27792
27838
  },
27793
27839
  },
27840
+ {
27841
+ description: "Fix datafilter duplication",
27842
+ from: 12,
27843
+ to: 12.5,
27844
+ applyMigration(data) {
27845
+ for (let sheet of data.sheets || []) {
27846
+ let knownDataFilterZones = [];
27847
+ for (let filterTable of sheet.filterTables || []) {
27848
+ const zone = toZone(filterTable.range);
27849
+ // See commit message for the details
27850
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
27851
+ if (intersectZoneIndex !== -1) {
27852
+ knownDataFilterZones[intersectZoneIndex] = zone;
27853
+ }
27854
+ else {
27855
+ knownDataFilterZones.push(zone);
27856
+ }
27857
+ }
27858
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
27859
+ range: zoneToXc(zone),
27860
+ }));
27861
+ }
27862
+ return data;
27863
+ },
27864
+ },
27794
27865
  ];
27795
27866
  /**
27796
27867
  * This function is used to repair faulty data independently of the migration.
@@ -28555,9 +28626,9 @@ class CellPlugin extends CorePlugin {
28555
28626
  allowDispatch(cmd) {
28556
28627
  switch (cmd.type) {
28557
28628
  case "UPDATE_CELL":
28558
- return this.checkCellOutOfSheet(cmd);
28629
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
28559
28630
  case "CLEAR_CELL":
28560
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28631
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
28561
28632
  default:
28562
28633
  return 0 /* CommandResult.Success */;
28563
28634
  }
@@ -28723,13 +28794,13 @@ class CellPlugin extends CorePlugin {
28723
28794
  /*
28724
28795
  * Reconstructs the original formula string based on a normalized form and its dependencies
28725
28796
  */
28726
- buildFormulaContent(sheetId, cell, dependencies) {
28797
+ buildFormulaContent(sheetId, cell, dependencies, useFixedZone = false) {
28727
28798
  const ranges = dependencies || cell.dependencies;
28728
28799
  let rangeIndex = 0;
28729
28800
  return concat(cell.compiledFormula.tokens.map((token) => {
28730
28801
  if (token.type === "REFERENCE") {
28731
28802
  const range = ranges[rangeIndex++];
28732
- return this.getters.getRangeString(range, sheetId);
28803
+ return this.getters.getRangeString(range, sheetId, useFixedZone);
28733
28804
  }
28734
28805
  return token.value;
28735
28806
  }));
@@ -28976,6 +29047,18 @@ class CellPlugin extends CorePlugin {
28976
29047
  }
28977
29048
  return 0 /* CommandResult.Success */;
28978
29049
  }
29050
+ checkUselessUpdateCell(cmd) {
29051
+ const cell = this.getters.getCell(cmd);
29052
+ const hasContent = "content" in cmd || "formula" in cmd;
29053
+ const hasStyle = "style" in cmd;
29054
+ const hasFormat = "format" in cmd;
29055
+ if ((!hasContent || (cell === null || cell === void 0 ? void 0 : cell.content) === cmd.content) &&
29056
+ (!hasStyle || deepEquals(cell === null || cell === void 0 ? void 0 : cell.style, cmd.style)) &&
29057
+ (!hasFormat || (cell === null || cell === void 0 ? void 0 : cell.format) === cmd.format)) {
29058
+ return 89 /* CommandResult.NoChanges */;
29059
+ }
29060
+ return 0 /* CommandResult.Success */;
29061
+ }
28979
29062
  }
28980
29063
  CellPlugin.getters = [
28981
29064
  "zoneToXC",
@@ -29302,7 +29385,13 @@ class ConditionalFormatPlugin extends CorePlugin {
29302
29385
  }
29303
29386
  }
29304
29387
  exportForExcel(data) {
29305
- this.export(data);
29388
+ if (data.sheets) {
29389
+ for (let sheet of data.sheets) {
29390
+ if (this.cfRules[sheet.id]) {
29391
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
29392
+ }
29393
+ }
29394
+ }
29306
29395
  }
29307
29396
  // ---------------------------------------------------------------------------
29308
29397
  // Getters
@@ -29352,11 +29441,11 @@ class ConditionalFormatPlugin extends CorePlugin {
29352
29441
  // ---------------------------------------------------------------------------
29353
29442
  // Private
29354
29443
  // ---------------------------------------------------------------------------
29355
- mapToConditionalFormat(sheetId, cf) {
29444
+ mapToConditionalFormat(sheetId, cf, options = { useFixedReference: false }) {
29356
29445
  return {
29357
29446
  ...cf,
29358
29447
  ranges: cf.ranges.map((range) => {
29359
- return this.getters.getRangeString(range, sheetId);
29448
+ return this.getters.getRangeString(range, sheetId, options.useFixedReference);
29360
29449
  }),
29361
29450
  };
29362
29451
  }
@@ -31267,8 +31356,9 @@ class RangeAdapter {
31267
31356
  *
31268
31357
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
31269
31358
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
31359
+ * @param [useFixedZone=false] if true, the range will be returned with fixed row and column
31270
31360
  */
31271
- getRangeString(range, forSheetId) {
31361
+ getRangeString(range, forSheetId, useFixedZone = false) {
31272
31362
  if (!range) {
31273
31363
  return INCORRECT_RANGE_STRING;
31274
31364
  }
@@ -31295,7 +31385,7 @@ class RangeAdapter {
31295
31385
  if (prefixSheet && !sheetName) {
31296
31386
  return INCORRECT_RANGE_STRING;
31297
31387
  }
31298
- let rangeString = this.getRangePartString(rangeImpl, 0);
31388
+ let rangeString = this.getRangePartString(rangeImpl, 0, useFixedZone);
31299
31389
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
31300
31390
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
31301
31391
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -31305,7 +31395,7 @@ class RangeAdapter {
31305
31395
  rangeImpl.parts[1].rowFixed ||
31306
31396
  rangeImpl.parts[1].colFixed) {
31307
31397
  rangeString += ":";
31308
- rangeString += this.getRangePartString(rangeImpl, 1);
31398
+ rangeString += this.getRangePartString(rangeImpl, 1, useFixedZone);
31309
31399
  }
31310
31400
  }
31311
31401
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -31335,14 +31425,14 @@ class RangeAdapter {
31335
31425
  /**
31336
31426
  * Get a Xc string that represent a part of a range
31337
31427
  */
31338
- getRangePartString(range, part) {
31428
+ getRangePartString(range, part, useFixedZone = false) {
31339
31429
  var _a, _b;
31340
31430
  const colFixed = range.parts && ((_a = range.parts[part]) === null || _a === void 0 ? void 0 : _a.colFixed) ? "$" : "";
31341
31431
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
31342
31432
  const rowFixed = range.parts && ((_b = range.parts[part]) === null || _b === void 0 ? void 0 : _b.rowFixed) ? "$" : "";
31343
31433
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
31344
31434
  let str = "";
31345
- if (range.isFullCol) {
31435
+ if (range.isFullCol && !useFixedZone) {
31346
31436
  if (part === 0 && range.unboundedZone.hasHeader) {
31347
31437
  str = colFixed + col + rowFixed + row;
31348
31438
  }
@@ -31350,7 +31440,7 @@ class RangeAdapter {
31350
31440
  str = colFixed + col;
31351
31441
  }
31352
31442
  }
31353
- else if (range.isFullRow) {
31443
+ else if (range.isFullRow && !useFixedZone) {
31354
31444
  if (part === 0 && range.unboundedZone.hasHeader) {
31355
31445
  str = colFixed + col + rowFixed + row;
31356
31446
  }
@@ -32725,6 +32815,10 @@ class EvaluationPlugin extends UIPlugin {
32725
32815
  if (cell.format !== evaluatedCell.format) {
32726
32816
  exportedCellData.computedFormat = evaluatedCell.format;
32727
32817
  }
32818
+ // if there is a formula but no dependencies (maybe because the cell is in error), no need to recompute the formula text
32819
+ if (cell.isFormula && cell.dependencies.length) {
32820
+ exportedCellData.content = this.getters.buildFormulaContent(sheet.id, cell, cell.dependencies, true);
32821
+ }
32728
32822
  }
32729
32823
  }
32730
32824
  }
@@ -34585,8 +34679,7 @@ class AutofillPlugin extends UIPlugin {
34585
34679
  let row = zone.bottom;
34586
34680
  if (col > 0) {
34587
34681
  let leftPosition = { sheetId, col: col - 1, row };
34588
- while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
34589
- ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content)) {
34682
+ while ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content) {
34590
34683
  row += 1;
34591
34684
  leftPosition = { sheetId, col: col - 1, row };
34592
34685
  }
@@ -34595,8 +34688,7 @@ class AutofillPlugin extends UIPlugin {
34595
34688
  col = zone.right;
34596
34689
  if (col <= this.getters.getNumberCols(sheetId)) {
34597
34690
  let rightPosition = { sheetId, col: col + 1, row };
34598
- while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
34599
- ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content)) {
34691
+ while ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content) {
34600
34692
  row += 1;
34601
34693
  rightPosition = { sheetId, col: col + 1, row };
34602
34694
  }
@@ -36288,9 +36380,6 @@ class SelectionInputPlugin extends UIPlugin {
36288
36380
  }
36289
36381
  }
36290
36382
  }
36291
- unsubscribe() {
36292
- this.unfocus();
36293
- }
36294
36383
  // ---------------------------------------------------------------------------
36295
36384
  // Getters || only callable by the parent
36296
36385
  // ---------------------------------------------------------------------------
@@ -36497,9 +36586,9 @@ class SelectionInputsManagerPlugin extends UIPlugin {
36497
36586
  break;
36498
36587
  case "FOCUS_RANGE":
36499
36588
  case "CHANGE_RANGE":
36500
- if (cmd.id !== this.focusedInputId) {
36501
- const input = this.inputs[cmd.id];
36502
- const range = input.ranges.find((range) => range.id === cmd.rangeId);
36589
+ const input = this.inputs[cmd.id];
36590
+ const range = input.ranges.find((range) => range.id === cmd.rangeId);
36591
+ if (range) {
36503
36592
  const sheetId = this.getters.getActiveSheetId();
36504
36593
  const zone = this.getters.getRangeFromSheetXC(sheetId, (range === null || range === void 0 ? void 0 : range.xc) || "A1").zone;
36505
36594
  this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, { handleEvent: input.handleEvent.bind(input) });
@@ -36509,9 +36598,6 @@ class SelectionInputsManagerPlugin extends UIPlugin {
36509
36598
  }
36510
36599
  (_a = this.currentInput) === null || _a === void 0 ? void 0 : _a.handle(cmd);
36511
36600
  }
36512
- unsubscribe() {
36513
- this.unfocus();
36514
- }
36515
36601
  // ---------------------------------------------------------------------------
36516
36602
  // Getters
36517
36603
  // ---------------------------------------------------------------------------
@@ -39582,6 +39668,19 @@ class ImageProvider {
39582
39668
  }
39583
39669
  }
39584
39670
 
39671
+ class FocusableElement {
39672
+ constructor() {
39673
+ this.focusableElement = undefined;
39674
+ }
39675
+ setFocusableElement(element) {
39676
+ this.focusableElement = element;
39677
+ }
39678
+ focus() {
39679
+ var _a;
39680
+ (_a = this.focusableElement) === null || _a === void 0 ? void 0 : _a.focus();
39681
+ }
39682
+ }
39683
+
39585
39684
  // -----------------------------------------------------------------------------
39586
39685
  // SpreadSheet
39587
39686
  // -----------------------------------------------------------------------------
@@ -40825,8 +40924,8 @@ css /* scss */ `
40825
40924
  &.corner {
40826
40925
  right: 0px;
40827
40926
  bottom: 0px;
40828
- height: ${SCROLLBAR_WIDTH$1}px;
40829
- width: ${SCROLLBAR_WIDTH$1}px;
40927
+ height: ${SCROLLBAR_WIDTH}px;
40928
+ width: ${SCROLLBAR_WIDTH}px;
40830
40929
  border-top: 1px solid #e2e3e3;
40831
40930
  border-left: 1px solid #e2e3e3;
40832
40931
  }
@@ -40873,6 +40972,7 @@ class Spreadsheet extends owl.Component {
40873
40972
  toggleSidePanel: this.toggleSidePanel.bind(this),
40874
40973
  _t: Spreadsheet._t,
40875
40974
  clipboard: this.env.clipboard || instantiateClipboard(),
40975
+ focusableElement: new FocusableElement(),
40876
40976
  });
40877
40977
  owl.useExternalListener(window, "resize", () => this.render(true));
40878
40978
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -40971,17 +41071,18 @@ class Spreadsheet extends owl.Component {
40971
41071
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
40972
41072
  this.composer.topBarFocus = "contentFocus";
40973
41073
  this.composer.gridFocusMode = "inactive";
40974
- this.setComposerContent({ selection } || {});
41074
+ this.setComposerContent({ selection });
40975
41075
  }
40976
- onGridComposerContentFocused() {
41076
+ onGridComposerContentFocused(selection) {
40977
41077
  if (this.model.getters.isReadonly()) {
40978
41078
  return;
40979
41079
  }
40980
41080
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
40981
41081
  this.composer.topBarFocus = "inactive";
40982
41082
  this.composer.gridFocusMode = "contentFocus";
40983
- this.setComposerContent({});
41083
+ this.setComposerContent({ selection });
40984
41084
  }
41085
+ // TODO: either both are defined or none of them. change those args to an object
40985
41086
  onGridComposerCellFocused(content, selection) {
40986
41087
  if (this.model.getters.isReadonly()) {
40987
41088
  return;
@@ -41962,18 +42063,17 @@ class EventStream {
41962
42063
  if (this.observers.find((sub) => sub.owner === owner)) {
41963
42064
  throw new Error("You are already subscribed forever");
41964
42065
  }
41965
- if ((_a = this.mainSubscription) === null || _a === void 0 ? void 0 : _a.owner) {
42066
+ if (((_a = this.mainSubscription) === null || _a === void 0 ? void 0 : _a.owner) && this.mainSubscription.owner !== owner) {
41966
42067
  (_c = (_b = this.mainSubscription.callbacks).release) === null || _c === void 0 ? void 0 : _c.call(_b);
41967
42068
  }
41968
42069
  this.mainSubscription = { owner, callbacks };
41969
42070
  }
41970
42071
  release(owner) {
41971
- var _a, _b, _c, _d;
42072
+ var _a;
41972
42073
  if (((_a = this.mainSubscription) === null || _a === void 0 ? void 0 : _a.owner) !== owner ||
41973
42074
  this.observers.find((sub) => sub.owner === owner)) {
41974
42075
  return;
41975
42076
  }
41976
- (_d = (_b = this.mainSubscription) === null || _b === void 0 ? void 0 : (_c = _b.callbacks).release) === null || _d === void 0 ? void 0 : _d.call(_c);
41977
42077
  this.mainSubscription = this.defaultSubscription;
41978
42078
  }
41979
42079
  /**
@@ -42441,11 +42541,10 @@ class SelectionStreamProcessor {
42441
42541
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
42442
42542
  */
42443
42543
  getEndOfCluster(startPosition, dim, dir) {
42444
- const sheet = this.getters.getActiveSheet();
42445
42544
  let currentPosition = startPosition;
42446
42545
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
42447
42546
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
42448
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
42547
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
42449
42548
  ? "endOfCluster"
42450
42549
  : "nextCluster";
42451
42550
  while (true) {
@@ -42455,7 +42554,7 @@ class SelectionStreamProcessor {
42455
42554
  currentPosition.row === nextCellPosition.row) {
42456
42555
  break;
42457
42556
  }
42458
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
42557
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
42459
42558
  if (mode === "endOfCluster" && isNextCellEmpty) {
42460
42559
  break;
42461
42560
  }
@@ -42469,14 +42568,25 @@ class SelectionStreamProcessor {
42469
42568
  return dim === "cols" ? currentPosition.col : currentPosition.row;
42470
42569
  }
42471
42570
  /**
42472
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
42473
- * check if the merge containing the cell is empty.
42571
+ * Check if a cell evaluated value is empty. If the cell is part of a merge,
42572
+ * the check applies to the main cell of the merge.
42474
42573
  */
42475
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
42574
+ isEvaluatedCellEmpty({ col, row }) {
42575
+ const sheetId = this.getters.getActiveSheetId();
42476
42576
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
42477
42577
  const cell = this.getters.getEvaluatedCell(position);
42478
42578
  return cell.type === CellValueType.empty;
42479
42579
  }
42580
+ /**
42581
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
42582
+ * the check applies to the main cell of the merge.
42583
+ */
42584
+ isCellEmpty({ col, row }) {
42585
+ var _a;
42586
+ const sheetId = this.getters.getActiveSheetId();
42587
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
42588
+ return !((_a = this.getters.getCell(position)) === null || _a === void 0 ? void 0 : _a.content);
42589
+ }
42480
42590
  /** Computes the next cell position in the given direction by crossing through merges and skipping hidden cells.
42481
42591
  *
42482
42592
  * This has the same behaviour as getNextAvailablePosition() for certain arguments, but use this method instead
@@ -42955,12 +43065,7 @@ function addFormula(cell) {
42955
43065
  attrs.push(["t", "str"]);
42956
43066
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
42957
43067
  }
42958
- node = escapeXml /*xml*/ `
42959
- <f>
42960
- ${XlsxFormula}
42961
- </f>
42962
- ${cycle}
42963
- `;
43068
+ node = escapeXml /*xml*/ `<f> ${XlsxFormula} </f> ${cycle}`;
42964
43069
  return { attrs, node };
42965
43070
  }
42966
43071
  else {
@@ -43701,11 +43806,10 @@ function addRows(construct, data, sheet) {
43701
43806
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
43702
43807
  }
43703
43808
  attributes.push(...additionalAttrs);
43704
- cellNodes.push(escapeXml /*xml*/ `
43705
- <c ${formatAttributes(attributes)}>
43706
- ${cellNode}
43707
- </c>
43708
- `);
43809
+ // prettier-ignore
43810
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
43811
+ ${cellNode}
43812
+ </c>`);
43709
43813
  }
43710
43814
  }
43711
43815
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -44462,7 +44566,7 @@ const SPREADSHEET_DIMENSIONS = {
44462
44566
  BOTTOMBAR_HEIGHT,
44463
44567
  DEFAULT_CELL_WIDTH,
44464
44568
  DEFAULT_CELL_HEIGHT,
44465
- SCROLLBAR_WIDTH: SCROLLBAR_WIDTH$1,
44569
+ SCROLLBAR_WIDTH,
44466
44570
  };
44467
44571
  const registries = {
44468
44572
  autofillModifiersRegistry,
@@ -44577,6 +44681,6 @@ exports.setTranslationMethod = setTranslationMethod;
44577
44681
  exports.tokenize = tokenize;
44578
44682
 
44579
44683
 
44580
- __info__.version = '16.1.33';
44581
- __info__.date = '2024-01-29T13:49:43.259Z';
44582
- __info__.hash = 'd3b27dd';
44684
+ __info__.version = '16.1.36';
44685
+ __info__.date = '2024-02-16T14:16:11.082Z';
44686
+ __info__.hash = 'ad5301c';