@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.
@@ -3,8 +3,8 @@
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
5
  * @version 19.1.0-alpha.3
6
- * @date 2025-12-26T10:26:09.971Z
7
- * @hash e6313bd
6
+ * @date 2026-01-14T10:01:54.190Z
7
+ * @hash 52a3e52
8
8
  */
9
9
 
10
10
  (function (exports) {
@@ -15076,9 +15076,10 @@
15076
15076
  throw new EvaluationError(_t("Function PIVOT takes an even number of arguments."));
15077
15077
  }
15078
15078
  }
15079
- function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
15079
+ function addPivotDependencies(evalContext, pivotId, forMeasures) {
15080
15080
  //TODO This function can be very costly when used with PIVOT.VALUE and PIVOT.HEADER
15081
15081
  const dependencies = [];
15082
+ const coreDefinition = evalContext.getters.getPivotCoreDefinition(pivotId);
15082
15083
  if (coreDefinition.type === "SPREADSHEET" && coreDefinition.dataSet) {
15083
15084
  const { sheetId, zone } = coreDefinition.dataSet;
15084
15085
  const xc = zoneToXc(zone);
@@ -15095,8 +15096,7 @@
15095
15096
  }
15096
15097
  for (const measure of forMeasures) {
15097
15098
  if (measure.computedBy) {
15098
- const formula = evalContext.getters.getMeasureCompiledFormula(measure);
15099
- dependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
15099
+ dependencies.push(...evalContext.getters.getMeasureFullDependencies(pivotId, measure));
15100
15100
  }
15101
15101
  }
15102
15102
  const originPosition = evalContext.__originCellPosition;
@@ -15593,7 +15593,7 @@
15593
15593
  assertDomainLength(domainArgs);
15594
15594
  const pivot = this.getters.getPivot(pivotId);
15595
15595
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
15596
- addPivotDependencies(this, coreDefinition, coreDefinition.measures.filter((m) => m.id === _measure));
15596
+ addPivotDependencies(this, pivotId, coreDefinition.measures.filter((m) => m.id === _measure));
15597
15597
  pivot.init({ reload: pivot.needsReevaluation });
15598
15598
  const error = pivot.assertIsValid({ throwOnError: false });
15599
15599
  if (error) {
@@ -15626,8 +15626,7 @@
15626
15626
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
15627
15627
  assertDomainLength(domainArgs);
15628
15628
  const pivot = this.getters.getPivot(_pivotId);
15629
- const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
15630
- addPivotDependencies(this, coreDefinition, []);
15629
+ addPivotDependencies(this, _pivotId, []);
15631
15630
  pivot.init({ reload: pivot.needsReevaluation });
15632
15631
  const error = pivot.assertIsValid({ throwOnError: false });
15633
15632
  if (error) {
@@ -15679,7 +15678,7 @@
15679
15678
  if (pivotStyle.numberOfColumns < 0) {
15680
15679
  return new EvaluationError(_t("The number of columns must be positive."));
15681
15680
  }
15682
- addPivotDependencies(this, coreDefinition, coreDefinition.measures);
15681
+ addPivotDependencies(this, pivotId, coreDefinition.measures);
15683
15682
  pivot.init({ reload: pivot.needsReevaluation });
15684
15683
  const error = pivot.assertIsValid({ throwOnError: false });
15685
15684
  if (error) {
@@ -18115,7 +18114,7 @@
18115
18114
  if (message.type === "CLIENT_JOINED" ||
18116
18115
  message.type === "CLIENT_LEFT" ||
18117
18116
  message.type === "CLIENT_MOVED") {
18118
- this.transportService.sendMessage(message);
18117
+ await this.transportService.sendMessage(message);
18119
18118
  }
18120
18119
  // ignore all other messages
18121
18120
  }
@@ -20734,7 +20733,7 @@
20734
20733
  }
20735
20734
  delete this.clients[this.clientId];
20736
20735
  this.transportService.leave(this.clientId);
20737
- this.transportService.sendMessage({
20736
+ this.sendToTransport({
20738
20737
  type: "CLIENT_LEFT",
20739
20738
  clientId: this.clientId,
20740
20739
  version: MESSAGE_VERSION,
@@ -20748,7 +20747,7 @@
20748
20747
  return;
20749
20748
  }
20750
20749
  const snapshotId = this.uuidGenerator.uuidv4();
20751
- await this.transportService.sendMessage({
20750
+ await this.sendToTransport({
20752
20751
  type: "SNAPSHOT",
20753
20752
  nextRevisionId: snapshotId,
20754
20753
  serverRevisionId: this.serverRevisionId,
@@ -20796,10 +20795,14 @@
20796
20795
  const type = currentPosition ? "CLIENT_MOVED" : "CLIENT_JOINED";
20797
20796
  const client = this.getCurrentClient();
20798
20797
  this.clients[this.clientId] = { ...client, position };
20799
- this.transportService.sendMessage({
20798
+ this.sendToTransport({
20800
20799
  type,
20801
20800
  version: MESSAGE_VERSION,
20802
20801
  client: { ...client, position },
20802
+ }).then(() => {
20803
+ if (this.pendingMessages.length > 0 && !this.waitingAck) {
20804
+ this.sendPendingMessage();
20805
+ }
20803
20806
  });
20804
20807
  }
20805
20808
  /**
@@ -20880,7 +20883,7 @@
20880
20883
  if (client) {
20881
20884
  const { position } = client;
20882
20885
  if (position) {
20883
- this.transportService.sendMessage({
20886
+ this.sendToTransport({
20884
20887
  type: "CLIENT_MOVED",
20885
20888
  version: MESSAGE_VERSION,
20886
20889
  client: { ...client, position },
@@ -20901,6 +20904,10 @@
20901
20904
  }
20902
20905
  this.sendPendingMessage();
20903
20906
  }
20907
+ async sendToTransport(message) {
20908
+ // wrap in an async function to ensure it returns a promise
20909
+ return this.transportService.sendMessage(message);
20910
+ }
20904
20911
  /**
20905
20912
  * Send the next pending message
20906
20913
  */
@@ -20929,9 +20936,14 @@
20929
20936
  ${JSON.stringify(message)}`);
20930
20937
  }
20931
20938
  this.waitingAck = true;
20932
- this.transportService.sendMessage({
20939
+ this.sendToTransport({
20933
20940
  ...message,
20934
20941
  serverRevisionId: this.serverRevisionId,
20942
+ }).catch((e) => {
20943
+ if (!(e instanceof ClientDisconnectedError)) {
20944
+ throw e.cause || e;
20945
+ }
20946
+ this.waitingAck = false;
20935
20947
  });
20936
20948
  }
20937
20949
  acknowledge(message) {
@@ -22772,6 +22784,403 @@
22772
22784
  */
22773
22785
  const DEFAULT_SYSTEM_COLOR = "FF000000";
22774
22786
 
22787
+ const globalReverseLookup$1 = new WeakMap();
22788
+ const globalIdCounter = new WeakMap();
22789
+ /**
22790
+ * Get the id of the given item (its key in the given dictionary).
22791
+ * If the given item does not exist in the dictionary, it creates one with a new id.
22792
+ */
22793
+ function getItemId(item, itemsDic) {
22794
+ if (!globalReverseLookup$1.has(itemsDic)) {
22795
+ globalReverseLookup$1.set(itemsDic, new Map());
22796
+ globalIdCounter.set(itemsDic, 0);
22797
+ }
22798
+ const reverseLookup = globalReverseLookup$1.get(itemsDic);
22799
+ const canonical = getCanonicalRepresentation(item);
22800
+ if (reverseLookup.has(canonical)) {
22801
+ const id = reverseLookup.get(canonical);
22802
+ itemsDic[id] = item;
22803
+ return id;
22804
+ }
22805
+ // Generate new Id if the item didn't exist in the dictionary
22806
+ const newId = globalIdCounter.get(itemsDic) + 1;
22807
+ reverseLookup.set(canonical, newId);
22808
+ globalIdCounter.set(itemsDic, newId);
22809
+ itemsDic[newId] = item;
22810
+ return newId;
22811
+ }
22812
+ function groupItemIdsByZones(positionsByItemId) {
22813
+ const result = {};
22814
+ for (const itemId in positionsByItemId) {
22815
+ const zones = recomputeZones(positionsByItemId[itemId].map(positionToZone));
22816
+ for (const zone of zones) {
22817
+ result[zoneToXc(zone)] = Number(itemId);
22818
+ }
22819
+ }
22820
+ return result;
22821
+ }
22822
+ function* iterateItemIdsPositions(sheetId, itemIdsByZones) {
22823
+ for (const zoneXc in itemIdsByZones) {
22824
+ const zone = toZone(zoneXc);
22825
+ const itemId = itemIdsByZones[zoneXc];
22826
+ for (let row = zone.top; row <= zone.bottom; row++) {
22827
+ for (let col = zone.left; col <= zone.right; col++) {
22828
+ const position = { sheetId, col, row };
22829
+ yield [position, itemId];
22830
+ }
22831
+ }
22832
+ }
22833
+ }
22834
+ function getCanonicalRepresentation(item) {
22835
+ if (item === null)
22836
+ return "null";
22837
+ if (item === undefined)
22838
+ return "undefined";
22839
+ if (typeof item !== "object")
22840
+ return String(item);
22841
+ if (Array.isArray(item)) {
22842
+ const len = item.length;
22843
+ let result = "[";
22844
+ for (let i = 0; i < len; i++) {
22845
+ if (i > 0)
22846
+ result += ",";
22847
+ result += getCanonicalRepresentation(item[i]);
22848
+ }
22849
+ return result + "]";
22850
+ }
22851
+ const keys = Object.keys(item).sort();
22852
+ let repr = "{";
22853
+ for (const key of keys) {
22854
+ if (item[key] !== undefined) {
22855
+ repr += `"${key}":${getCanonicalRepresentation(item[key])},`;
22856
+ }
22857
+ }
22858
+ repr += "}";
22859
+ return repr;
22860
+ }
22861
+
22862
+ // -------------------------------------
22863
+ // CF HELPERS
22864
+ // -------------------------------------
22865
+ /**
22866
+ * Convert the conditional formatting o-spreadsheet operator to
22867
+ * the corresponding excel operator.
22868
+ * */
22869
+ function convertOperator(operator) {
22870
+ switch (operator) {
22871
+ case "isNotEmpty":
22872
+ return "notContainsBlanks";
22873
+ case "isEmpty":
22874
+ return "containsBlanks";
22875
+ case "notContainsText":
22876
+ return "notContainsBlanks";
22877
+ case "containsText":
22878
+ return "containsText";
22879
+ case "beginsWithText":
22880
+ return "beginsWith";
22881
+ case "endsWithText":
22882
+ return "endsWith";
22883
+ case "isGreaterThan":
22884
+ return "greaterThan";
22885
+ case "isGreaterOrEqualTo":
22886
+ return "greaterThanOrEqual";
22887
+ case "isLessThan":
22888
+ return "lessThan";
22889
+ case "isLessOrEqualTo":
22890
+ return "lessThanOrEqual";
22891
+ case "isBetween":
22892
+ return "between";
22893
+ case "isNotBetween":
22894
+ return "notBetween";
22895
+ case "isEqual":
22896
+ return "equal";
22897
+ case "isNotEqual":
22898
+ return "notEqual";
22899
+ case "customFormula":
22900
+ return "";
22901
+ case "dateIs":
22902
+ return "";
22903
+ case "dateIsBefore":
22904
+ return "lessThan";
22905
+ case "dateIsAfter":
22906
+ return "greaterThan";
22907
+ case "dateIsOnOrAfter":
22908
+ return "greaterThanOrEqual";
22909
+ case "dateIsOnOrBefore":
22910
+ return "lessThanOrEqual";
22911
+ }
22912
+ }
22913
+ // -------------------------------------
22914
+ // WORKSHEET HELPERS
22915
+ // -------------------------------------
22916
+ function getCellType(value) {
22917
+ switch (typeof value) {
22918
+ case "boolean":
22919
+ return "b";
22920
+ case "string":
22921
+ return "str";
22922
+ case "number":
22923
+ return "n";
22924
+ default:
22925
+ return undefined;
22926
+ }
22927
+ }
22928
+ function convertHeightToExcel(height) {
22929
+ return Math.round(HEIGHT_FACTOR * height * 100) / 100;
22930
+ }
22931
+ function convertWidthToExcel(width) {
22932
+ return Math.round(WIDTH_FACTOR * width * 100) / 100;
22933
+ }
22934
+ function convertHeightFromExcel(height) {
22935
+ if (!height)
22936
+ return height;
22937
+ return Math.round((height / HEIGHT_FACTOR) * 100) / 100;
22938
+ }
22939
+ function convertWidthFromExcel(width) {
22940
+ if (!width)
22941
+ return width;
22942
+ return Math.round((width / WIDTH_FACTOR) * 100) / 100;
22943
+ }
22944
+ function extractStyle(data, content, styleId, formatId, borderId) {
22945
+ const style = styleId ? data.styles[styleId] : {};
22946
+ const format = formatId ? data.formats[formatId] : undefined;
22947
+ const styles = {
22948
+ font: {
22949
+ size: style?.fontSize || DEFAULT_FONT_SIZE,
22950
+ color: { rgb: style?.textColor ? style.textColor : "000000" },
22951
+ family: 2,
22952
+ name: "Arial",
22953
+ },
22954
+ fill: style?.fillColor
22955
+ ? {
22956
+ fgColor: { rgb: style.fillColor },
22957
+ }
22958
+ : { reservedAttribute: "none" },
22959
+ numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
22960
+ border: borderId || 0,
22961
+ alignment: {
22962
+ horizontal: style.align,
22963
+ vertical: style.verticalAlign
22964
+ ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
22965
+ : undefined,
22966
+ wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
22967
+ textRotation: style.rotation ? rotationToXLSX(style.rotation) : undefined,
22968
+ },
22969
+ };
22970
+ styles.font["strike"] = !!style?.strikethrough || undefined;
22971
+ styles.font["underline"] = !!style?.underline || undefined;
22972
+ styles.font["bold"] = !!style?.bold || undefined;
22973
+ styles.font["italic"] = !!style?.italic || undefined;
22974
+ return styles;
22975
+ }
22976
+ function rotationToXLSX(rad) {
22977
+ let deg = Math.round((-rad / Math.PI) * 180) % 180;
22978
+ if (deg > 90) {
22979
+ deg -= 180;
22980
+ }
22981
+ else if (deg < -90) {
22982
+ deg += 180;
22983
+ }
22984
+ if (deg >= 0) {
22985
+ return deg;
22986
+ }
22987
+ else {
22988
+ return 90 - deg;
22989
+ }
22990
+ }
22991
+ function rotationFromXLSX(deg) {
22992
+ if (deg <= 90) {
22993
+ return -(deg / 180) * Math.PI;
22994
+ }
22995
+ else {
22996
+ return (-(90 - deg) / 180) * Math.PI;
22997
+ }
22998
+ }
22999
+ function normalizeStyle(construct, styles) {
23000
+ // Normalize this
23001
+ const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
23002
+ const style = {
23003
+ fontId: pushElement(styles.font, construct.fonts),
23004
+ fillId: pushElement(styles.fill, construct.fills),
23005
+ borderId: styles.border,
23006
+ numFmtId,
23007
+ alignment: {
23008
+ vertical: styles.alignment.vertical,
23009
+ horizontal: styles.alignment.horizontal,
23010
+ wrapText: styles.alignment.wrapText,
23011
+ textRotation: styles.alignment.textRotation,
23012
+ },
23013
+ };
23014
+ return pushElement(style, construct.styles);
23015
+ }
23016
+ function convertFormat(format, numFmtStructure) {
23017
+ if (!format) {
23018
+ return 0;
23019
+ }
23020
+ let formatId = XLSX_FORMAT_MAP[format.format];
23021
+ if (!formatId) {
23022
+ formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
23023
+ }
23024
+ return formatId;
23025
+ }
23026
+ /**
23027
+ * Add a relation to the given file and return its id.
23028
+ */
23029
+ function addRelsToFile(relsFiles, path, rel) {
23030
+ const relsFile = relsFiles.find((file) => file.path === path);
23031
+ // the id is a one-based int casted as string
23032
+ let id;
23033
+ if (!relsFile) {
23034
+ id = "rId1";
23035
+ relsFiles.push({ path, rels: [{ ...rel, id }] });
23036
+ }
23037
+ else {
23038
+ id = `rId${(relsFile.rels.length + 1).toString()}`;
23039
+ relsFile.rels.push({
23040
+ ...rel,
23041
+ id,
23042
+ });
23043
+ }
23044
+ return id;
23045
+ }
23046
+ const globalReverseLookup = new WeakMap();
23047
+ function pushElement(property, propertyList) {
23048
+ let reverseLookup = globalReverseLookup.get(propertyList);
23049
+ if (!reverseLookup) {
23050
+ reverseLookup = new Map();
23051
+ for (let i = 0; i < propertyList.length; i++) {
23052
+ const canonical = getCanonicalRepresentation(propertyList[i]);
23053
+ reverseLookup.set(canonical, i);
23054
+ }
23055
+ globalReverseLookup.set(propertyList, reverseLookup);
23056
+ }
23057
+ const canonical = getCanonicalRepresentation(property);
23058
+ if (reverseLookup.has(canonical)) {
23059
+ return reverseLookup.get(canonical);
23060
+ }
23061
+ const maxId = propertyList.length;
23062
+ propertyList.push(property);
23063
+ reverseLookup.set(canonical, maxId);
23064
+ return maxId;
23065
+ }
23066
+ /**
23067
+ * Convert a chart o-spreadsheet id to a xlsx id which
23068
+ * are unsigned integers (starting from 1).
23069
+ */
23070
+ function convertChartId(chartId, construct) {
23071
+ const xlsxId = construct.chartIds.findIndex((id) => id === chartId);
23072
+ if (xlsxId === -1) {
23073
+ construct.chartIds.push(chartId);
23074
+ return construct.chartIds.length;
23075
+ }
23076
+ return xlsxId + 1;
23077
+ }
23078
+ const imageIds = [];
23079
+ /**
23080
+ * Convert a image o-spreadsheet id to a xlsx id which
23081
+ * are unsigned integers (starting from 1).
23082
+ */
23083
+ function convertImageId(imageId) {
23084
+ const xlsxId = imageIds.findIndex((id) => id === imageId);
23085
+ if (xlsxId === -1) {
23086
+ imageIds.push(imageId);
23087
+ return imageIds.length;
23088
+ }
23089
+ return xlsxId + 1;
23090
+ }
23091
+ /**
23092
+ * Convert a value expressed in dot to EMU.
23093
+ * EMU = English Metrical Unit
23094
+ * There are 914400 EMU per inch.
23095
+ *
23096
+ * /!\ A value expressed in EMU cannot be fractional.
23097
+ * See https://docs.microsoft.com/en-us/windows/win32/vml/msdn-online-vml-units#other-units-of-measurement
23098
+ */
23099
+ function convertDotValueToEMU(value) {
23100
+ const DPI = 96;
23101
+ return Math.round((value * 914400) / DPI);
23102
+ }
23103
+ function getRangeSize(reference, defaultSheetIndex, data) {
23104
+ let xc = reference;
23105
+ let sheetName = undefined;
23106
+ ({ xc, sheetName } = splitReference(reference));
23107
+ let rangeSheetIndex;
23108
+ if (sheetName) {
23109
+ const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
23110
+ if (index < 0) {
23111
+ throw new Error("Unable to find a sheet with the name " + sheetName);
23112
+ }
23113
+ rangeSheetIndex = index;
23114
+ }
23115
+ else {
23116
+ rangeSheetIndex = Number(defaultSheetIndex);
23117
+ }
23118
+ const zone = toUnboundedZone(xc);
23119
+ if (zone.right === undefined) {
23120
+ zone.right = data.sheets[rangeSheetIndex].colNumber;
23121
+ }
23122
+ if (zone.bottom === undefined) {
23123
+ zone.bottom = data.sheets[rangeSheetIndex].rowNumber;
23124
+ }
23125
+ return (zone.right - zone.left + 1) * (zone.bottom - zone.top + 1);
23126
+ }
23127
+ function convertEMUToDotValue(value) {
23128
+ const DPI = 96;
23129
+ return Math.round((value * DPI) / 914400);
23130
+ }
23131
+ /**
23132
+ * Get the position of the start of a column in Excel (in px).
23133
+ */
23134
+ function getColPosition(colIndex, sheetData) {
23135
+ let position = 0;
23136
+ for (let i = 0; i < colIndex; i++) {
23137
+ const colAtIndex = sheetData.cols.find((col) => i >= col.min && i <= col.max);
23138
+ if (colAtIndex?.width) {
23139
+ position += colAtIndex.width;
23140
+ }
23141
+ else if (sheetData.sheetFormat?.defaultColWidth) {
23142
+ position += sheetData.sheetFormat.defaultColWidth;
23143
+ }
23144
+ else {
23145
+ position += EXCEL_DEFAULT_COL_WIDTH;
23146
+ }
23147
+ }
23148
+ return position / WIDTH_FACTOR;
23149
+ }
23150
+ /**
23151
+ * Get the position of the start of a row in Excel (in px).
23152
+ */
23153
+ function getRowPosition(rowIndex, sheetData) {
23154
+ let position = 0;
23155
+ for (let i = 0; i < rowIndex; i++) {
23156
+ const rowAtIndex = sheetData.rows.find((row) => row.index - 1 === i);
23157
+ if (rowAtIndex?.height) {
23158
+ position += rowAtIndex.height;
23159
+ }
23160
+ else if (sheetData.sheetFormat?.defaultRowHeight) {
23161
+ position += sheetData.sheetFormat.defaultRowHeight;
23162
+ }
23163
+ else {
23164
+ position += EXCEL_DEFAULT_ROW_HEIGHT;
23165
+ }
23166
+ }
23167
+ return position / HEIGHT_FACTOR;
23168
+ }
23169
+ /**
23170
+ * Convert the o-spreadsheet data validation decimal
23171
+ * criterion type to the corresponding excel operator.
23172
+ */
23173
+ function convertDecimalCriterionTypeToExcelOperator(operator) {
23174
+ return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
23175
+ }
23176
+ /**
23177
+ * Convert the o-spreadsheet data validation date
23178
+ * criterion type to the corresponding excel operator.
23179
+ */
23180
+ function convertDateCriterionTypeToExcelOperator(operator) {
23181
+ return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
23182
+ }
23183
+
22775
23184
  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;
22776
23185
  /**
22777
23186
  * Convert excel format to o_spreadsheet format
@@ -22871,6 +23280,9 @@
22871
23280
  align: styleStruct.alignment?.horizontal
22872
23281
  ? H_ALIGNMENT_CONVERSION_MAP[styleStruct.alignment.horizontal]
22873
23282
  : undefined,
23283
+ rotation: styleStruct.alignment?.textRotation
23284
+ ? rotationFromXLSX(styleStruct.alignment.textRotation)
23285
+ : undefined,
22874
23286
  // 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
22875
23287
  fillColor: styleStruct.fillStyle?.patternType === "solid"
22876
23288
  ? convertColor(styleStruct.fillStyle?.fgColor)
@@ -23170,378 +23582,6 @@
23170
23582
  }
23171
23583
  }
23172
23584
 
23173
- const globalReverseLookup$1 = new WeakMap();
23174
- const globalIdCounter = new WeakMap();
23175
- /**
23176
- * Get the id of the given item (its key in the given dictionary).
23177
- * If the given item does not exist in the dictionary, it creates one with a new id.
23178
- */
23179
- function getItemId(item, itemsDic) {
23180
- if (!globalReverseLookup$1.has(itemsDic)) {
23181
- globalReverseLookup$1.set(itemsDic, new Map());
23182
- globalIdCounter.set(itemsDic, 0);
23183
- }
23184
- const reverseLookup = globalReverseLookup$1.get(itemsDic);
23185
- const canonical = getCanonicalRepresentation(item);
23186
- if (reverseLookup.has(canonical)) {
23187
- const id = reverseLookup.get(canonical);
23188
- itemsDic[id] = item;
23189
- return id;
23190
- }
23191
- // Generate new Id if the item didn't exist in the dictionary
23192
- const newId = globalIdCounter.get(itemsDic) + 1;
23193
- reverseLookup.set(canonical, newId);
23194
- globalIdCounter.set(itemsDic, newId);
23195
- itemsDic[newId] = item;
23196
- return newId;
23197
- }
23198
- function groupItemIdsByZones(positionsByItemId) {
23199
- const result = {};
23200
- for (const itemId in positionsByItemId) {
23201
- const zones = recomputeZones(positionsByItemId[itemId].map(positionToZone));
23202
- for (const zone of zones) {
23203
- result[zoneToXc(zone)] = Number(itemId);
23204
- }
23205
- }
23206
- return result;
23207
- }
23208
- function* iterateItemIdsPositions(sheetId, itemIdsByZones) {
23209
- for (const zoneXc in itemIdsByZones) {
23210
- const zone = toZone(zoneXc);
23211
- const itemId = itemIdsByZones[zoneXc];
23212
- for (let row = zone.top; row <= zone.bottom; row++) {
23213
- for (let col = zone.left; col <= zone.right; col++) {
23214
- const position = { sheetId, col, row };
23215
- yield [position, itemId];
23216
- }
23217
- }
23218
- }
23219
- }
23220
- function getCanonicalRepresentation(item) {
23221
- if (item === null)
23222
- return "null";
23223
- if (item === undefined)
23224
- return "undefined";
23225
- if (typeof item !== "object")
23226
- return String(item);
23227
- if (Array.isArray(item)) {
23228
- const len = item.length;
23229
- let result = "[";
23230
- for (let i = 0; i < len; i++) {
23231
- if (i > 0)
23232
- result += ",";
23233
- result += getCanonicalRepresentation(item[i]);
23234
- }
23235
- return result + "]";
23236
- }
23237
- const keys = Object.keys(item).sort();
23238
- let repr = "{";
23239
- for (const key of keys) {
23240
- if (item[key] !== undefined) {
23241
- repr += `"${key}":${getCanonicalRepresentation(item[key])},`;
23242
- }
23243
- }
23244
- repr += "}";
23245
- return repr;
23246
- }
23247
-
23248
- // -------------------------------------
23249
- // CF HELPERS
23250
- // -------------------------------------
23251
- /**
23252
- * Convert the conditional formatting o-spreadsheet operator to
23253
- * the corresponding excel operator.
23254
- * */
23255
- function convertOperator(operator) {
23256
- switch (operator) {
23257
- case "isNotEmpty":
23258
- return "notContainsBlanks";
23259
- case "isEmpty":
23260
- return "containsBlanks";
23261
- case "notContainsText":
23262
- return "notContainsBlanks";
23263
- case "containsText":
23264
- return "containsText";
23265
- case "beginsWithText":
23266
- return "beginsWith";
23267
- case "endsWithText":
23268
- return "endsWith";
23269
- case "isGreaterThan":
23270
- return "greaterThan";
23271
- case "isGreaterOrEqualTo":
23272
- return "greaterThanOrEqual";
23273
- case "isLessThan":
23274
- return "lessThan";
23275
- case "isLessOrEqualTo":
23276
- return "lessThanOrEqual";
23277
- case "isBetween":
23278
- return "between";
23279
- case "isNotBetween":
23280
- return "notBetween";
23281
- case "isEqual":
23282
- return "equal";
23283
- case "isNotEqual":
23284
- return "notEqual";
23285
- case "customFormula":
23286
- return "";
23287
- case "dateIs":
23288
- return "";
23289
- case "dateIsBefore":
23290
- return "lessThan";
23291
- case "dateIsAfter":
23292
- return "greaterThan";
23293
- case "dateIsOnOrAfter":
23294
- return "greaterThanOrEqual";
23295
- case "dateIsOnOrBefore":
23296
- return "lessThanOrEqual";
23297
- }
23298
- }
23299
- // -------------------------------------
23300
- // WORKSHEET HELPERS
23301
- // -------------------------------------
23302
- function getCellType(value) {
23303
- switch (typeof value) {
23304
- case "boolean":
23305
- return "b";
23306
- case "string":
23307
- return "str";
23308
- case "number":
23309
- return "n";
23310
- default:
23311
- return undefined;
23312
- }
23313
- }
23314
- function convertHeightToExcel(height) {
23315
- return Math.round(HEIGHT_FACTOR * height * 100) / 100;
23316
- }
23317
- function convertWidthToExcel(width) {
23318
- return Math.round(WIDTH_FACTOR * width * 100) / 100;
23319
- }
23320
- function convertHeightFromExcel(height) {
23321
- if (!height)
23322
- return height;
23323
- return Math.round((height / HEIGHT_FACTOR) * 100) / 100;
23324
- }
23325
- function convertWidthFromExcel(width) {
23326
- if (!width)
23327
- return width;
23328
- return Math.round((width / WIDTH_FACTOR) * 100) / 100;
23329
- }
23330
- function extractStyle(data, content, styleId, formatId, borderId) {
23331
- const style = styleId ? data.styles[styleId] : {};
23332
- const format = formatId ? data.formats[formatId] : undefined;
23333
- const styles = {
23334
- font: {
23335
- size: style?.fontSize || DEFAULT_FONT_SIZE,
23336
- color: { rgb: style?.textColor ? style.textColor : "000000" },
23337
- family: 2,
23338
- name: "Arial",
23339
- },
23340
- fill: style?.fillColor
23341
- ? {
23342
- fgColor: { rgb: style.fillColor },
23343
- }
23344
- : { reservedAttribute: "none" },
23345
- numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
23346
- border: borderId || 0,
23347
- alignment: {
23348
- horizontal: style.align,
23349
- vertical: style.verticalAlign
23350
- ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
23351
- : undefined,
23352
- wrapText: style.wrapping === "wrap" || content?.includes(NEWLINE) ? true : undefined,
23353
- },
23354
- };
23355
- styles.font["strike"] = !!style?.strikethrough || undefined;
23356
- styles.font["underline"] = !!style?.underline || undefined;
23357
- styles.font["bold"] = !!style?.bold || undefined;
23358
- styles.font["italic"] = !!style?.italic || undefined;
23359
- return styles;
23360
- }
23361
- function normalizeStyle(construct, styles) {
23362
- // Normalize this
23363
- const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
23364
- const style = {
23365
- fontId: pushElement(styles.font, construct.fonts),
23366
- fillId: pushElement(styles.fill, construct.fills),
23367
- borderId: styles.border,
23368
- numFmtId,
23369
- alignment: {
23370
- vertical: styles.alignment.vertical,
23371
- horizontal: styles.alignment.horizontal,
23372
- wrapText: styles.alignment.wrapText,
23373
- },
23374
- };
23375
- return pushElement(style, construct.styles);
23376
- }
23377
- function convertFormat(format, numFmtStructure) {
23378
- if (!format) {
23379
- return 0;
23380
- }
23381
- let formatId = XLSX_FORMAT_MAP[format.format];
23382
- if (!formatId) {
23383
- formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
23384
- }
23385
- return formatId;
23386
- }
23387
- /**
23388
- * Add a relation to the given file and return its id.
23389
- */
23390
- function addRelsToFile(relsFiles, path, rel) {
23391
- const relsFile = relsFiles.find((file) => file.path === path);
23392
- // the id is a one-based int casted as string
23393
- let id;
23394
- if (!relsFile) {
23395
- id = "rId1";
23396
- relsFiles.push({ path, rels: [{ ...rel, id }] });
23397
- }
23398
- else {
23399
- id = `rId${(relsFile.rels.length + 1).toString()}`;
23400
- relsFile.rels.push({
23401
- ...rel,
23402
- id,
23403
- });
23404
- }
23405
- return id;
23406
- }
23407
- const globalReverseLookup = new WeakMap();
23408
- function pushElement(property, propertyList) {
23409
- let reverseLookup = globalReverseLookup.get(propertyList);
23410
- if (!reverseLookup) {
23411
- reverseLookup = new Map();
23412
- for (let i = 0; i < propertyList.length; i++) {
23413
- const canonical = getCanonicalRepresentation(propertyList[i]);
23414
- reverseLookup.set(canonical, i);
23415
- }
23416
- globalReverseLookup.set(propertyList, reverseLookup);
23417
- }
23418
- const canonical = getCanonicalRepresentation(property);
23419
- if (reverseLookup.has(canonical)) {
23420
- return reverseLookup.get(canonical);
23421
- }
23422
- const maxId = propertyList.length;
23423
- propertyList.push(property);
23424
- reverseLookup.set(canonical, maxId);
23425
- return maxId;
23426
- }
23427
- /**
23428
- * Convert a chart o-spreadsheet id to a xlsx id which
23429
- * are unsigned integers (starting from 1).
23430
- */
23431
- function convertChartId(chartId, construct) {
23432
- const xlsxId = construct.chartIds.findIndex((id) => id === chartId);
23433
- if (xlsxId === -1) {
23434
- construct.chartIds.push(chartId);
23435
- return construct.chartIds.length;
23436
- }
23437
- return xlsxId + 1;
23438
- }
23439
- const imageIds = [];
23440
- /**
23441
- * Convert a image o-spreadsheet id to a xlsx id which
23442
- * are unsigned integers (starting from 1).
23443
- */
23444
- function convertImageId(imageId) {
23445
- const xlsxId = imageIds.findIndex((id) => id === imageId);
23446
- if (xlsxId === -1) {
23447
- imageIds.push(imageId);
23448
- return imageIds.length;
23449
- }
23450
- return xlsxId + 1;
23451
- }
23452
- /**
23453
- * Convert a value expressed in dot to EMU.
23454
- * EMU = English Metrical Unit
23455
- * There are 914400 EMU per inch.
23456
- *
23457
- * /!\ A value expressed in EMU cannot be fractional.
23458
- * See https://docs.microsoft.com/en-us/windows/win32/vml/msdn-online-vml-units#other-units-of-measurement
23459
- */
23460
- function convertDotValueToEMU(value) {
23461
- const DPI = 96;
23462
- return Math.round((value * 914400) / DPI);
23463
- }
23464
- function getRangeSize(reference, defaultSheetIndex, data) {
23465
- let xc = reference;
23466
- let sheetName = undefined;
23467
- ({ xc, sheetName } = splitReference(reference));
23468
- let rangeSheetIndex;
23469
- if (sheetName) {
23470
- const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
23471
- if (index < 0) {
23472
- throw new Error("Unable to find a sheet with the name " + sheetName);
23473
- }
23474
- rangeSheetIndex = index;
23475
- }
23476
- else {
23477
- rangeSheetIndex = Number(defaultSheetIndex);
23478
- }
23479
- const zone = toUnboundedZone(xc);
23480
- if (zone.right === undefined) {
23481
- zone.right = data.sheets[rangeSheetIndex].colNumber;
23482
- }
23483
- if (zone.bottom === undefined) {
23484
- zone.bottom = data.sheets[rangeSheetIndex].rowNumber;
23485
- }
23486
- return (zone.right - zone.left + 1) * (zone.bottom - zone.top + 1);
23487
- }
23488
- function convertEMUToDotValue(value) {
23489
- const DPI = 96;
23490
- return Math.round((value * DPI) / 914400);
23491
- }
23492
- /**
23493
- * Get the position of the start of a column in Excel (in px).
23494
- */
23495
- function getColPosition(colIndex, sheetData) {
23496
- let position = 0;
23497
- for (let i = 0; i < colIndex; i++) {
23498
- const colAtIndex = sheetData.cols.find((col) => i >= col.min && i <= col.max);
23499
- if (colAtIndex?.width) {
23500
- position += colAtIndex.width;
23501
- }
23502
- else if (sheetData.sheetFormat?.defaultColWidth) {
23503
- position += sheetData.sheetFormat.defaultColWidth;
23504
- }
23505
- else {
23506
- position += EXCEL_DEFAULT_COL_WIDTH;
23507
- }
23508
- }
23509
- return position / WIDTH_FACTOR;
23510
- }
23511
- /**
23512
- * Get the position of the start of a row in Excel (in px).
23513
- */
23514
- function getRowPosition(rowIndex, sheetData) {
23515
- let position = 0;
23516
- for (let i = 0; i < rowIndex; i++) {
23517
- const rowAtIndex = sheetData.rows.find((row) => row.index - 1 === i);
23518
- if (rowAtIndex?.height) {
23519
- position += rowAtIndex.height;
23520
- }
23521
- else if (sheetData.sheetFormat?.defaultRowHeight) {
23522
- position += sheetData.sheetFormat.defaultRowHeight;
23523
- }
23524
- else {
23525
- position += EXCEL_DEFAULT_ROW_HEIGHT;
23526
- }
23527
- }
23528
- return position / HEIGHT_FACTOR;
23529
- }
23530
- /**
23531
- * Convert the o-spreadsheet data validation decimal
23532
- * criterion type to the corresponding excel operator.
23533
- */
23534
- function convertDecimalCriterionTypeToExcelOperator(operator) {
23535
- return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
23536
- }
23537
- /**
23538
- * Convert the o-spreadsheet data validation date
23539
- * criterion type to the corresponding excel operator.
23540
- */
23541
- function convertDateCriterionTypeToExcelOperator(operator) {
23542
- return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
23543
- }
23544
-
23545
23585
  function convertFigures(sheetData) {
23546
23586
  let id = 1;
23547
23587
  return sheetData.figures
@@ -23913,17 +23953,41 @@
23913
23953
  const today = DateTime.now();
23914
23954
  switch (dateValue) {
23915
23955
  case "today":
23916
- return jsDateToNumber(today);
23917
- case "yesterday":
23918
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
23919
- case "tomorrow":
23920
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
23956
+ return Math.floor(jsDateToNumber(today));
23957
+ case "yesterday": {
23958
+ today.setDate(today.getDate() - 1);
23959
+ return Math.floor(jsDateToNumber(today));
23960
+ }
23961
+ case "tomorrow": {
23962
+ today.setDate(today.getDate() + 1);
23963
+ return Math.floor(jsDateToNumber(today));
23964
+ }
23921
23965
  case "lastWeek":
23922
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
23923
- case "lastMonth":
23924
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
23966
+ today.setDate(today.getDate() - 6);
23967
+ return Math.floor(jsDateToNumber(today));
23968
+ case "lastMonth": {
23969
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
23970
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
23971
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
23972
+ today.setDate(1);
23973
+ }
23974
+ else {
23975
+ today.setDate(today.getDate() + 1);
23976
+ today.setMonth(today.getMonth() - 1);
23977
+ }
23978
+ return Math.floor(jsDateToNumber(today));
23979
+ }
23925
23980
  case "lastYear":
23926
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
23981
+ // Handle leap year case
23982
+ if (today.getMonth() === 1 && today.getDate() === 29) {
23983
+ today.setDate(28);
23984
+ today.setFullYear(today.getFullYear() - 1);
23985
+ }
23986
+ else {
23987
+ today.setDate(today.getDate() + 1);
23988
+ today.setFullYear(today.getFullYear() - 1);
23989
+ }
23990
+ return Math.floor(jsDateToNumber(today));
23927
23991
  }
23928
23992
  }
23929
23993
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -28948,7 +29012,7 @@
28948
29012
  return false;
28949
29013
  }
28950
29014
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
28951
- const today = jsDateToRoundNumber(DateTime.now());
29015
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
28952
29016
  return isDateBetween(dateValue, today, criterionValue);
28953
29017
  }
28954
29018
  return areDatesSameDay(dateValue, criterionValue);
@@ -33375,6 +33439,7 @@
33375
33439
  "getMeasureCompiledFormula",
33376
33440
  "getPivotName",
33377
33441
  "isExistingPivot",
33442
+ "getMeasureFullDependencies",
33378
33443
  ];
33379
33444
  nextFormulaId = 1;
33380
33445
  pivots = {};
@@ -33460,7 +33525,7 @@
33460
33525
  }
33461
33526
  case "UPDATE_PIVOT": {
33462
33527
  this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
33463
- this.compileCalculatedMeasures(cmd.pivot.measures);
33528
+ this.compileCalculatedMeasures(cmd.pivotId, cmd.pivot.measures);
33464
33529
  break;
33465
33530
  }
33466
33531
  }
@@ -33478,9 +33543,14 @@
33478
33543
  this.history.update("pivots", pivotId, "definition", newDefinition);
33479
33544
  }
33480
33545
  }
33481
- for (const sheetId in this.compiledMeasureFormulas) {
33482
- for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
33483
- const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
33546
+ for (const pivotId in this.compiledMeasureFormulas) {
33547
+ for (const measureId in this.compiledMeasureFormulas[pivotId]) {
33548
+ const measure = this.pivots[pivotId]?.definition.measures.find((m) => m.id === measureId);
33549
+ if (!measure || !measure.computedBy) {
33550
+ continue;
33551
+ }
33552
+ const sheetId = measure.computedBy.sheetId;
33553
+ const compiledFormula = this.compiledMeasureFormulas[pivotId][measureId].formula;
33484
33554
  const newDependencies = [];
33485
33555
  for (const range of compiledFormula.dependencies) {
33486
33556
  const change = applyChange(range);
@@ -33492,8 +33562,9 @@
33492
33562
  }
33493
33563
  }
33494
33564
  const newFormulaString = this.getters.getFormulaString(sheetId, compiledFormula.tokens, newDependencies);
33495
- if (newFormulaString !== formulaString) {
33496
- this.replaceMeasureFormula(sheetId, formulaString, newFormulaString);
33565
+ const oldFormulaString = measure.computedBy.formula;
33566
+ if (newFormulaString !== oldFormulaString) {
33567
+ this.replaceMeasureFormula(pivotId, measure, newFormulaString);
33497
33568
  }
33498
33569
  }
33499
33570
  }
@@ -33531,31 +33602,60 @@
33531
33602
  isExistingPivot(pivotId) {
33532
33603
  return pivotId in this.pivots;
33533
33604
  }
33534
- getMeasureCompiledFormula(measure) {
33605
+ getMeasureCompiledFormula(pivotId, measure) {
33606
+ if (!measure.computedBy) {
33607
+ throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
33608
+ }
33609
+ return this.compiledMeasureFormulas[pivotId][measure.id].formula;
33610
+ }
33611
+ getMeasureFullDependencies(pivotId, measure) {
33535
33612
  if (!measure.computedBy) {
33536
33613
  throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
33537
33614
  }
33538
- const sheetId = measure.computedBy.sheetId;
33539
- return this.compiledMeasureFormulas[sheetId][measure.computedBy.formula];
33615
+ return this.compiledMeasureFormulas[pivotId][measure.id].dependencies;
33540
33616
  }
33541
33617
  // -------------------------------------------------------------------------
33542
33618
  // Private
33543
33619
  // -------------------------------------------------------------------------
33544
33620
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
33545
33621
  this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
33546
- this.compileCalculatedMeasures(pivot.measures);
33622
+ this.compileCalculatedMeasures(pivotId, pivot.measures);
33547
33623
  this.history.update("formulaIds", formulaId, pivotId);
33548
33624
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
33549
33625
  }
33550
- compileCalculatedMeasures(measures) {
33626
+ compileCalculatedMeasures(pivotId, measures) {
33551
33627
  for (const measure of measures) {
33552
33628
  if (measure.computedBy) {
33553
- const sheetId = measure.computedBy.sheetId;
33554
33629
  const compiledFormula = this.compileMeasureFormula(measure.computedBy.sheetId, measure.computedBy.formula);
33555
- this.history.update("compiledMeasureFormulas", sheetId, measure.computedBy.formula, compiledFormula);
33630
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "formula", compiledFormula);
33631
+ }
33632
+ }
33633
+ for (const measure of measures) {
33634
+ if (measure.computedBy) {
33635
+ const dependencies = this.computeMeasureFullDependencies(pivotId, measure);
33636
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "dependencies", dependencies);
33556
33637
  }
33557
33638
  }
33558
33639
  }
33640
+ computeMeasureFullDependencies(pivotId, measure, exploredMeasures = new Set()) {
33641
+ const rangeDependencies = [];
33642
+ const definition = this.getPivotCoreDefinition(pivotId);
33643
+ const formula = this.getMeasureCompiledFormula(pivotId, measure);
33644
+ exploredMeasures.add(measure.id);
33645
+ for (const token of formula.tokens) {
33646
+ if (token.type !== "SYMBOL") {
33647
+ continue;
33648
+ }
33649
+ const otherMeasure = definition.measures.find((measureCandidate) => getCanonicalSymbolName(measureCandidate.id) === token.value &&
33650
+ measure.id !== measureCandidate.id);
33651
+ if (!otherMeasure || exploredMeasures.has(otherMeasure.id) || !otherMeasure.computedBy) {
33652
+ continue;
33653
+ }
33654
+ rangeDependencies.push(...this.computeMeasureFullDependencies(pivotId, otherMeasure, exploredMeasures));
33655
+ }
33656
+ rangeDependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
33657
+ return rangeDependencies;
33658
+ }
33559
33659
  insertPivot(position, formulaId, table) {
33560
33660
  this.resizeSheet(position.sheetId, position, table);
33561
33661
  const pivotCells = table.getPivotCells();
@@ -33614,21 +33714,17 @@
33614
33714
  dependencies: rangeDependencies,
33615
33715
  };
33616
33716
  }
33617
- replaceMeasureFormula(sheetId, formulaString, newFormulaString) {
33618
- this.history.update("compiledMeasureFormulas", sheetId, formulaString, undefined);
33619
- this.history.update("compiledMeasureFormulas", sheetId, newFormulaString, this.compileMeasureFormula(sheetId, newFormulaString));
33620
- for (const pivotId in this.pivots) {
33621
- const pivot = this.pivots[pivotId];
33622
- if (!pivot) {
33623
- continue;
33624
- }
33625
- for (const measure of pivot.definition.measures) {
33626
- if (measure.computedBy?.formula === formulaString) {
33627
- const measureIndex = pivot.definition.measures.indexOf(measure);
33628
- this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
33629
- }
33630
- }
33717
+ replaceMeasureFormula(pivotId, measure, newFormulaString) {
33718
+ const pivot = this.pivots[pivotId];
33719
+ if (!pivot) {
33720
+ return;
33631
33721
  }
33722
+ const measureIndex = pivot.definition.measures.indexOf(measure);
33723
+ this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", {
33724
+ formula: newFormulaString,
33725
+ sheetId: measure.computedBy.sheetId,
33726
+ });
33727
+ this.compileCalculatedMeasures(pivotId, pivot.definition.measures);
33632
33728
  }
33633
33729
  checkSortedColumnInMeasures(definition) {
33634
33730
  const measures = definition.measures.map((measure) => measure.id);
@@ -34689,7 +34785,7 @@
34689
34785
  case "UPDATE_CELL":
34690
34786
  if (cmd.style !== undefined) {
34691
34787
  if (cmd.style !== null) {
34692
- this.setStyles(cmd.sheetId, [positionToZone(cmd)], cmd.style);
34788
+ this.setStyles(cmd.sheetId, [positionToZone(cmd)], cmd.style, { force: true });
34693
34789
  }
34694
34790
  else {
34695
34791
  this.clearStyle(cmd.sheetId, [positionToZone(cmd)]);
@@ -34757,16 +34853,22 @@
34757
34853
  }
34758
34854
  }
34759
34855
  styleIsDefault(style) {
34760
- return deepEquals(this.removeDefaultStyleValues(style), {});
34856
+ for (const key in style) {
34857
+ if (DEFAULT_STYLE_NO_ALIGN[key] !== style[key]) {
34858
+ return false;
34859
+ }
34860
+ }
34861
+ return true;
34761
34862
  }
34762
34863
  removeDefaultStyleValues(style) {
34763
34864
  const cleanedStyle = { ...style };
34764
- for (const property in DEFAULT_STYLE_NO_ALIGN) {
34765
- if (cleanedStyle[property] === DEFAULT_STYLE_NO_ALIGN[property]) {
34865
+ for (const property in style) {
34866
+ if (cleanedStyle[property] === undefined ||
34867
+ cleanedStyle[property] === DEFAULT_STYLE_NO_ALIGN[property]) {
34766
34868
  delete cleanedStyle[property];
34767
34869
  }
34768
34870
  }
34769
- return cleanedStyle;
34871
+ return Object.keys(cleanedStyle).length > 0 ? cleanedStyle : undefined;
34770
34872
  }
34771
34873
  onMerge(sheetId, zone) {
34772
34874
  this.setStyle(sheetId, zone, this.getCellStyle({ sheetId, col: zone.left, row: zone.top }), {
@@ -34802,13 +34904,13 @@
34802
34904
  }
34803
34905
  editingZone = recomputeZones(editingZone, [inter]);
34804
34906
  }
34907
+ style = this.removeDefaultStyleValues(style);
34805
34908
  if (style) {
34806
- const newStyle = this.removeDefaultStyleValues(style);
34807
34909
  styles.push(...editingZone.map((zone) => {
34808
- return { zone, style: newStyle };
34910
+ return { zone, style };
34809
34911
  }));
34810
34912
  }
34811
- this.history.update("styles", sheetId, styles.filter((zoneStyle) => !this.styleIsDefault(zoneStyle.style)));
34913
+ this.history.update("styles", sheetId, styles);
34812
34914
  }
34813
34915
  clearStyle(sheetId, zones) {
34814
34916
  this.setStyles(sheetId, zones, undefined, { force: true });
@@ -40450,14 +40552,16 @@
40450
40552
  function withPivotPresentationLayer (PivotClass) {
40451
40553
  class PivotPresentationLayer extends PivotClass {
40452
40554
  getters;
40555
+ pivotId;
40453
40556
  cache = {};
40454
40557
  rankAsc = {};
40455
40558
  rankDesc = {};
40456
40559
  runningTotal = {};
40457
40560
  runningTotalInPercent = {};
40458
- constructor(custom, params) {
40561
+ constructor(pivotId, custom, params) {
40459
40562
  super(custom, params);
40460
40563
  this.getters = params.getters;
40564
+ this.pivotId = pivotId;
40461
40565
  }
40462
40566
  markAsDirtyForEvaluation() {
40463
40567
  this.cache = {};
@@ -40507,7 +40611,7 @@
40507
40611
  return handleError(error, measure.aggregator.toUpperCase());
40508
40612
  }
40509
40613
  }
40510
- const formula = this.getters.getMeasureCompiledFormula(measure);
40614
+ const formula = this.getters.getMeasureCompiledFormula(this.pivotId, measure);
40511
40615
  const getSymbolValue = (symbolName) => {
40512
40616
  const { columns, rows } = this.definition;
40513
40617
  if (columns.find((col) => col.nameWithGranularity === symbolName)) {
@@ -41254,7 +41358,7 @@
41254
41358
  const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
41255
41359
  if (!(pivotId in this.pivots)) {
41256
41360
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
41257
- this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
41361
+ this.pivots[pivotId] = new Pivot(pivotId, this.custom, { definition, getters: this.getters });
41258
41362
  }
41259
41363
  else if (recreate) {
41260
41364
  this.pivots[pivotId].onDefinitionChange(definition);
@@ -50246,6 +50350,9 @@
50246
50350
  if (style.alignment && style.alignment.wrapText) {
50247
50351
  alignAttrs.push(["wrapText", "1"]);
50248
50352
  }
50353
+ if (style.alignment && style.alignment.textRotation) {
50354
+ alignAttrs.push(["textRotation", style.alignment.textRotation]);
50355
+ }
50249
50356
  if (alignAttrs.length > 0) {
50250
50357
  attributes.push(["applyAlignment", "1"]); // for Libre Office
50251
50358
  styleNodes.push(escapeXml /*xml*/ `<xf ${formatAttributes(attributes)}><alignment ${formatAttributes(alignAttrs)} /></xf> `);
@@ -51635,8 +51742,8 @@
51635
51742
 
51636
51743
 
51637
51744
  __info__.version = "19.1.0-alpha.3";
51638
- __info__.date = "2025-12-26T10:26:09.971Z";
51639
- __info__.hash = "e6313bd";
51745
+ __info__.date = "2026-01-14T10:01:54.190Z";
51746
+ __info__.hash = "52a3e52";
51640
51747
 
51641
51748
 
51642
51749
  })(this.o_spreadsheet_engine = this.o_spreadsheet_engine || {});