@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
  (function (exports, owl) {
@@ -3360,11 +3360,11 @@
3360
3360
  * number from the point of view of the isNumber function.
3361
3361
  */
3362
3362
  function parseNumber(str, locale) {
3363
+ // remove invaluable characters
3364
+ str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3363
3365
  if (locale.decimalSeparator !== ".") {
3364
3366
  str = str.replace(locale.decimalSeparator, ".");
3365
3367
  }
3366
- // remove invaluable characters
3367
- str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3368
3368
  let n = Number(str);
3369
3369
  if (isNaN(n) && str.includes("%")) {
3370
3370
  n = Number(str.split("%")[0]);
@@ -32537,36 +32537,58 @@ stores.inject(MyMetaStore, storeInstance);
32537
32537
  static maxSize = { maxHeight: ERROR_TOOLTIP_MAX_HEIGHT };
32538
32538
  static template = "o-spreadsheet-ErrorToolTip";
32539
32539
  static props = {
32540
- errors: Array,
32540
+ cellPosition: Object,
32541
32541
  onClosed: { type: Function, optional: true },
32542
32542
  };
32543
+ get dataValidationErrorMessage() {
32544
+ return this.env.model.getters.getInvalidDataValidationMessage(this.props.cellPosition);
32545
+ }
32546
+ get evaluationError() {
32547
+ const cell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
32548
+ if (cell.message) {
32549
+ return cell;
32550
+ }
32551
+ return undefined;
32552
+ }
32553
+ get errorOriginPositionString() {
32554
+ const evaluationError = this.evaluationError;
32555
+ const position = evaluationError?.errorOriginPosition;
32556
+ if (!position || deepEquals(position, this.props.cellPosition)) {
32557
+ return "";
32558
+ }
32559
+ const sheetId = position.sheetId;
32560
+ return this.env.model.getters.getRangeString(this.env.model.getters.getRangeFromZone(sheetId, positionToZone(position)), this.env.model.getters.getActiveSheetId());
32561
+ }
32562
+ selectCell() {
32563
+ const position = this.evaluationError?.errorOriginPosition;
32564
+ if (!position) {
32565
+ return;
32566
+ }
32567
+ const activeSheetId = this.env.model.getters.getActiveSheetId();
32568
+ if (position.sheetId !== activeSheetId) {
32569
+ this.env.model.dispatch("ACTIVATE_SHEET", {
32570
+ sheetIdFrom: activeSheetId,
32571
+ sheetIdTo: position.sheetId,
32572
+ });
32573
+ }
32574
+ this.env.model.selection.selectCell(position.col, position.row);
32575
+ }
32543
32576
  }
32544
32577
  const ErrorToolTipPopoverBuilder = {
32545
32578
  onHover: (position, getters) => {
32546
32579
  const cell = getters.getEvaluatedCell(position);
32547
- const errors = [];
32548
- if (cell.type === CellValueType.error && !!cell.message) {
32549
- errors.push({
32550
- title: _t("Error"),
32551
- message: cell.message,
32552
- });
32553
- }
32554
- const validationErrorMessage = getters.getInvalidDataValidationMessage(position);
32555
- if (validationErrorMessage) {
32556
- errors.push({
32557
- title: _t("Invalid"),
32558
- message: validationErrorMessage,
32559
- });
32560
- }
32561
- if (!errors.length) {
32562
- return { isOpen: false };
32580
+ if ((cell.type === CellValueType.error && !!cell.message) ||
32581
+ getters.getInvalidDataValidationMessage(position)) {
32582
+ return {
32583
+ isOpen: true,
32584
+ props: {
32585
+ cellPosition: position,
32586
+ },
32587
+ Component: ErrorToolTip,
32588
+ cellCorner: "TopRight",
32589
+ };
32563
32590
  }
32564
- return {
32565
- isOpen: true,
32566
- props: { errors: errors },
32567
- Component: ErrorToolTip,
32568
- cellCorner: "TopRight",
32569
- };
32591
+ return { isOpen: false };
32570
32592
  },
32571
32593
  };
32572
32594
 
@@ -33130,6 +33152,100 @@ stores.inject(MyMetaStore, storeInstance);
33130
33152
  function getOpenedMenus() {
33131
33153
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33132
33154
  }
33155
+ function getCurrentSelection(el) {
33156
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33157
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33158
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33159
+ return {
33160
+ start: startSizeBefore,
33161
+ end: endSizeBefore,
33162
+ };
33163
+ }
33164
+ function getStartAndEndSelection(el) {
33165
+ const selection = document.getSelection();
33166
+ return {
33167
+ startElement: selection.anchorNode || el,
33168
+ startSelectionOffset: selection.anchorOffset,
33169
+ endElement: selection.focusNode || el,
33170
+ endSelectionOffset: selection.focusOffset,
33171
+ };
33172
+ }
33173
+ /**
33174
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33175
+ * The selected node is either a Text node or an Element node.
33176
+ *
33177
+ * case 1 -Text node:
33178
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33179
+ * content length of all previous nodes.
33180
+ *
33181
+ * case 2 - Element node:
33182
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33183
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33184
+ *
33185
+ * See the MDN documentation for more details.
33186
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33187
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33188
+ *
33189
+ */
33190
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33191
+ let usedCharacters = 0;
33192
+ let it = iterateChildren(el);
33193
+ let current = it.next();
33194
+ let isFirstParagraph = true;
33195
+ while (!current.done && current.value !== nodeToFind) {
33196
+ if (!current.value.hasChildNodes()) {
33197
+ if (current.value.textContent) {
33198
+ usedCharacters += current.value.textContent.length;
33199
+ }
33200
+ }
33201
+ // One new paragraph = one new line character, except for the first paragraph
33202
+ if (current.value.nodeName === "P" ||
33203
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33204
+ ) {
33205
+ if (isFirstParagraph) {
33206
+ isFirstParagraph = false;
33207
+ }
33208
+ else {
33209
+ usedCharacters++;
33210
+ }
33211
+ }
33212
+ current = it.next();
33213
+ }
33214
+ if (current.value !== nodeToFind) {
33215
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33216
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33217
+ *
33218
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33219
+ */
33220
+ return 0;
33221
+ }
33222
+ else {
33223
+ if (!current.value.hasChildNodes()) {
33224
+ usedCharacters += nodeOffset;
33225
+ }
33226
+ else {
33227
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33228
+ usedCharacters += children.reduce((acc, child, index) => {
33229
+ if (child.textContent !== null) {
33230
+ // need to account for paragraph nodes that implicitly add a new line
33231
+ // except for the last paragraph
33232
+ let chars = child.textContent.length;
33233
+ if (child.nodeName === "P" && index !== children.length - 1) {
33234
+ chars++;
33235
+ }
33236
+ return acc + chars;
33237
+ }
33238
+ else {
33239
+ return acc;
33240
+ }
33241
+ }, 0);
33242
+ }
33243
+ }
33244
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33245
+ usedCharacters++;
33246
+ }
33247
+ return usedCharacters;
33248
+ }
33133
33249
  const letterRegex = /^[a-zA-Z]$/;
33134
33250
  /**
33135
33251
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -40095,14 +40211,21 @@ stores.inject(MyMetaStore, storeInstance);
40095
40211
  class MainChartPanelStore extends SpreadsheetStore {
40096
40212
  mutators = ["activatePanel", "changeChartType"];
40097
40213
  panel = "configuration";
40098
- creationContext = {};
40214
+ creationContexts = {};
40099
40215
  activatePanel(panel) {
40100
40216
  this.panel = panel;
40101
40217
  }
40102
40218
  changeChartType(figureId, newDisplayType) {
40103
- this.creationContext = {
40104
- ...this.creationContext,
40105
- ...this.getters.getContextCreationChart(figureId),
40219
+ const currentCreationContext = this.getters.getContextCreationChart(figureId);
40220
+ const savedCreationContext = this.creationContexts[figureId] || {};
40221
+ let newRanges = currentCreationContext?.range;
40222
+ if (newRanges?.every((range, i) => deepEquals(range, savedCreationContext.range?.[i]))) {
40223
+ newRanges = Object.assign([], savedCreationContext.range, currentCreationContext?.range);
40224
+ }
40225
+ this.creationContexts[figureId] = {
40226
+ ...savedCreationContext,
40227
+ ...currentCreationContext,
40228
+ range: newRanges,
40106
40229
  };
40107
40230
  const sheetId = this.getters.getFigureSheetId(figureId);
40108
40231
  if (!sheetId) {
@@ -40118,12 +40241,8 @@ stores.inject(MyMetaStore, storeInstance);
40118
40241
  getChartDefinitionFromContextCreation(figureId, newDisplayType) {
40119
40242
  const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
40120
40243
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
40121
- const contextCreation = {
40122
- ...this.creationContext,
40123
- ...this.getters.getContextCreationChart(figureId),
40124
- };
40125
40244
  return {
40126
- ...ChartClass.getChartDefinitionFromContextCreation(contextCreation),
40245
+ ...ChartClass.getChartDefinitionFromContextCreation(this.creationContexts[figureId]),
40127
40246
  ...newChartInfo.subtypeDefinition,
40128
40247
  };
40129
40248
  }
@@ -40334,6 +40453,10 @@ stores.inject(MyMetaStore, storeInstance);
40334
40453
  if (currentStart === start && currentEnd === end) {
40335
40454
  return;
40336
40455
  }
40456
+ if (selection.rangeCount === 0) {
40457
+ const range = document.createRange();
40458
+ selection.addRange(range);
40459
+ }
40337
40460
  const currentRange = selection.getRangeAt(0);
40338
40461
  let range;
40339
40462
  if (this.el.contains(currentRange.startContainer)) {
@@ -40496,7 +40619,7 @@ stores.inject(MyMetaStore, storeInstance);
40496
40619
  if (!focusedNode || !this.el.contains(focusedNode))
40497
40620
  return;
40498
40621
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40499
- element?.scrollIntoView({ block: "nearest" });
40622
+ element?.scrollIntoView?.({ block: "nearest" });
40500
40623
  }
40501
40624
  /**
40502
40625
  * remove the current selection of the user
@@ -40516,100 +40639,7 @@ stores.inject(MyMetaStore, storeInstance);
40516
40639
  * finds the indexes of the current selection.
40517
40640
  * */
40518
40641
  getCurrentSelection() {
40519
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40520
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40521
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40522
- return {
40523
- start: startSizeBefore,
40524
- end: endSizeBefore,
40525
- };
40526
- }
40527
- /**
40528
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40529
- * The selected node is either a Text node or an Element node.
40530
- *
40531
- * case 1 -Text node:
40532
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40533
- * content length of all previous nodes.
40534
- *
40535
- * case 2 - Element node:
40536
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40537
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40538
- *
40539
- * See the MDN documentation for more details.
40540
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40541
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40542
- *
40543
- */
40544
- findSelectionIndex(nodeToFind, nodeOffset) {
40545
- let usedCharacters = 0;
40546
- let it = iterateChildren(this.el);
40547
- let current = it.next();
40548
- let isFirstParagraph = true;
40549
- while (!current.done && current.value !== nodeToFind) {
40550
- if (!current.value.hasChildNodes()) {
40551
- if (current.value.textContent) {
40552
- usedCharacters += current.value.textContent.length;
40553
- }
40554
- }
40555
- // One new paragraph = one new line character, except for the first paragraph
40556
- if (current.value.nodeName === "P" ||
40557
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40558
- ) {
40559
- if (isFirstParagraph) {
40560
- isFirstParagraph = false;
40561
- }
40562
- else {
40563
- usedCharacters++;
40564
- }
40565
- }
40566
- current = it.next();
40567
- }
40568
- if (current.value !== nodeToFind) {
40569
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40570
- * In this case, we return 0 because we don't know the size of the text before the selection.
40571
- *
40572
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40573
- *
40574
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40575
- */
40576
- return 0;
40577
- }
40578
- else {
40579
- if (!current.value.hasChildNodes()) {
40580
- usedCharacters += nodeOffset;
40581
- }
40582
- else {
40583
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40584
- usedCharacters += children.reduce((acc, child, index) => {
40585
- if (child.textContent !== null) {
40586
- // need to account for paragraph nodes that implicitely add a new line
40587
- // except for the last paragraph
40588
- let chars = child.textContent.length;
40589
- if (child.nodeName === "P" && index !== children.length - 1) {
40590
- chars++;
40591
- }
40592
- return acc + chars;
40593
- }
40594
- else {
40595
- return acc;
40596
- }
40597
- }, 0);
40598
- }
40599
- }
40600
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40601
- usedCharacters++;
40602
- }
40603
- return usedCharacters;
40604
- }
40605
- getStartAndEndSelection() {
40606
- const selection = document.getSelection();
40607
- return {
40608
- startElement: selection.anchorNode || this.el,
40609
- startSelectionOffset: selection.anchorOffset,
40610
- endElement: selection.focusNode || this.el,
40611
- endSelectionOffset: selection.focusOffset,
40612
- };
40642
+ return getCurrentSelection(this.el);
40613
40643
  }
40614
40644
  getText() {
40615
40645
  let text = "";
@@ -40873,6 +40903,12 @@ stores.inject(MyMetaStore, storeInstance);
40873
40903
  }
40874
40904
  this.contentHelper.updateEl(el);
40875
40905
  });
