@odoo/o-spreadsheet 19.1.0-alpha.2 → 19.1.0-alpha.4

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 19.1.0-alpha.2
6
- * @date 2025-09-19T07:26:49.306Z
7
- * @hash 8cc543d
5
+ * @version 19.1.0-alpha.4
6
+ * @date 2025-10-07T10:03:20.917Z
7
+ * @hash b7cfed8
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -591,7 +591,6 @@ const BACKGROUND_HEADER_COLOR = "#F8F9FA";
591
591
  const BACKGROUND_HEADER_SELECTED_COLOR = "#E8EAED";
592
592
  const BACKGROUND_HEADER_ACTIVE_COLOR = "#595959";
593
593
  const TEXT_HEADER_COLOR = "#666666";
594
- const FIGURE_BORDER_COLOR = "#c9ccd2";
595
594
  const SELECTION_BORDER_COLOR = "#3266ca";
596
595
  const HEADER_BORDER_COLOR = "#C0C0C0";
597
596
  const CELL_BORDER_COLOR = "#E2E3E3";
@@ -599,7 +598,6 @@ const BACKGROUND_CHART_COLOR = "#FFFFFF";
599
598
  const DEFAULT_COLOR_SCALE_MIDPOINT_COLOR = 0xb6d7a8;
600
599
  const LINK_COLOR = HIGHLIGHT_COLOR;
601
600
  const FILTERS_COLOR = "#188038";
602
- const HEADER_GROUPING_BORDER_COLOR = "#999";
603
601
  const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
604
602
  const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
605
603
  const COMPOSER_ASSISTANT_COLOR = "#9B359B";
@@ -842,22 +840,6 @@ const DRAG_THRESHOLD = 5; // in pixels, to avoid unwanted drag when clicking
842
840
  // Miscellaneous
843
841
  //------------------------------------------------------------------------------
844
842
  const sanitizeSheetNameRegex = new RegExp(FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX, "g");
845
- /**
846
- * Remove quotes from a quoted string
847
- * ```js
848
- * removeStringQuotes('"Hello"')
849
- * > 'Hello'
850
- * ```
851
- */
852
- function removeStringQuotes(str) {
853
- if (str[0] === '"') {
854
- str = str.slice(1);
855
- }
856
- if (str[str.length - 1] === '"' && str[str.length - 2] !== "\\") {
857
- return str.slice(0, str.length - 1);
858
- }
859
- return str;
860
- }
861
843
  function isCloneable(obj) {
862
844
  return "clone" in obj && obj.clone instanceof Function;
863
845
  }
