@odoo/o-spreadsheet 18.0.7 → 18.0.9

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.0.7
6
- * @date 2024-12-05T10:41:39.380Z
7
- * @hash a2652c5
5
+ * @version 18.0.9
6
+ * @date 2025-01-14T11:33:47.429Z
7
+ * @hash 0c5220e
8
8
  */
9
9
 
10
10
  'use strict';
@@ -787,6 +787,7 @@ function removeFalsyAttributes(obj) {
787
787
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
788
788
  */
789
789
  const whiteSpaceSpecialCharacters = [
790
+ " ",
790
791
  "\t",
791
792
  "\f",
792
793
  "\v",
@@ -801,17 +802,15 @@ const whiteSpaceSpecialCharacters = [
801
802
  String.fromCharCode(parseInt("3000", 16)),
802
803
  String.fromCharCode(parseInt("feff", 16)),
803
804
  ];
804
- const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
805
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|"), "g");
806
+ const newLineRegexp = /(\r\n|\r)/g;
805
807
  /**
806
- * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
807
- * different newlines types by \n.
808
+ * Replace all different newlines characters by \n
808
809
  */
809
- function replaceSpecialSpaces(text) {
810
+ function replaceNewLines(text) {
810
811
  if (!text)
811
812
  return "";
812
- if (!whiteSpaceRegexp.test(text))
813
- return text;
814
- return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
813
+ return text.replace(newLineRegexp, NEWLINE);
815
814
  }
816
815
  /**
817
816
  * Determine if the numbers are consecutive.
@@ -3543,6 +3542,7 @@ exports.CommandResult = void 0;
3543
3542
  CommandResult["SheetIsHidden"] = "SheetIsHidden";
3544
3543
  CommandResult["InvalidTableResize"] = "InvalidTableResize";
3545
3544
  CommandResult["PivotIdNotFound"] = "PivotIdNotFound";
3545
+ CommandResult["PivotInError"] = "PivotInError";
3546
3546
  CommandResult["EmptyName"] = "EmptyName";
3547
3547
  CommandResult["ValueCellIsInvalidFormula"] = "ValueCellIsInvalidFormula";
3548
3548
  CommandResult["InvalidDefinition"] = "InvalidDefinition";
@@ -6294,7 +6294,7 @@ class BorderClipboardHandler extends AbstractCellClipboardHandler {
6294
6294
  const POSTFIX_UNARY_OPERATORS = ["%"];
6295
6295
  const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
6296
6296
  function tokenize(str, locale = DEFAULT_LOCALE) {
6297
- str = replaceSpecialSpaces(str);
6297
+ str = replaceNewLines(str);
6298
6298
  const chars = new TokenizingChars(str);
6299
6299
  const result = [];
6300
6300
  while (!chars.isOver()) {
@@ -6441,12 +6441,12 @@ function tokenizeSpace(chars) {
6441
6441
  if (length) {
6442
6442
  return { type: "SPACE", value: NEWLINE.repeat(length) };
6443
6443
  }
6444
- while (chars.current === " ") {
6445
- length++;
6446
- chars.shift();
6444
+ let spaces = "";
6445
+ while (chars.current && chars.current.match(whiteSpaceRegexp)) {
6446
+ spaces += chars.shift();
6447
6447
  }
6448
- if (length) {
6449
- return { type: "SPACE", value: " ".repeat(length) };
6448
+ if (spaces) {
6449
+ return { type: "SPACE", value: spaces };
6450
6450
  }
6451
6451
  return null;
6452
6452
  }
@@ -9389,9 +9389,9 @@ function getTrendDatasetForBarChart(config, dataset) {
9389
9389
  if (!newValues.length) {
9390
9390
  return;
9391
9391
  }
9392
- return getFullTrendingLineDataSet(dataset, config, newValues);
9392
+ return getFullTrendingLineDataSet(dataset, config, newValues, newLabels);
9393
9393
  }
9394
- function getFullTrendingLineDataSet(dataset, config, data) {
9394
+ function getFullTrendingLineDataSet(dataset, config, data, labels) {
9395
9395
  const defaultBorderColor = colorToRGBA(dataset.backgroundColor);
9396
9396
  defaultBorderColor.a = 1;
9397
9397
  const borderColor = config.color || lightenColor(rgbaToHex(defaultBorderColor), 0.5);
@@ -9400,7 +9400,7 @@ function getFullTrendingLineDataSet(dataset, config, data) {
9400
9400
  xAxisID: TREND_LINE_XAXIS_ID,
9401
9401
  yAxisID: dataset.yAxisID,
9402
9402
  label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
9403
- data,
9403
+ data: data.map((v, i) => ({ x: labels[i], y: v })),
9404
9404
  order: -1,
9405
9405
  showLine: true,
9406
9406
  pointRadius: 0,
@@ -19571,6 +19571,17 @@ const TEXT = {
19571
19571
  },
19572
19572
  isExported: true,
19573
19573
  };
19574
+ // -----------------------------------------------------------------------------
19575
+ // VALUE
19576
+ // -----------------------------------------------------------------------------
19577
+ const VALUE = {
19578
+ description: _t("Converts a string to a numeric value."),
19579
+ args: [arg("value (number)", _t("the string to be converted"))],
19580
+ compute: function (value) {
19581
+ return toNumber(value, this.locale);
19582
+ },
19583
+ isExported: true,
19584
+ };
19574
19585
 
19575
19586
  var text = /*#__PURE__*/Object.freeze({
19576
19587
  __proto__: null,
@@ -19593,7 +19604,8 @@ var text = /*#__PURE__*/Object.freeze({
19593
19604
  TEXT: TEXT,
19594
19605
  TEXTJOIN: TEXTJOIN,
19595
19606
  TRIM: TRIM,
19596
- UPPER: UPPER
19607
+ UPPER: UPPER,
19608
+ VALUE: VALUE
19597
19609
  });
19598
19610
 
19599
19611
  // -----------------------------------------------------------------------------
@@ -19857,14 +19869,11 @@ autoCompleteProviders.add("functions", {
19857
19869
  });
19858
19870
 
19859
19871
  class DOMFocusableElementStore {
19860
- mutators = ["setFocusableElement", "focus"];
19872
+ mutators = ["setFocusableElement"];
19861
19873
  focusableElement = undefined;
19862
19874
  setFocusableElement(element) {
19863
19875
  this.focusableElement = element;
19864
19876
  }
19865
- focus() {
19866
- this.focusableElement?.focus();
19867
- }
19868
19877
  }
19869
19878
 
19870
19879
  /**
@@ -20829,7 +20838,7 @@ class Composer extends owl.Component {
20829
20838
  if (document.activeElement === this.contentHelper.el &&
20830
20839
  this.props.composerStore.editionMode === "inactive" &&
20831
20840
  !this.props.isDefaultFocus) {
20832
- this.DOMFocusableElementStore.focus();
20841
+ this.DOMFocusableElementStore.focusableElement?.focus();
20833
20842
  }
20834
20843
  });
20835
20844
  owl.useEffect(() => {
@@ -24413,7 +24422,7 @@ function extractStyle(cell, data) {
24413
24422
  vertical: style.verticalAlign
24414
24423
  ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
24415
24424
  : undefined,
24416
- wrapText: style.wrapping === "wrap" || undefined,
24425
+ wrapText: style.wrapping === "wrap" || cell.content?.includes(NEWLINE) ? true : undefined,
24417
24426
  },
24418
24427
  };
24419
24428
  styles.font["strike"] = !!style?.strikethrough || undefined;
@@ -24636,7 +24645,7 @@ function convertFigure(figure, id, sheetData) {
24636
24645
  return undefined;
24637
24646
  }
24638
24647
  function isChartData(data) {
24639
- return "dataSets" in data;
24648
+ return "dataSets" in data && data.dataSets.length > 0;
24640
24649
  }
24641
24650
  function isImageData(data) {
24642
24651
  return "imageSrc" in data;
@@ -24875,9 +24884,8 @@ function convertRows(sheet, numberOfRows, headerGroups) {
24875
24884
  }
24876
24885
  return rows;
24877
24886
  }
24878
- /** Remove newlines (\n) in shared strings, We do not support them */
24879
24887
  function convertSharedStrings(xlsxSharedStrings) {
24880
- return xlsxSharedStrings.map((str) => str.replace(/\n/g, ""));
24888
+ return xlsxSharedStrings.map(replaceNewLines);
24881
24889
  }
24882
24890
  function convertCells(sheet, data, sheetDims, warningManager) {
24883
24891
  const cells = {};
@@ -25978,15 +25986,10 @@ class XlsxMiscExtractor extends XlsxBaseExtractor {
25978
25986
  getSharedStrings() {
25979
25987
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "si" }, (ssElement) => {
25980
25988
  // Shared string can either be a simple text, or a rich text (text with formatting, possibly in multiple parts)
25981
- if (ssElement.children[0].tagName === "t") {
25982
- return this.extractTextContent(ssElement) || "";
25983
- }
25984
25989
  // We don't support rich text formatting, we'll only extract the text
25985
- else {
25986
- return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25987
- return this.extractTextContent(textElement) || "";
25988
- }).join("");
25989
- }
25990
+ return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25991
+ return this.extractTextContent(textElement) || "";
25992
+ }).join("");
25990
25993
  });
25991
25994
  }
25992
25995
  }
