@odoo/o-spreadsheet 19.1.0-alpha.11 → 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-03T12:33:46.742Z
7
- * @hash d9230f3
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) {
@@ -5762,7 +5790,10 @@ function isInside(col, row, zone) {
5762
5790
  * Check if a zone is inside another
5763
5791
  */
5764
5792
  function isZoneInside(smallZone, biggerZone) {
5765
- return isEqual(union(biggerZone, smallZone), biggerZone);
5793
+ return (smallZone.left >= biggerZone.left &&
5794
+ smallZone.right <= biggerZone.right &&
5795
+ smallZone.top >= biggerZone.top &&
5796
+ smallZone.bottom <= biggerZone.bottom);
5766
5797
  }
5767
5798
  function zoneToDimension(zone) {
5768
5799
  return {
@@ -7646,7 +7677,7 @@ const SUBTOTAL = {
7646
7677
  if (!acceptHiddenCells && this.getters.isRowHiddenByUser(sheetId, row))
7647
7678
  continue;
7648
7679
  for (let col = left; col <= right; col++) {
7649
- const cell = this.getters.getCell({ sheetId, col, row });
7680
+ const cell = this.getters.getCorrespondingFormulaCell({ sheetId, col, row });
7650
7681
  if (!cell || !isSubtotalCell(cell)) {
7651
7682
  evaluatedCellToKeep.push(this.getters.getEvaluatedCell({ sheetId, col, row }));
7652
7683
  }
@@ -9541,30 +9572,6 @@ var database = /*#__PURE__*/Object.freeze({
9541
9572
  DVARP: DVARP
9542
9573
  });
9543
9574
 
9544
- const DEFAULT_LOCALES = [
9545
- {
9546
- name: "English (US)",
9547
- code: "en_US",
9548
- thousandsSeparator: ",",
9549
- decimalSeparator: ".",
9550
- weekStart: 7, // Sunday
9551
- dateFormat: "m/d/yyyy",
9552
- timeFormat: "hh:mm:ss a",
9553
- formulaArgSeparator: ",",
9554
- },
9555
- {
9556
- name: "French",
9557
- code: "fr_FR",
9558
- thousandsSeparator: " ",
9559
- decimalSeparator: ",",
9560
- weekStart: 1, // Monday
9561
- dateFormat: "dd/mm/yyyy",
9562
- timeFormat: "hh:mm:ss",
9563
- formulaArgSeparator: ";",
9564
- },
9565
- ];
9566
- const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
9567
-
9568
9575
  /**
9569
9576
  * Tokenizer
9570
9577
  *
@@ -13969,18 +13976,17 @@ var logical = /*#__PURE__*/Object.freeze({
13969
13976
  ["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
13970
13977
  ["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
13971
13978
  },
13972
- GeoChart: {
13973
- ColorScales: {
13974
- blues: _t("Blues"),
13975
- cividis: _t("Cividis"),
13976
- greens: _t("Greens"),
13977
- greys: _t("Greys"),
13978
- oranges: _t("Oranges"),
13979
- purples: _t("Purples"),
13980
- rainbow: _t("Rainbow"),
13981
- reds: _t("Reds"),
13982
- viridis: _t("Viridis"),
13983
- },
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"),
13984
13990
  },
13985
13991
  });
13986
13992
  ({
@@ -17906,7 +17912,7 @@ function getRangeString(range, forSheetId, getSheetName, options = { useBoundedR
17906
17912
  let sheetName = "";
17907
17913
  if (prefixSheet) {
17908
17914
  if (range.invalidSheetName) {
17909
- sheetName = range.invalidSheetName;
17915
+ sheetName = getCanonicalSymbolName(range.invalidSheetName);
17910
17916
  }
17911
17917
  else {
17912
17918
  sheetName = getCanonicalSymbolName(getSheetName(range.sheetId));
@@ -18815,6 +18821,17 @@ class AlternatingColorMap {
18815
18821
  return this.colors[id];
18816
18822
  }
18817
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
+ };
18818
18835
  /**
18819
18836
  * Returns a function that maps a value to a color using a color scale defined by the given
18820
18837
  * color/threshold values pairs.
@@ -19732,8 +19749,7 @@ function generateMasterChartConfig(chartJsConfig) {
19732
19749
  .filter((ds) => !isTrendLineAxis(ds["xAxisID"]))
19733
19750
  .map((ds) => ({
19734
19751
  ...ds,
19735
- pointRadius: 0,
19736
- showLine: true,
19752
+ pointRadius: ds.showLine === false ? 2 : 0, // Show points only for scatter plots
19737
19753
  })),
19738
19754
  },
19739
19755
  options: {
@@ -26511,6 +26527,20 @@ migrationStepRegistry
26511
26527
  }
26512
26528
  return data;
26513
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
+ },
26514
26544
  });
26515
26545
  function fixOverlappingFilters(data) {
26516
26546
  for (const sheet of data.sheets || []) {
@@ -27311,22 +27341,34 @@ class BordersPlugin extends CorePlugin {
27311
27341
  addBorder(sheetId, zone, newBorder, force = false) {
27312
27342
  const borders = [];
27313
27343
  const plannedBorder = newBorder ? { zone, style: newBorder } : undefined;
27314
- const sideToClear = {
27315
- left: force || !!newBorder?.left,
27316
- right: force || !!newBorder?.right,
27317
- top: force || !!newBorder?.top,
27318
- 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,
27319
27360
  };
27320
27361
  let editingZone = [zone];
27321
27362
  for (const existingBorder of this.borders[sheetId] ?? []) {
27322
27363
  const inter = intersection(existingBorder.zone, zone);
27323
27364
  if (!inter) {
27324
- // Clear adjacent borders on which you write
27365
+ // Check if the existing border is adjacent to the new zone
27325
27366
  const adjacentEdge = adjacent(existingBorder.zone, zone);
27326
- if (adjacentEdge && sideToClear[adjacentEdge.position]) {
27367
+ if (adjacentEdge && existingBorderSideToClear[adjacentEdge.position]) {
27327
27368
  for (const newZone of splitIfAdjacent(existingBorder.zone, zone)) {
27328
27369
  const border = this.computeBorderFromZone(newZone, existingBorder);
27329
27370
  const adjacentEdge = adjacent(newZone, zone);
27371
+ // Clear the existing border on the side that touches the new zone
27330
27372
  switch (adjacentEdge?.position) {
27331
27373
  case "left":
27332
27374
  border.style.left = undefined;
@@ -33213,6 +33255,13 @@ class StylePlugin extends CorePlugin {
33213
33255
  "getStyleColors",
33214
33256
  ];
33215
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
+ }
33216
33265
  handle(cmd) {
33217
33266
  switch (cmd.type) {
33218
33267
  case "ADD_MERGE":
@@ -33437,6 +33486,28 @@ class StylePlugin extends CorePlugin {
33437
33486
  exportForExcel(data) {
33438
33487
  this.export(data);
33439
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
+ }
33440
33511
  }
33441
33512
 
33442
33513
  class TableStylePlugin extends CorePlugin {
@@ -36464,48 +36535,64 @@ function getDefaultCellHeight(ctx, cell, style, colSize) {
36464
36535
  }
36465
36536
  function getCellContentHeight(ctx, content, style, colSize) {
36466
36537
  const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
36467
- const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
36468
- const fontSize = computeTextFontSizeInPixels(style);
36469
- 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;
36470
36540
  }
36471
36541
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
36472
36542
  const italicStr = italic ? "italic" : "";
36473
36543
  const weight = bold ? "bold" : "";
36474
36544
  return `${italicStr} ${weight} ${fontSize}px ${DEFAULT_FONT}`;
36475
36545
  }
36476
- const textWidthCache = {};
36477
- 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 };
36478
36549
  const font = computeTextFont(style, fontUnit);
36479
- return computeCachedTextWidth(context, text, font);
36480
- }
36481
- function computeCachedTextWidth(context, text, font) {
36482
- if (!textWidthCache[font]) {
36483
- 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 };
36484
36555
  }
36485
- if (textWidthCache[font][text] === undefined) {
36486
- const oldFont = context.font;
36487
- context.font = font;
36488
- textWidthCache[font][text] = context.measureText(text).width;
36489
- 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;
36490
36568
  }
36491
- 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;
36492
36572
  }
36493
36573
  const textDimensionsCache = {};
36494
36574
  function computeTextDimension(context, text, style, fontUnit = "pt") {
36495
36575
  const font = computeTextFont(style, fontUnit);
36496
- context.save();
36497
- context.font = font;
36498
- const dimensions = computeCachedTextDimension(context, text);
36499
- context.restore();
36500
- return dimensions;
36501
- }
36502
- function computeCachedTextDimension(context, text) {
36503
- 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) {
36504
36588
  if (!textDimensionsCache[font]) {
36505
36589
  textDimensionsCache[font] = {};
36506
36590
  }
36507
36591
  if (textDimensionsCache[font][text] === undefined) {
36592
+ context.save();
36593
+ context.font = font;
36508
36594
  const measure = context.measureText(text);
36595
+ context.restore();
36509
36596
  const width = measure.width;
36510
36597
  const height = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
36511
36598
  textDimensionsCache[font][text] = { width, height };
@@ -38048,9 +38135,13 @@ async function canvasToObjectUrl(canvas) {
38048
38135
  if (!blob) {
38049
38136
  return undefined;
38050
38137
  }
38051
- if (!URL.createObjectURL)
38052
- throw new Error("URL.createObjectURL is not supported in this environment");
38053
- return URL.createObjectURL(blob);
38138
+ return new Promise((resolve) => {
38139
+ const f = new FileReader();
38140
+ f.addEventListener("load", () => {
38141
+ resolve(f.result);
38142
+ });
38143
+ f.readAsDataURL(blob);
38144
+ });
38054
38145
  }
38055
38146
 
38056
38147
  class EvaluationChartPlugin extends CoreViewPlugin {
@@ -38698,7 +38789,8 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
38698
38789
  }
38699
38790
  break;
38700
38791
  case "SET_FORMATTING":
38701
- 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)) {
38702
38794
  for (const zone of cmd.target) {
38703
38795
  // TODO FLDA use rangeSet
38704
38796
  this.updateRowSizeForZoneChange(cmd.sheetId, zone);
@@ -43475,6 +43567,7 @@ class SheetUIPlugin extends UIPlugin {
43475
43567
  "getTextWidth",
43476
43568
  "getCellText",
43477
43569
  "getCellMultiLineText",
43570
+ "getMultilineTextSize",
43478
43571
  "getContiguousZone",
43479
43572
  "computeTextYCoordinate",
43480
43573
  ];
@@ -43525,7 +43618,7 @@ class SheetUIPlugin extends UIPlugin {
43525
43618
  const content = this.getters.getEvaluatedCell(position).formattedValue;
43526
43619
  if (content) {
43527
43620
  const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
43528
- contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
43621
+ contentWidth += computeMultilineTextSize(this.ctx, multiLineText, style).width;
43529
43622
  }
43530
43623
  for (const icon of this.getters.getCellIcons(position)) {
43531
43624
  contentWidth += icon.margin + icon.size;
@@ -43546,6 +43639,9 @@ class SheetUIPlugin extends UIPlugin {
43546
43639
  getTextWidth(text, style) {
43547
43640
  return computeTextWidth(this.ctx, text, style);
43548
43641
  }
43642
+ getMultilineTextSize(text, style) {
43643
+ return computeMultilineTextSize(this.ctx, text, style);
43644
+ }
43549
43645
  getCellText(position, args) {
43550
43646
  const cell = this.getters.getCell(position);
43551
43647
  const locale = this.getters.getLocale();
@@ -46129,6 +46225,7 @@ class SheetViewPlugin extends UIPlugin {
46129
46225
  "getGridOffset",
46130
46226
  "getViewportZoomLevel",
46131
46227
  "getScrollBarWidth",
46228
+ "getMaximumSheetOffset",
46132
46229
  ];
46133
46230
  viewports = {};
46134
46231
  /**
@@ -47234,6 +47331,9 @@ class EventStream {
47234
47331
  observe(owner, callbacks) {
47235
47332
  this.observers.set(owner, { owner, callbacks });
47236
47333
  }
47334
+ unobserve(owner) {
47335
+ this.observers.delete(owner);
47336
+ }
47237
47337
  /**
47238
47338
  * Capture the stream for yourself
47239
47339
  */
@@ -47326,6 +47426,9 @@ class SelectionStreamProcessorImpl {
47326
47426
  observe(owner, callbacks) {
47327
47427
  this.stream.observe(owner, callbacks);
47328
47428
  }
47429
+ unobserve(owner) {
47430
+ this.stream.unobserve(owner);
47431
+ }
47329
47432
  release(owner) {
47330
47433
  if (this.stream.isListening(owner)) {
47331
47434
  this.stream.release(owner);
@@ -50810,5 +50913,5 @@ export { BadExpressionError, BasePlugin, CellErrorType, CircularDependencyError,
50810
50913
 
50811
50914
 
50812
50915
  __info__.version = "19.1.0-alpha.3";
50813
- __info__.date = "2025-11-03T12:33:46.742Z";
50814
- __info__.hash = "d9230f3";
50916
+ __info__.date = "2025-11-24T07:52:34.809Z";
50917
+ __info__.hash = "e232982";