@odoo/o-spreadsheet 19.1.0-alpha.12 → 19.1.0-alpha.13

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,8 +3,8 @@
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
5
  * @version 19.1.0-alpha.3
6
- * @date 2025-11-12T14:16:26.552Z
7
- * @hash 6fefc9c
6
+ * @date 2025-11-24T07:52:34.809Z
7
+ * @hash e232982
8
8
  */
9
9
 
10
10
  (function (exports) {
@@ -637,6 +637,7 @@
637
637
  fontSize: 10,
638
638
  fillColor: "",
639
639
  textColor: "",
640
+ rotation: 0,
640
641
  };
641
642
  const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
642
643
  // Fonts
@@ -682,7 +683,7 @@
682
683
  };
683
684
  const PIVOT_INDENT = 15;
684
685
  const PIVOT_COLLAPSE_ICON_SIZE = 12;
685
- const PIVOT_MAX_NUMBER_OF_CELLS = 1e5;
686
+ const PIVOT_MAX_NUMBER_OF_CELLS = 5e5;
686
687
 
687
688
  //------------------------------------------------------------------------------
688
689
  // Miscellaneous
@@ -3648,6 +3649,30 @@
3648
3649
  WRAPROWS: WRAPROWS
3649
3650
  });
3650
3651
 
3652
+ const DEFAULT_LOCALES = [
3653
+ {
3654
+ name: "English (US)",
3655
+ code: "en_US",
3656
+ thousandsSeparator: ",",
3657
+ decimalSeparator: ".",
3658
+ weekStart: 7, // Sunday
3659
+ dateFormat: "m/d/yyyy",
3660
+ timeFormat: "hh:mm:ss a",
3661
+ formulaArgSeparator: ",",
3662
+ },
3663
+ {
3664
+ name: "French",
3665
+ code: "fr_FR",
3666
+ thousandsSeparator: " ",
3667
+ decimalSeparator: ",",
3668
+ weekStart: 1, // Monday
3669
+ dateFormat: "dd/mm/yyyy",
3670
+ timeFormat: "hh:mm:ss",
3671
+ formulaArgSeparator: ";",
3672
+ },
3673
+ ];
3674
+ const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
3675
+
3651
3676
  function tokenizeFormat(str) {
3652
3677
  const chars = new TokenizingChars(str);
3653
3678
  const result = [];
@@ -3932,7 +3957,7 @@
3932
3957
  }
