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