@@ -926,6 +908,13 @@ function isPlainObject(obj) {
926
908
  function getUnquotedSheetName(sheetName) {
927
909
  return unquote(sheetName, "'");
928
910
  }
911
+ /**
912
+ * Remove quotes from a quoted string
913
+ * ```js
914
+ * unquote('"Hello"')
915
+ * > 'Hello'
916
+ * ```
917
+ */
929
918
  function unquote(string, quoteChar = '"') {
930
919
  if (string.startsWith(quoteChar)) {
931
920
  string = string.slice(1);
@@ -1330,9 +1319,7 @@ function removeIndexesFromArray(array, indexes) {
1330
1319
  return newArray;
1331
1320
  }
1332
1321
  function insertItemsAtIndex(array, items, index) {
1333
- const newArray = [...array];
1334
- newArray.splice(index, 0, ...items);
1335
- return newArray;
1322
+ return array.slice(0, index).concat(items).concat(array.slice(index));
1336
1323
  }
1337
1324
  function replaceItemAtIndex(array, newItem, index) {
1338
1325
  const newArray = [...array];
@@ -5582,7 +5569,7 @@ function tokensToTextInternalFormat(tokens) {
5582
5569
  * Replace in place tokens "mm" and "m" that denote minutes in date format with "MM" to avoid confusion with months.
5583
5570
  *
5584
5571
  * As per OpenXML specification, in date formats if a date token "m" or "mm" is followed by a date token "s" or
5585
- * preceded by a data token "h", then it's not a month but an minute.
5572
+ * preceded by a data token "h", then it's not a month but a minute.
5586
5573
  */
5587
5574
  function convertTokensToMinutesInDateFormat(tokens) {
5588
5575
  const dateParts = tokens.filter((token) => token.type === "DATE_PART");
@@ -5625,6 +5612,9 @@ function internalFormatPartToFormat(internalFormat) {
5625
5612
  case "REPEATED_CHAR":
5626
5613
  format += "*" + token.value;
5627
5614
  break;
5615
+ case "DATE_PART":
5616
+ format += token.value === "MM" ? "mm" : token.value; // Convert "MM" back to "mm" for minutes
5617
+ break;
5628
5618
  default:
5629
5619
  format += token.value;
5630
5620
  }
@@ -5698,7 +5688,7 @@ function formatValue(value, { format, locale, formatWidth }) {
5698
5688
  return value;
5699
5689
  }
5700
5690
  const internalFormat = parseFormat(format);
5701
- const formatToApply = internalFormat.text || internalFormat.positive;
5691
+ const formatToApply = getFormatToApply(value, internalFormat).format;
5702
5692
  if (!formatToApply || formatToApply.type !== "text") {
5703
5693
  return value;
5704
5694
  }
@@ -5709,16 +5699,15 @@ function formatValue(value, { format, locale, formatWidth }) {
5709
5699
  format = createDefaultFormat(value);
5710
5700
  }
5711
5701
  const internalFormat = parseFormat(format);
5712
- if (internalFormat.positive.type === "text") {
5713
- return applyTextInternalFormat(value.toString(), internalFormat.positive, formatWidth);
5702
+ const { format: formatToApply, isNegativeFormat } = getFormatToApply(value, internalFormat);
5703
+ if (!formatToApply) {
5704
+ return value.toString();
5714
5705
  }
5715
- let formatToApply = internalFormat.positive;
5716
- if (value < 0 && internalFormat.negative) {
5717
- formatToApply = internalFormat.negative;
5718
- value = -value;
5706
+ if (formatToApply.type === "text") {
5707
+ return applyTextInternalFormat(value.toString(), formatToApply, formatWidth);
5719
5708
  }
5720
- else if (value === 0 && internalFormat.zero) {
5721
- formatToApply = internalFormat.zero;
5709
+ if (isNegativeFormat) {
5710
+ value = Math.abs(value);
5722
5711
  }
5723
5712
  if (formatToApply.type === "date") {
5724
5713
  return repeatCharToFitWidth(applyDateTimeFormat(value, formatToApply), formatWidth);
@@ -5730,6 +5719,31 @@ function formatValue(value, { format, locale, formatWidth }) {
5730
5719
  return "";
5731
5720
  }
5732
5721
  }
5722
+ function getFormatToApply(value, internalFormat) {
5723
+ let formatToApply = undefined;
5724
+ let isNegativeFormat = false;
5725
+ switch (typeof value) {
5726
+ case "number":
5727
+ if (value < 0 && internalFormat.negative) {
5728
+ formatToApply = internalFormat.negative;
5729
+ isNegativeFormat = true;
5730
+ }
5731
+ else if (value === 0 && internalFormat.zero) {
5732
+ formatToApply = internalFormat.zero;
5733
+ }
5734
+ else {
5735
+ formatToApply = internalFormat.positive;
5736
+ }
5737
+ break;
5738
+ case "string":
5739
+ const format = internalFormat.text || internalFormat.positive;
5740
+ if (format.type === "text") {
5741
+ formatToApply = format;
5742
+ }
5743
+ break;
5744
+ }
5745
+ return { format: formatToApply, isNegativeFormat };
5746
+ }
5733
5747
  function applyTextInternalFormat(value, internalFormat, formatWidth) {
5734
5748
  let formattedValue = "";
5735
5749
  for (const token of internalFormat.tokens) {
@@ -6341,6 +6355,27 @@ function isTextFormat(format) {
6341
6355
  return false;
6342
6356
  }
6343
6357
  }
6358
+ function formatHasRepeatedChar(value, format) {
6359
+ if (!format) {
6360
+ return false;
6361
+ }
6362
+ try {
6363
+ const internalFormat = parseFormat(format);
6364
+ const { format: formatToApply } = getFormatToApply(value, internalFormat);
6365
+ if (formatToApply?.type === "text" || formatToApply?.type === "date") {
6366
+ const tokens = formatToApply.tokens;
6367
+ return tokens.some((token) => token.type === "REPEATED_CHAR");
6368
+ }
6369
+ else if (formatToApply?.type === "number") {
6370
+ return (formatToApply.integerPart.some((token) => token.type === "REPEATED_CHAR") ||
6371
+ (formatToApply.decimalPart
6372
+ ? formatToApply.decimalPart.some((token) => token.type === "REPEATED_CHAR")
6373
+ : false));
6374
+ }
6375
+ }
6376
+ catch { }
6377
+ return false;
6378
+ }
6344
6379
 
6345
6380
  function evaluateLiteral(literalCell, localeFormat, position) {
6346
6381
  const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
@@ -18635,7 +18670,7 @@ function parseOperand(tokens) {
18635
18670
  case "STRING":
18636
18671
  return {
18637
18672
  type: "STRING",
18638
- value: removeStringQuotes(current.value),
18673
+ value: unquote(current.value),
18639
18674
  tokenStartIndex: current.tokenIndex,
18640
18675
  tokenEndIndex: current.tokenIndex,
18641
18676
  };
@@ -22392,7 +22427,7 @@ function formulaArguments(tokens) {
22392
22427
  dependencies.push(token.value);
22393
22428
  break;
22394
22429
  case "STRING":
22395
- const value = removeStringQuotes(token.value);
22430
+ const value = unquote(token.value);
22396
22431
  literalValues.strings.push({ value });
22397
22432
  break;
22398
22433
  case "NUMBER": {
@@ -24644,6 +24679,7 @@ class ScorecardChart extends Component {
24644
24679
  static template = "o-spreadsheet-ScorecardChart";
24645
24680
  static props = {
24646
24681
  chartId: String,
24682
+ isFullScreen: { type: Boolean, optional: true },
24647
24683
  };
24648
24684
  canvas = useRef("chartContainer");
24649
24685
  get runtime() {
@@ -26563,7 +26599,7 @@ function getRadarChartScales(definition, args) {
26563
26599
  },
26564
26600
  pointLabels: {
26565
26601
  color: chartFontColor(definition.background),
26566
- callback: truncateLabel,
26602
+ callback: (label) => truncateLabel(label),
26567
26603
  },
26568
26604
  suggestedMin: minValue < 0 ? minValue - 1 : 0,
26569
26605
  },
@@ -27407,26 +27443,6 @@ function createBarChartRuntime(chart, getters) {
27407
27443
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
27408
27444
  }
27409
27445
 
27410
- class FullScreenChartStore extends SpreadsheetStore {
27411
- mutators = ["toggleFullScreenChart"];
27412
- fullScreenFigure = undefined;
27413
- toggleFullScreenChart(figureId) {
27414
- if (this.fullScreenFigure?.id === figureId) {
27415
- this.fullScreenFigure = undefined;
27416
- }
27417
- else {
27418
- this.makeFullScreen(figureId);
27419
- }
27420
- }
27421
- makeFullScreen(figureId) {
27422
- const sheetId = this.getters.getActiveSheetId();
27423
- const figure = this.getters.getFigure(sheetId, figureId);
27424
- if (figure) {
27425
- this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
27426
- }
27427
- }
27428
- }
27429
-
27430
27446
  const TREND_LINE_AXES_IDS = [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID];
27431
27447
  const ZOOMABLE_AXIS_IDS = ["x", ...TREND_LINE_AXES_IDS];
27432
27448
  class ZoomableChartStore extends SpreadsheetStore {
@@ -27591,7 +27607,6 @@ chartJsExtensionRegistry.add("zoomWindowPlugin", {
27591
27607
  class ZoomableChartJsComponent extends ChartJsComponent {
27592
27608
  static template = "o-spreadsheet-ZoomableChartJsComponent";
27593
27609
  store;
27594
- fullScreenChartStore;
27595
27610
  masterChartCanvas = useRef("masterChartCanvas");
27596
27611
  masterChart;
27597
27612
  mode;
@@ -27602,7 +27617,6 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27602
27617
  removeEventListeners = () => { };
27603
27618
  setup() {
27604
27619
  this.store = useStore(ZoomableChartStore);
27605
- this.fullScreenChartStore = useStore(FullScreenChartStore);
27606
27620
  super.setup();
27607
27621
  }
27608
27622
  unmount() {
@@ -27617,12 +27631,8 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27617
27631
  `;
27618
27632
  }
27619
27633
  get sliceable() {
27620
- if (this.env.isDashboard()) {
27621
- const fullScreenFigureId = this.fullScreenChartStore.fullScreenFigure?.id;
27622
- const chartFigureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
27623
- if (fullScreenFigureId === chartFigureId) {
27624
- return true;
27625
- }
27634
+ if (this.props.isFullScreen) {
27635
+ return true;
27626
27636
  }
27627
27637
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27628
27638
  return ("zoomable" in definition && definition?.zoomable) ?? false;
@@ -27685,9 +27695,6 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27685
27695
  const xMax = Math.max(...xValues);
27686
27696
  return { xMin, xMax };
27687
27697
  }
27688
- get shouldAnimate() {
27689
- return this.env.model.getters.isDashboard() && !this.sliceable;
27690
- }
27691
27698
  createChart(chartRuntime) {
27692
27699
  const chartData = chartRuntime.chartJsConfig;
27693
27700
  this.isBarChart = chartData.type === "bar";
@@ -27706,6 +27713,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27706
27713
  const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
27707
27714
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27708
27715
  this.resetAxesLimits();
27716
+ if (this.chart?.options) {
27717
+ this.chart.options.animation = false;
27718
+ }
27709
27719
  }
27710
27720
  updateChartJs(chartRuntime) {
27711
27721
  const chartData = chartRuntime.chartJsConfig;
@@ -27739,6 +27749,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27739
27749
  }
27740
27750
  }
27741
27751
  this.resetAxesLimits();
27752
+ if (this.chart?.options) {
27753
+ this.chart.options.animation = false;
27754
+ }
27742
27755
  }
27743
27756
  resetAxesLimits() {
27744
27757
  if (!this.chart) {
@@ -27836,7 +27849,7 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27836
27849
  onPointerDownInMasterChart(ev) {
27837
27850
  this.removeEventListeners();
27838
27851
  const position = ev.offsetX;
27839
- if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
27852
+ if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
27840
27853
  return;
27841
27854
  }
27842
27855
  const { left, right, top, bottom } = this.masterChart.chartArea;
@@ -31394,6 +31407,26 @@ function getCarouselItemTitle(getters, item) {
31394
31407
  return matchedChart.displayName;
31395
31408
  }
31396
31409
 
31410
+ class FullScreenFigureStore extends SpreadsheetStore {
31411
+ mutators = ["toggleFullScreenFigure"];
31412
+ fullScreenFigure = undefined;
31413
+ toggleFullScreenFigure(figureId) {
31414
+ if (this.fullScreenFigure?.id === figureId) {
31415
+ this.fullScreenFigure = undefined;
31416
+ }
31417
+ else {
31418
+ this.makeFullScreen(figureId);
31419
+ }
31420
+ }
31421
+ makeFullScreen(figureId) {
31422
+ const sheetId = this.getters.getActiveSheetId();
31423
+ const figure = this.getters.getFigure(sheetId, figureId);
31424
+ if (figure) {
31425
+ this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
31426
+ }
31427
+ }
31428
+ }
31429
+
31397
31430
  /**
31398
31431
  * This file is largely inspired by owl 1.
31399
31432
  * `css` tag has been removed from owl 2 without workaround to manage css.
@@ -32131,12 +32164,13 @@ class MenuPopover extends Component {
32131
32164
  class ChartDashboardMenu extends Component {
32132
32165
  static template = "o-spreadsheet-ChartDashboardMenu";
32133
32166
  static components = { MenuPopover };
32134
- static props = { chartId: String };
32167
+ static props = { chartId: String, hasFullScreenButton: { type: Boolean, optional: true } };
32168
+ static defaultProps = { hasFullScreenButton: true };
32135
32169
  fullScreenFigureStore;
32136
32170
  menuState = useState({ isOpen: false, anchorRect: null, menuItems: [] });
32137
32171
  setup() {
32138
32172
  super.setup();
32139
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
32173
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32140
32174
  }
32141
32175
  getMenuItems() {
32142
32176
  return [this.fullScreenMenuItem].filter(isDefined);
@@ -32152,6 +32186,9 @@ class ChartDashboardMenu extends Component {
32152
32186
  this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32153
32187
  }
32154
32188
  get fullScreenMenuItem() {
32189
+ if (!this.props.hasFullScreenButton) {
32190
+ return undefined;
32191
+ }
32155
32192
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
32156
32193
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32157
32194
  if (definition.type === "scorecard") {
@@ -32163,7 +32200,7 @@ class ChartDashboardMenu extends Component {
32163
32200
  label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
32164
32201
  class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
32165
32202
  onClick: () => {
32166
- this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32203
+ this.fullScreenFigureStore.toggleFullScreenFigure(figureId);
32167
32204
  },
32168
32205
  };
32169
32206
  }
@@ -32175,6 +32212,7 @@ class CarouselFigure extends Component {
32175
32212
  figureUI: Object,
32176
32213
  onFigureDeleted: Function,
32177
32214
  editFigureStyle: { type: Function, optional: true },
32215
+ isFullScreen: { type: Boolean, optional: true },
32178
32216
  };
32179
32217
  static components = { ChartDashboardMenu, MenuPopover };
32180
32218
  carouselTabsRef = useRef("carouselTabs");
@@ -32182,8 +32220,10 @@ class CarouselFigure extends Component {
32182
32220
  menuState = useState({ isOpen: false, anchorRect: null, menuItems: [] });
32183
32221
  hiddenItems = [];
32184
32222
  animationStore;
32223
+ fullScreenFigureStore;
32185
32224
  setup() {
32186
32225
  this.animationStore = useStore(ChartAnimationStore);
32226
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32187
32227
  useEffect(() => {
32188
32228
  if (this.selectedCarouselItem?.type === "carouselDataView") {
32189
32229
  this.props.editFigureStyle?.({ "pointer-events": "none" });
@@ -32230,18 +32270,19 @@ class CarouselFigure extends Component {
32230
32270
  item,
32231
32271
  });
32232
32272
  if (item.type === "chart") {
32233
- this.animationStore?.enableAnimationForChart(item.chartId);
32273
+ const animationChartId = item.chartId + (this.props.isFullScreen ? "-fullscreen" : "");
32274
+ this.animationStore?.enableAnimationForChart(animationChartId);
32234
32275
  }
32235
32276
  }
32236
32277
  get headerStyle() {
32237
32278
  const cssProperties = {};
32238
- if (this.selectedCarouselItem?.type === "carouselDataView") {
32239
- cssProperties["background-color"] = "#ffffff";
32240
- }
32241
- else if (this.selectedCarouselItem?.type === "chart") {
32279
+ if (this.selectedCarouselItem?.type === "chart") {
32242
32280
  const chart = this.env.model.getters.getChartRuntime(this.selectedCarouselItem.chartId);
32243
32281
  cssProperties["background-color"] = chart.background;
32244
32282
  }
32283
+ else {
32284
+ cssProperties["background-color"] = "#ffffff";
32285
+ }
32245
32286
  return cssPropertiesToCss(cssProperties);
32246
32287
  }
32247
32288
  get title() {
@@ -32294,6 +32335,17 @@ class CarouselFigure extends Component {
32294
32335
  this.menuState.anchorRect = rect;
32295
32336
  this.menuState.menuItems = createActions(menuItems);
32296
32337
  }
32338
+ toggleFullScreen() {
32339
+ if (this.selectedCarouselItem?.type === "chart") {
32340
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.props.figureUI.id);
32341
+ }
32342
+ }
32343
+ get fullScreenButtonTitle() {
32344
+ return this.props.isFullScreen ? _t("Exit Full Screen") : _t("Full Screen");
32345
+ }
32346
+ get visibleCarouselItems() {
32347
+ return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32348
+ }
32297
32349
  }
32298
32350
 
32299
32351
  class ChartFigure extends Component {
@@ -32302,6 +32354,7 @@ class ChartFigure extends Component {
32302
32354
  figureUI: Object,
32303
32355
  onFigureDeleted: Function,
32304
32356
  editFigureStyle: { type: Function, optional: true },
32357
+ isFullScreen: { type: Boolean, optional: true },
32305
32358
  };
32306
32359
  static components = { ChartDashboardMenu };
32307
32360
  onDoubleClick() {
@@ -32404,9 +32457,7 @@ class FigureComponent extends Component {
32404
32457
  return this.isSelected ? ACTIVE_BORDER_WIDTH : this.borderWidth;
32405
32458
  }
32406
32459
  getBorderStyle(position) {
32407
- const borderWidth = this.getBorderWidth();
32408
- const borderColor = this.isSelected ? SELECTION_BORDER_COLOR : FIGURE_BORDER_COLOR;
32409
- return `border-${position}: ${borderWidth}px solid ${borderColor};`;
32460
+ return `border-${position}-width: ${this.getBorderWidth()}px;`;
32410
32461
  }
32411
32462
  get wrapperStyle() {
32412
32463
  const { x, y, width, height } = this.props.figureUI;
@@ -51342,6 +51393,9 @@ class GridRenderer extends SpreadsheetStore {
51342
51393
  }
51343
51394
  const { align } = this.getters.getCellStyle(position);
51344
51395
  const evaluatedCell = this.getters.getEvaluatedCell(position);
51396
+ if (formatHasRepeatedChar(evaluatedCell.value, evaluatedCell.format)) {
51397
+ return "left";
51398
+ }
51345
51399
  if (isOverflowing && evaluatedCell.type === CellValueType.number) {
51346
51400
  return align !== "center" ? "left" : align;
51347
51401
  }
@@ -56813,7 +56867,7 @@ class PivotMeasureEditor extends Component {
56813
56867
  return undefined;
56814
56868
  }
56815
56869
  get isCalculatedMeasureInvalid() {
56816
- return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
56870
+ return compile(this.props.measure.computedBy?.formula ?? "").isBadExpression;
56817
56871
  }
56818
56872
  }
56819
56873
 
@@ -59528,16 +59582,16 @@ class ClickableCellSortIcon extends Component {
59528
59582
  }
59529
59583
  }
59530
59584
 
59531
- class FullScreenChart extends Component {
59532
- static template = "o-spreadsheet-FullScreenChart";
59585
+ class FullScreenFigure extends Component {
59586
+ static template = "o-spreadsheet-FullScreenFigure";
59533
59587
  static props = {};
59534
- static components = { ChartDashboardMenu };
59535
- fullScreenChartStore;
59536
- ref = useRef("fullScreenChart");
59588
+ static components = { ChartFigure };
59589
+ fullScreenFigureStore;
59590
+ ref = useRef("fullScreenFigure");
59537
59591
  spreadsheetRect = useSpreadsheetRect();
59538
59592
  figureRegistry = figureRegistry;
59539
59593
  setup() {
59540
- this.fullScreenChartStore = useStore(FullScreenChartStore);
59594
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
59541
59595
  const animationStore = useStore(ChartAnimationStore);
59542
59596
  let lastFigureId = undefined;
59543
59597
  onWillUpdateProps(() => {
@@ -59549,7 +59603,7 @@ class FullScreenChart extends Component {
59549
59603
  useEffect((el) => el?.focus(), () => [this.ref.el]);
59550
59604
  }
59551
59605
  get figureUI() {
59552
- return this.fullScreenChartStore.fullScreenFigure;
59606
+ return this.fullScreenFigureStore.fullScreenFigure;
59553
59607
  }
59554
59608
  get chartId() {
59555
59609
  if (!this.figureUI)
@@ -59558,7 +59612,7 @@ class FullScreenChart extends Component {
59558
59612
  }
59559
59613
  exitFullScreen() {
59560
59614
  if (this.figureUI) {
59561
- this.fullScreenChartStore.toggleFullScreenChart(this.figureUI.id);
59615
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.figureUI.id);
59562
59616
  }
59563
59617
  }
59564
59618
  onKeyDown(ev) {
@@ -59566,15 +59620,10 @@ class FullScreenChart extends Component {
59566
59620
  this.exitFullScreen();
59567
59621
  }
59568
59622
  }
59569
- get chartComponent() {
59570
- if (!this.chartId)
59623
+ get figureComponent() {
59624
+ if (!this.figureUI)
59571
59625
  return undefined;
59572
- const type = this.env.model.getters.getChartType(this.chartId);
59573
- const component = chartComponentRegistry.get(type);
59574
- if (!component) {
59575
- throw new Error(`Component is not defined for type ${type}`);
59576
- }
59577
- return component;
59626
+ return figureRegistry.get(this.figureUI.tag).Component;
59578
59627
  }
59579
59628
  }
59580
59629
 
@@ -62277,11 +62326,11 @@ class HeaderSizePlugin extends CorePlugin {
62277
62326
  break;
62278
62327
  }
62279
62328
  case "ADD_COLUMNS_ROWS": {
62280
- const sizes = [...this.sizes[cmd.sheetId][cmd.dimension]];
62329
+ const sizes = this.sizes[cmd.sheetId][cmd.dimension];
62281
62330
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
62282
62331
  const baseSize = sizes[cmd.base];
62283
- sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
62284
- this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
62332
+ const newSizes = insertItemsAtIndex(sizes, Array(cmd.quantity).fill(baseSize), addIndex);
62333
+ this.history.update("sizes", cmd.sheetId, cmd.dimension, newSizes);
62285
62334
  break;
62286
62335
  }
62287
62336
  case "RESIZE_COLUMNS_ROWS":
@@ -62432,9 +62481,8 @@ class HeaderVisibilityPlugin extends CorePlugin {
62432
62481
  break;
62433
62482
  }
62434
62483
  case "ADD_COLUMNS_ROWS": {
62435
- const hiddenHeaders = [...this.hiddenHeaders[cmd.sheetId][cmd.dimension]];
62436
62484
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
62437
- hiddenHeaders.splice(addIndex, 0, ...Array(cmd.quantity).fill(false));
62485
+ const hiddenHeaders = insertItemsAtIndex([...this.hiddenHeaders[cmd.sheetId][cmd.dimension]], Array(cmd.quantity).fill(false), addIndex);
62438
62486
  this.history.update("hiddenHeaders", cmd.sheetId, cmd.dimension, hiddenHeaders);
62439
62487
  break;
62440
62488
  }
@@ -66618,12 +66666,12 @@ class SpreadsheetRTree {
66618
66666
  this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
66619
66667
  }
66620
66668
  rtreeItemComparer(left, right) {
66621
- return (left.data === right.data &&
66622
- left.boundingBox.sheetId === right.boundingBox.sheetId &&
66669
+ return (left.boundingBox.sheetId === right.boundingBox.sheetId &&
66623
66670
  left.boundingBox?.zone.left === right.boundingBox.zone.left &&
66624
66671
  left.boundingBox?.zone.top === right.boundingBox.zone.top &&
66625
66672
  left.boundingBox?.zone.right === right.boundingBox.zone.right &&
66626
- left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
66673
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom &&
66674
+ deepEquals(left.data, right.data));
66627
66675
  }
66628
66676
  }
66629
66677
  /**
@@ -72878,7 +72926,10 @@ class SortPlugin extends UIPlugin {
72878
72926
  return "Success" /* CommandResult.Success */;
72879
72927
  }
72880
72928
  checkArrayFormulaInSortZone({ sheetId, zone }) {
72881
- const arrayFormulaInZone = positions(zone).some(({ col, row }) => this.getters.getArrayFormulaSpreadingOn({ sheetId, col, row }));
72929
+ const arrayFormulaInZone = positions(zone).some(({ col, row }) => {
72930
+ const originPosition = this.getters.getArrayFormulaSpreadingOn({ sheetId, col, row });
72931
+ return originPosition && !deepEquals(originPosition, { sheetId, col, row });
72932
+ });
72882
72933
  return arrayFormulaInZone ? "SortZoneWithArrayFormulas" /* CommandResult.SortZoneWithArrayFormulas */ : "Success" /* CommandResult.Success */;
72883
72934
  }
72884
72935
  /**
@@ -79468,8 +79519,8 @@ class RowGroup extends AbstractHeaderGroup {
79468
79519
  left: `calc(50% - 1px)`, // -1px: we want the border to be on the center
79469
79520
  width: `30%`,
79470
79521
  height: `calc(100% - ${groupBox.headerRect.height / 2}px)`,
79471
- "border-left": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
79472
- "border-bottom": groupBox.isEndHidden ? "" : `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
79522
+ "border-left": `1px solid`,
79523
+ "border-bottom": groupBox.isEndHidden ? "" : `1px solid`,
79473
79524
  });
79474
79525
  }
79475
79526
  get groupHeaderStyle() {
@@ -79521,8 +79572,8 @@ class ColGroup extends AbstractHeaderGroup {
79521
79572
  left: `${groupBox.headerRect.width / 2}px`,
79522
79573
  width: `calc(100% - ${groupBox.headerRect.width / 2}px)`,
79523
79574
  height: `30%`,
79524
- "border-top": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
79525
- "border-right": groupBox.isEndHidden ? "" : `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
79575
+ "border-top": `1px solid`,
79576
+ "border-right": groupBox.isEndHidden ? "" : `1px solid`,
79526
79577
  });
79527
79578
  }
79528
79579
  get groupHeaderStyle() {
@@ -79859,6 +79910,9 @@ class SmallBottomBar extends Component {
79859
79910
  ? this.composerFocusStore.focusMode
79860
79911
  : "inactive";
79861
79912
  }
79913
+ get showFxIcon() {
79914
+ return this.focus === "inactive" && !this.composerStore.currentContent;
79915
+ }
79862
79916
  get rect() {
79863
79917
  return this.composerRef.el
79864
79918
  ? getBoundingRectAsPOJO(this.composerRef.el)
@@ -79901,13 +79955,6 @@ class SmallBottomBar extends Component {
79901
79955
  }
79902
79956
 
79903
79957
  const COMPOSER_MAX_HEIGHT = 300;
79904
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
79905
- // FIXME This svg is hardcoded in the css file. We should find a better way to handle it.
79906
- // const FX_SVG = /*xml*/ `
79907
- // <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
79908
- // <path d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='#BDBDBD'/>
79909
- // </svg>
79910
- // `;
79911
79958
  class TopBarComposer extends Component {
79912
79959
  static template = "o-spreadsheet-TopBarComposer";
79913
79960
  static props = {};
@@ -79934,6 +79981,9 @@ class TopBarComposer extends Component {
79934
79981
  ? this.composerFocusStore.focusMode
79935
79982
  : "inactive";
79936
79983
  }
79984
+ get showFxIcon() {
79985
+ return this.focus === "inactive" && !this.composerStore.currentContent;
79986
+ }
79937
79987
  get composerStyle() {
79938
79988
  const style = {
79939
79989
  padding: "5px 0px 5px 8px",
@@ -80871,7 +80921,7 @@ class Spreadsheet extends Component {
80871
80921
  SidePanels,
80872
80922
  SpreadsheetDashboard,
80873
80923
  HeaderGroupContainer,
80874
- FullScreenChart,
80924
+ FullScreenFigure,
80875
80925
  };
80876
80926
  sidePanel;
80877
80927
  spreadsheetRef = useRef("spreadsheet");
@@ -85595,6 +85645,7 @@ const components = {
85595
85645
  Grid,
85596
85646
  GridOverlay,
85597
85647
  ScorecardChart,
85648
+ GaugeChartComponent,
85598
85649
  LineConfigPanel,
85599
85650
  BarConfigPanel,
85600
85651
  PieChartDesignPanel,
@@ -85633,7 +85684,7 @@ const components = {
85633
85684
  RadioSelection,
85634
85685
  GeoChartRegionSelectSection,
85635
85686
  ChartDashboardMenu,
85636
- FullScreenChart,
85687
+ FullScreenFigure,
85637
85688
  };
85638
85689
  const hooks = {
85639
85690
  useDragAndDropListItems,
@@ -85683,6 +85734,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
85683
85734
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
85684
85735
 
85685
85736
 
85686
- __info__.version = "19.1.0-alpha.2";
85687
- __info__.date = "2025-09-19T07:26:49.306Z";
85688
- __info__.hash = "8cc543d";
85737
+ __info__.version = "19.1.0-alpha.4";
85738
+ __info__.date = "2025-10-07T10:03:20.917Z";
85739
+ __info__.hash = "b7cfed8";