@odoo/o-spreadsheet 18.1.0 → 18.1.2

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.1.0
6
- * @date 2024-12-26T06:37:07.879Z
7
- * @hash c520e89
5
+ * @version 18.1.2
6
+ * @date 2025-01-15T08:06:07.728Z
7
+ * @hash 002aa4a
8
8
  */
9
9
 
10
10
  'use strict';
@@ -3711,6 +3711,7 @@ exports.CommandResult = void 0;
3711
3711
  CommandResult["SheetIsHidden"] = "SheetIsHidden";
3712
3712
  CommandResult["InvalidTableResize"] = "InvalidTableResize";
3713
3713
  CommandResult["PivotIdNotFound"] = "PivotIdNotFound";
3714
+ CommandResult["PivotInError"] = "PivotInError";
3714
3715
  CommandResult["EmptyName"] = "EmptyName";
3715
3716
  CommandResult["ValueCellIsInvalidFormula"] = "ValueCellIsInvalidFormula";
3716
3717
  CommandResult["InvalidDefinition"] = "InvalidDefinition";
@@ -7479,18 +7480,18 @@ function predictLinearValues(Y, X, newX, computeIntercept) {
7479
7480
  });
7480
7481
  return newY.length === newX.length ? newY : transposeMatrix(newY);
7481
7482
  }
7482
- function getMovingAverageValues(dataset, windowSize = DEFAULT_WINDOW_SIZE) {
7483
+ function getMovingAverageValues(dataset, labels, windowSize = DEFAULT_WINDOW_SIZE) {
7483
7484
  const values = [];
7484
7485
  // Fill the starting values with null until we have a full window
7485
7486
  for (let i = 0; i < windowSize - 1; i++) {
7486
- values.push(null);
7487
+ values.push({ x: labels[i], y: NaN });
7487
7488
  }
7488
7489
  for (let i = 0; i <= dataset.length - windowSize; i++) {
7489
7490
  let sum = 0;
7490
7491
  for (let j = i; j < i + windowSize; j++) {
7491
7492
  sum += dataset[j];
7492
7493
  }
7493
- values.push(sum / windowSize);
7494
+ values.push({ x: labels[i + windowSize - 1], y: sum / windowSize });
7494
7495
  }
7495
7496
  return values;
7496
7497
  }
@@ -19932,6 +19933,17 @@ const TEXT = {
19932
19933
  },
19933
19934
  isExported: true,
19934
19935
  };
19936
+ // -----------------------------------------------------------------------------
19937
+ // VALUE
19938
+ // -----------------------------------------------------------------------------
19939
+ const VALUE = {
19940
+ description: _t("Converts a string to a numeric value."),
19941
+ args: [arg("value (number)", _t("the string to be converted"))],
19942
+ compute: function (value) {
19943
+ return toNumber(value, this.locale);
19944
+ },
19945
+ isExported: true,
19946
+ };
19935
19947
 
