@odoo/o-spreadsheet 19.1.1 → 19.1.3

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.1
6
- * @date 2025-12-26T10:26:46.141Z
7
- * @hash e6313bd
5
+ * @version 19.1.3
6
+ * @date 2026-01-14T10:02:32.431Z
7
+ * @hash 52a3e52
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -179,6 +179,7 @@
179
179
  textColor: "",
180
180
  rotation: 0,
181
181
  };
182
+ const ROTATION_EPSILON = 0.001;
182
183
  const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
183
184
  const DEFAULT_WRAPPING_MODE = DEFAULT_STYLE.wrapping;
184
185
  // Fonts
@@ -15492,9 +15493,10 @@
15492
15493
  throw new EvaluationError(_t("Function PIVOT takes an even number of arguments."));
15493
15494
  }
15494
15495
  }
15495
- function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
15496
+ function addPivotDependencies(evalContext, pivotId, forMeasures) {
15496
15497
  //TODO This function can be very costly when used with PIVOT.VALUE and PIVOT.HEADER
15497
15498
  const dependencies = [];
15499
+ const coreDefinition = evalContext.getters.getPivotCoreDefinition(pivotId);
15498
15500
  if (coreDefinition.type === "SPREADSHEET" && coreDefinition.dataSet) {
15499
15501
  const { sheetId, zone } = coreDefinition.dataSet;
15500
15502
  const xc = zoneToXc(zone);
@@ -15511,8 +15513,7 @@
15511
15513
  }
15512
15514
  for (const measure of forMeasures) {
15513
15515
  if (measure.computedBy) {
15514
- const formula = evalContext.getters.getMeasureCompiledFormula(measure);
15515
- dependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
15516
+ dependencies.push(...evalContext.getters.getMeasureFullDependencies(pivotId, measure));
15516
15517
  }
15517
15518
  }
15518
15519
  const originPosition = evalContext.__originCellPosition;
@@ -16009,7 +16010,7 @@
16009
16010
  assertDomainLength(domainArgs);
16010
16011
  const pivot = this.getters.getPivot(pivotId);
16011
16012
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
16012
- addPivotDependencies(this, coreDefinition, coreDefinition.measures.filter((m) => m.id === _measure));
16013
+ addPivotDependencies(this, pivotId, coreDefinition.measures.filter((m) => m.id === _measure));
16013
16014
  pivot.init({ reload: pivot.needsReevaluation });
16014
16015
  const error = pivot.assertIsValid({ throwOnError: false });
16015
16016
  if (error) {
@@ -16042,8 +16043,7 @@
16042
16043
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
16043
16044
  assertDomainLength(domainArgs);
16044
16045
  const pivot = this.getters.getPivot(_pivotId);
16045
- const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
16046
- addPivotDependencies(this, coreDefinition, []);
16046
+ addPivotDependencies(this, _pivotId, []);
16047
16047
  pivot.init({ reload: pivot.needsReevaluation });
16048
16048
  const error = pivot.assertIsValid({ throwOnError: false });
16049
16049
  if (error) {
@@ -16095,7 +16095,7 @@
16095
16095
  if (pivotStyle.numberOfColumns < 0) {
16096
16096
  return new EvaluationError(_t("The number of columns must be positive."));
16097
16097
  }
16098
- addPivotDependencies(this, coreDefinition, coreDefinition.measures);
16098
+ addPivotDependencies(this, pivotId, coreDefinition.measures);
16099
16099
  pivot.init({ reload: pivot.needsReevaluation });
16100
16100
  const error = pivot.assertIsValid({ throwOnError: false });
16101
16101
  if (error) {
@@ -19609,17 +19609,41 @@ stores.inject(MyMetaStore, storeInstance);
19609
19609
  const today = DateTime.now();
19610
19610
  switch (dateValue) {
19611
19611
  case "today":
19612
- return jsDateToNumber(today);
19613
- case "yesterday":
19614
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
19615
- case "tomorrow":
19616
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
19612
+ return Math.floor(jsDateToNumber(today));
19613
+ case "yesterday": {
19614
+ today.setDate(today.getDate() - 1);
19615
+ return Math.floor(jsDateToNumber(today));
19616
+ }
19617
+ case "tomorrow": {
19618
+ today.setDate(today.getDate() + 1);
19619
+ return Math.floor(jsDateToNumber(today));
19620
+ }
19617
19621
  case "lastWeek":
19618
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
19619
- case "lastMonth":
19620
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
19622
+ today.setDate(today.getDate() - 6);
19623
+ return Math.floor(jsDateToNumber(today));
19624
+ case "lastMonth": {
19625
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
19626
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
19627
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
19628
+ today.setDate(1);
19629
+ }
19630
+ else {
19631
+ today.setDate(today.getDate() + 1);
19632
+ today.setMonth(today.getMonth() - 1);
19633
+ }
19634
+ return Math.floor(jsDateToNumber(today));
19635
+ }
19621
19636
  case "lastYear":
19622
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
19637
+ // Handle leap year case
19638
+ if (today.getMonth() === 1 && today.getDate() === 29) {
19639
+ today.setDate(28);
19640
+ today.setFullYear(today.getFullYear() - 1);
19641
+ }
19642
+ else {
19643
+ today.setDate(today.getDate() + 1);
19644
+ today.setFullYear(today.getFullYear() - 1);
19645
+ }
19646
+ return Math.floor(jsDateToNumber(today));
19623
19647
  }
19624
19648
  }
19625
19649
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -20530,7 +20554,7 @@ stores.inject(MyMetaStore, storeInstance);
20530
20554
  let { x, y } = rect; // top-left when align=left and top-right when align=right, top-center when align=center
20531
20555
  const cos = Math.cos(-style.rotation);
20532
20556
  const sin = Math.sin(-style.rotation);
20533
- const width = rect.textWidth - MIN_CELL_TEXT_MARGIN;
20557
+ const width = rect.textWidth - 2 * MIN_CELL_TEXT_MARGIN;
20534
20558
  const height = rect.textHeight;
20535
20559
  const center = style.align === "center";
20536
20560
  const rotateTowardCellCenter = (style.align === "left") === sin < 0;
@@ -20564,6 +20588,13 @@ stores.inject(MyMetaStore, storeInstance);
20564
20588
  else {
20565
20589
  if (center) {
20566
20590
  x -= sh / 2;
20591
+ y -= height / 2;
20592
+ if (rotateTowardCellCenter) {
20593
+ y += sh;
20594
+ }
20595
+ else {
20596
+ y -= sh;
20597
+ }
20567
20598
  }
20568
20599
  else if (rotateTowardCellCenter) {
20569
20600
  x -= sh;
@@ -31116,7 +31147,7 @@ stores.inject(MyMetaStore, storeInstance);
31116
31147
  if (message.type === "CLIENT_JOINED" ||
31117
31148
  message.type === "CLIENT_LEFT" ||
31118
31149
  message.type === "CLIENT_MOVED") {
31119
- this.transportService.sendMessage(message);
31150
+ await this.transportService.sendMessage(message);
31120
31151
  }
31121
31152
  // ignore all other messages
31122
31153
  }
@@ -32290,7 +32321,7 @@ stores.inject(MyMetaStore, storeInstance);
32290
32321
  }
32291
32322
  delete this.clients[this.clientId];
32292
32323
  this.transportService.leave(this.clientId);
32293
- this.transportService.sendMessage({
32324
+ this.sendToTransport({
32294
32325
  type: "CLIENT_LEFT",
32295
32326
  clientId: this.clientId,
32296
32327
  version: MESSAGE_VERSION,
@@ -32304,7 +32335,7 @@ stores.inject(MyMetaStore, storeInstance);
32304
32335
  return;
32305
32336
  }
32306
32337
  const snapshotId = this.uuidGenerator.uuidv4();
32307
- await this.transportService.sendMessage({
32338
+ await this.sendToTransport({
32308
32339
  type: "SNAPSHOT",
32309
32340
  nextRevisionId: snapshotId,
32310
32341
  serverRevisionId: this.serverRevisionId,
@@ -32352,10 +32383,14 @@ stores.inject(MyMetaStore, storeInstance);
32352
32383
  const type = currentPosition ? "CLIENT_MOVED" : "CLIENT_JOINED";
32353
32384
  const client = this.getCurrentClient();
32354
32385
  this.clients[this.clientId] = { ...client, position };
32355
- this.transportService.sendMessage({
32386
+ this.sendToTransport({
32356
32387
  type,
32357
32388
  version: MESSAGE_VERSION,
32358
32389
  client: { ...client, position },
32390
+ }).then(() => {
32391
+ if (this.pendingMessages.length > 0 && !this.waitingAck) {
32392
+ this.sendPendingMessage();
32393
+ }
32359
32394
  });
32360
32395
  }
32361
32396
  /**
@@ -32436,7 +32471,7 @@ stores.inject(MyMetaStore, storeInstance);
32436
32471
  if (client) {
32437
32472
  const { position } = client;
32438
32473
  if (position) {
32439
- this.transportService.sendMessage({
32474
+ this.sendToTransport({
32440
32475
  type: "CLIENT_MOVED",
32441
32476
  version: MESSAGE_VERSION,
32442
32477
  client: { ...client, position },
@@ -32457,6 +32492,10 @@ stores.inject(MyMetaStore, storeInstance);
32457
32492
  }
32458
32493
  this.sendPendingMessage();
32459
32494
  }
32495
+ async sendToTransport(message) {
32496
+ // wrap in an async function to ensure it returns a promise
32497
+ return this.transportService.sendMessage(message);
32498
+ }
32460
32499
  /**
32461
32500
  * Send the next pending message
32462
32501
  */
@@ -32485,9 +32524,14 @@ stores.inject(MyMetaStore, storeInstance);
32485
32524
  ${JSON.stringify(message)}`);
32486
32525
  }
32487
32526
  this.waitingAck = true;
32488
- this.transportService.sendMessage({
32527
+ this.sendToTransport({
32489
32528
  ...message,
32490
32529
  serverRevisionId: this.serverRevisionId,
32530
+ }).catch((e) => {
32531
+ if (!(e instanceof ClientDisconnectedError)) {
32532
+ throw e.cause || e;
32533
+ }
32534
+ this.waitingAck = false;
32491
32535
  });
32492
32536
  }
32493
32537
  acknowledge(message) {
@@ -34363,6 +34407,328 @@ stores.inject(MyMetaStore, storeInstance);
34363
34407
  */
34364
34408
  const DEFAULT_SYSTEM_COLOR = "FF000000";
34365
34409
 
34410
+ // -------------------------------------
34411
+ // CF HELPERS
34412
+ // -------------------------------------
34413
+ /**
34414
+ * Convert the conditional formatting o-spreadsheet operator to
34415
+ * the corresponding excel operator.
34416
+ * */
34417
+ function convertOperator(operator) {
34418
+ switch (operator) {
34419
+ case "isNotEmpty":
34420
+ return "notContainsBlanks";
34421
+ case "isEmpty":
34422
+ return "containsBlanks";
34423
+ case "notContainsText":
34424
+ return "notContainsBlanks";
34425
+ case "containsText":
34426
+ return "containsText";
34427
+ case "beginsWithText":
34428
+ return "beginsWith";
34429
+ case "endsWithText":
34430
+ return "endsWith";
34431
+ case "isGreaterThan":
34432
+ return "greaterThan";
34433
+ case "isGreaterOrEqualTo":
34434
+ return "greaterThanOrEqual";
34435
+ case "isLessThan":
34436
+ return "lessThan";
34437
+ case "isLessOrEqualTo":
34438
+ return "lessThanOrEqual";
34439
+ case "isBetween":
34440
+ return "between";
34441
+ case "isNotBetween":
34442
+ return "notBetween";
34443
+ case "isEqual":
34444
+ return "equal";
34445
+ case "isNotEqual":
34446
+ return "notEqual";
34447
+ case "customFormula":
34448
+ return "";
34449
+ case "dateIs":
34450
+ return "";
34451
+ case "dateIsBefore":
34452
+ return "lessThan";
34453
+ case "dateIsAfter":
34454
+ return "greaterThan";
34455
+ case "dateIsOnOrAfter":
34456
+ return "greaterThanOrEqual";
34457
+ case "dateIsOnOrBefore":
34458
+ return "lessThanOrEqual";
34459
+ }
34460
+ }
34461
+ // -------------------------------------
34462
+ // WORKSHEET HELPERS
34463
+ // -------------------------------------
34464
+ function getCellType(value) {
34465
+ switch (typeof value) {
34466
+ case "boolean":
34467
+ return "b";
34468
+ case "string":
34469
+ return "str";
34470
+ case "number":
34471
+ return "n";
34472
+ default:
34473
+ return undefined;
34474
+ }
34475
+ }
34476
+ function convertHeightToExcel(height) {
34477
+ return Math.round(HEIGHT_FACTOR * height * 100) / 100;
34478
+ }
34479
+ function convertWidthToExcel(width) {
34480
+ return Math.round(WIDTH_FACTOR * width * 100) / 100;
34481
+ }
34482
+ function convertHeightFromExcel(height) {
34483
+ if (!height)
34484
+ return height;
34485
+ return Math.round((height / HEIGHT_FACTOR) * 100) / 100;
34486
+ }
34487
+ function convertWidthFromExcel(width) {
34488
+ if (!width)
34489
+ return width;
34490
+ return Math.round((width / WIDTH_FACTOR) * 100) / 100;
34491
+ }
34492
+ function extractStyle(data, content, styleId, formatId, borderId) {
34493
+ const style = styleId ? data.styles[styleId] : {};
34494
+ const format = formatId ? data.formats[formatId] : undefined;
34495
+ const styles = {
34496
+ font: {
34497
+ size: style?.fontSize || DEFAULT_FONT_SIZE,
34498
+ color: { rgb: style?.textColor ? style.textColor : "000000" },
34499
+ family: 2,
34500
+ name: "Arial",
34501
+ },
34502
+ fill: style?.fillColor
34503
+ ? {
34504
+ fgColor: { rgb: style.fillColor },
34505
+ }
34506
+ : { reservedAttribute: "none" },
34507
+ numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
34508
+ border: borderId || 0,
34509
+ alignment: {
34510
+ horizontal: style.align,
34511
+ vertical: style.verticalAlign
34512
+ ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
34513
+ : undefined,
34514
+ wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
34515
+ textRotation: style.rotation ? rotationToXLSX(style.rotation) : undefined,
34516
+ },
34517
+ };
34518
+ styles.font["strike"] = !!style?.strikethrough || undefined;
34519
+ styles.font["underline"] = !!style?.underline || undefined;
34520
+ styles.font["bold"] = !!style?.bold || undefined;
34521
+ styles.font["italic"] = !!style?.italic || undefined;
34522
+ return styles;
34523
+ }
34524
+ function rotationToXLSX(rad) {
34525
+ let deg = Math.round((-rad / Math.PI) * 180) % 180;
34526
+ if (deg > 90) {
34527
+ deg -= 180;
34528
+ }
34529
+ else if (deg < -90) {
34530
+ deg += 180;
34531
+ }
34532
+ if (deg >= 0) {
34533
+ return deg;
34534
+ }
34535
+ else {
34536
+ return 90 - deg;
34537
+ }
34538
+ }
34539
+ function rotationFromXLSX(deg) {
34540
+ if (deg <= 90) {
34541
+ return -(deg / 180) * Math.PI;
34542
+ }
34543
+ else {
34544
+ return (-(90 - deg) / 180) * Math.PI;
34545
+ }
34546
+ }
34547
+ function normalizeStyle(construct, styles) {
34548
+ // Normalize this
34549
+ const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
34550
+ const style = {
34551
+ fontId: pushElement(styles.font, construct.fonts),
34552
+ fillId: pushElement(styles.fill, construct.fills),
34553
+ borderId: styles.border,
34554
+ numFmtId,
34555
+ alignment: {
34556
+ vertical: styles.alignment.vertical,
34557
+ horizontal: styles.alignment.horizontal,
34558
+ wrapText: styles.alignment.wrapText,
34559
+ textRotation: styles.alignment.textRotation,
34560
+ },
34561
+ };
34562
+ return pushElement(style, construct.styles);
34563
+ }
34564
+ function convertFormat(format, numFmtStructure) {
34565
+ if (!format) {
34566
+ return 0;
34567
+ }
34568
+ let formatId = XLSX_FORMAT_MAP[format.format];
34569
+ if (!formatId) {
34570
+ formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
34571
+ }
34572
+ return formatId;
34573
+ }
34574
+ /**
34575
+ * Add a relation to the given file and return its id.
34576
+ */
34577
+ function addRelsToFile(relsFiles, path, rel) {
34578
+ const relsFile = relsFiles.find((file) => file.path === path);
34579
+ // the id is a one-based int casted as string
34580
+ let id;
34581
+ if (!relsFile) {
34582
+ id = "rId1";
34583
+ relsFiles.push({ path, rels: [{ ...rel, id }] });
34584
+ }
34585
+ else {
34586
+ id = `rId${(relsFile.rels.length + 1).toString()}`;
34587
+ relsFile.rels.push({
34588
+ ...rel,
34589
+ id,
34590
+ });
34591
+ }
34592
+ return id;
34593
+ }
34594
+ const globalReverseLookup = new WeakMap();
34595
+ function pushElement(property, propertyList) {
34596
+ let reverseLookup = globalReverseLookup.get(propertyList);
34597
+ if (!reverseLookup) {
34598
+ reverseLookup = new Map();
34599
+ for (let i = 0; i < propertyList.length; i++) {
34600
+ const canonical = getCanonicalRepresentation(propertyList[i]);
34601
+ reverseLookup.set(canonical, i);
34602
+ }
34603
+ globalReverseLookup.set(propertyList, reverseLookup);
34604
+ }
34605
+ const canonical = getCanonicalRepresentation(property);
34606
+ if (reverseLookup.has(canonical)) {
34607
+ return reverseLookup.get(canonical);
34608
+ }
34609
+ const maxId = propertyList.length;
34610
+ propertyList.push(property);
34611
+ reverseLookup.set(canonical, maxId);
34612
+ return maxId;
34613
+ }
34614
+ /**
34615
+ * Convert a chart o-spreadsheet id to a xlsx id which
34616
+ * are unsigned integers (starting from 1).
34617
+ */
34618
+ function convertChartId(chartId, construct) {
34619
+ const xlsxId = construct.chartIds.findIndex((id) => id === chartId);
34620
+ if (xlsxId === -1) {
34621
+ construct.chartIds.push(chartId);
34622
+ return construct.chartIds.length;
34623
+ }
34624
+ return xlsxId + 1;
34625
+ }
34626
+ const imageIds = [];
34627
+ /**
34628
+ * Convert a image o-spreadsheet id to a xlsx id which
34629
+ * are unsigned integers (starting from 1).
34630
+ */
34631
+ function convertImageId(imageId) {
34632
+ const xlsxId = imageIds.findIndex((id) => id === imageId);
34633
+ if (xlsxId === -1) {
34634
+ imageIds.push(imageId);
34635
+ return imageIds.length;
34636
+ }
34637
+ return xlsxId + 1;
34638
+ }
34639
+ /**
34640
+ * Convert a value expressed in dot to EMU.
34641
+ * EMU = English Metrical Unit
34642
+ * There are 914400 EMU per inch.
34643
+ *
34644
+ * /!\ A value expressed in EMU cannot be fractional.
34645
+ * See https://docs.microsoft.com/en-us/windows/win32/vml/msdn-online-vml-units#other-units-of-measurement
34646
+ */
34647
+ function convertDotValueToEMU(value) {
34648
+ const DPI = 96;
34649
+ return Math.round((value * 914400) / DPI);
34650
+ }
34651
+ function getRangeSize(reference, defaultSheetIndex, data) {
34652
+ let xc = reference;
34653
+ let sheetName = undefined;
34654
+ ({ xc, sheetName } = splitReference(reference));
34655
+ let rangeSheetIndex;
34656
+ if (sheetName) {
34657
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
34658
+ if (index < 0) {
34659
+ throw new Error("Unable to find a sheet with the name " + sheetName);
34660
+ }
34661
+ rangeSheetIndex = index;
34662
+ }
34663
+ else {
34664
+ rangeSheetIndex = Number(defaultSheetIndex);
34665
+ }
34666
+ const zone = toUnboundedZone(xc);
34667
+ if (zone.right === undefined) {
34668
+ zone.right = data.sheets[rangeSheetIndex].colNumber;
34669
+ }
34670
+ if (zone.bottom === undefined) {
34671
+ zone.bottom = data.sheets[rangeSheetIndex].rowNumber;
34672
+ }
34673
+ return (zone.right - zone.left + 1) * (zone.bottom - zone.top + 1);
34674
+ }
34675
+ function convertEMUToDotValue(value) {
34676
+ const DPI = 96;
34677
+ return Math.round((value * DPI) / 914400);
34678
+ }
34679
+ /**
34680
+ * Get the position of the start of a column in Excel (in px).
34681
+ */
34682
+ function getColPosition(colIndex, sheetData) {
34683
+ let position = 0;
34684
+ for (let i = 0; i < colIndex; i++) {
34685
+ const colAtIndex = sheetData.cols.find((col) => i >= col.min && i <= col.max);
34686
+ if (colAtIndex?.width) {
34687
+ position += colAtIndex.width;
34688
+ }
34689
+ else if (sheetData.sheetFormat?.defaultColWidth) {
34690
+ position += sheetData.sheetFormat.defaultColWidth;
34691
+ }
34692
+ else {
34693
+ position += EXCEL_DEFAULT_COL_WIDTH;
34694
+ }
34695
+ }
34696
+ return position / WIDTH_FACTOR;
34697
+ }
34698
+ /**
34699
+ * Get the position of the start of a row in Excel (in px).
34700
+ */
34701
+ function getRowPosition(rowIndex, sheetData) {
34702
+ let position = 0;
34703
+ for (let i = 0; i < rowIndex; i++) {
34704
+ const rowAtIndex = sheetData.rows.find((row) => row.index - 1 === i);
34705
+ if (rowAtIndex?.height) {
34706
+ position += rowAtIndex.height;
34707
+ }
34708
+ else if (sheetData.sheetFormat?.defaultRowHeight) {
34709
+ position += sheetData.sheetFormat.defaultRowHeight;
34710
+ }
34711
+ else {
34712
+ position += EXCEL_DEFAULT_ROW_HEIGHT;
34713
+ }
34714
+ }
34715
+ return position / HEIGHT_FACTOR;
34716
+ }
34717
+ /**
34718
+ * Convert the o-spreadsheet data validation decimal
34719
+ * criterion type to the corresponding excel operator.
34720
+ */
34721
+ function convertDecimalCriterionTypeToExcelOperator(operator) {
34722
+ return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
34723
+ }
34724
+ /**
34725
+ * Convert the o-spreadsheet data validation date
34726
+ * criterion type to the corresponding excel operator.
34727
+ */
34728
+ function convertDateCriterionTypeToExcelOperator(operator) {
34729
+ return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
34730
+ }
34731
+
34366
34732
  const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/m|\s|-|\/|\.|:)+$/i;
34367
34733
  /**
34368
34734
  * Convert excel format to o_spreadsheet format
@@ -34462,6 +34828,9 @@ stores.inject(MyMetaStore, storeInstance);
34462
34828
  align: styleStruct.alignment?.horizontal
34463
34829
  ? H_ALIGNMENT_CONVERSION_MAP[styleStruct.alignment.horizontal]
34464
34830
  : undefined,
34831
+ rotation: styleStruct.alignment?.textRotation
34832
+ ? rotationFromXLSX(styleStruct.alignment.textRotation)
34833
+ : undefined,
34465
34834
  // In xlsx fills, bgColor is the color of the fill, and fgColor is the color of the pattern above the background, except in solid fills
34466
34835
  fillColor: styleStruct.fillStyle?.patternType === "solid"
34467
34836
  ? convertColor(styleStruct.fillStyle?.fgColor)
@@ -34761,303 +35130,6 @@ stores.inject(MyMetaStore, storeInstance);
34761
35130
  }
34762
35131
  }
34763
35132
 
34764
- // -------------------------------------
34765
- // CF HELPERS
34766
- // -------------------------------------
34767
- /**
34768
- * Convert the conditional formatting o-spreadsheet operator to
34769
- * the corresponding excel operator.
34770
- * */
34771
- function convertOperator(operator) {
34772
- switch (operator) {
34773
- case "isNotEmpty":
34774
- return "notContainsBlanks";
34775
- case "isEmpty":
34776
- return "containsBlanks";
34777
- case "notContainsText":
34778
- return "notContainsBlanks";
34779
- case "containsText":
34780
- return "containsText";
34781
- case "beginsWithText":
34782
- return "beginsWith";
34783
- case "endsWithText":
34784
- return "endsWith";
34785
- case "isGreaterThan":
34786
- return "greaterThan";
34787
- case "isGreaterOrEqualTo":
34788
- return "greaterThanOrEqual";
34789
- case "isLessThan":
34790
- return "lessThan";
34791
- case "isLessOrEqualTo":
34792
- return "lessThanOrEqual";
34793
- case "isBetween":
34794
- return "between";
34795
- case "isNotBetween":
34796
- return "notBetween";
34797
- case "isEqual":
34798
- return "equal";
34799
- case "isNotEqual":
34800
- return "notEqual";
34801
- case "customFormula":
34802
- return "";
34803
- case "dateIs":
34804
- return "";
34805
- case "dateIsBefore":
34806
- return "lessThan";
34807
- case "dateIsAfter":
34808
- return "greaterThan";
34809
- case "dateIsOnOrAfter":
34810
- return "greaterThanOrEqual";
34811
- case "dateIsOnOrBefore":
34812
- return "lessThanOrEqual";
34813
- }
34814
- }
34815
- // -------------------------------------
34816
- // WORKSHEET HELPERS
34817
- // -------------------------------------
34818
- function getCellType(value) {
34819
- switch (typeof value) {
34820
- case "boolean":
34821
- return "b";
34822
- case "string":
34823
- return "str";
34824
- case "number":
34825
- return "n";
34826
- default:
34827
- return undefined;
34828
- }
34829
- }
34830
- function convertHeightToExcel(height) {
34831
- return Math.round(HEIGHT_FACTOR * height * 100) / 100;
34832
- }
34833
- function convertWidthToExcel(width) {
34834
- return Math.round(WIDTH_FACTOR * width * 100) / 100;
34835
- }
34836
- function convertHeightFromExcel(height) {
34837
- if (!height)
34838
- return height;
34839
- return Math.round((height / HEIGHT_FACTOR) * 100) / 100;
34840
- }
34841
- function convertWidthFromExcel(width) {
34842
- if (!width)
34843
- return width;
34844
- return Math.round((width / WIDTH_FACTOR) * 100) / 100;
34845
- }
34846
- function extractStyle(data, content, styleId, formatId, borderId) {
34847
- const style = styleId ? data.styles[styleId] : {};
34848
- const format = formatId ? data.formats[formatId] : undefined;
34849
- const styles = {
34850
- font: {
34851
- size: style?.fontSize || DEFAULT_FONT_SIZE,
34852
- color: { rgb: style?.textColor ? style.textColor : "000000" },
34853
- family: 2,
34854
- name: "Arial",
34855
- },
34856
- fill: style?.fillColor
34857
- ? {
34858
- fgColor: { rgb: style.fillColor },
34859
- }
34860
- : { reservedAttribute: "none" },
34861
- numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
34862
- border: borderId || 0,
34863
- alignment: {
34864
- horizontal: style.align,
34865
- vertical: style.verticalAlign
34866
- ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
34867
- : undefined,
34868
- wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
34869
- },
34870
- };
34871
- styles.font["strike"] = !!style?.strikethrough || undefined;
34872
- styles.font["underline"] = !!style?.underline || undefined;
34873
- styles.font["bold"] = !!style?.bold || undefined;
34874
- styles.font["italic"] = !!style?.italic || undefined;
34875
- return styles;
34876
- }
34877
- function normalizeStyle(construct, styles) {
34878
- // Normalize this
34879
- const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
34880
- const style = {
34881
- fontId: pushElement(styles.font, construct.fonts),
34882
- fillId: pushElement(styles.fill, construct.fills),
34883
- borderId: styles.border,
34884
- numFmtId,
34885
- alignment: {
34886
- vertical: styles.alignment.vertical,
34887
- horizontal: styles.alignment.horizontal,
34888
- wrapText: styles.alignment.wrapText,
34889
- },
34890
- };
34891
- return pushElement(style, construct.styles);
34892
- }
34893
- function convertFormat(format, numFmtStructure) {
34894
- if (!format) {
34895
- return 0;
34896
- }
34897
- let formatId = XLSX_FORMAT_MAP[format.format];
34898
- if (!formatId) {
34899
- formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
34900
- }
34901
- return formatId;
34902
- }
34903
- /**
34904
- * Add a relation to the given file and return its id.
34905
- */
34906
- function addRelsToFile(relsFiles, path, rel) {
34907
- const relsFile = relsFiles.find((file) => file.path === path);
34908
- // the id is a one-based int casted as string
34909
- let id;
34910
- if (!relsFile) {
34911
- id = "rId1";
34912
- relsFiles.push({ path, rels: [{ ...rel, id }] });
34913
- }
34914
- else {
34915
- id = `rId${(relsFile.rels.length + 1).toString()}`;
34916
- relsFile.rels.push({
34917
- ...rel,
34918
- id,
34919
- });
34920
- }
34921
- return id;
34922
- }
34923
- const globalReverseLookup = new WeakMap();
34924
- function pushElement(property, propertyList) {
34925
- let reverseLookup = globalReverseLookup.get(propertyList);
34926
- if (!reverseLookup) {
34927
- reverseLookup = new Map();
34928
- for (let i = 0; i < propertyList.length; i++) {
34929
- const canonical = getCanonicalRepresentation(propertyList[i]);
34930
- reverseLookup.set(canonical, i);
34931
- }
34932
- globalReverseLookup.set(propertyList, reverseLookup);
34933
- }
34934
- const canonical = getCanonicalRepresentation(property);
34935
- if (reverseLookup.has(canonical)) {
34936
- return reverseLookup.get(canonical);
34937
- }
34938
- const maxId = propertyList.length;
34939
- propertyList.push(property);
34940
- reverseLookup.set(canonical, maxId);
34941
- return maxId;
34942
- }
34943
- /**
34944
- * Convert a chart o-spreadsheet id to a xlsx id which
34945
- * are unsigned integers (starting from 1).
34946
- */
34947
- function convertChartId(chartId, construct) {
34948
- const xlsxId = construct.chartIds.findIndex((id) => id === chartId);
34949
- if (xlsxId === -1) {
34950
- construct.chartIds.push(chartId);
34951
- return construct.chartIds.length;
34952
- }
34953
- return xlsxId + 1;
34954
- }
34955
- const imageIds = [];
34956
- /**
34957
- * Convert a image o-spreadsheet id to a xlsx id which
34958
- * are unsigned integers (starting from 1).
34959
- */
34960
- function convertImageId(imageId) {
34961
- const xlsxId = imageIds.findIndex((id) => id === imageId);
34962
- if (xlsxId === -1) {
34963
- imageIds.push(imageId);
34964
- return imageIds.length;
34965
- }
34966
- return xlsxId + 1;
34967
- }
34968
- /**
34969
- * Convert a value expressed in dot to EMU.
34970
- * EMU = English Metrical Unit
34971
- * There are 914400 EMU per inch.
34972
- *
34973
- * /!\ A value expressed in EMU cannot be fractional.
34974
- * See https://docs.microsoft.com/en-us/windows/win32/vml/msdn-online-vml-units#other-units-of-measurement
34975
- */
34976
- function convertDotValueToEMU(value) {
34977
- const DPI = 96;
34978
- return Math.round((value * 914400) / DPI);
34979
- }
34980
- function getRangeSize(reference, defaultSheetIndex, data) {
34981
- let xc = reference;
34982
- let sheetName = undefined;
34983
- ({ xc, sheetName } = splitReference(reference));
34984
- let rangeSheetIndex;
34985
- if (sheetName) {
34986
- const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
34987
- if (index < 0) {
34988
- throw new Error("Unable to find a sheet with the name " + sheetName);
34989
- }
34990
- rangeSheetIndex = index;
34991
- }
34992
- else {
34993
- rangeSheetIndex = Number(defaultSheetIndex);
34994
- }
34995
- const zone = toUnboundedZone(xc);
34996
- if (zone.right === undefined) {
34997
- zone.right = data.sheets[rangeSheetIndex].colNumber;
34998
- }
34999
- if (zone.bottom === undefined) {
35000
- zone.bottom = data.sheets[rangeSheetIndex].rowNumber;
35001
- }
35002
- return (zone.right - zone.left + 1) * (zone.bottom - zone.top + 1);
35003
- }
35004
- function convertEMUToDotValue(value) {
35005
- const DPI = 96;
35006
- return Math.round((value * DPI) / 914400);
35007
- }
35008
- /**
35009
- * Get the position of the start of a column in Excel (in px).
35010
- */
35011
- function getColPosition(colIndex, sheetData) {
35012
- let position = 0;
35013
- for (let i = 0; i < colIndex; i++) {
35014
- const colAtIndex = sheetData.cols.find((col) => i >= col.min && i <= col.max);
35015
- if (colAtIndex?.width) {
35016
- position += colAtIndex.width;
35017
- }
35018
- else if (sheetData.sheetFormat?.defaultColWidth) {
35019
- position += sheetData.sheetFormat.defaultColWidth;
35020
- }
35021
- else {
35022
- position += EXCEL_DEFAULT_COL_WIDTH;
35023
- }
35024
- }
35025
- return position / WIDTH_FACTOR;
35026
- }
35027
- /**
35028
- * Get the position of the start of a row in Excel (in px).
35029
- */
35030
- function getRowPosition(rowIndex, sheetData) {
35031
- let position = 0;
35032
- for (let i = 0; i < rowIndex; i++) {
35033
- const rowAtIndex = sheetData.rows.find((row) => row.index - 1 === i);
35034
- if (rowAtIndex?.height) {
35035
- position += rowAtIndex.height;
35036
- }
35037
- else if (sheetData.sheetFormat?.defaultRowHeight) {
35038
- position += sheetData.sheetFormat.defaultRowHeight;
35039
- }
35040
- else {
35041
- position += EXCEL_DEFAULT_ROW_HEIGHT;
35042
- }
35043
- }
35044
- return position / HEIGHT_FACTOR;
35045
- }
35046
- /**
35047
- * Convert the o-spreadsheet data validation decimal
35048
- * criterion type to the corresponding excel operator.
35049
- */
35050
- function convertDecimalCriterionTypeToExcelOperator(operator) {
35051
- return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
35052
- }
35053
- /**
35054
- * Convert the o-spreadsheet data validation date
35055
- * criterion type to the corresponding excel operator.
35056
- */
35057
- function convertDateCriterionTypeToExcelOperator(operator) {
35058
- return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
35059
- }
35060
-
35061
35133
  function convertFigures(sheetData) {
35062
35134
  let id = 1;
35063
35135
  return sheetData.figures
@@ -40211,7 +40283,7 @@ stores.inject(MyMetaStore, storeInstance);
40211
40283
  return false;
40212
40284
  }
40213
40285
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
40214
- const today = jsDateToRoundNumber(DateTime.now());
40286
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
40215
40287
  return isDateBetween(dateValue, today, criterionValue);
40216
40288
  }
40217
40289
  return areDatesSameDay(dateValue, criterionValue);
@@ -44638,6 +44710,7 @@ stores.inject(MyMetaStore, storeInstance);
44638
44710
  "getMeasureCompiledFormula",
44639
44711
  "getPivotName",
44640
44712
  "isExistingPivot",
44713
+ "getMeasureFullDependencies",
44641
44714
  ];
44642
44715
  nextFormulaId = 1;
44643
44716
  pivots = {};
@@ -44723,7 +44796,7 @@ stores.inject(MyMetaStore, storeInstance);
44723
44796
  }
44724
44797
  case "UPDATE_PIVOT": {
44725
44798
  this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
44726
- this.compileCalculatedMeasures(cmd.pivot.measures);
44799
+ this.compileCalculatedMeasures(cmd.pivotId, cmd.pivot.measures);
44727
44800
  break;
44728
44801
  }
44729
44802
  }
@@ -44741,9 +44814,14 @@ stores.inject(MyMetaStore, storeInstance);
44741
44814
  this.history.update("pivots", pivotId, "definition", newDefinition);
44742
44815
  }
44743
44816
  }
44744
- for (const sheetId in this.compiledMeasureFormulas) {
44745
- for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
44746
- const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
44817
+ for (const pivotId in this.compiledMeasureFormulas) {
44818
+ for (const measureId in this.compiledMeasureFormulas[pivotId]) {
44819
+ const measure = this.pivots[pivotId]?.definition.measures.find((m) => m.id === measureId);
44820
+ if (!measure || !measure.computedBy) {
44821
+ continue;
44822
+ }
44823
+ const sheetId = measure.computedBy.sheetId;
44824
+ const compiledFormula = this.compiledMeasureFormulas[pivotId][measureId].formula;
44747
44825
  const newDependencies = [];
44748
44826
  for (const range of compiledFormula.dependencies) {
44749
44827
  const change = applyChange(range);
@@ -44755,8 +44833,9 @@ stores.inject(MyMetaStore, storeInstance);
44755
44833
  }
44756
44834
  }
44757
44835
  const newFormulaString = this.getters.getFormulaString(sheetId, compiledFormula.tokens, newDependencies);
44758
- if (newFormulaString !== formulaString) {
44759
- this.replaceMeasureFormula(sheetId, formulaString, newFormulaString);
44836
+ const oldFormulaString = measure.computedBy.formula;
44837
+ if (newFormulaString !== oldFormulaString) {
44838
+ this.replaceMeasureFormula(pivotId, measure, newFormulaString);
44760
44839
  }
44761
44840
  }
44762
44841
  }
@@ -44794,31 +44873,60 @@ stores.inject(MyMetaStore, storeInstance);
44794
44873
  isExistingPivot(pivotId) {
44795
44874
  return pivotId in this.pivots;
44796
44875
  }
44797
- getMeasureCompiledFormula(measure) {
44876
+ getMeasureCompiledFormula(pivotId, measure) {
44798
44877
  if (!measure.computedBy) {
44799
44878
  throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
44800
44879
  }
44801
- const sheetId = measure.computedBy.sheetId;
44802
- return this.compiledMeasureFormulas[sheetId][measure.computedBy.formula];
44880
+ return this.compiledMeasureFormulas[pivotId][measure.id].formula;
44881
+ }
44882
+ getMeasureFullDependencies(pivotId, measure) {
44883
+ if (!measure.computedBy) {
44884
+ throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
44885
+ }
44886
+ return this.compiledMeasureFormulas[pivotId][measure.id].dependencies;
44803
44887
  }
44804
44888
  // -------------------------------------------------------------------------
44805
44889
  // Private
44806
44890
  // -------------------------------------------------------------------------
44807
44891
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
44808
44892
  this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
44809
- this.compileCalculatedMeasures(pivot.measures);
44893
+ this.compileCalculatedMeasures(pivotId, pivot.measures);
44810
44894
  this.history.update("formulaIds", formulaId, pivotId);
44811
44895
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
44812
44896
  }
44813
- compileCalculatedMeasures(measures) {
44897
+ compileCalculatedMeasures(pivotId, measures) {
44814
44898
  for (const measure of measures) {
44815
44899
  if (measure.computedBy) {
44816
- const sheetId = measure.computedBy.sheetId;
44817
44900
  const compiledFormula = this.compileMeasureFormula(measure.computedBy.sheetId, measure.computedBy.formula);
44818
- this.history.update("compiledMeasureFormulas", sheetId, measure.computedBy.formula, compiledFormula);
44901
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "formula", compiledFormula);
44902
+ }
44903
+ }
44904
+ for (const measure of measures) {
44905
+ if (measure.computedBy) {
44906
+ const dependencies = this.computeMeasureFullDependencies(pivotId, measure);
44907
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "dependencies", dependencies);
44819
44908
  }
44820
44909
  }
44821
44910
  }
44911
+ computeMeasureFullDependencies(pivotId, measure, exploredMeasures = new Set()) {
44912
+ const rangeDependencies = [];
44913
+ const definition = this.getPivotCoreDefinition(pivotId);
44914
+ const formula = this.getMeasureCompiledFormula(pivotId, measure);
44915
+ exploredMeasures.add(measure.id);
44916
+ for (const token of formula.tokens) {
44917
+ if (token.type !== "SYMBOL") {
44918
+ continue;
44919
+ }
44920
+ const otherMeasure = definition.measures.find((measureCandidate) => getCanonicalSymbolName(measureCandidate.id) === token.value &&
44921
+ measure.id !== measureCandidate.id);
44922
+ if (!otherMeasure || exploredMeasures.has(otherMeasure.id) || !otherMeasure.computedBy) {
44923
+ continue;
44924
+ }
44925
+ rangeDependencies.push(...this.computeMeasureFullDependencies(pivotId, otherMeasure, exploredMeasures));
44926
+ }
44927
+ rangeDependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
44928
+ return rangeDependencies;
44929
+ }
44822
44930
  insertPivot(position, formulaId, table) {
44823
44931
  this.resizeSheet(position.sheetId, position, table);
44824
44932
  const pivotCells = table.getPivotCells();
@@ -44877,21 +44985,17 @@ stores.inject(MyMetaStore, storeInstance);
44877
44985
  dependencies: rangeDependencies,
44878
44986
  };
44879
44987
  }
44880
- replaceMeasureFormula(sheetId, formulaString, newFormulaString) {
44881
- this.history.update("compiledMeasureFormulas", sheetId, formulaString, undefined);
44882
- this.history.update("compiledMeasureFormulas", sheetId, newFormulaString, this.compileMeasureFormula(sheetId, newFormulaString));
44883
- for (const pivotId in this.pivots) {
44884
- const pivot = this.pivots[pivotId];
44885
- if (!pivot) {
44886
- continue;
44887
- }
44888
- for (const measure of pivot.definition.measures) {
44889
- if (measure.computedBy?.formula === formulaString) {
44890
- const measureIndex = pivot.definition.measures.indexOf(measure);
44891
- this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
44892
- }
44893
- }
44988
+ replaceMeasureFormula(pivotId, measure, newFormulaString) {
44989
+ const pivot = this.pivots[pivotId];
44990
+ if (!pivot) {
44991
+ return;
44894
44992
  }
44993
+ const measureIndex = pivot.definition.measures.indexOf(measure);
44994
+ this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", {
44995
+ formula: newFormulaString,
44996
+ sheetId: measure.computedBy.sheetId,
44997
+ });
44998
+ this.compileCalculatedMeasures(pivotId, pivot.definition.measures);
44895
44999
  }
44896
45000
  checkSortedColumnInMeasures(definition) {
44897
45001
  const measures = definition.measures.map((measure) => measure.id);
@@ -45952,7 +46056,7 @@ stores.inject(MyMetaStore, storeInstance);
45952
46056
  case "UPDATE_CELL":
45953
46057
  if (cmd.style !== undefined) {
45954
46058
  if (cmd.style !== null) {
45955
- this.setStyles(cmd.sheetId, [positionToZone(cmd)], cmd.style);
46059
+ this.setStyles(cmd.sheetId, [positionToZone(cmd)], cmd.style, { force: true });
45956
46060
  }
45957
46061
  else {
45958
46062
  this.clearStyle(cmd.sheetId, [positionToZone(cmd)]);
@@ -46020,16 +46124,22 @@ stores.inject(MyMetaStore, storeInstance);
46020
46124
  }
46021
46125
  }
46022
46126
  styleIsDefault(style) {
46023
- return deepEquals(this.removeDefaultStyleValues(style), {});
46127
+ for (const key in style) {
46128
+ if (DEFAULT_STYLE_NO_ALIGN[key] !== style[key]) {
46129
+ return false;
46130
+ }
46131
+ }
46132
+ return true;
46024
46133
  }
46025
46134
  removeDefaultStyleValues(style) {
46026
46135
  const cleanedStyle = { ...style };
46027
- for (const property in DEFAULT_STYLE_NO_ALIGN) {
46028
- if (cleanedStyle[property] === DEFAULT_STYLE_NO_ALIGN[property]) {
46136
+ for (const property in style) {
46137
+ if (cleanedStyle[property] === undefined ||
46138
+ cleanedStyle[property] === DEFAULT_STYLE_NO_ALIGN[property]) {
46029
46139
  delete cleanedStyle[property];
46030
46140
  }
46031
46141
  }
46032
- return cleanedStyle;
46142
+ return Object.keys(cleanedStyle).length > 0 ? cleanedStyle : undefined;
46033
46143
  }
46034
46144
  onMerge(sheetId, zone) {
46035
46145
  this.setStyle(sheetId, zone, this.getCellStyle({ sheetId, col: zone.left, row: zone.top }), {
@@ -46065,13 +46175,13 @@ stores.inject(MyMetaStore, storeInstance);
46065
46175
  }
46066
46176
  editingZone = recomputeZones(editingZone, [inter]);
46067
46177
  }
46178
+ style = this.removeDefaultStyleValues(style);
46068
46179
  if (style) {
46069
- const newStyle = this.removeDefaultStyleValues(style);
46070
46180
  styles.push(...editingZone.map((zone) => {
46071
- return { zone, style: newStyle };
46181
+ return { zone, style };
46072
46182
  }));
46073
46183
  }
46074
- this.history.update("styles", sheetId, styles.filter((zoneStyle) => !this.styleIsDefault(zoneStyle.style)));
46184
+ this.history.update("styles", sheetId, styles);
46075
46185
  }
46076
46186
  clearStyle(sheetId, zones) {
46077
46187
  this.setStyles(sheetId, zones, undefined, { force: true });
@@ -52000,14 +52110,16 @@ stores.inject(MyMetaStore, storeInstance);
52000
52110
  function withPivotPresentationLayer (PivotClass) {
52001
52111
  class PivotPresentationLayer extends PivotClass {
52002
52112
  getters;
52113
+ pivotId;
52003
52114
  cache = {};
52004
52115
  rankAsc = {};
52005
52116
  rankDesc = {};
52006
52117
  runningTotal = {};
52007
52118
  runningTotalInPercent = {};
52008
- constructor(custom, params) {
52119
+ constructor(pivotId, custom, params) {
52009
52120
  super(custom, params);
52010
52121
  this.getters = params.getters;
52122
+ this.pivotId = pivotId;
52011
52123
  }
52012
52124
  markAsDirtyForEvaluation() {
52013
52125
  this.cache = {};
@@ -52057,7 +52169,7 @@ stores.inject(MyMetaStore, storeInstance);
52057
52169
  return handleError(error, measure.aggregator.toUpperCase());
52058
52170
  }
52059
52171
  }
52060
- const formula = this.getters.getMeasureCompiledFormula(measure);
52172
+ const formula = this.getters.getMeasureCompiledFormula(this.pivotId, measure);
52061
52173
  const getSymbolValue = (symbolName) => {
52062
52174
  const { columns, rows } = this.definition;
52063
52175
  if (columns.find((col) => col.nameWithGranularity === symbolName)) {
@@ -52804,7 +52916,7 @@ stores.inject(MyMetaStore, storeInstance);
52804
52916
  const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
52805
52917
  if (!(pivotId in this.pivots)) {
52806
52918
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
52807
- this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
52919
+ this.pivots[pivotId] = new Pivot(pivotId, this.custom, { definition, getters: this.getters });
52808
52920
  }
52809
52921
  else if (recreate) {
52810
52922
  this.pivots[pivotId].onDefinitionChange(definition);
@@ -61902,6 +62014,9 @@ stores.inject(MyMetaStore, storeInstance);
61902
62014
  if (style.alignment && style.alignment.wrapText) {
61903
62015
  alignAttrs.push(["wrapText", "1"]);
61904
62016
  }
62017
+ if (style.alignment && style.alignment.textRotation) {
62018
+ alignAttrs.push(["textRotation", style.alignment.textRotation]);
62019
+ }
61905
62020
  if (alignAttrs.length > 0) {
61906
62021
  attributes.push(["applyAlignment", "1"]); // for Libre Office
61907
62022
  styleNodes.push(escapeXml /*xml*/ `<xf ${formatAttributes(attributes)}><alignment ${formatAttributes(alignAttrs)} /></xf> `);
@@ -72202,7 +72317,7 @@ stores.inject(MyMetaStore, storeInstance);
72202
72317
  }
72203
72318
  captureSelection(zone, col, row) {
72204
72319
  this.model.selection.capture(this, {
72205
- cell: { col: col ?? zone.left, row: row ?? zone.right },
72320
+ cell: { col: col ?? zone.left, row: row ?? zone.top },
72206
72321
  zone,
72207
72322
  }, {
72208
72323
  handleEvent: this.handleEvent.bind(this),
@@ -77925,35 +78040,46 @@ stores.inject(MyMetaStore, storeInstance);
77925
78040
  name: _t("Rotation"),
77926
78041
  icon: (env) => getRotationIcon(env),
77927
78042
  };
78043
+ function setRotation(env, rotation) {
78044
+ rotation = Math.trunc(rotation / ROTATION_EPSILON) * ROTATION_EPSILON;
78045
+ setStyle(env, { rotation });
78046
+ }
78047
+ function currentRotationEqual(env, rotation) {
78048
+ const current = env.model.getters.getCurrentStyle().rotation;
78049
+ if (current === undefined) {
78050
+ return rotation === 0;
78051
+ }
78052
+ return Math.abs(current - rotation) < ROTATION_EPSILON;
78053
+ }
77928
78054
  const formatNoRotation = {
77929
78055
  name: _t("No rotation"),
77930
78056
  execute: (env) => setStyle(env, { rotation: 0 }),
77931
78057
  icon: "o-spreadsheet-Icon.ROTATION-0",
77932
- isActive: (env) => env.model.getters.getCurrentStyle().rotation === 0,
78058
+ isActive: (env) => currentRotationEqual(env, 0),
77933
78059
  };
77934
78060
  const formatRotation45 = {
77935
78061
  name: _t("45° rotation"),
77936
- execute: (env) => setStyle(env, { rotation: Math.PI / 4 }),
78062
+ execute: (env) => setRotation(env, Math.PI / 4),
77937
78063
  icon: "o-spreadsheet-Icon.ROTATION-45",
77938
- isActive: (env) => env.model.getters.getCurrentStyle().rotation === Math.PI / 4,
78064
+ isActive: (env) => currentRotationEqual(env, Math.PI / 4),
77939
78065
  };
77940
78066
  const formatRotation90 = {
77941
78067
  name: _t("90° rotation"),
77942
- execute: (env) => setStyle(env, { rotation: Math.PI / 2 }),
78068
+ execute: (env) => setRotation(env, Math.PI / 2),
77943
78069
  icon: "o-spreadsheet-Icon.ROTATION-90",
77944
- isActive: (env) => env.model.getters.getCurrentStyle().rotation === Math.PI / 2,
78070
+ isActive: (env) => currentRotationEqual(env, Math.PI / 2),
77945
78071
  };
77946
78072
  const formatRotation270 = {
77947
78073
  name: _t("-90° rotation"),
77948
- execute: (env) => setStyle(env, { rotation: -Math.PI / 2 }),
78074
+ execute: (env) => setRotation(env, -Math.PI / 2),
77949
78075
  icon: "o-spreadsheet-Icon.ROTATION-270",
77950
- isActive: (env) => env.model.getters.getCurrentStyle().rotation === -Math.PI / 2,
78076
+ isActive: (env) => currentRotationEqual(env, -Math.PI / 2),
77951
78077
  };
77952
78078
  const formatRotation315 = {
77953
78079
  name: _t("-45° rotation"),
77954
- execute: (env) => setStyle(env, { rotation: -Math.PI / 4 }),
78080
+ execute: (env) => setRotation(env, -Math.PI / 4),
77955
78081
  icon: "o-spreadsheet-Icon.ROTATION-315",
77956
- isActive: (env) => env.model.getters.getCurrentStyle().rotation === -Math.PI / 4,
78082
+ isActive: (env) => currentRotationEqual(env, -Math.PI / 4),
77957
78083
  };
77958
78084
  const formatStrikethrough = {
77959
78085
  name: _t("Strikethrough"),
@@ -78119,10 +78245,6 @@ stores.inject(MyMetaStore, storeInstance);
78119
78245
  }
78120
78246
  return DEFAULT_WRAPPING_MODE;
78121
78247
  }
78122
- function getRotation(env) {
78123
- const style = env.model.getters.getCurrentStyle();
78124
- return style.rotation ?? 0;
78125
- }
78126
78248
  function getHorizontalAlignmentIcon(env) {
78127
78249
  const horizontalAlign = getHorizontalAlign(env);
78128
78250
  switch (horizontalAlign) {
@@ -78157,19 +78279,19 @@ stores.inject(MyMetaStore, storeInstance);
78157
78279
  }
78158
78280
  }
78159
78281
  function getRotationIcon(env) {
78160
- const rotation = getRotation(env);
78161
- switch (rotation) {
78162
- case Math.PI / 2:
78163
- return "o-spreadsheet-Icon.ROTATION-90";
78164
- case -Math.PI / 2:
78165
- return "o-spreadsheet-Icon.ROTATION-270";
78166
- case Math.PI / 4:
78167
- return "o-spreadsheet-Icon.ROTATION-45";
78168
- case -Math.PI / 4:
78169
- return "o-spreadsheet-Icon.ROTATION-315";
78170
- default:
78171
- return "o-spreadsheet-Icon.ROTATION-0";
78282
+ if (currentRotationEqual(env, Math.PI / 2)) {
78283
+ return "o-spreadsheet-Icon.ROTATION-90";
78284
+ }
78285
+ else if (currentRotationEqual(env, -Math.PI / 2)) {
78286
+ return "o-spreadsheet-Icon.ROTATION-270";
78172
78287
  }
78288
+ else if (currentRotationEqual(env, Math.PI / 4)) {
78289
+ return "o-spreadsheet-Icon.ROTATION-45";
78290
+ }
78291
+ else if (currentRotationEqual(env, -Math.PI / 4)) {
78292
+ return "o-spreadsheet-Icon.ROTATION-315";
78293
+ }
78294
+ return "o-spreadsheet-Icon.ROTATION-0";
78173
78295
  }
78174
78296
 
78175
78297
  const colMenuRegistry = new MenuItemRegistry();
@@ -80269,7 +80391,6 @@ stores.inject(MyMetaStore, storeInstance);
80269
80391
  onGridMoved: Function,
80270
80392
  gridOverlayDimensions: String,
80271
80393
  slots: { type: Object, optional: true },
80272
- getGridSize: Function,
80273
80394
  };
80274
80395
  static components = {
80275
80396
  FiguresContainer,
@@ -80288,14 +80409,7 @@ stores.inject(MyMetaStore, storeInstance);
80288
80409
  setup() {
80289
80410
  useCellHovered(this.env, this.gridOverlay);
80290
80411
  const resizeObserver = new ResizeObserver(() => {
80291
- const boundingRect = this.gridOverlayEl.getBoundingClientRect();
80292
- const { width, height } = this.props.getGridSize();
80293
- this.props.onGridResized({
80294
- x: boundingRect.left,
80295
- y: boundingRect.top,
80296
- height: height,
80297
- width: width,
80298
- });
80412
+ this.props.onGridResized();
80299
80413
  });
80300
80414
  owl.onMounted(() => {
80301
80415
  resizeObserver.observe(this.gridOverlayEl);
@@ -86389,6 +86503,12 @@ stores.inject(MyMetaStore, storeInstance);
86389
86503
  case "ACTIVATE_SHEET":
86390
86504
  this.isSearchDirty = true;
86391
86505
  this.shouldFinalizeUpdateSelection = true;
86506
+ if (this.searchOptions.specificRange) {
86507
+ this.searchOptions.specificRange = {
86508
+ ...this.searchOptions.specificRange,
86509
+ sheetId: this.getters.getActiveSheetId(),
86510
+ };
86511
+ }
86392
86512
  break;
86393
86513
  case "REPLACE_SEARCH":
86394
86514
  for (const match of cmd.matches) {
@@ -86775,9 +86895,20 @@ stores.inject(MyMetaStore, storeInstance);
86775
86895
  const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.state.dataRange);
86776
86896
  this.store.updateSearchOptions({ specificRange });
86777
86897
  }
86898
+ get specificRange() {
86899
+ const range = this.store.searchOptions.specificRange;
86900
+ return range ? this.env.model.getters.getRangeString(range, "forceSheetReference") : "";
86901
+ }
86778
86902
  get pendingSearch() {
86779
86903
  return this.updateSearchContent.isDebouncePending();
86780
86904
  }
86905
+ get selectionInputKey() {
86906
+ // Selections input are made to work with objects linked to a sheet id. They store the active sheet id at their creation,
86907
+ // and have specific behaviour linked to it (eg. go back to the initial sheet after confirmation).
86908
+ // We don't want all those behaviors here, so we force the recreation of the component when the active sheet changes.
86909
+ // The only drawback is that the input loses focus when changing sheet.
86910
+ return this.env.model.getters.getActiveSheetId();
86911
+ }
86781
86912
  }
86782
86913
 
86783
86914
  /**
@@ -89891,7 +90022,8 @@ stores.inject(MyMetaStore, storeInstance);
89891
90022
  });
89892
90023
  return !(rect.width === 0 || rect.height === 0);
89893
90024
  }
89894
- onGridResized({ height, width }) {
90025
+ onGridResized() {
90026
+ const { height, width } = this.props.getGridSize();
89895
90027
  this.env.model.dispatch("RESIZE_SHEETVIEW", {
89896
90028
  width: width - HEADER_WIDTH,
89897
90029
  height: height - HEADER_HEIGHT,
@@ -93439,10 +93571,8 @@ stores.inject(MyMetaStore, storeInstance);
93439
93571
  });
93440
93572
  }
93441
93573
  get gridContainer() {
93442
- const sheetId = this.env.model.getters.getActiveSheetId();
93443
- const { right } = this.env.model.getters.getSheetZone(sheetId);
93444
- const { end } = this.env.model.getters.getColDimensions(sheetId, right);
93445
- return cssPropertiesToCss({ "max-width": `${end}px` });
93574
+ const maxWidth = this.getMaxSheetWidth();
93575
+ return cssPropertiesToCss({ "max-width": `${maxWidth}px` });
93446
93576
  }
93447
93577
  get gridOverlayDimensions() {
93448
93578
  return cssPropertiesToCss({
@@ -93474,10 +93604,12 @@ stores.inject(MyMetaStore, storeInstance);
93474
93604
  onClosePopover() {
93475
93605
  this.cellPopovers.close();
93476
93606
  }
93477
- onGridResized({ height, width }) {
93607
+ onGridResized() {
93608
+ const { height, width } = this.props.getGridSize();
93609
+ const maxWidth = this.getMaxSheetWidth();
93478
93610
  this.env.model.dispatch("RESIZE_SHEETVIEW", {
93479
- width: width,
93480
- height: height,
93611
+ width: Math.min(maxWidth, width),
93612
+ height,
93481
93613
  gridOffsetX: 0,
93482
93614
  gridOffsetY: 0,
93483
93615
  });
@@ -93495,6 +93627,11 @@ stores.inject(MyMetaStore, storeInstance);
93495
93627
  ...this.env.model.getters.getSheetViewDimensionWithHeaders(),
93496
93628
  };
93497
93629
  }
93630
+ getMaxSheetWidth() {
93631
+ const sheetId = this.env.model.getters.getActiveSheetId();
93632
+ const { right } = this.env.model.getters.getSheetZone(sheetId);
93633
+ return this.env.model.getters.getColDimensions(sheetId, right).end;
93634
+ }
93498
93635
  }
93499
93636
 
93500
93637
  class AbstractHeaderGroup extends owl.Component {
@@ -97950,9 +98087,9 @@ stores.inject(MyMetaStore, storeInstance);
97950
98087
  exports.tokenize = tokenize;
97951
98088
 
97952
98089
 
97953
- __info__.version = "19.1.1";
97954
- __info__.date = "2025-12-26T10:26:46.141Z";
97955
- __info__.hash = "e6313bd";
98090
+ __info__.version = "19.1.3";
98091
+ __info__.date = "2026-01-14T10:02:32.431Z";
98092
+ __info__.hash = "52a3e52";
97956
98093
 
97957
98094
 
97958
98095
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);