@@ -28339,7 +28342,7 @@ function getChartDatasetValues(getters, dataSets) {
28339
28342
  // then using the classical aggregation method to sum the values.
28340
28343
  data.fill(1);
28341
28344
  }
28342
- else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), getters.getLocale()))) {
28345
+ else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28343
28346
  continue;
28344
28347
  }
28345
28348
  datasetValues.push({ data, label });
@@ -28915,12 +28918,12 @@ function getTrendDatasetForLineChart(config, dataset, axisType, locale) {
28915
28918
  }
28916
28919
  const numberOfStep = 5 * labels.length;
28917
28920
  const step = (xmax - xmin) / numberOfStep;
28918
- const newLabels = range(xmin, xmax + step / 2, step);
28919
- const newValues = interpolateData(config, filteredValues, filteredLabels, newLabels);
28920
- if (!newValues.length) {
28921
+ const trendLabels = range(xmin, xmax + step / 2, step);
28922
+ const trendValues = interpolateData(config, filteredValues, filteredLabels, trendLabels);
28923
+ if (!trendValues.length) {
28921
28924
  return;
28922
28925
  }
28923
- return getFullTrendingLineDataSet(dataset, config, newValues);
28926
+ return getFullTrendingLineDataSet(dataset, config, trendValues, trendLabels);
28924
28927
  }
