@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.
@@ -203,7 +203,7 @@ const TOPBAR_HEIGHT = 63;
203
203
  const BOTTOMBAR_HEIGHT = 36;
204
204
  const DEFAULT_CELL_WIDTH = 96;
205
205
  const DEFAULT_CELL_HEIGHT = 23;
206
- const SCROLLBAR_WIDTH$1 = 15;
206
+ const SCROLLBAR_WIDTH = 15;
207
207
  const AUTOFILL_EDGE_LENGTH = 8;
208
208
  const ICON_EDGE_LENGTH = 18;
209
209
  const UNHIDE_ICON_EDGE_LENGTH = 14;
@@ -1672,6 +1672,9 @@ function parseFormat(formatString) {
1672
1672
  function formatValue(value, format) {
1673
1673
  switch (typeof value) {
1674
1674
  case "string":
1675
+ if (value.includes('\\"')) {
1676
+ return value.replace(/\\"/g, '"');
1677
+ }
1675
1678
  return value;
1676
1679
  case "boolean":
1677
1680
  return value ? "TRUE" : "FALSE";
@@ -3874,14 +3877,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
3874
3877
  : undefined));
3875
3878
  }
3876
3879
  }
3877
- else if (zone.left === zone.right && zone.top === zone.bottom) {
3878
- // A single cell. If it's only the title, the dataset is not added.
3879
- if (!dataSetsHaveTitle) {
3880
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
3881
- }
3882
- }
3883
3880
  else {
3884
- /* 1 row or 1 column */
3881
+ /* 1 cell, 1 row or 1 column */
3885
3882
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
3886
3883
  ? {
3887
3884
  top: zone.top,
@@ -3931,8 +3928,10 @@ function toExcelDataset(getters, ds) {
3931
3928
  }
3932
3929
  const dataRange = ds.dataRange.clone({ zone: dataZone });
3933
3930
  return {
3934
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
3935
- range: getters.getRangeString(dataRange, "forceSheetReference"),
3931
+ label: ds.labelCell
3932
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", true)
3933
+ : undefined,
3934
+ range: getters.getRangeString(dataRange, "forceSheetReference", true),
3936
3935
  };
3937
3936
  }
3938
3937
  /**
@@ -4429,6 +4428,9 @@ class BarChart extends AbstractChart {
4429
4428
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
4430
4429
  return {
4431
4430
  ...this.getDefinition(),
4431
+ labelRange: this.labelRange
4432
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
4433
+ : undefined,
4432
4434
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
4433
4435
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
4434
4436
  dataSets,
@@ -4987,6 +4989,9 @@ class LineChart extends AbstractChart {
4987
4989
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
4988
4990
  return {
4989
4991
  ...this.getDefinition(),
4992
+ labelRange: this.labelRange
4993
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
4994
+ : undefined,
4990
4995
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
4991
4996
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
4992
4997
  dataSets,
@@ -5250,6 +5255,9 @@ class PieChart extends AbstractChart {
5250
5255
  .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
5251
5256
  return {
5252
5257
  ...this.getDefinition(),
5258
+ labelRange: this.labelRange
5259
+ ? this.getters.getRangeString(this.labelRange, this.sheetId, true)
5260
+ : undefined,
5253
5261
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
5254
5262
  fontColor: toXlsxHexColor(chartFontColor(this.background)),
5255
5263
  verticalAxisPosition: "left",
@@ -7255,6 +7263,12 @@ const INSERT_LINK = (env) => {
7255
7263
  let { col, row } = env.model.getters.getActivePosition();
7256
7264
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
7257
7265
  };
7266
+ const INSERT_LINK_NAME = (env) => {
7267
+ const sheetId = env.model.getters.getActiveSheetId();
7268
+ const { col, row } = env.model.getters.getActivePosition();
7269
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
7270
+ return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
7271
+ };
7258
7272
  //------------------------------------------------------------------------------
7259
7273
  // Filters action
7260
7274
  //------------------------------------------------------------------------------
@@ -7392,7 +7406,7 @@ cellMenuRegistry
7392
7406
  action: DELETE_CELL_SHIFT_LEFT,
7393
7407
  })
7394
7408
  .add("insert_link", {
7395
- name: _lt("Insert link"),
7409
+ name: INSERT_LINK_NAME,
7396
7410
  separator: true,
7397
7411
  sequence: 150,
7398
7412
  action: INSERT_LINK,
@@ -16551,9 +16565,6 @@ class EditionPlugin extends UIPlugin {
16551
16565
  break;
16552
16566
  }
16553
16567
  }
16554
- unsubscribe() {
16555
- this.mode = "inactive";
16556
- }
16557
16568
  // ---------------------------------------------------------------------------
16558
16569
  // Getters
16559
16570
  // ---------------------------------------------------------------------------
@@ -17446,13 +17457,12 @@ class Composer extends Component {
17446
17457
  setup() {
17447
17458
  onMounted(() => {
17448
17459
  const el = this.composerRef.el;
17460
+ if (this.props.isDefaultFocus) {
17461
+ this.env.focusableElement.setFocusableElement(el);
17462
+ }
17449
17463
  this.contentHelper.updateEl(el);
17450
17464
  this.processContent();
17451
17465
  });
17452
- onWillUnmount(() => {
17453
- var _a, _b;
17454
- (_b = (_a = this.props).onComposerUnmounted) === null || _b === void 0 ? void 0 : _b.call(_a);
17455
- });
17456
17466
  onPatched(() => {
17457
17467
  if (!this.isKeyStillDown) {
17458
17468
  this.processContent();
@@ -17463,7 +17473,8 @@ class Composer extends Component {
17463
17473
  // Handlers
17464
17474
  // ---------------------------------------------------------------------------
17465
17475
  processArrowKeys(ev) {
17466
- if (this.env.model.getters.isSelectingForComposer()) {
17476
+ if (this.env.model.getters.isSelectingForComposer() ||
17477
+ this.env.model.getters.getEditionMode() === "inactive") {
17467
17478
  this.functionDescriptionState.showDescription = false;
17468
17479
  // Prevent the default content editable behavior which moves the cursor
17469
17480
  ev.preventDefault();
@@ -17504,21 +17515,23 @@ class Composer extends Component {
17504
17515
  var _a;
17505
17516
  ev.preventDefault();
17506
17517
  ev.stopPropagation();
17507
- if (this.autoCompleteState.showProvider) {
17508
- const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
17509
- if (autoCompleteValue) {
17510
- this.autoComplete(autoCompleteValue);
17511
- return;
17518
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
17519
+ if (this.autoCompleteState.showProvider) {
17520
+ const autoCompleteValue = (_a = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
17521
+ if (autoCompleteValue) {
17522
+ this.autoComplete(autoCompleteValue);
17523
+ return;
17524
+ }
17512
17525
  }
17513
- }
17514
- else {
17515
- // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
17516
- // we can't let the model think that it is for a ref selection.
17517
- // todo: check if this can be removed someday
17518
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17526
+ else {
17527
+ // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
17528
+ // we can't let the model think that it is for a ref selection.
17529
+ // todo: check if this can be removed someday
17530
+ this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17531
+ }
17532
+ this.env.model.dispatch("STOP_EDITION");
17519
17533
  }
17520
17534
  const direction = ev.shiftKey ? "left" : "right";
17521
- this.env.model.dispatch("STOP_EDITION");
17522
17535
  this.env.model.selection.moveAnchorCell(direction, 1);
17523
17536
  }
17524
17537
  processEnterKey(ev) {
@@ -17551,6 +17564,9 @@ class Composer extends Component {
17551
17564
  this.compositionActive = false;
17552
17565
  }
17553
17566
  onKeydown(ev) {
17567
+ if (this.env.model.getters.getEditionMode() === "inactive") {
17568
+ return;
17569
+ }
17554
17570
  let handler = this.keyMapping[ev.key];
17555
17571
  if (handler) {
17556
17572
  handler.call(this, ev);
@@ -17561,23 +17577,46 @@ class Composer extends Component {
17561
17577
  }
17562
17578
  }
17563
17579
  updateCursorIfNeeded() {
17564
- if (!this.env.model.getters.isSelectingForComposer()) {
17580
+ const moveCursor = !this.env.model.getters.isSelectingForComposer() &&
17581
+ !(this.env.model.getters.getEditionMode() === "inactive");
17582
+ if (moveCursor) {
17565
17583
  const { start, end } = this.contentHelper.getCurrentSelection();
17566
17584
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
17567
17585
  this.isKeyStillDown = true;
17568
17586
  }
17569
17587
  }
17588
+ onPaste(ev) {
17589
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
17590
+ ev.stopPropagation();
17591
+ }
17592
+ }
17570
17593
  /*
17571
17594
  * Triggered automatically by the content-editable between the keydown and key up
17572
17595
  * */
17573
- onInput() {
17574
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
17596
+ onInput(ev) {
17597
+ var _a, _b;
17598
+ if (!this.shouldProcessInputEvents) {
17575
17599
  return;
17576
17600
  }
17601
+ if (ev.inputType === "insertFromPaste" &&
17602
+ this.env.model.getters.getEditionMode() === "inactive") {
17603
+ return;
17604
+ }
17605
+ ev.stopPropagation();
17606
+ let content;
17607
+ if (this.env.model.getters.getEditionMode() === "inactive") {
17608
+ content = ev.data || "";
17609
+ }
17610
+ else {
17611
+ const el = this.composerRef.el;
17612
+ content = el.childNodes.length ? el.textContent : "";
17613
+ }
17614
+ if (this.props.focus === "inactive") {
17615
+ return (_b = (_a = this.props).onComposerCellFocused) === null || _b === void 0 ? void 0 : _b.call(_a, content);
17616
+ }
17577
17617
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17578
- const el = this.composerRef.el;
17579
17618
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
17580
- content: el.childNodes.length ? el.textContent : "",
17619
+ content,
17581
17620
  selection: this.contentHelper.getCurrentSelection(),
17582
17621
  });
17583
17622
  }
