@odoo/o-spreadsheet 17.4.8 → 17.4.9

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,14 +2,119 @@
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 17.4.8
6
- * @date 2024-09-17T08:10:18.777Z
7
- * @hash caad8bd
5
+ * @version 17.4.9
6
+ * @date 2024-10-14T07:53:51.633Z
7
+ * @hash b7015b7
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
11
11
  'use strict';
12
12
 
13
+ function createActions(menuItems) {
14
+ return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
15
+ }
16
+ let nextItemId = 1;
17
+ function createAction(item) {
18
+ const name = item.name;
19
+ const children = item.children;
20
+ const description = item.description;
21
+ const icon = item.icon;
22
+ const secondaryIcon = item.secondaryIcon;
23
+ const itemId = item.id || nextItemId++;
24
+ return {
25
+ id: itemId.toString(),
26
+ name: typeof name === "function" ? name : () => name,
27
+ isVisible: item.isVisible ? item.isVisible : () => true,
28
+ isEnabled: item.isEnabled ? item.isEnabled : () => true,
29
+ isActive: item.isActive,
30
+ execute: item.execute,
31
+ children: children
32
+ ? (env) => {
33
+ return children
34
+ .map((child) => (typeof child === "function" ? child(env) : child))
35
+ .flat()
36
+ .map(createAction);
37
+ }
38
+ : () => [],
39
+ isReadonlyAllowed: item.isReadonlyAllowed || false,
40
+ separator: item.separator || false,
41
+ icon: typeof icon === "function" ? icon : () => icon || "",
42
+ secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
43
+ description: typeof description === "function" ? description : () => description || "",
44
+ textColor: item.textColor,
45
+ sequence: item.sequence || 0,
46
+ onStartHover: item.onStartHover,
47
+ onStopHover: item.onStopHover,
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Registry
53
+ *
54
+ * The Registry class is basically just a mapping from a string key to an object.
55
+ * It is really not much more than an object. It is however useful for the
56
+ * following reasons:
57
+ *
58
+ * 1. it let us react and execute code when someone add something to the registry
59
+ * (for example, the FunctionRegistry subclass this for this purpose)
60
+ * 2. it throws an error when the get operation fails
61
+ * 3. it provides a chained API to add items to the registry.
62
+ */
63
+ class Registry {
64
+ content = {};
65
+ /**
66
+ * Add an item to the registry
67
+ *
68
+ * Note that this also returns the registry, so another add method call can
69
+ * be chained
70
+ */
71
+ add(key, value) {
72
+ this.content[key] = value;
73
+ return this;
74
+ }
75
+ /**
76
+ * Get an item from the registry
77
+ */
78
+ get(key) {
79
+ /**
80
+ * Note: key in {} is ~12 times slower than {}[key].
81
+ * So, we check the absence of key only when the direct access returns
82
+ * a falsy value. It's done to ensure that the registry can contains falsy values
83
+ */
84
+ const content = this.content[key];
85
+ if (!content) {
86
+ if (!(key in this.content)) {
87
+ throw new Error(`Cannot find ${key} in this registry!`);
88
+ }
89
+ }
90
+ return content;
91
+ }
92
+ /**
93
+ * Check if the key is already in the registry
94
+ */
95
+ contains(key) {
96
+ return key in this.content;
97
+ }
98
+ /**
99
+ * Get a list of all elements in the registry
100
+ */
101
+ getAll() {
102
+ return Object.values(this.content);
103
+ }
104
+ /**
105
+ * Get a list of all keys in the registry
106
+ */
107
+ getKeys() {
108
+ return Object.keys(this.content);
109
+ }
110
+ /**
111
+ * Remove an item from the registry
112
+ */
113
+ remove(key) {
114
+ delete this.content[key];
115
+ }
116
+ }
117
+
13
118
  const CANVAS_SHIFT = 0.5;
14
119
  // Colors
15
120
  const HIGHLIGHT_COLOR = "#37A850";
@@ -166,7 +271,7 @@
166
271
  underline: false,
167
272
  fontSize: 10,
168
273
  fillColor: "",
169
- textColor: "#000000",
274
+ textColor: "",
170
275
  };
171
276
  const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
172
277
  const DEFAULT_WRAPPING_MODE = DEFAULT_STYLE.wrapping;
@@ -5398,110 +5503,6 @@
5398
5503
  }