28925
28928
  function createLineOrScatterChartRuntime(chart, getters) {
28926
28929
  const axisType = getChartAxisType(chart, getters);
@@ -29037,8 +29040,16 @@ function createLineOrScatterChartRuntime(chart, getters) {
29037
29040
  config.options.scales.x.type = "linear";
29038
29041
  config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale });
29039
29042
  config.options.plugins.tooltip.callbacks.label = (tooltipItem) => {
29040
- const dataSetPoint = dataSetsValues[tooltipItem.datasetIndex].data[tooltipItem.dataIndex];
29041
- let label = tooltipItem.label || labelValues.values[tooltipItem.dataIndex];
29043
+ let dataSetPoint;
29044
+ let label;
29045
+ if (tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID) {
29046
+ dataSetPoint = dataSetsValues[tooltipItem.datasetIndex].data[tooltipItem.dataIndex].y;
29047
+ label = "";
29048
+ }
29049
+ else {
29050
+ dataSetPoint = dataSetsValues[tooltipItem.datasetIndex].data[tooltipItem.dataIndex];
29051
+ label = labelValues.values[tooltipItem.dataIndex];
29052
+ }
29042
29053
  if (isNumber(label, locale)) {
29043
29054
  label = toNumber(label, locale);
29044
29055
  }
@@ -29109,16 +29120,18 @@ function createLineOrScatterChartRuntime(chart, getters) {
29109
29120
  }
29110
29121
  }