@@ -17637,9 +17676,8 @@ class Composer extends Component {
17637
17676
  }
17638
17677
  const newSelection = this.contentHelper.getCurrentSelection();
17639
17678
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
17640
- if (this.props.focus === "inactive") {
17641
- this.props.onComposerContentFocused(newSelection);
17642
- }
17679
+ this.props.onComposerContentFocused();
17680
+ if (this.props.focus === "inactive") ;
17643
17681
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
17644
17682
  this.processTokenAtCursor();
17645
17683
  }
@@ -17656,6 +17694,7 @@ class Composer extends Component {
17656
17694
  this.contentHelper.removeAll(); // removes the content of the composer, to be added just after
17657
17695
  this.shouldProcessInputEvents = false;
17658
17696
  if (this.props.focus !== "inactive") {
17697
+ this.contentHelper.el.focus();
17659
17698
  this.contentHelper.selectRange(0, 0); // move the cursor inside the composer at 0 0.
17660
17699
  }
17661
17700
  const content = this.getContent();
@@ -17822,18 +17861,18 @@ Composer.template = "o-spreadsheet-Composer";
17822
17861
  Composer.components = { TextValueProvider, FunctionDescriptionProvider };
17823
17862
  Composer.defaultProps = {
17824
17863
  inputStyle: "",
17864
+ isDefaultFocus: false,
17825
17865
  };
17826
17866
  Composer.props = {
17827
17867
  inputStyle: { type: String, optional: true },
17828
17868
  rect: { type: Object, optional: true },
17829
17869
  delimitation: { type: Object, optional: true },
17830
17870
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
17831
- onComposerUnmounted: { type: Function, optional: true },
17871
+ onComposerCellFocused: { type: Function, optional: true },
17832
17872
  onComposerContentFocused: Function,
17873
+ isDefaultFocus: { type: Boolean, optional: true },
17833
17874
  };
17834
17875
 
17835
- const SCROLLBAR_WIDTH = 14;
17836
- const SCROLLBAR_HIGHT = 15;
17837
17876
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
17838
17877
  css /* scss */ `
17839
17878
  div.o-grid-composer {
@@ -17848,35 +17887,52 @@ css /* scss */ `
17848
17887
  * It also applies the style of the cell to the composer input.
17849
17888
  */
17850
17889
  class GridComposer extends Component {
17890
+ constructor() {
17891
+ super(...arguments);
17892
+ this.rect = undefined;
17893
+ this.isEditing = false;
17894
+ }
17895
+ get defaultRect() {
17896
+ return { x: 0, y: 0, width: 0, height: 0 };
17897
+ }
17851
17898
  setup() {
17852
- this.gridComposerRef = useRef("gridComposer");
17853
- this.composerState = useState({
17854
- rect: undefined,
17855
- delimitation: undefined,
17856
- });
17857
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
17858
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
17859
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
17860
- onMounted(() => {
17861
- const el = this.gridComposerRef.el;
17862
- //TODO Should be more correct to have a props that give the parent's clientHeight and clientWidth
17863
- const maxHeight = el.parentElement.clientHeight - this.rect.y - SCROLLBAR_HIGHT;
17864
- el.style.maxHeight = (maxHeight + "px");
17865
- const maxWidth = el.parentElement.clientWidth - this.rect.x - SCROLLBAR_WIDTH;
17866
- el.style.maxWidth = (maxWidth + "px");
17867
- this.composerState.rect = {
17868
- x: this.rect.x,
17869
- y: this.rect.y,
17870
- width: el.clientWidth,
17871
- height: el.clientHeight,
17872
- };
17873
- this.composerState.delimitation = {
17874
- width: el.parentElement.clientWidth,
17875
- height: el.parentElement.clientHeight,
17876
- };
17899
+ onWillUpdateProps(() => {
17900
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
17901
+ if (this.isEditing !== isEditing) {
17902
+ this.isEditing = isEditing;
17903
+ if (!isEditing) {
17904
+ this.rect = undefined;
17905
+ this.env.focusableElement.focus();
17906
+ return;
17907
+ }
17908
+ const position = this.env.model.getters.getActivePosition();
17909
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
17910
+ this.rect = this.env.model.getters.getVisibleRect(zone);
17911
+ }
17877
17912
  });
17878
17913
  }
17914
+ get composerProps() {
17915
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
17916
+ return {
17917
+ rect: this.rect && { ...this.rect },
17918
+ delimitation: {
17919
+ width,
17920
+ height,
17921
+ },
17922
+ inputStyle: this.composerStyle,
17923
+ focus: this.props.focus,
17924
+ isDefaultFocus: true,
17925
+ onComposerContentFocused: this.props.onComposerContentFocused,
17926
+ onComposerCellFocused: this.props.onComposerCellFocused,
17927
+ };
17928
+ }
17879
17929
  get containerStyle() {
17930
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
17931
+ return `
17932
+ position: absolute;
17933
+ z-index: -1000;
17934
+ `;
17935
+ }
17880
17936
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
17881
17937
  const cell = this.env.model.getters.getActiveCell();
17882
17938
  const position = this.env.model.getters.getActivePosition();
@@ -17896,12 +17952,18 @@ class GridComposer extends Component {
17896
17952
  if (!isFormula) {
17897
17953
  textAlign = style.align || cell.defaultAlign;
17898
17954
  }
17955
+ const sheetDimensions = this.env.model.getters.getSheetViewDimensionWithHeaders();
17956
+ const maxWidth = sheetDimensions.width - this.rect.x;
17957
+ const maxHeight = sheetDimensions.height - this.rect.y;
17899
17958
  return `
17900
17959
  left: ${left - 1}px;
17901
17960
  top: ${top}px;
17902
17961
  min-width: ${width + 2}px;
17903
17962
  min-height: ${height + 1}px;
17904
17963
 
17964
+ max-width: ${maxWidth}px;
17965
+ max-height: ${maxHeight}px;
17966
+
17905
17967
  background: ${background};
17906
17968
  color: ${color};
17907
17969
 
@@ -17925,8 +17987,8 @@ GridComposer.template = "o-spreadsheet-GridComposer";
17925
17987
  GridComposer.components = { Composer };
17926
17988
  GridComposer.props = {
17927
17989
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
17928
- onComposerUnmounted: Function,
17929
17990
  onComposerContentFocused: Function,
17991
+ onComposerCellFocused: Function,
17930
17992
  };
17931
17993
 
17932
17994
  const CSS$2 = css /* scss */ `
@@ -22688,7 +22750,6 @@ class GridOverlay extends Component {
22688
22750
  this.props.onCellDoubleClicked(col, row);
22689
22751
  }
22690
22752
  onContextMenu(ev) {
22691
- ev.preventDefault();
22692
22753
  const [col, row] = this.getCartesianCoordinates(ev);
22693
22754
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
22694
22755
  }
@@ -23622,8 +23683,8 @@ css /* scss */ `
23622
23683
  &.corner {
23623
23684
  right: 0px;
23624
23685
  bottom: 0px;
23625
- height: ${SCROLLBAR_WIDTH$1}px;
23626
- width: ${SCROLLBAR_WIDTH$1}px;
23686
+ height: ${SCROLLBAR_WIDTH}px;
23687
+ width: ${SCROLLBAR_WIDTH}px;
23627
23688
  border-top: 1px solid #e2e3e3;
23628
23689
  border-left: 1px solid #e2e3e3;
23629
23690
  }
@@ -23696,7 +23757,7 @@ class HorizontalScrollBar extends Component {
23696
23757
  return {
23697
23758
  left: `${this.props.leftOffset + x}px`,
23698
23759
  bottom: "0px",
23699
- height: `${SCROLLBAR_WIDTH$1}px`,
23760
+ height: `${SCROLLBAR_WIDTH}px`,
23700
23761
  right: `0px`,
23701
23762
  };
23702
23763
  }
@@ -23741,7 +23802,7 @@ class VerticalScrollBar extends Component {
23741
23802
  return {
23742
23803
  top: `${this.props.topOffset + y}px`,
23743
23804
  right: "0px",
23744
- width: `${SCROLLBAR_WIDTH$1}px`,
23805
+ width: `${SCROLLBAR_WIDTH}px`,
23745
23806
  bottom: `0px`,
23746
23807
  };
23747
23808
  }
@@ -23915,17 +23976,16 @@ class Grid extends Component {
23915
23976
  menuItems: [],
23916
23977
  });
23917
23978
  this.gridRef = useRef("grid");
23918
- this.hiddenInput = useRef("hiddenInput");
23919
23979
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
23920
23980
  this.hoveredCell = useState({ col: undefined, row: undefined });
23921
23981
  useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
23922
23982
  useExternalListener(document.body, "cut", this.copy.bind(this, true));
23923
23983
  useExternalListener(document.body, "copy", this.copy.bind(this, false));
23924
23984
  useExternalListener(document.body, "paste", this.paste);
23925
- onMounted(() => this.focus());
23926
- this.props.exposeFocus(() => this.focus());
23985
+ onMounted(() => this.focusDefaultElement());
23986
+ this.props.exposeFocus(() => this.focusDefaultElement());
23927
23987
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
23928
- useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
23988
+ useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
23929
23989
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
23930
23990
  this.moveCanvas(deltaX, deltaY);
23931
23991
  this.hoveredCell.col = undefined;
@@ -23940,21 +24000,20 @@ class Grid extends Component {
23940
24000
  return `
23941
24001
  top: ${HEADER_HEIGHT}px;
23942
24002
  left: ${HEADER_WIDTH}px;
23943
- height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH$1}px);
23944
- width: calc(100% - ${HEADER_WIDTH + SCROLLBAR_WIDTH$1}px);
24003
+ height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH}px);
24004
+ width: calc(100% - ${HEADER_WIDTH + SCROLLBAR_WIDTH}px);
23945
24005
  `;
23946
24006
  }
23947
24007
  onClosePopover() {
23948
24008
  if (this.env.model.getters.hasOpenedPopover()) {
23949
24009
  this.closeOpenedPopover();
23950
24010
  }
23951
- this.focus();
24011
+ this.focusDefaultElement();
23952
24012
  }
23953
- focus() {
23954
- var _a;
24013
+ focusDefaultElement() {
23955
24014
  if (!this.env.model.getters.getSelectedFigureId() &&
23956
24015
  this.env.model.getters.getEditionMode() === "inactive") {
23957
- (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
24016
+ this.env.focusableElement.focus();
23958
24017
  }
23959
24018
  }
23960
24019
  get gridEl() {
@@ -24099,19 +24158,6 @@ class Grid extends Component {
24099
24158
  return;
24100
24159
  }
24101
24160
  }
24102
- onInput(ev) {
24103
- // the user meant to paste in the sheet, not open the composer with the pasted content
24104
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
24105
- return;
24106
- }
24107
- if (ev.data) {
24108
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
24109
- // character
24110
- ev.preventDefault();
24111
- ev.stopPropagation();
24112
- this.props.onGridComposerCellFocused(ev.data);
24113
- }
24114
- }
24115
24161
  // ---------------------------------------------------------------------------
24116
24162
  // Context Menu
24117
24163
  // ---------------------------------------------------------------------------
@@ -24208,7 +24254,7 @@ class Grid extends Component {
24208
24254
  }
24209
24255
  closeMenu() {
24210
24256
  this.menuState.isOpen = false;
24211
- this.focus();
24257
+ this.focusDefaultElement();
24212
24258
  }
24213
24259
  }
24214
24260
  Grid.template = "o-spreadsheet-Grid";
@@ -27521,7 +27567,7 @@ function normalizeV9(formula) {
27521
27567
  * a breaking change is made in the way the state is handled, and an upgrade
27522
27568
  * function should be defined
27523
27569
  */
27524
- const CURRENT_VERSION = 12;
27570
+ const CURRENT_VERSION = 12.5;
27525
27571
  const INITIAL_SHEET_ID = "Sheet1";
27526
27572
  /**
27527
27573
  * This function tries to load anything that could look like a valid
@@ -27787,6 +27833,31 @@ const MIGRATIONS = [
27787
27833
  return data;
27788
27834
  },
27789
27835
  },
27836
+ {
27837
+ description: "Fix datafilter duplication",
27838
+ from: 12,
27839
+ to: 12.5,
27840
+ applyMigration(data) {
27841
+ for (let sheet of data.sheets || []) {
27842
+ let knownDataFilterZones = [];
27843
+ for (let filterTable of sheet.filterTables || []) {
27844
+ const zone = toZone(filterTable.range);
27845
+ // See commit message for the details
27846
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
27847
+ if (intersectZoneIndex !== -1) {
27848
+ knownDataFilterZones[intersectZoneIndex] = zone;
27849
+ }
27850
+ else {
27851
+ knownDataFilterZones.push(zone);
27852
+ }
27853
+ }
27854
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
27855
+ range: zoneToXc(zone),
27856
+ }));
27857
+ }
27858
+ return data;
27859
+ },
27860
+ },
27790
27861
  ];
27791
27862
  /**
27792
27863
  * This function is used to repair faulty data independently of the migration.
@@ -28551,9 +28622,9 @@ class CellPlugin extends CorePlugin {
28551
28622
  allowDispatch(cmd) {
28552
28623
  switch (cmd.type) {
28553
28624
  case "UPDATE_CELL":
28554
- return this.checkCellOutOfSheet(cmd);
28625
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
28555
28626
  case "CLEAR_CELL":
28556
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28627
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
28557
28628
  default:
28558
28629
  return 0 /* CommandResult.Success */;
28559
28630
  }
@@ -28719,13 +28790,13 @@ class CellPlugin extends CorePlugin {
28719
28790
  /*
28720
28791
  * Reconstructs the original formula string based on a normalized form and its dependencies
28721
28792
  */
28722
- buildFormulaContent(sheetId, cell, dependencies) {
28793
+ buildFormulaContent(sheetId, cell, dependencies, useFixedZone = false) {
28723
28794
  const ranges = dependencies || cell.dependencies;
28724
28795
  let rangeIndex = 0;
28725
28796
  return concat(cell.compiledFormula.tokens.map((token) => {
28726
28797
  if (token.type === "REFERENCE") {
28727
28798
  const range = ranges[rangeIndex++];
28728
- return this.getters.getRangeString(range, sheetId);
28799
+ return this.getters.getRangeString(range, sheetId, useFixedZone);
28729
28800
  }
28730
28801
  return token.value;
28731
28802
  }));
@@ -28972,6 +29043,18 @@ class CellPlugin extends CorePlugin {
28972
29043
  }
28973
29044
  return 0 /* CommandResult.Success */;
28974
29045
  }
29046
+ checkUselessUpdateCell(cmd) {
29047
+ const cell = this.getters.getCell(cmd);
29048
+ const hasContent = "content" in cmd || "formula" in cmd;
29049
+ const hasStyle = "style" in cmd;
29050
+ const hasFormat = "format" in cmd;
29051
+ if ((!hasContent || (cell === null || cell === void 0 ? void 0 : cell.content) === cmd.content) &&
29052
+ (!hasStyle || deepEquals(cell === null || cell === void 0 ? void 0 : cell.style, cmd.style)) &&
29053
+ (!hasFormat || (cell === null || cell === void 0 ? void 0 : cell.format) === cmd.format)) {
29054
+ return 89 /* CommandResult.NoChanges */;
29055
+ }
29056
+ return 0 /* CommandResult.Success */;
29057
+ }
28975
29058
  }
28976
29059
  CellPlugin.getters = [
28977
29060
  "zoneToXC",
@@ -29298,7 +29381,13 @@ class ConditionalFormatPlugin extends CorePlugin {
29298
29381
  }
29299
29382
  }
29300
29383
  exportForExcel(data) {
29301
- this.export(data);
29384
+ if (data.sheets) {
29385
+ for (let sheet of data.sheets) {
29386
+ if (this.cfRules[sheet.id]) {
29387
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
29388
+ }
29389
+ }
29390
+ }
29302
29391
  }
29303
29392
  // ---------------------------------------------------------------------------
29304
29393
  // Getters
@@ -29348,11 +29437,11 @@ class ConditionalFormatPlugin extends CorePlugin {
29348
29437
  // ---------------------------------------------------------------------------
29349
29438
  // Private
29350
29439
  // ---------------------------------------------------------------------------
29351
- mapToConditionalFormat(sheetId, cf) {
29440
+ mapToConditionalFormat(sheetId, cf, options = { useFixedReference: false }) {
29352
29441
  return {
29353
29442
  ...cf,
29354
29443
  ranges: cf.ranges.map((range) => {
29355
- return this.getters.getRangeString(range, sheetId);
29444
+ return this.getters.getRangeString(range, sheetId, options.useFixedReference);
29356
29445
  }),
29357
29446
  };
29358
29447
  }
@@ -31263,8 +31352,9 @@ class RangeAdapter {
31263
31352
  *
31264
31353
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
31265
31354
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
31355
+ * @param [useFixedZone=false] if true, the range will be returned with fixed row and column
31266
31356
  */
31267
- getRangeString(range, forSheetId) {
31357
+ getRangeString(range, forSheetId, useFixedZone = false) {
31268
31358
  if (!range) {
31269
31359
  return INCORRECT_RANGE_STRING;
31270
31360
  }
@@ -31291,7 +31381,7 @@ class RangeAdapter {
31291
31381
  if (prefixSheet && !sheetName) {
31292
31382
  return INCORRECT_RANGE_STRING;
31293
31383
  }
31294
- let rangeString = this.getRangePartString(rangeImpl, 0);
31384
+ let rangeString = this.getRangePartString(rangeImpl, 0, useFixedZone);
31295
31385
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
31296
31386
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
31297
31387
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -31301,7 +31391,7 @@ class RangeAdapter {
31301
31391
  rangeImpl.parts[1].rowFixed ||
31302
31392
  rangeImpl.parts[1].colFixed) {
31303
31393
  rangeString += ":";
31304
- rangeString += this.getRangePartString(rangeImpl, 1);
31394
+ rangeString += this.getRangePartString(rangeImpl, 1, useFixedZone);
31305
31395
  }
31306
31396
  }
31307
31397
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -31331,14 +31421,14 @@ class RangeAdapter {
31331
31421
  /**
31332
31422
  * Get a Xc string that represent a part of a range
31333
31423
  */
31334
- getRangePartString(range, part) {
31424
+ getRangePartString(range, part, useFixedZone = false) {
31335
31425
  var _a, _b;
31336
31426
  const colFixed = range.parts && ((_a = range.parts[part]) === null || _a === void 0 ? void 0 : _a.colFixed) ? "$" : "";
31337
31427
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
31338
31428
  const rowFixed = range.parts && ((_b = range.parts[part]) === null || _b === void 0 ? void 0 : _b.rowFixed) ? "$" : "";
31339
31429
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
31340
31430
  let str = "";
31341
- if (range.isFullCol) {
31431
+ if (range.isFullCol && !useFixedZone) {
31342
31432
  if (part === 0 && range.unboundedZone.hasHeader) {
31343
31433
  str = colFixed + col + rowFixed + row;
31344
31434
  }
@@ -31346,7 +31436,7 @@ class RangeAdapter {
31346
31436
  str = colFixed + col;
31347
31437
  }
31348
31438
  }
31349
- else if (range.isFullRow) {
31439
+ else if (range.isFullRow && !useFixedZone) {
31350
31440
  if (part === 0 && range.unboundedZone.hasHeader) {
31351
31441
  str = colFixed + col + rowFixed + row;
31352
31442
  }
@@ -32721,6 +32811,10 @@ class EvaluationPlugin extends UIPlugin {
32721
32811
  if (cell.format !== evaluatedCell.format) {
32722
32812
  exportedCellData.computedFormat = evaluatedCell.format;
32723
32813
  }
32814
+ // if there is a formula but no dependencies (maybe because the cell is in error), no need to recompute the formula text
32815
+ if (cell.isFormula && cell.dependencies.length) {
32816
+ exportedCellData.content = this.getters.buildFormulaContent(sheet.id, cell, cell.dependencies, true);
32817
+ }
32724
32818
  }
32725
32819
  }
32726
32820
  }
@@ -34581,8 +34675,7 @@ class AutofillPlugin extends UIPlugin {
34581
34675
  let row = zone.bottom;
34582
34676
  if (col > 0) {
34583
34677
  let leftPosition = { sheetId, col: col - 1, row };
34584
- while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
34585
- ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content)) {
34678
+ while ((_a = this.getters.getCell(leftPosition)) === null || _a === void 0 ? void 0 : _a.content) {
34586
34679
  row += 1;
34587
34680
  leftPosition = { sheetId, col: col - 1, row };
34588
34681
  }
@@ -34591,8 +34684,7 @@ class AutofillPlugin extends UIPlugin {
34591
34684
  col = zone.right;
34592
34685
  if (col <= this.getters.getNumberCols(sheetId)) {
34593
34686
  let rightPosition = { sheetId, col: col + 1, row };
34594
- while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
34595
- ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content)) {
34687
+ while ((_b = this.getters.getCell(rightPosition)) === null || _b === void 0 ? void 0 : _b.content) {
34596
34688
  row += 1;
34597
34689
  rightPosition = { sheetId, col: col + 1, row };
34598
34690
  }
@@ -36284,9 +36376,6 @@ class SelectionInputPlugin extends UIPlugin {
36284
36376
  }
36285
36377
  }
36286
36378
  }
36287
- unsubscribe() {
36288
- this.unfocus();
36289
- }
36290
36379
  // ---------------------------------------------------------------------------
36291
36380
  // Getters || only callable by the parent
36292
36381
  // ---------------------------------------------------------------------------
@@ -36493,9 +36582,9 @@ class SelectionInputsManagerPlugin extends UIPlugin {
36493
36582
  break;
36494
36583
  case "FOCUS_RANGE":
36495
36584
  case "CHANGE_RANGE":
36496
- if (cmd.id !== this.focusedInputId) {
36497
- const input = this.inputs[cmd.id];
36498
- const range = input.ranges.find((range) => range.id === cmd.rangeId);
36585
+ const input = this.inputs[cmd.id];
36586
+ const range = input.ranges.find((range) => range.id === cmd.rangeId);
36587
+ if (range) {
36499
36588
  const sheetId = this.getters.getActiveSheetId();
36500
36589
  const zone = this.getters.getRangeFromSheetXC(sheetId, (range === null || range === void 0 ? void 0 : range.xc) || "A1").zone;
36501
36590
  this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, { handleEvent: input.handleEvent.bind(input) });
@@ -36505,9 +36594,6 @@ class SelectionInputsManagerPlugin extends UIPlugin {
36505
36594
  }
36506
36595
  (_a = this.currentInput) === null || _a === void 0 ? void 0 : _a.handle(cmd);
36507
36596
  }
36508
- unsubscribe() {
36509
- this.unfocus();
36510
- }
36511
36597
  // ---------------------------------------------------------------------------
36512
36598
  // Getters
36513
36599
  // ---------------------------------------------------------------------------
@@ -39578,6 +39664,19 @@ class ImageProvider {
39578
39664
  }
39579
39665
  }
39580
39666
 
39667
+ class FocusableElement {
39668
+ constructor() {
39669
+ this.focusableElement = undefined;
39670
+ }
39671
+ setFocusableElement(element) {
39672
+ this.focusableElement = element;
39673
+ }
39674
+ focus() {
39675
+ var _a;
39676
+ (_a = this.focusableElement) === null || _a === void 0 ? void 0 : _a.focus();
39677
+ }
39678
+ }
39679
+
39581
39680
  // -----------------------------------------------------------------------------
39582
39681
  // SpreadSheet
39583
39682
  // -----------------------------------------------------------------------------
@@ -40821,8 +40920,8 @@ css /* scss */ `
40821
40920
  &.corner {
40822
40921
  right: 0px;
40823
40922
  bottom: 0px;
40824
- height: ${SCROLLBAR_WIDTH$1}px;
40825
- width: ${SCROLLBAR_WIDTH$1}px;
40923
+ height: ${SCROLLBAR_WIDTH}px;
40924
+ width: ${SCROLLBAR_WIDTH}px;
40826
40925
  border-top: 1px solid #e2e3e3;
40827
40926
  border-left: 1px solid #e2e3e3;
40828
40927
  }
@@ -40869,6 +40968,7 @@ class Spreadsheet extends Component {
40869
40968
  toggleSidePanel: this.toggleSidePanel.bind(this),
40870
40969
  _t: Spreadsheet._t,
40871
40970
  clipboard: this.env.clipboard || instantiateClipboard(),
40971
+ focusableElement: new FocusableElement(),
40872
40972
  });