5399
5504
  }
5400
5505
 
5401
- function createActions(menuItems) {
5402
- return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
5403
- }
5404
- const uuidGenerator$1 = new UuidGenerator();
5405
- function createAction(item) {
5406
- const name = item.name;
5407
- const children = item.children;
5408
- const description = item.description;
5409
- const icon = item.icon;
5410
- const secondaryIcon = item.secondaryIcon;
5411
- return {
5412
- id: item.id || uuidGenerator$1.uuidv4(),
5413
- name: typeof name === "function" ? name : () => name,
5414
- isVisible: item.isVisible ? item.isVisible : () => true,
5415
- isEnabled: item.isEnabled ? item.isEnabled : () => true,
5416
- isActive: item.isActive,
5417
- execute: item.execute,
5418
- children: children
5419
- ? (env) => {
5420
- return children
5421
- .map((child) => (typeof child === "function" ? child(env) : child))
5422
- .flat()
5423
- .map(createAction);
5424
- }
5425
- : () => [],
5426
- isReadonlyAllowed: item.isReadonlyAllowed || false,
5427
- separator: item.separator || false,
5428
- icon: typeof icon === "function" ? icon : () => icon || "",
5429
- secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
5430
- description: typeof description === "function" ? description : () => description || "",
5431
- textColor: item.textColor,
5432
- sequence: item.sequence || 0,
5433
- onStartHover: item.onStartHover,
5434
- onStopHover: item.onStopHover,
5435
- };
5436
- }
5437
-
5438
- /**
5439
- * Registry
5440
- *
5441
- * The Registry class is basically just a mapping from a string key to an object.
5442
- * It is really not much more than an object. It is however useful for the
5443
- * following reasons:
5444
- *
5445
- * 1. it let us react and execute code when someone add something to the registry
5446
- * (for example, the FunctionRegistry subclass this for this purpose)
5447
- * 2. it throws an error when the get operation fails
5448
- * 3. it provides a chained API to add items to the registry.
5449
- */
5450
- class Registry {
5451
- content = {};
5452
- /**
5453
- * Add an item to the registry
5454
- *
5455
- * Note that this also returns the registry, so another add method call can
5456
- * be chained
5457
- */
5458
- add(key, value) {
5459
- this.content[key] = value;
5460
- return this;
5461
- }
5462
- /**
5463
- * Get an item from the registry
5464
- */
5465
- get(key) {
5466
- /**
5467
- * Note: key in {} is ~12 times slower than {}[key].
5468
- * So, we check the absence of key only when the direct access returns
5469
- * a falsy value. It's done to ensure that the registry can contains falsy values
5470
- */
5471
- const content = this.content[key];
5472
- if (!content) {
5473
- if (!(key in this.content)) {
5474
- throw new Error(`Cannot find ${key} in this registry!`);
5475
- }
5476
- }
5477
- return content;
5478
- }
5479
- /**
5480
- * Check if the key is already in the registry
5481
- */
5482
- contains(key) {
5483
- return key in this.content;
5484
- }
5485
- /**
5486
- * Get a list of all elements in the registry
5487
- */
5488
- getAll() {
5489
- return Object.values(this.content);
5490
- }
5491
- /**
5492
- * Get a list of all keys in the registry
5493
- */
5494
- getKeys() {
5495
- return Object.keys(this.content);
5496
- }
5497
- /**
5498
- * Remove an item from the registry
5499
- */
5500
- remove(key) {
5501
- delete this.content[key];
5502
- }
5503
- }
5504
-
5505
5506
  function getClipboardDataPositions(sheetId, zones) {
5506
5507
  const lefts = new Set(zones.map((z) => z.left));
5507
5508
  const rights = new Set(zones.map((z) => z.right));
@@ -7280,31 +7281,34 @@
7280
7281
  for (let col of columnsIndexes) {
7281
7282
  const position = { col, row, sheetId };
7282
7283
  const table = this.getters.getTable(position);
7283
- if (!table || copiedTablesIds.has(table.id)) {
7284
+ if (!table) {
7284
7285
  tableCellsInRow.push({});
7285
7286
  continue;
7286
7287
  }
7287
7288
  const coreTable = this.getters.getCoreTable(position);
7288
7289
  const tableZone = coreTable?.range.zone;
7290
+ let copiedTable = undefined;
7289
7291
  // Copy whole table
7290
- if (coreTable && tableZone && zones.some((z) => isZoneInside(tableZone, z))) {
7291
- copiedTablesIds.add(coreTable.id);
7292
+ if (!copiedTablesIds.has(table.id) &&
7293
+ coreTable &&
7294
+ tableZone &&
7295
+ zones.some((z) => isZoneInside(tableZone, z))) {
7296
+ copiedTablesIds.add(table.id);
7292
7297
  const values = [];
7293
7298
  for (const col of range(tableZone.left, tableZone.right + 1)) {
7294
7299
  values.push(this.getters.getFilterHiddenValues({ sheetId, col, row: tableZone.top }));
7295
7300
  }
7296
- tableCellsInRow.push({
7297
- table: {
7298
- range: coreTable.range,
7299
- config: coreTable.config,
7300
- type: coreTable.type,
7301
- },
7302
- });
7303
- }
7304
- // Copy only style of cell
7305
- else if (table) {
7306
- tableCellsInRow.push({ style: this.getTableStyleToCopy(position) });
7301
+ copiedTable = {
7302
+ range: coreTable.range,
7303
+ config: coreTable.config,
7304
+ type: coreTable.type,
7305
+ };
7307
7306
  }
7307
+ tableCellsInRow.push({
7308
+ table: copiedTable,
7309
+ style: this.getTableStyleToCopy(position),
7310
+ isWholeTableCopied: copiedTablesIds.has(table.id),
7311
+ });
7308
7312
  }
7309
7313
  }
7310
7314
  return {
@@ -7385,11 +7389,14 @@
7385
7389
  tableType: tableCell.table.type,
7386
7390
  });
7387
7391
  }