3933
3958
  function parseDateFormatTokens(tokens) {
3934
3959
  const internalFormat = tokens && areValidDateFormatTokens(tokens) ? { type: "date", tokens } : undefined;
3935
- if (!internalFormat) {
3960
+ if (!internalFormat || !tokens?.some((token) => token.type === "DATE_PART")) {
3936
3961
  return undefined;
3937
3962
  }
3938
3963
  if (internalFormat.tokens.length &&
@@ -4169,6 +4194,9 @@
4169
4194
  return prefix + repeatedChar.repeat(timesToRepeat) + padding + suffix;
4170
4195
  }
4171
4196
  function applyInternalNumberFormat(value, format, locale) {
4197
+ if (!format.integerPart.length && !format.decimalPart?.length) {
4198
+ return "";
4199
+ }
4172
4200
  if (value === Infinity) {
4173
4201
  return "∞" + (format.percentSymbols ? "%" : "");
4174
4202
  }
@@ -4775,7 +4803,7 @@
4775
4803
  function setXcToFixedReferenceType(xc, referenceType) {
4776
4804
  let sheetName;
4777
4805
  ({ sheetName, xc } = splitReference(xc));
4778
- sheetName = sheetName ? sheetName + "!" : "";
4806
+ sheetName = sheetName ? getCanonicalSymbolName(sheetName) + "!" : "";
4779
4807
  xc = xc.replace(/\$/g, "");
4780
4808
  const splitIndex = xc.indexOf(":");
4781
4809
  if (splitIndex >= 0) {
@@ -7652,7 +7680,7 @@
7652
7680
  if (!acceptHiddenCells && this.getters.isRowHiddenByUser(sheetId, row))
7653
7681
  continue;
7654
7682
  for (let col = left; col <= right; col++) {
7655
- const cell = this.getters.getCell({ sheetId, col, row });
7683
+ const cell = this.getters.getCorrespondingFormulaCell({ sheetId, col, row });
7656
7684
  if (!cell || !isSubtotalCell(cell)) {
7657
7685
  evaluatedCellToKeep.push(this.getters.getEvaluatedCell({ sheetId, col, row }));
7658
7686
  }
@@ -9547,30 +9575,6 @@
9547
9575
  DVARP: DVARP
9548
9576
  });
9549
9577
 
9550
- const DEFAULT_LOCALES = [
9551
- {
9552
- name: "English (US)",
9553
- code: "en_US",
9554
- thousandsSeparator: ",",
9555
- decimalSeparator: ".",
9556
- weekStart: 7, // Sunday
9557
- dateFormat: "m/d/yyyy",
9558
- timeFormat: "hh:mm:ss a",
9559
- formulaArgSeparator: ",",
9560
- },
9561
- {
9562
- name: "French",
9563
- code: "fr_FR",
9564
- thousandsSeparator: " ",
9565
- decimalSeparator: ",",
9566
- weekStart: 1, // Monday
9567
- dateFormat: "dd/mm/yyyy",
9568
- timeFormat: "hh:mm:ss",
9569
- formulaArgSeparator: ";",
9570
- },
9571
- ];
9572
- const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
9573
-
9574
9578
  /**
9575
9579
  * Tokenizer
9576
9580
  *
@@ -13975,18 +13979,17 @@
13975
13979
  ["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
13976
13980
  ["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
13977
13981
  },
13978
- GeoChart: {
13979
- ColorScales: {
13980
- blues: _t("Blues"),
13981
- cividis: _t("Cividis"),
13982
- greens: _t("Greens"),
13983
- greys: _t("Greys"),
13984
- oranges: _t("Oranges"),
13985
- purples: _t("Purples"),
13986
- rainbow: _t("Rainbow"),
13987
- reds: _t("Reds"),
13988
- viridis: _t("Viridis"),
13989
- },
13982
+ ColorScales: {
13983
+ blues: _t("Blues"),
13984
+ cividis: _t("Cividis"),
13985
+ custom: _t("Custom"),
13986
+ greens: _t("Greens"),
13987
+ greys: _t("Greys"),
13988
+ oranges: _t("Oranges"),
13989
+ purples: _t("Purples"),
13990
+ rainbow: _t("Rainbow"),
13991
+ reds: _t("Reds"),
13992
+ viridis: _t("Viridis"),
13990
13993
  },
13991
13994
  });
13992
13995
  ({
@@ -18821,6 +18824,17 @@
18821
18824
  return this.colors[id];
18822
18825
  }
18823
18826
  }
18827
+ const COLORSCHEMES = {
18828
+ greys: ["#ffffff", "#808080", "#000000"],
18829
+ blues: ["#f7fbff", "#6aaed6", "#08306b"],
18830
+ reds: ["#fff5f0", "#fb694a", "#67000d"],
18831
+ greens: ["#f7fcf5", "#73c476", "#00441b"],
18832
+ oranges: ["#fff5eb", "#fd8c3b", "#7f2704"],
18833
+ purples: ["#fcfbfd", "#9e9ac8", "#3f007d"],
18834
+ viridis: ["#440154", "#21918c", "#fde725"],
18835
+ cividis: ["#00224e", "#7d7c78", "#fee838"],
18836
+ rainbow: ["#B41DB4", "#FFFF00", "#00FFFF"],
18837
+ };
18824
18838
  /**
18825
18839
  * Returns a function that maps a value to a color using a color scale defined by the given
18826
18840
  * color/threshold values pairs.
@@ -26516,6 +26530,20 @@
26516
26530
  }
26517
26531
  return data;
26518
26532
  },
26533
+ })
26534
+ .add("19.2", {
26535
+ migrate(data) {
26536
+ for (const sheet of data.sheets || []) {
26537
+ for (const figure of sheet.figures || []) {
26538
+ if (figure.tag === "chart" && figure.data.type === "geo") {
26539
+ if ("colorScale" in figure.data && typeof figure.data.colorScale === "string") {
26540
+ figure.data.colorScale = COLORSCHEMES[figure.data.colorScale];
26541
+ }
26542
+ }
26543
+ }
26544
+ }
26545
+ return data;
26546
+ },
26519
26547
  });
26520
26548
  function fixOverlappingFilters(data) {
26521
26549
  for (const sheet of data.sheets || []) {
@@ -27316,22 +27344,34 @@
27316
27344
  addBorder(sheetId, zone, newBorder, force = false) {
27317
27345
  const borders = [];
27318
27346
  const plannedBorder = newBorder ? { zone, style: newBorder } : undefined;
27319
- const sideToClear = {
27320
- left: force || !!newBorder?.left,
27321
- right: force || !!newBorder?.right,
27322
- top: force || !!newBorder?.top,
27323
- bottom: force || !!newBorder?.bottom,
27347
+ // For each side, decide if we must clear the border on the *adjacent*
27348
+ // existing cell when we draw on the opposite side of the new zone.
27349
+ //
27350
+ // Example:
27351
+ // - newBorder.right is set → we draw border on the RIGHT side of `zone`
27352
+ // - the cell on the right may already have a LEFT border on that edge
27353
+ // In that case we clear that LEFT border, so only the new RIGHT border
27354
+ // remains on the shared edge.
27355
+ //
27356
+ // existingBorderSideToClear[side] = true means we should clear the border on that
27357
+ // side of the existing adjacent zone before adding the new border.
27358
+ const existingBorderSideToClear = {
27359
+ left: force || !!newBorder?.right,
27360
+ right: force || !!newBorder?.left,
27361
+ top: force || !!newBorder?.bottom,
27362
+ bottom: force || !!newBorder?.top,
27324
27363
  };
27325
27364
  let editingZone = [zone];
27326
27365
  for (const existingBorder of this.borders[sheetId] ?? []) {
27327
27366
  const inter = intersection(existingBorder.zone, zone);
27328
27367
  if (!inter) {
27329
- // Clear adjacent borders on which you write
27368
+ // Check if the existing border is adjacent to the new zone
27330
27369
  const adjacentEdge = adjacent(existingBorder.zone, zone);
27331
- if (adjacentEdge && sideToClear[adjacentEdge.position]) {
27370
+ if (adjacentEdge && existingBorderSideToClear[adjacentEdge.position]) {
27332
27371
  for (const newZone of splitIfAdjacent(existingBorder.zone, zone)) {
27333
27372
  const border = this.computeBorderFromZone(newZone, existingBorder);
27334
27373
  const adjacentEdge = adjacent(newZone, zone);
27374
+ // Clear the existing border on the side that touches the new zone
27335
27375
  switch (adjacentEdge?.position) {
27336
27376
  case "left":
27337
27377
  border.style.left = undefined;
@@ -33218,6 +33258,13 @@
33218
33258
  "getStyleColors",
33219
33259
  ];
33220
33260
  styles = {};
33261
+ allowDispatch(cmd) {
33262
+ switch (cmd.type) {
33263
+ case "SET_FORMATTING":
33264
+ return this.checkUselessSetFormatting(cmd);
33265
+ }
33266
+ return "Success" /* CommandResult.Success */;
33267
+ }
33221
33268
  handle(cmd) {
33222
33269
  switch (cmd.type) {
33223
33270
  case "ADD_MERGE":
@@ -33442,6 +33489,28 @@
33442
33489
  exportForExcel(data) {
33443
33490
  this.export(data);
33444
33491
  }
33492
+ checkUselessSetFormatting(cmd) {
33493
+ const { sheetId, target } = cmd;
33494
+ const hasStyle = "style" in cmd;
33495
+ const hasFormat = "format" in cmd;
33496
+ if (!hasStyle && !hasFormat) {
33497
+ return "NoChanges" /* CommandResult.NoChanges */;
33498
+ }
33499
+ for (const zone of recomputeZones(target)) {
33500
+ for (let col = zone.left; col <= zone.right; col++) {
33501
+ for (let row = zone.top; row <= zone.bottom; row++) {
33502
+ const position = { sheetId, col, row };
33503
+ const cell = this.getters.getCell(position);
33504
+ const style = this.getCellStyle(position);
33505
+ if ((hasStyle && !deepEquals(style, cmd.style)) ||
33506
+ (hasFormat && cell?.format !== cmd.format)) {
33507
+ return "Success" /* CommandResult.Success */;
33508
+ }
33509
+ }
33510
+ }
33511
+ }
33512
+ return "NoChanges" /* CommandResult.NoChanges */;
33513
+ }
33445
33514
  }
33446
33515
 
33447
33516
  class TableStylePlugin extends CorePlugin {
@@ -36469,48 +36538,64 @@
36469
36538
  }
36470
36539
  function getCellContentHeight(ctx, content, style, colSize) {
36471
36540
  const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
36472
- const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
36473
- const fontSize = computeTextFontSizeInPixels(style);
36474
- return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
36541
+ const lines = splitTextToWidth(ctx, content, style, maxWidth);
36542
+ return computeMultilineTextSize(ctx, lines, style).height + 2 * PADDING_AUTORESIZE_VERTICAL;
36475
36543
  }
36476
36544
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
36477
36545
  const italicStr = italic ? "italic" : "";
36478
36546
  const weight = bold ? "bold" : "";
36479
36547
  return `${italicStr} ${weight} ${fontSize}px ${DEFAULT_FONT}`;
36480
36548
  }
36481
- const textWidthCache = {};
36482
- function computeTextWidth(context, text, style, fontUnit = "pt") {
36549
+ function computeMultilineTextSize(context, textLines, style = {}, fontUnit = "pt") {
36550
+ if (!textLines.length)
36551
+ return { width: 0, height: 0 };
36483
36552
  const font = computeTextFont(style, fontUnit);
36484
- return computeCachedTextWidth(context, text, font);
36485
- }
36486
- function computeCachedTextWidth(context, text, font) {
36487
- if (!textWidthCache[font]) {
36488
- textWidthCache[font] = {};
36553
+ const sizes = textLines.map((line) => computeCachedTextDimension(context, line, font));
36554
+ const height = computeTextLinesHeight(sizes[0].height, textLines.length);
36555
+ const width = Math.max(...sizes.map((size) => size.width));
36556
+ if (!style.rotation) {
36557
+ return { height, width };
36489
36558
  }
36490
- if (textWidthCache[font][text] === undefined) {
36491
- const oldFont = context.font;
36492
- context.font = font;
36493
- textWidthCache[font][text] = context.measureText(text).width;
36494
- context.font = oldFont;
36559
+ const cos = Math.abs(Math.cos(style.rotation));
36560
+ const sin = Math.abs(Math.sin(style.rotation));
36561
+ return { width: width * cos + height * sin, height: sin * width + cos * height };
36562
+ }
36563
+ function computeTextWidth(context, text, style = {}, fontUnit = "pt") {
36564
+ const font = computeTextFont(style, fontUnit);
36565
+ return computeCachedTextWidth(context, text, font, style.rotation);
36566
+ }
36567
+ function computeCachedTextWidth(context, text, font, rotation) {
36568
+ const size = computeCachedTextDimension(context, text, font);
36569
+ if (!rotation) {
36570
+ return size.width;
36495
36571
  }
36496
- return textWidthCache[font][text];
36572
+ const cos = Math.abs(Math.cos(rotation));
36573
+ const sin = Math.abs(Math.sin(rotation));
36574
+ return size.width * cos + size.height * sin;
36497
36575
  }
36498
36576
  const textDimensionsCache = {};
36499
36577
  function computeTextDimension(context, text, style, fontUnit = "pt") {
36500
36578
  const font = computeTextFont(style, fontUnit);
36501
- context.save();
36502
- context.font = font;
36503
- const dimensions = computeCachedTextDimension(context, text);
36504
- context.restore();
36505
- return dimensions;
36506
- }
36507
- function computeCachedTextDimension(context, text) {
36508
- const font = context.font;
36579
+ const size = computeCachedTextDimension(context, text, font);
36580
+ if (!style.rotation) {
36581
+ return size;
36582
+ }
36583
+ const cos = Math.abs(Math.cos(style.rotation));
36584
+ const sin = Math.abs(Math.sin(style.rotation));
36585
+ return {
36586
+ width: size.width * cos + size.height * sin,
36587
+ height: size.height * cos + size.width * sin,
36588
+ };
36589
+ }
36590
+ function computeCachedTextDimension(context, text, font) {
36509
36591
  if (!textDimensionsCache[font]) {
36510
36592
  textDimensionsCache[font] = {};
36511
36593
  }
36512
36594
  if (textDimensionsCache[font][text] === undefined) {
36595
+ context.save();
36596
+ context.font = font;
36513
36597
  const measure = context.measureText(text);
36598
+ context.restore();
36514
36599
  const width = measure.width;
36515
36600
  const height = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
36516
36601
  textDimensionsCache[font][text] = { width, height };
@@ -38707,7 +38792,8 @@
38707
38792
  }
38708
38793
  break;
38709
38794
  case "SET_FORMATTING":
38710
- if (cmd.style && ("fontSize" in cmd.style || "wrapping" in cmd.style)) {
38795
+ if (cmd.style &&
38796
+ ("fontSize" in cmd.style || "wrapping" in cmd.style || "rotation" in cmd.style)) {
38711
38797
  for (const zone of cmd.target) {
38712
38798
  // TODO FLDA use rangeSet
38713
38799
  this.updateRowSizeForZoneChange(cmd.sheetId, zone);
@@ -43484,6 +43570,7 @@
43484
43570
  "getTextWidth",
43485
43571
  "getCellText",
43486
43572
  "getCellMultiLineText",
43573
+ "getMultilineTextSize",
43487
43574
  "getContiguousZone",
43488
43575
  "computeTextYCoordinate",
43489
43576
  ];
@@ -43534,7 +43621,7 @@
43534
43621
  const content = this.getters.getEvaluatedCell(position).formattedValue;
43535
43622
  if (content) {
43536
43623
  const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
43537
- contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
43624
+ contentWidth += computeMultilineTextSize(this.ctx, multiLineText, style).width;
43538
43625
  }
43539
43626
  for (const icon of this.getters.getCellIcons(position)) {
43540
43627
  contentWidth += icon.margin + icon.size;
@@ -43555,6 +43642,9 @@
43555
43642
  getTextWidth(text, style) {
43556
43643
  return computeTextWidth(this.ctx, text, style);
43557
43644
  }
43645
+ getMultilineTextSize(text, style) {
43646
+ return computeMultilineTextSize(this.ctx, text, style);
43647
+ }
43558
43648
  getCellText(position, args) {
43559
43649
  const cell = this.getters.getCell(position);
43560
43650
  const locale = this.getters.getLocale();
@@ -46138,6 +46228,7 @@
46138
46228
  "getGridOffset",
46139
46229
  "getViewportZoomLevel",
46140
46230
  "getScrollBarWidth",
46231
+ "getMaximumSheetOffset",
46141
46232
  ];
46142
46233
  viewports = {};
46143
46234
  /**
@@ -47243,6 +47334,9 @@
47243
47334
  observe(owner, callbacks) {
47244
47335
  this.observers.set(owner, { owner, callbacks });
47245
47336
  }
47337
+ unobserve(owner) {
47338
+ this.observers.delete(owner);
47339
+ }
47246
47340
  /**
47247
47341
  * Capture the stream for yourself
47248
47342
  */
@@ -47335,6 +47429,9 @@
47335
47429
  observe(owner, callbacks) {
47336
47430
  this.stream.observe(owner, callbacks);
47337
47431
  }
47432
+ unobserve(owner) {
47433
+ this.stream.unobserve(owner);
47434
+ }
47338
47435
  release(owner) {
47339
47436
  if (this.stream.isListening(owner)) {
47340
47437
  this.stream.release(owner);
@@ -50927,8 +51024,8 @@
50927
51024
 
50928
51025
 
50929
51026
  __info__.version = "19.1.0-alpha.3";
50930
- __info__.date = "2025-11-12T14:16:26.552Z";
50931
- __info__.hash = "6fefc9c";
51027
+ __info__.date = "2025-11-24T07:52:34.809Z";
51028
+ __info__.hash = "e232982";
50932
51029
 
50933
51030
 
50934
51031
  })(this.o_spreadsheet_engine = this.o_spreadsheet_engine || {});