@odoo/o-spreadsheet 18.2.0-alpha.3 → 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.3
6
- * @date 2025-01-27T10:07:28.716Z
7
- * @hash 63a13e5
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.
@@ -40094,14 +40210,21 @@ class ChartTypePicker extends Component {
40094
40210
  class MainChartPanelStore extends SpreadsheetStore {
40095
40211
  mutators = ["activatePanel", "changeChartType"];
40096
40212
  panel = "configuration";
40097
- creationContext = {};
40213
+ creationContexts = {};
40098
40214
  activatePanel(panel) {
40099
40215
  this.panel = panel;
40100
40216
  }
40101
40217
  changeChartType(figureId, newDisplayType) {
40102
- this.creationContext = {
40103
- ...this.creationContext,
40104
- ...this.getters.getContextCreationChart(figureId),
40218
+ const currentCreationContext = this.getters.getContextCreationChart(figureId);
40219
+ const savedCreationContext = this.creationContexts[figureId] || {};
40220
+ let newRanges = currentCreationContext?.range;
40221
+ if (newRanges?.every((range, i) => deepEquals(range, savedCreationContext.range?.[i]))) {
40222
+ newRanges = Object.assign([], savedCreationContext.range, currentCreationContext?.range);
40223
+ }
40224
+ this.creationContexts[figureId] = {
40225
+ ...savedCreationContext,
40226
+ ...currentCreationContext,
40227
+ range: newRanges,
40105
40228
  };
40106
40229
  const sheetId = this.getters.getFigureSheetId(figureId);
40107
40230
  if (!sheetId) {
@@ -40117,12 +40240,8 @@ class MainChartPanelStore extends SpreadsheetStore {
40117
40240
  getChartDefinitionFromContextCreation(figureId, newDisplayType) {
40118
40241
  const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
40119
40242
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
40120
- const contextCreation = {
40121
- ...this.creationContext,
40122
- ...this.getters.getContextCreationChart(figureId),
40123
- };
40124
40243
  return {
40125
- ...ChartClass.getChartDefinitionFromContextCreation(contextCreation),
40244
+ ...ChartClass.getChartDefinitionFromContextCreation(this.creationContexts[figureId]),
40126
40245
  ...newChartInfo.subtypeDefinition,
40127
40246
  };
40128
40247
  }
@@ -40333,6 +40452,10 @@ class ContentEditableHelper {
40333
40452
  if (currentStart === start && currentEnd === end) {
40334
40453
  return;
40335
40454
  }
40455
+ if (selection.rangeCount === 0) {
40456
+ const range = document.createRange();
40457
+ selection.addRange(range);
40458
+ }
40336
40459
  const currentRange = selection.getRangeAt(0);
40337
40460
  let range;
40338
40461
  if (this.el.contains(currentRange.startContainer)) {
@@ -40495,7 +40618,7 @@ class ContentEditableHelper {
40495
40618
  if (!focusedNode || !this.el.contains(focusedNode))
40496
40619
  return;
40497
40620
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40498
- element?.scrollIntoView({ block: "nearest" });
40621
+ element?.scrollIntoView?.({ block: "nearest" });
40499
40622
  }
40500
40623
  /**
40501
40624
  * remove the current selection of the user
@@ -40515,100 +40638,7 @@ class ContentEditableHelper {
40515
40638
  * finds the indexes of the current selection.
40516
40639
  * */
40517
40640
  getCurrentSelection() {
40518
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40519
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40520
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40521
- return {
40522
- start: startSizeBefore,
40523
- end: endSizeBefore,
40524
- };
40525
- }
40526
- /**
40527
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40528
- * The selected node is either a Text node or an Element node.
40529
- *
40530
- * case 1 -Text node:
40531
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40532
- * content length of all previous nodes.
40533
- *
40534
- * case 2 - Element node:
40535
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40536
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40537
- *
40538
- * See the MDN documentation for more details.
40539
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40540
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40541
- *
40542
- */
40543
- findSelectionIndex(nodeToFind, nodeOffset) {
40544
- let usedCharacters = 0;
40545
- let it = iterateChildren(this.el);
40546
- let current = it.next();
40547
- let isFirstParagraph = true;
40548
- while (!current.done && current.value !== nodeToFind) {
40549
- if (!current.value.hasChildNodes()) {
40550
- if (current.value.textContent) {
40551
- usedCharacters += current.value.textContent.length;
40552
- }
40553
- }
40554
- // One new paragraph = one new line character, except for the first paragraph
40555
- if (current.value.nodeName === "P" ||
40556
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40557
- ) {
40558
- if (isFirstParagraph) {
40559
- isFirstParagraph = false;
40560
- }
40561
- else {
40562
- usedCharacters++;
40563
- }
40564
- }
40565
- current = it.next();
40566
- }
40567
- if (current.value !== nodeToFind) {
40568
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40569
- * In this case, we return 0 because we don't know the size of the text before the selection.
40570
- *
40571
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40572
- *
40573
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40574
- */
40575
- return 0;
40576
- }
40577
- else {
40578
- if (!current.value.hasChildNodes()) {
40579
- usedCharacters += nodeOffset;
40580
- }
40581
- else {
40582
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40583
- usedCharacters += children.reduce((acc, child, index) => {
40584
- if (child.textContent !== null) {
40585
- // need to account for paragraph nodes that implicitely add a new line
40586
- // except for the last paragraph
40587
- let chars = child.textContent.length;
40588
- if (child.nodeName === "P" && index !== children.length - 1) {
40589
- chars++;
40590
- }
40591
- return acc + chars;
40592
- }
40593
- else {
40594
- return acc;
40595
- }
40596
- }, 0);
40597
- }
40598
- }
40599
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40600
- usedCharacters++;
40601
- }
40602
- return usedCharacters;
40603
- }
40604
- getStartAndEndSelection() {
40605
- const selection = document.getSelection();
40606
- return {
40607
- startElement: selection.anchorNode || this.el,
40608
- startSelectionOffset: selection.anchorOffset,
40609
- endElement: selection.focusNode || this.el,
40610
- endSelectionOffset: selection.focusOffset,
40611
- };
40641
+ return getCurrentSelection(this.el);
40612
40642
  }
40613
40643
  getText() {
40614
40644
  let text = "";
@@ -40872,6 +40902,12 @@ class Composer extends Component {
40872
40902
  }
40873
40903
  this.contentHelper.updateEl(el);
40874
40904
  });
40905
+ this.env.model.selection.observe(this, {
40906
+ handleEvent: () => this.autoCompleteState.hide(),
40907
+ });
40908
+ onWillUnmount(() => {
40909
+ this.env.model.selection.detachObserver(this);
40910
+ });
40875
40911
  useEffect(() => {
40876
40912
  this.processContent();
40877
40913
  if (document.activeElement === this.contentHelper.el &&
@@ -49211,7 +49247,7 @@ function isAxisVisible(getters, figure, axis) {
49211
49247
  axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
49212
49248
  break;
49213
49249
  }
49214
- return axisStartEndPositions.some(getters.isPositionVisible);
49250
+ return axisStartEndPositions.some(getters.isPixelPositionVisible);
49215
49251
  }
49216
49252
  /**
49217
49253
  * Get a snap line for the given figure, if the figure can snap to any other figure
@@ -51238,8 +51274,8 @@ class GridRenderer {
51238
51274
  previousColIndex = col;
51239
51275
  }
51240
51276
  else {
51241
- nextColIndex = this.findNextEmptyCol(col, right, row);
51242
- 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);
51243
51279
  box.isOverflow = true;
51244
51280
  }
51245
51281
  switch (align) {
@@ -52712,7 +52748,7 @@ class BordersPlugin extends CorePlugin {
52712
52748
  // map and slice preserve empty values and do not set `undefined` instead
52713
52749
  const bordersCopy = borders
52714
52750
  .slice()
52715
- .map((col) => col?.slice().map((border) => ({ ...border })));
52751
+ .map((col) => col?.slice().map((border) => deepCopy(border)));
52716
52752
  this.history.update("borders", cmd.sheetIdTo, bordersCopy);
52717
52753
  }
52718
52754
  break;
@@ -52742,32 +52778,12 @@ class BordersPlugin extends CorePlugin {
52742
52778
  const elements = [...cmd.elements].sort((a, b) => b - a);
52743
52779
  for (const group of groupConsecutive(elements)) {
52744
52780
  if (cmd.dimension === "COL") {
52745
- if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52746
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52747
- this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52748
- }
52749
- }
52750
- if (group[group.length - 1] === 0) {
52751
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52752
- this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52753
- }
52754
- }
52755
- 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]);
52756
52782
  this.clearInsideBorders(cmd.sheetId, [zone]);
52757
52783
  this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52758
52784
  }
52759
52785
  else {
52760
- if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52761
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52762
- this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52763
- }
52764
- }
52765
- if (group[group.length - 1] === 0) {
52766
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52767
- this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52768
- }
52769
- }
52770
- 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]);
52771
52787
  this.clearInsideBorders(cmd.sheetId, [zone]);
52772
52788
  this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52773
52789
  }
@@ -52792,16 +52808,12 @@ class BordersPlugin extends CorePlugin {
52792
52808
  let colLeftOfInsertion;
52793
52809
  let colRightOfInsertion;
52794
52810
  if (cmd.position === "before") {
52795
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity, {
52796
- moveFirstLeftBorder: true,
52797
- });
52811
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity);
52798
52812
  colLeftOfInsertion = cmd.base - 1;
52799
52813
  colRightOfInsertion = cmd.base + cmd.quantity;
52800
52814
  }
52801
52815
  else {
52802
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52803
- moveFirstLeftBorder: false,
52804
- });
52816
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity);
52805
52817
  colLeftOfInsertion = cmd.base;
52806
52818
  colRightOfInsertion = cmd.base + cmd.quantity + 1;
52807
52819
  }
@@ -52816,16 +52828,12 @@ class BordersPlugin extends CorePlugin {
52816
52828
  let rowAboveInsertion;
52817
52829
  let rowBelowInsertion;
52818
52830
  if (cmd.position === "before") {
52819
- this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity, {
52820
- moveFirstTopBorder: true,
52821
- });
52831
+ this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity);
52822
52832
  rowAboveInsertion = cmd.base - 1;
52823
52833
  rowBelowInsertion = cmd.base + cmd.quantity;
52824
52834
  }
52825
52835
  else {
52826
- this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52827
- moveFirstTopBorder: false,
52828
- });
52836
+ this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity);
52829
52837
  rowAboveInsertion = cmd.base;
52830
52838
  rowBelowInsertion = cmd.base + cmd.quantity + 1;
52831
52839
  }
@@ -52835,16 +52843,8 @@ class BordersPlugin extends CorePlugin {
52835
52843
  // Getters
52836
52844
  // ---------------------------------------------------------------------------
52837
52845
  getCellBorder({ sheetId, col, row }) {
52838
- const border = {
52839
- top: this.borders[sheetId]?.[col]?.[row]?.horizontal,
52840
- bottom: this.borders[sheetId]?.[col]?.[row + 1]?.horizontal,
52841
- left: this.borders[sheetId]?.[col]?.[row]?.vertical,
52842
- right: this.borders[sheetId]?.[col + 1]?.[row]?.vertical,
52843
- };
52844
- if (!border.bottom && !border.left && !border.right && !border.top) {
52845
- return null;
52846
- }
52847
- return border;
52846
+ const border = this.borders[sheetId]?.[col]?.[row];
52847
+ return border?.top || border?.bottom || border?.left || border?.right ? deepCopy(border) : null;
52848
52848
  }
52849
52849
  getBordersColors(sheetId) {
52850
52850
  const colors = [];
@@ -52852,11 +52852,13 @@ class BordersPlugin extends CorePlugin {
52852
52852
  if (sheetBorders) {
52853
52853
  for (const borders of sheetBorders.filter(isDefined)) {
52854
52854
  for (const cellBorder of borders) {
52855
- if (cellBorder?.horizontal) {
52856
- colors.push(cellBorder.horizontal.color);
52857
- }
52858
- if (cellBorder?.vertical) {
52859
- 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
+ }
52860
52862
  }
52861
52863
  }
52862
52864
  }
@@ -52909,7 +52911,7 @@ class BordersPlugin extends CorePlugin {
52909
52911
  getCommonSides(border1, border2) {
52910
52912
  const commonBorder = {};
52911
52913
  for (let side of ["top", "bottom", "left", "right"]) {
52912
- if (border1[side] && border1[side] === border2[side]) {
52914
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52913
52915
  commonBorder[side] = border1[side];
52914
52916
  }
52915
52917
  }
@@ -52954,23 +52956,15 @@ class BordersPlugin extends CorePlugin {
52954
52956
  * @param start starting column (included)
52955
52957
  * @param delta how much borders will be moved (negative if moved to the left)
52956
52958
  */
52957
- shiftBordersHorizontally(sheetId, start, delta, { moveFirstLeftBorder } = {}) {
52959
+ shiftBordersHorizontally(sheetId, start, delta) {
52958
52960
  const borders = this.borders[sheetId];
52959
52961
  if (!borders)
52960
52962
  return;
52961
- if (delta < 0) {
52962
- this.moveBordersOfColumn(sheetId, start, delta, "vertical", {
52963
- destructive: false,
52964
- });
52965
- }
52966
52963
  this.getColumnsWithBorders(sheetId)
52967
52964
  .filter((col) => col >= start)
52968
52965
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52969
52966
  .forEach((col) => {
52970
- if ((col === start && moveFirstLeftBorder) || col !== start) {
52971
- this.moveBordersOfColumn(sheetId, col, delta, "vertical");
52972
- }
52973
- this.moveBordersOfColumn(sheetId, col, delta, "horizontal");
52967
+ this.moveBordersOfColumn(sheetId, col, delta);
52974
52968
  });
52975
52969
  }
52976
52970
  /**
@@ -52979,12 +52973,12 @@ class BordersPlugin extends CorePlugin {
52979
52973
  * @param start starting row (included)
52980
52974
  * @param delta how much borders will be moved (negative if moved to the above)
52981
52975
  */
52982
- shiftBordersVertically(sheetId, start, delta, { moveFirstTopBorder } = {}) {
52976
+ shiftBordersVertically(sheetId, start, delta) {
52983
52977
  const borders = this.borders[sheetId];
52984
52978
  if (!borders)
52985
52979
  return;
52986
52980
  if (delta < 0) {
52987
- this.moveBordersOfRow(sheetId, start, delta, "horizontal", {
52981
+ this.moveBordersOfRow(sheetId, start, delta, {
52988
52982
  destructive: false,
52989
52983
  });
52990
52984
  }
@@ -52992,10 +52986,7 @@ class BordersPlugin extends CorePlugin {
52992
52986
  .filter((row) => row >= start)
52993
52987
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52994
52988
  .forEach((row) => {
52995
- if ((row === start && moveFirstTopBorder) || row !== start) {
52996
- this.moveBordersOfRow(sheetId, row, delta, "horizontal");
52997
- }
52998
- this.moveBordersOfRow(sheetId, row, delta, "vertical");
52989
+ this.moveBordersOfRow(sheetId, row, delta);
52999
52990
  });
53000
52991
  }
53001
52992
  /**
@@ -53009,15 +53000,15 @@ class BordersPlugin extends CorePlugin {
53009
53000
  * argument `destructive` is given false, the target border is preserved if
53010
53001
  * the moved border is empty
53011
53002
  */
53012
- moveBordersOfRow(sheetId, row, delta, borderDirection, { destructive } = { destructive: true }) {
53003
+ moveBordersOfRow(sheetId, row, delta, { destructive } = { destructive: true }) {
53013
53004
  const borders = this.borders[sheetId];
53014
53005
  if (!borders)
53015
53006
  return;
53016
53007
  this.getColumnsWithBorders(sheetId).forEach((col) => {
53017
- const targetBorder = borders[col]?.[row + delta]?.[borderDirection];
53018
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53019
- this.history.update("borders", sheetId, col, row + delta, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53020
- 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);
53021
53012
  });
53022
53013
  }
53023
53014
  /**
@@ -53031,15 +53022,17 @@ class BordersPlugin extends CorePlugin {
53031
53022
  * argument `destructive` is given false, the target border is preserved if
53032
53023
  * the moved border is empty
53033
53024
  */
53034
- moveBordersOfColumn(sheetId, col, delta, borderDirection, { destructive } = { destructive: true }) {
53025
+ moveBordersOfColumn(sheetId, col, delta, { destructive } = { destructive: true }) {
53035
53026
  const borders = this.borders[sheetId];
53036
53027
  if (!borders)
53037
53028
  return;
53038
53029
  this.getRowsRange(sheetId).forEach((row) => {
53039
- const targetBorder = borders[col + delta]?.[row]?.[borderDirection];
53040
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53041
- this.history.update("borders", sheetId, col + delta, row, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53042
- 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
+ }
53043
53036
  });
53044
53037
  }
53045
53038
  /**
@@ -53047,33 +53040,69 @@ class BordersPlugin extends CorePlugin {
53047
53040
  * It overrides the current border if override === true.
53048
53041
  */
53049
53042
  setBorder(sheetId, col, row, border, override = true) {
53050
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
53051
- 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
+ }
53052
53052
  }
53053
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.horizontal) {
53054
- 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
+ }
53055
53060
  }
53056
- if (override || !this.borders?.[sheetId]?.[col + 1]?.[row]?.vertical) {
53057
- 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
+ }
53058
53068
  }
53059
- if (override || !this.borders?.[sheetId]?.[col]?.[row + 1]?.horizontal) {
53060
- 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
+ }
53061
53076
  }
53062
53077
  }
53063
53078
  /**
53064
53079
  * Remove the borders of a zone
53065
53080
  */
53066
- 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;
53067
53084
  for (let zone of recomputeZones(zones)) {
53068
53085
  for (let row = zone.top; row <= zone.bottom; row++) {
53069
- 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
+ }
53070
53094
  for (let col = zone.left; col <= zone.right; col++) {
53071
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
+ }
53072
53104
  }
53073
53105
  }
53074
- for (let col = zone.left; col <= zone.right; col++) {
53075
- this.history.update("borders", sheetId, col, zone.bottom + 1, "horizontal", undefined);
53076
- }
53077
53106
  }
53078
53107
  }
53079
53108
  /**
@@ -53103,41 +53132,63 @@ class BordersPlugin extends CorePlugin {
53103
53132
  */
53104
53133
  setBorders(sheetId, zones, position, border) {
53105
53134
  if (position === "clear") {
53106
- return this.clearBorders(sheetId, zones);
53135
+ return this.clearBorders(sheetId, zones, true);
53107
53136
  }
53108
53137
  for (let zone of recomputeZones(zones)) {
53109
- if (position === "h" || position === "hv" || position === "all") {
53110
- for (let row = zone.top + 1; row <= zone.bottom; row++) {
53138
+ if (position === "all") {
53139
+ for (let row = zone.top; row <= zone.bottom; row++) {
53111
53140
  for (let col = zone.left; col <= zone.right; col++) {
53112
- 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
+ });
53147
+ }
53148
+ }
53149
+ }
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 });
53113
53158
  }
53159
+ this.addBorder(sheetId, col, zone.bottom, { top: border });
53114
53160
  }
53115
53161
  }
53116
- if (position === "v" || position === "hv" || position === "all") {
53162
+ if (position === "v" || position === "hv") {
53163
+ if (zone.left === zone.right) {
53164
+ continue;
53165
+ }
53117
53166
  for (let row = zone.top; row <= zone.bottom; row++) {
53118
- for (let col = zone.left + 1; col <= zone.right; col++) {
53119
- 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 });
53120
53170
  }
53171
+ this.addBorder(sheetId, zone.right, row, { left: border });
53121
53172
  }
53122
53173
  }
53123
- if (position === "left" || position === "all" || position === "external") {
53174
+ if (position === "left" || position === "external") {
53124
53175
  for (let row = zone.top; row <= zone.bottom; row++) {
53125
53176
  this.addBorder(sheetId, zone.left, row, { left: border });
53126
53177
  }
53127
53178
  }
53128
- if (position === "right" || position === "all" || position === "external") {
53179
+ if (position === "right" || position === "external") {
53129
53180
  for (let row = zone.top; row <= zone.bottom; row++) {
53130
- this.addBorder(sheetId, zone.right + 1, row, { left: border });
53181
+ this.addBorder(sheetId, zone.right, row, { right: border });
53131
53182
  }
53132
53183
  }
53133
- if (position === "top" || position === "all" || position === "external") {
53184
+ if (position === "top" || position === "external") {
53134
53185
  for (let col = zone.left; col <= zone.right; col++) {
53135
53186
  this.addBorder(sheetId, col, zone.top, { top: border });
53136
53187
  }
53137
53188
  }
53138
- if (position === "bottom" || position === "all" || position === "external") {
53189
+ if (position === "bottom" || position === "external") {
53139
53190
  for (let col = zone.left; col <= zone.right; col++) {
53140
- this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
53191
+ this.addBorder(sheetId, col, zone.bottom, { bottom: border });
53141
53192
  }
53142
53193
  }
53143
53194
  }
@@ -54683,10 +54734,20 @@ class DataValidationPlugin extends CorePlugin {
54683
54734
  for (const sheet of data.sheets) {
54684
54735
  sheet.dataValidationRules = [];
54685
54736
  for (const rule of this.rules[sheet.id]) {
54686
- sheet.dataValidationRules.push({
54687
- ...rule,
54737
+ const excelRule = {
54738
+ ...deepCopy(rule),
54688
54739
  ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useBoundedReference: true })),
54689
- });
54740
+ };
54741
+ if (rule.criterion.type === "isValueInRange") {
54742
+ excelRule.criterion.values = rule.criterion.values.map((value) => {
54743
+ const range = this.getters.getRangeFromSheetXC(sheet.id, value);
54744
+ return this.getters.getRangeString(range, sheet.id, {
54745
+ useBoundedReference: true,
54746
+ useFixedReference: true,
54747
+ });
54748
+ });
54749
+ }
54750
+ sheet.dataValidationRules.push(excelRule);
54690
54751
  }
54691
54752
  }
54692
54753
  }
@@ -56107,9 +56168,10 @@ class RangeAdapter {
56107
56168
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
56108
56169
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
56109
56170
  * @param options
56110
- * @param options.useBoundedReference if true, the range will be returned with fixed row and column
56171
+ * @param options.useBoundedReference if true, the range will be returned with bounded row and column
56172
+ * @param options.useFixedReference if true, the range will be returned with fixed row and column
56111
56173
  */
56112
- getRangeString(range, forSheetId, options = { useBoundedReference: false }) {
56174
+ getRangeString(range, forSheetId, options = { useBoundedReference: false, useFixedReference: false }) {
56113
56175
  if (!range) {
56114
56176
  return CellErrorType.InvalidReference;
56115
56177
  }
@@ -56212,10 +56274,10 @@ class RangeAdapter {
56212
56274
  /**
56213
56275
  * Get a Xc string that represent a part of a range
56214
56276
  */
56215
- getRangePartString(range, part, options = { useBoundedReference: false }) {
56216
- const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
56277
+ getRangePartString(range, part, options = { useBoundedReference: false, useFixedReference: false }) {
56278
+ const colFixed = range.parts[part]?.colFixed || options.useFixedReference ? "$" : "";
56217
56279
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
56218
- const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
56280
+ const rowFixed = range.parts[part]?.rowFixed || options.useFixedReference ? "$" : "";
56219
56281
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
56220
56282
  let str = "";
56221
56283
  if (range.isFullCol && !options.useBoundedReference) {
@@ -59871,7 +59933,11 @@ class Evaluator {
59871
59933
  computeFormulaCell(formulaPosition, cellData) {
59872
59934
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
59873
59935
  if (!isMatrix(formulaReturn)) {
59874
- 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;
59875
59941
  }
59876
59942
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
59877
59943
  const nbColumns = formulaReturn.length;
@@ -59944,6 +60010,9 @@ class Evaluator {
59944
60010
  const position = { sheetId, col: i + col, row: j + row };
59945
60011
  const cell = this.getters.getCell(position);
59946
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
+ }
59947
60016
  this.evaluatedCells.set(position, evaluatedCell);
59948
60017
  };
59949
60018
  return spreadValues;
@@ -61502,7 +61571,7 @@ function withPivotPresentationLayer (PivotClass) {
61502
61571
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
61503
61572
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
61504
61573
  }
61505
- return this._getPivotCellValueAndFormat(symbolName, domain);
61574
+ return this.getPivotCellValueAndFormat(symbolName, domain);
61506
61575
  };
61507
61576
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
61508
61577
  if (isMatrix(result)) {
@@ -63401,6 +63470,7 @@ class Session extends EventBus {
63401
63470
  waitingUndoRedoAck = false;
63402
63471
  isReplayingInitialRevisions = false;
63403
63472
  processedRevisions = new Set();
63473
+ lastRevisionMessage = undefined;
63404
63474
  uuidGenerator = new UuidGenerator();
63405
63475
  lastLocalOperation;
63406
63476
  /**
@@ -63501,7 +63571,10 @@ class Session extends EventBus {
63501
63571
  * Notify the server that the user client left the collaborative session
63502
63572
  */
63503
63573
  async leave(data) {
63504
- if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
63574
+ if (data &&
63575
+ Object.keys(this.clients).length === 1 &&
63576
+ this.lastRevisionMessage &&
63577
+ this.lastRevisionMessage?.type !== "SNAPSHOT_CREATED") {
63505
63578
  await this.snapshot(data());
63506
63579
  }
63507
63580
  delete this.clients[this.clientId];
@@ -63722,6 +63795,7 @@ class Session extends EventBus {
63722
63795
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63723
63796
  this.serverRevisionId = message.nextRevisionId;
63724
63797
  this.processedRevisions.add(message.nextRevisionId);
63798
+ this.lastRevisionMessage = message;
63725
63799
  this.sendPendingMessage();
63726
63800
  break;
63727
63801
  }
@@ -64683,14 +64757,12 @@ class SheetUIPlugin extends UIPlugin {
64683
64757
  }
64684
64758
  break;
64685
64759
  case "AUTORESIZE_ROWS":
64686
- for (let row of cmd.rows) {
64687
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64688
- elements: [row],
64689
- dimension: "ROW",
64690
- size: null,
64691
- sheetId: cmd.sheetId,
64692
- });
64693
- }
64760
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
64761
+ elements: cmd.rows,
64762
+ dimension: "ROW",
64763
+ size: null,
64764
+ sheetId: cmd.sheetId,
64765
+ });
64694
64766
  break;
64695
64767
  }
64696
64768
  }
@@ -67441,7 +67513,7 @@ class SheetViewPlugin extends UIPlugin {
67441
67513
  "getSheetViewVisibleCols",
67442
67514
  "getSheetViewVisibleRows",
67443
67515
  "getFrozenSheetViewRatio",
67444
- "isPositionVisible",
67516
+ "isPixelPositionVisible",
67445
67517
  "getColDimensionsInViewport",
67446
67518
  "getRowDimensionsInViewport",
67447
67519
  "getAllActiveViewportsZones",
@@ -68065,7 +68137,7 @@ class SheetViewPlugin extends UIPlugin {
68065
68137
  }
68066
68138
  return result;
68067
68139
  }
68068
- isPositionVisible(position) {
68140
+ isPixelPositionVisible(position) {
68069
68141
  const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
68070
68142
  const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
68071
68143
  const { width, height } = this.getters.getSheetViewDimension();
@@ -70460,12 +70532,8 @@ css /* scss */ `
70460
70532
  .o-spreadsheet {
70461
70533
  position: relative;
70462
70534
  display: grid;
70463
- color: ${TEXT_BODY};
70464
70535
  font-size: 14px;
70465
70536
 
70466
- input {
70467
- background-color: white;
70468
- }
70469
70537
  .text-muted {
70470
70538
  color: ${TEXT_BODY_MUTED} !important;
70471
70539
  }
@@ -71712,6 +71780,9 @@ class EventStream {
71712
71780
  observe(owner, callbacks) {
71713
71781
  this.observers.set(owner, { owner, callbacks });
71714
71782
  }
71783
+ detachObserver(owner) {
71784
+ this.observers.delete(owner);
71785
+ }
71715
71786
  /**
71716
71787
  * Capture the stream for yourself
71717
71788
  */
@@ -71804,6 +71875,9 @@ class SelectionStreamProcessorImpl {
71804
71875
  observe(owner, callbacks) {
71805
71876
  this.stream.observe(owner, callbacks);
71806
71877
  }
71878
+ detachObserver(owner) {
71879
+ this.stream.detachObserver(owner);
71880
+ }
71807
71881
  release(owner) {
71808
71882
  if (this.stream.isListening(owner)) {
71809
71883
  this.stream.release(owner);
@@ -75208,6 +75282,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75208
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 };
75209
75283
 
75210
75284
 
75211
- __info__.version = "18.2.0-alpha.3";
75212
- __info__.date = "2025-01-27T10:07:28.716Z";
75213
- __info__.hash = "63a13e5";
75285
+ __info__.version = "18.2.0-alpha.5";
75286
+ __info__.date = "2025-01-31T07:59:30.667Z";
75287
+ __info__.hash = "efce841";