@odoo/o-spreadsheet 17.3.2 → 17.3.4

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.3.2
7
- * @date 2024-06-10T09:38:56.657Z
8
- * @hash c3b358f
6
+ * @version 17.3.4
7
+ * @date 2024-06-21T20:00:08.515Z
8
+ * @hash 6efbf3b
9
9
  */
10
10
 
11
11
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -2779,7 +2779,7 @@ function evaluatePredicate(value, criterion) {
2779
2779
  return false;
2780
2780
  }
2781
2781
  if (typeof operand === "number" && operator === "=") {
2782
- return toString(value) === toString(operand);
2782
+ return value.toString() === operand.toString();
2783
2783
  }
2784
2784
  if (operator === "<>" || operator === "=") {
2785
2785
  let result;
@@ -2839,14 +2839,13 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
2839
2839
  if (countArg % 2 === 1) {
2840
2840
  throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects criteria_range and criterion to be in pairs."));
2841
2841
  }
2842
- const dimRow = args[0].length;
2843
- const dimCol = args[0][0].length;
2842
+ const firstArg = toMatrix(args[0]);
2843
+ const dimRow = firstArg.length;
2844
+ const dimCol = firstArg[0].length;
2844
2845
  let predicates = [];
2845
2846
  for (let i = 0; i < countArg - 1; i += 2) {
2846
- const criteriaRange = args[i];
2847
- if (!isMatrix(criteriaRange) ||
2848
- criteriaRange.length !== dimRow ||
2849
- criteriaRange[0].length !== dimCol) {
2847
+ const criteriaRange = toMatrix(args[i]);
2848
+ if (criteriaRange.length !== dimRow || criteriaRange[0].length !== dimCol) {
2850
2849
  throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects criteria_range to have the same dimension"));
2851
2850
  }
2852
2851
  const description = toString(args[i + 1]);
@@ -2860,7 +2859,7 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
2860
2859
  for (let j = 0; j < dimCol; j++) {
2861
2860
  let validatedPredicates = true;
2862
2861
  for (let k = 0; k < countArg - 1; k += 2) {
2863
- const criteriaValue = args[k][i][j].value;
2862
+ const criteriaValue = toMatrix(args[k])[i][j].value;
2864
2863
  const criterion = predicates[k / 2];
2865
2864
  validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion);
2866
2865
  if (!validatedPredicates) {
@@ -9460,10 +9459,8 @@ class ComposerStore extends SpreadsheetStore {
9460
9459
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
9461
9460
  return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
9462
9461
  });
9463
- // this function assumes that the previous range is always found because
9464
- // it's called when changing a highlight, which exists by definition
9465
9462
  if (!previousRefToken) {
9466
- throw new Error("Previous range not found");
9463
+ return;
9467
9464
  }
9468
9465
  const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
9469
9466
  this.selectionStart = previousRefToken.start;
@@ -10988,6 +10985,9 @@ function makeArg(str, description) {
10988
10985
  if (types.some((t) => t.startsWith("RANGE"))) {
10989
10986
  result.acceptMatrix = true;
10990
10987
  }
10988
+ if (types.every((t) => t.startsWith("RANGE"))) {
10989
+ result.acceptMatrixOnly = true;
10990
+ }
10991
10991
  return result;
10992
10992
  }
10993
10993
  /**
@@ -11269,11 +11269,16 @@ const CHOOSECOLS = {
11269
11269
  compute: function (array, ...columns) {
11270
11270
  const _array = toMatrix(array);
11271
11271
  const _columns = flattenRowFirst(columns, (item) => toInteger(item?.value, this.locale));
11272
- assert(() => _columns.every((col) => col > 0 && col <= _array.length), _t("The columns arguments must be between 1 and %s (got %s).", _array.length.toString(), (_columns.find((col) => col <= 0 || col > _array.length) || 0).toString()));
11272
+ const argOutOfRange = _columns.filter((col) => col === 0 || _array.length < Math.abs(col));
11273
+ assert(() => argOutOfRange.length === 0, _t("The columns arguments must be between -%s and %s (got %s), excluding 0.", _array.length.toString(), _array.length.toString(), argOutOfRange.join(",")));
11273
11274
  const result = Array(_columns.length);
11274
11275
  for (let col = 0; col < _columns.length; col++) {
11275
- const colIndex = _columns[col] - 1; // -1 because columns arguments are 1-indexed
11276
- result[col] = _array[colIndex];
11276
+ if (_columns[col] > 0) {
11277
+ result[col] = _array[_columns[col] - 1]; // -1 because columns arguments are 1-indexed
11278
+ }
11279
+ else {
11280
+ result[col] = _array[_array.length + _columns[col]];
11281
+ }
11277
11282
  }
11278
11283
  return result;
11279
11284
  },
@@ -11294,8 +11299,14 @@ const CHOOSEROWS = {
11294
11299
  const _array = toMatrix(array);
11295
11300
  const _rows = flattenRowFirst(rows, (item) => toInteger(item?.value, this.locale));
11296
11301
  const _nbColumns = _array.length;
11297
- assert(() => _rows.every((row) => row > 0 && row <= _array[0].length), _t("The rows arguments must be between 1 and %s (got %s).", _array[0].length.toString(), (_rows.find((row) => row <= 0 || row > _array[0].length) || 0).toString()));
11298
- return generateMatrix(_nbColumns, _rows.length, (col, row) => _array[col][_rows[row] - 1]); // -1 because rows arguments are 1-indexed
11302
+ const argOutOfRange = _rows.filter((row) => row === 0 || _array[0].length < Math.abs(row));
11303
+ assert(() => argOutOfRange.length === 0, _t("The rows arguments must be between -%s and %s (got %s), excluding 0.", _array[0].length.toString(), _array[0].length.toString(), argOutOfRange.join(",")));
11304
+ return generateMatrix(_nbColumns, _rows.length, (col, row) => {
11305
+ if (_rows[row] > 0) {
11306
+ return _array[col][_rows[row] - 1]; // -1 because columns arguments are 1-indexed
11307
+ }
11308
+ return _array[col][_array[col].length + _rows[row]];
11309
+ });
11299
11310
  },
11300
11311
  isExported: true,
11301
11312
  };
@@ -19762,6 +19773,9 @@ function addInputHandling(descr) {
19762
19773
  }
19763
19774
  args[i] = arg[0][0];
19764
19775
  }
19776
+ if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
19777
+ throw new BadExpressionError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be reference to a cell or range.", (i + 1).toString()));
19778
+ }
19765
19779
  }
19766
19780
  return descr.compute.apply(this, args);
19767
19781
  }
@@ -21356,12 +21370,6 @@ function compileTokens(tokens) {
21356
21370
  // detect when an argument need to be evaluated as a meta argument
21357
21371
  const isMeta = argTypes.includes("META");
21358
21372
  const hasRange = argTypes.some((t) => isRangeType(t));
21359
- const isRangeOnly = argTypes.every((t) => isRangeType(t));
21360
- if (isRangeOnly) {
21361
- if (!isRangeInput(currentArg)) {
21362
- throw new BadExpressionError(_t("Function %(function_name)s expects the parameter %(arg_index)s to be a reference to a cell or a range.", { function_name: functionName, arg_index: i + 1 }));
21363
- }
21364
- }
21365
21373
  compiledArgs.push(compileAST(currentArg, isMeta, hasRange));
21366
21374
  }
21367
21375
  return compiledArgs;
@@ -21526,16 +21534,6 @@ function assertEnoughArgs(ast) {
21526
21534
  function isRangeType(type) {
21527
21535
  return type.startsWith("RANGE");
21528
21536
  }
21529
- function isRangeInput(arg) {
21530
- if (arg.type === "REFERENCE") {
21531
- return true;
21532
- }
21533
- if (arg.type === "FUNCALL") {
21534
- const fnDef = functions$1[arg.value.toUpperCase()];
21535
- return fnDef && isRangeType(fnDef.returns[0]);
21536
- }
21537
- return false;
21538
- }
21539
21537
 
21540
21538
  const functions = functionRegistry.content;
21541
21539
  function isExportableToExcel(tokens) {
@@ -21964,7 +21962,9 @@ autoCompleteProviders.add("pivot_group_values", {
21964
21962
  if (!groupByField) {
21965
21963
  return;
21966
21964
  }
21967
- return dataSource.getPossibleFieldValues(groupByField.split(":")[0]).map(({ value, label }) => {
21965
+ return dataSource
21966
+ .getPossibleFieldValues(groupByField.toString().split(":")[0])
21967
+ .map(({ value, label }) => {
21968
21968
  const isString = typeof value === "string";
21969
21969
  const text = isString ? `"${value}"` : value.toString();
21970
21970
  const color = isString ? tokenColors.STRING : tokenColors.NUMBER;
@@ -25450,7 +25450,7 @@ class Popover extends Component {
25450
25450
  if (!anchor)
25451
25451
  return;
25452
25452
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
25453
- const elDims = {
25453
+ let elDims = {
25454
25454
  width: el.getBoundingClientRect().width,
25455
25455
  height: el.getBoundingClientRect().height,
25456
25456
  };
@@ -25458,7 +25458,14 @@ class Popover extends Component {
25458
25458
  const popoverPositionHelper = this.props.positioning === "BottomLeft"
25459
25459
  ? new BottomLeftPopoverContext(anchor, this.containerRect, propsMaxSize, spreadsheetRect)
25460
25460
  : new TopRightPopoverContext(anchor, this.containerRect, propsMaxSize, spreadsheetRect);
25461
- const style = popoverPositionHelper.getCss(elDims, this.props.verticalOffset);
25461
+ el.style["max-height"] = popoverPositionHelper.getMaxHeight(elDims.height) + "px";
25462
+ el.style["max-width"] = popoverPositionHelper.getMaxWidth(elDims.width) + "px";
25463
+ // Re-compute the dimensions after setting the max-width and max-height
25464
+ elDims = {
25465
+ width: el.getBoundingClientRect().width,
25466
+ height: el.getBoundingClientRect().height,
25467
+ };
25468
+ let style = popoverPositionHelper.getCss(elDims, this.props.verticalOffset);
25462
25469
  for (const property of Object.keys(style)) {
25463
25470
  el.style[property] = style[property];
25464
25471
  }
@@ -25521,8 +25528,6 @@ class PopoverPositionContext {
25521
25528
  const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
25522
25529
  verticalOffset = shouldRenderAtBottom ? verticalOffset : -verticalOffset;
25523
25530
  const cssProperties = {
25524
- "max-height": maxHeight + "px",
25525
- "max-width": maxWidth + "px",
25526
25531
  top: this.getTopCoordinate(actualHeight, shouldRenderAtBottom) -
25527
25532
  this.spreadsheetOffset.y -
25528
25533
  verticalOffset +
@@ -31336,10 +31341,8 @@ class ChartTitle extends Component {
31336
31341
 
31337
31342
  class AxisDesignEditor extends Component {
31338
31343
  static template = "o-spreadsheet-AxisDesignEditor";
31339
- static components = {
31340
- Section,
31341
- ChartTitle,
31342
- };
31344
+ static components = { Section, ChartTitle };
31345
+ static props = { figureId: String, definition: Object, updateChart: Function, axesList: Array };
31343
31346
  state = useState({ currentAxis: "x" });
31344
31347
  get axisTitleStyle() {
31345
31348
  const axisDesign = this.props.definition.axesDesign?.[this.state.currentAxis] ?? {};
@@ -31490,6 +31493,12 @@ class ChartWithAxisDesignPanel extends Component {
31490
31493
  AxisDesignEditor,
31491
31494
  RoundColorPicker,
31492
31495
  };
31496
+ static props = {
31497
+ figureId: String,
31498
+ definition: Object,
31499
+ canUpdateChart: Function,
31500
+ updateChart: Function,
31501
+ };
31493
31502
  state = useState({ index: 0 });
31494
31503
  get axesList() {
31495
31504
  const { useLeftAxis, useRightAxis } = getDefinedAxis(this.props.definition);
@@ -31671,14 +31680,6 @@ class GaugeChartDesignPanel extends Component {
31671
31680
  const cancelledReasons = [...(this.state.sectionRuleDispatchResult?.reasons || [])];
31672
31681
  return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
31673
31682
  }
31674
- updateBackgroundColor(color) {
31675
- this.props.updateChart(this.props.figureId, {
31676
- background: color,
31677
- });
31678
- }
31679
- updateTitle(content) {
31680
- this.props.updateChart(this.props.figureId, { title: { text: content } });
31681
- }
31682
31683
  isRangeMinInvalid() {
31683
31684
  return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */) ||
31684
31685
  this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */) ||
@@ -31718,9 +31719,6 @@ class GaugeChartDesignPanel extends Component {
31718
31719
  sectionRule,
31719
31720
  });
31720
31721
  }
