@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.
@@ -1190,7 +1190,7 @@ declare class MergePlugin extends CorePlugin<MergeState> implements MergeState {
1190
1190
  }
1191
1191
 
1192
1192
  type Aggregator = "array_agg" | "count" | "count_distinct" | "bool_and" | "bool_or" | "max" | "min" | "avg" | "sum";
1193
- type Granularity = "day" | "week" | "month" | "quarter" | "year" | "second_number" | "minute_number" | "hour_number" | "day_of_week" | "day_of_month" | "iso_week_number" | "month_number" | "quarter_number";
1193
+ type Granularity = "day" | "month" | "year" | "second_number" | "minute_number" | "hour_number" | "day_of_week" | "day_of_month" | "iso_week_number" | "month_number" | "quarter_number";
1194
1194
  interface PivotCoreDimension {
1195
1195
  fieldName: string;
1196
1196
  order?: SortDirection;
@@ -1645,6 +1645,7 @@ interface StylePluginState {
1645
1645
  declare class StylePlugin extends CorePlugin<StylePluginState> implements StylePluginState {
1646
1646
  static getters: readonly ["getCellStyle", "getCellStyleInZone", "getZoneStyles", "getStyleColors"];
1647
1647
  readonly styles: Record<UID, ZoneStyle[] | undefined>;
1648
+ allowDispatch(cmd: CoreCommand): CommandResult | CommandResult[];
1648
1649
  handle(cmd: CoreCommand): void;
1649
1650
  adaptRanges(applyChange: ApplyRangeChange, sheetId: UID): void;
1650
1651
  private handleAddColumnn;
@@ -1662,6 +1663,7 @@ declare class StylePlugin extends CorePlugin<StylePluginState> implements StyleP
1662
1663
  import(data: WorkbookData): void;
1663
1664
  export(data: WorkbookData): void;
1664
1665
  exportForExcel(data: ExcelWorkbookData): void;
1666
+ private checkUselessSetFormatting;
1665
1667
  }
1666
1668
 
1667
1669
  interface TableState {
@@ -1790,29 +1792,17 @@ interface CommonChartDefinition {
1790
1792
  readonly humanize?: boolean;
1791
1793
  }
1792
1794
 
1793
- interface GeoChartDefinition {
1795
+ interface GeoChartDefinition extends CommonChartDefinition {
1794
1796
  readonly type: "geo";
1795
- readonly dataSets: CustomizedDataSet[];
1796
- readonly dataSetsHaveTitle: boolean;
1797
- readonly labelRange?: string;
1798
- readonly title: TitleDesign;
1799
- readonly background?: Color;
1800
- readonly legendPosition: LegendPosition;
1801
- readonly colorScale?: GeoChartColorScale;
1797
+ readonly colorScale?: ChartColorScale;
1802
1798
  readonly missingValueColor?: Color;
1803
1799
  readonly region?: string;
1804
- readonly humanize?: boolean;
1800
+ readonly showColorBar?: boolean;
1805
1801
  }
1806
1802
  type GeoChartRuntime = {
1807
1803
  chartJsConfig: ChartConfiguration;
1808
1804
  background: Color;
1809
1805
  };
1810
- interface GeoChartCustomColorScale {
1811
- minColor: Color;
1812
- midColor?: Color;
1813
- maxColor: Color;
1814
- }
1815
- type GeoChartColorScale = GeoChartCustomColorScale | "blues" | "cividis" | "greens" | "greys" | "oranges" | "purples" | "rainbow" | "reds" | "viridis";
1816
1806
  type GeoChartProjection = "azimuthalEqualArea" | "azimuthalEquidistant" | "gnomonic" | "orthographic" | "stereographic" | "equalEarth" | "albers" | "albersUsa" | "conicConformal" | "conicEqualArea" | "conicEquidistant" | "equirectangular" | "mercator" | "transverseMercator" | "naturalEarth1";
1817
1807
  interface GeoChartRegion {
1818
1808
  id: string;
@@ -3054,6 +3044,7 @@ type StatefulStream<Event, State> = {
3054
3044
  resetDefaultAnchor: (owner: unknown, state: State) => void;
3055
3045
  resetAnchor: (owner: unknown, state: State) => void;
3056
3046
  observe: (owner: unknown, callbacks: StreamCallbacks<Event>) => void;
3047
+ unobserve: (owner: unknown) => void;
3057
3048
  release: (owner: unknown) => void;
3058
3049
  getBackToDefault(): void;
3059
3050
  };
@@ -3484,12 +3475,16 @@ declare class UIOptionsPlugin extends UIPlugin {
3484
3475
  }
3485
3476
 
3486
3477
  declare class SheetUIPlugin extends UIPlugin {
3487
- static getters: readonly ["getCellWidth", "getTextWidth", "getCellText", "getCellMultiLineText", "getContiguousZone", "computeTextYCoordinate"];
3478
+ static getters: readonly ["getCellWidth", "getTextWidth", "getCellText", "getCellMultiLineText", "getMultilineTextSize", "getContiguousZone", "computeTextYCoordinate"];
3488
3479
  private ctx;
3489
3480
  allowDispatch(cmd: LocalCommand): CommandResult | CommandResult[];
3490
3481
  handle(cmd: Command): void;
3491
3482
  getCellWidth(position: CellPosition): number;
3492
3483
  getTextWidth(text: string, style: Style): Pixel;
3484
+ getMultilineTextSize(text: string[], style: Style): {
3485
+ width: number;
3486
+ height: number;
3487
+ };
3493
3488
  getCellText(position: CellPosition, args?: {
3494
3489
  showFormula?: boolean;
3495
3490
  availableWidth?: number;
@@ -3892,7 +3887,7 @@ declare class InternalViewport {
3892
3887
  *
3893
3888
  */
3894
3889
  declare class SheetViewPlugin extends UIPlugin {
3895
- static getters: readonly ["getColIndex", "getRowIndex", "getActiveMainViewport", "getSheetViewDimension", "getSheetViewDimensionWithHeaders", "getMainViewportRect", "isVisibleInViewport", "getEdgeScrollCol", "getEdgeScrollRow", "getVisibleFigures", "getVisibleRect", "getVisibleRectWithoutHeaders", "getVisibleRectWithZoom", "getVisibleCellPositions", "getColRowOffsetInViewport", "getMainViewportCoordinates", "getActiveSheetScrollInfo", "getSheetViewVisibleCols", "getSheetViewVisibleRows", "getFrozenSheetViewRatio", "isPixelPositionVisible", "getColDimensionsInViewport", "getRowDimensionsInViewport", "getAllActiveViewportsZonesAndRect", "getRect", "getFigureUI", "getPositionAnchorOffset", "getGridOffset", "getViewportZoomLevel", "getScrollBarWidth"];
3890
+ static getters: readonly ["getColIndex", "getRowIndex", "getActiveMainViewport", "getSheetViewDimension", "getSheetViewDimensionWithHeaders", "getMainViewportRect", "isVisibleInViewport", "getEdgeScrollCol", "getEdgeScrollRow", "getVisibleFigures", "getVisibleRect", "getVisibleRectWithoutHeaders", "getVisibleRectWithZoom", "getVisibleCellPositions", "getColRowOffsetInViewport", "getMainViewportCoordinates", "getActiveSheetScrollInfo", "getSheetViewVisibleCols", "getSheetViewVisibleRows", "getFrozenSheetViewRatio", "isPixelPositionVisible", "getColDimensionsInViewport", "getRowDimensionsInViewport", "getAllActiveViewportsZonesAndRect", "getRect", "getFigureUI", "getPositionAnchorOffset", "getGridOffset", "getViewportZoomLevel", "getScrollBarWidth", "getMaximumSheetOffset"];
3896
3891
  private viewports;
3897
3892
  /**
3898
3893
  * The viewport dimensions are usually set by one of the components
@@ -3944,7 +3939,10 @@ declare class SheetViewPlugin extends UIPlugin {
3944
3939
  * Return the main viewport maximum size relative to the client size.
3945
3940
  */
3946
3941
  getMainViewportRect(): Rect;
3947
- private getMaximumSheetOffset;
3942
+ getMaximumSheetOffset(): {
3943
+ maxOffsetX: Pixel;
3944
+ maxOffsetY: Pixel;
3945
+ };
3948
3946
  getColRowOffsetInViewport(dimension: Dimension, referenceHeaderIndex: HeaderIndex, targetHeaderIndex: HeaderIndex): Pixel;
3949
3947
  /**
3950
3948
  * Check if a given position is visible in the viewport.
@@ -4603,6 +4601,8 @@ type Rect = DOMCoordinates & DOMDimension;
4603
4601
  interface BoxTextContent {
4604
4602
  textLines: string[];
4605
4603
  width: Pixel;
4604
+ textHeight: Pixel;
4605
+ textWidth: Pixel;
4606
4606
  align: Align;
4607
4607
  fontSizePx: number;
4608
4608
  x: Pixel;
@@ -4752,6 +4752,16 @@ interface XLSXExport {
4752
4752
  */
4753
4753
  type XlsxHexColor = string & Alias;
4754
4754
 
4755
+ declare const CALENDAR_CHART_GRANULARITIES: Granularity[];
4756
+ type CalendarChartGranularity = (typeof CALENDAR_CHART_GRANULARITIES)[number];
4757
+ interface CalendarChartDefinition extends CommonChartDefinition {
4758
+ readonly type: "calendar";
4759
+ readonly colorScale?: ChartColorScale;
4760
+ readonly missingValueColor?: Color;
4761
+ readonly horizontalGroupBy?: CalendarChartGranularity;
4762
+ readonly verticalGroupBy?: CalendarChartGranularity;
4763
+ }
4764
+
4755
4765
  interface ComboChartDefinition extends CommonChartDefinition {
4756
4766
  readonly dataSets: ComboChartDataSet[];
4757
4767
  readonly type: "combo";
@@ -5106,9 +5116,9 @@ type WaterfallChartRuntime = {
5106
5116
  background: Color;
5107
5117
  };
5108
5118
 
5109
- declare const CHART_TYPES: readonly ["line", "bar", "pie", "scorecard", "gauge", "scatter", "combo", "waterfall", "pyramid", "radar", "geo", "funnel", "sunburst", "treemap"];
5119
+ declare const CHART_TYPES: readonly ["line", "bar", "pie", "scorecard", "gauge", "scatter", "combo", "waterfall", "pyramid", "radar", "geo", "funnel", "sunburst", "treemap", "calendar"];
5110
5120
  type ChartType = (typeof CHART_TYPES)[number];
5111
- type ChartDefinition = LineChartDefinition | PieChartDefinition | BarChartDefinition | ScorecardChartDefinition | GaugeChartDefinition | ScatterChartDefinition | ComboChartDefinition | WaterfallChartDefinition | PyramidChartDefinition | RadarChartDefinition | GeoChartDefinition | FunnelChartDefinition | SunburstChartDefinition | TreeMapChartDefinition;
5121
+ type ChartDefinition = LineChartDefinition | PieChartDefinition | BarChartDefinition | ScorecardChartDefinition | GaugeChartDefinition | ScatterChartDefinition | ComboChartDefinition | WaterfallChartDefinition | PyramidChartDefinition | RadarChartDefinition | GeoChartDefinition | FunnelChartDefinition | SunburstChartDefinition | TreeMapChartDefinition | CalendarChartDefinition;
5112
5122
  type ChartJSRuntime = LineChartRuntime | PieChartRuntime | BarChartRuntime | ComboChartRuntime | ScatterChartRuntime | WaterfallChartRuntime | PyramidChartRuntime | RadarChartRuntime | GeoChartRuntime | FunnelChartRuntime | SunburstChartRuntime | TreeMapChartRuntime;
5113
5123
  type ChartRuntime = ChartJSRuntime | ScorecardChartRuntime | GaugeChartRuntime;
5114
5124
  interface DatasetDesign {
@@ -5227,6 +5237,11 @@ interface ChartCreationContext {
5227
5237
  readonly zoomable?: boolean;
5228
5238
  readonly humanize?: boolean;
5229
5239
  }
5240
+ interface ChartColorScale {
5241
+ minColor: Color;
5242
+ midColor?: Color;
5243
+ maxColor: Color;
5244
+ }
5230
5245
 
5231
5246
  /**
5232
5247
  * There are two kinds of commands: CoreCommands and LocalCommands
@@ -6184,6 +6199,7 @@ interface Style {
6184
6199
  fillColor?: Color;
6185
6200
  textColor?: Color;
6186
6201
  fontSize?: number;
6202
+ rotation?: number;
6187
6203
  }
6188
6204
  interface DataBarFill {
6189
6205
  color: Color;
@@ -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
  class FunctionCodeBuilder {
@@ -634,6 +634,7 @@ const DEFAULT_STYLE = {
634
634
  fontSize: 10,
635
635
  fillColor: "",
636
636
  textColor: "",
637
+ rotation: 0,
637
638
  };
638
639
  const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
639
640
  // Fonts
@@ -679,7 +680,7 @@ const PIVOT_TABLE_CONFIG = {
679
680
  };
680
681
  const PIVOT_INDENT = 15;
681
682
  const PIVOT_COLLAPSE_ICON_SIZE = 12;
682
- const PIVOT_MAX_NUMBER_OF_CELLS = 1e5;
683
+ const PIVOT_MAX_NUMBER_OF_CELLS = 5e5;
683
684
 
684
685
  //------------------------------------------------------------------------------
685
686
  // Miscellaneous
@@ -3645,6 +3646,30 @@ var array = /*#__PURE__*/Object.freeze({
3645
3646
  WRAPROWS: WRAPROWS
3646
3647
  });
3647
3648
 
3649
+ const DEFAULT_LOCALES = [
3650
+ {
3651
+ name: "English (US)",
3652
+ code: "en_US",
3653
+ thousandsSeparator: ",",
3654
+ decimalSeparator: ".",
3655
+ weekStart: 7, // Sunday
3656
+ dateFormat: "m/d/yyyy",
3657
+ timeFormat: "hh:mm:ss a",
3658
+ formulaArgSeparator: ",",
3659
+ },
3660
+ {
3661
+ name: "French",
3662
+ code: "fr_FR",
3663
+ thousandsSeparator: " ",
3664
+ decimalSeparator: ",",
3665
+ weekStart: 1, // Monday
3666
+ dateFormat: "dd/mm/yyyy",
3667
+ timeFormat: "hh:mm:ss",
3668
+ formulaArgSeparator: ";",
3669
+ },
3670
+ ];
3671
+ const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
3672
+
3648
3673
  function tokenizeFormat(str) {
3649
3674
  const chars = new TokenizingChars(str);
3650
3675
  const result = [];
@@ -3929,7 +3954,7 @@ function parseNumberFormatTokens(tokens) {
3929
3954
  }
3930
3955
  function parseDateFormatTokens(tokens) {
3931
3956
  const internalFormat = tokens && areValidDateFormatTokens(tokens) ? { type: "date", tokens } : undefined;
3932
- if (!internalFormat) {
3957
+ if (!internalFormat || !tokens?.some((token) => token.type === "DATE_PART")) {
3933
3958
  return undefined;
3934
3959
  }
3935
3960
  if (internalFormat.tokens.length &&
@@ -4166,6 +4191,9 @@ function repeatCharToFitWidth(formattedValue, formatWidth) {
4166
4191
  return prefix + repeatedChar.repeat(timesToRepeat) + padding + suffix;
4167
4192
  }
4168
4193
  function applyInternalNumberFormat(value, format, locale) {
4194
+ if (!format.integerPart.length && !format.decimalPart?.length) {
4195
+ return "";
4196
+ }
4169
4197
  if (value === Infinity) {
4170
4198
  return "∞" + (format.percentSymbols ? "%" : "");
4171
4199
  }
@@ -4772,7 +4800,7 @@ function getTokenNextReferenceType(xc) {
4772
4800
  function setXcToFixedReferenceType(xc, referenceType) {
4773
4801
  let sheetName;
4774
4802
  ({ sheetName, xc } = splitReference(xc));
4775
- sheetName = sheetName ? sheetName + "!" : "";
4803
+ sheetName = sheetName ? getCanonicalSymbolName(sheetName) + "!" : "";
4776
4804
  xc = xc.replace(/\$/g, "");
4777
4805
  const splitIndex = xc.indexOf(":");
4778
4806
  if (splitIndex >= 0) {
@@ -7649,7 +7677,7 @@ const SUBTOTAL = {
7649
7677
  if (!acceptHiddenCells && this.getters.isRowHiddenByUser(sheetId, row))
7650
7678
  continue;
7651
7679
  for (let col = left; col <= right; col++) {
7652
- const cell = this.getters.getCell({ sheetId, col, row });
7680
+ const cell = this.getters.getCorrespondingFormulaCell({ sheetId, col, row });
7653
7681
  if (!cell || !isSubtotalCell(cell)) {
7654
7682
  evaluatedCellToKeep.push(this.getters.getEvaluatedCell({ sheetId, col, row }));
7655
7683
  }
@@ -9544,30 +9572,6 @@ var database = /*#__PURE__*/Object.freeze({
9544
9572
  DVARP: DVARP
9545
9573
  });
9546
9574
 
9547
- const DEFAULT_LOCALES = [
9548
- {
9549
- name: "English (US)",
9550
- code: "en_US",
9551
- thousandsSeparator: ",",
9552
- decimalSeparator: ".",
9553
- weekStart: 7, // Sunday
9554
- dateFormat: "m/d/yyyy",
9555
- timeFormat: "hh:mm:ss a",
9556
- formulaArgSeparator: ",",
9557
- },
9558
- {
9559
- name: "French",
9560
- code: "fr_FR",
9561
- thousandsSeparator: " ",
9562
- decimalSeparator: ",",
9563
- weekStart: 1, // Monday
9564
- dateFormat: "dd/mm/yyyy",
9565
- timeFormat: "hh:mm:ss",
9566
- formulaArgSeparator: ";",
9567
- },
9568
- ];
9569
- const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
9570
-
9571
9575
  /**
9572
9576
  * Tokenizer
9573
9577
  *
@@ -13972,18 +13976,17 @@ var logical = /*#__PURE__*/Object.freeze({
13972
13976
  ["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
13973
13977
  ["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
13974
13978
  },
13975
- GeoChart: {
13976
- ColorScales: {
13977
- blues: _t("Blues"),
13978
- cividis: _t("Cividis"),
13979
- greens: _t("Greens"),
13980
- greys: _t("Greys"),
13981
- oranges: _t("Oranges"),
13982
- purples: _t("Purples"),
13983
- rainbow: _t("Rainbow"),
13984
- reds: _t("Reds"),
13985
- viridis: _t("Viridis"),
13986
- },
13979
+ ColorScales: {
13980
+ blues: _t("Blues"),
13981
+ cividis: _t("Cividis"),
13982
+ custom: _t("Custom"),
13983
+ greens: _t("Greens"),
13984
+ greys: _t("Greys"),
13985
+ oranges: _t("Oranges"),
13986
+ purples: _t("Purples"),
13987
+ rainbow: _t("Rainbow"),
13988
+ reds: _t("Reds"),
13989
+ viridis: _t("Viridis"),
13987
13990
  },
13988
13991
  });
13989
13992
  ({
@@ -18818,6 +18821,17 @@ class AlternatingColorMap {
18818
18821
  return this.colors[id];
18819
18822
  }
18820
18823
  }
18824
+ const COLORSCHEMES = {
18825
+ greys: ["#ffffff", "#808080", "#000000"],
18826
+ blues: ["#f7fbff", "#6aaed6", "#08306b"],
18827
+ reds: ["#fff5f0", "#fb694a", "#67000d"],
18828
+ greens: ["#f7fcf5", "#73c476", "#00441b"],
18829
+ oranges: ["#fff5eb", "#fd8c3b", "#7f2704"],
18830
+ purples: ["#fcfbfd", "#9e9ac8", "#3f007d"],
18831
+ viridis: ["#440154", "#21918c", "#fde725"],
18832
+ cividis: ["#00224e", "#7d7c78", "#fee838"],
18833
+ rainbow: ["#B41DB4", "#FFFF00", "#00FFFF"],
18834
+ };
18821
18835
  /**
18822
18836
  * Returns a function that maps a value to a color using a color scale defined by the given
18823
18837
  * color/threshold values pairs.
@@ -26513,6 +26527,20 @@ migrationStepRegistry
26513
26527
  }
26514
26528
  return data;
26515
26529
  },
26530
+ })
26531
+ .add("19.2", {
26532
+ migrate(data) {
26533
+ for (const sheet of data.sheets || []) {
26534
+ for (const figure of sheet.figures || []) {
26535
+ if (figure.tag === "chart" && figure.data.type === "geo") {
26536
+ if ("colorScale" in figure.data && typeof figure.data.colorScale === "string") {
26537
+ figure.data.colorScale = COLORSCHEMES[figure.data.colorScale];
26538
+ }
26539
+ }
26540
+ }
26541
+ }
26542
+ return data;
26543
+ },
26516
26544
  });
26517
26545
  function fixOverlappingFilters(data) {
26518
26546
  for (const sheet of data.sheets || []) {
@@ -27313,22 +27341,34 @@ class BordersPlugin extends CorePlugin {
27313
27341
  addBorder(sheetId, zone, newBorder, force = false) {
27314
27342
  const borders = [];
27315
27343
  const plannedBorder = newBorder ? { zone, style: newBorder } : undefined;
27316
- const sideToClear = {
27317
- left: force || !!newBorder?.left,
27318
- right: force || !!newBorder?.right,
27319
- top: force || !!newBorder?.top,
27320
- bottom: force || !!newBorder?.bottom,
27344
+ // For each side, decide if we must clear the border on the *adjacent*
27345
+ // existing cell when we draw on the opposite side of the new zone.
27346
+ //
27347
+ // Example:
27348
+ // - newBorder.right is set → we draw border on the RIGHT side of `zone`
27349
+ // - the cell on the right may already have a LEFT border on that edge
27350
+ // In that case we clear that LEFT border, so only the new RIGHT border
27351
+ // remains on the shared edge.
27352
+ //
27353
+ // existingBorderSideToClear[side] = true means we should clear the border on that
27354
+ // side of the existing adjacent zone before adding the new border.
27355
+ const existingBorderSideToClear = {
27356
+ left: force || !!newBorder?.right,
27357
+ right: force || !!newBorder?.left,
27358
+ top: force || !!newBorder?.bottom,
27359
+ bottom: force || !!newBorder?.top,
27321
27360
  };
27322
27361
  let editingZone = [zone];
27323
27362
  for (const existingBorder of this.borders[sheetId] ?? []) {
27324
27363
  const inter = intersection(existingBorder.zone, zone);
27325
27364
  if (!inter) {
27326
- // Clear adjacent borders on which you write
27365
+ // Check if the existing border is adjacent to the new zone
27327
27366
  const adjacentEdge = adjacent(existingBorder.zone, zone);
27328
- if (adjacentEdge && sideToClear[adjacentEdge.position]) {
27367
+ if (adjacentEdge && existingBorderSideToClear[adjacentEdge.position]) {
27329
27368
  for (const newZone of splitIfAdjacent(existingBorder.zone, zone)) {
27330
27369
  const border = this.computeBorderFromZone(newZone, existingBorder);
27331
27370
  const adjacentEdge = adjacent(newZone, zone);
27371
+ // Clear the existing border on the side that touches the new zone
27332
27372
  switch (adjacentEdge?.position) {
27333
27373
  case "left":
27334
27374
  border.style.left = undefined;
@@ -33215,6 +33255,13 @@ class StylePlugin extends CorePlugin {
33215
33255
  "getStyleColors",
33216
33256
  ];
33217
33257
  styles = {};
33258
+ allowDispatch(cmd) {
33259
+ switch (cmd.type) {
33260
+ case "SET_FORMATTING":
33261
+ return this.checkUselessSetFormatting(cmd);
33262
+ }
33263
+ return "Success" /* CommandResult.Success */;
33264
+ }
33218
33265
  handle(cmd) {
33219
33266
  switch (cmd.type) {
33220
33267
  case "ADD_MERGE":
@@ -33439,6 +33486,28 @@ class StylePlugin extends CorePlugin {
33439
33486
  exportForExcel(data) {
33440
33487
  this.export(data);
33441
33488
  }
33489
+ checkUselessSetFormatting(cmd) {
33490
+ const { sheetId, target } = cmd;
33491
+ const hasStyle = "style" in cmd;
33492
+ const hasFormat = "format" in cmd;
33493
+ if (!hasStyle && !hasFormat) {
33494
+ return "NoChanges" /* CommandResult.NoChanges */;
33495
+ }
33496
+ for (const zone of recomputeZones(target)) {
33497
+ for (let col = zone.left; col <= zone.right; col++) {
33498
+ for (let row = zone.top; row <= zone.bottom; row++) {
33499
+ const position = { sheetId, col, row };
33500
+ const cell = this.getters.getCell(position);
33501
+ const style = this.getCellStyle(position);
33502
+ if ((hasStyle && !deepEquals(style, cmd.style)) ||
33503
+ (hasFormat && cell?.format !== cmd.format)) {
33504
+ return "Success" /* CommandResult.Success */;
33505
+ }
33506
+ }
33507
+ }
33508
+ }
33509
+ return "NoChanges" /* CommandResult.NoChanges */;
33510
+ }
33442
33511
  }
33443
33512
 
33444
33513
  class TableStylePlugin extends CorePlugin {
@@ -36466,48 +36535,64 @@ function getDefaultCellHeight(ctx, cell, style, colSize) {
36466
36535
  }
36467
36536
  function getCellContentHeight(ctx, content, style, colSize) {
36468
36537
  const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
36469
- const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
36470
- const fontSize = computeTextFontSizeInPixels(style);
36471
- return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
36538
+ const lines = splitTextToWidth(ctx, content, style, maxWidth);
36539
+ return computeMultilineTextSize(ctx, lines, style).height + 2 * PADDING_AUTORESIZE_VERTICAL;
36472
36540
  }
36473
36541
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
36474
36542
  const italicStr = italic ? "italic" : "";
36475
36543
  const weight = bold ? "bold" : "";
36476
36544
  return `${italicStr} ${weight} ${fontSize}px ${DEFAULT_FONT}`;
36477
36545
  }
36478
- const textWidthCache = {};
36479
- function computeTextWidth(context, text, style, fontUnit = "pt") {
36546
+ function computeMultilineTextSize(context, textLines, style = {}, fontUnit = "pt") {
36547
+ if (!textLines.length)
36548
+ return { width: 0, height: 0 };
36480
36549
  const font = computeTextFont(style, fontUnit);
36481
- return computeCachedTextWidth(context, text, font);
36482
- }
36483
- function computeCachedTextWidth(context, text, font) {
36484
- if (!textWidthCache[font]) {
36485
- textWidthCache[font] = {};
36550
+ const sizes = textLines.map((line) => computeCachedTextDimension(context, line, font));
36551
+ const height = computeTextLinesHeight(sizes[0].height, textLines.length);
36552
+ const width = Math.max(...sizes.map((size) => size.width));
36553
+ if (!style.rotation) {
36554
+ return { height, width };
36486
36555
  }
36487
- if (textWidthCache[font][text] === undefined) {
36488
- const oldFont = context.font;
36489
- context.font = font;
36490
- textWidthCache[font][text] = context.measureText(text).width;
36491
- context.font = oldFont;
36556
+ const cos = Math.abs(Math.cos(style.rotation));
36557
+ const sin = Math.abs(Math.sin(style.rotation));
36558
+ return { width: width * cos + height * sin, height: sin * width + cos * height };
36559
+ }
36560
+ function computeTextWidth(context, text, style = {}, fontUnit = "pt") {
36561
+ const font = computeTextFont(style, fontUnit);
36562
+ return computeCachedTextWidth(context, text, font, style.rotation);
36563
+ }
36564
+ function computeCachedTextWidth(context, text, font, rotation) {
36565
+ const size = computeCachedTextDimension(context, text, font);
36566
+ if (!rotation) {
36567
+ return size.width;
36492
36568
  }
36493
- return textWidthCache[font][text];
36569
+ const cos = Math.abs(Math.cos(rotation));
36570
+ const sin = Math.abs(Math.sin(rotation));
36571
+ return size.width * cos + size.height * sin;
36494
36572
  }
36495
36573
  const textDimensionsCache = {};
36496
36574
  function computeTextDimension(context, text, style, fontUnit = "pt") {
36497
36575
  const font = computeTextFont(style, fontUnit);
36498
- context.save();
36499
- context.font = font;
36500
- const dimensions = computeCachedTextDimension(context, text);
36501
- context.restore();
36502
- return dimensions;
36503
- }
36504
- function computeCachedTextDimension(context, text) {
36505
- const font = context.font;
36576
+ const size = computeCachedTextDimension(context, text, font);
36577
+ if (!style.rotation) {
36578
+ return size;
36579
+ }
36580
+ const cos = Math.abs(Math.cos(style.rotation));
36581
+ const sin = Math.abs(Math.sin(style.rotation));
36582
+ return {
36583
+ width: size.width * cos + size.height * sin,
36584
+ height: size.height * cos + size.width * sin,
36585
+ };
36586
+ }
36587
+ function computeCachedTextDimension(context, text, font) {
36506
36588
  if (!textDimensionsCache[font]) {
36507
36589
  textDimensionsCache[font] = {};
36508
36590
  }
36509
36591
  if (textDimensionsCache[font][text] === undefined) {
36592
+ context.save();
36593
+ context.font = font;
36510
36594
  const measure = context.measureText(text);
36595
+ context.restore();
36511
36596
  const width = measure.width;
36512
36597
  const height = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
36513
36598
  textDimensionsCache[font][text] = { width, height };
@@ -38704,7 +38789,8 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
38704
38789
  }
38705
38790
  break;
38706
38791
  case "SET_FORMATTING":
38707
- if (cmd.style && ("fontSize" in cmd.style || "wrapping" in cmd.style)) {
38792
+ if (cmd.style &&
38793
+ ("fontSize" in cmd.style || "wrapping" in cmd.style || "rotation" in cmd.style)) {
38708
38794
  for (const zone of cmd.target) {
38709
38795
  // TODO FLDA use rangeSet
38710
38796
  this.updateRowSizeForZoneChange(cmd.sheetId, zone);
@@ -43481,6 +43567,7 @@ class SheetUIPlugin extends UIPlugin {
43481
43567
  "getTextWidth",
43482
43568
  "getCellText",
43483
43569
  "getCellMultiLineText",
43570
+ "getMultilineTextSize",
43484
43571
  "getContiguousZone",
43485
43572
  "computeTextYCoordinate",
43486
43573
  ];
@@ -43531,7 +43618,7 @@ class SheetUIPlugin extends UIPlugin {
43531
43618
  const content = this.getters.getEvaluatedCell(position).formattedValue;
43532
43619
  if (content) {
43533
43620
  const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
43534
- contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
43621
+ contentWidth += computeMultilineTextSize(this.ctx, multiLineText, style).width;
43535
43622
  }
43536
43623
  for (const icon of this.getters.getCellIcons(position)) {
43537
43624
  contentWidth += icon.margin + icon.size;
@@ -43552,6 +43639,9 @@ class SheetUIPlugin extends UIPlugin {
43552
43639
  getTextWidth(text, style) {
43553
43640
  return computeTextWidth(this.ctx, text, style);
43554
43641
  }
43642
+ getMultilineTextSize(text, style) {
43643
+ return computeMultilineTextSize(this.ctx, text, style);
43644
+ }
43555
43645
  getCellText(position, args) {
43556
43646
  const cell = this.getters.getCell(position);
43557
43647
  const locale = this.getters.getLocale();
@@ -46135,6 +46225,7 @@ class SheetViewPlugin extends UIPlugin {
46135
46225
  "getGridOffset",
46136
46226
  "getViewportZoomLevel",
46137
46227
  "getScrollBarWidth",
46228
+ "getMaximumSheetOffset",
46138
46229
  ];
46139
46230
  viewports = {};
46140
46231
  /**
@@ -47240,6 +47331,9 @@ class EventStream {
47240
47331
  observe(owner, callbacks) {
47241
47332
  this.observers.set(owner, { owner, callbacks });
47242
47333
  }
47334
+ unobserve(owner) {
47335
+ this.observers.delete(owner);
47336
+ }
47243
47337
  /**
47244
47338
  * Capture the stream for yourself
47245
47339
  */
@@ -47332,6 +47426,9 @@ class SelectionStreamProcessorImpl {
47332
47426
  observe(owner, callbacks) {
47333
47427
  this.stream.observe(owner, callbacks);
47334
47428
  }
47429
+ unobserve(owner) {
47430
+ this.stream.unobserve(owner);
47431
+ }
47335
47432
  release(owner) {
47336
47433
  if (this.stream.isListening(owner)) {
47337
47434
  this.stream.release(owner);
@@ -50816,5 +50913,5 @@ export { BadExpressionError, BasePlugin, CellErrorType, CircularDependencyError,
50816
50913
 
50817
50914
 
50818
50915
  __info__.version = "19.1.0-alpha.3";
50819
- __info__.date = "2025-11-12T14:16:26.552Z";
50820
- __info__.hash = "6fefc9c";
50916
+ __info__.date = "2025-11-24T07:52:34.809Z";
50917
+ __info__.hash = "e232982";