29111
29122
  if (trendDatasets.length) {
29112
- /* We add a second x axis here to draw the trend lines, with the labels length being
29113
- * set so that the second axis points match the classical x axis
29114
- */
29115
29123
  config.options.scales[TREND_LINE_XAXIS_ID] = {
29116
29124
  ...xAxis,
29117
- type: "category",
29118
- labels: range(0, maxLength).map((x) => x.toString()),
29119
- offset: false,
29120
29125
  display: false,
29121
29126
  };
29127
+ if (axisType === "category" || axisType === "time") {
29128
+ /* We add a second x axis here to draw the trend lines, with the labels length being
29129
+ * set so that the second axis points match the classical x axis
29130
+ */
29131
+ config.options.scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29132
+ config.options.scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29133
+ config.options.scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29134
+ }
29122
29135
  /* These datasets must be inserted after the original datasets to ensure the way we
29123
29136
  * distinguish the originals and trendLine datasets after
29124
29137
  */
@@ -29609,7 +29622,11 @@ function createGaugeChartRuntime(chart, getters) {
29609
29622
  colors.push(chartColors.upperColor);
29610
29623
  return {
29611
29624
  background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
29612
- title: chart.title ?? { text: "" },
29625
+ title: {
29626
+ ...chart.title,
29627
+ // chart titles are extracted from .json files and they are translated at runtime here
29628
+ text: _t(chart.title.text ?? ""),
29629
+ },
29613
29630
  minValue: {
29614
29631
  value: minValue,
29615
29632
  label: formatValue(minValue, { locale, format }),
@@ -34290,12 +34307,20 @@ function fontSizeMenuBuilder() {
34290
34307
  });
34291
34308
  }
34292
34309
  function isAutomaticFormatSelected(env) {
34293
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
34294
- return !activeCell || !activeCell.format;
34310
+ const activePosition = env.model.getters.getActivePosition();
34311
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
34312
+ if (pivotCell.type === "VALUE") {
34313
+ return !env.model.getters.getEvaluatedCell(activePosition).format;
34314
+ }
34315
+ return !env.model.getters.getCell(activePosition)?.format;
34295
34316
  }
34296
34317
  function isFormatSelected(env, format) {
34297
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
34298
- return activeCell?.format === format;
34318
+ const activePosition = env.model.getters.getActivePosition();
34319
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
34320
+ if (pivotCell.type === "VALUE") {
34321
+ return env.model.getters.getEvaluatedCell(activePosition).format === format;
34322
+ }
34323
+ return env.model.getters.getCell(activePosition)?.format === format;
34299
34324
  }
34300
34325
  function isFontSizeSelected(env, fontSize) {
34301
34326
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
@@ -39890,6 +39915,9 @@ class ConditionalFormattingEditor extends owl.Component {
39890
39915
  return [this.state.rules.dataBar.rangeValues || ""];
39891
39916
  }
39892
39917
  updateDataBarColor(color) {
39918
+ if (!isColorValid(color)) {
39919
+ return;
39920
+ }
39893
39921
  this.state.rules.dataBar.color = Number.parseInt(color.substr(1), 16);
39894
39922
  }
39895
39923
  onDataBarRangeUpdate(ranges) {
@@ -39975,6 +40003,10 @@ css /* scss */ `
39975
40003
  border: 1px solid #d8dadd;
39976
40004
  color: #374151;
39977
40005
  }
40006
+
40007
+ table {
40008
+ table-layout: fixed;
40009
+ }
39978
40010
  }
39979
40011
  `;
39980
40012
  class CustomCurrencyPanel extends owl.Component {
@@ -42210,16 +42242,21 @@ class TextInput extends owl.Component {
42210
42242
  }
42211
42243
  this.inputRef.el?.blur();
42212
42244
  }
42213
- focusInputAndSelectContent() {
42214
- const inputEl = this.inputRef.el;
42215
- if (!inputEl)
42216
- return;
42217
- // The onFocus event selects all text in the input.
42218
- // The subsequent mouseup event can deselect this text,
42219
- // so t-on-mouseup.prevent.stop is used to prevent this
42220
- // default behavior and preserve the selection.
42221
- inputEl.focus();
42222
- inputEl.select();
42245
+ onMouseDown(ev) {
42246
+ // Stop the event if the input is not focused, we handle everything in onMouseUp
42247
+ if (ev.target !== document.activeElement) {
42248
+ ev.preventDefault();
42249
+ ev.stopPropagation();
42250
+ }
42251
+ }
42252
+ onMouseUp(ev) {
42253
+ const target = ev.target;
42254
+ if (target !== document.activeElement) {
42255
+ target.focus();
42256
+ target.select();
42257
+ ev.preventDefault();
42258
+ ev.stopPropagation();
42259
+ }
42223
42260
  }
42224
42261
  }
42225
42262
 
@@ -42697,7 +42734,16 @@ class PivotTitleSection extends owl.Component {
42697
42734
  newPivotId,
42698
42735
  newSheetId,
42699
42736
  });
42700
- const text = result.isSuccessful ? _t("Pivot duplicated.") : _t("Pivot duplication failed");
42737
+ let text;
42738
+ if (result.isSuccessful) {
42739
+ text = _t("Pivot duplicated.");
42740
+ }
42741
+ else if (result.isCancelledBecause("PivotInError" /* CommandResult.PivotInError */)) {
42742
+ text = _t("Cannot duplicate a pivot in error.");
42743
+ }
42744
+ else {
42745
+ text = _t("Pivot duplication failed.");
42746
+ }
42701
42747
  const type = result.isSuccessful ? "success" : "danger";
42702
42748
  this.env.notifyUser({
42703
42749
  text,
@@ -43995,7 +44041,9 @@ class PivotSidePanelStore extends SpreadsheetStore {
43995
44041
  pivot: this.draft,
43996
44042
  });
43997
44043
  this.draft = null;
43998
- if (!this.alreadyNotified && !this.isDynamicPivotInViewport()) {
44044
+ if (!this.alreadyNotified &&
44045
+ !this.isDynamicPivotInViewport() &&
44046
+ this.isStaticPivotInViewport()) {
43999
44047
  const formulaId = this.getters.getPivotFormulaId(this.pivotId);
44000
44048
  const pivotExample = `=PIVOT(${formulaId})`;
44001
44049
  this.alreadyNotified = true;
@@ -44062,6 +44110,18 @@ class PivotSidePanelStore extends SpreadsheetStore {
44062
44110
  }
44063
44111
  return false;
44064
44112
  }
44113
+ isStaticPivotInViewport() {
44114
+ for (const position of this.getters.getVisibleCellPositions()) {
44115
+ const cell = this.getters.getCell(position);
44116
+ if (cell?.isFormula) {
44117
+ const pivotFunction = getFirstPivotFunction(cell.compiledFormula.tokens);
44118
+ if (pivotFunction && pivotFunction.functionName !== "PIVOT") {
44119
+ return true;
44120
+ }
44121
+ }
44122
+ }
44123
+ return false;
44124
+ }
44065
44125
  addDefaultDateTimeGranularity(fields, definition) {
44066
44126
  const { columns, rows } = definition;
44067
44127
  const columnsWithGranularity = deepCopy(columns);
@@ -45194,6 +45254,7 @@ css /* scss */ `
45194
45254
  }
45195
45255
  }
45196
45256
  `;
45257
+ const DEFAULT_TABLE_STYLE_COLOR = "#3C78D8";
45197
45258
  class TableStyleEditorPanel extends owl.Component {
45198
45259
  static template = "o-spreadsheet-TableStyleEditorPanel";
45199
45260
  static components = { Section, RoundColorPicker, TableStylePreview };
@@ -45212,7 +45273,7 @@ class TableStyleEditorPanel extends owl.Component {
45212
45273
  : null;
45213
45274
  return {
45214
45275
  pickerOpened: false,
45215
- primaryColor: editedStyle?.primaryColor || "#3C78D8",
45276
+ primaryColor: editedStyle?.primaryColor || DEFAULT_TABLE_STYLE_COLOR,
45216
45277
  selectedTemplateName: editedStyle?.templateName || "lightColoredText",
45217
45278
  styleName: editedStyle?.displayName || this.env.model.getters.getNewCustomTableStyleName(),
45218
45279
  };
@@ -45221,7 +45282,7 @@ class TableStyleEditorPanel extends owl.Component {
45221
45282
  this.state.pickerOpened = !this.state.pickerOpened;
45222
45283
  }
45223
45284
  onColorPicked(color) {
45224
- this.state.primaryColor = color;
45285
+ this.state.primaryColor = isColorValid(color) ? color : DEFAULT_TABLE_STYLE_COLOR;
45225
45286
  this.state.pickerOpened = false;
45226
45287
  }
45227
45288
  onTemplatePicked(templateName) {
@@ -46838,6 +46899,7 @@ class FiguresContainer extends owl.Component {
46838
46899
  draggedFigure: undefined,
46839
46900
  horizontalSnap: undefined,
46840
46901
  verticalSnap: undefined,
46902
+ cancelDnd: undefined,
46841
46903
  });
46842
46904
  setup() {
46843
46905
  owl.onMounted(() => {
@@ -46850,12 +46912,28 @@ class FiguresContainer extends owl.Component {
46850
46912
  // new rendering
46851
46913
  this.render();
46852
46914
  });
46915
+ owl.onWillUpdateProps(() => {
46916
+ const sheetId = this.env.model.getters.getActiveSheetId();
46917
+ const draggedFigureId = this.dnd.draggedFigure?.id;
46918
+ if (draggedFigureId && !this.env.model.getters.getFigure(sheetId, draggedFigureId)) {
46919
+ if (this.dnd.cancelDnd) {
46920
+ this.dnd.cancelDnd();
46921
+ }
46922
+ this.dnd.draggedFigure = undefined;
46923
+ this.dnd.horizontalSnap = undefined;
46924
+ this.dnd.verticalSnap = undefined;
46925
+ this.dnd.cancelDnd = undefined;
46926
+ }
46927
+ });
46853
46928
  }
46854
46929
  getVisibleFigures() {
46855
46930
  const visibleFigures = this.env.model.getters.getVisibleFigures();
46856
46931
  if (this.dnd.draggedFigure &&
46857
46932
  !visibleFigures.some((figure) => figure.id === this.dnd.draggedFigure?.id)) {
46858
- visibleFigures.push(this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.draggedFigure?.id));
46933
+ const draggedFigure = this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.draggedFigure?.id);
46934
+ if (draggedFigure) {
46935
+ visibleFigures.push(draggedFigure);
46936
+ }
46859
46937
  }
46860
46938
  return visibleFigures;
46861
46939
  }
@@ -46974,7 +47052,7 @@ class FiguresContainer extends owl.Component {
46974
47052
  this.dnd.verticalSnap = undefined;
46975
47053
  this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y });
46976
47054
  };
46977
- startDnd(onMouseMove, onMouseUp);
47055
+ this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
46978
47056
  }
46979
47057
  /**
46980
47058
  * Initialize the resize of a figure with mouse movements
@@ -47022,7 +47100,7 @@ class FiguresContainer extends owl.Component {
47022
47100
  this.dnd.horizontalSnap = undefined;
47023
47101
  this.dnd.verticalSnap = undefined;
47024
47102
  };
47025
- startDnd(onMouseMove, onMouseUp);
47103
+ this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
47026
47104
  }
47027
47105
  getOtherFigures(figId) {
47028
47106
  return this.getVisibleFigures().filter((f) => f.id !== figId);
@@ -49430,7 +49508,7 @@ class Grid extends owl.Component {
49430
49508
  this.cellPopovers = useStore(CellPopoverStore);
49431
49509
  owl.useEffect(() => {
49432
49510
  if (!this.sidePanel.isOpen) {
49433
- this.DOMFocusableElementStore.focus();
49511
+ this.DOMFocusableElementStore.focusableElement?.focus();
49434
49512
  }
49435
49513
  }, () => [this.sidePanel.isOpen]);
49436
49514
  }
@@ -49634,7 +49712,7 @@ class Grid extends owl.Component {
49634
49712
  focusDefaultElement() {
49635
49713
  if (!this.env.model.getters.getSelectedFigureId() &&
49636
49714
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
49637
- this.DOMFocusableElementStore.focus();
49715
+ this.DOMFocusableElementStore.focusableElement?.focus();
49638
49716
  }
49639
49717
  }
49640
49718
  get gridEl() {
@@ -50198,10 +50276,34 @@ class BordersPlugin extends CorePlugin {
50198
50276
  const elements = [...cmd.elements].sort((a, b) => b - a);
50199
50277
  for (const group of groupConsecutive(elements)) {
50200
50278
  if (cmd.dimension === "COL") {
50201
- this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
50279
+ if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
50280
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
50281
+ this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
50282
+ }
50283
+ }
50284
+ if (group[group.length - 1] === 0) {
50285
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
50286
+ this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
50287
+ }
50288
+ }
50289
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
50290
+ this.clearInsideBorders(cmd.sheetId, [zone]);
50291
+ this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
50202
50292
  }
50203
50293
  else {
50204
- this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
50294
+ if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
50295
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
50296
+ this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
50297
+ }
50298
+ }
50299
+ if (group[group.length - 1] === 0) {
50300
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
50301
+ this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
50302
+ }
50303
+ }
50304
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
50305
+ this.clearInsideBorders(cmd.sheetId, [zone]);
50306
+ this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
50205
50307
  }
50206
50308
  }
50207
50309
  break;
@@ -50508,6 +50610,18 @@ class BordersPlugin extends CorePlugin {
50508
50610
  }
50509
50611
  }
50510
50612
  }
50613
+ /**
50614
+ * Remove the borders inside of a zone
50615
+ */
50616
+ clearInsideBorders(sheetId, zones) {
50617
+ for (let zone of zones) {
50618
+ for (let row = zone.top; row <= zone.bottom; row++) {
50619
+ for (let col = zone.left; col <= zone.right; col++) {
50620
+ this.history.update("borders", sheetId, col, row, undefined);
50621
+ }
50622
+ }
50623
+ }
50624
+ }
50511
50625
  /**
50512
50626
  * Add a border to the existing one to a cell
50513
50627
  */
@@ -51175,7 +51289,7 @@ class CellPlugin extends CorePlugin {
51175
51289
  const before = this.getters.getCell({ sheetId, col, row });
51176
51290
  const hasContent = "content" in after || "formula" in after;
51177
51291
  // Compute the new cell properties
51178
- const afterContent = hasContent ? replaceSpecialSpaces(after?.content) : before?.content || "";
51292
+ const afterContent = hasContent ? replaceNewLines(after?.content) : before?.content || "";
51179
51293
  let style;
51180
51294
  if (after.style !== undefined) {
51181
51295
  style = after.style || undefined;
@@ -54087,6 +54201,9 @@ class SheetPlugin extends CorePlugin {
54087
54201
  };
54088
54202
  }
54089
54203
  getUnboundedZone(sheetId, zone) {
54204
+ if (zone.bottom === undefined || zone.right === undefined) {
54205
+ return zone;
54206
+ }
54090
54207
  const isFullRow = zone.left === 0 && zone.right === this.getNumberCols(sheetId) - 1;
54091
54208
  const isFullCol = zone.top === 0 && zone.bottom === this.getNumberRows(sheetId) - 1;
54092
54209
  return {
@@ -61132,6 +61249,9 @@ class Session extends EventBus {
61132
61249
  }
61133
61250
  }
61134
61251
  this.acknowledge(message);
61252
+ if (message.type === "REMOTE_REVISION" && message.clientId === this.clientId) {
61253
+ return;
61254
+ }
61135
61255
  this.trigger("collaborative-event-received");
61136
61256
  }
61137
61257
  onClientMoved(message) {
@@ -61575,7 +61695,7 @@ class FormatPlugin extends UIPlugin {
61575
61695
  for (let row = zone.top; row <= zone.bottom; row++) {
61576
61696
  const position = { sheetId, col, row };
61577
61697
  const pivotCell = this.getters.getPivotCellFromPosition(position);
61578
- if (pivotCell.type === "VALUE") {
61698
+ if (this.isSpilledPivotValueFormula(position, pivotCell)) {
61579
61699
  measurePositions.push(position);
61580
61700
  const pivotId = this.getters.getPivotIdFromPosition(position) || "";
61581
61701
  measuresByPivotId[pivotId] ??= new Set();
@@ -61612,6 +61732,10 @@ class FormatPlugin extends UIPlugin {
61612
61732
  format,
61613
61733
  });
61614
61734
  }
61735
+ isSpilledPivotValueFormula(position, pivotCell) {
61736
+ const cell = this.getters.getCell(position);
61737
+ return pivotCell.type === "VALUE" && !cell?.isFormula;
61738
+ }
61615
61739
  /**
61616
61740
  * This function allows to adjust the quantity of decimal places after a decimal
61617
61741
  * point on cells containing number value. It does this by changing the cells
@@ -61742,6 +61866,19 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
61742
61866
 
61743
61867
  class InsertPivotPlugin extends UIPlugin {
61744
61868
  static getters = [];
61869
+ allowDispatch(cmd) {
61870
+ switch (cmd.type) {
61871
+ case "DUPLICATE_PIVOT_IN_NEW_SHEET":
61872
+ if (!this.getters.isExistingPivot(cmd.pivotId)) {
61873
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
61874
+ }
61875
+ if (!this.getters.getPivot(cmd.pivotId).isValid()) {
61876
+ return "PivotInError" /* CommandResult.PivotInError */;
61877
+ }
61878
+ break;
61879
+ }
61880
+ return "Success" /* CommandResult.Success */;
61881
+ }
61745
61882
  handle(cmd) {
61746
61883
  switch (cmd.type) {
61747
61884
  case "INSERT_NEW_PIVOT":
@@ -65000,6 +65137,7 @@ class SheetViewPlugin extends UIPlugin {
65000
65137
  break;
65001
65138
  case "DELETE_SHEET":
65002
65139
  this.cleanViewports();
65140
+ this.sheetsWithDirtyViewports.delete(cmd.sheetId);
65003
65141
  break;
65004
65142
  case "ACTIVATE_SHEET":
65005
65143
  this.sheetsWithDirtyViewports.add(cmd.sheetIdTo);
@@ -65969,11 +66107,6 @@ class BottomBarSheet extends owl.Component {
65969
66107
  editionState = "initializing";
65970
66108
  DOMFocusableElementStore;
65971
66109
  setup() {
65972
- owl.onMounted(() => {
65973
- if (this.isSheetActive) {
65974
- this.scrollToSheet();
65975
- }
65976
- });
65977
66110
  owl.onPatched(() => {
65978
66111
  if (this.sheetNameRef.el && this.state.isEditing && this.editionState === "initializing") {
65979
66112
  this.editionState = "editing";
@@ -65982,6 +66115,11 @@ class BottomBarSheet extends owl.Component {
65982
66115
  });
65983
66116
  this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
65984
66117
  owl.useExternalListener(window, "click", () => (this.state.pickerOpened = false));
66118
+ owl.useEffect((sheetId) => {
66119
+ if (this.props.sheetId === sheetId) {
66120
+ this.scrollToSheet();
66121
+ }
66122
+ }, () => [this.env.model.getters.getActiveSheetId()]);
65985
66123
  }
65986
66124
  focusInputAndSelectContent() {
65987
66125
  if (!this.state.isEditing || !this.sheetNameRef.el)
@@ -65993,7 +66131,10 @@ class BottomBarSheet extends owl.Component {
65993
66131
  }
65994
66132
  }
65995
66133
  scrollToSheet() {
65996
- this.sheetDivRef.el?.scrollIntoView?.();
66134
+ this.sheetDivRef.el?.scrollIntoView?.({
66135
+ behavior: "smooth",
66136
+ inline: "nearest",
66137
+ });
65997
66138
  }
65998
66139
  onFocusOut() {
65999
66140
  if (this.state.isEditing && this.editionState !== "initializing") {
@@ -66023,11 +66164,11 @@ class BottomBarSheet extends owl.Component {
66023
66164
  if (ev.key === "Enter") {
66024
66165
  ev.preventDefault();
66025
66166
  this.stopEdition();
66026
- this.DOMFocusableElementStore.focus();
66167
+ this.DOMFocusableElementStore.focusableElement?.focus();
66027
66168
  }
66028
66169
  if (ev.key === "Escape") {
66029
66170
  this.cancelEdition();
66030
- this.DOMFocusableElementStore.focus();
66171
+ this.DOMFocusableElementStore.focusableElement?.focus();
66031
66172
  }
66032
66173
  }
66033
66174
  onMouseEventSheetName(ev) {
@@ -67398,7 +67539,6 @@ css /* scss */ `
67398
67539
  height: fit-content;
67399
67540
  margin-top: -1px;
67400
67541
  border: 1px solid;
67401
- z-index: ${ComponentsImportance.TopBarComposer};
67402
67542
  font-family: ${DEFAULT_FONT};
67403
67543
 
67404
67544
  .o-composer:empty:not(:focus):not(.active)::before {
@@ -67456,6 +67596,7 @@ class TopBarComposer extends owl.Component {
67456
67596
  }
67457
67597
  return cssPropertiesToCss({
67458
67598
  "border-color": SELECTION_BORDER_COLOR,
67599
+ "z-index": String(ComponentsImportance.TopBarComposer),
67459
67600
  });
67460
67601
  }
67461
67602
  onFocus(selection) {
@@ -71651,7 +71792,12 @@ function createSharedStrings(strings) {
71651
71792
  ["count", strings.length],
71652
71793
  ["uniqueCount", strings.length],
71653
71794
  ];
71654
- const stringNodes = strings.map((string) => escapeXml /*xml*/ `<si><t>${string}</t></si>`);
71795
+ const stringNodes = strings.map((string) => {
71796
+ if (string.trim() !== string) {
71797
+ return escapeXml /*xml*/ `<si><t xml:space="preserve">${string}</t></si>`;
71798
+ }
71799
+ return escapeXml /*xml*/ `<si><t>${string}</t></si>`;
71800
+ });
71655
71801
  const xml = escapeXml /*xml*/ `
71656
71802
  <sst ${formatAttributes(namespaces)}>
71657
71803
  ${joinXmlNodes(stringNodes)}
@@ -71896,7 +72042,7 @@ class Model extends EventBus {
71896
72042
  // events
71897
72043
  this.setupSessionEvents();
71898
72044
  this.joinSession();
71899
- if (config.snapshotRequested) {
72045
+ if (config.snapshotRequested || (data["[Content_Types].xml"] && !this.getters.isReadonly())) {
71900
72046
  const startSnapshot = performance.now();
71901
72047
  console.debug("Snapshot requested");
71902
72048
  this.session.snapshot(this.exportData());
@@ -72394,6 +72540,8 @@ const helpers = {
72394
72540
  splitReference,
72395
72541
  formatTickValue,
72396
72542
  sanitizeSheetName,
72543
+ isNumber,
72544
+ isDateTime,
72397
72545
  };
72398
72546
  const links = {
72399
72547
  isMarkdownLink,
@@ -72529,6 +72677,6 @@ exports.tokenColors = tokenColors;
72529
72677
  exports.tokenize = tokenize;
72530
72678
 
72531
72679
 
72532
- __info__.version = "18.0.7";
72533
- __info__.date = "2024-12-05T10:41:39.380Z";
72534
- __info__.hash = "a2652c5";
72680
+ __info__.version = "18.0.9";
72681
+ __info__.date = "2025-01-14T11:33:47.429Z";
72682
+ __info__.hash = "0c5220e";