40906
+ this.env.model.selection.observe(this, {
40907
+ handleEvent: () => this.autoCompleteState.hide(),
40908
+ });
40909
+ owl.onWillUnmount(() => {
40910
+ this.env.model.selection.detachObserver(this);
40911
+ });
40876
40912
  owl.useEffect(() => {
40877
40913
  this.processContent();
40878
40914
  if (document.activeElement === this.contentHelper.el &&
@@ -49212,7 +49248,7 @@ stores.inject(MyMetaStore, storeInstance);
49212
49248
  axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
49213
49249
  break;
49214
49250
  }
49215
- return axisStartEndPositions.some(getters.isPositionVisible);
49251
+ return axisStartEndPositions.some(getters.isPixelPositionVisible);
49216
49252
  }
49217
49253
  /**
49218
49254
  * Get a snap line for the given figure, if the figure can snap to any other figure
@@ -51239,8 +51275,8 @@ stores.inject(MyMetaStore, storeInstance);
51239
51275
  previousColIndex = col;
51240
51276
  }
51241
51277
  else {
51242
- nextColIndex = this.findNextEmptyCol(col, right, row);
51243
- previousColIndex = this.findPreviousEmptyCol(col, left, row);
51278
+ nextColIndex = box.border?.right ? zone.right : this.findNextEmptyCol(col, right, row);
51279
+ previousColIndex = box.border?.left ? zone.left : this.findPreviousEmptyCol(col, left, row);
51244
51280
  box.isOverflow = true;
51245
51281
  }
51246
51282
  switch (align) {
@@ -52713,7 +52749,7 @@ stores.inject(MyMetaStore, storeInstance);
52713
52749
  // map and slice preserve empty values and do not set `undefined` instead
52714
52750
  const bordersCopy = borders
52715
52751
  .slice()
52716
- .map((col) => col?.slice().map((border) => ({ ...border })));
52752
+ .map((col) => col?.slice().map((border) => deepCopy(border)));
52717
52753
  this.history.update("borders", cmd.sheetIdTo, bordersCopy);
52718
52754
  }
52719
52755
  break;
@@ -52743,32 +52779,12 @@ stores.inject(MyMetaStore, storeInstance);
52743
52779
  const elements = [...cmd.elements].sort((a, b) => b - a);
52744
52780
  for (const group of groupConsecutive(elements)) {
52745
52781
  if (cmd.dimension === "COL") {
52746
- if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52747
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52748
- this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52749
- }
52750
- }
52751
- if (group[group.length - 1] === 0) {
52752
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52753
- this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52754
- }
52755
- }
52756
- const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52782
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1], group[0]);
52757
52783
  this.clearInsideBorders(cmd.sheetId, [zone]);
52758
52784
  this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52759
52785
  }
52760
52786
  else {
52761
- if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52762
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52763
- this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52764
- }
52765
- }
52766
- if (group[group.length - 1] === 0) {
52767
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52768
- this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52769
- }
52770
- }
52771
- const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52787
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1], group[0]);
52772
52788
  this.clearInsideBorders(cmd.sheetId, [zone]);
52773
52789
  this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52774
52790
  }
@@ -52793,16 +52809,12 @@ stores.inject(MyMetaStore, storeInstance);
52793
52809
  let colLeftOfInsertion;
52794
52810
  let colRightOfInsertion;
52795
52811
  if (cmd.position === "before") {
52796
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity, {
52797
- moveFirstLeftBorder: true,
52798
- });
52812
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity);
52799
52813
  colLeftOfInsertion = cmd.base - 1;
52800
52814
  colRightOfInsertion = cmd.base + cmd.quantity;
52801
52815
  }
52802
52816
  else {
52803
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52804
- moveFirstLeftBorder: false,
52805
- });
52817
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity);
52806
52818
  colLeftOfInsertion = cmd.base;
52807
52819
  colRightOfInsertion = cmd.base + cmd.quantity + 1;
52808
52820
  }
@@ -52817,16 +52829,12 @@ stores.inject(MyMetaStore, storeInstance);
52817
52829
  let rowAboveInsertion;
52818
52830
  let rowBelowInsertion;
52819
52831
  if (cmd.position === "before") {
52820
- this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity, {
52821
- moveFirstTopBorder: true,
52822
- });
52832
+ this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity);
52823
52833
  rowAboveInsertion = cmd.base - 1;
52824
52834
  rowBelowInsertion = cmd.base + cmd.quantity;
52825
52835
  }
52826
52836
  else {
52827
- this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52828
- moveFirstTopBorder: false,
52829
- });
52837
+ this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity);
52830
52838
  rowAboveInsertion = cmd.base;
52831
52839
  rowBelowInsertion = cmd.base + cmd.quantity + 1;
52832
52840
  }
@@ -52836,16 +52844,8 @@ stores.inject(MyMetaStore, storeInstance);
52836
52844
  // Getters
52837
52845
  // ---------------------------------------------------------------------------
52838
52846
  getCellBorder({ sheetId, col, row }) {
52839
- const border = {
52840
- top: this.borders[sheetId]?.[col]?.[row]?.horizontal,
52841
- bottom: this.borders[sheetId]?.[col]?.[row + 1]?.horizontal,
52842
- left: this.borders[sheetId]?.[col]?.[row]?.vertical,
52843
- right: this.borders[sheetId]?.[col + 1]?.[row]?.vertical,
52844
- };
52845
- if (!border.bottom && !border.left && !border.right && !border.top) {
52846
- return null;
52847
- }
52848
- return border;
52847
+ const border = this.borders[sheetId]?.[col]?.[row];
52848
+ return border?.top || border?.bottom || border?.left || border?.right ? deepCopy(border) : null;
52849
52849
  }
52850
52850
  getBordersColors(sheetId) {
52851
52851
  const colors = [];
@@ -52853,11 +52853,13 @@ stores.inject(MyMetaStore, storeInstance);
52853
52853
  if (sheetBorders) {
52854
52854
  for (const borders of sheetBorders.filter(isDefined)) {
52855
52855
  for (const cellBorder of borders) {
52856
- if (cellBorder?.horizontal) {
52857
- colors.push(cellBorder.horizontal.color);
52858
- }
52859
- if (cellBorder?.vertical) {
52860
- colors.push(cellBorder.vertical.color);
52856
+ if (cellBorder) {
52857
+ for (const direction of ["top", "bottom", "left", "right"]) {
52858
+ const color = cellBorder[direction]?.color;
52859
+ if (color) {
52860
+ colors.push(color);
52861
+ }
52862
+ }
52861
52863
  }
52862
52864
  }
52863
52865
  }
@@ -52910,7 +52912,7 @@ stores.inject(MyMetaStore, storeInstance);
52910
52912
  getCommonSides(border1, border2) {
52911
52913
  const commonBorder = {};
52912
52914
  for (let side of ["top", "bottom", "left", "right"]) {
52913
- if (border1[side] && border1[side] === border2[side]) {
52915
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52914
52916
  commonBorder[side] = border1[side];
52915
52917
  }
52916
52918
  }
@@ -52955,23 +52957,15 @@ stores.inject(MyMetaStore, storeInstance);
52955
52957
  * @param start starting column (included)
52956
52958
  * @param delta how much borders will be moved (negative if moved to the left)
52957
52959
  */
52958
- shiftBordersHorizontally(sheetId, start, delta, { moveFirstLeftBorder } = {}) {
52960
+ shiftBordersHorizontally(sheetId, start, delta) {
52959
52961
  const borders = this.borders[sheetId];
52960
52962
  if (!borders)
52961
52963
  return;
52962
- if (delta < 0) {
52963
- this.moveBordersOfColumn(sheetId, start, delta, "vertical", {
52964
- destructive: false,
52965
- });
52966
- }
52967
52964
  this.getColumnsWithBorders(sheetId)
52968
52965
  .filter((col) => col >= start)
52969
52966
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52970
52967
  .forEach((col) => {
52971
- if ((col === start && moveFirstLeftBorder) || col !== start) {
52972
- this.moveBordersOfColumn(sheetId, col, delta, "vertical");
52973
- }
52974
- this.moveBordersOfColumn(sheetId, col, delta, "horizontal");
52968
+ this.moveBordersOfColumn(sheetId, col, delta);
52975
52969
  });
52976
52970
  }
52977
52971
  /**
@@ -52980,12 +52974,12 @@ stores.inject(MyMetaStore, storeInstance);
52980
52974
  * @param start starting row (included)
52981
52975
  * @param delta how much borders will be moved (negative if moved to the above)
52982
52976
  */
52983
- shiftBordersVertically(sheetId, start, delta, { moveFirstTopBorder } = {}) {
52977
+ shiftBordersVertically(sheetId, start, delta) {
52984
52978
  const borders = this.borders[sheetId];
52985
52979
  if (!borders)
52986
52980
  return;
52987
52981
  if (delta < 0) {
52988
- this.moveBordersOfRow(sheetId, start, delta, "horizontal", {
52982
+ this.moveBordersOfRow(sheetId, start, delta, {
52989
52983
  destructive: false,
52990
52984
  });
52991
52985
  }
@@ -52993,10 +52987,7 @@ stores.inject(MyMetaStore, storeInstance);
52993
52987
  .filter((row) => row >= start)
52994
52988
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52995
52989
  .forEach((row) => {
52996
- if ((row === start && moveFirstTopBorder) || row !== start) {
52997
- this.moveBordersOfRow(sheetId, row, delta, "horizontal");
52998
- }
52999
- this.moveBordersOfRow(sheetId, row, delta, "vertical");
52990
+ this.moveBordersOfRow(sheetId, row, delta);
53000
52991
  });
53001
52992
  }
53002
52993
  /**
@@ -53010,15 +53001,15 @@ stores.inject(MyMetaStore, storeInstance);
53010
53001
  * argument `destructive` is given false, the target border is preserved if
53011
53002
  * the moved border is empty
53012
53003
  */
53013
- moveBordersOfRow(sheetId, row, delta, borderDirection, { destructive } = { destructive: true }) {
53004
+ moveBordersOfRow(sheetId, row, delta, { destructive } = { destructive: true }) {
53014
53005
  const borders = this.borders[sheetId];
53015
53006
  if (!borders)
53016
53007
  return;
53017
53008
  this.getColumnsWithBorders(sheetId).forEach((col) => {
53018
- const targetBorder = borders[col]?.[row + delta]?.[borderDirection];
53019
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53020
- this.history.update("borders", sheetId, col, row + delta, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53021
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53009
+ const targetBorder = borders[col]?.[row + delta];
53010
+ const movedBorder = borders[col]?.[row];
53011
+ this.history.update("borders", sheetId, col, row + delta, destructive ? movedBorder : movedBorder || targetBorder);
53012
+ this.history.update("borders", sheetId, col, row, undefined);
53022
53013
  });
53023
53014
  }
53024
53015
  /**
@@ -53032,15 +53023,17 @@ stores.inject(MyMetaStore, storeInstance);
53032
53023
  * argument `destructive` is given false, the target border is preserved if
53033
53024
  * the moved border is empty
53034
53025
  */
53035
- moveBordersOfColumn(sheetId, col, delta, borderDirection, { destructive } = { destructive: true }) {
53026
+ moveBordersOfColumn(sheetId, col, delta, { destructive } = { destructive: true }) {
53036
53027
  const borders = this.borders[sheetId];
53037
53028
  if (!borders)
53038
53029
  return;
53039
53030
  this.getRowsRange(sheetId).forEach((row) => {
53040
- const targetBorder = borders[col + delta]?.[row]?.[borderDirection];
53041
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53042
- this.history.update("borders", sheetId, col + delta, row, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53043
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53031
+ const targetBorder = borders[col + delta]?.[row];
53032
+ const movedBorder = borders[col]?.[row];
53033
+ this.history.update("borders", sheetId, col + delta, row, destructive ? movedBorder : movedBorder || targetBorder);
53034
+ if (destructive) {
53035
+ this.history.update("borders", sheetId, col, row, undefined);
53036
+ }
53044
53037
  });
53045
53038
  }
53046
53039
  /**
@@ -53048,33 +53041,69 @@ stores.inject(MyMetaStore, storeInstance);
53048
53041
  * It overrides the current border if override === true.
53049
53042
  */
53050
53043
  setBorder(sheetId, col, row, border, override = true) {
53051
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
53052
- this.history.update("borders", sheetId, col, row, "vertical", border?.left);
53044
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53045
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53046
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.left) {
53047
+ this.history.update("borders", sheetId, col, row, "left", border?.left);
53048
+ if (border?.left &&
53049
+ col > 0 &&
53050
+ !deepEquals(this.getCellBorder({ sheetId, col: col - 1, row })?.right, border?.left)) {
53051
+ this.history.update("borders", sheetId, col - 1, row, "right", undefined);
53052
+ }
53053
53053
  }
53054
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.horizontal) {
53055
- this.history.update("borders", sheetId, col, row, "horizontal", border?.top);
53054
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.top) {
53055
+ this.history.update("borders", sheetId, col, row, "top", border?.top);
53056
+ if (border?.top &&
53057
+ row > 0 &&
53058
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row - 1 })?.bottom, border?.top)) {
53059
+ this.history.update("borders", sheetId, col, row - 1, "bottom", undefined);
53060
+ }
53056
53061
  }
53057
- if (override || !this.borders?.[sheetId]?.[col + 1]?.[row]?.vertical) {
53058
- this.history.update("borders", sheetId, col + 1, row, "vertical", border?.right);
53062
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.right) {
53063
+ this.history.update("borders", sheetId, col, row, "right", border?.right);
53064
+ if (border?.right &&
53065
+ col < maxCol &&
53066
+ !deepEquals(this.getCellBorder({ sheetId, col: col + 1, row })?.left, border?.right)) {
53067
+ this.history.update("borders", sheetId, col + 1, row, "left", undefined);
53068
+ }
53059
53069
  }
53060
- if (override || !this.borders?.[sheetId]?.[col]?.[row + 1]?.horizontal) {
53061
- this.history.update("borders", sheetId, col, row + 1, "horizontal", border?.bottom);
53070
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.bottom) {
53071
+ this.history.update("borders", sheetId, col, row, "bottom", border?.bottom);
53072
+ if (border?.bottom &&
53073
+ row < maxRow &&
53074
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row + 1 })?.top, border?.bottom)) {
53075
+ this.history.update("borders", sheetId, col, row + 1, "top", undefined);
53076
+ }
53062
53077
  }
53063
53078
  }
53064
53079
  /**
53065
53080
  * Remove the borders of a zone
53066
53081
  */
53067
- clearBorders(sheetId, zones) {
53082
+ clearBorders(sheetId, zones, eraseBoundaries = false) {
53083
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53084
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53068
53085
  for (let zone of recomputeZones(zones)) {
53069
53086
  for (let row = zone.top; row <= zone.bottom; row++) {
53070
- this.history.update("borders", sheetId, zone.right + 1, row, "vertical", undefined);
53087
+ if (eraseBoundaries) {
53088
+ if (zone.left > 0) {
53089
+ this.history.update("borders", sheetId, zone.left - 1, row, "right", undefined);
53090
+ }
53091
+ if (zone.right < maxCol) {
53092
+ this.history.update("borders", sheetId, zone.right + 1, row, "left", undefined);
53093
+ }
53094
+ }
53071
53095
  for (let col = zone.left; col <= zone.right; col++) {
53072
53096
  this.history.update("borders", sheetId, col, row, undefined);
53097
+ if (eraseBoundaries) {
53098
+ if (zone.top > 0) {
53099
+ this.history.update("borders", sheetId, col, zone.top - 1, "bottom", undefined);
53100
+ }
53101
+ if (zone.bottom < maxRow) {
53102
+ this.history.update("borders", sheetId, col, zone.bottom + 1, "top", undefined);
53103
+ }
53104
+ }
53073
53105
  }
53074
53106
  }
53075
- for (let col = zone.left; col <= zone.right; col++) {
53076
- this.history.update("borders", sheetId, col, zone.bottom + 1, "horizontal", undefined);
53077
- }
53078
53107
  }
53079
53108
  }
53080
53109
  /**
@@ -53104,41 +53133,63 @@ stores.inject(MyMetaStore, storeInstance);
53104
53133
  */
53105
53134
  setBorders(sheetId, zones, position, border) {
53106
53135
  if (position === "clear") {
53107
- return this.clearBorders(sheetId, zones);
53136
+ return this.clearBorders(sheetId, zones, true);
53108
53137
  }
53109
53138
  for (let zone of recomputeZones(zones)) {
53110
- if (position === "h" || position === "hv" || position === "all") {
53111
- for (let row = zone.top + 1; row <= zone.bottom; row++) {
53139
+ if (position === "all") {
53140
+ for (let row = zone.top; row <= zone.bottom; row++) {
53112
53141
  for (let col = zone.left; col <= zone.right; col++) {
53113
- this.addBorder(sheetId, col, row, { top: border });
53142
+ this.addBorder(sheetId, col, row, {
53143
+ top: border,
53144
+ right: border,
53145
+ bottom: border,
53146
+ left: border,
53147
+ });
53148
+ }
53149
+ }
53150
+ }
53151
+ if (position === "h" || position === "hv") {
53152
+ if (zone.top === zone.bottom) {
53153
+ continue;
53154
+ }
53155
+ for (let col = zone.left; col <= zone.right; col++) {
53156
+ this.addBorder(sheetId, col, zone.top, { bottom: border });
53157
+ for (let row = zone.top + 1; row < zone.bottom; row++) {
53158
+ this.addBorder(sheetId, col, row, { top: border, bottom: border });
53114
53159
  }
53160
+ this.addBorder(sheetId, col, zone.bottom, { top: border });
53115
53161
  }
53116
53162
  }
53117
- if (position === "v" || position === "hv" || position === "all") {
53163
+ if (position === "v" || position === "hv") {
53164
+ if (zone.left === zone.right) {
53165
+ continue;
53166
+ }
53118
53167
  for (let row = zone.top; row <= zone.bottom; row++) {
53119
- for (let col = zone.left + 1; col <= zone.right; col++) {
53120
- this.addBorder(sheetId, col, row, { left: border });
53168
+ this.addBorder(sheetId, zone.left, row, { right: border });
53169
+ for (let col = zone.left + 1; col < zone.right; col++) {
53170
+ this.addBorder(sheetId, col, row, { left: border, right: border });
53121
53171
  }
53172
+ this.addBorder(sheetId, zone.right, row, { left: border });
53122
53173
  }
53123
53174
  }
53124
- if (position === "left" || position === "all" || position === "external") {
53175
+ if (position === "left" || position === "external") {
53125
53176
  for (let row = zone.top; row <= zone.bottom; row++) {
53126
53177
  this.addBorder(sheetId, zone.left, row, { left: border });
53127
53178
  }
53128
53179
  }
53129
- if (position === "right" || position === "all" || position === "external") {
53180
+ if (position === "right" || position === "external") {
53130
53181
  for (let row = zone.top; row <= zone.bottom; row++) {
53131
- this.addBorder(sheetId, zone.right + 1, row, { left: border });
53182
+ this.addBorder(sheetId, zone.right, row, { right: border });
53132
53183
  }
53133
53184
  }
53134
- if (position === "top" || position === "all" || position === "external") {
53185
+ if (position === "top" || position === "external") {
53135
53186
  for (let col = zone.left; col <= zone.right; col++) {
53136
53187
  this.addBorder(sheetId, col, zone.top, { top: border });
53137
53188
  }
53138
53189
  }
53139
- if (position === "bottom" || position === "all" || position === "external") {
53190
+ if (position === "bottom" || position === "external") {
53140
53191
  for (let col = zone.left; col <= zone.right; col++) {
53141
- this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
53192
+ this.addBorder(sheetId, col, zone.bottom, { bottom: border });
53142
53193
  }
53143
53194
  }
53144
53195
  }
@@ -54684,10 +54735,20 @@ stores.inject(MyMetaStore, storeInstance);
54684
54735
  for (const sheet of data.sheets) {
54685
54736
  sheet.dataValidationRules = [];
54686
54737
  for (const rule of this.rules[sheet.id]) {
54687
- sheet.dataValidationRules.push({
54688
- ...rule,
54738
+ const excelRule = {
54739
+ ...deepCopy(rule),
54689
54740
  ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useBoundedReference: true })),
54690
- });
54741
+ };
54742
+ if (rule.criterion.type === "isValueInRange") {
54743
+ excelRule.criterion.values = rule.criterion.values.map((value) => {
54744
+ const range = this.getters.getRangeFromSheetXC(sheet.id, value);
54745
+ return this.getters.getRangeString(range, sheet.id, {
54746
+ useBoundedReference: true,
54747
+ useFixedReference: true,
54748
+ });
54749
+ });
54750
+ }
54751
+ sheet.dataValidationRules.push(excelRule);
54691
54752
  }
54692
54753
  }
54693
54754
  }
@@ -56108,9 +56169,10 @@ stores.inject(MyMetaStore, storeInstance);
56108
56169
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
56109
56170
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
56110
56171
  * @param options
56111
- * @param options.useBoundedReference if true, the range will be returned with fixed row and column
56172
+ * @param options.useBoundedReference if true, the range will be returned with bounded row and column
56173
+ * @param options.useFixedReference if true, the range will be returned with fixed row and column
56112
56174
  */
56113
- getRangeString(range, forSheetId, options = { useBoundedReference: false }) {
56175
+ getRangeString(range, forSheetId, options = { useBoundedReference: false, useFixedReference: false }) {
56114
56176
  if (!range) {
56115
56177
  return CellErrorType.InvalidReference;
56116
56178
  }
@@ -56213,10 +56275,10 @@ stores.inject(MyMetaStore, storeInstance);
56213
56275
  /**
56214
56276
  * Get a Xc string that represent a part of a range
56215
56277
  */
56216
- getRangePartString(range, part, options = { useBoundedReference: false }) {
56217
- const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
56278
+ getRangePartString(range, part, options = { useBoundedReference: false, useFixedReference: false }) {
56279
+ const colFixed = range.parts[part]?.colFixed || options.useFixedReference ? "$" : "";
56218
56280
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
56219
- const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
56281
+ const rowFixed = range.parts[part]?.rowFixed || options.useFixedReference ? "$" : "";
56220
56282
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
56221
56283
  let str = "";
56222
56284
  if (range.isFullCol && !options.useBoundedReference) {
@@ -59872,7 +59934,11 @@ stores.inject(MyMetaStore, storeInstance);
59872
59934
  computeFormulaCell(formulaPosition, cellData) {
59873
59935
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
59874
59936
  if (!isMatrix(formulaReturn)) {
59875
- return createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59937
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59938
+ if (evaluatedCell.type === CellValueType.error) {
59939
+ evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
59940
+ }
59941
+ return evaluatedCell;
59876
59942
  }
59877
59943
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
59878
59944
  const nbColumns = formulaReturn.length;
@@ -59945,6 +60011,9 @@ stores.inject(MyMetaStore, storeInstance);
59945
60011
  const position = { sheetId, col: i + col, row: j + row };
59946
60012
  const cell = this.getters.getCell(position);
59947
60013
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
60014
+ if (evaluatedCell.type === CellValueType.error) {
60015
+ evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
60016
+ }
59948
60017
  this.evaluatedCells.set(position, evaluatedCell);
59949
60018
  };
59950
60019
  return spreadValues;
@@ -61503,7 +61572,7 @@ stores.inject(MyMetaStore, storeInstance);
61503
61572
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
61504
61573
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
61505
61574
  }
61506
- return this._getPivotCellValueAndFormat(symbolName, domain);
61575
+ return this.getPivotCellValueAndFormat(symbolName, domain);
61507
61576
  };
61508
61577
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
61509
61578
  if (isMatrix(result)) {
@@ -63402,6 +63471,7 @@ stores.inject(MyMetaStore, storeInstance);
63402
63471
  waitingUndoRedoAck = false;
63403
63472
  isReplayingInitialRevisions = false;
63404
63473
  processedRevisions = new Set();
63474
+ lastRevisionMessage = undefined;
63405
63475
  uuidGenerator = new UuidGenerator();
63406
63476
  lastLocalOperation;
63407
63477
  /**
@@ -63502,7 +63572,10 @@ stores.inject(MyMetaStore, storeInstance);
63502
63572
  * Notify the server that the user client left the collaborative session
63503
63573
  */
63504
63574
  async leave(data) {
63505
- if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
63575
+ if (data &&
63576
+ Object.keys(this.clients).length === 1 &&
63577
+ this.lastRevisionMessage &&
63578
+ this.lastRevisionMessage?.type !== "SNAPSHOT_CREATED") {
63506
63579
  await this.snapshot(data());
63507
63580
  }
63508
63581
  delete this.clients[this.clientId];
@@ -63723,6 +63796,7 @@ stores.inject(MyMetaStore, storeInstance);
63723
63796
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63724
63797
  this.serverRevisionId = message.nextRevisionId;
63725
63798
  this.processedRevisions.add(message.nextRevisionId);
63799
+ this.lastRevisionMessage = message;
63726
63800
  this.sendPendingMessage();
63727
63801
  break;
63728
63802
  }
@@ -64684,14 +64758,12 @@ stores.inject(MyMetaStore, storeInstance);
64684
64758
  }
64685
64759
  break;
64686
64760
  case "AUTORESIZE_ROWS":
64687
- for (let row of cmd.rows) {
64688
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64689
- elements: [row],
64690
- dimension: "ROW",
64691
- size: null,
64692
- sheetId: cmd.sheetId,
64693
- });
64694
- }
64761
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
64762
+ elements: cmd.rows,
64763
+ dimension: "ROW",
64764
+ size: null,
64765
+ sheetId: cmd.sheetId,
64766
+ });
64695
64767
  break;
64696
64768
  }
64697
64769
  }
@@ -67442,7 +67514,7 @@ stores.inject(MyMetaStore, storeInstance);
67442
67514
  "getSheetViewVisibleCols",
67443
67515
  "getSheetViewVisibleRows",
67444
67516
  "getFrozenSheetViewRatio",
67445
- "isPositionVisible",
67517
+ "isPixelPositionVisible",
67446
67518
  "getColDimensionsInViewport",
67447
67519
  "getRowDimensionsInViewport",
67448
67520
  "getAllActiveViewportsZones",
@@ -68066,7 +68138,7 @@ stores.inject(MyMetaStore, storeInstance);
68066
68138
  }
68067
68139
  return result;
68068
68140
  }
68069
- isPositionVisible(position) {
68141
+ isPixelPositionVisible(position) {
68070
68142
  const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
68071
68143
  const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
68072
68144
  const { width, height } = this.getters.getSheetViewDimension();
@@ -70461,12 +70533,8 @@ stores.inject(MyMetaStore, storeInstance);
70461
70533
  .o-spreadsheet {
70462
70534
  position: relative;
70463
70535
  display: grid;
70464
- color: ${TEXT_BODY};
70465
70536
  font-size: 14px;
70466
70537
 
70467
- input {
70468
- background-color: white;
70469
- }
70470
70538
  .text-muted {
70471
70539
  color: ${TEXT_BODY_MUTED} !important;
70472
70540
  }
@@ -71713,6 +71781,9 @@ stores.inject(MyMetaStore, storeInstance);
71713
71781
  observe(owner, callbacks) {
71714
71782
  this.observers.set(owner, { owner, callbacks });
71715
71783
  }
71784
+ detachObserver(owner) {
71785
+ this.observers.delete(owner);
71786
+ }
71716
71787
  /**
71717
71788
  * Capture the stream for yourself
71718
71789
  */
@@ -71805,6 +71876,9 @@ stores.inject(MyMetaStore, storeInstance);
71805
71876
  observe(owner, callbacks) {
71806
71877
  this.stream.observe(owner, callbacks);
71807
71878
  }
71879
+ detachObserver(owner) {
71880
+ this.stream.detachObserver(owner);
71881
+ }
71808
71882
  release(owner) {
71809
71883
  if (this.stream.isListening(owner)) {
71810
71884
  this.stream.release(owner);
@@ -75254,9 +75328,9 @@ stores.inject(MyMetaStore, storeInstance);
75254
75328
  exports.tokenize = tokenize;
75255
75329
 
75256
75330
 
75257
- __info__.version = "18.2.0-alpha.3";
75258
- __info__.date = "2025-01-27T10:07:28.716Z";
75259
- __info__.hash = "63a13e5";
75331
+ __info__.version = "18.2.0-alpha.5";
75332
+ __info__.date = "2025-01-31T07:59:30.667Z";
75333
+ __info__.hash = "efce841";
75260
75334
 
75261
75335
 
75262
75336
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);