19936
19948
  var text = /*#__PURE__*/Object.freeze({
19937
19949
  __proto__: null,
@@ -19954,7 +19966,8 @@ var text = /*#__PURE__*/Object.freeze({
19954
19966
  TEXT: TEXT,
19955
19967
  TEXTJOIN: TEXTJOIN,
19956
19968
  TRIM: TRIM,
19957
- UPPER: UPPER
19969
+ UPPER: UPPER,
19970
+ VALUE: VALUE
19958
19971
  });
19959
19972
 
19960
19973
  // -----------------------------------------------------------------------------
@@ -24142,7 +24155,7 @@ function convertWidthFromExcel(width) {
24142
24155
  return width;
24143
24156
  return Math.round((width / WIDTH_FACTOR) * 100) / 100;
24144
24157
  }
24145
- function extractStyle(data, styleId, formatId, borderId) {
24158
+ function extractStyle(data, content, styleId, formatId, borderId) {
24146
24159
  const style = styleId ? data.styles[styleId] : {};
24147
24160
  const format = formatId ? data.formats[formatId] : undefined;
24148
24161
  const styles = {
@@ -24164,7 +24177,7 @@ function extractStyle(data, styleId, formatId, borderId) {
24164
24177
  vertical: style.verticalAlign
24165
24178
  ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
24166
24179
  : undefined,
24167
- wrapText: style.wrapping === "wrap" || undefined,
24180
+ wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
24168
24181
  },
24169
24182
  };
24170
24183
  styles.font["strike"] = !!style?.strikethrough || undefined;
@@ -24395,7 +24408,7 @@ function convertFigure(figure, id, sheetData) {
24395
24408
  return undefined;
24396
24409
  }
24397
24410
  function isChartData(data) {
24398
- return "dataSets" in data;
24411
+ return "dataSets" in data && data.dataSets.length > 0;
24399
24412
  }
24400
24413
  function isImageData(data) {
24401
24414
  return "imageSrc" in data;
@@ -24741,9 +24754,8 @@ function convertRows(sheet, numberOfRows, headerGroups) {
24741
24754
  }
24742
24755
  return rows;
24743
24756
  }
24744
- /** Remove newlines (\n) in shared strings, We do not support them */
24745
24757
  function convertSharedStrings(xlsxSharedStrings) {
24746
- return xlsxSharedStrings.map((str) => str.replace(/\n/g, ""));
24758
+ return xlsxSharedStrings.map(replaceNewLines);
24747
24759
  }
24748
24760
  function convertCells(sheet, data, sheetDims, warningManager) {
24749
24761
  const cells = {};
@@ -25831,15 +25843,10 @@ class XlsxMiscExtractor extends XlsxBaseExtractor {
25831
25843
  getSharedStrings() {
25832
25844
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "si" }, (ssElement) => {
25833
25845
  // Shared string can either be a simple text, or a rich text (text with formatting, possibly in multiple parts)
25834
- if (ssElement.children[0].tagName === "t") {
25835
- return this.extractTextContent(ssElement) || "";
25836
- }
25837
25846
  // We don't support rich text formatting, we'll only extract the text
25838
- else {
25839
- return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25840
- return this.extractTextContent(textElement) || "";
25841
- }).join("");
25842
- }
25847
+ return this.mapOnElements({ parent: ssElement, query: "t" }, (textElement) => {
25848
+ return this.extractTextContent(textElement) || "";
25849
+ }).join("");
25843
25850
  });
25844
25851
  }
25845
25852
  }
@@ -27480,6 +27487,13 @@ migrationStepRegistry
27480
27487
  }
27481
27488
  return data;
27482
27489
  },
27490
+ })
27491
+ .add("migration_24", {
27492
+ // Empty migration to allow odoo migrate pivot custom sorting.
27493
+ versionFrom: "24",
27494
+ migrate(data) {
27495
+ return data;
27496
+ },
27483
27497
  });