40873
40973
  useExternalListener(window, "resize", () => this.render(true));
40874
40974
  useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -40967,17 +41067,18 @@ class Spreadsheet extends Component {
40967
41067
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
40968
41068
  this.composer.topBarFocus = "contentFocus";
40969
41069
  this.composer.gridFocusMode = "inactive";
40970
- this.setComposerContent({ selection } || {});
41070
+ this.setComposerContent({ selection });
40971
41071
  }
40972
- onGridComposerContentFocused() {
41072
+ onGridComposerContentFocused(selection) {
40973
41073
  if (this.model.getters.isReadonly()) {
40974
41074
  return;
40975
41075
  }
40976
41076
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
40977
41077
  this.composer.topBarFocus = "inactive";
40978
41078
  this.composer.gridFocusMode = "contentFocus";
40979
- this.setComposerContent({});
41079
+ this.setComposerContent({ selection });
40980
41080
  }
41081
+ // TODO: either both are defined or none of them. change those args to an object
40981
41082
  onGridComposerCellFocused(content, selection) {
40982
41083
  if (this.model.getters.isReadonly()) {
40983
41084
  return;
@@ -41958,18 +42059,17 @@ class EventStream {
41958
42059
  if (this.observers.find((sub) => sub.owner === owner)) {
41959
42060
  throw new Error("You are already subscribed forever");
41960
42061
  }
41961
- if ((_a = this.mainSubscription) === null || _a === void 0 ? void 0 : _a.owner) {
42062
+ if (((_a = this.mainSubscription) === null || _a === void 0 ? void 0 : _a.owner) && this.mainSubscription.owner !== owner) {
41962
42063
  (_c = (_b = this.mainSubscription.callbacks).release) === null || _c === void 0 ? void 0 : _c.call(_b);
41963
42064
  }
41964
42065
  this.mainSubscription = { owner, callbacks };
41965
42066
  }
41966
42067
  release(owner) {
41967
- var _a, _b, _c, _d;
42068
+ var _a;
41968
42069
  if (((_a = this.mainSubscription) === null || _a === void 0 ? void 0 : _a.owner) !== owner ||
41969
42070
  this.observers.find((sub) => sub.owner === owner)) {
41970
42071
  return;
41971
42072
  }
41972
- (_d = (_b = this.mainSubscription) === null || _b === void 0 ? void 0 : (_c = _b.callbacks).release) === null || _d === void 0 ? void 0 : _d.call(_c);
41973
42073
  this.mainSubscription = this.defaultSubscription;
41974
42074
  }
41975
42075
  /**
@@ -42437,11 +42537,10 @@ class SelectionStreamProcessor {
42437
42537
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
42438
42538
  */
42439
42539
  getEndOfCluster(startPosition, dim, dir) {
42440
- const sheet = this.getters.getActiveSheet();
42441
42540
  let currentPosition = startPosition;
42442
42541
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
42443
42542
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
42444
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
42543
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
42445
42544
  ? "endOfCluster"
42446
42545
  : "nextCluster";
42447
42546
  while (true) {
@@ -42451,7 +42550,7 @@ class SelectionStreamProcessor {
42451
42550
  currentPosition.row === nextCellPosition.row) {
42452
42551
  break;
42453
42552
  }
42454
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
42553
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
42455
42554
  if (mode === "endOfCluster" && isNextCellEmpty) {
42456
42555
  break;
42457
42556
  }
@@ -42465,14 +42564,25 @@ class SelectionStreamProcessor {
42465
42564
  return dim === "cols" ? currentPosition.col : currentPosition.row;
42466
42565
  }
42467
42566
  /**
42468
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
42469
- * check if the merge containing the cell is empty.
42567
+ * Check if a cell evaluated value is empty. If the cell is part of a merge,
42568
+ * the check applies to the main cell of the merge.
42470
42569
  */
42471
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
42570
+ isEvaluatedCellEmpty({ col, row }) {
42571
+ const sheetId = this.getters.getActiveSheetId();
42472
42572
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
42473
42573
  const cell = this.getters.getEvaluatedCell(position);
42474
42574
  return cell.type === CellValueType.empty;
42475
42575
  }
42576
+ /**
42577
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
42578
+ * the check applies to the main cell of the merge.
42579
+ */
42580
+ isCellEmpty({ col, row }) {
42581
+ var _a;
42582
+ const sheetId = this.getters.getActiveSheetId();
42583
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
42584
+ return !((_a = this.getters.getCell(position)) === null || _a === void 0 ? void 0 : _a.content);
42585
+ }
42476
42586
  /** Computes the next cell position in the given direction by crossing through merges and skipping hidden cells.
42477
42587
  *
42478
42588
  * This has the same behaviour as getNextAvailablePosition() for certain arguments, but use this method instead
@@ -42951,12 +43061,7 @@ function addFormula(cell) {
42951
43061
  attrs.push(["t", "str"]);
42952
43062
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
42953
43063
  }
42954
- node = escapeXml /*xml*/ `
42955
- <f>
42956
- ${XlsxFormula}
42957
- </f>
42958
- ${cycle}
42959
- `;
43064
+ node = escapeXml /*xml*/ `<f> ${XlsxFormula} </f> ${cycle}`;
42960
43065
  return { attrs, node };
42961
43066
  }
42962
43067
  else {
@@ -43697,11 +43802,10 @@ function addRows(construct, data, sheet) {
43697
43802
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
43698
43803
  }
43699
43804
  attributes.push(...additionalAttrs);
43700
- cellNodes.push(escapeXml /*xml*/ `
43701
- <c ${formatAttributes(attributes)}>
43702
- ${cellNode}
43703
- </c>
43704
- `);
43805
+ // prettier-ignore
43806
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
43807
+ ${cellNode}
43808
+ </c>`);
43705
43809
  }
43706
43810
  }
43707
43811
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -44458,7 +44562,7 @@ const SPREADSHEET_DIMENSIONS = {
44458
44562
  BOTTOMBAR_HEIGHT,
44459
44563
  DEFAULT_CELL_WIDTH,
44460
44564
  DEFAULT_CELL_HEIGHT,
44461
- SCROLLBAR_WIDTH: SCROLLBAR_WIDTH$1,
44565
+ SCROLLBAR_WIDTH,
44462
44566
  };
44463
44567
  const registries = {
44464
44568
  autofillModifiersRegistry,
@@ -44545,6 +44649,6 @@ function addFunction(functionName, functionDescription) {
44545
44649
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
44546
44650
 
44547
44651
 
44548
- __info__.version = '16.1.33';
44549
- __info__.date = '2024-01-29T13:49:43.259Z';
44550
- __info__.hash = 'd3b27dd';
44652
+ __info__.version = '16.1.36';
44653
+ __info__.date = '2024-02-16T14:16:11.082Z';
44654
+ __info__.hash = 'ad5301c';