7388
- // Do not paste table style if we're inside another table
7389
7392
  // We cannot check for dynamic tables, because at this point the paste can have changed the evaluation, and the
7390
7393
  // dynamic tables are not yet computed
7391
- if (!this.getters.getCoreTable(position)) {
7392
- if (tableCell.style?.style && options?.pasteOption !== "asValue") {
7394
+ if (this.getters.getCoreTable(position) || options?.pasteOption === "asValue") {
7395
+ return;
7396
+ }
7397
+ if ((!options?.pasteOption && !tableCell.isWholeTableCopied) ||
7398
+ options?.pasteOption === "onlyFormat") {
7399
+ if (tableCell.style?.style) {
7393
7400
  this.dispatch("UPDATE_CELL", { ...position, style: tableCell.style.style });
7394
7401
  }
7395
7402
  if (tableCell.style?.border) {
@@ -19155,9 +19162,10 @@ stores.inject(MyMetaStore, storeInstance);
19155
19162
  return error;
19156
19163
  }
19157
19164
  if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19165
+ const suggestion = _t("Consider using a dynamic pivot formula: %s. Or re-insert the static pivot from the Data menu.", `=PIVOT(${_pivotFormulaId})`);
19158
19166
  return {
19159
19167
  value: CellErrorType.GenericError,
19160
- message: _t("Dimensions don't match the pivot definition"),
19168
+ message: _t("Dimensions don't match the pivot definition") + ". " + suggestion,
19161
19169
  };
19162
19170
  }
19163
19171
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
@@ -19184,9 +19192,10 @@ stores.inject(MyMetaStore, storeInstance);
19184
19192
  return error;
19185
19193
  }
19186
19194
  if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19195
+ const suggestion = _t("Consider using a dynamic pivot formula: %s. Or re-insert the static pivot from the Data menu.", `=PIVOT(${_pivotFormulaId})`);
19187
19196
  return {
19188
19197
  value: CellErrorType.GenericError,
19189
- message: _t("Dimensions don't match the pivot definition"),
19198
+ message: _t("Dimensions don't match the pivot definition") + ". " + suggestion,
19190
19199
  };
19191
19200
  }