27484
27498
  function fixOverlappingFilters(data) {
27485
27499
  for (let sheet of data.sheets || []) {
@@ -27507,7 +27521,7 @@ function fixOverlappingFilters(data) {
27507
27521
  * a breaking change is made in the way the state is handled, and an upgrade
27508
27522
  * function should be defined
27509
27523
  */
27510
- const CURRENT_VERSION = 24;
27524
+ const CURRENT_VERSION = 25;
27511
27525
  const INITIAL_SHEET_ID = "Sheet1";
27512
27526
  /**
27513
27527
  * This function tries to load anything that could look like a valid
@@ -28408,12 +28422,12 @@ function getTrendDatasetForLineChart(config, data, labels, axisType, locale) {
28408
28422
  }
28409
28423
  const numberOfStep = 5 * trendLabels.length;
28410
28424
  const step = (xmax - xmin) / numberOfStep;
28411
- const newLabels = range(xmin, xmax + step / 2, step);
28412
- const newValues = interpolateData(config, filteredValues, filteredLabels, newLabels);
28413
- if (!newValues.length) {
28425
+ const trendNewLabels = range(xmin, xmax + step / 2, step);
28426
+ const trendValues = interpolateData(config, filteredValues, filteredLabels, trendNewLabels);
28427
+ if (!trendValues.length) {
28414
28428
  return;
28415
28429
  }
28416
- return newValues;
28430
+ return trendValues;
28417
28431
  }
28418
28432
  function interpolateData(config, values, labels, newLabels) {
28419
28433
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
@@ -28429,13 +28443,16 @@ function interpolateData(config, values, labels, newLabels) {
28429
28443
  case "polynomial": {
28430
28444
  const order = config.order;
28431
28445
  if (!order) {
28432
- return Array.from({ length: newLabels.length }, () => NaN);
28446
+ return newLabels.map((x) => ({ x, y: NaN }));
28433
28447
  }
28434
28448
  if (order === 1) {
28435
- return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0];
28449
+ return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0].map((y, i) => ({ x: newLabels[i], y }));
28436
28450
  }
28437
28451
  const coeffs = polynomialRegression(values, normalizedLabels, order, true).flat();
28438
- return normalizedNewLabels.map((v) => evaluatePolynomial(coeffs, v, order));
28452
+ return normalizedNewLabels.map((x, i) => ({
28453
+ x: newLabels[i],
28454
+ y: evaluatePolynomial(coeffs, x, order),
28455
+ }));
28439
28456
  }
28440
28457
  case "exponential": {
28441
28458
  const positiveLogValues = [];
@@ -28447,22 +28464,22 @@ function interpolateData(config, values, labels, newLabels) {
28447
28464
  }
28448
28465
  }
28449
28466
  if (!filteredLabels.length) {
28450
- return Array.from({ length: newLabels.length }, () => NaN);
28467
+ return newLabels.map((x) => ({ x, y: NaN }));
28451
28468
  }
28452
- return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0];
28469
+ return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0].map((y, i) => ({ x: newLabels[i], y }));
28453
28470
  }
28454
28471
  case "logarithmic": {
28455
- return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0];
28472
+ return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0].map((y, i) => ({ x: newLabels[i], y }));
28456
28473
  }
28457
28474
  case "trailingMovingAverage": {
28458
- return getMovingAverageValues(values, config.window);
28475
+ return getMovingAverageValues(values, labels, config.window);
28459
28476
  }
28460
28477
  default:
28461
- return Array.from({ length: newLabels.length }, () => NaN);
28478
+ return newLabels.map((x) => ({ x, y: NaN }));
28462
28479
  }
28463
28480
  }
28464
28481
  catch (e) {
28465
- return Array.from({ length: newLabels.length }, () => NaN);
28482
+ return newLabels.map((x) => ({ x, y: NaN }));
28466
28483
  }
28467
28484
  }
28468
28485
  function getChartAxisType(chart, labelRange, getters) {
@@ -28698,7 +28715,7 @@ function getChartDatasetValues(getters, dataSets) {
28698
28715
  // then using the classical aggregation method to sum the values.
28699
28716
  data.fill(1);
28700
28717
  }
28701
- else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), getters.getLocale()))) {
28718
+ else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28702
28719
  continue;
28703
28720
  }
28704
28721
  datasetValues.push({ data, label });
@@ -29092,6 +29109,7 @@ function getWaterfallChartLegend(definition, args) {
29092
29109
  return legendValues;
29093
29110
  },
29094
29111
  },
29112
+ onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29095
29113
  };
29096
29114
  }
29097
29115
  function getRadarChartLegend(definition, args) {
@@ -29233,14 +29251,19 @@ function getLineChartScales(definition, args) {
29233
29251
  /* We add a second x axis here to draw the trend lines, with the labels length being
29234
29252
  * set so that the second axis points match the classical x axis
29235
29253
  */
29236
- const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset?.length || 0));
29237
29254
  scales[TREND_LINE_XAXIS_ID] = {
29238
29255
  ...scales.x,
29239
- type: "category",
29240
- labels: range(0, maxLength).map((x) => x.toString()),
29241
- offset: false,
29242
29256
  display: false,
29243
29257
  };
29258
+ if (axisType === "category" || axisType === "time") {
29259
+ /* We add a second x axis here to draw the trend lines, with the labels length being
29260
+ * set so that the second axis points match the classical x axis
29261
+ */
29262
+ const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset?.length || 0));
29263
+ scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29264
+ scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29265
+ scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29266
+ }
29244
29267
  }
