@odoo/o-spreadsheet 18.2.0-alpha.4 → 18.2.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.2.0-alpha.4
6
- * @date 2025-01-29T06:30:12.773Z
7
- * @hash 6838c26
5
+ * @version 18.2.0-alpha.5
6
+ * @date 2025-01-31T07:59:30.667Z
7
+ * @hash efce841
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -3359,11 +3359,11 @@ const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(l
3359
3359
  * number from the point of view of the isNumber function.
3360
3360
  */
3361
3361
  function parseNumber(str, locale) {
3362
+ // remove invaluable characters
3363
+ str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3362
3364
  if (locale.decimalSeparator !== ".") {
3363
3365
  str = str.replace(locale.decimalSeparator, ".");
3364
3366
  }
3365
- // remove invaluable characters
3366
- str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3367
3367
  let n = Number(str);
3368
3368
  if (isNaN(n) && str.includes("%")) {
3369
3369
  n = Number(str.split("%")[0]);
@@ -32536,36 +32536,58 @@ class ErrorToolTip extends Component {
32536
32536
  static maxSize = { maxHeight: ERROR_TOOLTIP_MAX_HEIGHT };
32537
32537
  static template = "o-spreadsheet-ErrorToolTip";
32538
32538
  static props = {
32539
- errors: Array,
32539
+ cellPosition: Object,
32540
32540
  onClosed: { type: Function, optional: true },
32541
32541
  };
32542
+ get dataValidationErrorMessage() {
32543
+ return this.env.model.getters.getInvalidDataValidationMessage(this.props.cellPosition);
32544
+ }
32545
+ get evaluationError() {
32546
+ const cell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
32547
+ if (cell.message) {
32548
+ return cell;
32549
+ }
32550
+ return undefined;
32551
+ }
32552
+ get errorOriginPositionString() {
32553
+ const evaluationError = this.evaluationError;
32554
+ const position = evaluationError?.errorOriginPosition;
32555
+ if (!position || deepEquals(position, this.props.cellPosition)) {
32556
+ return "";
32557
+ }
32558
+ const sheetId = position.sheetId;
32559
+ return this.env.model.getters.getRangeString(this.env.model.getters.getRangeFromZone(sheetId, positionToZone(position)), this.env.model.getters.getActiveSheetId());
32560
+ }
32561
+ selectCell() {
32562
+ const position = this.evaluationError?.errorOriginPosition;
32563
+ if (!position) {
32564
+ return;
32565
+ }
32566
+ const activeSheetId = this.env.model.getters.getActiveSheetId();
32567
+ if (position.sheetId !== activeSheetId) {
32568
+ this.env.model.dispatch("ACTIVATE_SHEET", {
32569
+ sheetIdFrom: activeSheetId,
32570
+ sheetIdTo: position.sheetId,
32571
+ });
32572
+ }
32573
+ this.env.model.selection.selectCell(position.col, position.row);
32574
+ }
32542
32575
  }
32543
32576
  const ErrorToolTipPopoverBuilder = {
32544
32577
  onHover: (position, getters) => {
32545
32578
  const cell = getters.getEvaluatedCell(position);
32546
- const errors = [];
32547
- if (cell.type === CellValueType.error && !!cell.message) {
32548
- errors.push({
32549
- title: _t("Error"),
32550
- message: cell.message,
32551
- });
32552
- }
32553
- const validationErrorMessage = getters.getInvalidDataValidationMessage(position);
32554
- if (validationErrorMessage) {
32555
- errors.push({
32556
- title: _t("Invalid"),
32557
- message: validationErrorMessage,
32558
- });
32559
- }
32560
- if (!errors.length) {
32561
- return { isOpen: false };
32579
+ if ((cell.type === CellValueType.error && !!cell.message) ||
32580
+ getters.getInvalidDataValidationMessage(position)) {
32581
+ return {
32582
+ isOpen: true,
32583
+ props: {
32584
+ cellPosition: position,
32585
+ },
32586
+ Component: ErrorToolTip,
32587
+ cellCorner: "TopRight",
32588
+ };
32562
32589
  }
32563
- return {
32564
- isOpen: true,
32565
- props: { errors: errors },
32566
- Component: ErrorToolTip,
32567
- cellCorner: "TopRight",
32568
- };
32590
+ return { isOpen: false };
32569
32591
  },
32570
32592
  };
32571
32593
 
@@ -33129,6 +33151,100 @@ function* iterateChildren(el) {
33129
33151
  function getOpenedMenus() {
33130
33152
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33131
33153
  }
33154
+ function getCurrentSelection(el) {
33155
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33156
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33157
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33158
+ return {
33159
+ start: startSizeBefore,
33160
+ end: endSizeBefore,
33161
+ };
33162
+ }
33163
+ function getStartAndEndSelection(el) {
33164
+ const selection = document.getSelection();
33165
+ return {
33166
+ startElement: selection.anchorNode || el,
33167
+ startSelectionOffset: selection.anchorOffset,
33168
+ endElement: selection.focusNode || el,
33169
+ endSelectionOffset: selection.focusOffset,
33170
+ };
33171
+ }
33172
+ /**
33173
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33174
+ * The selected node is either a Text node or an Element node.
33175
+ *
33176
+ * case 1 -Text node:
33177
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33178
+ * content length of all previous nodes.
33179
+ *
33180
+ * case 2 - Element node:
33181
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33182
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33183
+ *
33184
+ * See the MDN documentation for more details.
33185
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33186
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33187
+ *
33188
+ */
33189
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33190
+ let usedCharacters = 0;
33191
+ let it = iterateChildren(el);
33192
+ let current = it.next();
33193
+ let isFirstParagraph = true;
33194
+ while (!current.done && current.value !== nodeToFind) {
33195
+ if (!current.value.hasChildNodes()) {
33196
+ if (current.value.textContent) {
33197
+ usedCharacters += current.value.textContent.length;
33198
+ }
33199
+ }
33200
+ // One new paragraph = one new line character, except for the first paragraph
33201
+ if (current.value.nodeName === "P" ||
33202
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33203
+ ) {
33204
+ if (isFirstParagraph) {
33205
+ isFirstParagraph = false;
33206
+ }
33207
+ else {
33208
+ usedCharacters++;
33209
+ }
33210
+ }
33211
+ current = it.next();
33212
+ }
33213
+ if (current.value !== nodeToFind) {
33214
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33215
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33216
+ *
33217
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33218
+ */
33219
+ return 0;
33220
+ }
33221
+ else {
33222
+ if (!current.value.hasChildNodes()) {
33223
+ usedCharacters += nodeOffset;
33224
+ }
33225
+ else {
33226
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33227
+ usedCharacters += children.reduce((acc, child, index) => {
33228
+ if (child.textContent !== null) {
33229
+ // need to account for paragraph nodes that implicitly add a new line
33230
+ // except for the last paragraph
33231
+ let chars = child.textContent.length;
33232
+ if (child.nodeName === "P" && index !== children.length - 1) {
33233
+ chars++;
33234
+ }
33235
+ return acc + chars;
33236
+ }
33237
+ else {
33238
+ return acc;
33239
+ }
33240
+ }, 0);
33241
+ }
33242
+ }
33243
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33244
+ usedCharacters++;
33245
+ }
33246
+ return usedCharacters;
33247
+ }
33132
33248
  const letterRegex = /^[a-zA-Z]$/;
33133
33249
  /**
33134
33250
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -40336,6 +40452,10 @@ class ContentEditableHelper {
40336
40452
  if (currentStart === start && currentEnd === end) {
40337
40453
  return;
40338
40454
  }
40455
+ if (selection.rangeCount === 0) {
40456
+ const range = document.createRange();
40457
+ selection.addRange(range);
40458
+ }
40339
40459
  const currentRange = selection.getRangeAt(0);
40340
40460
  let range;
40341
40461
  if (this.el.contains(currentRange.startContainer)) {
@@ -40498,7 +40618,7 @@ class ContentEditableHelper {
40498
40618
  if (!focusedNode || !this.el.contains(focusedNode))
40499
40619
  return;
40500
40620
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40501
- element?.scrollIntoView({ block: "nearest" });
40621
+ element?.scrollIntoView?.({ block: "nearest" });
40502
40622
  }
40503
40623
  /**
40504
40624
  * remove the current selection of the user
@@ -40518,100 +40638,7 @@ class ContentEditableHelper {
40518
40638
  * finds the indexes of the current selection.
40519
40639
  * */
40520
40640
  getCurrentSelection() {
40521
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40522
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40523
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40524
- return {
40525
- start: startSizeBefore,
40526
- end: endSizeBefore,
40527
- };
40528
- }
40529
- /**
40530
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40531
- * The selected node is either a Text node or an Element node.
40532
- *
40533
- * case 1 -Text node:
40534
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40535
- * content length of all previous nodes.
40536
- *
40537
- * case 2 - Element node:
40538
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40539
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40540
- *
40541
- * See the MDN documentation for more details.
40542
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40543
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40544
- *
40545
- */
40546
- findSelectionIndex(nodeToFind, nodeOffset) {
40547
- let usedCharacters = 0;
40548
- let it = iterateChildren(this.el);
40549
- let current = it.next();
40550
- let isFirstParagraph = true;
40551
- while (!current.done && current.value !== nodeToFind) {
40552
- if (!current.value.hasChildNodes()) {
40553
- if (current.value.textContent) {
40554
- usedCharacters += current.value.textContent.length;
40555
- }
40556
- }
40557
- // One new paragraph = one new line character, except for the first paragraph
40558
- if (current.value.nodeName === "P" ||
40559
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40560
- ) {
40561
- if (isFirstParagraph) {
40562
- isFirstParagraph = false;
40563
- }
40564
- else {
40565
- usedCharacters++;
40566
- }
40567
- }
40568
- current = it.next();
40569
- }
40570
- if (current.value !== nodeToFind) {
40571
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40572
- * In this case, we return 0 because we don't know the size of the text before the selection.
40573
- *
40574
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40575
- *
40576
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40577
- */
40578
- return 0;
40579
- }
40580
- else {
40581
- if (!current.value.hasChildNodes()) {
40582
- usedCharacters += nodeOffset;
40583
- }
40584
- else {
40585
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40586
- usedCharacters += children.reduce((acc, child, index) => {
40587
- if (child.textContent !== null) {
40588
- // need to account for paragraph nodes that implicitely add a new line
40589
- // except for the last paragraph
40590
- let chars = child.textContent.length;
40591
- if (child.nodeName === "P" && index !== children.length - 1) {
40592
- chars++;
40593
- }
40594
- return acc + chars;
40595
- }
40596
- else {
40597
- return acc;
40598
- }
40599
- }, 0);
40600
- }
40601
- }
40602
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40603
- usedCharacters++;
40604
- }
40605
- return usedCharacters;
40606
- }
40607
- getStartAndEndSelection() {
40608
- const selection = document.getSelection();
40609
- return {
40610
- startElement: selection.anchorNode || this.el,
40611
- startSelectionOffset: selection.anchorOffset,
40612
- endElement: selection.focusNode || this.el,
40613
- endSelectionOffset: selection.focusOffset,
40614
- };
40641
+ return getCurrentSelection(this.el);
40615
40642
  }
40616
40643
  getText() {
40617
40644
  let text = "";
@@ -40875,6 +40902,12 @@ class Composer extends Component {
40875
40902
  }
40876
40903
  this.contentHelper.updateEl(el);
40877
40904
  });
40905
+ this.env.model.selection.observe(this, {
40906
+ handleEvent: () => this.autoCompleteState.hide(),
40907
+ });
40908
+ onWillUnmount(() => {
40909
+ this.env.model.selection.detachObserver(this);
40910
+ });
40878
40911
  useEffect(() => {
40879
40912
  this.processContent();
40880
40913
  if (document.activeElement === this.contentHelper.el &&
@@ -49214,7 +49247,7 @@ function isAxisVisible(getters, figure, axis) {
49214
49247
  axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
49215
49248
  break;
49216
49249
  }
49217
- return axisStartEndPositions.some(getters.isPositionVisible);
49250
+ return axisStartEndPositions.some(getters.isPixelPositionVisible);
49218
49251
  }
49219
49252
  /**
49220
49253
  * Get a snap line for the given figure, if the figure can snap to any other figure
@@ -51241,8 +51274,8 @@ class GridRenderer {
51241
51274
  previousColIndex = col;
51242
51275
  }
51243
51276
  else {
51244
- nextColIndex = this.findNextEmptyCol(col, right, row);
51245
- previousColIndex = this.findPreviousEmptyCol(col, left, row);
51277
+ nextColIndex = box.border?.right ? zone.right : this.findNextEmptyCol(col, right, row);
51278
+ previousColIndex = box.border?.left ? zone.left : this.findPreviousEmptyCol(col, left, row);
51246
51279
  box.isOverflow = true;
51247
51280
  }
51248
51281
  switch (align) {
@@ -52715,7 +52748,7 @@ class BordersPlugin extends CorePlugin {
52715
52748
  // map and slice preserve empty values and do not set `undefined` instead
52716
52749
  const bordersCopy = borders
52717
52750
  .slice()
52718
- .map((col) => col?.slice().map((border) => ({ ...border })));
52751
+ .map((col) => col?.slice().map((border) => deepCopy(border)));
52719
52752
  this.history.update("borders", cmd.sheetIdTo, bordersCopy);
52720
52753
  }
52721
52754
  break;
@@ -52745,32 +52778,12 @@ class BordersPlugin extends CorePlugin {
52745
52778
  const elements = [...cmd.elements].sort((a, b) => b - a);
52746
52779
  for (const group of groupConsecutive(elements)) {
52747
52780
  if (cmd.dimension === "COL") {
52748
- if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52749
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52750
- this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52751
- }
52752
- }
52753
- if (group[group.length - 1] === 0) {
52754
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52755
- this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52756
- }
52757
- }
52758
- const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52781
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1], group[0]);
52759
52782
  this.clearInsideBorders(cmd.sheetId, [zone]);
52760
52783
  this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52761
52784
  }
52762
52785
  else {
52763
- if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52764
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52765
- this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52766
- }
52767
- }
52768
- if (group[group.length - 1] === 0) {
52769
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52770
- this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52771
- }
52772
- }
52773
- const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52786
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1], group[0]);
52774
52787
  this.clearInsideBorders(cmd.sheetId, [zone]);
52775
52788
  this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52776
52789
  }
@@ -52795,16 +52808,12 @@ class BordersPlugin extends CorePlugin {
52795
52808
  let colLeftOfInsertion;
52796
52809
  let colRightOfInsertion;
52797
52810
  if (cmd.position === "before") {
52798
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity, {
52799
- moveFirstLeftBorder: true,
52800
- });
52811
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity);
52801
52812
  colLeftOfInsertion = cmd.base - 1;
52802
52813
  colRightOfInsertion = cmd.base + cmd.quantity;
52803
52814
  }
52804
52815
  else {
52805
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52806
- moveFirstLeftBorder: false,
52807
- });
52816
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity);
52808
52817
  colLeftOfInsertion = cmd.base;
52809
52818
  colRightOfInsertion = cmd.base + cmd.quantity + 1;
52810
52819
  }
@@ -52819,16 +52828,12 @@ class BordersPlugin extends CorePlugin {
52819
52828
  let rowAboveInsertion;
52820
52829
  let rowBelowInsertion;
52821
52830
  if (cmd.position === "before") {
52822
- this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity, {
52823
- moveFirstTopBorder: true,
52824
- });
52831
+ this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity);
52825
52832
  rowAboveInsertion = cmd.base - 1;
52826
52833
  rowBelowInsertion = cmd.base + cmd.quantity;
52827
52834
  }
52828
52835
  else {
52829
- this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52830
- moveFirstTopBorder: false,
52831
- });
52836
+ this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity);
52832
52837
  rowAboveInsertion = cmd.base;
52833
52838
  rowBelowInsertion = cmd.base + cmd.quantity + 1;
52834
52839
  }
@@ -52838,16 +52843,8 @@ class BordersPlugin extends CorePlugin {
52838
52843
  // Getters
52839
52844
  // ---------------------------------------------------------------------------
52840
52845
  getCellBorder({ sheetId, col, row }) {
52841
- const border = {
52842
- top: this.borders[sheetId]?.[col]?.[row]?.horizontal,
52843
- bottom: this.borders[sheetId]?.[col]?.[row + 1]?.horizontal,
52844
- left: this.borders[sheetId]?.[col]?.[row]?.vertical,
52845
- right: this.borders[sheetId]?.[col + 1]?.[row]?.vertical,
52846
- };
52847
- if (!border.bottom && !border.left && !border.right && !border.top) {
52848
- return null;
52849
- }
52850
- return border;
52846
+ const border = this.borders[sheetId]?.[col]?.[row];
52847
+ return border?.top || border?.bottom || border?.left || border?.right ? deepCopy(border) : null;
52851
52848
  }
52852
52849
  getBordersColors(sheetId) {
52853
52850
  const colors = [];
@@ -52855,11 +52852,13 @@ class BordersPlugin extends CorePlugin {
52855
52852
  if (sheetBorders) {
52856
52853
  for (const borders of sheetBorders.filter(isDefined)) {
52857
52854
  for (const cellBorder of borders) {
52858
- if (cellBorder?.horizontal) {
52859
- colors.push(cellBorder.horizontal.color);
52860
- }
52861
- if (cellBorder?.vertical) {
52862
- colors.push(cellBorder.vertical.color);
52855
+ if (cellBorder) {
52856
+ for (const direction of ["top", "bottom", "left", "right"]) {
52857
+ const color = cellBorder[direction]?.color;
52858
+ if (color) {
52859
+ colors.push(color);
52860
+ }
52861
+ }
52863
52862
  }
52864
52863
  }
52865
52864
  }
@@ -52912,7 +52911,7 @@ class BordersPlugin extends CorePlugin {
52912
52911
  getCommonSides(border1, border2) {
52913
52912
  const commonBorder = {};
52914
52913
  for (let side of ["top", "bottom", "left", "right"]) {
52915
- if (border1[side] && border1[side] === border2[side]) {
52914
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52916
52915
  commonBorder[side] = border1[side];
52917
52916
  }
52918
52917
  }
@@ -52957,23 +52956,15 @@ class BordersPlugin extends CorePlugin {
52957
52956
  * @param start starting column (included)
52958
52957
  * @param delta how much borders will be moved (negative if moved to the left)
52959
52958
  */
52960
- shiftBordersHorizontally(sheetId, start, delta, { moveFirstLeftBorder } = {}) {
52959
+ shiftBordersHorizontally(sheetId, start, delta) {
52961
52960
  const borders = this.borders[sheetId];
52962
52961
  if (!borders)
52963
52962
  return;
52964
- if (delta < 0) {
52965
- this.moveBordersOfColumn(sheetId, start, delta, "vertical", {
52966
- destructive: false,
52967
- });
52968
- }
52969
52963
  this.getColumnsWithBorders(sheetId)
52970
52964
  .filter((col) => col >= start)
52971
52965
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52972
52966
  .forEach((col) => {
52973
- if ((col === start && moveFirstLeftBorder) || col !== start) {
52974
- this.moveBordersOfColumn(sheetId, col, delta, "vertical");
52975
- }
52976
- this.moveBordersOfColumn(sheetId, col, delta, "horizontal");
52967
+ this.moveBordersOfColumn(sheetId, col, delta);
52977
52968
  });
52978
52969
  }
52979
52970
  /**
@@ -52982,12 +52973,12 @@ class BordersPlugin extends CorePlugin {
52982
52973
  * @param start starting row (included)
52983
52974
  * @param delta how much borders will be moved (negative if moved to the above)
52984
52975
  */
52985
- shiftBordersVertically(sheetId, start, delta, { moveFirstTopBorder } = {}) {
52976
+ shiftBordersVertically(sheetId, start, delta) {
52986
52977
  const borders = this.borders[sheetId];
52987
52978
  if (!borders)
52988
52979
  return;
52989
52980
  if (delta < 0) {
52990
- this.moveBordersOfRow(sheetId, start, delta, "horizontal", {
52981
+ this.moveBordersOfRow(sheetId, start, delta, {
52991
52982
  destructive: false,
52992
52983
  });
52993
52984
  }
@@ -52995,10 +52986,7 @@ class BordersPlugin extends CorePlugin {
52995
52986
  .filter((row) => row >= start)
52996
52987
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52997
52988
  .forEach((row) => {
52998
- if ((row === start && moveFirstTopBorder) || row !== start) {
52999
- this.moveBordersOfRow(sheetId, row, delta, "horizontal");
53000
- }
53001
- this.moveBordersOfRow(sheetId, row, delta, "vertical");
52989
+ this.moveBordersOfRow(sheetId, row, delta);
53002
52990
  });
53003
52991
  }
53004
52992
  /**
@@ -53012,15 +53000,15 @@ class BordersPlugin extends CorePlugin {
53012
53000
  * argument `destructive` is given false, the target border is preserved if
53013
53001
  * the moved border is empty
53014
53002
  */
53015
- moveBordersOfRow(sheetId, row, delta, borderDirection, { destructive } = { destructive: true }) {
53003
+ moveBordersOfRow(sheetId, row, delta, { destructive } = { destructive: true }) {
53016
53004
  const borders = this.borders[sheetId];
53017
53005
  if (!borders)
53018
53006
  return;
53019
53007
  this.getColumnsWithBorders(sheetId).forEach((col) => {
53020
- const targetBorder = borders[col]?.[row + delta]?.[borderDirection];
53021
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53022
- this.history.update("borders", sheetId, col, row + delta, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53023
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53008
+ const targetBorder = borders[col]?.[row + delta];
53009
+ const movedBorder = borders[col]?.[row];
53010
+ this.history.update("borders", sheetId, col, row + delta, destructive ? movedBorder : movedBorder || targetBorder);
53011
+ this.history.update("borders", sheetId, col, row, undefined);
53024
53012
  });
53025
53013
  }
53026
53014
  /**
@@ -53034,15 +53022,17 @@ class BordersPlugin extends CorePlugin {
53034
53022
  * argument `destructive` is given false, the target border is preserved if
53035
53023
  * the moved border is empty
53036
53024
  */
53037
- moveBordersOfColumn(sheetId, col, delta, borderDirection, { destructive } = { destructive: true }) {
53025
+ moveBordersOfColumn(sheetId, col, delta, { destructive } = { destructive: true }) {
53038
53026
  const borders = this.borders[sheetId];
53039
53027
  if (!borders)
53040
53028
  return;
53041
53029
  this.getRowsRange(sheetId).forEach((row) => {
53042
- const targetBorder = borders[col + delta]?.[row]?.[borderDirection];
53043
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53044
- this.history.update("borders", sheetId, col + delta, row, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53045
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53030
+ const targetBorder = borders[col + delta]?.[row];
53031
+ const movedBorder = borders[col]?.[row];
53032
+ this.history.update("borders", sheetId, col + delta, row, destructive ? movedBorder : movedBorder || targetBorder);
53033
+ if (destructive) {
53034
+ this.history.update("borders", sheetId, col, row, undefined);
53035
+ }
53046
53036
  });
53047
53037
  }
53048
53038
  /**
@@ -53050,33 +53040,69 @@ class BordersPlugin extends CorePlugin {
53050
53040
  * It overrides the current border if override === true.
53051
53041
  */
53052
53042
  setBorder(sheetId, col, row, border, override = true) {
53053
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
53054
- this.history.update("borders", sheetId, col, row, "vertical", border?.left);
53043
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53044
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53045
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.left) {
53046
+ this.history.update("borders", sheetId, col, row, "left", border?.left);
53047
+ if (border?.left &&
53048
+ col > 0 &&
53049
+ !deepEquals(this.getCellBorder({ sheetId, col: col - 1, row })?.right, border?.left)) {
53050
+ this.history.update("borders", sheetId, col - 1, row, "right", undefined);
53051
+ }
53055
53052
  }
53056
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.horizontal) {
53057
- this.history.update("borders", sheetId, col, row, "horizontal", border?.top);
53053
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.top) {
53054
+ this.history.update("borders", sheetId, col, row, "top", border?.top);
53055
+ if (border?.top &&
53056
+ row > 0 &&
53057
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row - 1 })?.bottom, border?.top)) {
53058
+ this.history.update("borders", sheetId, col, row - 1, "bottom", undefined);
53059
+ }
53058
53060
  }
53059
- if (override || !this.borders?.[sheetId]?.[col + 1]?.[row]?.vertical) {
53060
- this.history.update("borders", sheetId, col + 1, row, "vertical", border?.right);
53061
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.right) {
53062
+ this.history.update("borders", sheetId, col, row, "right", border?.right);
53063
+ if (border?.right &&
53064
+ col < maxCol &&
53065
+ !deepEquals(this.getCellBorder({ sheetId, col: col + 1, row })?.left, border?.right)) {
53066
+ this.history.update("borders", sheetId, col + 1, row, "left", undefined);
53067
+ }
53061
53068
  }
53062
- if (override || !this.borders?.[sheetId]?.[col]?.[row + 1]?.horizontal) {
53063
- this.history.update("borders", sheetId, col, row + 1, "horizontal", border?.bottom);
53069
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.bottom) {
53070
+ this.history.update("borders", sheetId, col, row, "bottom", border?.bottom);
53071
+ if (border?.bottom &&
53072
+ row < maxRow &&
53073
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row + 1 })?.top, border?.bottom)) {
53074
+ this.history.update("borders", sheetId, col, row + 1, "top", undefined);
53075
+ }
53064
53076
  }
53065
53077
  }
53066
53078
  /**
53067
53079
  * Remove the borders of a zone
53068
53080
  */
53069
- clearBorders(sheetId, zones) {
53081
+ clearBorders(sheetId, zones, eraseBoundaries = false) {
53082
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53083
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53070
53084
  for (let zone of recomputeZones(zones)) {
53071
53085
  for (let row = zone.top; row <= zone.bottom; row++) {
53072
- this.history.update("borders", sheetId, zone.right + 1, row, "vertical", undefined);
53086
+ if (eraseBoundaries) {
53087
+ if (zone.left > 0) {
53088
+ this.history.update("borders", sheetId, zone.left - 1, row, "right", undefined);
53089
+ }
53090
+ if (zone.right < maxCol) {
53091
+ this.history.update("borders", sheetId, zone.right + 1, row, "left", undefined);
53092
+ }
53093
+ }
53073
53094
  for (let col = zone.left; col <= zone.right; col++) {
53074
53095
  this.history.update("borders", sheetId, col, row, undefined);
53096
+ if (eraseBoundaries) {
53097
+ if (zone.top > 0) {
53098
+ this.history.update("borders", sheetId, col, zone.top - 1, "bottom", undefined);
53099
+ }
53100
+ if (zone.bottom < maxRow) {
53101
+ this.history.update("borders", sheetId, col, zone.bottom + 1, "top", undefined);
53102
+ }
53103
+ }
53075
53104
  }
53076
53105
  }
53077
- for (let col = zone.left; col <= zone.right; col++) {
53078
- this.history.update("borders", sheetId, col, zone.bottom + 1, "horizontal", undefined);
53079
- }
53080
53106
  }
53081
53107
  }
53082
53108
  /**
@@ -53106,41 +53132,63 @@ class BordersPlugin extends CorePlugin {
53106
53132
  */
53107
53133
  setBorders(sheetId, zones, position, border) {
53108
53134
  if (position === "clear") {
53109
- return this.clearBorders(sheetId, zones);
53135
+ return this.clearBorders(sheetId, zones, true);
53110
53136
  }
53111
53137
  for (let zone of recomputeZones(zones)) {
53112
- if (position === "h" || position === "hv" || position === "all") {
53113
- for (let row = zone.top + 1; row <= zone.bottom; row++) {
53138
+ if (position === "all") {
53139
+ for (let row = zone.top; row <= zone.bottom; row++) {
53114
53140
  for (let col = zone.left; col <= zone.right; col++) {
53115
- this.addBorder(sheetId, col, row, { top: border });
53141
+ this.addBorder(sheetId, col, row, {
53142
+ top: border,
53143
+ right: border,
53144
+ bottom: border,
53145
+ left: border,
53146
+ });
53116
53147
  }
53117
53148
  }
53118
53149
  }
53119
- if (position === "v" || position === "hv" || position === "all") {
53150
+ if (position === "h" || position === "hv") {
53151
+ if (zone.top === zone.bottom) {
53152
+ continue;
53153
+ }
53154
+ for (let col = zone.left; col <= zone.right; col++) {
53155
+ this.addBorder(sheetId, col, zone.top, { bottom: border });
53156
+ for (let row = zone.top + 1; row < zone.bottom; row++) {
53157
+ this.addBorder(sheetId, col, row, { top: border, bottom: border });
53158
+ }
53159
+ this.addBorder(sheetId, col, zone.bottom, { top: border });
53160
+ }
53161
+ }
53162
+ if (position === "v" || position === "hv") {
53163
+ if (zone.left === zone.right) {
53164
+ continue;
53165
+ }
53120
53166
  for (let row = zone.top; row <= zone.bottom; row++) {
53121
- for (let col = zone.left + 1; col <= zone.right; col++) {
53122
- this.addBorder(sheetId, col, row, { left: border });
53167
+ this.addBorder(sheetId, zone.left, row, { right: border });
53168
+ for (let col = zone.left + 1; col < zone.right; col++) {
53169
+ this.addBorder(sheetId, col, row, { left: border, right: border });
53123
53170
  }
53171
+ this.addBorder(sheetId, zone.right, row, { left: border });
53124
53172
  }
53125
53173
  }
53126
- if (position === "left" || position === "all" || position === "external") {
53174
+ if (position === "left" || position === "external") {
53127
53175
  for (let row = zone.top; row <= zone.bottom; row++) {
53128
53176
  this.addBorder(sheetId, zone.left, row, { left: border });
53129
53177
  }
53130
53178
  }
53131
- if (position === "right" || position === "all" || position === "external") {
53179
+ if (position === "right" || position === "external") {
53132
53180
  for (let row = zone.top; row <= zone.bottom; row++) {
53133
- this.addBorder(sheetId, zone.right + 1, row, { left: border });
53181
+ this.addBorder(sheetId, zone.right, row, { right: border });
53134
53182
  }
53135
53183
  }
53136
- if (position === "top" || position === "all" || position === "external") {
53184
+ if (position === "top" || position === "external") {
53137
53185
  for (let col = zone.left; col <= zone.right; col++) {
53138
53186
  this.addBorder(sheetId, col, zone.top, { top: border });
53139
53187
  }
53140
53188
  }
53141
- if (position === "bottom" || position === "all" || position === "external") {
53189
+ if (position === "bottom" || position === "external") {
53142
53190
  for (let col = zone.left; col <= zone.right; col++) {
53143
- this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
53191
+ this.addBorder(sheetId, col, zone.bottom, { bottom: border });
53144
53192
  }
53145
53193
  }
53146
53194
  }
@@ -59885,7 +59933,11 @@ class Evaluator {
59885
59933
  computeFormulaCell(formulaPosition, cellData) {
59886
59934
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
59887
59935
  if (!isMatrix(formulaReturn)) {
59888
- return createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59936
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59937
+ if (evaluatedCell.type === CellValueType.error) {
59938
+ evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
59939
+ }
59940
+ return evaluatedCell;
59889
59941
  }
59890
59942
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
59891
59943
  const nbColumns = formulaReturn.length;
@@ -59958,6 +60010,9 @@ class Evaluator {
59958
60010
  const position = { sheetId, col: i + col, row: j + row };
59959
60011
  const cell = this.getters.getCell(position);
59960
60012
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
60013
+ if (evaluatedCell.type === CellValueType.error) {
60014
+ evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
60015
+ }
59961
60016
  this.evaluatedCells.set(position, evaluatedCell);
59962
60017
  };
59963
60018
  return spreadValues;
@@ -61516,7 +61571,7 @@ function withPivotPresentationLayer (PivotClass) {
61516
61571
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
61517
61572
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
61518
61573
  }
61519
- return this._getPivotCellValueAndFormat(symbolName, domain);
61574
+ return this.getPivotCellValueAndFormat(symbolName, domain);
61520
61575
  };
61521
61576
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
61522
61577
  if (isMatrix(result)) {
@@ -64702,14 +64757,12 @@ class SheetUIPlugin extends UIPlugin {
64702
64757
  }
64703
64758
  break;
64704
64759
  case "AUTORESIZE_ROWS":
64705
- for (let row of cmd.rows) {
64706
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64707
- elements: [row],
64708
- dimension: "ROW",
64709
- size: null,
64710
- sheetId: cmd.sheetId,
64711
- });
64712
- }
64760
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
64761
+ elements: cmd.rows,
64762
+ dimension: "ROW",
64763
+ size: null,
64764
+ sheetId: cmd.sheetId,
64765
+ });
64713
64766
  break;
64714
64767
  }
64715
64768
  }
@@ -67460,7 +67513,7 @@ class SheetViewPlugin extends UIPlugin {
67460
67513
  "getSheetViewVisibleCols",
67461
67514
  "getSheetViewVisibleRows",
67462
67515
  "getFrozenSheetViewRatio",
67463
- "isPositionVisible",
67516
+ "isPixelPositionVisible",
67464
67517
  "getColDimensionsInViewport",
67465
67518
  "getRowDimensionsInViewport",
67466
67519
  "getAllActiveViewportsZones",
@@ -68084,7 +68137,7 @@ class SheetViewPlugin extends UIPlugin {
68084
68137
  }
68085
68138
  return result;
68086
68139
  }
68087
- isPositionVisible(position) {
68140
+ isPixelPositionVisible(position) {
68088
68141
  const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
68089
68142
  const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
68090
68143
  const { width, height } = this.getters.getSheetViewDimension();
@@ -70479,12 +70532,8 @@ css /* scss */ `
70479
70532
  .o-spreadsheet {
70480
70533
  position: relative;
70481
70534
  display: grid;
70482
- color: ${TEXT_BODY};
70483
70535
  font-size: 14px;
70484
70536
 
70485
- input {
70486
- background-color: white;
70487
- }
70488
70537
  .text-muted {
70489
70538
  color: ${TEXT_BODY_MUTED} !important;
70490
70539
  }
@@ -71731,6 +71780,9 @@ class EventStream {
71731
71780
  observe(owner, callbacks) {
71732
71781
  this.observers.set(owner, { owner, callbacks });
71733
71782
  }
71783
+ detachObserver(owner) {
71784
+ this.observers.delete(owner);
71785
+ }
71734
71786
  /**
71735
71787
  * Capture the stream for yourself
71736
71788
  */
@@ -71823,6 +71875,9 @@ class SelectionStreamProcessorImpl {
71823
71875
  observe(owner, callbacks) {
71824
71876
  this.stream.observe(owner, callbacks);
71825
71877
  }
71878
+ detachObserver(owner) {
71879
+ this.stream.detachObserver(owner);
71880
+ }
71826
71881
  release(owner) {
71827
71882
  if (this.stream.isListening(owner)) {
71828
71883
  this.stream.release(owner);
@@ -75227,6 +75282,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75227
75282
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75228
75283
 
75229
75284
 
75230
- __info__.version = "18.2.0-alpha.4";
75231
- __info__.date = "2025-01-29T06:30:12.773Z";
75232
- __info__.hash = "6838c26";
75285
+ __info__.version = "18.2.0-alpha.5";
75286
+ __info__.date = "2025-01-31T07:59:30.667Z";
75287
+ __info__.hash = "efce841";