19192
19201
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
@@ -22362,6 +22371,7 @@ stores.inject(MyMetaStore, storeInstance);
22362
22371
  // replace the whole token
22363
22372
  start = tokenAtCursor.start;
22364
22373
  }
22374
+ this.composer.stopComposerRangeSelection();
22365
22375
  this.composer.changeComposerCursorSelection(start, end);
22366
22376
  this.composer.replaceComposerCursorSelection(value);
22367
22377
  }
@@ -22379,6 +22389,7 @@ stores.inject(MyMetaStore, storeInstance);
22379
22389
  // replace the whole token
22380
22390
  start = tokenAtCursor.start;
22381
22391
  }
22392
+ this.composer.stopComposerRangeSelection();
22382
22393
  this.composer.changeComposerCursorSelection(start, end);
22383
22394
  this.composer.replaceComposerCursorSelection(value);
22384
22395
  }
@@ -37194,6 +37205,8 @@ stores.inject(MyMetaStore, storeInstance);
37194
37205
  mutators = ["reset", "deferUpdates", "applyUpdate", "discardPendingUpdate", "update"];
37195
37206
  updatesAreDeferred = false;
37196
37207
  draft = null;
37208
+ notification = this.get(NotificationStore);
37209
+ alreadyNotified = false;
37197
37210
  constructor(get, pivotId) {
37198
37211
  super(get);
37199
37212
  this.pivotId = pivotId;
@@ -37300,6 +37313,16 @@ stores.inject(MyMetaStore, storeInstance);
37300
37313
  pivot: this.draft,
37301
37314
  });
37302
37315
  this.draft = null;
37316
+ if (!this.alreadyNotified && !this.isDynamicPivotInViewport()) {
37317
+ const formulaId = this.getters.getPivotFormulaId(this.pivotId);
37318
+ const pivotExample = `=PIVOT(${formulaId})`;
37319
+ this.alreadyNotified = true;
37320
+ this.notification.notifyUser({
37321
+ type: "info",
37322
+ text: _t("Pivot updates only work with dynamic pivot tables. Use %s or re-insert the static pivot from the Data menu.", pivotExample),
37323
+ sticky: false,
37324
+ });
37325
+ }
37303
37326
  }
37304
37327
  }
37305
37328
  discardPendingUpdate() {
@@ -37330,15 +37353,22 @@ stores.inject(MyMetaStore, storeInstance);
37330
37353
  return;
37331
37354
  }
37332
37355
  const cleanedWithGranularity = this.addDefaultDateTimeGranularity(this.fields, cleanedDefinition);