31721
- get backgroundColorTitle() {
31722
- return ChartTerms.BackgroundColor;
31723
- }
31724
31722
  }
31725
31723
 
31726
31724
  class LineConfigPanel extends GenericChartConfigPanel {
@@ -31903,9 +31901,6 @@ class ScorecardChartDesignPanel extends Component {
31903
31901
  get humanizeNumbersLabel() {
31904
31902
  return _t("Humanize numbers");
31905
31903
  }
31906
- updateTitle(content) {
31907
- this.props.updateChart(this.props.figureId, { title: { text: content } });
31908
- }
31909
31904
  updateHumanizeNumbers(humanize) {
31910
31905
  this.props.updateChart(this.props.figureId, { humanize });
31911
31906
  }
@@ -31928,9 +31923,6 @@ class ScorecardChartDesignPanel extends Component {
31928
31923
  break;
31929
31924
  }
31930
31925
  }
31931
- get backgroundColorTitle() {
31932
- return ChartTerms.BackgroundColor;
31933
- }
31934
31926
  }
31935
31927
 
31936
31928
  class WaterfallChartDesignPanel extends Component {
@@ -33431,13 +33423,17 @@ class SelectMenu extends Component {
33431
33423
  class: { type: String, optional: true },
33432
33424
  };
33433
33425
  static components = { Menu };
33426
+ menuId = new UuidGenerator().uuidv4();
33434
33427
  selectRef = useRef("select");
33435
33428
  selectRect = useAbsoluteBoundingRect(this.selectRef);
33436
33429
  state = useState({
33437
33430
  isMenuOpen: false,
33438
33431
  });
33439
- onClick() {
33440
- this.state.isMenuOpen = true;
33432
+ onClick(ev) {
33433
+ if (ev.closedMenuId === this.menuId) {
33434
+ return;
33435
+ }
33436
+ this.state.isMenuOpen = !this.state.isMenuOpen;
33441
33437
  }
33442
33438
  onMenuClosed() {
33443
33439
  this.state.isMenuOpen = false;
@@ -33445,7 +33441,7 @@ class SelectMenu extends Component {
33445
33441
  get menuPosition() {
33446
33442
  return {
33447
33443
  x: this.selectRect.x,
33448
- y: this.selectRect.y,
33444
+ y: this.selectRect.y + this.selectRect.height,
33449
33445
  };
33450
33446
  }
33451
33447
  }
@@ -34385,9 +34381,9 @@ class FindAndReplacePanel extends Component {
34385
34381
  static props = {
34386
34382
  onCloseSidePanel: Function,
34387
34383
  };
34388
- dataRange = "";
34389
34384
  searchInput = useRef("searchInput");
34390
34385
  store;
34386
+ state;
34391
34387
  get hasSearchResult() {
34392
34388
  return this.store.selectedMatchIndex !== null;
34393
34389
  }
@@ -34417,6 +34413,7 @@ class FindAndReplacePanel extends Component {
34417
34413
  }
34418
34414
  setup() {
34419
34415
  this.store = useLocalStore(FindAndReplaceStore);
34416
+ this.state = useState({ dataRange: "" });
34420
34417
  onMounted(() => this.searchInput.el?.focus());
34421
34418
  }
34422
34419
  onFocusSearch() {
@@ -34453,13 +34450,13 @@ class FindAndReplacePanel extends Component {
34453
34450
  this.store.updateSearchOptions({ searchScope });
34454
34451
  }
34455
34452
  onSearchRangeChanged(ranges) {
34456
- this.dataRange = ranges[0];
34453
+ this.state.dataRange = ranges[0];
34457
34454
  }
34458
34455
  updateDataRange() {
34459
- if (!this.dataRange || this.searchOptions.searchScope !== "specificRange") {
34456
+ if (!this.state.dataRange || this.searchOptions.searchScope !== "specificRange") {
34460
34457
  return;
34461
34458
  }
34462
- const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.dataRange);
34459
+ const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.state.dataRange);
34463
34460
  this.store.updateSearchOptions({ specificRange });
34464
34461
  }
34465
34462
  }
@@ -34925,7 +34922,7 @@ function createMeasure(fields, measure) {
34925
34922
  function createPivotDimension(fields, dimension) {
34926
34923
  const field = fields[dimension.name];
34927
34924
  const type = field?.type ?? "integer";
34928
- const granularity = field && isDateField(field) ? dimension.granularity ?? "month_number" : undefined;
34925
+ const granularity = field && isDateField(field) ? dimension.granularity : undefined;
34929
34926
  return {
34930
34927
  /**
34931
34928
  * Get the display name of the dimension
@@ -35328,10 +35325,13 @@ function compareDimensionValues(dimension, a, b) {
35328
35325
  }
35329
35326
 
35330
35327
  function createDate(dimension, value, locale) {
35331
- const granularity = dimension.granularity || "month_number";
35332
- if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
35328
+ const granularity = dimension.granularity;
35329
+ if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
35333
35330
  throw new Error(`Unknown date granularity: ${granularity}`);
35334
35331
  }
35332
+ if (value === null) {
35333
+ return null;
35334
+ }
35335
35335
  if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
35336
35336
  MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
35337
35337
  const date = toJsDate(value, locale);
@@ -35558,7 +35558,8 @@ class SpreadsheetPivot {
35558
35558
  if (!finalCell) {
35559
35559
  return { value: "" };
35560
35560
  }
35561
- if (finalCell.value === null) {
35561
+ // Value can be null but stringified (e.g. an empty date, as for now every date is stringified)
35562
+ if (finalCell.value === null || finalCell.value === `${null}`) {
35562
35563
  return { value: _t("(Undefined)") };
35563
35564
  }
35564
35565
  if (dimension.type === "date") {
@@ -35594,10 +35595,15 @@ class SpreadsheetPivot {
35594
35595
  if (!operator) {
35595
35596
  throw new Error(`Aggregator ${aggregator} does not exist`);
35596
35597
  }
35597
- return {
35598
- value: values.length ? operator.fn([values], this.getters.getLocale()) : "",
35599
- format: operator.format(values[0]),
35600
- };
35598
+ try {
35599
+ return {
35600
+ value: values.length ? operator.fn([values], this.getters.getLocale()) : "",
35601
+ format: operator.format(values[0]),
35602
+ };
35603
+ }
35604
+ catch (e) {
35605
+ return handleError(e, aggregator.toUpperCase());
35606
+ }
35601
35607
  }
35602
35608
  getPossibleFieldValues(groupBy) {
35603
35609
  //TODO This method should be implemented for the autocomplete feature
@@ -36130,6 +36136,7 @@ css /* scss */ `
36130
36136
  class RemoveDuplicatesPanel extends Component {
36131
36137
  static template = "o-spreadsheet-RemoveDuplicatesPanel";
36132
36138
  static components = { ValidationMessages, Section, Checkbox };
36139
+ static props = { onCloseSidePanel: Function };
36133
36140
  state = useState({
36134
36141
  hasHeader: false,
36135
36142
  columns: {},
@@ -38554,7 +38561,7 @@ class FiguresContainer extends Component {
38554
38561
  });
38555
38562
  }
38556
38563
  getContainerRect(container) {
38557
- const { width: viewWidth, height: viewHeight } = this.env.model.getters.getMainViewportRect();
38564
+ const { width: viewWidth, height: viewHeight } = this.env.model.getters.getSheetViewDimension();
38558
38565
  const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
38559
38566
  const x = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
38560
38567
  const width = viewWidth - x;
@@ -51159,22 +51166,6 @@ class PivotCorePlugin extends CorePlugin {
51159
51166
  this.addPivotFormula(cellPosition, formulaId, pivotCell);
51160
51167
  }
51161
51168
  }
51162
- const pivotZone = {
51163
- top: position.row,
51164
- bottom: position.row + pivotCells[0].length - 1,
51165
- left: position.col,
51166
- right: position.col + pivotCells.length - 1,
51167
- };
51168
- const numberOfHeaders = table.columns.length - 1;
51169
- const cmdContent = {
51170
- sheetId: position.sheetId,
51171
- ranges: [this.getters.getRangeDataFromZone(position.sheetId, pivotZone)],
51172
- config: { ...PIVOT_TABLE_CONFIG, numberOfHeaders },
51173
- tableType: "static",
51174
- };
51175
- if (this.canDispatch("CREATE_TABLE", cmdContent).isSuccessful) {
51176
- this.dispatch("CREATE_TABLE", cmdContent);
51177
- }
51178
51169
  }
51179
51170
  resizeSheet(sheetId, { col, row }, table) {
51180
51171
  const colLimit = table.getNumberOfDataColumns() + 1; // +1 for the Top-Left
@@ -52854,7 +52845,6 @@ class Evaluator {
52854
52845
  if (!this.blockedArrayFormulas.has(position)) {
52855
52846
  this.invalidateSpreading(position);
52856
52847
  }
52857
- this.spreadingRelations.removeNode(position);
52858
52848
  const cell = this.getters.getCell(position);
52859
52849
  if (cell === undefined) {
52860
52850
  return EMPTY_CELL;
@@ -52896,6 +52886,7 @@ class Evaluator {
52896
52886
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
52897
52887
  const nbColumns = formulaReturn.length;
52898
52888
  const nbRows = formulaReturn[0].length;
52889
+ this.spreadingRelations.removeNode(formulaPosition);
52899
52890
  forEachSpreadPositionInMatrix(nbColumns, nbRows, this.updateSpreadRelation(formulaPosition));
52900
52891
  this.assertNoMergedCellsInSpreadZone(formulaPosition, formulaReturn);
52901
52892
  forEachSpreadPositionInMatrix(nbColumns, nbRows, this.checkCollision(formulaPosition));
@@ -55900,7 +55891,10 @@ class Session extends EventBus {
55900
55891
  /**
55901
55892
  * Notify the server that the user client left the collaborative session
55902
55893
  */
55903
- leave() {
55894
+ leave(data) {
55895
+ if (Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
55896
+ this.snapshot(data);
55897
+ }
55904
55898
  delete this.clients[this.clientId];
55905
55899
  this.transportService.leave(this.clientId);
55906
55900
  this.transportService.sendMessage({
@@ -55913,6 +55907,9 @@ class Session extends EventBus {
55913
55907
  * Send a snapshot of the spreadsheet to the collaboration server
55914
55908
  */
55915
55909
  snapshot(data) {
55910
+ if (this.pendingMessages.length !== 0) {
55911
+ return;
55912
+ }
55916
55913
  const snapshotId = this.uuidGenerator.uuidv4();
55917
55914
  this.transportService.sendMessage({
55918
55915
  type: "SNAPSHOT",
@@ -66400,7 +66397,7 @@ class Model extends EventBus {
66400
66397
  this.session.join(this.config.client);
66401
66398
  }
66402
66399
  leaveSession() {
66403
- this.session.leave();
66400
+ this.session.leave(this.exportData());
66404
66401
  }
66405
66402
  setupUiPlugin(Plugin) {
66406
66403
  const plugin = new Plugin(this.uiPluginConfig);
@@ -66948,6 +66945,6 @@ const constants = {
66948
66945
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, 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 };
66949
66946
 
66950
66947
 
66951
- __info__.version = "17.3.2";
66952
- __info__.date = "2024-06-10T09:38:56.657Z";
66953
- __info__.hash = "c3b358f";
66948
+ __info__.version = "17.3.4";
66949
+ __info__.date = "2024-06-21T20:00:08.515Z";
66950
+ __info__.hash = "6efbf3b";