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