29245
29268
  return scales;
29246
29269
  }
@@ -29510,7 +29533,9 @@ function getLineChartTooltip(definition, args) {
29510
29533
  if (axisType === "linear") {
29511
29534
  tooltip.callbacks.label = (tooltipItem) => {
29512
29535
  const dataSetPoint = tooltipItem.parsed.y;
29513
- let label = tooltipItem.parsed.x;
29536
+ let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29537
+ ? ""
29538
+ : tooltipItem.parsed.x;
29514
29539
  if (typeof label === "string" && isNumber(label, locale)) {
29515
29540
  label = toNumber(label, locale);
29516
29541
  }
@@ -30203,7 +30228,11 @@ function createGaugeChartRuntime(chart, getters) {
30203
30228
  colors.push(chartColors.upperColor);
30204
30229
  return {
30205
30230
  background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
30206
- title: chart.title ?? { text: "" },
30231
+ title: {
30232
+ ...chart.title,
30233
+ // chart titles are extracted from .json files and they are translated at runtime here
30234
+ text: _t(chart.title.text ?? ""),
30235
+ },
30207
30236
  minValue: {
30208
30237
  value: minValue,
30209
30238
  label: formatValue(minValue, { locale, format }),
@@ -35334,12 +35363,20 @@ function fontSizeMenuBuilder() {
35334
35363
  });
35335
35364
  }
35336
35365
  function isAutomaticFormatSelected(env) {
35337
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
35338
- return !activeCell || !activeCell.format;
35366
+ const activePosition = env.model.getters.getActivePosition();
35367
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
35368
+ if (pivotCell.type === "VALUE") {
35369
+ return !env.model.getters.getEvaluatedCell(activePosition).format;
35370
+ }
35371
+ return !env.model.getters.getCell(activePosition)?.format;
35339
35372
  }
35340
35373
  function isFormatSelected(env, format) {
35341
- const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
35342
- return activeCell?.format === format;
35374
+ const activePosition = env.model.getters.getActivePosition();
35375
+ const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
35376
+ if (pivotCell.type === "VALUE") {
35377
+ return env.model.getters.getEvaluatedCell(activePosition).format === format;
35378
+ }
35379
+ return env.model.getters.getCell(activePosition)?.format === format;
35343
35380
  }
35344
35381
  function isFontSizeSelected(env, fontSize) {
35345
35382
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
@@ -39678,14 +39715,11 @@ class ChartPanel extends owl.Component {
39678
39715
  }
39679
39716
 
39680
39717
  class DOMFocusableElementStore {
39681
- mutators = ["setFocusableElement", "focus"];
39718
+ mutators = ["setFocusableElement"];
39682
39719
  focusableElement = undefined;
39683
39720
  setFocusableElement(element) {
39684
39721
  this.focusableElement = element;
39685
39722
  }
39686
- focus() {
39687
- this.focusableElement?.focus();
39688
- }
39689
39723
  }
39690
39724
 
39691
39725
  css /* scss */ `
@@ -40333,7 +40367,7 @@ class Composer extends owl.Component {
40333
40367
  if (document.activeElement === this.contentHelper.el &&
40334
40368
  this.props.composerStore.editionMode === "inactive" &&
40335
40369
  !this.props.isDefaultFocus) {
40336
- this.DOMFocusableElementStore.focus();
40370
+ this.DOMFocusableElementStore.focusableElement?.focus();
40337
40371
  }
40338
40372
  });
40339
40373
  owl.useEffect(() => {
@@ -44314,16 +44348,21 @@ class TextInput extends owl.Component {
44314
44348
  }
44315
44349
  this.inputRef.el?.blur();
44316
44350
  }
44317
- focusInputAndSelectContent() {
44318
- const inputEl = this.inputRef.el;
44319
- if (!inputEl)
44320
- return;
44321
- // The onFocus event selects all text in the input.
44322
- // The subsequent mouseup event can deselect this text,
44323
- // so t-on-mouseup.prevent.stop is used to prevent this
44324
- // default behavior and preserve the selection.
44325
- inputEl.focus();
44326
- inputEl.select();
44351
+ onMouseDown(ev) {
44352
+ // Stop the event if the input is not focused, we handle everything in onMouseUp
44353
+ if (ev.target !== document.activeElement) {
44354
+ ev.preventDefault();
44355
+ ev.stopPropagation();
44356
+ }
44357
+ }
44358
+ onMouseUp(ev) {
44359
+ const target = ev.target;
44360
+ if (target !== document.activeElement) {
44361
+ target.focus();
44362
+ target.select();
44363
+ ev.preventDefault();
44364
+ ev.stopPropagation();
44365
+ }
44327
44366
  }
44328
44367
  }
44329
44368
 
@@ -44876,7 +44915,16 @@ class PivotTitleSection extends owl.Component {
44876
44915
  newPivotId,
44877
44916
  newSheetId,
44878
44917
  });
44879
- const text = result.isSuccessful ? _t("Pivot duplicated.") : _t("Pivot duplication failed");
44918
+ let text;
44919
+ if (result.isSuccessful) {
44920
+ text = _t("Pivot duplicated.");
44921
+ }
44922
+ else if (result.isCancelledBecause("PivotInError" /* CommandResult.PivotInError */)) {
44923
+ text = _t("Cannot duplicate a pivot in error.");
44924
+ }
44925
+ else {
44926
+ text = _t("Pivot duplication failed.");
44927
+ }
44880
44928
  const type = result.isSuccessful ? "success" : "danger";
44881
44929
  this.env.notifyUser({
44882
44930
  text,
@@ -46215,7 +46263,9 @@ class PivotSidePanelStore extends SpreadsheetStore {
46215
46263
  pivot: this.draft,
46216
46264
  });
46217
46265
  this.draft = null;
46218
- if (!this.alreadyNotified && !this.isDynamicPivotInViewport()) {
46266
+ if (!this.alreadyNotified &&
46267
+ !this.isDynamicPivotInViewport() &&
46268
+ this.isStaticPivotInViewport()) {
46219
46269
  const formulaId = this.getters.getPivotFormulaId(this.pivotId);
46220
46270
  const pivotExample = `=PIVOT(${formulaId})`;
46221
46271
  this.alreadyNotified = true;
@@ -46283,6 +46333,18 @@ class PivotSidePanelStore extends SpreadsheetStore {
46283
46333
  }
46284
46334
  return false;
46285
46335
  }
46336
+ isStaticPivotInViewport() {
46337
+ for (const position of this.getters.getVisibleCellPositions()) {
46338
+ const cell = this.getters.getCell(position);
46339
+ if (cell?.isFormula) {
46340
+ const pivotFunction = getFirstPivotFunction(cell.compiledFormula.tokens);
46341
+ if (pivotFunction && pivotFunction.functionName !== "PIVOT") {
46342
+ return true;
46343
+ }
46344
+ }
46345
+ }
46346
+ return false;
46347
+ }
46286
46348
  addDefaultDateTimeGranularity(fields, definition) {
46287
46349
  const { columns, rows } = definition;
46288
46350
  const columnsWithGranularity = deepCopy(columns);
@@ -51695,7 +51757,7 @@ class Grid extends owl.Component {
51695
51757
  this.cellPopovers = useStore(CellPopoverStore);
51696
51758
  owl.useEffect(() => {
51697
51759
  if (!this.sidePanel.isOpen) {
51698
- this.DOMFocusableElementStore.focus();
51760
+ this.DOMFocusableElementStore.focusableElement?.focus();
51699
51761
  }
51700
51762
  }, () => [this.sidePanel.isOpen]);
51701
51763
  }
@@ -51901,7 +51963,7 @@ class Grid extends owl.Component {
51901
51963
  focusDefaultElement() {
51902
51964
  if (!this.env.model.getters.getSelectedFigureId() &&
51903
51965
  this.composerFocusStore.activeComposer.editionMode === "inactive") {
51904
- this.DOMFocusableElementStore.focus();
51966
+ this.DOMFocusableElementStore.focusableElement?.focus();
51905
51967
  }
51906
51968
  }
51907
51969
  get gridEl() {
@@ -52438,10 +52500,34 @@ class BordersPlugin extends CorePlugin {
52438
52500
  const elements = [...cmd.elements].sort((a, b) => b - a);
52439
52501
  for (const group of groupConsecutive(elements)) {
52440
52502
  if (cmd.dimension === "COL") {
52441
- this.shiftBordersHorizontally(cmd.sheetId, group[group.length - 1] + 1, -group.length);
52503
+ if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52504
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52505
+ this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52506
+ }
52507
+ }
52508
+ if (group[group.length - 1] === 0) {
52509
+ for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52510
+ this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52511
+ }
52512
+ }
52513
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52514
+ this.clearInsideBorders(cmd.sheetId, [zone]);
52515
+ this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52442
52516
  }
52443
52517
  else {
52444
- this.shiftBordersVertically(cmd.sheetId, group[group.length - 1] + 1, -group.length);
52518
+ if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52519
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52520
+ this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52521
+ }
52522
+ }
52523
+ if (group[group.length - 1] === 0) {
52524
+ for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52525
+ this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52526
+ }
52527
+ }
52528
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52529
+ this.clearInsideBorders(cmd.sheetId, [zone]);
52530
+ this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52445
52531
  }
52446
52532
  }
52447
52533
  break;
@@ -52748,6 +52834,18 @@ class BordersPlugin extends CorePlugin {
52748
52834
  }
52749
52835
  }
52750
52836
  }
52837
+ /**
52838
+ * Remove the borders inside of a zone
52839
+ */
52840
+ clearInsideBorders(sheetId, zones) {
52841
+ for (let zone of zones) {
52842
+ for (let row = zone.top; row <= zone.bottom; row++) {
52843
+ for (let col = zone.left; col <= zone.right; col++) {
52844
+ this.history.update("borders", sheetId, col, row, undefined);
52845
+ }
52846
+ }
52847
+ }
52848
+ }
52751
52849
  /**
52752
52850
  * Add a border to the existing one to a cell
52753
52851
  */
@@ -63944,6 +64042,19 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
63944
64042
 
63945
64043
  class InsertPivotPlugin extends UIPlugin {
63946
64044
  static getters = [];
64045
+ allowDispatch(cmd) {
64046
+ switch (cmd.type) {
64047
+ case "DUPLICATE_PIVOT_IN_NEW_SHEET":
64048
+ if (!this.getters.isExistingPivot(cmd.pivotId)) {
64049
+ return "PivotIdNotFound" /* CommandResult.PivotIdNotFound */;
64050
+ }
64051
+ if (!this.getters.getPivot(cmd.pivotId).isValid()) {
64052
+ return "PivotInError" /* CommandResult.PivotInError */;
64053
+ }
64054
+ break;
64055
+ }
64056
+ return "Success" /* CommandResult.Success */;
64057
+ }
63947
64058
  handle(cmd) {
63948
64059
  switch (cmd.type) {
63949
64060
  case "INSERT_NEW_PIVOT":
@@ -68180,11 +68291,6 @@ class BottomBarSheet extends owl.Component {
68180
68291
  editionState = "initializing";
68181
68292
  DOMFocusableElementStore;
68182
68293
  setup() {
68183
- owl.onMounted(() => {
68184
- if (this.isSheetActive) {
68185
- this.scrollToSheet();
68186
- }
68187
- });
68188
68294
  owl.onPatched(() => {
68189
68295
  if (this.sheetNameRef.el && this.state.isEditing && this.editionState === "initializing") {
68190
68296
  this.editionState = "editing";
@@ -68193,6 +68299,11 @@ class BottomBarSheet extends owl.Component {
68193
68299
  });
68194
68300
  this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
68195
68301
  owl.useExternalListener(window, "click", () => (this.state.pickerOpened = false));
68302
+ owl.useEffect((sheetId) => {
68303
+ if (this.props.sheetId === sheetId) {
68304
+ this.scrollToSheet();
68305
+ }
68306
+ }, () => [this.env.model.getters.getActiveSheetId()]);
68196
68307
  }
68197
68308
  focusInputAndSelectContent() {
68198
68309
  if (!this.state.isEditing || !this.sheetNameRef.el)
@@ -68204,7 +68315,10 @@ class BottomBarSheet extends owl.Component {
68204
68315
  }
68205
68316
  }
68206
68317
  scrollToSheet() {
68207
- this.sheetDivRef.el?.scrollIntoView?.();
68318
+ this.sheetDivRef.el?.scrollIntoView?.({
68319
+ behavior: "smooth",
68320
+ inline: "nearest",
68321
+ });
68208
68322
  }
68209
68323
  onFocusOut() {
68210
68324
  if (this.state.isEditing && this.editionState !== "initializing") {
@@ -68234,11 +68348,11 @@ class BottomBarSheet extends owl.Component {
68234
68348
  if (ev.key === "Enter") {
68235
68349
  ev.preventDefault();
68236
68350
  this.stopEdition();
68237
- this.DOMFocusableElementStore.focus();
68351
+ this.DOMFocusableElementStore.focusableElement?.focus();
68238
68352
  }
68239
68353
  if (ev.key === "Escape") {
68240
68354
  this.cancelEdition();
68241
- this.DOMFocusableElementStore.focus();
68355
+ this.DOMFocusableElementStore.focusableElement?.focus();
68242
68356
  }
68243
68357
  }
68244
68358
  onMouseEventSheetName(ev) {
@@ -73614,7 +73728,7 @@ function addRows(construct, data, sheet) {
73614
73728
  if (content || styleId || formatId || borderId || value !== undefined) {
73615
73729
  const attributes = [["r", xc]];
73616
73730
  // style
73617
- const id = normalizeStyle(construct, extractStyle(data, styleId, formatId, borderId));
73731
+ const id = normalizeStyle(construct, extractStyle(data, content, styleId, formatId, borderId));
73618
73732
  // don't add style if default
73619
73733
  if (id) {
73620
73734
  attributes.push(["s", id]);
@@ -73968,7 +74082,12 @@ function createSharedStrings(strings) {
73968
74082
  ["count", strings.length],
73969
74083
  ["uniqueCount", strings.length],
73970
74084
  ];
73971
- const stringNodes = strings.map((string) => escapeXml /*xml*/ `<si><t>${string}</t></si>`);
74085
+ const stringNodes = strings.map((string) => {
74086
+ if (string.trim() !== string) {
74087
+ return escapeXml /*xml*/ `<si><t xml:space="preserve">${string}</t></si>`;
74088
+ }
74089
+ return escapeXml /*xml*/ `<si><t>${string}</t></si>`;
74090
+ });
73972
74091
  const xml = escapeXml /*xml*/ `
73973
74092
  <sst ${formatAttributes(namespaces)}>
73974
74093
  ${joinXmlNodes(stringNodes)}
@@ -74208,7 +74327,7 @@ class Model extends EventBus {
74208
74327
  // events
74209
74328
  this.setupSessionEvents();
74210
74329
  this.joinSession();
74211
- if (config.snapshotRequested) {
74330
+ if (config.snapshotRequested || (data["[Content_Types].xml"] && !this.getters.isReadonly())) {
74212
74331
  const startSnapshot = performance.now();
74213
74332
  console.debug("Snapshot requested");
74214
74333
  this.session.snapshot(this.exportData());
@@ -74695,6 +74814,8 @@ const helpers = {
74695
74814
  areDomainArgsFieldsValid,
74696
74815
  splitReference,
74697
74816
  sanitizeSheetName,
74817
+ isNumber,
74818
+ isDateTime,
74698
74819
  };
74699
74820
  const links = {
74700
74821
  isMarkdownLink,
@@ -74833,6 +74954,6 @@ exports.tokenColors = tokenColors;
74833
74954
  exports.tokenize = tokenize;
74834
74955
 
74835
74956
 
74836
- __info__.version = "18.1.0";
74837
- __info__.date = "2024-12-26T06:37:07.879Z";
74838
- __info__.hash = "c520e89";
74957
+ __info__.version = "18.1.2";
74958
+ __info__.date = "2025-01-15T08:06:07.728Z";
74959
+ __info__.hash = "002aa4a";