37333
- if (this.updatesAreDeferred) {
37334
- this.draft = cleanedWithGranularity;
37356
+ this.draft = cleanedWithGranularity;
37357
+ if (!this.updatesAreDeferred) {
37358
+ this.applyUpdate();
37335
37359
  }
37336
- else {
37337
- this.model.dispatch("UPDATE_PIVOT", {
37338
- pivotId: this.pivotId,
37339
- pivot: cleanedWithGranularity,
37340
- });
37360
+ }
37361
+ isDynamicPivotInViewport() {
37362
+ const sheetId = this.getters.getActiveSheetId();
37363
+ for (const col of this.getters.getSheetViewVisibleCols()) {
37364
+ for (const row of this.getters.getSheetViewVisibleRows()) {
37365
+ const isDynamicPivot = this.getters.isSpillPivotFormula({ sheetId, col, row });
37366
+ if (isDynamicPivot) {
37367
+ return true;
37368
+ }
37369
+ }
37341
37370
  }
37371
+ return false;
37342
37372
  }
37343
37373
  addDefaultDateTimeGranularity(fields, definition) {
37344
37374
  const { columns, rows } = definition;
@@ -52429,7 +52459,7 @@ stores.inject(MyMetaStore, storeInstance);
52429
52459
  case "DUPLICATE_PIVOT": {
52430
52460
  const { pivotId, newPivotId } = cmd;
52431
52461
  const pivot = deepCopy(this.getPivotCore(pivotId).definition);
52432
- pivot.name = _t("%s (copy)", pivot.name);
52462
+ pivot.name = cmd.duplicatedPivotName ?? pivot.name + " (copy)";
52433
52463
  this.addPivot(newPivotId, pivot);
52434
52464
  break;
52435
52465
  }
@@ -57518,6 +57548,7 @@ stores.inject(MyMetaStore, storeInstance);
57518
57548
  case "REMOTE_REVISION":
57519
57549
  case "REVISION_REDONE":
57520
57550
  case "REVISION_UNDONE":
57551
+ case "SNAPSHOT_CREATED":
57521
57552
  return this.processedRevisions.has(message.nextRevisionId);
57522
57553
  default:
57523
57554
  return false;
@@ -58033,12 +58064,13 @@ stores.inject(MyMetaStore, storeInstance);
58033
58064
  this.dispatch("DUPLICATE_PIVOT", {
58034
58065
  pivotId,
58035
58066
  newPivotId,
58067
+ duplicatedPivotName: _t("%s (copy)", this.getters.getPivotCoreDefinition(pivotId).name),
58036
58068
  });
58037
58069
  const activeSheetId = this.getters.getActiveSheetId();
58038
58070
  const position = this.getters.getSheetIds().indexOf(activeSheetId) + 1;
58039
58071
  const formulaId = this.getters.getPivotFormulaId(newPivotId);
58040
58072
  const newPivotName = this.getters.getPivotName(newPivotId);
58041
- this.dispatch("CREATE_SHEET", {
58073
+ const result = this.dispatch("CREATE_SHEET", {
58042
58074
  sheetId: newSheetId,
58043
58075
  name: this.getPivotDuplicateSheetName(_t("%(newPivotName)s (Pivot #%(formulaId)s)", {
58044
58076
  newPivotName,
@@ -58046,20 +58078,23 @@ stores.inject(MyMetaStore, storeInstance);
58046
58078
  })),
58047
58079
  position,
58048
58080
  });
58049
- this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
58050
- this.dispatch("UPDATE_CELL", {
58051
- sheetId: newSheetId,
58052
- col: 0,
58053
- row: 0,
58054
- content: `=PIVOT(${formulaId})`,
58055
- });
58081
+ if (result.isSuccessful) {
58082
+ this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
58083
+ this.dispatch("UPDATE_CELL", {
58084
+ sheetId: newSheetId,
58085
+ col: 0,
58086
+ row: 0,
58087
+ content: `=PIVOT(${formulaId})`,
58088
+ });
58089
+ }
58056
58090
  }
58057
58091
  getPivotDuplicateSheetName(pivotName) {
58058
58092
  let i = 1;
58059
58093
  const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
58060
- let name = pivotName;
58094
+ const sanitizedName = pivotName.replace(new RegExp(FORBIDDEN_IN_EXCEL_REGEX, "g"), " ");
58095
+ let name = sanitizedName;
58061
58096
  while (names.includes(name)) {
58062
- name = `${pivotName} (${i})`;
58097
+ name = `${sanitizedName} (${i})`;
58063
58098
  i++;
58064
58099
  }
58065
58100
  return name;
@@ -58640,9 +58675,12 @@ stores.inject(MyMetaStore, storeInstance);
58640
58675
  "HIDE_COLUMNS_ROWS",
58641
58676
  "UNHIDE_COLUMNS_ROWS",
58642
58677
  "UNFOLD_HEADER_GROUP",
58678
+ "UNGROUP_HEADERS",
58643
58679
  "FOLD_HEADER_GROUP",
58644
58680
  "FOLD_ALL_HEADER_GROUPS",
58645
58681
  "UNFOLD_ALL_HEADER_GROUPS",
58682
+ "FOLD_HEADER_GROUPS_IN_ZONE",
58683
+ "UNFOLD_HEADER_GROUPS_IN_ZONE",
58646
58684
  "CREATE_TABLE",
58647
58685
  "UPDATE_TABLE",
58648
58686
  "UPDATE_FILTER",
@@ -59958,6 +59996,8 @@ stores.inject(MyMetaStore, storeInstance);
59958
59996
  case "UNFOLD_HEADER_GROUP":
59959
59997
  case "FOLD_ALL_HEADER_GROUPS":
59960
59998
  case "UNFOLD_ALL_HEADER_GROUPS":
59999
+ case "FOLD_HEADER_GROUPS_IN_ZONE":
60000
+ case "UNFOLD_HEADER_GROUPS_IN_ZONE":
59961
60001
  this.updateHiddenRows(cmd.sheetId);
59962
60002
  break;
59963
60003
  case "UPDATE_FILTER":
@@ -65904,7 +65944,9 @@ stores.inject(MyMetaStore, storeInstance);
65904
65944
  * The value does not matter, it can be hardcoded.
65905
65945
  */
65906
65946
  const catAxId = 17781237;
65947
+ const secondaryCatAxId = 17781238;
65907
65948
  const valAxId = 88853993;
65949
+ const secondaryValAxId = 88853994;
65908
65950
  function createChart(chart, chartSheetIndex, data) {
65909
65951
  const namespaces = [
65910
65952
  ["xmlns:r", RELATIONSHIP_NSR],
@@ -66220,8 +66262,8 @@ stores.inject(MyMetaStore, storeInstance);
66220
66262
  <!-- each data marker in the series does not have a different color -->
66221
66263
  <c:varyColors val="0"/>
66222
66264
  ${barDataSetNode}
66223
- <c:axId val="${catAxId + (useRightAxisForBarSerie ? 1 : 0)}" />
66224
- <c:axId val="${valAxId + (useRightAxisForBarSerie ? 1 : 0)}" />
66265
+ <c:axId val="${useRightAxisForBarSerie ? secondaryCatAxId : catAxId}" />
66266
+ <c:axId val="${useRightAxisForBarSerie ? secondaryValAxId : valAxId}" />
66225
66267
  </c:barChart>
66226
66268
  ${leftDataSetsNodes.length
66227
66269
  ? escapeXml /*xml*/ `
@@ -66242,21 +66284,21 @@ stores.inject(MyMetaStore, storeInstance);
66242
66284
  <!-- each data marker in the series does not have a different color -->
66243
66285
  <c:varyColors val="0"/>
66244
66286
  ${joinXmlNodes(rightDataSetsNodes)}
66245
- <c:axId val="${catAxId + 1}" />
66246
- <c:axId val="${valAxId + 1}" />
66287
+ <c:axId val="${secondaryCatAxId}" />
66288
+ <c:axId val="${secondaryValAxId}" />
66247
66289
  </c:lineChart>
66248
66290
  `
66249
66291
  : ""}
66250
66292
  ${!useRightAxisForBarSerie || leftDataSetsNodes.length
66251
66293
  ? escapeXml /*xml*/ `
66252
- ${addAx("b", "c:catAx", catAxId + 1, valAxId + 1, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
66253
- ${addAx("r", "c:valAx", valAxId + 1, catAxId + 1, chart.axesDesign?.y1?.title, chart.fontColor)}
66294
+ ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
66295
+ ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
66254
66296
  `
66255
66297
  : ""}
66256
66298
  ${useRightAxisForBarSerie || rightDataSetsNodes.length
66257
66299
  ? escapeXml /*xml*/ `
66258
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length || !useRightAxisForBarSerie ? 1 : 0)}
66259
- ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
66300
+ ${addAx("b", "c:catAx", secondaryCatAxId, secondaryValAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length || !useRightAxisForBarSerie ? 1 : 0)}
66301
+ ${addAx("r", "c:valAx", secondaryValAxId, secondaryCatAxId, chart.axesDesign?.y1?.title, chart.fontColor)}
66260
66302
  `
66261
66303
  : ""}
66262
66304
  `;
@@ -67277,6 +67319,15 @@ stores.inject(MyMetaStore, storeInstance);
67277
67319
  ["id", i + 1], // id cannot be 0
67278
67320
  ["name", colName],
67279
67321
  ];
67322
+ if (table.config.totalRow) {
67323
+ // Note: To be 100% complete, we could also add a `totalsRowLabel` attribute for total strings, and a tag
67324
+ // `<totalsRowFormula>` for the formula of the total. But those doesn't seem to be mandatory for Excel.
67325
+ const colTotalXc = toXC(tableZone.left + i, tableZone.bottom);
67326
+ const colTotalContent = sheetData.cells[colTotalXc]?.content;
67327
+ if (colTotalContent?.startsWith("=")) {
67328
+ colAttributes.push(["totalsRowFunction", "custom"]);
67329
+ }
67330
+ }
67280
67331
  columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
67281
67332
  }
67282
67333
  return escapeXml /*xml*/ `
@@ -67371,8 +67422,9 @@ stores.inject(MyMetaStore, storeInstance);
67371
67422
  }
67372
67423
  else if (cell.content && cell.content !== "") {
67373
67424
  const isTableHeader = isCellTableHeader(c, r, sheet);
67425
+ const isTableTotal = isCellTableTotal(c, r, sheet);
67374
67426
  const isPlainText = !!(cell.format && data.formats[cell.format] === PLAIN_TEXT_FORMAT);
67375
- ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
67427
+ ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isTableTotal || isPlainText));
67376
67428
  }
67377
67429
  attributes.push(...additionalAttrs);
67378
67430
  // prettier-ignore
@@ -67406,6 +67458,16 @@ stores.inject(MyMetaStore, storeInstance);
67406
67458
  return isInside(col, row, headerZone);
67407
67459
  });
67408
67460
  }
67461
+ function isCellTableTotal(col, row, sheet) {
67462
+ return sheet.tables.some((table) => {
67463
+ if (!table.config.totalRow) {
67464
+ return false;
67465
+ }
67466
+ const zone = toZone(table.range);
67467
+ const totalZone = { ...zone, top: zone.bottom };
67468
+ return isInside(col, row, totalZone);
67469
+ });
67470
+ }
67409
67471
  function addHyperlinks(construct, data, sheetIndex) {
67410
67472
  const sheet = data.sheets[sheetIndex];
67411
67473
  const cells = sheet.cells;
@@ -67884,7 +67946,6 @@ stores.inject(MyMetaStore, storeInstance);
67884
67946
  isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
67885
67947
  isDashboard: () => this.config.mode === "dashboard",
67886
67948
  };
67887
- this.uuidGenerator.setIsFastStrategy(true);
67888
67949
  // Initiate stream processor
67889
67950
  this.selection = new SelectionStreamProcessorImpl(this.getters);
67890
67951
  this.coreHandlers.push(this.range);
@@ -68548,9 +68609,9 @@ stores.inject(MyMetaStore, storeInstance);
68548
68609
  exports.tokenize = tokenize;
68549
68610
 
68550
68611
 
68551
- __info__.version = "17.4.8";
68552
- __info__.date = "2024-09-17T08:10:18.777Z";
68553
- __info__.hash = "caad8bd";
68612
+ __info__.version = "17.4.9";
68613
+ __info__.date = "2024-10-14T07:53:51.633Z";
68614
+ __info__.hash = "b7015b7";
68554
68615
 
68555
68616
 
68556
68617
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);